compositor: double buffered states needs their own bit (#15645)

6484f43 introduced state merging, which showed a flaw with mako. a
second commit with just changing geometry was handled as a "empty"
commit and ignored. all protocols that listen to commit and is double
buffered as in state applied on next commit needs their own updated.bits.flag
to ensure the statequeue knows something is pending.

synced subsurface also needs to set the parent flag, because its waiting
for it to commit.
This commit is contained in:
Tom Englund
2026-07-29 16:20:07 +02:00
committed by GitHub
parent b3aa455ceb
commit 7803e68638
11 changed files with 64 additions and 8 deletions
+7
View File
@@ -28,6 +28,7 @@ void CAlphaModifier::setResource(UP<CWpAlphaModifierSurfaceV1>&& resource) {
}
m_alpha = alpha / sc<float>(UINT32_MAX);
markPending();
});
m_listeners.surfaceCommitted = m_surface->m_events.commit.listen([this] {
@@ -54,11 +55,17 @@ void CAlphaModifier::setResource(UP<CWpAlphaModifierSurfaceV1>&& resource) {
void CAlphaModifier::destroy() {
m_resource.reset();
m_alpha = 1.F;
markPending();
if (!m_surface)
PROTO::alphaModifier->destroyAlphaModifier(this);
}
void CAlphaModifier::markPending() {
if (m_surface)
m_surface->m_pending.updated.bits.alphaModifier = true;
}
CAlphaModifierProtocol::CAlphaModifierProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
+1
View File
@@ -22,6 +22,7 @@ class CAlphaModifier {
float m_alpha = 1.0;
void destroy();
void markPending();
struct {
CHyprSignalListener surfaceCommitted;
+8
View File
@@ -31,10 +31,12 @@ void CBackgroundEffect::setResource(SP<CExtBackgroundEffectSurfaceV1> resource)
if (!region) {
m_blurRegion.clear();
markPending();
return;
}
m_blurRegion = CWLRegionResource::fromResource(region)->m_region;
markPending();
});
m_listeners.surfaceCommitted = m_surface->m_events.commit.listen([this] {
@@ -68,9 +70,15 @@ void CBackgroundEffect::setResource(SP<CExtBackgroundEffectSurfaceV1> resource)
});
}
void CBackgroundEffect::markPending() {
if (m_surface)
m_surface->m_pending.updated.bits.backgroundEffect = true;
}
void CBackgroundEffect::destroy() {
m_resource.reset();
m_blurRegion.clear();
markPending();
// The spec requires effect removal to be double-buffered: state is cleared on next wl_surface commit.
// If the surface is already destroyed, clean up immediately.
if (!m_surface)
+1
View File
@@ -23,6 +23,7 @@ class CBackgroundEffect {
CRegion m_blurRegion;
void destroy();
void markPending();
struct {
CHyprSignalListener surfaceCommitted;
+9
View File
@@ -36,6 +36,7 @@ void CHyprlandSurface::setResource(SP<CHyprlandSurfaceV1> resource) {
}
m_opacity = fOpacity;
markPending();
});
m_resource->setSetVisibleRegion([this](CHyprlandSurfaceV1* resource, wl_resource* region) {
@@ -44,11 +45,13 @@ void CHyprlandSurface::setResource(SP<CHyprlandSurfaceV1> resource) {
m_visibleRegionChanged = true;
m_visibleRegion.clear();
markPending();
return;
}
m_visibleRegionChanged = true;
m_visibleRegion = CWLRegionResource::fromResource(region)->m_region;
markPending();
});
m_listeners.surfaceCommitted = m_surface->m_events.commit.listen([this] {
@@ -81,11 +84,17 @@ void CHyprlandSurface::destroy() {
m_visibleRegionChanged = true;
m_visibleRegion.clear();
markPending();
if (!m_surface)
PROTO::hyprlandSurface->destroySurface(this);
}
void CHyprlandSurface::markPending() {
if (m_surface)
m_surface->m_pending.updated.bits.hyprlandSurface = true;
}
CHyprlandSurfaceProtocol::CHyprlandSurfaceProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
+1
View File
@@ -25,6 +25,7 @@ class CHyprlandSurface {
CRegion m_visibleRegion;
void destroy();
void markPending();
struct {
CHyprSignalListener surfaceCommitted;
+13 -7
View File
@@ -83,7 +83,7 @@ CLayerShellResource::CLayerShellResource(SP<CZwlrLayerSurfaceV1> resource_, SP<C
});
m_resource->setSetSize([this](CZwlrLayerSurfaceV1* r, uint32_t x, uint32_t y) {
m_pending.committed |= STATE_SIZE;
markPending(STATE_SIZE);
m_pending.desiredSize = {sc<int>(x), sc<int>(y)};
});
@@ -93,17 +93,17 @@ CLayerShellResource::CLayerShellResource(SP<CZwlrLayerSurfaceV1> resource_, SP<C
return;
}
m_pending.committed |= STATE_ANCHOR;
markPending(STATE_ANCHOR);
m_pending.anchor = anchor;
});
m_resource->setSetExclusiveZone([this](CZwlrLayerSurfaceV1* r, int32_t zone) {
m_pending.committed |= STATE_EXCLUSIVE;
markPending(STATE_EXCLUSIVE);
m_pending.exclusive = zone;
});
m_resource->setSetMargin([this](CZwlrLayerSurfaceV1* r, int32_t top, int32_t right, int32_t bottom, int32_t left) {
m_pending.committed |= STATE_MARGIN;
markPending(STATE_MARGIN);
m_pending.margin = {left, right, top, bottom};
});
@@ -113,7 +113,7 @@ CLayerShellResource::CLayerShellResource(SP<CZwlrLayerSurfaceV1> resource_, SP<C
return;
}
m_pending.committed |= STATE_INTERACTIVITY;
markPending(STATE_INTERACTIVITY);
m_pending.interactivity = kbi;
});
@@ -149,7 +149,7 @@ CLayerShellResource::CLayerShellResource(SP<CZwlrLayerSurfaceV1> resource_, SP<C
return;
}
m_pending.committed |= STATE_LAYER;
markPending(STATE_LAYER);
m_pending.layer = sc<zwlrLayerShellV1Layer>(layer);
});
@@ -164,7 +164,7 @@ CLayerShellResource::CLayerShellResource(SP<CZwlrLayerSurfaceV1> resource_, SP<C
return;
}
m_pending.committed |= STATE_EDGE;
markPending(STATE_EDGE);
m_pending.exclusiveEdge = anchor;
});
}
@@ -179,6 +179,12 @@ bool CLayerShellResource::good() {
return m_resource->resource();
}
void CLayerShellResource::markPending(eCommittedState state) {
m_pending.committed |= state;
if (m_surface)
m_surface->m_pending.updated.bits.layershell = true;
}
void CLayerShellResource::sendClosed() {
if (m_closed)
return;
+1
View File
@@ -76,6 +76,7 @@ class CLayerShellResource {
private:
SP<CZwlrLayerSurfaceV1> m_resource;
void markPending(eCommittedState state);
struct {
CHyprSignalListener commitSurface;
+6
View File
@@ -238,11 +238,15 @@ CXDGToplevelResource::CXDGToplevelResource(SP<CXdgToplevel> resource_, SP<CXDGSu
m_resource->setSetMaxSize([this](CXdgToplevel* r, int32_t x, int32_t y) {
m_pending.maxSize = {x, y};
if (m_owner && m_owner->m_surface)
m_owner->m_surface->m_pending.updated.bits.xdgshell = true;
m_events.sizeLimitsChanged.emit();
});
m_resource->setSetMinSize([this](CXdgToplevel* r, int32_t x, int32_t y) {
m_pending.minSize = {x, y};
if (m_owner && m_owner->m_surface)
m_owner->m_surface->m_pending.updated.bits.xdgshell = true;
m_events.sizeLimitsChanged.emit();
});
@@ -608,6 +612,8 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBas
m_resource->setSetWindowGeometry([this](CXdgSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) {
LOGM(Log::DEBUG, "xdg_surface {:x} requests geometry {}x{} {}x{}", (uintptr_t)this, x, y, w, h);
m_pending.geometry = {x, y, w, h};
if (m_surface)
m_surface->m_pending.updated.bits.xdgshell = true;
});
}
+10
View File
@@ -166,6 +166,16 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : m_resource(re
return;
}
// a synced subsurface caches its state until the parent commits, so make sure that
// commit isnt dropped as empty. mark the t1 parent.
if (m_role->role() == SURFACE_ROLE_SUBSURFACE) {
const auto SUB = sc<CSubsurfaceRole*>(m_role.get())->m_subsurface.lock();
if (SUB && SUB->m_sync && SUB->m_parent) {
if (const auto PARENT = SUB->t1Parent())
PARENT->m_pending.updated.bits.subsurface = true;
}
}
// null buffer attached
if (!m_pending.buffer && m_pending.updated.bits.buffer) {
commitState(m_pending);
+7 -1
View File
@@ -41,7 +41,7 @@ inline eLockReason operator~(eLockReason a) {
struct SSurfaceState {
union {
uint16_t all = 0;
uint32_t all = 0;
struct {
bool buffer : 1;
bool damage : 1;
@@ -56,6 +56,12 @@ struct SSurfaceState {
bool frame : 1;
bool fifo : 1;
bool presentation : 1;
bool xdgshell : 1;
bool layershell : 1;
bool subsurface : 1;
bool alphaModifier : 1;
bool hyprlandSurface : 1;
bool backgroundEffect : 1;
} bits;
} updated;