rework rendering backend

This commit is contained in:
galister
2025-04-06 22:12:56 +09:00
parent 55867e803f
commit cf29682e66
27 changed files with 1135 additions and 1181 deletions
+1 -1
View File
@@ -429,7 +429,7 @@ where
.is_some_and(|x| x.grabbed_id == hit.overlay)
{
let can_curve = hovered
.frame_transform()
.frame_meta()
.is_some_and(|e| e.extent[0] >= e.extent[1]);
if can_curve {
+15 -8
View File
@@ -12,9 +12,10 @@ use vulkano::image::view::ImageView;
use vulkano::image::ImageLayout;
use crate::backend::overlay::{
FrameTransform, OverlayData, OverlayRenderer, OverlayState, SplitOverlayBackend, Z_ORDER_LINES,
FrameMeta, OverlayData, OverlayRenderer, OverlayState, ShouldRender, SplitOverlayBackend,
Z_ORDER_LINES,
};
use crate::graphics::WlxGraphics;
use crate::graphics::{CommandBuffers, WlxGraphics};
use crate::state::AppState;
use super::overlay::OpenVrOverlayData;
@@ -185,14 +186,20 @@ impl OverlayRenderer for StaticRenderer {
fn resume(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
Ok(())
}
fn render(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
Ok(())
fn should_render(&mut self, _app: &mut AppState) -> anyhow::Result<ShouldRender> {
Ok(ShouldRender::Unable)
}
fn view(&mut self) -> Option<Arc<ImageView>> {
Some(self.view.clone())
fn render(
&mut self,
_app: &mut AppState,
_tgt: Arc<ImageView>,
_buf: &mut CommandBuffers,
_alpha: f32,
) -> anyhow::Result<bool> {
Ok(false)
}
fn frame_transform(&mut self) -> Option<FrameTransform> {
Some(FrameTransform {
fn frame_meta(&mut self) -> Option<FrameMeta> {
Some(FrameMeta {
extent: self.view.image().extent(),
..Default::default()
})
+22 -3
View File
@@ -31,10 +31,10 @@ use crate::{
manifest::{install_manifest, uninstall_manifest},
overlay::OpenVrOverlayData,
},
overlay::OverlayData,
overlay::{OverlayData, ShouldRender},
task::{SystemTask, TaskType},
},
graphics::WlxGraphics,
graphics::{CommandBuffers, WlxGraphics},
overlays::{
toast::{Toast, ToastTopic},
watch::{watch_fade, WATCH_NAME},
@@ -321,6 +321,7 @@ pub fn openvr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
}
state.hid_provider.commit();
let mut buffers = CommandBuffers::default();
lines.update(universe.clone(), &mut overlay_mgr, &mut state)?;
@@ -344,12 +345,30 @@ pub fn openvr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
for o in overlays.iter_mut() {
if o.state.want_visible {
o.render(&mut state)?;
let ShouldRender::Should = o.should_render(&mut state)? else {
continue;
};
if !o.ensure_image_allocated(&mut state)? {
continue;
}
o.data.image_dirty = o.render(
&mut state,
o.data.image_view.as_ref().unwrap().clone(),
&mut buffers,
1.0, // alpha is instead set using OVR API
)?;
}
}
log::trace!("Rendering overlays");
if let Some(mut future) = buffers.execute_now(state.graphics.queue.clone())? {
if let Err(e) = future.flush() {
return Err(BackendError::Fatal(e.into()));
};
future.cleanup_finished();
}
overlays
.iter_mut()
.for_each(|o| o.after_render(universe.clone(), &mut overlay_mgr, &state.graphics));
+25 -12
View File
@@ -1,4 +1,5 @@
use core::f32;
use std::sync::Arc;
use glam::Vec4;
use ovr_overlay::{
@@ -6,7 +7,7 @@ use ovr_overlay::{
pose::Matrix3x4,
sys::{ETrackingUniverseOrigin, VRVulkanTextureData_t},
};
use vulkano::{Handle, VulkanObject};
use vulkano::{image::view::ImageView, Handle, VulkanObject};
use crate::{
backend::overlay::{OverlayData, RelativeTo},
@@ -19,12 +20,13 @@ use super::helpers::Affine3AConvert;
#[derive(Default)]
pub(super) struct OpenVrOverlayData {
pub(super) handle: Option<OverlayHandle>,
pub(super) last_image: Option<u64>,
pub(super) visible: bool,
pub(super) color: Vec4,
pub(crate) width: f32,
pub(super) override_width: bool,
pub(super) relative_to: RelativeTo,
pub(super) image_view: Option<Arc<ImageView>>,
pub(super) image_dirty: bool,
}
impl OverlayData<OpenVrOverlayData> {
@@ -61,6 +63,20 @@ impl OverlayData<OpenVrOverlayData> {
Ok(handle)
}
pub(super) fn ensure_image_allocated(&mut self, app: &mut AppState) -> anyhow::Result<bool> {
if self.data.image_view.is_some() {
return Ok(true);
}
let Some(meta) = self.backend.frame_meta() else {
return Ok(false);
};
let image = app
.graphics
.render_texture(meta.extent[0], meta.extent[1], meta.format)?;
self.data.image_view = Some(ImageView::new_default(image)?);
Ok(true)
}
pub(super) fn after_input(
&mut self,
overlay: &mut OverlayManager,
@@ -200,7 +216,7 @@ impl OverlayData<OpenVrOverlayData> {
let effective = self.state.transform
* self
.backend
.frame_transform()
.frame_meta()
.map(|f| f.transform)
.unwrap_or_default();
@@ -221,21 +237,17 @@ impl OverlayData<OpenVrOverlayData> {
return;
};
let Some(view) = self.backend.view() else {
let Some(view) = self.data.image_view.as_ref() else {
log::debug!("{}: Not rendered", self.state.name);
return;
};
let image = view.image().clone();
let raw_image = image.handle().as_raw();
if let Some(last_image) = self.data.last_image {
if last_image == raw_image {
return;
}
if !self.data.image_dirty {
return;
}
self.data.image_dirty = false;
let image = view.image().clone();
let dimensions = image.extent();
if !self.data.override_width {
let new_width = ((dimensions[0] as f32) / (dimensions[1] as f32)).min(1.0);
@@ -246,6 +258,7 @@ impl OverlayData<OpenVrOverlayData> {
}
}
let raw_image = image.handle().as_raw();
let format = image.format();
let mut texture = VRVulkanTextureData_t {
+1 -1
View File
@@ -475,7 +475,7 @@ fn is_bool(maybe_type_str: &Option<String>) -> bool {
.as_ref()
.unwrap() // want panic
.split('/')
.last()
.next_back()
.map(|last| matches!(last, "click" | "touch"))
.unwrap_or(false)
}
+64 -40
View File
@@ -9,55 +9,53 @@ use std::{
},
};
use vulkano::{command_buffer::CommandBufferUsage, format::Format, image::view::ImageView};
use vulkano::command_buffer::CommandBufferUsage;
use crate::{
backend::openxr::helpers,
graphics::{WlxCommandBuffer, WlxGraphics},
graphics::{CommandBuffers, WlxGraphics, WlxPipeline, SWAPCHAIN_FORMAT},
};
use super::{
swapchain::{create_swapchain_render_data, SwapchainOpts, SwapchainRenderData},
swapchain::{create_swapchain, SwapchainOpts, WlxSwapchain},
CompositionLayer, XrState,
};
static LINE_AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(1);
pub(super) const LINE_WIDTH: f32 = 0.002;
// TODO customizable colors
static COLORS: [[f32; 6]; 5] = {
[
[1., 1., 1., 1., 0., 0.],
[0., 0.375, 0.5, 1., 0., 0.],
[0.69, 0.188, 0., 1., 0., 0.],
[0.375, 0., 0.5, 1., 0., 0.],
[1., 0., 0., 1., 0., 0.],
]
};
pub(super) struct LinePool {
lines: IdMap<usize, LineContainer>,
colors: Vec<Arc<ImageView>>,
pipeline: Arc<WlxPipeline>,
}
impl LinePool {
pub(super) fn new(graphics: Arc<WlxGraphics>) -> anyhow::Result<Self> {
let mut command_buffer =
graphics.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
let Ok(shaders) = graphics.shared_shaders.read() else {
anyhow::bail!("Failed to lock shared shaders for reading");
};
// TODO customizable colors
let colors = [
[0xff, 0xff, 0xff, 0xff],
[0x00, 0x60, 0x80, 0xff],
[0xB0, 0x30, 0x00, 0xff],
[0x60, 0x00, 0x80, 0xff],
[0xff, 0x00, 0x00, 0xff],
];
let views: anyhow::Result<Vec<Arc<ImageView>>> = colors
.into_iter()
.map(
|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),
},
)
.collect();
command_buffer.build_and_execute_now()?;
let pipeline = graphics.create_pipeline(
shaders.get("vert_common").unwrap().clone(), // want panic
shaders.get("frag_color").unwrap().clone(), // want panic
SWAPCHAIN_FORMAT,
None,
)?;
Ok(LinePool {
lines: IdMap::new(),
colors: views?,
pipeline,
})
}
@@ -68,8 +66,7 @@ impl LinePool {
) -> anyhow::Result<usize> {
let id = LINE_AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed);
let srd =
create_swapchain_render_data(xr, graphics, [1, 1, 1], SwapchainOpts::new().srgb())?;
let srd = create_swapchain(xr, graphics, [1, 1, 1], SwapchainOpts::new())?;
self.lines.insert(
id,
LineContainer {
@@ -92,7 +89,7 @@ impl LinePool {
return;
}
debug_assert!(color < self.colors.len());
debug_assert!(color < COLORS.len());
let Some(line) = self.lines.get_mut(id) else {
log::warn!("Line {} not found", id);
@@ -125,28 +122,55 @@ impl LinePool {
let posef = helpers::transform_to_posef(&transform);
line.maybe_line = Some(Line {
view: self.colors[color].clone(),
color,
pose: posef,
length: len,
});
}
pub(super) fn present_xr<'a>(
pub(super) fn render(
&mut self,
graphics: Arc<WlxGraphics>,
buf: &mut CommandBuffers,
) -> 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 set0 = self
.pipeline
.uniform_buffer(0, COLORS[inner.color].to_vec())?;
let pass = self
.pipeline
.create_pass_for_target(tgt.clone(), vec![set0])?;
let mut cmd_buffer =
graphics.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
cmd_buffer.begin_rendering(tgt)?;
cmd_buffer.run_ref(&pass)?;
cmd_buffer.end_rendering()?;
buf.push(cmd_buffer.build()?);
}
}
Ok(())
}
pub(super) fn present<'a>(
&'a mut self,
xr: &'a XrState,
command_buffer: &mut WlxCommandBuffer,
) -> anyhow::Result<Vec<CompositionLayer<'a>>> {
let mut quads = Vec::new();
for line in self.lines.values_mut() {
line.swapchain.ensure_image_released()?;
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,
1.0,
)?)
.sub_image(line.swapchain.get_subimage())
.eye_visibility(xr::EyeVisibility::BOTH)
.space(&xr.stage)
.size(xr::Extent2Df {
@@ -171,12 +195,12 @@ impl LinePool {
}
pub(super) struct Line {
pub(super) view: Arc<ImageView>,
pub(super) color: usize,
pub(super) pose: xr::Posef,
pub(super) length: f32,
}
struct LineContainer {
swapchain: SwapchainRenderData,
swapchain: WlxSwapchain,
maybe_line: Option<Line>,
}
+69 -27
View File
@@ -12,7 +12,7 @@ use glam::{Affine3A, Vec3};
use libmonado::Monado;
use openxr as xr;
use skybox::create_skybox;
use vulkano::{command_buffer::CommandBufferUsage, Handle, VulkanObject};
use vulkano::{Handle, VulkanObject};
use crate::{
backend::{
@@ -20,10 +20,10 @@ use crate::{
input::interact,
notifications::NotificationManager,
openxr::{lines::LinePool, overlay::OpenXrOverlayData},
overlay::OverlayData,
overlay::{OverlayData, ShouldRender},
task::{SystemTask, TaskType},
},
graphics::WlxGraphics,
graphics::{CommandBuffers, WlxGraphics},
overlays::{
toast::{Toast, ToastTopic},
watch::{watch_fade, WATCH_NAME},
@@ -364,23 +364,6 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
});
}
let mut layers = vec![];
let mut command_buffer = app_state
.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));
}
}
}
#[cfg(feature = "wayvr")]
if let Err(e) =
crate::overlays::wayvr::tick_events::<OpenXrOverlayData>(&mut app_state, &mut overlays)
@@ -388,7 +371,17 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
log::error!("WayVR tick_events failed: {:?}", e);
}
// Begin rendering
let mut buffers = CommandBuffers::default();
if !main_session_visible {
if let Some(skybox) = skybox.as_mut() {
skybox.render(&xr_state, &app_state, &mut buffers)?;
}
}
for o in overlays.iter_mut() {
o.data.cur_visible = false;
if !o.state.want_visible {
continue;
}
@@ -398,37 +391,85 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
o.data.init = true;
}
o.render(&mut app_state)?;
let should_render = match o.should_render(&mut app_state)? {
ShouldRender::Should => true,
ShouldRender::Can => o.data.last_alpha != o.state.alpha,
ShouldRender::Unable => false, //try show old image if exists
};
if should_render {
if !o.ensure_swapchain(&app_state, &xr_state)? {
continue;
}
let tgt = o.data.swapchain.as_mut().unwrap().acquire_wait_image()?; // want
if !o.render(&mut app_state, tgt, &mut buffers, o.state.alpha)? {
o.data.swapchain.as_mut().unwrap().ensure_image_released()?; // want
continue;
}
o.data.last_alpha = o.state.alpha;
} else if o.data.swapchain.is_none() {
continue;
}
o.data.cur_visible = true;
}
lines.render(app_state.graphics.clone(), &mut buffers)?;
let future = buffers.execute_now(app_state.graphics.queue.clone())?;
if let Some(mut future) = future {
if let Err(e) = future.flush() {
return Err(BackendError::Fatal(e.into()));
};
future.cleanup_finished();
}
// End rendering
// Layer composition
let mut layers = vec![];
if !main_session_visible {
if let Some(skybox) = skybox.as_mut() {
for (idx, layer) in skybox
.present(&xr_state, &app_state)?
.into_iter()
.enumerate()
{
layers.push((200.0 - 50.0 * (idx as f32), layer));
}
}
}
for o in overlays.iter_mut() {
if !o.data.cur_visible {
continue;
}
let dist_sq = (app_state.input_state.hmd.translation - o.state.transform.translation)
.length_squared()
+ (100f32 - o.state.z_order as f32);
if !dist_sq.is_normal() {
o.data.swapchain.as_mut().unwrap().ensure_image_released()?;
continue;
}
let maybe_layer = o.present_xr(&xr_state, &mut command_buffer)?;
let maybe_layer = o.present(&xr_state)?;
if let CompositionLayer::None = maybe_layer {
continue;
}
layers.push((dist_sq, maybe_layer));
}
for maybe_layer in lines.present_xr(&xr_state, &mut command_buffer)? {
for maybe_layer in lines.present(&xr_state)? {
if let CompositionLayer::None = maybe_layer {
continue;
}
layers.push((0.0, maybe_layer));
}
// End layer composition
#[cfg(feature = "wayvr")]
if let Some(wayvr) = &app_state.wayvr {
wayvr.borrow_mut().data.tick_finish()?;
}
command_buffer.build_and_execute_now()?;
// Begin layer submit
layers.sort_by(|a, b| b.0.total_cmp(&a.0));
let frame_ref = layers
@@ -446,6 +487,7 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
environment_blend_mode,
&frame_ref,
)?;
// End layer submit
let removed_overlays = overlays.update(&mut app_state)?;
for o in removed_overlays {
+43 -45
View File
@@ -1,74 +1,72 @@
use glam::Vec3A;
use openxr::{self as xr, CompositionLayerFlags};
use std::{f32::consts::PI, sync::Arc};
use std::f32::consts::PI;
use xr::EyeVisibility;
use super::{helpers, swapchain::SwapchainRenderData, CompositionLayer, XrState};
use super::{helpers, swapchain::WlxSwapchain, CompositionLayer, XrState};
use crate::{
backend::{
openxr::swapchain::{create_swapchain_render_data, SwapchainOpts},
openxr::swapchain::{create_swapchain, SwapchainOpts},
overlay::OverlayData,
},
graphics::WlxCommandBuffer,
state::AppState,
};
use vulkano::image::view::ImageView;
#[derive(Default)]
pub struct OpenXrOverlayData {
last_view: Option<Arc<ImageView>>,
last_visible: bool,
pub(super) swapchain: Option<SwapchainRenderData>,
pub(super) swapchain: Option<WlxSwapchain>,
pub(super) init: bool,
pub(super) cur_visible: bool,
pub(super) last_alpha: f32,
}
impl OverlayData<OpenXrOverlayData> {
pub(super) fn present_xr<'a>(
pub(super) fn ensure_swapchain<'a>(
&'a mut self,
app: &AppState,
xr: &'a XrState,
command_buffer: &mut WlxCommandBuffer,
) -> anyhow::Result<CompositionLayer<'a>> {
if let Some(new_view) = self.view() {
self.data.last_view = Some(new_view);
) -> anyhow::Result<bool> {
if self.data.swapchain.is_some() {
return Ok(true);
}
let my_view = if let Some(view) = self.data.last_view.as_ref() {
view.clone()
} else {
log::warn!("{}: Will not show - image not ready", self.state.name);
let Some(meta) = self.frame_meta() else {
log::warn!(
"{}: swapchain cannot be created due to missing metadata",
self.state.name
);
return Ok(false);
};
let extent = meta.extent;
self.data.swapchain = Some(create_swapchain(
xr,
app.graphics.clone(),
extent,
SwapchainOpts::new(),
)?);
Ok(true)
}
pub(super) fn present<'a>(
&'a mut self,
xr: &'a XrState,
) -> anyhow::Result<CompositionLayer<'a>> {
let Some(swapchain) = self.data.swapchain.as_mut() else {
log::warn!("{}: swapchain not ready", self.state.name);
return Ok(CompositionLayer::None);
};
if !swapchain.ever_acquired {
log::warn!("{}: swapchain not rendered", self.state.name);
return Ok(CompositionLayer::None);
}
swapchain.ensure_image_released()?;
let frame_transform = self.frame_transform().unwrap(); // want panic
let extent = frame_transform.extent;
let sub_image = swapchain.get_subimage();
let transform = self.state.transform * self.backend.frame_meta().unwrap().transform; // contract
let data = match self.data.swapchain {
Some(ref mut data) => data,
None => {
let srd = create_swapchain_render_data(
xr,
command_buffer.graphics.clone(),
extent,
SwapchainOpts::new(),
)?;
log::debug!(
"{}: Created swapchain {}x{}, {} images, {} MB",
self.state.name,
extent[0],
extent[1],
srd.images.len(),
extent[0] * extent[1] * 4 * srd.images.len() as u32 / 1024 / 1024
);
self.data.swapchain = Some(srd);
self.data.swapchain.as_mut().unwrap() //safe
}
};
let sub_image = data.acquire_present_release(command_buffer, my_view, self.state.alpha)?;
let transform = self.state.transform * frame_transform.transform;
let aspect_ratio = extent[1] as f32 / extent[0] as f32;
let aspect_ratio = swapchain.extent[1] as f32 / swapchain.extent[0] as f32;
let (scale_x, scale_y) = if aspect_ratio < 1.0 {
let major = transform.matrix3.col(0).length();
(major, major * aspect_ratio)
+128 -54
View File
@@ -1,25 +1,29 @@
use std::{f32::consts::PI, fs::File, sync::Arc};
use glam::{Affine3A, Quat, Vec3A};
use glam::{Quat, Vec3A};
use once_cell::sync::Lazy;
use openxr::{self as xr, CompositionLayerFlags};
use vulkano::{command_buffer::CommandBufferUsage, image::view::ImageView};
use vulkano::{
command_buffer::CommandBufferUsage, image::view::ImageView,
pipeline::graphics::color_blend::AttachmentBlend,
};
use crate::{
backend::openxr::{helpers::translation_rotation_to_posef, swapchain::SwapchainOpts},
config_io,
graphics::{dds::WlxCommandBufferDds, format_is_srgb, WlxCommandBuffer},
graphics::{dds::WlxCommandBufferDds, CommandBuffers, SWAPCHAIN_FORMAT},
state::AppState,
};
use super::{
swapchain::{create_swapchain_render_data, SwapchainRenderData},
swapchain::{create_swapchain, WlxSwapchain},
CompositionLayer, XrState,
};
pub(super) struct Skybox {
view: Arc<ImageView>,
srd: Option<(SwapchainRenderData, SwapchainRenderData)>,
sky: Option<WlxSwapchain>,
grid: Option<WlxSwapchain>,
}
impl Skybox {
@@ -66,53 +70,127 @@ impl Skybox {
let view = ImageView::new_default(maybe_image.unwrap())?; // safe unwrap
Ok(Self { view, srd: None })
Ok(Self {
view,
sky: None,
grid: None,
})
}
pub(super) fn present_xr<'a>(
fn prepare_sky<'a>(
&'a mut self,
xr: &'a XrState,
hmd: Affine3A,
command_buffer: &mut WlxCommandBuffer,
) -> anyhow::Result<Vec<CompositionLayer<'a>>> {
let (sky_image, grid_image) = if let Some((ref mut srd_sky, ref mut srd_grid)) = self.srd {
(srd_sky.present_last()?, srd_grid.present_last()?)
} else {
log::debug!("Render skybox.");
app: &AppState,
buf: &mut CommandBuffers,
) -> anyhow::Result<()> {
if self.sky.is_some() {
return Ok(());
}
let opts = SwapchainOpts::new().immutable();
let mut opts = SwapchainOpts::new().immutable();
opts.srgb = format_is_srgb(self.view.image().format());
let srd_sky = create_swapchain_render_data(
xr,
command_buffer.graphics.clone(),
self.view.image().extent(),
opts,
)?;
let srd_grid = create_swapchain_render_data(
xr,
command_buffer.graphics.clone(),
[1024, 1024, 1],
SwapchainOpts::new().immutable().grid(),
)?;
self.srd = Some((srd_sky, srd_grid));
let (srd_sky, srd_grid) = self.srd.as_mut().unwrap(); // safe unwrap
(
srd_sky.acquire_present_release(command_buffer, self.view.clone(), 1.0)?,
srd_grid.acquire_compute_release(command_buffer)?,
)
let Ok(shaders) = app.graphics.shared_shaders.read() else {
anyhow::bail!("Failed to lock shared shaders for reading");
};
let extent = self.view.image().extent();
let mut swapchain = create_swapchain(xr, app.graphics.clone(), extent, opts)?;
let tgt = swapchain.acquire_wait_image()?;
let pipeline = app.graphics.create_pipeline(
shaders.get("vert_common").unwrap().clone(), // want panic
shaders.get("frag_srgb").unwrap().clone(), // want panic
SWAPCHAIN_FORMAT,
None,
)?;
let set0 =
pipeline.uniform_sampler(0, self.view.clone(), app.graphics.texture_filtering)?;
let set1 = pipeline.uniform_buffer(1, vec![1f32])?;
let pass = pipeline.create_pass_for_target(tgt.clone(), vec![set0, set1])?;
let mut cmd_buffer = app
.graphics
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
cmd_buffer.begin_rendering(tgt)?;
cmd_buffer.run_ref(&pass)?;
cmd_buffer.end_rendering()?;
buf.push(cmd_buffer.build()?);
self.sky = Some(swapchain);
Ok(())
}
fn prepare_grid<'a>(
&'a mut self,
xr: &'a XrState,
app: &AppState,
buf: &mut CommandBuffers,
) -> anyhow::Result<()> {
if self.grid.is_some() {
return Ok(());
}
let Ok(shaders) = app.graphics.shared_shaders.read() else {
anyhow::bail!("Failed to lock shared shaders for reading");
};
let extent = [1024, 1024, 1];
let mut swapchain = create_swapchain(
xr,
app.graphics.clone(),
extent,
SwapchainOpts::new().immutable(),
)?;
let pipeline = app.graphics.create_pipeline(
shaders.get("vert_common").unwrap().clone(), // want panic
shaders.get("frag_grid").unwrap().clone(), // want panic
SWAPCHAIN_FORMAT,
Some(AttachmentBlend::alpha()),
)?;
let tgt = swapchain.acquire_wait_image()?;
let pass = pipeline.create_pass_for_target(tgt.clone(), vec![])?;
let mut cmd_buffer = app
.graphics
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
cmd_buffer.begin_rendering(tgt)?;
cmd_buffer.run_ref(&pass)?;
cmd_buffer.end_rendering()?;
buf.push(cmd_buffer.build()?);
self.grid = Some(swapchain);
Ok(())
}
pub(super) fn render(
&mut self,
xr: &XrState,
app: &AppState,
buf: &mut CommandBuffers,
) -> anyhow::Result<()> {
self.prepare_sky(xr, app, buf)?;
self.prepare_grid(xr, app, buf)?;
Ok(())
}
pub(super) fn present<'a>(
&'a mut self,
xr: &'a XrState,
app: &AppState,
) -> anyhow::Result<Vec<CompositionLayer<'a>>> {
static GRID_POSE: Lazy<xr::Posef> = Lazy::new(|| {
translation_rotation_to_posef(Vec3A::ZERO, Quat::from_rotation_x(PI * -0.5))
});
let pose = xr::Posef {
orientation: xr::Quaternionf::IDENTITY,
position: xr::Vector3f {
x: hmd.translation.x,
y: hmd.translation.y,
z: hmd.translation.z,
x: app.input_state.hmd.translation.x,
y: app.input_state.hmd.translation.y,
z: app.input_state.hmd.translation.z,
},
};
@@ -121,25 +199,20 @@ impl Skybox {
const HI_VERT_ANGLE: f32 = 0.5 * PI;
const LO_VERT_ANGLE: f32 = -0.5 * PI;
let mut layers = vec![];
self.sky.as_mut().unwrap().ensure_image_released()?;
let sky = xr::CompositionLayerEquirect2KHR::new()
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
.pose(pose)
.radius(10.0)
.sub_image(sky_image)
.sub_image(self.sky.as_ref().unwrap().get_subimage())
.eye_visibility(xr::EyeVisibility::BOTH)
.space(&xr.stage)
.central_horizontal_angle(HORIZ_ANGLE)
.upper_vertical_angle(HI_VERT_ANGLE)
.lower_vertical_angle(LO_VERT_ANGLE);
layers.push(CompositionLayer::Equirect2(sky));
static GRID_POSE: Lazy<xr::Posef> = Lazy::new(|| {
translation_rotation_to_posef(Vec3A::ZERO, Quat::from_rotation_x(PI * -0.5))
});
self.grid.as_mut().unwrap().ensure_image_released()?;
let grid = xr::CompositionLayerQuad::new()
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
.pose(*GRID_POSE)
@@ -147,13 +220,14 @@ impl Skybox {
width: 10.0,
height: 10.0,
})
.sub_image(grid_image)
.sub_image(self.grid.as_ref().unwrap().get_subimage())
.eye_visibility(xr::EyeVisibility::BOTH)
.space(&xr.stage);
layers.push(CompositionLayer::Quad(grid));
Ok(layers)
Ok(vec![
CompositionLayer::Equirect2(sky),
CompositionLayer::Quad(grid),
])
}
}
+28 -130
View File
@@ -1,26 +1,21 @@
use std::sync::Arc;
use anyhow::bail;
use ash::vk;
use openxr as xr;
use smallvec::SmallVec;
use vulkano::{
format::Format,
image::{sys::RawImage, view::ImageView, ImageCreateInfo, ImageUsage},
pipeline::graphics::color_blend::AttachmentBlend,
Handle,
};
use crate::graphics::{WlxCommandBuffer, WlxGraphics, WlxPipeline, WlxPipelineDynamic};
use crate::graphics::{WlxGraphics, SWAPCHAIN_FORMAT};
use super::XrState;
#[derive(Default)]
pub(super) struct SwapchainOpts {
pub immutable: bool,
pub srgb: bool,
pub grid: bool,
}
impl SwapchainOpts {
@@ -31,22 +26,14 @@ impl SwapchainOpts {
self.immutable = true;
self
}
pub fn srgb(mut self) -> Self {
self.srgb = true;
self
}
pub fn grid(mut self) -> Self {
self.grid = true;
self
}
}
pub(super) fn create_swapchain_render_data(
pub(super) fn create_swapchain(
xr: &XrState,
graphics: Arc<WlxGraphics>,
extent: [u32; 3],
opts: SwapchainOpts,
) -> anyhow::Result<SwapchainRenderData> {
) -> anyhow::Result<WlxSwapchain> {
let create_flags = if opts.immutable {
xr::SwapchainCreateFlags::STATIC_IMAGE
} else {
@@ -56,7 +43,7 @@ pub(super) fn create_swapchain_render_data(
let swapchain = xr.session.create_swapchain(&xr::SwapchainCreateInfo {
create_flags,
usage_flags: xr::SwapchainUsageFlags::COLOR_ATTACHMENT | xr::SwapchainUsageFlags::SAMPLED,
format: Format::R8G8B8A8_SRGB as _,
format: SWAPCHAIN_FORMAT as _,
sample_count: 1,
width: extent[0],
height: extent[1],
@@ -65,28 +52,6 @@ pub(super) fn create_swapchain_render_data(
mip_count: 1,
})?;
let Ok(shaders) = graphics.shared_shaders.read() else {
bail!("Failed to lock shared shaders for reading");
};
let image_fmt = if opts.srgb {
Format::R8G8B8A8_SRGB
} else {
Format::R8G8B8A8_UNORM
};
let frag_shader = if opts.grid {
"frag_grid"
} else {
"frag_swapchain"
};
let pipeline = graphics.create_pipeline_dynamic(
shaders.get("vert_common").unwrap().clone(), // want panic
shaders.get(frag_shader).unwrap().clone(), // want panic
image_fmt,
Some(AttachmentBlend::alpha()),
)?;
let images = swapchain
.enumerate_images()?
.into_iter()
@@ -98,7 +63,7 @@ pub(super) fn create_swapchain_render_data(
graphics.device.clone(),
vk_image,
ImageCreateInfo {
format: image_fmt, // actually SRGB but we lie
format: SWAPCHAIN_FORMAT as _,
extent,
usage: ImageUsage::COLOR_ATTACHMENT,
..Default::default()
@@ -111,118 +76,51 @@ pub(super) fn create_swapchain_render_data(
})
.collect::<anyhow::Result<SmallVec<[Arc<ImageView>; 4]>>>()?;
Ok(SwapchainRenderData {
Ok(WlxSwapchain {
acquired: false,
ever_acquired: false,
swapchain,
pipeline,
images,
extent,
target_extent: [0, 0, 0],
})
}
pub(super) struct SwapchainRenderData {
pub(super) struct WlxSwapchain {
acquired: bool,
pub(super) ever_acquired: bool,
pub(super) swapchain: xr::Swapchain<xr::Vulkan>,
pub(super) pipeline: Arc<WlxPipeline<WlxPipelineDynamic>>,
pub(super) extent: [u32; 3],
pub(super) target_extent: [u32; 3],
pub(super) images: SmallVec<[Arc<ImageView>; 4]>,
}
impl SwapchainRenderData {
pub(super) fn acquire_present_release(
&mut self,
command_buffer: &mut WlxCommandBuffer,
view: Arc<ImageView>,
alpha: f32,
) -> anyhow::Result<xr::SwapchainSubImage<xr::Vulkan>> {
impl WlxSwapchain {
pub(super) fn acquire_wait_image(&mut self) -> anyhow::Result<Arc<ImageView>> {
let idx = self.swapchain.acquire_image()? as usize;
self.swapchain.wait_image(xr::Duration::INFINITE)?;
let render_target = &mut self.images[idx];
command_buffer.begin_rendering(render_target.clone())?;
self.target_extent = render_target.image().extent();
let set0 = self.pipeline.uniform_sampler(
0,
view.clone(),
command_buffer.graphics.texture_filtering,
)?;
let set1 = self.pipeline.uniform_buffer(1, vec![alpha])?;
let pass = self.pipeline.create_pass(
[self.target_extent[0] as _, self.target_extent[1] as _],
command_buffer.graphics.quad_verts.clone(),
command_buffer.graphics.quad_indices.clone(),
vec![set0, set1],
)?;
command_buffer.run_ref(&pass)?;
command_buffer.end_rendering()?;
self.swapchain.release_image()?;
Ok(xr::SwapchainSubImage::new()
.swapchain(&self.swapchain)
.image_rect(xr::Rect2Di {
offset: xr::Offset2Di { x: 0, y: 0 },
extent: xr::Extent2Di {
width: self.target_extent[0] as _,
height: self.target_extent[1] as _,
},
})
.image_array_index(0))
self.ever_acquired = true;
self.acquired = true;
Ok(self.images[idx].clone())
}
pub(super) fn acquire_compute_release(
&mut self,
command_buffer: &mut WlxCommandBuffer,
) -> anyhow::Result<xr::SwapchainSubImage<xr::Vulkan>> {
let idx = self.swapchain.acquire_image()? as usize;
self.swapchain.wait_image(xr::Duration::INFINITE)?;
let render_target = &mut self.images[idx];
command_buffer.begin_rendering(render_target.clone())?;
self.target_extent = render_target.image().extent();
let pass = self.pipeline.create_pass(
[self.target_extent[0] as _, self.target_extent[1] as _],
command_buffer.graphics.quad_verts.clone(),
command_buffer.graphics.quad_indices.clone(),
vec![],
)?;
command_buffer.run_ref(&pass)?;
command_buffer.end_rendering()?;
self.swapchain.release_image()?;
Ok(xr::SwapchainSubImage::new()
.swapchain(&self.swapchain)
.image_rect(xr::Rect2Di {
offset: xr::Offset2Di { x: 0, y: 0 },
extent: xr::Extent2Di {
width: self.target_extent[0] as _,
height: self.target_extent[1] as _,
},
})
.image_array_index(0))
pub(super) fn ensure_image_released(&mut self) -> anyhow::Result<()> {
if self.acquired {
self.swapchain.release_image()?;
self.acquired = false;
}
Ok(())
}
pub(super) fn present_last(&self) -> anyhow::Result<xr::SwapchainSubImage<xr::Vulkan>> {
debug_assert!(
self.target_extent[0] * self.target_extent[1] != 0,
"present_last: target_extent zero"
);
Ok(xr::SwapchainSubImage::new()
pub(super) fn get_subimage(&self) -> xr::SwapchainSubImage<xr::Vulkan> {
debug_assert!(self.ever_acquired, "swapchain was never acquired!");
xr::SwapchainSubImage::new()
.swapchain(&self.swapchain)
.image_rect(xr::Rect2Di {
offset: xr::Offset2Di { x: 0, y: 0 },
extent: xr::Extent2Di {
width: self.target_extent[0] as _,
height: self.target_extent[1] as _,
width: self.extent[0] as _,
height: self.extent[1] as _,
},
})
.image_array_index(0))
.image_array_index(0)
}
}
+61 -25
View File
@@ -9,10 +9,11 @@ use std::{
use anyhow::Ok;
use glam::{Affine2, Affine3A, Mat3A, Quat, Vec2, Vec3, Vec3A};
use serde::Deserialize;
use vulkano::image::view::ImageView;
use vulkano::{format::Format, image::view::ImageView};
use crate::{
config::AStrMapExt,
graphics::CommandBuffers,
state::{AppState, KeyboardFocus},
};
@@ -234,14 +235,20 @@ where
}
self.backend.init(app)
}
pub fn render(&mut self, app: &mut AppState) -> anyhow::Result<()> {
self.backend.render(app)
pub fn should_render(&mut self, app: &mut AppState) -> anyhow::Result<ShouldRender> {
self.backend.should_render(app)
}
pub fn view(&mut self) -> Option<Arc<ImageView>> {
self.backend.view()
pub fn render(
&mut self,
app: &mut AppState,
tgt: Arc<ImageView>,
buf: &mut CommandBuffers,
alpha: f32,
) -> anyhow::Result<bool> {
self.backend.render(app, tgt, buf, alpha)
}
pub fn frame_transform(&mut self) -> Option<FrameTransform> {
self.backend.frame_transform()
pub fn frame_meta(&mut self) -> Option<FrameMeta> {
self.backend.frame_meta()
}
pub fn set_visible(&mut self, app: &mut AppState, visible: bool) -> anyhow::Result<()> {
let old_visible = self.state.want_visible;
@@ -257,10 +264,17 @@ where
}
}
#[derive(Default)]
pub struct FrameTransform {
#[derive(Default, Clone, Copy)]
pub struct FrameMeta {
pub extent: [u32; 3],
pub transform: Affine3A,
pub format: Format,
}
pub enum ShouldRender {
Should,
Can,
Unable,
}
pub trait OverlayRenderer {
@@ -268,15 +282,24 @@ pub trait OverlayRenderer {
fn init(&mut self, app: &mut AppState) -> anyhow::Result<()>;
fn pause(&mut self, app: &mut AppState) -> anyhow::Result<()>;
fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()>;
/// Called when the presentation layer is ready to present a new frame
fn render(&mut self, app: &mut AppState) -> anyhow::Result<()>;
/// Called to retrieve the current image to be displayed
fn view(&mut self) -> Option<Arc<ImageView>>;
fn should_render(&mut self, app: &mut AppState) -> anyhow::Result<ShouldRender>;
/// Called when the contents need to be rendered to the swapchain
fn render(
&mut self,
app: &mut AppState,
tgt: Arc<ImageView>,
buf: &mut CommandBuffers,
alpha: f32,
) -> anyhow::Result<bool>;
/// Called to retrieve the effective extent of the image
/// Used for creating swapchains.
///
/// Muse not be None if view() is also not None
fn frame_transform(&mut self) -> Option<FrameTransform>;
/// Must be true if should_render was also true on the same frame.
fn frame_meta(&mut self) -> Option<FrameMeta>;
}
pub struct FallbackRenderer;
@@ -291,13 +314,19 @@ impl OverlayRenderer for FallbackRenderer {
fn resume(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
Ok(())
}
fn render(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
Ok(())
fn should_render(&mut self, _app: &mut AppState) -> anyhow::Result<ShouldRender> {
Ok(ShouldRender::Unable)
}
fn view(&mut self) -> Option<Arc<ImageView>> {
None
fn render(
&mut self,
_app: &mut AppState,
_tgt: Arc<ImageView>,
_buf: &mut CommandBuffers,
_alpha: f32,
) -> anyhow::Result<bool> {
Ok(false)
}
fn frame_transform(&mut self) -> Option<FrameTransform> {
fn frame_meta(&mut self) -> Option<FrameMeta> {
None
}
}
@@ -348,16 +377,23 @@ impl OverlayRenderer for SplitOverlayBackend {
fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()> {
self.renderer.resume(app)
}
fn render(&mut self, app: &mut AppState) -> anyhow::Result<()> {
self.renderer.render(app)
fn should_render(&mut self, app: &mut AppState) -> anyhow::Result<ShouldRender> {
self.renderer.should_render(app)
}
fn view(&mut self) -> Option<Arc<ImageView>> {
self.renderer.view()
fn render(
&mut self,
app: &mut AppState,
tgt: Arc<ImageView>,
buf: &mut CommandBuffers,
alpha: f32,
) -> anyhow::Result<bool> {
self.renderer.render(app, tgt, buf, alpha)
}
fn frame_transform(&mut self) -> Option<FrameTransform> {
self.renderer.frame_transform()
fn frame_meta(&mut self) -> Option<FrameMeta> {
self.renderer.frame_meta()
}
}
impl InteractionHandler for SplitOverlayBackend {
fn on_left(&mut self, app: &mut AppState, pointer: usize) {
self.interaction.on_left(app, pointer);
+12 -44
View File
@@ -1,7 +1,6 @@
use std::sync::Arc;
use vulkano::{
command_buffer::CommandBufferUsage,
image::{view::ImageView, ImageUsage},
swapchain::{
acquire_next_image, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo,
@@ -19,7 +18,7 @@ use winit::{
use crate::{
config::load_custom_ui,
config_io,
graphics::{DynamicPass, DynamicPipeline, WlxGraphics, BLEND_ALPHA},
graphics::{CommandBuffers, WlxGraphics},
gui::{
canvas::Canvas,
modular::{modular_canvas, ModularData},
@@ -37,8 +36,6 @@ static LAST_SIZE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::n
struct PreviewState {
canvas: Canvas<(), ModularData>,
pipeline: Arc<DynamicPipeline>,
pass: DynamicPass,
swapchain: Arc<Swapchain>,
images: Vec<Arc<ImageView>>,
}
@@ -63,7 +60,7 @@ impl PreviewState {
window.set_min_inner_size(Some(logical_size));
window.set_max_inner_size(Some(logical_size));
LAST_SIZE.store(
(config.size[1] as u64) << 32 | config.size[0] as u64,
((config.size[1] as u64) << 32) | config.size[0] as u64,
std::sync::atomic::Ordering::Relaxed,
);
}
@@ -75,34 +72,9 @@ impl PreviewState {
let mut canvas = modular_canvas(&config.size, &config.elements, state)?;
canvas.init(state)?;
let view = canvas.view().unwrap();
let pipeline = {
let shaders = state.graphics.shared_shaders.read().unwrap();
state.graphics.create_pipeline_dynamic(
shaders.get("vert_common").unwrap().clone(), // want panic
shaders.get("frag_sprite").unwrap().clone(), // want panic
state.graphics.native_format,
Some(BLEND_ALPHA),
)
}?;
let set0 = pipeline
.uniform_sampler(0, view.clone(), pipeline.graphics.texture_filtering)
.unwrap();
let pass = pipeline
.create_pass(
[swapchain_size[0] as f32, swapchain_size[1] as f32],
state.graphics.quad_verts.clone(),
state.graphics.quad_indices.clone(),
vec![set0],
)
.unwrap();
Ok(PreviewState {
canvas,
pipeline,
pass,
swapchain,
images,
})
@@ -132,6 +104,7 @@ pub fn uidev_run(panel_name: &str) -> anyhow::Result<()> {
let mut recreate = false;
let mut last_draw = std::time::Instant::now();
#[allow(deprecated)]
event_loop.run(move |event, elwt| {
elwt.set_control_flow(ControlFlow::Poll);
@@ -182,26 +155,21 @@ pub fn uidev_run(panel_name: &str) -> anyhow::Result<()> {
Err(e) => panic!("failed to acquire next image: {e}"),
};
if let Err(e) = preview.canvas.render(&mut state) {
let mut canvas_cmd_buf = CommandBuffers::default();
let tgt = preview.images[image_index as usize].clone();
if let Err(e) = preview
.canvas
.render(&mut state, tgt, &mut canvas_cmd_buf, 1.0)
{
log::error!("failed to render canvas: {e}");
window.request_redraw();
};
let target = preview.images[image_index as usize].clone();
let mut cmd_buf = state
.graphics
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)
.unwrap();
cmd_buf.begin_rendering(target).unwrap();
cmd_buf.run_ref(&preview.pass).unwrap();
cmd_buf.end_rendering().unwrap();
last_draw = std::time::Instant::now();
let command_buffer = cmd_buf.build().unwrap();
vulkano::sync::now(graphics.device.clone())
.join(acquire_future)
.then_execute(graphics.queue.clone(), command_buffer)
canvas_cmd_buf
.execute_after(state.graphics.queue.clone(), Box::new(acquire_future))
.unwrap()
.then_swapchain_present(
graphics.queue.clone(),