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::{
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,

View File

@@ -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,

View File

@@ -15,3 +15,5 @@ pub mod uidev;
pub mod osc;
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 thiserror::Error;
use crate::backend::common::{BackendError, ColorChannel};
use crate::backend::{common::BackendError, task::ColorChannel};
pub trait Affine3AConvert {
fn from_affine(affine: &Affine3A) -> Self;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
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::{
backend::{
common::{ColorChannel, OverlaySelector, SystemTask, TaskType},
common::OverlaySelector,
input::PointerMode,
overlay::RelativeTo,
task::{ColorChannel, SystemTask, TaskType},
},
config::{save_settings, save_state, AStrSetExt},
overlays::{

View File

@@ -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},
};

View File

@@ -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,

View File

@@ -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,