diff options
| author | Ikalco <73481042+ikalco@users.noreply.github.com> | 2025-04-07 14:03:27 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-04-07 21:03:27 +0200 |
| commit | da86db43d47141b9d7332d3cb756ff7c05dbb39a (patch) | |
| tree | 7b187bc96aedceda108b51eff034ba75e33fb510 /src/protocols/core/Compositor.cpp | |
| parent | 70ae99f52162a0ed2723d45c63adfb2e61f4e2f5 (diff) | |
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
Diffstat (limited to 'src/protocols/core/Compositor.cpp')
| -rw-r--r-- | src/protocols/core/Compositor.cpp | 109 |
1 files changed, 80 insertions, 29 deletions
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<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.updated.buffer = true; + pending.updated.offset = true; + pending.offset = {x, y}; - pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_BUFFER | SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_OFFSET; - if (!buffer) { - pending.buffer.reset(); + 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 { + pending.buffer = {}; pending.texture.reset(); + pending.size = Vector2D{}; pending.bufferSize = Vector2D{}; - } 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{}; } - 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<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,8 +221,8 @@ 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.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> 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; |
