Skip to main content

blade_graphics/vulkan/
mod.rs

1use ash::{
2    khr,
3    vk::{self},
4};
5use openxr as xr;
6use std::{mem, num::NonZeroU32, path::PathBuf, ptr, sync::Mutex};
7
8mod command;
9mod descriptor;
10mod init;
11mod pipeline;
12mod resource;
13mod surface;
14
15const QUERY_POOL_SIZE: usize = crate::limits::PASS_COUNT + 1;
16const MAX_XR_EYES: usize = 2;
17
18struct Instance {
19    core: ash::Instance,
20    _debug_utils: ash::ext::debug_utils::Instance,
21    get_physical_device_properties2: khr::get_physical_device_properties2::Instance,
22    cooperative_matrix: khr::cooperative_matrix::Instance,
23    get_surface_capabilities2: Option<khr::get_surface_capabilities2::Instance>,
24    surface: Option<khr::surface::Instance>,
25}
26
27#[derive(Clone)]
28struct RayTracingDevice {
29    acceleration_structure: khr::acceleration_structure::Device,
30    scratch_buffer_alignment: u64,
31}
32
33#[derive(Clone, Default)]
34struct CommandScopeDevice {}
35#[derive(Clone, Default)]
36struct TimingDevice {
37    period: f32,
38}
39
40#[derive(Clone)]
41struct Workarounds {
42    extra_sync_src_access: vk::AccessFlags,
43    extra_sync_dst_access: vk::AccessFlags,
44    extra_descriptor_pool_create_flags: vk::DescriptorPoolCreateFlags,
45}
46
47#[derive(Clone)]
48struct Device {
49    core: ash::Device,
50    device_information: crate::DeviceInformation,
51    swapchain: Option<khr::swapchain::Device>,
52    debug_utils: ash::ext::debug_utils::Device,
53    timeline_semaphore: khr::timeline_semaphore::Device,
54    dynamic_rendering: khr::dynamic_rendering::Device,
55    ray_tracing: Option<RayTracingDevice>,
56    buffer_device_address: bool,
57    inline_uniform_blocks: bool,
58    buffer_marker: Option<ash::amd::buffer_marker::Device>,
59    shader_info: Option<ash::amd::shader_info::Device>,
60    full_screen_exclusive: Option<ash::ext::full_screen_exclusive::Device>,
61    #[cfg(target_os = "windows")]
62    external_memory: Option<ash::khr::external_memory_win32::Device>,
63    #[cfg(not(target_os = "windows"))]
64    external_memory: Option<ash::khr::external_memory_fd::Device>,
65    command_scope: Option<CommandScopeDevice>,
66    timing: Option<TimingDevice>,
67    workarounds: Workarounds,
68}
69
70struct MemoryManager {
71    allocator: gpu_alloc::GpuAllocator<vk::DeviceMemory>,
72    slab: slab::Slab<gpu_alloc::MemoryBlock<vk::DeviceMemory>>,
73    valid_ash_memory_types: u32,
74}
75
76struct Queue {
77    raw: vk::Queue,
78    timeline_semaphore: vk::Semaphore,
79    last_progress: u64,
80}
81
82#[derive(Clone, Copy, Debug, Default, PartialEq)]
83struct InternalFrame {
84    acquire_semaphore: vk::Semaphore,
85    present_semaphore: vk::Semaphore,
86    image: vk::Image,
87    view: vk::ImageView,
88    xr_views: [vk::ImageView; MAX_XR_EYES],
89}
90
91#[derive(Clone, Copy, Debug, PartialEq)]
92struct Swapchain {
93    raw: vk::SwapchainKHR,
94    format: crate::TextureFormat,
95    alpha: crate::AlphaMode,
96    target_size: [u16; 2],
97}
98
99pub struct Surface {
100    device: khr::swapchain::Device,
101    raw: vk::SurfaceKHR,
102    frames: Vec<InternalFrame>,
103    next_semaphore: vk::Semaphore,
104    swapchain: Swapchain,
105    full_screen_exclusive: bool,
106}
107
108pub struct XrSurface {
109    raw: openxr::Swapchain<openxr::Vulkan>,
110    frames: Vec<InternalFrame>,
111    swapchain: Swapchain,
112    view_count: u32,
113}
114
115pub struct XrSessionState {
116    pub instance: xr::Instance,
117    pub system_id: xr::SystemId,
118    pub session: xr::Session<xr::Vulkan>,
119    pub frame_wait: xr::FrameWaiter,
120    pub frame_stream: xr::FrameStream<xr::Vulkan>,
121    pub view_type: xr::ViewConfigurationType,
122    pub environment_blend_mode: xr::EnvironmentBlendMode,
123    pub space: Option<xr::Space>,
124    pub predicted_display_time: Option<xr::Time>,
125}
126
127#[derive(Clone, Copy, Debug)]
128enum Presentation {
129    Window {
130        swapchain: vk::SwapchainKHR,
131        image_index: u32,
132        acquire_semaphore: vk::Semaphore,
133        present_semaphore: vk::Semaphore,
134    },
135    Xr {
136        swapchain: usize,
137        view_count: u32,
138        target_size: [u16; 2],
139        views: [XrView; MAX_XR_EYES],
140    },
141}
142
143#[derive(Clone, Copy, Debug, Default)]
144pub struct XrPose {
145    pub orientation: [f32; 4],
146    pub position: [f32; 3],
147}
148
149#[derive(Clone, Copy, Debug, Default)]
150pub struct XrFov {
151    pub angle_left: f32,
152    pub angle_right: f32,
153    pub angle_up: f32,
154    pub angle_down: f32,
155}
156
157#[derive(Clone, Copy, Debug, Default)]
158pub struct XrView {
159    pub pose: XrPose,
160    pub fov: XrFov,
161}
162
163#[derive(Clone, Copy, Debug)]
164pub struct Frame {
165    swapchain: Swapchain,
166    image_index: Option<u32>,
167    internal: InternalFrame,
168    xr_swapchain: usize,
169    xr_view_count: u32,
170    xr_views: [XrView; MAX_XR_EYES],
171}
172
173impl Frame {
174    pub fn texture(&self) -> Texture {
175        Texture {
176            raw: self.internal.image,
177            memory_handle: !0,
178            target_size: self.swapchain.target_size,
179            format: self.swapchain.format,
180            external: None,
181        }
182    }
183
184    pub fn texture_view(&self) -> TextureView {
185        TextureView {
186            raw: self.internal.view,
187            target_size: self.swapchain.target_size,
188            aspects: crate::TexelAspects::COLOR,
189        }
190    }
191
192    pub fn xr_texture_view(&self, eye: u32) -> TextureView {
193        let eye = eye as usize;
194        assert!(eye < MAX_XR_EYES, "XR eye {} is out of range", eye);
195        let raw = self.internal.xr_views[eye];
196        assert_ne!(
197            raw,
198            vk::ImageView::null(),
199            "XR eye {} view is not initialized",
200            eye
201        );
202        TextureView {
203            raw,
204            target_size: self.swapchain.target_size,
205            aspects: crate::TexelAspects::COLOR,
206        }
207    }
208
209    pub fn xr_view_count(&self) -> u32 {
210        self.xr_view_count
211    }
212
213    pub fn xr_view(&self, eye: u32) -> XrView {
214        let eye = eye as usize;
215        assert!(
216            eye < self.xr_view_count as usize,
217            "XR eye {} is out of range",
218            eye
219        );
220        self.xr_views[eye]
221    }
222}
223
224impl Context {
225    pub fn xr_session(&self) -> Option<xr::Session<xr::Vulkan>> {
226        self.xr
227            .as_ref()
228            .map(|xr| xr.lock().unwrap().session.clone())
229    }
230
231    /// Locate an action space (e.g. controller aim) relative to the XR reference space
232    /// at the last predicted display time.
233    pub fn xr_locate_space(&self, action_space: &xr::Space) -> Option<xr::Posef> {
234        let xr = self.xr.as_ref()?.lock().unwrap();
235        let time = xr.predicted_display_time?;
236        let ref_space = xr.space.as_ref()?;
237        let location = action_space.locate(ref_space, time).ok()?;
238        let flags = location.location_flags;
239        if flags.contains(
240            xr::SpaceLocationFlags::POSITION_VALID | xr::SpaceLocationFlags::ORIENTATION_VALID,
241        ) {
242            Some(location.pose)
243        } else {
244            None
245        }
246    }
247}
248
249fn map_timeout(millis: u32) -> u64 {
250    if millis == !0 {
251        !0
252    } else {
253        millis as u64 * 1_000_000
254    }
255}
256
257pub struct Context {
258    memory: Mutex<MemoryManager>,
259    device: Device,
260    queue_family_index: u32,
261    queue: Mutex<Queue>,
262    physical_device: vk::PhysicalDevice,
263    naga_flags: naga::back::spv::WriterFlags,
264    shader_debug_path: Option<PathBuf>,
265    min_buffer_alignment: u64,
266    min_uniform_buffer_offset_alignment: u64,
267    sample_count_flags: vk::SampleCountFlags,
268    dual_source_blending: bool,
269    shader_float16: bool,
270    cooperative_matrix: crate::CooperativeMatrix,
271    binding_array: bool,
272    memory_budget: bool,
273    instance: Instance,
274    entry: ash::Entry,
275    xr: Option<Mutex<XrSessionState>>,
276}
277
278#[derive(Clone, Copy, Debug, Hash, PartialEq)]
279pub struct Buffer {
280    raw: vk::Buffer,
281    memory_handle: usize,
282    mapped_data: *mut u8,
283    size: u64,
284    external: Option<crate::ExternalMemorySource>,
285}
286
287impl Default for Buffer {
288    fn default() -> Self {
289        Self {
290            raw: vk::Buffer::null(),
291            memory_handle: !0,
292            mapped_data: ptr::null_mut(),
293            size: 0,
294            external: None,
295        }
296    }
297}
298
299impl Buffer {
300    pub fn data(&self) -> *mut u8 {
301        self.mapped_data
302    }
303
304    pub fn size(&self) -> u64 {
305        self.size
306    }
307}
308
309unsafe impl Send for Buffer {}
310unsafe impl Sync for Buffer {}
311
312#[derive(Clone, Copy, Debug, Hash, PartialEq)]
313pub struct Texture {
314    raw: vk::Image,
315    memory_handle: usize,
316    target_size: [u16; 2],
317    format: crate::TextureFormat,
318    external: Option<crate::ExternalMemorySource>,
319}
320
321impl Default for Texture {
322    fn default() -> Self {
323        Self {
324            raw: vk::Image::default(),
325            memory_handle: !0,
326            target_size: [0; 2],
327            format: crate::TextureFormat::Rgba8Unorm,
328            external: None,
329        }
330    }
331}
332
333#[derive(Clone, Copy, Debug, Default, Hash, PartialEq)]
334pub struct TextureView {
335    raw: vk::ImageView,
336    target_size: [u16; 2],
337    aspects: crate::TexelAspects,
338}
339
340#[derive(Clone, Copy, Debug, Hash, PartialEq)]
341pub struct Sampler {
342    raw: vk::Sampler,
343}
344
345#[derive(Clone, Copy, Debug, Default, Hash, PartialEq)]
346pub struct AccelerationStructure {
347    raw: vk::AccelerationStructureKHR,
348    buffer: vk::Buffer,
349    memory_handle: usize,
350}
351
352#[derive(Debug, Default)]
353struct DescriptorSetLayout {
354    raw: vk::DescriptorSetLayout,
355    update_template: vk::DescriptorUpdateTemplate,
356    template_size: u32,
357    template_offsets: Box<[u32]>,
358}
359
360impl DescriptorSetLayout {
361    fn is_empty(&self) -> bool {
362        self.template_size == 0
363    }
364}
365
366#[derive(Debug)]
367struct PipelineLayout {
368    raw: vk::PipelineLayout,
369    descriptor_set_layouts: Vec<DescriptorSetLayout>,
370}
371
372#[derive(Debug)]
373struct ScratchBuffer {
374    raw: vk::Buffer,
375    memory_handle: usize,
376    mapped: *mut u8,
377    capacity: u64,
378    offset: u64,
379    alignment: u64,
380}
381
382pub struct PipelineContext<'a> {
383    update_data: &'a mut [u8],
384    template_offsets: &'a [u32],
385    scratch: Option<&'a mut ScratchBuffer>,
386}
387
388#[derive(Debug)]
389pub struct ComputePipeline {
390    raw: vk::Pipeline,
391    layout: PipelineLayout,
392    wg_size: [u32; 3],
393}
394
395#[hidden_trait::expose]
396impl crate::traits::ComputePipelineBase for ComputePipeline {
397    fn get_workgroup_size(&self) -> [u32; 3] {
398        self.wg_size
399    }
400}
401
402#[derive(Debug)]
403pub struct RenderPipeline {
404    raw: vk::Pipeline,
405    layout: PipelineLayout,
406}
407
408#[derive(Debug)]
409struct CommandBuffer {
410    raw: vk::CommandBuffer,
411    descriptor_pool: descriptor::DescriptorPool,
412    query_pool: vk::QueryPool,
413    timed_pass_names: Vec<String>,
414    scratch: Option<ScratchBuffer>,
415}
416
417struct CrashHandler {
418    name: String,
419    marker_buf: Buffer,
420    raw_string: Box<[u8]>,
421    next_offset: usize,
422}
423
424pub struct CommandEncoder {
425    pool: vk::CommandPool,
426    buffers: Box<[CommandBuffer]>,
427    device: Device,
428    update_data: Vec<u8>,
429    present: Option<Presentation>,
430    crash_handler: Option<CrashHandler>,
431    temp_label: Vec<u8>,
432    timings: crate::Timings,
433}
434pub struct TransferCommandEncoder<'a> {
435    raw: vk::CommandBuffer,
436    device: &'a Device,
437}
438pub struct AccelerationStructureCommandEncoder<'a> {
439    raw: vk::CommandBuffer,
440    device: &'a Device,
441}
442pub struct ComputeCommandEncoder<'a> {
443    cmd_buf: &'a mut CommandBuffer,
444    device: &'a Device,
445    update_data: &'a mut Vec<u8>,
446}
447//Note: we aren't merging this with `ComputeCommandEncoder`
448// because the destructors are different, and they can't be specialized
449// https://github.com/rust-lang/rust/issues/46893
450pub struct RenderCommandEncoder<'a> {
451    cmd_buf: &'a mut CommandBuffer,
452    device: &'a Device,
453    update_data: &'a mut Vec<u8>,
454}
455
456pub struct PipelineEncoder<'a, 'p> {
457    cmd_buf: &'a mut CommandBuffer,
458    layout: &'p PipelineLayout,
459    bind_point: vk::PipelineBindPoint,
460    device: &'a Device,
461    update_data: &'a mut Vec<u8>,
462}
463
464#[derive(Clone, Debug)]
465pub struct SyncPoint {
466    progress: u64,
467}
468
469#[hidden_trait::expose]
470impl crate::traits::CommandDevice for Context {
471    type CommandEncoder = CommandEncoder;
472    type SyncPoint = SyncPoint;
473
474    fn create_command_encoder(&self, desc: super::CommandEncoderDesc) -> CommandEncoder {
475        let pool_info = vk::CommandPoolCreateInfo {
476            flags: vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER,
477            ..Default::default()
478        };
479        let pool = unsafe {
480            self.device
481                .core
482                .create_command_pool(&pool_info, None)
483                .unwrap()
484        };
485        let cmd_buf_info = vk::CommandBufferAllocateInfo {
486            command_pool: pool,
487            command_buffer_count: desc.buffer_count,
488            ..Default::default()
489        };
490        let cmd_buffers = unsafe {
491            self.device
492                .core
493                .allocate_command_buffers(&cmd_buf_info)
494                .unwrap()
495        };
496
497        let buffers = cmd_buffers
498            .into_iter()
499            .map(|raw| {
500                if !desc.name.is_empty() {
501                    self.set_object_name(raw, desc.name);
502                };
503                let descriptor_pool = self.device.create_descriptor_pool();
504                let query_pool = if self.device.timing.is_some() {
505                    let query_pool_info = vk::QueryPoolCreateInfo::default()
506                        .query_type(vk::QueryType::TIMESTAMP)
507                        .query_count(QUERY_POOL_SIZE as u32);
508                    unsafe {
509                        self.device
510                            .core
511                            .create_query_pool(&query_pool_info, None)
512                            .unwrap()
513                    }
514                } else {
515                    vk::QueryPool::null()
516                };
517                let scratch = if !self.device.inline_uniform_blocks {
518                    const SCRATCH_SIZE: u64 = 1 << 20; // 1 MiB
519                    let buf = self.create_buffer(crate::BufferDesc {
520                        name: "_scratch",
521                        size: SCRATCH_SIZE,
522                        memory: crate::Memory::Shared,
523                    });
524                    Some(ScratchBuffer {
525                        raw: buf.raw,
526                        memory_handle: buf.memory_handle,
527                        mapped: buf.mapped_data,
528                        capacity: SCRATCH_SIZE,
529                        offset: 0,
530                        alignment: self.min_uniform_buffer_offset_alignment,
531                    })
532                } else {
533                    None
534                };
535                CommandBuffer {
536                    raw,
537                    descriptor_pool,
538                    query_pool,
539                    timed_pass_names: Vec::new(),
540                    scratch,
541                }
542            })
543            .collect();
544
545        let crash_handler = if self.device.buffer_marker.is_some() {
546            Some(CrashHandler {
547                name: desc.name.to_string(),
548                marker_buf: self.create_buffer(crate::BufferDesc {
549                    name: "_marker",
550                    size: 4,
551                    memory: crate::Memory::Shared,
552                }),
553                raw_string: vec![0; 0x1000].into_boxed_slice(),
554                next_offset: 0,
555            })
556        } else {
557            None
558        };
559
560        CommandEncoder {
561            pool,
562            buffers,
563            device: self.device.clone(),
564            update_data: Vec::new(),
565            present: None,
566            crash_handler,
567            temp_label: Vec::new(),
568            timings: Default::default(),
569        }
570    }
571
572    fn destroy_command_encoder(&self, command_encoder: &mut CommandEncoder) {
573        for cmd_buf in command_encoder.buffers.iter_mut() {
574            let raw_cmd_buffers = [cmd_buf.raw];
575            unsafe {
576                self.device
577                    .core
578                    .free_command_buffers(command_encoder.pool, &raw_cmd_buffers);
579            }
580            self.device
581                .destroy_descriptor_pool(&mut cmd_buf.descriptor_pool);
582            if self.device.timing.is_some() {
583                unsafe {
584                    self.device
585                        .core
586                        .destroy_query_pool(cmd_buf.query_pool, None);
587                }
588            }
589            if let Some(ref scratch) = cmd_buf.scratch {
590                self.destroy_buffer(super::Buffer {
591                    raw: scratch.raw,
592                    memory_handle: scratch.memory_handle,
593                    mapped_data: scratch.mapped,
594                    size: 0,
595                    external: None,
596                });
597            }
598        }
599        unsafe {
600            self.device
601                .core
602                .destroy_command_pool(mem::take(&mut command_encoder.pool), None)
603        };
604        if let Some(crash_handler) = command_encoder.crash_handler.take() {
605            self.destroy_buffer(crash_handler.marker_buf);
606        };
607    }
608
609    fn submit(&self, encoder: &mut CommandEncoder) -> SyncPoint {
610        let raw_cmd_buf = encoder.finish();
611        let mut queue = self.queue.lock().unwrap();
612        queue.last_progress += 1;
613        let progress = queue.last_progress;
614        let command_buffers = [raw_cmd_buf];
615        let wait_values_all = [0];
616        let mut wait_semaphores_all = [vk::Semaphore::null()];
617        let wait_stages = [vk::PipelineStageFlags::ALL_COMMANDS];
618        let mut signal_semaphores_all = [queue.timeline_semaphore, vk::Semaphore::null()];
619        let signal_values_all = [progress, 0];
620        let (num_wait_semaphores, num_signal_sepahores) = match encoder.present {
621            Some(Presentation::Window {
622                acquire_semaphore,
623                present_semaphore,
624                ..
625            }) => {
626                wait_semaphores_all[0] = acquire_semaphore;
627                signal_semaphores_all[1] = present_semaphore;
628                (1, 2)
629            }
630            Some(Presentation::Xr { .. }) | None => (0, 1),
631        };
632        let mut timeline_info = vk::TimelineSemaphoreSubmitInfo::default()
633            .wait_semaphore_values(&wait_values_all[..num_wait_semaphores])
634            .signal_semaphore_values(&signal_values_all[..num_signal_sepahores]);
635        let vk_info = vk::SubmitInfo::default()
636            .command_buffers(&command_buffers)
637            .wait_semaphores(&wait_semaphores_all[..num_wait_semaphores])
638            .wait_dst_stage_mask(&wait_stages[..num_wait_semaphores])
639            .signal_semaphores(&signal_semaphores_all[..num_signal_sepahores])
640            .push_next(&mut timeline_info);
641        let ret = unsafe {
642            self.device
643                .core
644                .queue_submit(queue.raw, &[vk_info], vk::Fence::null())
645        };
646        encoder.check_gpu_crash(ret);
647
648        if let Some(presentation) = encoder.present.take() {
649            match presentation {
650                Presentation::Window {
651                    swapchain,
652                    image_index,
653                    present_semaphore,
654                    ..
655                } => {
656                    let khr_swapchain = self.device.swapchain.as_ref().unwrap();
657                    let swapchains = [swapchain];
658                    let image_indices = [image_index];
659                    let wait_semaphores = [present_semaphore];
660                    let present_info = vk::PresentInfoKHR::default()
661                        .swapchains(&swapchains)
662                        .image_indices(&image_indices)
663                        .wait_semaphores(&wait_semaphores);
664                    let ret = unsafe { khr_swapchain.queue_present(queue.raw, &present_info) };
665                    let _ = encoder.check_gpu_crash(ret);
666                }
667                Presentation::Xr {
668                    swapchain,
669                    view_count,
670                    target_size,
671                    views,
672                } => {
673                    let semaphores = [queue.timeline_semaphore];
674                    let semaphore_values = [progress];
675                    let wait_info = vk::SemaphoreWaitInfoKHR::default()
676                        .semaphores(&semaphores)
677                        .values(&semaphore_values);
678                    unsafe {
679                        self.device
680                            .timeline_semaphore
681                            .wait_semaphores(&wait_info, !0)
682                            .unwrap();
683                    }
684                    let swapchain = unsafe { &mut *(swapchain as *mut xr::Swapchain<xr::Vulkan>) };
685                    swapchain.release_image().unwrap();
686
687                    let xr_state = self.xr.as_ref().expect("XR is not enabled in this context");
688                    let mut xr_state = xr_state.lock().unwrap();
689                    let environment_blend_mode = xr_state.environment_blend_mode;
690                    let space = xr_state.space.take().expect("XR space is not initialized");
691                    let predicted_display_time = xr_state
692                        .predicted_display_time
693                        .expect("XR frame timing is not initialized");
694                    let rect = xr::Rect2Di {
695                        offset: xr::Offset2Di { x: 0, y: 0 },
696                        extent: xr::Extent2Di {
697                            width: target_size[0] as _,
698                            height: target_size[1] as _,
699                        },
700                    };
701                    let projection_views = views[..view_count as usize]
702                        .iter()
703                        .enumerate()
704                        .map(|(i, view)| {
705                            xr::CompositionLayerProjectionView::new()
706                                .pose(xr::Posef {
707                                    orientation: xr::Quaternionf {
708                                        x: view.pose.orientation[0],
709                                        y: view.pose.orientation[1],
710                                        z: view.pose.orientation[2],
711                                        w: view.pose.orientation[3],
712                                    },
713                                    position: xr::Vector3f {
714                                        x: view.pose.position[0],
715                                        y: view.pose.position[1],
716                                        z: view.pose.position[2],
717                                    },
718                                })
719                                .fov(xr::Fovf {
720                                    angle_left: view.fov.angle_left,
721                                    angle_right: view.fov.angle_right,
722                                    angle_up: view.fov.angle_up,
723                                    angle_down: view.fov.angle_down,
724                                })
725                                .sub_image(
726                                    xr::SwapchainSubImage::new()
727                                        .swapchain(swapchain)
728                                        .image_array_index(i as u32)
729                                        .image_rect(rect),
730                                )
731                        })
732                        .collect::<Vec<_>>();
733                    match xr_state.frame_stream.end(
734                        predicted_display_time,
735                        environment_blend_mode,
736                        &[&xr::CompositionLayerProjection::new()
737                            .space(&space)
738                            .views(&projection_views)],
739                    ) {
740                        Ok(()) => {}
741                        Err(xr::sys::Result::ERROR_POSE_INVALID) => {
742                            // Tracking was lost between frame acquire and
743                            // present — transient, safe to ignore.
744                            log::warn!("XR frame end: pose invalid (tracking lost?)");
745                        }
746                        Err(e) => panic!("XR frame end failed: {e}"),
747                    }
748                    xr_state.space = Some(space);
749                }
750            }
751        }
752
753        SyncPoint { progress }
754    }
755
756    fn wait_for(&self, sp: &SyncPoint, timeout_ms: u32) -> Result<bool, crate::DeviceError> {
757        //Note: technically we could get away without locking the queue,
758        // but also this isn't time-sensitive, so it's fine.
759        let timeline_semaphore = self.queue.lock().unwrap().timeline_semaphore;
760        let semaphores = [timeline_semaphore];
761        let semaphore_values = [sp.progress];
762        let wait_info = vk::SemaphoreWaitInfoKHR::default()
763            .semaphores(&semaphores)
764            .values(&semaphore_values);
765        let timeout_ns = map_timeout(timeout_ms);
766        match unsafe {
767            self.device
768                .timeline_semaphore
769                .wait_semaphores(&wait_info, timeout_ns)
770        } {
771            Ok(()) => Ok(true),
772            Err(vk::Result::TIMEOUT) => Ok(false),
773            Err(vk::Result::ERROR_DEVICE_LOST) => Err(crate::DeviceError::DeviceLost),
774            Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY)
775            | Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => Err(crate::DeviceError::OutOfMemory),
776            Err(other) => {
777                log::error!("Unexpected wait_semaphores error: {:?}", other);
778                Err(crate::DeviceError::DeviceLost)
779            }
780        }
781    }
782}
783
784fn map_texture_format(format: crate::TextureFormat) -> vk::Format {
785    use crate::TextureFormat as Tf;
786    match format {
787        Tf::R8Unorm => vk::Format::R8_UNORM,
788        Tf::Rg8Unorm => vk::Format::R8G8_UNORM,
789        Tf::Rg8Snorm => vk::Format::R8G8_SNORM,
790        Tf::Rgba8Unorm => vk::Format::R8G8B8A8_UNORM,
791        Tf::Rgba8UnormSrgb => vk::Format::R8G8B8A8_SRGB,
792        Tf::Bgra8Unorm => vk::Format::B8G8R8A8_UNORM,
793        Tf::Bgra8UnormSrgb => vk::Format::B8G8R8A8_SRGB,
794        Tf::Rgba8Snorm => vk::Format::R8G8B8A8_SNORM,
795        Tf::R16Float => vk::Format::R16_SFLOAT,
796        Tf::Rg16Float => vk::Format::R16G16_SFLOAT,
797        Tf::Rgba16Float => vk::Format::R16G16B16A16_SFLOAT,
798        Tf::R32Float => vk::Format::R32_SFLOAT,
799        Tf::Rg32Float => vk::Format::R32G32_SFLOAT,
800        Tf::Rgba32Float => vk::Format::R32G32B32A32_SFLOAT,
801        Tf::R32Uint => vk::Format::R32_UINT,
802        Tf::Rg32Uint => vk::Format::R32G32_UINT,
803        Tf::Rgba32Uint => vk::Format::R32G32B32A32_UINT,
804        Tf::Depth32Float => vk::Format::D32_SFLOAT,
805        Tf::Depth32FloatStencil8Uint => vk::Format::D32_SFLOAT_S8_UINT,
806        Tf::Stencil8Uint => vk::Format::S8_UINT,
807        Tf::Bc1Unorm => vk::Format::BC1_RGBA_SRGB_BLOCK,
808        Tf::Bc1UnormSrgb => vk::Format::BC1_RGBA_UNORM_BLOCK,
809        Tf::Bc2Unorm => vk::Format::BC2_UNORM_BLOCK,
810        Tf::Bc2UnormSrgb => vk::Format::BC2_SRGB_BLOCK,
811        Tf::Bc3Unorm => vk::Format::BC3_UNORM_BLOCK,
812        Tf::Bc3UnormSrgb => vk::Format::BC3_SRGB_BLOCK,
813        Tf::Bc4Unorm => vk::Format::BC4_UNORM_BLOCK,
814        Tf::Bc4Snorm => vk::Format::BC4_SNORM_BLOCK,
815        Tf::Bc5Unorm => vk::Format::BC5_UNORM_BLOCK,
816        Tf::Bc5Snorm => vk::Format::BC5_SNORM_BLOCK,
817        Tf::Bc6hUfloat => vk::Format::BC6H_UFLOAT_BLOCK,
818        Tf::Bc6hFloat => vk::Format::BC6H_SFLOAT_BLOCK,
819        Tf::Bc7Unorm => vk::Format::BC7_UNORM_BLOCK,
820        Tf::Bc7UnormSrgb => vk::Format::BC7_SRGB_BLOCK,
821        Tf::Rgb10a2Unorm => vk::Format::A2B10G10R10_UNORM_PACK32,
822        Tf::Rg11b10Ufloat => vk::Format::B10G11R11_UFLOAT_PACK32,
823        Tf::Rgb9e5Ufloat => vk::Format::E5B9G9R9_UFLOAT_PACK32,
824    }
825}
826
827fn map_aspects(aspects: crate::TexelAspects) -> vk::ImageAspectFlags {
828    let mut flags = vk::ImageAspectFlags::empty();
829    if aspects.contains(crate::TexelAspects::COLOR) {
830        flags |= vk::ImageAspectFlags::COLOR;
831    }
832    if aspects.contains(crate::TexelAspects::DEPTH) {
833        flags |= vk::ImageAspectFlags::DEPTH;
834    }
835    if aspects.contains(crate::TexelAspects::STENCIL) {
836        flags |= vk::ImageAspectFlags::STENCIL;
837    }
838    flags
839}
840
841fn map_extent_3d(extent: &crate::Extent) -> vk::Extent3D {
842    vk::Extent3D {
843        width: extent.width,
844        height: extent.height,
845        depth: extent.depth,
846    }
847}
848
849fn map_subresource_range(
850    subresources: &crate::TextureSubresources,
851    aspects: crate::TexelAspects,
852) -> vk::ImageSubresourceRange {
853    vk::ImageSubresourceRange {
854        aspect_mask: map_aspects(aspects),
855        base_mip_level: subresources.base_mip_level,
856        level_count: subresources
857            .mip_level_count
858            .map_or(vk::REMAINING_MIP_LEVELS, NonZeroU32::get),
859        base_array_layer: subresources.base_array_layer,
860        layer_count: subresources
861            .array_layer_count
862            .map_or(vk::REMAINING_ARRAY_LAYERS, NonZeroU32::get),
863    }
864}
865
866fn map_comparison(fun: crate::CompareFunction) -> vk::CompareOp {
867    use crate::CompareFunction as Cf;
868    match fun {
869        Cf::Never => vk::CompareOp::NEVER,
870        Cf::Less => vk::CompareOp::LESS,
871        Cf::LessEqual => vk::CompareOp::LESS_OR_EQUAL,
872        Cf::Equal => vk::CompareOp::EQUAL,
873        Cf::GreaterEqual => vk::CompareOp::GREATER_OR_EQUAL,
874        Cf::Greater => vk::CompareOp::GREATER,
875        Cf::NotEqual => vk::CompareOp::NOT_EQUAL,
876        Cf::Always => vk::CompareOp::ALWAYS,
877    }
878}
879
880fn map_index_type(index_type: crate::IndexType) -> vk::IndexType {
881    match index_type {
882        crate::IndexType::U16 => vk::IndexType::UINT16,
883        crate::IndexType::U32 => vk::IndexType::UINT32,
884    }
885}
886
887fn map_vertex_format(vertex_format: crate::VertexFormat) -> vk::Format {
888    use crate::VertexFormat as Vf;
889    match vertex_format {
890        Vf::F32 => vk::Format::R32_SFLOAT,
891        Vf::F32Vec2 => vk::Format::R32G32_SFLOAT,
892        Vf::F32Vec3 => vk::Format::R32G32B32_SFLOAT,
893        Vf::F32Vec4 => vk::Format::R32G32B32A32_SFLOAT,
894        Vf::U32 => vk::Format::R32_UINT,
895        Vf::U32Vec2 => vk::Format::R32G32_UINT,
896        Vf::U32Vec3 => vk::Format::R32G32B32_UINT,
897        Vf::U32Vec4 => vk::Format::R32G32B32A32_UINT,
898        Vf::I32 => vk::Format::R32_SINT,
899        Vf::I32Vec2 => vk::Format::R32G32_SINT,
900        Vf::I32Vec3 => vk::Format::R32G32B32_SINT,
901        Vf::I32Vec4 => vk::Format::R32G32B32A32_SINT,
902    }
903}
904
905struct BottomLevelAccelerationStructureInput<'a> {
906    max_primitive_counts: Box<[u32]>,
907    build_range_infos: Box<[vk::AccelerationStructureBuildRangeInfoKHR]>,
908    _geometries: Box<[vk::AccelerationStructureGeometryKHR<'a>]>,
909    build_info: vk::AccelerationStructureBuildGeometryInfoKHR<'a>,
910}
911
912impl Device {
913    fn get_device_address(&self, piece: &crate::BufferPiece) -> u64 {
914        let vk_info = vk::BufferDeviceAddressInfo {
915            buffer: piece.buffer.raw,
916            ..Default::default()
917        };
918        let base = unsafe { self.core.get_buffer_device_address(&vk_info) };
919        base + piece.offset
920    }
921
922    fn map_acceleration_structure_meshes(
923        &self,
924        meshes: &[crate::AccelerationStructureMesh],
925    ) -> BottomLevelAccelerationStructureInput<'_> {
926        let mut total_primitive_count = 0;
927        let mut max_primitive_counts = Vec::with_capacity(meshes.len());
928        let mut build_range_infos = Vec::with_capacity(meshes.len());
929        let mut geometries = Vec::with_capacity(meshes.len());
930        for mesh in meshes {
931            total_primitive_count += mesh.triangle_count;
932            max_primitive_counts.push(mesh.triangle_count);
933            build_range_infos.push(vk::AccelerationStructureBuildRangeInfoKHR {
934                primitive_count: mesh.triangle_count,
935                primitive_offset: 0,
936                first_vertex: 0,
937                transform_offset: 0,
938            });
939
940            let mut triangles = vk::AccelerationStructureGeometryTrianglesDataKHR {
941                vertex_format: map_vertex_format(mesh.vertex_format),
942                vertex_data: {
943                    let device_address = self.get_device_address(&mesh.vertex_data);
944                    assert!(
945                        device_address & 0x3 == 0,
946                        "Vertex data address {device_address} is not aligned"
947                    );
948                    vk::DeviceOrHostAddressConstKHR { device_address }
949                },
950                vertex_stride: mesh.vertex_stride as u64,
951                max_vertex: mesh.vertex_count.saturating_sub(1),
952                ..Default::default()
953            };
954            if let Some(index_type) = mesh.index_type {
955                let device_address = self.get_device_address(&mesh.index_data);
956                assert!(
957                    device_address & 0x3 == 0,
958                    "Index data address {device_address} is not aligned"
959                );
960                triangles.index_type = map_index_type(index_type);
961                triangles.index_data = vk::DeviceOrHostAddressConstKHR { device_address };
962            }
963            if mesh.transform_data.buffer.raw != vk::Buffer::null() {
964                let device_address = self.get_device_address(&mesh.transform_data);
965                assert!(
966                    device_address & 0xF == 0,
967                    "Transform data address {device_address} is not aligned"
968                );
969                triangles.transform_data = vk::DeviceOrHostAddressConstKHR { device_address };
970            }
971
972            let geometry = vk::AccelerationStructureGeometryKHR {
973                geometry_type: vk::GeometryTypeKHR::TRIANGLES,
974                geometry: vk::AccelerationStructureGeometryDataKHR { triangles },
975                flags: if mesh.is_opaque {
976                    vk::GeometryFlagsKHR::OPAQUE
977                } else {
978                    vk::GeometryFlagsKHR::empty()
979                },
980                ..Default::default()
981            };
982            geometries.push(geometry);
983        }
984        let build_info = vk::AccelerationStructureBuildGeometryInfoKHR {
985            ty: vk::AccelerationStructureTypeKHR::BOTTOM_LEVEL,
986            flags: vk::BuildAccelerationStructureFlagsKHR::PREFER_FAST_TRACE,
987            mode: vk::BuildAccelerationStructureModeKHR::BUILD,
988            geometry_count: geometries.len() as u32,
989            p_geometries: geometries.as_ptr(),
990            ..Default::default()
991        };
992
993        log::debug!(
994            "BLAS total {} primitives in {} geometries",
995            total_primitive_count,
996            geometries.len()
997        );
998        BottomLevelAccelerationStructureInput {
999            max_primitive_counts: max_primitive_counts.into_boxed_slice(),
1000            build_range_infos: build_range_infos.into_boxed_slice(),
1001            _geometries: geometries.into_boxed_slice(),
1002            build_info,
1003        }
1004    }
1005}