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
@@ -1,8 +1,15 @@
|
||||
use super::*;
|
||||
|
||||
pub(super) fn rounded_clip_program(state: &mut Ferese, renderer: &mut GlesRenderer) -> Option<RoundedClipPrograms> {
|
||||
pub(super) fn rounded_clip_program(
|
||||
resources: &mut RenderResources,
|
||||
renderer: &mut GlesRenderer,
|
||||
) -> Option<RoundedClipPrograms> {
|
||||
let context = renderer.context_id().erased();
|
||||
if let Some(program) = state.rounded_clip_programs.get(&context) {
|
||||
if let Some(program) = resources
|
||||
.contexts
|
||||
.get(&context)
|
||||
.and_then(|programs| programs.rounded.as_ref())
|
||||
{
|
||||
return Some(program.clone());
|
||||
}
|
||||
|
||||
@@ -51,11 +58,11 @@ pub(super) fn rounded_clip_program(state: &mut Ferese, renderer: &mut GlesRender
|
||||
border,
|
||||
shadow,
|
||||
};
|
||||
state.rounded_clip_programs.insert(context, programs.clone());
|
||||
resources.contexts.entry(context).or_default().rounded = Some(programs.clone());
|
||||
Some(programs)
|
||||
}
|
||||
Err(error) => {
|
||||
if state.rounded_clip_warnings.insert(context) {
|
||||
if !std::mem::replace(&mut resources.contexts.entry(context).or_default().rounded_warned, true) {
|
||||
tracing::warn!(%error, "rounded-window shader unavailable; falling back to unrounded rendering");
|
||||
}
|
||||
None
|
||||
@@ -65,7 +72,8 @@ pub(super) fn rounded_clip_program(state: &mut Ferese, renderer: &mut GlesRender
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn window_border_element(
|
||||
state: &mut Ferese,
|
||||
resources: &mut RenderResources,
|
||||
theme: &crate::config::ThemeSettings,
|
||||
renderer: &GlesRenderer,
|
||||
id: ferese_layout::WindowId,
|
||||
geometry: Rectangle<i32, Logical>,
|
||||
@@ -98,17 +106,13 @@ pub(super) fn window_border_element(
|
||||
),
|
||||
None => (color, color, [0.0; 4]),
|
||||
};
|
||||
let (focus_from, focus_to, focus_gradient_line) = match state.theme_settings.focus_ring_gradient {
|
||||
let (focus_from, focus_to, focus_gradient_line) = match theme.focus_ring_gradient {
|
||||
Some(g) => (
|
||||
g.from.0,
|
||||
g.to.0,
|
||||
border_gradient_line(corners.rect, mode.size, output.current_transform().invert(), g.angle),
|
||||
),
|
||||
None => (
|
||||
state.theme_settings.accent_color.0,
|
||||
state.theme_settings.accent_color.0,
|
||||
[0.0; 4],
|
||||
),
|
||||
None => (theme.accent_color.0, theme.accent_color.0, [0.0; 4]),
|
||||
};
|
||||
let parameters = BorderParameters {
|
||||
geometry,
|
||||
@@ -124,7 +128,7 @@ pub(super) fn window_border_element(
|
||||
focus_mix,
|
||||
};
|
||||
let context = renderer.context_id().erased();
|
||||
let buffers = state.window_borders.entry(id).or_default();
|
||||
let buffers = &mut resources.windows.entry(id).or_default().borders;
|
||||
if !buffers.contexts.contains_key(&context) {
|
||||
let element = PixelShaderElement::new(
|
||||
programs.border.clone(),
|
||||
@@ -175,7 +179,7 @@ pub(super) fn border_uniforms(parameters: &BorderParameters) -> Vec<Uniform<'sta
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn window_tint_element(
|
||||
state: &mut Ferese,
|
||||
resources: &mut RenderResources,
|
||||
renderer: &mut GlesRenderer,
|
||||
id: ferese_layout::WindowId,
|
||||
geometry: Rectangle<i32, Logical>,
|
||||
@@ -186,14 +190,14 @@ pub(super) fn window_tint_element(
|
||||
) -> Option<PhysicalShaderElement> {
|
||||
if color[3] <= 0.0 {
|
||||
if resize_fill {
|
||||
state.window_resize_fills.remove(&id);
|
||||
resources.clear_tint(id, true);
|
||||
} else {
|
||||
state.window_dims.remove(&id);
|
||||
resources.clear_tint(id, false);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
let mode = output.current_mode()?;
|
||||
let program = material_program(state, renderer)?;
|
||||
let program = material_program(resources, renderer)?;
|
||||
let parameters = BorderParameters {
|
||||
geometry,
|
||||
clip_rect: framebuffer_clip_rect(corners.rect, mode.size, output.current_transform().invert()),
|
||||
@@ -219,9 +223,9 @@ pub(super) fn window_tint_element(
|
||||
};
|
||||
let context = renderer.context_id().erased();
|
||||
let buffers = if resize_fill {
|
||||
state.window_resize_fills.entry(id).or_default()
|
||||
&mut resources.windows.entry(id).or_default().resize_fill
|
||||
} else {
|
||||
state.window_dims.entry(id).or_default()
|
||||
&mut resources.windows.entry(id).or_default().dim
|
||||
};
|
||||
let cached = buffers.contexts.entry(context).or_insert_with(|| CachedBorder {
|
||||
element: PixelShaderElement::new(
|
||||
@@ -251,7 +255,7 @@ pub(super) fn window_tint_element(
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn window_shadow_element(
|
||||
state: &mut Ferese,
|
||||
resources: &mut RenderResources,
|
||||
renderer: &GlesRenderer,
|
||||
id: ferese_layout::WindowId,
|
||||
geometry: Rectangle<i32, Logical>,
|
||||
@@ -287,7 +291,7 @@ pub(super) fn window_shadow_element(
|
||||
color,
|
||||
};
|
||||
let context = renderer.context_id().erased();
|
||||
let buffers = state.window_shadows.entry(id).or_default();
|
||||
let buffers = &mut resources.windows.entry(id).or_default().shadow;
|
||||
if !buffers.contexts.contains_key(&context) {
|
||||
let element = PixelShaderElement::new(
|
||||
programs.shadow.clone(),
|
||||
|
||||
@@ -45,14 +45,14 @@ pub(super) fn append_material_surface(
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
if let Some(buffers) = state.material_buffers.get_mut(surface) {
|
||||
if let Some(buffers) = state.render.surfaces.get_mut(surface) {
|
||||
buffers.contexts.retain(|(_, index), _| *index < targets.len());
|
||||
}
|
||||
let material = (!materials.is_empty() && regions.is_none()).then_some(());
|
||||
let clip = material.as_ref().and_then(|_| {
|
||||
let corners = targets.first()?.1;
|
||||
let mode = output.current_mode()?;
|
||||
let program = rounded_clip_program(state, renderer)?;
|
||||
let program = rounded_clip_program(&mut state.render, renderer)?;
|
||||
Some((
|
||||
program,
|
||||
framebuffer_clip_rect(corners.rect, mode.size, output.current_transform().invert()),
|
||||
@@ -195,11 +195,13 @@ pub(super) fn material_element(
|
||||
visible_framebuffer: framebuffer_clip_rect(corners.rect, mode.size, transform),
|
||||
};
|
||||
let context = renderer.context_id().erased();
|
||||
let program = material_program(state, renderer)?;
|
||||
let blur_program = (blur > 0.0).then(|| blur_program(state, renderer)).flatten();
|
||||
let program = material_program(&mut state.render, renderer)?;
|
||||
let blur_program = (blur > 0.0)
|
||||
.then(|| blur_program(&mut state.render, renderer))
|
||||
.flatten();
|
||||
let capture_rect = framebuffer_capture_rect(parameters.sample_framebuffer);
|
||||
let capture_size = Size::from((capture_rect[2], capture_rect[3]));
|
||||
let buffers = state.material_buffers.entry(surface.clone()).or_default();
|
||||
let buffers = state.render.surfaces.entry(surface.clone()).or_default();
|
||||
let capture = if let Some(blur_program) = blur_program {
|
||||
if buffers
|
||||
.captures
|
||||
@@ -317,15 +319,19 @@ pub(super) fn material_blur_radius(style: crate::config::MaterialStyle, opacity:
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn blur_program(state: &mut Ferese, renderer: &mut GlesRenderer) -> Option<BlurProgram> {
|
||||
pub(super) fn blur_program(resources: &mut RenderResources, renderer: &mut GlesRenderer) -> Option<BlurProgram> {
|
||||
let context = renderer.context_id().erased();
|
||||
if let Some(program) = state.blur_programs.get(&context) {
|
||||
if let Some(program) = resources
|
||||
.contexts
|
||||
.get(&context)
|
||||
.and_then(|programs| programs.blur.as_ref())
|
||||
{
|
||||
return Some(program.clone());
|
||||
}
|
||||
match renderer.compile_custom_texture_shader(corner_shader(BLUR_SHADER), &blur_uniform_names()) {
|
||||
Ok(program) => {
|
||||
let program = BlurProgram(program);
|
||||
state.blur_programs.insert(context, program.clone());
|
||||
resources.contexts.entry(context).or_default().blur = Some(program.clone());
|
||||
Some(program)
|
||||
}
|
||||
Err(error) => {
|
||||
@@ -380,9 +386,16 @@ pub(super) fn expanded_blur_region(
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn material_program(state: &mut Ferese, renderer: &mut GlesRenderer) -> Option<MaterialProgram> {
|
||||
pub(super) fn material_program(
|
||||
resources: &mut RenderResources,
|
||||
renderer: &mut GlesRenderer,
|
||||
) -> Option<MaterialProgram> {
|
||||
let context = renderer.context_id().erased();
|
||||
if let Some(program) = state.material_programs.get(&context) {
|
||||
if let Some(program) = resources
|
||||
.contexts
|
||||
.get(&context)
|
||||
.and_then(|programs| programs.material.as_ref())
|
||||
{
|
||||
return Some(program.clone());
|
||||
}
|
||||
|
||||
@@ -397,7 +410,7 @@ pub(super) fn material_program(state: &mut Ferese, renderer: &mut GlesRenderer)
|
||||
match renderer.compile_custom_pixel_shader(corner_shader(MATERIAL_SHADER), &uniforms) {
|
||||
Ok(program) => {
|
||||
let program = MaterialProgram(program);
|
||||
state.material_programs.insert(context, program.clone());
|
||||
resources.contexts.entry(context).or_default().material = Some(program.clone());
|
||||
Some(program)
|
||||
}
|
||||
Err(error) => {
|
||||
|
||||
@@ -2,8 +2,8 @@ mod capture;
|
||||
mod decorations;
|
||||
mod materials;
|
||||
mod overview;
|
||||
mod resources;
|
||||
mod scene;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
@@ -13,9 +13,10 @@ pub(crate) use capture::{capture_resize_snapshot, capture_window_buffer, capture
|
||||
use decorations::*;
|
||||
use materials::*;
|
||||
use overview::*;
|
||||
pub(crate) use resources::RenderResources;
|
||||
use scene::cursor_elements;
|
||||
pub(crate) use scene::{
|
||||
animated_window_elements, cursorless_window_elements, frame_effect_metrics, layer_surfaces, output_elements,
|
||||
animated_window_elements, frame_effect_metrics, layer_surfaces, output_elements, sampled_output_elements,
|
||||
};
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
use smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement;
|
||||
|
||||
@@ -31,9 +31,9 @@ pub(super) fn overview_chrome_element(
|
||||
focus_mix: 0.0,
|
||||
};
|
||||
let program = if key.1 == OverviewChromePart::Outline {
|
||||
rounded_clip_program(state, renderer)?.border
|
||||
rounded_clip_program(&mut state.render, renderer)?.border
|
||||
} else {
|
||||
material_program(state, renderer)?.0
|
||||
material_program(&mut state.render, renderer)?.0
|
||||
};
|
||||
let uniforms = |p: &BorderParameters| {
|
||||
if key.1 == OverviewChromePart::Outline {
|
||||
@@ -51,7 +51,8 @@ pub(super) fn overview_chrome_element(
|
||||
};
|
||||
let context = renderer.context_id().erased();
|
||||
let chrome = &mut state
|
||||
.overview_scrims
|
||||
.render
|
||||
.outputs
|
||||
.entry(output_id)
|
||||
.or_insert_with(|| OverviewScrim { chrome: HashMap::new() })
|
||||
.chrome;
|
||||
@@ -89,15 +90,16 @@ pub(super) fn overview_strip_elements(
|
||||
output: &Output,
|
||||
output_geometry: Rectangle<i32, Logical>,
|
||||
scale: f64,
|
||||
frame: &crate::state::FrameScene,
|
||||
) -> Vec<AnimatedWindowRenderElement> {
|
||||
let alpha = state.overview.opacity();
|
||||
let alpha = frame.overview.opacity();
|
||||
if alpha <= 0.001 {
|
||||
return Vec::new();
|
||||
}
|
||||
let Some(bounds) = state.output_bounds_for(output) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let cards = state.overview_workspace_cards(output);
|
||||
let cards = state.overview_workspace_cards_for_frame(output, Some(frame));
|
||||
let strip = crate::overview::workspace_strip(bounds);
|
||||
let panel = match (cards.first(), cards.last()) {
|
||||
(Some(first), Some(last)) => ferese_layout::Rect::new(
|
||||
@@ -116,16 +118,17 @@ pub(super) fn overview_strip_elements(
|
||||
)
|
||||
.opacity;
|
||||
let mut elements = Vec::new();
|
||||
let programs = rounded_clip_program(state, renderer);
|
||||
let programs = rounded_clip_program(&mut state.render, renderer);
|
||||
// Resolve IDs once per strip, rather than scanning all managed windows
|
||||
// separately for every miniature. Own the handles so rendering can mutate state.
|
||||
let windows_by_id: std::collections::HashMap<_, _> = state
|
||||
.window_ids
|
||||
.windows
|
||||
.ids()
|
||||
.iter()
|
||||
.map(|(window, id)| (*id, window.clone()))
|
||||
.collect();
|
||||
if let Some(output_id) = state.output_id(output)
|
||||
&& let Some(cache) = state.overview_scrims.get_mut(&output_id)
|
||||
&& let Some(cache) = state.render.outputs.get_mut(&output_id)
|
||||
{
|
||||
cache.chrome.retain(|(id, part), _| match part {
|
||||
OverviewChromePart::Caption => windows_by_id.keys().any(|window| window.0 == *id),
|
||||
@@ -139,7 +142,7 @@ pub(super) fn overview_strip_elements(
|
||||
if !state.window_belongs_to_output(*id, output) {
|
||||
continue;
|
||||
}
|
||||
let Some(rect) = state.presented_window_rect(*id) else {
|
||||
let Some(rect) = frame.windows.get(id).map(|sample| sample.rect) else {
|
||||
continue;
|
||||
};
|
||||
let Some((buffer, size)) = state.overview_window_label(window, scale, rect.width) else {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -8,14 +8,6 @@ pub(crate) fn animated_window_elements(
|
||||
output_elements(state, renderer, output, true)
|
||||
}
|
||||
|
||||
pub(crate) fn cursorless_window_elements(
|
||||
state: &mut Ferese,
|
||||
renderer: &mut GlesRenderer,
|
||||
output: &Output,
|
||||
) -> Vec<AnimatedWindowRenderElement> {
|
||||
output_elements(state, renderer, output, false)
|
||||
}
|
||||
|
||||
pub(crate) fn frame_effect_metrics(_elements: &[AnimatedWindowRenderElement], _scale: f64) -> FrameEffectMetrics {
|
||||
FrameEffectMetrics
|
||||
}
|
||||
@@ -25,9 +17,20 @@ pub(crate) fn output_elements(
|
||||
renderer: &mut GlesRenderer,
|
||||
output: &Output,
|
||||
include_cursor: bool,
|
||||
) -> Vec<AnimatedWindowRenderElement> {
|
||||
let frame = state.sample_frame(output, Duration::ZERO);
|
||||
|
||||
sampled_output_elements(state, renderer, output, include_cursor, &frame)
|
||||
}
|
||||
|
||||
pub(crate) fn sampled_output_elements(
|
||||
state: &mut Ferese,
|
||||
renderer: &mut GlesRenderer,
|
||||
output: &Output,
|
||||
include_cursor: bool,
|
||||
frame: &crate::state::FrameScene,
|
||||
) -> Vec<AnimatedWindowRenderElement> {
|
||||
if state.session_lock.active {
|
||||
state.configure_lock_surfaces();
|
||||
let Some(geometry) = state.space.output_geometry(output) else {
|
||||
return Vec::new();
|
||||
};
|
||||
@@ -83,29 +86,36 @@ pub(crate) fn output_elements(
|
||||
return Vec::new();
|
||||
};
|
||||
let scale = output.current_scale().fractional_scale();
|
||||
let rounded_clip_program = rounded_clip_program(state, renderer);
|
||||
let rounded_clip_program = rounded_clip_program(&mut state.render, renderer);
|
||||
|
||||
let mut elements = if include_cursor {
|
||||
cursor_elements(state, renderer, output_geometry, scale)
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let upper_layers: &[Layer] = if state.output_has_fullscreen(output) {
|
||||
let upper_layers: &[Layer] = if state.output_has_fullscreen_for_frame(output, frame.overview.is_presenting()) {
|
||||
&[Layer::Overlay]
|
||||
} else {
|
||||
&[Layer::Overlay, Layer::Top]
|
||||
};
|
||||
elements.extend(layer_elements(state, renderer, output, upper_layers));
|
||||
if state.overview.is_presenting() {
|
||||
elements.extend(overview_strip_elements(state, renderer, output, output_geometry, scale));
|
||||
if frame.overview.is_presenting() {
|
||||
elements.extend(overview_strip_elements(
|
||||
state,
|
||||
renderer,
|
||||
output,
|
||||
output_geometry,
|
||||
scale,
|
||||
frame,
|
||||
));
|
||||
}
|
||||
let mut candidates = if state.overview.is_active() {
|
||||
state.window_ids.keys().cloned().collect::<Vec<_>>()
|
||||
state.windows.ids().keys().cloned().collect::<Vec<_>>()
|
||||
} else {
|
||||
state.space.elements().rev().cloned().collect::<Vec<_>>()
|
||||
};
|
||||
if state.overview.is_active() {
|
||||
candidates.sort_by_key(|window| std::cmp::Reverse(state.window_ids.get(window).map_or(0, |id| id.0)));
|
||||
candidates.sort_by_key(|window| std::cmp::Reverse(state.windows.ids().get(window).map_or(0, |id| id.0)));
|
||||
}
|
||||
let windows = candidates
|
||||
.iter()
|
||||
@@ -115,20 +125,17 @@ pub(crate) fn output_elements(
|
||||
if !state.window_content_ready(window) {
|
||||
return None;
|
||||
}
|
||||
let id = *state.window_ids.get(window)?;
|
||||
let id = *state.windows.ids().get(window)?;
|
||||
// Scrolling columns may sit outside their monitor's rectangle.
|
||||
// They must not reappear on a neighboring output just because
|
||||
// their global animated coordinates overlap it.
|
||||
if !state.window_belongs_to_output(id, output) {
|
||||
return None;
|
||||
}
|
||||
let visual = state.presented_window_rect(id)?;
|
||||
let (close_scale, close_alpha) = state.closing_visual(id);
|
||||
let visual = scaled_visual_rect(visual, close_scale);
|
||||
let decoration_progress = state
|
||||
.window_geometry
|
||||
.get(&id)
|
||||
.map_or(1.0, |geometry| geometry.decorations.clamp(0.0, 1.0));
|
||||
let sample = frame.windows.get(&id)?;
|
||||
let visual = scaled_visual_rect(sample.rect, sample.close_scale);
|
||||
let close_alpha = sample.close_alpha;
|
||||
let decoration_progress = sample.geometry.decorations.clamp(0.0, 1.0);
|
||||
|
||||
Some((window.clone(), id, visual, decoration_progress, close_alpha))
|
||||
})
|
||||
@@ -151,12 +158,12 @@ pub(crate) fn output_elements(
|
||||
let corners = RoundedRect::new(visual, output_geometry.loc, scale, window_radius);
|
||||
let pixels = corners.rect;
|
||||
// Only overview/close intentionally scale the complete application.
|
||||
let scale_content = state.overview.is_presenting() || state.closing_visual(id).0 != 1.0;
|
||||
let scale_content = frame.overview.is_presenting() || frame.windows[&id].close_scale != 1.0;
|
||||
let behavior = resize_content_behavior(scale_content);
|
||||
|
||||
let dim = state.window_dimming.get(&id).map_or(0.0, |dim| dim.current);
|
||||
let dim = frame.windows[&id].dim;
|
||||
if let Some(overlay) = window_tint_element(
|
||||
state,
|
||||
&mut state.render,
|
||||
renderer,
|
||||
id,
|
||||
constrain,
|
||||
@@ -175,22 +182,15 @@ pub(crate) fn output_elements(
|
||||
let shadow_blur = state.theme_settings.shadow_blur;
|
||||
let shadow_opacity = state.theme_settings.shadow_opacity;
|
||||
let shadow_color = state.theme_settings.shadow_color.0;
|
||||
let focused = if state.overview.is_active() {
|
||||
state.overview_selected(id)
|
||||
} else {
|
||||
state.focused_window == Some(id)
|
||||
};
|
||||
let focus = state
|
||||
.window_focus
|
||||
.get(&id)
|
||||
.map_or(if focused { 1.0 } else { 0.0 }, |v| v.current);
|
||||
let focus = frame.windows[&id].focus;
|
||||
let border_width = state.theme_settings.border_width
|
||||
+ (state.theme_settings.focus_ring_width - state.theme_settings.border_width) * focus;
|
||||
let border_color = state.theme_settings.border_color.0;
|
||||
let gradient = state.theme_settings.border_gradient;
|
||||
|
||||
if let Some(border) = window_border_element(
|
||||
state,
|
||||
&mut state.render,
|
||||
&state.theme_settings,
|
||||
renderer,
|
||||
id,
|
||||
constrain,
|
||||
@@ -207,7 +207,7 @@ pub(crate) fn output_elements(
|
||||
elements.push(border.into());
|
||||
}
|
||||
let shadow = window_shadow_element(
|
||||
state,
|
||||
&mut state.render,
|
||||
renderer,
|
||||
id,
|
||||
constrain,
|
||||
@@ -221,7 +221,7 @@ pub(crate) fn output_elements(
|
||||
&programs,
|
||||
);
|
||||
if !scale_content
|
||||
&& let Some(snapshot) = state.resize_snapshots.get(&id)
|
||||
&& let Some(snapshot) = state.render.snapshot(&id)
|
||||
&& snapshot.context == renderer.context_id().erased()
|
||||
&& (snapshot.scale - scale).abs() < 0.001
|
||||
{
|
||||
@@ -260,10 +260,7 @@ pub(crate) fn output_elements(
|
||||
corners,
|
||||
scale,
|
||||
close_alpha,
|
||||
state
|
||||
.window_geometry
|
||||
.get(&id)
|
||||
.is_some_and(|geometry| geometry.presentation_changed),
|
||||
frame.windows[&id].geometry.presentation_changed,
|
||||
output,
|
||||
programs.clone(),
|
||||
behavior,
|
||||
@@ -275,13 +272,15 @@ pub(crate) fn output_elements(
|
||||
{
|
||||
let mut color = state.theme_settings.surface_base_color.0;
|
||||
color[3] = close_alpha;
|
||||
if let Some(fill) = window_tint_element(state, renderer, id, constrain, corners, color, true, output) {
|
||||
if let Some(fill) =
|
||||
window_tint_element(&mut state.render, renderer, id, constrain, corners, color, true, output)
|
||||
{
|
||||
// Front-to-back: fill uncovered strips behind the native
|
||||
// content instead of stretching it or exposing wallpaper.
|
||||
elements.push(fill.into());
|
||||
}
|
||||
} else {
|
||||
state.window_resize_fills.remove(&id);
|
||||
state.render.clear_tint(id, true);
|
||||
}
|
||||
if let Some(surface) = material_surface
|
||||
&& let Some((background, _)) = material_element(
|
||||
@@ -370,7 +369,7 @@ pub(super) fn layer_elements(
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
state.material_buffers.retain(|surface, _| surface.is_alive());
|
||||
state.render.surfaces.retain(|surface, _| surface.is_alive());
|
||||
|
||||
let mut elements = Vec::new();
|
||||
for (geometry, layer) in layers {
|
||||
|
||||
Reference in new issue
Block a user