Fix sublayers not persisted on save

- SaveStage: explicitly call rootLayer->Save() before m_stage->Save() to
  ensure root-layer metadata (subLayers field) is always written to disk
- SaveUsdFile: redirect to SaveAs when the root layer is anonymous so
  Ctrl+S on an in-memory stage no longer silently does nothing
- SaveUsdFileAs: call RefreshManagers() after SaveStageAs so LayerManager
  stays in sync with the newly opened file-backed stage; without this,
  sublayers added after a Save As went to the old stale stage and were lost
- LayerCreateCommand::Execute: use GetSublayers() (sublayer-local indices)
  instead of GetLayerStack() (full-stack indices) to record m_insertedIndex,
  fixing undo of add-sublayer removing the wrong layer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 07:49:34 +08:00
parent 9816fb8759
commit e0b0fd8204
3 changed files with 40 additions and 13 deletions
+13
View File
@@ -134,6 +134,12 @@ bool UsdStageManager::SaveStage() {
return false; return false;
} }
SdfLayerHandle rootLayer = m_stage->GetRootLayer();
if (!rootLayer || rootLayer->IsAnonymous()) {
SetError("Stage has no file path — use Save As to save to disk");
return false;
}
try { try {
LOG_INFO("Saving USD stage"); LOG_INFO("Saving USD stage");
@@ -142,6 +148,13 @@ bool UsdStageManager::SaveStage() {
// cannot be saved by UsdStage::Save() itself). // cannot be saved by UsdStage::Save() itself).
MergeSessionLayerIntoRoot(m_stage); MergeSessionLayerIntoRoot(m_stage);
// Explicitly save the root layer first. UsdStage::Save() iterates
// the PCP composition stack and saves dirty layers, but sublayer-path
// edits (InsertSubLayerPath) are root-layer metadata changes that may
// not be flushed by stage-level Save() in all USD versions.
rootLayer->Save();
// Save any other dirty sublayers that have file paths.
m_stage->Save(); m_stage->Save();
LOG_INFO("Successfully saved USD stage"); LOG_INFO("Successfully saved USD stage");
return true; return true;
+8 -8
View File
@@ -15,15 +15,15 @@ LayerCreateCommand::LayerCreateCommand(LayerManager* mgr, std::string path, int
void LayerCreateCommand::Execute() { void LayerCreateCommand::Execute() {
if (!m_mgr) return; if (!m_mgr) return;
// Record how many sublayers exist before insert to compute the real index used. auto before = m_mgr->GetSublayers().size();
auto before = m_mgr->GetLayerStack().size();
m_mgr->CreateSublayer(m_path, m_index); m_mgr->CreateSublayer(m_path, m_index);
auto after = m_mgr->GetLayerStack().size(); // Find the sublayer-local index so Undo() can call RemoveSublayer() correctly.
// If a layer was actually inserted, compute its index. // RemoveSublayer() uses indices into GetSubLayerPaths() (0..N-1), not the full
if (after > before) { // layer stack that includes session and root layers.
auto layers = m_mgr->GetLayerStack(); auto sublayers = m_mgr->GetSublayers();
for (int i = 0; i < static_cast<int>(layers.size()); ++i) { if (sublayers.size() > before) {
if (layers[i].identifier == m_path || layers[i].displayName == m_path) { for (int i = 0; i < static_cast<int>(sublayers.size()); ++i) {
if (sublayers[i].identifier == m_path) {
m_insertedIndex = i; m_insertedIndex = i;
break; break;
} }
+14
View File
@@ -398,6 +398,14 @@ void Application::SaveUsdFile() {
return; return;
} }
// If the current stage is in-memory (anonymous root layer), fall through to
// Save As — UsdStage::Save() cannot write anonymous layers to disk.
auto rootLayer = m_stageManager->GetRootLayer();
if (!rootLayer || rootLayer->IsAnonymous()) {
SaveUsdFileAs();
return;
}
if (!m_stageManager->SaveStage()) { if (!m_stageManager->SaveStage()) {
LOG_ERROR("Failed to save USD file: " + m_stageManager->GetLastError()); LOG_ERROR("Failed to save USD file: " + m_stageManager->GetLastError());
} }
@@ -418,6 +426,12 @@ void Application::SaveUsdFileAs() {
if (!filePath.empty()) { if (!filePath.empty()) {
if (!m_stageManager->SaveStageAs(filePath)) { if (!m_stageManager->SaveStageAs(filePath)) {
LOG_ERROR("Failed to save USD file: " + m_stageManager->GetLastError()); LOG_ERROR("Failed to save USD file: " + m_stageManager->GetLastError());
} else {
// SaveStageAs reopens m_stageManager's stage from the new file path.
// RefreshManagers syncs m_layerManager (and others) to that new stage;
// without this, subsequent sublayer edits go to the old (now stale) stage
// and are silently lost on the next save.
RefreshManagers();
} }
} }
} }