Document shader safety requirements, make draw/dispatch unsafe (#2429)

* Document shader safety requirements, make draw/dispatch unsafe

* Extra docs

* Doctests

* Max index value

* Small change

* Update vulkano/src/command_buffer/mod.rs

Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com>

* Update vulkano/src/command_buffer/mod.rs

Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com>

---------

Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com>
This commit is contained in:
Rua 2023-12-25 04:01:16 +01:00 committed by GitHub
parent 94a8fb9a43
commit 289ec102e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 591 additions and 228 deletions

View File

@ -602,6 +602,7 @@ fn main() -> Result<(), impl Error> {
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -631,13 +632,15 @@ fn main() -> Result<(), impl Error> {
)
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
acquire_future.wait(None).unwrap();
previous_frame_end.as_mut().unwrap().cleanup_finished();

View File

@ -206,14 +206,12 @@ fn main() {
},
)
.unwrap();
// Note that we clone the pipeline and the set. Since they are both wrapped in an `Arc`,
// this only clones the `Arc` and not the whole pipeline or set (which aren't cloneable
// anyway). In this example we would avoid cloning them since this is the last time we use
// them, but in real code you would probably need to clone them.
builder
// The command buffer only does one thing: execute the compute pipeline. This is called a
// *dispatch* operation.
//
// Note that we clone the pipeline and the set. Since they are both wrapped in an `Arc`,
// this only clones the `Arc` and not the whole pipeline or set (which aren't cloneable
// anyway). In this example we would avoid cloning them since this is the last time we use
// them, but in real code you would probably need to clone them.
.bind_pipeline_compute(pipeline.clone())
.unwrap()
.bind_descriptor_sets(
@ -222,10 +220,14 @@ fn main() {
0,
set,
)
.unwrap()
.dispatch([1024, 1, 1])
.unwrap();
unsafe {
// The command buffer only does one thing: execute the compute pipeline. This is called a
// *dispatch* operation.
builder.dispatch([1024, 1, 1]).unwrap();
}
// Finish building the command buffer by calling `build`.
let command_buffer = builder.end().unwrap();

View File

@ -379,6 +379,7 @@ fn main() -> Result<(), impl Error> {
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -396,13 +397,15 @@ fn main() -> Result<(), impl Error> {
.bind_pipeline_graphics(pipeline.clone())
.unwrap()
.bind_vertex_buffers(0, buffer)
.unwrap()
.draw(num_vertices, 1, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder.draw(num_vertices, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_frame_end
.take()
.unwrap()

View File

@ -197,6 +197,7 @@ impl AmbientLightingSystem {
},
)
.unwrap();
builder
.set_viewport(0, [viewport].into_iter().collect())
.unwrap()
@ -212,9 +213,14 @@ impl AmbientLightingSystem {
.push_constants(self.pipeline.layout().clone(), 0, push_constants)
.unwrap()
.bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap()
.draw(self.vertex_buffer.len() as u32, 1, 0, 0)
.unwrap();
unsafe {
builder
.draw(self.vertex_buffer.len() as u32, 1, 0, 0)
.unwrap();
}
builder.end().unwrap()
}
}

View File

@ -211,6 +211,7 @@ impl DirectionalLightingSystem {
},
)
.unwrap();
builder
.set_viewport(0, [viewport].into_iter().collect())
.unwrap()
@ -226,9 +227,14 @@ impl DirectionalLightingSystem {
.push_constants(self.pipeline.layout().clone(), 0, push_constants)
.unwrap()
.bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap()
.draw(self.vertex_buffer.len() as u32, 1, 0, 0)
.unwrap();
unsafe {
builder
.draw(self.vertex_buffer.len() as u32, 1, 0, 0)
.unwrap();
}
builder.end().unwrap()
}
}

View File

@ -224,6 +224,7 @@ impl PointLightingSystem {
},
)
.unwrap();
builder
.set_viewport(0, [viewport].into_iter().collect())
.unwrap()
@ -239,9 +240,14 @@ impl PointLightingSystem {
.push_constants(self.pipeline.layout().clone(), 0, push_constants)
.unwrap()
.bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap()
.draw(self.vertex_buffer.len() as u32, 1, 0, 0)
.unwrap();
unsafe {
builder
.draw(self.vertex_buffer.len() as u32, 1, 0, 0)
.unwrap();
}
builder.end().unwrap()
}
}

View File

@ -143,6 +143,7 @@ impl TriangleDrawSystem {
},
)
.unwrap();
builder
.set_viewport(
0,
@ -158,9 +159,14 @@ impl TriangleDrawSystem {
.bind_pipeline_graphics(self.pipeline.clone())
.unwrap()
.bind_vertex_buffers(0, self.vertex_buffer.clone())
.unwrap()
.draw(self.vertex_buffer.len() as u32, 1, 0, 0)
.unwrap();
unsafe {
builder
.draw(self.vertex_buffer.len() as u32, 1, 0, 0)
.unwrap();
}
builder.end().unwrap()
}
}

View File

