#![deny(unsafe_op_in_unsafe_fn)]
use objc2::rc::Retained;
use objc2::runtime::ProtocolObject;
use objc2_metal::{
MTLCommandBuffer as _, MTLLoadAction, MTLPixelFormat, MTLPrimitiveType,
MTLRenderCommandEncoder as _, MTLRenderPassDescriptor, MTLRenderPipelineState, MTLStoreAction,
};
use crate::metal::context::MtlContext;
use crate::metal::encode::RenderEncode;
use crate::metal::post::fullscreen::{
FullscreenBlend, build_slang_fullscreen_pipeline, set_fragment_sampler_range,
};
use crate::metal::scoped_encoder::ScopedEncoder;
use crate::metal::slang_shaders::SlangLib;
const RT_POOL_SAMPLER_INDEX: usize = 12;
pub(crate) fn build_rt_reflection_pipeline(
device: &ProtocolObject<dyn objc2_metal::MTLDevice>,
fragment: &SlangLib,
hot_reload: bool,
) -> Result<Retained<ProtocolObject<dyn MTLRenderPipelineState>>, String> {
build_slang_fullscreen_pipeline(
device,
fragment,
MTLPixelFormat::RGBA16Float,
FullscreenBlend::Replace,
hot_reload,
)
}
impl MtlContext {
pub(in crate::metal) fn encode_rt_reflections(
&self,
cmd_buf: &ProtocolObject<dyn objc2_metal::MTLCommandBuffer>,
rt_params: &crate::gfx::render_types::RtParams,
bindless_tex_args: Option<&Retained<ProtocolObject<dyn objc2_metal::MTLBuffer>>>,
) -> Result<u32, String> {
let (targets, accel, gb_normal_depth, gb_roughness) = match (
&self.ssr.targets,
&self.rt.accel,
self.gbuffer_normal_depth(),
self.gbuffer_roughness(),
) {
(Some(t), Some(a), Some(n), Some(r)) => (t, a, n, r),
_ => return Ok(0),
};
let textured = bindless_tex_args.is_some() && self.rt.pipeline_textured.is_some();
let pipeline = match (textured, &self.rt.pipeline_textured, &self.rt.pipeline) {
(true, Some(p), _) => p,
(_, _, Some(p)) => p,
_ => return Ok(0),
};
let desc = MTLRenderPassDescriptor::new();
unsafe {
let ca = desc.colorAttachments().objectAtIndexedSubscript(0);
ca.setTexture(Some(targets.reflection.as_ref()));
ca.setLoadAction(MTLLoadAction::DontCare);
ca.setStoreAction(MTLStoreAction::Store);
}
if let Some(t) = &self.diagnostics.pass_timing {
t.attach_render(&desc, crate::metal::pass_timing::PassId::RtReflections);
}
let enc = ScopedEncoder::new(
cmd_buf
.renderCommandEncoderWithDescriptor(&desc)
.ok_or("failed to get RT reflections encoder")?,
"rt reflections",
);
enc.set_pipeline(pipeline);
enc.set_fragment_texture(self.hdr_targets.hdr_resolve.as_ref(), 0);
enc.set_fragment_texture(gb_normal_depth, 1);
enc.set_fragment_texture(gb_roughness, 2);
enc.set_fragment_texture(self.env_map.prefilter.as_ref(), 3);
for i in 0..concinnity_core::render::uniforms::MAX_PROBES {
enc.set_fragment_texture(self.probe_cube_or_sky(i), 4 + i);
}
set_fragment_sampler_range(&enc, &self.post_sampler, 0, 3);
set_fragment_sampler_range(
&enc,
self.cube_sampler.as_ref(),
3,
1 + concinnity_core::render::uniforms::MAX_PROBES,
);
set_fragment_sampler_range(&enc, self.sampler.as_ref(), RT_POOL_SAMPLER_INDEX, 1);
enc.set_fragment_value(rt_params, 0);
enc.set_fragment_buffer(self.vertex_buffer.as_ref(), 0, 1);
enc.set_fragment_buffer(self.index_buffer.as_ref(), 0, 2);
enc.set_fragment_buffer(accel.geom_table.as_ref(), 0, 3);
enc.set_fragment_acceleration_structure(accel.tlas.as_ref(), 4);
enc.set_fragment_buffer(accel.deformed_verts.as_ref(), 0, 5);
enc.set_fragment_buffer(accel.skinned_indices.as_ref(), 0, 6);
enc.set_fragment_value(&self.probe.set, 8);
if textured && let Some(tex_args) = bindless_tex_args {
enc.set_fragment_buffer(
tex_args.as_ref(),
0,
crate::metal::context::BINDLESS_TEXTURE_ARG_BUFFER_INDEX,
);
self.use_bindless_textures(&enc);
}
crate::metal::raytrace::use_blas_resident_fragment(&enc, &accel.blas);
unsafe {
enc.drawPrimitives_vertexStart_vertexCount(MTLPrimitiveType::Triangle, 0, 3);
}
drop(enc);
self.encode_reflection_composite(cmd_buf)?;
Ok(0)
}
}