Preload terrain textures on background thread and fix ramp Z-snapping

Load BLP texture data during prepareTile() and upload to GL cache in
finalizeTile(), eliminating file I/O stalls on the main thread. Reduce
ready tiles per frame to 1. Fix camera sweep to snap Z to ramp surfaces.
Change hearthstone action bar slot from spell to item.
This commit is contained in:
Kelsi 2026-02-08 01:16:23 -08:00
parent 0ce38cfb99
commit d910073d7a
6 changed files with 66 additions and 13 deletions

View file

@ -465,6 +465,11 @@ void CameraController::update(float deltaTime) {
if (!walkable) {
candidate.x = adjusted.x;
candidate.y = adjusted.y;
} else if (floorH && *floorH > candidate.z) {
// Snap Z to ramp surface so subsequent sweep
// steps measure feetZ from the ramp, not the
// starting position.
candidate.z = *floorH;
}
}
}

View file

@ -465,11 +465,19 @@ std::unique_ptr<PendingTile> TerrainManager::prepareTile(int x, int y) {
}
}
// Pre-load terrain texture BLP data on background thread so finalizeTile
// doesn't block the main thread with file I/O.
for (const auto& texPath : pending->terrain.textures) {
if (pending->preloadedTextures.find(texPath) != pending->preloadedTextures.end()) continue;
pending->preloadedTextures[texPath] = assetManager->loadTexture(texPath);
}
LOG_DEBUG("Prepared tile [", x, ",", y, "]: ",
pending->m2Models.size(), " M2 models, ",
pending->m2Placements.size(), " M2 placements, ",
pending->wmoModels.size(), " WMOs, ",
pending->wmoDoodads.size(), " WMO doodads");
pending->wmoDoodads.size(), " WMO doodads, ",
pending->preloadedTextures.size(), " textures");
return pending;
}
@ -489,6 +497,11 @@ void TerrainManager::finalizeTile(std::unique_ptr<PendingTile> pending) {
return;
}
// Upload pre-loaded textures to the GL cache so loadTerrain avoids file I/O
if (!pending->preloadedTextures.empty()) {
terrainRenderer->uploadPreloadedTextures(pending->preloadedTextures);
}
// Upload terrain to GPU
if (!terrainRenderer->loadTerrain(pending->mesh, pending->terrain.textures, x, y)) {
LOG_ERROR("Failed to upload terrain to GPU for tile [", x, ",", y, "]");
@ -657,9 +670,9 @@ void TerrainManager::workerLoop() {
}
void TerrainManager::processReadyTiles() {
// Process up to 2 ready tiles per frame to spread GPU work
// Process up to 1 ready tile per frame to avoid main-thread stalls
int processed = 0;
const int maxPerFrame = 2;
const int maxPerFrame = 1;
while (processed < maxPerFrame) {
std::unique_ptr<PendingTile> pending;

View file

@ -252,6 +252,33 @@ GLuint TerrainRenderer::loadTexture(const std::string& path) {
return textureID;
}
void TerrainRenderer::uploadPreloadedTextures(const std::unordered_map<std::string, pipeline::BLPImage>& textures) {
for (const auto& [path, blp] : textures) {
// Skip if already cached
if (textureCache.find(path) != textureCache.end()) continue;
if (!blp.isValid()) {
textureCache[path] = whiteTexture;
continue;
}
GLuint textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
blp.width, blp.height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, blp.data.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glGenerateMipmap(GL_TEXTURE_2D);
applyAnisotropicFiltering();
glBindTexture(GL_TEXTURE_2D, 0);
textureCache[path] = textureID;
}
}
GLuint TerrainRenderer::createAlphaTexture(const std::vector<uint8_t>& alphaData) {
if (alphaData.empty()) {
return 0;