#![deny(unsafe_op_in_unsafe_fn)]
use objc2::rc::Retained;
use objc2::runtime::ProtocolObject;
use objc2_metal::{
MTLCommandBuffer as _, MTLIndexType, MTLLoadAction, MTLPrimitiveType,
MTLRenderCommandEncoder as _, MTLScissorRect, MTLStoreAction, MTLTexture,
};
use crate::gfx::render_types::TextDrawCall;
use crate::metal::context::MtlContext;
use crate::metal::encode::RenderEncode;
use crate::metal::scoped_encoder::ScopedEncoder;
impl MtlContext {
pub(in crate::metal) fn encode_composite_and_text(
&self,
cmd_buf: &ProtocolObject<dyn objc2_metal::MTLCommandBuffer>,
scene_color: &Retained<ProtocolObject<dyn MTLTexture>>,
text_calls: &[TextDrawCall],
) -> Result<u32, String> {
let composite_pass_desc = self
.window
.view
.currentRenderPassDescriptor()
.ok_or("no current render pass descriptor")?;
unsafe {
let ca = composite_pass_desc
.colorAttachments()
.objectAtIndexedSubscript(0);
ca.setLoadAction(MTLLoadAction::DontCare);
ca.setStoreAction(MTLStoreAction::Store);
}
if let Some(t) = &self.diagnostics.pass_timing {
t.attach_render(
&composite_pass_desc,
super::super::pass_timing::PassId::Composite,
);
}
let post_encoder = ScopedEncoder::new(
cmd_buf
.renderCommandEncoderWithDescriptor(&composite_pass_desc)
.ok_or("failed to get post-process render encoder")?,
"composite",
);
post_encoder.set_pipeline(&self.post_pipeline_state);
let channel_view = if self.view.mode.is_gbuffer_channel() {
self.view.mode as u32
} else {
0
};
post_encoder.set_fragment_texture(scene_color.as_ref(), 0);
post_encoder.set_fragment_texture(self.bloom_targets.mips[0].as_ref(), 1);
post_encoder.set_fragment_texture(self.color_lut.as_ref(), 2);
if channel_view != 0 {
let nd = self
.gbuffer_normal_depth()
.unwrap_or_else(|| self.ssao.white.as_ref());
let rough = self
.gbuffer_roughness()
.unwrap_or_else(|| self.ssao.white.as_ref());
post_encoder.set_fragment_texture(nd, 3);
post_encoder.set_fragment_texture(rough, 4);
post_encoder.set_fragment_texture(self.ao_output_texture(), 5);
}
crate::metal::post::fullscreen::set_fragment_sampler_range(
&post_encoder,
&self.post_sampler,
0,
6,
);
let composite = crate::gfx::render_types::CompositeParams {
post: self.post_process,
fade: self.view.scene_fade,
view_mode: channel_view,
far: self.view.far,
};
post_encoder.set_fragment_value(&composite, 0);
unsafe {
post_encoder.drawPrimitives_vertexStart_vertexCount(MTLPrimitiveType::Triangle, 0, 3);
}
let mut draw_calls: u32 = 1;
if let Some(text_ps) = self.text.pipeline_state.clone()
&& let Some((text_buffer, text_ranges)) = self.text.upload.binding()
&& !self.text.atlas_textures.is_empty()
{
let logical = self.window.view.bounds().size;
let win_w = logical.width as f32;
let win_h = logical.height as f32;
let text_uniforms = crate::gfx::render_types::TextUniforms {
win_width: win_w,
win_height: win_h,
_pad: [0.0; 2],
};
let (fb_w, fb_h) = unsafe {
match composite_pass_desc
.colorAttachments()
.objectAtIndexedSubscript(0)
.texture()
{
Some(t) => (t.width(), t.height()),
None => (win_w.max(0.0) as usize, win_h.max(0.0) as usize),
}
};
post_encoder.set_pipeline(&text_ps);
post_encoder.set_fragment_sampler(&self.text.sampler, 0);
post_encoder.set_vertex_value(&text_uniforms, 0);
let mut binds = crate::gfx::fullscreen::TextBindCache::new();
for (call, range) in text_calls.iter().zip(text_ranges) {
if call.vertices.is_empty() {
continue;
}
let scissor = match call.clip_rect {
Some(clip) => {
let scissor = crate::gfx::fullscreen::clip_rect_to_scissor(
clip,
(win_w, win_h),
(fb_w as u32, fb_h as u32),
);
let Some(rect) = scissor else {
continue;
};
rect
}
None => (0, 0, fb_w as u32, fb_h as u32),
};
if binds.scissor_changed(scissor) {
let (x, y, w, h) = scissor;
post_encoder.setScissorRect(MTLScissorRect {
x: x as usize,
y: y as usize,
width: w as usize,
height: h as usize,
});
}
let atlas_idx = call.atlas_slot.min(self.text.atlas_textures.len() - 1);
if binds.atlas_changed(atlas_idx) {
post_encoder
.set_fragment_texture(self.text.atlas_textures[atlas_idx].as_ref(), 0);
}
post_encoder.set_vertex_buffer(text_buffer, range.vertex_offset, 1);
unsafe {
post_encoder
.drawIndexedPrimitives_indexCount_indexType_indexBuffer_indexBufferOffset(
MTLPrimitiveType::Triangle,
call.indices.len(),
MTLIndexType::UInt16,
text_buffer,
range.index_offset,
);
}
draw_calls += 1;
}
}
Ok(draw_calls)
}
}