From 4c7a4ac74fecd6d4904c5ffdec8bde4c587e699f Mon Sep 17 00:00:00 2001 From: king6cong Date: Thu, 21 Jun 2018 18:17:09 +0800 Subject: [PATCH 1/3] Update example instructions in README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 04837745161..ff1b90face7 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,12 @@ To run an example, simply use `cargo run` and specify the backend with `--featur ```bash git clone https://github.com/gfx-rs/gfx -cd gfx/examples/hal +cd gfx/examples +# macOS +cargo run --bin quad --features metal +# vulkan cargo run --bin quad --features vulkan +# Windows cargo run --bin compute --features dx12 1 2 3 4 ``` From 90fd19391e347aeda447027bc1e15bbbdb3dd41f Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 20 Jun 2018 21:24:20 -0400 Subject: [PATCH 2/3] [mtl] Borrowed commands --- src/backend/metal/src/command.rs | 1541 +++++++++++++++-------------- src/backend/metal/src/device.rs | 3 +- src/backend/metal/src/internal.rs | 202 ++-- src/backend/metal/src/native.rs | 49 +- src/backend/metal/src/soft.rs | 355 ++++++- 5 files changed, 1308 insertions(+), 842 deletions(-) diff --git a/src/backend/metal/src/command.rs b/src/backend/metal/src/command.rs index dec0b5083a0..05fdc81994f 100644 --- a/src/backend/metal/src/command.rs +++ b/src/backend/metal/src/command.rs @@ -1,6 +1,6 @@ -use {AutoreleasePool, Backend, Shared, validate_line_width}; +use {AutoreleasePool, Backend, PrivateDisabilities, Shared, validate_line_width}; use {conversions as conv, native, soft, window}; -use internal::{BlitVertex, Channel, ClearKey, ClearVertex}; +use internal::{BlitVertex, Channel, ClearKey, ClearVertex, ServicePipes}; use std::borrow::Borrow; use std::cell::RefCell; @@ -13,7 +13,7 @@ use hal::{buffer, command as com, error, memory, pool, pso}; use hal::{DrawCount, FrameImage, VertexCount, VertexOffset, InstanceCount, IndexCount, WorkGroupCount}; use hal::backend::FastHashMap; use hal::format::{Aspects, Format, FormatDesc}; -use hal::image::{Extent, Filter, Layout, SubresourceRange}; +use hal::image::{Extent, Filter, Layout, Level, SubresourceRange}; use hal::pass::{AttachmentLoadOp, AttachmentOps}; use hal::query::{Query, QueryControl, QueryId}; use hal::queue::{RawCommandQueue, RawSubmission}; @@ -26,7 +26,8 @@ use block::{ConcreteBlock}; use smallvec::SmallVec; -const WORD_ALIGNMENT: u64 = 4; +const WORD_SIZE: usize = 4; +const WORD_ALIGNMENT: u64 = WORD_SIZE as _; /// Enable an optimization to have multi-layered render passed /// with clear operations set up to implement our `clear_image` /// Note: currently doesn't work, needs a repro case for Apple @@ -103,6 +104,13 @@ pub struct CommandBuffer { inner: CommandBufferInnerPtr, shared: Arc, state: State, + temp: Temp, +} + +#[derive(Clone)] +struct Temp { + clear_vertices: Vec, + blit_vertices: FastHashMap<(Aspects, Level), Vec>, } #[derive(Clone)] @@ -120,7 +128,7 @@ struct State { resources_vs: StageResources, resources_fs: StageResources, resources_cs: StageResources, - index_buffer: Option, + index_buffer: Option>, rasterizer_state: Option, pipeline_depth_stencil: Option<(pso::DepthStencilDesc, metal::DepthStencilState)>, dynamic_depth_stencil_desc: Option, @@ -140,141 +148,385 @@ impl State { self.vertex_buffers.clear(); } - fn clamp_scissor(&self, sr: MTLScissorRect) -> MTLScissorRect { - let ex = self.framebuffer_inner.extent; + fn clamp_scissor(sr: MTLScissorRect, extent: Extent) -> MTLScissorRect { // sometimes there is not even an active render pass at this point - let x = sr.x.min(ex.width.max(1) as u64 - 1); - let y = sr.y.min(ex.height.max(1) as u64 - 1); + let x = sr.x.min(extent.width.max(1) as u64 - 1); + let y = sr.y.min(extent.height.max(1) as u64 - 1); //TODO: handle the zero scissor size sensibly MTLScissorRect { x, y, - width: ((sr.x + sr.width).min(ex.width as u64) - x).max(1), - height: ((sr.y + sr.height).min(ex.height as u64) - y).max(1), + width: ((sr.x + sr.width).min(extent.width as u64) - x).max(1), + height: ((sr.y + sr.height).min(extent.height as u64) - y).max(1), } } - fn make_render_commands(&self, aspects: Aspects) -> Vec { - // TODO: re-use storage - let mut commands = Vec::new(); + fn make_render_commands<'a>(&'a self, aspects: Aspects) -> impl Iterator> { // Apply previously bound values for this command buffer - commands.extend(self.viewport.map(soft::RenderCommand::SetViewport)); - if let Some(sr) = self.scissors { - let clamped = self.clamp_scissor(sr); - commands.push(soft::RenderCommand::SetScissor(clamped)); - } - if aspects.contains(Aspects::COLOR) { - commands.extend(self.blend_color.map(soft::RenderCommand::SetBlendColor)); - } - if aspects.contains(Aspects::DEPTH) { - commands.push(soft::RenderCommand::SetDepthBias( - self.rasterizer_state.clone().map(|r| r.depth_bias).unwrap_or_default() - )); - } - if self.render_pso_is_compatible { + let com_vp = self.viewport.map(soft::RenderCommand::SetViewport); + let com_scissor = self.scissors.map(|sr| soft::RenderCommand::SetScissor( + Self::clamp_scissor(sr, self.framebuffer_inner.extent) + )); + let com_blend = if aspects.contains(Aspects::COLOR) { + self.blend_color.map(soft::RenderCommand::SetBlendColor) + } else { + None + }; + let com_depth_bias = if aspects.contains(Aspects::DEPTH) { + Some(soft::RenderCommand::SetDepthBias( + self.rasterizer_state.as_ref().map(|r| r.depth_bias).unwrap_or_default() + )) + } else { + None + }; + let com_pso = if self.render_pso_is_compatible { let rast = self.rasterizer_state.clone(); - commands.extend(self.render_pso.as_ref().map(|&(ref pso, _, _)| { - soft::RenderCommand::BindPipeline(pso.clone(), rast) - })); - } + self.render_pso.as_ref().map(|&(ref pso, _, _)| { + soft::RenderCommand::BindPipeline(&**pso, rast) + }) + } else { + None + }; - let com = if let Some((_, ref static_state)) = self.pipeline_depth_stencil { - Some(static_state.clone()) - } else if let Some(ref dynamic_state) = self.dynamic_depth_stencil_state { - Some(dynamic_state.clone()) + 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 }; - if aspects.intersects(Aspects::DEPTH | Aspects::STENCIL) { - commands.extend(com.map(soft::RenderCommand::SetDepthStencilDesc)); - } - let stages = [pso::Stage::Vertex, pso::Stage::Fragment]; - for (&stage, resources) in stages.iter().zip(&[&self.resources_vs, &self.resources_fs]) { - commands.extend(resources.buffers.iter().enumerate().filter_map(|(i, resource)| { - resource.clone().map(|(buffer, offset)| { - soft::RenderCommand::BindBuffer { + 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] + .iter() + .zip(render_resources) + .flat_map(move |(&stage, resources)| { + let com_buffers = resources.buffers.iter().enumerate().filter_map(move |(i, resource)| { + resource.as_ref().map(|&(ref buffer, offset)| { + soft::RenderCommand::BindBuffer { + stage, + index: i as _, + buffer: Some(&**buffer), + offset, + } + }) + }); + let com_textures = resources.textures.iter().enumerate().filter_map(move |(i, resource)| { + resource.as_ref().map(|texture| { + soft::RenderCommand::BindTexture { + stage, + index: i as _, + texture: Some(texture.as_ref()), + } + }) + }); + let com_samplers = resources.samplers.iter().enumerate().filter_map(move |(i, resource)| { + resource.as_ref().map(|sampler| { + soft::RenderCommand::BindSampler { + stage, + index: i as _, + sampler: Some(&**sampler), + } + }) + }); + let com_push_constants = resources.push_constants_buffer_id + .map(|id| soft::RenderCommand::BindBufferData { stage, + index: id as _, + words: push_constants, + }); + com_buffers + .chain(com_textures) + .chain(com_samplers) + .chain(com_push_constants) + }); + + com_vp + .into_iter() + .chain(com_scissor) + .chain(com_blend) + .chain(com_depth_bias) + .chain(com_pso) + .chain(com_ds) + .chain(com_resources) + } + + fn make_compute_commands<'a>(&'a self) -> impl Iterator> { + let com_pso = self.compute_pso + .as_ref() + .map(|pso| soft::ComputeCommand::BindPipeline(&**pso)); + let com_buffers = self.resources_cs.buffers + .iter() + .enumerate() + .filter_map(|(i, resource)| { + resource.as_ref().map(|&(ref buffer, offset)| { + soft::ComputeCommand::BindBuffer { index: i as _, - buffer: Some(buffer), + buffer: Some(&**buffer), offset, } }) - })); - commands.extend(resources.textures - .iter() - .cloned() - .enumerate() - .filter(|&(_, ref resource)| resource.is_some()) - .map(|(i, texture)| soft::RenderCommand::BindTexture { - stage, - index: i as _, - texture, - }) - ); - commands.extend(resources.samplers - .iter() - .cloned() - .enumerate() - .filter(|&(_, ref resource)| resource.is_some()) - .map(|(i, sampler)| soft::RenderCommand::BindSampler { - stage, - index: i as _, - sampler, + }); + let com_textures = self.resources_cs.textures + .iter() + .enumerate() + .filter_map(|(i, ref resource)| { + resource.as_ref().map(|texture| { + soft::ComputeCommand::BindTexture { + index: i as _, + texture: Some(texture.as_ref()), + } }) - ); - commands.extend(resources.push_constants_buffer_id - .map(|id| soft::RenderCommand::BindBufferData { - stage, - index: id as _, - bytes: soft::push_data(&self.push_constants), + }); + let com_samplers = self.resources_cs.samplers + .iter() + .enumerate() + .filter_map(|(i, ref resource)| { + resource.as_ref().map(|sampler| { + soft::ComputeCommand::BindSampler { + index: i as _, + sampler: Some(&**sampler), + } }) - ); - } - commands + }); + let com_push_constants = self.resources_cs.push_constants_buffer_id + .map(|id| soft::ComputeCommand::BindBufferData { + index: id as _, + words: self.push_constants.as_slice(), + }); + + com_pso + .into_iter() + .chain(com_buffers) + .chain(com_textures) + .chain(com_samplers) + .chain(com_push_constants) } - fn make_compute_commands(&self) -> Vec { - let mut commands = Vec::new(); + fn set_vertex_buffers(&mut self) -> u64 { + let map = match self.render_pso { + Some((_, ref map, _)) => map, + None => return 0 + }; - commands.extend(self.compute_pso.clone().map(soft::ComputeCommand::BindPipeline)); - commands.extend(self.resources_cs.buffers.iter().enumerate().filter_map(|(i, resource)| { - resource.clone().map(|(buffer, offset)| { - soft::ComputeCommand::BindBuffer { - index: i as _, - buffer: Some(buffer), - offset, + let vs_buffers = &mut self.resources_vs.buffers; + let mut mask = 0; + for (&(binding, extra_offset), vb) in map { + let index = vb.binding as usize; + while vs_buffers.len() <= index { + vs_buffers.push(None) + } + let (buffer, offset) = match self.vertex_buffers.get(binding as usize) { + Some(&Some((ref buffer, base_offset))) => (buffer, extra_offset as u64 + base_offset), + // being unable to bind a buffer here is technically fine, since before this moment + // and actual rendering there might be more bind calls + _ => continue, + }; + + if let Some((ref old_buffer, old_offset)) = vs_buffers[index] { + if old_buffer.as_ptr() == buffer.as_ptr() && old_offset == offset { + continue; // already bound } - }) - })); - commands.extend(self.resources_cs.textures - .iter() - .cloned() - .enumerate() - .filter(|&(_, ref resource)| resource.is_some()) - .map(|(i, texture)| soft::ComputeCommand::BindTexture { - index: i as _, - texture, - }) - ); - commands.extend(self.resources_cs.samplers + } + vs_buffers[index] = Some((buffer.clone(), offset)); + mask |= 1<(&'a self, mask: u64) -> impl Iterator> { + self.resources_vs.buffers .iter() - .cloned() .enumerate() - .filter(|&(_, ref resource)| resource.is_some()) - .map(|(i, sampler)| soft::ComputeCommand::BindSampler { - index: i as _, - sampler, - }) - ); - commands.extend(self.resources_cs.push_constants_buffer_id - .map(|id| soft::ComputeCommand::BindBufferData { - index: id as _, - bytes: soft::push_data(&self.push_constants), + .filter_map(move |(index, maybe_buffer)| { + if mask & (1u64 << index) != 0 { + maybe_buffer.as_ref().map(|&(ref buffer, offset)| { + soft::RenderCommand::BindBuffer { + stage: pso::Stage::Vertex, + index, + buffer: Some(&**buffer), + offset, + } + }) + } else { + None + } }) - ); + } + + 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>, + ) -> 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); + } + + if let Some((fm, bm)) = front_back_write_masks_to_update { + front.set_write_mask(fm); + back.set_write_mask(bm); + } + + shared.device + .lock() + .unwrap() + .new_depth_stencil_state(&desc) + }); + + self.dynamic_depth_stencil_state + .as_ref() + .map(|ds| soft::RenderCommand::SetDepthStencilDesc(&**ds)) + } + + fn set_depth_bias<'a>(&mut self, depth_bias: &pso::DepthBias) -> soft::RenderCommand<&'a soft::Own> { + if let Some(ref mut r) = self.rasterizer_state { + r.depth_bias = *depth_bias; + } else { + self.rasterizer_state = Some(native::RasterizerState { + depth_bias: *depth_bias, + ..Default::default() + }); + } + 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 { + stage: pso::Stage::Vertex, + index: id as usize, + words: &self.push_constants, + } + } + + fn push_ps_constants<'a>(&'a mut self, id: u32) -> soft::RenderCommand<&'a soft::Own> { + self.resources_fs.push_constants_buffer_id = Some(id); + soft::RenderCommand::BindBufferData { + stage: pso::Stage::Fragment, + index: id as usize, + words: &self.push_constants, + } + } + + fn push_cs_constants<'a>(&'a mut self, id: u32) -> soft::ComputeCommand<&'a soft::Own> { + self.resources_cs.push_constants_buffer_id = Some(id); + soft::ComputeCommand::BindBufferData { + index: id as usize, + words: &self.push_constants, + } + } + + fn set_viewport<'a>( + &mut self, vp: &'a pso::Viewport, disabilities: &PrivateDisabilities + ) -> soft::RenderCommand<&'a soft::Own> { + let viewport = MTLViewport { + originX: vp.rect.x as _, + originY: vp.rect.y as _, + width: vp.rect.w as _, + height: vp.rect.h as _, + znear: vp.depth.start as _, + zfar: if disabilities.broken_viewport_near_depth { + (vp.depth.end - vp.depth.start) as _ + } else { + vp.depth.end as _ + }, + }; + self.viewport = Some(viewport); + soft::RenderCommand::SetViewport(viewport) + } + + fn set_scissor<'a>(&mut self, rect: &'a pso::Rect) -> soft::RenderCommand<&'a soft::Own> { + let scissor = MTLScissorRect { + x: rect.x as _, + y: rect.y as _, + width: rect.w as _, + height: rect.h as _, + }; + self.scissors = Some(scissor); + let clamped = State::clamp_scissor(scissor, self.framebuffer_inner.extent); + soft::RenderCommand::SetScissor(clamped) + } - commands + fn set_blend_color<'a>(&mut self, color: &'a pso::ColorValue) -> soft::RenderCommand<&'a soft::Own> { + self.blend_color = Some(*color); + soft::RenderCommand::SetBlendColor(*color) + } + + fn update_push_constants( + &mut self, + offset: u32, + constants: &[u32], + ) { + assert_eq!(offset % WORD_ALIGNMENT as u32, 0); + let offset = (offset / WORD_ALIGNMENT as u32) as usize; + let data = &mut self.push_constants; + while data.len() < offset + constants.len() { + data.push(0); + } + data[offset .. offset + constants.len()].copy_from_slice(constants); } } @@ -347,19 +599,19 @@ impl CommandSink { /// a render pass being actively encoded. /// The caller is expected to change the cached state accordingly, so these commands /// are going to be issued when a next pass starts, if not at this very moment. - fn pre_render_commands(&mut self, commands: I) + fn pre_render_commands<'a, I>(&mut self, commands: I) where - I: IntoIterator, + I: IntoIterator>, { match *self { CommandSink::Immediate { encoder_state: EncoderState::Render(ref encoder), .. } => { for command in commands { - exec_render(encoder, &command); + exec_render(encoder, command); } } CommandSink::Deferred { ref mut passes, is_encoding: true } => { if let Some(&mut soft::Pass::Render { commands: ref mut list, .. }) = passes.last_mut() { - list.extend(commands); + list.extend(commands.into_iter().map(soft::RenderCommand::own)); } } _ => {} @@ -367,16 +619,16 @@ impl CommandSink { } /// Issue provided render commands, expecting that we are encoding a render pass. - fn render_commands(&mut self, commands: I) + fn render_commands<'a, I>(&mut self, commands: I) where - I: Iterator, + I: Iterator>, { match *self { CommandSink::Immediate { ref mut encoder_state, .. } => { match *encoder_state { EncoderState::Render(ref encoder) => { for command in commands { - exec_render(encoder, &command); + exec_render(encoder, command); } } _ => panic!("Expected to be in render encoding state!") @@ -386,7 +638,7 @@ impl CommandSink { assert!(is_encoding); match passes.last_mut() { Some(&mut soft::Pass::Render { commands: ref mut list, .. }) => { - list.extend(commands); + list.extend(commands.into_iter().map(soft::RenderCommand::own)); } _ => panic!("Active pass is not a render pass") } @@ -396,9 +648,9 @@ impl CommandSink { /// Issue provided blit commands. This function doesn't expect an active blit pass, /// it will automatically start one when needed. - fn blit_commands(&mut self, commands: I) + fn blit_commands<'a, I>(&mut self, commands: I) where - I: Iterator, + I: Iterator>, { match *self { CommandSink::Immediate { ref cmd_buffer, ref mut encoder_state, .. } => { @@ -419,17 +671,18 @@ impl CommandSink { }); for command in commands { - exec_blit(&encoder, &command); + exec_blit(&encoder, command); } *encoder_state = EncoderState::Blit(encoder); } CommandSink::Deferred { ref mut passes, .. } => { + let owned_commands = commands.into_iter().map(soft::BlitCommand::own); if let Some(&mut soft::Pass::Blit(ref mut list)) = passes.last_mut() { - list.extend(commands); + list.extend(owned_commands); return; } - passes.push(soft::Pass::Blit(commands.collect())); + passes.push(soft::Pass::Blit(owned_commands.collect())); } } } @@ -438,19 +691,19 @@ impl CommandSink { /// a compute pass being actively encoded. /// The caller is expected to change the cached state accordingly, so these commands /// are going to be issued when a next pass starts, if not at this very moment. - fn pre_compute_commands(&mut self, commands: I) + fn pre_compute_commands<'a, I>(&mut self, commands: I) where - I: IntoIterator, + I: IntoIterator>, { match *self { CommandSink::Immediate { encoder_state: EncoderState::Compute(ref encoder), .. } => { for command in commands { - exec_compute(encoder, &command); + exec_compute(encoder, command); } } CommandSink::Deferred { ref mut passes, is_encoding: true } => { if let Some(&mut soft::Pass::Compute(ref mut list)) = passes.last_mut() { - list.extend(commands); + list.extend(commands.into_iter().map(soft::ComputeCommand::own)); } } _ => {} @@ -458,27 +711,28 @@ impl CommandSink { } /// Issue provided compute commands, expecting that we are encoding a compute pass. - fn compute_commands(&mut self, commands: I) + fn compute_commands<'a, I>(&mut self, commands: I) where - I: Iterator, + I: Iterator>, { match *self { CommandSink::Immediate { ref mut encoder_state, .. } => { match *encoder_state { EncoderState::Compute(ref encoder) => { for command in commands { - exec_compute(encoder, &command); + exec_compute(encoder, command); } } _ => panic!("Expected to be in compute pass"), } } CommandSink::Deferred { ref mut passes, .. } => { + let owned_commands = commands.into_iter().map(soft::ComputeCommand::own); if let Some(&mut soft::Pass::Compute(ref mut list)) = passes.last_mut() { - list.extend(commands); + list.extend(owned_commands); return; } - passes.push(soft::Pass::Compute(commands.collect())); + passes.push(soft::Pass::Compute(owned_commands.collect())); } } } @@ -494,98 +748,97 @@ impl CommandSink { } } - fn quick_render_pass( + fn begin_render_pass<'a, F, I>( &mut self, - descriptor: &metal::RenderPassDescriptorRef, - frames: I, - commands: J, + keep_open: bool, + descriptor: &'a metal::RenderPassDescriptorRef, + frames: F, + init_commands: I, ) where - I: IntoIterator, - J: IntoIterator, + F: Iterator, + I: Iterator>, { self.stop_encoding(); match *self { - CommandSink::Immediate { ref cmd_buffer, .. } => { + CommandSink::Immediate { ref cmd_buffer, ref mut encoder_state, .. } => { let _ap = AutoreleasePool::new(); resolve_frames(descriptor, frames); let encoder = cmd_buffer.new_render_command_encoder(descriptor); - for command in commands { - exec_render(encoder, &command); + for command in init_commands { + exec_render(encoder, command); + } + if keep_open { + *encoder_state = EncoderState::Render(encoder.to_owned()); + } else { + encoder.end_encoding(); } - encoder.end_encoding(); } - CommandSink::Deferred { ref mut passes, .. } => { + CommandSink::Deferred { ref mut passes, ref mut is_encoding } => { + *is_encoding = keep_open; passes.push(soft::Pass::Render { desc: descriptor.to_owned(), - frames: frames.into_iter().collect(), - commands: commands.into_iter().collect(), + frames: frames.collect(), + commands: init_commands.map(soft::RenderCommand::own).collect(), }); } } } - fn begin_render_pass( + fn begin_compute_pass<'a, I>( &mut self, - descriptor: metal::RenderPassDescriptor, - frames: I, - init_commands: Vec, + init_commands: I, ) where - I: Iterator, + I: Iterator>, { self.stop_encoding(); - match *self { - CommandSink::Immediate { ref cmd_buffer, ref mut encoder_state, .. } => { - let _ap = AutoreleasePool::new(); - resolve_frames(&descriptor, frames); - let encoder = cmd_buffer.new_render_command_encoder(&descriptor); - for command in init_commands { - exec_render(encoder, &command); - } - *encoder_state = EncoderState::Render(encoder.to_owned()); - } - CommandSink::Deferred { ref mut passes, ref mut is_encoding } => { - *is_encoding = true; - passes.push(soft::Pass::Render { - desc: descriptor, - frames: frames.into_iter().collect(), - commands: init_commands, - }); - } - } - } - - fn begin_compute_pass( - &mut self, - init_commands: Vec, - ) { - self.stop_encoding(); - match *self { CommandSink::Immediate { ref cmd_buffer, ref mut encoder_state, .. } => { let _ap = AutoreleasePool::new(); let encoder = cmd_buffer.new_compute_command_encoder(); for command in init_commands { - exec_compute(encoder, &command); + exec_compute(encoder, command); } *encoder_state = EncoderState::Compute(encoder.to_owned()); } CommandSink::Deferred { ref mut passes, ref mut is_encoding } => { *is_encoding = true; - passes.push(soft::Pass::Compute(init_commands)); + passes.push(soft::Pass::Compute( + init_commands.map(soft::ComputeCommand::own).collect(), + )); } } } } #[derive(Clone, Debug)] -pub struct IndexBuffer { - buffer: metal::Buffer, +pub struct IndexBuffer { + buffer: B, offset: buffer::Offset, index_type: MTLIndexType, } +impl IndexBuffer { + pub fn as_ref<'a>(&'a self) -> IndexBuffer<&'a metal::BufferRef> { + IndexBuffer { + buffer: &*self.buffer, + offset: self.offset, + index_type: self.index_type, + } + } +} + +impl<'a> IndexBuffer<&'a metal::BufferRef> { + pub fn own(self) -> IndexBuffer { + IndexBuffer { + buffer: self.buffer.to_owned(), + offset: self.offset, + index_type: self.index_type, + } + } +} + pub struct CommandBufferInner { sink: Option, retained_buffers: Vec, @@ -667,9 +920,9 @@ fn compute_pitches( (row_pitch, slice_pitch) } -fn exec_render(encoder: &metal::RenderCommandEncoderRef, command: &soft::RenderCommand) { +fn exec_render<'a>(encoder: &metal::RenderCommandEncoderRef, command: soft::RenderCommand<&'a soft::Own>) { use soft::RenderCommand as Cmd; - match *command { + match command { Cmd::SetViewport(viewport) => { encoder.set_viewport(viewport); } @@ -682,14 +935,13 @@ fn exec_render(encoder: &metal::RenderCommandEncoderRef, command: &soft::RenderC Cmd::SetDepthBias(depth_bias) => { encoder.set_depth_bias(depth_bias.const_factor, depth_bias.slope_factor, depth_bias.clamp); } - Cmd::SetDepthStencilDesc(ref depth_stencil_desc) => { + Cmd::SetDepthStencilDesc(depth_stencil_desc) => { encoder.set_depth_stencil_state(depth_stencil_desc); } Cmd::SetStencilReferenceValues(front, back) => { encoder.set_stencil_front_back_reference_value(front, back); } - Cmd::BindBuffer { stage, index, ref buffer, offset } => { - let buffer = buffer.as_ref().map(|x| x.as_ref()); + Cmd::BindBuffer { stage, index, buffer, offset } => { match stage { pso::Stage::Vertex => encoder.set_vertex_buffer(index as _, offset as _, buffer), @@ -698,19 +950,19 @@ fn exec_render(encoder: &metal::RenderCommandEncoderRef, command: &soft::RenderC _ => unimplemented!() } } - Cmd::BindBufferData { stage, ref bytes, index } => { + Cmd::BindBufferData { stage, index, words } => { match stage { pso::Stage::Vertex => - encoder.set_vertex_bytes(index as _, bytes.len() as _, bytes.as_ptr() as _), + encoder.set_vertex_bytes(index as _, (words.len() * WORD_SIZE) as u64, words.as_ptr() as _), pso::Stage::Fragment => - encoder.set_fragment_bytes(index as _, bytes.len() as _, bytes.as_ptr() as _), + encoder.set_fragment_bytes(index as _, (words.len() * WORD_SIZE) as u64, words.as_ptr() as _), _ => unimplemented!() } } - Cmd::BindTexture { stage, index, ref texture } => { + Cmd::BindTexture { stage, index, texture } => { let guard; let texture = match texture { - Some(ref root) => { + Some(root) => { guard = root.resolve(); Some(&*guard) } @@ -724,8 +976,7 @@ fn exec_render(encoder: &metal::RenderCommandEncoderRef, command: &soft::RenderC _ => unimplemented!() } } - Cmd::BindSampler { stage, index, ref sampler } => { - let sampler = sampler.as_ref().map(|x| x.as_ref()); + Cmd::BindSampler { stage, index, sampler } => { match stage { pso::Stage::Vertex => encoder.set_vertex_sampler_state(index as _, sampler), @@ -734,15 +985,15 @@ fn exec_render(encoder: &metal::RenderCommandEncoderRef, command: &soft::RenderC _ => unimplemented!() } } - Cmd::BindPipeline(ref pipeline_state, ref rasterizer) => { + Cmd::BindPipeline(pipeline_state, rasterizer) => { encoder.set_render_pipeline_state(pipeline_state); - if let Some(ref rasterizer_state) = *rasterizer { + if let Some(rasterizer_state) = rasterizer { encoder.set_depth_clip_mode(rasterizer_state.depth_clip); let db = rasterizer_state.depth_bias; encoder.set_depth_bias(db.const_factor, db.slope_factor, db.clamp); } } - Cmd::Draw { primitive_type, ref vertices, ref instances } => { + Cmd::Draw { primitive_type, vertices, instances } => { /*if instances.start == 0 { //TODO: needs metal-rs breaking update encoder.draw_primitives_instanced( primitive_type, @@ -760,7 +1011,7 @@ fn exec_render(encoder: &metal::RenderCommandEncoderRef, command: &soft::RenderC ); } } - Cmd::DrawIndexed { primitive_type, ref index, ref indices, base_vertex, ref instances } => { + Cmd::DrawIndexed { primitive_type, index, indices, base_vertex, instances } => { let index_size = match index.index_type { MTLIndexType::UInt16 => 2, MTLIndexType::UInt32 => 4, @@ -773,7 +1024,7 @@ fn exec_render(encoder: &metal::RenderCommandEncoderRef, command: &soft::RenderC primitive_type, (indices.end - indices.start) as NSUInteger, index.index_type, - &index.buffer, + index.buffer, index_offset, instances.end as NSUInteger, ); @@ -783,7 +1034,7 @@ fn exec_render(encoder: &metal::RenderCommandEncoderRef, command: &soft::RenderC primitive_type, (indices.end - indices.start) as NSUInteger, index.index_type, - &index.buffer, + index.buffer, index_offset, (instances.end - instances.start) as NSUInteger, base_vertex as NSInteger, @@ -791,18 +1042,18 @@ fn exec_render(encoder: &metal::RenderCommandEncoderRef, command: &soft::RenderC ); } } - Cmd::DrawIndirect { primitive_type, ref buffer, offset } => { + Cmd::DrawIndirect { primitive_type, buffer, offset } => { encoder.draw_primitives_indirect( primitive_type, buffer, offset, ); } - Cmd::DrawIndexedIndirect { primitive_type, ref index, ref buffer, offset } => { + Cmd::DrawIndexedIndirect { primitive_type, index, buffer, offset } => { encoder.draw_indexed_primitives_indirect( primitive_type, index.index_type, - &index.buffer, + index.buffer, index.offset, buffer, offset, @@ -811,10 +1062,10 @@ fn exec_render(encoder: &metal::RenderCommandEncoderRef, command: &soft::RenderC } } -pub(crate) fn exec_blit(encoder: &metal::BlitCommandEncoderRef, command: &soft::BlitCommand) { +fn exec_blit<'a>(encoder: &metal::BlitCommandEncoderRef, command: soft::BlitCommand<&'a soft::Own>) { use soft::BlitCommand as Cmd; - match *command { - Cmd::CopyBuffer { ref src, ref dst, ref region } => { + match command { + Cmd::CopyBuffer { src, dst, region } => { encoder.copy_from_buffer( src, region.src as NSUInteger, @@ -823,11 +1074,11 @@ pub(crate) fn exec_blit(encoder: &metal::BlitCommandEncoderRef, command: &soft:: region.size as NSUInteger ); } - Cmd::CopyImage { ref src, ref dst, ref region } => { + Cmd::CopyImage { src, dst, region } => { let size = conv::map_extent(region.extent); let src_offset = conv::map_offset(region.src_offset); let dst_offset = conv::map_offset(region.dst_offset); - let layers = region.src_subresource.layers.clone().zip(region.dst_subresource.layers.clone()); + let layers = region.src_subresource.layers.zip(region.dst_subresource.layers); for (src_layer, dst_layer) in layers { encoder.copy_from_texture( &*src.resolve(), @@ -842,7 +1093,7 @@ pub(crate) fn exec_blit(encoder: &metal::BlitCommandEncoderRef, command: &soft:: ); } } - Cmd::CopyBufferToImage { ref src, ref dst, dst_desc, ref region } => { + Cmd::CopyBufferToImage { src, dst, dst_desc, region } => { let extent = conv::map_extent(region.image_extent); let origin = conv::map_offset(region.image_offset); let (row_pitch, slice_pitch) = compute_pitches(®ion, &dst_desc, &extent); @@ -864,7 +1115,7 @@ pub(crate) fn exec_blit(encoder: &metal::BlitCommandEncoderRef, command: &soft:: ); } } - Cmd::CopyImageToBuffer { ref src, src_desc, ref dst, ref region } => { + Cmd::CopyImageToBuffer { src, src_desc, dst, region } => { let extent = conv::map_extent(region.image_extent); let origin = conv::map_offset(region.image_offset); let (row_pitch, slice_pitch) = compute_pitches(®ion, &src_desc, &extent); @@ -889,16 +1140,16 @@ pub(crate) fn exec_blit(encoder: &metal::BlitCommandEncoderRef, command: &soft:: } } -fn exec_compute(encoder: &metal::ComputeCommandEncoderRef, command: &soft::ComputeCommand) { +fn exec_compute<'a>(encoder: &metal::ComputeCommandEncoderRef, command: soft::ComputeCommand<&'a soft::Own>) { use soft::ComputeCommand as Cmd; - match *command { - Cmd::BindBuffer { index, ref buffer, offset } => { - encoder.set_buffer(index as _, offset, buffer.as_ref().map(|x| x.as_ref())); + match command { + Cmd::BindBuffer { index, buffer, offset } => { + encoder.set_buffer(index as _, offset, buffer); } - Cmd::BindBufferData { ref bytes, index } => { - encoder.set_bytes(index as _, bytes.len() as _, bytes.as_ptr() as _); + Cmd::BindBufferData { words, index } => { + encoder.set_bytes(index as _, (words.len() * WORD_SIZE) as u64, words.as_ptr() as _); } - Cmd::BindTexture { index, ref texture } => { + Cmd::BindTexture { index, texture } => { let guard; let texture = match texture { Some(ref root) => { @@ -909,16 +1160,16 @@ fn exec_compute(encoder: &metal::ComputeCommandEncoderRef, command: &soft::Compu }; encoder.set_texture(index as _, texture); } - Cmd::BindSampler { index, ref sampler } => { - encoder.set_sampler_state(index as _, sampler.as_ref().map(|x| x.as_ref())); + Cmd::BindSampler { index, sampler } => { + encoder.set_sampler_state(index as _, sampler); } - Cmd::BindPipeline(ref pipeline) => { + Cmd::BindPipeline(pipeline) => { encoder.set_compute_pipeline_state(pipeline); } Cmd::Dispatch { wg_size, wg_count } => { encoder.dispatch_thread_groups(wg_count, wg_size); } - Cmd::DispatchIndirect { wg_size, ref buffer, offset } => { + Cmd::DispatchIndirect { wg_size, buffer, offset } => { encoder.dispatch_thread_groups_indirect(buffer, offset, wg_size); } } @@ -948,21 +1199,21 @@ fn record_commands(command_buf: &metal::CommandBufferRef, passes: &[soft::Pass]) resolve_frames(desc, frames); let encoder = command_buf.new_render_command_encoder(desc); for command in commands { - exec_render(&encoder, command); + exec_render(&encoder, command.as_ref()); } encoder.end_encoding(); } soft::Pass::Blit(ref commands) => { let encoder = command_buf.new_blit_command_encoder(); for command in commands { - exec_blit(&encoder, command); + exec_blit(&encoder, command.as_ref()); } encoder.end_encoding(); } soft::Pass::Compute(ref commands) => { let encoder = command_buf.new_compute_command_encoder(); for command in commands { - exec_compute(&encoder, command); + exec_compute(&encoder, command.as_ref()); } encoder.end_encoding(); } @@ -1129,276 +1380,73 @@ impl pool::RawCommandPool for CommandPool { compute_pso: None, work_group_size: MTLSize { width: 0, height: 0, depth: 0 }, primitive_type: MTLPrimitiveType::Point, - resources_vs: StageResources::new(), - resources_fs: StageResources::new(), - 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, - front_read_mask: !0, - back_read_mask: !0, - front_write_mask: !0, - back_write_mask: !0, - }, - push_constants: Vec::new(), - vertex_buffers: Vec::new(), - framebuffer_inner: native::FramebufferInner { - extent: Extent::default(), - aspects: Aspects::empty(), - colors: Vec::new(), - depth_stencil: None, - } - }, - }).collect(); - - if let Some(ref mut managed) = self.managed { - managed.extend(buffers.iter().map(|buf| buf.inner.clone())); - } - buffers - } - - /// Free command buffers which are allocated from this pool. - unsafe fn free(&mut self, mut buffers: Vec) { - use hal::command::RawCommandBuffer; - for buf in &mut buffers { - buf.reset(true); - } - let managed = match self.managed { - Some(ref mut vec) => vec, - None => return, - }; - for cmd_buf in buffers { - match managed.iter_mut().position(|b| Arc::ptr_eq(b, &cmd_buf.inner)) { - Some(index) => { - managed.swap_remove(index); - } - None => { - error!("Unable to free a command buffer!") - } - } - } - } -} - -/// Sets up the load/store operations. Returns `true` if the clear color needs to be set. -fn set_operations(attachment: &metal::RenderPassAttachmentDescriptorRef, ops: AttachmentOps) -> AttachmentLoadOp { - attachment.set_load_action(conv::map_load_operation(ops.load)); - attachment.set_store_action(conv::map_store_operation(ops.store)); - ops.load -} - -impl CommandBuffer { - fn set_viewport(&mut self, vp: &pso::Viewport) -> soft::RenderCommand { - let viewport = MTLViewport { - originX: vp.rect.x as _, - originY: vp.rect.y as _, - width: vp.rect.w as _, - height: vp.rect.h as _, - znear: vp.depth.start as _, - zfar: if self.shared.disabilities.broken_viewport_near_depth { - (vp.depth.end - vp.depth.start) as _ - } else { - vp.depth.end as _ - }, - }; - self.state.viewport = Some(viewport); - soft::RenderCommand::SetViewport(viewport) - } - - fn set_scissor(&mut self, rect: &pso::Rect) -> soft::RenderCommand { - let scissor = MTLScissorRect { - x: rect.x as _, - y: rect.y as _, - width: rect.w as _, - height: rect.h as _, - }; - self.state.scissors = Some(scissor); - let clamped = self.state.clamp_scissor(scissor); - soft::RenderCommand::SetScissor(clamped) - } - - fn set_blend_color(&mut self, color: &pso::ColorValue) -> soft::RenderCommand { - self.state.blend_color = Some(*color); - soft::RenderCommand::SetBlendColor(*color) - } - - fn push_vs_constants(&mut self) -> soft::RenderCommand { - let id = self.shared.push_constants_buffer_id; - self.state.resources_vs.push_constants_buffer_id = Some(id); - soft::RenderCommand::BindBufferData { - stage: pso::Stage::Vertex, - index: id as _, - bytes: soft::push_data(&self.state.push_constants), - } - } - - fn push_ps_constants(&mut self) -> soft::RenderCommand { - let id = self.shared.push_constants_buffer_id; - self.state.resources_fs.push_constants_buffer_id = Some(id); - soft::RenderCommand::BindBufferData { - stage: pso::Stage::Fragment, - index: id as _, - bytes: soft::push_data(&self.state.push_constants), - } - } - - fn push_cs_constants(&mut self) -> soft::ComputeCommand { - let id = self.shared.push_constants_buffer_id; - self.state.resources_cs.push_constants_buffer_id = Some(id); - soft::ComputeCommand::BindBufferData { - index: id as _, - bytes: soft::push_data(&self.state.push_constants), - } - } - - fn update_push_constants( - &mut self, - offset: u32, - constants: &[u32], - ) { - assert_eq!(offset % WORD_ALIGNMENT as u32, 0); - let offset = (offset / WORD_ALIGNMENT as u32) as usize; - let data = &mut self.state.push_constants; - while data.len() < offset + constants.len() { - data.push(0); - } - data[offset .. offset + constants.len()].copy_from_slice(constants); - } - - fn set_depth_bias(&mut self, depth_bias: &pso::DepthBias) -> soft::RenderCommand { - if let Some(ref mut r) = self.state.rasterizer_state { - r.depth_bias = *depth_bias; - } else { - self.state.rasterizer_state = Some(native::RasterizerState { - depth_bias: *depth_bias, - ..Default::default() - }); - } - soft::RenderCommand::SetDepthBias(*depth_bias) - } - - fn set_vertex_buffers(&mut self, commands: &mut Vec) { - let map = match self.state.render_pso { - Some((_, ref map, _)) => map, - None => return - }; - - let vs_buffers = &mut self.state.resources_vs.buffers; - for (&(binding, extra_offset), vb) in map { - let index = vb.binding as usize; - while vs_buffers.len() <= index { - vs_buffers.push(None) - } - let (buffer, offset) = match self.state.vertex_buffers.get(binding as usize) { - Some(&Some((ref buffer, base_offset))) => (buffer, extra_offset as u64 + base_offset), - // being unable to bind a buffer here is technically fine, since before this moment - // and actual rendering there might be more bind calls - _ => continue, - }; - - if let Some((ref old_buffer, old_offset)) = vs_buffers[index] { - if old_buffer.as_ptr() == buffer.as_ptr() && old_offset == offset { - continue; // already bound + resources_vs: StageResources::new(), + resources_fs: StageResources::new(), + 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, + front_read_mask: !0, + back_read_mask: !0, + front_write_mask: !0, + back_write_mask: !0, + }, + push_constants: Vec::new(), + vertex_buffers: Vec::new(), + framebuffer_inner: native::FramebufferInner { + extent: Extent::default(), + aspects: Aspects::empty(), + colors: Vec::new(), + depth_stencil: None, } - } - vs_buffers[index] = Some((buffer.clone(), offset)); + }, + temp: Temp { + clear_vertices: Vec::new(), + blit_vertices: FastHashMap::default(), + }, + }).collect(); - commands.push(soft::RenderCommand::BindBuffer { - stage: pso::Stage::Vertex, - index, - buffer: Some(buffer.clone()), - offset, - }) + if let Some(ref mut managed) = self.managed { + managed.extend(buffers.iter().map(|buf| buf.inner.clone())); } + buffers } - fn set_depth_stencil_desc( - &mut self, - depth_stencil_desc: &pso::DepthStencilDesc, - depth_stencil_raw: &metal::DepthStencilState, - ) -> soft::RenderCommand { - self.state.pipeline_depth_stencil = Some((depth_stencil_desc.clone(), depth_stencil_raw.clone())); - soft::RenderCommand::SetDepthStencilDesc(depth_stencil_raw.clone()) - } - - fn set_stencil_reference_values( - &mut self, - front: pso::StencilValue, - back: pso::StencilValue, - ) -> soft::RenderCommand { - self.state.stencil.front_reference = front; - self.state.stencil.back_reference = back; - soft::RenderCommand::SetStencilReferenceValues(front, back) - } - - fn set_stencil_mask_values( - &mut self, - 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<&metal::DepthStencilDescriptor>, - ) -> Option { - if let Some((f, b)) = front_back_read_masks_to_update { - self.state.stencil.front_read_mask = f; - self.state.stencil.back_read_mask = b; - } - - if let Some((f, b)) = front_back_write_masks_to_update { - self.state.stencil.front_write_mask = f; - self.state.stencil.back_write_mask = b; - } - - if let Some(ds) = dynamic_depth_stencil_from_pipeline { - self.state.dynamic_depth_stencil_desc = Some(ds.clone()); + /// Free command buffers which are allocated from this pool. + unsafe fn free(&mut self, mut buffers: Vec) { + use hal::command::RawCommandBuffer; + for buf in &mut buffers { + buf.reset(true); } - - let dynamic_state = self.state.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 managed = match self.managed { + Some(ref mut vec) => vec, + None => return, + }; + for cmd_buf in buffers { + match managed.iter_mut().position(|b| Arc::ptr_eq(b, &cmd_buf.inner)) { + Some(index) => { + managed.swap_remove(index); } - }; - - 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 + error!("Unable to free a command buffer!") } - }; - - if let Some((fm, bm)) = front_back_read_masks_to_update { - front.set_read_mask(fm); - back.set_read_mask(bm); - } - - if let Some((fm, bm)) = front_back_write_masks_to_update { - front.set_write_mask(fm); - back.set_write_mask(bm); } - - self.shared.device - .lock() - .unwrap() - .new_depth_stencil_state(&desc) - }); - - self.state.dynamic_depth_stencil_state = dynamic_state.as_ref().map(|ds| ds.clone()); - - dynamic_state.map(soft::RenderCommand::SetDepthStencilDesc) + } } } +/// Sets up the load/store operations. Returns `true` if the clear color needs to be set. +fn set_operations(attachment: &metal::RenderPassAttachmentDescriptorRef, ops: AttachmentOps) -> AttachmentLoadOp { + attachment.set_load_action(conv::map_load_operation(ops.load)); + attachment.set_store_action(conv::map_store_operation(ops.store)); + ops.load +} + impl com::RawCommandBuffer for CommandBuffer { fn begin(&mut self, flags: com::CommandBufferFlags, _info: com::CommandBufferInheritanceInfo) { self.reset(false); @@ -1459,9 +1507,7 @@ impl com::RawCommandBuffer for CommandBuffer { let pipes = self.shared.service_pipes .lock() .unwrap(); - let pso = pipes - .get_fill_buffer() - .to_owned(); + let pso = pipes.get_fill_buffer(); let start = *range.start().unwrap_or(&0); assert_eq!(start % WORD_ALIGNMENT, 0); @@ -1495,21 +1541,16 @@ impl com::RawCommandBuffer for CommandBuffer { depth: 1, }; - let commands = vec![ + let commands = [ soft::ComputeCommand::BindPipeline(pso), soft::ComputeCommand::BindBuffer { index: 0, - buffer: Some(buffer.raw.clone()), + buffer: Some(&*buffer.raw), offset: start, }, soft::ComputeCommand::BindBufferData { index: 1, - bytes: unsafe { - slice::from_raw_parts( - value_and_length.as_ptr() as _, - mem::size_of::() * value_and_length.len() - ).to_owned() - }, + words: &value_and_length[..], }, soft::ComputeCommand::Dispatch { wg_size, @@ -1517,7 +1558,7 @@ impl com::RawCommandBuffer for CommandBuffer { }, ]; - inner.sink().begin_compute_pass(commands); + inner.sink().begin_compute_pass(commands.iter().cloned()); inner.sink().stop_encoding(); } @@ -1527,7 +1568,6 @@ impl com::RawCommandBuffer for CommandBuffer { offset: buffer::Offset, data: &[u8], ) { - let mut inner = self.inner.borrow_mut(); let src = self.shared.device .lock() .unwrap() @@ -1536,20 +1576,25 @@ impl com::RawCommandBuffer for CommandBuffer { data.len() as _, metal::MTLResourceOptions::CPUCacheModeWriteCombined, ); - inner.retained_buffers.push(src.clone()); - - let command = soft::BlitCommand::CopyBuffer { - src, - dst: dst.raw.clone(), - region: com::BufferCopy { - src: 0, - dst: offset, - size: data.len() as _, - }, - }; - inner - .sink() - .blit_commands(iter::once(command)); + + let mut inner = self.inner.borrow_mut(); + { + let command = soft::BlitCommand::CopyBuffer { + src: &*src, + dst: &*dst.raw, + region: com::BufferCopy { + src: 0, + dst: offset, + size: data.len() as _, + }, + }; + + inner + .sink() + .blit_commands(iter::once(command)); + } + + inner.retained_buffers.push(src); } fn clear_image( @@ -1581,7 +1626,7 @@ impl com::RawCommandBuffer for CommandBuffer { sub.layers.clone() }; let texture = if CLEAR_IMAGE_ARRAY && sub.layers.start > 0 { - let image_raw = image.root.resolve(); + let image_raw = image.root.as_ref().resolve(); // aliasing is necessary for bulk-clearing all layers starting with 0 let tex = image_raw.new_texture_view_from_slice( image.mtl_format, @@ -1601,7 +1646,7 @@ impl com::RawCommandBuffer for CommandBuffer { match image.root { native::ImageRoot::Texture(ref tex) => Some(tex.as_ref()), native::ImageRoot::Frame(ref f) => { - frame = Some((0, f.clone())); + frame = Some((0usize, f.clone())); None } } @@ -1678,7 +1723,7 @@ impl com::RawCommandBuffer for CommandBuffer { sink.as_mut() .unwrap() - .quick_render_pass(descriptor, frame.clone(), None); + .begin_render_pass(false, descriptor, frame.clone().into_iter(), None.into_iter()); // no actual pass body - everything is in the attachment clear operations } } @@ -1697,7 +1742,9 @@ impl com::RawCommandBuffer for CommandBuffer { { // gather vertices/polygons let de = self.state.framebuffer_inner.extent; - let mut vertices = Vec::new(); + let vertices = &mut self.temp.clear_vertices; + vertices.clear(); + for rect in rects { let r = rect.borrow(); for layer in r.layers.clone() { @@ -1738,8 +1785,8 @@ impl com::RawCommandBuffer for CommandBuffer { } } - let mut commands = Vec::new(); let mut vertex_is_dirty = true; + let mut inner = self.inner.borrow_mut(); // issue a PSO+color switch and a draw for each requested clear let mut pipes = self.shared.service_pipes @@ -1758,27 +1805,28 @@ impl com::RawCommandBuffer for CommandBuffer { *out = cat.mtl_format; } - let aspects = match *clear.borrow() { + let (aspects, com_clear) = match *clear.borrow() { com::AttachmentClear::Color { index, value } => { let cat = &self.state.framebuffer_inner.colors[index]; //Note: technically we should be able to derive the Channel from the // `value` variant, but this is blocked by the portability that is // always passing the attachment clears as `ClearColor::Float` atm. let raw_value = com::ClearColorRaw::from(value); - commands.push(soft::RenderCommand::BindBufferData { + let com = soft::RenderCommand::BindBufferData { stage: pso::Stage::Fragment, index: 0, - bytes: unsafe { - slice::from_raw_parts(raw_value.float32.as_ptr() as *const u8, 16) - }.to_owned(), - }); + words: unsafe { slice::from_raw_parts( + raw_value.float32.as_ptr() as *const u32, + mem::size_of::() / WORD_SIZE, + )}, + }; key.target_index = Some((index as u8, cat.channel)); - Aspects::COLOR + (Aspects::COLOR, Some(com)) } com::AttachmentClear::DepthStencil { depth, stencil } => { let mut aspects = Aspects::empty(); if let Some(value) = depth { - for v in &mut vertices { + for v in vertices.iter_mut() { v.pos[2] = value; } vertex_is_dirty = true; @@ -1788,79 +1836,103 @@ impl com::RawCommandBuffer for CommandBuffer { //TODO: soft::RenderCommand::SetStencilReference aspects |= Aspects::STENCIL; } - aspects + (aspects, None) } }; - if vertex_is_dirty { + let com_vertex = if vertex_is_dirty { vertex_is_dirty = false; - commands.push(soft::RenderCommand::BindBufferData { + Some(soft::RenderCommand::BindBufferData { stage: pso::Stage::Vertex, index: 0, - bytes: unsafe { + words: unsafe { slice::from_raw_parts( - vertices.as_ptr() as *const u8, - vertices.len() * mem::size_of::() - ).to_owned() + vertices.as_ptr() as *const u32, + vertices.len() * mem::size_of::() / WORD_SIZE + ) } - }); - } - let pso = pipes.get_clear_image( - key, - &self.shared.device - ).to_owned(); - commands.push(soft::RenderCommand::BindPipeline(pso, None)); + }) + } else { + None + }; - if !aspects.contains(Aspects::COLOR) { - commands.push(soft::RenderCommand::SetDepthStencilDesc( - pipes.get_depth_stencil(aspects).to_owned() - )); - } + let ServicePipes { + ref library, + ref depth_stencil_states, + ref mut clears, + .. + } = *pipes; - commands.push(soft::RenderCommand::Draw { - primitive_type: MTLPrimitiveType::Triangle, - vertices: 0 .. vertices.len() as _, - instances: 0 .. 1, - }); + let clear_pso = clears.get(key, library, &self.shared.device); + 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) + )) + } else { + None + }; + + let commands = com_clear + .into_iter() + .chain(com_vertex) + .chain(com_pso) + .chain(com_ds) + .chain(iter::once(soft::RenderCommand::Draw { + primitive_type: MTLPrimitiveType::Triangle, + vertices: 0 .. vertices.len() as _, + instances: 0 .. 1, + })); + + inner.sink().render_commands(commands); } // reset all the affected states - if let Some((ref pso, _, _)) = self.state.render_pso { + let com_pso = if let Some((ref pso, _, _)) = self.state.render_pso { if self.state.render_pso_is_compatible { - commands.push(soft::RenderCommand::BindPipeline( - pso.clone(), - None, - )); + Some(soft::RenderCommand::BindPipeline(&**pso, None)) } else { warn!("Not restoring the current PSO after clear_attachments because it's not compatible"); + None } - } + } else { + None + }; - if let Some((_, ref raw)) = self.state.pipeline_depth_stencil { - commands.push(soft::RenderCommand::SetDepthStencilDesc(raw.clone())); - } + let com_ds = self.state.pipeline_depth_stencil + .as_ref() + .map(|&(_, ref raw)| soft::RenderCommand::SetDepthStencilDesc(&**raw)); - if let Some(&Some((ref buffer, offset))) = self.state.resources_vs.buffers.first() { - commands.push(soft::RenderCommand::BindBuffer { + let com_vs = if let Some(&Some((ref buffer, offset))) = self.state.resources_vs.buffers.first() { + Some(soft::RenderCommand::BindBuffer { stage: pso::Stage::Vertex, index: 0, - buffer: Some(buffer.clone()), + buffer: Some(&**buffer), offset, - }); - } - if let Some(&Some((ref buffer, offset))) = self.state.resources_fs.buffers.first() { - commands.push(soft::RenderCommand::BindBuffer { + }) + } else { + None + }; + let com_fs = if let Some(&Some((ref buffer, offset))) = self.state.resources_fs.buffers.first() { + Some(soft::RenderCommand::BindBuffer { stage: pso::Stage::Fragment, index: 0, - buffer: Some(buffer.clone()), + buffer: Some(&**buffer), offset, - }); - } + }) + } else { + None + }; - self.inner - .borrow_mut() - .sink() - .render_commands(commands.into_iter()); + let commands = com_pso + .into_iter() + .chain(com_ds) + .chain(com_vs) + .chain(com_fs); + inner.sink().render_commands(commands); + + vertices.clear(); } fn resolve_image( @@ -1889,12 +1961,14 @@ impl com::RawCommandBuffer for CommandBuffer { T: IntoIterator, T::Item: Borrow { - let mut vertices = FastHashMap::default(); // a list of vertices per mipmap + let vertices = &mut self.temp.blit_vertices; + vertices.clear(); + let mut frame = None; let dst_texture = match dst.root { native::ImageRoot::Texture(ref tex) => Some(tex.as_ref()), native::ImageRoot::Frame(ref f) => { - frame = Some((0, f.clone())); + frame = Some((0usize, f.clone())); None } }; @@ -1980,34 +2054,42 @@ impl com::RawCommandBuffer for CommandBuffer { let mut pipes = self.shared.service_pipes .lock() .unwrap(); + let ServicePipes { + ref library, + ref sampler_states, + ref depth_stencil_states, + ref mut blits, + .. + } = *pipes; + let key = (dst.mtl_type, dst.mtl_format, src.format_desc.aspects, dst.shader_channel); - let mut prelude = vec![ + let prelude = [ soft::RenderCommand::BindPipeline( - pipes - .get_blit_image(key, &self.shared.device) - .to_owned(), + blits.get(key, library, &self.shared.device), None, ), soft::RenderCommand::BindSampler { stage: pso::Stage::Fragment, index: 0, - sampler: Some(pipes.get_sampler(filter).to_owned()), + sampler: Some(sampler_states.get(filter)), }, soft::RenderCommand::BindTexture { stage: pso::Stage::Fragment, index: 0, - texture: Some(src.root.clone()) + texture: Some(src.root.as_ref()) }, ]; - if src.format_desc.aspects.intersects(Aspects::DEPTH | Aspects::STENCIL) { - prelude.push(soft::RenderCommand::SetDepthStencilDesc( - pipes.get_depth_stencil(src.format_desc.aspects).to_owned() - )); - } + 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) + )) + } else { + None + }; - for ((aspects, level), list) in vertices { + for ((aspects, level), list) in vertices.drain() { let ext = &dst.extent; let extra = [ @@ -2029,11 +2111,11 @@ impl com::RawCommandBuffer for CommandBuffer { soft::RenderCommand::BindBufferData { stage: pso::Stage::Vertex, index: 0, - bytes: unsafe { + words: unsafe { slice::from_raw_parts( - list.as_ptr() as *const u8, - list.len() * mem::size_of::() - ).to_owned() + list.as_ptr() as *const u32, + list.len() * mem::size_of::() / WORD_SIZE + ) } }, soft::RenderCommand::Draw { @@ -2070,10 +2152,11 @@ impl com::RawCommandBuffer for CommandBuffer { let commands = prelude .iter() + .chain(&com_ds) .chain(&extra) .cloned(); - inner.sink().quick_render_pass(descriptor, frame.clone(), commands); + inner.sink().begin_render_pass(false, descriptor, frame.clone().into_iter(), commands); } } @@ -2096,13 +2179,14 @@ impl com::RawCommandBuffer for CommandBuffer { self.state.vertex_buffers[first_binding as usize + i] = Some((buffer.raw.clone(), buffer.range.start + offset)); } - let mut commands = Vec::new(); - self.set_vertex_buffers(&mut commands); - - self.inner - .borrow_mut() - .sink() - .pre_render_commands(commands); + let mask = self.state.set_vertex_buffers(); + if mask != 0 { + let commands = self.state.iter_vertex_buffers(mask); + self.inner + .borrow_mut() + .sink() + .pre_render_commands(commands); + } } fn set_viewports(&mut self, first_viewport: u32, vps: T) @@ -2122,7 +2206,7 @@ impl com::RawCommandBuffer for CommandBuffer { panic!("More than one viewport set; Metal supports only one viewport"); } - let com = self.set_viewport(vp); + let com = self.state.set_viewport(vp, &self.shared.disabilities); self.inner .borrow_mut() .sink() @@ -2145,7 +2229,7 @@ impl com::RawCommandBuffer for CommandBuffer { panic!("More than one scissor set; Metal supports only one viewport"); } - let com = self.set_scissor(rect); + let com = self.state.set_scissor(rect); self.inner .borrow_mut() .sink() @@ -2153,7 +2237,7 @@ impl com::RawCommandBuffer for CommandBuffer { } fn set_blend_constants(&mut self, color: pso::ColorValue) { - let com = self.set_blend_color(&color); + let com = self.state.set_blend_color(&color); self.inner .borrow_mut() .sink() @@ -2169,7 +2253,7 @@ impl com::RawCommandBuffer for CommandBuffer { } fn set_depth_bias(&mut self, depth_bias: pso::DepthBias) { - let com = self.set_depth_bias(&depth_bias); + let com = self.state.set_depth_bias(&depth_bias); self.inner .borrow_mut() .sink() @@ -2185,7 +2269,7 @@ impl com::RawCommandBuffer for CommandBuffer { _ => (value, value), }; - let com = self.set_stencil_reference_values(front, back); + let com = self.state.set_stencil_reference_values(front, back); self.inner .borrow_mut() @@ -2202,7 +2286,7 @@ impl com::RawCommandBuffer for CommandBuffer { _ => (value, value), }; - let com = self.set_stencil_mask_values(Some((front, back)), None, None); + let com = self.state.set_stencil_mask_values(&self.shared, Some((front, back)), None, None); self.inner .borrow_mut() .sink() @@ -2218,7 +2302,7 @@ impl com::RawCommandBuffer for CommandBuffer { _ => (value, value), }; - let com = self.set_stencil_mask_values(None, Some((front, back)), None); + let com = self.state.set_stencil_mask_values(&self.shared, None, Some((front, back)), None); self.inner .borrow_mut() .sink() @@ -2303,7 +2387,7 @@ impl com::RawCommandBuffer for CommandBuffer { let init_commands = self.state.make_render_commands(full_aspects); inner .sink() - .begin_render_pass(descriptor, frames, init_commands); + .begin_render_pass(true, &descriptor, frames, init_commands); } fn next_subpass(&mut self, _contents: com::SubpassContents) { @@ -2318,68 +2402,69 @@ impl com::RawCommandBuffer for CommandBuffer { } fn bind_graphics_pipeline(&mut self, pipeline: &native::GraphicsPipeline) { - let pipeline_state = pipeline.raw.to_owned(); self.state.render_pso_is_compatible = true; //assume good intent :) self.state.render_pso = Some(( - pipeline_state.clone(), + pipeline.raw.to_owned(), pipeline.vertex_buffer_map.clone(), 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 commands = SmallVec::<[soft::RenderCommand<_>; 5]>::new(); + commands.push(soft::RenderCommand::BindPipeline( + &*pipeline.raw, + pipeline.rasterizer_state.clone(), + )); - let mut commands = Vec::new(); - commands.push( - soft::RenderCommand::BindPipeline( - pipeline_state, - pipeline.rasterizer_state.clone(), - ) - ); if let Some(ref vp) = pipeline.baked_states.viewport { - commands.push(self.set_viewport(vp)); + commands.push(self.state.set_viewport(vp, &self.shared.disabilities)); } if let Some(ref rect) = pipeline.baked_states.scissor { - commands.push(self.set_scissor(rect)); + commands.push(self.state.set_scissor(rect)); } if let Some(ref color) = pipeline.baked_states.blend_color { - commands.push(self.set_blend_color(color)); + commands.push(self.state.set_blend_color(color)); + } + + // re-bind vertex buffers + if vertex_mask != 0 { + let vertex_commands = self.state.iter_vertex_buffers(vertex_mask); + inner.sink().pre_render_commands(vertex_commands); } 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() { + commands.push(self.state.set_stencil_reference_values(front_ref, back_ref)); + } + let command = match ds.depth_stencil_static { - Some(ref raw) => Some(self.set_depth_stencil_desc(&desc, raw)), + Some(ref raw) => Some(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); - self.set_stencil_mask_values( + 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(), + ds.depth_stencil_desc_raw.as_ref().map(Borrow::borrow), ) } }; commands.extend(command); - - // 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() { - commands.push(self.set_stencil_reference_values(front_ref, back_ref)); - } } - // re-bind vertex buffers - self.set_vertex_buffers(&mut commands); - - self.inner - .borrow_mut() - .sink() - .pre_render_commands(commands); + inner.sink().pre_render_commands(commands); } fn bind_graphics_descriptor_sets<'a, I, J>( @@ -2396,14 +2481,20 @@ impl com::RawCommandBuffer for CommandBuffer { { use spirv_cross::{msl, spirv}; - let mut commands = Vec::new(); //TODO: re-use the storage let mut offset_iter = offsets.into_iter(); for (set_index, desc_set) in sets.into_iter().enumerate() { match *desc_set.borrow() { native::DescriptorSet::Emulated(ref desc_inner) => { use native::DescriptorSetBinding::*; + let set = desc_inner.lock().unwrap(); + let mut commands = Vec::with_capacity( + set.bindings + .iter() + .map(|values| values.as_ref().map_or(0, |v| v.count())) + .sum() + ); let bindings = set.bindings .iter() .enumerate() @@ -2435,11 +2526,11 @@ impl com::RawCommandBuffer for CommandBuffer { for &mut (stage, ref loc, ref mut resources) in &mut bind_stages { let start = layout.res_overrides[loc].sampler_id as usize; resources.add_samplers(start, samplers.as_slice()); - commands.extend(samplers.iter().cloned().enumerate().map(|(i, sampler)| { + commands.extend(samplers.iter().enumerate().map(|(i, sampler)| { soft::RenderCommand::BindSampler { stage, index: start + i, - sampler, + sampler: sampler.as_ref().map(Borrow::borrow), } })); } @@ -2452,7 +2543,7 @@ impl com::RawCommandBuffer for CommandBuffer { soft::RenderCommand::BindTexture { stage, index: start + i, - texture: texture.as_ref().map(|&(ref root, _)| root.clone()), + texture: texture.as_ref().map(|&(ref root, _)| root.as_ref()), } })); } @@ -2461,18 +2552,18 @@ impl com::RawCommandBuffer for CommandBuffer { for &mut (stage, ref loc, ref mut resources) in &mut bind_stages { let start_tx = layout.res_overrides[loc].texture_id as usize; let start_sm = layout.res_overrides[loc].sampler_id as usize; - for (i, (ref texture, ref sampler)) in combos.iter().cloned().enumerate() { + for (i, (ref texture, ref sampler)) in combos.iter().enumerate() { resources.add_textures(start_tx + i, &[texture.clone()]); resources.add_samplers(start_sm + i, &[sampler.clone()]); commands.push(soft::RenderCommand::BindTexture { stage, index: start_tx + i, - texture: texture.as_ref().map(|&(ref root, _)| root.clone()), + texture: texture.as_ref().map(|&(ref root, _)| root.as_ref()), }); commands.push(soft::RenderCommand::BindSampler { stage, index: start_sm + i, - sampler: sampler.clone(), + sampler: sampler.as_ref().map(Borrow::borrow), }); } } @@ -2487,7 +2578,7 @@ impl com::RawCommandBuffer for CommandBuffer { .expect("No dynamic offset provided!") .borrow() as u64; } - (Some(buffer), offset) + (Some(&**buffer), offset) } None => (None, 0), }; @@ -2499,7 +2590,7 @@ impl com::RawCommandBuffer for CommandBuffer { commands.push(soft::RenderCommand::BindBuffer { stage, index: start + i, - buffer: buffer.cloned(), + buffer, offset, }); } @@ -2507,9 +2598,14 @@ impl com::RawCommandBuffer for CommandBuffer { } } } + + self.inner + .borrow_mut() + .sink() + .pre_render_commands(commands); } native::DescriptorSet::ArgumentBuffer { ref buffer, offset, stage_flags, .. } => { - if stage_flags.contains(pso::ShaderStageFlags::VERTEX) { + let com_vs = if stage_flags.contains(pso::ShaderStageFlags::VERTEX) { let loc = msl::ResourceBindingLocation { stage: spirv::ExecutionModel::Vertex, desc_set: (first_set + set_index) as _, @@ -2517,14 +2613,16 @@ impl com::RawCommandBuffer for CommandBuffer { }; let slot = layout.res_overrides[&loc].buffer_id; self.state.resources_vs.add_buffer(slot as _, buffer, offset as _); - commands.push(soft::RenderCommand::BindBuffer { + Some(soft::RenderCommand::BindBuffer { stage: pso::Stage::Vertex, index: slot as _, - buffer: Some(buffer.clone()), + buffer: Some(&**buffer), offset, - }); - } - if stage_flags.contains(pso::ShaderStageFlags::FRAGMENT) { + }) + } else { + None + }; + let com_fs = if stage_flags.contains(pso::ShaderStageFlags::FRAGMENT) { let loc = msl::ResourceBindingLocation { stage: spirv::ExecutionModel::Fragment, desc_set: (first_set + set_index) as _, @@ -2532,28 +2630,30 @@ impl com::RawCommandBuffer for CommandBuffer { }; let slot = layout.res_overrides[&loc].buffer_id; self.state.resources_fs.add_buffer(slot as _, &buffer, offset as _); - commands.push(soft::RenderCommand::BindBuffer { + Some(soft::RenderCommand::BindBuffer { stage: pso::Stage::Fragment, index: slot as _, - buffer: Some(buffer.clone()), + buffer: Some(&**buffer), offset, - }); - } + }) + } else { + None + }; + let commands = com_vs.into_iter().chain(com_fs); + self.inner + .borrow_mut() + .sink() + .pre_render_commands(commands); } } } - - self.inner - .borrow_mut() - .sink() - .pre_render_commands(commands); } fn bind_compute_pipeline(&mut self, pipeline: &native::ComputePipeline) { self.state.compute_pso = Some(pipeline.raw.clone()); self.state.work_group_size = pipeline.work_group_size; - let command = soft::ComputeCommand::BindPipeline(pipeline.raw.clone()); + let command = soft::ComputeCommand::BindPipeline(&*pipeline.raw); self.inner .borrow_mut() @@ -2575,8 +2675,8 @@ impl com::RawCommandBuffer for CommandBuffer { { use spirv_cross::{msl, spirv}; - let mut commands = Vec::new(); let mut offset_iter = offsets.into_iter(); + let mut inner = self.inner.borrow_mut(); for (set_index, desc_set) in sets.into_iter().enumerate() { let resources = &mut self.state.resources_cs; @@ -2588,7 +2688,14 @@ impl com::RawCommandBuffer for CommandBuffer { match *desc_set.borrow() { native::DescriptorSet::Emulated(ref desc_inner) => { use native::DescriptorSetBinding::*; + let set = desc_inner.lock().unwrap(); + let mut commands = Vec::with_capacity( + set.bindings + .iter() + .map(|values| values.as_ref().map_or(0, |v| v.count())) + .sum() + ); let bindings = set.bindings .iter() .enumerate() @@ -2606,10 +2713,10 @@ impl com::RawCommandBuffer for CommandBuffer { Sampler(ref samplers) => { let start = res.sampler_id as usize; resources.add_samplers(start, samplers.as_slice()); - commands.extend(samplers.iter().cloned().enumerate().map(|(i, sampler)| { + commands.extend(samplers.iter().enumerate().map(|(i, sampler)| { soft::ComputeCommand::BindSampler { index: start + i, - sampler, + sampler: sampler.as_ref().map(Borrow::borrow), } })); } @@ -2619,23 +2726,23 @@ impl com::RawCommandBuffer for CommandBuffer { commands.extend(images.iter().enumerate().map(|(i, texture)| { soft::ComputeCommand::BindTexture { index: start + i, - texture: texture.as_ref().map(|&(ref texture, _)| texture.clone()), + texture: texture.as_ref().map(|&(ref root, _)| root.as_ref()), } })); } Combined(ref combos) => { - for (i, (ref texture, ref sampler)) in combos.iter().cloned().enumerate() { + for (i, (ref texture, ref sampler)) in combos.iter().enumerate() { let id_tx = res.texture_id as usize + i; let id_sm = res.sampler_id as usize + i; resources.add_textures(id_tx, &[texture.clone()]); resources.add_samplers(id_sm, &[sampler.clone()]); commands.push(soft::ComputeCommand::BindTexture { index: id_tx, - texture: texture.as_ref().map(|&(ref root, _)| root.clone()), + texture: texture.as_ref().map(|&(ref root, _)| root.as_ref()), }); commands.push(soft::ComputeCommand::BindSampler { index: id_sm, - sampler: sampler.clone(), + sampler: sampler.as_ref().map(Borrow::borrow), }); } } @@ -2651,7 +2758,7 @@ impl com::RawCommandBuffer for CommandBuffer { .borrow() as u64; } resources.add_buffer(start + i, buffer.as_ref(), offset as _); - (Some(buffer.clone()), offset) + (Some(&**buffer), offset) }, None => (None, 0), }; @@ -2665,6 +2772,8 @@ impl com::RawCommandBuffer for CommandBuffer { } } } + + inner.sink().pre_compute_commands(commands); } native::DescriptorSet::ArgumentBuffer { ref buffer, offset, stage_flags, .. } => { if stage_flags.contains(pso::ShaderStageFlags::COMPUTE) { @@ -2674,11 +2783,6 @@ impl com::RawCommandBuffer for CommandBuffer { } } } - - self.inner - .borrow_mut() - .sink() - .pre_compute_commands(commands); } fn dispatch(&mut self, count: WorkGroupCount) { @@ -2706,7 +2810,7 @@ impl com::RawCommandBuffer for CommandBuffer { let command = soft::ComputeCommand::DispatchIndirect { wg_size: self.state.work_group_size, - buffer: buffer.raw.clone(), + buffer: &*buffer.raw, offset, }; @@ -2727,11 +2831,10 @@ impl com::RawCommandBuffer for CommandBuffer { T: IntoIterator, T::Item: Borrow, { - let compute_pipe = self.shared.service_pipes + let pipes = self.shared.service_pipes .lock() - .unwrap() - .get_copy_buffer() - .to_owned(); + .unwrap(); + let compute_pipe = pipes.get_copy_buffer(); let wg_size = MTLSize { width: compute_pipe.thread_execution_width(), height: 1, @@ -2740,16 +2843,16 @@ impl com::RawCommandBuffer for CommandBuffer { let mut inner = self.inner.borrow_mut(); let mut blit_commands = Vec::new(); - let mut compute_commands = vec![ + let mut compute_commands = vec![ //TODO: get rid of heap soft::ComputeCommand::BindPipeline(compute_pipe), ]; for region in regions { let r = region.borrow(); - if r.size % 4 == 0 { + if r.size % WORD_SIZE as u64 == 0 { blit_commands.push(soft::BlitCommand::CopyBuffer { - src: src.raw.clone(), - dst: dst.raw.clone(), + src: &*src.raw, + dst: &*dst.raw, region: r.clone(), }); } else { @@ -2764,22 +2867,20 @@ impl com::RawCommandBuffer for CommandBuffer { compute_commands.push(soft::ComputeCommand::BindBuffer { index: 0, - buffer: Some(dst.raw.clone()), + buffer: Some(&*dst.raw), offset: r.dst, }); compute_commands.push(soft::ComputeCommand::BindBuffer { index: 1, - buffer: Some(src.raw.clone()), + buffer: Some(&*src.raw), offset: r.src, }); compute_commands.push(soft::ComputeCommand::BindBufferData { index: 2, - bytes: unsafe { - slice::from_raw_parts( - &(r.size as u32) as *const u32 as _, - mem::size_of::() - ).to_owned() - } + words: unsafe { slice::from_raw_parts( + &(r.size as u32) as *const u32, + mem::size_of::() / WORD_SIZE, + )}, }); compute_commands.push(soft::ComputeCommand::Dispatch { wg_size, @@ -2794,7 +2895,7 @@ impl com::RawCommandBuffer for CommandBuffer { } if compute_commands.len() > 1 { // first is bind PSO - sink.begin_compute_pass(compute_commands); + sink.begin_compute_pass(compute_commands.into_iter()); sink.stop_encoding(); } } @@ -2820,15 +2921,15 @@ impl com::RawCommandBuffer for CommandBuffer { src.root.clone() } else { assert_eq!(src.format_desc.bits, dst.format_desc.bits); - let tex = src.root.resolve().new_texture_view(dst.mtl_format); + let tex = src.root.as_ref().resolve().new_texture_view(dst.mtl_format); retained_textures.push(tex.clone()); native::ImageRoot::Texture(tex) }; let commands = regions.into_iter().map(|region| { soft::BlitCommand::CopyImage { - src: new_src.clone(), - dst: dst.root.clone(), + src: new_src.as_ref(), + dst: dst.root.as_ref(), region: region.borrow().clone(), } }); @@ -2850,8 +2951,8 @@ impl com::RawCommandBuffer for CommandBuffer { // FIXME: layout let commands = regions.into_iter().map(|region| { soft::BlitCommand::CopyBufferToImage { - src: src.raw.clone(), - dst: dst.root.clone(), + src: &*src.raw, + dst: dst.root.as_ref(), dst_desc: dst.format_desc, region: region.borrow().clone(), } @@ -2875,9 +2976,9 @@ impl com::RawCommandBuffer for CommandBuffer { // FIXME: layout let commands = regions.into_iter().map(|region| { soft::BlitCommand::CopyImageToBuffer { - src: src.root.clone(), + src: src.root.as_ref(), src_desc: src.format_desc, - dst: dst.raw.clone(), + dst: dst.raw.as_ref(), region: region.borrow().clone(), } }); @@ -2919,7 +3020,10 @@ impl com::RawCommandBuffer for CommandBuffer { let command = soft::RenderCommand::DrawIndexed { primitive_type: self.state.primitive_type, - index: self.state.index_buffer.clone().expect("must bind index buffer"), + index: match self.state.index_buffer { + Some(ref ib) => ib.as_ref(), + None => panic!("must bind index buffer"), + }, indices, base_vertex, instances, @@ -2943,7 +3047,7 @@ impl com::RawCommandBuffer for CommandBuffer { let commands = (0 .. count) .map(|i| soft::RenderCommand::DrawIndirect { primitive_type: self.state.primitive_type, - buffer: buffer.raw.clone(), + buffer: &*buffer.raw, offset: offset + (i * stride) as buffer::Offset, }); @@ -2966,8 +3070,11 @@ impl com::RawCommandBuffer for CommandBuffer { let commands = (0 .. count) .map(|i| soft::RenderCommand::DrawIndexedIndirect { primitive_type: self.state.primitive_type, - index: self.state.index_buffer.clone().expect("must bind index buffer"), - buffer: buffer.raw.clone(), + index: match self.state.index_buffer { + Some(ref ib) => ib.as_ref(), + None => panic!("must bind index buffer"), + }, + buffer: &*buffer.raw, offset: offset + (i * stride) as buffer::Offset, }); @@ -3015,28 +3122,27 @@ impl com::RawCommandBuffer for CommandBuffer { offset: u32, constants: &[u32], ) { - self.update_push_constants(offset, constants); + self.state.update_push_constants(offset, constants); + let id = self.shared.push_constants_buffer_id; if stages.intersects(pso::ShaderStageFlags::GRAPHICS) { // Note: it's a waste to heap allocate the bytes here in case // of no active render pass. // Note: the whole range is re-uploaded, which may be inefficient - let com_vs = if stages.contains(pso::ShaderStageFlags::VERTEX) { - Some(self.push_vs_constants()) - } else { - None - }; - let com_ps = if stages.contains(pso::ShaderStageFlags::FRAGMENT) { - Some(self.push_ps_constants()) - } else { - None - }; - let commands = com_vs.into_iter().chain(com_ps); - - self.inner - .borrow_mut() - .sink() - .pre_render_commands(commands); + if stages.contains(pso::ShaderStageFlags::VERTEX) { + let com = self.state.push_vs_constants(id); + self.inner + .borrow_mut() + .sink() + .pre_render_commands(iter::once(com)); + } + if stages.contains(pso::ShaderStageFlags::FRAGMENT) { + let com = self.state.push_ps_constants(id); + self.inner + .borrow_mut() + .sink() + .pre_render_commands(iter::once(com)); + } } } @@ -3046,12 +3152,13 @@ impl com::RawCommandBuffer for CommandBuffer { offset: u32, constants: &[u32], ) { - self.update_push_constants(offset, constants); + self.state.update_push_constants(offset, constants); + let id = self.shared.push_constants_buffer_id; // Note: it's a waste to heap allocate the bytes here in case // of no active render pass. // Note: the whole range is re-uploaded, which may be inefficient - let command = self.push_cs_constants(); + let command = self.state.push_cs_constants(id); self.inner .borrow_mut() diff --git a/src/backend/metal/src/device.rs b/src/backend/metal/src/device.rs index 9aca9f78c6c..c522cac0abc 100644 --- a/src/backend/metal/src/device.rs +++ b/src/backend/metal/src/device.rs @@ -1489,7 +1489,8 @@ impl hal::Device for Device { encoder.set_sampler_states(&[&sampler.0], write.binding as _); } pso::Descriptor::Image(image, _layout) => { - encoder.set_textures(&[&*image.root.resolve()], write.binding as _); + let guard = image.root.as_ref().resolve(); + encoder.set_textures(&[&*guard], write.binding as _); } pso::Descriptor::Buffer(buffer, ref range) => { encoder.set_buffer(&buffer.raw, range.start.unwrap_or(0), write.binding as _); diff --git a/src/backend/metal/src/internal.rs b/src/backend/metal/src/internal.rs index d1c01951dbc..8783695df6e 100644 --- a/src/backend/metal/src/internal.rs +++ b/src/backend/metal/src/internal.rs @@ -8,12 +8,12 @@ use std::mem; use std::path::Path; use std::sync::Mutex; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct ClearVertex { pub pos: [f32; 4], } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct BlitVertex { pub uv: [f32; 4], pub pos: [f32; 4], @@ -70,104 +70,103 @@ impl Channel { } -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] -pub struct ClearKey { - pub framebuffer_aspects: Aspects, - pub color_formats: [metal::MTLPixelFormat; 1], - pub depth_stencil_format: metal::MTLPixelFormat, - pub target_index: Option<(u8, Channel)>, +pub struct SamplerStates { + nearest: metal::SamplerState, + linear: metal::SamplerState, } -pub type BlitKey = (metal::MTLTextureType, metal::MTLPixelFormat, Aspects, Channel); -//#[derive(Clone)] -pub struct ServicePipes { - library: metal::Library, - sampler_nearest: metal::SamplerState, - sampler_linear: metal::SamplerState, - ds_write_depth_state: metal::DepthStencilState, - ds_write_stencil_state: metal::DepthStencilState, - ds_write_all_state: metal::DepthStencilState, - clears: FastHashMap, - blits: FastHashMap, - copy_buffer: metal::ComputePipelineState, - fill_buffer: metal::ComputePipelineState, +impl SamplerStates { + fn new(device: &metal::DeviceRef) -> Self { + let desc = metal::SamplerDescriptor::new(); + desc.set_min_filter(metal::MTLSamplerMinMagFilter::Nearest); + desc.set_mag_filter(metal::MTLSamplerMinMagFilter::Nearest); + desc.set_mip_filter(metal::MTLSamplerMipFilter::Nearest); + let nearest = device.new_sampler(&desc); + desc.set_min_filter(metal::MTLSamplerMinMagFilter::Linear); + desc.set_mag_filter(metal::MTLSamplerMinMagFilter::Linear); + let linear = device.new_sampler(&desc); + + SamplerStates { + nearest, + linear, + } + } + + pub fn get(&self, filter: Filter) -> &metal::SamplerStateRef { + match filter { + Filter::Nearest => &self.nearest, + Filter::Linear => &self.linear, + } + } } -impl ServicePipes { - pub fn new(device: &metal::DeviceRef) -> Self { - let lib_path = Path::new(env!("OUT_DIR")) - .join("gfx_shaders.metallib"); - let library = device.new_library_with_file(lib_path).unwrap(); +pub struct DepthStencilStates { + write_depth: metal::DepthStencilState, + write_stencil: metal::DepthStencilState, + write_all: metal::DepthStencilState, +} - let sampler_desc = metal::SamplerDescriptor::new(); - sampler_desc.set_min_filter(metal::MTLSamplerMinMagFilter::Nearest); - sampler_desc.set_mag_filter(metal::MTLSamplerMinMagFilter::Nearest); - sampler_desc.set_mip_filter(metal::MTLSamplerMipFilter::Nearest); - let sampler_nearest = device.new_sampler(&sampler_desc); - sampler_desc.set_min_filter(metal::MTLSamplerMinMagFilter::Linear); - sampler_desc.set_mag_filter(metal::MTLSamplerMinMagFilter::Linear); - let sampler_linear = device.new_sampler(&sampler_desc); - - let ds_desc = metal::DepthStencilDescriptor::new(); - ds_desc.set_depth_write_enabled(true); - ds_desc.set_depth_compare_function(metal::MTLCompareFunction::Always); - let ds_write_depth_state = device.new_depth_stencil_state(&ds_desc); +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); - ds_desc.set_front_face_stencil(Some(&stencil_desc)); - let ds_write_all_state = device.new_depth_stencil_state(&ds_desc); - ds_desc.set_depth_write_enabled(false); - let ds_write_stencil_state = device.new_depth_stencil_state(&ds_desc); - - let copy_buffer = Self::create_copy_buffer(&library, device); - let fill_buffer = Self::create_fill_buffer(&library, device); - - ServicePipes { - clears: FastHashMap::default(), - blits: FastHashMap::default(), - sampler_nearest, - sampler_linear, - ds_write_depth_state, - ds_write_all_state, - ds_write_stencil_state, - library, - copy_buffer, - fill_buffer, - } - } - - pub fn get_sampler(&self, filter: Filter) -> &metal::SamplerStateRef { - match filter { - Filter::Nearest => &self.sampler_nearest, - Filter::Linear => &self.sampler_linear, + 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); + + DepthStencilStates { + write_depth, + write_stencil, + write_all, } } //TODO: return `Option` instead? - pub fn get_depth_stencil(&self, aspects: Aspects) -> &metal::DepthStencilStateRef { + pub fn get(&self, aspects: Aspects) -> &metal::DepthStencilStateRef { if aspects.contains(Aspects::DEPTH | Aspects::STENCIL) { - &self.ds_write_all_state + &self.write_all } else if aspects.contains(Aspects::DEPTH) { - &self.ds_write_depth_state + &self.write_depth } else if aspects.contains(Aspects::STENCIL) { - &self.ds_write_stencil_state + &self.write_stencil } else { panic!("Can't write nothing!") } } +} + - pub fn get_clear_image( +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub struct ClearKey { + pub framebuffer_aspects: Aspects, + pub color_formats: [metal::MTLPixelFormat; 1], + pub depth_stencil_format: metal::MTLPixelFormat, + pub target_index: Option<(u8, Channel)>, +} + +pub struct ImageClearPipes { + map: FastHashMap, +} + +impl ImageClearPipes { + pub fn get( &mut self, key: ClearKey, + library: &metal::LibraryRef, device: &Mutex, ) -> &metal::RenderPipelineStateRef { - let lib = &self.library; - self.clears + self.map .entry(key) - .or_insert_with(|| Self::create_clear_image(key, lib, &*device.lock().unwrap())) + .or_insert_with(|| Self::create(key, library, &*device.lock().unwrap())) } - fn create_clear_image( + fn create( key: ClearKey, library: &metal::LibraryRef, device: &metal::DeviceRef, ) -> metal::RenderPipelineState { let pipeline = metal::RenderPipelineDescriptor::new(); @@ -224,19 +223,28 @@ impl ServicePipes { device.new_render_pipeline_state(&pipeline).unwrap() } +} + - pub fn get_blit_image( +pub type BlitKey = (metal::MTLTextureType, metal::MTLPixelFormat, Aspects, Channel); + +pub struct ImageBlitPipes { + map: FastHashMap, +} + +impl ImageBlitPipes { + pub fn get( &mut self, key: BlitKey, + library: &metal::LibraryRef, device: &Mutex, ) -> &metal::RenderPipelineStateRef { - let lib = &self.library; - self.blits + self.map .entry(key) - .or_insert_with(|| Self::create_blit_image(key, lib, &*device.lock().unwrap())) + .or_insert_with(|| Self::create(key, library, &*device.lock().unwrap())) } - fn create_blit_image( + fn create( key: BlitKey, library: &metal::LibraryRef, device: &metal::DeviceRef, ) -> metal::RenderPipelineState { use metal::MTLTextureType as Tt; @@ -304,6 +312,42 @@ impl ServicePipes { device.new_render_pipeline_state(&pipeline).unwrap() } +} + + +pub struct ServicePipes { + pub library: metal::Library, + pub sampler_states: SamplerStates, + pub depth_stencil_states: DepthStencilStates, + pub clears: ImageClearPipes, + pub blits: ImageBlitPipes, + copy_buffer: metal::ComputePipelineState, + fill_buffer: metal::ComputePipelineState, +} + +impl ServicePipes { + pub fn new(device: &metal::DeviceRef) -> Self { + let lib_path = Path::new(env!("OUT_DIR")) + .join("gfx_shaders.metallib"); + let library = device.new_library_with_file(lib_path).unwrap(); + + let copy_buffer = Self::create_copy_buffer(&library, device); + let fill_buffer = Self::create_fill_buffer(&library, device); + + ServicePipes { + sampler_states: SamplerStates::new(device), + depth_stencil_states: DepthStencilStates::new(device), + clears: ImageClearPipes { + map: FastHashMap::default(), + }, + blits: ImageBlitPipes { + map: FastHashMap::default(), + }, + library, + copy_buffer, + fill_buffer, + } + } pub fn get_copy_buffer(&self) -> &metal::ComputePipelineStateRef { &self.copy_buffer diff --git a/src/backend/metal/src/native.rs b/src/backend/metal/src/native.rs index 6bd4100bb8f..fe1bf3e33dd 100644 --- a/src/backend/metal/src/native.rs +++ b/src/backend/metal/src/native.rs @@ -171,7 +171,31 @@ pub enum ImageRoot { Frame(Frame), } +#[derive(Clone)] pub enum ImageRootRef<'a> { + Texture(&'a metal::TextureRef), + Frame(&'a Frame), +} + +impl ImageRoot { + pub(crate) fn as_ref(&self) -> ImageRootRef { + match *self { + ImageRoot::Texture(ref tex) => ImageRootRef::Texture(tex), + ImageRoot::Frame(ref frame) => ImageRootRef::Frame(frame), + } + } +} + +impl<'a> ImageRootRef<'a> { + pub fn own(self) -> ImageRoot { + match self { + ImageRootRef::Texture(tex) => ImageRoot::Texture(tex.to_owned()), + ImageRootRef::Frame(frame) => ImageRoot::Frame(frame.clone()), + } + } +} + +pub enum ImageGuard<'a> { Texture(&'a metal::TextureRef), Frame { swapchain: RwLockReadGuard<'a, SwapchainInner>, @@ -179,21 +203,21 @@ pub enum ImageRootRef<'a> { }, } -impl<'a> Deref for ImageRootRef<'a> { +impl<'a> Deref for ImageGuard<'a> { type Target = metal::TextureRef; fn deref(&self) -> &Self::Target { match *self { - ImageRootRef::Texture(tex) => tex, - ImageRootRef::Frame { ref swapchain, index } => &swapchain[index], + ImageGuard::Texture(tex) => tex, + ImageGuard::Frame { ref swapchain, index } => &swapchain[index], } } } -impl ImageRoot { - pub fn resolve(&self) -> ImageRootRef { +impl<'a> ImageRootRef<'a> { + pub fn resolve(&self) -> ImageGuard<'a> { match *self { - ImageRoot::Texture(ref tex) => ImageRootRef::Texture(tex), - ImageRoot::Frame(ref frame) => ImageRootRef::Frame { + ImageRootRef::Texture(ref tex) => ImageGuard::Texture(tex), + ImageRootRef::Frame(ref frame) => ImageGuard::Frame { swapchain: frame.swapchain.read().unwrap(), index: frame.index, }, @@ -456,6 +480,17 @@ pub enum DescriptorSetBinding { //InputAttachment(Vec<(metal::Texture, image::Layout)>), } +impl DescriptorSetBinding { + pub(crate) fn count(&self) -> usize { + match *self { + DescriptorSetBinding::Sampler(ref vec) => vec.len(), + DescriptorSetBinding::Image(ref vec) => vec.len(), + DescriptorSetBinding::Combined(ref vec) => 2 * vec.len(), + DescriptorSetBinding::Buffer(ref vec) => vec.len(), + } + } +} + #[derive(Debug)] pub struct Memory { pub(crate) heap: MemoryHeap, diff --git a/src/backend/metal/src/soft.rs b/src/backend/metal/src/soft.rs index 42c2441735b..9acc5953594 100644 --- a/src/backend/metal/src/soft.rs +++ b/src/backend/metal/src/soft.rs @@ -1,49 +1,75 @@ use command::{IndexBuffer}; -use native::{Frame, ImageRoot, RasterizerState}; +use native::{Frame, ImageRoot, ImageRootRef, RasterizerState}; use hal; use metal; use std::ops::Range; -pub fn push_data(constants: &[u32]) -> Vec { - constants - .iter() - .flat_map(|&v| (0 .. 4).map(move |i| (v >> 8*i) as u8)) - .collect() + +pub trait Resources { + type Data; + type Buffer; + type Texture; + type Sampler; + type DepthStencil; + type RenderPipeline; + type ComputePipeline; } +#[derive(Debug)] +pub enum Own {} +impl Resources for Own { + type Data = Vec; + type Buffer = metal::Buffer; + type Texture = ImageRoot; + type Sampler = metal::SamplerState; + type DepthStencil = metal::DepthStencilState; + type RenderPipeline = metal::RenderPipelineState; + type ComputePipeline = metal::ComputePipelineState; +} +impl<'a> Resources for &'a Own { + type Data = &'a [u32]; + type Buffer = &'a metal::BufferRef; + type Texture = ImageRootRef<'a>; + type Sampler = &'a metal::SamplerStateRef; + type DepthStencil = &'a metal::DepthStencilStateRef; + type RenderPipeline = &'a metal::RenderPipelineStateRef; + type ComputePipeline = &'a metal::ComputePipelineStateRef; +} + + #[derive(Clone, Debug)] -pub enum RenderCommand { +pub enum RenderCommand { SetViewport(metal::MTLViewport), SetScissor(metal::MTLScissorRect), SetBlendColor(hal::pso::ColorValue), SetDepthBias(hal::pso::DepthBias), - SetDepthStencilDesc(metal::DepthStencilState), + SetDepthStencilDesc(R::DepthStencil), SetStencilReferenceValues(hal::pso::StencilValue, hal::pso::StencilValue), BindBuffer { stage: hal::pso::Stage, index: usize, - buffer: Option, + buffer: Option, offset: hal::buffer::Offset, }, BindBufferData { stage: hal::pso::Stage, index: usize, - bytes: Vec, + words: R::Data, }, BindTexture { stage: hal::pso::Stage, index: usize, - texture: Option, + texture: Option, }, BindSampler { stage: hal::pso::Stage, index: usize, - sampler: Option, + sampler: Option, }, BindPipeline( - metal::RenderPipelineState, + R::RenderPipeline, Option, ), Draw { @@ -53,88 +79,341 @@ pub enum RenderCommand { }, DrawIndexed { primitive_type: metal::MTLPrimitiveType, - index: IndexBuffer, + index: IndexBuffer, indices: Range, base_vertex: hal::VertexOffset, instances: Range, }, DrawIndirect { primitive_type: metal::MTLPrimitiveType, - buffer: metal::Buffer, + buffer: R::Buffer, offset: hal::buffer::Offset, }, DrawIndexedIndirect { primitive_type: metal::MTLPrimitiveType, - index: IndexBuffer, - buffer: metal::Buffer, + index: IndexBuffer, + buffer: R::Buffer, offset: hal::buffer::Offset, }, } -#[derive(Debug)] -pub enum BlitCommand { +impl RenderCommand { + pub fn as_ref<'a>(&'a self) -> RenderCommand<&'a Own> { + use std::borrow::Borrow; + use self::RenderCommand::*; + match *self { + SetViewport(vp) => SetViewport(vp), + SetScissor(rect) => SetScissor(rect), + SetBlendColor(color) => SetBlendColor(color), + SetDepthBias(bias) => SetDepthBias(bias), + SetDepthStencilDesc(ref desc) => SetDepthStencilDesc(&**desc), + SetStencilReferenceValues(front, back) => SetStencilReferenceValues(front, back), + BindBuffer { stage, index, ref buffer, offset } => BindBuffer { + stage, + index, + buffer: buffer.as_ref().map(Borrow::borrow), + offset, + }, + BindBufferData { stage, index, ref words } => BindBufferData { + stage, + index, + words: words.as_slice(), + }, + BindTexture { stage, index, ref texture } => BindTexture { + stage, + index, + texture: texture.as_ref().map(ImageRoot::as_ref), + }, + BindSampler { stage, index, ref sampler } => BindSampler { + stage, + index, + sampler: sampler.as_ref().map(Borrow::borrow), + }, + BindPipeline(ref pso, ref state) => BindPipeline(&**pso, state.clone()), + Draw { primitive_type, ref vertices, ref instances } => Draw { + primitive_type, + vertices: vertices.clone(), + instances: instances.clone(), + }, + DrawIndexed { primitive_type, ref index, ref indices, base_vertex, ref instances } => DrawIndexed { + primitive_type, + index: index.as_ref(), + indices: indices.clone(), + base_vertex, + instances: instances.clone(), + }, + DrawIndirect { primitive_type, ref buffer, offset } => DrawIndirect { + primitive_type, + buffer: &**buffer, + offset, + }, + DrawIndexedIndirect { primitive_type, ref index, ref buffer, offset } => DrawIndexedIndirect { + primitive_type, + index: index.as_ref(), + buffer: &**buffer, + offset, + }, + } + } +} + +impl<'a> RenderCommand<&'a Own> { + pub fn own(self) -> RenderCommand { + use self::RenderCommand::*; + match self { + SetViewport(vp) => SetViewport(vp), + SetScissor(rect) => SetScissor(rect), + SetBlendColor(color) => SetBlendColor(color), + SetDepthBias(bias) => SetDepthBias(bias), + SetDepthStencilDesc(desc) => SetDepthStencilDesc(desc.to_owned()), + SetStencilReferenceValues(front, back) => SetStencilReferenceValues(front, back), + BindBuffer { stage, index, buffer, offset } => BindBuffer { + stage, + index, + buffer: buffer.map(ToOwned::to_owned), + offset, + }, + BindBufferData { stage, index, words } => BindBufferData { + stage, + index, + words: words.to_vec(), + }, + BindTexture { stage, index, texture } => BindTexture { + stage, + index, + texture: texture.map(ImageRootRef::own), + }, + BindSampler { stage, index, sampler } => BindSampler { + stage, + index, + sampler: sampler.map(ToOwned::to_owned), + }, + BindPipeline(pso, state) => BindPipeline(pso.to_owned(), state), + Draw { primitive_type, vertices, instances } => Draw { + primitive_type, + vertices, + instances, + }, + DrawIndexed { primitive_type, index, indices, base_vertex, instances } => DrawIndexed { + primitive_type, + index: index.own(), + indices, + base_vertex, + instances, + }, + DrawIndirect { primitive_type, buffer, offset } => DrawIndirect { + primitive_type, + buffer: buffer.to_owned(), + offset, + }, + DrawIndexedIndirect { primitive_type, index, buffer, offset } => DrawIndexedIndirect { + primitive_type, + index: index.own(), + buffer: buffer.to_owned(), + offset, + }, + } + } +} + + +#[derive(Clone, Debug)] +pub enum BlitCommand { CopyBuffer { - src: metal::Buffer, - dst: metal::Buffer, + src: R::Buffer, + dst: R::Buffer, region: hal::command::BufferCopy, }, CopyImage { - src: ImageRoot, - dst: ImageRoot, + src: R::Texture, + dst: R::Texture, region: hal::command::ImageCopy, }, CopyBufferToImage { - src: metal::Buffer, - dst: ImageRoot, + src: R::Buffer, + dst: R::Texture, dst_desc: hal::format::FormatDesc, region: hal::command::BufferImageCopy, }, CopyImageToBuffer { - src: ImageRoot, + src: R::Texture, src_desc: hal::format::FormatDesc, - dst: metal::Buffer, + dst: R::Buffer, region: hal::command::BufferImageCopy, }, } -#[derive(Debug)] -pub enum ComputeCommand { +impl BlitCommand { + pub fn as_ref<'a>(&'a self) -> BlitCommand<&'a Own> { + use self::BlitCommand::*; + match *self { + CopyBuffer { ref src, ref dst, region } => CopyBuffer { + src: &*src, + dst: &*dst, + region, + }, + CopyImage { ref src, ref dst, ref region } => CopyImage { + src: src.as_ref(), + dst: dst.as_ref(), + region: region.clone(), + }, + CopyBufferToImage { ref src, ref dst, dst_desc, ref region } => CopyBufferToImage { + src: &*src, + dst: dst.as_ref(), + dst_desc, + region: region.clone(), + }, + CopyImageToBuffer { ref src, src_desc, ref dst, ref region } => CopyImageToBuffer { + src: src.as_ref(), + src_desc, + dst: &*dst, + region: region.clone(), + }, + } + } +} + +impl<'a> BlitCommand<&'a Own> { + pub fn own(self) -> BlitCommand { + use self::BlitCommand::*; + match self { + CopyBuffer { src, dst, region } => CopyBuffer { + src: src.to_owned(), + dst: dst.to_owned(), + region, + }, + CopyImage { src, dst, region } => CopyImage { + src: src.own(), + dst: dst.own(), + region, + }, + CopyBufferToImage { src, dst, dst_desc, region } => CopyBufferToImage { + src: src.to_owned(), + dst: dst.own(), + dst_desc, + region, + }, + CopyImageToBuffer { src, src_desc, dst, region } => CopyImageToBuffer { + src: src.own(), + src_desc, + dst: dst.to_owned(), + region, + }, + } + } +} + + +#[derive(Clone, Debug)] +pub enum ComputeCommand { BindBuffer { index: usize, - buffer: Option, + buffer: Option, offset: hal::buffer::Offset, }, BindBufferData { - bytes: Vec, index: usize, + words: R::Data, }, BindTexture { index: usize, - texture: Option, + texture: Option, }, BindSampler { index: usize, - sampler: Option, + sampler: Option, }, - BindPipeline(metal::ComputePipelineState), + BindPipeline(R::ComputePipeline), Dispatch { wg_size: metal::MTLSize, wg_count: metal::MTLSize, }, DispatchIndirect { wg_size: metal::MTLSize, - buffer: metal::Buffer, + buffer: R::Buffer, offset: hal::buffer::Offset, }, } +impl ComputeCommand { + pub fn as_ref<'a>(&'a self) -> ComputeCommand<&'a Own> { + use std::borrow::Borrow; + use self::ComputeCommand::*; + match *self { + BindBuffer { index, ref buffer, offset } => BindBuffer { + index, + buffer: buffer.as_ref().map(Borrow::borrow), + offset, + }, + BindBufferData { index, ref words } => BindBufferData { + index, + words: words.as_slice(), + }, + BindTexture { index, ref texture } => BindTexture { + index, + texture: texture.as_ref().map(ImageRoot::as_ref), + }, + BindSampler { index, ref sampler } => BindSampler { + index, + sampler: sampler.as_ref().map(Borrow::borrow), + }, + BindPipeline(ref pso) => BindPipeline(&**pso), + Dispatch { wg_size, wg_count } => Dispatch { + wg_size, + wg_count, + }, + DispatchIndirect { wg_size, ref buffer, offset } => DispatchIndirect { + wg_size, + buffer: buffer.borrow(), + offset, + }, + } + } +} + +impl<'a> ComputeCommand<&'a Own> { + pub fn own(self) -> ComputeCommand { + use self::ComputeCommand::*; + match self { + BindBuffer { index, buffer, offset } => BindBuffer { + index, + buffer: buffer.map(ToOwned::to_owned), + offset, + }, + BindBufferData { index, words } => BindBufferData { + index, + words: words.to_vec(), + }, + BindTexture { index, texture } => BindTexture { + index, + texture: texture.map(ImageRootRef::own), + }, + BindSampler { index, sampler } => BindSampler { + index, + sampler: sampler.map(ToOwned::to_owned), + }, + BindPipeline(pso) => BindPipeline(pso.to_owned()), + Dispatch { wg_size, wg_count } => Dispatch { + wg_size, + wg_count, + }, + DispatchIndirect { wg_size, buffer, offset } => DispatchIndirect { + wg_size, + buffer: buffer.to_owned(), + offset, + }, + } + } +} + + #[derive(Debug)] pub enum Pass { Render { desc: metal::RenderPassDescriptor, frames: Vec<(usize, Frame)>, - commands: Vec, + commands: Vec>, }, - Blit(Vec), - Compute(Vec), + Blit(Vec>), + Compute(Vec>), } From d7e0676c0fde687d66dc44678bdfc9e8ea8fbdc8 Mon Sep 17 00:00:00 2001 From: Hal Gentz Date: Fri, 1 Jun 2018 14:16:57 -0600 Subject: [PATCH 3/3] Adds basic descriptor set support to the opengl backend Signed-off-by: Hal Gentz --- src/backend/gl/Cargo.toml | 2 +- src/backend/gl/src/command.rs | 53 ++++++- src/backend/gl/src/device.rs | 252 ++++++++++++++++++++++++++++++++-- src/backend/gl/src/info.rs | 7 + src/backend/gl/src/lib.rs | 2 +- src/backend/gl/src/native.rs | 102 +++++++++++++- src/backend/gl/src/queue.rs | 13 ++ 7 files changed, 403 insertions(+), 28 deletions(-) diff --git a/src/backend/gl/Cargo.toml b/src/backend/gl/Cargo.toml index 80509e01d2c..536b7854a6b 100644 --- a/src/backend/gl/Cargo.toml +++ b/src/backend/gl/Cargo.toml @@ -24,4 +24,4 @@ gfx_gl = "0.5" gfx-hal = { path = "../../hal", version = "0.1" } smallvec = "0.6" glutin = { version = "0.16", optional = true } -spirv_cross = "0.8" +spirv_cross = "0.9.2" diff --git a/src/backend/gl/src/command.rs b/src/backend/gl/src/command.rs index ab28143b41f..7a3b30fd40f 100644 --- a/src/backend/gl/src/command.rs +++ b/src/backend/gl/src/command.rs @@ -108,6 +108,10 @@ pub enum Command { CopySurfaceToBuffer(n::Surface, n::RawBuffer, command::BufferImageCopy), CopyImageToTexture(n::ImageKind, n::Texture, command::ImageCopy), CopyImageToSurface(n::ImageKind, n::Surface, command::ImageCopy), + + BindBufferRange(gl::types::GLenum, gl::types::GLuint, n::RawBuffer, gl::types::GLintptr, gl::types::GLsizeiptr), + BindTexture(gl::types::GLenum, n::Texture), + BindSampler(gl::types::GLuint, n::Texture), } pub type FrameBufferTarget = gl::types::GLenum; @@ -862,17 +866,56 @@ impl command::RawCommandBuffer for RawCommandBuffer { fn bind_graphics_descriptor_sets( &mut self, - _layout: &n::PipelineLayout, - _first_set: usize, - _sets: I, - _offsets: J, + layout: &n::PipelineLayout, + first_set: usize, + sets: I, + offsets: J, ) where I: IntoIterator, I::Item: Borrow, J: IntoIterator, J::Item: Borrow, { - // TODO + assert!(offsets.into_iter().next().is_none()); // TODO: offsets unsupported + + let mut set = first_set as _; + let drd = &*layout.desc_remap_data.read().unwrap(); + + for desc_set in sets { + let desc_set = desc_set.borrow(); + for new_binding in &*desc_set.bindings.lock().unwrap() { + match new_binding { + n::DescSetBindings::Buffer {ty: btype, binding, buffer, offset, size} => { + for binding in drd.get_binding(n::BindingTypes::UniformBuffers, set, *binding).unwrap() { + self.push_cmd(Command::BindBufferRange( + gl::UNIFORM_BUFFER, + *binding, + *buffer, + *offset, + *size, + )) + } + } + n::DescSetBindings::Texture(binding, texture) => { + for binding in drd.get_binding(n::BindingTypes::Images, set, *binding).unwrap() { + self.push_cmd(Command::BindTexture( + *binding, + *texture, + )) + } + } + n::DescSetBindings::Sampler(binding, sampler) => { + for binding in drd.get_binding(n::BindingTypes::Images, set, *binding).unwrap() { + self.push_cmd(Command::BindSampler( + *binding, + *sampler, + )) + } + } + } + } + set += 1; + } } fn bind_compute_pipeline(&mut self, pipeline: &n::ComputePipeline) { diff --git a/src/backend/gl/src/device.rs b/src/backend/gl/src/device.rs index e5873f41f77..5f663fdc70e 100644 --- a/src/backend/gl/src/device.rs +++ b/src/backend/gl/src/device.rs @@ -3,7 +3,7 @@ use std::cell::Cell; use std::iter::repeat; use std::ops::Range; use std::{ptr, mem, slice}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use gl; use gl::types::{GLint, GLenum, GLfloat}; @@ -284,21 +284,143 @@ impl Device { }) } + fn remap_bindings( + &self, + ast: &mut spirv::Ast, + desc_remap_data: &mut n::DescRemapData, + nb_map: &mut FastHashMap, + ) { + let res = ast.get_shader_resources().unwrap(); + self.remap_binding(ast, desc_remap_data, nb_map, &res.sampled_images, n::BindingTypes::Images); + self.remap_binding(ast, desc_remap_data, nb_map, &res.uniform_buffers, n::BindingTypes::UniformBuffers); + } + + fn remap_binding( + &self, + ast: &mut spirv::Ast, + desc_remap_data: &mut n::DescRemapData, + nb_map: &mut FastHashMap, + all_res: &[spirv::Resource], + btype: n::BindingTypes, + ) { + for res in all_res { + let set = ast.get_decoration(res.id, spirv::Decoration::DescriptorSet).unwrap(); + let binding = ast.get_decoration(res.id, spirv::Decoration::Binding).unwrap(); + let nbs = desc_remap_data.get_binding(btype, set as _, binding).unwrap(); + + for nb in nbs { + ast.set_decoration(res.id, spirv::Decoration::DescriptorSet, 0).unwrap(); + if self.share.legacy_features.contains(LegacyFeatures::EXPLICIT_LAYOUTS_IN_SHADER) { + ast.set_decoration(res.id, spirv::Decoration::Binding, *nb).unwrap() + } else { + ast.set_decoration(res.id, spirv::Decoration::Binding, 0).unwrap(); + assert!(nb_map.insert(res.name.clone(), *nb).is_none()) + } + } + } + } + + fn combine_seperate_images_and_samplers( + &self, + ast: &mut spirv::Ast, + desc_remap_data: &mut n::DescRemapData, + nb_map: &mut FastHashMap, + ) { + let mut id_map = FastHashMap::::default(); + let res = ast.get_shader_resources().unwrap(); + self.populate_id_map(ast, &mut id_map, &res.separate_images); + self.populate_id_map(ast, &mut id_map, &res.separate_samplers); + + let comb_res = ast.get_shader_resources().unwrap().sampled_images; + + for cis in ast.get_combined_image_samplers().unwrap() { + let (set, binding) = id_map.get(&cis.image_id).unwrap(); + let nb = desc_remap_data.reserve_binding(n::BindingTypes::Images); + desc_remap_data.insert_missing_binding( + nb, + n::BindingTypes::Images, + *set, + *binding, + ); + let (set, binding) = id_map.get(&cis.sampler_id).unwrap(); + desc_remap_data.insert_missing_binding( + nb, + n::BindingTypes::Images, + *set, + *binding, + ); + + ast.set_decoration(cis.combined_id, spirv::Decoration::DescriptorSet, 0).unwrap(); + if self.share.legacy_features.contains(LegacyFeatures::EXPLICIT_LAYOUTS_IN_SHADER) { + ast.set_decoration(cis.combined_id, spirv::Decoration::Binding, nb).unwrap() + } else { + ast.set_decoration(cis.combined_id, spirv::Decoration::Binding, 0).unwrap(); + let name = comb_res + .iter() + .filter_map(|t| + if t.id == cis.combined_id { + Some(t.name.clone()) + } else { + None + } + ) + .next() + .unwrap(); + + assert!(nb_map.insert(name, nb).is_none()) + } + } + } + + fn populate_id_map( + &self, + ast: &mut spirv::Ast, + id_map: &mut FastHashMap, + all_res: &[spirv::Resource], + ) { + for res in all_res { + let set = ast.get_decoration(res.id, spirv::Decoration::DescriptorSet).unwrap(); + let binding = ast.get_decoration(res.id, spirv::Decoration::Binding).unwrap(); + assert!(id_map.insert(res.id, (set as _, binding)).is_none()) + } + } + fn compile_shader( - &self, point: &pso::EntryPoint, stage: pso::Stage + &self, point: &pso::EntryPoint, stage: pso::Stage, desc_remap_data: &mut n::DescRemapData ) -> n::Shader { assert_eq!(point.entry, "main"); match *point.module { - n::ShaderModule::Raw(raw) => raw, + n::ShaderModule::Raw(raw) => { + debug!("Can't remap bindings for raw shaders. Assuming they are already rebound."); + raw + } n::ShaderModule::Spirv(ref spirv) => { let mut ast = self.parse_spirv(spirv).unwrap(); + + let mut name_binding_map = FastHashMap::::default(); + self.specialize_ast(&mut ast, point.specialization).unwrap(); + self.remap_bindings(&mut ast, desc_remap_data, &mut name_binding_map); + self.combine_seperate_images_and_samplers(&mut ast, desc_remap_data, &mut name_binding_map); + let glsl = self.translate_spirv(&mut ast).unwrap(); info!("Generated:\n{:?}", glsl); - match self.create_shader_module_from_source(glsl.as_bytes(), stage).unwrap() { + let program = match self.create_shader_module_from_source(glsl.as_bytes(), stage).unwrap() { n::ShaderModule::Raw(raw) => raw, _ => panic!("Unhandled") + }; + + if !self.share.legacy_features.contains(LegacyFeatures::EXPLICIT_LAYOUTS_IN_SHADER) { + let gl = &self.share.context; + for (name, binding) in name_binding_map.iter() { + unsafe { + let index = gl.GetUniformBlockIndex(program, name.as_ptr() as _); + gl.UniformBlockBinding(program, index, *binding) + } + } } + + program } } } @@ -383,14 +505,57 @@ impl d::Device for Device { } } - fn create_pipeline_layout(&self, _: IS, _: IR) -> n::PipelineLayout + fn create_pipeline_layout(&self, layouts: IS, _: IR) -> n::PipelineLayout where IS: IntoIterator, IS::Item: Borrow, IR: IntoIterator, IR::Item: Borrow<(pso::ShaderStageFlags, Range)>, { - n::PipelineLayout + let mut drd = n::DescRemapData::new(); + + layouts + .into_iter() + .enumerate() + .for_each(|(set, layout)| { + layout.borrow().iter().for_each(|binding| { + // DescriptorType -> Descriptor + // + // Sampler -> Sampler + // Image -> SampledImage, StorageImage, InputAttachment + // CombinedImageSampler -> CombinedImageSampler + // Buffer -> UniformBuffer, StorageBuffer + // UniformTexel -> UniformTexel + // StorageTexel -> StorageTexel + + assert!(!binding.immutable_samplers); //TODO: Implement immutable_samplers + use pso::DescriptorType::*; + match binding.ty { + CombinedImageSampler => { + drd.insert_missing_binding_into_spare(n::BindingTypes::Images, set as _, binding.binding); + } + Sampler | SampledImage => { + // We need to figure out combos once we get the shaders, until then we + // do nothing + } + UniformBuffer => { + drd.insert_missing_binding_into_spare(n::BindingTypes::UniformBuffers, set as _, binding.binding); + } + StorageImage + | UniformTexelBuffer + | UniformBufferDynamic + | StorageTexelBuffer + | StorageBufferDynamic + | StorageBuffer + + | InputAttachment => unimplemented!(), // 6 + } + }) + }); + + n::PipelineLayout { + desc_remap_data: Arc::new(RwLock::new(drd)), + } } fn create_graphics_pipeline<'a>( @@ -423,7 +588,7 @@ impl d::Device for Device { .iter() .filter_map(|&(stage, point_maybe)| { point_maybe.map(|point| { - let shader_name = self.compile_shader(point, stage); + let shader_name = self.compile_shader(point, stage, &mut desc.layout.desc_remap_data.write().unwrap()); unsafe { gl.AttachShader(name, shader_name); } shader_name }) @@ -507,10 +672,11 @@ impl d::Device for Device { ) -> Result { let gl = &self.share.context; let share = &self.share; + let program = { let name = unsafe { gl.CreateProgram() }; - let shader = self.compile_shader(&desc.shader, pso::Stage::Compute); + let shader = self.compile_shader(&desc.shader, pso::Stage::Compute, &mut desc.layout.desc_remap_data.write().unwrap()); unsafe { gl.AttachShader(name, shader) }; unsafe { gl.LinkProgram(name) }; @@ -588,7 +754,6 @@ impl d::Device for Device { assert!(pass.attachments.len() <= att_points.len()); gl.DrawBuffers(attachments_len as _, att_points.as_ptr()); let status = gl.CheckFramebufferStatus(target); - assert_eq!(status, gl::FRAMEBUFFER_COMPLETE); gl.BindFramebuffer(target, 0); } if let Err(err) = self.share.check() { @@ -764,6 +929,7 @@ impl d::Device for Device { Ok(n::Buffer { raw: unbound.name, target, + size: unbound.requirements.size, }) } @@ -973,14 +1139,15 @@ impl d::Device for Device { n::DescriptorPool { } } - fn create_descriptor_set_layout(&self, _: I, _: J) -> n::DescriptorSetLayout + fn create_descriptor_set_layout(&self, layout: I, _: J) -> n::DescriptorSetLayout where I: IntoIterator, I::Item: Borrow, J: IntoIterator, J::Item: Borrow, { - n::DescriptorSetLayout + // Just return it + layout.into_iter().map(|l| l.borrow().clone()).collect() } fn write_descriptor_sets<'a, I, J>(&self, writes: I) @@ -989,9 +1156,66 @@ impl d::Device for Device { J: IntoIterator, J::Item: Borrow>, { - for _write in writes { - //unimplemented!() // not panicing because of Warden - warn!("TODO: implement `write_descriptor_sets`"); + for mut write in writes { + let set = &mut write.set; + let mut bindings = set.bindings.lock().unwrap(); + let binding = write.binding; + let mut offset = write.array_offset as _; + + for descriptor in write.descriptors { + match descriptor.borrow() { + pso::Descriptor::Buffer(buffer, ref range) => { + let start = range.start.unwrap_or(0); + let end = range.end.unwrap_or(buffer.size); + let size = (end - start) as _; + + bindings + .push(n::DescSetBindings::Buffer { + ty: n::BindingTypes::UniformBuffers, + binding, + buffer: buffer.raw, + offset, + size, + }); + + offset += size; + }, + pso::Descriptor::CombinedImageSampler(view, _layout, sampler) => { + match view { + n::ImageView::Texture(tex, _) + | n::ImageView::TextureLayer(tex, _, _) => + bindings + .push(n::DescSetBindings::Texture(binding, *tex)), + n::ImageView::Surface(_) => unimplemented!(), + } + match sampler { + n::FatSampler::Sampler(sampler) => + bindings + .push(n::DescSetBindings::Sampler(binding, *sampler)), + n::FatSampler::Info(_) => unimplemented!(), + } + } + pso::Descriptor::Image(view, _layout) => { + match view { + n::ImageView::Texture(tex, _) + | n::ImageView::TextureLayer(tex, _, _) => + bindings + .push(n::DescSetBindings::Texture(binding, *tex)), + n::ImageView::Surface(_) => unimplemented!(), + } + } + pso::Descriptor::Sampler(sampler) => { + match sampler { + n::FatSampler::Sampler(sampler) => + bindings + .push(n::DescSetBindings::Sampler(binding, *sampler)), + n::FatSampler::Info(_) => unimplemented!(), + } + } + pso::Descriptor::UniformTexelBuffer(_view) => unimplemented!(), + pso::Descriptor::StorageTexelBuffer(_view) => unimplemented!(), + } + } } } diff --git a/src/backend/gl/src/info.rs b/src/backend/gl/src/info.rs index fcf61338de0..8195a934c53 100644 --- a/src/backend/gl/src/info.rs +++ b/src/backend/gl/src/info.rs @@ -226,6 +226,8 @@ bitflags! { const SAMPLER_LOD_BIAS = 0x2000; /// Support setting border texel colors. const SAMPLER_BORDER_COLOR = 0x4000; + /// No explicit layouts in shader support + const EXPLICIT_LAYOUTS_IN_SHADER = 0x8000; } } @@ -347,6 +349,11 @@ pub fn query_all(gl: &gl::Gl) -> (Info, Features, LegacyFeatures, Limits, Privat ]) { features |= Features::SAMPLER_ANISOTROPY; } + if info.is_supported(&[ + Core(4, 2), + ]) { + legacy |= LegacyFeatures::EXPLICIT_LAYOUTS_IN_SHADER; + } if info.is_supported(&[ Core(3, 3), Es(3, 0), diff --git a/src/backend/gl/src/lib.rs b/src/backend/gl/src/lib.rs index 15208afc2a6..acc99fb4098 100644 --- a/src/backend/gl/src/lib.rs +++ b/src/backend/gl/src/lib.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use std::ops::Deref; use std::thread::{self, ThreadId}; -use hal::{error, image}; +use hal::{error, image, pso}; use hal::queue::{Queues, QueueFamilyId}; pub use self::device::Device; diff --git a/src/backend/gl/src/native.rs b/src/backend/gl/src/native.rs index 3f60af4100f..c483d96bba7 100644 --- a/src/backend/gl/src/native.rs +++ b/src/backend/gl/src/native.rs @@ -1,13 +1,14 @@ use std::cell::Cell; +use std::sync::{Arc, Mutex, RwLock}; use hal::{format, image as i, pass, pso}; use hal::memory::Properties; +use hal::backend::FastHashMap; use gl; use Backend; use std::borrow::Borrow; - pub type RawBuffer = gl::types::GLuint; pub type Shader = gl::types::GLuint; pub type Program = gl::types::GLuint; @@ -16,12 +17,15 @@ pub type Surface = gl::types::GLuint; pub type Texture = gl::types::GLuint; pub type Sampler = gl::types::GLuint; +pub type DescriptorSetLayout = Vec; + pub const DEFAULT_FRAMEBUFFER: FrameBuffer = 0; #[derive(Debug)] pub struct Buffer { pub(crate) raw: RawBuffer, pub(crate) target: gl::types::GLenum, + pub(crate) size: u64, } #[derive(Debug)] @@ -38,6 +42,72 @@ impl Fence { } } +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub enum BindingTypes { + Images, + UniformBuffers, +} + +#[derive(Clone, Debug)] +pub struct DescRemapData { + bindings: FastHashMap<(BindingTypes, pso::DescriptorSetIndex, pso::DescriptorBinding), Vec>, + names: FastHashMap, + next_binding: FastHashMap, +} + +/// Stores where the descriptor bindings have been remaped too. +/// +/// OpenGL doesn't support sets, so we have to flatten out the bindings. +impl DescRemapData { + pub fn new() -> Self { + DescRemapData { + bindings: FastHashMap::default(), + names: FastHashMap::default(), + next_binding: FastHashMap::default(), + } + } + + pub fn insert_missing_binding_into_spare( + &mut self, + btype: BindingTypes, + set: pso::DescriptorSetIndex, + binding: pso::DescriptorBinding, + ) -> &[pso::DescriptorBinding] { + let nb = self.next_binding.entry(btype).or_insert(0); + let val = self.bindings.entry((btype, set, binding)).or_insert(Vec::new()); + val.push(*nb); + *nb += 1; + &*val + } + + pub fn reserve_binding(&mut self, btype: BindingTypes) -> pso::DescriptorBinding { + let nb = self.next_binding.entry(btype).or_insert(0); + *nb += 1; + *nb - 1 + } + + pub fn insert_missing_binding( + &mut self, + nb: pso::DescriptorBinding, + btype: BindingTypes, + set: pso::DescriptorSetIndex, + binding: pso::DescriptorBinding, + ) -> &[pso::DescriptorBinding] { + let val = self.bindings.entry((btype, set, binding)).or_insert(Vec::new()); + val.push(nb); + &*val + } + + pub fn get_binding( + &self, + btype: BindingTypes, + set: pso::DescriptorSetIndex, + binding: pso::DescriptorBinding, + ) -> Option<&[pso::DescriptorBinding]> { + self.bindings.get(&(btype, set, binding)).map(AsRef::as_ref) + } +} + #[derive(Clone, Debug)] pub struct GraphicsPipeline { pub(crate) program: Program, @@ -48,7 +118,7 @@ pub struct GraphicsPipeline { pub(crate) vertex_buffers: Vec>, } -#[derive(Clone, Debug, Copy)] +#[derive(Clone, Debug)] pub struct ComputePipeline { pub(crate) program: Program, } @@ -82,10 +152,23 @@ pub enum ImageView { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct DescriptorSetLayout; +pub(crate) enum DescSetBindings { + Buffer { + ty: BindingTypes, + binding: pso::DescriptorBinding, + buffer: RawBuffer, + offset: gl::types::GLintptr, + size: gl::types::GLsizeiptr + }, + Texture(pso::DescriptorBinding, Texture), + Sampler(pso::DescriptorBinding, Sampler), +} -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct DescriptorSet; +#[derive(Clone, Debug)] +pub struct DescriptorSet { + layout: DescriptorSetLayout, + pub(crate) bindings: Arc>>, +} #[derive(Debug)] pub struct DescriptorPool {} @@ -96,7 +179,10 @@ impl pso::DescriptorPool for DescriptorPool { I: IntoIterator, I::Item: Borrow, { - layouts.into_iter().map(|_| Ok(DescriptorSet)).collect() + layouts.into_iter().map(|layout| Ok(DescriptorSet { + layout: layout.borrow().clone(), + bindings: Arc::new(Mutex::new(Vec::new())), + })).collect() } fn free_sets(&mut self, _descriptor_sets: &[DescriptorSet]) { @@ -166,7 +252,9 @@ impl SubpassDesc { } #[derive(Debug)] -pub struct PipelineLayout; +pub struct PipelineLayout { + pub(crate) desc_remap_data: Arc>, +} #[derive(Debug)] // No inter-queue synchronization required for GL. diff --git a/src/backend/gl/src/queue.rs b/src/backend/gl/src/queue.rs index 53c0e5d43bd..0b1c265169d 100644 --- a/src/backend/gl/src/queue.rs +++ b/src/backend/gl/src/queue.rs @@ -551,6 +551,19 @@ impl CommandQueue { com::Command::CopyImageToSurface(..) => { unimplemented!() //TODO: use FBO } + com::Command::BindBufferRange(target, index, buffer, offset, size) => unsafe { + let gl = &self.share.context; + gl.BindBufferRange(target, index, buffer, offset, size); + } + com::Command::BindTexture(index, texture) => unsafe { + let gl = &self.share.context; + gl.ActiveTexture(gl::TEXTURE0 + index); + gl.BindTexture(gl::TEXTURE_2D, texture); + } + com::Command::BindSampler(index, sampler) => unsafe { + let gl = &self.share.context; + gl.BindSampler(index, sampler); + } /* com::Command::BindConstantBuffer(pso::ConstantBufferParam(buffer, _, slot)) => unsafe { self.share.context.BindBufferBase(gl::UNIFORM_BUFFER, slot as gl::types::GLuint, buffer);