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 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}
447pub 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; 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 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 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}