#![deny(unsafe_op_in_unsafe_fn)]
use concinnity_core::gfx::render_types::{CLUSTER_COUNT, CLUSTER_LIST_LEN, ClusterParams};
use concinnity_core::render::error::{RenderError, RenderResult};
use objc2::rc::Retained;
use objc2::runtime::ProtocolObject;
use objc2_foundation::ns_string;
use objc2_metal::{
MTLCommandBuffer as _, MTLComputeCommandEncoder as _, MTLComputePipelineState, MTLDevice as _,
MTLResourceOptions, MTLSize,
};
use super::builtin_shaders::compute_pipeline;
use super::context::MtlContext;
use super::encode::ComputeEncode;
use super::error::allocation_failed;
use super::scoped_encoder::ScopedEncoder;
pub(crate) struct LightCullState {
pub pipeline: Retained<ProtocolObject<dyn MTLComputePipelineState>>,
pub cluster_buffer: Retained<ProtocolObject<dyn objc2_metal::MTLBuffer>>,
}
impl MtlContext {
pub(in crate::metal) fn encode_light_cull(
&self,
cmd_buf: &ProtocolObject<dyn objc2_metal::MTLCommandBuffer>,
cluster_params: &ClusterParams,
) -> RenderResult<u32> {
let desc = objc2_metal::MTLComputePassDescriptor::new();
if let Some(t) = &self.diagnostics.pass_timing {
t.attach_compute(&desc, super::pass_timing::PassId::LightCull);
}
let enc = ScopedEncoder::new(
cmd_buf
.computeCommandEncoderWithDescriptor(&desc)
.ok_or_else(|| {
RenderError::Other("failed to get light-cull compute encoder".to_string())
})?,
ns_string!("clustered light cull"),
);
enc.set_pipeline(&self.light_cull.pipeline);
enc.set_value(cluster_params, 0);
enc.set_buffer(&self.scene.local_light_buffer, 0, 1);
enc.set_buffer(&self.light_cull.cluster_buffer, 0, 2);
if let Some(records) = self.probe.records_buf.as_deref() {
enc.set_buffer(records, 0, 3);
}
let tg = MTLSize {
width: 64,
height: 1,
depth: 1,
};
let grid = MTLSize {
width: CLUSTER_COUNT as usize,
height: 1,
depth: 1,
};
enc.dispatchThreads_threadsPerThreadgroup(grid, tg);
Ok(0)
}
}
pub(super) fn build_light_cull_pipeline(
device: &ProtocolObject<dyn objc2_metal::MTLDevice>,
hot_reload: bool,
) -> RenderResult<Retained<ProtocolObject<dyn MTLComputePipelineState>>> {
compute_pipeline(device, &super::builtin_shaders::LIGHT_CULL, hot_reload)
}
pub(super) fn build_cluster_light_buffer(
device: &ProtocolObject<dyn objc2_metal::MTLDevice>,
) -> RenderResult<Retained<ProtocolObject<dyn objc2_metal::MTLBuffer>>> {
let len = CLUSTER_LIST_LEN as usize * std::mem::size_of::<u32>();
device
.newBufferWithLength_options(len, MTLResourceOptions::StorageModePrivate)
.ok_or_else(|| allocation_failed("cluster light buffer"))
}