mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-29 15:05:59 +01:00
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:
parent
0e86808e59
commit
640d161851
24 changed files with 378 additions and 165 deletions
|
@ -2282,7 +2282,7 @@ void CCompositor::setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFull
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, sFullscreenState state) {
|
void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, sFullscreenState state) {
|
||||||
static auto PNODIRECTSCANOUT = CConfigValue<Hyprlang::INT>("misc:no_direct_scanout");
|
static auto PDIRECTSCANOUT = CConfigValue<Hyprlang::INT>("render:direct_scanout");
|
||||||
|
|
||||||
if (!validMapped(PWINDOW) || g_pCompositor->m_bUnsafeState)
|
if (!validMapped(PWINDOW) || g_pCompositor->m_bUnsafeState)
|
||||||
return;
|
return;
|
||||||
|
@ -2342,7 +2342,7 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, sFullscreenS
|
||||||
|
|
||||||
// send a scanout tranche if we are entering fullscreen, and send a regular one if we aren't.
|
// send a scanout tranche if we are entering fullscreen, and send a regular one if we aren't.
|
||||||
// ignore if DS is disabled.
|
// ignore if DS is disabled.
|
||||||
if (!*PNODIRECTSCANOUT)
|
if (*PDIRECTSCANOUT)
|
||||||
g_pHyprRenderer->setSurfaceScanoutMode(PWINDOW->m_pWLSurface->resource(), EFFECTIVE_MODE != FSMODE_NONE ? PMONITOR->self.lock() : nullptr);
|
g_pHyprRenderer->setSurfaceScanoutMode(PWINDOW->m_pWLSurface->resource(), EFFECTIVE_MODE != FSMODE_NONE ? PMONITOR->self.lock() : nullptr);
|
||||||
|
|
||||||
g_pConfigManager->ensureVRR(PMONITOR);
|
g_pConfigManager->ensureVRR(PMONITOR);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "../render/decorations/CHyprGroupBarDecoration.hpp"
|
#include "../render/decorations/CHyprGroupBarDecoration.hpp"
|
||||||
#include "config/ConfigDataValues.hpp"
|
#include "config/ConfigDataValues.hpp"
|
||||||
|
#include "config/ConfigValue.hpp"
|
||||||
#include "helpers/varlist/VarList.hpp"
|
#include "helpers/varlist/VarList.hpp"
|
||||||
#include "../protocols/LayerShell.hpp"
|
#include "../protocols/LayerShell.hpp"
|
||||||
|
|
||||||
|
@ -346,7 +347,6 @@ CConfigManager::CConfigManager() {
|
||||||
m_pConfig->addConfigValue("misc:swallow_regex", {STRVAL_EMPTY});
|
m_pConfig->addConfigValue("misc:swallow_regex", {STRVAL_EMPTY});
|
||||||
m_pConfig->addConfigValue("misc:swallow_exception_regex", {STRVAL_EMPTY});
|
m_pConfig->addConfigValue("misc:swallow_exception_regex", {STRVAL_EMPTY});
|
||||||
m_pConfig->addConfigValue("misc:focus_on_activate", Hyprlang::INT{0});
|
m_pConfig->addConfigValue("misc:focus_on_activate", Hyprlang::INT{0});
|
||||||
m_pConfig->addConfigValue("misc:no_direct_scanout", Hyprlang::INT{1});
|
|
||||||
m_pConfig->addConfigValue("misc:mouse_move_focuses_monitor", Hyprlang::INT{1});
|
m_pConfig->addConfigValue("misc:mouse_move_focuses_monitor", Hyprlang::INT{1});
|
||||||
m_pConfig->addConfigValue("misc:render_ahead_of_time", Hyprlang::INT{0});
|
m_pConfig->addConfigValue("misc:render_ahead_of_time", Hyprlang::INT{0});
|
||||||
m_pConfig->addConfigValue("misc:render_ahead_safezone", Hyprlang::INT{1});
|
m_pConfig->addConfigValue("misc:render_ahead_safezone", Hyprlang::INT{1});
|
||||||
|
@ -560,7 +560,9 @@ CConfigManager::CConfigManager() {
|
||||||
m_pConfig->addConfigValue("group:groupbar:col.locked_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66ff5500"});
|
m_pConfig->addConfigValue("group:groupbar:col.locked_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66ff5500"});
|
||||||
m_pConfig->addConfigValue("group:groupbar:col.locked_inactive", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66775500"});
|
m_pConfig->addConfigValue("group:groupbar:col.locked_inactive", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66775500"});
|
||||||
|
|
||||||
m_pConfig->addConfigValue("experimental:explicit_sync", Hyprlang::INT{0});
|
m_pConfig->addConfigValue("render:explicit_sync", Hyprlang::INT{2});
|
||||||
|
m_pConfig->addConfigValue("render:explicit_sync_kms", Hyprlang::INT{2});
|
||||||
|
m_pConfig->addConfigValue("render:direct_scanout", Hyprlang::INT{0});
|
||||||
|
|
||||||
// devices
|
// devices
|
||||||
m_pConfig->addSpecialCategory("device", {"name"});
|
m_pConfig->addSpecialCategory("device", {"name"});
|
||||||
|
@ -798,6 +800,9 @@ std::optional<std::string> CConfigManager::resetHLConfig() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
|
void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
|
||||||
|
static const auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("render:explicit_sync");
|
||||||
|
static int prevEnabledExplicit = *PENABLEEXPLICIT;
|
||||||
|
|
||||||
for (auto& w : g_pCompositor->m_vWindows) {
|
for (auto& w : g_pCompositor->m_vWindows) {
|
||||||
w->uncacheWindowDecos();
|
w->uncacheWindowDecos();
|
||||||
}
|
}
|
||||||
|
@ -816,6 +821,9 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
|
||||||
if (!isFirstLaunch)
|
if (!isFirstLaunch)
|
||||||
g_pHyprOpenGL->m_bReloadScreenShader = true;
|
g_pHyprOpenGL->m_bReloadScreenShader = true;
|
||||||
|
|
||||||
|
if (!isFirstLaunch && *PENABLEEXPLICIT != prevEnabledExplicit)
|
||||||
|
g_pHyprError->queueCreate("Warning: You changed the render:explicit_sync option, this requires you to restart Hyprland.", CColor(0.9, 0.76, 0.221, 1.0));
|
||||||
|
|
||||||
// parseError will be displayed next frame
|
// parseError will be displayed next frame
|
||||||
|
|
||||||
if (result.error)
|
if (result.error)
|
||||||
|
@ -1417,7 +1425,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
|
||||||
|
|
||||||
if (USEVRR == 0) {
|
if (USEVRR == 0) {
|
||||||
if (m->vrrActive) {
|
if (m->vrrActive) {
|
||||||
|
m->output->state->resetExplicitFences();
|
||||||
m->output->state->setAdaptiveSync(false);
|
m->output->state->setAdaptiveSync(false);
|
||||||
|
|
||||||
if (!m->state.commit())
|
if (!m->state.commit())
|
||||||
|
@ -1427,6 +1435,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
|
||||||
return;
|
return;
|
||||||
} else if (USEVRR == 1) {
|
} else if (USEVRR == 1) {
|
||||||
if (!m->vrrActive) {
|
if (!m->vrrActive) {
|
||||||
|
m->output->state->resetExplicitFences();
|
||||||
m->output->state->setAdaptiveSync(true);
|
m->output->state->setAdaptiveSync(true);
|
||||||
|
|
||||||
if (!m->state.test()) {
|
if (!m->state.test()) {
|
||||||
|
@ -1451,6 +1460,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
|
||||||
const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && (PWORKSPACE->m_efFullscreenMode & FSMODE_FULLSCREEN);
|
const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && (PWORKSPACE->m_efFullscreenMode & FSMODE_FULLSCREEN);
|
||||||
|
|
||||||
if (WORKSPACEFULL) {
|
if (WORKSPACEFULL) {
|
||||||
|
m->output->state->resetExplicitFences();
|
||||||
m->output->state->setAdaptiveSync(true);
|
m->output->state->setAdaptiveSync(true);
|
||||||
|
|
||||||
if (!m->state.test()) {
|
if (!m->state.test()) {
|
||||||
|
@ -1462,6 +1472,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
|
||||||
Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name);
|
Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name);
|
||||||
|
|
||||||
} else if (!WORKSPACEFULL) {
|
} else if (!WORKSPACEFULL) {
|
||||||
|
m->output->state->resetExplicitFences();
|
||||||
m->output->state->setAdaptiveSync(false);
|
m->output->state->setAdaptiveSync(false);
|
||||||
|
|
||||||
if (!m->state.commit())
|
if (!m->state.commit())
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "Monitor.hpp"
|
#include "Monitor.hpp"
|
||||||
#include "MiscFunctions.hpp"
|
#include "MiscFunctions.hpp"
|
||||||
#include "math/Math.hpp"
|
#include "math/Math.hpp"
|
||||||
|
#include "sync/SyncReleaser.hpp"
|
||||||
|
#include "ScopeGuard.hpp"
|
||||||
#include "../Compositor.hpp"
|
#include "../Compositor.hpp"
|
||||||
#include "../config/ConfigValue.hpp"
|
#include "../config/ConfigValue.hpp"
|
||||||
#include "../protocols/GammaControl.hpp"
|
#include "../protocols/GammaControl.hpp"
|
||||||
|
@ -8,6 +10,7 @@
|
||||||
#include "../protocols/LayerShell.hpp"
|
#include "../protocols/LayerShell.hpp"
|
||||||
#include "../protocols/PresentationTime.hpp"
|
#include "../protocols/PresentationTime.hpp"
|
||||||
#include "../protocols/DRMLease.hpp"
|
#include "../protocols/DRMLease.hpp"
|
||||||
|
#include "../protocols/DRMSyncobj.hpp"
|
||||||
#include "../protocols/core/Output.hpp"
|
#include "../protocols/core/Output.hpp"
|
||||||
#include "../managers/PointerManager.hpp"
|
#include "../managers/PointerManager.hpp"
|
||||||
#include "../protocols/core/Compositor.hpp"
|
#include "../protocols/core/Compositor.hpp"
|
||||||
|
@ -77,6 +80,7 @@ void CMonitor::onConnect(bool noRule) {
|
||||||
tearingState.canTear = output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM;
|
tearingState.canTear = output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM;
|
||||||
|
|
||||||
if (m_bEnabled) {
|
if (m_bEnabled) {
|
||||||
|
output->state->resetExplicitFences();
|
||||||
output->state->setEnabled(true);
|
output->state->setEnabled(true);
|
||||||
state.commit();
|
state.commit();
|
||||||
return;
|
return;
|
||||||
|
@ -101,6 +105,7 @@ void CMonitor::onConnect(bool noRule) {
|
||||||
// if it's disabled, disable and ignore
|
// if it's disabled, disable and ignore
|
||||||
if (monitorRule.disabled) {
|
if (monitorRule.disabled) {
|
||||||
|
|
||||||
|
output->state->resetExplicitFences();
|
||||||
output->state->setEnabled(false);
|
output->state->setEnabled(false);
|
||||||
|
|
||||||
if (!state.commit())
|
if (!state.commit())
|
||||||
|
@ -137,6 +142,7 @@ void CMonitor::onConnect(bool noRule) {
|
||||||
|
|
||||||
m_bEnabled = true;
|
m_bEnabled = true;
|
||||||
|
|
||||||
|
output->state->resetExplicitFences();
|
||||||
output->state->setEnabled(true);
|
output->state->setEnabled(true);
|
||||||
|
|
||||||
// set mode, also applies
|
// set mode, also applies
|
||||||
|
@ -300,6 +306,7 @@ void CMonitor::onDisconnect(bool destroy) {
|
||||||
activeWorkspace->m_bVisible = false;
|
activeWorkspace->m_bVisible = false;
|
||||||
activeWorkspace.reset();
|
activeWorkspace.reset();
|
||||||
|
|
||||||
|
output->state->resetExplicitFences();
|
||||||
output->state->setEnabled(false);
|
output->state->setEnabled(false);
|
||||||
|
|
||||||
if (!state.commit())
|
if (!state.commit())
|
||||||
|
@ -808,28 +815,77 @@ bool CMonitor::attemptDirectScanout() {
|
||||||
if (!PSURFACE->current.buffer || !PSURFACE->current.texture || !PSURFACE->current.texture->m_pEglImage /* dmabuf */)
|
if (!PSURFACE->current.buffer || !PSURFACE->current.texture || !PSURFACE->current.texture->m_pEglImage /* dmabuf */)
|
||||||
return false;
|
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
|
// FIXME: make sure the buffer actually follows the available scanout dmabuf formats
|
||||||
// and comes from the appropriate device. This may implode on multi-gpu!!
|
// and comes from the appropriate device. This may implode on multi-gpu!!
|
||||||
output->state->setBuffer(PSURFACE->current.buffer->buffer.lock());
|
output->state->setBuffer(PSURFACE->current.buffer->buffer.lock());
|
||||||
output->state->setPresentationMode(tearingState.activelyTearing ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE :
|
output->state->setPresentationMode(tearingState.activelyTearing ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE :
|
||||||
Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);
|
Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);
|
||||||
|
|
||||||
if (!state.test())
|
if (!state.test()) {
|
||||||
|
Debug::log(TRACE, "attemptDirectScanout: failed basic test");
|
||||||
return false;
|
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;
|
timespec now;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
Debug::log(TRACE, "presentFeedback for DS");
|
PSURFACE->presentFeedback(&now, this);
|
||||||
PSURFACE->presentFeedback(&now, this, true);
|
|
||||||
|
|
||||||
output->state->addDamage(CBox{{}, vecPixelSize});
|
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()) {
|
if (lastScanout.expired()) {
|
||||||
lastScanout = PCANDIDATE;
|
lastScanout = PCANDIDATE;
|
||||||
Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle);
|
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 {
|
} else {
|
||||||
|
Debug::log(TRACE, "attemptDirectScanout: failed to scanout surface");
|
||||||
lastScanout.reset();
|
lastScanout.reset();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,6 @@ class CMonitor {
|
||||||
// explicit sync
|
// explicit sync
|
||||||
SP<CSyncTimeline> inTimeline;
|
SP<CSyncTimeline> inTimeline;
|
||||||
SP<CSyncTimeline> outTimeline;
|
SP<CSyncTimeline> outTimeline;
|
||||||
uint64_t lastWaitPoint = 0;
|
|
||||||
uint64_t commitSeq = 0;
|
uint64_t commitSeq = 0;
|
||||||
|
|
||||||
WP<CMonitor> self;
|
WP<CMonitor> self;
|
||||||
|
|
10
src/helpers/ScopeGuard.cpp
Normal file
10
src/helpers/ScopeGuard.cpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#include "ScopeGuard.hpp"
|
||||||
|
|
||||||
|
CScopeGuard::CScopeGuard(const std::function<void()>& fn_) : fn(fn_) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
CScopeGuard::~CScopeGuard() {
|
||||||
|
if (fn)
|
||||||
|
fn();
|
||||||
|
}
|
13
src/helpers/ScopeGuard.hpp
Normal file
13
src/helpers/ScopeGuard.hpp
Normal 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;
|
||||||
|
};
|
25
src/helpers/sync/SyncReleaser.cpp
Normal file
25
src/helpers/sync/SyncReleaser.cpp
Normal 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();
|
||||||
|
}
|
31
src/helpers/sync/SyncReleaser.hpp
Normal file
31
src/helpers/sync/SyncReleaser.hpp
Normal 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;
|
||||||
|
};
|
|
@ -188,3 +188,8 @@ bool CSyncTimeline::transfer(SP<CSyncTimeline> from, uint64_t fromPoint, uint64_
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSyncTimeline::signal(uint64_t point) {
|
||||||
|
if (drmSyncobjTimelineSignal(drmFD, &handle, &point, 1))
|
||||||
|
Debug::log(ERR, "CSyncTimeline::signal: drmSyncobjTimelineSignal failed");
|
||||||
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ class CSyncTimeline {
|
||||||
int exportAsSyncFileFD(uint64_t src);
|
int exportAsSyncFileFD(uint64_t src);
|
||||||
bool importFromSyncFileFD(uint64_t dst, int fd);
|
bool importFromSyncFileFD(uint64_t dst, int fd);
|
||||||
bool transfer(SP<CSyncTimeline> from, uint64_t fromPoint, uint64_t toPoint);
|
bool transfer(SP<CSyncTimeline> from, uint64_t fromPoint, uint64_t toPoint);
|
||||||
|
void signal(uint64_t point);
|
||||||
|
|
||||||
int drmFD = -1;
|
int drmFD = -1;
|
||||||
uint32_t handle = 0;
|
uint32_t handle = 0;
|
||||||
|
|
|
@ -2293,6 +2293,7 @@ void CKeybindManager::dpms(std::string arg) {
|
||||||
if (!port.empty() && m->szName != port)
|
if (!port.empty() && m->szName != port)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
m->output->state->resetExplicitFences();
|
||||||
m->output->state->setEnabled(enable);
|
m->output->state->setEnabled(enable);
|
||||||
|
|
||||||
m->dpmsStatus = enable;
|
m->dpmsStatus = enable;
|
||||||
|
|
|
@ -76,7 +76,7 @@ void CProtocolManager::onMonitorModeChange(CMonitor* pMonitor) {
|
||||||
|
|
||||||
CProtocolManager::CProtocolManager() {
|
CProtocolManager::CProtocolManager() {
|
||||||
|
|
||||||
static const auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("experimental:explicit_sync");
|
static const auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("render:explicit_sync");
|
||||||
|
|
||||||
// Outputs are a bit dumb, we have to agree.
|
// Outputs are a bit dumb, we have to agree.
|
||||||
static auto P = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) {
|
static auto P = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) {
|
||||||
|
|
|
@ -25,8 +25,8 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SP<CWpLinuxDrmSyncobjSurf
|
||||||
}
|
}
|
||||||
|
|
||||||
auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_);
|
auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_);
|
||||||
acquireTimeline = timeline;
|
pending.acquireTimeline = timeline;
|
||||||
acquirePoint = ((uint64_t)hi << 32) | (uint64_t)lo;
|
pending.acquirePoint = ((uint64_t)hi << 32) | (uint64_t)lo;
|
||||||
});
|
});
|
||||||
|
|
||||||
resource->setSetReleasePoint([this](CWpLinuxDrmSyncobjSurfaceV1* r, wl_resource* timeline_, uint32_t hi, uint32_t lo) {
|
resource->setSetReleasePoint([this](CWpLinuxDrmSyncobjSurfaceV1* r, wl_resource* timeline_, uint32_t hi, uint32_t lo) {
|
||||||
|
@ -36,28 +36,32 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SP<CWpLinuxDrmSyncobjSurf
|
||||||
}
|
}
|
||||||
|
|
||||||
auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_);
|
auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_);
|
||||||
releaseTimeline = timeline;
|
pending.releaseTimeline = timeline;
|
||||||
releasePoint = ((uint64_t)hi << 32) | (uint64_t)lo;
|
pending.releasePoint = ((uint64_t)hi << 32) | (uint64_t)lo;
|
||||||
});
|
});
|
||||||
|
|
||||||
listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) {
|
listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) {
|
||||||
if (!!acquireTimeline != !!releaseTimeline) {
|
if ((pending.acquireTimeline || pending.releaseTimeline) && !surface->pending.texture) {
|
||||||
resource->error(acquireTimeline ? WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT : WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, "Missing timeline");
|
|
||||||
surface->pending.rejected = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((acquireTimeline || releaseTimeline) && !surface->pending.texture) {
|
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!acquireTimeline)
|
if (!surface->pending.newBuffer)
|
||||||
|
return; // this commit does not change the state here
|
||||||
|
|
||||||
|
if (!!pending.acquireTimeline != !!pending.releaseTimeline) {
|
||||||
|
resource->error(pending.acquireTimeline ? WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT : WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT,
|
||||||
|
"Missing timeline");
|
||||||
|
surface->pending.rejected = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pending.acquireTimeline)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// wait for the acquire timeline to materialize
|
// wait for the acquire timeline to materialize
|
||||||
auto materialized = acquireTimeline->timeline->check(acquirePoint, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE);
|
auto materialized = pending.acquireTimeline->timeline->check(pending.acquirePoint, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE);
|
||||||
if (!materialized.has_value()) {
|
if (!materialized.has_value()) {
|
||||||
LOGM(ERR, "Failed to check the acquire timeline");
|
LOGM(ERR, "Failed to check the acquire timeline");
|
||||||
resource->noMemory();
|
resource->noMemory();
|
||||||
|
@ -68,7 +72,24 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SP<CWpLinuxDrmSyncobjSurf
|
||||||
return;
|
return;
|
||||||
|
|
||||||
surface->lockPendingState();
|
surface->lockPendingState();
|
||||||
acquireTimeline->timeline->addWaiter([this]() { surface->unlockPendingState(); }, acquirePoint, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE);
|
pending.acquireTimeline->timeline->addWaiter([this]() { surface->unlockPendingState(); }, pending.acquirePoint, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE);
|
||||||
|
});
|
||||||
|
|
||||||
|
listeners.surfaceCommit = surface->events.commit.registerListener([this](std::any d) {
|
||||||
|
// apply timelines if new ones have been attached, otherwise don't touch
|
||||||
|
// the current ones
|
||||||
|
if (pending.releaseTimeline) {
|
||||||
|
current.releaseTimeline = pending.releaseTimeline;
|
||||||
|
current.releasePoint = pending.releasePoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pending.acquireTimeline) {
|
||||||
|
current.acquireTimeline = pending.acquireTimeline;
|
||||||
|
current.acquirePoint = pending.acquirePoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
pending.releaseTimeline.reset();
|
||||||
|
pending.acquireTimeline.reset();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,14 +17,17 @@ class CDRMSyncobjSurfaceResource {
|
||||||
bool good();
|
bool good();
|
||||||
|
|
||||||
WP<CWLSurfaceResource> surface;
|
WP<CWLSurfaceResource> surface;
|
||||||
|
struct {
|
||||||
WP<CDRMSyncobjTimelineResource> acquireTimeline, releaseTimeline;
|
WP<CDRMSyncobjTimelineResource> acquireTimeline, releaseTimeline;
|
||||||
uint64_t acquirePoint = 0, releasePoint = 0;
|
uint64_t acquirePoint = 0, releasePoint = 0;
|
||||||
|
} current, pending;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SP<CWpLinuxDrmSyncobjSurfaceV1> resource;
|
SP<CWpLinuxDrmSyncobjSurfaceV1> resource;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
CHyprSignalListener surfacePrecommit;
|
CHyprSignalListener surfacePrecommit;
|
||||||
|
CHyprSignalListener surfaceCommit;
|
||||||
} listeners;
|
} listeners;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "Subcompositor.hpp"
|
#include "Subcompositor.hpp"
|
||||||
#include "../Viewporter.hpp"
|
#include "../Viewporter.hpp"
|
||||||
#include "../../helpers/Monitor.hpp"
|
#include "../../helpers/Monitor.hpp"
|
||||||
|
#include "../../helpers/sync/SyncReleaser.hpp"
|
||||||
#include "../PresentationTime.hpp"
|
#include "../PresentationTime.hpp"
|
||||||
#include "../DRMSyncobj.hpp"
|
#include "../DRMSyncobj.hpp"
|
||||||
#include "../../render/Renderer.hpp"
|
#include "../../render/Renderer.hpp"
|
||||||
|
@ -70,6 +71,7 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
|
||||||
|
|
||||||
resource->setAttach([this](CWlSurface* r, wl_resource* buffer, int32_t x, int32_t y) {
|
resource->setAttach([this](CWlSurface* r, wl_resource* buffer, int32_t x, int32_t y) {
|
||||||
pending.offset = {x, y};
|
pending.offset = {x, y};
|
||||||
|
pending.newBuffer = true;
|
||||||
|
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
pending.buffer.reset();
|
pending.buffer.reset();
|
||||||
|
@ -428,6 +430,10 @@ void CWLSurfaceResource::commitPendingState() {
|
||||||
current = pending;
|
current = pending;
|
||||||
pending.damage.clear();
|
pending.damage.clear();
|
||||||
pending.bufferDamage.clear();
|
pending.bufferDamage.clear();
|
||||||
|
pending.newBuffer = false;
|
||||||
|
|
||||||
|
if (syncobj && syncobj->current.releaseTimeline && syncobj->current.releaseTimeline->timeline && current.buffer && current.buffer->buffer)
|
||||||
|
current.buffer->releaser = makeShared<CSyncReleaser>(syncobj->current.releaseTimeline->timeline, syncobj->current.releasePoint);
|
||||||
|
|
||||||
if (current.texture)
|
if (current.texture)
|
||||||
current.texture->m_eTransform = wlTransformToHyprutils(current.transform);
|
current.texture->m_eTransform = wlTransformToHyprutils(current.transform);
|
||||||
|
@ -501,23 +507,18 @@ void CWLSurfaceResource::updateCursorShm() {
|
||||||
memcpy(shmData.data(), pixelData, bufLen);
|
memcpy(shmData.data(), pixelData, bufLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWLSurfaceResource::presentFeedback(timespec* when, CMonitor* pMonitor, bool needsExplicitSync) {
|
void CWLSurfaceResource::presentFeedback(timespec* when, CMonitor* pMonitor) {
|
||||||
frame(when);
|
frame(when);
|
||||||
auto FEEDBACK = makeShared<CQueuedPresentationData>(self.lock());
|
auto FEEDBACK = makeShared<CQueuedPresentationData>(self.lock());
|
||||||
FEEDBACK->attachMonitor(pMonitor);
|
FEEDBACK->attachMonitor(pMonitor);
|
||||||
FEEDBACK->presented();
|
FEEDBACK->presented();
|
||||||
PROTO::presentation->queueData(FEEDBACK);
|
PROTO::presentation->queueData(FEEDBACK);
|
||||||
|
|
||||||
if (!pMonitor || !pMonitor->outTimeline || !syncobj || !needsExplicitSync)
|
if (!pMonitor || !pMonitor->outTimeline || !syncobj)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// attach explicit sync
|
// attach explicit sync
|
||||||
g_pHyprRenderer->explicitPresented.emplace_back(self.lock());
|
g_pHyprRenderer->explicitPresented.emplace_back(self.lock());
|
||||||
|
|
||||||
if (syncobj->acquirePoint > pMonitor->lastWaitPoint) {
|
|
||||||
Debug::log(TRACE, "presentFeedback lastWaitPoint {} -> {}", pMonitor->lastWaitPoint, syncobj->acquirePoint);
|
|
||||||
pMonitor->lastWaitPoint = syncobj->acquirePoint;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CWLCompositorResource::CWLCompositorResource(SP<CWlCompositor> resource_) : resource(resource_) {
|
CWLCompositorResource::CWLCompositorResource(SP<CWlCompositor> resource_) : resource(resource_) {
|
||||||
|
|
|
@ -98,6 +98,7 @@ class CWLSurfaceResource {
|
||||||
CBox source;
|
CBox source;
|
||||||
} viewport;
|
} viewport;
|
||||||
bool rejected = false;
|
bool rejected = false;
|
||||||
|
bool newBuffer = false;
|
||||||
|
|
||||||
//
|
//
|
||||||
void reset() {
|
void reset() {
|
||||||
|
@ -122,7 +123,7 @@ 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);
|
||||||
CRegion accumulateCurrentBufferDamage();
|
CRegion accumulateCurrentBufferDamage();
|
||||||
void presentFeedback(timespec* when, CMonitor* pMonitor, bool needsExplicitSync = false);
|
void presentFeedback(timespec* when, CMonitor* pMonitor);
|
||||||
void lockPendingState();
|
void lockPendingState();
|
||||||
void unlockPendingState();
|
void unlockPendingState();
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,6 @@ void IHLBuffer::sendRelease() {
|
||||||
resource->sendRelease();
|
resource->sendRelease();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHLBuffer::sendReleaseWithSurface(SP<CWLSurfaceResource> surf) {
|
|
||||||
if (resource && resource->good())
|
|
||||||
resource->sendReleaseWithSurface(surf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IHLBuffer::lock() {
|
void IHLBuffer::lock() {
|
||||||
nLocks++;
|
nLocks++;
|
||||||
}
|
}
|
||||||
|
@ -27,26 +22,13 @@ void IHLBuffer::unlock() {
|
||||||
sendRelease();
|
sendRelease();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHLBuffer::unlockWithSurface(SP<CWLSurfaceResource> surf) {
|
|
||||||
nLocks--;
|
|
||||||
|
|
||||||
ASSERT(nLocks >= 0);
|
|
||||||
|
|
||||||
if (nLocks == 0)
|
|
||||||
sendReleaseWithSurface(surf);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IHLBuffer::locked() {
|
bool IHLBuffer::locked() {
|
||||||
return nLocks > 0;
|
return nLocks > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHLBuffer::unlockOnBufferRelease(WP<CWLSurfaceResource> surf) {
|
void IHLBuffer::unlockOnBufferRelease(WP<CWLSurfaceResource> surf) {
|
||||||
unlockSurface = surf;
|
|
||||||
hlEvents.backendRelease = events.backendRelease.registerListener([this](std::any data) {
|
hlEvents.backendRelease = events.backendRelease.registerListener([this](std::any data) {
|
||||||
if (unlockSurface.expired())
|
|
||||||
unlock();
|
unlock();
|
||||||
else
|
|
||||||
unlockWithSurface(unlockSurface.lock());
|
|
||||||
hlEvents.backendRelease.reset();
|
hlEvents.backendRelease.reset();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -59,8 +41,5 @@ CHLBufferReference::~CHLBufferReference() {
|
||||||
if (buffer.expired())
|
if (buffer.expired())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (surface)
|
|
||||||
buffer->unlockWithSurface(surface.lock());
|
|
||||||
else
|
|
||||||
buffer->unlock();
|
buffer->unlock();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include <aquamarine/buffer/Buffer.hpp>
|
#include <aquamarine/buffer/Buffer.hpp>
|
||||||
|
|
||||||
|
class CSyncReleaser;
|
||||||
|
|
||||||
class IHLBuffer : public Aquamarine::IBuffer {
|
class IHLBuffer : public Aquamarine::IBuffer {
|
||||||
public:
|
public:
|
||||||
virtual ~IHLBuffer();
|
virtual ~IHLBuffer();
|
||||||
|
@ -15,10 +17,8 @@ class IHLBuffer : public Aquamarine::IBuffer {
|
||||||
virtual bool isSynchronous() = 0; // whether the updates to this buffer are synchronous, aka happen over cpu
|
virtual bool isSynchronous() = 0; // whether the updates to this buffer are synchronous, aka happen over cpu
|
||||||
virtual bool good() = 0;
|
virtual bool good() = 0;
|
||||||
virtual void sendRelease();
|
virtual void sendRelease();
|
||||||
virtual void sendReleaseWithSurface(SP<CWLSurfaceResource>);
|
|
||||||
virtual void lock();
|
virtual void lock();
|
||||||
virtual void unlock();
|
virtual void unlock();
|
||||||
virtual void unlockWithSurface(SP<CWLSurfaceResource> surf);
|
|
||||||
virtual bool locked();
|
virtual bool locked();
|
||||||
|
|
||||||
void unlockOnBufferRelease(WP<CWLSurfaceResource> surf /* optional */);
|
void unlockOnBufferRelease(WP<CWLSurfaceResource> surf /* optional */);
|
||||||
|
@ -29,12 +29,11 @@ class IHLBuffer : public Aquamarine::IBuffer {
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
CHyprSignalListener backendRelease;
|
CHyprSignalListener backendRelease;
|
||||||
|
CHyprSignalListener backendRelease2; // for explicit ds
|
||||||
} hlEvents;
|
} hlEvents;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int nLocks = 0;
|
int nLocks = 0;
|
||||||
|
|
||||||
WP<CWLSurfaceResource> unlockSurface;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// for ref-counting. Releases in ~dtor
|
// for ref-counting. Releases in ~dtor
|
||||||
|
@ -45,6 +44,7 @@ class CHLBufferReference {
|
||||||
~CHLBufferReference();
|
~CHLBufferReference();
|
||||||
|
|
||||||
WP<IHLBuffer> buffer;
|
WP<IHLBuffer> buffer;
|
||||||
|
SP<CSyncReleaser> releaser;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WP<CWLSurfaceResource> surface;
|
WP<CWLSurfaceResource> surface;
|
||||||
|
|
|
@ -32,16 +32,6 @@ void CWLBufferResource::sendRelease() {
|
||||||
resource->sendRelease();
|
resource->sendRelease();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWLBufferResource::sendReleaseWithSurface(SP<CWLSurfaceResource> surf) {
|
|
||||||
sendRelease();
|
|
||||||
|
|
||||||
if (!surf || !surf->syncobj)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (drmSyncobjTimelineSignal(g_pCompositor->m_iDRMFD, &surf->syncobj->releaseTimeline->timeline->handle, &surf->syncobj->releasePoint, 1))
|
|
||||||
Debug::log(ERR, "sendReleaseWithSurface: drmSyncobjTimelineSignal failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_resource* CWLBufferResource::getResource() {
|
wl_resource* CWLBufferResource::getResource() {
|
||||||
return resource->resource();
|
return resource->resource();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ class CWLBufferResource {
|
||||||
|
|
||||||
bool good();
|
bool good();
|
||||||
void sendRelease();
|
void sendRelease();
|
||||||
void sendReleaseWithSurface(SP<CWLSurfaceResource>);
|
|
||||||
wl_resource* getResource();
|
wl_resource* getResource();
|
||||||
|
|
||||||
WP<IHLBuffer> buffer;
|
WP<IHLBuffer> buffer;
|
||||||
|
|
|
@ -2870,7 +2870,7 @@ SP<CEGLSync> CHyprOpenGLImpl::createEGLSync(int fenceFD) {
|
||||||
std::vector<EGLint> attribs;
|
std::vector<EGLint> attribs;
|
||||||
int dupFd = -1;
|
int dupFd = -1;
|
||||||
if (fenceFD > 0) {
|
if (fenceFD > 0) {
|
||||||
int dupFd = fcntl(fenceFD, F_DUPFD_CLOEXEC, 0);
|
dupFd = fcntl(fenceFD, F_DUPFD_CLOEXEC, 0);
|
||||||
if (dupFd < 0) {
|
if (dupFd < 0) {
|
||||||
Debug::log(ERR, "createEGLSync: dup failed");
|
Debug::log(ERR, "createEGLSync: dup failed");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -2889,8 +2889,18 @@ SP<CEGLSync> CHyprOpenGLImpl::createEGLSync(int fenceFD) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we need to flush otherwise we might not get a valid fd
|
||||||
|
glFlush();
|
||||||
|
|
||||||
|
int fd = g_pHyprOpenGL->m_sProc.eglDupNativeFenceFDANDROID(g_pHyprOpenGL->m_pEglDisplay, sync);
|
||||||
|
if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
|
||||||
|
Debug::log(ERR, "eglDupNativeFenceFDANDROID failed");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
auto eglsync = SP<CEGLSync>(new CEGLSync);
|
auto eglsync = SP<CEGLSync>(new CEGLSync);
|
||||||
eglsync->sync = sync;
|
eglsync->sync = sync;
|
||||||
|
eglsync->m_iFd = fd;
|
||||||
return eglsync;
|
return eglsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2983,19 +2993,13 @@ CEGLSync::~CEGLSync() {
|
||||||
|
|
||||||
if (g_pHyprOpenGL->m_sProc.eglDestroySyncKHR(g_pHyprOpenGL->m_pEglDisplay, sync) != EGL_TRUE)
|
if (g_pHyprOpenGL->m_sProc.eglDestroySyncKHR(g_pHyprOpenGL->m_pEglDisplay, sync) != EGL_TRUE)
|
||||||
Debug::log(ERR, "eglDestroySyncKHR failed");
|
Debug::log(ERR, "eglDestroySyncKHR failed");
|
||||||
|
|
||||||
|
if (m_iFd >= 0)
|
||||||
|
close(m_iFd);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CEGLSync::dupFenceFD() {
|
int CEGLSync::fd() {
|
||||||
if (sync == EGL_NO_SYNC_KHR)
|
return m_iFd;
|
||||||
return -1;
|
|
||||||
|
|
||||||
int fd = g_pHyprOpenGL->m_sProc.eglDupNativeFenceFDANDROID(g_pHyprOpenGL->m_pEglDisplay, sync);
|
|
||||||
if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
|
|
||||||
Debug::log(ERR, "eglDupNativeFenceFDANDROID failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEGLSync::wait() {
|
bool CEGLSync::wait() {
|
||||||
|
|
|
@ -131,12 +131,14 @@ class CEGLSync {
|
||||||
|
|
||||||
EGLSyncKHR sync = nullptr;
|
EGLSyncKHR sync = nullptr;
|
||||||
|
|
||||||
int dupFenceFD();
|
int fd();
|
||||||
bool wait();
|
bool wait();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CEGLSync() = default;
|
CEGLSync() = default;
|
||||||
|
|
||||||
|
int m_iFd = -1;
|
||||||
|
|
||||||
friend class CHyprOpenGLImpl;
|
friend class CHyprOpenGLImpl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
#include "Renderer.hpp"
|
#include "Renderer.hpp"
|
||||||
#include "../Compositor.hpp"
|
#include "../Compositor.hpp"
|
||||||
#include "../helpers/math/Math.hpp"
|
#include "../helpers/math/Math.hpp"
|
||||||
|
#include "../helpers/ScopeGuard.hpp"
|
||||||
|
#include "../helpers/sync/SyncReleaser.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <aquamarine/output/Output.hpp>
|
#include <aquamarine/output/Output.hpp>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
#include "../config/ConfigValue.hpp"
|
#include "../config/ConfigValue.hpp"
|
||||||
#include "../managers/CursorManager.hpp"
|
#include "../managers/CursorManager.hpp"
|
||||||
#include "../managers/PointerManager.hpp"
|
#include "../managers/PointerManager.hpp"
|
||||||
|
@ -115,8 +118,8 @@ static void renderSurface(SP<CWLSurfaceResource> surface, int x, int y, void* da
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// explicit sync: wait for the timeline, if any
|
// explicit sync: wait for the timeline, if any
|
||||||
if (surface->syncobj && surface->syncobj->acquireTimeline) {
|
if (surface->syncobj && surface->syncobj->current.acquireTimeline) {
|
||||||
if (!g_pHyprOpenGL->waitForTimelinePoint(surface->syncobj->acquireTimeline->timeline, surface->syncobj->acquirePoint)) {
|
if (!g_pHyprOpenGL->waitForTimelinePoint(surface->syncobj->current.acquireTimeline->timeline, surface->syncobj->current.acquirePoint)) {
|
||||||
Debug::log(ERR, "Renderer: failed to wait for explicit timeline");
|
Debug::log(ERR, "Renderer: failed to wait for explicit timeline");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1095,7 +1098,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
|
||||||
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
|
static auto PDEBUGOVERLAY = CConfigValue<Hyprlang::INT>("debug:overlay");
|
||||||
static auto PDAMAGETRACKINGMODE = CConfigValue<Hyprlang::INT>("debug:damage_tracking");
|
static auto PDAMAGETRACKINGMODE = CConfigValue<Hyprlang::INT>("debug:damage_tracking");
|
||||||
static auto PDAMAGEBLINK = CConfigValue<Hyprlang::INT>("debug:damage_blink");
|
static auto PDAMAGEBLINK = CConfigValue<Hyprlang::INT>("debug:damage_blink");
|
||||||
static auto PNODIRECTSCANOUT = CConfigValue<Hyprlang::INT>("misc:no_direct_scanout");
|
static auto PDIRECTSCANOUT = CConfigValue<Hyprlang::INT>("render:direct_scanout");
|
||||||
static auto PVFR = CConfigValue<Hyprlang::INT>("misc:vfr");
|
static auto PVFR = CConfigValue<Hyprlang::INT>("misc:vfr");
|
||||||
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
|
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
|
||||||
static auto PANIMENABLED = CConfigValue<Hyprlang::INT>("animations:enabled");
|
static auto PANIMENABLED = CConfigValue<Hyprlang::INT>("animations:enabled");
|
||||||
|
@ -1195,7 +1198,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
|
||||||
|
|
||||||
pMonitor->tearingState.activelyTearing = shouldTear;
|
pMonitor->tearingState.activelyTearing = shouldTear;
|
||||||
|
|
||||||
if (!*PNODIRECTSCANOUT && !shouldTear) {
|
if (*PDIRECTSCANOUT && !shouldTear) {
|
||||||
if (pMonitor->attemptDirectScanout()) {
|
if (pMonitor->attemptDirectScanout()) {
|
||||||
return;
|
return;
|
||||||
} else if (!pMonitor->lastScanout.expired()) {
|
} else if (!pMonitor->lastScanout.expired()) {
|
||||||
|
@ -1403,39 +1406,21 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CHyprRenderer::commitPendingAndDoExplicitSync(CMonitor* pMonitor) {
|
bool CHyprRenderer::commitPendingAndDoExplicitSync(CMonitor* pMonitor) {
|
||||||
static auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("experimental:explicit_sync");
|
|
||||||
|
|
||||||
// apply timelines for explicit sync
|
// apply timelines for explicit sync
|
||||||
|
// save inFD otherwise reset will reset it
|
||||||
|
auto inFD = pMonitor->output->state->state().explicitInFence;
|
||||||
pMonitor->output->state->resetExplicitFences();
|
pMonitor->output->state->resetExplicitFences();
|
||||||
|
if (inFD >= 0)
|
||||||
bool anyExplicit = !explicitPresented.empty();
|
pMonitor->output->state->setExplicitInFence(inFD);
|
||||||
if (anyExplicit) {
|
|
||||||
Debug::log(TRACE, "Explicit sync presented begin");
|
|
||||||
auto inFence = pMonitor->inTimeline->exportAsSyncFileFD(pMonitor->lastWaitPoint);
|
|
||||||
if (inFence < 0)
|
|
||||||
Debug::log(ERR, "Export lastWaitPoint {} as sync explicitInFence failed", pMonitor->lastWaitPoint);
|
|
||||||
|
|
||||||
pMonitor->output->state->setExplicitInFence(inFence);
|
|
||||||
|
|
||||||
for (auto& e : explicitPresented) {
|
|
||||||
Debug::log(TRACE, "Explicit sync presented releasePoint {}", e->syncobj && e->syncobj->releaseTimeline ? e->syncobj->releasePoint : -1);
|
|
||||||
if (!e->syncobj || !e->syncobj->releaseTimeline)
|
|
||||||
continue;
|
|
||||||
e->syncobj->releaseTimeline->timeline->transfer(pMonitor->outTimeline, pMonitor->commitSeq, e->syncobj->releasePoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
explicitPresented.clear();
|
|
||||||
auto outFence = pMonitor->outTimeline->exportAsSyncFileFD(pMonitor->commitSeq);
|
|
||||||
if (outFence < 0)
|
|
||||||
Debug::log(ERR, "Export commitSeq {} as sync explicitOutFence failed", pMonitor->commitSeq);
|
|
||||||
|
|
||||||
pMonitor->output->state->setExplicitOutFence(outFence);
|
|
||||||
Debug::log(TRACE, "Explicit sync presented end");
|
|
||||||
}
|
|
||||||
|
|
||||||
pMonitor->lastWaitPoint = 0;
|
|
||||||
|
|
||||||
bool ok = pMonitor->state.commit();
|
bool ok = pMonitor->state.commit();
|
||||||
|
if (!ok) {
|
||||||
|
if (inFD >= 0) {
|
||||||
|
Debug::log(TRACE, "Monitor state commit failed, retrying without a fence");
|
||||||
|
pMonitor->output->state->resetExplicitFences();
|
||||||
|
ok = pMonitor->state.commit();
|
||||||
|
}
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
Debug::log(TRACE, "Monitor state commit failed");
|
Debug::log(TRACE, "Monitor state commit failed");
|
||||||
// rollback the buffer to avoid writing to the front buffer that is being
|
// rollback the buffer to avoid writing to the front buffer that is being
|
||||||
|
@ -1443,18 +1428,39 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(CMonitor* pMonitor) {
|
||||||
pMonitor->output->swapchain->rollback();
|
pMonitor->output->swapchain->rollback();
|
||||||
pMonitor->damage.damageEntire();
|
pMonitor->damage.damageEntire();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!*PENABLEEXPLICIT)
|
auto explicitOptions = getExplicitSyncSettings();
|
||||||
|
|
||||||
|
if (!explicitOptions.explicitEnabled)
|
||||||
return ok;
|
return ok;
|
||||||
|
|
||||||
if (pMonitor->output->state->state().explicitInFence >= 0)
|
if (inFD >= 0)
|
||||||
close(pMonitor->output->state->state().explicitInFence);
|
close(inFD);
|
||||||
|
|
||||||
if (pMonitor->output->state->state().explicitOutFence >= 0) {
|
if (pMonitor->output->state->state().explicitOutFence >= 0) {
|
||||||
if (ok)
|
Debug::log(TRACE, "Aquamarine returned an explicit out fence at {}", pMonitor->output->state->state().explicitOutFence);
|
||||||
pMonitor->outTimeline->importFromSyncFileFD(pMonitor->commitSeq, pMonitor->output->state->state().explicitOutFence);
|
|
||||||
close(pMonitor->output->state->state().explicitOutFence);
|
close(pMonitor->output->state->state().explicitOutFence);
|
||||||
|
} else
|
||||||
|
Debug::log(TRACE, "Aquamarine did not return an explicit out fence");
|
||||||
|
|
||||||
|
Debug::log(TRACE, "Explicit: {} presented", explicitPresented.size());
|
||||||
|
auto sync = g_pHyprOpenGL->createEGLSync(-1);
|
||||||
|
|
||||||
|
if (!sync)
|
||||||
|
Debug::log(TRACE, "Explicit: can't add sync, EGLSync failed");
|
||||||
|
else {
|
||||||
|
for (auto& e : explicitPresented) {
|
||||||
|
if (!e->current.buffer || !e->current.buffer->releaser)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
e->current.buffer->releaser->addReleaseSync(sync);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
explicitPresented.clear();
|
||||||
|
|
||||||
|
pMonitor->output->state->resetExplicitFences();
|
||||||
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
@ -1898,6 +1904,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
|
||||||
pMonitor->currentMode = nullptr;
|
pMonitor->currentMode = nullptr;
|
||||||
|
|
||||||
pMonitor->output->state->setFormat(DRM_FORMAT_XRGB8888);
|
pMonitor->output->state->setFormat(DRM_FORMAT_XRGB8888);
|
||||||
|
pMonitor->output->state->resetExplicitFences();
|
||||||
|
|
||||||
bool autoScale = false;
|
bool autoScale = false;
|
||||||
|
|
||||||
|
@ -2643,10 +2650,16 @@ bool CHyprRenderer::beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode
|
||||||
void CHyprRenderer::endRender() {
|
void CHyprRenderer::endRender() {
|
||||||
const auto PMONITOR = g_pHyprOpenGL->m_RenderData.pMonitor;
|
const auto PMONITOR = g_pHyprOpenGL->m_RenderData.pMonitor;
|
||||||
static auto PNVIDIAANTIFLICKER = CConfigValue<Hyprlang::INT>("opengl:nvidia_anti_flicker");
|
static auto PNVIDIAANTIFLICKER = CConfigValue<Hyprlang::INT>("opengl:nvidia_anti_flicker");
|
||||||
static auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("experimental:explicit_sync");
|
|
||||||
|
|
||||||
PMONITOR->commitSeq++;
|
PMONITOR->commitSeq++;
|
||||||
|
|
||||||
|
auto cleanup = CScopeGuard([this]() {
|
||||||
|
if (m_pCurrentRenderbuffer)
|
||||||
|
m_pCurrentRenderbuffer->unbind();
|
||||||
|
m_pCurrentRenderbuffer = nullptr;
|
||||||
|
m_pCurrentBuffer = nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
if (m_eRenderMode != RENDER_MODE_TO_BUFFER_READ_ONLY)
|
if (m_eRenderMode != RENDER_MODE_TO_BUFFER_READ_ONLY)
|
||||||
g_pHyprOpenGL->end();
|
g_pHyprOpenGL->end();
|
||||||
else {
|
else {
|
||||||
|
@ -2661,35 +2674,28 @@ void CHyprRenderer::endRender() {
|
||||||
if (m_eRenderMode == RENDER_MODE_NORMAL) {
|
if (m_eRenderMode == RENDER_MODE_NORMAL) {
|
||||||
PMONITOR->output->state->setBuffer(m_pCurrentBuffer);
|
PMONITOR->output->state->setBuffer(m_pCurrentBuffer);
|
||||||
|
|
||||||
if (PMONITOR->inTimeline && *PENABLEEXPLICIT) {
|
auto explicitOptions = getExplicitSyncSettings();
|
||||||
|
|
||||||
|
if (PMONITOR->inTimeline && explicitOptions.explicitEnabled && explicitOptions.explicitKMSEnabled) {
|
||||||
auto sync = g_pHyprOpenGL->createEGLSync(-1);
|
auto sync = g_pHyprOpenGL->createEGLSync(-1);
|
||||||
if (!sync) {
|
if (!sync) {
|
||||||
m_pCurrentRenderbuffer->unbind();
|
|
||||||
m_pCurrentRenderbuffer = nullptr;
|
|
||||||
m_pCurrentBuffer = nullptr;
|
|
||||||
Debug::log(ERR, "renderer: couldn't create an EGLSync for out in endRender");
|
Debug::log(ERR, "renderer: couldn't create an EGLSync for out in endRender");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto dupedfd = sync->dupFenceFD();
|
bool ok = PMONITOR->inTimeline->importFromSyncFileFD(PMONITOR->commitSeq, sync->fd());
|
||||||
sync.reset();
|
|
||||||
if (dupedfd < 0) {
|
|
||||||
m_pCurrentRenderbuffer->unbind();
|
|
||||||
m_pCurrentRenderbuffer = nullptr;
|
|
||||||
m_pCurrentBuffer = nullptr;
|
|
||||||
Debug::log(ERR, "renderer: couldn't dup an EGLSync fence for out in endRender");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ok = PMONITOR->inTimeline->importFromSyncFileFD(PMONITOR->commitSeq, dupedfd);
|
|
||||||
close(dupedfd);
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
m_pCurrentRenderbuffer->unbind();
|
|
||||||
m_pCurrentRenderbuffer = nullptr;
|
|
||||||
m_pCurrentBuffer = nullptr;
|
|
||||||
Debug::log(ERR, "renderer: couldn't import from sync file fd in endRender");
|
Debug::log(ERR, "renderer: couldn't import from sync file fd in endRender");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto fd = PMONITOR->inTimeline->exportAsSyncFileFD(PMONITOR->commitSeq);
|
||||||
|
if (fd <= 0) {
|
||||||
|
Debug::log(ERR, "renderer: couldn't export from sync timeline in endRender");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PMONITOR->output->state->setExplicitInFence(fd);
|
||||||
} else {
|
} else {
|
||||||
if (isNvidia() && *PNVIDIAANTIFLICKER)
|
if (isNvidia() && *PNVIDIAANTIFLICKER)
|
||||||
glFinish();
|
glFinish();
|
||||||
|
@ -2697,11 +2703,6 @@ void CHyprRenderer::endRender() {
|
||||||
glFlush();
|
glFlush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_pCurrentRenderbuffer->unbind();
|
|
||||||
|
|
||||||
m_pCurrentRenderbuffer = nullptr;
|
|
||||||
m_pCurrentBuffer = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprRenderer::onRenderbufferDestroy(CRenderbuffer* rb) {
|
void CHyprRenderer::onRenderbufferDestroy(CRenderbuffer* rb) {
|
||||||
|
@ -2715,3 +2716,57 @@ SP<CRenderbuffer> CHyprRenderer::getCurrentRBO() {
|
||||||
bool CHyprRenderer::isNvidia() {
|
bool CHyprRenderer::isNvidia() {
|
||||||
return m_bNvidia;
|
return m_bNvidia;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SExplicitSyncSettings CHyprRenderer::getExplicitSyncSettings() {
|
||||||
|
static auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("render:explicit_sync");
|
||||||
|
static auto PENABLEEXPLICITKMS = CConfigValue<Hyprlang::INT>("render:explicit_sync_kms");
|
||||||
|
|
||||||
|
SExplicitSyncSettings settings;
|
||||||
|
settings.explicitEnabled = *PENABLEEXPLICIT;
|
||||||
|
settings.explicitKMSEnabled = *PENABLEEXPLICITKMS;
|
||||||
|
|
||||||
|
if (*PENABLEEXPLICIT == 2 /* auto */)
|
||||||
|
settings.explicitEnabled = true;
|
||||||
|
if (*PENABLEEXPLICITKMS == 2 /* auto */) {
|
||||||
|
if (!m_bNvidia)
|
||||||
|
settings.explicitKMSEnabled = true;
|
||||||
|
else {
|
||||||
|
|
||||||
|
// check nvidia version. Explicit KMS is supported in >=560
|
||||||
|
// in the case of an error, driverMajor will stay 0 and explicit KMS will be disabled
|
||||||
|
int driverMajor = 0;
|
||||||
|
|
||||||
|
static bool once = true;
|
||||||
|
if (once) {
|
||||||
|
once = false;
|
||||||
|
|
||||||
|
Debug::log(LOG, "Renderer: checking for explicit KMS support for nvidia");
|
||||||
|
|
||||||
|
if (std::filesystem::exists("/sys/module/nvidia_drm/version")) {
|
||||||
|
Debug::log(LOG, "Renderer: Nvidia version file exists");
|
||||||
|
|
||||||
|
std::ifstream ifs("/sys/module/nvidia_drm/version");
|
||||||
|
if (ifs.good()) {
|
||||||
|
try {
|
||||||
|
std::string driverInfo((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
|
||||||
|
|
||||||
|
Debug::log(LOG, "Renderer: Read nvidia version {}", driverInfo);
|
||||||
|
|
||||||
|
CVarList ver(driverInfo, 0, '.', true);
|
||||||
|
driverMajor = std::stoi(ver[0]);
|
||||||
|
|
||||||
|
Debug::log(LOG, "Renderer: Parsed nvidia major version: {}", driverMajor);
|
||||||
|
|
||||||
|
} catch (std::exception& e) { settings.explicitKMSEnabled = false; }
|
||||||
|
|
||||||
|
ifs.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.explicitKMSEnabled = driverMajor >= 560;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
|
@ -39,6 +39,10 @@ class CToplevelExportProtocolManager;
|
||||||
class CInputManager;
|
class CInputManager;
|
||||||
struct SSessionLockSurface;
|
struct SSessionLockSurface;
|
||||||
|
|
||||||
|
struct SExplicitSyncSettings {
|
||||||
|
bool explicitEnabled = false, explicitKMSEnabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
class CHyprRenderer {
|
class CHyprRenderer {
|
||||||
public:
|
public:
|
||||||
CHyprRenderer();
|
CHyprRenderer();
|
||||||
|
@ -73,6 +77,7 @@ class CHyprRenderer {
|
||||||
bool isNvidia();
|
bool isNvidia();
|
||||||
void makeEGLCurrent();
|
void makeEGLCurrent();
|
||||||
void unsetEGL();
|
void unsetEGL();
|
||||||
|
SExplicitSyncSettings getExplicitSyncSettings();
|
||||||
|
|
||||||
// if RENDER_MODE_NORMAL, provided damage will be written to.
|
// if RENDER_MODE_NORMAL, provided damage will be written to.
|
||||||
// otherwise, it will be the one used.
|
// otherwise, it will be the one used.
|
||||||
|
@ -142,6 +147,7 @@ class CHyprRenderer {
|
||||||
friend class CToplevelExportFrame;
|
friend class CToplevelExportFrame;
|
||||||
friend class CInputManager;
|
friend class CInputManager;
|
||||||
friend class CPointerManager;
|
friend class CPointerManager;
|
||||||
|
friend class CMonitor;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::unique_ptr<CHyprRenderer> g_pHyprRenderer;
|
inline std::unique_ptr<CHyprRenderer> g_pHyprRenderer;
|
||||||
|
|
Loading…
Reference in a new issue