perf: constexpr reciprocals, cache redundant lookups, consolidate texture maps

- Hoist DBC field index lookups before loops in game_handler (7 DBC iteration loops)
- Cache getSkybox()/getPosition() calls instead of redundant per-frame queries
- Merge textureHasAlphaByPtr_ + textureColorKeyBlackByPtr_ into single map
- Add constexpr for DEG_TO_RAD, reciprocal constants, physics delta
- Add reserve() for WMO/M2 collision grid queries and portal BFS
- Frustum plane normalize: inversesqrt instead of length+divide
- M2 particle emission: inversesqrt for direction normalization
- Parse creature display IDs from query response
- UI: show spell names/IDs as fallback instead of "Unknown"
This commit is contained in:
Kelsi 2026-03-27 16:47:30 -07:00
parent b0466e9029
commit d26eed1e7c
9 changed files with 153 additions and 104 deletions

View file

@ -18,6 +18,8 @@ namespace rendering {
namespace {
constexpr float kMaxPhysicsDelta = 1.0f / 30.0f;
std::optional<float> selectReachableFloor(const std::optional<float>& terrainH,
const std::optional<float>& wmoH,
float refZ,
@ -157,7 +159,7 @@ void CameraController::update(float deltaTime) {
return;
}
// Keep physics integration stable during render hitches to avoid floor tunneling.
const float physicsDeltaTime = std::min(deltaTime, 1.0f / 30.0f);
const float physicsDeltaTime = std::min(deltaTime, kMaxPhysicsDelta);
// During taxi flights, skip movement logic but keep camera orbit/zoom controls.
if (externalFollow_) {

View file

@ -342,8 +342,7 @@ void CharacterRenderer::shutdown() {
// Clean up texture cache (VkTexture unique_ptrs auto-destroy)
textureCache.clear();
textureHasAlphaByPtr_.clear();
textureColorKeyBlackByPtr_.clear();
texturePropsByPtr_.clear();
textureCacheBytes_ = 0;
textureCacheCounter_ = 0;
@ -437,8 +436,7 @@ void CharacterRenderer::clear() {
// Clear texture cache (VkTexture unique_ptrs auto-destroy)
textureCache.clear();
textureHasAlphaByPtr_.clear();
textureColorKeyBlackByPtr_.clear();
texturePropsByPtr_.clear();
textureCacheBytes_ = 0;
textureCacheCounter_ = 0;
loggedTextureLoadFails_.clear();
@ -745,8 +743,7 @@ VkTexture* CharacterRenderer::loadTexture(const std::string& path) {
}
textureCacheBytes_ += e.approxBytes;
textureHasAlphaByPtr_[texPtr] = hasAlpha;
textureColorKeyBlackByPtr_[texPtr] = colorKeyBlackHint;
texturePropsByPtr_[texPtr] = {hasAlpha, colorKeyBlackHint};
textureCache[key] = std::move(e);
failedTextureCache_.erase(key);
failedTextureRetryAt_.erase(key);
@ -2297,10 +2294,11 @@ void CharacterRenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
bool alphaCutout = false;
bool colorKeyBlack = false;
if (texPtr != nullptr && texPtr != whiteTexture_.get()) {
auto ait = textureHasAlphaByPtr_.find(texPtr);
alphaCutout = (ait != textureHasAlphaByPtr_.end()) ? ait->second : false;
auto cit = textureColorKeyBlackByPtr_.find(texPtr);
colorKeyBlack = (cit != textureColorKeyBlackByPtr_.end()) ? cit->second : false;
auto pit = texturePropsByPtr_.find(texPtr);
if (pit != texturePropsByPtr_.end()) {
alphaCutout = pit->second.hasAlpha;
colorKeyBlack = pit->second.colorKeyBlack;
}
}
const bool blendNeedsCutout = (blendMode == 1) || (blendMode >= 2 && !alphaCutout);
const bool unlit = ((materialFlags & 0x01) != 0) || (blendMode >= 3);

View file

@ -46,6 +46,7 @@ bool envFlagEnabled(const char* key, bool defaultValue) {
static constexpr uint32_t kParticleFlagRandomized = 0x40;
static constexpr uint32_t kParticleFlagTiled = 0x80;
static constexpr float kSmokeEmitInterval = 1.0f / 48.0f;
float computeGroundDetailDownOffset(const M2ModelGPU& model, float scale) {
// Keep a tiny sink to avoid hovering, but cap pivot compensation so details
@ -750,8 +751,7 @@ void M2Renderer::shutdown() {
textureCache.clear();
textureCacheBytes_ = 0;
textureCacheCounter_ = 0;
textureHasAlphaByPtr_.clear();
textureColorKeyBlackByPtr_.clear();
texturePropsByPtr_.clear();
failedTextureCache_.clear();
failedTextureRetryAt_.clear();
loggedTextureLoadFails_.clear();
@ -1356,18 +1356,13 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) {
(!tcls.likelyFlame || modelLanternFamily));
bgpu.glowCardLike = bgpu.lanternGlowHint && tcls.hasGlowCardToken;
bgpu.glowTint = tcls.glowTint;
bool texHasAlpha = false;
if (tex != nullptr && tex != whiteTexture_.get()) {
auto ait = textureHasAlphaByPtr_.find(tex);
texHasAlpha = (ait != textureHasAlphaByPtr_.end()) ? ait->second : false;
auto pit = texturePropsByPtr_.find(tex);
if (pit != texturePropsByPtr_.end()) {
bgpu.hasAlpha = pit->second.hasAlpha;
bgpu.colorKeyBlack = pit->second.colorKeyBlack;
}
}
bgpu.hasAlpha = texHasAlpha;
bool colorKeyBlack = false;
if (tex != nullptr && tex != whiteTexture_.get()) {
auto cit = textureColorKeyBlackByPtr_.find(tex);
colorKeyBlack = (cit != textureColorKeyBlackByPtr_.end()) ? cit->second : false;
}
bgpu.colorKeyBlack = colorKeyBlack;
// textureCoordIndex is an index into a texture coord combo table, not directly
// a UV set selector. Most batches have index=0 (UV set 0). We always use UV set 0
// since we don't have the full combo table — dual-UV effects are rare edge cases.
@ -1443,18 +1438,13 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) {
bgpu.indexStart = 0;
bgpu.indexCount = gpuModel.indexCount;
bgpu.texture = allTextures.empty() ? whiteTexture_.get() : allTextures[0];
bool texHasAlpha = false;
if (bgpu.texture != nullptr && bgpu.texture != whiteTexture_.get()) {
auto ait = textureHasAlphaByPtr_.find(bgpu.texture);
texHasAlpha = (ait != textureHasAlphaByPtr_.end()) ? ait->second : false;
auto pit = texturePropsByPtr_.find(bgpu.texture);
if (pit != texturePropsByPtr_.end()) {
bgpu.hasAlpha = pit->second.hasAlpha;
bgpu.colorKeyBlack = pit->second.colorKeyBlack;
}
}
bgpu.hasAlpha = texHasAlpha;
bool colorKeyBlack = false;
if (bgpu.texture != nullptr && bgpu.texture != whiteTexture_.get()) {
auto cit = textureColorKeyBlackByPtr_.find(bgpu.texture);
colorKeyBlack = (cit != textureColorKeyBlackByPtr_.end()) ? cit->second : false;
}
bgpu.colorKeyBlack = colorKeyBlack;
gpuModel.batches.push_back(bgpu);
}
@ -1915,7 +1905,7 @@ void M2Renderer::update(float deltaTime, const glm::vec3& cameraPos, const glm::
std::uniform_real_distribution<float> distDrift(-0.2f, 0.2f);
smokeEmitAccum += deltaTime;
float emitInterval = 1.0f / 48.0f; // 48 particles per second per emitter (was 32; increased for denser lava/magma steam effects in sparse areas)
constexpr float emitInterval = kSmokeEmitInterval; // 48 particles per second per emitter
if (smokeEmitAccum >= emitInterval &&
static_cast<int>(smokeParticles.size()) < MAX_SMOKE_PARTICLES) {
@ -4285,8 +4275,7 @@ VkTexture* M2Renderer::loadTexture(const std::string& path, uint32_t texFlags) {
textureCache[key] = std::move(e);
failedTextureCache_.erase(key);
failedTextureRetryAt_.erase(key);
textureHasAlphaByPtr_[texPtr] = hasAlpha;
textureColorKeyBlackByPtr_[texPtr] = colorKeyBlackHint;
texturePropsByPtr_[texPtr] = {hasAlpha, colorKeyBlackHint};
LOG_DEBUG("M2: Loaded texture: ", path, " (", blp.width, "x", blp.height, ")");
return texPtr;

View file

@ -5473,7 +5473,8 @@ void Renderer::renderWorld(game::World* world, game::GameHandler* gameHandler) {
static const bool skipSky = (std::getenv("WOWEE_SKIP_SKY") != nullptr);
// Get time of day for sky-related rendering
float timeOfDay = (skySystem && skySystem->getSkybox()) ? skySystem->getSkybox()->getTimeOfDay() : 12.0f;
auto* skybox = skySystem ? skySystem->getSkybox() : nullptr;
float timeOfDay = skybox ? skybox->getTimeOfDay() : 12.0f;
// ── Multithreaded secondary command buffer recording ──
// Terrain, WMO, and M2 record on worker threads while main thread handles
@ -6427,13 +6428,14 @@ void Renderer::renderReflectionPass() {
bool canRenderScene = (vkCtx->getMsaaSamples() == VK_SAMPLE_COUNT_1_BIT);
// Find dominant water height near camera
auto waterH = waterRenderer->getDominantWaterHeight(camera->getPosition());
const glm::vec3 camPos = camera->getPosition();
auto waterH = waterRenderer->getDominantWaterHeight(camPos);
if (!waterH) return;
float waterHeight = *waterH;
// Skip reflection if camera is underwater (Z is up)
if (camera->getPosition().z < waterHeight + 0.5f) return;
if (camPos.z < waterHeight + 0.5f) return;
// Compute reflected view and oblique projection
glm::mat4 reflView = WaterRenderer::computeReflectedView(*camera, waterHeight);
@ -6448,7 +6450,7 @@ void Renderer::renderReflectionPass() {
reflData.view = reflView;
reflData.projection = reflProj;
// Reflected camera position (Z is up)
glm::vec3 reflPos = camera->getPosition();
glm::vec3 reflPos = camPos;
reflPos.z = 2.0f * waterHeight - reflPos.z;
reflData.viewPos = glm::vec4(reflPos, 1.0f);
std::memcpy(reflPerFrameUBOMapped, &reflData, sizeof(GPUPerFrameData));
@ -6460,7 +6462,8 @@ void Renderer::renderReflectionPass() {
// Render scene into reflection texture (sky + terrain + WMO only for perf)
if (skySystem) {
rendering::SkyParams skyParams;
skyParams.timeOfDay = (skySystem->getSkybox()) ? skySystem->getSkybox()->getTimeOfDay() : 12.0f;
auto* reflSkybox = skySystem->getSkybox();
skyParams.timeOfDay = reflSkybox ? reflSkybox->getTimeOfDay() : 12.0f;
if (lightingManager) {
const auto& lp = lightingManager->getLightingParams();
skyParams.directionalDir = lp.directionalDir;

View file

@ -48,6 +48,10 @@ constexpr size_t ALPHA_MAP_PACKED = 2048; // 64×64 packed 4-bit alpha (half
constexpr uint8_t ALPHA_FILL_FLAG = 0x80; // RLE command: fill vs. copy
constexpr uint8_t ALPHA_COUNT_MASK = 0x7F; // RLE command: count bits
// Placement transform constants
constexpr float kDegToRad = 3.14159f / 180.0f;
constexpr float kInv1024 = 1.0f / 1024.0f;
int computeTerrainWorkerCount() {
const char* raw = std::getenv("WOWEE_TERRAIN_WORKERS");
if (raw && *raw) {
@ -491,11 +495,11 @@ std::shared_ptr<PendingTile> TerrainManager::prepareTile(int x, int y) {
p.uniqueId = placement.uniqueId;
p.position = glPos;
p.rotation = glm::vec3(
-placement.rotation[2] * 3.14159f / 180.0f,
-placement.rotation[0] * 3.14159f / 180.0f,
(placement.rotation[1] + 180.0f) * 3.14159f / 180.0f
-placement.rotation[2] * kDegToRad,
-placement.rotation[0] * kDegToRad,
(placement.rotation[1] + 180.0f) * kDegToRad
);
p.scale = placement.scale / 1024.0f;
p.scale = placement.scale * kInv1024;
pending->m2Placements.push_back(p);
}
@ -561,9 +565,9 @@ std::shared_ptr<PendingTile> TerrainManager::prepareTile(int x, int y) {
placement.position[2]);
glm::vec3 rot(
-placement.rotation[2] * 3.14159f / 180.0f,
-placement.rotation[0] * 3.14159f / 180.0f,
(placement.rotation[1] + 180.0f) * 3.14159f / 180.0f
-placement.rotation[2] * kDegToRad,
-placement.rotation[0] * kDegToRad,
(placement.rotation[1] + 180.0f) * kDegToRad
);
// Pre-load WMO doodads (M2 models inside WMO)