rework rendering backend
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user