openxr lasers
This commit is contained in:
@@ -10,7 +10,6 @@ type XrSession = xr::Session<xr::Vulkan>;
|
|||||||
pub(super) struct OpenXrInputSource {
|
pub(super) struct OpenXrInputSource {
|
||||||
action_set: xr::ActionSet,
|
action_set: xr::ActionSet,
|
||||||
hands: [OpenXrHand; 2],
|
hands: [OpenXrHand; 2],
|
||||||
pub(super) stage: xr::Space,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct OpenXrHand {
|
pub(super) struct OpenXrHand {
|
||||||
@@ -45,18 +44,12 @@ impl OpenXrInputSource {
|
|||||||
|
|
||||||
xr.session.attach_action_sets(&[&action_set]).unwrap();
|
xr.session.attach_action_sets(&[&action_set]).unwrap();
|
||||||
|
|
||||||
let stage = xr
|
|
||||||
.session
|
|
||||||
.create_reference_space(xr::ReferenceSpaceType::STAGE, xr::Posef::IDENTITY)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
action_set,
|
action_set,
|
||||||
hands: [
|
hands: [
|
||||||
OpenXrHand::new(&xr, left_source),
|
OpenXrHand::new(&xr, left_source),
|
||||||
OpenXrHand::new(&xr, right_source),
|
OpenXrHand::new(&xr, right_source),
|
||||||
],
|
],
|
||||||
stage,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +61,7 @@ impl OpenXrInputSource {
|
|||||||
for i in 0..2 {
|
for i in 0..2 {
|
||||||
self.hands[i].update(
|
self.hands[i].update(
|
||||||
&mut state.input_state.pointers[i],
|
&mut state.input_state.pointers[i],
|
||||||
&self.stage,
|
&xr.stage,
|
||||||
&xr.session,
|
&xr.session,
|
||||||
xr.predicted_display_time,
|
xr.predicted_display_time,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
use glam::{Affine3A, Vec3, Vec3A};
|
||||||
|
use idmap::IdMap;
|
||||||
|
use openxr as xr;
|
||||||
|
use std::{
|
||||||
|
f32::consts::PI,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use vulkano::{command_buffer::CommandBufferUsage, format::Format, image::view::ImageView};
|
||||||
|
|
||||||
|
use crate::graphics::{WlxCommandBuffer, WlxGraphics};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
swapchain::{create_swapchain_render_data, SwapchainRenderData},
|
||||||
|
transform_to_posef, XrState,
|
||||||
|
};
|
||||||
|
|
||||||
|
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(1);
|
||||||
|
pub(super) const LINE_WIDTH: f32 = 0.002;
|
||||||
|
|
||||||
|
pub(super) struct LinePool {
|
||||||
|
lines: IdMap<usize, LineContainer>,
|
||||||
|
colors: Vec<Arc<ImageView>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LinePool {
|
||||||
|
pub(super) fn new(graphics: Arc<WlxGraphics>) -> Self {
|
||||||
|
let mut command_buffer = graphics.create_command_buffer(CommandBufferUsage::OneTimeSubmit);
|
||||||
|
|
||||||
|
// TODO customizable colors
|
||||||
|
let colors = [
|
||||||
|
[0xff, 0xff, 0xff, 0xff],
|
||||||
|
[0x00, 0x60, 0x80, 0xff],
|
||||||
|
[0xB0, 0x30, 0x00, 0xff],
|
||||||
|
[0x60, 0x00, 0x80, 0xff],
|
||||||
|
];
|
||||||
|
|
||||||
|
let views = colors
|
||||||
|
.into_iter()
|
||||||
|
.map(|color| {
|
||||||
|
let tex = command_buffer.texture2d(1, 1, Format::R8G8B8A8_UNORM, &color);
|
||||||
|
ImageView::new_default(tex).unwrap()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
command_buffer.build_and_execute_now();
|
||||||
|
|
||||||
|
LinePool {
|
||||||
|
lines: IdMap::new(),
|
||||||
|
colors: views,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn allocate(&mut self, xr: &XrState, graphics: Arc<WlxGraphics>) -> usize {
|
||||||
|
let id = AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed);
|
||||||
|
|
||||||
|
let srd = create_swapchain_render_data(xr, graphics, [1, 1, 1]);
|
||||||
|
self.lines.insert(
|
||||||
|
id,
|
||||||
|
LineContainer {
|
||||||
|
swapchain: srd,
|
||||||
|
maybe_line: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn draw_from(&mut self, id: usize, mut from: Affine3A, len: f32, color: usize) {
|
||||||
|
if len < 0.01 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert!(color < self.colors.len());
|
||||||
|
|
||||||
|
let Some(line) = self.lines.get_mut(&id) else {
|
||||||
|
log::warn!("Line {} not found", id);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let rotation = Affine3A::from_axis_angle(Vec3::X, PI * 1.5);
|
||||||
|
|
||||||
|
from.translation = from.translation + from.transform_vector3a(Vec3A::NEG_Z) * (len * 0.5);
|
||||||
|
let transform = from * rotation;
|
||||||
|
|
||||||
|
let posef = transform_to_posef(&transform);
|
||||||
|
|
||||||
|
line.maybe_line = Some(Line {
|
||||||
|
view: self.colors[color].clone(),
|
||||||
|
pose: posef,
|
||||||
|
length: len,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn present_xr<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
xr: &'a XrState,
|
||||||
|
command_buffer: &mut WlxCommandBuffer,
|
||||||
|
) -> Vec<xr::CompositionLayerQuad<xr::Vulkan>> {
|
||||||
|
let mut quads = Vec::new();
|
||||||
|
|
||||||
|
for line in self.lines.values_mut() {
|
||||||
|
if let Some(inner) = line.maybe_line.take() {
|
||||||
|
let quad = xr::CompositionLayerQuad::new()
|
||||||
|
.pose(inner.pose)
|
||||||
|
.sub_image(
|
||||||
|
line.swapchain
|
||||||
|
.acquire_present_release(command_buffer, inner.view),
|
||||||
|
)
|
||||||
|
.eye_visibility(xr::EyeVisibility::BOTH)
|
||||||
|
.layer_flags(xr::CompositionLayerFlags::CORRECT_CHROMATIC_ABERRATION)
|
||||||
|
.space(&xr.stage)
|
||||||
|
.size(xr::Extent2Df {
|
||||||
|
width: LINE_WIDTH,
|
||||||
|
height: inner.length,
|
||||||
|
});
|
||||||
|
|
||||||
|
quads.push(quad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
quads
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct Line {
|
||||||
|
pub(super) view: Arc<ImageView>,
|
||||||
|
pub(super) pose: xr::Posef,
|
||||||
|
pub(super) length: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LineContainer {
|
||||||
|
swapchain: SwapchainRenderData,
|
||||||
|
maybe_line: Option<Line>,
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,11 +9,14 @@ use std::{
|
|||||||
use anyhow::{bail, ensure};
|
use anyhow::{bail, ensure};
|
||||||
use glam::{Affine3A, Quat, Vec3};
|
use glam::{Affine3A, Quat, Vec3};
|
||||||
use openxr as xr;
|
use openxr as xr;
|
||||||
use vulkano::{Handle, VulkanObject};
|
use vulkano::{command_buffer::CommandBufferUsage, Handle, VulkanObject};
|
||||||
use xr::{CompositionLayerFlags, EyeVisibility};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::{common::OverlayContainer, input::interact, openxr::overlay::OpenXrOverlayData},
|
backend::{
|
||||||
|
common::OverlayContainer,
|
||||||
|
input::interact,
|
||||||
|
openxr::{lines::LinePool, overlay::OpenXrOverlayData},
|
||||||
|
},
|
||||||
graphics::WlxGraphics,
|
graphics::WlxGraphics,
|
||||||
state::AppState,
|
state::AppState,
|
||||||
};
|
};
|
||||||
@@ -23,6 +26,7 @@ use super::common::BackendError;
|
|||||||
mod input;
|
mod input;
|
||||||
mod lines;
|
mod lines;
|
||||||
mod overlay;
|
mod overlay;
|
||||||
|
mod swapchain;
|
||||||
|
|
||||||
const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO;
|
const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO;
|
||||||
const VIEW_COUNT: u32 = 2;
|
const VIEW_COUNT: u32 = 2;
|
||||||
@@ -32,6 +36,7 @@ struct XrState {
|
|||||||
system: xr::SystemId,
|
system: xr::SystemId,
|
||||||
session: xr::Session<xr::Vulkan>,
|
session: xr::Session<xr::Vulkan>,
|
||||||
predicted_display_time: xr::Time,
|
predicted_display_time: xr::Time,
|
||||||
|
stage: Arc<xr::Space>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn openxr_run(running: Arc<AtomicBool>) -> Result<(), BackendError> {
|
pub fn openxr_run(running: Arc<AtomicBool>) -> Result<(), BackendError> {
|
||||||
@@ -54,6 +59,7 @@ pub fn openxr_run(running: Arc<AtomicBool>) -> Result<(), BackendError> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut overlays = OverlayContainer::<OpenXrOverlayData>::new(&mut app_state);
|
let mut overlays = OverlayContainer::<OpenXrOverlayData>::new(&mut app_state);
|
||||||
|
let mut lines = LinePool::new(app_state.graphics.clone());
|
||||||
|
|
||||||
app_state.hid_provider.set_desktop_extent(overlays.extent);
|
app_state.hid_provider.set_desktop_extent(overlays.extent);
|
||||||
|
|
||||||
@@ -77,13 +83,23 @@ pub fn openxr_run(running: Arc<AtomicBool>) -> Result<(), BackendError> {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let stage = session
|
||||||
|
.create_reference_space(xr::ReferenceSpaceType::STAGE, xr::Posef::IDENTITY)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut xr_state = XrState {
|
let mut xr_state = XrState {
|
||||||
instance: xr_instance,
|
instance: xr_instance,
|
||||||
system,
|
system,
|
||||||
session,
|
session,
|
||||||
predicted_display_time: xr::Time::from_nanos(0),
|
predicted_display_time: xr::Time::from_nanos(0),
|
||||||
|
stage: Arc::new(stage),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let pointer_lines = [
|
||||||
|
lines.allocate(&xr_state, app_state.graphics.clone()),
|
||||||
|
lines.allocate(&xr_state, app_state.graphics.clone()),
|
||||||
|
];
|
||||||
|
|
||||||
let input_source = input::OpenXrInputSource::new(&xr_state);
|
let input_source = input::OpenXrInputSource::new(&xr_state);
|
||||||
|
|
||||||
let mut session_running = false;
|
let mut session_running = false;
|
||||||
@@ -164,17 +180,26 @@ pub fn openxr_run(running: Arc<AtomicBool>) -> Result<(), BackendError> {
|
|||||||
.locate_views(
|
.locate_views(
|
||||||
VIEW_TYPE,
|
VIEW_TYPE,
|
||||||
xr_frame_state.predicted_display_time,
|
xr_frame_state.predicted_display_time,
|
||||||
&input_source.stage,
|
&xr_state.stage,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
app_state.input_state.hmd = hmd_pose_from_views(&views);
|
app_state.input_state.hmd = hmd_pose_from_views(&views);
|
||||||
|
|
||||||
let _pointer_lengths = interact(&mut overlays, &mut app_state);
|
let pointer_lengths = interact(&mut overlays, &mut app_state);
|
||||||
|
for (idx, len) in pointer_lengths.iter().enumerate() {
|
||||||
//TODO lines
|
lines.draw_from(
|
||||||
|
pointer_lines[idx],
|
||||||
|
app_state.input_state.pointers[idx].pose,
|
||||||
|
*len,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let mut layers = vec![];
|
let mut layers = vec![];
|
||||||
|
let mut command_buffer = app_state
|
||||||
|
.graphics
|
||||||
|
.create_command_buffer(CommandBufferUsage::OneTimeSubmit);
|
||||||
|
|
||||||
for o in overlays.iter_mut() {
|
for o in overlays.iter_mut() {
|
||||||
if !o.state.want_visible {
|
if !o.state.want_visible {
|
||||||
@@ -186,23 +211,18 @@ pub fn openxr_run(running: Arc<AtomicBool>) -> Result<(), BackendError> {
|
|||||||
o.data.init = true;
|
o.data.init = true;
|
||||||
}
|
}
|
||||||
o.render(&mut app_state);
|
o.render(&mut app_state);
|
||||||
let transform = o.state.transform;
|
|
||||||
|
|
||||||
let Some((sub_image, extent)) = o.present_xr(&xr_state, &mut app_state) else {
|
if let Some(quad) = o.present_xr(&xr_state, &mut command_buffer) {
|
||||||
continue;
|
layers.push(quad);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let quad = xr::CompositionLayerQuad::new()
|
for quad in lines.present_xr(&xr_state, &mut command_buffer) {
|
||||||
.pose(transform_to_posef(&transform))
|
|
||||||
.sub_image(sub_image)
|
|
||||||
.eye_visibility(EyeVisibility::BOTH)
|
|
||||||
.layer_flags(CompositionLayerFlags::CORRECT_CHROMATIC_ABERRATION)
|
|
||||||
.space(&input_source.stage)
|
|
||||||
.size(extent);
|
|
||||||
|
|
||||||
layers.push(quad);
|
layers.push(quad);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
command_buffer.build_and_execute_now();
|
||||||
|
|
||||||
let frame_ref = layers
|
let frame_ref = layers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| f as &xr::CompositionLayerBase<xr::Vulkan>)
|
.map(|f| f as &xr::CompositionLayerBase<xr::Vulkan>)
|
||||||
@@ -328,7 +348,7 @@ fn quat_lerp(a: Quat, mut b: Quat, t: f32) -> Quat {
|
|||||||
|
|
||||||
fn transform_to_posef(transform: &Affine3A) -> xr::Posef {
|
fn transform_to_posef(transform: &Affine3A) -> xr::Posef {
|
||||||
let translation = transform.translation;
|
let translation = transform.translation;
|
||||||
let rotation = Quat::from_mat3a(&transform.matrix3);
|
let rotation = Quat::from_affine3(transform).normalize();
|
||||||
|
|
||||||
xr::Posef {
|
xr::Posef {
|
||||||
orientation: xr::Quaternionf {
|
orientation: xr::Quaternionf {
|
||||||
|
|||||||
@@ -1,29 +1,27 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::XrState;
|
|
||||||
use crate::{backend::overlay::OverlayData, graphics::WlxPipeline, state::AppState};
|
|
||||||
use ash::vk::{self};
|
|
||||||
use openxr as xr;
|
use openxr as xr;
|
||||||
use vulkano::{
|
use std::sync::Arc;
|
||||||
command_buffer::CommandBufferUsage,
|
use xr::{CompositionLayerFlags, EyeVisibility};
|
||||||
image::{sampler::Filter, view::ImageView, ImageCreateInfo, ImageUsage},
|
|
||||||
render_pass::{Framebuffer, FramebufferCreateInfo},
|
use super::{swapchain::SwapchainRenderData, transform_to_posef, XrState};
|
||||||
Handle,
|
use crate::{
|
||||||
|
backend::{openxr::swapchain::create_swapchain_render_data, overlay::OverlayData},
|
||||||
|
graphics::WlxCommandBuffer,
|
||||||
};
|
};
|
||||||
|
use vulkano::image::view::ImageView;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct OpenXrOverlayData {
|
pub struct OpenXrOverlayData {
|
||||||
last_view: Option<Arc<ImageView>>,
|
last_view: Option<Arc<ImageView>>,
|
||||||
inner: Option<XrOverlayData>,
|
pub(super) swapchain: Option<SwapchainRenderData>,
|
||||||
pub(super) init: bool,
|
pub(super) init: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OverlayData<OpenXrOverlayData> {
|
impl OverlayData<OpenXrOverlayData> {
|
||||||
pub(super) fn present_xr(
|
pub(super) fn present_xr<'a>(
|
||||||
&mut self,
|
&'a mut self,
|
||||||
xr: &XrState,
|
xr: &'a XrState,
|
||||||
state: &mut AppState,
|
command_buffer: &mut WlxCommandBuffer,
|
||||||
) -> Option<(xr::SwapchainSubImage<xr::Vulkan>, xr::Extent2Df)> {
|
) -> Option<xr::CompositionLayerQuad<xr::Vulkan>> {
|
||||||
if let Some(new_view) = self.view() {
|
if let Some(new_view) = self.view() {
|
||||||
self.data.last_view = Some(new_view);
|
self.data.last_view = Some(new_view);
|
||||||
}
|
}
|
||||||
@@ -35,152 +33,35 @@ impl OverlayData<OpenXrOverlayData> {
|
|||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
let data = self.data.inner.get_or_insert_with(|| {
|
let data = self.data.swapchain.get_or_insert_with(|| {
|
||||||
let extent = self.backend.extent();
|
let extent = self.backend.extent();
|
||||||
|
let srd = create_swapchain_render_data(xr, command_buffer.graphics.clone(), extent);
|
||||||
let swapchain = xr
|
|
||||||
.session
|
|
||||||
.create_swapchain(&xr::SwapchainCreateInfo {
|
|
||||||
create_flags: xr::SwapchainCreateFlags::EMPTY,
|
|
||||||
usage_flags: xr::SwapchainUsageFlags::COLOR_ATTACHMENT
|
|
||||||
| xr::SwapchainUsageFlags::SAMPLED,
|
|
||||||
format: state.graphics.native_format as _,
|
|
||||||
sample_count: 1,
|
|
||||||
width: extent[0],
|
|
||||||
height: extent[1],
|
|
||||||
face_count: 1,
|
|
||||||
array_size: 1,
|
|
||||||
mip_count: 1,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let framebuffers: Vec<XrFramebuffer> = swapchain
|
|
||||||
.enumerate_images()
|
|
||||||
.unwrap()
|
|
||||||
.into_iter()
|
|
||||||
.map(|handle| {
|
|
||||||
let vk_image = vk::Image::from_raw(handle);
|
|
||||||
// thanks @yshui
|
|
||||||
let raw_image = unsafe {
|
|
||||||
vulkano::image::sys::RawImage::from_handle(
|
|
||||||
state.graphics.device.clone(),
|
|
||||||
vk_image,
|
|
||||||
ImageCreateInfo {
|
|
||||||
format: state.graphics.native_format,
|
|
||||||
extent,
|
|
||||||
usage: ImageUsage::COLOR_ATTACHMENT | ImageUsage::TRANSFER_DST,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
// SAFETY: OpenXR guarantees that the image is a swapchain image, thus has memory backing it.
|
|
||||||
let image = Arc::new(unsafe { raw_image.assume_bound() });
|
|
||||||
let view = ImageView::new_default(image).unwrap();
|
|
||||||
|
|
||||||
// HACK: maybe not create one pipeline per image?
|
|
||||||
|
|
||||||
let shaders = state.graphics.shared_shaders.read().unwrap();
|
|
||||||
|
|
||||||
let pipeline = state.graphics.create_pipeline(
|
|
||||||
view.clone(),
|
|
||||||
shaders.get("vert_common").unwrap().clone(),
|
|
||||||
shaders.get("frag_srgb").unwrap().clone(),
|
|
||||||
state.graphics.native_format,
|
|
||||||
);
|
|
||||||
|
|
||||||
let inner = Framebuffer::new(
|
|
||||||
pipeline.render_pass.clone(),
|
|
||||||
FramebufferCreateInfo {
|
|
||||||
attachments: vec![view.clone()],
|
|
||||||
extent: [view.image().extent()[0] as _, view.image().extent()[1] as _],
|
|
||||||
layers: 1,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
XrFramebuffer {
|
|
||||||
inner,
|
|
||||||
view,
|
|
||||||
pipeline,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
log::info!(
|
log::info!(
|
||||||
"{}: Created swapchain {}x{}, {} images, {} MB",
|
"{}: Created swapchain {}x{}, {} images, {} MB",
|
||||||
self.state.name,
|
self.state.name,
|
||||||
extent[0],
|
extent[0],
|
||||||
extent[1],
|
extent[1],
|
||||||
framebuffers.len(),
|
srd.images.len(),
|
||||||
extent[0] * extent[1] * 4 * framebuffers.len() as u32 / 1024 / 1024
|
extent[0] * extent[1] * 4 * srd.images.len() as u32 / 1024 / 1024
|
||||||
);
|
);
|
||||||
|
srd
|
||||||
XrOverlayData {
|
|
||||||
swapchain,
|
|
||||||
framebuffers,
|
|
||||||
extent,
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let idx = data.swapchain.acquire_image().unwrap();
|
let sub_image = data.acquire_present_release(command_buffer, my_view);
|
||||||
|
let posef = transform_to_posef(&self.state.transform);
|
||||||
|
|
||||||
data.swapchain.wait_image(xr::Duration::INFINITE).unwrap();
|
let quad = xr::CompositionLayerQuad::new()
|
||||||
|
.pose(posef)
|
||||||
let frame = &data.framebuffers[idx as usize];
|
.sub_image(sub_image)
|
||||||
let mut command_buffer = state
|
.eye_visibility(EyeVisibility::BOTH)
|
||||||
.graphics
|
.layer_flags(CompositionLayerFlags::CORRECT_CHROMATIC_ABERRATION)
|
||||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)
|
.space(&xr.stage)
|
||||||
.begin_render_pass(&frame.pipeline);
|
.size(xr::Extent2Df {
|
||||||
|
|
||||||
let set = frame
|
|
||||||
.pipeline
|
|
||||||
.uniform_sampler(0, my_view.clone(), Filter::Linear);
|
|
||||||
let pass = frame.pipeline.create_pass(
|
|
||||||
[
|
|
||||||
my_view.image().extent()[0] as _,
|
|
||||||
my_view.image().extent()[1] as _,
|
|
||||||
],
|
|
||||||
state.graphics.quad_verts.clone(),
|
|
||||||
state.graphics.quad_indices.clone(),
|
|
||||||
vec![set],
|
|
||||||
);
|
|
||||||
|
|
||||||
command_buffer.run_ref(&pass);
|
|
||||||
command_buffer.end_render_pass().build_and_execute_now();
|
|
||||||
|
|
||||||
data.swapchain.release_image().unwrap();
|
|
||||||
|
|
||||||
let extent = xr::Extent2Df {
|
|
||||||
width: self.state.width,
|
width: self.state.width,
|
||||||
height: (data.extent[1] as f32 / data.extent[0] as f32) * self.state.width,
|
height: (self.backend.extent()[1] as f32 / self.backend.extent()[0] as f32)
|
||||||
};
|
* self.state.width,
|
||||||
|
});
|
||||||
Some((
|
Some(quad)
|
||||||
xr::SwapchainSubImage::new()
|
|
||||||
.swapchain(&data.swapchain)
|
|
||||||
.image_rect(xr::Rect2Di {
|
|
||||||
offset: xr::Offset2Di { x: 0, y: 0 },
|
|
||||||
extent: xr::Extent2Di {
|
|
||||||
width: data.extent[0] as _,
|
|
||||||
height: data.extent[1] as _,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.image_array_index(0),
|
|
||||||
extent,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct XrOverlayData {
|
|
||||||
swapchain: xr::Swapchain<xr::Vulkan>,
|
|
||||||
extent: [u32; 3],
|
|
||||||
framebuffers: Vec<XrFramebuffer>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct XrFramebuffer {
|
|
||||||
inner: Arc<Framebuffer>,
|
|
||||||
view: Arc<ImageView>,
|
|
||||||
pipeline: Arc<WlxPipeline>,
|
|
||||||
}
|
|
||||||
|
|||||||
149
src/backend/openxr/swapchain.rs
Normal file
149
src/backend/openxr/swapchain.rs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ash::vk;
|
||||||
|
use openxr as xr;
|
||||||
|
|
||||||
|
use vulkano::{
|
||||||
|
image::{sampler::Filter, view::ImageView, ImageCreateInfo, ImageUsage},
|
||||||
|
render_pass::{Framebuffer, FramebufferCreateInfo},
|
||||||
|
Handle,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::graphics::{WlxCommandBuffer, WlxGraphics, WlxPipeline};
|
||||||
|
|
||||||
|
use super::XrState;
|
||||||
|
|
||||||
|
pub(super) fn create_swapchain_render_data(
|
||||||
|
xr: &XrState,
|
||||||
|
graphics: Arc<WlxGraphics>,
|
||||||
|
extent: [u32; 3],
|
||||||
|
) -> SwapchainRenderData {
|
||||||
|
let swapchain = xr
|
||||||
|
.session
|
||||||
|
.create_swapchain(&xr::SwapchainCreateInfo {
|
||||||
|
create_flags: xr::SwapchainCreateFlags::EMPTY,
|
||||||
|
usage_flags: xr::SwapchainUsageFlags::COLOR_ATTACHMENT
|
||||||
|
| xr::SwapchainUsageFlags::SAMPLED,
|
||||||
|
format: graphics.native_format as _,
|
||||||
|
sample_count: 1,
|
||||||
|
width: extent[0],
|
||||||
|
height: extent[1],
|
||||||
|
face_count: 1,
|
||||||
|
array_size: 1,
|
||||||
|
mip_count: 1,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sips: Vec<SwapchainImagePipeline> = swapchain
|
||||||
|
.enumerate_images()
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|handle| {
|
||||||
|
let vk_image = vk::Image::from_raw(handle);
|
||||||
|
// thanks @yshui
|
||||||
|
let raw_image = unsafe {
|
||||||
|
vulkano::image::sys::RawImage::from_handle(
|
||||||
|
graphics.device.clone(),
|
||||||
|
vk_image,
|
||||||
|
ImageCreateInfo {
|
||||||
|
format: graphics.native_format,
|
||||||
|
extent,
|
||||||
|
usage: ImageUsage::COLOR_ATTACHMENT | ImageUsage::TRANSFER_DST,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
// SAFETY: OpenXR guarantees that the image is a swapchain image, thus has memory backing it.
|
||||||
|
let image = Arc::new(unsafe { raw_image.assume_bound() });
|
||||||
|
let view = ImageView::new_default(image).unwrap();
|
||||||
|
|
||||||
|
// HACK: maybe not create one pipeline per image?
|
||||||
|
|
||||||
|
let shaders = graphics.shared_shaders.read().unwrap();
|
||||||
|
|
||||||
|
let pipeline = graphics.create_pipeline(
|
||||||
|
view.clone(),
|
||||||
|
shaders.get("vert_common").unwrap().clone(),
|
||||||
|
shaders.get("frag_srgb").unwrap().clone(),
|
||||||
|
graphics.native_format,
|
||||||
|
);
|
||||||
|
|
||||||
|
let buffer = Framebuffer::new(
|
||||||
|
pipeline.render_pass.clone(),
|
||||||
|
FramebufferCreateInfo {
|
||||||
|
attachments: vec![view.clone()],
|
||||||
|
extent: [view.image().extent()[0] as _, view.image().extent()[1] as _],
|
||||||
|
layers: 1,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
SwapchainImagePipeline {
|
||||||
|
buffer,
|
||||||
|
view,
|
||||||
|
pipeline,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
SwapchainRenderData {
|
||||||
|
swapchain,
|
||||||
|
images: sips,
|
||||||
|
extent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct SwapchainRenderData {
|
||||||
|
pub(super) swapchain: xr::Swapchain<xr::Vulkan>,
|
||||||
|
pub(super) extent: [u32; 3],
|
||||||
|
pub(super) images: Vec<SwapchainImagePipeline>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct SwapchainImagePipeline {
|
||||||
|
pub(super) view: Arc<ImageView>,
|
||||||
|
pub(super) buffer: Arc<Framebuffer>,
|
||||||
|
pub(super) pipeline: Arc<WlxPipeline>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwapchainRenderData {
|
||||||
|
pub(super) fn acquire_present_release(
|
||||||
|
&mut self,
|
||||||
|
command_buffer: &mut WlxCommandBuffer,
|
||||||
|
view: Arc<ImageView>,
|
||||||
|
) -> xr::SwapchainSubImage<xr::Vulkan> {
|
||||||
|
let idx = self.swapchain.acquire_image().unwrap() as usize;
|
||||||
|
self.swapchain.wait_image(xr::Duration::INFINITE).unwrap();
|
||||||
|
|
||||||
|
let image = &mut self.images[idx];
|
||||||
|
let pipeline = image.pipeline.clone();
|
||||||
|
command_buffer.begin_render_pass(&pipeline);
|
||||||
|
|
||||||
|
let target_extent = image.pipeline.view.image().extent();
|
||||||
|
let set = image
|
||||||
|
.pipeline
|
||||||
|
.uniform_sampler(0, view.clone(), Filter::Linear);
|
||||||
|
let pass = image.pipeline.create_pass(
|
||||||
|
[target_extent[0] as _, target_extent[1] as _],
|
||||||
|
command_buffer.graphics.quad_verts.clone(),
|
||||||
|
command_buffer.graphics.quad_indices.clone(),
|
||||||
|
vec![set],
|
||||||
|
);
|
||||||
|
command_buffer.run_ref(&pass);
|
||||||
|
command_buffer.end_render_pass();
|
||||||
|
|
||||||
|
self.swapchain.release_image().unwrap();
|
||||||
|
|
||||||
|
xr::SwapchainSubImage::new()
|
||||||
|
.swapchain(&self.swapchain)
|
||||||
|
.image_rect(xr::Rect2Di {
|
||||||
|
offset: xr::Offset2Di { x: 0, y: 0 },
|
||||||
|
extent: xr::Extent2Di {
|
||||||
|
width: target_extent[0] as _,
|
||||||
|
height: target_extent[1] as _,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.image_array_index(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -737,12 +737,12 @@ impl WlxGraphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct WlxCommandBuffer {
|
pub struct WlxCommandBuffer {
|
||||||
graphics: Arc<WlxGraphics>,
|
pub graphics: Arc<WlxGraphics>,
|
||||||
command_buffer: RecordingCommandBuffer,
|
pub command_buffer: RecordingCommandBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WlxCommandBuffer {
|
impl WlxCommandBuffer {
|
||||||
pub fn begin_render_pass(mut self, pipeline: &WlxPipeline) -> Self {
|
pub fn begin_render_pass(&mut self, pipeline: &WlxPipeline) {
|
||||||
self.command_buffer
|
self.command_buffer
|
||||||
.begin_render_pass(
|
.begin_render_pass(
|
||||||
RenderPassBeginInfo {
|
RenderPassBeginInfo {
|
||||||
@@ -755,7 +755,6 @@ impl WlxCommandBuffer {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_ref(&mut self, pass: &WlxPass) -> &mut Self {
|
pub fn run_ref(&mut self, pass: &WlxPass) -> &mut Self {
|
||||||
@@ -832,11 +831,10 @@ impl WlxCommandBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WlxCommandBuffer {
|
impl WlxCommandBuffer {
|
||||||
pub fn end_render_pass(mut self) -> Self {
|
pub fn end_render_pass(&mut self) {
|
||||||
self.command_buffer
|
self.command_buffer
|
||||||
.end_render_pass(SubpassEndInfo::default())
|
.end_render_pass(SubpassEndInfo::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Arc<CommandBuffer> {
|
pub fn build(self) -> Arc<CommandBuffer> {
|
||||||
|
|||||||
@@ -320,28 +320,30 @@ impl<D, S> Canvas<D, S> {
|
|||||||
let mut cmd_buffer = self
|
let mut cmd_buffer = self
|
||||||
.canvas
|
.canvas
|
||||||
.graphics
|
.graphics
|
||||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)
|
.create_command_buffer(CommandBufferUsage::OneTimeSubmit);
|
||||||
.begin_render_pass(&self.canvas.pipeline_bg_color);
|
cmd_buffer.begin_render_pass(&self.canvas.pipeline_bg_color);
|
||||||
for c in self.controls.iter_mut() {
|
for c in self.controls.iter_mut() {
|
||||||
if let Some(fun) = c.on_render_bg {
|
if let Some(fun) = c.on_render_bg {
|
||||||
fun(c, &self.canvas, app, &mut cmd_buffer);
|
fun(c, &self.canvas, app, &mut cmd_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cmd_buffer.end_render_pass().build_and_execute_now()
|
cmd_buffer.end_render_pass();
|
||||||
|
cmd_buffer.build_and_execute_now();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_fg(&mut self, app: &mut AppState) {
|
fn render_fg(&mut self, app: &mut AppState) {
|
||||||
let mut cmd_buffer = self
|
let mut cmd_buffer = self
|
||||||
.canvas
|
.canvas
|
||||||
.graphics
|
.graphics
|
||||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)
|
.create_command_buffer(CommandBufferUsage::OneTimeSubmit);
|
||||||
.begin_render_pass(&self.canvas.pipeline_fg_glyph);
|
cmd_buffer.begin_render_pass(&self.canvas.pipeline_fg_glyph);
|
||||||
for c in self.controls.iter_mut() {
|
for c in self.controls.iter_mut() {
|
||||||
if let Some(fun) = c.on_render_fg {
|
if let Some(fun) = c.on_render_fg {
|
||||||
fun(c, &self.canvas, app, &mut cmd_buffer);
|
fun(c, &self.canvas, app, &mut cmd_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cmd_buffer.end_render_pass().build_and_execute_now()
|
cmd_buffer.end_render_pass();
|
||||||
|
cmd_buffer.build_and_execute_now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,8 +426,8 @@ impl<D, S> OverlayRenderer for Canvas<D, S> {
|
|||||||
let mut cmd_buffer = self
|
let mut cmd_buffer = self
|
||||||
.canvas
|
.canvas
|
||||||
.graphics
|
.graphics
|
||||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)
|
.create_command_buffer(CommandBufferUsage::OneTimeSubmit);
|
||||||
.begin_render_pass(&self.canvas.pipeline_final);
|
cmd_buffer.begin_render_pass(&self.canvas.pipeline_final);
|
||||||
|
|
||||||
// static background
|
// static background
|
||||||
cmd_buffer.run_ref(&self.pass_bg);
|
cmd_buffer.run_ref(&self.pass_bg);
|
||||||
@@ -446,7 +448,8 @@ impl<D, S> OverlayRenderer for Canvas<D, S> {
|
|||||||
// mostly static text
|
// mostly static text
|
||||||
cmd_buffer.run_ref(&self.pass_fg);
|
cmd_buffer.run_ref(&self.pass_fg);
|
||||||
|
|
||||||
cmd_buffer.end_render_pass().build_and_execute_now();
|
cmd_buffer.end_render_pass();
|
||||||
|
cmd_buffer.build_and_execute_now();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
self.canvas
|
self.canvas
|
||||||
|
|||||||
@@ -158,8 +158,8 @@ impl ScreenPipeline {
|
|||||||
|
|
||||||
let mut command_buffer = self
|
let mut command_buffer = self
|
||||||
.graphics
|
.graphics
|
||||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)
|
.create_command_buffer(CommandBufferUsage::OneTimeSubmit);
|
||||||
.begin_render_pass(&self.pipeline);
|
command_buffer.begin_render_pass(&self.pipeline);
|
||||||
|
|
||||||
let set0 = self.pipeline.uniform_sampler(
|
let set0 = self.pipeline.uniform_sampler(
|
||||||
0,
|
0,
|
||||||
@@ -177,9 +177,10 @@ impl ScreenPipeline {
|
|||||||
vec![set0],
|
vec![set0],
|
||||||
);
|
);
|
||||||
command_buffer.run_ref(&pass);
|
command_buffer.run_ref(&pass);
|
||||||
|
command_buffer.end_render_pass();
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut exec = command_buffer.end_render_pass().build_and_execute();
|
let mut exec = command_buffer.build_and_execute();
|
||||||
exec.flush().unwrap();
|
exec.flush().unwrap();
|
||||||
exec.cleanup_finished();
|
exec.cleanup_finished();
|
||||||
}
|
}
|
||||||
@@ -417,7 +418,7 @@ where
|
|||||||
grabbable: true,
|
grabbable: true,
|
||||||
spawn_rotation: Quat::from_axis_angle(axis, angle),
|
spawn_rotation: Quat::from_axis_angle(axis, angle),
|
||||||
spawn_point: vec3a(0., 0.5, -1.),
|
spawn_point: vec3a(0., 0.5, -1.),
|
||||||
width: 1.5,
|
width: 1.,
|
||||||
interaction_transform,
|
interaction_transform,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user