From ff54a8ab96ed00f618c46d11de3f12a1b195b0b3 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Mon, 18 Mar 2024 19:18:30 +0100 Subject: [PATCH] wayland: implement alpha_modifier_v1 --- .builds/test.yml | 1 + Cargo.lock | 21 ++++ Cargo.toml | 1 + build/vulkan.rs | 32 ++++-- src/backends/metal/video.rs | 4 + src/cursor.rs | 3 + src/gfx_api.rs | 2 + src/gfx_apis/gl.rs | 32 ++++-- src/gfx_apis/gl/renderer/context.rs | 82 +++++++++----- src/gfx_apis/gl/shaders/tex-alpha.frag.glsl | 7 -- .../gl/shaders/tex-external-alpha.frag.glsl | 9 -- .../gl/shaders/tex-external.frag.glsl | 9 -- src/gfx_apis/gl/shaders/tex.frag.glsl | 27 +++++ src/gfx_apis/vulkan/renderer.rs | 66 +++++++++-- src/gfx_apis/vulkan/shaders.rs | 11 ++ src/gfx_apis/vulkan/shaders/tex.frag | 12 ++ src/globals.rs | 2 + src/ifs.rs | 1 + src/ifs/wl_surface.rs | 20 +++- .../wp_alpha_modifier_surface_v1.rs | 78 +++++++++++++ src/ifs/wp_alpha_modifier_v1.rs | 106 ++++++++++++++++++ src/it/test_ifs.rs | 4 +- src/it/test_ifs/test_alpha_modifier.rs | 49 ++++++++ .../test_ifs/test_alpha_modifier_surface.rs | 41 +++++++ src/it/test_ifs/test_registry.rs | 13 ++- src/it/test_transport.rs | 1 + src/it/tests.rs | 2 + src/it/tests/t0039_alpha_modifier.rs | 56 +++++++++ .../t0039_alpha_modifier/screenshot_1.qoi | Bin 0 -> 8142 bytes .../t0039_alpha_modifier/screenshot_2.qoi | Bin 0 -> 8145 bytes src/portal/ptr_gui.rs | 2 + src/renderer.rs | 18 ++- src/renderer/renderer_base.rs | 2 + src/state.rs | 1 + src/theme.rs | 14 +++ wire/wp_alpha_modifier_surface_v1.txt | 6 + wire/wp_alpha_modifier_v1.txt | 7 ++ 37 files changed, 654 insertions(+), 88 deletions(-) delete mode 100644 src/gfx_apis/gl/shaders/tex-alpha.frag.glsl delete mode 100644 src/gfx_apis/gl/shaders/tex-external-alpha.frag.glsl delete mode 100644 src/gfx_apis/gl/shaders/tex-external.frag.glsl create mode 100644 src/ifs/wl_surface/wp_alpha_modifier_surface_v1.rs create mode 100644 src/ifs/wp_alpha_modifier_v1.rs create mode 100644 src/it/test_ifs/test_alpha_modifier.rs create mode 100644 src/it/test_ifs/test_alpha_modifier_surface.rs create mode 100644 src/it/tests/t0039_alpha_modifier.rs create mode 100644 src/it/tests/t0039_alpha_modifier/screenshot_1.qoi create mode 100644 src/it/tests/t0039_alpha_modifier/screenshot_2.qoi create mode 100644 wire/wp_alpha_modifier_surface_v1.txt create mode 100644 wire/wp_alpha_modifier_v1.txt diff --git a/.builds/test.yml b/.builds/test.yml index 446d1f25..7bb6dd49 100644 --- a/.builds/test.yml +++ b/.builds/test.yml @@ -16,4 +16,5 @@ tasks: - test: | cd jay export RUST_BACKTRACE=1 + export GALLIUM_DRIVER=softpipe ./target/debug/jay run-tests diff --git a/Cargo.lock b/Cargo.lock index 7f9e62d9..f0c30c8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -314,6 +314,26 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -516,6 +536,7 @@ dependencies = [ "clap", "clap_complete", "dirs", + "enum-map", "futures-util", "gpu-alloc", "gpu-alloc-ash", diff --git a/Cargo.toml b/Cargo.toml index b52e9355..951f8368 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ ash = "0.37.3" gpu-alloc = "0.6.0" gpu-alloc-ash = "0.6.0" serde = { version = "1.0.196", features = ["derive"] } +enum-map = "2.7.3" [build-dependencies] repc = "0.1.1" diff --git a/build/vulkan.rs b/build/vulkan.rs index 69f41aab..e43ad3fb 100644 --- a/build/vulkan.rs +++ b/build/vulkan.rs @@ -1,6 +1,7 @@ use { crate::open, anyhow::{bail, Context}, + shaderc::CompileOptions, std::{io::Write, path::Path}, }; @@ -8,15 +9,32 @@ const ROOT: &str = "src/gfx_apis/vulkan/shaders"; pub fn main() -> anyhow::Result<()> { println!("cargo:rerun-if-changed={}", ROOT); - for shader in std::fs::read_dir(ROOT)? { - let shader = shader?; - let name = shader.file_name().to_string_lossy().into_owned(); - compile_shader(&name).context(name)?; + compile_simple("fill.frag")?; + compile_simple("fill.vert")?; + compile_simple("tex.vert")?; + compile_tex_frag("tex.frag.spv", false, false)?; + compile_tex_frag("tex.frag.mult+opaque.spv", false, true)?; + compile_tex_frag("tex.frag.mult+alpha.spv", true, true)?; + Ok(()) +} + +fn compile_tex_frag(out: &str, alpha: bool, alpha_multiplier: bool) -> anyhow::Result<()> { + let mut opts = CompileOptions::new().unwrap(); + if alpha { + opts.add_macro_definition("ALPHA", None); } + if alpha_multiplier { + opts.add_macro_definition("ALPHA_MULTIPLIER", None); + } + compile_shader("tex.frag", out, Some(&opts)).with_context(|| out.to_string())?; Ok(()) } -fn compile_shader(name: &str) -> anyhow::Result<()> { +fn compile_simple(name: &str) -> anyhow::Result<()> { + compile_shader(name, &format!("{name}.spv"), None).with_context(|| name.to_string()) +} + +fn compile_shader(name: &str, out: &str, options: Option<&CompileOptions>) -> anyhow::Result<()> { let stage = match Path::new(name) .extension() .and_then(|e| e.to_str()) @@ -29,9 +47,9 @@ fn compile_shader(name: &str) -> anyhow::Result<()> { let src = std::fs::read_to_string(format!("{}/{}", ROOT, name))?; let compiler = shaderc::Compiler::new().unwrap(); let binary = compiler - .compile_into_spirv(&src, stage, name, "main", None) + .compile_into_spirv(&src, stage, name, "main", options) .unwrap(); - let mut file = open(&format!("{}.spv", name))?; + let mut file = open(out)?; file.write_all(binary.as_binary_u8())?; file.flush()?; Ok(()) diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 650bc030..7d28ff93 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -426,6 +426,10 @@ impl MetalConnector { } return None; }; + if ct.alpha.is_some() { + // Direct scanout with alpha factor is not supported. + return None; + } if !ct.tex.format().has_alpha && ct.target.is_covering() { // Texture covers the entire screen and is opaque. break 'ct ct; diff --git a/src/cursor.rs b/src/cursor.rs index 098b6e76..85342742 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -377,6 +377,7 @@ fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed if extents.intersects(&renderer.pixel_extents()) { renderer.base.render_texture( &img.tex, + None, extents.x1(), extents.y1(), None, @@ -399,6 +400,7 @@ impl Cursor for StaticCursor { if let Some(img) = self.image.scales.get(&renderer.scale()) { renderer.base.render_texture( &img.tex, + None, 0, 0, None, @@ -438,6 +440,7 @@ impl Cursor for AnimatedCursor { if let Some(img) = img.scales.get(&renderer.scale()) { renderer.base.render_texture( &img.tex, + None, 0, 0, None, diff --git a/src/gfx_api.rs b/src/gfx_api.rs index e8db5348..96b6060b 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -147,6 +147,7 @@ pub struct CopyTexture { pub buffer_resv: Option>, pub acquire_sync: AcquireSync, pub release_sync: ReleaseSync, + pub alpha: Option, } #[derive(Clone, Debug)] @@ -292,6 +293,7 @@ impl dyn GfxFramebuffer { let mut renderer = self.renderer_base(&mut ops, scale, Transform::None); renderer.render_texture( texture, + None, x, y, None, diff --git a/src/gfx_apis/gl.rs b/src/gfx_apis/gl.rs index e18af215..0fd8ed71 100644 --- a/src/gfx_apis/gl.rs +++ b/src/gfx_apis/gl.rs @@ -73,7 +73,11 @@ use { }, gfx_apis::gl::{ gl::texture::image_target, - renderer::{context::GlRenderContext, framebuffer::Framebuffer, texture::Texture}, + renderer::{ + context::{GlRenderContext, TexCopyType, TexSourceType}, + framebuffer::Framebuffer, + texture::Texture, + }, sys::{ GL_BLEND, GL_FALSE, GL_FLOAT, GL_LINEAR, GL_TEXTURE0, GL_TEXTURE_MIN_FILTER, GL_TRIANGLES, GL_TRIANGLE_STRIP, @@ -336,16 +340,20 @@ fn render_texture(ctx: &GlRenderContext, tex: &CopyTexture) { }, false => &ctx.tex_internal, }; - let prog = match texture.gl.format.has_alpha { - true => { - (gles.glEnable)(GL_BLEND); - &progs.alpha - } - false => { - (gles.glDisable)(GL_BLEND); - &progs.solid - } + let copy_type = match tex.alpha.is_some() { + true => TexCopyType::Multiply, + false => TexCopyType::Identity, + }; + let source_type = match texture.gl.format.has_alpha { + true => TexSourceType::HasAlpha, + false => TexSourceType::Opaque, }; + if (copy_type, source_type) == (TexCopyType::Identity, TexSourceType::Opaque) { + (gles.glDisable)(GL_BLEND); + } else { + (gles.glEnable)(GL_BLEND); + } + let prog = &progs[copy_type][source_type]; (gles.glUseProgram)(prog.prog.prog); @@ -354,6 +362,10 @@ fn render_texture(ctx: &GlRenderContext, tex: &CopyTexture) { let texcoord = tex.source.to_points(); let pos = tex.target.to_points(); + if let Some(alpha) = tex.alpha { + (gles.glUniform1f)(prog.alpha, alpha); + } + (gles.glVertexAttribPointer)( prog.texcoord as _, 2, diff --git a/src/gfx_apis/gl/renderer/context.rs b/src/gfx_apis/gl/renderer/context.rs index b740cb3b..4b31edba 100644 --- a/src/gfx_apis/gl/renderer/context.rs +++ b/src/gfx_apis/gl/renderer/context.rs @@ -21,6 +21,7 @@ use { }, }, ahash::AHashMap, + enum_map::{enum_map, Enum, EnumMap}, jay_config::video::GfxApi, std::{ cell::{Cell, RefCell}, @@ -36,22 +37,35 @@ pub(crate) struct TexProg { pub(crate) pos: GLint, pub(crate) texcoord: GLint, pub(crate) tex: GLint, + pub(crate) alpha: GLint, } impl TexProg { - unsafe fn from(prog: GlProgram) -> Self { + unsafe fn from(prog: GlProgram, alpha_multiplier: bool) -> Self { + let alpha = match alpha_multiplier { + true => prog.get_uniform_location(ustr!("alpha")), + false => 0, + }; Self { pos: prog.get_attrib_location(ustr!("pos")), texcoord: prog.get_attrib_location(ustr!("texcoord")), tex: prog.get_uniform_location(ustr!("tex")), + alpha, prog, } } } -pub(crate) struct TexProgs { - pub alpha: TexProg, - pub solid: TexProg, +#[derive(Copy, Clone, PartialEq, Enum)] +pub(in crate::gfx_apis::gl) enum TexCopyType { + Identity, + Multiply, +} + +#[derive(Copy, Clone, PartialEq, Enum)] +pub(in crate::gfx_apis::gl) enum TexSourceType { + Opaque, + HasAlpha, } pub(in crate::gfx_apis::gl) struct GlRenderContext { @@ -61,8 +75,8 @@ pub(in crate::gfx_apis::gl) struct GlRenderContext { pub(crate) render_node: Rc, - pub(crate) tex_internal: TexProgs, - pub(crate) tex_external: Option, + pub(crate) tex_internal: EnumMap>, + pub(crate) tex_external: Option>>, pub(crate) fill_prog: GlProgram, pub(crate) fill_prog_pos: GLint, @@ -100,28 +114,37 @@ impl GlRenderContext { unsafe fn new(ctx: &Rc, node: &Rc) -> Result { let tex_vert = include_str!("../shaders/tex.vert.glsl"); - let tex_prog = - GlProgram::from_shaders(ctx, tex_vert, include_str!("../shaders/tex.frag.glsl"))?; - let tex_alpha_prog = GlProgram::from_shaders( - ctx, - tex_vert, - include_str!("../shaders/tex-alpha.frag.glsl"), - )?; - let tex_external = if ctx.ext.contains(GL_OES_EGL_IMAGE_EXTERNAL) { - let solid = GlProgram::from_shaders( - ctx, - tex_vert, - include_str!("../shaders/tex-external.frag.glsl"), - )?; - let alpha = GlProgram::from_shaders( - ctx, - tex_vert, - include_str!("../shaders/tex-external-alpha.frag.glsl"), - )?; - Some(TexProgs { - alpha: TexProg::from(alpha), - solid: TexProg::from(solid), + let tex_frag = include_str!("../shaders/tex.frag.glsl"); + let create_programs = |external: bool| { + let create_program = |alpha_multiplier: bool, alpha: bool| { + let mut tex_frac_src = String::new(); + if external { + tex_frac_src.push_str("#define EXTERNAL\n"); + } + if alpha_multiplier { + tex_frac_src.push_str("#define ALPHA_MULTIPLIER\n"); + } + if alpha { + tex_frac_src.push_str("#define ALPHA\n"); + } + tex_frac_src.push_str(tex_frag); + let prog = GlProgram::from_shaders(ctx, tex_vert, &tex_frac_src)?; + Ok::<_, RenderError>(TexProg::from(prog, alpha_multiplier)) + }; + Ok::<_, RenderError>(enum_map! { + TexCopyType::Identity => enum_map! { + TexSourceType::Opaque => create_program(false, false)?, + TexSourceType::HasAlpha => create_program(false, true)?, + }, + TexCopyType::Multiply => enum_map! { + TexSourceType::Opaque => create_program(true, false)?, + TexSourceType::HasAlpha => create_program(true, true)?, + }, }) + }; + let tex_internal = create_programs(false)?; + let tex_external = if ctx.ext.contains(GL_OES_EGL_IMAGE_EXTERNAL) { + Some(create_programs(true)?) } else { None }; @@ -137,10 +160,7 @@ impl GlRenderContext { render_node: node.clone(), - tex_internal: TexProgs { - solid: TexProg::from(tex_prog), - alpha: TexProg::from(tex_alpha_prog), - }, + tex_internal, tex_external, fill_prog_pos: fill_prog.get_attrib_location(ustr!("pos")), diff --git a/src/gfx_apis/gl/shaders/tex-alpha.frag.glsl b/src/gfx_apis/gl/shaders/tex-alpha.frag.glsl deleted file mode 100644 index e8b03a6a..00000000 --- a/src/gfx_apis/gl/shaders/tex-alpha.frag.glsl +++ /dev/null @@ -1,7 +0,0 @@ -precision mediump float; -varying vec2 v_texcoord; -uniform sampler2D tex; - -void main() { - gl_FragColor = texture2D(tex, v_texcoord); -} diff --git a/src/gfx_apis/gl/shaders/tex-external-alpha.frag.glsl b/src/gfx_apis/gl/shaders/tex-external-alpha.frag.glsl deleted file mode 100644 index 580e31cd..00000000 --- a/src/gfx_apis/gl/shaders/tex-external-alpha.frag.glsl +++ /dev/null @@ -1,9 +0,0 @@ -#extension GL_OES_EGL_image_external : require - -precision mediump float; -varying vec2 v_texcoord; -uniform samplerExternalOES tex; - -void main() { - gl_FragColor = texture2D(tex, v_texcoord); -} diff --git a/src/gfx_apis/gl/shaders/tex-external.frag.glsl b/src/gfx_apis/gl/shaders/tex-external.frag.glsl deleted file mode 100644 index 2c540b27..00000000 --- a/src/gfx_apis/gl/shaders/tex-external.frag.glsl +++ /dev/null @@ -1,9 +0,0 @@ -#extension GL_OES_EGL_image_external : require - -precision mediump float; -varying vec2 v_texcoord; -uniform samplerExternalOES tex; - -void main() { - gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0); -} diff --git a/src/gfx_apis/gl/shaders/tex.frag.glsl b/src/gfx_apis/gl/shaders/tex.frag.glsl index d713be11..213ed085 100644 --- a/src/gfx_apis/gl/shaders/tex.frag.glsl +++ b/src/gfx_apis/gl/shaders/tex.frag.glsl @@ -1,7 +1,34 @@ +#ifdef EXTERNAL +#extension GL_OES_EGL_image_external : require +#endif + precision mediump float; varying vec2 v_texcoord; +#ifdef EXTERNAL +uniform samplerExternalOES tex; +#else uniform sampler2D tex; +#endif +#ifdef ALPHA_MULTIPLIER +uniform float alpha; +#endif void main() { +#ifdef ALPHA + +#ifdef ALPHA_MULTIPLIER + gl_FragColor = texture2D(tex, v_texcoord) * alpha; +#else // !ALPHA_MULTIPLIER + gl_FragColor = texture2D(tex, v_texcoord); +#endif // ALPHA_MULTIPLIER + +#else // !ALPHA + +#ifdef ALPHA_MULTIPLIER + gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb * alpha, alpha); +#else // !ALPHA_MULTIPLIER gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0); +#endif // ALPHA_MULTIPLIER + +#endif // ALPHA } diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 18fa6e14..69a53aa5 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -15,8 +15,9 @@ use { pipeline::{PipelineCreateInfo, VulkanPipeline}, semaphore::VulkanSemaphore, shaders::{ - FillFragPushConstants, FillVertPushConstants, TexVertPushConstants, FILL_FRAG, - FILL_VERT, TEX_FRAG, TEX_VERT, + FillFragPushConstants, FillVertPushConstants, TexFragPushConstants, + TexVertPushConstants, VulkanShader, FILL_FRAG, FILL_VERT, TEX_FRAG, + TEX_FRAG_MULT_ALPHA, TEX_FRAG_MULT_OPAQUE, TEX_VERT, }, staging::VulkanStagingBuffer, VulkanError, @@ -41,6 +42,7 @@ use { }, Device, }, + enum_map::{enum_map, Enum, EnumMap}, isnt::std_1::collections::IsntHashMapExt, std::{ cell::{Cell, RefCell}, @@ -56,8 +58,7 @@ pub struct VulkanRenderer { pub(super) formats: Rc>, pub(super) device: Rc, pub(super) fill_pipeline: Rc, - pub(super) tex_opaque_pipeline: Rc, - pub(super) tex_alpha_pipeline: Rc, + pub(super) tex_pipelines: EnumMap>>, pub(super) command_pool: Rc, pub(super) command_buffers: Stack>, pub(super) wait_semaphores: Stack>, @@ -76,6 +77,18 @@ pub(super) struct UsedTexture { release_sync: ReleaseSync, } +#[derive(Enum)] +pub(super) enum TexCopyType { + Identity, + Multiply, +} + +#[derive(Enum)] +pub(super) enum TexSourceType { + Opaque, + HasAlpha, +} + #[derive(Default)] pub(super) struct Memory { sample: Vec>, @@ -115,6 +128,8 @@ impl VulkanDevice { let tex_descriptor_set_layout = self.create_descriptor_set_layout(&sampler)?; let tex_vert_shader = self.create_shader(TEX_VERT)?; let tex_frag_shader = self.create_shader(TEX_FRAG)?; + let tex_frag_mult_opaque_shader = self.create_shader(TEX_FRAG_MULT_OPAQUE)?; + let tex_frag_mult_alpha_shader = self.create_shader(TEX_FRAG_MULT_ALPHA)?; let create_tex_pipeline = |alpha| { self.create_pipeline::(PipelineCreateInfo { vert: tex_vert_shader.clone(), @@ -123,8 +138,18 @@ impl VulkanDevice { frag_descriptor_set_layout: Some(tex_descriptor_set_layout.clone()), }) }; + let create_tex_mult_pipeline = |frag: &Rc| { + self.create_pipeline::(PipelineCreateInfo { + vert: tex_vert_shader.clone(), + frag: frag.clone(), + alpha: true, + frag_descriptor_set_layout: Some(tex_descriptor_set_layout.clone()), + }) + }; let tex_opaque_pipeline = create_tex_pipeline(false)?; let tex_alpha_pipeline = create_tex_pipeline(true)?; + let tex_mult_opaque_pipeline = create_tex_mult_pipeline(&tex_frag_mult_opaque_shader)?; + let tex_mult_alpha_pipeline = create_tex_mult_pipeline(&tex_frag_mult_alpha_shader)?; let command_pool = self.create_command_pool()?; let formats: AHashMap = self .formats @@ -155,8 +180,16 @@ impl VulkanDevice { formats: Rc::new(formats), device: self.clone(), fill_pipeline, - tex_opaque_pipeline, - tex_alpha_pipeline, + tex_pipelines: enum_map! { + TexCopyType::Identity => enum_map! { + TexSourceType::HasAlpha => tex_alpha_pipeline.clone(), + TexSourceType::Opaque => tex_opaque_pipeline.clone(), + }, + TexCopyType::Multiply => enum_map! { + TexSourceType::HasAlpha => tex_mult_alpha_pipeline.clone(), + TexSourceType::Opaque => tex_mult_opaque_pipeline.clone(), + }, + }, command_pool, command_buffers: Default::default(), wait_semaphores: Default::default(), @@ -449,10 +482,15 @@ impl VulkanRenderer { } GfxApiOpt::CopyTexture(c) => { let tex = c.tex.as_vk(&self.device.device); - let pipeline = match tex.format.has_alpha { - true => &self.tex_alpha_pipeline, - false => &self.tex_opaque_pipeline, + let copy_type = match c.alpha.is_some() { + true => TexCopyType::Multiply, + false => TexCopyType::Identity, + }; + let source_type = match tex.format.has_alpha { + true => TexSourceType::HasAlpha, + false => TexSourceType::Opaque, }; + let pipeline = &self.tex_pipelines[copy_type][source_type]; bind(pipeline); let vert = TexVertPushConstants { pos: c.target.to_points(), @@ -480,6 +518,16 @@ impl VulkanRenderer { 0, uapi::as_bytes(&vert), ); + if let Some(alpha) = c.alpha { + let frag = TexFragPushConstants { alpha }; + dev.cmd_push_constants( + buf, + pipeline.pipeline_layout, + ShaderStageFlags::FRAGMENT, + mem::size_of_val(&vert) as _, + uapi::as_bytes(&frag), + ); + } dev.cmd_draw(buf, 4, 1, 0, 0); } } diff --git a/src/gfx_apis/vulkan/shaders.rs b/src/gfx_apis/vulkan/shaders.rs index 511895ac..2ded4418 100644 --- a/src/gfx_apis/vulkan/shaders.rs +++ b/src/gfx_apis/vulkan/shaders.rs @@ -9,6 +9,10 @@ pub const FILL_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/fill.vert pub const FILL_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/fill.frag.spv")); pub const TEX_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/tex.vert.spv")); pub const TEX_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/tex.frag.spv")); +pub const TEX_FRAG_MULT_OPAQUE: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/tex.frag.mult+opaque.spv")); +pub const TEX_FRAG_MULT_ALPHA: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/tex.frag.mult+alpha.spv")); pub struct VulkanShader { pub(super) device: Rc, @@ -38,7 +42,14 @@ pub struct TexVertPushConstants { pub tex_pos: [[f32; 2]; 4], } +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct TexFragPushConstants { + pub alpha: f32, +} + unsafe impl Packed for TexVertPushConstants {} +unsafe impl Packed for TexFragPushConstants {} impl VulkanDevice { pub(super) fn create_shader( diff --git a/src/gfx_apis/vulkan/shaders/tex.frag b/src/gfx_apis/vulkan/shaders/tex.frag index 5d3b52e2..04774ae7 100644 --- a/src/gfx_apis/vulkan/shaders/tex.frag +++ b/src/gfx_apis/vulkan/shaders/tex.frag @@ -1,9 +1,21 @@ #version 450 +#ifdef ALPHA_MULTIPLIER +layout(push_constant, std430) uniform Data { + layout(offset = 64) float mul; +} data; +#endif layout(set = 0, binding = 0) uniform sampler2D tex; layout(location = 0) in vec2 tex_pos; layout(location = 0) out vec4 out_color; void main() { +#ifdef ALPHA_MULTIPLIER +#ifdef ALPHA + out_color = textureLod(tex, tex_pos, 0) * data.mul; +#endif // !ALPHA + out_color = vec4(textureLod(tex, tex_pos, 0).rgb * data.mul, data.mul); +#else // !ALPHA_MULTIPLIER out_color = textureLod(tex, tex_pos, 0); +#endif } diff --git a/src/globals.rs b/src/globals.rs index afbf9bd7..4f53ff80 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -23,6 +23,7 @@ use { wl_shm::WlShmGlobal, wl_subcompositor::WlSubcompositorGlobal, wl_surface::xwayland_shell_v1::XwaylandShellV1Global, + wp_alpha_modifier_v1::WpAlphaModifierV1Global, wp_content_type_manager_v1::WpContentTypeManagerV1Global, wp_cursor_shape_manager_v1::WpCursorShapeManagerV1Global, wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1Global, @@ -173,6 +174,7 @@ impl Globals { add_singleton!(ExtIdleNotifierV1Global); add_singleton!(XdgToplevelDragManagerV1Global); add_singleton!(ZwlrDataControlManagerV1Global); + add_singleton!(WpAlphaModifierV1Global); } pub fn add_backend_singletons(&self, backend: &Rc) { diff --git a/src/ifs.rs b/src/ifs.rs index 8399ffc8..ed153a84 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -33,6 +33,7 @@ pub mod wl_shm; pub mod wl_shm_pool; pub mod wl_subcompositor; pub mod wl_surface; +pub mod wp_alpha_modifier_v1; pub mod wp_content_type_manager_v1; pub mod wp_content_type_v1; pub mod wp_cursor_shape_device_v1; diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 707a37fe..74977c19 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -2,6 +2,7 @@ pub mod commit_timeline; pub mod cursor; pub mod ext_session_lock_surface_v1; pub mod wl_subsurface; +pub mod wp_alpha_modifier_surface_v1; pub mod wp_fractional_scale_v1; pub mod wp_linux_drm_syncobj_surface_v1; pub mod wp_tearing_control_v1; @@ -30,6 +31,7 @@ use { commit_timeline::{ClearReason, CommitTimeline, CommitTimelineError}, cursor::CursorSurface, wl_subsurface::{PendingSubsurfaceData, SubsurfaceId, WlSubsurface}, + wp_alpha_modifier_surface_v1::WpAlphaModifierSurfaceV1, wp_fractional_scale_v1::WpFractionalScaleV1, wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1, wp_tearing_control_v1::WpTearingControlV1, @@ -245,6 +247,8 @@ pub struct WlSurface { sync_obj_surface: CloneCell>>, destroyed: Cell, commit_timeline: CommitTimeline, + alpha_modifier: CloneCell>>, + alpha: Cell>, } impl Debug for WlSurface { @@ -366,6 +370,7 @@ struct PendingState { subsurfaces: AHashMap, acquire_point: Option<(Rc, SyncObjPoint)>, release_point: Option<(Rc, SyncObjPoint)>, + alpha_multiplier: Option>, explicit_sync: bool, } @@ -416,6 +421,7 @@ impl PendingState { opt!(xwayland_serial); opt!(tearing); opt!(content_type); + opt!(alpha_multiplier); { let (dx1, dy1) = self.offset; let (dx2, dy2) = mem::take(&mut next.offset); @@ -525,6 +531,8 @@ impl WlSurface { sync_obj_surface: Default::default(), destroyed: Cell::new(false), commit_timeline: client.commit_timelines.create_timeline(), + alpha_modifier: Default::default(), + alpha: Default::default(), } } @@ -915,6 +923,11 @@ impl WlSurface { } } } + let mut alpha_changed = false; + if let Some(alpha) = pending.alpha_multiplier.take() { + alpha_changed = true; + self.alpha.set(alpha); + } let mut buffer_changed = false; let mut old_raw_size = None; let (dx, dy) = mem::take(&mut pending.offset); @@ -1076,7 +1089,7 @@ impl WlSurface { if self.need_extents_update.get() { self.calculate_extents(); } - if buffer_changed || transform_changed { + if buffer_changed || transform_changed || alpha_changed { for (_, cursor) in &self.cursors { cursor.handle_buffer_change(); cursor.update_hardware_cursor(); @@ -1278,6 +1291,10 @@ impl WlSurface { } self.set_visible(self.dnd_icons.is_not_empty() && self.client.state.root_visible()); } + + pub fn alpha(&self) -> Option { + self.alpha.get() + } } object_base! { @@ -1304,6 +1321,7 @@ impl Object for WlSurface { self.constraints.clear(); self.drm_feedback.clear(); self.commit_timeline.clear(ClearReason::BreakLoops); + self.alpha_modifier.take(); } } diff --git a/src/ifs/wl_surface/wp_alpha_modifier_surface_v1.rs b/src/ifs/wl_surface/wp_alpha_modifier_surface_v1.rs new file mode 100644 index 00000000..36ae3b65 --- /dev/null +++ b/src/ifs/wl_surface/wp_alpha_modifier_surface_v1.rs @@ -0,0 +1,78 @@ +use { + crate::{ + client::{Client, ClientError}, + ifs::wl_surface::WlSurface, + leaks::Tracker, + object::{Object, Version}, + wire::{wp_alpha_modifier_surface_v1::*, WpAlphaModifierSurfaceV1Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct WpAlphaModifierSurfaceV1 { + pub id: WpAlphaModifierSurfaceV1Id, + pub version: Version, + pub client: Rc, + pub surface: Rc, + pub tracker: Tracker, +} + +impl WpAlphaModifierSurfaceV1 { + pub fn new(id: WpAlphaModifierSurfaceV1Id, surface: &Rc, version: Version) -> Self { + Self { + id, + version, + client: surface.client.clone(), + surface: surface.clone(), + tracker: Default::default(), + } + } + + pub fn install(self: &Rc) -> Result<(), WpAlphaModifierSurfaceV1Error> { + if self.surface.alpha_modifier.is_some() { + return Err(WpAlphaModifierSurfaceV1Error::Exists); + } + self.surface.alpha_modifier.set(Some(self.clone())); + Ok(()) + } +} + +impl WpAlphaModifierSurfaceV1RequestHandler for WpAlphaModifierSurfaceV1 { + type Error = WpAlphaModifierSurfaceV1Error; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.surface.alpha_modifier.take(); + self.surface.pending.borrow_mut().alpha_multiplier = Some(None); + self.client.remove_obj(self)?; + Ok(()) + } + + fn set_multiplier(&self, req: SetMultiplier, _slf: &Rc) -> Result<(), Self::Error> { + let multiplier = if req.factor == u32::MAX { + None + } else { + Some(((req.factor as f64) / (u32::MAX as f64)) as f32) + }; + self.surface.pending.borrow_mut().alpha_multiplier = Some(multiplier); + Ok(()) + } +} + +object_base! { + self = WpAlphaModifierSurfaceV1; + version = self.version; +} + +impl Object for WpAlphaModifierSurfaceV1 {} + +simple_add_obj!(WpAlphaModifierSurfaceV1); + +#[derive(Debug, Error)] +pub enum WpAlphaModifierSurfaceV1Error { + #[error(transparent)] + ClientError(Box), + #[error("The surface already has an alpha modifier extension attached")] + Exists, +} +efrom!(WpAlphaModifierSurfaceV1Error, ClientError); diff --git a/src/ifs/wp_alpha_modifier_v1.rs b/src/ifs/wp_alpha_modifier_v1.rs new file mode 100644 index 00000000..ea88381a --- /dev/null +++ b/src/ifs/wp_alpha_modifier_v1.rs @@ -0,0 +1,106 @@ +use { + crate::{ + client::{Client, ClientError}, + globals::{Global, GlobalName}, + ifs::wl_surface::wp_alpha_modifier_surface_v1::{ + WpAlphaModifierSurfaceV1, WpAlphaModifierSurfaceV1Error, + }, + leaks::Tracker, + object::{Object, Version}, + wire::{wp_alpha_modifier_v1::*, WpAlphaModifierV1Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct WpAlphaModifierV1Global { + name: GlobalName, +} + +pub struct WpAlphaModifierV1 { + id: WpAlphaModifierV1Id, + client: Rc, + version: Version, + pub tracker: Tracker, +} + +impl WpAlphaModifierV1Global { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + fn bind_( + self: Rc, + id: WpAlphaModifierV1Id, + client: &Rc, + version: Version, + ) -> Result<(), WpAlphaModifierV1Error> { + let obj = Rc::new(WpAlphaModifierV1 { + id, + client: client.clone(), + version, + tracker: Default::default(), + }); + track!(client, obj); + client.add_client_obj(&obj)?; + Ok(()) + } +} +impl WpAlphaModifierV1RequestHandler for WpAlphaModifierV1 { + type Error = WpAlphaModifierV1Error; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.client.remove_obj(self)?; + Ok(()) + } + + fn get_surface(&self, req: GetSurface, _slf: &Rc) -> Result<(), Self::Error> { + let surface = self.client.lookup(req.surface)?; + let modifier = Rc::new(WpAlphaModifierSurfaceV1::new( + req.id, + &surface, + self.version, + )); + track!(self.client, surface); + self.client.add_client_obj(&modifier)?; + modifier.install()?; + Ok(()) + } +} + +global_base!( + WpAlphaModifierV1Global, + WpAlphaModifierV1, + WpAlphaModifierV1Error +); + +impl Global for WpAlphaModifierV1Global { + fn singleton(&self) -> bool { + true + } + + fn version(&self) -> u32 { + 1 + } +} + +simple_add_global!(WpAlphaModifierV1Global); + +object_base! { + self = WpAlphaModifierV1; + version = self.version; +} + +impl Object for WpAlphaModifierV1 {} + +simple_add_obj!(WpAlphaModifierV1); + +#[derive(Debug, Error)] +pub enum WpAlphaModifierV1Error { + #[error(transparent)] + ClientError(Box), + #[error(transparent)] + WpAlphaModifierSurfaceV1Error(#[from] WpAlphaModifierSurfaceV1Error), +} + +efrom!(WpAlphaModifierV1Error, ClientError); diff --git a/src/it/test_ifs.rs b/src/it/test_ifs.rs index eb2d633b..b795b532 100644 --- a/src/it/test_ifs.rs +++ b/src/it/test_ifs.rs @@ -1,4 +1,6 @@ -mod test_buffer; +pub mod test_alpha_modifier; +pub mod test_alpha_modifier_surface; +pub mod test_buffer; pub mod test_callback; pub mod test_compositor; pub mod test_content_type; diff --git a/src/it/test_ifs/test_alpha_modifier.rs b/src/it/test_ifs/test_alpha_modifier.rs new file mode 100644 index 00000000..242c1b28 --- /dev/null +++ b/src/it/test_ifs/test_alpha_modifier.rs @@ -0,0 +1,49 @@ +use { + crate::{ + it::{ + test_error::TestResult, + test_ifs::{ + test_alpha_modifier_surface::TestAlphaModifierSurface, test_surface::TestSurface, + }, + test_object::TestObject, + test_transport::TestTransport, + }, + wire::{wp_alpha_modifier_v1::*, WpAlphaModifierV1Id}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestAlphaModifier { + pub id: WpAlphaModifierV1Id, + pub tran: Rc, +} + +impl TestAlphaModifier { + pub fn new(tran: &Rc) -> Self { + Self { + id: tran.id(), + tran: tran.clone(), + } + } + + pub fn get_surface(&self, surface: &TestSurface) -> TestResult> { + let obj = Rc::new(TestAlphaModifierSurface { + id: self.tran.id(), + tran: self.tran.clone(), + destroyed: Cell::new(false), + }); + self.tran.add_obj(obj.clone())?; + self.tran.send(GetSurface { + self_id: self.id, + id: obj.id, + surface: surface.id, + })?; + Ok(obj) + } +} + +test_object! { + TestAlphaModifier, WpAlphaModifierV1; +} + +impl TestObject for TestAlphaModifier {} diff --git a/src/it/test_ifs/test_alpha_modifier_surface.rs b/src/it/test_ifs/test_alpha_modifier_surface.rs new file mode 100644 index 00000000..6463f7a0 --- /dev/null +++ b/src/it/test_ifs/test_alpha_modifier_surface.rs @@ -0,0 +1,41 @@ +use { + crate::{ + it::{test_error::TestError, test_object::TestObject, test_transport::TestTransport}, + wire::{wp_alpha_modifier_surface_v1::*, WpAlphaModifierSurfaceV1Id}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestAlphaModifierSurface { + pub id: WpAlphaModifierSurfaceV1Id, + pub tran: Rc, + pub destroyed: Cell, +} + +impl TestAlphaModifierSurface { + pub fn destroy(&self) -> Result<(), TestError> { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + pub fn set_multiplier(&self, factor: f64) -> Result<(), TestError> { + self.tran.send(SetMultiplier { + self_id: self.id, + factor: (factor * u32::MAX as f64) as u32, + }) + } +} + +impl Drop for TestAlphaModifierSurface { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestAlphaModifierSurface, WpAlphaModifierSurfaceV1; +} + +impl TestObject for TestAlphaModifierSurface {} diff --git a/src/it/test_ifs/test_registry.rs b/src/it/test_ifs/test_registry.rs index 8428e2f3..6c48d770 100644 --- a/src/it/test_ifs/test_registry.rs +++ b/src/it/test_ifs/test_registry.rs @@ -5,7 +5,8 @@ use { it::{ test_error::TestError, test_ifs::{ - test_compositor::TestCompositor, test_content_type_manager::TestContentTypeManager, + test_alpha_modifier::TestAlphaModifier, test_compositor::TestCompositor, + test_content_type_manager::TestContentTypeManager, test_cursor_shape_manager::TestCursorShapeManager, test_data_control_manager::TestDataControlManager, test_data_device_manager::TestDataDeviceManager, test_dmabuf::TestDmabuf, @@ -50,6 +51,7 @@ pub struct TestRegistrySingletons { pub zwlr_data_control_manager_v1: u32, pub zwp_linux_dmabuf_v1: u32, pub xdg_toplevel_drag_manager_v1: u32, + pub wp_alpha_modifier_v1: u32, } pub struct TestRegistry { @@ -73,6 +75,7 @@ pub struct TestRegistry { pub data_control_manager: CloneCell>>, pub dmabuf: CloneCell>>, pub drag_manager: CloneCell>>, + pub alpha_modifier: CloneCell>>, pub seats: CopyHashMap>, } @@ -140,6 +143,7 @@ impl TestRegistry { zwlr_data_control_manager_v1, zwp_linux_dmabuf_v1, xdg_toplevel_drag_manager_v1, + wp_alpha_modifier_v1, }; self.singletons.set(Some(singletons.clone())); Ok(singletons) @@ -227,6 +231,13 @@ impl TestRegistry { 1, TestToplevelDragManager ); + create_singleton!( + get_alpha_modifier, + alpha_modifier, + wp_alpha_modifier_v1, + 1, + TestAlphaModifier + ); pub fn bind( &self, diff --git a/src/it/test_transport.rs b/src/it/test_transport.rs index bd8f6aff..2c846d46 100644 --- a/src/it/test_transport.rs +++ b/src/it/test_transport.rs @@ -68,6 +68,7 @@ impl TestTransport { data_control_manager: Default::default(), dmabuf: Default::default(), drag_manager: Default::default(), + alpha_modifier: Default::default(), seats: Default::default(), }); self.send(wl_display::GetRegistry { diff --git a/src/it/tests.rs b/src/it/tests.rs index a8bdc8b2..6756d8ea 100644 --- a/src/it/tests.rs +++ b/src/it/tests.rs @@ -70,6 +70,7 @@ mod t0035_scanout_feedback; mod t0036_idle; mod t0037_toplevel_drag; mod t0038_subsurface_parent_state; +mod t0039_alpha_modifier; pub trait TestCase: Sync { fn name(&self) -> &'static str; @@ -127,5 +128,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> { t0036_idle, t0037_toplevel_drag, t0038_subsurface_parent_state, + t0039_alpha_modifier, } } diff --git a/src/it/tests/t0039_alpha_modifier.rs b/src/it/tests/t0039_alpha_modifier.rs new file mode 100644 index 00000000..dc567abb --- /dev/null +++ b/src/it/tests/t0039_alpha_modifier.rs @@ -0,0 +1,56 @@ +use { + crate::{ + it::{test_error::TestResult, testrun::TestRun}, + theme::Color, + }, + std::rc::Rc, +}; + +testcase!(); + +async fn test(run: Rc) -> TestResult { + let _ds = run.create_default_setup().await?; + + let client = run.create_client().await?; + let win = client.create_window().await?; + win.set_color(255, 0, 0, 255); + win.map2().await?; + + macro_rules! create_surface { + ($buf:expr, $x:expr, $y:expr) => {{ + let ss = client.comp.create_surface().await?; + let vp = client.viewporter.get_viewport(&ss)?; + vp.set_destination(100, 100)?; + ss.attach($buf.id)?; + ss.commit()?; + let alpha = client + .registry + .get_alpha_modifier() + .await? + .get_surface(&ss)?; + let sub = client.sub.get_subsurface(ss.id, win.surface.id).await?; + sub.set_desync()?; + sub.set_position($x, $y)?; + win.surface.commit()?; + (ss, alpha) + }}; + } + + let buf1 = client.spbm.create_buffer(Color::from_rgb(0, 255, 0))?; + let (ss1, alpha1) = create_surface!(&buf1, 0, 0); + + let buf2 = client.shm.create_buffer(1, 1)?; + buf2.fill(Color::from_rgb(0, 255, 0)); + let (ss2, alpha2) = create_surface!(&buf2.buffer, 100, 0); + + client.compare_screenshot("1", false).await?; + + alpha1.set_multiplier(0.5)?; + ss1.commit()?; + alpha2.set_multiplier(0.5)?; + ss2.commit()?; + + client.compare_screenshot("2", false).await?; + + Ok(()) +} diff --git a/src/it/tests/t0039_alpha_modifier/screenshot_1.qoi b/src/it/tests/t0039_alpha_modifier/screenshot_1.qoi new file mode 100644 index 0000000000000000000000000000000000000000..8fe5d0b20afa62ec1d7d2d78e5404f23f866b3a7 GIT binary patch literal 8142 zcmeI%p$@_@6ouh4_7Jm3Bw4dZA(=uhK@rqma>>kIVF?OZ*9)r{yB5p-n)Wm`{c?Lv zANN})l}G8&ma@~OA5BtnW3I-=#k9L@Ap{Q)E1&*sJ=Oj~(e%#6FkHX8&vVY(DKZ|^ z1MgvN5cC83f$s-vT`(hn00IagfB*srAbYTFxTWq_J?WfFMXw$y;VHor { let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y); self.base.render_texture( &title.tex, + None, x, y, None, @@ -167,6 +168,7 @@ impl Renderer<'_> { let (x, y) = self.base.scale_point(x + status.tex_x, y + status.tex_y); self.base.render_texture( &status.tex.texture, + None, x, y, None, @@ -215,6 +217,7 @@ impl Renderer<'_> { let y = y + (pos.height() - tex_height) / 2; self.base.render_texture( &tex.texture, + None, x, y, None, @@ -255,6 +258,7 @@ impl Renderer<'_> { let (x, y) = self.base.scale_point(x + title.x, y + title.y); self.base.render_texture( &title.tex.texture, + None, x, y, None, @@ -338,6 +342,7 @@ impl Renderer<'_> { } else { size = self.base.scale_point(size.0, size.1); } + let alpha = surface.alpha(); if let Some(children) = children.deref() { macro_rules! render { ($children:expr) => { @@ -359,10 +364,10 @@ impl Renderer<'_> { }; } render!(&children.below); - self.render_buffer(&buffer, x, y, *tpoints, size, bounds); + self.render_buffer(&buffer, alpha, x, y, *tpoints, size, bounds); render!(&children.above); } else { - self.render_buffer(&buffer, x, y, *tpoints, size, bounds); + self.render_buffer(&buffer, alpha, x, y, *tpoints, size, bounds); } if let Some(result) = self.result.as_deref_mut() { { @@ -379,6 +384,7 @@ impl Renderer<'_> { pub fn render_buffer( &mut self, buffer: &Rc, + alpha: Option, x: i32, y: i32, tpoints: SampleRect, @@ -388,6 +394,7 @@ impl Renderer<'_> { if let Some(tex) = buffer.buffer.texture.get() { self.base.render_texture( &tex, + alpha, x, y, Some(tpoints), @@ -406,7 +413,11 @@ impl Renderer<'_> { }; if !rect.is_empty() { self.base.ops.push(GfxApiOpt::Sync); - self.base.fill_boxes(&[rect], color); + let mut color = *color; + if let Some(alpha) = alpha { + color = color * alpha; + } + self.base.fill_boxes(&[rect], &color); } } } else { @@ -448,6 +459,7 @@ impl Renderer<'_> { let (x, y) = self.base.scale_point(x + bw, y + bw); self.base.render_texture( &title.texture, + None, x, y, None, diff --git a/src/renderer/renderer_base.rs b/src/renderer/renderer_base.rs index 759a8901..e29190ac 100644 --- a/src/renderer/renderer_base.rs +++ b/src/renderer/renderer_base.rs @@ -127,6 +127,7 @@ impl RendererBase<'_> { pub fn render_texture( &mut self, texture: &Rc, + alpha: Option, x: i32, y: i32, tpoints: Option, @@ -174,6 +175,7 @@ impl RendererBase<'_> { tex: texture.clone(), source: texcoord, target, + alpha, buffer_resv, acquire_sync, release_sync, diff --git a/src/state.rs b/src/state.rs index b18b6399..e98715be 100644 --- a/src/state.rs +++ b/src/state.rs @@ -836,6 +836,7 @@ impl State { sample_rect.buffer_transform = transform; renderer.base.render_texture( src, + None, x_off, y_off, Some(sample_rect), diff --git a/src/theme.rs b/src/theme.rs index 98fd5db1..f94dc0bd 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -1,6 +1,7 @@ use std::{ cell::{Cell, RefCell}, cmp::Ordering, + ops::Mul, }; #[derive(Copy, Clone, Debug, PartialEq)] @@ -23,6 +24,19 @@ impl Ord for Color { } } +impl Mul for Color { + type Output = Self; + + fn mul(self, rhs: f32) -> Self::Output { + Self { + r: self.r * rhs, + g: self.g * rhs, + b: self.b * rhs, + a: self.a * rhs, + } + } +} + impl PartialOrd for Color { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) diff --git a/wire/wp_alpha_modifier_surface_v1.txt b/wire/wp_alpha_modifier_surface_v1.txt new file mode 100644 index 00000000..03a5d051 --- /dev/null +++ b/wire/wp_alpha_modifier_surface_v1.txt @@ -0,0 +1,6 @@ +request destroy { +} + +request set_multiplier { + factor: u32, +} diff --git a/wire/wp_alpha_modifier_v1.txt b/wire/wp_alpha_modifier_v1.txt new file mode 100644 index 00000000..1b2e0c7f --- /dev/null +++ b/wire/wp_alpha_modifier_v1.txt @@ -0,0 +1,7 @@ +request destroy { +} + +request get_surface { + id: id(wp_alpha_modifier_surface_v1), + surface: id(wl_surface), +}