feat: render semantic layer materials
This commit is contained in:
1 parent
ecefae6f81
commit
ae6ec1c5f5
2 files changed
+166
-9
No files matched your search
@@ -122,6 +122,8 @@ pub struct Ferese {
|
|||||||
pub(crate) window_borders: HashMap<WindowId, crate::winit::WindowBorderBuffers>,
|
pub(crate) window_borders: HashMap<WindowId, crate::winit::WindowBorderBuffers>,
|
||||||
pub(crate) window_shadows: HashMap<WindowId, crate::winit::WindowShadowBuffers>,
|
pub(crate) window_shadows: HashMap<WindowId, crate::winit::WindowShadowBuffers>,
|
||||||
pub(crate) rounded_clip_programs: HashMap<ErasedContextId, crate::winit::RoundedClipPrograms>,
|
pub(crate) rounded_clip_programs: HashMap<ErasedContextId, crate::winit::RoundedClipPrograms>,
|
||||||
|
pub(crate) material_programs: HashMap<ErasedContextId, crate::winit::MaterialProgram>,
|
||||||
|
pub(crate) material_buffers: HashMap<WlSurface, crate::winit::MaterialBuffers>,
|
||||||
closing_windows: HashMap<WindowId, ClosingAnimation>,
|
closing_windows: HashMap<WindowId, ClosingAnimation>,
|
||||||
viewport_animations: HashMap<WorkspaceId, AnimatedValue>,
|
viewport_animations: HashMap<WorkspaceId, AnimatedValue>,
|
||||||
scrolling_world_x: HashMap<WindowId, (WorkspaceId, AnimatedValue)>,
|
scrolling_world_x: HashMap<WindowId, (WorkspaceId, AnimatedValue)>,
|
||||||
@@ -434,6 +436,8 @@ impl Ferese {
|
|||||||
window_borders: HashMap::new(),
|
window_borders: HashMap::new(),
|
||||||
window_shadows: HashMap::new(),
|
window_shadows: HashMap::new(),
|
||||||
rounded_clip_programs: HashMap::new(),
|
rounded_clip_programs: HashMap::new(),
|
||||||
|
material_programs: HashMap::new(),
|
||||||
|
material_buffers: HashMap::new(),
|
||||||
closing_windows: HashMap::new(),
|
closing_windows: HashMap::new(),
|
||||||
viewport_animations: HashMap::new(),
|
viewport_animations: HashMap::new(),
|
||||||
scrolling_world_x: HashMap::new(),
|
scrolling_world_x: HashMap::new(),
|
||||||
|
|||||||
+162
-9
@@ -40,6 +40,7 @@ use smithay::{
|
|||||||
output::{Mode, Output, PhysicalProperties, Scale, Subpixel},
|
output::{Mode, Output, PhysicalProperties, Scale, Subpixel},
|
||||||
reexports::calloop::EventLoop,
|
reexports::calloop::EventLoop,
|
||||||
reexports::wayland_protocols::wp::presentation_time::server::wp_presentation_feedback::Kind as PresentationKind,
|
reexports::wayland_protocols::wp::presentation_time::server::wp_presentation_feedback::Kind as PresentationKind,
|
||||||
|
reexports::wayland_server::Resource,
|
||||||
utils::{
|
utils::{
|
||||||
Buffer, Clock, Logical, Monotonic, Physical, Point, Rectangle, Scale as RenderScale,
|
Buffer, Clock, Logical, Monotonic, Physical, Point, Rectangle, Scale as RenderScale,
|
||||||
Transform,
|
Transform,
|
||||||
@@ -210,6 +211,26 @@ void main() {
|
|||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
|
const MATERIAL_SHADER: &str = r#"
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
uniform float alpha;
|
||||||
|
uniform vec4 tint;
|
||||||
|
uniform float noise_amount;
|
||||||
|
varying vec2 v_coords;
|
||||||
|
|
||||||
|
float random(vec2 point) {
|
||||||
|
return fract(sin(dot(point, vec2(12.9898, 78.233))) * 43758.5453);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float grain = (random(gl_FragCoord.xy) - 0.5) * noise_amount;
|
||||||
|
vec3 color = clamp(tint.rgb + vec3(grain), 0.0, 1.0);
|
||||||
|
float opacity = tint.a * alpha;
|
||||||
|
gl_FragColor = vec4(color * opacity, opacity);
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct RoundedClipPrograms {
|
pub(crate) struct RoundedClipPrograms {
|
||||||
texture: GlesTexProgram,
|
texture: GlesTexProgram,
|
||||||
@@ -217,6 +238,29 @@ pub(crate) struct RoundedClipPrograms {
|
|||||||
shadow: GlesPixelProgram,
|
shadow: GlesPixelProgram,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct MaterialProgram(GlesPixelProgram);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
struct MaterialParameters {
|
||||||
|
geometry: Rectangle<i32, Logical>,
|
||||||
|
tint: [f32; 4],
|
||||||
|
noise: f32,
|
||||||
|
generation: u64,
|
||||||
|
opaque: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct CachedMaterial {
|
||||||
|
element: PixelShaderElement,
|
||||||
|
parameters: MaterialParameters,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub(crate) struct MaterialBuffers {
|
||||||
|
contexts: HashMap<ErasedContextId, CachedMaterial>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
struct BorderParameters {
|
struct BorderParameters {
|
||||||
geometry: Rectangle<i32, Logical>,
|
geometry: Rectangle<i32, Logical>,
|
||||||
@@ -569,6 +613,7 @@ fn output_elements(
|
|||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
elements.extend(layer_elements(
|
elements.extend(layer_elements(
|
||||||
|
state,
|
||||||
renderer,
|
renderer,
|
||||||
output,
|
output,
|
||||||
&[Layer::Overlay, Layer::Top],
|
&[Layer::Overlay, Layer::Top],
|
||||||
@@ -680,6 +725,7 @@ fn output_elements(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
elements.extend(layer_elements(
|
elements.extend(layer_elements(
|
||||||
|
state,
|
||||||
renderer,
|
renderer,
|
||||||
output,
|
output,
|
||||||
&[Layer::Bottom, Layer::Background],
|
&[Layer::Bottom, Layer::Background],
|
||||||
@@ -1081,6 +1127,7 @@ fn layer_surfaces(output: &Output) -> Vec<LayerSurface> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn layer_elements(
|
fn layer_elements(
|
||||||
|
state: &mut Ferese,
|
||||||
renderer: &mut GlesRenderer,
|
renderer: &mut GlesRenderer,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
requested_layers: &[Layer],
|
requested_layers: &[Layer],
|
||||||
@@ -1098,33 +1145,139 @@ fn layer_elements(
|
|||||||
.flat_map(|requested| {
|
.flat_map(|requested| {
|
||||||
map.layers_on(*requested).rev().filter_map(|layer| {
|
map.layers_on(*requested).rev().filter_map(|layer| {
|
||||||
map.layer_geometry(layer)
|
map.layer_geometry(layer)
|
||||||
.map(|geometry| (geometry.loc, layer.clone()))
|
.map(|geometry| (geometry, layer.clone()))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
layers
|
state
|
||||||
.into_iter()
|
.material_buffers
|
||||||
.flat_map(|(location, layer)| {
|
.retain(|surface, _| surface.is_alive());
|
||||||
|
|
||||||
|
let mut elements = Vec::new();
|
||||||
|
for (geometry, layer) in layers {
|
||||||
|
elements.extend(
|
||||||
AsRenderElements::<GlesRenderer>::render_elements::<
|
AsRenderElements::<GlesRenderer>::render_elements::<
|
||||||
WaylandSurfaceRenderElement<GlesRenderer>,
|
WaylandSurfaceRenderElement<GlesRenderer>,
|
||||||
>(
|
>(
|
||||||
&layer,
|
&layer,
|
||||||
renderer,
|
renderer,
|
||||||
location.to_physical_precise_round(scale),
|
geometry.loc.to_physical_precise_round(scale),
|
||||||
scale.into(),
|
scale.into(),
|
||||||
1.0,
|
1.0,
|
||||||
)
|
)
|
||||||
})
|
.into_iter()
|
||||||
.filter_map(|element| {
|
.filter_map(|element| {
|
||||||
let origin = Point::<i32, Physical>::default();
|
let origin = Point::<i32, Physical>::default();
|
||||||
let element = RescaleRenderElement::from_element(element, origin, 1.0);
|
let element = RescaleRenderElement::from_element(element, origin, 1.0);
|
||||||
let element = RelocateRenderElement::from_element(element, origin, Relocate::Relative);
|
let element =
|
||||||
|
RelocateRenderElement::from_element(element, origin, Relocate::Relative);
|
||||||
|
|
||||||
CropRenderElement::from_element(element, scale, output_crop).map(Into::into)
|
CropRenderElement::from_element(element, scale, output_crop).map(Into::into)
|
||||||
})
|
}),
|
||||||
.collect()
|
);
|
||||||
|
|
||||||
|
if let Some(material) = material_element(state, renderer, &layer, geometry) {
|
||||||
|
elements.push(material.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elements
|
||||||
|
}
|
||||||
|
|
||||||
|
fn material_element(
|
||||||
|
state: &mut Ferese,
|
||||||
|
renderer: &mut GlesRenderer,
|
||||||
|
layer: &LayerSurface,
|
||||||
|
geometry: Rectangle<i32, Logical>,
|
||||||
|
) -> Option<PixelShaderElement> {
|
||||||
|
let surface = layer.wl_surface();
|
||||||
|
let (role, generation) = crate::effects::surface_role(surface)?;
|
||||||
|
let material = crate::effects::resolve_material(role, state.theme_settings.material_style);
|
||||||
|
let program = material_program(state, renderer)?;
|
||||||
|
let tint = match material.style {
|
||||||
|
crate::config::MaterialStyle::Solid => [0.067, 0.094, 0.129, material.opacity],
|
||||||
|
crate::config::MaterialStyle::Glass | crate::config::MaterialStyle::Translucent => {
|
||||||
|
[0.067, 0.094, 0.129, material.opacity]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let parameters = MaterialParameters {
|
||||||
|
geometry,
|
||||||
|
tint,
|
||||||
|
noise: material.noise,
|
||||||
|
generation,
|
||||||
|
opaque: material.opacity == 1.0,
|
||||||
|
};
|
||||||
|
let context = renderer.context_id().erased();
|
||||||
|
let buffers = state.material_buffers.entry(surface.clone()).or_default();
|
||||||
|
if !buffers.contexts.contains_key(&context) {
|
||||||
|
let element = PixelShaderElement::new(
|
||||||
|
program.0,
|
||||||
|
geometry,
|
||||||
|
parameters
|
||||||
|
.opaque
|
||||||
|
.then(|| vec![Rectangle::from_size(geometry.size)]),
|
||||||
|
1.0,
|
||||||
|
material_uniforms(¶meters),
|
||||||
|
RenderElementKind::Unspecified,
|
||||||
|
);
|
||||||
|
buffers.contexts.insert(
|
||||||
|
context.clone(),
|
||||||
|
CachedMaterial {
|
||||||
|
element,
|
||||||
|
parameters: parameters.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let cached = buffers.contexts.get_mut(&context)?;
|
||||||
|
if cached.parameters != parameters {
|
||||||
|
if cached.parameters.geometry != parameters.geometry
|
||||||
|
|| cached.parameters.opaque != parameters.opaque
|
||||||
|
{
|
||||||
|
let opaque = parameters
|
||||||
|
.opaque
|
||||||
|
.then(|| vec![Rectangle::from_size(parameters.geometry.size)]);
|
||||||
|
cached.element.resize(parameters.geometry, opaque);
|
||||||
|
}
|
||||||
|
cached
|
||||||
|
.element
|
||||||
|
.update_uniforms(material_uniforms(¶meters));
|
||||||
|
cached.parameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(cached.element.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn material_program(state: &mut Ferese, renderer: &mut GlesRenderer) -> Option<MaterialProgram> {
|
||||||
|
let context = renderer.context_id().erased();
|
||||||
|
if let Some(program) = state.material_programs.get(&context) {
|
||||||
|
return Some(program.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let uniforms = [
|
||||||
|
UniformName::new("tint", UniformType::_4f),
|
||||||
|
UniformName::new("noise_amount", UniformType::_1f),
|
||||||
|
];
|
||||||
|
match renderer.compile_custom_pixel_shader(MATERIAL_SHADER, &uniforms) {
|
||||||
|
Ok(program) => {
|
||||||
|
let program = MaterialProgram(program);
|
||||||
|
state.material_programs.insert(context, program.clone());
|
||||||
|
Some(program)
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
tracing::error!(%error, "failed to compile semantic material shader");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn material_uniforms(parameters: &MaterialParameters) -> Vec<Uniform<'static>> {
|
||||||
|
vec![
|
||||||
|
Uniform::new("tint", parameters.tint).into_owned(),
|
||||||
|
Uniform::new("noise_amount", parameters.noise).into_owned(),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursor_elements(
|
fn cursor_elements(
|
||||||
|
|||||||
Reference in new issue
Block a user