fix: dont overload xr runtime with notify spam

This commit is contained in:
galister
2024-04-24 11:31:04 +09:00
parent 1fa842bb44
commit 065ce8f136
12 changed files with 161 additions and 126 deletions

View File

@@ -1,9 +1,4 @@
use std::{ use std::{f32::consts::PI, sync::Arc};
collections::{BinaryHeap, VecDeque},
f32::consts::PI,
sync::Arc,
time::Instant,
};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
#[cfg(feature = "openxr")] #[cfg(feature = "openxr")]
@@ -25,7 +20,7 @@ use crate::{
state::AppState, state::AppState,
}; };
use super::overlay::{OverlayBackend, OverlayData, OverlayState}; use super::overlay::OverlayData;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum BackendError { pub enum BackendError {
@@ -351,90 +346,6 @@ pub enum OverlaySelector {
Name(Arc<str>), Name(Arc<str>),
} }
struct AppTask {
pub not_before: Instant,
pub task: TaskType,
}
impl PartialEq<AppTask> for AppTask {
fn eq(&self, other: &Self) -> bool {
self.not_before == other.not_before
}
}
impl PartialOrd<AppTask> for AppTask {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Eq for AppTask {}
impl Ord for AppTask {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.not_before.cmp(&other.not_before).reverse()
}
}
pub enum SystemTask {
ColorGain(ColorChannel, f32),
ResetPlayspace,
FixFloor,
}
pub type OverlayTask = dyn FnOnce(&mut AppState, &mut OverlayState) + Send;
pub type CreateOverlayTask =
dyn FnOnce(&mut AppState) -> Option<(OverlayState, Box<dyn OverlayBackend>)> + Send;
pub enum TaskType {
Global(Box<dyn FnOnce(&mut AppState) + Send>),
Overlay(OverlaySelector, Box<OverlayTask>),
CreateOverlay(OverlaySelector, Box<CreateOverlayTask>),
DropOverlay(OverlaySelector),
System(SystemTask),
}
#[derive(Deserialize, Clone, Copy)]
pub enum ColorChannel {
R,
G,
B,
All,
}
pub struct TaskContainer {
tasks: BinaryHeap<AppTask>,
}
impl TaskContainer {
pub fn new() -> Self {
Self {
tasks: BinaryHeap::new(),
}
}
pub fn enqueue(&mut self, task: TaskType) {
self.tasks.push(AppTask {
not_before: Instant::now(),
task,
});
}
pub fn enqueue_at(&mut self, task: TaskType, not_before: Instant) {
self.tasks.push(AppTask { not_before, task });
}
pub fn retrieve_due(&mut self, dest_buf: &mut VecDeque<TaskType>) {
let now = Instant::now();
while let Some(task) = self.tasks.peek() {
if task.not_before > now {
break;
}
// Safe unwrap because we peeked.
dest_buf.push_back(self.tasks.pop().unwrap().task);
}
}
}
pub fn raycast_plane( pub fn raycast_plane(
source: &Affine3A, source: &Affine3A,
source_fwd: Vec3A, source_fwd: Vec3A,

View File

@@ -4,12 +4,12 @@ use glam::{Affine3A, Vec2, Vec3, Vec3A};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use crate::backend::common::{snap_upright, OverlaySelector, TaskType}; use crate::backend::common::{snap_upright, OverlaySelector};
use crate::config::{save_state, AStrMapExt, GeneralConfig}; use crate::config::{save_state, AStrMapExt, GeneralConfig};
use crate::overlays::anchor::ANCHOR_NAME; use crate::overlays::anchor::ANCHOR_NAME;
use crate::state::AppState; use crate::state::AppState;
use super::common::TaskContainer; use super::task::{TaskContainer, TaskType};
use super::{ use super::{
common::{raycast_cylinder, raycast_plane, OverlayContainer}, common::{raycast_cylinder, raycast_plane, OverlayContainer},
overlay::OverlayData, overlay::OverlayData,

View File

@@ -15,3 +15,5 @@ pub mod uidev;
pub mod osc; pub mod osc;
pub mod overlay; pub mod overlay;
pub mod task;

View File

@@ -4,7 +4,7 @@ use glam::Affine3A;
use ovr_overlay::{pose::Matrix3x4, settings::SettingsManager, sys::HmdMatrix34_t}; use ovr_overlay::{pose::Matrix3x4, settings::SettingsManager, sys::HmdMatrix34_t};
use thiserror::Error; use thiserror::Error;
use crate::backend::common::{BackendError, ColorChannel}; use crate::backend::{common::BackendError, task::ColorChannel};
pub trait Affine3AConvert { pub trait Affine3AConvert {
fn from_affine(affine: &Affine3A) -> Self; fn from_affine(affine: &Affine3A) -> Self;

View File

@@ -20,7 +20,7 @@ use vulkano::{
use crate::{ use crate::{
backend::{ backend::{
common::SystemTask, common::{BackendError, OverlayContainer},
input::interact, input::interact,
notifications::NotificationManager, notifications::NotificationManager,
openvr::{ openvr::{
@@ -31,6 +31,7 @@ use crate::{
overlay::OpenVrOverlayData, overlay::OpenVrOverlayData,
}, },
overlay::OverlayData, overlay::OverlayData,
task::{SystemTask, TaskType},
}, },
graphics::WlxGraphics, graphics::WlxGraphics,
overlays::{ overlays::{
@@ -40,8 +41,6 @@ use crate::{
state::AppState, state::AppState,
}; };
use super::common::{BackendError, OverlayContainer, TaskType};
pub mod helpers; pub mod helpers;
pub mod input; pub mod input;
pub mod lines; pub mod lines;

View File

@@ -13,11 +13,12 @@ use vulkano::{command_buffer::CommandBufferUsage, Handle, VulkanObject};
use crate::{ use crate::{
backend::{ backend::{
common::{OverlayContainer, TaskType}, common::{BackendError, OverlayContainer},
input::interact, input::interact,
notifications::NotificationManager, notifications::NotificationManager,
openxr::{input::DoubleClickCounter, lines::LinePool, overlay::OpenXrOverlayData}, openxr::{input::DoubleClickCounter, lines::LinePool, overlay::OpenXrOverlayData},
overlay::OverlayData, overlay::OverlayData,
task::TaskType,
}, },
graphics::WlxGraphics, graphics::WlxGraphics,
overlays::{ overlays::{
@@ -27,8 +28,6 @@ use crate::{
state::AppState, state::AppState,
}; };
use super::common::BackendError;
mod helpers; mod helpers;
mod input; mod input;
mod lines; mod lines;

View File

@@ -124,7 +124,7 @@ impl OverlayState {
self.saved_transform = None; self.saved_transform = None;
} }
self.transform = app.anchor * self.get_transform(); self.transform = self.parent_transform(app).unwrap_or(app.anchor) * self.get_transform();
if self.grabbable && hard_reset { if self.grabbable && hard_reset {
self.realign(&app.input_state.hmd); self.realign(&app.input_state.hmd);

113
src/backend/task.rs Normal file
View File

@@ -0,0 +1,113 @@
use std::{
cmp,
collections::{BinaryHeap, VecDeque},
sync::atomic::{self, AtomicUsize},
time::Instant,
};
use serde::Deserialize;
use crate::state::AppState;
use super::{
common::OverlaySelector,
overlay::{OverlayBackend, OverlayState},
};
static TASK_AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0);
struct AppTask {
pub not_before: Instant,
pub id: usize,
pub task: TaskType,
}
impl PartialEq<AppTask> for AppTask {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == cmp::Ordering::Equal
}
}
impl PartialOrd<AppTask> for AppTask {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Eq for AppTask {}
impl Ord for AppTask {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.not_before
.cmp(&other.not_before)
.then(self.id.cmp(&other.id))
.reverse()
}
}
pub enum SystemTask {
ColorGain(ColorChannel, f32),
ResetPlayspace,
FixFloor,
}
pub type OverlayTask = dyn FnOnce(&mut AppState, &mut OverlayState) + Send;
pub type CreateOverlayTask =
dyn FnOnce(&mut AppState) -> Option<(OverlayState, Box<dyn OverlayBackend>)> + Send;
pub enum TaskType {
Global(Box<dyn FnOnce(&mut AppState) + Send>),
Overlay(OverlaySelector, Box<OverlayTask>),
CreateOverlay(OverlaySelector, Box<CreateOverlayTask>),
DropOverlay(OverlaySelector),
System(SystemTask),
}
#[derive(Deserialize, Clone, Copy)]
pub enum ColorChannel {
R,
G,
B,
All,
}
pub struct TaskContainer {
tasks: BinaryHeap<AppTask>,
}
impl TaskContainer {
pub fn new() -> Self {
Self {
tasks: BinaryHeap::new(),
}
}
pub fn enqueue(&mut self, task: TaskType) {
self.tasks.push(AppTask {
not_before: Instant::now(),
id: TASK_AUTO_INCREMENT.fetch_add(1, atomic::Ordering::Relaxed),
task,
});
}
/// Enqueue a task to be executed at a specific time.
/// If the time is in the past, the task will be executed immediately.
/// Multiple tasks enqueued for the same instant will be executed in order of submission.
pub fn enqueue_at(&mut self, task: TaskType, not_before: Instant) {
self.tasks.push(AppTask {
not_before,
id: TASK_AUTO_INCREMENT.fetch_add(1, atomic::Ordering::Relaxed),
task,
});
}
pub fn retrieve_due(&mut self, dest_buf: &mut VecDeque<TaskType>) {
let now = Instant::now();
while let Some(task) = self.tasks.peek() {
if task.not_before > now {
break;
}
// Safe unwrap because we peeked.
dest_buf.push_back(self.tasks.pop().unwrap().task);
}
}
}

View File

@@ -11,9 +11,10 @@ use serde::Deserialize;
use crate::{ use crate::{
backend::{ backend::{
common::{ColorChannel, OverlaySelector, SystemTask, TaskType}, common::OverlaySelector,
input::PointerMode, input::PointerMode,
overlay::RelativeTo, overlay::RelativeTo,
task::{ColorChannel, SystemTask, TaskType},
}, },
config::{save_settings, save_state, AStrSetExt}, config::{save_settings, save_state, AStrSetExt},
overlays::{ overlays::{

View File

@@ -9,10 +9,11 @@ use wlx_capture::pipewire::{pipewire_select_screen, PipewireCapture, PipewireSel
use crate::{ use crate::{
backend::{ backend::{
common::{OverlaySelector, TaskType}, common::OverlaySelector,
overlay::{ overlay::{
ui_transform, OverlayBackend, OverlayRenderer, OverlayState, SplitOverlayBackend, ui_transform, OverlayBackend, OverlayRenderer, OverlayState, SplitOverlayBackend,
}, },
task::TaskType,
}, },
state::{AppSession, AppState}, state::{AppSession, AppState},
}; };

View File

@@ -1,18 +1,15 @@
use std::{ use std::{f32::consts::PI, ops::Add, sync::Arc, time::Instant};
f32::consts::PI,
ops::Add,
sync::{atomic::AtomicUsize, Arc},
time::Instant,
};
use glam::{vec3a, Quat, Vec3A}; use glam::{vec3a, Quat, Vec3A};
use idmap_derive::IntegerId; use idmap_derive::IntegerId;
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
backend::{ backend::{
common::{OverlaySelector, TaskType}, common::OverlaySelector,
overlay::{OverlayBackend, OverlayState, RelativeTo}, overlay::{OverlayBackend, OverlayState, RelativeTo},
task::TaskType,
}, },
gui::{color_parse, CanvasBuilder}, gui::{color_parse, CanvasBuilder},
state::{AppState, LeftRight}, state::{AppState, LeftRight},
@@ -22,8 +19,7 @@ const FONT_SIZE: isize = 16;
const PADDING: (f32, f32) = (25., 7.); const PADDING: (f32, f32) = (25., 7.);
const PIXELS_TO_METERS: f32 = 1. / 2000.; const PIXELS_TO_METERS: f32 = 1. / 2000.;
const TOAST_AUDIO_WAV: &[u8] = include_bytes!("../res/557297.wav"); const TOAST_AUDIO_WAV: &[u8] = include_bytes!("../res/557297.wav");
static TOAST_NAME: Lazy<Arc<str>> = Lazy::new(|| "toast".into());
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug, Clone, Copy, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum DisplayMethod { pub enum DisplayMethod {
@@ -77,36 +73,49 @@ impl Toast {
self.submit_at(app, Instant::now()); self.submit_at(app, Instant::now());
} }
pub fn submit_at(self, app: &mut AppState, instant: Instant) { pub fn submit_at(self, app: &mut AppState, instant: Instant) {
let auto_increment = AUTO_INCREMENT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); let selector = OverlaySelector::Name(TOAST_NAME.clone());
let name: Arc<str> = format!("toast-{}", auto_increment).into();
let selector = OverlaySelector::Name(name.clone());
let destroy_at = instant.add(std::time::Duration::from_secs_f32(self.timeout)); let destroy_at = instant.add(std::time::Duration::from_secs_f32(self.timeout));
let has_sound = self.sound && app.session.config.notifications_sound_enabled; let has_sound = self.sound && app.session.config.notifications_sound_enabled;
// drop any toast that was created before us.
// (DropOverlay only drops overlays that were
// created before current frame)
app.tasks
.enqueue_at(TaskType::DropOverlay(selector.clone()), instant);
// CreateOverlay only creates the overlay if
// the selector doesn't exist yet, so in case
// multiple toasts are submitted for the same
// frame, only the first one gets created
app.tasks.enqueue_at( app.tasks.enqueue_at(
TaskType::CreateOverlay( TaskType::CreateOverlay(
selector.clone(), selector.clone(),
Box::new(move |app| new_toast(self, name, app)), Box::new(move |app| {
let mut maybe_toast = new_toast(self, app);
if let Some((state, _)) = maybe_toast.as_mut() {
state.auto_movement(app);
app.tasks.enqueue_at(
// at timeout, drop the overlay by ID instead
// in order to avoid dropping any newer toasts
TaskType::DropOverlay(OverlaySelector::Id(state.id)),
destroy_at,
);
}
maybe_toast
}),
), ),
instant, instant,
); );
app.tasks
.enqueue_at(TaskType::DropOverlay(selector), destroy_at);
if has_sound { if has_sound {
app.audio.play(TOAST_AUDIO_WAV); app.audio.play(TOAST_AUDIO_WAV);
} }
} }
} }
fn new_toast( fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn OverlayBackend>)> {
toast: Toast,
name: Arc<str>,
app: &mut AppState,
) -> Option<(OverlayState, Box<dyn OverlayBackend>)> {
let current_method = app let current_method = app
.session .session
.toast_topics .toast_topics
@@ -186,7 +195,7 @@ fn new_toast(
} }
let state = OverlayState { let state = OverlayState {
name, name: TOAST_NAME.clone(),
want_visible: true, want_visible: true,
spawn_scale: size.0 * PIXELS_TO_METERS, spawn_scale: size.0 * PIXELS_TO_METERS,
spawn_rotation, spawn_rotation,

View File

@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use crate::{ use crate::{
backend::{common::TaskContainer, input::InputState}, backend::{input::InputState, task::TaskContainer},
config::GeneralConfig, config::GeneralConfig,
config_io, config_io,
graphics::WlxGraphics, graphics::WlxGraphics,