use crate::{
maths::{Pose, Quat, Rect, Vec2},
prelude::*,
system::{Backend, BackendGraphics, BackendOpenXR, BackendXRType},
tex::{Tex, TexFormat, TexType},
};
#[cfg(target_os = "android")]
use openxr_sys::{pfn::CreateSwapchainAndroidSurfaceKHR, platform::jobject};
use openxr_sys::{
Duration, Session, Space, Swapchain, SwapchainImageWaitInfo,
pfn::{
AcquireSwapchainImage, CreateSwapchain, DestroySwapchain, EnumerateSwapchainImages, ReleaseSwapchainImage,
WaitSwapchainImage,
},
};
use openxr_sys::{
CompositionLayerFlags, CompositionLayerQuad, Extent2Df, Extent2Di, EyeVisibility, Offset2Di, Posef, Quaternionf,
Rect2Di, Result as XrResult, StructureType, SwapchainCreateFlags, SwapchainCreateInfo, SwapchainSubImage,
SwapchainUsageFlags, Vector3f,
};
use std::ptr::null_mut;
#[derive(Debug)]
pub struct XrCompLayers {
#[cfg(target_os = "android")]
xr_create_swapchain_android: Option<CreateSwapchainAndroidSurfaceKHR>,
xr_create_swapchain: Option<CreateSwapchain>,
xr_destroy_swapchain: Option<DestroySwapchain>,
xr_enumerate_swaptchain_images: Option<EnumerateSwapchainImages>,
xr_acquire_swapchain_image: Option<AcquireSwapchainImage>,
xr_wait_swaptchain_image: Option<WaitSwapchainImage>,
xr_release_swaptchain_image: Option<ReleaseSwapchainImage>,
}
impl Default for XrCompLayers {
fn default() -> Self {
Self {
#[cfg(target_os = "android")]
xr_create_swapchain_android: BackendOpenXR::get_function::<CreateSwapchainAndroidSurfaceKHR>(
"xrCreateSwapchainAndroidSurfaceKHR",
),
xr_create_swapchain: BackendOpenXR::get_function::<CreateSwapchain>("xrCreateSwapchain"),
xr_destroy_swapchain: BackendOpenXR::get_function::<DestroySwapchain>("xrDestroySwapchain"),
xr_enumerate_swaptchain_images: BackendOpenXR::get_function::<EnumerateSwapchainImages>(
"xrEnumerateSwapchainImages",
),
xr_acquire_swapchain_image: BackendOpenXR::get_function::<AcquireSwapchainImage>("xrAcquireSwapchainImage"),
xr_wait_swaptchain_image: BackendOpenXR::get_function::<WaitSwapchainImage>("xrWaitSwapchainImage"),
xr_release_swaptchain_image: BackendOpenXR::get_function::<ReleaseSwapchainImage>(
"xrReleaseSwapchainImage",
),
}
}
}
impl XrCompLayers {
pub fn new() -> Option<Self> {
let this = Self::default();
#[cfg(target_os = "android")]
{
if !BackendOpenXR::ext_enabled("XR_KHR_android_surface_swapchain") {
Log::warn(
"XrCompLayers: XR_KHR_android_surface_swapchain extension is not enabled. You have to enable it before sk::init.",
);
return None;
}
}
if !(Backend::xr_type() == BackendXRType::OpenXR && this.load_bindings()) {
Log::warn(format!("❌ XrCompLayers: some function bindings are missing : {this:?}"));
return None;
}
Some(this)
}
fn load_bindings(&self) -> bool {
let mut swapchain_func_present = true;
swapchain_func_present &= self.xr_create_swapchain.is_some();
#[cfg(target_os = "android")]
{
swapchain_func_present &= self.xr_create_swapchain_android.is_some();
}
swapchain_func_present
&& self.xr_destroy_swapchain.is_some()
&& self.xr_enumerate_swaptchain_images.is_some()
&& self.xr_acquire_swapchain_image.is_some()
&& self.xr_wait_swaptchain_image.is_some()
&& self.xr_release_swaptchain_image.is_some()
}
pub fn to_native_format(format: TexFormat) -> i64 {
match Backend::graphics() {
BackendGraphics::D3D11 => match format {
TexFormat::RGBA32 => 29,
TexFormat::RGBA32Linear => 28,
TexFormat::BGRA32 => 91,
TexFormat::BGRA32Linear => 87,
TexFormat::RGB10A2 => 24,
TexFormat::RG11B10 => 26,
_ => panic!("Unsupported texture format"),
},
BackendGraphics::OpenGLESEGL => match format {
TexFormat::RGBA32 => 0x8C43,
TexFormat::RGBA32Linear => 0x8058,
TexFormat::RGB10A2 => 0x8059,
TexFormat::RG11B10 => 0x8C3A,
_ => panic!("Unsupported texture format"),
},
_ => panic!("Unsupported graphics backend"),
}
}
#[allow(clippy::too_many_arguments)]
pub fn submit_quad_layer(
world_pose: Pose,
size: Vec2,
swapchain: Swapchain,
swapchain_rect: Rect,
swapchain_array_index: u32,
composition_sort_order: i32,
visibility: Option<EyeVisibility>,
xr_space: Option<u64>,
) {
let orientation = (world_pose.orientation * Quat::from_angles(180.0, 0.0, 0.0)).conjugate();
let xr_space = xr_space.unwrap_or_else(BackendOpenXR::space);
let mut quad_layer = CompositionLayerQuad {
ty: StructureType::COMPOSITION_LAYER_QUAD,
next: null_mut(),
layer_flags: CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA,
space: Space::from_raw(xr_space),
eye_visibility: visibility.unwrap_or(EyeVisibility::BOTH),
sub_image: SwapchainSubImage {
swapchain,
image_rect: Rect2Di {
offset: Offset2Di { x: swapchain_rect.x as i32, y: swapchain_rect.y as i32 },
extent: Extent2Di { width: swapchain_rect.width as i32, height: swapchain_rect.height as i32 },
},
image_array_index: swapchain_array_index,
},
pose: Posef {
orientation: Quaternionf { x: orientation.x, y: orientation.y, z: orientation.z, w: orientation.w },
position: Vector3f { x: world_pose.position.x, y: world_pose.position.y, z: world_pose.position.z },
},
size: Extent2Df { width: size.x, height: size.y },
};
BackendOpenXR::add_composition_layer(&mut quad_layer, composition_sort_order);
}
#[cfg(target_os = "android")]
pub fn try_make_android_swapchain(
&self,
width: u32,
height: u32,
usage: SwapchainUsageFlags,
single_image: bool,
) -> Option<(Swapchain, *mut jobject)> {
use openxr_sys::platform::jobject;
let mut swapchain = Swapchain::default();
let mut surface: *mut jobject = null_mut();
let create_flags = if single_image {
SwapchainCreateFlags::STATIC_IMAGE
} else {
SwapchainCreateFlags::PROTECTED_CONTENT
};
let info = SwapchainCreateInfo {
ty: StructureType::SWAPCHAIN_CREATE_INFO,
next: null_mut(),
create_flags: create_flags,
usage_flags: usage,
format: 0, sample_count: 0, width,
height,
face_count: 0, array_size: 0, mip_count: 0, };
if let Some(func) = self.xr_create_swapchain_android {
let res = unsafe {
func(
Session::from_raw(BackendOpenXR::session()),
&info,
&mut swapchain,
&mut surface as *mut _ as *mut *mut std::ffi::c_void,
)
};
match res {
XrResult::SUCCESS => Some((swapchain, surface)),
otherwise => {
Log::err(format!("❌ xrDestroySwapchain failed: {otherwise}"));
None
}
}
} else {
None
}
}
#[cfg(target_os = "android")]
pub fn destroy_android_swapchain(&self, handle: Swapchain) {
match unsafe { self.xr_destroy_swapchain.unwrap()(handle) } {
XrResult::SUCCESS => {}
otherwise => {
Log::err(format!("❌ xrDestroySwapchain failed: {otherwise}"));
}
}
}
pub fn try_make_swapchain(
&self,
width: u32,
height: u32,
format: TexFormat,
usage: SwapchainUsageFlags,
single_image: bool,
) -> Option<Swapchain> {
let mut swapchain = Swapchain::default();
let create_flags = if single_image { SwapchainCreateFlags::STATIC_IMAGE } else { SwapchainCreateFlags::EMPTY };
let info = SwapchainCreateInfo {
ty: StructureType::SWAPCHAIN_CREATE_INFO,
next: null_mut(),
format: Self::to_native_format(format),
create_flags,
usage_flags: usage,
sample_count: 1,
width,
height,
face_count: 1,
array_size: 1,
mip_count: 1,
};
match unsafe {
self.xr_create_swapchain.unwrap()(Session::from_raw(BackendOpenXR::session()), &info, &mut swapchain)
} {
XrResult::SUCCESS => {}
otherwise => {
Log::err(format!("❌ xrCreateSwapchain failed: {otherwise}"));
return None;
}
}
Some(swapchain)
}
pub fn destroy_swapchain(swapchain: Swapchain) {
let xr_destroy_swapchain = BackendOpenXR::get_function::<DestroySwapchain>("xrDestroySwapchain");
if let Some(func) = xr_destroy_swapchain {
unsafe {
func(swapchain);
}
}
}
}
pub struct SwapchainSk {
pub xr_comp_layers: XrCompLayers,
pub handle: Swapchain,
pub width: u32,
pub height: u32,
pub acquired: u32,
images: Vec<Tex>,
#[cfg(unix)]
gles_images: Vec<openxr_sys::SwapchainImageOpenGLESKHR>,
#[cfg(windows)]
d3d_images: Vec<openxr_sys::SwapchainImageD3D11KHR>,
}
impl SwapchainSk {
pub fn new(format: TexFormat, width: u32, height: u32, xr_comp_layers: Option<XrCompLayers>) -> Option<Self> {
let xr_comp_layers = get_xr_comp_layers(xr_comp_layers)?;
if Backend::xr_type() == BackendXRType::OpenXR {
if let Some(handle) =
xr_comp_layers.try_make_swapchain(width, height, format, SwapchainUsageFlags::COLOR_ATTACHMENT, false)
{
SwapchainSk::wrap(handle, format, width, height, Some(xr_comp_layers))
} else {
Log::warn("❌ Failed to create XR swapchain: Try_make_swapchain failed");
None
}
} else {
Log::warn("Swapchain: OpenXR backend is not available");
None
}
}
pub fn get_render_target(&self) -> Option<&Tex> {
if self.images.is_empty() {
return None;
}
Some(&self.images[self.acquired as usize])
}
#[cfg(unix)]
pub fn wrap(
handle: Swapchain,
format: TexFormat,
width: u32,
height: u32,
xr_comp_layers: Option<XrCompLayers>,
) -> Option<Self> {
use openxr_sys::SwapchainImageOpenGLESKHR;
let xr_comp_layers = get_xr_comp_layers(xr_comp_layers)?;
let mut image_count = 0;
match unsafe { xr_comp_layers.xr_enumerate_swaptchain_images.unwrap()(handle, 0, &mut image_count, null_mut()) }
{
XrResult::SUCCESS => {}
otherwise => {
Log::err(format!("❌ xrEnumerateSwapchainImages failed: {otherwise}"));
return None;
}
}
if Backend::graphics() == BackendGraphics::OpenGLESEGL {
let mut gles_images: Vec<SwapchainImageOpenGLESKHR> = {
let images: Vec<SwapchainImageOpenGLESKHR> = vec![
SwapchainImageOpenGLESKHR {
image: 0,
ty: StructureType::SWAPCHAIN_IMAGE_OPENGL_ES_KHR,
next: null_mut()
};
image_count as usize
];
images
};
let mut final_count = 0;
match unsafe {
xr_comp_layers.xr_enumerate_swaptchain_images.unwrap()(
handle,
image_count,
&mut final_count,
gles_images.as_mut_ptr() as *mut _,
)
} {
XrResult::SUCCESS => {}
otherwise => {
Log::err(format!("❌ xrEnumerateSwapchainImages failed: {otherwise}"));
return None;
}
}
assert_eq!(gles_images.len(), image_count as usize);
let mut this =
Self { xr_comp_layers, handle, width, height, acquired: 0, gles_images, images: Vec::with_capacity(0) };
for image in &this.gles_images {
Log::diag(format!("SwapchainSk: image: {image:#?}"));
let mut image_sk =
Tex::render_target(width as usize, height as usize, Some(2), Some(format), None).unwrap();
unsafe {
image_sk.set_native_surface(
image.image as *mut std::ffi::c_void,
TexType::Rendertarget,
XrCompLayers::to_native_format(format),
width as i32,
height as i32,
1,
true,
)
};
this.images.push(image_sk);
}
Some(this)
} else {
Log::warn("❌ SwapchainSk: OpenGL ES backend is not available");
None
}
}
#[cfg(windows)]
pub fn wrap(
handle: Swapchain,
format: TexFormat,
width: u32,
height: u32,
xr_comp_layers: Option<XrCompLayers>,
) -> Option<Self> {
use openxr_sys::SwapchainImageD3D11KHR;
use std::ptr::null_mut;
let xr_comp_layers = get_xr_comp_layers(xr_comp_layers)?;
let mut image_count = 0;
match unsafe { xr_comp_layers.xr_enumerate_swaptchain_images.unwrap()(handle, 0, &mut image_count, null_mut()) }
{
XrResult::SUCCESS => {}
err => {
Log::err(format!("❌ xrEnumerateSwapchainImages failed: {err}"));
return None;
}
}
if Backend::graphics() == BackendGraphics::D3D11 {
let mut d3d_images: Vec<SwapchainImageD3D11KHR> = vec![
SwapchainImageD3D11KHR {
texture: null_mut(),
ty: StructureType::SWAPCHAIN_IMAGE_D3D11_KHR,
next: null_mut(),
};
image_count as usize
];
let mut final_count = 0;
match unsafe {
xr_comp_layers.xr_enumerate_swaptchain_images.unwrap()(
handle,
image_count,
&mut final_count,
d3d_images.as_mut_ptr() as *mut _,
)
} {
XrResult::SUCCESS => {}
err => {
Log::err(format!("❌ xrEnumerateSwapchainImages failed: {err}"));
return None;
}
}
let mut this =
Self { xr_comp_layers, handle, width, height, acquired: 0, d3d_images, images: Vec::with_capacity(0) };
for img in &this.d3d_images {
Log::diag(format!("SwapchainSk: image: {:#?}", img));
let mut image_sk =
Tex::render_target(width as usize, height as usize, Some(1), Some(format), None).unwrap();
unsafe {
image_sk.set_native_surface(
img.texture,
TexType::Rendertarget,
XrCompLayers::to_native_format(format),
width as i32,
height as i32,
1,
true,
);
}
this.images.push(image_sk);
}
Some(this)
} else {
Log::warn("❌ SwapchainSk: D3D11 backend is not available");
None
}
}
pub fn acquire_image(&mut self, timeout_ns: Option<i64>) -> std::result::Result<u32, XrResult> {
let timeout_ns = timeout_ns.unwrap_or(0x7fffffffffffffff);
let timeout = Duration::from_nanos(timeout_ns);
match unsafe {
self.xr_comp_layers.xr_acquire_swapchain_image.unwrap()(self.handle, null_mut(), &mut self.acquired)
} {
XrResult::SUCCESS => {}
otherwise => return Err(otherwise),
}
let swapchain_image_wait_info =
SwapchainImageWaitInfo { ty: StructureType::SWAPCHAIN_IMAGE_WAIT_INFO, next: null_mut(), timeout };
match unsafe { self.xr_comp_layers.xr_wait_swaptchain_image.unwrap()(self.handle, &swapchain_image_wait_info) }
{
XrResult::SUCCESS => Ok(self.acquired),
otherwise => Err(otherwise),
}
}
pub fn release_image(&mut self) -> std::result::Result<(), XrResult> {
match unsafe { self.xr_comp_layers.xr_release_swaptchain_image.unwrap()(self.handle, null_mut()) } {
XrResult::SUCCESS => Ok(()),
otherwise => Err(otherwise),
}
}
pub fn destroy(&mut self) {
XrCompLayers::destroy_swapchain(self.handle);
self.handle = Swapchain::default();
}
}
pub fn get_xr_comp_layers(xr_comp_layers: Option<XrCompLayers>) -> Option<XrCompLayers> {
if let Some(comp_layers) = xr_comp_layers { Some(comp_layers) } else { XrCompLayers::new() }
}