From 37e1411e8d94fe8f3fb678588a7df9b8f931910f Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Wed, 31 Jul 2024 20:47:26 +0100 Subject: [PATCH] core/surface/buffer: Buffer lock/release fixes (#7110) --- src/desktop/LayerSurface.cpp | 2 +- src/desktop/WLSurface.cpp | 23 +++-- src/desktop/Window.cpp | 10 +-- src/events/Windows.cpp | 2 +- src/helpers/Monitor.cpp | 7 +- src/managers/PointerManager.cpp | 50 ++++++++--- src/protocols/DRMSyncobj.cpp | 2 +- src/protocols/InputMethodV2.cpp | 4 +- src/protocols/LayerShell.cpp | 2 +- src/protocols/SessionLock.cpp | 2 +- src/protocols/Viewporter.cpp | 4 +- src/protocols/XDGShell.cpp | 6 +- src/protocols/core/Compositor.cpp | 124 +++++++++++++++++---------- src/protocols/core/Compositor.hpp | 35 +++++--- src/protocols/core/DataDevice.cpp | 8 +- src/protocols/core/Seat.cpp | 21 ++++- src/protocols/core/Seat.hpp | 15 ++++ src/protocols/core/Subcompositor.cpp | 4 +- src/protocols/types/Buffer.cpp | 56 ++++++++++++ src/protocols/types/Buffer.hpp | 28 +++++- src/protocols/types/SurfaceRole.hpp | 1 + src/protocols/types/WLBuffer.cpp | 1 - src/protocols/types/WLBuffer.hpp | 2 - src/render/Renderer.cpp | 14 +-- src/render/Texture.cpp | 3 + src/render/Texture.hpp | 1 + src/xwayland/XSurface.cpp | 6 +- 27 files changed, 304 insertions(+), 129 deletions(-) diff --git a/src/desktop/LayerSurface.cpp b/src/desktop/LayerSurface.cpp index 80138910..ba1b1776 100644 --- a/src/desktop/LayerSurface.cpp +++ b/src/desktop/LayerSurface.cpp @@ -242,7 +242,7 @@ void CLayerSurface::onCommit() { if (!mapped) { // we're re-mapping if this is the case - if (layerSurface->surface && !layerSurface->surface->current.buffer) { + if (layerSurface->surface && !layerSurface->surface->current.texture) { fadingOut = false; geometry = {}; g_pHyprRenderer->arrangeLayersForMonitor(monitorID); diff --git a/src/desktop/WLSurface.cpp b/src/desktop/WLSurface.cpp index 3c91a142..45050e35 100644 --- a/src/desktop/WLSurface.cpp +++ b/src/desktop/WLSurface.cpp @@ -57,12 +57,12 @@ bool CWLSurface::small() const { if (!validMapped(m_pWindowOwner) || !exists()) return false; - if (!m_pResource->current.buffer) + if (!m_pResource->current.texture) return false; const auto O = m_pWindowOwner.lock(); - return O->m_vReportedSize.x > m_pResource->current.buffer->size.x + 1 || O->m_vReportedSize.y > m_pResource->current.buffer->size.y + 1; + return O->m_vReportedSize.x > m_pResource->current.bufferSize.x + 1 || O->m_vReportedSize.y > m_pResource->current.bufferSize.y + 1; } Vector2D CWLSurface::correctSmallVec() const { @@ -76,37 +76,36 @@ Vector2D CWLSurface::correctSmallVec() const { } Vector2D CWLSurface::correctSmallVecBuf() const { - if (!exists() || !small() || m_bFillIgnoreSmall || !m_pResource->current.buffer) + if (!exists() || !small() || m_bFillIgnoreSmall || !m_pResource->current.texture) return {}; const auto SIZE = getViewporterCorrectedSize(); - const auto BS = m_pResource->current.buffer->size; + const auto BS = m_pResource->current.bufferSize; return Vector2D{(BS.x - SIZE.x) / 2, (BS.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}); } Vector2D CWLSurface::getViewporterCorrectedSize() const { - if (!exists() || !m_pResource->current.buffer) + if (!exists() || !m_pResource->current.texture) return {}; - return m_pResource->current.viewport.hasDestination ? m_pResource->current.viewport.destination : m_pResource->current.buffer->size; + return m_pResource->current.viewport.hasDestination ? m_pResource->current.viewport.destination : m_pResource->current.bufferSize; } CRegion CWLSurface::computeDamage() const { - if (!m_pResource->current.buffer) + if (!m_pResource->current.texture) return {}; CRegion damage = m_pResource->accumulateCurrentBufferDamage(); - damage.transform(wlTransformToHyprutils(m_pResource->current.transform), m_pResource->current.buffer->size.x, m_pResource->current.buffer->size.y); + damage.transform(wlTransformToHyprutils(m_pResource->current.transform), m_pResource->current.bufferSize.x, m_pResource->current.bufferSize.y); - const auto BUFSIZE = m_pResource->current.buffer->size; + const auto BUFSIZE = m_pResource->current.bufferSize; const auto CORRECTVEC = correctSmallVecBuf(); if (m_pResource->current.viewport.hasSource) damage.intersect(m_pResource->current.viewport.source); - const auto SCALEDSRCSIZE = - m_pResource->current.viewport.hasSource ? m_pResource->current.viewport.source.size() * m_pResource->current.scale : m_pResource->current.buffer->size; + const auto SCALEDSRCSIZE = m_pResource->current.viewport.hasSource ? m_pResource->current.viewport.source.size() * m_pResource->current.scale : m_pResource->current.bufferSize; damage.scale({BUFSIZE.x / SCALEDSRCSIZE.x, BUFSIZE.y / SCALEDSRCSIZE.y}); damage.translate(CORRECTVEC); @@ -114,7 +113,7 @@ CRegion CWLSurface::computeDamage() const { // go from buffer coords in the damage to hl logical const auto BOX = getSurfaceBoxGlobal(); - const Vector2D SCALE = BOX.has_value() ? BOX->size() / m_pResource->current.buffer->size : + const Vector2D SCALE = BOX.has_value() ? BOX->size() / m_pResource->current.bufferSize : Vector2D{1.0 / m_pResource->current.scale, 1.0 / m_pResource->current.scale /* Wrong... but we can't really do better */}; damage.scale(SCALE); diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp index 9316959d..27010454 100644 --- a/src/desktop/Window.cpp +++ b/src/desktop/Window.cpp @@ -1097,18 +1097,18 @@ bool CWindow::opaque() { if (PWORKSPACE->m_fAlpha.value() != 1.f) return false; - if (m_bIsX11 && m_pXWaylandSurface && m_pXWaylandSurface->surface && m_pXWaylandSurface->surface->current.buffer) - return m_pXWaylandSurface->surface->current.buffer->opaque; + if (m_bIsX11 && m_pXWaylandSurface && m_pXWaylandSurface->surface && m_pXWaylandSurface->surface->current.texture) + return m_pXWaylandSurface->surface->current.texture->m_bOpaque; - if (!m_pWLSurface->resource() || !m_pWLSurface->resource()->current.buffer) + if (!m_pWLSurface->resource() || !m_pWLSurface->resource()->current.texture) return false; // TODO: this is wrong const auto EXTENTS = m_pXDGSurface->surface->current.opaque.getExtents(); - if (EXTENTS.w >= m_pXDGSurface->surface->current.buffer->size.x && EXTENTS.h >= m_pXDGSurface->surface->current.buffer->size.y) + if (EXTENTS.w >= m_pXDGSurface->surface->current.bufferSize.x && EXTENTS.h >= m_pXDGSurface->surface->current.bufferSize.y) return true; - return m_pWLSurface->resource()->current.buffer->opaque; + return m_pWLSurface->resource()->current.texture->m_bOpaque; } float CWindow::rounding() { diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index 71ad4ba1..0389f57e 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -752,7 +752,7 @@ void Events::listener_commitWindow(void* owner, void* data) { // tearing: if solitary, redraw it. This still might be a single surface window const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); - if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.buffer) { + if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.texture) { CRegion damageBox{PWINDOW->m_pWLSurface->resource()->accumulateCurrentBufferDamage()}; if (!damageBox.empty()) { diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 377825fb..1cf1b069 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -801,17 +801,16 @@ bool CMonitor::attemptDirectScanout() { const auto PSURFACE = g_pXWaylandManager->getWindowSurface(PCANDIDATE); - if (!PSURFACE || !PSURFACE->current.buffer || PSURFACE->current.buffer->size != vecPixelSize || PSURFACE->current.transform != transform) + if (!PSURFACE || !PSURFACE->current.buffer || PSURFACE->current.bufferSize != vecPixelSize || PSURFACE->current.transform != transform) return false; // we can't scanout shm buffers. - if (!PSURFACE->current.buffer->dmabuf().success) + if (!PSURFACE->current.buffer || !PSURFACE->current.texture || !PSURFACE->current.texture->m_pEglImage /* dmabuf */) return false; // FIXME: make sure the buffer actually follows the available scanout dmabuf formats // and comes from the appropriate device. This may implode on multi-gpu!! - - output->state->setBuffer(PSURFACE->current.buffer); + output->state->setBuffer(PSURFACE->current.buffer->buffer.lock()); output->state->setPresentationMode(tearingState.activelyTearing ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE : Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC); diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index 8314e79a..3ba34c11 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -4,6 +4,7 @@ #include "../protocols/PointerGestures.hpp" #include "../protocols/FractionalScale.hpp" #include "../protocols/core/Compositor.hpp" +#include "../protocols/core/Seat.hpp" #include "eventLoop/EventLoopManager.hpp" #include "SeatManager.hpp" #include @@ -156,15 +157,15 @@ void CPointerManager::setCursorSurface(SP surf, const Vector2D& hots currentCursorImage.destroySurface = surf->events.destroy.registerListener([this](std::any data) { resetCursorImage(); }); currentCursorImage.commitSurface = surf->resource()->events.commit.registerListener([this](std::any data) { damageIfSoftware(); - currentCursorImage.size = currentCursorImage.surface->resource()->current.buffer ? currentCursorImage.surface->resource()->current.buffer->size : Vector2D{}; + currentCursorImage.size = currentCursorImage.surface->resource()->current.texture ? currentCursorImage.surface->resource()->current.bufferSize : Vector2D{}; currentCursorImage.scale = currentCursorImage.surface ? currentCursorImage.surface->resource()->current.scale : 1.F; recheckEnteredOutputs(); updateCursorBackend(); damageIfSoftware(); }); - if (surf->resource()->current.buffer) { - currentCursorImage.size = surf->resource()->current.buffer->size; + if (surf->resource()->current.texture) { + currentCursorImage.size = surf->resource()->current.bufferSize; timespec now; clock_gettime(CLOCK_MONOTONIC, &now); surf->resource()->frame(&now); @@ -430,16 +431,39 @@ SP CPointerManager::renderHWCursorBuffer(SP(bufData)); - auto texBuffer = currentCursorImage.pBuffer ? currentCursorImage.pBuffer : currentCursorImage.surface->resource()->current.buffer; + if (currentCursorImage.pBuffer) { + auto texAttrs = currentCursorImage.pBuffer->shm(); - if (texBuffer) { - auto textAttrs = texBuffer->shm(); - auto texData = texBuffer->beginDataPtr(GBM_BO_TRANSFER_WRITE); - auto texPtr = std::get<0>(texData); - Debug::log(TRACE, "cursor texture {}x{} {} {} {}", textAttrs.size.x, textAttrs.size.y, (void*)texPtr, textAttrs.format, textAttrs.stride); + if (!texAttrs.success) { + Debug::log(TRACE, "Cannot use dumb copy on dmabuf cursor buffers"); + return nullptr; + } + + auto texData = currentCursorImage.pBuffer->beginDataPtr(GBM_BO_TRANSFER_WRITE); + auto texPtr = std::get<0>(texData); + Debug::log(TRACE, "cursor texture {}x{} {} {} {}", texAttrs.size.x, texAttrs.size.y, (void*)texPtr, texAttrs.format, texAttrs.stride); // copy cursor texture - for (int i = 0; i < texBuffer->shm().size.y; i++) - memcpy(bufPtr + i * buf->dmabuf().strides[0], texPtr + i * textAttrs.stride, textAttrs.stride); + for (int i = 0; i < texAttrs.size.y; i++) + memcpy(bufPtr + i * buf->dmabuf().strides[0], texPtr + i * texAttrs.stride, texAttrs.stride); + } else if (currentCursorImage.surface && currentCursorImage.surface->resource()->role->role() == SURFACE_ROLE_CURSOR) { + const auto SURFACE = currentCursorImage.surface->resource(); + auto& shmBuffer = CCursorSurfaceRole::cursorPixelData(SURFACE); + Debug::log(TRACE, "cursor texture pixel data length: {}B", shmBuffer.size()); + + if (shmBuffer.data()) { + // copy cursor texture + // assume format is 32bpp + size_t STRIDE = 4 * SURFACE->current.bufferSize.x; + for (int i = 0; i < SURFACE->current.bufferSize.y; i++) + memcpy(bufPtr + i * buf->dmabuf().strides[0], shmBuffer.data() + i * STRIDE, STRIDE); + } else { + // if there is no data, hide the cursor + memset(bufPtr, '\0', buf->size.x * buf->size.y * 4 /* assume 32bpp */); + } + + } else { + Debug::log(TRACE, "Unsupported cursor buffer/surface, falling back to sw (can't dumb copy)"); + return nullptr; } buf->endDataPtr(); @@ -740,7 +764,7 @@ void CPointerManager::onMonitorLayoutChange() { } SP CPointerManager::getCurrentCursorTexture() { - if (!currentCursorImage.pBuffer && (!currentCursorImage.surface || !currentCursorImage.surface->resource()->current.buffer)) + if (!currentCursorImage.pBuffer && (!currentCursorImage.surface || !currentCursorImage.surface->resource()->current.texture)) return nullptr; if (currentCursorImage.pBuffer) { @@ -749,7 +773,7 @@ SP CPointerManager::getCurrentCursorTexture() { return currentCursorImage.bufferTex; } - return currentCursorImage.surface->resource()->current.buffer->texture; + return currentCursorImage.surface->resource()->current.texture; } void CPointerManager::attachPointer(SP pointer) { diff --git a/src/protocols/DRMSyncobj.cpp b/src/protocols/DRMSyncobj.cpp index 41178109..33339554 100644 --- a/src/protocols/DRMSyncobj.cpp +++ b/src/protocols/DRMSyncobj.cpp @@ -47,7 +47,7 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SPpending.buffer) { + if ((acquireTimeline || releaseTimeline) && !surface->pending.texture) { resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer"); surface->pending.rejected = true; return; diff --git a/src/protocols/InputMethodV2.cpp b/src/protocols/InputMethodV2.cpp index def4d837..fd306f09 100644 --- a/src/protocols/InputMethodV2.cpp +++ b/src/protocols/InputMethodV2.cpp @@ -107,14 +107,14 @@ CInputMethodPopupV2::CInputMethodPopupV2(SP resource_, }); listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) { - if (pSurface->current.buffer && !mapped) { + if (pSurface->current.texture && !mapped) { mapped = true; pSurface->map(); events.map.emit(); return; } - if (!pSurface->current.buffer && mapped) { + if (!pSurface->current.texture && mapped) { mapped = false; pSurface->unmap(); events.unmap.emit(); diff --git a/src/protocols/LayerShell.cpp b/src/protocols/LayerShell.cpp index 5018828e..0ed1b219 100644 --- a/src/protocols/LayerShell.cpp +++ b/src/protocols/LayerShell.cpp @@ -44,7 +44,7 @@ CLayerShellResource::CLayerShellResource(SP resource_, SPcurrent.buffer; + bool attachedBuffer = surface->current.texture; if (attachedBuffer && !configured) { surface->error(-1, "layerSurface was not configured, but a buffer was attached"); diff --git a/src/protocols/SessionLock.cpp b/src/protocols/SessionLock.cpp index 42df5fd6..df97413c 100644 --- a/src/protocols/SessionLock.cpp +++ b/src/protocols/SessionLock.cpp @@ -24,7 +24,7 @@ CSessionLockSurface::CSessionLockSurface(SP resource_, resource->setAckConfigure([this](CExtSessionLockSurfaceV1* r, uint32_t serial) { ackdConfigure = true; }); listeners.surfaceCommit = pSurface->events.commit.registerListener([this](std::any d) { - if (!pSurface->current.buffer) { + if (!pSurface->current.texture) { LOGM(ERR, "SessionLock attached a null buffer"); resource->error(EXT_SESSION_LOCK_SURFACE_V1_ERROR_NULL_BUFFER, "Null buffer attached"); return; diff --git a/src/protocols/Viewporter.cpp b/src/protocols/Viewporter.cpp index 03c5775e..78f3039f 100644 --- a/src/protocols/Viewporter.cpp +++ b/src/protocols/Viewporter.cpp @@ -54,13 +54,13 @@ CViewportResource::CViewportResource(SP resource_, SPevents.precommit.registerListener([this](std::any d) { - if (!surface || !surface->pending.buffer) + if (!surface || !surface->pending.texture) return; if (surface->pending.viewport.hasSource) { auto& src = surface->pending.viewport.source; - if (src.w + src.x > surface->pending.buffer->size.x || src.h + src.y > surface->pending.buffer->size.y) { + if (src.w + src.x > surface->pending.bufferSize.x || src.h + src.y > surface->pending.bufferSize.y) { resource->error(WP_VIEWPORT_ERROR_BAD_VALUE, "Box doesn't fit"); surface->pending.rejected = true; return; diff --git a/src/protocols/XDGShell.cpp b/src/protocols/XDGShell.cpp index cb8a5bc3..71873374 100644 --- a/src/protocols/XDGShell.cpp +++ b/src/protocols/XDGShell.cpp @@ -347,12 +347,12 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP resource_, SPcurrent = toplevel->pending; - if (initialCommit && surface->pending.buffer) { + if (initialCommit && surface->pending.texture) { resource->error(-1, "Buffer attached before initial commit"); return; } - if (surface->current.buffer && !mapped) { + if (surface->current.texture && !mapped) { // this forces apps to not draw CSD. if (toplevel) toplevel->setMaximized(true); @@ -363,7 +363,7 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP resource_, SPcurrent.buffer && mapped) { + if (!surface->current.texture && mapped) { mapped = false; events.unmap.emit(); surface->unmap(); diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index 43d3059b..81be8e46 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -1,5 +1,6 @@ #include "Compositor.hpp" #include "Output.hpp" +#include "Seat.hpp" #include "../types/WLBuffer.hpp" #include #include @@ -9,6 +10,7 @@ #include "../PresentationTime.hpp" #include "../DRMSyncobj.hpp" #include "../../render/Renderer.hpp" +#include #define LOGM PROTO::compositor->protoLog @@ -19,8 +21,6 @@ class CDefaultSurfaceRole : public ISurfaceRole { } }; -SP defaultRole = makeShared(); - CWLCallbackResource::CWLCallbackResource(SP resource_) : resource(resource_) { ; } @@ -63,7 +63,7 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : resource(reso resource->setData(this); - role = defaultRole; + role = makeShared(); resource->setDestroy([this](CWlSurface* r) { destroy(); }); resource->setOnDestroy([this](CWlSurface* r) { destroy(); }); @@ -75,41 +75,42 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : resource(reso pending.buffer.reset(); pending.texture.reset(); } else { - auto res = CWLBufferResource::fromResource(buffer); - pending.buffer = res && res->buffer ? res->buffer.lock() : nullptr; - pending.size = res && res->buffer ? res->buffer->size : Vector2D{}; - pending.texture = res && res->buffer ? res->buffer->texture : nullptr; - if (res) - res->released = false; + auto res = CWLBufferResource::fromResource(buffer); + pending.buffer = res && res->buffer ? makeShared(res->buffer.lock(), self.lock()) : nullptr; + pending.size = res && res->buffer ? res->buffer->size : Vector2D{}; + pending.texture = res && res->buffer ? res->buffer->texture : nullptr; + pending.bufferSize = res && res->buffer ? res->buffer->size : Vector2D{}; } - Vector2D oldBufSize = current.buffer ? current.buffer->size : Vector2D{}; - Vector2D newBufSize = pending.buffer ? pending.buffer->size : Vector2D{}; + Vector2D oldBufSize = current.buffer ? current.bufferSize : Vector2D{}; + Vector2D newBufSize = pending.buffer ? pending.bufferSize : Vector2D{}; if (oldBufSize != newBufSize || current.buffer != pending.buffer) pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}}; }); resource->setCommit([this](CWlSurface* r) { - if (pending.buffer) - pending.bufferDamage.intersect(CBox{{}, pending.buffer->size}); + if (pending.texture) + pending.bufferDamage.intersect(CBox{{}, pending.bufferSize}); - if (!pending.buffer) + if (!pending.texture) pending.size = {}; else if (pending.viewport.hasDestination) pending.size = pending.viewport.destination; else if (pending.viewport.hasSource) pending.size = pending.viewport.source.size(); else { - Vector2D tfs = pending.transform % 2 == 1 ? Vector2D{pending.buffer->size.y, pending.buffer->size.x} : pending.buffer->size; + Vector2D tfs = pending.transform % 2 == 1 ? Vector2D{pending.bufferSize.y, pending.bufferSize.x} : pending.bufferSize; pending.size = tfs / pending.scale; } pending.damage.intersect(CBox{{}, pending.size}); events.precommit.emit(); - if (pending.rejected) + if (pending.rejected) { + dropPendingBuffer(); return; + } if (stateLocks <= 0) commitPendingState(); @@ -160,6 +161,14 @@ void CWLSurfaceResource::destroy() { PROTO::compositor->destroyResource(this); } +void CWLSurfaceResource::dropPendingBuffer() { + pending.buffer.reset(); +} + +void CWLSurfaceResource::dropCurrentBuffer() { + current.buffer.reset(); +} + SP CWLSurfaceResource::fromResource(wl_resource* res) { auto data = (CWLSurfaceResource*)(((CWlSurface*)wl_resource_get_user_data(res))->data()); return data ? data->self.lock() : nullptr; @@ -240,7 +249,7 @@ void CWLSurfaceResource::frame(timespec* now) { } void CWLSurfaceResource::resetRole() { - role = defaultRole; + role = makeShared(); } void CWLSurfaceResource::bfHelper(std::vector> nodes, std::function, const Vector2D&, void*)> fn, void* data) { @@ -254,6 +263,8 @@ void CWLSurfaceResource::bfHelper(std::vector> nodes, std for (auto& c : n->subsurfaces) { if (c->zIndex >= 0) break; + if (c->surface.expired()) + continue; nodes2.push_back(c->surface.lock()); } } @@ -277,6 +288,8 @@ void CWLSurfaceResource::bfHelper(std::vector> nodes, std for (auto& c : n->subsurfaces) { if (c->zIndex < 0) continue; + if (c->surface.expired()) + continue; nodes2.push_back(c->surface.lock()); } } @@ -343,14 +356,9 @@ void CWLSurfaceResource::unmap() { } void CWLSurfaceResource::releaseBuffers(bool onlyCurrent) { - if (current.buffer && !current.buffer->resource->released) - current.buffer->sendRelease(); - if (pending.buffer && !pending.buffer->resource->released && !onlyCurrent) - pending.buffer->sendRelease(); - - pending.buffer.reset(); if (!onlyCurrent) - current.buffer.reset(); + dropPendingBuffer(); + dropCurrentBuffer(); } void CWLSurfaceResource::error(int code, const std::string& str) { @@ -375,18 +383,18 @@ CBox CWLSurfaceResource::extends() { } Vector2D CWLSurfaceResource::sourceSize() { - if (!current.buffer) + if (!current.texture) return {}; if (current.viewport.hasSource) return current.viewport.source.size(); - Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.buffer->size.y, current.buffer->size.x} : current.buffer->size; + Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.bufferSize.y, current.bufferSize.x} : current.bufferSize; return trc / current.scale; } CRegion CWLSurfaceResource::accumulateCurrentBufferDamage() { - if (!current.buffer) + if (!current.texture) return {}; CRegion surfaceDamage = current.damage; @@ -398,7 +406,7 @@ CRegion CWLSurfaceResource::accumulateCurrentBufferDamage() { if (current.viewport.hasSource) surfaceDamage.translate(current.viewport.source.pos()); - Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.buffer->size.y, current.buffer->size.x} : current.buffer->size; + 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); } @@ -421,16 +429,21 @@ void CWLSurfaceResource::commitPendingState() { pending.damage.clear(); pending.bufferDamage.clear(); - if (current.buffer && current.buffer->texture) - current.buffer->texture->m_eTransform = wlTransformToHyprutils(current.transform); + if (current.texture) + current.texture->m_eTransform = wlTransformToHyprutils(current.transform); - if (current.buffer && !current.buffer->resource->released) { - current.buffer->update(accumulateCurrentBufferDamage()); + if (current.buffer && current.buffer->buffer) { + current.buffer->buffer->update(accumulateCurrentBufferDamage()); + + // if the surface is a cursor, update the shm buffer + // TODO: don't update the entire texture + if (role->role() == SURFACE_ROLE_CURSOR) + updateCursorShm(); // release the buffer if it's synchronous as update() has done everything thats needed // so we can let the app know we're done. - if (current.buffer->isSynchronous()) - current.buffer->sendReleaseWithSurface(self.lock()); + if (current.buffer->buffer->isSynchronous()) + dropCurrentBuffer(); } // TODO: we should _accumulate_ and not replace above if sync @@ -455,20 +468,37 @@ void CWLSurfaceResource::commitPendingState() { } // for async buffers, we can only release the buffer once we are unrefing it from current. - if (previousBuffer && !previousBuffer->isSynchronous() && !previousBuffer->resource->released) { - if (previousBuffer->lockedByBackend) { - previousBuffer->hlEvents.backendRelease = previousBuffer->events.backendRelease.registerListener([this, previousBuffer](std::any data) { - if (!self.expired()) // could be dead in the dtor - previousBuffer->sendReleaseWithSurface(self.lock()); - else - previousBuffer->sendRelease(); - previousBuffer->hlEvents.backendRelease.reset(); - }); - } else - previousBuffer->sendReleaseWithSurface(self.lock()); - - previousBuffer->resource->released = true; // set it here regardless so we dont set more listeners for backendRelease + // if the backend took it, ref it with the lambda. Otherwise, the end of this scope will release it. + if (previousBuffer && previousBuffer->buffer && !previousBuffer->buffer->isSynchronous()) { + if (previousBuffer->buffer->lockedByBackend && !previousBuffer->buffer->hlEvents.backendRelease) { + previousBuffer->buffer->lock(); + previousBuffer->buffer->unlockOnBufferRelease(self); + } } + + lastBuffer = current.buffer ? current.buffer->buffer : WP{}; +} + +void CWLSurfaceResource::updateCursorShm() { + auto buf = current.buffer ? current.buffer : lastBuffer; + + if (!buf) + return; + + // TODO: actually use damage + auto& shmData = CCursorSurfaceRole::cursorPixelData(self.lock()); + auto shmAttrs = current.buffer->buffer->shm(); + + if (!shmAttrs.success) { + LOGM(TRACE, "updateCursorShm: ignoring, not a shm buffer"); + return; + } + + // no need to end, shm. + auto [pixelData, fmt, bufLen] = current.buffer->buffer->beginDataPtr(0); + + shmData.resize(bufLen); + memcpy(shmData.data(), pixelData, bufLen); } void CWLSurfaceResource::presentFeedback(timespec* when, CMonitor* pMonitor, bool needsExplicitSync) { diff --git a/src/protocols/core/Compositor.hpp b/src/protocols/core/Compositor.hpp index 79cd1de6..460ec755 100644 --- a/src/protocols/core/Compositor.hpp +++ b/src/protocols/core/Compositor.hpp @@ -84,13 +84,13 @@ class CWLSurfaceResource { } events; struct SState { - CRegion opaque, input = CBox{{}, {INT32_MAX, INT32_MAX}}, damage, bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}} /* initial damage */; - wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; - int scale = 1; - SP buffer; - SP texture; - Vector2D offset; - Vector2D size; + CRegion opaque, input = CBox{{}, {INT32_MAX, INT32_MAX}}, damage, bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}} /* initial damage */; + wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + int scale = 1; + SP buffer; // buffer ref will be released once the buffer is no longer locked. For checking if a buffer is attached to this state, check texture. + SP texture; + Vector2D offset; + Vector2D size, bufferSize; struct { bool hasDestination = false; bool hasSource = false; @@ -116,7 +116,7 @@ class CWLSurfaceResource { std::vector> enteredOutputs; bool mapped = false; std::vector> subsurfaces; - WP role; + SP role; WP viewportResource; WP syncobj; // may not be present @@ -134,12 +134,21 @@ class CWLSurfaceResource { SP resource; wl_client* pClient = nullptr; - int stateLocks = 0; + // this is for cursor dumb copy. Due to our (and wayland's...) architecture, + // this stupid-ass hack is used + WP lastBuffer; - void destroy(); - void releaseBuffers(bool onlyCurrent = true); - void commitPendingState(); - void bfHelper(std::vector> nodes, std::function, const Vector2D&, void*)> fn, void* data); + int stateLocks = 0; + + void destroy(); + void releaseBuffers(bool onlyCurrent = true); + void dropPendingBuffer(); + void dropCurrentBuffer(); + void commitPendingState(); + void bfHelper(std::vector> nodes, std::function, const Vector2D&, void*)> fn, void* data); + void updateCursorShm(); + + friend class CWLPointerResource; }; class CWLCompositorResource { diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp index 9634d569..fe3905d0 100644 --- a/src/protocols/core/DataDevice.cpp +++ b/src/protocols/core/DataDevice.cpp @@ -469,12 +469,12 @@ void CWLDataDeviceProtocol::initiateDrag(WP currentSource if (dragSurface) { dnd.dndSurfaceDestroy = dragSurface->events.destroy.registerListener([this](std::any d) { abortDrag(); }); dnd.dndSurfaceCommit = dragSurface->events.commit.registerListener([this](std::any d) { - if (dnd.dndSurface->current.buffer && !dnd.dndSurface->mapped) { + if (dnd.dndSurface->current.texture && !dnd.dndSurface->mapped) { dnd.dndSurface->map(); return; } - if (dnd.dndSurface->current.buffer <= 0 && dnd.dndSurface->mapped) { + if (dnd.dndSurface->current.texture <= 0 && dnd.dndSurface->mapped) { dnd.dndSurface->unmap(); return; } @@ -660,13 +660,13 @@ void CWLDataDeviceProtocol::abortDrag() { } void CWLDataDeviceProtocol::renderDND(CMonitor* pMonitor, timespec* when) { - if (!dnd.dndSurface || !dnd.dndSurface->current.buffer || !dnd.dndSurface->current.buffer->texture) + if (!dnd.dndSurface || !dnd.dndSurface->current.texture) return; const auto POS = g_pInputManager->getMouseCoordsInternal(); CBox box = CBox{POS, dnd.dndSurface->current.size}.translate(-pMonitor->vecPosition + g_pPointerManager->cursorSizeLogical() / 2.F).scale(pMonitor->scale); - g_pHyprOpenGL->renderTexture(dnd.dndSurface->current.buffer->texture, &box, 1.F); + g_pHyprOpenGL->renderTexture(dnd.dndSurface->current.texture, &box, 1.F); box = CBox{POS, dnd.dndSurface->current.size}.translate(g_pPointerManager->cursorSizeLogical() / 2.F); g_pHyprRenderer->damageBox(&box); diff --git a/src/protocols/core/Seat.cpp b/src/protocols/core/Seat.cpp index 7a295372..bb6a9d4d 100644 --- a/src/protocols/core/Seat.cpp +++ b/src/protocols/core/Seat.cpp @@ -119,7 +119,19 @@ CWLPointerResource::CWLPointerResource(SP resource_, SPonSetCursor(owner.lock(), serial, surf ? CWLSurfaceResource::fromResource(surf) : nullptr, {hotX, hotY}); + auto surfResource = surf ? CWLSurfaceResource::fromResource(surf) : nullptr; + + if (surfResource && surfResource->role->role() != SURFACE_ROLE_CURSOR && surfResource->role->role() != SURFACE_ROLE_UNASSIGNED) { + r->error(-1, "Cursor surface already has a different role"); + return; + } + + if (surfResource) { + surfResource->role = makeShared(); + surfResource->updateCursorShm(); + } + + g_pSeatManager->onSetCursor(owner.lock(), serial, surfResource, {hotX, hotY}); }); if (g_pSeatManager->state.pointerFocus && g_pSeatManager->state.pointerFocus->client() == resource->client()) @@ -546,3 +558,10 @@ SP CWLSeatProtocol::seatResourceForClient(wl_client* client) { return nullptr; } + +std::vector& CCursorSurfaceRole::cursorPixelData(SP surface) { + RASSERT(surface->role->role() == SURFACE_ROLE_CURSOR, "cursorPixelData called on a non-cursor surface"); + + auto role = (CCursorSurfaceRole*)surface->role.get(); + return role->cursorShmPixelData; +} diff --git a/src/protocols/core/Seat.hpp b/src/protocols/core/Seat.hpp index 09b36056..755a9c2f 100644 --- a/src/protocols/core/Seat.hpp +++ b/src/protocols/core/Seat.hpp @@ -16,6 +16,7 @@ #include "wayland.hpp" #include "../../helpers/signal/Signal.hpp" #include "../../helpers/math/Math.hpp" +#include "../types/SurfaceRole.hpp" constexpr const char* HL_SEAT_NAME = "Hyprland"; @@ -27,6 +28,20 @@ class CWLKeyboardResource; class CWLTouchResource; class CWLSeatResource; +class CCursorSurfaceRole : public ISurfaceRole { + public: + virtual eSurfaceRole role() { + return SURFACE_ROLE_CURSOR; + } + + // gets the current pixel data from a shm surface + // will assert if the surface is not a cursor + static std::vector& cursorPixelData(SP surface); + + private: + std::vector cursorShmPixelData; +}; + class CWLTouchResource { public: CWLTouchResource(SP resource_, SP owner_); diff --git a/src/protocols/core/Subcompositor.cpp b/src/protocols/core/Subcompositor.cpp index c0c1f258..90f89d91 100644 --- a/src/protocols/core/Subcompositor.cpp +++ b/src/protocols/core/Subcompositor.cpp @@ -80,13 +80,13 @@ CWLSubsurfaceResource::CWLSubsurfaceResource(SP resource_, SPevents.commit.registerListener([this](std::any d) { - if (surface->current.buffer && !surface->mapped) { + if (surface->current.texture && !surface->mapped) { surface->map(); surface->events.map.emit(); return; } - if (!surface->current.buffer && surface->mapped) { + if (!surface->current.texture && surface->mapped) { surface->events.unmap.emit(); surface->unmap(); return; diff --git a/src/protocols/types/Buffer.cpp b/src/protocols/types/Buffer.cpp index 0217f7e2..40f2eaf8 100644 --- a/src/protocols/types/Buffer.cpp +++ b/src/protocols/types/Buffer.cpp @@ -1,5 +1,10 @@ #include "Buffer.hpp" +IHLBuffer::~IHLBuffer() { + if (locked() && resource) + sendRelease(); +} + void IHLBuffer::sendRelease() { resource->sendRelease(); } @@ -8,3 +13,54 @@ void IHLBuffer::sendReleaseWithSurface(SP surf) { if (resource && resource->good()) resource->sendReleaseWithSurface(surf); } + +void IHLBuffer::lock() { + nLocks++; +} + +void IHLBuffer::unlock() { + nLocks--; + + ASSERT(nLocks >= 0); + + if (nLocks == 0) + sendRelease(); +} + +void IHLBuffer::unlockWithSurface(SP surf) { + nLocks--; + + ASSERT(nLocks >= 0); + + if (nLocks == 0) + sendReleaseWithSurface(surf); +} + +bool IHLBuffer::locked() { + return nLocks > 0; +} + +void IHLBuffer::unlockOnBufferRelease(WP surf) { + unlockSurface = surf; + hlEvents.backendRelease = events.backendRelease.registerListener([this](std::any data) { + if (unlockSurface.expired()) + unlock(); + else + unlockWithSurface(unlockSurface.lock()); + hlEvents.backendRelease.reset(); + }); +} + +CHLBufferReference::CHLBufferReference(SP buffer_, SP surface_) : buffer(buffer_), surface(surface_) { + buffer->lock(); +} + +CHLBufferReference::~CHLBufferReference() { + if (buffer.expired()) + return; + + if (surface) + buffer->unlockWithSurface(surface.lock()); + else + buffer->unlock(); +} diff --git a/src/protocols/types/Buffer.hpp b/src/protocols/types/Buffer.hpp index ba8278f3..d2157181 100644 --- a/src/protocols/types/Buffer.hpp +++ b/src/protocols/types/Buffer.hpp @@ -8,9 +8,7 @@ class IHLBuffer : public Aquamarine::IBuffer { public: - virtual ~IHLBuffer() { - ; - } + virtual ~IHLBuffer(); virtual Aquamarine::eBufferCapability caps() = 0; virtual Aquamarine::eBufferType type() = 0; virtual void update(const CRegion& damage) = 0; @@ -18,6 +16,12 @@ class IHLBuffer : public Aquamarine::IBuffer { virtual bool good() = 0; virtual void sendRelease(); virtual void sendReleaseWithSurface(SP); + virtual void lock(); + virtual void unlock(); + virtual void unlockWithSurface(SP surf); + virtual bool locked(); + + void unlockOnBufferRelease(WP surf /* optional */); SP texture; bool opaque = false; @@ -26,4 +30,22 @@ class IHLBuffer : public Aquamarine::IBuffer { struct { CHyprSignalListener backendRelease; } hlEvents; + + private: + int nLocks = 0; + + WP unlockSurface; +}; + +// for ref-counting. Releases in ~dtor +// surface optional +class CHLBufferReference { + public: + CHLBufferReference(SP buffer, SP surface); + ~CHLBufferReference(); + + WP buffer; + + private: + WP surface; }; diff --git a/src/protocols/types/SurfaceRole.hpp b/src/protocols/types/SurfaceRole.hpp index faaf70ee..64586f01 100644 --- a/src/protocols/types/SurfaceRole.hpp +++ b/src/protocols/types/SurfaceRole.hpp @@ -6,6 +6,7 @@ enum eSurfaceRole { SURFACE_ROLE_LAYER_SHELL, SURFACE_ROLE_EASTER_EGG, SURFACE_ROLE_SUBSURFACE, + SURFACE_ROLE_CURSOR, }; class ISurfaceRole { diff --git a/src/protocols/types/WLBuffer.cpp b/src/protocols/types/WLBuffer.cpp index e42094b1..d34a867d 100644 --- a/src/protocols/types/WLBuffer.cpp +++ b/src/protocols/types/WLBuffer.cpp @@ -29,7 +29,6 @@ bool CWLBufferResource::good() { } void CWLBufferResource::sendRelease() { - released = true; resource->sendRelease(); } diff --git a/src/protocols/types/WLBuffer.hpp b/src/protocols/types/WLBuffer.hpp index f4424abc..59512128 100644 --- a/src/protocols/types/WLBuffer.hpp +++ b/src/protocols/types/WLBuffer.hpp @@ -24,8 +24,6 @@ class CWLBufferResource { WP self; - bool released = false; - private: CWLBufferResource(SP resource_); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index e4f97895..127ae187 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -102,10 +102,10 @@ CHyprRenderer::~CHyprRenderer() { } static void renderSurface(SP surface, int x, int y, void* data) { - if (!surface->current.buffer || !surface->current.buffer->texture) + if (!surface->current.texture) return; - const auto& TEXTURE = surface->current.buffer->texture; + const auto& TEXTURE = surface->current.texture; const auto RDATA = (SRenderData*)data; const auto INTERACTIVERESIZEINPROGRESS = RDATA->pWindow && g_pInputManager->currentlyDraggedWindow.lock() == RDATA->pWindow && g_pInputManager->dragMode == MBIND_RESIZE; @@ -192,8 +192,8 @@ static void renderSurface(SP surface, int x, int y, void* da windowBox.round(); const bool MISALIGNEDFSV1 = std::floor(RDATA->pMonitor->scale) != RDATA->pMonitor->scale /* Fractional */ && surface->current.scale == 1 /* fs protocol */ && - windowBox.size() != surface->current.buffer->size /* misaligned */ && DELTALESSTHAN(windowBox.width, surface->current.buffer->size.x, 3) && - DELTALESSTHAN(windowBox.height, surface->current.buffer->size.y, 3) /* off by one-or-two */ && + windowBox.size() != surface->current.bufferSize /* misaligned */ && DELTALESSTHAN(windowBox.width, surface->current.bufferSize.x, 3) && + DELTALESSTHAN(windowBox.height, surface->current.bufferSize.y, 3) /* off by one-or-two */ && (!RDATA->pWindow || (!RDATA->pWindow->m_vRealSize.isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */; g_pHyprRenderer->calculateUVForSurface(RDATA->pWindow, surface, RDATA->surface == surface, windowBox.size(), MISALIGNEDFSV1); @@ -1014,7 +1014,7 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SPcurrent.viewport.hasSource) { // we stretch it to dest. if no dest, to 1,1 - Vector2D bufferSize = pSurface->current.buffer->size; + Vector2D bufferSize = pSurface->current.bufferSize; auto bufferSource = pSurface->current.viewport.source; // calculate UV for the basic src_box. Assume dest == size. Scale to dest later @@ -1030,8 +1030,8 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SPcurrent.buffer->size; - const Vector2D MISALIGNMENT = pSurface->current.buffer->size - projSize; + const Vector2D PIXELASUV = Vector2D{1, 1} / pSurface->current.bufferSize; + const Vector2D MISALIGNMENT = pSurface->current.bufferSize - projSize; if (MISALIGNMENT != Vector2D{}) uvBR -= MISALIGNMENT * PIXELASUV; } diff --git a/src/render/Texture.cpp b/src/render/Texture.cpp index 0f5b4c4c..94d00184 100644 --- a/src/render/Texture.cpp +++ b/src/render/Texture.cpp @@ -28,6 +28,8 @@ CTexture::CTexture(const SP buffer) { if (!buffer) return; + m_bOpaque = buffer->opaque; + auto attrs = buffer->dmabuf(); if (!attrs.success) { @@ -86,6 +88,7 @@ void CTexture::createFromDma(const Aquamarine::SDMABUFAttrs& attrs, void* image) return; } + m_bOpaque = FormatUtils::isFormatOpaque(attrs.format); m_iTarget = GL_TEXTURE_2D; m_iType = TEXTURE_RGBA; m_vSize = attrs.size; diff --git a/src/render/Texture.hpp b/src/render/Texture.hpp index a54be8c5..e0ef5503 100644 --- a/src/render/Texture.hpp +++ b/src/render/Texture.hpp @@ -41,6 +41,7 @@ class CTexture { Vector2D m_vSize = {}; void* m_pEglImage = nullptr; eTransform m_eTransform = HYPRUTILS_TRANSFORM_NORMAL; + bool m_bOpaque = 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 107b22da..30ffbc68 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.buffer && !mapped) { + if (surface->pending.texture && !mapped) { map(); return; } - if (!surface->pending.buffer && mapped) { + if (!surface->pending.texture && mapped) { unmap(); return; } @@ -131,7 +131,7 @@ void CXWaylandSurface::considerMap() { return; } - if (surface->pending.buffer) { + if (surface->pending.texture) { Debug::log(LOG, "XWayland surface: considerMap, sure, we have a buffer"); map(); return;