Refactor window ownership and immutable frame sampling
This commit is contained in:
1 parent
c0010ccbb6
commit
07bda5a21c
31 files changed
+1222
-623
No files matched your search
@@ -0,0 +1,176 @@
|
||||
use ferese_core::OutputId;
|
||||
use ferese_layout::WindowId;
|
||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Resource ownership follows the lifetime of a window, output, surface or GL
|
||||
/// context. Desktop policy and animation values do not live in these caches.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct RenderResources {
|
||||
pub(super) windows: HashMap<WindowId, WindowBuffers>,
|
||||
pub(super) contexts: HashMap<ErasedContextId, ContextPrograms>,
|
||||
pub(super) outputs: HashMap<OutputId, OverviewScrim>,
|
||||
pub(super) surfaces: HashMap<WlSurface, MaterialBuffers>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct WindowBuffers {
|
||||
pub borders: WindowBorderBuffers,
|
||||
pub dim: WindowBorderBuffers,
|
||||
pub resize_fill: WindowBorderBuffers,
|
||||
pub shadow: WindowShadowBuffers,
|
||||
snapshot: Option<ResizeSnapshot>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct ContextPrograms {
|
||||
pub rounded: Option<RoundedClipPrograms>,
|
||||
pub rounded_warned: bool,
|
||||
pub material: Option<MaterialProgram>,
|
||||
pub blur: Option<BlurProgram>,
|
||||
}
|
||||
|
||||
impl RenderResources {
|
||||
pub fn remove_window(&mut self, id: WindowId) {
|
||||
self.windows.remove(&id);
|
||||
}
|
||||
|
||||
pub fn remove_output(&mut self, id: OutputId) {
|
||||
self.outputs.remove(&id);
|
||||
}
|
||||
|
||||
pub fn snapshot(&self, id: &WindowId) -> Option<&ResizeSnapshot> {
|
||||
self.windows.get(id)?.snapshot.as_ref()
|
||||
}
|
||||
|
||||
pub fn snapshots(&self) -> impl Iterator<Item = &ResizeSnapshot> {
|
||||
self.windows.values().filter_map(|window| window.snapshot.as_ref())
|
||||
}
|
||||
|
||||
pub fn set_snapshot(&mut self, id: WindowId, snapshot: ResizeSnapshot) {
|
||||
self.windows.entry(id).or_default().snapshot = Some(snapshot);
|
||||
}
|
||||
|
||||
pub fn clear_snapshot(&mut self, id: &WindowId) {
|
||||
if let Some(window) = self.windows.get_mut(id) {
|
||||
window.snapshot = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn retain_snapshots(&mut self, mut keep: impl FnMut(&WindowId, &mut ResizeSnapshot) -> bool) {
|
||||
for (id, window) in &mut self.windows {
|
||||
if let Some(snapshot) = &mut window.snapshot
|
||||
&& !keep(id, snapshot)
|
||||
{
|
||||
window.snapshot = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn clear_tint(&mut self, id: WindowId, resize_fill: bool) {
|
||||
let Some(window) = self.windows.get_mut(&id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if resize_fill {
|
||||
window.resize_fill.contexts.clear();
|
||||
} else {
|
||||
window.dim.contexts.clear();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn forget_context(&mut self, context: &ErasedContextId) {
|
||||
self.contexts.remove(context);
|
||||
|
||||
for window in self.windows.values_mut() {
|
||||
window.borders.contexts.remove(context);
|
||||
window.dim.contexts.remove(context);
|
||||
window.resize_fill.contexts.remove(context);
|
||||
window.shadow.contexts.remove(context);
|
||||
|
||||
if window
|
||||
.snapshot
|
||||
.as_ref()
|
||||
.is_some_and(|snapshot| &snapshot.context == context)
|
||||
{
|
||||
window.snapshot = None;
|
||||
}
|
||||
}
|
||||
|
||||
for output in self.outputs.values_mut() {
|
||||
for contexts in output.chrome.values_mut() {
|
||||
contexts.remove(context);
|
||||
}
|
||||
}
|
||||
|
||||
for surface in self.surfaces.values_mut() {
|
||||
surface.contexts.retain(|(id, _), _| id != context);
|
||||
surface.captures.remove(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn window_removal_releases_all_its_caches_without_removing_other_windows() {
|
||||
let mut resources = RenderResources::default();
|
||||
resources.windows.insert(WindowId(1), WindowBuffers::default());
|
||||
resources.windows.insert(WindowId(2), WindowBuffers::default());
|
||||
|
||||
resources.remove_window(WindowId(1));
|
||||
resources.clear_snapshot(&WindowId(1));
|
||||
resources.clear_tint(WindowId(1), true);
|
||||
|
||||
assert!(!resources.windows.contains_key(&WindowId(1)));
|
||||
assert!(resources.windows.contains_key(&WindowId(2)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "requires an EGL rendering device"]
|
||||
fn context_removal_evicts_programs_and_snapshots_without_touching_another_gpu() {
|
||||
use smithay::backend::egl::{EGLContext, EGLDevice, EGLDisplay};
|
||||
|
||||
let device = EGLDevice::enumerate().unwrap().last().expect("an EGL device");
|
||||
let display = unsafe { EGLDisplay::new(device).unwrap() };
|
||||
let mut first = unsafe { GlesRenderer::new(EGLContext::new(&display).unwrap()).unwrap() };
|
||||
let mut second = unsafe { GlesRenderer::new(EGLContext::new(&display).unwrap()).unwrap() };
|
||||
let first_id = first.context_id().erased();
|
||||
let second_id = second.context_id().erased();
|
||||
let mut resources = RenderResources::default();
|
||||
|
||||
for (id, renderer) in [(WindowId(1), &mut first), (WindowId(2), &mut second)] {
|
||||
rounded_clip_program(&mut resources, renderer).unwrap();
|
||||
material_program(&mut resources, renderer).unwrap();
|
||||
blur_program(&mut resources, renderer).unwrap();
|
||||
let texture = Offscreen::<GlesTexture>::create_buffer(renderer, Fourcc::Abgr8888, (16, 16).into()).unwrap();
|
||||
resources.set_snapshot(
|
||||
id,
|
||||
ResizeSnapshot {
|
||||
texture,
|
||||
context: renderer.context_id().erased(),
|
||||
id: Id::new(),
|
||||
commit: CommitCounter::default(),
|
||||
elapsed: Duration::ZERO,
|
||||
last_tick: None,
|
||||
scale: 1.0,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
resources.forget_context(&first_id);
|
||||
|
||||
assert!(!resources.contexts.contains_key(&first_id));
|
||||
assert!(resources.contexts.contains_key(&second_id));
|
||||
assert!(resources.snapshot(&WindowId(1)).is_none());
|
||||
assert!(resources.snapshot(&WindowId(2)).is_some());
|
||||
assert_eq!(resources.snapshots().count(), 1);
|
||||
|
||||
// Reconnecting a context can build fresh resources after eviction.
|
||||
rounded_clip_program(&mut resources, &mut first).unwrap();
|
||||
assert!(resources.contexts.contains_key(&first_id));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user