mirror of
https://github.com/hyprwm/Hyprland.git
synced 2026-08-24 10:04:19 -05:00
buffer: dont drop damage until actually read (#15718)
we cant drop dmabuf damage until we have actually hit a frame that reads it, otherwise clients like firefox attaches a buffer with damage, eventually sends a .frame() and .commit() then we clear the damage and the render has nothing to use.
This commit is contained in:
@@ -85,52 +85,52 @@ CRegion CWLSurface::computeDamage(const std::optional<CBox>& box) const {
|
||||
if (!m_resource->m_current.texture)
|
||||
return {};
|
||||
|
||||
CRegion damage = m_resource->m_current.accumulateBufferDamage();
|
||||
damage.transform(Math::wlTransformToHyprutils(m_resource->m_current.transform), m_resource->m_current.bufferSize.x, m_resource->m_current.bufferSize.y);
|
||||
|
||||
const auto BUFSIZE = m_resource->m_current.bufferSize;
|
||||
if (BUFSIZE.x <= 0 || BUFSIZE.y <= 0)
|
||||
return {};
|
||||
|
||||
const auto CORRECTVEC = correctSmallVecBuf();
|
||||
|
||||
if (m_resource->m_current.viewport.hasSource)
|
||||
damage.intersect(m_resource->m_current.viewport.source);
|
||||
|
||||
const auto SCALEDSRCSIZE =
|
||||
m_resource->m_current.viewport.hasSource ? m_resource->m_current.viewport.source.size() * m_resource->m_current.scale : m_resource->m_current.bufferSize;
|
||||
if (SCALEDSRCSIZE.x <= 0 || SCALEDSRCSIZE.y <= 0)
|
||||
return {};
|
||||
|
||||
damage.scale({BUFSIZE.x / SCALEDSRCSIZE.x, BUFSIZE.y / SCALEDSRCSIZE.y});
|
||||
damage.translate(CORRECTVEC);
|
||||
|
||||
// go from buffer coords in the damage to hl logical
|
||||
|
||||
const auto SURFSIZE = m_resource->m_current.size;
|
||||
if (SURFSIZE.x <= 0 || SURFSIZE.y <= 0)
|
||||
return {};
|
||||
|
||||
const Vector2D SCALE = SURFSIZE / m_resource->m_current.bufferSize;
|
||||
|
||||
damage.scale(SCALE);
|
||||
std::optional<Vector2D> boxSize;
|
||||
if (box.has_value()) {
|
||||
auto boxSize = box->size();
|
||||
boxSize = box->size();
|
||||
|
||||
if (m_view->type() == VIEW_TYPE_WINDOW) {
|
||||
const auto WINDOW = dynamicPointerCast<CWindow>(m_view.lock());
|
||||
if (!WINDOW)
|
||||
return {};
|
||||
|
||||
boxSize = boxSize * WINDOW->m_X11SurfaceScaledBy;
|
||||
boxSize = boxSize.value() * WINDOW->m_X11SurfaceScaledBy;
|
||||
}
|
||||
|
||||
if (boxSize.x <= 0 || boxSize.y <= 0)
|
||||
if (boxSize->x <= 0 || boxSize->y <= 0)
|
||||
return {};
|
||||
|
||||
damage.intersect(CBox{{}, boxSize});
|
||||
}
|
||||
|
||||
const auto CORRECTVEC = correctSmallVecBuf();
|
||||
|
||||
CRegion damage = m_resource->m_current.accumulateBufferDamage();
|
||||
damage.transform(Math::wlTransformToHyprutils(m_resource->m_current.transform), BUFSIZE.x, BUFSIZE.y);
|
||||
|
||||
if (m_resource->m_current.viewport.hasSource)
|
||||
damage.intersect(m_resource->m_current.viewport.source);
|
||||
|
||||
damage.scale({BUFSIZE.x / SCALEDSRCSIZE.x, BUFSIZE.y / SCALEDSRCSIZE.y});
|
||||
damage.translate(CORRECTVEC);
|
||||
|
||||
// go from buffer coords in the damage to hl logical
|
||||
damage.scale(SURFSIZE / BUFSIZE);
|
||||
|
||||
if (boxSize)
|
||||
damage.intersect(CBox{{}, boxSize.value()});
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
|
||||
@@ -2785,6 +2785,8 @@ void CWindow::commitWindow() {
|
||||
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
|
||||
// damageSurface consumes damage, so snapshot it for the tearing check below
|
||||
const bool HADDAMAGE = !wlSurface()->resource()->m_current.damage.empty() || !wlSurface()->resource()->m_current.bufferDamage.empty();
|
||||
g_pHyprRenderer->damageSurface(wlSurface()->resource(), m_realPosition->goal().x, m_realPosition->goal().y, m_isX11 ? 1.0 / m_X11SurfaceScaledBy : 1.0);
|
||||
|
||||
if (!m_isX11) {
|
||||
@@ -2794,16 +2796,12 @@ void CWindow::commitWindow() {
|
||||
|
||||
// tearing: if solitary, redraw it. This still might be a single surface window
|
||||
if (PMONITOR && PMONITOR->m_solitaryClient.lock() == m_self.lock() && canBeTorn() && PMONITOR->m_tearingState.canTear && wlSurface()->resource()->m_current.texture &&
|
||||
!PMONITOR->isTearingBlocked()) {
|
||||
CRegion damageBox{wlSurface()->resource()->m_current.accumulateBufferDamage()};
|
||||
|
||||
if (!damageBox.empty()) {
|
||||
if (PMONITOR->m_tearingState.busy) {
|
||||
PMONITOR->m_tearingState.frameScheduledWhileBusy = true;
|
||||
} else {
|
||||
PMONITOR->m_tearingState.nextRenderTorn = true;
|
||||
g_pHyprRenderer->renderMonitor(PMONITOR);
|
||||
}
|
||||
!PMONITOR->isTearingBlocked() && HADDAMAGE) {
|
||||
if (PMONITOR->m_tearingState.busy) {
|
||||
PMONITOR->m_tearingState.frameScheduledWhileBusy = true;
|
||||
} else {
|
||||
PMONITOR->m_tearingState.nextRenderTorn = true;
|
||||
g_pHyprRenderer->renderMonitor(PMONITOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-1
@@ -2207,7 +2207,13 @@ bool CMonitor::attemptDirectScanout() {
|
||||
|
||||
PSURFACE->presentFeedback(Time::steadyNow(), m_self.lock());
|
||||
|
||||
m_output->state->addDamage(PSURFACE->m_current.accumulateBufferDamage());
|
||||
// the commit path already added the damage into the damagering
|
||||
CRegion scanoutDamage = m_damage.getBufferDamage(1);
|
||||
// the ring is in transformed space, the fb we hand to KMS is in pixel space
|
||||
scanoutDamage.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_transform)), m_transformedSize.x, m_transformedSize.y);
|
||||
// expand to not miss pixels from rounding, being a bit over is safe, going below is stale pixels.
|
||||
scanoutDamage.expand(1).intersect(CBox{{}, m_pixelSize});
|
||||
m_output->state->addDamage(scanoutDamage);
|
||||
|
||||
// multigpu needs a fence to trigger fence syncing blits and also committing with the recreated dgpu fence
|
||||
if (g_pHyprRenderer->explicitSyncSupported() && isMultiGPU()) {
|
||||
@@ -2235,6 +2241,9 @@ bool CMonitor::attemptDirectScanout() {
|
||||
|
||||
scanoutCommitted = true;
|
||||
|
||||
// the flip has used the damage, rotate it.
|
||||
m_damage.rotate();
|
||||
|
||||
if (m_lastScanout.expired()) {
|
||||
m_lastScanout = PCANDIDATE;
|
||||
Log::logger->log(Log::DEBUG, "Entered a direct scanout to {:x}: \"{}\"", rc<uintptr_t>(PCANDIDATE.get()), PCANDIDATE->m_title);
|
||||
@@ -2268,6 +2277,7 @@ void CMonitor::handleDSleave() {
|
||||
|
||||
m_drmFormat = m_prevDrmFormat;
|
||||
m_blurFBDirty = true;
|
||||
m_damage.damageEntire();
|
||||
}
|
||||
|
||||
bool CMonitor::canAttemptDirectScanoutFast() const {
|
||||
|
||||
@@ -17,24 +17,32 @@ Vector2D SSurfaceState::sourceSize() {
|
||||
return trc / scale;
|
||||
}
|
||||
|
||||
CRegion SSurfaceState::accumulateBufferDamage() {
|
||||
if (damage.empty())
|
||||
return bufferDamage;
|
||||
bool SSurfaceState::consumeBufferDamage() const {
|
||||
return buffer && !buffer->isSynchronous();
|
||||
}
|
||||
|
||||
CRegion surfaceDamage = damage;
|
||||
if (viewport.hasDestination) {
|
||||
Vector2D scale = sourceSize() / viewport.destination;
|
||||
surfaceDamage.scale(scale);
|
||||
CRegion SSurfaceState::accumulateBufferDamage() {
|
||||
if (!damage.empty()) {
|
||||
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(Math::wlTransformToHyprutils(Math::invertTransform(transform)), trc.x, trc.y).add(bufferDamage);
|
||||
damage.clear();
|
||||
}
|
||||
|
||||
if (viewport.hasSource)
|
||||
surfaceDamage.translate(viewport.source.pos());
|
||||
auto taken = bufferDamage;
|
||||
if (consumeBufferDamage())
|
||||
bufferDamage.clear();
|
||||
|
||||
Vector2D trc = transform % 2 == 1 ? Vector2D{bufferSize.y, bufferSize.x} : bufferSize;
|
||||
|
||||
bufferDamage = surfaceDamage.scale(scale).transform(Math::wlTransformToHyprutils(Math::invertTransform(transform)), trc.x, trc.y).add(bufferDamage);
|
||||
damage.clear();
|
||||
return bufferDamage;
|
||||
return taken;
|
||||
}
|
||||
|
||||
CRegion SSurfaceState::effectiveInputRegion() const {
|
||||
@@ -181,10 +189,19 @@ void SSurfaceState::updateFrom(SSurfaceState& ref) {
|
||||
}
|
||||
|
||||
if (ref.updated.bits.damage) {
|
||||
damage = ref.damage;
|
||||
bufferDamage = ref.bufferDamage;
|
||||
} else {
|
||||
// damage is always relative to the current commit
|
||||
if (consumeBufferDamage()) {
|
||||
// what we still hold is only consumed on rendering.
|
||||
// dropping it here would leave those pixels stale.
|
||||
damage.add(ref.damage);
|
||||
bufferDamage.add(ref.bufferDamage);
|
||||
} else {
|
||||
damage = ref.damage;
|
||||
bufferDamage = ref.bufferDamage;
|
||||
}
|
||||
} else if (ref.updated.bits.buffer || !consumeBufferDamage()) {
|
||||
// damage is relative to the buffer, shm drops the buffer on commit
|
||||
// dmabuf doesnt drop it until we recieve a new one. and we cant clear
|
||||
// the dmabuf damage until the renderer has actually consumed it.
|
||||
damage.clear();
|
||||
bufferDamage.clear();
|
||||
}
|
||||
|
||||
@@ -119,6 +119,7 @@ struct SSurfaceState {
|
||||
|
||||
// helpers
|
||||
CRegion accumulateBufferDamage(); // transforms state.damage and merges it into state.bufferDamage
|
||||
bool consumeBufferDamage() const; // whether accumulateBufferDamage() takes the damage rather than leaving it
|
||||
CRegion effectiveInputRegion() const; // materializes the input region clipped to the current surface size
|
||||
void updateFrom(SSurfaceState& ref); // updates this state based on a reference state.
|
||||
void reset(); // resets pending state after commit
|
||||
|
||||
Reference in New Issue
Block a user