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:
parent
70ae99f521
commit
da86db43d4
15 changed files with 285 additions and 223 deletions
|
|
@ -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)
|
||||
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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue