1use super::{
4 bindless, VulkanBuffer, VulkanComputePipeline, VulkanDevice, VulkanTexture,
5 VulkanTimelineSemaphore,
6};
7use crate::HalError;
8use ash::vk;
9
10pub struct VulkanCommandRecorder {
11 pub(crate) command_pool: vk::CommandPool,
12 pub(crate) command_buffer: vk::CommandBuffer,
13}
14
15unsafe impl Send for VulkanCommandRecorder {}
16
17#[derive(Clone, Copy)]
18pub enum DrawCommand<'a> {
19 Array {
20 vertex_count: u32,
21 instance_count: u32,
22 first_vertex: u32,
23 first_instance: u32,
24 },
25 Indexed {
26 index_buffer: &'a VulkanBuffer,
27 index_count: u32,
28 instance_count: u32,
29 first_index: u32,
30 vertex_offset: i32,
31 first_instance: u32,
32 index_type: vk::IndexType,
33 },
34 Indirect {
35 indirect_buffer: &'a VulkanBuffer,
36 offset: u64,
37 draw_count: u32,
38 stride: u32,
39 },
40 IndexedIndirect {
41 index_buffer: &'a VulkanBuffer,
42 indirect_buffer: &'a VulkanBuffer,
43 offset: u64,
44 draw_count: u32,
45 stride: u32,
46 index_type: vk::IndexType,
47 },
48}
49
50impl VulkanDevice {
51 pub fn create_command_recorder(&self) -> Result<VulkanCommandRecorder, HalError> {
52 {
54 let current_val = unsafe {
55 self.device
56 .get_semaphore_counter_value(self.internal_frame_semaphore)
57 .unwrap_or(0)
58 };
59 let mut pending = self.pending_async_recorders.lock().unwrap();
60 let mut cache = self.command_recorder_cache.lock().unwrap();
61 pending.retain_mut(|(val, recorders)| {
62 if *val <= current_val {
63 for r in recorders.drain(..) {
64 cache.push((r.command_pool, r.command_buffer));
65 }
66 false
67 } else {
68 true
69 }
70 });
71 }
72
73 let (command_pool, command_buffer) = match self.command_recorder_cache.lock().unwrap().pop()
74 {
75 Some((pool, cb)) => {
76 unsafe {
77 self.device
78 .reset_command_buffer(cb, vk::CommandBufferResetFlags::empty())
79 .ok();
80 }
81 (pool, cb)
82 }
83 None => {
84 let pool_info = vk::CommandPoolCreateInfo::default()
85 .queue_family_index(self.queue_family_index)
86 .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER);
87
88 let command_pool = unsafe {
89 self.device
90 .create_command_pool(&pool_info, None)
91 .map_err(|e| {
92 HalError::CommandError(format!("Failed to create command pool: {e}"))
93 })?
94 };
95
96 let alloc_info = vk::CommandBufferAllocateInfo::default()
97 .command_pool(command_pool)
98 .level(vk::CommandBufferLevel::PRIMARY)
99 .command_buffer_count(1);
100
101 let command_buffer =
102 match unsafe { self.device.allocate_command_buffers(&alloc_info) } {
103 Ok(bufs) if !bufs.is_empty() => bufs[0],
104 Ok(_) => {
105 unsafe {
106 self.device.destroy_command_pool(command_pool, None);
107 }
108 return Err(HalError::CommandError(
109 "Allocated 0 command buffers".to_string(),
110 ));
111 }
112 Err(e) => {
113 unsafe {
114 self.device.destroy_command_pool(command_pool, None);
115 }
116 return Err(HalError::CommandError(format!(
117 "Failed to allocate command buffer: {e}"
118 )));
119 }
120 };
121
122 (command_pool, command_buffer)
123 }
124 };
125
126 let begin_info = vk::CommandBufferBeginInfo::default()
127 .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT);
128
129 if let Err(e) = unsafe {
130 self.device
131 .begin_command_buffer(command_buffer, &begin_info)
132 } {
133 self.command_recorder_cache
134 .lock()
135 .unwrap()
136 .push((command_pool, command_buffer));
137 return Err(HalError::CommandError(e.to_string()));
138 }
139
140 Ok(VulkanCommandRecorder {
141 command_pool,
142 command_buffer,
143 })
144 }
145
146 pub fn recycle_command_recorder(&self, recorder: VulkanCommandRecorder) {
147 unsafe {
148 let _ = self.device.reset_command_buffer(
149 recorder.command_buffer,
150 vk::CommandBufferResetFlags::empty(),
151 );
152 }
153 self.command_recorder_cache
154 .lock()
155 .unwrap()
156 .push((recorder.command_pool, recorder.command_buffer));
157 }
158
159 pub fn bind_buffers_to_compute_pipeline(
160 &self,
161 pipeline: &VulkanComputePipeline,
162 buffers: &[&VulkanBuffer],
163 ) {
164 let mut buffer_infos = Vec::with_capacity(buffers.len());
165 for buffer in buffers {
166 buffer_infos.push([vk::DescriptorBufferInfo::default()
167 .buffer(buffer.buffer)
168 .offset(0)
169 .range(buffer.size)]);
170 }
171
172 let mut writes = Vec::with_capacity(buffers.len());
173 for (binding, info_arr) in buffer_infos.iter().enumerate() {
174 let write = vk::WriteDescriptorSet::default()
175 .dst_set(pipeline.descriptor_set)
176 .dst_binding(binding as u32)
177 .descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
178 .buffer_info(info_arr);
179 writes.push(write);
180 }
181
182 unsafe {
183 self.device.update_descriptor_sets(&writes, &[]);
184 }
185 }
186
187 pub fn dispatch(
188 &self,
189 recorder: &VulkanCommandRecorder,
190 pipeline: &VulkanComputePipeline,
191 x: u32,
192 y: u32,
193 z: u32,
194 ) {
195 unsafe {
196 self.device.cmd_bind_pipeline(
197 recorder.command_buffer,
198 vk::PipelineBindPoint::COMPUTE,
199 pipeline.pipeline,
200 );
201 self.device.cmd_bind_descriptor_sets(
202 recorder.command_buffer,
203 vk::PipelineBindPoint::COMPUTE,
204 pipeline.layout,
205 0,
206 &[pipeline.descriptor_set],
207 &[],
208 );
209 self.device.cmd_dispatch(recorder.command_buffer, x, y, z);
210 }
211 }
212
213 pub fn dispatch_bindless(
214 &self,
215 recorder: &VulkanCommandRecorder,
216 pipeline: vk::Pipeline,
217 push_constants: &[u8],
218 x: u32,
219 y: u32,
220 z: u32,
221 ) -> Result<(), HalError> {
222 let heap = self.bindless_heap.as_ref().ok_or_else(|| {
223 HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
224 })?;
225
226 if push_constants.len() > bindless::PUSH_CONSTANT_SIZE as usize {
227 return Err(HalError::CommandError(format!(
228 "Push constants size {} exceeds maximum allowed size of {}",
229 push_constants.len(),
230 bindless::PUSH_CONSTANT_SIZE
231 )));
232 }
233
234 unsafe {
235 self.device.cmd_bind_pipeline(
236 recorder.command_buffer,
237 vk::PipelineBindPoint::COMPUTE,
238 pipeline,
239 );
240 self.device.cmd_bind_descriptor_sets(
241 recorder.command_buffer,
242 vk::PipelineBindPoint::COMPUTE,
243 heap.pipeline_layout,
244 0,
245 &[heap.descriptor_set],
246 &[],
247 );
248 if !push_constants.is_empty() {
249 let mut aligned_pc = [0u8; bindless::PUSH_CONSTANT_SIZE as usize];
250 aligned_pc[..push_constants.len()].copy_from_slice(push_constants);
251 let aligned_len = ((push_constants.len() + 3) / 4) * 4;
252
253 self.device.cmd_push_constants(
254 recorder.command_buffer,
255 heap.pipeline_layout,
256 vk::ShaderStageFlags::ALL,
257 0,
258 &aligned_pc[..aligned_len],
259 );
260 }
261 self.device.cmd_dispatch(recorder.command_buffer, x, y, z);
262 }
263 Ok(())
264 }
265
266 fn cmd_clear_and_render_internal(
267 &self,
268 recorder: &VulkanCommandRecorder,
269 color_texture: &VulkanTexture,
270 depth: Option<(&VulkanTexture, f32)>,
271 clear_color: [f32; 4],
272 ) {
273 unsafe {
274 let color_subresource = vk::ImageSubresourceRange::default()
275 .aspect_mask(vk::ImageAspectFlags::COLOR)
276 .base_mip_level(0)
277 .level_count(1)
278 .base_array_layer(0)
279 .layer_count(1);
280
281 let old_color_layout =
282 color_texture.transition_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
283
284 let (src_stage, src_access) = match old_color_layout {
285 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL => (
286 vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
287 vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
288 ),
289 vk::ImageLayout::TRANSFER_SRC_OPTIMAL => (
290 vk::PipelineStageFlags::TRANSFER,
291 vk::AccessFlags::TRANSFER_READ,
292 ),
293 _ => (
294 vk::PipelineStageFlags::TOP_OF_PIPE,
295 vk::AccessFlags::empty(),
296 ),
297 };
298
299 let color_barrier_to_attach = vk::ImageMemoryBarrier::default()
300 .old_layout(old_color_layout)
301 .new_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
302 .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
303 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
304 .image(color_texture.image)
305 .subresource_range(color_subresource)
306 .src_access_mask(src_access)
307 .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE);
308
309 let mut barriers = vec![color_barrier_to_attach];
310 let mut wait_dst_stage = vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT;
311 let mut total_src_stage = src_stage;
312
313 let mut depth_attachment_val = None;
314 if let Some((depth_texture, clear_depth)) = depth {
315 let depth_subresource = vk::ImageSubresourceRange::default()
316 .aspect_mask(vk::ImageAspectFlags::DEPTH)
317 .base_mip_level(0)
318 .level_count(1)
319 .base_array_layer(0)
320 .layer_count(1);
321
322 let old_depth_layout =
323 depth_texture.transition_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL);
324 let (depth_src_stage, depth_src_access) = match old_depth_layout {
325 vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL => (
326 vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
327 | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS,
328 vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
329 ),
330 _ => (
331 vk::PipelineStageFlags::TOP_OF_PIPE,
332 vk::AccessFlags::empty(),
333 ),
334 };
335
336 let depth_barrier_to_attach = vk::ImageMemoryBarrier::default()
337 .old_layout(old_depth_layout)
338 .new_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL)
339 .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
340 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
341 .image(depth_texture.image)
342 .subresource_range(depth_subresource)
343 .src_access_mask(depth_src_access)
344 .dst_access_mask(
345 vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ
346 | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
347 );
348
349 barriers.push(depth_barrier_to_attach);
350 total_src_stage |= depth_src_stage;
351 wait_dst_stage |= vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS;
352
353 let depth_attachment = vk::RenderingAttachmentInfo::default()
354 .image_view(depth_texture.view)
355 .image_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL)
356 .load_op(vk::AttachmentLoadOp::CLEAR)
357 .store_op(vk::AttachmentStoreOp::STORE)
358 .clear_value(vk::ClearValue {
359 depth_stencil: vk::ClearDepthStencilValue {
360 depth: clear_depth,
361 stencil: 0,
362 },
363 });
364 depth_attachment_val = Some(depth_attachment);
365 }
366
367 self.device.cmd_pipeline_barrier(
368 recorder.command_buffer,
369 total_src_stage,
370 wait_dst_stage,
371 vk::DependencyFlags::empty(),
372 &[],
373 &[],
374 &barriers,
375 );
376
377 let clear_value = vk::ClearValue {
378 color: vk::ClearColorValue {
379 float32: clear_color,
380 },
381 };
382
383 let color_attachment = vk::RenderingAttachmentInfo::default()
384 .image_view(color_texture.view)
385 .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
386 .load_op(vk::AttachmentLoadOp::CLEAR)
387 .store_op(vk::AttachmentStoreOp::STORE)
388 .clear_value(clear_value);
389
390 let color_attachments = [color_attachment];
391 let render_area = vk::Rect2D {
392 offset: vk::Offset2D { x: 0, y: 0 },
393 extent: vk::Extent2D {
394 width: color_texture.width,
395 height: color_texture.height,
396 },
397 };
398
399 let mut rendering_info = vk::RenderingInfo::default()
400 .render_area(render_area)
401 .layer_count(1)
402 .color_attachments(&color_attachments);
403
404 if let Some(ref da) = depth_attachment_val {
405 rendering_info = rendering_info.depth_attachment(da);
406 }
407
408 self.device
409 .cmd_begin_rendering(recorder.command_buffer, &rendering_info);
410 self.device.cmd_end_rendering(recorder.command_buffer);
411
412 let barrier_to_src = vk::ImageMemoryBarrier::default()
413 .old_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
414 .new_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL)
415 .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
416 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
417 .image(color_texture.image)
418 .subresource_range(color_subresource)
419 .src_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE)
420 .dst_access_mask(vk::AccessFlags::TRANSFER_READ);
421
422 self.device.cmd_pipeline_barrier(
423 recorder.command_buffer,
424 vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
425 vk::PipelineStageFlags::TRANSFER,
426 vk::DependencyFlags::empty(),
427 &[],
428 &[],
429 &[barrier_to_src],
430 );
431
432 color_texture.transition_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL);
433 if let Some((depth_texture, _)) = depth {
434 depth_texture.transition_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL);
435 }
436 }
437 }
438
439 pub fn cmd_clear_and_render(
440 &self,
441 recorder: &VulkanCommandRecorder,
442 texture: &VulkanTexture,
443 clear_color: [f32; 4],
444 ) -> Result<(), HalError> {
445 self.cmd_clear_and_render_internal(recorder, texture, None, clear_color);
446 Ok(())
447 }
448
449 pub fn cmd_clear_and_render_with_depth(
450 &self,
451 recorder: &VulkanCommandRecorder,
452 color_texture: &VulkanTexture,
453 depth_texture: &VulkanTexture,
454 clear_color: [f32; 4],
455 clear_depth: f32,
456 ) -> Result<(), HalError> {
457 self.cmd_clear_and_render_internal(
458 recorder,
459 color_texture,
460 Some((depth_texture, clear_depth)),
461 clear_color,
462 );
463 Ok(())
464 }
465
466 fn cmd_draw_internal(
467 &self,
468 recorder: &VulkanCommandRecorder,
469 color_texture: &VulkanTexture,
470 depth: Option<(&VulkanTexture, crate::DepthLoadOp)>,
471 pipeline: vk::Pipeline,
472 draw_cmd: DrawCommand<'_>,
473 push_constants: &[u8],
474 color_load_op: crate::ColorLoadOp,
475 ) -> Result<(), HalError> {
476 let heap = self.bindless_heap.as_ref().ok_or_else(|| {
477 HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
478 })?;
479
480 if push_constants.len() > bindless::PUSH_CONSTANT_SIZE as usize {
481 return Err(HalError::CommandError(format!(
482 "Push constants size {} exceeds maximum allowed size of {}",
483 push_constants.len(),
484 bindless::PUSH_CONSTANT_SIZE
485 )));
486 }
487
488 unsafe {
489 let color_subresource = vk::ImageSubresourceRange::default()
490 .aspect_mask(vk::ImageAspectFlags::COLOR)
491 .base_mip_level(0)
492 .level_count(color_texture.mip_levels.max(1))
493 .base_array_layer(0)
494 .layer_count(1);
495
496 let old_color_layout =
497 color_texture.transition_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
498
499 let (src_stage, src_access) = match old_color_layout {
500 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL => (
501 vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
502 vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
503 ),
504 vk::ImageLayout::TRANSFER_SRC_OPTIMAL => (
505 vk::PipelineStageFlags::TRANSFER,
506 vk::AccessFlags::TRANSFER_READ,
507 ),
508 _ => (
509 vk::PipelineStageFlags::TOP_OF_PIPE,
510 vk::AccessFlags::empty(),
511 ),
512 };
513
514 let color_barrier_to_attach = vk::ImageMemoryBarrier::default()
515 .old_layout(old_color_layout)
516 .new_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
517 .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
518 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
519 .image(color_texture.image)
520 .subresource_range(color_subresource)
521 .src_access_mask(src_access)
522 .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE);
523
524 let mut barriers = vec![color_barrier_to_attach];
525 let mut total_src_stage = src_stage;
526 let mut wait_dst_stage = vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT;
527
528 let mut depth_attachment_val = None;
529 if let Some((depth_texture, depth_load_op)) = depth {
530 let depth_subresource = vk::ImageSubresourceRange::default()
531 .aspect_mask(vk::ImageAspectFlags::DEPTH)
532 .base_mip_level(0)
533 .level_count(depth_texture.mip_levels.max(1))
534 .base_array_layer(0)
535 .layer_count(1);
536
537 let old_depth_layout =
538 depth_texture.transition_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL);
539
540 let (depth_src_stage, depth_src_access) = match old_depth_layout {
541 vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL => (
542 vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
543 | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS,
544 vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
545 ),
546 _ => (
547 vk::PipelineStageFlags::TOP_OF_PIPE,
548 vk::AccessFlags::empty(),
549 ),
550 };
551
552 let depth_barrier_to_attach = vk::ImageMemoryBarrier::default()
553 .old_layout(old_depth_layout)
554 .new_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL)
555 .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
556 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
557 .image(depth_texture.image)
558 .subresource_range(depth_subresource)
559 .src_access_mask(depth_src_access)
560 .dst_access_mask(
561 vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ
562 | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
563 );
564
565 barriers.push(depth_barrier_to_attach);
566 total_src_stage |= depth_src_stage;
567 wait_dst_stage |= vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
568 | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS;
569
570 let (depth_load_op_vk, clear_depth_val) = match depth_load_op {
571 crate::LoadOp::Clear(d) => (
572 vk::AttachmentLoadOp::CLEAR,
573 vk::ClearValue {
574 depth_stencil: vk::ClearDepthStencilValue {
575 depth: d,
576 stencil: 0,
577 },
578 },
579 ),
580 crate::LoadOp::Load => (vk::AttachmentLoadOp::LOAD, vk::ClearValue::default()),
581 crate::LoadOp::DontCare => {
582 (vk::AttachmentLoadOp::DONT_CARE, vk::ClearValue::default())
583 }
584 };
585
586 let depth_attachment = vk::RenderingAttachmentInfo::default()
587 .image_view(depth_texture.view)
588 .image_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL)
589 .load_op(depth_load_op_vk)
590 .store_op(vk::AttachmentStoreOp::STORE)
591 .clear_value(clear_depth_val);
592 depth_attachment_val = Some(depth_attachment);
593 }
594
595 self.device.cmd_pipeline_barrier(
596 recorder.command_buffer,
597 total_src_stage,
598 wait_dst_stage,
599 vk::DependencyFlags::empty(),
600 &[],
601 &[],
602 &barriers,
603 );
604
605 let (color_load_op_vk, clear_color_value) = match color_load_op {
606 crate::LoadOp::Clear(c) => (
607 vk::AttachmentLoadOp::CLEAR,
608 vk::ClearValue {
609 color: vk::ClearColorValue { float32: c },
610 },
611 ),
612 crate::LoadOp::Load => (vk::AttachmentLoadOp::LOAD, vk::ClearValue::default()),
613 crate::LoadOp::DontCare => {
614 (vk::AttachmentLoadOp::DONT_CARE, vk::ClearValue::default())
615 }
616 };
617
618 let color_attachment = vk::RenderingAttachmentInfo::default()
619 .image_view(color_texture.view)
620 .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
621 .load_op(color_load_op_vk)
622 .store_op(vk::AttachmentStoreOp::STORE)
623 .clear_value(clear_color_value);
624
625 let color_attachments = [color_attachment];
626 let render_area = vk::Rect2D {
627 offset: vk::Offset2D { x: 0, y: 0 },
628 extent: vk::Extent2D {
629 width: color_texture.width,
630 height: color_texture.height,
631 },
632 };
633
634 let mut rendering_info = vk::RenderingInfo::default()
635 .render_area(render_area)
636 .layer_count(1)
637 .color_attachments(&color_attachments);
638
639 if let Some(ref da) = depth_attachment_val {
640 rendering_info = rendering_info.depth_attachment(da);
641 }
642
643 self.device
644 .cmd_begin_rendering(recorder.command_buffer, &rendering_info);
645
646 let viewport = vk::Viewport {
647 x: 0.0,
648 y: 0.0,
649 width: color_texture.width as f32,
650 height: color_texture.height as f32,
651 min_depth: 0.0,
652 max_depth: 1.0,
653 };
654 self.device
655 .cmd_set_viewport(recorder.command_buffer, 0, &[viewport]);
656
657 let scissor = render_area;
658 self.device
659 .cmd_set_scissor(recorder.command_buffer, 0, &[scissor]);
660
661 self.device.cmd_bind_pipeline(
662 recorder.command_buffer,
663 vk::PipelineBindPoint::GRAPHICS,
664 pipeline,
665 );
666 self.device.cmd_bind_descriptor_sets(
667 recorder.command_buffer,
668 vk::PipelineBindPoint::GRAPHICS,
669 heap.pipeline_layout,
670 0,
671 &[heap.descriptor_set],
672 &[],
673 );
674 if !push_constants.is_empty() {
675 let mut aligned_pc = [0u8; bindless::PUSH_CONSTANT_SIZE as usize];
676 aligned_pc[..push_constants.len()].copy_from_slice(push_constants);
677 let aligned_len = ((push_constants.len() + 3) / 4) * 4;
678
679 self.device.cmd_push_constants(
680 recorder.command_buffer,
681 heap.pipeline_layout,
682 vk::ShaderStageFlags::ALL,
683 0,
684 &aligned_pc[..aligned_len],
685 );
686 }
687
688 match draw_cmd {
689 DrawCommand::Array {
690 vertex_count,
691 instance_count,
692 first_vertex,
693 first_instance,
694 } => {
695 self.device.cmd_draw(
696 recorder.command_buffer,
697 vertex_count,
698 instance_count,
699 first_vertex,
700 first_instance,
701 );
702 }
703 DrawCommand::Indexed {
704 index_buffer,
705 index_count,
706 instance_count,
707 first_index,
708 vertex_offset,
709 first_instance,
710 index_type,
711 } => {
712 self.device.cmd_bind_index_buffer(
713 recorder.command_buffer,
714 index_buffer.buffer,
715 0,
716 index_type,
717 );
718 self.device.cmd_draw_indexed(
719 recorder.command_buffer,
720 index_count,
721 instance_count,
722 first_index,
723 vertex_offset,
724 first_instance,
725 );
726 }
727 DrawCommand::Indirect {
728 indirect_buffer,
729 offset,
730 draw_count,
731 stride,
732 } => {
733 self.device.cmd_draw_indirect(
734 recorder.command_buffer,
735 indirect_buffer.buffer,
736 offset,
737 draw_count,
738 stride,
739 );
740 }
741 DrawCommand::IndexedIndirect {
742 index_buffer,
743 indirect_buffer,
744 offset,
745 draw_count,
746 stride,
747 index_type,
748 } => {
749 self.device.cmd_bind_index_buffer(
750 recorder.command_buffer,
751 index_buffer.buffer,
752 0,
753 index_type,
754 );
755 self.device.cmd_draw_indexed_indirect(
756 recorder.command_buffer,
757 indirect_buffer.buffer,
758 offset,
759 draw_count,
760 stride,
761 );
762 }
763 }
764
765 self.device.cmd_end_rendering(recorder.command_buffer);
766
767 let barrier_to_src = vk::ImageMemoryBarrier::default()
768 .old_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
769 .new_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL)
770 .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
771 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
772 .image(color_texture.image)
773 .subresource_range(color_subresource)
774 .src_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE)
775 .dst_access_mask(vk::AccessFlags::TRANSFER_READ);
776
777 self.device.cmd_pipeline_barrier(
778 recorder.command_buffer,
779 vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
780 vk::PipelineStageFlags::TRANSFER,
781 vk::DependencyFlags::empty(),
782 &[],
783 &[],
784 &[barrier_to_src],
785 );
786
787 color_texture.transition_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL);
788 if let Some((depth_texture, _)) = depth {
789 depth_texture.transition_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL);
790 }
791 }
792 Ok(())
793 }
794
795 pub fn cmd_draw(
796 &self,
797 recorder: &VulkanCommandRecorder,
798 texture: &VulkanTexture,
799 pipeline: vk::Pipeline,
800 vertex_count: u32,
801 push_constants: &[u8],
802 clear_color: [f32; 4],
803 ) -> Result<(), HalError> {
804 self.cmd_draw_internal(
805 recorder,
806 texture,
807 None,
808 pipeline,
809 DrawCommand::Array {
810 vertex_count,
811 instance_count: 1,
812 first_vertex: 0,
813 first_instance: 0,
814 },
815 push_constants,
816 crate::LoadOp::Clear(clear_color),
817 )
818 }
819
820 pub fn cmd_draw_instanced(
821 &self,
822 recorder: &VulkanCommandRecorder,
823 texture: &VulkanTexture,
824 pipeline: vk::Pipeline,
825 vertex_count: u32,
826 instance_count: u32,
827 first_vertex: u32,
828 first_instance: u32,
829 push_constants: &[u8],
830 clear_color: [f32; 4],
831 ) -> Result<(), HalError> {
832 self.cmd_draw_internal(
833 recorder,
834 texture,
835 None,
836 pipeline,
837 DrawCommand::Array {
838 vertex_count,
839 instance_count,
840 first_vertex,
841 first_instance,
842 },
843 push_constants,
844 crate::LoadOp::Clear(clear_color),
845 )
846 }
847
848 pub fn cmd_draw_triangle(
849 &self,
850 recorder: &VulkanCommandRecorder,
851 texture: &VulkanTexture,
852 pipeline: vk::Pipeline,
853 clear_color: [f32; 4],
854 ) -> Result<(), HalError> {
855 self.cmd_draw(recorder, texture, pipeline, 3, &[], clear_color)
856 }
857
858 pub fn cmd_draw_with_depth(
859 &self,
860 recorder: &VulkanCommandRecorder,
861 color_texture: &VulkanTexture,
862 depth_texture: &VulkanTexture,
863 pipeline: vk::Pipeline,
864 vertex_count: u32,
865 push_constants: &[u8],
866 clear_color: [f32; 4],
867 clear_depth: f32,
868 ) -> Result<(), HalError> {
869 self.cmd_draw_internal(
870 recorder,
871 color_texture,
872 Some((depth_texture, crate::LoadOp::Clear(clear_depth))),
873 pipeline,
874 DrawCommand::Array {
875 vertex_count,
876 instance_count: 1,
877 first_vertex: 0,
878 first_instance: 0,
879 },
880 push_constants,
881 crate::LoadOp::Clear(clear_color),
882 )
883 }
884
885 pub fn cmd_draw_mrt(
886 &self,
887 recorder: &VulkanCommandRecorder,
888 color_textures: &[&VulkanTexture],
889 depth_texture: Option<&VulkanTexture>,
890 pipeline: vk::Pipeline,
891 vertex_count: u32,
892 instance_count: u32,
893 first_vertex: u32,
894 first_instance: u32,
895 push_constants: &[u8],
896 clear_color: [f32; 4],
897 clear_depth: Option<f32>,
898 ) -> Result<(), HalError> {
899 if color_textures.is_empty() {
900 return Err(HalError::InvalidParameter(
901 "color_textures cannot be empty for MRT".to_string(),
902 ));
903 }
904
905 let heap = self.bindless_heap.as_ref().ok_or_else(|| {
906 HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
907 })?;
908
909 if push_constants.len() > bindless::PUSH_CONSTANT_SIZE as usize {
910 return Err(HalError::CommandError(format!(
911 "Push constants size {} exceeds maximum allowed size of {}",
912 push_constants.len(),
913 bindless::PUSH_CONSTANT_SIZE
914 )));
915 }
916
917 unsafe {
918 let mut barriers = Vec::with_capacity(color_textures.len() + 1);
919 let mut total_src_stage = vk::PipelineStageFlags::empty();
920 let mut wait_dst_stage = vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT;
921 let mut color_subresources = Vec::with_capacity(color_textures.len());
922
923 for tex in color_textures {
924 let subresource = vk::ImageSubresourceRange::default()
925 .aspect_mask(vk::ImageAspectFlags::COLOR)
926 .base_mip_level(0)
927 .level_count(tex.mip_levels.max(1))
928 .base_array_layer(0)
929 .layer_count(1);
930 color_subresources.push(subresource);
931
932 let old_layout = tex.transition_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
933 let (src_stage, src_access) = match old_layout {
934 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL => (
935 vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
936 vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
937 ),
938 vk::ImageLayout::TRANSFER_SRC_OPTIMAL => (
939 vk::PipelineStageFlags::TRANSFER,
940 vk::AccessFlags::TRANSFER_READ,
941 ),
942 _ => (
943 vk::PipelineStageFlags::TOP_OF_PIPE,
944 vk::AccessFlags::empty(),
945 ),
946 };
947
948 let barrier = vk::ImageMemoryBarrier::default()
949 .old_layout(old_layout)
950 .new_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
951 .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
952 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
953 .image(tex.image)
954 .subresource_range(subresource)
955 .src_access_mask(src_access)
956 .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE);
957
958 barriers.push(barrier);
959 total_src_stage |= src_stage;
960 }
961
962 let mut depth_attachment_val = None;
963 if let (Some(depth_tex), Some(depth_val)) = (depth_texture, clear_depth) {
964 let depth_subresource = vk::ImageSubresourceRange::default()
965 .aspect_mask(vk::ImageAspectFlags::DEPTH)
966 .base_mip_level(0)
967 .level_count(1)
968 .base_array_layer(0)
969 .layer_count(1);
970
971 let old_depth_layout =
972 depth_tex.transition_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL);
973 let (depth_src_stage, depth_src_access) = match old_depth_layout {
974 vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL => (
975 vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
976 | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS,
977 vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
978 ),
979 _ => (
980 vk::PipelineStageFlags::TOP_OF_PIPE,
981 vk::AccessFlags::empty(),
982 ),
983 };
984
985 let depth_barrier = vk::ImageMemoryBarrier::default()
986 .old_layout(old_depth_layout)
987 .new_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL)
988 .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
989 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
990 .image(depth_tex.image)
991 .subresource_range(depth_subresource)
992 .src_access_mask(depth_src_access)
993 .dst_access_mask(
994 vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ
995 | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
996 );
997
998 barriers.push(depth_barrier);
999 total_src_stage |= depth_src_stage;
1000 wait_dst_stage |= vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
1001 | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS;
1002
1003 let depth_attachment = vk::RenderingAttachmentInfo::default()
1004 .image_view(depth_tex.view)
1005 .image_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL)
1006 .load_op(vk::AttachmentLoadOp::CLEAR)
1007 .store_op(vk::AttachmentStoreOp::STORE)
1008 .clear_value(vk::ClearValue {
1009 depth_stencil: vk::ClearDepthStencilValue {
1010 depth: depth_val,
1011 stencil: 0,
1012 },
1013 });
1014 depth_attachment_val = Some(depth_attachment);
1015 }
1016
1017 self.device.cmd_pipeline_barrier(
1018 recorder.command_buffer,
1019 total_src_stage,
1020 wait_dst_stage,
1021 vk::DependencyFlags::empty(),
1022 &[],
1023 &[],
1024 &barriers,
1025 );
1026
1027 let clear_value = vk::ClearValue {
1028 color: vk::ClearColorValue {
1029 float32: clear_color,
1030 },
1031 };
1032
1033 let attachments: Vec<vk::RenderingAttachmentInfo> = color_textures
1034 .iter()
1035 .map(|tex| {
1036 vk::RenderingAttachmentInfo::default()
1037 .image_view(tex.view)
1038 .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
1039 .load_op(vk::AttachmentLoadOp::CLEAR)
1040 .store_op(vk::AttachmentStoreOp::STORE)
1041 .clear_value(clear_value)
1042 })
1043 .collect();
1044
1045 let first_tex = color_textures[0];
1046 let render_area = vk::Rect2D {
1047 offset: vk::Offset2D { x: 0, y: 0 },
1048 extent: vk::Extent2D {
1049 width: first_tex.width,
1050 height: first_tex.height,
1051 },
1052 };
1053
1054 let mut rendering_info = vk::RenderingInfo::default()
1055 .render_area(render_area)
1056 .layer_count(1)
1057 .color_attachments(&attachments);
1058
1059 if let Some(ref da) = depth_attachment_val {
1060 rendering_info = rendering_info.depth_attachment(da);
1061 }
1062
1063 self.device
1064 .cmd_begin_rendering(recorder.command_buffer, &rendering_info);
1065
1066 let viewport = vk::Viewport {
1067 x: 0.0,
1068 y: 0.0,
1069 width: first_tex.width as f32,
1070 height: first_tex.height as f32,
1071 min_depth: 0.0,
1072 max_depth: 1.0,
1073 };
1074 self.device
1075 .cmd_set_viewport(recorder.command_buffer, 0, &[viewport]);
1076 self.device
1077 .cmd_set_scissor(recorder.command_buffer, 0, &[render_area]);
1078
1079 self.device.cmd_bind_pipeline(
1080 recorder.command_buffer,
1081 vk::PipelineBindPoint::GRAPHICS,
1082 pipeline,
1083 );
1084 self.device.cmd_bind_descriptor_sets(
1085 recorder.command_buffer,
1086 vk::PipelineBindPoint::GRAPHICS,
1087 heap.pipeline_layout,
1088 0,
1089 &[heap.descriptor_set],
1090 &[],
1091 );
1092
1093 if !push_constants.is_empty() {
1094 let mut aligned_pc = [0u8; bindless::PUSH_CONSTANT_SIZE as usize];
1095 aligned_pc[..push_constants.len()].copy_from_slice(push_constants);
1096 let aligned_len = ((push_constants.len() + 3) / 4) * 4;
1097
1098 self.device.cmd_push_constants(
1099 recorder.command_buffer,
1100 heap.pipeline_layout,
1101 vk::ShaderStageFlags::ALL,
1102 0,
1103 &aligned_pc[..aligned_len],
1104 );
1105 }
1106
1107 self.device.cmd_draw(
1108 recorder.command_buffer,
1109 vertex_count,
1110 instance_count,
1111 first_vertex,
1112 first_instance,
1113 );
1114
1115 self.device.cmd_end_rendering(recorder.command_buffer);
1116
1117 let post_barriers: Vec<vk::ImageMemoryBarrier> = color_textures
1118 .iter()
1119 .zip(color_subresources.iter())
1120 .map(|(tex, &subres)| {
1121 vk::ImageMemoryBarrier::default()
1122 .old_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
1123 .new_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL)
1124 .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
1125 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
1126 .image(tex.image)
1127 .subresource_range(subres)
1128 .src_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE)
1129 .dst_access_mask(vk::AccessFlags::TRANSFER_READ)
1130 })
1131 .collect();
1132
1133 self.device.cmd_pipeline_barrier(
1134 recorder.command_buffer,
1135 vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
1136 vk::PipelineStageFlags::TRANSFER,
1137 vk::DependencyFlags::empty(),
1138 &[],
1139 &[],
1140 &post_barriers,
1141 );
1142
1143 for tex in color_textures {
1144 tex.transition_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL);
1145 }
1146 }
1147 Ok(())
1148 }
1149
1150 pub fn cmd_draw_indexed(
1151 &self,
1152 recorder: &VulkanCommandRecorder,
1153 texture: &VulkanTexture,
1154 pipeline: vk::Pipeline,
1155 index_buffer: &VulkanBuffer,
1156 index_count: u32,
1157 instance_count: u32,
1158 first_index: u32,
1159 vertex_offset: i32,
1160 first_instance: u32,
1161 index_type: vk::IndexType,
1162 push_constants: &[u8],
1163 clear_color: [f32; 4],
1164 ) -> Result<(), HalError> {
1165 self.cmd_draw_internal(
1166 recorder,
1167 texture,
1168 None,
1169 pipeline,
1170 DrawCommand::Indexed {
1171 index_buffer,
1172 index_count,
1173 instance_count,
1174 first_index,
1175 vertex_offset,
1176 first_instance,
1177 index_type,
1178 },
1179 push_constants,
1180 crate::LoadOp::Clear(clear_color),
1181 )
1182 }
1183
1184 pub fn cmd_draw_indexed_with_depth(
1185 &self,
1186 recorder: &VulkanCommandRecorder,
1187 color_texture: &VulkanTexture,
1188 depth_texture: &VulkanTexture,
1189 pipeline: vk::Pipeline,
1190 index_buffer: &VulkanBuffer,
1191 index_count: u32,
1192 instance_count: u32,
1193 first_index: u32,
1194 vertex_offset: i32,
1195 first_instance: u32,
1196 index_type: vk::IndexType,
1197 push_constants: &[u8],
1198 clear_color: [f32; 4],
1199 clear_depth: f32,
1200 ) -> Result<(), HalError> {
1201 self.cmd_draw_internal(
1202 recorder,
1203 color_texture,
1204 Some((depth_texture, crate::LoadOp::Clear(clear_depth))),
1205 pipeline,
1206 DrawCommand::Indexed {
1207 index_buffer,
1208 index_count,
1209 instance_count,
1210 first_index,
1211 vertex_offset,
1212 first_instance,
1213 index_type,
1214 },
1215 push_constants,
1216 crate::LoadOp::Clear(clear_color),
1217 )
1218 }
1219
1220 pub fn cmd_draw_indirect(
1221 &self,
1222 recorder: &VulkanCommandRecorder,
1223 texture: &VulkanTexture,
1224 pipeline: vk::Pipeline,
1225 indirect_buffer: &VulkanBuffer,
1226 offset: u64,
1227 draw_count: u32,
1228 stride: u32,
1229 push_constants: &[u8],
1230 clear_color: [f32; 4],
1231 ) -> Result<(), HalError> {
1232 self.cmd_draw_internal(
1233 recorder,
1234 texture,
1235 None,
1236 pipeline,
1237 DrawCommand::Indirect {
1238 indirect_buffer,
1239 offset,
1240 draw_count,
1241 stride,
1242 },
1243 push_constants,
1244 crate::LoadOp::Clear(clear_color),
1245 )
1246 }
1247
1248 pub fn cmd_draw_indirect_with_depth(
1249 &self,
1250 recorder: &VulkanCommandRecorder,
1251 color_texture: &VulkanTexture,
1252 depth_texture: &VulkanTexture,
1253 pipeline: vk::Pipeline,
1254 indirect_buffer: &VulkanBuffer,
1255 offset: u64,
1256 draw_count: u32,
1257 stride: u32,
1258 push_constants: &[u8],
1259 clear_color: [f32; 4],
1260 clear_depth: f32,
1261 ) -> Result<(), HalError> {
1262 self.cmd_draw_internal(
1263 recorder,
1264 color_texture,
1265 Some((depth_texture, crate::LoadOp::Clear(clear_depth))),
1266 pipeline,
1267 DrawCommand::Indirect {
1268 indirect_buffer,
1269 offset,
1270 draw_count,
1271 stride,
1272 },
1273 push_constants,
1274 crate::LoadOp::Clear(clear_color),
1275 )
1276 }
1277
1278 pub fn cmd_draw_with_ops(
1279 &self,
1280 recorder: &VulkanCommandRecorder,
1281 color_texture: &VulkanTexture,
1282 depth: Option<(&VulkanTexture, crate::DepthLoadOp)>,
1283 pipeline: vk::Pipeline,
1284 draw_cmd: DrawCommand<'_>,
1285 push_constants: &[u8],
1286 color_load_op: crate::ColorLoadOp,
1287 ) -> Result<(), HalError> {
1288 self.cmd_draw_internal(
1289 recorder,
1290 color_texture,
1291 depth,
1292 pipeline,
1293 draw_cmd,
1294 push_constants,
1295 color_load_op,
1296 )
1297 }
1298
1299 pub fn cmd_dispatch_indirect(
1300 &self,
1301 recorder: &VulkanCommandRecorder,
1302 pipeline: vk::Pipeline,
1303 indirect_buffer: &VulkanBuffer,
1304 offset: u64,
1305 push_constants: &[u8],
1306 ) -> Result<(), HalError> {
1307 let heap = self.bindless_heap.as_ref().ok_or_else(|| {
1308 HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
1309 })?;
1310
1311 if push_constants.len() > bindless::PUSH_CONSTANT_SIZE as usize {
1312 return Err(HalError::CommandError(format!(
1313 "Push constants size {} exceeds maximum allowed size of {}",
1314 push_constants.len(),
1315 bindless::PUSH_CONSTANT_SIZE
1316 )));
1317 }
1318
1319 unsafe {
1320 self.device.cmd_bind_pipeline(
1321 recorder.command_buffer,
1322 vk::PipelineBindPoint::COMPUTE,
1323 pipeline,
1324 );
1325 self.device.cmd_bind_descriptor_sets(
1326 recorder.command_buffer,
1327 vk::PipelineBindPoint::COMPUTE,
1328 heap.pipeline_layout,
1329 0,
1330 &[heap.descriptor_set],
1331 &[],
1332 );
1333 if !push_constants.is_empty() {
1334 let mut aligned_pc = [0u8; bindless::PUSH_CONSTANT_SIZE as usize];
1335 aligned_pc[..push_constants.len()].copy_from_slice(push_constants);
1336 let aligned_len = ((push_constants.len() + 3) / 4) * 4;
1337
1338 self.device.cmd_push_constants(
1339 recorder.command_buffer,
1340 heap.pipeline_layout,
1341 vk::ShaderStageFlags::ALL,
1342 0,
1343 &aligned_pc[..aligned_len],
1344 );
1345 }
1346 self.device.cmd_dispatch_indirect(
1347 recorder.command_buffer,
1348 indirect_buffer.buffer,
1349 offset,
1350 );
1351 }
1352 Ok(())
1353 }
1354
1355 pub fn cmd_copy_buffer_to_buffer(
1356 &self,
1357 recorder: &VulkanCommandRecorder,
1358 src: &VulkanBuffer,
1359 dst: &VulkanBuffer,
1360 size: u64,
1361 src_offset: u64,
1362 dst_offset: u64,
1363 ) {
1364 let region = vk::BufferCopy::default()
1365 .src_offset(src_offset)
1366 .dst_offset(dst_offset)
1367 .size(size);
1368 unsafe {
1369 self.device
1370 .cmd_copy_buffer(recorder.command_buffer, src.buffer, dst.buffer, &[region]);
1371 }
1372 }
1373
1374 pub fn cmd_copy_buffer_to_image(
1375 &self,
1376 recorder: &VulkanCommandRecorder,
1377 buffer: &VulkanBuffer,
1378 texture: &VulkanTexture,
1379 ) {
1380 unsafe {
1381 let mut lock = texture.layout.lock().unwrap();
1382 let old_layout = *lock;
1383
1384 if old_layout != vk::ImageLayout::TRANSFER_DST_OPTIMAL {
1385 let subresource_range = vk::ImageSubresourceRange::default()
1386 .aspect_mask(vk::ImageAspectFlags::COLOR)
1387 .base_mip_level(0)
1388 .level_count(texture.mip_levels.max(1))
1389 .base_array_layer(0)
1390 .layer_count(1);
1391
1392 let (src_stage, src_access) = match old_layout {
1393 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL => (
1394 vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
1395 vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
1396 ),
1397 vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL => (
1398 vk::PipelineStageFlags::FRAGMENT_SHADER
1399 | vk::PipelineStageFlags::COMPUTE_SHADER,
1400 vk::AccessFlags::SHADER_READ,
1401 ),
1402 _ => (
1403 vk::PipelineStageFlags::TOP_OF_PIPE,
1404 vk::AccessFlags::empty(),
1405 ),
1406 };
1407
1408 let barrier_to_dst = vk::ImageMemoryBarrier::default()
1409 .old_layout(old_layout)
1410 .new_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
1411 .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
1412 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
1413 .image(texture.image)
1414 .subresource_range(subresource_range)
1415 .src_access_mask(src_access)
1416 .dst_access_mask(vk::AccessFlags::TRANSFER_WRITE);
1417
1418 self.device.cmd_pipeline_barrier(
1419 recorder.command_buffer,
1420 src_stage,
1421 vk::PipelineStageFlags::TRANSFER,
1422 vk::DependencyFlags::empty(),
1423 &[],
1424 &[],
1425 &[barrier_to_dst],
1426 );
1427
1428 *lock = vk::ImageLayout::TRANSFER_DST_OPTIMAL;
1429 }
1430
1431 let subresource = vk::ImageSubresourceLayers::default()
1432 .aspect_mask(vk::ImageAspectFlags::COLOR)
1433 .mip_level(0)
1434 .base_array_layer(0)
1435 .layer_count(1);
1436
1437 let copy_region = vk::BufferImageCopy::default()
1438 .buffer_offset(0)
1439 .buffer_row_length(texture.width)
1440 .buffer_image_height(texture.height)
1441 .image_subresource(subresource)
1442 .image_offset(vk::Offset3D { x: 0, y: 0, z: 0 })
1443 .image_extent(vk::Extent3D {
1444 width: texture.width,
1445 height: texture.height,
1446 depth: 1,
1447 });
1448
1449 self.device.cmd_copy_buffer_to_image(
1450 recorder.command_buffer,
1451 buffer.buffer,
1452 texture.image,
1453 vk::ImageLayout::TRANSFER_DST_OPTIMAL,
1454 &[copy_region],
1455 );
1456 }
1457 }
1458
1459 pub fn write_texture(&self, texture: &VulkanTexture, data: &[u8]) -> Result<(), HalError> {
1460 let staging_buf = self.create_buffer(&crate::BufferDesc {
1461 label: Some("staging_texture_upload"),
1462 size: data.len() as u64,
1463 usage: crate::BufferUsage::TRANSFER_SRC,
1464 memory_location: crate::MemoryLocation::CpuToGpu,
1465 })?;
1466
1467 self.write_buffer(&staging_buf, 0, data)?;
1468
1469 let recorder = self.create_command_recorder()?;
1470 self.cmd_copy_buffer_to_image(&recorder, &staging_buf, texture);
1471
1472 unsafe {
1473 let subresource_range = vk::ImageSubresourceRange::default()
1474 .aspect_mask(vk::ImageAspectFlags::COLOR)
1475 .base_mip_level(0)
1476 .level_count(texture.mip_levels.max(1))
1477 .base_array_layer(0)
1478 .layer_count(1);
1479
1480 let barrier_to_read = vk::ImageMemoryBarrier::default()
1481 .old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
1482 .new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
1483 .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
1484 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
1485 .image(texture.image)
1486 .subresource_range(subresource_range)
1487 .src_access_mask(vk::AccessFlags::TRANSFER_WRITE)
1488 .dst_access_mask(vk::AccessFlags::SHADER_READ);
1489
1490 self.device.cmd_pipeline_barrier(
1491 recorder.command_buffer,
1492 vk::PipelineStageFlags::TRANSFER,
1493 vk::PipelineStageFlags::FRAGMENT_SHADER | vk::PipelineStageFlags::COMPUTE_SHADER,
1494 vk::DependencyFlags::empty(),
1495 &[],
1496 &[],
1497 &[barrier_to_read],
1498 );
1499 *texture.layout.lock().unwrap() = vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL;
1500 }
1501
1502 self.submit_and_wait(recorder)?;
1503 self.wait_idle()?;
1504 self.destroy_buffer(staging_buf);
1505
1506 Ok(())
1507 }
1508
1509 pub fn cmd_copy_image_to_buffer(
1510 &self,
1511 recorder: &VulkanCommandRecorder,
1512 texture: &VulkanTexture,
1513 buffer: &VulkanBuffer,
1514 ) {
1515 unsafe {
1516 let mut lock = texture.layout.lock().unwrap();
1517 let old_layout = *lock;
1518
1519 if old_layout != vk::ImageLayout::TRANSFER_SRC_OPTIMAL {
1520 let subresource_range = vk::ImageSubresourceRange::default()
1521 .aspect_mask(vk::ImageAspectFlags::COLOR)
1522 .base_mip_level(0)
1523 .level_count(1)
1524 .base_array_layer(0)
1525 .layer_count(1);
1526
1527 let (src_stage, src_access) = match old_layout {
1528 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL => (
1529 vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
1530 vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
1531 ),
1532 _ => (
1533 vk::PipelineStageFlags::TOP_OF_PIPE,
1534 vk::AccessFlags::empty(),
1535 ),
1536 };
1537
1538 let barrier_to_src = vk::ImageMemoryBarrier::default()
1539 .old_layout(old_layout)
1540 .new_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL)
1541 .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
1542 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
1543 .image(texture.image)
1544 .subresource_range(subresource_range)
1545 .src_access_mask(src_access)
1546 .dst_access_mask(vk::AccessFlags::TRANSFER_READ);
1547
1548 self.device.cmd_pipeline_barrier(
1549 recorder.command_buffer,
1550 src_stage,
1551 vk::PipelineStageFlags::TRANSFER,
1552 vk::DependencyFlags::empty(),
1553 &[],
1554 &[],
1555 &[barrier_to_src],
1556 );
1557
1558 *lock = vk::ImageLayout::TRANSFER_SRC_OPTIMAL;
1559 }
1560
1561 let subresource = vk::ImageSubresourceLayers::default()
1562 .aspect_mask(vk::ImageAspectFlags::COLOR)
1563 .mip_level(0)
1564 .base_array_layer(0)
1565 .layer_count(1);
1566
1567 let copy_region = vk::BufferImageCopy::default()
1568 .buffer_offset(0)
1569 .buffer_row_length(texture.width)
1570 .buffer_image_height(texture.height)
1571 .image_subresource(subresource)
1572 .image_offset(vk::Offset3D { x: 0, y: 0, z: 0 })
1573 .image_extent(vk::Extent3D {
1574 width: texture.width,
1575 height: texture.height,
1576 depth: 1,
1577 });
1578
1579 self.device.cmd_copy_image_to_buffer(
1580 recorder.command_buffer,
1581 texture.image,
1582 vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
1583 buffer.buffer,
1584 &[copy_region],
1585 );
1586 }
1587 }
1588
1589 fn submit_batches_internal(
1590 &self,
1591 recorders: Vec<VulkanCommandRecorder>,
1592 wait_semaphores: &[(&VulkanTimelineSemaphore, u64)],
1593 signal_semaphores: &[(&VulkanTimelineSemaphore, u64)],
1594 sync_fence: Option<vk::Fence>,
1595 ) -> Result<(), HalError> {
1596 if recorders.is_empty() {
1597 return Ok(());
1598 }
1599
1600 unsafe {
1601 let mut vk_cmd_buffers = Vec::with_capacity(recorders.len());
1602 for r in &recorders {
1603 self.device
1604 .end_command_buffer(r.command_buffer)
1605 .map_err(|e| HalError::CommandError(e.to_string()))?;
1606 vk_cmd_buffers.push(r.command_buffer);
1607 }
1608
1609 let mut vk_wait_sems = Vec::with_capacity(wait_semaphores.len());
1610 let mut vk_wait_values = Vec::with_capacity(wait_semaphores.len());
1611 let mut vk_wait_stages = Vec::with_capacity(wait_semaphores.len());
1612 for (sem, val) in wait_semaphores {
1613 vk_wait_sems.push(sem.semaphore);
1614 vk_wait_values.push(*val);
1615 vk_wait_stages.push(vk::PipelineStageFlags::ALL_COMMANDS);
1616 }
1617
1618 let next_val = self
1619 .internal_frame_counter
1620 .fetch_add(1, std::sync::atomic::Ordering::AcqRel)
1621 + 1;
1622 let mut vk_signal_sems = Vec::with_capacity(signal_semaphores.len() + 1);
1623 let mut vk_signal_values = Vec::with_capacity(signal_semaphores.len() + 1);
1624 for (sem, val) in signal_semaphores {
1625 vk_signal_sems.push(sem.semaphore);
1626 vk_signal_values.push(*val);
1627 }
1628 vk_signal_sems.push(self.internal_frame_semaphore);
1629 vk_signal_values.push(next_val);
1630
1631 let mut timeline_info = vk::TimelineSemaphoreSubmitInfo::default()
1632 .wait_semaphore_values(&vk_wait_values)
1633 .signal_semaphore_values(&vk_signal_values);
1634
1635 let submit_info = vk::SubmitInfo::default()
1636 .command_buffers(&vk_cmd_buffers)
1637 .wait_semaphores(&vk_wait_sems)
1638 .wait_dst_stage_mask(&vk_wait_stages)
1639 .signal_semaphores(&vk_signal_sems)
1640 .push_next(&mut timeline_info);
1641
1642 let fence = sync_fence.unwrap_or(vk::Fence::null());
1643 {
1644 let queue = self.queue.lock().unwrap();
1645 if let Err(e) = self.device.queue_submit(*queue, &[submit_info], fence) {
1646 return Err(HalError::CommandError(e.to_string()));
1647 }
1648 }
1649
1650 if let Some(f) = sync_fence {
1651 let wait_res = self.device.wait_for_fences(&[f], true, u64::MAX);
1652 self.device.reset_fences(&[f]).ok();
1653 self.fence_cache.lock().unwrap().push(f);
1654 wait_res.map_err(|e| HalError::CommandError(e.to_string()))?;
1655
1656 let current_val = self
1658 .device
1659 .get_semaphore_counter_value(self.internal_frame_semaphore)
1660 .unwrap_or(0);
1661 let mut pending = self.pending_async_recorders.lock().unwrap();
1662 let mut cache = self.command_recorder_cache.lock().unwrap();
1663 for r in recorders {
1664 cache.push((r.command_pool, r.command_buffer));
1665 }
1666
1667 pending.retain_mut(|(val, async_recorders)| {
1668 if *val <= current_val {
1669 for r in async_recorders.drain(..) {
1670 cache.push((r.command_pool, r.command_buffer));
1671 }
1672 false
1673 } else {
1674 true
1675 }
1676 });
1677 } else {
1678 self.pending_async_recorders
1679 .lock()
1680 .unwrap()
1681 .push((next_val, recorders));
1682 }
1683 }
1684 Ok(())
1685 }
1686
1687 pub fn submit_and_wait(&self, recorder: VulkanCommandRecorder) -> Result<(), HalError> {
1688 let fence = self.fence_cache.lock().unwrap().pop();
1689 let fence = match fence {
1690 Some(f) => f,
1691 None => {
1692 let fence_info = vk::FenceCreateInfo::default();
1693 unsafe {
1694 self.device.create_fence(&fence_info, None).map_err(|e| {
1695 HalError::CommandError(format!("Failed to create fence: {e}"))
1696 })?
1697 }
1698 }
1699 };
1700 let res = self.submit_batches_internal(vec![recorder], &[], &[], Some(fence));
1701 if res.is_err() {
1702 unsafe {
1703 self.device.reset_fences(&[fence]).ok();
1704 self.fence_cache.lock().unwrap().push(fence);
1705 }
1706 }
1707 res
1708 }
1709
1710 pub fn submit_batches_and_wait(
1711 &self,
1712 recorders: Vec<VulkanCommandRecorder>,
1713 ) -> Result<(), HalError> {
1714 let fence = self.fence_cache.lock().unwrap().pop();
1715 let fence = match fence {
1716 Some(f) => f,
1717 None => {
1718 let fence_info = vk::FenceCreateInfo::default();
1719 unsafe {
1720 self.device.create_fence(&fence_info, None).map_err(|e| {
1721 HalError::CommandError(format!("Failed to create fence: {e}"))
1722 })?
1723 }
1724 }
1725 };
1726 let res = self.submit_batches_internal(recorders, &[], &[], Some(fence));
1727 if res.is_err() {
1728 unsafe {
1729 self.device.reset_fences(&[fence]).ok();
1730 self.fence_cache.lock().unwrap().push(fence);
1731 }
1732 }
1733 res
1734 }
1735
1736 pub fn submit_batches_async(
1737 &self,
1738 recorders: Vec<VulkanCommandRecorder>,
1739 wait_semaphores: &[(&VulkanTimelineSemaphore, u64)],
1740 signal_semaphores: &[(&VulkanTimelineSemaphore, u64)],
1741 ) -> Result<(), HalError> {
1742 self.submit_batches_internal(recorders, wait_semaphores, signal_semaphores, None)
1743 }
1744
1745 pub fn cmd_memory_barrier(&self, recorder: &VulkanCommandRecorder) {
1746 let barrier = vk::MemoryBarrier::default()
1747 .src_access_mask(vk::AccessFlags::MEMORY_WRITE | vk::AccessFlags::SHADER_WRITE)
1748 .dst_access_mask(vk::AccessFlags::MEMORY_READ | vk::AccessFlags::SHADER_READ);
1749 unsafe {
1750 self.device.cmd_pipeline_barrier(
1751 recorder.command_buffer,
1752 vk::PipelineStageFlags::COMPUTE_SHADER
1753 | vk::PipelineStageFlags::TRANSFER
1754 | vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
1755 vk::PipelineStageFlags::COMPUTE_SHADER
1756 | vk::PipelineStageFlags::TRANSFER
1757 | vk::PipelineStageFlags::VERTEX_INPUT
1758 | vk::PipelineStageFlags::DRAW_INDIRECT,
1759 vk::DependencyFlags::empty(),
1760 &[barrier],
1761 &[],
1762 &[],
1763 );
1764 }
1765 }
1766
1767 pub fn cmd_buffer_barrier(&self, recorder: &VulkanCommandRecorder, buffer: &VulkanBuffer) {
1768 let barrier = vk::BufferMemoryBarrier::default()
1769 .buffer(buffer.buffer)
1770 .offset(0)
1771 .size(buffer.size)
1772 .src_access_mask(vk::AccessFlags::SHADER_WRITE | vk::AccessFlags::TRANSFER_WRITE)
1773 .dst_access_mask(
1774 vk::AccessFlags::SHADER_READ
1775 | vk::AccessFlags::TRANSFER_READ
1776 | vk::AccessFlags::HOST_READ,
1777 )
1778 .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
1779 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED);
1780 unsafe {
1781 self.device.cmd_pipeline_barrier(
1782 recorder.command_buffer,
1783 vk::PipelineStageFlags::COMPUTE_SHADER
1784 | vk::PipelineStageFlags::TRANSFER
1785 | vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
1786 vk::PipelineStageFlags::COMPUTE_SHADER
1787 | vk::PipelineStageFlags::TRANSFER
1788 | vk::PipelineStageFlags::VERTEX_INPUT
1789 | vk::PipelineStageFlags::HOST,
1790 vk::DependencyFlags::empty(),
1791 &[],
1792 &[barrier],
1793 &[],
1794 );
1795 }
1796 }
1797}