Add host_cached field to all CpuAccessibleBuffer initializers to allow the user to perfer host cached memory.

This commit is contained in:
Austin Johnson 2020-01-26 08:12:44 -06:00 committed by GitHub
parent 044366a24c
commit 9977805518
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 79 additions and 45 deletions

View File

@ -11,6 +11,7 @@
- Pipeline barriers are now correctly inserted when a resource is used more than two times.
- Update Winit to 0.20.0
- Update MacOS dependency cocoa to 0.20
- Add `host_cached` field to all `CpuAccessibleBuffer` initializers to allow the user to perfer host cached memory.
- Propogate new lines correctly in shader compile errors.
- `Queue` and `QueueFamily` now implement `PartialEq` and `Eq`

View File

@ -97,7 +97,7 @@ fn main() {
// Iterator that produces the data.
let data_iter = (0 .. 65536u32).map(|n| n);
// Builds the buffer and fills it with this iterator.
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), data_iter).unwrap()
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, data_iter).unwrap()
};
// In order to let the shader access the buffer, we need to build a *descriptor set* that

View File

@ -41,7 +41,7 @@ impl AmbientLightingSystem {
// TODO: vulkano doesn't allow us to draw without a vertex buffer, otherwise we could
// hard-code these values in the shader
let vertex_buffer = {
CpuAccessibleBuffer::from_iter(gfx_queue.device().clone(), BufferUsage::all(), [
CpuAccessibleBuffer::from_iter(gfx_queue.device().clone(), BufferUsage::all(), false, [
Vertex { position: [-1.0, -1.0] },
Vertex { position: [-1.0, 3.0] },
Vertex { position: [3.0, -1.0] }

View File

@ -42,7 +42,7 @@ impl DirectionalLightingSystem {
// TODO: vulkano doesn't allow us to draw without a vertex buffer, otherwise we could
// hard-code these values in the shader
let vertex_buffer = {
CpuAccessibleBuffer::from_iter(gfx_queue.device().clone(), BufferUsage::all(), [
CpuAccessibleBuffer::from_iter(gfx_queue.device().clone(), BufferUsage::all(), false, [
Vertex { position: [-1.0, -1.0] },
Vertex { position: [-1.0, 3.0] },
Vertex { position: [3.0, -1.0] }

View File

@ -42,7 +42,7 @@ impl PointLightingSystem {
// TODO: vulkano doesn't allow us to draw without a vertex buffer, otherwise we could
// hard-code these values in the shader
let vertex_buffer = {
CpuAccessibleBuffer::from_iter(gfx_queue.device().clone(), BufferUsage::all(), [
CpuAccessibleBuffer::from_iter(gfx_queue.device().clone(), BufferUsage::all(), false, [
Vertex { position: [-1.0, -1.0] },
Vertex { position: [-1.0, 3.0] },
Vertex { position: [3.0, -1.0] }

View File

@ -33,7 +33,7 @@ impl TriangleDrawSystem {
where R: RenderPassAbstract + Send + Sync + 'static
{
let vertex_buffer = {
CpuAccessibleBuffer::from_iter(gfx_queue.device().clone(), BufferUsage::all(), [
CpuAccessibleBuffer::from_iter(gfx_queue.device().clone(), BufferUsage::all(), false, [
Vertex { position: [-0.5, -0.25] },
Vertex { position: [0.0, 0.5] },
Vertex { position: [0.25, -0.1] }

View File

@ -75,6 +75,7 @@ fn main() {
let vertex_buffer = CpuAccessibleBuffer::<[Vertex]>::from_iter(
device.clone(),
BufferUsage::all(),
false,
[
Vertex { position: [-0.5, -0.5 ] },
Vertex { position: [-0.5, 0.5 ] },

View File

@ -94,7 +94,7 @@ fn main() {
// We now create a buffer that will store the shape of our triangle.
// This triangle is identical to the one in the `triangle.rs` example.
let triangle_vertex_buffer = {
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), [
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, [
Vertex { position: [-0.5, -0.25] },
Vertex { position: [0.0, 0.5] },
Vertex { position: [0.25, -0.1] }
@ -119,7 +119,7 @@ fn main() {
data.push(InstanceData { position_offset, scale });
}
}
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), data.iter().cloned())
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, data.iter().cloned())
.unwrap()
};

View File

@ -191,7 +191,7 @@ fn main() {
let vertex1 = Vertex { position: [-0.5, -0.5] };
let vertex2 = Vertex { position: [ 0.0, 0.5] };
let vertex3 = Vertex { position: [ 0.5, -0.25] };
let vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(),
let vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false,
vec![vertex1, vertex2, vertex3].into_iter()).unwrap();
let pipeline = Arc::new(GraphicsPipeline::start()
@ -212,7 +212,7 @@ fn main() {
.. DynamicState::none()
};
let buf = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(),
let buf = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false,
(0 .. 1024 * 1024 * 4).map(|_| 0u8)).unwrap();
let command_buffer = AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family()).unwrap()

View File

@ -94,7 +94,7 @@ fn main() {
struct Vertex { position: [f32; 2] }
vulkano::impl_vertex!(Vertex, position);
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), [
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, [
Vertex { position: [-0.5, -0.25] },
Vertex { position: [0.0, 0.5] },
Vertex { position: [0.25, -0.1] }

View File

@ -64,7 +64,7 @@ fn main() {
let data_buffer = {
let data_iter = (0 .. 65536u32).map(|n| n);
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), data_iter).unwrap()
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, data_iter).unwrap()
};
let layout = pipeline.layout().descriptor_set_layout(0).unwrap();

View File

@ -367,6 +367,7 @@ fn main() {
let vertex_buffer = CpuAccessibleBuffer::from_iter(
device.clone(),
BufferUsage::all(),
false,
[
Vertex { position: [-1.0, 1.0], color: [1.0, 0.0, 0.0] },
Vertex { position: [ 0.0, -1.0], color: [0.0, 1.0, 0.0] },

View File

@ -72,7 +72,7 @@ fn main() {
let data_buffer = {
let data_iter = (0 .. 65536u32).map(|n| n);
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), data_iter).unwrap()
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, data_iter).unwrap()
};
let layout = pipeline.layout().descriptor_set_layout(0).unwrap();
let set = Arc::new(PersistentDescriptorSet::start(layout.clone())

View File

@ -68,7 +68,7 @@ fn main() {
let data_buffer = {
let data_iter = (0 .. 65536u32).map(|n| n);
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), data_iter).unwrap()
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, data_iter).unwrap()
};
let layout = pipeline.layout().descriptor_set_layout(0).unwrap();

View File

@ -75,13 +75,13 @@ fn main() {
};
let vertices = VERTICES.iter().cloned();
let vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), vertices).unwrap();
let vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, vertices).unwrap();
let normals = NORMALS.iter().cloned();
let normals_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), normals).unwrap();
let normals_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, normals).unwrap();
let indices = INDICES.iter().cloned();
let index_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), indices).unwrap();
let index_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, indices).unwrap();
let uniform_buffer = CpuBufferPool::<vs::ty::Data>::new(device.clone(), BufferUsage::all());

View File

@ -169,7 +169,7 @@ fn main() {
struct Vertex { position: [f32; 2] }
vulkano::impl_vertex!(Vertex, position);
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), [
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, [
Vertex { position: [-0.5, -0.25] },
Vertex { position: [ 0.0, 0.5] },
Vertex { position: [ 0.25, -0.1] },

View File

@ -165,7 +165,7 @@ fn main() {
struct Vertex { position: [f32; 2] }
vulkano::impl_vertex!(Vertex, position);
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), [
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, [
Vertex { position: [-0.5, -0.25] },
Vertex { position: [0.0, 0.5] },
Vertex { position: [0.25, -0.1] }

View File

@ -59,6 +59,12 @@ use sync::AccessError;
use sync::Sharing;
/// Buffer whose content is accessible by the CPU.
///
/// Setting the `host_cached` field on the various initializers to `true` will make it so
/// the `CpuAccessibleBuffer` perfers to allocate from host_cached memory. Host cached
/// memory caches GPU data on the CPU side. This can be more performant in cases where
/// the cpu needs to read data coming off the GPU.
#[derive(Debug)]
pub struct CpuAccessibleBuffer<T: ?Sized, A = PotentialDedicatedAllocation<StdMemoryPoolAlloc>> {
// Inner content.
@ -94,13 +100,13 @@ enum CurrentGpuAccess {
impl<T> CpuAccessibleBuffer<T> {
/// Builds a new buffer with some data in it. Only allowed for sized data.
pub fn from_data(device: Arc<Device>, usage: BufferUsage, data: T)
pub fn from_data(device: Arc<Device>, usage: BufferUsage, host_cached: bool, data: T)
-> Result<Arc<CpuAccessibleBuffer<T>>, DeviceMemoryAllocError>
where T: Content + 'static
{
unsafe {
let uninitialized =
CpuAccessibleBuffer::raw(device, mem::size_of::<T>(), usage, iter::empty())?;
CpuAccessibleBuffer::raw(device, mem::size_of::<T>(), usage, host_cached, iter::empty())?;
// Note that we are in panic-unsafety land here. However a panic should never ever
// happen here, so in theory we are safe.
@ -117,23 +123,23 @@ impl<T> CpuAccessibleBuffer<T> {
/// Builds a new uninitialized buffer. Only allowed for sized data.
#[inline]
pub unsafe fn uninitialized(device: Arc<Device>, usage: BufferUsage)
pub unsafe fn uninitialized(device: Arc<Device>, usage: BufferUsage, host_cached: bool)
-> Result<Arc<CpuAccessibleBuffer<T>>, DeviceMemoryAllocError> {
CpuAccessibleBuffer::raw(device, mem::size_of::<T>(), usage, iter::empty())
CpuAccessibleBuffer::raw(device, mem::size_of::<T>(), usage, host_cached, iter::empty())
}
}
impl<T> CpuAccessibleBuffer<[T]> {
/// Builds a new buffer that contains an array `T`. The initial data comes from an iterator
/// that produces that list of Ts.
pub fn from_iter<I>(device: Arc<Device>, usage: BufferUsage, data: I)
pub fn from_iter<I>(device: Arc<Device>, usage: BufferUsage, host_cached: bool, data: I)
-> Result<Arc<CpuAccessibleBuffer<[T]>>, DeviceMemoryAllocError>
where I: ExactSizeIterator<Item = T>,
T: Content + 'static
{
unsafe {
let uninitialized =
CpuAccessibleBuffer::uninitialized_array(device, data.len(), usage)?;
CpuAccessibleBuffer::uninitialized_array(device, data.len(), usage, host_cached)?;
// Note that we are in panic-unsafety land here. However a panic should never ever
// happen here, so in theory we are safe.
@ -154,9 +160,9 @@ impl<T> CpuAccessibleBuffer<[T]> {
/// Builds a new buffer. Can be used for arrays.
#[inline]
pub unsafe fn uninitialized_array(
device: Arc<Device>, len: usize, usage: BufferUsage)
device: Arc<Device>, len: usize, usage: BufferUsage, host_cached: bool)
-> Result<Arc<CpuAccessibleBuffer<[T]>>, DeviceMemoryAllocError> {
CpuAccessibleBuffer::raw(device, len * mem::size_of::<T>(), usage, iter::empty())
CpuAccessibleBuffer::raw(device, len * mem::size_of::<T>(), usage, host_cached, iter::empty())
}
}
@ -168,7 +174,7 @@ impl<T: ?Sized> CpuAccessibleBuffer<T> {
/// You must ensure that the size that you pass is correct for `T`.
///
pub unsafe fn raw<'a, I>(device: Arc<Device>, size: usize, usage: BufferUsage,
queue_families: I)
host_cached: bool, queue_families: I)
-> Result<Arc<CpuAccessibleBuffer<T>>, DeviceMemoryAllocError>
where I: IntoIterator<Item = QueueFamily<'a>>
{
@ -197,7 +203,19 @@ impl<T: ?Sized> CpuAccessibleBuffer<T> {
AllocLayout::Linear,
MappingRequirement::Map,
DedicatedAlloc::Buffer(&buffer),
|_| AllocFromRequirementsFilter::Allowed)?;
|m| if m.is_host_cached() {
if host_cached {
AllocFromRequirementsFilter::Preferred
} else {
AllocFromRequirementsFilter::Allowed
}
} else {
if host_cached {
AllocFromRequirementsFilter::Allowed
} else {
AllocFromRequirementsFilter::Preferred
}
})?;
debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
debug_assert!(mem.mapped_memory().is_some());
buffer.bind_memory(mem.memory(), mem.offset())?;
@ -579,6 +597,6 @@ mod tests {
const EMPTY: [i32; 0] = [];
let _ = CpuAccessibleBuffer::from_data(device, BufferUsage::all(), EMPTY.iter());
let _ = CpuAccessibleBuffer::from_data(device, BufferUsage::all(), false, EMPTY.iter());
}
}

View File

@ -97,6 +97,7 @@ impl<T: ?Sized> ImmutableBuffer<T> {
{
let source = CpuAccessibleBuffer::from_data(queue.device().clone(),
BufferUsage::transfer_source(),
false,
data)?;
ImmutableBuffer::from_buffer(source, usage, queue)
}
@ -180,6 +181,7 @@ impl<T> ImmutableBuffer<[T]> {
{
let source = CpuAccessibleBuffer::from_iter(queue.device().clone(),
BufferUsage::transfer_source(),
false,
data)?;
ImmutableBuffer::from_buffer(source, usage, queue)
}
@ -469,7 +471,7 @@ mod tests {
let (buffer, _) = ImmutableBuffer::from_data(12u32, BufferUsage::all(), queue.clone())
.unwrap();
let destination = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), 0)
let destination = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0)
.unwrap();
let _ = AutoCommandBufferBuilder::new(device.clone(), queue.family())
@ -498,6 +500,7 @@ mod tests {
let destination = CpuAccessibleBuffer::from_iter(device.clone(),
BufferUsage::all(),
false,
(0 .. 512).map(|_| 0u32))
.unwrap();
@ -549,7 +552,7 @@ mod tests {
ImmutableBuffer::<u32>::uninitialized(device.clone(), BufferUsage::all()).unwrap()
};
let source = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), 0).unwrap();
let source = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0).unwrap();
assert_should_panic!({
// TODO: check Result error instead of panicking
@ -575,7 +578,7 @@ mod tests {
ImmutableBuffer::<u32>::uninitialized(device.clone(), BufferUsage::all()).unwrap()
};
let source = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), 0).unwrap();
let source = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0).unwrap();
let _ = AutoCommandBufferBuilder::new(device.clone(), queue.family())
.unwrap()
@ -600,7 +603,7 @@ mod tests {
ImmutableBuffer::<u32>::uninitialized(device.clone(), BufferUsage::all()).unwrap()
};
let source = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), 0).unwrap();
let source = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0).unwrap();
let cb1 = AutoCommandBufferBuilder::new(device.clone(), queue.family())
.unwrap()

View File

@ -366,7 +366,7 @@ mod tests {
const EMPTY: [i32; 0] = [];
let buf =
CpuAccessibleBuffer::from_data(device, BufferUsage::vertex_buffer(), EMPTY.iter())
CpuAccessibleBuffer::from_data(device, BufferUsage::vertex_buffer(), false, EMPTY.iter())
.unwrap();
let mut cacher = StateCacher::new();
@ -390,7 +390,7 @@ mod tests {
const EMPTY: [i32; 0] = [];
let buf =
CpuAccessibleBuffer::from_data(device, BufferUsage::vertex_buffer(), EMPTY.iter())
CpuAccessibleBuffer::from_data(device, BufferUsage::vertex_buffer(), false, EMPTY.iter())
.unwrap();
let mut cacher = StateCacher::new();
@ -423,15 +423,19 @@ mod tests {
const EMPTY: [i32; 0] = [];
let buf1 = CpuAccessibleBuffer::from_data(device.clone(),
BufferUsage::vertex_buffer(),
false,
EMPTY.iter())
.unwrap();
let buf2 = CpuAccessibleBuffer::from_data(device.clone(),
BufferUsage::vertex_buffer(),
false,
EMPTY.iter())
.unwrap();
let buf3 = CpuAccessibleBuffer::from_data(device,
BufferUsage::vertex_buffer(),
false,
EMPTY.iter())
.unwrap();
let buf3 =
CpuAccessibleBuffer::from_data(device, BufferUsage::vertex_buffer(), EMPTY.iter())
.unwrap();
let mut cacher = StateCacher::new();

View File

@ -34,7 +34,7 @@ fn basic_conflict() {
let pool = Device::standard_command_pool(&device, queue.family());
let mut sync = SyncCommandBufferBuilder::new(&pool, Kind::primary(), Flags::None).unwrap();
let buf = CpuAccessibleBuffer::from_data(device, BufferUsage::all(), 0u32).unwrap();
let buf = CpuAccessibleBuffer::from_data(device, BufferUsage::all(), false, 0u32).unwrap();
match sync.copy_buffer(buf.clone(), buf.clone(), iter::once((0, 0, 4))) {
Err(SyncCommandBufferBuilderError::Conflict { .. }) => (),

View File

@ -78,7 +78,7 @@ mod tests {
fn missing_usage() {
let (device, queue) = gfx_dev_and_queue!();
let buffer =
CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::vertex_buffer(), 0u32)
CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::vertex_buffer(), false, 0u32)
.unwrap();
match check_fill_buffer(&device, &buffer) {
@ -91,7 +91,7 @@ mod tests {
fn wrong_device() {
let (dev1, queue) = gfx_dev_and_queue!();
let (dev2, _) = gfx_dev_and_queue!();
let buffer = CpuAccessibleBuffer::from_data(dev1, BufferUsage::all(), 0u32).unwrap();
let buffer = CpuAccessibleBuffer::from_data(dev1, BufferUsage::all(), false, 0u32).unwrap();
assert_should_panic!({
let _ = check_fill_buffer(&dev2, &buffer);

View File

@ -96,6 +96,7 @@ mod tests {
let (device, queue) = gfx_dev_and_queue!();
let buffer = CpuAccessibleBuffer::from_iter(device.clone(),
BufferUsage::index_buffer(),
false,
0 .. 500u32)
.unwrap();
@ -112,6 +113,7 @@ mod tests {
let (device, queue) = gfx_dev_and_queue!();
let buffer = CpuAccessibleBuffer::from_iter(device.clone(),
BufferUsage::vertex_buffer(),
false,
0 .. 500u32)
.unwrap();
@ -126,7 +128,7 @@ mod tests {
let (dev1, queue) = gfx_dev_and_queue!();
let (dev2, _) = gfx_dev_and_queue!();
let buffer = CpuAccessibleBuffer::from_iter(dev1, BufferUsage::all(), 0 .. 500u32).unwrap();
let buffer = CpuAccessibleBuffer::from_iter(dev1, BufferUsage::all(), false, 0 .. 500u32).unwrap();
assert_should_panic!({
let _ = check_index_buffer(&dev2, &buffer);

View File

@ -96,7 +96,7 @@ mod tests {
fn missing_usage() {
let (device, queue) = gfx_dev_and_queue!();
let buffer =
CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::vertex_buffer(), 0u32)
CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::vertex_buffer(), false, 0u32)
.unwrap();
match check_update_buffer(&device, &buffer, &0) {
@ -110,6 +110,7 @@ mod tests {
let (device, queue) = gfx_dev_and_queue!();
let buffer = CpuAccessibleBuffer::from_iter(device.clone(),
BufferUsage::transfer_destination(),
false,
0 .. 65536)
.unwrap();
let data = (0 .. 65536).collect::<Vec<u32>>();
@ -125,6 +126,7 @@ mod tests {
let (device, queue) = gfx_dev_and_queue!();
let buffer = CpuAccessibleBuffer::from_iter(device.clone(),
BufferUsage::transfer_destination(),
false,
(0 .. 100000).map(|_| 0))
.unwrap();
let data = (0 .. 65536).map(|_| 0).collect::<Vec<u8>>();
@ -140,6 +142,7 @@ mod tests {
let (device, queue) = gfx_dev_and_queue!();
let buffer = CpuAccessibleBuffer::from_iter(device.clone(),
BufferUsage::transfer_destination(),
false,
0 .. 100)
.unwrap();
let data = (0 .. 30).collect::<Vec<u8>>();
@ -154,7 +157,7 @@ mod tests {
fn wrong_device() {
let (dev1, queue) = gfx_dev_and_queue!();
let (dev2, _) = gfx_dev_and_queue!();
let buffer = CpuAccessibleBuffer::from_data(dev1, BufferUsage::all(), 0u32).unwrap();
let buffer = CpuAccessibleBuffer::from_data(dev1, BufferUsage::all(), false, 0u32).unwrap();
assert_should_panic!({
let _ = check_update_buffer(&dev2, &buffer, &0);

View File

@ -201,6 +201,7 @@ impl<F> ImmutableImage<F> {
{
let source = CpuAccessibleBuffer::from_iter(queue.device().clone(),
BufferUsage::transfer_source(),
false,
iter)?;
ImmutableImage::from_buffer(source, dimensions, format, queue)
}

View File

@ -969,7 +969,7 @@ mod tests {
&SpecConsts { VALUE: 0x12345678 })
.unwrap());
let data_buffer = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), 0)
let data_buffer = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0)
.unwrap();
let layout = pipeline.layout().descriptor_set_layout(0).unwrap();
let set = PersistentDescriptorSet::start(layout.clone())