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
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 285 additions and 223 deletions

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_) {
buffer->lock();
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) {
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;
}

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