mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-05-19 08:30:22 -07:00
core/compositor: Correctly track SHM buffer damage (#9678)
This commit is contained in:
parent
4600043a49
commit
6384f4acf4
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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(); });
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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)) {
|
||||||
|
@ -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;
|
||||||
|
58
src/protocols/types/SurfaceState.cpp
Normal file
58
src/protocols/types/SurfaceState.cpp
Normal 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 = {};
|
||||||
|
}
|
@ -23,13 +23,9 @@ struct SSurfaceState {
|
|||||||
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 = {};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
@ -66,6 +66,7 @@ void CTexture::createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t strid
|
|||||||
|
|
||||||
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));
|
||||||
|
@ -43,6 +43,7 @@ class CTexture {
|
|||||||
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);
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user