core/compositor: Correctly track SHM buffer damage (#9678)

This commit is contained in:
Lee Bousfield 2025-03-22 11:13:44 -05:00 committed by GitHub
parent 4600043a49
commit 6384f4acf4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 103 additions and 82 deletions

View File

@ -98,7 +98,7 @@ CRegion CWLSurface::computeDamage() const {
if (!m_pResource->current.texture) if (!m_pResource->current.texture)
return {}; return {};
CRegion damage = m_pResource->accumulateCurrentBufferDamage(); CRegion damage = m_pResource->current.accumulateBufferDamage();
damage.transform(wlTransformToHyprutils(m_pResource->current.transform), m_pResource->current.bufferSize.x, m_pResource->current.bufferSize.y); damage.transform(wlTransformToHyprutils(m_pResource->current.transform), m_pResource->current.bufferSize.x, m_pResource->current.bufferSize.y);
const auto BUFSIZE = m_pResource->current.bufferSize; const auto BUFSIZE = m_pResource->current.bufferSize;

View File

@ -880,7 +880,7 @@ void Events::listener_commitWindow(void* owner, void* data) {
// tearing: if solitary, redraw it. This still might be a single surface window // tearing: if solitary, redraw it. This still might be a single surface window
if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.texture) { if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.texture) {
CRegion damageBox{PWINDOW->m_pWLSurface->resource()->accumulateCurrentBufferDamage()}; CRegion damageBox{PWINDOW->m_pWLSurface->resource()->current.accumulateBufferDamage()};
if (!damageBox.empty()) { if (!damageBox.empty()) {
if (PMONITOR->tearingState.busy) { if (PMONITOR->tearingState.busy) {

View File

@ -1400,7 +1400,7 @@ bool CMonitor::attemptDirectScanout() {
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
PSURFACE->presentFeedback(&now, self.lock()); PSURFACE->presentFeedback(&now, self.lock());
output->state->addDamage(PSURFACE->accumulateCurrentBufferDamage()); output->state->addDamage(PSURFACE->current.accumulateBufferDamage());
output->state->resetExplicitFences(); output->state->resetExplicitFences();
auto cleanup = CScopeGuard([this]() { output->state->resetExplicitFences(); }); auto cleanup = CScopeGuard([this]() { output->state->resetExplicitFences(); });

View File

@ -137,7 +137,7 @@ CDRMSyncobjSurfaceResource::~CDRMSyncobjSurfaceResource() {
} }
bool CDRMSyncobjSurfaceResource::protocolError() { bool CDRMSyncobjSurfaceResource::protocolError() {
if (!surface->pending.texture) { if (!surface->pending.buffer) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer"); resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer");
surface->pending.rejected = true; surface->pending.rejected = true;
return true; return true;

View File

@ -52,7 +52,7 @@ CViewportResource::CViewportResource(SP<CWpViewport> resource_, SP<CWLSurfaceRes
}); });
listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) { listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) {
if (!surface || !surface->pending.texture) if (!surface || !surface->pending.buffer)
return; return;
if (surface->pending.viewport.hasSource) { if (surface->pending.viewport.hasSource) {

View File

@ -387,7 +387,7 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBas
if (toplevel) if (toplevel)
toplevel->current = toplevel->pending; toplevel->current = toplevel->pending;
if UNLIKELY (initialCommit && surface->pending.texture) { if UNLIKELY (initialCommit && surface->pending.buffer) {
resource->error(-1, "Buffer attached before initial commit"); resource->error(-1, "Buffer attached before initial commit");
return; return;
} }

View File

@ -12,6 +12,7 @@
#include "../DRMSyncobj.hpp" #include "../DRMSyncobj.hpp"
#include "../../render/Renderer.hpp" #include "../../render/Renderer.hpp"
#include "config/ConfigValue.hpp" #include "config/ConfigValue.hpp"
#include "render/Texture.hpp"
#include <cstring> #include <cstring>
class CDefaultSurfaceRole : public ISurfaceRole { class CDefaultSurfaceRole : public ISurfaceRole {
@ -91,10 +92,10 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
}); });
resource->setCommit([this](CWlSurface* r) { resource->setCommit([this](CWlSurface* r) {
if (pending.texture) if (pending.buffer)
pending.bufferDamage.intersect(CBox{{}, pending.bufferSize}); pending.bufferDamage.intersect(CBox{{}, pending.bufferSize});
if (!pending.texture) if (!pending.buffer)
pending.size = {}; pending.size = {};
else if (pending.viewport.hasDestination) else if (pending.viewport.hasDestination)
pending.size = pending.viewport.destination; pending.size = pending.viewport.destination;
@ -399,36 +400,8 @@ CBox CWLSurfaceResource::extends() {
return full.getExtents(); return full.getExtents();
} }
Vector2D CWLSurfaceResource::sourceSize() {
if UNLIKELY (!current.texture)
return {};
if UNLIKELY (current.viewport.hasSource)
return current.viewport.source.size();
Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.bufferSize.y, current.bufferSize.x} : current.bufferSize;
return trc / current.scale;
}
CRegion CWLSurfaceResource::accumulateCurrentBufferDamage() {
if UNLIKELY (!current.texture)
return {};
CRegion surfaceDamage = current.damage;
if (current.viewport.hasDestination) {
Vector2D scale = sourceSize() / current.viewport.destination;
surfaceDamage.scale(scale);
}
if (current.viewport.hasSource)
surfaceDamage.translate(current.viewport.source.pos());
Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.bufferSize.y, current.bufferSize.x} : current.bufferSize;
return surfaceDamage.scale(current.scale).transform(wlTransformToHyprutils(invertTransform(current.transform)), trc.x, trc.y).add(current.bufferDamage);
}
void CWLSurfaceResource::commitPendingState(SSurfaceState& state) { void CWLSurfaceResource::commitPendingState(SSurfaceState& state) {
auto lastTexture = current.texture;
if (state.newBuffer) { if (state.newBuffer) {
state.newBuffer = false; state.newBuffer = false;
current = state; current = state;
@ -437,20 +410,19 @@ void CWLSurfaceResource::commitPendingState(SSurfaceState& state) {
state.buffer.reset(); state.buffer.reset();
} }
if (current.texture)
current.texture->m_eTransform = wlTransformToHyprutils(current.transform);
if (current.buffer) { if (current.buffer) {
const auto DAMAGE = accumulateCurrentBufferDamage(); if (current.buffer->buffer->isSynchronous())
current.buffer->buffer->update(DAMAGE); current.updateSynchronousTexture(lastTexture);
// if the surface is a cursor, update the shm buffer // if the surface is a cursor, update the shm buffer
// TODO: don't update the entire texture // TODO: don't update the entire texture
if (role->role() == SURFACE_ROLE_CURSOR && !DAMAGE.empty()) if (role->role() == SURFACE_ROLE_CURSOR)
updateCursorShm(DAMAGE); updateCursorShm(current.accumulateBufferDamage());
} }
// TODO: we should _accumulate_ and not replace above if sync if (current.texture)
current.texture->m_eTransform = wlTransformToHyprutils(current.transform);
if (role->role() == SURFACE_ROLE_SUBSURFACE) { if (role->role() == SURFACE_ROLE_SUBSURFACE) {
auto subsurface = ((CSubsurfaceRole*)role.get())->subsurface.lock(); auto subsurface = ((CSubsurfaceRole*)role.get())->subsurface.lock();
if (subsurface->sync) if (subsurface->sync)
@ -478,6 +450,9 @@ void CWLSurfaceResource::commitPendingState(SSurfaceState& state) {
} }
void CWLSurfaceResource::updateCursorShm(CRegion damage) { void CWLSurfaceResource::updateCursorShm(CRegion damage) {
if (damage.empty())
return;
auto buf = current.buffer ? current.buffer->buffer : SP<IHLBuffer>{}; auto buf = current.buffer ? current.buffer->buffer : SP<IHLBuffer>{};
if UNLIKELY (!buf) if UNLIKELY (!buf)

View File

@ -11,6 +11,7 @@
#include <vector> #include <vector>
#include <cstdint> #include <cstdint>
#include "../WaylandProtocol.hpp" #include "../WaylandProtocol.hpp"
#include "render/Texture.hpp"
#include "wayland.hpp" #include "wayland.hpp"
#include "../../helpers/signal/Signal.hpp" #include "../../helpers/signal/Signal.hpp"
#include "../../helpers/math/Math.hpp" #include "../../helpers/math/Math.hpp"
@ -75,7 +76,6 @@ class CWLSurfaceResource {
SP<CWlSurface> getResource(); SP<CWlSurface> getResource();
CBox extends(); CBox extends();
void resetRole(); void resetRole();
Vector2D sourceSize();
struct { struct {
CSignal precommit; // before commit CSignal precommit; // before commit
@ -102,7 +102,6 @@ class CWLSurfaceResource {
void breadthfirst(std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data); void breadthfirst(std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data);
SP<CWLSurfaceResource> findFirstPreorder(std::function<bool(SP<CWLSurfaceResource>)> fn); SP<CWLSurfaceResource> findFirstPreorder(std::function<bool(SP<CWLSurfaceResource>)> fn);
CRegion accumulateCurrentBufferDamage();
void presentFeedback(timespec* when, PHLMONITOR pMonitor, bool discarded = false); void presentFeedback(timespec* when, PHLMONITOR pMonitor, bool discarded = false);
void commitPendingState(SSurfaceState& state); void commitPendingState(SSurfaceState& state);

View File

@ -22,19 +22,12 @@ CWLSHMBuffer::CWLSHMBuffer(SP<CWLSHMPoolResource> pool_, uint32_t id, int32_t of
offset = offset_; offset = offset_;
opaque = NFormatUtils::isFormatOpaque(NFormatUtils::shmToDRM(fmt_)); opaque = NFormatUtils::isFormatOpaque(NFormatUtils::shmToDRM(fmt_));
texture = makeShared<CTexture>(NFormatUtils::shmToDRM(fmt), (uint8_t*)pool->data + offset, stride, size_);
resource = CWLBufferResource::create(makeShared<CWlBuffer>(pool_->resource->client(), 1, id)); resource = CWLBufferResource::create(makeShared<CWlBuffer>(pool_->resource->client(), 1, id));
listeners.bufferResourceDestroy = events.destroy.registerListener([this](std::any d) { listeners.bufferResourceDestroy = events.destroy.registerListener([this](std::any d) {
listeners.bufferResourceDestroy.reset(); listeners.bufferResourceDestroy.reset();
PROTO::shm->destroyResource(this); PROTO::shm->destroyResource(this);
}); });
success = texture->m_iTexID;
if UNLIKELY (!success)
Debug::log(ERR, "Failed creating a shm texture: null texture id");
} }
CWLSHMBuffer::~CWLSHMBuffer() { CWLSHMBuffer::~CWLSHMBuffer() {
@ -74,11 +67,11 @@ void CWLSHMBuffer::endDataPtr() {
} }
bool CWLSHMBuffer::good() { bool CWLSHMBuffer::good() {
return success; return true;
} }
void CWLSHMBuffer::update(const CRegion& damage) { void CWLSHMBuffer::update(const CRegion& damage) {
texture->update(NFormatUtils::shmToDRM(fmt), (uint8_t*)pool->data + offset, stride, damage); ;
} }
CSHMPool::CSHMPool(CFileDescriptor fd_, size_t size_) : fd(std::move(fd_)), size(size_), data(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0)) { CSHMPool::CSHMPool(CFileDescriptor fd_, size_t size_) : fd(std::move(fd_)), size(size_), data(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0)) {

View File

@ -50,8 +50,6 @@ class CWLSHMBuffer : public IHLBuffer {
SP<CSHMPool> pool; SP<CSHMPool> pool;
private: private:
bool success = false;
struct { struct {
CHyprSignalListener bufferResourceDestroy; CHyprSignalListener bufferResourceDestroy;
} listeners; } listeners;

View File

@ -0,0 +1,58 @@
#include "SurfaceState.hpp"
#include "helpers/Format.hpp"
#include "protocols/types/Buffer.hpp"
#include "render/Texture.hpp"
Vector2D SSurfaceState::sourceSize() {
if UNLIKELY (!texture)
return {};
if UNLIKELY (viewport.hasSource)
return viewport.source.size();
Vector2D trc = transform % 2 == 1 ? Vector2D{bufferSize.y, bufferSize.x} : bufferSize;
return trc / scale;
}
CRegion SSurfaceState::accumulateBufferDamage() {
if (damage.empty())
return bufferDamage;
CRegion surfaceDamage = damage;
if (viewport.hasDestination) {
Vector2D scale = sourceSize() / viewport.destination;
surfaceDamage.scale(scale);
}
if (viewport.hasSource)
surfaceDamage.translate(viewport.source.pos());
Vector2D trc = transform % 2 == 1 ? Vector2D{bufferSize.y, bufferSize.x} : bufferSize;
bufferDamage = surfaceDamage.scale(scale).transform(wlTransformToHyprutils(invertTransform(transform)), trc.x, trc.y).add(bufferDamage);
damage.clear();
return bufferDamage;
}
void SSurfaceState::updateSynchronousTexture(SP<CTexture> lastTexture) {
auto [dataPtr, fmt, size] = buffer->buffer->beginDataPtr(0);
if (dataPtr) {
auto drmFmt = NFormatUtils::shmToDRM(fmt);
auto stride = bufferSize.y ? size / bufferSize.y : 0;
if (lastTexture && lastTexture->m_isSynchronous && lastTexture->m_vSize == bufferSize) {
texture = lastTexture;
texture->update(drmFmt, dataPtr, stride, accumulateBufferDamage());
} else
texture = makeShared<CTexture>(drmFmt, dataPtr, stride, bufferSize);
}
buffer->buffer->endDataPtr();
}
void SSurfaceState::reset() {
damage.clear();
bufferDamage.clear();
transform = WL_OUTPUT_TRANSFORM_NORMAL;
scale = 1;
offset = {};
size = {};
}

View File

@ -20,16 +20,12 @@ struct SSurfaceState {
Vector2D destination; Vector2D destination;
CBox source; CBox source;
} viewport; } viewport;
bool rejected = false; bool rejected = false;
bool newBuffer = false; bool newBuffer = false;
// Vector2D sourceSize();
void reset() { // Translates damage into bufferDamage, clearing damage and returning the updated bufferDamage
damage.clear(); CRegion accumulateBufferDamage();
bufferDamage.clear(); void updateSynchronousTexture(SP<CTexture> lastTexture);
transform = WL_OUTPUT_TRANSFORM_NORMAL; void reset();
scale = 1;
offset = {};
size = {};
}
}; };

View File

@ -64,8 +64,9 @@ void CTexture::createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t strid
const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat);
ASSERT(format); ASSERT(format);
m_iType = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX; m_iType = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX;
m_vSize = size_; m_vSize = size_;
m_isSynchronous = true;
allocate(); allocate();
GLCALL(glBindTexture(GL_TEXTURE_2D, m_iTexID)); GLCALL(glBindTexture(GL_TEXTURE_2D, m_iTexID));

View File

@ -35,14 +35,15 @@ class CTexture {
void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage); void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage);
const std::vector<uint8_t>& dataCopy(); const std::vector<uint8_t>& dataCopy();
eTextureType m_iType = TEXTURE_RGBA; eTextureType m_iType = TEXTURE_RGBA;
GLenum m_iTarget = GL_TEXTURE_2D; GLenum m_iTarget = GL_TEXTURE_2D;
GLuint m_iTexID = 0; GLuint m_iTexID = 0;
Vector2D m_vSize = {}; Vector2D m_vSize = {};
void* m_pEglImage = nullptr; void* m_pEglImage = nullptr;
eTransform m_eTransform = HYPRUTILS_TRANSFORM_NORMAL; eTransform m_eTransform = HYPRUTILS_TRANSFORM_NORMAL;
bool m_bOpaque = false; bool m_bOpaque = false;
uint32_t m_iDrmFormat = 0; // for shm uint32_t m_iDrmFormat = 0; // for shm
bool m_isSynchronous = false;
private: private:
void createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size); void createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size);

View File

@ -62,12 +62,12 @@ void CXWaylandSurface::ensureListeners() {
}); });
listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) { listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) {
if (surface->pending.texture && !mapped) { if (surface->current.texture && !mapped) {
map(); map();
return; return;
} }
if (!surface->pending.texture && mapped) { if (!surface->current.texture && mapped) {
unmap(); unmap();
return; return;
} }
@ -131,7 +131,7 @@ void CXWaylandSurface::considerMap() {
return; return;
} }
if (surface->pending.texture) { if (surface->current.texture) {
Debug::log(LOG, "XWayland surface: considerMap, sure, we have a buffer"); Debug::log(LOG, "XWayland surface: considerMap, sure, we have a buffer");
map(); map();
return; return;