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
This commit is contained in:
Maximilian Seidler 2024-07-07 18:43:17 +02:00 committed by GitHub
parent a50296c181
commit 0552a1eddd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 66 additions and 40 deletions

View file

@ -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});

View file

@ -18,7 +18,7 @@
#include <fstream>
#include <algorithm>
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");
}

View file

@ -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;

View file

@ -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";
}
@ -27,6 +28,7 @@ int main(int argc, char** argv, char** envp) {
std::string configPath;
std::string wlDisplay;
bool immediate = false;
bool immediateRender = false;
bool showHelp = false;
std::vector<std::string> 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<CHyprlock>(wlDisplay, immediate);
g_pHyprlock = std::make_unique<CHyprlock>(wlDisplay, immediate, immediateRender);
g_pHyprlock->run();
} catch (const std::exception& ex) {
Debug::log(CRIT, "Hyprlock threw: {}", ex.what());

View file

@ -49,10 +49,10 @@ CAsyncResourceGatherer::CAsyncResourceGatherer() {
dmas.emplace_back(std::make_unique<CDMAFrame>(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,6 +158,8 @@ void CAsyncResourceGatherer::gather() {
const auto CAIRO = cairo_create(CAIROISURFACE);
cairo_scale(CAIRO, 1, 1);
{
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)};
@ -170,12 +172,13 @@ void CAsyncResourceGatherer::gather() {
TARGET->data = DATA;
}
}
}
while (std::any_of(dmas.begin(), dmas.end(), [](const auto& d) { return !d->asset.ready; })) {
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();
}

View file

@ -16,8 +16,7 @@
class CAsyncResourceGatherer {
public:
CAsyncResourceGatherer();
std::atomic<bool> ready = false;
std::atomic<bool> applied = false;
std::atomic<bool> gathered = false;
std::atomic<float> progress = 0;
@ -52,6 +51,7 @@ class CAsyncResourceGatherer {
private:
std::thread asyncLoopThread;
std::thread initialGatherThread;
void asyncAssetSpinLock();
void renderText(const SPreloadRequest& rq);

View file

@ -160,6 +160,7 @@ CRenderer::CRenderer() {
}
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::microseconds>(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::microseconds>(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<std::unique_ptr<IWidget>>* 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)
}

View file

@ -36,7 +36,7 @@ class CRenderer {
void blurFB(const CFramebuffer& outfb, SBlurParams params);
std::unique_ptr<CAsyncResourceGatherer> asyncResourceGatherer;
std::chrono::system_clock::time_point gatheredAt;
std::chrono::system_clock::time_point firstFullFrameTime;
void pushFb(GLint fb);
void popFb();

View file

@ -15,21 +15,29 @@ CBackground::CBackground(const Vector2D& viewport_, COutput* output_, const std:
contrast = std::any_cast<Hyprlang::FLOAT>(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;
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);

View file

@ -16,6 +16,7 @@ class CBackground : public IWidget {
CBackground(const Vector2D& viewport, COutput* output_, const std::string& resourceID, const std::unordered_map<std::string, std::any>& props, bool ss_);
virtual bool draw(const SRenderData& data);
void renderRect(CColor color);
private:
// if needed