diff --git a/src/backends/metal/present.rs b/src/backends/metal/present.rs index a4edf006..810531be 100644 --- a/src/backends/metal/present.rs +++ b/src/backends/metal/present.rs @@ -9,8 +9,8 @@ use { }, cmm::cmm_description::ColorDescription, gfx_api::{ - AcquireSync, BufferResv, GfxApiOpt, GfxRenderPass, GfxTexture, ReleaseSync, SyncFile, - create_render_pass, + AcquireSync, AlphaMode, BufferResv, GfxApiOpt, GfxRenderPass, GfxTexture, ReleaseSync, + SyncFile, create_render_pass, }, ifs::wl_output::BlendSpace, rect::Region, @@ -668,6 +668,10 @@ impl MetalConnector { } return None; }; + if ct.alpha_mode != AlphaMode::PremultipliedElectrical { + // Direct scanout requires premultiplied electrical alpha. + return None; + } if !ct.cd.embeds_into(cd) { // Direct scanout requires embeddable color descriptions. return None; diff --git a/src/cmm/cmm_transform.rs b/src/cmm/cmm_transform.rs index 2c2816a7..64e576ca 100644 --- a/src/cmm/cmm_transform.rs +++ b/src/cmm/cmm_transform.rs @@ -1,6 +1,7 @@ use { crate::{ cmm::{cmm_eotf::Eotf, cmm_primaries::Primaries}, + gfx_api::AlphaMode, theme::Color, utils::ordered_float::F64, }, @@ -140,11 +141,14 @@ impl Mul for ColorMatrix { } } let [r, g, b] = self * [rgba[0] as f64, rgba[1] as f64, rgba[2] as f64]; - let mut color = Color::new(Eotf::Linear, r as f32, g as f32, b as f32); - if a < 1.0 { - color = color * a; - } - color + Color::new( + Eotf::Linear, + AlphaMode::Straight, + r as f32, + g as f32, + b as f32, + a, + ) } } diff --git a/src/cursor.rs b/src/cursor.rs index 65c1ee39..efcd7339 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -3,7 +3,7 @@ use { async_engine::AsyncEngine, fixed::Fixed, format::ARGB8888, - gfx_api::{AcquireSync, GfxContext, GfxError, GfxTexture, ReleaseSync}, + gfx_api::{AcquireSync, AlphaMode, GfxContext, GfxError, GfxTexture, ReleaseSync}, rect::Rect, renderer::Renderer, scale::Scale, @@ -398,6 +398,7 @@ fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed ReleaseSync::None, false, renderer.state.color_manager.srgb_gamma22(), + AlphaMode::PremultipliedElectrical, ); } } @@ -423,6 +424,7 @@ impl Cursor for StaticCursor { ReleaseSync::None, false, renderer.state.color_manager.srgb_gamma22(), + AlphaMode::PremultipliedElectrical, ); } } @@ -466,6 +468,7 @@ impl Cursor for AnimatedCursor { ReleaseSync::None, false, renderer.state.color_manager.srgb_gamma22(), + AlphaMode::PremultipliedElectrical, ); } } diff --git a/src/gfx_api.rs b/src/gfx_api.rs index 44704c20..cb4f6643 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -219,6 +219,7 @@ pub struct CopyTexture { pub alpha: Option, pub opaque: bool, pub cd: Rc, + pub alpha_mode: AlphaMode, } #[derive(Clone, Debug, PartialEq)] @@ -292,6 +293,14 @@ pub enum ResetStatus { Other(u32), } +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)] +pub enum AlphaMode { + #[default] + PremultipliedElectrical, + PremultipliedOptical, + Straight, +} + pub trait GfxBlendBuffer: Any + Debug {} pub trait GfxFramebuffer: Debug { @@ -435,6 +444,7 @@ impl dyn GfxFramebuffer { release_sync, false, texture_cd, + AlphaMode::PremultipliedElectrical, ); let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT); self.render( @@ -817,6 +827,10 @@ pub trait GfxContext: Debug { false } + fn supports_alpha_modes(&self) -> bool { + false + } + fn supports_invalid_modifier(&self) -> bool { false } diff --git a/src/gfx_apis/vulkan.rs b/src/gfx_apis/vulkan.rs index ad0a5943..36662e47 100644 --- a/src/gfx_apis/vulkan.rs +++ b/src/gfx_apis/vulkan.rs @@ -1,4 +1,5 @@ mod allocator; +mod alpha_modes; mod blend_buffer; mod bo_allocator; mod buffer_cache; @@ -387,6 +388,10 @@ impl GfxContext for Context { self.0.device.descriptor_buffer.is_some() } + fn supports_alpha_modes(&self) -> bool { + self.0.device.descriptor_buffer.is_some() + } + fn create_dmabuf_buffer( &self, dmabuf: &OwnedFd, diff --git a/src/gfx_apis/vulkan/alpha_modes.rs b/src/gfx_apis/vulkan/alpha_modes.rs new file mode 100644 index 00000000..9480c2b0 --- /dev/null +++ b/src/gfx_apis/vulkan/alpha_modes.rs @@ -0,0 +1,15 @@ +use crate::gfx_api::AlphaMode; + +pub const AM_PREMULTIPLIED_ELECTRICAL: u32 = 0; +pub const AM_PREMULTIPLIED_OPTICAL: u32 = 1; +pub const AM_STRAIGHT: u32 = 2; + +impl AlphaMode { + pub fn to_vulkan(self) -> u32 { + match self { + AlphaMode::PremultipliedElectrical => AM_PREMULTIPLIED_ELECTRICAL, + AlphaMode::PremultipliedOptical => AM_PREMULTIPLIED_OPTICAL, + AlphaMode::Straight => AM_STRAIGHT, + } + } +} diff --git a/src/gfx_apis/vulkan/pipeline.rs b/src/gfx_apis/vulkan/pipeline.rs index 3e446eb2..5351a483 100644 --- a/src/gfx_apis/vulkan/pipeline.rs +++ b/src/gfx_apis/vulkan/pipeline.rs @@ -1,7 +1,10 @@ use { - crate::gfx_apis::vulkan::{ - VulkanError, descriptor::VulkanDescriptorSetLayout, device::VulkanDevice, - shaders::VulkanShader, + crate::{ + gfx_api::AlphaMode, + gfx_apis::vulkan::{ + VulkanError, descriptor::VulkanDescriptorSetLayout, device::VulkanDevice, + shaders::VulkanShader, + }, }, arrayvec::ArrayVec, ash::{ @@ -37,6 +40,7 @@ pub(super) struct PipelineCreateInfo { pub(super) blend: bool, pub(super) src_has_alpha: bool, pub(super) has_alpha_mult: bool, + pub(super) alpha_mode: AlphaMode, pub(super) eotf: u32, pub(super) inv_eotf: u32, pub(super) descriptor_set_layouts: ArrayVec, 2>, @@ -76,8 +80,8 @@ impl VulkanDevice { }; let destroy_layout = on_drop(|| unsafe { self.device.destroy_pipeline_layout(pipeline_layout, None) }); - let mut frag_spec_data = ArrayVec::<_, { 5 * 4 }>::new(); - let mut frag_spec_entries = ArrayVec::<_, 5>::new(); + let mut frag_spec_data = ArrayVec::<_, { 6 * 4 }>::new(); + let mut frag_spec_entries = ArrayVec::<_, 6>::new(); let mut frag_spec_entry = |data: &[u8]| { let entry = SpecializationMapEntry::default() .constant_id(frag_spec_entries.len() as _) @@ -91,6 +95,7 @@ impl VulkanDevice { frag_spec_entry(&info.eotf.to_ne_bytes()); frag_spec_entry(&info.inv_eotf.to_ne_bytes()); frag_spec_entry(&(info.has_color_management_data as u32).to_ne_bytes()); + frag_spec_entry(&info.alpha_mode.to_vulkan().to_ne_bytes()); let frag_spec = SpecializationInfo::default() .map_entries(&frag_spec_entries) .data(&frag_spec_data); diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 29a983a2..44d11c2c 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -8,8 +8,8 @@ use { }, cpu_worker::PendingJob, gfx_api::{ - AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxBlendBuffer, GfxFormat, - GfxTexture, GfxWriteModifier, ReleaseSync, SyncFile, + AcquireSync, AlphaMode, BufferResv, BufferResvUser, GfxApiOpt, GfxBlendBuffer, + GfxFormat, GfxTexture, GfxWriteModifier, ReleaseSync, SyncFile, }, gfx_apis::vulkan::{ VulkanError, @@ -209,6 +209,7 @@ struct VulkanTexOp { alpha: f32, source_type: TexSourceType, copy_type: TexCopyType, + alpha_mode: AlphaMode, range_address: DeviceAddress, instances: u32, tex_cd: Rc, @@ -258,6 +259,7 @@ type FillPipelines = Rc>>; struct TexPipelineKey { tex_copy_type: TexCopyType, tex_source_type: TexSourceType, + tex_alpha_mode: AlphaMode, eotf: VulkanEotf, has_color_management_data: bool, } @@ -431,6 +433,7 @@ impl VulkanRenderer { blend: src_has_alpha, src_has_alpha, has_alpha_mult: false, + alpha_mode: AlphaMode::PremultipliedOptical, // all transformations are applied in the compositor eotf: EOTF_LINEAR, inv_eotf: EOTF_LINEAR, @@ -474,11 +477,16 @@ impl VulkanRenderer { tex_cd: &ColorDescription, tex_copy_type: TexCopyType, tex_source_type: TexSourceType, + mut tex_alpha_mode: AlphaMode, has_color_management_data: bool, ) -> Result, VulkanError> { + if tex_source_type == TexSourceType::Opaque { + tex_alpha_mode = AlphaMode::PremultipliedElectrical; + } let key = TexPipelineKey { tex_copy_type, tex_source_type, + tex_alpha_mode, eotf: tex_cd.eotf.to_vulkan(), has_color_management_data, }; @@ -505,6 +513,7 @@ impl VulkanRenderer { blend: src_has_alpha || has_alpha_mult, src_has_alpha, has_alpha_mult, + alpha_mode: key.tex_alpha_mode, eotf: key.eotf.to_vulkan(), inv_eotf: pipelines.eotf.to_vulkan(), descriptor_set_layouts: self.tex_descriptor_set_layouts.clone(), @@ -543,6 +552,7 @@ impl VulkanRenderer { blend: false, src_has_alpha: true, has_alpha_mult: false, + alpha_mode: AlphaMode::PremultipliedElectrical, eotf: key.eotf.to_vulkan(), inv_eotf: fb_eotf.to_vulkan(), descriptor_set_layouts, @@ -886,6 +896,7 @@ impl VulkanRenderer { alpha: ct.alpha.unwrap_or_default(), source_type, copy_type, + alpha_mode: ct.alpha_mode, range_address: 0, instances: 0, tex_cd: ct.cd.clone(), @@ -1333,6 +1344,7 @@ impl VulkanRenderer { &c.tex_cd, c.copy_type, c.source_type, + c.alpha_mode, c.color_management_data_address.is_some(), )?; bind(&pipeline); diff --git a/src/gfx_apis/vulkan/shaders/alpha_modes.glsl b/src/gfx_apis/vulkan/shaders/alpha_modes.glsl new file mode 100644 index 00000000..1bb87a3e --- /dev/null +++ b/src/gfx_apis/vulkan/shaders/alpha_modes.glsl @@ -0,0 +1,8 @@ +#ifndef ALPHA_MODES_GLSL +#define ALPHA_MODES_GLSL + +#define AM_PREMULTIPLIED_ELECTRICAL 0 +#define AM_PREMULTIPLIED_OPTICAL 1 +#define AM_STRAIGHT 2 + +#endif diff --git a/src/gfx_apis/vulkan/shaders/frag_spec_const.glsl b/src/gfx_apis/vulkan/shaders/frag_spec_const.glsl index cf7f8a7c..29e3b2ef 100644 --- a/src/gfx_apis/vulkan/shaders/frag_spec_const.glsl +++ b/src/gfx_apis/vulkan/shaders/frag_spec_const.glsl @@ -6,5 +6,6 @@ layout(constant_id = 1) const bool has_alpha_multiplier = false; layout(constant_id = 2) const uint eotf = 0; layout(constant_id = 3) const uint inv_eotf = 0; layout(constant_id = 4) const bool has_matrix = false; +layout(constant_id = 5) const uint alpha_mode = 0; #endif diff --git a/src/gfx_apis/vulkan/shaders/tex.frag b/src/gfx_apis/vulkan/shaders/tex.frag index df6ee5ec..e1ccacda 100644 --- a/src/gfx_apis/vulkan/shaders/tex.frag +++ b/src/gfx_apis/vulkan/shaders/tex.frag @@ -8,6 +8,7 @@ #include "tex.common.glsl" #include "tex_set.glsl" #include "eotfs.glsl" +#include "alpha_modes.glsl" layout(set = 0, binding = 0) uniform sampler sam; layout(location = 0) in vec2 tex_pos; @@ -15,12 +16,15 @@ layout(location = 0) out vec4 out_color; void main() { vec4 c = textureLod(sampler2D(tex, sam), tex_pos, 0); - if (eotf != inv_eotf || has_matrix) { + if (eotf != inv_eotf || has_matrix || alpha_mode != AM_PREMULTIPLIED_ELECTRICAL) { vec3 rgb = c.rgb; - if (src_has_alpha) { + if (src_has_alpha && alpha_mode == AM_PREMULTIPLIED_ELECTRICAL) { rgb /= mix(c.a, 1.0, c.a == 0.0); } rgb = apply_eotf(rgb); + if (src_has_alpha && alpha_mode == AM_PREMULTIPLIED_OPTICAL) { + rgb /= mix(c.a, 1.0, c.a == 0.0); + } if (has_matrix) { rgb = (cm_data.matrix * vec4(rgb, 1.0)).rgb; } diff --git a/src/gfx_apis/vulkan/shaders_bin/legacy_fill.frag.spv b/src/gfx_apis/vulkan/shaders_bin/legacy_fill.frag.spv index 961877a7..b1377f0a 100644 Binary files a/src/gfx_apis/vulkan/shaders_bin/legacy_fill.frag.spv and b/src/gfx_apis/vulkan/shaders_bin/legacy_fill.frag.spv differ diff --git a/src/gfx_apis/vulkan/shaders_bin/legacy_tex.frag.spv b/src/gfx_apis/vulkan/shaders_bin/legacy_tex.frag.spv index 1c2f806f..9664f044 100644 Binary files a/src/gfx_apis/vulkan/shaders_bin/legacy_tex.frag.spv and b/src/gfx_apis/vulkan/shaders_bin/legacy_tex.frag.spv differ diff --git a/src/gfx_apis/vulkan/shaders_bin/out.frag.spv b/src/gfx_apis/vulkan/shaders_bin/out.frag.spv index d7421166..8490503a 100644 Binary files a/src/gfx_apis/vulkan/shaders_bin/out.frag.spv and b/src/gfx_apis/vulkan/shaders_bin/out.frag.spv differ diff --git a/src/gfx_apis/vulkan/shaders_bin/tex.frag.spv b/src/gfx_apis/vulkan/shaders_bin/tex.frag.spv index 6146efa8..c013804d 100644 Binary files a/src/gfx_apis/vulkan/shaders_bin/tex.frag.spv and b/src/gfx_apis/vulkan/shaders_bin/tex.frag.spv differ diff --git a/src/gfx_apis/vulkan/shaders_hash.txt b/src/gfx_apis/vulkan/shaders_hash.txt index 9d144084..ab855a2d 100644 --- a/src/gfx_apis/vulkan/shaders_hash.txt +++ b/src/gfx_apis/vulkan/shaders_hash.txt @@ -1,8 +1,9 @@ +302a9f250bdc4f8e0e71a9f77c9a8a7aa55fd003bc91c2422a700c4abd83f54e src/gfx_apis/vulkan/shaders/alpha_modes.glsl b6a0df1e231fab533499329636b7a580384784418baee06c147af5fcc384cf5c src/gfx_apis/vulkan/shaders/eotfs.glsl 8a38df18851cd13884499820f26939fb7319f45d913d867f254d8118d59fb117 src/gfx_apis/vulkan/shaders/fill.common.glsl 21c488d12aa5ad2f109ec44cb856dfe837e02ea9025b5ed64439d742c17cbf30 src/gfx_apis/vulkan/shaders/fill.frag 4fb481d8d73afdfb0d8f077eb8665d86f06c8a32a91e44ed369ef5dff554646d src/gfx_apis/vulkan/shaders/fill.vert -63af15c4e00587a7bb8494934c88d9874712a511217829b50f3c08fa3c461082 src/gfx_apis/vulkan/shaders/frag_spec_const.glsl +f93524fd077bc9984702b1e0f92232f80bfe28a0a92439dc164c1ea41fd16d64 src/gfx_apis/vulkan/shaders/frag_spec_const.glsl c315a064b48dd5bdb607a6b79c30d31b6e59ffec69e93d50ab875abf97c41bbf src/gfx_apis/vulkan/shaders/legacy/fill.common.glsl 590d061b97446fc501158609eaf098b71bc7b328c008b586ff36613ce690d618 src/gfx_apis/vulkan/shaders/legacy/fill.frag ad22a79e1a88a12daa40c0a2b953084c129a408297c8ca544d60e0b6001470b9 src/gfx_apis/vulkan/shaders/legacy/fill.vert @@ -13,6 +14,6 @@ e0a8769dd7938dd02e66db9e9048ed6bef8f8c42671f2e2c7a7976a6d498f685 src/gfx_apis/vu 5069f619c7d722815a022e2d84720a2d8290af49a3ed49ea0cd26b52115cc39a src/gfx_apis/vulkan/shaders/out.frag 0adc7e12328c15fb3e7e6c8b8701a182223c2f15337e14131f41dd247e697809 src/gfx_apis/vulkan/shaders/out.vert e22d4d3318a350def8ef19c7b27dc6a308a84c2fe9d7c02b81107f72073cd481 src/gfx_apis/vulkan/shaders/tex.common.glsl -06993d4d882fe5c651e5ab54f0116b9622352a97f3575985076ef464b472dd39 src/gfx_apis/vulkan/shaders/tex.frag +1f196cee646a934072beb3e5648a5042c035953d9a0c26b0a22e330c2f8bb994 src/gfx_apis/vulkan/shaders/tex.frag 423cf327c9fcc4070dbf75321c1224a1589b6cf3d2f1ea5e8bd0362e1a9f3aa1 src/gfx_apis/vulkan/shaders/tex.vert b982f7101c22931a33b32dce3408387f3392c0f0ad0ca5852da265b0d12856bb src/gfx_apis/vulkan/shaders/tex_set.glsl diff --git a/src/ifs/jay_damage_tracking.rs b/src/ifs/jay_damage_tracking.rs index 7d58ed59..04d30db0 100644 --- a/src/ifs/jay_damage_tracking.rs +++ b/src/ifs/jay_damage_tracking.rs @@ -2,6 +2,7 @@ use { crate::{ client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientError}, cmm::cmm_eotf::Eotf, + gfx_api::AlphaMode, globals::{Global, GlobalName}, leaks::Tracker, object::{Object, Version}, @@ -97,7 +98,14 @@ impl JayDamageTrackingRequestHandler for JayDamageTracking { req: SetVisualizerColor, _slf: &Rc, ) -> Result<(), Self::Error> { - let color = Color::new(Eotf::Gamma22, req.r, req.g, req.b) * req.a; + let color = Color::new( + Eotf::Gamma22, + AlphaMode::Straight, + req.r, + req.g, + req.b, + req.a, + ); self.client.state.damage_visualizer.set_color(color); Ok(()) } diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 4ef51ed3..4477d61c 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -30,7 +30,7 @@ use { drm_feedback::DrmFeedback, fixed::Fixed, gfx_api::{ - AsyncShmGfxTexture, BufferResv, BufferResvUser, GfxError, GfxStagingBuffer, + AlphaMode, AsyncShmGfxTexture, BufferResv, BufferResvUser, GfxError, GfxStagingBuffer, ReleaseSync, SampleRect, SyncFile, }, ifs::{ @@ -353,6 +353,7 @@ pub struct WlSurface { CopyHashMap>, color_description: CloneCell>>, color_representation_surface: CloneCell>>, + alpha_mode: Cell, } impl Debug for WlSurface { @@ -490,6 +491,7 @@ struct PendingState { tray_item_ack_serial: Option, color_description: Option>>, serial: Option, + alpha_mode: Option, } struct AttachedSubsurfaceState { @@ -544,6 +546,7 @@ impl PendingState { opt!(tray_item_ack_serial); opt!(color_description); opt!(serial); + opt!(alpha_mode); { let (dx1, dy1) = self.offset; let (dx2, dy2) = mem::take(&mut next.offset); @@ -708,6 +711,7 @@ impl WlSurface { color_management_feedback: Default::default(), color_description: Default::default(), color_representation_surface: Default::default(), + alpha_mode: Default::default(), } } @@ -1202,6 +1206,12 @@ impl WlSurface { color_description_changed = true; self.color_description.set(desc); } + let mut alpha_mode_changed = false; + if let Some(alpha_mode) = pending.alpha_mode.take() + && self.alpha_mode.replace(alpha_mode) != alpha_mode + { + alpha_mode_changed = true; + } let mut alpha_changed = false; if let Some(alpha) = pending.alpha_multiplier.take() { alpha_changed = true; @@ -1213,7 +1223,8 @@ impl WlSurface { || buffer_transform_changed || viewport_changed || alpha_changed - || color_description_changed; + || color_description_changed + || alpha_mode_changed; let mut buffer_changed = false; let mut old_raw_size = None; let (mut dx, mut dy) = mem::take(&mut pending.offset); @@ -1725,6 +1736,10 @@ impl WlSurface { self.alpha.get() } + pub fn alpha_mode(&self) -> AlphaMode { + self.alpha_mode.get() + } + pub fn opaque(&self) -> bool { self.is_opaque.get() } diff --git a/src/ifs/wl_surface/wp_color_representation_surface_v1.rs b/src/ifs/wl_surface/wp_color_representation_surface_v1.rs index 87300e28..f4173fb7 100644 --- a/src/ifs/wl_surface/wp_color_representation_surface_v1.rs +++ b/src/ifs/wl_surface/wp_color_representation_surface_v1.rs @@ -1,6 +1,7 @@ use { crate::{ client::{Client, ClientError}, + gfx_api::AlphaMode, ifs::wl_surface::WlSurface, leaks::Tracker, object::{Object, Version}, @@ -22,12 +23,11 @@ pub struct WpColorRepresentationSurfaceV1 { pub version: Version, pub tracker: Tracker, pub surface: Rc, + pub supports_alpha_modes: bool, } pub const AM_PREMULTIPLIED_ELECTRICAL: u32 = 0; -#[expect(dead_code)] pub const AM_PREMULTIPLIED_OPTICAL: u32 = 1; -#[expect(dead_code)] pub const AM_STRAIGHT: u32 = 2; impl WpColorRepresentationSurfaceV1 { @@ -47,16 +47,22 @@ impl WpColorRepresentationSurfaceV1RequestHandler for WpColorRepresentationSurfa fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { self.surface.color_representation_surface.take(); + self.surface.pending.borrow_mut().alpha_mode = Some(Default::default()); self.client.remove_obj(self)?; Ok(()) } fn set_alpha_mode(&self, req: SetAlphaMode, _slf: &Rc) -> Result<(), Self::Error> { - if req.alpha_mode != AM_PREMULTIPLIED_ELECTRICAL { - return Err(WpColorRepresentationSurfaceV1Error::UnsupportedAlphaMode( - req.alpha_mode, - )); - } + let sam = self.supports_alpha_modes; + let alpha_mode = match req.alpha_mode { + AM_PREMULTIPLIED_ELECTRICAL => AlphaMode::PremultipliedElectrical, + AM_PREMULTIPLIED_OPTICAL if sam => AlphaMode::PremultipliedOptical, + AM_STRAIGHT if sam => AlphaMode::Straight, + n => { + return Err(WpColorRepresentationSurfaceV1Error::UnsupportedAlphaMode(n)); + } + }; + self.surface.pending.borrow_mut().alpha_mode = Some(alpha_mode); Ok(()) } diff --git a/src/ifs/wp_color_representation_manager_v1.rs b/src/ifs/wp_color_representation_manager_v1.rs index 9e43c8eb..9ebc7c15 100644 --- a/src/ifs/wp_color_representation_manager_v1.rs +++ b/src/ifs/wp_color_representation_manager_v1.rs @@ -3,8 +3,8 @@ use { client::{Client, ClientError}, globals::{Global, GlobalName}, ifs::wl_surface::wp_color_representation_surface_v1::{ - AM_PREMULTIPLIED_ELECTRICAL, WpColorRepresentationSurfaceV1, - WpColorRepresentationSurfaceV1Error, + AM_PREMULTIPLIED_ELECTRICAL, AM_PREMULTIPLIED_OPTICAL, AM_STRAIGHT, + WpColorRepresentationSurfaceV1, WpColorRepresentationSurfaceV1Error, }, leaks::Tracker, object::{Object, Version}, @@ -35,11 +35,18 @@ impl WpColorRepresentationManagerV1Global { client: &Rc, version: Version, ) -> Result<(), WpColorRepresentationManagerV1Error> { + let mut supports_alpha_modes = false; + if let Some(ctx) = client.state.render_ctx.get() + && ctx.supports_alpha_modes() + { + supports_alpha_modes = true; + } let obj = Rc::new(WpColorRepresentationManagerV1 { id, client: client.clone(), tracker: Default::default(), version, + supports_alpha_modes, }); track!(client, obj); client.add_client_obj(&obj)?; @@ -53,11 +60,16 @@ pub struct WpColorRepresentationManagerV1 { pub client: Rc, pub version: Version, pub tracker: Tracker, + pub supports_alpha_modes: bool, } impl WpColorRepresentationManagerV1 { fn send_capabilities(&self) { self.send_supported_alpha_mode(AM_PREMULTIPLIED_ELECTRICAL); + if self.supports_alpha_modes { + self.send_supported_alpha_mode(AM_PREMULTIPLIED_OPTICAL); + self.send_supported_alpha_mode(AM_STRAIGHT); + } self.send_done(); } @@ -89,6 +101,7 @@ impl WpColorRepresentationManagerV1RequestHandler for WpColorRepresentationManag version: self.version, tracker: Default::default(), surface: surface.clone(), + supports_alpha_modes: self.supports_alpha_modes, }); track!(self.client, obj); self.client.add_client_obj(&obj)?; diff --git a/src/portal/ptr_gui.rs b/src/portal/ptr_gui.rs index d508c22c..7f01fc4e 100644 --- a/src/portal/ptr_gui.rs +++ b/src/portal/ptr_gui.rs @@ -7,7 +7,8 @@ use { fixed::Fixed, format::ARGB8888, gfx_api::{ - AcquireSync, GfxContext, GfxFramebuffer, GfxTexture, ReleaseSync, needs_render_usage, + AcquireSync, AlphaMode, GfxContext, GfxFramebuffer, GfxTexture, ReleaseSync, + needs_render_usage, }, ifs::zwlr_layer_shell_v1::OVERLAY, portal::{ @@ -230,6 +231,7 @@ impl GuiElement for Button { ReleaseSync::None, false, srgb_srgb, + AlphaMode::PremultipliedElectrical, ); } } @@ -332,6 +334,7 @@ impl GuiElement for Label { ReleaseSync::None, false, color_manager.srgb_gamma22(), + AlphaMode::PremultipliedElectrical, ); } } diff --git a/src/renderer.rs b/src/renderer.rs index 2c94020f..74d56b2e 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,6 +1,6 @@ use { crate::{ - gfx_api::{AcquireSync, GfxApiOpt, ReleaseSync, SampleRect}, + gfx_api::{AcquireSync, AlphaMode, GfxApiOpt, ReleaseSync, SampleRect}, icons::{IconState, SizedIcons}, ifs::wl_surface::{ SurfaceBuffer, WlSurface, @@ -145,6 +145,7 @@ impl Renderer<'_> { ReleaseSync::None, false, self.state.color_manager.srgb_gamma22(), + AlphaMode::PremultipliedElectrical, ); } x += bar_rect.x1() - non_exclusive_rect_rel.x1(); @@ -167,6 +168,7 @@ impl Renderer<'_> { ReleaseSync::None, false, srgb_srgb, + AlphaMode::PremultipliedElectrical, ); } for item in output.tray_items.iter() { @@ -253,6 +255,7 @@ impl Renderer<'_> { ReleaseSync::None, false, self.state.color_manager.srgb_gamma22(), + AlphaMode::PremultipliedElectrical, ); } self.render_tl_aux(placeholder.tl_data(), bounds, true); @@ -304,6 +307,7 @@ impl Renderer<'_> { ReleaseSync::None, false, srgb_srgb, + AlphaMode::PremultipliedElectrical, ); } } @@ -466,6 +470,7 @@ impl Renderer<'_> { ) { let alpha = surface.alpha(); let cd = surface.color_description(); + let alpha_mode = surface.alpha_mode(); if let Some(tex) = buffer.buffer.get_texture(surface) { let mut opaque = surface.opaque(); if !opaque && tex.format().has_alpha { @@ -485,6 +490,7 @@ impl Renderer<'_> { buffer.release_sync, opaque, &cd, + alpha_mode, ); } else if let Some(color) = &buffer.buffer.color { if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) { @@ -493,8 +499,8 @@ impl Renderer<'_> { Some(bounds) => rect.intersect(*bounds), }; if !rect.is_empty() { - let color = Color::from_u32_premultiplied( - cd.eotf, color[0], color[1], color[2], color[3], + let color = Color::from_u32( + cd.eotf, alpha_mode, color[0], color[1], color[2], color[3], ); self.base.sync(); self.base @@ -581,6 +587,7 @@ impl Renderer<'_> { ReleaseSync::None, false, srgb_srgb, + AlphaMode::PremultipliedElectrical, ); } x1 += th; @@ -603,6 +610,7 @@ impl Renderer<'_> { ReleaseSync::None, false, srgb_srgb, + AlphaMode::PremultipliedElectrical, ); } let body = Rect::new_sized_saturating( diff --git a/src/renderer/renderer_base.rs b/src/renderer/renderer_base.rs index 962c18db..ab84c6a9 100644 --- a/src/renderer/renderer_base.rs +++ b/src/renderer/renderer_base.rs @@ -2,8 +2,8 @@ use { crate::{ cmm::cmm_description::{ColorDescription, LinearColorDescription}, gfx_api::{ - AcquireSync, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture, - ReleaseSync, SampleRect, + AcquireSync, AlphaMode, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt, + GfxTexture, ReleaseSync, SampleRect, }, rect::Rect, scale::Scale, @@ -181,6 +181,7 @@ impl RendererBase<'_> { release_sync: ReleaseSync, opaque: bool, cd: &Rc, + alpha_mode: AlphaMode, ) { // log::info!("rendering texture {:?}", std::ptr::from_ref(&**texture) as *const u8); // log::info!("{:?}", backtrace::Backtrace::new()); @@ -228,6 +229,7 @@ impl RendererBase<'_> { release_sync, opaque, cd: cd.clone(), + alpha_mode, })); } diff --git a/src/state.rs b/src/state.rs index 667d8fd9..fd18d498 100644 --- a/src/state.rs +++ b/src/state.rs @@ -31,8 +31,9 @@ use { forker::ForkerProxy, format::Format, gfx_api::{ - AcquireSync, BufferResv, GfxBlendBuffer, GfxContext, GfxError, GfxFramebuffer, - GfxTexture, PendingShmTransfer, ReleaseSync, STAGING_DOWNLOAD, SampleRect, SyncFile, + AcquireSync, AlphaMode, BufferResv, GfxBlendBuffer, GfxContext, GfxError, + GfxFramebuffer, GfxTexture, PendingShmTransfer, ReleaseSync, STAGING_DOWNLOAD, + SampleRect, SyncFile, }, gfx_apis::create_gfx_context, globals::{Globals, GlobalsError, RemovableWaylandGlobal, WaylandGlobal}, @@ -1272,6 +1273,7 @@ impl State { release_sync, false, src_cd, + AlphaMode::PremultipliedElectrical, ); if render_hardware_cursors && let Some(cursor_user_group) = self.cursor_user_group_hardware_cursor.get() diff --git a/src/theme.rs b/src/theme.rs index 91602aeb..8deda63d 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -3,6 +3,7 @@ use { crate::{ cmm::cmm_eotf::{Eotf, bt1886_eotf_args, bt1886_inv_eotf_args}, + gfx_api::AlphaMode, utils::clonecell::CloneCell, }, jay_config::theme::BarPosition, @@ -72,7 +73,27 @@ impl Color { a: 1.0, }; - pub fn new(eotf: Eotf, mut r: f32, mut g: f32, mut b: f32) -> Self { + pub fn new( + eotf: Eotf, + alpha_mode: AlphaMode, + mut r: f32, + mut g: f32, + mut b: f32, + a: f32, + ) -> Self { + if eotf == Eotf::Linear { + if alpha_mode == AlphaMode::Straight && a < 1.0 { + for c in [&mut r, &mut g, &mut b] { + *c *= a; + } + } + return Self { r, g, b, a }; + } + if alpha_mode == AlphaMode::PremultipliedElectrical && a < 1.0 && a > 0.0 { + for c in [&mut r, &mut g, &mut b] { + *c /= a; + } + } #[inline(always)] fn linear(c: f32) -> f32 { c @@ -144,23 +165,12 @@ impl Color { } Eotf::CompoundPower24 => convert!(compound_power_2_4), } - Self { r, g, b, a: 1.0 } - } - - pub fn new_premultiplied(eotf: Eotf, mut r: f32, mut g: f32, mut b: f32, a: f32) -> Self { - if eotf == Eotf::Linear { - return Self { r, g, b, a }; - } - if a < 1.0 && a > 0.0 { + if alpha_mode != AlphaMode::PremultipliedOptical && a < 1.0 { for c in [&mut r, &mut g, &mut b] { - *c /= a; + *c *= a; } } - let mut c = Self::new(eotf, r, g, b); - if a < 1.0 { - c = c * a; - } - c + Self { r, g, b, a } } pub fn is_opaque(&self) -> bool { @@ -172,26 +182,43 @@ impl Color { } pub fn from_srgb(r: u8, g: u8, b: u8) -> Self { - Self::new(Eotf::Gamma22, to_f32(r), to_f32(g), to_f32(b)) + Self::new( + Eotf::Gamma22, + AlphaMode::PremultipliedOptical, + to_f32(r), + to_f32(g), + to_f32(b), + 1.0, + ) } pub fn from_srgba_premultiplied(r: u8, g: u8, b: u8, a: u8) -> Self { - Self::new_premultiplied(Eotf::Gamma22, to_f32(r), to_f32(g), to_f32(b), to_f32(a)) + Self::new( + Eotf::Gamma22, + AlphaMode::PremultipliedElectrical, + to_f32(r), + to_f32(g), + to_f32(b), + to_f32(a), + ) } - pub fn from_u32_premultiplied(eotf: Eotf, r: u32, g: u32, b: u32, a: u32) -> Self { + pub fn from_u32(eotf: Eotf, alpha_mode: AlphaMode, r: u32, g: u32, b: u32, a: u32) -> Self { fn to_f32(c: u32) -> f32 { ((c as f64) / (u32::MAX as f64)) as f32 } - Self::new_premultiplied(eotf, to_f32(r), to_f32(g), to_f32(b), to_f32(a)) + Self::new(eotf, alpha_mode, to_f32(r), to_f32(g), to_f32(b), to_f32(a)) } pub fn from_srgba_straight(r: u8, g: u8, b: u8, a: u8) -> Self { - let mut c = Self::new(Eotf::Gamma22, to_f32(r), to_f32(g), to_f32(b)); - if a < 255 { - c = c * to_f32(a); - } - c + Self::new( + Eotf::Gamma22, + AlphaMode::Straight, + to_f32(r), + to_f32(g), + to_f32(b), + to_f32(a), + ) } pub fn to_srgba_premultiplied(self) -> [u8; 4] { @@ -314,7 +341,14 @@ impl Color { impl From for Color { fn from(f: jay_config::theme::Color) -> Self { let [r, g, b, a] = f.to_f32_premultiplied(); - Self::new_premultiplied(Eotf::Gamma22, r, g, b, a) + Self::new( + Eotf::Gamma22, + AlphaMode::PremultipliedElectrical, + r, + g, + b, + a, + ) } }