#![deny(unsafe_op_in_unsafe_fn)]
use concinnity_core::render::error::{RenderError, RenderResult};
use concinnity_core::render::post::device::PostExtent;
use concinnity_core::render::post::taa::{TaaInputs, TaaPass, TaaRing};
use objc2::rc::Retained;
use objc2::runtime::ProtocolObject;
use objc2_metal::MTLTexture;
use crate::metal::context::MtlContext;
use crate::metal::post::post_device::{MtlPostDevice, MtlPostPipeline, MtlPostTarget};
pub(crate) type MtlTaaPass = TaaPass<MtlPostPipeline, MtlPostTarget>;
pub(crate) struct TaaState {
pub enabled: bool,
pub pass: Option<MtlTaaPass>,
pub frame: u32,
}
impl TaaState {
pub(crate) fn output(&self) -> Option<&Retained<ProtocolObject<dyn MTLTexture>>> {
let pass = self.pass.as_ref()?;
Some(pass.target(pass.ring().write()).texture())
}
}
pub(crate) fn build_taa_pass(
device: &MtlPostDevice,
width: u32,
height: u32,
) -> RenderResult<MtlTaaPass> {
TaaPass::new(device, TaaRing::ping_pong(), PostExtent { width, height })
}
impl MtlContext {
pub(in crate::metal) fn post_device(&self) -> MtlPostDevice<'_> {
MtlPostDevice {
device: &self.hw.device,
sampler: &self.composite.sampler,
cube_sampler: &self.scene.cube_sampler,
probes: Some(self.probe_bindings()),
timing: self.diagnostics.pass_timing.as_ref(),
hot_reload: self.hot_reload.enabled,
}
}
pub(in crate::metal) fn encode_taa(
&self,
cmd_buf: &ProtocolObject<dyn objc2_metal::MTLCommandBuffer>,
scene_input: &ProtocolObject<dyn objc2_metal::MTLTexture>,
reactive_written: bool,
) -> RenderResult<u32> {
let pass =
self.taa.pass.as_ref().ok_or_else(|| {
RenderError::Other("TAA enabled but the resolve is missing".into())
})?;
let velocity = self.gbuffer_velocity().ok_or_else(|| {
RenderError::Other("TAA enabled but the pooled G-buffer velocity is missing".into())
})?;
let device = self.post_device();
pass.encode(
&device,
cmd_buf,
pass.ring().write(),
TaaInputs {
scene: scene_input,
velocity,
reactive: reactive_written.then(|| self.targets.hdr.reactive_mask.as_ref()),
},
)?;
Ok(0)
}
}