use concinnity_core::render::error::{RenderError, RenderResult};
use concinnity_core::render::reactive_mask::ReactiveWrite;
use objc2::runtime::ProtocolObject;
use objc2_foundation::ns_string;
use objc2_metal::{
MTLBlendFactor, MTLBlendOperation, MTLClearColor, MTLCommandBuffer, MTLLoadAction,
MTLRenderPassDescriptor, MTLRenderPipelineDescriptor, MTLStoreAction,
};
use super::context::MtlContext;
use super::scoped_encoder::ScopedEncoder;
use super::texture::REACTIVE_MASK_FORMAT;
pub(super) const REACTIVE_MASK_TARGET: usize = 1;
pub(super) fn declare_target(desc: &MTLRenderPipelineDescriptor) {
unsafe {
let ca = desc
.colorAttachments()
.objectAtIndexedSubscript(REACTIVE_MASK_TARGET);
ca.setPixelFormat(REACTIVE_MASK_FORMAT);
ca.setBlendingEnabled(true);
ca.setRgbBlendOperation(MTLBlendOperation::Max);
ca.setSourceRGBBlendFactor(MTLBlendFactor::One);
ca.setDestinationRGBBlendFactor(MTLBlendFactor::One);
ca.setAlphaBlendOperation(MTLBlendOperation::Max);
ca.setSourceAlphaBlendFactor(MTLBlendFactor::One);
ca.setDestinationAlphaBlendFactor(MTLBlendFactor::One);
}
}
impl MtlContext {
pub(super) fn attach_reactive_mask(
&self,
desc: &MTLRenderPassDescriptor,
write: ReactiveWrite,
) {
let (load, store) = match write {
ReactiveWrite::Unstored => (MTLLoadAction::DontCare, MTLStoreAction::DontCare),
ReactiveWrite::Clear => (MTLLoadAction::Clear, MTLStoreAction::Store),
ReactiveWrite::Load => (MTLLoadAction::Load, MTLStoreAction::Store),
};
unsafe {
let ca = desc
.colorAttachments()
.objectAtIndexedSubscript(REACTIVE_MASK_TARGET);
ca.setTexture(Some(self.targets.hdr.reactive_mask.as_ref()));
ca.setLoadAction(load);
ca.setStoreAction(store);
ca.setClearColor(MTLClearColor {
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.0,
});
}
}
pub(super) fn clear_reactive_mask(
&self,
cmd_buf: &ProtocolObject<dyn MTLCommandBuffer>,
write: ReactiveWrite,
) -> RenderResult<()> {
if write != ReactiveWrite::Clear {
return Ok(());
}
let desc = MTLRenderPassDescriptor::new();
self.attach_reactive_mask(&desc, write);
let _enc = ScopedEncoder::new(
cmd_buf
.renderCommandEncoderWithDescriptor(&desc)
.ok_or_else(|| {
RenderError::Other("failed to get reactive mask clear encoder".into())
})?,
ns_string!("reactive mask: clear"),
);
Ok(())
}
}