fix: dont overload xr runtime with notify spam
This commit is contained in:
@@ -1,9 +1,4 @@
|
||||
use std::{
|
||||
collections::{BinaryHeap, VecDeque},
|
||||
f32::consts::PI,
|
||||
sync::Arc,
|
||||
time::Instant,
|
||||
};
|
||||
use std::{f32::consts::PI, sync::Arc};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
#[cfg(feature = "openxr")]
|
||||
@@ -25,7 +20,7 @@ use crate::{
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
use super::overlay::{OverlayBackend, OverlayData, OverlayState};
|
||||
use super::overlay::OverlayData;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum BackendError {
|
||||
@@ -351,90 +346,6 @@ pub enum OverlaySelector {
|
||||
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(
|
||||
source: &Affine3A,
|
||||
source_fwd: Vec3A,
|
||||
|
||||
@@ -4,12 +4,12 @@ use glam::{Affine3A, Vec2, Vec3, Vec3A};
|
||||
|
||||
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::overlays::anchor::ANCHOR_NAME;
|
||||
use crate::state::AppState;
|
||||
|
||||
use super::common::TaskContainer;
|
||||
use super::task::{TaskContainer, TaskType};
|
||||
use super::{
|
||||
common::{raycast_cylinder, raycast_plane, OverlayContainer},
|
||||
overlay::OverlayData,
|
||||
|
||||
@@ -15,3 +15,5 @@ pub mod uidev;
|
||||
pub mod osc;
|
||||
|
||||
pub mod overlay;
|
||||
|
||||
pub mod task;
|
||||
|
||||
@@ -4,7 +4,7 @@ use glam::Affine3A;
|
||||
use ovr_overlay::{pose::Matrix3x4, settings::SettingsManager, sys::HmdMatrix34_t};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::backend::common::{BackendError, ColorChannel};
|
||||
use crate::backend::{common::BackendError, task::ColorChannel};
|
||||
|
||||
pub trait Affine3AConvert {
|
||||
fn from_affine(affine: &Affine3A) -> Self;
|
||||
|
||||
@@ -20,7 +20,7 @@ use vulkano::{
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
common::SystemTask,
|
||||
common::{BackendError, OverlayContainer},
|
||||
input::interact,
|
||||
notifications::NotificationManager,
|
||||
openvr::{
|
||||
@@ -31,6 +31,7 @@ use crate::{
|
||||
overlay::OpenVrOverlayData,
|
||||
},
|
||||
overlay::OverlayData,
|
||||
task::{SystemTask, TaskType},
|
||||
},
|
||||
graphics::WlxGraphics,
|
||||
overlays::{
|
||||
@@ -40,8 +41,6 @@ use crate::{
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
use super::common::{BackendError, OverlayContainer, TaskType};
|
||||
|
||||
pub mod helpers;
|
||||
pub mod input;
|
||||
pub mod lines;
|
||||
|
||||
@@ -13,11 +13,12 @@ use vulkano::{command_buffer::CommandBufferUsage, Handle, VulkanObject};
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
common::{OverlayContainer, TaskType},
|
||||
common::{BackendError, OverlayContainer},
|
||||
input::interact,
|
||||
notifications::NotificationManager,
|
||||
openxr::{input::DoubleClickCounter, lines::LinePool, overlay::OpenXrOverlayData},
|
||||
overlay::OverlayData,
|
||||
task::TaskType,
|
||||
},
|
||||
graphics::WlxGraphics,
|
||||
overlays::{
|
||||
@@ -27,8 +28,6 @@ use crate::{
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
use super::common::BackendError;
|
||||
|
||||
mod helpers;
|
||||
mod input;
|
||||
mod lines;
|
||||
|
||||
@@ -124,7 +124,7 @@ impl OverlayState {
|
||||
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 {
|
||||
self.realign(&app.input_state.hmd);
|
||||
|
||||
113
src/backend/task.rs
Normal file
113
src/backend/task.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,10 @@ use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
common::{ColorChannel, OverlaySelector, SystemTask, TaskType},
|
||||
common::OverlaySelector,
|
||||
input::PointerMode,
|
||||
overlay::RelativeTo,
|
||||
task::{ColorChannel, SystemTask, TaskType},
|
||||
},
|
||||
config::{save_settings, save_state, AStrSetExt},
|
||||
overlays::{
|
||||
|
||||
@@ -9,10 +9,11 @@ use wlx_capture::pipewire::{pipewire_select_screen, PipewireCapture, PipewireSel
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
common::{OverlaySelector, TaskType},
|
||||
common::OverlaySelector,
|
||||
overlay::{
|
||||
ui_transform, OverlayBackend, OverlayRenderer, OverlayState, SplitOverlayBackend,
|
||||
},
|
||||
task::TaskType,
|
||||
},
|
||||
state::{AppSession, AppState},
|
||||
};
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
use std::{
|
||||
f32::consts::PI,
|
||||
ops::Add,
|
||||
sync::{atomic::AtomicUsize, Arc},
|
||||
time::Instant,
|
||||
};
|
||||
use std::{f32::consts::PI, ops::Add, sync::Arc, time::Instant};
|
||||
|
||||
use glam::{vec3a, Quat, Vec3A};
|
||||
use idmap_derive::IntegerId;
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
common::{OverlaySelector, TaskType},
|
||||
common::OverlaySelector,
|
||||
overlay::{OverlayBackend, OverlayState, RelativeTo},
|
||||
task::TaskType,
|
||||
},
|
||||
gui::{color_parse, CanvasBuilder},
|
||||
state::{AppState, LeftRight},
|
||||
@@ -22,8 +19,7 @@ const FONT_SIZE: isize = 16;
|
||||
const PADDING: (f32, f32) = (25., 7.);
|
||||
const PIXELS_TO_METERS: f32 = 1. / 2000.;
|
||||
const TOAST_AUDIO_WAV: &[u8] = include_bytes!("../res/557297.wav");
|
||||
|
||||
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0);
|
||||
static TOAST_NAME: Lazy<Arc<str>> = Lazy::new(|| "toast".into());
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub enum DisplayMethod {
|
||||
@@ -77,36 +73,49 @@ impl Toast {
|
||||
self.submit_at(app, Instant::now());
|
||||
}
|
||||
pub fn submit_at(self, app: &mut AppState, instant: Instant) {
|
||||
let auto_increment = AUTO_INCREMENT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
let name: Arc<str> = format!("toast-{}", auto_increment).into();
|
||||
let selector = OverlaySelector::Name(name.clone());
|
||||
let selector = OverlaySelector::Name(TOAST_NAME.clone());
|
||||
|
||||
let destroy_at = instant.add(std::time::Duration::from_secs_f32(self.timeout));
|
||||
|
||||
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(
|
||||
TaskType::CreateOverlay(
|
||||
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,
|
||||
);
|
||||
|
||||
app.tasks
|
||||
.enqueue_at(TaskType::DropOverlay(selector), destroy_at);
|
||||
|
||||
if has_sound {
|
||||
app.audio.play(TOAST_AUDIO_WAV);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn new_toast(
|
||||
toast: Toast,
|
||||
name: Arc<str>,
|
||||
app: &mut AppState,
|
||||
) -> Option<(OverlayState, Box<dyn OverlayBackend>)> {
|
||||
fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn OverlayBackend>)> {
|
||||
let current_method = app
|
||||
.session
|
||||
.toast_topics
|
||||
@@ -186,7 +195,7 @@ fn new_toast(
|
||||
}
|
||||
|
||||
let state = OverlayState {
|
||||
name,
|
||||
name: TOAST_NAME.clone(),
|
||||
want_visible: true,
|
||||
spawn_scale: size.0 * PIXELS_TO_METERS,
|
||||
spawn_rotation,
|
||||
|
||||
@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::{
|
||||
backend::{common::TaskContainer, input::InputState},
|
||||
backend::{input::InputState, task::TaskContainer},
|
||||
config::GeneralConfig,
|
||||
config_io,
|
||||
graphics::WlxGraphics,
|
||||
|
||||
Reference in New Issue
Block a user