sbs 3d support

This commit is contained in:
galister
2025-12-16 23:12:35 +09:00
parent 9f5c0b9049
commit c41c0b9b59
30 changed files with 929 additions and 360 deletions

View File

@@ -1,19 +1,20 @@
use glam::{Affine3A, Vec3, Vec3A};
use idmap::IdMap;
use openxr as xr;
use smallvec::SmallVec;
use std::{
f32::consts::PI,
sync::{
Arc,
atomic::{AtomicUsize, Ordering},
Arc,
},
};
use wgui::gfx::{
WGfx,
cmd::WGfxClearMode,
pass::WGfxPass,
pipeline::{WGfxPipeline, WPipelineCreateInfo},
WGfx,
};
use crate::{
@@ -27,8 +28,8 @@ use vulkano::{
};
use super::{
swapchain::{create_swapchain, SwapchainOpts, WlxSwapchain},
CompositionLayer, XrState,
swapchain::{SwapchainOpts, WlxSwapchain, create_swapchain},
};
static LINE_AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(1);
@@ -156,7 +157,13 @@ impl LinePool {
) -> anyhow::Result<()> {
for line in self.lines.values_mut() {
if let Some(inner) = line.maybe_line.as_mut() {
let tgt = line.swapchain.acquire_wait_image()?;
let tgt = line
.swapchain
.acquire_wait_image()?
.views
.into_iter()
.next()
.unwrap();
self.buf_color.write()?[0..6].copy_from_slice(&COLORS[inner.color]);
@@ -167,7 +174,7 @@ impl LinePool {
cmd_buffer.run_ref(&self.pass)?;
cmd_buffer.end_rendering()?;
futures.execute((cmd_buffer.queue.clone(), cmd_buffer.build()?))?;
futures.execute(cmd_buffer.queue.clone(), cmd_buffer.build()?)?;
}
}
@@ -177,8 +184,8 @@ impl LinePool {
pub(super) fn present<'a>(
&'a mut self,
xr: &'a XrState,
) -> anyhow::Result<Vec<CompositionLayer<'a>>> {
let mut quads = Vec::new();
) -> anyhow::Result<SmallVec<[CompositionLayer<'a>; 2]>> {
let mut quads = SmallVec::new_const();
for line in self.lines.values_mut() {
line.swapchain.ensure_image_released()?;
@@ -186,7 +193,7 @@ impl LinePool {
if let Some(inner) = line.maybe_line.take() {
let quad = xr::CompositionLayerQuad::new()
.pose(inner.pose)
.sub_image(line.swapchain.get_subimage())
.sub_image(line.swapchain.get_subimage(0))
.eye_visibility(xr::EyeVisibility::BOTH)
.space(&xr.stage)
.size(xr::Extent2Df {

View File

@@ -1,7 +1,7 @@
use std::{
collections::VecDeque,
ops::Add,
sync::{Arc, atomic::Ordering},
sync::{atomic::Ordering, Arc},
time::{Duration, Instant},
};
@@ -14,25 +14,25 @@ use vulkano::{Handle, VulkanObject};
use wlx_common::overlays::ToastTopic;
use crate::{
FRAME_COUNTER, RUNNING,
backend::{
BackendError,
input::interact,
openxr::{lines::LinePool, overlay::OpenXrOverlayData},
task::{OverlayTask, TaskType},
BackendError, XrBackend,
},
config::save_state,
graphics::{GpuFutures, init_openxr_graphics},
graphics::{init_openxr_graphics, GpuFutures},
overlays::{
toast::Toast,
watch::{WATCH_NAME, watch_fade},
watch::{watch_fade, WATCH_NAME},
},
state::AppState,
subsystem::notifications::NotificationManager,
windowing::{
backend::{RenderResources, ShouldRender},
backend::{RenderResources, RenderTarget, ShouldRender},
manager::OverlayWindowManager,
},
FRAME_COUNTER, RUNNING,
};
#[cfg(feature = "wayvr")]
@@ -70,7 +70,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
let mut app = {
let (gfx, gfx_extras) = init_openxr_graphics(xr_instance.clone(), system)?;
AppState::from_graphics(gfx, gfx_extras)?
AppState::from_graphics(gfx, gfx_extras, XrBackend::OpenXR)?
};
let environment_blend_mode = {
@@ -403,11 +403,12 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
if should_render {
let meta = o.config.backend.frame_meta().unwrap(); // want panic
let tgt = o.ensure_swapchain_acquire(&app, &xr_state, meta.extent)?;
let wsi = o.ensure_swapchain_acquire(&app, &xr_state, meta.extent)?;
let tgt = RenderTarget { views: wsi.views };
let mut rdr = RenderResources::new(app.gfx.clone(), tgt, &meta, alpha)?;
o.render(&mut app, &mut rdr)?;
o.data.last_alpha = alpha;
futures.execute(rdr.end()?)?;
futures.execute_results(rdr.end()?)?;
} else if o.data.swapchain.is_none() {
continue;
}
@@ -439,18 +440,13 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
o.data.swapchain.as_mut().unwrap().ensure_image_released()?;
continue;
}
let maybe_layer = o.present(&xr_state)?;
if matches!(maybe_layer, CompositionLayer::None) {
continue;
for layer in o.present(&xr_state)? {
layers.push((dist_sq, layer));
}
layers.push((dist_sq, maybe_layer));
}
for maybe_layer in lines.present(&xr_state)? {
if matches!(maybe_layer, CompositionLayer::None) {
continue;
}
layers.push((0.0, maybe_layer));
for layer in lines.present(&xr_state)? {
layers.push((0.0, layer));
}
// End layer composition
@@ -468,7 +464,6 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
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<_>>();
@@ -523,7 +518,6 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
}
pub(super) enum CompositionLayer<'a> {
None,
Quad(xr::CompositionLayerQuad<'a, xr::Vulkan>),
Cylinder(xr::CompositionLayerCylinderKHR<'a, xr::Vulkan>),
Equirect2(xr::CompositionLayerEquirect2KHR<'a, xr::Vulkan>),

View File

@@ -1,12 +1,12 @@
use glam::Vec3A;
use openxr::{self as xr, CompositionLayerFlags};
use std::{f32::consts::PI, sync::Arc};
use vulkano::image::view::ImageView;
use smallvec::{smallvec, SmallVec};
use std::f32::consts::PI;
use xr::EyeVisibility;
use super::{CompositionLayer, XrState, helpers, swapchain::WlxSwapchain};
use super::{helpers, swapchain::WlxSwapchain, CompositionLayer, XrState};
use crate::{
backend::openxr::swapchain::{SwapchainOpts, create_swapchain},
backend::openxr::swapchain::{create_swapchain, SwapchainOpts, WlxSwapchainImage},
state::AppState,
windowing::window::OverlayWindowData,
};
@@ -26,7 +26,7 @@ impl OverlayWindowData<OpenXrOverlayData> {
app: &AppState,
xr: &'a XrState,
extent: [u32; 3],
) -> anyhow::Result<Arc<ImageView>> {
) -> anyhow::Result<WlxSwapchainImage> {
if let Some(swapchain) = self.data.swapchain.as_mut()
&& swapchain.extent == extent
{
@@ -34,10 +34,11 @@ impl OverlayWindowData<OpenXrOverlayData> {
}
log::debug!(
"{}: recreating swapchain at {}x{}",
"{}: recreating swapchain at {}x{}x{}",
self.config.name,
extent[0],
extent[1],
extent[2],
);
let mut swapchain = create_swapchain(xr, app.gfx.clone(), extent, SwapchainOpts::new())?;
let tgt = swapchain.acquire_wait_image()?;
@@ -48,21 +49,31 @@ impl OverlayWindowData<OpenXrOverlayData> {
pub(super) fn present<'a>(
&'a mut self,
xr: &'a XrState,
) -> anyhow::Result<CompositionLayer<'a>> {
) -> anyhow::Result<SmallVec<[CompositionLayer<'a>; 2]>> {
let mut layers = SmallVec::new_const();
let Some(swapchain) = self.data.swapchain.as_mut() else {
log::warn!("{}: swapchain not ready", self.config.name);
return Ok(CompositionLayer::None);
log::trace!("{}: no swapchain", self.config.name);
return Ok(layers);
};
if !swapchain.ever_acquired {
log::warn!("{}: swapchain not rendered", self.config.name);
return Ok(CompositionLayer::None);
return Ok(layers);
}
swapchain.ensure_image_released()?;
// overlays without active_state don't get queued for present
let state = self.config.active_state.as_ref().unwrap();
let sub_image = swapchain.get_subimage();
let sub_images: SmallVec<[_; 2]> = if swapchain.extent[2] > 1 {
smallvec![
(swapchain.get_subimage(0), EyeVisibility::LEFT),
(swapchain.get_subimage(1), EyeVisibility::RIGHT),
]
} else {
smallvec![(swapchain.get_subimage(0), EyeVisibility::BOTH),]
};
let transform = state.transform * self.config.backend.frame_meta().unwrap().transform; // contract
let aspect_ratio = swapchain.extent[1] as f32 / swapchain.extent[0] as f32;
@@ -89,30 +100,35 @@ impl OverlayWindowData<OpenXrOverlayData> {
let posef = helpers::translation_rotation_to_posef(center_point, quat);
let angle = 2.0 * (scale_x / (2.0 * radius));
let cylinder = xr::CompositionLayerCylinderKHR::new()
.layer_flags(flags)
.pose(posef)
.sub_image(sub_image)
.eye_visibility(EyeVisibility::BOTH)
.space(&xr.stage)
.radius(radius)
.central_angle(angle)
.aspect_ratio(aspect_ratio);
Ok(CompositionLayer::Cylinder(cylinder))
for sub_image in sub_images {
let cylinder = xr::CompositionLayerCylinderKHR::new()
.layer_flags(flags)
.pose(posef)
.sub_image(sub_image.0)
.eye_visibility(sub_image.1)
.space(&xr.stage)
.radius(radius)
.central_angle(angle)
.aspect_ratio(aspect_ratio);
layers.push(CompositionLayer::Cylinder(cylinder))
}
} else {
let posef = helpers::transform_to_posef(&transform);
let quad = xr::CompositionLayerQuad::new()
.layer_flags(flags)
.pose(posef)
.sub_image(sub_image)
.eye_visibility(EyeVisibility::BOTH)
.space(&xr.stage)
.size(xr::Extent2Df {
width: scale_x,
height: scale_y,
});
Ok(CompositionLayer::Quad(quad))
for sub_image in sub_images {
let quad = xr::CompositionLayerQuad::new()
.layer_flags(flags)
.pose(posef)
.sub_image(sub_image.0)
.eye_visibility(sub_image.1)
.space(&xr.stage)
.size(xr::Extent2Df {
width: scale_x,
height: scale_y,
});
layers.push(CompositionLayer::Quad(quad))
}
}
Ok(layers)
}
pub(super) fn after_input(&mut self, app: &mut AppState) -> anyhow::Result<()> {

View File

@@ -15,13 +15,13 @@ use wgui::gfx::{cmd::WGfxClearMode, pipeline::WPipelineCreateInfo};
use crate::{
backend::openxr::{helpers::translation_rotation_to_posef, swapchain::SwapchainOpts},
config_io,
graphics::{ExtentExt, GpuFutures, dds::WlxCommandBufferDds},
graphics::{dds::WlxCommandBufferDds, ExtentExt, GpuFutures},
state::AppState,
};
use super::{
swapchain::{create_swapchain, WlxSwapchain},
CompositionLayer, XrState,
swapchain::{WlxSwapchain, create_swapchain},
};
pub(super) struct Skybox {
@@ -94,7 +94,12 @@ impl Skybox {
let extent = self.view.image().extent();
let mut swapchain = create_swapchain(xr, app.gfx.clone(), extent, opts)?;
let tgt = swapchain.acquire_wait_image()?;
let tgt = swapchain
.acquire_wait_image()?
.views
.into_iter()
.next()
.unwrap();
let pipeline = app.gfx.create_pipeline(
app.gfx_extras.shaders.get("vert_quad").unwrap(), // want panic
app.gfx_extras.shaders.get("frag_srgb").unwrap(), // want panic
@@ -119,7 +124,7 @@ impl Skybox {
cmd_buffer.run_ref(&pass)?;
cmd_buffer.end_rendering()?;
futures.execute((cmd_buffer.queue.clone(), cmd_buffer.build()?))?;
futures.execute(cmd_buffer.queue.clone(), cmd_buffer.build()?)?;
self.sky = Some(swapchain);
Ok(())
@@ -148,7 +153,12 @@ impl Skybox {
WPipelineCreateInfo::new(app.gfx.surface_format).use_blend(AttachmentBlend::alpha()),
)?;
let tgt = swapchain.acquire_wait_image()?;
let tgt = swapchain
.acquire_wait_image()?
.views
.into_iter()
.next()
.unwrap();
let pass = pipeline.create_pass(
tgt.extent_f32(),
app.gfx_extras.quad_verts.clone(),
@@ -165,7 +175,7 @@ impl Skybox {
cmd_buffer.run_ref(&pass)?;
cmd_buffer.end_rendering()?;
futures.execute((cmd_buffer.queue.clone(), cmd_buffer.build()?))?;
futures.execute(cmd_buffer.queue.clone(), cmd_buffer.build()?)?;
self.grid = Some(swapchain);
Ok(())
@@ -211,7 +221,7 @@ impl Skybox {
.layer_flags(xr::CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
.pose(pose)
.radius(10.0)
.sub_image(self.sky.as_ref().unwrap().get_subimage())
.sub_image(self.sky.as_ref().unwrap().get_subimage(0))
.eye_visibility(xr::EyeVisibility::BOTH)
.space(&xr.stage)
.central_horizontal_angle(HORIZ_ANGLE)
@@ -226,7 +236,7 @@ impl Skybox {
width: 10.0,
height: 10.0,
})
.sub_image(self.grid.as_ref().unwrap().get_subimage())
.sub_image(self.grid.as_ref().unwrap().get_subimage(0))
.eye_visibility(xr::EyeVisibility::BOTH)
.space(&xr.stage);

View File

@@ -5,8 +5,12 @@ use openxr as xr;
use smallvec::SmallVec;
use vulkano::{
image::{
sys::RawImage,
view::{ImageView, ImageViewCreateInfo},
ImageCreateInfo, ImageUsage,
},
Handle,
image::{ImageCreateInfo, ImageUsage, sys::RawImage, view::ImageView},
};
use wgui::gfx::WGfx;
@@ -47,7 +51,7 @@ pub(super) fn create_swapchain(
width: extent[0],
height: extent[1],
face_count: 1,
array_size: 1,
array_size: extent[2],
mip_count: 1,
})?;
@@ -63,7 +67,8 @@ pub(super) fn create_swapchain(
vk_image,
ImageCreateInfo {
format: gfx.surface_format as _,
extent,
extent: [extent[0], extent[1], 1],
array_layers: extent[2],
usage: ImageUsage::COLOR_ATTACHMENT,
..Default::default()
},
@@ -71,9 +76,15 @@ pub(super) fn create_swapchain(
};
// SAFETY: OpenXR guarantees that the image is a swapchain image, thus has memory backing it.
let image = Arc::new(unsafe { raw_image.assume_bound() });
Ok(ImageView::new_default(image)?)
let mut wsi = WlxSwapchainImage::default();
for d in 0..extent[2] {
let mut create_info = ImageViewCreateInfo::from_image(&*image);
create_info.subresource_range.array_layers = d..d + 1;
wsi.views.push(ImageView::new(image.clone(), create_info)?);
}
Ok(wsi)
})
.collect::<anyhow::Result<SmallVec<[Arc<ImageView>; 4]>>>()?;
.collect::<anyhow::Result<SmallVec<[WlxSwapchainImage; 4]>>>()?;
Ok(WlxSwapchain {
acquired: false,
@@ -84,16 +95,21 @@ pub(super) fn create_swapchain(
})
}
#[derive(Default, Clone)]
pub(super) struct WlxSwapchainImage {
pub views: SmallVec<[Arc<ImageView>; 2]>,
}
pub(super) struct WlxSwapchain {
acquired: bool,
pub(super) ever_acquired: bool,
pub(super) swapchain: xr::Swapchain<xr::Vulkan>,
pub(super) extent: [u32; 3],
pub(super) images: SmallVec<[Arc<ImageView>; 4]>,
pub(super) images: SmallVec<[WlxSwapchainImage; 4]>,
}
impl WlxSwapchain {
pub(super) fn acquire_wait_image(&mut self) -> anyhow::Result<Arc<ImageView>> {
pub(super) fn acquire_wait_image(&mut self) -> anyhow::Result<WlxSwapchainImage> {
let idx = self.swapchain.acquire_image()? as usize;
self.swapchain.wait_image(xr::Duration::INFINITE)?;
self.ever_acquired = true;
@@ -109,7 +125,7 @@ impl WlxSwapchain {
Ok(())
}
pub(super) fn get_subimage(&self) -> xr::SwapchainSubImage<'_, xr::Vulkan> {
pub(super) fn get_subimage(&self, array_index: u32) -> xr::SwapchainSubImage<'_, xr::Vulkan> {
debug_assert!(self.ever_acquired, "swapchain was never acquired!");
xr::SwapchainSubImage::new()
.swapchain(&self.swapchain)
@@ -120,6 +136,6 @@ impl WlxSwapchain {
height: self.extent[1] as _,
},
})
.image_array_index(0)
.image_array_index(array_index)
}
}