core: refactor and improve surface commit (#9805)

* make CHLBufferReference not a SP anymore

* copy over release and acquire points in CHLBufferReference

* use CHLBufferReference in screencopy and toplevel export

TODO: use CHLBufferReference in direct scanout properly
      the only problem is the scanout buffer release timing,
      specifically the onBackendRelease mechanism

* cleanup SSurfaceState and surface pending commit tracking

* move surface code from DRMSyncobj, and move acquire to SSurfaceState

* use queue for comitted pending surface states like proto says

"The content update is placed in a queue until it becomes active." - wl_surface::commit

* drop, not release, prev buffer if 2nd buffer wl_surface.attach is sent

"A wl_buffer that has been attached and then replaced by another attach instead of committed will not receive a release event, and is not used by the compositor." - wl_surface::attach
This commit is contained in:
Ikalco
2025-04-07 14:03:27 -05:00
committed by GitHub
parent 70ae99f521
commit da86db43d4
15 changed files with 285 additions and 223 deletions

View File

@@ -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());

View File

@@ -75,98 +75,41 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(UP<CWpLinuxDrmSyncobjSurf
});
listeners.surfacePrecommit = surface->events.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
return;
}
if (pendingAcquire.timeline()) {
surface->pending.buffer->acquire = makeUnique<CDRMSyncPointState>(std::move(pendingAcquire));
pendingAcquire = {};
}
if (pendingRelease.timeline()) {
surface->pending.buffer->release = makeUnique<CDRMSyncPointState>(std::move(pendingRelease));
pendingRelease = {};
}
if (protocolError())
return;
const auto& state = pendingStates.emplace_back(makeShared<SSurfaceState>(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();
state->buffer->buffer->syncReleaser = state->buffer->release->createSyncRelease();
state->buffer->acquire->addWaiter([this, surf = surface, wp = CWeakPointer<SSurfaceState>(*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) {
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 true;
}
return;
}
if (!surface->pending.buffer->acquire || !surface->pending.buffer->acquire->timeline()) {
if (!pendingAcquire.timeline()) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, "Missing acquire timeline");
surface->pending.rejected = true;
return true;
return;
}
if (!surface->pending.buffer->release || !surface->pending.buffer->release->timeline()) {
if (!pendingRelease.timeline()) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT, "Missing release timeline");
surface->pending.rejected = true;
return true;
return;
}
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() {

View File

@@ -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 <hyprutils/os/FileDescriptor.hpp>
#include <list>
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<CSyncTimeline> m_timeline = {};
uint64_t m_point = 0;
@@ -38,19 +39,15 @@ class CDRMSyncPointState {
class CDRMSyncobjSurfaceResource {
public:
CDRMSyncobjSurfaceResource(UP<CWpLinuxDrmSyncobjSurfaceV1>&& resource_, SP<CWLSurfaceResource> surface_);
~CDRMSyncobjSurfaceResource();
bool protocolError();
bool good();
private:
void removeAllWaiters();
WP<CWLSurfaceResource> surface;
UP<CWpLinuxDrmSyncobjSurfaceV1> resource;
CDRMSyncPointState pendingAcquire;
CDRMSyncPointState pendingRelease;
std::vector<SP<SSurfaceState>> pendingStates;
struct {
CHyprSignalListener surfacePrecommit;

View File

@@ -14,11 +14,6 @@
#include <algorithm>
#include <functional>
CScreencopyFrame::~CScreencopyFrame() {
if (buffer && buffer->locked())
buffer->unlock();
}
CScreencopyFrame::CScreencopyFrame(SP<CZwlrScreencopyFrameV1> 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<void(bool)> 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;

View File

@@ -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<CZwlrScreencopyFrameV1> 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<IHLBuffer> buffer;
CHLBufferReference buffer;
bool bufferDMA = false;
uint32_t shmFormat = 0;
uint32_t dmabufFormat = 0;

View File

@@ -73,11 +73,6 @@ bool CToplevelExportClient::good() {
return resource->resource();
}
CToplevelExportFrame::~CToplevelExportFrame() {
if (buffer && buffer->locked())
buffer->unlock();
}
CToplevelExportFrame::CToplevelExportFrame(SP<CHyprlandToplevelExportFrameV1> 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));

View File

@@ -40,7 +40,6 @@ class CToplevelExportClient {
class CToplevelExportFrame {
public:
CToplevelExportFrame(SP<CHyprlandToplevelExportFrameV1> resource_, int32_t overlayCursor, PHLWINDOW pWindow);
~CToplevelExportFrame();
bool good();
@@ -55,7 +54,7 @@ class CToplevelExportFrame {
bool m_ignoreDamage = false;
bool lockedSWCursors = false;
WP<IHLBuffer> buffer;
CHLBufferReference buffer;
bool bufferDMA = false;
uint32_t shmFormat = 0;
uint32_t dmabufFormat = 0;

View File

@@ -15,6 +15,8 @@ CViewportResource::CViewportResource(SP<CWpViewport> resource_, SP<CWLSurfaceRes
return;
}
surface->pending.updated.viewport = true;
if (x == -1 && y == -1) {
surface->pending.viewport.hasDestination = false;
return;
@@ -35,6 +37,8 @@ CViewportResource::CViewportResource(SP<CWpViewport> resource_, SP<CWLSurfaceRes
return;
}
surface->pending.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) {

View File

@@ -71,23 +71,32 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> 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<CHLBufferReference>(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)
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<CWlSurface> 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<SSurfaceState>(pending));
pending.reset();
auto whenReadable = [this, surf = self, state = WP<SSurfaceState>(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<CWlSurface> 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<CWlSurface> 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,7 +221,7 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
resource->setFrame([this](CWlSurface* r, uint32_t id) { callbacks.emplace_back(makeShared<CWLCallbackResource>(makeShared<CWlCallback>(pClient, 1, id))); });
resource->setOffset([this](CWlSurface* r, int32_t x, int32_t y) {
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_OFFSET;
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> CWLSurfaceResource::fromResource(wl_resource* res) {
@@ -279,7 +332,6 @@ void CWLSurfaceResource::resetRole() {
}
void CWLSurfaceResource::bfHelper(std::vector<SP<CWLSurfaceResource>> const& nodes, std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data) {
std::vector<SP<CWLSurfaceResource>> 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<IHLBuffer>{};
auto buf = current.buffer ? current.buffer : SP<IHLBuffer>{};
if UNLIKELY (!buf)
return;

View File

@@ -9,6 +9,7 @@
*/
#include <vector>
#include <queue>
#include <cstdint>
#include "../WaylandProtocol.hpp"
#include "../../render/Texture.hpp"
@@ -87,6 +88,7 @@ class CWLSurfaceResource {
} events;
SSurfaceState current, pending;
std::queue<UP<SSurfaceState>> pendingStates;
std::vector<SP<CWLCallbackResource>> callbacks;
WP<CWLSurfaceResource> self;
@@ -103,7 +105,7 @@ class CWLSurfaceResource {
void breadthfirst(std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data);
SP<CWLSurfaceResource> findFirstPreorder(std::function<bool(SP<CWLSurfaceResource>)> 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

View File

@@ -40,13 +40,61 @@ void IHLBuffer::onBackendRelease(const std::function<void()>& fn) {
});
}
CHLBufferReference::CHLBufferReference(SP<IHLBuffer> buffer_, SP<CWLSurfaceResource> surface_) : buffer(buffer_), surface(surface_) {
CHLBufferReference::CHLBufferReference() : buffer(nullptr) {
;
}
CHLBufferReference::CHLBufferReference(const CHLBufferReference& other) : release(other.release), buffer(other.buffer) {
if (buffer)
buffer->lock();
}
CHLBufferReference::CHLBufferReference(SP<IHLBuffer> 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<IHLBuffer>& other) const {
return buffer == other;
}
bool CHLBufferReference::operator==(const SP<Aquamarine::IBuffer>& other) const {
return buffer == other;
}
SP<IHLBuffer> 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;
}

View File

@@ -8,6 +8,7 @@
#include <aquamarine/buffer/Buffer.hpp>
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<IHLBuffer> buffer, SP<CWLSurfaceResource> surface);
CHLBufferReference();
CHLBufferReference(const CHLBufferReference& other);
CHLBufferReference(SP<IHLBuffer> buffer);
~CHLBufferReference();
SP<IHLBuffer> buffer;
UP<CDRMSyncPointState> acquire;
UP<CDRMSyncPointState> release;
CHLBufferReference& operator=(const CHLBufferReference& other);
bool operator==(const CHLBufferReference& other) const;
bool operator==(const SP<IHLBuffer>& other) const;
bool operator==(const SP<Aquamarine::IBuffer>& other) const;
SP<IHLBuffer> operator->() const;
operator bool() const;
private:
WP<CWLSurfaceResource> surface;
// unlock and drop the buffer without sending release
void drop();
CDRMSyncPointState release;
SP<IHLBuffer> buffer;
};

View File

@@ -35,7 +35,7 @@ CRegion SSurfaceState::accumulateBufferDamage() {
}
void SSurfaceState::updateSynchronousTexture(SP<CTexture> 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<CTexture> lastTexture) {
} else
texture = makeShared<CTexture>(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) {
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 & SURFACE_UPDATED_INPUT)
if (ref.updated.input)
input = ref.input;
if (ref.updated & SURFACE_UPDATED_OPAQUE)
if (ref.updated.opaque)
opaque = ref.opaque;
if (ref.updated & SURFACE_UPDATED_OFFSET)
if (ref.updated.offset)
offset = ref.offset;
if (ref.updated & SURFACE_UPDATED_SCALE)
if (ref.updated.scale)
scale = ref.scale;
if (ref.updated & SURFACE_UPDATED_VIEWPORT)
if (ref.updated.transform)
transform = ref.transform;
if (ref.updated.viewport)
viewport = ref.viewport;
if (ref.updated & SURFACE_UPDATED_TRANSFORM)
transform = ref.transform;
}
if (ref.updated.acquire)
acquire = ref.acquire;
}

View File

@@ -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<CHLBufferReference> 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<CTexture> texture;
Vector2D offset;
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();
// drm syncobj protocol surface state
CDRMSyncPointState acquire;
// texture of surface content, used for rendering
SP<CTexture> texture;
void updateSynchronousTexture(SP<CTexture> 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);
// 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
};

View File

@@ -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);
}
}