Move to manual management of descriptor set and command buffer allocators (#1957)

* Rename `Pool` -> `Allocator`

* Fix module names

* Rename actual pool types

* Remove internally managed allocators

* Fix doc tests

* Update docs

* Update examples

* Fix merging oopsie

* Fix cb allocators only working for one q family

* Make `DescriptorPool` `!Sync`, remove `&mut`

* Combine lean files

* Consistency

* Rename single layout pools

* Re-export variable descriptor set pool

Co-authored-by: comrademarc <40955683+comrademarc@users.noreply.github.com>
This commit is contained in:
marc0246 2022-10-05 11:09:26 +02:00 committed by GitHub
parent f70f43340f
commit ef65c98ad1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 1669 additions and 1300 deletions

View File

@ -15,8 +15,12 @@
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer},
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
command_buffer::{
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -140,6 +144,9 @@ fn main() {
.unwrap()
};
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
// We start by creating the buffer that will store the data.
let data_buffer = {
// Iterator that produces the data.
@ -167,6 +174,7 @@ fn main() {
// descriptor sets that each contain the buffer you want to run the shader on.
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::buffer(0, data_buffer.clone())],
)
@ -174,7 +182,7 @@ fn main() {
// In order to execute our operation, we have to build a command buffer.
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -27,7 +27,8 @@ use std::{
use vulkano::{
buffer::CpuBufferPool,
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
@ -239,6 +240,8 @@ fn main() {
let mut recreate_swapchain = false;
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
@ -330,7 +333,7 @@ fn main() {
// Allocate a new chunk from buffer_pool
let buffer = buffer_pool.from_iter(data.to_vec()).unwrap();
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -10,8 +10,8 @@
use std::sync::Arc;
use vulkano::{
command_buffer::{
AutoCommandBufferBuilder, ClearAttachment, ClearRect, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, ClearAttachment,
ClearRect, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
@ -153,6 +153,8 @@ fn main() {
)
.unwrap();
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let mut width = swapchain.image_extent()[0];
let mut height = swapchain.image_extent()[1];
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone());
@ -213,7 +215,7 @@ fn main() {
}
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -9,6 +9,7 @@
use std::sync::Arc;
use vulkano::{
command_buffer::allocator::StandardCommandBufferAllocator,
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -158,7 +159,7 @@ fn main() {
})
.expect("no device available");
let (_, mut queues) = Device::new(
let (device, mut queues) = Device::new(
physical_device,
DeviceCreateInfo {
enabled_extensions: device_extensions,
@ -172,6 +173,8 @@ fn main() {
.expect("failed to create device");
let queue = queues.next().unwrap();
let command_buffer_allocator = StandardCommandBufferAllocator::new(device);
// Create an image in order to generate some additional logging:
let pixel_format = Format::R8G8B8A8_UINT;
let dimensions = ImageDimensions::Dim2d {
@ -185,6 +188,7 @@ fn main() {
dimensions,
MipmapsCount::One,
pixel_format,
&command_buffer_allocator,
queue,
)
.unwrap();

View File

@ -8,14 +8,16 @@
// according to those terms.
use bytemuck::{Pod, Zeroable};
use std::sync::Arc;
use std::{rc::Rc, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferInheritanceInfo, CommandBufferUsage,
SecondaryAutoCommandBuffer,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder,
CommandBufferInheritanceInfo, CommandBufferUsage, SecondaryAutoCommandBuffer,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
device::Queue,
image::ImageViewAbstract,
impl_vertex,
@ -37,11 +39,18 @@ pub struct AmbientLightingSystem {
vertex_buffer: Arc<CpuAccessibleBuffer<[Vertex]>>,
subpass: Subpass,
pipeline: Arc<GraphicsPipeline>,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
}
impl AmbientLightingSystem {
/// Initializes the ambient lighting system.
pub fn new(gfx_queue: Arc<Queue>, subpass: Subpass) -> AmbientLightingSystem {
pub fn new(
gfx_queue: Arc<Queue>,
subpass: Subpass,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
) -> AmbientLightingSystem {
// TODO: vulkano doesn't allow us to draw without a vertex buffer, otherwise we could
// hard-code these values in the shader
let vertices = [
@ -98,6 +107,8 @@ impl AmbientLightingSystem {
vertex_buffer,
subpass,
pipeline,
command_buffer_allocator,
descriptor_set_allocator,
}
}
@ -125,6 +136,7 @@ impl AmbientLightingSystem {
let layout = self.pipeline.layout().set_layouts().get(0).unwrap();
let descriptor_set = PersistentDescriptorSet::new(
&*self.descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::image_view(0, color_input)],
)
@ -137,7 +149,7 @@ impl AmbientLightingSystem {
};
let mut builder = AutoCommandBufferBuilder::secondary(
self.gfx_queue.device().clone(),
&*self.command_buffer_allocator,
self.gfx_queue.queue_family_index(),
CommandBufferUsage::MultipleSubmit,
CommandBufferInheritanceInfo {

View File

@ -9,14 +9,16 @@
use bytemuck::{Pod, Zeroable};
use cgmath::Vector3;
use std::sync::Arc;
use std::{rc::Rc, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferInheritanceInfo, CommandBufferUsage,
SecondaryAutoCommandBuffer,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder,
CommandBufferInheritanceInfo, CommandBufferUsage, SecondaryAutoCommandBuffer,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
device::Queue,
image::ImageViewAbstract,
impl_vertex,
@ -38,11 +40,18 @@ pub struct DirectionalLightingSystem {
vertex_buffer: Arc<CpuAccessibleBuffer<[Vertex]>>,
subpass: Subpass,
pipeline: Arc<GraphicsPipeline>,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
}
impl DirectionalLightingSystem {
/// Initializes the directional lighting system.
pub fn new(gfx_queue: Arc<Queue>, subpass: Subpass) -> DirectionalLightingSystem {
pub fn new(
gfx_queue: Arc<Queue>,
subpass: Subpass,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
) -> DirectionalLightingSystem {
// TODO: vulkano doesn't allow us to draw without a vertex buffer, otherwise we could
// hard-code these values in the shader
let vertices = [
@ -99,6 +108,8 @@ impl DirectionalLightingSystem {
vertex_buffer,
subpass,
pipeline,
command_buffer_allocator,
descriptor_set_allocator,
}
}
@ -136,6 +147,7 @@ impl DirectionalLightingSystem {
let layout = self.pipeline.layout().set_layouts().get(0).unwrap();
let descriptor_set = PersistentDescriptorSet::new(
&*self.descriptor_set_allocator,
layout.clone(),
[
WriteDescriptorSet::image_view(0, color_input),
@ -151,7 +163,7 @@ impl DirectionalLightingSystem {
};
let mut builder = AutoCommandBufferBuilder::secondary(
self.gfx_queue.device().clone(),
&*self.command_buffer_allocator,
self.gfx_queue.queue_family_index(),
CommandBufferUsage::MultipleSubmit,
CommandBufferInheritanceInfo {

View File

@ -9,14 +9,16 @@
use bytemuck::{Pod, Zeroable};
use cgmath::{Matrix4, Vector3};
use std::sync::Arc;
use std::{rc::Rc, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferInheritanceInfo, CommandBufferUsage,
SecondaryAutoCommandBuffer,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder,
CommandBufferInheritanceInfo, CommandBufferUsage, SecondaryAutoCommandBuffer,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
device::Queue,
image::ImageViewAbstract,
impl_vertex,
@ -37,11 +39,18 @@ pub struct PointLightingSystem {
vertex_buffer: Arc<CpuAccessibleBuffer<[Vertex]>>,
subpass: Subpass,
pipeline: Arc<GraphicsPipeline>,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
}
impl PointLightingSystem {
/// Initializes the point lighting system.
pub fn new(gfx_queue: Arc<Queue>, subpass: Subpass) -> PointLightingSystem {
pub fn new(
gfx_queue: Arc<Queue>,
subpass: Subpass,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
) -> PointLightingSystem {
// TODO: vulkano doesn't allow us to draw without a vertex buffer, otherwise we could
// hard-code these values in the shader
let vertices = [
@ -98,6 +107,8 @@ impl PointLightingSystem {
vertex_buffer,
subpass,
pipeline,
command_buffer_allocator,
descriptor_set_allocator,
}
}
@ -148,6 +159,7 @@ impl PointLightingSystem {
let layout = self.pipeline.layout().set_layouts().get(0).unwrap();
let descriptor_set = PersistentDescriptorSet::new(
&*self.descriptor_set_allocator,
layout.clone(),
[
WriteDescriptorSet::image_view(0, color_input),
@ -164,7 +176,7 @@ impl PointLightingSystem {
};
let mut builder = AutoCommandBufferBuilder::secondary(
self.gfx_queue.device().clone(),
&*self.command_buffer_allocator,
self.gfx_queue.queue_family_index(),
CommandBufferUsage::MultipleSubmit,
CommandBufferInheritanceInfo {

View File

@ -13,12 +13,13 @@ use super::{
point_lighting_system::PointLightingSystem,
};
use cgmath::{Matrix4, SquareMatrix, Vector3};
use std::sync::Arc;
use std::{rc::Rc, sync::Arc};
use vulkano::{
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer,
RenderPassBeginInfo, SecondaryCommandBuffer, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
PrimaryAutoCommandBuffer, RenderPassBeginInfo, SecondaryCommandBuffer, SubpassContents,
},
descriptor_set::allocator::StandardDescriptorSetAllocator,
device::Queue,
format::Format,
image::{view::ImageView, AttachmentImage, ImageAccess, ImageUsage, ImageViewAbstract},
@ -36,6 +37,8 @@ pub struct FrameSystem {
// in of a change in the dimensions.
render_pass: Arc<RenderPass>,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
// Intermediate render target that will contain the albedo of each pixel of the scene.
diffuse_buffer: Arc<ImageView<AttachmentImage>>,
// Intermediate render target that will contain the normal vector in world coordinates of each
@ -64,7 +67,11 @@ impl FrameSystem {
/// `frame()` method. We need to know that in advance. If that format ever changes, we have
/// to create a new `FrameSystem`.
///
pub fn new(gfx_queue: Arc<Queue>, final_output_format: Format) -> FrameSystem {
pub fn new(
gfx_queue: Arc<Queue>,
final_output_format: Format,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
) -> FrameSystem {
// Creating the render pass.
//
// The render pass has two subpasses. In the first subpass, we draw all the objects of the
@ -185,18 +192,36 @@ impl FrameSystem {
)
.unwrap();
let descriptor_set_allocator = Rc::new(StandardDescriptorSetAllocator::new(
gfx_queue.device().clone(),
));
// Initialize the three lighting systems.
// Note that we need to pass to them the subpass where they will be executed.
let lighting_subpass = Subpass::from(render_pass.clone(), 1).unwrap();
let ambient_lighting_system =
AmbientLightingSystem::new(gfx_queue.clone(), lighting_subpass.clone());
let directional_lighting_system =
DirectionalLightingSystem::new(gfx_queue.clone(), lighting_subpass.clone());
let point_lighting_system = PointLightingSystem::new(gfx_queue.clone(), lighting_subpass);
let ambient_lighting_system = AmbientLightingSystem::new(
gfx_queue.clone(),
lighting_subpass.clone(),
command_buffer_allocator.clone(),
descriptor_set_allocator.clone(),
);
let directional_lighting_system = DirectionalLightingSystem::new(
gfx_queue.clone(),
lighting_subpass.clone(),
command_buffer_allocator.clone(),
descriptor_set_allocator.clone(),
);
let point_lighting_system = PointLightingSystem::new(
gfx_queue.clone(),
lighting_subpass,
command_buffer_allocator.clone(),
descriptor_set_allocator,
);
FrameSystem {
gfx_queue,
render_pass,
command_buffer_allocator,
diffuse_buffer,
normals_buffer,
depth_buffer,
@ -304,7 +329,7 @@ impl FrameSystem {
// Start the command buffer builder that will be filled throughout the frame handling.
let mut command_buffer_builder = AutoCommandBufferBuilder::primary(
self.gfx_queue.device().clone(),
&*self.command_buffer_allocator,
self.gfx_queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -30,7 +30,9 @@ use crate::{
triangle_draw_system::TriangleDrawSystem,
};
use cgmath::{Matrix4, SquareMatrix, Vector3};
use std::rc::Rc;
use vulkano::{
command_buffer::allocator::StandardCommandBufferAllocator,
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -161,10 +163,19 @@ fn main() {
(swapchain, images)
};
let command_buffer_allocator = Rc::new(StandardCommandBufferAllocator::new(device.clone()));
// Here is the basic initialization for the deferred system.
let mut frame_system = FrameSystem::new(queue.clone(), swapchain.image_format());
let triangle_draw_system =
TriangleDrawSystem::new(queue.clone(), frame_system.deferred_subpass());
let mut frame_system = FrameSystem::new(
queue.clone(),
swapchain.image_format(),
command_buffer_allocator.clone(),
);
let triangle_draw_system = TriangleDrawSystem::new(
queue.clone(),
frame_system.deferred_subpass(),
command_buffer_allocator,
);
let mut recreate_swapchain = false;
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());

View File

@ -8,12 +8,12 @@
// according to those terms.
use bytemuck::{Pod, Zeroable};
use std::sync::Arc;
use std::{rc::Rc, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferInheritanceInfo, CommandBufferUsage,
SecondaryAutoCommandBuffer,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder,
CommandBufferInheritanceInfo, CommandBufferUsage, SecondaryAutoCommandBuffer,
},
device::Queue,
impl_vertex,
@ -34,11 +34,16 @@ pub struct TriangleDrawSystem {
vertex_buffer: Arc<CpuAccessibleBuffer<[Vertex]>>,
subpass: Subpass,
pipeline: Arc<GraphicsPipeline>,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
}
impl TriangleDrawSystem {
/// Initializes a triangle drawing system.
pub fn new(gfx_queue: Arc<Queue>, subpass: Subpass) -> TriangleDrawSystem {
pub fn new(
gfx_queue: Arc<Queue>,
subpass: Subpass,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
) -> TriangleDrawSystem {
let vertices = [
Vertex {
position: [-0.5, -0.25],
@ -84,13 +89,14 @@ impl TriangleDrawSystem {
vertex_buffer,
subpass,
pipeline,
command_buffer_allocator,
}
}
/// Builds a secondary command buffer that draws the triangle on the current subpass.
pub fn draw(&self, viewport_dimensions: [u32; 2]) -> SecondaryAutoCommandBuffer {
let mut builder = AutoCommandBufferBuilder::secondary(
self.gfx_queue.device().clone(),
&*self.command_buffer_allocator,
self.gfx_queue.queue_family_index(),
CommandBufferUsage::MultipleSubmit,
CommandBufferInheritanceInfo {

View File

@ -17,9 +17,12 @@
use std::{iter::repeat, mem::size_of};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer},
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage},
command_buffer::{
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
},
descriptor_set::{
layout::DescriptorType, DescriptorSet, PersistentDescriptorSet, WriteDescriptorSet,
allocator::StandardDescriptorSetAllocator, layout::DescriptorType, DescriptorSet,
PersistentDescriptorSet, WriteDescriptorSet,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
@ -128,6 +131,9 @@ fn main() {
)
.unwrap();
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
// Declare input buffer.
// Data in a dynamic buffer **MUST** be aligned to min_uniform_buffer_offset_align
// or min_storage_buffer_offset_align, depending on the type of buffer.
@ -181,6 +187,7 @@ fn main() {
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
layout.clone(),
[
WriteDescriptorSet::buffer(0, input_buffer),
@ -191,7 +198,7 @@ fn main() {
// Build the command buffer, using different offsets for each call.
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -17,8 +17,13 @@
use std::{fs::File, io::BufWriter, path::Path};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer},
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, CopyImageToBufferInfo},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
command_buffer::{
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
CopyImageToBufferInfo,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -193,6 +198,9 @@ fn main() {
)
.unwrap();
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let image = StorageImage::new(
device.clone(),
ImageDimensions::Dim2d {
@ -207,8 +215,11 @@ fn main() {
let view = ImageView::new_default(image.clone()).unwrap();
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set =
PersistentDescriptorSet::new(layout.clone(), [WriteDescriptorSet::image_view(0, view)])
let set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::image_view(0, view)],
)
.unwrap();
let buf = CpuAccessibleBuffer::from_iter(
@ -223,7 +234,7 @@ fn main() {
.unwrap();
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -17,10 +17,13 @@ mod linux {
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SemaphoreSubmitInfo,
SubmitInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder,
CommandBufferUsage, RenderPassBeginInfo, SemaphoreSubmitInfo, SubmitInfo,
SubpassContents,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueCreateInfo,
@ -225,9 +228,13 @@ mod linux {
}
});
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::image_view_sampler(
0, image_view, sampler,
@ -324,7 +331,7 @@ mod linux {
}
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -12,11 +12,14 @@ use std::{io::Cursor, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, BlitImageInfo, BufferImageCopy, ClearColorImageInfo,
CommandBufferUsage, CopyBufferToImageInfo, CopyImageInfo, ImageBlit, ImageCopy,
PrimaryCommandBuffer, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, BlitImageInfo,
BufferImageCopy, ClearColorImageInfo, CommandBufferUsage, CopyBufferToImageInfo,
CopyImageInfo, ImageBlit, ImageCopy, PrimaryCommandBuffer, RenderPassBeginInfo,
SubpassContents,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -207,6 +210,9 @@ fn main() {
)
.unwrap();
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let (texture, tex_future) = {
let png_bytes = include_bytes!("image_img.png").to_vec();
let cursor = Cursor::new(png_bytes);
@ -243,7 +249,7 @@ fn main() {
.unwrap();
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)
@ -339,6 +345,7 @@ fn main() {
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::image_view_sampler(0, texture, sampler)],
)
@ -406,7 +413,7 @@ fn main() {
}
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -12,9 +12,12 @@ use std::{io::Cursor, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -205,6 +208,9 @@ fn main() {
)
.unwrap();
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let (texture, tex_future) = {
let png_bytes = include_bytes!("image_img.png").to_vec();
let cursor = Cursor::new(png_bytes);
@ -225,6 +231,7 @@ fn main() {
dimensions,
MipmapsCount::One,
Format::R8G8B8A8_SRGB,
&command_buffer_allocator,
queue.clone(),
)
.unwrap();
@ -256,6 +263,7 @@ fn main() {
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::image_view_sampler(0, texture, sampler)],
)
@ -323,7 +331,7 @@ fn main() {
}
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -21,9 +21,12 @@ use std::{io::Cursor, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -211,6 +214,9 @@ fn main() {
)
.unwrap();
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let (texture, tex_future) = {
let png_bytes = include_bytes!("image_img.png").to_vec();
let cursor = Cursor::new(png_bytes);
@ -231,6 +237,7 @@ fn main() {
dimensions,
MipmapsCount::One,
Format::R8G8B8A8_SRGB,
&command_buffer_allocator,
queue.clone(),
)
.unwrap();
@ -268,8 +275,11 @@ fn main() {
let layout = pipeline.layout().set_layouts().get(0).unwrap();
// Use `image_view` instead of `image_view_sampler`, since the sampler is already in the layout.
let set =
PersistentDescriptorSet::new(layout.clone(), [WriteDescriptorSet::image_view(0, texture)])
let set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::image_view(0, texture)],
)
.unwrap();
let mut viewport = Viewport {
@ -334,7 +344,7 @@ fn main() {
}
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -29,10 +29,12 @@ use std::sync::Arc;
use vulkano::{
buffer::{BufferUsage, CpuBufferPool},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DrawIndirectCommand, RenderPassBeginInfo,
SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
DrawIndirectCommand, RenderPassBeginInfo, SubpassContents,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -315,6 +317,9 @@ fn main() {
let mut recreate_swapchain = false;
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
@ -390,6 +395,7 @@ fn main() {
// Pass the two buffers to the compute shader
let layout = compute_pipeline.layout().set_layouts().get(0).unwrap();
let cs_desciptor_set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
layout.clone(),
[
WriteDescriptorSet::buffer(0, vertices.clone()),
@ -399,7 +405,7 @@ fn main() {
.unwrap();
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -17,7 +17,8 @@ use std::sync::Arc;
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
@ -314,6 +315,8 @@ fn main() {
let mut recreate_swapchain = false;
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
@ -371,7 +374,7 @@ fn main() {
}
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -10,8 +10,10 @@
use crate::fractal_compute_pipeline::FractalComputePipeline;
use crate::place_over_frame::RenderPassPlaceOverFrame;
use cgmath::Vector2;
use std::sync::Arc;
use std::time::Instant;
use std::{rc::Rc, sync::Arc};
use vulkano::command_buffer::allocator::StandardCommandBufferAllocator;
use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
use vulkano::device::Queue;
use vulkano::sync::GpuFuture;
use vulkano_util::renderer::{DeviceImageView, VulkanoWindowRenderer};
@ -58,9 +60,25 @@ pub struct FractalApp {
impl FractalApp {
pub fn new(gfx_queue: Arc<Queue>, image_format: vulkano::format::Format) -> FractalApp {
let command_buffer_allocator = Rc::new(StandardCommandBufferAllocator::new(
gfx_queue.device().clone(),
));
let descriptor_set_allocator = Rc::new(StandardDescriptorSetAllocator::new(
gfx_queue.device().clone(),
));
FractalApp {
fractal_pipeline: FractalComputePipeline::new(gfx_queue.clone()),
place_over_frame: RenderPassPlaceOverFrame::new(gfx_queue, image_format),
fractal_pipeline: FractalComputePipeline::new(
gfx_queue.clone(),
command_buffer_allocator.clone(),
descriptor_set_allocator.clone(),
),
place_over_frame: RenderPassPlaceOverFrame::new(
gfx_queue,
command_buffer_allocator,
descriptor_set_allocator,
image_format,
),
is_julia: false,
is_c_paused: false,
c: Vector2::new(0.0, 0.0),

View File

@ -9,11 +9,16 @@
use cgmath::Vector2;
use rand::Rng;
use std::sync::Arc;
use std::{rc::Rc, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer},
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBuffer},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
command_buffer::{
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
PrimaryCommandBuffer,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
device::Queue,
image::ImageAccess,
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
@ -24,13 +29,19 @@ use vulkano_util::renderer::DeviceImageView;
pub struct FractalComputePipeline {
queue: Arc<Queue>,
pipeline: Arc<ComputePipeline>,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
palette: Arc<CpuAccessibleBuffer<[[f32; 4]]>>,
palette_size: i32,
end_color: [f32; 4],
}
impl FractalComputePipeline {
pub fn new(queue: Arc<Queue>) -> FractalComputePipeline {
pub fn new(
queue: Arc<Queue>,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
) -> FractalComputePipeline {
// Initial colors
let colors = vec![
[1.0, 0.0, 0.0, 1.0],
@ -64,9 +75,12 @@ impl FractalComputePipeline {
)
.unwrap()
};
FractalComputePipeline {
queue,
pipeline,
command_buffer_allocator,
descriptor_set_allocator,
palette,
palette_size,
end_color,
@ -96,7 +110,7 @@ impl FractalComputePipeline {
}
pub fn compute(
&mut self,
&self,
image: DeviceImageView,
c: Vector2<f32>,
scale: Vector2<f32>,
@ -109,6 +123,7 @@ impl FractalComputePipeline {
let pipeline_layout = self.pipeline.layout();
let desc_layout = pipeline_layout.set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
&*self.descriptor_set_allocator,
desc_layout.clone(),
[
WriteDescriptorSet::image_view(0, image),
@ -117,7 +132,7 @@ impl FractalComputePipeline {
)
.unwrap();
let mut builder = AutoCommandBufferBuilder::primary(
self.queue.device().clone(),
&*self.command_buffer_allocator,
self.queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -8,14 +8,16 @@
// according to those terms.
use bytemuck::{Pod, Zeroable};
use std::sync::Arc;
use std::{rc::Rc, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferInheritanceInfo, CommandBufferUsage,
SecondaryAutoCommandBuffer,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder,
CommandBufferInheritanceInfo, CommandBufferUsage, SecondaryAutoCommandBuffer,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
device::Queue,
image::ImageViewAbstract,
impl_vertex,
@ -69,12 +71,19 @@ pub struct PixelsDrawPipeline {
gfx_queue: Arc<Queue>,
subpass: Subpass,
pipeline: Arc<GraphicsPipeline>,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
vertices: Arc<CpuAccessibleBuffer<[TexturedVertex]>>,
indices: Arc<CpuAccessibleBuffer<[u32]>>,
}
impl PixelsDrawPipeline {
pub fn new(gfx_queue: Arc<Queue>, subpass: Subpass) -> PixelsDrawPipeline {
pub fn new(
gfx_queue: Arc<Queue>,
subpass: Subpass,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
) -> PixelsDrawPipeline {
let (vertices, indices) = textured_quad(2.0, 2.0);
let vertex_buffer = CpuAccessibleBuffer::<[TexturedVertex]>::from_iter(
gfx_queue.device().clone(),
@ -110,10 +119,13 @@ impl PixelsDrawPipeline {
.build(gfx_queue.device().clone())
.unwrap()
};
PixelsDrawPipeline {
gfx_queue,
subpass,
pipeline,
command_buffer_allocator,
descriptor_set_allocator,
vertices: vertex_buffer,
indices: index_buffer,
}
@ -137,6 +149,7 @@ impl PixelsDrawPipeline {
.unwrap();
PersistentDescriptorSet::new(
&*self.descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::image_view_sampler(
0,
@ -154,7 +167,7 @@ impl PixelsDrawPipeline {
image: Arc<dyn ImageViewAbstract>,
) -> SecondaryAutoCommandBuffer {
let mut builder = AutoCommandBufferBuilder::secondary(
self.gfx_queue.device().clone(),
&*self.command_buffer_allocator,
self.gfx_queue.queue_family_index(),
CommandBufferUsage::MultipleSubmit,
CommandBufferInheritanceInfo {

View File

@ -8,11 +8,13 @@
// according to those terms.
use crate::pixels_draw_pipeline::PixelsDrawPipeline;
use std::sync::Arc;
use std::{rc::Rc, sync::Arc};
use vulkano::{
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
descriptor_set::allocator::StandardDescriptorSetAllocator,
device::Queue,
format::Format,
image::ImageAccess,
@ -26,10 +28,16 @@ pub struct RenderPassPlaceOverFrame {
gfx_queue: Arc<Queue>,
render_pass: Arc<RenderPass>,
pixels_draw_pipeline: PixelsDrawPipeline,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
}
impl RenderPassPlaceOverFrame {
pub fn new(gfx_queue: Arc<Queue>, output_format: Format) -> RenderPassPlaceOverFrame {
pub fn new(
gfx_queue: Arc<Queue>,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
output_format: Format,
) -> RenderPassPlaceOverFrame {
let render_pass = vulkano::single_pass_renderpass!(gfx_queue.device().clone(),
attachments: {
color: {
@ -46,11 +54,18 @@ impl RenderPassPlaceOverFrame {
)
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pixels_draw_pipeline = PixelsDrawPipeline::new(gfx_queue.clone(), subpass);
let pixels_draw_pipeline = PixelsDrawPipeline::new(
gfx_queue.clone(),
subpass,
command_buffer_allocator.clone(),
descriptor_set_allocator,
);
RenderPassPlaceOverFrame {
gfx_queue,
render_pass,
pixels_draw_pipeline,
command_buffer_allocator,
}
}
@ -78,7 +93,7 @@ impl RenderPassPlaceOverFrame {
.unwrap();
// Create primary command buffer builder
let mut command_buffer_builder = AutoCommandBufferBuilder::primary(
self.gfx_queue.device().clone(),
&*self.command_buffer_allocator,
self.gfx_queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -69,8 +69,8 @@ use std::{fs::File, io::BufWriter, path::Path};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, CopyImageToBufferInfo, PrimaryCommandBuffer,
RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
CopyImageToBufferInfo, PrimaryCommandBuffer, RenderPassBeginInfo, SubpassContents,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
@ -314,8 +314,10 @@ fn main() {
depth_range: 0.0..1.0,
};
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let buf = CpuAccessibleBuffer::from_iter(
device.clone(),
device,
BufferUsage {
transfer_dst: true,
..BufferUsage::empty()
@ -326,7 +328,7 @@ fn main() {
.unwrap();
let mut builder = AutoCommandBufferBuilder::primary(
device,
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -21,7 +21,8 @@ use std::{collections::HashMap, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
@ -272,6 +273,8 @@ fn main() {
depth_range: 0.0..1.0,
};
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
window_surfaces.insert(
window_id,
WindowSurface {
@ -415,7 +418,7 @@ fn main() {
}
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -11,7 +11,9 @@ use crate::{
game_of_life::GameOfLifeComputePipeline, render_pass::RenderPassPlaceOverFrame, SCALING,
WINDOW2_HEIGHT, WINDOW2_WIDTH, WINDOW_HEIGHT, WINDOW_WIDTH,
};
use std::{collections::HashMap, sync::Arc};
use std::{collections::HashMap, rc::Rc, sync::Arc};
use vulkano::command_buffer::allocator::StandardCommandBufferAllocator;
use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
use vulkano::{device::Queue, format::Format};
use vulkano_util::context::{VulkanoConfig, VulkanoContext};
use vulkano_util::window::{VulkanoWindows, WindowDescriptor};
@ -29,9 +31,26 @@ impl RenderPipeline {
size: [u32; 2],
swapchain_format: Format,
) -> RenderPipeline {
let command_buffer_allocator = Rc::new(StandardCommandBufferAllocator::new(
gfx_queue.device().clone(),
));
let descriptor_set_allocator = Rc::new(StandardDescriptorSetAllocator::new(
gfx_queue.device().clone(),
));
RenderPipeline {
compute: GameOfLifeComputePipeline::new(compute_queue, size),
place_over_frame: RenderPassPlaceOverFrame::new(gfx_queue, swapchain_format),
compute: GameOfLifeComputePipeline::new(
compute_queue,
command_buffer_allocator.clone(),
descriptor_set_allocator.clone(),
size,
),
place_over_frame: RenderPassPlaceOverFrame::new(
gfx_queue,
command_buffer_allocator,
descriptor_set_allocator,
swapchain_format,
),
}
}
}

View File

@ -9,7 +9,9 @@
use cgmath::Vector2;
use rand::Rng;
use std::sync::Arc;
use std::{rc::Rc, sync::Arc};
use vulkano::command_buffer::allocator::StandardCommandBufferAllocator;
use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
use vulkano::image::{ImageUsage, StorageImage};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer},
@ -31,6 +33,8 @@ use vulkano_util::renderer::DeviceImageView;
pub struct GameOfLifeComputePipeline {
compute_queue: Arc<Queue>,
compute_life_pipeline: Arc<ComputePipeline>,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
life_in: Arc<CpuAccessibleBuffer<[u32]>>,
life_out: Arc<CpuAccessibleBuffer<[u32]>>,
image: DeviceImageView,
@ -52,7 +56,12 @@ fn rand_grid(compute_queue: &Arc<Queue>, size: [u32; 2]) -> Arc<CpuAccessibleBuf
}
impl GameOfLifeComputePipeline {
pub fn new(compute_queue: Arc<Queue>, size: [u32; 2]) -> GameOfLifeComputePipeline {
pub fn new(
compute_queue: Arc<Queue>,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
size: [u32; 2],
) -> GameOfLifeComputePipeline {
let life_in = rand_grid(&compute_queue, size);
let life_out = rand_grid(&compute_queue, size);
@ -81,9 +90,12 @@ impl GameOfLifeComputePipeline {
},
)
.unwrap();
GameOfLifeComputePipeline {
compute_queue,
compute_life_pipeline,
command_buffer_allocator,
descriptor_set_allocator,
life_in,
life_out,
image,
@ -94,7 +106,7 @@ impl GameOfLifeComputePipeline {
self.image.clone()
}
pub fn draw_life(&mut self, pos: Vector2<i32>) {
pub fn draw_life(&self, pos: Vector2<i32>) {
let mut life_in = self.life_in.write().unwrap();
let size = self.image.image().dimensions().width_height();
if pos.y < 0 || pos.y >= size[1] as i32 || pos.x < 0 || pos.x >= size[0] as i32 {
@ -111,7 +123,7 @@ impl GameOfLifeComputePipeline {
dead_color: [f32; 4],
) -> Box<dyn GpuFuture> {
let mut builder = AutoCommandBufferBuilder::primary(
self.compute_queue.device().clone(),
&*self.command_buffer_allocator,
self.compute_queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)
@ -140,7 +152,7 @@ impl GameOfLifeComputePipeline {
/// Build the command for a dispatch.
fn dispatch(
&mut self,
&self,
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
life_color: [f32; 4],
dead_color: [f32; 4],
@ -152,6 +164,7 @@ impl GameOfLifeComputePipeline {
let pipeline_layout = self.compute_life_pipeline.layout();
let desc_layout = pipeline_layout.set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
&*self.descriptor_set_allocator,
desc_layout.clone(),
[
WriteDescriptorSet::image_view(0, self.image.clone()),

View File

@ -8,14 +8,16 @@
// according to those terms.
use bytemuck::{Pod, Zeroable};
use std::sync::Arc;
use std::{rc::Rc, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferInheritanceInfo, CommandBufferUsage,
SecondaryAutoCommandBuffer,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder,
CommandBufferInheritanceInfo, CommandBufferUsage, SecondaryAutoCommandBuffer,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
device::Queue,
image::ImageViewAbstract,
impl_vertex,
@ -69,12 +71,19 @@ pub struct PixelsDrawPipeline {
gfx_queue: Arc<Queue>,
subpass: Subpass,
pipeline: Arc<GraphicsPipeline>,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
vertices: Arc<CpuAccessibleBuffer<[TexturedVertex]>>,
indices: Arc<CpuAccessibleBuffer<[u32]>>,
}
impl PixelsDrawPipeline {
pub fn new(gfx_queue: Arc<Queue>, subpass: Subpass) -> PixelsDrawPipeline {
pub fn new(
gfx_queue: Arc<Queue>,
subpass: Subpass,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
) -> PixelsDrawPipeline {
let (vertices, indices) = textured_quad(2.0, 2.0);
let vertex_buffer = CpuAccessibleBuffer::<[TexturedVertex]>::from_iter(
gfx_queue.device().clone(),
@ -110,10 +119,13 @@ impl PixelsDrawPipeline {
.build(gfx_queue.device().clone())
.unwrap()
};
PixelsDrawPipeline {
gfx_queue,
subpass,
pipeline,
command_buffer_allocator,
descriptor_set_allocator,
vertices: vertex_buffer,
indices: index_buffer,
}
@ -137,6 +149,7 @@ impl PixelsDrawPipeline {
.unwrap();
PersistentDescriptorSet::new(
&*self.descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::image_view_sampler(
0,
@ -149,12 +162,12 @@ impl PixelsDrawPipeline {
/// Draw input `image` over a quad of size -1.0 to 1.0
pub fn draw(
&mut self,
&self,
viewport_dimensions: [u32; 2],
image: Arc<dyn ImageViewAbstract>,
) -> SecondaryAutoCommandBuffer {
let mut builder = AutoCommandBufferBuilder::secondary(
self.gfx_queue.device().clone(),
&*self.command_buffer_allocator,
self.gfx_queue.queue_family_index(),
CommandBufferUsage::MultipleSubmit,
CommandBufferInheritanceInfo {

View File

@ -8,11 +8,13 @@
// according to those terms.
use crate::pixels_draw::PixelsDrawPipeline;
use std::sync::Arc;
use std::{rc::Rc, sync::Arc};
use vulkano::{
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
descriptor_set::allocator::StandardDescriptorSetAllocator,
device::Queue,
format::Format,
image::ImageAccess,
@ -26,10 +28,16 @@ pub struct RenderPassPlaceOverFrame {
gfx_queue: Arc<Queue>,
render_pass: Arc<RenderPass>,
pixels_draw_pipeline: PixelsDrawPipeline,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
}
impl RenderPassPlaceOverFrame {
pub fn new(gfx_queue: Arc<Queue>, output_format: Format) -> RenderPassPlaceOverFrame {
pub fn new(
gfx_queue: Arc<Queue>,
command_buffer_allocator: Rc<StandardCommandBufferAllocator>,
descriptor_set_allocator: Rc<StandardDescriptorSetAllocator>,
output_format: Format,
) -> RenderPassPlaceOverFrame {
let render_pass = vulkano::single_pass_renderpass!(gfx_queue.device().clone(),
attachments: {
color: {
@ -46,18 +54,25 @@ impl RenderPassPlaceOverFrame {
)
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pixels_draw_pipeline = PixelsDrawPipeline::new(gfx_queue.clone(), subpass);
let pixels_draw_pipeline = PixelsDrawPipeline::new(
gfx_queue.clone(),
subpass,
command_buffer_allocator.clone(),
descriptor_set_allocator,
);
RenderPassPlaceOverFrame {
gfx_queue,
render_pass,
pixels_draw_pipeline,
command_buffer_allocator,
}
}
/// Place view exactly over swapchain image target.
/// Texture draw pipeline uses a quad onto which it places the view.
pub fn render<F>(
&mut self,
&self,
before_future: F,
view: DeviceImageView,
target: SwapchainImageView,
@ -78,7 +93,7 @@ impl RenderPassPlaceOverFrame {
.unwrap();
// Create primary command buffer builder
let mut command_buffer_builder = AutoCommandBufferBuilder::primary(
self.gfx_queue.device().clone(),
&*self.command_buffer_allocator,
self.gfx_queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -18,8 +18,8 @@ use std::{fs::File, io::BufWriter, path::Path, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, BufferImageCopy, CommandBufferUsage, CopyImageToBufferInfo,
RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, BufferImageCopy,
CommandBufferUsage, CopyImageToBufferInfo, RenderPassBeginInfo, SubpassContents,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Features,
@ -277,6 +277,8 @@ fn main() {
.build(device.clone())
.unwrap();
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let create_buffer = || {
CpuAccessibleBuffer::from_iter(
device.clone(),
@ -294,7 +296,7 @@ fn main() {
let buffer2 = create_buffer();
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -16,7 +16,8 @@ use std::sync::Arc;
use vulkano::{
buffer::{BufferAccess, BufferUsage, CpuAccessibleBuffer},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceOwned,
@ -321,6 +322,8 @@ fn main() {
depth_range: 0.0..1.0,
};
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
let mut recreate_swapchain = false;
@ -378,7 +381,7 @@ fn main() {
}
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -14,8 +14,12 @@
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer},
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
command_buffer::{
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -125,6 +129,9 @@ fn main() {
)
.unwrap();
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let data_buffer = {
let data_iter = 0..65536u32;
CpuAccessibleBuffer::from_iter(
@ -141,6 +148,7 @@ fn main() {
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::buffer(0, data_buffer.clone())],
)
@ -159,7 +167,7 @@ fn main() {
// Note that there is no type safety for the push constants argument.
// So be careful to only pass an instance of the struct generated by the `vulkano_shaders::shaders!` macro.
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -12,7 +12,8 @@ use std::{io::Cursor, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
descriptor_set::WriteDescriptorSet,
device::{
@ -203,6 +204,8 @@ fn main() {
)
.unwrap();
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let (texture, tex_future) = {
let png_bytes = include_bytes!("image_img.png").to_vec();
let cursor = Cursor::new(png_bytes);
@ -223,6 +226,7 @@ fn main() {
dimensions,
MipmapsCount::One,
Format::R8G8B8A8_SRGB,
&command_buffer_allocator,
queue.clone(),
)
.unwrap();
@ -319,7 +323,7 @@ fn main() {
}
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -24,7 +24,8 @@ use std::{fs::File, io::Read, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
@ -256,6 +257,8 @@ fn main() {
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
event_loop.run(move |event, _, control_flow| match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
@ -308,7 +311,7 @@ fn main() {
}
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::MultipleSubmit,
)

View File

@ -12,9 +12,11 @@ use std::{io::Cursor, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator,
layout::{
DescriptorSetLayout, DescriptorSetLayoutCreateInfo, DescriptorSetLayoutCreationError,
},
@ -268,6 +270,9 @@ fn main() {
)
.unwrap();
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let mascot_texture = {
let png_bytes = include_bytes!("rust_mascot.png").to_vec();
let cursor = Cursor::new(png_bytes);
@ -288,6 +293,7 @@ fn main() {
dimensions,
MipmapsCount::One,
Format::R8G8B8A8_SRGB,
&command_buffer_allocator,
queue.clone(),
)
.unwrap()
@ -316,6 +322,7 @@ fn main() {
dimensions,
MipmapsCount::One,
Format::R8G8B8A8_SRGB,
&command_buffer_allocator,
queue.clone(),
)
.unwrap()
@ -381,6 +388,7 @@ fn main() {
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new_variable(
&descriptor_set_allocator,
layout.clone(),
2,
[WriteDescriptorSet::image_view_sampler_array(
@ -457,7 +465,7 @@ fn main() {
}
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -13,9 +13,12 @@
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer},
command_buffer::{
AutoCommandBufferBuilder, BufferCopy, CommandBufferUsage, CopyBufferInfoTyped,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, BufferCopy,
CommandBufferUsage, CopyBufferInfoTyped,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -113,6 +116,9 @@ fn main() {
.unwrap()
};
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let data_buffer = {
// we intitialize half of the array and leave the other half to 0, we will use copy later to fill it
let data_iter = (0..65536u32).map(|n| if n < 65536 / 2 { n } else { 0 });
@ -132,13 +138,14 @@ fn main() {
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::buffer(0, data_buffer.clone())],
)
.unwrap();
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -13,8 +13,12 @@
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer},
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
command_buffer::{
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -120,6 +124,9 @@ fn main() {
.unwrap()
};
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let data_buffer = {
let data_iter = 0..65536u32;
CpuAccessibleBuffer::from_iter(
@ -136,13 +143,14 @@ fn main() {
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::buffer(0, data_buffer.clone())],
)
.unwrap();
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -30,8 +30,12 @@
use std::sync::Arc;
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer},
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
command_buffer::{
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueCreateInfo,
@ -194,16 +198,19 @@ fn main() {
queue: Arc<Queue>,
data_buffer: Arc<CpuAccessibleBuffer<[u32]>>,
parameters: shaders::ty::Parameters,
command_buffer_allocator: &StandardCommandBufferAllocator,
descriptor_set_allocator: &mut StandardDescriptorSetAllocator,
) {
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::buffer(0, data_buffer)],
)
.unwrap();
let mut builder = AutoCommandBufferBuilder::primary(
queue.device().clone(),
command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)
@ -230,6 +237,9 @@ fn main() {
future.wait(None).unwrap();
}
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let mut descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
// Preparing test data array `[0, 1, 2, 3....]`
let data_buffer = {
let data_iter = 0..65536u32;
@ -277,6 +287,8 @@ fn main() {
queue.clone(),
data_buffer.clone(),
shaders::ty::Parameters { value: 2 },
&command_buffer_allocator,
&mut descriptor_set_allocator,
);
// Then add 1 to each value
@ -285,6 +297,8 @@ fn main() {
queue.clone(),
data_buffer.clone(),
shaders::ty::Parameters { value: 1 },
&command_buffer_allocator,
&mut descriptor_set_allocator,
);
// Then multiply each value by 3
@ -293,6 +307,8 @@ fn main() {
queue,
data_buffer.clone(),
shaders::ty::Parameters { value: 3 },
&command_buffer_allocator,
&mut descriptor_set_allocator,
);
let data_buffer_content = data_buffer.read().unwrap();

View File

@ -18,10 +18,12 @@ use std::{sync::Arc, time::SystemTime};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, DeviceLocalBuffer},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, CopyBufferInfo, PrimaryCommandBuffer,
RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
CopyBufferInfo, PrimaryCommandBuffer, RenderPassBeginInfo, SubpassContents,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -317,6 +319,9 @@ fn main() {
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
struct Vertex {
@ -366,7 +371,7 @@ fn main() {
// Create one-time command to copy between the buffers.
let mut cbb = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)
@ -402,6 +407,7 @@ fn main() {
// Create a new descriptor set for binding vertices as a Storage Buffer.
use vulkano::pipeline::Pipeline; // Required to access layout() method of pipeline.
let descriptor_set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
compute_pipeline
.layout()
.set_layouts()
@ -497,7 +503,7 @@ fn main() {
};
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -11,8 +11,12 @@
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer},
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
command_buffer::{
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -121,6 +125,9 @@ fn main() {
)
.unwrap();
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let data_buffer = {
let data_iter = 0..65536u32;
CpuAccessibleBuffer::from_iter(
@ -137,13 +144,14 @@ fn main() {
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::buffer(0, data_buffer.clone())],
)
.unwrap();
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -13,9 +13,12 @@ use std::{sync::Arc, time::Instant};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, CpuBufferPool, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -223,6 +226,9 @@ fn main() {
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
let rotation_start = Instant::now();
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
@ -303,6 +309,7 @@ fn main() {
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)],
)
@ -323,7 +330,7 @@ fn main() {
}
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -23,7 +23,8 @@ use std::sync::Arc;
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Features,
@ -363,6 +364,8 @@ fn main() {
};
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
event_loop.run(move |event, _, control_flow| match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
@ -415,7 +418,7 @@ fn main() {
}
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -12,9 +12,12 @@ use std::{io::Cursor, sync::Arc};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
},
@ -207,6 +210,9 @@ fn main() {
)
.unwrap();
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
let (texture, tex_future) = {
let image_array_data: Vec<_> = vec![
include_bytes!("square.png").to_vec(),
@ -235,6 +241,7 @@ fn main() {
dimensions,
MipmapsCount::Log2,
Format::R8G8B8A8_SRGB,
&command_buffer_allocator,
queue.clone(),
)
.unwrap();
@ -257,6 +264,7 @@ fn main() {
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
&descriptor_set_allocator,
layout.clone(),
[WriteDescriptorSet::image_view_sampler(0, texture, sampler)],
)
@ -324,7 +332,7 @@ fn main() {
}
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -26,7 +26,8 @@ use std::sync::Arc;
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderingAttachmentInfo, RenderingInfo,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Features,
@ -405,6 +406,14 @@ fn main() {
// each image.
let mut attachment_image_views = window_size_dependent_setup(&images, &mut viewport);
// Before we can start creating and recording command buffers, we need a way of allocating
// them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools
// underneath and provides a safe interface for them.
//
// A Vulkan command pool only works for one queue family, and vulkano's command buffer allocator
// reflects that, therefore we need to pass the queue family during creation.
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
// Initialization is finally finished!
// In some situations, the swapchain will become invalid by itself. This includes for example
@ -506,7 +515,7 @@ fn main() {
// 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 mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -21,7 +21,8 @@ use std::sync::Arc;
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents,
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
RenderPassBeginInfo, SubpassContents,
},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo,
@ -412,6 +413,14 @@ fn main() {
// each image.
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
// Before we can start creating and recording command buffers, we need a way of allocating
// them. Vulkano provides a command buffer allocator, which manages raw Vulkan command pools
// underneath and provides a safe interface for them.
//
// A Vulkan command pool only works for one queue family, and vulkano's command buffer allocator
// reflects that, therefore we need to pass the queue family during creation.
let command_buffer_allocator = StandardCommandBufferAllocator::new(device.clone());
// Initialization is finally finished!
// In some situations, the swapchain will become invalid by itself. This includes for example
@ -523,7 +532,7 @@ fn main() {
// 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 mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -67,6 +67,7 @@ use std::{
/// use vulkano::sync::GpuFuture;
/// # let device: std::sync::Arc<vulkano::device::Device> = return;
/// # let queue: std::sync::Arc<vulkano::device::Queue> = return;
/// # let command_buffer_allocator: vulkano::command_buffer::allocator::StandardCommandBufferAllocator = return;
///
/// // Create the ring buffer.
/// let buffer = CpuBufferPool::upload(device.clone());
@ -77,7 +78,11 @@ use std::{
/// let sub_buffer = buffer.from_data(data).unwrap();
///
/// // You can then use `sub_buffer` as if it was an entirely separate buffer.
/// AutoCommandBufferBuilder::primary(device.clone(), queue.queue_family_index(), CommandBufferUsage::OneTimeSubmit)
/// AutoCommandBufferBuilder::primary(
/// &command_buffer_allocator,
/// queue.queue_family_index(),
/// CommandBufferUsage::OneTimeSubmit,
/// )
/// .unwrap()
/// // For the sake of the example we just call `update_buffer` on the buffer, even though
/// // it is pointless to do that.
@ -90,7 +95,6 @@ use std::{
/// .unwrap();
/// }
/// ```
///
pub struct CpuBufferPool<T, A = Arc<StandardMemoryPool>>
where
[T]: BufferContents,

View File

@ -21,8 +21,8 @@ use super::{
};
use crate::{
command_buffer::{
AutoCommandBufferBuilder, CommandBufferBeginError, CommandBufferExecFuture,
CommandBufferUsage, CopyBufferInfo, PrimaryCommandBuffer,
allocator::CommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferBeginError,
CommandBufferExecFuture, CommandBufferUsage, CopyBufferInfo, PrimaryCommandBuffer,
},
device::{Device, DeviceOwned, Queue},
memory::{
@ -79,6 +79,7 @@ use std::{
/// use vulkano::sync::GpuFuture;
/// # let device: std::sync::Arc<vulkano::device::Device> = return;
/// # let queue: std::sync::Arc<vulkano::device::Queue> = return;
/// # let command_buffer_allocator: vulkano::command_buffer::allocator::StandardCommandBufferAllocator = return;
///
/// // Simple iterator to construct test data.
/// let data = (0..10_000).map(|i| i as f32);
@ -107,7 +108,7 @@ use std::{
///
/// // Create a one-time command to copy between the buffers.
/// let mut cbb = AutoCommandBufferBuilder::primary(
/// device.clone(),
/// &command_buffer_allocator,
/// queue.queue_family_index(),
/// CommandBufferUsage::OneTimeSubmit,
/// )
@ -170,9 +171,6 @@ where
}
}
// TODO: make this prettier
type DeviceLocalBufferFromBufferFuture = CommandBufferExecFuture<NowFuture>;
impl<T> DeviceLocalBuffer<T>
where
T: BufferContents + ?Sized,
@ -186,9 +184,13 @@ where
pub fn from_buffer<B>(
source: Arc<B>,
usage: BufferUsage,
command_buffer_allocator: &impl CommandBufferAllocator,
queue: Arc<Queue>,
) -> Result<
(Arc<DeviceLocalBuffer<T>>, DeviceLocalBufferFromBufferFuture),
(
Arc<DeviceLocalBuffer<T>>,
CommandBufferExecFuture<NowFuture>,
),
DeviceLocalBufferCreationError,
>
where
@ -213,7 +215,7 @@ where
)?;
let mut cbb = AutoCommandBufferBuilder::primary(
source.device().clone(),
command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::MultipleSubmit,
)?;
@ -252,9 +254,13 @@ where
pub fn from_data(
data: T,
usage: BufferUsage,
command_buffer_allocator: &impl CommandBufferAllocator,
queue: Arc<Queue>,
) -> Result<
(Arc<DeviceLocalBuffer<T>>, DeviceLocalBufferFromBufferFuture),
(
Arc<DeviceLocalBuffer<T>>,
CommandBufferExecFuture<NowFuture>,
),
DeviceLocalBufferCreationError,
> {
let source = CpuAccessibleBuffer::from_data(
@ -266,7 +272,7 @@ where
false,
data,
)?;
DeviceLocalBuffer::from_buffer(source, usage, queue)
DeviceLocalBuffer::from_buffer(source, usage, command_buffer_allocator, queue)
}
}
@ -281,11 +287,12 @@ where
pub fn from_iter<D>(
data: D,
usage: BufferUsage,
command_buffer_allocator: &impl CommandBufferAllocator,
queue: Arc<Queue>,
) -> Result<
(
Arc<DeviceLocalBuffer<[T]>>,
DeviceLocalBufferFromBufferFuture,
CommandBufferExecFuture<NowFuture>,
),
DeviceLocalBufferCreationError,
>
@ -302,7 +309,7 @@ where
false,
data,
)?;
DeviceLocalBuffer::from_buffer(source, usage, queue)
DeviceLocalBuffer::from_buffer(source, usage, command_buffer_allocator, queue)
}
}
@ -587,24 +594,27 @@ impl From<CommandBufferBeginError> for DeviceLocalBufferCreationError {
#[cfg(test)]
mod tests {
use super::*;
use crate::sync::GpuFuture;
use crate::{command_buffer::allocator::StandardCommandBufferAllocator, sync::GpuFuture};
#[test]
fn from_data_working() {
let (device, queue) = gfx_dev_and_queue!();
let cb_allocator = StandardCommandBufferAllocator::new(device.clone());
let (buffer, _) = DeviceLocalBuffer::from_data(
12u32,
BufferUsage {
transfer_src: true,
..BufferUsage::empty()
},
&cb_allocator,
queue.clone(),
)
.unwrap();
let destination = CpuAccessibleBuffer::from_data(
device.clone(),
device,
BufferUsage {
transfer_dst: true,
..BufferUsage::empty()
@ -615,7 +625,7 @@ mod tests {
.unwrap();
let mut cbb = AutoCommandBufferBuilder::primary(
device,
&cb_allocator,
queue.queue_family_index(),
CommandBufferUsage::MultipleSubmit,
)
@ -638,18 +648,21 @@ mod tests {
fn from_iter_working() {
let (device, queue) = gfx_dev_and_queue!();
let cb_allocator = StandardCommandBufferAllocator::new(device.clone());
let (buffer, _) = DeviceLocalBuffer::from_iter(
(0..512u32).map(|n| n * 2),
BufferUsage {
transfer_src: true,
..BufferUsage::empty()
},
&cb_allocator,
queue.clone(),
)
.unwrap();
let destination = CpuAccessibleBuffer::from_iter(
device.clone(),
device,
BufferUsage {
transfer_dst: true,
..BufferUsage::empty()
@ -660,7 +673,7 @@ mod tests {
.unwrap();
let mut cbb = AutoCommandBufferBuilder::primary(
device,
&cb_allocator,
queue.queue_family_index(),
CommandBufferUsage::MultipleSubmit,
)
@ -686,6 +699,8 @@ mod tests {
fn create_buffer_zero_size_data() {
let (device, queue) = gfx_dev_and_queue!();
let cb_allocator = StandardCommandBufferAllocator::new(device);
assert_should_panic!({
DeviceLocalBuffer::from_data(
(),
@ -693,6 +708,7 @@ mod tests {
transfer_dst: true,
..BufferUsage::empty()
},
&cb_allocator,
queue.clone(),
)
.unwrap();

View File

@ -26,13 +26,18 @@
//!
//! # let device: Arc<vulkano::device::Device> = return;
//! # let queue: Arc<vulkano::device::Queue> = return;
//! # let command_buffer_allocator: vulkano::command_buffer::allocator::StandardCommandBufferAllocator = return;
//! let usage = BufferUsage {
//! storage_texel_buffer: true,
//! ..BufferUsage::empty()
//! };
//!
//! let (buffer, _future) = DeviceLocalBuffer::<[u32]>::from_iter((0..128).map(|n| n), usage,
//! queue.clone()).unwrap();
//! let (buffer, _future) = DeviceLocalBuffer::<[u32]>::from_iter(
//! (0..128).map(|n| n),
//! usage,
//! &command_buffer_allocator,
//! queue.clone()
//! ).unwrap();
//! let _view = BufferView::new(
//! buffer,
//! BufferViewCreateInfo {
@ -478,21 +483,28 @@ mod tests {
view::{BufferView, BufferViewCreateInfo, BufferViewCreationError},
BufferUsage, DeviceLocalBuffer,
},
command_buffer::allocator::StandardCommandBufferAllocator,
format::Format,
};
#[test]
fn create_uniform() {
// `VK_FORMAT_R8G8B8A8_UNORM` guaranteed to be a supported format
let (_device, queue) = gfx_dev_and_queue!();
let (device, queue) = gfx_dev_and_queue!();
let usage = BufferUsage {
uniform_texel_buffer: true,
..BufferUsage::empty()
};
let (buffer, _) =
DeviceLocalBuffer::<[[u8; 4]]>::from_iter((0..128).map(|_| [0; 4]), usage, queue)
let cb_allocator = StandardCommandBufferAllocator::new(device);
let (buffer, _) = DeviceLocalBuffer::<[[u8; 4]]>::from_iter(
(0..128).map(|_| [0; 4]),
usage,
&cb_allocator,
queue,
)
.unwrap();
BufferView::new(
buffer,
@ -507,15 +519,21 @@ mod tests {
#[test]
fn create_storage() {
// `VK_FORMAT_R8G8B8A8_UNORM` guaranteed to be a supported format
let (_device, queue) = gfx_dev_and_queue!();
let (device, queue) = gfx_dev_and_queue!();
let usage = BufferUsage {
storage_texel_buffer: true,
..BufferUsage::empty()
};
let (buffer, _) =
DeviceLocalBuffer::<[[u8; 4]]>::from_iter((0..128).map(|_| [0; 4]), usage, queue)
let cb_allocator = StandardCommandBufferAllocator::new(device);
let (buffer, _) = DeviceLocalBuffer::<[[u8; 4]]>::from_iter(
(0..128).map(|_| [0; 4]),
usage,
&cb_allocator,
queue,
)
.unwrap();
BufferView::new(
buffer,
@ -530,15 +548,18 @@ mod tests {
#[test]
fn create_storage_atomic() {
// `VK_FORMAT_R32_UINT` guaranteed to be a supported format for atomics
let (_device, queue) = gfx_dev_and_queue!();
let (device, queue) = gfx_dev_and_queue!();
let usage = BufferUsage {
storage_texel_buffer: true,
..BufferUsage::empty()
};
let cb_allocator = StandardCommandBufferAllocator::new(device);
let (buffer, _) =
DeviceLocalBuffer::<[u32]>::from_iter((0..128).map(|_| 0), usage, queue).unwrap();
DeviceLocalBuffer::<[u32]>::from_iter((0..128).map(|_| 0), usage, &cb_allocator, queue)
.unwrap();
BufferView::new(
buffer,
BufferViewCreateInfo {
@ -552,11 +573,14 @@ mod tests {
#[test]
fn wrong_usage() {
// `VK_FORMAT_R8G8B8A8_UNORM` guaranteed to be a supported format
let (_device, queue) = gfx_dev_and_queue!();
let (device, queue) = gfx_dev_and_queue!();
let cb_allocator = StandardCommandBufferAllocator::new(device);
let (buffer, _) = DeviceLocalBuffer::<[[u8; 4]]>::from_iter(
(0..128).map(|_| [0; 4]),
BufferUsage::empty(),
&cb_allocator,
queue,
)
.unwrap();
@ -575,7 +599,7 @@ mod tests {
#[test]
fn unsupported_format() {
let (_device, queue) = gfx_dev_and_queue!();
let (device, queue) = gfx_dev_and_queue!();
let usage = BufferUsage {
uniform_texel_buffer: true,
@ -583,8 +607,14 @@ mod tests {
..BufferUsage::empty()
};
let (buffer, _) =
DeviceLocalBuffer::<[[f64; 4]]>::from_iter((0..128).map(|_| [0.0; 4]), usage, queue)
let cb_allocator = StandardCommandBufferAllocator::new(device);
let (buffer, _) = DeviceLocalBuffer::<[[f64; 4]]>::from_iter(
(0..128).map(|_| [0.0; 4]),
usage,
&cb_allocator,
queue,
)
.unwrap();
// TODO: what if R64G64B64A64_SFLOAT is supported?

View File

@ -0,0 +1,369 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// 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
//! [`CommandBufferAllocator`] trait, which you can implement yourself or use the vulkano-provided
//! [`StandardCommandBufferAllocator`].
use super::{
pool::{
CommandBufferAllocateInfo, CommandPool, CommandPoolAlloc, CommandPoolCreateInfo,
CommandPoolCreationError,
},
CommandBufferLevel,
};
use crate::{
device::{Device, DeviceOwned},
OomError,
};
use crossbeam_queue::SegQueue;
use smallvec::SmallVec;
use std::{cell::UnsafeCell, marker::PhantomData, mem::ManuallyDrop, sync::Arc, vec::IntoIter};
/// Types that manage the memory of command buffers.
///
/// # Safety
///
/// A Vulkan command pool must be externally synchronized as if it owned the command buffers that
/// were allocated from it. This includes allocating from the pool, freeing from the pool, resetting
/// the pool or individual command buffers, and most importantly recording commands to command
/// buffers. The implementation of `CommandBufferAllocator` is expected to manage this.
///
/// The destructors of the [`CommandBufferBuilderAlloc`] and the [`CommandBufferAlloc`] are expected
/// to free the command buffer, reset the command buffer, or add it to a pool so that it gets
/// reused. If the implementation frees or resets the command buffer, it must not forget that this
/// operation must be externally synchronized.
pub unsafe trait CommandBufferAllocator: DeviceOwned {
/// See [`allocate`](Self::allocate).
type Iter: Iterator<Item = Self::Builder>;
/// Represents a command buffer that has been allocated and that is currently being built.
type Builder: CommandBufferBuilderAlloc<Alloc = Self::Alloc>;
/// Represents a command buffer that has been allocated and that is pending execution or is
/// being executed.
type Alloc: CommandBufferAlloc;
/// Allocates command buffers.
///
/// Returns an iterator that contains the requested amount of allocated command buffers.
fn allocate(
&self,
queue_family_index: u32,
level: CommandBufferLevel,
command_buffer_count: u32,
) -> Result<Self::Iter, OomError>;
}
/// A command buffer allocated from a pool and that can be recorded.
///
/// # Safety
///
/// See [`CommandBufferAllocator`] for information about safety.
pub unsafe trait CommandBufferBuilderAlloc: DeviceOwned {
/// Return type of `into_alloc`.
type Alloc: CommandBufferAlloc;
/// Returns the internal object that contains the command buffer.
fn inner(&self) -> &CommandPoolAlloc;
/// Turns this builder into a command buffer that is pending execution.
fn into_alloc(self) -> Self::Alloc;
/// Returns the index of the queue family that the pool targets.
fn queue_family_index(&self) -> u32;
}
/// A command buffer allocated from a pool that has finished being recorded.
///
/// # Safety
///
/// See [`CommandBufferAllocator`] for information about safety.
pub unsafe trait CommandBufferAlloc: DeviceOwned + Send + Sync + 'static {
/// Returns the internal object that contains the command buffer.
fn inner(&self) -> &CommandPoolAlloc;
/// Returns the index of the queue family that the pool targets.
fn queue_family_index(&self) -> u32;
}
/// Standard implementation of a command buffer allocator.
///
/// A thread can have as many `StandardCommandBufferAllocator`s as needed, but they can't be shared
/// between threads. This is done so that there are no locks involved when creating command
/// buffers. You are encouraged to create one allocator per frame in flight per thread.
///
/// Command buffers can't be moved between threads during the building process, but finished command
/// buffers can. When a command buffer is dropped, it is returned back to the pool for reuse.
#[derive(Debug)]
pub struct StandardCommandBufferAllocator {
device: Arc<Device>,
/// Each queue family index points directly to its pool.
pools: SmallVec<[UnsafeCell<Option<Arc<Pool>>>; 8]>,
}
impl StandardCommandBufferAllocator {
/// Creates a new `StandardCommandBufferAllocator`.
#[inline]
pub fn new(device: Arc<Device>) -> Self {
let pools = device
.physical_device()
.queue_family_properties()
.iter()
.map(|_| UnsafeCell::new(None))
.collect();
StandardCommandBufferAllocator { device, pools }
}
}
unsafe impl CommandBufferAllocator for StandardCommandBufferAllocator {
type Iter = IntoIter<StandardCommandBufferBuilderAlloc>;
type Builder = StandardCommandBufferBuilderAlloc;
type Alloc = StandardCommandBufferAlloc;
/// # Panics
///
/// - Panics if the queue family index is not active on the device.
#[inline]
fn allocate(
&self,
queue_family_index: u32,
level: CommandBufferLevel,
command_buffer_count: u32,
) -> Result<Self::Iter, OomError> {
// VUID-vkCreateCommandPool-queueFamilyIndex-01937
assert!(self
.device
.active_queue_family_indices()
.contains(&queue_family_index));
let pool = unsafe { &mut *self.pools[queue_family_index as usize].get() };
if pool.is_none() {
*pool = Some(Pool::new(self.device.clone(), queue_family_index)?);
}
pool.as_ref().unwrap().allocate(level, command_buffer_count)
}
}
unsafe impl DeviceOwned for StandardCommandBufferAllocator {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
#[derive(Debug)]
struct Pool {
// The Vulkan pool specific to a device's queue family.
inner: CommandPool,
// List of existing primary command buffers that are available for reuse.
primary_pool: SegQueue<CommandPoolAlloc>,
// List of existing secondary command buffers that are available for reuse.
secondary_pool: SegQueue<CommandPoolAlloc>,
}
impl Pool {
fn new(device: Arc<Device>, queue_family_index: u32) -> Result<Arc<Self>, OomError> {
CommandPool::new(
device,
CommandPoolCreateInfo {
queue_family_index,
reset_command_buffer: true,
..Default::default()
},
)
.map(|inner| {
Arc::new(Pool {
inner,
primary_pool: Default::default(),
secondary_pool: Default::default(),
})
})
.map_err(|err| match err {
CommandPoolCreationError::OomError(err) => err,
// We check that the provided queue family index is active on the device, so it can't
// be out of range.
CommandPoolCreationError::QueueFamilyIndexOutOfRange { .. } => unreachable!(),
})
}
fn allocate(
self: &Arc<Self>,
level: CommandBufferLevel,
mut command_buffer_count: u32,
) -> Result<IntoIter<StandardCommandBufferBuilderAlloc>, OomError> {
// The final output.
let mut output = Vec::with_capacity(command_buffer_count as usize);
// First, pick from already-existing command buffers.
{
let existing = match level {
CommandBufferLevel::Primary => &self.primary_pool,
CommandBufferLevel::Secondary => &self.secondary_pool,
};
for _ in 0..command_buffer_count as usize {
if let Some(cmd) = existing.pop() {
output.push(StandardCommandBufferBuilderAlloc {
inner: StandardCommandBufferAlloc {
cmd: ManuallyDrop::new(cmd),
pool: self.clone(),
},
dummy_avoid_send_sync: PhantomData,
});
} else {
break;
}
}
}
// Then allocate the rest.
if output.len() < command_buffer_count as usize {
command_buffer_count -= output.len() as u32;
for cmd in self
.inner
.allocate_command_buffers(CommandBufferAllocateInfo {
level,
command_buffer_count,
..Default::default()
})?
{
output.push(StandardCommandBufferBuilderAlloc {
inner: StandardCommandBufferAlloc {
cmd: ManuallyDrop::new(cmd),
pool: self.clone(),
},
dummy_avoid_send_sync: PhantomData,
});
}
}
// Final output.
Ok(output.into_iter())
}
}
/// Command buffer allocated from a [`StandardCommandBufferAllocator`] that is currently being
/// built.
pub struct StandardCommandBufferBuilderAlloc {
// The only difference between a `StandardCommandBufferBuilder` and a
// `StandardCommandBufferAlloc` is that the former must not implement `Send` and `Sync`.
// Therefore we just share the structs.
inner: StandardCommandBufferAlloc,
// Unimplemented `Send` and `Sync` from the builder.
dummy_avoid_send_sync: PhantomData<*const u8>,
}
unsafe impl CommandBufferBuilderAlloc for StandardCommandBufferBuilderAlloc {
type Alloc = StandardCommandBufferAlloc;
#[inline]
fn inner(&self) -> &CommandPoolAlloc {
self.inner.inner()
}
#[inline]
fn into_alloc(self) -> Self::Alloc {
self.inner
}
#[inline]
fn queue_family_index(&self) -> u32 {
self.inner.queue_family_index()
}
}
unsafe impl DeviceOwned for StandardCommandBufferBuilderAlloc {
#[inline]
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
/// Command buffer allocated from a [`StandardCommandBufferAllocator`].
pub struct StandardCommandBufferAlloc {
// The actual command buffer. Extracted in the `Drop` implementation.
cmd: ManuallyDrop<CommandPoolAlloc>,
// We hold a reference to the command pool for our destructor.
pool: Arc<Pool>,
}
unsafe impl Send for StandardCommandBufferAlloc {}
unsafe impl Sync for StandardCommandBufferAlloc {}
unsafe impl CommandBufferAlloc for StandardCommandBufferAlloc {
#[inline]
fn inner(&self) -> &CommandPoolAlloc {
&self.cmd
}
#[inline]
fn queue_family_index(&self) -> u32 {
self.pool.inner.queue_family_index()
}
}
unsafe impl DeviceOwned for StandardCommandBufferAlloc {
#[inline]
fn device(&self) -> &Arc<Device> {
self.pool.inner.device()
}
}
impl Drop for StandardCommandBufferAlloc {
#[inline]
fn drop(&mut self) {
let cmd = unsafe { ManuallyDrop::take(&mut self.cmd) };
match cmd.level() {
CommandBufferLevel::Primary => self.pool.primary_pool.push(cmd),
CommandBufferLevel::Secondary => self.pool.secondary_pool.push(cmd),
}
}
}
#[cfg(test)]
mod tests {
use super::{
CommandBufferAllocator, CommandBufferBuilderAlloc, StandardCommandBufferAllocator,
};
use crate::{command_buffer::CommandBufferLevel, VulkanObject};
#[test]
fn reuse_command_buffers() {
let (device, queue) = gfx_dev_and_queue!();
let allocator = StandardCommandBufferAllocator::new(device);
let cb = allocator
.allocate(queue.queue_family_index(), CommandBufferLevel::Primary, 1)
.unwrap()
.next()
.unwrap();
let raw = cb.inner().internal_object();
drop(cb);
let cb2 = allocator
.allocate(queue.queue_family_index(), CommandBufferLevel::Primary, 1)
.unwrap()
.next()
.unwrap();
assert_eq!(raw, cb2.inner().internal_object());
}
}

View File

@ -8,9 +8,9 @@
// according to those terms.
use super::{
pool::{
standard::{StandardCommandPoolAlloc, StandardCommandPoolBuilder},
CommandPool, CommandPoolAlloc, CommandPoolBuilderAlloc,
allocator::{
CommandBufferAlloc, CommandBufferAllocator, CommandBufferBuilderAlloc,
StandardCommandBufferAlloc, StandardCommandBufferAllocator,
},
synced::{CommandBufferState, SyncCommandBuffer, SyncCommandBufferBuilder},
sys::{CommandBufferBeginInfo, UnsafeCommandBuffer},
@ -41,13 +41,16 @@ use std::{
},
};
/// Note that command buffers allocated from the default command pool (`Arc<StandardCommandPool>`)
/// don't implement the `Send` and `Sync` traits. If you use this pool, then the
/// `AutoCommandBufferBuilder` will not implement `Send` and `Sync` either. Once a command buffer
/// is built, however, it *does* implement `Send` and `Sync`.
pub struct AutoCommandBufferBuilder<L, P = StandardCommandPoolBuilder> {
/// Note that command buffers allocated from `StandardCommandBufferAllocator` don't implement
/// the `Send` and `Sync` traits. If you use this allocator, then the `AutoCommandBufferBuilder`
/// will not implement `Send` and `Sync` either. Once a command buffer is built, however, it *does*
/// implement `Send` and `Sync`.
pub struct AutoCommandBufferBuilder<L, A = StandardCommandBufferAllocator>
where
A: CommandBufferAllocator,
{
pub(super) inner: SyncCommandBufferBuilder,
pool_builder_alloc: P, // Safety: must be dropped after `inner`
builder_alloc: A::Builder, // Safety: must be dropped after `inner`
// The index of the queue family that this command buffer is being created for.
queue_family_index: u32,
@ -122,20 +125,23 @@ pub(super) struct QueryState {
pub(super) in_subpass: bool,
}
impl AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, StandardCommandPoolBuilder> {
impl<A> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, A>
where
A: CommandBufferAllocator,
{
/// Starts recording a primary command buffer.
#[inline]
pub fn primary(
device: Arc<Device>,
allocator: &A,
queue_family_index: u32,
usage: CommandBufferUsage,
) -> Result<
AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, StandardCommandPoolBuilder>,
AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<A::Alloc>, A>,
CommandBufferBeginError,
> {
unsafe {
AutoCommandBufferBuilder::begin(
device,
allocator,
queue_family_index,
CommandBufferLevel::Primary,
CommandBufferBeginInfo {
@ -148,21 +154,24 @@ impl AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, StandardCommandPoolBuild
}
}
impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder> {
impl<A> AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, A>
where
A: CommandBufferAllocator,
{
/// Starts recording a secondary command buffer.
#[inline]
pub fn secondary(
device: Arc<Device>,
allocator: &A,
queue_family_index: u32,
usage: CommandBufferUsage,
inheritance_info: CommandBufferInheritanceInfo,
) -> Result<
AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<A::Alloc>, A>,
CommandBufferBeginError,
> {
unsafe {
AutoCommandBufferBuilder::begin(
device,
allocator,
queue_family_index,
CommandBufferLevel::Secondary,
CommandBufferBeginInfo {
@ -175,18 +184,20 @@ impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBui
}
}
impl<L> AutoCommandBufferBuilder<L, StandardCommandPoolBuilder> {
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
// Actual constructor. Private.
//
// `begin_info.inheritance_info` must match `level`.
unsafe fn begin(
device: Arc<Device>,
allocator: &A,
queue_family_index: u32,
level: CommandBufferLevel,
begin_info: CommandBufferBeginInfo,
) -> Result<AutoCommandBufferBuilder<L, StandardCommandPoolBuilder>, CommandBufferBeginError>
{
Self::validate_begin(&device, queue_family_index, level, &begin_info)?;
) -> Result<AutoCommandBufferBuilder<L, A>, CommandBufferBeginError> {
Self::validate_begin(allocator.device(), queue_family_index, level, &begin_info)?;
let &CommandBufferBeginInfo {
usage,
@ -250,17 +261,16 @@ impl<L> AutoCommandBufferBuilder<L, StandardCommandPoolBuilder> {
}
}
device.with_standard_command_pool(queue_family_index, |pool| {
let pool_builder_alloc = pool
.allocate(level, 1)?
let builder_alloc = allocator
.allocate(queue_family_index, level, 1)?
.next()
.expect("Requested one command buffer from the command pool, but got zero.");
.expect("requested one command buffer from the command pool, but got zero");
let inner = SyncCommandBufferBuilder::new(pool_builder_alloc.inner(), begin_info)?;
let inner = SyncCommandBufferBuilder::new(builder_alloc.inner(), begin_info)?;
Ok(AutoCommandBufferBuilder {
inner,
pool_builder_alloc,
builder_alloc,
queue_family_index,
render_pass_state,
query_state: HashMap::default(),
@ -268,7 +278,6 @@ impl<L> AutoCommandBufferBuilder<L, StandardCommandPoolBuilder> {
usage,
_data: PhantomData,
})
})?
}
fn validate_begin(
@ -592,12 +601,12 @@ impl From<RequirementNotMet> for CommandBufferBeginError {
}
}
impl<P> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P>
impl<A> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<A::Alloc>, A>
where
P: CommandPoolBuilderAlloc,
A: CommandBufferAllocator,
{
/// Builds the command buffer.
pub fn build(self) -> Result<PrimaryAutoCommandBuffer<P::Alloc>, BuildError> {
pub fn build(self) -> Result<PrimaryAutoCommandBuffer<A::Alloc>, BuildError> {
if self.render_pass_state.is_some() {
return Err(BuildError::RenderPassActive);
}
@ -618,18 +627,18 @@ where
Ok(PrimaryAutoCommandBuffer {
inner: self.inner.build()?,
_pool_alloc: self.pool_builder_alloc.into_alloc(),
_alloc: self.builder_alloc.into_alloc(),
submit_state,
})
}
}
impl<P> AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<P::Alloc>, P>
impl<A> AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<A::Alloc>, A>
where
P: CommandPoolBuilderAlloc,
A: CommandBufferAllocator,
{
/// Builds the command buffer.
pub fn build(self) -> Result<SecondaryAutoCommandBuffer<P::Alloc>, BuildError> {
pub fn build(self) -> Result<SecondaryAutoCommandBuffer<A::Alloc>, BuildError> {
if !self.query_state.is_empty() {
return Err(BuildError::QueryActive);
}
@ -646,7 +655,7 @@ where
Ok(SecondaryAutoCommandBuffer {
inner: self.inner.build()?,
_pool_alloc: self.pool_builder_alloc.into_alloc(),
_alloc: self.builder_alloc.into_alloc(),
inheritance_info: self.inheritance_info.unwrap(),
submit_state,
})
@ -692,7 +701,10 @@ impl From<OomError> for BuildError {
}
}
impl<L, P> AutoCommandBufferBuilder<L, P> {
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
pub(super) fn queue_family_properties(&self) -> &QueueFamilyProperties {
&self.device().physical_device().queue_family_properties()[self.queue_family_index as usize]
}
@ -703,15 +715,18 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
}
}
unsafe impl<L, P> DeviceOwned for AutoCommandBufferBuilder<L, P> {
unsafe impl<L, A> DeviceOwned for AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
pub struct PrimaryAutoCommandBuffer<P = StandardCommandPoolAlloc> {
pub struct PrimaryAutoCommandBuffer<A = StandardCommandBufferAlloc> {
inner: SyncCommandBuffer,
_pool_alloc: P, // Safety: must be dropped after `inner`
_alloc: A, // Safety: must be dropped after `inner`
// Tracks usage of the command buffer on the GPU.
submit_state: SubmitState,
@ -723,9 +738,9 @@ unsafe impl<P> DeviceOwned for PrimaryAutoCommandBuffer<P> {
}
}
unsafe impl<P> PrimaryCommandBuffer for PrimaryAutoCommandBuffer<P>
unsafe impl<A> PrimaryCommandBuffer for PrimaryAutoCommandBuffer<A>
where
P: CommandPoolAlloc,
A: CommandBufferAlloc,
{
fn inner(&self) -> &UnsafeCommandBuffer {
self.inner.as_ref()
@ -817,24 +832,24 @@ where
}
}
pub struct SecondaryAutoCommandBuffer<P = StandardCommandPoolAlloc> {
pub struct SecondaryAutoCommandBuffer<A = StandardCommandBufferAlloc> {
inner: SyncCommandBuffer,
_pool_alloc: P, // Safety: must be dropped after `inner`
_alloc: A, // Safety: must be dropped after `inner`
inheritance_info: CommandBufferInheritanceInfo,
// Tracks usage of the command buffer on the GPU.
submit_state: SubmitState,
}
unsafe impl<P> DeviceOwned for SecondaryAutoCommandBuffer<P> {
unsafe impl<A> DeviceOwned for SecondaryAutoCommandBuffer<A> {
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
unsafe impl<P> SecondaryCommandBuffer for SecondaryAutoCommandBuffer<P>
unsafe impl<A> SecondaryCommandBuffer for SecondaryAutoCommandBuffer<A>
where
P: CommandPoolAlloc,
A: CommandBufferAlloc,
{
fn inner(&self) -> &UnsafeCommandBuffer {
self.inner.as_ref()
@ -992,8 +1007,9 @@ mod tests {
)
.unwrap();
let allocator = StandardCommandBufferAllocator::new(device);
let mut cbb = AutoCommandBufferBuilder::primary(
device,
&allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)
@ -1029,9 +1045,11 @@ mod tests {
fn secondary_nonconcurrent_conflict() {
let (device, queue) = gfx_dev_and_queue!();
let allocator = StandardCommandBufferAllocator::new(device);
// Make a secondary CB that doesn't support simultaneous use.
let builder = AutoCommandBufferBuilder::secondary(
device.clone(),
&allocator,
queue.queue_family_index(),
CommandBufferUsage::MultipleSubmit,
Default::default(),
@ -1041,7 +1059,7 @@ mod tests {
{
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&allocator,
queue.queue_family_index(),
CommandBufferUsage::SimultaneousUse,
)
@ -1064,7 +1082,7 @@ mod tests {
{
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
&allocator,
queue.queue_family_index(),
CommandBufferUsage::SimultaneousUse,
)
@ -1073,7 +1091,7 @@ mod tests {
let cb1 = builder.build().unwrap();
let mut builder = AutoCommandBufferBuilder::primary(
device,
&allocator,
queue.queue_family_index(),
CommandBufferUsage::SimultaneousUse,
)
@ -1113,8 +1131,9 @@ mod tests {
)
.unwrap();
let allocator = StandardCommandBufferAllocator::new(device);
let mut builder = AutoCommandBufferBuilder::primary(
device,
&allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)
@ -1163,8 +1182,9 @@ mod tests {
)
.unwrap();
let allocator = StandardCommandBufferAllocator::new(device);
let mut builder = AutoCommandBufferBuilder::primary(
device,
&allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -10,6 +10,7 @@
use crate::{
buffer::{BufferAccess, BufferContents, TypedBufferAccess},
command_buffer::{
allocator::CommandBufferAllocator,
auto::RenderPassStateType,
synced::{Command, SetOrPush, SyncCommandBufferBuilder},
sys::UnsafeCommandBufferBuilder,
@ -45,7 +46,10 @@ use std::{
/// # Commands to bind or push state for pipeline execution commands.
///
/// These commands require a queue with a pipeline type that uses the given state.
impl<L, P> AutoCommandBufferBuilder<L, P> {
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
/// Binds descriptor sets for future dispatch or draw calls.
///
/// # Panics

View File

@ -9,6 +9,7 @@
use crate::{
command_buffer::{
allocator::CommandBufferAllocator,
synced::{Command, SyncCommandBufferBuilder},
sys::UnsafeCommandBufferBuilder,
AutoCommandBufferBuilder,
@ -28,7 +29,10 @@ use std::{
/// These commands all require the
/// [`ext_debug_utils`](crate::instance::InstanceExtensions::ext_debug_utils) to be enabled on the
/// instance.
impl<L, P> AutoCommandBufferBuilder<L, P> {
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
/// Opens a command buffer debug label region.
pub fn begin_debug_utils_label(
&mut self,

View File

@ -9,6 +9,7 @@
use crate::{
command_buffer::{
allocator::CommandBufferAllocator,
synced::{Command, SyncCommandBufferBuilder},
sys::UnsafeCommandBufferBuilder,
AutoCommandBufferBuilder,
@ -37,7 +38,10 @@ use std::{
/// # Commands to set dynamic state for pipelines.
///
/// These commands require a queue with a pipeline type that uses the given state.
impl<L, P> AutoCommandBufferBuilder<L, P> {
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
// Helper function for dynamic state setting.
fn validate_pipeline_fixed_state(
&self,

View File

@ -9,6 +9,7 @@
use crate::{
command_buffer::{
allocator::CommandBufferAllocator,
synced::{Command, Resource, SyncCommandBufferBuilder, SyncCommandBufferBuilderError},
sys::UnsafeCommandBufferBuilder,
AutoCommandBufferBuilder, CopyError, CopyErrorResource,
@ -33,7 +34,10 @@ use std::{
///
/// Unlike transfer commands, these require a graphics queue, except for `clear_color_image`, which
/// can also be called on a compute queue.
impl<L, P> AutoCommandBufferBuilder<L, P> {
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
/// Blits an image to another.
///
/// A *blit* is similar to an image copy operation, except that the portion of the image that

View File

@ -10,6 +10,7 @@
use crate::{
buffer::{view::BufferViewAbstract, BufferAccess, TypedBufferAccess},
command_buffer::{
allocator::CommandBufferAllocator,
auto::{RenderPassState, RenderPassStateType},
synced::{Command, Resource, SyncCommandBufferBuilder, SyncCommandBufferBuilderError},
sys::UnsafeCommandBufferBuilder,
@ -49,7 +50,10 @@ use std::{
/// # Commands to execute a bound pipeline.
///
/// Dispatch commands require a compute queue, draw commands require a graphics queue.
impl<L, P> AutoCommandBufferBuilder<L, P> {
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
/// Perform a single compute operation using a compute pipeline.
///
/// A compute pipeline must have been bound using

View File

@ -10,6 +10,7 @@
use crate::{
buffer::TypedBufferAccess,
command_buffer::{
allocator::CommandBufferAllocator,
auto::QueryState,
synced::{Command, Resource, SyncCommandBufferBuilder, SyncCommandBufferBuilderError},
sys::UnsafeCommandBufferBuilder,
@ -32,7 +33,10 @@ use std::{
};
/// # Commands related to queries.
impl<L, P> AutoCommandBufferBuilder<L, P> {
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
/// Begins a query.
///
/// The query will be active until [`end_query`](Self::end_query) is called for the same query.

View File

@ -9,14 +9,14 @@
use crate::{
command_buffer::{
allocator::CommandBufferAllocator,
auto::{
BeginRenderPassState, BeginRenderingAttachments, BeginRenderingState, RenderPassState,
RenderPassStateType,
},
pool::CommandPoolBuilderAlloc,
synced::{Command, Resource, SyncCommandBufferBuilder, SyncCommandBufferBuilderError},
sys::UnsafeCommandBufferBuilder,
AutoCommandBufferBuilder, PrimaryAutoCommandBuffer, SubpassContents,
AutoCommandBufferBuilder, SubpassContents,
},
device::DeviceOwned,
format::{ClearColorValue, ClearValue, Format, NumericType},
@ -40,9 +40,9 @@ use std::{
/// # Commands for render passes.
///
/// These commands require a graphics queue.
impl<P> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P>
impl<L, A> AutoCommandBufferBuilder<L, A>
where
P: CommandPoolBuilderAlloc,
A: CommandBufferAllocator,
{
/// Begins a render pass using a render pass object and framebuffer.
///
@ -526,7 +526,10 @@ where
}
}
impl<L, P> AutoCommandBufferBuilder<L, P> {
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
/// Begins a render pass without a render pass object or framebuffer.
///
/// You must call this or `begin_render_pass` before you can record draw commands.

View File

@ -9,12 +9,12 @@
use crate::{
command_buffer::{
allocator::CommandBufferAllocator,
auto::RenderPassStateType,
pool::CommandPoolBuilderAlloc,
synced::{Command, Resource, SyncCommandBufferBuilder, SyncCommandBufferBuilderError},
sys::UnsafeCommandBufferBuilder,
AutoCommandBufferBuilder, CommandBufferExecError, CommandBufferInheritanceRenderPassType,
CommandBufferUsage, PrimaryAutoCommandBuffer, SecondaryCommandBuffer, SubpassContents,
CommandBufferUsage, SecondaryCommandBuffer, SubpassContents,
},
device::DeviceOwned,
format::Format,
@ -32,9 +32,9 @@ use std::{
///
/// These commands can be called on any queue that can execute the commands recorded in the
/// secondary command buffer.
impl<P> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P>
impl<L, A> AutoCommandBufferBuilder<L, A>
where
P: CommandPoolBuilderAlloc,
A: CommandBufferAllocator,
{
/// Executes a secondary command buffer.
///

View File

@ -10,6 +10,7 @@
use crate::{
buffer::{BufferAccess, BufferContents, TypedBufferAccess},
command_buffer::{
allocator::CommandBufferAllocator,
synced::{Command, Resource, SyncCommandBufferBuilder, SyncCommandBufferBuilderError},
sys::UnsafeCommandBufferBuilder,
AutoCommandBufferBuilder, CopyError, CopyErrorResource,
@ -33,7 +34,10 @@ use std::{
/// # Commands to transfer data to a resource, either from the host or from another resource.
///
/// These commands can be called on a transfer queue, in addition to a compute or graphics queue.
impl<L, P> AutoCommandBufferBuilder<L, P> {
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
/// Copies data from a buffer to another buffer.
///
/// # Panics

View File

@ -61,8 +61,9 @@
//! # let vertex_buffer: std::sync::Arc<vulkano::buffer::CpuAccessibleBuffer<[Vertex]>> = return;
//! # let render_pass_begin_info: vulkano::command_buffer::RenderPassBeginInfo = return;
//! # let graphics_pipeline: std::sync::Arc<vulkano::pipeline::graphics::GraphicsPipeline> = return;
//! # let command_buffer_allocator: vulkano::command_buffer::allocator::StandardCommandBufferAllocator = return;
//! let cb = AutoCommandBufferBuilder::primary(
//! device.clone(),
//! &command_buffer_allocator,
//! queue.queue_family_index(),
//! CommandBufferUsage::MultipleSubmit
//! ).unwrap()
@ -129,6 +130,7 @@ use crate::{
use bytemuck::{Pod, Zeroable};
use std::sync::Arc;
pub mod allocator;
mod auto;
mod commands;
pub mod pool;

View File

@ -14,6 +14,7 @@ use crate::{
};
use smallvec::SmallVec;
use std::{
cell::Cell,
error::Error,
fmt::{Display, Error as FmtError, Formatter},
hash::{Hash, Hasher},
@ -23,7 +24,7 @@ use std::{
sync::Arc,
};
/// Low-level implementation of a command pool.
/// Represents a Vulkan command pool.
///
/// A command pool is always tied to a specific queue family. Command buffers allocated from a pool
/// can only be executed on the corresponding queue family.
@ -31,44 +32,40 @@ use std::{
/// This struct doesn't implement the `Sync` trait because Vulkan command pools are not thread
/// safe. In other words, you can only use a pool from one thread at a time.
#[derive(Debug)]
pub struct UnsafeCommandPool {
pub struct CommandPool {
handle: ash::vk::CommandPool,
device: Arc<Device>,
// We don't want `UnsafeCommandPool` to implement Sync.
// This marker unimplements both Send and Sync, but we reimplement Send manually right under.
dummy_avoid_sync: PhantomData<*const u8>,
queue_family_index: u32,
_transient: bool,
_reset_command_buffer: bool,
// Unimplement `Sync`, as Vulkan command pools are not thread-safe.
_marker: PhantomData<Cell<ash::vk::CommandPool>>,
}
unsafe impl Send for UnsafeCommandPool {}
impl UnsafeCommandPool {
/// Creates a new `UnsafeCommandPool`.
impl CommandPool {
/// Creates a new `CommandPool`.
pub fn new(
device: Arc<Device>,
mut create_info: UnsafeCommandPoolCreateInfo,
) -> Result<UnsafeCommandPool, UnsafeCommandPoolCreationError> {
mut create_info: CommandPoolCreateInfo,
) -> Result<CommandPool, CommandPoolCreationError> {
Self::validate(&device, &mut create_info)?;
let handle = unsafe { Self::create(&device, &create_info)? };
let UnsafeCommandPoolCreateInfo {
let CommandPoolCreateInfo {
queue_family_index,
transient,
reset_command_buffer,
_ne: _,
} = create_info;
Ok(UnsafeCommandPool {
Ok(CommandPool {
handle,
device,
dummy_avoid_sync: PhantomData,
queue_family_index,
_transient: transient,
_reset_command_buffer: reset_command_buffer,
_marker: PhantomData,
})
}
@ -82,31 +79,30 @@ impl UnsafeCommandPool {
pub unsafe fn from_handle(
device: Arc<Device>,
handle: ash::vk::CommandPool,
create_info: UnsafeCommandPoolCreateInfo,
) -> UnsafeCommandPool {
let UnsafeCommandPoolCreateInfo {
create_info: CommandPoolCreateInfo,
) -> CommandPool {
let CommandPoolCreateInfo {
queue_family_index,
transient,
reset_command_buffer,
_ne: _,
} = create_info;
UnsafeCommandPool {
CommandPool {
handle,
device,
dummy_avoid_sync: PhantomData,
queue_family_index,
_transient: transient,
_reset_command_buffer: reset_command_buffer,
_marker: PhantomData,
}
}
fn validate(
device: &Device,
create_info: &mut UnsafeCommandPoolCreateInfo,
) -> Result<(), UnsafeCommandPoolCreationError> {
let &mut UnsafeCommandPoolCreateInfo {
create_info: &mut CommandPoolCreateInfo,
) -> Result<(), CommandPoolCreationError> {
let &mut CommandPoolCreateInfo {
queue_family_index,
transient: _,
reset_command_buffer: _,
@ -115,7 +111,7 @@ impl UnsafeCommandPool {
// VUID-vkCreateCommandPool-queueFamilyIndex-01937
if queue_family_index >= device.physical_device().queue_family_properties().len() as u32 {
return Err(UnsafeCommandPoolCreationError::QueueFamilyIndexOutOfRange {
return Err(CommandPoolCreationError::QueueFamilyIndexOutOfRange {
queue_family_index,
queue_family_count: device.physical_device().queue_family_properties().len() as u32,
});
@ -126,9 +122,9 @@ impl UnsafeCommandPool {
unsafe fn create(
device: &Device,
create_info: &UnsafeCommandPoolCreateInfo,
) -> Result<ash::vk::CommandPool, UnsafeCommandPoolCreationError> {
let &UnsafeCommandPoolCreateInfo {
create_info: &CommandPoolCreateInfo,
) -> Result<ash::vk::CommandPool, CommandPoolCreationError> {
let &CommandPoolCreateInfo {
queue_family_index,
transient,
reset_command_buffer,
@ -197,7 +193,7 @@ impl UnsafeCommandPool {
pub fn allocate_command_buffers(
&self,
allocate_info: CommandBufferAllocateInfo,
) -> Result<impl ExactSizeIterator<Item = UnsafeCommandPoolAlloc>, OomError> {
) -> Result<impl ExactSizeIterator<Item = CommandPoolAlloc>, OomError> {
let CommandBufferAllocateInfo {
level,
command_buffer_count,
@ -232,9 +228,7 @@ impl UnsafeCommandPool {
let device = self.device.clone();
Ok(out
.into_iter()
.map(move |command_buffer| UnsafeCommandPoolAlloc {
Ok(out.into_iter().map(move |command_buffer| CommandPoolAlloc {
handle: command_buffer,
device: device.clone(),
@ -250,7 +244,7 @@ impl UnsafeCommandPool {
/// - The `command_buffers` must not be in the pending state.
pub unsafe fn free_command_buffers(
&self,
command_buffers: impl IntoIterator<Item = UnsafeCommandPoolAlloc>,
command_buffers: impl IntoIterator<Item = CommandPoolAlloc>,
) {
let command_buffers: SmallVec<[_; 4]> =
command_buffers.into_iter().map(|cb| cb.handle).collect();
@ -316,7 +310,7 @@ impl UnsafeCommandPool {
}
}
impl Drop for UnsafeCommandPool {
impl Drop for CommandPool {
#[inline]
fn drop(&mut self) {
unsafe {
@ -330,7 +324,7 @@ impl Drop for UnsafeCommandPool {
}
}
unsafe impl VulkanObject for UnsafeCommandPool {
unsafe impl VulkanObject for CommandPool {
type Object = ash::vk::CommandPool;
#[inline]
@ -339,32 +333,32 @@ unsafe impl VulkanObject for UnsafeCommandPool {
}
}
unsafe impl DeviceOwned for UnsafeCommandPool {
unsafe impl DeviceOwned for CommandPool {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl PartialEq for UnsafeCommandPool {
impl PartialEq for CommandPool {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle && self.device() == other.device()
}
}
impl Eq for UnsafeCommandPool {}
impl Eq for CommandPool {}
impl Hash for UnsafeCommandPool {
impl Hash for CommandPool {
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
self.device().hash(state);
}
}
/// Error that can happen when creating an `UnsafeCommandPool`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum UnsafeCommandPoolCreationError {
/// Error that can happen when creating a `CommandPool`.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CommandPoolCreationError {
/// Not enough memory.
OomError(OomError),
@ -376,7 +370,7 @@ pub enum UnsafeCommandPoolCreationError {
},
}
impl Error for UnsafeCommandPoolCreationError {
impl Error for CommandPoolCreationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::OomError(err) => Some(err),
@ -385,7 +379,7 @@ impl Error for UnsafeCommandPoolCreationError {
}
}
impl Display for UnsafeCommandPoolCreationError {
impl Display for CommandPoolCreationError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::OomError(_) => write!(f, "not enough memory",),
@ -402,7 +396,7 @@ impl Display for UnsafeCommandPoolCreationError {
}
}
impl From<VulkanError> for UnsafeCommandPoolCreationError {
impl From<VulkanError> for CommandPoolCreationError {
fn from(err: VulkanError) -> Self {
match err {
err @ VulkanError::OutOfHostMemory => Self::OomError(OomError::from(err)),
@ -411,9 +405,9 @@ impl From<VulkanError> for UnsafeCommandPoolCreationError {
}
}
/// Parameters to create an `UnsafeCommandPool`.
/// Parameters to create an `CommandPool`.
#[derive(Clone, Debug)]
pub struct UnsafeCommandPoolCreateInfo {
pub struct CommandPoolCreateInfo {
/// The index of the queue family that this pool is created for. All command buffers allocated
/// from this pool must be submitted on a queue belonging to that family.
///
@ -434,7 +428,7 @@ pub struct UnsafeCommandPoolCreateInfo {
pub _ne: crate::NonExhaustive,
}
impl Default for UnsafeCommandPoolCreateInfo {
impl Default for CommandPoolCreateInfo {
#[inline]
fn default() -> Self {
Self {
@ -475,13 +469,13 @@ impl Default for CommandBufferAllocateInfo {
/// Opaque type that represents a command buffer allocated from a pool.
#[derive(Debug)]
pub struct UnsafeCommandPoolAlloc {
pub struct CommandPoolAlloc {
handle: ash::vk::CommandBuffer,
device: Arc<Device>,
level: CommandBufferLevel,
}
impl UnsafeCommandPoolAlloc {
impl CommandPoolAlloc {
/// Returns the level of the command buffer.
#[inline]
pub fn level(&self) -> CommandBufferLevel {
@ -489,7 +483,7 @@ impl UnsafeCommandPoolAlloc {
}
}
unsafe impl VulkanObject for UnsafeCommandPoolAlloc {
unsafe impl VulkanObject for CommandPoolAlloc {
type Object = ash::vk::CommandBuffer;
#[inline]
@ -498,23 +492,23 @@ unsafe impl VulkanObject for UnsafeCommandPoolAlloc {
}
}
unsafe impl DeviceOwned for UnsafeCommandPoolAlloc {
unsafe impl DeviceOwned for CommandPoolAlloc {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl PartialEq for UnsafeCommandPoolAlloc {
impl PartialEq for CommandPoolAlloc {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle && self.device() == other.device()
}
}
impl Eq for UnsafeCommandPoolAlloc {}
impl Eq for CommandPoolAlloc {}
impl Hash for UnsafeCommandPoolAlloc {
impl Hash for CommandPoolAlloc {
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
self.device().hash(state);
@ -556,20 +550,19 @@ impl From<VulkanError> for CommandPoolTrimError {
#[cfg(test)]
mod tests {
use super::{
CommandPoolTrimError, UnsafeCommandPool, UnsafeCommandPoolCreateInfo,
UnsafeCommandPoolCreationError,
CommandPool, CommandPoolCreateInfo, CommandPoolCreationError, CommandPoolTrimError,
};
use crate::{
command_buffer::{pool::sys::CommandBufferAllocateInfo, CommandBufferLevel},
command_buffer::{pool::CommandBufferAllocateInfo, CommandBufferLevel},
RequiresOneOf, Version,
};
#[test]
fn basic_create() {
let (device, queue) = gfx_dev_and_queue!();
let _ = UnsafeCommandPool::new(
let _ = CommandPool::new(
device,
UnsafeCommandPoolCreateInfo {
CommandPoolCreateInfo {
queue_family_index: queue.queue_family_index(),
..Default::default()
},
@ -580,9 +573,9 @@ mod tests {
#[test]
fn queue_family_getter() {
let (device, queue) = gfx_dev_and_queue!();
let pool = UnsafeCommandPool::new(
let pool = CommandPool::new(
device,
UnsafeCommandPoolCreateInfo {
CommandPoolCreateInfo {
queue_family_index: queue.queue_family_index(),
..Default::default()
},
@ -595,13 +588,13 @@ mod tests {
fn check_queue_family_too_high() {
let (device, _) = gfx_dev_and_queue!();
match UnsafeCommandPool::new(
match CommandPool::new(
device,
UnsafeCommandPoolCreateInfo {
CommandPoolCreateInfo {
..Default::default()
},
) {
Err(UnsafeCommandPoolCreationError::QueueFamilyIndexOutOfRange { .. }) => (),
Err(CommandPoolCreationError::QueueFamilyIndexOutOfRange { .. }) => (),
_ => panic!(),
}
}
@ -609,9 +602,9 @@ mod tests {
#[test]
fn check_maintenance_when_trim() {
let (device, queue) = gfx_dev_and_queue!();
let pool = UnsafeCommandPool::new(
let pool = CommandPool::new(
device.clone(),
UnsafeCommandPoolCreateInfo {
CommandPoolCreateInfo {
queue_family_index: queue.queue_family_index(),
..Default::default()
},
@ -651,9 +644,9 @@ mod tests {
#[test]
fn basic_alloc() {
let (device, queue) = gfx_dev_and_queue!();
let pool = UnsafeCommandPool::new(
let pool = CommandPool::new(
device,
UnsafeCommandPoolCreateInfo {
CommandPoolCreateInfo {
queue_family_index: queue.queue_family_index(),
..Default::default()
},

View File

@ -1,105 +0,0 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// 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.
pub use self::{
standard::StandardCommandPool,
sys::{
CommandPoolTrimError, UnsafeCommandPool, UnsafeCommandPoolAlloc,
UnsafeCommandPoolCreateInfo, UnsafeCommandPoolCreationError,
},
};
use super::CommandBufferLevel;
use crate::{device::DeviceOwned, OomError};
pub mod standard;
mod sys;
/// Types that manage the memory of command buffers.
///
/// # Safety
///
/// A Vulkan command pool must be externally synchronized as if it owned the command buffers that
/// were allocated from it. This includes allocating from the pool, freeing from the pool,
/// resetting the pool or individual command buffers, and most importantly recording commands to
/// command buffers.
///
/// The implementation of `CommandPool` is expected to manage this. For as long as a `Builder`
/// is alive, the trait implementation is expected to lock the pool that allocated the `Builder`
/// for the current thread.
///
/// > **Note**: This may be modified in the future to allow different implementation strategies.
///
/// The destructors of the `CommandPoolBuilderAlloc` and the `CommandPoolAlloc` are expected to
/// free the command buffer, reset the command buffer, or add it to a pool so that it gets reused.
/// If the implementation frees or resets the command buffer, it must not forget that this
/// operation must lock the pool.
pub unsafe trait CommandPool: DeviceOwned {
/// See `alloc()`.
type Iter: Iterator<Item = Self::Builder>;
/// Represents a command buffer that has been allocated and that is currently being built.
type Builder: CommandPoolBuilderAlloc<Alloc = Self::Alloc>;
/// Represents a command buffer that has been allocated and that is pending execution or is
/// being executed.
type Alloc: CommandPoolAlloc;
/// Allocates command buffers from this pool.
///
/// Returns an iterator that contains an bunch of allocated command buffers.
fn allocate(
&self,
level: CommandBufferLevel,
command_buffer_count: u32,
) -> Result<Self::Iter, OomError>;
/// Returns the index of the queue family that this pool targets.
fn queue_family_index(&self) -> u32;
}
/// A command buffer allocated from a pool and that can be recorded.
///
/// # Safety
///
/// See `CommandPool` for information about safety.
pub unsafe trait CommandPoolBuilderAlloc: DeviceOwned {
/// Return type of `into_alloc`.
type Alloc: CommandPoolAlloc;
/// Returns the internal object that contains the command buffer.
fn inner(&self) -> &UnsafeCommandPoolAlloc;
/// Turns this builder into a command buffer that is pending execution.
fn into_alloc(self) -> Self::Alloc;
/// Returns the index of the queue family that the pool targets.
fn queue_family_index(&self) -> u32;
}
/// A command buffer allocated from a pool that has finished being recorded.
///
/// # Safety
///
/// See `CommandPool` for information about safety.
pub unsafe trait CommandPoolAlloc: DeviceOwned + Send + Sync {
/// Returns the internal object that contains the command buffer.
fn inner(&self) -> &UnsafeCommandPoolAlloc;
/// Returns the index of the queue family that the pool targets.
fn queue_family_index(&self) -> u32;
}

View File

@ -1,291 +0,0 @@
use super::{
sys::{CommandBufferAllocateInfo, UnsafeCommandPoolCreateInfo, UnsafeCommandPoolCreationError},
CommandPool, CommandPoolAlloc, CommandPoolBuilderAlloc, UnsafeCommandPool,
UnsafeCommandPoolAlloc,
};
use crate::{
command_buffer::CommandBufferLevel,
device::{Device, DeviceOwned},
OomError,
};
use crossbeam_queue::SegQueue;
use std::{marker::PhantomData, mem::ManuallyDrop, ptr, sync::Arc, vec::IntoIter as VecIntoIter};
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
/// Standard implementation of a command pool.
///
/// A thread can have as many `Arc<StandardCommandPool>`s as needed, but none of them can escape the
/// thread they were created on. This is done so that there are no locks involved when creating
/// command buffers. Command buffers can't be moved between threads during the building process, but
/// finished command buffers can. When a command buffer is dropped, it is returned back to the pool
/// for reuse.
#[derive(Debug)]
pub struct StandardCommandPool {
// The Vulkan pool specific to a device's queue family.
inner: UnsafeCommandPool,
// List of existing primary command buffers that are available for reuse.
available_primary_command_buffers: SegQueue<UnsafeCommandPoolAlloc>,
// List of existing secondary command buffers that are available for reuse.
available_secondary_command_buffers: SegQueue<UnsafeCommandPoolAlloc>,
}
impl StandardCommandPool {
/// Builds a new pool.
///
/// # Panics
///
/// - Panics if the device and the queue family don't belong to the same physical device.
pub fn new(
device: Arc<Device>,
queue_family_index: u32,
) -> Result<StandardCommandPool, OomError> {
assert!(
queue_family_index < device.physical_device().queue_family_properties().len() as u32
);
let inner = UnsafeCommandPool::new(
device,
UnsafeCommandPoolCreateInfo {
queue_family_index,
reset_command_buffer: true,
..Default::default()
},
)
.map_err(|err| match err {
UnsafeCommandPoolCreationError::OomError(err) => err,
_ => panic!("Unexpected error: {}", err),
})?;
Ok(StandardCommandPool {
inner,
available_primary_command_buffers: Default::default(),
available_secondary_command_buffers: Default::default(),
})
}
}
unsafe impl CommandPool for Arc<StandardCommandPool> {
type Iter = VecIntoIter<StandardCommandPoolBuilder>;
type Builder = StandardCommandPoolBuilder;
type Alloc = StandardCommandPoolAlloc;
#[inline]
fn allocate(
&self,
level: CommandBufferLevel,
mut command_buffer_count: u32,
) -> Result<Self::Iter, OomError> {
// The final output.
let mut output = Vec::with_capacity(command_buffer_count as usize);
// First, pick from already-existing command buffers.
{
let existing = match level {
CommandBufferLevel::Primary => &self.available_primary_command_buffers,
CommandBufferLevel::Secondary => &self.available_secondary_command_buffers,
};
for _ in 0..command_buffer_count as usize {
if let Some(cmd) = existing.pop() {
output.push(StandardCommandPoolBuilder {
inner: StandardCommandPoolAlloc {
cmd: ManuallyDrop::new(cmd),
pool: self.clone(),
},
dummy_avoid_send_sync: PhantomData,
});
} else {
break;
}
}
}
// Then allocate the rest.
if output.len() < command_buffer_count as usize {
command_buffer_count -= output.len() as u32;
for cmd in self
.inner
.allocate_command_buffers(CommandBufferAllocateInfo {
level,
command_buffer_count,
..Default::default()
})?
{
output.push(StandardCommandPoolBuilder {
inner: StandardCommandPoolAlloc {
cmd: ManuallyDrop::new(cmd),
pool: self.clone(),
},
dummy_avoid_send_sync: PhantomData,
});
}
}
// Final output.
Ok(output.into_iter())
}
#[inline]
fn queue_family_index(&self) -> u32 {
self.inner.queue_family_index()
}
}
unsafe impl DeviceOwned for StandardCommandPool {
#[inline]
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
/// Command buffer allocated from a `StandardCommandPool` that is currently being built.
pub struct StandardCommandPoolBuilder {
// The only difference between a `StandardCommandPoolBuilder` and a `StandardCommandPoolAlloc`
// is that the former must not implement `Send` and `Sync`. Therefore we just share the structs.
inner: StandardCommandPoolAlloc,
// Unimplemented `Send` and `Sync` from the builder.
dummy_avoid_send_sync: PhantomData<*const u8>,
}
unsafe impl CommandPoolBuilderAlloc for StandardCommandPoolBuilder {
type Alloc = StandardCommandPoolAlloc;
#[inline]
fn inner(&self) -> &UnsafeCommandPoolAlloc {
self.inner.inner()
}
#[inline]
fn into_alloc(self) -> Self::Alloc {
self.inner
}
#[inline]
fn queue_family_index(&self) -> u32 {
self.inner.queue_family_index()
}
}
unsafe impl DeviceOwned for StandardCommandPoolBuilder {
#[inline]
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
/// Command buffer allocated from a `StandardCommandPool`.
pub struct StandardCommandPoolAlloc {
// The actual command buffer. Extracted in the `Drop` implementation.
cmd: ManuallyDrop<UnsafeCommandPoolAlloc>,
// We hold a reference to the command pool for our destructor.
pool: Arc<StandardCommandPool>,
}
unsafe impl Send for StandardCommandPoolAlloc {}
unsafe impl Sync for StandardCommandPoolAlloc {}
unsafe impl CommandPoolAlloc for StandardCommandPoolAlloc {
#[inline]
fn inner(&self) -> &UnsafeCommandPoolAlloc {
&self.cmd
}
#[inline]
fn queue_family_index(&self) -> u32 {
self.pool.queue_family_index()
}
}
unsafe impl DeviceOwned for StandardCommandPoolAlloc {
#[inline]
fn device(&self) -> &Arc<Device> {
self.pool.device()
}
}
impl Drop for StandardCommandPoolAlloc {
#[inline]
fn drop(&mut self) {
// Safe because `self.cmd` is wrapped in a `ManuallyDrop`.
let cmd: UnsafeCommandPoolAlloc = unsafe { ptr::read(&*self.cmd) };
match cmd.level() {
CommandBufferLevel::Primary => self.pool.available_primary_command_buffers.push(cmd),
CommandBufferLevel::Secondary => {
self.pool.available_secondary_command_buffers.push(cmd)
}
}
}
}
#[cfg(test)]
mod tests {
use crate::{
command_buffer::{
pool::{CommandPool, CommandPoolBuilderAlloc},
CommandBufferLevel,
},
VulkanObject,
};
use std::{sync::Arc, thread};
#[test]
fn reuse_command_buffers() {
let (device, queue) = gfx_dev_and_queue!();
device
.with_standard_command_pool(queue.queue_family_index(), |pool| {
let cb = pool
.allocate(CommandBufferLevel::Primary, 1)
.unwrap()
.next()
.unwrap();
let raw = cb.inner().internal_object();
drop(cb);
let cb2 = pool
.allocate(CommandBufferLevel::Primary, 1)
.unwrap()
.next()
.unwrap();
assert_eq!(raw, cb2.inner().internal_object());
})
.unwrap();
}
#[test]
fn pool_kept_alive_by_thread() {
let (device, queue) = gfx_dev_and_queue!();
let thread = thread::spawn({
let (device, queue) = (device, queue);
move || {
device
.with_standard_command_pool(queue.queue_family_index(), |pool| {
pool.allocate(CommandBufferLevel::Primary, 1)
.unwrap()
.next()
.unwrap()
.inner
})
.unwrap()
}
});
// The thread-local storage should drop its reference to the pool here
let cb = thread.join().unwrap();
let pool_weak = Arc::downgrade(&cb.pool);
drop(cb);
assert!(pool_weak.upgrade().is_none());
}
}

View File

@ -17,7 +17,7 @@ pub use crate::command_buffer::commands::{
use crate::{
buffer::{sys::UnsafeBuffer, BufferAccess},
command_buffer::{
pool::UnsafeCommandPoolAlloc,
pool::CommandPoolAlloc,
synced::{BufferFinalState, BufferUse, ImageFinalState, ImageUse},
sys::{CommandBufferBeginInfo, UnsafeCommandBufferBuilder},
CommandBufferExecError, CommandBufferLevel,
@ -121,7 +121,7 @@ impl SyncCommandBufferBuilder {
/// See `UnsafeCommandBufferBuilder::new()`.
#[inline]
pub unsafe fn new(
pool_alloc: &UnsafeCommandPoolAlloc,
pool_alloc: &CommandPoolAlloc,
begin_info: CommandBufferBeginInfo,
) -> Result<SyncCommandBufferBuilder, OomError> {
let level = pool_alloc.level();

View File

@ -532,11 +532,14 @@ mod tests {
use crate::{
buffer::{BufferUsage, CpuAccessibleBuffer, DeviceLocalBuffer},
command_buffer::{
pool::{CommandPool, CommandPoolBuilderAlloc},
allocator::{
CommandBufferAllocator, CommandBufferBuilderAlloc, StandardCommandBufferAllocator,
},
sys::CommandBufferBeginInfo,
AutoCommandBufferBuilder, CommandBufferLevel, CommandBufferUsage, FillBufferInfo,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator,
layout::{
DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo,
DescriptorType,
@ -553,17 +556,16 @@ mod tests {
unsafe {
let (device, queue) = gfx_dev_and_queue!();
let pool_builder_alloc = device
.with_standard_command_pool(queue.queue_family_index(), |pool| {
pool.allocate(CommandBufferLevel::Primary, 1)
let allocator = StandardCommandBufferAllocator::new(device);
let builder_alloc = allocator
.allocate(queue.queue_family_index(), CommandBufferLevel::Primary, 1)
.unwrap()
.next()
.unwrap()
})
.unwrap();
SyncCommandBufferBuilder::new(
pool_builder_alloc.inner(),
builder_alloc.inner(),
CommandBufferBeginInfo {
usage: CommandBufferUsage::MultipleSubmit,
..Default::default()
@ -578,6 +580,8 @@ mod tests {
unsafe {
let (device, queue) = gfx_dev_and_queue!();
let allocator = StandardCommandBufferAllocator::new(device);
// Create a tiny test buffer
let (buf, future) = DeviceLocalBuffer::from_data(
0u32,
@ -585,6 +589,7 @@ mod tests {
transfer_dst: true,
..BufferUsage::empty()
},
&allocator,
queue.clone(),
)
.unwrap();
@ -598,7 +603,7 @@ mod tests {
let secondary = (0..2)
.map(|_| {
let mut builder = AutoCommandBufferBuilder::secondary(
device.clone(),
&allocator,
queue.queue_family_index(),
CommandBufferUsage::SimultaneousUse,
Default::default(),
@ -614,13 +619,10 @@ mod tests {
})
.collect::<Vec<_>>();
let allocs = device
.with_standard_command_pool(queue.queue_family_index(), |pool| {
pool.allocate(CommandBufferLevel::Primary, 2)
let allocs = allocator
.allocate(queue.queue_family_index(), CommandBufferLevel::Primary, 2)
.unwrap()
.collect::<Vec<_>>()
})
.unwrap();
.collect::<Vec<_>>();
{
let mut builder = SyncCommandBufferBuilder::new(
@ -676,16 +678,15 @@ mod tests {
unsafe {
let (device, queue) = gfx_dev_and_queue!();
let pool_builder_alloc = device
.with_standard_command_pool(queue.queue_family_index(), |pool| {
pool.allocate(CommandBufferLevel::Primary, 1)
let allocator = StandardCommandBufferAllocator::new(device.clone());
let builder_alloc = allocator
.allocate(queue.queue_family_index(), CommandBufferLevel::Primary, 1)
.unwrap()
.next()
.unwrap()
})
.unwrap();
let mut sync = SyncCommandBufferBuilder::new(
pool_builder_alloc.inner(),
builder_alloc.inner(),
CommandBufferBeginInfo {
usage: CommandBufferUsage::MultipleSubmit,
..Default::default()
@ -717,16 +718,14 @@ mod tests {
unsafe {
let (device, queue) = gfx_dev_and_queue!();
let pool_builder_alloc = device
.with_standard_command_pool(queue.queue_family_index(), |pool| {
pool.allocate(CommandBufferLevel::Primary, 1)
let cb_allocator = StandardCommandBufferAllocator::new(device.clone());
let builder_alloc = cb_allocator
.allocate(queue.queue_family_index(), CommandBufferLevel::Primary, 1)
.unwrap()
.next()
.unwrap()
})
.unwrap();
let mut sync = SyncCommandBufferBuilder::new(
pool_builder_alloc.inner(),
builder_alloc.inner(),
CommandBufferBeginInfo {
usage: CommandBufferUsage::MultipleSubmit,
..Default::default()
@ -757,7 +756,10 @@ mod tests {
)
.unwrap();
let ds_allocator = StandardDescriptorSetAllocator::new(device.clone());
let set = PersistentDescriptorSet::new(
&ds_allocator,
set_layout.clone(),
[WriteDescriptorSet::sampler(
0,
@ -815,6 +817,7 @@ mod tests {
.unwrap();
let set = PersistentDescriptorSet::new(
&ds_allocator,
set_layout,
[WriteDescriptorSet::sampler(
0,

View File

@ -12,8 +12,7 @@ pub use super::commands::{
secondary::UnsafeCommandBufferBuilderExecuteCommands,
};
use super::{
pool::UnsafeCommandPoolAlloc, CommandBufferInheritanceInfo, CommandBufferLevel,
CommandBufferUsage,
pool::CommandPoolAlloc, CommandBufferInheritanceInfo, CommandBufferLevel, CommandBufferUsage,
};
use crate::{
command_buffer::{
@ -52,7 +51,7 @@ impl UnsafeCommandBufferBuilder {
/// - `kind` must match how `pool_alloc` was created.
#[inline]
pub unsafe fn new(
pool_alloc: &UnsafeCommandPoolAlloc,
pool_alloc: &CommandPoolAlloc,
begin_info: CommandBufferBeginInfo,
) -> Result<UnsafeCommandBufferBuilder, OomError> {
let CommandBufferBeginInfo {

View File

@ -0,0 +1,183 @@
// Copyright (c) 2021 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// 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, descriptor sets must be allocated from *descriptor pools*.
//!
//! A descriptor pool holds and manages the memory of one or more descriptor sets. If you destroy a
//! descriptor pool, all of its descriptor sets are automatically destroyed.
//!
//! In vulkano, creating a descriptor set requires passing an implementation of the
//! [`DescriptorSetAllocator`] trait, which you can implement yourself or use the vulkano-provided
//! [`StandardDescriptorSetAllocator`].
use super::{
layout::DescriptorSetLayout,
single_layout_pool::{
SingleLayoutDescriptorSetPool, SingleLayoutPoolAlloc,
SingleLayoutVariableDescriptorSetPool, SingleLayoutVariablePoolAlloc,
},
sys::UnsafeDescriptorSet,
};
use crate::{
device::{Device, DeviceOwned},
OomError,
};
use ahash::HashMap;
use std::{cell::UnsafeCell, sync::Arc};
/// Types that manage the memory of descriptor sets.
///
/// # Safety
///
/// A Vulkan descriptor pool must be externally synchronized as if it owned the descriptor sets that
/// were allocated from it. This includes allocating from the pool, freeing from the pool and
/// resetting the pool or individual descriptor sets. The implementation of `DescriptorSetAllocator`
/// is expected to manage this.
///
/// The destructor of the [`DescriptorSetAlloc`] is expected to free the descriptor set, reset the
/// descriptor set, or add it to a pool so that it gets reused. If the implementation frees or
/// resets the descriptor set, it must not forget that this operation must be externally
/// synchronized.
pub unsafe trait DescriptorSetAllocator: DeviceOwned {
/// Object that represented an allocated descriptor set.
///
/// The destructor of this object should free the descriptor set.
type Alloc: DescriptorSetAlloc;
/// Allocates a descriptor set.
fn allocate(
&self,
layout: &Arc<DescriptorSetLayout>,
variable_descriptor_count: u32,
) -> Result<Self::Alloc, OomError>;
}
/// An allocated descriptor set.
pub trait DescriptorSetAlloc: Send + Sync {
/// Returns the inner unsafe descriptor set object.
fn inner(&self) -> &UnsafeDescriptorSet;
/// Returns the inner unsafe descriptor set object.
fn inner_mut(&mut self) -> &mut UnsafeDescriptorSet;
}
/// Standard implementation of a descriptor set allocator.
///
/// Internally, this implementation uses one [`SingleLayoutDescriptorSetPool`] /
/// [`SingleLayoutVariableDescriptorSetPool`] per descriptor set layout.
#[derive(Debug)]
pub struct StandardDescriptorSetAllocator {
device: Arc<Device>,
pools: UnsafeCell<HashMap<Arc<DescriptorSetLayout>, Pool>>,
}
#[derive(Debug)]
enum Pool {
Fixed(SingleLayoutDescriptorSetPool),
Variable(SingleLayoutVariableDescriptorSetPool),
}
impl StandardDescriptorSetAllocator {
/// Creates a new `StandardDescriptorSetAllocator`.
#[inline]
pub fn new(device: Arc<Device>) -> StandardDescriptorSetAllocator {
StandardDescriptorSetAllocator {
device,
pools: UnsafeCell::new(HashMap::default()),
}
}
}
unsafe impl DescriptorSetAllocator for StandardDescriptorSetAllocator {
type Alloc = StandardDescriptorSetAlloc;
#[inline]
fn allocate(
&self,
layout: &Arc<DescriptorSetLayout>,
variable_descriptor_count: u32,
) -> Result<StandardDescriptorSetAlloc, OomError> {
assert!(
!layout.push_descriptor(),
"the provided descriptor set layout is for push descriptors, and cannot be used to \
build a descriptor set object",
);
let max_count = layout.variable_descriptor_count();
assert!(
variable_descriptor_count <= max_count,
"the provided variable_descriptor_count ({}) is greater than the maximum number of \
variable count descriptors in the set ({})",
variable_descriptor_count,
max_count,
);
let pools = unsafe { &mut *self.pools.get() };
// We do this instead of using `HashMap::entry` directly because that would involve cloning
// an `Arc` every time. `hash_raw_entry` is still not stabilized >:(
let pool = if let Some(pool) = pools.get_mut(layout) {
pool
} else {
pools.entry(layout.clone()).or_insert(if max_count == 0 {
Pool::Fixed(SingleLayoutDescriptorSetPool::new(layout.clone())?)
} else {
Pool::Variable(SingleLayoutVariableDescriptorSetPool::new(layout.clone())?)
})
};
let inner = match pool {
Pool::Fixed(pool) => PoolAlloc::Fixed(pool.next_alloc()?),
Pool::Variable(pool) => {
PoolAlloc::Variable(pool.next_alloc(variable_descriptor_count)?)
}
};
Ok(StandardDescriptorSetAlloc { inner })
}
}
unsafe impl DeviceOwned for StandardDescriptorSetAllocator {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
/// A descriptor set allocated from a [`StandardDescriptorSetAllocator`].
#[derive(Debug)]
pub struct StandardDescriptorSetAlloc {
// The actual descriptor alloc.
inner: PoolAlloc,
}
#[derive(Debug)]
enum PoolAlloc {
Fixed(SingleLayoutPoolAlloc),
Variable(SingleLayoutVariablePoolAlloc),
}
impl DescriptorSetAlloc for StandardDescriptorSetAlloc {
#[inline]
fn inner(&self) -> &UnsafeDescriptorSet {
match &self.inner {
PoolAlloc::Fixed(alloc) => alloc.inner(),
PoolAlloc::Variable(alloc) => alloc.inner(),
}
}
#[inline]
fn inner_mut(&mut self) -> &mut UnsafeDescriptorSet {
match &mut self.inner {
PoolAlloc::Fixed(alloc) => alloc.inner_mut(),
PoolAlloc::Variable(alloc) => alloc.inner_mut(),
}
}
}

View File

@ -52,32 +52,37 @@
//!
//! - A `DescriptorSetLayout` is a Vulkan object that describes to the Vulkan implementation the
//! layout of a future descriptor set. When you allocate a descriptor set, you have to pass an
//! instance of this object. This is represented with the `DescriptorSetLayout` type in
//! instance of this object. This is represented with the [`DescriptorSetLayout`] type in
//! vulkano.
//! - A `DescriptorPool` is a Vulkan object that holds the memory of descriptor sets and that can
//! be used to allocate and free individual descriptor sets. This is represented with the
//! `UnsafeDescriptorPool` type in vulkano.
//! [`DescriptorPool`] type in vulkano.
//! - A `DescriptorSet` contains the bindings to resources and is allocated from a pool. This is
//! represented with the `UnsafeDescriptorSet` type in vulkano.
//! represented with the [`UnsafeDescriptorSet`] type in vulkano.
//!
//! In addition to this, vulkano defines the following:
//!
//! - The `DescriptorPool` trait can be implemented on types from which you can allocate and free
//! descriptor sets. However it is different from Vulkan descriptor pools in the sense that an
//! implementation of the `DescriptorPool` trait can manage multiple Vulkan descriptor pools.
//! - The `StandardDescriptorPool` type is a default implementation of the `DescriptorPool` trait.
//! - The `DescriptorSet` trait is implemented on types that wrap around Vulkan descriptor sets in
//! - The [`DescriptorSetAllocator`] trait can be implemented on types from which you can allocate
//! and free descriptor sets. However it is different from Vulkan descriptor pools in the sense
//! that an implementation of the [`DescriptorSetAllocator`] trait can manage multiple Vulkan
//! descriptor pools.
//! - The [`StandardDescriptorSetAllocator`] type is a default implementation of the
//! [`DescriptorSetAllocator`] trait.
//! - The [`DescriptorSet`] trait is implemented on types that wrap around Vulkan descriptor sets in
//! a safe way. A Vulkan descriptor set is inherently unsafe, so we need safe wrappers around
//! them.
//! - The `SimpleDescriptorSet` type is a default implementation of the `DescriptorSet` trait.
//! - The `DescriptorSetsCollection` trait is implemented on collections of types that implement
//! `DescriptorSet`. It is what you pass to the draw functions.
//! - The [`DescriptorSetsCollection`] trait is implemented on collections of types that implement
//! [`DescriptorSet`]. It is what you pass to the draw functions.
//!
//! [`DescriptorPool`]: pool::DescriptorPool
//! [`DescriptorSetAllocator`]: allocator::DescriptorSetAllocator
//! [`StandardDescriptorSetAllocator`]: allocator::StandardDescriptorSetAllocator
pub(crate) use self::update::{check_descriptor_write, DescriptorWriteInfo};
pub use self::{
collection::DescriptorSetsCollection,
persistent::PersistentDescriptorSet,
single_layout_pool::SingleLayoutDescSetPool,
single_layout_pool::{SingleLayoutDescriptorSetPool, SingleLayoutVariableDescriptorSetPool},
update::{DescriptorSetUpdateError, WriteDescriptorSet, WriteDescriptorSetElements},
};
use self::{layout::DescriptorSetLayout, sys::UnsafeDescriptorSet};
@ -99,6 +104,7 @@ use std::{
sync::Arc,
};
pub mod allocator;
mod collection;
pub mod layout;
pub mod persistent;

View File

@ -23,7 +23,7 @@
use crate::{
descriptor_set::{
pool::{standard::StandardDescriptorPoolAlloc, DescriptorPool, DescriptorPoolAlloc},
allocator::{DescriptorSetAlloc, DescriptorSetAllocator, StandardDescriptorSetAlloc},
update::WriteDescriptorSet,
DescriptorSet, DescriptorSetCreationError, DescriptorSetInner, DescriptorSetLayout,
DescriptorSetResources, UnsafeDescriptorSet,
@ -37,7 +37,7 @@ use std::{
};
/// A simple, immutable descriptor set that is expected to be long-lived.
pub struct PersistentDescriptorSet<P = StandardDescriptorPoolAlloc> {
pub struct PersistentDescriptorSet<P = StandardDescriptorSetAlloc> {
alloc: P,
inner: DescriptorSetInner,
}
@ -47,33 +47,15 @@ impl PersistentDescriptorSet {
///
/// See `new_with_pool` for more.
#[inline]
pub fn new(
pub fn new<A>(
allocator: &A,
layout: Arc<DescriptorSetLayout>,
descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>,
) -> Result<Arc<PersistentDescriptorSet>, DescriptorSetCreationError> {
layout
.device()
.clone()
.with_standard_descriptor_pool(|pool| {
Self::new_with_pool(layout, 0, pool, descriptor_writes)
})
}
/// Creates and returns a new descriptor set with the requested variable descriptor count.
///
/// See `new_with_pool` for more.
#[inline]
pub fn new_variable(
layout: Arc<DescriptorSetLayout>,
variable_descriptor_count: u32,
descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>,
) -> Result<Arc<PersistentDescriptorSet>, DescriptorSetCreationError> {
layout
.device()
.clone()
.with_standard_descriptor_pool(|pool| {
Self::new_with_pool(layout, variable_descriptor_count, pool, descriptor_writes)
})
) -> Result<Arc<PersistentDescriptorSet<A::Alloc>>, DescriptorSetCreationError>
where
A: DescriptorSetAllocator + ?Sized,
{
Self::new_variable(allocator, layout, 0, descriptor_writes)
}
/// Creates and returns a new descriptor set with the requested variable descriptor count,
@ -83,14 +65,14 @@ impl PersistentDescriptorSet {
///
/// - Panics if `layout` was created for push descriptors rather than descriptor sets.
/// - Panics if `variable_descriptor_count` is too large for the given `layout`.
pub fn new_with_pool<P>(
pub fn new_variable<A>(
allocator: &A,
layout: Arc<DescriptorSetLayout>,
variable_descriptor_count: u32,
pool: &mut P,
descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>,
) -> Result<Arc<PersistentDescriptorSet<P::Alloc>>, DescriptorSetCreationError>
) -> Result<Arc<PersistentDescriptorSet<A::Alloc>>, DescriptorSetCreationError>
where
P: ?Sized + DescriptorPool,
A: DescriptorSetAllocator + ?Sized,
{
assert!(
!layout.push_descriptor(),
@ -108,7 +90,7 @@ impl PersistentDescriptorSet {
max_count,
);
let alloc = pool.allocate(&layout, variable_descriptor_count)?;
let alloc = allocator.allocate(&layout, variable_descriptor_count)?;
let inner = DescriptorSetInner::new(
alloc.inner().internal_object(),
layout,
@ -122,7 +104,7 @@ impl PersistentDescriptorSet {
unsafe impl<P> DescriptorSet for PersistentDescriptorSet<P>
where
P: DescriptorPoolAlloc,
P: DescriptorSetAlloc,
{
fn inner(&self) -> &UnsafeDescriptorSet {
self.alloc.inner()
@ -139,7 +121,7 @@ where
unsafe impl<P> DeviceOwned for PersistentDescriptorSet<P>
where
P: DescriptorPoolAlloc,
P: DescriptorSetAlloc,
{
fn device(&self) -> &Arc<Device> {
self.inner.layout().device()
@ -148,7 +130,7 @@ where
impl<P> PartialEq for PersistentDescriptorSet<P>
where
P: DescriptorPoolAlloc,
P: DescriptorSetAlloc,
{
fn eq(&self, other: &Self) -> bool {
self.inner().internal_object() == other.inner().internal_object()
@ -156,11 +138,11 @@ where
}
}
impl<P> Eq for PersistentDescriptorSet<P> where P: DescriptorPoolAlloc {}
impl<P> Eq for PersistentDescriptorSet<P> where P: DescriptorSetAlloc {}
impl<P> Hash for PersistentDescriptorSet<P>
where
P: DescriptorPoolAlloc,
P: DescriptorSetAlloc,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner().internal_object().hash(state);

View File

@ -18,9 +18,11 @@ use crate::{
use ahash::HashMap;
use smallvec::SmallVec;
use std::{
cell::Cell,
error::Error,
fmt::{Display, Error as FmtError, Formatter},
hash::{Hash, Hasher},
marker::PhantomData,
mem::MaybeUninit,
ptr,
sync::Arc,
@ -31,16 +33,18 @@ use std::{
/// A pool has a maximum number of descriptor sets and a maximum number of descriptors (one value
/// per descriptor type) it can allocate.
#[derive(Debug)]
pub struct UnsafeDescriptorPool {
pub struct DescriptorPool {
handle: ash::vk::DescriptorPool,
device: Arc<Device>,
max_sets: u32,
pool_sizes: HashMap<DescriptorType, u32>,
can_free_descriptor_sets: bool,
// Unimplement `Sync`, as Vulkan descriptor pools are not thread safe.
_marker: PhantomData<Cell<ash::vk::DescriptorPool>>,
}
impl UnsafeDescriptorPool {
impl DescriptorPool {
/// Creates a new `UnsafeDescriptorPool`.
///
/// # Panics
@ -50,9 +54,9 @@ impl UnsafeDescriptorPool {
/// - Panics if `create_info.pool_sizes` contains a descriptor type with a count of `0`.
pub fn new(
device: Arc<Device>,
create_info: UnsafeDescriptorPoolCreateInfo,
) -> Result<UnsafeDescriptorPool, OomError> {
let UnsafeDescriptorPoolCreateInfo {
create_info: DescriptorPoolCreateInfo,
) -> Result<DescriptorPool, OomError> {
let DescriptorPoolCreateInfo {
max_sets,
pool_sizes,
can_free_descriptor_sets,
@ -108,13 +112,13 @@ impl UnsafeDescriptorPool {
}
};
Ok(UnsafeDescriptorPool {
Ok(DescriptorPool {
handle,
device,
max_sets,
pool_sizes,
can_free_descriptor_sets,
_marker: PhantomData,
})
}
@ -128,22 +132,22 @@ impl UnsafeDescriptorPool {
pub unsafe fn from_handle(
device: Arc<Device>,
handle: ash::vk::DescriptorPool,
create_info: UnsafeDescriptorPoolCreateInfo,
) -> UnsafeDescriptorPool {
let UnsafeDescriptorPoolCreateInfo {
create_info: DescriptorPoolCreateInfo,
) -> DescriptorPool {
let DescriptorPoolCreateInfo {
max_sets,
pool_sizes,
can_free_descriptor_sets,
_ne: _,
} = create_info;
UnsafeDescriptorPool {
DescriptorPool {
handle,
device,
max_sets,
pool_sizes,
can_free_descriptor_sets,
_marker: PhantomData,
}
}
@ -184,7 +188,7 @@ impl UnsafeDescriptorPool {
/// - You must ensure that the allocated descriptor sets are no longer in use when the pool
/// is destroyed, as destroying the pool is equivalent to freeing all the sets.
pub unsafe fn allocate_descriptor_sets<'a>(
&mut self,
&self,
allocate_info: impl IntoIterator<Item = DescriptorSetAllocateInfo<'a>>,
) -> Result<impl ExactSizeIterator<Item = UnsafeDescriptorSet>, DescriptorPoolAllocError> {
let (layouts, variable_descriptor_counts): (SmallVec<[_; 1]>, SmallVec<[_; 1]>) =
@ -282,7 +286,7 @@ impl UnsafeDescriptorPool {
/// - The descriptor sets must not be free'd twice.
/// - The descriptor sets must not be in use by the GPU.
pub unsafe fn free_descriptor_sets(
&mut self,
&self,
descriptor_sets: impl IntoIterator<Item = UnsafeDescriptorSet>,
) -> Result<(), OomError> {
let sets: SmallVec<[_; 8]> = descriptor_sets
@ -308,7 +312,7 @@ impl UnsafeDescriptorPool {
///
/// This destroys all descriptor sets and empties the pool.
#[inline]
pub unsafe fn reset(&mut self) -> Result<(), OomError> {
pub unsafe fn reset(&self) -> Result<(), OomError> {
let fns = self.device.fns();
(fns.v1_0.reset_descriptor_pool)(
self.device.internal_object(),
@ -322,7 +326,7 @@ impl UnsafeDescriptorPool {
}
}
impl Drop for UnsafeDescriptorPool {
impl Drop for DescriptorPool {
#[inline]
fn drop(&mut self) {
unsafe {
@ -336,7 +340,7 @@ impl Drop for UnsafeDescriptorPool {
}
}
unsafe impl VulkanObject for UnsafeDescriptorPool {
unsafe impl VulkanObject for DescriptorPool {
type Object = ash::vk::DescriptorPool;
#[inline]
@ -345,23 +349,23 @@ unsafe impl VulkanObject for UnsafeDescriptorPool {
}
}
unsafe impl DeviceOwned for UnsafeDescriptorPool {
unsafe impl DeviceOwned for DescriptorPool {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl PartialEq for UnsafeDescriptorPool {
impl PartialEq for DescriptorPool {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle && self.device() == other.device()
}
}
impl Eq for UnsafeDescriptorPool {}
impl Eq for DescriptorPool {}
impl Hash for UnsafeDescriptorPool {
impl Hash for DescriptorPool {
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
self.device().hash(state);
@ -370,7 +374,7 @@ impl Hash for UnsafeDescriptorPool {
/// Parameters to create a new `UnsafeDescriptorPool`.
#[derive(Clone, Debug)]
pub struct UnsafeDescriptorPoolCreateInfo {
pub struct DescriptorPoolCreateInfo {
/// The maximum number of descriptor sets that can be allocated from the pool.
///
/// The default value is `0`, which must be overridden.
@ -390,7 +394,7 @@ pub struct UnsafeDescriptorPoolCreateInfo {
pub _ne: crate::NonExhaustive,
}
impl Default for UnsafeDescriptorPoolCreateInfo {
impl Default for DescriptorPoolCreateInfo {
#[inline]
fn default() -> Self {
Self {
@ -451,7 +455,7 @@ impl Display for DescriptorPoolAllocError {
#[cfg(test)]
mod tests {
use super::{UnsafeDescriptorPool, UnsafeDescriptorPoolCreateInfo};
use super::{DescriptorPool, DescriptorPoolCreateInfo};
use crate::{
descriptor_set::{
layout::{
@ -467,9 +471,9 @@ mod tests {
fn pool_create() {
let (device, _) = gfx_dev_and_queue!();
let _ = UnsafeDescriptorPool::new(
let _ = DescriptorPool::new(
device,
UnsafeDescriptorPoolCreateInfo {
DescriptorPoolCreateInfo {
max_sets: 10,
pool_sizes: [(DescriptorType::UniformBuffer, 1)].into_iter().collect(),
..Default::default()
@ -483,9 +487,9 @@ mod tests {
let (device, _) = gfx_dev_and_queue!();
assert_should_panic!({
let _ = UnsafeDescriptorPool::new(
let _ = DescriptorPool::new(
device,
UnsafeDescriptorPoolCreateInfo {
DescriptorPoolCreateInfo {
max_sets: 0,
pool_sizes: [(DescriptorType::UniformBuffer, 1)].into_iter().collect(),
..Default::default()
@ -499,9 +503,9 @@ mod tests {
let (device, _) = gfx_dev_and_queue!();
assert_should_panic!({
let _ = UnsafeDescriptorPool::new(
let _ = DescriptorPool::new(
device,
UnsafeDescriptorPoolCreateInfo {
DescriptorPoolCreateInfo {
max_sets: 10,
..Default::default()
},
@ -529,9 +533,9 @@ mod tests {
)
.unwrap();
let mut pool = UnsafeDescriptorPool::new(
let pool = DescriptorPool::new(
device,
UnsafeDescriptorPoolCreateInfo {
DescriptorPoolCreateInfo {
max_sets: 10,
pool_sizes: [(DescriptorType::UniformBuffer, 10)].into_iter().collect(),
..Default::default()
@ -571,9 +575,9 @@ mod tests {
.unwrap();
assert_should_panic!({
let mut pool = UnsafeDescriptorPool::new(
let pool = DescriptorPool::new(
device2,
UnsafeDescriptorPoolCreateInfo {
DescriptorPoolCreateInfo {
max_sets: 10,
pool_sizes: [(DescriptorType::UniformBuffer, 10)].into_iter().collect(),
..Default::default()
@ -594,9 +598,9 @@ mod tests {
fn alloc_zero() {
let (device, _) = gfx_dev_and_queue!();
let mut pool = UnsafeDescriptorPool::new(
let pool = DescriptorPool::new(
device,
UnsafeDescriptorPoolCreateInfo {
DescriptorPoolCreateInfo {
max_sets: 1,
pool_sizes: [(DescriptorType::UniformBuffer, 1)].into_iter().collect(),
..Default::default()

View File

@ -1,48 +0,0 @@
// Copyright (c) 2021 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! A pool from which descriptor sets can be allocated.
pub use self::{
standard::StandardDescriptorPool,
sys::{
DescriptorPoolAllocError, DescriptorSetAllocateInfo, UnsafeDescriptorPool,
UnsafeDescriptorPoolCreateInfo,
},
};
use super::{layout::DescriptorSetLayout, sys::UnsafeDescriptorSet};
use crate::{device::DeviceOwned, OomError};
use std::sync::Arc;
pub mod standard;
mod sys;
/// A pool from which descriptor sets can be allocated.
pub unsafe trait DescriptorPool: DeviceOwned {
/// Object that represented an allocated descriptor set.
///
/// The destructor of this object should free the descriptor set.
type Alloc: DescriptorPoolAlloc;
/// Allocates a descriptor set.
fn allocate(
&mut self,
layout: &Arc<DescriptorSetLayout>,
variable_descriptor_count: u32,
) -> Result<Self::Alloc, OomError>;
}
/// An allocated descriptor set.
pub trait DescriptorPoolAlloc: Send + Sync {
/// Returns the inner unsafe descriptor set object.
fn inner(&self) -> &UnsafeDescriptorSet;
/// Returns the inner unsafe descriptor set object.
fn inner_mut(&mut self) -> &mut UnsafeDescriptorSet;
}

View File

@ -1,139 +0,0 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use super::{DescriptorPool, DescriptorPoolAlloc};
use crate::{
descriptor_set::{
layout::DescriptorSetLayout,
single_layout_pool::{
SingleLayoutPoolAlloc, SingleLayoutVariableDescSetPool, SingleLayoutVariablePoolAlloc,
},
sys::UnsafeDescriptorSet,
SingleLayoutDescSetPool,
},
device::{Device, DeviceOwned},
OomError,
};
use ahash::HashMap;
use std::sync::Arc;
/// Standard implementation of a descriptor pool.
///
/// Interally, this implementation uses one [`SingleLayoutDescSetPool`] /
/// [`SingleLayoutVariableDescSetPool`] per descriptor set layout.
#[derive(Debug)]
pub struct StandardDescriptorPool {
device: Arc<Device>,
pools: HashMap<Arc<DescriptorSetLayout>, Pool>,
}
#[derive(Debug)]
enum Pool {
Fixed(SingleLayoutDescSetPool),
Variable(SingleLayoutVariableDescSetPool),
}
impl StandardDescriptorPool {
/// Builds a new `StandardDescriptorPool`.
#[inline]
pub fn new(device: Arc<Device>) -> StandardDescriptorPool {
StandardDescriptorPool {
device,
pools: HashMap::default(),
}
}
}
unsafe impl DescriptorPool for StandardDescriptorPool {
type Alloc = StandardDescriptorPoolAlloc;
#[inline]
fn allocate(
&mut self,
layout: &Arc<DescriptorSetLayout>,
variable_descriptor_count: u32,
) -> Result<StandardDescriptorPoolAlloc, OomError> {
assert!(
!layout.push_descriptor(),
"the provided descriptor set layout is for push descriptors, and cannot be used to \
build a descriptor set object",
);
let max_count = layout.variable_descriptor_count();
assert!(
variable_descriptor_count <= max_count,
"the provided variable_descriptor_count ({}) is greater than the maximum number of \
variable count descriptors in the set ({})",
variable_descriptor_count,
max_count,
);
// We do this instead of using `HashMap::entry` directly because that would involve cloning
// an `Arc` every time. `hash_raw_entry` is still not stabilized >:(
let pool = if let Some(pool) = self.pools.get_mut(layout) {
pool
} else {
self.pools
.entry(layout.clone())
.or_insert(if max_count == 0 {
Pool::Fixed(SingleLayoutDescSetPool::new(layout.clone())?)
} else {
Pool::Variable(SingleLayoutVariableDescSetPool::new(layout.clone())?)
})
};
let inner = match pool {
Pool::Fixed(pool) => PoolAlloc::Fixed(pool.next_alloc()?),
Pool::Variable(pool) => {
PoolAlloc::Variable(pool.next_alloc(variable_descriptor_count)?)
}
};
Ok(StandardDescriptorPoolAlloc { inner })
}
}
unsafe impl DeviceOwned for StandardDescriptorPool {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
/// A descriptor set allocated from a `StandardDescriptorPool`.
#[derive(Debug)]
pub struct StandardDescriptorPoolAlloc {
// The actual descriptor alloc.
inner: PoolAlloc,
}
#[derive(Debug)]
enum PoolAlloc {
Fixed(SingleLayoutPoolAlloc),
Variable(SingleLayoutVariablePoolAlloc),
}
impl DescriptorPoolAlloc for StandardDescriptorPoolAlloc {
#[inline]
fn inner(&self) -> &UnsafeDescriptorSet {
match &self.inner {
PoolAlloc::Fixed(alloc) => alloc.inner(),
PoolAlloc::Variable(alloc) => alloc.inner(),
}
}
#[inline]
fn inner_mut(&mut self) -> &mut UnsafeDescriptorSet {
match &mut self.inner {
PoolAlloc::Fixed(alloc) => alloc.inner_mut(),
PoolAlloc::Variable(alloc) => alloc.inner_mut(),
}
}
}

View File

@ -8,10 +8,11 @@
// according to those terms.
use super::{
allocator::DescriptorSetAlloc,
layout::DescriptorSetLayout,
pool::{
DescriptorPoolAlloc, DescriptorPoolAllocError, DescriptorSetAllocateInfo,
UnsafeDescriptorPool, UnsafeDescriptorPoolCreateInfo,
DescriptorPool, DescriptorPoolAllocError, DescriptorPoolCreateInfo,
DescriptorSetAllocateInfo,
},
sys::UnsafeDescriptorSet,
DescriptorSet, DescriptorSetCreationError, DescriptorSetInner, DescriptorSetResources,
@ -23,7 +24,7 @@ use crate::{
};
use crossbeam_queue::ArrayQueue;
use std::{
cell::UnsafeCell,
cell::{Cell, UnsafeCell},
hash::{Hash, Hasher},
mem::ManuallyDrop,
sync::Arc,
@ -33,25 +34,29 @@ const MAX_SETS: usize = 32;
const MAX_POOLS: usize = 32;
/// `SingleLayoutDescSetPool` is a convenience wrapper provided by Vulkano not to be confused with
/// `VkDescriptorPool`. Its function is to provide access to pool(s) to allocate descriptor sets
/// from and optimizes for a specific layout which must not have a variable descriptor count. If
/// you need a variable descriptor count see [`SingleLayoutVariableDescSetPool`]. For a more general
/// purpose pool see [`StandardDescriptorPool`].
/// `SingleLayoutDescriptorSetPool` is a convenience wrapper provided by Vulkano not to be confused
/// with `VkDescriptorPool`. Its function is to provide access to pool(s) to allocate descriptor
/// sets from and optimizes for a specific layout which must not have a variable descriptor count.
/// If you need a variable descriptor count see [`SingleLayoutVariableDescriptorSetPool`]. For a
/// general-purpose descriptor set allocator see [`StandardDescriptorSetAllocator`].
///
/// [`StandardDescriptorPool`]: super::pool::standard::StandardDescriptorPool
/// [`StandardDescriptorSetAllocator`]: super::allocator::standard::StandardDescriptorSetAllocator
#[derive(Debug)]
pub struct SingleLayoutDescSetPool {
pub struct SingleLayoutDescriptorSetPool {
// The `SingleLayoutPool` struct contains an actual Vulkan pool. Every time it is full we create
// a new pool and replace the current one with the new one.
inner: Arc<SingleLayoutPool>,
inner: UnsafeCell<Arc<SingleLayoutPool>>,
// The amount of sets available to use when we create a new Vulkan pool.
set_count: usize,
set_count: Cell<usize>,
// The descriptor set layout that this pool is for.
layout: Arc<DescriptorSetLayout>,
}
impl SingleLayoutDescSetPool {
// This is needed because of the blanket impl on `Arc<T>`, which requires that `T` is `Send + Sync`.
// `SingleLayoutPool` is `Send + !Sync`.
unsafe impl Send for SingleLayoutDescriptorSetPool {}
impl SingleLayoutDescriptorSetPool {
/// Initializes a new pool. The pool is configured to allocate sets that corresponds to the
/// parameters passed to this function.
///
@ -70,12 +75,12 @@ impl SingleLayoutDescSetPool {
assert!(
layout.variable_descriptor_count() == 0,
"the provided descriptor set layout has a binding with a variable descriptor count, \
which cannot be used with SingleLayoutDescSetPool",
which cannot be used with SingleLayoutDescriptorSetPool",
);
Ok(Self {
inner: SingleLayoutPool::new(&layout, MAX_SETS)?,
set_count: MAX_SETS,
inner: UnsafeCell::new(SingleLayoutPool::new(&layout, MAX_SETS)?),
set_count: Cell::new(MAX_SETS),
layout,
})
}
@ -83,7 +88,7 @@ impl SingleLayoutDescSetPool {
/// Returns a new descriptor set, either by creating a new one or returning an existing one
/// from the internal reserve.
pub fn next(
&mut self,
&self,
descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>,
) -> Result<Arc<SingleLayoutDescSet>, DescriptorSetCreationError> {
let alloc = self.next_alloc()?;
@ -97,18 +102,19 @@ impl SingleLayoutDescSetPool {
Ok(Arc::new(SingleLayoutDescSet { alloc, inner }))
}
pub(crate) fn next_alloc(&mut self) -> Result<SingleLayoutPoolAlloc, OomError> {
pub(crate) fn next_alloc(&self) -> Result<SingleLayoutPoolAlloc, OomError> {
let inner = unsafe { &mut *self.inner.get() };
loop {
if let Some(existing) = self.inner.reserve.pop() {
if let Some(existing) = inner.reserve.pop() {
return Ok(SingleLayoutPoolAlloc {
pool: self.inner.clone(),
pool: inner.clone(),
inner: ManuallyDrop::new(existing),
});
}
self.set_count *= 2;
self.set_count.set(self.set_count.get() * 2);
self.inner = SingleLayoutPool::new(&self.layout, self.set_count)?;
*inner = SingleLayoutPool::new(&self.layout, self.set_count.get())?;
}
}
}
@ -117,7 +123,7 @@ impl SingleLayoutDescSetPool {
struct SingleLayoutPool {
// The actual Vulkan descriptor pool. This field isn't actually used anywhere, but we need to
// keep the pool alive in order to keep the descriptor sets valid.
_inner: UnsafeDescriptorPool,
_inner: DescriptorPool,
// List of descriptor sets. When `alloc` is called, a descriptor will be extracted from this
// list. When a `SingleLayoutPoolAlloc` is dropped, its descriptor set is put back in this list.
reserve: ArrayQueue<UnsafeDescriptorSet>,
@ -125,9 +131,9 @@ struct SingleLayoutPool {
impl SingleLayoutPool {
fn new(layout: &Arc<DescriptorSetLayout>, set_count: usize) -> Result<Arc<Self>, OomError> {
let mut inner = UnsafeDescriptorPool::new(
let inner = DescriptorPool::new(
layout.device().clone(),
UnsafeDescriptorPoolCreateInfo {
DescriptorPoolCreateInfo {
max_sets: set_count as u32,
pool_sizes: layout
.descriptor_counts()
@ -185,7 +191,12 @@ pub(crate) struct SingleLayoutPoolAlloc {
pool: Arc<SingleLayoutPool>,
}
impl DescriptorPoolAlloc for SingleLayoutPoolAlloc {
// This is required for the same reason as for `SingleLayoutDescriptorSetPool`.
unsafe impl Send for SingleLayoutPoolAlloc {}
// `DescriptorPool` is `!Sync`, but we never access it, only keep it alive.
unsafe impl Sync for SingleLayoutPoolAlloc {}
impl DescriptorSetAlloc for SingleLayoutPoolAlloc {
fn inner(&self) -> &UnsafeDescriptorSet {
&self.inner
}
@ -202,7 +213,7 @@ impl Drop for SingleLayoutPoolAlloc {
}
}
/// A descriptor set created from a [`SingleLayoutDescSetPool`].
/// A descriptor set created from a [`SingleLayoutDescriptorSetPool`].
pub struct SingleLayoutDescSet {
alloc: SingleLayoutPoolAlloc,
inner: DescriptorSetInner,
@ -249,26 +260,30 @@ impl Hash for SingleLayoutDescSet {
}
}
/// Much like [`SingleLayoutDescSetPool`], except that it allows you to allocate descriptor sets
/// with a variable descriptor count. As this has more overhead, you should only use this pool if
/// you need the functionality and prefer [`SingleLayoutDescSetPool`] otherwise. For a more general
/// purpose pool see [`StandardDescriptorPool`].
/// Much like [`SingleLayoutDescriptorSetPool`], except that it allows you to allocate descriptor
/// sets with a variable descriptor count. As this has more overhead, you should only use this pool
/// if you need the functionality and prefer [`SingleLayoutDescriptorSetPool`] otherwise. For a
/// more general purpose descriptor set allocator see [`StandardDescriptorSetAllocator`].
///
/// [`StandardDescriptorPool`]: super::pool::standard::StandardDescriptorPool
/// [`StandardDescriptorSetAllocator`]: super::allocator::standard::StandardDescriptorSetAllocator
#[derive(Debug)]
pub struct SingleLayoutVariableDescSetPool {
pub struct SingleLayoutVariableDescriptorSetPool {
// The `SingleLayoutVariablePool` struct contains an actual Vulkan pool. Every time it is full
// we grab one from the reserve, or create a new pool if there are none.
inner: Arc<SingleLayoutVariablePool>,
inner: UnsafeCell<Arc<SingleLayoutVariablePool>>,
// When a `SingleLayoutVariablePool` is dropped, it returns its Vulkan pool here for reuse.
reserve: Arc<ArrayQueue<UnsafeDescriptorPool>>,
reserve: Arc<ArrayQueue<DescriptorPool>>,
// The descriptor set layout that this pool is for.
layout: Arc<DescriptorSetLayout>,
// The number of sets currently allocated from the Vulkan pool.
allocated_sets: usize,
allocated_sets: Cell<usize>,
}
impl SingleLayoutVariableDescSetPool {
// This is needed because of the blanket impl on `Arc<T>`, which requires that `T` is `Send + Sync`.
// `SingleLayoutVariablePool` is `Send + !Sync`.
unsafe impl Send for SingleLayoutVariableDescriptorSetPool {}
impl SingleLayoutVariableDescriptorSetPool {
/// Initializes a new pool. The pool is configured to allocate sets that corresponds to the
/// parameters passed to this function.
///
@ -287,10 +302,10 @@ impl SingleLayoutVariableDescSetPool {
let reserve = Arc::new(ArrayQueue::new(MAX_POOLS));
Ok(Self {
inner: SingleLayoutVariablePool::new(&layout, reserve.clone())?,
inner: UnsafeCell::new(SingleLayoutVariablePool::new(&layout, reserve.clone())?),
reserve,
layout,
allocated_sets: 0,
allocated_sets: Cell::new(0),
})
}
@ -300,7 +315,7 @@ impl SingleLayoutVariableDescSetPool {
///
/// - Panics if the provided `variable_descriptor_count` exceeds the maximum for the layout.
pub fn next(
&mut self,
&self,
variable_descriptor_count: u32,
descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>,
) -> Result<SingleLayoutVariableDescSet, DescriptorSetCreationError> {
@ -326,30 +341,29 @@ impl SingleLayoutVariableDescSetPool {
}
pub(crate) fn next_alloc(
&mut self,
&self,
variable_descriptor_count: u32,
) -> Result<SingleLayoutVariablePoolAlloc, OomError> {
if self.allocated_sets >= MAX_SETS {
self.inner = if let Some(unsafe_pool) = self.reserve.pop() {
if self.allocated_sets.get() >= MAX_SETS {
*unsafe { &mut *self.inner.get() } = if let Some(unsafe_pool) = self.reserve.pop() {
Arc::new(SingleLayoutVariablePool {
inner: UnsafeCell::new(ManuallyDrop::new(unsafe_pool)),
inner: ManuallyDrop::new(unsafe_pool),
reserve: self.reserve.clone(),
})
} else {
SingleLayoutVariablePool::new(&self.layout, self.reserve.clone())?
};
self.allocated_sets = 0;
self.allocated_sets.set(0);
}
let inner = {
let unsafe_pool = unsafe { &mut *self.inner.inner.get() };
let allocate_info = DescriptorSetAllocateInfo {
layout: &self.layout,
variable_descriptor_count,
};
match unsafe { unsafe_pool.allocate_descriptor_sets([allocate_info]) } {
let pool = unsafe { &*self.inner.get() }.clone();
let inner = match unsafe { pool.inner.allocate_descriptor_sets([allocate_info]) } {
Ok(mut sets) => sets.next().unwrap(),
Err(DescriptorPoolAllocError::OutOfHostMemory) => {
return Err(OomError::OutOfHostMemory);
@ -365,34 +379,30 @@ impl SingleLayoutVariableDescSetPool {
// We created the pool to fit the maximum variable descriptor count.
unreachable!();
}
}
};
self.allocated_sets += 1;
self.allocated_sets.set(self.allocated_sets.get() + 1);
Ok(SingleLayoutVariablePoolAlloc {
inner,
_pool: self.inner.clone(),
})
Ok(SingleLayoutVariablePoolAlloc { inner, _pool: pool })
}
}
#[derive(Debug)]
struct SingleLayoutVariablePool {
// The actual Vulkan descriptor pool.
inner: UnsafeCell<ManuallyDrop<UnsafeDescriptorPool>>,
inner: ManuallyDrop<DescriptorPool>,
// Where we return the Vulkan descriptor pool in our `Drop` impl.
reserve: Arc<ArrayQueue<UnsafeDescriptorPool>>,
reserve: Arc<ArrayQueue<DescriptorPool>>,
}
impl SingleLayoutVariablePool {
fn new(
layout: &Arc<DescriptorSetLayout>,
reserve: Arc<ArrayQueue<UnsafeDescriptorPool>>,
reserve: Arc<ArrayQueue<DescriptorPool>>,
) -> Result<Arc<Self>, OomError> {
let unsafe_pool = UnsafeDescriptorPool::new(
let unsafe_pool = DescriptorPool::new(
layout.device().clone(),
UnsafeDescriptorPoolCreateInfo {
DescriptorPoolCreateInfo {
max_sets: MAX_SETS as u32,
pool_sizes: layout
.descriptor_counts()
@ -404,7 +414,7 @@ impl SingleLayoutVariablePool {
)?;
Ok(Arc::new(Self {
inner: UnsafeCell::new(ManuallyDrop::new(unsafe_pool)),
inner: ManuallyDrop::new(unsafe_pool),
reserve,
}))
}
@ -412,7 +422,7 @@ impl SingleLayoutVariablePool {
impl Drop for SingleLayoutVariablePool {
fn drop(&mut self) {
let mut inner = unsafe { ManuallyDrop::take(&mut *self.inner.get()) };
let inner = unsafe { ManuallyDrop::take(&mut self.inner) };
// TODO: This should not return `Result`, resetting a pool can't fail.
unsafe { inner.reset() }.unwrap();
@ -433,10 +443,12 @@ pub(crate) struct SingleLayoutVariablePoolAlloc {
_pool: Arc<SingleLayoutVariablePool>,
}
// This is required for the same reason as for `SingleLayoutVariableDescriptorSetPool`.
unsafe impl Send for SingleLayoutVariablePoolAlloc {}
// `DescriptorPool` is `!Sync`, but we never access it, only keep it alive.
unsafe impl Sync for SingleLayoutVariablePoolAlloc {}
impl DescriptorPoolAlloc for SingleLayoutVariablePoolAlloc {
impl DescriptorSetAlloc for SingleLayoutVariablePoolAlloc {
fn inner(&self) -> &UnsafeDescriptorSet {
&self.inner
}
@ -446,7 +458,7 @@ impl DescriptorPoolAlloc for SingleLayoutVariablePoolAlloc {
}
}
/// A descriptor set created from a [`SingleLayoutVariableDescSetPool`].
/// A descriptor set created from a [`SingleLayoutVariableDescriptorSetPool`].
pub struct SingleLayoutVariableDescSet {
alloc: SingleLayoutVariablePoolAlloc,
inner: DescriptorSetInner,

View File

@ -105,28 +105,22 @@ use self::physical::PhysicalDevice;
pub(crate) use self::{features::FeaturesFfi, properties::PropertiesFfi};
pub use self::{
features::{FeatureRestriction, FeatureRestrictionError, Features},
properties::Properties,
queue::{Queue, QueueError, QueueFamilyProperties, QueueFlags, QueueGuard},
};
use crate::{
command_buffer::pool::StandardCommandPool,
descriptor_set::pool::StandardDescriptorPool,
instance::Instance,
memory::{pool::StandardMemoryPool, ExternalMemoryHandleType},
OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject,
};
pub use crate::{
device::extensions::DeviceExtensions,
extensions::{ExtensionRestriction, ExtensionRestrictionError},
fns::DeviceFunctions,
};
use ahash::HashMap;
use crate::{
instance::Instance,
memory::{pool::StandardMemoryPool, ExternalMemoryHandleType},
OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject,
};
use ash::vk::Handle;
use parking_lot::Mutex;
use smallvec::SmallVec;
use std::{
cell::RefCell,
collections::hash_map::Entry,
error::Error,
ffi::CString,
fmt::{Display, Error as FmtError, Formatter},
@ -511,72 +505,6 @@ impl Device {
new_pool
}
/// Gives you access to the standard descriptor pool that is used by default if you don't
/// provide any other pool.
///
/// Pools are stored in thread-local storage to avoid locks, which means that a pool is only
/// dropped once both the thread exits and all descriptor sets allocated from it are dropped.
/// A pool is created lazily for each thread.
///
/// # Panics
///
/// - Panics if called again from within the callback.
pub fn with_standard_descriptor_pool<T>(
self: &Arc<Self>,
f: impl FnOnce(&mut StandardDescriptorPool) -> T,
) -> T {
thread_local! {
static TLS: RefCell<HashMap<ash::vk::Device, StandardDescriptorPool>> =
RefCell::new(HashMap::default());
}
TLS.with(|tls| {
let mut tls = tls.borrow_mut();
let pool = match tls.entry(self.internal_object()) {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => entry.insert(StandardDescriptorPool::new(self.clone())),
};
f(pool)
})
}
/// Gives you access to the standard command buffer pool used by default if you don't provide
/// any other pool.
///
/// Pools are stored in thread-local storage to avoid locks, which means that a pool is only
/// dropped once both the thread exits and all command buffers allocated from it are dropped.
/// A pool is created lazily for each thread, device and queue family combination as needed,
/// which is why this function might return an `OomError`.
///
/// # Panics
///
/// - Panics if the device and the queue family don't belong to the same physical device.
/// - Panics if called again from within the callback.
pub fn with_standard_command_pool<T>(
self: &Arc<Self>,
queue_family_index: u32,
f: impl FnOnce(&Arc<StandardCommandPool>) -> T,
) -> Result<T, OomError> {
thread_local! {
static TLS: RefCell<HashMap<(ash::vk::Device, u32), Arc<StandardCommandPool>>> =
RefCell::new(Default::default());
}
TLS.with(|tls| {
let mut tls = tls.borrow_mut();
let pool = match tls.entry((self.internal_object(), queue_family_index)) {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => entry.insert(Arc::new(StandardCommandPool::new(
self.clone(),
queue_family_index,
)?)),
};
Ok(f(pool))
})
}
/// Used to track the number of allocations on this device.
///
/// To ensure valid usage of the Vulkan API, we cannot call `vkAllocateMemory` when

View File

@ -11,7 +11,7 @@ use super::QueueFamilyProperties;
use crate::{
buffer::{ExternalBufferInfo, ExternalBufferProperties},
cache::OnceCache,
device::{DeviceExtensions, Features, FeaturesFfi, Properties, PropertiesFfi},
device::{properties::Properties, DeviceExtensions, Features, FeaturesFfi, PropertiesFfi},
format::{Format, FormatProperties},
image::{
ImageCreateFlags, ImageFormatInfo, ImageFormatProperties, ImageUsage,

View File

@ -15,8 +15,9 @@ use super::{
use crate::{
buffer::{BufferAccess, BufferContents, BufferUsage, CpuAccessibleBuffer},
command_buffer::{
AutoCommandBufferBuilder, BlitImageInfo, CommandBufferBeginError, CommandBufferExecFuture,
CommandBufferUsage, CopyBufferToImageInfo, ImageBlit, PrimaryCommandBuffer,
allocator::CommandBufferAllocator, AutoCommandBufferBuilder, BlitImageInfo,
CommandBufferBeginError, CommandBufferExecFuture, CommandBufferUsage,
CopyBufferToImageInfo, ImageBlit, PrimaryCommandBuffer,
},
device::{Device, DeviceOwned, Queue},
format::Format,
@ -59,12 +60,14 @@ fn has_mipmaps(mipmaps: MipmapsCount) -> bool {
}
}
fn generate_mipmaps<L>(
cbb: &mut AutoCommandBufferBuilder<L>,
fn generate_mipmaps<L, Cba>(
cbb: &mut AutoCommandBufferBuilder<L, Cba>,
image: Arc<dyn ImageAccess>,
dimensions: ImageDimensions,
_layout: ImageLayout,
) {
) where
Cba: CommandBufferAllocator,
{
for level in 1..image.mip_levels() {
let src_size = dimensions
.mip_level_dimensions(level - 1)
@ -98,53 +101,6 @@ fn generate_mipmaps<L>(
}
impl ImmutableImage {
#[deprecated(note = "use ImmutableImage::uninitialized instead")]
pub fn new(
device: Arc<Device>,
dimensions: ImageDimensions,
format: Format,
queue_family_indices: impl IntoIterator<Item = u32>,
) -> Result<Arc<ImmutableImage>, ImmutableImageCreationError> {
#[allow(deprecated)]
ImmutableImage::with_mipmaps(
device,
dimensions,
format,
MipmapsCount::One,
queue_family_indices,
)
}
#[deprecated(note = "use ImmutableImage::uninitialized instead")]
pub fn with_mipmaps(
device: Arc<Device>,
dimensions: ImageDimensions,
format: Format,
mip_levels: impl Into<MipmapsCount>,
queue_family_indices: impl IntoIterator<Item = u32>,
) -> Result<Arc<ImmutableImage>, ImmutableImageCreationError> {
let usage = ImageUsage {
transfer_src: true, // for blits
transfer_dst: true,
sampled: true,
..ImageUsage::empty()
};
let flags = ImageCreateFlags::empty();
let (image, _) = ImmutableImage::uninitialized(
device,
dimensions,
format,
mip_levels,
usage,
flags,
ImageLayout::ShaderReadOnlyOptimal,
queue_family_indices,
)?;
Ok(image)
}
/// Builds an uninitialized immutable image.
///
/// Returns two things: the image, and a special access that should be used for the initial
@ -226,6 +182,7 @@ impl ImmutableImage {
dimensions: ImageDimensions,
mip_levels: MipmapsCount,
format: Format,
command_buffer_allocator: &impl CommandBufferAllocator,
queue: Arc<Queue>,
) -> Result<(Arc<Self>, CommandBufferExecFuture<NowFuture>), ImmutableImageCreationError>
where
@ -242,7 +199,14 @@ impl ImmutableImage {
false,
iter,
)?;
ImmutableImage::from_buffer(source, dimensions, mip_levels, format, queue)
ImmutableImage::from_buffer(
source,
dimensions,
mip_levels,
format,
command_buffer_allocator,
queue,
)
}
/// Construct an ImmutableImage containing a copy of the data in `source`.
@ -251,6 +215,7 @@ impl ImmutableImage {
dimensions: ImageDimensions,
mip_levels: MipmapsCount,
format: Format,
command_buffer_allocator: &impl CommandBufferAllocator,
queue: Arc<Queue>,
) -> Result<(Arc<Self>, CommandBufferExecFuture<NowFuture>), ImmutableImageCreationError> {
let need_to_generate_mipmaps = has_mipmaps(mip_levels);
@ -279,7 +244,7 @@ impl ImmutableImage {
)?;
let mut cbb = AutoCommandBufferBuilder::primary(
source.device().clone(),
command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::MultipleSubmit,
)?;

View File

@ -867,6 +867,7 @@ pub struct SparseImageMemoryRequirements {
#[cfg(test)]
mod tests {
use crate::{
command_buffer::allocator::StandardCommandBufferAllocator,
format::Format,
image::{ImageAccess, ImageDimensions, ImmutableImage, MipmapsCount},
};
@ -973,7 +974,9 @@ mod tests {
#[test]
fn mipmap_working_immutable_image() {
let (_device, queue) = gfx_dev_and_queue!();
let (device, queue) = gfx_dev_and_queue!();
let cb_allocator = StandardCommandBufferAllocator::new(device);
let dimensions = ImageDimensions::Dim2d {
width: 512,
@ -990,6 +993,7 @@ mod tests {
dimensions,
MipmapsCount::One,
Format::R8_UNORM,
&cb_allocator,
queue.clone(),
)
.unwrap();
@ -1005,6 +1009,7 @@ mod tests {
dimensions,
MipmapsCount::Log2,
Format::R8_UNORM,
&cb_allocator,
queue,
)
.unwrap();

View File

@ -403,8 +403,12 @@ impl From<VulkanError> for ComputePipelineCreationError {
mod tests {
use crate::{
buffer::{BufferUsage, CpuAccessibleBuffer},
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
command_buffer::{
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
},
descriptor_set::{
allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet,
},
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
shader::{ShaderModule, SpecializationConstants, SpecializationMapEntry},
sync::{now, GpuFuture},
@ -498,14 +502,17 @@ mod tests {
)
.unwrap();
let ds_allocator = StandardDescriptorSetAllocator::new(device.clone());
let set = PersistentDescriptorSet::new(
&ds_allocator,
pipeline.layout().set_layouts().get(0).unwrap().clone(),
[WriteDescriptorSet::buffer(0, data_buffer.clone())],
)
.unwrap();
let cb_allocator = StandardCommandBufferAllocator::new(device.clone());
let mut cbb = AutoCommandBufferBuilder::primary(
device.clone(),
&cb_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)

View File

@ -26,6 +26,8 @@
//! # let device: std::sync::Arc<vulkano::device::Device> = return;
//! # let image_data: Vec<u8> = return;
//! # let queue: std::sync::Arc<vulkano::device::Queue> = return;
//! # let command_buffer_allocator: vulkano::command_buffer::allocator::StandardCommandBufferAllocator = return;
//! # let descriptor_set_allocator: vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator = return;
//! use vulkano::descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet};
//! use vulkano::descriptor_set::layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType};
//! use vulkano::format::Format;
@ -69,6 +71,7 @@
//! ImageDimensions::Dim2d { width: 1920, height: 1080, array_layers: 1 },
//! MipmapsCount::One,
//! Format::G8_B8_R8_3PLANE_420_UNORM,
//! &command_buffer_allocator,
//! queue.clone(),
//! ).unwrap();
//!
@ -79,6 +82,7 @@
//! let image_view = ImageView::new(image, create_info).unwrap();
//!
//! let descriptor_set = PersistentDescriptorSet::new(
//! &descriptor_set_allocator,
//! descriptor_set_layout.clone(),
//! [WriteDescriptorSet::image_view(0, image_view)],
//! ).unwrap();