diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 98f45cce..97f74609 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -1348,13 +1348,13 @@ bool CMonitor::attemptDirectScanout() { return false; // we can't scanout shm buffers. - const auto params = PSURFACE->current.buffer->buffer->dmabuf(); + const auto params = PSURFACE->current.buffer->dmabuf(); if (!params.success || !PSURFACE->current.texture->m_pEglImage /* dmabuf */) return false; - Debug::log(TRACE, "attemptDirectScanout: surface {:x} passed, will attempt, buffer {}", (uintptr_t)PSURFACE.get(), (uintptr_t)PSURFACE->current.buffer->buffer.get()); + Debug::log(TRACE, "attemptDirectScanout: surface {:x} passed, will attempt, buffer {}", (uintptr_t)PSURFACE.get(), (uintptr_t)PSURFACE->current.buffer.buffer.get()); - auto PBUFFER = PSURFACE->current.buffer->buffer; + auto PBUFFER = PSURFACE->current.buffer.buffer; if (PBUFFER == output->state->state().buffer) { if (scanoutNeedsCursorUpdate) { @@ -1407,10 +1407,10 @@ bool CMonitor::attemptDirectScanout() { auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings(output); - bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->current.buffer && PSURFACE->current.buffer->acquire && explicitOptions.explicitKMSEnabled; + bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->current.buffer && PSURFACE->current.acquire && explicitOptions.explicitKMSEnabled; if (DOEXPLICIT) { // wait for surface's explicit fence if present - inFence = PSURFACE->current.buffer->acquire->exportAsFD(); + inFence = PSURFACE->current.acquire.exportAsFD(); if (inFence.isValid()) { Debug::log(TRACE, "attemptDirectScanout: setting IN_FENCE for aq to {}", inFence.get()); output->state->setExplicitInFence(inFence.get()); diff --git a/src/protocols/DRMSyncobj.cpp b/src/protocols/DRMSyncobj.cpp index 0a4ca439..c2960746 100644 --- a/src/protocols/DRMSyncobj.cpp +++ b/src/protocols/DRMSyncobj.cpp @@ -75,98 +75,41 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(UPevents.precommit.registerListener([this](std::any d) { - const bool PENDING_HAS_NEW_BUFFER = surface->pending.updated & SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_BUFFER; - - if (!surface->pending.buffer && PENDING_HAS_NEW_BUFFER && !surface->pending.texture) { - removeAllWaiters(); - surface->commitPendingState(surface->pending); - return; // null buffer attached. - } - - if (!surface->pending.buffer && !PENDING_HAS_NEW_BUFFER && surface->current.buffer) { - surface->current.bufferDamage.clear(); - surface->current.damage.clear(); - surface->commitPendingState(surface->current); - return; // no new buffer, but we still have current around and a commit happend, commit current again. - } - - if (!surface->pending.buffer && !PENDING_HAS_NEW_BUFFER && !surface->current.buffer) { - surface->commitPendingState(surface->pending); // no pending buffer, no current buffer. probably first commit + if (!surface->pending.updated.buffer || !surface->pending.buffer) { + if (pendingAcquire.timeline() || pendingRelease.timeline()) { + resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer"); + surface->pending.rejected = true; + } return; } - if (pendingAcquire.timeline()) { - surface->pending.buffer->acquire = makeUnique(std::move(pendingAcquire)); - pendingAcquire = {}; - } - - if (pendingRelease.timeline()) { - surface->pending.buffer->release = makeUnique(std::move(pendingRelease)); - pendingRelease = {}; - } - - if (protocolError()) + if (!pendingAcquire.timeline()) { + resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, "Missing acquire timeline"); + surface->pending.rejected = true; return; + } - const auto& state = pendingStates.emplace_back(makeShared(surface->pending)); - surface->pending.damage.clear(); - surface->pending.bufferDamage.clear(); - surface->pending.updated &= ~SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_BUFFER; - surface->pending.updated &= ~SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE; - surface->pending.buffer.reset(); + if (!pendingRelease.timeline()) { + resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT, "Missing release timeline"); + surface->pending.rejected = true; + return; + } - state->buffer->buffer->syncReleaser = state->buffer->release->createSyncRelease(); - state->buffer->acquire->addWaiter([this, surf = surface, wp = CWeakPointer(*std::prev(pendingStates.end()))] { - if (!surf) - return; - - surf->commitPendingState(*wp.lock()); - std::erase(pendingStates, wp); - }); - }); -} - -void CDRMSyncobjSurfaceResource::removeAllWaiters() { - for (auto& s : pendingStates) { - if (s && s->buffer && s->buffer->acquire) - s->buffer->acquire->timeline()->removeAllWaiters(); - } - - pendingStates.clear(); -} - -CDRMSyncobjSurfaceResource::~CDRMSyncobjSurfaceResource() { - removeAllWaiters(); -} - -bool CDRMSyncobjSurfaceResource::protocolError() { - if (!surface->pending.buffer) { - resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer"); - surface->pending.rejected = true; - return true; - } - - if (!surface->pending.buffer->acquire || !surface->pending.buffer->acquire->timeline()) { - resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, "Missing acquire timeline"); - surface->pending.rejected = true; - return true; - } - - if (!surface->pending.buffer->release || !surface->pending.buffer->release->timeline()) { - resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT, "Missing release timeline"); - surface->pending.rejected = true; - return true; - } - - if (surface->pending.buffer->acquire->timeline() == surface->pending.buffer->release->timeline()) { - if (surface->pending.buffer->acquire->point() >= surface->pending.buffer->release->point()) { + if (pendingAcquire.timeline() == pendingRelease.timeline() && pendingAcquire.point() >= pendingRelease.point()) { resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_CONFLICTING_POINTS, "Acquire and release points are on the same timeline, and acquire >= release"); surface->pending.rejected = true; - return true; + return; } - } - return false; + surface->pending.updated.acquire = true; + surface->pending.acquire = pendingAcquire; + pendingAcquire = {}; + + surface->pending.buffer.release = pendingRelease; + pendingRelease = {}; + + surface->pending.buffer->syncReleaser = surface->pending.buffer.release.createSyncRelease(); + }); } bool CDRMSyncobjSurfaceResource::good() { diff --git a/src/protocols/DRMSyncobj.hpp b/src/protocols/DRMSyncobj.hpp index d2259a34..a724258b 100644 --- a/src/protocols/DRMSyncobj.hpp +++ b/src/protocols/DRMSyncobj.hpp @@ -5,14 +5,11 @@ #include "../helpers/sync/SyncReleaser.hpp" #include "linux-drm-syncobj-v1.hpp" #include "../helpers/signal/Signal.hpp" -#include "types/SurfaceState.hpp" #include -#include class CWLSurfaceResource; class CDRMSyncobjTimelineResource; class CSyncTimeline; -struct SSurfaceState; class CDRMSyncPointState { public: @@ -28,6 +25,10 @@ class CDRMSyncPointState { Hyprutils::OS::CFileDescriptor exportAsFD(); void signal(); + operator bool() const { + return m_timeline; + } + private: SP m_timeline = {}; uint64_t m_point = 0; @@ -38,19 +39,15 @@ class CDRMSyncPointState { class CDRMSyncobjSurfaceResource { public: CDRMSyncobjSurfaceResource(UP&& resource_, SP surface_); - ~CDRMSyncobjSurfaceResource(); - bool protocolError(); bool good(); private: - void removeAllWaiters(); WP surface; UP resource; CDRMSyncPointState pendingAcquire; CDRMSyncPointState pendingRelease; - std::vector> pendingStates; struct { CHyprSignalListener surfacePrecommit; diff --git a/src/protocols/Screencopy.cpp b/src/protocols/Screencopy.cpp index 3fd9b9f2..c124e6d2 100644 --- a/src/protocols/Screencopy.cpp +++ b/src/protocols/Screencopy.cpp @@ -14,11 +14,6 @@ #include #include -CScreencopyFrame::~CScreencopyFrame() { - if (buffer && buffer->locked()) - buffer->unlock(); -} - CScreencopyFrame::CScreencopyFrame(SP resource_, int32_t overlay_cursor, wl_resource* output, CBox box_) : resource(resource_) { if UNLIKELY (!good()) return; @@ -102,8 +97,6 @@ void CScreencopyFrame::copy(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer_ return; } - PBUFFER->buffer->lock(); - if UNLIKELY (PBUFFER->buffer->size != box.size()) { LOGM(ERR, "Invalid dimensions in {:x}", (uintptr_t)this); resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions"); @@ -146,7 +139,7 @@ void CScreencopyFrame::copy(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer_ return; } - buffer = PBUFFER->buffer; + buffer = CHLBufferReference(PBUFFER->buffer.lock()); PROTO::screencopy->m_vFramesAwaitingWrite.emplace_back(self); @@ -207,7 +200,7 @@ void CScreencopyFrame::copyDmabuf(std::function callback) { CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; - if (!g_pHyprRenderer->beginRender(pMonitor.lock(), fakeDamage, RENDER_MODE_TO_BUFFER, buffer.lock(), nullptr, true)) { + if (!g_pHyprRenderer->beginRender(pMonitor.lock(), fakeDamage, RENDER_MODE_TO_BUFFER, buffer.buffer, nullptr, true)) { LOGM(ERR, "Can't copy: failed to begin rendering to dma frame"); callback(false); return; diff --git a/src/protocols/Screencopy.hpp b/src/protocols/Screencopy.hpp index 3b46b0b8..99dbcc2b 100644 --- a/src/protocols/Screencopy.hpp +++ b/src/protocols/Screencopy.hpp @@ -1,6 +1,7 @@ #pragma once #include "../defines.hpp" +#include "./types/Buffer.hpp" #include "wlr-screencopy-unstable-v1.hpp" #include "WaylandProtocol.hpp" @@ -50,7 +51,6 @@ class CScreencopyClient { class CScreencopyFrame { public: CScreencopyFrame(SP resource, int32_t overlay_cursor, wl_resource* output, CBox box); - ~CScreencopyFrame(); bool good(); @@ -65,7 +65,7 @@ class CScreencopyFrame { bool withDamage = false; bool lockedSWCursors = false; - WP buffer; + CHLBufferReference buffer; bool bufferDMA = false; uint32_t shmFormat = 0; uint32_t dmabufFormat = 0; diff --git a/src/protocols/ToplevelExport.cpp b/src/protocols/ToplevelExport.cpp index fb0bd9c7..66e74164 100644 --- a/src/protocols/ToplevelExport.cpp +++ b/src/protocols/ToplevelExport.cpp @@ -73,11 +73,6 @@ bool CToplevelExportClient::good() { return resource->resource(); } -CToplevelExportFrame::~CToplevelExportFrame() { - if (buffer && buffer->locked()) - buffer->unlock(); -} - CToplevelExportFrame::CToplevelExportFrame(SP resource_, int32_t overlayCursor_, PHLWINDOW pWindow_) : resource(resource_), pWindow(pWindow_) { if UNLIKELY (!good()) return; @@ -159,8 +154,6 @@ void CToplevelExportFrame::copy(CHyprlandToplevelExportFrameV1* pFrame, wl_resou return; } - PBUFFER->buffer->lock(); - if UNLIKELY (PBUFFER->buffer->size != box.size()) { resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions"); PROTO::toplevelExport->destroyResource(this); @@ -197,7 +190,7 @@ void CToplevelExportFrame::copy(CHyprlandToplevelExportFrameV1* pFrame, wl_resou return; } - buffer = PBUFFER->buffer; + buffer = CHLBufferReference(PBUFFER->buffer.lock()); m_ignoreDamage = ignoreDamage; @@ -340,7 +333,7 @@ bool CToplevelExportFrame::copyDmabuf(timespec* now) { g_pPointerManager->damageCursor(PMONITOR->self.lock()); } - if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_TO_BUFFER, buffer.lock())) + if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_TO_BUFFER, buffer.buffer)) return false; g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 1.0)); diff --git a/src/protocols/ToplevelExport.hpp b/src/protocols/ToplevelExport.hpp index 8d734dbd..21aafde5 100644 --- a/src/protocols/ToplevelExport.hpp +++ b/src/protocols/ToplevelExport.hpp @@ -40,7 +40,6 @@ class CToplevelExportClient { class CToplevelExportFrame { public: CToplevelExportFrame(SP resource_, int32_t overlayCursor, PHLWINDOW pWindow); - ~CToplevelExportFrame(); bool good(); @@ -55,7 +54,7 @@ class CToplevelExportFrame { bool m_ignoreDamage = false; bool lockedSWCursors = false; - WP buffer; + CHLBufferReference buffer; bool bufferDMA = false; uint32_t shmFormat = 0; uint32_t dmabufFormat = 0; diff --git a/src/protocols/Viewporter.cpp b/src/protocols/Viewporter.cpp index d14e7ee5..674b8373 100644 --- a/src/protocols/Viewporter.cpp +++ b/src/protocols/Viewporter.cpp @@ -15,6 +15,8 @@ CViewportResource::CViewportResource(SP resource_, SPpending.updated.viewport = true; + if (x == -1 && y == -1) { surface->pending.viewport.hasDestination = false; return; @@ -35,6 +37,8 @@ CViewportResource::CViewportResource(SP resource_, SPpending.updated.viewport = true; + double x = wl_fixed_to_double(fx), y = wl_fixed_to_double(fy), w = wl_fixed_to_double(fw), h = wl_fixed_to_double(fh); if (x == -1 && y == -1 && w == -1 && h == -1) { diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index cb1aa69a..57f0ff3f 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -71,23 +71,32 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : resource(reso resource->setOnDestroy([this](CWlSurface* r) { destroy(); }); resource->setAttach([this](CWlSurface* r, wl_resource* buffer, int32_t x, int32_t y) { - pending.offset = {x, y}; - pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_BUFFER | SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_OFFSET; + pending.updated.buffer = true; + pending.updated.offset = true; - if (!buffer) { - pending.buffer.reset(); - pending.texture.reset(); - pending.bufferSize = Vector2D{}; + pending.offset = {x, y}; + + if (pending.buffer) + pending.buffer.drop(); + + auto buf = buffer ? CWLBufferResource::fromResource(buffer) : nullptr; + + if (buf && buf->buffer) { + pending.buffer = CHLBufferReference(buf->buffer.lock()); + pending.texture = buf->buffer->texture; + pending.size = buf->buffer->size; + pending.bufferSize = buf->buffer->size; } else { - 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{}; + pending.buffer = {}; + pending.texture.reset(); + pending.size = Vector2D{}; + pending.bufferSize = Vector2D{}; } - if (pending.bufferSize != current.bufferSize) - pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}}; + if (pending.bufferSize != current.bufferSize) { + pending.updated.damage = true; + pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}}; + } }); resource->setCommit([this](CWlSurface* r) { @@ -109,34 +118,78 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : resource(reso events.precommit.emit(); if (pending.rejected) { + pending.rejected = false; dropPendingBuffer(); return; } - if (!syncobj) - commitPendingState(pending); + if ((!pending.updated.buffer) || // no new buffer attached + (!pending.buffer && !pending.texture) || // null buffer attached + (!pending.updated.acquire && pending.buffer->isSynchronous()) // synchronous buffers (ex. shm) can be read immediately + ) { + commitState(pending); + pending.reset(); + return; + } + + // save state while we wait for buffer to become ready + const auto& state = pendingStates.emplace(makeUnique(pending)); + pending.reset(); + + auto whenReadable = [this, surf = self, state = WP(pendingStates.back())] { + if (!surf || state.expired()) + return; + + while (!pendingStates.empty() && pendingStates.front() != state) { + commitState(*pendingStates.front()); + pendingStates.pop(); + } + + commitState(*pendingStates.front()); + pendingStates.pop(); + }; + + if (state->updated.acquire) { + // wait on acquire point for this surface, from explicit sync protocol + state->acquire.addWaiter(whenReadable); + } else if (state->buffer->dmabuf().success) { + // https://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#implicit-fence-poll-support + // TODO: wait for the dma-buf fd's to become readable + whenReadable(); + } else { + // huh??? only buffers with acquire or dmabuf should get through here... + Debug::log(ERR, "BUG THIS: wl_surface.commit: non-acquire non-dmabuf buffers needs wait..."); + whenReadable(); + } }); resource->setDamage([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { - pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE; + pending.updated.damage = true; pending.damage.add(CBox{x, y, w, h}); }); resource->setDamageBuffer([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { - pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE; + pending.updated.damage = true; pending.bufferDamage.add(CBox{x, y, w, h}); }); resource->setSetBufferScale([this](CWlSurface* r, int32_t scale) { if (scale == pending.scale) return; - pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_SCALE | SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE; + + pending.updated.scale = true; + pending.updated.damage = true; + pending.scale = scale; pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}}; }); + resource->setSetBufferTransform([this](CWlSurface* r, uint32_t tr) { if (tr == pending.transform) return; - pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_TRANSFORM | SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE; + + pending.updated.transform = true; + pending.updated.damage = true; + pending.transform = (wl_output_transform)tr; pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}}; }); @@ -147,7 +200,7 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : resource(reso return; } - pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_INPUT; + pending.updated.input = true; auto RG = CWLRegionResource::fromResource(region); pending.input = RG->region; @@ -159,7 +212,7 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : resource(reso return; } - pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_OPAQUE; + pending.updated.opaque = true; auto RG = CWLRegionResource::fromResource(region); pending.opaque = RG->region; @@ -168,8 +221,8 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : resource(reso resource->setFrame([this](CWlSurface* r, uint32_t id) { callbacks.emplace_back(makeShared(makeShared(pClient, 1, id))); }); resource->setOffset([this](CWlSurface* r, int32_t x, int32_t y) { - pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_OFFSET; - pending.offset = {x, y}; + pending.updated.offset = true; + pending.offset = {x, y}; }); } @@ -188,11 +241,11 @@ void CWLSurfaceResource::destroy() { } void CWLSurfaceResource::dropPendingBuffer() { - pending.buffer.reset(); + pending.buffer = {}; } void CWLSurfaceResource::dropCurrentBuffer() { - current.buffer.reset(); + current.buffer = {}; } SP CWLSurfaceResource::fromResource(wl_resource* res) { @@ -279,7 +332,6 @@ void CWLSurfaceResource::resetRole() { } void CWLSurfaceResource::bfHelper(std::vector> const& nodes, std::function, const Vector2D&, void*)> fn, void* data) { - std::vector> nodes2; nodes2.reserve(nodes.size() * 2); @@ -424,13 +476,12 @@ CBox CWLSurfaceResource::extends() { return full.getExtents(); } -void CWLSurfaceResource::commitPendingState(SSurfaceState& state) { +void CWLSurfaceResource::commitState(SSurfaceState& state) { auto lastTexture = current.texture; current.updateFrom(state); - state.updated = 0; if (current.buffer) { - if (current.buffer->buffer->isSynchronous()) + if (current.buffer->isSynchronous()) current.updateSynchronousTexture(lastTexture); // if the surface is a cursor, update the shm buffer @@ -465,7 +516,7 @@ void CWLSurfaceResource::commitPendingState(SSurfaceState& state) { // release the buffer if it's synchronous (SHM) as update() has done everything thats needed // so we can let the app know we're done. // if it doesn't have a role, we can't release it yet, in case it gets turned into a cursor. - if (current.buffer && current.buffer->buffer && current.buffer->buffer->isSynchronous() && role->role() != SURFACE_ROLE_UNASSIGNED) + if (current.buffer && current.buffer->isSynchronous() && role->role() != SURFACE_ROLE_UNASSIGNED) dropCurrentBuffer(); } @@ -473,7 +524,7 @@ void CWLSurfaceResource::updateCursorShm(CRegion damage) { if (damage.empty()) return; - auto buf = current.buffer ? current.buffer->buffer : SP{}; + auto buf = current.buffer ? current.buffer : SP{}; if UNLIKELY (!buf) return; diff --git a/src/protocols/core/Compositor.hpp b/src/protocols/core/Compositor.hpp index c732f797..ccfcf0cc 100644 --- a/src/protocols/core/Compositor.hpp +++ b/src/protocols/core/Compositor.hpp @@ -9,6 +9,7 @@ */ #include +#include #include #include "../WaylandProtocol.hpp" #include "../../render/Texture.hpp" @@ -87,6 +88,7 @@ class CWLSurfaceResource { } events; SSurfaceState current, pending; + std::queue> pendingStates; std::vector> callbacks; WP self; @@ -103,7 +105,7 @@ class CWLSurfaceResource { void breadthfirst(std::function, const Vector2D&, void*)> fn, void* data); SP findFirstPreorder(std::function)> fn); void presentFeedback(timespec* when, PHLMONITOR pMonitor, bool discarded = false); - void commitPendingState(SSurfaceState& state); + void commitState(SSurfaceState& state); // returns a pair: found surface (null if not found) and surface local coords. // localCoords param is relative to 0,0 of this surface diff --git a/src/protocols/types/Buffer.cpp b/src/protocols/types/Buffer.cpp index 2623435b..22745c06 100644 --- a/src/protocols/types/Buffer.cpp +++ b/src/protocols/types/Buffer.cpp @@ -40,13 +40,61 @@ void IHLBuffer::onBackendRelease(const std::function& fn) { }); } -CHLBufferReference::CHLBufferReference(SP buffer_, SP surface_) : buffer(buffer_), surface(surface_) { - buffer->lock(); +CHLBufferReference::CHLBufferReference() : buffer(nullptr) { + ; +} + +CHLBufferReference::CHLBufferReference(const CHLBufferReference& other) : release(other.release), buffer(other.buffer) { + if (buffer) + buffer->lock(); +} + +CHLBufferReference::CHLBufferReference(SP buffer_) : buffer(buffer_) { + if (buffer) + buffer->lock(); } CHLBufferReference::~CHLBufferReference() { + if (buffer) + buffer->unlock(); +} + +CHLBufferReference& CHLBufferReference::operator=(const CHLBufferReference& other) { + if (other.buffer) + other.buffer->lock(); + if (buffer) + buffer->unlock(); + buffer = other.buffer; + release = other.release; + return *this; +} + +bool CHLBufferReference::operator==(const CHLBufferReference& other) const { + return buffer == other.buffer; +} + +bool CHLBufferReference::operator==(const SP& other) const { + return buffer == other; +} + +bool CHLBufferReference::operator==(const SP& other) const { + return buffer == other; +} + +SP CHLBufferReference::operator->() const { + return buffer; +} + +CHLBufferReference::operator bool() const { + return buffer; +} + +void CHLBufferReference::drop() { if (!buffer) return; - buffer->unlock(); + buffer->nLocks--; + ASSERT(buffer->nLocks >= 0); + + buffer = nullptr; } diff --git a/src/protocols/types/Buffer.hpp b/src/protocols/types/Buffer.hpp index a0b4e9a8..1bf7583c 100644 --- a/src/protocols/types/Buffer.hpp +++ b/src/protocols/types/Buffer.hpp @@ -8,6 +8,7 @@ #include class CSyncReleaser; +class CHLBufferReference; class IHLBuffer : public Aquamarine::IBuffer { public: @@ -36,19 +37,28 @@ class IHLBuffer : public Aquamarine::IBuffer { private: int nLocks = 0; + + friend class CHLBufferReference; }; // for ref-counting. Releases in ~dtor -// surface optional class CHLBufferReference { public: - CHLBufferReference(SP buffer, SP surface); + CHLBufferReference(); + CHLBufferReference(const CHLBufferReference& other); + CHLBufferReference(SP buffer); ~CHLBufferReference(); - SP buffer; - UP acquire; - UP release; + CHLBufferReference& operator=(const CHLBufferReference& other); + bool operator==(const CHLBufferReference& other) const; + bool operator==(const SP& other) const; + bool operator==(const SP& other) const; + SP operator->() const; + operator bool() const; - private: - WP surface; + // unlock and drop the buffer without sending release + void drop(); + + CDRMSyncPointState release; + SP buffer; }; diff --git a/src/protocols/types/SurfaceState.cpp b/src/protocols/types/SurfaceState.cpp index b57fef81..7d1f5702 100644 --- a/src/protocols/types/SurfaceState.cpp +++ b/src/protocols/types/SurfaceState.cpp @@ -35,7 +35,7 @@ CRegion SSurfaceState::accumulateBufferDamage() { } void SSurfaceState::updateSynchronousTexture(SP lastTexture) { - auto [dataPtr, fmt, size] = buffer->buffer->beginDataPtr(0); + auto [dataPtr, fmt, size] = buffer->beginDataPtr(0); if (dataPtr) { auto drmFmt = NFormatUtils::shmToDRM(fmt); auto stride = bufferSize.y ? size / bufferSize.y : 0; @@ -45,49 +45,56 @@ void SSurfaceState::updateSynchronousTexture(SP lastTexture) { } else texture = makeShared(drmFmt, dataPtr, stride, bufferSize); } - buffer->buffer->endDataPtr(); + buffer->endDataPtr(); } void SSurfaceState::reset() { + updated.all = false; + + // After commit, there is no pending buffer until the next attach. + buffer = {}; + + // applies only to the buffer that is attached to the surface + acquire = {}; + + // wl_surface.commit assings pending ... and clears pending damage. damage.clear(); bufferDamage.clear(); - transform = WL_OUTPUT_TRANSFORM_NORMAL; - scale = 1; - offset = {}; - size = {}; } void SSurfaceState::updateFrom(SSurfaceState& ref) { updated = ref.updated; - if (ref.updated & SURFACE_UPDATED_BUFFER) { - ref.updated &= ~SURFACE_UPDATED_BUFFER; - *this = ref; - ref.damage.clear(); - ref.bufferDamage.clear(); - ref.buffer.reset(); - } else { - if (ref.updated & SURFACE_UPDATED_DAMAGE) { - damage = ref.damage; - bufferDamage = ref.bufferDamage; - } - - if (ref.updated & SURFACE_UPDATED_INPUT) - input = ref.input; - - if (ref.updated & SURFACE_UPDATED_OPAQUE) - opaque = ref.opaque; - - if (ref.updated & SURFACE_UPDATED_OFFSET) - offset = ref.offset; - - if (ref.updated & SURFACE_UPDATED_SCALE) - scale = ref.scale; - - if (ref.updated & SURFACE_UPDATED_VIEWPORT) - viewport = ref.viewport; - - if (ref.updated & SURFACE_UPDATED_TRANSFORM) - transform = ref.transform; + if (ref.updated.buffer) { + buffer = ref.buffer; + texture = ref.texture; + size = ref.size; + bufferSize = ref.bufferSize; } + + if (ref.updated.damage) { + damage = ref.damage; + bufferDamage = ref.bufferDamage; + } + + if (ref.updated.input) + input = ref.input; + + if (ref.updated.opaque) + opaque = ref.opaque; + + if (ref.updated.offset) + offset = ref.offset; + + if (ref.updated.scale) + scale = ref.scale; + + if (ref.updated.transform) + transform = ref.transform; + + if (ref.updated.viewport) + viewport = ref.viewport; + + if (ref.updated.acquire) + acquire = ref.acquire; } diff --git a/src/protocols/types/SurfaceState.hpp b/src/protocols/types/SurfaceState.hpp index 4c995bb0..5a1b2b68 100644 --- a/src/protocols/types/SurfaceState.hpp +++ b/src/protocols/types/SurfaceState.hpp @@ -2,44 +2,59 @@ #include "../../helpers/math/Math.hpp" #include "../WaylandProtocol.hpp" +#include "./Buffer.hpp" -class CHLBufferReference; class CTexture; +class CDRMSyncPointState; struct SSurfaceState { - enum eUpdatedProperties : uint8_t { - SURFACE_UPDATED_OPAQUE = 1 << 0, - SURFACE_UPDATED_INPUT = 1 << 1, - SURFACE_UPDATED_DAMAGE = 1 << 2, - SURFACE_UPDATED_SCALE = 1 << 3, - SURFACE_UPDATED_BUFFER = 1 << 4, - SURFACE_UPDATED_OFFSET = 1 << 5, - SURFACE_UPDATED_VIEWPORT = 1 << 6, - SURFACE_UPDATED_TRANSFORM = 1 << 7, - }; + union { + uint16_t all = 0; + struct { + bool buffer : 1; + bool damage : 1; + bool opaque : 1; + bool input : 1; + bool transform : 1; + bool scale : 1; + bool offset : 1; + bool viewport : 1; + bool acquire : 1; + }; + } updated; - 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; + bool rejected = false; + + // initial values, copied from protocol text + CHLBufferReference buffer = {}; // The initial surface contents are void + CRegion damage, bufferDamage; // The initial value for pending damage is empty + CRegion opaque; // The initial value for an opaque region is empty + CRegion input = CBox{{}, {INT32_MAX, INT32_MAX}}; // The initial value for an input region is infinite + wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; // A newly created surface has its buffer transformation set to normal + int scale = 1; // A newly created surface has its buffer scale set to 1 + + // these don't have well defined initial values in the protocol, but these work + Vector2D size, bufferSize; + Vector2D offset; + + // viewporter protocol surface state struct { bool hasDestination = false; bool hasSource = false; Vector2D destination; CBox source; } viewport; - bool rejected = false; - uint8_t updated = 0; // eUpdatedProperties. Stores what the last update changed - Vector2D sourceSize(); - // Translates damage into bufferDamage, clearing damage and returning the updated bufferDamage - CRegion accumulateBufferDamage(); - void updateSynchronousTexture(SP lastTexture); - void reset(); - // updates this state from a reference state. Mutates the reference state. If a new buffer is committed, - // reference state gets its damage and buffer cleared. - void updateFrom(SSurfaceState& ref); + + // drm syncobj protocol surface state + CDRMSyncPointState acquire; + + // texture of surface content, used for rendering + SP texture; + void updateSynchronousTexture(SP lastTexture); + + // helpers + CRegion accumulateBufferDamage(); // transforms state.damage and merges it into state.bufferDamage + void updateFrom(SSurfaceState& ref); // updates this state based on a reference state. + void reset(); // resets pending state after commit }; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index dca76f63..dc5c1284 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1565,10 +1565,10 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { Debug::log(TRACE, "Explicit: can't add sync, monitor has no EGLSync"); else { for (auto const& e : explicitPresented) { - if (!e->current.buffer || !e->current.buffer->buffer->syncReleaser) + if (!e->current.buffer || !e->current.buffer->syncReleaser) continue; - e->current.buffer->buffer->syncReleaser->addReleaseSync(pMonitor->eglSync); + e->current.buffer->syncReleaser->addReleaseSync(pMonitor->eglSync); } }