From 0552a1edddead3b956e5ceaa19001c056deccb57 Mon Sep 17 00:00:00 2001 From: Maximilian Seidler <78690852+PaideiaDilemma@users.noreply.github.com> Date: Sun, 7 Jul 2024 18:43:17 +0200 Subject: [PATCH] core: add option to render solid background immediatly when bg assets are not ready (#407) * asyncResourceGatherer: start the asyncLoop at the same time as gather This is a prerequesit for labels beeing drawn, while backgrounds are note ready yet. * core: allow immediate rendering even when backgrounds are not gathered yet Note: We don't really need to call `asyncResourceGatherer::apply` in the `renderLock` function, since it will get called by a call to `asyncResourceGatherer::getAssetById` anyways. * background: render color rectangle when asset is not ready yet * config: add general:immediate_render config option * core: use the --immediate-render flag in attemptRestoreOnDeath --- src/config/ConfigManager.cpp | 1 + src/core/hyprlock.cpp | 13 +++++++---- src/core/hyprlock.hpp | 4 +++- src/main.cpp | 11 ++++++--- src/renderer/AsyncResourceGatherer.cpp | 32 +++++++++++++++----------- src/renderer/AsyncResourceGatherer.hpp | 4 ++-- src/renderer/Renderer.cpp | 22 ++++++++++-------- src/renderer/Renderer.hpp | 2 +- src/renderer/widgets/Background.cpp | 16 +++++++++---- src/renderer/widgets/Background.hpp | 1 + 10 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 2f1c00e..990bfe5 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -55,6 +55,7 @@ void CConfigManager::init() { m_config.addConfigValue("general:no_fade_in", Hyprlang::INT{0}); m_config.addConfigValue("general:no_fade_out", Hyprlang::INT{0}); m_config.addConfigValue("general:ignore_empty_input", Hyprlang::INT{0}); + m_config.addConfigValue("general:immediate_render", Hyprlang::INT{0}); m_config.addConfigValue("general:pam_module", Hyprlang::STRING{"hyprlock"}); m_config.addSpecialCategory("background", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true}); diff --git a/src/core/hyprlock.cpp b/src/core/hyprlock.cpp index 7d07272..7b3e856 100644 --- a/src/core/hyprlock.cpp +++ b/src/core/hyprlock.cpp @@ -18,7 +18,7 @@ #include #include -CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediate) { +CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediate, const bool immediateRender) { m_sWaylandState.display = wl_display_connect(wlDisplay.empty() ? nullptr : wlDisplay.c_str()); if (!m_sWaylandState.display) { Debug::log(CRIT, "Couldn't connect to a wayland compositor"); @@ -32,11 +32,14 @@ CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediate) { Debug::log(ERR, "Failed to create xkb context"); if (!immediate) { - const auto GRACE = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:grace"); - m_tGraceEnds = **GRACE ? std::chrono::system_clock::now() + std::chrono::seconds(**GRACE) : std::chrono::system_clock::from_time_t(0); + const auto PGRACE = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:grace"); + m_tGraceEnds = **PGRACE ? std::chrono::system_clock::now() + std::chrono::seconds(**PGRACE) : std::chrono::system_clock::from_time_t(0); } else { m_tGraceEnds = std::chrono::system_clock::from_time_t(0); } + + const auto PIMMEDIATERENDER = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:immediate_render"); + m_bImmediateRender = immediateRender || **PIMMEDIATERENDER; } CHyprlock::~CHyprlock() { @@ -383,7 +386,7 @@ void CHyprlock::run() { // Hyprland violates the protocol a bit to allow for this. if (SZCURRENTD != "Hyprland") { - while (!g_pRenderer->asyncResourceGatherer->ready) { + while (!g_pRenderer->asyncResourceGatherer->gathered) { wl_display_flush(m_sWaylandState.display); if (wl_display_prepare_read(m_sWaylandState.display) == 0) { wl_display_read_events(m_sWaylandState.display); @@ -1137,5 +1140,5 @@ void CHyprlock::attemptRestoreOnDeath() { ofs.close(); spawnSync("hyprctl keyword misc:allow_session_lock_restore true"); - spawnAsync("sleep 2 && hyprlock --immediate & disown"); + spawnAsync("sleep 2 && hyprlock --immediate --immediate-render & disown"); } diff --git a/src/core/hyprlock.hpp b/src/core/hyprlock.hpp index 5a2efb7..67a4d37 100644 --- a/src/core/hyprlock.hpp +++ b/src/core/hyprlock.hpp @@ -28,7 +28,7 @@ struct SDMABUFModifier { class CHyprlock { public: - CHyprlock(const std::string& wlDisplay, const bool immediate); + CHyprlock(const std::string& wlDisplay, const bool immediate, const bool immediateRender); ~CHyprlock(); void run(); @@ -100,6 +100,8 @@ class CHyprlock { bool m_bNumLock = false; bool m_bCtrl = false; bool m_bFadeStarted = false; + + bool m_bImmediateRender = false; // std::chrono::system_clock::time_point m_tGraceEnds; std::chrono::system_clock::time_point m_tFadeEnds; diff --git a/src/main.cpp b/src/main.cpp index 6273a3b..2cf92b7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ void help() { " -c FILE, --config FILE - Specify config file to use\n" " --display (display) - Specify the Wayland display to connect to\n" " --immediate - Lock immediately, ignoring any configured grace period\n" + " --immediate-render - Do not wait for resources before drawing the background\n" " -h, --help - Show this help message\n"; } @@ -26,8 +27,9 @@ std::optional parseArg(const std::vector& args, const int main(int argc, char** argv, char** envp) { std::string configPath; std::string wlDisplay; - bool immediate = false; - bool showHelp = false; + bool immediate = false; + bool immediateRender = false; + bool showHelp = false; std::vector args(argv, argv + argc); @@ -55,6 +57,9 @@ int main(int argc, char** argv, char** envp) { } else if (arg == "--immediate") immediate = true; + else if (arg == "--immediate-render") + immediateRender = true; + else if (arg == "--help" || arg == "-h") { showHelp = true; break; @@ -83,7 +88,7 @@ int main(int argc, char** argv, char** envp) { } try { - g_pHyprlock = std::make_unique(wlDisplay, immediate); + g_pHyprlock = std::make_unique(wlDisplay, immediate, immediateRender); g_pHyprlock->run(); } catch (const std::exception& ex) { Debug::log(CRIT, "Hyprlock threw: {}", ex.what()); diff --git a/src/renderer/AsyncResourceGatherer.cpp b/src/renderer/AsyncResourceGatherer.cpp index c73308c..df98349 100644 --- a/src/renderer/AsyncResourceGatherer.cpp +++ b/src/renderer/AsyncResourceGatherer.cpp @@ -49,10 +49,10 @@ CAsyncResourceGatherer::CAsyncResourceGatherer() { dmas.emplace_back(std::make_unique(PMONITOR)); } - asyncLoopThread = std::thread([this]() { - this->gather(); /* inital gather */ - this->asyncAssetSpinLock(); - }); + initialGatherThread = std::thread([this]() { this->gather(); }); + initialGatherThread.detach(); + + asyncLoopThread = std::thread([this]() { this->asyncAssetSpinLock(); }); asyncLoopThread.detach(); } @@ -158,16 +158,19 @@ void CAsyncResourceGatherer::gather() { const auto CAIRO = cairo_create(CAIROISURFACE); cairo_scale(CAIRO, 1, 1); - const auto TARGET = &preloadTargets.emplace_back(CAsyncResourceGatherer::SPreloadTarget{}); + { + std::lock_guard lg{preloadTargetsMutex}; + const auto TARGET = &preloadTargets.emplace_back(CAsyncResourceGatherer::SPreloadTarget{}); - TARGET->size = {cairo_image_surface_get_width(CAIROISURFACE), cairo_image_surface_get_height(CAIROISURFACE)}; - TARGET->type = TARGET_IMAGE; - TARGET->id = id; + TARGET->size = {cairo_image_surface_get_width(CAIROISURFACE), cairo_image_surface_get_height(CAIROISURFACE)}; + TARGET->type = TARGET_IMAGE; + TARGET->id = id; - const auto DATA = cairo_image_surface_get_data(CAIROISURFACE); - TARGET->cairo = CAIRO; - TARGET->cairosurface = CAIROISURFACE; - TARGET->data = DATA; + const auto DATA = cairo_image_surface_get_data(CAIROISURFACE); + TARGET->cairo = CAIRO; + TARGET->cairosurface = CAIROISURFACE; + TARGET->data = DATA; + } } } @@ -175,7 +178,7 @@ void CAsyncResourceGatherer::gather() { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - ready = true; + gathered = true; } bool CAsyncResourceGatherer::apply() { @@ -225,7 +228,6 @@ bool CAsyncResourceGatherer::apply() { } } - applied = true; return true; } @@ -414,4 +416,6 @@ void CAsyncResourceGatherer::notify() { void CAsyncResourceGatherer::await() { if (asyncLoopThread.joinable()) asyncLoopThread.join(); + if (initialGatherThread.joinable()) + initialGatherThread.join(); } diff --git a/src/renderer/AsyncResourceGatherer.hpp b/src/renderer/AsyncResourceGatherer.hpp index 8e8f5a1..19defd3 100644 --- a/src/renderer/AsyncResourceGatherer.hpp +++ b/src/renderer/AsyncResourceGatherer.hpp @@ -16,8 +16,7 @@ class CAsyncResourceGatherer { public: CAsyncResourceGatherer(); - std::atomic ready = false; - std::atomic applied = false; + std::atomic gathered = false; std::atomic progress = 0; @@ -52,6 +51,7 @@ class CAsyncResourceGatherer { private: std::thread asyncLoopThread; + std::thread initialGatherThread; void asyncAssetSpinLock(); void renderText(const SPreloadRequest& rq); diff --git a/src/renderer/Renderer.cpp b/src/renderer/Renderer.cpp index bd19238..560ceae 100644 --- a/src/renderer/Renderer.cpp +++ b/src/renderer/Renderer.cpp @@ -159,7 +159,8 @@ CRenderer::CRenderer() { asyncResourceGatherer = std::make_unique(); } -static int frames = 0; +static int frames = 0; +static bool firstFullFrame = false; // CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf) { @@ -183,12 +184,11 @@ CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); SRenderFeedback feedback; + float bga = 0.0; + const bool WAITFORASSETS = !g_pHyprlock->m_bImmediateRender && !asyncResourceGatherer->gathered; - float bga = asyncResourceGatherer->applied ? - std::clamp(std::chrono::duration_cast(std::chrono::system_clock::now() - gatheredAt).count() / 500000.0, 0.0, 1.0) : - 0.0; + if (WAITFORASSETS) { - if (!asyncResourceGatherer->ready) { // render status if (!**PDISABLEBAR) { CBox progress = {0, 0, asyncResourceGatherer->progress * surf.size.x, 2}; @@ -196,11 +196,13 @@ CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf } } else { - if (!asyncResourceGatherer->applied) { - asyncResourceGatherer->apply(); - gatheredAt = std::chrono::system_clock::now(); + if (!firstFullFrame) { + firstFullFrameTime = std::chrono::system_clock::now(); + firstFullFrame = true; } + bga = std::clamp(std::chrono::duration_cast(std::chrono::system_clock::now() - firstFullFrameTime).count() / 500000.0, 0.0, 1.0); + if (**PNOFADEIN) bga = 1.0; @@ -220,7 +222,7 @@ CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf Debug::log(TRACE, "frame {}", frames); - feedback.needsFrame = feedback.needsFrame || !asyncResourceGatherer->ready || bga < 1.0; + feedback.needsFrame = feedback.needsFrame || !asyncResourceGatherer->gathered || bga < 1.0; glDisable(GL_BLEND); @@ -325,7 +327,7 @@ std::vector>* CRenderer::getOrCreateWidgetsFor(const CS resourceID = CDMAFrame::getResourceId(surf->output); // When the initial gather of the asyncResourceGatherer is completed (ready), all DMAFrames are available. // Dynamic ones are tricky, because a screencopy would copy hyprlock itself. - if (asyncResourceGatherer->ready) { + if (asyncResourceGatherer->gathered) { if (!asyncResourceGatherer->getAssetByID(resourceID)) resourceID = ""; // Fallback to solid color (background:color) } diff --git a/src/renderer/Renderer.hpp b/src/renderer/Renderer.hpp index d3fc2b6..07de9bc 100644 --- a/src/renderer/Renderer.hpp +++ b/src/renderer/Renderer.hpp @@ -36,7 +36,7 @@ class CRenderer { void blurFB(const CFramebuffer& outfb, SBlurParams params); std::unique_ptr asyncResourceGatherer; - std::chrono::system_clock::time_point gatheredAt; + std::chrono::system_clock::time_point firstFullFrameTime; void pushFb(GLint fb); void popFb(); diff --git a/src/renderer/widgets/Background.cpp b/src/renderer/widgets/Background.cpp index 9a53bae..f3601c8 100644 --- a/src/renderer/widgets/Background.cpp +++ b/src/renderer/widgets/Background.cpp @@ -15,21 +15,29 @@ CBackground::CBackground(const Vector2D& viewport_, COutput* output_, const std: contrast = std::any_cast(props.at("contrast")); } +void CBackground::renderRect(CColor color) { + CBox monbox = {0, 0, viewport.x, viewport.y}; + g_pRenderer->renderRect(monbox, color, 0); +} + bool CBackground::draw(const SRenderData& data) { if (resourceID.empty()) { - CBox monbox = {0, 0, viewport.x, viewport.y}; - CColor col = color; + CColor col = color; col.a *= data.opacity; - g_pRenderer->renderRect(monbox, col, 0); + renderRect(col); return data.opacity < 1.0; } if (!asset) asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID); - if (!asset) + if (!asset) { + CColor col = color; + col.a *= data.opacity; + renderRect(col); return true; + } if (asset->texture.m_iType == TEXTURE_INVALID) { g_pRenderer->asyncResourceGatherer->unloadAsset(asset); diff --git a/src/renderer/widgets/Background.hpp b/src/renderer/widgets/Background.hpp index 33fd838..a2a648e 100644 --- a/src/renderer/widgets/Background.hpp +++ b/src/renderer/widgets/Background.hpp @@ -16,6 +16,7 @@ class CBackground : public IWidget { CBackground(const Vector2D& viewport, COutput* output_, const std::string& resourceID, const std::unordered_map& props, bool ss_); virtual bool draw(const SRenderData& data); + void renderRect(CColor color); private: // if needed