From 6384f4acf4e2988bcb35d3dfc5821f07867a34d0 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Sat, 22 Mar 2025 11:13:44 -0500 Subject: [PATCH] core/compositor: Correctly track SHM buffer damage (#9678) --- src/desktop/WLSurface.cpp | 2 +- src/events/Windows.cpp | 2 +- src/helpers/Monitor.cpp | 2 +- src/protocols/DRMSyncobj.cpp | 2 +- src/protocols/Viewporter.cpp | 2 +- src/protocols/XDGShell.cpp | 2 +- src/protocols/core/Compositor.cpp | 53 +++++++------------------ src/protocols/core/Compositor.hpp | 3 +- src/protocols/core/Shm.cpp | 11 +----- src/protocols/core/Shm.hpp | 2 - src/protocols/types/SurfaceState.cpp | 58 ++++++++++++++++++++++++++++ src/protocols/types/SurfaceState.hpp | 18 ++++----- src/render/Texture.cpp | 5 ++- src/render/Texture.hpp | 17 ++++---- src/xwayland/XSurface.cpp | 6 +-- 15 files changed, 103 insertions(+), 82 deletions(-) create mode 100644 src/protocols/types/SurfaceState.cpp diff --git a/src/desktop/WLSurface.cpp b/src/desktop/WLSurface.cpp index e86ac036f..11da7b680 100644 --- a/src/desktop/WLSurface.cpp +++ b/src/desktop/WLSurface.cpp @@ -98,7 +98,7 @@ CRegion CWLSurface::computeDamage() const { if (!m_pResource->current.texture) return {}; - CRegion damage = m_pResource->accumulateCurrentBufferDamage(); + CRegion damage = m_pResource->current.accumulateBufferDamage(); damage.transform(wlTransformToHyprutils(m_pResource->current.transform), m_pResource->current.bufferSize.x, m_pResource->current.bufferSize.y); const auto BUFSIZE = m_pResource->current.bufferSize; diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index b5273187d..adc0c7623 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -880,7 +880,7 @@ void Events::listener_commitWindow(void* owner, void* data) { // tearing: if solitary, redraw it. This still might be a single surface window if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.texture) { - CRegion damageBox{PWINDOW->m_pWLSurface->resource()->accumulateCurrentBufferDamage()}; + CRegion damageBox{PWINDOW->m_pWLSurface->resource()->current.accumulateBufferDamage()}; if (!damageBox.empty()) { if (PMONITOR->tearingState.busy) { diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index fe8ab9df8..75c0d8b98 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -1400,7 +1400,7 @@ bool CMonitor::attemptDirectScanout() { clock_gettime(CLOCK_MONOTONIC, &now); PSURFACE->presentFeedback(&now, self.lock()); - output->state->addDamage(PSURFACE->accumulateCurrentBufferDamage()); + output->state->addDamage(PSURFACE->current.accumulateBufferDamage()); output->state->resetExplicitFences(); auto cleanup = CScopeGuard([this]() { output->state->resetExplicitFences(); }); diff --git a/src/protocols/DRMSyncobj.cpp b/src/protocols/DRMSyncobj.cpp index 943aad4dd..5238d1fb1 100644 --- a/src/protocols/DRMSyncobj.cpp +++ b/src/protocols/DRMSyncobj.cpp @@ -137,7 +137,7 @@ CDRMSyncobjSurfaceResource::~CDRMSyncobjSurfaceResource() { } bool CDRMSyncobjSurfaceResource::protocolError() { - if (!surface->pending.texture) { + if (!surface->pending.buffer) { resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer"); surface->pending.rejected = true; return true; diff --git a/src/protocols/Viewporter.cpp b/src/protocols/Viewporter.cpp index 4964bead5..d14e7ee58 100644 --- a/src/protocols/Viewporter.cpp +++ b/src/protocols/Viewporter.cpp @@ -52,7 +52,7 @@ CViewportResource::CViewportResource(SP resource_, SPevents.precommit.registerListener([this](std::any d) { - if (!surface || !surface->pending.texture) + if (!surface || !surface->pending.buffer) return; if (surface->pending.viewport.hasSource) { diff --git a/src/protocols/XDGShell.cpp b/src/protocols/XDGShell.cpp index 9424a9d61..db274e3cc 100644 --- a/src/protocols/XDGShell.cpp +++ b/src/protocols/XDGShell.cpp @@ -387,7 +387,7 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP resource_, SPcurrent = toplevel->pending; - if UNLIKELY (initialCommit && surface->pending.texture) { + if UNLIKELY (initialCommit && surface->pending.buffer) { resource->error(-1, "Buffer attached before initial commit"); return; } diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index 6b47b5039..5462fbe94 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -12,6 +12,7 @@ #include "../DRMSyncobj.hpp" #include "../../render/Renderer.hpp" #include "config/ConfigValue.hpp" +#include "render/Texture.hpp" #include class CDefaultSurfaceRole : public ISurfaceRole { @@ -91,10 +92,10 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : resource(reso }); resource->setCommit([this](CWlSurface* r) { - if (pending.texture) + if (pending.buffer) pending.bufferDamage.intersect(CBox{{}, pending.bufferSize}); - if (!pending.texture) + if (!pending.buffer) pending.size = {}; else if (pending.viewport.hasDestination) pending.size = pending.viewport.destination; @@ -399,36 +400,8 @@ CBox CWLSurfaceResource::extends() { return full.getExtents(); } -Vector2D CWLSurfaceResource::sourceSize() { - if UNLIKELY (!current.texture) - return {}; - - if UNLIKELY (current.viewport.hasSource) - return current.viewport.source.size(); - - Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.bufferSize.y, current.bufferSize.x} : current.bufferSize; - return trc / current.scale; -} - -CRegion CWLSurfaceResource::accumulateCurrentBufferDamage() { - if UNLIKELY (!current.texture) - return {}; - - CRegion surfaceDamage = current.damage; - if (current.viewport.hasDestination) { - Vector2D scale = sourceSize() / current.viewport.destination; - surfaceDamage.scale(scale); - } - - if (current.viewport.hasSource) - surfaceDamage.translate(current.viewport.source.pos()); - - Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.bufferSize.y, current.bufferSize.x} : current.bufferSize; - - return surfaceDamage.scale(current.scale).transform(wlTransformToHyprutils(invertTransform(current.transform)), trc.x, trc.y).add(current.bufferDamage); -} - void CWLSurfaceResource::commitPendingState(SSurfaceState& state) { + auto lastTexture = current.texture; if (state.newBuffer) { state.newBuffer = false; current = state; @@ -437,20 +410,19 @@ void CWLSurfaceResource::commitPendingState(SSurfaceState& state) { state.buffer.reset(); } - if (current.texture) - current.texture->m_eTransform = wlTransformToHyprutils(current.transform); - if (current.buffer) { - const auto DAMAGE = accumulateCurrentBufferDamage(); - current.buffer->buffer->update(DAMAGE); + if (current.buffer->buffer->isSynchronous()) + current.updateSynchronousTexture(lastTexture); // if the surface is a cursor, update the shm buffer // TODO: don't update the entire texture - if (role->role() == SURFACE_ROLE_CURSOR && !DAMAGE.empty()) - updateCursorShm(DAMAGE); + if (role->role() == SURFACE_ROLE_CURSOR) + updateCursorShm(current.accumulateBufferDamage()); } - // TODO: we should _accumulate_ and not replace above if sync + if (current.texture) + current.texture->m_eTransform = wlTransformToHyprutils(current.transform); + if (role->role() == SURFACE_ROLE_SUBSURFACE) { auto subsurface = ((CSubsurfaceRole*)role.get())->subsurface.lock(); if (subsurface->sync) @@ -478,6 +450,9 @@ void CWLSurfaceResource::commitPendingState(SSurfaceState& state) { } void CWLSurfaceResource::updateCursorShm(CRegion damage) { + if (damage.empty()) + return; + auto buf = current.buffer ? current.buffer->buffer : SP{}; if UNLIKELY (!buf) diff --git a/src/protocols/core/Compositor.hpp b/src/protocols/core/Compositor.hpp index f958528c5..5d2240a70 100644 --- a/src/protocols/core/Compositor.hpp +++ b/src/protocols/core/Compositor.hpp @@ -11,6 +11,7 @@ #include #include #include "../WaylandProtocol.hpp" +#include "render/Texture.hpp" #include "wayland.hpp" #include "../../helpers/signal/Signal.hpp" #include "../../helpers/math/Math.hpp" @@ -75,7 +76,6 @@ class CWLSurfaceResource { SP getResource(); CBox extends(); void resetRole(); - Vector2D sourceSize(); struct { CSignal precommit; // before commit @@ -102,7 +102,6 @@ class CWLSurfaceResource { void breadthfirst(std::function, const Vector2D&, void*)> fn, void* data); SP findFirstPreorder(std::function)> fn); - CRegion accumulateCurrentBufferDamage(); void presentFeedback(timespec* when, PHLMONITOR pMonitor, bool discarded = false); void commitPendingState(SSurfaceState& state); diff --git a/src/protocols/core/Shm.cpp b/src/protocols/core/Shm.cpp index 37a48fd31..420b4a3f3 100644 --- a/src/protocols/core/Shm.cpp +++ b/src/protocols/core/Shm.cpp @@ -22,19 +22,12 @@ CWLSHMBuffer::CWLSHMBuffer(SP pool_, uint32_t id, int32_t of offset = offset_; opaque = NFormatUtils::isFormatOpaque(NFormatUtils::shmToDRM(fmt_)); - texture = makeShared(NFormatUtils::shmToDRM(fmt), (uint8_t*)pool->data + offset, stride, size_); - resource = CWLBufferResource::create(makeShared(pool_->resource->client(), 1, id)); listeners.bufferResourceDestroy = events.destroy.registerListener([this](std::any d) { listeners.bufferResourceDestroy.reset(); PROTO::shm->destroyResource(this); }); - - success = texture->m_iTexID; - - if UNLIKELY (!success) - Debug::log(ERR, "Failed creating a shm texture: null texture id"); } CWLSHMBuffer::~CWLSHMBuffer() { @@ -74,11 +67,11 @@ void CWLSHMBuffer::endDataPtr() { } bool CWLSHMBuffer::good() { - return success; + return true; } void CWLSHMBuffer::update(const CRegion& damage) { - texture->update(NFormatUtils::shmToDRM(fmt), (uint8_t*)pool->data + offset, stride, damage); + ; } CSHMPool::CSHMPool(CFileDescriptor fd_, size_t size_) : fd(std::move(fd_)), size(size_), data(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0)) { diff --git a/src/protocols/core/Shm.hpp b/src/protocols/core/Shm.hpp index fa5f29a40..61b26f126 100644 --- a/src/protocols/core/Shm.hpp +++ b/src/protocols/core/Shm.hpp @@ -50,8 +50,6 @@ class CWLSHMBuffer : public IHLBuffer { SP pool; private: - bool success = false; - struct { CHyprSignalListener bufferResourceDestroy; } listeners; diff --git a/src/protocols/types/SurfaceState.cpp b/src/protocols/types/SurfaceState.cpp new file mode 100644 index 000000000..6f9636ff3 --- /dev/null +++ b/src/protocols/types/SurfaceState.cpp @@ -0,0 +1,58 @@ +#include "SurfaceState.hpp" +#include "helpers/Format.hpp" +#include "protocols/types/Buffer.hpp" +#include "render/Texture.hpp" + +Vector2D SSurfaceState::sourceSize() { + if UNLIKELY (!texture) + return {}; + + if UNLIKELY (viewport.hasSource) + return viewport.source.size(); + + Vector2D trc = transform % 2 == 1 ? Vector2D{bufferSize.y, bufferSize.x} : bufferSize; + return trc / scale; +} + +CRegion SSurfaceState::accumulateBufferDamage() { + if (damage.empty()) + return bufferDamage; + + CRegion surfaceDamage = damage; + if (viewport.hasDestination) { + Vector2D scale = sourceSize() / viewport.destination; + surfaceDamage.scale(scale); + } + + if (viewport.hasSource) + surfaceDamage.translate(viewport.source.pos()); + + Vector2D trc = transform % 2 == 1 ? Vector2D{bufferSize.y, bufferSize.x} : bufferSize; + + bufferDamage = surfaceDamage.scale(scale).transform(wlTransformToHyprutils(invertTransform(transform)), trc.x, trc.y).add(bufferDamage); + damage.clear(); + return bufferDamage; +} + +void SSurfaceState::updateSynchronousTexture(SP lastTexture) { + auto [dataPtr, fmt, size] = buffer->buffer->beginDataPtr(0); + if (dataPtr) { + auto drmFmt = NFormatUtils::shmToDRM(fmt); + auto stride = bufferSize.y ? size / bufferSize.y : 0; + if (lastTexture && lastTexture->m_isSynchronous && lastTexture->m_vSize == bufferSize) { + texture = lastTexture; + texture->update(drmFmt, dataPtr, stride, accumulateBufferDamage()); + } else + texture = makeShared(drmFmt, dataPtr, stride, bufferSize); + } + buffer->buffer->endDataPtr(); +} + +void SSurfaceState::reset() { + damage.clear(); + bufferDamage.clear(); + transform = WL_OUTPUT_TRANSFORM_NORMAL; + scale = 1; + offset = {}; + size = {}; +} diff --git a/src/protocols/types/SurfaceState.hpp b/src/protocols/types/SurfaceState.hpp index cd9be5caa..b50937594 100644 --- a/src/protocols/types/SurfaceState.hpp +++ b/src/protocols/types/SurfaceState.hpp @@ -20,16 +20,12 @@ struct SSurfaceState { Vector2D destination; CBox source; } viewport; - bool rejected = false; - bool newBuffer = false; + bool rejected = false; + bool newBuffer = false; - // - void reset() { - damage.clear(); - bufferDamage.clear(); - transform = WL_OUTPUT_TRANSFORM_NORMAL; - scale = 1; - offset = {}; - size = {}; - } + Vector2D sourceSize(); + // Translates damage into bufferDamage, clearing damage and returning the updated bufferDamage + CRegion accumulateBufferDamage(); + void updateSynchronousTexture(SP lastTexture); + void reset(); }; diff --git a/src/render/Texture.cpp b/src/render/Texture.cpp index 95efb15e6..fd5b682e9 100644 --- a/src/render/Texture.cpp +++ b/src/render/Texture.cpp @@ -64,8 +64,9 @@ void CTexture::createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t strid const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); ASSERT(format); - m_iType = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX; - m_vSize = size_; + m_iType = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX; + m_vSize = size_; + m_isSynchronous = true; allocate(); GLCALL(glBindTexture(GL_TEXTURE_2D, m_iTexID)); diff --git a/src/render/Texture.hpp b/src/render/Texture.hpp index 601347072..039b5247e 100644 --- a/src/render/Texture.hpp +++ b/src/render/Texture.hpp @@ -35,14 +35,15 @@ class CTexture { void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage); const std::vector& dataCopy(); - eTextureType m_iType = TEXTURE_RGBA; - GLenum m_iTarget = GL_TEXTURE_2D; - GLuint m_iTexID = 0; - Vector2D m_vSize = {}; - void* m_pEglImage = nullptr; - eTransform m_eTransform = HYPRUTILS_TRANSFORM_NORMAL; - bool m_bOpaque = false; - uint32_t m_iDrmFormat = 0; // for shm + eTextureType m_iType = TEXTURE_RGBA; + GLenum m_iTarget = GL_TEXTURE_2D; + GLuint m_iTexID = 0; + Vector2D m_vSize = {}; + void* m_pEglImage = nullptr; + eTransform m_eTransform = HYPRUTILS_TRANSFORM_NORMAL; + bool m_bOpaque = false; + uint32_t m_iDrmFormat = 0; // for shm + bool m_isSynchronous = false; private: void createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size); diff --git a/src/xwayland/XSurface.cpp b/src/xwayland/XSurface.cpp index b297c29c9..30e38ac26 100644 --- a/src/xwayland/XSurface.cpp +++ b/src/xwayland/XSurface.cpp @@ -62,12 +62,12 @@ void CXWaylandSurface::ensureListeners() { }); listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) { - if (surface->pending.texture && !mapped) { + if (surface->current.texture && !mapped) { map(); return; } - if (!surface->pending.texture && mapped) { + if (!surface->current.texture && mapped) { unmap(); return; } @@ -131,7 +131,7 @@ void CXWaylandSurface::considerMap() { return; } - if (surface->pending.texture) { + if (surface->current.texture) { Debug::log(LOG, "XWayland surface: considerMap, sure, we have a buffer"); map(); return;