renderer: Explicit sync fixes (#7151)

Enables explicit sync by default for most platforms

`misc:no_direct_scanout` -> `render:direct_scanout`
This commit is contained in:
Vaxry 2024-08-06 14:52:19 +01:00 committed by GitHub
parent 0e86808e59
commit 640d161851
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 378 additions and 165 deletions

View file

@ -1,6 +1,8 @@
#include "Monitor.hpp"
#include "MiscFunctions.hpp"
#include "math/Math.hpp"
#include "sync/SyncReleaser.hpp"
#include "ScopeGuard.hpp"
#include "../Compositor.hpp"
#include "../config/ConfigValue.hpp"
#include "../protocols/GammaControl.hpp"
@ -8,6 +10,7 @@
#include "../protocols/LayerShell.hpp"
#include "../protocols/PresentationTime.hpp"
#include "../protocols/DRMLease.hpp"
#include "../protocols/DRMSyncobj.hpp"
#include "../protocols/core/Output.hpp"
#include "../managers/PointerManager.hpp"
#include "../protocols/core/Compositor.hpp"
@ -77,6 +80,7 @@ void CMonitor::onConnect(bool noRule) {
tearingState.canTear = output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM;
if (m_bEnabled) {
output->state->resetExplicitFences();
output->state->setEnabled(true);
state.commit();
return;
@ -101,6 +105,7 @@ void CMonitor::onConnect(bool noRule) {
// if it's disabled, disable and ignore
if (monitorRule.disabled) {
output->state->resetExplicitFences();
output->state->setEnabled(false);
if (!state.commit())
@ -137,6 +142,7 @@ void CMonitor::onConnect(bool noRule) {
m_bEnabled = true;
output->state->resetExplicitFences();
output->state->setEnabled(true);
// set mode, also applies
@ -300,6 +306,7 @@ void CMonitor::onDisconnect(bool destroy) {
activeWorkspace->m_bVisible = false;
activeWorkspace.reset();
output->state->resetExplicitFences();
output->state->setEnabled(false);
if (!state.commit())
@ -808,28 +815,77 @@ bool CMonitor::attemptDirectScanout() {
if (!PSURFACE->current.buffer || !PSURFACE->current.texture || !PSURFACE->current.texture->m_pEglImage /* dmabuf */)
return false;
Debug::log(TRACE, "attemptDirectScanout: surface {:x} passed, will attempt", (uintptr_t)PSURFACE.get());
// FIXME: make sure the buffer actually follows the available scanout dmabuf formats
// and comes from the appropriate device. This may implode on multi-gpu!!
output->state->setBuffer(PSURFACE->current.buffer->buffer.lock());
output->state->setPresentationMode(tearingState.activelyTearing ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE :
Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);
if (!state.test())
if (!state.test()) {
Debug::log(TRACE, "attemptDirectScanout: failed basic test");
return false;
}
auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings();
// 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;
int explicitWaitFD = -1;
if (DOEXPLICIT) {
explicitWaitFD = PSURFACE->syncobj->current.acquireTimeline->timeline->exportAsSyncFileFD(PSURFACE->syncobj->current.acquirePoint);
if (explicitWaitFD < 0)
Debug::log(TRACE, "attemptDirectScanout: failed to acquire an explicit wait fd");
}
DOEXPLICIT = DOEXPLICIT && explicitWaitFD >= 0;
auto cleanup = CScopeGuard([explicitWaitFD, this]() {
output->state->resetExplicitFences();
if (explicitWaitFD >= 0)
close(explicitWaitFD);
});
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
Debug::log(TRACE, "presentFeedback for DS");
PSURFACE->presentFeedback(&now, this, true);
PSURFACE->presentFeedback(&now, this);
output->state->addDamage(CBox{{}, vecPixelSize});
output->state->resetExplicitFences();
if (state.commit()) {
if (DOEXPLICIT) {
Debug::log(TRACE, "attemptDirectScanout: setting IN_FENCE for aq to {}", explicitWaitFD);
output->state->setExplicitInFence(explicitWaitFD);
}
bool ok = output->commit();
if (!ok && DOEXPLICIT) {
Debug::log(TRACE, "attemptDirectScanout: EXPLICIT SYNC FAILED: commit() returned false. Resetting fences and retrying, might result in glitches.");
output->state->resetExplicitFences();
ok = output->commit();
}
if (ok) {
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();
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;
}

View file

@ -121,8 +121,7 @@ class CMonitor {
// explicit sync
SP<CSyncTimeline> inTimeline;
SP<CSyncTimeline> outTimeline;
uint64_t lastWaitPoint = 0;
uint64_t commitSeq = 0;
uint64_t commitSeq = 0;
WP<CMonitor> self;

View file

@ -0,0 +1,10 @@
#include "ScopeGuard.hpp"
CScopeGuard::CScopeGuard(const std::function<void()>& fn_) : fn(fn_) {
;
}
CScopeGuard::~CScopeGuard() {
if (fn)
fn();
}

View file

@ -0,0 +1,13 @@
#pragma once
#include <functional>
// calls a function when it goes out of scope
class CScopeGuard {
public:
CScopeGuard(const std::function<void()>& fn_);
~CScopeGuard();
private:
std::function<void()> fn;
};

View file

@ -0,0 +1,25 @@
#include "SyncReleaser.hpp"
#include "SyncTimeline.hpp"
#include "../../render/OpenGL.hpp"
CSyncReleaser::CSyncReleaser(WP<CSyncTimeline> timeline_, uint64_t point_) : timeline(timeline_), point(point_) {
;
}
CSyncReleaser::~CSyncReleaser() {
if (timeline.expired())
return;
if (sync)
timeline->importFromSyncFileFD(point, sync->fd());
else
timeline->signal(point);
}
void CSyncReleaser::addReleaseSync(SP<CEGLSync> sync_) {
sync = sync_;
}
void CSyncReleaser::drop() {
timeline.reset();
}

View file

@ -0,0 +1,31 @@
#pragma once
#include <cstdint>
#include <optional>
#include <vector>
#include <functional>
#include "../memory/Memory.hpp"
/*
A helper (inspired by KDE's KWin) that will release the timeline point in the dtor
*/
class CSyncTimeline;
class CEGLSync;
class CSyncReleaser {
public:
CSyncReleaser(WP<CSyncTimeline> timeline_, uint64_t point_);
~CSyncReleaser();
// drops the releaser, will never signal anymore
void drop();
// wait for this gpu job to finish before releasing
void addReleaseSync(SP<CEGLSync> sync);
private:
WP<CSyncTimeline> timeline;
uint64_t point = 0;
SP<CEGLSync> sync;
};

View file

@ -188,3 +188,8 @@ bool CSyncTimeline::transfer(SP<CSyncTimeline> from, uint64_t fromPoint, uint64_
return true;
}
void CSyncTimeline::signal(uint64_t point) {
if (drmSyncobjTimelineSignal(drmFD, &handle, &point, 1))
Debug::log(ERR, "CSyncTimeline::signal: drmSyncobjTimelineSignal failed");
}

View file

@ -35,6 +35,7 @@ class CSyncTimeline {
int exportAsSyncFileFD(uint64_t src);
bool importFromSyncFileFD(uint64_t dst, int fd);
bool transfer(SP<CSyncTimeline> from, uint64_t fromPoint, uint64_t toPoint);
void signal(uint64_t point);
int drmFD = -1;
uint32_t handle = 0;