From 5c4504e5bef47442621959906b586447ba88f4ea Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Sat, 4 Jun 2016 11:31:12 +0200 Subject: [PATCH 1/3] Rework command pools --- examples/src/bin/image.rs | 6 +- examples/src/bin/teapot.rs | 4 +- examples/src/bin/triangle.rs | 10 +- vulkano/src/command_buffer/inner.rs | 148 ++++------ vulkano/src/command_buffer/mod.rs | 3 +- vulkano/src/command_buffer/outer.rs | 242 +++++++++------- vulkano/src/command_buffer/pool.rs | 418 +++++++++++++++++++++++++--- vulkano/src/device.rs | 38 +++ 8 files changed, 627 insertions(+), 242 deletions(-) diff --git a/examples/src/bin/image.rs b/examples/src/bin/image.rs index 5002208b..fb6a62d3 100644 --- a/examples/src/bin/image.rs +++ b/examples/src/bin/image.rs @@ -63,10 +63,6 @@ fn main() { }; - let cb_pool = vulkano::command_buffer::CommandBufferPool::new(&device, &queue.family()); - - - let vertex_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer::<[Vertex]> @@ -202,7 +198,7 @@ fn main() { let command_buffers = framebuffers.iter().map(|framebuffer| { - vulkano::command_buffer::PrimaryCommandBufferBuilder::new(&cb_pool) + vulkano::command_buffer::PrimaryCommandBufferBuilder::new(&device, queue.family()) .copy_buffer_to_color_image(&pixel_buffer, &texture, 0, 0 .. 1, [0, 0, 0], [texture.dimensions().width(), texture.dimensions().height(), 1]) //.clear_color_image(&texture, [0.0, 1.0, 0.0, 1.0]) diff --git a/examples/src/bin/teapot.rs b/examples/src/bin/teapot.rs index 87275d6a..ff1bacff 100644 --- a/examples/src/bin/teapot.rs +++ b/examples/src/bin/teapot.rs @@ -68,8 +68,6 @@ fn main() { }; - let cb_pool = vulkano::command_buffer::CommandBufferPool::new(&device, &queue.family()); - let depth_buffer = vulkano::image::attachment::AttachmentImage::transient(&device, images[0].dimensions(), vulkano::format::D16Unorm).unwrap(); @@ -204,7 +202,7 @@ fn main() { let command_buffers = framebuffers.iter().map(|framebuffer| { - vulkano::command_buffer::PrimaryCommandBufferBuilder::new(&cb_pool) + vulkano::command_buffer::PrimaryCommandBufferBuilder::new(&device, queue.family()) .draw_inline(&renderpass, &framebuffer, renderpass::ClearValues { color: [0.0, 0.0, 1.0, 1.0], depth: 1.0, diff --git a/examples/src/bin/triangle.rs b/examples/src/bin/triangle.rs index 706cec2d..a77d265a 100644 --- a/examples/src/bin/triangle.rs +++ b/examples/src/bin/triangle.rs @@ -34,7 +34,6 @@ use vulkano_win::VkSurfaceBuild; use vulkano::buffer::BufferUsage; use vulkano::buffer::CpuAccessibleBuffer; use vulkano::command_buffer; -use vulkano::command_buffer::CommandBufferPool; use vulkano::command_buffer::DynamicState; use vulkano::command_buffer::PrimaryCommandBufferBuilder; use vulkano::command_buffer::Submission; @@ -235,10 +234,6 @@ fn main() { // implicitely does a lot of computation whenever you draw. In Vulkan, you have to do all this // manually. - // We are going to create a command buffer below. Command buffers need to be allocated - // from a *command buffer pool*, so we create the pool. - let cb_pool = CommandBufferPool::new(&device, &queue.family()); - // The next step is to create a *render pass*, which is an object that describes where the // output of the graphics pipeline will go. It describes the layout of the images // where the colors, depth and/or stencil information will be written. @@ -397,7 +392,10 @@ fn main() { // Building a command buffer is an expensive operation (usually a few hundred // microseconds), but it is known to be a hot path in the driver and is expected to be // optimized. - let command_buffer = PrimaryCommandBufferBuilder::new(&cb_pool) + // + // Note that we have to pass a queue family when we create the command buffer. The command + // buffer will only be executable on that given queue family. + let command_buffer = PrimaryCommandBufferBuilder::new(&device, queue.family()) // Before we can draw, we have to *enter a render pass*. There are two methods to do // this: `draw_inline` and `draw_secondary`. The latter is a bit more advanced and is // not covered here. diff --git a/vulkano/src/command_buffer/inner.rs b/vulkano/src/command_buffer/inner.rs index f19e8ec1..e4e85b23 100644 --- a/vulkano/src/command_buffer/inner.rs +++ b/vulkano/src/command_buffer/inner.rs @@ -26,9 +26,11 @@ use buffer::Buffer; use buffer::BufferSlice; use buffer::TypedBuffer; use buffer::traits::AccessRange as BufferAccessRange; -use command_buffer::CommandBufferPool; use command_buffer::DrawIndirectCommand; use command_buffer::DynamicState; +use command_buffer::pool::CommandPool; +use command_buffer::pool::CommandPoolFinished; +use command_buffer::pool::StandardCommandPool; use descriptor::descriptor_set::DescriptorSetsCollection; use descriptor::PipelineLayout; use device::Queue; @@ -76,9 +78,9 @@ lazy_static! { // for this design is that we want to minimize the number of pipeline barriers. In order to know // what must be in a pipeline barrier, we have to be ahead of the actual commands. // -pub struct InnerCommandBufferBuilder { +pub struct InnerCommandBufferBuilder

where P: CommandPool { device: Arc, - pool: Arc, + pool: Option

, cmd: Option, // If true, we're inside a secondary command buffer (compute or graphics). @@ -129,38 +131,17 @@ pub struct InnerCommandBufferBuilder { current_dynamic_state: DynamicState, } -impl InnerCommandBufferBuilder { +impl

InnerCommandBufferBuilder

where P: CommandPool { /// Creates a new builder. - pub fn new(pool: &Arc, secondary: bool, secondary_cont: Option>, + pub fn new(pool: P, secondary: bool, secondary_cont: Option>, secondary_cont_fb: Option<&Arc>>) - -> Result + -> Result, OomError> where R: RenderPass + 'static + Send + Sync { - let device = pool.device(); + let device = pool.device().clone(); let vk = device.pointers(); - let pool_obj = pool.internal_object_guard(); - - let cmd = unsafe { - let infos = vk::CommandBufferAllocateInfo { - sType: vk::STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - pNext: ptr::null(), - commandPool: *pool_obj, - level: if secondary { - assert!(secondary_cont.is_some()); - vk::COMMAND_BUFFER_LEVEL_SECONDARY - } else { - vk::COMMAND_BUFFER_LEVEL_PRIMARY - }, - // vulkan can allocate multiple command buffers at once, hence the 1 - commandBufferCount: 1, - }; - - let mut output = mem::uninitialized(); - try!(check_errors(vk.AllocateCommandBuffers(device.internal_object(), &infos, - &mut output))); - output - }; + let cmd = try!(pool.alloc(secondary, 1)).next().unwrap().internal_object(); let mut keep_alive = Vec::new(); @@ -206,7 +187,7 @@ impl InnerCommandBufferBuilder { Ok(InnerCommandBufferBuilder { device: device.clone(), - pool: pool.clone(), + pool: Some(pool), cmd: Some(cmd), is_secondary: secondary, is_secondary_graphics: secondary_cont.is_some(), @@ -230,9 +211,12 @@ impl InnerCommandBufferBuilder { /// # Safety /// /// Care must be taken to respect the rules about secondary command buffers. - pub unsafe fn execute_commands<'a>(mut self, cb_arc: Arc, - cb: &InnerCommandBuffer) -> InnerCommandBufferBuilder + pub unsafe fn execute_commands<'a, S>(mut self, cb_arc: Arc, + cb: &InnerCommandBuffer) + -> InnerCommandBufferBuilder

+ where S: CommandPool { + debug_assert!(cb.is_secondary); debug_assert!(!self.is_secondary); debug_assert!(!self.is_secondary_graphics); @@ -351,7 +335,7 @@ impl InnerCommandBufferBuilder { /// - Care must be taken to respect the rules about secondary command buffers. /// pub unsafe fn update_buffer<'a, B, T, Bt>(mut self, buffer: B, data: &T) - -> InnerCommandBufferBuilder + -> InnerCommandBufferBuilder

where B: Into>, Bt: Buffer + 'static, T: Clone + Send + Sync + 'static { debug_assert!(self.render_pass_staging_commands.is_empty()); @@ -402,12 +386,12 @@ impl InnerCommandBufferBuilder { /// - Care must be taken to respect the rules about secondary command buffers. /// pub unsafe fn fill_buffer(mut self, buffer: &Arc, offset: usize, - size: usize, data: u32) -> InnerCommandBufferBuilder + size: usize, data: u32) -> InnerCommandBufferBuilder

where B: Buffer + 'static { debug_assert!(self.render_pass_staging_commands.is_empty()); - assert!(self.pool.queue_family().supports_transfers()); + assert!(self.pool.as_ref().unwrap().queue_family().supports_transfers()); assert!(offset + size <= buffer.size()); assert!(offset % 4 == 0); assert!(size % 4 == 0); @@ -448,7 +432,7 @@ impl InnerCommandBufferBuilder { // TODO: doesn't support slices pub unsafe fn copy_buffer(mut self, source: &Arc, destination: &Arc) - -> InnerCommandBufferBuilder + -> InnerCommandBufferBuilder

where Bs: TypedBuffer + 'static, Bd: TypedBuffer + 'static { debug_assert!(self.render_pass_staging_commands.is_empty()); @@ -492,7 +476,7 @@ impl InnerCommandBufferBuilder { /// - Care must be taken to respect the rules about secondary command buffers. /// pub unsafe fn clear_color_image<'a, I, V>(mut self, image: &Arc, color: V) - -> InnerCommandBufferBuilder + -> InnerCommandBufferBuilder

where I: ImageClearValue + 'static // FIXME: should accept uint and int images too { debug_assert!(self.render_pass_staging_commands.is_empty()); @@ -536,11 +520,11 @@ impl InnerCommandBufferBuilder { /// /// - Care must be taken to respect the rules about secondary command buffers. /// - pub unsafe fn copy_buffer_to_color_image<'a, P, S, Sb, Img>(mut self, source: S, image: &Arc, + pub unsafe fn copy_buffer_to_color_image<'a, Pi, S, Sb, Img>(mut self, source: S, image: &Arc, mip_level: u32, array_layers_range: Range, offset: [u32; 3], extent: [u32; 3]) - -> InnerCommandBufferBuilder - where S: Into>, Img: ImageContent

+ Image + 'static, + -> InnerCommandBufferBuilder

+ where S: Into>, Img: ImageContent + Image + 'static, Sb: Buffer + 'static { // FIXME: check the parameters @@ -605,11 +589,11 @@ impl InnerCommandBufferBuilder { /// /// - Care must be taken to respect the rules about secondary command buffers. /// - pub unsafe fn copy_color_image_to_buffer<'a, P, S, Sb, Img>(mut self, dest: S, image: &Arc, + pub unsafe fn copy_color_image_to_buffer<'a, Pi, S, Sb, Img>(mut self, dest: S, image: &Arc, mip_level: u32, array_layers_range: Range, offset: [u32; 3], extent: [u32; 3]) - -> InnerCommandBufferBuilder - where S: Into>, Img: ImageContent

+ Image + 'static, + -> InnerCommandBufferBuilder

+ where S: Into>, Img: ImageContent + Image + 'static, Sb: Buffer + 'static { // FIXME: check the parameters @@ -670,7 +654,7 @@ impl InnerCommandBufferBuilder { source_array_layers: Range, src_coords: [Range; 3], destination: &Arc, dest_mip_level: u32, dest_array_layers: Range, dest_coords: [Range; 3]) - -> InnerCommandBufferBuilder + -> InnerCommandBufferBuilder

where Si: Image + 'static, Di: Image + 'static { // FIXME: check the parameters @@ -745,7 +729,7 @@ impl InnerCommandBufferBuilder { } pub unsafe fn dispatch(mut self, pipeline: &Arc>, sets: L, - dimensions: [u32; 3], push_constants: &Pc) -> InnerCommandBufferBuilder + dimensions: [u32; 3], push_constants: &Pc) -> InnerCommandBufferBuilder

where L: 'static + DescriptorSetsCollection + Send + Sync, Pl: 'static + PipelineLayout + Send + Sync, Pc: 'static + Clone + Send + Sync @@ -765,7 +749,7 @@ impl InnerCommandBufferBuilder { // FIXME: push constants pub unsafe fn draw(mut self, pipeline: &Arc>, vertices: V, dynamic: &DynamicState, - sets: L, push_constants: &Pc) -> InnerCommandBufferBuilder + sets: L, push_constants: &Pc) -> InnerCommandBufferBuilder

where Pv: 'static + VertexSource, L: DescriptorSetsCollection + Send + Sync, Pl: 'static + PipelineLayout + Send + Sync, Rp: 'static + Send + Sync, Pc: 'static + Clone + Send + Sync @@ -807,7 +791,7 @@ impl InnerCommandBufferBuilder { // FIXME: push constants 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, push_constants: &Pc) -> InnerCommandBufferBuilder + sets: L, push_constants: &Pc) -> InnerCommandBufferBuilder

where L: DescriptorSetsCollection + Send + Sync, Pv: 'static + VertexSource, Pl: 'static + PipelineLayout + Send + Sync, Rp: 'static + Send + Sync, @@ -865,7 +849,7 @@ impl InnerCommandBufferBuilder { // FIXME: push constants pub unsafe fn draw_indirect(mut self, buffer: &Arc, pipeline: &Arc>, vertices: V, dynamic: &DynamicState, - sets: L, push_constants: &Pc) -> InnerCommandBufferBuilder + sets: L, push_constants: &Pc) -> InnerCommandBufferBuilder

where Pv: 'static + VertexSource, L: DescriptorSetsCollection + Send + Sync, Pl: 'static + PipelineLayout + Send + Sync, Rp: 'static + Send + Sync, Pc: 'static + Clone + Send + Sync, I: 'static + TypedBuffer @@ -1088,7 +1072,7 @@ impl InnerCommandBufferBuilder { pub unsafe fn begin_renderpass(mut self, render_pass: &Arc, framebuffer: &Arc>, secondary_cmd_buffers: bool, - clear_values: &[ClearValue]) -> InnerCommandBufferBuilder + clear_values: &[ClearValue]) -> InnerCommandBufferBuilder

where R: RenderPass + 'static, F: RenderPass + 'static { debug_assert!(self.render_pass_staging_commands.is_empty()); @@ -1178,7 +1162,7 @@ impl InnerCommandBufferBuilder { } #[inline] - pub unsafe fn next_subpass(mut self, secondary_cmd_buffers: bool) -> InnerCommandBufferBuilder { + pub unsafe fn next_subpass(mut self, secondary_cmd_buffers: bool) -> InnerCommandBufferBuilder

{ debug_assert!(!self.render_pass_staging_commands.is_empty()); let content = if secondary_cmd_buffers { @@ -1201,7 +1185,7 @@ impl InnerCommandBufferBuilder { /// Assumes that you're inside a render pass and that all subpasses have been processed. /// #[inline] - pub unsafe fn end_renderpass(mut self) -> InnerCommandBufferBuilder { + pub unsafe fn end_renderpass(mut self) -> InnerCommandBufferBuilder

{ debug_assert!(!self.render_pass_staging_commands.is_empty()); self.flush_render_pass(); self.staging_commands.push(Box::new(move |vk, cmd| { @@ -1624,7 +1608,7 @@ impl InnerCommandBufferBuilder { } /// Finishes building the command buffer. - pub fn build(mut self) -> Result { + pub fn build(mut self) -> Result, OomError> { unsafe { self.flush_render_pass(); self.flush(true); @@ -1673,7 +1657,7 @@ impl InnerCommandBufferBuilder { debug_assert!(self.staging_required_image_accesses.is_empty()); let vk = self.device.pointers(); - let _ = self.pool.internal_object_guard(); // the pool needs to be synchronized + let _pool_lock = self.pool.as_ref().unwrap().lock(); // the pool needs to be synchronized let cmd = self.cmd.take().unwrap(); // Ending the commands recording. @@ -1681,8 +1665,9 @@ impl InnerCommandBufferBuilder { Ok(InnerCommandBuffer { device: self.device.clone(), - pool: self.pool.clone(), + pool: self.pool.take().unwrap().finish(), cmd: cmd, + is_secondary: self.is_secondary, buffers_state: self.buffers_state.clone(), // TODO: meh images_state: self.images_state.clone(), // TODO: meh extern_buffers_sync: { @@ -1737,7 +1722,7 @@ impl InnerCommandBufferBuilder { } } -impl Drop for InnerCommandBufferBuilder { +impl

Drop for InnerCommandBufferBuilder

where P: CommandPool { #[inline] fn drop(&mut self) { if let Some(cmd) = self.cmd { @@ -1745,18 +1730,18 @@ impl Drop for InnerCommandBufferBuilder { let vk = self.device.pointers(); vk.EndCommandBuffer(cmd); // TODO: really needed? - let pool = self.pool.internal_object_guard(); - vk.FreeCommandBuffers(self.device.internal_object(), *pool, 1, &cmd); + self.pool.as_ref().unwrap().free(self.is_secondary, Some(cmd.into()).into_iter()); } } } } /// Actual implementation of all command buffers. -pub struct InnerCommandBuffer { +pub struct InnerCommandBuffer

> where P: CommandPool { device: Arc, - pool: Arc, + pool: P::Finished, cmd: vk::CommandBuffer, + is_secondary: bool, buffers_state: HashMap<(BufferKey, usize), InternalBufferBlockAccess, BuildHasherDefault>, images_state: HashMap<(ImageKey, (u32, u32)), InternalImageBlockAccess, BuildHasherDefault>, extern_buffers_sync: SmallVec<[(Arc, SmallVec<[BufferAccessRange; 4]>); 32]>, @@ -1773,9 +1758,12 @@ pub struct InnerCommandBuffer { /// - Panicks if the queue doesn't belong to the device this command buffer was created with. /// - Panicks if the queue doesn't belong to the family the pool was created with. /// -pub fn submit(me: &InnerCommandBuffer, me_arc: Arc, - queue: &Arc) -> Result, OomError> // TODO: wrong error type +pub fn submit

(me: &InnerCommandBuffer

, me_arc: Arc, + queue: &Arc) -> Result, OomError> // TODO: wrong error type + where P: CommandPool { + debug_assert!(!me.is_secondary); + // TODO: see comment of GLOBAL_MUTEX let _global_lock = GLOBAL_MUTEX.lock().unwrap(); @@ -1891,13 +1879,13 @@ pub fn submit(me: &InnerCommandBuffer, me_arc: Arc, } for transition in result.before_transitions { - let cb = transition_cb(&me.pool, resource.clone(), transition.block, transition.from, transition.to).unwrap(); + let cb = transition_cb(Device::standard_command_pool(&me.device, me.pool.queue_family()), resource.clone(), transition.block, transition.from, transition.to).unwrap(); before_command_buffers.push(cb.cmd); submission.keep_alive_cb.lock().unwrap().push(Arc::new(cb)); } for transition in result.after_transitions { - let cb = transition_cb(&me.pool, resource.clone(), transition.block, transition.from, transition.to).unwrap(); + let cb = transition_cb(Device::standard_command_pool(&me.device, me.pool.queue_family()), resource.clone(), transition.block, transition.from, transition.to).unwrap(); after_command_buffers.push(cb.cmd); submission.keep_alive_cb.lock().unwrap().push(Arc::new(cb)); } @@ -2036,13 +2024,11 @@ pub fn submit(me: &InnerCommandBuffer, me_arc: Arc, Ok(submission) } -impl Drop for InnerCommandBuffer { +impl

Drop for InnerCommandBuffer

where P: CommandPool { #[inline] fn drop(&mut self) { unsafe { - let vk = self.device.pointers(); - let pool = self.pool.internal_object_guard(); - vk.FreeCommandBuffers(self.device.internal_object(), *pool, 1, &self.cmd); + self.pool.free(self.is_secondary, Some(self.cmd.into()).into_iter()); } } } @@ -2213,28 +2199,15 @@ struct InternalImageBlockAccess { /// Builds an `InnerCommandBuffer` whose only purpose is to transition an image between two /// layouts. -fn transition_cb(pool: &Arc, image: Arc, block: (u32, u32), - old_layout: ImageLayout, new_layout: ImageLayout) - -> Result +fn transition_cb

(pool: P, image: Arc, block: (u32, u32), + old_layout: ImageLayout, new_layout: ImageLayout) + -> Result, OomError> + where P: CommandPool { - let device = pool.device(); + let device = pool.device().clone(); let vk = device.pointers(); - let pool_obj = pool.internal_object_guard(); - let cmd = unsafe { - let infos = vk::CommandBufferAllocateInfo { - sType: vk::STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - pNext: ptr::null(), - commandPool: *pool_obj, - level: vk::COMMAND_BUFFER_LEVEL_PRIMARY, - commandBufferCount: 1, - }; - - let mut output = mem::uninitialized(); - try!(check_errors(vk.AllocateCommandBuffers(device.internal_object(), &infos, - &mut output))); - output - }; + let cmd = try!(pool.alloc(false, 1)).next().unwrap().internal_object(); unsafe { let infos = vk::CommandBufferBeginInfo { @@ -2287,8 +2260,9 @@ fn transition_cb(pool: &Arc, image: Arc, block: (u32, Ok(InnerCommandBuffer { device: device.clone(), - pool: pool.clone(), + pool: pool.finish(), cmd: cmd, + is_secondary: false, buffers_state: HashMap::with_hasher(BuildHasherDefault::::default()), images_state: HashMap::with_hasher(BuildHasherDefault::::default()), extern_buffers_sync: SmallVec::new(), diff --git a/vulkano/src/command_buffer/mod.rs b/vulkano/src/command_buffer/mod.rs index 81839284..baa38243 100644 --- a/vulkano/src/command_buffer/mod.rs +++ b/vulkano/src/command_buffer/mod.rs @@ -52,7 +52,6 @@ pub use self::outer::SecondaryGraphicsCommandBufferBuilder; pub use self::outer::SecondaryGraphicsCommandBuffer; pub use self::outer::SecondaryComputeCommandBufferBuilder; pub use self::outer::SecondaryComputeCommandBuffer; -pub use self::pool::CommandBufferPool; #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -75,4 +74,4 @@ pub struct DrawIndexedIndirectCommand { mod inner; mod outer; -mod pool; +pub mod pool; diff --git a/vulkano/src/command_buffer/outer.rs b/vulkano/src/command_buffer/outer.rs index c5ecd87e..437d26b4 100644 --- a/vulkano/src/command_buffer/outer.rs +++ b/vulkano/src/command_buffer/outer.rs @@ -14,14 +14,16 @@ use smallvec::SmallVec; use buffer::Buffer; use buffer::BufferSlice; use buffer::TypedBuffer; -use command_buffer::CommandBufferPool; use command_buffer::DrawIndirectCommand; use command_buffer::inner::InnerCommandBufferBuilder; use command_buffer::inner::InnerCommandBuffer; use command_buffer::inner::Submission; use command_buffer::inner::submit as inner_submit; +use command_buffer::pool::CommandPool; +use command_buffer::pool::StandardCommandPool; use descriptor::descriptor_set::DescriptorSetsCollection; use descriptor::PipelineLayout; +use device::Device; use device::Queue; use framebuffer::Framebuffer; use framebuffer::UnsafeRenderPass; @@ -33,6 +35,7 @@ use framebuffer::Subpass; use image::traits::Image; use image::traits::ImageClearValue; use image::traits::ImageContent; +use instance::QueueFamily; use pipeline::ComputePipeline; use pipeline::GraphicsPipeline; use pipeline::input_assembly::Index; @@ -55,31 +58,32 @@ use OomError; /// /// ``` /// -pub struct PrimaryCommandBufferBuilder { - inner: InnerCommandBufferBuilder, +pub struct PrimaryCommandBufferBuilder

> where P: CommandPool { + inner: InnerCommandBufferBuilder

, } -impl PrimaryCommandBufferBuilder { - /// See the docs of new(). - #[inline] - pub fn raw(pool: &Arc) - -> Result - { - let inner = try!(InnerCommandBufferBuilder::new::(pool, false, None, None)); - Ok(PrimaryCommandBufferBuilder { inner: inner }) - } - +impl PrimaryCommandBufferBuilder> { /// Builds a new primary command buffer and start recording commands in it. /// /// # Panic /// /// - Panicks if the device or host ran out of memory. + /// - Panicks if the device and queue family do not belong to the same physical device. /// #[inline] - pub fn new(pool: &Arc) - -> PrimaryCommandBufferBuilder + pub fn new(device: &Arc, queue_family: QueueFamily) + -> PrimaryCommandBufferBuilder> { - PrimaryCommandBufferBuilder::raw(pool).unwrap() + PrimaryCommandBufferBuilder::raw(Device::standard_command_pool(device, queue_family)).unwrap() + } +} + +impl

PrimaryCommandBufferBuilder

where P: CommandPool { + /// See the docs of new(). + #[inline] + pub fn raw(pool: P) -> Result, OomError> { + let inner = try!(InnerCommandBufferBuilder::new::(pool, false, None, None)); + Ok(PrimaryCommandBufferBuilder { inner: inner }) } /// Writes data to a buffer. @@ -97,7 +101,7 @@ impl PrimaryCommandBufferBuilder { /// - Panicks if the queue family doesn't support transfer operations. /// #[inline] - pub fn update_buffer<'a, B, T, Bb>(self, buffer: B, data: &T) -> PrimaryCommandBufferBuilder + pub fn update_buffer<'a, B, T, Bb>(self, buffer: B, data: &T) -> PrimaryCommandBufferBuilder

where B: Into>, Bb: Buffer + 'static, T: Clone + 'static + Send + Sync { unsafe { @@ -124,7 +128,7 @@ impl PrimaryCommandBufferBuilder { /// - Type safety is not enforced by the API. /// pub unsafe fn fill_buffer(self, buffer: &Arc, offset: usize, - size: usize, data: u32) -> PrimaryCommandBufferBuilder + size: usize, data: u32) -> PrimaryCommandBufferBuilder

where B: Buffer + 'static { PrimaryCommandBufferBuilder { @@ -133,7 +137,7 @@ impl PrimaryCommandBufferBuilder { } pub fn copy_buffer(self, source: &Arc, destination: &Arc) - -> PrimaryCommandBufferBuilder + -> PrimaryCommandBufferBuilder

where Bs: TypedBuffer + 'static, Bd: TypedBuffer + 'static { unsafe { @@ -143,11 +147,11 @@ impl PrimaryCommandBufferBuilder { } } - pub fn copy_buffer_to_color_image<'a, P, S, Img, Sb>(self, source: S, destination: &Arc, mip_level: u32, array_layers_range: Range, + pub fn copy_buffer_to_color_image<'a, Pi, S, Img, Sb>(self, source: S, destination: &Arc, mip_level: u32, array_layers_range: Range, offset: [u32; 3], extent: [u32; 3]) - -> PrimaryCommandBufferBuilder - where S: Into>, Sb: Buffer + 'static, - Img: ImageContent

+ 'static + -> PrimaryCommandBufferBuilder

+ where S: Into>, Sb: Buffer + 'static, + Img: ImageContent + 'static { unsafe { PrimaryCommandBufferBuilder { @@ -157,11 +161,11 @@ impl PrimaryCommandBufferBuilder { } } - pub fn copy_color_image_to_buffer<'a, P, S, Img, Sb>(self, dest: S, destination: &Arc, mip_level: u32, array_layers_range: Range, + pub fn copy_color_image_to_buffer<'a, Pi, S, Img, Sb>(self, dest: S, destination: &Arc, mip_level: u32, array_layers_range: Range, offset: [u32; 3], extent: [u32; 3]) - -> PrimaryCommandBufferBuilder - where S: Into>, Sb: Buffer + 'static, - Img: ImageContent

+ 'static + -> PrimaryCommandBufferBuilder

+ where S: Into>, Sb: Buffer + 'static, + Img: ImageContent + 'static { unsafe { PrimaryCommandBufferBuilder { @@ -175,7 +179,7 @@ impl PrimaryCommandBufferBuilder { source_array_layers: Range, src_coords: [Range; 3], destination: &Arc, dest_mip_level: u32, dest_array_layers: Range, dest_coords: [Range; 3]) - -> PrimaryCommandBufferBuilder + -> PrimaryCommandBufferBuilder

where Si: Image + 'static, Di: Image + 'static { unsafe { @@ -189,7 +193,7 @@ impl PrimaryCommandBufferBuilder { /// /// Note that compressed formats are not supported. pub fn clear_color_image<'a, I, V>(self, image: &Arc, color: V) - -> PrimaryCommandBufferBuilder + -> PrimaryCommandBufferBuilder

where I: ImageClearValue + 'static { unsafe { @@ -201,8 +205,10 @@ impl PrimaryCommandBufferBuilder { /// Executes secondary compute command buffers within this primary command buffer. #[inline] - pub fn execute_commands(self, cb: &Arc) - -> PrimaryCommandBufferBuilder + pub fn execute_commands(self, cb: &Arc>) + -> PrimaryCommandBufferBuilder

+ where S: CommandPool + 'static, + S::Finished: Send + Sync + 'static, { unsafe { PrimaryCommandBufferBuilder { @@ -214,7 +220,7 @@ impl PrimaryCommandBufferBuilder { /// Executes a compute pipeline. #[inline] pub fn dispatch(self, pipeline: &Arc>, sets: L, - dimensions: [u32; 3], push_constants: &Pc) -> PrimaryCommandBufferBuilder + dimensions: [u32; 3], push_constants: &Pc) -> PrimaryCommandBufferBuilder

where L: 'static + DescriptorSetsCollection + Send + Sync, Pl: 'static + PipelineLayout + Send + Sync, Pc: 'static + Clone + Send + Sync @@ -239,7 +245,7 @@ impl PrimaryCommandBufferBuilder { #[inline] pub fn draw_inline(self, renderpass: &Arc, framebuffer: &Arc>, clear_values: C) - -> PrimaryCommandBufferBuilderInlineDraw + -> PrimaryCommandBufferBuilderInlineDraw

where F: RenderPass + RenderPassDesc + RenderPassClearValues + 'static, R: RenderPass + RenderPassDesc + 'static { @@ -273,7 +279,7 @@ impl PrimaryCommandBufferBuilder { #[inline] pub fn draw_secondary(self, renderpass: &Arc, framebuffer: &Arc>, clear_values: C) - -> PrimaryCommandBufferBuilderSecondaryDraw + -> PrimaryCommandBufferBuilderSecondaryDraw

where F: RenderPass + RenderPassDesc + RenderPassClearValues + 'static, R: RenderPass + RenderPassDesc + 'static { @@ -295,7 +301,7 @@ impl PrimaryCommandBufferBuilder { /// See the docs of build(). #[inline] - pub fn build_raw(self) -> Result { + pub fn build_raw(self) -> Result, OomError> { let inner = try!(self.inner.build()); Ok(PrimaryCommandBuffer { inner: inner }) } @@ -307,24 +313,26 @@ impl PrimaryCommandBufferBuilder { /// - Panicks if the device or host ran out of memory. /// #[inline] - pub fn build(self) -> Arc { + pub fn build(self) -> Arc> { Arc::new(self.build_raw().unwrap()) } } /// Object that you obtain when calling `draw_inline` or `next_subpass_inline`. -pub struct PrimaryCommandBufferBuilderInlineDraw { - inner: InnerCommandBufferBuilder, +pub struct PrimaryCommandBufferBuilderInlineDraw

> + where P: CommandPool +{ + inner: InnerCommandBufferBuilder

, current_subpass: u32, num_subpasses: u32, } -impl PrimaryCommandBufferBuilderInlineDraw { +impl

PrimaryCommandBufferBuilderInlineDraw

where P: CommandPool { /// Calls `vkCmdDraw`. // FIXME: push constants pub fn draw(self, pipeline: &Arc>, vertices: V, dynamic: &DynamicState, sets: L, push_constants: &Pc) - -> PrimaryCommandBufferBuilderInlineDraw + -> PrimaryCommandBufferBuilderInlineDraw

where Pv: VertexSource + 'static, Pl: PipelineLayout + 'static + Send + Sync, Rp: 'static + Send + Sync, L: DescriptorSetsCollection + Send + Sync, Pc: 'static + Clone + Send + Sync { @@ -342,7 +350,7 @@ impl PrimaryCommandBufferBuilderInlineDraw { /// Calls `vkCmdDrawIndexed`. 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, push_constants: &Pc) -> PrimaryCommandBufferBuilderInlineDraw + sets: L, push_constants: &Pc) -> PrimaryCommandBufferBuilderInlineDraw

where Pv: 'static + 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, Pc: 'static + Clone + Send + Sync @@ -367,7 +375,7 @@ impl PrimaryCommandBufferBuilderInlineDraw { /// - Panicks if no more subpasses remain. /// #[inline] - pub fn next_subpass_inline(self) -> PrimaryCommandBufferBuilderInlineDraw { + pub fn next_subpass_inline(self) -> PrimaryCommandBufferBuilderInlineDraw

{ assert!(self.current_subpass + 1 < self.num_subpasses); unsafe { @@ -390,7 +398,7 @@ impl PrimaryCommandBufferBuilderInlineDraw { /// - Panicks if no more subpasses remain. /// #[inline] - pub fn next_subpass_secondary(self) -> PrimaryCommandBufferBuilderSecondaryDraw { + pub fn next_subpass_secondary(self) -> PrimaryCommandBufferBuilderSecondaryDraw

{ assert!(self.current_subpass + 1 < self.num_subpasses); unsafe { @@ -411,7 +419,7 @@ impl PrimaryCommandBufferBuilderInlineDraw { /// - Panicks if not at the last subpass. /// #[inline] - pub fn draw_end(self) -> PrimaryCommandBufferBuilder { + pub fn draw_end(self) -> PrimaryCommandBufferBuilder

{ assert!(self.current_subpass + 1 == self.num_subpasses); unsafe { @@ -424,13 +432,15 @@ impl PrimaryCommandBufferBuilderInlineDraw { } /// Object that you obtain when calling `draw_secondary` or `next_subpass_secondary`. -pub struct PrimaryCommandBufferBuilderSecondaryDraw { - inner: InnerCommandBufferBuilder, +pub struct PrimaryCommandBufferBuilderSecondaryDraw

> + where P: CommandPool +{ + inner: InnerCommandBufferBuilder

, current_subpass: u32, num_subpasses: u32, } -impl PrimaryCommandBufferBuilderSecondaryDraw { +impl

PrimaryCommandBufferBuilderSecondaryDraw

where P: CommandPool { /// Switches to the next subpass of the current renderpass. /// /// This function is similar to `draw_inline` on the builder. @@ -440,7 +450,7 @@ impl PrimaryCommandBufferBuilderSecondaryDraw { /// - Panicks if no more subpasses remain. /// #[inline] - pub fn next_subpass_inline(self) -> PrimaryCommandBufferBuilderInlineDraw { + pub fn next_subpass_inline(self) -> PrimaryCommandBufferBuilderInlineDraw

{ assert!(self.current_subpass + 1 < self.num_subpasses); unsafe { @@ -463,7 +473,7 @@ impl PrimaryCommandBufferBuilderSecondaryDraw { /// - Panicks if no more subpasses remain. /// #[inline] - pub fn next_subpass_secondary(self) -> PrimaryCommandBufferBuilderSecondaryDraw { + pub fn next_subpass_secondary(self) -> PrimaryCommandBufferBuilderSecondaryDraw

{ assert!(self.current_subpass + 1 < self.num_subpasses); unsafe { @@ -484,9 +494,11 @@ impl PrimaryCommandBufferBuilderSecondaryDraw { /// - Panicks if the secondary command buffers wasn't created with a compatible /// renderpass or is using the wrong subpass. #[inline] - pub fn execute_commands(mut self, cb: &Arc>) - -> PrimaryCommandBufferBuilderSecondaryDraw - where R: 'static + Send + Sync + pub fn execute_commands(mut self, cb: &Arc>) + -> PrimaryCommandBufferBuilderSecondaryDraw

+ where R: 'static + Send + Sync, + Ps: CommandPool + 'static, + Ps::Finished: Send + Sync + 'static, { // FIXME: check renderpass, subpass and framebuffer @@ -503,7 +515,7 @@ impl PrimaryCommandBufferBuilderSecondaryDraw { /// - Panicks if not at the last subpass. /// #[inline] - pub fn draw_end(self) -> PrimaryCommandBufferBuilder { + pub fn draw_end(self) -> PrimaryCommandBufferBuilder

{ assert!(self.current_subpass + 1 == self.num_subpasses); unsafe { @@ -518,8 +530,8 @@ impl PrimaryCommandBufferBuilderSecondaryDraw { /// Represents a collection of commands to be executed by the GPU. /// /// A primary command buffer can contain any command. -pub struct PrimaryCommandBuffer { - inner: InnerCommandBuffer, +pub struct PrimaryCommandBuffer

> where P: CommandPool { + inner: InnerCommandBuffer

, } /// Submits the command buffer to a queue so that it is executed. @@ -532,28 +544,55 @@ pub struct PrimaryCommandBuffer { /// - Panicks if the queue doesn't belong to the family the pool was created with. /// #[inline] -pub fn submit(cmd: &Arc, queue: &Arc) - -> Result, OomError> +pub fn submit

(cmd: &Arc>, queue: &Arc) + -> Result, OomError> + where P: CommandPool + 'static, + P::Finished: Send + Sync + 'static { // TODO: wrong error type inner_submit(&cmd.inner, cmd.clone() as Arc<_>, queue) } /// A prototype of a secondary compute command buffer. -pub struct SecondaryGraphicsCommandBufferBuilder { - inner: InnerCommandBufferBuilder, +pub struct SecondaryGraphicsCommandBufferBuilder> + where P: CommandPool +{ + inner: InnerCommandBufferBuilder

, render_pass: Arc, render_pass_subpass: u32, framebuffer: Option>>, } -impl SecondaryGraphicsCommandBufferBuilder +impl SecondaryGraphicsCommandBufferBuilder> where R: RenderPass + RenderPassDesc + 'static +{ + /// Builds a new secondary command buffer and start recording commands in it. + /// + /// The `framebuffer` parameter is optional and can be used as an optimisation. + /// + /// # Panic + /// + /// - Panicks if the device or host ran out of memory. + /// - Panicks if the device and queue family do not belong to the same physical device. + /// + #[inline] + pub fn new(device: &Arc, queue_family: QueueFamily, subpass: Subpass, + framebuffer: Option<&Arc>>) + -> SecondaryGraphicsCommandBufferBuilder> + where R: 'static + Send + Sync + { + SecondaryGraphicsCommandBufferBuilder::raw(Device::standard_command_pool(device, + queue_family), subpass, framebuffer).unwrap() + } +} + +impl SecondaryGraphicsCommandBufferBuilder + where R: RenderPass + RenderPassDesc + 'static, + P: CommandPool { /// See the docs of new(). #[inline] - pub fn raw(pool: &Arc, subpass: Subpass, - framebuffer: Option<&Arc>>) - -> Result, OomError> + pub fn raw(pool: P, subpass: Subpass, framebuffer: Option<&Arc>>) + -> Result, OomError> where R: 'static + Send + Sync { let inner = try!(InnerCommandBufferBuilder::new(pool, true, Some(subpass), framebuffer.clone())); @@ -565,28 +604,11 @@ impl SecondaryGraphicsCommandBufferBuilder }) } - /// Builds a new secondary command buffer and start recording commands in it. - /// - /// The `framebuffer` parameter is optional and can be used as an optimisation. - /// - /// # Panic - /// - /// - Panicks if the device or host ran out of memory. - /// - #[inline] - pub fn new(pool: &Arc, subpass: Subpass, - framebuffer: Option<&Arc>>) - -> SecondaryGraphicsCommandBufferBuilder - where R: 'static + Send + Sync - { - SecondaryGraphicsCommandBufferBuilder::raw(pool, subpass, framebuffer).unwrap() - } - /// Calls `vkCmdDraw`. // FIXME: push constants pub fn draw(self, pipeline: &Arc>, vertices: V, dynamic: &DynamicState, sets: L, push_constants: &Pc) - -> SecondaryGraphicsCommandBufferBuilder + -> SecondaryGraphicsCommandBufferBuilder where Pv: VertexSource + 'static, Pl: PipelineLayout + 'static + Send + Sync, Rp: RenderPass + RenderPassDesc + 'static + Send + Sync, L: DescriptorSetsCollection + Send + Sync, R: RenderPassCompatible, Pc: 'static + Clone + Send + Sync @@ -607,7 +629,7 @@ impl SecondaryGraphicsCommandBufferBuilder /// Calls `vkCmdDrawIndexed`. 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, push_constants: &Pc) -> SecondaryGraphicsCommandBufferBuilder + sets: L, push_constants: &Pc) -> SecondaryGraphicsCommandBufferBuilder where Pv: 'static + VertexSource, Pl: 'static + PipelineLayout + Send + Sync, Rp: RenderPass + RenderPassDesc + 'static + Send + Sync, Ib: Into>, I: 'static + Index, Ibb: Buffer + 'static, @@ -629,7 +651,7 @@ impl SecondaryGraphicsCommandBufferBuilder /// Calls `vkCmdDrawIndirect`. pub fn draw_indirect(self, buffer: &Arc, pipeline: &Arc>, vertices: V, dynamic: &DynamicState, - sets: L, push_constants: &Pc) -> SecondaryGraphicsCommandBufferBuilder + sets: L, push_constants: &Pc) -> SecondaryGraphicsCommandBufferBuilder where Pv: 'static + VertexSource, L: DescriptorSetsCollection + Send + Sync, Pl: 'static + PipelineLayout + Send + Sync, Rp: RenderPass + RenderPassDesc + 'static + Send + Sync, Pc: 'static + Clone + Send + Sync, @@ -650,7 +672,7 @@ impl SecondaryGraphicsCommandBufferBuilder /// See the docs of build(). #[inline] - pub fn build_raw(self) -> Result, OomError> { + pub fn build_raw(self) -> Result, OomError> { let inner = try!(self.inner.build()); Ok(SecondaryGraphicsCommandBuffer { @@ -667,7 +689,7 @@ impl SecondaryGraphicsCommandBufferBuilder /// - Panicks if the device or host ran out of memory. /// #[inline] - pub fn build(self) -> Arc> { + pub fn build(self) -> Arc> { Arc::new(self.build_raw().unwrap()) } } @@ -679,38 +701,40 @@ impl SecondaryGraphicsCommandBufferBuilder /// a primary command buffer, specify a framebuffer, and then call the secondary command buffer. /// /// A secondary graphics command buffer can't be called outside of a renderpass. -pub struct SecondaryGraphicsCommandBuffer { - inner: InnerCommandBuffer, +pub struct SecondaryGraphicsCommandBuffer> where P: CommandPool { + inner: InnerCommandBuffer

, render_pass: Arc, render_pass_subpass: u32, } /// A prototype of a secondary compute command buffer. -pub struct SecondaryComputeCommandBufferBuilder { - inner: InnerCommandBufferBuilder, +pub struct SecondaryComputeCommandBufferBuilder

> where P: CommandPool { + inner: InnerCommandBufferBuilder

, } -impl SecondaryComputeCommandBufferBuilder { - /// See the docs of new(). - #[inline] - pub fn raw(pool: &Arc) - -> Result - { - let inner = try!(InnerCommandBufferBuilder::new::(pool, true, None, None)); - Ok(SecondaryComputeCommandBufferBuilder { inner: inner }) - } - +impl SecondaryComputeCommandBufferBuilder> { /// Builds a new secondary command buffer and start recording commands in it. /// /// # Panic /// /// - Panicks if the device or host ran out of memory. + /// - Panicks if the device and queue family do not belong to the same physical device. /// #[inline] - pub fn new(pool: &Arc) - -> SecondaryComputeCommandBufferBuilder + pub fn new(device: &Arc, queue_family: QueueFamily) + -> SecondaryComputeCommandBufferBuilder> { - SecondaryComputeCommandBufferBuilder::raw(pool).unwrap() + SecondaryComputeCommandBufferBuilder::raw(Device::standard_command_pool(device, + queue_family)).unwrap() + } +} + +impl

SecondaryComputeCommandBufferBuilder

where P: CommandPool { + /// See the docs of new(). + #[inline] + pub fn raw(pool: P) -> Result, OomError> { + let inner = try!(InnerCommandBufferBuilder::new::(pool, true, None, None)); + Ok(SecondaryComputeCommandBufferBuilder { inner: inner }) } /// Writes data to a buffer. @@ -728,7 +752,7 @@ impl SecondaryComputeCommandBufferBuilder { /// - Panicks if the queue family doesn't support transfer operations. /// #[inline] - pub fn update_buffer<'a, B, T, Bb>(self, buffer: B, data: &T) -> SecondaryComputeCommandBufferBuilder + pub fn update_buffer<'a, B, T, Bb>(self, buffer: B, data: &T) -> SecondaryComputeCommandBufferBuilder

where B: Into>, Bb: Buffer + 'static, T: Clone + 'static + Send + Sync { unsafe { @@ -754,7 +778,7 @@ impl SecondaryComputeCommandBufferBuilder { /// /// - Type safety is not enforced by the API. pub unsafe fn fill_buffer(self, buffer: &Arc, offset: usize, size: usize, data: u32) - -> SecondaryComputeCommandBufferBuilder + -> SecondaryComputeCommandBufferBuilder

where B: Buffer + 'static { SecondaryComputeCommandBufferBuilder { @@ -764,7 +788,7 @@ impl SecondaryComputeCommandBufferBuilder { /// See the docs of build(). #[inline] - pub fn build_raw(self) -> Result { + pub fn build_raw(self) -> Result, OomError> { let inner = try!(self.inner.build()); Ok(SecondaryComputeCommandBuffer { inner: inner }) } @@ -776,7 +800,7 @@ impl SecondaryComputeCommandBufferBuilder { /// - Panicks if the device or host ran out of memory. /// #[inline] - pub fn build(self) -> Arc { + pub fn build(self) -> Arc> { Arc::new(self.build_raw().unwrap()) } } @@ -785,8 +809,10 @@ impl SecondaryComputeCommandBufferBuilder { /// /// A secondary compute command buffer contains non-draw commands (like copy commands, compute /// shader execution, etc.). It can only be called outside of a renderpass. -pub struct SecondaryComputeCommandBuffer { - inner: InnerCommandBuffer, +pub struct SecondaryComputeCommandBuffer

> + where P: CommandPool +{ + inner: InnerCommandBuffer

, } /// The dynamic state to use for a draw command. diff --git a/vulkano/src/command_buffer/pool.rs b/vulkano/src/command_buffer/pool.rs index 3eb65ae1..10a83bee 100644 --- a/vulkano/src/command_buffer/pool.rs +++ b/vulkano/src/command_buffer/pool.rs @@ -7,45 +7,336 @@ // notice may not be copied, modified, or distributed except // according to those terms. +//! In the Vulkan API, command buffers must be allocated from *command pools*. +//! +//! A command pool holds and manages the memory of one or more command buffers. If you destroy a +//! command pool, all of its command buffers are automatically destroyed. +//! +//! In vulkano, creating a command buffer requires passing an implementation of the `CommandPool` +//! trait. By default vulkano will use the `StandardCommandPool` struct, but you can implement +//! this trait yourself by wrapping around the `UnsafeCommandPool` type. + +use std::cmp; +use std::collections::HashMap; +use std::iter::Chain; +use std::marker::PhantomData; use std::mem; use std::ptr; use std::sync::Arc; use std::sync::Mutex; -use std::sync::MutexGuard; +use std::vec::IntoIter as VecIntoIter; +use smallvec::SmallVec; use instance::QueueFamily; use device::Device; use OomError; -use SynchronizedVulkanObject; use VulkanObject; use VulkanPointers; use check_errors; use vk; -/// A pool from which command buffers are created from. -pub struct CommandBufferPool { - pool: Mutex, - device: Arc, - queue_family_index: u32, +/// Types that manage the memory of command buffers. +pub unsafe trait CommandPool { + /// See `alloc()`. + type Iter: Iterator; + /// See `lock()`. + type Lock; + /// See `finish()`. + type Finished: CommandPoolFinished; + + /// Allocates command buffers from this pool. + fn alloc(&self, secondary: bool, count: u32) -> Result; + + /// Frees command buffers from this pool. + /// + /// # Safety + /// + /// - The command buffers must have been allocated from this pool. + /// - `secondary` must have the same value as what was passed to `alloc`. + /// + unsafe fn free(&self, secondary: bool, command_buffers: I) + where I: Iterator; + + /// Once a command buffer has finished being built, it should call this method in order to + /// produce a `Finished` object. + /// + /// The `Finished` object must hold the pool alive. + /// + /// The point of this object is to change the Send/Sync strategy after a command buffer has + /// finished being built compared to before. + fn finish(self) -> Self::Finished; + + /// Before any command buffer allocated from this pool can be modified, the pool itself must + /// be locked by calling this method. + /// + /// All the operations are atomic at the thread level, so the point of this lock is to + /// prevent the pool from being accessed from multiple threads in parallel. + fn lock(&self) -> Self::Lock; + + /// Returns true if command buffers can be reset individually. In other words, if the pool + /// was created with `reset_cb` set to true. + fn can_reset_invidual_command_buffers(&self) -> bool; + + /// Returns the device used to create this pool. + fn device(&self) -> &Arc; + + /// Returns the queue family that this pool targets. + fn queue_family(&self) -> QueueFamily; } -impl CommandBufferPool { - /// See the docs of new(). +/// See `CommandPool::finish()`. +pub unsafe trait CommandPoolFinished { + /// Frees command buffers. + /// + /// # Safety + /// + /// - The command buffers must have been allocated from this pool. + /// - `secondary` must have the same value as what was passed to `alloc`. + /// + unsafe fn free(&self, secondary: bool, command_buffers: I) + where I: Iterator; + + /// Returns the device used to create this pool. + fn device(&self) -> &Arc; + + /// Returns the queue family that this pool targets. + fn queue_family(&self) -> QueueFamily; +} + +/// Opaque type that represents a command buffer allocated from a pool. +pub struct AllocatedCommandBuffer(vk::CommandBuffer); + +impl From for AllocatedCommandBuffer { #[inline] - pub fn raw(device: &Arc, queue_family: &QueueFamily) - -> Result + fn from(cmd: vk::CommandBuffer) -> AllocatedCommandBuffer { + AllocatedCommandBuffer(cmd) + } +} + +unsafe impl VulkanObject for AllocatedCommandBuffer { + type Object = vk::CommandBuffer; + + #[inline] + fn internal_object(&self) -> vk::CommandBuffer { + self.0 + } +} + +// Since the stdlib doesn't have a "thread ID" yet, we store a `Box` for each thread and the +// value of the pointer will be used as a thread id. +thread_local!(static THREAD_ID: Box = Box::new(0)); +#[inline] +fn curr_thread_id() -> usize { THREAD_ID.with(|data| &**data as *const u8 as usize) } + +/// Standard implementation of a command pool. +/// +/// Will use one pool per thread in order to avoid locking. Will try to reuse command buffers. +/// Locking is required only when allocating/freeing command buffers. +pub struct StandardCommandPool { + device: Arc, + queue_family: u32, + per_thread: Mutex>, + + // Dummy marker in order to not implement `Send` and `Sync`. + // + // Since `StandardCommandPool` isn't Send/Sync, then the command buffers that use this pool + // won't be Send/Sync either, which means that we don't need to lock the pool while the CB + // is being built. + // + // However `StandardCommandPoolFinished` *is* Send/Sync because the only operation that can + // be called on `StandardCommandPoolFinished` is freeing, and freeing does actually lock. + dummy_avoid_send_sync: PhantomData<*const u8>, +} + +impl StandardCommandPool { + /// Builds a new pool. + /// + /// # Panic + /// + /// - Panicks if the device and the queue family don't belong to the same physical device. + /// + pub fn new(device: &Arc, queue_family: QueueFamily) -> StandardCommandPool { + assert_eq!(device.physical_device().internal_object(), + queue_family.physical_device().internal_object()); + + StandardCommandPool { + device: device.clone(), + queue_family: queue_family.id(), + per_thread: Mutex::new(HashMap::new()), + dummy_avoid_send_sync: PhantomData, + } + } +} + +struct StandardCommandPoolPerThread { + pool: UnsafeCommandPool, + available_primary_command_buffers: Vec, + available_secondary_command_buffers: Vec, +} + +unsafe impl CommandPool for Arc { + type Iter = Chain, UnsafeCommandPoolAllocIter>; + type Lock = (); + type Finished = StandardCommandPoolFinished; + + fn alloc(&self, secondary: bool, count: u32) -> Result { + let mut per_thread = self.per_thread.lock().unwrap(); + let mut per_thread = per_thread.entry(curr_thread_id()) + .or_insert_with(|| { + StandardCommandPoolPerThread { + pool: UnsafeCommandPool::new(&self.device, self.queue_family(), false, true).unwrap(), // FIXME: return error instead + available_primary_command_buffers: Vec::new(), + available_secondary_command_buffers: Vec::new(), + } + }); + + let mut existing = if secondary { &mut per_thread.available_secondary_command_buffers } + else { &mut per_thread.available_primary_command_buffers }; + + let num_from_existing = cmp::min(count as usize, existing.len()); + let from_existing = existing.drain(0 .. num_from_existing).collect::>().into_iter(); + + let num_new = count as usize - num_from_existing; + debug_assert!(num_new <= count as usize); // Check overflows. + let newly_allocated = try!(per_thread.pool.alloc_command_buffers(secondary, num_new)); + + Ok(from_existing.chain(newly_allocated)) + } + + unsafe fn free(&self, secondary: bool, command_buffers: I) + where I: Iterator + { + let mut per_thread = self.per_thread.lock().unwrap(); + let mut per_thread = per_thread.get_mut(&curr_thread_id()).unwrap(); + + if secondary { + for cb in command_buffers { + per_thread.available_secondary_command_buffers.push(cb); + } + } else { + for cb in command_buffers { + per_thread.available_primary_command_buffers.push(cb); + } + } + } + + #[inline] + fn finish(self) -> Self::Finished { + StandardCommandPoolFinished { + pool: self, + thread_id: curr_thread_id(), + } + } + + #[inline] + fn lock(&self) -> Self::Lock { + () + } + + #[inline] + fn can_reset_invidual_command_buffers(&self) -> bool { + true + } + + #[inline] + fn device(&self) -> &Arc { + &self.device + } + + #[inline] + fn queue_family(&self) -> QueueFamily { + self.device.physical_device().queue_family_by_id(self.queue_family).unwrap() + } +} + +pub struct StandardCommandPoolFinished { + pool: Arc, + thread_id: usize, +} + +unsafe impl CommandPoolFinished for StandardCommandPoolFinished { + unsafe fn free(&self, secondary: bool, command_buffers: I) + where I: Iterator + { + let mut per_thread = self.pool.per_thread.lock().unwrap(); + let mut per_thread = per_thread.get_mut(&curr_thread_id()).unwrap(); + + if secondary { + for cb in command_buffers { + per_thread.available_secondary_command_buffers.push(cb); + } + } else { + for cb in command_buffers { + per_thread.available_primary_command_buffers.push(cb); + } + } + } + + #[inline] + fn device(&self) -> &Arc { + self.pool.device() + } + + #[inline] + fn queue_family(&self) -> QueueFamily { + self.pool.queue_family() + } +} + +// See `StandardCommandPool` for comments about this. +unsafe impl Send for StandardCommandPoolFinished {} +unsafe impl Sync for StandardCommandPoolFinished {} + +/// Low-level implementation of a command pool. +pub struct UnsafeCommandPool { + pool: vk::CommandPool, + device: Arc, + queue_family_index: u32, + + // We don't want `UnsafeCommandPool` to implement Sync, since the Vulkan command pool isn't + // thread safe. + // + // This marker unimplements both Send and Sync, but we reimplement Send manually right under. + dummy_avoid_sync: PhantomData<*const u8>, +} + +unsafe impl Send for UnsafeCommandPool {} + +impl UnsafeCommandPool { + /// Creates a new pool. + /// + /// The command buffers created with this pool can only be executed on queues of the given + /// family. + /// + /// Setting `transient` to true is a hint to the implementation that the command buffers will + /// be short-lived. + /// Setting `reset_cb` to true means that command buffers can be reset individually. + /// + /// # Panic + /// + /// - Panicks if the queue family doesn't belong to the same physical device as `device`. + /// + pub fn new(device: &Arc, queue_family: QueueFamily, transient: bool, + reset_cb: bool) -> Result { assert_eq!(device.physical_device().internal_object(), queue_family.physical_device().internal_object()); let vk = device.pointers(); + let flags = { + let flag1 = if transient { vk::COMMAND_POOL_CREATE_TRANSIENT_BIT } else { 0 }; + let flag2 = if reset_cb { vk::COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT } + else { 0 }; + flag1 | flag2 + }; + let pool = unsafe { let infos = vk::CommandPoolCreateInfo { sType: vk::STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, pNext: ptr::null(), - flags: 0, // TODO: + flags: flags, queueFamilyIndex: queue_family.id(), }; @@ -55,28 +346,75 @@ impl CommandBufferPool { output }; - Ok(CommandBufferPool { - pool: Mutex::new(pool), + Ok(UnsafeCommandPool { + pool: pool, device: device.clone(), queue_family_index: queue_family.id(), + dummy_avoid_sync: PhantomData, }) } - - /// Creates a new pool. + + /// Resets the pool, which resets all the command buffers that were allocated from it. /// - /// The command buffers created with this pool can only be executed on queues of the given - /// family. + /// # Safety /// - /// # Panic - /// - /// - Panicks if the queue family doesn't belong to the same physical device as `device`. - /// - Panicks if the device or host ran out of memory. + /// The command buffers allocated from this pool jump to the initial state. /// #[inline] - pub fn new(device: &Arc, queue_family: &QueueFamily) - -> Arc + pub unsafe fn reset(&self, release_resources: bool) -> Result<(), OomError> { + let flags = if release_resources { vk::COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT } + else { 0 }; + + let vk = self.device.pointers(); + try!(check_errors(vk.ResetCommandPool(self.device.internal_object(), self.pool, flags))); + Ok(()) + } + + /// Allocates `count` command buffers. + /// + /// If `secondary` is true, allocates secondary command buffers. Otherwise, allocates primary + /// command buffers. + pub fn alloc_command_buffers(&self, secondary: bool, count: usize) + -> Result { - Arc::new(CommandBufferPool::raw(device, queue_family).unwrap()) + if count == 0 { + return Ok(UnsafeCommandPoolAllocIter(None)); + } + + let infos = vk::CommandBufferAllocateInfo { + sType: vk::STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + pNext: ptr::null(), + commandPool: self.pool, + level: if secondary { vk::COMMAND_BUFFER_LEVEL_SECONDARY } + else { vk::COMMAND_BUFFER_LEVEL_PRIMARY }, + commandBufferCount: count as u32, + }; + + unsafe { + let vk = self.device.pointers(); + let mut out = Vec::with_capacity(count); + try!(check_errors(vk.AllocateCommandBuffers(self.device.internal_object(), &infos, + out.as_mut_ptr()))); + + out.set_len(count); + + Ok(UnsafeCommandPoolAllocIter(Some(out.into_iter()))) + } + } + + /// Frees individual command buffers. + /// + /// # Safety + /// + /// The command buffers must have been allocated from this pool. + /// + pub unsafe fn free_command_buffers(&self, command_buffers: I) + where I: Iterator + { + let command_buffers: SmallVec<[_; 4]> = command_buffers.map(|cb| cb.0).collect(); + let vk = self.device.pointers(); + vk.FreeCommandBuffers(self.device.internal_object(), self.pool, + command_buffers.len() as u32, command_buffers.as_ptr()) } /// Returns the device this command pool was created with. @@ -92,22 +430,40 @@ impl CommandBufferPool { } } -unsafe impl SynchronizedVulkanObject for CommandBufferPool { +unsafe impl VulkanObject for UnsafeCommandPool { type Object = vk::CommandPool; #[inline] - fn internal_object_guard(&self) -> MutexGuard { - self.pool.lock().unwrap() + fn internal_object(&self) -> vk::CommandPool { + self.pool } } -impl Drop for CommandBufferPool { +impl Drop for UnsafeCommandPool { #[inline] fn drop(&mut self) { unsafe { let vk = self.device.pointers(); - let pool = self.pool.lock().unwrap(); - vk.DestroyCommandPool(self.device.internal_object(), *pool, ptr::null()); + vk.DestroyCommandPool(self.device.internal_object(), self.pool, ptr::null()); } } } + +/// Iterator for newly-allocated command buffers. +pub struct UnsafeCommandPoolAllocIter(Option>); + +impl Iterator for UnsafeCommandPoolAllocIter { + type Item = AllocatedCommandBuffer; + + #[inline] + fn next(&mut self) -> Option { + self.0.as_mut().and_then(|i| i.next()).map(|cb| AllocatedCommandBuffer(cb)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.as_ref().map(|i| i.size_hint()).unwrap_or((0, Some(0))) + } +} + +impl ExactSizeIterator for UnsafeCommandPoolAllocIter {} diff --git a/vulkano/src/device.rs b/vulkano/src/device.rs index 0b1daf48..39a86422 100644 --- a/vulkano/src/device.rs +++ b/vulkano/src/device.rs @@ -12,6 +12,8 @@ //! The `Device` is one of the most important objects of Vulkan. Creating a `Device` is required //! before you can create buffers, textures, shaders, etc. //! +use std::collections::HashMap; +use std::collections::hash_map::Entry; use std::fmt; use std::error; use std::mem; @@ -22,6 +24,7 @@ use std::sync::MutexGuard; use std::sync::Weak; use smallvec::SmallVec; +use command_buffer::pool::StandardCommandPool; use instance::Features; use instance::Instance; use instance::PhysicalDevice; @@ -46,10 +49,16 @@ pub struct Device { device: vk::Device, vk: vk::DevicePointers, standard_pool: Mutex>>, // TODO: use Weak::new() instead + standard_command_pools: Mutex>>, // TODO: use a better hasher features: Features, extensions: DeviceExtensions, } +// The `StandardCommandPool` type doesn't implement Send/Sync, so we have to manually reimplement +// them for the device itself. +unsafe impl Send for Device {} +unsafe impl Sync for Device {} + impl Device { /// Builds a new Vulkan device for the given physical device. /// @@ -189,6 +198,7 @@ impl Device { device: device, vk: vk, standard_pool: Mutex::new(None), + standard_command_pools: Mutex::new(HashMap::new()), features: requested_features.clone(), extensions: extensions.clone(), }); @@ -265,6 +275,34 @@ impl Device { *pool = Some(Arc::downgrade(&new_pool)); new_pool } + + /// Returns the standard command buffer pool used by default if you don't provide any other + /// pool. + /// + /// # Panic + /// + /// - Panicks if the device and the queue family don't belong to the same physical device. + /// + pub fn standard_command_pool(me: &Arc, queue: QueueFamily) -> Arc { + let mut standard_command_pools = me.standard_command_pools.lock().unwrap(); + + match standard_command_pools.entry(queue.id()) { + Entry::Occupied(mut entry) => { + if let Some(pool) = entry.get().upgrade() { + return pool; + } + + let new_pool = Arc::new(StandardCommandPool::new(me, queue)); + *entry.get_mut() = Arc::downgrade(&new_pool); + new_pool + }, + Entry::Vacant(entry) => { + let new_pool = Arc::new(StandardCommandPool::new(me, queue)); + entry.insert(Arc::downgrade(&new_pool)); + new_pool + } + } + } } impl fmt::Debug for Device { From 58f77753ad44cee7e82059578bfac73e95ccaebc Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Sat, 4 Jun 2016 13:19:03 +0200 Subject: [PATCH 2/3] Split pool module in three submodules --- vulkano/src/command_buffer/pool.rs | 469 -------------------- vulkano/src/command_buffer/pool/mod.rs | 121 +++++ vulkano/src/command_buffer/pool/standard.rs | 192 ++++++++ vulkano/src/command_buffer/pool/sys.rs | 205 +++++++++ 4 files changed, 518 insertions(+), 469 deletions(-) delete mode 100644 vulkano/src/command_buffer/pool.rs create mode 100644 vulkano/src/command_buffer/pool/mod.rs create mode 100644 vulkano/src/command_buffer/pool/standard.rs create mode 100644 vulkano/src/command_buffer/pool/sys.rs diff --git a/vulkano/src/command_buffer/pool.rs b/vulkano/src/command_buffer/pool.rs deleted file mode 100644 index 10a83bee..00000000 --- a/vulkano/src/command_buffer/pool.rs +++ /dev/null @@ -1,469 +0,0 @@ -// Copyright (c) 2016 The vulkano developers -// Licensed under the Apache License, Version 2.0 -// or the MIT -// license , -// at your option. All files in the project carrying such -// notice may not be copied, modified, or distributed except -// according to those terms. - -//! In the Vulkan API, command buffers must be allocated from *command pools*. -//! -//! A command pool holds and manages the memory of one or more command buffers. If you destroy a -//! command pool, all of its command buffers are automatically destroyed. -//! -//! In vulkano, creating a command buffer requires passing an implementation of the `CommandPool` -//! trait. By default vulkano will use the `StandardCommandPool` struct, but you can implement -//! this trait yourself by wrapping around the `UnsafeCommandPool` type. - -use std::cmp; -use std::collections::HashMap; -use std::iter::Chain; -use std::marker::PhantomData; -use std::mem; -use std::ptr; -use std::sync::Arc; -use std::sync::Mutex; -use std::vec::IntoIter as VecIntoIter; -use smallvec::SmallVec; - -use instance::QueueFamily; - -use device::Device; -use OomError; -use VulkanObject; -use VulkanPointers; -use check_errors; -use vk; - -/// Types that manage the memory of command buffers. -pub unsafe trait CommandPool { - /// See `alloc()`. - type Iter: Iterator; - /// See `lock()`. - type Lock; - /// See `finish()`. - type Finished: CommandPoolFinished; - - /// Allocates command buffers from this pool. - fn alloc(&self, secondary: bool, count: u32) -> Result; - - /// Frees command buffers from this pool. - /// - /// # Safety - /// - /// - The command buffers must have been allocated from this pool. - /// - `secondary` must have the same value as what was passed to `alloc`. - /// - unsafe fn free(&self, secondary: bool, command_buffers: I) - where I: Iterator; - - /// Once a command buffer has finished being built, it should call this method in order to - /// produce a `Finished` object. - /// - /// The `Finished` object must hold the pool alive. - /// - /// The point of this object is to change the Send/Sync strategy after a command buffer has - /// finished being built compared to before. - fn finish(self) -> Self::Finished; - - /// Before any command buffer allocated from this pool can be modified, the pool itself must - /// be locked by calling this method. - /// - /// All the operations are atomic at the thread level, so the point of this lock is to - /// prevent the pool from being accessed from multiple threads in parallel. - fn lock(&self) -> Self::Lock; - - /// Returns true if command buffers can be reset individually. In other words, if the pool - /// was created with `reset_cb` set to true. - fn can_reset_invidual_command_buffers(&self) -> bool; - - /// Returns the device used to create this pool. - fn device(&self) -> &Arc; - - /// Returns the queue family that this pool targets. - fn queue_family(&self) -> QueueFamily; -} - -/// See `CommandPool::finish()`. -pub unsafe trait CommandPoolFinished { - /// Frees command buffers. - /// - /// # Safety - /// - /// - The command buffers must have been allocated from this pool. - /// - `secondary` must have the same value as what was passed to `alloc`. - /// - unsafe fn free(&self, secondary: bool, command_buffers: I) - where I: Iterator; - - /// Returns the device used to create this pool. - fn device(&self) -> &Arc; - - /// Returns the queue family that this pool targets. - fn queue_family(&self) -> QueueFamily; -} - -/// Opaque type that represents a command buffer allocated from a pool. -pub struct AllocatedCommandBuffer(vk::CommandBuffer); - -impl From for AllocatedCommandBuffer { - #[inline] - fn from(cmd: vk::CommandBuffer) -> AllocatedCommandBuffer { - AllocatedCommandBuffer(cmd) - } -} - -unsafe impl VulkanObject for AllocatedCommandBuffer { - type Object = vk::CommandBuffer; - - #[inline] - fn internal_object(&self) -> vk::CommandBuffer { - self.0 - } -} - -// Since the stdlib doesn't have a "thread ID" yet, we store a `Box` for each thread and the -// value of the pointer will be used as a thread id. -thread_local!(static THREAD_ID: Box = Box::new(0)); -#[inline] -fn curr_thread_id() -> usize { THREAD_ID.with(|data| &**data as *const u8 as usize) } - -/// Standard implementation of a command pool. -/// -/// Will use one pool per thread in order to avoid locking. Will try to reuse command buffers. -/// Locking is required only when allocating/freeing command buffers. -pub struct StandardCommandPool { - device: Arc, - queue_family: u32, - per_thread: Mutex>, - - // Dummy marker in order to not implement `Send` and `Sync`. - // - // Since `StandardCommandPool` isn't Send/Sync, then the command buffers that use this pool - // won't be Send/Sync either, which means that we don't need to lock the pool while the CB - // is being built. - // - // However `StandardCommandPoolFinished` *is* Send/Sync because the only operation that can - // be called on `StandardCommandPoolFinished` is freeing, and freeing does actually lock. - dummy_avoid_send_sync: PhantomData<*const u8>, -} - -impl StandardCommandPool { - /// Builds a new pool. - /// - /// # Panic - /// - /// - Panicks if the device and the queue family don't belong to the same physical device. - /// - pub fn new(device: &Arc, queue_family: QueueFamily) -> StandardCommandPool { - assert_eq!(device.physical_device().internal_object(), - queue_family.physical_device().internal_object()); - - StandardCommandPool { - device: device.clone(), - queue_family: queue_family.id(), - per_thread: Mutex::new(HashMap::new()), - dummy_avoid_send_sync: PhantomData, - } - } -} - -struct StandardCommandPoolPerThread { - pool: UnsafeCommandPool, - available_primary_command_buffers: Vec, - available_secondary_command_buffers: Vec, -} - -unsafe impl CommandPool for Arc { - type Iter = Chain, UnsafeCommandPoolAllocIter>; - type Lock = (); - type Finished = StandardCommandPoolFinished; - - fn alloc(&self, secondary: bool, count: u32) -> Result { - let mut per_thread = self.per_thread.lock().unwrap(); - let mut per_thread = per_thread.entry(curr_thread_id()) - .or_insert_with(|| { - StandardCommandPoolPerThread { - pool: UnsafeCommandPool::new(&self.device, self.queue_family(), false, true).unwrap(), // FIXME: return error instead - available_primary_command_buffers: Vec::new(), - available_secondary_command_buffers: Vec::new(), - } - }); - - let mut existing = if secondary { &mut per_thread.available_secondary_command_buffers } - else { &mut per_thread.available_primary_command_buffers }; - - let num_from_existing = cmp::min(count as usize, existing.len()); - let from_existing = existing.drain(0 .. num_from_existing).collect::>().into_iter(); - - let num_new = count as usize - num_from_existing; - debug_assert!(num_new <= count as usize); // Check overflows. - let newly_allocated = try!(per_thread.pool.alloc_command_buffers(secondary, num_new)); - - Ok(from_existing.chain(newly_allocated)) - } - - unsafe fn free(&self, secondary: bool, command_buffers: I) - where I: Iterator - { - let mut per_thread = self.per_thread.lock().unwrap(); - let mut per_thread = per_thread.get_mut(&curr_thread_id()).unwrap(); - - if secondary { - for cb in command_buffers { - per_thread.available_secondary_command_buffers.push(cb); - } - } else { - for cb in command_buffers { - per_thread.available_primary_command_buffers.push(cb); - } - } - } - - #[inline] - fn finish(self) -> Self::Finished { - StandardCommandPoolFinished { - pool: self, - thread_id: curr_thread_id(), - } - } - - #[inline] - fn lock(&self) -> Self::Lock { - () - } - - #[inline] - fn can_reset_invidual_command_buffers(&self) -> bool { - true - } - - #[inline] - fn device(&self) -> &Arc { - &self.device - } - - #[inline] - fn queue_family(&self) -> QueueFamily { - self.device.physical_device().queue_family_by_id(self.queue_family).unwrap() - } -} - -pub struct StandardCommandPoolFinished { - pool: Arc, - thread_id: usize, -} - -unsafe impl CommandPoolFinished for StandardCommandPoolFinished { - unsafe fn free(&self, secondary: bool, command_buffers: I) - where I: Iterator - { - let mut per_thread = self.pool.per_thread.lock().unwrap(); - let mut per_thread = per_thread.get_mut(&curr_thread_id()).unwrap(); - - if secondary { - for cb in command_buffers { - per_thread.available_secondary_command_buffers.push(cb); - } - } else { - for cb in command_buffers { - per_thread.available_primary_command_buffers.push(cb); - } - } - } - - #[inline] - fn device(&self) -> &Arc { - self.pool.device() - } - - #[inline] - fn queue_family(&self) -> QueueFamily { - self.pool.queue_family() - } -} - -// See `StandardCommandPool` for comments about this. -unsafe impl Send for StandardCommandPoolFinished {} -unsafe impl Sync for StandardCommandPoolFinished {} - -/// Low-level implementation of a command pool. -pub struct UnsafeCommandPool { - pool: vk::CommandPool, - device: Arc, - queue_family_index: u32, - - // We don't want `UnsafeCommandPool` to implement Sync, since the Vulkan command pool isn't - // thread safe. - // - // This marker unimplements both Send and Sync, but we reimplement Send manually right under. - dummy_avoid_sync: PhantomData<*const u8>, -} - -unsafe impl Send for UnsafeCommandPool {} - -impl UnsafeCommandPool { - /// Creates a new pool. - /// - /// The command buffers created with this pool can only be executed on queues of the given - /// family. - /// - /// Setting `transient` to true is a hint to the implementation that the command buffers will - /// be short-lived. - /// Setting `reset_cb` to true means that command buffers can be reset individually. - /// - /// # Panic - /// - /// - Panicks if the queue family doesn't belong to the same physical device as `device`. - /// - pub fn new(device: &Arc, queue_family: QueueFamily, transient: bool, - reset_cb: bool) -> Result - { - assert_eq!(device.physical_device().internal_object(), - queue_family.physical_device().internal_object()); - - let vk = device.pointers(); - - let flags = { - let flag1 = if transient { vk::COMMAND_POOL_CREATE_TRANSIENT_BIT } else { 0 }; - let flag2 = if reset_cb { vk::COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT } - else { 0 }; - flag1 | flag2 - }; - - let pool = unsafe { - let infos = vk::CommandPoolCreateInfo { - sType: vk::STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - pNext: ptr::null(), - flags: flags, - queueFamilyIndex: queue_family.id(), - }; - - let mut output = mem::uninitialized(); - try!(check_errors(vk.CreateCommandPool(device.internal_object(), &infos, - ptr::null(), &mut output))); - output - }; - - Ok(UnsafeCommandPool { - pool: pool, - device: device.clone(), - queue_family_index: queue_family.id(), - dummy_avoid_sync: PhantomData, - }) - } - - /// Resets the pool, which resets all the command buffers that were allocated from it. - /// - /// # Safety - /// - /// The command buffers allocated from this pool jump to the initial state. - /// - #[inline] - pub unsafe fn reset(&self, release_resources: bool) -> Result<(), OomError> { - let flags = if release_resources { vk::COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT } - else { 0 }; - - let vk = self.device.pointers(); - try!(check_errors(vk.ResetCommandPool(self.device.internal_object(), self.pool, flags))); - Ok(()) - } - - /// Allocates `count` command buffers. - /// - /// If `secondary` is true, allocates secondary command buffers. Otherwise, allocates primary - /// command buffers. - pub fn alloc_command_buffers(&self, secondary: bool, count: usize) - -> Result - { - if count == 0 { - return Ok(UnsafeCommandPoolAllocIter(None)); - } - - let infos = vk::CommandBufferAllocateInfo { - sType: vk::STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - pNext: ptr::null(), - commandPool: self.pool, - level: if secondary { vk::COMMAND_BUFFER_LEVEL_SECONDARY } - else { vk::COMMAND_BUFFER_LEVEL_PRIMARY }, - commandBufferCount: count as u32, - }; - - unsafe { - let vk = self.device.pointers(); - let mut out = Vec::with_capacity(count); - try!(check_errors(vk.AllocateCommandBuffers(self.device.internal_object(), &infos, - out.as_mut_ptr()))); - - out.set_len(count); - - Ok(UnsafeCommandPoolAllocIter(Some(out.into_iter()))) - } - } - - /// Frees individual command buffers. - /// - /// # Safety - /// - /// The command buffers must have been allocated from this pool. - /// - pub unsafe fn free_command_buffers(&self, command_buffers: I) - where I: Iterator - { - let command_buffers: SmallVec<[_; 4]> = command_buffers.map(|cb| cb.0).collect(); - let vk = self.device.pointers(); - vk.FreeCommandBuffers(self.device.internal_object(), self.pool, - command_buffers.len() as u32, command_buffers.as_ptr()) - } - - /// Returns the device this command pool was created with. - #[inline] - pub fn device(&self) -> &Arc { - &self.device - } - - /// Returns the queue family on which command buffers of this pool can be executed. - #[inline] - pub fn queue_family(&self) -> QueueFamily { - self.device.physical_device().queue_family_by_id(self.queue_family_index).unwrap() - } -} - -unsafe impl VulkanObject for UnsafeCommandPool { - type Object = vk::CommandPool; - - #[inline] - fn internal_object(&self) -> vk::CommandPool { - self.pool - } -} - -impl Drop for UnsafeCommandPool { - #[inline] - fn drop(&mut self) { - unsafe { - let vk = self.device.pointers(); - vk.DestroyCommandPool(self.device.internal_object(), self.pool, ptr::null()); - } - } -} - -/// Iterator for newly-allocated command buffers. -pub struct UnsafeCommandPoolAllocIter(Option>); - -impl Iterator for UnsafeCommandPoolAllocIter { - type Item = AllocatedCommandBuffer; - - #[inline] - fn next(&mut self) -> Option { - self.0.as_mut().and_then(|i| i.next()).map(|cb| AllocatedCommandBuffer(cb)) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.0.as_ref().map(|i| i.size_hint()).unwrap_or((0, Some(0))) - } -} - -impl ExactSizeIterator for UnsafeCommandPoolAllocIter {} diff --git a/vulkano/src/command_buffer/pool/mod.rs b/vulkano/src/command_buffer/pool/mod.rs new file mode 100644 index 00000000..48f893d0 --- /dev/null +++ b/vulkano/src/command_buffer/pool/mod.rs @@ -0,0 +1,121 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +//! In the Vulkan API, command buffers must be allocated from *command pools*. +//! +//! A command pool holds and manages the memory of one or more command buffers. If you destroy a +//! command pool, all of its command buffers are automatically destroyed. +//! +//! In vulkano, creating a command buffer requires passing an implementation of the `CommandPool` +//! trait. By default vulkano will use the `StandardCommandPool` struct, but you can implement +//! this trait yourself by wrapping around the `UnsafeCommandPool` type. + +use std::sync::Arc; + +use instance::QueueFamily; + +use device::Device; +use OomError; +use VulkanObject; +use vk; + +pub use self::standard::StandardCommandPool; +pub use self::standard::StandardCommandPoolFinished; +pub use self::sys::UnsafeCommandPool; +pub use self::sys::UnsafeCommandPoolAllocIter; + +mod standard; +mod sys; + +/// Types that manage the memory of command buffers. +pub unsafe trait CommandPool { + /// See `alloc()`. + type Iter: Iterator; + /// See `lock()`. + type Lock; + /// See `finish()`. + type Finished: CommandPoolFinished; + + /// Allocates command buffers from this pool. + fn alloc(&self, secondary: bool, count: u32) -> Result; + + /// Frees command buffers from this pool. + /// + /// # Safety + /// + /// - The command buffers must have been allocated from this pool. + /// - `secondary` must have the same value as what was passed to `alloc`. + /// + unsafe fn free(&self, secondary: bool, command_buffers: I) + where I: Iterator; + + /// Once a command buffer has finished being built, it should call this method in order to + /// produce a `Finished` object. + /// + /// The `Finished` object must hold the pool alive. + /// + /// The point of this object is to change the Send/Sync strategy after a command buffer has + /// finished being built compared to before. + fn finish(self) -> Self::Finished; + + /// Before any command buffer allocated from this pool can be modified, the pool itself must + /// be locked by calling this method. + /// + /// All the operations are atomic at the thread level, so the point of this lock is to + /// prevent the pool from being accessed from multiple threads in parallel. + fn lock(&self) -> Self::Lock; + + /// Returns true if command buffers can be reset individually. In other words, if the pool + /// was created with `reset_cb` set to true. + fn can_reset_invidual_command_buffers(&self) -> bool; + + /// Returns the device used to create this pool. + fn device(&self) -> &Arc; + + /// Returns the queue family that this pool targets. + fn queue_family(&self) -> QueueFamily; +} + +/// See `CommandPool::finish()`. +pub unsafe trait CommandPoolFinished { + /// Frees command buffers. + /// + /// # Safety + /// + /// - The command buffers must have been allocated from this pool. + /// - `secondary` must have the same value as what was passed to `alloc`. + /// + unsafe fn free(&self, secondary: bool, command_buffers: I) + where I: Iterator; + + /// Returns the device used to create this pool. + fn device(&self) -> &Arc; + + /// Returns the queue family that this pool targets. + fn queue_family(&self) -> QueueFamily; +} + +/// Opaque type that represents a command buffer allocated from a pool. +pub struct AllocatedCommandBuffer(vk::CommandBuffer); + +impl From for AllocatedCommandBuffer { + #[inline] + fn from(cmd: vk::CommandBuffer) -> AllocatedCommandBuffer { + AllocatedCommandBuffer(cmd) + } +} + +unsafe impl VulkanObject for AllocatedCommandBuffer { + type Object = vk::CommandBuffer; + + #[inline] + fn internal_object(&self) -> vk::CommandBuffer { + self.0 + } +} diff --git a/vulkano/src/command_buffer/pool/standard.rs b/vulkano/src/command_buffer/pool/standard.rs new file mode 100644 index 00000000..23d19228 --- /dev/null +++ b/vulkano/src/command_buffer/pool/standard.rs @@ -0,0 +1,192 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use std::cmp; +use std::collections::HashMap; +use std::iter::Chain; +use std::marker::PhantomData; +use std::sync::Arc; +use std::sync::Mutex; +use std::vec::IntoIter as VecIntoIter; + +use command_buffer::pool::AllocatedCommandBuffer; +use command_buffer::pool::CommandPool; +use command_buffer::pool::CommandPoolFinished; +use command_buffer::pool::UnsafeCommandPool; +use command_buffer::pool::UnsafeCommandPoolAllocIter; +use instance::QueueFamily; + +use device::Device; +use OomError; +use VulkanObject; + +// Since the stdlib doesn't have a "thread ID" yet, we store a `Box` for each thread and the +// value of the pointer will be used as a thread id. +thread_local!(static THREAD_ID: Box = Box::new(0)); +#[inline] +fn curr_thread_id() -> usize { THREAD_ID.with(|data| &**data as *const u8 as usize) } + +/// Standard implementation of a command pool. +/// +/// Will use one pool per thread in order to avoid locking. Will try to reuse command buffers. +/// Locking is required only when allocating/freeing command buffers. +pub struct StandardCommandPool { + device: Arc, + queue_family: u32, + per_thread: Mutex>, + + // Dummy marker in order to not implement `Send` and `Sync`. + // + // Since `StandardCommandPool` isn't Send/Sync, then the command buffers that use this pool + // won't be Send/Sync either, which means that we don't need to lock the pool while the CB + // is being built. + // + // However `StandardCommandPoolFinished` *is* Send/Sync because the only operation that can + // be called on `StandardCommandPoolFinished` is freeing, and freeing does actually lock. + dummy_avoid_send_sync: PhantomData<*const u8>, +} + +impl StandardCommandPool { + /// Builds a new pool. + /// + /// # Panic + /// + /// - Panicks if the device and the queue family don't belong to the same physical device. + /// + pub fn new(device: &Arc, queue_family: QueueFamily) -> StandardCommandPool { + assert_eq!(device.physical_device().internal_object(), + queue_family.physical_device().internal_object()); + + StandardCommandPool { + device: device.clone(), + queue_family: queue_family.id(), + per_thread: Mutex::new(HashMap::new()), + dummy_avoid_send_sync: PhantomData, + } + } +} + +struct StandardCommandPoolPerThread { + pool: UnsafeCommandPool, + available_primary_command_buffers: Vec, + available_secondary_command_buffers: Vec, +} + +unsafe impl CommandPool for Arc { + type Iter = Chain, UnsafeCommandPoolAllocIter>; + type Lock = (); + type Finished = StandardCommandPoolFinished; + + fn alloc(&self, secondary: bool, count: u32) -> Result { + let mut per_thread = self.per_thread.lock().unwrap(); + let mut per_thread = per_thread.entry(curr_thread_id()) + .or_insert_with(|| { + StandardCommandPoolPerThread { + pool: UnsafeCommandPool::new(&self.device, self.queue_family(), false, true).unwrap(), // FIXME: return error instead + available_primary_command_buffers: Vec::new(), + available_secondary_command_buffers: Vec::new(), + } + }); + + let mut existing = if secondary { &mut per_thread.available_secondary_command_buffers } + else { &mut per_thread.available_primary_command_buffers }; + + let num_from_existing = cmp::min(count as usize, existing.len()); + let from_existing = existing.drain(0 .. num_from_existing).collect::>().into_iter(); + + let num_new = count as usize - num_from_existing; + debug_assert!(num_new <= count as usize); // Check overflows. + let newly_allocated = try!(per_thread.pool.alloc_command_buffers(secondary, num_new)); + + Ok(from_existing.chain(newly_allocated)) + } + + unsafe fn free(&self, secondary: bool, command_buffers: I) + where I: Iterator + { + let mut per_thread = self.per_thread.lock().unwrap(); + let mut per_thread = per_thread.get_mut(&curr_thread_id()).unwrap(); + + if secondary { + for cb in command_buffers { + per_thread.available_secondary_command_buffers.push(cb); + } + } else { + for cb in command_buffers { + per_thread.available_primary_command_buffers.push(cb); + } + } + } + + #[inline] + fn finish(self) -> Self::Finished { + StandardCommandPoolFinished { + pool: self, + thread_id: curr_thread_id(), + } + } + + #[inline] + fn lock(&self) -> Self::Lock { + () + } + + #[inline] + fn can_reset_invidual_command_buffers(&self) -> bool { + true + } + + #[inline] + fn device(&self) -> &Arc { + &self.device + } + + #[inline] + fn queue_family(&self) -> QueueFamily { + self.device.physical_device().queue_family_by_id(self.queue_family).unwrap() + } +} + +pub struct StandardCommandPoolFinished { + pool: Arc, + thread_id: usize, +} + +unsafe impl CommandPoolFinished for StandardCommandPoolFinished { + unsafe fn free(&self, secondary: bool, command_buffers: I) + where I: Iterator + { + let mut per_thread = self.pool.per_thread.lock().unwrap(); + let mut per_thread = per_thread.get_mut(&curr_thread_id()).unwrap(); + + if secondary { + for cb in command_buffers { + per_thread.available_secondary_command_buffers.push(cb); + } + } else { + for cb in command_buffers { + per_thread.available_primary_command_buffers.push(cb); + } + } + } + + #[inline] + fn device(&self) -> &Arc { + self.pool.device() + } + + #[inline] + fn queue_family(&self) -> QueueFamily { + self.pool.queue_family() + } +} + +// See `StandardCommandPool` for comments about this. +unsafe impl Send for StandardCommandPoolFinished {} +unsafe impl Sync for StandardCommandPoolFinished {} diff --git a/vulkano/src/command_buffer/pool/sys.rs b/vulkano/src/command_buffer/pool/sys.rs new file mode 100644 index 00000000..1fe79f2d --- /dev/null +++ b/vulkano/src/command_buffer/pool/sys.rs @@ -0,0 +1,205 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use std::marker::PhantomData; +use std::mem; +use std::ptr; +use std::sync::Arc; +use std::vec::IntoIter as VecIntoIter; +use smallvec::SmallVec; + +use command_buffer::pool::AllocatedCommandBuffer; +use instance::QueueFamily; + +use device::Device; +use OomError; +use VulkanObject; +use VulkanPointers; +use check_errors; +use vk; + +/// Low-level implementation of a command pool. +pub struct UnsafeCommandPool { + pool: vk::CommandPool, + device: Arc, + queue_family_index: u32, + + // We don't want `UnsafeCommandPool` to implement Sync, since the Vulkan command pool isn't + // thread safe. + // + // This marker unimplements both Send and Sync, but we reimplement Send manually right under. + dummy_avoid_sync: PhantomData<*const u8>, +} + +unsafe impl Send for UnsafeCommandPool {} + +impl UnsafeCommandPool { + /// Creates a new pool. + /// + /// The command buffers created with this pool can only be executed on queues of the given + /// family. + /// + /// Setting `transient` to true is a hint to the implementation that the command buffers will + /// be short-lived. + /// Setting `reset_cb` to true means that command buffers can be reset individually. + /// + /// # Panic + /// + /// - Panicks if the queue family doesn't belong to the same physical device as `device`. + /// + pub fn new(device: &Arc, queue_family: QueueFamily, transient: bool, + reset_cb: bool) -> Result + { + assert_eq!(device.physical_device().internal_object(), + queue_family.physical_device().internal_object()); + + let vk = device.pointers(); + + let flags = { + let flag1 = if transient { vk::COMMAND_POOL_CREATE_TRANSIENT_BIT } else { 0 }; + let flag2 = if reset_cb { vk::COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT } + else { 0 }; + flag1 | flag2 + }; + + let pool = unsafe { + let infos = vk::CommandPoolCreateInfo { + sType: vk::STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + pNext: ptr::null(), + flags: flags, + queueFamilyIndex: queue_family.id(), + }; + + let mut output = mem::uninitialized(); + try!(check_errors(vk.CreateCommandPool(device.internal_object(), &infos, + ptr::null(), &mut output))); + output + }; + + Ok(UnsafeCommandPool { + pool: pool, + device: device.clone(), + queue_family_index: queue_family.id(), + dummy_avoid_sync: PhantomData, + }) + } + + /// Resets the pool, which resets all the command buffers that were allocated from it. + /// + /// # Safety + /// + /// The command buffers allocated from this pool jump to the initial state. + /// + #[inline] + pub unsafe fn reset(&self, release_resources: bool) -> Result<(), OomError> { + let flags = if release_resources { vk::COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT } + else { 0 }; + + let vk = self.device.pointers(); + try!(check_errors(vk.ResetCommandPool(self.device.internal_object(), self.pool, flags))); + Ok(()) + } + + /// Allocates `count` command buffers. + /// + /// If `secondary` is true, allocates secondary command buffers. Otherwise, allocates primary + /// command buffers. + pub fn alloc_command_buffers(&self, secondary: bool, count: usize) + -> Result + { + if count == 0 { + return Ok(UnsafeCommandPoolAllocIter(None)); + } + + let infos = vk::CommandBufferAllocateInfo { + sType: vk::STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + pNext: ptr::null(), + commandPool: self.pool, + level: if secondary { vk::COMMAND_BUFFER_LEVEL_SECONDARY } + else { vk::COMMAND_BUFFER_LEVEL_PRIMARY }, + commandBufferCount: count as u32, + }; + + unsafe { + let vk = self.device.pointers(); + let mut out = Vec::with_capacity(count); + try!(check_errors(vk.AllocateCommandBuffers(self.device.internal_object(), &infos, + out.as_mut_ptr()))); + + out.set_len(count); + + Ok(UnsafeCommandPoolAllocIter(Some(out.into_iter()))) + } + } + + /// Frees individual command buffers. + /// + /// # Safety + /// + /// The command buffers must have been allocated from this pool. + /// + pub unsafe fn free_command_buffers(&self, command_buffers: I) + where I: Iterator + { + let command_buffers: SmallVec<[_; 4]> = command_buffers.map(|cb| cb.0).collect(); + let vk = self.device.pointers(); + vk.FreeCommandBuffers(self.device.internal_object(), self.pool, + command_buffers.len() as u32, command_buffers.as_ptr()) + } + + /// Returns the device this command pool was created with. + #[inline] + pub fn device(&self) -> &Arc { + &self.device + } + + /// Returns the queue family on which command buffers of this pool can be executed. + #[inline] + pub fn queue_family(&self) -> QueueFamily { + self.device.physical_device().queue_family_by_id(self.queue_family_index).unwrap() + } +} + +unsafe impl VulkanObject for UnsafeCommandPool { + type Object = vk::CommandPool; + + #[inline] + fn internal_object(&self) -> vk::CommandPool { + self.pool + } +} + +impl Drop for UnsafeCommandPool { + #[inline] + fn drop(&mut self) { + unsafe { + let vk = self.device.pointers(); + vk.DestroyCommandPool(self.device.internal_object(), self.pool, ptr::null()); + } + } +} + +/// Iterator for newly-allocated command buffers. +pub struct UnsafeCommandPoolAllocIter(Option>); + +impl Iterator for UnsafeCommandPoolAllocIter { + type Item = AllocatedCommandBuffer; + + #[inline] + fn next(&mut self) -> Option { + self.0.as_mut().and_then(|i| i.next()).map(|cb| AllocatedCommandBuffer(cb)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.as_ref().map(|i| i.size_hint()).unwrap_or((0, Some(0))) + } +} + +impl ExactSizeIterator for UnsafeCommandPoolAllocIter {} From 17c1ed08db2e1539786020109b9ccaa03ba2fa4b Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Sat, 4 Jun 2016 14:08:12 +0200 Subject: [PATCH 3/3] Add some comments --- vulkano/src/command_buffer/pool/standard.rs | 42 ++++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/vulkano/src/command_buffer/pool/standard.rs b/vulkano/src/command_buffer/pool/standard.rs index 23d19228..ae328eed 100644 --- a/vulkano/src/command_buffer/pool/standard.rs +++ b/vulkano/src/command_buffer/pool/standard.rs @@ -8,6 +8,7 @@ // according to those terms. use std::cmp; +use std::collections::hash_map::Entry; use std::collections::HashMap; use std::iter::Chain; use std::marker::PhantomData; @@ -34,11 +35,16 @@ fn curr_thread_id() -> usize { THREAD_ID.with(|data| &**data as *const u8 as usi /// Standard implementation of a command pool. /// -/// Will use one pool per thread in order to avoid locking. Will try to reuse command buffers. -/// Locking is required only when allocating/freeing command buffers. +/// Will use one Vulkan pool per thread in order to avoid locking. Will try to reuse command +/// buffers. Locking is required only when allocating/freeing command buffers. pub struct StandardCommandPool { + // The device. device: Arc, + + // Identifier of the queue family. queue_family: u32, + + // For each "thread id" (see `THREAD_ID` above), we store thread-specific info. per_thread: Mutex>, // Dummy marker in order to not implement `Send` and `Sync`. @@ -73,8 +79,11 @@ impl StandardCommandPool { } struct StandardCommandPoolPerThread { + // The Vulkan pool of this thread. pool: UnsafeCommandPool, + // List of existing primary command buffers that are available for reuse. available_primary_command_buffers: Vec, + // List of existing secondary command buffers that are available for reuse. available_secondary_command_buffers: Vec, } @@ -84,32 +93,45 @@ unsafe impl CommandPool for Arc { type Finished = StandardCommandPoolFinished; fn alloc(&self, secondary: bool, count: u32) -> Result { + // Find the correct `StandardCommandPoolPerThread` structure. let mut per_thread = self.per_thread.lock().unwrap(); - let mut per_thread = per_thread.entry(curr_thread_id()) - .or_insert_with(|| { - StandardCommandPoolPerThread { - pool: UnsafeCommandPool::new(&self.device, self.queue_family(), false, true).unwrap(), // FIXME: return error instead - available_primary_command_buffers: Vec::new(), - available_secondary_command_buffers: Vec::new(), - } - }); + let mut per_thread = match per_thread.entry(curr_thread_id()) { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + let new_pool = try!(UnsafeCommandPool::new(&self.device, self.queue_family(), + false, true)); + entry.insert(StandardCommandPoolPerThread { + pool: new_pool, + available_primary_command_buffers: Vec::new(), + available_secondary_command_buffers: Vec::new(), + }) + }, + }; + + // Which list of already-existing command buffers we are going to pick CBs from. let mut existing = if secondary { &mut per_thread.available_secondary_command_buffers } else { &mut per_thread.available_primary_command_buffers }; + // Build an iterator to pick from already-existing command buffers. let num_from_existing = cmp::min(count as usize, existing.len()); let from_existing = existing.drain(0 .. num_from_existing).collect::>().into_iter(); + // Build an iterator to construct the missing command buffers from the Vulkan pool. let num_new = count as usize - num_from_existing; debug_assert!(num_new <= count as usize); // Check overflows. let newly_allocated = try!(per_thread.pool.alloc_command_buffers(secondary, num_new)); + // Returning them as a chain. Ok(from_existing.chain(newly_allocated)) } unsafe fn free(&self, secondary: bool, command_buffers: I) where I: Iterator { + // Do not actually free the command buffers. Instead adding them to the list of command + // buffers available for reuse. + let mut per_thread = self.per_thread.lock().unwrap(); let mut per_thread = per_thread.get_mut(&curr_thread_id()).unwrap();