From 312994be83bc2b243d0ca9d86b40b7eae73e2ab3 Mon Sep 17 00:00:00 2001 From: Pavel Okhlopkov Date: Mon, 6 Apr 2026 21:05:20 +0300 Subject: [PATCH] world loading memory pressure detector Signed-off-by: Pavel Okhlopkov --- include/core/memory_monitor.hpp | 9 ++++++++- include/rendering/terrain_manager.hpp | 5 +++++ src/core/memory_monitor.cpp | 7 +++++++ src/rendering/terrain_manager.cpp | 22 ++++++++++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/include/core/memory_monitor.hpp b/include/core/memory_monitor.hpp index 7fa2fcce..787fe7f3 100644 --- a/include/core/memory_monitor.hpp +++ b/include/core/memory_monitor.hpp @@ -34,10 +34,17 @@ public: size_t getRecommendedCacheBudget() const; /** - * Check if system is under memory pressure + * Check if system is under memory pressure (< 10% RAM available) */ bool isMemoryPressure() const; + /** + * Check if system is under severe memory pressure (< 15% RAM available). + * At this level, background loading should pause entirely until memory + * is freed — continuing to allocate risks OOM-killing other applications. + */ + bool isSevereMemoryPressure() const; + private: MemoryMonitor() = default; size_t totalRAM_ = 0; diff --git a/include/rendering/terrain_manager.hpp b/include/rendering/terrain_manager.hpp index 59c9c4e2..9333ea38 100644 --- a/include/rendering/terrain_manager.hpp +++ b/include/rendering/terrain_manager.hpp @@ -366,6 +366,11 @@ private: std::condition_variable queueCV; std::deque loadQueue; std::queue> readyQueue; + // Maximum number of prepared-but-not-finalized tiles in readyQueue. + // Each prepared tile can hold 100–500 MB of decoded textures in RAM. + // Workers sleep when this limit is reached, letting the main thread + // finalize (GPU-upload + free) before more tiles are prepared. + static constexpr size_t maxReadyQueueSize_ = 3; // In-RAM tile cache (LRU) to avoid re-reading from disk struct CachedTile { diff --git a/src/core/memory_monitor.cpp b/src/core/memory_monitor.cpp index 75ee47e3..5ea68462 100644 --- a/src/core/memory_monitor.cpp +++ b/src/core/memory_monitor.cpp @@ -126,5 +126,12 @@ bool MemoryMonitor::isMemoryPressure() const { return available < (totalRAM_ * 10 / 100); } +bool MemoryMonitor::isSevereMemoryPressure() const { + size_t available = getAvailableRAM(); + // Severe pressure if < 15% RAM available — background workers should + // pause entirely to avoid OOM-killing other applications. + return available < (totalRAM_ * 15 / 100); +} + } // namespace core } // namespace wowee diff --git a/src/rendering/terrain_manager.cpp b/src/rendering/terrain_manager.cpp index e8a21469..d67e89ea 100644 --- a/src/rendering/terrain_manager.cpp +++ b/src/rendering/terrain_manager.cpp @@ -1212,6 +1212,28 @@ void TerrainManager::workerLoop() { break; } + // --- Memory-aware throttling --- + // Back-pressure: if the ready queue is deep (finalization can't + // keep up), or the system is running low on RAM, sleep instead + // of pulling more tiles. Each prepared tile can hold hundreds + // of MB of decoded textures; limiting concurrency here prevents + // WoWee from consuming all system memory during world load. + const auto& memMon = core::MemoryMonitor::getInstance(); + if (memMon.isSevereMemoryPressure()) { + // Severe pressure — don't pull ANY work until main thread + // finalizes tiles and frees decoded texture data. + lock.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + continue; + } + if (readyQueue.size() >= maxReadyQueueSize_ || memMon.isMemoryPressure()) { + // Moderate pressure or ready queue is backing up — sleep briefly + // to let the main thread catch up with finalization. + lock.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + continue; + } + if (!loadQueue.empty()) { coord = loadQueue.front(); loadQueue.pop_front();