diff --git a/src/backend/metal/Cargo.toml b/src/backend/metal/Cargo.toml index daaf3061c9d..38ff69e019f 100644 --- a/src/backend/metal/Cargo.toml +++ b/src/backend/metal/Cargo.toml @@ -31,7 +31,5 @@ block = "0.1" cocoa = "0.15" core-foundation = "0.6" core-graphics = "0.14" -io-surface = "0.10" smallvec = "0.6" spirv_cross = "0.9.2" -fxhash = "0.2.1" diff --git a/src/backend/metal/src/command.rs b/src/backend/metal/src/command.rs index 358c7a238a7..b38de10b0ff 100644 --- a/src/backend/metal/src/command.rs +++ b/src/backend/metal/src/command.rs @@ -116,12 +116,20 @@ struct Temp { blit_vertices: FastHashMap<(Aspects, Level), Vec>, } +#[derive(Clone)] +struct RenderPipelineState { + raw: metal::RenderPipelineState, + ds_desc: pso::DepthStencilDesc, + vbuf_map: native::VertexBufferMap, + at_formats: SmallVec<[Option; 8]>, +} + #[derive(Clone)] struct State { viewport: Option, scissors: Option, blend_color: Option, - render_pso: Option<(metal::RenderPipelineState, native::VertexBufferMap, Vec>)>, + render_pso: Option, /// A flag to handle edge cases of Vulkan binding inheritance: /// we don't want to consider the current PSO bound for a new pass if it's not compatible. render_pso_is_compatible: bool, @@ -133,9 +141,6 @@ struct State { resources_cs: StageResources, index_buffer: Option>, rasterizer_state: Option, - pipeline_depth_stencil: Option<(pso::DepthStencilDesc, metal::DepthStencilState)>, - dynamic_depth_stencil_desc: Option, - dynamic_depth_stencil_state: Option, stencil: native::StencilState, push_constants: Vec, vertex_buffers: Vec>, @@ -164,7 +169,9 @@ impl State { } } - fn make_render_commands<'a>(&'a self, aspects: Aspects) -> impl Iterator> { + fn make_render_commands<'a>( + &'a self, aspects: Aspects + ) -> impl Iterator> { // Apply previously bound values for this command buffer let com_vp = self.viewport.map(soft::RenderCommand::SetViewport); let com_scissor = self.scissors.map(|sr| soft::RenderCommand::SetScissor( @@ -184,25 +191,13 @@ impl State { }; let com_pso = if self.render_pso_is_compatible { let rast = self.rasterizer_state.clone(); - self.render_pso.as_ref().map(|&(ref pso, _, _)| { - soft::RenderCommand::BindPipeline(&**pso, rast) + self.render_pso.as_ref().map(|ps| { + soft::RenderCommand::BindPipeline(&*ps.raw, rast) }) } else { None }; - let com_ds = if aspects.intersects(Aspects::DEPTH | Aspects::STENCIL) { - if let Some((_, ref static_state)) = self.pipeline_depth_stencil { - Some(soft::RenderCommand::SetDepthStencilDesc(&**static_state)) - } else if let Some(ref dynamic_state) = self.dynamic_depth_stencil_state { - Some(soft::RenderCommand::SetDepthStencilDesc(&**dynamic_state)) - } else { - None - } - } else { - None - }; - let render_resources = iter::once(&self.resources_vs).chain(iter::once(&self.resources_fs)); let push_constants = self.push_constants.as_slice(); let com_resources = [pso::Stage::Vertex, pso::Stage::Fragment] @@ -255,7 +250,7 @@ impl State { .chain(com_blend) .chain(com_depth_bias) .chain(com_pso) - .chain(com_ds) + //.chain(com_ds) // done outside .chain(com_resources) } @@ -313,7 +308,7 @@ impl State { fn set_vertex_buffers(&mut self) -> u64 { let map = match self.render_pso { - Some((_, ref map, _)) => map, + Some(ref ps) => &ps.vbuf_map, None => return 0 }; @@ -362,67 +357,32 @@ impl State { }) } - fn set_stencil_mask_values<'a>( - &'a mut self, - shared: &Shared, - front_back_read_masks_to_update: Option<(pso::StencilValue, pso::StencilValue)>, - front_back_write_masks_to_update: Option<(pso::StencilValue, pso::StencilValue)>, - dynamic_depth_stencil_from_pipeline: Option<&'a metal::DepthStencilDescriptorRef>, + fn sync_depth_stencil<'a>( + &'a self, pipes: &'a mut ServicePipes, device: &Mutex, ) -> Option> { - if let Some((f, b)) = front_back_read_masks_to_update { - self.stencil.front_read_mask = f; - self.stencil.back_read_mask = b; - } - - if let Some((f, b)) = front_back_write_masks_to_update { - self.stencil.front_write_mask = f; - self.stencil.back_write_mask = b; - } - - if let Some(ds) = dynamic_depth_stencil_from_pipeline { - self.dynamic_depth_stencil_desc = Some(ds.to_owned()); - } - - self.dynamic_depth_stencil_state = self.dynamic_depth_stencil_desc.as_ref().map(|desc| { - let f_owned; - let front = match desc.front_face_stencil() { - Some(f) => f, - None => { - f_owned = metal::StencilDescriptor::new(); - desc.set_front_face_stencil(Some(&f_owned)); - &f_owned - } - }; - - let b_owned; - let back = match desc.back_face_stencil() { - Some(b) => b, - None => { - b_owned = metal::StencilDescriptor::new(); - desc.set_front_face_stencil(Some(&b_owned)); - &b_owned - } - }; - - if let Some((fm, bm)) = front_back_read_masks_to_update { - front.set_read_mask(fm); - back.set_read_mask(bm); + let mut desc = match self.render_pso { + Some(ref ps) => ps.ds_desc.clone(), + None => return None, + }; + if let pso::StencilTest::On { ref mut front, ref mut back } = desc.stencil { + front.reference = pso::State::Dynamic; + if front.mask_read.is_dynamic() { + front.mask_read = pso::State::Static(self.stencil.front_read_mask); } - - if let Some((fm, bm)) = front_back_write_masks_to_update { - front.set_write_mask(fm); - back.set_write_mask(bm); + if front.mask_write.is_dynamic() { + front.mask_write = pso::State::Static(self.stencil.front_write_mask); } + back.reference = pso::State::Dynamic; + if back.mask_read.is_dynamic() { + back.mask_read = pso::State::Static(self.stencil.back_read_mask); + } + if back.mask_write.is_dynamic() { + back.mask_write = pso::State::Static(self.stencil.back_write_mask); + } + } - shared.device - .lock() - .unwrap() - .new_depth_stencil_state(&desc) - }); - - self.dynamic_depth_stencil_state - .as_ref() - .map(|ds| soft::RenderCommand::SetDepthStencilDesc(&**ds)) + let state = pipes.depth_stencil_states.get(desc, device); + Some(soft::RenderCommand::SetDepthStencilState(state)) } fn set_depth_bias<'a>(&mut self, depth_bias: &pso::DepthBias) -> soft::RenderCommand<&'a soft::Own> { @@ -437,25 +397,6 @@ impl State { soft::RenderCommand::SetDepthBias(*depth_bias) } - fn set_depth_stencil_desc<'a>( - &mut self, - depth_stencil_desc: &pso::DepthStencilDesc, - depth_stencil_raw: &'a metal::DepthStencilStateRef, - ) -> soft::RenderCommand<&'a soft::Own> { - self.pipeline_depth_stencil = Some((depth_stencil_desc.clone(), depth_stencil_raw.to_owned())); - soft::RenderCommand::SetDepthStencilDesc(depth_stencil_raw) - } - - fn set_stencil_reference_values<'a>( - &mut self, - front: pso::StencilValue, - back: pso::StencilValue, - ) -> soft::RenderCommand<&'a soft::Own> { - self.stencil.front_reference = front; - self.stencil.back_reference = back; - soft::RenderCommand::SetStencilReferenceValues(front, back) - } - fn push_vs_constants<'a>(&'a mut self, id: u32) -> soft::RenderCommand<&'a soft::Own>{ self.resources_vs.push_constants_buffer_id = Some(id); soft::RenderCommand::BindBufferData { @@ -980,8 +921,8 @@ fn exec_render<'a>(encoder: &metal::RenderCommandEncoderRef, command: soft::Rend Cmd::SetDepthBias(depth_bias) => { encoder.set_depth_bias(depth_bias.const_factor, depth_bias.slope_factor, depth_bias.clamp); } - Cmd::SetDepthStencilDesc(depth_stencil_desc) => { - encoder.set_depth_stencil_state(depth_stencil_desc); + Cmd::SetDepthStencilState(depth_stencil) => { + encoder.set_depth_stencil_state(depth_stencil); } Cmd::SetStencilReferenceValues(front, back) => { encoder.set_stencil_front_back_reference_value(front, back); @@ -1430,9 +1371,6 @@ impl pool::RawCommandPool for CommandPool { resources_cs: StageResources::new(), index_buffer: None, rasterizer_state: None, - pipeline_depth_stencil: None, - dynamic_depth_stencil_desc: None, - dynamic_depth_stencil_state: None, stencil: native::StencilState:: { front_reference: 0, back_reference: 0, @@ -1492,6 +1430,19 @@ fn set_operations(attachment: &metal::RenderPassAttachmentDescriptorRef, ops: At ops.load } +impl CommandBuffer { + fn update_depth_stencil(&self) { + let mut inner = self.inner.borrow_mut(); + let mut pre = inner.sink().pre_render(); + if !pre.is_void() { + let mut pipes = self.shared.service_pipes.lock().unwrap(); + if let Some(com) = self.state.sync_depth_stencil(&mut *pipes, &self.shared.device) { + pre.issue(com) + } + } + } +} + impl com::RawCommandBuffer for CommandBuffer { fn begin(&mut self, flags: com::CommandBufferFlags, _info: com::CommandBufferInheritanceInfo) { self.reset(false); @@ -1925,8 +1876,8 @@ impl com::RawCommandBuffer for CommandBuffer { let com_pso = iter::once(soft::RenderCommand::BindPipeline(clear_pso, None)); let com_ds = if !aspects.contains(Aspects::COLOR) { - Some(soft::RenderCommand::SetDepthStencilDesc( - depth_stencil_states.get(aspects) + Some(soft::RenderCommand::SetDepthStencilState( + depth_stencil_states.get_write(aspects) )) } else { None @@ -1947,9 +1898,9 @@ impl com::RawCommandBuffer for CommandBuffer { } // reset all the affected states - let com_pso = if let Some((ref pso, _, _)) = self.state.render_pso { + let com_pso = if let Some(ref ps) = self.state.render_pso { if self.state.render_pso_is_compatible { - Some(soft::RenderCommand::BindPipeline(&**pso, None)) + Some(soft::RenderCommand::BindPipeline(&*ps.raw, None)) } else { warn!("Not restoring the current PSO after clear_attachments because it's not compatible"); None @@ -1958,9 +1909,7 @@ impl com::RawCommandBuffer for CommandBuffer { None }; - let com_ds = self.state.pipeline_depth_stencil - .as_ref() - .map(|&(_, ref raw)| soft::RenderCommand::SetDepthStencilDesc(&**raw)); + let com_ds = self.state.sync_depth_stencil(&mut *pipes, &self.shared.device); let com_vs = if let Some(&Some((buffer, offset))) = self.state.resources_vs.buffers.first() { Some(soft::RenderCommand::BindBuffer { @@ -2154,8 +2103,8 @@ impl com::RawCommandBuffer for CommandBuffer { ]; let com_ds = if src.format_desc.aspects.intersects(Aspects::DEPTH | Aspects::STENCIL) { - Some(soft::RenderCommand::SetDepthStencilDesc( - depth_stencil_states.get(src.format_desc.aspects) + Some(soft::RenderCommand::SetDepthStencilState( + depth_stencil_states.get_write(src.format_desc.aspects) )) } else { None @@ -2351,15 +2300,17 @@ impl com::RawCommandBuffer for CommandBuffer { } fn set_stencil_reference(&mut self, faces: pso::Face, value: pso::StencilValue) { - assert!(!faces.is_empty()); - - let (front, back) = match faces { - pso::Face::FRONT => (value, self.state.stencil.back_reference), - pso::Face::BACK => (self.state.stencil.front_reference, value), - _ => (value, value), - }; + if faces.contains(pso::Face::FRONT) { + self.state.stencil.front_reference = value; + } + if faces.contains(pso::Face::BACK) { + self.state.stencil.back_reference = value; + } - let com = self.state.set_stencil_reference_values(front, back); + let com = soft::RenderCommand::SetStencilReferenceValues( + self.state.stencil.front_reference, + self.state.stencil.back_reference, + ); self.inner .borrow_mut() .sink() @@ -2368,39 +2319,23 @@ impl com::RawCommandBuffer for CommandBuffer { } fn set_stencil_read_mask(&mut self, faces: pso::Face, value: pso::StencilValue) { - assert!(!faces.is_empty()); - - let (front, back) = match faces { - pso::Face::FRONT => (value, self.state.stencil.back_read_mask), - pso::Face::BACK => (self.state.stencil.front_read_mask, value), - _ => (value, value), - }; - - if let Some(com) = self.state.set_stencil_mask_values(&self.shared, Some((front, back)), None, None) { - self.inner - .borrow_mut() - .sink() - .pre_render() - .issue(com); + if faces.contains(pso::Face::FRONT) { + self.state.stencil.front_read_mask = value; + } + if faces.contains(pso::Face::BACK) { + self.state.stencil.back_read_mask = value; } + self.update_depth_stencil(); } fn set_stencil_write_mask(&mut self, faces: pso::Face, value: pso::StencilValue) { - assert!(!faces.is_empty()); - - let (front, back) = match faces { - pso::Face::FRONT => (value, self.state.stencil.back_write_mask), - pso::Face::BACK => (self.state.stencil.front_write_mask, value), - _ => (value, value), - }; - - if let Some(com) = self.state.set_stencil_mask_values(&self.shared, None, Some((front, back)), None) { - self.inner - .borrow_mut() - .sink() - .pre_render() - .issue(com); + if faces.contains(pso::Face::FRONT) { + self.state.stencil.front_write_mask = value; } + if faces.contains(pso::Face::BACK) { + self.state.stencil.back_write_mask = value; + } + self.update_depth_stencil(); } fn begin_render_pass( @@ -2470,13 +2405,22 @@ impl com::RawCommandBuffer for CommandBuffer { } self.state.render_pso_is_compatible = match self.state.render_pso { - Some((_, _, ref formats)) => formats.len() == render_pass.attachments.len() && - formats.iter().zip(&render_pass.attachments).all(|(f, at)| *f == at.format), + Some(ref ps) => ps.at_formats.len() == render_pass.attachments.len() && + ps.at_formats.iter().zip(&render_pass.attachments).all(|(f, at)| *f == at.format), _ => false }; self.state.framebuffer_inner = framebuffer.inner.clone(); - let init_commands = self.state.make_render_commands(full_aspects); + let mut pipes = self.shared.service_pipes.lock().unwrap(); + let com_ds = if full_aspects.intersects(Aspects::DEPTH | Aspects::STENCIL) { + self.state.sync_depth_stencil(&mut *pipes, &self.shared.device) + } else { + None + }; + let init_commands = self.state + .make_render_commands(full_aspects) + .chain(com_ds); + inner .sink() .begin_render_pass(true, &*descriptor, init_commands); @@ -2494,23 +2438,74 @@ impl com::RawCommandBuffer for CommandBuffer { } fn bind_graphics_pipeline(&mut self, pipeline: &native::GraphicsPipeline) { + let mut ds_desc = pipeline.depth_stencil_desc.clone(); + let mut set_stencil_references = false; + match ds_desc.depth { + pso::DepthTest::On { .. } if !self.state.framebuffer_inner.aspects.contains(Aspects::DEPTH) => { + error!("Using {:?} with no depth attachment!", ds_desc); + ds_desc.depth = pso::DepthTest::Off; + } + pso::DepthTest::On { .. } => {} + pso::DepthTest::Off => {} + } + match ds_desc.stencil { + pso::StencilTest::On { .. } if !self.state.framebuffer_inner.aspects.contains(Aspects::STENCIL) => { + error!("Using {:?} with no stencil attachment!", ds_desc); + ds_desc.stencil = pso::StencilTest::Off; + } + pso::StencilTest::On { ref front, ref back } => { + if let pso::State::Static(value) = front.mask_read { + self.state.stencil.front_read_mask = value; + } + if let pso::State::Static(value) = front.mask_write { + self.state.stencil.front_write_mask = value; + } + if let pso::State::Static(value) = front.reference { + self.state.stencil.front_reference = value; + set_stencil_references = true; + } + if let pso::State::Static(value) = back.mask_read { + self.state.stencil.back_read_mask = value; + } + if let pso::State::Static(value) = back.mask_write { + self.state.stencil.back_write_mask = value; + } + if let pso::State::Static(value) = back.reference { + self.state.stencil.back_reference = value; + set_stencil_references = true; + } + } + pso::StencilTest::Off => {} + } + self.state.render_pso_is_compatible = true; //assume good intent :) - self.state.render_pso = Some(( - pipeline.raw.to_owned(), - pipeline.vertex_buffer_map.clone(), - pipeline.attachment_formats.clone(), - )); + self.state.render_pso = Some(RenderPipelineState { + raw: pipeline.raw.to_owned(), + ds_desc, + vbuf_map: pipeline.vertex_buffer_map.clone(), + at_formats: pipeline.attachment_formats.clone(), + }); self.state.rasterizer_state = pipeline.rasterizer_state.clone(); self.state.primitive_type = pipeline.primitive_type; let vertex_mask = self.state.set_vertex_buffers(); let mut inner = self.inner.borrow_mut(); let mut pre = inner.sink().pre_render(); + pre.issue(soft::RenderCommand::BindPipeline( &*pipeline.raw, pipeline.rasterizer_state.clone(), )); + let mut pipes = self.shared.service_pipes.lock().unwrap(); + pre.issue(self.state.sync_depth_stencil(&mut *pipes, &self.shared.device).unwrap()); + if set_stencil_references { + pre.issue(soft::RenderCommand::SetStencilReferenceValues( + self.state.stencil.front_reference, + self.state.stencil.back_reference, + )); + } + if let Some(ref vp) = pipeline.baked_states.viewport { pre.issue(self.state.set_viewport(vp, &self.shared.disabilities)); } @@ -2527,35 +2522,6 @@ impl com::RawCommandBuffer for CommandBuffer { pre.issue(command); } } - - let ds = &pipeline.depth_stencil_state; - if let Some(desc) = ds.depth_stencil_desc { - // If static stencil reference values were provided, update them here - // Otherwise, leave any dynamic stencil reference values bound - let front_ref = ds.stencil.front_reference.static_or(self.state.stencil.front_reference); - let back_ref = ds.stencil.back_reference.static_or(self.state.stencil.back_reference); - if ds.stencil.front_reference.is_static() || ds.stencil.back_reference.is_static() { - pre.issue(self.state.set_stencil_reference_values(front_ref, back_ref)); - } - - match ds.depth_stencil_static { - Some(ref raw) => pre.issue(self.state.set_depth_stencil_desc(&desc, raw)), - None => { - let front_r = ds.stencil.front_read_mask.static_or(self.state.stencil.front_read_mask); - let back_r = ds.stencil.back_read_mask.static_or(self.state.stencil.back_read_mask); - let front_w = ds.stencil.front_write_mask.static_or(self.state.stencil.front_write_mask); - let back_w = ds.stencil.back_write_mask.static_or(self.state.stencil.back_write_mask); - if let Some(com) = self.state.set_stencil_mask_values( - &self.shared, - Some((front_r, back_r)), - Some((front_w, back_w)), - ds.depth_stencil_desc_raw.as_ref().map(Borrow::borrow), - ) { - pre.issue(com); - } - } - }; - } } fn bind_graphics_descriptor_sets<'a, I, J>( diff --git a/src/backend/metal/src/device.rs b/src/backend/metal/src/device.rs index 96426050860..ae8b579b8a4 100644 --- a/src/backend/metal/src/device.rs +++ b/src/backend/metal/src/device.rs @@ -120,88 +120,6 @@ fn get_final_function(library: &metal::LibraryRef, entry: &str, specialization: Ok(mtl_function) } -fn create_depth_stencil_state( - device: &metal::DeviceRef, - desc: &pso::DepthStencilDesc, -) -> native::DepthStencilState { - let raw = metal::DepthStencilDescriptor::new(); - - let mut dss = native::DepthStencilState { - depth_stencil_desc: Some(*desc), - ..Default::default() - }; - - let mut all_masks_static = true; - - match desc.depth { - pso::DepthTest::On { fun, write } => { - raw.set_depth_compare_function(conv::map_compare_function(fun)); - raw.set_depth_write_enabled(write); - } - pso::DepthTest::Off => {} - } - match desc.stencil { - pso::StencilTest::On { ref front, ref back } => { - dss.stencil.front_reference = front.reference; - dss.stencil.back_reference = back.reference; - - let front_desc = metal::StencilDescriptor::new(); - front_desc.set_stencil_compare_function(conv::map_compare_function(front.fun)); - - dss.stencil.front_read_mask = front.mask_read; - match front.mask_read { - pso::State::Static(mr) => front_desc.set_read_mask(mr), - pso::State::Dynamic => all_masks_static = false, - } - - dss.stencil.front_write_mask = front.mask_write; - if let pso::State::Static(mw) = front.mask_write { - front_desc.set_write_mask(mw); - } else { - all_masks_static = false; - } - - front_desc.set_stencil_failure_operation(conv::map_stencil_op(front.op_fail)); - front_desc.set_depth_failure_operation(conv::map_stencil_op(front.op_depth_fail)); - front_desc.set_depth_stencil_pass_operation(conv::map_stencil_op(front.op_pass)); - - raw.set_front_face_stencil(Some(&front_desc)); - - let back_desc = metal::StencilDescriptor::new(); - back_desc.set_stencil_compare_function(conv::map_compare_function(back.fun)); - - dss.stencil.back_read_mask = back.mask_read; - match back.mask_read { - pso::State::Static(mr) => back_desc.set_read_mask(mr), - pso::State::Dynamic => all_masks_static = false, - } - - dss.stencil.back_write_mask = back.mask_write; - if let pso::State::Static(mw) = back.mask_write { - back_desc.set_write_mask(mw); - } else { - all_masks_static = false; - } - - back_desc.set_stencil_failure_operation(conv::map_stencil_op(back.op_fail)); - back_desc.set_depth_failure_operation(conv::map_stencil_op(back.op_depth_fail)); - back_desc.set_depth_stencil_pass_operation(conv::map_stencil_op(back.op_pass)); - - raw.set_back_face_stencil(Some(&back_desc)); - } - pso::StencilTest::Off => {} - } - - if all_masks_static { - // The depth stencil state will never be updated for this pipeline, so cache it - dss.depth_stencil_static = Some(device.new_depth_stencil_state(&raw)); - } - - dss.depth_stencil_desc_raw = Some(raw); - - dss -} - //#[derive(Clone)] pub struct Device { pub(crate) shared: Arc, @@ -960,9 +878,6 @@ impl hal::Device for Device { } } - let depth_stencil = pipeline_desc.depth_stencil; - let depth_stencil_state = create_depth_stencil_state(&device, &depth_stencil); - // Vertex buffers let vertex_descriptor = metal::VertexDescriptor::new(); let mut vertex_buffer_map = n::VertexBufferMap::default(); @@ -1057,6 +972,14 @@ impl hal::Device for Device { }, depth_bias: pipeline_desc.rasterizer.depth_bias.unwrap_or_default(), }); + + // prepare the depth-stencil state now + self.shared.service_pipes + .lock() + .unwrap() + .depth_stencil_states + .prepare(&pipeline_desc.depth_stencil, &*device); + let attachment_formats = pass_descriptor.main_pass.attachments .iter() .map(|at| at.format) @@ -1071,7 +994,7 @@ impl hal::Device for Device { primitive_type, attribute_buffer_index: pipeline_layout.attribute_buffer_index, rasterizer_state, - depth_stencil_state, + depth_stencil_desc: pipeline_desc.depth_stencil.clone(), baked_states: pipeline_desc.baked_states.clone(), vertex_buffer_map, attachment_formats, diff --git a/src/backend/metal/src/internal.rs b/src/backend/metal/src/internal.rs index 8783695df6e..8c44bdf5180 100644 --- a/src/backend/metal/src/internal.rs +++ b/src/backend/metal/src/internal.rs @@ -1,4 +1,7 @@ +use conversions as conv; + use metal; +use hal::pso; use hal::backend::FastHashMap; use hal::command::ClearColorRaw; use hal::format::{Aspects, ChannelType}; @@ -8,6 +11,7 @@ use std::mem; use std::path::Path; use std::sync::Mutex; + #[derive(Clone, Debug)] pub struct ClearVertex { pub pos: [f32; 4], @@ -101,44 +105,153 @@ impl SamplerStates { } pub struct DepthStencilStates { - write_depth: metal::DepthStencilState, - write_stencil: metal::DepthStencilState, - write_all: metal::DepthStencilState, + map: FastHashMap, + write_none: pso::DepthStencilDesc, + write_depth: pso::DepthStencilDesc, + write_stencil: pso::DepthStencilDesc, + write_all: pso::DepthStencilDesc, } impl DepthStencilStates { fn new(device: &metal::DeviceRef) -> Self { - let desc = metal::DepthStencilDescriptor::new(); - desc.set_depth_write_enabled(true); - desc.set_depth_compare_function(metal::MTLCompareFunction::Always); - let write_depth = device.new_depth_stencil_state(&desc); - let stencil_desc = metal::StencilDescriptor::new(); - stencil_desc.set_depth_stencil_pass_operation(metal::MTLStencilOperation::Replace); - desc.set_front_face_stencil(Some(&stencil_desc)); - desc.set_back_face_stencil(Some(&stencil_desc)); - let write_all = device.new_depth_stencil_state(&desc); - desc.set_depth_write_enabled(false); - let write_stencil = device.new_depth_stencil_state(&desc); + let write_none = pso::DepthStencilDesc { + depth: pso::DepthTest::Off, + depth_bounds: false, + stencil: pso::StencilTest::Off, + }; + let write_depth = pso::DepthStencilDesc { + depth: pso::DepthTest::On { + fun: pso::Comparison::Always, + write: true, + }, + depth_bounds: false, + stencil: pso::StencilTest::Off, + }; + let face = pso::StencilFace { + fun: pso::Comparison::Always, + mask_read: pso::State::Static(!0), + mask_write: pso::State::Static(!0), + op_fail: pso::StencilOp::Replace, + op_depth_fail: pso::StencilOp::Replace, + op_pass: pso::StencilOp::Replace, + reference: pso::State::Dynamic, //irrelevant + }; + let write_stencil = pso::DepthStencilDesc { + depth: pso::DepthTest::Off, + depth_bounds: false, + stencil: pso::StencilTest::On { + front: face, + back: face, + }, + }; + let write_all = pso::DepthStencilDesc { + depth: pso::DepthTest::On { + fun: pso::Comparison::Always, + write: true, + }, + depth_bounds: false, + stencil: pso::StencilTest::On { + front: face, + back: face, + }, + }; + + let mut map = FastHashMap::default(); + for desc in &[&write_none, &write_depth, &write_stencil, &write_all] { + let raw_desc = Self::create_desc(desc).unwrap(); + let raw = device.new_depth_stencil_state(&raw_desc); + map.insert(**desc, raw); + } DepthStencilStates { + map, + write_none, write_depth, write_stencil, write_all, } } - //TODO: return `Option` instead? - pub fn get(&self, aspects: Aspects) -> &metal::DepthStencilStateRef { - if aspects.contains(Aspects::DEPTH | Aspects::STENCIL) { + pub fn get( + &mut self, + desc: pso::DepthStencilDesc, + device: &Mutex, + ) -> &metal::DepthStencilStateRef { + self.map + .entry(desc) + .or_insert_with(|| { + let raw_desc = Self::create_desc(&desc) + .expect("Incomplete descriptor provided"); + device.lock().unwrap().new_depth_stencil_state(&raw_desc) + }) + } + + pub fn get_write(&self, aspects: Aspects) -> &metal::DepthStencilStateRef { + let key = if aspects.contains(Aspects::DEPTH | Aspects::STENCIL) { &self.write_all } else if aspects.contains(Aspects::DEPTH) { &self.write_depth } else if aspects.contains(Aspects::STENCIL) { &self.write_stencil } else { - panic!("Can't write nothing!") + &self.write_none + }; + self.map.get(key).unwrap() + } + + pub fn prepare(&mut self, desc: &pso::DepthStencilDesc, device: &metal::DeviceRef) { + use std::collections::hash_map::Entry; + + if let Entry::Vacant(e) = self.map.entry(*desc) { + if let Some(raw_desc) = Self::create_desc(desc) { + e.insert(device.new_depth_stencil_state(&raw_desc)); + } } } + + fn create_stencil(face: &pso::StencilFace) -> Option { + let desc = metal::StencilDescriptor::new(); + desc.set_stencil_compare_function(conv::map_compare_function(face.fun)); + desc.set_read_mask(match face.mask_read { + pso::State::Static(value) => value, + pso::State::Dynamic => return None, + }); + desc.set_write_mask(match face.mask_write { + pso::State::Static(value) => value, + pso::State::Dynamic => return None, + }); + desc.set_stencil_failure_operation(conv::map_stencil_op(face.op_fail)); + desc.set_depth_failure_operation(conv::map_stencil_op(face.op_depth_fail)); + desc.set_depth_stencil_pass_operation(conv::map_stencil_op(face.op_pass)); + Some(desc) + } + + fn create_desc(desc: &pso::DepthStencilDesc) -> Option { + let raw = metal::DepthStencilDescriptor::new(); + + match desc.depth { + pso::DepthTest::On { fun, write } => { + raw.set_depth_compare_function(conv::map_compare_function(fun)); + raw.set_depth_write_enabled(write); + } + pso::DepthTest::Off => {} + } + match desc.stencil { + pso::StencilTest::On { ref front, ref back } => { + let front_desc = Self::create_stencil(front)?; + raw.set_front_face_stencil(Some(&front_desc)); + let back_desc = if front == back { + front_desc + } else { + Self::create_stencil(back)? + }; + raw.set_back_face_stencil(Some(&back_desc)); + } + pso::StencilTest::Off => {} + } + + Some(raw) + } } diff --git a/src/backend/metal/src/lib.rs b/src/backend/metal/src/lib.rs index af3553e3ba0..59ba67642e6 100644 --- a/src/backend/metal/src/lib.rs +++ b/src/backend/metal/src/lib.rs @@ -5,7 +5,6 @@ extern crate cocoa; extern crate derivative; extern crate foreign_types; #[macro_use] extern crate objc; -extern crate io_surface; extern crate core_foundation; extern crate core_graphics; #[macro_use] extern crate log; diff --git a/src/backend/metal/src/native.rs b/src/backend/metal/src/native.rs index 89ed8bb840a..1d9bd45fb01 100644 --- a/src/backend/metal/src/native.rs +++ b/src/backend/metal/src/native.rs @@ -101,32 +101,6 @@ pub struct StencilState { pub back_write_mask: T, } -#[derive(Clone, Debug)] -pub struct DepthStencilState { - pub depth_stencil_desc: Option, - pub depth_stencil_desc_raw: Option, - pub depth_stencil_static: Option, - pub stencil: StencilState>, -} - -impl Default for DepthStencilState { - fn default() -> Self { - DepthStencilState { - depth_stencil_desc: None, - depth_stencil_desc_raw: None, - depth_stencil_static: None, - stencil: StencilState::> { - front_reference: pso::State::Static(0), - back_reference: pso::State::Static(0), - front_read_mask: pso::State::Static(!0), - back_read_mask: pso::State::Static(!0), - front_write_mask: pso::State::Static(!0), - back_write_mask: pso::State::Static(!0), - } - } - } -} - pub type VertexBufferMap = FastHashMap<(pso::BufferIndex, pso::ElemOffset), pso::VertexBufferDesc>; #[derive(Debug)] @@ -139,7 +113,7 @@ pub struct GraphicsPipeline { pub(crate) primitive_type: metal::MTLPrimitiveType, pub(crate) attribute_buffer_index: u32, pub(crate) rasterizer_state: Option, - pub(crate) depth_stencil_state: DepthStencilState, + pub(crate) depth_stencil_desc: pso::DepthStencilDesc, pub(crate) baked_states: pso::BakedStates, /// The mapping of additional vertex buffer bindings over the original ones. /// This is needed because Vulkan allows attribute offsets to exceed the strides, @@ -147,7 +121,7 @@ pub struct GraphicsPipeline { /// adjusted offsets to cover this use case. pub(crate) vertex_buffer_map: VertexBufferMap, /// Tracked attachment formats for figuring (roughly) renderpass compatibility. - pub(crate) attachment_formats: Vec>, + pub(crate) attachment_formats: SmallVec<[Option; 8]>, } unsafe impl Send for GraphicsPipeline {} diff --git a/src/backend/metal/src/soft.rs b/src/backend/metal/src/soft.rs index f86ad62ecf5..c9a7b9b8b52 100644 --- a/src/backend/metal/src/soft.rs +++ b/src/backend/metal/src/soft.rs @@ -47,7 +47,7 @@ pub enum RenderCommand { SetScissor(metal::MTLScissorRect), SetBlendColor(hal::pso::ColorValue), SetDepthBias(hal::pso::DepthBias), - SetDepthStencilDesc(R::DepthStencil), + SetDepthStencilState(R::DepthStencil), SetStencilReferenceValues(hal::pso::StencilValue, hal::pso::StencilValue), BindBuffer { stage: hal::pso::Stage, @@ -107,7 +107,7 @@ impl RenderCommand { SetScissor(rect) => SetScissor(rect), SetBlendColor(color) => SetBlendColor(color), SetDepthBias(bias) => SetDepthBias(bias), - SetDepthStencilDesc(ref desc) => SetDepthStencilDesc(&**desc), + SetDepthStencilState(ref state) => SetDepthStencilState(&**state), SetStencilReferenceValues(front, back) => SetStencilReferenceValues(front, back), BindBuffer { stage, index, buffer, offset } => BindBuffer { stage, @@ -166,7 +166,7 @@ impl<'a> RenderCommand<&'a Own> { SetScissor(rect) => SetScissor(rect), SetBlendColor(color) => SetBlendColor(color), SetDepthBias(bias) => SetDepthBias(bias), - SetDepthStencilDesc(desc) => SetDepthStencilDesc(desc.to_owned()), + SetDepthStencilState(state) => SetDepthStencilState(state.to_owned()), SetStencilReferenceValues(front, back) => SetStencilReferenceValues(front, back), BindBuffer { stage, index, buffer, offset } => BindBuffer { stage,