mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Add dynamic memory-based asset caching and aggressive loading
- Add MemoryMonitor class for dynamic cache sizing based on available RAM - Increase terrain load radius to 8 tiles (17x17 grid, 289 tiles) - Scale worker threads to 75% of logical cores (no cap) - Increase cache budget to 80% of available RAM, max file size to 50% - Increase M2 render distance: 1200 units during taxi, 800 when >2000 instances - Fix camera positioning during taxi flights (external follow mode) - Add 2-second landing cooldown to prevent re-entering taxi mode on lag - Update interval reduced to 33ms for faster streaming responsiveness Optimized for high-memory systems while scaling gracefully to lower-end hardware. Cache and render distances now fully utilize available VRAM on minimum spec GPUs.
This commit is contained in:
parent
27d0496894
commit
c047446fb7
12 changed files with 198 additions and 19 deletions
|
|
@ -69,6 +69,7 @@ set(WOWEE_SOURCES
|
||||||
src/core/window.cpp
|
src/core/window.cpp
|
||||||
src/core/input.cpp
|
src/core/input.cpp
|
||||||
src/core/logger.cpp
|
src/core/logger.cpp
|
||||||
|
src/core/memory_monitor.cpp
|
||||||
|
|
||||||
# Network
|
# Network
|
||||||
src/network/socket.cpp
|
src/network/socket.cpp
|
||||||
|
|
|
||||||
47
include/core/memory_monitor.hpp
Normal file
47
include/core/memory_monitor.hpp
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace wowee {
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Monitors system memory and provides dynamic cache sizing
|
||||||
|
*/
|
||||||
|
class MemoryMonitor {
|
||||||
|
public:
|
||||||
|
static MemoryMonitor& getInstance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize memory monitoring
|
||||||
|
*/
|
||||||
|
void initialize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get total system RAM in bytes
|
||||||
|
*/
|
||||||
|
size_t getTotalRAM() const { return totalRAM_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get currently available RAM in bytes
|
||||||
|
*/
|
||||||
|
size_t getAvailableRAM() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get recommended cache budget (30% of available RAM)
|
||||||
|
*/
|
||||||
|
size_t getRecommendedCacheBudget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if system is under memory pressure
|
||||||
|
*/
|
||||||
|
bool isMemoryPressure() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MemoryMonitor() = default;
|
||||||
|
size_t totalRAM_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace core
|
||||||
|
} // namespace wowee
|
||||||
|
|
@ -965,6 +965,7 @@ private:
|
||||||
bool taxiActivatePending_ = false;
|
bool taxiActivatePending_ = false;
|
||||||
float taxiActivateTimer_ = 0.0f;
|
float taxiActivateTimer_ = 0.0f;
|
||||||
bool taxiClientActive_ = false;
|
bool taxiClientActive_ = false;
|
||||||
|
float taxiLandingCooldown_ = 0.0f; // Prevent re-entering taxi right after landing
|
||||||
size_t taxiClientIndex_ = 0;
|
size_t taxiClientIndex_ = 0;
|
||||||
std::vector<glm::vec3> taxiClientPath_;
|
std::vector<glm::vec3> taxiClientPath_;
|
||||||
float taxiClientSpeed_ = 18.0f; // Reduced from 32 to prevent loading hitches
|
float taxiClientSpeed_ = 18.0f; // Reduced from 32 to prevent loading hitches
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ private:
|
||||||
mutable std::mutex readMutex;
|
mutable std::mutex readMutex;
|
||||||
std::map<std::string, std::shared_ptr<DBCFile>> dbcCache;
|
std::map<std::string, std::shared_ptr<DBCFile>> dbcCache;
|
||||||
|
|
||||||
// Decompressed file cache (LRU, 1GB budget for modern RAM)
|
// Decompressed file cache (LRU, dynamic budget based on system RAM)
|
||||||
struct CachedFile {
|
struct CachedFile {
|
||||||
std::vector<uint8_t> data;
|
std::vector<uint8_t> data;
|
||||||
uint64_t lastAccessTime;
|
uint64_t lastAccessTime;
|
||||||
|
|
@ -114,7 +114,7 @@ private:
|
||||||
mutable uint64_t fileCacheAccessCounter = 0;
|
mutable uint64_t fileCacheAccessCounter = 0;
|
||||||
mutable size_t fileCacheHits = 0;
|
mutable size_t fileCacheHits = 0;
|
||||||
mutable size_t fileCacheMisses = 0;
|
mutable size_t fileCacheMisses = 0;
|
||||||
static constexpr size_t FILE_CACHE_BUDGET = 1024 * 1024 * 1024; // 1GB
|
mutable size_t fileCacheBudget = 1024 * 1024 * 1024; // Dynamic, starts at 1GB
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize path for case-insensitive lookup
|
* Normalize path for case-insensitive lookup
|
||||||
|
|
|
||||||
|
|
@ -272,9 +272,9 @@ private:
|
||||||
|
|
||||||
// Streaming parameters
|
// Streaming parameters
|
||||||
bool streamingEnabled = true;
|
bool streamingEnabled = true;
|
||||||
int loadRadius = 2; // Load tiles within this radius (5x5 grid)
|
int loadRadius = 8; // Load tiles within this radius (17x17 grid)
|
||||||
int unloadRadius = 3; // Unload tiles beyond this radius
|
int unloadRadius = 12; // Unload tiles beyond this radius
|
||||||
float updateInterval = 0.1f; // Check streaming every 0.1 seconds
|
float updateInterval = 0.033f; // Check streaming every 33ms (~30 fps)
|
||||||
float timeSinceLastUpdate = 0.0f;
|
float timeSinceLastUpdate = 0.0f;
|
||||||
|
|
||||||
// Tile size constants (WoW ADT specifications)
|
// Tile size constants (WoW ADT specifications)
|
||||||
|
|
@ -300,7 +300,7 @@ private:
|
||||||
std::unordered_map<TileCoord, CachedTile, TileCoord::Hash> tileCache_;
|
std::unordered_map<TileCoord, CachedTile, TileCoord::Hash> tileCache_;
|
||||||
std::list<TileCoord> tileCacheLru_;
|
std::list<TileCoord> tileCacheLru_;
|
||||||
size_t tileCacheBytes_ = 0;
|
size_t tileCacheBytes_ = 0;
|
||||||
size_t tileCacheBudgetBytes_ = 8ull * 1024 * 1024 * 1024; // 8GB for modern systems
|
size_t tileCacheBudgetBytes_ = 8ull * 1024 * 1024 * 1024; // Dynamic, set at init based on RAM
|
||||||
std::mutex tileCacheMutex_;
|
std::mutex tileCacheMutex_;
|
||||||
|
|
||||||
std::shared_ptr<PendingTile> getCachedTile(const TileCoord& coord);
|
std::shared_ptr<PendingTile> getCachedTile(const TileCoord& coord);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include "core/spawn_presets.hpp"
|
#include "core/spawn_presets.hpp"
|
||||||
#include "core/logger.hpp"
|
#include "core/logger.hpp"
|
||||||
|
#include "core/memory_monitor.hpp"
|
||||||
#include "rendering/renderer.hpp"
|
#include "rendering/renderer.hpp"
|
||||||
#include "rendering/camera.hpp"
|
#include "rendering/camera.hpp"
|
||||||
#include "rendering/camera_controller.hpp"
|
#include "rendering/camera_controller.hpp"
|
||||||
|
|
@ -79,6 +80,9 @@ Application::~Application() {
|
||||||
bool Application::initialize() {
|
bool Application::initialize() {
|
||||||
LOG_INFO("Initializing Wowee Native Client");
|
LOG_INFO("Initializing Wowee Native Client");
|
||||||
|
|
||||||
|
// Initialize memory monitoring for dynamic cache sizing
|
||||||
|
core::MemoryMonitor::getInstance().initialize();
|
||||||
|
|
||||||
// Create window
|
// Create window
|
||||||
WindowConfig windowConfig;
|
WindowConfig windowConfig;
|
||||||
windowConfig.title = "Wowee";
|
windowConfig.title = "Wowee";
|
||||||
|
|
|
||||||
51
src/core/memory_monitor.cpp
Normal file
51
src/core/memory_monitor.cpp
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#include "core/memory_monitor.hpp"
|
||||||
|
#include "core/logger.hpp"
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/sysinfo.h>
|
||||||
|
|
||||||
|
namespace wowee {
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
MemoryMonitor& MemoryMonitor::getInstance() {
|
||||||
|
static MemoryMonitor instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryMonitor::initialize() {
|
||||||
|
struct sysinfo info;
|
||||||
|
if (sysinfo(&info) == 0) {
|
||||||
|
totalRAM_ = static_cast<size_t>(info.totalram) * info.mem_unit;
|
||||||
|
LOG_INFO("System RAM detected: ", totalRAM_ / (1024 * 1024 * 1024), " GB");
|
||||||
|
} else {
|
||||||
|
// Fallback: assume 16GB
|
||||||
|
totalRAM_ = 16ull * 1024 * 1024 * 1024;
|
||||||
|
LOG_WARNING("Could not detect system RAM, assuming 16GB");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MemoryMonitor::getAvailableRAM() const {
|
||||||
|
struct sysinfo info;
|
||||||
|
if (sysinfo(&info) == 0) {
|
||||||
|
// Available = free + buffers + cached
|
||||||
|
return static_cast<size_t>(info.freeram) * info.mem_unit;
|
||||||
|
}
|
||||||
|
return totalRAM_ / 2; // Fallback: assume 50% available
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MemoryMonitor::getRecommendedCacheBudget() const {
|
||||||
|
size_t available = getAvailableRAM();
|
||||||
|
// Use 80% of available RAM for caches (very aggressive), but cap at 90% of total
|
||||||
|
size_t budget = available * 80 / 100;
|
||||||
|
size_t maxBudget = totalRAM_ * 90 / 100;
|
||||||
|
return budget < maxBudget ? budget : maxBudget;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MemoryMonitor::isMemoryPressure() const {
|
||||||
|
size_t available = getAvailableRAM();
|
||||||
|
// Memory pressure if < 20% RAM available
|
||||||
|
return available < (totalRAM_ * 20 / 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace core
|
||||||
|
} // namespace wowee
|
||||||
|
|
@ -182,6 +182,11 @@ void GameHandler::update(float deltaTime) {
|
||||||
// Update combat text (Phase 2)
|
// Update combat text (Phase 2)
|
||||||
updateCombatText(deltaTime);
|
updateCombatText(deltaTime);
|
||||||
|
|
||||||
|
// Update taxi landing cooldown
|
||||||
|
if (taxiLandingCooldown_ > 0.0f) {
|
||||||
|
taxiLandingCooldown_ -= deltaTime;
|
||||||
|
}
|
||||||
|
|
||||||
// Detect taxi flight landing: UNIT_FLAG_TAXI_FLIGHT (0x00000100) cleared
|
// Detect taxi flight landing: UNIT_FLAG_TAXI_FLIGHT (0x00000100) cleared
|
||||||
if (onTaxiFlight_) {
|
if (onTaxiFlight_) {
|
||||||
updateClientTaxi(deltaTime);
|
updateClientTaxi(deltaTime);
|
||||||
|
|
@ -194,6 +199,7 @@ void GameHandler::update(float deltaTime) {
|
||||||
auto unit = std::static_pointer_cast<Unit>(playerEntity);
|
auto unit = std::static_pointer_cast<Unit>(playerEntity);
|
||||||
if ((unit->getUnitFlags() & 0x00000100) == 0) {
|
if ((unit->getUnitFlags() & 0x00000100) == 0) {
|
||||||
onTaxiFlight_ = false;
|
onTaxiFlight_ = false;
|
||||||
|
taxiLandingCooldown_ = 2.0f; // 2 second cooldown to prevent re-entering
|
||||||
if (taxiMountActive_ && mountCallback_) {
|
if (taxiMountActive_ && mountCallback_) {
|
||||||
mountCallback_(0);
|
mountCallback_(0);
|
||||||
}
|
}
|
||||||
|
|
@ -1670,7 +1676,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
||||||
}
|
}
|
||||||
if (block.guid == playerGuid) {
|
if (block.guid == playerGuid) {
|
||||||
constexpr uint32_t UNIT_FLAG_TAXI_FLIGHT = 0x00000100;
|
constexpr uint32_t UNIT_FLAG_TAXI_FLIGHT = 0x00000100;
|
||||||
if ((unit->getUnitFlags() & UNIT_FLAG_TAXI_FLIGHT) != 0 && !onTaxiFlight_) {
|
if ((unit->getUnitFlags() & UNIT_FLAG_TAXI_FLIGHT) != 0 && !onTaxiFlight_ && taxiLandingCooldown_ <= 0.0f) {
|
||||||
onTaxiFlight_ = true;
|
onTaxiFlight_ = true;
|
||||||
applyTaxiMountForCurrentNode();
|
applyTaxiMountForCurrentNode();
|
||||||
}
|
}
|
||||||
|
|
@ -5242,6 +5248,7 @@ void GameHandler::updateClientTaxi(float deltaTime) {
|
||||||
if (taxiClientIndex_ + 1 >= taxiClientPath_.size()) {
|
if (taxiClientIndex_ + 1 >= taxiClientPath_.size()) {
|
||||||
taxiClientActive_ = false;
|
taxiClientActive_ = false;
|
||||||
onTaxiFlight_ = false;
|
onTaxiFlight_ = false;
|
||||||
|
taxiLandingCooldown_ = 2.0f; // 2 second cooldown to prevent re-entering
|
||||||
if (taxiMountActive_ && mountCallback_) {
|
if (taxiMountActive_ && mountCallback_) {
|
||||||
mountCallback_(0);
|
mountCallback_(0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "pipeline/asset_manager.hpp"
|
#include "pipeline/asset_manager.hpp"
|
||||||
#include "core/logger.hpp"
|
#include "core/logger.hpp"
|
||||||
|
#include "core/memory_monitor.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
namespace wowee {
|
namespace wowee {
|
||||||
|
|
@ -25,8 +26,14 @@ bool AssetManager::initialize(const std::string& dataPath_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set dynamic file cache budget based on available RAM
|
||||||
|
auto& memMonitor = core::MemoryMonitor::getInstance();
|
||||||
|
size_t recommendedBudget = memMonitor.getRecommendedCacheBudget();
|
||||||
|
fileCacheBudget = recommendedBudget / 2; // Split budget: half for file cache, half for other caches
|
||||||
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
LOG_INFO("Asset manager initialized successfully (1GB file cache enabled)");
|
LOG_INFO("Asset manager initialized (dynamic file cache: ",
|
||||||
|
fileCacheBudget / (1024 * 1024), " MB, adjusts based on RAM)");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,9 +176,9 @@ std::vector<uint8_t> AssetManager::readFile(const std::string& path) const {
|
||||||
|
|
||||||
// Add to cache if within budget
|
// Add to cache if within budget
|
||||||
size_t fileSize = data.size();
|
size_t fileSize = data.size();
|
||||||
if (fileSize > 0 && fileSize < FILE_CACHE_BUDGET / 10) { // Don't cache files > 100MB
|
if (fileSize > 0 && fileSize < fileCacheBudget / 2) { // Don't cache files > 50% of budget (very aggressive)
|
||||||
// Evict old entries if needed (LRU)
|
// Evict old entries if needed (LRU)
|
||||||
while (fileCacheTotalBytes + fileSize > FILE_CACHE_BUDGET && !fileCache.empty()) {
|
while (fileCacheTotalBytes + fileSize > fileCacheBudget && !fileCache.empty()) {
|
||||||
// Find least recently used entry
|
// Find least recently used entry
|
||||||
auto lru = fileCache.begin();
|
auto lru = fileCache.begin();
|
||||||
for (auto it = fileCache.begin(); it != fileCache.end(); ++it) {
|
for (auto it = fileCache.begin(); it != fileCache.end(); ++it) {
|
||||||
|
|
|
||||||
|
|
@ -80,8 +80,51 @@ void CameraController::update(float deltaTime) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip all collision/movement logic during taxi flights (position controlled externally)
|
// During taxi flights, skip input/movement logic but still position camera
|
||||||
if (externalFollow_) {
|
if (externalFollow_) {
|
||||||
|
// Mouse look (right mouse button)
|
||||||
|
if (rightMouseDown) {
|
||||||
|
int mouseDX, mouseDY;
|
||||||
|
SDL_GetRelativeMouseState(&mouseDX, &mouseDY);
|
||||||
|
yaw -= mouseDX * mouseSensitivity;
|
||||||
|
pitch -= mouseDY * mouseSensitivity;
|
||||||
|
pitch = glm::clamp(pitch, -89.0f, 89.0f);
|
||||||
|
camera->setRotation(yaw, pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position camera behind character during taxi
|
||||||
|
if (thirdPerson && followTarget) {
|
||||||
|
glm::vec3 targetPos = *followTarget;
|
||||||
|
glm::vec3 forward3D = camera->getForward();
|
||||||
|
|
||||||
|
// Pivot point at upper chest/neck
|
||||||
|
float mountedOffset = mounted_ ? mountHeightOffset_ : 0.0f;
|
||||||
|
glm::vec3 pivot = targetPos + glm::vec3(0.0f, 0.0f, PIVOT_HEIGHT + mountedOffset);
|
||||||
|
|
||||||
|
// Camera direction from yaw/pitch
|
||||||
|
glm::vec3 camDir = -forward3D;
|
||||||
|
|
||||||
|
// Use current distance
|
||||||
|
float actualDist = std::min(currentDistance, collisionDistance);
|
||||||
|
|
||||||
|
// Compute camera position
|
||||||
|
glm::vec3 actualCam;
|
||||||
|
if (actualDist < MIN_DISTANCE + 0.1f) {
|
||||||
|
actualCam = pivot + forward3D * 0.1f;
|
||||||
|
} else {
|
||||||
|
actualCam = pivot + camDir * actualDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smooth camera position
|
||||||
|
if (glm::length(smoothedCamPos) < 0.01f) {
|
||||||
|
smoothedCamPos = actualCam;
|
||||||
|
}
|
||||||
|
float camLerp = 1.0f - std::exp(-CAM_SMOOTH_SPEED * deltaTime);
|
||||||
|
smoothedCamPos += (actualCam - smoothedCamPos) * camLerp;
|
||||||
|
|
||||||
|
camera->setPosition(smoothedCamPos);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1379,7 +1379,7 @@ void M2Renderer::update(float deltaTime, const glm::vec3& cameraPos, const glm::
|
||||||
|
|
||||||
// Cache camera state for frustum-culling bone computation
|
// Cache camera state for frustum-culling bone computation
|
||||||
cachedCamPos_ = cameraPos;
|
cachedCamPos_ = cameraPos;
|
||||||
const float maxRenderDistance = (instances.size() > 600) ? 320.0f : 2800.0f;
|
const float maxRenderDistance = (instances.size() > 2000) ? 800.0f : 2800.0f;
|
||||||
cachedMaxRenderDistSq_ = maxRenderDistance * maxRenderDistance;
|
cachedMaxRenderDistSq_ = maxRenderDistance * maxRenderDistance;
|
||||||
|
|
||||||
// Build frustum for culling bones
|
// Build frustum for culling bones
|
||||||
|
|
@ -1643,9 +1643,9 @@ void M2Renderer::render(const Camera& camera, const glm::mat4& view, const glm::
|
||||||
|
|
||||||
lastDrawCallCount = 0;
|
lastDrawCallCount = 0;
|
||||||
|
|
||||||
// Adaptive render distance: keep longer tree/foliage visibility to reduce pop-in.
|
// Adaptive render distance: aggressive settings to utilize VRAM fully
|
||||||
// During taxi, use very short render distance to prevent loading hitches
|
// During taxi, render far to upload models/textures to VRAM cache
|
||||||
const float maxRenderDistance = onTaxi_ ? 150.0f : (instances.size() > 600) ? 320.0f : 2800.0f;
|
const float maxRenderDistance = onTaxi_ ? 1200.0f : (instances.size() > 2000) ? 800.0f : 2800.0f;
|
||||||
const float maxRenderDistanceSq = maxRenderDistance * maxRenderDistance;
|
const float maxRenderDistanceSq = maxRenderDistance * maxRenderDistance;
|
||||||
const float fadeStartFraction = 0.75f;
|
const float fadeStartFraction = 0.75f;
|
||||||
const glm::vec3 camPos = camera.getPosition();
|
const glm::vec3 camPos = camera.getPosition();
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include "rendering/wmo_renderer.hpp"
|
#include "rendering/wmo_renderer.hpp"
|
||||||
#include "rendering/camera.hpp"
|
#include "rendering/camera.hpp"
|
||||||
#include "core/coordinates.hpp"
|
#include "core/coordinates.hpp"
|
||||||
|
#include "core/memory_monitor.hpp"
|
||||||
#include "pipeline/asset_manager.hpp"
|
#include "pipeline/asset_manager.hpp"
|
||||||
#include "pipeline/adt_loader.hpp"
|
#include "pipeline/adt_loader.hpp"
|
||||||
#include "pipeline/m2_loader.hpp"
|
#include "pipeline/m2_loader.hpp"
|
||||||
|
|
@ -113,10 +114,21 @@ bool TerrainManager::initialize(pipeline::AssetManager* assets, TerrainRenderer*
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start background worker pool
|
// Set dynamic tile cache budget (use other half of recommended budget)
|
||||||
|
auto& memMonitor = core::MemoryMonitor::getInstance();
|
||||||
|
tileCacheBudgetBytes_ = memMonitor.getRecommendedCacheBudget() / 2;
|
||||||
|
LOG_INFO("Terrain tile cache budget: ", tileCacheBudgetBytes_ / (1024 * 1024), " MB (dynamic)");
|
||||||
|
|
||||||
|
// Start background worker pool (dynamic: scales with available cores)
|
||||||
|
// Use 75% of logical cores for decompression, leaving headroom for render/OS
|
||||||
workerRunning.store(true);
|
workerRunning.store(true);
|
||||||
unsigned hc = std::thread::hardware_concurrency();
|
unsigned hc = std::thread::hardware_concurrency();
|
||||||
workerCount = static_cast<int>(hc > 0 ? std::min(4u, std::max(2u, hc - 1)) : 2u);
|
if (hc > 0) {
|
||||||
|
unsigned targetWorkers = std::max(6u, (hc * 3) / 4); // 75% of cores, minimum 6
|
||||||
|
workerCount = static_cast<int>(targetWorkers);
|
||||||
|
} else {
|
||||||
|
workerCount = 6; // Fallback
|
||||||
|
}
|
||||||
workerThreads.reserve(workerCount);
|
workerThreads.reserve(workerCount);
|
||||||
for (int i = 0; i < workerCount; i++) {
|
for (int i = 0; i < workerCount; i++) {
|
||||||
workerThreads.emplace_back(&TerrainManager::workerLoop, this);
|
workerThreads.emplace_back(&TerrainManager::workerLoop, this);
|
||||||
|
|
@ -917,10 +929,16 @@ void TerrainManager::unloadAll() {
|
||||||
m2Renderer->clear();
|
m2Renderer->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart worker threads so streaming can resume
|
// Restart worker threads so streaming can resume (dynamic: scales with available cores)
|
||||||
|
// Use 75% of logical cores for decompression, leaving headroom for render/OS
|
||||||
workerRunning.store(true);
|
workerRunning.store(true);
|
||||||
unsigned hc = std::thread::hardware_concurrency();
|
unsigned hc = std::thread::hardware_concurrency();
|
||||||
workerCount = static_cast<int>(hc > 0 ? std::min(4u, std::max(2u, hc - 1)) : 2u);
|
if (hc > 0) {
|
||||||
|
unsigned targetWorkers = std::max(6u, (hc * 3) / 4); // 75% of cores, minimum 6
|
||||||
|
workerCount = static_cast<int>(targetWorkers);
|
||||||
|
} else {
|
||||||
|
workerCount = 6; // Fallback
|
||||||
|
}
|
||||||
workerThreads.reserve(workerCount);
|
workerThreads.reserve(workerCount);
|
||||||
for (int i = 0; i < workerCount; i++) {
|
for (int i = 0; i < workerCount; i++) {
|
||||||
workerThreads.emplace_back(&TerrainManager::workerLoop, this);
|
workerThreads.emplace_back(&TerrainManager::workerLoop, this);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue