1
0
Fork 0
forked from wry/wry

wl_surface: handle alpha modes

This commit is contained in:
Julian Orth 2026-02-21 14:24:38 +01:00
parent 37674a229c
commit 69ca5d92e7
10 changed files with 82 additions and 25 deletions

View file

@ -9,8 +9,8 @@ use {
}, },
cmm::cmm_description::ColorDescription, cmm::cmm_description::ColorDescription,
gfx_api::{ gfx_api::{
AcquireSync, BufferResv, GfxApiOpt, GfxRenderPass, GfxTexture, ReleaseSync, SyncFile, AcquireSync, AlphaMode, BufferResv, GfxApiOpt, GfxRenderPass, GfxTexture, ReleaseSync,
create_render_pass, SyncFile, create_render_pass,
}, },
ifs::wl_output::BlendSpace, ifs::wl_output::BlendSpace,
rect::Region, rect::Region,
@ -668,6 +668,10 @@ impl MetalConnector {
} }
return None; return None;
}; };
if ct.alpha_mode != AlphaMode::PremultipliedElectrical {
// Direct scanout requires premultiplied electrical alpha.
return None;
}
if !ct.cd.embeds_into(cd) { if !ct.cd.embeds_into(cd) {
// Direct scanout requires embeddable color descriptions. // Direct scanout requires embeddable color descriptions.
return None; return None;

View file

@ -3,7 +3,7 @@ use {
async_engine::AsyncEngine, async_engine::AsyncEngine,
fixed::Fixed, fixed::Fixed,
format::ARGB8888, format::ARGB8888,
gfx_api::{AcquireSync, GfxContext, GfxError, GfxTexture, ReleaseSync}, gfx_api::{AcquireSync, AlphaMode, GfxContext, GfxError, GfxTexture, ReleaseSync},
rect::Rect, rect::Rect,
renderer::Renderer, renderer::Renderer,
scale::Scale, scale::Scale,
@ -398,6 +398,7 @@ fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed
ReleaseSync::None, ReleaseSync::None,
false, false,
renderer.state.color_manager.srgb_gamma22(), renderer.state.color_manager.srgb_gamma22(),
AlphaMode::PremultipliedElectrical,
); );
} }
} }
@ -423,6 +424,7 @@ impl Cursor for StaticCursor {
ReleaseSync::None, ReleaseSync::None,
false, false,
renderer.state.color_manager.srgb_gamma22(), renderer.state.color_manager.srgb_gamma22(),
AlphaMode::PremultipliedElectrical,
); );
} }
} }
@ -466,6 +468,7 @@ impl Cursor for AnimatedCursor {
ReleaseSync::None, ReleaseSync::None,
false, false,
renderer.state.color_manager.srgb_gamma22(), renderer.state.color_manager.srgb_gamma22(),
AlphaMode::PremultipliedElectrical,
); );
} }
} }

View file

@ -219,6 +219,7 @@ pub struct CopyTexture {
pub alpha: Option<f32>, pub alpha: Option<f32>,
pub opaque: bool, pub opaque: bool,
pub cd: Rc<ColorDescription>, pub cd: Rc<ColorDescription>,
pub alpha_mode: AlphaMode,
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -443,6 +444,7 @@ impl dyn GfxFramebuffer {
release_sync, release_sync,
false, false,
texture_cd, texture_cd,
AlphaMode::PremultipliedElectrical,
); );
let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT); let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT);
self.render( self.render(
@ -825,6 +827,10 @@ pub trait GfxContext: Debug {
false false
} }
fn supports_alpha_modes(&self) -> bool {
false
}
fn supports_invalid_modifier(&self) -> bool { fn supports_invalid_modifier(&self) -> bool {
false false
} }

View file

@ -30,7 +30,7 @@ use {
drm_feedback::DrmFeedback, drm_feedback::DrmFeedback,
fixed::Fixed, fixed::Fixed,
gfx_api::{ gfx_api::{
AsyncShmGfxTexture, BufferResv, BufferResvUser, GfxError, GfxStagingBuffer, AlphaMode, AsyncShmGfxTexture, BufferResv, BufferResvUser, GfxError, GfxStagingBuffer,
ReleaseSync, SampleRect, SyncFile, ReleaseSync, SampleRect, SyncFile,
}, },
ifs::{ ifs::{
@ -353,6 +353,7 @@ pub struct WlSurface {
CopyHashMap<WpColorManagementSurfaceFeedbackV1Id, Rc<WpColorManagementSurfaceFeedbackV1>>, CopyHashMap<WpColorManagementSurfaceFeedbackV1Id, Rc<WpColorManagementSurfaceFeedbackV1>>,
color_description: CloneCell<Option<Rc<ColorDescription>>>, color_description: CloneCell<Option<Rc<ColorDescription>>>,
color_representation_surface: CloneCell<Option<Rc<WpColorRepresentationSurfaceV1>>>, color_representation_surface: CloneCell<Option<Rc<WpColorRepresentationSurfaceV1>>>,
alpha_mode: Cell<AlphaMode>,
} }
impl Debug for WlSurface { impl Debug for WlSurface {
@ -490,6 +491,7 @@ struct PendingState {
tray_item_ack_serial: Option<u32>, tray_item_ack_serial: Option<u32>,
color_description: Option<Option<Rc<ColorDescription>>>, color_description: Option<Option<Rc<ColorDescription>>>,
serial: Option<u64>, serial: Option<u64>,
alpha_mode: Option<AlphaMode>,
} }
struct AttachedSubsurfaceState { struct AttachedSubsurfaceState {
@ -544,6 +546,7 @@ impl PendingState {
opt!(tray_item_ack_serial); opt!(tray_item_ack_serial);
opt!(color_description); opt!(color_description);
opt!(serial); opt!(serial);
opt!(alpha_mode);
{ {
let (dx1, dy1) = self.offset; let (dx1, dy1) = self.offset;
let (dx2, dy2) = mem::take(&mut next.offset); let (dx2, dy2) = mem::take(&mut next.offset);
@ -708,6 +711,7 @@ impl WlSurface {
color_management_feedback: Default::default(), color_management_feedback: Default::default(),
color_description: Default::default(), color_description: Default::default(),
color_representation_surface: Default::default(), color_representation_surface: Default::default(),
alpha_mode: Default::default(),
} }
} }
@ -1202,6 +1206,12 @@ impl WlSurface {
color_description_changed = true; color_description_changed = true;
self.color_description.set(desc); 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; let mut alpha_changed = false;
if let Some(alpha) = pending.alpha_multiplier.take() { if let Some(alpha) = pending.alpha_multiplier.take() {
alpha_changed = true; alpha_changed = true;
@ -1213,7 +1223,8 @@ impl WlSurface {
|| buffer_transform_changed || buffer_transform_changed
|| viewport_changed || viewport_changed
|| alpha_changed || alpha_changed
|| color_description_changed; || color_description_changed
|| alpha_mode_changed;
let mut buffer_changed = false; let mut buffer_changed = false;
let mut old_raw_size = None; let mut old_raw_size = None;
let (mut dx, mut dy) = mem::take(&mut pending.offset); let (mut dx, mut dy) = mem::take(&mut pending.offset);
@ -1725,6 +1736,10 @@ impl WlSurface {
self.alpha.get() self.alpha.get()
} }
pub fn alpha_mode(&self) -> AlphaMode {
self.alpha_mode.get()
}
pub fn opaque(&self) -> bool { pub fn opaque(&self) -> bool {
self.is_opaque.get() self.is_opaque.get()
} }

View file

@ -1,6 +1,7 @@
use { use {
crate::{ crate::{
client::{Client, ClientError}, client::{Client, ClientError},
gfx_api::AlphaMode,
ifs::wl_surface::WlSurface, ifs::wl_surface::WlSurface,
leaks::Tracker, leaks::Tracker,
object::{Object, Version}, object::{Object, Version},
@ -22,12 +23,11 @@ pub struct WpColorRepresentationSurfaceV1 {
pub version: Version, pub version: Version,
pub tracker: Tracker<Self>, pub tracker: Tracker<Self>,
pub surface: Rc<WlSurface>, pub surface: Rc<WlSurface>,
pub supports_alpha_modes: bool,
} }
pub const AM_PREMULTIPLIED_ELECTRICAL: u32 = 0; pub const AM_PREMULTIPLIED_ELECTRICAL: u32 = 0;
#[expect(dead_code)]
pub const AM_PREMULTIPLIED_OPTICAL: u32 = 1; pub const AM_PREMULTIPLIED_OPTICAL: u32 = 1;
#[expect(dead_code)]
pub const AM_STRAIGHT: u32 = 2; pub const AM_STRAIGHT: u32 = 2;
impl WpColorRepresentationSurfaceV1 { impl WpColorRepresentationSurfaceV1 {
@ -47,16 +47,22 @@ impl WpColorRepresentationSurfaceV1RequestHandler for WpColorRepresentationSurfa
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> { fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.surface.color_representation_surface.take(); self.surface.color_representation_surface.take();
self.surface.pending.borrow_mut().alpha_mode = Some(Default::default());
self.client.remove_obj(self)?; self.client.remove_obj(self)?;
Ok(()) Ok(())
} }
fn set_alpha_mode(&self, req: SetAlphaMode, _slf: &Rc<Self>) -> Result<(), Self::Error> { fn set_alpha_mode(&self, req: SetAlphaMode, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if req.alpha_mode != AM_PREMULTIPLIED_ELECTRICAL { let sam = self.supports_alpha_modes;
return Err(WpColorRepresentationSurfaceV1Error::UnsupportedAlphaMode( let alpha_mode = match req.alpha_mode {
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(()) Ok(())
} }

View file

@ -3,8 +3,8 @@ use {
client::{Client, ClientError}, client::{Client, ClientError},
globals::{Global, GlobalName}, globals::{Global, GlobalName},
ifs::wl_surface::wp_color_representation_surface_v1::{ ifs::wl_surface::wp_color_representation_surface_v1::{
AM_PREMULTIPLIED_ELECTRICAL, WpColorRepresentationSurfaceV1, AM_PREMULTIPLIED_ELECTRICAL, AM_PREMULTIPLIED_OPTICAL, AM_STRAIGHT,
WpColorRepresentationSurfaceV1Error, WpColorRepresentationSurfaceV1, WpColorRepresentationSurfaceV1Error,
}, },
leaks::Tracker, leaks::Tracker,
object::{Object, Version}, object::{Object, Version},
@ -35,11 +35,18 @@ impl WpColorRepresentationManagerV1Global {
client: &Rc<Client>, client: &Rc<Client>,
version: Version, version: Version,
) -> Result<(), WpColorRepresentationManagerV1Error> { ) -> 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 { let obj = Rc::new(WpColorRepresentationManagerV1 {
id, id,
client: client.clone(), client: client.clone(),
tracker: Default::default(), tracker: Default::default(),
version, version,
supports_alpha_modes,
}); });
track!(client, obj); track!(client, obj);
client.add_client_obj(&obj)?; client.add_client_obj(&obj)?;
@ -53,11 +60,16 @@ pub struct WpColorRepresentationManagerV1 {
pub client: Rc<Client>, pub client: Rc<Client>,
pub version: Version, pub version: Version,
pub tracker: Tracker<Self>, pub tracker: Tracker<Self>,
pub supports_alpha_modes: bool,
} }
impl WpColorRepresentationManagerV1 { impl WpColorRepresentationManagerV1 {
fn send_capabilities(&self) { fn send_capabilities(&self) {
self.send_supported_alpha_mode(AM_PREMULTIPLIED_ELECTRICAL); 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(); self.send_done();
} }
@ -89,6 +101,7 @@ impl WpColorRepresentationManagerV1RequestHandler for WpColorRepresentationManag
version: self.version, version: self.version,
tracker: Default::default(), tracker: Default::default(),
surface: surface.clone(), surface: surface.clone(),
supports_alpha_modes: self.supports_alpha_modes,
}); });
track!(self.client, obj); track!(self.client, obj);
self.client.add_client_obj(&obj)?; self.client.add_client_obj(&obj)?;

View file

@ -7,7 +7,8 @@ use {
fixed::Fixed, fixed::Fixed,
format::ARGB8888, format::ARGB8888,
gfx_api::{ 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, ifs::zwlr_layer_shell_v1::OVERLAY,
portal::{ portal::{
@ -230,6 +231,7 @@ impl GuiElement for Button {
ReleaseSync::None, ReleaseSync::None,
false, false,
srgb_srgb, srgb_srgb,
AlphaMode::PremultipliedElectrical,
); );
} }
} }
@ -332,6 +334,7 @@ impl GuiElement for Label {
ReleaseSync::None, ReleaseSync::None,
false, false,
color_manager.srgb_gamma22(), color_manager.srgb_gamma22(),
AlphaMode::PremultipliedElectrical,
); );
} }
} }

View file

@ -145,6 +145,7 @@ impl Renderer<'_> {
ReleaseSync::None, ReleaseSync::None,
false, false,
self.state.color_manager.srgb_gamma22(), self.state.color_manager.srgb_gamma22(),
AlphaMode::PremultipliedElectrical,
); );
} }
x += bar_rect.x1() - non_exclusive_rect_rel.x1(); x += bar_rect.x1() - non_exclusive_rect_rel.x1();
@ -167,6 +168,7 @@ impl Renderer<'_> {
ReleaseSync::None, ReleaseSync::None,
false, false,
srgb_srgb, srgb_srgb,
AlphaMode::PremultipliedElectrical,
); );
} }
for item in output.tray_items.iter() { for item in output.tray_items.iter() {
@ -253,6 +255,7 @@ impl Renderer<'_> {
ReleaseSync::None, ReleaseSync::None,
false, false,
self.state.color_manager.srgb_gamma22(), self.state.color_manager.srgb_gamma22(),
AlphaMode::PremultipliedElectrical,
); );
} }
self.render_tl_aux(placeholder.tl_data(), bounds, true); self.render_tl_aux(placeholder.tl_data(), bounds, true);
@ -304,6 +307,7 @@ impl Renderer<'_> {
ReleaseSync::None, ReleaseSync::None,
false, false,
srgb_srgb, srgb_srgb,
AlphaMode::PremultipliedElectrical,
); );
} }
} }
@ -466,6 +470,7 @@ impl Renderer<'_> {
) { ) {
let alpha = surface.alpha(); let alpha = surface.alpha();
let cd = surface.color_description(); let cd = surface.color_description();
let alpha_mode = surface.alpha_mode();
if let Some(tex) = buffer.buffer.get_texture(surface) { if let Some(tex) = buffer.buffer.get_texture(surface) {
let mut opaque = surface.opaque(); let mut opaque = surface.opaque();
if !opaque && tex.format().has_alpha { if !opaque && tex.format().has_alpha {
@ -485,6 +490,7 @@ impl Renderer<'_> {
buffer.release_sync, buffer.release_sync,
opaque, opaque,
&cd, &cd,
alpha_mode,
); );
} else if let Some(color) = &buffer.buffer.color { } else if let Some(color) = &buffer.buffer.color {
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) { if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
@ -494,12 +500,7 @@ impl Renderer<'_> {
}; };
if !rect.is_empty() { if !rect.is_empty() {
let color = Color::from_u32( let color = Color::from_u32(
cd.eotf, cd.eotf, alpha_mode, color[0], color[1], color[2], color[3],
AlphaMode::PremultipliedElectrical,
color[0],
color[1],
color[2],
color[3],
); );
self.base.sync(); self.base.sync();
self.base self.base
@ -586,6 +587,7 @@ impl Renderer<'_> {
ReleaseSync::None, ReleaseSync::None,
false, false,
srgb_srgb, srgb_srgb,
AlphaMode::PremultipliedElectrical,
); );
} }
x1 += th; x1 += th;
@ -608,6 +610,7 @@ impl Renderer<'_> {
ReleaseSync::None, ReleaseSync::None,
false, false,
srgb_srgb, srgb_srgb,
AlphaMode::PremultipliedElectrical,
); );
} }
let body = Rect::new_sized_saturating( let body = Rect::new_sized_saturating(

View file

@ -2,8 +2,8 @@ use {
crate::{ crate::{
cmm::cmm_description::{ColorDescription, LinearColorDescription}, cmm::cmm_description::{ColorDescription, LinearColorDescription},
gfx_api::{ gfx_api::{
AcquireSync, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture, AcquireSync, AlphaMode, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt,
ReleaseSync, SampleRect, GfxTexture, ReleaseSync, SampleRect,
}, },
rect::Rect, rect::Rect,
scale::Scale, scale::Scale,
@ -181,6 +181,7 @@ impl RendererBase<'_> {
release_sync: ReleaseSync, release_sync: ReleaseSync,
opaque: bool, opaque: bool,
cd: &Rc<ColorDescription>, cd: &Rc<ColorDescription>,
alpha_mode: AlphaMode,
) { ) {
// log::info!("rendering texture {:?}", std::ptr::from_ref(&**texture) as *const u8); // log::info!("rendering texture {:?}", std::ptr::from_ref(&**texture) as *const u8);
// log::info!("{:?}", backtrace::Backtrace::new()); // log::info!("{:?}", backtrace::Backtrace::new());
@ -228,6 +229,7 @@ impl RendererBase<'_> {
release_sync, release_sync,
opaque, opaque,
cd: cd.clone(), cd: cd.clone(),
alpha_mode,
})); }));
} }

