mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-05-13 15:20:36 +01:00
renderer: skip ds commits if buffer didn't change (#9556)
this fixes direct scanout glitches by ensuring that attemptDirectScanout doesn't try to recommit the same buffer to AQ which would cause a pageflip event and the backendRelease to release the same buffer too early
This commit is contained in:
parent
f15b49e0fd
commit
d30cc19d25
5 changed files with 54 additions and 61 deletions
|
@ -1280,23 +1280,26 @@ bool CMonitor::attemptDirectScanout() {
|
|||
|
||||
const auto PSURFACE = g_pXWaylandManager->getWindowSurface(PCANDIDATE);
|
||||
|
||||
if (!PSURFACE || !PSURFACE->current.buffer || PSURFACE->current.bufferSize != vecPixelSize || PSURFACE->current.transform != transform)
|
||||
if (!PSURFACE || !PSURFACE->current.texture || !PSURFACE->current.buffer || PSURFACE->current.buffer->buffer.expired())
|
||||
return false;
|
||||
|
||||
if (PSURFACE->current.bufferSize != vecPixelSize || PSURFACE->current.transform != transform)
|
||||
return false;
|
||||
|
||||
// we can't scanout shm buffers.
|
||||
if (!PSURFACE->current.buffer || !PSURFACE->current.buffer->buffer || !PSURFACE->current.texture || !PSURFACE->current.texture->m_pEglImage /* dmabuf */)
|
||||
const auto params = PSURFACE->current.buffer->buffer->dmabuf();
|
||||
if (!params.success || !PSURFACE->current.texture->m_pEglImage /* dmabuf */)
|
||||
return false;
|
||||
|
||||
Debug::log(TRACE, "attemptDirectScanout: surface {:x} passed, will attempt", (uintptr_t)PSURFACE.get());
|
||||
Debug::log(TRACE, "attemptDirectScanout: surface {:x} passed, will attempt, buffer {}", (uintptr_t)PSURFACE.get(), (uintptr_t)PSURFACE->current.buffer->buffer.get());
|
||||
|
||||
auto PBUFFER = PSURFACE->current.buffer->buffer.lock();
|
||||
if (PBUFFER == output->state->state().buffer)
|
||||
return true;
|
||||
|
||||
// FIXME: make sure the buffer actually follows the available scanout dmabuf formats
|
||||
// and comes from the appropriate device. This may implode on multi-gpu!!
|
||||
|
||||
const auto params = PSURFACE->current.buffer->buffer->dmabuf();
|
||||
// scanout buffer isn't dmabuf, so no scanout
|
||||
if (!params.success)
|
||||
return false;
|
||||
|
||||
// entering into scanout, so save monitor format
|
||||
if (lastScanout.expired())
|
||||
prevDrmFormat = drmFormat;
|
||||
|
@ -1306,7 +1309,7 @@ bool CMonitor::attemptDirectScanout() {
|
|||
drmFormat = params.format;
|
||||
}
|
||||
|
||||
output->state->setBuffer(PSURFACE->current.buffer->buffer.lock());
|
||||
output->state->setBuffer(PBUFFER);
|
||||
output->state->setPresentationMode(tearingState.activelyTearing ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE :
|
||||
Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);
|
||||
|
||||
|
@ -1315,20 +1318,6 @@ bool CMonitor::attemptDirectScanout() {
|
|||
return false;
|
||||
}
|
||||
|
||||
auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings(output);
|
||||
|
||||
// wait for the explicit fence if present, and if kms explicit is allowed
|
||||
bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->syncobj->current.acquireTimeline && PSURFACE->syncobj->current.acquireTimeline->timeline && explicitOptions.explicitKMSEnabled;
|
||||
CFileDescriptor explicitWaitFD;
|
||||
if (DOEXPLICIT) {
|
||||
explicitWaitFD = PSURFACE->syncobj->current.acquireTimeline->timeline->exportAsSyncFileFD(PSURFACE->syncobj->current.acquirePoint);
|
||||
if (!explicitWaitFD.isValid())
|
||||
Debug::log(TRACE, "attemptDirectScanout: failed to acquire an explicit wait fd");
|
||||
}
|
||||
DOEXPLICIT = DOEXPLICIT && explicitWaitFD.isValid();
|
||||
|
||||
auto cleanup = CScopeGuard([this]() { output->state->resetExplicitFences(); });
|
||||
|
||||
timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
PSURFACE->presentFeedback(&now, self.lock());
|
||||
|
@ -1336,11 +1325,24 @@ bool CMonitor::attemptDirectScanout() {
|
|||
output->state->addDamage(CBox{{}, vecPixelSize});
|
||||
output->state->resetExplicitFences();
|
||||
|
||||
auto cleanup = CScopeGuard([this]() { output->state->resetExplicitFences(); });
|
||||
|
||||
auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings(output);
|
||||
|
||||
bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->syncobj->current.acquireTimeline && PSURFACE->syncobj->current.acquireTimeline->timeline && explicitOptions.explicitKMSEnabled;
|
||||
if (DOEXPLICIT) {
|
||||
Debug::log(TRACE, "attemptDirectScanout: setting IN_FENCE for aq to {}", explicitWaitFD.get());
|
||||
output->state->setExplicitInFence(explicitWaitFD.get());
|
||||
// wait for surface's explicit fence if present
|
||||
CFileDescriptor fd = PSURFACE->syncobj->current.acquireTimeline->timeline->exportAsSyncFileFD(PSURFACE->syncobj->current.acquirePoint);
|
||||
if (fd.isValid()) {
|
||||
Debug::log(TRACE, "attemptDirectScanout: setting IN_FENCE for aq to {}", fd.get());
|
||||
output->state->setExplicitInFence(fd.get());
|
||||
} else
|
||||
Debug::log(TRACE, "attemptDirectScanout: failed to acquire an sync file fd for aq IN_FENCE");
|
||||
DOEXPLICIT = fd.isValid();
|
||||
}
|
||||
|
||||
commitSeq++;
|
||||
|
||||
bool ok = output->commit();
|
||||
|
||||
if (!ok && DOEXPLICIT) {
|
||||
|
@ -1350,28 +1352,24 @@ bool CMonitor::attemptDirectScanout() {
|
|||
ok = output->commit();
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
if (!ok) {
|
||||
Debug::log(TRACE, "attemptDirectScanout: failed to scanout surface");
|
||||
lastScanout.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lastScanout.expired()) {
|
||||
lastScanout = PCANDIDATE;
|
||||
Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle);
|
||||
}
|
||||
|
||||
// delay explicit sync feedback until kms release of the buffer
|
||||
if (DOEXPLICIT) {
|
||||
Debug::log(TRACE, "attemptDirectScanout: Delaying explicit sync release feedback until kms release");
|
||||
PSURFACE->current.buffer->releaser->drop();
|
||||
if (!PBUFFER->lockedByBackend)
|
||||
return true;
|
||||
|
||||
PSURFACE->current.buffer->buffer->hlEvents.backendRelease2 = PSURFACE->current.buffer->buffer->events.backendRelease.registerListener([PSURFACE](std::any d) {
|
||||
const bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->syncobj->current.releaseTimeline && PSURFACE->syncobj->current.releaseTimeline->timeline;
|
||||
if (DOEXPLICIT)
|
||||
PSURFACE->syncobj->current.releaseTimeline->timeline->signal(PSURFACE->syncobj->current.releasePoint);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
Debug::log(TRACE, "attemptDirectScanout: failed to scanout surface");
|
||||
lastScanout.reset();
|
||||
return false;
|
||||
}
|
||||
// lock buffer while DRM/KMS is using it, then release it when page flip happens since DRM/KMS should be done by then
|
||||
// btw buffer's syncReleaser will take care of signaling release point, so we don't do that here
|
||||
PBUFFER->lock();
|
||||
PBUFFER->onBackendRelease([PBUFFER]() { PBUFFER->unlock(); });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -440,7 +440,6 @@ void CWLSurfaceResource::unlockPendingState() {
|
|||
|
||||
void CWLSurfaceResource::commitPendingState() {
|
||||
static auto PDROP = CConfigValue<Hyprlang::INT>("render:allow_early_buffer_release");
|
||||
auto const previousBuffer = current.buffer;
|
||||
current = pending;
|
||||
pending.damage.clear();
|
||||
pending.bufferDamage.clear();
|
||||
|
@ -495,15 +494,6 @@ void CWLSurfaceResource::commitPendingState() {
|
|||
nullptr);
|
||||
}
|
||||
|
||||
// for async buffers, we can only release the buffer once we are unrefing it from current.
|
||||
// if the backend took it, ref it with the lambda. Otherwise, the end of this scope will release it.
|
||||
if (previousBuffer && previousBuffer->buffer && !previousBuffer->buffer->isSynchronous()) {
|
||||
if (previousBuffer->buffer->lockedByBackend && !previousBuffer->buffer->hlEvents.backendRelease) {
|
||||
previousBuffer->buffer->lock();
|
||||
previousBuffer->buffer->unlockOnBufferRelease(self);
|
||||
}
|
||||
}
|
||||
|
||||
lastBuffer = current.buffer ? current.buffer->buffer : WP<IHLBuffer>{};
|
||||
}
|
||||
|
||||
|
|
|
@ -26,9 +26,14 @@ bool IHLBuffer::locked() {
|
|||
return nLocks > 0;
|
||||
}
|
||||
|
||||
void IHLBuffer::unlockOnBufferRelease(WP<CWLSurfaceResource> surf) {
|
||||
hlEvents.backendRelease = events.backendRelease.registerListener([this](std::any data) {
|
||||
unlock();
|
||||
void IHLBuffer::onBackendRelease(const std::function<void()>& fn) {
|
||||
if (hlEvents.backendRelease) {
|
||||
hlEvents.backendRelease->emit(nullptr);
|
||||
Debug::log(LOG, "backendRelease emitted early");
|
||||
}
|
||||
|
||||
hlEvents.backendRelease = events.backendRelease.registerListener([this, fn](std::any) {
|
||||
fn();
|
||||
hlEvents.backendRelease.reset();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ class IHLBuffer : public Aquamarine::IBuffer {
|
|||
virtual void unlock();
|
||||
virtual bool locked();
|
||||
|
||||
void unlockOnBufferRelease(WP<CWLSurfaceResource> surf /* optional */);
|
||||
void onBackendRelease(const std::function<void()>& fn);
|
||||
|
||||
SP<CTexture> texture;
|
||||
bool opaque = false;
|
||||
|
|
|
@ -1486,6 +1486,8 @@ static hdr_output_metadata createHDRMetadata(SImageDescription settings, Aquamar
|
|||
}
|
||||
|
||||
bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
|
||||
pMonitor->commitSeq++;
|
||||
|
||||
// apply timelines for explicit sync
|
||||
// save inFD otherwise reset will reset it
|
||||
CFileDescriptor inFD{pMonitor->output->state->state().explicitInFence};
|
||||
|
@ -2278,8 +2280,6 @@ void CHyprRenderer::endRender() {
|
|||
const auto PMONITOR = g_pHyprOpenGL->m_RenderData.pMonitor;
|
||||
static auto PNVIDIAANTIFLICKER = CConfigValue<Hyprlang::INT>("opengl:nvidia_anti_flicker");
|
||||
|
||||
PMONITOR->commitSeq++;
|
||||
|
||||
g_pHyprOpenGL->m_RenderData.damage = m_sRenderPass.render(g_pHyprOpenGL->m_RenderData.damage);
|
||||
|
||||
auto cleanup = CScopeGuard([this]() {
|
||||
|
|
Loading…
Reference in a new issue