1use ash::vk::{self, Handle as _};
2use openxr as xr;
3use std::mem;
4
5impl super::Surface {
6 pub fn info(&self) -> crate::SurfaceInfo {
7 crate::SurfaceInfo {
8 format: self.swapchain.format,
9 alpha: self.swapchain.alpha,
10 }
11 }
12
13 unsafe fn deinit_swapchain(&mut self, raw_device: &ash::Device) {
14 unsafe {
15 let _ = raw_device.device_wait_idle();
16 self.device
17 .destroy_swapchain(mem::take(&mut self.swapchain.raw), None);
18 }
19 for frame in self.frames.drain(..) {
20 for view in frame.xr_views {
21 if view != vk::ImageView::null() {
22 unsafe { raw_device.destroy_image_view(view, None) };
23 }
24 }
25 unsafe {
26 raw_device.destroy_image_view(frame.view, None);
27 raw_device.destroy_semaphore(frame.acquire_semaphore, None);
28 raw_device.destroy_semaphore(frame.present_semaphore, None);
29 }
30 }
31 }
32
33 pub fn acquire_frame(&mut self) -> super::Frame {
34 let acquire_semaphore = self.next_semaphore;
35 match unsafe {
36 self.device.acquire_next_image(
37 self.swapchain.raw,
38 !0,
39 acquire_semaphore,
40 vk::Fence::null(),
41 )
42 } {
43 Ok((index, _suboptimal)) => {
44 self.next_semaphore = mem::replace(
45 &mut self.frames[index as usize].acquire_semaphore,
46 acquire_semaphore,
47 );
48 super::Frame {
49 internal: self.frames[index as usize],
50 swapchain: self.swapchain,
51 image_index: Some(index),
52 xr_swapchain: 0,
53 xr_view_count: 0,
54 xr_views: [super::XrView::default(); super::MAX_XR_EYES],
55 }
56 }
57 Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => {
58 log::warn!("Acquire failed because the surface is out of date");
59 super::Frame {
60 internal: self.frames[0],
61 swapchain: self.swapchain,
62 image_index: None,
63 xr_swapchain: 0,
64 xr_view_count: 0,
65 xr_views: [super::XrView::default(); super::MAX_XR_EYES],
66 }
67 }
68 Err(other) => {
69 log::error!("Acquire image error: {}", other);
70 super::Frame {
71 internal: self.frames[0],
72 swapchain: self.swapchain,
73 image_index: None,
74 xr_swapchain: 0,
75 xr_view_count: 0,
76 xr_views: [super::XrView::default(); super::MAX_XR_EYES],
77 }
78 }
79 }
80 }
81}
82
83impl super::XrSurface {
84 pub fn acquire_frame(&mut self, context: &super::Context) -> Option<super::Frame> {
85 let xr_state = context.xr.as_ref()?;
86 {
87 let mut xr = xr_state.lock().unwrap();
88 let frame_state = xr.frame_wait.wait().ok()?;
89 xr.frame_stream.begin().ok()?;
90 xr.predicted_display_time = Some(frame_state.predicted_display_time);
91 if !frame_state.should_render {
92 xr.predicted_display_time = None;
93 let environment_blend_mode = xr.environment_blend_mode;
94 xr.frame_stream
95 .end(
96 frame_state.predicted_display_time,
97 environment_blend_mode,
98 &[],
99 )
100 .ok()?;
101 return None;
102 }
103 }
104
105 let image_index = self.raw.acquire_image().ok()?;
106 self.raw.wait_image(xr::Duration::INFINITE).ok()?;
107 let mut xr_views = [super::XrView::default(); super::MAX_XR_EYES];
108 let xr_view_count = {
109 let xr = xr_state.lock().unwrap();
110 let predicted_display_time = xr.predicted_display_time?;
111 let space = xr.space.as_ref()?;
112 let (_, views) = xr
113 .session
114 .locate_views(xr.view_type, predicted_display_time, space)
115 .ok()?;
116 let count = views.len().min(self.view_count as usize);
117 if views.len() > self.view_count as usize {
118 log::warn!(
119 "OpenXR returned {} views, truncating to {}",
120 views.len(),
121 self.view_count
122 );
123 }
124 for (i, view) in views.iter().take(count).enumerate() {
125 xr_views[i] = super::XrView {
126 pose: super::XrPose {
127 orientation: [
128 view.pose.orientation.x,
129 view.pose.orientation.y,
130 view.pose.orientation.z,
131 view.pose.orientation.w,
132 ],
133 position: [
134 view.pose.position.x,
135 view.pose.position.y,
136 view.pose.position.z,
137 ],
138 },
139 fov: super::XrFov {
140 angle_left: view.fov.angle_left,
141 angle_right: view.fov.angle_right,
142 angle_up: view.fov.angle_up,
143 angle_down: view.fov.angle_down,
144 },
145 };
146 }
147 count as u32
148 };
149 Some(super::Frame {
150 internal: self.frames[image_index as usize],
151 swapchain: self.swapchain,
152 image_index: Some(image_index),
153 xr_swapchain: (&mut self.raw as *mut xr::Swapchain<xr::Vulkan>) as usize,
154 xr_view_count,
155 xr_views,
156 })
157 }
158
159 pub fn release_frame(&mut self) {
160 self.raw.release_image().unwrap();
161 }
162
163 pub fn extent(&self) -> crate::Extent {
164 crate::Extent {
165 width: self.swapchain.target_size[0] as u32,
166 height: self.swapchain.target_size[1] as u32,
167 depth: 1,
168 }
169 }
170
171 pub fn view_count(&self) -> u32 {
172 self.view_count
173 }
174
175 pub fn format(&self) -> crate::TextureFormat {
176 self.swapchain.format
177 }
178
179 pub fn swapchain(&self) -> &xr::Swapchain<xr::Vulkan> {
180 &self.raw
181 }
182}
183
184impl super::Context {
185 pub fn create_surface<
186 I: raw_window_handle::HasWindowHandle + raw_window_handle::HasDisplayHandle,
187 >(
188 &self,
189 window: &I,
190 ) -> Result<super::Surface, crate::NotSupportedError> {
191 let khr_swapchain = self
192 .device
193 .swapchain
194 .clone()
195 .ok_or(crate::NotSupportedError::NoSupportedDeviceFound)?;
196
197 let raw = unsafe {
198 ash_window::create_surface(
199 &self.entry,
200 &self.instance.core,
201 window.display_handle().unwrap().as_raw(),
202 window.window_handle().unwrap().as_raw(),
203 None,
204 )
205 .map_err(crate::PlatformError::init)?
206 };
207
208 let khr_surface = self
209 .instance
210 .surface
211 .as_ref()
212 .ok_or(crate::NotSupportedError::PlatformNotSupported)?;
213 if unsafe {
214 khr_surface.get_physical_device_surface_support(
215 self.physical_device,
216 self.queue_family_index,
217 raw,
218 ) != Ok(true)
219 } {
220 log::warn!("Rejected for not presenting to the window surface");
221 return Err(crate::NotSupportedError::PlatformNotSupported);
222 }
223
224 let mut surface_info = vk::PhysicalDeviceSurfaceInfo2KHR {
225 surface: raw,
226 ..Default::default()
227 };
228 let mut fullscreen_exclusive_win32 = vk::SurfaceFullScreenExclusiveWin32InfoEXT::default();
229 surface_info = surface_info.push_next(&mut fullscreen_exclusive_win32);
230 let mut fullscreen_exclusive_ext = vk::SurfaceCapabilitiesFullScreenExclusiveEXT::default();
231 let mut capabilities2_khr =
232 vk::SurfaceCapabilities2KHR::default().push_next(&mut fullscreen_exclusive_ext);
233 let _ = unsafe {
234 self.instance
235 .get_surface_capabilities2
236 .as_ref()
237 .unwrap()
238 .get_physical_device_surface_capabilities2(
239 self.physical_device,
240 &surface_info,
241 &mut capabilities2_khr,
242 )
243 };
244 log::debug!("{:?}", capabilities2_khr.surface_capabilities);
245
246 let semaphore_create_info = vk::SemaphoreCreateInfo::default();
247 let next_semaphore = unsafe {
248 self.device
249 .core
250 .create_semaphore(&semaphore_create_info, None)
251 .unwrap()
252 };
253
254 Ok(super::Surface {
255 device: khr_swapchain,
256 raw,
257 frames: Vec::new(),
258 next_semaphore,
259 swapchain: super::Swapchain {
260 raw: vk::SwapchainKHR::null(),
261 format: crate::TextureFormat::Rgba8Unorm,
262 alpha: crate::AlphaMode::Ignored,
263 target_size: [0; 2],
264 },
265 full_screen_exclusive: fullscreen_exclusive_ext.full_screen_exclusive_supported != 0,
266 })
267 }
268
269 pub fn destroy_surface(&self, surface: &mut super::Surface) {
270 unsafe {
271 surface.deinit_swapchain(&self.device.core);
272 self.device
273 .core
274 .destroy_semaphore(surface.next_semaphore, None)
275 };
276 if let Some(ref surface_instance) = self.instance.surface {
277 unsafe { surface_instance.destroy_surface(surface.raw, None) };
278 }
279 }
280
281 pub fn reconfigure_surface(&self, surface: &mut super::Surface, config: crate::SurfaceConfig) {
282 let khr_surface = self.instance.surface.as_ref().unwrap();
283
284 let capabilities = unsafe {
285 khr_surface
286 .get_physical_device_surface_capabilities(self.physical_device, surface.raw)
287 .unwrap()
288 };
289 if config.size.width < capabilities.min_image_extent.width
290 || config.size.width > capabilities.max_image_extent.width
291 || config.size.height < capabilities.min_image_extent.height
292 || config.size.height > capabilities.max_image_extent.height
293 {
294 log::warn!(
295 "Requested size {}x{} is outside of surface capabilities",
296 config.size.width,
297 config.size.height
298 );
299 }
300
301 let (alpha, composite_alpha) = if config.transparent {
302 if capabilities
303 .supported_composite_alpha
304 .contains(vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED)
305 {
306 (
307 crate::AlphaMode::PostMultiplied,
308 vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED,
309 )
310 } else if capabilities
311 .supported_composite_alpha
312 .contains(vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED)
313 {
314 (
315 crate::AlphaMode::PreMultiplied,
316 vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
317 )
318 } else {
319 log::error!(
320 "No composite alpha flag for transparency: {:?}",
321 capabilities.supported_composite_alpha
322 );
323 (
324 crate::AlphaMode::Ignored,
325 vk::CompositeAlphaFlagsKHR::OPAQUE,
326 )
327 }
328 } else {
329 (
330 crate::AlphaMode::Ignored,
331 vk::CompositeAlphaFlagsKHR::OPAQUE,
332 )
333 };
334
335 let (requested_frame_count, mode_preferences) = match config.display_sync {
336 crate::DisplaySync::Block => (3, [vk::PresentModeKHR::FIFO].as_slice()),
337 crate::DisplaySync::Recent => (
338 3,
339 [
340 vk::PresentModeKHR::MAILBOX,
341 vk::PresentModeKHR::FIFO_RELAXED,
342 vk::PresentModeKHR::IMMEDIATE,
343 ]
344 .as_slice(),
345 ),
346 crate::DisplaySync::Tear => (2, [vk::PresentModeKHR::IMMEDIATE].as_slice()),
347 };
348 let effective_frame_count = requested_frame_count.max(capabilities.min_image_count);
349
350 let present_modes = unsafe {
351 khr_surface
352 .get_physical_device_surface_present_modes(self.physical_device, surface.raw)
353 .unwrap()
354 };
355 let present_mode = *mode_preferences
356 .iter()
357 .find(|mode| present_modes.contains(mode))
358 .unwrap();
359 log::info!("Using surface present mode {:?}", present_mode);
360
361 let queue_families = [self.queue_family_index];
362
363 let mut supported_formats = Vec::new();
364 let (format, surface_format) = if surface.swapchain.target_size[0] > 0 {
365 let format = surface.swapchain.format;
366 log::info!("Retaining current format: {:?}", format);
367 let vk_color_space = match (format, config.color_space) {
368 (crate::TextureFormat::Bgra8Unorm, crate::ColorSpace::Srgb) => {
369 vk::ColorSpaceKHR::SRGB_NONLINEAR
370 }
371 (crate::TextureFormat::Bgra8Unorm, crate::ColorSpace::Linear) => {
372 vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT
373 }
374 (crate::TextureFormat::Bgra8UnormSrgb, crate::ColorSpace::Linear) => {
375 vk::ColorSpaceKHR::default()
376 }
377 _ => panic!(
378 "Unexpected format {:?} under color space {:?}",
379 format, config.color_space
380 ),
381 };
382 (
383 format,
384 vk::SurfaceFormatKHR {
385 format: super::map_texture_format(format),
386 color_space: vk_color_space,
387 },
388 )
389 } else {
390 supported_formats = unsafe {
391 khr_surface
392 .get_physical_device_surface_formats(self.physical_device, surface.raw)
393 .unwrap()
394 };
395 match config.color_space {
396 crate::ColorSpace::Linear => {
397 let surface_format = vk::SurfaceFormatKHR {
398 format: vk::Format::B8G8R8A8_UNORM,
399 color_space: vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT,
400 };
401 if supported_formats.contains(&surface_format) {
402 log::info!("Using linear SRGB color space");
403 (crate::TextureFormat::Bgra8Unorm, surface_format)
404 } else {
405 (
406 crate::TextureFormat::Bgra8UnormSrgb,
407 vk::SurfaceFormatKHR {
408 format: vk::Format::B8G8R8A8_SRGB,
409 color_space: vk::ColorSpaceKHR::default(),
410 },
411 )
412 }
413 }
414 crate::ColorSpace::Srgb => (
415 crate::TextureFormat::Bgra8Unorm,
416 vk::SurfaceFormatKHR {
417 format: vk::Format::B8G8R8A8_UNORM,
418 color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR,
419 },
420 ),
421 }
422 };
423 if !supported_formats.is_empty() && !supported_formats.contains(&surface_format) {
424 log::error!("Surface formats are incompatible: {:?}", supported_formats);
425 }
426
427 let vk_usage = super::resource::map_texture_usage(config.usage, crate::TexelAspects::COLOR);
428 if !capabilities.supported_usage_flags.contains(vk_usage) {
429 log::error!(
430 "Surface usages are incompatible: {:?}",
431 capabilities.supported_usage_flags
432 );
433 }
434
435 let mut full_screen_exclusive_info = vk::SurfaceFullScreenExclusiveInfoEXT {
436 full_screen_exclusive: if config.allow_exclusive_full_screen {
437 vk::FullScreenExclusiveEXT::ALLOWED
438 } else {
439 vk::FullScreenExclusiveEXT::DISALLOWED
440 },
441 ..Default::default()
442 };
443
444 let mut create_info = vk::SwapchainCreateInfoKHR {
445 surface: surface.raw,
446 min_image_count: effective_frame_count,
447 image_format: surface_format.format,
448 image_color_space: surface_format.color_space,
449 image_extent: vk::Extent2D {
450 width: config.size.width,
451 height: config.size.height,
452 },
453 image_array_layers: 1,
454 image_usage: vk_usage,
455 pre_transform: vk::SurfaceTransformFlagsKHR::IDENTITY,
456 composite_alpha,
457 present_mode,
458 old_swapchain: surface.swapchain.raw,
459 ..Default::default()
460 }
461 .queue_family_indices(&queue_families);
462
463 if surface.full_screen_exclusive {
464 assert!(self.device.full_screen_exclusive.is_some());
465 create_info = create_info.push_next(&mut full_screen_exclusive_info);
466 log::info!(
467 "Configuring exclusive full screen: {}",
468 config.allow_exclusive_full_screen
469 );
470 }
471 let raw_swapchain = unsafe { surface.device.create_swapchain(&create_info, None).unwrap() };
472
473 unsafe {
474 surface.deinit_swapchain(&self.device.core);
475 }
476
477 let images = unsafe { surface.device.get_swapchain_images(raw_swapchain).unwrap() };
478 let target_size = [config.size.width as u16, config.size.height as u16];
479 let subresource_range = vk::ImageSubresourceRange {
480 aspect_mask: vk::ImageAspectFlags::COLOR,
481 base_mip_level: 0,
482 level_count: 1,
483 base_array_layer: 0,
484 layer_count: 1,
485 };
486 for image in images {
487 let view_create_info = vk::ImageViewCreateInfo {
488 image,
489 view_type: vk::ImageViewType::TYPE_2D,
490 format: surface_format.format,
491 subresource_range,
492 ..Default::default()
493 };
494 let view = unsafe {
495 self.device
496 .core
497 .create_image_view(&view_create_info, None)
498 .unwrap()
499 };
500 let semaphore_create_info = vk::SemaphoreCreateInfo::default();
501 let acquire_semaphore = unsafe {
502 self.device
503 .core
504 .create_semaphore(&semaphore_create_info, None)
505 .unwrap()
506 };
507 let present_semaphore = unsafe {
508 self.device
509 .core
510 .create_semaphore(&semaphore_create_info, None)
511 .unwrap()
512 };
513 surface.frames.push(super::InternalFrame {
514 acquire_semaphore,
515 present_semaphore,
516 image,
517 view,
518 xr_views: [vk::ImageView::null(); super::MAX_XR_EYES],
519 });
520 }
521 surface.swapchain = super::Swapchain {
522 raw: raw_swapchain,
523 format,
524 alpha,
525 target_size,
526 };
527 }
528
529 fn xr_recommended_surface_config(
530 &self,
531 view_type: xr::ViewConfigurationType,
532 ) -> Option<crate::XrSurfaceConfig> {
533 let xr = self.xr.as_ref()?;
534 let xr = xr.lock().unwrap();
535 let views = xr
536 .instance
537 .enumerate_view_configuration_views(xr.system_id, view_type)
538 .ok()?;
539 let first = *views.first()?;
540 let view_count = (views.len() as u32).min(super::MAX_XR_EYES as u32);
541 Some(crate::XrSurfaceConfig {
542 size: crate::Extent {
543 width: first.recommended_image_rect_width,
544 height: first.recommended_image_rect_height,
545 depth: 1,
546 },
547 usage: crate::TextureUsage::TARGET,
548 color_space: crate::ColorSpace::Linear,
549 view_count,
550 })
551 }
552
553 pub fn create_xr_surface(&self) -> Option<super::XrSurface> {
554 let config =
555 self.xr_recommended_surface_config(xr::ViewConfigurationType::PRIMARY_STEREO)?;
556 self.create_xr_surface_configured(config)
557 }
558
559 fn create_xr_surface_configured(
560 &self,
561 config: crate::XrSurfaceConfig,
562 ) -> Option<super::XrSurface> {
563 let xr = self.xr.as_ref()?;
564 let mut surface = {
565 let xr = xr.lock().unwrap();
566 let (raw_format, format) = select_xr_swapchain_format(&xr.session, config.color_space);
567 let raw = xr
568 .session
569 .create_swapchain(&xr::SwapchainCreateInfo {
570 create_flags: xr::SwapchainCreateFlags::EMPTY,
571 usage_flags: xr_swapchain_usage(config.usage),
572 format: raw_format,
573 sample_count: 1,
574 width: config.size.width,
575 height: config.size.height,
576 face_count: 1,
577 array_size: config.view_count.max(1),
578 mip_count: 1,
579 })
580 .ok()?;
581 super::XrSurface {
582 raw,
583 frames: Vec::new(),
584 swapchain: super::Swapchain {
585 raw: vk::SwapchainKHR::null(),
586 format,
587 alpha: crate::AlphaMode::Ignored,
588 target_size: [config.size.width as u16, config.size.height as u16],
589 },
590 view_count: config.view_count.max(1),
591 }
592 };
593 self.reconfigure_xr_surface(&mut surface, config);
594 Some(surface)
595 }
596
597 pub fn destroy_xr_surface(&self, surface: &mut super::XrSurface) {
598 for frame in surface.frames.drain(..) {
599 for view in frame.xr_views {
600 if view != vk::ImageView::null() {
601 unsafe { self.device.core.destroy_image_view(view, None) };
602 }
603 }
604 unsafe {
605 self.device.core.destroy_image_view(frame.view, None);
606 self.device
607 .core
608 .destroy_semaphore(frame.acquire_semaphore, None);
609 self.device
610 .core
611 .destroy_semaphore(frame.present_semaphore, None);
612 }
613 }
614 }
615
616 fn reconfigure_xr_surface(
617 &self,
618 surface: &mut super::XrSurface,
619 config: crate::XrSurfaceConfig,
620 ) {
621 self.destroy_xr_surface(surface);
622 let xr = self.xr.as_ref().expect("XR is not enabled in this context");
623 let xr = xr.lock().unwrap();
624 assert!(
625 config.view_count as usize <= super::MAX_XR_EYES,
626 "XR view count {} exceeds MAX_XR_EYES={}",
627 config.view_count,
628 super::MAX_XR_EYES
629 );
630 let (raw_format, format) = select_xr_swapchain_format(&xr.session, config.color_space);
631
632 let new_handle = xr
633 .session
634 .create_swapchain(&xr::SwapchainCreateInfo {
635 create_flags: xr::SwapchainCreateFlags::EMPTY,
636 usage_flags: xr_swapchain_usage(config.usage),
637 format: raw_format,
638 sample_count: 1,
639 width: config.size.width,
640 height: config.size.height,
641 face_count: 1,
642 array_size: config.view_count.max(1),
643 mip_count: 1,
644 })
645 .unwrap();
646 surface.raw = new_handle;
647
648 let target_size = [config.size.width as u16, config.size.height as u16];
649 let view_type = if config.view_count > 1 {
650 vk::ImageViewType::TYPE_2D_ARRAY
651 } else {
652 vk::ImageViewType::TYPE_2D
653 };
654 let subresource_range = vk::ImageSubresourceRange {
655 aspect_mask: vk::ImageAspectFlags::COLOR,
656 base_mip_level: 0,
657 level_count: 1,
658 base_array_layer: 0,
659 layer_count: config.view_count.max(1),
660 };
661
662 for raw_image in surface.raw.enumerate_images().unwrap() {
663 let image = vk::Image::from_raw(raw_image);
664 let view_create_info = vk::ImageViewCreateInfo {
665 image,
666 view_type,
667 format: super::map_texture_format(format),
668 subresource_range,
669 ..Default::default()
670 };
671 let view = unsafe {
672 self.device
673 .core
674 .create_image_view(&view_create_info, None)
675 .unwrap()
676 };
677 let mut xr_views = [vk::ImageView::null(); super::MAX_XR_EYES];
678 for eye in 0..config.view_count.max(1) {
679 let xr_view_info = vk::ImageViewCreateInfo {
680 image,
681 view_type: vk::ImageViewType::TYPE_2D,
682 format: super::map_texture_format(format),
683 subresource_range: vk::ImageSubresourceRange {
684 aspect_mask: vk::ImageAspectFlags::COLOR,
685 base_mip_level: 0,
686 level_count: 1,
687 base_array_layer: eye,
688 layer_count: 1,
689 },
690 ..Default::default()
691 };
692 let xr_view = unsafe {
693 self.device
694 .core
695 .create_image_view(&xr_view_info, None)
696 .unwrap()
697 };
698 xr_views[eye as usize] = xr_view;
699 }
700 let semaphore_create_info = vk::SemaphoreCreateInfo::default();
701 let acquire_semaphore = unsafe {
702 self.device
703 .core
704 .create_semaphore(&semaphore_create_info, None)
705 .unwrap()
706 };
707 let present_semaphore = unsafe {
708 self.device
709 .core
710 .create_semaphore(&semaphore_create_info, None)
711 .unwrap()
712 };
713 surface.frames.push(super::InternalFrame {
714 acquire_semaphore,
715 present_semaphore,
716 image,
717 view,
718 xr_views,
719 });
720 }
721
722 surface.swapchain = super::Swapchain {
723 raw: vk::SwapchainKHR::null(),
724 format,
725 alpha: crate::AlphaMode::Ignored,
726 target_size,
727 };
728 surface.view_count = config.view_count.max(1);
729 }
730}
731
732fn xr_swapchain_usage(usage: crate::TextureUsage) -> xr::SwapchainUsageFlags {
733 let mut out = xr::SwapchainUsageFlags::EMPTY;
734 if usage.contains(crate::TextureUsage::TARGET) {
735 out |= xr::SwapchainUsageFlags::COLOR_ATTACHMENT;
736 }
737 if usage.contains(crate::TextureUsage::RESOURCE) {
738 out |= xr::SwapchainUsageFlags::SAMPLED;
739 }
740 if usage.contains(crate::TextureUsage::STORAGE) {
741 out |= xr::SwapchainUsageFlags::UNORDERED_ACCESS;
742 }
743 if out.is_empty() {
744 out = xr::SwapchainUsageFlags::COLOR_ATTACHMENT;
745 }
746 out
747}
748
749fn texture_format_from_xr_raw(raw: u32) -> Option<crate::TextureFormat> {
750 let format = vk::Format::from_raw(raw as i32);
751 Some(match format {
752 vk::Format::R8G8B8A8_UNORM => crate::TextureFormat::Rgba8Unorm,
753 vk::Format::R8G8B8A8_SRGB => crate::TextureFormat::Rgba8UnormSrgb,
754 vk::Format::B8G8R8A8_UNORM => crate::TextureFormat::Bgra8Unorm,
755 vk::Format::B8G8R8A8_SRGB => crate::TextureFormat::Bgra8UnormSrgb,
756 _ => return None,
757 })
758}
759
760fn select_xr_swapchain_format(
761 session: &xr::Session<xr::Vulkan>,
762 color_space: crate::ColorSpace,
763) -> (u32, crate::TextureFormat) {
764 let formats = session.enumerate_swapchain_formats().unwrap();
765 let mut linear_candidate = None;
766 let mut srgb_candidate = None;
767 for raw in formats {
768 if let Some(format) = texture_format_from_xr_raw(raw) {
769 match format {
770 crate::TextureFormat::Rgba8Unorm | crate::TextureFormat::Bgra8Unorm => {
771 if linear_candidate.is_none() {
772 linear_candidate = Some((raw, format));
773 }
774 }
775 crate::TextureFormat::Rgba8UnormSrgb | crate::TextureFormat::Bgra8UnormSrgb => {
776 if srgb_candidate.is_none() {
777 srgb_candidate = Some((raw, format));
778 }
779 }
780 _ => {}
781 }
782 }
783 }
784 match color_space {
785 crate::ColorSpace::Linear => linear_candidate.or(srgb_candidate),
786 crate::ColorSpace::Srgb => srgb_candidate.or(linear_candidate),
787 }
788 .expect("No compatible XR swapchain format available")
789}