Skip to content

Commit

Permalink
feat: ui sprites + openxr skybox
Browse files Browse the repository at this point in the history
  • Loading branch information
galister committed Jul 24, 2024
1 parent 17addcd commit 7a6040b
Show file tree
Hide file tree
Showing 30 changed files with 1,926 additions and 1,063 deletions.
385 changes: 162 additions & 223 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ libc = "0.2.155"
libloading = "0.8.3"
log = "0.4.21"
once_cell = "1.19.0"
openxr = { version = "0.17.1", features = ["linked"], optional = true }
openxr = { git = "https://github.com/galister/openxrs", rev = "af4a55d", features = [
"linked",
], optional = true }
ovr_overlay = { features = [
"ovr_input",
"ovr_system",
Expand Down Expand Up @@ -62,6 +64,9 @@ xkbcommon = { version = "0.7.0" }
xcb = { version = "1.4.0", optional = true, features = [
"as-raw-xcb-connection",
] }
image_dds = { version = "0.5.1", default-features = false, features = [
"ddsfile",
] }

[features]
default = ["openvr", "openxr", "osc", "x11", "wayland"]
Expand Down
2 changes: 1 addition & 1 deletion src/backend/openvr/lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl LinePool {

let buf = vec![255; 16];

let texture = command_buffer.texture2d(2, 2, Format::R8G8B8A8_UNORM, &buf)?;
let texture = command_buffer.texture2d_raw(2, 2, Format::R8G8B8A8_UNORM, &buf)?;
command_buffer.build_and_execute_now()?;

graphics
Expand Down
5 changes: 5 additions & 0 deletions src/backend/openxr/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ pub(super) fn init_xr() -> Result<(xr::Instance, xr::SystemId), anyhow::Error> {
} else {
log::warn!("Missing EXT_hp_mixed_reality_controller extension.");
}
if available_extensions.khr_composition_layer_equirect2 {
enabled_extensions.khr_composition_layer_equirect2 = true;
} else {
log::warn!("Missing EXT_composition_layer_equirect2 extension.");
}

//#[cfg(not(debug_assertions))]
let layers = [];
Expand Down
149 changes: 149 additions & 0 deletions src/backend/openxr/libmonado.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
use std::{env, ffi::c_void, fs};

use anyhow::bail;
use libloading::{Library, Symbol};
use serde::Deserialize;

#[repr(C)]
#[derive(Default, Debug)]
struct MndPose {
orientation: [f32; 4],
position: [f32; 3],
}

const MND_REFERENCE_TYPE_STAGE: i32 = 3;

const MND_SUCCESS: i32 = 0;
const MND_ERROR_BAD_SPACE_TYPE: i32 = -7;

type GetDeviceCount = extern "C" fn(*mut c_void, *mut u32) -> i32;
type GetDeviceInfo = extern "C" fn(*mut c_void, u32, *mut u32, *mut *const char) -> i32;
type GetDeviceFromRole = extern "C" fn(*mut c_void, *const std::os::raw::c_char, *mut i32) -> i32;
type GetDeviceBatteryStatus =
extern "C" fn(*mut c_void, u32, *mut bool, *mut bool, *mut f32) -> i32;

type PlaySpaceMove = extern "C" fn(*mut c_void, f32, f32, f32) -> i32;
type ApplyStageOffset = extern "C" fn(*mut c_void, *const MndPose) -> i32;

// New implementation
type GetReferenceSpaceOffset = extern "C" fn(*mut c_void, i32, *mut MndPose) -> i32;
type SetReferenceSpaceOffset = extern "C" fn(*mut c_void, i32, *const MndPose) -> i32;

// TODO: Clean up after merge into upstream Monado
enum MoverImpl {
None,
PlaySpaceMove(PlaySpaceMove),
ApplyStageOffset(ApplyStageOffset),
SpaceOffsetApi {
get_reference: GetReferenceSpaceOffset,
set_reference: SetReferenceSpaceOffset,
},
}

pub struct LibMonado {
libmonado: Library,
mnd_root: *mut c_void,
mover: MoverImpl,
}

impl Drop for LibMonado {
fn drop(&mut self) {
unsafe {
type RootDestroy = extern "C" fn(*mut *mut c_void) -> i32;
let Ok(root_destroy) = self.libmonado.get::<RootDestroy>(b"mnd_root_destroy\0") else {
return;
};
root_destroy(&mut self.mnd_root);
}
}
}

impl LibMonado {
pub fn new() -> anyhow::Result<Self> {
let lib_path = if let Ok(path) = env::var("LIBMONADO_PATH") {
path
} else if let Some(path) = xr_runtime_manifest()
.map(|manifest| manifest.runtime.mnd_libmonado_path)
.ok()
.flatten()
{
path
} else {
bail!("Monado: libmonado not found. Update your Monado/WiVRn or set LIBMONADO_PATH to point at your libmonado.so");
};

let (libmonado, mnd_root) = unsafe {
let libmonado = libloading::Library::new(lib_path)?;
let root_create: Symbol<extern "C" fn(*mut *mut c_void) -> i32> =
libmonado.get(b"mnd_root_create\0")?;

let mut root: *mut c_void = std::ptr::null_mut();
let ret = root_create(&mut root);
if ret != 0 {
anyhow::bail!("Failed to create libmonado root, code: {}", ret);
}

(libmonado, root)
};

let space_api = unsafe {
if let (Ok(get_reference), Ok(set_reference)) = (
libmonado.get(b"mnd_root_get_reference_space_offset\0"),
libmonado.get(b"mnd_root_set_reference_space_offset\0"),
) {
log::info!("Monado: using space offset API");

let get_reference: GetReferenceSpaceOffset = *get_reference;
let set_reference: SetReferenceSpaceOffset = *set_reference;

MoverImpl::SpaceOffsetApi {
get_reference,
set_reference,
}
} else if let Ok(playspace_move) = libmonado.get(b"mnd_root_playspace_move\0") {
log::warn!("Monado: using playspace_move, which is obsolete. Consider updating.");
MoverImpl::PlaySpaceMove(*playspace_move)
} else if let Ok(apply_stage_offset) = libmonado.get(b"mnd_root_apply_stage_offset\0") {
log::warn!(
"Monado: using apply_stage_offset, which is obsolete. Consider updating."
);
MoverImpl::ApplyStageOffset(*apply_stage_offset)
} else {
MoverImpl::None
}
};

Ok(Self {
libmonado,
mnd_root,
mover: space_api,
})
}

pub fn mover_supported(&self) -> bool {
!matches!(self.mover, MoverImpl::None)
}
}

#[derive(Deserialize)]
struct XrRuntimeManifestRuntime {
name: String,
library_path: String,
mnd_libmonado_path: Option<String>,
}

#[derive(Deserialize)]
struct XrRuntimeManifest {
file_format_version: String,
runtime: XrRuntimeManifestRuntime,
}

fn xr_runtime_manifest() -> anyhow::Result<XrRuntimeManifest> {
let xdg_dirs = xdg::BaseDirectories::new()?; // only fails if $HOME unset
let mut file = xdg_dirs.get_config_home();
file.push("openxr/1/active_runtime.json");

let json = fs::read_to_string(file)?;
let manifest = serde_json::from_str(&json)?;
Ok(manifest)
}
7 changes: 4 additions & 3 deletions src/backend/openxr/lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{
};

use super::{
swapchain::{create_swapchain_render_data, SwapchainRenderData},
swapchain::{create_swapchain_render_data, SwapchainOpts, SwapchainRenderData},
CompositionLayer, XrState,
};

Expand Down Expand Up @@ -46,7 +46,7 @@ impl LinePool {
let views: anyhow::Result<Vec<Arc<ImageView>>> = colors
.into_iter()
.map(
|color| match command_buffer.texture2d(1, 1, Format::R8G8B8A8_UNORM, &color) {
|color| match command_buffer.texture2d_raw(1, 1, Format::R8G8B8A8_UNORM, &color) {
Ok(tex) => ImageView::new_default(tex).map_err(|e| anyhow::anyhow!(e)),
Err(e) => Err(e),
},
Expand All @@ -68,7 +68,8 @@ impl LinePool {
) -> anyhow::Result<usize> {
let id = AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed);

let srd = create_swapchain_render_data(xr, graphics, [1, 1, 1])?;
let srd =
create_swapchain_render_data(xr, graphics, [1, 1, 1], SwapchainOpts::new().srgb())?;
self.lines.insert(
id,
LineContainer {
Expand Down
33 changes: 33 additions & 0 deletions src/backend/openxr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::{

use glam::{Affine3A, Vec3};
use openxr as xr;
use skybox::create_skybox;
use vulkano::{command_buffer::CommandBufferUsage, Handle, VulkanObject};

use crate::{
Expand All @@ -34,6 +35,7 @@ mod input;
mod lines;
mod overlay;
mod playspace;
mod skybox;
mod swapchain;

const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO;
Expand Down Expand Up @@ -122,6 +124,8 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
stage_offset: Affine3A::IDENTITY,
};

let mut skybox = create_skybox(&xr_state, &app_state);

let pointer_lines = [
lines.allocate(&xr_state, app_state.graphics.clone())?,
lines.allocate(&xr_state, app_state.graphics.clone())?,
Expand All @@ -136,6 +140,8 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),

let mut due_tasks = VecDeque::with_capacity(4);

let mut main_session_visible = false;

'main_loop: loop {
let cur_frame = FRAME_COUNTER.fetch_add(1, Ordering::Relaxed);

Expand Down Expand Up @@ -179,6 +185,19 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
EventsLost(e) => {
log::warn!("lost {} events", e.lost_event_count());
}
MainSessionVisibilityChangedEXTX(e) => {
if main_session_visible != e.visible() {
main_session_visible = e.visible();
log::info!("Main session visible: {}", main_session_visible);
if main_session_visible {
log::debug!("Destroying skybox.");
skybox = None;
} else {
log::debug!("Allocating skybox.");
skybox = create_skybox(&xr_state, &app_state);
}
}
}
_ => {}
}
}
Expand Down Expand Up @@ -284,6 +303,18 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
.graphics
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;

if !main_session_visible {
if let Some(skybox) = skybox.as_mut() {
for (idx, layer) in skybox
.present_xr(&xr_state, app_state.input_state.hmd, &mut command_buffer)?
.into_iter()
.enumerate()
{
layers.push((200.0 - 50.0 * (idx as f32), layer));
}
}
}

for o in overlays.iter_mut() {
if !o.state.want_visible {
continue;
Expand Down Expand Up @@ -327,6 +358,7 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
.map(|f| match f.1 {
CompositionLayer::Quad(ref l) => l as &xr::CompositionLayerBase<xr::Vulkan>,
CompositionLayer::Cylinder(ref l) => l as &xr::CompositionLayerBase<xr::Vulkan>,
CompositionLayer::Equirect2(ref l) => l as &xr::CompositionLayerBase<xr::Vulkan>,
CompositionLayer::None => unreachable!(),
})
.collect::<Vec<_>>();
Expand Down Expand Up @@ -413,4 +445,5 @@ pub(super) enum CompositionLayer<'a> {
None,
Quad(xr::CompositionLayerQuad<'a, xr::Vulkan>),
Cylinder(xr::CompositionLayerCylinderKHR<'a, xr::Vulkan>),
Equirect2(xr::CompositionLayerEquirect2KHR<'a, xr::Vulkan>),
}
17 changes: 13 additions & 4 deletions src/backend/openxr/overlay.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use glam::Vec3A;
use openxr as xr;
use openxr::{self as xr, CompositionLayerFlags};
use std::{f32::consts::PI, sync::Arc};
use xr::EyeVisibility;

use super::{helpers, swapchain::SwapchainRenderData, CompositionLayer, XrState};
use crate::{
backend::{openxr::swapchain::create_swapchain_render_data, overlay::OverlayData},
backend::{
openxr::swapchain::{create_swapchain_render_data, SwapchainOpts},
overlay::OverlayData,
},
graphics::WlxCommandBuffer,
state::AppState,
};
Expand Down Expand Up @@ -40,8 +43,12 @@ impl OverlayData<OpenXrOverlayData> {
let data = match self.data.swapchain {
Some(ref mut data) => data,
None => {
let srd =
create_swapchain_render_data(xr, command_buffer.graphics.clone(), extent)?;
let srd = create_swapchain_render_data(
xr,
command_buffer.graphics.clone(),
extent,
SwapchainOpts::new(),
)?;
log::debug!(
"{}: Created swapchain {}x{}, {} images, {} MB",
self.state.name,
Expand Down Expand Up @@ -75,6 +82,7 @@ impl OverlayData<OpenXrOverlayData> {
let angle = 2.0 * (scale_x / (2.0 * radius));

let cylinder = xr::CompositionLayerCylinderKHR::new()
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
.pose(posef)
.sub_image(sub_image)
.eye_visibility(EyeVisibility::BOTH)
Expand All @@ -86,6 +94,7 @@ impl OverlayData<OpenXrOverlayData> {
} else {
let posef = helpers::transform_to_posef(&self.state.transform);
let quad = xr::CompositionLayerQuad::new()
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
.pose(posef)
.sub_image(sub_image)
.eye_visibility(EyeVisibility::BOTH)
Expand Down
Loading

0 comments on commit 7a6040b

Please sign in to comment.