micro optimization fixes
This commit is contained in:
1 parent
0d7203f84b
commit
da60ecb719
36 files changed
+1781
-521
No files matched your search
@@ -0,0 +1,231 @@
|
||||
//! Static uniform names stay borrowed; cached frame elements share their state.
|
||||
//! Copy on write preserves the ID and commit seen by an already sampled frame.
|
||||
use std::sync::Arc;
|
||||
|
||||
use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement};
|
||||
use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesPixelProgram, GlesRenderer, Uniform};
|
||||
use smithay::backend::renderer::utils::{CommitCounter, OpaqueRegions};
|
||||
use smithay::utils::{Buffer, Logical, Physical, Rectangle, Scale, Transform};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct SharedPixelShaderElement {
|
||||
shader: GlesPixelProgram,
|
||||
state: Arc<ShaderState>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct ShaderState {
|
||||
id: Id,
|
||||
commit: CommitCounter,
|
||||
area: Rectangle<i32, Logical>,
|
||||
opaque: Vec<Rectangle<i32, Logical>>,
|
||||
alpha: f32,
|
||||
uniforms: Vec<Uniform<'static>>,
|
||||
kind: Kind,
|
||||
}
|
||||
|
||||
impl SharedPixelShaderElement {
|
||||
pub(crate) fn new(
|
||||
shader: GlesPixelProgram,
|
||||
area: Rectangle<i32, Logical>,
|
||||
opaque: Option<Vec<Rectangle<i32, Logical>>>,
|
||||
alpha: f32,
|
||||
uniforms: Vec<Uniform<'static>>,
|
||||
kind: Kind,
|
||||
) -> Self {
|
||||
Self {
|
||||
shader,
|
||||
state: Arc::new(ShaderState {
|
||||
id: Id::new(),
|
||||
commit: CommitCounter::default(),
|
||||
area,
|
||||
opaque: opaque.unwrap_or_default(),
|
||||
alpha,
|
||||
uniforms,
|
||||
kind,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resize(&mut self, area: Rectangle<i32, Logical>, opaque: Option<Vec<Rectangle<i32, Logical>>>) {
|
||||
let opaque = opaque.unwrap_or_default();
|
||||
|
||||
if self.state.area != area || self.state.opaque != opaque {
|
||||
let state = Arc::make_mut(&mut self.state);
|
||||
state.area = area;
|
||||
state.opaque = opaque;
|
||||
state.commit.increment();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn update_uniforms(&mut self, uniforms: Vec<Uniform<'static>>) {
|
||||
let state = Arc::make_mut(&mut self.state);
|
||||
state.uniforms = uniforms;
|
||||
state.commit.increment();
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for SharedPixelShaderElement {
|
||||
fn id(&self) -> &Id {
|
||||
&self.state.id
|
||||
}
|
||||
|
||||
fn current_commit(&self) -> CommitCounter {
|
||||
self.state.commit
|
||||
}
|
||||
|
||||
fn src(&self) -> Rectangle<f64, Buffer> {
|
||||
Rectangle::from_size(self.state.area.size.to_f64().to_buffer(1.0, Transform::Normal))
|
||||
}
|
||||
|
||||
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
|
||||
self.state.area.to_physical_precise_round(scale)
|
||||
}
|
||||
|
||||
fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {
|
||||
self.state
|
||||
.opaque
|
||||
.iter()
|
||||
.map(|region| region.to_physical_precise_round(scale))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn alpha(&self) -> f32 {
|
||||
self.state.alpha
|
||||
}
|
||||
|
||||
fn kind(&self) -> Kind {
|
||||
self.state.kind
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderElement<GlesRenderer> for SharedPixelShaderElement {
|
||||
fn draw(
|
||||
&self,
|
||||
frame: &mut GlesFrame<'_, '_>,
|
||||
src: Rectangle<f64, Buffer>,
|
||||
dst: Rectangle<i32, Physical>,
|
||||
damage: &[Rectangle<i32, Physical>],
|
||||
_opaque: &[Rectangle<i32, Physical>],
|
||||
) -> Result<(), GlesError> {
|
||||
frame.render_pixel_shader_to(
|
||||
&self.shader,
|
||||
src,
|
||||
dst,
|
||||
self.state.area.size.to_buffer(1, Transform::Normal),
|
||||
Some(damage),
|
||||
self.state.alpha,
|
||||
&self.state.uniforms,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
use smithay::backend::egl::{EGLContext, EGLDevice, EGLDisplay};
|
||||
use smithay::backend::renderer::gles::element::PixelShaderElement;
|
||||
use smithay::backend::renderer::gles::{GlesTexture, UniformName, UniformType};
|
||||
use smithay::backend::renderer::{Bind, Color32F, ExportMem, Frame, Offscreen, Renderer};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn pixels(renderer: &mut GlesRenderer, element: &impl RenderElement<GlesRenderer>) -> Vec<u8> {
|
||||
let size = (32, 32).into();
|
||||
let mut texture: GlesTexture = renderer.create_buffer(Fourcc::Abgr8888, size).unwrap();
|
||||
let rect = Rectangle::from_size((32, 32).into());
|
||||
let mut target = renderer.bind(&mut texture).unwrap();
|
||||
|
||||
{
|
||||
let mut frame = renderer
|
||||
.render(&mut target, (32, 32).into(), Transform::Normal)
|
||||
.unwrap();
|
||||
frame.clear(Color32F::TRANSPARENT, &[rect]).unwrap();
|
||||
element.draw(&mut frame, element.src(), rect, &[rect], &[]).unwrap();
|
||||
let _ = frame.finish().unwrap();
|
||||
}
|
||||
|
||||
let mapping = renderer
|
||||
.copy_framebuffer(&target, Rectangle::from_size(size), Fourcc::Abgr8888)
|
||||
.unwrap();
|
||||
renderer.map_texture(&mapping).unwrap().to_vec()
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "requires an EGL rendering device"]
|
||||
fn shared_shader_matches_smithay_and_keeps_old_frames_immutable() {
|
||||
let device = EGLDevice::enumerate().unwrap().last().expect("an EGL device");
|
||||
let display = unsafe { EGLDisplay::new(device).unwrap() };
|
||||
let mut renderer = unsafe { GlesRenderer::new(EGLContext::new(&display).unwrap()).unwrap() };
|
||||
let program = renderer
|
||||
.compile_custom_pixel_shader(
|
||||
super::super::corner_shader(super::super::ROUNDED_SOLID_SHADER),
|
||||
&[
|
||||
UniformName::new("clip_rect", UniformType::_4f),
|
||||
UniformName::new("radius", UniformType::_1f),
|
||||
UniformName::new("color", UniformType::_4f),
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
let area = Rectangle::new((2, 3).into(), (32, 32).into());
|
||||
let uniforms = |color| {
|
||||
vec![
|
||||
Uniform::new("clip_rect", [0.0_f32, 0.0, 32.0, 32.0]),
|
||||
Uniform::new("radius", 8.0_f32),
|
||||
Uniform::new("color", color),
|
||||
]
|
||||
};
|
||||
let red = [0.5_f32, 0.0, 0.0, 0.5];
|
||||
let green = [0.0_f32, 0.5, 0.0, 0.5];
|
||||
let opaque = Some(vec![Rectangle::from_size((20, 20).into())]);
|
||||
let baseline = PixelShaderElement::new(
|
||||
program.clone(),
|
||||
area,
|
||||
opaque.clone(),
|
||||
0.5,
|
||||
uniforms(red),
|
||||
Kind::Unspecified,
|
||||
);
|
||||
let mut shared = SharedPixelShaderElement::new(program, area, opaque, 0.5, uniforms(red), Kind::Unspecified);
|
||||
let old = shared.clone();
|
||||
let old_commit = old.current_commit();
|
||||
|
||||
assert!(Arc::ptr_eq(&shared.state, &old.state));
|
||||
assert!(
|
||||
shared
|
||||
.state
|
||||
.uniforms
|
||||
.iter()
|
||||
.all(|uniform| matches!(uniform.name, Cow::Borrowed(_)))
|
||||
);
|
||||
|
||||
for scale in [1.0, 1.25, 2.0] {
|
||||
assert_eq!(shared.geometry(scale.into()), baseline.geometry(scale.into()));
|
||||
assert_eq!(
|
||||
&*shared.opaque_regions(scale.into()),
|
||||
&*baseline.opaque_regions(scale.into())
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(pixels(&mut renderer, &shared), pixels(&mut renderer, &baseline));
|
||||
shared.resize(area, Some(vec![Rectangle::from_size((20, 20).into())]));
|
||||
assert!(Arc::ptr_eq(&shared.state, &old.state));
|
||||
shared.update_uniforms(uniforms(green));
|
||||
assert!(!Arc::ptr_eq(&shared.state, &old.state));
|
||||
assert_eq!(shared.id(), old.id());
|
||||
assert_eq!(old.current_commit(), old_commit);
|
||||
assert_ne!(shared.current_commit(), old_commit);
|
||||
assert_eq!(pixels(&mut renderer, &old), pixels(&mut renderer, &baseline));
|
||||
assert_ne!(pixels(&mut renderer, &shared), pixels(&mut renderer, &baseline));
|
||||
|
||||
let retained = shared.clone();
|
||||
shared.resize(Rectangle::from_size((16, 16).into()), None);
|
||||
assert_eq!(retained.geometry(1.0.into()), area.to_physical_precise_round(1.0));
|
||||
assert_eq!(shared.geometry(1.0.into()).size, (16, 16).into());
|
||||
assert!(shared.opaque_regions(1.0.into()).is_empty());
|
||||
assert_eq!(shared.alpha(), baseline.alpha());
|
||||
assert_eq!(shared.kind(), baseline.kind());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user