diff --git a/src/desktop/view/WLSurface.cpp b/src/desktop/view/WLSurface.cpp index 1c3ac8aa1..4c2de539e 100644 --- a/src/desktop/view/WLSurface.cpp +++ b/src/desktop/view/WLSurface.cpp @@ -85,52 +85,52 @@ CRegion CWLSurface::computeDamage(const std::optional& 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 boxSize; if (box.has_value()) { - auto boxSize = box->size(); + boxSize = box->size(); if (m_view->type() == VIEW_TYPE_WINDOW) { const auto WINDOW = dynamicPointerCast(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; } diff --git a/src/desktop/view/Window.cpp b/src/desktop/view/Window.cpp index ff9e88942..ff98ee9a4 100644 --- a/src/desktop/view/Window.cpp +++ b/src/desktop/view/Window.cpp @@ -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); } } } diff --git a/src/output/Monitor.cpp b/src/output/Monitor.cpp index fc397a63b..4097916b2 100644 --- a/src/output/Monitor.cpp +++ b/src/output/Monitor.cpp @@ -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(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 { diff --git a/src/protocols/types/SurfaceState.cpp b/src/protocols/types/SurfaceState.cpp index 80189ed36..4605a64f8 100644 --- a/src/protocols/types/SurfaceState.cpp +++ b/src/protocols/types/SurfaceState.cpp @@ -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(); } diff --git a/src/protocols/types/SurfaceState.hpp b/src/protocols/types/SurfaceState.hpp index 08da928f2..cb8a63a51 100644 --- a/src/protocols/types/SurfaceState.hpp +++ b/src/protocols/types/SurfaceState.hpp @@ -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