From 09e867eb07c35fc7d2acb807a33b6123b52eda6e Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 5 May 2026 23:53:52 -0700 Subject: [PATCH] fix(editor): move rebuildObjects AFTER beginFrame (instance SSBO fix) ROOT CAUSE of NPCs not rendering: rebuildObjects() called createInstance() BEFORE beginFrame(), causing instance SSBO writes to use the wrong frame index. The M2 renderer writes instance transforms to a per-frame buffer indexed by getCurrentFrame(), but the frame index isn't valid until after beginFrame() returns. This is the same bug documented in the project memory: "M2 models not rendering (draws=0): update() was called before beginFrame(), causing frame index mismatch in instance SSBO" Models loaded correctly (log confirmed 2192v 7926i 8b) but instances were invisible because their transform data was written to the wrong frame buffer. Fix: move the rebuild block after beginFrame(), alongside update(). --- tools/editor/editor_app.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/editor/editor_app.cpp b/tools/editor/editor_app.cpp index 1c90d3ec..4e388bd8 100644 --- a/tools/editor/editor_app.cpp +++ b/tools/editor/editor_app.cpp @@ -125,17 +125,6 @@ void EditorApp::run() { lastNpcCount_ = npcCount; } - if (objChanged || objectsDirty_) { - // Full M2 rebuild only when placed objects change - objectsDirty_ = false; - lastObjCount_ = objCount; - if (objCount > 0 || npcCount > 0) { - vkDeviceWaitIdle(vkCtx->getDevice()); - viewport_.rebuildObjects(objectPlacer_.getObjects(), npcSpawner_.getSpawns()); - } - lastNpcCount_ = npcCount; // sync after rebuild - } - // Show gizmo arrows on selected object auto& gizmo = viewport_.getGizmo(); if (auto* sel = objectPlacer_.getSelected()) { @@ -148,6 +137,17 @@ void EditorApp::run() { VkCommandBuffer cmd = vkCtx->beginFrame(imageIndex); if (cmd == VK_NULL_HANDLE) continue; + // Rebuild objects AFTER beginFrame so instance SSBO uses correct frame index + if (objChanged || objectsDirty_) { + objectsDirty_ = false; + lastObjCount_ = objCount; + if (objCount > 0 || npcCount > 0) { + vkDeviceWaitIdle(vkCtx->getDevice()); + viewport_.rebuildObjects(objectPlacer_.getObjects(), npcSpawner_.getSpawns()); + } + lastNpcCount_ = npcCount; + } + // Update M2 animations AFTER beginFrame (so getCurrentFrame is correct) viewport_.update(dt);