#![deny(unsafe_op_in_unsafe_fn)]
use concinnity_core::gfx::view_modes::ViewMode;
use concinnity_core::render::error::{RenderError, RenderResult};
use concinnity_core::render::sky;
use concinnity_core::render::uniforms::{GBufferView, ViewUniforms};
use objc2::rc::Retained;
use objc2::runtime::ProtocolObject;
use objc2_metal::{
MTLCommandEncoder as _, MTLDevice, MTLPixelFormat, MTLPrimitiveType, MTLRenderCommandEncoder,
MTLRenderPipelineDescriptor, MTLRenderPipelineState,
};
use super::builtin_shaders::{self, GBUFFER_SKY_FRAG, GBUFFER_SKY_VERT, SKY_FRAG, SKY_VERT};
use super::context::MtlContext;
use super::encode::RenderEncode;
pub(super) struct SkyState {
pub(super) pipeline: Retained<ProtocolObject<dyn MTLRenderPipelineState>>,
pub(super) velocity_pipeline: Retained<ProtocolObject<dyn MTLRenderPipelineState>>,
pub(super) background: bool,
}
impl SkyState {
pub(super) fn build(
device: &ProtocolObject<dyn MTLDevice>,
sample_count: u32,
background: bool,
hot_reload: bool,
) -> RenderResult<Self> {
Ok(Self {
pipeline: build_sky_pipeline(device, sample_count, hot_reload)?,
velocity_pipeline: build_sky_velocity_pipeline(device, hot_reload)?,
background,
})
}
}
pub(super) fn build_sky_pipeline(
device: &ProtocolObject<dyn MTLDevice>,
sample_count: u32,
hot_reload: bool,
) -> RenderResult<Retained<ProtocolObject<dyn MTLRenderPipelineState>>> {
let vert = builtin_shaders::entry_function(device, &SKY_VERT, hot_reload)?;
let frag = builtin_shaders::entry_function(device, &SKY_FRAG, hot_reload)?;
let desc = MTLRenderPipelineDescriptor::new();
desc.setVertexFunction(Some(&vert));
desc.setFragmentFunction(Some(&frag));
desc.setRasterSampleCount(sample_count as usize);
unsafe {
desc.colorAttachments()
.objectAtIndexedSubscript(0)
.setPixelFormat(MTLPixelFormat::RGBA16Float);
}
desc.setDepthAttachmentPixelFormat(MTLPixelFormat::Depth32Float);
device
.newRenderPipelineStateWithDescriptor_error(&desc)
.map_err(|e| RenderError::ShaderCompile(format!("sky pipeline state: {e:?}")))
}
pub(super) fn build_sky_velocity_pipeline(
device: &ProtocolObject<dyn MTLDevice>,
hot_reload: bool,
) -> RenderResult<Retained<ProtocolObject<dyn MTLRenderPipelineState>>> {
let vert = builtin_shaders::entry_function(device, &GBUFFER_SKY_VERT, hot_reload)?;
let frag = builtin_shaders::entry_function(device, &GBUFFER_SKY_FRAG, hot_reload)?;
let desc = MTLRenderPipelineDescriptor::new();
desc.setVertexFunction(Some(&vert));
desc.setFragmentFunction(Some(&frag));
desc.setRasterSampleCount(1);
unsafe {
let targets = desc.colorAttachments();
for (i, format) in super::post::gbuffer::GBUFFER_FORMATS.iter().enumerate() {
targets.objectAtIndexedSubscript(i).setPixelFormat(*format);
}
}
desc.setDepthAttachmentPixelFormat(MTLPixelFormat::Depth32Float);
device
.newRenderPipelineStateWithDescriptor_error(&desc)
.map_err(|e| RenderError::ShaderCompile(format!("sky velocity pipeline state: {e:?}")))
}
impl MtlContext {
pub(super) fn draws_sky(&self, mode: ViewMode) -> bool {
sky::draws_sky(
self.scene.env_map.prefilter_mip_count > 0,
self.sky.background,
mode,
)
}
pub(super) fn encode_sky(
&self,
enc: &ProtocolObject<dyn MTLRenderCommandEncoder>,
view: &ViewUniforms,
) {
enc.pushDebugGroup(objc2_foundation::ns_string!("sky"));
enc.set_pipeline(&self.sky.pipeline);
enc.set_depth_stencil(&self.targets.depth_state_read_only);
enc.set_vertex_value(view, 0);
enc.set_fragment_value(view, 0);
enc.set_fragment_texture(&self.scene.env_map.prefilter, 0);
enc.set_fragment_sampler(&self.scene.cube_sampler, 0);
unsafe {
enc.drawPrimitives_vertexStart_vertexCount(MTLPrimitiveType::Triangle, 0, 3);
}
enc.popDebugGroup();
}
pub(super) fn encode_sky_velocity(
&self,
enc: &ProtocolObject<dyn MTLRenderCommandEncoder>,
view: &GBufferView,
) {
enc.set_pipeline(&self.sky.velocity_pipeline);
enc.set_depth_stencil(&self.targets.depth_state_read_only);
enc.set_vertex_value(view, 0);
unsafe {
enc.drawPrimitives_vertexStart_vertexCount(MTLPrimitiveType::Triangle, 0, 3);
}
}
}