core: stabilize label updates and revision locking in the asyncResourceGatherer (#384)

* core: handle rerendering when frameCallback is pending

* core: log when skipping label updates

* asyncResourceGatherer: remove busy and use loopMutex

Makes getAssetById fail less often and thus labels get more stable
updates

* asyncResourceGatherer: revision locking

`assetsMutex` was not needed, since `apply` only gets called from the
main thread and resources are also only aquired via the main thread.

`preloadTargets`, previously kinda guarded by the `busy` flag are now
locked as suggested in #367 (but via a copy of `peloadTargets`).

`apply` now returns a boolean so that the locking of preloadTargets in
combination with checking `preloadTargets.empty()` is a bit nicer.

* asyncResourceGatherer: remove explicit template arg for unique lock
This commit is contained in:
Maximilian Seidler 2024-06-26 20:31:15 +02:00 committed by GitHub
parent d31e600f14
commit 318c00d6d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 43 additions and 49 deletions

View file

@ -129,8 +129,10 @@ static const wl_callback_listener callbackListener = {
void CSessionLockSurface::render() { void CSessionLockSurface::render() {
Debug::log(TRACE, "render lock"); Debug::log(TRACE, "render lock");
if (frameCallback || !readyForFrame) if (frameCallback || !readyForFrame) {
needsFrame = true;
return; return;
}
const auto FEEDBACK = g_pRenderer->renderLock(*this); const auto FEEDBACK = g_pRenderer->renderLock(*this);
frameCallback = wl_surface_frame(surface); frameCallback = wl_surface_frame(surface);

View file

@ -11,8 +11,6 @@
#include "../helpers/Jpeg.hpp" #include "../helpers/Jpeg.hpp"
#include "../helpers/Webp.hpp" #include "../helpers/Webp.hpp"
std::mutex cvmtx;
CAsyncResourceGatherer::CAsyncResourceGatherer() { CAsyncResourceGatherer::CAsyncResourceGatherer() {
asyncLoopThread = std::thread([this]() { asyncLoopThread = std::thread([this]() {
this->gather(); /* inital gather */ this->gather(); /* inital gather */
@ -83,23 +81,17 @@ void CAsyncResourceGatherer::recheckDMAFramesFor(COutput* output) {
} }
SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) { SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) {
if (asyncLoopState.busy)
return nullptr;
std::lock_guard<std::mutex> lg(asyncLoopState.loopMutex);
for (auto& a : assets) { for (auto& a : assets) {
if (a.first == id) if (a.first == id)
return &a.second; return &a.second;
} }
if (!preloadTargets.empty()) { if (apply()) {
apply();
for (auto& a : assets) { for (auto& a : assets) {
if (a.first == id) if (a.first == id)
return &a.second; return &a.second;
} }
} };
for (auto& dma : dmas) { for (auto& dma : dmas) {
if (id == "dma:" + dma->name) if (id == "dma:" + dma->name)
@ -210,18 +202,27 @@ void CAsyncResourceGatherer::gather() {
ready = true; ready = true;
} }
void CAsyncResourceGatherer::apply() { bool CAsyncResourceGatherer::apply() {
preloadTargetsMutex.lock();
for (auto& t : preloadTargets) { if (preloadTargets.empty()) {
preloadTargetsMutex.unlock();
return false;
}
auto currentPreloadTargets = preloadTargets;
preloadTargets.clear();
preloadTargetsMutex.unlock();
for (auto& t : currentPreloadTargets) {
if (t.type == TARGET_IMAGE) { if (t.type == TARGET_IMAGE) {
std::lock_guard<std::mutex> lg(asyncLoopState.assetsMutex); const auto ASSET = &assets[t.id];
const auto ASSET = &assets[t.id];
const auto SURFACESTATUS = cairo_surface_status((cairo_surface_t*)t.cairosurface); const auto SURFACESTATUS = cairo_surface_status((cairo_surface_t*)t.cairosurface);
const auto CAIROFORMAT = cairo_image_surface_get_format((cairo_surface_t*)t.cairosurface); const auto CAIROFORMAT = cairo_image_surface_get_format((cairo_surface_t*)t.cairosurface);
const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA; const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA;
const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA; const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA;
const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE; const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE;
if (SURFACESTATUS != CAIRO_STATUS_SUCCESS) { if (SURFACESTATUS != CAIRO_STATUS_SUCCESS) {
Debug::log(ERR, "Resource {} invalid ({})", t.id, cairo_status_to_string(SURFACESTATUS)); Debug::log(ERR, "Resource {} invalid ({})", t.id, cairo_status_to_string(SURFACESTATUS));
@ -248,9 +249,8 @@ void CAsyncResourceGatherer::apply() {
} }
} }
preloadTargets.clear();
applied = true; applied = true;
return true;
} }
void CAsyncResourceGatherer::renderImage(const SPreloadRequest& rq) { void CAsyncResourceGatherer::renderImage(const SPreloadRequest& rq) {
@ -269,6 +269,7 @@ void CAsyncResourceGatherer::renderImage(const SPreloadRequest& rq) {
target.data = cairo_image_surface_get_data(CAIROISURFACE); target.data = cairo_image_surface_get_data(CAIROISURFACE);
target.size = {(double)cairo_image_surface_get_width(CAIROISURFACE), (double)cairo_image_surface_get_height(CAIROISURFACE)}; target.size = {(double)cairo_image_surface_get_width(CAIROISURFACE), (double)cairo_image_surface_get_height(CAIROISURFACE)};
std::lock_guard lg{preloadTargetsMutex};
preloadTargets.push_back(target); preloadTargets.push_back(target);
} }
@ -363,6 +364,7 @@ void CAsyncResourceGatherer::renderText(const SPreloadRequest& rq) {
target.data = cairo_image_surface_get_data(CAIROSURFACE); target.data = cairo_image_surface_get_data(CAIROSURFACE);
target.size = {layoutWidth / PANGO_SCALE, layoutHeight / PANGO_SCALE}; target.size = {layoutWidth / PANGO_SCALE, layoutHeight / PANGO_SCALE};
std::lock_guard lg{preloadTargetsMutex};
preloadTargets.push_back(target); preloadTargets.push_back(target);
} }
@ -380,27 +382,23 @@ static void timerCallback(std::shared_ptr<CTimer> self, void* data_) {
void CAsyncResourceGatherer::asyncAssetSpinLock() { void CAsyncResourceGatherer::asyncAssetSpinLock() {
while (!g_pHyprlock->m_bTerminate) { while (!g_pHyprlock->m_bTerminate) {
std::unique_lock lk(cvmtx); std::unique_lock lk(asyncLoopState.requestsMutex);
if (asyncLoopState.pending == false) // avoid a lock if a thread managed to request something already since we .unlock()ed if (asyncLoopState.pending == false) // avoid a lock if a thread managed to request something already since we .unlock()ed
asyncLoopState.loopGuard.wait_for(lk, std::chrono::seconds(5), [this] { return asyncLoopState.pending; }); // wait for events asyncLoopState.requestsCV.wait_for(lk, std::chrono::seconds(5), [this] { return asyncLoopState.pending; }); // wait for events
asyncLoopState.requestMutex.lock();
asyncLoopState.pending = false; asyncLoopState.pending = false;
if (asyncLoopState.requests.empty()) { if (asyncLoopState.requests.empty()) {
asyncLoopState.requestMutex.unlock(); lk.unlock();
continue; continue;
} }
auto requests = asyncLoopState.requests; auto requests = asyncLoopState.requests;
asyncLoopState.requests.clear(); asyncLoopState.requests.clear();
asyncLoopState.requestMutex.unlock(); lk.unlock();
// process requests // process requests
asyncLoopState.busy = true;
for (auto& r : requests) { for (auto& r : requests) {
if (r.type == TARGET_TEXT) { if (r.type == TARGET_TEXT) {
renderText(r); renderText(r);
@ -415,29 +413,26 @@ void CAsyncResourceGatherer::asyncAssetSpinLock() {
if (r.callback) if (r.callback)
g_pHyprlock->addTimer(std::chrono::milliseconds(0), timerCallback, new STimerCallbackData{r.callback, r.callbackData}); g_pHyprlock->addTimer(std::chrono::milliseconds(0), timerCallback, new STimerCallbackData{r.callback, r.callbackData});
} }
asyncLoopState.busy = false;
} }
dmas.clear(); dmas.clear();
} }
void CAsyncResourceGatherer::requestAsyncAssetPreload(const SPreloadRequest& request) { void CAsyncResourceGatherer::requestAsyncAssetPreload(const SPreloadRequest& request) {
std::lock_guard<std::mutex> lg(asyncLoopState.requestMutex); std::lock_guard<std::mutex> lg(asyncLoopState.requestsMutex);
asyncLoopState.requests.push_back(request); asyncLoopState.requests.push_back(request);
asyncLoopState.pending = true; asyncLoopState.pending = true;
asyncLoopState.loopGuard.notify_all(); asyncLoopState.requestsCV.notify_all();
} }
void CAsyncResourceGatherer::unloadAsset(SPreloadedAsset* asset) { void CAsyncResourceGatherer::unloadAsset(SPreloadedAsset* asset) {
std::lock_guard<std::mutex> lg(asyncLoopState.assetsMutex);
std::erase_if(assets, [asset](const auto& a) { return &a.second == asset; }); std::erase_if(assets, [asset](const auto& a) { return &a.second == asset; });
} }
void CAsyncResourceGatherer::notify() { void CAsyncResourceGatherer::notify() {
std::lock_guard<std::mutex> lg(asyncLoopState.requestsMutex);
asyncLoopState.pending = true; asyncLoopState.pending = true;
asyncLoopState.loopGuard.notify_all(); asyncLoopState.requestsCV.notify_all();
} }
void CAsyncResourceGatherer::await() { void CAsyncResourceGatherer::await() {

View file

@ -24,7 +24,7 @@ class CAsyncResourceGatherer {
/* only call from ogl thread */ /* only call from ogl thread */
SPreloadedAsset* getAssetByID(const std::string& id); SPreloadedAsset* getAssetByID(const std::string& id);
void apply(); bool apply();
enum eTargetType { enum eTargetType {
TARGET_IMAGE = 0, TARGET_IMAGE = 0,
@ -59,12 +59,8 @@ class CAsyncResourceGatherer {
void renderImage(const SPreloadRequest& rq); void renderImage(const SPreloadRequest& rq);
struct { struct {
std::condition_variable loopGuard; std::condition_variable requestsCV;
std::mutex loopMutex; std::mutex requestsMutex;
std::mutex requestMutex;
std::mutex assetsMutex;
std::vector<SPreloadRequest> requests; std::vector<SPreloadRequest> requests;
bool pending = false; bool pending = false;
@ -86,6 +82,8 @@ class CAsyncResourceGatherer {
std::vector<std::unique_ptr<CDMAFrame>> dmas; std::vector<std::unique_ptr<CDMAFrame>> dmas;
std::vector<SPreloadTarget> preloadTargets; std::vector<SPreloadTarget> preloadTargets;
std::mutex preloadTargetsMutex;
std::unordered_map<std::string, SPreloadedAsset> assets; std::unordered_map<std::string, SPreloadedAsset> assets;
void gather(); void gather();

View file

@ -39,8 +39,10 @@ void CLabel::onTimerUpdate() {
if (label.formatted == oldFormatted && !label.alwaysUpdate) if (label.formatted == oldFormatted && !label.alwaysUpdate)
return; return;
if (!pendingResourceID.empty()) if (!pendingResourceID.empty()) {
return; // too many updates, we'll miss some. Shouldn't happen tbh Debug::log(WARN, "Trying to update label, but resource {} is still pending! Skipping update.", pendingResourceID);
return;
}
// request new // request new
request.id = getUniqueResourceId(); request.id = getUniqueResourceId();
@ -141,7 +143,4 @@ static void onAssetCallbackTimer(std::shared_ptr<CTimer> self, void* data) {
void CLabel::renderSuper() { void CLabel::renderSuper() {
g_pHyprlock->renderOutput(outputStringPort); g_pHyprlock->renderOutput(outputStringPort);
if (!pendingResourceID.empty()) /* did not consume the pending resource */
g_pHyprlock->addTimer(std::chrono::milliseconds(100), onAssetCallbackTimer, this);
} }