Batch GPU uploads to eliminate per-upload fence waits (stutter fix)

Every uploadBuffer/VkTexture::upload called immediateSubmit which did a
separate vkQueueSubmit + vkWaitForFences. Loading a single creature model
with textures caused 4-8+ fence waits; terrain chunks caused 80+ per batch.

Added beginUploadBatch/endUploadBatch to VkContext: records all upload
commands into a single command buffer, submits once with one fence wait.
Staging buffers are deferred for cleanup after the batch completes.

Wrapped in batch mode:
- CharacterRenderer::loadModel (creature VB/IB + textures)
- M2Renderer::loadModel (doodad VB/IB + textures)
- TerrainRenderer::loadTerrain/loadTerrainIncremental (chunk geometry + textures)
- TerrainRenderer::uploadPreloadedTextures
- WMORenderer::loadModel (group geometry + textures)
This commit is contained in:
Kelsi 2026-03-07 12:19:59 -08:00
parent 884b72bc1c
commit 16b4336700
8 changed files with 97 additions and 4 deletions

View file

@ -1423,10 +1423,44 @@ void VkContext::endSingleTimeCommands(VkCommandBuffer cmd) {
}
void VkContext::immediateSubmit(std::function<void(VkCommandBuffer cmd)>&& function) {
if (inUploadBatch_) {
// Record into the batch command buffer — no submit, no fence wait
function(batchCmd_);
return;
}
VkCommandBuffer cmd = beginSingleTimeCommands();
function(cmd);
endSingleTimeCommands(cmd);
}
void VkContext::beginUploadBatch() {
uploadBatchDepth_++;
if (inUploadBatch_) return; // already in a batch (nested call)
inUploadBatch_ = true;
batchCmd_ = beginSingleTimeCommands();
}
void VkContext::endUploadBatch() {
if (uploadBatchDepth_ <= 0) return;
uploadBatchDepth_--;
if (uploadBatchDepth_ > 0) return; // still inside an outer batch
inUploadBatch_ = false;
// Submit all recorded commands with a single fence wait
endSingleTimeCommands(batchCmd_);
batchCmd_ = VK_NULL_HANDLE;
// Destroy all deferred staging buffers
for (auto& staging : batchStagingBuffers_) {
destroyBuffer(allocator, staging);
}
batchStagingBuffers_.clear();
}
void VkContext::deferStagingCleanup(AllocatedBuffer staging) {
batchStagingBuffers_.push_back(staging);
}
} // namespace rendering
} // namespace wowee