Bugfixes to enable slice-to-prefab conversion to run with less warnings/errors/crashes (#768)
While trying to process all slices and levels in Automated Testing, a few bugs came up that needed to be addressed: - [LYN-3832] TransformComponent had a field removed without updating the version number and converter, which caused a lot of excessive warnings - SliceComponent would crash in debug builds on instantiation failures due to a null dereference that was guarded against in most but not all places - SliceConverter now detects when nested slices exist and gracefully warns about it. - InstanceUpdateExecutor / TemplateInstanceMapper will now immediately remove instances that are unregistered, so that any in the queue don't get processed on a subsequent tick. This was causing crashes when the instance was destroyed before the processing occurred. It also has a side benefit of preventing the same instance from executing multiple times. - Minor logic bugfix to the pack close warning, the boolean check was flipped. Also added an early-out on SetTemplateId, since this was causing some unnecessary instance queue entries.
This commit is contained in:
@@ -1740,7 +1740,10 @@ namespace AZ
|
||||
if (!iter->IsInstantiated())
|
||||
{
|
||||
#if defined(AZ_ENABLE_TRACING)
|
||||
Data::Asset<SliceAsset> thisAsset = Data::AssetManager::Instance().FindAsset(GetMyAsset()->GetId(), AZ::Data::AssetLoadBehavior::Default);
|
||||
Data::Asset<SliceAsset> thisAsset = GetMyAsset()
|
||||
? Data::Asset<SliceAsset>(Data::AssetManager::Instance().FindAsset(
|
||||
GetMyAsset()->GetId(), AZ::Data::AssetLoadBehavior::Default))
|
||||
: Data::Asset<SliceAsset>();
|
||||
AZ_Warning("Slice", false, "Removing %d instances of slice asset %s from parent asset %s due to failed instantiation. "
|
||||
"Saving parent asset will result in loss of slice data.",
|
||||
iter->GetInstances().size(),
|
||||
|
||||
@@ -80,6 +80,12 @@ namespace AzToolsFramework
|
||||
|
||||
void Instance::SetTemplateId(const TemplateId& templateId)
|
||||
{
|
||||
// If we aren't changing the template Id, there's no need to unregister / re-register
|
||||
if (templateId == m_templateId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If this instance's templateId is valid, we should be able to unregister this instance from
|
||||
// Template to Instance mapping successfully.
|
||||
if (m_templateId != InvalidTemplateId &&
|
||||
|
||||
+17
-6
@@ -72,10 +72,18 @@ namespace AzToolsFramework
|
||||
|
||||
for (auto instance : findInstancesResult->get())
|
||||
{
|
||||
m_instancesUpdateQueue.emplace(instance);
|
||||
m_instancesUpdateQueue.emplace_back(instance);
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceUpdateExecutor::RemoveTemplateInstanceFromQueue(const Instance* instance)
|
||||
{
|
||||
AZStd::erase_if(m_instancesUpdateQueue, [instance](Instance* entry)
|
||||
{
|
||||
return entry == instance;
|
||||
});
|
||||
}
|
||||
|
||||
bool InstanceUpdateExecutor::UpdateTemplateInstancesInQueue()
|
||||
{
|
||||
bool isUpdateSuccessful = true;
|
||||
@@ -97,9 +105,16 @@ namespace AzToolsFramework
|
||||
ToolsApplicationRequestBus::BroadcastResult(selectedEntityIds, &ToolsApplicationRequests::GetSelectedEntities);
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, EntityIdList());
|
||||
|
||||
for (int i = 0; i < instanceCountToUpdateInBatch; ++i)
|
||||
// Process all instances in the queue, capped to the batch size.
|
||||
// Even though we potentially initialized the batch size to the queue, it's possible for the queue size to shrink
|
||||
// during instance processing if the instance gets deleted and it was queued multiple times. To handle this, we
|
||||
// make sure to end the loop once the queue is empty, regardless of what the initial size was.
|
||||
for (int i = 0; (i < instanceCountToUpdateInBatch) && !m_instancesUpdateQueue.empty(); ++i)
|
||||
{
|
||||
Instance* instanceToUpdate = m_instancesUpdateQueue.front();
|
||||
m_instancesUpdateQueue.pop_front();
|
||||
AZ_Assert(instanceToUpdate != nullptr, "Invalid instance on update queue.");
|
||||
|
||||
TemplateId instanceTemplateId = instanceToUpdate->GetTemplateId();
|
||||
if (currentTemplateId != instanceTemplateId)
|
||||
{
|
||||
@@ -115,7 +130,6 @@ namespace AzToolsFramework
|
||||
|
||||
// Remove the instance from update queue if its corresponding template couldn't be found
|
||||
isUpdateSuccessful = false;
|
||||
m_instancesUpdateQueue.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -127,7 +141,6 @@ namespace AzToolsFramework
|
||||
// Since nested instances get reconstructed during propagation, remove any nested instance that no longer
|
||||
// maps to a template.
|
||||
isUpdateSuccessful = false;
|
||||
m_instancesUpdateQueue.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -148,8 +161,6 @@ namespace AzToolsFramework
|
||||
|
||||
isUpdateSuccessful = false;
|
||||
}
|
||||
|
||||
m_instancesUpdateQueue.pop();
|
||||
}
|
||||
|
||||
for (auto entityIdIterator = selectedEntityIds.begin(); entityIdIterator != selectedEntityIds.end(); entityIdIterator++)
|
||||
|
||||
+3
-2
@@ -14,7 +14,7 @@
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/std/containers/queue.h>
|
||||
#include <AzCore/std/containers/deque.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace AzToolsFramework
|
||||
|
||||
void AddTemplateInstancesToQueue(TemplateId instanceTemplateId) override;
|
||||
bool UpdateTemplateInstancesInQueue() override;
|
||||
virtual void RemoveTemplateInstanceFromQueue(const Instance* instance) override;
|
||||
|
||||
void RegisterInstanceUpdateExecutorInterface();
|
||||
void UnregisterInstanceUpdateExecutorInterface();
|
||||
@@ -45,7 +46,7 @@ namespace AzToolsFramework
|
||||
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
|
||||
TemplateInstanceMapperInterface* m_templateInstanceMapperInterface = nullptr;
|
||||
int m_instanceCountToUpdateInBatch = 0;
|
||||
AZStd::queue<Instance*> m_instancesUpdateQueue;
|
||||
AZStd::deque<Instance*> m_instancesUpdateQueue;
|
||||
bool m_updatingTemplateInstancesInQueue { false };
|
||||
};
|
||||
}
|
||||
|
||||
+3
@@ -31,6 +31,9 @@ namespace AzToolsFramework
|
||||
|
||||
// Update Instances in the waiting queue.
|
||||
virtual bool UpdateTemplateInstancesInQueue() = 0;
|
||||
|
||||
// Remove an Instance from the waiting queue.
|
||||
virtual void RemoveTemplateInstanceFromQueue(const Instance* instance) = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -71,6 +72,12 @@ namespace AzToolsFramework
|
||||
|
||||
bool TemplateInstanceMapper::UnregisterInstance(Instance& instance)
|
||||
{
|
||||
// The InstanceUpdateExecutor queries the TemplateInstanceMapper for a list of instances related to a template.
|
||||
// Consequently, if an instance gets unregistered for a template, we need to notify the InstanceUpdateExecutor as well
|
||||
// so that it clears any internal associations that it might have in its queue.
|
||||
AZ_Assert(AZ::Interface<InstanceUpdateExecutorInterface>::Get() != nullptr, "InstanceUpdateExecutor doesn't exist");
|
||||
AZ::Interface<InstanceUpdateExecutorInterface>::Get()->RemoveTemplateInstanceFromQueue(&instance);
|
||||
|
||||
auto found = m_templateIdToInstancesMap.find(instance.GetTemplateId());
|
||||
return found != m_templateIdToInstancesMap.end() &&
|
||||
found->second.erase(&instance) != 0;
|
||||
|
||||
+7
-1
@@ -162,6 +162,12 @@ namespace AzToolsFramework
|
||||
classElement.RemoveElementByName(AZ_CRC("InterpolateScale", 0x9d00b831));
|
||||
}
|
||||
|
||||
if (classElement.GetVersion() < 10)
|
||||
{
|
||||
// The "Sync Enabled" flag is no longer needed.
|
||||
classElement.RemoveElementByName(AZ_CRC_CE("Sync Enabled"));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace Internal
|
||||
@@ -1305,7 +1311,7 @@ namespace AzToolsFramework
|
||||
Field("IsStatic", &TransformComponent::m_isStatic)->
|
||||
Field("InterpolatePosition", &TransformComponent::m_interpolatePosition)->
|
||||
Field("InterpolateRotation", &TransformComponent::m_interpolateRotation)->
|
||||
Version(9, &Internal::TransformComponentDataConverter);
|
||||
Version(10, &Internal::TransformComponentDataConverter);
|
||||
|
||||
if (AZ::EditContext* ptrEdit = serializeContext->GetEditContext())
|
||||
{
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace AZ
|
||||
if (packOpened)
|
||||
{
|
||||
[[maybe_unused]] bool closeResult = archiveInterface->ClosePack(filePath);
|
||||
AZ_Warning("Convert-Slice", !closeResult, "Failed to close '%s'.", filePath.c_str());
|
||||
AZ_Warning("Convert-Slice", closeResult, "Failed to close '%s'.", filePath.c_str());
|
||||
}
|
||||
|
||||
AZ_Printf("Convert-Slice", "Finished converting '%s' to '%s'\n", filePath.c_str(), outputPath.c_str());
|
||||
@@ -166,14 +166,16 @@ namespace AZ
|
||||
}
|
||||
|
||||
// Get all of the entities from the slice.
|
||||
SliceComponent::EntityList sliceEntities;
|
||||
bool getEntitiesResult = sliceComponent->GetEntities(sliceEntities);
|
||||
if ((!getEntitiesResult) || (sliceEntities.empty()))
|
||||
SliceComponent::EntityList sliceEntities = sliceComponent->GetNewEntities();
|
||||
if (sliceEntities.empty())
|
||||
{
|
||||
AZ_Printf("Convert-Slice", " File not converted: Slice entities could not be retrieved.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const SliceComponent::SliceList& sliceList = sliceComponent->GetSlices();
|
||||
AZ_Warning("Convert-Slice", sliceList.empty(), " Slice depends on other slices, this conversion will lose data.\n");
|
||||
|
||||
// Create the Prefab with the entities from the slice
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> sourceInstance(
|
||||
prefabSystemComponent->CreatePrefab(sliceEntities, {}, outputPath));
|
||||
|
||||
Reference in New Issue
Block a user