mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-08-13 11:05:46 -07:00
Renderer: Implement new render scheduling (#10936)
Implements a new render scheduling method, where we triple buffer when necessary. Enabled by default, improves FPS on underpowered devices. --------- Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
This commit is contained in:
@@ -104,7 +104,7 @@ find_package(Threads REQUIRED)
|
|||||||
set(GLES_VERSION "GLES3")
|
set(GLES_VERSION "GLES3")
|
||||||
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
|
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
|
||||||
|
|
||||||
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.8.0)
|
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.9.0)
|
||||||
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
|
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
|
||||||
pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7)
|
pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7)
|
||||||
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.7.0)
|
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.7.0)
|
||||||
|
@@ -31,7 +31,7 @@ if cpp_compiler.check_header('execinfo.h')
|
|||||||
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
|
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
aquamarine = dependency('aquamarine', version: '>=0.8.0')
|
aquamarine = dependency('aquamarine', version: '>=0.9.0')
|
||||||
hyprcursor = dependency('hyprcursor', version: '>=0.1.7')
|
hyprcursor = dependency('hyprcursor', version: '>=0.1.7')
|
||||||
hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.3')
|
hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.3')
|
||||||
hyprlang = dependency('hyprlang', version: '>= 0.3.2')
|
hyprlang = dependency('hyprlang', version: '>= 0.3.2')
|
||||||
|
@@ -62,6 +62,7 @@
|
|||||||
#include "hyprerror/HyprError.hpp"
|
#include "hyprerror/HyprError.hpp"
|
||||||
#include "debug/HyprNotificationOverlay.hpp"
|
#include "debug/HyprNotificationOverlay.hpp"
|
||||||
#include "debug/HyprDebugOverlay.hpp"
|
#include "debug/HyprDebugOverlay.hpp"
|
||||||
|
#include "helpers/MonitorFrameScheduler.hpp"
|
||||||
|
|
||||||
#include <hyprutils/string/String.hpp>
|
#include <hyprutils/string/String.hpp>
|
||||||
#include <aquamarine/input/Input.hpp>
|
#include <aquamarine/input/Input.hpp>
|
||||||
@@ -3097,7 +3098,7 @@ void CCompositor::onNewMonitor(SP<Aquamarine::IOutput> output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_pHyprRenderer->damageMonitor(PNEWMONITOR);
|
g_pHyprRenderer->damageMonitor(PNEWMONITOR);
|
||||||
PNEWMONITOR->onMonitorFrame();
|
PNEWMONITOR->m_frameScheduler->onFrame();
|
||||||
|
|
||||||
if (PROTO::colorManagement && shouldChangePreferredImageDescription()) {
|
if (PROTO::colorManagement && shouldChangePreferredImageDescription()) {
|
||||||
Debug::log(ERR, "FIXME: color management protocol is enabled, need a preferred image description id");
|
Debug::log(ERR, "FIXME: color management protocol is enabled, need a preferred image description id");
|
||||||
|
@@ -1488,6 +1488,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||||||
.type = CONFIG_OPTION_INT,
|
.type = CONFIG_OPTION_INT,
|
||||||
.data = SConfigOptionDescription::SRangeData{.value = 1, .min = 0, .max = 2},
|
.data = SConfigOptionDescription::SRangeData{.value = 1, .min = 0, .max = 2},
|
||||||
},
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "render:new_render_scheduling",
|
||||||
|
.description = "enable new render scheduling, which should improve FPS on underpowered devices. This does not add latency when your PC can keep up.",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{true},
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* cursor:
|
* cursor:
|
||||||
@@ -1897,16 +1903,21 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||||||
.type = CONFIG_OPTION_BOOL,
|
.type = CONFIG_OPTION_BOOL,
|
||||||
.data = SConfigOptionDescription::SBoolData{true},
|
.data = SConfigOptionDescription::SBoolData{true},
|
||||||
},
|
},
|
||||||
SConfigOptionDescription{
|
|
||||||
.value = "experimental:xx_color_management_v4",
|
|
||||||
.description = "enable color management protocol",
|
|
||||||
.type = CONFIG_OPTION_BOOL,
|
|
||||||
.data = SConfigOptionDescription::SBoolData{false},
|
|
||||||
},
|
|
||||||
SConfigOptionDescription{
|
SConfigOptionDescription{
|
||||||
.value = "master:always_keep_position",
|
.value = "master:always_keep_position",
|
||||||
.description = "whether to keep the master window in its configured position when there are no slave windows",
|
.description = "whether to keep the master window in its configured position when there are no slave windows",
|
||||||
.type = CONFIG_OPTION_BOOL,
|
.type = CONFIG_OPTION_BOOL,
|
||||||
.data = SConfigOptionDescription::SBoolData{false},
|
.data = SConfigOptionDescription::SBoolData{false},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Experimental
|
||||||
|
*/
|
||||||
|
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "experimental:xx_color_management_v4",
|
||||||
|
.description = "enable color management protocol",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{false},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@@ -749,6 +749,7 @@ CConfigManager::CConfigManager() {
|
|||||||
registerConfigVar("render:cm_enabled", Hyprlang::INT{1});
|
registerConfigVar("render:cm_enabled", Hyprlang::INT{1});
|
||||||
registerConfigVar("render:send_content_type", Hyprlang::INT{1});
|
registerConfigVar("render:send_content_type", Hyprlang::INT{1});
|
||||||
registerConfigVar("render:cm_auto_hdr", Hyprlang::INT{1});
|
registerConfigVar("render:cm_auto_hdr", Hyprlang::INT{1});
|
||||||
|
registerConfigVar("render:new_render_scheduling", Hyprlang::INT{1});
|
||||||
|
|
||||||
registerConfigVar("ecosystem:no_update_news", Hyprlang::INT{0});
|
registerConfigVar("ecosystem:no_update_news", Hyprlang::INT{0});
|
||||||
registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0});
|
registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0});
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
#include "./math/Math.hpp"
|
#include "./math/Math.hpp"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
constexpr static int DAMAGE_RING_PREVIOUS_LEN = 2;
|
constexpr static int DAMAGE_RING_PREVIOUS_LEN = 3;
|
||||||
|
|
||||||
class CDamageRing {
|
class CDamageRing {
|
||||||
public:
|
public:
|
||||||
|
@@ -29,6 +29,7 @@
|
|||||||
#include <aquamarine/output/Output.hpp>
|
#include <aquamarine/output/Output.hpp>
|
||||||
#include "debug/Log.hpp"
|
#include "debug/Log.hpp"
|
||||||
#include "debug/HyprNotificationOverlay.hpp"
|
#include "debug/HyprNotificationOverlay.hpp"
|
||||||
|
#include "MonitorFrameScheduler.hpp"
|
||||||
#include <hyprutils/string/String.hpp>
|
#include <hyprutils/string/String.hpp>
|
||||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -67,7 +68,7 @@ void CMonitor::onConnect(bool noRule) {
|
|||||||
|
|
||||||
g_pEventLoopManager->doLater([] { g_pConfigManager->ensurePersistentWorkspacesPresent(); });
|
g_pEventLoopManager->doLater([] { g_pConfigManager->ensurePersistentWorkspacesPresent(); });
|
||||||
|
|
||||||
m_listeners.frame = m_output->events.frame.registerListener([this](std::any d) { onMonitorFrame(); });
|
m_listeners.frame = m_output->events.frame.registerListener([this](std::any d) { m_frameScheduler->onFrame(); });
|
||||||
m_listeners.commit = m_output->events.commit.registerListener([this](std::any d) {
|
m_listeners.commit = m_output->events.commit.registerListener([this](std::any d) {
|
||||||
if (true) { // FIXME: E->state->committed & WLR_OUTPUT_STATE_BUFFER
|
if (true) { // FIXME: E->state->committed & WLR_OUTPUT_STATE_BUFFER
|
||||||
PROTO::screencopy->onOutputCommit(m_self.lock());
|
PROTO::screencopy->onOutputCommit(m_self.lock());
|
||||||
@@ -92,6 +93,8 @@ void CMonitor::onConnect(bool noRule) {
|
|||||||
PROTO::presentation->onPresented(m_self.lock(), Time::steadyNow(), E.refresh, E.seq, E.flags);
|
PROTO::presentation->onPresented(m_self.lock(), Time::steadyNow(), E.refresh, E.seq, E.flags);
|
||||||
else
|
else
|
||||||
PROTO::presentation->onPresented(m_self.lock(), Time::fromTimespec(E.when), E.refresh, E.seq, E.flags);
|
PROTO::presentation->onPresented(m_self.lock(), Time::fromTimespec(E.when), E.refresh, E.seq, E.flags);
|
||||||
|
|
||||||
|
m_frameScheduler->onPresented();
|
||||||
});
|
});
|
||||||
|
|
||||||
m_listeners.destroy = m_output->events.destroy.registerListener([this](std::any d) {
|
m_listeners.destroy = m_output->events.destroy.registerListener([this](std::any d) {
|
||||||
@@ -134,6 +137,8 @@ void CMonitor::onConnect(bool noRule) {
|
|||||||
applyMonitorRule(&rule);
|
applyMonitorRule(&rule);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_frameScheduler = makeUnique<CMonitorFrameScheduler>(m_self.lock());
|
||||||
|
|
||||||
m_tearingState.canTear = m_output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM;
|
m_tearingState.canTear = m_output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM;
|
||||||
|
|
||||||
m_name = m_output->name;
|
m_name = m_output->name;
|
||||||
@@ -306,6 +311,8 @@ void CMonitor::onDisconnect(bool destroy) {
|
|||||||
m_renderTimer = nullptr;
|
m_renderTimer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_frameScheduler.reset();
|
||||||
|
|
||||||
if (!m_enabled || g_pCompositor->m_isShuttingDown)
|
if (!m_enabled || g_pCompositor->m_isShuttingDown)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -1562,69 +1569,6 @@ void CMonitor::debugLastPresentation(const std::string& message) {
|
|||||||
m_lastPresentationTimer.getMillis() > 0 ? 1000.0f / m_lastPresentationTimer.getMillis() : 0.0f);
|
m_lastPresentationTimer.getMillis() > 0 ? 1000.0f / m_lastPresentationTimer.getMillis() : 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMonitor::onMonitorFrame() {
|
|
||||||
if ((g_pCompositor->m_aqBackend->hasSession() && !g_pCompositor->m_aqBackend->session->active) || !g_pCompositor->m_sessionActive || g_pCompositor->m_unsafeState) {
|
|
||||||
Debug::log(WARN, "Attempted to render frame on inactive session!");
|
|
||||||
|
|
||||||
if (g_pCompositor->m_unsafeState && std::ranges::any_of(g_pCompositor->m_monitors.begin(), g_pCompositor->m_monitors.end(), [&](auto& m) {
|
|
||||||
return m->m_output != g_pCompositor->m_unsafeOutput->m_output;
|
|
||||||
})) {
|
|
||||||
// restore from unsafe state
|
|
||||||
g_pCompositor->leaveUnsafeState();
|
|
||||||
}
|
|
||||||
|
|
||||||
return; // cannot draw on session inactive (different tty)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
g_pHyprRenderer->recheckSolitaryForMonitor(m_self.lock());
|
|
||||||
|
|
||||||
m_tearingState.busy = false;
|
|
||||||
|
|
||||||
if (m_tearingState.activelyTearing && m_solitaryClient.lock() /* can be invalidated by a recheck */) {
|
|
||||||
|
|
||||||
if (!m_tearingState.frameScheduledWhileBusy)
|
|
||||||
return; // we did not schedule a frame yet to be displayed, but we are tearing. Why render?
|
|
||||||
|
|
||||||
m_tearingState.nextRenderTorn = true;
|
|
||||||
m_tearingState.frameScheduledWhileBusy = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static auto PENABLERAT = CConfigValue<Hyprlang::INT>("misc:render_ahead_of_time");
|
|
||||||
static auto PRATSAFE = CConfigValue<Hyprlang::INT>("misc:render_ahead_safezone");
|
|
||||||
|
|
||||||
m_lastPresentationTimer.reset();
|
|
||||||
|
|
||||||
if (*PENABLERAT && !m_tearingState.nextRenderTorn) {
|
|
||||||
if (!m_ratsScheduled) {
|
|
||||||
// render
|
|
||||||
g_pHyprRenderer->renderMonitor(m_self.lock());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ratsScheduled = false;
|
|
||||||
|
|
||||||
const auto& [avg, max, min] = g_pHyprRenderer->getRenderTimes(m_self.lock());
|
|
||||||
|
|
||||||
if (max + *PRATSAFE > 1000.0 / m_refreshRate)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto MSLEFT = (1000.0 / m_refreshRate) - m_lastPresentationTimer.getMillis();
|
|
||||||
|
|
||||||
m_ratsScheduled = true;
|
|
||||||
|
|
||||||
const auto ESTRENDERTIME = std::ceil(avg + *PRATSAFE);
|
|
||||||
const auto TIMETOSLEEP = std::floor(MSLEFT - ESTRENDERTIME);
|
|
||||||
|
|
||||||
if (MSLEFT < 1 || MSLEFT < ESTRENDERTIME || TIMETOSLEEP < 1)
|
|
||||||
g_pHyprRenderer->renderMonitor(m_self.lock());
|
|
||||||
else
|
|
||||||
wl_event_source_timer_update(m_renderTimer, TIMETOSLEEP);
|
|
||||||
} else
|
|
||||||
g_pHyprRenderer->renderMonitor(m_self.lock());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CMonitor::onCursorMovedOnMonitor() {
|
void CMonitor::onCursorMovedOnMonitor() {
|
||||||
if (!m_tearingState.activelyTearing || !m_solitaryClient || !g_pHyprRenderer->shouldRenderCursor())
|
if (!m_tearingState.activelyTearing || !m_solitaryClient || !g_pHyprRenderer->shouldRenderCursor())
|
||||||
return;
|
return;
|
||||||
@@ -1717,7 +1661,7 @@ bool CMonitorState::updateSwapchain() {
|
|||||||
}
|
}
|
||||||
options.format = m_owner->m_drmFormat;
|
options.format = m_owner->m_drmFormat;
|
||||||
options.scanout = true;
|
options.scanout = true;
|
||||||
options.length = 2;
|
options.length = 3;
|
||||||
options.size = MODE->pixelSize;
|
options.size = MODE->pixelSize;
|
||||||
return m_owner->m_output->swapchain->reconfigure(options);
|
return m_owner->m_output->swapchain->reconfigure(options);
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,8 @@
|
|||||||
#include <aquamarine/allocator/Swapchain.hpp>
|
#include <aquamarine/allocator/Swapchain.hpp>
|
||||||
#include <hyprutils/os/FileDescriptor.hpp>
|
#include <hyprutils/os/FileDescriptor.hpp>
|
||||||
|
|
||||||
|
class CMonitorFrameScheduler;
|
||||||
|
|
||||||
// Enum for the different types of auto directions, e.g. auto-left, auto-up.
|
// Enum for the different types of auto directions, e.g. auto-left, auto-up.
|
||||||
enum eAutoDirs : uint8_t {
|
enum eAutoDirs : uint8_t {
|
||||||
DIR_AUTO_NONE = 0, /* None will be treated as right. */
|
DIR_AUTO_NONE = 0, /* None will be treated as right. */
|
||||||
@@ -160,6 +162,8 @@ class CMonitor {
|
|||||||
|
|
||||||
PHLMONITORREF m_self;
|
PHLMONITORREF m_self;
|
||||||
|
|
||||||
|
UP<CMonitorFrameScheduler> m_frameScheduler;
|
||||||
|
|
||||||
// mirroring
|
// mirroring
|
||||||
PHLMONITORREF m_mirrorOf;
|
PHLMONITORREF m_mirrorOf;
|
||||||
std::vector<PHLMONITORREF> m_mirrors;
|
std::vector<PHLMONITORREF> m_mirrors;
|
||||||
@@ -228,7 +232,6 @@ class CMonitor {
|
|||||||
void onCursorMovedOnMonitor();
|
void onCursorMovedOnMonitor();
|
||||||
|
|
||||||
void debugLastPresentation(const std::string& message);
|
void debugLastPresentation(const std::string& message);
|
||||||
void onMonitorFrame();
|
|
||||||
|
|
||||||
bool supportsWideColor();
|
bool supportsWideColor();
|
||||||
bool supportsHDR();
|
bool supportsHDR();
|
||||||
|
130
src/helpers/MonitorFrameScheduler.cpp
Normal file
130
src/helpers/MonitorFrameScheduler.cpp
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
#include "MonitorFrameScheduler.hpp"
|
||||||
|
#include "../config/ConfigValue.hpp"
|
||||||
|
#include "../Compositor.hpp"
|
||||||
|
#include "../render/Renderer.hpp"
|
||||||
|
#include "../managers/eventLoop/EventLoopManager.hpp"
|
||||||
|
|
||||||
|
CMonitorFrameScheduler::CMonitorFrameScheduler(PHLMONITOR m) : m_monitor(m) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMonitorFrameScheduler::onSyncFired() {
|
||||||
|
static auto PENABLENEW = CConfigValue<Hyprlang::INT>("render:new_render_scheduling");
|
||||||
|
|
||||||
|
if (!*PENABLENEW)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Sync fired: reset submitted state, set as rendered. Check the last render time. If we are running
|
||||||
|
// late, we will instantly render here.
|
||||||
|
|
||||||
|
if (std::chrono::duration_cast<std::chrono::microseconds>(hrc::now() - m_lastRenderBegun).count() / 1000.F < 1000.F / m_monitor->m_refreshRate) {
|
||||||
|
// we are in. Frame is valid. We can just render as normal.
|
||||||
|
Debug::log(TRACE, "CMonitorFrameScheduler: {} -> onSyncFired, didn't miss.", m_monitor->m_name);
|
||||||
|
m_renderAtFrame = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug::log(TRACE, "CMonitorFrameScheduler: {} -> onSyncFired, missed.", m_monitor->m_name);
|
||||||
|
|
||||||
|
// we are out. The frame is taking too long to render. Begin rendering immediately, but don't commit yet.
|
||||||
|
m_pendingThird = true;
|
||||||
|
m_renderAtFrame = false; // block frame rendering, we already scheduled
|
||||||
|
|
||||||
|
m_lastRenderBegun = hrc::now();
|
||||||
|
g_pHyprRenderer->renderMonitor(m_monitor.lock(), false);
|
||||||
|
onFinishRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMonitorFrameScheduler::onPresented() {
|
||||||
|
static auto PENABLENEW = CConfigValue<Hyprlang::INT>("render:new_render_scheduling");
|
||||||
|
|
||||||
|
if (!*PENABLENEW)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!m_pendingThird)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "CMonitorFrameScheduler: {} -> onPresented, missed, committing pending.", m_monitor->m_name);
|
||||||
|
|
||||||
|
m_pendingThird = false;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "CMonitorFrameScheduler: {} -> onPresented, missed, committing pending at the earliest convenience.", m_monitor->m_name);
|
||||||
|
|
||||||
|
m_pendingThird = false;
|
||||||
|
|
||||||
|
g_pEventLoopManager->doLater([m = m_monitor.lock()] {
|
||||||
|
g_pHyprRenderer->commitPendingAndDoExplicitSync(m); // commit the pending frame. If it didn't fire yet (is not rendered) it doesn't matter. Syncs will wait.
|
||||||
|
|
||||||
|
// schedule a frame: we might have some missed damage, which got cleared due to the above commit.
|
||||||
|
// TODO: this is not always necessary, but doesn't hurt in general. We likely won't hit this if nothing's happening anyways.
|
||||||
|
if (m->m_damage.hasChanged())
|
||||||
|
g_pCompositor->scheduleFrameForMonitor(m);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMonitorFrameScheduler::onFrame() {
|
||||||
|
static auto PENABLENEW = CConfigValue<Hyprlang::INT>("render:new_render_scheduling");
|
||||||
|
|
||||||
|
if (!canRender())
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_pHyprRenderer->recheckSolitaryForMonitor(m_monitor.lock());
|
||||||
|
|
||||||
|
m_monitor->m_tearingState.busy = false;
|
||||||
|
|
||||||
|
if (m_monitor->m_tearingState.activelyTearing && m_monitor->m_solitaryClient.lock() /* can be invalidated by a recheck */) {
|
||||||
|
|
||||||
|
if (!m_monitor->m_tearingState.frameScheduledWhileBusy)
|
||||||
|
return; // we did not schedule a frame yet to be displayed, but we are tearing. Why render?
|
||||||
|
|
||||||
|
m_monitor->m_tearingState.nextRenderTorn = true;
|
||||||
|
m_monitor->m_tearingState.frameScheduledWhileBusy = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*PENABLENEW) {
|
||||||
|
m_monitor->m_lastPresentationTimer.reset();
|
||||||
|
|
||||||
|
g_pHyprRenderer->renderMonitor(m_monitor.lock());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_renderAtFrame) {
|
||||||
|
Debug::log(TRACE, "CMonitorFrameScheduler: {} -> frame event, but m_renderAtFrame = false.", m_monitor->m_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug::log(TRACE, "CMonitorFrameScheduler: {} -> frame event, render = true, rendering normally.", m_monitor->m_name);
|
||||||
|
|
||||||
|
m_lastRenderBegun = hrc::now();
|
||||||
|
g_pHyprRenderer->renderMonitor(m_monitor.lock());
|
||||||
|
onFinishRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMonitorFrameScheduler::onFinishRender() {
|
||||||
|
m_sync = CEGLSync::create(); // this destroys the old sync
|
||||||
|
g_pEventLoopManager->doOnReadable(m_sync->fd().duplicate(), [this, mon = m_monitor] {
|
||||||
|
if (!m_monitor) // might've gotten destroyed
|
||||||
|
return;
|
||||||
|
onSyncFired();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CMonitorFrameScheduler::canRender() {
|
||||||
|
if ((g_pCompositor->m_aqBackend->hasSession() && !g_pCompositor->m_aqBackend->session->active) || !g_pCompositor->m_sessionActive || g_pCompositor->m_unsafeState) {
|
||||||
|
Debug::log(WARN, "Attempted to render frame on inactive session!");
|
||||||
|
|
||||||
|
if (g_pCompositor->m_unsafeState && std::ranges::any_of(g_pCompositor->m_monitors.begin(), g_pCompositor->m_monitors.end(), [&](auto& m) {
|
||||||
|
return m->m_output != g_pCompositor->m_unsafeOutput->m_output;
|
||||||
|
})) {
|
||||||
|
// restore from unsafe state
|
||||||
|
g_pCompositor->leaveUnsafeState();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // cannot draw on session inactive (different tty)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_monitor->m_enabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
35
src/helpers/MonitorFrameScheduler.hpp
Normal file
35
src/helpers/MonitorFrameScheduler.hpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Monitor.hpp"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
class CEGLSync;
|
||||||
|
|
||||||
|
class CMonitorFrameScheduler {
|
||||||
|
public:
|
||||||
|
using hrc = std::chrono::high_resolution_clock;
|
||||||
|
|
||||||
|
CMonitorFrameScheduler(PHLMONITOR m);
|
||||||
|
|
||||||
|
CMonitorFrameScheduler(const CMonitorFrameScheduler&) = delete;
|
||||||
|
CMonitorFrameScheduler(CMonitorFrameScheduler&&) = delete;
|
||||||
|
CMonitorFrameScheduler& operator=(const CMonitorFrameScheduler&) = delete;
|
||||||
|
CMonitorFrameScheduler& operator=(CMonitorFrameScheduler&&) = delete;
|
||||||
|
|
||||||
|
void onSyncFired();
|
||||||
|
void onPresented();
|
||||||
|
void onFrame();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool canRender();
|
||||||
|
void onFinishRender();
|
||||||
|
|
||||||
|
bool m_renderAtFrame = true;
|
||||||
|
bool m_pendingThird = false;
|
||||||
|
hrc::time_point m_lastRenderBegun;
|
||||||
|
|
||||||
|
PHLMONITORREF m_monitor;
|
||||||
|
|
||||||
|
UP<CEGLSync> m_sync;
|
||||||
|
};
|
@@ -1153,7 +1153,7 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
|
void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) {
|
||||||
static std::chrono::high_resolution_clock::time_point renderStart = std::chrono::high_resolution_clock::now();
|
static std::chrono::high_resolution_clock::time_point renderStart = std::chrono::high_resolution_clock::now();
|
||||||
static std::chrono::high_resolution_clock::time_point renderStartOverlay = std::chrono::high_resolution_clock::now();
|
static std::chrono::high_resolution_clock::time_point renderStartOverlay = std::chrono::high_resolution_clock::now();
|
||||||
static std::chrono::high_resolution_clock::time_point endRenderOverlay = std::chrono::high_resolution_clock::now();
|
static std::chrono::high_resolution_clock::time_point endRenderOverlay = std::chrono::high_resolution_clock::now();
|
||||||
@@ -1425,6 +1425,7 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
|
|||||||
pMonitor->m_output->state->setPresentationMode(shouldTear ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE :
|
pMonitor->m_output->state->setPresentationMode(shouldTear ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE :
|
||||||
Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);
|
Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);
|
||||||
|
|
||||||
|
if (commit)
|
||||||
commitPendingAndDoExplicitSync(pMonitor);
|
commitPendingAndDoExplicitSync(pMonitor);
|
||||||
|
|
||||||
if (shouldTear)
|
if (shouldTear)
|
||||||
@@ -2247,12 +2248,10 @@ bool CHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMod
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is a constant expression, as we always use double-buffering in our swapchain
|
int bufferAge = 0;
|
||||||
TODO: Rewrite the CDamageRing to take advantage of that maybe? It's made to support longer swapchains atm because we used to do wlroots */
|
|
||||||
static constexpr const int HL_BUFFER_AGE = 2;
|
|
||||||
|
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
m_currentBuffer = pMonitor->m_output->swapchain->next(nullptr);
|
m_currentBuffer = pMonitor->m_output->swapchain->next(&bufferAge);
|
||||||
if (!m_currentBuffer) {
|
if (!m_currentBuffer) {
|
||||||
Debug::log(ERR, "Failed to acquire swapchain buffer for {}", pMonitor->m_name);
|
Debug::log(ERR, "Failed to acquire swapchain buffer for {}", pMonitor->m_name);
|
||||||
return false;
|
return false;
|
||||||
@@ -2273,7 +2272,7 @@ bool CHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMod
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mode == RENDER_MODE_NORMAL) {
|
if (mode == RENDER_MODE_NORMAL) {
|
||||||
damage = pMonitor->m_damage.getBufferDamage(HL_BUFFER_AGE);
|
damage = pMonitor->m_damage.getBufferDamage(bufferAge);
|
||||||
pMonitor->m_damage.rotate();
|
pMonitor->m_damage.rotate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -51,7 +51,7 @@ class CHyprRenderer {
|
|||||||
CHyprRenderer();
|
CHyprRenderer();
|
||||||
~CHyprRenderer();
|
~CHyprRenderer();
|
||||||
|
|
||||||
void renderMonitor(PHLMONITOR pMonitor);
|
void renderMonitor(PHLMONITOR pMonitor, bool commit = true);
|
||||||
void arrangeLayersForMonitor(const MONITORID&);
|
void arrangeLayersForMonitor(const MONITORID&);
|
||||||
void damageSurface(SP<CWLSurfaceResource>, double, double, double scale = 1.0);
|
void damageSurface(SP<CWLSurfaceResource>, double, double, double scale = 1.0);
|
||||||
void damageWindow(PHLWINDOW, bool forceFull = false);
|
void damageWindow(PHLWINDOW, bool forceFull = false);
|
||||||
@@ -156,6 +156,7 @@ class CHyprRenderer {
|
|||||||
friend class CInputManager;
|
friend class CInputManager;
|
||||||
friend class CPointerManager;
|
friend class CPointerManager;
|
||||||
friend class CMonitor;
|
friend class CMonitor;
|
||||||
|
friend class CMonitorFrameScheduler;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline UP<CHyprRenderer> g_pHyprRenderer;
|
inline UP<CHyprRenderer> g_pHyprRenderer;
|
||||||
|
Reference in New Issue
Block a user