1 // Copyright (c) 2022 The vulkano developers 2 // Licensed under the Apache License, Version 2.0 3 // <LICENSE-APACHE or 4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT 5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, 6 // at your option. All files in the project carrying such 7 // notice may not be copied, modified, or distributed except 8 // according to those terms. 9 10 use crate::{ 11 buffer::{view::BufferView, BufferUsage, Subbuffer}, 12 command_buffer::{ 13 allocator::CommandBufferAllocator, 14 auto::{RenderPassState, RenderPassStateType}, 15 synced::{Command, Resource, SyncCommandBufferBuilder, SyncCommandBufferBuilderError}, 16 sys::UnsafeCommandBufferBuilder, 17 AutoCommandBufferBuilder, DispatchIndirectCommand, DrawIndexedIndirectCommand, 18 DrawIndirectCommand, ResourceInCommand, ResourceUseRef, SubpassContents, 19 }, 20 descriptor_set::{layout::DescriptorType, DescriptorBindingResources}, 21 device::{DeviceOwned, QueueFlags}, 22 format::{Format, FormatFeatures}, 23 image::{ 24 view::ImageViewType, ImageAccess, ImageAspects, ImageSubresourceRange, ImageViewAbstract, 25 SampleCount, 26 }, 27 pipeline::{ 28 graphics::{ 29 input_assembly::{PrimitiveTopology, PrimitiveTopologyClass}, 30 render_pass::PipelineRenderPassType, 31 vertex_input::VertexInputRate, 32 }, 33 DynamicState, GraphicsPipeline, PartialStateMode, Pipeline, PipelineLayout, 34 }, 35 sampler::{Sampler, SamplerImageViewIncompatibleError}, 36 shader::{DescriptorBindingRequirements, ShaderScalarType, ShaderStage}, 37 sync::{AccessFlags, PipelineMemoryAccess, PipelineStages}, 38 DeviceSize, RequiresOneOf, VulkanObject, 39 }; 40 use std::{ 41 cmp::min, 42 error::Error, 43 fmt::{Display, Error as FmtError, Formatter}, 44 mem::size_of, 45 ops::Range, 46 sync::Arc, 47 }; 48 49 /// # Commands to execute a bound pipeline. 50 /// 51 /// Dispatch commands require a compute queue, draw commands require a graphics queue. 52 impl<L, A> AutoCommandBufferBuilder<L, A> 53 where 54 A: CommandBufferAllocator, 55 { 56 /// Perform a single compute operation using a compute pipeline. 57 /// 58 /// A compute pipeline must have been bound using 59 /// [`bind_pipeline_compute`](Self::bind_pipeline_compute). Any resources used by the compute 60 /// pipeline, such as descriptor sets, must have been set beforehand. dispatch( &mut self, group_counts: [u32; 3], ) -> Result<&mut Self, PipelineExecutionError>61 pub fn dispatch( 62 &mut self, 63 group_counts: [u32; 3], 64 ) -> Result<&mut Self, PipelineExecutionError> { 65 self.validate_dispatch(group_counts)?; 66 67 unsafe { 68 self.inner.dispatch(group_counts)?; 69 } 70 71 Ok(self) 72 } 73 validate_dispatch(&self, group_counts: [u32; 3]) -> Result<(), PipelineExecutionError>74 fn validate_dispatch(&self, group_counts: [u32; 3]) -> Result<(), PipelineExecutionError> { 75 let queue_family_properties = self.queue_family_properties(); 76 77 // VUID-vkCmdDispatch-commandBuffer-cmdpool 78 if !queue_family_properties 79 .queue_flags 80 .intersects(QueueFlags::COMPUTE) 81 { 82 return Err(PipelineExecutionError::NotSupportedByQueueFamily); 83 } 84 85 // VUID-vkCmdDispatch-renderpass 86 if self.render_pass_state.is_some() { 87 return Err(PipelineExecutionError::ForbiddenInsideRenderPass); 88 } 89 90 // VUID-vkCmdDispatch-None-02700 91 let pipeline = match self.state().pipeline_compute() { 92 Some(x) => x.as_ref(), 93 None => return Err(PipelineExecutionError::PipelineNotBound), 94 }; 95 96 self.validate_pipeline_descriptor_sets(pipeline)?; 97 self.validate_pipeline_push_constants(pipeline.layout())?; 98 99 let max = self 100 .device() 101 .physical_device() 102 .properties() 103 .max_compute_work_group_count; 104 105 // VUID-vkCmdDispatch-groupCountX-00386 106 // VUID-vkCmdDispatch-groupCountY-00387 107 // VUID-vkCmdDispatch-groupCountZ-00388 108 if group_counts[0] > max[0] || group_counts[1] > max[1] || group_counts[2] > max[2] { 109 return Err(PipelineExecutionError::MaxComputeWorkGroupCountExceeded { 110 requested: group_counts, 111 max, 112 }); 113 } 114 115 Ok(()) 116 } 117 118 /// Perform multiple compute operations using a compute pipeline. One dispatch is performed for 119 /// each [`DispatchIndirectCommand`] struct in `indirect_buffer`. 120 /// 121 /// A compute pipeline must have been bound using 122 /// [`bind_pipeline_compute`](Self::bind_pipeline_compute). Any resources used by the compute 123 /// pipeline, such as descriptor sets, must have been set beforehand. dispatch_indirect( &mut self, indirect_buffer: Subbuffer<[DispatchIndirectCommand]>, ) -> Result<&mut Self, PipelineExecutionError>124 pub fn dispatch_indirect( 125 &mut self, 126 indirect_buffer: Subbuffer<[DispatchIndirectCommand]>, 127 ) -> Result<&mut Self, PipelineExecutionError> { 128 self.validate_dispatch_indirect(indirect_buffer.as_bytes())?; 129 130 unsafe { 131 self.inner.dispatch_indirect(indirect_buffer)?; 132 } 133 134 Ok(self) 135 } 136 validate_dispatch_indirect( &self, indirect_buffer: &Subbuffer<[u8]>, ) -> Result<(), PipelineExecutionError>137 fn validate_dispatch_indirect( 138 &self, 139 indirect_buffer: &Subbuffer<[u8]>, 140 ) -> Result<(), PipelineExecutionError> { 141 let queue_family_properties = self.queue_family_properties(); 142 143 // VUID-vkCmdDispatchIndirect-commandBuffer-cmdpool 144 if !queue_family_properties 145 .queue_flags 146 .intersects(QueueFlags::COMPUTE) 147 { 148 return Err(PipelineExecutionError::NotSupportedByQueueFamily); 149 } 150 151 // VUID-vkCmdDispatchIndirect-renderpass 152 if self.render_pass_state.is_some() { 153 return Err(PipelineExecutionError::ForbiddenInsideRenderPass); 154 } 155 156 // VUID-vkCmdDispatchIndirect-None-02700 157 let pipeline = match self.state().pipeline_compute() { 158 Some(x) => x.as_ref(), 159 None => return Err(PipelineExecutionError::PipelineNotBound), 160 }; 161 162 self.validate_pipeline_descriptor_sets(pipeline)?; 163 self.validate_pipeline_push_constants(pipeline.layout())?; 164 self.validate_indirect_buffer(indirect_buffer)?; 165 166 Ok(()) 167 } 168 169 /// Perform a single draw operation using a graphics pipeline. 170 /// 171 /// The parameters specify the first vertex and the number of vertices to draw, and the first 172 /// instance and number of instances. For non-instanced drawing, specify `instance_count` as 1 173 /// and `first_instance` as 0. 174 /// 175 /// A graphics pipeline must have been bound using 176 /// [`bind_pipeline_graphics`](Self::bind_pipeline_graphics). Any resources used by the graphics 177 /// pipeline, such as descriptor sets, vertex buffers and dynamic state, must have been set 178 /// beforehand. If the bound graphics pipeline uses vertex buffers, then the provided vertex and 179 /// instance ranges must be in range of the bound vertex buffers. draw( &mut self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32, ) -> Result<&mut Self, PipelineExecutionError>180 pub fn draw( 181 &mut self, 182 vertex_count: u32, 183 instance_count: u32, 184 first_vertex: u32, 185 first_instance: u32, 186 ) -> Result<&mut Self, PipelineExecutionError> { 187 self.validate_draw(vertex_count, instance_count, first_vertex, first_instance)?; 188 189 unsafe { 190 self.inner 191 .draw(vertex_count, instance_count, first_vertex, first_instance)?; 192 } 193 194 if let RenderPassStateType::BeginRendering(state) = 195 &mut self.render_pass_state.as_mut().unwrap().render_pass 196 { 197 state.pipeline_used = true; 198 } 199 200 Ok(self) 201 } 202 validate_draw( &self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32, ) -> Result<(), PipelineExecutionError>203 fn validate_draw( 204 &self, 205 vertex_count: u32, 206 instance_count: u32, 207 first_vertex: u32, 208 first_instance: u32, 209 ) -> Result<(), PipelineExecutionError> { 210 // VUID-vkCmdDraw-renderpass 211 let render_pass_state = self 212 .render_pass_state 213 .as_ref() 214 .ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?; 215 216 // VUID-vkCmdDraw-None-02700 217 let pipeline = match self.state().pipeline_graphics() { 218 Some(x) => x.as_ref(), 219 None => return Err(PipelineExecutionError::PipelineNotBound), 220 }; 221 222 self.validate_pipeline_descriptor_sets(pipeline)?; 223 self.validate_pipeline_push_constants(pipeline.layout())?; 224 self.validate_pipeline_graphics_dynamic_state(pipeline)?; 225 self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?; 226 self.validate_pipeline_graphics_vertex_buffers( 227 pipeline, 228 Some((first_vertex, vertex_count)), 229 Some((first_instance, instance_count)), 230 )?; 231 232 Ok(()) 233 } 234 235 /// Perform multiple draw operations using a graphics pipeline. 236 /// 237 /// One draw is performed for each [`DrawIndirectCommand`] struct in `indirect_buffer`. 238 /// The maximum number of draw commands in the buffer is limited by the 239 /// [`max_draw_indirect_count`](crate::device::Properties::max_draw_indirect_count) limit. 240 /// This limit is 1 unless the 241 /// [`multi_draw_indirect`](crate::device::Features::multi_draw_indirect) feature has been 242 /// enabled. 243 /// 244 /// A graphics pipeline must have been bound using 245 /// [`bind_pipeline_graphics`](Self::bind_pipeline_graphics). Any resources used by the graphics 246 /// pipeline, such as descriptor sets, vertex buffers and dynamic state, must have been set 247 /// beforehand. If the bound graphics pipeline uses vertex buffers, then the vertex and instance 248 /// ranges of each `DrawIndirectCommand` in the indirect buffer must be in range of the bound 249 /// vertex buffers. draw_indirect( &mut self, indirect_buffer: Subbuffer<[DrawIndirectCommand]>, ) -> Result<&mut Self, PipelineExecutionError>250 pub fn draw_indirect( 251 &mut self, 252 indirect_buffer: Subbuffer<[DrawIndirectCommand]>, 253 ) -> Result<&mut Self, PipelineExecutionError> { 254 let draw_count = indirect_buffer.len() as u32; 255 let stride = size_of::<DrawIndirectCommand>() as u32; 256 self.validate_draw_indirect(indirect_buffer.as_bytes(), draw_count, stride)?; 257 258 unsafe { 259 self.inner 260 .draw_indirect(indirect_buffer, draw_count, stride)?; 261 } 262 263 if let RenderPassStateType::BeginRendering(state) = 264 &mut self.render_pass_state.as_mut().unwrap().render_pass 265 { 266 state.pipeline_used = true; 267 } 268 269 Ok(self) 270 } 271 validate_draw_indirect( &self, indirect_buffer: &Subbuffer<[u8]>, draw_count: u32, _stride: u32, ) -> Result<(), PipelineExecutionError>272 fn validate_draw_indirect( 273 &self, 274 indirect_buffer: &Subbuffer<[u8]>, 275 draw_count: u32, 276 _stride: u32, 277 ) -> Result<(), PipelineExecutionError> { 278 // VUID-vkCmdDrawIndirect-renderpass 279 let render_pass_state = self 280 .render_pass_state 281 .as_ref() 282 .ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?; 283 284 // VUID-vkCmdDrawIndirect-None-02700 285 let pipeline = match self.state().pipeline_graphics() { 286 Some(x) => x.as_ref(), 287 None => return Err(PipelineExecutionError::PipelineNotBound), 288 }; 289 290 self.validate_pipeline_descriptor_sets(pipeline)?; 291 self.validate_pipeline_push_constants(pipeline.layout())?; 292 self.validate_pipeline_graphics_dynamic_state(pipeline)?; 293 self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?; 294 self.validate_pipeline_graphics_vertex_buffers(pipeline, None, None)?; 295 296 self.validate_indirect_buffer(indirect_buffer)?; 297 298 // VUID-vkCmdDrawIndirect-drawCount-02718 299 if draw_count > 1 && !self.device().enabled_features().multi_draw_indirect { 300 return Err(PipelineExecutionError::RequirementNotMet { 301 required_for: "`draw_count` is greater than `1`", 302 requires_one_of: RequiresOneOf { 303 features: &["multi_draw_indirect"], 304 ..Default::default() 305 }, 306 }); 307 } 308 309 let max = self 310 .device() 311 .physical_device() 312 .properties() 313 .max_draw_indirect_count; 314 315 // VUID-vkCmdDrawIndirect-drawCount-02719 316 if draw_count > max { 317 return Err(PipelineExecutionError::MaxDrawIndirectCountExceeded { 318 provided: draw_count, 319 max, 320 }); 321 } 322 323 Ok(()) 324 } 325 326 /// Perform a single draw operation using a graphics pipeline, using an index buffer. 327 /// 328 /// The parameters specify the first index and the number of indices in the index buffer that 329 /// should be used, and the first instance and number of instances. For non-instanced drawing, 330 /// specify `instance_count` as 1 and `first_instance` as 0. The `vertex_offset` is a constant 331 /// value that should be added to each index in the index buffer to produce the final vertex 332 /// number to be used. 333 /// 334 /// An index buffer must have been bound using 335 /// [`bind_index_buffer`](Self::bind_index_buffer), and the provided index range must be in 336 /// range of the bound index buffer. 337 /// 338 /// A graphics pipeline must have been bound using 339 /// [`bind_pipeline_graphics`](Self::bind_pipeline_graphics). Any resources used by the graphics 340 /// pipeline, such as descriptor sets, vertex buffers and dynamic state, must have been set 341 /// beforehand. If the bound graphics pipeline uses vertex buffers, then the provided instance 342 /// range must be in range of the bound vertex buffers. The vertex indices in the index buffer 343 /// must be in range of the bound vertex buffers. draw_indexed( &mut self, index_count: u32, instance_count: u32, first_index: u32, vertex_offset: i32, first_instance: u32, ) -> Result<&mut Self, PipelineExecutionError>344 pub fn draw_indexed( 345 &mut self, 346 index_count: u32, 347 instance_count: u32, 348 first_index: u32, 349 vertex_offset: i32, 350 first_instance: u32, 351 ) -> Result<&mut Self, PipelineExecutionError> { 352 self.validate_draw_indexed( 353 index_count, 354 instance_count, 355 first_index, 356 vertex_offset, 357 first_instance, 358 )?; 359 360 unsafe { 361 self.inner.draw_indexed( 362 index_count, 363 instance_count, 364 first_index, 365 vertex_offset, 366 first_instance, 367 )?; 368 } 369 370 if let RenderPassStateType::BeginRendering(state) = 371 &mut self.render_pass_state.as_mut().unwrap().render_pass 372 { 373 state.pipeline_used = true; 374 } 375 376 Ok(self) 377 } 378 validate_draw_indexed( &self, index_count: u32, instance_count: u32, first_index: u32, _vertex_offset: i32, first_instance: u32, ) -> Result<(), PipelineExecutionError>379 fn validate_draw_indexed( 380 &self, 381 index_count: u32, 382 instance_count: u32, 383 first_index: u32, 384 _vertex_offset: i32, 385 first_instance: u32, 386 ) -> Result<(), PipelineExecutionError> { 387 // TODO: how to handle an index out of range of the vertex buffers? 388 389 // VUID-vkCmdDrawIndexed-renderpass 390 let render_pass_state = self 391 .render_pass_state 392 .as_ref() 393 .ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?; 394 395 // VUID-vkCmdDrawIndexed-None-02700 396 let pipeline = match self.state().pipeline_graphics() { 397 Some(x) => x.as_ref(), 398 None => return Err(PipelineExecutionError::PipelineNotBound), 399 }; 400 401 self.validate_pipeline_descriptor_sets(pipeline)?; 402 self.validate_pipeline_push_constants(pipeline.layout())?; 403 self.validate_pipeline_graphics_dynamic_state(pipeline)?; 404 self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?; 405 self.validate_pipeline_graphics_vertex_buffers( 406 pipeline, 407 None, 408 Some((first_instance, instance_count)), 409 )?; 410 411 self.validate_index_buffer(Some((first_index, index_count)))?; 412 413 Ok(()) 414 } 415 416 /// Perform multiple draw operations using a graphics pipeline, using an index buffer. 417 /// 418 /// One draw is performed for each [`DrawIndexedIndirectCommand`] struct in `indirect_buffer`. 419 /// The maximum number of draw commands in the buffer is limited by the 420 /// [`max_draw_indirect_count`](crate::device::Properties::max_draw_indirect_count) limit. 421 /// This limit is 1 unless the 422 /// [`multi_draw_indirect`](crate::device::Features::multi_draw_indirect) feature has been 423 /// enabled. 424 /// 425 /// An index buffer must have been bound using 426 /// [`bind_index_buffer`](Self::bind_index_buffer), and the index ranges of each 427 /// `DrawIndexedIndirectCommand` in the indirect buffer must be in range of the bound index 428 /// buffer. 429 /// 430 /// A graphics pipeline must have been bound using 431 /// [`bind_pipeline_graphics`](Self::bind_pipeline_graphics). Any resources used by the graphics 432 /// pipeline, such as descriptor sets, vertex buffers and dynamic state, must have been set 433 /// beforehand. If the bound graphics pipeline uses vertex buffers, then the instance ranges of 434 /// each `DrawIndexedIndirectCommand` in the indirect buffer must be in range of the bound 435 /// vertex buffers. draw_indexed_indirect( &mut self, indirect_buffer: Subbuffer<[DrawIndexedIndirectCommand]>, ) -> Result<&mut Self, PipelineExecutionError>436 pub fn draw_indexed_indirect( 437 &mut self, 438 indirect_buffer: Subbuffer<[DrawIndexedIndirectCommand]>, 439 ) -> Result<&mut Self, PipelineExecutionError> { 440 let draw_count = indirect_buffer.len() as u32; 441 let stride = size_of::<DrawIndexedIndirectCommand>() as u32; 442 self.validate_draw_indexed_indirect(indirect_buffer.as_bytes(), draw_count, stride)?; 443 444 unsafe { 445 self.inner 446 .draw_indexed_indirect(indirect_buffer, draw_count, stride)?; 447 } 448 449 if let RenderPassStateType::BeginRendering(state) = 450 &mut self.render_pass_state.as_mut().unwrap().render_pass 451 { 452 state.pipeline_used = true; 453 } 454 455 Ok(self) 456 } 457 validate_draw_indexed_indirect( &self, indirect_buffer: &Subbuffer<[u8]>, draw_count: u32, _stride: u32, ) -> Result<(), PipelineExecutionError>458 fn validate_draw_indexed_indirect( 459 &self, 460 indirect_buffer: &Subbuffer<[u8]>, 461 draw_count: u32, 462 _stride: u32, 463 ) -> Result<(), PipelineExecutionError> { 464 // VUID-vkCmdDrawIndexedIndirect-renderpass 465 let render_pass_state = self 466 .render_pass_state 467 .as_ref() 468 .ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?; 469 470 // VUID-vkCmdDrawIndexedIndirect-None-02700 471 let pipeline = match self.state().pipeline_graphics() { 472 Some(x) => x.as_ref(), 473 None => return Err(PipelineExecutionError::PipelineNotBound), 474 }; 475 476 self.validate_pipeline_descriptor_sets(pipeline)?; 477 self.validate_pipeline_push_constants(pipeline.layout())?; 478 self.validate_pipeline_graphics_dynamic_state(pipeline)?; 479 self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?; 480 self.validate_pipeline_graphics_vertex_buffers(pipeline, None, None)?; 481 482 self.validate_index_buffer(None)?; 483 self.validate_indirect_buffer(indirect_buffer)?; 484 485 // VUID-vkCmdDrawIndexedIndirect-drawCount-02718 486 if draw_count > 1 && !self.device().enabled_features().multi_draw_indirect { 487 return Err(PipelineExecutionError::RequirementNotMet { 488 required_for: "`draw_count` is greater than `1`", 489 requires_one_of: RequiresOneOf { 490 features: &["multi_draw_indirect"], 491 ..Default::default() 492 }, 493 }); 494 } 495 496 let max = self 497 .device() 498 .physical_device() 499 .properties() 500 .max_draw_indirect_count; 501 502 // VUID-vkCmdDrawIndexedIndirect-drawCount-02719 503 if draw_count > max { 504 return Err(PipelineExecutionError::MaxDrawIndirectCountExceeded { 505 provided: draw_count, 506 max, 507 }); 508 } 509 510 Ok(()) 511 } 512 validate_index_buffer( &self, indices: Option<(u32, u32)>, ) -> Result<(), PipelineExecutionError>513 fn validate_index_buffer( 514 &self, 515 indices: Option<(u32, u32)>, 516 ) -> Result<(), PipelineExecutionError> { 517 let current_state = self.state(); 518 519 // VUID? 520 let (index_buffer, index_type) = match current_state.index_buffer() { 521 Some(x) => x, 522 None => return Err(PipelineExecutionError::IndexBufferNotBound), 523 }; 524 525 if let Some((first_index, index_count)) = indices { 526 let max_index_count = (index_buffer.size() / index_type.size()) as u32; 527 528 // // VUID-vkCmdDrawIndexed-firstIndex-04932 529 if first_index + index_count > max_index_count { 530 return Err(PipelineExecutionError::IndexBufferRangeOutOfBounds { 531 highest_index: first_index + index_count, 532 max_index_count, 533 }); 534 } 535 } 536 537 Ok(()) 538 } 539 validate_indirect_buffer( &self, buffer: &Subbuffer<[u8]>, ) -> Result<(), PipelineExecutionError>540 fn validate_indirect_buffer( 541 &self, 542 buffer: &Subbuffer<[u8]>, 543 ) -> Result<(), PipelineExecutionError> { 544 // VUID-vkCmdDispatchIndirect-commonparent 545 assert_eq!(self.device(), buffer.device()); 546 547 // VUID-vkCmdDispatchIndirect-buffer-02709 548 if !buffer 549 .buffer() 550 .usage() 551 .intersects(BufferUsage::INDIRECT_BUFFER) 552 { 553 return Err(PipelineExecutionError::IndirectBufferMissingUsage); 554 } 555 556 // VUID-vkCmdDispatchIndirect-offset-02710 557 // TODO: 558 559 Ok(()) 560 } 561 validate_pipeline_descriptor_sets<Pl: Pipeline>( &self, pipeline: &Pl, ) -> Result<(), PipelineExecutionError>562 fn validate_pipeline_descriptor_sets<Pl: Pipeline>( 563 &self, 564 pipeline: &Pl, 565 ) -> Result<(), PipelineExecutionError> { 566 fn validate_resources<T>( 567 set_num: u32, 568 binding_num: u32, 569 binding_reqs: &DescriptorBindingRequirements, 570 elements: &[Option<T>], 571 mut extra_check: impl FnMut(u32, &T) -> Result<(), DescriptorResourceInvalidError>, 572 ) -> Result<(), PipelineExecutionError> { 573 let elements_to_check = if let Some(descriptor_count) = binding_reqs.descriptor_count { 574 // The shader has a fixed-sized array, so it will never access more than 575 // the first `descriptor_count` elements. 576 elements.get(..descriptor_count as usize).ok_or({ 577 // There are less than `descriptor_count` elements in `elements` 578 PipelineExecutionError::DescriptorResourceInvalid { 579 set_num, 580 binding_num, 581 index: elements.len() as u32, 582 error: DescriptorResourceInvalidError::Missing, 583 } 584 })? 585 } else { 586 // The shader has a runtime-sized array, so any element could potentially 587 // be accessed. We must check them all. 588 elements 589 }; 590 591 for (index, element) in elements_to_check.iter().enumerate() { 592 let index = index as u32; 593 594 // VUID-vkCmdDispatch-None-02699 595 let element = match element { 596 Some(x) => x, 597 None => { 598 return Err(PipelineExecutionError::DescriptorResourceInvalid { 599 set_num, 600 binding_num, 601 index, 602 error: DescriptorResourceInvalidError::Missing, 603 }) 604 } 605 }; 606 607 if let Err(error) = extra_check(index, element) { 608 return Err(PipelineExecutionError::DescriptorResourceInvalid { 609 set_num, 610 binding_num, 611 index, 612 error, 613 }); 614 } 615 } 616 617 Ok(()) 618 } 619 620 if pipeline.num_used_descriptor_sets() == 0 { 621 return Ok(()); 622 } 623 624 let current_state = self.state(); 625 626 // VUID-vkCmdDispatch-None-02697 627 let bindings_pipeline_layout = 628 match current_state.descriptor_sets_pipeline_layout(pipeline.bind_point()) { 629 Some(x) => x, 630 None => return Err(PipelineExecutionError::PipelineLayoutNotCompatible), 631 }; 632 633 // VUID-vkCmdDispatch-None-02697 634 if !pipeline.layout().is_compatible_with( 635 bindings_pipeline_layout, 636 pipeline.num_used_descriptor_sets(), 637 ) { 638 return Err(PipelineExecutionError::PipelineLayoutNotCompatible); 639 } 640 641 for (&(set_num, binding_num), binding_reqs) in pipeline.descriptor_binding_requirements() { 642 let layout_binding = 643 &pipeline.layout().set_layouts()[set_num as usize].bindings()[&binding_num]; 644 645 let check_buffer = 646 |_index: u32, (_buffer, _range): &(Subbuffer<[u8]>, Range<DeviceSize>)| Ok(()); 647 648 let check_buffer_view = |index: u32, buffer_view: &Arc<BufferView>| { 649 for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter()) 650 .chain(binding_reqs.descriptors.get(&None)) 651 { 652 if layout_binding.descriptor_type == DescriptorType::StorageTexelBuffer { 653 // VUID-vkCmdDispatch-OpTypeImage-06423 654 if binding_reqs.image_format.is_none() 655 && !desc_reqs.memory_write.is_empty() 656 && !buffer_view 657 .format_features() 658 .intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT) 659 { 660 return Err(DescriptorResourceInvalidError::StorageWriteWithoutFormatNotSupported); 661 } 662 663 // VUID-vkCmdDispatch-OpTypeImage-06424 664 if binding_reqs.image_format.is_none() 665 && !desc_reqs.memory_read.is_empty() 666 && !buffer_view 667 .format_features() 668 .intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT) 669 { 670 return Err(DescriptorResourceInvalidError::StorageReadWithoutFormatNotSupported); 671 } 672 } 673 } 674 675 Ok(()) 676 }; 677 678 let check_image_view_common = |index: u32, image_view: &Arc<dyn ImageViewAbstract>| { 679 for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter()) 680 .chain(binding_reqs.descriptors.get(&None)) 681 { 682 // VUID-vkCmdDispatch-None-02691 683 if desc_reqs.storage_image_atomic 684 && !image_view 685 .format_features() 686 .intersects(FormatFeatures::STORAGE_IMAGE_ATOMIC) 687 { 688 return Err(DescriptorResourceInvalidError::StorageImageAtomicNotSupported); 689 } 690 691 if layout_binding.descriptor_type == DescriptorType::StorageImage { 692 // VUID-vkCmdDispatch-OpTypeImage-06423 693 if binding_reqs.image_format.is_none() 694 && !desc_reqs.memory_write.is_empty() 695 && !image_view 696 .format_features() 697 .intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT) 698 { 699 return Err( 700 DescriptorResourceInvalidError::StorageWriteWithoutFormatNotSupported, 701 ); 702 } 703 704 // VUID-vkCmdDispatch-OpTypeImage-06424 705 if binding_reqs.image_format.is_none() 706 && !desc_reqs.memory_read.is_empty() 707 && !image_view 708 .format_features() 709 .intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT) 710 { 711 return Err( 712 DescriptorResourceInvalidError::StorageReadWithoutFormatNotSupported, 713 ); 714 } 715 } 716 } 717 718 /* 719 Instruction/Sampler/Image View Validation 720 https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap16.html#textures-input-validation 721 */ 722 723 // The SPIR-V Image Format is not compatible with the image view’s format. 724 if let Some(format) = binding_reqs.image_format { 725 if image_view.format() != Some(format) { 726 return Err(DescriptorResourceInvalidError::ImageViewFormatMismatch { 727 required: format, 728 provided: image_view.format(), 729 }); 730 } 731 } 732 733 // Rules for viewType 734 if let Some(image_view_type) = binding_reqs.image_view_type { 735 if image_view.view_type() != image_view_type { 736 return Err(DescriptorResourceInvalidError::ImageViewTypeMismatch { 737 required: image_view_type, 738 provided: image_view.view_type(), 739 }); 740 } 741 } 742 743 // - If the image was created with VkImageCreateInfo::samples equal to 744 // VK_SAMPLE_COUNT_1_BIT, the instruction must have MS = 0. 745 // - If the image was created with VkImageCreateInfo::samples not equal to 746 // VK_SAMPLE_COUNT_1_BIT, the instruction must have MS = 1. 747 if binding_reqs.image_multisampled 748 != (image_view.image().samples() != SampleCount::Sample1) 749 { 750 return Err( 751 DescriptorResourceInvalidError::ImageViewMultisampledMismatch { 752 required: binding_reqs.image_multisampled, 753 provided: image_view.image().samples() != SampleCount::Sample1, 754 }, 755 ); 756 } 757 758 // - If the Sampled Type of the OpTypeImage does not match the numeric format of the 759 // image, as shown in the SPIR-V Sampled Type column of the 760 // Interpretation of Numeric Format table. 761 // - If the signedness of any read or sample operation does not match the signedness of 762 // the image’s format. 763 if let Some(scalar_type) = binding_reqs.image_scalar_type { 764 let aspects = image_view.subresource_range().aspects; 765 let view_scalar_type = ShaderScalarType::from( 766 if aspects.intersects( 767 ImageAspects::COLOR 768 | ImageAspects::PLANE_0 769 | ImageAspects::PLANE_1 770 | ImageAspects::PLANE_2, 771 ) { 772 image_view.format().unwrap().type_color().unwrap() 773 } else if aspects.intersects(ImageAspects::DEPTH) { 774 image_view.format().unwrap().type_depth().unwrap() 775 } else if aspects.intersects(ImageAspects::STENCIL) { 776 image_view.format().unwrap().type_stencil().unwrap() 777 } else { 778 // Per `ImageViewBuilder::aspects` and 779 // VUID-VkDescriptorImageInfo-imageView-01976 780 unreachable!() 781 }, 782 ); 783 784 if scalar_type != view_scalar_type { 785 return Err( 786 DescriptorResourceInvalidError::ImageViewScalarTypeMismatch { 787 required: scalar_type, 788 provided: view_scalar_type, 789 }, 790 ); 791 } 792 } 793 794 Ok(()) 795 }; 796 797 let check_sampler_common = |index: u32, sampler: &Arc<Sampler>| { 798 for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter()) 799 .chain(binding_reqs.descriptors.get(&None)) 800 { 801 // VUID-vkCmdDispatch-None-02703 802 // VUID-vkCmdDispatch-None-02704 803 if desc_reqs.sampler_no_unnormalized_coordinates 804 && sampler.unnormalized_coordinates() 805 { 806 return Err( 807 DescriptorResourceInvalidError::SamplerUnnormalizedCoordinatesNotAllowed, 808 ); 809 } 810 811 // - OpImageFetch, OpImageSparseFetch, OpImage*Gather, and OpImageSparse*Gather must not 812 // be used with a sampler that enables sampler Y′CBCR conversion. 813 // - The ConstOffset and Offset operands must not be used with a sampler that enables 814 // sampler Y′CBCR conversion. 815 if desc_reqs.sampler_no_ycbcr_conversion 816 && sampler.sampler_ycbcr_conversion().is_some() 817 { 818 return Err( 819 DescriptorResourceInvalidError::SamplerYcbcrConversionNotAllowed, 820 ); 821 } 822 823 /* 824 Instruction/Sampler/Image View Validation 825 https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap16.html#textures-input-validation 826 */ 827 828 // - The SPIR-V instruction is one of the OpImage*Dref* instructions and the sampler 829 // compareEnable is VK_FALSE 830 // - The SPIR-V instruction is not one of the OpImage*Dref* instructions and the sampler 831 // compareEnable is VK_TRUE 832 if desc_reqs.sampler_compare != sampler.compare().is_some() { 833 return Err(DescriptorResourceInvalidError::SamplerCompareMismatch { 834 required: desc_reqs.sampler_compare, 835 provided: sampler.compare().is_some(), 836 }); 837 } 838 } 839 840 Ok(()) 841 }; 842 843 let check_image_view = |index: u32, image_view: &Arc<dyn ImageViewAbstract>| { 844 check_image_view_common(index, image_view)?; 845 846 if let Some(sampler) = layout_binding.immutable_samplers.get(index as usize) { 847 check_sampler_common(index, sampler)?; 848 } 849 850 Ok(()) 851 }; 852 853 let check_image_view_sampler = 854 |index: u32, (image_view, sampler): &(Arc<dyn ImageViewAbstract>, Arc<Sampler>)| { 855 check_image_view_common(index, image_view)?; 856 check_sampler_common(index, sampler)?; 857 858 Ok(()) 859 }; 860 861 let check_sampler = |index: u32, sampler: &Arc<Sampler>| { 862 check_sampler_common(index, sampler)?; 863 864 for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter()) 865 .chain(binding_reqs.descriptors.get(&None)) 866 { 867 // Check sampler-image compatibility. Only done for separate samplers; 868 // combined image samplers are checked when updating the descriptor set. 869 870 // If the image view isn't actually present in the resources, then just skip it. 871 // It will be caught later by check_resources. 872 let iter = desc_reqs.sampler_with_images.iter().filter_map(|id| { 873 current_state 874 .descriptor_set(pipeline.bind_point(), id.set) 875 .and_then(|set| set.resources().binding(id.binding)) 876 .and_then(|res| match res { 877 DescriptorBindingResources::ImageView(elements) => elements 878 .get(id.index as usize) 879 .and_then(|opt| opt.as_ref().map(|opt| (id, opt))), 880 _ => None, 881 }) 882 }); 883 884 for (id, image_view) in iter { 885 if let Err(error) = sampler.check_can_sample(image_view.as_ref()) { 886 return Err( 887 DescriptorResourceInvalidError::SamplerImageViewIncompatible { 888 image_view_set_num: id.set, 889 image_view_binding_num: id.binding, 890 image_view_index: id.index, 891 error, 892 }, 893 ); 894 } 895 } 896 } 897 898 Ok(()) 899 }; 900 901 let check_none = |index: u32, _: &()| { 902 if let Some(sampler) = layout_binding.immutable_samplers.get(index as usize) { 903 check_sampler(index, sampler)?; 904 } 905 906 Ok(()) 907 }; 908 909 let set_resources = match current_state.descriptor_set(pipeline.bind_point(), set_num) { 910 Some(x) => x.resources(), 911 None => return Err(PipelineExecutionError::DescriptorSetNotBound { set_num }), 912 }; 913 914 let binding_resources = set_resources.binding(binding_num).unwrap(); 915 916 match binding_resources { 917 DescriptorBindingResources::None(elements) => { 918 validate_resources(set_num, binding_num, binding_reqs, elements, check_none)?; 919 } 920 DescriptorBindingResources::Buffer(elements) => { 921 validate_resources(set_num, binding_num, binding_reqs, elements, check_buffer)?; 922 } 923 DescriptorBindingResources::BufferView(elements) => { 924 validate_resources( 925 set_num, 926 binding_num, 927 binding_reqs, 928 elements, 929 check_buffer_view, 930 )?; 931 } 932 DescriptorBindingResources::ImageView(elements) => { 933 validate_resources( 934 set_num, 935 binding_num, 936 binding_reqs, 937 elements, 938 check_image_view, 939 )?; 940 } 941 DescriptorBindingResources::ImageViewSampler(elements) => { 942 validate_resources( 943 set_num, 944 binding_num, 945 binding_reqs, 946 elements, 947 check_image_view_sampler, 948 )?; 949 } 950 DescriptorBindingResources::Sampler(elements) => { 951 validate_resources( 952 set_num, 953 binding_num, 954 binding_reqs, 955 elements, 956 check_sampler, 957 )?; 958 } 959 } 960 } 961 962 Ok(()) 963 } 964 validate_pipeline_push_constants( &self, pipeline_layout: &PipelineLayout, ) -> Result<(), PipelineExecutionError>965 fn validate_pipeline_push_constants( 966 &self, 967 pipeline_layout: &PipelineLayout, 968 ) -> Result<(), PipelineExecutionError> { 969 if pipeline_layout.push_constant_ranges().is_empty() 970 || self.device().enabled_features().maintenance4 971 { 972 return Ok(()); 973 } 974 975 let current_state = self.state(); 976 977 // VUID-vkCmdDispatch-maintenance4-06425 978 let constants_pipeline_layout = match current_state.push_constants_pipeline_layout() { 979 Some(x) => x, 980 None => return Err(PipelineExecutionError::PushConstantsMissing), 981 }; 982 983 // VUID-vkCmdDispatch-maintenance4-06425 984 if pipeline_layout.handle() != constants_pipeline_layout.handle() 985 && pipeline_layout.push_constant_ranges() 986 != constants_pipeline_layout.push_constant_ranges() 987 { 988 return Err(PipelineExecutionError::PushConstantsNotCompatible); 989 } 990 991 let set_bytes = current_state.push_constants(); 992 993 // VUID-vkCmdDispatch-maintenance4-06425 994 if !pipeline_layout 995 .push_constant_ranges() 996 .iter() 997 .all(|pc_range| set_bytes.contains(pc_range.offset..pc_range.offset + pc_range.size)) 998 { 999 return Err(PipelineExecutionError::PushConstantsMissing); 1000 } 1001 1002 Ok(()) 1003 } 1004 validate_pipeline_graphics_dynamic_state( &self, pipeline: &GraphicsPipeline, ) -> Result<(), PipelineExecutionError>1005 fn validate_pipeline_graphics_dynamic_state( 1006 &self, 1007 pipeline: &GraphicsPipeline, 1008 ) -> Result<(), PipelineExecutionError> { 1009 let device = pipeline.device(); 1010 let current_state = self.state(); 1011 1012 // VUID-vkCmdDraw-commandBuffer-02701 1013 for dynamic_state in pipeline 1014 .dynamic_states() 1015 .filter(|(_, d)| *d) 1016 .map(|(s, _)| s) 1017 { 1018 match dynamic_state { 1019 DynamicState::BlendConstants => { 1020 // VUID? 1021 if current_state.blend_constants().is_none() { 1022 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1023 } 1024 } 1025 DynamicState::ColorWriteEnable => { 1026 // VUID-vkCmdDraw-attachmentCount-06667 1027 let enables = if let Some(enables) = current_state.color_write_enable() { 1028 enables 1029 } else { 1030 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1031 }; 1032 1033 // VUID-vkCmdDraw-attachmentCount-06667 1034 if enables.len() < pipeline.color_blend_state().unwrap().attachments.len() { 1035 return Err( 1036 PipelineExecutionError::DynamicColorWriteEnableNotEnoughValues { 1037 color_write_enable_count: enables.len() as u32, 1038 attachment_count: pipeline 1039 .color_blend_state() 1040 .unwrap() 1041 .attachments 1042 .len() as u32, 1043 }, 1044 ); 1045 } 1046 } 1047 DynamicState::CullMode => { 1048 // VUID? 1049 if current_state.cull_mode().is_none() { 1050 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1051 } 1052 } 1053 DynamicState::DepthBias => { 1054 // VUID? 1055 if current_state.depth_bias().is_none() { 1056 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1057 } 1058 } 1059 DynamicState::DepthBiasEnable => { 1060 // VUID-vkCmdDraw-None-04877 1061 if current_state.depth_bias_enable().is_none() { 1062 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1063 } 1064 } 1065 DynamicState::DepthBounds => { 1066 // VUID? 1067 if current_state.depth_bounds().is_none() { 1068 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1069 } 1070 } 1071 DynamicState::DepthBoundsTestEnable => { 1072 // VUID? 1073 if current_state.depth_bounds_test_enable().is_none() { 1074 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1075 } 1076 } 1077 DynamicState::DepthCompareOp => { 1078 // VUID? 1079 if current_state.depth_compare_op().is_none() { 1080 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1081 } 1082 } 1083 DynamicState::DepthTestEnable => { 1084 // VUID? 1085 if current_state.depth_test_enable().is_none() { 1086 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1087 } 1088 } 1089 DynamicState::DepthWriteEnable => { 1090 // VUID? 1091 if current_state.depth_write_enable().is_none() { 1092 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1093 } 1094 1095 // TODO: Check if the depth buffer is writable 1096 } 1097 DynamicState::DiscardRectangle => { 1098 let discard_rectangle_count = 1099 match pipeline.discard_rectangle_state().unwrap().rectangles { 1100 PartialStateMode::Dynamic(count) => count, 1101 _ => unreachable!(), 1102 }; 1103 1104 for num in 0..discard_rectangle_count { 1105 // VUID? 1106 if current_state.discard_rectangle(num).is_none() { 1107 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1108 } 1109 } 1110 } 1111 DynamicState::ExclusiveScissor => todo!(), 1112 DynamicState::FragmentShadingRate => todo!(), 1113 DynamicState::FrontFace => { 1114 // VUID? 1115 if current_state.front_face().is_none() { 1116 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1117 } 1118 } 1119 DynamicState::LineStipple => { 1120 // VUID? 1121 if current_state.line_stipple().is_none() { 1122 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1123 } 1124 } 1125 DynamicState::LineWidth => { 1126 // VUID? 1127 if current_state.line_width().is_none() { 1128 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1129 } 1130 } 1131 DynamicState::LogicOp => { 1132 // VUID-vkCmdDraw-logicOp-04878 1133 if current_state.logic_op().is_none() { 1134 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1135 } 1136 } 1137 DynamicState::PatchControlPoints => { 1138 // VUID-vkCmdDraw-None-04875 1139 if current_state.patch_control_points().is_none() { 1140 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1141 } 1142 } 1143 DynamicState::PrimitiveRestartEnable => { 1144 // VUID-vkCmdDraw-None-04879 1145 let primitive_restart_enable = 1146 if let Some(enable) = current_state.primitive_restart_enable() { 1147 enable 1148 } else { 1149 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1150 }; 1151 1152 if primitive_restart_enable { 1153 let topology = match pipeline.input_assembly_state().topology { 1154 PartialStateMode::Fixed(topology) => topology, 1155 PartialStateMode::Dynamic(_) => { 1156 if let Some(topology) = current_state.primitive_topology() { 1157 topology 1158 } else { 1159 return Err(PipelineExecutionError::DynamicStateNotSet { 1160 dynamic_state: DynamicState::PrimitiveTopology, 1161 }); 1162 } 1163 } 1164 }; 1165 1166 match topology { 1167 PrimitiveTopology::PointList 1168 | PrimitiveTopology::LineList 1169 | PrimitiveTopology::TriangleList 1170 | PrimitiveTopology::LineListWithAdjacency 1171 | PrimitiveTopology::TriangleListWithAdjacency => { 1172 // VUID? 1173 if !device.enabled_features().primitive_topology_list_restart { 1174 return Err(PipelineExecutionError::RequirementNotMet { 1175 required_for: "The bound pipeline sets \ 1176 `DynamicState::PrimitiveRestartEnable` and the \ 1177 current primitive topology is \ 1178 `PrimitiveTopology::*List`", 1179 requires_one_of: RequiresOneOf { 1180 features: &["primitive_topology_list_restart"], 1181 ..Default::default() 1182 }, 1183 }); 1184 } 1185 } 1186 PrimitiveTopology::PatchList => { 1187 // VUID? 1188 if !device 1189 .enabled_features() 1190 .primitive_topology_patch_list_restart 1191 { 1192 return Err(PipelineExecutionError::RequirementNotMet { 1193 required_for: "The bound pipeline sets \ 1194 `DynamicState::PrimitiveRestartEnable` and the \ 1195 current primitive topology is \ 1196 `PrimitiveTopology::PatchList`", 1197 requires_one_of: RequiresOneOf { 1198 features: &["primitive_topology_patch_list_restart"], 1199 ..Default::default() 1200 }, 1201 }); 1202 } 1203 } 1204 _ => (), 1205 } 1206 } 1207 } 1208 DynamicState::PrimitiveTopology => { 1209 // VUID-vkCmdDraw-primitiveTopology-03420 1210 let topology = if let Some(topology) = current_state.primitive_topology() { 1211 topology 1212 } else { 1213 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1214 }; 1215 1216 if pipeline.shader(ShaderStage::TessellationControl).is_some() { 1217 // VUID? 1218 if !matches!(topology, PrimitiveTopology::PatchList) { 1219 return Err(PipelineExecutionError::DynamicPrimitiveTopologyInvalid { 1220 topology, 1221 }); 1222 } 1223 } else { 1224 // VUID? 1225 if matches!(topology, PrimitiveTopology::PatchList) { 1226 return Err(PipelineExecutionError::DynamicPrimitiveTopologyInvalid { 1227 topology, 1228 }); 1229 } 1230 } 1231 1232 let required_topology_class = match pipeline.input_assembly_state().topology { 1233 PartialStateMode::Dynamic(topology_class) => topology_class, 1234 _ => unreachable!(), 1235 }; 1236 1237 // VUID-vkCmdDraw-primitiveTopology-03420 1238 if topology.class() != required_topology_class { 1239 return Err( 1240 PipelineExecutionError::DynamicPrimitiveTopologyClassMismatch { 1241 provided_class: topology.class(), 1242 required_class: required_topology_class, 1243 }, 1244 ); 1245 } 1246 1247 // TODO: check that the topology matches the geometry shader 1248 } 1249 DynamicState::RasterizerDiscardEnable => { 1250 // VUID-vkCmdDraw-None-04876 1251 if current_state.rasterizer_discard_enable().is_none() { 1252 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1253 } 1254 } 1255 DynamicState::RayTracingPipelineStackSize => unreachable!( 1256 "RayTracingPipelineStackSize dynamic state should not occur on a graphics pipeline" 1257 ), 1258 DynamicState::SampleLocations => todo!(), 1259 DynamicState::Scissor => { 1260 for num in 0..pipeline.viewport_state().unwrap().count().unwrap() { 1261 // VUID? 1262 if current_state.scissor(num).is_none() { 1263 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1264 } 1265 } 1266 } 1267 DynamicState::ScissorWithCount => { 1268 // VUID-vkCmdDraw-scissorCount-03418 1269 // VUID-vkCmdDraw-viewportCount-03419 1270 let scissor_count = if let Some(scissors) = current_state.scissor_with_count() { 1271 scissors.len() as u32 1272 } else { 1273 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1274 }; 1275 1276 // Check if the counts match, but only if the viewport count is fixed. 1277 // If the viewport count is also dynamic, then the 1278 // DynamicState::ViewportWithCount match arm will handle it. 1279 if let Some(viewport_count) = pipeline.viewport_state().unwrap().count() { 1280 // VUID-vkCmdDraw-scissorCount-03418 1281 if viewport_count != scissor_count { 1282 return Err( 1283 PipelineExecutionError::DynamicViewportScissorCountMismatch { 1284 viewport_count, 1285 scissor_count, 1286 }, 1287 ); 1288 } 1289 } 1290 } 1291 DynamicState::StencilCompareMask => { 1292 let state = current_state.stencil_compare_mask(); 1293 1294 // VUID? 1295 if state.front.is_none() || state.back.is_none() { 1296 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1297 } 1298 } 1299 DynamicState::StencilOp => { 1300 let state = current_state.stencil_op(); 1301 1302 // VUID? 1303 if state.front.is_none() || state.back.is_none() { 1304 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1305 } 1306 } 1307 DynamicState::StencilReference => { 1308 let state = current_state.stencil_reference(); 1309 1310 // VUID? 1311 if state.front.is_none() || state.back.is_none() { 1312 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1313 } 1314 } 1315 DynamicState::StencilTestEnable => { 1316 // VUID? 1317 if current_state.stencil_test_enable().is_none() { 1318 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1319 } 1320 1321 // TODO: Check if the stencil buffer is writable 1322 } 1323 DynamicState::StencilWriteMask => { 1324 let state = current_state.stencil_write_mask(); 1325 1326 // VUID? 1327 if state.front.is_none() || state.back.is_none() { 1328 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1329 } 1330 } 1331 DynamicState::VertexInput => todo!(), 1332 DynamicState::VertexInputBindingStride => todo!(), 1333 DynamicState::Viewport => { 1334 for num in 0..pipeline.viewport_state().unwrap().count().unwrap() { 1335 // VUID? 1336 if current_state.viewport(num).is_none() { 1337 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1338 } 1339 } 1340 } 1341 DynamicState::ViewportCoarseSampleOrder => todo!(), 1342 DynamicState::ViewportShadingRatePalette => todo!(), 1343 DynamicState::ViewportWithCount => { 1344 // VUID-vkCmdDraw-viewportCount-03417 1345 let viewport_count = if let Some(viewports) = current_state.viewport_with_count() { 1346 viewports.len() as u32 1347 } else { 1348 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1349 }; 1350 1351 let scissor_count = if let Some(scissor_count) = 1352 pipeline.viewport_state().unwrap().count() 1353 { 1354 // The scissor count is fixed. 1355 scissor_count 1356 } else { 1357 // VUID-vkCmdDraw-viewportCount-03419 1358 // The scissor count is also dynamic. 1359 if let Some(scissors) = current_state.scissor_with_count() { 1360 scissors.len() as u32 1361 } else { 1362 return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state }); 1363 } 1364 }; 1365 1366 // VUID-vkCmdDraw-viewportCount-03417 1367 // VUID-vkCmdDraw-viewportCount-03419 1368 if viewport_count != scissor_count { 1369 return Err( 1370 PipelineExecutionError::DynamicViewportScissorCountMismatch { 1371 viewport_count, 1372 scissor_count, 1373 }, 1374 ); 1375 } 1376 1377 // TODO: VUID-vkCmdDrawIndexed-primitiveFragmentShadingRateWithMultipleViewports-04552 1378 // If the primitiveFragmentShadingRateWithMultipleViewports limit is not supported, 1379 // the bound graphics pipeline was created with the 1380 // VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT dynamic state enabled, and any of the 1381 // shader stages of the bound graphics pipeline write to the PrimitiveShadingRateKHR 1382 // built-in, then vkCmdSetViewportWithCountEXT must have been called in the current 1383 // command buffer prior to this drawing command, and the viewportCount parameter of 1384 // vkCmdSetViewportWithCountEXT must be 1 1385 } 1386 DynamicState::ViewportWScaling => todo!(), 1387 DynamicState::TessellationDomainOrigin => todo!(), 1388 DynamicState::DepthClampEnable => todo!(), 1389 DynamicState::PolygonMode => todo!(), 1390 DynamicState::RasterizationSamples => todo!(), 1391 DynamicState::SampleMask => todo!(), 1392 DynamicState::AlphaToCoverageEnable => todo!(), 1393 DynamicState::AlphaToOneEnable => todo!(), 1394 DynamicState::LogicOpEnable => todo!(), 1395 DynamicState::ColorBlendEnable => todo!(), 1396 DynamicState::ColorBlendEquation => todo!(), 1397 DynamicState::ColorWriteMask => todo!(), 1398 DynamicState::RasterizationStream => todo!(), 1399 DynamicState::ConservativeRasterizationMode => todo!(), 1400 DynamicState::ExtraPrimitiveOverestimationSize => todo!(), 1401 DynamicState::DepthClipEnable => todo!(), 1402 DynamicState::SampleLocationsEnable => todo!(), 1403 DynamicState::ColorBlendAdvanced => todo!(), 1404 DynamicState::ProvokingVertexMode => todo!(), 1405 DynamicState::LineRasterizationMode => todo!(), 1406 DynamicState::LineStippleEnable => todo!(), 1407 DynamicState::DepthClipNegativeOneToOne => todo!(), 1408 DynamicState::ViewportWScalingEnable => todo!(), 1409 DynamicState::ViewportSwizzle => todo!(), 1410 DynamicState::CoverageToColorEnable => todo!(), 1411 DynamicState::CoverageToColorLocation => todo!(), 1412 DynamicState::CoverageModulationMode => todo!(), 1413 DynamicState::CoverageModulationTableEnable => todo!(), 1414 DynamicState::CoverageModulationTable => todo!(), 1415 DynamicState::ShadingRateImageEnable => todo!(), 1416 DynamicState::RepresentativeFragmentTestEnable => todo!(), 1417 DynamicState::CoverageReductionMode => todo!(), 1418 } 1419 } 1420 1421 Ok(()) 1422 } 1423 validate_pipeline_graphics_render_pass( &self, pipeline: &GraphicsPipeline, render_pass_state: &RenderPassState, ) -> Result<(), PipelineExecutionError>1424 fn validate_pipeline_graphics_render_pass( 1425 &self, 1426 pipeline: &GraphicsPipeline, 1427 render_pass_state: &RenderPassState, 1428 ) -> Result<(), PipelineExecutionError> { 1429 // VUID? 1430 if render_pass_state.contents != SubpassContents::Inline { 1431 return Err(PipelineExecutionError::ForbiddenWithSubpassContents { 1432 subpass_contents: render_pass_state.contents, 1433 }); 1434 } 1435 1436 match (&render_pass_state.render_pass, pipeline.render_pass()) { 1437 ( 1438 RenderPassStateType::BeginRenderPass(state), 1439 PipelineRenderPassType::BeginRenderPass(pipeline_subpass), 1440 ) => { 1441 // VUID-vkCmdDraw-renderPass-02684 1442 if !pipeline_subpass 1443 .render_pass() 1444 .is_compatible_with(state.subpass.render_pass()) 1445 { 1446 return Err(PipelineExecutionError::PipelineRenderPassNotCompatible); 1447 } 1448 1449 // VUID-vkCmdDraw-subpass-02685 1450 if pipeline_subpass.index() != state.subpass.index() { 1451 return Err(PipelineExecutionError::PipelineSubpassMismatch { 1452 pipeline: pipeline_subpass.index(), 1453 current: state.subpass.index(), 1454 }); 1455 } 1456 } 1457 ( 1458 RenderPassStateType::BeginRendering(current_rendering_info), 1459 PipelineRenderPassType::BeginRendering(pipeline_rendering_info), 1460 ) => { 1461 // VUID-vkCmdDraw-viewMask-06178 1462 if pipeline_rendering_info.view_mask != render_pass_state.view_mask { 1463 return Err(PipelineExecutionError::PipelineViewMaskMismatch { 1464 pipeline_view_mask: pipeline_rendering_info.view_mask, 1465 required_view_mask: render_pass_state.view_mask, 1466 }); 1467 } 1468 1469 // VUID-vkCmdDraw-colorAttachmentCount-06179 1470 if pipeline_rendering_info.color_attachment_formats.len() 1471 != current_rendering_info.color_attachment_formats.len() 1472 { 1473 return Err( 1474 PipelineExecutionError::PipelineColorAttachmentCountMismatch { 1475 pipeline_count: pipeline_rendering_info.color_attachment_formats.len() 1476 as u32, 1477 required_count: current_rendering_info.color_attachment_formats.len() 1478 as u32, 1479 }, 1480 ); 1481 } 1482 1483 for (color_attachment_index, required_format, pipeline_format) in 1484 current_rendering_info 1485 .color_attachment_formats 1486 .iter() 1487 .zip( 1488 pipeline_rendering_info 1489 .color_attachment_formats 1490 .iter() 1491 .copied(), 1492 ) 1493 .enumerate() 1494 .filter_map(|(i, (r, p))| r.map(|r| (i as u32, r, p))) 1495 { 1496 // VUID-vkCmdDraw-colorAttachmentCount-06180 1497 if Some(required_format) != pipeline_format { 1498 return Err( 1499 PipelineExecutionError::PipelineColorAttachmentFormatMismatch { 1500 color_attachment_index, 1501 pipeline_format, 1502 required_format, 1503 }, 1504 ); 1505 } 1506 } 1507 1508 if let Some((required_format, pipeline_format)) = current_rendering_info 1509 .depth_attachment_format 1510 .map(|r| (r, pipeline_rendering_info.depth_attachment_format)) 1511 { 1512 // VUID-vkCmdDraw-pDepthAttachment-06181 1513 if Some(required_format) != pipeline_format { 1514 return Err( 1515 PipelineExecutionError::PipelineDepthAttachmentFormatMismatch { 1516 pipeline_format, 1517 required_format, 1518 }, 1519 ); 1520 } 1521 } 1522 1523 if let Some((required_format, pipeline_format)) = current_rendering_info 1524 .stencil_attachment_format 1525 .map(|r| (r, pipeline_rendering_info.stencil_attachment_format)) 1526 { 1527 // VUID-vkCmdDraw-pStencilAttachment-06182 1528 if Some(required_format) != pipeline_format { 1529 return Err( 1530 PipelineExecutionError::PipelineStencilAttachmentFormatMismatch { 1531 pipeline_format, 1532 required_format, 1533 }, 1534 ); 1535 } 1536 } 1537 1538 // VUID-vkCmdDraw-imageView-06172 1539 // VUID-vkCmdDraw-imageView-06173 1540 // VUID-vkCmdDraw-imageView-06174 1541 // VUID-vkCmdDraw-imageView-06175 1542 // VUID-vkCmdDraw-imageView-06176 1543 // VUID-vkCmdDraw-imageView-06177 1544 // TODO: 1545 } 1546 _ => return Err(PipelineExecutionError::PipelineRenderPassTypeMismatch), 1547 } 1548 1549 // VUID-vkCmdDraw-None-02686 1550 // TODO: 1551 1552 Ok(()) 1553 } 1554 validate_pipeline_graphics_vertex_buffers( &self, pipeline: &GraphicsPipeline, vertices: Option<(u32, u32)>, instances: Option<(u32, u32)>, ) -> Result<(), PipelineExecutionError>1555 fn validate_pipeline_graphics_vertex_buffers( 1556 &self, 1557 pipeline: &GraphicsPipeline, 1558 vertices: Option<(u32, u32)>, 1559 instances: Option<(u32, u32)>, 1560 ) -> Result<(), PipelineExecutionError> { 1561 let vertex_input = pipeline.vertex_input_state(); 1562 let mut vertices_in_buffers: Option<u64> = None; 1563 let mut instances_in_buffers: Option<u64> = None; 1564 let current_state = self.state(); 1565 1566 for (&binding_num, binding_desc) in &vertex_input.bindings { 1567 // VUID-vkCmdDraw-None-04007 1568 let vertex_buffer = match current_state.vertex_buffer(binding_num) { 1569 Some(x) => x, 1570 None => return Err(PipelineExecutionError::VertexBufferNotBound { binding_num }), 1571 }; 1572 1573 let mut num_elements = vertex_buffer.size() / binding_desc.stride as u64; 1574 1575 match binding_desc.input_rate { 1576 VertexInputRate::Vertex => { 1577 vertices_in_buffers = Some(if let Some(x) = vertices_in_buffers { 1578 min(x, num_elements) 1579 } else { 1580 num_elements 1581 }); 1582 } 1583 VertexInputRate::Instance { divisor } => { 1584 if divisor == 0 { 1585 // A divisor of 0 means the same instance data is used for all instances, 1586 // so we can draw any number of instances from a single element. 1587 // The buffer must contain at least one element though. 1588 if num_elements != 0 { 1589 num_elements = u64::MAX; 1590 } 1591 } else { 1592 // If divisor is e.g. 2, we use only half the amount of data from the source 1593 // buffer, so the number of instances that can be drawn is twice as large. 1594 num_elements = num_elements.saturating_mul(divisor as u64); 1595 } 1596 1597 instances_in_buffers = Some(if let Some(x) = instances_in_buffers { 1598 min(x, num_elements) 1599 } else { 1600 num_elements 1601 }); 1602 } 1603 }; 1604 } 1605 1606 if let Some((first_vertex, vertex_count)) = vertices { 1607 let vertices_needed = first_vertex as u64 + vertex_count as u64; 1608 1609 if let Some(vertices_in_buffers) = vertices_in_buffers { 1610 // VUID-vkCmdDraw-None-02721 1611 if vertices_needed > vertices_in_buffers { 1612 return Err(PipelineExecutionError::VertexBufferVertexRangeOutOfBounds { 1613 vertices_needed, 1614 vertices_in_buffers, 1615 }); 1616 } 1617 } 1618 } 1619 1620 if let Some((first_instance, instance_count)) = instances { 1621 let instances_needed = first_instance as u64 + instance_count as u64; 1622 1623 if let Some(instances_in_buffers) = instances_in_buffers { 1624 // VUID-vkCmdDraw-None-02721 1625 if instances_needed > instances_in_buffers { 1626 return Err( 1627 PipelineExecutionError::VertexBufferInstanceRangeOutOfBounds { 1628 instances_needed, 1629 instances_in_buffers, 1630 }, 1631 ); 1632 } 1633 } 1634 1635 let view_mask = match pipeline.render_pass() { 1636 PipelineRenderPassType::BeginRenderPass(subpass) => { 1637 subpass.render_pass().views_used() 1638 } 1639 PipelineRenderPassType::BeginRendering(rendering_info) => rendering_info.view_mask, 1640 }; 1641 1642 if view_mask != 0 { 1643 let max = pipeline 1644 .device() 1645 .physical_device() 1646 .properties() 1647 .max_multiview_instance_index 1648 .unwrap_or(0); 1649 1650 let highest_instance = instances_needed.saturating_sub(1); 1651 1652 // VUID-vkCmdDraw-maxMultiviewInstanceIndex-02688 1653 if highest_instance > max as u64 { 1654 return Err(PipelineExecutionError::MaxMultiviewInstanceIndexExceeded { 1655 highest_instance, 1656 max, 1657 }); 1658 } 1659 } 1660 } 1661 1662 Ok(()) 1663 } 1664 } 1665 1666 impl SyncCommandBufferBuilder { 1667 /// Calls `vkCmdDispatch` on the builder. 1668 #[inline] dispatch( &mut self, group_counts: [u32; 3], ) -> Result<(), SyncCommandBufferBuilderError>1669 pub unsafe fn dispatch( 1670 &mut self, 1671 group_counts: [u32; 3], 1672 ) -> Result<(), SyncCommandBufferBuilderError> { 1673 struct Cmd { 1674 group_counts: [u32; 3], 1675 } 1676 1677 impl Command for Cmd { name(&self) -> &'static str1678 fn name(&self) -> &'static str { 1679 "dispatch" 1680 } 1681 send(&self, out: &mut UnsafeCommandBufferBuilder)1682 unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { 1683 out.dispatch(self.group_counts); 1684 } 1685 } 1686 1687 let command_index = self.commands.len(); 1688 let command_name = "dispatch"; 1689 let pipeline = self 1690 .current_state 1691 .pipeline_compute 1692 .as_ref() 1693 .unwrap() 1694 .as_ref(); 1695 1696 let mut resources = Vec::new(); 1697 self.add_descriptor_sets(&mut resources, command_index, command_name, pipeline); 1698 1699 for resource in &resources { 1700 self.check_resource_conflicts(resource)?; 1701 } 1702 1703 self.commands.push(Box::new(Cmd { group_counts })); 1704 1705 for resource in resources { 1706 self.add_resource(resource); 1707 } 1708 1709 Ok(()) 1710 } 1711 1712 /// Calls `vkCmdDispatchIndirect` on the builder. 1713 #[inline] dispatch_indirect( &mut self, indirect_buffer: Subbuffer<[DispatchIndirectCommand]>, ) -> Result<(), SyncCommandBufferBuilderError>1714 pub unsafe fn dispatch_indirect( 1715 &mut self, 1716 indirect_buffer: Subbuffer<[DispatchIndirectCommand]>, 1717 ) -> Result<(), SyncCommandBufferBuilderError> { 1718 struct Cmd { 1719 indirect_buffer: Subbuffer<[DispatchIndirectCommand]>, 1720 } 1721 1722 impl Command for Cmd { 1723 fn name(&self) -> &'static str { 1724 "dispatch_indirect" 1725 } 1726 1727 unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { 1728 out.dispatch_indirect(&self.indirect_buffer); 1729 } 1730 } 1731 1732 let command_index = self.commands.len(); 1733 let command_name = "dispatch_indirect"; 1734 let pipeline = self 1735 .current_state 1736 .pipeline_compute 1737 .as_ref() 1738 .unwrap() 1739 .as_ref(); 1740 1741 let mut resources = Vec::new(); 1742 self.add_descriptor_sets(&mut resources, command_index, command_name, pipeline); 1743 self.add_indirect_buffer( 1744 &mut resources, 1745 command_index, 1746 command_name, 1747 indirect_buffer.as_bytes(), 1748 ); 1749 1750 for resource in &resources { 1751 self.check_resource_conflicts(resource)?; 1752 } 1753 1754 self.commands.push(Box::new(Cmd { indirect_buffer })); 1755 1756 for resource in resources { 1757 self.add_resource(resource); 1758 } 1759 1760 Ok(()) 1761 } 1762 1763 /// Calls `vkCmdDraw` on the builder. 1764 #[inline] draw( &mut self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32, ) -> Result<(), SyncCommandBufferBuilderError>1765 pub unsafe fn draw( 1766 &mut self, 1767 vertex_count: u32, 1768 instance_count: u32, 1769 first_vertex: u32, 1770 first_instance: u32, 1771 ) -> Result<(), SyncCommandBufferBuilderError> { 1772 struct Cmd { 1773 vertex_count: u32, 1774 instance_count: u32, 1775 first_vertex: u32, 1776 first_instance: u32, 1777 } 1778 1779 impl Command for Cmd { 1780 fn name(&self) -> &'static str { 1781 "draw" 1782 } 1783 1784 unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { 1785 out.draw( 1786 self.vertex_count, 1787 self.instance_count, 1788 self.first_vertex, 1789 self.first_instance, 1790 ); 1791 } 1792 } 1793 1794 let command_index = self.commands.len(); 1795 let command_name = "draw"; 1796 let pipeline = self 1797 .current_state 1798 .pipeline_graphics 1799 .as_ref() 1800 .unwrap() 1801 .as_ref(); 1802 1803 let mut resources = Vec::new(); 1804 self.add_descriptor_sets(&mut resources, command_index, command_name, pipeline); 1805 self.add_vertex_buffers(&mut resources, command_index, command_name, pipeline); 1806 1807 for resource in &resources { 1808 self.check_resource_conflicts(resource)?; 1809 } 1810 1811 self.commands.push(Box::new(Cmd { 1812 vertex_count, 1813 instance_count, 1814 first_vertex, 1815 first_instance, 1816 })); 1817 1818 for resource in resources { 1819 self.add_resource(resource); 1820 } 1821 1822 Ok(()) 1823 } 1824 1825 /// Calls `vkCmdDrawIndexed` on the builder. 1826 #[inline] draw_indexed( &mut self, index_count: u32, instance_count: u32, first_index: u32, vertex_offset: i32, first_instance: u32, ) -> Result<(), SyncCommandBufferBuilderError>1827 pub unsafe fn draw_indexed( 1828 &mut self, 1829 index_count: u32, 1830 instance_count: u32, 1831 first_index: u32, 1832 vertex_offset: i32, 1833 first_instance: u32, 1834 ) -> Result<(), SyncCommandBufferBuilderError> { 1835 struct Cmd { 1836 index_count: u32, 1837 instance_count: u32, 1838 first_index: u32, 1839 vertex_offset: i32, 1840 first_instance: u32, 1841 } 1842 1843 impl Command for Cmd { 1844 fn name(&self) -> &'static str { 1845 "draw_indexed" 1846 } 1847 1848 unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { 1849 out.draw_indexed( 1850 self.index_count, 1851 self.instance_count, 1852 self.first_index, 1853 self.vertex_offset, 1854 self.first_instance, 1855 ); 1856 } 1857 } 1858 1859 let command_index = self.commands.len(); 1860 let command_name = "draw_indexed"; 1861 let pipeline = self 1862 .current_state 1863 .pipeline_graphics 1864 .as_ref() 1865 .unwrap() 1866 .as_ref(); 1867 1868 let mut resources = Vec::new(); 1869 self.add_descriptor_sets(&mut resources, command_index, command_name, pipeline); 1870 self.add_vertex_buffers(&mut resources, command_index, command_name, pipeline); 1871 self.add_index_buffer(&mut resources, command_index, command_name); 1872 1873 for resource in &resources { 1874 self.check_resource_conflicts(resource)?; 1875 } 1876 1877 self.commands.push(Box::new(Cmd { 1878 index_count, 1879 instance_count, 1880 first_index, 1881 vertex_offset, 1882 first_instance, 1883 })); 1884 1885 for resource in resources { 1886 self.add_resource(resource); 1887 } 1888 1889 Ok(()) 1890 } 1891 1892 /// Calls `vkCmdDrawIndirect` on the builder. 1893 #[inline] draw_indirect( &mut self, indirect_buffer: Subbuffer<[DrawIndirectCommand]>, draw_count: u32, stride: u32, ) -> Result<(), SyncCommandBufferBuilderError>1894 pub unsafe fn draw_indirect( 1895 &mut self, 1896 indirect_buffer: Subbuffer<[DrawIndirectCommand]>, 1897 draw_count: u32, 1898 stride: u32, 1899 ) -> Result<(), SyncCommandBufferBuilderError> { 1900 struct Cmd { 1901 indirect_buffer: Subbuffer<[DrawIndirectCommand]>, 1902 draw_count: u32, 1903 stride: u32, 1904 } 1905 1906 impl Command for Cmd { 1907 fn name(&self) -> &'static str { 1908 "draw_indirect" 1909 } 1910 1911 unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { 1912 out.draw_indirect(&self.indirect_buffer, self.draw_count, self.stride); 1913 } 1914 } 1915 1916 let command_index = self.commands.len(); 1917 let command_name = "draw_indirect"; 1918 let pipeline = self 1919 .current_state 1920 .pipeline_graphics 1921 .as_ref() 1922 .unwrap() 1923 .as_ref(); 1924 1925 let mut resources = Vec::new(); 1926 self.add_descriptor_sets(&mut resources, command_index, command_name, pipeline); 1927 self.add_vertex_buffers(&mut resources, command_index, command_name, pipeline); 1928 self.add_indirect_buffer( 1929 &mut resources, 1930 command_index, 1931 command_name, 1932 indirect_buffer.as_bytes(), 1933 ); 1934 1935 for resource in &resources { 1936 self.check_resource_conflicts(resource)?; 1937 } 1938 1939 self.commands.push(Box::new(Cmd { 1940 indirect_buffer, 1941 draw_count, 1942 stride, 1943 })); 1944 1945 for resource in resources { 1946 self.add_resource(resource); 1947 } 1948 1949 Ok(()) 1950 } 1951 1952 /// Calls `vkCmdDrawIndexedIndirect` on the builder. 1953 #[inline] draw_indexed_indirect( &mut self, indirect_buffer: Subbuffer<[DrawIndexedIndirectCommand]>, draw_count: u32, stride: u32, ) -> Result<(), SyncCommandBufferBuilderError>1954 pub unsafe fn draw_indexed_indirect( 1955 &mut self, 1956 indirect_buffer: Subbuffer<[DrawIndexedIndirectCommand]>, 1957 draw_count: u32, 1958 stride: u32, 1959 ) -> Result<(), SyncCommandBufferBuilderError> { 1960 struct Cmd { 1961 indirect_buffer: Subbuffer<[DrawIndexedIndirectCommand]>, 1962 draw_count: u32, 1963 stride: u32, 1964 } 1965 1966 impl Command for Cmd { 1967 fn name(&self) -> &'static str { 1968 "draw_indexed_indirect" 1969 } 1970 1971 unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { 1972 out.draw_indexed_indirect(&self.indirect_buffer, self.draw_count, self.stride); 1973 } 1974 } 1975 1976 let command_index = self.commands.len(); 1977 let command_name = "draw_indexed_indirect"; 1978 let pipeline = self 1979 .current_state 1980 .pipeline_graphics 1981 .as_ref() 1982 .unwrap() 1983 .as_ref(); 1984 1985 let mut resources = Vec::new(); 1986 self.add_descriptor_sets(&mut resources, command_index, command_name, pipeline); 1987 self.add_vertex_buffers(&mut resources, command_index, command_name, pipeline); 1988 self.add_index_buffer(&mut resources, command_index, command_name); 1989 self.add_indirect_buffer( 1990 &mut resources, 1991 command_index, 1992 command_name, 1993 indirect_buffer.as_bytes(), 1994 ); 1995 1996 for resource in &resources { 1997 self.check_resource_conflicts(resource)?; 1998 } 1999 2000 self.commands.push(Box::new(Cmd { 2001 indirect_buffer, 2002 draw_count, 2003 stride, 2004 })); 2005 2006 for resource in resources { 2007 self.add_resource(resource); 2008 } 2009 2010 Ok(()) 2011 } 2012 add_descriptor_sets<Pl: Pipeline>( &self, resources: &mut Vec<(ResourceUseRef, Resource)>, command_index: usize, command_name: &'static str, pipeline: &Pl, )2013 fn add_descriptor_sets<Pl: Pipeline>( 2014 &self, 2015 resources: &mut Vec<(ResourceUseRef, Resource)>, 2016 command_index: usize, 2017 command_name: &'static str, 2018 pipeline: &Pl, 2019 ) { 2020 let descriptor_sets_state = match self 2021 .current_state 2022 .descriptor_sets 2023 .get(&pipeline.bind_point()) 2024 { 2025 Some(x) => x, 2026 None => return, 2027 }; 2028 2029 for (&(set, binding), binding_reqs) in pipeline.descriptor_binding_requirements() { 2030 // TODO: Can things be refactored so that the pipeline layout isn't needed at all? 2031 let descriptor_type = descriptor_sets_state.pipeline_layout.set_layouts()[set as usize] 2032 .bindings()[&binding] 2033 .descriptor_type; 2034 2035 let (access_read, access_write) = match descriptor_type { 2036 DescriptorType::Sampler => continue, 2037 DescriptorType::InputAttachment => { 2038 // FIXME: This is tricky. Since we read from the input attachment 2039 // and this input attachment is being written in an earlier pass, 2040 // vulkano will think that it needs to put a pipeline barrier and will 2041 // return a `Conflict` error. For now as a work-around we simply ignore 2042 // input attachments. 2043 continue; 2044 } 2045 DescriptorType::CombinedImageSampler 2046 | DescriptorType::SampledImage 2047 | DescriptorType::UniformTexelBuffer => (Some(AccessFlags::SHADER_READ), None), 2048 DescriptorType::StorageImage 2049 | DescriptorType::StorageTexelBuffer 2050 | DescriptorType::StorageBuffer 2051 | DescriptorType::StorageBufferDynamic => ( 2052 Some(AccessFlags::SHADER_READ), 2053 Some(AccessFlags::SHADER_WRITE), 2054 ), 2055 DescriptorType::UniformBuffer | DescriptorType::UniformBufferDynamic => { 2056 (Some(AccessFlags::UNIFORM_READ), None) 2057 } 2058 }; 2059 2060 let memory_iter = move |index: u32| { 2061 let mut stages_read = PipelineStages::empty(); 2062 let mut stages_write = PipelineStages::empty(); 2063 2064 for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter()) 2065 .chain(binding_reqs.descriptors.get(&None)) 2066 { 2067 stages_read |= desc_reqs.memory_read.into(); 2068 stages_write |= desc_reqs.memory_write.into(); 2069 } 2070 2071 let memory_read = (!stages_read.is_empty()).then(|| PipelineMemoryAccess { 2072 stages: stages_read, 2073 access: access_read.unwrap(), 2074 exclusive: false, 2075 }); 2076 let memory_write = (!stages_write.is_empty()).then(|| PipelineMemoryAccess { 2077 stages: stages_write, 2078 access: access_write.unwrap(), 2079 exclusive: true, 2080 }); 2081 2082 [memory_read, memory_write].into_iter().flatten() 2083 }; 2084 let buffer_resource = 2085 |(index, buffer, range): (u32, Subbuffer<[u8]>, Range<DeviceSize>)| { 2086 memory_iter(index).map(move |memory| { 2087 ( 2088 ResourceUseRef { 2089 command_index, 2090 command_name, 2091 resource_in_command: ResourceInCommand::DescriptorSet { 2092 set, 2093 binding, 2094 index, 2095 }, 2096 secondary_use_ref: None, 2097 }, 2098 Resource::Buffer { 2099 buffer: buffer.clone(), 2100 range: range.clone(), 2101 memory, 2102 }, 2103 ) 2104 }) 2105 }; 2106 let image_resource = |(index, image, subresource_range): ( 2107 u32, 2108 Arc<dyn ImageAccess>, 2109 ImageSubresourceRange, 2110 )| { 2111 let layout = image 2112 .descriptor_layouts() 2113 .expect("descriptor_layouts must return Some when used in an image view") 2114 .layout_for(descriptor_type); 2115 2116 memory_iter(index).map(move |memory| { 2117 ( 2118 ResourceUseRef { 2119 command_index, 2120 command_name, 2121 resource_in_command: ResourceInCommand::DescriptorSet { 2122 set, 2123 binding, 2124 index, 2125 }, 2126 secondary_use_ref: None, 2127 }, 2128 Resource::Image { 2129 image: image.clone(), 2130 subresource_range: subresource_range.clone(), 2131 memory, 2132 start_layout: layout, 2133 end_layout: layout, 2134 }, 2135 ) 2136 }) 2137 }; 2138 2139 let descriptor_set_state = &descriptor_sets_state.descriptor_sets[&set]; 2140 2141 match descriptor_set_state.resources().binding(binding).unwrap() { 2142 DescriptorBindingResources::None(_) => continue, 2143 DescriptorBindingResources::Buffer(elements) => { 2144 if matches!( 2145 descriptor_type, 2146 DescriptorType::UniformBufferDynamic | DescriptorType::StorageBufferDynamic 2147 ) { 2148 let dynamic_offsets = descriptor_set_state.dynamic_offsets(); 2149 resources.extend( 2150 (elements.iter().enumerate()) 2151 .filter_map(|(index, element)| { 2152 element.as_ref().map(|(buffer, range)| { 2153 let dynamic_offset = dynamic_offsets[index] as DeviceSize; 2154 2155 ( 2156 index as u32, 2157 buffer.clone(), 2158 dynamic_offset + range.start 2159 ..dynamic_offset + range.end, 2160 ) 2161 }) 2162 }) 2163 .flat_map(buffer_resource), 2164 ); 2165 } else { 2166 resources.extend( 2167 (elements.iter().enumerate()) 2168 .filter_map(|(index, element)| { 2169 element.as_ref().map(|(buffer, range)| { 2170 (index as u32, buffer.clone(), range.clone()) 2171 }) 2172 }) 2173 .flat_map(buffer_resource), 2174 ); 2175 } 2176 } 2177 DescriptorBindingResources::BufferView(elements) => { 2178 resources.extend( 2179 (elements.iter().enumerate()) 2180 .filter_map(|(index, element)| { 2181 element.as_ref().map(|buffer_view| { 2182 ( 2183 index as u32, 2184 buffer_view.buffer().clone(), 2185 buffer_view.range(), 2186 ) 2187 }) 2188 }) 2189 .flat_map(buffer_resource), 2190 ); 2191 } 2192 DescriptorBindingResources::ImageView(elements) => { 2193 resources.extend( 2194 (elements.iter().enumerate()) 2195 .filter_map(|(index, element)| { 2196 element.as_ref().map(|image_view| { 2197 ( 2198 index as u32, 2199 image_view.image(), 2200 image_view.subresource_range().clone(), 2201 ) 2202 }) 2203 }) 2204 .flat_map(image_resource), 2205 ); 2206 } 2207 DescriptorBindingResources::ImageViewSampler(elements) => { 2208 resources.extend( 2209 (elements.iter().enumerate()) 2210 .filter_map(|(index, element)| { 2211 element.as_ref().map(|(image_view, _)| { 2212 ( 2213 index as u32, 2214 image_view.image(), 2215 image_view.subresource_range().clone(), 2216 ) 2217 }) 2218 }) 2219 .flat_map(image_resource), 2220 ); 2221 } 2222 DescriptorBindingResources::Sampler(_) => (), 2223 } 2224 } 2225 } 2226 add_vertex_buffers( &self, resources: &mut Vec<(ResourceUseRef, Resource)>, command_index: usize, command_name: &'static str, pipeline: &GraphicsPipeline, )2227 fn add_vertex_buffers( 2228 &self, 2229 resources: &mut Vec<(ResourceUseRef, Resource)>, 2230 command_index: usize, 2231 command_name: &'static str, 2232 pipeline: &GraphicsPipeline, 2233 ) { 2234 resources.extend( 2235 pipeline 2236 .vertex_input_state() 2237 .bindings 2238 .iter() 2239 .map(|(&binding, _)| { 2240 let vertex_buffer = &self.current_state.vertex_buffers[&binding]; 2241 ( 2242 ResourceUseRef { 2243 command_index, 2244 command_name, 2245 resource_in_command: ResourceInCommand::VertexBuffer { binding }, 2246 secondary_use_ref: None, 2247 }, 2248 Resource::Buffer { 2249 buffer: vertex_buffer.clone(), 2250 range: 0..vertex_buffer.size(), // TODO: 2251 memory: PipelineMemoryAccess { 2252 stages: PipelineStages::VERTEX_INPUT, 2253 access: AccessFlags::VERTEX_ATTRIBUTE_READ, 2254 exclusive: false, 2255 }, 2256 }, 2257 ) 2258 }), 2259 ); 2260 } 2261 add_index_buffer( &self, resources: &mut Vec<(ResourceUseRef, Resource)>, command_index: usize, command_name: &'static str, )2262 fn add_index_buffer( 2263 &self, 2264 resources: &mut Vec<(ResourceUseRef, Resource)>, 2265 command_index: usize, 2266 command_name: &'static str, 2267 ) { 2268 let index_buffer = &self.current_state.index_buffer.as_ref().unwrap().0; 2269 resources.push(( 2270 ResourceUseRef { 2271 command_index, 2272 command_name, 2273 resource_in_command: ResourceInCommand::IndexBuffer, 2274 secondary_use_ref: None, 2275 }, 2276 Resource::Buffer { 2277 buffer: index_buffer.clone(), 2278 range: 0..index_buffer.size(), // TODO: 2279 memory: PipelineMemoryAccess { 2280 stages: PipelineStages::VERTEX_INPUT, 2281 access: AccessFlags::INDEX_READ, 2282 exclusive: false, 2283 }, 2284 }, 2285 )); 2286 } 2287 add_indirect_buffer( &self, resources: &mut Vec<(ResourceUseRef, Resource)>, command_index: usize, command_name: &'static str, indirect_buffer: &Subbuffer<[u8]>, )2288 fn add_indirect_buffer( 2289 &self, 2290 resources: &mut Vec<(ResourceUseRef, Resource)>, 2291 command_index: usize, 2292 command_name: &'static str, 2293 indirect_buffer: &Subbuffer<[u8]>, 2294 ) { 2295 resources.push(( 2296 ResourceUseRef { 2297 command_index, 2298 command_name, 2299 resource_in_command: ResourceInCommand::IndirectBuffer, 2300 secondary_use_ref: None, 2301 }, 2302 Resource::Buffer { 2303 buffer: indirect_buffer.clone(), 2304 range: 0..indirect_buffer.size(), // TODO: 2305 memory: PipelineMemoryAccess { 2306 stages: PipelineStages::DRAW_INDIRECT, 2307 access: AccessFlags::INDIRECT_COMMAND_READ, 2308 exclusive: false, 2309 }, 2310 }, 2311 )); 2312 } 2313 } 2314 2315 impl UnsafeCommandBufferBuilder { 2316 /// Calls `vkCmdDispatch` on the builder. 2317 #[inline] dispatch(&mut self, group_counts: [u32; 3])2318 pub unsafe fn dispatch(&mut self, group_counts: [u32; 3]) { 2319 debug_assert!({ 2320 let max_group_counts = self 2321 .device 2322 .physical_device() 2323 .properties() 2324 .max_compute_work_group_count; 2325 group_counts[0] <= max_group_counts[0] 2326 && group_counts[1] <= max_group_counts[1] 2327 && group_counts[2] <= max_group_counts[2] 2328 }); 2329 2330 let fns = self.device.fns(); 2331 (fns.v1_0.cmd_dispatch)( 2332 self.handle, 2333 group_counts[0], 2334 group_counts[1], 2335 group_counts[2], 2336 ); 2337 } 2338 2339 /// Calls `vkCmdDispatchIndirect` on the builder. 2340 #[inline] dispatch_indirect(&mut self, buffer: &Subbuffer<[DispatchIndirectCommand]>)2341 pub unsafe fn dispatch_indirect(&mut self, buffer: &Subbuffer<[DispatchIndirectCommand]>) { 2342 let fns = self.device.fns(); 2343 2344 debug_assert!(buffer.offset() < buffer.buffer().size()); 2345 debug_assert!(buffer 2346 .buffer() 2347 .usage() 2348 .intersects(BufferUsage::INDIRECT_BUFFER)); 2349 debug_assert_eq!(buffer.offset() % 4, 0); 2350 2351 (fns.v1_0.cmd_dispatch_indirect)(self.handle, buffer.buffer().handle(), buffer.offset()); 2352 } 2353 2354 /// Calls `vkCmdDraw` on the builder. 2355 #[inline] draw( &mut self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32, )2356 pub unsafe fn draw( 2357 &mut self, 2358 vertex_count: u32, 2359 instance_count: u32, 2360 first_vertex: u32, 2361 first_instance: u32, 2362 ) { 2363 let fns = self.device.fns(); 2364 (fns.v1_0.cmd_draw)( 2365 self.handle, 2366 vertex_count, 2367 instance_count, 2368 first_vertex, 2369 first_instance, 2370 ); 2371 } 2372 2373 /// Calls `vkCmdDrawIndexed` on the builder. 2374 #[inline] draw_indexed( &mut self, index_count: u32, instance_count: u32, first_index: u32, vertex_offset: i32, first_instance: u32, )2375 pub unsafe fn draw_indexed( 2376 &mut self, 2377 index_count: u32, 2378 instance_count: u32, 2379 first_index: u32, 2380 vertex_offset: i32, 2381 first_instance: u32, 2382 ) { 2383 let fns = self.device.fns(); 2384 (fns.v1_0.cmd_draw_indexed)( 2385 self.handle, 2386 index_count, 2387 instance_count, 2388 first_index, 2389 vertex_offset, 2390 first_instance, 2391 ); 2392 } 2393 2394 /// Calls `vkCmdDrawIndirect` on the builder. 2395 #[inline] draw_indirect( &mut self, buffer: &Subbuffer<[DrawIndirectCommand]>, draw_count: u32, stride: u32, )2396 pub unsafe fn draw_indirect( 2397 &mut self, 2398 buffer: &Subbuffer<[DrawIndirectCommand]>, 2399 draw_count: u32, 2400 stride: u32, 2401 ) { 2402 let fns = self.device.fns(); 2403 2404 debug_assert!( 2405 draw_count == 0 2406 || ((stride % 4) == 0) 2407 && stride as usize >= size_of::<ash::vk::DrawIndirectCommand>() 2408 ); 2409 2410 debug_assert!(buffer.offset() < buffer.buffer().size()); 2411 debug_assert!(buffer 2412 .buffer() 2413 .usage() 2414 .intersects(BufferUsage::INDIRECT_BUFFER)); 2415 2416 (fns.v1_0.cmd_draw_indirect)( 2417 self.handle, 2418 buffer.buffer().handle(), 2419 buffer.offset(), 2420 draw_count, 2421 stride, 2422 ); 2423 } 2424 2425 /// Calls `vkCmdDrawIndexedIndirect` on the builder. 2426 #[inline] draw_indexed_indirect( &mut self, buffer: &Subbuffer<[DrawIndexedIndirectCommand]>, draw_count: u32, stride: u32, )2427 pub unsafe fn draw_indexed_indirect( 2428 &mut self, 2429 buffer: &Subbuffer<[DrawIndexedIndirectCommand]>, 2430 draw_count: u32, 2431 stride: u32, 2432 ) { 2433 let fns = self.device.fns(); 2434 2435 debug_assert!(buffer.offset() < buffer.buffer().size()); 2436 debug_assert!(buffer 2437 .buffer() 2438 .usage() 2439 .intersects(BufferUsage::INDIRECT_BUFFER)); 2440 2441 (fns.v1_0.cmd_draw_indexed_indirect)( 2442 self.handle, 2443 buffer.buffer().handle(), 2444 buffer.offset(), 2445 draw_count, 2446 stride, 2447 ); 2448 } 2449 } 2450 2451 /// Error that can happen when recording a bound pipeline execution command. 2452 #[derive(Debug, Clone)] 2453 pub enum PipelineExecutionError { 2454 SyncCommandBufferBuilderError(SyncCommandBufferBuilderError), 2455 2456 RequirementNotMet { 2457 required_for: &'static str, 2458 requires_one_of: RequiresOneOf, 2459 }, 2460 2461 /// The resource bound to a descriptor set binding at a particular index is not compatible 2462 /// with the requirements of the pipeline and shaders. 2463 DescriptorResourceInvalid { 2464 set_num: u32, 2465 binding_num: u32, 2466 index: u32, 2467 error: DescriptorResourceInvalidError, 2468 }, 2469 2470 /// The pipeline layout requires a descriptor set bound to a set number, but none was bound. 2471 DescriptorSetNotBound { 2472 set_num: u32, 2473 }, 2474 2475 /// The bound pipeline uses a dynamic color write enable setting, but the number of provided 2476 /// enable values is less than the number of attachments in the current render subpass. 2477 DynamicColorWriteEnableNotEnoughValues { 2478 color_write_enable_count: u32, 2479 attachment_count: u32, 2480 }, 2481 2482 /// The bound pipeline uses a dynamic primitive topology, but the provided topology is of a 2483 /// different topology class than what the pipeline requires. 2484 DynamicPrimitiveTopologyClassMismatch { 2485 provided_class: PrimitiveTopologyClass, 2486 required_class: PrimitiveTopologyClass, 2487 }, 2488 2489 /// The bound pipeline uses a dynamic primitive topology, but the provided topology is not 2490 /// compatible with the shader stages in the pipeline. 2491 DynamicPrimitiveTopologyInvalid { 2492 topology: PrimitiveTopology, 2493 }, 2494 2495 /// The pipeline requires a particular dynamic state, but this state was not set. 2496 DynamicStateNotSet { 2497 dynamic_state: DynamicState, 2498 }, 2499 2500 /// The bound pipeline uses a dynamic scissor and/or viewport count, but the scissor count 2501 /// does not match the viewport count. 2502 DynamicViewportScissorCountMismatch { 2503 viewport_count: u32, 2504 scissor_count: u32, 2505 }, 2506 2507 /// Operation forbidden inside a render pass. 2508 ForbiddenInsideRenderPass, 2509 2510 /// Operation forbidden outside a render pass. 2511 ForbiddenOutsideRenderPass, 2512 2513 /// Operation forbidden inside a render subpass with the specified contents. 2514 ForbiddenWithSubpassContents { 2515 subpass_contents: SubpassContents, 2516 }, 2517 2518 /// An indexed draw command was recorded, but no index buffer was bound. 2519 IndexBufferNotBound, 2520 2521 /// The highest index to be drawn exceeds the available number of indices in the bound index 2522 /// buffer. 2523 IndexBufferRangeOutOfBounds { 2524 highest_index: u32, 2525 max_index_count: u32, 2526 }, 2527 2528 /// The `indirect_buffer` usage was not enabled on the indirect buffer. 2529 IndirectBufferMissingUsage, 2530 2531 /// The `max_compute_work_group_count` limit has been exceeded. 2532 MaxComputeWorkGroupCountExceeded { 2533 requested: [u32; 3], 2534 max: [u32; 3], 2535 }, 2536 2537 /// The `max_draw_indirect_count` limit has been exceeded. 2538 MaxDrawIndirectCountExceeded { 2539 provided: u32, 2540 max: u32, 2541 }, 2542 2543 /// The `max_multiview_instance_index` limit has been exceeded. 2544 MaxMultiviewInstanceIndexExceeded { 2545 highest_instance: u64, 2546 max: u32, 2547 }, 2548 2549 /// The queue family doesn't allow this operation. 2550 NotSupportedByQueueFamily, 2551 2552 /// The color attachment count in the bound pipeline does not match the count of the current 2553 /// render pass. 2554 PipelineColorAttachmentCountMismatch { 2555 pipeline_count: u32, 2556 required_count: u32, 2557 }, 2558 2559 /// The format of a color attachment in the bound pipeline does not match the format of the 2560 /// corresponding color attachment in the current render pass. 2561 PipelineColorAttachmentFormatMismatch { 2562 color_attachment_index: u32, 2563 pipeline_format: Option<Format>, 2564 required_format: Format, 2565 }, 2566 2567 /// The format of the depth attachment in the bound pipeline does not match the format of the 2568 /// depth attachment in the current render pass. 2569 PipelineDepthAttachmentFormatMismatch { 2570 pipeline_format: Option<Format>, 2571 required_format: Format, 2572 }, 2573 2574 /// The bound pipeline is not compatible with the layout used to bind the descriptor sets. 2575 PipelineLayoutNotCompatible, 2576 2577 /// No pipeline was bound to the bind point used by the operation. 2578 PipelineNotBound, 2579 2580 /// The bound graphics pipeline uses a render pass that is not compatible with the currently 2581 /// active render pass. 2582 PipelineRenderPassNotCompatible, 2583 2584 /// The bound graphics pipeline uses a render pass of a different type than the currently 2585 /// active render pass. 2586 PipelineRenderPassTypeMismatch, 2587 2588 /// The bound graphics pipeline uses a render subpass index that doesn't match the currently 2589 /// active subpass index. 2590 PipelineSubpassMismatch { 2591 pipeline: u32, 2592 current: u32, 2593 }, 2594 2595 /// The format of the stencil attachment in the bound pipeline does not match the format of the 2596 /// stencil attachment in the current render pass. 2597 PipelineStencilAttachmentFormatMismatch { 2598 pipeline_format: Option<Format>, 2599 required_format: Format, 2600 }, 2601 2602 /// The view mask of the bound pipeline does not match the view mask of the current render pass. 2603 PipelineViewMaskMismatch { 2604 pipeline_view_mask: u32, 2605 required_view_mask: u32, 2606 }, 2607 2608 /// The push constants are not compatible with the pipeline layout. 2609 PushConstantsNotCompatible, 2610 2611 /// Not all push constants used by the pipeline have been set. 2612 PushConstantsMissing, 2613 2614 /// The bound graphics pipeline requires a vertex buffer bound to a binding number, but none 2615 /// was bound. 2616 VertexBufferNotBound { 2617 binding_num: u32, 2618 }, 2619 2620 /// The number of instances to be drawn exceeds the available number of indices in the 2621 /// bound vertex buffers used by the pipeline. 2622 VertexBufferInstanceRangeOutOfBounds { 2623 instances_needed: u64, 2624 instances_in_buffers: u64, 2625 }, 2626 2627 /// The number of vertices to be drawn exceeds the lowest available number of vertices in the 2628 /// bound vertex buffers used by the pipeline. 2629 VertexBufferVertexRangeOutOfBounds { 2630 vertices_needed: u64, 2631 vertices_in_buffers: u64, 2632 }, 2633 } 2634 2635 impl Error for PipelineExecutionError { source(&self) -> Option<&(dyn Error + 'static)>2636 fn source(&self) -> Option<&(dyn Error + 'static)> { 2637 match self { 2638 Self::SyncCommandBufferBuilderError(err) => Some(err), 2639 Self::DescriptorResourceInvalid { error, .. } => Some(error), 2640 _ => None, 2641 } 2642 } 2643 } 2644 2645 impl Display for PipelineExecutionError { fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>2646 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { 2647 match self { 2648 Self::SyncCommandBufferBuilderError(_) => write!(f, "a SyncCommandBufferBuilderError"), 2649 Self::RequirementNotMet { 2650 required_for, 2651 requires_one_of, 2652 } => write!( 2653 f, 2654 "a requirement was not met for: {}; requires one of: {}", 2655 required_for, requires_one_of, 2656 ), 2657 Self::DescriptorResourceInvalid { 2658 set_num, 2659 binding_num, 2660 index, 2661 .. 2662 } => write!( 2663 f, 2664 "the resource bound to descriptor set {} binding {} at index {} is not compatible \ 2665 with the requirements of the pipeline and shaders", 2666 set_num, binding_num, index, 2667 ), 2668 Self::DescriptorSetNotBound { set_num } => write!( 2669 f, 2670 "the pipeline layout requires a descriptor set bound to set number {}, but none \ 2671 was bound", 2672 set_num, 2673 ), 2674 Self::DynamicColorWriteEnableNotEnoughValues { 2675 color_write_enable_count, 2676 attachment_count, 2677 } => write!( 2678 f, 2679 "the bound pipeline uses a dynamic color write enable setting, but the number of \ 2680 provided enable values ({}) is less than the number of attachments in the current \ 2681 render subpass ({})", 2682 color_write_enable_count, attachment_count, 2683 ), 2684 Self::DynamicPrimitiveTopologyClassMismatch { 2685 provided_class, 2686 required_class, 2687 } => write!( 2688 f, 2689 "The bound pipeline uses a dynamic primitive topology, but the provided topology \ 2690 is of a different topology class ({:?}) than what the pipeline requires ({:?})", 2691 provided_class, required_class, 2692 ), 2693 Self::DynamicPrimitiveTopologyInvalid { topology } => write!( 2694 f, 2695 "the bound pipeline uses a dynamic primitive topology, but the provided topology \ 2696 ({:?}) is not compatible with the shader stages in the pipeline", 2697 topology, 2698 ), 2699 Self::DynamicStateNotSet { dynamic_state } => write!( 2700 f, 2701 "the pipeline requires the dynamic state {:?}, but this state was not set", 2702 dynamic_state, 2703 ), 2704 Self::DynamicViewportScissorCountMismatch { 2705 viewport_count, 2706 scissor_count, 2707 } => write!( 2708 f, 2709 "the bound pipeline uses a dynamic scissor and/or viewport count, but the scissor \ 2710 count ({}) does not match the viewport count ({})", 2711 scissor_count, viewport_count, 2712 ), 2713 Self::ForbiddenInsideRenderPass => { 2714 write!(f, "operation forbidden inside a render pass") 2715 } 2716 Self::ForbiddenOutsideRenderPass => { 2717 write!(f, "operation forbidden outside a render pass") 2718 } 2719 Self::ForbiddenWithSubpassContents { subpass_contents } => write!( 2720 f, 2721 "operation forbidden inside a render subpass with contents {:?}", 2722 subpass_contents, 2723 ), 2724 Self::IndexBufferNotBound => write!( 2725 f, 2726 "an indexed draw command was recorded, but no index buffer was bound", 2727 ), 2728 Self::IndexBufferRangeOutOfBounds { 2729 highest_index, 2730 max_index_count, 2731 } => write!( 2732 f, 2733 "the highest index to be drawn ({}) exceeds the available number of indices in the \ 2734 bound index buffer ({})", 2735 highest_index, max_index_count, 2736 ), 2737 Self::IndirectBufferMissingUsage => write!( 2738 f, 2739 "the `indirect_buffer` usage was not enabled on the indirect buffer", 2740 ), 2741 Self::MaxComputeWorkGroupCountExceeded { .. } => write!( 2742 f, 2743 "the `max_compute_work_group_count` limit has been exceeded", 2744 ), 2745 Self::MaxDrawIndirectCountExceeded { .. } => { 2746 write!(f, "the `max_draw_indirect_count` limit has been exceeded") 2747 } 2748 Self::MaxMultiviewInstanceIndexExceeded { .. } => write!( 2749 f, 2750 "the `max_multiview_instance_index` limit has been exceeded", 2751 ), 2752 Self::NotSupportedByQueueFamily => { 2753 write!(f, "the queue family doesn't allow this operation") 2754 } 2755 Self::PipelineColorAttachmentCountMismatch { 2756 pipeline_count, 2757 required_count, 2758 } => write!( 2759 f, 2760 "the color attachment count in the bound pipeline ({}) does not match the count of \ 2761 the current render pass ({})", 2762 pipeline_count, required_count, 2763 ), 2764 Self::PipelineColorAttachmentFormatMismatch { 2765 color_attachment_index, 2766 pipeline_format, 2767 required_format, 2768 } => write!( 2769 f, 2770 "the format of color attachment {} in the bound pipeline ({:?}) does not match the \ 2771 format of the corresponding color attachment in the current render pass ({:?})", 2772 color_attachment_index, pipeline_format, required_format, 2773 ), 2774 Self::PipelineDepthAttachmentFormatMismatch { 2775 pipeline_format, 2776 required_format, 2777 } => write!( 2778 f, 2779 "the format of the depth attachment in the bound pipeline ({:?}) does not match \ 2780 the format of the depth attachment in the current render pass ({:?})", 2781 pipeline_format, required_format, 2782 ), 2783 Self::PipelineLayoutNotCompatible => write!( 2784 f, 2785 "the bound pipeline is not compatible with the layout used to bind the descriptor \ 2786 sets", 2787 ), 2788 Self::PipelineNotBound => write!( 2789 f, 2790 "no pipeline was bound to the bind point used by the operation", 2791 ), 2792 Self::PipelineRenderPassNotCompatible => write!( 2793 f, 2794 "the bound graphics pipeline uses a render pass that is not compatible with the \ 2795 currently active render pass", 2796 ), 2797 Self::PipelineRenderPassTypeMismatch => write!( 2798 f, 2799 "the bound graphics pipeline uses a render pass of a different type than the \ 2800 currently active render pass", 2801 ), 2802 Self::PipelineSubpassMismatch { pipeline, current } => write!( 2803 f, 2804 "the bound graphics pipeline uses a render subpass index ({}) that doesn't match \ 2805 the currently active subpass index ({})", 2806 pipeline, current, 2807 ), 2808 Self::PipelineStencilAttachmentFormatMismatch { 2809 pipeline_format, 2810 required_format, 2811 } => write!( 2812 f, 2813 "the format of the stencil attachment in the bound pipeline ({:?}) does not match \ 2814 the format of the stencil attachment in the current render pass ({:?})", 2815 pipeline_format, required_format, 2816 ), 2817 Self::PipelineViewMaskMismatch { 2818 pipeline_view_mask, 2819 required_view_mask, 2820 } => write!( 2821 f, 2822 "the view mask of the bound pipeline ({}) does not match the view mask of the \ 2823 current render pass ({})", 2824 pipeline_view_mask, required_view_mask, 2825 ), 2826 Self::PushConstantsNotCompatible => write!( 2827 f, 2828 "the push constants are not compatible with the pipeline layout", 2829 ), 2830 Self::PushConstantsMissing => write!( 2831 f, 2832 "not all push constants used by the pipeline have been set", 2833 ), 2834 Self::VertexBufferNotBound { binding_num } => write!( 2835 f, 2836 "the bound graphics pipeline requires a vertex buffer bound to binding number {}, \ 2837 but none was bound", 2838 binding_num, 2839 ), 2840 Self::VertexBufferInstanceRangeOutOfBounds { 2841 instances_needed, 2842 instances_in_buffers, 2843 } => write!( 2844 f, 2845 "the number of instances to be drawn ({}) exceeds the available number of \ 2846 instances in the bound vertex buffers ({}) used by the pipeline", 2847 instances_needed, instances_in_buffers, 2848 ), 2849 Self::VertexBufferVertexRangeOutOfBounds { 2850 vertices_needed, 2851 vertices_in_buffers, 2852 } => write!( 2853 f, 2854 "the number of vertices to be drawn ({}) exceeds the available number of vertices \ 2855 in the bound vertex buffers ({}) used by the pipeline", 2856 vertices_needed, vertices_in_buffers, 2857 ), 2858 } 2859 } 2860 } 2861 2862 impl From<SyncCommandBufferBuilderError> for PipelineExecutionError { from(err: SyncCommandBufferBuilderError) -> Self2863 fn from(err: SyncCommandBufferBuilderError) -> Self { 2864 Self::SyncCommandBufferBuilderError(err) 2865 } 2866 } 2867 2868 #[derive(Clone, Copy, Debug)] 2869 pub enum DescriptorResourceInvalidError { 2870 ImageViewFormatMismatch { 2871 required: Format, 2872 provided: Option<Format>, 2873 }, 2874 ImageViewMultisampledMismatch { 2875 required: bool, 2876 provided: bool, 2877 }, 2878 ImageViewScalarTypeMismatch { 2879 required: ShaderScalarType, 2880 provided: ShaderScalarType, 2881 }, 2882 ImageViewTypeMismatch { 2883 required: ImageViewType, 2884 provided: ImageViewType, 2885 }, 2886 Missing, 2887 SamplerCompareMismatch { 2888 required: bool, 2889 provided: bool, 2890 }, 2891 SamplerImageViewIncompatible { 2892 image_view_set_num: u32, 2893 image_view_binding_num: u32, 2894 image_view_index: u32, 2895 error: SamplerImageViewIncompatibleError, 2896 }, 2897 SamplerUnnormalizedCoordinatesNotAllowed, 2898 SamplerYcbcrConversionNotAllowed, 2899 StorageImageAtomicNotSupported, 2900 StorageReadWithoutFormatNotSupported, 2901 StorageWriteWithoutFormatNotSupported, 2902 } 2903 2904 impl Error for DescriptorResourceInvalidError { source(&self) -> Option<&(dyn Error + 'static)>2905 fn source(&self) -> Option<&(dyn Error + 'static)> { 2906 match self { 2907 Self::SamplerImageViewIncompatible { error, .. } => Some(error), 2908 _ => None, 2909 } 2910 } 2911 } 2912 2913 impl Display for DescriptorResourceInvalidError { fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>2914 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { 2915 match self { 2916 Self::ImageViewFormatMismatch { provided, required } => write!( 2917 f, 2918 "the format of the bound image view ({:?}) does not match what the pipeline \ 2919 requires ({:?})", 2920 provided, required, 2921 ), 2922 Self::ImageViewMultisampledMismatch { provided, required } => write!( 2923 f, 2924 "the multisampling of the bound image ({}) does not match what the pipeline \ 2925 requires ({})", 2926 provided, required, 2927 ), 2928 Self::ImageViewScalarTypeMismatch { provided, required } => write!( 2929 f, 2930 "the scalar type of the format and aspect of the bound image view ({:?}) does not \ 2931 match what the pipeline requires ({:?})", 2932 provided, required, 2933 ), 2934 Self::ImageViewTypeMismatch { provided, required } => write!( 2935 f, 2936 "the image view type of the bound image view ({:?}) does not match what the \ 2937 pipeline requires ({:?})", 2938 provided, required, 2939 ), 2940 Self::Missing => write!(f, "no resource was bound"), 2941 Self::SamplerImageViewIncompatible { .. } => write!( 2942 f, 2943 "the bound sampler samples an image view that is not compatible with that sampler", 2944 ), 2945 Self::SamplerCompareMismatch { provided, required } => write!( 2946 f, 2947 "the depth comparison state of the bound sampler ({}) does not match what the \ 2948 pipeline requires ({})", 2949 provided, required, 2950 ), 2951 Self::SamplerUnnormalizedCoordinatesNotAllowed => write!( 2952 f, 2953 "the bound sampler is required to have unnormalized coordinates disabled", 2954 ), 2955 Self::SamplerYcbcrConversionNotAllowed => write!( 2956 f, 2957 "the bound sampler is required to have no attached sampler YCbCr conversion", 2958 ), 2959 Self::StorageImageAtomicNotSupported => write!( 2960 f, 2961 "the bound image view does not support the `storage_image_atomic` format feature", 2962 ), 2963 Self::StorageReadWithoutFormatNotSupported => write!( 2964 f, 2965 "the bound image view or buffer view does not support the \ 2966 `storage_read_without_format` format feature", 2967 ), 2968 Self::StorageWriteWithoutFormatNotSupported => write!( 2969 f, 2970 "the bound image view or buffer view does not support the \ 2971 `storage_write_without_format` format feature", 2972 ), 2973 } 2974 } 2975 } 2976