From ef2578540403fb01adce80c67dd54d7c51e538fd Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sun, 29 Mar 2026 20:14:35 -0700 Subject: [PATCH] fix: terrain chunk UBO allocation failure crashed GPU via null descriptor vmaCreateBuffer return value was silently discarded in both loadTerrain and loadTerrainIncremental. If allocation failed (OOM/fragmentation), the chunk proceeded with a VK_NULL_HANDLE UBO, causing the GPU to read from an invalid descriptor on the next draw call. Now checks the return value and skips the chunk on failure. --- src/rendering/terrain_renderer.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/rendering/terrain_renderer.cpp b/src/rendering/terrain_renderer.cpp index 7543e639..9ba0e732 100644 --- a/src/rendering/terrain_renderer.cpp +++ b/src/rendering/terrain_renderer.cpp @@ -388,17 +388,21 @@ bool TerrainRenderer::loadTerrain(const pipeline::TerrainMesh& mesh, allocCI.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; VmaAllocationInfo mapInfo{}; - vmaCreateBuffer(vkCtx->getAllocator(), &bufCI, &allocCI, - &gpuChunk.paramsUBO, &gpuChunk.paramsAlloc, &mapInfo); + // Check return value — a null UBO handle would cause the GPU to + // read from an invalid descriptor, crashing the driver under + // memory pressure instead of gracefully skipping the chunk. + if (vmaCreateBuffer(vkCtx->getAllocator(), &bufCI, &allocCI, + &gpuChunk.paramsUBO, &gpuChunk.paramsAlloc, &mapInfo) != VK_SUCCESS) { + LOG_WARNING("Terrain chunk UBO allocation failed — skipping chunk"); + destroyChunkGPU(gpuChunk); + continue; + } if (mapInfo.pMappedData) { std::memcpy(mapInfo.pMappedData, ¶ms, sizeof(params)); } - // Allocate and write material descriptor set gpuChunk.materialSet = allocateMaterialSet(); if (!gpuChunk.materialSet) { - // Pool exhaustion can happen transiently while tile churn is high. - // Drop this chunk instead of retaining non-renderable GPU resources. destroyChunkGPU(gpuChunk); continue; } @@ -484,15 +488,18 @@ bool TerrainRenderer::loadTerrainIncremental(const pipeline::TerrainMesh& mesh, allocCI.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; VmaAllocationInfo mapInfo{}; - vmaCreateBuffer(vkCtx->getAllocator(), &bufCI, &allocCI, - &gpuChunk.paramsUBO, &gpuChunk.paramsAlloc, &mapInfo); + if (vmaCreateBuffer(vkCtx->getAllocator(), &bufCI, &allocCI, + &gpuChunk.paramsUBO, &gpuChunk.paramsAlloc, &mapInfo) != VK_SUCCESS) { + LOG_WARNING("Terrain chunk UBO allocation failed (incremental) — skipping chunk"); + destroyChunkGPU(gpuChunk); + continue; + } if (mapInfo.pMappedData) { std::memcpy(mapInfo.pMappedData, ¶ms, sizeof(params)); } gpuChunk.materialSet = allocateMaterialSet(); if (!gpuChunk.materialSet) { - // Keep memory/work bounded under descriptor pool pressure. destroyChunkGPU(gpuChunk); continue; }