Skip to main content

lgpu_hal/vulkan/
command.rs

1//! Vulkan command recording, render passes, copy operations, and queue submission.
2
3use 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        // Harvest any completed async command recorders back into cache
53        {
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                // Acquire locks in standard order: pending_async_recorders THEN command_recorder_cache
1657                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}