@ -248,37 +248,23 @@ fn main() {
)
.unwrap();
#[allow(clippy::erasing_op, clippy::identity_op)]
builder
.bind_pipeline_compute(pipeline.clone())
.unwrap()
.bind_descriptor_sets(
PipelineBindPoint::Compute,
pipeline.layout().clone(),
0,
set.clone().offsets([0 * align as u32]),
)
.unwrap()
.dispatch([12, 1, 1])
.unwrap()
.bind_descriptor_sets(
PipelineBindPoint::Compute,
pipeline.layout().clone(),
0,
set.clone().offsets([1 * align as u32]),
)
.unwrap()
.dispatch([12, 1, 1])
.unwrap()
.bind_descriptor_sets(
PipelineBindPoint::Compute,
pipeline.layout().clone(),
0,
set.offsets([2 * align as u32]),
)
.unwrap()
.dispatch([12, 1, 1])
.unwrap();
builder.bind_pipeline_compute(pipeline.clone()).unwrap();
for index in 0..3 {
builder
.bind_descriptor_sets(
PipelineBindPoint::Compute,
pipeline.layout().clone(),
0,
set.clone().offsets([index * align as u32]),
)
.unwrap();
unsafe {
builder.dispatch([12, 1, 1]).unwrap();
}
}
let command_buffer = builder.end().unwrap();
let future = sync::now(device)

View File

@ -259,6 +259,7 @@ fn main() {
},
)
.unwrap();
builder
.bind_pipeline_compute(pipeline.clone())
.unwrap()
@ -268,12 +269,19 @@ fn main() {
0,
set,
)
.unwrap()
.unwrap();
unsafe {
// Note that dispatch dimensions must be proportional to the local size.
.dispatch([1024 / local_size_x, 1024 / local_size_y, 1])
.unwrap()
builder
.dispatch([1024 / local_size_x, 1024 / local_size_y, 1])
.unwrap();
}
builder
.copy_image_to_buffer(CopyImageToBufferInfo::image_buffer(image, buf.clone()))
.unwrap();
let command_buffer = builder.end().unwrap();
let future = sync::now(device)

View File

