Add unsafe support for push constants

This commit is contained in:
Pierre Krieger 2016-04-08 21:52:40 +02:00
parent e9d82d8c18
commit f6733628e8
9 changed files with 104 additions and 40 deletions

View File

@ -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 - 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` 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`. 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.

View File

@ -220,7 +220,7 @@ fn main() {
.draw_inline(&renderpass, &framebuffer, renderpass::ClearValues { .draw_inline(&renderpass, &framebuffer, renderpass::ClearValues {
color: [0.0, 0.0, 1.0, 1.0] 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() .draw_end()
.build().unwrap() .build().unwrap()
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();

View File

@ -216,7 +216,7 @@ fn main() {
depth: 1.0, depth: 1.0,
}) })
.draw_indexed(&pipeline, (&vertex_buffer, &normals_buffer), &index_buffer, .draw_indexed(&pipeline, (&vertex_buffer, &normals_buffer), &index_buffer,
&vulkano::command_buffer::DynamicState::none(), &set) &vulkano::command_buffer::DynamicState::none(), &set, &())
.draw_end() .draw_end()
.build().unwrap() .build().unwrap()
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();

View File

@ -237,7 +237,7 @@ fn main() {
let command_buffers = framebuffers.iter().map(|framebuffer| { let command_buffers = framebuffers.iter().map(|framebuffer| {
vulkano::command_buffer::PrimaryCommandBufferBuilder::new(&cb_pool).unwrap() vulkano::command_buffer::PrimaryCommandBufferBuilder::new(&cb_pool).unwrap()
.draw_inline(&renderpass, &framebuffer, ([0.0, 0.0, 1.0, 1.0],)) .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() .draw_end()
.build().unwrap() .build().unwrap()
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();

View File

@ -686,15 +686,15 @@ impl InnerCommandBufferBuilder {
/// Calls `vkCmdDraw`. /// Calls `vkCmdDraw`.
// FIXME: push constants // FIXME: push constants
pub unsafe fn draw<V, Pv, Pl, L, Rp>(mut self, pipeline: &Arc<GraphicsPipeline<Pv, Pl, Rp>>, pub unsafe fn draw<V, Pv, Pl, L, Rp, Pc>(mut self, pipeline: &Arc<GraphicsPipeline<Pv, Pl, Rp>>,
vertices: V, dynamic: &DynamicState, vertices: V, dynamic: &DynamicState,
sets: L) -> InnerCommandBufferBuilder sets: L, push_constants: &Pc) -> InnerCommandBufferBuilder
where Pv: 'static + VertexDefinition + VertexSource<V>, L: DescriptorSetsCollection + Send + Sync, where Pv: 'static + VertexDefinition + VertexSource<V>, 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 // 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); let vertices = pipeline.vertex_definition().decode(vertices);
@ -727,17 +727,18 @@ impl InnerCommandBufferBuilder {
/// Calls `vkCmdDrawIndexed`. /// Calls `vkCmdDrawIndexed`.
// FIXME: push constants // FIXME: push constants
pub unsafe fn draw_indexed<'a, V, Pv, Pl, Rp, L, I, Ib, Ibb>(mut self, pipeline: &Arc<GraphicsPipeline<Pv, Pl, Rp>>, pub unsafe fn draw_indexed<'a, V, Pv, Pl, Rp, L, I, Ib, Ibb, Pc>(mut self, pipeline: &Arc<GraphicsPipeline<Pv, Pl, Rp>>,
vertices: V, indices: Ib, dynamic: &DynamicState, vertices: V, indices: Ib, dynamic: &DynamicState,
sets: L) -> InnerCommandBufferBuilder sets: L, push_constants: &Pc) -> InnerCommandBufferBuilder
where L: DescriptorSetsCollection + Send + Sync, where L: DescriptorSetsCollection + Send + Sync,
Pv: 'static + VertexDefinition + VertexSource<V>, Pv: 'static + VertexDefinition + VertexSource<V>,
Pl: 'static + PipelineLayout + Send + Sync, Rp: 'static + Send + Sync, Pl: 'static + PipelineLayout + Send + Sync, Rp: 'static + Send + Sync,
Ib: Into<BufferSlice<'a, [I], Ibb>>, I: 'static + Index, Ibb: Buffer + 'static Ib: Into<BufferSlice<'a, [I], Ibb>>, I: 'static + Index, Ibb: Buffer + 'static,
Pc: 'static + Clone
{ {
// FIXME: add buffers to the resources // 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(); let indices = indices.into();
@ -830,10 +831,10 @@ impl InnerCommandBufferBuilder {
} }
} }
fn bind_gfx_pipeline_state<V, Pl, L, Rp>(&mut self, pipeline: &Arc<GraphicsPipeline<V, Pl, Rp>>, fn bind_gfx_pipeline_state<V, Pl, L, Rp, Pc>(&mut self, pipeline: &Arc<GraphicsPipeline<V, Pl, Rp>>,
dynamic: &DynamicState, sets: L) dynamic: &DynamicState, sets: L, push_constants: &Pc)
where V: 'static + VertexDefinition + Send + Sync, L: DescriptorSetsCollection + Send + Sync, 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 { unsafe {
//assert!(sets.is_compatible_with(pipeline.layout())); //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: */); } 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::<SmallVec<[_; 32]>>()); let mut descriptor_sets = Some(descriptor_sets.into_iter().map(|set| set.inner_descriptor_set().internal_object()).collect::<SmallVec<[_; 32]>>());
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 // FIXME: input attachments of descriptor sets have to be checked against input
// attachments of the render pass // attachments of the render pass

View File

@ -281,17 +281,17 @@ pub struct PrimaryCommandBufferBuilderInlineDraw {
impl PrimaryCommandBufferBuilderInlineDraw { impl PrimaryCommandBufferBuilderInlineDraw {
/// Calls `vkCmdDraw`. /// Calls `vkCmdDraw`.
// FIXME: push constants // FIXME: push constants
pub fn draw<V, L, Pv, Pl, Rp>(self, pipeline: &Arc<GraphicsPipeline<Pv, Pl, Rp>>, pub fn draw<V, L, Pv, Pl, Rp, Pc>(self, pipeline: &Arc<GraphicsPipeline<Pv, Pl, Rp>>,
vertices: V, dynamic: &DynamicState, sets: L) vertices: V, dynamic: &DynamicState, sets: L, push_constants: &Pc)
-> PrimaryCommandBufferBuilderInlineDraw -> PrimaryCommandBufferBuilderInlineDraw
where Pv: VertexDefinition + VertexSource<V> + 'static, Pl: PipelineLayout + 'static + Send + Sync, Rp: 'static + Send + Sync, where Pv: VertexDefinition + VertexSource<V> + 'static, Pl: PipelineLayout + 'static + Send + Sync, Rp: 'static + Send + Sync,
L: DescriptorSetsCollection + Send + Sync L: DescriptorSetsCollection + Send + Sync, Pc: 'static + Clone
{ {
// FIXME: check subpass // FIXME: check subpass
unsafe { unsafe {
PrimaryCommandBufferBuilderInlineDraw { PrimaryCommandBufferBuilderInlineDraw {
inner: self.inner.draw(pipeline, vertices, dynamic, sets), inner: self.inner.draw(pipeline, vertices, dynamic, sets, push_constants),
num_subpasses: self.num_subpasses, num_subpasses: self.num_subpasses,
current_subpass: self.current_subpass, current_subpass: self.current_subpass,
} }
@ -299,18 +299,18 @@ impl PrimaryCommandBufferBuilderInlineDraw {
} }
/// Calls `vkCmdDrawIndexed`. /// Calls `vkCmdDrawIndexed`.
pub fn draw_indexed<'a, V, L, Pv, Pl, Rp, I, Ib, Ibb>(self, pipeline: &Arc<GraphicsPipeline<Pv, Pl, Rp>>, pub fn draw_indexed<'a, V, L, Pv, Pl, Rp, I, Ib, Ibb, Pc>(self, pipeline: &Arc<GraphicsPipeline<Pv, Pl, Rp>>,
vertices: V, indices: Ib, dynamic: &DynamicState, vertices: V, indices: Ib, dynamic: &DynamicState,
sets: L) -> PrimaryCommandBufferBuilderInlineDraw sets: L, push_constants: &Pc) -> PrimaryCommandBufferBuilderInlineDraw
where Pv: 'static + VertexDefinition + VertexSource<V> + Send + Sync, Pl: 'static + PipelineLayout + Send + Sync, Rp: 'static + Send + Sync, where Pv: 'static + VertexDefinition + VertexSource<V> + Send + Sync, Pl: 'static + PipelineLayout + Send + Sync, Rp: 'static + Send + Sync,
Ib: Into<BufferSlice<'a, [I], Ibb>>, I: 'static + Index, Ibb: Buffer + 'static + Send + Sync, Ib: Into<BufferSlice<'a, [I], Ibb>>, I: 'static + Index, Ibb: Buffer + 'static + Send + Sync,
L: DescriptorSetsCollection + Send + Sync L: DescriptorSetsCollection + Send + Sync, Pc: 'static + Clone
{ {
// FIXME: check subpass // FIXME: check subpass
unsafe { unsafe {
PrimaryCommandBufferBuilderInlineDraw { 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, num_subpasses: self.num_subpasses,
current_subpass: self.current_subpass, current_subpass: self.current_subpass,
} }
@ -528,19 +528,19 @@ impl<R> SecondaryGraphicsCommandBufferBuilder<R>
/// Calls `vkCmdDraw`. /// Calls `vkCmdDraw`.
// FIXME: push constants // FIXME: push constants
pub fn draw<V, L, Pv, Pl, Rp>(self, pipeline: &Arc<GraphicsPipeline<Pv, Pl, Rp>>, pub fn draw<V, L, Pv, Pl, Rp, Pc>(self, pipeline: &Arc<GraphicsPipeline<Pv, Pl, Rp>>,
vertices: V, dynamic: &DynamicState, sets: L) vertices: V, dynamic: &DynamicState, sets: L, push_constants: &Pc)
-> SecondaryGraphicsCommandBufferBuilder<R> -> SecondaryGraphicsCommandBufferBuilder<R>
where Pv: VertexDefinition + VertexSource<V> + 'static, Pl: PipelineLayout + 'static + Send + Sync, where Pv: VertexDefinition + VertexSource<V> + 'static, Pl: PipelineLayout + 'static + Send + Sync,
Rp: RenderPass + 'static + Send + Sync, L: DescriptorSetsCollection + Send + Sync, Rp: RenderPass + 'static + Send + Sync, L: DescriptorSetsCollection + Send + Sync,
R: RenderPassCompatible<Rp> R: RenderPassCompatible<Rp>, Pc: 'static + Clone
{ {
assert!(self.render_pass.is_compatible_with(pipeline.subpass().render_pass())); assert!(self.render_pass.is_compatible_with(pipeline.subpass().render_pass()));
assert_eq!(self.render_pass_subpass, pipeline.subpass().index()); assert_eq!(self.render_pass_subpass, pipeline.subpass().index());
unsafe { unsafe {
SecondaryGraphicsCommandBufferBuilder { 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: self.render_pass,
render_pass_subpass: self.render_pass_subpass, render_pass_subpass: self.render_pass_subpass,
framebuffer: self.framebuffer, framebuffer: self.framebuffer,
@ -549,20 +549,20 @@ impl<R> SecondaryGraphicsCommandBufferBuilder<R>
} }
/// Calls `vkCmdDrawIndexed`. /// Calls `vkCmdDrawIndexed`.
pub fn draw_indexed<'a, V, L, Pv, Pl, Rp, I, Ib, Ibb>(self, pipeline: &Arc<GraphicsPipeline<Pv, Pl, Rp>>, pub fn draw_indexed<'a, V, L, Pv, Pl, Rp, I, Ib, Ibb, Pc>(self, pipeline: &Arc<GraphicsPipeline<Pv, Pl, Rp>>,
vertices: V, indices: Ib, dynamic: &DynamicState, vertices: V, indices: Ib, dynamic: &DynamicState,
sets: L) -> SecondaryGraphicsCommandBufferBuilder<R> sets: L, push_constants: &Pc) -> SecondaryGraphicsCommandBufferBuilder<R>
where Pv: 'static + VertexDefinition + VertexSource<V>, Pl: 'static + PipelineLayout + Send + Sync, where Pv: 'static + VertexDefinition + VertexSource<V>, Pl: 'static + PipelineLayout + Send + Sync,
Rp: RenderPass + 'static + Send + Sync, Rp: RenderPass + 'static + Send + Sync,
Ib: Into<BufferSlice<'a, [I], Ibb>>, I: 'static + Index, Ibb: Buffer + 'static, Ib: Into<BufferSlice<'a, [I], Ibb>>, 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!(self.render_pass.is_compatible_with(pipeline.subpass().render_pass()));
assert_eq!(self.render_pass_subpass, pipeline.subpass().index()); assert_eq!(self.render_pass_subpass, pipeline.subpass().index());
unsafe { unsafe {
SecondaryGraphicsCommandBufferBuilder { 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: self.render_pass,
render_pass_subpass: self.render_pass_subpass, render_pass_subpass: self.render_pass_subpass,
framebuffer: self.framebuffer, framebuffer: self.framebuffer,

View File

@ -314,7 +314,7 @@ impl DescriptorType {
} }
/// Describes which shader stages have access to a descriptor. /// Describes which shader stages have access to a descriptor.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ShaderStages { pub struct ShaderStages {
/// `True` means that the descriptor will be used by the vertex shader. /// `True` means that the descriptor will be used by the vertex shader.
pub vertex: bool, 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`. /// Creates a `ShaderStages` struct will all graphics stages set to `true`.
#[inline] #[inline]
pub fn all_graphics() -> ShaderStages { pub fn all_graphics() -> ShaderStages {

View File

@ -21,16 +21,23 @@ use sampler::Sampler;
// TODO: more docs // TODO: more docs
#[macro_export] #[macro_export]
macro_rules! pipeline_layout { 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::sync::Arc;
use std::vec::IntoIter as VecIntoIter; use std::vec::IntoIter as VecIntoIter;
use $crate::OomError; use $crate::OomError;
use $crate::device::Device; use $crate::device::Device;
use $crate::descriptor::descriptor::DescriptorDesc; use $crate::descriptor::descriptor::DescriptorDesc;
use $crate::descriptor::descriptor::ShaderStages;
use $crate::descriptor::pipeline_layout::PipelineLayout; use $crate::descriptor::pipeline_layout::PipelineLayout;
use $crate::descriptor::pipeline_layout::PipelineLayoutDesc; use $crate::descriptor::pipeline_layout::PipelineLayoutDesc;
use $crate::descriptor::pipeline_layout::UnsafePipelineLayout; use $crate::descriptor::pipeline_layout::UnsafePipelineLayout;
#[derive(Debug, Copy, Clone)]
pub struct PushConstants {
$(pub $pc_f: $pc_t,)*
}
pub struct CustomPipeline { pub struct CustomPipeline {
inner: UnsafePipelineLayout inner: UnsafePipelineLayout
} }
@ -44,8 +51,14 @@ macro_rules! pipeline_layout {
),* ),*
]; ];
let push_constants = if mem::size_of::<PushConstants>() >= 1 {
Some((0, mem::size_of::<PushConstants>(), ShaderStages::all()))
} else {
None
};
let inner = unsafe { let inner = unsafe {
try!(UnsafePipelineLayout::new(device, layouts.iter())) try!(UnsafePipelineLayout::new(device, layouts.iter(), push_constants))
}; };
Ok(Arc::new(CustomPipeline { Ok(Arc::new(CustomPipeline {
@ -85,6 +98,10 @@ macro_rules! pipeline_layout {
pipeline_layout!{__inner__ (0) $($name: {$($field: $ty),*})*} 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)*) => { (__inner__ ($num:expr) $name:ident: { $($field:ident: $ty:ty),* } $($rest:tt)*) => {
pub mod $name { pub mod $name {
#![allow(unused_imports)] #![allow(unused_imports)]

View File

@ -18,6 +18,7 @@ use VulkanObject;
use VulkanPointers; use VulkanPointers;
use vk; use vk;
use descriptor::descriptor::ShaderStages;
use descriptor::descriptor_set::UnsafeDescriptorSetLayout; use descriptor::descriptor_set::UnsafeDescriptorSetLayout;
use device::Device; use device::Device;
@ -33,16 +34,19 @@ impl UnsafePipelineLayout {
/// Creates a new `UnsafePipelineLayout`. /// Creates a new `UnsafePipelineLayout`.
// TODO: is this function unsafe? // TODO: is this function unsafe?
#[inline] #[inline]
pub unsafe fn new<'a, I>(device: &Arc<Device>, layouts: I) pub unsafe fn new<'a, I, P>(device: &Arc<Device>, layouts: I, push_constants: P)
-> Result<UnsafePipelineLayout, OomError> -> Result<UnsafePipelineLayout, OomError>
where I: IntoIterator<Item = &'a Arc<UnsafeDescriptorSetLayout>> where I: IntoIterator<Item = &'a Arc<UnsafeDescriptorSetLayout>>,
P: IntoIterator<Item = (usize, usize, ShaderStages)>,
{ {
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? // TODO: is this function unsafe?
unsafe fn new_inner(device: &Arc<Device>, unsafe fn new_inner(device: &Arc<Device>,
layouts: SmallVec<[Arc<UnsafeDescriptorSetLayout>; 16]>) layouts: SmallVec<[Arc<UnsafeDescriptorSetLayout>; 16]>,
push_constants: SmallVec<[(usize, usize, ShaderStages); 8]>)
-> Result<UnsafePipelineLayout, OomError> -> Result<UnsafePipelineLayout, OomError>
{ {
let vk = device.pointers(); let vk = device.pointers();
@ -51,6 +55,21 @@ impl UnsafePipelineLayout {
let layouts_ids = layouts.iter().map(|l| l.internal_object()) let layouts_ids = layouts.iter().map(|l| l.internal_object())
.collect::<SmallVec<[_; 16]>>(); .collect::<SmallVec<[_; 16]>>();
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::<SmallVec<[_; 8]>>();
let layout = { let layout = {
let infos = vk::PipelineLayoutCreateInfo { let infos = vk::PipelineLayoutCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, sType: vk::STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
@ -58,8 +77,8 @@ impl UnsafePipelineLayout {
flags: 0, // reserved flags: 0, // reserved
setLayoutCount: layouts_ids.len() as u32, setLayoutCount: layouts_ids.len() as u32,
pSetLayouts: layouts_ids.as_ptr(), pSetLayouts: layouts_ids.as_ptr(),
pushConstantRangeCount: 0, // TODO: unimplemented pushConstantRangeCount: push_constants.len() as u32,
pPushConstantRanges: ptr::null(), // TODO: unimplemented pPushConstantRanges: push_constants.as_ptr(),
}; };
let mut output = mem::uninitialized(); let mut output = mem::uninitialized();