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.inner.entry,
200 &self.inner.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 .inner
210 .instance
211 .surface
212 .as_ref()
213 .ok_or(crate::NotSupportedError::PlatformNotSupported)?;
214 if unsafe {
215 khr_surface.get_physical_device_surface_support(
216 self.physical_device,
217 self.queue_family_index,
218 raw,
219 ) != Ok(true)
220 } {
221 log::warn!("Rejected for not presenting to the window surface");
222 return Err(crate::NotSupportedError::PlatformNotSupported);
223 }
224
225 let mut surface_info = vk::PhysicalDeviceSurfaceInfo2KHR {
226 surface: raw,
227 ..Default::default()
228 };
229 let mut fullscreen_exclusive_win32 = vk::SurfaceFullScreenExclusiveWin32InfoEXT::default();
230 surface_info = surface_info.push_next(&mut fullscreen_exclusive_win32);
231 let mut fullscreen_exclusive_ext = vk::SurfaceCapabilitiesFullScreenExclusiveEXT::default();
232 let mut capabilities2_khr =
233 vk::SurfaceCapabilities2KHR::default().push_next(&mut fullscreen_exclusive_ext);
234 let _ = unsafe {
235 self.inner
236 .instance
237 .get_surface_capabilities2
238 .as_ref()
239 .unwrap()
240 .get_physical_device_surface_capabilities2(
241 self.physical_device,
242 &surface_info,
243 &mut capabilities2_khr,
244 )
245 };
246 log::debug!("{:?}", capabilities2_khr.surface_capabilities);
247
248 let semaphore_create_info = vk::SemaphoreCreateInfo::default();
249 let next_semaphore = unsafe {
250 self.device
251 .core
252 .create_semaphore(&semaphore_create_info, None)
253 .unwrap()
254 };
255
256 Ok(super::Surface {
257 device: khr_swapchain,
258 raw,
259 frames: Vec::new(),
260 next_semaphore,
261 swapchain: super::Swapchain {
262 raw: vk::SwapchainKHR::null(),
263 format: crate::TextureFormat::Rgba8Unorm,
264 alpha: crate::AlphaMode::Ignored,
265 target_size: [0; 2],
266 },
267 full_screen_exclusive: fullscreen_exclusive_ext.full_screen_exclusive_supported != 0,
268 })
269 }
270
271 pub fn destroy_surface(&self, surface: &mut super::Surface) {
272 unsafe {
273 surface.deinit_swapchain(&self.device.core);
274 self.device
275 .core
276 .destroy_semaphore(surface.next_semaphore, None)
277 };
278 if let Some(ref surface_instance) = self.inner.instance.surface {
279 unsafe { surface_instance.destroy_surface(surface.raw, None) };
280 }
281 }
282
283 pub fn reconfigure_surface(&self, surface: &mut super::Surface, config: crate::SurfaceConfig) {
284 let khr_surface = self.inner.instance.surface.as_ref().unwrap();
285
286 let capabilities = unsafe {
287 khr_surface
288 .get_physical_device_surface_capabilities(self.physical_device, surface.raw)
289 .unwrap()
290 };
291 if config.size.width < capabilities.min_image_extent.width
292 || config.size.width > capabilities.max_image_extent.width
293 || config.size.height < capabilities.min_image_extent.height
294 || config.size.height > capabilities.max_image_extent.height
295 {
296 log::warn!(
297 "Requested size {}x{} is outside of surface capabilities",
298 config.size.width,
299 config.size.height
300 );
301 }
302
303 let (alpha, composite_alpha) = if config.transparent {
304 if capabilities
305 .supported_composite_alpha
306 .contains(vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED)
307 {
308 (
309 crate::AlphaMode::PostMultiplied,
310 vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED,
311 )
312 } else if capabilities
313 .supported_composite_alpha
314 .contains(vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED)
315 {
316 (
317 crate::AlphaMode::PreMultiplied,
318 vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
319 )
320 } else {
321 log::error!(
322 "No composite alpha flag for transparency: {:?}",
323 capabilities.supported_composite_alpha
324 );
325 (
326 crate::AlphaMode::Ignored,
327 vk::CompositeAlphaFlagsKHR::OPAQUE,
328 )
329 }
330 } else {
331 (
332 crate::AlphaMode::Ignored,
333 vk::CompositeAlphaFlagsKHR::OPAQUE,
334 )
335 };
336
337 let (requested_frame_count, mode_preferences) = match config.display_sync {
338 crate::DisplaySync::Block => (3, [vk::PresentModeKHR::FIFO].as_slice()),
339 crate::DisplaySync::Recent => (
340 3,
341 [
342 vk::PresentModeKHR::MAILBOX,
343 vk::PresentModeKHR::FIFO_RELAXED,
344 vk::PresentModeKHR::IMMEDIATE,
345 ]
346 .as_slice(),
347 ),
348 crate::DisplaySync::Tear => (2, [vk::PresentModeKHR::IMMEDIATE].as_slice()),
349 };
350 let effective_frame_count = requested_frame_count.max(capabilities.min_image_count);
351
352 let present_modes = unsafe {
353 khr_surface
354 .get_physical_device_surface_present_modes(self.physical_device, surface.raw)
355 .unwrap()
356 };
357 let present_mode = *mode_preferences
358 .iter()
359 .find(|mode| present_modes.contains(mode))
360 .unwrap();
361 log::info!("Using surface present mode {:?}", present_mode);
362
363 let queue_families = [self.queue_family_index];
364
365 let mut supported_formats = Vec::new();
366 let (format, surface_format) = if surface.swapchain.target_size[0] > 0 {
367 let format = surface.swapchain.format;
368 log::info!("Retaining current format: {:?}", format);
369 let vk_color_space = match (format, config.color_space) {
370 (crate::TextureFormat::Bgra8Unorm, crate::ColorSpace::Srgb) => {
371 vk::ColorSpaceKHR::SRGB_NONLINEAR
372 }
373 (crate::TextureFormat::Bgra8Unorm, crate::ColorSpace::Linear) => {
374 vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT
375 }
376 (crate::TextureFormat::Bgra8UnormSrgb, crate::ColorSpace::Linear) => {
377 vk::ColorSpaceKHR::default()
378 }
379 _ => panic!(
380 "Unexpected format {:?} under color space {:?}",
381 format, config.color_space
382 ),
383 };
384 (
385 format,
386 vk::SurfaceFormatKHR {
387 format: super::map_texture_format(format),
388 color_space: vk_color_space,
389 },
390 )
391 } else {
392 supported_formats = unsafe {
393 khr_surface
394 .get_physical_device_surface_formats(self.physical_device, surface.raw)
395 .unwrap()
396 };
397 match config.color_space {
398 crate::ColorSpace::Linear => {
399 let surface_format = vk::SurfaceFormatKHR {
400 format: vk::Format::B8G8R8A8_UNORM,
401 color_space: vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT,
402 };
403 if supported_formats.contains(&surface_format) {
404 log::info!("Using linear SRGB color space");
405 (crate::TextureFormat::Bgra8Unorm, surface_format)
406 } else {
407 (
408 crate::TextureFormat::Bgra8UnormSrgb,
409 vk::SurfaceFormatKHR {
410 format: vk::Format::B8G8R8A8_SRGB,
411 color_space: vk::ColorSpaceKHR::default(),
412 },
413 )
414 }
415 }
416 crate::ColorSpace::Srgb => (
417 crate::TextureFormat::Bgra8Unorm,
418 vk::SurfaceFormatKHR {
419 format: vk::Format::B8G8R8A8_UNORM,
420 color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR,
421 },
422 ),
423 }
424 };
425 if !supported_formats.is_empty() && !supported_formats.contains(&surface_format) {
426 log::error!("Surface formats are incompatible: {:?}", supported_formats);
427 }
428
429 let vk_usage = super::resource::map_texture_usage(config.usage, crate::TexelAspects::COLOR);
430 if !capabilities.supported_usage_flags.contains(vk_usage) {
431 log::error!(
432 "Surface usages are incompatible: {:?}",
433 capabilities.supported_usage_flags
434 );
435 }
436
437 let mut full_screen_exclusive_info = vk::SurfaceFullScreenExclusiveInfoEXT {
438 full_screen_exclusive: if config.allow_exclusive_full_screen {
439 vk::FullScreenExclusiveEXT::ALLOWED
440 } else {
441 vk::FullScreenExclusiveEXT::DISALLOWED
442 },
443 ..Default::default()
444 };
445
446 let mut create_info = vk::SwapchainCreateInfoKHR {
447 surface: surface.raw,
448 min_image_count: effective_frame_count,
449 image_format: surface_format.format,
450 image_color_space: surface_format.color_space,
451 image_extent: vk::Extent2D {
452 width: config.size.width,
453 height: config.size.height,
454 },
455 image_array_layers: 1,
456 image_usage: vk_usage,
457 pre_transform: vk::SurfaceTransformFlagsKHR::IDENTITY,
458 composite_alpha,
459 present_mode,
460 old_swapchain: surface.swapchain.raw,
461 ..Default::default()
462 }
463 .queue_family_indices(&queue_families);
464
465 if surface.full_screen_exclusive {
466 assert!(self.device.full_screen_exclusive.is_some());
467 create_info = create_info.push_next(&mut full_screen_exclusive_info);
468 log::info!(
469 "Configuring exclusive full screen: {}",
470 config.allow_exclusive_full_screen
471 );
472 }
473 let raw_swapchain = unsafe { surface.device.create_swapchain(&create_info, None).unwrap() };
474
475 unsafe {
476 surface.deinit_swapchain(&self.device.core);
477 }
478
479 let images = unsafe { surface.device.get_swapchain_images(raw_swapchain).unwrap() };
480 let target_size = [config.size.width as u16, config.size.height as u16];
481 let subresource_range = vk::ImageSubresourceRange {
482 aspect_mask: vk::ImageAspectFlags::COLOR,
483 base_mip_level: 0,
484 level_count: 1,
485 base_array_layer: 0,
486 layer_count: 1,
487 };
488 for image in images {
489 let view_create_info = vk::ImageViewCreateInfo {
490 image,
491 view_type: vk::ImageViewType::TYPE_2D,
492 format: surface_format.format,
493 subresource_range,
494 ..Default::default()
495 };
496 let view = unsafe {
497 self.device
498 .core
499 .create_image_view(&view_create_info, None)
500 .unwrap()
501 };
502 let semaphore_create_info = vk::SemaphoreCreateInfo::default();
503 let acquire_semaphore = unsafe {
504 self.device
505 .core
506 .create_semaphore(&semaphore_create_info, None)
507 .unwrap()
508 };
509 let present_semaphore = unsafe {
510 self.device
511 .core
512 .create_semaphore(&semaphore_create_info, None)
513 .unwrap()
514 };
515 surface.frames.push(super::InternalFrame {
516 acquire_semaphore,
517 present_semaphore,
518 image,
519 view,
520 xr_views: [vk::ImageView::null(); super::MAX_XR_EYES],
521 });
522 }
523 surface.swapchain = super::Swapchain {
524 raw: raw_swapchain,
525 format,
526 alpha,
527 target_size,
528 };
529 }
530
531 fn xr_recommended_surface_config(
532 &self,
533 view_type: xr::ViewConfigurationType,
534 ) -> Option<crate::XrSurfaceConfig> {
535 let xr = self.xr.as_ref()?;
536 let xr = xr.lock().unwrap();
537 let views = xr
538 .instance
539 .enumerate_view_configuration_views(xr.system_id, view_type)
540 .ok()?;
541 let first = *views.first()?;
542 let view_count = (views.len() as u32).min(super::MAX_XR_EYES as u32);
543 Some(crate::XrSurfaceConfig {
544 size: crate::Extent {
545 width: first.recommended_image_rect_width,
546 height: first.recommended_image_rect_height,
547 depth: 1,
548 },
549 usage: crate::TextureUsage::TARGET,
550 color_space: crate::ColorSpace::Linear,
551 view_count,
552 })
553 }
554
555 pub fn create_xr_surface(&self) -> Option<super::XrSurface> {
556 let config =
557 self.xr_recommended_surface_config(xr::ViewConfigurationType::PRIMARY_STEREO)?;
558 self.create_xr_surface_configured(config)
559 }
560
561 fn create_xr_surface_configured(
562 &self,
563 config: crate::XrSurfaceConfig,
564 ) -> Option<super::XrSurface> {
565 let xr = self.xr.as_ref()?;
566 let mut surface = {
567 let xr = xr.lock().unwrap();
568 let (raw_format, format) = select_xr_swapchain_format(&xr.session, config.color_space);
569 let raw = xr
570 .session
571 .create_swapchain(&xr::SwapchainCreateInfo {
572 create_flags: xr::SwapchainCreateFlags::EMPTY,
573 usage_flags: xr_swapchain_usage(config.usage),
574 format: raw_format,
575 sample_count: 1,
576 width: config.size.width,
577 height: config.size.height,
578 face_count: 1,
579 array_size: config.view_count.max(1),
580 mip_count: 1,
581 })
582 .ok()?;
583 super::XrSurface {
584 raw,
585 frames: Vec::new(),
586 swapchain: super::Swapchain {
587 raw: vk::SwapchainKHR::null(),
588 format,
589 alpha: crate::AlphaMode::Ignored,
590 target_size: [config.size.width as u16, config.size.height as u16],
591 },
592 view_count: config.view_count.max(1),
593 }
594 };
595 self.reconfigure_xr_surface(&mut surface, config);
596 Some(surface)
597 }
598
599 pub fn destroy_xr_surface(&self, surface: &mut super::XrSurface) {
600 for frame in surface.frames.drain(..) {
601 for view in frame.xr_views {
602 if view != vk::ImageView::null() {
603 unsafe { self.device.core.destroy_image_view(view, None) };
604 }
605 }
606 unsafe {
607 self.device.core.destroy_image_view(frame.view, None);
608 self.device
609 .core
610 .destroy_semaphore(frame.acquire_semaphore, None);
611 self.device
612 .core
613 .destroy_semaphore(frame.present_semaphore, None);
614 }
615 }
616 }
617
618 fn reconfigure_xr_surface(
619 &self,
620 surface: &mut super::XrSurface,
621 config: crate::XrSurfaceConfig,
622 ) {
623 self.destroy_xr_surface(surface);
624 let xr = self.xr.as_ref().expect("XR is not enabled in this context");
625 let xr = xr.lock().unwrap();
626 assert!(
627 config.view_count as usize <= super::MAX_XR_EYES,
628 "XR view count {} exceeds MAX_XR_EYES={}",
629 config.view_count,
630 super::MAX_XR_EYES
631 );
632 let (raw_format, format) = select_xr_swapchain_format(&xr.session, config.color_space);
633
634 let new_handle = xr
635 .session
636 .create_swapchain(&xr::SwapchainCreateInfo {
637 create_flags: xr::SwapchainCreateFlags::EMPTY,
638 usage_flags: xr_swapchain_usage(config.usage),
639 format: raw_format,
640 sample_count: 1,
641 width: config.size.width,
642 height: config.size.height,
643 face_count: 1,
644 array_size: config.view_count.max(1),
645 mip_count: 1,
646 })
647 .unwrap();
648 surface.raw = new_handle;
649
650 let target_size = [config.size.width as u16, config.size.height as u16];
651 let view_type = if config.view_count > 1 {
652 vk::ImageViewType::TYPE_2D_ARRAY
653 } else {
654 vk::ImageViewType::TYPE_2D
655 };
656 let subresource_range = vk::ImageSubresourceRange {
657 aspect_mask: vk::ImageAspectFlags::COLOR,
658 base_mip_level: 0,
659 level_count: 1,
660 base_array_layer: 0,
661 layer_count: config.view_count.max(1),
662 };
663
664 for raw_image in surface.raw.enumerate_images().unwrap() {
665 let image = vk::Image::from_raw(raw_image);
666 let view_create_info = vk::ImageViewCreateInfo {
667 image,
668 view_type,
669 format: super::map_texture_format(format),
670 subresource_range,
671 ..Default::default()
672 };
673 let view = unsafe {
674 self.device
675 .core
676 .create_image_view(&view_create_info, None)
677 .unwrap()
678 };
679 let mut xr_views = [vk::ImageView::null(); super::MAX_XR_EYES];
680 for eye in 0..config.view_count.max(1) {
681 let xr_view_info = vk::ImageViewCreateInfo {
682 image,
683 view_type: vk::ImageViewType::TYPE_2D,
684 format: super::map_texture_format(format),
685 subresource_range: vk::ImageSubresourceRange {
686 aspect_mask: vk::ImageAspectFlags::COLOR,
687 base_mip_level: 0,
688 level_count: 1,
689 base_array_layer: eye,
690 layer_count: 1,
691 },
692 ..Default::default()
693 };
694 let xr_view = unsafe {
695 self.device
696 .core
697 .create_image_view(&xr_view_info, None)
698 .unwrap()
699 };
700 xr_views[eye as usize] = xr_view;
701 }
702 let semaphore_create_info = vk::SemaphoreCreateInfo::default();
703 let acquire_semaphore = unsafe {
704 self.device
705 .core
706 .create_semaphore(&semaphore_create_info, None)
707 .unwrap()
708 };
709 let present_semaphore = unsafe {
710 self.device
711 .core
712 .create_semaphore(&semaphore_create_info, None)
713 .unwrap()
714 };
715 surface.frames.push(super::InternalFrame {
716 acquire_semaphore,
717 present_semaphore,
718 image,
719 view,
720 xr_views,
721 });
722 }
723
724 surface.swapchain = super::Swapchain {
725 raw: vk::SwapchainKHR::null(),
726 format,
727 alpha: crate::AlphaMode::Ignored,
728 target_size,
729 };
730 surface.view_count = config.view_count.max(1);
731 }
732}
733
734fn xr_swapchain_usage(usage: crate::TextureUsage) -> xr::SwapchainUsageFlags {
735 let mut out = xr::SwapchainUsageFlags::EMPTY;
736 if usage.contains(crate::TextureUsage::TARGET) {
737 out |= xr::SwapchainUsageFlags::COLOR_ATTACHMENT;
738 }
739 if usage.contains(crate::TextureUsage::RESOURCE) {
740 out |= xr::SwapchainUsageFlags::SAMPLED;
741 }
742 if usage.contains(crate::TextureUsage::STORAGE) {
743 out |= xr::SwapchainUsageFlags::UNORDERED_ACCESS;
744 }
745 if out.is_empty() {
746 out = xr::SwapchainUsageFlags::COLOR_ATTACHMENT;
747 }
748 out
749}
750
751fn texture_format_from_xr_raw(raw: u32) -> Option<crate::TextureFormat> {
752 let format = vk::Format::from_raw(raw as i32);
753 Some(match format {
754 vk::Format::R8G8B8A8_UNORM => crate::TextureFormat::Rgba8Unorm,
755 vk::Format::R8G8B8A8_SRGB => crate::TextureFormat::Rgba8UnormSrgb,
756 vk::Format::B8G8R8A8_UNORM => crate::TextureFormat::Bgra8Unorm,
757 vk::Format::B8G8R8A8_SRGB => crate::TextureFormat::Bgra8UnormSrgb,
758 _ => return None,
759 })
760}
761
762fn select_xr_swapchain_format(
763 session: &xr::Session<xr::Vulkan>,
764 color_space: crate::ColorSpace,
765) -> (u32, crate::TextureFormat) {
766 let formats = session.enumerate_swapchain_formats().unwrap();
767 let mut linear_candidate = None;
768 let mut srgb_candidate = None;
769 for raw in formats {
770 if let Some(format) = texture_format_from_xr_raw(raw) {
771 match format {
772 crate::TextureFormat::Rgba8Unorm | crate::TextureFormat::Bgra8Unorm => {
773 if linear_candidate.is_none() {
774 linear_candidate = Some((raw, format));
775 }
776 }
777 crate::TextureFormat::Rgba8UnormSrgb | crate::TextureFormat::Bgra8UnormSrgb => {
778 if srgb_candidate.is_none() {
779 srgb_candidate = Some((raw, format));
780 }
781 }
782 _ => {}
783 }
784 }
785 }
786 match color_space {
787 crate::ColorSpace::Linear => linear_candidate.or(srgb_candidate),
788 crate::ColorSpace::Srgb => srgb_candidate.or(linear_candidate),
789 }
790 .expect("No compatible XR swapchain format available")
791}