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 super::{CommandBufferBuilder, RenderPassStateType, SetOrPush}; 11 use crate::{ 12 buffer::{BufferContents, BufferUsage, Subbuffer}, 13 command_buffer::{allocator::CommandBufferAllocator, commands::bind_push::BindPushError}, 14 descriptor_set::{ 15 check_descriptor_write, layout::DescriptorType, DescriptorBindingResources, 16 DescriptorSetResources, DescriptorSetWithOffsets, DescriptorSetsCollection, 17 DescriptorWriteInfo, WriteDescriptorSet, 18 }, 19 device::{DeviceOwned, QueueFlags}, 20 memory::is_aligned, 21 pipeline::{ 22 graphics::{ 23 input_assembly::{Index, IndexType}, 24 render_pass::PipelineRenderPassType, 25 vertex_input::VertexBuffersCollection, 26 }, 27 ComputePipeline, GraphicsPipeline, PipelineBindPoint, PipelineLayout, 28 }, 29 DeviceSize, RequiresOneOf, VulkanObject, 30 }; 31 use smallvec::SmallVec; 32 use std::{cmp::min, mem::size_of_val, os::raw::c_void, sync::Arc}; 33 34 impl<L, A> CommandBufferBuilder<L, A> 35 where 36 A: CommandBufferAllocator, 37 { 38 /// Binds descriptor sets for future dispatch or draw calls. 39 /// 40 /// # Panics 41 /// 42 /// - Panics if the queue family of the command buffer does not support `pipeline_bind_point`. 43 /// - Panics if the highest descriptor set slot being bound is not less than the number of sets 44 /// in `pipeline_layout`. 45 /// - Panics if `self` and any element of `descriptor_sets` do not belong to the same device. bind_descriptor_sets( &mut self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: Arc<PipelineLayout>, first_set: u32, descriptor_sets: impl DescriptorSetsCollection, ) -> &mut Self46 pub fn bind_descriptor_sets( 47 &mut self, 48 pipeline_bind_point: PipelineBindPoint, 49 pipeline_layout: Arc<PipelineLayout>, 50 first_set: u32, 51 descriptor_sets: impl DescriptorSetsCollection, 52 ) -> &mut Self { 53 let descriptor_sets = descriptor_sets.into_vec(); 54 self.validate_bind_descriptor_sets( 55 pipeline_bind_point, 56 &pipeline_layout, 57 first_set, 58 &descriptor_sets, 59 ) 60 .unwrap(); 61 62 unsafe { 63 self.bind_descriptor_sets_unchecked( 64 pipeline_bind_point, 65 pipeline_layout, 66 first_set, 67 descriptor_sets, 68 ) 69 } 70 } 71 validate_bind_descriptor_sets( &self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: &PipelineLayout, first_set: u32, descriptor_sets: &[DescriptorSetWithOffsets], ) -> Result<(), BindPushError>72 fn validate_bind_descriptor_sets( 73 &self, 74 pipeline_bind_point: PipelineBindPoint, 75 pipeline_layout: &PipelineLayout, 76 first_set: u32, 77 descriptor_sets: &[DescriptorSetWithOffsets], 78 ) -> Result<(), BindPushError> { 79 // VUID-vkCmdBindDescriptorSets-pipelineBindPoint-parameter 80 pipeline_bind_point.validate_device(self.device())?; 81 82 let queue_family_properties = self.queue_family_properties(); 83 84 // VUID-vkCmdBindDescriptorSets-commandBuffer-cmdpool 85 // VUID-vkCmdBindDescriptorSets-pipelineBindPoint-00361 86 match pipeline_bind_point { 87 PipelineBindPoint::Compute => { 88 if !queue_family_properties 89 .queue_flags 90 .intersects(QueueFlags::COMPUTE) 91 { 92 return Err(BindPushError::NotSupportedByQueueFamily); 93 } 94 } 95 PipelineBindPoint::Graphics => { 96 if !queue_family_properties 97 .queue_flags 98 .intersects(QueueFlags::GRAPHICS) 99 { 100 return Err(BindPushError::NotSupportedByQueueFamily); 101 } 102 } 103 } 104 105 // VUID-vkCmdBindDescriptorSets-firstSet-00360 106 if first_set + descriptor_sets.len() as u32 > pipeline_layout.set_layouts().len() as u32 { 107 return Err(BindPushError::DescriptorSetOutOfRange { 108 set_num: first_set + descriptor_sets.len() as u32, 109 pipeline_layout_set_count: pipeline_layout.set_layouts().len() as u32, 110 }); 111 } 112 113 let properties = self.device().physical_device().properties(); 114 let uniform_alignment = properties.min_uniform_buffer_offset_alignment; 115 let storage_alignment = properties.min_storage_buffer_offset_alignment; 116 117 for (i, set) in descriptor_sets.iter().enumerate() { 118 let set_num = first_set + i as u32; 119 let (set, dynamic_offsets) = set.as_ref(); 120 121 // VUID-vkCmdBindDescriptorSets-commonparent 122 assert_eq!(self.device(), set.device()); 123 124 let set_layout = set.layout(); 125 let pipeline_set_layout = &pipeline_layout.set_layouts()[set_num as usize]; 126 127 // VUID-vkCmdBindDescriptorSets-pDescriptorSets-00358 128 if !pipeline_set_layout.is_compatible_with(set_layout) { 129 return Err(BindPushError::DescriptorSetNotCompatible { set_num }); 130 } 131 132 let mut dynamic_offsets_remaining = dynamic_offsets; 133 let mut required_dynamic_offset_count = 0; 134 135 for (&binding_num, binding) in set_layout.bindings() { 136 let required_alignment = match binding.descriptor_type { 137 DescriptorType::UniformBufferDynamic => uniform_alignment, 138 DescriptorType::StorageBufferDynamic => storage_alignment, 139 _ => continue, 140 }; 141 142 let count = if binding.variable_descriptor_count { 143 set.variable_descriptor_count() 144 } else { 145 binding.descriptor_count 146 } as usize; 147 148 required_dynamic_offset_count += count; 149 150 if !dynamic_offsets_remaining.is_empty() { 151 let split_index = min(count, dynamic_offsets_remaining.len()); 152 let dynamic_offsets = &dynamic_offsets_remaining[..split_index]; 153 dynamic_offsets_remaining = &dynamic_offsets_remaining[split_index..]; 154 155 let elements = match set.resources().binding(binding_num) { 156 Some(DescriptorBindingResources::Buffer(elements)) => elements.as_slice(), 157 _ => unreachable!(), 158 }; 159 160 for (index, (&offset, element)) in 161 dynamic_offsets.iter().zip(elements).enumerate() 162 { 163 // VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01971 164 // VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01972 165 if !is_aligned(offset as DeviceSize, required_alignment) { 166 return Err(BindPushError::DynamicOffsetNotAligned { 167 set_num, 168 binding_num, 169 index: index as u32, 170 offset, 171 required_alignment, 172 }); 173 } 174 175 if let Some((buffer, range)) = element { 176 // VUID-vkCmdBindDescriptorSets-pDescriptorSets-01979 177 if offset as DeviceSize + range.end > buffer.size() { 178 return Err(BindPushError::DynamicOffsetOutOfBufferBounds { 179 set_num, 180 binding_num, 181 index: index as u32, 182 offset, 183 range_end: range.end, 184 buffer_size: buffer.size(), 185 }); 186 } 187 } 188 } 189 } 190 } 191 192 // VUID-vkCmdBindDescriptorSets-dynamicOffsetCount-00359 193 if dynamic_offsets.len() != required_dynamic_offset_count { 194 return Err(BindPushError::DynamicOffsetCountMismatch { 195 set_num, 196 provided_count: dynamic_offsets.len(), 197 required_count: required_dynamic_offset_count, 198 }); 199 } 200 } 201 202 Ok(()) 203 } 204 205 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] bind_descriptor_sets_unchecked( &mut self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: Arc<PipelineLayout>, first_set: u32, descriptor_sets: impl DescriptorSetsCollection, ) -> &mut Self206 pub unsafe fn bind_descriptor_sets_unchecked( 207 &mut self, 208 pipeline_bind_point: PipelineBindPoint, 209 pipeline_layout: Arc<PipelineLayout>, 210 first_set: u32, 211 descriptor_sets: impl DescriptorSetsCollection, 212 ) -> &mut Self { 213 let descriptor_sets_with_offsets = descriptor_sets.into_vec(); 214 215 if descriptor_sets_with_offsets.is_empty() { 216 return self; 217 } 218 219 let descriptor_sets: SmallVec<[_; 12]> = descriptor_sets_with_offsets 220 .iter() 221 .map(|x| x.as_ref().0.inner().handle()) 222 .collect(); 223 let dynamic_offsets: SmallVec<[_; 32]> = descriptor_sets_with_offsets 224 .iter() 225 .flat_map(|x| x.as_ref().1.iter().copied()) 226 .collect(); 227 228 let fns = self.device().fns(); 229 (fns.v1_0.cmd_bind_descriptor_sets)( 230 self.handle(), 231 pipeline_bind_point.into(), 232 pipeline_layout.handle(), 233 first_set, 234 descriptor_sets.len() as u32, 235 descriptor_sets.as_ptr(), 236 dynamic_offsets.len() as u32, 237 dynamic_offsets.as_ptr(), 238 ); 239 240 let state = self.builder_state.invalidate_descriptor_sets( 241 pipeline_bind_point, 242 pipeline_layout.clone(), 243 first_set, 244 descriptor_sets_with_offsets.len() as u32, 245 ); 246 247 self.resources 248 .reserve(descriptor_sets_with_offsets.len() + 1); 249 250 for (set_num, descriptor_set_with_offsets) in 251 descriptor_sets_with_offsets.into_iter().enumerate() 252 { 253 let descriptor_set = descriptor_set_with_offsets.as_ref().0.clone(); 254 state.descriptor_sets.insert( 255 first_set + set_num as u32, 256 SetOrPush::Set(descriptor_set_with_offsets), 257 ); 258 self.resources.push(Box::new(descriptor_set)); 259 } 260 261 self.resources.push(Box::new(pipeline_layout)); 262 263 self.next_command_index += 1; 264 self 265 } 266 267 /// Binds an index buffer for future indexed draw calls. 268 /// 269 /// # Panics 270 /// 271 /// - Panics if the queue family of the command buffer does not support graphics operations. 272 /// - Panics if `self` and `index_buffer` do not belong to the same device. 273 /// - Panics if `index_buffer` does not have the [`BufferUsage::INDEX_BUFFER`] usage enabled. 274 /// - If the index buffer contains `u8` indices, panics if the [`index_type_uint8`] feature is 275 /// not enabled on the device. 276 /// 277 /// [`BufferUsage::INDEX_BUFFER`]: crate::buffer::BufferUsage::INDEX_BUFFER 278 /// [`index_type_uint8`]: crate::device::Features::index_type_uint8 bind_index_buffer<I: Index>(&mut self, index_buffer: Subbuffer<[I]>) -> &mut Self279 pub fn bind_index_buffer<I: Index>(&mut self, index_buffer: Subbuffer<[I]>) -> &mut Self { 280 self.validate_bind_index_buffer(index_buffer.as_bytes(), I::ty()) 281 .unwrap(); 282 283 unsafe { self.bind_index_buffer_unchecked(index_buffer.into_bytes(), I::ty()) } 284 } 285 validate_bind_index_buffer( &self, index_buffer: &Subbuffer<[u8]>, index_type: IndexType, ) -> Result<(), BindPushError>286 fn validate_bind_index_buffer( 287 &self, 288 index_buffer: &Subbuffer<[u8]>, 289 index_type: IndexType, 290 ) -> Result<(), BindPushError> { 291 let queue_family_properties = self.queue_family_properties(); 292 293 // VUID-vkCmdBindIndexBuffer-commandBuffer-cmdpool 294 if !queue_family_properties 295 .queue_flags 296 .intersects(QueueFlags::GRAPHICS) 297 { 298 return Err(BindPushError::NotSupportedByQueueFamily); 299 } 300 301 // VUID-vkCmdBindIndexBuffer-commonparent 302 assert_eq!(self.device(), index_buffer.device()); 303 304 // VUID-vkCmdBindIndexBuffer-buffer-00433 305 if !index_buffer 306 .buffer() 307 .usage() 308 .intersects(BufferUsage::INDEX_BUFFER) 309 { 310 return Err(BindPushError::IndexBufferMissingUsage); 311 } 312 313 // VUID-vkCmdBindIndexBuffer-indexType-02765 314 if index_type == IndexType::U8 && !self.device().enabled_features().index_type_uint8 { 315 return Err(BindPushError::RequirementNotMet { 316 required_for: "`index_type` is `IndexType::U8`", 317 requires_one_of: RequiresOneOf { 318 features: &["index_type_uint8"], 319 ..Default::default() 320 }, 321 }); 322 } 323 324 // TODO: 325 // VUID-vkCmdBindIndexBuffer-offset-00432 326 327 Ok(()) 328 } 329 330 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] bind_index_buffer_unchecked( &mut self, buffer: Subbuffer<[u8]>, index_type: IndexType, ) -> &mut Self331 pub unsafe fn bind_index_buffer_unchecked( 332 &mut self, 333 buffer: Subbuffer<[u8]>, 334 index_type: IndexType, 335 ) -> &mut Self { 336 let fns = self.device().fns(); 337 (fns.v1_0.cmd_bind_index_buffer)( 338 self.handle(), 339 buffer.buffer().handle(), 340 buffer.offset(), 341 index_type.into(), 342 ); 343 344 self.builder_state.index_buffer = Some((buffer.clone(), index_type)); 345 self.resources.push(Box::new(buffer)); 346 347 self.next_command_index += 1; 348 self 349 } 350 351 /// Binds a compute pipeline for future dispatch calls. 352 /// 353 /// # Panics 354 /// 355 /// - Panics if the queue family of the command buffer does not support compute operations. 356 /// - Panics if `self` and `pipeline` do not belong to the same device. bind_pipeline_compute(&mut self, pipeline: Arc<ComputePipeline>) -> &mut Self357 pub fn bind_pipeline_compute(&mut self, pipeline: Arc<ComputePipeline>) -> &mut Self { 358 self.validate_bind_pipeline_compute(&pipeline).unwrap(); 359 360 unsafe { self.bind_pipeline_compute_unchecked(pipeline) } 361 } 362 validate_bind_pipeline_compute( &self, pipeline: &ComputePipeline, ) -> Result<(), BindPushError>363 fn validate_bind_pipeline_compute( 364 &self, 365 pipeline: &ComputePipeline, 366 ) -> Result<(), BindPushError> { 367 let queue_family_properties = self.queue_family_properties(); 368 369 // VUID-vkCmdBindPipeline-pipelineBindPoint-00777 370 if !queue_family_properties 371 .queue_flags 372 .intersects(QueueFlags::COMPUTE) 373 { 374 return Err(BindPushError::NotSupportedByQueueFamily); 375 } 376 377 // VUID-vkCmdBindPipeline-commonparent 378 assert_eq!(self.device(), pipeline.device()); 379 380 Ok(()) 381 } 382 383 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] bind_pipeline_compute_unchecked( &mut self, pipeline: Arc<ComputePipeline>, ) -> &mut Self384 pub unsafe fn bind_pipeline_compute_unchecked( 385 &mut self, 386 pipeline: Arc<ComputePipeline>, 387 ) -> &mut Self { 388 let fns = self.device().fns(); 389 (fns.v1_0.cmd_bind_pipeline)( 390 self.handle(), 391 ash::vk::PipelineBindPoint::COMPUTE, 392 pipeline.handle(), 393 ); 394 395 self.builder_state.pipeline_compute = Some(pipeline.clone()); 396 self.resources.push(Box::new(pipeline)); 397 398 self.next_command_index += 1; 399 self 400 } 401 402 /// Binds a graphics pipeline for future draw calls. 403 /// 404 /// # Panics 405 /// 406 /// - Panics if the queue family of the command buffer does not support graphics operations. 407 /// - Panics if `self` and `pipeline` do not belong to the same device. bind_pipeline_graphics(&mut self, pipeline: Arc<GraphicsPipeline>) -> &mut Self408 pub fn bind_pipeline_graphics(&mut self, pipeline: Arc<GraphicsPipeline>) -> &mut Self { 409 self.validate_bind_pipeline_graphics(&pipeline).unwrap(); 410 411 unsafe { self.bind_pipeline_graphics_unchecked(pipeline) } 412 } 413 validate_bind_pipeline_graphics( &self, pipeline: &GraphicsPipeline, ) -> Result<(), BindPushError>414 fn validate_bind_pipeline_graphics( 415 &self, 416 pipeline: &GraphicsPipeline, 417 ) -> Result<(), BindPushError> { 418 let queue_family_properties = self.queue_family_properties(); 419 420 // VUID-vkCmdBindPipeline-pipelineBindPoint-00778 421 if !queue_family_properties 422 .queue_flags 423 .intersects(QueueFlags::GRAPHICS) 424 { 425 return Err(BindPushError::NotSupportedByQueueFamily); 426 } 427 428 // VUID-vkCmdBindPipeline-commonparent 429 assert_eq!(self.device(), pipeline.device()); 430 431 if let Some(last_pipeline) = 432 self.builder_state 433 .render_pass 434 .as_ref() 435 .and_then(|render_pass_state| match &render_pass_state.render_pass { 436 RenderPassStateType::BeginRendering(state) if state.pipeline_used => { 437 self.builder_state.pipeline_graphics.as_ref() 438 } 439 _ => None, 440 }) 441 { 442 if let ( 443 PipelineRenderPassType::BeginRendering(pipeline_rendering_info), 444 PipelineRenderPassType::BeginRendering(last_pipeline_rendering_info), 445 ) = (pipeline.render_pass(), last_pipeline.render_pass()) 446 { 447 // VUID-vkCmdBindPipeline-pipeline-06195 448 // VUID-vkCmdBindPipeline-pipeline-06196 449 if pipeline_rendering_info.color_attachment_formats 450 != last_pipeline_rendering_info.color_attachment_formats 451 { 452 return Err(BindPushError::PreviousPipelineColorAttachmentFormatMismatch); 453 } 454 455 // VUID-vkCmdBindPipeline-pipeline-06197 456 if pipeline_rendering_info.depth_attachment_format 457 != last_pipeline_rendering_info.depth_attachment_format 458 { 459 return Err(BindPushError::PreviousPipelineDepthAttachmentFormatMismatch); 460 } 461 462 // VUID-vkCmdBindPipeline-pipeline-06194 463 if pipeline_rendering_info.stencil_attachment_format 464 != last_pipeline_rendering_info.stencil_attachment_format 465 { 466 return Err(BindPushError::PreviousPipelineStencilAttachmentFormatMismatch); 467 } 468 } 469 } 470 471 // VUID-vkCmdBindPipeline-pipeline-00781 472 // TODO: 473 474 Ok(()) 475 } 476 477 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] bind_pipeline_graphics_unchecked( &mut self, pipeline: Arc<GraphicsPipeline>, ) -> &mut Self478 pub unsafe fn bind_pipeline_graphics_unchecked( 479 &mut self, 480 pipeline: Arc<GraphicsPipeline>, 481 ) -> &mut Self { 482 let fns = self.device().fns(); 483 (fns.v1_0.cmd_bind_pipeline)( 484 self.handle(), 485 ash::vk::PipelineBindPoint::GRAPHICS, 486 pipeline.handle(), 487 ); 488 489 // Reset any states that are fixed in the new pipeline. The pipeline bind command will 490 // overwrite these states. 491 self.builder_state.reset_dynamic_states( 492 pipeline 493 .dynamic_states() 494 .filter(|(_, d)| !d) // not dynamic 495 .map(|(s, _)| s), 496 ); 497 self.builder_state.pipeline_graphics = Some(pipeline.clone()); 498 self.resources.push(Box::new(pipeline)); 499 500 self.next_command_index += 1; 501 self 502 } 503 504 /// Binds vertex buffers for future draw calls. 505 /// 506 /// # Panics 507 /// 508 /// - Panics if the queue family of the command buffer does not support graphics operations. 509 /// - Panics if the highest vertex buffer binding being bound is greater than the 510 /// [`max_vertex_input_bindings`] device property. 511 /// - Panics if `self` and any element of `vertex_buffers` do not belong to the same device. 512 /// - Panics if any element of `vertex_buffers` does not have the 513 /// [`BufferUsage::VERTEX_BUFFER`] usage enabled. 514 /// 515 /// [`max_vertex_input_bindings`]: crate::device::Properties::max_vertex_input_bindings 516 /// [`BufferUsage::VERTEX_BUFFER`]: crate::buffer::BufferUsage::VERTEX_BUFFER bind_vertex_buffers( &mut self, first_binding: u32, vertex_buffers: impl VertexBuffersCollection, ) -> &mut Self517 pub fn bind_vertex_buffers( 518 &mut self, 519 first_binding: u32, 520 vertex_buffers: impl VertexBuffersCollection, 521 ) -> &mut Self { 522 let vertex_buffers = vertex_buffers.into_vec(); 523 self.validate_bind_vertex_buffers(first_binding, &vertex_buffers) 524 .unwrap(); 525 526 unsafe { self.bind_vertex_buffers_unchecked(first_binding, vertex_buffers) } 527 } 528 validate_bind_vertex_buffers( &self, first_binding: u32, vertex_buffers: &[Subbuffer<[u8]>], ) -> Result<(), BindPushError>529 fn validate_bind_vertex_buffers( 530 &self, 531 first_binding: u32, 532 vertex_buffers: &[Subbuffer<[u8]>], 533 ) -> Result<(), BindPushError> { 534 let queue_family_properties = self.queue_family_properties(); 535 536 // VUID-vkCmdBindVertexBuffers-commandBuffer-cmdpool 537 if !queue_family_properties 538 .queue_flags 539 .intersects(QueueFlags::GRAPHICS) 540 { 541 return Err(BindPushError::NotSupportedByQueueFamily); 542 } 543 544 // VUID-vkCmdBindVertexBuffers-firstBinding-00624 545 // VUID-vkCmdBindVertexBuffers-firstBinding-00625 546 if first_binding + vertex_buffers.len() as u32 547 > self 548 .device() 549 .physical_device() 550 .properties() 551 .max_vertex_input_bindings 552 { 553 return Err(BindPushError::MaxVertexInputBindingsExceeded { 554 _binding_count: first_binding + vertex_buffers.len() as u32, 555 _max: self 556 .device() 557 .physical_device() 558 .properties() 559 .max_vertex_input_bindings, 560 }); 561 } 562 563 for buffer in vertex_buffers { 564 // VUID-vkCmdBindVertexBuffers-commonparent 565 assert_eq!(self.device(), buffer.device()); 566 567 // VUID-vkCmdBindVertexBuffers-pBuffers-00627 568 if !buffer 569 .buffer() 570 .usage() 571 .intersects(BufferUsage::VERTEX_BUFFER) 572 { 573 return Err(BindPushError::VertexBufferMissingUsage); 574 } 575 } 576 577 Ok(()) 578 } 579 580 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] bind_vertex_buffers_unchecked( &mut self, first_binding: u32, buffers: impl VertexBuffersCollection, ) -> &mut Self581 pub unsafe fn bind_vertex_buffers_unchecked( 582 &mut self, 583 first_binding: u32, 584 buffers: impl VertexBuffersCollection, 585 ) -> &mut Self { 586 let buffers = buffers.into_vec(); 587 588 if buffers.is_empty() { 589 return self; 590 } 591 592 let (buffers_vk, offsets_vk): (SmallVec<[_; 4]>, SmallVec<[_; 4]>) = buffers 593 .iter() 594 .map(|buffer| (buffer.buffer().handle(), buffer.offset())) 595 .unzip(); 596 597 let fns = self.device().fns(); 598 (fns.v1_0.cmd_bind_vertex_buffers)( 599 self.handle(), 600 first_binding, 601 buffers_vk.len() as u32, 602 buffers_vk.as_ptr(), 603 offsets_vk.as_ptr(), 604 ); 605 606 self.resources.reserve(buffers.len()); 607 608 for (i, buffer) in buffers.into_iter().enumerate() { 609 self.builder_state 610 .vertex_buffers 611 .insert(first_binding + i as u32, buffer.clone()); 612 self.resources.push(Box::new(buffer)); 613 } 614 615 self.next_command_index += 1; 616 self 617 } 618 619 /// Sets push constants for future dispatch or draw calls. 620 /// 621 /// # Panics 622 /// 623 /// - Panics if `offset` is not a multiple of 4. 624 /// - Panics if the size of `push_constants` is not a multiple of 4. 625 /// - Panics if any of the bytes in `push_constants` do not fall within any of the pipeline 626 /// layout's push constant ranges. push_constants( &mut self, pipeline_layout: Arc<PipelineLayout>, offset: u32, push_constants: &(impl BufferContents + ?Sized), ) -> &mut Self627 pub fn push_constants( 628 &mut self, 629 pipeline_layout: Arc<PipelineLayout>, 630 offset: u32, 631 push_constants: &(impl BufferContents + ?Sized), 632 ) -> &mut Self { 633 self.validate_push_constants(&pipeline_layout, offset, size_of_val(push_constants) as u32) 634 .unwrap(); 635 636 unsafe { self.push_constants_unchecked(pipeline_layout, offset, push_constants) } 637 } 638 validate_push_constants( &self, pipeline_layout: &PipelineLayout, offset: u32, data_size: u32, ) -> Result<(), BindPushError>639 fn validate_push_constants( 640 &self, 641 pipeline_layout: &PipelineLayout, 642 offset: u32, 643 data_size: u32, 644 ) -> Result<(), BindPushError> { 645 if offset % 4 != 0 { 646 return Err(BindPushError::PushConstantsOffsetNotAligned); 647 } 648 649 if data_size % 4 != 0 { 650 return Err(BindPushError::PushConstantsSizeNotAligned); 651 } 652 653 let mut current_offset = offset; 654 let mut remaining_size = data_size; 655 656 for range in pipeline_layout 657 .push_constant_ranges_disjoint() 658 .iter() 659 .skip_while(|range| range.offset + range.size <= offset) 660 { 661 // there is a gap between ranges, but the passed push_constants contains 662 // some bytes in this gap, exit the loop and report error 663 if range.offset > current_offset { 664 break; 665 } 666 667 // push the minimum of the whole remaining data, and the part until the end of this range 668 let push_size = remaining_size.min(range.offset + range.size - current_offset); 669 current_offset += push_size; 670 remaining_size -= push_size; 671 672 if remaining_size == 0 { 673 break; 674 } 675 } 676 677 if remaining_size != 0 { 678 return Err(BindPushError::PushConstantsDataOutOfRange { 679 offset: current_offset, 680 }); 681 } 682 683 Ok(()) 684 } 685 686 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] push_constants_unchecked( &mut self, pipeline_layout: Arc<PipelineLayout>, offset: u32, push_constants: &(impl BufferContents + ?Sized), ) -> &mut Self687 pub unsafe fn push_constants_unchecked( 688 &mut self, 689 pipeline_layout: Arc<PipelineLayout>, 690 offset: u32, 691 push_constants: &(impl BufferContents + ?Sized), 692 ) -> &mut Self { 693 let mut current_offset = offset; 694 let mut remaining_size = size_of_val(push_constants) as u32; 695 696 let fns = self.device().fns(); 697 698 for range in pipeline_layout 699 .push_constant_ranges_disjoint() 700 .iter() 701 .skip_while(|range| range.offset + range.size <= offset) 702 { 703 // there is a gap between ranges, but the passed push_constants contains 704 // some bytes in this gap, exit the loop and report error 705 if range.offset > current_offset { 706 break; 707 } 708 709 // push the minimum of the whole remaining data, and the part until the end of this range 710 let push_size = min(remaining_size, range.offset + range.size - current_offset); 711 let data_offset = (current_offset - offset) as usize; 712 713 (fns.v1_0.cmd_push_constants)( 714 self.handle(), 715 pipeline_layout.handle(), 716 range.stages.into(), 717 current_offset, 718 push_size, 719 (push_constants as *const _ as *const c_void).add(data_offset), 720 ); 721 722 current_offset += push_size; 723 remaining_size -= push_size; 724 725 if remaining_size == 0 { 726 break; 727 } 728 } 729 730 debug_assert!(remaining_size == 0); 731 732 // TODO: Push constant invalidations. 733 // The Vulkan spec currently is unclear about this, so Vulkano currently just marks 734 // push constants as set, and never unsets them. See: 735 // https://github.com/KhronosGroup/Vulkan-Docs/issues/1485 736 // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/2711 737 self.builder_state 738 .push_constants 739 .insert(offset..offset + size_of_val(push_constants) as u32); 740 self.builder_state.push_constants_pipeline_layout = Some(pipeline_layout.clone()); 741 self.resources.push(Box::new(pipeline_layout)); 742 743 self.next_command_index += 1; 744 self 745 } 746 747 /// Pushes descriptor data directly into the command buffer for future dispatch or draw calls. 748 /// 749 /// # Panics 750 /// 751 /// - Panics if the queue family of the command buffer does not support `pipeline_bind_point`. 752 /// - Panics if the [`khr_push_descriptor`] extension is not enabled on the device. 753 /// - Panics if `set_num` is not less than the number of sets in `pipeline_layout`. 754 /// - Panics if an element of `descriptor_writes` is not compatible with `pipeline_layout`. 755 /// 756 /// [`khr_push_descriptor`]: crate::device::DeviceExtensions::khr_push_descriptor push_descriptor_set( &mut self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: Arc<PipelineLayout>, set_num: u32, descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>, ) -> &mut Self757 pub fn push_descriptor_set( 758 &mut self, 759 pipeline_bind_point: PipelineBindPoint, 760 pipeline_layout: Arc<PipelineLayout>, 761 set_num: u32, 762 descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>, 763 ) -> &mut Self { 764 let descriptor_writes: SmallVec<[_; 8]> = descriptor_writes.into_iter().collect(); 765 self.validate_push_descriptor_set( 766 pipeline_bind_point, 767 &pipeline_layout, 768 set_num, 769 &descriptor_writes, 770 ) 771 .unwrap(); 772 773 unsafe { 774 self.push_descriptor_set_unchecked( 775 pipeline_bind_point, 776 pipeline_layout, 777 set_num, 778 descriptor_writes, 779 ) 780 } 781 } 782 validate_push_descriptor_set( &self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: &PipelineLayout, set_num: u32, descriptor_writes: &[WriteDescriptorSet], ) -> Result<(), BindPushError>783 fn validate_push_descriptor_set( 784 &self, 785 pipeline_bind_point: PipelineBindPoint, 786 pipeline_layout: &PipelineLayout, 787 set_num: u32, 788 descriptor_writes: &[WriteDescriptorSet], 789 ) -> Result<(), BindPushError> { 790 if !self.device().enabled_extensions().khr_push_descriptor { 791 return Err(BindPushError::RequirementNotMet { 792 required_for: "`CommandBufferBuilder::push_descriptor_set`", 793 requires_one_of: RequiresOneOf { 794 device_extensions: &["khr_push_descriptor"], 795 ..Default::default() 796 }, 797 }); 798 } 799 800 // VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-parameter 801 pipeline_bind_point.validate_device(self.device())?; 802 803 let queue_family_properties = self.queue_family_properties(); 804 805 // VUID-vkCmdPushDescriptorSetKHR-commandBuffer-cmdpool 806 // VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-00363 807 match pipeline_bind_point { 808 PipelineBindPoint::Compute => { 809 if !queue_family_properties 810 .queue_flags 811 .intersects(QueueFlags::COMPUTE) 812 { 813 return Err(BindPushError::NotSupportedByQueueFamily); 814 } 815 } 816 PipelineBindPoint::Graphics => { 817 if !queue_family_properties 818 .queue_flags 819 .intersects(QueueFlags::GRAPHICS) 820 { 821 return Err(BindPushError::NotSupportedByQueueFamily); 822 } 823 } 824 } 825 826 // VUID-vkCmdPushDescriptorSetKHR-commonparent 827 assert_eq!(self.device(), pipeline_layout.device()); 828 829 // VUID-vkCmdPushDescriptorSetKHR-set-00364 830 if set_num as usize > pipeline_layout.set_layouts().len() { 831 return Err(BindPushError::DescriptorSetOutOfRange { 832 set_num, 833 pipeline_layout_set_count: pipeline_layout.set_layouts().len() as u32, 834 }); 835 } 836 837 let descriptor_set_layout = &pipeline_layout.set_layouts()[set_num as usize]; 838 839 // VUID-vkCmdPushDescriptorSetKHR-set-00365 840 if !descriptor_set_layout.push_descriptor() { 841 return Err(BindPushError::DescriptorSetNotPush { set_num }); 842 } 843 844 for write in descriptor_writes { 845 check_descriptor_write(write, descriptor_set_layout, 0)?; 846 } 847 848 Ok(()) 849 } 850 851 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] push_descriptor_set_unchecked( &mut self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: Arc<PipelineLayout>, set_num: u32, descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>, ) -> &mut Self852 pub unsafe fn push_descriptor_set_unchecked( 853 &mut self, 854 pipeline_bind_point: PipelineBindPoint, 855 pipeline_layout: Arc<PipelineLayout>, 856 set_num: u32, 857 descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>, 858 ) -> &mut Self { 859 let descriptor_writes: SmallVec<[WriteDescriptorSet; 8]> = 860 descriptor_writes.into_iter().collect(); 861 862 debug_assert!(self.device().enabled_extensions().khr_push_descriptor); 863 864 let (infos, mut writes): (SmallVec<[_; 8]>, SmallVec<[_; 8]>) = descriptor_writes 865 .iter() 866 .map(|write| { 867 let binding = 868 &pipeline_layout.set_layouts()[set_num as usize].bindings()[&write.binding()]; 869 870 ( 871 write.to_vulkan_info(binding.descriptor_type), 872 write.to_vulkan(ash::vk::DescriptorSet::null(), binding.descriptor_type), 873 ) 874 }) 875 .unzip(); 876 877 if writes.is_empty() { 878 return self; 879 } 880 881 // Set the info pointers separately. 882 for (info, write) in infos.iter().zip(writes.iter_mut()) { 883 match info { 884 DescriptorWriteInfo::Image(info) => { 885 write.descriptor_count = info.len() as u32; 886 write.p_image_info = info.as_ptr(); 887 } 888 DescriptorWriteInfo::Buffer(info) => { 889 write.descriptor_count = info.len() as u32; 890 write.p_buffer_info = info.as_ptr(); 891 } 892 DescriptorWriteInfo::BufferView(info) => { 893 write.descriptor_count = info.len() as u32; 894 write.p_texel_buffer_view = info.as_ptr(); 895 } 896 } 897 898 debug_assert!(write.descriptor_count != 0); 899 } 900 901 let fns = self.device().fns(); 902 (fns.khr_push_descriptor.cmd_push_descriptor_set_khr)( 903 self.handle(), 904 pipeline_bind_point.into(), 905 pipeline_layout.handle(), 906 set_num, 907 writes.len() as u32, 908 writes.as_ptr(), 909 ); 910 911 let state = self.builder_state.invalidate_descriptor_sets( 912 pipeline_bind_point, 913 pipeline_layout.clone(), 914 set_num, 915 1, 916 ); 917 let descriptor_set_layout = state.pipeline_layout.set_layouts()[set_num as usize].as_ref(); 918 debug_assert!(descriptor_set_layout.push_descriptor()); 919 920 let set_resources = match state.descriptor_sets.entry(set_num).or_insert_with(|| { 921 SetOrPush::Push(DescriptorSetResources::new(descriptor_set_layout, 0)) 922 }) { 923 SetOrPush::Push(set_resources) => set_resources, 924 _ => unreachable!(), 925 }; 926 927 for write in &descriptor_writes { 928 set_resources.update(write); 929 } 930 931 self.resources.push(Box::new(pipeline_layout)); 932 933 self.next_command_index += 1; 934 self 935 } 936 } 937