From 3ebaaca45d8b7d68443751c8c781130f4ac00313 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Sat, 20 Feb 2016 14:40:50 +0100 Subject: [PATCH] Descriptor sets are working --- vulkano-shaders/src/lib.rs | 27 ++++++ vulkano/examples/teapot.rs | 21 ++++- vulkano/examples/teapot_vs.glsl | 2 +- vulkano/examples/triangle.rs | 6 +- vulkano/src/buffer.rs | 10 +- vulkano/src/command_buffer/inner.rs | 50 ++++++---- vulkano/src/command_buffer/outer.rs | 8 +- vulkano/src/descriptor_set.rs | 107 ++++++++++++++++++++-- vulkano/src/pipeline/graphics_pipeline.rs | 6 ++ 9 files changed, 200 insertions(+), 37 deletions(-) diff --git a/vulkano-shaders/src/lib.rs b/vulkano-shaders/src/lib.rs index 018dc873..17031f34 100644 --- a/vulkano-shaders/src/lib.rs +++ b/vulkano-shaders/src/lib.rs @@ -242,6 +242,10 @@ fn write_descriptor_sets(doc: &parse::Spirv) -> String { pub struct Set1; unsafe impl ::vulkano::descriptor_set::DescriptorSetDesc for Set1 { + type Write = ::std::sync::Arc<::vulkano::buffer::BufferResource>; + + type Init = ::std::sync::Arc<::vulkano::buffer::BufferResource>; + fn descriptors(&self) -> Vec<::vulkano::descriptor_set::DescriptorDesc> { vec![ ::vulkano::descriptor_set::DescriptorDesc { @@ -252,6 +256,21 @@ unsafe impl ::vulkano::descriptor_set::DescriptorSetDesc for Set1 { } ] } + + fn decode_write(&self, write: Self::Write) -> Vec<::vulkano::descriptor_set::DescriptorWrite> { + vec![ + ::vulkano::descriptor_set::DescriptorWrite { + binding: 0, + array_element: 0, + content: ::vulkano::descriptor_set::DescriptorBind::UniformBuffer(write), + } + ] + } + + #[inline] + fn decode_init(&self, write: Self::Init) -> Vec<::vulkano::descriptor_set::DescriptorWrite> { + self.decode_write(write) + } } #[derive(Default)] @@ -269,6 +288,14 @@ unsafe impl ::vulkano::descriptor_set::PipelineLayoutDesc for Layout { layouts ] } + + fn decode_descriptor_sets(&self, sets: Self::DescriptorSets) + -> Vec<::std::sync::Arc<::vulkano::descriptor_set::AbstractDescriptorSet>> + { + vec![ + sets + ] + } } "#.to_owned(); diff --git a/vulkano/examples/teapot.rs b/vulkano/examples/teapot.rs index 08a1c679..8001bd56 100644 --- a/vulkano/examples/teapot.rs +++ b/vulkano/examples/teapot.rs @@ -18,7 +18,7 @@ fn main() { // TODO: for the moment the AMD driver crashes if you don't pass an ApplicationInfo, but in theory it's optional let app = vulkano::instance::ApplicationInfo { application_name: "test", application_version: 1, engine_name: "test", engine_version: 1 }; - let instance = vulkano::instance::Instance::new(Some(&app), &["VK_LAYER_LUNARG_draw_state"]).expect("failed to create instance"); + let instance = vulkano::instance::Instance::new(Some(&app), &[]).expect("failed to create instance"); let physical = vulkano::instance::PhysicalDevice::enumerate(&instance) .next().expect("no device available"); @@ -33,7 +33,7 @@ fn main() { let (device, queues) = vulkano::device::Device::new(&physical, physical.supported_features(), [(queue, 0.5)].iter().cloned(), - &["VK_LAYER_LUNARG_draw_state"]) + &[]) .expect("failed to create device"); let queue = queues.into_iter().next().unwrap(); @@ -66,6 +66,20 @@ fn main() { mapping[2].position = [0.25, -0.1]; } + let uniform_buffer = vulkano::buffer::Buffer::<[[f32; 4]; 4], _> + ::new(&device, &vulkano::buffer::Usage::all(), + vulkano::memory::HostVisible, &queue) + .expect("failed to create buffer"); + { + let mut mapping = uniform_buffer.try_write().unwrap(); + *mapping = [ + [1.0, 0.0, 0.0, 0.0], + [0.0, 2.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0] + ]; + } + mod vs { include!{concat!(env!("OUT_DIR"), "/examples-teapot_vs.rs")} } let vs = vs::TeapotShader::load(&device).expect("failed to create shader module"); mod fs { include!{concat!(env!("OUT_DIR"), "/examples-teapot_fs.rs")} } @@ -137,11 +151,12 @@ fn main() { }).collect::>(); + set.write(uniform_buffer.clone()); 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(), set.clone()) .draw_end() .build().unwrap() }).collect::>(); diff --git a/vulkano/examples/teapot_vs.glsl b/vulkano/examples/teapot_vs.glsl index aff34a2d..8ab548c4 100644 --- a/vulkano/examples/teapot_vs.glsl +++ b/vulkano/examples/teapot_vs.glsl @@ -5,7 +5,7 @@ layout(location = 0) in vec2 position; -uniform Data { +layout(set = 0, binding = 0) uniform Data { mat4 worldview; } uniforms; diff --git a/vulkano/examples/triangle.rs b/vulkano/examples/triangle.rs index 29465628..1ce1c5fb 100644 --- a/vulkano/examples/triangle.rs +++ b/vulkano/examples/triangle.rs @@ -162,7 +162,7 @@ fn main() { } }.unwrap(); - let pipeline: Arc>>> = { + let pipeline: Arc>, _>> = { let ia = vulkano::pipeline::input_assembly::InputAssembly { topology: vulkano::pipeline::input_assembly::PrimitiveTopology::TriangleList, primitive_restart_enable: false, @@ -190,7 +190,7 @@ fn main() { }; vulkano::pipeline::GraphicsPipeline::new(&device, &vs.main_entry_point(), &ia, &viewports, - &raster, &ms, &blend, &fs.main_entry_point(), + &raster, &ms, &blend, &fs.main_entry_point(), &vulkano::descriptor_set::PipelineLayout::new(&device, Default::default(), ()).unwrap(), &renderpass.subpass(0).unwrap()).unwrap() }; @@ -215,7 +215,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/buffer.rs b/vulkano/src/buffer.rs index d674a6f7..ffa4d90b 100644 --- a/vulkano/src/buffer.rs +++ b/vulkano/src/buffer.rs @@ -40,7 +40,10 @@ use VulkanPointers; use check_errors; use vk; -pub unsafe trait BufferResource: Resource { +pub unsafe trait BufferResource: Resource + ::VulkanObjectU64 { + /// Returns the size of the buffer in bytes. + fn size(&self) -> usize; + /// Instructs the resource that it is going to be used by the GPU soon in the future. The /// function should block if the memory is currently being accessed by the CPU. /// @@ -302,6 +305,11 @@ unsafe impl Resource for Buffer where M: MemorySourceChunk { } unsafe impl BufferResource for Buffer where M: MemorySourceChunk { + #[inline] + fn size(&self) -> usize { + self.inner.size + } + #[inline] fn gpu_access(&self, write: bool, offset: usize, size: usize, queue: &mut Queue, fence: Option>, semaphore: Option>) diff --git a/vulkano/src/command_buffer/inner.rs b/vulkano/src/command_buffer/inner.rs index cc6a0b13..78be6175 100644 --- a/vulkano/src/command_buffer/inner.rs +++ b/vulkano/src/command_buffer/inner.rs @@ -7,6 +7,7 @@ use buffer::BufferSlice; use buffer::BufferResource; use command_buffer::CommandBufferPool; use command_buffer::DynamicState; +use descriptor_set::PipelineLayoutDesc; use device::Queue; use framebuffer::ClearValue; use framebuffer::Framebuffer; @@ -263,14 +264,15 @@ impl InnerCommandBufferBuilder { /// Calls `vkCmdDraw`. // FIXME: push constants pub unsafe fn draw(mut self, pipeline: &Arc>, - vertices: V, dynamic: &DynamicState) -> InnerCommandBufferBuilder - where V: MultiVertex + vertices: V, dynamic: &DynamicState, + sets: L::DescriptorSets) -> InnerCommandBufferBuilder + where V: MultiVertex, L: PipelineLayoutDesc { // FIXME: add buffers to the resources { - self.bind_gfx_pipeline_state(pipeline, dynamic); + self.bind_gfx_pipeline_state(pipeline, dynamic, sets); let vk = self.device.pointers(); @@ -285,28 +287,40 @@ impl InnerCommandBufferBuilder { } fn bind_gfx_pipeline_state(&mut self, pipeline: &Arc>, - dynamic: &DynamicState) + dynamic: &DynamicState, sets: L::DescriptorSets) + where V: MultiVertex, L: PipelineLayoutDesc { - let vk = self.device.pointers(); + unsafe { + let vk = self.device.pointers(); - if self.graphics_pipeline != Some(pipeline.internal_object()) { - unsafe { + if self.graphics_pipeline != Some(pipeline.internal_object()) { vk.CmdBindPipeline(self.cmd.unwrap(), vk::PIPELINE_BIND_POINT_GRAPHICS, pipeline.internal_object()); + self.pipelines.push(pipeline.clone()); + self.graphics_pipeline = Some(pipeline.internal_object()); } - self.pipelines.push(pipeline.clone()); - self.graphics_pipeline = Some(pipeline.internal_object()); - } - if let Some(line_width) = dynamic.line_width { - assert!(pipeline.has_dynamic_line_width()); - // TODO: check limits - if self.dynamic_state.line_width != Some(line_width) { - unsafe { vk.CmdSetLineWidth(self.cmd.unwrap(), line_width) }; - self.dynamic_state.line_width = Some(line_width); + if let Some(line_width) = dynamic.line_width { + assert!(pipeline.has_dynamic_line_width()); + // TODO: check limits + if self.dynamic_state.line_width != Some(line_width) { + vk.CmdSetLineWidth(self.cmd.unwrap(), line_width); + self.dynamic_state.line_width = Some(line_width); + } + } else { + assert!(!pipeline.has_dynamic_line_width()); + } + + // FIXME: keep these alive + let descriptor_sets = pipeline.layout().description().decode_descriptor_sets(sets); + let descriptor_sets = descriptor_sets.into_iter().map(|set| set.internal_object()).collect::>(); + + // TODO: shouldn't rebind everything every time + if !descriptor_sets.is_empty() { + vk.CmdBindDescriptorSets(self.cmd.unwrap(), vk::PIPELINE_BIND_POINT_GRAPHICS, + pipeline.layout().internal_object(), 0, descriptor_sets.len() as u32, + descriptor_sets.as_ptr(), 0, ptr::null()); // FIXME: dynamic offsets } - } else { - assert!(!pipeline.has_dynamic_line_width()); } } diff --git a/vulkano/src/command_buffer/outer.rs b/vulkano/src/command_buffer/outer.rs index b85d5797..82257984 100644 --- a/vulkano/src/command_buffer/outer.rs +++ b/vulkano/src/command_buffer/outer.rs @@ -5,6 +5,7 @@ use buffer::BufferSlice; use command_buffer::CommandBufferPool; use command_buffer::inner::InnerCommandBufferBuilder; use command_buffer::inner::InnerCommandBuffer; +use descriptor_set::PipelineLayoutDesc; use device::Queue; use framebuffer::Framebuffer; use framebuffer::RenderPass; @@ -198,12 +199,13 @@ impl PrimaryCommandBufferBuilderInlineDraw { /// Calls `vkCmdDraw`. // FIXME: push constants pub fn draw(self, pipeline: &Arc>, - vertices: V, dynamic: &DynamicState) -> PrimaryCommandBufferBuilderInlineDraw - where V: MultiVertex + 'static, L: 'static + vertices: V, dynamic: &DynamicState, sets: L::DescriptorSets) + -> PrimaryCommandBufferBuilderInlineDraw + where V: MultiVertex + 'static, L: PipelineLayoutDesc + 'static { unsafe { PrimaryCommandBufferBuilderInlineDraw { - inner: self.inner.draw(pipeline, vertices, dynamic), + inner: self.inner.draw(pipeline, vertices, dynamic, sets), num_subpasses: self.num_subpasses, current_subpass: self.current_subpass, } diff --git a/vulkano/src/descriptor_set.rs b/vulkano/src/descriptor_set.rs index a2781448..cd122d2c 100644 --- a/vulkano/src/descriptor_set.rs +++ b/vulkano/src/descriptor_set.rs @@ -23,6 +23,7 @@ use std::mem; use std::ptr; use std::sync::Arc; +use buffer::BufferResource; use device::Device; use OomError; @@ -31,7 +32,7 @@ use VulkanPointers; use check_errors; use vk; - +/// Types that describe the layout of a pipeline (descriptor sets and push constants). pub unsafe trait PipelineLayoutDesc { type DescriptorSets; // example: (Arc>, Arc>) where Layout1 and Layout2 implement DescriptorSetDesc type DescriptorSetLayouts; // example: (Arc>, Arc>) where Layout1 and Layout2 implement DescriptorSetDesc @@ -39,19 +40,37 @@ pub unsafe trait PipelineLayoutDesc { fn decode_descriptor_set_layouts(&self, Self::DescriptorSetLayouts) -> Vec>; + fn decode_descriptor_sets(&self, Self::DescriptorSets) -> Vec>; + // FIXME: implement this correctly fn is_compatible_with

(&self, _: &P) -> bool where P: PipelineLayoutDesc { true } } -/// Description of a descriptor. -/// -/// A descriptor is a single entry in the list of resources accessible by a shader. This struct -/// describes it the resource that can be binded to it. +#[derive(Debug, Copy, Clone, Default)] +pub struct EmptyPipelineDesc; +unsafe impl PipelineLayoutDesc for EmptyPipelineDesc { + type DescriptorSets = (); + type DescriptorSetLayouts = (); + type PushConstants = (); + + fn decode_descriptor_set_layouts(&self, _: Self::DescriptorSetLayouts) -> Vec> { vec![] } + fn decode_descriptor_sets(&self, _: Self::DescriptorSets) -> Vec> { vec![] } +} + +/// Types that describe a single descriptor set. pub unsafe trait DescriptorSetDesc { + type Write; + + type Init; + fn descriptors(&self) -> Vec; // TODO: Cow for better perfs // FIXME: implement this correctly fn is_compatible_with(&self, _: &S) -> bool where S: DescriptorSetDesc { true } + + fn decode_write(&self, Self::Write) -> Vec; + + fn decode_init(&self, Self::Init) -> Vec; } pub struct DescriptorDesc { @@ -194,7 +213,7 @@ pub struct DescriptorSet { layout: Arc>, } -impl DescriptorSet { +impl DescriptorSet where S: DescriptorSetDesc { /// /// # Panic /// @@ -228,8 +247,62 @@ impl DescriptorSet { layout: layout.clone(), })) } + + pub fn write(&self, write: S::Write) { + let vk = self.pool.device.pointers(); + let write = self.layout.description().decode_write(write); + + // FIXME: store resources in the descriptor set so that they aren't destroyed + + // TODO: the architecture of this function is going to be tricky + + let buffer_descriptors = write.iter().enumerate().map(|(num, write)| { + match write.content { + DescriptorBind::UniformBuffer(ref buffer) => { + Some(vk::DescriptorBufferInfo { + buffer: buffer.internal_object(), + offset: 0, // FIXME: allow buffer slices + range: buffer.size() as u64, // FIXME: allow buffer slices + }) + }, + } + }).collect::>(); + + let vk_writes = write.iter().enumerate().map(|(num, write)| { + vk::WriteDescriptorSet { + sType: vk::STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + pNext: ptr::null(), + dstSet: self.set, + dstBinding: write.binding, + dstArrayElement: write.array_element, + descriptorCount: 1, + descriptorType: vk::DESCRIPTOR_TYPE_UNIFORM_BUFFER, // FIXME: + pImageInfo: ptr::null(), // FIXME: + pBufferInfo: if let Some(ref b) = buffer_descriptors[num] { b } else { ptr::null() }, + pTexelBufferView: ptr::null(), // FIXME: + } + }).collect::>(); + + unsafe { + vk.UpdateDescriptorSets(self.pool.device.internal_object(), vk_writes.len() as u32, + vk_writes.as_ptr(), 0, ptr::null()); + } + } } +impl VulkanObject for DescriptorSet { + type Object = vk::DescriptorSet; + + #[inline] + fn internal_object(&self) -> vk::DescriptorSet { + self.set + } +} + +/// Trait that is implemented on all `DescriptorSet` objects. +pub unsafe trait AbstractDescriptorSet: ::VulkanObjectU64 {} +unsafe impl AbstractDescriptorSet for DescriptorSet {} + pub struct DescriptorSetLayout { layout: vk::DescriptorSetLayout, device: Arc, @@ -313,9 +386,9 @@ impl

PipelineLayout

where P: PipelineLayoutDesc { let vk = device.pointers(); let layouts = description.decode_descriptor_set_layouts(layouts); - let layouts_ids = layouts.clone().into_iter().map(|l| { + let layouts_ids = layouts.iter().map(|l| { // FIXME: check that they belong to the same device - ::VulkanObjectU64::internal_object(&*l) + ::VulkanObjectU64::internal_object(&**l) }).collect::>(); let layout = unsafe { @@ -342,6 +415,11 @@ impl

PipelineLayout

where P: PipelineLayoutDesc { layouts: layouts, })) } + + #[inline] + pub fn description(&self) -> &P { + &self.description + } } impl

VulkanObject for PipelineLayout

{ @@ -392,3 +470,16 @@ impl DescriptorPool { })) } } + +// FIXME: shoud allow multiple array binds at once +pub struct DescriptorWrite { + pub binding: u32, + pub array_element: u32, + pub content: DescriptorBind, +} + +// FIXME: incomplete +#[derive(Clone)] // TODO: Debug +pub enum DescriptorBind { + UniformBuffer(Arc), +} diff --git a/vulkano/src/pipeline/graphics_pipeline.rs b/vulkano/src/pipeline/graphics_pipeline.rs index d00a290c..2d71ad63 100644 --- a/vulkano/src/pipeline/graphics_pipeline.rs +++ b/vulkano/src/pipeline/graphics_pipeline.rs @@ -296,6 +296,12 @@ impl GraphicsPipeline marker: PhantomData, })) } + + /// Returns the pipeline layout used in the constructor. + #[inline] + pub fn layout(&self) -> &Arc> { + &self.layout + } } impl GraphicsPipeline {