From c3e57629a6b90f7945ae6c2d11e2a82f533e00b2 Mon Sep 17 00:00:00 2001 From: Mariana Date: Sat, 1 Nov 2025 23:02:50 +0000 Subject: [PATCH] Route badge spawns through a thread-safe queue --- src/app/main.cpp | 2 +- src/overlay/overlay.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/app/main.cpp b/src/app/main.cpp index 3df229a..40c2e37 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -259,7 +259,7 @@ int main(int argc, char **argv) { bx = cx; by = cy; } - overlay.spawn_badge(bx, by); + overlay.enqueue_spawn(bx, by); } } }, diff --git a/src/overlay/overlay.cpp b/src/overlay/overlay.cpp index 0883915..9085695 100644 --- a/src/overlay/overlay.cpp +++ b/src/overlay/overlay.cpp @@ -33,6 +33,7 @@ void stbi_image_free(void *); #include #include #include +#include #include #ifdef _WIN32 @@ -101,6 +102,8 @@ class Overlay { void shutdown(); void spawn_badge(int sprite, float x, float y); void spawn_badge(float x, float y); + void enqueue_spawn(int sprite, float x, float y); + void enqueue_spawn(float x, float y); void run(std::stop_token st); void stop(); void refresh_from_config(const app::Config &cfg); @@ -122,6 +125,7 @@ class Overlay { void render(); void update_frame_interval(); void apply_pending_config(); + void process_spawn_queue(); struct AtlasData { std::vector sprites; std::unordered_map lookup; @@ -134,6 +138,12 @@ class Overlay { static std::optional normalize_path(const std::optional &path); + struct SpawnRequest { + std::optional sprite; + float x; + float y; + }; + struct PendingConfig { std::string spawn_strategy; int badge_min_px = 60; @@ -178,6 +188,8 @@ class Overlay { std::optional m_pending_config; std::atomic m_has_pending_config{false}; std::mutex m_spawn_config_mutex; + std::mutex m_spawn_queue_mutex; + std::queue m_spawn_queue; }; void Overlay::update_frame_interval() { @@ -759,6 +771,16 @@ void Overlay::spawn_badge(int sprite, float x, float y) { spawn_badge_locked(sprite, x, y); } +void Overlay::enqueue_spawn(float x, float y) { + std::lock_guard lock(m_spawn_queue_mutex); + m_spawn_queue.push(SpawnRequest{std::nullopt, x, y}); +} + +void Overlay::enqueue_spawn(int sprite, float x, float y) { + std::lock_guard lock(m_spawn_queue_mutex); + m_spawn_queue.push(SpawnRequest{std::make_optional(sprite), x, y}); +} + int Overlay::select_sprite_locked() { if (m_selector_indices.empty()) { return 0; @@ -827,6 +849,23 @@ void Overlay::spawn_badge_locked(int sprite, float x, float y) { m_spawn_times.push_back(now); } +void Overlay::process_spawn_queue() { + std::queue local; + { + std::lock_guard lock(m_spawn_queue_mutex); + std::swap(local, m_spawn_queue); + } + while (!local.empty()) { + auto &request = local.front(); + if (request.sprite.has_value()) { + spawn_badge(request.sprite.value(), request.x, request.y); + } else { + spawn_badge(request.x, request.y); + } + local.pop(); + } +} + void Overlay::stop() { m_running = false; } void Overlay::update(float dt) { @@ -900,6 +939,7 @@ void Overlay::run(std::stop_token st) { if (m_has_pending_config.exchange(false, std::memory_order_acq_rel)) { apply_pending_config(); } + process_spawn_queue(); auto frame = std::chrono::microseconds(m_frame_interval_us.load()); if (m_paused.load()) { std::this_thread::sleep_for(frame);