From d0144c2d9822016dc0de9026b87774275d619239 Mon Sep 17 00:00:00 2001 From: Rua Date: Fri, 4 Aug 2023 20:55:16 +0200 Subject: [PATCH] ValidationError-ify commands, add some validation to `UnsafeCommandBufferBuilder` (#2266) * ValidationError-ify commands, add some validation to `UnsafeCommandBufferBuilder` * Use `SubpassBeginInfo` for the triangle example --- examples/src/bin/async-update.rs | 10 +- examples/src/bin/basic-compute-shader.rs | 2 + examples/src/bin/buffer-allocator.rs | 9 +- examples/src/bin/clear_attachments.rs | 6 +- .../deferred/frame/ambient_lighting_system.rs | 5 + .../frame/directional_lighting_system.rs | 5 + .../deferred/frame/point_lighting_system.rs | 5 + examples/src/bin/deferred/frame/system.rs | 17 +- .../src/bin/deferred/triangle_draw_system.rs | 3 + examples/src/bin/dynamic-buffers.rs | 4 + examples/src/bin/dynamic-local-size.rs | 2 + examples/src/bin/gl-interop.rs | 9 +- examples/src/bin/image-self-copy-blit/main.rs | 9 +- examples/src/bin/image/main.rs | 10 +- examples/src/bin/immutable-sampler/main.rs | 10 +- examples/src/bin/indirect.rs | 11 +- examples/src/bin/instancing.rs | 9 +- .../fractal_compute_pipeline.rs | 3 + .../pixels_draw_pipeline.rs | 5 + .../interactive_fractal/place_over_frame.rs | 11 +- examples/src/bin/msaa-renderpass.rs | 9 +- examples/src/bin/multi-window.rs | 9 +- .../multi_window_game_of_life/game_of_life.rs | 3 + .../multi_window_game_of_life/pixels_draw.rs | 5 + .../multi_window_game_of_life/render_pass.rs | 11 +- examples/src/bin/multiview.rs | 8 +- examples/src/bin/occlusion-query.rs | 11 +- examples/src/bin/push-constants.rs | 3 + examples/src/bin/push-descriptors/main.rs | 10 +- examples/src/bin/runtime-shader/main.rs | 9 +- examples/src/bin/runtime_array/main.rs | 10 +- examples/src/bin/self-copy-buffer.rs | 2 + examples/src/bin/shader-include/main.rs | 2 + examples/src/bin/shader-types-sharing.rs | 3 + examples/src/bin/simple-particles.rs | 11 +- examples/src/bin/specialization-constants.rs | 2 + examples/src/bin/teapot/main.rs | 10 +- examples/src/bin/tessellation.rs | 9 +- examples/src/bin/texture_array/main.rs | 10 +- examples/src/bin/triangle-v1_3.rs | 3 + examples/src/bin/triangle.rs | 18 +- vulkano-shaders/src/entry_point.rs | 4 +- vulkano/autogen/formats.rs | 80 +- vulkano/src/command_buffer/auto/builder.rs | 35 +- vulkano/src/command_buffer/auto/mod.rs | 53 +- .../commands/acceleration_structure.rs | 1489 ++-- .../src/command_buffer/commands/bind_push.rs | 1730 ++-- vulkano/src/command_buffer/commands/clear.rs | 1221 ++- vulkano/src/command_buffer/commands/copy.rs | 7799 ++++++++++------- vulkano/src/command_buffer/commands/debug.rs | 313 +- .../command_buffer/commands/dynamic_state.rs | 3388 +++---- .../src/command_buffer/commands/pipeline.rs | 3203 ++++--- vulkano/src/command_buffer/commands/query.rs | 1211 +-- .../command_buffer/commands/render_pass.rs | 3772 ++++---- .../src/command_buffer/commands/secondary.rs | 1052 +-- vulkano/src/command_buffer/commands/sync.rs | 803 +- vulkano/src/command_buffer/mod.rs | 23 +- vulkano/src/command_buffer/traits.rs | 6 +- vulkano/src/format.rs | 95 +- vulkano/src/image/sampler/mod.rs | 18 +- vulkano/src/image/sampler/ycbcr.rs | 6 +- vulkano/src/image/view.rs | 2 +- vulkano/src/pipeline/compute.rs | 2 + vulkano/src/pipeline/graphics/mod.rs | 27 +- vulkano/src/render_pass/framebuffer.rs | 4 +- vulkano/src/render_pass/mod.rs | 30 +- vulkano/src/shader/mod.rs | 30 +- vulkano/src/shader/reflect.rs | 16 +- vulkano/src/sync/pipeline.rs | 1654 +++- 69 files changed, 17022 insertions(+), 11347 deletions(-) diff --git a/examples/src/bin/async-update.rs b/examples/src/bin/async-update.rs index a69206a6..3e37cb59 100644 --- a/examples/src/bin/async-update.rs +++ b/examples/src/bin/async-update.rs @@ -52,7 +52,7 @@ use vulkano::{ command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, BufferImageCopy, ClearColorImageInfo, CommandBufferUsage, CopyBufferToImageInfo, - PrimaryCommandBufferAbstract, RenderPassBeginInfo, SubpassContents, + PrimaryCommandBufferAbstract, RenderPassBeginInfo, }, descriptor_set::{ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, @@ -597,11 +597,13 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .bind_pipeline_graphics(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, pipeline.layout().clone(), @@ -614,10 +616,12 @@ fn main() { .clone(), ), ) + .unwrap() .bind_vertex_buffers(0, vertex_buffer.clone()) + .unwrap() .draw(vertex_buffer.len() as u32, 1, 0, 0) .unwrap() - .end_render_pass() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/basic-compute-shader.rs b/examples/src/bin/basic-compute-shader.rs index 59ec05f0..eb790139 100644 --- a/examples/src/bin/basic-compute-shader.rs +++ b/examples/src/bin/basic-compute-shader.rs @@ -213,12 +213,14 @@ fn main() { // 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( PipelineBindPoint::Compute, pipeline.layout().clone(), 0, set, ) + .unwrap() .dispatch([1024, 1, 1]) .unwrap(); diff --git a/examples/src/bin/buffer-allocator.rs b/examples/src/bin/buffer-allocator.rs index cc107477..a27d872a 100644 --- a/examples/src/bin/buffer-allocator.rs +++ b/examples/src/bin/buffer-allocator.rs @@ -20,7 +20,7 @@ use vulkano::{ }, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - RenderPassBeginInfo, SubpassContents, + RenderPassBeginInfo, }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, @@ -381,16 +381,19 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() // Draw our buffer .bind_pipeline_graphics(pipeline.clone()) + .unwrap() .bind_vertex_buffers(0, buffer) + .unwrap() .draw(num_vertices, 1, 0, 0) .unwrap() - .end_render_pass() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/clear_attachments.rs b/examples/src/bin/clear_attachments.rs index feb3a69f..6b5fbdd7 100644 --- a/examples/src/bin/clear_attachments.rs +++ b/examples/src/bin/clear_attachments.rs @@ -11,7 +11,7 @@ use std::sync::Arc; use vulkano::{ command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, ClearAttachment, - ClearRect, CommandBufferUsage, RenderPassBeginInfo, SubpassContents, + ClearRect, CommandBufferUsage, RenderPassBeginInfo, }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, @@ -223,7 +223,7 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() // Clear attachments with clear values and rects information. All the rects will be @@ -260,7 +260,7 @@ fn main() { .collect(), ) .unwrap() - .end_render_pass() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/deferred/frame/ambient_lighting_system.rs b/examples/src/bin/deferred/frame/ambient_lighting_system.rs index 14b8975a..babd8166 100644 --- a/examples/src/bin/deferred/frame/ambient_lighting_system.rs +++ b/examples/src/bin/deferred/frame/ambient_lighting_system.rs @@ -198,15 +198,20 @@ impl AmbientLightingSystem { .unwrap(); builder .set_viewport(0, [viewport].into_iter().collect()) + .unwrap() .bind_pipeline_graphics(self.pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, self.pipeline.layout().clone(), 0, descriptor_set, ) + .unwrap() .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(); builder.build().unwrap() diff --git a/examples/src/bin/deferred/frame/directional_lighting_system.rs b/examples/src/bin/deferred/frame/directional_lighting_system.rs index f072d2f9..0d8c8853 100644 --- a/examples/src/bin/deferred/frame/directional_lighting_system.rs +++ b/examples/src/bin/deferred/frame/directional_lighting_system.rs @@ -212,15 +212,20 @@ impl DirectionalLightingSystem { .unwrap(); builder .set_viewport(0, [viewport].into_iter().collect()) + .unwrap() .bind_pipeline_graphics(self.pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, self.pipeline.layout().clone(), 0, descriptor_set, ) + .unwrap() .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(); builder.build().unwrap() diff --git a/examples/src/bin/deferred/frame/point_lighting_system.rs b/examples/src/bin/deferred/frame/point_lighting_system.rs index 69c5ab78..2d2ac08e 100644 --- a/examples/src/bin/deferred/frame/point_lighting_system.rs +++ b/examples/src/bin/deferred/frame/point_lighting_system.rs @@ -225,15 +225,20 @@ impl PointLightingSystem { .unwrap(); builder .set_viewport(0, [viewport].into_iter().collect()) + .unwrap() .bind_pipeline_graphics(self.pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, self.pipeline.layout().clone(), 0, descriptor_set, ) + .unwrap() .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(); builder.build().unwrap() diff --git a/examples/src/bin/deferred/frame/system.rs b/examples/src/bin/deferred/frame/system.rs index 96909a51..0337e3a7 100644 --- a/examples/src/bin/deferred/frame/system.rs +++ b/examples/src/bin/deferred/frame/system.rs @@ -18,7 +18,7 @@ use vulkano::{ command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, RenderPassBeginInfo, SecondaryCommandBufferAbstract, - SubpassContents, + SubpassBeginInfo, SubpassContents, }, descriptor_set::allocator::StandardDescriptorSetAllocator, device::Queue, @@ -363,7 +363,10 @@ impl FrameSystem { ], ..RenderPassBeginInfo::framebuffer(framebuffer.clone()) }, - SubpassContents::SecondaryCommandBuffers, + SubpassBeginInfo { + contents: SubpassContents::SecondaryCommandBuffers, + ..Default::default() + }, ) .unwrap(); @@ -428,7 +431,13 @@ impl<'a> Frame<'a> { self.command_buffer_builder .as_mut() .unwrap() - .next_subpass(SubpassContents::SecondaryCommandBuffers) + .next_subpass( + Default::default(), + SubpassBeginInfo { + contents: SubpassContents::SecondaryCommandBuffers, + ..Default::default() + }, + ) .unwrap(); // And returning an object that will allow the user to apply lighting to the scene. @@ -442,7 +451,7 @@ impl<'a> Frame<'a> { self.command_buffer_builder .as_mut() .unwrap() - .end_render_pass() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = self.command_buffer_builder.take().unwrap().build().unwrap(); diff --git a/examples/src/bin/deferred/triangle_draw_system.rs b/examples/src/bin/deferred/triangle_draw_system.rs index 55c8b7d2..5966277a 100644 --- a/examples/src/bin/deferred/triangle_draw_system.rs +++ b/examples/src/bin/deferred/triangle_draw_system.rs @@ -151,8 +151,11 @@ impl TriangleDrawSystem { .into_iter() .collect(), ) + .unwrap() .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(); builder.build().unwrap() diff --git a/examples/src/bin/dynamic-buffers.rs b/examples/src/bin/dynamic-buffers.rs index c97023b5..9c238ad5 100644 --- a/examples/src/bin/dynamic-buffers.rs +++ b/examples/src/bin/dynamic-buffers.rs @@ -250,12 +250,14 @@ fn main() { #[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( @@ -264,6 +266,7 @@ fn main() { 0, set.clone().offsets([1 * align as u32]), ) + .unwrap() .dispatch([12, 1, 1]) .unwrap() .bind_descriptor_sets( @@ -272,6 +275,7 @@ fn main() { 0, set.offsets([2 * align as u32]), ) + .unwrap() .dispatch([12, 1, 1]) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/dynamic-local-size.rs b/examples/src/bin/dynamic-local-size.rs index 15db77a8..bf267535 100644 --- a/examples/src/bin/dynamic-local-size.rs +++ b/examples/src/bin/dynamic-local-size.rs @@ -260,12 +260,14 @@ fn main() { .unwrap(); builder .bind_pipeline_compute(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Compute, pipeline.layout().clone(), 0, set, ) + .unwrap() // Note that dispatch dimensions must be proportional to the local size. .dispatch([1024 / local_size_x, 1024 / local_size_y, 1]) .unwrap() diff --git a/examples/src/bin/gl-interop.rs b/examples/src/bin/gl-interop.rs index c52af7c8..df718385 100644 --- a/examples/src/bin/gl-interop.rs +++ b/examples/src/bin/gl-interop.rs @@ -18,7 +18,6 @@ mod linux { command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SemaphoreSubmitInfo, SubmitInfo, - SubpassContents, }, descriptor_set::{ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, @@ -378,21 +377,25 @@ mod linux { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .bind_pipeline_graphics(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, pipeline.layout().clone(), 0, set.clone(), ) + .unwrap() .bind_vertex_buffers(0, vertex_buffer.clone()) + .unwrap() .draw(vertex_buffer.len() as u32, 1, 0, 0) .unwrap() - .end_render_pass() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/image-self-copy-blit/main.rs b/examples/src/bin/image-self-copy-blit/main.rs index fad89305..d40199e5 100644 --- a/examples/src/bin/image-self-copy-blit/main.rs +++ b/examples/src/bin/image-self-copy-blit/main.rs @@ -14,7 +14,6 @@ use vulkano::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, BlitImageInfo, BufferImageCopy, ClearColorImageInfo, CommandBufferUsage, CopyBufferToImageInfo, CopyImageInfo, ImageBlit, ImageCopy, PrimaryCommandBufferAbstract, RenderPassBeginInfo, - SubpassContents, }, descriptor_set::{ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, @@ -471,21 +470,25 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .bind_pipeline_graphics(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, pipeline.layout().clone(), 0, set.clone(), ) + .unwrap() .bind_vertex_buffers(0, vertex_buffer.clone()) + .unwrap() .draw(vertex_buffer.len() as u32, 1, 0, 0) .unwrap() - .end_render_pass() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/image/main.rs b/examples/src/bin/image/main.rs index 33bc24af..5ff3d2c5 100644 --- a/examples/src/bin/image/main.rs +++ b/examples/src/bin/image/main.rs @@ -12,7 +12,7 @@ use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - CopyBufferToImageInfo, PrimaryCommandBufferAbstract, RenderPassBeginInfo, SubpassContents, + CopyBufferToImageInfo, PrimaryCommandBufferAbstract, RenderPassBeginInfo, }, descriptor_set::{ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, @@ -418,21 +418,25 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .bind_pipeline_graphics(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, pipeline.layout().clone(), 0, set.clone(), ) + .unwrap() .bind_vertex_buffers(0, vertex_buffer.clone()) + .unwrap() .draw(vertex_buffer.len() as u32, 1, 0, 0) .unwrap() - .end_render_pass() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/immutable-sampler/main.rs b/examples/src/bin/immutable-sampler/main.rs index 64f4c56f..44a2358d 100644 --- a/examples/src/bin/immutable-sampler/main.rs +++ b/examples/src/bin/immutable-sampler/main.rs @@ -21,7 +21,7 @@ use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - CopyBufferToImageInfo, PrimaryCommandBufferAbstract, RenderPassBeginInfo, SubpassContents, + CopyBufferToImageInfo, PrimaryCommandBufferAbstract, RenderPassBeginInfo, }, descriptor_set::{ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, @@ -439,21 +439,25 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .bind_pipeline_graphics(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, pipeline.layout().clone(), 0, set.clone(), ) + .unwrap() .bind_vertex_buffers(0, vertex_buffer.clone()) + .unwrap() .draw(vertex_buffer.len() as u32, 1, 0, 0) .unwrap() - .end_render_pass() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/indirect.rs b/examples/src/bin/indirect.rs index 363c8fc2..32bf7daa 100644 --- a/examples/src/bin/indirect.rs +++ b/examples/src/bin/indirect.rs @@ -31,7 +31,7 @@ use vulkano::{ }, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - DrawIndirectCommand, RenderPassBeginInfo, SubpassContents, + DrawIndirectCommand, RenderPassBeginInfo, }, descriptor_set::{ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, @@ -474,12 +474,14 @@ fn main() { // vertices and fill out the draw call arguments. builder .bind_pipeline_compute(compute_pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Compute, compute_pipeline.layout().clone(), 0, cs_desciptor_set, ) + .unwrap() .dispatch([1, 1, 1]) .unwrap() .begin_render_pass( @@ -489,17 +491,20 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .bind_pipeline_graphics(render_pipeline.clone()) + .unwrap() .bind_vertex_buffers(0, vertices) + .unwrap() // 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() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/instancing.rs b/examples/src/bin/instancing.rs index 560327fc..e4ae64bc 100644 --- a/examples/src/bin/instancing.rs +++ b/examples/src/bin/instancing.rs @@ -17,7 +17,7 @@ use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - RenderPassBeginInfo, SubpassContents, + RenderPassBeginInfo, }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, @@ -415,13 +415,16 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .bind_pipeline_graphics(pipeline.clone()) + .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, @@ -429,7 +432,7 @@ fn main() { 0, ) .unwrap() - .end_render_pass() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs b/examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs index ea337818..b76d3a0e 100644 --- a/examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs +++ b/examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs @@ -175,8 +175,11 @@ impl FractalComputePipeline { }; 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(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs b/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs index bd97d48f..06e8048c 100644 --- a/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs +++ b/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs @@ -228,15 +228,20 @@ impl PixelsDrawPipeline { .into_iter() .collect(), ) + .unwrap() .bind_pipeline_graphics(self.pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, self.pipeline.layout().clone(), 0, desc_set, ) + .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(); builder.build().unwrap() diff --git a/examples/src/bin/interactive_fractal/place_over_frame.rs b/examples/src/bin/interactive_fractal/place_over_frame.rs index 1eb82154..f348cb19 100644 --- a/examples/src/bin/interactive_fractal/place_over_frame.rs +++ b/examples/src/bin/interactive_fractal/place_over_frame.rs @@ -12,7 +12,7 @@ use std::sync::Arc; use vulkano::{ command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - RenderPassBeginInfo, SubpassContents, + RenderPassBeginInfo, SubpassBeginInfo, SubpassContents, }, descriptor_set::allocator::StandardDescriptorSetAllocator, device::Queue, @@ -111,7 +111,10 @@ impl RenderPassPlaceOverFrame { clear_values: vec![Some([0.0; 4].into())], ..RenderPassBeginInfo::framebuffer(framebuffer) }, - SubpassContents::SecondaryCommandBuffers, + SubpassBeginInfo { + contents: SubpassContents::SecondaryCommandBuffers, + ..Default::default() + }, ) .unwrap(); @@ -122,7 +125,9 @@ impl RenderPassPlaceOverFrame { command_buffer_builder.execute_commands(cb).unwrap(); // End render pass. - command_buffer_builder.end_render_pass().unwrap(); + command_buffer_builder + .end_render_pass(Default::default()) + .unwrap(); // Build command buffer. let command_buffer = command_buffer_builder.build().unwrap(); diff --git a/examples/src/bin/msaa-renderpass.rs b/examples/src/bin/msaa-renderpass.rs index dcf00c31..990af118 100644 --- a/examples/src/bin/msaa-renderpass.rs +++ b/examples/src/bin/msaa-renderpass.rs @@ -67,7 +67,7 @@ use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - CopyImageToBufferInfo, PrimaryCommandBufferAbstract, RenderPassBeginInfo, SubpassContents, + CopyImageToBufferInfo, PrimaryCommandBufferAbstract, RenderPassBeginInfo, }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, @@ -394,15 +394,18 @@ fn main() { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into()), None], ..RenderPassBeginInfo::framebuffer(framebuffer) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport].into_iter().collect()) + .unwrap() .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() + .end_render_pass(Default::default()) .unwrap() .copy_image_to_buffer(CopyImageToBufferInfo::image_buffer(image, buf.clone())) .unwrap(); diff --git a/examples/src/bin/multi-window.rs b/examples/src/bin/multi-window.rs index 86ba4da9..9b68fb8a 100644 --- a/examples/src/bin/multi-window.rs +++ b/examples/src/bin/multi-window.rs @@ -21,7 +21,7 @@ use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - RenderPassBeginInfo, SubpassContents, + RenderPassBeginInfo, }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, @@ -462,15 +462,18 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .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() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/multi_window_game_of_life/game_of_life.rs b/examples/src/bin/multi_window_game_of_life/game_of_life.rs index 5774831e..b92042dd 100644 --- a/examples/src/bin/multi_window_game_of_life/game_of_life.rs +++ b/examples/src/bin/multi_window_game_of_life/game_of_life.rs @@ -201,8 +201,11 @@ impl GameOfLifeComputePipeline { }; builder .bind_pipeline_compute(self.compute_life_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(); } diff --git a/examples/src/bin/multi_window_game_of_life/pixels_draw.rs b/examples/src/bin/multi_window_game_of_life/pixels_draw.rs index 6ecfac64..b8ceba66 100644 --- a/examples/src/bin/multi_window_game_of_life/pixels_draw.rs +++ b/examples/src/bin/multi_window_game_of_life/pixels_draw.rs @@ -224,15 +224,20 @@ impl PixelsDrawPipeline { .into_iter() .collect(), ) + .unwrap() .bind_pipeline_graphics(self.pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, self.pipeline.layout().clone(), 0, desc_set, ) + .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(); builder.build().unwrap() diff --git a/examples/src/bin/multi_window_game_of_life/render_pass.rs b/examples/src/bin/multi_window_game_of_life/render_pass.rs index 8f43cc51..5c527b26 100644 --- a/examples/src/bin/multi_window_game_of_life/render_pass.rs +++ b/examples/src/bin/multi_window_game_of_life/render_pass.rs @@ -12,7 +12,7 @@ use std::sync::Arc; use vulkano::{ command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - RenderPassBeginInfo, SubpassContents, + RenderPassBeginInfo, SubpassBeginInfo, SubpassContents, }, device::Queue, format::Format, @@ -101,7 +101,10 @@ impl RenderPassPlaceOverFrame { clear_values: vec![Some([0.0; 4].into())], ..RenderPassBeginInfo::framebuffer(framebuffer) }, - SubpassContents::SecondaryCommandBuffers, + SubpassBeginInfo { + contents: SubpassContents::SecondaryCommandBuffers, + ..Default::default() + }, ) .unwrap(); @@ -112,7 +115,9 @@ impl RenderPassPlaceOverFrame { command_buffer_builder.execute_commands(cb).unwrap(); // End the render pass. - command_buffer_builder.end_render_pass().unwrap(); + command_buffer_builder + .end_render_pass(Default::default()) + .unwrap(); // Build the command buffer. let command_buffer = command_buffer_builder.build().unwrap(); diff --git a/examples/src/bin/multiview.rs b/examples/src/bin/multiview.rs index e2ff5e12..6b53fe37 100644 --- a/examples/src/bin/multiview.rs +++ b/examples/src/bin/multiview.rs @@ -17,7 +17,7 @@ use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, BufferImageCopy, - CommandBufferUsage, CopyImageToBufferInfo, RenderPassBeginInfo, SubpassContents, + CommandBufferUsage, CopyImageToBufferInfo, RenderPassBeginInfo, }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Features, @@ -346,14 +346,16 @@ fn main() { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer(framebuffer) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .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() + .end_render_pass(Default::default()) .unwrap(); // Copy the image layers to different buffers to save them as individual images to disk. diff --git a/examples/src/bin/occlusion-query.rs b/examples/src/bin/occlusion-query.rs index d6ed354e..35e24ac1 100644 --- a/examples/src/bin/occlusion-query.rs +++ b/examples/src/bin/occlusion-query.rs @@ -16,7 +16,7 @@ use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - RenderPassBeginInfo, SubpassContents, + RenderPassBeginInfo, }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, @@ -436,7 +436,9 @@ fn main() { .reset_query_pool(query_pool.clone(), 0..3) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .bind_pipeline_graphics(pipeline.clone()) + .unwrap() .begin_render_pass( RenderPassBeginInfo { clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into()), Some(1.0.into())], @@ -444,7 +446,7 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() // Begin query 0, then draw the red triangle. Enabling the @@ -458,6 +460,7 @@ fn main() { ) .unwrap() .bind_vertex_buffers(0, triangle1.clone()) + .unwrap() .draw(triangle1.len() as u32, 1, 0, 0) .unwrap() // End query 0. @@ -467,6 +470,7 @@ fn main() { .begin_query(query_pool.clone(), 1, QueryControlFlags::empty()) .unwrap() .bind_vertex_buffers(0, triangle2.clone()) + .unwrap() .draw(triangle2.len() as u32, 1, 0, 0) .unwrap() .end_query(query_pool.clone(), 1) @@ -475,11 +479,12 @@ fn main() { .begin_query(query_pool.clone(), 2, QueryControlFlags::empty()) .unwrap() .bind_vertex_buffers(0, triangle3.clone()) + .unwrap() .draw(triangle3.len() as u32, 1, 0, 0) .unwrap() .end_query(query_pool.clone(), 2) .unwrap() - .end_render_pass() + .end_render_pass(Default::default()) .unwrap(); } diff --git a/examples/src/bin/push-constants.rs b/examples/src/bin/push-constants.rs index 08299dd9..cd91fe19 100644 --- a/examples/src/bin/push-constants.rs +++ b/examples/src/bin/push-constants.rs @@ -191,13 +191,16 @@ fn main() { .unwrap(); builder .bind_pipeline_compute(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Compute, pipeline.layout().clone(), 0, set, ) + .unwrap() .push_constants(pipeline.layout().clone(), 0, push_constants) + .unwrap() .dispatch([1024, 1, 1]) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/push-descriptors/main.rs b/examples/src/bin/push-descriptors/main.rs index 0e7aabbd..043a60b6 100644 --- a/examples/src/bin/push-descriptors/main.rs +++ b/examples/src/bin/push-descriptors/main.rs @@ -12,7 +12,7 @@ use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - CopyBufferToImageInfo, PrimaryCommandBufferAbstract, RenderPassBeginInfo, SubpassContents, + CopyBufferToImageInfo, PrimaryCommandBufferAbstract, RenderPassBeginInfo, }, descriptor_set::{layout::DescriptorSetLayoutCreateFlags, WriteDescriptorSet}, device::{ @@ -412,11 +412,13 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .bind_pipeline_graphics(pipeline.clone()) + .unwrap() .push_descriptor_set( PipelineBindPoint::Graphics, pipeline.layout().clone(), @@ -425,10 +427,12 @@ fn main() { .into_iter() .collect(), ) + .unwrap() .bind_vertex_buffers(0, vertex_buffer.clone()) + .unwrap() .draw(vertex_buffer.len() as u32, 1, 0, 0) .unwrap() - .end_render_pass() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/runtime-shader/main.rs b/examples/src/bin/runtime-shader/main.rs index dc25098d..dbb63a2b 100644 --- a/examples/src/bin/runtime-shader/main.rs +++ b/examples/src/bin/runtime-shader/main.rs @@ -26,7 +26,7 @@ use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - RenderPassBeginInfo, SubpassContents, + RenderPassBeginInfo, }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, @@ -357,15 +357,18 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .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() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/runtime_array/main.rs b/examples/src/bin/runtime_array/main.rs index e6e9bfa9..0e3a5b1e 100644 --- a/examples/src/bin/runtime_array/main.rs +++ b/examples/src/bin/runtime_array/main.rs @@ -12,7 +12,7 @@ use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - CopyBufferToImageInfo, PrimaryCommandBufferAbstract, RenderPassBeginInfo, SubpassContents, + CopyBufferToImageInfo, PrimaryCommandBufferAbstract, RenderPassBeginInfo, }, descriptor_set::{ allocator::StandardDescriptorSetAllocator, layout::DescriptorBindingFlags, @@ -547,21 +547,25 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .bind_pipeline_graphics(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, pipeline.layout().clone(), 0, set.clone(), ) + .unwrap() .bind_vertex_buffers(0, vertex_buffer.clone()) + .unwrap() .draw(vertex_buffer.len() as u32, 1, 0, 0) .unwrap() - .end_render_pass() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/self-copy-buffer.rs b/examples/src/bin/self-copy-buffer.rs index 3d52c102..d0b26820 100644 --- a/examples/src/bin/self-copy-buffer.rs +++ b/examples/src/bin/self-copy-buffer.rs @@ -186,12 +186,14 @@ fn main() { }) .unwrap() .bind_pipeline_compute(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Compute, pipeline.layout().clone(), 0, set, ) + .unwrap() .dispatch([1024, 1, 1]) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/shader-include/main.rs b/examples/src/bin/shader-include/main.rs index 96f81646..9d952c84 100644 --- a/examples/src/bin/shader-include/main.rs +++ b/examples/src/bin/shader-include/main.rs @@ -177,12 +177,14 @@ fn main() { .unwrap(); builder .bind_pipeline_compute(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Compute, pipeline.layout().clone(), 0, set, ) + .unwrap() .dispatch([1024, 1, 1]) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/shader-types-sharing.rs b/examples/src/bin/shader-types-sharing.rs index aea97994..80f923ab 100644 --- a/examples/src/bin/shader-types-sharing.rs +++ b/examples/src/bin/shader-types-sharing.rs @@ -209,13 +209,16 @@ fn main() { .unwrap(); builder .bind_pipeline_compute(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Compute, pipeline.layout().clone(), 0, set, ) + .unwrap() .push_constants(pipeline.layout().clone(), 0, parameters) + .unwrap() .dispatch([1024, 1, 1]) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/simple-particles.rs b/examples/src/bin/simple-particles.rs index b8b27bcb..de4a57ae 100644 --- a/examples/src/bin/simple-particles.rs +++ b/examples/src/bin/simple-particles.rs @@ -17,7 +17,7 @@ use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - CopyBufferInfo, PrimaryCommandBufferAbstract, RenderPassBeginInfo, SubpassContents, + CopyBufferInfo, PrimaryCommandBufferAbstract, RenderPassBeginInfo, }, descriptor_set::{ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, @@ -582,14 +582,17 @@ fn main() { builder // Push constants for compute shader. .push_constants(compute_pipeline.layout().clone(), 0, push_constants) + .unwrap() // Perform compute operation to update particle positions. .bind_pipeline_compute(compute_pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Compute, compute_pipeline.layout().clone(), 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. @@ -600,14 +603,16 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .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() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/specialization-constants.rs b/examples/src/bin/specialization-constants.rs index 9fed03b8..3a6b55cd 100644 --- a/examples/src/bin/specialization-constants.rs +++ b/examples/src/bin/specialization-constants.rs @@ -177,12 +177,14 @@ fn main() { .unwrap(); builder .bind_pipeline_compute(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Compute, pipeline.layout().clone(), 0, set, ) + .unwrap() .dispatch([1024, 1, 1]) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/teapot/main.rs b/examples/src/bin/teapot/main.rs index 3af3557e..575d4809 100644 --- a/examples/src/bin/teapot/main.rs +++ b/examples/src/bin/teapot/main.rs @@ -17,7 +17,7 @@ use vulkano::{ }, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - RenderPassBeginInfo, SubpassContents, + RenderPassBeginInfo, }, descriptor_set::{ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, @@ -382,21 +382,25 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .bind_pipeline_graphics(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, pipeline.layout().clone(), 0, set, ) + .unwrap() .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() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/tessellation.rs b/examples/src/bin/tessellation.rs index 08278249..d23650be 100644 --- a/examples/src/bin/tessellation.rs +++ b/examples/src/bin/tessellation.rs @@ -26,7 +26,7 @@ use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - RenderPassBeginInfo, SubpassContents, + RenderPassBeginInfo, }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Features, @@ -473,15 +473,18 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .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() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/texture_array/main.rs b/examples/src/bin/texture_array/main.rs index 77f83f75..b08da2a6 100644 --- a/examples/src/bin/texture_array/main.rs +++ b/examples/src/bin/texture_array/main.rs @@ -12,7 +12,7 @@ use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - CopyBufferToImageInfo, PrimaryCommandBufferAbstract, RenderPassBeginInfo, SubpassContents, + CopyBufferToImageInfo, PrimaryCommandBufferAbstract, RenderPassBeginInfo, }, descriptor_set::{ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, @@ -429,21 +429,25 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - SubpassContents::Inline, + Default::default(), ) .unwrap() .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .bind_pipeline_graphics(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, pipeline.layout().clone(), 0, set.clone(), ) + .unwrap() .bind_vertex_buffers(0, vertex_buffer.clone()) + .unwrap() .draw(vertex_buffer.len() as u32, 3, 0, 0) .unwrap() - .end_render_pass() + .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); diff --git a/examples/src/bin/triangle-v1_3.rs b/examples/src/bin/triangle-v1_3.rs index 5dcea996..720e27bc 100644 --- a/examples/src/bin/triangle-v1_3.rs +++ b/examples/src/bin/triangle-v1_3.rs @@ -635,8 +635,11 @@ fn main() { // // TODO: Document state setting and how it affects subsequent draw commands. .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .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() diff --git a/examples/src/bin/triangle.rs b/examples/src/bin/triangle.rs index 25309d6a..35574866 100644 --- a/examples/src/bin/triangle.rs +++ b/examples/src/bin/triangle.rs @@ -21,7 +21,7 @@ use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - RenderPassBeginInfo, SubpassContents, + RenderPassBeginInfo, SubpassBeginInfo, SubpassContents, }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, @@ -618,24 +618,30 @@ fn main() { framebuffers[image_index as usize].clone(), ) }, - // The contents of the first (and only) subpass. This can be either - // `Inline` or `SecondaryCommandBuffers`. The latter is a bit more advanced - // and is not covered here. - SubpassContents::Inline, + SubpassBeginInfo { + // The contents of the first (and only) subpass. + // This can be either `Inline` or `SecondaryCommandBuffers`. + // The latter is a bit more advanced and is not covered here. + contents: SubpassContents::Inline, + ..Default::default() + }, ) .unwrap() // We are now inside the first subpass of the render pass. // // TODO: Document state setting and how it affects subsequent draw commands. .set_viewport(0, [viewport.clone()].into_iter().collect()) + .unwrap() .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() // 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() + .end_render_pass(Default::default()) .unwrap(); // Finish building the command buffer by calling `build`. diff --git a/vulkano-shaders/src/entry_point.rs b/vulkano-shaders/src/entry_point.rs index 41b3b12a..d3c02408 100644 --- a/vulkano-shaders/src/entry_point.rs +++ b/vulkano-shaders/src/entry_point.rs @@ -129,7 +129,7 @@ fn write_descriptor_binding_requirements( let image_scalar_type = match image_scalar_type { Some(image_scalar_type) => { let ident = format_ident!("{}", format!("{:?}", image_scalar_type)); - quote! { Some(::vulkano::shader::ShaderScalarType::#ident) } + quote! { Some(::vulkano::format::NumericType::#ident) } } None => quote! { None }, }; @@ -305,7 +305,7 @@ fn write_interface(interface: &ShaderInterface) -> TokenStream { location: #location, component: #component, ty: ::vulkano::shader::ShaderInterfaceEntryType { - base_type: ::vulkano::shader::ShaderScalarType::#base_type, + base_type: ::vulkano::format::NumericType::#base_type, num_components: #num_components, num_elements: #num_elements, is_64bit: #is_64bit, diff --git a/vulkano/autogen/formats.rs b/vulkano/autogen/formats.rs index b1bf7f1e..b4e17049 100644 --- a/vulkano/autogen/formats.rs +++ b/vulkano/autogen/formats.rs @@ -53,9 +53,9 @@ struct FormatMember { compression: Option, planes: Vec, texels_per_block: u8, - type_color: Option, - type_depth: Option, - type_stencil: Option, + numeric_format_color: Option, + numeric_format_depth: Option, + numeric_format_stencil: Option, ycbcr_chroma_sampling: Option, type_std_array: Option, @@ -182,31 +182,37 @@ fn formats_output(members: &[FormatMember]) -> TokenStream { }) }, ); - let type_color_items = members.iter().filter_map( + let numeric_format_color_items = members.iter().filter_map( |FormatMember { - name, type_color, .. + name, + numeric_format_color, + .. }| { - type_color + numeric_format_color .as_ref() - .map(|ty| quote! { Self::#name => Some(NumericType::#ty), }) + .map(|ty| quote! { Self::#name => Some(NumericFormat::#ty), }) }, ); - let type_depth_items = members.iter().filter_map( + let numeric_format_depth_items = members.iter().filter_map( |FormatMember { - name, type_depth, .. + name, + numeric_format_depth, + .. }| { - type_depth + numeric_format_depth .as_ref() - .map(|ty| quote! { Self::#name => Some(NumericType::#ty), }) + .map(|ty| quote! { Self::#name => Some(NumericFormat::#ty), }) }, ); - let type_stencil_items = members.iter().filter_map( + let numeric_format_stencil_items = members.iter().filter_map( |FormatMember { - name, type_stencil, .. + name, + numeric_format_stencil, + .. }| { - type_stencil + numeric_format_stencil .as_ref() - .map(|ty| quote! { Self::#name => Some(NumericType::#ty), }) + .map(|ty| quote! { Self::#name => Some(NumericFormat::#ty), }) }, ); let ycbcr_chroma_sampling_items = members.iter().filter_map( @@ -432,29 +438,29 @@ fn formats_output(members: &[FormatMember]) -> TokenStream { } } - /// Returns the numeric data type of the color aspect of this format. Returns `None` + /// Returns the numeric format of the color aspect of this format. Returns `None` /// for depth/stencil formats. - pub fn type_color(self) -> Option { + pub fn numeric_format_color(self) -> Option { match self { - #(#type_color_items)* + #(#numeric_format_color_items)* _ => None, } } - /// Returns the numeric data type of the depth aspect of this format. Returns `None` + /// Returns the numeric format of the depth aspect of this format. Returns `None` /// color and stencil-only formats. - pub fn type_depth(self) -> Option { + pub fn numeric_format_depth(self) -> Option { match self { - #(#type_depth_items)* + #(#numeric_format_depth_items)* _ => None, } } - /// Returns the numeric data type of the stencil aspect of this format. Returns `None` + /// Returns the numeric format of the stencil aspect of this format. Returns `None` /// for color and depth-only formats. - pub fn type_stencil(self) -> Option { + pub fn numeric_format_stencil(self) -> Option { match self { - #(#type_stencil_items)* + #(#numeric_format_stencil_items)* _ => None, } } @@ -606,9 +612,9 @@ fn formats_members( compression: None, planes: vec![], texels_per_block: 0, - type_color: None, - type_depth: None, - type_stencil: None, + numeric_format_color: None, + numeric_format_depth: None, + numeric_format_stencil: None, ycbcr_chroma_sampling: None, type_std_array: None, @@ -655,9 +661,9 @@ fn formats_members( .map(|c| format_ident!("{}", c.replace(' ', "_"))), planes: vec![], texels_per_block: format.texelsPerBlock, - type_color: None, - type_depth: None, - type_stencil: None, + numeric_format_color: None, + numeric_format_depth: None, + numeric_format_stencil: None, ycbcr_chroma_sampling: None, type_std_array: None, @@ -684,32 +690,32 @@ fn formats_members( "R" => { member.aspect_color = true; member.components[0] += bits; - member.type_color = Some(ty); + member.numeric_format_color = Some(ty); } "G" => { member.aspect_color = true; member.components[1] += bits; - member.type_color = Some(ty); + member.numeric_format_color = Some(ty); } "B" => { member.aspect_color = true; member.components[2] += bits; - member.type_color = Some(ty); + member.numeric_format_color = Some(ty); } "A" => { member.aspect_color = true; member.components[3] += bits; - member.type_color = Some(ty); + member.numeric_format_color = Some(ty); } "D" => { member.aspect_depth = true; member.components[0] += bits; - member.type_depth = Some(ty); + member.numeric_format_depth = Some(ty); } "S" => { member.aspect_stencil = true; member.components[1] += bits; - member.type_stencil = Some(ty); + member.numeric_format_stencil = Some(ty); } _ => { panic!("Unknown component type {} on format {}", name, format.name) @@ -752,7 +758,7 @@ fn formats_members( } }; - if let (Some(numeric_type), true) = (&member.type_color, member.planes.is_empty()) { + if let (Some(numeric_type), true) = (&member.numeric_format_color, member.planes.is_empty()) { if format.compressed.is_some() { member.type_std_array = Some({ let block_size = Literal::usize_unsuffixed(format.blockSize as usize); diff --git a/vulkano/src/command_buffer/auto/builder.rs b/vulkano/src/command_buffer/auto/builder.rs index c704c96d..c7b44561 100644 --- a/vulkano/src/command_buffer/auto/builder.rs +++ b/vulkano/src/command_buffer/auto/builder.rs @@ -24,7 +24,7 @@ use crate::{ SecondaryCommandBufferResourcesUsage, SubpassContents, }, descriptor_set::{DescriptorSetResources, DescriptorSetWithOffsets}, - device::{Device, DeviceOwned, QueueFamilyProperties}, + device::{Device, DeviceOwned}, image::{view::ImageView, Image, ImageAspects, ImageLayout, ImageSubresourceRange}, pipeline::{ graphics::{ @@ -42,7 +42,7 @@ use crate::{ range_set::RangeSet, render_pass::{Framebuffer, Subpass}, sync::{ - AccessFlags, BufferMemoryBarrier, DependencyInfo, ImageMemoryBarrier, + AccessFlags, BufferMemoryBarrier, DependencyFlags, DependencyInfo, ImageMemoryBarrier, PipelineStageAccessFlags, PipelineStages, }, DeviceSize, Validated, ValidationError, VulkanError, @@ -293,7 +293,13 @@ where if let Some(barriers) = barriers.remove(&command_index) { for dependency_info in barriers { unsafe { - self.inner.pipeline_barrier(&dependency_info); + #[cfg(debug_assertions)] + self.inner + .pipeline_barrier(&dependency_info) + .expect("bug in Vulkano"); + + #[cfg(not(debug_assertions))] + self.inner.pipeline_barrier_unchecked(&dependency_info); } } } @@ -305,7 +311,13 @@ where if let Some(final_barriers) = barriers.remove(&final_barrier_index) { for dependency_info in final_barriers { unsafe { - self.inner.pipeline_barrier(&dependency_info); + #[cfg(debug_assertions)] + self.inner + .pipeline_barrier(&dependency_info) + .expect("bug in Vulkano"); + + #[cfg(not(debug_assertions))] + self.inner.pipeline_barrier_unchecked(&dependency_info); } } } @@ -402,11 +414,6 @@ impl AutoCommandBufferBuilder where A: CommandBufferAllocator, { - #[inline] - pub(in crate::command_buffer) fn queue_family_properties(&self) -> &QueueFamilyProperties { - self.inner.queue_family_properties() - } - pub(in crate::command_buffer) fn add_command( &mut self, name: &'static str, @@ -490,7 +497,10 @@ impl AutoSyncState { level, command_index: 0, - pending_barrier: DependencyInfo::default(), + pending_barrier: DependencyInfo { + dependency_flags: DependencyFlags::BY_REGION, + ..DependencyInfo::default() + }, barriers: Default::default(), first_unflushed: 0, latest_render_pass_enter: has_inherited_render_pass.then_some(0), @@ -517,7 +527,10 @@ impl AutoSyncState { // Add one last barrier to transition images to their desired final layout. if self.level == CommandBufferLevel::Primary { - let mut final_barrier = DependencyInfo::default(); + let mut final_barrier = DependencyInfo { + dependency_flags: DependencyFlags::BY_REGION, + ..DependencyInfo::default() + }; for (image, range_map) in self.images.iter_mut() { for (range, state) in range_map diff --git a/vulkano/src/command_buffer/auto/mod.rs b/vulkano/src/command_buffer/auto/mod.rs index 1f59dff1..e62777c4 100644 --- a/vulkano/src/command_buffer/auto/mod.rs +++ b/vulkano/src/command_buffer/auto/mod.rs @@ -73,8 +73,8 @@ pub(in crate::command_buffer) use self::builder::{ use super::{ allocator::{CommandBufferAllocator, StandardCommandBufferAllocator}, sys::{UnsafeCommandBuffer, UnsafeCommandBufferBuilder}, - CommandBufferExecError, CommandBufferInheritanceInfo, CommandBufferResourcesUsage, - CommandBufferState, CommandBufferUsage, PrimaryCommandBufferAbstract, ResourceInCommand, + CommandBufferInheritanceInfo, CommandBufferResourcesUsage, CommandBufferState, + CommandBufferUsage, PrimaryCommandBufferAbstract, ResourceInCommand, SecondaryCommandBufferAbstract, SecondaryCommandBufferResourcesUsage, SecondaryResourceUseRef, }; use crate::{ @@ -82,7 +82,7 @@ use crate::{ device::{Device, DeviceOwned}, image::{Image, ImageLayout, ImageSubresourceRange}, sync::PipelineStageAccessFlags, - DeviceSize, VulkanObject, + DeviceSize, ValidationError, VulkanObject, }; use parking_lot::{Mutex, MutexGuard}; use std::{ @@ -187,20 +187,34 @@ where self.inner.inheritance_info().as_ref().unwrap() } - fn lock_record(&self) -> Result<(), CommandBufferExecError> { + fn lock_record(&self) -> Result<(), Box> { match self.submit_state { SubmitState::OneTime { ref already_submitted, } => { let was_already_submitted = already_submitted.swap(true, Ordering::SeqCst); if was_already_submitted { - return Err(CommandBufferExecError::OneTimeSubmitAlreadySubmitted); + return Err(Box::new(ValidationError { + problem: "the command buffer was created with the \ + `CommandBufferUsage::OneTimeSubmit` usage, but \ + it was already submitted before" + .into(), + // vuids? + ..Default::default() + })); } } SubmitState::ExclusiveUse { ref in_use } => { let already_in_use = in_use.swap(true, Ordering::SeqCst); if already_in_use { - return Err(CommandBufferExecError::ExclusiveAlreadyInUse); + return Err(Box::new(ValidationError { + problem: "the command buffer was created with the \ + `CommandBufferUsage::MultipleSubmit` usage, but \ + it is currently being executed" + .into(), + // vuids? + ..Default::default() + })); } } SubmitState::Concurrent => (), @@ -308,8 +322,7 @@ mod tests { buffer::{Buffer, BufferCreateInfo, BufferUsage}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, BufferCopy, - CommandBufferExecError, CommandBufferUsage, CopyBufferInfoTyped, CopyError, - ExecuteCommandsError, PrimaryCommandBufferAbstract, + CommandBufferUsage, CopyBufferInfoTyped, PrimaryCommandBufferAbstract, }, descriptor_set::{ allocator::StandardDescriptorSetAllocator, @@ -458,12 +471,7 @@ mod tests { // Recording the same non-concurrent secondary command buffer twice into the same // primary is an error. - assert!(matches!( - builder.execute_commands(secondary.clone()), - Err(ExecuteCommandsError::ExecError( - CommandBufferExecError::ExclusiveAlreadyInUse - )) - )); + assert!(builder.execute_commands(secondary.clone()).is_err()); } { @@ -485,12 +493,7 @@ mod tests { // Recording the same non-concurrent secondary command buffer into multiple // primaries is an error. - assert!(matches!( - builder.execute_commands(secondary.clone()), - Err(ExecuteCommandsError::ExecError( - CommandBufferExecError::ExclusiveAlreadyInUse - )) - )); + assert!(builder.execute_commands(secondary.clone()).is_err()); std::mem::drop(cb1); @@ -582,8 +585,8 @@ mod tests { ) .unwrap(); - assert!(matches!( - builder.copy_buffer(CopyBufferInfoTyped { + assert!(builder + .copy_buffer(CopyBufferInfoTyped { regions: [BufferCopy { src_offset: 0, dst_offset: 1, @@ -592,12 +595,8 @@ mod tests { }] .into(), ..CopyBufferInfoTyped::buffers(source.clone(), source) - }), - Err(CopyError::OverlappingRegions { - src_region_index: 0, - dst_region_index: 0, }) - )); + .is_err()); } #[test] diff --git a/vulkano/src/command_buffer/commands/acceleration_structure.rs b/vulkano/src/command_buffer/commands/acceleration_structure.rs index c96eafaa..dc6f496c 100644 --- a/vulkano/src/command_buffer/commands/acceleration_structure.rs +++ b/vulkano/src/command_buffer/commands/acceleration_structure.rs @@ -100,6 +100,714 @@ where Ok(self.build_acceleration_structure_unchecked(info, build_range_infos)) } + fn validate_build_acceleration_structure( + &self, + info: &AccelerationStructureBuildGeometryInfo, + build_range_infos: &[AccelerationStructureBuildRangeInfo], + ) -> Result<(), Box> { + self.inner + .validate_build_acceleration_structure(info, build_range_infos)?; + + if self.builder_state.render_pass.is_some() { + return Err(Box::new(ValidationError { + context: "self".into(), + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdBuildAccelerationStructuresKHR-renderpass"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn build_acceleration_structure_unchecked( + &mut self, + info: AccelerationStructureBuildGeometryInfo, + build_range_infos: SmallVec<[AccelerationStructureBuildRangeInfo; 8]>, + ) -> &mut Self { + let mut used_resources = Vec::new(); + add_build_geometry_resources(&mut used_resources, &info); + + self.add_command( + "build_acceleration_structure", + used_resources, + move |out: &mut UnsafeCommandBufferBuilder| { + out.build_acceleration_structure_unchecked(&info, &build_range_infos); + }, + ); + + self + } + + /// Builds or updates an acceleration structure, using [`AccelerationStructureBuildRangeInfo`] + /// elements stored in an indirect buffer. + /// + /// # Safety + /// + /// The same requirements as for [`build_acceleration_structure`]. In addition, the following + /// requirements apply for each [`AccelerationStructureBuildRangeInfo`] element contained in + /// `indirect_buffer`: + /// - [`primitive_count`] must not be greater than the corresponding element of + /// `max_primitive_counts`. + /// - If `info.geometries` is [`AccelerationStructureGeometries::Instances`], then + /// [`primitive_count`] must not be greater than the [`max_instance_count`] limit. + /// Otherwise, it must not be greater than the [`max_primitive_count`] limit. + /// + /// If `info.geometries` is [`AccelerationStructureGeometries::Triangles`], then: + /// - [`primitive_offset`] must be a multiple of: + /// - [`index_data.index_type().size()`] if [`index_data`] is `Some`. + /// - The byte size of the smallest component of [`vertex_format`] if [`index_data`] is + /// `None`. + /// - [`transform_offset`] must be a multiple of 16. + /// - The size of [`vertex_data`] must be at least
+ /// [`primitive_offset`] + ([`first_vertex`] + 3 * [`primitive_count`]) * [`vertex_stride`] + ///
if [`index_data`] is `None`, and as in [`build_acceleration_structure`] if + /// [`index_data`] is `Some`. + /// - The size of [`index_data`] must be at least
+ /// [`primitive_offset`] + 3 * [`primitive_count`] * + /// [`index_data.index_type().size()`]. + /// - The size of [`transform_data`] must be at least
+ /// [`transform_offset`] + `size_of::()`. + /// + /// If `info.geometries` is [`AccelerationStructureGeometries::Aabbs`], then: + /// - [`primitive_offset`] must be a multiple of 8. + /// - The size of [`data`](AccelerationStructureGeometryAabbsData::data) must be at least
+ /// [`primitive_offset`] + [`primitive_count`] * + /// [`stride`](AccelerationStructureGeometryAabbsData::stride). + /// + /// If `info.geometries` is [`AccelerationStructureGeometries::Instances`], then: + /// - [`primitive_offset`] must be a multiple of 16. + /// - The size of [`data`](AccelerationStructureGeometryInstancesData::data) must be at least: + /// - [`primitive_offset`] + [`primitive_count`] * + /// `size_of::()`
if + /// [`data`](AccelerationStructureGeometryInstancesData::data) is + /// [`AccelerationStructureGeometryInstancesDataType::Values`]. + /// - [`primitive_offset`] + [`primitive_count`] * + /// `size_of::()`
if + /// [`data`](AccelerationStructureGeometryInstancesData::data) is + /// [`AccelerationStructureGeometryInstancesDataType::Pointers`]. + /// + /// [`build_acceleration_structure`]: Self::build_acceleration_structure + /// [`primitive_count`]: AccelerationStructureBuildRangeInfo::primitive_count + /// [`max_instance_count`]: crate::device::Properties::max_instance_count + /// [`max_primitive_count`]: crate::device::Properties::max_primitive_count + /// [`primitive_offset`]: AccelerationStructureBuildRangeInfo::primitive_offset + /// [`index_data.index_type().size()`]: AccelerationStructureGeometryTrianglesData::index_data + /// [`index_data`]: AccelerationStructureGeometryTrianglesData::index_data + /// [`vertex_format`]: AccelerationStructureGeometryTrianglesData::vertex_format + /// [`transform_offset`]: AccelerationStructureBuildRangeInfo::transform_offset + /// [`vertex_data`]: AccelerationStructureGeometryTrianglesData::vertex_data + /// [`first_vertex`]: AccelerationStructureBuildRangeInfo::first_vertex + /// [`vertex_stride`]: AccelerationStructureGeometryTrianglesData::vertex_stride + /// [`transform_data`]: AccelerationStructureGeometryTrianglesData::transform_data + pub unsafe fn build_acceleration_structure_indirect( + &mut self, + info: AccelerationStructureBuildGeometryInfo, + indirect_buffer: Subbuffer<[u8]>, + stride: u32, + max_primitive_counts: SmallVec<[u32; 8]>, + ) -> Result<&mut Self, Box> { + self.validate_build_acceleration_structure_indirect( + &info, + &indirect_buffer, + stride, + &max_primitive_counts, + )?; + + Ok(self.build_acceleration_structure_indirect_unchecked( + info, + indirect_buffer, + stride, + max_primitive_counts, + )) + } + + fn validate_build_acceleration_structure_indirect( + &self, + info: &AccelerationStructureBuildGeometryInfo, + indirect_buffer: &Subbuffer<[u8]>, + stride: u32, + max_primitive_counts: &[u32], + ) -> Result<(), Box> { + self.inner.validate_build_acceleration_structure_indirect( + info, + indirect_buffer, + stride, + max_primitive_counts, + )?; + + if self.builder_state.render_pass.is_some() { + return Err(Box::new(ValidationError { + context: "self".into(), + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdBuildAccelerationStructuresIndirectKHR-renderpass"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn build_acceleration_structure_indirect_unchecked( + &mut self, + info: AccelerationStructureBuildGeometryInfo, + indirect_buffer: Subbuffer<[u8]>, + stride: u32, + max_primitive_counts: SmallVec<[u32; 8]>, + ) -> &mut Self { + let mut used_resources = Vec::new(); + add_build_geometry_resources(&mut used_resources, &info); + add_indirect_buffer_resources(&mut used_resources, &indirect_buffer); + + self.add_command( + "build_acceleration_structure_indirect", + used_resources, + move |out: &mut UnsafeCommandBufferBuilder
| { + out.build_acceleration_structure_indirect_unchecked( + &info, + &indirect_buffer, + stride, + &max_primitive_counts, + ); + }, + ); + + self + } + + /// Copies the data of one acceleration structure to another. + /// + /// # Safety + /// + /// - `info.src` must have been built when this command is executed. + /// - If `info.mode` is [`CopyAccelerationStructureMode::Compact`], then `info.src` must have + /// been built with [`BuildAccelerationStructureFlags::ALLOW_COMPACTION`]. + /// + /// [`CopyAccelerationStructureMode::Compact`]: crate::acceleration_structure::CopyAccelerationStructureMode::Compact + /// [`BuildAccelerationStructureFlags::ALLOW_COMPACTION`]: crate::acceleration_structure::BuildAccelerationStructureFlags::ALLOW_COMPACTION + #[inline] + pub unsafe fn copy_acceleration_structure( + &mut self, + info: CopyAccelerationStructureInfo, + ) -> Result<&mut Self, Box> { + self.validate_copy_acceleration_structure(&info)?; + + Ok(self.copy_acceleration_structure_unchecked(info)) + } + + fn validate_copy_acceleration_structure( + &self, + info: &CopyAccelerationStructureInfo, + ) -> Result<(), Box> { + self.inner.validate_copy_acceleration_structure(info)?; + + if self.builder_state.render_pass.is_some() { + return Err(Box::new(ValidationError { + context: "self".into(), + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdCopyAccelerationStructureKHR-renderpass"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn copy_acceleration_structure_unchecked( + &mut self, + info: CopyAccelerationStructureInfo, + ) -> &mut Self { + let &CopyAccelerationStructureInfo { + ref src, + ref dst, + mode: _, + _ne: _, + } = &info; + + let src_buffer = src.buffer(); + let dst_buffer = dst.buffer(); + self.add_command( + "copy_acceleration_structure", + [ + ( + ResourceInCommand::Source.into(), + Resource::Buffer { + buffer: src_buffer.clone(), + range: 0..src_buffer.size(), // TODO: + memory_access: + PipelineStageAccessFlags::AccelerationStructureCopy_AccelerationStructureRead, + }, + ), + ( + ResourceInCommand::Destination.into(), + Resource::Buffer { + buffer: dst_buffer.clone(), + range: 0..dst_buffer.size(), // TODO: + memory_access: + PipelineStageAccessFlags::AccelerationStructureCopy_AccelerationStructureWrite, + }, + ), + ] + .into_iter() + .collect(), + move |out: &mut UnsafeCommandBufferBuilder| { + out.copy_acceleration_structure_unchecked(&info); + }, + ); + + self + } + + /// Serializes the data of an acceleration structure and writes it to a buffer. + /// + /// # Safety + /// + /// - `info.src` must have been built when this command is executed. + /// - `info.dst` must be large enough to hold the serialized form of `info.src`. This can be + /// queried using [`write_acceleration_structures_properties`] with a query pool whose type is + /// [`QueryType::AccelerationStructureSerializationSize`]. + /// + /// [`write_acceleration_structures_properties`]: Self::write_acceleration_structures_properties + #[inline] + pub unsafe fn copy_acceleration_structure_to_memory( + &mut self, + info: CopyAccelerationStructureToMemoryInfo, + ) -> Result<&mut Self, Box> { + self.validate_copy_acceleration_structure_to_memory(&info)?; + + Ok(self.copy_acceleration_structure_to_memory_unchecked(info)) + } + + fn validate_copy_acceleration_structure_to_memory( + &self, + info: &CopyAccelerationStructureToMemoryInfo, + ) -> Result<(), Box> { + self.inner + .validate_copy_acceleration_structure_to_memory(info)?; + + if self.builder_state.render_pass.is_some() { + return Err(Box::new(ValidationError { + context: "self".into(), + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdCopyAccelerationStructureToMemoryKHR-renderpass"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn copy_acceleration_structure_to_memory_unchecked( + &mut self, + info: CopyAccelerationStructureToMemoryInfo, + ) -> &mut Self { + let &CopyAccelerationStructureToMemoryInfo { + ref src, + ref dst, + mode: _, + _ne: _, + } = &info; + + let src_buffer = src.buffer(); + self.add_command( + "copy_acceleration_structure_to_memory", + [ + ( + ResourceInCommand::Source.into(), + Resource::Buffer { + buffer: src_buffer.clone(), + range: 0..src_buffer.size(), // TODO: + memory_access: + PipelineStageAccessFlags::AccelerationStructureCopy_AccelerationStructureRead, + }, + ), + ( + ResourceInCommand::Destination.into(), + Resource::Buffer { + buffer: dst.clone(), + range: 0..dst.size(), // TODO: + memory_access: + PipelineStageAccessFlags::AccelerationStructureCopy_TransferWrite, + }, + ), + ] + .into_iter() + .collect(), + move |out: &mut UnsafeCommandBufferBuilder| { + out.copy_acceleration_structure_to_memory_unchecked(&info); + }, + ); + + self + } + + /// Reads data of a previously serialized acceleration structure from a buffer, and + /// deserializes it back into an acceleration structure. + /// + /// # Safety + /// + /// - `info.src` must contain data previously serialized using + /// [`copy_acceleration_structure_to_memory`], and must have a format compatible with the + /// device (as queried by [`Device::acceleration_structure_is_compatible`]). + /// - `info.dst.size()` must be at least the size that the structure in `info.src` had + /// before it was serialized. + /// + /// [`copy_acceleration_structure_to_memory`]: Self::copy_acceleration_structure_to_memory + /// [`Device::acceleration_structure_is_compatible`]: crate::device::Device::acceleration_structure_is_compatible + #[inline] + pub unsafe fn copy_memory_to_acceleration_structure( + &mut self, + info: CopyMemoryToAccelerationStructureInfo, + ) -> Result<&mut Self, Box> { + self.validate_copy_memory_to_acceleration_structure(&info)?; + + Ok(self.copy_memory_to_acceleration_structure_unchecked(info)) + } + + fn validate_copy_memory_to_acceleration_structure( + &self, + info: &CopyMemoryToAccelerationStructureInfo, + ) -> Result<(), Box> { + self.inner + .validate_copy_memory_to_acceleration_structure(info)?; + + if self.builder_state.render_pass.is_some() { + return Err(Box::new(ValidationError { + context: "self".into(), + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdCopyMemoryToAccelerationStructureKHR-renderpass"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn copy_memory_to_acceleration_structure_unchecked( + &mut self, + info: CopyMemoryToAccelerationStructureInfo, + ) -> &mut Self { + let &CopyMemoryToAccelerationStructureInfo { + ref src, + ref dst, + mode: _, + _ne: _, + } = &info; + + let dst_buffer = dst.buffer(); + self.add_command( + "copy_memory_to_acceleration_structure", + [ + ( + ResourceInCommand::Source.into(), + Resource::Buffer { + buffer: src.clone(), + range: 0..src.size(), // TODO: + memory_access: + PipelineStageAccessFlags::AccelerationStructureCopy_TransferRead, + }, + ), + ( + ResourceInCommand::Destination.into(), + Resource::Buffer { + buffer: dst_buffer.clone(), + range: 0..dst_buffer.size(), // TODO: + memory_access: + PipelineStageAccessFlags::AccelerationStructureCopy_AccelerationStructureWrite, + }, + ), + ] + .into_iter() + .collect(), + move |out: &mut UnsafeCommandBufferBuilder| { + out.copy_memory_to_acceleration_structure_unchecked(&info); + }, + ); + + self + } + + /// Writes the properties of one or more acceleration structures to a query. + /// + /// For each element in `acceleration_structures`, one query is written, in numeric order + /// starting at `first_query`. + /// + /// # Safety + /// + /// - All elements of `acceleration_structures` must have been built when this command is + /// executed. + /// - If `query_pool.query_type()` is [`QueryType::AccelerationStructureCompactedSize`], + /// all elements of `acceleration_structures` must have been built with + /// [`BuildAccelerationStructureFlags::ALLOW_COMPACTION`]. + /// - The queries must be unavailable, ensured by calling [`reset_query_pool`]. + /// + /// [`BuildAccelerationStructureFlags::ALLOW_COMPACTION`]: crate::acceleration_structure::BuildAccelerationStructureFlags::ALLOW_COMPACTION + /// [`reset_query_pool`]: Self::reset_query_pool + #[inline] + pub unsafe fn write_acceleration_structures_properties( + &mut self, + acceleration_structures: SmallVec<[Arc; 4]>, + query_pool: Arc, + first_query: u32, + ) -> Result<&mut Self, Box> { + self.validate_write_acceleration_structures_properties( + &acceleration_structures, + &query_pool, + first_query, + )?; + + Ok(self.write_acceleration_structures_properties_unchecked( + acceleration_structures, + query_pool, + first_query, + )) + } + + fn validate_write_acceleration_structures_properties( + &self, + acceleration_structures: &[Arc], + query_pool: &QueryPool, + first_query: u32, + ) -> Result<(), Box> { + self.inner + .validate_write_acceleration_structures_properties( + acceleration_structures, + query_pool, + first_query, + )?; + + if self.builder_state.render_pass.is_some() { + return Err(Box::new(ValidationError { + context: "self".into(), + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdWriteAccelerationStructuresPropertiesKHR-renderpass"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn write_acceleration_structures_properties_unchecked( + &mut self, + acceleration_structures: SmallVec<[Arc; 4]>, + query_pool: Arc, + first_query: u32, + ) -> &mut Self { + if acceleration_structures.is_empty() { + return self; + } + + self.add_command( + "write_acceleration_structures_properties", + acceleration_structures.iter().enumerate().map(|(index, acs)| { + let index = index as u32; + let buffer = acs.buffer(); + + ( + ResourceInCommand::AccelerationStructure { index }.into(), + Resource::Buffer { + buffer: buffer.clone(), + range: 0..buffer.size(), // TODO: + memory_access: + PipelineStageAccessFlags::AccelerationStructureCopy_AccelerationStructureRead, + }, + ) + }).collect(), + move |out: &mut UnsafeCommandBufferBuilder| { + out.write_acceleration_structures_properties_unchecked( + &acceleration_structures, + &query_pool, + first_query, + ); + }, + ); + + self + } +} + +fn add_build_geometry_resources( + used_resources: &mut Vec<(ResourceUseRef2, Resource)>, + info: &AccelerationStructureBuildGeometryInfo, +) { + let &AccelerationStructureBuildGeometryInfo { + flags: _, + ref mode, + ref dst_acceleration_structure, + ref geometries, + ref scratch_data, + _ne: _, + } = &info; + + match geometries { + AccelerationStructureGeometries::Triangles(geometries) => { + used_resources.extend( + geometries.iter().enumerate().flat_map(|(index, triangles_data)| { + let index = index as u32; + let &AccelerationStructureGeometryTrianglesData { + flags: _, + vertex_format: _, + ref vertex_data, + vertex_stride: _, + max_vertex: _, + ref index_data, + ref transform_data, + _ne, + } = triangles_data; + + [ + ( + ResourceInCommand::GeometryTrianglesVertexData { index }.into(), + Resource::Buffer { + buffer: vertex_data.clone(), + range: 0..vertex_data.size(), // TODO: + memory_access: PipelineStageAccessFlags::AccelerationStructureBuild_ShaderSampledRead + | PipelineStageAccessFlags::AccelerationStructureBuild_ShaderStorageRead, + }, + ), + ].into_iter() + .chain(index_data.as_ref().map(|index_data| { + let index_data_bytes = index_data.as_bytes(); + + ( + ResourceInCommand::GeometryTrianglesIndexData { index }.into(), + Resource::Buffer { + buffer: index_data_bytes.clone(), + range: 0..index_data_bytes.size(), // TODO: + memory_access: PipelineStageAccessFlags::AccelerationStructureBuild_ShaderSampledRead + | PipelineStageAccessFlags::AccelerationStructureBuild_ShaderStorageRead, + }, + ) + })) + .chain(transform_data.as_ref().map(|transform_data| { + ( + ResourceInCommand::GeometryTrianglesTransformData { index }.into(), + Resource::Buffer { + buffer: transform_data.as_bytes().clone(), + range: 0..transform_data.size(), // TODO: + memory_access: PipelineStageAccessFlags::AccelerationStructureBuild_ShaderSampledRead + | PipelineStageAccessFlags::AccelerationStructureBuild_ShaderStorageRead, + }, + ) + })) + }) + ); + } + AccelerationStructureGeometries::Aabbs(geometries) => { + used_resources.extend(geometries.iter().enumerate().map(|(index, aabbs_data)| { + let index = index as u32; + let &AccelerationStructureGeometryAabbsData { + flags: _, + ref data, + stride: _, + _ne: _, + } = aabbs_data; + + ( + ResourceInCommand::GeometryAabbsData { index }.into(), + Resource::Buffer { + buffer: data.as_bytes().clone(), + range: 0..data.size(), // TODO: + memory_access: PipelineStageAccessFlags::AccelerationStructureBuild_ShaderSampledRead + | PipelineStageAccessFlags::AccelerationStructureBuild_ShaderStorageRead, + }, + ) + })); + } + AccelerationStructureGeometries::Instances(instances_data) => { + let &AccelerationStructureGeometryInstancesData { + flags: _, + ref data, + _ne: _, + } = instances_data; + + let data = match data { + AccelerationStructureGeometryInstancesDataType::Values(data) => data.as_bytes(), + AccelerationStructureGeometryInstancesDataType::Pointers(data) => data.as_bytes(), + }; + let size = data.size(); + + used_resources.push(( + ResourceInCommand::GeometryInstancesData.into(), + Resource::Buffer { + buffer: data.clone(), + range: 0..size, // TODO: + memory_access: PipelineStageAccessFlags::AccelerationStructureBuild_ShaderSampledRead + | PipelineStageAccessFlags::AccelerationStructureBuild_ShaderStorageRead, + }, + )); + } + }; + + if let BuildAccelerationStructureMode::Update(src_acceleration_structure) = mode { + let src_buffer = src_acceleration_structure.buffer(); + used_resources.push(( + ResourceInCommand::Source.into(), + Resource::Buffer { + buffer: src_buffer.clone(), + range: 0..src_buffer.size(), // TODO: + memory_access: + PipelineStageAccessFlags::AccelerationStructureBuild_AccelerationStructureRead, + }, + )); + } + + let dst_buffer = dst_acceleration_structure.buffer(); + used_resources.push(( + ResourceInCommand::Destination.into(), + Resource::Buffer { + buffer: dst_buffer.clone(), + range: 0..dst_buffer.size(), // TODO: + memory_access: + PipelineStageAccessFlags::AccelerationStructureBuild_AccelerationStructureWrite, + }, + )); + used_resources.push(( + ResourceInCommand::ScratchData.into(), + Resource::Buffer { + buffer: scratch_data.clone(), + range: 0..scratch_data.size(), // TODO: + memory_access: PipelineStageAccessFlags::AccelerationStructureBuild_AccelerationStructureRead + | PipelineStageAccessFlags::AccelerationStructureBuild_AccelerationStructureWrite, + }, + )); +} + +fn add_indirect_buffer_resources( + used_resources: &mut Vec<(ResourceUseRef2, Resource)>, + indirect_buffer: &Subbuffer<[u8]>, +) { + used_resources.push(( + ResourceInCommand::IndirectBuffer.into(), + Resource::Buffer { + buffer: indirect_buffer.as_bytes().clone(), + range: 0..indirect_buffer.size(), // TODO: + memory_access: PipelineStageAccessFlags::AccelerationStructureBuild_IndirectCommandRead, + }, + )); +} + +impl UnsafeCommandBufferBuilder +where + A: CommandBufferAllocator, +{ + pub unsafe fn build_acceleration_structure( + &mut self, + info: &AccelerationStructureBuildGeometryInfo, + build_range_infos: &[AccelerationStructureBuildRangeInfo], + ) -> Result<&mut Self, Box> { + self.validate_build_acceleration_structure(info, build_range_infos)?; + + Ok(self.build_acceleration_structure_unchecked(info, build_range_infos)) + } + fn validate_build_acceleration_structure( &self, info: &AccelerationStructureBuildGeometryInfo, @@ -118,15 +826,6 @@ where })); } - if self.builder_state.render_pass.is_some() { - return Err(Box::new(ValidationError { - context: "self".into(), - problem: "a render pass instance is active".into(), - vuids: &["VUID-vkCmdBuildAccelerationStructuresKHR-renderpass"], - ..Default::default() - })); - } - // VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-parameter info.validate(self.device()) .map_err(|err| err.add_context("info"))?; @@ -789,96 +1488,63 @@ where #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn build_acceleration_structure_unchecked( &mut self, - info: AccelerationStructureBuildGeometryInfo, - build_range_infos: SmallVec<[AccelerationStructureBuildRangeInfo; 8]>, + info: &AccelerationStructureBuildGeometryInfo, + build_range_infos: &[AccelerationStructureBuildRangeInfo], ) -> &mut Self { - let mut used_resources = Vec::new(); - add_build_geometry_resources(&mut used_resources, &info); + let (mut info_vk, geometries_vk) = info.to_vulkan(); + info_vk = ash::vk::AccelerationStructureBuildGeometryInfoKHR { + geometry_count: geometries_vk.len() as u32, + p_geometries: geometries_vk.as_ptr(), + ..info_vk + }; - self.add_command( - "build_acceleration_structure", - used_resources, - move |out: &mut UnsafeCommandBufferBuilder| { - out.build_acceleration_structure(&info, &build_range_infos); - }, + let build_range_info_elements_vk: SmallVec<[_; 8]> = build_range_infos + .iter() + .map(|build_range_info| { + let &AccelerationStructureBuildRangeInfo { + primitive_count, + primitive_offset, + first_vertex, + transform_offset, + } = build_range_info; + + ash::vk::AccelerationStructureBuildRangeInfoKHR { + primitive_count, + primitive_offset, + first_vertex, + transform_offset, + } + }) + .collect(); + let build_range_info_pointers_vk: SmallVec<[_; 8]> = build_range_info_elements_vk + .iter() + .map(|element| element as *const _) + .collect(); + + let fns = self.device().fns(); + (fns.khr_acceleration_structure + .cmd_build_acceleration_structures_khr)( + self.handle(), + 1, + &info_vk, + build_range_info_pointers_vk.as_ptr(), ); self } - /// Builds or updates an acceleration structure, using [`AccelerationStructureBuildRangeInfo`] - /// elements stored in an indirect buffer. - /// - /// # Safety - /// - /// The same requirements as for [`build_acceleration_structure`]. In addition, the following - /// requirements apply for each [`AccelerationStructureBuildRangeInfo`] element contained in - /// `indirect_buffer`: - /// - [`primitive_count`] must not be greater than the corresponding element of - /// `max_primitive_counts`. - /// - If `info.geometries` is [`AccelerationStructureGeometries::Instances`], then - /// [`primitive_count`] must not be greater than the [`max_instance_count`] limit. - /// Otherwise, it must not be greater than the [`max_primitive_count`] limit. - /// - /// If `info.geometries` is [`AccelerationStructureGeometries::Triangles`], then: - /// - [`primitive_offset`] must be a multiple of: - /// - [`index_data.index_type().size()`] if [`index_data`] is `Some`. - /// - The byte size of the smallest component of [`vertex_format`] if [`index_data`] is - /// `None`. - /// - [`transform_offset`] must be a multiple of 16. - /// - The size of [`vertex_data`] must be at least
- /// [`primitive_offset`] + ([`first_vertex`] + 3 * [`primitive_count`]) * [`vertex_stride`] - ///
if [`index_data`] is `None`, and as in [`build_acceleration_structure`] if - /// [`index_data`] is `Some`. - /// - The size of [`index_data`] must be at least
- /// [`primitive_offset`] + 3 * [`primitive_count`] * - /// [`index_data.index_type().size()`]. - /// - The size of [`transform_data`] must be at least
- /// [`transform_offset`] + `size_of::()`. - /// - /// If `info.geometries` is [`AccelerationStructureGeometries::Aabbs`], then: - /// - [`primitive_offset`] must be a multiple of 8. - /// - The size of [`data`](AccelerationStructureGeometryAabbsData::data) must be at least
- /// [`primitive_offset`] + [`primitive_count`] * - /// [`stride`](AccelerationStructureGeometryAabbsData::stride). - /// - /// If `info.geometries` is [`AccelerationStructureGeometries::Instances`], then: - /// - [`primitive_offset`] must be a multiple of 16. - /// - The size of [`data`](AccelerationStructureGeometryInstancesData::data) must be at least: - /// - [`primitive_offset`] + [`primitive_count`] * - /// `size_of::()`
if - /// [`data`](AccelerationStructureGeometryInstancesData::data) is - /// [`AccelerationStructureGeometryInstancesDataType::Values`]. - /// - [`primitive_offset`] + [`primitive_count`] * - /// `size_of::()`
if - /// [`data`](AccelerationStructureGeometryInstancesData::data) is - /// [`AccelerationStructureGeometryInstancesDataType::Pointers`]. - /// - /// [`build_acceleration_structure`]: Self::build_acceleration_structure - /// [`primitive_count`]: AccelerationStructureBuildRangeInfo::primitive_count - /// [`max_instance_count`]: crate::device::Properties::max_instance_count - /// [`max_primitive_count`]: crate::device::Properties::max_primitive_count - /// [`primitive_offset`]: AccelerationStructureBuildRangeInfo::primitive_offset - /// [`index_data.index_type().size()`]: AccelerationStructureGeometryTrianglesData::index_data - /// [`index_data`]: AccelerationStructureGeometryTrianglesData::index_data - /// [`vertex_format`]: AccelerationStructureGeometryTrianglesData::vertex_format - /// [`transform_offset`]: AccelerationStructureBuildRangeInfo::transform_offset - /// [`vertex_data`]: AccelerationStructureGeometryTrianglesData::vertex_data - /// [`first_vertex`]: AccelerationStructureBuildRangeInfo::first_vertex - /// [`vertex_stride`]: AccelerationStructureGeometryTrianglesData::vertex_stride - /// [`transform_data`]: AccelerationStructureGeometryTrianglesData::transform_data pub unsafe fn build_acceleration_structure_indirect( &mut self, - info: AccelerationStructureBuildGeometryInfo, - indirect_buffer: Subbuffer<[u8]>, + info: &AccelerationStructureBuildGeometryInfo, + indirect_buffer: &Subbuffer<[u8]>, stride: u32, - max_primitive_counts: SmallVec<[u32; 8]>, + max_primitive_counts: &[u32], ) -> Result<&mut Self, Box> { self.validate_build_acceleration_structure_indirect( - &info, - &indirect_buffer, + info, + indirect_buffer, stride, - &max_primitive_counts, + max_primitive_counts, )?; Ok(self.build_acceleration_structure_indirect_unchecked( @@ -921,15 +1587,6 @@ where })); } - if self.builder_state.render_pass.is_some() { - return Err(Box::new(ValidationError { - context: "self".into(), - problem: "a render pass instance is active".into(), - vuids: &["VUID-vkCmdBuildAccelerationStructuresIndirectKHR-renderpass"], - ..Default::default() - })); - } - // VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-parameter info.validate(self.device()) .map_err(|err| err.add_context("info"))?; @@ -1417,47 +2074,37 @@ where #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn build_acceleration_structure_indirect_unchecked( &mut self, - info: AccelerationStructureBuildGeometryInfo, - indirect_buffer: Subbuffer<[u8]>, + info: &AccelerationStructureBuildGeometryInfo, + indirect_buffer: &Subbuffer<[u8]>, stride: u32, - max_primitive_counts: SmallVec<[u32; 8]>, + max_primitive_counts: &[u32], ) -> &mut Self { - let mut used_resources = Vec::new(); - add_build_geometry_resources(&mut used_resources, &info); - add_indirect_buffer_resources(&mut used_resources, &indirect_buffer); + let (mut info_vk, geometries_vk) = info.to_vulkan(); + info_vk = ash::vk::AccelerationStructureBuildGeometryInfoKHR { + geometry_count: geometries_vk.len() as u32, + p_geometries: geometries_vk.as_ptr(), + ..info_vk + }; - self.add_command( - "build_acceleration_structure_indirect", - used_resources, - move |out: &mut UnsafeCommandBufferBuilder
| { - out.build_acceleration_structure_indirect( - &info, - &indirect_buffer, - stride, - &max_primitive_counts, - ); - }, + let fns = self.device().fns(); + (fns.khr_acceleration_structure + .cmd_build_acceleration_structures_indirect_khr)( + self.handle(), + 1, + &info_vk, + &indirect_buffer.device_address().unwrap().get(), + &stride, + &max_primitive_counts.as_ptr(), ); self } - /// Copies the data of one acceleration structure to another. - /// - /// # Safety - /// - /// - `info.src` must have been built when this command is executed. - /// - If `info.mode` is [`CopyAccelerationStructureMode::Compact`], then `info.src` must have - /// been built with [`BuildAccelerationStructureFlags::ALLOW_COMPACTION`]. - /// - /// [`CopyAccelerationStructureMode::Compact`]: crate::acceleration_structure::CopyAccelerationStructureMode::Compact - /// [`BuildAccelerationStructureFlags::ALLOW_COMPACTION`]: crate::acceleration_structure::BuildAccelerationStructureFlags::ALLOW_COMPACTION - #[inline] pub unsafe fn copy_acceleration_structure( &mut self, - info: CopyAccelerationStructureInfo, + info: &CopyAccelerationStructureInfo, ) -> Result<&mut Self, Box> { - self.validate_copy_acceleration_structure(&info)?; + self.validate_copy_acceleration_structure(info)?; Ok(self.copy_acceleration_structure_unchecked(info)) } @@ -1479,15 +2126,6 @@ where })); } - if self.builder_state.render_pass.is_some() { - return Err(Box::new(ValidationError { - context: "self".into(), - problem: "a render pass instance is active".into(), - vuids: &["VUID-vkCmdCopyAccelerationStructureKHR-renderpass"], - ..Default::default() - })); - } - // VUID-vkCmdCopyAccelerationStructureKHR-pInfo-parameter info.validate(self.device()) .map_err(|err| err.add_context("info"))?; @@ -1498,65 +2136,34 @@ where #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn copy_acceleration_structure_unchecked( &mut self, - info: CopyAccelerationStructureInfo, + info: &CopyAccelerationStructureInfo, ) -> &mut Self { let &CopyAccelerationStructureInfo { ref src, ref dst, - mode: _, + mode, _ne: _, - } = &info; + } = info; - let src_buffer = src.buffer(); - let dst_buffer = dst.buffer(); - self.add_command( - "copy_acceleration_structure", - [ - ( - ResourceInCommand::Source.into(), - Resource::Buffer { - buffer: src_buffer.clone(), - range: 0..src_buffer.size(), // TODO: - memory_access: - PipelineStageAccessFlags::AccelerationStructureCopy_AccelerationStructureRead, - }, - ), - ( - ResourceInCommand::Destination.into(), - Resource::Buffer { - buffer: dst_buffer.clone(), - range: 0..dst_buffer.size(), // TODO: - memory_access: - PipelineStageAccessFlags::AccelerationStructureCopy_AccelerationStructureWrite, - }, - ), - ] - .into_iter() - .collect(), - move |out: &mut UnsafeCommandBufferBuilder| { - out.copy_acceleration_structure(&info); - }, - ); + let info_vk = ash::vk::CopyAccelerationStructureInfoKHR { + src: src.handle(), + dst: dst.handle(), + mode: mode.into(), + ..Default::default() + }; + + let fns = self.device().fns(); + (fns.khr_acceleration_structure + .cmd_copy_acceleration_structure_khr)(self.handle(), &info_vk); self } - /// Serializes the data of an acceleration structure and writes it to a buffer. - /// - /// # Safety - /// - /// - `info.src` must have been built when this command is executed. - /// - `info.dst` must be large enough to hold the serialized form of `info.src`. This can be - /// queried using [`write_acceleration_structures_properties`] with a query pool whose type is - /// [`QueryType::AccelerationStructureSerializationSize`]. - /// - /// [`write_acceleration_structures_properties`]: Self::write_acceleration_structures_properties - #[inline] pub unsafe fn copy_acceleration_structure_to_memory( &mut self, - info: CopyAccelerationStructureToMemoryInfo, + info: &CopyAccelerationStructureToMemoryInfo, ) -> Result<&mut Self, Box> { - self.validate_copy_acceleration_structure_to_memory(&info)?; + self.validate_copy_acceleration_structure_to_memory(info)?; Ok(self.copy_acceleration_structure_to_memory_unchecked(info)) } @@ -1578,15 +2185,6 @@ where })); } - if self.builder_state.render_pass.is_some() { - return Err(Box::new(ValidationError { - context: "self".into(), - problem: "a render pass instance is active".into(), - vuids: &["VUID-vkCmdCopyAccelerationStructureToMemoryKHR-renderpass"], - ..Default::default() - })); - } - if info.dst.device_address().unwrap().get() % 256 != 0 { return Err(Box::new(ValidationError { context: "info.dst".into(), @@ -1606,67 +2204,36 @@ where #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn copy_acceleration_structure_to_memory_unchecked( &mut self, - info: CopyAccelerationStructureToMemoryInfo, + info: &CopyAccelerationStructureToMemoryInfo, ) -> &mut Self { let &CopyAccelerationStructureToMemoryInfo { ref src, ref dst, - mode: _, + mode, _ne: _, - } = &info; + } = info; - let src_buffer = src.buffer(); - self.add_command( - "copy_acceleration_structure_to_memory", - [ - ( - ResourceInCommand::Source.into(), - Resource::Buffer { - buffer: src_buffer.clone(), - range: 0..src_buffer.size(), // TODO: - memory_access: - PipelineStageAccessFlags::AccelerationStructureCopy_AccelerationStructureRead, - }, - ), - ( - ResourceInCommand::Destination.into(), - Resource::Buffer { - buffer: dst.clone(), - range: 0..dst.size(), // TODO: - memory_access: - PipelineStageAccessFlags::AccelerationStructureCopy_TransferWrite, - }, - ), - ] - .into_iter() - .collect(), - move |out: &mut UnsafeCommandBufferBuilder| { - out.copy_acceleration_structure_to_memory(&info); + let info_vk = ash::vk::CopyAccelerationStructureToMemoryInfoKHR { + src: src.handle(), + dst: ash::vk::DeviceOrHostAddressKHR { + device_address: dst.device_address().unwrap().get(), }, - ); + mode: mode.into(), + ..Default::default() + }; + + let fns = self.device().fns(); + (fns.khr_acceleration_structure + .cmd_copy_acceleration_structure_to_memory_khr)(self.handle(), &info_vk); self } - /// Reads data of a previously serialized acceleration structure from a buffer, and - /// deserializes it back into an acceleration structure. - /// - /// # Safety - /// - /// - `info.src` must contain data previously serialized using - /// [`copy_acceleration_structure_to_memory`], and must have a format compatible with the - /// device (as queried by [`Device::acceleration_structure_is_compatible`]). - /// - `info.dst.size()` must be at least the size that the structure in `info.src` had - /// before it was serialized. - /// - /// [`copy_acceleration_structure_to_memory`]: Self::copy_acceleration_structure_to_memory - /// [`Device::acceleration_structure_is_compatible`]: crate::device::Device::acceleration_structure_is_compatible - #[inline] pub unsafe fn copy_memory_to_acceleration_structure( &mut self, - info: CopyMemoryToAccelerationStructureInfo, + info: &CopyMemoryToAccelerationStructureInfo, ) -> Result<&mut Self, Box> { - self.validate_copy_memory_to_acceleration_structure(&info)?; + self.validate_copy_memory_to_acceleration_structure(info)?; Ok(self.copy_memory_to_acceleration_structure_unchecked(info)) } @@ -1688,15 +2255,6 @@ where })); } - if self.builder_state.render_pass.is_some() { - return Err(Box::new(ValidationError { - context: "self".into(), - problem: "a render pass instance is active".into(), - vuids: &["VUID-vkCmdCopyMemoryToAccelerationStructureKHR-renderpass"], - ..Default::default() - })); - } - if info.src.device_address().unwrap().get() % 256 != 0 { return Err(Box::new(ValidationError { context: "info.src".into(), @@ -1716,74 +2274,40 @@ where #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn copy_memory_to_acceleration_structure_unchecked( &mut self, - info: CopyMemoryToAccelerationStructureInfo, + info: &CopyMemoryToAccelerationStructureInfo, ) -> &mut Self { let &CopyMemoryToAccelerationStructureInfo { ref src, ref dst, - mode: _, + mode, _ne: _, - } = &info; + } = info; - let dst_buffer = dst.buffer(); - self.add_command( - "copy_memory_to_acceleration_structure", - [ - ( - ResourceInCommand::Source.into(), - Resource::Buffer { - buffer: src.clone(), - range: 0..src.size(), // TODO: - memory_access: - PipelineStageAccessFlags::AccelerationStructureCopy_TransferRead, - }, - ), - ( - ResourceInCommand::Destination.into(), - Resource::Buffer { - buffer: dst_buffer.clone(), - range: 0..dst_buffer.size(), // TODO: - memory_access: - PipelineStageAccessFlags::AccelerationStructureCopy_AccelerationStructureWrite, - }, - ), - ] - .into_iter() - .collect(), - move |out: &mut UnsafeCommandBufferBuilder| { - out.copy_memory_to_acceleration_structure(&info); + let info_vk = ash::vk::CopyMemoryToAccelerationStructureInfoKHR { + src: ash::vk::DeviceOrHostAddressConstKHR { + device_address: src.device_address().unwrap().get(), }, - ); + dst: dst.handle(), + mode: mode.into(), + ..Default::default() + }; + + let fns = self.device().fns(); + (fns.khr_acceleration_structure + .cmd_copy_memory_to_acceleration_structure_khr)(self.handle(), &info_vk); self } - /// Writes the properties of one or more acceleration structures to a query. - /// - /// For each element in `acceleration_structures`, one query is written, in numeric order - /// starting at `first_query`. - /// - /// # Safety - /// - /// - All elements of `acceleration_structures` must have been built when this command is - /// executed. - /// - If `query_pool.query_type()` is [`QueryType::AccelerationStructureCompactedSize`], - /// all elements of `acceleration_structures` must have been built with - /// [`BuildAccelerationStructureFlags::ALLOW_COMPACTION`]. - /// - The queries must be unavailable, ensured by calling [`reset_query_pool`]. - /// - /// [`BuildAccelerationStructureFlags::ALLOW_COMPACTION`]: crate::acceleration_structure::BuildAccelerationStructureFlags::ALLOW_COMPACTION - /// [`reset_query_pool`]: Self::reset_query_pool - #[inline] pub unsafe fn write_acceleration_structures_properties( &mut self, - acceleration_structures: SmallVec<[Arc; 4]>, - query_pool: Arc, + acceleration_structures: &[Arc], + query_pool: &QueryPool, first_query: u32, ) -> Result<&mut Self, Box> { self.validate_write_acceleration_structures_properties( - &acceleration_structures, - &query_pool, + acceleration_structures, + query_pool, first_query, )?; @@ -1800,10 +2324,6 @@ where query_pool: &QueryPool, first_query: u32, ) -> Result<(), Box> { - if acceleration_structures.is_empty() { - return Ok(()); - } - if !self .queue_family_properties() .queue_flags @@ -1819,15 +2339,6 @@ where })); } - if self.builder_state.render_pass.is_some() { - return Err(Box::new(ValidationError { - context: "self".into(), - problem: "a render pass instance is active".into(), - vuids: &["VUID-vkCmdWriteAccelerationStructuresPropertiesKHR-renderpass"], - ..Default::default() - })); - } - for acs in acceleration_structures { // VUID-vkCmdWriteAccelerationStructuresPropertiesKHR-commonparent assert_eq!(self.device(), acs.device()); @@ -1876,364 +2387,6 @@ where #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn write_acceleration_structures_properties_unchecked( - &mut self, - acceleration_structures: SmallVec<[Arc; 4]>, - query_pool: Arc, - first_query: u32, - ) -> &mut Self { - if acceleration_structures.is_empty() { - return self; - } - - self.add_command( - "write_acceleration_structures_properties", - acceleration_structures.iter().enumerate().map(|(index, acs)| { - let index = index as u32; - let buffer = acs.buffer(); - - ( - ResourceInCommand::AccelerationStructure { index }.into(), - Resource::Buffer { - buffer: buffer.clone(), - range: 0..buffer.size(), // TODO: - memory_access: - PipelineStageAccessFlags::AccelerationStructureCopy_AccelerationStructureRead, - }, - ) - }).collect(), - move |out: &mut UnsafeCommandBufferBuilder| { - out.write_acceleration_structures_properties( - &acceleration_structures, - &query_pool, - first_query, - ); - }, - ); - - self - } -} - -fn add_build_geometry_resources( - used_resources: &mut Vec<(ResourceUseRef2, Resource)>, - info: &AccelerationStructureBuildGeometryInfo, -) { - let &AccelerationStructureBuildGeometryInfo { - flags: _, - ref mode, - ref dst_acceleration_structure, - ref geometries, - ref scratch_data, - _ne: _, - } = &info; - - match geometries { - AccelerationStructureGeometries::Triangles(geometries) => { - used_resources.extend( - geometries.iter().enumerate().flat_map(|(index, triangles_data)| { - let index = index as u32; - let &AccelerationStructureGeometryTrianglesData { - flags: _, - vertex_format: _, - ref vertex_data, - vertex_stride: _, - max_vertex: _, - ref index_data, - ref transform_data, - _ne, - } = triangles_data; - - [ - ( - ResourceInCommand::GeometryTrianglesVertexData { index }.into(), - Resource::Buffer { - buffer: vertex_data.clone(), - range: 0..vertex_data.size(), // TODO: - memory_access: PipelineStageAccessFlags::AccelerationStructureBuild_ShaderSampledRead - | PipelineStageAccessFlags::AccelerationStructureBuild_ShaderStorageRead, - }, - ), - ].into_iter() - .chain(index_data.as_ref().map(|index_data| { - let index_data_bytes = index_data.as_bytes(); - - ( - ResourceInCommand::GeometryTrianglesIndexData { index }.into(), - Resource::Buffer { - buffer: index_data_bytes.clone(), - range: 0..index_data_bytes.size(), // TODO: - memory_access: PipelineStageAccessFlags::AccelerationStructureBuild_ShaderSampledRead - | PipelineStageAccessFlags::AccelerationStructureBuild_ShaderStorageRead, - }, - ) - })) - .chain(transform_data.as_ref().map(|transform_data| { - ( - ResourceInCommand::GeometryTrianglesTransformData { index }.into(), - Resource::Buffer { - buffer: transform_data.as_bytes().clone(), - range: 0..transform_data.size(), // TODO: - memory_access: PipelineStageAccessFlags::AccelerationStructureBuild_ShaderSampledRead - | PipelineStageAccessFlags::AccelerationStructureBuild_ShaderStorageRead, - }, - ) - })) - }) - ); - } - AccelerationStructureGeometries::Aabbs(geometries) => { - used_resources.extend(geometries.iter().enumerate().map(|(index, aabbs_data)| { - let index = index as u32; - let &AccelerationStructureGeometryAabbsData { - flags: _, - ref data, - stride: _, - _ne: _, - } = aabbs_data; - - ( - ResourceInCommand::GeometryAabbsData { index }.into(), - Resource::Buffer { - buffer: data.as_bytes().clone(), - range: 0..data.size(), // TODO: - memory_access: PipelineStageAccessFlags::AccelerationStructureBuild_ShaderSampledRead - | PipelineStageAccessFlags::AccelerationStructureBuild_ShaderStorageRead, - }, - ) - })); - } - AccelerationStructureGeometries::Instances(instances_data) => { - let &AccelerationStructureGeometryInstancesData { - flags: _, - ref data, - _ne: _, - } = instances_data; - - let data = match data { - AccelerationStructureGeometryInstancesDataType::Values(data) => data.as_bytes(), - AccelerationStructureGeometryInstancesDataType::Pointers(data) => data.as_bytes(), - }; - let size = data.size(); - - used_resources.push(( - ResourceInCommand::GeometryInstancesData.into(), - Resource::Buffer { - buffer: data.clone(), - range: 0..size, // TODO: - memory_access: PipelineStageAccessFlags::AccelerationStructureBuild_ShaderSampledRead - | PipelineStageAccessFlags::AccelerationStructureBuild_ShaderStorageRead, - }, - )); - } - }; - - if let BuildAccelerationStructureMode::Update(src_acceleration_structure) = mode { - let src_buffer = src_acceleration_structure.buffer(); - used_resources.push(( - ResourceInCommand::Source.into(), - Resource::Buffer { - buffer: src_buffer.clone(), - range: 0..src_buffer.size(), // TODO: - memory_access: - PipelineStageAccessFlags::AccelerationStructureBuild_AccelerationStructureRead, - }, - )); - } - - let dst_buffer = dst_acceleration_structure.buffer(); - used_resources.push(( - ResourceInCommand::Destination.into(), - Resource::Buffer { - buffer: dst_buffer.clone(), - range: 0..dst_buffer.size(), // TODO: - memory_access: - PipelineStageAccessFlags::AccelerationStructureBuild_AccelerationStructureWrite, - }, - )); - used_resources.push(( - ResourceInCommand::ScratchData.into(), - Resource::Buffer { - buffer: scratch_data.clone(), - range: 0..scratch_data.size(), // TODO: - memory_access: PipelineStageAccessFlags::AccelerationStructureBuild_AccelerationStructureRead - | PipelineStageAccessFlags::AccelerationStructureBuild_AccelerationStructureWrite, - }, - )); -} - -fn add_indirect_buffer_resources( - used_resources: &mut Vec<(ResourceUseRef2, Resource)>, - indirect_buffer: &Subbuffer<[u8]>, -) { - used_resources.push(( - ResourceInCommand::IndirectBuffer.into(), - Resource::Buffer { - buffer: indirect_buffer.as_bytes().clone(), - range: 0..indirect_buffer.size(), // TODO: - memory_access: PipelineStageAccessFlags::AccelerationStructureBuild_IndirectCommandRead, - }, - )); -} - -impl UnsafeCommandBufferBuilder -where - A: CommandBufferAllocator, -{ - pub unsafe fn build_acceleration_structure( - &mut self, - info: &AccelerationStructureBuildGeometryInfo, - build_range_infos: &[AccelerationStructureBuildRangeInfo], - ) -> &mut Self { - let (mut info_vk, geometries_vk) = info.to_vulkan(); - info_vk = ash::vk::AccelerationStructureBuildGeometryInfoKHR { - geometry_count: geometries_vk.len() as u32, - p_geometries: geometries_vk.as_ptr(), - ..info_vk - }; - - let build_range_info_elements_vk: SmallVec<[_; 8]> = build_range_infos - .iter() - .map(|build_range_info| { - let &AccelerationStructureBuildRangeInfo { - primitive_count, - primitive_offset, - first_vertex, - transform_offset, - } = build_range_info; - - ash::vk::AccelerationStructureBuildRangeInfoKHR { - primitive_count, - primitive_offset, - first_vertex, - transform_offset, - } - }) - .collect(); - let build_range_info_pointers_vk: SmallVec<[_; 8]> = build_range_info_elements_vk - .iter() - .map(|element| element as *const _) - .collect(); - - let fns = self.device().fns(); - (fns.khr_acceleration_structure - .cmd_build_acceleration_structures_khr)( - self.handle(), - 1, - &info_vk, - build_range_info_pointers_vk.as_ptr(), - ); - - self - } - - pub unsafe fn build_acceleration_structure_indirect( - &mut self, - info: &AccelerationStructureBuildGeometryInfo, - indirect_buffer: &Subbuffer<[u8]>, - stride: u32, - max_primitive_counts: &[u32], - ) -> &mut Self { - let (mut info_vk, geometries_vk) = info.to_vulkan(); - info_vk = ash::vk::AccelerationStructureBuildGeometryInfoKHR { - geometry_count: geometries_vk.len() as u32, - p_geometries: geometries_vk.as_ptr(), - ..info_vk - }; - - let fns = self.device().fns(); - (fns.khr_acceleration_structure - .cmd_build_acceleration_structures_indirect_khr)( - self.handle(), - 1, - &info_vk, - &indirect_buffer.device_address().unwrap().get(), - &stride, - &max_primitive_counts.as_ptr(), - ); - - self - } - - pub unsafe fn copy_acceleration_structure( - &mut self, - info: &CopyAccelerationStructureInfo, - ) -> &mut Self { - let &CopyAccelerationStructureInfo { - ref src, - ref dst, - mode, - _ne: _, - } = info; - - let info_vk = ash::vk::CopyAccelerationStructureInfoKHR { - src: src.handle(), - dst: dst.handle(), - mode: mode.into(), - ..Default::default() - }; - - let fns = self.device().fns(); - (fns.khr_acceleration_structure - .cmd_copy_acceleration_structure_khr)(self.handle(), &info_vk); - - self - } - - pub unsafe fn copy_acceleration_structure_to_memory( - &mut self, - info: &CopyAccelerationStructureToMemoryInfo, - ) -> &mut Self { - let &CopyAccelerationStructureToMemoryInfo { - ref src, - ref dst, - mode, - _ne: _, - } = info; - - let info_vk = ash::vk::CopyAccelerationStructureToMemoryInfoKHR { - src: src.handle(), - dst: ash::vk::DeviceOrHostAddressKHR { - device_address: dst.device_address().unwrap().get(), - }, - mode: mode.into(), - ..Default::default() - }; - - let fns = self.device().fns(); - (fns.khr_acceleration_structure - .cmd_copy_acceleration_structure_to_memory_khr)(self.handle(), &info_vk); - - self - } - - pub unsafe fn copy_memory_to_acceleration_structure( - &mut self, - info: &CopyMemoryToAccelerationStructureInfo, - ) -> &mut Self { - let &CopyMemoryToAccelerationStructureInfo { - ref src, - ref dst, - mode, - _ne: _, - } = info; - - let info_vk = ash::vk::CopyMemoryToAccelerationStructureInfoKHR { - src: ash::vk::DeviceOrHostAddressConstKHR { - device_address: src.device_address().unwrap().get(), - }, - dst: dst.handle(), - mode: mode.into(), - ..Default::default() - }; - - let fns = self.device().fns(); - (fns.khr_acceleration_structure - .cmd_copy_memory_to_acceleration_structure_khr)(self.handle(), &info_vk); - - self - } - - pub unsafe fn write_acceleration_structures_properties( &mut self, acceleration_structures: &[Arc], query_pool: &QueryPool, diff --git a/vulkano/src/command_buffer/commands/bind_push.rs b/vulkano/src/command_buffer/commands/bind_push.rs index 74bd9626..93e9ee60 100644 --- a/vulkano/src/command_buffer/commands/bind_push.rs +++ b/vulkano/src/command_buffer/commands/bind_push.rs @@ -10,9 +10,7 @@ use crate::{ buffer::{BufferContents, BufferUsage, IndexBuffer, Subbuffer}, command_buffer::{ - allocator::CommandBufferAllocator, - auto::{RenderPassStateType, SetOrPush}, - sys::UnsafeCommandBufferBuilder, + allocator::CommandBufferAllocator, auto::SetOrPush, sys::UnsafeCommandBufferBuilder, AutoCommandBufferBuilder, }, descriptor_set::{ @@ -22,23 +20,15 @@ use crate::{ WriteDescriptorSet, }, device::{DeviceOwned, QueueFlags}, - memory::{is_aligned, DeviceAlignment}, + memory::is_aligned, pipeline::{ - graphics::{subpass::PipelineSubpassType, vertex_input::VertexBuffersCollection}, - ComputePipeline, GraphicsPipeline, PipelineBindPoint, PipelineLayout, + graphics::vertex_input::VertexBuffersCollection, ComputePipeline, GraphicsPipeline, + PipelineBindPoint, PipelineLayout, }, - DeviceSize, RequirementNotMet, Requires, RequiresAllOf, RequiresOneOf, ValidationError, - VulkanObject, + DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, VulkanObject, }; use smallvec::SmallVec; -use std::{ - cmp::min, - error, - ffi::c_void, - fmt::{Display, Error as FmtError, Formatter}, - mem::size_of, - sync::Arc, -}; +use std::{cmp::min, ffi::c_void, mem::size_of, sync::Arc}; /// # Commands to bind or push state for pipeline execution commands. /// @@ -48,39 +38,29 @@ where A: CommandBufferAllocator, { /// Binds descriptor sets for future dispatch or draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support `pipeline_bind_point`. - /// - Panics if the highest descriptor set slot being bound is not less than the number of sets - /// in `pipeline_layout`. - /// - Panics if `self` and any element of `descriptor_sets` do not belong to the same device. pub fn bind_descriptor_sets( &mut self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: Arc, first_set: u32, descriptor_sets: impl DescriptorSetsCollection, - ) -> &mut Self { + ) -> Result<&mut Self, Box> { let descriptor_sets = descriptor_sets.into_vec(); self.validate_bind_descriptor_sets( pipeline_bind_point, &pipeline_layout, first_set, &descriptor_sets, - ) - .unwrap(); + )?; unsafe { - self.bind_descriptor_sets_unchecked( + Ok(self.bind_descriptor_sets_unchecked( pipeline_bind_point, pipeline_layout, first_set, descriptor_sets, - ); + )) } - - self } fn validate_bind_descriptor_sets( @@ -89,134 +69,13 @@ where pipeline_layout: &PipelineLayout, first_set: u32, descriptor_sets: &[DescriptorSetWithOffsets], - ) -> Result<(), BindPushError> { - // VUID-vkCmdBindDescriptorSets-pipelineBindPoint-parameter - pipeline_bind_point.validate_device(self.device())?; - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdBindDescriptorSets-commandBuffer-cmdpool - // VUID-vkCmdBindDescriptorSets-pipelineBindPoint-00361 - match pipeline_bind_point { - PipelineBindPoint::Compute => { - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::COMPUTE) - { - return Err(BindPushError::NotSupportedByQueueFamily); - } - } - PipelineBindPoint::Graphics => { - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(BindPushError::NotSupportedByQueueFamily); - } - } - } - - // VUID-vkCmdBindDescriptorSets-firstSet-00360 - if first_set + descriptor_sets.len() as u32 > pipeline_layout.set_layouts().len() as u32 { - return Err(BindPushError::DescriptorSetOutOfRange { - set_num: first_set + descriptor_sets.len() as u32, - pipeline_layout_set_count: pipeline_layout.set_layouts().len() as u32, - }); - } - - let properties = self.device().physical_device().properties(); - let uniform_alignment = properties.min_uniform_buffer_offset_alignment; - let storage_alignment = properties.min_storage_buffer_offset_alignment; - - for (i, set) in descriptor_sets.iter().enumerate() { - let set_num = first_set + i as u32; - let (set, dynamic_offsets) = set.as_ref(); - - // VUID-vkCmdBindDescriptorSets-commonparent - assert_eq!(self.device(), set.device()); - - let set_layout = set.layout(); - let pipeline_set_layout = &pipeline_layout.set_layouts()[set_num as usize]; - - // VUID-vkCmdBindDescriptorSets-pDescriptorSets-00358 - if !pipeline_set_layout.is_compatible_with(set_layout) { - return Err(BindPushError::DescriptorSetNotCompatible { set_num }); - } - - let mut dynamic_offsets_remaining = dynamic_offsets; - let mut required_dynamic_offset_count = 0; - - for (&binding_num, binding) in set_layout.bindings() { - let required_alignment = match binding.descriptor_type { - DescriptorType::UniformBufferDynamic => uniform_alignment, - DescriptorType::StorageBufferDynamic => storage_alignment, - _ => continue, - }; - - let count = if binding - .binding_flags - .intersects(DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT) - { - set.variable_descriptor_count() - } else { - binding.descriptor_count - } as usize; - - required_dynamic_offset_count += count; - - if !dynamic_offsets_remaining.is_empty() { - let split_index = min(count, dynamic_offsets_remaining.len()); - let dynamic_offsets = &dynamic_offsets_remaining[..split_index]; - dynamic_offsets_remaining = &dynamic_offsets_remaining[split_index..]; - - let elements = match set.resources().binding(binding_num) { - Some(DescriptorBindingResources::Buffer(elements)) => elements.as_slice(), - _ => unreachable!(), - }; - - for (index, (&offset, element)) in - dynamic_offsets.iter().zip(elements).enumerate() - { - // VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01971 - // VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01972 - if !is_aligned(offset as DeviceSize, required_alignment) { - return Err(BindPushError::DynamicOffsetNotAligned { - set_num, - binding_num, - index: index as u32, - offset, - required_alignment, - }); - } - - if let Some(buffer_info) = element { - let DescriptorBufferInfo { buffer, range } = buffer_info; - - // VUID-vkCmdBindDescriptorSets-pDescriptorSets-01979 - if offset as DeviceSize + range.end > buffer.size() { - return Err(BindPushError::DynamicOffsetOutOfBufferBounds { - set_num, - binding_num, - index: index as u32, - offset, - range_end: range.end, - buffer_size: buffer.size(), - }); - } - } - } - } - } - - // VUID-vkCmdBindDescriptorSets-dynamicOffsetCount-00359 - if dynamic_offsets.len() != required_dynamic_offset_count { - return Err(BindPushError::DynamicOffsetCountMismatch { - set_num, - provided_count: dynamic_offsets.len(), - required_count: required_dynamic_offset_count, - }); - } - } + ) -> Result<(), Box> { + self.inner.validate_bind_descriptor_sets( + pipeline_bind_point, + pipeline_layout, + first_set, + descriptor_sets, + )?; Ok(()) } @@ -252,7 +111,7 @@ where "bind_descriptor_sets", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.bind_descriptor_sets( + out.bind_descriptor_sets_unchecked( pipeline_bind_point, &pipeline_layout, first_set, @@ -265,67 +124,21 @@ where } /// Binds an index buffer for future indexed draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if `self` and `index_buffer` do not belong to the same device. - /// - Panics if `index_buffer` does not have the [`BufferUsage::INDEX_BUFFER`] usage enabled. - /// - If the index buffer contains `u8` indices, panics if the [`index_type_uint8`] feature is - /// not enabled on the device. - /// - /// [`BufferUsage::INDEX_BUFFER`]: crate::buffer::BufferUsage::INDEX_BUFFER - /// [`index_type_uint8`]: crate::device::Features::index_type_uint8 - pub fn bind_index_buffer(&mut self, index_buffer: impl Into) -> &mut Self { + pub fn bind_index_buffer( + &mut self, + index_buffer: impl Into, + ) -> Result<&mut Self, Box> { let index_buffer = index_buffer.into(); - self.validate_bind_index_buffer(&index_buffer).unwrap(); + self.validate_bind_index_buffer(&index_buffer)?; - unsafe { - self.bind_index_buffer_unchecked(index_buffer); - } - - self + unsafe { Ok(self.bind_index_buffer_unchecked(index_buffer)) } } - fn validate_bind_index_buffer(&self, index_buffer: &IndexBuffer) -> Result<(), BindPushError> { - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdBindIndexBuffer-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(BindPushError::NotSupportedByQueueFamily); - } - - let index_buffer_bytes = index_buffer.as_bytes(); - - // VUID-vkCmdBindIndexBuffer-commonparent - assert_eq!(self.device(), index_buffer_bytes.device()); - - // VUID-vkCmdBindIndexBuffer-buffer-00433 - if !index_buffer_bytes - .buffer() - .usage() - .intersects(BufferUsage::INDEX_BUFFER) - { - return Err(BindPushError::IndexBufferMissingUsage); - } - - // VUID-vkCmdBindIndexBuffer-indexType-02765 - if matches!(index_buffer, IndexBuffer::U8(_)) - && !self.device().enabled_features().index_type_uint8 - { - return Err(BindPushError::RequirementNotMet { - required_for: "`index_buffer` is `IndexBuffer::U8`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "index_type_uint8", - )])]), - }); - } - - // TODO: - // VUID-vkCmdBindIndexBuffer-offset-00432 + fn validate_bind_index_buffer( + &self, + index_buffer: &IndexBuffer, + ) -> Result<(), Box> { + self.inner.validate_bind_index_buffer(index_buffer)?; Ok(()) } @@ -341,7 +154,7 @@ where "bind_index_buffer", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.bind_index_buffer(&index_buffer); + out.bind_index_buffer_unchecked(&index_buffer); }, ); @@ -349,37 +162,20 @@ where } /// Binds a compute pipeline for future dispatch calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support compute operations. - /// - Panics if `self` and `pipeline` do not belong to the same device. - pub fn bind_pipeline_compute(&mut self, pipeline: Arc) -> &mut Self { - self.validate_bind_pipeline_compute(&pipeline).unwrap(); + pub fn bind_pipeline_compute( + &mut self, + pipeline: Arc, + ) -> Result<&mut Self, Box> { + self.validate_bind_pipeline_compute(&pipeline)?; - unsafe { - self.bind_pipeline_compute_unchecked(pipeline); - } - - self + unsafe { Ok(self.bind_pipeline_compute_unchecked(pipeline)) } } fn validate_bind_pipeline_compute( &self, pipeline: &ComputePipeline, - ) -> Result<(), BindPushError> { - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdBindPipeline-pipelineBindPoint-00777 - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::COMPUTE) - { - return Err(BindPushError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdBindPipeline-commonparent - assert_eq!(self.device(), pipeline.device()); + ) -> Result<(), Box> { + self.inner.validate_bind_pipeline_compute(pipeline)?; Ok(()) } @@ -394,7 +190,7 @@ where "bind_pipeline_compute", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.bind_pipeline_compute(&pipeline); + out.bind_pipeline_compute_unchecked(&pipeline); }, ); @@ -402,77 +198,20 @@ where } /// Binds a graphics pipeline for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if `self` and `pipeline` do not belong to the same device. - pub fn bind_pipeline_graphics(&mut self, pipeline: Arc) -> &mut Self { - self.validate_bind_pipeline_graphics(&pipeline).unwrap(); + pub fn bind_pipeline_graphics( + &mut self, + pipeline: Arc, + ) -> Result<&mut Self, Box> { + self.validate_bind_pipeline_graphics(&pipeline)?; - unsafe { - self.bind_pipeline_graphics_unchecked(pipeline); - } - - self + unsafe { Ok(self.bind_pipeline_graphics_unchecked(pipeline)) } } fn validate_bind_pipeline_graphics( &self, pipeline: &GraphicsPipeline, - ) -> Result<(), BindPushError> { - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdBindPipeline-pipelineBindPoint-00778 - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(BindPushError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdBindPipeline-commonparent - assert_eq!(self.device(), pipeline.device()); - - if let Some(last_pipeline) = - self.builder_state - .render_pass - .as_ref() - .and_then(|render_pass_state| match &render_pass_state.render_pass { - RenderPassStateType::BeginRendering(state) if state.pipeline_used => { - self.builder_state.pipeline_graphics.as_ref() - } - _ => None, - }) - { - if let ( - PipelineSubpassType::BeginRendering(pipeline_rendering_info), - PipelineSubpassType::BeginRendering(last_pipeline_rendering_info), - ) = (pipeline.subpass(), last_pipeline.subpass()) - { - // VUID-vkCmdBindPipeline-pipeline-06195 - // VUID-vkCmdBindPipeline-pipeline-06196 - if pipeline_rendering_info.color_attachment_formats - != last_pipeline_rendering_info.color_attachment_formats - { - return Err(BindPushError::PreviousPipelineColorAttachmentFormatMismatch); - } - - // VUID-vkCmdBindPipeline-pipeline-06197 - if pipeline_rendering_info.depth_attachment_format - != last_pipeline_rendering_info.depth_attachment_format - { - return Err(BindPushError::PreviousPipelineDepthAttachmentFormatMismatch); - } - - // VUID-vkCmdBindPipeline-pipeline-06194 - if pipeline_rendering_info.stencil_attachment_format - != last_pipeline_rendering_info.stencil_attachment_format - { - return Err(BindPushError::PreviousPipelineStencilAttachmentFormatMismatch); - } - } - } + ) -> Result<(), Box> { + self.inner.validate_bind_pipeline_graphics(pipeline)?; // VUID-vkCmdBindPipeline-pipeline-00781 // TODO: @@ -498,7 +237,7 @@ where "bind_pipeline_graphics", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.bind_pipeline_graphics(&pipeline); + out.bind_pipeline_graphics_unchecked(&pipeline); }, ); @@ -506,81 +245,24 @@ where } /// Binds vertex buffers for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the highest vertex buffer binding being bound is greater than the - /// [`max_vertex_input_bindings`] device property. - /// - Panics if `self` and any element of `vertex_buffers` do not belong to the same device. - /// - Panics if any element of `vertex_buffers` does not have the - /// [`BufferUsage::VERTEX_BUFFER`] usage enabled. - /// - /// [`max_vertex_input_bindings`]: crate::device::Properties::max_vertex_input_bindings - /// [`BufferUsage::VERTEX_BUFFER`]: crate::buffer::BufferUsage::VERTEX_BUFFER pub fn bind_vertex_buffers( &mut self, first_binding: u32, vertex_buffers: impl VertexBuffersCollection, - ) -> &mut Self { + ) -> Result<&mut Self, Box> { let vertex_buffers = vertex_buffers.into_vec(); - self.validate_bind_vertex_buffers(first_binding, &vertex_buffers) - .unwrap(); + self.validate_bind_vertex_buffers(first_binding, &vertex_buffers)?; - unsafe { - self.bind_vertex_buffers_unchecked(first_binding, vertex_buffers); - } - - self + unsafe { Ok(self.bind_vertex_buffers_unchecked(first_binding, vertex_buffers)) } } fn validate_bind_vertex_buffers( &self, first_binding: u32, vertex_buffers: &[Subbuffer<[u8]>], - ) -> Result<(), BindPushError> { - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdBindVertexBuffers-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(BindPushError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdBindVertexBuffers-firstBinding-00624 - // VUID-vkCmdBindVertexBuffers-firstBinding-00625 - if first_binding + vertex_buffers.len() as u32 - > self - .device() - .physical_device() - .properties() - .max_vertex_input_bindings - { - return Err(BindPushError::MaxVertexInputBindingsExceeded { - _binding_count: first_binding + vertex_buffers.len() as u32, - _max: self - .device() - .physical_device() - .properties() - .max_vertex_input_bindings, - }); - } - - for buffer in vertex_buffers { - // VUID-vkCmdBindVertexBuffers-commonparent - assert_eq!(self.device(), buffer.device()); - - // VUID-vkCmdBindVertexBuffers-pBuffers-00627 - if !buffer - .buffer() - .usage() - .intersects(BufferUsage::VERTEX_BUFFER) - { - return Err(BindPushError::VertexBufferMissingUsage); - } - } + ) -> Result<(), Box> { + self.inner + .validate_bind_vertex_buffers(first_binding, vertex_buffers)?; Ok(()) } @@ -603,7 +285,7 @@ where "bind_vertex_buffers", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.bind_vertex_buffers(first_binding, &vertex_buffers); + out.bind_vertex_buffers_unchecked(first_binding, &vertex_buffers); }, ); @@ -611,83 +293,34 @@ where } /// Sets push constants for future dispatch or draw calls. - /// - /// # Panics - /// - /// - Panics if `offset` is not a multiple of 4. - /// - Panics if the size of `push_constants` is not a multiple of 4. - /// - Panics if any of the bytes in `push_constants` do not fall within any of the pipeline - /// layout's push constant ranges. pub fn push_constants( &mut self, pipeline_layout: Arc, offset: u32, push_constants: Pc, - ) -> &mut Self + ) -> Result<&mut Self, Box> where Pc: BufferContents, { let size = size_of::() as u32; if size == 0 { - return self; + return Ok(self); } - self.validate_push_constants(&pipeline_layout, offset, &push_constants) - .unwrap(); + self.validate_push_constants(&pipeline_layout, offset, &push_constants)?; - unsafe { - self.push_constants_unchecked(pipeline_layout, offset, push_constants); - } - - self + unsafe { Ok(self.push_constants_unchecked(pipeline_layout, offset, push_constants)) } } fn validate_push_constants( &self, pipeline_layout: &PipelineLayout, offset: u32, - _push_constants: &Pc, - ) -> Result<(), BindPushError> { - let mut remaining_size = size_of::(); - - if offset % 4 != 0 { - return Err(BindPushError::PushConstantsOffsetNotAligned); - } - - if remaining_size % 4 != 0 { - return Err(BindPushError::PushConstantsSizeNotAligned); - } - - let mut current_offset = offset as usize; - - for range in pipeline_layout - .push_constant_ranges_disjoint() - .iter() - .skip_while(|range| range.offset + range.size <= offset) - { - // there is a gap between ranges, but the passed push_constants contains - // some bytes in this gap, exit the loop and report error - if range.offset as usize > current_offset { - break; - } - - // push the minimum of the whole remaining data, and the part until the end of this range - let push_size = - remaining_size.min(range.offset as usize + range.size as usize - current_offset); - current_offset += push_size; - remaining_size -= push_size; - - if remaining_size == 0 { - break; - } - } - - if remaining_size != 0 { - return Err(BindPushError::PushConstantsDataOutOfRange { - offset: current_offset, - }); - } + push_constants: &Pc, + ) -> Result<(), Box> { + self.inner + .validate_push_constants(pipeline_layout, offset, push_constants)?; Ok(()) } @@ -716,7 +349,7 @@ where "push_constants", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.push_constants(&pipeline_layout, offset, &push_constants); + out.push_constants_unchecked(&pipeline_layout, offset, &push_constants); }, ); @@ -724,41 +357,815 @@ where } /// Pushes descriptor data directly into the command buffer for future dispatch or draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support `pipeline_bind_point`. - /// - Panics if the - /// [`khr_push_descriptor`](crate::device::DeviceExtensions::khr_push_descriptor) - /// extension is not enabled on the device. - /// - Panics if `set_num` is not less than the number of sets in `pipeline_layout`. - /// - Panics if an element of `descriptor_writes` is not compatible with `pipeline_layout`. pub fn push_descriptor_set( &mut self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: Arc, set_num: u32, descriptor_writes: SmallVec<[WriteDescriptorSet; 8]>, - ) -> &mut Self { + ) -> Result<&mut Self, Box> { self.validate_push_descriptor_set( pipeline_bind_point, &pipeline_layout, set_num, &descriptor_writes, - ) - .unwrap(); + )?; unsafe { - self.push_descriptor_set_unchecked( + Ok(self.push_descriptor_set_unchecked( pipeline_bind_point, pipeline_layout, set_num, descriptor_writes, - ); + )) + } + } + + fn validate_push_descriptor_set( + &self, + pipeline_bind_point: PipelineBindPoint, + pipeline_layout: &PipelineLayout, + set_num: u32, + descriptor_writes: &[WriteDescriptorSet], + ) -> Result<(), Box> { + self.inner.validate_push_descriptor_set( + pipeline_bind_point, + pipeline_layout, + set_num, + descriptor_writes, + )?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn push_descriptor_set_unchecked( + &mut self, + pipeline_bind_point: PipelineBindPoint, + pipeline_layout: Arc, + set_num: u32, + descriptor_writes: SmallVec<[WriteDescriptorSet; 8]>, + ) -> &mut Self { + let state = self.builder_state.invalidate_descriptor_sets( + pipeline_bind_point, + pipeline_layout.clone(), + set_num, + 1, + ); + let layout = state.pipeline_layout.set_layouts()[set_num as usize].as_ref(); + debug_assert!(layout + .flags() + .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR)); + + let set_resources = match state + .descriptor_sets + .entry(set_num) + .or_insert_with(|| SetOrPush::Push(DescriptorSetResources::new(layout, 0))) + { + SetOrPush::Push(set_resources) => set_resources, + _ => unreachable!(), + }; + + for write in &descriptor_writes { + set_resources.write(write, layout); } + self.add_command( + "push_descriptor_set", + Default::default(), + move |out: &mut UnsafeCommandBufferBuilder| { + out.push_descriptor_set_unchecked( + pipeline_bind_point, + &pipeline_layout, + set_num, + &descriptor_writes, + ); + }, + ); + self } +} + +impl UnsafeCommandBufferBuilder +where + A: CommandBufferAllocator, +{ + pub unsafe fn bind_descriptor_sets( + &mut self, + pipeline_bind_point: PipelineBindPoint, + pipeline_layout: &PipelineLayout, + first_set: u32, + descriptor_sets: &[DescriptorSetWithOffsets], + ) -> Result<&mut Self, Box> { + self.validate_bind_descriptor_sets( + pipeline_bind_point, + pipeline_layout, + first_set, + descriptor_sets, + )?; + + Ok(self.bind_descriptor_sets_unchecked( + pipeline_bind_point, + pipeline_layout, + first_set, + descriptor_sets, + )) + } + + fn validate_bind_descriptor_sets( + &self, + pipeline_bind_point: PipelineBindPoint, + pipeline_layout: &PipelineLayout, + first_set: u32, + descriptor_sets: &[DescriptorSetWithOffsets], + ) -> Result<(), Box> { + pipeline_bind_point + .validate_device(self.device()) + .map_err(|err| ValidationError { + context: "pipeline_bind_point".into(), + vuids: &["VUID-vkCmdBindDescriptorSets-pipelineBindPoint-parameter"], + ..ValidationError::from_requirement(err) + })?; + + let queue_family_properties = self.queue_family_properties(); + + match pipeline_bind_point { + PipelineBindPoint::Compute => { + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + context: "pipeline_bind_point".into(), + problem: "is `PipelineBindPoint::Compute`, but \ + the queue family of the command buffer does not support \ + compute operations" + .into(), + vuids: &[ + "VUID-vkCmdBindDescriptorSets-pipelineBindPoint-00361", + "VUID-vkCmdBindDescriptorSets-commandBuffer-cmdpool", + ], + ..Default::default() + })); + } + } + PipelineBindPoint::Graphics => { + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + context: "pipeline_bind_point".into(), + problem: "is `PipelineBindPoint::Graphics`, but \ + the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &[ + "VUID-vkCmdBindDescriptorSets-pipelineBindPoint-00361", + "VUID-vkCmdBindDescriptorSets-commandBuffer-cmdpool", + ], + ..Default::default() + })); + } + } + } + + if first_set + descriptor_sets.len() as u32 > pipeline_layout.set_layouts().len() as u32 { + return Err(Box::new(ValidationError { + problem: "`first_set + descriptor_sets.len()` is greater than \ + `pipeline_layout.set_layouts().len()`" + .into(), + vuids: &["VUID-vkCmdBindDescriptorSets-firstSet-00360"], + ..Default::default() + })); + } + + let properties = self.device().physical_device().properties(); + + for (descriptor_sets_index, set) in descriptor_sets.iter().enumerate() { + let set_num = first_set + descriptor_sets_index as u32; + let (set, dynamic_offsets) = set.as_ref(); + + // VUID-vkCmdBindDescriptorSets-commonparent + assert_eq!(self.device(), set.device()); + + let set_layout = set.layout(); + let pipeline_set_layout = &pipeline_layout.set_layouts()[set_num as usize]; + + if !pipeline_set_layout.is_compatible_with(set_layout) { + return Err(Box::new(ValidationError { + problem: format!( + "`descriptor_sets[{0}]` (for set number {1}) is not compatible with \ + `pipeline_layout.set_layouts()[{1}]`", + descriptor_sets_index, set_num + ) + .into(), + vuids: &["VUID-vkCmdBindDescriptorSets-pDescriptorSets-00358"], + ..Default::default() + })); + } + + let mut dynamic_offsets_remaining = dynamic_offsets; + let mut required_dynamic_offset_count = 0; + + for (&binding_num, binding) in set_layout.bindings() { + let required_alignment = match binding.descriptor_type { + DescriptorType::UniformBufferDynamic => { + properties.min_uniform_buffer_offset_alignment + } + DescriptorType::StorageBufferDynamic => { + properties.min_storage_buffer_offset_alignment + } + _ => continue, + }; + + let count = if binding + .binding_flags + .intersects(DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT) + { + set.variable_descriptor_count() + } else { + binding.descriptor_count + } as usize; + + required_dynamic_offset_count += count; + + if !dynamic_offsets_remaining.is_empty() { + let split_index = min(count, dynamic_offsets_remaining.len()); + let dynamic_offsets = &dynamic_offsets_remaining[..split_index]; + dynamic_offsets_remaining = &dynamic_offsets_remaining[split_index..]; + + let elements = match set.resources().binding(binding_num) { + Some(DescriptorBindingResources::Buffer(elements)) => elements.as_slice(), + _ => unreachable!(), + }; + + for (index, (&offset, element)) in + dynamic_offsets.iter().zip(elements).enumerate() + { + if !is_aligned(offset as DeviceSize, required_alignment) { + match binding.descriptor_type { + DescriptorType::UniformBufferDynamic => { + return Err(Box::new(ValidationError { + problem: format!( + "the descriptor type of `descriptor_sets[{}]` \ + (for set number {}) is \ + `DescriptorType::UniformBufferDynamic`, but the \ + dynamic offset provided for binding {} index {} is \ + not aligned to the \ + `min_uniform_buffer_offset_alignment` device property", + descriptor_sets_index, set_num, binding_num, index, + ) + .into(), + vuids: &[ + "VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01971", + ], + ..Default::default() + })); + } + DescriptorType::StorageBufferDynamic => { + return Err(Box::new(ValidationError { + problem: format!( + "the descriptor type of `descriptor_sets[{}]` \ + (for set number {}) is \ + `DescriptorType::StorageBufferDynamic`, but the \ + dynamic offset provided for binding {} index {} is \ + not aligned to the \ + `min_storage_buffer_offset_alignment` device property", + descriptor_sets_index, set_num, binding_num, index, + ) + .into(), + vuids: &[ + "VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01972", + ], + ..Default::default() + })); + } + _ => unreachable!(), + } + } + + if let Some(buffer_info) = element { + let DescriptorBufferInfo { buffer, range } = buffer_info; + + if offset as DeviceSize + range.end > buffer.size() { + return Err(Box::new(ValidationError { + problem: format!( + "the dynamic offset of `descriptor_sets[{}]` \ + (for set number {}) for binding {} index {}, when \ + added to `range.end` of the descriptor write, is \ + greater than the size of the bound buffer", + descriptor_sets_index, set_num, binding_num, index, + ) + .into(), + vuids: &["VUID-vkCmdBindDescriptorSets-pDescriptorSets-01979"], + ..Default::default() + })); + } + } + } + } + } + + if dynamic_offsets.len() != required_dynamic_offset_count { + return Err(Box::new(ValidationError { + problem: format!( + "the number of dynamic offsets provided for `descriptor_sets[{}]` \ + (for set number {}) does not equal the number required ({})", + descriptor_sets_index, set_num, required_dynamic_offset_count, + ) + .into(), + vuids: &["VUID-vkCmdBindDescriptorSets-dynamicOffsetCount-00359"], + ..Default::default() + })); + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn bind_descriptor_sets_unchecked( + &mut self, + pipeline_bind_point: PipelineBindPoint, + pipeline_layout: &PipelineLayout, + first_set: u32, + descriptor_sets: &[DescriptorSetWithOffsets], + ) -> &mut Self { + if descriptor_sets.is_empty() { + return self; + } + + let descriptor_sets_vk: SmallVec<[_; 12]> = descriptor_sets + .iter() + .map(|x| x.as_ref().0.inner().handle()) + .collect(); + let dynamic_offsets_vk: SmallVec<[_; 32]> = descriptor_sets + .iter() + .flat_map(|x| x.as_ref().1.iter().copied()) + .collect(); + + let fns = self.device().fns(); + (fns.v1_0.cmd_bind_descriptor_sets)( + self.handle(), + pipeline_bind_point.into(), + pipeline_layout.handle(), + first_set, + descriptor_sets_vk.len() as u32, + descriptor_sets_vk.as_ptr(), + dynamic_offsets_vk.len() as u32, + dynamic_offsets_vk.as_ptr(), + ); + + self + } + + pub unsafe fn bind_index_buffer( + &mut self, + index_buffer: &IndexBuffer, + ) -> Result<&mut Self, Box> { + self.validate_bind_index_buffer(index_buffer)?; + + Ok(self.bind_index_buffer_unchecked(index_buffer)) + } + + fn validate_bind_index_buffer( + &self, + index_buffer: &IndexBuffer, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdBindIndexBuffer-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + let index_buffer_bytes = index_buffer.as_bytes(); + + // VUID-vkCmdBindIndexBuffer-commonparent + assert_eq!(self.device(), index_buffer_bytes.device()); + + if !index_buffer_bytes + .buffer() + .usage() + .intersects(BufferUsage::INDEX_BUFFER) + { + return Err(Box::new(ValidationError { + context: "index_buffer.usage()".into(), + problem: "does not contain `BufferUsage::INDEX_BUFFER`".into(), + vuids: &["VUID-vkCmdBindIndexBuffer-buffer-00433"], + ..Default::default() + })); + } + + if matches!(index_buffer, IndexBuffer::U8(_)) + && !self.device().enabled_features().index_type_uint8 + { + return Err(Box::new(ValidationError { + context: "index_buffer".into(), + problem: "is `IndexBuffer::U8`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "index_type_uint8", + )])]), + vuids: &["VUID-vkCmdBindIndexBuffer-indexType-02765"], + })); + } + + // TODO: + // VUID-vkCmdBindIndexBuffer-offset-00432 + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn bind_index_buffer_unchecked(&mut self, index_buffer: &IndexBuffer) -> &mut Self { + let index_buffer_bytes = index_buffer.as_bytes(); + + let fns = self.device().fns(); + (fns.v1_0.cmd_bind_index_buffer)( + self.handle(), + index_buffer_bytes.buffer().handle(), + index_buffer_bytes.offset(), + index_buffer.index_type().into(), + ); + + self + } + + pub unsafe fn bind_pipeline_compute( + &mut self, + pipeline: &ComputePipeline, + ) -> Result<&mut Self, Box> { + self.validate_bind_pipeline_compute(pipeline)?; + + Ok(self.bind_pipeline_compute_unchecked(pipeline)) + } + + fn validate_bind_pipeline_compute( + &self, + pipeline: &ComputePipeline, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + compute operations" + .into(), + vuids: &["VUID-vkCmdBindPipeline-pipelineBindPoint-00777"], + ..Default::default() + })); + } + + // VUID-vkCmdBindPipeline-commonparent + assert_eq!(self.device(), pipeline.device()); + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn bind_pipeline_compute_unchecked( + &mut self, + pipeline: &ComputePipeline, + ) -> &mut Self { + let fns = self.device().fns(); + (fns.v1_0.cmd_bind_pipeline)( + self.handle(), + ash::vk::PipelineBindPoint::COMPUTE, + pipeline.handle(), + ); + + self + } + + pub unsafe fn bind_pipeline_graphics( + &mut self, + pipeline: &GraphicsPipeline, + ) -> Result<&mut Self, Box> { + self.validate_bind_pipeline_graphics(pipeline)?; + + Ok(self.bind_pipeline_graphics_unchecked(pipeline)) + } + + fn validate_bind_pipeline_graphics( + &self, + pipeline: &GraphicsPipeline, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdBindPipeline-pipelineBindPoint-00778"], + ..Default::default() + })); + } + + // VUID-vkCmdBindPipeline-commonparent + assert_eq!(self.device(), pipeline.device()); + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn bind_pipeline_graphics_unchecked( + &mut self, + pipeline: &GraphicsPipeline, + ) -> &mut Self { + let fns = self.device().fns(); + (fns.v1_0.cmd_bind_pipeline)( + self.handle(), + ash::vk::PipelineBindPoint::GRAPHICS, + pipeline.handle(), + ); + + self + } + + pub unsafe fn bind_vertex_buffers( + &mut self, + first_binding: u32, + vertex_buffers: &[Subbuffer<[u8]>], + ) -> Result<&mut Self, Box> { + self.validate_bind_vertex_buffers(first_binding, vertex_buffers)?; + + Ok(self.bind_vertex_buffers_unchecked(first_binding, vertex_buffers)) + } + + fn validate_bind_vertex_buffers( + &self, + first_binding: u32, + vertex_buffers: &[Subbuffer<[u8]>], + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdBindVertexBuffers-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + let properties = self.device().physical_device().properties(); + + if first_binding + vertex_buffers.len() as u32 > properties.max_vertex_input_bindings { + return Err(Box::new(ValidationError { + problem: "`first_binding + vertex_buffers.len()` is greater than the \ + `max_vertex_input_bindings` limit" + .into(), + vuids: &[ + "VUID-vkCmdBindVertexBuffers-firstBinding-00624", + "VUID-vkCmdBindVertexBuffers-firstBinding-00625", + ], + ..Default::default() + })); + } + + for (vertex_buffers_index, buffer) in vertex_buffers.iter().enumerate() { + // VUID-vkCmdBindVertexBuffers-commonparent + assert_eq!(self.device(), buffer.device()); + + if !buffer + .buffer() + .usage() + .intersects(BufferUsage::VERTEX_BUFFER) + { + return Err(Box::new(ValidationError { + context: format!("vertex_buffers[{}].usage()", vertex_buffers_index).into(), + problem: "does not contain `BufferUsage::VERTEX_BUFFER`".into(), + vuids: &["VUID-vkCmdBindVertexBuffers-pBuffers-00627"], + ..Default::default() + })); + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn bind_vertex_buffers_unchecked( + &mut self, + first_binding: u32, + vertex_buffers: &[Subbuffer<[u8]>], + ) -> &mut Self { + if vertex_buffers.is_empty() { + return self; + } + + let (buffers_vk, offsets_vk): (SmallVec<[_; 2]>, SmallVec<[_; 2]>) = vertex_buffers + .iter() + .map(|buffer| (buffer.buffer().handle(), buffer.offset())) + .unzip(); + + let fns = self.device().fns(); + (fns.v1_0.cmd_bind_vertex_buffers)( + self.handle(), + first_binding, + buffers_vk.len() as u32, + buffers_vk.as_ptr(), + offsets_vk.as_ptr(), + ); + + self + } + + pub unsafe fn push_constants( + &mut self, + pipeline_layout: &PipelineLayout, + offset: u32, + push_constants: &Pc, + ) -> Result<&mut Self, Box> + where + Pc: BufferContents, + { + self.validate_push_constants(pipeline_layout, offset, push_constants)?; + + Ok(self.push_constants_unchecked(pipeline_layout, offset, push_constants)) + } + + fn validate_push_constants( + &self, + pipeline_layout: &PipelineLayout, + offset: u32, + _push_constants: &Pc, + ) -> Result<(), Box> { + let mut remaining_size = size_of::(); + + if offset % 4 != 0 { + return Err(Box::new(ValidationError { + context: "offset".into(), + problem: "is not a multiple of 4".into(), + vuids: &["VUID-vkCmdPushConstants-offset-00368"], + ..Default::default() + })); + } + + if remaining_size % 4 != 0 { + return Err(Box::new(ValidationError { + context: "push_constants".into(), + problem: "the size is not a multiple of 4".into(), + vuids: &["VUID-vkCmdPushConstants-size-00369"], + ..Default::default() + })); + } + + let properties = self.device().physical_device().properties(); + + if offset >= properties.max_push_constants_size { + return Err(Box::new(ValidationError { + context: "offset".into(), + problem: "is not less than the `max_push_constants_size` limit".into(), + vuids: &["VUID-vkCmdPushConstants-offset-00370"], + ..Default::default() + })); + } + + if offset as usize + remaining_size >= properties.max_push_constants_size as usize { + return Err(Box::new(ValidationError { + problem: "`offset` + the size of `push_constants` is not less than the \ + `max_push_constants_size` limit" + .into(), + vuids: &["VUID-vkCmdPushConstants-size-00371"], + ..Default::default() + })); + } + + let mut current_offset = offset as usize; + + for range in pipeline_layout + .push_constant_ranges_disjoint() + .iter() + .skip_while(|range| range.offset + range.size <= offset) + { + // there is a gap between ranges, but the passed push_constants contains + // some bytes in this gap, exit the loop and report error + if range.offset as usize > current_offset { + break; + } + + // push the minimum of the whole remaining data, and the part until the end of this range + let push_size = + remaining_size.min(range.offset as usize + range.size as usize - current_offset); + current_offset += push_size; + remaining_size -= push_size; + + if remaining_size == 0 { + break; + } + } + + if remaining_size != 0 { + return Err(Box::new(ValidationError { + problem: "one or more bytes of `push_constants` are not within any push constant \ + range of `pipeline_layout`" + .into(), + vuids: &["VUID-vkCmdPushConstants-offset-01795"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn push_constants_unchecked( + &mut self, + pipeline_layout: &PipelineLayout, + offset: u32, + push_constants: &Pc, + ) -> &mut Self + where + Pc: BufferContents, + { + let size = u32::try_from(size_of::()).unwrap(); + + if size == 0 { + return self; + } + + let fns = self.device().fns(); + let mut current_offset = offset; + let mut remaining_size = size; + + for range in pipeline_layout + .push_constant_ranges_disjoint() + .iter() + .skip_while(|range| range.offset + range.size <= offset) + { + // there is a gap between ranges, but the passed push_constants contains + // some bytes in this gap, exit the loop and report error + if range.offset > current_offset { + break; + } + + // push the minimum of the whole remaining data, and the part until the end of this range + let push_size = remaining_size.min(range.offset + range.size - current_offset); + let data_offset = (current_offset - offset) as usize; + debug_assert!(data_offset < size as usize); + let data = (push_constants as *const Pc as *const c_void).add(data_offset); + + (fns.v1_0.cmd_push_constants)( + self.handle(), + pipeline_layout.handle(), + range.stages.into(), + current_offset, + push_size, + data, + ); + + current_offset += push_size; + remaining_size -= push_size; + + if remaining_size == 0 { + break; + } + } + + debug_assert!(remaining_size == 0); + + self + } + + pub unsafe fn push_descriptor_set( + &mut self, + pipeline_bind_point: PipelineBindPoint, + pipeline_layout: &PipelineLayout, + set_num: u32, + descriptor_writes: &[WriteDescriptorSet], + ) -> Result<&mut Self, Box> { + self.validate_push_descriptor_set( + pipeline_bind_point, + pipeline_layout, + set_num, + descriptor_writes, + )?; + + Ok(self.push_descriptor_set_unchecked( + pipeline_bind_point, + pipeline_layout, + set_num, + descriptor_writes, + )) + } fn validate_push_descriptor_set( &self, @@ -865,234 +1272,6 @@ where #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn push_descriptor_set_unchecked( - &mut self, - pipeline_bind_point: PipelineBindPoint, - pipeline_layout: Arc, - set_num: u32, - descriptor_writes: SmallVec<[WriteDescriptorSet; 8]>, - ) -> &mut Self { - let state = self.builder_state.invalidate_descriptor_sets( - pipeline_bind_point, - pipeline_layout.clone(), - set_num, - 1, - ); - let layout = state.pipeline_layout.set_layouts()[set_num as usize].as_ref(); - debug_assert!(layout - .flags() - .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR)); - - let set_resources = match state - .descriptor_sets - .entry(set_num) - .or_insert_with(|| SetOrPush::Push(DescriptorSetResources::new(layout, 0))) - { - SetOrPush::Push(set_resources) => set_resources, - _ => unreachable!(), - }; - - for write in &descriptor_writes { - set_resources.write(write, layout); - } - - self.add_command( - "push_descriptor_set", - Default::default(), - move |out: &mut UnsafeCommandBufferBuilder| { - out.push_descriptor_set( - pipeline_bind_point, - &pipeline_layout, - set_num, - &descriptor_writes, - ); - }, - ); - - self - } -} - -impl UnsafeCommandBufferBuilder -where - A: CommandBufferAllocator, -{ - /// Calls `vkCmdBindDescriptorSets` on the builder. - /// - /// Does nothing if the list of descriptor sets is empty, as it would be a no-op and isn't a - /// valid usage of the command anyway. - #[inline] - pub unsafe fn bind_descriptor_sets( - &mut self, - pipeline_bind_point: PipelineBindPoint, - pipeline_layout: &PipelineLayout, - first_set: u32, - descriptor_sets: &[DescriptorSetWithOffsets], - ) -> &mut Self { - if descriptor_sets.is_empty() { - return self; - } - - let descriptor_sets_vk: SmallVec<[_; 12]> = descriptor_sets - .iter() - .map(|x| x.as_ref().0.inner().handle()) - .collect(); - let dynamic_offsets_vk: SmallVec<[_; 32]> = descriptor_sets - .iter() - .flat_map(|x| x.as_ref().1.iter().copied()) - .collect(); - - let fns = self.device().fns(); - (fns.v1_0.cmd_bind_descriptor_sets)( - self.handle(), - pipeline_bind_point.into(), - pipeline_layout.handle(), - first_set, - descriptor_sets_vk.len() as u32, - descriptor_sets_vk.as_ptr(), - dynamic_offsets_vk.len() as u32, - dynamic_offsets_vk.as_ptr(), - ); - - self - } - - /// Calls `vkCmdBindIndexBuffer` on the builder. - #[inline] - pub unsafe fn bind_index_buffer(&mut self, index_buffer: &IndexBuffer) -> &mut Self { - let index_buffer_bytes = index_buffer.as_bytes(); - - let fns = self.device().fns(); - (fns.v1_0.cmd_bind_index_buffer)( - self.handle(), - index_buffer_bytes.buffer().handle(), - index_buffer_bytes.offset(), - index_buffer.index_type().into(), - ); - - self - } - - /// Calls `vkCmdBindPipeline` on the builder with a compute pipeline. - #[inline] - pub unsafe fn bind_pipeline_compute(&mut self, pipeline: &ComputePipeline) -> &mut Self { - let fns = self.device().fns(); - (fns.v1_0.cmd_bind_pipeline)( - self.handle(), - ash::vk::PipelineBindPoint::COMPUTE, - pipeline.handle(), - ); - - self - } - - /// Calls `vkCmdBindPipeline` on the builder with a graphics pipeline. - #[inline] - pub unsafe fn bind_pipeline_graphics(&mut self, pipeline: &GraphicsPipeline) -> &mut Self { - let fns = self.device().fns(); - (fns.v1_0.cmd_bind_pipeline)( - self.handle(), - ash::vk::PipelineBindPoint::GRAPHICS, - pipeline.handle(), - ); - - self - } - - /// Calls `vkCmdBindVertexBuffers` on the builder. - /// - /// Does nothing if the list of buffers is empty, as it would be a no-op and isn't a valid - /// usage of the command anyway. - // TODO: vkCmdBindVertexBuffers2EXT - #[inline] - pub unsafe fn bind_vertex_buffers( - &mut self, - first_binding: u32, - vertex_buffers: &[Subbuffer<[u8]>], - ) -> &mut Self { - if vertex_buffers.is_empty() { - return self; - } - - let (buffers_vk, offsets_vk): (SmallVec<[_; 2]>, SmallVec<[_; 2]>) = vertex_buffers - .iter() - .map(|buffer| (buffer.buffer().handle(), buffer.offset())) - .unzip(); - - let fns = self.device().fns(); - (fns.v1_0.cmd_bind_vertex_buffers)( - self.handle(), - first_binding, - buffers_vk.len() as u32, - buffers_vk.as_ptr(), - offsets_vk.as_ptr(), - ); - - self - } - - /// Calls `vkCmdPushConstants` on the builder. - pub unsafe fn push_constants( - &mut self, - pipeline_layout: &PipelineLayout, - offset: u32, - push_constants: &Pc, - ) -> &mut Self - where - Pc: BufferContents, - { - let size = u32::try_from(size_of::()).unwrap(); - - if size == 0 { - return self; - } - - let fns = self.device().fns(); - let mut current_offset = offset; - let mut remaining_size = size; - - for range in pipeline_layout - .push_constant_ranges_disjoint() - .iter() - .skip_while(|range| range.offset + range.size <= offset) - { - // there is a gap between ranges, but the passed push_constants contains - // some bytes in this gap, exit the loop and report error - if range.offset > current_offset { - break; - } - - // push the minimum of the whole remaining data, and the part until the end of this range - let push_size = remaining_size.min(range.offset + range.size - current_offset); - let data_offset = (current_offset - offset) as usize; - debug_assert!(data_offset < size as usize); - let data = (push_constants as *const Pc as *const c_void).add(data_offset); - - (fns.v1_0.cmd_push_constants)( - self.handle(), - pipeline_layout.handle(), - range.stages.into(), - current_offset, - push_size, - data, - ); - - current_offset += push_size; - remaining_size -= push_size; - - if remaining_size == 0 { - break; - } - } - - debug_assert!(remaining_size == 0); - - self - } - - /// Calls `vkCmdPushDescriptorSetKHR` on the builder. - /// - /// If the list is empty then the command is automatically ignored. - pub unsafe fn push_descriptor_set( &mut self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: &PipelineLayout, @@ -1176,208 +1355,3 @@ where self } } - -#[derive(Clone, Debug)] -pub(in super::super) enum BindPushError { - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, - - /// The element of `descriptor_sets` being bound to a slot is not compatible with the - /// corresponding slot in `pipeline_layout`. - DescriptorSetNotCompatible { set_num: u32 }, - - /// The highest descriptor set slot being bound is greater than the number of sets in - /// `pipeline_layout`. - DescriptorSetOutOfRange { - set_num: u32, - pipeline_layout_set_count: u32, - }, - - /// In an element of `descriptor_sets`, the number of provided dynamic offsets does not match - /// the number required by the descriptor set. - DynamicOffsetCountMismatch { - set_num: u32, - provided_count: usize, - required_count: usize, - }, - - /// In an element of `descriptor_sets`, a provided dynamic offset - /// is not a multiple of the value of the [`min_uniform_buffer_offset_alignment`] or - /// [`min_storage_buffer_offset_alignment`] property. - /// - /// min_uniform_buffer_offset_alignment: crate::device::Properties::min_uniform_buffer_offset_alignment - /// min_storage_buffer_offset_alignment: crate::device::Properties::min_storage_buffer_offset_alignment - DynamicOffsetNotAligned { - set_num: u32, - binding_num: u32, - index: u32, - offset: u32, - required_alignment: DeviceAlignment, - }, - - /// In an element of `descriptor_sets`, a provided dynamic offset, when added to the end of the - /// buffer range bound to the descriptor set, is greater than the size of the buffer. - DynamicOffsetOutOfBufferBounds { - set_num: u32, - binding_num: u32, - index: u32, - offset: u32, - range_end: DeviceSize, - buffer_size: DeviceSize, - }, - - /// An index buffer is missing the `index_buffer` usage. - IndexBufferMissingUsage, - - /// The `max_vertex_input_bindings` limit has been exceeded. - MaxVertexInputBindingsExceeded { _binding_count: u32, _max: u32 }, - - /// The queue family doesn't allow this operation. - NotSupportedByQueueFamily, - - /// The newly set pipeline has color attachment formats that do not match the - /// previously used pipeline. - PreviousPipelineColorAttachmentFormatMismatch, - - /// The newly set pipeline has a depth attachment format that does not match the - /// previously used pipeline. - PreviousPipelineDepthAttachmentFormatMismatch, - - /// The newly set pipeline has a stencil attachment format that does not match the - /// previously used pipeline. - PreviousPipelineStencilAttachmentFormatMismatch, - - /// The push constants data to be written at an offset is not included in any push constant - /// range of the pipeline layout. - PushConstantsDataOutOfRange { offset: usize }, - - /// The push constants offset is not a multiple of 4. - PushConstantsOffsetNotAligned, - - /// The push constants size is not a multiple of 4. - PushConstantsSizeNotAligned, - - /// A vertex buffer is missing the `vertex_buffer` usage. - VertexBufferMissingUsage, -} - -impl error::Error for BindPushError {} - -impl Display for BindPushError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::DescriptorSetNotCompatible { set_num } => write!( - f, - "the element of `descriptor_sets` being bound to slot {} is not compatible with \ - the corresponding slot in `pipeline_layout`", - set_num, - ), - Self::DescriptorSetOutOfRange { - set_num, - pipeline_layout_set_count, - } => write!( - f, - "the highest descriptor set slot being bound ({}) is greater than the number of \ - sets in `pipeline_layout` ({})", - set_num, pipeline_layout_set_count, - ), - Self::DynamicOffsetCountMismatch { - set_num, - provided_count, - required_count, - } => write!( - f, - "in the element of `descriptor_sets` being bound to slot {}, the number of \ - provided dynamic offsets ({}) does not match the number required by the \ - descriptor set ({})", - set_num, provided_count, required_count, - ), - Self::DynamicOffsetNotAligned { - set_num, - binding_num, - index, - offset, - required_alignment, - } => write!( - f, - "in the element of `descriptor_sets` being bound to slot {}, the dynamic offset \ - provided for binding {} index {} ({}) is not a multiple of the value of the \ - `min_uniform_buffer_offset_alignment` or `min_storage_buffer_offset_alignment` \ - property ({:?})", - set_num, binding_num, index, offset, required_alignment, - ), - Self::DynamicOffsetOutOfBufferBounds { - set_num, - binding_num, - index, - offset, - range_end, - buffer_size, - } => write!( - f, - "in the element of `descriptor_sets` being bound to slot {}, the dynamic offset \ - provided for binding {} index {} ({}), when added to the end of the buffer range \ - bound to the descriptor set ({}), is greater than the size of the buffer ({})", - set_num, binding_num, index, offset, range_end, buffer_size, - ), - Self::IndexBufferMissingUsage => { - write!(f, "an index buffer is missing the `index_buffer` usage") - } - Self::MaxVertexInputBindingsExceeded { .. } => { - write!(f, "the `max_vertex_input_bindings` limit has been exceeded") - } - Self::NotSupportedByQueueFamily => { - write!(f, "the queue family doesn't allow this operation") - } - Self::PreviousPipelineColorAttachmentFormatMismatch => write!( - f, - "the newly set pipeline has color attachment formats that do not match the \ - previously used pipeline", - ), - Self::PreviousPipelineDepthAttachmentFormatMismatch => write!( - f, - "the newly set pipeline has a depth attachment format that does not match the \ - previously used pipeline", - ), - Self::PreviousPipelineStencilAttachmentFormatMismatch => write!( - f, - "the newly set pipeline has a stencil attachment format that does not match the \ - previously used pipeline", - ), - Self::PushConstantsDataOutOfRange { offset } => write!( - f, - "the push constants data to be written at offset {} is not included in any push \ - constant range of the pipeline layout", - offset, - ), - Self::PushConstantsOffsetNotAligned => { - write!(f, "the push constants offset is not a multiple of 4") - } - Self::PushConstantsSizeNotAligned => { - write!(f, "the push constants size is not a multiple of 4") - } - Self::VertexBufferMissingUsage => { - write!(f, "a vertex buffer is missing the `vertex_buffer` usage") - } - } - } -} - -impl From for BindPushError { - fn from(err: RequirementNotMet) -> Self { - Self::RequirementNotMet { - required_for: err.required_for, - requires_one_of: err.requires_one_of, - } - } -} diff --git a/vulkano/src/command_buffer/commands/clear.rs b/vulkano/src/command_buffer/commands/clear.rs index 0e36eeaf..278f572f 100644 --- a/vulkano/src/command_buffer/commands/clear.rs +++ b/vulkano/src/command_buffer/commands/clear.rs @@ -13,20 +13,15 @@ use crate::{ allocator::CommandBufferAllocator, auto::Resource, sys::UnsafeCommandBufferBuilder, AutoCommandBufferBuilder, ResourceInCommand, }, - device::{DeviceOwned, QueueFlags}, - format::{ClearColorValue, ClearDepthStencilValue, Format, FormatFeatures}, + device::{Device, DeviceOwned, QueueFlags}, + format::{ClearColorValue, ClearDepthStencilValue, FormatFeatures}, image::{Image, ImageAspects, ImageLayout, ImageSubresourceRange, ImageUsage}, sync::PipelineStageAccessFlags, - DeviceSize, RequirementNotMet, Requires, RequiresAllOf, RequiresOneOf, SafeDeref, Version, + DeviceSize, Requires, RequiresAllOf, RequiresOneOf, SafeDeref, ValidationError, Version, VulkanObject, }; use smallvec::{smallvec, SmallVec}; -use std::{ - error::Error, - fmt::{Display, Error as FmtError, Formatter}, - mem::size_of_val, - sync::Arc, -}; +use std::{mem::size_of_val, sync::Arc}; /// # Commands to fill resources with new data. impl AutoCommandBufferBuilder @@ -37,142 +32,24 @@ where pub fn clear_color_image( &mut self, clear_info: ClearColorImageInfo, - ) -> Result<&mut Self, ClearError> { + ) -> Result<&mut Self, Box> { self.validate_clear_color_image(&clear_info)?; - unsafe { - self.clear_color_image_unchecked(clear_info); - } - - Ok(self) + unsafe { Ok(self.clear_color_image_unchecked(clear_info)) } } fn validate_clear_color_image( &self, clear_info: &ClearColorImageInfo, - ) -> Result<(), ClearError> { - let device = self.device(); + ) -> Result<(), Box> { + self.inner.validate_clear_color_image(clear_info)?; - // VUID-vkCmdClearColorImage-renderpass if self.builder_state.render_pass.is_some() { - return Err(ClearError::ForbiddenInsideRenderPass); - } - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdClearColorImage-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(ClearError::NotSupportedByQueueFamily); - } - - let &ClearColorImageInfo { - ref image, - image_layout, - clear_value: _, - ref regions, - _ne: _, - } = clear_info; - - // VUID-vkCmdClearColorImage-imageLayout-parameter - image_layout.validate_device(device)?; - - // VUID-vkCmdClearColorImage-commonparent - assert_eq!(device, image.device()); - - // VUID-vkCmdClearColorImage-image-00002 - if !image.usage().intersects(ImageUsage::TRANSFER_DST) { - return Err(ClearError::MissingUsage { - usage: "transfer_dst", - }); - } - - if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { - // VUID-vkCmdClearColorImage-image-01993 - if !image - .format_features() - .intersects(FormatFeatures::TRANSFER_DST) - { - return Err(ClearError::MissingFormatFeature { - format_feature: "transfer_dst", - }); - } - } - - let image_aspects = image.format().aspects(); - - // VUID-vkCmdClearColorImage-image-00007 - if image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { - return Err(ClearError::FormatNotSupported { - format: image.format(), - }); - } - - // VUID-vkCmdClearColorImage-image-00007 - if image.format().compression().is_some() { - return Err(ClearError::FormatNotSupported { - format: image.format(), - }); - } - - // VUID-vkCmdClearColorImage-image-01545 - if image.format().ycbcr_chroma_sampling().is_some() { - return Err(ClearError::FormatNotSupported { - format: image.format(), - }); - } - - // VUID-vkCmdClearColorImage-imageLayout-01394 - if !matches!( - image_layout, - ImageLayout::TransferDstOptimal | ImageLayout::General - ) { - return Err(ClearError::ImageLayoutInvalid { image_layout }); - } - - for (region_index, subresource_range) in regions.iter().enumerate() { - // VUID-VkImageSubresourceRange-aspectMask-parameter - subresource_range.aspects.validate_device(device)?; - - // VUID-VkImageSubresourceRange-aspectMask-requiredbitmask - assert!(!subresource_range.aspects.is_empty()); - - // VUID-vkCmdClearColorImage-aspectMask-02498 - if !image_aspects.contains(subresource_range.aspects) { - return Err(ClearError::AspectsNotAllowed { - region_index, - aspects: subresource_range.aspects, - allowed_aspects: image_aspects, - }); - } - - // VUID-VkImageSubresourceRange-levelCount-01720 - assert!(!subresource_range.mip_levels.is_empty()); - - // VUID-vkCmdClearColorImage-baseMipLevel-01470 - // VUID-vkCmdClearColorImage-pRanges-01692 - if subresource_range.mip_levels.end > image.mip_levels() { - return Err(ClearError::MipLevelsOutOfRange { - region_index, - mip_levels_range_end: subresource_range.mip_levels.end, - image_mip_levels: image.array_layers(), - }); - } - - // VUID-VkImageSubresourceRange-layerCount-01721 - assert!(!subresource_range.array_layers.is_empty()); - - // VUID-vkCmdClearDepthStencilImage-baseArrayLayer-01476 - // VUID-vkCmdClearDepthStencilImage-pRanges-01695 - if subresource_range.array_layers.end > image.array_layers() { - return Err(ClearError::ArrayLayersOutOfRange { - region_index, - array_layers_range_end: subresource_range.array_layers.end, - image_array_layers: image.array_layers(), - }); - } + return Err(Box::new(ValidationError { + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdClearColorImage-renderpass"], + ..Default::default() + })); } Ok(()) @@ -210,7 +87,7 @@ where }) .collect(), move |out: &mut UnsafeCommandBufferBuilder| { - out.clear_color_image(&clear_info); + out.clear_color_image_unchecked(&clear_info); }, ); @@ -221,159 +98,24 @@ where pub fn clear_depth_stencil_image( &mut self, clear_info: ClearDepthStencilImageInfo, - ) -> Result<&mut Self, ClearError> { + ) -> Result<&mut Self, Box> { self.validate_clear_depth_stencil_image(&clear_info)?; - unsafe { - self.clear_depth_stencil_image_unchecked(clear_info); - } - - Ok(self) + unsafe { Ok(self.clear_depth_stencil_image_unchecked(clear_info)) } } fn validate_clear_depth_stencil_image( &self, clear_info: &ClearDepthStencilImageInfo, - ) -> Result<(), ClearError> { - let device = self.device(); + ) -> Result<(), Box> { + self.inner.validate_clear_depth_stencil_image(clear_info)?; - // VUID-vkCmdClearDepthStencilImage-renderpass if self.builder_state.render_pass.is_some() { - return Err(ClearError::ForbiddenInsideRenderPass); - } - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdClearDepthStencilImage-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(ClearError::NotSupportedByQueueFamily); - } - - let &ClearDepthStencilImageInfo { - ref image, - image_layout, - clear_value, - ref regions, - _ne: _, - } = clear_info; - - // VUID-vkCmdClearDepthStencilImage-imageLayout-parameter - image_layout.validate_device(device)?; - - // VUID-vkCmdClearDepthStencilImage-commonparent - assert_eq!(device, image.device()); - - if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { - // VUID-vkCmdClearDepthStencilImage-image-01994 - if !image - .format_features() - .intersects(FormatFeatures::TRANSFER_DST) - { - return Err(ClearError::MissingFormatFeature { - format_feature: "transfer_dst", - }); - } - } - - let image_aspects = image.format().aspects(); - - // VUID-vkCmdClearDepthStencilImage-image-00014 - if !image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { - return Err(ClearError::FormatNotSupported { - format: image.format(), - }); - } - - // VUID-vkCmdClearDepthStencilImage-imageLayout-00012 - if !matches!( - image_layout, - ImageLayout::TransferDstOptimal | ImageLayout::General - ) { - return Err(ClearError::ImageLayoutInvalid { image_layout }); - } - - // VUID-VkClearDepthStencilValue-depth-00022 - if !device.enabled_extensions().ext_depth_range_unrestricted - && !(0.0..=1.0).contains(&clear_value.depth) - { - return Err(ClearError::RequirementNotMet { - required_for: "`clear_info.clear_value.depth` is not between `0.0` and `1.0` \ - inclusive", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( - "ext_depth_range_unrestricted", - )])]), - }); - } - - let mut image_aspects_used = ImageAspects::empty(); - - for (region_index, subresource_range) in regions.iter().enumerate() { - // VUID-VkImageSubresourceRange-aspectMask-parameter - subresource_range.aspects.validate_device(device)?; - - // VUID-VkImageSubresourceRange-aspectMask-requiredbitmask - assert!(!subresource_range.aspects.is_empty()); - - // VUID-vkCmdClearDepthStencilImage-aspectMask-02824 - // VUID-vkCmdClearDepthStencilImage-image-02825 - // VUID-vkCmdClearDepthStencilImage-image-02826 - if !image_aspects.contains(subresource_range.aspects) { - return Err(ClearError::AspectsNotAllowed { - region_index, - aspects: subresource_range.aspects, - allowed_aspects: image_aspects, - }); - } - - image_aspects_used |= subresource_range.aspects; - - // VUID-VkImageSubresourceRange-levelCount-01720 - assert!(!subresource_range.mip_levels.is_empty()); - - // VUID-vkCmdClearDepthStencilImage-baseMipLevel-01474 - // VUID-vkCmdClearDepthStencilImage-pRanges-01694 - if subresource_range.mip_levels.end > image.mip_levels() { - return Err(ClearError::MipLevelsOutOfRange { - region_index, - mip_levels_range_end: subresource_range.mip_levels.end, - image_mip_levels: image.array_layers(), - }); - } - - // VUID-VkImageSubresourceRange-layerCount-01721 - assert!(!subresource_range.array_layers.is_empty()); - - // VUID-vkCmdClearDepthStencilImage-baseArrayLayer-01476 - // VUID-vkCmdClearDepthStencilImage-pRanges-01695 - if subresource_range.array_layers.end > image.array_layers() { - return Err(ClearError::ArrayLayersOutOfRange { - region_index, - array_layers_range_end: subresource_range.array_layers.end, - image_array_layers: image.array_layers(), - }); - } - } - - // VUID-vkCmdClearDepthStencilImage-pRanges-02658 - // VUID-vkCmdClearDepthStencilImage-pRanges-02659 - if image_aspects_used.intersects(ImageAspects::STENCIL) - && !image.stencil_usage().intersects(ImageUsage::TRANSFER_DST) - { - return Err(ClearError::MissingUsage { - usage: "transfer_dst", - }); - } - - // VUID-vkCmdClearDepthStencilImage-pRanges-02660 - if !(image_aspects_used - ImageAspects::STENCIL).is_empty() - && !image.usage().intersects(ImageUsage::TRANSFER_DST) - { - return Err(ClearError::MissingUsage { - usage: "transfer_dst", - }); + return Err(Box::new(ValidationError { + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdClearDepthStencilImage-renderpass"], + ..Default::default() + })); } Ok(()) @@ -411,7 +153,7 @@ where }) .collect(), move |out: &mut UnsafeCommandBufferBuilder| { - out.clear_depth_stencil_image(&clear_info); + out.clear_depth_stencil_image_unchecked(&clear_info); }, ); @@ -422,81 +164,31 @@ where /// /// This function is similar to the `memset` function in C. The `data` parameter is a number /// that will be repeatedly written through the entire buffer. - /// - /// # Panics - /// - /// - Panics if `dst_buffer` was not created from the same device as `self`. pub fn fill_buffer( &mut self, dst_buffer: Subbuffer<[u32]>, data: u32, - ) -> Result<&mut Self, ClearError> { + ) -> Result<&mut Self, Box> { self.validate_fill_buffer(&dst_buffer, data)?; - unsafe { - self.fill_buffer_unchecked(dst_buffer, data); - } - - Ok(self) + unsafe { Ok(self.fill_buffer_unchecked(dst_buffer, data)) } } fn validate_fill_buffer( &self, dst_buffer: &Subbuffer<[u32]>, - _data: u32, - ) -> Result<(), ClearError> { - let device = self.device(); + data: u32, + ) -> Result<(), Box> { + self.inner.validate_fill_buffer(dst_buffer, data)?; - // VUID-vkCmdFillBuffer-renderpass if self.builder_state.render_pass.is_some() { - return Err(ClearError::ForbiddenInsideRenderPass); + return Err(Box::new(ValidationError { + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdFillBuffer-renderpass"], + ..Default::default() + })); } - let queue_family_properties = self.queue_family_properties(); - - if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { - // VUID-vkCmdFillBuffer-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(ClearError::NotSupportedByQueueFamily); - } - } else { - // VUID-vkCmdFillBuffer-commandBuffer-00030 - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(ClearError::NotSupportedByQueueFamily); - } - } - - // VUID-vkCmdFillBuffer-commonparent - assert_eq!(device, dst_buffer.device()); - - // VUID-vkCmdFillBuffer-size-00026 - // Guaranteed by `Subbuffer` - - // VUID-vkCmdFillBuffer-dstBuffer-00029 - if !dst_buffer - .buffer() - .usage() - .intersects(BufferUsage::TRANSFER_DST) - { - return Err(ClearError::MissingUsage { - usage: "transfer_dst", - }); - } - - // VUID-vkCmdFillBuffer-dstOffset-00024 - // VUID-vkCmdFillBuffer-size-00027 - // Guaranteed by `Subbuffer` - - // VUID-vkCmdFillBuffer-dstOffset-00025 - // VUID-vkCmdFillBuffer-size-00028 - // Guaranteed because we take `Subbuffer<[u32]>` - Ok(()) } @@ -519,7 +211,7 @@ where .into_iter() .collect(), move |out: &mut UnsafeCommandBufferBuilder| { - out.fill_buffer(&dst_buffer, data); + out.fill_buffer_unchecked(&dst_buffer, data); }, ); @@ -527,15 +219,11 @@ where } /// Writes data to a region of a buffer. - /// - /// # Panics - /// - /// - Panics if `dst_buffer` was not created from the same device as `self`. pub fn update_buffer( &mut self, dst_buffer: Subbuffer, data: Dd, - ) -> Result<&mut Self, ClearError> + ) -> Result<&mut Self, Box> where D: BufferContents + ?Sized, Dd: SafeDeref + Send + Sync + 'static, @@ -545,86 +233,22 @@ where size_of_val(data.deref()) as DeviceSize, )?; - unsafe { - self.update_buffer_unchecked(dst_buffer, data); - } - - Ok(self) + unsafe { Ok(self.update_buffer_unchecked(dst_buffer, data)) } } fn validate_update_buffer( &self, dst_buffer: &Subbuffer<[u8]>, data_size: DeviceSize, - ) -> Result<(), ClearError> { - let device = self.device(); + ) -> Result<(), Box> { + self.inner.validate_update_buffer(dst_buffer, data_size)?; - // VUID-vkCmdUpdateBuffer-renderpass if self.builder_state.render_pass.is_some() { - return Err(ClearError::ForbiddenInsideRenderPass); - } - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdUpdateBuffer-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(ClearError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdUpdateBuffer-commonparent - assert_eq!(device, dst_buffer.device()); - - // VUID-vkCmdUpdateBuffer-dataSize-arraylength - assert!(data_size != 0); - - // VUID-vkCmdUpdateBuffer-dstBuffer-00034 - if !dst_buffer - .buffer() - .usage() - .intersects(BufferUsage::TRANSFER_DST) - { - return Err(ClearError::MissingUsage { - usage: "transfer_dst", - }); - } - - // VUID-vkCmdUpdateBuffer-dstOffset-00032 - // VUID-vkCmdUpdateBuffer-dataSize-00033 - if data_size > dst_buffer.size() { - return Err(ClearError::RegionOutOfBufferBounds { - region_index: 0, - offset_range_end: data_size, - buffer_size: dst_buffer.size(), - }); - } - - // VUID-vkCmdUpdateBuffer-dstOffset-00036 - if dst_buffer.offset() % 4 != 0 { - return Err(ClearError::OffsetNotAlignedForBuffer { - region_index: 0, - offset: dst_buffer.offset(), - required_alignment: 4, - }); - } - - // VUID-vkCmdUpdateBuffer-dataSize-00037 - if data_size > 65536 { - return Err(ClearError::DataTooLarge { - size: data_size, - max: 65536, - }); - } - - // VUID-vkCmdUpdateBuffer-dataSize-00038 - if data_size % 4 != 0 { - return Err(ClearError::SizeNotAlignedForBuffer { - region_index: 0, - size: data_size, - required_alignment: 4, - }); + return Err(Box::new(ValidationError { + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdUpdateBuffer-renderpass"], + ..Default::default() + })); } Ok(()) @@ -653,7 +277,7 @@ where .into_iter() .collect(), move |out: &mut UnsafeCommandBufferBuilder| { - out.update_buffer(&dst_buffer, &data); + out.update_buffer_unchecked(&dst_buffer, &data); }, ); @@ -665,12 +289,45 @@ impl UnsafeCommandBufferBuilder where A: CommandBufferAllocator, { - /// Calls `vkCmdClearColorImage` on the builder. - /// - /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid - /// usage of the command anyway. - #[inline] - pub unsafe fn clear_color_image(&mut self, clear_info: &ClearColorImageInfo) -> &mut Self { + pub unsafe fn clear_color_image( + &mut self, + clear_info: &ClearColorImageInfo, + ) -> Result<&mut Self, Box> { + self.validate_clear_color_image(clear_info)?; + + Ok(self.clear_color_image_unchecked(clear_info)) + } + + fn validate_clear_color_image( + &self, + clear_info: &ClearColorImageInfo, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics or compute operations" + .into(), + vuids: &["VUID-vkCmdClearColorImage-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + clear_info + .validate(self.device()) + .map_err(|err| err.add_context("clear_info"))?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn clear_color_image_unchecked( + &mut self, + clear_info: &ClearColorImageInfo, + ) -> &mut Self { let &ClearColorImageInfo { ref image, image_layout, @@ -703,14 +360,44 @@ where self } - /// Calls `vkCmdClearDepthStencilImage` on the builder. - /// - /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid - /// usage of the command anyway. - #[inline] pub unsafe fn clear_depth_stencil_image( &mut self, clear_info: &ClearDepthStencilImageInfo, + ) -> Result<&mut Self, Box> { + self.validate_clear_depth_stencil_image(clear_info)?; + + Ok(self.clear_depth_stencil_image_unchecked(clear_info)) + } + + fn validate_clear_depth_stencil_image( + &self, + clear_info: &ClearDepthStencilImageInfo, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdClearDepthStencilImage-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + clear_info + .validate(self.device()) + .map_err(|err| err.add_context("clear_info"))?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn clear_depth_stencil_image_unchecked( + &mut self, + clear_info: &ClearDepthStencilImageInfo, ) -> &mut Self { let &ClearDepthStencilImageInfo { ref image, @@ -744,9 +431,88 @@ where self } - /// Calls `vkCmdFillBuffer` on the builder. - #[inline] - pub unsafe fn fill_buffer(&mut self, dst_buffer: &Subbuffer<[u32]>, data: u32) -> &mut Self { + pub unsafe fn fill_buffer( + &mut self, + dst_buffer: &Subbuffer<[u32]>, + data: u32, + ) -> Result<&mut Self, Box> { + self.validate_fill_buffer(dst_buffer, data)?; + + Ok(self.fill_buffer_unchecked(dst_buffer, data)) + } + + fn validate_fill_buffer( + &self, + dst_buffer: &Subbuffer<[u32]>, + _data: u32, + ) -> Result<(), Box> { + let device = self.device(); + let queue_family_properties = self.queue_family_properties(); + + if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + transfer, graphics or compute operations" + .into(), + vuids: &["VUID-vkCmdFillBuffer-commandBuffer-cmdpool"], + ..Default::default() + })); + } + } else { + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics or compute operations" + .into(), + vuids: &["VUID-vkCmdFillBuffer-commandBuffer-00030"], + ..Default::default() + })); + } + } + + // VUID-vkCmdFillBuffer-commonparent + assert_eq!(device, dst_buffer.device()); + + // VUID-vkCmdFillBuffer-size-00026 + // Guaranteed by `Subbuffer` + + if !dst_buffer + .buffer() + .usage() + .intersects(BufferUsage::TRANSFER_DST) + { + return Err(Box::new(ValidationError { + context: "dst_buffer.buffer().usage()".into(), + problem: "does not contain `BufferUsage::TRANSFER_DST`".into(), + vuids: &["VUID-vkCmdFillBuffer-dstBuffer-00029"], + ..Default::default() + })); + } + + // VUID-vkCmdFillBuffer-dstOffset-00024 + // VUID-vkCmdFillBuffer-size-00027 + // Guaranteed by `Subbuffer` + + // VUID-vkCmdFillBuffer-dstOffset-00025 + // VUID-vkCmdFillBuffer-size-00028 + // Guaranteed because we take `Subbuffer<[u32]>` + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn fill_buffer_unchecked( + &mut self, + dst_buffer: &Subbuffer<[u32]>, + data: u32, + ) -> &mut Self { let fns = self.device().fns(); (fns.v1_0.cmd_fill_buffer)( self.handle(), @@ -759,11 +525,117 @@ where self } - /// Calls `vkCmdUpdateBuffer` on the builder. - pub unsafe fn update_buffer(&mut self, dst_buffer: &Subbuffer, data: &D) -> &mut Self + pub unsafe fn update_buffer( + &mut self, + dst_buffer: &Subbuffer, + data: &D, + ) -> Result<&mut Self, Box> where D: BufferContents + ?Sized, { + if size_of_val(data) == 0 { + return Ok(self); + } + + self.validate_update_buffer(dst_buffer.as_bytes(), size_of_val(data) as DeviceSize)?; + + Ok(self.update_buffer_unchecked(dst_buffer, data)) + } + + fn validate_update_buffer( + &self, + dst_buffer: &Subbuffer<[u8]>, + data_size: DeviceSize, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + transfer, graphics or compute operations" + .into(), + vuids: &["VUID-vkCmdUpdateBuffer-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + let device = self.device(); + + // VUID-vkCmdUpdateBuffer-commonparent + assert_eq!(device, dst_buffer.device()); + + // VUID-vkCmdUpdateBuffer-dataSize-arraylength + // Ensured because we return when the size is 0. + + if !dst_buffer + .buffer() + .usage() + .intersects(BufferUsage::TRANSFER_DST) + { + return Err(Box::new(ValidationError { + context: "dst_buffer.buffer().usage()".into(), + problem: "does not contain `BufferUsage::TRANSFER_DST`".into(), + vuids: &["VUID-vkCmdUpdateBuffer-dstBuffer-00034"], + ..Default::default() + })); + } + + if data_size > dst_buffer.size() { + return Err(Box::new(ValidationError { + problem: "the size of `data` is greater than `dst_buffer.size()`".into(), + vuids: &[ + "VUID-vkCmdUpdateBuffer-dstOffset-00032", + "VUID-vkCmdUpdateBuffer-dataSize-00033", + ], + ..Default::default() + })); + } + + if dst_buffer.offset() % 4 != 0 { + return Err(Box::new(ValidationError { + context: "dst_buffer.offset()".into(), + problem: "is not a multiple of 4".into(), + vuids: &["VUID-vkCmdUpdateBuffer-dstOffset-00036"], + ..Default::default() + })); + } + + if data_size > 65536 { + return Err(Box::new(ValidationError { + context: "data".into(), + problem: "the size is greater than 65536 bytes".into(), + vuids: &["VUID-vkCmdUpdateBuffer-dataSize-00037"], + ..Default::default() + })); + } + + if data_size % 4 != 0 { + return Err(Box::new(ValidationError { + context: "data".into(), + problem: "the size is not a multiple of 4".into(), + vuids: &["VUID-vkCmdUpdateBuffer-dataSize-00038"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn update_buffer_unchecked( + &mut self, + dst_buffer: &Subbuffer, + data: &D, + ) -> &mut Self + where + D: BufferContents + ?Sized, + { + if size_of_val(data) == 0 { + return self; + } + let fns = self.device().fns(); (fns.v1_0.cmd_update_buffer)( self.handle(), @@ -821,6 +693,142 @@ impl ClearColorImageInfo { _ne: crate::NonExhaustive(()), } } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + ref image, + image_layout, + clear_value: _, + ref regions, + _ne: _, + } = self; + + image_layout + .validate_device(device) + .map_err(|err| ValidationError { + context: "image_layout".into(), + vuids: &["VUID-vkCmdClearColorImage-imageLayout-parameter"], + ..ValidationError::from_requirement(err) + })?; + + // VUID-vkCmdClearColorImage-commonparent + assert_eq!(device, image.device().as_ref()); + + if !image.usage().intersects(ImageUsage::TRANSFER_DST) { + return Err(Box::new(ValidationError { + context: "image.usage()".into(), + problem: "does not contain `ImageUsage::TRANSFER_DST`".into(), + vuids: &["VUID-vkCmdClearColorImage-image-00002"], + ..Default::default() + })); + } + + if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { + if !image + .format_features() + .intersects(FormatFeatures::TRANSFER_DST) + { + return Err(Box::new(ValidationError { + context: "image.format_features()".into(), + problem: "does not contain `FormatFeatures::TRANSFER_DST`".into(), + vuids: &["VUID-vkCmdClearColorImage-image-01993"], + ..Default::default() + })); + } + } + + let image_aspects = image.format().aspects(); + + if image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { + return Err(Box::new(ValidationError { + context: "image.format()".into(), + problem: "is a depth/stencil format".into(), + vuids: &["VUID-vkCmdClearColorImage-image-00007"], + ..Default::default() + })); + } + + if image.format().compression().is_some() { + return Err(Box::new(ValidationError { + context: "image.format()".into(), + problem: "is a compressed format".into(), + vuids: &["VUID-vkCmdClearColorImage-image-00007"], + ..Default::default() + })); + } + + if image.format().ycbcr_chroma_sampling().is_some() { + return Err(Box::new(ValidationError { + context: "image.format()".into(), + problem: "is a YCbCr format".into(), + vuids: &["VUID-vkCmdClearColorImage-image-01545"], + ..Default::default() + })); + } + + if !matches!( + image_layout, + ImageLayout::TransferDstOptimal | ImageLayout::General + ) { + return Err(Box::new(ValidationError { + context: "image_layout".into(), + problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`" + .into(), + vuids: &["VUID-vkCmdClearColorImage-imageLayout-01394"], + ..Default::default() + })); + } + + for (region_index, subresource_range) in regions.iter().enumerate() { + subresource_range + .validate(device) + .map_err(|err| err.add_context(format!("regions[{}]", region_index)))?; + + if !image_aspects.contains(subresource_range.aspects) { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].aspects` is not a subset of `image.format().aspects()`", + region_index + ) + .into(), + vuids: &["VUID-vkCmdClearColorImage-aspectMask-02498"], + ..Default::default() + })); + } + + if subresource_range.mip_levels.end > image.mip_levels() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].mip_levels.end` is greater than `image.mip_levels()`", + region_index + ) + .into(), + vuids: &[ + "VUID-vkCmdClearColorImage-baseMipLevel-01470", + "VUID-vkCmdClearColorImage-pRanges-01692", + ], + ..Default::default() + })); + } + + if subresource_range.array_layers.end > image.array_layers() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].array_layers.end` is greater than `image.array_layers()`", + region_index + ) + .into(), + vuids: &[ + "VUID-vkCmdClearColorImage-baseArrayLayer-01472", + "VUID-vkCmdClearColorImage-pRanges-01693", + ], + ..Default::default() + })); + } + } + + Ok(()) + } } /// Parameters to clear a depth/stencil image. @@ -867,197 +875,164 @@ impl ClearDepthStencilImageInfo { _ne: crate::NonExhaustive(()), } } -} -/// Error that can happen when recording a clear command. -#[derive(Clone, Debug)] -pub enum ClearError { - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + ref image, + image_layout, + clear_value, + ref regions, + _ne: _, + } = self; - /// Operation forbidden inside of a render pass. - ForbiddenInsideRenderPass, + image_layout + .validate_device(device) + .map_err(|err| ValidationError { + context: "image_layout".into(), + vuids: &["VUID-vkCmdClearDepthStencilImage-imageLayout-parameter"], + ..ValidationError::from_requirement(err) + })?; - /// The queue family doesn't allow this operation. - NotSupportedByQueueFamily, + // VUID-vkCmdClearDepthStencilImage-commonparent + assert_eq!(device, image.device().as_ref()); - /// The end of the range of accessed array layers of the subresource range of a region is - /// greater than the number of array layers in the image. - ArrayLayersOutOfRange { - region_index: usize, - array_layers_range_end: u32, - image_array_layers: u32, - }, - - /// The aspects of the subresource range of a region contain aspects that are not present - /// in the image, or that are not allowed. - AspectsNotAllowed { - region_index: usize, - aspects: ImageAspects, - allowed_aspects: ImageAspects, - }, - - /// The provided data has a size larger than the maximum allowed. - DataTooLarge { size: DeviceSize, max: DeviceSize }, - - /// The format of an image is not supported for this operation. - FormatNotSupported { format: Format }, - - /// A specified image layout is not valid for this operation. - ImageLayoutInvalid { image_layout: ImageLayout }, - - /// The end of the range of accessed mip levels of the subresource range of a region is greater - /// than the number of mip levels in the image. - MipLevelsOutOfRange { - region_index: usize, - mip_levels_range_end: u32, - image_mip_levels: u32, - }, - - /// An image does not have a required format feature. - MissingFormatFeature { format_feature: &'static str }, - - /// A resource did not have a required usage enabled. - MissingUsage { usage: &'static str }, - - /// The buffer offset of a region is not a multiple of the required buffer alignment. - OffsetNotAlignedForBuffer { - region_index: usize, - offset: DeviceSize, - required_alignment: DeviceSize, - }, - - /// The end of the range of accessed byte offsets of a region is greater than the size of the - /// buffer. - RegionOutOfBufferBounds { - region_index: usize, - offset_range_end: DeviceSize, - buffer_size: DeviceSize, - }, - - /// The buffer size of a region is not a multiple of the required buffer alignment. - SizeNotAlignedForBuffer { - region_index: usize, - size: DeviceSize, - required_alignment: DeviceSize, - }, -} - -impl Error for ClearError {} - -impl Display for ClearError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::ForbiddenInsideRenderPass => { - write!(f, "operation forbidden inside of a render pass") + if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { + if !image + .format_features() + .intersects(FormatFeatures::TRANSFER_DST) + { + return Err(Box::new(ValidationError { + context: "image.format_features()".into(), + problem: "does not contain `FormatFeatures::TRANSFER_DST`".into(), + vuids: &["VUID-vkCmdClearDepthStencilImage-image-01994"], + ..Default::default() + })); } - Self::NotSupportedByQueueFamily => { - write!(f, "the queue family doesn't allow this operation") - } - Self::ArrayLayersOutOfRange { - region_index, - array_layers_range_end, - image_array_layers, - } => write!( - f, - "the end of the range of accessed array layers ({}) of the subresource range of \ - region {} is greater than the number of array layers in the image ({})", - array_layers_range_end, region_index, image_array_layers, - ), - Self::AspectsNotAllowed { - region_index, - aspects, - allowed_aspects, - } => write!( - f, - "the aspects ({:?}) of the subresource range of region {} contain aspects that \ - are not present in the image, or that are not allowed ({:?})", - aspects, region_index, allowed_aspects, - ), - Self::DataTooLarge { size, max } => write!( - f, - "the provided data has a size ({}) greater than the maximum allowed ({})", - size, max, - ), - Self::FormatNotSupported { format } => write!( - f, - "the format of the image ({:?}) is not supported for this operation", - format, - ), - Self::ImageLayoutInvalid { image_layout } => write!( - f, - "the specified image layout {:?} is not valid for this operation", - image_layout, - ), - Self::MipLevelsOutOfRange { - region_index, - mip_levels_range_end, - image_mip_levels, - } => write!( - f, - "the end of the range of accessed mip levels ({}) of the subresource range of \ - region {} is not less than the number of mip levels in the image ({})", - mip_levels_range_end, region_index, image_mip_levels, - ), - Self::MissingFormatFeature { format_feature } => write!( - f, - "the image does not have the required format feature {}", - format_feature, - ), - Self::MissingUsage { usage } => write!( - f, - "the resource did not have the required usage {} enabled", - usage, - ), - Self::OffsetNotAlignedForBuffer { - region_index, - offset, - required_alignment, - } => write!( - f, - "the buffer offset ({}) of region {} is not a multiple of the required \ - buffer alignment ({})", - offset, region_index, required_alignment, - ), - Self::RegionOutOfBufferBounds { - region_index, - offset_range_end, - buffer_size, - } => write!( - f, - "the end of the range of accessed byte offsets ({}) of region {} is greater \ - than the size of the buffer ({})", - offset_range_end, region_index, buffer_size, - ), - Self::SizeNotAlignedForBuffer { - region_index, - size, - required_alignment, - } => write!( - f, - "the buffer size ({}) of region {} is not a multiple of the required buffer \ - alignment ({})", - size, region_index, required_alignment, - ), - } - } -} - -impl From for ClearError { - fn from(err: RequirementNotMet) -> Self { - Self::RequirementNotMet { - required_for: err.required_for, - requires_one_of: err.requires_one_of, } + + let image_aspects = image.format().aspects(); + + if !image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { + return Err(Box::new(ValidationError { + context: "image.format()".into(), + problem: "is not a depth/stencil format".into(), + vuids: &["VUID-vkCmdClearDepthStencilImage-image-00014"], + ..Default::default() + })); + } + + if !matches!( + image_layout, + ImageLayout::TransferDstOptimal | ImageLayout::General + ) { + return Err(Box::new(ValidationError { + context: "image_layout".into(), + problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`" + .into(), + vuids: &["VUID-vkCmdClearDepthStencilImage-imageLayout-00012"], + ..Default::default() + })); + } + + if !(0.0..=1.0).contains(&clear_value.depth) + && !device.enabled_extensions().ext_depth_range_unrestricted + { + return Err(Box::new(ValidationError { + context: "clear_value.depth".into(), + problem: "is not between `0.0` and `1.0` inclusive".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "ext_depth_range_unrestricted", + )])]), + vuids: &["VUID-VkClearDepthStencilValue-depth-00022"], + })); + } + + for (region_index, subresource_range) in regions.iter().enumerate() { + subresource_range + .validate(device) + .map_err(|err| err.add_context(format!("regions[{}]", region_index)))?; + + if !image_aspects.contains(subresource_range.aspects) { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].aspects` is not a subset of `image.format().aspects()`", + region_index + ) + .into(), + vuids: &[ + "VUID-vkCmdClearDepthStencilImage-aspectMask-02824", + "VUID-vkCmdClearDepthStencilImage-image-02825", + "VUID-vkCmdClearDepthStencilImage-image-02826", + ], + ..Default::default() + })); + } + + if subresource_range.aspects.intersects(ImageAspects::STENCIL) + && !image.stencil_usage().intersects(ImageUsage::TRANSFER_DST) + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].aspects` contains `ImageAspects::STENCIL`, but \ + `image.stencil_usage()` does not contain `ImageUsage::TRANSFER_DST`", + region_index + ) + .into(), + vuids: &[ + "VUID-vkCmdClearDepthStencilImage-pRanges-02658", + "VUID-vkCmdClearDepthStencilImage-pRanges-02659", + ], + ..Default::default() + })); + } + + if !(subresource_range.aspects - ImageAspects::STENCIL).is_empty() + && !image.usage().intersects(ImageUsage::TRANSFER_DST) + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].aspects` contains aspects other than \ + `ImageAspects::STENCIL`, but \ + `image.usage()` does not contain `ImageUsage::TRANSFER_DST`", + region_index + ) + .into(), + vuids: &["VUID-vkCmdClearDepthStencilImage-pRanges-02660"], + ..Default::default() + })); + } + + if subresource_range.mip_levels.end > image.mip_levels() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].mip_levels.end` is greater than `image.mip_levels()`", + region_index + ) + .into(), + vuids: &[ + "VUID-vkCmdClearDepthStencilImage-baseMipLevel-01474", + "VUID-vkCmdClearDepthStencilImage-pRanges-01694", + ], + ..Default::default() + })); + } + + if subresource_range.array_layers.end > image.array_layers() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].array_layers.end` is greater than `image.array_layers()`", + region_index + ) + .into(), + vuids: &[ + "VUID-vkCmdClearDepthStencilImage-baseArrayLayer-01476", + "VUID-vkCmdClearDepthStencilImage-pRanges-01695", + ], + ..Default::default() + })); + } + } + + Ok(()) } } diff --git a/vulkano/src/command_buffer/commands/copy.rs b/vulkano/src/command_buffer/commands/copy.rs index e55f685e..2d815408 100644 --- a/vulkano/src/command_buffer/commands/copy.rs +++ b/vulkano/src/command_buffer/commands/copy.rs @@ -13,20 +13,18 @@ use crate::{ allocator::CommandBufferAllocator, auto::Resource, sys::UnsafeCommandBufferBuilder, AutoCommandBufferBuilder, ResourceInCommand, }, - device::{DeviceOwned, QueueFlags}, - format::{Format, FormatFeatures, NumericType}, + device::{Device, DeviceOwned, QueueFlags}, + format::{Format, FormatFeatures}, image::{ mip_level_extent, sampler::Filter, Image, ImageAspects, ImageLayout, - ImageSubresourceLayers, ImageType, ImageUsage, SampleCount, SampleCounts, + ImageSubresourceLayers, ImageTiling, ImageType, ImageUsage, SampleCount, }, sync::PipelineStageAccessFlags, - DeviceSize, RequirementNotMet, RequiresOneOf, Version, VulkanObject, + DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, Version, VulkanObject, }; use smallvec::{smallvec, SmallVec}; use std::{ cmp::{max, min}, - error::Error, - fmt::{Display, Error as FmtError, Formatter}, mem::size_of, sync::Arc, }; @@ -45,134 +43,25 @@ where pub fn copy_buffer( &mut self, copy_buffer_info: impl Into, - ) -> Result<&mut Self, CopyError> { + ) -> Result<&mut Self, Box> { let copy_buffer_info = copy_buffer_info.into(); self.validate_copy_buffer(©_buffer_info)?; - unsafe { - self.copy_buffer_unchecked(copy_buffer_info); - } - - Ok(self) + unsafe { Ok(self.copy_buffer_unchecked(copy_buffer_info)) } } - fn validate_copy_buffer(&self, copy_buffer_info: &CopyBufferInfo) -> Result<(), CopyError> { - let device = self.device(); + fn validate_copy_buffer( + &self, + copy_buffer_info: &CopyBufferInfo, + ) -> Result<(), Box> { + self.inner.validate_copy_buffer(copy_buffer_info)?; - // VUID-vkCmdCopyBuffer2-renderpass if self.builder_state.render_pass.is_some() { - return Err(CopyError::ForbiddenInsideRenderPass); - } - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdCopyBuffer2-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(CopyError::NotSupportedByQueueFamily); - } - - let &CopyBufferInfo { - ref src_buffer, - ref dst_buffer, - ref regions, - _ne: _, - } = copy_buffer_info; - - // VUID-VkCopyBufferInfo2-commonparent - assert_eq!(device, src_buffer.device()); - assert_eq!(device, dst_buffer.device()); - - // VUID-VkCopyBufferInfo2-srcBuffer-00118 - if !src_buffer - .buffer() - .usage() - .intersects(BufferUsage::TRANSFER_SRC) - { - return Err(CopyError::MissingUsage { - resource: CopyErrorResource::Source, - usage: "transfer_src", - }); - } - - // VUID-VkCopyBufferInfo2-dstBuffer-00120 - if !dst_buffer - .buffer() - .usage() - .intersects(BufferUsage::TRANSFER_DST) - { - return Err(CopyError::MissingUsage { - resource: CopyErrorResource::Destination, - usage: "transfer_dst", - }); - } - - let same_buffer = src_buffer.buffer() == dst_buffer.buffer(); - let mut overlap_indices = None; - - for (region_index, region) in regions.iter().enumerate() { - let &BufferCopy { - src_offset, - dst_offset, - size, - _ne: _, - } = region; - - // VUID-VkBufferCopy2-size-01988 - assert!(size != 0); - - // VUID-VkCopyBufferInfo2-srcOffset-00113 - // VUID-VkCopyBufferInfo2-size-00115 - if src_offset + size > src_buffer.size() { - return Err(CopyError::RegionOutOfBufferBounds { - resource: CopyErrorResource::Source, - region_index, - offset_range_end: src_offset + size, - buffer_size: src_buffer.size(), - }); - } - - // VUID-VkCopyBufferInfo2-dstOffset-00114 - // VUID-VkCopyBufferInfo2-size-00116 - if dst_offset + size > dst_buffer.size() { - return Err(CopyError::RegionOutOfBufferBounds { - resource: CopyErrorResource::Destination, - region_index, - offset_range_end: dst_offset + size, - buffer_size: dst_buffer.size(), - }); - } - - // VUID-VkCopyBufferInfo2-pRegions-00117 - if same_buffer { - let src_region_index = region_index; - let src_range = - src_buffer.offset() + src_offset..src_buffer.offset() + src_offset + size; - - for (dst_region_index, dst_region) in regions.iter().enumerate() { - let &BufferCopy { dst_offset, .. } = dst_region; - - let dst_range = - dst_buffer.offset() + dst_offset..dst_buffer.offset() + dst_offset + size; - - if src_range.start >= dst_range.end || dst_range.start >= src_range.end { - // The regions do not overlap - continue; - } - - overlap_indices = Some((src_region_index, dst_region_index)); - } - } - } - - // VUID-VkCopyBufferInfo2-pRegions-00117 - if let Some((src_region_index, dst_region_index)) = overlap_indices { - return Err(CopyError::OverlappingRegions { - src_region_index, - dst_region_index, - }); + return Err(Box::new(ValidationError { + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdCopyBuffer2-renderpass"], + ..Default::default() + })); } Ok(()) @@ -224,7 +113,7 @@ where }) .collect(), move |out: &mut UnsafeCommandBufferBuilder| { - out.copy_buffer(©_buffer_info); + out.copy_buffer_unchecked(©_buffer_info); }, ); @@ -251,672 +140,27 @@ where /// /// - Panics if `src_image` or `dst_image` were not created from the same device /// as `self`. - pub fn copy_image(&mut self, copy_image_info: CopyImageInfo) -> Result<&mut Self, CopyError> { + pub fn copy_image( + &mut self, + copy_image_info: CopyImageInfo, + ) -> Result<&mut Self, Box> { self.validate_copy_image(©_image_info)?; - unsafe { - self.copy_image_unchecked(copy_image_info); - } - - Ok(self) + unsafe { Ok(self.copy_image_unchecked(copy_image_info)) } } - fn validate_copy_image(&self, copy_image_info: &CopyImageInfo) -> Result<(), CopyError> { - let device = self.device(); + fn validate_copy_image( + &self, + copy_image_info: &CopyImageInfo, + ) -> Result<(), Box> { + self.inner.validate_copy_image(copy_image_info)?; - // VUID-vkCmdCopyImage2-renderpass if self.builder_state.render_pass.is_some() { - return Err(CopyError::ForbiddenInsideRenderPass); - } - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdCopyImage2-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(CopyError::NotSupportedByQueueFamily); - } - - let &CopyImageInfo { - ref src_image, - src_image_layout, - ref dst_image, - dst_image_layout, - ref regions, - _ne: _, - } = copy_image_info; - - // VUID-VkCopyImageInfo2-srcImageLayout-parameter - src_image_layout.validate_device(device)?; - - // VUID-VkCopyImageInfo2-dstImageLayout-parameter - dst_image_layout.validate_device(device)?; - - // VUID-VkCopyImageInfo2-commonparent - assert_eq!(device, src_image.device()); - assert_eq!(device, dst_image.device()); - - let copy_2d_3d_supported = - device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1; - let mut src_image_aspects = src_image.format().aspects(); - let mut dst_image_aspects = dst_image.format().aspects(); - - if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { - // VUID-VkCopyImageInfo2-srcImage-01995 - if !src_image - .format_features() - .intersects(FormatFeatures::TRANSFER_SRC) - { - return Err(CopyError::MissingFormatFeature { - resource: CopyErrorResource::Source, - format_feature: "transfer_src", - }); - } - - // VUID-VkCopyImageInfo2-dstImage-01996 - if !dst_image - .format_features() - .intersects(FormatFeatures::TRANSFER_DST) - { - return Err(CopyError::MissingFormatFeature { - resource: CopyErrorResource::Destination, - format_feature: "transfer_dst", - }); - } - } - - // VUID-VkCopyImageInfo2-srcImage-00136 - if src_image.samples() != dst_image.samples() { - return Err(CopyError::SampleCountMismatch { - src_sample_count: src_image.samples(), - dst_sample_count: dst_image.samples(), - }); - } - - if !(src_image_aspects.intersects(ImageAspects::COLOR) - || dst_image_aspects.intersects(ImageAspects::COLOR)) - { - // VUID-VkCopyImageInfo2-srcImage-01548 - if src_image.format() != dst_image.format() { - return Err(CopyError::FormatsMismatch { - src_format: src_image.format(), - dst_format: dst_image.format(), - }); - } - } - - // VUID-VkCopyImageInfo2-srcImageLayout-01917 - if !matches!( - src_image_layout, - ImageLayout::TransferSrcOptimal | ImageLayout::General - ) { - return Err(CopyError::ImageLayoutInvalid { - resource: CopyErrorResource::Source, - image_layout: src_image_layout, - }); - } - - // VUID-VkCopyImageInfo2-dstImageLayout-01395 - if !matches!( - dst_image_layout, - ImageLayout::TransferDstOptimal | ImageLayout::General - ) { - return Err(CopyError::ImageLayoutInvalid { - resource: CopyErrorResource::Destination, - image_layout: dst_image_layout, - }); - } - - let extent_alignment = match queue_family_properties.min_image_transfer_granularity { - [0, 0, 0] => None, - min_image_transfer_granularity => { - let granularity = move |block_extent: [u32; 3], is_multi_plane: bool| { - if is_multi_plane { - // Assume planes always have 1x1 blocks - min_image_transfer_granularity - } else { - // "The value returned in minImageTransferGranularity has a unit of - // compressed texel blocks for images having a block-compressed format, and - // a unit of texels otherwise." - [ - min_image_transfer_granularity[0] * block_extent[0], - min_image_transfer_granularity[1] * block_extent[1], - min_image_transfer_granularity[2] * block_extent[2], - ] - } - }; - - Some(( - granularity( - src_image.format().block_extent(), - src_image_aspects.intersects(ImageAspects::PLANE_0), - ), - granularity( - dst_image.format().block_extent(), - dst_image_aspects.intersects(ImageAspects::PLANE_0), - ), - )) - } - }; - - if src_image_aspects.intersects(ImageAspects::PLANE_0) { - // VUID-VkCopyImageInfo2-srcImage-01552 - // VUID-VkCopyImageInfo2-srcImage-01553 - src_image_aspects -= ImageAspects::COLOR; - } - - if dst_image_aspects.intersects(ImageAspects::PLANE_0) { - // VUID-VkCopyImageInfo2-dstImage-01554 - // VUID-VkCopyImageInfo2-dstImage-01555 - dst_image_aspects -= ImageAspects::COLOR; - } - - let mut src_image_aspects_used = ImageAspects::empty(); - let mut dst_image_aspects_used = ImageAspects::empty(); - let is_same_image = src_image == dst_image; - let mut overlap_subresource_indices = None; - let mut overlap_extent_indices = None; - - for (region_index, region) in regions.iter().enumerate() { - let &ImageCopy { - ref src_subresource, - src_offset, - ref dst_subresource, - dst_offset, - extent, - _ne, - } = region; - - let check_subresource = |resource: CopyErrorResource, - image: &Image, - image_aspects: ImageAspects, - subresource: &ImageSubresourceLayers| - -> Result<_, CopyError> { - // VUID-VkCopyImageInfo2-srcSubresource-01696 - // VUID-VkCopyImageInfo2-dstSubresource-01697 - if subresource.mip_level >= image.mip_levels() { - return Err(CopyError::MipLevelsOutOfRange { - resource, - region_index, - mip_levels_range_end: subresource.mip_level + 1, - image_mip_levels: image.mip_levels(), - }); - } - - // VUID-VkImageSubresourceLayers-layerCount-01700 - assert!(!subresource.array_layers.is_empty()); - - // VUID-VkCopyImageInfo2-srcSubresource-01698 - // VUID-VkCopyImageInfo2-dstSubresource-01699 - // VUID-VkCopyImageInfo2-srcImage-04443 - // VUID-VkCopyImageInfo2-dstImage-04444 - if subresource.array_layers.end > image.array_layers() { - return Err(CopyError::ArrayLayersOutOfRange { - resource, - region_index, - array_layers_range_end: subresource.array_layers.end, - image_array_layers: image.array_layers(), - }); - } - - // VUID-VkImageSubresourceLayers-aspectMask-parameter - subresource.aspects.validate_device(device)?; - - // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask - assert!(!subresource.aspects.is_empty()); - - // VUID-VkCopyImageInfo2-aspectMask-00142 - // VUID-VkCopyImageInfo2-aspectMask-00143 - if !image_aspects.contains(subresource.aspects) { - return Err(CopyError::AspectsNotAllowed { - resource, - region_index, - aspects: subresource.aspects, - allowed_aspects: image_aspects, - }); - } - - let (subresource_format, subresource_extent) = - if image_aspects.intersects(ImageAspects::PLANE_0) { - // VUID-VkCopyImageInfo2-srcImage-01552 - // VUID-VkCopyImageInfo2-srcImage-01553 - // VUID-VkCopyImageInfo2-dstImage-01554 - // VUID-VkCopyImageInfo2-dstImage-01555 - if subresource.aspects.count() != 1 { - return Err(CopyError::MultipleAspectsNotAllowed { - resource, - region_index, - aspects: subresource.aspects, - }); - } - - if subresource.aspects.intersects(ImageAspects::PLANE_0) { - (image.format().planes()[0], image.extent()) - } else if subresource.aspects.intersects(ImageAspects::PLANE_1) { - ( - image.format().planes()[1], - image - .format() - .ycbcr_chroma_sampling() - .unwrap() - .subsampled_extent(image.extent()), - ) - } else { - ( - image.format().planes()[2], - image - .format() - .ycbcr_chroma_sampling() - .unwrap() - .subsampled_extent(image.extent()), - ) - } - } else { - ( - image.format(), - mip_level_extent(image.extent(), subresource.mip_level).unwrap(), - ) - }; - - Ok((subresource_format, subresource_extent)) - }; - - src_image_aspects_used |= src_subresource.aspects; - dst_image_aspects_used |= dst_subresource.aspects; - - let (src_subresource_format, src_subresource_extent) = check_subresource( - CopyErrorResource::Source, - src_image, - src_image_aspects, - src_subresource, - )?; - let (dst_subresource_format, dst_subresource_extent) = check_subresource( - CopyErrorResource::Destination, - dst_image, - dst_image_aspects, - dst_subresource, - )?; - - if !(src_image_aspects.intersects(ImageAspects::PLANE_0) - || dst_image_aspects.intersects(ImageAspects::PLANE_0)) - { - // VUID-VkCopyImageInfo2-srcImage-01551 - if src_subresource.aspects != dst_subresource.aspects { - return Err(CopyError::AspectsMismatch { - region_index, - src_aspects: src_subresource.aspects, - dst_aspects: dst_subresource.aspects, - }); - } - } - - // VUID-VkCopyImageInfo2-srcImage-01548 - // VUID-VkCopyImageInfo2-None-01549 - // Color formats must be size-compatible. - if src_subresource_format.block_size() != dst_subresource_format.block_size() { - return Err(CopyError::FormatsNotCompatible { - src_format: src_subresource_format, - dst_format: dst_subresource_format, - }); - } - - // TODO: - // "When copying between compressed and uncompressed formats the extent members - // represent the texel dimensions of the source image and not the destination." - let mut src_extent = extent; - let mut dst_extent = extent; - let src_layer_count = - src_subresource.array_layers.end - src_subresource.array_layers.start; - let dst_layer_count = - dst_subresource.array_layers.end - dst_subresource.array_layers.start; - - if copy_2d_3d_supported { - match (src_image.image_type(), dst_image.image_type()) { - (ImageType::Dim2d, ImageType::Dim3d) => { - src_extent[2] = 1; - - // VUID-vkCmdCopyImage-srcImage-01791 - if dst_extent[2] != src_layer_count { - return Err(CopyError::ArrayLayerCountMismatch { - region_index, - src_layer_count, - dst_layer_count: dst_extent[2], - }); - } - } - (ImageType::Dim3d, ImageType::Dim2d) => { - dst_extent[2] = 1; - - // VUID-vkCmdCopyImage-dstImage-01792 - if src_extent[2] != dst_layer_count { - return Err(CopyError::ArrayLayerCountMismatch { - region_index, - src_layer_count: src_extent[2], - dst_layer_count, - }); - } - } - _ => { - // VUID-VkImageCopy2-extent-00140 - if src_layer_count != dst_layer_count { - return Err(CopyError::ArrayLayerCountMismatch { - region_index, - src_layer_count, - dst_layer_count, - }); - } - } - } - } else { - // VUID-VkImageCopy2-extent-00140 - if src_layer_count != dst_layer_count { - return Err(CopyError::ArrayLayerCountMismatch { - region_index, - src_layer_count, - dst_layer_count, - }); - } - }; - - if let Some((src_extent_alignment, dst_extent_alignment)) = extent_alignment { - let check_offset_extent = |resource: CopyErrorResource, - extent_alignment: [u32; 3], - subresource_extent: [u32; 3], - offset: [u32; 3], - extent: [u32; 3]| - -> Result<_, CopyError> { - for i in 0..3 { - // VUID-VkImageCopy2-extent-06668 - // VUID-VkImageCopy2-extent-06669 - // VUID-VkImageCopy2-extent-06670 - assert!(extent[i] != 0); - - // VUID-VkCopyImageInfo2-srcOffset-00144 - // VUID-VkCopyImageInfo2-srcOffset-00145 - // VUID-VkCopyImageInfo2-srcOffset-00147 - // VUID-VkCopyImageInfo2-dstOffset-00150 - // VUID-VkCopyImageInfo2-dstOffset-00151 - // VUID-VkCopyImageInfo2-dstOffset-00153 - if offset[i] + extent[i] > subresource_extent[i] { - return Err(CopyError::RegionOutOfImageBounds { - resource, - region_index, - offset_range_end: [ - offset[0] + extent[0], - offset[1] + extent[1], - offset[2] + extent[2], - ], - subresource_extent, - }); - } - - // VUID-VkCopyImageInfo2-srcImage-01727 - // VUID-VkCopyImageInfo2-dstImage-01731 - // VUID-VkCopyImageInfo2-srcOffset-01783 - // VUID-VkCopyImageInfo2-dstOffset-01784 - if offset[i] % extent_alignment[i] != 0 { - return Err(CopyError::OffsetNotAlignedForImage { - resource, - region_index, - offset, - required_alignment: extent_alignment, - }); - } - - // VUID-VkCopyImageInfo2-srcImage-01728 - // VUID-VkCopyImageInfo2-srcImage-01729 - // VUID-VkCopyImageInfo2-srcImage-01730 - // VUID-VkCopyImageInfo2-dstImage-01732 - // VUID-VkCopyImageInfo2-dstImage-01733 - // VUID-VkCopyImageInfo2-dstImage-01734 - if offset[i] + extent[i] != subresource_extent[i] - && extent[i] % extent_alignment[i] != 0 - { - return Err(CopyError::ExtentNotAlignedForImage { - resource, - region_index, - extent, - required_alignment: extent_alignment, - }); - } - } - - Ok(()) - }; - - check_offset_extent( - CopyErrorResource::Source, - src_extent_alignment, - src_subresource_extent, - src_offset, - src_extent, - )?; - check_offset_extent( - CopyErrorResource::Destination, - dst_extent_alignment, - dst_subresource_extent, - dst_offset, - dst_extent, - )?; - - // VUID-VkCopyImageInfo2-pRegions-00124 - if is_same_image { - let src_region_index = region_index; - let src_subresource_axes = [ - src_subresource.mip_level..src_subresource.mip_level + 1, - src_subresource.array_layers.start..src_subresource.array_layers.end, - ]; - let src_extent_axes = [ - src_offset[0]..src_offset[0] + extent[0], - src_offset[1]..src_offset[1] + extent[1], - src_offset[2]..src_offset[2] + extent[2], - ]; - - for (dst_region_index, dst_region) in regions.iter().enumerate() { - let &ImageCopy { - ref dst_subresource, - dst_offset, - .. - } = dst_region; - - // For a single-plane image, the aspects must always be identical anyway - if src_image_aspects.intersects(ImageAspects::PLANE_0) - && src_subresource.aspects != dst_subresource.aspects - { - continue; - } - - let dst_subresource_axes = [ - dst_subresource.mip_level..dst_subresource.mip_level + 1, - src_subresource.array_layers.start..src_subresource.array_layers.end, - ]; - - if src_subresource_axes.iter().zip(dst_subresource_axes).any( - |(src_range, dst_range)| { - src_range.start >= dst_range.end || dst_range.start >= src_range.end - }, - ) { - continue; - } - - // If the subresource axes all overlap, then the source and destination must - // have the same layout. - overlap_subresource_indices = Some((src_region_index, dst_region_index)); - - let dst_extent_axes = [ - dst_offset[0]..dst_offset[0] + extent[0], - dst_offset[1]..dst_offset[1] + extent[1], - dst_offset[2]..dst_offset[2] + extent[2], - ]; - - // There is only overlap if all of the axes overlap. - if src_extent_axes.iter().zip(dst_extent_axes).any( - |(src_range, dst_range)| { - src_range.start >= dst_range.end || dst_range.start >= src_range.end - }, - ) { - continue; - } - - overlap_extent_indices = Some((src_region_index, dst_region_index)); - } - } - } else { - // If granularity is `None`, then we can only copy whole subresources. - let check_offset_extent = |resource: CopyErrorResource, - subresource_extent: [u32; 3], - offset: [u32; 3], - extent: [u32; 3]| - -> Result<_, CopyError> { - // VUID-VkCopyImageInfo2-srcImage-01727 - // VUID-VkCopyImageInfo2-dstImage-01731 - // VUID-vkCmdCopyImage-srcOffset-01783 - // VUID-vkCmdCopyImage-dstOffset-01784 - if offset != [0, 0, 0] { - return Err(CopyError::OffsetNotAlignedForImage { - resource, - region_index, - offset, - required_alignment: subresource_extent, - }); - } - - // VUID-VkCopyImageInfo2-srcImage-01728 - // VUID-VkCopyImageInfo2-srcImage-01729 - // VUID-VkCopyImageInfo2-srcImage-01730 - // VUID-VkCopyImageInfo2-dstImage-01732 - // VUID-VkCopyImageInfo2-dstImage-01733 - // VUID-VkCopyImageInfo2-dstImage-01734 - if extent != subresource_extent { - return Err(CopyError::ExtentNotAlignedForImage { - resource, - region_index, - extent, - required_alignment: subresource_extent, - }); - } - - Ok(()) - }; - - check_offset_extent( - CopyErrorResource::Source, - src_subresource_extent, - src_offset, - src_extent, - )?; - check_offset_extent( - CopyErrorResource::Destination, - dst_subresource_extent, - dst_offset, - dst_extent, - )?; - - // VUID-VkCopyImageInfo2-pRegions-00124 - // A simpler version that assumes the region covers the full extent. - if is_same_image { - let src_region_index = region_index; - let src_axes = [ - src_subresource.mip_level..src_subresource.mip_level + 1, - src_subresource.array_layers.start..src_subresource.array_layers.end, - ]; - - for (dst_region_index, dst_region) in regions.iter().enumerate() { - let &ImageCopy { - ref dst_subresource, - dst_offset: _, - .. - } = dst_region; - - if src_image_aspects.intersects(ImageAspects::PLANE_0) - && src_subresource.aspects != dst_subresource.aspects - { - continue; - } - - let dst_axes = [ - dst_subresource.mip_level..dst_subresource.mip_level + 1, - src_subresource.array_layers.start..src_subresource.array_layers.end, - ]; - - // There is only overlap if all of the axes overlap. - if src_axes.iter().zip(dst_axes).any(|(src_range, dst_range)| { - src_range.start >= dst_range.end || dst_range.start >= src_range.end - }) { - continue; - } - - overlap_extent_indices = Some((src_region_index, dst_region_index)); - } - } - } - } - - // VUID-VkCopyImageInfo2-aspect-06662 - if !(src_image_aspects_used - ImageAspects::STENCIL).is_empty() - && !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) - { - return Err(CopyError::MissingUsage { - resource: CopyErrorResource::Source, - usage: "transfer_src", - }); - } - - // VUID-VkCopyImageInfo2-aspect-06663 - if !(dst_image_aspects_used - ImageAspects::STENCIL).is_empty() - && !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) - { - return Err(CopyError::MissingUsage { - resource: CopyErrorResource::Destination, - usage: "transfer_dst", - }); - } - - // VUID-VkCopyImageInfo2-aspect-06664 - if src_image_aspects_used.intersects(ImageAspects::STENCIL) - && !src_image - .stencil_usage() - .intersects(ImageUsage::TRANSFER_SRC) - { - return Err(CopyError::MissingUsage { - resource: CopyErrorResource::Source, - usage: "transfer_src", - }); - } - - // VUID-VkCopyImageInfo2-aspect-06665 - if dst_image_aspects_used.intersects(ImageAspects::STENCIL) - && !dst_image - .stencil_usage() - .intersects(ImageUsage::TRANSFER_DST) - { - return Err(CopyError::MissingUsage { - resource: CopyErrorResource::Destination, - usage: "transfer_dst", - }); - } - - // VUID-VkCopyImageInfo2-pRegions-00124 - if let Some((src_region_index, dst_region_index)) = overlap_extent_indices { - return Err(CopyError::OverlappingRegions { - src_region_index, - dst_region_index, - }); - } - - // VUID-VkCopyImageInfo2-srcImageLayout-00128 - // VUID-VkCopyImageInfo2-dstImageLayout-00133 - if let Some((src_region_index, dst_region_index)) = overlap_subresource_indices { - if src_image_layout != dst_image_layout { - return Err(CopyError::OverlappingSubresourcesLayoutMismatch { - src_region_index, - dst_region_index, - src_image_layout, - dst_image_layout, - }); - } + return Err(Box::new(ValidationError { + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdCopyImage2-renderpass"], + ..Default::default() + })); } Ok(()) @@ -972,7 +216,7 @@ where }) .collect(), move |out: &mut UnsafeCommandBufferBuilder| { - out.copy_image(©_image_info); + out.copy_image_unchecked(©_image_info); }, ); @@ -983,434 +227,27 @@ where pub fn copy_buffer_to_image( &mut self, copy_buffer_to_image_info: CopyBufferToImageInfo, - ) -> Result<&mut Self, CopyError> { + ) -> Result<&mut Self, Box> { self.validate_copy_buffer_to_image(©_buffer_to_image_info)?; - unsafe { - self.copy_buffer_to_image_unchecked(copy_buffer_to_image_info); - } - - Ok(self) + unsafe { Ok(self.copy_buffer_to_image_unchecked(copy_buffer_to_image_info)) } } fn validate_copy_buffer_to_image( &self, copy_buffer_to_image_info: &CopyBufferToImageInfo, - ) -> Result<(), CopyError> { - let device = self.device(); + ) -> Result<(), Box> { + self.inner + .validate_copy_buffer_to_image(copy_buffer_to_image_info)?; - // VUID-vkCmdCopyBufferToImage2-renderpass if self.builder_state.render_pass.is_some() { - return Err(CopyError::ForbiddenInsideRenderPass); + return Err(Box::new(ValidationError { + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdCopyBufferToImage2-renderpass"], + ..Default::default() + })); } - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdCopyBufferToImage2-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(CopyError::NotSupportedByQueueFamily); - } - - let &CopyBufferToImageInfo { - ref src_buffer, - ref dst_image, - dst_image_layout, - ref regions, - _ne: _, - } = copy_buffer_to_image_info; - - // VUID-VkCopyBufferToImageInfo2-dstImageLayout-parameter - dst_image_layout.validate_device(device)?; - - // VUID-VkCopyBufferToImageInfo2-commonparent - assert_eq!(device, src_buffer.device()); - assert_eq!(device, dst_image.device()); - - let mut image_aspects = dst_image.format().aspects(); - - // VUID-VkCopyBufferToImageInfo2-commandBuffer-04477 - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - && !image_aspects.intersects(ImageAspects::COLOR) - { - return Err(CopyError::DepthStencilNotSupportedByQueueFamily); - } - - // VUID-VkCopyBufferToImageInfo2-srcBuffer-00174 - if !src_buffer - .buffer() - .usage() - .intersects(BufferUsage::TRANSFER_SRC) - { - return Err(CopyError::MissingUsage { - resource: CopyErrorResource::Source, - usage: "transfer_src", - }); - } - - // VUID-VkCopyBufferToImageInfo2-dstImage-00177 - if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) { - return Err(CopyError::MissingUsage { - resource: CopyErrorResource::Destination, - usage: "transfer_dst", - }); - } - - if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { - // VUID-VkCopyBufferToImageInfo2-dstImage-01997 - if !dst_image - .format_features() - .intersects(FormatFeatures::TRANSFER_DST) - { - return Err(CopyError::MissingFormatFeature { - resource: CopyErrorResource::Destination, - format_feature: "transfer_dst", - }); - } - } - - // VUID-VkCopyBufferToImageInfo2-dstImage-00179 - if dst_image.samples() != SampleCount::Sample1 { - return Err(CopyError::SampleCountInvalid { - resource: CopyErrorResource::Destination, - sample_count: dst_image.samples(), - allowed_sample_counts: SampleCounts::SAMPLE_1, - }); - } - - // VUID-VkCopyBufferToImageInfo2-dstImageLayout-01396 - if !matches!( - dst_image_layout, - ImageLayout::TransferDstOptimal | ImageLayout::General - ) { - return Err(CopyError::ImageLayoutInvalid { - resource: CopyErrorResource::Destination, - image_layout: dst_image_layout, - }); - } - - let extent_alignment = match queue_family_properties.min_image_transfer_granularity { - [0, 0, 0] => None, - min_image_transfer_granularity => { - let granularity = move |block_extent: [u32; 3], is_multi_plane: bool| { - if is_multi_plane { - // Assume planes always have 1x1 blocks - min_image_transfer_granularity - } else { - // "The value returned in minImageTransferGranularity has a unit of - // compressed texel blocks for images having a block-compressed format, and - // a unit of texels otherwise." - [ - min_image_transfer_granularity[0] * block_extent[0], - min_image_transfer_granularity[1] * block_extent[1], - min_image_transfer_granularity[2] * block_extent[2], - ] - } - }; - - Some(granularity( - dst_image.format().block_extent(), - image_aspects.intersects(ImageAspects::PLANE_0), - )) - } - }; - - if image_aspects.intersects(ImageAspects::PLANE_0) { - // VUID-VkCopyBufferToImageInfo2-aspectMask-01560 - image_aspects -= ImageAspects::COLOR; - } - - for (region_index, region) in regions.iter().enumerate() { - let &BufferImageCopy { - buffer_offset, - buffer_row_length, - buffer_image_height, - ref image_subresource, - image_offset, - image_extent, - _ne: _, - } = region; - - // VUID-VkCopyBufferToImageInfo2-imageSubresource-01701 - if image_subresource.mip_level >= dst_image.mip_levels() { - return Err(CopyError::MipLevelsOutOfRange { - resource: CopyErrorResource::Destination, - region_index, - mip_levels_range_end: image_subresource.mip_level + 1, - image_mip_levels: dst_image.mip_levels(), - }); - } - - // VUID-VkImageSubresourceLayers-layerCount-01700 - // VUID-VkCopyBufferToImageInfo2-baseArrayLayer-00213 - assert!(!image_subresource.array_layers.is_empty()); - - // VUID-VkCopyBufferToImageInfo2-imageSubresource-01702 - // VUID-VkCopyBufferToImageInfo2-baseArrayLayer-00213 - if image_subresource.array_layers.end > dst_image.array_layers() { - return Err(CopyError::ArrayLayersOutOfRange { - resource: CopyErrorResource::Destination, - region_index, - array_layers_range_end: image_subresource.array_layers.end, - image_array_layers: dst_image.array_layers(), - }); - } - - // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask - assert!(!image_subresource.aspects.is_empty()); - - // VUID-VkCopyBufferToImageInfo2-aspectMask-00211 - if !image_aspects.contains(image_subresource.aspects) { - return Err(CopyError::AspectsNotAllowed { - resource: CopyErrorResource::Destination, - region_index, - aspects: image_subresource.aspects, - allowed_aspects: image_aspects, - }); - } - - // VUID-VkBufferImageCopy2-aspectMask-00212 - // VUID-VkCopyBufferToImageInfo2-aspectMask-01560 - if image_subresource.aspects.count() != 1 { - return Err(CopyError::MultipleAspectsNotAllowed { - resource: CopyErrorResource::Destination, - region_index, - aspects: image_subresource.aspects, - }); - } - - let (image_subresource_format, image_subresource_extent) = - if image_aspects.intersects(ImageAspects::PLANE_0) { - if image_subresource.aspects.intersects(ImageAspects::PLANE_0) { - (dst_image.format().planes()[0], dst_image.extent()) - } else if image_subresource.aspects.intersects(ImageAspects::PLANE_1) { - ( - dst_image.format().planes()[1], - dst_image - .format() - .ycbcr_chroma_sampling() - .unwrap() - .subsampled_extent(dst_image.extent()), - ) - } else { - ( - dst_image.format().planes()[2], - dst_image - .format() - .ycbcr_chroma_sampling() - .unwrap() - .subsampled_extent(dst_image.extent()), - ) - } - } else { - ( - dst_image.format(), - mip_level_extent(dst_image.extent(), image_subresource.mip_level).unwrap(), - ) - }; - - if let Some(extent_alignment) = extent_alignment { - for i in 0..3 { - // VUID-VkBufferImageCopy2-imageExtent-06659 - // VUID-VkBufferImageCopy2-imageExtent-06660 - // VUID-VkBufferImageCopy2-imageExtent-06661 - assert!(image_extent[i] != 0); - - // VUID-VkCopyBufferToImageInfo2-pRegions-06223 - // VUID-VkCopyBufferToImageInfo2-pRegions-06224 - // VUID-VkCopyBufferToImageInfo2-imageOffset-00200 - if image_offset[i] + image_extent[i] > image_subresource_extent[i] { - return Err(CopyError::RegionOutOfImageBounds { - resource: CopyErrorResource::Destination, - region_index, - offset_range_end: [ - image_offset[0] + image_extent[0], - image_offset[1] + image_extent[1], - image_offset[2] + image_extent[2], - ], - subresource_extent: image_subresource_extent, - }); - } - - // VUID-VkCopyBufferToImageInfo2-imageOffset-01793 - // VUID-VkCopyBufferToImageInfo2-imageOffset-00205 - if image_offset[i] % extent_alignment[i] != 0 { - return Err(CopyError::OffsetNotAlignedForImage { - resource: CopyErrorResource::Destination, - region_index, - offset: image_offset, - required_alignment: extent_alignment, - }); - } - - // VUID-VkCopyBufferToImageInfo2-imageOffset-01793 - // VUID-VkCopyBufferToImageInfo2-imageExtent-00207 - // VUID-VkCopyBufferToImageInfo2-imageExtent-00208 - // VUID-VkCopyBufferToImageInfo2-imageExtent-00209 - if image_offset[i] + image_extent[i] != image_subresource_extent[i] - && image_extent[i] % extent_alignment[i] != 0 - { - return Err(CopyError::ExtentNotAlignedForImage { - resource: CopyErrorResource::Destination, - region_index, - extent: image_extent, - required_alignment: extent_alignment, - }); - } - } - } else { - // If granularity is `None`, then we can only copy whole subresources. - - // VUID-VkCopyBufferToImageInfo2-imageOffset-01793 - if image_offset != [0, 0, 0] { - return Err(CopyError::OffsetNotAlignedForImage { - resource: CopyErrorResource::Destination, - region_index, - offset: image_offset, - required_alignment: image_subresource_extent, - }); - } - - // VUID-VkCopyBufferToImageInfo2-imageOffset-01793 - if image_extent != image_subresource_extent { - return Err(CopyError::ExtentNotAlignedForImage { - resource: CopyErrorResource::Destination, - region_index, - extent: image_extent, - required_alignment: image_subresource_extent, - }); - } - } - - // VUID-VkBufferImageCopy2-bufferRowLength-00195 - if !(buffer_row_length == 0 || buffer_row_length >= image_extent[0]) { - return Err(CopyError::BufferRowLengthTooSmall { - resource: CopyErrorResource::Source, - region_index, - row_length: buffer_row_length, - min: image_extent[0], - }); - } - - // VUID-VkBufferImageCopy2-bufferImageHeight-00196 - if !(buffer_image_height == 0 || buffer_image_height >= image_extent[1]) { - return Err(CopyError::BufferImageHeightTooSmall { - resource: CopyErrorResource::Source, - region_index, - image_height: buffer_image_height, - min: image_extent[1], - }); - } - - let image_subresource_block_extent = image_subresource_format.block_extent(); - - // VUID-VkCopyBufferToImageInfo2-bufferRowLength-00203 - if buffer_row_length % image_subresource_block_extent[0] != 0 { - return Err(CopyError::BufferRowLengthNotAligned { - resource: CopyErrorResource::Source, - region_index, - row_length: buffer_row_length, - required_alignment: image_subresource_block_extent[0], - }); - } - - // VUID-VkCopyBufferToImageInfo2-bufferImageHeight-00204 - if buffer_image_height % image_subresource_block_extent[1] != 0 { - return Err(CopyError::BufferImageHeightNotAligned { - resource: CopyErrorResource::Source, - region_index, - image_height: buffer_image_height, - required_alignment: image_subresource_block_extent[1], - }); - } - - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBufferImageCopy.html#_description - let image_subresource_block_size = - if image_subresource.aspects.intersects(ImageAspects::STENCIL) { - 1 - } else if image_subresource.aspects.intersects(ImageAspects::DEPTH) { - match image_subresource_format { - Format::D16_UNORM | Format::D16_UNORM_S8_UINT => 2, - Format::D32_SFLOAT - | Format::D32_SFLOAT_S8_UINT - | Format::X8_D24_UNORM_PACK32 - | Format::D24_UNORM_S8_UINT => 4, - _ => unreachable!(), - } - } else { - image_subresource_format.block_size() - }; - - // VUID-VkCopyBufferToImageInfo2-pRegions-04725 - // VUID-VkCopyBufferToImageInfo2-pRegions-04726 - if (buffer_row_length / image_subresource_block_extent[0]) as DeviceSize - * image_subresource_block_size - > 0x7FFFFFFF - { - return Err(CopyError::BufferRowLengthTooLarge { - resource: CopyErrorResource::Source, - region_index, - buffer_row_length, - }); - } - - let buffer_offset_alignment = - if image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { - 4 - } else { - let mut buffer_offset_alignment = image_subresource_block_size; - - // VUID-VkCopyBufferToImageInfo2-commandBuffer-04052 - // Make the alignment a multiple of 4. - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - if buffer_offset_alignment % 2 != 0 { - buffer_offset_alignment *= 2; - } - - if buffer_offset_alignment % 4 != 0 { - buffer_offset_alignment *= 2; - } - } - - buffer_offset_alignment - }; - - // VUID-VkCopyBufferToImageInfo2-bufferOffset-00206 - // VUID-VkCopyBufferToImageInfo2-bufferOffset-01558 - // VUID-VkCopyBufferToImageInfo2-bufferOffset-01559 - // VUID-VkCopyBufferToImageInfo2-srcImage-04053 - if (src_buffer.offset() + buffer_offset) % buffer_offset_alignment != 0 { - return Err(CopyError::OffsetNotAlignedForBuffer { - resource: CopyErrorResource::Source, - region_index, - offset: src_buffer.offset() + buffer_offset, - required_alignment: buffer_offset_alignment, - }); - } - - let buffer_copy_size = region.buffer_copy_size(image_subresource_format); - - // VUID-VkCopyBufferToImageInfo2-pRegions-00171 - if buffer_offset + buffer_copy_size > src_buffer.size() { - return Err(CopyError::RegionOutOfBufferBounds { - resource: CopyErrorResource::Source, - region_index, - offset_range_end: buffer_offset + buffer_copy_size, - buffer_size: src_buffer.size(), - }); - } - } - - // VUID-VkCopyBufferToImageInfo2-pRegions-00173 - // Can't occur as long as memory aliasing isn't allowed. - Ok(()) } @@ -1466,7 +303,7 @@ where }) .collect(), move |out: &mut UnsafeCommandBufferBuilder| { - out.copy_buffer_to_image(©_buffer_to_image_info); + out.copy_buffer_to_image_unchecked(©_buffer_to_image_info); }, ); @@ -1477,423 +314,27 @@ where pub fn copy_image_to_buffer( &mut self, copy_image_to_buffer_info: CopyImageToBufferInfo, - ) -> Result<&mut Self, CopyError> { + ) -> Result<&mut Self, Box> { self.validate_copy_image_to_buffer(©_image_to_buffer_info)?; - unsafe { - self.copy_image_to_buffer_unchecked(copy_image_to_buffer_info); - } - - Ok(self) + unsafe { Ok(self.copy_image_to_buffer_unchecked(copy_image_to_buffer_info)) } } fn validate_copy_image_to_buffer( &self, copy_image_to_buffer_info: &CopyImageToBufferInfo, - ) -> Result<(), CopyError> { - let device = self.device(); + ) -> Result<(), Box> { + self.inner + .validate_copy_image_to_buffer(copy_image_to_buffer_info)?; - // VUID-vkCmdCopyImageToBuffer2-renderpass if self.builder_state.render_pass.is_some() { - return Err(CopyError::ForbiddenInsideRenderPass); + return Err(Box::new(ValidationError { + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdCopyImageToBuffer2-renderpass"], + ..Default::default() + })); } - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdCopyImageToBuffer2-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(CopyError::NotSupportedByQueueFamily); - } - - let &CopyImageToBufferInfo { - ref src_image, - src_image_layout, - ref dst_buffer, - ref regions, - _ne: _, - } = copy_image_to_buffer_info; - - // VUID-VkCopyImageToBufferInfo2-srcImageLayout-parameter - src_image_layout.validate_device(device)?; - - // VUID-VkCopyImageToBufferInfo2-commonparent - assert_eq!(device, dst_buffer.device()); - assert_eq!(device, src_image.device()); - - let mut image_aspects = src_image.format().aspects(); - - // VUID-VkCopyImageToBufferInfo2-srcImage-00186 - if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) { - return Err(CopyError::MissingUsage { - resource: CopyErrorResource::Source, - usage: "transfer_src", - }); - } - - // VUID-VkCopyImageToBufferInfo2-dstBuffer-00191 - if !dst_buffer - .buffer() - .usage() - .intersects(BufferUsage::TRANSFER_DST) - { - return Err(CopyError::MissingUsage { - resource: CopyErrorResource::Destination, - usage: "transfer_dst", - }); - } - - if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { - // VUID-VkCopyImageToBufferInfo2-srcImage-01998 - if !src_image - .format_features() - .intersects(FormatFeatures::TRANSFER_SRC) - { - return Err(CopyError::MissingFormatFeature { - resource: CopyErrorResource::Source, - format_feature: "transfer_src", - }); - } - } - - // VUID-VkCopyImageToBufferInfo2-srcImage-00188 - if src_image.samples() != SampleCount::Sample1 { - return Err(CopyError::SampleCountInvalid { - resource: CopyErrorResource::Source, - sample_count: src_image.samples(), - allowed_sample_counts: SampleCounts::SAMPLE_1, - }); - } - - // VUID-VkCopyImageToBufferInfo2-srcImageLayout-01397 - if !matches!( - src_image_layout, - ImageLayout::TransferSrcOptimal | ImageLayout::General - ) { - return Err(CopyError::ImageLayoutInvalid { - resource: CopyErrorResource::Source, - image_layout: src_image_layout, - }); - } - - let extent_alignment = match queue_family_properties.min_image_transfer_granularity { - [0, 0, 0] => None, - min_image_transfer_granularity => { - let granularity = move |block_extent: [u32; 3], is_multi_plane: bool| { - if is_multi_plane { - // Assume planes always have 1x1 blocks - min_image_transfer_granularity - } else { - // "The value returned in minImageTransferGranularity has a unit of - // compressed texel blocks for images having a block-compressed format, and - // a unit of texels otherwise." - [ - min_image_transfer_granularity[0] * block_extent[0], - min_image_transfer_granularity[1] * block_extent[1], - min_image_transfer_granularity[2] * block_extent[2], - ] - } - }; - - Some(granularity( - src_image.format().block_extent(), - image_aspects.intersects(ImageAspects::PLANE_0), - )) - } - }; - - if image_aspects.intersects(ImageAspects::PLANE_0) { - // VUID-VkCopyImageToBufferInfo2-aspectMask-01560 - image_aspects -= ImageAspects::COLOR; - } - - for (region_index, region) in regions.iter().enumerate() { - let &BufferImageCopy { - buffer_offset, - buffer_row_length, - buffer_image_height, - ref image_subresource, - image_offset, - image_extent, - _ne: _, - } = region; - - // VUID-VkCopyImageToBufferInfo2-imageSubresource-01703 - if image_subresource.mip_level >= src_image.mip_levels() { - return Err(CopyError::MipLevelsOutOfRange { - resource: CopyErrorResource::Source, - region_index, - mip_levels_range_end: image_subresource.mip_level + 1, - image_mip_levels: src_image.mip_levels(), - }); - } - - // VUID-VkImageSubresourceLayers-layerCount-01700 - assert!(!image_subresource.array_layers.is_empty()); - - // VUID-VkCopyImageToBufferInfo2-imageSubresource-01704 - // VUID-VkCopyImageToBufferInfo2-baseArrayLayer-00213 - if image_subresource.array_layers.end > src_image.array_layers() { - return Err(CopyError::ArrayLayersOutOfRange { - resource: CopyErrorResource::Source, - region_index, - array_layers_range_end: image_subresource.array_layers.end, - image_array_layers: src_image.array_layers(), - }); - } - - // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask - assert!(!image_subresource.aspects.is_empty()); - - // VUID-VkCopyImageToBufferInfo2-aspectMask-00211 - if !image_aspects.contains(image_subresource.aspects) { - return Err(CopyError::AspectsNotAllowed { - resource: CopyErrorResource::Source, - region_index, - aspects: image_subresource.aspects, - allowed_aspects: image_aspects, - }); - } - - // VUID-VkBufferImageCopy2-aspectMask-00212 - if image_subresource.aspects.count() != 1 { - return Err(CopyError::MultipleAspectsNotAllowed { - resource: CopyErrorResource::Source, - region_index, - aspects: image_subresource.aspects, - }); - } - - let (image_subresource_format, image_subresource_extent) = - if image_aspects.intersects(ImageAspects::PLANE_0) { - if image_subresource.aspects.intersects(ImageAspects::PLANE_0) { - (src_image.format().planes()[0], src_image.extent()) - } else if image_subresource.aspects.intersects(ImageAspects::PLANE_1) { - ( - src_image.format().planes()[1], - src_image - .format() - .ycbcr_chroma_sampling() - .unwrap() - .subsampled_extent(src_image.extent()), - ) - } else { - ( - src_image.format().planes()[2], - src_image - .format() - .ycbcr_chroma_sampling() - .unwrap() - .subsampled_extent(src_image.extent()), - ) - } - } else { - ( - src_image.format(), - mip_level_extent(src_image.extent(), image_subresource.mip_level).unwrap(), - ) - }; - - if let Some(extent_alignment) = extent_alignment { - for i in 0..3 { - // VUID-VkBufferImageCopy2-imageExtent-06659 - // VUID-VkBufferImageCopy2-imageExtent-06660 - // VUID-VkBufferImageCopy2-imageExtent-06661 - assert!(image_extent[i] != 0); - - // VUID-VkCopyImageToBufferInfo2-imageOffset-00197 - // VUID-VkCopyImageToBufferInfo2-imageOffset-00198 - // VUID-VkCopyImageToBufferInfo2-imageOffset-00200 - if image_offset[i] + image_extent[i] > image_subresource_extent[i] { - return Err(CopyError::RegionOutOfImageBounds { - resource: CopyErrorResource::Source, - region_index, - offset_range_end: [ - image_offset[0] + image_extent[0], - image_offset[1] + image_extent[1], - image_offset[2] + image_extent[2], - ], - subresource_extent: image_subresource_extent, - }); - } - - // VUID-VkCopyImageToBufferInfo2-imageOffset-01794 - // VUID-VkCopyImageToBufferInfo2-imageOffset-00205 - if image_offset[i] % extent_alignment[i] != 0 { - return Err(CopyError::OffsetNotAlignedForImage { - resource: CopyErrorResource::Source, - region_index, - offset: image_offset, - required_alignment: extent_alignment, - }); - } - - // VUID-VkCopyImageToBufferInfo2-imageOffset-01794 - // VUID-VkCopyImageToBufferInfo2-imageExtent-00207 - // VUID-VkCopyImageToBufferInfo2-imageExtent-00208 - // VUID-VkCopyImageToBufferInfo2-imageExtent-00209 - if image_offset[i] + image_extent[i] != image_subresource_extent[i] - && image_extent[i] % extent_alignment[i] != 0 - { - return Err(CopyError::ExtentNotAlignedForImage { - resource: CopyErrorResource::Source, - region_index, - extent: image_extent, - required_alignment: extent_alignment, - }); - } - } - } else { - // If granularity is `None`, then we can only copy whole subresources. - - // VUID-VkCopyBufferToImageInfo2-imageOffset-01793 - if image_offset != [0, 0, 0] { - return Err(CopyError::OffsetNotAlignedForImage { - resource: CopyErrorResource::Source, - region_index, - offset: image_offset, - required_alignment: image_subresource_extent, - }); - } - - // VUID-VkCopyBufferToImageInfo2-imageOffset-01793 - if image_extent != image_subresource_extent { - return Err(CopyError::ExtentNotAlignedForImage { - resource: CopyErrorResource::Source, - region_index, - extent: image_extent, - required_alignment: image_subresource_extent, - }); - } - } - - // VUID-VkBufferImageCopy2-bufferRowLength-00195 - if !(buffer_row_length == 0 || buffer_row_length >= image_extent[0]) { - return Err(CopyError::BufferRowLengthTooSmall { - resource: CopyErrorResource::Destination, - region_index, - row_length: buffer_row_length, - min: image_extent[0], - }); - } - - // VUID-VkBufferImageCopy2-bufferImageHeight-00196 - if !(buffer_image_height == 0 || buffer_image_height >= image_extent[1]) { - return Err(CopyError::BufferImageHeightTooSmall { - resource: CopyErrorResource::Destination, - region_index, - image_height: buffer_image_height, - min: image_extent[1], - }); - } - - let image_subresource_block_extent = image_subresource_format.block_extent(); - - // VUID-VkCopyImageToBufferInfo2-bufferRowLength-00203 - if buffer_row_length % image_subresource_block_extent[0] != 0 { - return Err(CopyError::BufferRowLengthNotAligned { - resource: CopyErrorResource::Destination, - region_index, - row_length: buffer_row_length, - required_alignment: image_subresource_block_extent[0], - }); - } - - // VUID-VkCopyImageToBufferInfo2-bufferImageHeight-00204 - if buffer_image_height % image_subresource_block_extent[1] != 0 { - return Err(CopyError::BufferImageHeightNotAligned { - resource: CopyErrorResource::Destination, - region_index, - image_height: buffer_image_height, - required_alignment: image_subresource_block_extent[1], - }); - } - - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBufferImageCopy.html#_description - let image_subresource_block_size = - if image_subresource.aspects.intersects(ImageAspects::STENCIL) { - 1 - } else if image_subresource.aspects.intersects(ImageAspects::DEPTH) { - match image_subresource_format { - Format::D16_UNORM | Format::D16_UNORM_S8_UINT => 2, - Format::D32_SFLOAT - | Format::D32_SFLOAT_S8_UINT - | Format::X8_D24_UNORM_PACK32 - | Format::D24_UNORM_S8_UINT => 4, - _ => unreachable!(), - } - } else { - image_subresource_format.block_size() - }; - - // VUID-VkCopyImageToBufferInfo2-pRegions-04725 - // VUID-VkCopyImageToBufferInfo2-pRegions-04726 - if (buffer_row_length / image_subresource_block_extent[0]) as DeviceSize - * image_subresource_block_size - > 0x7FFFFFFF - { - return Err(CopyError::BufferRowLengthTooLarge { - resource: CopyErrorResource::Destination, - region_index, - buffer_row_length, - }); - } - - let buffer_offset_alignment = - if image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { - 4 - } else { - let mut buffer_offset_alignment = image_subresource_block_size; - - // VUID-VkCopyImageToBufferInfo2-commandBuffer-04052 - // Make the alignment a multiple of 4. - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - if buffer_offset_alignment % 2 != 0 { - buffer_offset_alignment *= 2; - } - - if buffer_offset_alignment % 4 != 0 { - buffer_offset_alignment *= 2; - } - } - - buffer_offset_alignment - }; - - // VUID-VkCopyImageToBufferInfo2-bufferOffset-01558 - // VUID-VkCopyImageToBufferInfo2-bufferOffset-01559 - // VUID-VkCopyImageToBufferInfo2-bufferOffset-00206 - // VUID-VkCopyImageToBufferInfo2-srcImage-04053 - if (dst_buffer.offset() + buffer_offset) % buffer_offset_alignment != 0 { - return Err(CopyError::OffsetNotAlignedForBuffer { - resource: CopyErrorResource::Destination, - region_index, - offset: dst_buffer.offset() + buffer_offset, - required_alignment: buffer_offset_alignment, - }); - } - - let buffer_copy_size = region.buffer_copy_size(image_subresource_format); - - // VUID-VkCopyImageToBufferInfo2-pRegions-00183 - if buffer_offset + buffer_copy_size > dst_buffer.size() { - return Err(CopyError::RegionOutOfBufferBounds { - resource: CopyErrorResource::Destination, - region_index, - offset_range_end: buffer_offset + buffer_copy_size, - buffer_size: dst_buffer.size(), - }); - } - } - - // VUID-VkCopyImageToBufferInfo2-pRegions-00184 - // Can't occur as long as memory aliasing isn't allowed. - Ok(()) } @@ -1949,7 +390,7 @@ where }) .collect(), move |out: &mut UnsafeCommandBufferBuilder| { - out.copy_image_to_buffer(©_image_to_buffer_info); + out.copy_image_to_buffer_unchecked(©_image_to_buffer_info); }, ); @@ -1986,496 +427,27 @@ where /// # Panics /// /// - Panics if the source or the destination was not created with `device`. - pub fn blit_image(&mut self, blit_image_info: BlitImageInfo) -> Result<&mut Self, CopyError> { + pub fn blit_image( + &mut self, + blit_image_info: BlitImageInfo, + ) -> Result<&mut Self, Box> { self.validate_blit_image(&blit_image_info)?; - unsafe { - self.blit_image_unchecked(blit_image_info); - } - - Ok(self) + unsafe { Ok(self.blit_image_unchecked(blit_image_info)) } } - fn validate_blit_image(&self, blit_image_info: &BlitImageInfo) -> Result<(), CopyError> { - let device = self.device(); + fn validate_blit_image( + &self, + blit_image_info: &BlitImageInfo, + ) -> Result<(), Box> { + self.inner.validate_blit_image(blit_image_info)?; - // VUID-vkCmdBlitImage2-renderpass if self.builder_state.render_pass.is_some() { - return Err(CopyError::ForbiddenInsideRenderPass); - } - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdBlitImage2-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(CopyError::NotSupportedByQueueFamily); - } - - let &BlitImageInfo { - ref src_image, - src_image_layout, - ref dst_image, - dst_image_layout, - ref regions, - filter, - _ne: _, - } = blit_image_info; - - // VUID-VkBlitImageInfo2-srcImageLayout-parameter - src_image_layout.validate_device(device)?; - - // VUID-VkBlitImageInfo2-dstImageLayout-parameter - dst_image_layout.validate_device(device)?; - - // VUID-VkBlitImageInfo2-filter-parameter - filter.validate_device(device)?; - - // VUID-VkBlitImageInfo2-commonparent - assert_eq!(device, src_image.device()); - assert_eq!(device, dst_image.device()); - - let src_image_aspects = src_image.format().aspects(); - let dst_image_aspects = dst_image.format().aspects(); - let src_image_type = src_image.image_type(); - let dst_image_type = dst_image.image_type(); - - // VUID-VkBlitImageInfo2-srcImage-00219 - if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) { - return Err(CopyError::MissingUsage { - resource: CopyErrorResource::Source, - usage: "transfer_src", - }); - } - - // VUID-VkBlitImageInfo2-dstImage-00224 - if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) { - return Err(CopyError::MissingUsage { - resource: CopyErrorResource::Destination, - usage: "transfer_dst", - }); - } - - // VUID-VkBlitImageInfo2-srcImage-01999 - if !src_image - .format_features() - .intersects(FormatFeatures::BLIT_SRC) - { - return Err(CopyError::MissingFormatFeature { - resource: CopyErrorResource::Source, - format_feature: "blit_src", - }); - } - - // VUID-VkBlitImageInfo2-dstImage-02000 - if !dst_image - .format_features() - .intersects(FormatFeatures::BLIT_DST) - { - return Err(CopyError::MissingFormatFeature { - resource: CopyErrorResource::Destination, - format_feature: "blit_dst", - }); - } - - // VUID-VkBlitImageInfo2-srcImage-06421 - if src_image.format().ycbcr_chroma_sampling().is_some() { - return Err(CopyError::FormatNotSupported { - resource: CopyErrorResource::Source, - format: src_image.format(), - }); - } - - // VUID-VkBlitImageInfo2-dstImage-06422 - if dst_image.format().ycbcr_chroma_sampling().is_some() { - return Err(CopyError::FormatNotSupported { - resource: CopyErrorResource::Destination, - format: src_image.format(), - }); - } - - if !(src_image_aspects.intersects(ImageAspects::COLOR) - && dst_image_aspects.intersects(ImageAspects::COLOR)) - { - // VUID-VkBlitImageInfo2-srcImage-00231 - if src_image.format() != dst_image.format() { - return Err(CopyError::FormatsMismatch { - src_format: src_image.format(), - dst_format: dst_image.format(), - }); - } - } else { - // VUID-VkBlitImageInfo2-srcImage-00229 - // VUID-VkBlitImageInfo2-srcImage-00230 - if !matches!( - ( - src_image.format().type_color().unwrap(), - dst_image.format().type_color().unwrap(), - ), - ( - NumericType::SFLOAT - | NumericType::UFLOAT - | NumericType::SNORM - | NumericType::UNORM - | NumericType::SSCALED - | NumericType::USCALED - | NumericType::SRGB, - NumericType::SFLOAT - | NumericType::UFLOAT - | NumericType::SNORM - | NumericType::UNORM - | NumericType::SSCALED - | NumericType::USCALED - | NumericType::SRGB, - ) | (NumericType::SINT, NumericType::SINT) - | (NumericType::UINT, NumericType::UINT) - ) { - return Err(CopyError::FormatsNotCompatible { - src_format: src_image.format(), - dst_format: dst_image.format(), - }); - } - } - - // VUID-VkBlitImageInfo2-srcImage-00233 - if src_image.samples() != SampleCount::Sample1 { - return Err(CopyError::SampleCountInvalid { - resource: CopyErrorResource::Destination, - sample_count: dst_image.samples(), - allowed_sample_counts: SampleCounts::SAMPLE_1, - }); - } - - // VUID-VkBlitImageInfo2-dstImage-00234 - if dst_image.samples() != SampleCount::Sample1 { - return Err(CopyError::SampleCountInvalid { - resource: CopyErrorResource::Destination, - sample_count: dst_image.samples(), - allowed_sample_counts: SampleCounts::SAMPLE_1, - }); - } - - // VUID-VkBlitImageInfo2-srcImageLayout-01398 - if !matches!( - src_image_layout, - ImageLayout::TransferSrcOptimal | ImageLayout::General - ) { - return Err(CopyError::ImageLayoutInvalid { - resource: CopyErrorResource::Source, - image_layout: src_image_layout, - }); - } - - // VUID-VkBlitImageInfo2-dstImageLayout-01399 - if !matches!( - dst_image_layout, - ImageLayout::TransferDstOptimal | ImageLayout::General - ) { - return Err(CopyError::ImageLayoutInvalid { - resource: CopyErrorResource::Destination, - image_layout: dst_image_layout, - }); - } - - // VUID-VkBlitImageInfo2-srcImage-00232 - if !src_image_aspects.intersects(ImageAspects::COLOR) && filter != Filter::Nearest { - return Err(CopyError::FilterNotSupportedByFormat); - } - - match filter { - Filter::Nearest => (), - Filter::Linear => { - // VUID-VkBlitImageInfo2-filter-02001 - if !src_image - .format_features() - .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_LINEAR) - { - return Err(CopyError::FilterNotSupportedByFormat); - } - } - Filter::Cubic => { - // VUID-VkBlitImageInfo2-filter-02002 - if !src_image - .format_features() - .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_CUBIC) - { - return Err(CopyError::FilterNotSupportedByFormat); - } - - // VUID-VkBlitImageInfo2-filter-00237 - if !matches!(src_image.image_type(), ImageType::Dim2d) { - return Err(CopyError::FilterNotSupportedForImageType); - } - } - } - - let is_same_image = src_image == dst_image; - let mut overlap_subresource_indices = None; - let mut overlap_extent_indices = None; - - for (region_index, region) in regions.iter().enumerate() { - let &ImageBlit { - ref src_subresource, - src_offsets, - ref dst_subresource, - dst_offsets, - _ne: _, - } = region; - - let check_subresource = |resource: CopyErrorResource, - image: &Image, - image_aspects: ImageAspects, - subresource: &ImageSubresourceLayers| - -> Result<_, CopyError> { - // VUID-VkBlitImageInfo2-srcSubresource-01705 - // VUID-VkBlitImageInfo2-dstSubresource-01706 - if subresource.mip_level >= image.mip_levels() { - return Err(CopyError::MipLevelsOutOfRange { - resource, - region_index, - mip_levels_range_end: subresource.mip_level + 1, - image_mip_levels: image.mip_levels(), - }); - } - - // VUID-VkImageSubresourceLayers-layerCount-01700 - assert!(!subresource.array_layers.is_empty()); - - // VUID-VkBlitImageInfo2-srcSubresource-01707 - // VUID-VkBlitImageInfo2-dstSubresource-01708 - // VUID-VkBlitImageInfo2-srcImage-00240 - if subresource.array_layers.end > image.array_layers() { - return Err(CopyError::ArrayLayersOutOfRange { - resource, - region_index, - array_layers_range_end: subresource.array_layers.end, - image_array_layers: image.array_layers(), - }); - } - - // VUID-VkImageSubresourceLayers-aspectMask-parameter - subresource.aspects.validate_device(device)?; - - // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask - assert!(!subresource.aspects.is_empty()); - - // VUID-VkBlitImageInfo2-aspectMask-00241 - // VUID-VkBlitImageInfo2-aspectMask-00242 - if !image_aspects.contains(subresource.aspects) { - return Err(CopyError::AspectsNotAllowed { - resource, - region_index, - aspects: subresource.aspects, - allowed_aspects: image_aspects, - }); - } - - Ok(mip_level_extent(image.extent(), subresource.mip_level).unwrap()) - }; - - let src_subresource_extent = check_subresource( - CopyErrorResource::Source, - src_image, - src_image_aspects, - src_subresource, - )?; - let dst_subresource_extent = check_subresource( - CopyErrorResource::Destination, - dst_image, - dst_image_aspects, - dst_subresource, - )?; - - // VUID-VkImageBlit2-aspectMask-00238 - if src_subresource.aspects != dst_subresource.aspects { - return Err(CopyError::AspectsMismatch { - region_index, - src_aspects: src_subresource.aspects, - dst_aspects: dst_subresource.aspects, - }); - } - - let src_layer_count = - src_subresource.array_layers.end - src_subresource.array_layers.start; - let dst_layer_count = - dst_subresource.array_layers.end - dst_subresource.array_layers.start; - - // VUID-VkImageBlit2-layerCount-00239 - // VUID-VkBlitImageInfo2-srcImage-00240 - if src_layer_count != dst_layer_count { - return Err(CopyError::ArrayLayerCountMismatch { - region_index, - src_layer_count, - dst_layer_count, - }); - } - - let check_offset_extent = |resource: CopyErrorResource, - image_type: ImageType, - subresource_extent: [u32; 3], - offsets: [[u32; 3]; 2]| - -> Result<_, CopyError> { - match image_type { - ImageType::Dim1d => { - // VUID-VkBlitImageInfo2-srcImage-00245 - // VUID-VkBlitImageInfo2-dstImage-00250 - if !(offsets[0][1] == 0 && offsets[1][1] == 1) { - return Err(CopyError::OffsetsInvalidForImageType { - resource, - region_index, - offsets: [offsets[0][1], offsets[1][1]], - }); - } - - // VUID-VkBlitImageInfo2-srcImage-00247 - // VUID-VkBlitImageInfo2-dstImage-00252 - if !(offsets[0][2] == 0 && offsets[1][2] == 1) { - return Err(CopyError::OffsetsInvalidForImageType { - resource, - region_index, - offsets: [offsets[0][2], offsets[1][2]], - }); - } - } - ImageType::Dim2d => { - // VUID-VkBlitImageInfo2-srcImage-00247 - // VUID-VkBlitImageInfo2-dstImage-00252 - if !(offsets[0][2] == 0 && offsets[1][2] == 1) { - return Err(CopyError::OffsetsInvalidForImageType { - resource, - region_index, - offsets: [offsets[0][2], offsets[1][2]], - }); - } - } - ImageType::Dim3d => (), - } - - let offset_range_end = [ - max(offsets[0][0], offsets[1][0]), - max(offsets[0][1], offsets[1][1]), - max(offsets[0][2], offsets[1][2]), - ]; - - for i in 0..3 { - // VUID-VkBlitImageInfo2-srcOffset-00243 - // VUID-VkBlitImageInfo2-srcOffset-00244 - // VUID-VkBlitImageInfo2-srcOffset-00246 - // VUID-VkBlitImageInfo2-dstOffset-00248 - // VUID-VkBlitImageInfo2-dstOffset-00249 - // VUID-VkBlitImageInfo2-dstOffset-00251 - if offset_range_end[i] > subresource_extent[i] { - return Err(CopyError::RegionOutOfImageBounds { - resource, - region_index, - offset_range_end, - subresource_extent, - }); - } - } - - Ok(()) - }; - - check_offset_extent( - CopyErrorResource::Source, - src_image_type, - src_subresource_extent, - src_offsets, - )?; - check_offset_extent( - CopyErrorResource::Destination, - dst_image_type, - dst_subresource_extent, - dst_offsets, - )?; - - // VUID-VkBlitImageInfo2-pRegions-00217 - if is_same_image { - let src_region_index = region_index; - let src_subresource_axes = [ - src_subresource.mip_level..src_subresource.mip_level + 1, - src_subresource.array_layers.start..src_subresource.array_layers.end, - ]; - let src_extent_axes = [ - min(src_offsets[0][0], src_offsets[1][0]) - ..max(src_offsets[0][0], src_offsets[1][0]), - min(src_offsets[0][1], src_offsets[1][1]) - ..max(src_offsets[0][1], src_offsets[1][1]), - min(src_offsets[0][2], src_offsets[1][2]) - ..max(src_offsets[0][2], src_offsets[1][2]), - ]; - - for (dst_region_index, dst_region) in regions.iter().enumerate() { - let &ImageBlit { - ref dst_subresource, - dst_offsets, - .. - } = dst_region; - - let dst_subresource_axes = [ - dst_subresource.mip_level..dst_subresource.mip_level + 1, - src_subresource.array_layers.start..src_subresource.array_layers.end, - ]; - - if src_subresource_axes.iter().zip(dst_subresource_axes).any( - |(src_range, dst_range)| { - src_range.start >= dst_range.end || dst_range.start >= src_range.end - }, - ) { - continue; - } - - // If the subresource axes all overlap, then the source and destination must - // have the same layout. - overlap_subresource_indices = Some((src_region_index, dst_region_index)); - - let dst_extent_axes = [ - min(dst_offsets[0][0], dst_offsets[1][0]) - ..max(dst_offsets[0][0], dst_offsets[1][0]), - min(dst_offsets[0][1], dst_offsets[1][1]) - ..max(dst_offsets[0][1], dst_offsets[1][1]), - min(dst_offsets[0][2], dst_offsets[1][2]) - ..max(dst_offsets[0][2], dst_offsets[1][2]), - ]; - - if src_extent_axes - .iter() - .zip(dst_extent_axes) - .any(|(src_range, dst_range)| { - src_range.start >= dst_range.end || dst_range.start >= src_range.end - }) - { - continue; - } - - // If the extent axes *also* overlap, then that's an error. - overlap_extent_indices = Some((src_region_index, dst_region_index)); - } - } - } - - // VUID-VkBlitImageInfo2-pRegions-00217 - if let Some((src_region_index, dst_region_index)) = overlap_extent_indices { - return Err(CopyError::OverlappingRegions { - src_region_index, - dst_region_index, - }); - } - - // VUID-VkBlitImageInfo2-srcImageLayout-00221 - // VUID-VkBlitImageInfo2-dstImageLayout-00226 - if let Some((src_region_index, dst_region_index)) = overlap_subresource_indices { - if src_image_layout != dst_image_layout { - return Err(CopyError::OverlappingSubresourcesLayoutMismatch { - src_region_index, - dst_region_index, - src_image_layout, - dst_image_layout, - }); - } + return Err(Box::new(ValidationError { + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdBlitImage2-renderpass"], + ..Default::default() + })); } Ok(()) @@ -2531,7 +503,7 @@ where }) .collect(), move |out: &mut UnsafeCommandBufferBuilder| { - out.blit_image(&blit_image_info); + out.blit_image_unchecked(&blit_image_info); }, ); @@ -2547,264 +519,26 @@ where pub fn resolve_image( &mut self, resolve_image_info: ResolveImageInfo, - ) -> Result<&mut Self, CopyError> { + ) -> Result<&mut Self, Box> { self.validate_resolve_image(&resolve_image_info)?; - unsafe { - self.resolve_image_unchecked(resolve_image_info); - } - - Ok(self) + unsafe { Ok(self.resolve_image_unchecked(resolve_image_info)) } } fn validate_resolve_image( &self, resolve_image_info: &ResolveImageInfo, - ) -> Result<(), CopyError> { - let device = self.device(); + ) -> Result<(), Box> { + self.inner.validate_resolve_image(resolve_image_info)?; - // VUID-vkCmdResolveImage2-renderpass if self.builder_state.render_pass.is_some() { - return Err(CopyError::ForbiddenInsideRenderPass); + return Err(Box::new(ValidationError { + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdResolveImage2-renderpass"], + ..Default::default() + })); } - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdResolveImage2-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(CopyError::NotSupportedByQueueFamily); - } - - let &ResolveImageInfo { - ref src_image, - src_image_layout, - ref dst_image, - dst_image_layout, - ref regions, - _ne: _, - } = resolve_image_info; - - // VUID-VkResolveImageInfo2-srcImageLayout-parameter - src_image_layout.validate_device(device)?; - - // VUID-VkResolveImageInfo2-dstImageLayout-parameter - dst_image_layout.validate_device(device)?; - - // VUID-VkResolveImageInfo2-commonparent - assert_eq!(device, src_image.device()); - assert_eq!(device, dst_image.device()); - - let src_image_type = src_image.image_type(); - let dst_image_type = dst_image.image_type(); - - // VUID-VkResolveImageInfo2-srcImage-00257 - if src_image.samples() == SampleCount::Sample1 { - return Err(CopyError::SampleCountInvalid { - resource: CopyErrorResource::Source, - sample_count: dst_image.samples(), - allowed_sample_counts: SampleCounts::SAMPLE_2 - | SampleCounts::SAMPLE_4 - | SampleCounts::SAMPLE_8 - | SampleCounts::SAMPLE_16 - | SampleCounts::SAMPLE_32 - | SampleCounts::SAMPLE_64, - }); - } - - // VUID-VkResolveImageInfo2-dstImage-00259 - if dst_image.samples() != SampleCount::Sample1 { - return Err(CopyError::SampleCountInvalid { - resource: CopyErrorResource::Destination, - sample_count: dst_image.samples(), - allowed_sample_counts: SampleCounts::SAMPLE_1, - }); - } - - // VUID-VkResolveImageInfo2-dstImage-02003 - if !dst_image - .format_features() - .intersects(FormatFeatures::COLOR_ATTACHMENT) - { - return Err(CopyError::MissingFormatFeature { - resource: CopyErrorResource::Destination, - format_feature: "color_attachment", - }); - } - - // VUID-VkResolveImageInfo2-srcImage-01386 - if src_image.format() != dst_image.format() { - return Err(CopyError::FormatsMismatch { - src_format: src_image.format(), - dst_format: dst_image.format(), - }); - } - - // VUID-VkResolveImageInfo2-srcImageLayout-01400 - if !matches!( - src_image_layout, - ImageLayout::TransferSrcOptimal | ImageLayout::General - ) { - return Err(CopyError::ImageLayoutInvalid { - resource: CopyErrorResource::Source, - image_layout: src_image_layout, - }); - } - - // VUID-VkResolveImageInfo2-dstImageLayout-01401 - if !matches!( - dst_image_layout, - ImageLayout::TransferDstOptimal | ImageLayout::General - ) { - return Err(CopyError::ImageLayoutInvalid { - resource: CopyErrorResource::Destination, - image_layout: dst_image_layout, - }); - } - - // Should be guaranteed by the requirement that formats match, and that the destination - // image format features support color attachments. - debug_assert!( - src_image.format().aspects().intersects(ImageAspects::COLOR) - && dst_image.format().aspects().intersects(ImageAspects::COLOR) - ); - - for (region_index, region) in regions.iter().enumerate() { - let &ImageResolve { - ref src_subresource, - src_offset, - ref dst_subresource, - dst_offset, - extent, - _ne: _, - } = region; - - let check_subresource = |resource: CopyErrorResource, - image: &Image, - subresource: &ImageSubresourceLayers| - -> Result<_, CopyError> { - // VUID-VkResolveImageInfo2-srcSubresource-01709 - // VUID-VkResolveImageInfo2-dstSubresource-01710 - if subresource.mip_level >= image.mip_levels() { - return Err(CopyError::MipLevelsOutOfRange { - resource, - region_index, - mip_levels_range_end: subresource.mip_level + 1, - image_mip_levels: image.mip_levels(), - }); - } - - // VUID-VkImageSubresourceLayers-layerCount-01700 - // VUID-VkResolveImageInfo2-srcImage-04446 - // VUID-VkResolveImageInfo2-srcImage-04447 - assert!(!subresource.array_layers.is_empty()); - - // VUID-VkResolveImageInfo2-srcSubresource-01711 - // VUID-VkResolveImageInfo2-dstSubresource-01712 - // VUID-VkResolveImageInfo2-srcImage-04446 - // VUID-VkResolveImageInfo2-srcImage-04447 - if subresource.array_layers.end > image.array_layers() { - return Err(CopyError::ArrayLayersOutOfRange { - resource: CopyErrorResource::Destination, - region_index, - array_layers_range_end: subresource.array_layers.end, - image_array_layers: image.array_layers(), - }); - } - - // VUID-VkImageSubresourceLayers-aspectMask-parameter - subresource.aspects.validate_device(device)?; - - // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask - // VUID-VkImageResolve2-aspectMask-00266 - if subresource.aspects != (ImageAspects::COLOR) { - return Err(CopyError::AspectsNotAllowed { - resource, - region_index, - aspects: subresource.aspects, - allowed_aspects: ImageAspects::COLOR, - }); - } - - Ok(mip_level_extent(image.extent(), subresource.mip_level).unwrap()) - }; - - let src_subresource_extent = - check_subresource(CopyErrorResource::Source, src_image, src_subresource)?; - let dst_subresource_extent = - check_subresource(CopyErrorResource::Destination, dst_image, dst_subresource)?; - - let src_layer_count = - src_subresource.array_layers.end - src_subresource.array_layers.start; - let dst_layer_count = - dst_subresource.array_layers.end - dst_subresource.array_layers.start; - - // VUID-VkImageResolve2-layerCount-00267 - // VUID-VkResolveImageInfo2-srcImage-04446 - // VUID-VkResolveImageInfo2-srcImage-04447 - if src_layer_count != dst_layer_count { - return Err(CopyError::ArrayLayerCountMismatch { - region_index, - src_layer_count, - dst_layer_count, - }); - } - - // No VUID, but it makes sense? - assert!(extent[0] != 0 && extent[1] != 0 && extent[2] != 0); - - let check_offset_extent = |resource: CopyErrorResource, - _image_type: ImageType, - subresource_extent: [u32; 3], - offset: [u32; 3]| - -> Result<_, CopyError> { - for i in 0..3 { - // No VUID, but makes sense? - assert!(extent[i] != 0); - - // VUID-VkResolveImageInfo2-srcOffset-00269 - // VUID-VkResolveImageInfo2-srcOffset-00270 - // VUID-VkResolveImageInfo2-srcOffset-00272 - // VUID-VkResolveImageInfo2-dstOffset-00274 - // VUID-VkResolveImageInfo2-dstOffset-00275 - // VUID-VkResolveImageInfo2-dstOffset-00277 - if offset[i] + extent[i] > subresource_extent[i] { - return Err(CopyError::RegionOutOfImageBounds { - resource, - region_index, - offset_range_end: [ - offset[0] + extent[0], - offset[1] + extent[1], - offset[2] + extent[2], - ], - subresource_extent, - }); - } - } - - Ok(()) - }; - - check_offset_extent( - CopyErrorResource::Source, - src_image_type, - src_subresource_extent, - src_offset, - )?; - check_offset_extent( - CopyErrorResource::Destination, - dst_image_type, - dst_subresource_extent, - dst_offset, - )?; - } - - // VUID-VkResolveImageInfo2-pRegions-00255 - // Can't occur as long as memory aliasing isn't allowed, because `src_image` and - // `dst_image` must have different sample counts and therefore can never be the same image. - Ok(()) } @@ -2861,7 +595,7 @@ where }) .collect(), move |out: &mut UnsafeCommandBufferBuilder| { - out.resolve_image(&resolve_image_info); + out.resolve_image_unchecked(&resolve_image_info); }, ); @@ -2873,12 +607,42 @@ impl UnsafeCommandBufferBuilder where A: CommandBufferAllocator, { - /// Calls `vkCmdCopyBuffer` on the builder. - /// - /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid - /// usage of the command anyway. - #[inline] - pub unsafe fn copy_buffer(&mut self, copy_buffer_info: &CopyBufferInfo) -> &mut Self { + pub unsafe fn copy_buffer( + &mut self, + copy_buffer_info: &CopyBufferInfo, + ) -> Result<&mut Self, Box> { + self.validate_copy_buffer(copy_buffer_info)?; + + Ok(self.copy_buffer_unchecked(copy_buffer_info)) + } + + fn validate_copy_buffer( + &self, + copy_buffer_info: &CopyBufferInfo, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + transfer, graphics or compute operations" + .into(), + vuids: &["VUID-vkCmdCopyBuffer2-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + copy_buffer_info + .validate(self.device()) + .map_err(|err| err.add_context("copy_buffer_info"))?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn copy_buffer_unchecked(&mut self, copy_buffer_info: &CopyBufferInfo) -> &mut Self { let CopyBufferInfo { src_buffer, dst_buffer, @@ -2958,12 +722,312 @@ where self } - /// Calls `vkCmdCopyImage` on the builder. - /// - /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid - /// usage of the command anyway. - #[inline] - pub unsafe fn copy_image(&mut self, copy_image_info: &CopyImageInfo) -> &mut Self { + pub unsafe fn copy_image( + &mut self, + copy_image_info: &CopyImageInfo, + ) -> Result<&mut Self, Box> { + self.validate_copy_image(copy_image_info)?; + + Ok(self.copy_image_unchecked(copy_image_info)) + } + + fn validate_copy_image( + &self, + copy_image_info: &CopyImageInfo, + ) -> Result<(), Box> { + let queue_family_properties = self.queue_family_properties(); + + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + transfer, graphics or compute operations" + .into(), + vuids: &["VUID-vkCmdCopyImage2-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + copy_image_info + .validate(self.device()) + .map_err(|err| err.add_context("copy_image_info"))?; + + let &CopyImageInfo { + ref src_image, + src_image_layout: _, + ref dst_image, + dst_image_layout: _, + ref regions, + _ne: _, + } = copy_image_info; + + let src_image_format = src_image.format(); + let src_image_format_subsampled_extent = src_image_format + .ycbcr_chroma_sampling() + .map_or(src_image.extent(), |s| { + s.subsampled_extent(src_image.extent()) + }); + + let dst_image_format = dst_image.format(); + let dst_image_format_subsampled_extent = dst_image_format + .ycbcr_chroma_sampling() + .map_or(dst_image.extent(), |s| { + s.subsampled_extent(dst_image.extent()) + }); + + let min_image_transfer_granularity = + // `[1; 3]` means the granularity is 1x1x1 texel, so we can ignore it. + // Only check this if there are values greater than 1. + (queue_family_properties.min_image_transfer_granularity != [1; 3]).then(|| { + // `[0; 3]` means only the whole subresource can be copied. + (queue_family_properties.min_image_transfer_granularity != [0; 3]).then(|| { + // Spec: + // "The value returned in minImageTransferGranularity has a unit of + // compressed texel blocks for images having a block-compressed format, + // and a unit of texels otherwise."" + + let src_granularity = if src_image_format.compression().is_some() { + let granularity = queue_family_properties.min_image_transfer_granularity; + let block_extent = src_image_format.block_extent(); + + [ + granularity[0] * block_extent[0], + granularity[1] * block_extent[1], + granularity[2] * block_extent[2], + ] + } else { + queue_family_properties.min_image_transfer_granularity + }; + + let dst_granularity = if dst_image_format.compression().is_some() { + let granularity = queue_family_properties.min_image_transfer_granularity; + let block_extent = dst_image_format.block_extent(); + + [ + granularity[0] * block_extent[0], + granularity[1] * block_extent[1], + granularity[2] * block_extent[2], + ] + } else { + queue_family_properties.min_image_transfer_granularity + }; + + (src_granularity, dst_granularity) + }) + }); + + if min_image_transfer_granularity.is_some() { + for (region_index, region) in regions.iter().enumerate() { + let &ImageCopy { + ref src_subresource, + src_offset, + ref dst_subresource, + dst_offset, + extent, + _ne: _, + } = region; + + if let Some(min_image_transfer_granularity) = &min_image_transfer_granularity { + let mut src_subresource_extent = + mip_level_extent(src_image.extent(), src_subresource.mip_level).unwrap(); + + if matches!( + src_subresource.aspects, + ImageAspects::PLANE_1 | ImageAspects::PLANE_2 + ) { + src_subresource_extent = src_image_format_subsampled_extent; + } + + let mut dst_subresource_extent = + mip_level_extent(dst_image.extent(), dst_subresource.mip_level).unwrap(); + + if matches!( + dst_subresource.aspects, + ImageAspects::PLANE_1 | ImageAspects::PLANE_2 + ) { + dst_subresource_extent = dst_image_format_subsampled_extent; + } + + if let Some((src_granularity, dst_granularity)) = + &min_image_transfer_granularity + { + /* + Check src + */ + + for i in 0..3 { + if src_offset[i] % src_granularity[i] != 0 { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is not `[0; 3]`, but \ + `regions[{}].src_offset[{1}]` is not a multiple of \ + `min_image_transfer_granularity[{1}]` texel blocks", + region_index, i, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcOffset-01783"], + ..Default::default() + })); + } + + if src_offset[i] + extent[i] != src_subresource_extent[i] + && extent[i] % src_granularity[i] != 0 + { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is not `[0; 3]`, and \ + `regions[{0}].src_offset[{1}] + regions[{0}].extent[{1}]` \ + is not equal to coordinate {1} of the extent of the \ + subresource of `src_image` selected by \ + `regions[{0}].src_subresource`, but \ + `regions[{}].extent[{1}]` is not a multiple of \ + `min_image_transfer_granularity[{1}]` texel blocks", + region_index, i, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcOffset-01783"], + ..Default::default() + })); + } + } + + /* + Check dst + */ + + for i in 0..3 { + if dst_offset[i] % dst_granularity[i] != 0 { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is not `[0; 3]`, but \ + `regions[{}].dst_offset[{1}]` is not a multiple of \ + `min_image_transfer_granularity[{1}]` texel blocks", + region_index, i, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstOffset-01784"], + ..Default::default() + })); + } + + if dst_offset[i] + extent[i] != dst_subresource_extent[i] + && extent[i] % dst_granularity[i] != 0 + { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is not `[0; 3]`, and \ + `regions[{0}].dst_offset[{1}] + regions[{0}].extent[{1}]` \ + is not equal to coordinate {1} of the extent of the \ + subresource of `dst_image` selected by \ + `regions[{0}].dst_subresource`, but \ + `regions[{}].extent[{1}]` is not a multiple of \ + `min_image_transfer_granularity[{1}]` texel blocks", + region_index, i, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstOffset-01784"], + ..Default::default() + })); + } + } + } else { + /* + Check src + */ + + for i in 0..3 { + if src_offset[i] != 0 { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is `[0; 3]`, but \ + `regions[{}].src_offset[{}]` is not 0", + region_index, i, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcOffset-01783"], + ..Default::default() + })); + } + + if src_offset[i] + extent[i] != src_subresource_extent[i] { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is `[0; 3]`, but \ + `regions[{0}].src_offset[{1}] + regions[{0}].extent[{1}]` \ + is not equal to coordinate {1} of the extent of the \ + subresource of `src_image` selected by \ + `regions[{0}].src_subresource`", + region_index, i, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcOffset-01783"], + ..Default::default() + })); + } + } + + /* + Check dst + */ + + for i in 0..3 { + if dst_offset[i] != 0 { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is `[0; 3]`, but \ + `regions[{}].dst_offset[{}]` is not 0", + region_index, i, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstOffset-01784"], + ..Default::default() + })); + } + + if dst_offset[i] + extent[i] != dst_subresource_extent[i] { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is `[0; 3]`, but \ + `regions[{0}].dst_offset[{1}] + regions[{0}].extent[{1}]` \ + is not equal to coordinate {1} of the extent of the \ + subresource of `dst_image` selected by \ + `regions[{0}].dst_subresource`", + region_index, i, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstOffset-01784"], + ..Default::default() + })); + } + } + } + } + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn copy_image_unchecked(&mut self, copy_image_info: &CopyImageInfo) -> &mut Self { let &CopyImageInfo { ref src_image, src_image_layout, @@ -3081,14 +1145,234 @@ where self } - /// Calls `vkCmdCopyBufferToImage` on the builder. - /// - /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid - /// usage of the command anyway. - #[inline] pub unsafe fn copy_buffer_to_image( &mut self, copy_buffer_to_image_info: &CopyBufferToImageInfo, + ) -> Result<&mut Self, Box> { + self.validate_copy_buffer_to_image(copy_buffer_to_image_info)?; + + Ok(self.copy_buffer_to_image_unchecked(copy_buffer_to_image_info)) + } + + fn validate_copy_buffer_to_image( + &self, + copy_buffer_to_image_info: &CopyBufferToImageInfo, + ) -> Result<(), Box> { + let queue_family_properties = self.queue_family_properties(); + + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + transfer, graphics or compute operations" + .into(), + vuids: &["VUID-vkCmdCopyBufferToImage2-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + copy_buffer_to_image_info + .validate(self.device()) + .map_err(|err| err.add_context("copy_buffer_to_image_info"))?; + + let &CopyBufferToImageInfo { + src_buffer: _, + ref dst_image, + dst_image_layout: _, + ref regions, + _ne, + } = copy_buffer_to_image_info; + + let dst_image_format = dst_image.format(); + let dst_image_format_subsampled_extent = dst_image_format + .ycbcr_chroma_sampling() + .map_or(dst_image.extent(), |s| { + s.subsampled_extent(dst_image.extent()) + }); + + let min_image_transfer_granularity = + // `[1; 3]` means the granularity is 1x1x1 texel, so we can ignore it. + // Only check this if there are values greater than 1. + (queue_family_properties.min_image_transfer_granularity != [1; 3]).then(|| { + // `[0; 3]` means only the whole subresource can be copied. + (queue_family_properties.min_image_transfer_granularity != [0; 3]).then(|| { + // Spec: + // "The value returned in minImageTransferGranularity has a unit of + // compressed texel blocks for images having a block-compressed format, + // and a unit of texels otherwise."" + + if dst_image_format.compression().is_some() { + let granularity = queue_family_properties.min_image_transfer_granularity; + let block_extent = dst_image_format.block_extent(); + + [ + granularity[0] * block_extent[0], + granularity[1] * block_extent[1], + granularity[2] * block_extent[2], + ] + } else { + queue_family_properties.min_image_transfer_granularity + } + }) + }); + + let queue_family_no_graphics = !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS); + let queue_family_no_compute = !queue_family_properties + .queue_flags + .intersects(QueueFlags::COMPUTE); + + if min_image_transfer_granularity.is_some() || queue_family_no_graphics { + for (region_index, region) in regions.iter().enumerate() { + let &BufferImageCopy { + buffer_offset, + buffer_row_length: _, + buffer_image_height: _, + ref image_subresource, + image_offset, + image_extent, + _ne, + } = region; + + if queue_family_no_graphics { + if queue_family_no_compute && buffer_offset % 4 != 0 { + return Err(Box::new(ValidationError { + context: "create_info".into(), + problem: format!( + "the queue family of the command buffer does not support \ + graphics or compute operations, but \ + `regions[{}].buffer_offset` is not a multiple of 4", + region_index + ) + .into(), + vuids: &["VUID-vkCmdCopyBufferToImage2-commandBuffer-07737"], + ..Default::default() + })); + } + + if image_subresource + .aspects + .intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) + { + return Err(Box::new(ValidationError { + context: "create_info".into(), + problem: format!( + "the queue family of the command buffer does not support \ + graphics operations, but \ + `regions[{}].image_subresource.aspects` contains \ + `ImageAspects::DEPTH` or `ImageAspects::STENCIL`", + region_index + ) + .into(), + vuids: &["VUID-vkCmdCopyBufferToImage2-commandBuffer-07739"], + ..Default::default() + })); + } + } + + if let Some(min_image_transfer_granularity) = &min_image_transfer_granularity { + let mut image_subresource_extent = + mip_level_extent(dst_image.extent(), image_subresource.mip_level).unwrap(); + + if matches!( + image_subresource.aspects, + ImageAspects::PLANE_1 | ImageAspects::PLANE_2 + ) { + image_subresource_extent = dst_image_format_subsampled_extent; + } + + if let Some(dst_granularity) = &min_image_transfer_granularity { + for i in 0..3 { + if image_offset[i] % dst_granularity[i] != 0 { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is not `[0; 3]`, but \ + `regions[{}].image_offset[{1}]` is not a multiple of \ + `min_image_transfer_granularity[{1}]` texel blocks", + region_index, i, + ) + .into(), + vuids: &["VUID-vkCmdCopyBufferToImage2-imageOffset-07738"], + ..Default::default() + })); + } + + if image_offset[i] + image_extent[i] != image_subresource_extent[i] + && image_extent[i] % dst_granularity[i] != 0 + { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is not `[0; 3]`, and \ + `regions[{0}].image_offset[{1}] + \ + regions[{0}].image_extent[{1}]` \ + is not equal to coordinate {1} of the extent of the \ + subresource of `dst_image` selected by \ + `regions[{0}].image_subresource`, but \ + `regions[{}].image_extent[{1}]` is not a multiple of \ + `min_image_transfer_granularity[{1}]` texel blocks", + region_index, i, + ) + .into(), + vuids: &["VUID-vkCmdCopyBufferToImage2-imageOffset-07738"], + ..Default::default() + })); + } + } + } else { + for i in 0..3 { + if image_offset[i] != 0 { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is `[0; 3]`, but \ + `regions[{}].image_offset[{}]` is not 0", + region_index, i, + ) + .into(), + vuids: &["VUID-vkCmdCopyBufferToImage2-imageOffset-07738"], + ..Default::default() + })); + } + + if image_offset[i] + image_extent[i] != image_subresource_extent[i] { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is `[0; 3]`, but \ + `regions[{0}].image_offset[{1}] + \ + regions[{0}].image_extent[{1}]` \ + is not equal to coordinate {1} of the extent of the \ + subresource of `dst_image` selected by \ + `regions[{0}].image_subresource`", + region_index, i, + ) + .into(), + vuids: &["VUID-vkCmdCopyBufferToImage2-imageOffset-07738"], + ..Default::default() + })); + } + } + } + } + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn copy_buffer_to_image_unchecked( + &mut self, + copy_buffer_to_image_info: &CopyBufferToImageInfo, ) -> &mut Self { let &CopyBufferToImageInfo { ref src_buffer, @@ -3203,14 +1487,213 @@ where self } - /// Calls `vkCmdCopyImageToBuffer` on the builder. - /// - /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid - /// usage of the command anyway. - #[inline] pub unsafe fn copy_image_to_buffer( &mut self, copy_image_to_buffer_info: &CopyImageToBufferInfo, + ) -> Result<&mut Self, Box> { + self.validate_copy_image_to_buffer(copy_image_to_buffer_info)?; + + Ok(self.copy_image_to_buffer_unchecked(copy_image_to_buffer_info)) + } + + fn validate_copy_image_to_buffer( + &self, + copy_image_to_buffer_info: &CopyImageToBufferInfo, + ) -> Result<(), Box> { + let queue_family_properties = self.queue_family_properties(); + + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + transfer, graphics or compute operations" + .into(), + vuids: &["VUID-vkCmdCopyImageToBuffer2-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + copy_image_to_buffer_info + .validate(self.device()) + .map_err(|err| err.add_context("copy_image_to_buffer_info"))?; + + let &CopyImageToBufferInfo { + ref src_image, + src_image_layout: _, + dst_buffer: _, + ref regions, + _ne, + } = copy_image_to_buffer_info; + + let src_image_format = src_image.format(); + let src_image_format_subsampled_extent = src_image_format + .ycbcr_chroma_sampling() + .map_or(src_image.extent(), |s| { + s.subsampled_extent(src_image.extent()) + }); + + let min_image_transfer_granularity = + // `[1; 3]` means the granularity is 1x1x1 texel, so we can ignore it. + // Only check this if there are values greater than 1. + (queue_family_properties.min_image_transfer_granularity != [1; 3]).then(|| { + // `[0; 3]` means only the whole subresource can be copied. + (queue_family_properties.min_image_transfer_granularity != [0; 3]).then(|| { + // Spec: + // "The value returned in minImageTransferGranularity has a unit of + // compressed texel blocks for images having a block-compressed format, + // and a unit of texels otherwise."" + + if src_image_format.compression().is_some() { + let granularity = queue_family_properties.min_image_transfer_granularity; + let block_extent = src_image_format.block_extent(); + + [ + granularity[0] * block_extent[0], + granularity[1] * block_extent[1], + granularity[2] * block_extent[2], + ] + } else { + queue_family_properties.min_image_transfer_granularity + } + }) + }); + + let queue_family_no_graphics = !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS); + let queue_family_no_compute = !queue_family_properties + .queue_flags + .intersects(QueueFlags::COMPUTE); + + if min_image_transfer_granularity.is_some() || queue_family_no_graphics { + for (region_index, region) in regions.iter().enumerate() { + let &BufferImageCopy { + buffer_offset, + buffer_row_length: _, + buffer_image_height: _, + ref image_subresource, + image_offset, + image_extent, + _ne, + } = region; + + if queue_family_no_graphics && queue_family_no_compute && buffer_offset % 4 != 0 { + return Err(Box::new(ValidationError { + context: "create_info".into(), + problem: format!( + "the queue family of the command buffer does not support \ + graphics or compute operations, but \ + `regions[{}].buffer_offset` is not a multiple of 4", + region_index + ) + .into(), + vuids: &["VUID-vkCmdCopyImageToBuffer2-commandBuffer-07746"], + ..Default::default() + })); + } + + if let Some(min_image_transfer_granularity) = &min_image_transfer_granularity { + let mut image_subresource_extent = + mip_level_extent(src_image.extent(), image_subresource.mip_level).unwrap(); + + if matches!( + image_subresource.aspects, + ImageAspects::PLANE_1 | ImageAspects::PLANE_2 + ) { + image_subresource_extent = src_image_format_subsampled_extent; + } + + if let Some(src_granularity) = &min_image_transfer_granularity { + for i in 0..3 { + if image_offset[i] % src_granularity[i] != 0 { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is not `[0; 3]`, but \ + `regions[{}].image_offset[{1}]` is not a multiple of \ + `min_image_transfer_granularity[{1}]` texel blocks", + region_index, i, + ) + .into(), + vuids: &["VUID-vkCmdCopyImageToBuffer2-imageOffset-07747"], + ..Default::default() + })); + } + + if image_offset[i] + image_extent[i] != image_subresource_extent[i] + && image_extent[i] % src_granularity[i] != 0 + { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is not `[0; 3]`, and \ + `regions[{0}].image_offset[{1}] + \ + regions[{0}].image_extent[{1}]` \ + is not equal to coordinate {1} of the extent of the \ + subresource of `src_image` selected by \ + `regions[{0}].image_subresource`, but \ + `regions[{}].image_extent[{1}]` is not a multiple of \ + `min_image_transfer_granularity[{1}]` texel blocks", + region_index, i, + ) + .into(), + vuids: &["VUID-vkCmdCopyImageToBuffer2-imageOffset-07747"], + ..Default::default() + })); + } + } + } else { + for i in 0..3 { + if image_offset[i] != 0 { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is `[0; 3]`, but \ + `regions[{}].image_offset[{}]` is not 0", + region_index, i, + ) + .into(), + vuids: &["VUID-vkCmdCopyImageToBuffer2-imageOffset-07747"], + ..Default::default() + })); + } + + if image_offset[i] + image_extent[i] != image_subresource_extent[i] { + return Err(Box::new(ValidationError { + context: "copy_image_info".into(), + problem: format!( + "the `min_image_transfer_granularity` property of the \ + queue family of the command buffer is `[0; 3]`, but \ + `regions[{0}].image_offset[{1}] + \ + regions[{0}].image_extent[{1}]` \ + is not equal to coordinate {1} of the extent of the \ + subresource of `src_image` selected by \ + `regions[{0}].image_subresource`", + region_index, i, + ) + .into(), + vuids: &["VUID-vkCmdCopyImageToBuffer2-imageOffset-07747"], + ..Default::default() + })); + } + } + } + } + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn copy_image_to_buffer_unchecked( + &mut self, + copy_image_to_buffer_info: &CopyImageToBufferInfo, ) -> &mut Self { let &CopyImageToBufferInfo { ref src_image, @@ -3325,12 +1808,42 @@ where self } - /// Calls `vkCmdBlitImage` on the builder. - /// - /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid - /// usage of the command anyway. - #[inline] - pub unsafe fn blit_image(&mut self, blit_image_info: &BlitImageInfo) -> &mut Self { + pub unsafe fn blit_image( + &mut self, + blit_image_info: &BlitImageInfo, + ) -> Result<&mut Self, Box> { + self.validate_blit_image(blit_image_info)?; + + Ok(self.blit_image_unchecked(blit_image_info)) + } + + fn validate_blit_image( + &self, + blit_image_info: &BlitImageInfo, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdBlitImage2-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + blit_image_info + .validate(self.device()) + .map_err(|err| err.add_context("blit_image_info"))?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn blit_image_unchecked(&mut self, blit_image_info: &BlitImageInfo) -> &mut Self { let &BlitImageInfo { ref src_image, src_image_layout, @@ -3467,12 +1980,45 @@ where self } - /// Calls `vkCmdResolveImage` on the builder. - /// - /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid - /// usage of the command anyway. - #[inline] - pub unsafe fn resolve_image(&mut self, resolve_image_info: &ResolveImageInfo) -> &mut Self { + pub unsafe fn resolve_image( + &mut self, + resolve_image_info: &ResolveImageInfo, + ) -> Result<&mut Self, Box> { + self.validate_resolve_image(resolve_image_info)?; + + Ok(self.resolve_image_unchecked(resolve_image_info)) + } + + fn validate_resolve_image( + &self, + resolve_image_info: &ResolveImageInfo, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdResolveImage2-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + resolve_image_info + .validate(self.device()) + .map_err(|err| err.add_context("resolve_image_info"))?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn resolve_image_unchecked( + &mut self, + resolve_image_info: &ResolveImageInfo, + ) -> &mut Self { let &ResolveImageInfo { ref src_image, src_image_layout, @@ -3633,6 +2179,129 @@ impl CopyBufferInfo { _ne: crate::NonExhaustive(()), } } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + ref src_buffer, + ref dst_buffer, + ref regions, + _ne: _, + } = self; + + // VUID-VkCopyBufferInfo2-commonparent + assert_eq!(device, src_buffer.device().as_ref()); + assert_eq!(device, dst_buffer.device().as_ref()); + + if !src_buffer + .buffer() + .usage() + .intersects(BufferUsage::TRANSFER_SRC) + { + return Err(Box::new(ValidationError { + context: "src_buffer.buffer().usage()".into(), + problem: "does not contain `BufferUsage::TRANSFER_SRC`".into(), + vuids: &["VUID-VkCopyBufferInfo2-srcBuffer-00118"], + ..Default::default() + })); + } + + if !dst_buffer + .buffer() + .usage() + .intersects(BufferUsage::TRANSFER_DST) + { + return Err(Box::new(ValidationError { + context: "dst_buffer.buffer().usage()".into(), + problem: "does not contain `BufferUsage::TRANSFER_DST`".into(), + vuids: &["VUID-VkCopyBufferInfo2-dstBuffer-00120"], + ..Default::default() + })); + } + + let same_buffer = src_buffer.buffer() == dst_buffer.buffer(); + let mut overlap_indices = None; + + for (region_index, region) in regions.iter().enumerate() { + region + .validate(device) + .map_err(|err| err.add_context(format!("regions[{}]", region_index)))?; + + let &BufferCopy { + src_offset, + dst_offset, + size, + _ne: _, + } = region; + + if src_offset + size > src_buffer.size() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_offset + regions[{0}].size` is greater than \ + `src_buffer.size()`", + region_index + ) + .into(), + vuids: &[ + "VUID-VkCopyBufferInfo2-srcOffset-00113", + "VUID-VkCopyBufferInfo2-size-00115", + ], + ..Default::default() + })); + } + + if dst_offset + size > dst_buffer.size() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_offset + regions[{0}].size` is greater than \ + `dst_buffer.size()`", + region_index + ) + .into(), + vuids: &[ + "VUID-VkCopyBufferInfo2-dstOffset-00114", + "VUID-VkCopyBufferInfo2-size-00116", + ], + ..Default::default() + })); + } + + // VUID-VkCopyBufferInfo2-pRegions-00117 + if same_buffer { + let src_region_index = region_index; + let src_range = + src_buffer.offset() + src_offset..src_buffer.offset() + src_offset + size; + + for (dst_region_index, dst_region) in regions.iter().enumerate() { + let &BufferCopy { dst_offset, .. } = dst_region; + + let dst_range = + dst_buffer.offset() + dst_offset..dst_buffer.offset() + dst_offset + size; + + if src_range.start >= dst_range.end || dst_range.start >= src_range.end { + // The regions do not overlap + continue; + } + + overlap_indices = Some((src_region_index, dst_region_index)); + } + } + } + + if let Some((src_region_index, dst_region_index)) = overlap_indices { + return Err(Box::new(ValidationError { + problem: format!( + "`src_buffer.buffer()` is equal to `dst_buffer.buffer()`, and \ + the source of `regions[{}]` overlaps with the destination of `regions[{}]`", + src_region_index, dst_region_index + ) + .into(), + vuids: &["VUID-VkCopyBufferInfo2-pRegions-00117"], + ..Default::default() + })); + } + + Ok(()) + } } /// Parameters to copy data from a buffer to another buffer, with type information. @@ -3735,6 +2404,28 @@ impl Default for BufferCopy { } } +impl BufferCopy { + pub(crate) fn validate(&self, _device: &Device) -> Result<(), Box> { + let &Self { + src_offset: _, + dst_offset: _, + size, + _ne: _, + } = self; + + if size == 0 { + return Err(Box::new(ValidationError { + context: "size".into(), + problem: "is zero".into(), + vuids: &["VUID-VkBufferCopy2-size-01988"], + ..Default::default() + })); + } + + Ok(()) + } +} + /// Parameters to copy data from an image to another image. #[derive(Clone, Debug)] pub struct CopyImageInfo { @@ -3812,6 +2503,1189 @@ impl CopyImageInfo { _ne: crate::NonExhaustive(()), } } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + ref src_image, + src_image_layout, + ref dst_image, + dst_image_layout, + ref regions, + _ne: _, + } = self; + + src_image_layout + .validate_device(device) + .map_err(|err| ValidationError { + context: "src_image_layout".into(), + vuids: &["VUID-VkCopyImageInfo2-srcImageLayout-parameter"], + ..ValidationError::from_requirement(err) + })?; + + dst_image_layout + .validate_device(device) + .map_err(|err| ValidationError { + context: "dst_image_layout".into(), + vuids: &["VUID-VkCopyImageInfo2-dstImageLayout-parameter"], + ..ValidationError::from_requirement(err) + })?; + + // VUID-VkCopyImageInfo2-commonparent + assert_eq!(device, src_image.device().as_ref()); + assert_eq!(device, dst_image.device().as_ref()); + + let src_image_format = src_image.format(); + let src_image_format_aspects = src_image_format.aspects(); + let src_image_format_planes = src_image_format.planes(); + let src_image_format_subsampled_extent = src_image_format + .ycbcr_chroma_sampling() + .map_or(src_image.extent(), |s| { + s.subsampled_extent(src_image.extent()) + }); + + let dst_image_format = dst_image.format(); + let dst_image_format_aspects = dst_image_format.aspects(); + let dst_image_format_planes = dst_image_format.planes(); + let dst_image_format_subsampled_extent = dst_image_format + .ycbcr_chroma_sampling() + .map_or(dst_image.extent(), |s| { + s.subsampled_extent(dst_image.extent()) + }); + + if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { + if !src_image + .format_features() + .intersects(FormatFeatures::TRANSFER_SRC) + { + return Err(Box::new(ValidationError { + context: "src_image.format_features()".into(), + problem: "does not contain `FormatFeatures::TRANSFER_SRC`".into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-01995"], + ..Default::default() + })); + } + + if !dst_image + .format_features() + .intersects(FormatFeatures::TRANSFER_DST) + { + return Err(Box::new(ValidationError { + context: "dst_image.format_features()".into(), + problem: "does not contain `FormatFeatures::TRANSFER_DST`".into(), + vuids: &["VUID-VkCopyImageInfo2-dstImage-01996"], + ..Default::default() + })); + } + } + + if src_image.samples() != dst_image.samples() { + return Err(Box::new(ValidationError { + problem: "`src_image.samples()` does not equal `dst_image.samples()`".into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-00136"], + ..Default::default() + })); + } + + if !matches!( + src_image_layout, + ImageLayout::TransferSrcOptimal | ImageLayout::General + ) { + return Err(Box::new(ValidationError { + context: "src_image_layout".into(), + problem: "is not `ImageLayout::TransferSrcOptimal` or `ImageLayout::General`" + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImageLayout-01917"], + ..Default::default() + })); + } + + if !matches!( + dst_image_layout, + ImageLayout::TransferDstOptimal | ImageLayout::General + ) { + return Err(Box::new(ValidationError { + context: "dst_image_layout".into(), + problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`" + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstImageLayout-01395"], + ..Default::default() + })); + } + + if src_image.image_type() != dst_image.image_type() { + if !(matches!(src_image.image_type(), ImageType::Dim2d | ImageType::Dim3d) + && matches!(dst_image.image_type(), ImageType::Dim2d | ImageType::Dim3d)) + { + return Err(Box::new(ValidationError { + problem: "`src_image.image_type()` does not equal `dst_image.image_type()`, \ + but they are not both `ImageType::Dim2d` or `ImageType::Dim3d`" + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-07743"], + ..Default::default() + })); + } + + if !(device.api_version() >= Version::V1_1 + || device.enabled_extensions().khr_maintenance1) + { + return Err(Box::new(ValidationError { + problem: "`src_image.image_type()` does not equal `dst_image.image_type()`, \ + and are both `ImageType::Dim2d` or `ImageType::Dim3d`" + .into(), + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_1)]), + RequiresAllOf(&[Requires::DeviceExtension("khr_maintenance1")]), + ]), + vuids: &["VUID-VkCopyImageInfo2-apiVersion-07933"], + ..Default::default() + })); + } + } + + let is_same_image = src_image == dst_image; + let mut overlap_subresource_indices = None; + let mut overlap_extent_indices = None; + + for (region_index, region) in regions.iter().enumerate() { + region + .validate(device) + .map_err(|err| err.add_context(format!("regions[{}]", region_index)))?; + + let &ImageCopy { + ref src_subresource, + src_offset, + ref dst_subresource, + dst_offset, + extent, + _ne, + } = region; + + /* + Check src + */ + + if src_subresource.mip_level >= src_image.mip_levels() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].src_subresource.mip_level` is not less than \ + `src_image.mip_levels()`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcSubresource-07967"], + ..Default::default() + })); + } + + let mut src_subresource_format = src_image_format; + let mut src_subresource_extent = + mip_level_extent(src_image.extent(), src_subresource.mip_level).unwrap(); + + if src_image_format_planes.is_empty() { + if !src_image_format_aspects.contains(src_subresource.aspects) { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].src_subresource.aspects` is not a subset of \ + `src_image.format().aspects()`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-aspectMask-00142"], + ..Default::default() + })); + } + } else if src_image_format_planes.len() == 2 { + match src_subresource.aspects { + ImageAspects::PLANE_0 => { + src_subresource_format = src_image_format_planes[0]; + } + ImageAspects::PLANE_1 => { + src_subresource_format = src_image_format_planes[1]; + src_subresource_extent = src_image_format_subsampled_extent; + } + _ => { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.format()` is a multi-planar format with two planes, \ + but `regions[{}].src_subresource.aspect` is not \ + `ImageAspects::PLANE_0` or `ImageAspects::PLANE_1`", + region_index, + ) + .into(), + vuids: &[ + "VUID-VkCopyImageInfo2-srcImage-08713", + "VUID-VkCopyImageInfo2-aspectMask-00142", + ], + ..Default::default() + })); + } + } + } else if src_image_format_planes.len() == 3 { + match src_subresource.aspects { + ImageAspects::PLANE_0 => { + src_subresource_format = src_image_format_planes[0]; + } + ImageAspects::PLANE_1 => { + src_subresource_format = src_image_format_planes[1]; + src_subresource_extent = src_image_format_subsampled_extent; + } + ImageAspects::PLANE_2 => { + src_subresource_format = src_image_format_planes[2]; + src_subresource_extent = src_image_format_subsampled_extent; + } + _ => { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.format()` is a multi-planar format with three planes, \ + but `regions[{}].src_subresource.aspect` is not \ + `ImageAspects::PLANE_0`, `ImageAspects::PLANE_1` or \ + `ImageAspects::PLANE_2`", + region_index, + ) + .into(), + vuids: &[ + "VUID-VkCopyImageInfo2-srcImage-08713", + "VUID-VkCopyImageInfo2-aspectMask-00142", + ], + ..Default::default() + })); + } + } + } + + match src_image.image_type() { + ImageType::Dim1d => { + if src_offset[1] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].src_offset[1]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-00146"], + ..Default::default() + })); + } + + if extent[1] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].extent[1]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-00146"], + ..Default::default() + })); + } + + if src_offset[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].src_offset[2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-01785"], + ..Default::default() + })); + } + + if extent[2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].extent[2]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-01785"], + ..Default::default() + })); + } + } + ImageType::Dim2d => { + if src_offset[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{}].src_offset[2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-01787"], + ..Default::default() + })); + } + } + ImageType::Dim3d => { + if src_subresource.array_layers != (0..1) { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim3d`, but \ + `regions[{}].src_subresource.array_layers` is not `0..1`", + region_index, + ) + .into(), + vuids: &[ + "VUID-VkCopyImageInfo2-srcImage-04443", + "VUID-VkCopyImageInfo2-apiVersion-07932", + ], + ..Default::default() + })); + } + } + } + + if src_subresource.array_layers.end > src_image.array_layers() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].src_subresource.array_layers.end` is not less than \ + `src_image.array_layers()`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcSubresource-07968"], + ..Default::default() + })); + } + + if src_offset[0] + extent[0] > src_subresource_extent[0] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_offset[0] + regions[{0}].extent[0]` is greater \ + than coordinate 0 of the extent of the subresource of `src_image` \ + selected by `regions[{0}].src_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcOffset-00144"], + ..Default::default() + })); + } + + if src_offset[1] + extent[1] > src_subresource_extent[1] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_offset[1] + regions[{0}].extent[1]` is greater \ + than coordinate 1 of the extent of the subresource of `src_image` \ + selected by `regions[{0}].src_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcOffset-00145"], + ..Default::default() + })); + } + + if src_offset[2] + extent[2] > src_subresource_extent[2] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_offset[2] + regions[{0}].extent[2]` is greater \ + than coordinate 2 of the extent of the subresource of `src_image` \ + selected by `regions[{0}].src_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcOffset-00147"], + ..Default::default() + })); + } + + let src_subresource_format_block_extent = src_subresource_format.block_extent(); + + if src_offset[0] % src_subresource_format_block_extent[0] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_offset[0]` is not a multiple of coordinate 0 of the \ + block extent of the format of the subresource of `src_image` \ + selected by `regions[{0}].src_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-pRegions-07278"], + ..Default::default() + })); + } + + if src_offset[1] % src_subresource_format_block_extent[1] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_offset[1]` is not a multiple of coordinate 1 of the \ + block extent of the format of the subresource of `src_image` \ + selected by `regions[{0}].src_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-pRegions-07279"], + ..Default::default() + })); + } + + if src_offset[2] % src_subresource_format_block_extent[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_offset[2]` is not a multiple of coordinate 2 of the \ + block extent of the format of the subresource of `src_image` \ + selected by `regions[{0}].src_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-pRegions-07280"], + ..Default::default() + })); + } + + if src_offset[0] + extent[0] != src_subresource_extent[0] + && (src_offset[0] + extent[0]) % src_subresource_format_block_extent[0] != 0 + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_offset[0] + regions[{0}].extent[0]` is not \ + equal to the extent of the subresource of `src_image` \ + selected by `regions[{0}].src_subresource`, but \ + it is also not a multiple of coordinate 0 of the block extent of the \ + format of that subresource", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-01728"], + ..Default::default() + })); + } + + if src_offset[1] + extent[1] != src_subresource_extent[1] + && (src_offset[1] + extent[1]) % src_subresource_format_block_extent[1] != 0 + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_offset[1] + regions[{0}].extent[1]` is not \ + equal to the extent of the subresource of `src_image` \ + selected by `regions[{0}].src_subresource`, but \ + it is also not a multiple of coordinate 1 of the block extent of the \ + format of that subresource", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-01729"], + ..Default::default() + })); + } + + if src_offset[2] + extent[2] != src_subresource_extent[2] + && (src_offset[2] + extent[2]) % src_subresource_format_block_extent[2] != 0 + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_offset[2] + regions[{0}].extent[2]` is not \ + equal to the extent of the subresource of `src_image` \ + selected by `regions[{0}].src_subresource`, but \ + it is also not a multiple of coordinate 2 of the block extent of the \ + format of that subresource", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-01730"], + ..Default::default() + })); + } + + if !(src_subresource.aspects - ImageAspects::STENCIL).is_empty() + && !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_subresource.aspects` contains aspects other than \ + `ImageAspects::STENCIL`, but \ + `src_image.usage()` does not contain `ImageUsage::TRANSFER_SRC`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-aspect-06662"], + ..Default::default() + })); + } + + if src_subresource.aspects.intersects(ImageAspects::STENCIL) + && !src_image + .stencil_usage() + .intersects(ImageUsage::TRANSFER_SRC) + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_subresource.aspects` contains \ + `ImageAspects::STENCIL`, but \ + `src_image.stencil_usage()` does not contain `ImageUsage::TRANSFER_SRC`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-aspect-06664"], + ..Default::default() + })); + } + + /* + Check dst + */ + + if dst_subresource.mip_level >= dst_image.mip_levels() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].dst_subresource.mip_level` is not less than \ + `dst_image.mip_levels()`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstSubresource-07967"], + ..Default::default() + })); + } + + let mut dst_subresource_format = dst_image_format; + let mut dst_subresource_extent = + mip_level_extent(dst_image.extent(), dst_subresource.mip_level).unwrap(); + + if dst_image_format_planes.is_empty() { + if !dst_image_format_aspects.contains(dst_subresource.aspects) { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].dst_subresource.aspects` is not a subset of \ + `dst_image.format().aspects()`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-aspectMask-00143"], + ..Default::default() + })); + } + } else if dst_image_format_planes.len() == 2 { + match dst_subresource.aspects { + ImageAspects::PLANE_0 => { + dst_subresource_format = dst_image_format_planes[0]; + } + ImageAspects::PLANE_1 => { + dst_subresource_format = dst_image_format_planes[1]; + dst_subresource_extent = dst_image_format_subsampled_extent; + } + _ => { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.format()` is a multi-planar format with two planes, \ + but `regions[{}].dst_subresource.aspect` is not \ + `ImageAspects::PLANE_0` or `ImageAspects::PLANE_1`", + region_index, + ) + .into(), + vuids: &[ + "VUID-VkCopyImageInfo2-dstImage-08714", + "VUID-VkCopyImageInfo2-aspectMask-00143", + ], + ..Default::default() + })); + } + } + } else if dst_image_format_planes.len() == 3 { + match dst_subresource.aspects { + ImageAspects::PLANE_0 => { + dst_subresource_format = dst_image_format_planes[0]; + } + ImageAspects::PLANE_1 => { + dst_subresource_format = dst_image_format_planes[1]; + dst_subresource_extent = dst_image_format_subsampled_extent; + } + ImageAspects::PLANE_2 => { + dst_subresource_format = dst_image_format_planes[2]; + dst_subresource_extent = dst_image_format_subsampled_extent; + } + _ => { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.format()` is a multi-planar format with three planes, \ + but `regions[{}].dst_subresource.aspect` is not \ + `ImageAspects::PLANE_0`, `ImageAspects::PLANE_1` or \ + `ImageAspects::PLANE_2`", + region_index, + ) + .into(), + vuids: &[ + "VUID-VkCopyImageInfo2-dstImage-08714", + "VUID-VkCopyImageInfo2-aspectMask-00143", + ], + ..Default::default() + })); + } + } + } + + match dst_image.image_type() { + ImageType::Dim1d => { + if dst_offset[1] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].dst_offset[1]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstImage-00152"], + ..Default::default() + })); + } + + if extent[1] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].extent[1]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstImage-00152"], + ..Default::default() + })); + } + + if dst_offset[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].dst_offset[2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstImage-01786"], + ..Default::default() + })); + } + + if extent[2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].extent[2]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstImage-01786"], + ..Default::default() + })); + } + } + ImageType::Dim2d => { + if dst_offset[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{}].dst_offset[2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstImage-01788"], + ..Default::default() + })); + } + } + ImageType::Dim3d => { + if dst_subresource.array_layers != (0..1) { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim3d`, but \ + `regions[{}].dst_subresource.array_layers` is not `0..1`", + region_index, + ) + .into(), + vuids: &[ + "VUID-VkCopyImageInfo2-dstImage-04444", + "VUID-VkCopyImageInfo2-apiVersion-07932", + ], + ..Default::default() + })); + } + } + } + + if dst_subresource.array_layers.end > dst_image.array_layers() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].dst_subresource.array_layers.end` is not less than \ + `dst_image.array_layers()`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstSubresource-07968"], + ..Default::default() + })); + } + + if dst_offset[0] + extent[0] > dst_subresource_extent[0] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_offset[0] + regions[{0}].extent[0]` is greater \ + than coordinate 0 of the extent of the subresource of `dst_image` \ + selected by `regions[{0}].dst_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstOffset-00150"], + ..Default::default() + })); + } + + if dst_offset[1] + extent[1] > dst_subresource_extent[1] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_offset[1] + regions[{0}].extent[1]` is greater \ + than coordinate 1 of the extent of the subresource of `dst_image` \ + selected by `regions[{0}].dst_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstOffset-00151"], + ..Default::default() + })); + } + + if dst_offset[2] + extent[2] > dst_subresource_extent[2] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_offset[2] + regions[{0}].extent[2]` is greater \ + than coordinate 2 of the extent of the subresource of `dst_image` \ + selected by `regions[{0}].dst_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstOffset-00153"], + ..Default::default() + })); + } + + let dst_subresource_format_block_extent = dst_subresource_format.block_extent(); + + if dst_offset[0] % dst_subresource_format_block_extent[0] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_offset[0]` is not a multiple of coordinate 0 of the \ + block extent of the format of the subresource of `dst_image` \ + selected by `regions[{0}].dst_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-pRegions-07281"], + ..Default::default() + })); + } + + if dst_offset[1] % dst_subresource_format_block_extent[1] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_offset[1]` is not a multiple of coordinate 1 of the \ + block extent of the format of the subresource of `dst_image` \ + selected by `regions[{0}].dst_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-pRegions-07282"], + ..Default::default() + })); + } + + if dst_offset[2] % dst_subresource_format_block_extent[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_offset[2]` is not a multiple of coordinate 2 of the \ + block extent of the format of the subresource of `dst_image` \ + selected by `regions[{0}].dst_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-pRegions-07283"], + ..Default::default() + })); + } + + if dst_offset[0] + extent[0] != dst_subresource_extent[0] + && (dst_offset[0] + extent[0]) % dst_subresource_format_block_extent[0] != 0 + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_offset[0] + regions[{0}].extent[0]` is not \ + equal to the extent of the subresource of `dst_image` \ + selected by `regions[{0}].dst_subresource`, but \ + it is also not a multiple of coordinate 0 of the block extent of the \ + format of that subresource", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstImage-01732"], + ..Default::default() + })); + } + + if dst_offset[1] + extent[1] != dst_subresource_extent[1] + && (dst_offset[1] + extent[1]) % dst_subresource_format_block_extent[1] != 0 + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_offset[1] + regions[{0}].extent[1]` is not \ + equal to the extent of the subresource of `dst_image` \ + selected by `regions[{0}].dst_subresource`, but \ + it is also not a multiple of coordinate 1 of the block extent of the \ + format of that subresource", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstImage-01733"], + ..Default::default() + })); + } + + if dst_offset[2] + extent[2] != dst_subresource_extent[2] + && (dst_offset[2] + extent[2]) % dst_subresource_format_block_extent[2] != 0 + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_offset[2] + regions[{0}].extent[2]` is not \ + equal to the extent of the subresource of `dst_image` \ + selected by `regions[{0}].dst_subresource`, but \ + it is also not a multiple of coordinate 2 of the block extent of the \ + format of that subresource", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstImage-01734"], + ..Default::default() + })); + } + + if !(dst_subresource.aspects - ImageAspects::STENCIL).is_empty() + && !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_subresource.aspects` contains aspects other than \ + `ImageAspects::STENCIL`, but \ + `dst_image.usage()` does not contain `ImageUsage::TRANSFER_DST`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-aspect-06663"], + ..Default::default() + })); + } + + if dst_subresource.aspects.intersects(ImageAspects::STENCIL) + && !dst_image + .stencil_usage() + .intersects(ImageUsage::TRANSFER_DST) + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_subresource.aspects` contains \ + `ImageAspects::STENCIL`, but \ + `dst_image.stencil_usage()` does not contain `ImageUsage::TRANSFER_DST`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-aspect-06665"], + ..Default::default() + })); + } + + /* + Check src and dst together + */ + + match ( + src_image_format_planes.is_empty(), + dst_image_format_planes.is_empty(), + ) { + (true, true) => { + if src_subresource.aspects != dst_subresource.aspects { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.format()` and `dst_image.format()` are both not \ + multi-planar formats, but \ + `regions[{0}].src_subresource.aspects` does not equal \ + `regions[{0}].dst_subresource.aspects`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-01551"], + ..Default::default() + })); + } + + if src_image_format.block_size() != dst_image_format.block_size() { + return Err(Box::new(ValidationError { + problem: "`src_image.format()` and `dst_image.format()` are both not \ + multi-planar formats, but \ + `src_image.format().block_size()` does not equal \ + `dst_image.format().block_size()`" + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-01548"], + ..Default::default() + })); + } + } + (false, true) => { + if dst_subresource.aspects != ImageAspects::COLOR { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.format()` is a multi-planar format, and \ + `dst_image.format()` is not a multi-planar format, but \ + `regions[{}].dst_subresource.aspects` is not \ + `ImageAspects::COLOR`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-01556"], + ..Default::default() + })); + } + + if src_subresource_format.block_size() != dst_image_format.block_size() { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.format()` is a multi-planar format, and \ + `dst_image.format()` is not a multi-planar format, but \ + the block size of the plane of `src_image.format()` selected by \ + `regions[{}].src_subresource.aspects` does not equal \ + `dst_image.format().block_size()`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-None-01549"], + ..Default::default() + })); + } + } + (true, false) => { + if src_subresource.aspects != ImageAspects::COLOR { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.format()` is not a multi-planar format, and \ + `dst_image.format()` is a multi-planar format, but \ + `regions[{}].src_subresource.aspects` is not \ + `ImageAspects::COLOR`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstImage-01557"], + ..Default::default() + })); + } + + if src_image_format.block_size() != dst_subresource_format.block_size() { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.format()` is not a multi-planar format, and \ + `dst_image.format()` is a multi-planar format, but \ + `src_image.format().block_size()` does not equal \ + the block size of the plane of `dst_image.format()` selected by \ + `regions[{}].dst_subresource.aspects`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-None-01549"], + ..Default::default() + })); + } + } + (false, false) => { + if src_subresource_format.block_size() != dst_subresource_format.block_size() { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.format()` and `dst_image.format()` are both \ + multi-planar formats, but \ + the block size of the plane of `src_image.format()` selected by \ + `regions[{0}].src_subresource.aspects` does not equal \ + the block size of the plane of `dst_image.format()` selected by \ + `regions[{0}].dst_subresource.aspects`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-None-01549"], + ..Default::default() + })); + } + } + } + + if src_image.image_type() == dst_image.image_type() { + if src_subresource.array_layers.len() != dst_subresource.array_layers.len() { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` equals `dst_image.image_type()`, but \ + the length of `regions[{0}].src_subresource.array_layers` \ + does not equal \ + the length of `regions[{0}].dst_subresource.array_layers`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-07744"], + ..Default::default() + })); + } + } + + match (src_image.image_type(), dst_image.image_type()) { + (ImageType::Dim2d, ImageType::Dim2d) => { + if extent[2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` and `dst_image.image_type()` are \ + both `ImageType::Dim2d`, but `regions[{}].extent[2]` is not 1", + region_index, + ) + .into(), + vuids: &[ + "VUID-VkCopyImageInfo2-srcImage-01790", + "VUID-VkCopyImageInfo2-apiVersion-08969", + ], + ..Default::default() + })); + } + } + (ImageType::Dim2d, ImageType::Dim3d) => { + if extent[2] as usize != src_subresource.array_layers.len() { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim2d` and \ + `dst_image.image_type()` is `ImageType::Dim3d`, but \ + `regions[{0}].extent[2]` does not equal the length of \ + `regions[{0}].src_subresource.array_layers`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-srcImage-01791"], + ..Default::default() + })); + } + } + (ImageType::Dim3d, ImageType::Dim2d) => { + if extent[2] as usize != dst_subresource.array_layers.len() { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim3d` and \ + `dst_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{0}].extent[2]` does not equal the length of \ + `regions[{0}].dst_subresource.array_layers`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-dstImage-01792"], + ..Default::default() + })); + } + } + _ => (), + } + + // VUID-VkCopyImageInfo2-pRegions-00124 + if is_same_image { + let src_region_index = region_index; + let src_subresource_axes = [ + src_subresource.mip_level..src_subresource.mip_level + 1, + src_subresource.array_layers.start..src_subresource.array_layers.end, + ]; + let src_extent_axes = [ + src_offset[0]..src_offset[0] + extent[0], + src_offset[1]..src_offset[1] + extent[1], + src_offset[2]..src_offset[2] + extent[2], + ]; + + for (dst_region_index, dst_region) in regions.iter().enumerate() { + let &ImageCopy { + ref dst_subresource, + dst_offset, + .. + } = dst_region; + + // For a single-plane image, the aspects must always be identical anyway + if src_image_format_aspects.intersects(ImageAspects::PLANE_0) + && src_subresource.aspects != dst_subresource.aspects + { + continue; + } + + let dst_subresource_axes = [ + dst_subresource.mip_level..dst_subresource.mip_level + 1, + src_subresource.array_layers.start..src_subresource.array_layers.end, + ]; + + if src_subresource_axes.iter().zip(dst_subresource_axes).any( + |(src_range, dst_range)| { + src_range.start >= dst_range.end || dst_range.start >= src_range.end + }, + ) { + continue; + } + + // If the subresource axes all overlap, then the source and destination must + // have the same layout. + overlap_subresource_indices = Some((src_region_index, dst_region_index)); + + let dst_extent_axes = [ + dst_offset[0]..dst_offset[0] + extent[0], + dst_offset[1]..dst_offset[1] + extent[1], + dst_offset[2]..dst_offset[2] + extent[2], + ]; + + // There is only overlap if all of the axes overlap. + if src_extent_axes + .iter() + .zip(dst_extent_axes) + .any(|(src_range, dst_range)| { + src_range.start >= dst_range.end || dst_range.start >= src_range.end + }) + { + continue; + } + + overlap_extent_indices = Some((src_region_index, dst_region_index)); + } + } + } + + if let Some((src_region_index, dst_region_index)) = overlap_extent_indices { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image` is equal to `dst_image`, and `regions[{0}].src_subresource` \ + overlaps with `regions[{1}].dst_subresource`, but \ + the `src_offset` and `extent` of `regions[{0}]` overlaps with \ + the `dst_offset` and `extent` of `regions[{1}]`", + src_region_index, dst_region_index + ) + .into(), + vuids: &["VUID-VkCopyImageInfo2-pRegions-00124"], + ..Default::default() + })); + } + + if let Some((src_region_index, dst_region_index)) = overlap_subresource_indices { + if src_image_layout != dst_image_layout { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image` is equal to `dst_image`, and `regions[{0}].src_subresource` \ + overlaps with `regions[{1}].dst_subresource`, but \ + `src_image_layout` does not equal `dst_image_layout`", + src_region_index, dst_region_index + ) + .into(), + vuids: &[ + "VUID-VkCopyImageInfo2-srcImageLayout-00128", + "VUID-VkCopyImageInfo2-dstImageLayout-00133", + ], + ..Default::default() + })); + } + } + + Ok(()) + } } /// A region of data to copy between images. @@ -3867,6 +3741,81 @@ impl Default for ImageCopy { } } +impl ImageCopy { + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + ref src_subresource, + src_offset: _, + ref dst_subresource, + dst_offset: _, + extent, + _ne, + } = self; + + src_subresource + .validate(device) + .map_err(|err| err.add_context("src_subresource"))?; + + dst_subresource + .validate(device) + .map_err(|err| err.add_context("dst_subresource"))?; + + if device.api_version() < Version::V1_1 { + if src_subresource.aspects != dst_subresource.aspects + && !device.enabled_extensions().khr_sampler_ycbcr_conversion + { + return Err(Box::new(ValidationError { + problem: "`src_subresource.aspects` does not equal `dst_subresource.aspects`" + .into(), + vuids: &["VUID-VkImageCopy2-apiVersion-07940"], + ..Default::default() + })); + } + + if src_subresource.array_layers.len() != dst_subresource.array_layers.len() + && !device.enabled_extensions().khr_maintenance1 + { + return Err(Box::new(ValidationError { + problem: "the length of `src_subresource.array_layers` does not equal \ + the length of `dst_subresource.array_layers`" + .into(), + vuids: &["VUID-VkImageCopy2-extent-00140"], + ..Default::default() + })); + } + } + + if extent[0] == 0 { + return Err(Box::new(ValidationError { + context: "extent[0]".into(), + problem: "is zero".into(), + vuids: &["VUID-VkImageCopy2-extent-06668"], + ..Default::default() + })); + } + + if extent[1] == 0 { + return Err(Box::new(ValidationError { + context: "extent[1]".into(), + problem: "is zero".into(), + vuids: &["VUID-VkImageCopy2-extent-06669"], + ..Default::default() + })); + } + + if extent[2] == 0 { + return Err(Box::new(ValidationError { + context: "extent[2]".into(), + problem: "is zero".into(), + vuids: &["VUID-VkImageCopy2-extent-06670"], + ..Default::default() + })); + } + + Ok(()) + } +} + /// Parameters to copy data from a buffer to an image. #[derive(Clone, Debug)] pub struct CopyBufferToImageInfo { @@ -3917,6 +3866,566 @@ impl CopyBufferToImageInfo { _ne: crate::NonExhaustive(()), } } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + ref src_buffer, + ref dst_image, + dst_image_layout, + ref regions, + _ne: _, + } = self; + + dst_image_layout + .validate_device(device) + .map_err(|err| ValidationError { + context: "dst_image_layout".into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-dstImageLayout-parameter"], + ..ValidationError::from_requirement(err) + })?; + + // VUID-VkCopyBufferToImageInfo2-commonparent + assert_eq!(device, src_buffer.device().as_ref()); + assert_eq!(device, dst_image.device().as_ref()); + + let dst_image_format = dst_image.format(); + let dst_image_format_aspects = dst_image_format.aspects(); + let dst_image_format_planes = dst_image_format.planes(); + let dst_image_format_subsampled_extent = dst_image_format + .ycbcr_chroma_sampling() + .map_or(dst_image.extent(), |s| { + s.subsampled_extent(dst_image.extent()) + }); + + if !src_buffer + .buffer() + .usage() + .intersects(BufferUsage::TRANSFER_SRC) + { + return Err(Box::new(ValidationError { + context: "src_buffer.buffer().usage()".into(), + problem: "does not contain `BufferUsage::TRANSFER_SRC`".into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-srcBuffer-00174"], + ..Default::default() + })); + } + + if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) { + return Err(Box::new(ValidationError { + context: "dst_image.usage()".into(), + problem: "does not contain `ImageUsage::TRANSFER_DST`".into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-00177"], + ..Default::default() + })); + } + + if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { + if !dst_image + .format_features() + .intersects(FormatFeatures::TRANSFER_DST) + { + return Err(Box::new(ValidationError { + context: "dst_image.format_features()".into(), + problem: "does not contain `FormatFeatures::TRANSFER_DST`".into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-01997"], + ..Default::default() + })); + } + } + + if dst_image.samples() != SampleCount::Sample1 { + return Err(Box::new(ValidationError { + context: "dst_image.samples()".into(), + problem: "is not `SampleCount::Sample1`".into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07973"], + ..Default::default() + })); + } + + if !matches!( + dst_image_layout, + ImageLayout::TransferDstOptimal | ImageLayout::General + ) { + return Err(Box::new(ValidationError { + context: "dst_image_layout".into(), + problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`" + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-dstImageLayout-01396"], + ..Default::default() + })); + } + + for (region_index, region) in regions.iter().enumerate() { + region + .validate(device) + .map_err(|err| err.add_context(format!("regions[{}]", region_index)))?; + + let &BufferImageCopy { + buffer_offset, + buffer_row_length, + buffer_image_height, + ref image_subresource, + image_offset, + image_extent, + _ne: _, + } = region; + + /* + Check image + */ + + if image_subresource.mip_level >= dst_image.mip_levels() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].image_subresource.mip_level` is not less than \ + `dst_image.mip_levels()`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-imageSubresource-01701"], + ..Default::default() + })); + } + + let mut image_subresource_format = dst_image_format; + let mut image_subresource_extent = + mip_level_extent(dst_image.extent(), image_subresource.mip_level).unwrap(); + + if dst_image_format_planes.is_empty() { + if !dst_image_format_aspects.contains(image_subresource.aspects) { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].image_subresource.aspects` is not a subset of \ + `dst_image.format().aspects()`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-aspectMask-00211"], + ..Default::default() + })); + } + } else if dst_image_format_planes.len() == 2 { + match image_subresource.aspects { + ImageAspects::PLANE_0 => { + image_subresource_format = dst_image_format_planes[0]; + } + ImageAspects::PLANE_1 => { + image_subresource_format = dst_image_format_planes[1]; + image_subresource_extent = dst_image_format_subsampled_extent; + } + _ => { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.format()` is a multi-planar format with two planes, \ + but `regions[{}].image_subresource.aspect` is not \ + `ImageAspects::PLANE_0` or `ImageAspects::PLANE_1`", + region_index, + ) + .into(), + vuids: &[ + "VUID-VkCopyBufferToImageInfo2-dstImage-07981", + "VUID-VkCopyBufferToImageInfo2-aspectMask-00211", + ], + ..Default::default() + })); + } + } + } else if dst_image_format_planes.len() == 3 { + match image_subresource.aspects { + ImageAspects::PLANE_0 => { + image_subresource_format = dst_image_format_planes[0]; + } + ImageAspects::PLANE_1 => { + image_subresource_format = dst_image_format_planes[1]; + image_subresource_extent = dst_image_format_subsampled_extent; + } + ImageAspects::PLANE_2 => { + image_subresource_format = dst_image_format_planes[2]; + image_subresource_extent = dst_image_format_subsampled_extent; + } + _ => { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.format()` is a multi-planar format with three planes, \ + but `regions[{}].image_subresource.aspect` is not \ + `ImageAspects::PLANE_0`, `ImageAspects::PLANE_1` or \ + `ImageAspects::PLANE_2`", + region_index, + ) + .into(), + vuids: &[ + "VUID-VkCopyBufferToImageInfo2-dstImage-07982", + "VUID-VkCopyBufferToImageInfo2-aspectMask-00211", + ], + ..Default::default() + })); + } + } + } + + match dst_image.image_type() { + ImageType::Dim1d => { + if image_offset[1] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].image_offset[1]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07979"], + ..Default::default() + })); + } + + if image_extent[1] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].image_extent[1]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07979"], + ..Default::default() + })); + } + + if image_offset[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].image_offset[2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07980"], + ..Default::default() + })); + } + + if image_extent[2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].image_extent[2]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07980"], + ..Default::default() + })); + } + } + ImageType::Dim2d => { + if image_offset[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{}].image_offset[2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07980"], + ..Default::default() + })); + } + + if image_extent[2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{}].image_extent[2]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07980"], + ..Default::default() + })); + } + } + ImageType::Dim3d => { + if image_subresource.array_layers != (0..1) { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim3d`, but \ + `regions[{}].image_subresource.array_layers` is not `0..1`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07983"], + ..Default::default() + })); + } + } + } + + if image_subresource.array_layers.end > dst_image.array_layers() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].dst_subresource.array_layers.end` is not less than \ + `dst_image.array_layers()`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-imageSubresource-07968"], + ..Default::default() + })); + } + + if image_offset[0] + image_extent[0] > image_subresource_extent[0] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[0] + regions[{0}].image_extent[0]` is greater \ + than coordinate 0 of the extent of the subresource of `dst_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-06223"], + ..Default::default() + })); + } + + if image_offset[1] + image_extent[1] > image_subresource_extent[1] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[1] + regions[{0}].image_extent[1]` is greater \ + than coordinate 1 of the extent of the subresource of `dst_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-06224"], + ..Default::default() + })); + } + + if image_offset[2] + image_extent[2] > image_subresource_extent[2] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[2] + regions[{0}].image_extent[2]` is greater \ + than coordinate 2 of the extent of the subresource of `dst_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-imageOffset-00200"], + ..Default::default() + })); + } + + let image_subresource_format_block_extent = image_subresource_format.block_extent(); + + if image_offset[0] % image_subresource_format_block_extent[0] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[0]` is not a multiple of coordinate 0 of the \ + block extent of the format of the subresource of `dst_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-07274"], + ..Default::default() + })); + } + + if image_offset[1] % image_subresource_format_block_extent[1] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[1]` is not a multiple of coordinate 1 of the \ + block extent of the format of the subresource of `dst_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-07275"], + ..Default::default() + })); + } + + if image_offset[2] % image_subresource_format_block_extent[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[2]` is not a multiple of coordinate 2 of the \ + block extent of the format of the subresource of `dst_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-07276"], + ..Default::default() + })); + } + + if image_offset[0] + image_extent[0] != image_subresource_extent[0] + && (image_offset[0] + image_extent[0]) % image_subresource_format_block_extent[0] + != 0 + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[0] + regions[{0}].image_extent[0]` is not \ + equal to the extent of the subresource of `dst_image` \ + selected by `regions[{0}].image_subresource`, but \ + it is also not a multiple of coordinate 0 of the block extent of the \ + format of that subresource", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-imageExtent-00207"], + ..Default::default() + })); + } + + if image_offset[1] + image_extent[1] != image_subresource_extent[1] + && (image_offset[1] + image_extent[1]) % image_subresource_format_block_extent[1] + != 0 + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[1] + regions[{0}].image_extent[1]` is not \ + equal to the extent of the subresource of `dst_image` \ + selected by `regions[{0}].image_subresource`, but \ + it is also not a multiple of coordinate 1 of the block extent of the \ + format of that subresource", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-imageExtent-00208"], + ..Default::default() + })); + } + + if image_offset[2] + image_extent[2] != image_subresource_extent[2] + && (image_offset[2] + image_extent[2]) % image_subresource_format_block_extent[2] + != 0 + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[2] + regions[{0}].image_extent[2]` is not \ + equal to the extent of the subresource of `dst_image` \ + selected by `regions[{0}].image_subresource`, but \ + it is also not a multiple of coordinate 2 of the block extent of the \ + format of that subresource", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-imageExtent-00209"], + ..Default::default() + })); + } + + /* + Check buffer and image together + */ + + let image_subresource_format_block_size = image_subresource_format.block_size(); + + if dst_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { + if (src_buffer.offset() + buffer_offset) % 4 != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.format()` is a depth/stencil format, but \ + `src_buffer.offset() + regions[{0}].buffer_offset` is not a \ + multiple of 4", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07978"], + ..Default::default() + })); + } + } else { + if (src_buffer.offset() + buffer_offset) % image_subresource_format_block_size != 0 + { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.format()` is not a depth/stencil format, but \ + `src_buffer.offset() + regions[{0}].buffer_offset` is not a \ + multiple of the block size of the format of the subresource of \ + `dst_image` selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &[ + "VUID-VkCopyBufferToImageInfo2-dstImage-07975", + "VUID-VkCopyBufferToImageInfo2-dstImage-07976", + ], + ..Default::default() + })); + } + } + + if buffer_row_length % image_subresource_format_block_extent[0] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].buffer_row_length` is not a multiple of coordinate 0 of \ + the block extent of the format of the subresource of `dst_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-bufferRowLength-00203"], + ..Default::default() + })); + } + + if buffer_image_height % image_subresource_format_block_extent[1] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].buffer_image_height` is not a multiple of coordinate 1 of \ + the block extent of the format of the subresource of `dst_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-bufferImageHeight-00204"], + ..Default::default() + })); + } + + if (buffer_row_length / image_subresource_format_block_extent[0]) as DeviceSize + * image_subresource_format_block_size + > 0x7FFFFFFF + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].buffer_row_length`, divided by the block size of the \ + format of the subresource of `dst_image` selected by \ + `regions[{0}].image_subresource`, and then multiplied by the block size \ + of that subresource, is greater than 0x7FFFFFFF", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-bufferRowLength-00203"], + ..Default::default() + })); + } + + if buffer_offset + region.buffer_copy_size(image_subresource_format) > src_buffer.size() + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].buffer_offset` plus the number of bytes being copied \ + is greater than `src_buffer.size()`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-00171"], + ..Default::default() + })); + } + } + + // VUID-VkCopyBufferToImageInfo2-pRegions-00173 + // Can't occur as long as memory aliasing isn't allowed. + + // VUID-VkCopyBufferToImageInfo2-pRegions-07931 + // Unsafe, can't validate + + Ok(()) + } } /// Parameters to copy data from an image to a buffer. @@ -3969,6 +4478,563 @@ impl CopyImageToBufferInfo { _ne: crate::NonExhaustive(()), } } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + ref src_image, + src_image_layout, + ref dst_buffer, + ref regions, + _ne: _, + } = self; + + src_image_layout + .validate_device(device) + .map_err(|err| ValidationError { + context: "src_image_layout".into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-srcImageLayout-parameter"], + ..ValidationError::from_requirement(err) + })?; + + // VUID-VkCopyImageToBufferInfo2-commonparent + assert_eq!(device, src_image.device().as_ref()); + assert_eq!(device, dst_buffer.device().as_ref()); + + let src_image_format = src_image.format(); + let src_image_format_aspects = src_image_format.aspects(); + let src_image_format_planes = src_image_format.planes(); + let src_image_format_subsampled_extent = src_image_format + .ycbcr_chroma_sampling() + .map_or(src_image.extent(), |s| { + s.subsampled_extent(src_image.extent()) + }); + + if !dst_buffer + .buffer() + .usage() + .intersects(BufferUsage::TRANSFER_DST) + { + return Err(Box::new(ValidationError { + context: "dst_buffer.buffer().usage()".into(), + problem: "does not contain `BufferUsage::TRANSFER_DST`".into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-dstBuffer-00191"], + ..Default::default() + })); + } + + if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) { + return Err(Box::new(ValidationError { + context: "src_image.usage()".into(), + problem: "does not contain `ImageUsage::TRANSFER_SRC`".into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-00186"], + ..Default::default() + })); + } + + if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { + if !src_image + .format_features() + .intersects(FormatFeatures::TRANSFER_SRC) + { + return Err(Box::new(ValidationError { + context: "src_image.format_features()".into(), + problem: "does not contain `FormatFeatures::TRANSFER_SRC`".into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-01998"], + ..Default::default() + })); + } + } + + if src_image.samples() != SampleCount::Sample1 { + return Err(Box::new(ValidationError { + context: "src_image.samples()".into(), + problem: "is not `SampleCount::Sample1`".into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07973"], + ..Default::default() + })); + } + + if !matches!( + src_image_layout, + ImageLayout::TransferSrcOptimal | ImageLayout::General + ) { + return Err(Box::new(ValidationError { + context: "src_image_layout".into(), + problem: "is not `ImageLayout::TransferSrcOptimal` or `ImageLayout::General`" + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-srcImageLayout-01397"], + ..Default::default() + })); + } + + for (region_index, region) in regions.iter().enumerate() { + region + .validate(device) + .map_err(|err| err.add_context(format!("regions[{}]", region_index)))?; + + let &BufferImageCopy { + buffer_offset, + buffer_row_length, + buffer_image_height, + ref image_subresource, + image_offset, + image_extent, + _ne: _, + } = region; + + /* + Check image + */ + + if image_subresource.mip_level >= src_image.mip_levels() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].image_subresource.mip_level` is not less than \ + `src_image.mip_levels()`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-imageSubresource-07967"], + ..Default::default() + })); + } + + let mut image_subresource_format = src_image_format; + let mut image_subresource_extent = + mip_level_extent(src_image.extent(), image_subresource.mip_level).unwrap(); + + if src_image_format_planes.is_empty() { + if !src_image_format_aspects.contains(image_subresource.aspects) { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].image_subresource.aspects` is not a subset of \ + `src_image.format().aspects()`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-aspectMask-00211"], + ..Default::default() + })); + } + } else if src_image_format_planes.len() == 2 { + match image_subresource.aspects { + ImageAspects::PLANE_0 => { + image_subresource_format = src_image_format_planes[0]; + } + ImageAspects::PLANE_1 => { + image_subresource_format = src_image_format_planes[1]; + image_subresource_extent = src_image_format_subsampled_extent; + } + _ => { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.format()` is a multi-planar format with two planes, \ + but `regions[{}].image_subresource.aspect` is not \ + `ImageAspects::PLANE_0` or `ImageAspects::PLANE_1`", + region_index, + ) + .into(), + vuids: &[ + "VUID-VkCopyImageToBufferInfo2-srcImage-07981", + "VUID-VkCopyImageToBufferInfo2-aspectMask-00211", + ], + ..Default::default() + })); + } + } + } else if src_image_format_planes.len() == 3 { + match image_subresource.aspects { + ImageAspects::PLANE_0 => { + image_subresource_format = src_image_format_planes[0]; + } + ImageAspects::PLANE_1 => { + image_subresource_format = src_image_format_planes[1]; + image_subresource_extent = src_image_format_subsampled_extent; + } + ImageAspects::PLANE_2 => { + image_subresource_format = src_image_format_planes[2]; + image_subresource_extent = src_image_format_subsampled_extent; + } + _ => { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.format()` is a multi-planar format with three planes, \ + but `regions[{}].image_subresource.aspect` is not \ + `ImageAspects::PLANE_0`, `ImageAspects::PLANE_1` or \ + `ImageAspects::PLANE_2`", + region_index, + ) + .into(), + vuids: &[ + "VUID-VkCopyImageToBufferInfo2-srcImage-07982", + "VUID-VkCopyImageToBufferInfo2-aspectMask-00211", + ], + ..Default::default() + })); + } + } + } + + match src_image.image_type() { + ImageType::Dim1d => { + if image_offset[1] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].image_offset[1]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07979"], + ..Default::default() + })); + } + + if image_extent[1] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].image_extent[1]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07979"], + ..Default::default() + })); + } + + if image_offset[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].image_offset[2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07980"], + ..Default::default() + })); + } + + if image_extent[2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].image_extent[2]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07980"], + ..Default::default() + })); + } + } + ImageType::Dim2d => { + if image_offset[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{}].image_offset[2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07980"], + ..Default::default() + })); + } + + if image_extent[2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{}].image_extent[2]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07980"], + ..Default::default() + })); + } + } + ImageType::Dim3d => { + if image_subresource.array_layers != (0..1) { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim3d`, but \ + `regions[{}].image_subresource.array_layers` is not `0..1`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07983"], + ..Default::default() + })); + } + } + } + + if image_subresource.array_layers.end > src_image.array_layers() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].dst_subresource.array_layers.end` is not less than \ + `src_image.array_layers()`", + region_index + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-imageSubresource-07968"], + ..Default::default() + })); + } + + if image_offset[0] + image_extent[0] > image_subresource_extent[0] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[0] + regions[{0}].image_extent[0]` is greater \ + than coordinate 0 of the extent of the subresource of `src_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-imageOffset-00197"], + ..Default::default() + })); + } + + if image_offset[1] + image_extent[1] > image_subresource_extent[1] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[1] + regions[{0}].image_extent[1]` is greater \ + than coordinate 1 of the extent of the subresource of `src_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-imageOffset-00198"], + ..Default::default() + })); + } + + if image_offset[2] + image_extent[2] > image_subresource_extent[2] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[2] + regions[{0}].image_extent[2]` is greater \ + than coordinate 2 of the extent of the subresource of `src_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-imageOffset-00200"], + ..Default::default() + })); + } + + let image_subresource_format_block_extent = image_subresource_format.block_extent(); + + if image_offset[0] % image_subresource_format_block_extent[0] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[0]` is not a multiple of coordinate 0 of the \ + block extent of the format of the subresource of `src_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-07274"], + ..Default::default() + })); + } + + if image_offset[1] % image_subresource_format_block_extent[1] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[1]` is not a multiple of coordinate 1 of the \ + block extent of the format of the subresource of `src_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-07275"], + ..Default::default() + })); + } + + if image_offset[2] % image_subresource_format_block_extent[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[2]` is not a multiple of coordinate 2 of the \ + block extent of the format of the subresource of `src_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-07276"], + ..Default::default() + })); + } + + if image_offset[0] + image_extent[0] != image_subresource_extent[0] + && (image_offset[0] + image_extent[0]) % image_subresource_format_block_extent[0] + != 0 + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[0] + regions[{0}].image_extent[0]` is not \ + equal to the extent of the subresource of `src_image` \ + selected by `regions[{0}].image_subresource`, but \ + it is also not a multiple of coordinate 0 of the block extent of the \ + format of that subresource", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-imageExtent-00207"], + ..Default::default() + })); + } + + if image_offset[1] + image_extent[1] != image_subresource_extent[1] + && (image_offset[1] + image_extent[1]) % image_subresource_format_block_extent[1] + != 0 + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[1] + regions[{0}].image_extent[1]` is not \ + equal to the extent of the subresource of `src_image` \ + selected by `regions[{0}].image_subresource`, but \ + it is also not a multiple of coordinate 1 of the block extent of the \ + format of that subresource", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-imageExtent-00208"], + ..Default::default() + })); + } + + if image_offset[2] + image_extent[2] != image_subresource_extent[2] + && (image_offset[2] + image_extent[2]) % image_subresource_format_block_extent[2] + != 0 + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].image_offset[2] + regions[{0}].image_extent[2]` is not \ + equal to the extent of the subresource of `src_image` \ + selected by `regions[{0}].image_subresource`, but \ + it is also not a multiple of coordinate 2 of the block extent of the \ + format of that subresource", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-imageExtent-00209"], + ..Default::default() + })); + } + + /* + Check buffer and image together + */ + + let image_subresource_format_block_size = image_subresource_format.block_size(); + + if src_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { + if (dst_buffer.offset() + buffer_offset) % 4 != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.format()` is a depth/stencil format, but \ + `dst_buffer.offset() + regions[{0}].buffer_offset` is not a \ + multiple of 4", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07978"], + ..Default::default() + })); + } + } else { + if (dst_buffer.offset() + buffer_offset) % image_subresource_format_block_size != 0 + { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.format()` is not a depth/stencil format, but \ + `dst_buffer.offset() + regions[{0}].buffer_offset` is not a \ + multiple of the block size of the format of the subresource of \ + `src_image` selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &[ + "VUID-VkCopyImageToBufferInfo2-srcImage-07975", + "VUID-VkCopyImageToBufferInfo2-srcImage-07976", + ], + ..Default::default() + })); + } + } + + if buffer_row_length % image_subresource_format_block_extent[0] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].buffer_row_length` is not a multiple of coordinate 0 of \ + the block extent of the format of the subresource of `src_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-bufferRowLength-00203"], + ..Default::default() + })); + } + + if buffer_image_height % image_subresource_format_block_extent[1] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].buffer_image_height` is not a multiple of coordinate 1 of \ + the block extent of the format of the subresource of `src_image` \ + selected by `regions[{0}].image_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-bufferImageHeight-00204"], + ..Default::default() + })); + } + + if (buffer_row_length / image_subresource_format_block_extent[0]) as DeviceSize + * image_subresource_format_block_size + > 0x7FFFFFFF + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].buffer_row_length`, divided by the block size of the \ + format of the subresource of `src_image` selected by \ + `regions[{0}].image_subresource`, and then multiplied by the block size \ + of that subresource, is greater than 0x7FFFFFFF", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-07277"], + ..Default::default() + })); + } + + if buffer_offset + region.buffer_copy_size(image_subresource_format) > dst_buffer.size() + { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].buffer_offset` plus the number of bytes being copied \ + is greater than `dst_buffer.size()`", + region_index, + ) + .into(), + vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-00183"], + ..Default::default() + })); + } + } + + // VUID-VkCopyImageToBufferInfo2-pRegions-00184 + // Can't occur as long as memory aliasing isn't allowed. + + Ok(()) + } } /// A region of data to copy between a buffer and an image. @@ -4074,23 +5140,81 @@ impl BufferImageCopy { (image_extent[1] as DeviceSize - 1) * buffer_row_length as DeviceSize; let num_blocks = blocks_to_last_slice + blocks_to_last_row + image_extent[0] as DeviceSize; - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBufferImageCopy.html#_description - let block_size = if image_subresource.aspects.intersects(ImageAspects::STENCIL) { - 1 - } else if image_subresource.aspects.intersects(ImageAspects::DEPTH) { - match format { - Format::D16_UNORM | Format::D16_UNORM_S8_UINT => 2, - Format::D32_SFLOAT - | Format::D32_SFLOAT_S8_UINT - | Format::X8_D24_UNORM_PACK32 - | Format::D24_UNORM_S8_UINT => 4, - _ => unreachable!(), - } - } else { - format.block_size() - }; + num_blocks * format.block_size() + } - num_blocks * block_size + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + buffer_offset: _, + buffer_row_length, + buffer_image_height, + ref image_subresource, + image_offset: _, + image_extent, + _ne: _, + } = self; + + image_subresource + .validate(device) + .map_err(|err| err.add_context("image_subresource"))?; + + if !(buffer_row_length == 0 || buffer_row_length >= image_extent[0]) { + return Err(Box::new(ValidationError { + problem: "`buffer_row_length` is not either zero, or greater than or equal to \ + `image_extent[0]`" + .into(), + vuids: &["VUID-VkBufferImageCopy2-bufferRowLength-00195"], + ..Default::default() + })); + } + + if !(buffer_image_height == 0 || buffer_image_height >= image_extent[1]) { + return Err(Box::new(ValidationError { + problem: "`buffer_image_height` is not either zero, or greater than or equal to \ + `image_extent[1]`" + .into(), + vuids: &["VUID-VkBufferImageCopy2-bufferImageHeight-00196"], + ..Default::default() + })); + } + + if image_subresource.aspects.count() != 1 { + return Err(Box::new(ValidationError { + context: "image_subresource.aspects".into(), + problem: "contains more than one aspect".into(), + vuids: &["VUID-VkBufferImageCopy2-aspectMask-00212"], + ..Default::default() + })); + } + + if image_extent[0] == 0 { + return Err(Box::new(ValidationError { + context: "image_extent[0]".into(), + problem: "is zero".into(), + vuids: &["VUID-VkBufferImageCopy2-imageExtent-06659"], + ..Default::default() + })); + } + + if image_extent[1] == 0 { + return Err(Box::new(ValidationError { + context: "image_extent[1]".into(), + problem: "is zero".into(), + vuids: &["VUID-VkBufferImageCopy2-imageExtent-06660"], + ..Default::default() + })); + } + + if image_extent[2] == 0 { + return Err(Box::new(ValidationError { + context: "image_extent[2]".into(), + problem: "is zero".into(), + vuids: &["VUID-VkBufferImageCopy2-imageExtent-06661"], + ..Default::default() + })); + } + + Ok(()) } } @@ -4170,6 +5294,747 @@ impl BlitImageInfo { _ne: crate::NonExhaustive(()), } } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + ref src_image, + src_image_layout, + ref dst_image, + dst_image_layout, + ref regions, + filter, + _ne: _, + } = self; + + src_image_layout + .validate_device(device) + .map_err(|err| ValidationError { + context: "src_image_layout".into(), + vuids: &["VUID-VkBlitImageInfo2-srcImageLayout-parameter"], + ..ValidationError::from_requirement(err) + })?; + + dst_image_layout + .validate_device(device) + .map_err(|err| ValidationError { + context: "dst_image_layout".into(), + vuids: &["VUID-VkBlitImageInfo2-dstImageLayout-parameter"], + ..ValidationError::from_requirement(err) + })?; + + filter + .validate_device(device) + .map_err(|err| ValidationError { + context: "filter".into(), + vuids: &["VUID-VkBlitImageInfo2-filter-parameter"], + ..ValidationError::from_requirement(err) + })?; + + // VUID-VkBlitImageInfo2-commonparent + assert_eq!(device, src_image.device().as_ref()); + assert_eq!(device, dst_image.device().as_ref()); + + let src_image_format = src_image.format(); + let src_image_format_aspects = src_image_format.aspects(); + + let dst_image_format = dst_image.format(); + let dst_image_format_aspects = dst_image_format.aspects(); + + if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) { + return Err(Box::new(ValidationError { + context: "src_image.usage()".into(), + problem: "does not contain `ImageUsage::TRANSFER_SRC`".into(), + vuids: &["VUID-VkBlitImageInfo2-srcImage-00219"], + ..Default::default() + })); + } + + if !src_image + .format_features() + .intersects(FormatFeatures::BLIT_SRC) + { + return Err(Box::new(ValidationError { + context: "src_image.format_features()".into(), + problem: "does not contain `FormatFeatures::BLIT_SRC`".into(), + vuids: &["VUID-VkBlitImageInfo2-srcImage-01999"], + ..Default::default() + })); + } + + if src_image_format.ycbcr_chroma_sampling().is_some() { + return Err(Box::new(ValidationError { + context: "src_image.format()".into(), + problem: "is a YCbCr format".into(), + vuids: &["VUID-VkBlitImageInfo2-srcImage-06421"], + ..Default::default() + })); + } + + if src_image.samples() != SampleCount::Sample1 { + return Err(Box::new(ValidationError { + context: "src_image.samples()".into(), + problem: "is not `SampleCount::Sample1`".into(), + vuids: &["VUID-VkBlitImageInfo2-srcImage-00233"], + ..Default::default() + })); + } + + if !matches!( + src_image_layout, + ImageLayout::TransferSrcOptimal | ImageLayout::General + ) { + return Err(Box::new(ValidationError { + context: "src_image_layout".into(), + problem: "is not `ImageLayout::TransferSrcOptimal` or `ImageLayout::General`" + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcImageLayout-01398"], + ..Default::default() + })); + } + + if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) { + return Err(Box::new(ValidationError { + context: "dst_image.usage()".into(), + problem: "does not contain `ImageUsage::TRANSFER_DST`".into(), + vuids: &["VUID-VkBlitImageInfo2-dstImage-00224"], + ..Default::default() + })); + } + + if !dst_image + .format_features() + .intersects(FormatFeatures::BLIT_DST) + { + return Err(Box::new(ValidationError { + context: "dst_image.format_features()".into(), + problem: "does not contain `FormatFeatures::BLIT_DST`".into(), + vuids: &["VUID-VkBlitImageInfo2-dstImage-02000"], + ..Default::default() + })); + } + + if dst_image_format.ycbcr_chroma_sampling().is_some() { + return Err(Box::new(ValidationError { + context: "dst_image.format()".into(), + problem: "is a YCbCr format".into(), + vuids: &["VUID-VkBlitImageInfo2-dstImage-06422"], + ..Default::default() + })); + } + + if dst_image.samples() != SampleCount::Sample1 { + return Err(Box::new(ValidationError { + context: "dst_image.samples()".into(), + problem: "is not `SampleCount::Sample1`".into(), + vuids: &["VUID-VkBlitImageInfo2-dstImage-00234"], + ..Default::default() + })); + } + + if !matches!( + dst_image_layout, + ImageLayout::TransferDstOptimal | ImageLayout::General + ) { + return Err(Box::new(ValidationError { + context: "dst_image_layout".into(), + problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`" + .into(), + vuids: &["VUID-VkBlitImageInfo2-dstImageLayout-01399"], + ..Default::default() + })); + } + + if src_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) + || dst_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) + { + if src_image_format != dst_image_format { + return Err(Box::new(ValidationError { + problem: "one of `src_image.format()` or `dst_image.format()` is a \ + depth/stencil format, but they are not equal" + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcImage-00231"], + ..Default::default() + })); + } + } else { + if src_image_format + .numeric_format_color() + .unwrap() + .numeric_type() + != dst_image_format + .numeric_format_color() + .unwrap() + .numeric_type() + { + return Err(Box::new(ValidationError { + problem: "neither `src_image.format()` nor `dst_image.format()` is a \ + depth/stencil format, but their numeric types are not equal" + .into(), + vuids: &[ + "VUID-VkBlitImageInfo2-srcImage-00229", + "VUID-VkBlitImageInfo2-srcImage-00230", + ], + ..Default::default() + })); + } + } + + if src_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) + && filter != Filter::Nearest + { + return Err(Box::new(ValidationError { + problem: "`src_image.format()` is a depth/stencil format, but \ + `filter` is not `Filter::Nearest`" + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcImage-00232"], + ..Default::default() + })); + } + + match filter { + Filter::Nearest => (), + Filter::Linear => { + if !src_image + .format_features() + .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_LINEAR) + { + return Err(Box::new(ValidationError { + problem: "`filter` is `Filter::Linear`, but \ + `src_image.format_features()` do not contain \ + `FormatFeatures::SAMPLED_IMAGE_FILTER_LINEAR`" + .into(), + vuids: &["VUID-VkBlitImageInfo2-filter-02001"], + ..Default::default() + })); + } + } + Filter::Cubic => { + if !src_image + .format_features() + .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_CUBIC) + { + return Err(Box::new(ValidationError { + problem: "`filter` is `Filter::Cubic`, but \ + `src_image.format_features()` do not contain \ + `FormatFeatures::SAMPLED_IMAGE_FILTER_CUBIC`" + .into(), + vuids: &["VUID-VkBlitImageInfo2-filter-02002"], + ..Default::default() + })); + } + + if src_image.image_type() != ImageType::Dim2d { + return Err(Box::new(ValidationError { + problem: "`filter` is `Filter::Cubic`, but \ + `src_image.image_type()` is not `ImageType::Dim2d`" + .into(), + vuids: &["VUID-VkBlitImageInfo2-filter-00237"], + ..Default::default() + })); + } + } + } + + let is_same_image = src_image == dst_image; + let mut overlap_subresource_indices = None; + let mut overlap_extent_indices = None; + + for (region_index, region) in regions.iter().enumerate() { + region + .validate(device) + .map_err(|err| err.add_context(format!("regions[{}]", region_index)))?; + + let &ImageBlit { + ref src_subresource, + src_offsets, + ref dst_subresource, + dst_offsets, + _ne: _, + } = region; + + /* + Check src + */ + + if src_subresource.mip_level >= src_image.mip_levels() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].src_subresource.mip_level` is not less than \ + `src_image.mip_levels()`", + region_index + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcSubresource-01705"], + ..Default::default() + })); + } + + let src_subresource_extent = + mip_level_extent(src_image.extent(), src_subresource.mip_level).unwrap(); + + if !src_image_format_aspects.contains(src_subresource.aspects) { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].src_subresource.aspects` is not a subset of \ + `src_image.format().aspects()`", + region_index + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-aspectMask-00241"], + ..Default::default() + })); + } + + match src_image.image_type() { + ImageType::Dim1d => { + if src_offsets[0][1] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].src_offsets[0][1]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcImage-00245"], + ..Default::default() + })); + } + + if src_offsets[1][1] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].src_offsets[1][1]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcImage-00245"], + ..Default::default() + })); + } + + if src_offsets[0][2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].src_offsets[0][2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcImage-00247"], + ..Default::default() + })); + } + + if src_offsets[1][2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].src_offsets[1][2]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcImage-00247"], + ..Default::default() + })); + } + } + ImageType::Dim2d => { + if src_offsets[0][2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{}].src_offsets[0][2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcImage-00247"], + ..Default::default() + })); + } + + if src_offsets[1][2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{}].src_offsets[1][2]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcImage-00247"], + ..Default::default() + })); + } + } + ImageType::Dim3d => { + if src_subresource.array_layers != (0..1) { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim3d`, but \ + `regions[{}].src_subresource.array_layers` is not `0..1`", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcImage-00240"], + ..Default::default() + })); + } + } + } + + if src_subresource.array_layers.end > src_image.array_layers() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].src_subresource.array_layers.end` is not less than \ + `src_image.array_layers()`", + region_index + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcSubresource-01707"], + ..Default::default() + })); + } + + let src_offsets_max = [ + max(src_offsets[0][0], src_offsets[1][0]), + max(src_offsets[0][1], src_offsets[1][1]), + max(src_offsets[0][2], src_offsets[1][2]), + ]; + + if src_offsets_max[0] > src_subresource_extent[0] { + return Err(Box::new(ValidationError { + problem: format!( + "`max(regions[{0}].src_offsets[0][0], regions[{0}].src_offsets[1][0])` is \ + greater than coordinate 0 of the extent of the subresource of \ + `src_image` selected by `regions[{0}].src_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcOffset-00243"], + ..Default::default() + })); + } + + if src_offsets_max[1] > src_subresource_extent[1] { + return Err(Box::new(ValidationError { + problem: format!( + "`max(regions[{0}].src_offsets[0][1], regions[{0}].src_offsets[1][1])` is \ + greater than coordinate 1 of the extent of the subresource of \ + `src_image` selected by `regions[{0}].src_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcOffset-00244"], + ..Default::default() + })); + } + + if src_offsets_max[2] > src_subresource_extent[2] { + return Err(Box::new(ValidationError { + problem: format!( + "`max(regions[{0}].src_offsets[0][2], regions[{0}].src_offsets[1][2])` is \ + greater than coordinate 2 of the extent of the subresource of \ + `src_image` selected by `regions[{0}].src_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcOffset-00246"], + ..Default::default() + })); + } + + /* + Check dst + */ + + if dst_subresource.mip_level >= dst_image.mip_levels() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].dst_subresource.mip_level` is not less than \ + `dst_image.mip_levels()`", + region_index + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcSubresource-01705"], + ..Default::default() + })); + } + + let dst_subresource_extent = + mip_level_extent(dst_image.extent(), dst_subresource.mip_level).unwrap(); + + if !dst_image_format_aspects.contains(dst_subresource.aspects) { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].dst_subresource.aspects` is not a subset of \ + `dst_image.format().aspects()`", + region_index + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-aspectMask-00242"], + ..Default::default() + })); + } + + match dst_image.image_type() { + ImageType::Dim1d => { + if dst_offsets[0][1] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].dst_offsets[0][1]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-dstImage-00250"], + ..Default::default() + })); + } + + if dst_offsets[1][1] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].dst_offsets[1][1]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-dstImage-00250"], + ..Default::default() + })); + } + + if dst_offsets[0][2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].dst_offsets[0][2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-dstImage-00252"], + ..Default::default() + })); + } + + if dst_offsets[1][2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].dst_offsets[1][2]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-dstImage-00252"], + ..Default::default() + })); + } + } + ImageType::Dim2d => { + if dst_offsets[0][2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{}].dst_offsets[0][2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-dstImage-00252"], + ..Default::default() + })); + } + + if dst_offsets[1][2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{}].dst_offsets[1][2]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-dstImage-00252"], + ..Default::default() + })); + } + } + ImageType::Dim3d => { + if dst_subresource.array_layers != (0..1) { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim3d`, but \ + `regions[{}].dst_subresource.array_layers` is not `0..1`", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcImage-00240"], + ..Default::default() + })); + } + } + } + + if dst_subresource.array_layers.end > dst_image.array_layers() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].dst_subresource.array_layers.end` is not less than \ + `dst_image.array_layers()`", + region_index + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-srcSubresource-01707"], + ..Default::default() + })); + } + + let dst_offsets_max = [ + max(dst_offsets[0][0], dst_offsets[1][0]), + max(dst_offsets[0][1], dst_offsets[1][1]), + max(dst_offsets[0][2], dst_offsets[1][2]), + ]; + + if dst_offsets_max[0] > dst_subresource_extent[0] { + return Err(Box::new(ValidationError { + problem: format!( + "`max(regions[{0}].dst_offsets[0][0], regions[{0}].dst_offsets[1][0])` is \ + greater than coordinate 0 of the extent of the subresource of \ + `dst_image` selected by `regions[{0}].dst_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-dstOffset-00248"], + ..Default::default() + })); + } + + if dst_offsets_max[1] > dst_subresource_extent[1] { + return Err(Box::new(ValidationError { + problem: format!( + "`max(regions[{0}].dst_offsets[0][1], regions[{0}].dst_offsets[1][1])` is \ + greater than coordinate 1 of the extent of the subresource of \ + `dst_image` selected by `regions[{0}].dst_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-dstOffset-00249"], + ..Default::default() + })); + } + + if dst_offsets_max[2] > dst_subresource_extent[2] { + return Err(Box::new(ValidationError { + problem: format!( + "`max(regions[{0}].dst_offsets[0][2], regions[{0}].dst_offsets[1][2])` is \ + greater than coordinate 2 of the extent of the subresource of \ + `dst_image` selected by `regions[{0}].dst_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-dstOffset-00251"], + ..Default::default() + })); + } + + // VUID-VkBlitImageInfo2-pRegions-00217 + if is_same_image { + let src_region_index = region_index; + let src_subresource_axes = [ + src_subresource.mip_level..src_subresource.mip_level + 1, + src_subresource.array_layers.start..src_subresource.array_layers.end, + ]; + let src_extent_axes = [ + min(src_offsets[0][0], src_offsets[1][0]) + ..max(src_offsets[0][0], src_offsets[1][0]), + min(src_offsets[0][1], src_offsets[1][1]) + ..max(src_offsets[0][1], src_offsets[1][1]), + min(src_offsets[0][2], src_offsets[1][2]) + ..max(src_offsets[0][2], src_offsets[1][2]), + ]; + + for (dst_region_index, dst_region) in regions.iter().enumerate() { + let &ImageBlit { + ref dst_subresource, + dst_offsets, + .. + } = dst_region; + + let dst_subresource_axes = [ + dst_subresource.mip_level..dst_subresource.mip_level + 1, + src_subresource.array_layers.start..src_subresource.array_layers.end, + ]; + + if src_subresource_axes.iter().zip(dst_subresource_axes).any( + |(src_range, dst_range)| { + src_range.start >= dst_range.end || dst_range.start >= src_range.end + }, + ) { + continue; + } + + // If the subresource axes all overlap, then the source and destination must + // have the same layout. + overlap_subresource_indices = Some((src_region_index, dst_region_index)); + + let dst_extent_axes = [ + min(dst_offsets[0][0], dst_offsets[1][0]) + ..max(dst_offsets[0][0], dst_offsets[1][0]), + min(dst_offsets[0][1], dst_offsets[1][1]) + ..max(dst_offsets[0][1], dst_offsets[1][1]), + min(dst_offsets[0][2], dst_offsets[1][2]) + ..max(dst_offsets[0][2], dst_offsets[1][2]), + ]; + + if src_extent_axes + .iter() + .zip(dst_extent_axes) + .any(|(src_range, dst_range)| { + src_range.start >= dst_range.end || dst_range.start >= src_range.end + }) + { + continue; + } + + // If the extent axes *also* overlap, then that's an error. + overlap_extent_indices = Some((src_region_index, dst_region_index)); + } + } + } + + if let Some((src_region_index, dst_region_index)) = overlap_extent_indices { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image` is equal to `dst_image`, and `regions[{0}].src_subresource` \ + overlaps with `regions[{1}].dst_subresource`, but \ + the `src_offsets` of `regions[{0}]` overlaps with \ + the `dst_offsets` of `regions[{1}]`", + src_region_index, dst_region_index + ) + .into(), + vuids: &["VUID-VkBlitImageInfo2-pRegions-00217"], + ..Default::default() + })); + } + + if let Some((src_region_index, dst_region_index)) = overlap_subresource_indices { + if src_image_layout != dst_image_layout { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image` is equal to `dst_image`, and `regions[{0}].src_subresource` \ + overlaps with `regions[{1}].dst_subresource`, but \ + `src_image_layout` does not equal `dst_image_layout`", + src_region_index, dst_region_index + ) + .into(), + vuids: &[ + "VUID-VkBlitImageInfo2-srcImageLayout-00221", + "VUID-VkBlitImageInfo2-dstImageLayout-00226", + ], + ..Default::default() + })); + } + } + + Ok(()) + } } /// A region of data to blit between images. @@ -4225,6 +6090,47 @@ impl Default for ImageBlit { } } +impl ImageBlit { + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + ref src_subresource, + src_offsets: _, + ref dst_subresource, + dst_offsets: _, + _ne: _, + } = self; + + src_subresource + .validate(device) + .map_err(|err| err.add_context("src_subresource"))?; + + dst_subresource + .validate(device) + .map_err(|err| err.add_context("dst_subresource"))?; + + if src_subresource.aspects != dst_subresource.aspects { + return Err(Box::new(ValidationError { + problem: "`src_subresource.aspects` does not equal `dst_subresource.aspects`" + .into(), + vuids: &["VUID-VkImageBlit2-aspectMask-00238"], + ..Default::default() + })); + } + + if src_subresource.array_layers.len() != dst_subresource.array_layers.len() { + return Err(Box::new(ValidationError { + problem: "the length of `src_subresource.array_layers` does not equal \ + the length of `dst_subresource.array_layers`" + .into(), + vuids: &["VUID-VkImageBlit2-layerCount-00239"], + ..Default::default() + })); + } + + Ok(()) + } +} + /// Parameters to resolve image data. #[derive(Clone, Debug)] pub struct ResolveImageInfo { @@ -4302,6 +6208,521 @@ impl ResolveImageInfo { _ne: crate::NonExhaustive(()), } } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + ref src_image, + src_image_layout, + ref dst_image, + dst_image_layout, + ref regions, + _ne: _, + } = self; + + src_image_layout + .validate_device(device) + .map_err(|err| ValidationError { + context: "src_image_layout".into(), + vuids: &["VUID-VkResolveImageInfo2-srcImageLayout-parameter"], + ..ValidationError::from_requirement(err) + })?; + + dst_image_layout + .validate_device(device) + .map_err(|err| ValidationError { + context: "dst_image_layout".into(), + vuids: &["VUID-VkResolveImageInfo2-dstImageLayout-parameter"], + ..ValidationError::from_requirement(err) + })?; + + // VUID-VkResolveImageInfo2-commonparent + assert_eq!(device, src_image.device().as_ref()); + assert_eq!(device, dst_image.device().as_ref()); + + let src_image_format = src_image.format(); + let dst_image_format = dst_image.format(); + + if src_image.samples() == SampleCount::Sample1 { + return Err(Box::new(ValidationError { + context: "src_image.samples()".into(), + problem: "is `SampleCount::Sample1`".into(), + vuids: &["VUID-VkResolveImageInfo2-srcImage-00257"], + ..Default::default() + })); + } + + if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) { + return Err(Box::new(ValidationError { + context: "src_image.usage()".into(), + problem: "does not contain `ImageUsage::TRANSFER_SRC`".into(), + vuids: &["VUID-VkResolveImageInfo2-srcImage-06762"], + ..Default::default() + })); + } + + if !src_image + .format_features() + .intersects(FormatFeatures::TRANSFER_SRC) + { + return Err(Box::new(ValidationError { + context: "src_image.format_features()".into(), + problem: "does not contain `FormatFeatures::TRANSFER_SRC`".into(), + vuids: &["VUID-VkResolveImageInfo2-srcImage-06763"], + ..Default::default() + })); + } + + if !matches!( + src_image_layout, + ImageLayout::TransferSrcOptimal | ImageLayout::General + ) { + return Err(Box::new(ValidationError { + context: "src_image_layout".into(), + problem: "is not `ImageLayout::TransferSrcOptimal` or `ImageLayout::General`" + .into(), + vuids: &["VUID-VkResolveImageInfo2-srcImageLayout-01400"], + ..Default::default() + })); + } + + if dst_image.samples() != SampleCount::Sample1 { + return Err(Box::new(ValidationError { + context: "dst_image.samples()".into(), + problem: "is not `SampleCount::Sample1`".into(), + vuids: &["VUID-VkResolveImageInfo2-dstImage-00259"], + ..Default::default() + })); + } + + if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) { + return Err(Box::new(ValidationError { + context: "dst_image.usage()".into(), + problem: "does not contain `ImageUsage::TRANSFER_DST`".into(), + vuids: &["VUID-VkResolveImageInfo2-dstImage-06764"], + ..Default::default() + })); + } + + if !dst_image + .format_features() + .contains(FormatFeatures::TRANSFER_DST | FormatFeatures::COLOR_ATTACHMENT) + { + return Err(Box::new(ValidationError { + context: "dst_image.format_features()".into(), + problem: "does not contain both `FormatFeatures::TRANSFER_DST` and \ + `FormatFeatures::COLOR_ATTACHMENT`" + .into(), + vuids: &[ + "VUID-VkResolveImageInfo2-dstImage-06765", + "VUID-VkResolveImageInfo2-dstImage-02003", + ], + ..Default::default() + })); + } + + if device.enabled_features().linear_color_attachment + && dst_image.tiling() == ImageTiling::Linear + && !dst_image + .format_features() + .contains(FormatFeatures::LINEAR_COLOR_ATTACHMENT) + { + return Err(Box::new(ValidationError { + problem: "the `linear_color_attachment` feature is enabled on the device, and \ + `dst_image.tiling()` is `ImageTiling::Linear`, but \ + `dst_image.format_features()` does not contain \ + `FormatFeatures::LINEAR_COLOR_ATTACHMENT`" + .into(), + vuids: &["VUID-VkResolveImageInfo2-linearColorAttachment-06519"], + ..Default::default() + })); + } + + if !matches!( + dst_image_layout, + ImageLayout::TransferDstOptimal | ImageLayout::General + ) { + return Err(Box::new(ValidationError { + context: "dst_image_layout".into(), + problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`" + .into(), + vuids: &["VUID-VkResolveImageInfo2-dstImageLayout-01401"], + ..Default::default() + })); + } + + if src_image_format != dst_image_format { + return Err(Box::new(ValidationError { + problem: "`src_image.format()` does not equal `dst_image.format()`".into(), + vuids: &["VUID-VkResolveImageInfo2-srcImage-01386"], + ..Default::default() + })); + } + + for (region_index, region) in regions.iter().enumerate() { + region + .validate(device) + .map_err(|err| err.add_context(format!("regions[{}]", region_index)))?; + + let &ImageResolve { + ref src_subresource, + src_offset, + ref dst_subresource, + dst_offset, + extent, + _ne: _, + } = region; + + /* + Check src + */ + + if src_subresource.mip_level >= src_image.mip_levels() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].src_subresource.mip_level` is not less than \ + `src_image.mip_levels()`", + region_index + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-srcSubresource-01709"], + ..Default::default() + })); + } + + let src_subresource_extent = + mip_level_extent(src_image.extent(), src_subresource.mip_level).unwrap(); + + match src_image.image_type() { + ImageType::Dim1d => { + if src_offset[1] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].src_offset[1]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-srcImage-00271"], + ..Default::default() + })); + } + + if extent[1] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].extent[1]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-srcImage-00271"], + ..Default::default() + })); + } + + if src_offset[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].src_offset[2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-srcImage-00273"], + ..Default::default() + })); + } + + if extent[2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].extent[2]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-srcImage-00273"], + ..Default::default() + })); + } + } + ImageType::Dim2d => { + if src_offset[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{}].src_offset[2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-srcImage-00273"], + ..Default::default() + })); + } + + if extent[2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{}].extent[2]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-srcImage-00273"], + ..Default::default() + })); + } + } + ImageType::Dim3d => { + if src_subresource.array_layers != (0..1) { + return Err(Box::new(ValidationError { + problem: format!( + "`src_image.image_type()` is `ImageType::Dim3d`, but \ + `regions[{}].src_subresource.array_layers` is not `0..1`", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-srcImage-04446"], + ..Default::default() + })); + } + } + } + + if src_subresource.array_layers.end > src_image.array_layers() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].src_subresource.array_layers.end` is not less than \ + `src_image.array_layers()`", + region_index + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-srcSubresource-01711"], + ..Default::default() + })); + } + + if src_offset[0] + extent[0] > src_subresource_extent[0] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_offset[0] + regions[{0}].extent[0]` is greater \ + than coordinate 0 of the extent of the subresource of `src_image` \ + selected by `regions[{0}].src_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-srcOffset-00269"], + ..Default::default() + })); + } + + if src_offset[1] + extent[1] > src_subresource_extent[1] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_offset[1] + regions[{0}].extent[1]` is greater \ + than coordinate 1 of the extent of the subresource of `src_image` \ + selected by `regions[{0}].src_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-srcOffset-00270"], + ..Default::default() + })); + } + + if src_offset[2] + extent[2] > src_subresource_extent[2] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].src_offset[2] + regions[{0}].extent[2]` is greater \ + than coordinate 2 of the extent of the subresource of `src_image` \ + selected by `regions[{0}].src_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-srcOffset-00272"], + ..Default::default() + })); + } + + /* + Check dst + */ + + if dst_subresource.mip_level >= dst_image.mip_levels() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].dst_subresource.mip_level` is not less than \ + `dst_image.mip_levels()`", + region_index + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-dstSubresource-01710"], + ..Default::default() + })); + } + + let dst_subresource_extent = + mip_level_extent(dst_image.extent(), dst_subresource.mip_level).unwrap(); + + match dst_image.image_type() { + ImageType::Dim1d => { + if dst_offset[1] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].dst_offset[1]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-dstImage-00276"], + ..Default::default() + })); + } + + if extent[1] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].extent[1]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-dstImage-00276"], + ..Default::default() + })); + } + + if dst_offset[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].dst_offset[2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-dstImage-00278"], + ..Default::default() + })); + } + + if extent[2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim1d`, but \ + `regions[{}].extent[2]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-dstImage-00278"], + ..Default::default() + })); + } + } + ImageType::Dim2d => { + if dst_offset[2] != 0 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{}].dst_offset[2]` is not 0", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-dstImage-00278"], + ..Default::default() + })); + } + + if extent[2] != 1 { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim2d`, but \ + `regions[{}].extent[2]` is not 1", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-dstImage-00278"], + ..Default::default() + })); + } + } + ImageType::Dim3d => { + if dst_subresource.array_layers != (0..1) { + return Err(Box::new(ValidationError { + problem: format!( + "`dst_image.image_type()` is `ImageType::Dim3d`, but \ + `regions[{}].dst_subresource.array_layers` is not `0..1`", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-srcImage-04447"], + ..Default::default() + })); + } + } + } + + if dst_subresource.array_layers.end > dst_image.array_layers() { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{}].dst_subresource.array_layers.end` is not less than \ + `dst_image.array_layers()`", + region_index + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-dstSubresource-01712"], + ..Default::default() + })); + } + + if dst_offset[0] + extent[0] > dst_subresource_extent[0] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_offset[0] + regions[{0}].extent[0]` is greater \ + than coordinate 0 of the extent of the subresource of `dst_image` \ + selected by `regions[{0}].dst_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-dstOffset-00274"], + ..Default::default() + })); + } + + if dst_offset[1] + extent[1] > dst_subresource_extent[1] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_offset[1] + regions[{0}].extent[1]` is greater \ + than coordinate 1 of the extent of the subresource of `dst_image` \ + selected by `regions[{0}].dst_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-dstOffset-00275"], + ..Default::default() + })); + } + + if dst_offset[2] + extent[2] > dst_subresource_extent[2] { + return Err(Box::new(ValidationError { + problem: format!( + "`regions[{0}].dst_offset[2] + regions[{0}].extent[2]` is greater \ + than coordinate 2 of the extent of the subresource of `dst_image` \ + selected by `regions[{0}].dst_subresource`", + region_index, + ) + .into(), + vuids: &["VUID-VkResolveImageInfo2-dstOffset-00277"], + ..Default::default() + })); + } + } + + // VUID-VkResolveImageInfo2-pRegions-00255 + // Can't occur as long as memory aliasing isn't allowed, because `src_image` and + // `dst_image` must have different sample counts and therefore can never be the same image. + + Ok(()) + } } /// A region of data to resolve between images. @@ -4357,559 +6778,52 @@ impl Default for ImageResolve { } } -/// Error that can happen when recording a copy command. -#[derive(Clone, Debug)] -pub enum CopyError { - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, +impl ImageResolve { + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + ref src_subresource, + src_offset: _, + ref dst_subresource, + dst_offset: _, + extent: _, + _ne: _, + } = self; - /// Operation forbidden inside of a render pass. - ForbiddenInsideRenderPass, + src_subresource + .validate(device) + .map_err(|err| err.add_context("src_subresource"))?; - /// The queue family doesn't allow this operation. - NotSupportedByQueueFamily, + dst_subresource + .validate(device) + .map_err(|err| err.add_context("dst_subresource"))?; - /// The array layer counts of the source and destination subresource ranges of a region do not - /// match. - ArrayLayerCountMismatch { - region_index: usize, - src_layer_count: u32, - dst_layer_count: u32, - }, - - /// The end of the range of accessed array layers of the subresource range of a region is - /// greater than the number of array layers in the image. - ArrayLayersOutOfRange { - resource: CopyErrorResource, - region_index: usize, - array_layers_range_end: u32, - image_array_layers: u32, - }, - - /// The aspects of the source and destination subresource ranges of a region do not match. - AspectsMismatch { - region_index: usize, - src_aspects: ImageAspects, - dst_aspects: ImageAspects, - }, - - /// The aspects of the subresource range of a region contain aspects that are not present - /// in the image, or that are not allowed. - AspectsNotAllowed { - resource: CopyErrorResource, - region_index: usize, - aspects: ImageAspects, - allowed_aspects: ImageAspects, - }, - - /// The buffer image height of a region is not a multiple of the required buffer alignment. - BufferImageHeightNotAligned { - resource: CopyErrorResource, - region_index: usize, - image_height: u32, - required_alignment: u32, - }, - - /// The buffer image height of a region is smaller than the image extent height. - BufferImageHeightTooSmall { - resource: CopyErrorResource, - region_index: usize, - image_height: u32, - min: u32, - }, - - /// The buffer row length of a region is not a multiple of the required buffer alignment. - BufferRowLengthNotAligned { - resource: CopyErrorResource, - region_index: usize, - row_length: u32, - required_alignment: u32, - }, - - /// The buffer row length of a region specifies a row of texels that is greater than 0x7FFFFFFF - /// bytes in size. - BufferRowLengthTooLarge { - resource: CopyErrorResource, - region_index: usize, - buffer_row_length: u32, - }, - - /// The buffer row length of a region is smaller than the image extent width. - BufferRowLengthTooSmall { - resource: CopyErrorResource, - region_index: usize, - row_length: u32, - min: u32, - }, - - /// Depth/stencil images are not supported by the queue family of this command buffer; a - /// graphics queue family is required. - DepthStencilNotSupportedByQueueFamily, - - /// The image extent of a region is not a multiple of the required image alignment. - ExtentNotAlignedForImage { - resource: CopyErrorResource, - region_index: usize, - extent: [u32; 3], - required_alignment: [u32; 3], - }, - - /// The chosen filter type does not support the dimensionality of the source image. - FilterNotSupportedForImageType, - - /// The chosen filter type does not support the format of the source image. - FilterNotSupportedByFormat, - - /// The format of an image is not supported for this operation. - FormatNotSupported { - resource: CopyErrorResource, - format: Format, - }, - - /// The format of the source image does not match the format of the destination image. - FormatsMismatch { - src_format: Format, - dst_format: Format, - }, - - /// The format of the source image subresource is not compatible with the format of the - /// destination image subresource. - FormatsNotCompatible { - src_format: Format, - dst_format: Format, - }, - - /// A specified image layout is not valid for this operation. - ImageLayoutInvalid { - resource: CopyErrorResource, - image_layout: ImageLayout, - }, - - /// The end of the range of accessed mip levels of the subresource range of a region is greater - /// than the number of mip levels in the image. - MipLevelsOutOfRange { - resource: CopyErrorResource, - region_index: usize, - mip_levels_range_end: u32, - image_mip_levels: u32, - }, - - /// An image does not have a required format feature. - MissingFormatFeature { - resource: CopyErrorResource, - format_feature: &'static str, - }, - - /// A resource did not have a required usage enabled. - MissingUsage { - resource: CopyErrorResource, - usage: &'static str, - }, - - /// A subresource range of a region specifies multiple aspects, but only one aspect can be - /// selected for the image. - MultipleAspectsNotAllowed { - resource: CopyErrorResource, - region_index: usize, - aspects: ImageAspects, - }, - - /// The buffer offset of a region is not a multiple of the required buffer alignment. - OffsetNotAlignedForBuffer { - resource: CopyErrorResource, - region_index: usize, - offset: DeviceSize, - required_alignment: DeviceSize, - }, - - /// The image offset of a region is not a multiple of the required image alignment. - OffsetNotAlignedForImage { - resource: CopyErrorResource, - region_index: usize, - offset: [u32; 3], - required_alignment: [u32; 3], - }, - - /// The image offsets of a region are not the values required for that axis ([0, 1]) for the - /// type of the image. - OffsetsInvalidForImageType { - resource: CopyErrorResource, - region_index: usize, - offsets: [u32; 2], - }, - - /// The source bounds of a region overlap with the destination bounds of a region. - OverlappingRegions { - src_region_index: usize, - dst_region_index: usize, - }, - - /// The source subresources of a region overlap with the destination subresources of a region, - /// but the source image layout does not equal the destination image layout. - OverlappingSubresourcesLayoutMismatch { - src_region_index: usize, - dst_region_index: usize, - src_image_layout: ImageLayout, - dst_image_layout: ImageLayout, - }, - - /// The end of the range of accessed byte offsets of a region is greater than the size of the - /// buffer. - RegionOutOfBufferBounds { - resource: CopyErrorResource, - region_index: usize, - offset_range_end: DeviceSize, - buffer_size: DeviceSize, - }, - - /// The end of the range of accessed texel offsets of a region is greater than the extent of - /// the selected subresource of the image. - RegionOutOfImageBounds { - resource: CopyErrorResource, - region_index: usize, - offset_range_end: [u32; 3], - subresource_extent: [u32; 3], - }, - - /// An image has a sample count that is not valid for this operation. - SampleCountInvalid { - resource: CopyErrorResource, - sample_count: SampleCount, - allowed_sample_counts: SampleCounts, - }, - - /// The source image has a different sample count than the destination image. - SampleCountMismatch { - src_sample_count: SampleCount, - dst_sample_count: SampleCount, - }, -} - -impl Error for CopyError {} - -impl Display for CopyError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::ForbiddenInsideRenderPass => { - write!(f, "operation forbidden inside of a render pass") - } - Self::NotSupportedByQueueFamily => { - write!(f, "the queue family doesn't allow this operation") - } - Self::ArrayLayerCountMismatch { - region_index, - src_layer_count, - dst_layer_count, - } => write!( - f, - "the array layer counts of the source and destination subresource ranges of region \ - {} do not match (source: {}; destination: {})", - region_index, src_layer_count, dst_layer_count, - ), - Self::ArrayLayersOutOfRange { - resource, - region_index, - array_layers_range_end, - image_array_layers, - } => write!( - f, - "the end of the range of accessed array layers ({}) of the {} subresource range of \ - region {} is greater than the number of array layers in the {} image ({})", - array_layers_range_end, resource, region_index, resource, image_array_layers, - ), - Self::AspectsMismatch { - region_index, - src_aspects, - dst_aspects, - } => write!( - f, - "the aspects of the source and destination subresource ranges of region {} do not \ - match (source: {:?}; destination: {:?})", - region_index, src_aspects, dst_aspects, - ), - Self::AspectsNotAllowed { - resource, - region_index, - aspects, - allowed_aspects, - } => write!( - f, - "the aspects ({:?}) of the {} subresource range of region {} contain aspects that \ - are not present in the {} image, or that are not allowed ({:?})", - aspects, resource, region_index, resource, allowed_aspects, - ), - Self::BufferImageHeightNotAligned { - resource, - region_index, - image_height, - required_alignment, - } => write!( - f, - "the {} buffer image height ({}) of region {} is not a multiple of the required {} \ - buffer alignment ({})", - resource, image_height, region_index, resource, required_alignment, - ), - Self::BufferRowLengthTooLarge { - resource, - region_index, - buffer_row_length, - } => write!( - f, - "the {} buffer row length ({}) of region {} specifies a row of texels that is \ - greater than 0x7FFFFFFF bytes in size", - resource, buffer_row_length, region_index, - ), - Self::BufferImageHeightTooSmall { - resource, - region_index, - image_height, - min, - } => write!( - f, - "the {} buffer image height ({}) of region {} is smaller than the {} image extent \ - height ({})", - resource, image_height, region_index, resource, min, - ), - Self::BufferRowLengthNotAligned { - resource, - region_index, - row_length, - required_alignment, - } => write!( - f, - "the {} buffer row length ({}) of region {} is not a multiple of the required {} \ - buffer alignment ({})", - resource, row_length, region_index, resource, required_alignment, - ), - Self::BufferRowLengthTooSmall { - resource, - region_index, - row_length, - min, - } => write!( - f, - "the {} buffer row length length ({}) of region {} is smaller than the {} image \ - extent width ({})", - resource, row_length, region_index, resource, min, - ), - Self::DepthStencilNotSupportedByQueueFamily => write!( - f, - "depth/stencil images are not supported by the queue family of this command \ - buffer; a graphics queue family is required", - ), - Self::ExtentNotAlignedForImage { - resource, - region_index, - extent, - required_alignment, - } => write!( - f, - "the {} image extent ({:?}) of region {} is not a multiple of the required {} \ - image alignment ({:?})", - resource, extent, region_index, resource, required_alignment, - ), - Self::FilterNotSupportedForImageType => write!( - f, - "the chosen filter is not supported for the source image type", - ), - Self::FilterNotSupportedByFormat => write!( - f, - "the chosen filter is not supported by the format of the source image", - ), - Self::FormatNotSupported { resource, format } => write!( - f, - "the format of the {} image ({:?}) is not supported for this operation", - resource, format, - ), - Self::FormatsMismatch { - src_format, - dst_format, - } => write!( - f, - "the format of the source image ({:?}) does not match the format of the \ - destination image ({:?})", - src_format, dst_format, - ), - Self::FormatsNotCompatible { - src_format, - dst_format, - } => write!( - f, - "the format of the source image subresource ({:?}) is not compatible with the \ - format of the destination image subresource ({:?})", - src_format, dst_format, - ), - Self::ImageLayoutInvalid { - resource, - image_layout, - } => write!( - f, - "the specified {} image layout {:?} is not valid for this operation", - resource, image_layout, - ), - Self::MipLevelsOutOfRange { - resource, - region_index, - mip_levels_range_end, - image_mip_levels, - } => write!( - f, - "the end of the range of accessed mip levels ({}) of the {} subresource range of \ - region {} is not less than the number of mip levels in the {} image ({})", - mip_levels_range_end, resource, region_index, resource, image_mip_levels, - ), - Self::MissingFormatFeature { - resource, - format_feature, - } => write!( - f, - "the {} image does not have the required format feature {}", - resource, format_feature, - ), - Self::MissingUsage { resource, usage } => write!( - f, - "the {} resource did not have the required usage {} enabled", - resource, usage, - ), - Self::MultipleAspectsNotAllowed { - resource, - region_index, - aspects, - } => write!( - f, - "the {} subresource range of region {} specifies multiple aspects ({:?}), but only \ - one aspect can be selected for the {} image", - resource, region_index, aspects, resource, - ), - Self::OffsetNotAlignedForBuffer { - resource, - region_index, - offset, - required_alignment, - } => write!( - f, - "the {} buffer offset ({}) of region {} is not a multiple of the required {} \ - buffer alignment ({})", - resource, offset, region_index, resource, required_alignment, - ), - Self::OffsetNotAlignedForImage { - resource, - region_index, - offset, - required_alignment, - } => write!( - f, - "the {} image offset ({:?}) of region {} is not a multiple of the required {} \ - image alignment ({:?})", - resource, offset, region_index, resource, required_alignment, - ), - Self::OffsetsInvalidForImageType { - resource, - region_index, - offsets, - } => write!( - f, - "the {} image offsets ({:?}) of region {} are not the values required for that \ - axis ([0, 1]) for the type of the {} image", - resource, offsets, region_index, resource, - ), - Self::OverlappingRegions { - src_region_index, - dst_region_index, - } => write!( - f, - "the source bounds of region {} overlap with the destination bounds of region {}", - src_region_index, dst_region_index, - ), - Self::OverlappingSubresourcesLayoutMismatch { - src_region_index, - dst_region_index, - src_image_layout, - dst_image_layout, - } => write!( - f, - "the source subresources of region {} overlap with the destination subresources of \ - region {}, but the source image layout ({:?}) does not equal the destination image \ - layout ({:?})", - src_region_index, dst_region_index, src_image_layout, dst_image_layout, - ), - Self::RegionOutOfBufferBounds { - resource, - region_index, - offset_range_end, - buffer_size, - } => write!( - f, - "the end of the range of accessed {} byte offsets ({}) of region {} is greater \ - than the size of the {} buffer ({})", - resource, offset_range_end, region_index, resource, buffer_size, - ), - Self::RegionOutOfImageBounds { - resource, - region_index, - offset_range_end, - subresource_extent, - } => write!( - f, - "the end of the range of accessed {} texel offsets ({:?}) of region {} is greater \ - than the extent of the selected subresource of the {} image ({:?})", - resource, offset_range_end, region_index, resource, subresource_extent, - ), - Self::SampleCountInvalid { - resource, - sample_count, - allowed_sample_counts, - } => write!( - f, - "the {} image has a sample count ({:?}) that is not valid for this operation \ - ({:?})", - resource, sample_count, allowed_sample_counts, - ), - Self::SampleCountMismatch { - src_sample_count, - dst_sample_count, - } => write!( - f, - "the source image has a different sample count ({:?}) than the destination image \ - ({:?})", - src_sample_count, dst_sample_count, - ), + if src_subresource.aspects != ImageAspects::COLOR { + return Err(Box::new(ValidationError { + problem: "`src_subresource.aspects` is not `ImageAspects::COLOR`".into(), + vuids: &["VUID-VkImageResolve2-aspectMask-00266"], + ..Default::default() + })); } - } -} -impl From for CopyError { - fn from(err: RequirementNotMet) -> Self { - Self::RequirementNotMet { - required_for: err.required_for, - requires_one_of: err.requires_one_of, + if dst_subresource.aspects != ImageAspects::COLOR { + return Err(Box::new(ValidationError { + problem: "`dst_subresource.aspects` is not `ImageAspects::COLOR`".into(), + vuids: &["VUID-VkImageResolve2-aspectMask-00266"], + ..Default::default() + })); } - } -} -/// Indicates which resource a `CopyError` applies to. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum CopyErrorResource { - Source, - Destination, -} - -impl Display for CopyErrorResource { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::Source => write!(f, "source"), - Self::Destination => write!(f, "destination"), + if src_subresource.array_layers.len() != dst_subresource.array_layers.len() { + return Err(Box::new(ValidationError { + problem: "the length of `src_subresource.array_layers` does not equal \ + the length of `dst_subresource.array_layers`" + .into(), + vuids: &["VUID-VkImageResolve2-layerCount-00267"], + ..Default::default() + })); } + + Ok(()) } } @@ -4931,6 +6845,7 @@ mod tests { }) .product::() * layer_count as DeviceSize; + num_blocks * format.block_size() } diff --git a/vulkano/src/command_buffer/commands/debug.rs b/vulkano/src/command_buffer/commands/debug.rs index 8c2c4593..9851fad9 100644 --- a/vulkano/src/command_buffer/commands/debug.rs +++ b/vulkano/src/command_buffer/commands/debug.rs @@ -14,13 +14,9 @@ use crate::{ }, device::{DeviceOwned, QueueFlags}, instance::debug::DebugUtilsLabel, - Requires, RequiresAllOf, RequiresOneOf, VulkanObject, -}; -use std::{ - error::Error, - ffi::CString, - fmt::{Display, Error as FmtError, Formatter}, + Requires, RequiresAllOf, RequiresOneOf, ValidationError, VulkanObject, }; +use std::ffi::CString; /// # Commands for debugging. /// @@ -35,43 +31,17 @@ where pub fn begin_debug_utils_label( &mut self, label_info: DebugUtilsLabel, - ) -> Result<&mut Self, DebugUtilsError> { + ) -> Result<&mut Self, Box> { self.validate_begin_debug_utils_label(&label_info)?; - unsafe { - self.begin_debug_utils_label_unchecked(label_info); - } - - Ok(self) + unsafe { Ok(self.begin_debug_utils_label_unchecked(label_info)) } } fn validate_begin_debug_utils_label( &self, - _label_info: &DebugUtilsLabel, - ) -> Result<(), DebugUtilsError> { - if !self - .device() - .instance() - .enabled_extensions() - .ext_debug_utils - { - return Err(DebugUtilsError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::begin_debug_utils_label`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( - "ext_debug_utils", - )])]), - }); - } - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdBeginDebugUtilsLabelEXT-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(DebugUtilsError::NotSupportedByQueueFamily); - } + label_info: &DebugUtilsLabel, + ) -> Result<(), Box> { + self.inner.validate_begin_debug_utils_label(label_info)?; Ok(()) } @@ -85,7 +55,7 @@ where "begin_debug_utils_label", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.begin_debug_utils_label(&label_info); + out.begin_debug_utils_label_unchecked(&label_info); }, ); @@ -99,44 +69,18 @@ where /// - When submitting the command buffer, there must be an outstanding command buffer label /// region begun with `begin_debug_utils_label` in the queue, either within this command /// buffer or a previously submitted one. - pub unsafe fn end_debug_utils_label(&mut self) -> Result<&mut Self, DebugUtilsError> { + pub unsafe fn end_debug_utils_label(&mut self) -> Result<&mut Self, Box> { self.validate_end_debug_utils_label()?; - self.end_debug_utils_label_unchecked(); - - Ok(self) + Ok(self.end_debug_utils_label_unchecked()) } - fn validate_end_debug_utils_label(&self) -> Result<(), DebugUtilsError> { - if !self - .device() - .instance() - .enabled_extensions() - .ext_debug_utils - { - return Err(DebugUtilsError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::end_debug_utils_label`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( - "ext_debug_utils", - )])]), - }); - } - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdEndDebugUtilsLabelEXT-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(DebugUtilsError::NotSupportedByQueueFamily); - } + fn validate_end_debug_utils_label(&self) -> Result<(), Box> { + self.inner.validate_end_debug_utils_label()?; + // TODO: // VUID-vkCmdEndDebugUtilsLabelEXT-commandBuffer-01912 - // TODO: not checked, so unsafe for now - // VUID-vkCmdEndDebugUtilsLabelEXT-commandBuffer-01913 - // TODO: not checked, so unsafe for now Ok(()) } @@ -147,7 +91,7 @@ where "end_debug_utils_label", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.end_debug_utils_label(); + out.end_debug_utils_label_unchecked(); }, ); @@ -158,43 +102,17 @@ where pub fn insert_debug_utils_label( &mut self, label_info: DebugUtilsLabel, - ) -> Result<&mut Self, DebugUtilsError> { + ) -> Result<&mut Self, Box> { self.validate_insert_debug_utils_label(&label_info)?; - unsafe { - self.insert_debug_utils_label_unchecked(label_info); - } - - Ok(self) + unsafe { Ok(self.insert_debug_utils_label_unchecked(label_info)) } } fn validate_insert_debug_utils_label( &self, - _label_info: &DebugUtilsLabel, - ) -> Result<(), DebugUtilsError> { - if !self - .device() - .instance() - .enabled_extensions() - .ext_debug_utils - { - return Err(DebugUtilsError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::insert_debug_utils_label`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( - "ext_debug_utils", - )])]), - }); - } - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdInsertDebugUtilsLabelEXT-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(DebugUtilsError::NotSupportedByQueueFamily); - } + label_info: &DebugUtilsLabel, + ) -> Result<(), Box> { + self.inner.validate_insert_debug_utils_label(label_info)?; Ok(()) } @@ -208,7 +126,7 @@ where "insert_debug_utils_label", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.insert_debug_utils_label(&label_info); + out.insert_debug_utils_label_unchecked(&label_info); }, ); @@ -220,13 +138,55 @@ impl UnsafeCommandBufferBuilder where A: CommandBufferAllocator, { - /// Calls `vkCmdBeginDebugUtilsLabelEXT` on the builder. - /// - /// # Safety - /// The command pool that this command buffer was allocated from must support graphics or - /// compute operations - #[inline] - pub unsafe fn begin_debug_utils_label(&mut self, label_info: &DebugUtilsLabel) -> &mut Self { + pub unsafe fn begin_debug_utils_label( + &mut self, + label_info: &DebugUtilsLabel, + ) -> Result<&mut Self, Box> { + self.validate_begin_debug_utils_label(label_info)?; + + Ok(self.begin_debug_utils_label_unchecked(label_info)) + } + + fn validate_begin_debug_utils_label( + &self, + _label_info: &DebugUtilsLabel, + ) -> Result<(), Box> { + if !self + .device() + .instance() + .enabled_extensions() + .ext_debug_utils + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( + "ext_debug_utils", + )])]), + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics or compute operations" + .into(), + vuids: &["VUID-vkCmdBeginDebugUtilsLabelEXT-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn begin_debug_utils_label_unchecked( + &mut self, + label_info: &DebugUtilsLabel, + ) -> &mut Self { let &DebugUtilsLabel { ref label_name, color, @@ -246,26 +206,101 @@ where self } - /// Calls `vkCmdEndDebugUtilsLabelEXT` on the builder. - /// - /// # Safety - /// There must be an outstanding `vkCmdBeginDebugUtilsLabelEXT` command prior to the - /// `vkQueueEndDebugUtilsLabelEXT` on the queue tha `CommandBuffer` is submitted to. - #[inline] - pub unsafe fn end_debug_utils_label(&mut self) -> &mut Self { + pub unsafe fn end_debug_utils_label(&mut self) -> Result<&mut Self, Box> { + self.validate_end_debug_utils_label()?; + + Ok(self.end_debug_utils_label_unchecked()) + } + + fn validate_end_debug_utils_label(&self) -> Result<(), Box> { + if !self + .device() + .instance() + .enabled_extensions() + .ext_debug_utils + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( + "ext_debug_utils", + )])]), + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics or compute operations" + .into(), + vuids: &["VUID-vkCmdEndDebugUtilsLabelEXT-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn end_debug_utils_label_unchecked(&mut self) -> &mut Self { let fns = self.device().instance().fns(); (fns.ext_debug_utils.cmd_end_debug_utils_label_ext)(self.handle()); self } - /// Calls `vkCmdInsertDebugUtilsLabelEXT` on the builder. - /// - /// # Safety - /// The command pool that this command buffer was allocated from must support graphics or - /// compute operations - #[inline] - pub unsafe fn insert_debug_utils_label(&mut self, label_info: &DebugUtilsLabel) -> &mut Self { + pub unsafe fn insert_debug_utils_label( + &mut self, + label_info: &DebugUtilsLabel, + ) -> Result<&mut Self, Box> { + self.validate_insert_debug_utils_label(label_info)?; + + Ok(self.insert_debug_utils_label_unchecked(label_info)) + } + + fn validate_insert_debug_utils_label( + &self, + _label_info: &DebugUtilsLabel, + ) -> Result<(), Box> { + if !self + .device() + .instance() + .enabled_extensions() + .ext_debug_utils + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension( + "ext_debug_utils", + )])]), + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics or compute operations" + .into(), + vuids: &["VUID-vkCmdInsertDebugUtilsLabelEXT-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn insert_debug_utils_label_unchecked( + &mut self, + label_info: &DebugUtilsLabel, + ) -> &mut Self { let &DebugUtilsLabel { ref label_name, color, @@ -285,35 +320,3 @@ where self } } - -/// Error that can happen when recording a debug utils command. -#[derive(Clone, Debug)] -pub enum DebugUtilsError { - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, - - /// The queue family doesn't allow this operation. - NotSupportedByQueueFamily, -} - -impl Error for DebugUtilsError {} - -impl Display for DebugUtilsError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::NotSupportedByQueueFamily => { - write!(f, "the queue family doesn't allow this operation") - } - } - } -} diff --git a/vulkano/src/command_buffer/commands/dynamic_state.rs b/vulkano/src/command_buffer/commands/dynamic_state.rs index dac4e77f..a6eb7678 100644 --- a/vulkano/src/command_buffer/commands/dynamic_state.rs +++ b/vulkano/src/command_buffer/commands/dynamic_state.rs @@ -23,14 +23,10 @@ use crate::{ }, DynamicState, }, - RequirementNotMet, Requires, RequiresAllOf, RequiresOneOf, Version, VulkanObject, + Requires, RequiresAllOf, RequiresOneOf, ValidationError, Version, VulkanObject, }; use smallvec::SmallVec; -use std::{ - error::Error, - fmt::{Display, Error as FmtError, Formatter}, - ops::RangeInclusive, -}; +use std::ops::RangeInclusive; /// # Commands to set dynamic state for pipelines. /// @@ -40,11 +36,10 @@ where A: CommandBufferAllocator, { // Helper function for dynamic state setting. - fn validate_pipeline_fixed_state( + fn validate_graphics_pipeline_fixed_state( &self, state: DynamicState, - ) -> Result<(), SetDynamicStateError> { - // VUID-vkCmdDispatch-None-02859 + ) -> Result<(), Box> { if self .builder_state .pipeline_graphics @@ -53,43 +48,35 @@ where matches!(pipeline.dynamic_state(state), Some(false)) }) { - return Err(SetDynamicStateError::PipelineHasFixedState); + return Err(Box::new(ValidationError { + problem: "the state for this value in the currently bound graphics pipeline \ + is fixed, and cannot be set" + .into(), + vuids: &["VUID-vkCmdDispatch-None-08608", "VUID-vkCmdDraw-None-08608"], + ..Default::default() + })); } Ok(()) } /// Sets the dynamic blend constants for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - pub fn set_blend_constants(&mut self, constants: [f32; 4]) -> &mut Self { - self.validate_set_blend_constants(constants).unwrap(); + pub fn set_blend_constants( + &mut self, + constants: [f32; 4], + ) -> Result<&mut Self, Box> { + self.validate_set_blend_constants(constants)?; - unsafe { - self.set_blend_constants_unchecked(constants); - } - - self + unsafe { Ok(self.set_blend_constants_unchecked(constants)) } } fn validate_set_blend_constants( &self, - _constants: [f32; 4], - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::BlendConstants)?; + constants: [f32; 4], + ) -> Result<(), Box> { + self.inner.validate_set_blend_constants(constants)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetBlendConstants-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::BlendConstants)?; Ok(()) } @@ -101,7 +88,7 @@ where "set_blend_constants", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_blend_constants(constants); + out.set_blend_constants_unchecked(constants); }, ); @@ -110,50 +97,22 @@ where /// Sets whether dynamic color writes should be enabled for each attachment in the /// framebuffer. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the [`color_write_enable`](crate::device::Features::color_write_enable) - /// feature is not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - /// - If there is a graphics pipeline with color blend state bound, `enables.len()` must equal - /// - [`attachments.len()`](crate::pipeline::graphics::color_blend::ColorBlendState::attachments). - pub fn set_color_write_enable(&mut self, enables: SmallVec<[bool; 4]>) -> &mut Self { - self.validate_set_color_write_enable(&enables).unwrap(); + pub fn set_color_write_enable( + &mut self, + enables: SmallVec<[bool; 4]>, + ) -> Result<&mut Self, Box> { + self.validate_set_color_write_enable(&enables)?; - unsafe { - self.set_color_write_enable_unchecked(enables); - } - - self + unsafe { Ok(self.set_color_write_enable_unchecked(enables)) } } fn validate_set_color_write_enable( &self, enables: &[bool], - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::ColorWriteEnable)?; + ) -> Result<(), Box> { + self.inner.validate_set_color_write_enable(enables)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetColorWriteEnableEXT-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetColorWriteEnableEXT-None-04803 - if !self.device().enabled_features().color_write_enable { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_color_write_enable`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( - "ext_color_write_enable", - )])]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::ColorWriteEnable)?; if let Some(color_blend_state) = self .builder_state @@ -161,15 +120,15 @@ where .as_ref() .and_then(|pipeline| pipeline.color_blend_state()) { - // VUID-vkCmdSetColorWriteEnableEXT-attachmentCount-06656 // Indirectly checked if enables.len() != color_blend_state.attachments.len() { - return Err( - SetDynamicStateError::PipelineColorBlendAttachmentCountMismatch { - provided_count: enables.len() as u32, - required_count: color_blend_state.attachments.len() as u32, - }, - ); + return Err(Box::new(ValidationError { + problem: "the length of `enables` does not match the number of \ + color attachments in the subpass of the currently bound graphics pipeline" + .into(), + vuids: &["VUID-vkCmdSetColorWriteEnableEXT-attachmentCount-06656"], + ..Default::default() + })); } } @@ -186,7 +145,7 @@ where "set_color_write_enable", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_color_write_enable(&enables); + out.set_color_write_enable_unchecked(&enables); }, ); @@ -194,52 +153,19 @@ where } /// Sets the dynamic cull mode for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the device API version is less than 1.3 and the - /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is - /// not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - pub fn set_cull_mode(&mut self, cull_mode: CullMode) -> &mut Self { - self.validate_set_cull_mode(cull_mode).unwrap(); + pub fn set_cull_mode( + &mut self, + cull_mode: CullMode, + ) -> Result<&mut Self, Box> { + self.validate_set_cull_mode(cull_mode)?; - unsafe { - self.set_cull_mode_unchecked(cull_mode); - } - - self + unsafe { Ok(self.set_cull_mode_unchecked(cull_mode)) } } - fn validate_set_cull_mode(&self, cull_mode: CullMode) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::CullMode)?; + fn validate_set_cull_mode(&self, cull_mode: CullMode) -> Result<(), Box> { + self.inner.validate_set_cull_mode(cull_mode)?; - // VUID-vkCmdSetCullMode-cullMode-parameter - cull_mode.validate_device(self.device())?; - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetCullMode-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetCullMode-None-03384 - if !(self.device().api_version() >= Version::V1_3 - || self.device().enabled_features().extended_dynamic_state) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_cull_mode`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), - RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), - ]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::CullMode)?; Ok(()) } @@ -251,7 +177,7 @@ where "set_cull_mode", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_cull_mode(cull_mode); + out.set_cull_mode_unchecked(cull_mode); }, ); @@ -259,56 +185,27 @@ where } /// Sets the dynamic depth bias values for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - /// - If the [`depth_bias_clamp`](crate::device::Features::depth_bias_clamp) - /// feature is not enabled on the device, panics if `clamp` is not 0.0. pub fn set_depth_bias( &mut self, constant_factor: f32, clamp: f32, slope_factor: f32, - ) -> &mut Self { - self.validate_set_depth_bias(constant_factor, clamp, slope_factor) - .unwrap(); + ) -> Result<&mut Self, Box> { + self.validate_set_depth_bias(constant_factor, clamp, slope_factor)?; - unsafe { - self.set_depth_bias_unchecked(constant_factor, clamp, slope_factor); - } - - self + unsafe { Ok(self.set_depth_bias_unchecked(constant_factor, clamp, slope_factor)) } } fn validate_set_depth_bias( &self, - _constant_factor: f32, + constant_factor: f32, clamp: f32, - _slope_factor: f32, - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::DepthBias)?; + slope_factor: f32, + ) -> Result<(), Box> { + self.inner + .validate_set_depth_bias(constant_factor, clamp, slope_factor)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetDepthBias-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetDepthBias-depthBiasClamp-00790 - if clamp != 0.0 && !self.device().enabled_features().depth_bias_clamp { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`clamp` is not `0.0`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "depth_bias_clamp", - )])]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::DepthBias)?; Ok(()) } @@ -329,7 +226,7 @@ where "set_depth_bias", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_depth_bias(constant_factor, clamp, slope_factor); + out.set_depth_bias_unchecked(constant_factor, clamp, slope_factor); }, ); @@ -337,49 +234,19 @@ where } /// Sets whether dynamic depth bias is enabled for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the device API version is less than 1.3 and the - /// [`extended_dynamic_state2`](crate::device::Features::extended_dynamic_state2) feature is - /// not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - pub fn set_depth_bias_enable(&mut self, enable: bool) -> &mut Self { - self.validate_set_depth_bias_enable(enable).unwrap(); + pub fn set_depth_bias_enable( + &mut self, + enable: bool, + ) -> Result<&mut Self, Box> { + self.validate_set_depth_bias_enable(enable)?; - unsafe { - self.set_depth_bias_enable_unchecked(enable); - } - - self + unsafe { Ok(self.set_depth_bias_enable_unchecked(enable)) } } - fn validate_set_depth_bias_enable(&self, _enable: bool) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::DepthBiasEnable)?; + fn validate_set_depth_bias_enable(&self, enable: bool) -> Result<(), Box> { + self.inner.validate_set_depth_bias_enable(enable)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetDepthBiasEnable-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetDepthBiasEnable-None-04872 - if !(self.device().api_version() >= Version::V1_3 - || self.device().enabled_features().extended_dynamic_state2) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_depth_bias_enable`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), - RequiresAllOf(&[Requires::Feature("extended_dynamic_state2")]), - ]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::DepthBiasEnable)?; Ok(()) } @@ -391,7 +258,7 @@ where "set_depth_bias_enable", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_depth_bias_enable(enable); + out.set_depth_bias_enable_unchecked(enable); }, ); @@ -399,56 +266,22 @@ where } /// Sets the dynamic depth bounds for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - /// - If the - /// [`ext_depth_range_unrestricted`](crate::device::DeviceExtensions::ext_depth_range_unrestricted) - /// device extension is not enabled, panics if the start and end of `bounds` are not between - /// 0.0 and 1.0 inclusive. - pub fn set_depth_bounds(&mut self, bounds: RangeInclusive) -> &mut Self { - self.validate_set_depth_bounds(bounds.clone()).unwrap(); + pub fn set_depth_bounds( + &mut self, + bounds: RangeInclusive, + ) -> Result<&mut Self, Box> { + self.validate_set_depth_bounds(bounds.clone())?; - unsafe { - self.set_depth_bounds_unchecked(bounds); - } - - self + unsafe { Ok(self.set_depth_bounds_unchecked(bounds)) } } fn validate_set_depth_bounds( &self, bounds: RangeInclusive, - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::DepthBounds)?; + ) -> Result<(), Box> { + self.inner.validate_set_depth_bounds(bounds)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetDepthBounds-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetDepthBounds-minDepthBounds-00600 - // VUID-vkCmdSetDepthBounds-maxDepthBounds-00601 - if !self - .device() - .enabled_extensions() - .ext_depth_range_unrestricted - && !((0.0..=1.0).contains(bounds.start()) && (0.0..=1.0).contains(bounds.end())) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`bounds` is not between `0.0` and `1.0` inclusive", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( - "ext_depth_range_unrestricted", - )])]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::DepthBounds)?; Ok(()) } @@ -460,7 +293,7 @@ where "set_depth_bounds", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_depth_bounds(bounds.clone()); + out.set_depth_bounds_unchecked(bounds.clone()); }, ); @@ -468,52 +301,22 @@ where } /// Sets whether dynamic depth bounds testing is enabled for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the device API version is less than 1.3 and the - /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is - /// not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - pub fn set_depth_bounds_test_enable(&mut self, enable: bool) -> &mut Self { - self.validate_set_depth_bounds_test_enable(enable).unwrap(); + pub fn set_depth_bounds_test_enable( + &mut self, + enable: bool, + ) -> Result<&mut Self, Box> { + self.validate_set_depth_bounds_test_enable(enable)?; - unsafe { - self.set_depth_bounds_test_enable_unchecked(enable); - } - - self + unsafe { Ok(self.set_depth_bounds_test_enable_unchecked(enable)) } } fn validate_set_depth_bounds_test_enable( &self, - _enable: bool, - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::DepthBoundsTestEnable)?; + enable: bool, + ) -> Result<(), Box> { + self.inner.validate_set_depth_bounds_test_enable(enable)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetDepthBoundsTestEnable-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetDepthBoundsTestEnable-None-03349 - if !(self.device().api_version() >= Version::V1_3 - || self.device().enabled_features().extended_dynamic_state) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_depth_bounds_test_enable`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), - RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), - ]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::DepthBoundsTestEnable)?; Ok(()) } @@ -525,7 +328,7 @@ where "set_depth_bounds_test_enable", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_depth_bounds_test_enable(enable); + out.set_depth_bounds_test_enable_unchecked(enable); }, ); @@ -533,55 +336,22 @@ where } /// Sets the dynamic depth compare op for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the device API version is less than 1.3 and the - /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is - /// not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - pub fn set_depth_compare_op(&mut self, compare_op: CompareOp) -> &mut Self { - self.validate_set_depth_compare_op(compare_op).unwrap(); + pub fn set_depth_compare_op( + &mut self, + compare_op: CompareOp, + ) -> Result<&mut Self, Box> { + self.validate_set_depth_compare_op(compare_op)?; - unsafe { - self.set_depth_compare_op_unchecked(compare_op); - } - - self + unsafe { Ok(self.set_depth_compare_op_unchecked(compare_op)) } } fn validate_set_depth_compare_op( &self, compare_op: CompareOp, - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::DepthCompareOp)?; + ) -> Result<(), Box> { + self.inner.validate_set_depth_compare_op(compare_op)?; - // VUID-vkCmdSetDepthCompareOp-depthCompareOp-parameter - compare_op.validate_device(self.device())?; - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetDepthCompareOp-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetDepthCompareOp-None-03353 - if !(self.device().api_version() >= Version::V1_3 - || self.device().enabled_features().extended_dynamic_state) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_depth_compare_op`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), - RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), - ]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::DepthCompareOp)?; Ok(()) } @@ -593,7 +363,7 @@ where "set_depth_compare_op", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_depth_compare_op(compare_op); + out.set_depth_compare_op_unchecked(compare_op); }, ); @@ -601,49 +371,19 @@ where } /// Sets whether dynamic depth testing is enabled for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the device API version is less than 1.3 and the - /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is - /// not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - pub fn set_depth_test_enable(&mut self, enable: bool) -> &mut Self { - self.validate_set_depth_test_enable(enable).unwrap(); + pub fn set_depth_test_enable( + &mut self, + enable: bool, + ) -> Result<&mut Self, Box> { + self.validate_set_depth_test_enable(enable)?; - unsafe { - self.set_depth_test_enable_unchecked(enable); - } - - self + unsafe { Ok(self.set_depth_test_enable_unchecked(enable)) } } - fn validate_set_depth_test_enable(&self, _enable: bool) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::DepthTestEnable)?; + fn validate_set_depth_test_enable(&self, enable: bool) -> Result<(), Box> { + self.inner.validate_set_depth_test_enable(enable)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetDepthTestEnable-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetDepthTestEnable-None-03352 - if !(self.device().api_version() >= Version::V1_3 - || self.device().enabled_features().extended_dynamic_state) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_depth_test_enable`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), - RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), - ]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::DepthTestEnable)?; Ok(()) } @@ -655,7 +395,7 @@ where "set_depth_test_enable", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_depth_test_enable(enable); + out.set_depth_test_enable_unchecked(enable); }, ); @@ -663,49 +403,19 @@ where } /// Sets whether dynamic depth write is enabled for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the device API version is less than 1.3 and the - /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is - /// not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - pub fn set_depth_write_enable(&mut self, enable: bool) -> &mut Self { - self.validate_set_depth_write_enable(enable).unwrap(); + pub fn set_depth_write_enable( + &mut self, + enable: bool, + ) -> Result<&mut Self, Box> { + self.validate_set_depth_write_enable(enable)?; - unsafe { - self.set_depth_write_enable_unchecked(enable); - } - - self + unsafe { Ok(self.set_depth_write_enable_unchecked(enable)) } } - fn validate_set_depth_write_enable(&self, _enable: bool) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::DepthWriteEnable)?; + fn validate_set_depth_write_enable(&self, enable: bool) -> Result<(), Box> { + self.inner.validate_set_depth_write_enable(enable)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetDepthWriteEnable-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetDepthWriteEnable-None-03354 - if !(self.device().api_version() >= Version::V1_3 - || self.device().enabled_features().extended_dynamic_state) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_depth_write_enable`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), - RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), - ]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::DepthWriteEnable)?; Ok(()) } @@ -717,7 +427,7 @@ where "set_depth_write_enable", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_depth_write_enable(enable); + out.set_depth_write_enable_unchecked(enable); }, ); @@ -725,77 +435,25 @@ where } /// Sets the dynamic discard rectangles for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the - /// [`ext_discard_rectangles`](crate::device::DeviceExtensions::ext_discard_rectangles) - /// extension is not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - /// - Panics if the highest discard rectangle slot being set is greater than the - /// [`max_discard_rectangles`](crate::device::Properties::max_discard_rectangles) device - /// property. pub fn set_discard_rectangle( &mut self, first_rectangle: u32, rectangles: SmallVec<[Scissor; 2]>, - ) -> &mut Self { - self.validate_set_discard_rectangle(first_rectangle, &rectangles) - .unwrap(); + ) -> Result<&mut Self, Box> { + self.validate_set_discard_rectangle(first_rectangle, &rectangles)?; - unsafe { - self.set_discard_rectangle_unchecked(first_rectangle, rectangles); - } - - self + unsafe { Ok(self.set_discard_rectangle_unchecked(first_rectangle, rectangles)) } } fn validate_set_discard_rectangle( &self, first_rectangle: u32, rectangles: &[Scissor], - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::DiscardRectangle)?; + ) -> Result<(), Box> { + self.inner + .validate_set_discard_rectangle(first_rectangle, rectangles)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetDiscardRectangle-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - if self.device().enabled_extensions().ext_discard_rectangles { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_discard_rectangle`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( - "ext_discard_rectangles", - )])]), - }); - } - - // VUID-vkCmdSetDiscardRectangleEXT-firstDiscardRectangle-00585 - if first_rectangle + rectangles.len() as u32 - > self - .device() - .physical_device() - .properties() - .max_discard_rectangles - .unwrap() - { - return Err(SetDynamicStateError::MaxDiscardRectanglesExceeded { - provided: first_rectangle + rectangles.len() as u32, - max: self - .device() - .physical_device() - .properties() - .max_discard_rectangles - .unwrap(), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::DiscardRectangle)?; Ok(()) } @@ -815,7 +473,7 @@ where "set_discard_rectangle", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_discard_rectangle(first_rectangle, &rectangles); + out.set_discard_rectangle_unchecked(first_rectangle, &rectangles); }, ); @@ -823,52 +481,16 @@ where } /// Sets the dynamic front face for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the device API version is less than 1.3 and the - /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is - /// not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - pub fn set_front_face(&mut self, face: FrontFace) -> &mut Self { - self.validate_set_front_face(face).unwrap(); + pub fn set_front_face(&mut self, face: FrontFace) -> Result<&mut Self, Box> { + self.validate_set_front_face(face)?; - unsafe { - self.set_front_face_unchecked(face); - } - - self + unsafe { Ok(self.set_front_face_unchecked(face)) } } - fn validate_set_front_face(&self, face: FrontFace) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::FrontFace)?; + fn validate_set_front_face(&self, face: FrontFace) -> Result<(), Box> { + self.inner.validate_set_front_face(face)?; - // VUID-vkCmdSetFrontFace-frontFace-parameter - face.validate_device(self.device())?; - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetFrontFace-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetFrontFace-None-03383 - if !(self.device().api_version() >= Version::V1_3 - || self.device().enabled_features().extended_dynamic_state) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_front_face`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), - RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), - ]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::FrontFace)?; Ok(()) } @@ -880,7 +502,7 @@ where "set_front_face", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_front_face(face); + out.set_front_face_unchecked(face); }, ); @@ -888,54 +510,24 @@ where } /// Sets the dynamic line stipple values for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the [`ext_line_rasterization`](crate::device::DeviceExtensions::ext_line_rasterization) - /// extension is not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - /// - Panics if `factor` is not between 1 and 256 inclusive. - pub fn set_line_stipple(&mut self, factor: u32, pattern: u16) -> &mut Self { - self.validate_set_line_stipple(factor, pattern).unwrap(); + pub fn set_line_stipple( + &mut self, + factor: u32, + pattern: u16, + ) -> Result<&mut Self, Box> { + self.validate_set_line_stipple(factor, pattern)?; - unsafe { - self.set_line_stipple_unchecked(factor, pattern); - } - - self + unsafe { Ok(self.set_line_stipple_unchecked(factor, pattern)) } } fn validate_set_line_stipple( &self, factor: u32, - _pattern: u16, - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::LineStipple)?; + pattern: u16, + ) -> Result<(), Box> { + self.inner.validate_set_line_stipple(factor, pattern)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetLineStippleEXT-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - if !self.device().enabled_extensions().ext_line_rasterization { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_line_stipple`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( - "ext_line_rasterization", - )])]), - }); - } - - // VUID-vkCmdSetLineStippleEXT-lineStippleFactor-02776 - if !(1..=256).contains(&factor) { - return Err(SetDynamicStateError::FactorOutOfRange); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::LineStipple)?; Ok(()) } @@ -947,7 +539,7 @@ where "set_line_stipple", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_line_stipple(factor, pattern); + out.set_line_stipple_unchecked(factor, pattern); }, ); @@ -955,45 +547,16 @@ where } /// Sets the dynamic line width for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - /// - If the [`wide_lines`](crate::device::Features::wide_lines) feature is not enabled, panics - /// if `line_width` is not 1.0. - pub fn set_line_width(&mut self, line_width: f32) -> &mut Self { - self.validate_set_line_width(line_width).unwrap(); + pub fn set_line_width(&mut self, line_width: f32) -> Result<&mut Self, Box> { + self.validate_set_line_width(line_width)?; - unsafe { - self.set_line_width_unchecked(line_width); - } - - self + unsafe { Ok(self.set_line_width_unchecked(line_width)) } } - fn validate_set_line_width(&self, line_width: f32) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::LineWidth)?; + fn validate_set_line_width(&self, line_width: f32) -> Result<(), Box> { + self.inner.validate_set_line_width(line_width)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetLineWidth-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetLineWidth-lineWidth-00788 - if !self.device().enabled_features().wide_lines && line_width != 1.0 { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`line_width` is not `1.0`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "wide_lines", - )])]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::LineWidth)?; Ok(()) } @@ -1005,7 +568,7 @@ where "set_line_width", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_line_width(line_width); + out.set_line_width_unchecked(line_width); }, ); @@ -1013,53 +576,16 @@ where } /// Sets the dynamic logic op for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the - /// [`extended_dynamic_state2_logic_op`](crate::device::Features::extended_dynamic_state2_logic_op) - /// feature is not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - pub fn set_logic_op(&mut self, logic_op: LogicOp) -> &mut Self { - self.validate_set_logic_op(logic_op).unwrap(); + pub fn set_logic_op(&mut self, logic_op: LogicOp) -> Result<&mut Self, Box> { + self.validate_set_logic_op(logic_op)?; - unsafe { - self.set_logic_op_unchecked(logic_op); - } - - self + unsafe { Ok(self.set_logic_op_unchecked(logic_op)) } } - fn validate_set_logic_op(&self, logic_op: LogicOp) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::LogicOp)?; + fn validate_set_logic_op(&self, logic_op: LogicOp) -> Result<(), Box> { + self.inner.validate_set_logic_op(logic_op)?; - // VUID-vkCmdSetLogicOpEXT-logicOp-parameter - logic_op.validate_device(self.device())?; - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetLogicOpEXT-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetLogicOpEXT-None-04867 - if !self - .device() - .enabled_features() - .extended_dynamic_state2_logic_op - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_logic_op`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "extended_dynamic_state2_logic_op", - )])]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::LogicOp)?; Ok(()) } @@ -1071,7 +597,7 @@ where "set_logic_op", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_logic_op(logic_op); + out.set_logic_op_unchecked(logic_op); }, ); @@ -1079,75 +605,19 @@ where } /// Sets the dynamic number of patch control points for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the - /// [`extended_dynamic_state2_patch_control_points`](crate::device::Features::extended_dynamic_state2_patch_control_points) - /// feature is not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - /// - Panics if `num` is 0. - /// - Panics if `num` is greater than the - /// [`max_tessellation_patch_size`](crate::device::Properties::max_tessellation_patch_size) - /// property of the device. - pub fn set_patch_control_points(&mut self, num: u32) -> &mut Self { - self.validate_set_patch_control_points(num).unwrap(); + pub fn set_patch_control_points( + &mut self, + num: u32, + ) -> Result<&mut Self, Box> { + self.validate_set_patch_control_points(num)?; - unsafe { - self.set_patch_control_points_unchecked(num); - } - - self + unsafe { Ok(self.set_patch_control_points_unchecked(num)) } } - fn validate_set_patch_control_points(&self, num: u32) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::PatchControlPoints)?; + fn validate_set_patch_control_points(&self, num: u32) -> Result<(), Box> { + self.inner.validate_set_patch_control_points(num)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetPatchControlPointsEXT-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetPatchControlPointsEXT-None-04873 - if !self - .device() - .enabled_features() - .extended_dynamic_state2_patch_control_points - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_patch_control_points`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "extended_dynamic_state2_patch_control_points", - )])]), - }); - } - - // VUID-vkCmdSetPatchControlPointsEXT-patchControlPoints-04874 - assert!(num > 0, "num must be greater than 0"); - - // VUID-vkCmdSetPatchControlPointsEXT-patchControlPoints-04874 - if num - > self - .device() - .physical_device() - .properties() - .max_tessellation_patch_size - { - return Err(SetDynamicStateError::MaxTessellationPatchSizeExceeded { - provided: num, - max: self - .device() - .physical_device() - .properties() - .max_tessellation_patch_size, - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::PatchControlPoints)?; Ok(()) } @@ -1159,7 +629,7 @@ where "set_patch_control_points", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_patch_control_points(num); + out.set_patch_control_points_unchecked(num); }, ); @@ -1167,52 +637,22 @@ where } /// Sets whether dynamic primitive restart is enabled for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the device API version is less than 1.3 and the - /// [`extended_dynamic_state2`](crate::device::Features::extended_dynamic_state2) feature is - /// not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - pub fn set_primitive_restart_enable(&mut self, enable: bool) -> &mut Self { - self.validate_set_primitive_restart_enable(enable).unwrap(); + pub fn set_primitive_restart_enable( + &mut self, + enable: bool, + ) -> Result<&mut Self, Box> { + self.validate_set_primitive_restart_enable(enable)?; - unsafe { - self.set_primitive_restart_enable_unchecked(enable); - } - - self + unsafe { Ok(self.set_primitive_restart_enable_unchecked(enable)) } } fn validate_set_primitive_restart_enable( &self, - _enable: bool, - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::PrimitiveRestartEnable)?; + enable: bool, + ) -> Result<(), Box> { + self.inner.validate_set_primitive_restart_enable(enable)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetPrimitiveRestartEnable-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetPrimitiveRestartEnable-None-04866 - if !(self.device().api_version() >= Version::V1_3 - || self.device().enabled_features().extended_dynamic_state2) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_primitive_restart_enable`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), - RequiresAllOf(&[Requires::Feature("extended_dynamic_state2")]), - ]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::PrimitiveRestartEnable)?; Ok(()) } @@ -1224,7 +664,7 @@ where "set_primitive_restart_enable", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_primitive_restart_enable(enable); + out.set_primitive_restart_enable_unchecked(enable); }, ); @@ -1232,102 +672,22 @@ where } /// Sets the dynamic primitive topology for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the device API version is less than 1.3 and the - /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is - /// not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - /// - If the [`geometry_shader`](crate::device::Features::geometry_shader) feature is not - /// enabled, panics if `topology` is a `WithAdjacency` topology. - /// - If the [`tessellation_shader`](crate::device::Features::tessellation_shader) feature is - /// not enabled, panics if `topology` is `PatchList`. - pub fn set_primitive_topology(&mut self, topology: PrimitiveTopology) -> &mut Self { - self.validate_set_primitive_topology(topology).unwrap(); + pub fn set_primitive_topology( + &mut self, + topology: PrimitiveTopology, + ) -> Result<&mut Self, Box> { + self.validate_set_primitive_topology(topology)?; - unsafe { - self.set_primitive_topology_unchecked(topology); - } - - self + unsafe { Ok(self.set_primitive_topology_unchecked(topology)) } } fn validate_set_primitive_topology( &self, topology: PrimitiveTopology, - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::PrimitiveTopology)?; + ) -> Result<(), Box> { + self.inner.validate_set_primitive_topology(topology)?; - // VUID-vkCmdSetPrimitiveTopology-primitiveTopology-parameter - topology.validate_device(self.device())?; - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetPrimitiveTopology-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetPrimitiveTopology-None-03347 - if !(self.device().api_version() >= Version::V1_3 - || self.device().enabled_features().extended_dynamic_state) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_primitive_topology`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), - RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), - ]), - }); - } - - // VUID? - // Since these requirements exist for fixed state when creating the pipeline, - // I assume they exist for dynamic state as well. - match topology { - PrimitiveTopology::TriangleFan => { - if self.device().enabled_extensions().khr_portability_subset - && !self.device().enabled_features().triangle_fans - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "this device is a portability subset device, and `topology` \ - is `PrimitiveTopology::TriangleFan`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "triangle_fans", - )])]), - }); - } - } - PrimitiveTopology::LineListWithAdjacency - | PrimitiveTopology::LineStripWithAdjacency - | PrimitiveTopology::TriangleListWithAdjacency - | PrimitiveTopology::TriangleStripWithAdjacency => { - if !self.device().enabled_features().geometry_shader { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`topology` is `PrimitiveTopology::*WithAdjacency`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "geometry_shader", - )])]), - }); - } - } - PrimitiveTopology::PatchList => { - if !self.device().enabled_features().tessellation_shader { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`topology` is `PrimitiveTopology::PatchList`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "tessellation_shader", - )])]), - }); - } - } - _ => (), - } + self.validate_graphics_pipeline_fixed_state(DynamicState::PrimitiveTopology)?; Ok(()) } @@ -1342,7 +702,7 @@ where "set_primitive_topology", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_primitive_topology(topology); + out.set_primitive_topology_unchecked(topology); }, ); @@ -1350,52 +710,22 @@ where } /// Sets whether dynamic rasterizer discard is enabled for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the device API version is less than 1.3 and the - /// [`extended_dynamic_state2`](crate::device::Features::extended_dynamic_state2) feature is - /// not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - pub fn set_rasterizer_discard_enable(&mut self, enable: bool) -> &mut Self { - self.validate_set_rasterizer_discard_enable(enable).unwrap(); + pub fn set_rasterizer_discard_enable( + &mut self, + enable: bool, + ) -> Result<&mut Self, Box> { + self.validate_set_rasterizer_discard_enable(enable)?; - unsafe { - self.set_rasterizer_discard_enable_unchecked(enable); - } - - self + unsafe { Ok(self.set_rasterizer_discard_enable_unchecked(enable)) } } fn validate_set_rasterizer_discard_enable( &self, - _enable: bool, - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::RasterizerDiscardEnable)?; + enable: bool, + ) -> Result<(), Box> { + self.inner.validate_set_rasterizer_discard_enable(enable)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetRasterizerDiscardEnable-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetRasterizerDiscardEnable-None-04871 - if !(self.device().api_version() >= Version::V1_3 - || self.device().enabled_features().extended_dynamic_state2) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_rasterizer_discard_enable`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), - RequiresAllOf(&[Requires::Feature("extended_dynamic_state2")]), - ]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::RasterizerDiscardEnable)?; Ok(()) } @@ -1407,7 +737,7 @@ where "set_rasterizer_discard_enable", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_rasterizer_discard_enable(enable); + out.set_rasterizer_discard_enable_unchecked(enable); }, ); @@ -1415,77 +745,24 @@ where } /// Sets the dynamic scissors for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - /// - Panics if the highest scissor slot being set is greater than the - /// [`max_viewports`](crate::device::Properties::max_viewports) device property. - /// - If the [`multi_viewport`](crate::device::Features::multi_viewport) feature is not enabled, - /// panics if `first_scissor` is not 0, or if more than 1 scissor is provided. pub fn set_scissor( &mut self, first_scissor: u32, scissors: SmallVec<[Scissor; 2]>, - ) -> &mut Self { - self.validate_set_scissor(first_scissor, &scissors).unwrap(); + ) -> Result<&mut Self, Box> { + self.validate_set_scissor(first_scissor, &scissors)?; - unsafe { - self.set_scissor_unchecked(first_scissor, scissors); - } - - self + unsafe { Ok(self.set_scissor_unchecked(first_scissor, scissors)) } } fn validate_set_scissor( &self, first_scissor: u32, scissors: &[Scissor], - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::Scissor)?; + ) -> Result<(), Box> { + self.inner.validate_set_scissor(first_scissor, scissors)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetScissor-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetScissor-firstScissor-00592 - if first_scissor + scissors.len() as u32 - > self.device().physical_device().properties().max_viewports - { - return Err(SetDynamicStateError::MaxViewportsExceeded { - provided: first_scissor + scissors.len() as u32, - max: self.device().physical_device().properties().max_viewports, - }); - } - - if !self.device().enabled_features().multi_viewport { - // VUID-vkCmdSetScissor-firstScissor-00593 - if first_scissor != 0 { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`first_scissor` is not `0`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "multi_viewport", - )])]), - }); - } - - // VUID-vkCmdSetScissor-scissorCount-00594 - if scissors.len() > 1 { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`scissors.len()` is greater than `1`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "multi_viewport", - )])]), - }); - } - } + self.validate_graphics_pipeline_fixed_state(DynamicState::Scissor)?; Ok(()) } @@ -1507,7 +784,7 @@ where "set_scissor", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_scissor(first_scissor, &scissors); + out.set_scissor_unchecked(first_scissor, &scissors); }, ); @@ -1515,74 +792,22 @@ where } /// Sets the dynamic scissors with count for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the device API version is less than 1.3 and the - /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is - /// not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - /// - Panics if the highest scissor slot being set is greater than the - /// [`max_viewports`](crate::device::Properties::max_viewports) device property. - /// - If the [`multi_viewport`](crate::device::Features::multi_viewport) feature is not enabled, - /// panics if more than 1 scissor is provided. - pub fn set_scissor_with_count(&mut self, scissors: SmallVec<[Scissor; 2]>) -> &mut Self { - self.validate_set_scissor_with_count(&scissors).unwrap(); + pub fn set_scissor_with_count( + &mut self, + scissors: SmallVec<[Scissor; 2]>, + ) -> Result<&mut Self, Box> { + self.validate_set_scissor_with_count(&scissors)?; - unsafe { - self.set_scissor_with_count_unchecked(scissors); - } - - self + unsafe { Ok(self.set_scissor_with_count_unchecked(scissors)) } } fn validate_set_scissor_with_count( &self, scissors: &[Scissor], - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::ScissorWithCount)?; + ) -> Result<(), Box> { + self.inner.validate_set_scissor_with_count(scissors)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetScissorWithCount-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetScissorWithCount-None-03396 - if !(self.device().api_version() >= Version::V1_3 - || self.device().enabled_features().extended_dynamic_state) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_scissor_with_count`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), - RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), - ]), - }); - } - - // VUID-vkCmdSetScissorWithCount-scissorCount-03397 - if scissors.len() as u32 > self.device().physical_device().properties().max_viewports { - return Err(SetDynamicStateError::MaxViewportsExceeded { - provided: scissors.len() as u32, - max: self.device().physical_device().properties().max_viewports, - }); - } - - // VUID-vkCmdSetScissorWithCount-scissorCount-03398 - if !self.device().enabled_features().multi_viewport && scissors.len() > 1 { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`scissors.len()` is greater than `1`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "multi_viewport", - )])]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::ScissorWithCount)?; Ok(()) } @@ -1597,7 +822,7 @@ where "set_scissor_with_count", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_scissor_with_count(&scissors); + out.set_scissor_with_count_unchecked(&scissors); }, ); @@ -1605,45 +830,25 @@ where } /// Sets the dynamic stencil compare mask on one or both faces for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the currently bound graphics pipeline already contains this state internally. pub fn set_stencil_compare_mask( &mut self, faces: StencilFaces, compare_mask: u32, - ) -> &mut Self { - self.validate_set_stencil_compare_mask(faces, compare_mask) - .unwrap(); + ) -> Result<&mut Self, Box> { + self.validate_set_stencil_compare_mask(faces, compare_mask)?; - unsafe { - self.set_stencil_compare_mask_unchecked(faces, compare_mask); - } - - self + unsafe { Ok(self.set_stencil_compare_mask_unchecked(faces, compare_mask)) } } fn validate_set_stencil_compare_mask( &self, faces: StencilFaces, - _compare_mask: u32, - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::StencilCompareMask)?; + compare_mask: u32, + ) -> Result<(), Box> { + self.inner + .validate_set_stencil_compare_mask(faces, compare_mask)?; - // VUID-vkCmdSetStencilCompareMask-faceMask-parameter - faces.validate_device(self.device())?; - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetStencilCompareMask-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::StencilCompareMask)?; Ok(()) } @@ -1668,7 +873,7 @@ where "set_stencil_compare_mask", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_stencil_compare_mask(faces, compare_mask); + out.set_stencil_compare_mask_unchecked(faces, compare_mask); }, ); @@ -1676,14 +881,6 @@ where } /// Sets the dynamic stencil ops on one or both faces for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the device API version is less than 1.3 and the - /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is - /// not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. pub fn set_stencil_op( &mut self, faces: StencilFaces, @@ -1691,15 +888,12 @@ where pass_op: StencilOp, depth_fail_op: StencilOp, compare_op: CompareOp, - ) -> &mut Self { - self.validate_set_stencil_op(faces, fail_op, pass_op, depth_fail_op, compare_op) - .unwrap(); + ) -> Result<&mut Self, Box> { + self.validate_set_stencil_op(faces, fail_op, pass_op, depth_fail_op, compare_op)?; unsafe { - self.set_stencil_op_unchecked(faces, fail_op, pass_op, depth_fail_op, compare_op); + Ok(self.set_stencil_op_unchecked(faces, fail_op, pass_op, depth_fail_op, compare_op)) } - - self } fn validate_set_stencil_op( @@ -1709,46 +903,11 @@ where pass_op: StencilOp, depth_fail_op: StencilOp, compare_op: CompareOp, - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::StencilOp)?; + ) -> Result<(), Box> { + self.inner + .validate_set_stencil_op(faces, fail_op, pass_op, depth_fail_op, compare_op)?; - // VUID-vkCmdSetStencilOp-faceMask-parameter - faces.validate_device(self.device())?; - - // VUID-vkCmdSetStencilOp-failOp-parameter - fail_op.validate_device(self.device())?; - - // VUID-vkCmdSetStencilOp-passOp-parameter - pass_op.validate_device(self.device())?; - - // VUID-vkCmdSetStencilOp-depthFailOp-parameter - depth_fail_op.validate_device(self.device())?; - - // VUID-vkCmdSetStencilOp-compareOp-parameter - compare_op.validate_device(self.device())?; - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetStencilOp-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetStencilOp-None-03351 - if !(self.device().api_version() >= Version::V1_3 - || self.device().enabled_features().extended_dynamic_state) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_stencil_op`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), - RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), - ]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::StencilOp)?; Ok(()) } @@ -1786,7 +945,7 @@ where "set_stencil_op", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_stencil_op(faces, fail_op, pass_op, depth_fail_op, compare_op); + out.set_stencil_op_unchecked(faces, fail_op, pass_op, depth_fail_op, compare_op); }, ); @@ -1794,41 +953,25 @@ where } /// Sets the dynamic stencil reference on one or both faces for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - pub fn set_stencil_reference(&mut self, faces: StencilFaces, reference: u32) -> &mut Self { - self.validate_set_stencil_reference(faces, reference) - .unwrap(); + pub fn set_stencil_reference( + &mut self, + faces: StencilFaces, + reference: u32, + ) -> Result<&mut Self, Box> { + self.validate_set_stencil_reference(faces, reference)?; - unsafe { - self.set_stencil_reference_unchecked(faces, reference); - } - - self + unsafe { Ok(self.set_stencil_reference_unchecked(faces, reference)) } } fn validate_set_stencil_reference( &self, faces: StencilFaces, - _reference: u32, - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::StencilReference)?; + reference: u32, + ) -> Result<(), Box> { + self.inner + .validate_set_stencil_reference(faces, reference)?; - // VUID-vkCmdSetStencilReference-faceMask-parameter - faces.validate_device(self.device())?; - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetStencilReference-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::StencilReference)?; Ok(()) } @@ -1853,7 +996,7 @@ where "set_stencil_reference", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_stencil_reference(faces, reference); + out.set_stencil_reference_unchecked(faces, reference); }, ); @@ -1861,49 +1004,19 @@ where } /// Sets whether dynamic stencil testing is enabled for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the device API version is less than 1.3 and the - /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is - /// not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - pub fn set_stencil_test_enable(&mut self, enable: bool) -> &mut Self { - self.validate_set_stencil_test_enable(enable).unwrap(); + pub fn set_stencil_test_enable( + &mut self, + enable: bool, + ) -> Result<&mut Self, Box> { + self.validate_set_stencil_test_enable(enable)?; - unsafe { - self.set_stencil_test_enable_unchecked(enable); - } - - self + unsafe { Ok(self.set_stencil_test_enable_unchecked(enable)) } } - fn validate_set_stencil_test_enable(&self, _enable: bool) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::StencilTestEnable)?; + fn validate_set_stencil_test_enable(&self, enable: bool) -> Result<(), Box> { + self.inner.validate_set_stencil_test_enable(enable)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetStencilTestEnable-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetStencilTestEnable-None-03350 - if !(self.device().api_version() >= Version::V1_3 - || self.device().enabled_features().extended_dynamic_state) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_stencil_test_enable`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), - RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), - ]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::StencilTestEnable)?; Ok(()) } @@ -1915,7 +1028,7 @@ where "set_stencil_test_enable", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_stencil_test_enable(enable); + out.set_stencil_test_enable_unchecked(enable); }, ); @@ -1923,41 +1036,25 @@ where } /// Sets the dynamic stencil write mask on one or both faces for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - pub fn set_stencil_write_mask(&mut self, faces: StencilFaces, write_mask: u32) -> &mut Self { - self.validate_set_stencil_write_mask(faces, write_mask) - .unwrap(); + pub fn set_stencil_write_mask( + &mut self, + faces: StencilFaces, + write_mask: u32, + ) -> Result<&mut Self, Box> { + self.validate_set_stencil_write_mask(faces, write_mask)?; - unsafe { - self.set_stencil_write_mask_unchecked(faces, write_mask); - } - - self + unsafe { Ok(self.set_stencil_write_mask_unchecked(faces, write_mask)) } } fn validate_set_stencil_write_mask( &self, faces: StencilFaces, - _write_mask: u32, - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::StencilWriteMask)?; + write_mask: u32, + ) -> Result<(), Box> { + self.inner + .validate_set_stencil_write_mask(faces, write_mask)?; - // VUID-vkCmdSetStencilWriteMask-faceMask-parameter - faces.validate_device(self.device())?; - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetStencilWriteMask-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::StencilWriteMask)?; Ok(()) } @@ -1982,7 +1079,7 @@ where "set_stencil_write_mask", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_stencil_write_mask(faces, write_mask); + out.set_stencil_write_mask_unchecked(faces, write_mask); }, ); @@ -1990,78 +1087,25 @@ where } /// Sets the dynamic viewports for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - /// - Panics if the highest viewport slot being set is greater than the - /// [`max_viewports`](crate::device::Properties::max_viewports) device property. - /// - If the [`multi_viewport`](crate::device::Features::multi_viewport) feature is not enabled, - /// panics if `first_viewport` is not 0, or if more than 1 viewport is provided. pub fn set_viewport( &mut self, first_viewport: u32, viewports: SmallVec<[Viewport; 2]>, - ) -> &mut Self { - self.validate_set_viewport(first_viewport, &viewports) - .unwrap(); + ) -> Result<&mut Self, Box> { + self.validate_set_viewport(first_viewport, &viewports)?; - unsafe { - self.set_viewport_unchecked(first_viewport, viewports); - } - - self + unsafe { Ok(self.set_viewport_unchecked(first_viewport, viewports)) } } fn validate_set_viewport( &self, first_viewport: u32, viewports: &[Viewport], - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::Viewport)?; + ) -> Result<(), Box> { + self.inner + .validate_set_viewport(first_viewport, viewports)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetViewport-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetViewport-firstViewport-01223 - if first_viewport + viewports.len() as u32 - > self.device().physical_device().properties().max_viewports - { - return Err(SetDynamicStateError::MaxViewportsExceeded { - provided: first_viewport + viewports.len() as u32, - max: self.device().physical_device().properties().max_viewports, - }); - } - - if !self.device().enabled_features().multi_viewport { - // VUID-vkCmdSetViewport-firstViewport-01224 - if first_viewport != 0 { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`first_scissors` is not `0`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "multi_viewport", - )])]), - }); - } - - // VUID-vkCmdSetViewport-viewportCount-01225 - if viewports.len() > 1 { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`viewports.len()` is greater than `1`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "multi_viewport", - )])]), - }); - } - } + self.validate_graphics_pipeline_fixed_state(DynamicState::Viewport)?; Ok(()) } @@ -2081,7 +1125,7 @@ where "set_viewport", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_viewport(first_viewport, &viewports); + out.set_viewport_unchecked(first_viewport, &viewports); }, ); @@ -2089,74 +1133,22 @@ where } /// Sets the dynamic viewports with count for future draw calls. - /// - /// # Panics - /// - /// - Panics if the queue family of the command buffer does not support graphics operations. - /// - Panics if the device API version is less than 1.3 and the - /// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature is - /// not enabled on the device. - /// - Panics if the currently bound graphics pipeline already contains this state internally. - /// - Panics if the highest viewport slot being set is greater than the - /// [`max_viewports`](crate::device::Properties::max_viewports) device property. - /// - If the [`multi_viewport`](crate::device::Features::multi_viewport) feature is not enabled, - /// panics if more than 1 viewport is provided. - pub fn set_viewport_with_count(&mut self, viewports: SmallVec<[Viewport; 2]>) -> &mut Self { - self.validate_set_viewport_with_count(&viewports).unwrap(); + pub fn set_viewport_with_count( + &mut self, + viewports: SmallVec<[Viewport; 2]>, + ) -> Result<&mut Self, Box> { + self.validate_set_viewport_with_count(&viewports)?; - unsafe { - self.set_viewport_with_count_unchecked(viewports); - } - - self + unsafe { Ok(self.set_viewport_with_count_unchecked(viewports)) } } fn validate_set_viewport_with_count( &self, viewports: &[Viewport], - ) -> Result<(), SetDynamicStateError> { - self.validate_pipeline_fixed_state(DynamicState::ViewportWithCount)?; + ) -> Result<(), Box> { + self.inner.validate_set_viewport_with_count(viewports)?; - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdSetViewportWithCount-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(SetDynamicStateError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdSetViewportWithCount-None-03393 - if !(self.device().api_version() >= Version::V1_3 - || self.device().enabled_features().extended_dynamic_state) - { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::set_viewport_with_count`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), - RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), - ]), - }); - } - - // VUID-vkCmdSetViewportWithCount-viewportCount-03394 - if viewports.len() as u32 > self.device().physical_device().properties().max_viewports { - return Err(SetDynamicStateError::MaxViewportsExceeded { - provided: viewports.len() as u32, - max: self.device().physical_device().properties().max_viewports, - }); - } - - // VUID-vkCmdSetViewportWithCount-viewportCount-03395 - if !self.device().enabled_features().multi_viewport && viewports.len() > 1 { - return Err(SetDynamicStateError::RequirementNotMet { - required_for: "`viewports.len()` is greater than `1`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "multi_viewport", - )])]), - }); - } + self.validate_graphics_pipeline_fixed_state(DynamicState::ViewportWithCount)?; Ok(()) } @@ -2171,7 +1163,7 @@ where "set_viewport", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.set_viewport_with_count(&viewports); + out.set_viewport_with_count_unchecked(&viewports); }, ); @@ -2183,24 +1175,92 @@ impl UnsafeCommandBufferBuilder where A: CommandBufferAllocator, { - /// Calls `vkCmdSetBlendConstants` on the builder. - #[inline] - pub unsafe fn set_blend_constants(&mut self, constants: [f32; 4]) -> &mut Self { + pub unsafe fn set_blend_constants( + &mut self, + constants: [f32; 4], + ) -> Result<&mut Self, Box> { + self.validate_set_blend_constants(constants)?; + + Ok(self.set_blend_constants_unchecked(constants)) + } + + fn validate_set_blend_constants( + &self, + _constants: [f32; 4], + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetBlendConstants-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_blend_constants_unchecked(&mut self, constants: [f32; 4]) -> &mut Self { let fns = self.device().fns(); (fns.v1_0.cmd_set_blend_constants)(self.handle(), &constants); self } - /// Calls `vkCmdSetColorWriteEnableEXT` on the builder. - /// - /// If the list is empty then the command is automatically ignored. - pub unsafe fn set_color_write_enable(&mut self, enables: &[bool]) -> &mut Self { + pub unsafe fn set_color_write_enable( + &mut self, + enables: &[bool], + ) -> Result<&mut Self, Box> { + self.validate_set_color_write_enable(enables)?; + + Ok(self.set_color_write_enable_unchecked(enables)) + } + + fn validate_set_color_write_enable( + &self, + _enables: &[bool], + ) -> Result<(), Box> { + if !self.device().enabled_features().color_write_enable { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "ext_color_write_enable", + )])]), + vuids: &["VUID-vkCmdSetColorWriteEnableEXT-None-04803"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetColorWriteEnableEXT-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_color_write_enable_unchecked(&mut self, enables: &[bool]) -> &mut Self { let enables = enables .iter() .copied() .map(|v| v as ash::vk::Bool32) .collect::>(); + if enables.is_empty() { return self; } @@ -2215,9 +1275,56 @@ where self } - /// Calls `vkCmdSetCullModeEXT` on the builder. - #[inline] - pub unsafe fn set_cull_mode(&mut self, cull_mode: CullMode) -> &mut Self { + pub unsafe fn set_cull_mode( + &mut self, + cull_mode: CullMode, + ) -> Result<&mut Self, Box> { + self.validate_set_cull_mode(cull_mode)?; + + Ok(self.set_cull_mode_unchecked(cull_mode)) + } + + fn validate_set_cull_mode(&self, cull_mode: CullMode) -> Result<(), Box> { + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), + ]), + vuids: &["VUID-vkCmdSetCullMode-None-03384"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetCullMode-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + cull_mode + .validate_device(self.device()) + .map_err(|err| ValidationError { + context: "cull_mode".into(), + vuids: &["VUID-vkCmdSetCullMode-cullMode-parameter"], + ..ValidationError::from_requirement(err) + })?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_cull_mode_unchecked(&mut self, cull_mode: CullMode) -> &mut Self { let fns = self.device().fns(); if self.device().api_version() >= Version::V1_3 { @@ -2229,13 +1336,57 @@ where self } - /// Calls `vkCmdSetDepthBias` on the builder. - #[inline] pub unsafe fn set_depth_bias( &mut self, constant_factor: f32, clamp: f32, slope_factor: f32, + ) -> Result<&mut Self, Box> { + self.validate_set_depth_bias(constant_factor, clamp, slope_factor)?; + + Ok(self.set_depth_bias_unchecked(constant_factor, clamp, slope_factor)) + } + + fn validate_set_depth_bias( + &self, + _constant_factor: f32, + clamp: f32, + _slope_factor: f32, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetDepthBias-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + if clamp != 0.0 && !self.device().enabled_features().depth_bias_clamp { + return Err(Box::new(ValidationError { + context: "clamp".into(), + problem: "is not `0.0`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "depth_bias_clamp", + )])]), + vuids: &["VUID-vkCmdSetDepthBias-depthBiasClamp-00790"], + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_depth_bias_unchecked( + &mut self, + constant_factor: f32, + clamp: f32, + slope_factor: f32, ) -> &mut Self { let fns = self.device().fns(); (fns.v1_0.cmd_set_depth_bias)(self.handle(), constant_factor, clamp, slope_factor); @@ -2243,9 +1394,48 @@ where self } - /// Calls `vkCmdSetDepthBiasEnableEXT` on the builder. - #[inline] - pub unsafe fn set_depth_bias_enable(&mut self, enable: bool) -> &mut Self { + pub unsafe fn set_depth_bias_enable( + &mut self, + enable: bool, + ) -> Result<&mut Self, Box> { + self.validate_set_depth_bias_enable(enable)?; + + Ok(self.set_depth_bias_enable_unchecked(enable)) + } + + fn validate_set_depth_bias_enable(&self, _enable: bool) -> Result<(), Box> { + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state2) + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::Feature("extended_dynamic_state2")]), + ]), + vuids: &["VUID-vkCmdSetDepthBiasEnable-None-04872"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetDepthBiasEnable-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_depth_bias_enable_unchecked(&mut self, enable: bool) -> &mut Self { let fns = self.device().fns(); if self.device().api_version() >= Version::V1_3 { @@ -2258,18 +1448,117 @@ where self } - /// Calls `vkCmdSetDepthBounds` on the builder. - #[inline] - pub unsafe fn set_depth_bounds(&mut self, bounds: RangeInclusive) -> &mut Self { + pub unsafe fn set_depth_bounds( + &mut self, + bounds: RangeInclusive, + ) -> Result<&mut Self, Box> { + self.validate_set_depth_bounds(bounds.clone())?; + + Ok(self.set_depth_bounds_unchecked(bounds)) + } + + fn validate_set_depth_bounds( + &self, + bounds: RangeInclusive, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetDepthBounds-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + if !self + .device() + .enabled_extensions() + .ext_depth_range_unrestricted + { + if !(0.0..=1.0).contains(bounds.start()) { + return Err(Box::new(ValidationError { + context: "bounds.start()".into(), + problem: "is not between `0.0` and `1.0` inclusive".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "ext_depth_range_unrestricted", + )])]), + vuids: &["VUID-vkCmdSetDepthBounds-minDepthBounds-00600"], + })); + } + + if !(0.0..=1.0).contains(bounds.end()) { + return Err(Box::new(ValidationError { + context: "bounds.end()".into(), + problem: "is not between `0.0` and `1.0` inclusive".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "ext_depth_range_unrestricted", + )])]), + vuids: &["VUID-vkCmdSetDepthBounds-maxDepthBounds-00601"], + })); + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_depth_bounds_unchecked(&mut self, bounds: RangeInclusive) -> &mut Self { let fns = self.device().fns(); (fns.v1_0.cmd_set_depth_bounds)(self.handle(), *bounds.start(), *bounds.end()); self } - /// Calls `vkCmdSetDepthBoundsTestEnableEXT` on the builder. - #[inline] - pub unsafe fn set_depth_bounds_test_enable(&mut self, enable: bool) -> &mut Self { + pub unsafe fn set_depth_bounds_test_enable( + &mut self, + enable: bool, + ) -> Result<&mut Self, Box> { + self.validate_set_depth_bounds_test_enable(enable)?; + + Ok(self.set_depth_bounds_test_enable_unchecked(enable)) + } + + fn validate_set_depth_bounds_test_enable( + &self, + _enable: bool, + ) -> Result<(), Box> { + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), + ]), + vuids: &["VUID-vkCmdSetDepthBoundsTestEnable-None-03349"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetDepthBoundsTestEnable-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_depth_bounds_test_enable_unchecked(&mut self, enable: bool) -> &mut Self { let fns = self.device().fns(); if self.device().api_version() >= Version::V1_3 { @@ -2282,9 +1571,59 @@ where self } - /// Calls `vkCmdSetDepthCompareOpEXT` on the builder. - #[inline] - pub unsafe fn set_depth_compare_op(&mut self, compare_op: CompareOp) -> &mut Self { + pub unsafe fn set_depth_compare_op( + &mut self, + compare_op: CompareOp, + ) -> Result<&mut Self, Box> { + self.validate_set_depth_compare_op(compare_op)?; + + Ok(self.set_depth_compare_op_unchecked(compare_op)) + } + + fn validate_set_depth_compare_op( + &self, + compare_op: CompareOp, + ) -> Result<(), Box> { + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), + ]), + vuids: &["VUID-vkCmdSetDepthCompareOp-None-03353"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetDepthCompareOp-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + compare_op + .validate_device(self.device()) + .map_err(|err| ValidationError { + context: "compare_op".into(), + vuids: &["VUID-vkCmdSetDepthCompareOp-depthCompareOp-parameter"], + ..ValidationError::from_requirement(err) + })?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_depth_compare_op_unchecked(&mut self, compare_op: CompareOp) -> &mut Self { let fns = self.device().fns(); if self.device().api_version() >= Version::V1_3 { @@ -2299,9 +1638,48 @@ where self } - /// Calls `vkCmdSetDepthTestEnableEXT` on the builder. - #[inline] - pub unsafe fn set_depth_test_enable(&mut self, enable: bool) -> &mut Self { + pub unsafe fn set_depth_test_enable( + &mut self, + enable: bool, + ) -> Result<&mut Self, Box> { + self.validate_set_depth_test_enable(enable)?; + + Ok(self.set_depth_test_enable_unchecked(enable)) + } + + fn validate_set_depth_test_enable(&self, _enable: bool) -> Result<(), Box> { + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), + ]), + vuids: &["VUID-vkCmdSetDepthTestEnable-None-03352"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetDepthTestEnable-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_depth_test_enable_unchecked(&mut self, enable: bool) -> &mut Self { let fns = self.device().fns(); if self.device().api_version() >= Version::V1_3 { @@ -2316,9 +1694,48 @@ where self } - /// Calls `vkCmdSetDepthWriteEnableEXT` on the builder. - #[inline] - pub unsafe fn set_depth_write_enable(&mut self, enable: bool) -> &mut Self { + pub unsafe fn set_depth_write_enable( + &mut self, + enable: bool, + ) -> Result<&mut Self, Box> { + self.validate_set_depth_write_enable(enable)?; + + Ok(self.set_depth_write_enable_unchecked(enable)) + } + + fn validate_set_depth_write_enable(&self, _enable: bool) -> Result<(), Box> { + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), + ]), + vuids: &["VUID-vkCmdSetDepthWriteEnable-None-03354"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetDepthWriteEnable-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_depth_write_enable_unchecked(&mut self, enable: bool) -> &mut Self { let fns = self.device().fns(); if self.device().api_version() >= Version::V1_3 { @@ -2331,13 +1748,64 @@ where self } - /// Calls `vkCmdSetDiscardRectangleEXT` on the builder. - /// - /// If the list is empty then the command is automatically ignored. pub unsafe fn set_discard_rectangle( &mut self, first_rectangle: u32, rectangles: &[Scissor], + ) -> Result<&mut Self, Box> { + self.validate_set_discard_rectangle(first_rectangle, rectangles)?; + + Ok(self.set_discard_rectangle_unchecked(first_rectangle, rectangles)) + } + + fn validate_set_discard_rectangle( + &self, + first_rectangle: u32, + rectangles: &[Scissor], + ) -> Result<(), Box> { + if self.device().enabled_extensions().ext_discard_rectangles { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "ext_discard_rectangles", + )])]), + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetDiscardRectangle-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + let properties = self.device().physical_device().properties(); + + if first_rectangle + rectangles.len() as u32 > properties.max_discard_rectangles.unwrap() { + return Err(Box::new(ValidationError { + problem: "`first_rectangle + rectangles.len()` exceeds the \ + `max_discard_rectangles` limit" + .into(), + vuids: &["VUID-vkCmdSetDiscardRectangleEXT-firstDiscardRectangle-00585"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_discard_rectangle_unchecked( + &mut self, + first_rectangle: u32, + rectangles: &[Scissor], ) -> &mut Self { let rectangles = rectangles .iter() @@ -2358,9 +1826,55 @@ where self } - /// Calls `vkCmdSetFrontFaceEXT` on the builder. - #[inline] - pub unsafe fn set_front_face(&mut self, face: FrontFace) -> &mut Self { + pub unsafe fn set_front_face( + &mut self, + face: FrontFace, + ) -> Result<&mut Self, Box> { + self.validate_set_front_face(face)?; + + Ok(self.set_front_face_unchecked(face)) + } + + fn validate_set_front_face(&self, face: FrontFace) -> Result<(), Box> { + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), + ]), + vuids: &["VUID-vkCmdSetFrontFace-None-03383"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetFrontFace-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + face.validate_device(self.device()) + .map_err(|err| ValidationError { + context: "face".into(), + vuids: &["VUID-vkCmdSetFrontFace-frontFace-parameter"], + ..ValidationError::from_requirement(err) + })?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_front_face_unchecked(&mut self, face: FrontFace) -> &mut Self { let fns = self.device().fns(); if self.device().api_version() >= Version::V1_3 { @@ -2372,36 +1886,230 @@ where self } - /// Calls `vkCmdSetLineStippleEXT` on the builder. - #[inline] - pub unsafe fn set_line_stipple(&mut self, factor: u32, pattern: u16) -> &mut Self { + pub unsafe fn set_line_stipple( + &mut self, + factor: u32, + pattern: u16, + ) -> Result<&mut Self, Box> { + self.validate_set_line_stipple(factor, pattern)?; + + Ok(self.set_line_stipple_unchecked(factor, pattern)) + } + + fn validate_set_line_stipple( + &self, + factor: u32, + _pattern: u16, + ) -> Result<(), Box> { + if !self.device().enabled_extensions().ext_line_rasterization { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "ext_line_rasterization", + )])]), + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetLineStippleEXT-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + if !(1..=256).contains(&factor) { + return Err(Box::new(ValidationError { + context: "factor".into(), + problem: "is not between 1 and 256 inclusive".into(), + vuids: &["VUID-vkCmdSetLineStippleEXT-lineStippleFactor-02776"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_line_stipple_unchecked(&mut self, factor: u32, pattern: u16) -> &mut Self { let fns = self.device().fns(); (fns.ext_line_rasterization.cmd_set_line_stipple_ext)(self.handle(), factor, pattern); self } - /// Calls `vkCmdSetLineWidth` on the builder. - #[inline] - pub unsafe fn set_line_width(&mut self, line_width: f32) -> &mut Self { + pub unsafe fn set_line_width( + &mut self, + line_width: f32, + ) -> Result<&mut Self, Box> { + self.validate_set_line_width(line_width)?; + + Ok(self.set_line_width_unchecked(line_width)) + } + + fn validate_set_line_width(&self, line_width: f32) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetLineWidth-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + if line_width != 1.0 && !self.device().enabled_features().wide_lines { + return Err(Box::new(ValidationError { + context: "line_width".into(), + problem: "is not 1.0".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "wide_lines", + )])]), + vuids: &["VUID-vkCmdSetLineWidth-lineWidth-00788"], + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_line_width_unchecked(&mut self, line_width: f32) -> &mut Self { let fns = self.device().fns(); (fns.v1_0.cmd_set_line_width)(self.handle(), line_width); self } - /// Calls `vkCmdSetLogicOpEXT` on the builder. - #[inline] - pub unsafe fn set_logic_op(&mut self, logic_op: LogicOp) -> &mut Self { + pub unsafe fn set_logic_op( + &mut self, + logic_op: LogicOp, + ) -> Result<&mut Self, Box> { + self.validate_set_logic_op(logic_op)?; + + Ok(self.set_logic_op_unchecked(logic_op)) + } + + fn validate_set_logic_op(&self, logic_op: LogicOp) -> Result<(), Box> { + if !self + .device() + .enabled_features() + .extended_dynamic_state2_logic_op + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "extended_dynamic_state2_logic_op", + )])]), + vuids: &["VUID-vkCmdSetLogicOpEXT-None-04867"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetLogicOpEXT-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + logic_op + .validate_device(self.device()) + .map_err(|err| ValidationError { + context: "logic_op".into(), + vuids: &["VUID-vkCmdSetLogicOpEXT-logicOp-parameter"], + ..ValidationError::from_requirement(err) + })?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_logic_op_unchecked(&mut self, logic_op: LogicOp) -> &mut Self { let fns = self.device().fns(); (fns.ext_extended_dynamic_state2.cmd_set_logic_op_ext)(self.handle(), logic_op.into()); self } - /// Calls `vkCmdSetPatchControlPointsEXT` on the builder. - #[inline] - pub unsafe fn set_patch_control_points(&mut self, num: u32) -> &mut Self { + pub unsafe fn set_patch_control_points( + &mut self, + num: u32, + ) -> Result<&mut Self, Box> { + self.validate_set_patch_control_points(num)?; + + Ok(self.set_patch_control_points_unchecked(num)) + } + + fn validate_set_patch_control_points(&self, num: u32) -> Result<(), Box> { + if !self + .device() + .enabled_features() + .extended_dynamic_state2_patch_control_points + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "extended_dynamic_state2_patch_control_points", + )])]), + vuids: &["VUID-vkCmdSetPatchControlPointsEXT-None-04873"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetPatchControlPointsEXT-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + if num == 0 { + return Err(Box::new(ValidationError { + context: "num".into(), + problem: "is zero".into(), + vuids: &["VUID-vkCmdSetPatchControlPointsEXT-patchControlPoints-04874"], + ..Default::default() + })); + } + + let properties = self.device().physical_device().properties(); + + if num > properties.max_tessellation_patch_size { + return Err(Box::new(ValidationError { + context: "num".into(), + problem: "exceeds the `max_tessellation_patch_size` limit".into(), + vuids: &["VUID-vkCmdSetPatchControlPointsEXT-patchControlPoints-04874"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_patch_control_points_unchecked(&mut self, num: u32) -> &mut Self { let fns = self.device().fns(); (fns.ext_extended_dynamic_state2 .cmd_set_patch_control_points_ext)(self.handle(), num); @@ -2409,9 +2117,51 @@ where self } - /// Calls `vkCmdSetPrimitiveRestartEnableEXT` on the builder. - #[inline] - pub unsafe fn set_primitive_restart_enable(&mut self, enable: bool) -> &mut Self { + pub unsafe fn set_primitive_restart_enable( + &mut self, + enable: bool, + ) -> Result<&mut Self, Box> { + self.validate_set_primitive_restart_enable(enable)?; + + Ok(self.set_primitive_restart_enable_unchecked(enable)) + } + + fn validate_set_primitive_restart_enable( + &self, + _enable: bool, + ) -> Result<(), Box> { + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state2) + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::Feature("extended_dynamic_state2")]), + ]), + vuids: &["VUID-vkCmdSetPrimitiveRestartEnable-None-04866"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetPrimitiveRestartEnable-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_primitive_restart_enable_unchecked(&mut self, enable: bool) -> &mut Self { let fns = self.device().fns(); if self.device().api_version() >= Version::V1_3 { @@ -2424,9 +2174,109 @@ where self } - /// Calls `vkCmdSetPrimitiveTopologyEXT` on the builder. - #[inline] - pub unsafe fn set_primitive_topology(&mut self, topology: PrimitiveTopology) -> &mut Self { + pub unsafe fn set_primitive_topology( + &mut self, + topology: PrimitiveTopology, + ) -> Result<&mut Self, Box> { + self.validate_set_primitive_topology(topology)?; + + Ok(self.set_primitive_topology_unchecked(topology)) + } + + fn validate_set_primitive_topology( + &self, + topology: PrimitiveTopology, + ) -> Result<(), Box> { + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), + ]), + vuids: &["VUID-vkCmdSetPrimitiveTopology-None-03347"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetPrimitiveTopology-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + topology + .validate_device(self.device()) + .map_err(|err| ValidationError { + context: "topology".into(), + vuids: &["VUID-vkCmdSetPrimitiveTopology-primitiveTopology-parameter"], + ..ValidationError::from_requirement(err) + })?; + + // VUID? + // Since these requirements exist for fixed state when creating the pipeline, + // I assume they exist for dynamic state as well. + match topology { + PrimitiveTopology::TriangleFan => { + if self.device().enabled_extensions().khr_portability_subset + && !self.device().enabled_features().triangle_fans + { + return Err(Box::new(ValidationError { + problem: "this device is a portability subset device, and `topology` \ + is `PrimitiveTopology::TriangleFan`" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "triangle_fans", + )])]), + ..Default::default() + })); + } + } + PrimitiveTopology::LineListWithAdjacency + | PrimitiveTopology::LineStripWithAdjacency + | PrimitiveTopology::TriangleListWithAdjacency + | PrimitiveTopology::TriangleStripWithAdjacency => { + if !self.device().enabled_features().geometry_shader { + return Err(Box::new(ValidationError { + problem: "`topology` is `PrimitiveTopology::*WithAdjacency`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "geometry_shader", + )])]), + ..Default::default() + })); + } + } + PrimitiveTopology::PatchList => { + if !self.device().enabled_features().tessellation_shader { + return Err(Box::new(ValidationError { + problem: "`topology` is `PrimitiveTopology::PatchList`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "tessellation_shader", + )])]), + ..Default::default() + })); + } + } + _ => (), + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_primitive_topology_unchecked( + &mut self, + topology: PrimitiveTopology, + ) -> &mut Self { let fns = self.device().fns(); if self.device().api_version() >= Version::V1_3 { @@ -2439,9 +2289,51 @@ where self } - /// Calls `vkCmdSetRasterizerDiscardEnableEXT` on the builder. - #[inline] - pub unsafe fn set_rasterizer_discard_enable(&mut self, enable: bool) -> &mut Self { + pub unsafe fn set_rasterizer_discard_enable( + &mut self, + enable: bool, + ) -> Result<&mut Self, Box> { + self.validate_set_rasterizer_discard_enable(enable)?; + + Ok(self.set_rasterizer_discard_enable_unchecked(enable)) + } + + fn validate_set_rasterizer_discard_enable( + &self, + _enable: bool, + ) -> Result<(), Box> { + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state2) + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::Feature("extended_dynamic_state2")]), + ]), + vuids: &["VUID-vkCmdSetRasterizerDiscardEnable-None-04871"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetRasterizerDiscardEnable-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_rasterizer_discard_enable_unchecked(&mut self, enable: bool) -> &mut Self { let fns = self.device().fns(); if self.device().api_version() >= Version::V1_3 { @@ -2454,99 +2346,79 @@ where self } - /// Calls `vkCmdSetStencilCompareMask` on the builder. - #[inline] - pub unsafe fn set_stencil_compare_mask( + pub unsafe fn set_scissor( &mut self, - face_mask: StencilFaces, - compare_mask: u32, - ) -> &mut Self { - let fns = self.device().fns(); - (fns.v1_0.cmd_set_stencil_compare_mask)(self.handle(), face_mask.into(), compare_mask); + first_scissor: u32, + scissors: &[Scissor], + ) -> Result<&mut Self, Box> { + self.validate_set_scissor(first_scissor, scissors)?; - self + Ok(self.set_scissor_unchecked(first_scissor, scissors)) } - /// Calls `vkCmdSetStencilOpEXT` on the builder. - #[inline] - pub unsafe fn set_stencil_op( - &mut self, - face_mask: StencilFaces, - fail_op: StencilOp, - pass_op: StencilOp, - depth_fail_op: StencilOp, - compare_op: CompareOp, - ) -> &mut Self { - let fns = self.device().fns(); - - if self.device().api_version() >= Version::V1_3 { - (fns.v1_3.cmd_set_stencil_op)( - self.handle(), - face_mask.into(), - fail_op.into(), - pass_op.into(), - depth_fail_op.into(), - compare_op.into(), - ); - } else { - (fns.ext_extended_dynamic_state.cmd_set_stencil_op_ext)( - self.handle(), - face_mask.into(), - fail_op.into(), - pass_op.into(), - depth_fail_op.into(), - compare_op.into(), - ); + fn validate_set_scissor( + &self, + first_scissor: u32, + scissors: &[Scissor], + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetScissor-commandBuffer-cmdpool"], + ..Default::default() + })); } - self - } + let properties = self.device().physical_device().properties(); - /// Calls `vkCmdSetStencilReference` on the builder. - #[inline] - pub unsafe fn set_stencil_reference( - &mut self, - face_mask: StencilFaces, - reference: u32, - ) -> &mut Self { - let fns = self.device().fns(); - (fns.v1_0.cmd_set_stencil_reference)(self.handle(), face_mask.into(), reference); - - self - } - - /// Calls `vkCmdSetStencilTestEnableEXT` on the builder. - #[inline] - pub unsafe fn set_stencil_test_enable(&mut self, enable: bool) -> &mut Self { - let fns = self.device().fns(); - - if self.device().api_version() >= Version::V1_3 { - (fns.v1_3.cmd_set_stencil_test_enable)(self.handle(), enable.into()); - } else { - (fns.ext_extended_dynamic_state - .cmd_set_stencil_test_enable_ext)(self.handle(), enable.into()); + if first_scissor + scissors.len() as u32 > properties.max_viewports { + return Err(Box::new(ValidationError { + problem: "`first_scissor + scissors.len()` exceeds the `max_viewports` limit" + .into(), + vuids: &["VUID-vkCmdSetScissor-firstScissor-00592"], + ..Default::default() + })); } - self + if !self.device().enabled_features().multi_viewport { + if first_scissor != 0 { + return Err(Box::new(ValidationError { + problem: "`first_scissor` is not 0".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "multi_viewport", + )])]), + vuids: &["VUID-vkCmdSetScissor-firstScissor-00593"], + ..Default::default() + })); + } + + if scissors.len() > 1 { + return Err(Box::new(ValidationError { + problem: "`scissors.len()` is greater than 1".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "multi_viewport", + )])]), + vuids: &["VUID-vkCmdSetScissor-scissorCount-00594"], + ..Default::default() + })); + } + } + + Ok(()) } - /// Calls `vkCmdSetStencilWriteMask` on the builder. - #[inline] - pub unsafe fn set_stencil_write_mask( + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_scissor_unchecked( &mut self, - face_mask: StencilFaces, - write_mask: u32, + first_scissor: u32, + scissors: &[Scissor], ) -> &mut Self { - let fns = self.device().fns(); - (fns.v1_0.cmd_set_stencil_write_mask)(self.handle(), face_mask.into(), write_mask); - - self - } - - /// Calls `vkCmdSetScissor` on the builder. - /// - /// If the list is empty then the command is automatically ignored. - pub unsafe fn set_scissor(&mut self, first_scissor: u32, scissors: &[Scissor]) -> &mut Self { let scissors = scissors .iter() .map(ash::vk::Rect2D::from) @@ -2566,10 +2438,72 @@ where self } - /// Calls `vkCmdSetScissorWithCountEXT` on the builder. - /// - /// If the list is empty then the command is automatically ignored. - pub unsafe fn set_scissor_with_count(&mut self, scissors: &[Scissor]) -> &mut Self { + pub unsafe fn set_scissor_with_count( + &mut self, + scissors: &[Scissor], + ) -> Result<&mut Self, Box> { + self.validate_set_scissor_with_count(scissors)?; + + Ok(self.set_scissor_with_count_unchecked(scissors)) + } + + fn validate_set_scissor_with_count( + &self, + scissors: &[Scissor], + ) -> Result<(), Box> { + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), + ]), + vuids: &["VUID-vkCmdSetScissorWithCount-None-03396"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetScissorWithCount-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + let properties = self.device().physical_device().properties(); + + if scissors.len() as u32 > properties.max_viewports { + return Err(Box::new(ValidationError { + problem: "`scissors.len()` exceeds the `max_viewports` limit".into(), + vuids: &["VUID-vkCmdSetScissorWithCount-scissorCount-03397"], + ..Default::default() + })); + } + + if !self.device().enabled_features().multi_viewport && scissors.len() > 1 { + return Err(Box::new(ValidationError { + problem: "`scissors.len()` is greater than 1".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "multi_viewport", + )])]), + vuids: &["VUID-vkCmdSetScissorWithCount-scissorCount-03398"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_scissor_with_count_unchecked(&mut self, scissors: &[Scissor]) -> &mut Self { let scissors = scissors .iter() .map(ash::vk::Rect2D::from) @@ -2598,13 +2532,413 @@ where self } - /// Calls `vkCmdSetViewport` on the builder. - /// - /// If the list is empty then the command is automatically ignored. + pub unsafe fn set_stencil_compare_mask( + &mut self, + faces: StencilFaces, + compare_mask: u32, + ) -> Result<&mut Self, Box> { + self.validate_set_stencil_compare_mask(faces, compare_mask)?; + + Ok(self.set_stencil_compare_mask_unchecked(faces, compare_mask)) + } + + fn validate_set_stencil_compare_mask( + &self, + faces: StencilFaces, + _compare_mask: u32, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetStencilCompareMask-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + faces + .validate_device(self.device()) + .map_err(|err| ValidationError { + context: "faces".into(), + vuids: &["VUID-vkCmdSetStencilCompareMask-faceMask-parameter"], + ..ValidationError::from_requirement(err) + })?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_stencil_compare_mask_unchecked( + &mut self, + faces: StencilFaces, + compare_mask: u32, + ) -> &mut Self { + let fns = self.device().fns(); + (fns.v1_0.cmd_set_stencil_compare_mask)(self.handle(), faces.into(), compare_mask); + + self + } + + pub unsafe fn set_stencil_op( + &mut self, + faces: StencilFaces, + fail_op: StencilOp, + pass_op: StencilOp, + depth_fail_op: StencilOp, + compare_op: CompareOp, + ) -> Result<&mut Self, Box> { + self.validate_set_stencil_op(faces, fail_op, pass_op, depth_fail_op, compare_op)?; + + Ok(self.set_stencil_op_unchecked(faces, fail_op, pass_op, depth_fail_op, compare_op)) + } + + fn validate_set_stencil_op( + &self, + faces: StencilFaces, + fail_op: StencilOp, + pass_op: StencilOp, + depth_fail_op: StencilOp, + compare_op: CompareOp, + ) -> Result<(), Box> { + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), + ]), + vuids: &["VUID-vkCmdSetStencilOp-None-03351"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetStencilOp-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + faces + .validate_device(self.device()) + .map_err(|err| ValidationError { + context: "faces".into(), + vuids: &["VUID-vkCmdSetStencilOp-faceMask-parameter"], + ..ValidationError::from_requirement(err) + })?; + + fail_op + .validate_device(self.device()) + .map_err(|err| ValidationError { + context: "fail_op".into(), + vuids: &["VUID-vkCmdSetStencilOp-failOp-parameter"], + ..ValidationError::from_requirement(err) + })?; + + pass_op + .validate_device(self.device()) + .map_err(|err| ValidationError { + context: "pass_op".into(), + vuids: &["VUID-vkCmdSetStencilOp-passOp-parameter"], + ..ValidationError::from_requirement(err) + })?; + + depth_fail_op + .validate_device(self.device()) + .map_err(|err| ValidationError { + context: "depth_fail_op".into(), + vuids: &["VUID-vkCmdSetStencilOp-depthFailOp-parameter"], + ..ValidationError::from_requirement(err) + })?; + + compare_op + .validate_device(self.device()) + .map_err(|err| ValidationError { + context: "compare_op".into(), + vuids: &["VUID-vkCmdSetStencilOp-compareOp-parameter"], + ..ValidationError::from_requirement(err) + })?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_stencil_op_unchecked( + &mut self, + faces: StencilFaces, + fail_op: StencilOp, + pass_op: StencilOp, + depth_fail_op: StencilOp, + compare_op: CompareOp, + ) -> &mut Self { + let fns = self.device().fns(); + + if self.device().api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_stencil_op)( + self.handle(), + faces.into(), + fail_op.into(), + pass_op.into(), + depth_fail_op.into(), + compare_op.into(), + ); + } else { + (fns.ext_extended_dynamic_state.cmd_set_stencil_op_ext)( + self.handle(), + faces.into(), + fail_op.into(), + pass_op.into(), + depth_fail_op.into(), + compare_op.into(), + ); + } + + self + } + + pub unsafe fn set_stencil_reference( + &mut self, + faces: StencilFaces, + reference: u32, + ) -> Result<&mut Self, Box> { + self.validate_set_stencil_reference(faces, reference)?; + + Ok(self.set_stencil_reference_unchecked(faces, reference)) + } + + fn validate_set_stencil_reference( + &self, + faces: StencilFaces, + _reference: u32, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetStencilReference-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + faces + .validate_device(self.device()) + .map_err(|err| ValidationError { + context: "faces".into(), + vuids: &["VUID-vkCmdSetStencilReference-faceMask-parameter"], + ..ValidationError::from_requirement(err) + })?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_stencil_reference_unchecked( + &mut self, + faces: StencilFaces, + reference: u32, + ) -> &mut Self { + let fns = self.device().fns(); + (fns.v1_0.cmd_set_stencil_reference)(self.handle(), faces.into(), reference); + + self + } + + pub unsafe fn set_stencil_test_enable( + &mut self, + enable: bool, + ) -> Result<&mut Self, Box> { + self.validate_set_stencil_test_enable(enable)?; + + Ok(self.set_stencil_test_enable_unchecked(enable)) + } + + fn validate_set_stencil_test_enable(&self, _enable: bool) -> Result<(), Box> { + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), + ]), + vuids: &["VUID-vkCmdSetStencilTestEnable-None-03350"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetStencilTestEnable-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_stencil_test_enable_unchecked(&mut self, enable: bool) -> &mut Self { + let fns = self.device().fns(); + + if self.device().api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_stencil_test_enable)(self.handle(), enable.into()); + } else { + (fns.ext_extended_dynamic_state + .cmd_set_stencil_test_enable_ext)(self.handle(), enable.into()); + } + + self + } + + pub unsafe fn set_stencil_write_mask( + &mut self, + faces: StencilFaces, + write_mask: u32, + ) -> Result<&mut Self, Box> { + self.validate_set_stencil_write_mask(faces, write_mask)?; + + Ok(self.set_stencil_write_mask_unchecked(faces, write_mask)) + } + + fn validate_set_stencil_write_mask( + &self, + faces: StencilFaces, + _write_mask: u32, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetStencilWriteMask-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + faces + .validate_device(self.device()) + .map_err(|err| ValidationError { + context: "faces".into(), + vuids: &["VUID-vkCmdSetStencilWriteMask-faceMask-parameter"], + ..ValidationError::from_requirement(err) + })?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_stencil_write_mask_unchecked( + &mut self, + faces: StencilFaces, + write_mask: u32, + ) -> &mut Self { + let fns = self.device().fns(); + (fns.v1_0.cmd_set_stencil_write_mask)(self.handle(), faces.into(), write_mask); + + self + } + pub unsafe fn set_viewport( &mut self, first_viewport: u32, viewports: &[Viewport], + ) -> Result<&mut Self, Box> { + self.validate_set_viewport(first_viewport, viewports)?; + + Ok(self.set_viewport_unchecked(first_viewport, viewports)) + } + + fn validate_set_viewport( + &self, + first_viewport: u32, + viewports: &[Viewport], + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetViewport-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + let properties = self.device().physical_device().properties(); + + if first_viewport + viewports.len() as u32 > properties.max_viewports { + return Err(Box::new(ValidationError { + problem: "`first_viewport + viewports.len()` exceeds the `max_viewports` limit" + .into(), + vuids: &["VUID-vkCmdSetViewport-firstViewport-01223"], + ..Default::default() + })); + } + + if !self.device().enabled_features().multi_viewport { + if first_viewport != 0 { + return Err(Box::new(ValidationError { + problem: "`first_viewport` is not 0".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "multi_viewport", + )])]), + vuids: &["VUID-vkCmdSetViewport-firstViewport-01224"], + ..Default::default() + })); + } + + if viewports.len() > 1 { + return Err(Box::new(ValidationError { + problem: "`viewports.len()` is greater than 1".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "multi_viewport", + )])]), + vuids: &["VUID-vkCmdSetViewport-viewportCount-01225"], + ..Default::default() + })); + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_viewport_unchecked( + &mut self, + first_viewport: u32, + viewports: &[Viewport], ) -> &mut Self { let viewports = viewports .iter() @@ -2625,10 +2959,75 @@ where self } - /// Calls `vkCmdSetViewportWithCountEXT` on the builder. - /// - /// If the list is empty then the command is automatically ignored. - pub unsafe fn set_viewport_with_count(&mut self, viewports: &[Viewport]) -> &mut Self { + pub unsafe fn set_viewport_with_count( + &mut self, + viewports: &[Viewport], + ) -> Result<&mut Self, Box> { + self.validate_set_viewport_with_count(viewports)?; + + Ok(self.set_viewport_with_count_unchecked(viewports)) + } + + fn validate_set_viewport_with_count( + &self, + viewports: &[Viewport], + ) -> Result<(), Box> { + if !(self.device().api_version() >= Version::V1_3 + || self.device().enabled_features().extended_dynamic_state) + { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::Feature("extended_dynamic_state")]), + ]), + vuids: &["VUID-vkCmdSetViewportWithCount-None-03393"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdSetViewportWithCount-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + let properties = self.device().physical_device().properties(); + + if viewports.len() as u32 > properties.max_viewports { + return Err(Box::new(ValidationError { + problem: "`viewports.len()` exceeds the `max_viewports` limit".into(), + vuids: &["VUID-vkCmdSetViewportWithCount-viewportCount-03394"], + ..Default::default() + })); + } + + if viewports.len() > 1 && !self.device().enabled_features().multi_viewport { + return Err(Box::new(ValidationError { + problem: "`viewports.len()` is greater than 1".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "multi_viewport", + )])]), + vuids: &["VUID-vkCmdSetViewportWithCount-viewportCount-03395"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_viewport_with_count_unchecked( + &mut self, + viewports: &[Viewport], + ) -> &mut Self { let viewports = viewports .iter() .map(|v| v.into()) @@ -2657,98 +3056,3 @@ where self } } - -#[derive(Clone, Debug)] -#[allow(dead_code)] -pub(in super::super) enum SetDynamicStateError { - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, - - /// The provided `factor` is not between 1 and 256 inclusive. - FactorOutOfRange, - - /// The [`max_discard_rectangles`](crate::device::Properties::max_discard_rectangles) - /// limit has been exceeded. - MaxDiscardRectanglesExceeded { provided: u32, max: u32 }, - - /// The [`max_tessellation_patch_size`](crate::device::Properties::max_tessellation_patch_size) - /// limit has been exceeded. - MaxTessellationPatchSizeExceeded { provided: u32, max: u32 }, - - /// The [`max_viewports`](crate::device::Properties::max_viewports) - /// limit has been exceeded. - MaxViewportsExceeded { provided: u32, max: u32 }, - - /// The queue family doesn't allow this operation. - NotSupportedByQueueFamily, - - /// The provided item count is different from the number of attachments in the color blend - /// state of the currently bound pipeline. - PipelineColorBlendAttachmentCountMismatch { - provided_count: u32, - required_count: u32, - }, - - /// The currently bound pipeline contains this state as internally fixed state, which cannot be - /// overridden with dynamic state. - PipelineHasFixedState, -} - -impl Error for SetDynamicStateError {} - -impl Display for SetDynamicStateError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::FactorOutOfRange => write!( - f, - "the provided `factor` is not between 1 and 256 inclusive", - ), - Self::MaxDiscardRectanglesExceeded { .. } => { - write!(f, "the `max_discard_rectangles` limit has been exceeded") - } - Self::MaxTessellationPatchSizeExceeded { .. } => write!( - f, - "the `max_tessellation_patch_size` limit has been exceeded", - ), - Self::MaxViewportsExceeded { .. } => { - write!(f, "the `max_viewports` limit has been exceeded") - } - Self::NotSupportedByQueueFamily => { - write!(f, "the queue family doesn't allow this operation") - } - Self::PipelineColorBlendAttachmentCountMismatch { - provided_count, - required_count, - } => write!( - f, - "the provided item count ({}) is different from the number of attachments in the \ - color blend state of the currently bound pipeline ({})", - provided_count, required_count, - ), - Self::PipelineHasFixedState => write!( - f, - "the currently bound pipeline contains this state as internally fixed state, which \ - cannot be overridden with dynamic state", - ), - } - } -} - -impl From for SetDynamicStateError { - fn from(err: RequirementNotMet) -> Self { - Self::RequirementNotMet { - required_for: err.required_for, - requires_one_of: err.requires_one_of, - } - } -} diff --git a/vulkano/src/command_buffer/commands/pipeline.rs b/vulkano/src/command_buffer/commands/pipeline.rs index 89df64cf..22363be0 100644 --- a/vulkano/src/command_buffer/commands/pipeline.rs +++ b/vulkano/src/command_buffer/commands/pipeline.rs @@ -22,31 +22,33 @@ use crate::{ DescriptorImageViewInfo, }, device::{DeviceOwned, QueueFlags}, - format::{Format, FormatFeatures}, - image::{ - sampler::Sampler, - view::{ImageView, ImageViewType}, - ImageAspects, ImageLayout, SampleCount, - }, + format::{FormatFeatures, NumericType}, + image::{sampler::Sampler, view::ImageView, ImageAspects, ImageLayout, SampleCount}, pipeline::{ graphics::{ - input_assembly::{PrimitiveTopology, PrimitiveTopologyClass}, - subpass::PipelineSubpassType, + input_assembly::PrimitiveTopology, subpass::PipelineSubpassType, vertex_input::VertexInputRate, }, DynamicState, GraphicsPipeline, PartialStateMode, Pipeline, PipelineLayout, }, - shader::{DescriptorBindingRequirements, ShaderScalarType, ShaderStage, ShaderStages}, + shader::{DescriptorBindingRequirements, DescriptorIdentifier, ShaderStage, ShaderStages}, sync::{PipelineStageAccess, PipelineStageAccessFlags}, DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, VulkanObject, }; -use std::{ - cmp::min, - error::Error, - fmt::{Display, Error as FmtError, Formatter}, - mem::size_of, - sync::Arc, -}; +use std::{mem::size_of, sync::Arc}; + +macro_rules! vuids { + ($vuid_type:ident, $($id:literal),+ $(,)?) => { + match $vuid_type { + VUIDType::Dispatch => &[$(concat!("VUID-vkCmdDispatch-", $id)),+], + VUIDType::DispatchIndirect => &[$(concat!("VUID-vkCmdDispatchIndirect-", $id)),+], + VUIDType::Draw => &[$(concat!("VUID-vkCmdDraw-", $id)),+], + VUIDType::DrawIndirect => &[$(concat!("VUID-vkCmdDrawIndirect-", $id)),+], + VUIDType::DrawIndexed => &[$(concat!("VUID-vkCmdDrawIndexed-", $id)),+], + VUIDType::DrawIndexedIndirect => &[$(concat!("VUID-vkCmdDrawIndexedIndirect-", $id)),+], + } + }; +} /// # Commands to execute a bound pipeline. /// @@ -60,61 +62,37 @@ where /// 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, PipelineExecutionError> { + pub fn dispatch(&mut self, group_counts: [u32; 3]) -> Result<&mut Self, Box> { self.validate_dispatch(group_counts)?; - unsafe { - self.dispatch_unchecked(group_counts); - } - - Ok(self) + unsafe { Ok(self.dispatch_unchecked(group_counts)) } } - fn validate_dispatch(&self, group_counts: [u32; 3]) -> Result<(), PipelineExecutionError> { - let queue_family_properties = self.queue_family_properties(); + fn validate_dispatch(&self, group_counts: [u32; 3]) -> Result<(), Box> { + self.inner.validate_dispatch(group_counts)?; - // VUID-vkCmdDispatch-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::COMPUTE) - { - return Err(PipelineExecutionError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdDispatch-renderpass if self.builder_state.render_pass.is_some() { - return Err(PipelineExecutionError::ForbiddenInsideRenderPass); + return Err(Box::new(ValidationError { + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdDispatch-renderpass"], + ..Default::default() + })); } - // VUID-vkCmdDispatch-None-02700 let pipeline = self .builder_state .pipeline_compute .as_ref() - .ok_or(PipelineExecutionError::PipelineNotBound)? + .ok_or(Box::new(ValidationError { + problem: "no compute pipeline is currently bound".into(), + vuids: &["VUID-vkCmdDispatch-None-08606"], + ..Default::default() + }))? .as_ref(); - self.validate_pipeline_descriptor_sets(pipeline)?; - self.validate_pipeline_push_constants(pipeline.layout())?; - - let max = self - .device() - .physical_device() - .properties() - .max_compute_work_group_count; - - // VUID-vkCmdDispatch-groupCountX-00386 - // VUID-vkCmdDispatch-groupCountY-00387 - // VUID-vkCmdDispatch-groupCountZ-00388 - if group_counts[0] > max[0] || group_counts[1] > max[1] || group_counts[2] > max[2] { - return Err(PipelineExecutionError::MaxComputeWorkGroupCountExceeded { - requested: group_counts, - max, - }); - } + const VUID_TYPE: VUIDType = VUIDType::Dispatch; + self.validate_pipeline_descriptor_sets(VUID_TYPE, pipeline)?; + self.validate_pipeline_push_constants(VUID_TYPE, pipeline.layout())?; Ok(()) } @@ -135,7 +113,7 @@ where "dispatch", used_resources, move |out: &mut UnsafeCommandBufferBuilder| { - out.dispatch(group_counts); + out.dispatch_unchecked(group_counts); }, ); @@ -151,46 +129,40 @@ where pub fn dispatch_indirect( &mut self, indirect_buffer: Subbuffer<[DispatchIndirectCommand]>, - ) -> Result<&mut Self, PipelineExecutionError> { + ) -> Result<&mut Self, Box> { self.validate_dispatch_indirect(indirect_buffer.as_bytes())?; - unsafe { - self.dispatch_indirect_unchecked(indirect_buffer); - } - - Ok(self) + unsafe { Ok(self.dispatch_indirect_unchecked(indirect_buffer)) } } fn validate_dispatch_indirect( &self, indirect_buffer: &Subbuffer<[u8]>, - ) -> Result<(), PipelineExecutionError> { - let queue_family_properties = self.queue_family_properties(); + ) -> Result<(), Box> { + self.inner.validate_dispatch_indirect(indirect_buffer)?; - // VUID-vkCmdDispatchIndirect-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::COMPUTE) - { - return Err(PipelineExecutionError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdDispatchIndirect-renderpass if self.builder_state.render_pass.is_some() { - return Err(PipelineExecutionError::ForbiddenInsideRenderPass); + return Err(Box::new(ValidationError { + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdDispatchIndirect-renderpass"], + ..Default::default() + })); } - // VUID-vkCmdDispatchIndirect-None-02700 let pipeline = self .builder_state .pipeline_compute .as_ref() - .ok_or(PipelineExecutionError::PipelineNotBound)? + .ok_or(Box::new(ValidationError { + problem: "no compute pipeline is currently bound".into(), + vuids: &["VUID-vkCmdDispatchIndirect-None-08606"], + ..Default::default() + }))? .as_ref(); - self.validate_pipeline_descriptor_sets(pipeline)?; - self.validate_pipeline_push_constants(pipeline.layout())?; - self.validate_indirect_buffer(indirect_buffer)?; + const VUID_TYPE: VUIDType = VUIDType::DispatchIndirect; + self.validate_pipeline_descriptor_sets(VUID_TYPE, pipeline)?; + self.validate_pipeline_push_constants(VUID_TYPE, pipeline.layout())?; Ok(()) } @@ -215,7 +187,7 @@ where "dispatch", used_resources, move |out: &mut UnsafeCommandBufferBuilder| { - out.dispatch_indirect(&indirect_buffer); + out.dispatch_indirect_unchecked(&indirect_buffer); }, ); @@ -239,14 +211,12 @@ where instance_count: u32, first_vertex: u32, first_instance: u32, - ) -> Result<&mut Self, PipelineExecutionError> { + ) -> Result<&mut Self, Box> { self.validate_draw(vertex_count, instance_count, first_vertex, first_instance)?; unsafe { - self.draw_unchecked(vertex_count, instance_count, first_vertex, first_instance); + Ok(self.draw_unchecked(vertex_count, instance_count, first_vertex, first_instance)) } - - Ok(self) } fn validate_draw( @@ -255,31 +225,112 @@ where instance_count: u32, first_vertex: u32, first_instance: u32, - ) -> Result<(), PipelineExecutionError> { - // VUID-vkCmdDraw-renderpass - let render_pass_state = self - .builder_state - .render_pass - .as_ref() - .ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?; + ) -> Result<(), Box> { + self.inner + .validate_draw(vertex_count, instance_count, first_vertex, first_instance)?; + + let render_pass_state = + self.builder_state + .render_pass + .as_ref() + .ok_or(Box::new(ValidationError { + problem: "a render pass instance is not active".into(), + vuids: &["VUID-vkCmdDraw-renderpass"], + ..Default::default() + }))?; - // VUID-vkCmdDraw-None-02700 let pipeline = self .builder_state .pipeline_graphics .as_ref() - .ok_or(PipelineExecutionError::PipelineNotBound)? + .ok_or(Box::new(ValidationError { + problem: "no graphics pipeline is currently bound".into(), + vuids: &["VUID-vkCmdDraw-None-08606"], + ..Default::default() + }))? .as_ref(); - self.validate_pipeline_descriptor_sets(pipeline)?; - self.validate_pipeline_push_constants(pipeline.layout())?; - self.validate_pipeline_graphics_dynamic_state(pipeline)?; - self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?; - self.validate_pipeline_graphics_vertex_buffers( - pipeline, - Some((first_vertex, vertex_count)), - Some((first_instance, instance_count)), - )?; + const VUID_TYPE: VUIDType = VUIDType::Draw; + self.validate_pipeline_descriptor_sets(VUID_TYPE, pipeline)?; + self.validate_pipeline_push_constants(VUID_TYPE, pipeline.layout())?; + self.validate_pipeline_graphics_dynamic_state(VUID_TYPE, pipeline)?; + self.validate_pipeline_graphics_render_pass(VUID_TYPE, pipeline, render_pass_state)?; + self.validate_pipeline_graphics_vertex_buffers(VUID_TYPE, pipeline)?; + + let view_mask = match pipeline.subpass() { + PipelineSubpassType::BeginRenderPass(subpass) => subpass.render_pass().views_used(), + PipelineSubpassType::BeginRendering(rendering_info) => rendering_info.view_mask, + }; + + if view_mask != 0 { + let properties = self.device().physical_device().properties(); + + if (first_instance + instance_count).saturating_sub(1) + > properties.max_multiview_instance_index.unwrap_or(0) + { + return Err(Box::new(ValidationError { + problem: "the current render pass instance has a nonzero view mask, but \ + `first_instance + instance_count - 1` is greater than the \ + `max_multiview_instance_index` limit" + .into(), + vuids: &["VUID-vkCmdDraw-maxMultiviewInstanceIndex-02688"], + ..Default::default() + })); + } + } + + for (&binding_num, binding_desc) in &pipeline.vertex_input_state().bindings { + let vertex_buffer = &self.builder_state.vertex_buffers[&binding_num]; + + // Per spec: + // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap22.html#fxvertex-input-address-calculation + match binding_desc.input_rate { + VertexInputRate::Vertex => { + let max_vertex_offset = (first_vertex as DeviceSize + + vertex_count as DeviceSize) + * binding_desc.stride as DeviceSize; + + if max_vertex_offset > vertex_buffer.size() { + return Err(Box::new(ValidationError { + problem: format!( + "the size of the vertex buffer bound to binding {} is less \ + than the minimum size required, for the provided \ + `first_vertex` and `vertex_count` values, and the vertex \ + input state bindings of the currently bound graphics pipeline", + binding_num + ) + .into(), + vuids: &["VUID-vkCmdDraw-None-02721"], + ..Default::default() + })); + } + } + VertexInputRate::Instance { divisor } => { + let max_vertex_offset = if divisor == 0 { + (first_instance as DeviceSize + 1) * binding_desc.stride as DeviceSize + } else { + (first_instance as DeviceSize + + instance_count as DeviceSize / divisor as DeviceSize) + * binding_desc.stride as DeviceSize + }; + + if max_vertex_offset > vertex_buffer.size() { + return Err(Box::new(ValidationError { + problem: format!( + "the size of the vertex buffer bound to binding {} is less \ + than the minimum size required, for the provided \ + `first_instance` and `instance_count` values, and the vertex \ + input state bindings of the currently bound graphics pipeline", + binding_num + ) + .into(), + vuids: &["VUID-vkCmdDraw-None-02721"], + ..Default::default() + })); + } + } + }; + } Ok(()) } @@ -313,7 +364,7 @@ where "draw", used_resources, move |out: &mut UnsafeCommandBufferBuilder| { - out.draw(vertex_count, instance_count, first_vertex, first_instance); + out.draw_unchecked(vertex_count, instance_count, first_vertex, first_instance); }, ); @@ -338,70 +389,50 @@ where pub fn draw_indirect( &mut self, indirect_buffer: Subbuffer<[DrawIndirectCommand]>, - ) -> Result<&mut Self, PipelineExecutionError> { + ) -> Result<&mut Self, Box> { let draw_count = indirect_buffer.len() as u32; let stride = size_of::() as u32; self.validate_draw_indirect(indirect_buffer.as_bytes(), draw_count, stride)?; - unsafe { - self.draw_indirect_unchecked(indirect_buffer, draw_count, stride); - } - - Ok(self) + unsafe { Ok(self.draw_indirect_unchecked(indirect_buffer, draw_count, stride)) } } fn validate_draw_indirect( &self, indirect_buffer: &Subbuffer<[u8]>, draw_count: u32, - _stride: u32, - ) -> Result<(), PipelineExecutionError> { - // VUID-vkCmdDrawIndirect-renderpass - let render_pass_state = self - .builder_state - .render_pass - .as_ref() - .ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?; + stride: u32, + ) -> Result<(), Box> { + self.inner + .validate_draw_indirect(indirect_buffer, draw_count, stride)?; + + let render_pass_state = + self.builder_state + .render_pass + .as_ref() + .ok_or(Box::new(ValidationError { + problem: "a render pass instance is not active".into(), + vuids: &["VUID-vkCmdDrawIndirect-renderpass"], + ..Default::default() + }))?; - // VUID-vkCmdDrawIndirect-None-02700 let pipeline = self .builder_state .pipeline_graphics .as_ref() - .ok_or(PipelineExecutionError::PipelineNotBound)? + .ok_or(Box::new(ValidationError { + problem: "no graphics pipeline is currently bound".into(), + vuids: &["VUID-vkCmdDrawIndirect-None-08606"], + ..Default::default() + }))? .as_ref(); - self.validate_pipeline_descriptor_sets(pipeline)?; - self.validate_pipeline_push_constants(pipeline.layout())?; - self.validate_pipeline_graphics_dynamic_state(pipeline)?; - self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?; - self.validate_pipeline_graphics_vertex_buffers(pipeline, None, None)?; - - self.validate_indirect_buffer(indirect_buffer)?; - - // VUID-vkCmdDrawIndirect-drawCount-02718 - if draw_count > 1 && !self.device().enabled_features().multi_draw_indirect { - return Err(PipelineExecutionError::RequirementNotMet { - required_for: "`draw_count` is greater than `1`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "multi_draw_indirect", - )])]), - }); - } - - let max = self - .device() - .physical_device() - .properties() - .max_draw_indirect_count; - - // VUID-vkCmdDrawIndirect-drawCount-02719 - if draw_count > max { - return Err(PipelineExecutionError::MaxDrawIndirectCountExceeded { - provided: draw_count, - max, - }); - } + const VUID_TYPE: VUIDType = VUIDType::DrawIndirect; + self.validate_pipeline_descriptor_sets(VUID_TYPE, pipeline)?; + self.validate_pipeline_push_constants(VUID_TYPE, pipeline.layout())?; + self.validate_pipeline_graphics_dynamic_state(VUID_TYPE, pipeline)?; + self.validate_pipeline_graphics_render_pass(VUID_TYPE, pipeline, render_pass_state)?; + self.validate_pipeline_graphics_vertex_buffers(VUID_TYPE, pipeline)?; Ok(()) } @@ -435,7 +466,7 @@ where "draw_indirect", used_resources, move |out: &mut UnsafeCommandBufferBuilder| { - out.draw_indirect(&indirect_buffer, draw_count, stride); + out.draw_indirect_unchecked(&indirect_buffer, draw_count, stride); }, ); @@ -467,7 +498,7 @@ where first_index: u32, vertex_offset: i32, first_instance: u32, - ) -> Result<&mut Self, PipelineExecutionError> { + ) -> Result<&mut Self, Box> { self.validate_draw_indexed( index_count, instance_count, @@ -477,16 +508,14 @@ where )?; unsafe { - self.draw_indexed_unchecked( + Ok(self.draw_indexed_unchecked( index_count, instance_count, first_index, vertex_offset, first_instance, - ); + )) } - - Ok(self) } fn validate_draw_indexed( @@ -494,37 +523,131 @@ where index_count: u32, instance_count: u32, first_index: u32, - _vertex_offset: i32, + vertex_offset: i32, first_instance: u32, - ) -> Result<(), PipelineExecutionError> { - // TODO: how to handle an index out of range of the vertex buffers? + ) -> Result<(), Box> { + self.inner.validate_draw_indexed( + index_count, + instance_count, + first_index, + vertex_offset, + first_instance, + )?; - // VUID-vkCmdDrawIndexed-renderpass - let render_pass_state = self - .builder_state - .render_pass - .as_ref() - .ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?; + let render_pass_state = + self.builder_state + .render_pass + .as_ref() + .ok_or(Box::new(ValidationError { + problem: "a render pass instance is not active".into(), + vuids: &["VUID-vkCmdDrawIndexed-renderpass"], + ..Default::default() + }))?; - // VUID-vkCmdDrawIndexed-None-02700 let pipeline = self .builder_state .pipeline_graphics .as_ref() - .ok_or(PipelineExecutionError::PipelineNotBound)? + .ok_or(Box::new(ValidationError { + problem: "no graphics pipeline is currently bound".into(), + vuids: &["VUID-vkCmdDrawIndexed-None-08606"], + ..Default::default() + }))? .as_ref(); - self.validate_pipeline_descriptor_sets(pipeline)?; - self.validate_pipeline_push_constants(pipeline.layout())?; - self.validate_pipeline_graphics_dynamic_state(pipeline)?; - self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?; - self.validate_pipeline_graphics_vertex_buffers( - pipeline, - None, - Some((first_instance, instance_count)), - )?; + const VUID_TYPE: VUIDType = VUIDType::DrawIndexed; + self.validate_pipeline_descriptor_sets(VUID_TYPE, pipeline)?; + self.validate_pipeline_push_constants(VUID_TYPE, pipeline.layout())?; + self.validate_pipeline_graphics_dynamic_state(VUID_TYPE, pipeline)?; + self.validate_pipeline_graphics_render_pass(VUID_TYPE, pipeline, render_pass_state)?; + self.validate_pipeline_graphics_vertex_buffers(VUID_TYPE, pipeline)?; - self.validate_index_buffer(Some((first_index, index_count)))?; + let index_buffer = + self.builder_state + .index_buffer + .as_ref() + .ok_or(Box::new(ValidationError { + problem: "no index buffer is currently bound".into(), + vuids: &["VUID-vkCmdDrawIndexed-None-07312"], + ..Default::default() + }))?; + + let index_buffer_bytes = index_buffer.as_bytes(); + + if !self.device().enabled_features().robust_buffer_access2 { + if index_buffer.index_type().size() + * (first_index as DeviceSize + index_count as DeviceSize) + > index_buffer_bytes.size() + { + return Err(Box::new(ValidationError { + problem: "`first_index + index_count`, \ + multiplied by the size of the indices in the bound index buffer, \ + is greater than the size of the bound index buffer" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "robust_buffer_access2", + )])]), + vuids: &["VUID-vkCmdDrawIndexed-robustBufferAccess2-07825"], + ..Default::default() + })); + } + } + + let view_mask = match pipeline.subpass() { + PipelineSubpassType::BeginRenderPass(subpass) => subpass.render_pass().views_used(), + PipelineSubpassType::BeginRendering(rendering_info) => rendering_info.view_mask, + }; + + if view_mask != 0 { + let properties = self.device().physical_device().properties(); + + if (first_instance + instance_count).saturating_sub(1) + > properties.max_multiview_instance_index.unwrap_or(0) + { + return Err(Box::new(ValidationError { + problem: "the current render pass instance has a nonzero view mask, but \ + `first_instance + instance_count - 1` is greater than the \ + `max_multiview_instance_index` limit" + .into(), + vuids: &["VUID-vkCmdDrawIndexed-maxMultiviewInstanceIndex-02688"], + ..Default::default() + })); + } + } + + for (&binding_num, binding_desc) in &pipeline.vertex_input_state().bindings { + let vertex_buffer = &self.builder_state.vertex_buffers[&binding_num]; + + // Per spec: + // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap22.html#fxvertex-input-address-calculation + match binding_desc.input_rate { + VertexInputRate::Vertex => (), + VertexInputRate::Instance { divisor } => { + let max_vertex_offset = if divisor == 0 { + (first_instance as DeviceSize + 1) * binding_desc.stride as DeviceSize + } else { + (first_instance as DeviceSize + + instance_count as DeviceSize / divisor as DeviceSize) + * binding_desc.stride as DeviceSize + }; + + if max_vertex_offset > vertex_buffer.size() { + return Err(Box::new(ValidationError { + problem: format!( + "the size of the vertex buffer bound to binding {} is less \ + than the minimum size required, for the provided \ + `first_instance` and `instance_count` values, and the vertex \ + input state bindings of the currently bound graphics pipeline", + binding_num + ) + .into(), + vuids: &["VUID-vkCmdDrawIndexed-None-02721"], + ..Default::default() + })); + } + } + }; + } Ok(()) } @@ -560,7 +683,7 @@ where "draw_indexed", used_resources, move |out: &mut UnsafeCommandBufferBuilder| { - out.draw_indexed( + out.draw_indexed_unchecked( index_count, instance_count, first_index, @@ -596,71 +719,60 @@ where pub fn draw_indexed_indirect( &mut self, indirect_buffer: Subbuffer<[DrawIndexedIndirectCommand]>, - ) -> Result<&mut Self, PipelineExecutionError> { + ) -> Result<&mut Self, Box> { let draw_count = indirect_buffer.len() as u32; let stride = size_of::() as u32; self.validate_draw_indexed_indirect(indirect_buffer.as_bytes(), draw_count, stride)?; - unsafe { - self.draw_indexed_indirect_unchecked(indirect_buffer, draw_count, stride); - } - - Ok(self) + unsafe { Ok(self.draw_indexed_indirect_unchecked(indirect_buffer, draw_count, stride)) } } fn validate_draw_indexed_indirect( &self, indirect_buffer: &Subbuffer<[u8]>, draw_count: u32, - _stride: u32, - ) -> Result<(), PipelineExecutionError> { - // VUID-vkCmdDrawIndexedIndirect-renderpass - let render_pass_state = self - .builder_state - .render_pass - .as_ref() - .ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?; + stride: u32, + ) -> Result<(), Box> { + self.inner + .validate_draw_indexed_indirect(indirect_buffer, draw_count, stride)?; + + let render_pass_state = + self.builder_state + .render_pass + .as_ref() + .ok_or(Box::new(ValidationError { + problem: "a render pass instance is not active".into(), + vuids: &["VUID-vkCmdDrawIndexedIndirect-renderpass"], + ..Default::default() + }))?; - // VUID-vkCmdDrawIndexedIndirect-None-02700 let pipeline = self .builder_state .pipeline_graphics .as_ref() - .ok_or(PipelineExecutionError::PipelineNotBound)? + .ok_or(Box::new(ValidationError { + problem: "no graphics pipeline is currently bound".into(), + vuids: &["VUID-vkCmdDrawIndexedIndirect-None-08606"], + ..Default::default() + }))? .as_ref(); - self.validate_pipeline_descriptor_sets(pipeline)?; - self.validate_pipeline_push_constants(pipeline.layout())?; - self.validate_pipeline_graphics_dynamic_state(pipeline)?; - self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?; - self.validate_pipeline_graphics_vertex_buffers(pipeline, None, None)?; + const VUID_TYPE: VUIDType = VUIDType::DrawIndexedIndirect; + self.validate_pipeline_descriptor_sets(VUID_TYPE, pipeline)?; + self.validate_pipeline_push_constants(VUID_TYPE, pipeline.layout())?; + self.validate_pipeline_graphics_dynamic_state(VUID_TYPE, pipeline)?; + self.validate_pipeline_graphics_render_pass(VUID_TYPE, pipeline, render_pass_state)?; + self.validate_pipeline_graphics_vertex_buffers(VUID_TYPE, pipeline)?; - self.validate_index_buffer(None)?; - self.validate_indirect_buffer(indirect_buffer)?; - - // VUID-vkCmdDrawIndexedIndirect-drawCount-02718 - if draw_count > 1 && !self.device().enabled_features().multi_draw_indirect { - return Err(PipelineExecutionError::RequirementNotMet { - required_for: "`draw_count` is greater than `1`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "multi_draw_indirect", - )])]), - }); - } - - let max = self - .device() - .physical_device() - .properties() - .max_draw_indirect_count; - - // VUID-vkCmdDrawIndexedIndirect-drawCount-02719 - if draw_count > max { - return Err(PipelineExecutionError::MaxDrawIndirectCountExceeded { - provided: draw_count, - max, - }); - } + let _index_buffer = + self.builder_state + .index_buffer + .as_ref() + .ok_or(Box::new(ValidationError { + problem: "no index buffer is currently bound".into(), + vuids: &["VUID-vkCmdDrawIndexedIndirect-None-07312"], + ..Default::default() + }))?; Ok(()) } @@ -695,85 +807,43 @@ where "draw_indexed_indirect", used_resources, move |out: &mut UnsafeCommandBufferBuilder| { - out.draw_indexed_indirect(&indirect_buffer, draw_count, stride); + out.draw_indexed_indirect_unchecked(&indirect_buffer, draw_count, stride); }, ); self } - fn validate_index_buffer( - &self, - indices: Option<(u32, u32)>, - ) -> Result<(), PipelineExecutionError> { - // VUID? - let index_buffer = match &self.builder_state.index_buffer { - Some(x) => x, - None => return Err(PipelineExecutionError::IndexBufferNotBound), - }; - - let index_buffer_bytes = index_buffer.as_bytes(); - - if let Some((first_index, index_count)) = indices { - let max_index_count = - (index_buffer_bytes.size() / index_buffer.index_type().size()) as u32; - - // // VUID-vkCmdDrawIndexed-firstIndex-04932 - if first_index + index_count > max_index_count { - return Err(PipelineExecutionError::IndexBufferRangeOutOfBounds { - highest_index: first_index + index_count, - max_index_count, - }); - } - } - - Ok(()) - } - - fn validate_indirect_buffer( - &self, - buffer: &Subbuffer<[u8]>, - ) -> Result<(), PipelineExecutionError> { - // VUID-vkCmdDispatchIndirect-commonparent - assert_eq!(self.device(), buffer.device()); - - // VUID-vkCmdDispatchIndirect-buffer-02709 - if !buffer - .buffer() - .usage() - .intersects(BufferUsage::INDIRECT_BUFFER) - { - return Err(PipelineExecutionError::IndirectBufferMissingUsage); - } - - // VUID-vkCmdDispatchIndirect-offset-02710 - // TODO: - - Ok(()) - } - fn validate_pipeline_descriptor_sets( &self, + vuid_type: VUIDType, pipeline: &Pl, - ) -> Result<(), PipelineExecutionError> { + ) -> Result<(), Box> { fn validate_resources( + vuid_type: VUIDType, set_num: u32, binding_num: u32, binding_reqs: &DescriptorBindingRequirements, elements: &[Option], - mut extra_check: impl FnMut(u32, &T) -> Result<(), DescriptorResourceInvalidError>, - ) -> Result<(), PipelineExecutionError> { + mut extra_check: impl FnMut(u32, u32, u32, &T) -> Result<(), Box>, + ) -> Result<(), Box> { let elements_to_check = if let Some(descriptor_count) = binding_reqs.descriptor_count { // The shader has a fixed-sized array, so it will never access more than // the first `descriptor_count` elements. elements.get(..descriptor_count as usize).ok_or({ // There are less than `descriptor_count` elements in `elements` - PipelineExecutionError::DescriptorResourceInvalid { - set_num, - binding_num, - index: elements.len() as u32, - error: DescriptorResourceInvalidError::Missing, - } + Box::new(ValidationError { + problem: format!( + "the currently bound pipeline accesses the resource bound to \ + descriptor set {set_num}, binding {binding_num}, \ + descriptor index {}, but no descriptor was written to the \ + descriptor set currently bound to set {set_num}", + elements.len() + ) + .into(), + vuids: vuids!(vuid_type, "None-02699"), + ..Default::default() + }) })? } else { // The shader has a runtime-sized array, so any element could potentially @@ -784,27 +854,24 @@ where for (index, element) in elements_to_check.iter().enumerate() { let index = index as u32; - // VUID-vkCmdDispatch-None-02699 let element = match element { Some(x) => x, None => { - return Err(PipelineExecutionError::DescriptorResourceInvalid { - set_num, - binding_num, - index, - error: DescriptorResourceInvalidError::Missing, - }) + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline accesses the resource bound to \ + descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, but no descriptor was written to the \ + descriptor set currently bound to set {set_num}" + ) + .into(), + vuids: vuids!(vuid_type, "None-02699"), + ..Default::default() + })); } }; - if let Err(error) = extra_check(index, element) { - return Err(PipelineExecutionError::DescriptorResourceInvalid { - set_num, - binding_num, - index, - error, - }); - } + extra_check(set_num, binding_num, index, element)?; } Ok(()) @@ -814,238 +881,387 @@ where return Ok(()); } - // VUID-vkCmdDispatch-None-02697 let descriptor_set_state = self .builder_state .descriptor_sets .get(&pipeline.bind_point()) - .ok_or(PipelineExecutionError::PipelineLayoutNotCompatible)?; + .ok_or(Box::new(ValidationError { + problem: "the currently bound pipeline accesses descriptor sets, but no \ + descriptor sets were previously bound" + .into(), + vuids: vuids!(vuid_type, "None-02697"), + ..Default::default() + }))?; - // VUID-vkCmdDispatch-None-02697 if !pipeline.layout().is_compatible_with( &descriptor_set_state.pipeline_layout, pipeline.num_used_descriptor_sets(), ) { - return Err(PipelineExecutionError::PipelineLayoutNotCompatible); + return Err(Box::new(ValidationError { + problem: "the currently bound pipeline accesses descriptor sets, but the \ + pipeline layouts that were used to bind the descriptor sets are \ + not compatible with the pipeline layout of the currently bound pipeline" + .into(), + vuids: vuids!(vuid_type, "None-02697"), + ..Default::default() + })); } for (&(set_num, binding_num), binding_reqs) in pipeline.descriptor_binding_requirements() { let layout_binding = &pipeline.layout().set_layouts()[set_num as usize].bindings()[&binding_num]; - let check_buffer = |_index: u32, _buffer_info: &DescriptorBufferInfo| Ok(()); + let check_buffer = + |_set_num: u32, + _binding_num: u32, + _index: u32, + _buffer_info: &DescriptorBufferInfo| Ok(()); - let check_buffer_view = |index: u32, buffer_view: &Arc| { - for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter()) - .chain(binding_reqs.descriptors.get(&None)) - { - if layout_binding.descriptor_type == DescriptorType::StorageTexelBuffer { - // VUID-vkCmdDispatch-OpTypeImage-06423 - if binding_reqs.image_format.is_none() - && !desc_reqs.memory_write.is_empty() - && !buffer_view - .format_features() - .intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT) - { - return Err(DescriptorResourceInvalidError::StorageWriteWithoutFormatNotSupported); - } - - // VUID-vkCmdDispatch-OpTypeImage-06424 - if binding_reqs.image_format.is_none() - && !desc_reqs.memory_read.is_empty() - && !buffer_view - .format_features() - .intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT) - { - return Err(DescriptorResourceInvalidError::StorageReadWithoutFormatNotSupported); - } - } - } - - Ok(()) - }; - - let check_image_view_common = |index: u32, image_view: &Arc| { - for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter()) - .chain(binding_reqs.descriptors.get(&None)) - { - // VUID-vkCmdDispatch-None-02691 - if desc_reqs.storage_image_atomic - && !image_view - .format_features() - .intersects(FormatFeatures::STORAGE_IMAGE_ATOMIC) + let check_buffer_view = + |set_num: u32, binding_num: u32, index: u32, buffer_view: &Arc| { + for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter()) + .chain(binding_reqs.descriptors.get(&None)) { - return Err(DescriptorResourceInvalidError::StorageImageAtomicNotSupported); + if layout_binding.descriptor_type == DescriptorType::StorageTexelBuffer { + if binding_reqs.image_format.is_none() + && !desc_reqs.memory_write.is_empty() + && !buffer_view + .format_features() + .intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT) + { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline writes to the buffer view \ + bound to descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, without specifying a format, \ + but the format features of the buffer view's format do \ + not contain `FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT`" + ) + .into(), + vuids: vuids!(vuid_type, "OpTypeImage-06423"), + ..Default::default() + })); + } + + if binding_reqs.image_format.is_none() + && !desc_reqs.memory_read.is_empty() + && !buffer_view + .format_features() + .intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT) + { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline reads from the buffer view \ + bound to descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, without specifying a format, \ + but the format features of the buffer view's format do \ + not contain `FormatFeatures::STORAGE_READ_WITHOUT_FORMAT`" + ) + .into(), + vuids: vuids!(vuid_type, "OpTypeImage-06424"), + ..Default::default() + })); + } + } } - if layout_binding.descriptor_type == DescriptorType::StorageImage { - // VUID-vkCmdDispatch-OpTypeImage-06423 - if binding_reqs.image_format.is_none() - && !desc_reqs.memory_write.is_empty() + Ok(()) + }; + + let check_image_view_common = + |set_num: u32, binding_num: u32, index: u32, image_view: &Arc| { + for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter()) + .chain(binding_reqs.descriptors.get(&None)) + { + if desc_reqs.storage_image_atomic && !image_view .format_features() - .intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT) + .intersects(FormatFeatures::STORAGE_IMAGE_ATOMIC) { - return Err( - DescriptorResourceInvalidError::StorageWriteWithoutFormatNotSupported, - ); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline performs atomic operations on \ + the image view bound to descriptor set {set_num}, \ + binding {binding_num}, descriptor index {index}, but \ + the format features of the image view's format do \ + not contain `FormatFeatures::STORAGE_IMAGE_ATOMIC`" + ) + .into(), + vuids: vuids!(vuid_type, "None-02691"), + ..Default::default() + })); } - // VUID-vkCmdDispatch-OpTypeImage-06424 - if binding_reqs.image_format.is_none() - && !desc_reqs.memory_read.is_empty() - && !image_view - .format_features() - .intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT) - { - return Err( - DescriptorResourceInvalidError::StorageReadWithoutFormatNotSupported, - ); + if layout_binding.descriptor_type == DescriptorType::StorageImage { + if binding_reqs.image_format.is_none() + && !desc_reqs.memory_write.is_empty() + && !image_view + .format_features() + .intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT) + { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline writes to the image view \ + bound to descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, without specifying a format, \ + but the format features of the image view's format do \ + not contain `FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT`" + ) + .into(), + vuids: vuids!(vuid_type, "OpTypeImage-06423"), + ..Default::default() + })); + } + + if binding_reqs.image_format.is_none() + && !desc_reqs.memory_read.is_empty() + && !image_view + .format_features() + .intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT) + { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline reads from the image view \ + bound to descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, without specifying a format, \ + but the format features of the image view's format do \ + not contain `FormatFeatures::STORAGE_READ_WITHOUT_FORMAT`" + ) + .into(), + vuids: vuids!(vuid_type, "OpTypeImage-06424"), + ..Default::default() + })); + } } } - } - - /* - Instruction/Sampler/Image View Validation - https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap16.html#textures-input-validation - */ - - // The SPIR-V Image Format is not compatible with the image view’s format. - if let Some(format) = binding_reqs.image_format { - if image_view.format() != format { - return Err(DescriptorResourceInvalidError::ImageViewFormatMismatch { - required: format, - provided: image_view.format(), - }); - } - } - - // Rules for viewType - if let Some(image_view_type) = binding_reqs.image_view_type { - if image_view.view_type() != image_view_type { - return Err(DescriptorResourceInvalidError::ImageViewTypeMismatch { - required: image_view_type, - provided: image_view.view_type(), - }); - } - } - - // - If the image was created with VkImageCreateInfo::samples equal to - // VK_SAMPLE_COUNT_1_BIT, the instruction must have MS = 0. - // - If the image was created with VkImageCreateInfo::samples not equal to - // VK_SAMPLE_COUNT_1_BIT, the instruction must have MS = 1. - if binding_reqs.image_multisampled - != (image_view.image().samples() != SampleCount::Sample1) - { - return Err( - DescriptorResourceInvalidError::ImageViewMultisampledMismatch { - required: binding_reqs.image_multisampled, - provided: image_view.image().samples() != SampleCount::Sample1, - }, - ); - } - - // - If the Sampled Type of the OpTypeImage does not match the numeric format of the - // image, as shown in the SPIR-V Sampled Type column of the - // Interpretation of Numeric Format table. - // - If the signedness of any read or sample operation does not match the signedness of - // the image’s format. - if let Some(scalar_type) = binding_reqs.image_scalar_type { - let aspects = image_view.subresource_range().aspects; - let view_scalar_type = ShaderScalarType::from( - if aspects.intersects( - ImageAspects::COLOR - | ImageAspects::PLANE_0 - | ImageAspects::PLANE_1 - | ImageAspects::PLANE_2, - ) { - image_view.format().type_color().unwrap() - } else if aspects.intersects(ImageAspects::DEPTH) { - image_view.format().type_depth().unwrap() - } else if aspects.intersects(ImageAspects::STENCIL) { - image_view.format().type_stencil().unwrap() - } else { - // Per `ImageViewBuilder::aspects` and - // VUID-VkDescriptorImageInfo-imageView-01976 - unreachable!() - }, - ); - - if scalar_type != view_scalar_type { - return Err( - DescriptorResourceInvalidError::ImageViewScalarTypeMismatch { - required: scalar_type, - provided: view_scalar_type, - }, - ); - } - } - - Ok(()) - }; - - let check_sampler_common = |index: u32, sampler: &Arc| { - for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter()) - .chain(binding_reqs.descriptors.get(&None)) - { - // VUID-vkCmdDispatch-None-02703 - // VUID-vkCmdDispatch-None-02704 - if desc_reqs.sampler_no_unnormalized_coordinates - && sampler.unnormalized_coordinates() - { - return Err( - DescriptorResourceInvalidError::SamplerUnnormalizedCoordinatesNotAllowed, - ); - } - - // - OpImageFetch, OpImageSparseFetch, OpImage*Gather, and OpImageSparse*Gather must not - // be used with a sampler that enables sampler Y′CBCR conversion. - // - The ConstOffset and Offset operands must not be used with a sampler that enables - // sampler Y′CBCR conversion. - if desc_reqs.sampler_no_ycbcr_conversion - && sampler.sampler_ycbcr_conversion().is_some() - { - return Err( - DescriptorResourceInvalidError::SamplerYcbcrConversionNotAllowed, - ); - } /* - Instruction/Sampler/Image View Validation - https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap16.html#textures-input-validation + Instruction/Sampler/Image View Validation + https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap16.html#textures-input-validation */ - // - The SPIR-V instruction is one of the OpImage*Dref* instructions and the sampler - // compareEnable is VK_FALSE - // - The SPIR-V instruction is not one of the OpImage*Dref* instructions and the sampler - // compareEnable is VK_TRUE - if desc_reqs.sampler_compare != sampler.compare().is_some() { - return Err(DescriptorResourceInvalidError::SamplerCompareMismatch { - required: desc_reqs.sampler_compare, - provided: sampler.compare().is_some(), - }); + // The SPIR-V Image Format is not compatible with the image view’s format. + if let Some(format) = binding_reqs.image_format { + if image_view.format() != format { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline accesses the image view \ + bound to descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, but the format of the image view \ + is not equal to the format required by the pipeline" + ) + .into(), + // vuids? + ..Default::default() + })); + } } - } - Ok(()) - }; + // Rules for viewType + if let Some(image_view_type) = binding_reqs.image_view_type { + if image_view.view_type() != image_view_type { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline accesses the image view \ + bound to descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, but the view type of the image view \ + is not equal to the view type required by the pipeline" + ) + .into(), + // vuids? + ..Default::default() + })); + } + } - let check_image_view = |index: u32, image_view_info: &DescriptorImageViewInfo| { - let DescriptorImageViewInfo { - image_view, - image_layout: _, - } = image_view_info; + // - If the image was created with VkImageCreateInfo::samples equal to + // VK_SAMPLE_COUNT_1_BIT, the instruction must have MS = 0. + // - If the image was created with VkImageCreateInfo::samples not equal to + // VK_SAMPLE_COUNT_1_BIT, the instruction must have MS = 1. + if binding_reqs.image_multisampled + && image_view.image().samples() == SampleCount::Sample1 + { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline accesses the image view \ + bound to descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, and the pipeline requires a \ + multisampled image, but the image view has only one sample" + ) + .into(), + // vuids? + ..Default::default() + })); + } else if !binding_reqs.image_multisampled + && image_view.image().samples() != SampleCount::Sample1 + { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline accesses the image view \ + bound to descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, and the pipeline requires a non-\ + multisampled image, but the image view has more than one sample" + ) + .into(), + // vuids? + ..Default::default() + })); + } - check_image_view_common(index, image_view)?; + // - If the Sampled Type of the OpTypeImage does not match the numeric format of the + // image, as shown in the SPIR-V Sampled Type column of the + // Interpretation of Numeric Format table. + // - If the signedness of any read or sample operation does not match the signedness of + // the image’s format. + if let Some(shader_numeric_type) = binding_reqs.image_scalar_type { + let aspects = image_view.subresource_range().aspects; + let view_numeric_type = NumericType::from( + if aspects.intersects( + ImageAspects::COLOR + | ImageAspects::PLANE_0 + | ImageAspects::PLANE_1 + | ImageAspects::PLANE_2, + ) { + image_view.format().numeric_format_color().unwrap() + } else if aspects.intersects(ImageAspects::DEPTH) { + image_view.format().numeric_format_depth().unwrap() + } else if aspects.intersects(ImageAspects::STENCIL) { + image_view.format().numeric_format_stencil().unwrap() + } else { + // Per `ImageViewBuilder::aspects` and + // VUID-VkDescriptorImageInfo-imageView-01976 + unreachable!() + }, + ); - if let Some(sampler) = layout_binding.immutable_samplers.get(index as usize) { - check_sampler_common(index, sampler)?; - } + if shader_numeric_type != view_numeric_type { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline accesses the image view \ + bound to descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, and the pipeline requires an \ + image view whose format has a `{shader_numeric_type:?}` \ + numeric type, but the format of the image view has a \ + `{view_numeric_type:?}` numeric type" + ) + .into(), + // vuids? + ..Default::default() + })); + } + } - Ok(()) - }; + Ok(()) + }; - let check_image_view_sampler = |index: u32, + let check_sampler_common = + |set_num: u32, binding_num: u32, index: u32, sampler: &Arc| { + for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter()) + .chain(binding_reqs.descriptors.get(&None)) + { + if desc_reqs.sampler_no_unnormalized_coordinates + && sampler.unnormalized_coordinates() + { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline accesses the sampler bound to \ + descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, in a way that does not support \ + samplers with unnormalized coordinates, but \ + the sampler currently bound to that descriptor \ + uses unnormalized coordinates" + ) + .into(), + vuids: vuids!(vuid_type, "None-02703", "None-02704"), + ..Default::default() + })); + } + + // - OpImageFetch, OpImageSparseFetch, OpImage*Gather, and OpImageSparse*Gather must not + // be used with a sampler that enables sampler Y′CBCR conversion. + // - The ConstOffset and Offset operands must not be used with a sampler that enables + // sampler Y′CBCR conversion. + if desc_reqs.sampler_no_ycbcr_conversion + && sampler.sampler_ycbcr_conversion().is_some() + { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline accesses the sampler bound to \ + descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, in a way that does not support \ + samplers with a sampler YCbCr conversion, but the sampler \ + currently bound to that descriptor has a \ + sampler YCbCr conversion" + ) + .into(), + // vuids? + ..Default::default() + })); + } + + /* + Instruction/Sampler/Image View Validation + https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap16.html#textures-input-validation + */ + + if desc_reqs.sampler_compare && sampler.compare().is_none() { + // - The SPIR-V instruction is one of the OpImage*Dref* instructions and the sampler + // compareEnable is VK_FALSE + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline accesses the sampler bound to \ + descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, in a way that requires a sampler \ + with a compare operation, but the sampler currently bound to \ + that descriptor does not have a compare operation" + ) + .into(), + // vuids? + ..Default::default() + })); + } else if !desc_reqs.sampler_compare && sampler.compare().is_some() { + // - The SPIR-V instruction is not one of the OpImage*Dref* instructions and the sampler + // compareEnable is VK_TRUE + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline accesses the sampler bound to \ + descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, in a way that requires a sampler \ + without a compare operation, but the sampler currently bound \ + to that descriptor has a compare operation" + ) + .into(), + // vuids? + ..Default::default() + })); + } + } + + Ok(()) + }; + + let check_image_view = + |set_num: u32, + binding_num: u32, + index: u32, + image_view_info: &DescriptorImageViewInfo| { + let DescriptorImageViewInfo { + image_view, + image_layout: _, + } = image_view_info; + + check_image_view_common(set_num, binding_num, index, image_view)?; + + if let Some(sampler) = layout_binding.immutable_samplers.get(index as usize) { + check_sampler_common(set_num, binding_num, index, sampler)?; + } + + Ok(()) + }; + + let check_image_view_sampler = |set_num: u32, + binding_num: u32, + index: u32, (image_view_info, sampler): &( DescriptorImageViewInfo, Arc, @@ -1055,64 +1271,79 @@ where image_layout: _, } = image_view_info; - check_image_view_common(index, image_view)?; - check_sampler_common(index, sampler)?; + check_image_view_common(set_num, binding_num, index, image_view)?; + check_sampler_common(set_num, binding_num, index, sampler)?; Ok(()) }; - let check_sampler = |index: u32, sampler: &Arc| { - check_sampler_common(index, sampler)?; + let check_sampler = + |set_num: u32, binding_num: u32, index: u32, sampler: &Arc| { + check_sampler_common(set_num, binding_num, index, sampler)?; - for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter()) - .chain(binding_reqs.descriptors.get(&None)) - { - // Check sampler-image compatibility. Only done for separate samplers; - // combined image samplers are checked when updating the descriptor set. + for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter()) + .chain(binding_reqs.descriptors.get(&None)) + { + // Check sampler-image compatibility. Only done for separate samplers; + // combined image samplers are checked when updating the descriptor set. - // If the image view isn't actually present in the resources, then just skip it. - // It will be caught later by check_resources. - let iter = desc_reqs.sampler_with_images.iter().filter_map(|id| { - descriptor_set_state - .descriptor_sets - .get(&id.set) - .and_then(|set| set.resources().binding(id.binding)) - .and_then(|res| match res { - DescriptorBindingResources::ImageView(elements) => elements - .get(id.index as usize) - .and_then(|opt| opt.as_ref().map(|opt| (id, opt))), - _ => None, - }) - }); + // If the image view isn't actually present in the resources, then just skip it. + // It will be caught later by check_resources. + let iter = desc_reqs.sampler_with_images.iter().filter_map(|id| { + descriptor_set_state + .descriptor_sets + .get(&id.set) + .and_then(|set| set.resources().binding(id.binding)) + .and_then(|res| match res { + DescriptorBindingResources::ImageView(elements) => elements + .get(id.index as usize) + .and_then(|opt| opt.as_ref().map(|opt| (id, opt))), + _ => None, + }) + }); - for (id, image_view_info) in iter { - let DescriptorImageViewInfo { - image_view, - image_layout: _, - } = image_view_info; + for (id, image_view_info) in iter { + let DescriptorIdentifier { + set: iset_num, + binding: ibinding_num, + index: iindex, + } = id; + let DescriptorImageViewInfo { + image_view, + image_layout: _, + } = image_view_info; - if let Err(error) = sampler.check_can_sample(image_view.as_ref()) { - return Err( - DescriptorResourceInvalidError::SamplerImageViewIncompatible { - image_view_set_num: id.set, - image_view_binding_num: id.binding, - image_view_index: id.index, - error, - }, - ); + if let Err(error) = sampler.check_can_sample(image_view.as_ref()) { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline uses the sampler bound to \ + descriptor set {set_num}, binding {binding_num}, \ + descriptor index {index}, to sample the image bound to \ + descriptor set {iset_num}, binding {ibinding_num}, \ + descriptor index {iindex}, but the sampler is not \ + compatible with the image: {}", + error, + ) + .into(), + // vuids? + ..Default::default() + })); + } } } - } - Ok(()) - }; + Ok(()) + }; let check_acceleration_structure = - |_index: u32, _acceleration_structure: &Arc| Ok(()); + |_set_num: u32, + _binding_num: u32, + _index: u32, + _acceleration_structure: &Arc| Ok(()); - let check_none = |index: u32, _: &()| { + let check_none = |set_num: u32, binding_num: u32, index: u32, _: &()| { if let Some(sampler) = layout_binding.immutable_samplers.get(index as usize) { - check_sampler(index, sampler)?; + check_sampler(set_num, binding_num, index, sampler)?; } Ok(()) @@ -1121,20 +1352,43 @@ where let set_resources = descriptor_set_state .descriptor_sets .get(&set_num) - .ok_or(PipelineExecutionError::DescriptorSetNotBound { set_num })? + .ok_or(Box::new(ValidationError { + problem: format!( + "the currently bound pipeline accesses descriptor set {set_num}, but \ + no descriptor set was previously bound" + ) + .into(), + // vuids? + ..Default::default() + }))? .resources(); let binding_resources = set_resources.binding(binding_num).unwrap(); match binding_resources { DescriptorBindingResources::None(elements) => { - validate_resources(set_num, binding_num, binding_reqs, elements, check_none)?; + validate_resources( + vuid_type, + set_num, + binding_num, + binding_reqs, + elements, + check_none, + )?; } DescriptorBindingResources::Buffer(elements) => { - validate_resources(set_num, binding_num, binding_reqs, elements, check_buffer)?; + validate_resources( + vuid_type, + set_num, + binding_num, + binding_reqs, + elements, + check_buffer, + )?; } DescriptorBindingResources::BufferView(elements) => { validate_resources( + vuid_type, set_num, binding_num, binding_reqs, @@ -1144,6 +1398,7 @@ where } DescriptorBindingResources::ImageView(elements) => { validate_resources( + vuid_type, set_num, binding_num, binding_reqs, @@ -1153,6 +1408,7 @@ where } DescriptorBindingResources::ImageViewSampler(elements) => { validate_resources( + vuid_type, set_num, binding_num, binding_reqs, @@ -1162,6 +1418,7 @@ where } DescriptorBindingResources::Sampler(elements) => { validate_resources( + vuid_type, set_num, binding_num, binding_reqs, @@ -1178,6 +1435,7 @@ where DescriptorBindingResources::InlineUniformBlock => (), DescriptorBindingResources::AccelerationStructure(elements) => { validate_resources( + vuid_type, set_num, binding_num, binding_reqs, @@ -1193,38 +1451,56 @@ where fn validate_pipeline_push_constants( &self, + vuid_type: VUIDType, pipeline_layout: &PipelineLayout, - ) -> Result<(), PipelineExecutionError> { + ) -> Result<(), Box> { if pipeline_layout.push_constant_ranges().is_empty() || self.device().enabled_features().maintenance4 { return Ok(()); } - // VUID-vkCmdDispatch-maintenance4-06425 let constants_pipeline_layout = self .builder_state .push_constants_pipeline_layout .as_ref() - .ok_or(PipelineExecutionError::PushConstantsMissing)?; + .ok_or(Box::new(ValidationError { + problem: "the currently bound pipeline accesses push constants, but no \ + push constants were previously set" + .into(), + vuids: vuids!(vuid_type, "maintenance4-06425"), + ..Default::default() + }))?; - // VUID-vkCmdDispatch-maintenance4-06425 if pipeline_layout.handle() != constants_pipeline_layout.handle() && pipeline_layout.push_constant_ranges() != constants_pipeline_layout.push_constant_ranges() { - return Err(PipelineExecutionError::PushConstantsNotCompatible); + return Err(Box::new(ValidationError { + problem: "the currently bound pipeline accesses push constants, but the \ + pipeline layouts that were used to set the push constants are \ + not compatible with the pipeline layout of the currently bound pipeline" + .into(), + vuids: vuids!(vuid_type, "maintenance4-06425"), + ..Default::default() + })); } let set_bytes = &self.builder_state.push_constants; - // VUID-vkCmdDispatch-maintenance4-06425 if !pipeline_layout .push_constant_ranges() .iter() .all(|pc_range| set_bytes.contains(pc_range.offset..pc_range.offset + pc_range.size)) { - return Err(PipelineExecutionError::PushConstantsMissing); + return Err(Box::new(ValidationError { + problem: "the currently bound pipeline accesses push constants, but \ + not all bytes in the push constant ranges of the pipeline layout of the \ + currently bound pipeline have been set" + .into(), + vuids: vuids!(vuid_type, "maintenance4-06425"), + ..Default::default() + })); } Ok(()) @@ -1232,11 +1508,11 @@ where fn validate_pipeline_graphics_dynamic_state( &self, + vuid_type: VUIDType, pipeline: &GraphicsPipeline, - ) -> Result<(), PipelineExecutionError> { + ) -> Result<(), Box> { let device = pipeline.device(); - // VUID-vkCmdDraw-commandBuffer-02701 for dynamic_state in pipeline .dynamic_states() .filter(|(_, d)| *d) @@ -1244,82 +1520,169 @@ where { match dynamic_state { DynamicState::BlendConstants => { - // VUID? if self.builder_state.blend_constants.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07835"), + ..Default::default() + })); } } DynamicState::ColorWriteEnable => { - // VUID-vkCmdDraw-attachmentCount-06667 let enables = if let Some(enables) = &self.builder_state.color_write_enable { enables } else { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07749"), + ..Default::default() + })); }; - // VUID-vkCmdDraw-attachmentCount-06667 if enables.len() < pipeline.color_blend_state().unwrap().attachments.len() { - return Err( - PipelineExecutionError::DynamicColorWriteEnableNotEnoughValues { - color_write_enable_count: enables.len() as u32, - attachment_count: pipeline - .color_blend_state() - .unwrap() - .attachments - .len() as u32, - }, - ); + return Err(Box::new(ValidationError { + problem: "the currently bound graphics pipeline requires the \ + `DynamicState::ColorWriteEnable` dynamic state, but \ + the number of enable values that were set is less than the number \ + of color attachments in the color blend state of the \ + graphics pipeline" + .into(), + vuids: vuids!(vuid_type, "attachmentCount-07750"), + ..Default::default() + })); } } DynamicState::CullMode => { - // VUID? if self.builder_state.cull_mode.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07840"), + ..Default::default() + })); } } DynamicState::DepthBias => { - // VUID? if self.builder_state.depth_bias.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07834"), + ..Default::default() + })); } } DynamicState::DepthBiasEnable => { - // VUID-vkCmdDraw-None-04877 if self.builder_state.depth_bias_enable.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-04877"), + ..Default::default() + })); } } DynamicState::DepthBounds => { - // VUID? if self.builder_state.depth_bounds.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07836"), + ..Default::default() + })); } } DynamicState::DepthBoundsTestEnable => { - // VUID? if self.builder_state.depth_bounds_test_enable.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07846"), + ..Default::default() + })); } } DynamicState::DepthCompareOp => { - // VUID? if self.builder_state.depth_compare_op.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07845"), + ..Default::default() + })); } } DynamicState::DepthTestEnable => { - // VUID? if self.builder_state.depth_test_enable.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07843"), + ..Default::default() + })); } } DynamicState::DepthWriteEnable => { - // VUID? if self.builder_state.depth_write_enable.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07844"), + ..Default::default() + })); } - - // TODO: Check if the depth buffer is writable } DynamicState::DiscardRectangle => { let discard_rectangle_count = @@ -1329,51 +1692,115 @@ where }; for num in 0..discard_rectangle_count { - // VUID? if !self.builder_state.discard_rectangle.contains_key(&num) { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set for discard rectangle {1}, or \ + it was overwritten by a more recent \ + `bind_pipeline_graphics` command", + dynamic_state, num, + ).into(), + vuids: vuids!(vuid_type, "None-07751"), + ..Default::default() + })); } } } DynamicState::ExclusiveScissor => todo!(), DynamicState::FragmentShadingRate => todo!(), DynamicState::FrontFace => { - // VUID? if self.builder_state.front_face.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-0784"), + ..Default::default() + })); } } DynamicState::LineStipple => { - // VUID? if self.builder_state.line_stipple.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07849"), + ..Default::default() + })); } } DynamicState::LineWidth => { - // VUID? if self.builder_state.line_width.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07833"), + ..Default::default() + })); } } DynamicState::LogicOp => { - // VUID-vkCmdDraw-logicOp-04878 if self.builder_state.logic_op.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "logicOp-04878"), + ..Default::default() + })); } } DynamicState::PatchControlPoints => { - // VUID-vkCmdDraw-None-04875 if self.builder_state.patch_control_points.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-04875"), + ..Default::default() + })); } } DynamicState::PrimitiveRestartEnable => { - // VUID-vkCmdDraw-None-04879 let primitive_restart_enable = if let Some(enable) = self.builder_state.primitive_restart_enable { enable } else { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-04879"), + ..Default::default() + })); }; if primitive_restart_enable { @@ -1383,9 +1810,16 @@ where if let Some(topology) = self.builder_state.primitive_topology { topology } else { - return Err(PipelineExecutionError::DynamicStateNotSet { - dynamic_state: DynamicState::PrimitiveTopology, - }); + return Err(Box::new(ValidationError { + problem: "the currently bound graphics pipeline requires \ + the `DynamicState::PrimitiveTopology` dynamic state, \ + but this state was either not set, or it was \ + overwritten by a more recent `bind_pipeline_graphics` \ + command" + .into(), + vuids: vuids!(vuid_type, "None-07842"), + ..Default::default() + })); } } }; @@ -1396,30 +1830,34 @@ where | PrimitiveTopology::TriangleList | PrimitiveTopology::LineListWithAdjacency | PrimitiveTopology::TriangleListWithAdjacency => { - // VUID? if !device.enabled_features().primitive_topology_list_restart { - return Err(PipelineExecutionError::RequirementNotMet { - required_for: "The bound pipeline sets \ - `DynamicState::PrimitiveRestartEnable` and the \ - current primitive topology is \ - `PrimitiveTopology::*List`", + return Err(Box::new(ValidationError { + problem: "primitive restart is enabled for the currently \ + bound graphics pipeline, but the currently set \ + dynamic primitive topology is \ + `PrimitiveTopology::*List`" + .into(), requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature("primitive_topology_list_restart")])]), - }); + // vuids? + ..Default::default() + })); } } PrimitiveTopology::PatchList => { - // VUID? if !device .enabled_features() .primitive_topology_patch_list_restart { - return Err(PipelineExecutionError::RequirementNotMet { - required_for: "The bound pipeline sets \ - `DynamicState::PrimitiveRestartEnable` and the \ - current primitive topology is \ - `PrimitiveTopology::PatchList`", + return Err(Box::new(ValidationError { + problem: "primitive restart is enabled for the currently \ + bound graphics pipeline, but the currently set \ + dynamic primitive topology is \ + `PrimitiveTopology::PatchList`" + .into(), requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature("primitive_topology_patch_list_restart")])]), - }); + // vuids? + ..Default::default() + })); } } _ => (), @@ -1427,50 +1865,92 @@ where } } DynamicState::PrimitiveTopology => { - // VUID-vkCmdDraw-primitiveTopology-03420 let topology = if let Some(topology) = self.builder_state.primitive_topology { topology } else { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07842"), + ..Default::default() + })); }; if pipeline.shader(ShaderStage::TessellationControl).is_some() { - // VUID? if !matches!(topology, PrimitiveTopology::PatchList) { - return Err(PipelineExecutionError::DynamicPrimitiveTopologyInvalid { - topology, - }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, and the graphics pipeline \ + includes tessellation shader stages, but the dynamic \ + primitive topology is not `PrimitiveTopology::PatchList`", + dynamic_state + ).into(), + // vuids? + ..Default::default() + })); } } else { - // VUID? if matches!(topology, PrimitiveTopology::PatchList) { - return Err(PipelineExecutionError::DynamicPrimitiveTopologyInvalid { - topology, - }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, and the graphics pipeline \ + does not include tessellation shader stages, but the dynamic \ + primitive topology is `PrimitiveTopology::PatchList`", + dynamic_state + ).into(), + // vuids? + ..Default::default() + })); } } - let required_topology_class = match pipeline.input_assembly_state().topology { - PartialStateMode::Dynamic(topology_class) => topology_class, - _ => unreachable!(), - }; + let properties = device.physical_device().properties(); - // VUID-vkCmdDraw-primitiveTopology-03420 - if topology.class() != required_topology_class { - return Err( - PipelineExecutionError::DynamicPrimitiveTopologyClassMismatch { - provided_class: topology.class(), - required_class: required_topology_class, - }, - ); + if !properties.dynamic_primitive_topology_unrestricted.unwrap_or(false) { + let required_topology_class = match pipeline.input_assembly_state().topology { + PartialStateMode::Dynamic(topology_class) => topology_class, + _ => unreachable!(), + }; + + if topology.class() != required_topology_class { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, and the \ + `dynamic_primitive_topology_unrestricted` device property is \ + `false`, but the dynamic primitive topology does not belong \ + to the same topology class as the topology that the \ + graphics pipeline was created with", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "dynamicPrimitiveTopologyUnrestricted-07500"), + ..Default::default() + })); + } } // TODO: check that the topology matches the geometry shader } DynamicState::RasterizerDiscardEnable => { - // VUID-vkCmdDraw-None-04876 if self.builder_state.rasterizer_discard_enable.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-04876"), + ..Default::default() + })); } } DynamicState::RayTracingPipelineStackSize => unreachable!( @@ -1479,120 +1959,218 @@ where DynamicState::SampleLocations => todo!(), DynamicState::Scissor => { for num in 0..pipeline.viewport_state().unwrap().count().unwrap() { - // VUID? if !self.builder_state.scissor.contains_key(&num) { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07832"), + ..Default::default() + })); } } } DynamicState::ScissorWithCount => { - // VUID-vkCmdDraw-scissorCount-03418 - // VUID-vkCmdDraw-viewportCount-03419 - let scissor_count = if let Some(scissors) = &self.builder_state.scissor_with_count { - scissors.len() as u32 - } else { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); - }; + if let Some(scissors) = &self.builder_state.scissor_with_count { + if let Some(viewport_count) = pipeline.viewport_state().unwrap().count() { + // Check if the counts match, but only if the viewport count is fixed. + // If the viewport count is also dynamic, then the + // DynamicState::ViewportWithCount match arm will handle it. - // Check if the counts match, but only if the viewport count is fixed. - // If the viewport count is also dynamic, then the - // DynamicState::ViewportWithCount match arm will handle it. - if let Some(viewport_count) = pipeline.viewport_state().unwrap().count() { - // VUID-vkCmdDraw-scissorCount-03418 - if viewport_count != scissor_count { - return Err( - PipelineExecutionError::DynamicViewportScissorCountMismatch { - viewport_count, - scissor_count, - }, - ); + if viewport_count != scissors.len() as u32 { + return Err(Box::new(ValidationError { + problem: "the currently bound graphics pipeline requires the \ + `DynamicState::ScissorWithCount` dynamic state, and \ + not the `DynamicState::ViewportWithCount` dynamic state, \ + but the dynamic scissor count is not equal to the \ + viewport count specified when creating the pipeline" + .into(), + vuids: vuids!(vuid_type, "scissorCount-03418"), + ..Default::default() + })); + } } - } + } else { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "scissorCount-03418", "viewportCount-03419"), + ..Default::default() + })); + }; } DynamicState::StencilCompareMask => { let state = self.builder_state.stencil_compare_mask; - // VUID? if state.front.is_none() || state.back.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07837"), + ..Default::default() + })); } } DynamicState::StencilOp => { let state = self.builder_state.stencil_op; - // VUID? if state.front.is_none() || state.back.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07848"), + ..Default::default() + })); } } DynamicState::StencilReference => { let state = self.builder_state.stencil_reference; - // VUID? if state.front.is_none() || state.back.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07839"), + ..Default::default() + })); } } DynamicState::StencilTestEnable => { - // VUID? if self.builder_state.stencil_test_enable.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07847"), + ..Default::default() + })); } - - // TODO: Check if the stencil buffer is writable } DynamicState::StencilWriteMask => { let state = self.builder_state.stencil_write_mask; - // VUID? if state.front.is_none() || state.back.is_none() { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07838"), + ..Default::default() + })); } } DynamicState::VertexInput => todo!(), DynamicState::VertexInputBindingStride => todo!(), DynamicState::Viewport => { for num in 0..pipeline.viewport_state().unwrap().count().unwrap() { - // VUID? if !self.builder_state.viewport.contains_key(&num) { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "None-07831"), + ..Default::default() + })); } } } DynamicState::ViewportCoarseSampleOrder => todo!(), DynamicState::ViewportShadingRatePalette => todo!(), DynamicState::ViewportWithCount => { - // VUID-vkCmdDraw-viewportCount-03417 let viewport_count = if let Some(viewports) = &self.builder_state.viewport_with_count { viewports.len() as u32 } else { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "viewportCount-03417", "viewportCount-03419"), + ..Default::default() + })); }; - let scissor_count = if let Some(scissor_count) = + if let Some(scissor_count) = pipeline.viewport_state().unwrap().count() { - // The scissor count is fixed. - scissor_count - } else { - // VUID-vkCmdDraw-viewportCount-03419 - // The scissor count is also dynamic. - if let Some(scissors) = &self.builder_state.scissor_with_count { - scissors.len() as u32 - } else { - return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); + if viewport_count != scissor_count { + return Err(Box::new(ValidationError { + problem: "the currently bound graphics pipeline requires the \ + `DynamicState::ViewportWithCount` dynamic state, and \ + not the `DynamicState::ScissorWithCount` dynamic state, but \ + the dynamic scissor count is not equal to the scissor count \ + specified when creating the pipeline" + .into(), + vuids: vuids!(vuid_type, "viewportCount-03417"), + ..Default::default() + })); + } + } else { + if let Some(scissors) = &self.builder_state.scissor_with_count { + if viewport_count != scissors.len() as u32 { + return Err(Box::new(ValidationError { + problem: "the currently bound graphics pipeline requires both \ + the `DynamicState::ViewportWithCount` and the \ + `DynamicState::ScissorWithCount` dynamic states, but \ + the dynamic scissor count is not equal to the \ + dynamic scissor count " + .into(), + vuids: vuids!(vuid_type, "viewportCount-03419"), + ..Default::default() + })); + } + } else { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline requires the \ + `DynamicState::{:?}` dynamic state, but \ + this state was either not set, or it was overwritten by a \ + more recent `bind_pipeline_graphics` command", + dynamic_state + ).into(), + vuids: vuids!(vuid_type, "scissorCount-03418", "viewportCount-03419"), + ..Default::default() + })); } - }; - - // VUID-vkCmdDraw-viewportCount-03417 - // VUID-vkCmdDraw-viewportCount-03419 - if viewport_count != scissor_count { - return Err( - PipelineExecutionError::DynamicViewportScissorCountMismatch { - viewport_count, - scissor_count, - }, - ); } // TODO: VUID-vkCmdDrawIndexed-primitiveFragmentShadingRateWithMultipleViewports-04552 @@ -1644,14 +2222,18 @@ where fn validate_pipeline_graphics_render_pass( &self, + vuid_type: VUIDType, pipeline: &GraphicsPipeline, render_pass_state: &RenderPassState, - ) -> Result<(), PipelineExecutionError> { - // VUID? + ) -> Result<(), Box> { if render_pass_state.contents != SubpassContents::Inline { - return Err(PipelineExecutionError::ForbiddenWithSubpassContents { - subpass_contents: render_pass_state.contents, - }); + return Err(Box::new(ValidationError { + problem: "the contents of the current subpass instance is not \ + `SubpassContents::Inline`" + .into(), + // vuids? + ..Default::default() + })); } match (&render_pass_state.render_pass, pipeline.subpass()) { @@ -1659,51 +2241,59 @@ where RenderPassStateType::BeginRenderPass(state), PipelineSubpassType::BeginRenderPass(pipeline_subpass), ) => { - // VUID-vkCmdDraw-renderPass-02684 if !pipeline_subpass .render_pass() .is_compatible_with(state.subpass.render_pass()) { - return Err(PipelineExecutionError::PipelineRenderPassNotCompatible); + return Err(Box::new(ValidationError { + problem: "the current render pass instance is not compatible with the \ + render pass that the currently bound graphics pipeline was \ + created with" + .into(), + vuids: vuids!(vuid_type, "renderPass-02684"), + ..Default::default() + })); } - // VUID-vkCmdDraw-subpass-02685 if pipeline_subpass.index() != state.subpass.index() { - return Err(PipelineExecutionError::PipelineSubpassMismatch { - pipeline: pipeline_subpass.index(), - current: state.subpass.index(), - }); + return Err(Box::new(ValidationError { + problem: "the subpass index of the current render pass instance is not \ + equal to the index of the subpass that the bound graphics pipeline was \ + created with" + .into(), + vuids: vuids!(vuid_type, "subpass-02685"), + ..Default::default() + })); } } ( RenderPassStateType::BeginRendering(_), PipelineSubpassType::BeginRendering(pipeline_rendering_info), ) => { - // VUID-vkCmdDraw-viewMask-06178 if pipeline_rendering_info.view_mask != render_pass_state.rendering_info.view_mask { - return Err(PipelineExecutionError::PipelineViewMaskMismatch { - pipeline_view_mask: pipeline_rendering_info.view_mask, - required_view_mask: render_pass_state.rendering_info.view_mask, - }); + return Err(Box::new(ValidationError { + problem: "the `view_mask` of the current render pass instance is not \ + equal to the `view_mask` the bound graphics pipeline was created with" + .into(), + vuids: vuids!(vuid_type, "viewMask-06178"), + ..Default::default() + })); } - // VUID-vkCmdDraw-colorAttachmentCount-06179 if pipeline_rendering_info.color_attachment_formats.len() != render_pass_state .rendering_info .color_attachment_formats .len() { - return Err( - PipelineExecutionError::PipelineColorAttachmentCountMismatch { - pipeline_count: pipeline_rendering_info.color_attachment_formats.len() - as u32, - required_count: render_pass_state - .rendering_info - .color_attachment_formats - .len() as u32, - }, - ); + return Err(Box::new(ValidationError { + problem: "`color_attachments.len()` of the current render pass \ + instance is not equal to `color_attachment_formats.len()` that the + currently bound graphics pipeline was created with" + .into(), + vuids: vuids!(vuid_type, "colorAttachmentCount-06179"), + ..Default::default() + })); } for (color_attachment_index, required_format, pipeline_format) in render_pass_state @@ -1719,15 +2309,19 @@ where .enumerate() .filter_map(|(i, (r, p))| r.map(|r| (i as u32, r, p))) { - // VUID-vkCmdDraw-colorAttachmentCount-06180 if Some(required_format) != pipeline_format { - return Err( - PipelineExecutionError::PipelineColorAttachmentFormatMismatch { + return Err(Box::new(ValidationError { + problem: format!( + "`color_attachments[{0}].image_view.format()` of the current \ + render pass instance is not equal to \ + `color_attachment_formats[{0}]` that the currently bound \ + graphics pipeline was created with", color_attachment_index, - pipeline_format, - required_format, - }, - ); + ) + .into(), + vuids: vuids!(vuid_type, "colorAttachmentCount-06180"), + ..Default::default() + })); } } @@ -1736,14 +2330,16 @@ where .depth_attachment_format .map(|r| (r, pipeline_rendering_info.depth_attachment_format)) { - // VUID-vkCmdDraw-pDepthAttachment-06181 if Some(required_format) != pipeline_format { - return Err( - PipelineExecutionError::PipelineDepthAttachmentFormatMismatch { - pipeline_format, - required_format, - }, - ); + return Err(Box::new(ValidationError { + problem: "`depth_attachment.image_view.format()` of the current \ + render pass instance is not equal to \ + `depth_attachment_format` that the currently bound \ + graphics pipeline was created with" + .into(), + vuids: vuids!(vuid_type, "pDepthAttachment-06181"), + ..Default::default() + })); } } @@ -1752,14 +2348,16 @@ where .stencil_attachment_format .map(|r| (r, pipeline_rendering_info.stencil_attachment_format)) { - // VUID-vkCmdDraw-pStencilAttachment-06182 if Some(required_format) != pipeline_format { - return Err( - PipelineExecutionError::PipelineStencilAttachmentFormatMismatch { - pipeline_format, - required_format, - }, - ); + return Err(Box::new(ValidationError { + problem: "`stencil_attachment.image_view.format()` of the current \ + render pass instance is not equal to \ + `stencil_attachment_format` that the currently bound \ + graphics pipeline was created with" + .into(), + vuids: vuids!(vuid_type, "pStencilAttachment-06182"), + ..Default::default() + })); } } @@ -1771,7 +2369,26 @@ where // VUID-vkCmdDraw-imageView-06177 // TODO: } - _ => return Err(PipelineExecutionError::PipelineRenderPassTypeMismatch), + (RenderPassStateType::BeginRenderPass(_), PipelineSubpassType::BeginRendering(_)) => { + return Err(Box::new(ValidationError { + problem: "the current render pass instance was begun with \ + `begin_render_pass`, but the currently bound graphics pipeline requires \ + a render pass instance begun with `begin_rendering`" + .into(), + // vuids? + ..Default::default() + })); + } + (RenderPassStateType::BeginRendering(_), PipelineSubpassType::BeginRenderPass(_)) => { + return Err(Box::new(ValidationError { + problem: "the current render pass instance was begun with \ + `begin_rendering`, but the currently bound graphics pipeline requires \ + a render pass instance begun with `begin_render_pass`" + .into(), + // vuids? + ..Default::default() + })); + } } // VUID-vkCmdDraw-None-02686 @@ -1782,105 +2399,24 @@ where fn validate_pipeline_graphics_vertex_buffers( &self, + vuid_type: VUIDType, pipeline: &GraphicsPipeline, - vertices: Option<(u32, u32)>, - instances: Option<(u32, u32)>, - ) -> Result<(), PipelineExecutionError> { + ) -> Result<(), Box> { let vertex_input = pipeline.vertex_input_state(); - let mut vertices_in_buffers: Option = None; - let mut instances_in_buffers: Option = None; - for (&binding_num, binding_desc) in &vertex_input.bindings { - // VUID-vkCmdDraw-None-04007 - let vertex_buffer = match self.builder_state.vertex_buffers.get(&binding_num) { - Some(x) => x, - None => return Err(PipelineExecutionError::VertexBufferNotBound { binding_num }), - }; - - let mut num_elements = vertex_buffer.size() / binding_desc.stride as u64; - - match binding_desc.input_rate { - VertexInputRate::Vertex => { - vertices_in_buffers = Some(if let Some(x) = vertices_in_buffers { - min(x, num_elements) - } else { - num_elements - }); - } - VertexInputRate::Instance { divisor } => { - if divisor == 0 { - // A divisor of 0 means the same instance data is used for all instances, - // so we can draw any number of instances from a single element. - // The buffer must contain at least one element though. - if num_elements != 0 { - num_elements = u64::MAX; - } - } else { - // If divisor is e.g. 2, we use only half the amount of data from the source - // buffer, so the number of instances that can be drawn is twice as large. - num_elements = num_elements.saturating_mul(divisor as u64); - } - - instances_in_buffers = Some(if let Some(x) = instances_in_buffers { - min(x, num_elements) - } else { - num_elements - }); - } - }; - } - - if let Some((first_vertex, vertex_count)) = vertices { - let vertices_needed = first_vertex as u64 + vertex_count as u64; - - if let Some(vertices_in_buffers) = vertices_in_buffers { - // VUID-vkCmdDraw-None-02721 - if vertices_needed > vertices_in_buffers { - return Err(PipelineExecutionError::VertexBufferVertexRangeOutOfBounds { - vertices_needed, - vertices_in_buffers, - }); - } - } - } - - if let Some((first_instance, instance_count)) = instances { - let instances_needed = first_instance as u64 + instance_count as u64; - - if let Some(instances_in_buffers) = instances_in_buffers { - // VUID-vkCmdDraw-None-02721 - if instances_needed > instances_in_buffers { - return Err( - PipelineExecutionError::VertexBufferInstanceRangeOutOfBounds { - instances_needed, - instances_in_buffers, - }, - ); - } - } - - let view_mask = match pipeline.subpass() { - PipelineSubpassType::BeginRenderPass(subpass) => subpass.render_pass().views_used(), - PipelineSubpassType::BeginRendering(rendering_info) => rendering_info.view_mask, - }; - - if view_mask != 0 { - let max = pipeline - .device() - .physical_device() - .properties() - .max_multiview_instance_index - .unwrap_or(0); - - let highest_instance = instances_needed.saturating_sub(1); - - // VUID-vkCmdDraw-maxMultiviewInstanceIndex-02688 - if highest_instance > max as u64 { - return Err(PipelineExecutionError::MaxMultiviewInstanceIndexExceeded { - highest_instance, - max, - }); - } + for &binding_num in vertex_input.bindings.keys() { + if !self.builder_state.vertex_buffers.contains_key(&binding_num) { + return Err(Box::new(ValidationError { + problem: format!( + "the currently bound graphics pipeline uses \ + vertex buffer binding {0}, but \ + no vertex buffer is currently bound to binding {0}", + binding_num + ) + .into(), + vuids: vuids!(vuid_type, "None-04007"), + ..Default::default() + })); } } @@ -2148,9 +2684,64 @@ impl UnsafeCommandBufferBuilder where A: CommandBufferAllocator, { - /// Calls `vkCmdDispatch` on the builder. - #[inline] - pub unsafe fn dispatch(&mut self, group_counts: [u32; 3]) -> &mut Self { + pub unsafe fn dispatch( + &mut self, + group_counts: [u32; 3], + ) -> Result<&mut Self, Box> { + self.validate_dispatch(group_counts)?; + + Ok(self.dispatch_unchecked(group_counts)) + } + + fn validate_dispatch(&self, group_counts: [u32; 3]) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + compute operations" + .into(), + vuids: &["VUID-vkCmdDispatch-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + let properties = self.device().physical_device().properties(); + + if group_counts[0] > properties.max_compute_work_group_count[0] { + return Err(Box::new(ValidationError { + context: "group_counts[0]".into(), + problem: "is greater than the `max_compute_work_group_count[0]` limit".into(), + vuids: &["VUID-vkCmdDispatch-groupCountX-00386"], + ..Default::default() + })); + } + + if group_counts[1] > properties.max_compute_work_group_count[1] { + return Err(Box::new(ValidationError { + context: "group_counts[1]".into(), + problem: "is greater than the `max_compute_work_group_count[1]` limit".into(), + vuids: &["VUID-vkCmdDispatch-groupCountY-00387"], + ..Default::default() + })); + } + + if group_counts[2] > properties.max_compute_work_group_count[2] { + return Err(Box::new(ValidationError { + context: "group_counts[2]".into(), + problem: "is greater than the `max_compute_work_group_count[2]` limit".into(), + vuids: &["VUID-vkCmdDispatch-groupCountZ-00388"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn dispatch_unchecked(&mut self, group_counts: [u32; 3]) -> &mut Self { let fns = self.device().fns(); (fns.v1_0.cmd_dispatch)( self.handle(), @@ -2162,11 +2753,69 @@ where self } - /// Calls `vkCmdDispatchIndirect` on the builder. - #[inline] pub unsafe fn dispatch_indirect( &mut self, indirect_buffer: &Subbuffer<[DispatchIndirectCommand]>, + ) -> Result<&mut Self, Box> { + self.validate_dispatch_indirect(indirect_buffer.as_bytes())?; + + Ok(self.dispatch_indirect_unchecked(indirect_buffer)) + } + + fn validate_dispatch_indirect( + &self, + indirect_buffer: &Subbuffer<[u8]>, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + compute operations" + .into(), + vuids: &["VUID-vkCmdDispatchIndirect-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + // VUID-vkCmdDispatchIndirect-commonparent + assert_eq!(self.device(), indirect_buffer.device()); + + if !indirect_buffer + .buffer() + .usage() + .intersects(BufferUsage::INDIRECT_BUFFER) + { + return Err(Box::new(ValidationError { + context: "indirect_buffer.usage()".into(), + problem: "does not contain `BufferUsage::INDIRECT_BUFFER`".into(), + vuids: &["VUID-vkCmdDispatchIndirect-buffer-02709"], + ..Default::default() + })); + } + + if size_of::() as DeviceSize > indirect_buffer.size() { + return Err(Box::new(ValidationError { + problem: "`size_of::()` is greater than \ + `indirect_buffer.size()`" + .into(), + vuids: &["VUID-vkCmdDispatchIndirect-offset-00407"], + ..Default::default() + })); + } + + // VUID-vkCmdDispatchIndirect-offset-02710 + // TODO: + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn dispatch_indirect_unchecked( + &mut self, + indirect_buffer: &Subbuffer<[DispatchIndirectCommand]>, ) -> &mut Self { let fns = self.device().fns(); (fns.v1_0.cmd_dispatch_indirect)( @@ -2178,14 +2827,49 @@ where self } - /// Calls `vkCmdDraw` on the builder. - #[inline] pub unsafe fn draw( &mut self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32, + ) -> Result<&mut Self, Box> { + self.validate_draw(vertex_count, instance_count, first_vertex, first_instance)?; + + Ok(self.draw_unchecked(vertex_count, instance_count, first_vertex, first_instance)) + } + + fn validate_draw( + &self, + _vertex_count: u32, + _instance_count: u32, + _first_vertex: u32, + _first_instance: u32, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdDraw-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn draw_unchecked( + &mut self, + vertex_count: u32, + instance_count: u32, + first_vertex: u32, + first_instance: u32, ) -> &mut Self { let fns = self.device().fns(); (fns.v1_0.cmd_draw)( @@ -2199,8 +2883,140 @@ where self } - /// Calls `vkCmdDrawIndexed` on the builder. - #[inline] + pub unsafe fn draw_indirect( + &mut self, + indirect_buffer: &Subbuffer<[DrawIndirectCommand]>, + draw_count: u32, + stride: u32, + ) -> Result<&mut Self, Box> { + self.validate_draw_indirect(indirect_buffer.as_bytes(), draw_count, stride)?; + + Ok(self.draw_indirect_unchecked(indirect_buffer, draw_count, stride)) + } + + fn validate_draw_indirect( + &self, + indirect_buffer: &Subbuffer<[u8]>, + draw_count: u32, + stride: u32, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdDrawIndirect-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + if !indirect_buffer + .buffer() + .usage() + .intersects(BufferUsage::INDIRECT_BUFFER) + { + return Err(Box::new(ValidationError { + context: "indirect_buffer.usage()".into(), + problem: "does not contain `BufferUsage::INDIRECT_BUFFER`".into(), + vuids: &["VUID-vkCmdDrawIndirect-buffer-02709"], + ..Default::default() + })); + } + + if draw_count > 1 { + if !self.device().enabled_features().multi_draw_indirect { + return Err(Box::new(ValidationError { + context: "draw_count".into(), + problem: "is greater than 1".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "multi_draw_indirect", + )])]), + vuids: &["VUID-vkCmdDrawIndirect-drawCount-02718"], + })); + } + + if stride % 4 != 0 { + return Err(Box::new(ValidationError { + problem: "`draw_count` is greater than 1, but \ + `stride` is not a multiple of 4" + .into(), + vuids: &["VUID-vkCmdDrawIndirect-drawCount-00476"], + ..Default::default() + })); + } + + if (stride as DeviceSize) < size_of::() as DeviceSize { + return Err(Box::new(ValidationError { + problem: "`draw_count` is greater than 1, but \ + `stride` is not greater than `size_of::()`" + .into(), + vuids: &["VUID-vkCmdDrawIndirect-drawCount-00476"], + ..Default::default() + })); + } + + if stride as DeviceSize * (draw_count as DeviceSize - 1) + + size_of::() as DeviceSize + > indirect_buffer.size() + { + return Err(Box::new(ValidationError { + problem: "`draw_count` is greater than 1, but \ + `stride * (draw_count - 1) + size_of::()` is \ + greater than `indirect_buffer.size()`" + .into(), + vuids: &["VUID-vkCmdDrawIndirect-drawCount-00488"], + ..Default::default() + })); + } + } else { + if size_of::() as DeviceSize > indirect_buffer.size() { + return Err(Box::new(ValidationError { + problem: "`draw_count` is 1, but `size_of::()` is \ + greater than `indirect_buffer.size()`" + .into(), + vuids: &["VUID-vkCmdDrawIndirect-drawCount-00487"], + ..Default::default() + })); + } + } + + let properties = self.device().physical_device().properties(); + + if draw_count > properties.max_draw_indirect_count { + return Err(Box::new(ValidationError { + context: "draw_count".into(), + problem: "is greater than the `max_draw_indirect_count` limit".into(), + vuids: &["VUID-vkCmdDrawIndirect-drawCount-02719"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn draw_indirect_unchecked( + &mut self, + indirect_buffer: &Subbuffer<[DrawIndirectCommand]>, + draw_count: u32, + stride: u32, + ) -> &mut Self { + let fns = self.device().fns(); + (fns.v1_0.cmd_draw_indirect)( + self.handle(), + indirect_buffer.buffer().handle(), + indirect_buffer.offset(), + draw_count, + stride, + ); + + self + } + pub unsafe fn draw_indexed( &mut self, index_count: u32, @@ -2208,6 +3024,57 @@ where first_index: u32, vertex_offset: i32, first_instance: u32, + ) -> Result<&mut Self, Box> { + self.validate_draw_indexed( + index_count, + instance_count, + first_index, + vertex_offset, + first_instance, + )?; + + Ok(self.draw_indexed_unchecked( + index_count, + instance_count, + first_index, + vertex_offset, + first_instance, + )) + } + + fn validate_draw_indexed( + &self, + _index_count: u32, + _instance_count: u32, + _first_index: u32, + _vertex_offset: i32, + _first_instance: u32, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdDrawIndexed-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn draw_indexed_unchecked( + &mut self, + index_count: u32, + instance_count: u32, + first_index: u32, + vertex_offset: i32, + first_instance: u32, ) -> &mut Self { let fns = self.device().fns(); (fns.v1_0.cmd_draw_indexed)( @@ -2222,29 +3089,123 @@ where self } - /// Calls `vkCmdDrawIndirect` on the builder. - #[inline] - pub unsafe fn draw_indirect( + pub unsafe fn draw_indexed_indirect( &mut self, - indirect_buffer: &Subbuffer<[DrawIndirectCommand]>, + indirect_buffer: &Subbuffer<[DrawIndexedIndirectCommand]>, draw_count: u32, stride: u32, - ) -> &mut Self { - let fns = self.device().fns(); - (fns.v1_0.cmd_draw_indirect)( - self.handle(), - indirect_buffer.buffer().handle(), - indirect_buffer.offset(), - draw_count, - stride, - ); + ) -> Result<&mut Self, Box> { + self.validate_draw_indexed_indirect(indirect_buffer.as_bytes(), draw_count, stride)?; - self + Ok(self.draw_indexed_indirect_unchecked(indirect_buffer, draw_count, stride)) } - /// Calls `vkCmdDrawIndexedIndirect` on the builder. - #[inline] - pub unsafe fn draw_indexed_indirect( + fn validate_draw_indexed_indirect( + &self, + indirect_buffer: &Subbuffer<[u8]>, + draw_count: u32, + stride: u32, + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdDrawIndexedIndirect-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + if !indirect_buffer + .buffer() + .usage() + .intersects(BufferUsage::INDIRECT_BUFFER) + { + return Err(Box::new(ValidationError { + context: "indirect_buffer.usage()".into(), + problem: "does not contain `BufferUsage::INDIRECT_BUFFER`".into(), + vuids: &["VUID-vkCmdDrawIndexedIndirect-buffer-02709"], + ..Default::default() + })); + } + + if draw_count > 1 { + if !self.device().enabled_features().multi_draw_indirect { + return Err(Box::new(ValidationError { + context: "draw_count".into(), + problem: "is greater than 1".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "multi_draw_indirect", + )])]), + vuids: &["VUID-vkCmdDrawIndexedIndirect-drawCount-02718"], + })); + } + + if stride % 4 != 0 { + return Err(Box::new(ValidationError { + problem: "`draw_count` is greater than 1, but \ + `stride` is not a multiple of 4" + .into(), + vuids: &["VUID-vkCmdDrawIndexedIndirect-drawCount-00528"], + ..Default::default() + })); + } + + if (stride as DeviceSize) < size_of::() as DeviceSize { + return Err(Box::new(ValidationError { + problem: "`draw_count` is greater than 1, but \ + `stride` is not greater than `size_of::()`" + .into(), + vuids: &["VUID-vkCmdDrawIndexedIndirect-drawCount-00528"], + ..Default::default() + })); + } + + if stride as DeviceSize * (draw_count as DeviceSize - 1) + + size_of::() as DeviceSize + > indirect_buffer.size() + { + return Err(Box::new(ValidationError { + problem: "`draw_count` is greater than 1, but \ + `stride * (draw_count - 1) + size_of::()` is \ + greater than `indirect_buffer.size()`" + .into(), + vuids: &["VUID-vkCmdDrawIndexedIndirect-drawCount-00540"], + ..Default::default() + })); + } + } else { + if size_of::() as DeviceSize > indirect_buffer.size() { + return Err(Box::new(ValidationError { + problem: "`draw_count` is 1, but `size_of::()` is \ + greater than `indirect_buffer.size()`" + .into(), + vuids: &["VUID-vkCmdDrawIndexedIndirect-drawCount-00539"], + ..Default::default() + })); + } + } + + let properties = self.device().physical_device().properties(); + + if draw_count > properties.max_draw_indirect_count { + return Err(Box::new(ValidationError { + context: "draw_count".into(), + problem: "is greater than the `max_draw_indirect_count` limit".into(), + vuids: &["VUID-vkCmdDrawIndexedIndirect-drawCount-02719"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn draw_indexed_indirect_unchecked( &mut self, indirect_buffer: &Subbuffer<[DrawIndexedIndirectCommand]>, draw_count: u32, @@ -2263,496 +3224,12 @@ where } } -/// Error that can happen when recording a bound pipeline execution command. -#[derive(Debug, Clone)] -pub enum PipelineExecutionError { - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, - - /// The resource bound to a descriptor set binding at a particular index is not compatible - /// with the requirements of the pipeline and shaders. - DescriptorResourceInvalid { - set_num: u32, - binding_num: u32, - index: u32, - error: DescriptorResourceInvalidError, - }, - - /// The pipeline layout requires a descriptor set bound to a set number, but none was bound. - DescriptorSetNotBound { set_num: u32 }, - - /// The bound pipeline uses a dynamic color write enable setting, but the number of provided - /// enable values is less than the number of attachments in the current render subpass. - DynamicColorWriteEnableNotEnoughValues { - color_write_enable_count: u32, - attachment_count: u32, - }, - - /// The bound pipeline uses a dynamic primitive topology, but the provided topology is of a - /// different topology class than what the pipeline requires. - DynamicPrimitiveTopologyClassMismatch { - provided_class: PrimitiveTopologyClass, - required_class: PrimitiveTopologyClass, - }, - - /// The bound pipeline uses a dynamic primitive topology, but the provided topology is not - /// compatible with the shader stages in the pipeline. - DynamicPrimitiveTopologyInvalid { topology: PrimitiveTopology }, - - /// The pipeline requires a particular dynamic state, but this state was not set. - DynamicStateNotSet { dynamic_state: DynamicState }, - - /// The bound pipeline uses a dynamic scissor and/or viewport count, but the scissor count - /// does not match the viewport count. - DynamicViewportScissorCountMismatch { - viewport_count: u32, - scissor_count: u32, - }, - - /// Operation forbidden inside a render pass. - ForbiddenInsideRenderPass, - - /// Operation forbidden outside a render pass. - ForbiddenOutsideRenderPass, - - /// Operation forbidden inside a render subpass with the specified contents. - ForbiddenWithSubpassContents { subpass_contents: SubpassContents }, - - /// An indexed draw command was recorded, but no index buffer was bound. - IndexBufferNotBound, - - /// The highest index to be drawn exceeds the available number of indices in the bound index - /// buffer. - IndexBufferRangeOutOfBounds { - highest_index: u32, - max_index_count: u32, - }, - - /// The `indirect_buffer` usage was not enabled on the indirect buffer. - IndirectBufferMissingUsage, - - /// The `max_compute_work_group_count` limit has been exceeded. - MaxComputeWorkGroupCountExceeded { requested: [u32; 3], max: [u32; 3] }, - - /// The `max_draw_indirect_count` limit has been exceeded. - MaxDrawIndirectCountExceeded { provided: u32, max: u32 }, - - /// The `max_multiview_instance_index` limit has been exceeded. - MaxMultiviewInstanceIndexExceeded { highest_instance: u64, max: u32 }, - - /// The queue family doesn't allow this operation. - NotSupportedByQueueFamily, - - /// The color attachment count in the bound pipeline does not match the count of the current - /// render pass. - PipelineColorAttachmentCountMismatch { - pipeline_count: u32, - required_count: u32, - }, - - /// The format of a color attachment in the bound pipeline does not match the format of the - /// corresponding color attachment in the current render pass. - PipelineColorAttachmentFormatMismatch { - color_attachment_index: u32, - pipeline_format: Option, - required_format: Format, - }, - - /// The format of the depth attachment in the bound pipeline does not match the format of the - /// depth attachment in the current render pass. - PipelineDepthAttachmentFormatMismatch { - pipeline_format: Option, - required_format: Format, - }, - - /// The bound pipeline is not compatible with the layout used to bind the descriptor sets. - PipelineLayoutNotCompatible, - - /// No pipeline was bound to the bind point used by the operation. - PipelineNotBound, - - /// The bound graphics pipeline uses a render pass that is not compatible with the currently - /// active render pass. - PipelineRenderPassNotCompatible, - - /// The bound graphics pipeline uses a render pass of a different type than the currently - /// active render pass. - PipelineRenderPassTypeMismatch, - - /// The bound graphics pipeline uses a render subpass index that doesn't match the currently - /// active subpass index. - PipelineSubpassMismatch { pipeline: u32, current: u32 }, - - /// The format of the stencil attachment in the bound pipeline does not match the format of the - /// stencil attachment in the current render pass. - PipelineStencilAttachmentFormatMismatch { - pipeline_format: Option, - required_format: Format, - }, - - /// The view mask of the bound pipeline does not match the view mask of the current render pass. - PipelineViewMaskMismatch { - pipeline_view_mask: u32, - required_view_mask: u32, - }, - - /// The push constants are not compatible with the pipeline layout. - PushConstantsNotCompatible, - - /// Not all push constants used by the pipeline have been set. - PushConstantsMissing, - - /// The bound graphics pipeline requires a vertex buffer bound to a binding number, but none - /// was bound. - VertexBufferNotBound { binding_num: u32 }, - - /// The number of instances to be drawn exceeds the available number of indices in the - /// bound vertex buffers used by the pipeline. - VertexBufferInstanceRangeOutOfBounds { - instances_needed: u64, - instances_in_buffers: u64, - }, - - /// The number of vertices to be drawn exceeds the lowest available number of vertices in the - /// bound vertex buffers used by the pipeline. - VertexBufferVertexRangeOutOfBounds { - vertices_needed: u64, - vertices_in_buffers: u64, - }, -} - -impl Error for PipelineExecutionError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::DescriptorResourceInvalid { error, .. } => Some(error), - _ => None, - } - } -} - -impl Display for PipelineExecutionError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::DescriptorResourceInvalid { - set_num, - binding_num, - index, - .. - } => write!( - f, - "the resource bound to descriptor set {} binding {} at index {} is not compatible \ - with the requirements of the pipeline and shaders", - set_num, binding_num, index, - ), - Self::DescriptorSetNotBound { set_num } => write!( - f, - "the pipeline layout requires a descriptor set bound to set number {}, but none \ - was bound", - set_num, - ), - Self::DynamicColorWriteEnableNotEnoughValues { - color_write_enable_count, - attachment_count, - } => write!( - f, - "the bound pipeline uses a dynamic color write enable setting, but the number of \ - provided enable values ({}) is less than the number of attachments in the current \ - render subpass ({})", - color_write_enable_count, attachment_count, - ), - Self::DynamicPrimitiveTopologyClassMismatch { - provided_class, - required_class, - } => write!( - f, - "The bound pipeline uses a dynamic primitive topology, but the provided topology \ - is of a different topology class ({:?}) than what the pipeline requires ({:?})", - provided_class, required_class, - ), - Self::DynamicPrimitiveTopologyInvalid { topology } => write!( - f, - "the bound pipeline uses a dynamic primitive topology, but the provided topology \ - ({:?}) is not compatible with the shader stages in the pipeline", - topology, - ), - Self::DynamicStateNotSet { dynamic_state } => write!( - f, - "the pipeline requires the dynamic state {:?}, but this state was not set", - dynamic_state, - ), - Self::DynamicViewportScissorCountMismatch { - viewport_count, - scissor_count, - } => write!( - f, - "the bound pipeline uses a dynamic scissor and/or viewport count, but the scissor \ - count ({}) does not match the viewport count ({})", - scissor_count, viewport_count, - ), - Self::ForbiddenInsideRenderPass => { - write!(f, "operation forbidden inside a render pass") - } - Self::ForbiddenOutsideRenderPass => { - write!(f, "operation forbidden outside a render pass") - } - Self::ForbiddenWithSubpassContents { subpass_contents } => write!( - f, - "operation forbidden inside a render subpass with contents {:?}", - subpass_contents, - ), - Self::IndexBufferNotBound => write!( - f, - "an indexed draw command was recorded, but no index buffer was bound", - ), - Self::IndexBufferRangeOutOfBounds { - highest_index, - max_index_count, - } => write!( - f, - "the highest index to be drawn ({}) exceeds the available number of indices in the \ - bound index buffer ({})", - highest_index, max_index_count, - ), - Self::IndirectBufferMissingUsage => write!( - f, - "the `indirect_buffer` usage was not enabled on the indirect buffer", - ), - Self::MaxComputeWorkGroupCountExceeded { .. } => write!( - f, - "the `max_compute_work_group_count` limit has been exceeded", - ), - Self::MaxDrawIndirectCountExceeded { .. } => { - write!(f, "the `max_draw_indirect_count` limit has been exceeded") - } - Self::MaxMultiviewInstanceIndexExceeded { .. } => write!( - f, - "the `max_multiview_instance_index` limit has been exceeded", - ), - Self::NotSupportedByQueueFamily => { - write!(f, "the queue family doesn't allow this operation") - } - Self::PipelineColorAttachmentCountMismatch { - pipeline_count, - required_count, - } => write!( - f, - "the color attachment count in the bound pipeline ({}) does not match the count of \ - the current render pass ({})", - pipeline_count, required_count, - ), - Self::PipelineColorAttachmentFormatMismatch { - color_attachment_index, - pipeline_format, - required_format, - } => write!( - f, - "the format of color attachment {} in the bound pipeline ({:?}) does not match the \ - format of the corresponding color attachment in the current render pass ({:?})", - color_attachment_index, pipeline_format, required_format, - ), - Self::PipelineDepthAttachmentFormatMismatch { - pipeline_format, - required_format, - } => write!( - f, - "the format of the depth attachment in the bound pipeline ({:?}) does not match \ - the format of the depth attachment in the current render pass ({:?})", - pipeline_format, required_format, - ), - Self::PipelineLayoutNotCompatible => write!( - f, - "the bound pipeline is not compatible with the layout used to bind the descriptor \ - sets", - ), - Self::PipelineNotBound => write!( - f, - "no pipeline was bound to the bind point used by the operation", - ), - Self::PipelineRenderPassNotCompatible => write!( - f, - "the bound graphics pipeline uses a render pass that is not compatible with the \ - currently active render pass", - ), - Self::PipelineRenderPassTypeMismatch => write!( - f, - "the bound graphics pipeline uses a render pass of a different type than the \ - currently active render pass", - ), - Self::PipelineSubpassMismatch { pipeline, current } => write!( - f, - "the bound graphics pipeline uses a render subpass index ({}) that doesn't match \ - the currently active subpass index ({})", - pipeline, current, - ), - Self::PipelineStencilAttachmentFormatMismatch { - pipeline_format, - required_format, - } => write!( - f, - "the format of the stencil attachment in the bound pipeline ({:?}) does not match \ - the format of the stencil attachment in the current render pass ({:?})", - pipeline_format, required_format, - ), - Self::PipelineViewMaskMismatch { - pipeline_view_mask, - required_view_mask, - } => write!( - f, - "the view mask of the bound pipeline ({}) does not match the view mask of the \ - current render pass ({})", - pipeline_view_mask, required_view_mask, - ), - Self::PushConstantsNotCompatible => write!( - f, - "the push constants are not compatible with the pipeline layout", - ), - Self::PushConstantsMissing => write!( - f, - "not all push constants used by the pipeline have been set", - ), - Self::VertexBufferNotBound { binding_num } => write!( - f, - "the bound graphics pipeline requires a vertex buffer bound to binding number {}, \ - but none was bound", - binding_num, - ), - Self::VertexBufferInstanceRangeOutOfBounds { - instances_needed, - instances_in_buffers, - } => write!( - f, - "the number of instances to be drawn ({}) exceeds the available number of \ - instances in the bound vertex buffers ({}) used by the pipeline", - instances_needed, instances_in_buffers, - ), - Self::VertexBufferVertexRangeOutOfBounds { - vertices_needed, - vertices_in_buffers, - } => write!( - f, - "the number of vertices to be drawn ({}) exceeds the available number of vertices \ - in the bound vertex buffers ({}) used by the pipeline", - vertices_needed, vertices_in_buffers, - ), - } - } -} - -#[derive(Clone, Debug)] -pub enum DescriptorResourceInvalidError { - ImageViewFormatMismatch { - required: Format, - provided: Format, - }, - ImageViewMultisampledMismatch { - required: bool, - provided: bool, - }, - ImageViewScalarTypeMismatch { - required: ShaderScalarType, - provided: ShaderScalarType, - }, - ImageViewTypeMismatch { - required: ImageViewType, - provided: ImageViewType, - }, - Missing, - SamplerCompareMismatch { - required: bool, - provided: bool, - }, - SamplerImageViewIncompatible { - image_view_set_num: u32, - image_view_binding_num: u32, - image_view_index: u32, - error: Box, - }, - SamplerUnnormalizedCoordinatesNotAllowed, - SamplerYcbcrConversionNotAllowed, - StorageImageAtomicNotSupported, - StorageReadWithoutFormatNotSupported, - StorageWriteWithoutFormatNotSupported, -} - -impl Error for DescriptorResourceInvalidError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::SamplerImageViewIncompatible { error, .. } => Some(error), - _ => None, - } - } -} - -impl Display for DescriptorResourceInvalidError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::ImageViewFormatMismatch { provided, required } => write!( - f, - "the format of the bound image view ({:?}) does not match what the pipeline \ - requires ({:?})", - provided, required, - ), - Self::ImageViewMultisampledMismatch { provided, required } => write!( - f, - "the multisampling of the bound image ({}) does not match what the pipeline \ - requires ({})", - provided, required, - ), - Self::ImageViewScalarTypeMismatch { provided, required } => write!( - f, - "the scalar type of the format and aspect of the bound image view ({:?}) does not \ - match what the pipeline requires ({:?})", - provided, required, - ), - Self::ImageViewTypeMismatch { provided, required } => write!( - f, - "the image view type of the bound image view ({:?}) does not match what the \ - pipeline requires ({:?})", - provided, required, - ), - Self::Missing => write!(f, "no resource was bound"), - Self::SamplerImageViewIncompatible { .. } => write!( - f, - "the bound sampler samples an image view that is not compatible with that sampler", - ), - Self::SamplerCompareMismatch { provided, required } => write!( - f, - "the depth comparison state of the bound sampler ({}) does not match what the \ - pipeline requires ({})", - provided, required, - ), - Self::SamplerUnnormalizedCoordinatesNotAllowed => write!( - f, - "the bound sampler is required to have unnormalized coordinates disabled", - ), - Self::SamplerYcbcrConversionNotAllowed => write!( - f, - "the bound sampler is required to have no attached sampler YCbCr conversion", - ), - Self::StorageImageAtomicNotSupported => write!( - f, - "the bound image view does not support the `storage_image_atomic` format feature", - ), - Self::StorageReadWithoutFormatNotSupported => write!( - f, - "the bound image view or buffer view does not support the \ - `storage_read_without_format` format feature", - ), - Self::StorageWriteWithoutFormatNotSupported => write!( - f, - "the bound image view or buffer view does not support the \ - `storage_write_without_format` format feature", - ), - } - } +#[derive(Clone, Copy)] +enum VUIDType { + Dispatch, + DispatchIndirect, + Draw, + DrawIndirect, + DrawIndexed, + DrawIndexedIndirect, } diff --git a/vulkano/src/command_buffer/commands/query.rs b/vulkano/src/command_buffer/commands/query.rs index 4fa287fd..e9960f94 100644 --- a/vulkano/src/command_buffer/commands/query.rs +++ b/vulkano/src/command_buffer/commands/query.rs @@ -18,14 +18,9 @@ use crate::{ device::{DeviceOwned, QueueFlags}, query::{QueryControlFlags, QueryPool, QueryResultElement, QueryResultFlags, QueryType}, sync::{PipelineStage, PipelineStageAccessFlags, PipelineStages}, - DeviceSize, RequirementNotMet, Requires, RequiresAllOf, RequiresOneOf, Version, VulkanObject, -}; -use std::{ - error::Error, - fmt::{Display, Error as FmtError, Formatter}, - ops::Range, - sync::Arc, + DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, Version, VulkanObject, }; +use std::{ops::Range, sync::Arc}; /// # Commands related to queries. impl AutoCommandBufferBuilder @@ -45,12 +40,10 @@ where query_pool: Arc, query: u32, flags: QueryControlFlags, - ) -> Result<&mut Self, QueryError> { + ) -> Result<&mut Self, Box> { self.validate_begin_query(&query_pool, query, flags)?; - self.begin_query_unchecked(query_pool, query, flags); - - Ok(self) + Ok(self.begin_query_unchecked(query_pool, query, flags)) } fn validate_begin_query( @@ -58,97 +51,35 @@ where query_pool: &QueryPool, query: u32, flags: QueryControlFlags, - ) -> Result<(), QueryError> { - let queue_family_properties = self.queue_family_properties(); + ) -> Result<(), Box> { + self.inner.validate_begin_query(query_pool, query, flags)?; - // VUID-vkCmdBeginQuery-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(QueryError::NotSupportedByQueueFamily); - } - - let device = self.device(); - - // VUID-vkCmdBeginQuery-flags-parameter - flags.validate_device(device)?; - - // VUID-vkCmdBeginQuery-commonparent - assert_eq!(device, query_pool.device()); - - // VUID-vkCmdBeginQuery-query-00802 - query_pool.query(query).ok_or(QueryError::OutOfRange)?; - - match query_pool.query_type() { - QueryType::Occlusion => { - // VUID-vkCmdBeginQuery-commandBuffer-cmdpool - // // VUID-vkCmdBeginQuery-queryType-00803 - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(QueryError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdBeginQuery-queryType-00800 - if flags.intersects(QueryControlFlags::PRECISE) - && !device.enabled_features().occlusion_query_precise - { - return Err(QueryError::RequirementNotMet { - required_for: "`flags` contains `QueryControlFlags::PRECISE`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "occlusion_query_precise", - )])]), - }); - } - } - QueryType::PipelineStatistics(statistic_flags) => { - // VUID-vkCmdBeginQuery-commandBuffer-cmdpool - // VUID-vkCmdBeginQuery-queryType-00804 - // VUID-vkCmdBeginQuery-queryType-00805 - if statistic_flags.is_compute() - && !queue_family_properties - .queue_flags - .intersects(QueueFlags::COMPUTE) - || statistic_flags.is_graphics() - && !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(QueryError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdBeginQuery-queryType-00800 - if flags.intersects(QueryControlFlags::PRECISE) { - return Err(QueryError::InvalidFlags); - } - } - // VUID-vkCmdBeginQuery-queryType-02804 - // VUID-vkCmdBeginQuery-queryType-04728 - // VUID-vkCmdBeginQuery-queryType-06741 - QueryType::Timestamp - | QueryType::AccelerationStructureCompactedSize - | QueryType::AccelerationStructureSerializationSize - | QueryType::AccelerationStructureSerializationBottomLevelPointers - | QueryType::AccelerationStructureSize => return Err(QueryError::NotPermitted), - } - - // VUID-vkCmdBeginQuery-queryPool-01922 if self .builder_state .queries .contains_key(&query_pool.query_type().into()) { - return Err(QueryError::QueryIsActive); + return Err(Box::new(ValidationError { + problem: "a query with the same type as `query_pool.query_type()` is \ + already active" + .into(), + vuids: &["VUID-vkCmdBeginQuery-queryPool-01922"], + ..Default::default() + })); } if let Some(render_pass_state) = &self.builder_state.render_pass { - // VUID-vkCmdBeginQuery-query-00808 if query + render_pass_state.rendering_info.view_mask.count_ones() > query_pool.query_count() { - return Err(QueryError::OutOfRangeMultiview); + return Err(Box::new(ValidationError { + problem: "a render subpass with a non-zero `view_mask` is active, but \ + `query` + the number of views in `view_mask` is greater than \ + `query_pool.query_count()`" + .into(), + vuids: &["VUID-vkCmdBeginQuery-query-00808"], + ..Default::default() + })); } } @@ -180,7 +111,7 @@ where "begin_query", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.begin_query(&query_pool, query, flags); + out.begin_query_unchecked(&query_pool, query, flags); }, ); @@ -192,33 +123,19 @@ where &mut self, query_pool: Arc, query: u32, - ) -> Result<&mut Self, QueryError> { + ) -> Result<&mut Self, Box> { self.validate_end_query(&query_pool, query)?; - unsafe { - self.end_query_unchecked(query_pool, query); - } - - Ok(self) + unsafe { Ok(self.end_query_unchecked(query_pool, query)) } } - fn validate_end_query(&self, query_pool: &QueryPool, query: u32) -> Result<(), QueryError> { - let queue_family_properties = self.queue_family_properties(); + fn validate_end_query( + &self, + query_pool: &QueryPool, + query: u32, + ) -> Result<(), Box> { + self.inner.validate_end_query(query_pool, query)?; - // VUID-vkCmdEndQuery-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(QueryError::NotSupportedByQueueFamily); - } - - let device = self.device(); - - // VUID-vkCmdEndQuery-commonparent - assert_eq!(device, query_pool.device()); - - // VUID-vkCmdEndQuery-None-01923 if !self .builder_state .queries @@ -227,18 +144,26 @@ where *state.query_pool == *query_pool && state.query == query }) { - return Err(QueryError::QueryNotActive); + return Err(Box::new(ValidationError { + problem: "no query with the same type as `query_pool.query_type()` is active" + .into(), + vuids: &["VUID-vkCmdEndQuery-None-01923"], + ..Default::default() + })); } - // VUID-vkCmdEndQuery-query-00810 - query_pool.query(query).ok_or(QueryError::OutOfRange)?; - if let Some(render_pass_state) = &self.builder_state.render_pass { - // VUID-vkCmdEndQuery-query-00812 if query + render_pass_state.rendering_info.view_mask.count_ones() > query_pool.query_count() { - return Err(QueryError::OutOfRangeMultiview); + return Err(Box::new(ValidationError { + problem: "a render subpass with a non-zero `view_mask` is active, but \ + `query` + the number of views in `view_mask` is greater than \ + `query_pool.query_count()`" + .into(), + vuids: &["VUID-vkCmdEndQuery-query-00812"], + ..Default::default() + })); } } @@ -258,7 +183,7 @@ where "end_query", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.end_query(&query_pool, query); + out.end_query_unchecked(&query_pool, query); }, ); @@ -276,12 +201,10 @@ where query_pool: Arc, query: u32, stage: PipelineStage, - ) -> Result<&mut Self, QueryError> { + ) -> Result<&mut Self, Box> { self.validate_write_timestamp(&query_pool, query, stage)?; - self.write_timestamp_unchecked(query_pool, query, stage); - - Ok(self) + Ok(self.write_timestamp_unchecked(query_pool, query, stage)) } fn validate_write_timestamp( @@ -289,184 +212,22 @@ where query_pool: &QueryPool, query: u32, stage: PipelineStage, - ) -> Result<(), QueryError> { - let device = self.device(); - - if !device.enabled_features().synchronization2 - && PipelineStages::from(stage).contains_flags2() - { - return Err(QueryError::RequirementNotMet { - required_for: "`stage` has flags set from `VkPipelineStageFlagBits2`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "synchronization2", - )])]), - }); - } - - // VUID-vkCmdWriteTimestamp2-stage-parameter - stage.validate_device(device)?; - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdWriteTimestamp2-commandBuffer-cmdpool - if !queue_family_properties.queue_flags.intersects( - QueueFlags::TRANSFER - | QueueFlags::GRAPHICS - | QueueFlags::COMPUTE - | QueueFlags::VIDEO_DECODE - | QueueFlags::VIDEO_ENCODE, - ) { - return Err(QueryError::NotSupportedByQueueFamily); - } - - let device = self.device(); - - // VUID-vkCmdWriteTimestamp2-commonparent - assert_eq!(device, query_pool.device()); - - // VUID-vkCmdWriteTimestamp2-stage-03860 - if !PipelineStages::from(queue_family_properties.queue_flags).contains_enum(stage) { - return Err(QueryError::StageNotSupported); - } - - match stage { - PipelineStage::GeometryShader => { - // VUID-vkCmdWriteTimestamp2-stage-03929 - if !device.enabled_features().geometry_shader { - return Err(QueryError::RequirementNotMet { - required_for: "`stage` is `PipelineStage::GeometryShader`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "geometry_shadere", - )])]), - }); - } - } - PipelineStage::TessellationControlShader - | PipelineStage::TessellationEvaluationShader => { - // VUID-vkCmdWriteTimestamp2-stage-03930 - if !device.enabled_features().tessellation_shader { - return Err(QueryError::RequirementNotMet { - required_for: "`stage` is `PipelineStage::TessellationControlShader` or \ - `PipelineStage::TessellationEvaluationShader`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "tessellation_shader", - )])]), - }); - } - } - PipelineStage::ConditionalRendering => { - // VUID-vkCmdWriteTimestamp2-stage-03931 - if !device.enabled_features().conditional_rendering { - return Err(QueryError::RequirementNotMet { - required_for: "`stage` is `PipelineStage::ConditionalRendering`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "conditional_rendering", - )])]), - }); - } - } - PipelineStage::FragmentDensityProcess => { - // VUID-vkCmdWriteTimestamp2-stage-03932 - if !device.enabled_features().fragment_density_map { - return Err(QueryError::RequirementNotMet { - required_for: "`stage` is `PipelineStage::FragmentDensityProcess`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "fragment_density_map", - )])]), - }); - } - } - PipelineStage::TransformFeedback => { - // VUID-vkCmdWriteTimestamp2-stage-03933 - if !device.enabled_features().transform_feedback { - return Err(QueryError::RequirementNotMet { - required_for: "`stage` is `PipelineStage::TransformFeedback`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "transform_feedback", - )])]), - }); - } - } - PipelineStage::MeshShader => { - // VUID-vkCmdWriteTimestamp2-stage-03934 - if !device.enabled_features().mesh_shader { - return Err(QueryError::RequirementNotMet { - required_for: "`stage` is `PipelineStage::MeshShader`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "mesh_shader", - )])]), - }); - } - } - PipelineStage::TaskShader => { - // VUID-vkCmdWriteTimestamp2-stage-03935 - if !device.enabled_features().task_shader { - return Err(QueryError::RequirementNotMet { - required_for: "`stage` is `PipelineStage::TaskShader`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "task_shader", - )])]), - }); - } - } - PipelineStage::FragmentShadingRateAttachment => { - // VUID-vkCmdWriteTimestamp2-shadingRateImage-07316 - if !(device.enabled_features().attachment_fragment_shading_rate - || device.enabled_features().shading_rate_image) - { - return Err(QueryError::RequirementNotMet { - required_for: "`stage` is `PipelineStage::FragmentShadingRateAttachment`", - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::Feature("attachment_fragment_shading_rate")]), - RequiresAllOf(&[Requires::Feature("shading_rate_image")]), - ]), - }); - } - } - PipelineStage::SubpassShading => { - // VUID-vkCmdWriteTimestamp2-stage-04957 - if !device.enabled_features().subpass_shading { - return Err(QueryError::RequirementNotMet { - required_for: "`stage` is `PipelineStage::SubpassShading`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "subpass_shading", - )])]), - }); - } - } - PipelineStage::InvocationMask => { - // VUID-vkCmdWriteTimestamp2-stage-04995 - if !device.enabled_features().invocation_mask { - return Err(QueryError::RequirementNotMet { - required_for: "`stage` is `PipelineStage::InvocationMask`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "invocation_mask", - )])]), - }); - } - } - _ => (), - } - - // VUID-vkCmdWriteTimestamp2-queryPool-03861 - if !matches!(query_pool.query_type(), QueryType::Timestamp) { - return Err(QueryError::NotPermitted); - } - - // VUID-vkCmdWriteTimestamp2-timestampValidBits-03863 - if queue_family_properties.timestamp_valid_bits.is_none() { - return Err(QueryError::NoTimestampValidBits); - } - - // VUID-vkCmdWriteTimestamp2-query-04903 - query_pool.query(query).ok_or(QueryError::OutOfRange)?; + ) -> Result<(), Box> { + self.inner + .validate_write_timestamp(query_pool, query, stage)?; if let Some(render_pass_state) = &self.builder_state.render_pass { - // VUID-vkCmdWriteTimestamp2-query-03865 if query + render_pass_state.rendering_info.view_mask.count_ones() > query_pool.query_count() { - return Err(QueryError::OutOfRangeMultiview); + return Err(Box::new(ValidationError { + problem: "a render subpass with a non-zero `view_mask` is active, but \ + `query` + the number of views in `view_mask` is greater than \ + `query_pool.query_count()`" + .into(), + vuids: &["VUID-vkCmdWriteTimestamp2-query-03865"], + ..Default::default() + })); } } @@ -489,7 +250,7 @@ where "write_timestamp", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.write_timestamp(&query_pool, query, stage); + out.write_timestamp_unchecked(&query_pool, query, stage); }, ); @@ -513,17 +274,15 @@ where queries: Range, destination: Subbuffer<[T]>, flags: QueryResultFlags, - ) -> Result<&mut Self, QueryError> + ) -> Result<&mut Self, Box> where T: QueryResultElement, { self.validate_copy_query_pool_results(&query_pool, queries.clone(), &destination, flags)?; unsafe { - self.copy_query_pool_results_unchecked(query_pool, queries, destination, flags); + Ok(self.copy_query_pool_results_unchecked(query_pool, queries, destination, flags)) } - - Ok(self) } fn validate_copy_query_pool_results( @@ -532,70 +291,19 @@ where queries: Range, destination: &Subbuffer<[T]>, flags: QueryResultFlags, - ) -> Result<(), QueryError> + ) -> Result<(), Box> where T: QueryResultElement, { - let queue_family_properties = self.queue_family_properties(); + self.inner + .validate_copy_query_pool_results(query_pool, queries, destination, flags)?; - // VUID-vkCmdCopyQueryPoolResults-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(QueryError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdCopyQueryPoolResults-renderpass if self.builder_state.render_pass.is_some() { - return Err(QueryError::ForbiddenInsideRenderPass); - } - - let device = self.device(); - - // VUID-vkCmdCopyQueryPoolResults-commonparent - assert_eq!(device, destination.buffer().device()); - assert_eq!(device, query_pool.device()); - - assert!(destination.len() > 0); - - // VUID-vkCmdCopyQueryPoolResults-flags-00822 - // VUID-vkCmdCopyQueryPoolResults-flags-00823 - debug_assert!(destination.offset() % std::mem::size_of::() as DeviceSize == 0); - - // VUID-vkCmdCopyQueryPoolResults-firstQuery-00820 - // VUID-vkCmdCopyQueryPoolResults-firstQuery-00821 - query_pool - .queries_range(queries.clone()) - .ok_or(QueryError::OutOfRange)?; - - let count = queries.end - queries.start; - let per_query_len = query_pool.query_type().result_len() - + flags.intersects(QueryResultFlags::WITH_AVAILABILITY) as DeviceSize; - let required_len = per_query_len * count as DeviceSize; - - // VUID-vkCmdCopyQueryPoolResults-dstBuffer-00824 - if destination.len() < required_len { - return Err(QueryError::BufferTooSmall { - required_len, - actual_len: destination.len(), - }); - } - - // VUID-vkCmdCopyQueryPoolResults-dstBuffer-00825 - if !destination - .buffer() - .usage() - .intersects(BufferUsage::TRANSFER_DST) - { - return Err(QueryError::DestinationMissingUsage); - } - - // VUID-vkCmdCopyQueryPoolResults-queryType-00827 - if matches!(query_pool.query_type(), QueryType::Timestamp) - && flags.intersects(QueryResultFlags::PARTIAL) - { - return Err(QueryError::InvalidFlags); + return Err(Box::new(ValidationError { + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdCopyQueryPoolResults-renderpass"], + ..Default::default() + })); } Ok(()) @@ -625,7 +333,12 @@ where .into_iter() .collect(), move |out: &mut UnsafeCommandBufferBuilder| { - out.copy_query_pool_results(&query_pool, queries.clone(), &destination, flags); + out.copy_query_pool_results_unchecked( + &query_pool, + queries.clone(), + &destination, + flags, + ); }, ); @@ -644,53 +357,39 @@ where &mut self, query_pool: Arc, queries: Range, - ) -> Result<&mut Self, QueryError> { + ) -> Result<&mut Self, Box> { self.validate_reset_query_pool(&query_pool, queries.clone())?; - self.reset_query_pool_unchecked(query_pool, queries); - - Ok(self) + Ok(self.reset_query_pool_unchecked(query_pool, queries)) } fn validate_reset_query_pool( &self, query_pool: &QueryPool, queries: Range, - ) -> Result<(), QueryError> { - // VUID-vkCmdResetQueryPool-renderpass + ) -> Result<(), Box> { + self.inner + .validate_reset_query_pool(query_pool, queries.clone())?; + if self.builder_state.render_pass.is_some() { - return Err(QueryError::ForbiddenInsideRenderPass); + return Err(Box::new(ValidationError { + problem: "a render pass instance is active".into(), + vuids: &["VUID-vkCmdResetQueryPool-renderpass"], + ..Default::default() + })); } - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdResetQueryPool-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(QueryError::NotSupportedByQueueFamily); - } - - let device = self.device(); - - // VUID-vkCmdResetQueryPool-commonparent - assert_eq!(device, query_pool.device()); - - // VUID-vkCmdResetQueryPool-firstQuery-00796 - // VUID-vkCmdResetQueryPool-firstQuery-00797 - query_pool - .queries_range(queries.clone()) - .ok_or(QueryError::OutOfRange)?; - - // VUID-vkCmdResetQueryPool-None-02841 if self .builder_state .queries .values() .any(|state| *state.query_pool == *query_pool && queries.contains(&state.query)) { - return Err(QueryError::QueryIsActive); + return Err(Box::new(ValidationError { + problem: "one of the `queries` in `query_pool` is currently active".into(), + vuids: &["VUID-vkCmdResetQueryPool-None-02841"], + ..Default::default() + })); } Ok(()) @@ -706,7 +405,7 @@ where "reset_query_pool", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.reset_query_pool(&query_pool, queries.clone()); + out.reset_query_pool_unchecked(&query_pool, queries.clone()); }, ); @@ -718,13 +417,162 @@ impl UnsafeCommandBufferBuilder where A: CommandBufferAllocator, { - /// Calls `vkCmdBeginQuery` on the builder. - #[inline] pub unsafe fn begin_query( &mut self, query_pool: &QueryPool, query: u32, flags: QueryControlFlags, + ) -> Result<&mut Self, Box> { + self.validate_begin_query(query_pool, query, flags)?; + + Ok(self.begin_query_unchecked(query_pool, query, flags)) + } + + fn validate_begin_query( + &self, + query_pool: &QueryPool, + query: u32, + flags: QueryControlFlags, + ) -> Result<(), Box> { + let queue_family_properties = self.queue_family_properties(); + + if !queue_family_properties.queue_flags.intersects( + QueueFlags::GRAPHICS + | QueueFlags::COMPUTE + | QueueFlags::VIDEO_DECODE + | QueueFlags::VIDEO_ENCODE, + ) { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics, compute, video decode or video encode operations" + .into(), + vuids: &["VUID-vkCmdBeginQuery-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + let device = self.device(); + + flags + .validate_device(device) + .map_err(|err| ValidationError { + context: "flags".into(), + vuids: &["VUID-vkCmdBeginQuery-flags-parameter"], + ..ValidationError::from_requirement(err) + })?; + + // VUID-vkCmdBeginQuery-commonparent + assert_eq!(device, query_pool.device()); + + if query > query_pool.query_count() { + return Err(Box::new(ValidationError { + problem: "`query` is greater than `query_pool.query_count()`".into(), + vuids: &["VUID-vkCmdBeginQuery-query-00802"], + ..Default::default() + })); + } + + match query_pool.query_type() { + QueryType::Occlusion => { + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "`query_pool.query_type()` is `QueryType::Occlusion`, but \ + the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdBeginQuery-queryType-00803"], + ..Default::default() + })); + } + } + QueryType::PipelineStatistics(statistic_flags) => { + if statistic_flags.is_graphics() + && !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + context: "query_pool.query_type()".into(), + problem: "is `QueryType::PipelineStatistics`, and the \ + pipeline statistics flags include a graphics flag, but \ + the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdBeginQuery-queryType-00804"], + ..Default::default() + })); + } + + if statistic_flags.is_compute() + && !queue_family_properties + .queue_flags + .intersects(QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + context: "query_pool.query_type()".into(), + problem: "is `QueryType::PipelineStatistics`, and the \ + pipeline statistics flags include a compute flag, but \ + the queue family of the command buffer does not support \ + compute operations" + .into(), + vuids: &["VUID-vkCmdBeginQuery-queryType-00805"], + ..Default::default() + })); + } + } + QueryType::Timestamp + | QueryType::AccelerationStructureCompactedSize + | QueryType::AccelerationStructureSerializationSize + | QueryType::AccelerationStructureSerializationBottomLevelPointers + | QueryType::AccelerationStructureSize => { + return Err(Box::new(ValidationError { + context: "query_pool.query_type()".into(), + problem: "is not allowed for this command".into(), + vuids: &[ + "VUID-vkCmdBeginQuery-queryType-02804", + "VUID-vkCmdBeginQuery-queryType-04728", + "VUID-vkCmdBeginQuery-queryType-06741", + ], + ..Default::default() + })); + } + } + + if flags.intersects(QueryControlFlags::PRECISE) { + if !device.enabled_features().occlusion_query_precise { + return Err(Box::new(ValidationError { + context: "flags".into(), + problem: "contains `QueryControlFlags::PRECISE`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "occlusion_query_precise", + )])]), + vuids: &["VUID-vkCmdBeginQuery-queryType-00800"], + })); + } + + if !matches!(query_pool.query_type(), QueryType::Occlusion) { + return Err(Box::new(ValidationError { + problem: "`flags` contains `QueryControlFlags::PRECISE`, but \ + `query_pool.query_type()` is not `QueryType::Occlusion`" + .into(), + vuids: &["VUID-vkCmdBeginQuery-queryType-00800"], + ..Default::default() + })); + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn begin_query_unchecked( + &mut self, + query_pool: &QueryPool, + query: u32, + flags: QueryControlFlags, ) -> &mut Self { let fns = self.device().fns(); (fns.v1_0.cmd_begin_query)(self.handle(), query_pool.handle(), query, flags.into()); @@ -732,22 +580,298 @@ where self } - /// Calls `vkCmdEndQuery` on the builder. - #[inline] - pub unsafe fn end_query(&mut self, query_pool: &QueryPool, query: u32) -> &mut Self { + pub unsafe fn end_query( + &mut self, + query_pool: &QueryPool, + query: u32, + ) -> Result<&mut Self, Box> { + self.validate_end_query(query_pool, query)?; + + Ok(self.end_query_unchecked(query_pool, query)) + } + + fn validate_end_query( + &self, + query_pool: &QueryPool, + query: u32, + ) -> Result<(), Box> { + let queue_family_properties = self.queue_family_properties(); + + if !queue_family_properties.queue_flags.intersects( + QueueFlags::GRAPHICS + | QueueFlags::COMPUTE + | QueueFlags::VIDEO_DECODE + | QueueFlags::VIDEO_ENCODE, + ) { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics, compute, video decode or video encode operations" + .into(), + vuids: &["VUID-vkCmdEndQuery-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + let device = self.device(); + + // VUID-vkCmdEndQuery-commonparent + assert_eq!(device, query_pool.device()); + + if query > query_pool.query_count() { + return Err(Box::new(ValidationError { + problem: "`query` is greater than `query_pool.query_count()`".into(), + vuids: &["VUID-vkCmdEndQuery-query-00810"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn end_query_unchecked(&mut self, query_pool: &QueryPool, query: u32) -> &mut Self { let fns = self.device().fns(); (fns.v1_0.cmd_end_query)(self.handle(), query_pool.handle(), query); self } - /// Calls `vkCmdWriteTimestamp` on the builder. - #[inline] pub unsafe fn write_timestamp( &mut self, query_pool: &QueryPool, query: u32, stage: PipelineStage, + ) -> Result<&mut Self, Box> { + self.validate_write_timestamp(query_pool, query, stage)?; + + Ok(self.write_timestamp_unchecked(query_pool, query, stage)) + } + + fn validate_write_timestamp( + &self, + query_pool: &QueryPool, + query: u32, + stage: PipelineStage, + ) -> Result<(), Box> { + let queue_family_properties = self.queue_family_properties(); + + if !queue_family_properties.queue_flags.intersects( + QueueFlags::TRANSFER + | QueueFlags::GRAPHICS + | QueueFlags::COMPUTE + | QueueFlags::VIDEO_DECODE + | QueueFlags::VIDEO_ENCODE, + ) { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + transfer, graphics, compute, video decode or video encode operations" + .into(), + vuids: &["VUID-vkCmdWriteTimestamp2-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + let device = self.device(); + + stage + .validate_device(device) + .map_err(|err| ValidationError { + context: "stage".into(), + vuids: &["VUID-vkCmdWriteTimestamp2-stage-parameter"], + ..ValidationError::from_requirement(err) + })?; + + if !device.enabled_features().synchronization2 + && PipelineStages::from(stage).contains_flags2() + { + return Err(Box::new(ValidationError { + context: "stage".into(), + problem: "is a stage flag from `VkPipelineStageFlagBits2`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "synchronization2", + )])]), + ..Default::default() + })); + } + + // VUID-vkCmdWriteTimestamp2-commonparent + assert_eq!(device, query_pool.device()); + + if !PipelineStages::from(queue_family_properties.queue_flags).contains_enum(stage) { + return Err(Box::new(ValidationError { + context: "stage".into(), + problem: "is not supported by the queue family of the command buffer".into(), + vuids: &["VUID-vkCmdWriteTimestamp2-stage-03860"], + ..Default::default() + })); + } + + match stage { + PipelineStage::GeometryShader => { + if !device.enabled_features().geometry_shader { + return Err(Box::new(ValidationError { + context: "stage".into(), + problem: "is `PipelineStage::GeometryShader`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "geometry_shadere", + )])]), + vuids: &["VUID-vkCmdWriteTimestamp2-stage-03929"], + })); + } + } + PipelineStage::TessellationControlShader + | PipelineStage::TessellationEvaluationShader => { + if !device.enabled_features().tessellation_shader { + return Err(Box::new(ValidationError { + context: "stage".into(), + problem: "is `PipelineStage::TessellationControlShader` or \ + `PipelineStage::TessellationEvaluationShader`" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "tessellation_shader", + )])]), + vuids: &["VUID-vkCmdWriteTimestamp2-stage-03930"], + })); + } + } + PipelineStage::ConditionalRendering => { + if !device.enabled_features().conditional_rendering { + return Err(Box::new(ValidationError { + context: "stage".into(), + problem: "is `PipelineStage::ConditionalRendering`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "conditional_rendering", + )])]), + vuids: &["VUID-vkCmdWriteTimestamp2-stage-03931"], + })); + } + } + PipelineStage::FragmentDensityProcess => { + if !device.enabled_features().fragment_density_map { + return Err(Box::new(ValidationError { + context: "stage".into(), + problem: "is `PipelineStage::FragmentDensityProcess`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "fragment_density_map", + )])]), + vuids: &["VUID-vkCmdWriteTimestamp2-stage-03932"], + })); + } + } + PipelineStage::TransformFeedback => { + if !device.enabled_features().transform_feedback { + return Err(Box::new(ValidationError { + context: "stage".into(), + problem: "is `PipelineStage::TransformFeedback`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "transform_feedback", + )])]), + vuids: &["VUID-vkCmdWriteTimestamp2-stage-03933"], + })); + } + } + PipelineStage::MeshShader => { + if !device.enabled_features().mesh_shader { + return Err(Box::new(ValidationError { + context: "stage".into(), + problem: "is `PipelineStage::MeshShader`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "mesh_shader", + )])]), + vuids: &["VUID-vkCmdWriteTimestamp2-stage-03934"], + })); + } + } + PipelineStage::TaskShader => { + if !device.enabled_features().task_shader { + return Err(Box::new(ValidationError { + context: "stage".into(), + problem: "is `PipelineStage::TaskShader`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "task_shader", + )])]), + vuids: &["VUID-vkCmdWriteTimestamp2-stage-03935"], + })); + } + } + PipelineStage::FragmentShadingRateAttachment => { + if !(device.enabled_features().attachment_fragment_shading_rate + || device.enabled_features().shading_rate_image) + { + return Err(Box::new(ValidationError { + context: "stage".into(), + problem: "is `PipelineStage::FragmentShadingRateAttachment`".into(), + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::Feature("attachment_fragment_shading_rate")]), + RequiresAllOf(&[Requires::Feature("shading_rate_image")]), + ]), + vuids: &["VUID-vkCmdWriteTimestamp2-shadingRateImage-07316"], + })); + } + } + PipelineStage::SubpassShading => { + if !device.enabled_features().subpass_shading { + return Err(Box::new(ValidationError { + context: "stage".into(), + problem: "is `PipelineStage::SubpassShading`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "subpass_shading", + )])]), + vuids: &["VUID-vkCmdWriteTimestamp2-stage-04957"], + })); + } + } + PipelineStage::InvocationMask => { + if !device.enabled_features().invocation_mask { + return Err(Box::new(ValidationError { + context: "stage".into(), + problem: "is `PipelineStage::InvocationMask`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "invocation_mask", + )])]), + vuids: &["VUID-vkCmdWriteTimestamp2-stage-04995"], + })); + } + } + _ => (), + } + + if !matches!(query_pool.query_type(), QueryType::Timestamp) { + return Err(Box::new(ValidationError { + context: "query_pool.query_type()".into(), + problem: "is not `QueryType::Timestamp`".into(), + vuids: &["VUID-vkCmdWriteTimestamp2-queryPool-03861"], + ..Default::default() + })); + } + + if queue_family_properties.timestamp_valid_bits.is_none() { + return Err(Box::new(ValidationError { + problem: "the `timestamp_valid_bits` value of the queue family properties of \ + the command buffer is `None`" + .into(), + vuids: &["VUID-vkCmdWriteTimestamp2-timestampValidBits-03863"], + ..Default::default() + })); + } + + if query > query_pool.query_count() { + return Err(Box::new(ValidationError { + problem: "`query` is greater than `query_pool.query_count()`".into(), + vuids: &["VUID-vkCmdWriteTimestamp2-query-04903"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn write_timestamp_unchecked( + &mut self, + query_pool: &QueryPool, + query: u32, + stage: PipelineStage, ) -> &mut Self { let fns = self.device().fns(); @@ -774,13 +898,123 @@ where self } - /// Calls `vkCmdCopyQueryPoolResults` on the builder. pub unsafe fn copy_query_pool_results( &mut self, query_pool: &QueryPool, queries: Range, destination: &Subbuffer<[T]>, flags: QueryResultFlags, + ) -> Result<&mut Self, Box> + where + T: QueryResultElement, + { + self.validate_copy_query_pool_results(query_pool, queries.clone(), destination, flags)?; + + Ok(self.copy_query_pool_results_unchecked(query_pool, queries, destination, flags)) + } + + fn validate_copy_query_pool_results( + &self, + query_pool: &QueryPool, + queries: Range, + destination: &Subbuffer<[T]>, + flags: QueryResultFlags, + ) -> Result<(), Box> + where + T: QueryResultElement, + { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics or compute operations" + .into(), + vuids: &["VUID-vkCmdCopyQueryPoolResults-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + let device = self.device(); + + // VUID-vkCmdCopyQueryPoolResults-commonparent + assert_eq!(device, destination.buffer().device()); + assert_eq!(device, query_pool.device()); + + // VUID-vkCmdCopyQueryPoolResults-flags-00822 + // VUID-vkCmdCopyQueryPoolResults-flags-00823 + debug_assert!(destination.offset() % std::mem::size_of::() as DeviceSize == 0); + + if queries.end < queries.start { + return Err(Box::new(ValidationError { + context: "queries".into(), + problem: "`end` is less than `start`".into(), + ..Default::default() + })); + } + + if queries.end > query_pool.query_count() { + return Err(Box::new(ValidationError { + problem: "`queries.end` is greater than `query_pool.query_count()`".into(), + vuids: &[ + "VUID-vkCmdCopyQueryPoolResults-firstQuery-00820", + "VUID-vkCmdCopyQueryPoolResults-firstQuery-00821", + ], + ..Default::default() + })); + } + + let count = queries.end - queries.start; + let per_query_len = query_pool.query_type().result_len() + + flags.intersects(QueryResultFlags::WITH_AVAILABILITY) as DeviceSize; + let required_len = per_query_len * count as DeviceSize; + + if destination.len() < required_len { + return Err(Box::new(ValidationError { + problem: "`destination` is smaller than the size required to write the results" + .into(), + vuids: &["VUID-vkCmdCopyQueryPoolResults-dstBuffer-00824"], + ..Default::default() + })); + } + + if !destination + .buffer() + .usage() + .intersects(BufferUsage::TRANSFER_DST) + { + return Err(Box::new(ValidationError { + context: "destination.usage()".into(), + problem: "does not contain `BufferUsage::TRANSFER_DST`".into(), + vuids: &["VUID-vkCmdCopyQueryPoolResults-dstBuffer-00825"], + ..Default::default() + })); + } + + if matches!(query_pool.query_type(), QueryType::Timestamp) + && flags.intersects(QueryResultFlags::PARTIAL) + { + return Err(Box::new(ValidationError { + problem: "`query_pool.query_type()` is `QueryType::Timestamp`, but \ + `flags` contains `QueryResultFlags::PARTIAL`" + .into(), + vuids: &["VUID-vkCmdCopyQueryPoolResults-queryType-00827"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn copy_query_pool_results_unchecked( + &mut self, + query_pool: &QueryPool, + queries: Range, + destination: &Subbuffer<[T]>, + flags: QueryResultFlags, ) -> &mut Self where T: QueryResultElement, @@ -804,12 +1038,71 @@ where self } - /// Calls `vkCmdResetQueryPool` on the builder. - #[inline] pub unsafe fn reset_query_pool( &mut self, query_pool: &QueryPool, queries: Range, + ) -> Result<&mut Self, Box> { + self.validate_reset_query_pool(query_pool, queries.clone())?; + + Ok(self.reset_query_pool_unchecked(query_pool, queries)) + } + + fn validate_reset_query_pool( + &self, + query_pool: &QueryPool, + queries: Range, + ) -> Result<(), Box> { + let queue_family_properties = self.queue_family_properties(); + + if !queue_family_properties.queue_flags.intersects( + QueueFlags::GRAPHICS + | QueueFlags::COMPUTE + | QueueFlags::VIDEO_DECODE + | QueueFlags::VIDEO_ENCODE + | QueueFlags::OPTICAL_FLOW, + ) { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics, compute, video decode, video encode or optical flow operations" + .into(), + vuids: &["VUID-vkCmdResetQueryPool-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + let device = self.device(); + + // VUID-vkCmdResetQueryPool-commonparent + assert_eq!(device, query_pool.device()); + + if queries.end < queries.start { + return Err(Box::new(ValidationError { + context: "queries".into(), + problem: "`end` is less than `start`".into(), + ..Default::default() + })); + } + + if queries.end > query_pool.query_count() { + return Err(Box::new(ValidationError { + problem: "`queries.end` is greater than `query_pool.query_count()`".into(), + vuids: &[ + "VUID-vkCmdResetQueryPool-firstQuery-00796", + "VUID-vkCmdResetQueryPool-firstQuery-00797", + ], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn reset_query_pool_unchecked( + &mut self, + query_pool: &QueryPool, + queries: Range, ) -> &mut Self { let fns = self.device().fns(); (fns.v1_0.cmd_reset_query_pool)( @@ -822,115 +1115,3 @@ where self } } - -/// Error that can happen when recording a query command. -#[derive(Clone, Debug)] -pub enum QueryError { - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, - - /// The buffer is too small for the copy operation. - BufferTooSmall { - /// Required number of elements in the buffer. - required_len: DeviceSize, - /// Actual number of elements in the buffer. - actual_len: DeviceSize, - }, - - /// The destination buffer is missing the `transfer_dst` usage. - DestinationMissingUsage, - - /// Operation forbidden inside of a render pass. - ForbiddenInsideRenderPass, - - /// The provided flags are not allowed for this type of query. - InvalidFlags, - - /// The queue family's `timestamp_valid_bits` value is `None`. - NoTimestampValidBits, - - /// This operation is not permitted on this query type. - NotPermitted, - - /// The queue family doesn't allow this operation. - NotSupportedByQueueFamily, - - /// The provided query index is not valid for this pool. - OutOfRange, - - /// The provided query index plus the number of views in the current render subpass is greater - /// than the number of queries in the pool. - OutOfRangeMultiview, - - /// A query is active that conflicts with the current operation. - QueryIsActive, - - /// This query was not active. - QueryNotActive, - - /// The provided stage is not supported by the queue family. - StageNotSupported, -} - -impl Error for QueryError {} - -impl Display for QueryError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::BufferTooSmall { .. } => { - write!(f, "the buffer is too small for the copy operation") - } - Self::DestinationMissingUsage => write!( - f, - "the destination buffer is missing the `transfer_dst` usage", - ), - Self::ForbiddenInsideRenderPass => { - write!(f, "operation forbidden inside of a render pass") - } - Self::InvalidFlags => write!( - f, - "the provided flags are not allowed for this type of query", - ), - Self::NoTimestampValidBits => { - write!(f, "the queue family's timestamp_valid_bits value is None") - } - Self::NotPermitted => write!(f, "this operation is not permitted on this query type"), - Self::NotSupportedByQueueFamily => { - write!(f, "the queue family doesn't allow this operation") - } - Self::OutOfRange => write!(f, "the provided query index is not valid for this pool"), - Self::OutOfRangeMultiview => write!( - f, - "the provided query index plus the number of views in the current render subpass \ - is greater than the number of queries in the pool", - ), - Self::QueryIsActive => write!( - f, - "a query is active that conflicts with the current operation" - ), - Self::QueryNotActive => write!(f, "this query was not active"), - Self::StageNotSupported => { - write!(f, "the provided stage is not supported by the queue family") - } - } - } -} - -impl From for QueryError { - fn from(err: RequirementNotMet) -> Self { - Self::RequirementNotMet { - required_for: err.required_for, - requires_one_of: err.requires_one_of, - } - } -} diff --git a/vulkano/src/command_buffer/commands/render_pass.rs b/vulkano/src/command_buffer/commands/render_pass.rs index 08c833cf..ce1278f6 100644 --- a/vulkano/src/command_buffer/commands/render_pass.rs +++ b/vulkano/src/command_buffer/commands/render_pass.rs @@ -15,27 +15,21 @@ use crate::{ RenderPassStateType, Resource, }, sys::UnsafeCommandBufferBuilder, - AutoCommandBufferBuilder, ResourceInCommand, SubpassContents, + AutoCommandBufferBuilder, CommandBufferLevel, ResourceInCommand, SubpassContents, }, - device::{DeviceOwned, QueueFlags}, - format::{ClearColorValue, ClearValue, Format, NumericType}, - image::{view::ImageView, ImageAspect, ImageAspects, ImageLayout, ImageUsage, SampleCount}, + device::{Device, DeviceOwned, QueueFlags}, + format::{ClearColorValue, ClearValue, NumericType}, + image::{view::ImageView, ImageAspects, ImageLayout, ImageUsage, SampleCount}, pipeline::graphics::subpass::PipelineRenderingCreateInfo, render_pass::{ AttachmentDescription, AttachmentLoadOp, AttachmentStoreOp, Framebuffer, RenderPass, ResolveMode, SubpassDescription, }, sync::PipelineStageAccessFlags, - RequirementNotMet, Requires, RequiresAllOf, RequiresOneOf, Version, VulkanObject, + Requires, RequiresAllOf, RequiresOneOf, ValidationError, Version, VulkanObject, }; use smallvec::SmallVec; -use std::{ - cmp::min, - error::Error, - fmt::{Display, Error as FmtError, Formatter}, - ops::Range, - sync::Arc, -}; +use std::{cmp::min, ops::Range, sync::Arc}; /// # Commands for render passes. /// @@ -53,354 +47,27 @@ where pub fn begin_render_pass( &mut self, render_pass_begin_info: RenderPassBeginInfo, - contents: SubpassContents, - ) -> Result<&mut Self, RenderPassError> { - self.validate_begin_render_pass(&render_pass_begin_info, contents)?; + subpass_begin_info: SubpassBeginInfo, + ) -> Result<&mut Self, Box> { + self.validate_begin_render_pass(&render_pass_begin_info, &subpass_begin_info)?; - unsafe { - self.begin_render_pass_unchecked(render_pass_begin_info, contents); - } - - Ok(self) + unsafe { Ok(self.begin_render_pass_unchecked(render_pass_begin_info, subpass_begin_info)) } } fn validate_begin_render_pass( &self, render_pass_begin_info: &RenderPassBeginInfo, - contents: SubpassContents, - ) -> Result<(), RenderPassError> { - let device = self.device(); + subpass_begin_info: &SubpassBeginInfo, + ) -> Result<(), Box> { + self.inner + .validate_begin_render_pass(render_pass_begin_info, subpass_begin_info)?; - // VUID-VkSubpassBeginInfo-contents-parameter - contents.validate_device(device)?; - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdBeginRenderPass2-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(RenderPassError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdBeginRenderPass2-renderpass if self.builder_state.render_pass.is_some() { - return Err(RenderPassError::ForbiddenInsideRenderPass); - } - - let RenderPassBeginInfo { - render_pass, - framebuffer, - render_area_offset, - render_area_extent, - clear_values, - _ne: _, - } = render_pass_begin_info; - - // VUID-VkRenderPassBeginInfo-commonparent - // VUID-vkCmdBeginRenderPass2-framebuffer-02779 - assert_eq!(device, framebuffer.device()); - - // VUID-VkRenderPassBeginInfo-renderPass-00904 - if !render_pass.is_compatible_with(framebuffer.render_pass()) { - return Err(RenderPassError::FramebufferNotCompatible); - } - - for i in 0..2 { - // VUID-VkRenderPassBeginInfo-pNext-02852 - // VUID-VkRenderPassBeginInfo-pNext-02853 - if render_area_offset[i] + render_area_extent[i] > framebuffer.extent()[i] { - return Err(RenderPassError::RenderAreaOutOfBounds); - } - } - - for (attachment_index, (attachment_desc, image_view)) in render_pass - .attachments() - .iter() - .zip(framebuffer.attachments()) - .enumerate() - { - let attachment_index = attachment_index as u32; - let &AttachmentDescription { - initial_layout, - final_layout, - stencil_initial_layout, - stencil_final_layout, - .. - } = attachment_desc; - - for layout in [ - Some(initial_layout), - Some(final_layout), - stencil_initial_layout, - stencil_final_layout, - ] - .into_iter() - .flatten() - { - match layout { - ImageLayout::ColorAttachmentOptimal => { - // VUID-vkCmdBeginRenderPass2-initialLayout-03094 - if !image_view.usage().intersects(ImageUsage::COLOR_ATTACHMENT) { - return Err(RenderPassError::AttachmentImageMissingUsage { - attachment_index, - usage: "color_attachment", - }); - } - } - ImageLayout::DepthReadOnlyStencilAttachmentOptimal - | ImageLayout::DepthAttachmentStencilReadOnlyOptimal - | ImageLayout::DepthStencilAttachmentOptimal - | ImageLayout::DepthStencilReadOnlyOptimal - | ImageLayout::DepthAttachmentOptimal - | ImageLayout::DepthReadOnlyOptimal - | ImageLayout::StencilAttachmentOptimal - | ImageLayout::StencilReadOnlyOptimal => { - // VUID-vkCmdBeginRenderPass2-initialLayout-03096 - // VUID-vkCmdBeginRenderPass2-initialLayout-02844 - if !image_view - .usage() - .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT) - { - return Err(RenderPassError::AttachmentImageMissingUsage { - attachment_index, - usage: "depth_stencil_attachment", - }); - } - } - ImageLayout::ShaderReadOnlyOptimal => { - // VUID-vkCmdBeginRenderPass2-initialLayout-03097 - if !image_view - .usage() - .intersects(ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT) - { - return Err(RenderPassError::AttachmentImageMissingUsage { - attachment_index, - usage: "sampled or input_attachment", - }); - } - } - ImageLayout::TransferSrcOptimal => { - // VUID-vkCmdBeginRenderPass2-initialLayout-03098 - if !image_view.usage().intersects(ImageUsage::TRANSFER_SRC) { - return Err(RenderPassError::AttachmentImageMissingUsage { - attachment_index, - usage: "transfer_src", - }); - } - } - ImageLayout::TransferDstOptimal => { - // VUID-vkCmdBeginRenderPass2-initialLayout-03099 - if !image_view.usage().intersects(ImageUsage::TRANSFER_DST) { - return Err(RenderPassError::AttachmentImageMissingUsage { - attachment_index, - usage: "transfer_dst", - }); - } - } - ImageLayout::Undefined - | ImageLayout::General - | ImageLayout::Preinitialized - | ImageLayout::PresentSrc => (), - } - } - } - - for subpass_desc in render_pass.subpasses() { - let SubpassDescription { - flags: _, - view_mask: _, - input_attachments, - color_attachments, - color_resolve_attachments, - depth_stencil_attachment, - depth_stencil_resolve_attachment, - depth_resolve_mode: _, - stencil_resolve_mode: _, - preserve_attachments: _, - _ne: _, - } = subpass_desc; - - for atch_ref in (input_attachments.iter().flatten()) - .chain(color_attachments.iter().flatten()) - .chain(color_resolve_attachments.iter().flatten()) - .chain(depth_stencil_attachment.iter()) - .chain(depth_stencil_resolve_attachment.iter()) - { - let image_view = &framebuffer.attachments()[atch_ref.attachment as usize]; - - match atch_ref.layout { - ImageLayout::ColorAttachmentOptimal => { - // VUID-vkCmdBeginRenderPass2-initialLayout-03094 - if !image_view.usage().intersects(ImageUsage::COLOR_ATTACHMENT) { - return Err(RenderPassError::AttachmentImageMissingUsage { - attachment_index: atch_ref.attachment, - usage: "color_attachment", - }); - } - } - ImageLayout::DepthReadOnlyStencilAttachmentOptimal - | ImageLayout::DepthAttachmentStencilReadOnlyOptimal - | ImageLayout::DepthStencilAttachmentOptimal - | ImageLayout::DepthStencilReadOnlyOptimal - | ImageLayout::DepthAttachmentOptimal - | ImageLayout::DepthReadOnlyOptimal - | ImageLayout::StencilAttachmentOptimal - | ImageLayout::StencilReadOnlyOptimal => { - // VUID-vkCmdBeginRenderPass2-initialLayout-03096 - // VUID-vkCmdBeginRenderPass2-initialLayout-02844 - if !image_view - .usage() - .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT) - { - return Err(RenderPassError::AttachmentImageMissingUsage { - attachment_index: atch_ref.attachment, - usage: "depth_stencil_attachment", - }); - } - } - ImageLayout::ShaderReadOnlyOptimal => { - // VUID-vkCmdBeginRenderPass2-initialLayout-03097 - if !image_view - .usage() - .intersects(ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT) - { - return Err(RenderPassError::AttachmentImageMissingUsage { - attachment_index: atch_ref.attachment, - usage: "sampled or input_attachment", - }); - } - } - ImageLayout::TransferSrcOptimal => { - // VUID-vkCmdBeginRenderPass2-initialLayout-03098 - if !image_view.usage().intersects(ImageUsage::TRANSFER_SRC) { - return Err(RenderPassError::AttachmentImageMissingUsage { - attachment_index: atch_ref.attachment, - usage: "transfer_src", - }); - } - } - ImageLayout::TransferDstOptimal => { - // VUID-vkCmdBeginRenderPass2-initialLayout-03099 - if !image_view.usage().intersects(ImageUsage::TRANSFER_DST) { - return Err(RenderPassError::AttachmentImageMissingUsage { - attachment_index: atch_ref.attachment, - usage: "transfer_dst", - }); - } - } - ImageLayout::Undefined - | ImageLayout::General - | ImageLayout::Preinitialized - | ImageLayout::PresentSrc => (), - } - } - } - - // VUID-VkRenderPassBeginInfo-clearValueCount-00902 - if clear_values.len() < render_pass.attachments().len() { - return Err(RenderPassError::ClearValueMissing { - attachment_index: clear_values.len() as u32, - }); - } - - // VUID-VkRenderPassBeginInfo-clearValueCount-04962 - for (attachment_index, (attachment_desc, &clear_value)) in render_pass - .attachments() - .iter() - .zip(clear_values) - .enumerate() - { - let attachment_index = attachment_index as u32; - let attachment_format = attachment_desc.format; - - if attachment_desc.load_op == AttachmentLoadOp::Clear - || attachment_desc - .stencil_load_op - .unwrap_or(attachment_desc.load_op) - == AttachmentLoadOp::Clear - { - let clear_value = match clear_value { - Some(x) => x, - None => return Err(RenderPassError::ClearValueMissing { attachment_index }), - }; - - if let (Some(numeric_type), AttachmentLoadOp::Clear) = - (attachment_format.type_color(), attachment_desc.load_op) - { - match numeric_type { - NumericType::SFLOAT - | NumericType::UFLOAT - | NumericType::SNORM - | NumericType::UNORM - | NumericType::SSCALED - | NumericType::USCALED - | NumericType::SRGB => { - if !matches!(clear_value, ClearValue::Float(_)) { - return Err(RenderPassError::ClearValueNotCompatible { - clear_value, - attachment_index, - attachment_format, - }); - } - } - NumericType::SINT => { - if !matches!(clear_value, ClearValue::Int(_)) { - return Err(RenderPassError::ClearValueNotCompatible { - clear_value, - attachment_index, - attachment_format, - }); - } - } - NumericType::UINT => { - if !matches!(clear_value, ClearValue::Uint(_)) { - return Err(RenderPassError::ClearValueNotCompatible { - clear_value, - attachment_index, - attachment_format, - }); - } - } - } - } else { - let attachment_aspects = attachment_format.aspects(); - let need_depth = attachment_aspects.intersects(ImageAspects::DEPTH) - && attachment_desc.load_op == AttachmentLoadOp::Clear; - let need_stencil = attachment_aspects.intersects(ImageAspects::STENCIL) - && attachment_desc - .stencil_load_op - .unwrap_or(attachment_desc.load_op) - == AttachmentLoadOp::Clear; - - if need_depth && need_stencil { - if !matches!(clear_value, ClearValue::DepthStencil(_)) { - return Err(RenderPassError::ClearValueNotCompatible { - clear_value, - attachment_index, - attachment_format, - }); - } - } else if need_depth { - if !matches!(clear_value, ClearValue::Depth(_)) { - return Err(RenderPassError::ClearValueNotCompatible { - clear_value, - attachment_index, - attachment_format, - }); - } - } else if need_stencil { - if !matches!(clear_value, ClearValue::Stencil(_)) { - return Err(RenderPassError::ClearValueNotCompatible { - clear_value, - attachment_index, - attachment_format, - }); - } - } - } - } + return Err(Box::new(ValidationError { + problem: "a render pass instance is already active".into(), + vuids: &["VUID-vkCmdBeginRenderPass2-renderpass"], + ..Default::default() + })); } // VUID-vkCmdBeginRenderPass2-initialLayout-03100 @@ -423,7 +90,7 @@ where pub unsafe fn begin_render_pass_unchecked( &mut self, render_pass_begin_info: RenderPassBeginInfo, - contents: SubpassContents, + subpass_begin_info: SubpassBeginInfo, ) -> &mut Self { let &RenderPassBeginInfo { ref render_pass, @@ -436,7 +103,7 @@ where let subpass = render_pass.clone().first_subpass(); self.builder_state.render_pass = Some(RenderPassState { - contents, + contents: subpass_begin_info.contents, render_area_offset, render_area_extent, @@ -483,7 +150,7 @@ where }) .collect(), move |out: &mut UnsafeCommandBufferBuilder| { - out.begin_render_pass(&render_pass_begin_info, contents); + out.begin_render_pass_unchecked(&render_pass_begin_info, &subpass_begin_info); }, ); @@ -493,68 +160,75 @@ where /// Advances to the next subpass of the render pass previously begun with `begin_render_pass`. pub fn next_subpass( &mut self, - contents: SubpassContents, - ) -> Result<&mut Self, RenderPassError> { - self.validate_next_subpass(contents)?; + subpass_end_info: SubpassEndInfo, + subpass_begin_info: SubpassBeginInfo, + ) -> Result<&mut Self, Box> { + self.validate_next_subpass(&subpass_end_info, &subpass_begin_info)?; - unsafe { - self.next_subpass_unchecked(contents); - } - - Ok(self) + unsafe { Ok(self.next_subpass_unchecked(subpass_end_info, subpass_begin_info)) } } - fn validate_next_subpass(&self, contents: SubpassContents) -> Result<(), RenderPassError> { - let device = self.device(); + fn validate_next_subpass( + &self, + subpass_end_info: &SubpassEndInfo, + subpass_begin_info: &SubpassBeginInfo, + ) -> Result<(), Box> { + self.inner + .validate_next_subpass(subpass_end_info, subpass_begin_info)?; - // VUID-VkSubpassBeginInfo-contents-parameter - contents.validate_device(device)?; - - // VUID-vkCmdNextSubpass2-renderpass - let render_pass_state = self - .builder_state - .render_pass - .as_ref() - .ok_or(RenderPassError::ForbiddenOutsideRenderPass)?; + let render_pass_state = + self.builder_state + .render_pass + .as_ref() + .ok_or(Box::new(ValidationError { + problem: "a render pass instance is not active".into(), + vuids: &["VUID-vkCmdNextSubpass2-renderpass"], + ..Default::default() + }))?; let begin_render_pass_state = match &render_pass_state.render_pass { RenderPassStateType::BeginRenderPass(state) => state, RenderPassStateType::BeginRendering(_) => { - return Err(RenderPassError::ForbiddenWithBeginRendering) + return Err(Box::new(ValidationError { + problem: "the current render pass instance was not begun with \ + `begin_render_pass`" + .into(), + // vuids? + ..Default::default() + })); } }; - // VUID-vkCmdNextSubpass2-None-03102 if begin_render_pass_state.subpass.is_last_subpass() { - return Err(RenderPassError::NoSubpassesRemaining { - current_subpass: begin_render_pass_state.subpass.index(), - }); + return Err(Box::new(ValidationError { + problem: "the current subpass is the last subpass of the render pass".into(), + vuids: &["VUID-vkCmdNextSubpass2-None-03102"], + ..Default::default() + })); } - // VUID? if self .builder_state .queries .values() .any(|state| state.in_subpass) { - return Err(RenderPassError::QueryIsActive); + return Err(Box::new(ValidationError { + problem: "a query that was begun in the current subpass is still active".into(), + // vuids? + ..Default::default() + })); } - // VUID-vkCmdNextSubpass2-commandBuffer-cmdpool - debug_assert!(self - .queue_family_properties() - .queue_flags - .intersects(QueueFlags::GRAPHICS)); - - // VUID-vkCmdNextSubpass2-bufferlevel - // Ensured by the type of the impl block - Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] - pub unsafe fn next_subpass_unchecked(&mut self, contents: SubpassContents) -> &mut Self { + pub unsafe fn next_subpass_unchecked( + &mut self, + subpass_end_info: SubpassEndInfo, + subpass_begin_info: SubpassBeginInfo, + ) -> &mut Self { let render_pass_state = self.builder_state.render_pass.as_mut().unwrap(); let begin_render_pass_state = match &mut render_pass_state.render_pass { RenderPassStateType::BeginRenderPass(x) => x, @@ -562,7 +236,7 @@ where }; begin_render_pass_state.subpass.next_subpass(); - render_pass_state.contents = contents; + render_pass_state.contents = subpass_begin_info.contents; render_pass_state.rendering_info = PipelineRenderingCreateInfo::from_subpass(&begin_render_pass_state.subpass); render_pass_state.attachments = Some(RenderPassStateAttachments::from_subpass( @@ -580,7 +254,7 @@ where "next_subpass", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.next_subpass(contents); + out.next_subpass_unchecked(&subpass_end_info, &subpass_begin_info); }, ); @@ -590,75 +264,80 @@ where /// Ends the render pass previously begun with `begin_render_pass`. /// /// This must be called after you went through all the subpasses. - pub fn end_render_pass(&mut self) -> Result<&mut Self, RenderPassError> { - self.validate_end_render_pass()?; + pub fn end_render_pass( + &mut self, + subpass_end_info: SubpassEndInfo, + ) -> Result<&mut Self, Box> { + self.validate_end_render_pass(&subpass_end_info)?; - unsafe { - self.end_render_pass_unchecked(); - } - - Ok(self) + unsafe { Ok(self.end_render_pass_unchecked(subpass_end_info)) } } - fn validate_end_render_pass(&self) -> Result<(), RenderPassError> { - // VUID-vkCmdEndRenderPass2-renderpass - let render_pass_state = self - .builder_state - .render_pass - .as_ref() - .ok_or(RenderPassError::ForbiddenOutsideRenderPass)?; + fn validate_end_render_pass( + &self, + subpass_end_info: &SubpassEndInfo, + ) -> Result<(), Box> { + self.inner.validate_end_render_pass(subpass_end_info)?; + + let render_pass_state = + self.builder_state + .render_pass + .as_ref() + .ok_or(Box::new(ValidationError { + problem: "a render pass instance is not active".into(), + vuids: &["VUID-vkCmdEndRenderPass2-renderpass"], + ..Default::default() + }))?; let begin_render_pass_state = match &render_pass_state.render_pass { RenderPassStateType::BeginRenderPass(state) => state, RenderPassStateType::BeginRendering(_) => { - return Err(RenderPassError::ForbiddenWithBeginRendering) + return Err(Box::new(ValidationError { + problem: "the current render pass instance was not begun with \ + `begin_render_pass`" + .into(), + vuids: &["VUID-vkCmdEndRenderPass2-None-06171"], + ..Default::default() + })); } }; - // VUID-vkCmdEndRenderPass2-None-03103 if !begin_render_pass_state.subpass.is_last_subpass() { - return Err(RenderPassError::SubpassesRemaining { - current_subpass: begin_render_pass_state.subpass.index(), - remaining_subpasses: begin_render_pass_state - .subpass - .render_pass() - .subpasses() - .len() as u32 - - begin_render_pass_state.subpass.index(), - }); + return Err(Box::new(ValidationError { + problem: "the current subpass is not the last subpass of the render pass".into(), + vuids: &["VUID-vkCmdEndRenderPass2-None-03103"], + ..Default::default() + })); } - // VUID? if self .builder_state .queries .values() .any(|state| state.in_subpass) { - return Err(RenderPassError::QueryIsActive); + return Err(Box::new(ValidationError { + problem: "a query that was begun in the current subpass is still active".into(), + vuids: &["VUID-vkCmdEndRenderPass2-None-07005"], + ..Default::default() + })); } - // VUID-vkCmdEndRenderPass2-commandBuffer-cmdpool - debug_assert!(self - .queue_family_properties() - .queue_flags - .intersects(QueueFlags::GRAPHICS)); - - // VUID-vkCmdEndRenderPass2-bufferlevel - // Ensured by the type of the impl block - Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] - pub unsafe fn end_render_pass_unchecked(&mut self) -> &mut Self { + pub unsafe fn end_render_pass_unchecked( + &mut self, + subpass_end_info: SubpassEndInfo, + ) -> &mut Self { self.builder_state.render_pass = None; self.add_render_pass_end( "end_render_pass", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.end_render_pass(); + out.end_render_pass_unchecked(&subpass_end_info); }, ); @@ -676,655 +355,37 @@ where pub fn begin_rendering( &mut self, mut rendering_info: RenderingInfo, - ) -> Result<&mut Self, RenderPassError> { - rendering_info.set_extent_layers()?; + ) -> Result<&mut Self, Box> { + rendering_info.set_auto_extent_layers(); self.validate_begin_rendering(&rendering_info)?; - unsafe { - self.begin_rendering_unchecked(rendering_info); - } - - Ok(self) + unsafe { Ok(self.begin_rendering_unchecked(rendering_info)) } } fn validate_begin_rendering( &self, rendering_info: &RenderingInfo, - ) -> Result<(), RenderPassError> { - let device = self.device(); - let properties = device.physical_device().properties(); + ) -> Result<(), Box> { + self.inner.validate_begin_rendering(rendering_info)?; - // VUID-vkCmdBeginRendering-dynamicRendering-06446 - if !device.enabled_features().dynamic_rendering { - return Err(RenderPassError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::begin_rendering`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "dynamic_rendering", - )])]), - }); - } - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdBeginRendering-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::GRAPHICS) - { - return Err(RenderPassError::NotSupportedByQueueFamily); - } - - // VUID-vkCmdBeginRendering-renderpass if self.builder_state.render_pass.is_some() { - return Err(RenderPassError::ForbiddenInsideRenderPass); - } - - let &RenderingInfo { - render_area_offset, - render_area_extent, - layer_count, - view_mask, - ref color_attachments, - ref depth_attachment, - ref stencil_attachment, - contents, - _ne: _, - } = rendering_info; - - // VUID-VkRenderingInfo-flags-parameter - contents.validate_device(device)?; - - // VUID-vkCmdBeginRendering-commandBuffer-06068 - if self.inner.inheritance_info().is_some() - && contents == SubpassContents::SecondaryCommandBuffers - { - return Err(RenderPassError::ContentsForbiddenInSecondaryCommandBuffer); - } - - // No VUID, but for sanity it makes sense to treat this the same as in framebuffers. - if view_mask != 0 && layer_count != 1 { - return Err(RenderPassError::MultiviewLayersInvalid); - } - - // VUID-VkRenderingInfo-multiview-06127 - if view_mask != 0 && !device.enabled_features().multiview { - return Err(RenderPassError::RequirementNotMet { - required_for: "`rendering_info.viewmask` is not `0`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature("multiview")])]), - }); - } - - let view_count = u32::BITS - view_mask.leading_zeros(); - - // VUID-VkRenderingInfo-viewMask-06128 - if view_count > properties.max_multiview_view_count.unwrap_or(0) { - return Err(RenderPassError::MaxMultiviewViewCountExceeded { - view_count, - max: properties.max_multiview_view_count.unwrap_or(0), - }); - } - - let mut samples = None; - - // VUID-VkRenderingInfo-colorAttachmentCount-06106 - if color_attachments.len() > properties.max_color_attachments as usize { - return Err(RenderPassError::MaxColorAttachmentsExceeded { - color_attachment_count: color_attachments.len() as u32, - max: properties.max_color_attachments, - }); - } - - for (attachment_index, attachment_info) in - color_attachments - .iter() - .enumerate() - .filter_map(|(index, attachment_info)| { - attachment_info - .as_ref() - .map(|attachment_info| (index, attachment_info)) - }) - { - let attachment_index = attachment_index as u32; - let RenderingAttachmentInfo { - image_view, - image_layout, - resolve_info, - load_op, - store_op, - clear_value: _, - _ne: _, - } = attachment_info; - - // VUID-VkRenderingAttachmentInfo-imageLayout-parameter - image_layout.validate_device(device)?; - - // VUID-VkRenderingAttachmentInfo-loadOp-parameter - load_op.validate_device(device)?; - - // VUID-VkRenderingAttachmentInfo-storeOp-parameter - store_op.validate_device(device)?; - - // VUID-VkRenderingInfo-colorAttachmentCount-06087 - if !image_view.usage().intersects(ImageUsage::COLOR_ATTACHMENT) { - return Err(RenderPassError::ColorAttachmentMissingUsage { attachment_index }); - } - - let image = image_view.image(); - let image_extent = image.extent(); - - for i in 0..2 { - // VUID-VkRenderingInfo-pNext-06079 - // VUID-VkRenderingInfo-pNext-06080 - if render_area_offset[i] + render_area_extent[i] > image_extent[i] { - return Err(RenderPassError::RenderAreaOutOfBounds); - } - } - - // VUID-VkRenderingInfo-imageView-06070 - match samples { - Some(samples) if samples == image.samples() => (), - Some(_) => { - return Err(RenderPassError::ColorAttachmentSamplesMismatch { - attachment_index, - }); - } - None => samples = Some(image.samples()), - } - - // VUID-VkRenderingAttachmentInfo-imageView-06135 - // VUID-VkRenderingAttachmentInfo-imageView-06145 - if matches!( - image_layout, - ImageLayout::Undefined - | ImageLayout::ShaderReadOnlyOptimal - | ImageLayout::TransferSrcOptimal - | ImageLayout::TransferDstOptimal - | ImageLayout::Preinitialized - | ImageLayout::PresentSrc - ) { - return Err(RenderPassError::ColorAttachmentLayoutInvalid { attachment_index }); - } - - // VUID-VkRenderingInfo-colorAttachmentCount-06090 - // VUID-VkRenderingInfo-colorAttachmentCount-06096 - // VUID-VkRenderingInfo-colorAttachmentCount-06100 - if matches!( - image_layout, - ImageLayout::DepthStencilAttachmentOptimal - | ImageLayout::DepthStencilReadOnlyOptimal - | ImageLayout::DepthReadOnlyStencilAttachmentOptimal - | ImageLayout::DepthAttachmentStencilReadOnlyOptimal - | ImageLayout::DepthAttachmentOptimal - | ImageLayout::DepthReadOnlyOptimal - | ImageLayout::StencilAttachmentOptimal - | ImageLayout::StencilReadOnlyOptimal - ) { - return Err(RenderPassError::ColorAttachmentLayoutInvalid { attachment_index }); - } - - if let Some(resolve_info) = resolve_info { - let &RenderingAttachmentResolveInfo { - mode, - image_view: ref resolve_image_view, - image_layout: resolve_image_layout, - } = resolve_info; - - // VUID-VkRenderingAttachmentInfo-resolveImageLayout-parameter - resolve_image_layout.validate_device(device)?; - - // VUID-VkRenderingAttachmentInfo-resolveMode-parameter - mode.validate_device(device)?; - - let resolve_image = resolve_image_view.image(); - - match image_view.format().type_color().unwrap() { - NumericType::SFLOAT - | NumericType::UFLOAT - | NumericType::SNORM - | NumericType::UNORM - | NumericType::SSCALED - | NumericType::USCALED - | NumericType::SRGB => { - // VUID-VkRenderingAttachmentInfo-imageView-06129 - if mode != ResolveMode::Average { - return Err(RenderPassError::ColorAttachmentResolveModeNotSupported { - attachment_index, - }); - } - } - NumericType::SINT | NumericType::UINT => { - // VUID-VkRenderingAttachmentInfo-imageView-06130 - if mode != ResolveMode::SampleZero { - return Err(RenderPassError::ColorAttachmentResolveModeNotSupported { - attachment_index, - }); - } - } - } - - // VUID-VkRenderingAttachmentInfo-imageView-06132 - if image.samples() == SampleCount::Sample1 { - return Err(RenderPassError::ColorAttachmentWithResolveNotMultisampled { - attachment_index, - }); - } - - // VUID-VkRenderingAttachmentInfo-imageView-06133 - if resolve_image.samples() != SampleCount::Sample1 { - return Err(RenderPassError::ColorAttachmentResolveMultisampled { - attachment_index, - }); - } - - // VUID-VkRenderingAttachmentInfo-imageView-06134 - if image_view.format() != resolve_image_view.format() { - return Err(RenderPassError::ColorAttachmentResolveFormatMismatch { - attachment_index, - }); - } - - // VUID-VkRenderingAttachmentInfo-imageView-06136 - // VUID-VkRenderingAttachmentInfo-imageView-06146 - if matches!( - resolve_image_layout, - ImageLayout::Undefined - | ImageLayout::ShaderReadOnlyOptimal - | ImageLayout::TransferSrcOptimal - | ImageLayout::TransferDstOptimal - | ImageLayout::Preinitialized - | ImageLayout::PresentSrc - ) { - return Err(RenderPassError::ColorAttachmentResolveLayoutInvalid { - attachment_index, - }); - } - - // VUID-VkRenderingInfo-colorAttachmentCount-06091 - // VUID-VkRenderingInfo-colorAttachmentCount-06097 - // VUID-VkRenderingInfo-colorAttachmentCount-06101 - if matches!( - resolve_image_layout, - ImageLayout::DepthStencilAttachmentOptimal - | ImageLayout::DepthStencilReadOnlyOptimal - | ImageLayout::DepthReadOnlyStencilAttachmentOptimal - | ImageLayout::DepthAttachmentStencilReadOnlyOptimal - | ImageLayout::DepthAttachmentOptimal - | ImageLayout::DepthReadOnlyOptimal - | ImageLayout::StencilAttachmentOptimal - | ImageLayout::StencilReadOnlyOptimal - ) { - return Err(RenderPassError::ColorAttachmentResolveLayoutInvalid { - attachment_index, - }); - } - - // VUID-VkRenderingAttachmentInfo-imageView-06136 - if !resolve_image_layout.is_writable(ImageAspect::Color) { - return Err(RenderPassError::ColorAttachmentResolveLayoutInvalid { - attachment_index, - }); - } - } - } - - if let Some(attachment_info) = depth_attachment { - let RenderingAttachmentInfo { - image_view, - image_layout, - resolve_info, - load_op, - store_op, - clear_value: _, - _ne: _, - } = attachment_info; - - // VUID-VkRenderingAttachmentInfo-imageLayout-parameter - image_layout.validate_device(device)?; - - // VUID-VkRenderingAttachmentInfo-loadOp-parameter - load_op.validate_device(device)?; - - // VUID-VkRenderingAttachmentInfo-storeOp-parameter - store_op.validate_device(device)?; - - let image_aspects = image_view.format().aspects(); - - // VUID-VkRenderingInfo-pDepthAttachment-06547 - if !image_aspects.intersects(ImageAspects::DEPTH) { - return Err(RenderPassError::DepthAttachmentFormatUsageNotSupported); - } - - // VUID-VkRenderingInfo-pDepthAttachment-06088 - if !image_view - .usage() - .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT) - { - return Err(RenderPassError::DepthAttachmentMissingUsage); - } - - let image = image_view.image(); - let image_extent = image.extent(); - - for i in 0..2 { - // VUID-VkRenderingInfo-pNext-06079 - // VUID-VkRenderingInfo-pNext-06080 - if render_area_offset[i] + render_area_extent[i] > image_extent[i] { - return Err(RenderPassError::RenderAreaOutOfBounds); - } - } - - // VUID-VkRenderingInfo-imageView-06070 - match samples { - Some(samples) if samples == image.samples() => (), - Some(_) => { - return Err(RenderPassError::DepthAttachmentSamplesMismatch); - } - None => samples = Some(image.samples()), - } - - // VUID-VkRenderingAttachmentInfo-imageView-06135 - // VUID-VkRenderingAttachmentInfo-imageView-06145 - if matches!( - image_layout, - ImageLayout::Undefined - | ImageLayout::ShaderReadOnlyOptimal - | ImageLayout::TransferSrcOptimal - | ImageLayout::TransferDstOptimal - | ImageLayout::Preinitialized - | ImageLayout::PresentSrc - ) { - return Err(RenderPassError::DepthAttachmentLayoutInvalid); - } - - // VUID-VkRenderingInfo-pDepthAttachment-06092 - if matches!(image_layout, ImageLayout::ColorAttachmentOptimal) { - return Err(RenderPassError::DepthAttachmentLayoutInvalid); - } - - if let Some(resolve_info) = resolve_info { - let &RenderingAttachmentResolveInfo { - mode, - image_view: ref resolve_image_view, - image_layout: resolve_image_layout, - } = resolve_info; - - // VUID-VkRenderingAttachmentInfo-resolveImageLayout-parameter - resolve_image_layout.validate_device(device)?; - - // VUID-VkRenderingAttachmentInfo-resolveMode-parameter - mode.validate_device(device)?; - - // VUID-VkRenderingInfo-pDepthAttachment-06102 - if !properties - .supported_depth_resolve_modes - .map_or(false, |modes| modes.contains_enum(mode)) - { - return Err(RenderPassError::DepthAttachmentResolveModeNotSupported); - } - - let resolve_image = resolve_image_view.image(); - - // VUID-VkRenderingAttachmentInfo-imageView-06132 - if image.samples() == SampleCount::Sample1 { - return Err(RenderPassError::DepthAttachmentWithResolveNotMultisampled); - } - - // VUID-VkRenderingAttachmentInfo-imageView-06133 - if resolve_image.samples() != SampleCount::Sample1 { - return Err(RenderPassError::DepthAttachmentResolveMultisampled); - } - - // VUID-VkRenderingAttachmentInfo-imageView-06134 - if image_view.format() != resolve_image_view.format() { - return Err(RenderPassError::DepthAttachmentResolveFormatMismatch); - } - - // VUID-VkRenderingAttachmentInfo-imageView-06136 - // VUID-VkRenderingAttachmentInfo-imageView-06146 - if matches!( - resolve_image_layout, - ImageLayout::Undefined - | ImageLayout::ShaderReadOnlyOptimal - | ImageLayout::TransferSrcOptimal - | ImageLayout::TransferDstOptimal - | ImageLayout::Preinitialized - | ImageLayout::PresentSrc - ) { - return Err(RenderPassError::DepthAttachmentResolveLayoutInvalid); - } - - // VUID-VkRenderingInfo-pDepthAttachment-06093 - if matches!(resolve_image_layout, ImageLayout::ColorAttachmentOptimal) { - return Err(RenderPassError::DepthAttachmentResolveLayoutInvalid); - } - - // VUID-VkRenderingAttachmentInfo-imageView-06136 - // VUID-VkRenderingAttachmentInfo-imageView-06137 - // VUID-VkRenderingInfo-pDepthAttachment-06098 - if !resolve_image_layout.is_writable(ImageAspect::Depth) { - return Err(RenderPassError::DepthAttachmentResolveLayoutInvalid); - } - } - } - - if let Some(attachment_info) = stencil_attachment { - let RenderingAttachmentInfo { - image_view, - image_layout, - resolve_info, - load_op, - store_op, - clear_value: _, - _ne: _, - } = attachment_info; - - // VUID-VkRenderingAttachmentInfo-imageLayout-parameter - image_layout.validate_device(device)?; - - // VUID-VkRenderingAttachmentInfo-loadOp-parameter - load_op.validate_device(device)?; - - // VUID-VkRenderingAttachmentInfo-storeOp-parameter - store_op.validate_device(device)?; - - let image_aspects = image_view.format().aspects(); - - // VUID-VkRenderingInfo-pStencilAttachment-06548 - if !image_aspects.intersects(ImageAspects::STENCIL) { - return Err(RenderPassError::StencilAttachmentFormatUsageNotSupported); - } - - // VUID-VkRenderingInfo-pStencilAttachment-06089 - if !image_view - .usage() - .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT) - { - return Err(RenderPassError::StencilAttachmentMissingUsage); - } - - let image = image_view.image(); - let image_extent = image.extent(); - - for i in 0..2 { - // VUID-VkRenderingInfo-pNext-06079 - // VUID-VkRenderingInfo-pNext-06080 - if render_area_offset[i] + render_area_extent[i] > image_extent[i] { - return Err(RenderPassError::RenderAreaOutOfBounds); - } - } - - // VUID-VkRenderingInfo-imageView-06070 - match samples { - Some(samples) if samples == image.samples() => (), - Some(_) => { - return Err(RenderPassError::StencilAttachmentSamplesMismatch); - } - None => (), - } - - // VUID-VkRenderingAttachmentInfo-imageView-06135 - // VUID-VkRenderingAttachmentInfo-imageView-06145 - if matches!( - image_layout, - ImageLayout::Undefined - | ImageLayout::ShaderReadOnlyOptimal - | ImageLayout::TransferSrcOptimal - | ImageLayout::TransferDstOptimal - | ImageLayout::Preinitialized - | ImageLayout::PresentSrc - ) { - return Err(RenderPassError::StencilAttachmentLayoutInvalid); - } - - // VUID-VkRenderingInfo-pStencilAttachment-06094 - if matches!(image_layout, ImageLayout::ColorAttachmentOptimal) { - return Err(RenderPassError::StencilAttachmentLayoutInvalid); - } - - if let Some(resolve_info) = resolve_info { - let &RenderingAttachmentResolveInfo { - mode, - image_view: ref resolve_image_view, - image_layout: resolve_image_layout, - } = resolve_info; - - // VUID-VkRenderingAttachmentInfo-resolveImageLayout-parameter - resolve_image_layout.validate_device(device)?; - - // VUID-VkRenderingAttachmentInfo-resolveMode-parameter - mode.validate_device(device)?; - - // VUID-VkRenderingInfo-pStencilAttachment-06103 - if !properties - .supported_stencil_resolve_modes - .map_or(false, |modes| modes.contains_enum(mode)) - { - return Err(RenderPassError::StencilAttachmentResolveModeNotSupported); - } - - let resolve_image = resolve_image_view.image(); - - // VUID-VkRenderingAttachmentInfo-imageView-06132 - if image.samples() == SampleCount::Sample1 { - return Err(RenderPassError::StencilAttachmentWithResolveNotMultisampled); - } - - // VUID-VkRenderingAttachmentInfo-imageView-06133 - if resolve_image.samples() != SampleCount::Sample1 { - return Err(RenderPassError::StencilAttachmentResolveMultisampled); - } - - // VUID-VkRenderingAttachmentInfo-imageView-06134 - if image_view.format() != resolve_image_view.format() { - return Err(RenderPassError::StencilAttachmentResolveFormatMismatch); - } - - // VUID-VkRenderingAttachmentInfo-imageView-06136 - // VUID-VkRenderingAttachmentInfo-imageView-06146 - if matches!( - resolve_image_layout, - ImageLayout::Undefined - | ImageLayout::ShaderReadOnlyOptimal - | ImageLayout::TransferSrcOptimal - | ImageLayout::TransferDstOptimal - | ImageLayout::Preinitialized - | ImageLayout::PresentSrc - ) { - return Err(RenderPassError::StencilAttachmentResolveLayoutInvalid); - } - - // VUID-VkRenderingInfo-pStencilAttachment-06095 - if matches!(resolve_image_layout, ImageLayout::ColorAttachmentOptimal) { - return Err(RenderPassError::StencilAttachmentResolveLayoutInvalid); - } - - // VUID-VkRenderingAttachmentInfo-imageView-06136 - // VUID-VkRenderingAttachmentInfo-imageView-06137 - // VUID-VkRenderingInfo-pStencilAttachment-06099 - if !resolve_image_layout.is_writable(ImageAspect::Stencil) { - return Err(RenderPassError::StencilAttachmentResolveLayoutInvalid); - } - } - } - - if let (Some(depth_attachment_info), Some(stencil_attachment_info)) = - (depth_attachment, stencil_attachment) - { - // VUID-VkRenderingInfo-pDepthAttachment-06085 - if &depth_attachment_info.image_view != &stencil_attachment_info.image_view { - return Err(RenderPassError::DepthStencilAttachmentImageViewMismatch); - } - - if depth_attachment_info.image_layout != stencil_attachment_info.image_layout - && !device.enabled_features().separate_depth_stencil_layouts - { - return Err(RenderPassError::RequirementNotMet { - required_for: "`rendering_info.depth_attachment` and \ - `rendering_info.stencil_attachment` are both \ - `Some`, and `rendering_info.depth_attachment.image_layout` does not \ - equal `rendering_info.stencil_attachment.attachment_ref.layout`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "separate_depth_stencil_layouts", - )])]), - }); - } - - match ( - &depth_attachment_info.resolve_info, - &stencil_attachment_info.resolve_info, - ) { - (None, None) => (), - (None, Some(_)) | (Some(_), None) => { - // VUID-VkRenderingInfo-pDepthAttachment-06104 - if !properties.independent_resolve_none.unwrap_or(false) { - return Err( - RenderPassError::DepthStencilAttachmentResolveModesNotSupported, - ); - } - } - (Some(depth_resolve_info), Some(stencil_resolve_info)) => { - if depth_resolve_info.image_layout != stencil_resolve_info.image_layout - && !device.enabled_features().separate_depth_stencil_layouts - { - return Err(RenderPassError::RequirementNotMet { - required_for: "`rendering_info.depth_attachment` and \ - `rendering_info.stencil_attachment` are both `Some`, and \ - `rendering_info.depth_attachment.resolve_info` and \ - `rendering_info.stencil_attachment.resolve_info` are also both \ - `Some`, and \ - `rendering_info.depth_attachment.resolve_info.image_layout` \ - does not equal \ - `rendering_info.stencil_attachment.resolve_info.image_layout`", - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "separate_depth_stencil_layouts", - )])]), - }); - } - - // VUID-VkRenderingInfo-pDepthAttachment-06105 - if !properties.independent_resolve.unwrap_or(false) - && depth_resolve_info.mode != stencil_resolve_info.mode - { - return Err( - RenderPassError::DepthStencilAttachmentResolveModesNotSupported, - ); - } - - // VUID-VkRenderingInfo-pDepthAttachment-06086 - if &depth_resolve_info.image_view != &stencil_resolve_info.image_view { - return Err( - RenderPassError::DepthStencilAttachmentResolveImageViewMismatch, - ); - } - } - } + return Err(Box::new(ValidationError { + problem: "a render pass instance is already active".into(), + vuids: &["VUID-vkCmdBeginRendering-renderpass"], + ..Default::default() + })); } Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] - pub unsafe fn begin_rendering_unchecked(&mut self, rendering_info: RenderingInfo) -> &mut Self { + pub unsafe fn begin_rendering_unchecked( + &mut self, + mut rendering_info: RenderingInfo, + ) -> &mut Self { + rendering_info.set_auto_extent_layers(); + let &RenderingInfo { render_area_offset, render_area_extent, @@ -1517,7 +578,7 @@ where })) .collect(), move |out: &mut UnsafeCommandBufferBuilder| { - out.begin_rendering(&rendering_info); + out.begin_rendering_unchecked(&rendering_info); }, ); @@ -1525,43 +586,55 @@ where } /// Ends the render pass previously begun with `begin_rendering`. - pub fn end_rendering(&mut self) -> Result<&mut Self, RenderPassError> { + pub fn end_rendering(&mut self) -> Result<&mut Self, Box> { self.validate_end_rendering()?; - unsafe { - self.end_rendering_unchecked(); - } - - Ok(self) + unsafe { Ok(self.end_rendering_unchecked()) } } - fn validate_end_rendering(&self) -> Result<(), RenderPassError> { - // VUID-vkCmdEndRendering-renderpass - let render_pass_state = self - .builder_state - .render_pass - .as_ref() - .ok_or(RenderPassError::ForbiddenOutsideRenderPass)?; + fn validate_end_rendering(&self) -> Result<(), Box> { + self.inner.validate_end_rendering()?; - // VUID? - if self.inner.inheritance_info().is_some() { - return Err(RenderPassError::ForbiddenWithInheritedRenderPass); - } + let render_pass_state = + self.builder_state + .render_pass + .as_ref() + .ok_or(Box::new(ValidationError { + problem: "a render pass instance is not active".into(), + vuids: &[ + "VUID-vkCmdEndRendering-renderpass", + "VUID-vkCmdEndRendering-commandBuffer-06162", + ], + ..Default::default() + }))?; - // VUID-vkCmdEndRendering-None-06161 - // VUID-vkCmdEndRendering-commandBuffer-06162 match &render_pass_state.render_pass { RenderPassStateType::BeginRenderPass(_) => { - return Err(RenderPassError::ForbiddenWithBeginRenderPass) + return Err(Box::new(ValidationError { + problem: "the current render pass instance was not begun with \ + `begin_rendering`" + .into(), + vuids: &["VUID-vkCmdEndRendering-None-06161"], + ..Default::default() + })) } RenderPassStateType::BeginRendering(_) => (), } - // VUID-vkCmdEndRendering-commandBuffer-cmdpool - debug_assert!(self - .queue_family_properties() - .queue_flags - .intersects(QueueFlags::GRAPHICS)); + if self + .builder_state + .queries + .values() + .any(|state| state.in_subpass) + { + return Err(Box::new(ValidationError { + problem: "a query that was begun in the current render pass instance \ + is still active" + .into(), + vuids: &["VUID-vkCmdEndRendering-None-06999"], + ..Default::default() + })); + } Ok(()) } @@ -1574,7 +647,7 @@ where "end_rendering", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.end_rendering(); + out.end_rendering_unchecked(); }, ); @@ -1597,40 +670,42 @@ where &mut self, attachments: SmallVec<[ClearAttachment; 4]>, rects: SmallVec<[ClearRect; 4]>, - ) -> Result<&mut Self, RenderPassError> { + ) -> Result<&mut Self, Box> { self.validate_clear_attachments(&attachments, &rects)?; - unsafe { - self.clear_attachments_unchecked(attachments, rects); - } - - Ok(self) + unsafe { Ok(self.clear_attachments_unchecked(attachments, rects)) } } fn validate_clear_attachments( &self, attachments: &[ClearAttachment], rects: &[ClearRect], - ) -> Result<(), RenderPassError> { - // VUID-vkCmdClearAttachments-renderpass - let render_pass_state = self - .builder_state - .render_pass - .as_ref() - .ok_or(RenderPassError::ForbiddenOutsideRenderPass)?; + ) -> Result<(), Box> { + self.inner.validate_clear_attachments(attachments, rects)?; + + let render_pass_state = + self.builder_state + .render_pass + .as_ref() + .ok_or(Box::new(ValidationError { + problem: "a render pass instance is not active".into(), + vuids: &["VUID-vkCmdClearAttachments-renderpass"], + ..Default::default() + }))?; if render_pass_state.contents != SubpassContents::Inline { - return Err(RenderPassError::ForbiddenWithSubpassContents { - contents: render_pass_state.contents, - }); + return Err(Box::new(ValidationError { + problem: "the contents of the current subpass instance is not \ + `SubpassContents::Inline`" + .into(), + // vuids? + ..Default::default() + })); } - //let subpass_desc = begin_render_pass_state.subpass.subpass_desc(); - //let render_pass = begin_render_pass_state.subpass.render_pass(); - let is_multiview = render_pass_state.rendering_info.view_mask != 0; let mut layer_count = u32::MAX; - for &clear_attachment in attachments { + for (clear_index, &clear_attachment) in attachments.iter().enumerate() { match clear_attachment { ClearAttachment::Color { color_attachment, @@ -1640,35 +715,38 @@ where .rendering_info .color_attachment_formats .get(color_attachment as usize) - .ok_or(RenderPassError::ColorAttachmentIndexOutOfRange { - color_attachment_index: color_attachment, - num_color_attachments: render_pass_state - .rendering_info - .color_attachment_formats - .len() as u32, - })?; + .ok_or(Box::new(ValidationError { + context: format!("attachments[{}].color_attachment", clear_index) + .into(), + problem: "is not less than the number of color attachments in the \ + current subpass instance" + .into(), + vuids: &["VUID-vkCmdClearAttachments-aspectMask-07271"], + ..Default::default() + }))?; - // VUID-vkCmdClearAttachments-aspectMask-02501 - if !attachment_format.map_or(false, |format| { - matches!( - (clear_value, format.type_color().unwrap()), - ( - ClearColorValue::Float(_), - NumericType::SFLOAT - | NumericType::UFLOAT - | NumericType::SNORM - | NumericType::UNORM - | NumericType::SSCALED - | NumericType::USCALED - | NumericType::SRGB - ) | (ClearColorValue::Int(_), NumericType::SINT) - | (ClearColorValue::Uint(_), NumericType::UINT) - ) - }) { - return Err(RenderPassError::ClearAttachmentNotCompatible { - clear_attachment, - attachment_format, - }); + if let Some(attachment_format) = attachment_format { + let required_numeric_type = attachment_format + .numeric_format_color() + .unwrap() + .numeric_type(); + + if clear_value.numeric_type() != required_numeric_type { + return Err(Box::new(ValidationError { + problem: format!( + "`attachments[{0}].clear_value` is `ClearColorValue::{1:?}`, \ + but the color attachment specified by \ + `attachments[{0}].color_attachment` requires a clear value \ + of type `ClearColorValue::{2:?}`", + clear_index, + clear_value.numeric_type(), + required_numeric_type, + ) + .into(), + vuids: &["VUID-vkCmdClearAttachments-aspectMask-02501"], + ..Default::default() + })); + } } let image_view = render_pass_state @@ -1686,33 +764,46 @@ where ClearAttachment::Depth(_) | ClearAttachment::Stencil(_) | ClearAttachment::DepthStencil(_) => { - let depth_format = render_pass_state.rendering_info.depth_attachment_format; - let stencil_format = render_pass_state.rendering_info.stencil_attachment_format; - - // VUID-vkCmdClearAttachments-aspectMask-02502 if matches!( clear_attachment, ClearAttachment::Depth(_) | ClearAttachment::DepthStencil(_) - ) && !depth_format.map_or(false, |format| { - format.aspects().intersects(ImageAspects::DEPTH) - }) { - return Err(RenderPassError::ClearAttachmentNotCompatible { - clear_attachment, - attachment_format: None, - }); + ) && render_pass_state + .rendering_info + .depth_attachment_format + .is_none() + { + return Err(Box::new(ValidationError { + problem: format!( + "`attachments[{0}]` is `ClearAttachment::Depth` or \ + `ClearAttachment::DepthStencil`, but \ + the current subpass instance does not have a depth attachment", + clear_index, + ) + .into(), + vuids: &["VUID-vkCmdClearAttachments-aspectMask-02502"], + ..Default::default() + })); } - // VUID-vkCmdClearAttachments-aspectMask-02503 if matches!( clear_attachment, ClearAttachment::Stencil(_) | ClearAttachment::DepthStencil(_) - ) && !stencil_format.map_or(false, |format| { - format.aspects().intersects(ImageAspects::STENCIL) - }) { - return Err(RenderPassError::ClearAttachmentNotCompatible { - clear_attachment, - attachment_format: None, - }); + ) && render_pass_state + .rendering_info + .stencil_attachment_format + .is_none() + { + return Err(Box::new(ValidationError { + problem: format!( + "`attachments[{0}]` is `ClearAttachment::Stencil` or \ + `ClearAttachment::DepthStencil`, but \ + the current subpass instance does not have a stencil attachment", + clear_index, + ) + .into(), + vuids: &["VUID-vkCmdClearAttachments-aspectMask-02503"], + ..Default::default() + })); } let image_view = render_pass_state @@ -1732,47 +823,68 @@ where for (rect_index, rect) in rects.iter().enumerate() { for i in 0..2 { - // VUID-vkCmdClearAttachments-rect-02682 - // VUID-vkCmdClearAttachments-rect-02683 - if rect.extent[i] == 0 { - return Err(RenderPassError::RectExtentZero { rect_index }); - } - - // VUID-vkCmdClearAttachments-pRects-00016 // TODO: This check will always pass in secondary command buffers because of how // it's set in `with_level`. // It needs to be checked during `execute_commands` instead. - if rect.offset[i] < render_pass_state.render_area_offset[i] - || rect.offset[i] + rect.extent[i] - > render_pass_state.render_area_offset[i] - + render_pass_state.render_area_extent[i] + + if rect.offset[i] < render_pass_state.render_area_offset[i] { + return Err(Box::new(ValidationError { + problem: format!( + "`rects[{0}].offset[{i}]` is less than \ + `render_area_offset[{i}]` of the current render pass instance", + rect_index + ) + .into(), + vuids: &["VUID-vkCmdClearAttachments-pRects-00016"], + ..Default::default() + })); + } + + if rect.offset[i] + rect.extent[i] + > render_pass_state.render_area_offset[i] + + render_pass_state.render_area_extent[i] { - return Err(RenderPassError::RectOutOfBounds { rect_index }); + return Err(Box::new(ValidationError { + problem: format!( + "`rects[{0}].offset[{i}] + rects[{0}].extent[{i}]` is \ + greater than `render_area_offset[{i}] + render_area_extent[{i}]` \ + of the current render pass instance", + rect_index, + ) + .into(), + vuids: &["VUID-vkCmdClearAttachments-pRects-00016"], + ..Default::default() + })); } } - // VUID-vkCmdClearAttachments-layerCount-01934 - if rect.array_layers.is_empty() { - return Err(RenderPassError::RectArrayLayersEmpty { rect_index }); - } - - // VUID-vkCmdClearAttachments-pRects-00017 if rect.array_layers.end > layer_count { - return Err(RenderPassError::RectArrayLayersOutOfBounds { rect_index }); + return Err(Box::new(ValidationError { + problem: format!( + "`rects[{}].array_layers.end` is greater than the number of \ + array layers in the current render pass instance", + rect_index + ) + .into(), + vuids: &["VUID-vkCmdClearAttachments-pRects-06937"], + ..Default::default() + })); } - // VUID-vkCmdClearAttachments-baseArrayLayer-00018 - if is_multiview && rect.array_layers != (0..1) { - return Err(RenderPassError::MultiviewRectArrayLayersInvalid { rect_index }); + if render_pass_state.rendering_info.view_mask != 0 && rect.array_layers != (0..1) { + return Err(Box::new(ValidationError { + problem: format!( + "the current render pass instance has a non-zero `view_mask`, but \ + `rects[{}].array_layers` is not `0..1`", + rect_index + ) + .into(), + vuids: &["VUID-vkCmdClearAttachments-baseArrayLayer-00018"], + ..Default::default() + })); } } - // VUID-vkCmdClearAttachments-commandBuffer-cmdpool - debug_assert!(self - .queue_family_properties() - .queue_flags - .intersects(QueueFlags::GRAPHICS)); - Ok(()) } @@ -1786,7 +898,7 @@ where "clear_attachments", Default::default(), move |out: &mut UnsafeCommandBufferBuilder| { - out.clear_attachments(&attachments, &rects); + out.clear_attachments_unchecked(&attachments, &rects); }, ); @@ -1798,12 +910,354 @@ impl UnsafeCommandBufferBuilder where A: CommandBufferAllocator, { - /// Calls `vkCmdBeginRenderPass` on the builder. - #[inline] pub unsafe fn begin_render_pass( &mut self, render_pass_begin_info: &RenderPassBeginInfo, - contents: SubpassContents, + subpass_begin_info: &SubpassBeginInfo, + ) -> Result<&mut Self, Box> { + self.validate_begin_render_pass(render_pass_begin_info, subpass_begin_info)?; + + Ok(self.begin_render_pass_unchecked(render_pass_begin_info, subpass_begin_info)) + } + + fn validate_begin_render_pass( + &self, + render_pass_begin_info: &RenderPassBeginInfo, + subpass_begin_info: &SubpassBeginInfo, + ) -> Result<(), Box> { + if self.level() != CommandBufferLevel::Primary { + return Err(Box::new(ValidationError { + problem: "this command buffer is not a primary command buffer".into(), + vuids: &["VUID-vkCmdBeginRenderPass2-bufferlevel"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdBeginRenderPass2-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + render_pass_begin_info + .validate(self.device()) + .map_err(|err| err.add_context("render_pass_begin_info"))?; + + subpass_begin_info + .validate(self.device()) + .map_err(|err| err.add_context("subpass_begin_info"))?; + + let RenderPassBeginInfo { + render_pass, + framebuffer, + render_area_offset: _, + render_area_extent: _, + clear_values: _, + _ne: _, + } = render_pass_begin_info; + + for (attachment_index, (attachment_desc, image_view)) in render_pass + .attachments() + .iter() + .zip(framebuffer.attachments()) + .enumerate() + { + let attachment_index = attachment_index as u32; + let &AttachmentDescription { + initial_layout, + final_layout, + stencil_initial_layout, + stencil_final_layout, + .. + } = attachment_desc; + + for layout in [ + Some(initial_layout), + Some(final_layout), + stencil_initial_layout, + stencil_final_layout, + ] + .into_iter() + .flatten() + { + match layout { + ImageLayout::ColorAttachmentOptimal => { + if !image_view.usage().intersects(ImageUsage::COLOR_ATTACHMENT) { + return Err(Box::new(ValidationError { + problem: format!( + "`framebuffer.attachments()[{0}]` is used in `render_pass` \ + with the `ImageLayout::ColorAttachmentOptimal` layout, but \ + `framebuffer.attachments()[{0}].usage()` does not contain \ + `ImageUsage::COLOR_ATTACHMENT`", + attachment_index, + ) + .into(), + vuids: &["VUID-vkCmdBeginRenderPass2-initialLayout-03094"], + ..Default::default() + })); + } + } + ImageLayout::DepthReadOnlyStencilAttachmentOptimal + | ImageLayout::DepthAttachmentStencilReadOnlyOptimal + | ImageLayout::DepthStencilAttachmentOptimal + | ImageLayout::DepthStencilReadOnlyOptimal + | ImageLayout::DepthAttachmentOptimal + | ImageLayout::DepthReadOnlyOptimal + | ImageLayout::StencilAttachmentOptimal + | ImageLayout::StencilReadOnlyOptimal => { + if !image_view + .usage() + .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT) + { + return Err(Box::new(ValidationError { + problem: format!( + "`framebuffer.attachments()[{0}]` is used in `render_pass` \ + with the \ + `ImageLayout::DepthReadOnlyStencilAttachmentOptimal`, \ + `ImageLayout::DepthAttachmentStencilReadOnlyOptimal`, \ + `ImageLayout::DepthStencilAttachmentOptimal`, \ + `ImageLayout::DepthStencilReadOnlyOptimal`, \ + `ImageLayout::DepthAttachmentOptimal`, \ + `ImageLayout::DepthReadOnlyOptimal`, \ + `ImageLayout::StencilAttachmentOptimal` or \ + `ImageLayout::StencilReadOnlyOptimal` layout, but \ + `framebuffer.attachments()[{0}].usage()` does not contain \ + `ImageUsage::DEPTH_STENCIL_ATTACHMENT`", + attachment_index, + ) + .into(), + vuids: &[ + "VUID-vkCmdBeginRenderPass2-initialLayout-03096", + "VUID-vkCmdBeginRenderPass2-initialLayout-02844", + ], + ..Default::default() + })); + } + } + ImageLayout::ShaderReadOnlyOptimal => { + if !image_view + .usage() + .intersects(ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT) + { + return Err(Box::new(ValidationError { + problem: format!( + "`framebuffer.attachments()[{0}]` is used in `render_pass` \ + with the `ImageLayout::ShaderReadOnlyOptimal` layout, but \ + `framebuffer.attachments()[{0}].usage()` does not contain \ + `ImageUsage::SAMPLED` or `ImageUsage::INPUT_ATTACHMENT", + attachment_index, + ) + .into(), + vuids: &["VUID-vkCmdBeginRenderPass2-initialLayout-03097"], + ..Default::default() + })); + } + } + ImageLayout::TransferSrcOptimal => { + if !image_view.usage().intersects(ImageUsage::TRANSFER_SRC) { + return Err(Box::new(ValidationError { + problem: format!( + "`framebuffer.attachments()[{0}]` is used in `render_pass` \ + with the `ImageLayout::TransferSrcOptimal` layout, but \ + `framebuffer.attachments()[{0}].usage()` does not contain \ + `ImageUsage::TRANSFER_SRC", + attachment_index, + ) + .into(), + vuids: &["VUID-vkCmdBeginRenderPass2-initialLayout-03098"], + ..Default::default() + })); + } + } + ImageLayout::TransferDstOptimal => { + if !image_view.usage().intersects(ImageUsage::TRANSFER_DST) { + return Err(Box::new(ValidationError { + problem: format!( + "`framebuffer.attachments()[{0}]` is used in `render_pass` \ + with the `ImageLayout::TransferDstOptimal` layout, but \ + `framebuffer.attachments()[{0}].usage()` does not contain \ + `ImageUsage::TRANSFER_DST", + attachment_index, + ) + .into(), + vuids: &["VUID-vkCmdBeginRenderPass2-initialLayout-03099"], + ..Default::default() + })); + } + } + ImageLayout::Undefined + | ImageLayout::General + | ImageLayout::Preinitialized + | ImageLayout::PresentSrc => (), + } + } + } + + for subpass_desc in render_pass.subpasses() { + let SubpassDescription { + flags: _, + view_mask: _, + input_attachments, + color_attachments, + color_resolve_attachments, + depth_stencil_attachment, + depth_stencil_resolve_attachment, + depth_resolve_mode: _, + stencil_resolve_mode: _, + preserve_attachments: _, + _ne: _, + } = subpass_desc; + + for atch_ref in (input_attachments.iter().flatten()) + .chain(color_attachments.iter().flatten()) + .chain(color_resolve_attachments.iter().flatten()) + .chain(depth_stencil_attachment.iter()) + .chain(depth_stencil_resolve_attachment.iter()) + { + let image_view = &framebuffer.attachments()[atch_ref.attachment as usize]; + + match atch_ref.layout { + ImageLayout::ColorAttachmentOptimal => { + if !image_view.usage().intersects(ImageUsage::COLOR_ATTACHMENT) { + return Err(Box::new(ValidationError { + problem: format!( + "`framebuffer.attachments()[{0}]` is used in `render_pass` \ + with the `ImageLayout::ColorAttachmentOptimal` layout, but \ + `framebuffer.attachments()[{0}].usage()` does not contain \ + `ImageUsage::COLOR_ATTACHMENT`", + atch_ref.attachment, + ) + .into(), + vuids: &["VUID-vkCmdBeginRenderPass2-initialLayout-03094"], + ..Default::default() + })); + } + } + ImageLayout::DepthReadOnlyStencilAttachmentOptimal + | ImageLayout::DepthAttachmentStencilReadOnlyOptimal + | ImageLayout::DepthStencilAttachmentOptimal + | ImageLayout::DepthStencilReadOnlyOptimal + | ImageLayout::DepthAttachmentOptimal + | ImageLayout::DepthReadOnlyOptimal + | ImageLayout::StencilAttachmentOptimal + | ImageLayout::StencilReadOnlyOptimal => { + if !image_view + .usage() + .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT) + { + return Err(Box::new(ValidationError { + problem: format!( + "`framebuffer.attachments()[{0}]` is used in `render_pass` \ + with the \ + `ImageLayout::DepthReadOnlyStencilAttachmentOptimal`, \ + `ImageLayout::DepthAttachmentStencilReadOnlyOptimal`, \ + `ImageLayout::DepthStencilAttachmentOptimal`, \ + `ImageLayout::DepthStencilReadOnlyOptimal`, \ + `ImageLayout::DepthAttachmentOptimal`, \ + `ImageLayout::DepthReadOnlyOptimal`, \ + `ImageLayout::StencilAttachmentOptimal` or \ + `ImageLayout::StencilReadOnlyOptimal` layout, but \ + `framebuffer.attachments()[{0}].usage()` does not contain \ + `ImageUsage::DEPTH_STENCIL_ATTACHMENT`", + atch_ref.attachment, + ) + .into(), + vuids: &[ + "VUID-vkCmdBeginRenderPass2-initialLayout-03096", + "VUID-vkCmdBeginRenderPass2-initialLayout-02844", + ], + ..Default::default() + })); + } + } + ImageLayout::ShaderReadOnlyOptimal => { + if !image_view + .usage() + .intersects(ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT) + { + return Err(Box::new(ValidationError { + problem: format!( + "`framebuffer.attachments()[{0}]` is used in `render_pass` \ + with the `ImageLayout::ShaderReadOnlyOptimal` layout, but \ + `framebuffer.attachments()[{0}].usage()` does not contain \ + `ImageUsage::SAMPLED` or `ImageUsage::INPUT_ATTACHMENT", + atch_ref.attachment, + ) + .into(), + vuids: &["VUID-vkCmdBeginRenderPass2-initialLayout-03097"], + ..Default::default() + })); + } + } + ImageLayout::TransferSrcOptimal => { + if !image_view.usage().intersects(ImageUsage::TRANSFER_SRC) { + return Err(Box::new(ValidationError { + problem: format!( + "`framebuffer.attachments()[{0}]` is used in `render_pass` \ + with the `ImageLayout::TransferSrcOptimal` layout, but \ + `framebuffer.attachments()[{0}].usage()` does not contain \ + `ImageUsage::TRANSFER_SRC", + atch_ref.attachment, + ) + .into(), + vuids: &["VUID-vkCmdBeginRenderPass2-initialLayout-03098"], + ..Default::default() + })); + } + } + ImageLayout::TransferDstOptimal => { + if !image_view.usage().intersects(ImageUsage::TRANSFER_DST) { + return Err(Box::new(ValidationError { + problem: format!( + "`framebuffer.attachments()[{0}]` is used in `render_pass` \ + with the `ImageLayout::TransferDstOptimal` layout, but \ + `framebuffer.attachments()[{0}].usage()` does not contain \ + `ImageUsage::TRANSFER_DST", + atch_ref.attachment, + ) + .into(), + vuids: &["VUID-vkCmdBeginRenderPass2-initialLayout-03099"], + ..Default::default() + })); + } + } + ImageLayout::Undefined + | ImageLayout::General + | ImageLayout::Preinitialized + | ImageLayout::PresentSrc => (), + } + } + } + + // VUID-vkCmdBeginRenderPass2-initialLayout-03100 + // TODO: + + // VUID-vkCmdBeginRenderPass2-srcStageMask-06453 + // TODO: + + // VUID-vkCmdBeginRenderPass2-dstStageMask-06454 + // TODO: + + // VUID-vkCmdBeginRenderPass2-framebuffer-02533 + // For any attachment in framebuffer that is used by renderPass and is bound to memory locations that are also bound to another attachment used by renderPass, and if at least one of those uses causes either + // attachment to be written to, both attachments must have had the VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT set + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn begin_render_pass_unchecked( + &mut self, + render_pass_begin_info: &RenderPassBeginInfo, + subpass_begin_info: &SubpassBeginInfo, ) -> &mut Self { let &RenderPassBeginInfo { ref render_pass, @@ -1838,6 +1292,8 @@ where ..Default::default() }; + let &SubpassBeginInfo { contents, _ne: _ } = subpass_begin_info; + let subpass_begin_info = ash::vk::SubpassBeginInfo { contents: contents.into(), ..Default::default() @@ -1874,60 +1330,165 @@ where self } - /// Calls `vkCmdNextSubpass` on the builder. - #[inline] - pub unsafe fn next_subpass(&mut self, contents: SubpassContents) -> &mut Self { + pub unsafe fn next_subpass( + &mut self, + subpass_end_info: &SubpassEndInfo, + subpass_begin_info: &SubpassBeginInfo, + ) -> Result<&mut Self, Box> { + self.validate_next_subpass(subpass_end_info, subpass_begin_info)?; + + Ok(self.next_subpass_unchecked(subpass_end_info, subpass_begin_info)) + } + + fn validate_next_subpass( + &self, + subpass_end_info: &SubpassEndInfo, + subpass_begin_info: &SubpassBeginInfo, + ) -> Result<(), Box> { + if self.level() != CommandBufferLevel::Primary { + return Err(Box::new(ValidationError { + problem: "this command buffer is not a primary command buffer".into(), + vuids: &["VUID-vkCmdNextSubpass2-bufferlevel"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdNextSubpass2-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + subpass_end_info + .validate(self.device()) + .map_err(|err| err.add_context("subpass_end_info"))?; + + subpass_begin_info + .validate(self.device()) + .map_err(|err| err.add_context("subpass_begin_info"))?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn next_subpass_unchecked( + &mut self, + subpass_end_info: &SubpassEndInfo, + subpass_begin_info: &SubpassBeginInfo, + ) -> &mut Self { let fns = self.device().fns(); - let subpass_begin_info = ash::vk::SubpassBeginInfo { + let &SubpassEndInfo { _ne: _ } = subpass_end_info; + + let subpass_end_info_vk = ash::vk::SubpassEndInfo::default(); + + let &SubpassBeginInfo { contents, _ne: _ } = subpass_begin_info; + + let subpass_begin_info_vk = ash::vk::SubpassBeginInfo { contents: contents.into(), ..Default::default() }; - let subpass_end_info = ash::vk::SubpassEndInfo::default(); - if self.device().api_version() >= Version::V1_2 || self.device().enabled_extensions().khr_create_renderpass2 { if self.device().api_version() >= Version::V1_2 { - (fns.v1_2.cmd_next_subpass2)(self.handle(), &subpass_begin_info, &subpass_end_info); + (fns.v1_2.cmd_next_subpass2)( + self.handle(), + &subpass_begin_info_vk, + &subpass_end_info_vk, + ); } else { (fns.khr_create_renderpass2.cmd_next_subpass2_khr)( self.handle(), - &subpass_begin_info, - &subpass_end_info, + &subpass_begin_info_vk, + &subpass_end_info_vk, ); } } else { - debug_assert!(subpass_begin_info.p_next.is_null()); - debug_assert!(subpass_end_info.p_next.is_null()); + debug_assert!(subpass_begin_info_vk.p_next.is_null()); + debug_assert!(subpass_end_info_vk.p_next.is_null()); - (fns.v1_0.cmd_next_subpass)(self.handle(), subpass_begin_info.contents); + (fns.v1_0.cmd_next_subpass)(self.handle(), subpass_begin_info_vk.contents); } self } - /// Calls `vkCmdEndRenderPass` on the builder. - #[inline] - pub unsafe fn end_render_pass(&mut self) -> &mut Self { + pub unsafe fn end_render_pass( + &mut self, + subpass_end_info: &SubpassEndInfo, + ) -> Result<&mut Self, Box> { + self.validate_end_render_pass(subpass_end_info)?; + + Ok(self.end_render_pass_unchecked(subpass_end_info)) + } + + fn validate_end_render_pass( + &self, + subpass_end_info: &SubpassEndInfo, + ) -> Result<(), Box> { + if self.level() != CommandBufferLevel::Primary { + return Err(Box::new(ValidationError { + problem: "this command buffer is not a primary command buffer".into(), + vuids: &["VUID-vkCmdEndRenderPass2-bufferlevel"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdEndRenderPass2-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + subpass_end_info + .validate(self.device()) + .map_err(|err| err.add_context("subpass_end_info"))?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn end_render_pass_unchecked( + &mut self, + subpass_end_info: &SubpassEndInfo, + ) -> &mut Self { let fns = self.device().fns(); - let subpass_end_info = ash::vk::SubpassEndInfo::default(); + let &SubpassEndInfo { _ne: _ } = subpass_end_info; + + let subpass_end_info_vk = ash::vk::SubpassEndInfo::default(); if self.device().api_version() >= Version::V1_2 || self.device().enabled_extensions().khr_create_renderpass2 { if self.device().api_version() >= Version::V1_2 { - (fns.v1_2.cmd_end_render_pass2)(self.handle(), &subpass_end_info); + (fns.v1_2.cmd_end_render_pass2)(self.handle(), &subpass_end_info_vk); } else { (fns.khr_create_renderpass2.cmd_end_render_pass2_khr)( self.handle(), - &subpass_end_info, + &subpass_end_info_vk, ); } } else { - debug_assert!(subpass_end_info.p_next.is_null()); + debug_assert!(subpass_end_info_vk.p_next.is_null()); (fns.v1_0.cmd_end_render_pass)(self.handle()); } @@ -1935,9 +1496,81 @@ where self } - /// Calls `vkCmdBeginRendering` on the builder. - #[inline] - pub unsafe fn begin_rendering(&mut self, rendering_info: &RenderingInfo) -> &mut Self { + pub unsafe fn begin_rendering( + &mut self, + rendering_info: &RenderingInfo, + ) -> Result<&mut Self, Box> { + self.validate_begin_rendering(rendering_info)?; + + Ok(self.begin_rendering_unchecked(rendering_info)) + } + + fn validate_begin_rendering( + &self, + rendering_info: &RenderingInfo, + ) -> Result<(), Box> { + let device = self.device(); + + if !device.enabled_features().dynamic_rendering { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "dynamic_rendering", + )])]), + vuids: &["VUID-vkCmdBeginRendering-dynamicRendering-06446"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdBeginRendering-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + rendering_info + .validate(self.device()) + .map_err(|err| err.add_context("rendering_info"))?; + + let &RenderingInfo { + render_area_offset: _, + render_area_extent: _, + layer_count: _, + view_mask: _, + color_attachments: _, + depth_attachment: _, + stencil_attachment: _, + contents, + _ne: _, + } = rendering_info; + + if self.level() == CommandBufferLevel::Secondary + && contents == SubpassContents::SecondaryCommandBuffers + { + return Err(Box::new(ValidationError { + problem: "this command buffer is a secondary command buffer, but \ + `rendering_info.contents` is `SubpassContents::SecondaryCommandBuffers`" + .into(), + vuids: &["VUID-vkCmdBeginRendering-commandBuffer-06068"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn begin_rendering_unchecked( + &mut self, + rendering_info: &RenderingInfo, + ) -> &mut Self { let &RenderingInfo { render_area_offset, render_area_extent, @@ -2035,9 +1668,32 @@ where self } - /// Calls `vkCmdEndRendering` on the builder. - #[inline] - pub unsafe fn end_rendering(&mut self) -> &mut Self { + pub unsafe fn end_rendering(&mut self) -> Result<&mut Self, Box> { + self.validate_end_rendering()?; + + Ok(self.end_rendering_unchecked()) + } + + fn validate_end_rendering(&self) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdEndRendering-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn end_rendering_unchecked(&mut self) -> &mut Self { let fns = self.device().fns(); if self.device().api_version() >= Version::V1_3 { @@ -2049,14 +1705,84 @@ where self } - /// Calls `vkCmdClearAttachments` on the builder. - /// - /// Does nothing if the list of attachments or the list of rects is empty, as it would be a - /// no-op and isn't a valid usage of the command anyway. pub unsafe fn clear_attachments( &mut self, attachments: &[ClearAttachment], rects: &[ClearRect], + ) -> Result<&mut Self, Box> { + self.validate_clear_attachments(attachments, rects)?; + + Ok(self.clear_attachments_unchecked(attachments, rects)) + } + + fn validate_clear_attachments( + &self, + attachments: &[ClearAttachment], + rects: &[ClearRect], + ) -> Result<(), Box> { + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics operations" + .into(), + vuids: &["VUID-vkCmdClearAttachments-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + for (clear_index, clear_attachment) in attachments.iter().enumerate() { + clear_attachment + .validate(self.device()) + .map_err(|err| err.add_context(format!("attachments[{}]", clear_index)))?; + } + + for (rect_index, rect) in rects.iter().enumerate() { + let ClearRect { + offset: _, + extent, + ref array_layers, + } = rect; + + if extent[0] == 0 { + return Err(Box::new(ValidationError { + context: format!("rects[{}].extent[0]", rect_index).into(), + problem: "is 0".into(), + vuids: &["VUID-vkCmdClearAttachments-rect-02682"], + ..Default::default() + })); + } + + if extent[1] == 0 { + return Err(Box::new(ValidationError { + context: format!("rects[{}].extent[1]", rect_index).into(), + problem: "is 0".into(), + vuids: &["VUID-vkCmdClearAttachments-rect-02683"], + ..Default::default() + })); + } + + if array_layers.is_empty() { + return Err(Box::new(ValidationError { + context: format!("rects[{}].array_layers", rect_index).into(), + problem: "is empty".into(), + vuids: &["VUID-vkCmdClearAttachments-layerCount-01934"], + ..Default::default() + })); + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn clear_attachments_unchecked( + &mut self, + attachments: &[ClearAttachment], + rects: &[ClearRect], ) -> &mut Self { if attachments.is_empty() || rects.is_empty() { return self; @@ -2151,6 +1877,192 @@ impl RenderPassBeginInfo { _ne: crate::NonExhaustive(()), } } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + ref render_pass, + ref framebuffer, + render_area_offset, + render_area_extent, + ref clear_values, + _ne, + } = self; + + // VUID-VkRenderPassBeginInfo-commonparent + // VUID-vkCmdBeginRenderPass2-framebuffer-02779 + assert_eq!(device, framebuffer.device().as_ref()); + + if !render_pass.is_compatible_with(framebuffer.render_pass()) { + return Err(Box::new(ValidationError { + problem: "`render_pass` is not compatible with `framebuffer.render_pass()`".into(), + vuids: &["VUID-VkRenderPassBeginInfo-renderPass-00904"], + ..Default::default() + })); + } + + if render_area_extent[0] == 0 { + return Err(Box::new(ValidationError { + context: "render_area_extent[0]".into(), + problem: "is 0".into(), + vuids: &["VUID-VkRenderPassBeginInfo-None-08996"], + ..Default::default() + })); + } + + if render_area_extent[1] == 0 { + return Err(Box::new(ValidationError { + context: "render_area_extent[1]".into(), + problem: "is 0".into(), + vuids: &["VUID-VkRenderPassBeginInfo-None-08997"], + ..Default::default() + })); + } + + if render_area_offset[0] + render_area_extent[0] > framebuffer.extent()[0] { + return Err(Box::new(ValidationError { + problem: "`render_area_offset[0] + render_area_extent[0]` is greater than \ + `framebuffer.extent()[0]`" + .into(), + vuids: &["VUID-VkRenderPassBeginInfo-pNext-02852"], + ..Default::default() + })); + } + + if render_area_offset[1] + render_area_extent[1] > framebuffer.extent()[1] { + return Err(Box::new(ValidationError { + problem: "`render_area_offset[1] + render_area_extent[1]` is greater than \ + `framebuffer.extent()[1]`" + .into(), + vuids: &["VUID-VkRenderPassBeginInfo-pNext-02853"], + ..Default::default() + })); + } + + if clear_values.len() != render_pass.attachments().len() { + return Err(Box::new(ValidationError { + problem: "`clear_values.len()` is not equal to `render_pass.attachments().len()`" + .into(), + vuids: &["VUID-VkRenderPassBeginInfo-clearValueCount-00902"], + ..Default::default() + })); + } + + // VUID-VkRenderPassBeginInfo-clearValueCount-04962 + for (attachment_index, (attachment_desc, clear_value)) in render_pass + .attachments() + .iter() + .zip(clear_values) + .enumerate() + { + match (clear_value, attachment_desc.required_clear_value()) { + (None, None) => continue, + (None, Some(_)) => { + return Err(Box::new(ValidationError { + problem: format!( + "`render_pass.attachments()[{0}]` requires a clear value, but \ + `clear_values[{0}]` is `None`", + attachment_index + ) + .into(), + ..Default::default() + })); + } + (Some(_), None) => { + return Err(Box::new(ValidationError { + problem: format!( + "`render_pass.attachments()[{0}]` does not require a clear value, but \ + `clear_values[{0}]` is `Some`", + attachment_index + ) + .into(), + ..Default::default() + })); + } + (Some(clear_value), Some(required_clear_value)) => { + if required_clear_value != clear_value.clear_value_type() { + return Err(Box::new(ValidationError { + problem: format!( + "`clear_values[{0}]` is `ClearValue::{1:?}`, but \ + `render_pass.attachments()[{0}]` requires a clear value of type \ + `ClearValue::{2:?}`", + attachment_index, + clear_value.clear_value_type(), + required_clear_value, + ) + .into(), + ..Default::default() + })); + } + + clear_value.validate(device).map_err(|err| { + err.add_context(format!("clear_values[{}]", attachment_index)) + })?; + } + } + } + + Ok(()) + } +} + +/// Parameters to begin a new subpass within a render pass. +#[derive(Clone, Debug)] +pub struct SubpassBeginInfo { + /// What kinds of commands will be recorded in the subpass. + /// + /// The default value is [`SubpassContents::Inline`]. + pub contents: SubpassContents, + + pub _ne: crate::NonExhaustive, +} + +impl Default for SubpassBeginInfo { + #[inline] + fn default() -> Self { + Self { + contents: SubpassContents::Inline, + _ne: crate::NonExhaustive(()), + } + } +} + +impl SubpassBeginInfo { + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { contents, _ne: _ } = self; + + contents + .validate_device(device) + .map_err(|err| ValidationError { + context: "contents".into(), + vuids: &["VUID-VkSubpassBeginInfo-contents-parameter"], + ..ValidationError::from_requirement(err) + })?; + + Ok(()) + } +} + +/// Parameters to end the current subpass within a render pass. +#[derive(Clone, Debug)] +pub struct SubpassEndInfo { + pub _ne: crate::NonExhaustive, +} + +impl Default for SubpassEndInfo { + #[inline] + fn default() -> Self { + Self { + _ne: crate::NonExhaustive(()), + } + } +} + +impl SubpassEndInfo { + pub(crate) fn validate(&self, _device: &Device) -> Result<(), Box> { + let &Self { _ne: _ } = self; + + Ok(()) + } } /// Parameters to begin rendering. @@ -2250,7 +2162,7 @@ impl Default for RenderingInfo { } impl RenderingInfo { - pub(crate) fn set_extent_layers(&mut self) -> Result<(), RenderPassError> { + pub(crate) fn set_auto_extent_layers(&mut self) { let &mut RenderingInfo { render_area_offset, ref mut render_area_extent, @@ -2263,16 +2175,22 @@ impl RenderingInfo { _ne: _, } = self; - let auto_extent = render_area_extent[0] == 0 || render_area_extent[1] == 0; - let auto_layers = *layer_count == 0; + let is_auto_extent = render_area_extent[0] == 0 || render_area_extent[1] == 0; + let is_auto_layers = *layer_count == 0; - // Set the values based on the attachment sizes. - if auto_extent || auto_layers { - if auto_extent { + if (is_auto_extent || is_auto_layers) + && !(color_attachments.is_empty() + && depth_attachment.is_none() + && stencil_attachment.is_none()) + { + let mut auto_extent = [u32::MAX, u32::MAX]; + let mut auto_layers = if view_mask != 0 { 1 } else { u32::MAX }; + + if is_auto_extent { *render_area_extent = [u32::MAX, u32::MAX]; } - if auto_layers { + if is_auto_layers { if view_mask != 0 { *layer_count = 1; } else { @@ -2292,41 +2210,630 @@ impl RenderingInfo { ) }) { - if auto_extent { - let extent = image_view.image().extent(); + let image_view_extent = image_view.image().extent(); + let image_view_array_layers = + image_view.subresource_range().array_layers.len() as u32; - for i in 0..2 { - render_area_extent[i] = min(render_area_extent[i], extent[i]); - } - } - - if auto_layers { - let subresource_range = image_view.subresource_range(); - let array_layers = - subresource_range.array_layers.end - subresource_range.array_layers.start; - - *layer_count = min(*layer_count, array_layers); - } + auto_extent[0] = auto_extent[0].min(image_view_extent[0]); + auto_extent[1] = auto_extent[1].min(image_view_extent[1]); + auto_layers = auto_layers.min(image_view_array_layers); } - if auto_extent { - if *render_area_extent == [u32::MAX, u32::MAX] { - return Err(RenderPassError::AutoExtentAttachmentsEmpty); - } - + if is_auto_extent { // Subtract the offset from the calculated max extent. // If there is an underflow, then the offset is too large, and validation should // catch that later. for i in 0..2 { - render_area_extent[i] = render_area_extent[i] + render_area_extent[i] = auto_extent[i] .checked_sub(render_area_offset[i]) .unwrap_or(1); } } - if auto_layers { - if *layer_count == u32::MAX { - return Err(RenderPassError::AutoLayersAttachmentsEmpty); + if is_auto_layers { + *layer_count = auto_layers; + } + } + } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + render_area_offset, + render_area_extent, + layer_count, + view_mask, + ref color_attachments, + ref depth_attachment, + ref stencil_attachment, + contents, + _ne: _, + } = self; + + let properties = device.physical_device().properties(); + + contents + .validate_device(device) + .map_err(|err| ValidationError { + context: "contents".into(), + vuids: &["VUID-VkRenderingInfo-flags-parameter"], + ..ValidationError::from_requirement(err) + })?; + + if render_area_extent[0] == 0 { + return Err(Box::new(ValidationError { + context: "render_area_extent[0]".into(), + problem: "is 0".into(), + vuids: &["VUID-VkRenderingInfo-None-08994"], + ..Default::default() + })); + } + + if render_area_extent[1] == 0 { + return Err(Box::new(ValidationError { + context: "render_area_extent[1]".into(), + problem: "is 0".into(), + vuids: &["VUID-VkRenderingInfo-None-08995"], + ..Default::default() + })); + } + + if render_area_offset[0] + render_area_extent[0] > properties.max_framebuffer_width { + return Err(Box::new(ValidationError { + problem: "`render_area_offset[0] + render_area_extent[0]` is greater than the \ + `max_framebuffer_width` limit" + .into(), + vuids: &["VUID-VkRenderingInfo-pNext-07815"], + ..Default::default() + })); + } + + if render_area_offset[1] + render_area_extent[1] > properties.max_framebuffer_height { + return Err(Box::new(ValidationError { + problem: "`render_area_offset[1] + render_area_extent[1]` is greater than the \ + `max_framebuffer_height` limit" + .into(), + vuids: &["VUID-VkRenderingInfo-pNext-07816"], + ..Default::default() + })); + } + + // No VUID, but for sanity it makes sense to treat this the same as in framebuffers. + if view_mask != 0 && layer_count != 1 { + return Err(Box::new(ValidationError { + problem: "`view_mask` is not 0, but `layer_count` is not 1".into(), + // vuids? + ..Default::default() + })); + } + + if view_mask != 0 && !device.enabled_features().multiview { + return Err(Box::new(ValidationError { + context: "view_mask".into(), + problem: "is not 0".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature("multiview")])]), + vuids: &["VUID-VkRenderingInfo-multiview-06127"], + })); + } + + let highest_view_index = u32::BITS - view_mask.leading_zeros(); + + if highest_view_index > properties.max_multiview_view_count.unwrap_or(0) { + return Err(Box::new(ValidationError { + context: "view_mask".into(), + problem: "the highest enabled view index is not less than the \ + `max_multiview_view_count` limit" + .into(), + vuids: &["VUID-VkRenderingInfo-viewMask-06128"], + ..Default::default() + })); + } + + let mut samples = None; + + if color_attachments.len() > properties.max_color_attachments as usize { + return Err(Box::new(ValidationError { + context: "color_attachments".into(), + problem: "the number of elements is greater than the `max_color_attachments` limit" + .into(), + vuids: &["VUID-VkRenderingInfo-colorAttachmentCount-06106"], + ..Default::default() + })); + } + + for (attachment_index, attachment_info) in + color_attachments + .iter() + .enumerate() + .filter_map(|(index, attachment_info)| { + attachment_info + .as_ref() + .map(|attachment_info| (index, attachment_info)) + }) + { + attachment_info.validate(device).map_err(|err| { + err.add_context(format!("color_attachments[{}]", attachment_index)) + })?; + + let RenderingAttachmentInfo { + image_view, + image_layout, + resolve_info, + load_op: _, + store_op: _, + clear_value: _, + _ne: _, + } = attachment_info; + + if !image_view.usage().intersects(ImageUsage::COLOR_ATTACHMENT) { + return Err(Box::new(ValidationError { + context: format!("color_attachments[{}].image_view.usage()", attachment_index) + .into(), + problem: "does not contain `ImageUsage::COLOR_ATTACHMENT".into(), + vuids: &["VUID-VkRenderingInfo-colorAttachmentCount-06087"], + ..Default::default() + })); + } + + if render_area_offset[0] + render_area_extent[0] > image_view.image().extent()[0] { + return Err(Box::new(ValidationError { + problem: format!( + "`render_area_offset[0] + render_area_extent[0]` is greater than \ + `color_attachments[{}].image_view.image().extent()[0]`", + attachment_index, + ) + .into(), + vuids: &["VUID-VkRenderingInfo-pNext-06079"], + ..Default::default() + })); + } + + if render_area_offset[1] + render_area_extent[1] > image_view.image().extent()[1] { + return Err(Box::new(ValidationError { + problem: format!( + "`render_area_offset[1] + render_area_extent[1]` is greater than \ + `color_attachments[{}].image_view.image().extent()[1]`", + attachment_index, + ) + .into(), + vuids: &["VUID-VkRenderingInfo-pNext-06080"], + ..Default::default() + })); + } + + match samples { + Some(samples) => { + if samples != image_view.image().samples() { + return Err(Box::new(ValidationError { + problem: format!( + "`color_attachments[{0}].image_view.image().samples()` \ + is not equal to the number of samples of the other attachments", + attachment_index + ) + .into(), + vuids: &["VUID-VkRenderingInfo-imageView-06070"], + ..Default::default() + })); + } + } + None => samples = Some(image_view.image().samples()), + } + + if matches!( + image_layout, + ImageLayout::DepthStencilAttachmentOptimal + | ImageLayout::DepthStencilReadOnlyOptimal + | ImageLayout::DepthReadOnlyStencilAttachmentOptimal + | ImageLayout::DepthAttachmentStencilReadOnlyOptimal + | ImageLayout::DepthAttachmentOptimal + | ImageLayout::DepthReadOnlyOptimal + | ImageLayout::StencilAttachmentOptimal + | ImageLayout::StencilReadOnlyOptimal + ) { + return Err(Box::new(ValidationError { + context: format!("color_attachments[{0}].image_layout", attachment_index) + .into(), + problem: "is `ImageLayout::DepthStencilAttachmentOptimal`, \ + `ImageLayout::DepthStencilReadOnlyOptimal`, \ + `ImageLayout::DepthReadOnlyStencilAttachmentOptimal`, \ + `ImageLayout::DepthAttachmentStencilReadOnlyOptimal`, \ + `ImageLayout::DepthAttachmentOptimal`, \ + `ImageLayout::DepthReadOnlyOptimal`, \ + `ImageLayout::StencilAttachmentOptimal` or \ + `ImageLayout::StencilReadOnlyOptimal`" + .into(), + vuids: &[ + "VUID-VkRenderingInfo-colorAttachmentCount-06090", + "VUID-VkRenderingInfo-colorAttachmentCount-06096", + "VUID-VkRenderingInfo-colorAttachmentCount-06100", + ], + ..Default::default() + })); + } + + if let Some(resolve_info) = resolve_info { + let &RenderingAttachmentResolveInfo { + mode: _, + image_view: _, + image_layout: resolve_image_layout, + } = resolve_info; + + if matches!( + resolve_image_layout, + ImageLayout::DepthStencilAttachmentOptimal + | ImageLayout::DepthStencilReadOnlyOptimal + | ImageLayout::DepthReadOnlyStencilAttachmentOptimal + | ImageLayout::DepthAttachmentStencilReadOnlyOptimal + | ImageLayout::DepthAttachmentOptimal + | ImageLayout::DepthReadOnlyOptimal + | ImageLayout::StencilAttachmentOptimal + | ImageLayout::StencilReadOnlyOptimal + ) { + return Err(Box::new(ValidationError { + context: format!( + "color_attachments[{0}].resolve_info.image_layout", + attachment_index + ) + .into(), + problem: "is `ImageLayout::DepthStencilAttachmentOptimal`, \ + `ImageLayout::DepthStencilReadOnlyOptimal`, \ + `ImageLayout::DepthReadOnlyStencilAttachmentOptimal`, \ + `ImageLayout::DepthAttachmentStencilReadOnlyOptimal`, \ + `ImageLayout::DepthAttachmentOptimal`, \ + `ImageLayout::DepthReadOnlyOptimal`, \ + `ImageLayout::StencilAttachmentOptimal` or \ + `ImageLayout::StencilReadOnlyOptimal`" + .into(), + vuids: &[ + "VUID-VkRenderingInfo-colorAttachmentCount-06091", + "VUID-VkRenderingInfo-colorAttachmentCount-06097", + "VUID-VkRenderingInfo-colorAttachmentCount-06101", + ], + ..Default::default() + })); + } + } + } + + if let Some(attachment_info) = depth_attachment { + attachment_info + .validate(device) + .map_err(|err| err.add_context("depth_attachment"))?; + + let RenderingAttachmentInfo { + image_view, + image_layout, + resolve_info, + load_op: _, + store_op: _, + clear_value: _, + _ne: _, + } = attachment_info; + + if !image_view + .format() + .aspects() + .intersects(ImageAspects::DEPTH) + { + return Err(Box::new(ValidationError { + context: "depth_attachment.image_view.format()".into(), + problem: "does not have a depth aspect".into(), + vuids: &["VUID-VkRenderingInfo-pDepthAttachment-06547"], + ..Default::default() + })); + } + + if !image_view + .usage() + .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT) + { + return Err(Box::new(ValidationError { + context: "depth_attachment.image_view.usage()".into(), + problem: "does not contain `ImageUsage::DEPTH_STENCIL_ATTACHMENT`".into(), + vuids: &["VUID-VkRenderingInfo-pDepthAttachment-06088"], + ..Default::default() + })); + } + + if render_area_offset[0] + render_area_extent[0] > image_view.image().extent()[0] { + return Err(Box::new(ValidationError { + problem: "`render_area_offset[0] + render_area_extent[0]` is greater than \ + `depth_attachment.image_view.image().extent()[0]`" + .into(), + vuids: &["VUID-VkRenderingInfo-pNext-06079"], + ..Default::default() + })); + } + + if render_area_offset[1] + render_area_extent[1] > image_view.image().extent()[1] { + return Err(Box::new(ValidationError { + problem: "`render_area_offset[1] + render_area_extent[1]` is greater than \ + `depth_attachment.image_view.image().extent()[1]`" + .into(), + vuids: &["VUID-VkRenderingInfo-pNext-06080"], + ..Default::default() + })); + } + + match samples { + Some(samples) => { + if samples != image_view.image().samples() { + return Err(Box::new(ValidationError { + problem: "`depth_attachment.image_view.image().samples()` \ + is not equal to the number of samples of the other attachments" + .into(), + vuids: &["VUID-VkRenderingInfo-imageView-06070"], + ..Default::default() + })); + } + } + None => samples = Some(image_view.image().samples()), + } + + if matches!(image_layout, ImageLayout::ColorAttachmentOptimal) { + return Err(Box::new(ValidationError { + context: "depth_attachment.image_layout".into(), + problem: "is `ImageLayout::ColorAttachmentOptimal`".into(), + vuids: &["VUID-VkRenderingInfo-pDepthAttachment-06092"], + ..Default::default() + })); + } + + if let Some(resolve_info) = resolve_info { + let &RenderingAttachmentResolveInfo { + mode, + image_view: _, + image_layout: resolve_image_layout, + } = resolve_info; + + if !properties + .supported_depth_resolve_modes + .map_or(false, |modes| modes.contains_enum(mode)) + { + return Err(Box::new(ValidationError { + problem: "`depth_attachment.resolve_info.mode` is not one of the modes in \ + the `supported_depth_resolve_modes` device property" + .into(), + vuids: &["VUID-VkRenderingInfo-pDepthAttachment-06102"], + ..Default::default() + })); + } + + if matches!( + resolve_image_layout, + ImageLayout::ColorAttachmentOptimal + | ImageLayout::DepthReadOnlyStencilAttachmentOptimal + ) { + return Err(Box::new(ValidationError { + context: "depth_attachment.resolve_info.image_layout".into(), + problem: "is `ImageLayout::ColorAttachmentOptimal` or \ + `ImageLayout::DepthReadOnlyStencilAttachmentOptimal`" + .into(), + vuids: &[ + "VUID-VkRenderingInfo-pDepthAttachment-06093", + "VUID-VkRenderingInfo-pDepthAttachment-06098", + ], + ..Default::default() + })); + } + } + } + + if let Some(attachment_info) = stencil_attachment { + attachment_info + .validate(device) + .map_err(|err| err.add_context("stencil_attachment"))?; + + let RenderingAttachmentInfo { + image_view, + image_layout, + resolve_info, + load_op: _, + store_op: _, + clear_value: _, + _ne: _, + } = attachment_info; + + if !image_view + .format() + .aspects() + .intersects(ImageAspects::STENCIL) + { + return Err(Box::new(ValidationError { + context: "stencil_attachment.image_view.format()".into(), + problem: "does not have a stencil aspect".into(), + vuids: &["VUID-VkRenderingInfo-pStencilAttachment-06548"], + ..Default::default() + })); + } + + if !image_view + .usage() + .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT) + { + return Err(Box::new(ValidationError { + context: "stencil_attachment.image_view.usage()".into(), + problem: "does not contain `ImageUsage::DEPTH_STENCIL_ATTACHMENT`".into(), + vuids: &["VUID-VkRenderingInfo-pStencilAttachment-06089"], + ..Default::default() + })); + } + + if render_area_offset[0] + render_area_extent[0] > image_view.image().extent()[0] { + return Err(Box::new(ValidationError { + problem: "`render_area_offset[0] + render_area_extent[0]` is greater than \ + `stencil_attachment.image_view.image().extent()[0]`" + .into(), + vuids: &["VUID-VkRenderingInfo-pNext-06079"], + ..Default::default() + })); + } + + if render_area_offset[1] + render_area_extent[1] > image_view.image().extent()[1] { + return Err(Box::new(ValidationError { + problem: "`render_area_offset[1] + render_area_extent[1]` is greater than \ + `stencil_attachment.image_view.image().extent()[1]`" + .into(), + vuids: &["VUID-VkRenderingInfo-pNext-06080"], + ..Default::default() + })); + } + + if let Some(samples) = samples { + if samples != image_view.image().samples() { + return Err(Box::new(ValidationError { + problem: "`stencil_attachment.image_view.image().samples()` \ + is not equal to the number of samples of the other attachments" + .into(), + vuids: &["VUID-VkRenderingInfo-imageView-06070"], + ..Default::default() + })); + } + } + + if matches!(image_layout, ImageLayout::ColorAttachmentOptimal) { + return Err(Box::new(ValidationError { + context: "stencil_attachment.image_layout".into(), + problem: "is `ImageLayout::ColorAttachmentOptimal`".into(), + vuids: &["VUID-VkRenderingInfo-pStencilAttachment-06094"], + ..Default::default() + })); + } + + if let Some(resolve_info) = resolve_info { + let &RenderingAttachmentResolveInfo { + mode, + image_view: _, + image_layout: resolve_image_layout, + } = resolve_info; + + if !properties + .supported_stencil_resolve_modes + .map_or(false, |modes| modes.contains_enum(mode)) + { + return Err(Box::new(ValidationError { + problem: + "`stencil_attachment.resolve_info.mode` is not one of the modes in \ + the `supported_stencil_resolve_modes` device property" + .into(), + vuids: &["VUID-VkRenderingInfo-pStencilAttachment-06103"], + ..Default::default() + })); + } + + if matches!( + resolve_image_layout, + ImageLayout::ColorAttachmentOptimal + | ImageLayout::DepthAttachmentStencilReadOnlyOptimal + ) { + return Err(Box::new(ValidationError { + context: "stencil_attachment.resolve_info.image_layout".into(), + problem: "is `ImageLayout::ColorAttachmentOptimal` or \ + `ImageLayout::DepthReadOnlyStencilAttachmentOptimal`" + .into(), + vuids: &[ + "VUID-VkRenderingInfo-pStencilAttachment-06095", + "VUID-VkRenderingInfo-pStencilAttachment-06099", + ], + ..Default::default() + })); + } + } + } + + if let (Some(depth_attachment_info), Some(stencil_attachment_info)) = + (depth_attachment, stencil_attachment) + { + if &depth_attachment_info.image_view != &stencil_attachment_info.image_view { + return Err(Box::new(ValidationError { + problem: "`depth_attachment` and `stencil_attachment` are both `Some`, but \ + `depth_attachment.image_view` does not equal \ + `stencil_attachment.image_view`" + .into(), + vuids: &["VUID-VkRenderingInfo-pDepthAttachment-06085"], + ..Default::default() + })); + } + + if depth_attachment_info.image_layout != stencil_attachment_info.image_layout + && !device.enabled_features().separate_depth_stencil_layouts + { + return Err(Box::new(ValidationError { + problem: "`depth_attachment` and `stencil_attachment` are both `Some`, and \ + `depth_attachment.image_layout` does not equal \ + `stencil_attachment.attachment_ref.layout`" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "separate_depth_stencil_layouts", + )])]), + ..Default::default() + })); + } + + match ( + &depth_attachment_info.resolve_info, + &stencil_attachment_info.resolve_info, + ) { + (None, None) => (), + (None, Some(_)) | (Some(_), None) => { + if !properties.independent_resolve_none.unwrap_or(false) { + return Err(Box::new(ValidationError { + problem: "`depth_attachment` and `stencil_attachment` are both \ + `Some`, and the `independent_resolve_none` device property is \ + `false`, but one of `depth_attachment.resolve_info` and \ + `stencil_attachment.resolve_info` is `Some` while the other is \ + `None`" + .into(), + vuids: &["VUID-VkRenderingInfo-pDepthAttachment-06104"], + ..Default::default() + })); + } + } + (Some(depth_resolve_info), Some(stencil_resolve_info)) => { + if depth_resolve_info.image_layout != stencil_resolve_info.image_layout + && !device.enabled_features().separate_depth_stencil_layouts + { + return Err(Box::new(ValidationError { + problem: "`depth_attachment` and `stencil_attachment` are both \ + `Some`, and `depth_attachment.resolve_info` and \ + `stencil_attachment.resolve_info` are also both `Some`, and \ + `depth_attachment.resolve_info.image_layout` does not equal \ + `stencil_attachment.resolve_info.image_layout`" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "separate_depth_stencil_layouts", + )])]), + ..Default::default() + })); + } + + if !properties.independent_resolve.unwrap_or(false) + && depth_resolve_info.mode != stencil_resolve_info.mode + { + return Err(Box::new(ValidationError { + problem: "`depth_attachment` and `stencil_attachment` are both \ + `Some`, and `depth_attachment.resolve_info` and \ + `stencil_attachment.resolve_info` are also both `Some`, and \ + the `independent_resolve` device property is `false`, but \ + `depth_attachment.resolve_info.mode` does not equal \ + `stencil_attachment.resolve_info.mode`" + .into(), + vuids: &["VUID-VkRenderingInfo-pDepthAttachment-06105"], + ..Default::default() + })); + } + + if &depth_resolve_info.image_view != &stencil_resolve_info.image_view { + return Err(Box::new(ValidationError { + problem: "`depth_attachment` and `stencil_attachment` are both \ + `Some`, and `depth_attachment.resolve_info` and \ + `stencil_attachment.resolve_info` are also both `Some`, but \ + `depth_attachment.resolve_info.image_view` does not equal \ + `stencil_attachment.resolve_info.image_view`" + .into(), + vuids: &["VUID-VkRenderingInfo-pDepthAttachment-06086"], + ..Default::default() + })); + } } } } @@ -2397,6 +2904,127 @@ impl RenderingAttachmentInfo { _ne: crate::NonExhaustive(()), } } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + ref image_view, + image_layout, + ref resolve_info, + load_op, + store_op, + ref clear_value, + _ne, + } = self; + + image_layout + .validate_device(device) + .map_err(|err| ValidationError { + context: "image_layout".into(), + vuids: &["VUID-VkRenderingAttachmentInfo-imageLayout-parameter"], + ..ValidationError::from_requirement(err) + })?; + + load_op + .validate_device(device) + .map_err(|err| ValidationError { + context: "load_op".into(), + vuids: &["VUID-VkRenderingAttachmentInfo-loadOp-parameter"], + ..ValidationError::from_requirement(err) + })?; + + store_op + .validate_device(device) + .map_err(|err| ValidationError { + context: "store_op".into(), + vuids: &["VUID-VkRenderingAttachmentInfo-storeOp-parameter"], + ..ValidationError::from_requirement(err) + })?; + + if matches!( + image_layout, + ImageLayout::Undefined + | ImageLayout::ShaderReadOnlyOptimal + | ImageLayout::TransferSrcOptimal + | ImageLayout::TransferDstOptimal + | ImageLayout::Preinitialized + | ImageLayout::PresentSrc + ) { + return Err(Box::new(ValidationError { + context: "image_layout".into(), + problem: "is `ImageLayout::Undefined`, \ + `ImageLayout::ShaderReadOnlyOptimal`, \ + `ImageLayout::TransferSrcOptimal`, \ + `ImageLayout::TransferDstOptimal`, \ + `ImageLayout::Preinitialized` or \ + `ImageLayout::PresentSrc`" + .into(), + vuids: &[ + "VUID-VkRenderingAttachmentInfo-imageView-06135", + "VUID-VkRenderingAttachmentInfo-imageView-06145", + ], + ..Default::default() + })); + } + + if let Some(resolve_info) = resolve_info { + resolve_info + .validate(device) + .map_err(|err| err.add_context("resolve_info"))?; + + let &RenderingAttachmentResolveInfo { + mode: _, + image_view: ref resolve_image_view, + image_layout: _, + } = resolve_info; + + if image_view.image().samples() == SampleCount::Sample1 { + return Err(Box::new(ValidationError { + problem: "`resolve_info` is `Some`, but \ + `image_view.image().samples()` is `SampleCount::Sample1`" + .into(), + vuids: &["VUID-VkRenderingAttachmentInfo-imageView-06132"], + ..Default::default() + })); + } + + if image_view.format() != resolve_image_view.format() { + return Err(Box::new(ValidationError { + problem: "`resolve_info.image_view.format()` does not equal \ + `image_view.format()`" + .into(), + vuids: &["VUID-VkRenderingAttachmentInfo-imageView-06134"], + ..Default::default() + })); + } + } + + match (clear_value, load_op == AttachmentLoadOp::Clear) { + (None, false) => (), + (None, true) => { + return Err(Box::new(ValidationError { + problem: "`load_op` is `AttachmentLoadOp::Clear`, but \ + `clear_value` is `None`" + .into(), + ..Default::default() + })); + } + (Some(_), false) => { + return Err(Box::new(ValidationError { + problem: "`load_op` is not `AttachmentLoadOp::Clear`, but \ + `clear_value` is `Some`" + .into(), + ..Default::default() + })); + } + (Some(clear_value), true) => { + clear_value + .validate(device) + .map_err(|err| err.add_context("clear_value"))?; + } + }; + + Ok(()) + } } /// Parameters to specify the resolve behavior of an attachment. @@ -2437,6 +3065,100 @@ impl RenderingAttachmentResolveInfo { image_layout, } } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + mode, + ref image_view, + image_layout, + } = self; + + mode.validate_device(device) + .map_err(|err| ValidationError { + context: "mode".into(), + vuids: &["VUID-VkRenderingAttachmentInfo-resolveMode-parameter"], + ..ValidationError::from_requirement(err) + })?; + + image_layout + .validate_device(device) + .map_err(|err| ValidationError { + context: "image_layout".into(), + vuids: &["VUID-VkRenderingAttachmentInfo-resolveImageLayout-parameter"], + ..ValidationError::from_requirement(err) + })?; + + if let Some(numeric_format) = image_view.format().numeric_format_color() { + match numeric_format.numeric_type() { + NumericType::Float => { + if mode != ResolveMode::Average { + return Err(Box::new(ValidationError { + problem: "`image_view.format()` is a floating-point color format, but \ + `mode` is not `ResolveMode::Average`" + .into(), + vuids: &["VUID-VkRenderingAttachmentInfo-imageView-06129"], + ..Default::default() + })); + } + } + NumericType::Int | NumericType::Uint => { + if mode != ResolveMode::SampleZero { + return Err(Box::new(ValidationError { + problem: "`image_view.format()` is an integer color format, but \ + `mode` is not `ResolveMode::SampleZero`" + .into(), + vuids: &["VUID-VkRenderingAttachmentInfo-imageView-06130"], + ..Default::default() + })); + } + } + } + } + + if image_view.image().samples() != SampleCount::Sample1 { + return Err(Box::new(ValidationError { + context: "image_view.image().samples()".into(), + problem: "is not `SampleCount::Sample1`".into(), + vuids: &["VUID-VkRenderingAttachmentInfo-imageView-06133"], + ..Default::default() + })); + } + + if matches!( + image_layout, + ImageLayout::Undefined + | ImageLayout::ShaderReadOnlyOptimal + | ImageLayout::TransferSrcOptimal + | ImageLayout::TransferDstOptimal + | ImageLayout::Preinitialized + | ImageLayout::PresentSrc + | ImageLayout::DepthStencilReadOnlyOptimal + | ImageLayout::DepthReadOnlyOptimal + | ImageLayout::StencilReadOnlyOptimal + ) { + return Err(Box::new(ValidationError { + context: "image_layout".into(), + problem: "is `ImageLayout::Undefined`, \ + `ImageLayout::ShaderReadOnlyOptimal`, \ + `ImageLayout::TransferSrcOptimal`, \ + `ImageLayout::TransferDstOptimal`, \ + `ImageLayout::Preinitialized`, \ + `ImageLayout::PresentSrc`, \ + `ImageLayout::DepthStencilReadOnlyOptimal`, \ + `ImageLayout::DepthReadOnlyOptimal` or \ + `ImageLayout::StencilReadOnlyOptimal`" + .into(), + vuids: &[ + "VUID-VkRenderingAttachmentInfo-imageView-06136", + "VUID-VkRenderingAttachmentInfo-imageView-06137", + "VUID-VkRenderingAttachmentInfo-imageView-06146", + ], + ..Default::default() + })); + } + + Ok(()) + } } /// Clear attachment type, used in [`clear_attachments`] command. @@ -2460,6 +3182,29 @@ pub enum ClearAttachment { DepthStencil((f32, u32)), } +impl ClearAttachment { + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + if let ClearAttachment::Depth(depth) | ClearAttachment::DepthStencil((depth, _)) = self { + if !(0.0..=1.0).contains(depth) + && !device.enabled_extensions().ext_depth_range_unrestricted + { + return Err(Box::new(ValidationError { + problem: "is `ClearAttachment::Depth` or `ClearAttachment::DepthStencil`, and \ + the depth value is not between 0.0 and 1.0 inclusive" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "ext_depth_range_unrestricted", + )])]), + vuids: &["VUID-VkClearDepthStencilValue-depth-00022"], + ..Default::default() + })); + } + } + + Ok(()) + } +} + impl From for ash::vk::ClearAttachment { #[inline] fn from(v: ClearAttachment) -> Self { @@ -2516,530 +3261,3 @@ pub struct ClearRect { /// The range of array layers to be cleared. pub array_layers: Range, } - -/// Error that can happen when recording a render pass command. -#[derive(Clone, Debug)] -pub enum RenderPassError { - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, - - /// A framebuffer image did not have the required usage enabled. - AttachmentImageMissingUsage { - attachment_index: u32, - usage: &'static str, - }, - - /// One of the elements of `render_pass_extent` is zero, but no attachment images were given to - /// calculate the extent from. - AutoExtentAttachmentsEmpty, - - /// `layer_count` is zero, but no attachment images were given to calculate the number of layers - /// from. - AutoLayersAttachmentsEmpty, - - /// A clear attachment value is not compatible with the attachment's format. - ClearAttachmentNotCompatible { - clear_attachment: ClearAttachment, - attachment_format: Option, - }, - - /// A clear value for a render pass attachment is missing. - ClearValueMissing { attachment_index: u32 }, - - /// A clear value provided for a render pass attachment is not compatible with the attachment's - /// format. - ClearValueNotCompatible { - clear_value: ClearValue, - attachment_index: u32, - attachment_format: Format, - }, - - /// An attachment clear value specifies a `color_attachment` index that is not less than the - /// number of color attachments in the subpass. - ColorAttachmentIndexOutOfRange { - color_attachment_index: u32, - num_color_attachments: u32, - }, - - /// A color attachment has a layout that is not supported. - ColorAttachmentLayoutInvalid { attachment_index: u32 }, - - /// A color attachment is missing the `color_attachment` usage. - ColorAttachmentMissingUsage { attachment_index: u32 }, - - /// A color resolve attachment has a `format` value different from the corresponding color - /// attachment. - ColorAttachmentResolveFormatMismatch { attachment_index: u32 }, - - /// A color resolve attachment has a layout that is not supported. - ColorAttachmentResolveLayoutInvalid { attachment_index: u32 }, - - /// A color resolve attachment has a resolve mode that is not supported. - ColorAttachmentResolveModeNotSupported { attachment_index: u32 }, - - /// A color resolve attachment has a `samples` value other than [`SampleCount::Sample1`]. - ColorAttachmentResolveMultisampled { attachment_index: u32 }, - - /// A color attachment has a `samples` value that is different from the first - /// color attachment. - ColorAttachmentSamplesMismatch { attachment_index: u32 }, - - /// A color attachment with a resolve attachment has a `samples` value of - /// [`SampleCount::Sample1`]. - ColorAttachmentWithResolveNotMultisampled { attachment_index: u32 }, - - /// The contents `SubpassContents::SecondaryCommandBuffers` is not allowed inside a secondary - /// command buffer. - ContentsForbiddenInSecondaryCommandBuffer, - - /// The depth attachment has a format that does not support that usage. - DepthAttachmentFormatUsageNotSupported, - - /// The depth attachment has a layout that is not supported. - DepthAttachmentLayoutInvalid, - - /// The depth attachment is missing the `depth_stencil_attachment` usage. - DepthAttachmentMissingUsage, - - /// The depth resolve attachment has a `format` value different from the corresponding depth - /// attachment. - DepthAttachmentResolveFormatMismatch, - - /// The depth resolve attachment has a layout that is not supported. - DepthAttachmentResolveLayoutInvalid, - - /// The depth resolve attachment has a resolve mode that is not supported. - DepthAttachmentResolveModeNotSupported, - - /// The depth resolve attachment has a `samples` value other than [`SampleCount::Sample1`]. - DepthAttachmentResolveMultisampled, - - /// The depth attachment has a `samples` value that is different from the first - /// color attachment. - DepthAttachmentSamplesMismatch, - - /// The depth attachment has a resolve attachment and has a `samples` value of - /// [`SampleCount::Sample1`]. - DepthAttachmentWithResolveNotMultisampled, - - /// The depth and stencil attachments have different image views. - DepthStencilAttachmentImageViewMismatch, - - /// The depth and stencil resolve attachments have different image views. - DepthStencilAttachmentResolveImageViewMismatch, - - /// The combination of depth and stencil resolve modes is not supported by the device. - DepthStencilAttachmentResolveModesNotSupported, - - /// Operation forbidden inside a render pass. - ForbiddenInsideRenderPass, - - /// Operation forbidden outside a render pass. - ForbiddenOutsideRenderPass, - - /// Operation forbidden inside a render pass instance that was begun with `begin_rendering`. - ForbiddenWithBeginRendering, - - /// Operation forbidden inside a render pass instance that was begun with `begin_render_pass`. - ForbiddenWithBeginRenderPass, - - /// Operation forbidden inside a render pass instance that is inherited by a secondary command - /// buffer. - ForbiddenWithInheritedRenderPass, - - /// Operation forbidden inside a render subpass with the specified contents. - ForbiddenWithSubpassContents { contents: SubpassContents }, - - /// The framebuffer is not compatible with the render pass. - FramebufferNotCompatible, - - /// The `max_color_attachments` limit has been exceeded. - MaxColorAttachmentsExceeded { - color_attachment_count: u32, - max: u32, - }, - - /// The `max_multiview_view_count` limit has been exceeded. - MaxMultiviewViewCountExceeded { view_count: u32, max: u32 }, - - /// The render pass uses multiview, but `layer_count` was not 0 or 1. - MultiviewLayersInvalid, - - /// The render pass uses multiview, and in a clear rectangle, `array_layers` was not `0..1`. - MultiviewRectArrayLayersInvalid { rect_index: usize }, - - /// Tried to advance to the next subpass, but there are no subpasses remaining in the render - /// pass. - NoSubpassesRemaining { current_subpass: u32 }, - - /// The queue family doesn't allow this operation. - NotSupportedByQueueFamily, - - /// A query is active that conflicts with the current operation. - QueryIsActive, - - /// A clear rectangle's `array_layers` is empty. - RectArrayLayersEmpty { rect_index: usize }, - - /// A clear rectangle's `array_layers` is outside the range of layers of the attachments. - RectArrayLayersOutOfBounds { rect_index: usize }, - - /// A clear rectangle's `extent` is zero. - RectExtentZero { rect_index: usize }, - - /// A clear rectangle's `offset` and `extent` are outside the render area of the render pass - /// instance. - RectOutOfBounds { rect_index: usize }, - - /// The render area's `offset` and `extent` are outside the extent of the framebuffer. - RenderAreaOutOfBounds, - - /// The stencil attachment has a format that does not support that usage. - StencilAttachmentFormatUsageNotSupported, - - /// The stencil attachment has a layout that is not supported. - StencilAttachmentLayoutInvalid, - - /// The stencil attachment is missing the `depth_stencil_attachment` usage. - StencilAttachmentMissingUsage, - - /// The stencil resolve attachment has a `format` value different from the corresponding stencil - /// attachment. - StencilAttachmentResolveFormatMismatch, - - /// The stencil resolve attachment has a layout that is not supported. - StencilAttachmentResolveLayoutInvalid, - - /// The stencil resolve attachment has a resolve mode that is not supported. - StencilAttachmentResolveModeNotSupported, - - /// The stencil resolve attachment has a `samples` value other than [`SampleCount::Sample1`]. - StencilAttachmentResolveMultisampled, - - /// The stencil attachment has a `samples` value that is different from the first - /// color attachment or the depth attachment. - StencilAttachmentSamplesMismatch, - - /// The stencil attachment has a resolve attachment and has a `samples` value of - /// [`SampleCount::Sample1`]. - StencilAttachmentWithResolveNotMultisampled, - - /// Tried to end a render pass with subpasses still remaining in the render pass. - SubpassesRemaining { - current_subpass: u32, - remaining_subpasses: u32, - }, -} - -impl Error for RenderPassError {} - -impl Display for RenderPassError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::AttachmentImageMissingUsage { - attachment_index, - usage, - } => write!( - f, - "the framebuffer image attached to attachment index {} did not have the required \ - usage {} enabled", - attachment_index, usage, - ), - Self::AutoExtentAttachmentsEmpty => write!( - f, - "one of the elements of `render_pass_extent` is zero, but no attachment images \ - were given to calculate the extent from", - ), - Self::AutoLayersAttachmentsEmpty => write!( - f, - "`layer_count` is zero, but no attachment images were given to calculate the \ - number of layers from", - ), - Self::ClearAttachmentNotCompatible { - clear_attachment, - attachment_format, - } => write!( - f, - "a clear attachment value ({:?}) is not compatible with the attachment's format \ - ({:?})", - clear_attachment, attachment_format, - ), - Self::ClearValueMissing { attachment_index } => write!( - f, - "a clear value for render pass attachment {} is missing", - attachment_index, - ), - Self::ClearValueNotCompatible { - clear_value, - attachment_index, - attachment_format, - } => write!( - f, - "a clear value ({:?}) provided for render pass attachment {} is not compatible \ - with the attachment's format ({:?})", - clear_value, attachment_index, attachment_format, - ), - Self::ColorAttachmentIndexOutOfRange { - color_attachment_index, - num_color_attachments, - } => write!( - f, - "an attachment clear value specifies a `color_attachment` index {} that is not \ - less than the number of color attachments in the subpass ({})", - color_attachment_index, num_color_attachments, - ), - Self::ColorAttachmentLayoutInvalid { attachment_index } => write!( - f, - "color attachment {} has a layout that is not supported", - attachment_index, - ), - Self::ColorAttachmentMissingUsage { attachment_index } => write!( - f, - "color attachment {} is missing the `color_attachment` usage", - attachment_index, - ), - Self::ColorAttachmentResolveFormatMismatch { attachment_index } => write!( - f, - "color attachment {} has a `format` value different from the corresponding color \ - attachment", - attachment_index, - ), - Self::ColorAttachmentResolveLayoutInvalid { attachment_index } => write!( - f, - "color resolve attachment {} has a layout that is not supported", - attachment_index, - ), - Self::ColorAttachmentResolveModeNotSupported { attachment_index } => write!( - f, - "color resolve attachment {} has a resolve mode that is not supported", - attachment_index, - ), - Self::ColorAttachmentResolveMultisampled { attachment_index } => write!( - f, - "color resolve attachment {} has a `samples` value other than \ - `SampleCount::Sample1`", - attachment_index, - ), - Self::ColorAttachmentSamplesMismatch { attachment_index } => write!( - f, - "color attachment {} has a `samples` value that is different from the first color \ - attachment", - attachment_index, - ), - Self::ColorAttachmentWithResolveNotMultisampled { attachment_index } => write!( - f, - "color attachment {} with a resolve attachment has a `samples` value of \ - `SampleCount::Sample1`", - attachment_index, - ), - Self::ContentsForbiddenInSecondaryCommandBuffer => write!( - f, - "the contents `SubpassContents::SecondaryCommandBuffers` is not allowed inside a \ - secondary command buffer", - ), - Self::DepthAttachmentFormatUsageNotSupported => write!( - f, - "the depth attachment has a format that does not support that usage", - ), - Self::DepthAttachmentLayoutInvalid => { - write!(f, "the depth attachment has a layout that is not supported") - } - Self::DepthAttachmentMissingUsage => write!( - f, - "the depth attachment is missing the `depth_stencil_attachment` usage", - ), - Self::DepthAttachmentResolveFormatMismatch => write!( - f, - "the depth resolve attachment has a `format` value different from the \ - corresponding depth attachment", - ), - Self::DepthAttachmentResolveLayoutInvalid => write!( - f, - "the depth resolve attachment has a layout that is not supported", - ), - Self::DepthAttachmentResolveModeNotSupported => write!( - f, - "the depth resolve attachment has a resolve mode that is not supported", - ), - Self::DepthAttachmentResolveMultisampled => write!( - f, - "the depth resolve attachment has a `samples` value other than \ - `SampleCount::Sample1`", - ), - Self::DepthAttachmentSamplesMismatch => write!( - f, - "the depth attachment has a `samples` value that is different from the first color \ - attachment", - ), - Self::DepthAttachmentWithResolveNotMultisampled => write!( - f, - "the depth attachment has a resolve attachment and has a `samples` value of \ - `SampleCount::Sample1`", - ), - Self::DepthStencilAttachmentImageViewMismatch => write!( - f, - "the depth and stencil attachments have different image views", - ), - Self::DepthStencilAttachmentResolveImageViewMismatch => write!( - f, - "the depth and stencil resolve attachments have different image views", - ), - Self::DepthStencilAttachmentResolveModesNotSupported => write!( - f, - "the combination of depth and stencil resolve modes is not supported by the device", - ), - Self::ForbiddenInsideRenderPass => { - write!(f, "operation forbidden inside a render pass") - } - Self::ForbiddenOutsideRenderPass => { - write!(f, "operation forbidden outside a render pass") - } - Self::ForbiddenWithBeginRendering => write!( - f, - "operation forbidden inside a render pass instance that was begun with \ - `begin_rendering`", - ), - Self::ForbiddenWithBeginRenderPass => write!( - f, - "operation forbidden inside a render pass instance that was begun with \ - `begin_render_pass`", - ), - Self::ForbiddenWithInheritedRenderPass => write!( - f, - "operation forbidden inside a render pass instance that is inherited by a \ - secondary command buffer", - ), - Self::ForbiddenWithSubpassContents { - contents: subpass_contents, - } => write!( - f, - "operation forbidden inside a render subpass with contents {:?}", - subpass_contents, - ), - Self::FramebufferNotCompatible => { - write!(f, "the framebuffer is not compatible with the render pass") - } - Self::MaxColorAttachmentsExceeded { .. } => { - write!(f, "the `max_color_attachments` limit has been exceeded") - } - Self::MaxMultiviewViewCountExceeded { .. } => { - write!(f, "the `max_multiview_view_count` limit has been exceeded") - } - Self::MultiviewLayersInvalid => write!( - f, - "the render pass uses multiview, but `layer_count` was not 0 or 1", - ), - Self::MultiviewRectArrayLayersInvalid { rect_index } => write!( - f, - "the render pass uses multiview, and in clear rectangle index {}, `array_layers` \ - was not `0..1`", - rect_index, - ), - Self::NoSubpassesRemaining { current_subpass } => write!( - f, - "tried to advance to the next subpass after subpass {}, but there are no subpasses \ - remaining in the render pass", - current_subpass, - ), - Self::NotSupportedByQueueFamily => { - write!(f, "the queue family doesn't allow this operation") - } - Self::QueryIsActive => write!( - f, - "a query is active that conflicts with the current operation", - ), - Self::RectArrayLayersEmpty { rect_index } => write!( - f, - "clear rectangle index {} `array_layers` is empty", - rect_index, - ), - Self::RectArrayLayersOutOfBounds { rect_index } => write!( - f, - "clear rectangle index {} `array_layers` is outside the range of layers of the \ - attachments", - rect_index, - ), - Self::RectExtentZero { rect_index } => { - write!(f, "clear rectangle index {} `extent` is zero", rect_index) - } - Self::RectOutOfBounds { rect_index } => write!( - f, - "clear rectangle index {} `offset` and `extent` are outside the render area of the \ - render pass instance", - rect_index, - ), - Self::RenderAreaOutOfBounds => write!( - f, - "the render area's `offset` and `extent` are outside the extent of the framebuffer", - ), - Self::StencilAttachmentFormatUsageNotSupported => write!( - f, - "the stencil attachment has a format that does not support that usage", - ), - Self::StencilAttachmentLayoutInvalid => write!( - f, - "the stencil attachment has a layout that is not supported", - ), - Self::StencilAttachmentMissingUsage => write!( - f, - "the stencil attachment is missing the `depth_stencil_attachment` usage", - ), - Self::StencilAttachmentResolveFormatMismatch => write!( - f, - "the stencil resolve attachment has a `format` value different from the \ - corresponding stencil attachment", - ), - Self::StencilAttachmentResolveLayoutInvalid => write!( - f, - "the stencil resolve attachment has a layout that is not supported", - ), - Self::StencilAttachmentResolveModeNotSupported => write!( - f, - "the stencil resolve attachment has a resolve mode that is not supported", - ), - Self::StencilAttachmentResolveMultisampled => write!( - f, - "the stencil resolve attachment has a `samples` value other than \ - `SampleCount::Sample1`", - ), - Self::StencilAttachmentSamplesMismatch => write!( - f, - "the stencil attachment has a `samples` value that is different from the first \ - color attachment", - ), - Self::StencilAttachmentWithResolveNotMultisampled => write!( - f, - "the stencil attachment has a resolve attachment and has a `samples` value of \ - `SampleCount::Sample1`", - ), - Self::SubpassesRemaining { - current_subpass, - remaining_subpasses, - } => write!( - f, - "tried to end a render pass at subpass {}, with {} subpasses still remaining in \ - the render pass", - current_subpass, remaining_subpasses, - ), - } - } -} - -impl From for RenderPassError { - fn from(err: RequirementNotMet) -> Self { - Self::RequirementNotMet { - required_for: err.required_for, - requires_one_of: err.requires_one_of, - } - } -} diff --git a/vulkano/src/command_buffer/commands/secondary.rs b/vulkano/src/command_buffer/commands/secondary.rs index 68384557..3c41ab65 100644 --- a/vulkano/src/command_buffer/commands/secondary.rs +++ b/vulkano/src/command_buffer/commands/secondary.rs @@ -12,23 +12,16 @@ use crate::{ allocator::CommandBufferAllocator, auto::{RenderPassStateType, Resource, ResourceUseRef2}, sys::UnsafeCommandBufferBuilder, - AutoCommandBufferBuilder, CommandBufferExecError, CommandBufferInheritanceRenderPassType, + AutoCommandBufferBuilder, CommandBufferInheritanceRenderPassType, CommandBufferLevel, ResourceInCommand, SecondaryCommandBufferAbstract, SecondaryCommandBufferBufferUsage, SecondaryCommandBufferImageUsage, SecondaryCommandBufferResourcesUsage, SubpassContents, }, device::{DeviceOwned, QueueFlags}, - format::Format, - image::SampleCount, - query::{QueryControlFlags, QueryPipelineStatisticFlags, QueryType}, - Requires, RequiresAllOf, RequiresOneOf, SafeDeref, VulkanObject, + query::QueryType, + Requires, RequiresAllOf, RequiresOneOf, SafeDeref, ValidationError, VulkanObject, }; use smallvec::{smallvec, SmallVec}; -use std::{ - cmp::min, - error::Error, - fmt::{Display, Error as FmtError, Formatter}, - sync::Arc, -}; +use std::{cmp::min, iter, ops::Deref, sync::Arc}; /// # Commands to execute a secondary command buffer inside a primary command buffer. /// @@ -46,15 +39,11 @@ where pub fn execute_commands( &mut self, command_buffer: Arc, - ) -> Result<&mut Self, ExecuteCommandsError> { + ) -> Result<&mut Self, Box> { let command_buffer = DropUnlockCommandBuffer::new(command_buffer)?; - self.validate_execute_commands(&command_buffer, 0)?; + self.validate_execute_commands(iter::once(&**command_buffer))?; - unsafe { - self.execute_commands_locked(smallvec![command_buffer]); - } - - Ok(self) + unsafe { Ok(self.execute_commands_locked(smallvec![command_buffer])) } } /// Executes multiple secondary command buffers in a vector. @@ -66,289 +55,405 @@ where pub fn execute_commands_from_vec( &mut self, command_buffers: Vec>, - ) -> Result<&mut Self, ExecuteCommandsError> { + ) -> Result<&mut Self, Box> { let command_buffers: SmallVec<[_; 4]> = command_buffers .into_iter() .map(DropUnlockCommandBuffer::new) .collect::>()?; - for (command_buffer_index, command_buffer) in command_buffers.iter().enumerate() { - self.validate_execute_commands(command_buffer, command_buffer_index as u32)?; - } + self.validate_execute_commands(command_buffers.iter().map(|cb| &***cb))?; - unsafe { - self.execute_commands_locked(command_buffers); - } - - Ok(self) + unsafe { Ok(self.execute_commands_locked(command_buffers)) } } - fn validate_execute_commands( + fn validate_execute_commands<'a>( &self, - command_buffer: &dyn SecondaryCommandBufferAbstract, - command_buffer_index: u32, - ) -> Result<(), ExecuteCommandsError> { - // VUID-vkCmdExecuteCommands-commonparent - assert_eq!(self.device(), command_buffer.device()); - - let queue_family_properties = self.queue_family_properties(); - - // VUID-vkCmdExecuteCommands-commandBuffer-cmdpool - if !queue_family_properties - .queue_flags - .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) - { - return Err(ExecuteCommandsError::NotSupportedByQueueFamily); - } - - // TODO: - // VUID-vkCmdExecuteCommands-pCommandBuffers-00094 + command_buffers: impl Iterator + Clone, + ) -> Result<(), Box> { + self.inner + .validate_execute_commands(command_buffers.clone())?; if let Some(render_pass_state) = &self.builder_state.render_pass { - // VUID-vkCmdExecuteCommands-contents-06018 - // VUID-vkCmdExecuteCommands-flags-06024 if render_pass_state.contents != SubpassContents::SecondaryCommandBuffers { - return Err(ExecuteCommandsError::ForbiddenWithSubpassContents { - contents: render_pass_state.contents, - }); - } - - // VUID-vkCmdExecuteCommands-pCommandBuffers-00096 - let inheritance_render_pass = command_buffer - .inheritance_info() - .render_pass - .as_ref() - .ok_or(ExecuteCommandsError::RenderPassInheritanceRequired { - command_buffer_index, - })?; - - match (&render_pass_state.render_pass, inheritance_render_pass) { - ( - RenderPassStateType::BeginRenderPass(state), - CommandBufferInheritanceRenderPassType::BeginRenderPass(inheritance_info), - ) => { - // VUID-vkCmdExecuteCommands-pBeginInfo-06020 - if !inheritance_info - .subpass - .render_pass() - .is_compatible_with(state.subpass.render_pass()) - { - return Err(ExecuteCommandsError::RenderPassNotCompatible { - command_buffer_index, - }); - } - - // VUID-vkCmdExecuteCommands-pCommandBuffers-06019 - if inheritance_info.subpass.index() != state.subpass.index() { - return Err(ExecuteCommandsError::RenderPassSubpassMismatch { - command_buffer_index, - required_subpass: state.subpass.index(), - inherited_subpass: inheritance_info.subpass.index(), - }); - } - - // VUID-vkCmdExecuteCommands-pCommandBuffers-00099 - if let Some(framebuffer) = &inheritance_info.framebuffer { - if framebuffer != state.framebuffer.as_ref().unwrap() { - return Err(ExecuteCommandsError::RenderPassFramebufferMismatch { - command_buffer_index, - }); - } - } - } - ( - RenderPassStateType::BeginRendering(_), - CommandBufferInheritanceRenderPassType::BeginRendering(inheritance_info), - ) => { - let attachments = render_pass_state.attachments.as_ref().unwrap(); - - // VUID-vkCmdExecuteCommands-colorAttachmentCount-06027 - if inheritance_info.color_attachment_formats.len() - != attachments.color_attachments.len() - { - return Err( - ExecuteCommandsError::RenderPassColorAttachmentCountMismatch { - command_buffer_index, - required_count: attachments.color_attachments.len() as u32, - inherited_count: inheritance_info.color_attachment_formats.len() - as u32, - }, - ); - } - - for (color_attachment_index, image_view, inherited_format) in attachments - .color_attachments - .iter() - .zip(inheritance_info.color_attachment_formats.iter().copied()) - .enumerate() - .filter_map(|(i, (a, f))| a.as_ref().map(|a| (i as u32, &a.image_view, f))) - { - let required_format = image_view.format(); - - // VUID-vkCmdExecuteCommands-imageView-06028 - if Some(required_format) != inherited_format { - return Err( - ExecuteCommandsError::RenderPassColorAttachmentFormatMismatch { - command_buffer_index, - color_attachment_index, - required_format, - inherited_format, - }, - ); - } - - // VUID-vkCmdExecuteCommands-pNext-06035 - if image_view.image().samples() != inheritance_info.rasterization_samples { - return Err( - ExecuteCommandsError::RenderPassColorAttachmentSamplesMismatch { - command_buffer_index, - color_attachment_index, - required_samples: image_view.image().samples(), - inherited_samples: inheritance_info.rasterization_samples, - }, - ); - } - } - - if let Some((image_view, format)) = attachments - .depth_attachment - .as_ref() - .map(|a| (&a.image_view, inheritance_info.depth_attachment_format)) - { - // VUID-vkCmdExecuteCommands-pDepthAttachment-06029 - if Some(image_view.format()) != format { - return Err( - ExecuteCommandsError::RenderPassDepthAttachmentFormatMismatch { - command_buffer_index, - required_format: image_view.format(), - inherited_format: format, - }, - ); - } - - // VUID-vkCmdExecuteCommands-pNext-06036 - if image_view.image().samples() != inheritance_info.rasterization_samples { - return Err( - ExecuteCommandsError::RenderPassDepthAttachmentSamplesMismatch { - command_buffer_index, - required_samples: image_view.image().samples(), - inherited_samples: inheritance_info.rasterization_samples, - }, - ); - } - } - - if let Some((image_view, format)) = attachments - .stencil_attachment - .as_ref() - .map(|a| (&a.image_view, inheritance_info.stencil_attachment_format)) - { - // VUID-vkCmdExecuteCommands-pStencilAttachment-06030 - if Some(image_view.format()) != format { - return Err( - ExecuteCommandsError::RenderPassStencilAttachmentFormatMismatch { - command_buffer_index, - required_format: image_view.format(), - inherited_format: format, - }, - ); - } - - // VUID-vkCmdExecuteCommands-pNext-06037 - if image_view.image().samples() != inheritance_info.rasterization_samples { - return Err( - ExecuteCommandsError::RenderPassStencilAttachmentSamplesMismatch { - command_buffer_index, - required_samples: image_view.image().samples(), - inherited_samples: inheritance_info.rasterization_samples, - }, - ); - } - } - - // VUID-vkCmdExecuteCommands-viewMask-06031 - if inheritance_info.view_mask != render_pass_state.rendering_info.view_mask { - return Err(ExecuteCommandsError::RenderPassViewMaskMismatch { - command_buffer_index, - required_view_mask: render_pass_state.rendering_info.view_mask, - inherited_view_mask: inheritance_info.view_mask, - }); - } - } - _ => { - // VUID-vkCmdExecuteCommands-pBeginInfo-06025 - return Err(ExecuteCommandsError::RenderPassTypeMismatch { - command_buffer_index, - }); - } - } - - // TODO: - // VUID-vkCmdExecuteCommands-commandBuffer-06533 - // VUID-vkCmdExecuteCommands-commandBuffer-06534 - // VUID-vkCmdExecuteCommands-pCommandBuffers-06535 - // VUID-vkCmdExecuteCommands-pCommandBuffers-06536 - } else { - // VUID-vkCmdExecuteCommands-pCommandBuffers-00100 - if command_buffer.inheritance_info().render_pass.is_some() { - return Err(ExecuteCommandsError::RenderPassInheritanceForbidden { - command_buffer_index, - }); + return Err(Box::new(ValidationError { + problem: "a render pass instance is active, but its current subpass contents \ + is `SubpassContents::SecondaryCommandBuffers`" + .into(), + vuids: &[ + "VUID-vkCmdExecuteCommands-contents-06018", + "VUID-vkCmdExecuteCommands-flags-06024", + ], + ..Default::default() + })); } } - // VUID-vkCmdExecuteCommands-commandBuffer-00101 if !self.builder_state.queries.is_empty() && !self.device().enabled_features().inherited_queries { - return Err(ExecuteCommandsError::RequirementNotMet { - required_for: "`AutoCommandBufferBuilder::execute_commands` when a query is active", + return Err(Box::new(ValidationError { + problem: "a query is active".into(), requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( "inherited_queries", )])]), - }); + vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-00101"], + ..Default::default() + })); } - for state in self.builder_state.queries.values() { - match state.query_pool.query_type() { - QueryType::Occlusion => { - // VUID-vkCmdExecuteCommands-commandBuffer-00102 - let inherited_flags = command_buffer.inheritance_info().occlusion_query.ok_or( - ExecuteCommandsError::OcclusionQueryInheritanceRequired { - command_buffer_index, - }, - )?; + for (command_buffer_index, command_buffer) in command_buffers.enumerate() { + if let Some(render_pass_state) = &self.builder_state.render_pass { + let inheritance_render_pass = command_buffer + .inheritance_info() + .render_pass + .as_ref() + .ok_or(Box::new(ValidationError { + problem: format!( + "a render pass instance is active, but \ + `command_buffers[{}].inheritance_info().render_pass` is `None`", + command_buffer_index + ) + .into(), + vuids: &["VUID-vkCmdExecuteCommands-pCommandBuffers-00096"], + ..Default::default() + }))?; - let inherited_flags_vk = ash::vk::QueryControlFlags::from(inherited_flags); - let state_flags_vk = ash::vk::QueryControlFlags::from(state.flags); + match (&render_pass_state.render_pass, inheritance_render_pass) { + ( + RenderPassStateType::BeginRenderPass(state), + CommandBufferInheritanceRenderPassType::BeginRenderPass(inheritance_info), + ) => { + if !inheritance_info + .subpass + .render_pass() + .is_compatible_with(state.subpass.render_pass()) + { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().render_pass\ + .subpass.render_pass()", + command_buffer_index + ) + .into(), + problem: "is not compatible with the current render pass instance" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-pBeginInfo-06020"], + ..Default::default() + })); + } - // VUID-vkCmdExecuteCommands-commandBuffer-00103 - if inherited_flags_vk & state_flags_vk != state_flags_vk { - return Err(ExecuteCommandsError::OcclusionQueryFlagsNotSuperset { - command_buffer_index, - required_flags: state.flags, - inherited_flags, - }); + if inheritance_info.subpass.index() != state.subpass.index() { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().render_pass\ + .subpass.index()", + command_buffer_index + ) + .into(), + problem: "is not equal to the index of the current \ + subpass instance" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-pCommandBuffers-06019"], + ..Default::default() + })); + } + + if let Some(framebuffer) = &inheritance_info.framebuffer { + if framebuffer != state.framebuffer.as_ref().unwrap() { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().render_pass\ + .framebuffer", + command_buffer_index + ) + .into(), + problem: "is `Some`, but is not equal to the framebuffer of \ + the current render pass instance" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-pCommandBuffers-00099"], + ..Default::default() + })); + } + } + } + ( + RenderPassStateType::BeginRendering(_), + CommandBufferInheritanceRenderPassType::BeginRendering(inheritance_info), + ) => { + let attachments = render_pass_state.attachments.as_ref().unwrap(); + + if inheritance_info.color_attachment_formats.len() + != attachments.color_attachments.len() + { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().render_pass\ + .color_attachment_formats.len()", + command_buffer_index + ) + .into(), + problem: "is not equal to the number of color attachments in the \ + current subpass instance" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-colorAttachmentCount-06027"], + ..Default::default() + })); + } + + for (color_attachment_index, image_view, inherited_format) in (attachments + .color_attachments + .iter()) + .zip(inheritance_info.color_attachment_formats.iter().copied()) + .enumerate() + .filter_map(|(i, (a, f))| a.as_ref().map(|a| (i as u32, &a.image_view, f))) + { + let required_format = image_view.format(); + + if Some(required_format) != inherited_format { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().render_pass\ + .color_attachment_formats[{}]", + command_buffer_index, color_attachment_index + ) + .into(), + problem: "is not equal to the format of the \ + corresponding color attachment in the current subpass \ + instance" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-imageView-06028"], + ..Default::default() + })); + } + + if image_view.image().samples() + != inheritance_info.rasterization_samples + { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().render_pass\ + .rasterization_samples", + command_buffer_index, + ) + .into(), + problem: "is not equal to the number of samples of the \ + attachments in the current subpass instance" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-pNext-06035"], + ..Default::default() + })); + } + } + + if let Some((image_view, format)) = attachments + .depth_attachment + .as_ref() + .map(|a| (&a.image_view, inheritance_info.depth_attachment_format)) + { + if Some(image_view.format()) != format { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().render_pass\ + .depth_attachment_format", + command_buffer_index + ) + .into(), + problem: "is not equal to the format of the \ + depth attachment in the current subpass instance" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-pDepthAttachment-06029"], + ..Default::default() + })); + } + + if image_view.image().samples() + != inheritance_info.rasterization_samples + { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().render_pass\ + .rasterization_samples", + command_buffer_index, + ) + .into(), + problem: "is not equal to the number of samples of the \ + attachments in the current subpass instance" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-pNext-06036"], + ..Default::default() + })); + } + } + + if let Some((image_view, format)) = attachments + .stencil_attachment + .as_ref() + .map(|a| (&a.image_view, inheritance_info.stencil_attachment_format)) + { + if Some(image_view.format()) != format { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().render_pass\ + .stencil_attachment_format", + command_buffer_index + ) + .into(), + problem: "is not equal to the format of the \ + stencil attachment in the current subpass instance" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-pStencilAttachment-06030"], + ..Default::default() + })); + } + + if image_view.image().samples() + != inheritance_info.rasterization_samples + { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().render_pass\ + .rasterization_samples", + command_buffer_index, + ) + .into(), + problem: "is not equal to the number of samples of the \ + attachments in the current subpass instance" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-pNext-06037"], + ..Default::default() + })); + } + } + + if inheritance_info.view_mask != render_pass_state.rendering_info.view_mask + { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().render_pass\ + .view_mask", + command_buffer_index, + ) + .into(), + problem: "is not equal to the `view_mask` of the current subpass \ + instance" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-viewMask-06031"], + ..Default::default() + })); + } + } + ( + RenderPassStateType::BeginRenderPass(_), + CommandBufferInheritanceRenderPassType::BeginRendering(_), + ) => { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().render_pass", + command_buffer_index + ) + .into(), + problem: "is `CommandBufferInheritanceRenderPassType::\ + BeginRendering`, but the current render pass instance was begun \ + with `begin_render_pass`" + .into(), + // vuids? + ..Default::default() + })); + } + ( + RenderPassStateType::BeginRendering(_), + CommandBufferInheritanceRenderPassType::BeginRenderPass(_), + ) => { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().render_pass", + command_buffer_index + ) + .into(), + problem: "is `CommandBufferInheritanceRenderPassType::\ + BeginRenderPass`, but the current render pass instance was begun \ + with `begin_rendering`" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-pBeginInfo-06025"], + ..Default::default() + })); } } - &QueryType::PipelineStatistics(state_flags) => { - let inherited_flags = command_buffer.inheritance_info().query_statistics_flags; - let inherited_flags_vk = - ash::vk::QueryPipelineStatisticFlags::from(inherited_flags); - let state_flags_vk = ash::vk::QueryPipelineStatisticFlags::from(state_flags); - // VUID-vkCmdExecuteCommands-commandBuffer-00104 - if inherited_flags_vk & state_flags_vk != state_flags_vk { - return Err( - ExecuteCommandsError::PipelineStatisticsQueryFlagsNotSuperset { - command_buffer_index, - required_flags: state_flags, - inherited_flags, - }, - ); - } + // TODO: + // VUID-vkCmdExecuteCommands-commandBuffer-06533 + // VUID-vkCmdExecuteCommands-commandBuffer-06534 + // VUID-vkCmdExecuteCommands-pCommandBuffers-06535 + // VUID-vkCmdExecuteCommands-pCommandBuffers-06536 + } else { + if command_buffer.inheritance_info().render_pass.is_some() { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().render_pass", + command_buffer_index + ) + .into(), + problem: "is `Some`, but a render pass instance is not active".into(), + vuids: &["VUID-vkCmdExecuteCommands-pCommandBuffers-00100"], + ..Default::default() + })); + } + } + + for state in self.builder_state.queries.values() { + match state.query_pool.query_type() { + QueryType::Occlusion => { + let inherited_flags = command_buffer + .inheritance_info() + .occlusion_query + .ok_or(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().occlusion_query", + command_buffer_index + ) + .into(), + problem: "is `None`, but an occlusion query is currently active" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-00102"], + ..Default::default() + }))?; + + if !inherited_flags.contains(state.flags) { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().occlusion_query", + command_buffer_index + ) + .into(), + problem: "is not a superset of the flags of the active \ + occlusion query" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-00103"], + ..Default::default() + })); + } + } + &QueryType::PipelineStatistics(state_flags) => { + let inherited_flags = + command_buffer.inheritance_info().query_statistics_flags; + + if !inherited_flags.contains(state_flags) { + return Err(Box::new(ValidationError { + context: format!( + "command_buffers[{}].inheritance_info().query_statistics_flags", + command_buffer_index + ) + .into(), + problem: "is not a superset of the flags of the active \ + pipeline statistics query" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-00104"], + ..Default::default() + })); + } + } + _ => (), } - _ => (), } } @@ -358,13 +463,6 @@ where // VUID-vkCmdExecuteCommands-pCommandBuffers-00093 // VUID-vkCmdExecuteCommands-pCommandBuffers-00105 - // VUID-vkCmdExecuteCommands-bufferlevel - // Ensured by the type of the impl block. - - // VUID-vkCmdExecuteCommands-pCommandBuffers-00088 - // VUID-vkCmdExecuteCommands-pCommandBuffers-00089 - // Ensured by the SecondaryCommandBuffer trait. - Ok(()) } @@ -462,14 +560,66 @@ impl UnsafeCommandBufferBuilder where A: CommandBufferAllocator, { - /// Calls `vkCmdExecuteCommands` on the builder. - /// - /// Does nothing if the list of command buffers is empty, as it would be a no-op and isn't a - /// valid usage of the command anyway. - #[inline] pub unsafe fn execute_commands( &mut self, command_buffers: &[Arc], + ) -> Result<&mut Self, Box> { + self.validate_execute_commands(command_buffers.iter().map(Deref::deref))?; + + Ok(self.execute_commands_unchecked(command_buffers)) + } + + fn validate_execute_commands<'a>( + &self, + command_buffers: impl Iterator, + ) -> Result<(), Box> { + if self.level() != CommandBufferLevel::Primary { + return Err(Box::new(ValidationError { + problem: "this command buffer is not a primary command buffer".into(), + vuids: &["VUID-vkCmdExecuteCommands-bufferlevel"], + ..Default::default() + })); + } + + if !self + .queue_family_properties() + .queue_flags + .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + transfer, graphics or compute operations" + .into(), + vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + for (_command_buffer_index, command_buffer) in command_buffers.enumerate() { + // VUID-vkCmdExecuteCommands-commonparent + assert_eq!(self.device(), command_buffer.device()); + + // TODO: + // VUID-vkCmdExecuteCommands-pCommandBuffers-00094 + } + + // TODO: + // VUID-vkCmdExecuteCommands-pCommandBuffers-00091 + // VUID-vkCmdExecuteCommands-pCommandBuffers-00092 + // VUID-vkCmdExecuteCommands-pCommandBuffers-00093 + // VUID-vkCmdExecuteCommands-pCommandBuffers-00105 + + // VUID-vkCmdExecuteCommands-pCommandBuffers-00088 + // VUID-vkCmdExecuteCommands-pCommandBuffers-00089 + // Ensured by the SecondaryCommandBuffer trait. + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn execute_commands_unchecked( + &mut self, + command_buffers: &[Arc], ) -> &mut Self { if command_buffers.is_empty() { return self; @@ -529,7 +679,7 @@ struct DropUnlockCommandBuffer(Arc); impl DropUnlockCommandBuffer { fn new( command_buffer: Arc, - ) -> Result { + ) -> Result> { command_buffer.lock_record()?; Ok(Self(command_buffer)) } @@ -551,335 +701,3 @@ impl Drop for DropUnlockCommandBuffer { } } } - -/// Error that can happen when executing a secondary command buffer. -#[derive(Clone, Debug)] -pub enum ExecuteCommandsError { - ExecError(CommandBufferExecError), - - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, - - /// Operation forbidden inside a render subpass with the specified contents. - ForbiddenWithSubpassContents { - contents: SubpassContents, - }, - - /// The queue family doesn't allow this operation. - NotSupportedByQueueFamily, - - /// A render pass is active, but a command buffer does not contain occlusion query inheritance - /// info. - OcclusionQueryInheritanceRequired { - command_buffer_index: u32, - }, - - /// The inherited occlusion query control flags of a command buffer are not a superset of the - /// currently active flags. - OcclusionQueryFlagsNotSuperset { - command_buffer_index: u32, - required_flags: QueryControlFlags, - inherited_flags: QueryControlFlags, - }, - - /// The inherited pipeline statistics query flags of a command buffer are not a superset of the - /// currently active flags. - PipelineStatisticsQueryFlagsNotSuperset { - command_buffer_index: u32, - required_flags: QueryPipelineStatisticFlags, - inherited_flags: QueryPipelineStatisticFlags, - }, - - /// The inherited color attachment count of a command buffer does not match the current - /// attachment count. - RenderPassColorAttachmentCountMismatch { - command_buffer_index: u32, - required_count: u32, - inherited_count: u32, - }, - - /// The inherited format of a color attachment of a command buffer does not match the current - /// attachment format. - RenderPassColorAttachmentFormatMismatch { - command_buffer_index: u32, - color_attachment_index: u32, - required_format: Format, - inherited_format: Option, - }, - - /// The inherited sample count of a color attachment of a command buffer does not match the - /// current attachment sample count. - RenderPassColorAttachmentSamplesMismatch { - command_buffer_index: u32, - color_attachment_index: u32, - required_samples: SampleCount, - inherited_samples: SampleCount, - }, - - /// The inherited format of the depth attachment of a command buffer does not match the current - /// attachment format. - RenderPassDepthAttachmentFormatMismatch { - command_buffer_index: u32, - required_format: Format, - inherited_format: Option, - }, - - /// The inherited sample count of the depth attachment of a command buffer does not match the - /// current attachment sample count. - RenderPassDepthAttachmentSamplesMismatch { - command_buffer_index: u32, - required_samples: SampleCount, - inherited_samples: SampleCount, - }, - - /// The inherited framebuffer of a command buffer does not match the current framebuffer. - RenderPassFramebufferMismatch { - command_buffer_index: u32, - }, - - /// A render pass is active, but a command buffer does not contain render pass inheritance info. - RenderPassInheritanceRequired { - command_buffer_index: u32, - }, - - /// A render pass is not active, but a command buffer contains render pass inheritance info. - RenderPassInheritanceForbidden { - command_buffer_index: u32, - }, - - /// The inherited render pass of a command buffer is not compatible with the current render - /// pass. - RenderPassNotCompatible { - command_buffer_index: u32, - }, - - /// The inherited format of the stencil attachment of a command buffer does not match the - /// current attachment format. - RenderPassStencilAttachmentFormatMismatch { - command_buffer_index: u32, - required_format: Format, - inherited_format: Option, - }, - - /// The inherited sample count of the stencil attachment of a command buffer does not match the - /// current attachment sample count. - RenderPassStencilAttachmentSamplesMismatch { - command_buffer_index: u32, - required_samples: SampleCount, - inherited_samples: SampleCount, - }, - - /// The inherited subpass index of a command buffer does not match the current subpass index. - RenderPassSubpassMismatch { - command_buffer_index: u32, - required_subpass: u32, - inherited_subpass: u32, - }, - - /// The inherited render pass of a command buffer is of the wrong type. - RenderPassTypeMismatch { - command_buffer_index: u32, - }, - - /// The inherited view mask of a command buffer does not match the current view mask. - RenderPassViewMaskMismatch { - command_buffer_index: u32, - required_view_mask: u32, - inherited_view_mask: u32, - }, -} - -impl Error for ExecuteCommandsError {} - -impl Display for ExecuteCommandsError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::ExecError(err) => Display::fmt(err, f), - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - Self::ForbiddenWithSubpassContents { - contents: subpass_contents, - } => write!( - f, - "operation forbidden inside a render subpass with contents {:?}", - subpass_contents, - ), - Self::NotSupportedByQueueFamily => { - write!(f, "the queue family doesn't allow this operation") - } - Self::OcclusionQueryInheritanceRequired { - command_buffer_index, - } => write!( - f, - "a render pass is active, but command buffer {} does not contain occlusion query \ - inheritance info", - command_buffer_index, - ), - Self::OcclusionQueryFlagsNotSuperset { - command_buffer_index, - required_flags, - inherited_flags, - } => write!( - f, - "the inherited occlusion query control flags ({:?}) of command buffer {} are not a \ - superset of the currently active flags ({:?})", - inherited_flags, command_buffer_index, required_flags, - ), - Self::PipelineStatisticsQueryFlagsNotSuperset { - command_buffer_index, - required_flags, - inherited_flags, - } => write!( - f, - "the inherited pipeline statistics query flags ({:?}) of command buffer {} are not \ - a superset of the currently active flags ({:?})", - inherited_flags, command_buffer_index, required_flags, - ), - Self::RenderPassColorAttachmentCountMismatch { - command_buffer_index, - required_count, - inherited_count, - } => write!( - f, - "the inherited color attachment count ({}) of command buffer {} does not match the \ - current attachment count ({})", - inherited_count, command_buffer_index, required_count, - ), - Self::RenderPassColorAttachmentFormatMismatch { - command_buffer_index, - color_attachment_index, - required_format, - inherited_format, - } => write!( - f, - "the inherited format ({:?}) of color attachment {} of command buffer {} does not \ - match the current attachment format ({:?})", - inherited_format, color_attachment_index, command_buffer_index, required_format, - ), - Self::RenderPassColorAttachmentSamplesMismatch { - command_buffer_index, - color_attachment_index, - required_samples, - inherited_samples, - } => write!( - f, - "the inherited sample count ({:?}) of color attachment {} of command buffer {} \ - does not match the current attachment sample count ({:?})", - inherited_samples, color_attachment_index, command_buffer_index, required_samples, - ), - Self::RenderPassDepthAttachmentFormatMismatch { - command_buffer_index, - required_format, - inherited_format, - } => write!( - f, - "the inherited format ({:?}) of the depth attachment of command buffer {} does not \ - match the current attachment format ({:?})", - inherited_format, command_buffer_index, required_format, - ), - Self::RenderPassDepthAttachmentSamplesMismatch { - command_buffer_index, - required_samples, - inherited_samples, - } => write!( - f, - "the inherited sample count ({:?}) of the depth attachment of command buffer {} \ - does not match the current attachment sample count ({:?})", - inherited_samples, command_buffer_index, required_samples, - ), - Self::RenderPassFramebufferMismatch { - command_buffer_index, - } => write!( - f, - "the inherited framebuffer of command buffer {} does not match the current \ - framebuffer", - command_buffer_index, - ), - Self::RenderPassInheritanceRequired { - command_buffer_index, - } => write!( - f, - "a render pass is active, but command buffer {} does not contain render pass \ - inheritance info", - command_buffer_index, - ), - Self::RenderPassInheritanceForbidden { - command_buffer_index, - } => write!( - f, - "a render pass is not active, but command buffer {} contains render pass \ - inheritance info", - command_buffer_index, - ), - Self::RenderPassNotCompatible { - command_buffer_index, - } => write!( - f, - "the inherited render pass of command buffer {} is not compatible with the current \ - render pass", - command_buffer_index, - ), - Self::RenderPassStencilAttachmentFormatMismatch { - command_buffer_index, - required_format, - inherited_format, - } => write!( - f, - "the inherited format ({:?}) of the stencil attachment of command buffer {} does \ - not match the current attachment format ({:?})", - inherited_format, command_buffer_index, required_format, - ), - Self::RenderPassStencilAttachmentSamplesMismatch { - command_buffer_index, - required_samples, - inherited_samples, - } => write!( - f, - "the inherited sample count ({:?}) of the stencil attachment of command buffer {} \ - does not match the current attachment sample count ({:?})", - inherited_samples, command_buffer_index, required_samples, - ), - Self::RenderPassSubpassMismatch { - command_buffer_index, - required_subpass, - inherited_subpass, - } => write!( - f, - "the inherited subpass index ({}) of command buffer {} does not match the current \ - subpass index ({})", - inherited_subpass, command_buffer_index, required_subpass, - ), - Self::RenderPassTypeMismatch { - command_buffer_index, - } => write!( - f, - "the inherited render pass of command buffer {} is of the wrong type", - command_buffer_index, - ), - Self::RenderPassViewMaskMismatch { - command_buffer_index, - required_view_mask, - inherited_view_mask, - } => write!( - f, - "the inherited view mask ({}) of command buffer {} does not match the current view \ - mask ({})", - inherited_view_mask, command_buffer_index, required_view_mask, - ), - } - } -} - -impl From for ExecuteCommandsError { - fn from(val: CommandBufferExecError) -> Self { - Self::ExecError(val) - } -} diff --git a/vulkano/src/command_buffer/commands/sync.rs b/vulkano/src/command_buffer/commands/sync.rs index b7013dc2..3657c892 100644 --- a/vulkano/src/command_buffer/commands/sync.rs +++ b/vulkano/src/command_buffer/commands/sync.rs @@ -9,12 +9,12 @@ use crate::{ command_buffer::{allocator::CommandBufferAllocator, sys::UnsafeCommandBufferBuilder}, - device::DeviceOwned, + device::{DeviceOwned, QueueFlags}, sync::{ event::Event, BufferMemoryBarrier, DependencyFlags, DependencyInfo, ImageMemoryBarrier, MemoryBarrier, PipelineStages, }, - Version, VulkanObject, + Requires, RequiresAllOf, RequiresOneOf, ValidationError, Version, VulkanObject, }; use smallvec::SmallVec; use std::{ptr, sync::Arc}; @@ -23,23 +23,199 @@ impl UnsafeCommandBufferBuilder where A: CommandBufferAllocator, { - #[inline] - pub unsafe fn pipeline_barrier(&mut self, dependency_info: &DependencyInfo) -> &mut Self { + pub unsafe fn pipeline_barrier( + &mut self, + dependency_info: &DependencyInfo, + ) -> Result<&mut Self, Box> { + self.validate_pipeline_barrier(dependency_info)?; + + Ok(self.pipeline_barrier_unchecked(dependency_info)) + } + + fn validate_pipeline_barrier( + &self, + dependency_info: &DependencyInfo, + ) -> Result<(), Box> { + let queue_family_properties = self.queue_family_properties(); + + if !queue_family_properties.queue_flags.intersects( + QueueFlags::TRANSFER + | QueueFlags::GRAPHICS + | QueueFlags::COMPUTE + | QueueFlags::VIDEO_DECODE + | QueueFlags::VIDEO_ENCODE, + ) { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + transfer, graphics, compute, video decode or video encode operations" + .into(), + vuids: &["VUID-vkCmdPipelineBarrier2-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + dependency_info + .validate(self.device()) + .map_err(|err| err.add_context("dependency_info"))?; + + let &DependencyInfo { + dependency_flags: _, + ref memory_barriers, + ref buffer_memory_barriers, + ref image_memory_barriers, + _ne, + } = dependency_info; + + let supported_pipeline_stages = PipelineStages::from(queue_family_properties.queue_flags); + + for (barrier_index, memory_barrier) in memory_barriers.iter().enumerate() { + let &MemoryBarrier { + src_stages, + src_access: _, + dst_stages, + dst_access: _, + _ne: _, + } = memory_barrier; + + if !supported_pipeline_stages.contains(src_stages) { + return Err(Box::new(ValidationError { + context: format!( + "dependency_info.memory_barriers[{}].src_stages", + barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of the \ + command buffer" + .into(), + vuids: &["VUID-vkCmdPipelineBarrier2-srcStageMask-03849"], + ..Default::default() + })); + } + + if !supported_pipeline_stages.contains(dst_stages) { + return Err(Box::new(ValidationError { + context: format!( + "dependency_info.memory_barriers[{}].dst_stages", + barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of the \ + command buffer" + .into(), + vuids: &["VUID-vkCmdPipelineBarrier2-dstStageMask-03850"], + ..Default::default() + })); + } + } + + for (barrier_index, buffer_memory_barrier) in buffer_memory_barriers.iter().enumerate() { + let &BufferMemoryBarrier { + src_stages, + src_access: _, + dst_stages, + dst_access: _, + queue_family_ownership_transfer: _, + buffer: _, + range: _, + _ne: _, + } = buffer_memory_barrier; + + if !supported_pipeline_stages.contains(src_stages) { + return Err(Box::new(ValidationError { + context: format!( + "dependency_info.buffer_memory_barriers[{}].src_stages", + barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of the \ + command buffer" + .into(), + vuids: &["VUID-vkCmdPipelineBarrier2-srcStageMask-03849"], + ..Default::default() + })); + } + + if !supported_pipeline_stages.contains(dst_stages) { + return Err(Box::new(ValidationError { + context: format!( + "dependency_info.buffer_memory_barriers[{}].dst_stages", + barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of the \ + command buffer" + .into(), + vuids: &["VUID-vkCmdPipelineBarrier2-dstStageMask-03850"], + ..Default::default() + })); + } + } + + for (barrier_index, image_memory_barrier) in image_memory_barriers.iter().enumerate() { + let &ImageMemoryBarrier { + src_stages, + src_access: _, + dst_stages, + dst_access: _, + old_layout: _, + new_layout: _, + queue_family_ownership_transfer: _, + image: _, + subresource_range: _, + _ne: _, + } = image_memory_barrier; + + if !supported_pipeline_stages.contains(src_stages) { + return Err(Box::new(ValidationError { + context: format!( + "dependency_info.image_memory_barriers[{}].src_stages", + barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of the \ + command buffer" + .into(), + vuids: &["VUID-vkCmdPipelineBarrier2-srcStageMask-03849"], + ..Default::default() + })); + } + + if !supported_pipeline_stages.contains(dst_stages) { + return Err(Box::new(ValidationError { + context: format!( + "dependency_info.image_memory_barriers[{}].dst_stages", + barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of the \ + command buffer" + .into(), + vuids: &["VUID-vkCmdPipelineBarrier2-dstStageMask-03850"], + ..Default::default() + })); + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn pipeline_barrier_unchecked( + &mut self, + dependency_info: &DependencyInfo, + ) -> &mut Self { if dependency_info.is_empty() { return self; } let &DependencyInfo { - mut dependency_flags, + dependency_flags, ref memory_barriers, ref buffer_memory_barriers, ref image_memory_barriers, _ne: _, } = dependency_info; - // TODO: Is this needed? - dependency_flags |= DependencyFlags::BY_REGION; - if self.device().enabled_features().synchronization2 { let memory_barriers_vk: SmallVec<[_; 2]> = memory_barriers .iter() @@ -287,12 +463,200 @@ where self } - /// Calls `vkCmdSetEvent` on the builder. - #[inline] pub unsafe fn set_event( &mut self, event: &Event, dependency_info: &DependencyInfo, + ) -> Result<&mut Self, Box> { + self.validate_set_event(event, dependency_info)?; + + Ok(self.set_event_unchecked(event, dependency_info)) + } + + fn validate_set_event( + &self, + event: &Event, + dependency_info: &DependencyInfo, + ) -> Result<(), Box> { + let queue_family_properties = self.queue_family_properties(); + + if !queue_family_properties.queue_flags.intersects( + QueueFlags::GRAPHICS + | QueueFlags::COMPUTE + | QueueFlags::VIDEO_DECODE + | QueueFlags::VIDEO_ENCODE, + ) { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics, compute, video decode or video encode operations" + .into(), + vuids: &["VUID-vkCmdSetEvent2-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + // VUID-vkCmdSetEvent2-commonparent + assert_eq!(self.device(), event.device()); + + dependency_info + .validate(self.device()) + .map_err(|err| err.add_context("dependency_info"))?; + + let &DependencyInfo { + dependency_flags, + ref memory_barriers, + ref buffer_memory_barriers, + ref image_memory_barriers, + _ne, + } = dependency_info; + + if !dependency_flags.is_empty() { + return Err(Box::new(ValidationError { + context: "dependency_info.dependency_flags".into(), + problem: "is not empty".into(), + vuids: &["VUID-vkCmdSetEvent2-dependencyFlags-03825"], + ..Default::default() + })); + } + + let supported_pipeline_stages = PipelineStages::from(queue_family_properties.queue_flags); + + for (barrier_index, memory_barrier) in memory_barriers.iter().enumerate() { + let &MemoryBarrier { + src_stages, + src_access: _, + dst_stages, + dst_access: _, + _ne: _, + } = memory_barrier; + + if !supported_pipeline_stages.contains(src_stages) { + return Err(Box::new(ValidationError { + context: format!( + "dependency_info.memory_barriers[{}].src_stages", + barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of the \ + command buffer" + .into(), + vuids: &["VUID-vkCmdSetEvent2-srcStageMask-03827"], + ..Default::default() + })); + } + + if !supported_pipeline_stages.contains(dst_stages) { + return Err(Box::new(ValidationError { + context: format!( + "dependency_info.memory_barriers[{}].dst_stages", + barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of the \ + command buffer" + .into(), + vuids: &["VUID-vkCmdSetEvent2-dstStageMask-03828"], + ..Default::default() + })); + } + } + + for (barrier_index, buffer_memory_barrier) in buffer_memory_barriers.iter().enumerate() { + let &BufferMemoryBarrier { + src_stages, + src_access: _, + dst_stages, + dst_access: _, + queue_family_ownership_transfer: _, + buffer: _, + range: _, + _ne: _, + } = buffer_memory_barrier; + + if !supported_pipeline_stages.contains(src_stages) { + return Err(Box::new(ValidationError { + context: format!( + "dependency_info.buffer_memory_barriers[{}].src_stages", + barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of the \ + command buffer" + .into(), + vuids: &["VUID-vkCmdSetEvent2-srcStageMask-03827"], + ..Default::default() + })); + } + + if !supported_pipeline_stages.contains(dst_stages) { + return Err(Box::new(ValidationError { + context: format!( + "dependency_info.buffer_memory_barriers[{}].dst_stages", + barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of the \ + command buffer" + .into(), + vuids: &["VUID-vkCmdSetEvent2-dstStageMask-03828"], + ..Default::default() + })); + } + } + + for (barrier_index, image_memory_barrier) in image_memory_barriers.iter().enumerate() { + let &ImageMemoryBarrier { + src_stages, + src_access: _, + dst_stages, + dst_access: _, + old_layout: _, + new_layout: _, + queue_family_ownership_transfer: _, + image: _, + subresource_range: _, + _ne: _, + } = image_memory_barrier; + + if !supported_pipeline_stages.contains(src_stages) { + return Err(Box::new(ValidationError { + context: format!( + "dependency_info.image_memory_barriers[{}].src_stages", + barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of the \ + command buffer" + .into(), + vuids: &["VUID-vkCmdSetEvent2-srcStageMask-03827"], + ..Default::default() + })); + } + + if !supported_pipeline_stages.contains(dst_stages) { + return Err(Box::new(ValidationError { + context: format!( + "dependency_info.image_memory_barriers[{}].dst_stages", + barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of the \ + command buffer" + .into(), + vuids: &["VUID-vkCmdSetEvent2-dstStageMask-03828"], + ..Default::default() + })); + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_event_unchecked( + &mut self, + event: &Event, + dependency_info: &DependencyInfo, ) -> &mut Self { let &DependencyInfo { mut dependency_flags, @@ -453,8 +817,193 @@ where self } - /// Calls `vkCmdWaitEvents` on the builder. - pub unsafe fn wait_events(&mut self, events: &[(Arc, DependencyInfo)]) -> &mut Self { + pub unsafe fn wait_events( + &mut self, + events: &[(Arc, DependencyInfo)], + ) -> Result<&mut Self, Box> { + self.validate_wait_events(events)?; + + Ok(self.wait_events_unchecked(events)) + } + + fn validate_wait_events( + &self, + events: &[(Arc, DependencyInfo)], + ) -> Result<(), Box> { + let queue_family_properties = self.queue_family_properties(); + + if !queue_family_properties.queue_flags.intersects( + QueueFlags::GRAPHICS + | QueueFlags::COMPUTE + | QueueFlags::VIDEO_DECODE + | QueueFlags::VIDEO_ENCODE, + ) { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics, compute, video decode or video encode operations" + .into(), + vuids: &["VUID-vkCmdWaitEvents2-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + for (event_index, (event, dependency_info)) in events.iter().enumerate() { + // VUID-vkCmdWaitEvents2-commandBuffer-cmdpool + assert_eq!(self.device(), event.device()); + + dependency_info + .validate(self.device()) + .map_err(|err| err.add_context(format!("events[{}].1", event_index)))?; + + let &DependencyInfo { + dependency_flags: _, + ref memory_barriers, + ref buffer_memory_barriers, + ref image_memory_barriers, + _ne, + } = dependency_info; + + let supported_pipeline_stages = + PipelineStages::from(queue_family_properties.queue_flags); + + for (barrier_index, memory_barrier) in memory_barriers.iter().enumerate() { + let &MemoryBarrier { + src_stages, + src_access: _, + dst_stages, + dst_access: _, + _ne: _, + } = memory_barrier; + + if !supported_pipeline_stages.contains(src_stages) { + return Err(Box::new(ValidationError { + context: format!( + "events[{}].1.memory_barriers[{}].src_stages", + event_index, barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of \ + the command buffer" + .into(), + vuids: &["VUID-vkCmdSetEvent2-srcStageMask-03827"], + ..Default::default() + })); + } + + if !supported_pipeline_stages.contains(dst_stages) { + return Err(Box::new(ValidationError { + context: format!( + "events[{}].1.memory_barriers[{}].dst_stages", + event_index, barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of \ + the command buffer" + .into(), + vuids: &["VUID-vkCmdSetEvent2-dstStageMask-03828"], + ..Default::default() + })); + } + } + + for (barrier_index, buffer_memory_barrier) in buffer_memory_barriers.iter().enumerate() + { + let &BufferMemoryBarrier { + src_stages, + src_access: _, + dst_stages, + dst_access: _, + queue_family_ownership_transfer: _, + buffer: _, + range: _, + _ne: _, + } = buffer_memory_barrier; + + if !supported_pipeline_stages.contains(src_stages) { + return Err(Box::new(ValidationError { + context: format!( + "events[{}].1.buffer_memory_barriers[{}].src_stages", + event_index, barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of \ + the command buffer" + .into(), + vuids: &["VUID-vkCmdSetEvent2-srcStageMask-03827"], + ..Default::default() + })); + } + + if !supported_pipeline_stages.contains(dst_stages) { + return Err(Box::new(ValidationError { + context: format!( + "events[{}].1.buffer_memory_barriers[{}].dst_stages", + event_index, barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of \ + the command buffer" + .into(), + vuids: &["VUID-vkCmdSetEvent2-dstStageMask-03828"], + ..Default::default() + })); + } + } + + for (barrier_index, image_memory_barrier) in image_memory_barriers.iter().enumerate() { + let &ImageMemoryBarrier { + src_stages, + src_access: _, + dst_stages, + dst_access: _, + old_layout: _, + new_layout: _, + queue_family_ownership_transfer: _, + image: _, + subresource_range: _, + _ne: _, + } = image_memory_barrier; + + if !supported_pipeline_stages.contains(src_stages) { + return Err(Box::new(ValidationError { + context: format!( + "events[{}].1.image_memory_barriers[{}].src_stages", + event_index, barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of \ + the command buffer" + .into(), + vuids: &["VUID-vkCmdSetEvent2-srcStageMask-03827"], + ..Default::default() + })); + } + + if !supported_pipeline_stages.contains(dst_stages) { + return Err(Box::new(ValidationError { + context: format!( + "events[{}].1.image_memory_barriers[{}].dst_stages", + event_index, barrier_index + ) + .into(), + problem: "contains stages that are not supported by the queue family of \ + the command buffer" + .into(), + vuids: &["VUID-vkCmdSetEvent2-dstStageMask-03828"], + ..Default::default() + })); + } + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn wait_events_unchecked( + &mut self, + events: &[(Arc, DependencyInfo)], + ) -> &mut Self { let fns = self.device().fns(); if self.device().enabled_features().synchronization2 { @@ -775,9 +1324,233 @@ where self } - /// Calls `vkCmdResetEvent` on the builder. - #[inline] - pub unsafe fn reset_event(&mut self, event: Arc, stages: PipelineStages) -> &mut Self { + pub unsafe fn reset_event( + &mut self, + event: &Event, + stages: PipelineStages, + ) -> Result<&mut Self, Box> { + self.validate_reset_event(event, stages)?; + + Ok(self.reset_event_unchecked(event, stages)) + } + + fn validate_reset_event( + &self, + event: &Event, + stages: PipelineStages, + ) -> Result<(), Box> { + if !self.queue_family_properties().queue_flags.intersects( + QueueFlags::GRAPHICS + | QueueFlags::COMPUTE + | QueueFlags::VIDEO_DECODE + | QueueFlags::VIDEO_ENCODE, + ) { + return Err(Box::new(ValidationError { + problem: "the queue family of the command buffer does not support \ + graphics, compute, video decode or video encode operations" + .into(), + vuids: &["VUID-vkCmdResetEvent2-commandBuffer-cmdpool"], + ..Default::default() + })); + } + + let device = self.device(); + + // VUID-vkCmdResetEvent2-commonparent + assert_eq!(device, event.device()); + + stages + .validate_device(device) + .map_err(|err| ValidationError { + context: "stages".into(), + vuids: &["VUID-vkCmdResetEvent2-stageMask-parameter"], + ..ValidationError::from_requirement(err) + })?; + + if !device.enabled_features().synchronization2 { + if stages.contains_flags2() { + return Err(Box::new(ValidationError { + context: "stages".into(), + problem: "contains flags from `VkPipelineStageFlagBits2`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "synchronization2", + )])]), + ..Default::default() + })); + } + } + + if !device.enabled_features().geometry_shader { + if stages.intersects(PipelineStages::GEOMETRY_SHADER) { + return Err(Box::new(ValidationError { + context: "stages".into(), + problem: "contains `PipelineStages::GEOMETRY_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "geometry_shader", + )])]), + vuids: &["VUID-vkCmdResetEvent2-stageMask-03929"], + })); + } + } + + if !device.enabled_features().tessellation_shader { + if stages.intersects( + PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER, + ) { + return Err(Box::new(ValidationError { + context: "stages".into(), + problem: "contains `PipelineStages::TESSELLATION_CONTROL_SHADER` or \ + `PipelineStages::TESSELLATION_EVALUATION_SHADER`" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "tessellation_shader", + )])]), + vuids: &["VUID-vkCmdResetEvent2-stageMask-03930"], + })); + } + } + + if !device.enabled_features().conditional_rendering { + if stages.intersects(PipelineStages::CONDITIONAL_RENDERING) { + return Err(Box::new(ValidationError { + context: "stages".into(), + problem: "contains `PipelineStages::CONDITIONAL_RENDERING`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "conditional_rendering", + )])]), + vuids: &["VUID-vkCmdResetEvent2-stageMask-03931"], + })); + } + } + + if !device.enabled_features().fragment_density_map { + if stages.intersects(PipelineStages::FRAGMENT_DENSITY_PROCESS) { + return Err(Box::new(ValidationError { + context: "stages".into(), + problem: "contains `PipelineStages::FRAGMENT_DENSITY_PROCESS`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "fragment_density_map", + )])]), + vuids: &["VUID-vkCmdResetEvent2-stageMask-03932"], + })); + } + } + + if !device.enabled_features().transform_feedback { + if stages.intersects(PipelineStages::TRANSFORM_FEEDBACK) { + return Err(Box::new(ValidationError { + context: "stages".into(), + problem: "contains `PipelineStages::TRANSFORM_FEEDBACK`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "transform_feedback", + )])]), + vuids: &["VUID-vkCmdResetEvent2-stageMask-03933"], + })); + } + } + + if !device.enabled_features().mesh_shader { + if stages.intersects(PipelineStages::MESH_SHADER) { + return Err(Box::new(ValidationError { + context: "stages".into(), + problem: "contains `PipelineStages::MESH_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "mesh_shader", + )])]), + vuids: &["VUID-vkCmdResetEvent2-stageMask-03934"], + })); + } + } + + if !device.enabled_features().task_shader { + if stages.intersects(PipelineStages::TASK_SHADER) { + return Err(Box::new(ValidationError { + context: "stages".into(), + problem: "contains `PipelineStages::TASK_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "task_shader", + )])]), + vuids: &["VUID-vkCmdResetEvent2-stageMask-03935"], + })); + } + } + + if !(device.enabled_features().attachment_fragment_shading_rate + || device.enabled_features().shading_rate_image) + { + if stages.intersects(PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT) { + return Err(Box::new(ValidationError { + context: "stages".into(), + problem: "contains `PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT`".into(), + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::Feature("attachment_fragment_shading_rate")]), + RequiresAllOf(&[Requires::Feature("shading_rate_image")]), + ]), + vuids: &["VUID-VkImageMemoryBarrier2-shadingRateImage-07316"], + })); + } + } + + if !device.enabled_features().subpass_shading { + if stages.intersects(PipelineStages::SUBPASS_SHADING) { + return Err(Box::new(ValidationError { + context: "stages".into(), + problem: "contains `PipelineStages::SUBPASS_SHADING`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "subpass_shading", + )])]), + vuids: &["VUID-vkCmdResetEvent2-stageMask-04957"], + })); + } + } + + if !device.enabled_features().invocation_mask { + if stages.intersects(PipelineStages::INVOCATION_MASK) { + return Err(Box::new(ValidationError { + context: "stages".into(), + problem: "contains `PipelineStages::INVOCATION_MASK`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "invocation_mask", + )])]), + vuids: &["VUID-vkCmdResetEvent2-stageMask-04995"], + })); + } + } + + if !(device.enabled_extensions().nv_ray_tracing + || device.enabled_features().ray_tracing_pipeline) + { + if stages.intersects(PipelineStages::RAY_TRACING_SHADER) { + return Err(Box::new(ValidationError { + context: "stages".into(), + problem: "contains `PipelineStages::RAY_TRACING_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "ray_tracing_pipeline", + )])]), + vuids: &["VUID-vkCmdResetEvent2-stageMask-07946"], + })); + } + } + + if stages.intersects(PipelineStages::HOST) { + return Err(Box::new(ValidationError { + context: "stages".into(), + problem: "contains `PipelineStages::HOST`".into(), + vuids: &["VUID-vkCmdResetEvent2-stageMask-03830"], + ..Default::default() + })); + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn reset_event_unchecked( + &mut self, + event: &Event, + stages: PipelineStages, + ) -> &mut Self { let fns = self.device().fns(); if self.device().enabled_features().synchronization2 { diff --git a/vulkano/src/command_buffer/mod.rs b/vulkano/src/command_buffer/mod.rs index 6bc9d24e..add6357c 100644 --- a/vulkano/src/command_buffer/mod.rs +++ b/vulkano/src/command_buffer/mod.rs @@ -91,11 +91,11 @@ //! queue.queue_family_index(), //! CommandBufferUsage::MultipleSubmit //! ).unwrap() -//! .begin_render_pass(render_pass_begin_info, SubpassContents::Inline).unwrap() -//! .bind_pipeline_graphics(graphics_pipeline.clone()) -//! .bind_vertex_buffers(0, vertex_buffer.clone()) +//! .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().unwrap() +//! .end_render_pass(Default::default()).unwrap() //! .build().unwrap(); //! //! let _future = cb.execute(queue.clone()); @@ -111,19 +111,8 @@ pub use self::{ auto::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer, SecondaryAutoCommandBuffer}, commands::{ - clear::{ClearColorImageInfo, ClearDepthStencilImageInfo, ClearError}, - copy::{ - BlitImageInfo, BufferCopy, BufferImageCopy, CopyBufferInfo, CopyBufferInfoTyped, - CopyBufferToImageInfo, CopyError, CopyErrorResource, CopyImageInfo, - CopyImageToBufferInfo, ImageBlit, ImageCopy, ImageResolve, ResolveImageInfo, - }, - pipeline::PipelineExecutionError, - query::QueryError, - render_pass::{ - ClearAttachment, ClearRect, RenderPassBeginInfo, RenderPassError, - RenderingAttachmentInfo, RenderingAttachmentResolveInfo, RenderingInfo, - }, - secondary::ExecuteCommandsError, + acceleration_structure::*, clear::*, copy::*, debug::*, dynamic_state::*, pipeline::*, + query::*, render_pass::*, secondary::*, sync::*, }, traits::{ CommandBufferExecError, CommandBufferExecFuture, PrimaryCommandBufferAbstract, diff --git a/vulkano/src/command_buffer/traits.rs b/vulkano/src/command_buffer/traits.rs index a71fcef0..727884d6 100644 --- a/vulkano/src/command_buffer/traits.rs +++ b/vulkano/src/command_buffer/traits.rs @@ -22,7 +22,7 @@ use crate::{ }, PipelineStages, }, - DeviceSize, SafeDeref, VulkanObject, + DeviceSize, SafeDeref, ValidationError, VulkanObject, }; use parking_lot::{Mutex, MutexGuard}; use std::{ @@ -172,7 +172,7 @@ pub unsafe trait SecondaryCommandBufferAbstract: /// and if so locks it. /// /// If you call this function, then you should call `unlock` afterwards. - fn lock_record(&self) -> Result<(), CommandBufferExecError>; + fn lock_record(&self) -> Result<(), Box>; /// Unlocks the command buffer. Should be called once for each call to `lock_record`. /// @@ -198,7 +198,7 @@ where (**self).inheritance_info() } - fn lock_record(&self) -> Result<(), CommandBufferExecError> { + fn lock_record(&self) -> Result<(), Box> { (**self).lock_record() } diff --git a/vulkano/src/format.rs b/vulkano/src/format.rs index 31ef7546..db63512a 100644 --- a/vulkano/src/format.rs +++ b/vulkano/src/format.rs @@ -94,11 +94,11 @@ //! method on a format. use crate::{ - device::physical::PhysicalDevice, + device::{physical::PhysicalDevice, Device}, image::{ImageAspects, ImageTiling}, macros::vulkan_bitflags, shader::spirv::ImageFormat, - DeviceSize, + DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, }; // Generated by build.rs @@ -276,9 +276,9 @@ impl ChromaSampling { } } -/// The numeric type that represents data of a format in memory. +/// The numeric format in memory of the components of a format. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum NumericType { +pub enum NumericFormat { /// Signed floating-point number. SFLOAT, /// Unsigned floating-point number. @@ -300,6 +300,38 @@ pub enum NumericType { SRGB, } +impl NumericFormat { + // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap47.html#formats-numericformat + pub const fn numeric_type(self) -> NumericType { + match self { + NumericFormat::SFLOAT + | NumericFormat::UFLOAT + | NumericFormat::SNORM + | NumericFormat::UNORM + | NumericFormat::SSCALED + | NumericFormat::USCALED + | NumericFormat::SRGB => NumericType::Float, + NumericFormat::SINT => NumericType::Int, + NumericFormat::UINT => NumericType::Uint, + } + } +} + +/// The numeric base type of a scalar value, in a format, a shader, or elsewhere. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum NumericType { + Float, + Int, + Uint, +} + +impl From for NumericType { + #[inline] + fn from(val: NumericFormat) -> Self { + val.numeric_type() + } +} + /// An opaque type that represents a format compatibility class. /// /// Two formats are compatible if their compatibility classes compare equal. @@ -417,6 +449,40 @@ pub enum ClearValue { DepthStencil((f32, u32)), } +impl ClearValue { + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + if let ClearValue::Depth(depth) | ClearValue::DepthStencil((depth, _)) = self { + if !(0.0..=1.0).contains(depth) + && !device.enabled_extensions().ext_depth_range_unrestricted + { + return Err(Box::new(ValidationError { + problem: "is `ClearValue::Depth` or `ClearValue::DepthStencil`, and \ + the depth value is not between 0.0 and 1.0 inclusive" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "ext_depth_range_unrestricted", + )])]), + vuids: &["VUID-VkClearDepthStencilValue-depth-00022"], + ..Default::default() + })); + } + } + + Ok(()) + } + + pub(crate) fn clear_value_type(&self) -> ClearValueType { + match self { + ClearValue::Float(_) => ClearValueType::Float, + ClearValue::Int(_) => ClearValueType::Int, + ClearValue::Uint(_) => ClearValueType::Uint, + ClearValue::Depth(_) => ClearValueType::Depth, + ClearValue::Stencil(_) => ClearValueType::Stencil, + ClearValue::DepthStencil(_) => ClearValueType::DepthStencil, + } + } +} + impl From for ash::vk::ClearValue { #[inline] fn from(val: ClearValue) -> Self { @@ -562,6 +628,16 @@ impl From<(f32, u32)> for ClearValue { } } +#[derive(Debug, PartialEq, Eq)] +pub(crate) enum ClearValueType { + Float, + Int, + Uint, + Depth, + Stencil, + DepthStencil, +} + /// A value that will be used to clear a color image. #[derive(Clone, Copy, Debug, PartialEq)] pub enum ClearColorValue { @@ -573,6 +649,17 @@ pub enum ClearColorValue { Uint([u32; 4]), } +impl ClearColorValue { + /// Returns the numeric type of the clear value. + pub fn numeric_type(&self) -> NumericType { + match self { + ClearColorValue::Float(_) => NumericType::Float, + ClearColorValue::Int(_) => NumericType::Int, + ClearColorValue::Uint(_) => NumericType::Uint, + } + } +} + impl From for ash::vk::ClearColorValue { #[inline] fn from(val: ClearColorValue) -> Self { diff --git a/vulkano/src/image/sampler/mod.rs b/vulkano/src/image/sampler/mod.rs index 6126824b..c6d72cb3 100644 --- a/vulkano/src/image/sampler/mod.rs +++ b/vulkano/src/image/sampler/mod.rs @@ -49,7 +49,7 @@ pub mod ycbcr; use self::ycbcr::SamplerYcbcrConversion; use crate::{ device::{Device, DeviceOwned, DeviceOwnedDebugWrapper}, - format::FormatFeatures, + format::{FormatFeatures, NumericType}, image::{ view::{ImageView, ImageViewType}, ImageAspects, @@ -57,7 +57,6 @@ use crate::{ instance::InstanceOwnedDebugWrapper, macros::{impl_id_counter, vulkan_enum}, pipeline::graphics::depth_stencil::CompareOp, - shader::ShaderScalarType, Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, VulkanError, VulkanObject, }; use std::{mem::MaybeUninit, num::NonZeroU64, ops::RangeInclusive, ptr, sync::Arc}; @@ -395,18 +394,18 @@ impl Sampler { if let Some(border_color) = self.border_color { let aspects = image_view.subresource_range().aspects; - let view_scalar_type = ShaderScalarType::from( + let view_numeric_type = NumericType::from( if aspects.intersects( ImageAspects::COLOR | ImageAspects::PLANE_0 | ImageAspects::PLANE_1 | ImageAspects::PLANE_2, ) { - image_view.format().type_color().unwrap() + image_view.format().numeric_format_color().unwrap() } else if aspects.intersects(ImageAspects::DEPTH) { - image_view.format().type_depth().unwrap() + image_view.format().numeric_format_depth().unwrap() } else if aspects.intersects(ImageAspects::STENCIL) { - image_view.format().type_stencil().unwrap() + image_view.format().numeric_format_stencil().unwrap() } else { // Per `ImageViewBuilder::aspects` and // VUID-VkDescriptorImageInfo-imageView-01976 @@ -421,10 +420,7 @@ impl Sampler { // The sampler borderColor is an integer type and the image view // format is not one of the VkFormat integer types or a stencil // component of a depth/stencil format. - if !matches!( - view_scalar_type, - ShaderScalarType::Sint | ShaderScalarType::Uint - ) { + if !matches!(view_numeric_type, NumericType::Int | NumericType::Uint) { return Err(Box::new(ValidationError { problem: "the sampler has an integer border color, and \ the image view does not have an integer format" @@ -439,7 +435,7 @@ impl Sampler { // The sampler borderColor is a float type and the image view // format is not one of the VkFormat float types or a depth // component of a depth/stencil format. - if !matches!(view_scalar_type, ShaderScalarType::Float) { + if !matches!(view_numeric_type, NumericType::Float) { return Err(Box::new(ValidationError { problem: "the sampler has an floating-point border color, and \ the image view does not have a floating-point format" diff --git a/vulkano/src/image/sampler/ycbcr.rs b/vulkano/src/image/sampler/ycbcr.rs index 154b7888..6a83ff30 100644 --- a/vulkano/src/image/sampler/ycbcr.rs +++ b/vulkano/src/image/sampler/ycbcr.rs @@ -111,7 +111,7 @@ use crate::{ device::{Device, DeviceOwned}, - format::{ChromaSampling, Format, FormatFeatures, NumericType}, + format::{ChromaSampling, Format, FormatFeatures, NumericFormat}, image::sampler::{ComponentMapping, ComponentSwizzle, Filter}, instance::InstanceOwnedDebugWrapper, macros::{impl_id_counter, vulkan_enum}, @@ -518,8 +518,8 @@ impl SamplerYcbcrConversionCreateInfo { })?; if !format - .type_color() - .map_or(false, |ty| ty == NumericType::UNORM) + .numeric_format_color() + .map_or(false, |ty| ty == NumericFormat::UNORM) { return Err(Box::new(ValidationError { context: "format".into(), diff --git a/vulkano/src/image/view.rs b/vulkano/src/image/view.rs index 6b4004cf..5be6f1bc 100644 --- a/vulkano/src/image/view.rs +++ b/vulkano/src/image/view.rs @@ -1170,7 +1170,7 @@ unsafe fn get_format_features(view_format: Format, image: &Image) -> FormatFeatu || device.enabled_extensions().khr_format_feature_flags2) && matches!(image.tiling(), ImageTiling::Linear | ImageTiling::Optimal) { - if view_format.type_color().is_none() + if view_format.numeric_format_color().is_none() && format_features.intersects(FormatFeatures::SAMPLED_IMAGE) { format_features |= FormatFeatures::SAMPLED_IMAGE_DEPTH_COMPARISON; diff --git a/vulkano/src/pipeline/compute.rs b/vulkano/src/pipeline/compute.rs index 867bdd2f..d58155b3 100644 --- a/vulkano/src/pipeline/compute.rs +++ b/vulkano/src/pipeline/compute.rs @@ -493,12 +493,14 @@ mod tests { ) .unwrap(); cbb.bind_pipeline_compute(pipeline.clone()) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Compute, pipeline.layout().clone(), 0, set, ) + .unwrap() .dispatch([1, 1, 1]) .unwrap(); let cb = cbb.build().unwrap(); diff --git a/vulkano/src/pipeline/graphics/mod.rs b/vulkano/src/pipeline/graphics/mod.rs index bcf2c70d..82e09ec4 100644 --- a/vulkano/src/pipeline/graphics/mod.rs +++ b/vulkano/src/pipeline/graphics/mod.rs @@ -74,7 +74,7 @@ use super::{ }; use crate::{ device::{Device, DeviceOwned, DeviceOwnedDebugWrapper}, - format::{FormatFeatures, NumericType}, + format::FormatFeatures, image::{ImageAspect, ImageAspects}, instance::InstanceOwnedDebugWrapper, macros::impl_id_counter, @@ -91,7 +91,7 @@ use crate::{ }, shader::{ DescriptorBindingRequirements, FragmentShaderExecution, FragmentTestsStages, - ShaderExecution, ShaderScalarType, ShaderStage, ShaderStages, + ShaderExecution, ShaderStage, ShaderStages, }, Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, VulkanError, VulkanObject, }; @@ -2702,27 +2702,18 @@ impl GraphicsPipelineCreateInfo { // same location but in different components. let shader_type = element.ty.base_type; - let attribute_type = attribute_desc.format.type_color().unwrap(); + let attribute_type = attribute_desc + .format + .numeric_format_color() + .unwrap() + .numeric_type(); // VUID? - if !matches!( - (shader_type, attribute_type), - ( - ShaderScalarType::Float, - NumericType::SFLOAT - | NumericType::UFLOAT - | NumericType::SNORM - | NumericType::UNORM - | NumericType::SSCALED - | NumericType::USCALED - | NumericType::SRGB, - ) | (ShaderScalarType::Sint, NumericType::SINT) - | (ShaderScalarType::Uint, NumericType::UINT) - ) { + if shader_type != attribute_type { return Err(Box::new(ValidationError { problem: format!( "`vertex_input_state.attributes[{}].format` has a different \ - scalar type than the vertex shader input variable with \ + numeric type than the vertex shader input variable with \ location {0}", location, ) diff --git a/vulkano/src/render_pass/framebuffer.rs b/vulkano/src/render_pass/framebuffer.rs index 84d0ba39..2a338066 100644 --- a/vulkano/src/render_pass/framebuffer.rs +++ b/vulkano/src/render_pass/framebuffer.rs @@ -496,8 +496,8 @@ impl FramebufferCreateInfo { for image_view in attachments.iter() { let image_view_extent = image_view.image().extent(); - let image_view_array_layers = image_view.subresource_range().array_layers.end - - image_view.subresource_range().array_layers.start; + let image_view_array_layers = + image_view.subresource_range().array_layers.len() as u32; auto_extent[0] = auto_extent[0].min(image_view_extent[0]); auto_extent[1] = auto_extent[1].min(image_view_extent[1]); diff --git a/vulkano/src/render_pass/mod.rs b/vulkano/src/render_pass/mod.rs index 5691d5b9..d54cbe3d 100644 --- a/vulkano/src/render_pass/mod.rs +++ b/vulkano/src/render_pass/mod.rs @@ -28,7 +28,7 @@ pub use self::framebuffer::{Framebuffer, FramebufferCreateFlags, FramebufferCreateInfo}; use crate::{ device::{Device, DeviceOwned, QueueFlags}, - format::{Format, FormatFeatures}, + format::{ClearValueType, Format, FormatFeatures, NumericType}, image::{ImageAspects, ImageLayout, SampleCount}, instance::InstanceOwnedDebugWrapper, macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum, vulkan_enum}, @@ -1261,7 +1261,7 @@ impl RenderPassCreateInfo { let resolve_format = resolve_attachment_desc.format; if !(resolve_format.components()[0] == format.components()[0] - && resolve_format.type_depth() == format.type_depth()) + && resolve_format.numeric_format_depth() == format.numeric_format_depth()) { return Err(Box::new(ValidationError { problem: format!( @@ -1282,7 +1282,8 @@ impl RenderPassCreateInfo { } if !(resolve_format.components()[1] == format.components()[1] - && resolve_format.type_stencil() == format.type_stencil()) + && resolve_format.numeric_format_stencil() + == format.numeric_format_stencil()) { return Err(Box::new(ValidationError { problem: format!( @@ -2187,6 +2188,29 @@ impl AttachmentDescription { Ok(()) } + + pub(crate) fn required_clear_value(&self) -> Option { + if let Some(numeric_format) = self.format.numeric_format_color() { + (self.load_op == AttachmentLoadOp::Clear).then(|| match numeric_format.numeric_type() { + NumericType::Float => ClearValueType::Float, + NumericType::Int => ClearValueType::Int, + NumericType::Uint => ClearValueType::Uint, + }) + } else { + let aspects = self.format.aspects(); + let need_depth = + aspects.intersects(ImageAspects::DEPTH) && self.load_op == AttachmentLoadOp::Clear; + let need_stencil = aspects.intersects(ImageAspects::STENCIL) + && self.stencil_load_op.unwrap_or(self.load_op) == AttachmentLoadOp::Clear; + + match (need_depth, need_stencil) { + (true, true) => Some(ClearValueType::DepthStencil), + (true, false) => Some(ClearValueType::Depth), + (false, true) => Some(ClearValueType::Stencil), + (false, false) => None, + } + } + } } vulkan_bitflags! { diff --git a/vulkano/src/shader/mod.rs b/vulkano/src/shader/mod.rs index 15af22d8..0c5512ed 100644 --- a/vulkano/src/shader/mod.rs +++ b/vulkano/src/shader/mod.rs @@ -730,7 +730,7 @@ pub struct DescriptorBindingRequirements { /// The base scalar type required for the format of image views bound to this binding. /// This is `None` for non-image bindings. - pub image_scalar_type: Option, + pub image_scalar_type: Option, /// The view type that is required for image views bound to this binding. /// This is `None` for non-image bindings. @@ -1140,7 +1140,7 @@ pub struct ShaderInterfaceEntry { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct ShaderInterfaceEntryType { /// The base numeric type. - pub base_type: ShaderScalarType, + pub base_type: NumericType, /// The number of vector components. Must be in the range 1..=4. pub num_components: u32, @@ -1160,32 +1160,6 @@ impl ShaderInterfaceEntryType { } } -/// The numeric base type of a shader variable. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum ShaderScalarType { - Float, - Sint, - Uint, -} - -// https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap43.html#formats-numericformat -impl From for ShaderScalarType { - #[inline] - fn from(val: NumericType) -> Self { - match val { - NumericType::SFLOAT => Self::Float, - NumericType::UFLOAT => Self::Float, - NumericType::SINT => Self::Sint, - NumericType::UINT => Self::Uint, - NumericType::SNORM => Self::Float, - NumericType::UNORM => Self::Float, - NumericType::SSCALED => Self::Float, - NumericType::USCALED => Self::Float, - NumericType::SRGB => Self::Float, - } - } -} - vulkan_bitflags_enum! { #[non_exhaustive] diff --git a/vulkano/src/shader/reflect.rs b/vulkano/src/shader/reflect.rs index 31ec1178..f46e3014 100644 --- a/vulkano/src/shader/reflect.rs +++ b/vulkano/src/shader/reflect.rs @@ -20,8 +20,8 @@ use crate::{ StorageClass, }, DescriptorIdentifier, DescriptorRequirements, EntryPointInfo, GeometryShaderExecution, - GeometryShaderInput, ShaderExecution, ShaderInterface, ShaderInterfaceEntry, - ShaderInterfaceEntryType, ShaderScalarType, ShaderStage, SpecializationConstant, + GeometryShaderInput, NumericType, ShaderExecution, ShaderInterface, ShaderInterfaceEntry, + ShaderInterfaceEntryType, ShaderStage, SpecializationConstant, }, DeviceSize, }; @@ -854,14 +854,14 @@ fn descriptor_binding_requirements_of(spirv: &Spirv, variable_id: Id) -> Descrip } => { assert!(width == 32); // TODO: 64-bit components match signedness { - 0 => ShaderScalarType::Uint, - 1 => ShaderScalarType::Sint, + 0 => NumericType::Uint, + 1 => NumericType::Int, _ => unreachable!(), } } Instruction::TypeFloat { width, .. } => { assert!(width == 32); // TODO: 64-bit components - ShaderScalarType::Float + NumericType::Float } _ => unreachable!(), }); @@ -1347,8 +1347,8 @@ fn shader_interface_type_of( assert!(!ignore_first_array); ShaderInterfaceEntryType { base_type: match signedness { - 0 => ShaderScalarType::Uint, - 1 => ShaderScalarType::Sint, + 0 => NumericType::Uint, + 1 => NumericType::Int, _ => unreachable!(), }, num_components: 1, @@ -1363,7 +1363,7 @@ fn shader_interface_type_of( Instruction::TypeFloat { width, .. } => { assert!(!ignore_first_array); ShaderInterfaceEntryType { - base_type: ShaderScalarType::Float, + base_type: NumericType::Float, num_components: 1, num_elements: 1, is_64bit: match width { diff --git a/vulkano/src/sync/pipeline.rs b/vulkano/src/sync/pipeline.rs index 514f2fc3..34be620e 100644 --- a/vulkano/src/sync/pipeline.rs +++ b/vulkano/src/sync/pipeline.rs @@ -11,7 +11,9 @@ use crate::{ buffer::Buffer, descriptor_set::layout::DescriptorType, device::{Device, QueueFlags}, - image::{Image, ImageAspects, ImageLayout, ImageSubresourceRange}, + image::{ + Image, ImageAspects, ImageCreateFlags, ImageLayout, ImageSubresourceRange, ImageUsage, + }, macros::{vulkan_bitflags, vulkan_bitflags_enum}, shader::ShaderStages, DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, @@ -1762,6 +1764,44 @@ impl DependencyInfo { && self.buffer_memory_barriers.is_empty() && self.image_memory_barriers.is_empty() } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + dependency_flags, + ref memory_barriers, + ref buffer_memory_barriers, + ref image_memory_barriers, + _ne: _, + } = self; + + dependency_flags + .validate_device(device) + .map_err(|err| ValidationError { + context: "dependency_flags".into(), + vuids: &["VUID-VkDependencyInfo-dependencyFlags-parameter"], + ..ValidationError::from_requirement(err) + })?; + + for (barrier_index, memory_barrier) in memory_barriers.iter().enumerate() { + memory_barrier + .validate(device) + .map_err(|err| err.add_context(format!("memory_barriers[{}]", barrier_index)))?; + } + + for (barrier_index, buffer_memory_barrier) in buffer_memory_barriers.iter().enumerate() { + buffer_memory_barrier.validate(device).map_err(|err| { + err.add_context(format!("buffer_memory_barriers[{}]", barrier_index)) + })?; + } + + for (barrier_index, image_memory_barrier) in image_memory_barriers.iter().enumerate() { + image_memory_barrier.validate(device).map_err(|err| { + err.add_context(format!("image_memory_barriers[{}]", barrier_index)) + })?; + } + + Ok(()) + } } impl Default for DependencyInfo { @@ -2386,6 +2426,624 @@ impl BufferMemoryBarrier { _ne: crate::NonExhaustive(()), } } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + src_stages, + src_access, + dst_stages, + dst_access, + ref queue_family_ownership_transfer, + ref buffer, + ref range, + _ne, + } = self; + + src_stages + .validate_device(device) + .map_err(|err| ValidationError { + context: "src_stages".into(), + vuids: &["VUID-VkBufferMemoryBarrier2-srcStageMask-parameter"], + ..ValidationError::from_requirement(err) + })?; + + dst_stages + .validate_device(device) + .map_err(|err| ValidationError { + context: "dst_stages".into(), + vuids: &["VUID-VkBufferMemoryBarrier2-dstStageMask-parameter"], + ..ValidationError::from_requirement(err) + })?; + + src_access + .validate_device(device) + .map_err(|err| ValidationError { + context: "src_access".into(), + vuids: &["VUID-VkBufferMemoryBarrier2-srcAccessMask-parameter"], + ..ValidationError::from_requirement(err) + })?; + + dst_access + .validate_device(device) + .map_err(|err| ValidationError { + context: "dst_access".into(), + vuids: &["VUID-VkBufferMemoryBarrier2-dstAccessMask-parameter"], + ..ValidationError::from_requirement(err) + })?; + + if !device.enabled_features().synchronization2 { + if src_stages.contains_flags2() { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains flags from `VkPipelineStageFlagBits2`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "synchronization2", + )])]), + ..Default::default() + })); + } + + if dst_stages.contains_flags2() { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains flags from `VkPipelineStageFlagBits2`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "synchronization2", + )])]), + ..Default::default() + })); + } + + if src_access.contains_flags2() { + return Err(Box::new(ValidationError { + context: "src_access".into(), + problem: "contains flags from `VkAccessFlagBits2`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "synchronization2", + )])]), + ..Default::default() + })); + } + + if dst_access.contains_flags2() { + return Err(Box::new(ValidationError { + context: "dst_access".into(), + problem: "contains flags from `VkAccessFlagBits2`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "synchronization2", + )])]), + ..Default::default() + })); + } + } + + if !device.enabled_features().geometry_shader { + if src_stages.intersects(PipelineStages::GEOMETRY_SHADER) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::GEOMETRY_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "geometry_shader", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-srcStageMask-03929"], + })); + } + + if dst_stages.intersects(PipelineStages::GEOMETRY_SHADER) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::GEOMETRY_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "geometry_shader", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-dstStageMask-03929"], + })); + } + } + + if !device.enabled_features().tessellation_shader { + if src_stages.intersects( + PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER, + ) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::TESSELLATION_CONTROL_SHADER` or \ + `PipelineStages::TESSELLATION_EVALUATION_SHADER`" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "tessellation_shader", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-srcStageMask-03930"], + })); + } + + if dst_stages.intersects( + PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER, + ) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::TESSELLATION_CONTROL_SHADER` or \ + `PipelineStages::TESSELLATION_EVALUATION_SHADER`" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "tessellation_shader", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-dstStageMask-03930"], + })); + } + } + + if !device.enabled_features().conditional_rendering { + if src_stages.intersects(PipelineStages::CONDITIONAL_RENDERING) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::CONDITIONAL_RENDERING`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "conditional_rendering", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-srcStageMask-03931"], + })); + } + + if dst_stages.intersects(PipelineStages::CONDITIONAL_RENDERING) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::CONDITIONAL_RENDERING`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "conditional_rendering", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-dstStageMask-03931"], + })); + } + } + + if !device.enabled_features().fragment_density_map { + if src_stages.intersects(PipelineStages::FRAGMENT_DENSITY_PROCESS) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::FRAGMENT_DENSITY_PROCESS`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "fragment_density_map", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-srcStageMask-03932"], + })); + } + + if dst_stages.intersects(PipelineStages::FRAGMENT_DENSITY_PROCESS) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::FRAGMENT_DENSITY_PROCESS`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "fragment_density_map", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-dstStageMask-03932"], + })); + } + } + + if !device.enabled_features().transform_feedback { + if src_stages.intersects(PipelineStages::TRANSFORM_FEEDBACK) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::TRANSFORM_FEEDBACK`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "transform_feedback", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-srcStageMask-03933"], + })); + } + + if dst_stages.intersects(PipelineStages::TRANSFORM_FEEDBACK) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::TRANSFORM_FEEDBACK`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "transform_feedback", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-dstStageMask-03933"], + })); + } + } + + if !device.enabled_features().mesh_shader { + if src_stages.intersects(PipelineStages::MESH_SHADER) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::MESH_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "mesh_shader", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-srcStageMask-03934"], + })); + } + + if dst_stages.intersects(PipelineStages::MESH_SHADER) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::MESH_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "mesh_shader", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-dstStageMask-03934"], + })); + } + } + + if !device.enabled_features().task_shader { + if src_stages.intersects(PipelineStages::TASK_SHADER) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::TASK_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "task_shader", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-srcStageMask-03935"], + })); + } + + if dst_stages.intersects(PipelineStages::TASK_SHADER) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::TASK_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "task_shader", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-dstStageMask-03935"], + })); + } + } + + if !(device.enabled_features().attachment_fragment_shading_rate + || device.enabled_features().shading_rate_image) + { + if src_stages.intersects(PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT`".into(), + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::Feature("attachment_fragment_shading_rate")]), + RequiresAllOf(&[Requires::Feature("shading_rate_image")]), + ]), + vuids: &["VUID-VkBufferMemoryBarrier2-shadingRateImage-07316"], + })); + } + + if dst_stages.intersects(PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT`".into(), + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::Feature("attachment_fragment_shading_rate")]), + RequiresAllOf(&[Requires::Feature("shading_rate_image")]), + ]), + vuids: &["VUID-VkBufferMemoryBarrier2-shadingRateImage-07316"], + })); + } + } + + if !device.enabled_features().subpass_shading { + if src_stages.intersects(PipelineStages::SUBPASS_SHADING) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::SUBPASS_SHADING`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "subpass_shading", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-srcStageMask-04957"], + })); + } + + if dst_stages.intersects(PipelineStages::SUBPASS_SHADING) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::SUBPASS_SHADING`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "subpass_shading", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-dstStageMask-04957"], + })); + } + } + + if !device.enabled_features().invocation_mask { + if src_stages.intersects(PipelineStages::INVOCATION_MASK) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::INVOCATION_MASK`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "invocation_mask", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-srcStageMask-04995"], + })); + } + + if dst_stages.intersects(PipelineStages::INVOCATION_MASK) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::INVOCATION_MASK`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "invocation_mask", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-dstStageMask-04995"], + })); + } + } + + if !(device.enabled_extensions().nv_ray_tracing + || device.enabled_features().ray_tracing_pipeline) + { + if src_stages.intersects(PipelineStages::RAY_TRACING_SHADER) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::RAY_TRACING_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "ray_tracing_pipeline", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-srcStageMask-07946"], + })); + } + + if dst_stages.intersects(PipelineStages::RAY_TRACING_SHADER) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::RAY_TRACING_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "ray_tracing_pipeline", + )])]), + vuids: &["VUID-VkBufferMemoryBarrier2-dstStageMask-07946"], + })); + } + } + + if !AccessFlags::from(src_stages).contains(src_access) { + return Err(Box::new(ValidationError { + problem: "`src_access` contains one or more access types that are not performed \ + by any stage in `src_stages`" + .into(), + vuids: &[ + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03900", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03901", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03902", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03903", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03904", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03905", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03906", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03907", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-07454", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03909", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03910", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03911", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03912", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03913", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03914", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03915", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03916", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03917", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03918", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03919", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03920", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-04747", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03922", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03923", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-04994", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03924", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03925", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03926", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03927", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-03928", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-06256", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-07272", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-04858", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-04859", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-04860", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-04861", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-07455", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-07456", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-07457", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-07458", + "VUID-VkBufferMemoryBarrier2-srcAccessMask-08118", + ], + ..Default::default() + })); + } + + if !AccessFlags::from(dst_stages).contains(dst_access) { + return Err(Box::new(ValidationError { + problem: "`dst_access` contains one or more access types that are not performed \ + by any stage in `dst_stages`" + .into(), + vuids: &[ + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03900", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03901", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03902", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03903", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03904", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03905", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03906", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03907", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-07454", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03909", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03910", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03911", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03912", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03913", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03914", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03915", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03916", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03917", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03918", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03919", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03920", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-04747", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03922", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03923", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-04994", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03924", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03925", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03926", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03927", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-03928", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-06256", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-07272", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-04858", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-04859", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-04860", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-04861", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-07455", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-07456", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-07457", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-07458", + "VUID-VkBufferMemoryBarrier2-dstAccessMask-08118", + ], + ..Default::default() + })); + } + + if range.is_empty() { + return Err(Box::new(ValidationError { + context: "range".into(), + problem: "is empty".into(), + vuids: &["VUID-VkBufferMemoryBarrier2-size-01188"], + ..Default::default() + })); + } + + if range.end > buffer.size() { + return Err(Box::new(ValidationError { + problem: "`range.end` is greater than `buffer.size()`".into(), + vuids: &[ + "VUID-VkBufferMemoryBarrier2-offset-01187", + "VUID-VkBufferMemoryBarrier2-size-01189", + ], + ..Default::default() + })); + } + + if let Some(queue_family_ownership_transfer) = queue_family_ownership_transfer { + if src_stages.intersects(PipelineStages::HOST) { + return Err(Box::new(ValidationError { + problem: "`src_stages` contains `PipelineStages::HOST`, but \ + `queue_family_ownership_transfer` is `Some`" + .into(), + vuids: &["VUID-VkBufferMemoryBarrier2-srcStageMask-03851"], + ..Default::default() + })); + } + + if dst_stages.intersects(PipelineStages::HOST) { + return Err(Box::new(ValidationError { + problem: "`dst_stages` contains `PipelineStages::HOST`, but \ + `queue_family_ownership_transfer` is `Some`" + .into(), + vuids: &["VUID-VkBufferMemoryBarrier2-srcStageMask-03851"], + ..Default::default() + })); + } + + // VUID-VkBufferMemoryBarrier2-srcQueueFamilyIndex-04087 + // VUID-VkBufferMemoryBarrier2-buffer-04088 + // Ensured by the use of an enum. + + let queue_family_count = + device.physical_device().queue_family_properties().len() as u32; + + match queue_family_ownership_transfer { + &QueueFamilyOwnershipTransfer::ExclusiveBetweenLocal { + src_index, + dst_index, + } => { + if src_index >= queue_family_count { + return Err(Box::new(ValidationError { + context: "queue_family_ownership_transfer.src_index".into(), + problem: "is not less than the number of queue families in the \ + physical device" + .into(), + vuids: &["VUID-VkBufferMemoryBarrier2-buffer-04089"], + ..Default::default() + })); + } + + if dst_index >= queue_family_count { + return Err(Box::new(ValidationError { + context: "queue_family_ownership_transfer.dst_index".into(), + problem: "is not less than the number of queue families in the \ + physical device" + .into(), + vuids: &["VUID-VkBufferMemoryBarrier2-buffer-04089"], + ..Default::default() + })); + } + + if src_index == dst_index { + return Err(Box::new(ValidationError { + problem: "`queue_family_ownership_transfer.src_index` is equal to \ + `queue_family_ownership_transfer.dst_index`" + .into(), + // just for sanity, so that the value is `Some` + // if and only if there is a transfer + ..Default::default() + })); + } + } + &QueueFamilyOwnershipTransfer::ExclusiveToExternal { src_index } => { + if src_index >= queue_family_count { + return Err(Box::new(ValidationError { + context: "queue_family_ownership_transfer.src_index".into(), + problem: "is not less than the number of queue families in the \ + physical device" + .into(), + vuids: &["VUID-VkBufferMemoryBarrier2-buffer-04089"], + ..Default::default() + })); + } + } + &QueueFamilyOwnershipTransfer::ExclusiveFromExternal { dst_index } => { + if dst_index >= queue_family_count { + return Err(Box::new(ValidationError { + context: "queue_family_ownership_transfer.dst_index".into(), + problem: "is not less than the number of queue families in the \ + physical device" + .into(), + vuids: &["VUID-VkBufferMemoryBarrier2-buffer-04089"], + ..Default::default() + })); + } + } + &QueueFamilyOwnershipTransfer::ExclusiveToForeign { src_index } => { + if src_index >= queue_family_count { + return Err(Box::new(ValidationError { + context: "queue_family_ownership_transfer.src_index".into(), + problem: "is not less than the number of queue families in the \ + physical device" + .into(), + vuids: &["VUID-VkBufferMemoryBarrier2-buffer-04089"], + ..Default::default() + })); + } + } + &QueueFamilyOwnershipTransfer::ExclusiveFromForeign { dst_index } => { + if dst_index >= queue_family_count { + return Err(Box::new(ValidationError { + context: "queue_family_ownership_transfer.dst_index".into(), + problem: "is not less than the number of queue families in the \ + physical device" + .into(), + vuids: &["VUID-VkBufferMemoryBarrier2-buffer-04089"], + ..Default::default() + })); + } + } + QueueFamilyOwnershipTransfer::ConcurrentToExternal + | QueueFamilyOwnershipTransfer::ConcurrentFromExternal + | QueueFamilyOwnershipTransfer::ConcurrentToForeign + | QueueFamilyOwnershipTransfer::ConcurrentFromForeign => (), + } + } + + Ok(()) + } } /// A memory barrier that is applied to a single image. @@ -2451,6 +3109,1000 @@ impl ImageMemoryBarrier { _ne: crate::NonExhaustive(()), } } + + pub(crate) fn validate(&self, device: &Device) -> Result<(), Box> { + let &Self { + src_stages, + src_access, + dst_stages, + dst_access, + old_layout, + new_layout, + ref queue_family_ownership_transfer, + ref image, + ref subresource_range, + _ne, + } = self; + + src_stages + .validate_device(device) + .map_err(|err| ValidationError { + context: "src_stages".into(), + vuids: &["VUID-VkImageMemoryBarrier2-srcStageMask-parameter"], + ..ValidationError::from_requirement(err) + })?; + + dst_stages + .validate_device(device) + .map_err(|err| ValidationError { + context: "dst_stages".into(), + vuids: &["VUID-VkImageMemoryBarrier2-dstStageMask-parameter"], + ..ValidationError::from_requirement(err) + })?; + + src_access + .validate_device(device) + .map_err(|err| ValidationError { + context: "src_access".into(), + vuids: &["VUID-VkImageMemoryBarrier2-srcAccessMask-parameter"], + ..ValidationError::from_requirement(err) + })?; + + dst_access + .validate_device(device) + .map_err(|err| ValidationError { + context: "dst_access".into(), + vuids: &["VUID-VkImageMemoryBarrier2-dstAccessMask-parameter"], + ..ValidationError::from_requirement(err) + })?; + + if !device.enabled_features().synchronization2 { + if src_stages.contains_flags2() { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains flags from `VkPipelineStageFlagBits2`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "synchronization2", + )])]), + ..Default::default() + })); + } + + if dst_stages.contains_flags2() { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains flags from `VkPipelineStageFlagBits2`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "synchronization2", + )])]), + ..Default::default() + })); + } + + if src_access.contains_flags2() { + return Err(Box::new(ValidationError { + context: "src_access".into(), + problem: "contains flags from `VkAccessFlagBits2`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "synchronization2", + )])]), + ..Default::default() + })); + } + + if dst_access.contains_flags2() { + return Err(Box::new(ValidationError { + context: "dst_access".into(), + problem: "contains flags from `VkAccessFlagBits2`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "synchronization2", + )])]), + ..Default::default() + })); + } + } + + if !device.enabled_features().geometry_shader { + if src_stages.intersects(PipelineStages::GEOMETRY_SHADER) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::GEOMETRY_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "geometry_shader", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-srcStageMask-03929"], + })); + } + + if dst_stages.intersects(PipelineStages::GEOMETRY_SHADER) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::GEOMETRY_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "geometry_shader", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-dstStageMask-03929"], + })); + } + } + + if !device.enabled_features().tessellation_shader { + if src_stages.intersects( + PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER, + ) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::TESSELLATION_CONTROL_SHADER` or \ + `PipelineStages::TESSELLATION_EVALUATION_SHADER`" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "tessellation_shader", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-srcStageMask-03930"], + })); + } + + if dst_stages.intersects( + PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER, + ) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::TESSELLATION_CONTROL_SHADER` or \ + `PipelineStages::TESSELLATION_EVALUATION_SHADER`" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "tessellation_shader", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-dstStageMask-03930"], + })); + } + } + + if !device.enabled_features().conditional_rendering { + if src_stages.intersects(PipelineStages::CONDITIONAL_RENDERING) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::CONDITIONAL_RENDERING`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "conditional_rendering", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-srcStageMask-03931"], + })); + } + + if dst_stages.intersects(PipelineStages::CONDITIONAL_RENDERING) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::CONDITIONAL_RENDERING`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "conditional_rendering", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-dstStageMask-03931"], + })); + } + } + + if !device.enabled_features().fragment_density_map { + if src_stages.intersects(PipelineStages::FRAGMENT_DENSITY_PROCESS) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::FRAGMENT_DENSITY_PROCESS`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "fragment_density_map", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-srcStageMask-03932"], + })); + } + + if dst_stages.intersects(PipelineStages::FRAGMENT_DENSITY_PROCESS) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::FRAGMENT_DENSITY_PROCESS`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "fragment_density_map", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-dstStageMask-03932"], + })); + } + } + + if !device.enabled_features().transform_feedback { + if src_stages.intersects(PipelineStages::TRANSFORM_FEEDBACK) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::TRANSFORM_FEEDBACK`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "transform_feedback", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-srcStageMask-03933"], + })); + } + + if dst_stages.intersects(PipelineStages::TRANSFORM_FEEDBACK) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::TRANSFORM_FEEDBACK`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "transform_feedback", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-dstStageMask-03933"], + })); + } + } + + if !device.enabled_features().mesh_shader { + if src_stages.intersects(PipelineStages::MESH_SHADER) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::MESH_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "mesh_shader", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-srcStageMask-03934"], + })); + } + + if dst_stages.intersects(PipelineStages::MESH_SHADER) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::MESH_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "mesh_shader", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-dstStageMask-03934"], + })); + } + } + + if !device.enabled_features().task_shader { + if src_stages.intersects(PipelineStages::TASK_SHADER) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::TASK_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "task_shader", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-srcStageMask-03935"], + })); + } + + if dst_stages.intersects(PipelineStages::TASK_SHADER) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::TASK_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "task_shader", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-dstStageMask-03935"], + })); + } + } + + if !(device.enabled_features().attachment_fragment_shading_rate + || device.enabled_features().shading_rate_image) + { + if src_stages.intersects(PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT`".into(), + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::Feature("attachment_fragment_shading_rate")]), + RequiresAllOf(&[Requires::Feature("shading_rate_image")]), + ]), + vuids: &["VUID-VkImageMemoryBarrier2-shadingRateImage-07316"], + })); + } + + if dst_stages.intersects(PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT`".into(), + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::Feature("attachment_fragment_shading_rate")]), + RequiresAllOf(&[Requires::Feature("shading_rate_image")]), + ]), + vuids: &["VUID-VkImageMemoryBarrier2-shadingRateImage-07316"], + })); + } + } + + if !device.enabled_features().subpass_shading { + if src_stages.intersects(PipelineStages::SUBPASS_SHADING) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::SUBPASS_SHADING`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "subpass_shading", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-srcStageMask-04957"], + })); + } + + if dst_stages.intersects(PipelineStages::SUBPASS_SHADING) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::SUBPASS_SHADING`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "subpass_shading", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-dstStageMask-04957"], + })); + } + } + + if !device.enabled_features().invocation_mask { + if src_stages.intersects(PipelineStages::INVOCATION_MASK) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::INVOCATION_MASK`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "invocation_mask", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-srcStageMask-04995"], + })); + } + + if dst_stages.intersects(PipelineStages::INVOCATION_MASK) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::INVOCATION_MASK`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "invocation_mask", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-dstStageMask-04995"], + })); + } + } + + if !(device.enabled_extensions().nv_ray_tracing + || device.enabled_features().ray_tracing_pipeline) + { + if src_stages.intersects(PipelineStages::RAY_TRACING_SHADER) { + return Err(Box::new(ValidationError { + context: "src_stages".into(), + problem: "contains `PipelineStages::RAY_TRACING_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "ray_tracing_pipeline", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-srcStageMask-07946"], + })); + } + + if dst_stages.intersects(PipelineStages::RAY_TRACING_SHADER) { + return Err(Box::new(ValidationError { + context: "dst_stages".into(), + problem: "contains `PipelineStages::RAY_TRACING_SHADER`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "ray_tracing_pipeline", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-dstStageMask-07946"], + })); + } + } + + if !AccessFlags::from(src_stages).contains(src_access) { + return Err(Box::new(ValidationError { + problem: "`src_access` contains one or more access types that are not performed \ + by any stage in `src_stages`" + .into(), + vuids: &[ + "VUID-VkImageMemoryBarrier2-srcAccessMask-03900", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03901", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03902", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03903", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03904", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03905", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03906", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03907", + "VUID-VkImageMemoryBarrier2-srcAccessMask-07454", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03909", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03910", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03911", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03912", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03913", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03914", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03915", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03916", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03917", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03918", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03919", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03920", + "VUID-VkImageMemoryBarrier2-srcAccessMask-04747", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03922", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03923", + "VUID-VkImageMemoryBarrier2-srcAccessMask-04994", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03924", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03925", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03926", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03927", + "VUID-VkImageMemoryBarrier2-srcAccessMask-03928", + "VUID-VkImageMemoryBarrier2-srcAccessMask-06256", + "VUID-VkImageMemoryBarrier2-srcAccessMask-07272", + "VUID-VkImageMemoryBarrier2-srcAccessMask-04858", + "VUID-VkImageMemoryBarrier2-srcAccessMask-04859", + "VUID-VkImageMemoryBarrier2-srcAccessMask-04860", + "VUID-VkImageMemoryBarrier2-srcAccessMask-04861", + "VUID-VkImageMemoryBarrier2-srcAccessMask-07455", + "VUID-VkImageMemoryBarrier2-srcAccessMask-07456", + "VUID-VkImageMemoryBarrier2-srcAccessMask-07457", + "VUID-VkImageMemoryBarrier2-srcAccessMask-07458", + "VUID-VkImageMemoryBarrier2-srcAccessMask-08118", + ], + ..Default::default() + })); + } + + if !AccessFlags::from(dst_stages).contains(dst_access) { + return Err(Box::new(ValidationError { + problem: "`dst_access` contains one or more access types that are not performed \ + by any stage in `dst_stages`" + .into(), + vuids: &[ + "VUID-VkImageMemoryBarrier2-dstAccessMask-03900", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03901", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03902", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03903", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03904", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03905", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03906", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03907", + "VUID-VkImageMemoryBarrier2-dstAccessMask-07454", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03909", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03910", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03911", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03912", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03913", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03914", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03915", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03916", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03917", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03918", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03919", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03920", + "VUID-VkImageMemoryBarrier2-dstAccessMask-04747", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03922", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03923", + "VUID-VkImageMemoryBarrier2-dstAccessMask-04994", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03924", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03925", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03926", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03927", + "VUID-VkImageMemoryBarrier2-dstAccessMask-03928", + "VUID-VkImageMemoryBarrier2-dstAccessMask-06256", + "VUID-VkImageMemoryBarrier2-dstAccessMask-07272", + "VUID-VkImageMemoryBarrier2-dstAccessMask-04858", + "VUID-VkImageMemoryBarrier2-dstAccessMask-04859", + "VUID-VkImageMemoryBarrier2-dstAccessMask-04860", + "VUID-VkImageMemoryBarrier2-dstAccessMask-04861", + "VUID-VkImageMemoryBarrier2-dstAccessMask-07455", + "VUID-VkImageMemoryBarrier2-dstAccessMask-07456", + "VUID-VkImageMemoryBarrier2-dstAccessMask-07457", + "VUID-VkImageMemoryBarrier2-dstAccessMask-07458", + "VUID-VkImageMemoryBarrier2-dstAccessMask-08118", + ], + ..Default::default() + })); + } + + // VUID-VkImageMemoryBarrier2-synchronization2-07793 + // If the synchronization2 feature is not enabled, oldLayout must not be VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR or VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR + + // VUID-VkImageMemoryBarrier2-synchronization2-07794 + // If the synchronization2 feature is not enabled, newLayout must not be VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR or VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR + + // VUID-VkImageMemoryBarrier2-attachmentFeedbackLoopLayout-07313 + // If the attachmentFeedbackLoopLayout feature is not enabled, newLayout must not be VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT + + subresource_range + .validate(device) + .map_err(|err| err.add_context("subresource_range"))?; + + if subresource_range.mip_levels.end > image.mip_levels() { + return Err(Box::new(ValidationError { + problem: "`subresource_range.mip_levels.end` is greater than \ + `image.mip_levels()`" + .into(), + vuids: &[ + "VUID-VkImageMemoryBarrier2-subresourceRange-01486", + "VUID-VkImageMemoryBarrier2-subresourceRange-01724", + ], + ..Default::default() + })); + } + + if subresource_range.array_layers.end > image.array_layers() { + return Err(Box::new(ValidationError { + problem: "`subresource_range.array_layers.end` is greater than \ + `image.array_layers()`" + .into(), + vuids: &[ + "VUID-VkImageMemoryBarrier2-subresourceRange-01488", + "VUID-VkImageMemoryBarrier2-subresourceRange-01725", + ], + ..Default::default() + })); + } + + let image_format_aspects = image.format().aspects(); + + if !image_format_aspects.contains(subresource_range.aspects) { + return Err(Box::new(ValidationError { + problem: "`subresource_range.aspects` is not a subset of \ + `image.format().aspects()`" + .into(), + vuids: &[ + "VUID-VkImageMemoryBarrier2-image-01672", + "VUID-VkImageMemoryBarrier2-image-03319", + ], + ..Default::default() + })); + } + + if image_format_aspects.intersects(ImageAspects::COLOR) + && !image.flags().intersects(ImageCreateFlags::DISJOINT) + && subresource_range.aspects != ImageAspects::COLOR + { + return Err(Box::new(ValidationError { + problem: "`image.format()` is a color format, and \ + `image.flags()` does not contain `ImageCreateFlags::DISJOINT`, but \ + `subresource_range.aspects` is not `ImageAspects::COLOR`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-image-01671"], + ..Default::default() + })); + } + + if image_format_aspects.contains(ImageAspects::DEPTH | ImageAspects::STENCIL) + && !subresource_range + .aspects + .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) + && !device.enabled_features().separate_depth_stencil_layouts + { + return Err(Box::new(ValidationError { + problem: "`image.format()` has both a depth and a stencil component, and \ + `subresource_range.aspects` does not contain both \ + `ImageAspects::DEPTH` and `ImageAspects::STENCIL`" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "separate_depth_stencil_layouts", + )])]), + vuids: &["VUID-VkImageMemoryBarrier2-image-03320"], + ..Default::default() + })); + } + + if subresource_range.aspects.intersects(ImageAspects::DEPTH) { + if matches!( + old_layout, + ImageLayout::StencilAttachmentOptimal | ImageLayout::StencilReadOnlyOptimal + ) { + return Err(Box::new(ValidationError { + problem: "`subresource_range.aspects` contains `ImageAspects::DEPTH`, but \ + `old_layout` is `ImageLayout::StencilAttachmentOptimal` or \ + `ImageLayout::StencilReadOnlyOptimal`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-aspectMask-08702"], + ..Default::default() + })); + } + + if matches!( + new_layout, + ImageLayout::StencilAttachmentOptimal | ImageLayout::StencilReadOnlyOptimal + ) { + return Err(Box::new(ValidationError { + problem: "`subresource_range.aspects` contains `ImageAspects::DEPTH`, but \ + `new_layout` is `ImageLayout::StencilAttachmentOptimal` or \ + `ImageLayout::StencilReadOnlyOptimal`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-aspectMask-08702"], + ..Default::default() + })); + } + } + + if subresource_range.aspects.intersects(ImageAspects::STENCIL) { + if matches!( + old_layout, + ImageLayout::StencilAttachmentOptimal | ImageLayout::StencilReadOnlyOptimal + ) { + return Err(Box::new(ValidationError { + problem: "`subresource_range.aspects` contains `ImageAspects::STENCIL`, but \ + `old_layout` is `ImageLayout::DepthAttachmentOptimal` or \ + `ImageLayout::DepthReadOnlyOptimal`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-aspectMask-08703"], + ..Default::default() + })); + } + + if matches!( + new_layout, + ImageLayout::DepthAttachmentOptimal | ImageLayout::DepthReadOnlyOptimal + ) { + return Err(Box::new(ValidationError { + problem: "`subresource_range.aspects` contains `ImageAspects::STENCIL`, but \ + `new_layout` is `ImageLayout::DepthAttachmentOptimal` or \ + `ImageLayout::DepthReadOnlyOptimal`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-aspectMask-08703"], + ..Default::default() + })); + } + } + + if let Some(queue_family_ownership_transfer) = queue_family_ownership_transfer { + if src_stages.intersects(PipelineStages::HOST) { + return Err(Box::new(ValidationError { + problem: "`src_stages` contains `PipelineStages::HOST`, but \ + `queue_family_ownership_transfer` is `Some`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-srcStageMask-03854"], + ..Default::default() + })); + } + + if dst_stages.intersects(PipelineStages::HOST) { + return Err(Box::new(ValidationError { + problem: "`dst_stages` contains `PipelineStages::HOST`, but \ + `queue_family_ownership_transfer` is `Some`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-srcStageMask-03854"], + ..Default::default() + })); + } + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04070 + // VUID-VkImageMemoryBarrier2-image-04071 + // Ensured by the use of an enum. + + let queue_family_count = + device.physical_device().queue_family_properties().len() as u32; + + match queue_family_ownership_transfer { + &QueueFamilyOwnershipTransfer::ExclusiveBetweenLocal { + src_index, + dst_index, + } => { + if src_index >= queue_family_count { + return Err(Box::new(ValidationError { + context: "queue_family_ownership_transfer.src_index".into(), + problem: "is not less than the number of queue families in the \ + physical device" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-image-04072"], + ..Default::default() + })); + } + + if dst_index >= queue_family_count { + return Err(Box::new(ValidationError { + context: "queue_family_ownership_transfer.dst_index".into(), + problem: "is not less than the number of queue families in the \ + physical device" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-image-04072"], + ..Default::default() + })); + } + + if src_index == dst_index { + return Err(Box::new(ValidationError { + problem: "`queue_family_ownership_transfer.src_index` is equal to \ + `queue_family_ownership_transfer.dst_index`" + .into(), + // just for sanity, so that the value is `Some` + // if and only if there is a transfer + ..Default::default() + })); + } + } + &QueueFamilyOwnershipTransfer::ExclusiveToExternal { src_index } => { + if src_index >= queue_family_count { + return Err(Box::new(ValidationError { + context: "queue_family_ownership_transfer.src_index".into(), + problem: "is not less than the number of queue families in the \ + physical device" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-image-04072"], + ..Default::default() + })); + } + } + &QueueFamilyOwnershipTransfer::ExclusiveFromExternal { dst_index } => { + if dst_index >= queue_family_count { + return Err(Box::new(ValidationError { + context: "queue_family_ownership_transfer.dst_index".into(), + problem: "is not less than the number of queue families in the \ + physical device" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-image-04072"], + ..Default::default() + })); + } + } + &QueueFamilyOwnershipTransfer::ExclusiveToForeign { src_index } => { + if src_index >= queue_family_count { + return Err(Box::new(ValidationError { + context: "queue_family_ownership_transfer.src_index".into(), + problem: "is not less than the number of queue families in the \ + physical device" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-image-04072"], + ..Default::default() + })); + } + } + &QueueFamilyOwnershipTransfer::ExclusiveFromForeign { dst_index } => { + if dst_index >= queue_family_count { + return Err(Box::new(ValidationError { + context: "queue_family_ownership_transfer.dst_index".into(), + problem: "is not less than the number of queue families in the \ + physical device" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-image-04072"], + ..Default::default() + })); + } + } + QueueFamilyOwnershipTransfer::ConcurrentToExternal + | QueueFamilyOwnershipTransfer::ConcurrentFromExternal + | QueueFamilyOwnershipTransfer::ConcurrentToForeign + | QueueFamilyOwnershipTransfer::ConcurrentFromForeign => (), + } + } + + let is_image_layout_transition = + !device.enabled_features().synchronization2 || old_layout != new_layout; + let is_queue_family_ownership_transfer = queue_family_ownership_transfer.is_some(); + + if is_image_layout_transition || is_queue_family_ownership_transfer { + match old_layout { + ImageLayout::ColorAttachmentOptimal => { + if !image.usage().intersects(ImageUsage::COLOR_ATTACHMENT) { + return Err(Box::new(ValidationError { + problem: "`old_layout` is `ImageLayout::ColorAttachmentOptimal`, but \ + `image.usage()` does not contain `ImageUsage::COLOR_ATTACHMENT`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-oldLayout-01208"], + ..Default::default() + })); + } + } + ImageLayout::DepthStencilAttachmentOptimal + | ImageLayout::DepthStencilReadOnlyOptimal + | ImageLayout::DepthReadOnlyStencilAttachmentOptimal + | ImageLayout::DepthAttachmentStencilReadOnlyOptimal + | ImageLayout::DepthAttachmentOptimal + | ImageLayout::StencilAttachmentOptimal => { + if !image + .usage() + .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT) + { + return Err(Box::new(ValidationError { + problem: "`old_layout` is \ + `ImageLayout::DepthStencilAttachmentOptimal`, \ + `ImageLayout::DepthStencilReadOnlyOptimal`, \ + `ImageLayout::DepthReadOnlyStencilAttachmentOptimal`, \ + `ImageLayout::DepthAttachmentStencilReadOnlyOptimal`, \ + `ImageLayout::DepthAttachmentOptimal` or \ + `ImageLayout::StencilAttachmentOptimal`, but \ + `image.usage()` does not contain \ + `ImageUsage::DEPTH_STENCIL_ATTACHMENT`" + .into(), + vuids: &[ + "VUID-VkImageMemoryBarrier2-oldLayout-01209", + "VUID-VkImageMemoryBarrier2-oldLayout-01210", + "VUID-VkImageMemoryBarrier2-oldLayout-01658", + "VUID-VkImageMemoryBarrier2-oldLayout-01659", + "VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04066", + "VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04068", + ], + ..Default::default() + })); + } + } + ImageLayout::ShaderReadOnlyOptimal => { + if !image + .usage() + .intersects(ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT) + { + return Err(Box::new(ValidationError { + problem: "`old_layout` is `ImageLayout::ShaderReadOnlyOptimal`, but \ + `image.usage()` does not contain `ImageUsage::SAMPLED` or \ + `ImageUsage::INPUT_ATTACHMENT`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-oldLayout-01211"], + ..Default::default() + })); + } + } + ImageLayout::TransferSrcOptimal => { + if !image.usage().intersects(ImageUsage::TRANSFER_SRC) { + return Err(Box::new(ValidationError { + problem: "`old_layout` is `ImageLayout::TransferSrcOptimal`, but \ + `image.usage()` does not contain `ImageUsage::TRANSFER_SRC`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-oldLayout-01212"], + ..Default::default() + })); + } + } + ImageLayout::TransferDstOptimal => { + if !image.usage().intersects(ImageUsage::TRANSFER_DST) { + return Err(Box::new(ValidationError { + problem: "`old_layout` is `ImageLayout::TransferDstOptimal`, but \ + `image.usage()` does not contain `ImageUsage::TRANSFER_DST`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-oldLayout-01213"], + ..Default::default() + })); + } + } + ImageLayout::Preinitialized => todo!(), + ImageLayout::DepthReadOnlyOptimal | ImageLayout::StencilReadOnlyOptimal => { + if !image.usage().intersects( + ImageUsage::DEPTH_STENCIL_ATTACHMENT + | ImageUsage::SAMPLED + | ImageUsage::INPUT_ATTACHMENT, + ) { + return Err(Box::new(ValidationError { + problem: "`old_layout` is `ImageLayout::DepthReadOnlyOptimal` or \ + `ImageLayout::StencilReadOnlyOptimal`, but \ + `image.usage()` does not contain \ + `ImageUsage::DEPTH_STENCIL_ATTACHMENT`, `ImageUsage::SAMPLED` or \ + `ImageUsage::INPUT_ATTACHMENT`" + .into(), + vuids: &[ + "VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04065", + "VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04067", + ], + ..Default::default() + })); + } + } + ImageLayout::Undefined | ImageLayout::General | ImageLayout::PresentSrc => (), + } + + match new_layout { + ImageLayout::Undefined | ImageLayout::Preinitialized => { + return Err(Box::new(ValidationError { + context: "new_layout".into(), + problem: "is `ImageLayout::Undefined` or `ImageLayout::Preinitialized`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-newLayout-01198"], + ..Default::default() + })); + } + ImageLayout::ColorAttachmentOptimal => { + if !image.usage().intersects(ImageUsage::COLOR_ATTACHMENT) { + return Err(Box::new(ValidationError { + problem: "`new_layout` is `ImageLayout::ColorAttachmentOptimal`, but \ + `image.usage()` does not contain `ImageUsage::COLOR_ATTACHMENT`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-oldLayout-01208"], + ..Default::default() + })); + } + } + ImageLayout::DepthStencilAttachmentOptimal + | ImageLayout::DepthStencilReadOnlyOptimal + | ImageLayout::DepthReadOnlyStencilAttachmentOptimal + | ImageLayout::DepthAttachmentStencilReadOnlyOptimal + | ImageLayout::DepthAttachmentOptimal + | ImageLayout::StencilAttachmentOptimal => { + if !image + .usage() + .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT) + { + return Err(Box::new(ValidationError { + problem: "`new_layout` is \ + `ImageLayout::DepthStencilAttachmentOptimal`, \ + `ImageLayout::DepthStencilReadOnlyOptimal`, \ + `ImageLayout::DepthReadOnlyStencilAttachmentOptimal`, \ + `ImageLayout::DepthAttachmentStencilReadOnlyOptimal`, \ + `ImageLayout::DepthAttachmentOptimal` or \ + `ImageLayout::StencilAttachmentOptimal`, but \ + `image.usage()` does not contain \ + `ImageUsage::DEPTH_STENCIL_ATTACHMENT`" + .into(), + vuids: &[ + "VUID-VkImageMemoryBarrier2-oldLayout-01209", + "VUID-VkImageMemoryBarrier2-oldLayout-01210", + "VUID-VkImageMemoryBarrier2-oldLayout-01658", + "VUID-VkImageMemoryBarrier2-oldLayout-01659", + "VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04066", + "VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04068", + ], + ..Default::default() + })); + } + } + ImageLayout::ShaderReadOnlyOptimal => { + if !image + .usage() + .intersects(ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT) + { + return Err(Box::new(ValidationError { + problem: "`new_layout` is `ImageLayout::ShaderReadOnlyOptimal`, but \ + `image.usage()` does not contain `ImageUsage::SAMPLED` or \ + `ImageUsage::INPUT_ATTACHMENT`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-oldLayout-01211"], + ..Default::default() + })); + } + } + ImageLayout::TransferSrcOptimal => { + if !image.usage().intersects(ImageUsage::TRANSFER_SRC) { + return Err(Box::new(ValidationError { + problem: "`new_layout` is `ImageLayout::TransferSrcOptimal`, but \ + `image.usage()` does not contain `ImageUsage::TRANSFER_SRC`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-oldLayout-01212"], + ..Default::default() + })); + } + } + ImageLayout::TransferDstOptimal => { + if !image.usage().intersects(ImageUsage::TRANSFER_DST) { + return Err(Box::new(ValidationError { + problem: "`new_layout` is `ImageLayout::TransferDstOptimal`, but \ + `image.usage()` does not contain `ImageUsage::TRANSFER_DST`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-oldLayout-01213"], + ..Default::default() + })); + } + } + ImageLayout::DepthReadOnlyOptimal | ImageLayout::StencilReadOnlyOptimal => { + if !image.usage().intersects( + ImageUsage::DEPTH_STENCIL_ATTACHMENT + | ImageUsage::SAMPLED + | ImageUsage::INPUT_ATTACHMENT, + ) { + return Err(Box::new(ValidationError { + problem: "`new_layout` is `ImageLayout::DepthReadOnlyOptimal` or \ + `ImageLayout::StencilReadOnlyOptimal`, but \ + `image.usage()` does not contain \ + `ImageUsage::DEPTH_STENCIL_ATTACHMENT`, `ImageUsage::SAMPLED` or \ + `ImageUsage::INPUT_ATTACHMENT`" + .into(), + vuids: &[ + "VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04065", + "VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04067", + ], + ..Default::default() + })); + } + } + ImageLayout::General | ImageLayout::PresentSrc => (), + } + + if src_stages.intersects(PipelineStages::HOST) + && !matches!( + old_layout, + ImageLayout::Preinitialized | ImageLayout::Undefined | ImageLayout::General + ) + { + return Err(Box::new(ValidationError { + problem: "`src_stages` contains `PipelineStages::HOST`, but \ + `old_layout` is not `ImageLayout::Preinitialized`, \ + `ImageLayout::Undefined` or `ImageLayout::General`" + .into(), + vuids: &["VUID-VkImageMemoryBarrier2-srcStageMask-03855"], + ..Default::default() + })); + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01197 + // Unsafe + } + + Ok(()) + } } /// Specifies a queue family ownership transfer for a resource.