View file

@ -31,8 +31,9 @@ use {
forker::ForkerProxy, forker::ForkerProxy,
format::Format, format::Format,
gfx_api::{ gfx_api::{
AcquireSync, BufferResv, GfxBlendBuffer, GfxContext, GfxError, GfxFramebuffer, AcquireSync, AlphaMode, BufferResv, GfxBlendBuffer, GfxContext, GfxError,
GfxTexture, PendingShmTransfer, ReleaseSync, STAGING_DOWNLOAD, SampleRect, SyncFile, GfxFramebuffer, GfxTexture, PendingShmTransfer, ReleaseSync, STAGING_DOWNLOAD,
SampleRect, SyncFile,
}, },
gfx_apis::create_gfx_context, gfx_apis::create_gfx_context,
globals::{Globals, GlobalsError, RemovableWaylandGlobal, WaylandGlobal}, globals::{Globals, GlobalsError, RemovableWaylandGlobal, WaylandGlobal},
@ -1272,6 +1273,7 @@ impl State {
release_sync, release_sync,
false, false,
src_cd, src_cd,
AlphaMode::PremultipliedElectrical,
); );
if render_hardware_cursors if render_hardware_cursors
&& let Some(cursor_user_group) = self.cursor_user_group_hardware_cursor.get() && let Some(cursor_user_group) = self.cursor_user_group_hardware_cursor.get()