From f6733628e83c90d8b17ba5137e8d3b6d625f4aac Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Fri, 8 Apr 2016 21:52:40 +0200 Subject: [PATCH] Add unsafe support for push constants --- TROUBLES.md | 2 ++ vulkano/examples/image.rs | 2 +- vulkano/examples/teapot.rs | 2 +- vulkano/examples/triangle.rs | 2 +- vulkano/src/command_buffer/inner.rs | 35 +++++++++++++------ vulkano/src/command_buffer/outer.rs | 32 ++++++++--------- vulkano/src/descriptor/descriptor.rs | 15 +++++++- .../pipeline_layout/custom_pipeline_macro.rs | 21 +++++++++-- vulkano/src/descriptor/pipeline_layout/sys.rs | 33 +++++++++++++---- 9 files changed, 104 insertions(+), 40 deletions(-) diff --git a/TROUBLES.md b/TROUBLES.md index e1d2349d..23c15615 100644 --- a/TROUBLES.md +++ b/TROUBLES.md @@ -29,3 +29,5 @@ - This repository contains the `vulkano-shaders` library, which generates Rust code that uses the `vulkano` library. If the API of `vulkano` gets a breaking change, there is no way to enforce or to check the fact that the user uses a correct combination of versions for `vulkano-shaders` and `vulkano`. + +- No way to set the alignment of a struct member, or to force the size of a struct to be a multiple of a certain size. diff --git a/vulkano/examples/image.rs b/vulkano/examples/image.rs index 31889fb4..550fd85f 100644 --- a/vulkano/examples/image.rs +++ b/vulkano/examples/image.rs @@ -220,7 +220,7 @@ fn main() { .draw_inline(&renderpass, &framebuffer, renderpass::ClearValues { color: [0.0, 0.0, 1.0, 1.0] }) - .draw(&pipeline, &vertex_buffer, &vulkano::command_buffer::DynamicState::none(), set.clone()) + .draw(&pipeline, &vertex_buffer, &vulkano::command_buffer::DynamicState::none(), set.clone(), &()) .draw_end() .build().unwrap() }).collect::>(); diff --git a/vulkano/examples/teapot.rs b/vulkano/examples/teapot.rs index 67e1ab11..a6f8d93c 100644 --- a/vulkano/examples/teapot.rs +++ b/vulkano/examples/teapot.rs @@ -216,7 +216,7 @@ fn main() { depth: 1.0, }) .draw_indexed(&pipeline, (&vertex_buffer, &normals_buffer), &index_buffer, - &vulkano::command_buffer::DynamicState::none(), &set) + &vulkano::command_buffer::DynamicState::none(), &set, &()) .draw_end() .build().unwrap() }).collect::>(); diff --git a/vulkano/examples/triangle.rs b/vulkano/examples/triangle.rs index 909d4f98..7419d527 100644 --- a/vulkano/examples/triangle.rs +++ b/vulkano/examples/triangle.rs @@ -237,7 +237,7 @@ fn main() { let command_buffers = framebuffers.iter().map(|framebuffer| { vulkano::command_buffer::PrimaryCommandBufferBuilder::new(&cb_pool).unwrap() .draw_inline(&renderpass, &framebuffer, ([0.0, 0.0, 1.0, 1.0],)) - .draw(&pipeline, vertex_buffer.clone(), &vulkano::command_buffer::DynamicState::none(), ()) + .draw(&pipeline, vertex_buffer.clone(), &vulkano::command_buffer::DynamicState::none(), (), &()) .draw_end() .build().unwrap() }).collect::>(); diff --git a/vulkano/src/command_buffer/inner.rs b/vulkano/src/command_buffer/inner.rs index d50884f3..308e595f 100644 --- a/vulkano/src/command_buffer/inner.rs +++ b/vulkano/src/command_buffer/inner.rs @@ -686,15 +686,15 @@ impl InnerCommandBufferBuilder { /// Calls `vkCmdDraw`. // FIXME: push constants - pub unsafe fn draw(mut self, pipeline: &Arc>, + pub unsafe fn draw(mut self, pipeline: &Arc>, vertices: V, dynamic: &DynamicState, - sets: L) -> InnerCommandBufferBuilder + sets: L, push_constants: &Pc) -> InnerCommandBufferBuilder where Pv: 'static + VertexDefinition + VertexSource, L: DescriptorSetsCollection + Send + Sync, - Pl: 'static + PipelineLayout + Send + Sync, Rp: 'static + Send + Sync + Pl: 'static + PipelineLayout + Send + Sync, Rp: 'static + Send + Sync, Pc: 'static + Clone { // FIXME: add buffers to the resources - self.bind_gfx_pipeline_state(pipeline, dynamic, sets); + self.bind_gfx_pipeline_state(pipeline, dynamic, sets, push_constants); let vertices = pipeline.vertex_definition().decode(vertices); @@ -727,17 +727,18 @@ impl InnerCommandBufferBuilder { /// Calls `vkCmdDrawIndexed`. // FIXME: push constants - pub unsafe fn draw_indexed<'a, V, Pv, Pl, Rp, L, I, Ib, Ibb>(mut self, pipeline: &Arc>, + pub unsafe fn draw_indexed<'a, V, Pv, Pl, Rp, L, I, Ib, Ibb, Pc>(mut self, pipeline: &Arc>, vertices: V, indices: Ib, dynamic: &DynamicState, - sets: L) -> InnerCommandBufferBuilder + sets: L, push_constants: &Pc) -> InnerCommandBufferBuilder where L: DescriptorSetsCollection + Send + Sync, Pv: 'static + VertexDefinition + VertexSource, Pl: 'static + PipelineLayout + Send + Sync, Rp: 'static + Send + Sync, - Ib: Into>, I: 'static + Index, Ibb: Buffer + 'static + Ib: Into>, I: 'static + Index, Ibb: Buffer + 'static, + Pc: 'static + Clone { // FIXME: add buffers to the resources - self.bind_gfx_pipeline_state(pipeline, dynamic, sets); + self.bind_gfx_pipeline_state(pipeline, dynamic, sets, push_constants); let indices = indices.into(); @@ -830,10 +831,10 @@ impl InnerCommandBufferBuilder { } } - fn bind_gfx_pipeline_state(&mut self, pipeline: &Arc>, - dynamic: &DynamicState, sets: L) + fn bind_gfx_pipeline_state(&mut self, pipeline: &Arc>, + dynamic: &DynamicState, sets: L, push_constants: &Pc) where V: 'static + VertexDefinition + Send + Sync, L: DescriptorSetsCollection + Send + Sync, - Pl: 'static + PipelineLayout + Send + Sync, Rp: 'static + Send + Sync + Pl: 'static + PipelineLayout + Send + Sync, Rp: 'static + Send + Sync, Pc: 'static + Clone { unsafe { //assert!(sets.is_compatible_with(pipeline.layout())); @@ -905,6 +906,18 @@ impl InnerCommandBufferBuilder { for d in descriptor_sets.iter() { self.keep_alive.push(mem::transmute(d.clone()) /* FIXME: */); } let mut descriptor_sets = Some(descriptor_sets.into_iter().map(|set| set.inner_descriptor_set().internal_object()).collect::>()); + if mem::size_of_val(push_constants) >= 1 { + let pipeline = PipelineLayout::inner_pipeline_layout(&**pipeline.layout()).internal_object(); + let size = mem::size_of_val(push_constants); + let push_constants = push_constants.clone(); + assert!((size % 4) == 0); + + self.render_pass_staging_commands.push(Box::new(move |vk, cmd| { + vk.CmdPushConstants(cmd, pipeline, 0x7fffffff, 0, size as u32, + &push_constants as *const Pc as *const _); + })); + } + // FIXME: input attachments of descriptor sets have to be checked against input // attachments of the render pass diff --git a/vulkano/src/command_buffer/outer.rs b/vulkano/src/command_buffer/outer.rs index db5cc88a..f38c189f 100644 --- a/vulkano/src/command_buffer/outer.rs +++ b/vulkano/src/command_buffer/outer.rs @@ -281,17 +281,17 @@ pub struct PrimaryCommandBufferBuilderInlineDraw { impl PrimaryCommandBufferBuilderInlineDraw { /// Calls `vkCmdDraw`. // FIXME: push constants - pub fn draw(self, pipeline: &Arc>, - vertices: V, dynamic: &DynamicState, sets: L) + pub fn draw(self, pipeline: &Arc>, + vertices: V, dynamic: &DynamicState, sets: L, push_constants: &Pc) -> PrimaryCommandBufferBuilderInlineDraw where Pv: VertexDefinition + VertexSource + 'static, Pl: PipelineLayout + 'static + Send + Sync, Rp: 'static + Send + Sync, - L: DescriptorSetsCollection + Send + Sync + L: DescriptorSetsCollection + Send + Sync, Pc: 'static + Clone { // FIXME: check subpass unsafe { PrimaryCommandBufferBuilderInlineDraw { - inner: self.inner.draw(pipeline, vertices, dynamic, sets), + inner: self.inner.draw(pipeline, vertices, dynamic, sets, push_constants), num_subpasses: self.num_subpasses, current_subpass: self.current_subpass, } @@ -299,18 +299,18 @@ impl PrimaryCommandBufferBuilderInlineDraw { } /// Calls `vkCmdDrawIndexed`. - pub fn draw_indexed<'a, V, L, Pv, Pl, Rp, I, Ib, Ibb>(self, pipeline: &Arc>, + pub fn draw_indexed<'a, V, L, Pv, Pl, Rp, I, Ib, Ibb, Pc>(self, pipeline: &Arc>, vertices: V, indices: Ib, dynamic: &DynamicState, - sets: L) -> PrimaryCommandBufferBuilderInlineDraw + sets: L, push_constants: &Pc) -> PrimaryCommandBufferBuilderInlineDraw where Pv: 'static + VertexDefinition + VertexSource + Send + Sync, Pl: 'static + PipelineLayout + Send + Sync, Rp: 'static + Send + Sync, Ib: Into>, I: 'static + Index, Ibb: Buffer + 'static + Send + Sync, - L: DescriptorSetsCollection + Send + Sync + L: DescriptorSetsCollection + Send + Sync, Pc: 'static + Clone { // FIXME: check subpass unsafe { PrimaryCommandBufferBuilderInlineDraw { - inner: self.inner.draw_indexed(pipeline, vertices, indices, dynamic, sets), + inner: self.inner.draw_indexed(pipeline, vertices, indices, dynamic, sets, push_constants), num_subpasses: self.num_subpasses, current_subpass: self.current_subpass, } @@ -528,19 +528,19 @@ impl SecondaryGraphicsCommandBufferBuilder /// Calls `vkCmdDraw`. // FIXME: push constants - pub fn draw(self, pipeline: &Arc>, - vertices: V, dynamic: &DynamicState, sets: L) + pub fn draw(self, pipeline: &Arc>, + vertices: V, dynamic: &DynamicState, sets: L, push_constants: &Pc) -> SecondaryGraphicsCommandBufferBuilder where Pv: VertexDefinition + VertexSource + 'static, Pl: PipelineLayout + 'static + Send + Sync, Rp: RenderPass + 'static + Send + Sync, L: DescriptorSetsCollection + Send + Sync, - R: RenderPassCompatible + R: RenderPassCompatible, Pc: 'static + Clone { assert!(self.render_pass.is_compatible_with(pipeline.subpass().render_pass())); assert_eq!(self.render_pass_subpass, pipeline.subpass().index()); unsafe { SecondaryGraphicsCommandBufferBuilder { - inner: self.inner.draw(pipeline, vertices, dynamic, sets), + inner: self.inner.draw(pipeline, vertices, dynamic, sets, push_constants), render_pass: self.render_pass, render_pass_subpass: self.render_pass_subpass, framebuffer: self.framebuffer, @@ -549,20 +549,20 @@ impl SecondaryGraphicsCommandBufferBuilder } /// Calls `vkCmdDrawIndexed`. - pub fn draw_indexed<'a, V, L, Pv, Pl, Rp, I, Ib, Ibb>(self, pipeline: &Arc>, + pub fn draw_indexed<'a, V, L, Pv, Pl, Rp, I, Ib, Ibb, Pc>(self, pipeline: &Arc>, vertices: V, indices: Ib, dynamic: &DynamicState, - sets: L) -> SecondaryGraphicsCommandBufferBuilder + sets: L, push_constants: &Pc) -> SecondaryGraphicsCommandBufferBuilder where Pv: 'static + VertexDefinition + VertexSource, Pl: 'static + PipelineLayout + Send + Sync, Rp: RenderPass + 'static + Send + Sync, Ib: Into>, I: 'static + Index, Ibb: Buffer + 'static, - L: DescriptorSetsCollection + Send + Sync + L: DescriptorSetsCollection + Send + Sync, Pc: 'static + Clone { assert!(self.render_pass.is_compatible_with(pipeline.subpass().render_pass())); assert_eq!(self.render_pass_subpass, pipeline.subpass().index()); unsafe { SecondaryGraphicsCommandBufferBuilder { - inner: self.inner.draw_indexed(pipeline, vertices, indices, dynamic, sets), + inner: self.inner.draw_indexed(pipeline, vertices, indices, dynamic, sets, push_constants), render_pass: self.render_pass, render_pass_subpass: self.render_pass_subpass, framebuffer: self.framebuffer, diff --git a/vulkano/src/descriptor/descriptor.rs b/vulkano/src/descriptor/descriptor.rs index 35653502..d89add4b 100644 --- a/vulkano/src/descriptor/descriptor.rs +++ b/vulkano/src/descriptor/descriptor.rs @@ -314,7 +314,7 @@ impl DescriptorType { } /// Describes which shader stages have access to a descriptor. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct ShaderStages { /// `True` means that the descriptor will be used by the vertex shader. pub vertex: bool, @@ -344,6 +344,19 @@ impl ShaderStages { } } + /// Creates a `ShaderStages` struct will all stages set to `false`. + #[inline] + pub fn none() -> ShaderStages { + ShaderStages { + vertex: false, + tessellation_control: false, + tessellation_evaluation: false, + geometry: false, + fragment: false, + compute: false, + } + } + /// Creates a `ShaderStages` struct will all graphics stages set to `true`. #[inline] pub fn all_graphics() -> ShaderStages { diff --git a/vulkano/src/descriptor/pipeline_layout/custom_pipeline_macro.rs b/vulkano/src/descriptor/pipeline_layout/custom_pipeline_macro.rs index 75cb608e..5218ad38 100644 --- a/vulkano/src/descriptor/pipeline_layout/custom_pipeline_macro.rs +++ b/vulkano/src/descriptor/pipeline_layout/custom_pipeline_macro.rs @@ -21,16 +21,23 @@ use sampler::Sampler; // TODO: more docs #[macro_export] macro_rules! pipeline_layout { - ($($name:ident: { $($field:ident: $ty:ty),* }),*) => { + (push_constants: { $($pc_f:ident: $pc_t:ty),* } $(, $name:ident: { $($field:ident: $ty:ty),* })*) => { + use std::mem; use std::sync::Arc; use std::vec::IntoIter as VecIntoIter; use $crate::OomError; use $crate::device::Device; use $crate::descriptor::descriptor::DescriptorDesc; + use $crate::descriptor::descriptor::ShaderStages; use $crate::descriptor::pipeline_layout::PipelineLayout; use $crate::descriptor::pipeline_layout::PipelineLayoutDesc; use $crate::descriptor::pipeline_layout::UnsafePipelineLayout; + #[derive(Debug, Copy, Clone)] + pub struct PushConstants { + $(pub $pc_f: $pc_t,)* + } + pub struct CustomPipeline { inner: UnsafePipelineLayout } @@ -44,8 +51,14 @@ macro_rules! pipeline_layout { ),* ]; + let push_constants = if mem::size_of::() >= 1 { + Some((0, mem::size_of::(), ShaderStages::all())) + } else { + None + }; + let inner = unsafe { - try!(UnsafePipelineLayout::new(device, layouts.iter())) + try!(UnsafePipelineLayout::new(device, layouts.iter(), push_constants)) }; Ok(Arc::new(CustomPipeline { @@ -85,6 +98,10 @@ macro_rules! pipeline_layout { pipeline_layout!{__inner__ (0) $($name: {$($field: $ty),*})*} }; + ($($name:ident: { $($field:ident: $ty:ty),* }),*) => { + pipeline_layout!{ push_constants: {} $(, $name: {$($field: $ty),*})* } + }; + (__inner__ ($num:expr) $name:ident: { $($field:ident: $ty:ty),* } $($rest:tt)*) => { pub mod $name { #![allow(unused_imports)] diff --git a/vulkano/src/descriptor/pipeline_layout/sys.rs b/vulkano/src/descriptor/pipeline_layout/sys.rs index 1e1772d7..fdb77a2c 100644 --- a/vulkano/src/descriptor/pipeline_layout/sys.rs +++ b/vulkano/src/descriptor/pipeline_layout/sys.rs @@ -18,6 +18,7 @@ use VulkanObject; use VulkanPointers; use vk; +use descriptor::descriptor::ShaderStages; use descriptor::descriptor_set::UnsafeDescriptorSetLayout; use device::Device; @@ -33,16 +34,19 @@ impl UnsafePipelineLayout { /// Creates a new `UnsafePipelineLayout`. // TODO: is this function unsafe? #[inline] - pub unsafe fn new<'a, I>(device: &Arc, layouts: I) - -> Result - where I: IntoIterator> + pub unsafe fn new<'a, I, P>(device: &Arc, layouts: I, push_constants: P) + -> Result + where I: IntoIterator>, + P: IntoIterator, { - UnsafePipelineLayout::new_inner(device, layouts.into_iter().map(|e| e.clone()).collect()) + UnsafePipelineLayout::new_inner(device, layouts.into_iter().map(|e| e.clone()).collect(), + push_constants.into_iter().collect()) } // TODO: is this function unsafe? unsafe fn new_inner(device: &Arc, - layouts: SmallVec<[Arc; 16]>) + layouts: SmallVec<[Arc; 16]>, + push_constants: SmallVec<[(usize, usize, ShaderStages); 8]>) -> Result { let vk = device.pointers(); @@ -51,6 +55,21 @@ impl UnsafePipelineLayout { let layouts_ids = layouts.iter().map(|l| l.internal_object()) .collect::>(); + let push_constants = push_constants.iter().map(|pc| { + // TODO: error + assert!(pc.2 != ShaderStages::none()); + assert!(pc.1 > 0); + assert!((pc.1 % 4) == 0); + assert!(pc.0 + pc.1 <= + device.physical_device().limits().max_push_constants_size() as usize); + + vk::PushConstantRange { + stageFlags: pc.2.into(), + offset: pc.0 as u32, + size: pc.1 as u32, + } + }).collect::>(); + let layout = { let infos = vk::PipelineLayoutCreateInfo { sType: vk::STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, @@ -58,8 +77,8 @@ impl UnsafePipelineLayout { flags: 0, // reserved setLayoutCount: layouts_ids.len() as u32, pSetLayouts: layouts_ids.as_ptr(), - pushConstantRangeCount: 0, // TODO: unimplemented - pPushConstantRanges: ptr::null(), // TODO: unimplemented + pushConstantRangeCount: push_constants.len() as u32, + pPushConstantRanges: push_constants.as_ptr(), }; let mut output = mem::uninitialized();