Fix GPU resource leaks and re-entrant world loading for instance transitions

Reset descriptor pools in CharacterRenderer/M2Renderer/WMORenderer on map
change to prevent VK_ERROR_DEVICE_LOST from pool exhaustion. Defer re-entrant
SMSG_NEW_WORLD during active world load to avoid recursive cleanup crashes.
Gate swim bubbles on swimming state, skip redundant shadow pipeline re-init,
add WOWEE_SKIP_* env vars for render isolation debugging.
This commit is contained in:
Kelsi 2026-03-02 08:06:35 -08:00
parent 19652ae521
commit 48eb0b70a3
9 changed files with 255 additions and 47 deletions

View file

@ -383,6 +383,85 @@ void CharacterRenderer::shutdown() {
vkCtx_ = nullptr;
}
void CharacterRenderer::clear() {
if (!vkCtx_) return;
LOG_INFO("CharacterRenderer::clear instances=", instances.size(),
" models=", models.size());
vkDeviceWaitIdle(vkCtx_->getDevice());
VkDevice device = vkCtx_->getDevice();
VmaAllocator alloc = vkCtx_->getAllocator();
// Destroy GPU resources for all models
for (auto& pair : models) {
destroyModelGPU(pair.second);
}
// Destroy bone buffers for all instances
for (auto& pair : instances) {
destroyInstanceBones(pair.second);
}
// Clear texture cache (VkTexture unique_ptrs auto-destroy)
textureCache.clear();
textureHasAlphaByPtr_.clear();
textureColorKeyBlackByPtr_.clear();
textureCacheBytes_ = 0;
textureCacheCounter_ = 0;
loggedTextureLoadFails_.clear();
// Clear composite and failed caches
compositeCache_.clear();
failedTextureCache_.clear();
// Recreate default textures (needed by loadModel/loadTexture fallbacks)
whiteTexture_.reset();
transparentTexture_.reset();
flatNormalTexture_.reset();
{
uint8_t white[] = {255, 255, 255, 255};
whiteTexture_ = std::make_unique<VkTexture>();
whiteTexture_->upload(*vkCtx_, white, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, false);
whiteTexture_->createSampler(device, VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_ADDRESS_MODE_REPEAT);
}
{
uint8_t transparent[] = {0, 0, 0, 0};
transparentTexture_ = std::make_unique<VkTexture>();
transparentTexture_->upload(*vkCtx_, transparent, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, false);
transparentTexture_->createSampler(device, VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_ADDRESS_MODE_REPEAT);
}
{
uint8_t flatNormal[] = {128, 128, 255, 128};
flatNormalTexture_ = std::make_unique<VkTexture>();
flatNormalTexture_->upload(*vkCtx_, flatNormal, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, false);
flatNormalTexture_->createSampler(device, VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_ADDRESS_MODE_REPEAT);
}
models.clear();
instances.clear();
// Release deferred transient material UBOs
for (int i = 0; i < 2; i++) {
for (const auto& b : transientMaterialUbos_[i]) {
if (b.first) {
vmaDestroyBuffer(alloc, b.first, b.second);
}
}
transientMaterialUbos_[i].clear();
}
// Reset descriptor pools (don't destroy — reuse for new allocations)
for (int i = 0; i < 2; i++) {
if (materialDescPools_[i]) {
vkResetDescriptorPool(device, materialDescPools_[i], 0);
}
}
if (boneDescPool_) {
vkResetDescriptorPool(device, boneDescPool_, 0);
}
}
void CharacterRenderer::destroyModelGPU(M2ModelGPU& gpuModel) {
if (!vkCtx_) return;
VmaAllocator alloc = vkCtx_->getAllocator();