@ -401,6 +401,7 @@ mod linux {
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -424,13 +425,15 @@ mod linux {
)
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_frame_end.take().unwrap().join(acquire_future);
let future = future

View File

@ -485,6 +485,7 @@ fn main() -> Result<(), impl Error> {
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -508,13 +509,15 @@ fn main() -> Result<(), impl Error> {
)
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_frame_end
.take()
.unwrap()

View File

@ -432,6 +432,7 @@ fn main() -> Result<(), impl Error> {
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -455,13 +456,15 @@ fn main() -> Result<(), impl Error> {
)
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_frame_end
.take()
.unwrap()

View File

@ -450,6 +450,7 @@ fn main() -> Result<(), impl Error> {
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -473,13 +474,15 @@ fn main() -> Result<(), impl Error> {
)
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_frame_end
.take()
.unwrap()

View File

@ -489,9 +489,13 @@ fn main() -> Result<(), impl Error> {
0,
cs_desciptor_set,
)
.unwrap()
.dispatch([1, 1, 1])
.unwrap()
.unwrap();
unsafe {
builder.dispatch([1, 1, 1]).unwrap();
}
builder
.begin_render_pass(
RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())],
@ -507,13 +511,16 @@ fn main() -> Result<(), impl Error> {
.bind_pipeline_graphics(render_pipeline.clone())
.unwrap()
.bind_vertex_buffers(0, vertices)
.unwrap()
.unwrap();
unsafe {
// The indirect draw call is placed in the command buffer with a reference to
// the buffer that will contain the arguments for the draw.
.draw_indirect(indirect_buffer)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
builder.draw_indirect(indirect_buffer).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_frame_end

View File

@ -412,6 +412,7 @@ fn main() -> Result<(), impl Error> {
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -429,18 +430,22 @@ fn main() -> Result<(), impl Error> {
.unwrap()
// We pass both our lists of vertices here.
.bind_vertex_buffers(0, (vertex_buffer.clone(), instance_buffer.clone()))
.unwrap()
.draw(
vertex_buffer.len() as u32,
instance_buffer.len() as u32,
0,
0,
)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder
.draw(
vertex_buffer.len() as u32,
instance_buffer.len() as u32,
0,
0,
)
.unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_frame_end
.take()
.unwrap()

View File

@ -168,15 +168,21 @@ impl FractalComputePipeline {
max_iters: max_iters as i32,
is_julia: is_julia as u32,
};
builder
.bind_pipeline_compute(self.pipeline.clone())
.unwrap()
.bind_descriptor_sets(PipelineBindPoint::Compute, pipeline_layout.clone(), 0, set)
.unwrap()
.push_constants(pipeline_layout.clone(), 0, push_constants)
.unwrap()
.dispatch([image_extent[0] / 8, image_extent[1] / 8, 1])
.unwrap();
unsafe {
builder
.dispatch([image_extent[0] / 8, image_extent[1] / 8, 1])
.unwrap();
}
let command_buffer = builder.end().unwrap();
let finished = command_buffer.execute(self.queue.clone()).unwrap();
finished.then_signal_fence_and_flush().unwrap().boxed()

View File

@ -215,7 +215,7 @@ impl PixelsDrawPipeline {
},
)
.unwrap();
let desc_set = self.create_descriptor_set(image);
builder
.set_viewport(
0,
@ -234,15 +234,20 @@ impl PixelsDrawPipeline {
PipelineBindPoint::Graphics,
self.pipeline.layout().clone(),
0,
desc_set,
self.create_descriptor_set(image),
)
.unwrap()
.bind_vertex_buffers(0, self.vertices.clone())
.unwrap()
.bind_index_buffer(self.indices.clone())
.unwrap()
.draw_indexed(self.indices.len() as u32, 1, 0, 0, 0)
.unwrap();
unsafe {
builder
.draw_indexed(self.indices.len() as u32, 1, 0, 0, 0)
.unwrap();
}
builder.end().unwrap()
}
}

View File

@ -390,6 +390,7 @@ fn main() {
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -404,15 +405,19 @@ fn main() {
.bind_pipeline_graphics(pipeline)
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.unwrap();
unsafe {
builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap();
}
builder
.end_render_pass(Default::default())
.unwrap()
.copy_image_to_buffer(CopyImageToBufferInfo::image_buffer(image, buf.clone()))
.unwrap();
let command_buffer = builder.end().unwrap();
let command_buffer = builder.end().unwrap();
let finished = command_buffer.execute(queue).unwrap();
finished
.then_signal_fence_and_flush()

View File

@ -200,9 +200,13 @@ impl GameOfLifeComputePipeline {
.bind_descriptor_sets(PipelineBindPoint::Compute, pipeline_layout.clone(), 0, set)
.unwrap()
.push_constants(pipeline_layout.clone(), 0, push_constants)
.unwrap()
.dispatch([image_extent[0] / 8, image_extent[1] / 8, 1])
.unwrap();
unsafe {
builder
.dispatch([image_extent[0] / 8, image_extent[1] / 8, 1])
.unwrap();
}
}
}

View File

@ -211,7 +211,7 @@ impl PixelsDrawPipeline {
},
)
.unwrap();
let desc_set = self.create_image_sampler_nearest(image);
builder
.set_viewport(
0,
@ -230,15 +230,20 @@ impl PixelsDrawPipeline {
PipelineBindPoint::Graphics,
self.pipeline.layout().clone(),
0,
desc_set,
self.create_image_sampler_nearest(image),
)
.unwrap()
.bind_vertex_buffers(0, self.vertices.clone())
.unwrap()
.bind_index_buffer(self.indices.clone())
.unwrap()
.draw_indexed(self.indices.len() as u32, 1, 0, 0, 0)
.unwrap();
unsafe {
builder
.draw_indexed(self.indices.len() as u32, 1, 0, 0, 0)
.unwrap();
}
builder.end().unwrap()
}
}

View File

@ -474,13 +474,15 @@ fn main() -> Result<(), impl Error> {
.bind_pipeline_graphics(pipeline.clone())
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_frame_end
.take()
.unwrap()

View File

@ -342,8 +342,6 @@ fn main() {
)
.unwrap();
// Drawing commands are broadcast to each view in the view mask of the active renderpass which
// means only a single draw call is needed to draw to multiple layers of the framebuffer.
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -356,12 +354,16 @@ fn main() {
.bind_pipeline_graphics(pipeline)
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
unsafe {
// Drawing commands are broadcast to each view in the view mask of the active renderpass which
// means only a single draw call is needed to draw to multiple layers of the framebuffer.
builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
// Copy the image layers to different buffers to save them as individual images to disk.
builder
.copy_image_to_buffer(CopyImageToBufferInfo {

View File

@ -191,6 +191,7 @@ fn main() {
},
)
.unwrap();
builder
.bind_pipeline_compute(pipeline.clone())
.unwrap()
@ -202,9 +203,12 @@ fn main() {
)
.unwrap()
.push_constants(pipeline.layout().clone(), 0, push_constants)
.unwrap()
.dispatch([1024, 1, 1])
.unwrap();
unsafe {
builder.dispatch([1024, 1, 1]).unwrap();
}
let command_buffer = builder.end().unwrap();
let future = sync::now(device)

View File

@ -419,6 +419,7 @@ fn main() -> Result<(), impl Error> {
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -449,13 +450,15 @@ fn main() -> Result<(), impl Error> {
)
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_frame_end
.take()
.unwrap()

View File

@ -553,6 +553,7 @@ fn main() -> Result<(), impl Error> {
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -576,13 +577,15 @@ fn main() -> Result<(), impl Error> {
)
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_frame_end
.take()
.unwrap()

View File

@ -358,6 +358,7 @@ fn main() -> Result<(), impl Error> {
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -374,13 +375,15 @@ fn main() -> Result<(), impl Error> {
.bind_pipeline_graphics(graphics_pipeline.clone())
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_frame_end
.take()
.unwrap()

View File

@ -172,6 +172,7 @@ fn main() {
},
)
.unwrap();
builder
// Copy from the first half to the second half (inside the same buffer) before we run the
// computation.
@ -194,9 +195,12 @@ fn main() {
0,
set,
)
.unwrap()
.dispatch([1024, 1, 1])
.unwrap();
unsafe {
builder.dispatch([1024, 1, 1]).unwrap();
}
let command_buffer = builder.end().unwrap();
let future = sync::now(device)

View File

@ -177,6 +177,7 @@ fn main() {
},
)
.unwrap();
builder
.bind_pipeline_compute(pipeline.clone())
.unwrap()
@ -186,9 +187,12 @@ fn main() {
0,
set,
)
.unwrap()
.dispatch([1024, 1, 1])
.unwrap();
unsafe {
builder.dispatch([1024, 1, 1]).unwrap();
}
let command_buffer = builder.end().unwrap();
let future = sync::now(device)
.then_execute(queue, command_buffer)

View File

@ -203,6 +203,7 @@ fn main() {
},
)
.unwrap();
builder
.bind_pipeline_compute(pipeline.clone())
.unwrap()
@ -214,11 +215,13 @@ fn main() {
)
.unwrap()
.push_constants(pipeline.layout().clone(), 0, parameters)
.unwrap()
.dispatch([1024, 1, 1])
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder.dispatch([1024, 1, 1]).unwrap();
}
let command_buffer = builder.end().unwrap();
let future = sync::now(queue.device().clone())
.then_execute(queue.clone(), command_buffer)
.unwrap()

View File

@ -595,6 +595,7 @@ fn main() -> Result<(), impl Error> {
},
)
.unwrap();
builder
// Push constants for compute shader.
.push_constants(compute_pipeline.layout().clone(), 0, push_constants)
@ -608,10 +609,16 @@ fn main() -> Result<(), impl Error> {
0, // Bind this descriptor set to index 0.
descriptor_set.clone(),
)
.unwrap()
.dispatch([PARTICLE_COUNT as u32 / 128, 1, 1])
.unwrap()
// Use render-pass to draw particles to swapchain.
.unwrap();
unsafe {
builder
.dispatch([PARTICLE_COUNT as u32 / 128, 1, 1])
.unwrap();
}
// Use render-pass to draw particles to swapchain.
builder
.begin_render_pass(
RenderPassBeginInfo {
clear_values: vec![Some([0., 0., 0., 1.].into())],
@ -625,13 +632,15 @@ fn main() -> Result<(), impl Error> {
.bind_pipeline_graphics(graphics_pipeline.clone())
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
.draw(PARTICLE_COUNT as u32, 1, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder.draw(PARTICLE_COUNT as u32, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_future
.join(acquire_future)
.then_execute(queue.clone(), command_buffer)

View File

@ -178,6 +178,7 @@ fn main() {
},
)
.unwrap();
builder
.bind_pipeline_compute(pipeline.clone())
.unwrap()
@ -187,9 +188,12 @@ fn main() {
0,
set,
)
.unwrap()
.dispatch([1024, 1, 1])
.unwrap();
unsafe {
builder.dispatch([1024, 1, 1]).unwrap();
}
let command_buffer = builder.end().unwrap();
let future = sync::now(device)

View File

@ -375,6 +375,7 @@ fn main() -> Result<(), impl Error> {
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -401,13 +402,17 @@ fn main() -> Result<(), impl Error> {
.bind_vertex_buffers(0, (vertex_buffer.clone(), normals_buffer.clone()))
.unwrap()
.bind_index_buffer(index_buffer.clone())
.unwrap()
.draw_indexed(index_buffer.len() as u32, 1, 0, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder
.draw_indexed(index_buffer.len() as u32, 1, 0, 0, 0)
.unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_frame_end
.take()
.unwrap()

View File

@ -476,6 +476,7 @@ fn main() -> Result<(), impl Error> {
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -492,13 +493,15 @@ fn main() -> Result<(), impl Error> {
.bind_pipeline_graphics(pipeline.clone())
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_frame_end
.take()
.unwrap()

View File

@ -443,6 +443,7 @@ fn main() -> Result<(), impl Error> {
},
)
.unwrap();
builder
.begin_render_pass(
RenderPassBeginInfo {
@ -466,13 +467,15 @@ fn main() -> Result<(), impl Error> {
)
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
.draw(vertex_buffer.len() as u32, 3, 0, 0)
.unwrap()
.end_render_pass(Default::default())
.unwrap();
let command_buffer = builder.end().unwrap();
unsafe {
builder.draw(vertex_buffer.len() as u32, 3, 0, 0).unwrap();
}
builder.end_render_pass(Default::default()).unwrap();
let command_buffer = builder.end().unwrap();
let future = previous_frame_end
.take()
.unwrap()

View File

@ -588,10 +588,10 @@ fn main() -> Result<(), impl Error> {
recreate_swapchain = true;
}
// In order to draw, we have to build a *command buffer*. The command buffer object
// In order to draw, we have to record a *command buffer*. The command buffer object
// holds the list of commands that are going to be executed.
//
// Building a command buffer is an expensive operation (usually a few hundred
// Recording a command buffer is an expensive operation (usually a few hundred
// microseconds), but it is known to be a hot path in the driver and is expected to
// be optimized.
//
@ -645,15 +645,21 @@ fn main() -> Result<(), impl Error> {
.bind_pipeline_graphics(pipeline.clone())
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
// We add a draw command.
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.unwrap();
unsafe {
builder
// We add a draw command.
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap();
}
builder
// We leave the render pass.
.end_rendering()
.unwrap();
// Finish building the command buffer by calling `build`.
// Finish recording the command buffer by calling `end`.
let command_buffer = builder.end().unwrap();
let future = previous_frame_end

View File

@ -591,10 +591,10 @@ fn main() -> Result<(), impl Error> {
recreate_swapchain = true;
}
// In order to draw, we have to build a *command buffer*. The command buffer object
// In order to draw, we have to record a *command buffer*. The command buffer object
// holds the list of commands that are going to be executed.
//
// Building a command buffer is an expensive operation (usually a few hundred
// Recording a command buffer is an expensive operation (usually a few hundred
// microseconds), but it is known to be a hot path in the driver and is expected to
// be optimized.
//
@ -644,16 +644,22 @@ fn main() -> Result<(), impl Error> {
.bind_pipeline_graphics(pipeline.clone())
.unwrap()
.bind_vertex_buffers(0, vertex_buffer.clone())
.unwrap()
// We add a draw command.
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.unwrap();
unsafe {
builder
// We add a draw command.
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap();
}
builder
// We leave the render pass. Note that if we had multiple subpasses we could
// have called `next_subpass` to jump to the next subpass.
.end_render_pass(Default::default())
.unwrap();
// Finish building the command buffer by calling `build`.
// Finish recording the command buffer by calling `end`.
let command_buffer = builder.end().unwrap();
let future = previous_frame_end

View File

@ -1,3 +1,5 @@
#[cfg(doc)]
use crate::device::{Features, Properties};
use crate::{
acceleration_structure::AccelerationStructure,
buffer::{view::BufferView, BufferUsage, Subbuffer},
@ -50,7 +52,14 @@ impl RecordingCommandBuffer {
/// A compute pipeline must have been bound using
/// [`bind_pipeline_compute`](Self::bind_pipeline_compute). Any resources used by the compute
/// pipeline, such as descriptor sets, must have been set beforehand.
pub fn dispatch(&mut self, group_counts: [u32; 3]) -> Result<&mut Self, Box<ValidationError>> {
///
/// # Safety
///
/// - The general [shader safety requirements](crate::shader#safety) apply.
pub unsafe fn dispatch(
&mut self,
group_counts: [u32; 3],
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_dispatch(group_counts)?;
unsafe { Ok(self.dispatch_unchecked(group_counts)) }
@ -116,7 +125,13 @@ impl RecordingCommandBuffer {
/// A compute pipeline must have been bound using
/// [`bind_pipeline_compute`](Self::bind_pipeline_compute). Any resources used by the compute
/// pipeline, such as descriptor sets, must have been set beforehand.
pub fn dispatch_indirect(
///
/// # Safety
///
/// - The general [shader safety requirements](crate::shader#safety) apply.
/// - The [safety requirements for `DispatchIndirectCommand`](DispatchIndirectCommand#safety)
/// apply.
pub unsafe fn dispatch_indirect(
&mut self,
indirect_buffer: Subbuffer<[DispatchIndirectCommand]>,
) -> Result<&mut Self, Box<ValidationError>> {
@ -197,7 +212,11 @@ impl RecordingCommandBuffer {
/// pipeline, such as descriptor sets, vertex buffers and dynamic state, must have been set
/// beforehand. If the bound graphics pipeline uses vertex buffers, then the provided vertex and
/// instance ranges must be in range of the bound vertex buffers.
pub fn draw(
///
/// # Safety
///
/// - The general [shader safety requirements](crate::shader#safety) apply.
pub unsafe fn draw(
&mut self,
vertex_count: u32,
instance_count: u32,
@ -373,9 +392,9 @@ impl RecordingCommandBuffer {
///
/// One draw is performed for each [`DrawIndirectCommand`] struct in `indirect_buffer`.
/// The maximum number of draw commands in the buffer is limited by the
/// [`max_draw_indirect_count`](crate::device::Properties::max_draw_indirect_count) limit.
/// [`max_draw_indirect_count`](Properties::max_draw_indirect_count) limit.
/// This limit is 1 unless the
/// [`multi_draw_indirect`](crate::device::Features::multi_draw_indirect) feature has been
/// [`multi_draw_indirect`](Features::multi_draw_indirect) feature has been
/// enabled.
///
/// A graphics pipeline must have been bound using
@ -384,7 +403,13 @@ impl RecordingCommandBuffer {
/// beforehand. If the bound graphics pipeline uses vertex buffers, then the vertex and instance
/// ranges of each `DrawIndirectCommand` in the indirect buffer must be in range of the bound
/// vertex buffers.
pub fn draw_indirect(
///
/// # Safety
///
/// - The general [shader safety requirements](crate::shader#safety) apply.
/// - The [safety requirements for `DrawIndirectCommand`](DrawIndirectCommand#safety)
/// apply.
pub unsafe fn draw_indirect(
&mut self,
indirect_buffer: Subbuffer<[DrawIndirectCommand]>,
) -> Result<&mut Self, Box<ValidationError>> {
@ -489,7 +514,16 @@ impl RecordingCommandBuffer {
/// beforehand. If the bound graphics pipeline uses vertex buffers, then the provided instance
/// range must be in range of the bound vertex buffers. The vertex indices in the index buffer
/// must be in range of the bound vertex buffers.
pub fn draw_indexed(
///
/// # Safety
///
/// - The general [shader safety requirements](crate::shader#safety) apply.
/// - Every vertex number that is retrieved from the index buffer must fall within the range of
/// the bound vertex-rate vertex buffers.
/// - Every vertex number that is retrieved from the index buffer, if it is not the special
/// primitive restart value, must be no greater than the
/// [`max_draw_indexed_index_value`](Properties::max_draw_indexed_index_value) device limit.
pub unsafe fn draw_indexed(
&mut self,
index_count: u32,
instance_count: u32,
@ -702,9 +736,9 @@ impl RecordingCommandBuffer {
///
/// One draw is performed for each [`DrawIndexedIndirectCommand`] struct in `indirect_buffer`.
/// The maximum number of draw commands in the buffer is limited by the
/// [`max_draw_indirect_count`](crate::device::Properties::max_draw_indirect_count) limit.
/// [`max_draw_indirect_count`](Properties::max_draw_indirect_count) limit.
/// This limit is 1 unless the
/// [`multi_draw_indirect`](crate::device::Features::multi_draw_indirect) feature has been
/// [`multi_draw_indirect`](Features::multi_draw_indirect) feature has been
/// enabled.
///
/// An index buffer must have been bound using
@ -718,7 +752,13 @@ impl RecordingCommandBuffer {
/// beforehand. If the bound graphics pipeline uses vertex buffers, then the instance ranges of
/// each `DrawIndexedIndirectCommand` in the indirect buffer must be in range of the bound
/// vertex buffers.
pub fn draw_indexed_indirect(
///
/// # Safety
///
/// - The general [shader safety requirements](crate::shader#safety) apply.
/// - The [safety requirements for `DrawIndexedIndirectCommand`](DrawIndexedIndirectCommand#safety)
/// apply.
pub unsafe fn draw_indexed_indirect(
&mut self,
indirect_buffer: Subbuffer<[DrawIndexedIndirectCommand]>,
) -> Result<&mut Self, Box<ValidationError>> {
@ -1070,7 +1110,7 @@ impl RecordingCommandBuffer {
is not equal to the view type required by the pipeline"
)
.into(),
// vuids?
vuids: vuids!(vuid_type, "viewType-07752"),
..Default::default()
}));
}
@ -1147,7 +1187,7 @@ impl RecordingCommandBuffer {
`{view_numeric_type:?}` numeric type"
)
.into(),
// vuids?
vuids: vuids!(vuid_type, "format-07753"),
..Default::default()
}));
}
@ -1196,7 +1236,7 @@ impl RecordingCommandBuffer {
sampler YCbCr conversion"
)
.into(),
// vuids?
vuids: vuids!(vuid_type, "None-06550", "ConstOffset-06551"),
..Default::default()
}));
}

View File

@ -62,26 +62,29 @@
//! # let graphics_pipeline: std::sync::Arc<vulkano::pipeline::graphics::GraphicsPipeline> = return;
//! # let command_buffer_allocator: std::sync::Arc<vulkano::command_buffer::allocator::StandardCommandBufferAllocator> = return;
//! #
//! let cb = RecordingCommandBuffer::new(
//! let mut cb = RecordingCommandBuffer::new(
//! command_buffer_allocator.clone(),
//! queue.queue_family_index(),
//! CommandBufferLevel::Primary,
//! CommandBufferBeginInfo::default(),
//! )
//! .unwrap()
//! .begin_render_pass(render_pass_begin_info, Default::default())
//! .unwrap()
//! .bind_pipeline_graphics(graphics_pipeline.clone())
//! .unwrap()
//! .bind_vertex_buffers(0, vertex_buffer.clone())
//! .unwrap()
//! .draw(vertex_buffer.len() as u32, 1, 0, 0)
//! .unwrap()
//! .end_render_pass(Default::default())
//! .unwrap()
//! .end()
//! .unwrap();
//!
//! cb.begin_render_pass(render_pass_begin_info, Default::default())
//! .unwrap()
//! .bind_pipeline_graphics(graphics_pipeline.clone())
//! .unwrap()
//! .bind_vertex_buffers(0, vertex_buffer.clone())
//! .unwrap();
//!
//! unsafe {
//! cb.draw(vertex_buffer.len() as u32, 1, 0, 0).unwrap();
//! }
//!
//! cb.end_render_pass(Default::default()).unwrap();
//!
//! let cb = cb.end().unwrap();
//!
//! let future = cb.execute(queue.clone());
//! ```
//!
@ -115,6 +118,11 @@ use crate::{
},
DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError,
};
#[cfg(doc)]
use crate::{
device::{Features, Properties},
pipeline::graphics::vertex_input::VertexInputRate,
};
use ahash::HashMap;
use bytemuck::{Pod, Zeroable};
use std::{ops::Range, sync::Arc};
@ -126,6 +134,35 @@ pub mod pool;
pub mod sys;
mod traits;
/// Used as buffer contents to provide input for the
/// [`RecordingCommandBuffer::dispatch_indirect`] command.
///
/// # Safety
///
/// - The `x`, `y` and `z` values must not be greater than the respective elements of the
/// [`max_compute_work_group_count`](Properties::max_compute_work_group_count) device limit.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod, PartialEq, Eq)]
pub struct DispatchIndirectCommand {
pub x: u32,
pub y: u32,
pub z: u32,
}
/// Used as buffer contents to provide input for the
/// [`RecordingCommandBuffer::draw_indirect`] command.
///
/// # Safety
///
/// - Every vertex number within the specified range must fall within the range of
/// the bound vertex-rate vertex buffers.
/// - Every instance number within the specified range must fall within the range of
/// the bound instance-rate vertex buffers.
/// - If the [`draw_indirect_first_instance`](Features::draw_indirect_first_instance) feature
/// is not enabled, then `first_instance` must be `0`.
/// - If an [instance divisor](VertexInputRate::Instance) other than 1 is used, and
/// the [`supports_non_zero_first_instance`](Properties::supports_non_zero_first_instance)
/// device property is `false`, then `first_instance` must be `0`.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod, PartialEq, Eq)]
pub struct DrawIndirectCommand {
@ -135,6 +172,24 @@ pub struct DrawIndirectCommand {
pub first_instance: u32,
}
/// Used as buffer contents to provide input for the
/// [`RecordingCommandBuffer::draw_indexed_indirect`] command.
///
/// # Safety
///
/// - Every index within the specified range must fall within the range of the bound index buffer.
/// - Every vertex number that is retrieved from the index buffer must fall within the range of
/// the bound vertex-rate vertex buffers.
/// - Every vertex number that is retrieved from the index buffer, if it is not the special
/// primitive restart value, must be no greater than the
/// [`max_draw_indexed_index_value`](Properties::max_draw_indexed_index_value) device limit.
/// - Every instance number within the specified range must fall within the range of
/// the bound instance-rate vertex buffers.
/// - If the [`draw_indirect_first_instance`](Features::draw_indirect_first_instance) feature
/// is not enabled, then `first_instance` must be `0`.
/// - If an [instance divisor](VertexInputRate::Instance) other than 1 is used, and
/// the [`supports_non_zero_first_instance`](Properties::supports_non_zero_first_instance)
/// device property is `false`, then `first_instance` must be `0`.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod, PartialEq, Eq)]
pub struct DrawIndexedIndirectCommand {
@ -145,14 +200,6 @@ pub struct DrawIndexedIndirectCommand {
pub first_instance: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod, PartialEq, Eq)]
pub struct DispatchIndirectCommand {
pub x: u32,
pub y: u32,
pub z: u32,
}
vulkan_enum! {
#[non_exhaustive]

View File

@ -567,6 +567,7 @@ mod tests {
},
)
.unwrap();
cbb.bind_pipeline_compute(pipeline.clone())
.unwrap()
.bind_descriptor_sets(
@ -575,9 +576,12 @@ mod tests {
0,
set,
)
.unwrap()
.dispatch([1, 1, 1])
.unwrap();
unsafe {
cbb.dispatch([1, 1, 1]).unwrap();
}
let cb = cbb.end().unwrap();
let future = now(device)
@ -718,6 +722,7 @@ mod tests {
},
)
.unwrap();
cbb.bind_pipeline_compute(pipeline.clone())
.unwrap()
.bind_descriptor_sets(
@ -726,9 +731,12 @@ mod tests {
0,
set,
)
.unwrap()
.dispatch([128, 1, 1])
.unwrap();
unsafe {
cbb.dispatch([128, 1, 1]).unwrap();
}
let cb = cbb.end().unwrap();
let future = now(device)

View File

@ -117,12 +117,141 @@
//! of the alignments of its members. As with arrays, in the extended alignment, the alignment
//! of a struct is at least 16.
//!
//! # Safety
//!
//! The following general safety requirements apply to the descriptors in a shader, and to the
//! resources that were bound to them. They apply to all shader types, and must be met at the
//! moment the shader executes on the device.
//!
//! Vulkano will validate many of these requirements, but it is only able to do so when the
//! resources involved are statically known. This means that either the descriptor binding must not
//! be arrayed, or if it is arrayed, that the array must be indexed only by constants. If the
//! array index is dynamic (meaning that it depends on values that are inputs to the shader),
//! then Vulkano cannot check these requirements, and you must ensure them yourself.
//!
//! Some requirements, such as the validity of pointers to device memory, cannot be validated
//! by Vulkano at all.
//!
//! ## Descriptors
//!
//! - If a descriptor set binding was created with [`DescriptorBindingFlags::PARTIALLY_BOUND`],
//! then if the shader accesses a descriptor in that binding, the descriptor must be initialized
//! and contain a valid resource.
//!
//! ## Buffers
//!
//! - If the [`robust_buffer_access`](Features::robust_buffer_access) feature is not enabled
//! on the device, then the shader must not access any values outside the range of the buffer,
//! as specified when writing the descriptor set.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-uniformBuffers-06935)
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-storageBuffers-06936)
//! - If any `PhysicalStorageBuffer` pointers to device memory are dereferenced in the shader,
//! then they must point to valid buffer memory of the correct type.
//!
//! ## Image views and buffer views
//!
//! - The [`view_type`](ImageView::view_type) of the bound image view
//! must match the `Dim` operand of the `OpImageType`.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-viewType-07752)
//! - The numeric type of the [`format`](ImageView::format) of the bound image view
//! must match the `Sampled Type` operand of the `OpImageType`.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-format-07753)
//! - For every `OpImageWrite` instruction, the type of the `Texel` operand must have at least
//! as many components as the format of the bound image view or buffer view.
//! If the bound image view's format is [`Format::A8_UNORM`], then the type of the `Texel`
//! operand must have four components.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpImageWrite-04469)
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpImageWrite-08795)
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpImageWrite-08796)
//! - The `Sampled Type` operand of the `OpTypeImage` declaration must have a `Width` of 64,
//! if and only if the format of the bound image view or buffer view also has a 64-bit component.
//! Otherwise, it must have a `Width` of 32.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-SampledType-04470)
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-SampledType-04471)
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-SampledType-04472)
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-SampledType-04473)
//! - For a storage image/texel buffer declared with `OpTypeImage` with an `Unknown` format:
//! - If it is written to in the shader, the format of the bound image view or buffer view must
//! have the [`FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT`] format feature.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpTypeImage-07027)
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpTypeImage-07029)
//! - If it is read from in the shader, the format of the bound image view or buffer view must
//! have the [`FormatFeatures::STORAGE_READ_WITHOUT_FORMAT`] format feature.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpTypeImage-07028)
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-OpTypeImage-07030)
//! - If atomic operations are used on a storage image/texel buffer:
//! - The bound image view's format must have the [`FormatFeatures::STORAGE_IMAGE_ATOMIC`]
//! format feature.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-02691)
//! - The bound buffer view's format must have the [`FormatFeatures::STORAGE_TEXEL_BUFFER_ATOMIC`]
//! format feature.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-07888)
//!
//! ## Image sampling
//!
//! If the bound sampler uses [`Filter::Linear`] or [`SamplerMipmapMode::Linear`]:
//! - The bound image view's format must have the [`FormatFeatures::SAMPLED_IMAGE_FILTER_LINEAR`]
//! format feature.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-magFilter-04553)
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-mipmapMode-04770)
//!
//! If the bound sampler uses [`Filter::Cubic`]:
//! - The bound image view's format must have the [`FormatFeatures::SAMPLED_IMAGE_FILTER_CUBIC`]
//! format feature.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-02692)
//! - The bound image view's type and format must support cubic filtering, as indicated in
//! [`ImageFormatProperties::filter_cubic`] returned from
//! [`PhysicalDevice::image_format_properties`].
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-filterCubic-02694)
//! - If the sampler's reduction mode is [`SamplerReductionMode::Min`] or
//! [`SamplerReductionMode::Max`], the image view type and format must support cubic minmax
//! filtering, as indicated in [`ImageFormatProperties::filter_cubic_minmax`] returned from
//! [`PhysicalDevice::image_format_properties`].
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-filterCubicMinmax-02695)
//!
//! If the bound sampler uses [depth comparison](SamplerCreateInfo::compare):
//! - The bound image view's format must have the [`FormatFeatures::SAMPLED_IMAGE_DEPTH_COMPARISON`]
//! format feature.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-06479)
//!
//! If the bound sampler uses [unnormalized coordinates](SamplerCreateInfo::unnormalized_coordinates):
//! - The bound image view must have a type of [`ImageViewType::Dim1d`] or [`ImageViewType::Dim2d`].
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-08609)
//! - The sampler must not be used in any `OpImageSample*` or `OpImageSparseSample*` instructions,
//! that contain `ImplicitLod`, `Dref` or `Proj` in their name.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-08610)
//! - The sampler must not be used in any `OpImageSample*` or `OpImageSparseSample*` instructions,
//! that include an LOD bias or offset operand.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-08611)
//!
//! If the bound sampler has a [sampler YCbCr conversion](crate::image::sampler::ycbcr):
//! - The sampler must only be used in `OpImageSample*` or `OpImageSparseSample*` instructions.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-None-06550)
//! - The sampler must not be used with the `ConstOffset` or `Offset` image operands.
//! [\[spec\]](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdDispatch-ConstOffset-06551)
//!
//! ## Acceleration structures
//!
//! - In any top-level acceleration structure, the pointers that refer to the contained
//! bottom-level acceleration structure instances must point to valid acceleration structures.
//!
//! [alignment rules]: <https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html#interfaces-resources-layout>
//! [`GL_EXT_scalar_block_layout`]: <https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GL_EXT_scalar_block_layout.txt>
//! [`scalar_block_layout`]: crate::device::Features::scalar_block_layout
//! [`uniform_buffer_standard_layout`]: crate::device::Features::uniform_buffer_standard_layout
use self::spirv::{Id, Instruction};
#[cfg(doc)]
use crate::{
descriptor_set::layout::DescriptorBindingFlags,
device::{physical::PhysicalDevice, Features},
format::FormatFeatures,
image::{
sampler::{Filter, Sampler, SamplerCreateInfo, SamplerMipmapMode, SamplerReductionMode},
view::ImageView,
ImageFormatProperties,
},
};
use crate::{
descriptor_set::layout::DescriptorType,
device::{Device, DeviceOwned},