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