@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <AzCore/Debug/Timer.h>
|
||||
#include <AzCore/Debug/TraceMessageBus.h>
|
||||
#include <AzCore/std/typetraits/typetraits.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
|
||||
@@ -14,453 +14,450 @@
|
||||
#include <AzCore/std/parallel/lock.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Data
|
||||
{
|
||||
namespace Data
|
||||
AssetFilterInfo::AssetFilterInfo(const AssetId& id, const AssetType& assetType, AssetLoadBehavior loadBehavior)
|
||||
: m_assetId(id)
|
||||
, m_assetType(assetType)
|
||||
, m_loadBehavior(loadBehavior)
|
||||
{
|
||||
AssetFilterInfo::AssetFilterInfo(const AssetId& id, const AssetType& assetType, AssetLoadBehavior loadBehavior)
|
||||
: m_assetId(id)
|
||||
, m_assetType(assetType)
|
||||
, m_loadBehavior(loadBehavior)
|
||||
}
|
||||
|
||||
AssetFilterInfo::AssetFilterInfo(const Asset<AssetData>& asset)
|
||||
: m_assetId(asset.GetId())
|
||||
, m_assetType(asset.GetType())
|
||||
, m_loadBehavior(asset.GetAutoLoadBehavior())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
AssetId AssetId::CreateString(AZStd::string_view input)
|
||||
{
|
||||
size_t separatorIdx = input.find(':');
|
||||
if (separatorIdx == AZStd::string_view::npos)
|
||||
{
|
||||
return AssetId();
|
||||
}
|
||||
|
||||
AssetFilterInfo::AssetFilterInfo(const Asset<AssetData>& asset)
|
||||
: m_assetId(asset.GetId())
|
||||
, m_assetType(asset.GetType())
|
||||
, m_loadBehavior(asset.GetAutoLoadBehavior())
|
||||
AssetId assetId;
|
||||
assetId.m_guid = Uuid::CreateString(input.data(), separatorIdx);
|
||||
if (assetId.m_guid.IsNull())
|
||||
{
|
||||
return AssetId();
|
||||
}
|
||||
|
||||
assetId.m_subId = strtoul(&input[separatorIdx + 1], nullptr, 16);
|
||||
|
||||
AssetId AssetId::CreateString(AZStd::string_view input)
|
||||
return assetId;
|
||||
}
|
||||
|
||||
void AssetId::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
size_t separatorIdx = input.find(':');
|
||||
if (separatorIdx == AZStd::string_view::npos)
|
||||
{
|
||||
return AssetId();
|
||||
}
|
||||
|
||||
AssetId assetId;
|
||||
assetId.m_guid = Uuid::CreateString(input.data(), separatorIdx);
|
||||
if (assetId.m_guid.IsNull())
|
||||
{
|
||||
return AssetId();
|
||||
}
|
||||
|
||||
assetId.m_subId = strtoul(&input[separatorIdx + 1], nullptr, 16);
|
||||
|
||||
return assetId;
|
||||
serializeContext->Class<Data::AssetId>()
|
||||
->Version(1)
|
||||
->Field("guid", &Data::AssetId::m_guid)
|
||||
->Field("subId", &Data::AssetId::m_subId)
|
||||
;
|
||||
}
|
||||
|
||||
void AssetId::Reflect(AZ::ReflectContext* context)
|
||||
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<Data::AssetId>()
|
||||
->Version(1)
|
||||
->Field("guid", &Data::AssetId::m_guid)
|
||||
->Field("subId", &Data::AssetId::m_subId)
|
||||
;
|
||||
}
|
||||
behaviorContext->Class<Data::AssetId>()
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Asset")
|
||||
->Attribute(AZ::Script::Attributes::Module, "asset")
|
||||
->Constructor()
|
||||
->Constructor<const Uuid&, u32>()
|
||||
->Method("CreateString", &Data::AssetId::CreateString)
|
||||
->Method("IsValid", &Data::AssetId::IsValid)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_valid")
|
||||
->Method("ToString", [](const Data::AssetId* self) { return self->ToString<AZStd::string>(); })
|
||||
->Attribute(AZ::Script::Attributes::Alias, "to_string")
|
||||
->Method("IsEqual", [](const Data::AssetId& self, const Data::AssetId& other) { return self == other; })
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_equal")
|
||||
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal)
|
||||
;
|
||||
|
||||
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<Data::AssetId>()
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Asset")
|
||||
->Attribute(AZ::Script::Attributes::Module, "asset")
|
||||
->Constructor()
|
||||
->Constructor<const Uuid&, u32>()
|
||||
->Method("CreateString", &Data::AssetId::CreateString)
|
||||
->Method("IsValid", &Data::AssetId::IsValid)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_valid")
|
||||
->Method("ToString", [](const Data::AssetId* self) { return self->ToString<AZStd::string>(); })
|
||||
->Attribute(AZ::Script::Attributes::Alias, "to_string")
|
||||
->Method("IsEqual", [](const Data::AssetId& self, const Data::AssetId& other) { return self == other; })
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_equal")
|
||||
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal)
|
||||
;
|
||||
behaviorContext->Class<Data::AssetInfo>()
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Asset")
|
||||
->Attribute(AZ::Script::Attributes::Module, "asset")
|
||||
->Property("assetId", BehaviorValueGetter(&Data::AssetInfo::m_assetId), nullptr)
|
||||
->Property("assetType", BehaviorValueGetter(&Data::AssetInfo::m_assetType), nullptr)
|
||||
->Property("sizeBytes", BehaviorValueGetter(&Data::AssetInfo::m_sizeBytes), nullptr)
|
||||
->Property("relativePath", BehaviorValueGetter(&Data::AssetInfo::m_relativePath), nullptr)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
behaviorContext->Class<Data::AssetInfo>()
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Asset")
|
||||
->Attribute(AZ::Script::Attributes::Module, "asset")
|
||||
->Property("assetId", BehaviorValueGetter(&Data::AssetInfo::m_assetId), nullptr)
|
||||
->Property("assetType", BehaviorValueGetter(&Data::AssetInfo::m_assetType), nullptr)
|
||||
->Property("sizeBytes", BehaviorValueGetter(&Data::AssetInfo::m_sizeBytes), nullptr)
|
||||
->Property("relativePath", BehaviorValueGetter(&Data::AssetInfo::m_relativePath), nullptr)
|
||||
;
|
||||
}
|
||||
namespace AssetInternal
|
||||
{
|
||||
Asset<AssetData> FindOrCreateAsset(const AssetId& id, const AssetType& type, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
return AssetManager::Instance().FindOrCreateAsset(id, type, assetReferenceLoadBehavior);
|
||||
}
|
||||
|
||||
namespace AssetInternal
|
||||
Asset<AssetData> GetAsset(const AssetId& id, const AssetType& type, AssetLoadBehavior assetReferenceLoadBehavior,
|
||||
const AssetLoadParameters& loadParams)
|
||||
{
|
||||
Asset<AssetData> FindOrCreateAsset(const AssetId& id, const AssetType& type, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
return AssetManager::Instance().FindOrCreateAsset(id, type, assetReferenceLoadBehavior);
|
||||
}
|
||||
|
||||
Asset<AssetData> GetAsset(const AssetId& id, const AssetType& type, AssetLoadBehavior assetReferenceLoadBehavior,
|
||||
const AssetLoadParameters& loadParams)
|
||||
{
|
||||
return AssetManager::Instance().GetAsset(id, type, assetReferenceLoadBehavior, loadParams);
|
||||
}
|
||||
|
||||
AssetData::AssetStatus BlockUntilLoadComplete(const Asset<AssetData>& asset)
|
||||
{
|
||||
return AssetManager::Instance().BlockUntilLoadComplete(asset);
|
||||
}
|
||||
|
||||
void UpdateAssetInfo(AssetId& id, AZStd::string& assetHint)
|
||||
{
|
||||
// it is possible that the assetID given is legacy / old and we have a new assetId we can use instead for it.
|
||||
// in that case, upgrade the AssetID to the new one, so that future saves are in the new format.
|
||||
// this function should only be invoked if the feature is turned on in the asset manager as it can be (slightly) expensive
|
||||
|
||||
if ((!AssetManager::IsReady()) || (!AssetManager::Instance().GetAssetInfoUpgradingEnabled()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, id);
|
||||
if (assetInfo.m_assetId.IsValid())
|
||||
{
|
||||
id = assetInfo.m_assetId;
|
||||
if (!assetInfo.m_relativePath.empty())
|
||||
{
|
||||
assetHint = assetInfo.m_relativePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ReloadAsset(AssetData* assetData, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
AssetManager::Instance().ReloadAsset(assetData->GetId(), assetReferenceLoadBehavior);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveAsset(AssetData* assetData, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
AssetManager::Instance().SaveAsset({ assetData, assetReferenceLoadBehavior });
|
||||
return true;
|
||||
}
|
||||
|
||||
Asset<AssetData> GetAssetData(const AssetId& id, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
if (AssetManager::IsReady())
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> assetLock(AssetManager::Instance().m_assetMutex);
|
||||
auto it = AssetManager::Instance().m_assets.find(id);
|
||||
if (it != AssetManager::Instance().m_assets.end())
|
||||
{
|
||||
return { it->second, assetReferenceLoadBehavior };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
AssetId ResolveAssetId(const AssetId& id)
|
||||
{
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, id);
|
||||
if (assetInfo.m_assetId.IsValid())
|
||||
{
|
||||
return assetInfo.m_assetId;
|
||||
}
|
||||
else
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
return AssetManager::Instance().GetAsset(id, type, assetReferenceLoadBehavior, loadParams);
|
||||
}
|
||||
|
||||
AssetData::~AssetData()
|
||||
AssetData::AssetStatus BlockUntilLoadComplete(const Asset<AssetData>& asset)
|
||||
{
|
||||
UnregisterWithHandler();
|
||||
return AssetManager::Instance().BlockUntilLoadComplete(asset);
|
||||
}
|
||||
|
||||
void AssetData::Reflect(AZ::ReflectContext* context)
|
||||
void UpdateAssetInfo(AssetId& id, AZStd::string& assetHint)
|
||||
{
|
||||
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
// it is possible that the assetID given is legacy / old and we have a new assetId we can use instead for it.
|
||||
// in that case, upgrade the AssetID to the new one, so that future saves are in the new format.
|
||||
// this function should only be invoked if the feature is turned on in the asset manager as it can be (slightly) expensive
|
||||
|
||||
if ((!AssetManager::IsReady()) || (!AssetManager::Instance().GetAssetInfoUpgradingEnabled()))
|
||||
{
|
||||
serializeContext->Class<AZ::Data::AssetData>()
|
||||
->Version(1)
|
||||
;
|
||||
}
|
||||
|
||||
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<AssetData>("AssetData")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Asset")
|
||||
->Attribute(AZ::Script::Attributes::Module, "asset")
|
||||
->Method("IsReady", &AssetData::IsReady)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_ready")
|
||||
->Method("IsError", &AssetData::IsError)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_error")
|
||||
->Method("IsLoading", &AssetData::IsLoading)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_loading")
|
||||
->Method("GetId", &AssetData::GetId)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "get_id")
|
||||
->Method("GetUseCount", &AssetData::GetUseCount)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "get_use_count")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void AssetData::Acquire()
|
||||
{
|
||||
AZ_Assert(m_useCount >= 0, "AssetData has been deleted");
|
||||
|
||||
AcquireWeak();
|
||||
++m_useCount;
|
||||
}
|
||||
|
||||
void AssetData::Release()
|
||||
{
|
||||
AZ_Assert(m_useCount > 0, "Usecount is already 0!");
|
||||
|
||||
if (m_useCount.fetch_sub(1) == 1)
|
||||
{
|
||||
if (AssetManager::IsReady())
|
||||
{
|
||||
AssetManager::Instance().OnAssetUnused(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Attempting to release asset after AssetManager has been destroyed!");
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseWeak();
|
||||
}
|
||||
|
||||
void AssetData::AcquireWeak()
|
||||
{
|
||||
AZ_Assert(m_useCount >= 0, "AssetData has been deleted");
|
||||
++m_weakUseCount;
|
||||
}
|
||||
|
||||
void AssetData::ReleaseWeak()
|
||||
{
|
||||
AZ_Assert(m_weakUseCount > 0, "WeakUseCount is already 0");
|
||||
|
||||
AssetId assetId = m_assetId;
|
||||
int creationToken = m_creationToken;
|
||||
AssetType assetType = GetType();
|
||||
bool removeFromHash = IsRegisterReadonlyAndShareable();
|
||||
// default creation token implies that the asset was not created by the asset manager and therefore it cannot be in the asset map.
|
||||
removeFromHash = creationToken == s_defaultCreationToken ? false : removeFromHash;
|
||||
|
||||
if (m_weakUseCount.fetch_sub(1) == 1)
|
||||
{
|
||||
if (AssetManager::IsReady())
|
||||
{
|
||||
AssetManager::Instance().ReleaseAsset(this, assetId, assetType, removeFromHash, creationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Attempting to release asset after AssetManager has been destroyed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetData::IsLoading(bool includeQueued) const
|
||||
{
|
||||
auto curStatus = GetStatus();
|
||||
return(curStatus == AssetStatus::Loading || curStatus == AssetStatus::LoadedPreReady || curStatus==AssetStatus::StreamReady ||
|
||||
(includeQueued && curStatus == AssetStatus::Queued));
|
||||
}
|
||||
|
||||
void AssetData::RegisterWithHandler(AssetHandler* handler)
|
||||
{
|
||||
if (!handler)
|
||||
{
|
||||
AZ_Error("AssetData", false, "No handler to register with");
|
||||
return;
|
||||
}
|
||||
m_registeredHandler = handler;
|
||||
}
|
||||
|
||||
void AssetData::UnregisterWithHandler()
|
||||
{
|
||||
if (m_registeredHandler)
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, id);
|
||||
if (assetInfo.m_assetId.IsValid())
|
||||
{
|
||||
m_registeredHandler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetData::GetFlag(const AssetDataFlags& checkFlag) const
|
||||
{
|
||||
return m_flags[aznumeric_cast<AZStd::size_t>(checkFlag)];
|
||||
}
|
||||
|
||||
void AssetData::SetFlag(const AssetDataFlags& checkFlag, bool setValue)
|
||||
{
|
||||
m_flags.set(aznumeric_cast<AZStd::size_t>(checkFlag), setValue);
|
||||
}
|
||||
|
||||
bool AssetData::GetRequeue() const
|
||||
{
|
||||
return GetFlag(AssetDataFlags::Requeue);
|
||||
}
|
||||
void AssetData::SetRequeue(bool requeue)
|
||||
{
|
||||
SetFlag(AssetDataFlags::Requeue, requeue);
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetCallbacks(const AssetReadyCB& readyCB, const AssetMovedCB& movedCB, const AssetReloadedCB& reloadedCB,
|
||||
const AssetSavedCB& savedCB, const AssetUnloadedCB& unloadedCB, const AssetErrorCB& errorCB, const AssetCanceledCB& cancelCB)
|
||||
{
|
||||
m_onAssetReadyCB = readyCB;
|
||||
m_onAssetMovedCB = movedCB;
|
||||
m_onAssetReloadedCB = reloadedCB;
|
||||
m_onAssetSavedCB = savedCB;
|
||||
m_onAssetUnloadedCB = unloadedCB;
|
||||
m_onAssetErrorCB = errorCB;
|
||||
m_onAssetCanceledCB = cancelCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::ClearCallbacks()
|
||||
{
|
||||
SetCallbacks(AssetBusCallbacks::AssetReadyCB(),
|
||||
AssetBusCallbacks::AssetMovedCB(),
|
||||
AssetBusCallbacks::AssetReloadedCB(),
|
||||
AssetBusCallbacks::AssetSavedCB(),
|
||||
AssetBusCallbacks::AssetUnloadedCB(),
|
||||
AssetBusCallbacks::AssetErrorCB(),
|
||||
AssetBusCallbacks::AssetCanceledCB());
|
||||
}
|
||||
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetReadyCallback(const AssetReadyCB& readyCB)
|
||||
{
|
||||
m_onAssetReadyCB = readyCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetMovedCallback(const AssetMovedCB& movedCB)
|
||||
{
|
||||
m_onAssetMovedCB = movedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetReloadedCallback(const AssetReloadedCB& reloadedCB)
|
||||
{
|
||||
m_onAssetReloadedCB = reloadedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetSavedCallback(const AssetSavedCB& savedCB)
|
||||
{
|
||||
m_onAssetSavedCB = savedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetUnloadedCallback(const AssetUnloadedCB& unloadedCB)
|
||||
{
|
||||
m_onAssetUnloadedCB = unloadedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetErrorCallback(const AssetErrorCB& errorCB)
|
||||
{
|
||||
m_onAssetErrorCB = errorCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetCanceledCallback(const AssetCanceledCB& cancelCB)
|
||||
{
|
||||
m_onAssetCanceledCB = cancelCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetReady(Asset<AssetData> asset)
|
||||
{
|
||||
if (m_onAssetReadyCB)
|
||||
{
|
||||
m_onAssetReadyCB(asset, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetMoved(Asset<AssetData> asset, void* oldDataPointer)
|
||||
{
|
||||
if (m_onAssetMovedCB)
|
||||
{
|
||||
m_onAssetMovedCB(asset, oldDataPointer, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetReloaded(Asset<AssetData> asset)
|
||||
{
|
||||
if (m_onAssetReloadedCB)
|
||||
{
|
||||
m_onAssetReloadedCB(asset, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetSaved(Asset<AssetData> asset, bool isSuccessful)
|
||||
{
|
||||
if (m_onAssetSavedCB)
|
||||
{
|
||||
m_onAssetSavedCB(asset, isSuccessful, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetUnloaded(const AssetId assetId, const AssetType assetType)
|
||||
{
|
||||
if (m_onAssetUnloadedCB)
|
||||
{
|
||||
m_onAssetUnloadedCB(assetId, assetType, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetError(Asset<AssetData> asset)
|
||||
{
|
||||
if (m_onAssetErrorCB)
|
||||
{
|
||||
m_onAssetErrorCB(asset, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetCanceled(const AssetId assetId)
|
||||
{
|
||||
if (m_onAssetCanceledCB)
|
||||
{
|
||||
m_onAssetCanceledCB(assetId, *this);
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ bool AssetFilterNoAssetLoading([[maybe_unused]] const AssetFilterInfo& filterInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
namespace ProductDependencyInfo
|
||||
{
|
||||
AZ::Data::AssetLoadBehavior LoadBehaviorFromFlags(const ProductDependencyFlags& dependencyFlags)
|
||||
{
|
||||
AZ::u8 loadBehaviorValue = 0;
|
||||
for (AZ::u8 thisFlag = aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorLow);
|
||||
thisFlag <= aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorHigh); ++thisFlag)
|
||||
id = assetInfo.m_assetId;
|
||||
if (!assetInfo.m_relativePath.empty())
|
||||
{
|
||||
if (dependencyFlags[thisFlag])
|
||||
{
|
||||
loadBehaviorValue |= (1 << thisFlag);
|
||||
}
|
||||
assetHint = assetInfo.m_relativePath;
|
||||
}
|
||||
return static_cast<AZ::Data::AssetLoadBehavior>(loadBehaviorValue);
|
||||
}
|
||||
|
||||
ProductDependencyFlags CreateFlags(AZ::Data::AssetLoadBehavior autoLoadBehavior)
|
||||
{
|
||||
AZ::Data::ProductDependencyInfo::ProductDependencyFlags returnFlags;
|
||||
AZ::u8 loadBehavior = aznumeric_caster(autoLoadBehavior);
|
||||
for (AZ::u8 thisFlag = aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorLow);
|
||||
thisFlag <= aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorHigh); ++thisFlag)
|
||||
{
|
||||
if (loadBehavior & (1 << thisFlag))
|
||||
{
|
||||
returnFlags[thisFlag] = true;
|
||||
}
|
||||
}
|
||||
return returnFlags;
|
||||
}
|
||||
}
|
||||
} // namespace Data
|
||||
} // namespace AZ
|
||||
|
||||
bool ReloadAsset(AssetData* assetData, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
AssetManager::Instance().ReloadAsset(assetData->GetId(), assetReferenceLoadBehavior);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveAsset(AssetData* assetData, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
AssetManager::Instance().SaveAsset({ assetData, assetReferenceLoadBehavior });
|
||||
return true;
|
||||
}
|
||||
|
||||
Asset<AssetData> GetAssetData(const AssetId& id, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
if (AssetManager::IsReady())
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> assetLock(AssetManager::Instance().m_assetMutex);
|
||||
auto it = AssetManager::Instance().m_assets.find(id);
|
||||
if (it != AssetManager::Instance().m_assets.end())
|
||||
{
|
||||
return { it->second, assetReferenceLoadBehavior };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
AssetId ResolveAssetId(const AssetId& id)
|
||||
{
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, id);
|
||||
if (assetInfo.m_assetId.IsValid())
|
||||
{
|
||||
return assetInfo.m_assetId;
|
||||
}
|
||||
else
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
AssetData::~AssetData()
|
||||
{
|
||||
UnregisterWithHandler();
|
||||
}
|
||||
|
||||
void AssetData::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<AZ::Data::AssetData>()
|
||||
->Version(1)
|
||||
;
|
||||
}
|
||||
|
||||
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<AssetData>("AssetData")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Asset")
|
||||
->Attribute(AZ::Script::Attributes::Module, "asset")
|
||||
->Method("IsReady", &AssetData::IsReady)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_ready")
|
||||
->Method("IsError", &AssetData::IsError)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_error")
|
||||
->Method("IsLoading", &AssetData::IsLoading)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_loading")
|
||||
->Method("GetId", &AssetData::GetId)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "get_id")
|
||||
->Method("GetUseCount", &AssetData::GetUseCount)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "get_use_count")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void AssetData::Acquire()
|
||||
{
|
||||
AZ_Assert(m_useCount >= 0, "AssetData has been deleted");
|
||||
|
||||
AcquireWeak();
|
||||
++m_useCount;
|
||||
}
|
||||
|
||||
void AssetData::Release()
|
||||
{
|
||||
AZ_Assert(m_useCount > 0, "Usecount is already 0!");
|
||||
|
||||
if (m_useCount.fetch_sub(1) == 1)
|
||||
{
|
||||
if (AssetManager::IsReady())
|
||||
{
|
||||
AssetManager::Instance().OnAssetUnused(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Attempting to release asset after AssetManager has been destroyed!");
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseWeak();
|
||||
}
|
||||
|
||||
void AssetData::AcquireWeak()
|
||||
{
|
||||
AZ_Assert(m_useCount >= 0, "AssetData has been deleted");
|
||||
++m_weakUseCount;
|
||||
}
|
||||
|
||||
void AssetData::ReleaseWeak()
|
||||
{
|
||||
AZ_Assert(m_weakUseCount > 0, "WeakUseCount is already 0");
|
||||
|
||||
AssetId assetId = m_assetId;
|
||||
int creationToken = m_creationToken;
|
||||
AssetType assetType = GetType();
|
||||
bool removeFromHash = IsRegisterReadonlyAndShareable();
|
||||
// default creation token implies that the asset was not created by the asset manager and therefore it cannot be in the asset map.
|
||||
removeFromHash = creationToken == s_defaultCreationToken ? false : removeFromHash;
|
||||
|
||||
if (m_weakUseCount.fetch_sub(1) == 1)
|
||||
{
|
||||
if (AssetManager::IsReady())
|
||||
{
|
||||
AssetManager::Instance().ReleaseAsset(this, assetId, assetType, removeFromHash, creationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Attempting to release asset after AssetManager has been destroyed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetData::IsLoading(bool includeQueued) const
|
||||
{
|
||||
auto curStatus = GetStatus();
|
||||
return(curStatus == AssetStatus::Loading || curStatus == AssetStatus::LoadedPreReady || curStatus==AssetStatus::StreamReady ||
|
||||
(includeQueued && curStatus == AssetStatus::Queued));
|
||||
}
|
||||
|
||||
void AssetData::RegisterWithHandler(AssetHandler* handler)
|
||||
{
|
||||
if (!handler)
|
||||
{
|
||||
AZ_Error("AssetData", false, "No handler to register with");
|
||||
return;
|
||||
}
|
||||
m_registeredHandler = handler;
|
||||
}
|
||||
|
||||
void AssetData::UnregisterWithHandler()
|
||||
{
|
||||
if (m_registeredHandler)
|
||||
{
|
||||
m_registeredHandler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetData::GetFlag(const AssetDataFlags& checkFlag) const
|
||||
{
|
||||
return m_flags[aznumeric_cast<AZStd::size_t>(checkFlag)];
|
||||
}
|
||||
|
||||
void AssetData::SetFlag(const AssetDataFlags& checkFlag, bool setValue)
|
||||
{
|
||||
m_flags.set(aznumeric_cast<AZStd::size_t>(checkFlag), setValue);
|
||||
}
|
||||
|
||||
bool AssetData::GetRequeue() const
|
||||
{
|
||||
return GetFlag(AssetDataFlags::Requeue);
|
||||
}
|
||||
void AssetData::SetRequeue(bool requeue)
|
||||
{
|
||||
SetFlag(AssetDataFlags::Requeue, requeue);
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetCallbacks(const AssetReadyCB& readyCB, const AssetMovedCB& movedCB, const AssetReloadedCB& reloadedCB,
|
||||
const AssetSavedCB& savedCB, const AssetUnloadedCB& unloadedCB, const AssetErrorCB& errorCB, const AssetCanceledCB& cancelCB)
|
||||
{
|
||||
m_onAssetReadyCB = readyCB;
|
||||
m_onAssetMovedCB = movedCB;
|
||||
m_onAssetReloadedCB = reloadedCB;
|
||||
m_onAssetSavedCB = savedCB;
|
||||
m_onAssetUnloadedCB = unloadedCB;
|
||||
m_onAssetErrorCB = errorCB;
|
||||
m_onAssetCanceledCB = cancelCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::ClearCallbacks()
|
||||
{
|
||||
SetCallbacks(AssetBusCallbacks::AssetReadyCB(),
|
||||
AssetBusCallbacks::AssetMovedCB(),
|
||||
AssetBusCallbacks::AssetReloadedCB(),
|
||||
AssetBusCallbacks::AssetSavedCB(),
|
||||
AssetBusCallbacks::AssetUnloadedCB(),
|
||||
AssetBusCallbacks::AssetErrorCB(),
|
||||
AssetBusCallbacks::AssetCanceledCB());
|
||||
}
|
||||
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetReadyCallback(const AssetReadyCB& readyCB)
|
||||
{
|
||||
m_onAssetReadyCB = readyCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetMovedCallback(const AssetMovedCB& movedCB)
|
||||
{
|
||||
m_onAssetMovedCB = movedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetReloadedCallback(const AssetReloadedCB& reloadedCB)
|
||||
{
|
||||
m_onAssetReloadedCB = reloadedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetSavedCallback(const AssetSavedCB& savedCB)
|
||||
{
|
||||
m_onAssetSavedCB = savedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetUnloadedCallback(const AssetUnloadedCB& unloadedCB)
|
||||
{
|
||||
m_onAssetUnloadedCB = unloadedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetErrorCallback(const AssetErrorCB& errorCB)
|
||||
{
|
||||
m_onAssetErrorCB = errorCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetCanceledCallback(const AssetCanceledCB& cancelCB)
|
||||
{
|
||||
m_onAssetCanceledCB = cancelCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetReady(Asset<AssetData> asset)
|
||||
{
|
||||
if (m_onAssetReadyCB)
|
||||
{
|
||||
m_onAssetReadyCB(asset, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetMoved(Asset<AssetData> asset, void* oldDataPointer)
|
||||
{
|
||||
if (m_onAssetMovedCB)
|
||||
{
|
||||
m_onAssetMovedCB(asset, oldDataPointer, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetReloaded(Asset<AssetData> asset)
|
||||
{
|
||||
if (m_onAssetReloadedCB)
|
||||
{
|
||||
m_onAssetReloadedCB(asset, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetSaved(Asset<AssetData> asset, bool isSuccessful)
|
||||
{
|
||||
if (m_onAssetSavedCB)
|
||||
{
|
||||
m_onAssetSavedCB(asset, isSuccessful, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetUnloaded(const AssetId assetId, const AssetType assetType)
|
||||
{
|
||||
if (m_onAssetUnloadedCB)
|
||||
{
|
||||
m_onAssetUnloadedCB(assetId, assetType, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetError(Asset<AssetData> asset)
|
||||
{
|
||||
if (m_onAssetErrorCB)
|
||||
{
|
||||
m_onAssetErrorCB(asset, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetCanceled(const AssetId assetId)
|
||||
{
|
||||
if (m_onAssetCanceledCB)
|
||||
{
|
||||
m_onAssetCanceledCB(assetId, *this);
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ bool AssetFilterNoAssetLoading([[maybe_unused]] const AssetFilterInfo& filterInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
namespace ProductDependencyInfo
|
||||
{
|
||||
AZ::Data::AssetLoadBehavior LoadBehaviorFromFlags(const ProductDependencyFlags& dependencyFlags)
|
||||
{
|
||||
AZ::u8 loadBehaviorValue = 0;
|
||||
for (AZ::u8 thisFlag = aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorLow);
|
||||
thisFlag <= aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorHigh); ++thisFlag)
|
||||
{
|
||||
if (dependencyFlags[thisFlag])
|
||||
{
|
||||
loadBehaviorValue |= (1 << thisFlag);
|
||||
}
|
||||
}
|
||||
return static_cast<AZ::Data::AssetLoadBehavior>(loadBehaviorValue);
|
||||
}
|
||||
|
||||
ProductDependencyFlags CreateFlags(AZ::Data::AssetLoadBehavior autoLoadBehavior)
|
||||
{
|
||||
AZ::Data::ProductDependencyInfo::ProductDependencyFlags returnFlags;
|
||||
AZ::u8 loadBehavior = aznumeric_caster(autoLoadBehavior);
|
||||
for (AZ::u8 thisFlag = aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorLow);
|
||||
thisFlag <= aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorHigh); ++thisFlag)
|
||||
{
|
||||
if (loadBehavior & (1 << thisFlag))
|
||||
{
|
||||
returnFlags[thisFlag] = true;
|
||||
}
|
||||
}
|
||||
return returnFlags;
|
||||
}
|
||||
}
|
||||
} // namespace AZ::Data
|
||||
|
||||
@@ -556,16 +556,24 @@ namespace AZ
|
||||
Asset<AssetData> assetData(AssetInternal::GetAssetData(actualId, AZ::Data::AssetLoadBehavior::Default));
|
||||
if (assetData)
|
||||
{
|
||||
auto curStatus = assetData->GetStatus();
|
||||
auto isReady = assetData->GetStatus() == AssetData::AssetStatus::Ready;
|
||||
bool isError = assetData->IsError();
|
||||
connectLock.unlock();
|
||||
if (curStatus == AssetData::AssetStatus::Ready)
|
||||
|
||||
if (isReady || isError)
|
||||
{
|
||||
handler->OnAssetReady(assetData);
|
||||
}
|
||||
else if (isError)
|
||||
{
|
||||
handler->OnAssetError(assetData);
|
||||
connectLock.unlock();
|
||||
|
||||
if (isReady)
|
||||
{
|
||||
handler->OnAssetReady(assetData);
|
||||
}
|
||||
else if (isError)
|
||||
{
|
||||
handler->OnAssetError(assetData);
|
||||
}
|
||||
|
||||
// Lock the mutex again since some destructors will be modifying the context afterwards
|
||||
connectLock.lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,8 +24,8 @@ namespace AZ
|
||||
|
||||
// AssetContainer loads an asset and all of its dependencies as a collection which is parallellized as much as possible.
|
||||
// With the container, the data will all load in parallel. Dependent asset loads will still obey the expected rules
|
||||
// where PreLoad assets will emit OnAssetReady before the parent does, and QueueLoad assets will emit OnAssetReady in
|
||||
// no guaranteed order. However, the OnAssetContainerReady signals will not emit until all PreLoad and QueueLoad assets
|
||||
// where PreLoad assets will emit OnAssetReady before the parent does, and QueueLoad assets will emit OnAssetReady in
|
||||
// no guaranteed order. However, the OnAssetContainerReady signals will not emit until all PreLoad and QueueLoad assets
|
||||
// are ready. NoLoad dependencies are not loaded by default but can be loaded along with their dependencies using the
|
||||
// same rules as above by using the LoadAll dependency rule.
|
||||
class AssetContainer :
|
||||
@@ -36,7 +36,7 @@ namespace AZ
|
||||
AZ_CLASS_ALLOCATOR(AssetContainer, SystemAllocator, 0);
|
||||
|
||||
AssetContainer() = default;
|
||||
|
||||
|
||||
AssetContainer(Asset<AssetData> asset, const AssetLoadParameters& loadParams);
|
||||
~AssetContainer();
|
||||
|
||||
@@ -81,6 +81,10 @@ namespace AZ
|
||||
// AssetLoadBus
|
||||
void OnAssetDataLoaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
|
||||
protected:
|
||||
|
||||
virtual AZStd::vector<AZStd::pair<AssetInfo, Asset<AssetData>>> CreateAndQueueDependentAssets(
|
||||
const AZStd::vector<AssetInfo>& dependencyInfoList, const AssetLoadParameters& loadParamsCopyWithNoLoadingFilter);
|
||||
|
||||
// Waiting assets are those which have not yet signalled ready. In the case of PreLoad dependencies the data may have completed the load cycle but
|
||||
// the Assets aren't considered "Ready" yet if there are PreLoad dependencies still loading and will still be in the list until the point that asset and
|
||||
// All of its preload dependencies have been loaded, when it signals OnAssetReady
|
||||
@@ -97,7 +101,7 @@ namespace AZ
|
||||
void AddDependency(Asset<AssetData>&& addDependency);
|
||||
|
||||
// Add a "graph section" to our list of dependencies. This checks the catalog for all Pre and Queue load assets which are dependents of the requested asset and kicks off loads
|
||||
// NoLoads which are encounted are placed in another list and can be loaded on demand with the LoadDependency call.
|
||||
// NoLoads which are encounted are placed in another list and can be loaded on demand with the LoadDependency call.
|
||||
void AddDependentAssets(Asset<AssetData> rootAsset, const AssetLoadParameters& loadParams);
|
||||
|
||||
// If "PreLoad" assets are found in the graph these are cached and tracked with both OnAssetReady and OnAssetDataLoaded messages.
|
||||
@@ -117,7 +121,7 @@ namespace AZ
|
||||
// duringInit if we're coming from the checkReady method - containers that start ready don't need to signal
|
||||
void HandleReadyAsset(AZ::Data::Asset<AZ::Data::AssetData> asset);
|
||||
|
||||
// Optimization to save the lookup in the dependencies map
|
||||
// Optimization to save the lookup in the dependencies map
|
||||
AssetInternal::WeakAsset<AssetData> m_rootAsset;
|
||||
|
||||
// The root asset id is stored here semi-redundantly on initialization so that we can still refer to it even if the
|
||||
@@ -136,7 +140,7 @@ namespace AZ
|
||||
AZStd::atomic_bool m_finalNotificationSent{false};
|
||||
|
||||
mutable AZStd::recursive_mutex m_preloadMutex;
|
||||
// AssetId -> List of assets it is still waiting on
|
||||
// AssetId -> List of assets it is still waiting on
|
||||
PreloadAssetListType m_preloadList;
|
||||
|
||||
// AssetId -> List of assets waiting on it
|
||||
|
||||
@@ -12,207 +12,204 @@
|
||||
#include <AzCore/Serialization/Json/StackedString.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Data
|
||||
{
|
||||
namespace Data
|
||||
AZ_CLASS_ALLOCATOR_IMPL(AssetJsonSerializer, SystemAllocator, 0);
|
||||
|
||||
JsonSerializationResult::Result AssetJsonSerializer::Load(void* outputValue, const Uuid& /*outputValueTypeId*/,
|
||||
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(AssetJsonSerializer, SystemAllocator, 0);
|
||||
namespace JSR = JsonSerializationResult;
|
||||
|
||||
JsonSerializationResult::Result AssetJsonSerializer::Load(void* outputValue, const Uuid& /*outputValueTypeId*/,
|
||||
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
|
||||
switch (inputValue.GetType())
|
||||
{
|
||||
namespace JSR = JsonSerializationResult;
|
||||
case rapidjson::kObjectType:
|
||||
return LoadAsset(outputValue, inputValue, context);
|
||||
case rapidjson::kArrayType: // fall through
|
||||
case rapidjson::kNullType: // fall through
|
||||
case rapidjson::kStringType: // fall through
|
||||
case rapidjson::kFalseType: // fall through
|
||||
case rapidjson::kTrueType: // fall through
|
||||
case rapidjson::kNumberType:
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
|
||||
"Unsupported type. Asset<T> can only be read from an object.");
|
||||
|
||||
switch (inputValue.GetType())
|
||||
default:
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown, "Unknown json type encountered for Asset<T>.");
|
||||
}
|
||||
}
|
||||
|
||||
JsonSerializationResult::Result AssetJsonSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
|
||||
const void* defaultValue, const Uuid& /*valueTypeId*/, JsonSerializerContext& context)
|
||||
{
|
||||
namespace JSR = JsonSerializationResult;
|
||||
|
||||
const Asset<AssetData>* instance = reinterpret_cast<const Asset<AssetData>*>(inputValue);
|
||||
const Asset<AssetData>* defaultInstance = reinterpret_cast<const Asset<AssetData>*>(defaultValue);
|
||||
|
||||
JSR::ResultCode result(JSR::Tasks::WriteValue);
|
||||
{
|
||||
ScopedContextPath subPathId(context, "m_assetId");
|
||||
const auto* id = &instance->GetId();
|
||||
const auto* defaultId = defaultInstance ? &defaultInstance->GetId() : nullptr;
|
||||
rapidjson::Value assetIdValue;
|
||||
result = ContinueStoring(assetIdValue, id, defaultId, azrtti_typeid<AssetId>(), context);
|
||||
if (result.GetOutcome() == JSR::Outcomes::Success || result.GetOutcome() == JSR::Outcomes::PartialDefaults)
|
||||
{
|
||||
case rapidjson::kObjectType:
|
||||
return LoadAsset(outputValue, inputValue, context);
|
||||
case rapidjson::kArrayType: // fall through
|
||||
case rapidjson::kNullType: // fall through
|
||||
case rapidjson::kStringType: // fall through
|
||||
case rapidjson::kFalseType: // fall through
|
||||
case rapidjson::kTrueType: // fall through
|
||||
case rapidjson::kNumberType:
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
|
||||
"Unsupported type. Asset<T> can only be read from an object.");
|
||||
|
||||
default:
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown, "Unknown json type encountered for Asset<T>.");
|
||||
if (!outputValue.IsObject())
|
||||
{
|
||||
outputValue.SetObject();
|
||||
}
|
||||
outputValue.AddMember(rapidjson::StringRef("assetId"), AZStd::move(assetIdValue), context.GetJsonAllocator());
|
||||
}
|
||||
}
|
||||
|
||||
JsonSerializationResult::Result AssetJsonSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
|
||||
const void* defaultValue, const Uuid& /*valueTypeId*/, JsonSerializerContext& context)
|
||||
{
|
||||
namespace JSR = JsonSerializationResult;
|
||||
const AZ::Data::AssetLoadBehavior autoLoadBehavior = instance->GetAutoLoadBehavior();
|
||||
const AZ::Data::AssetLoadBehavior defaultAutoLoadBehavior = defaultInstance ?
|
||||
defaultInstance->GetAutoLoadBehavior() : AZ::Data::AssetLoadBehavior::Default;
|
||||
|
||||
const Asset<AssetData>* instance = reinterpret_cast<const Asset<AssetData>*>(inputValue);
|
||||
const Asset<AssetData>* defaultInstance = reinterpret_cast<const Asset<AssetData>*>(defaultValue);
|
||||
|
||||
JSR::ResultCode result(JSR::Tasks::WriteValue);
|
||||
{
|
||||
ScopedContextPath subPathId(context, "m_assetId");
|
||||
const auto* id = &instance->GetId();
|
||||
const auto* defaultId = defaultInstance ? &defaultInstance->GetId() : nullptr;
|
||||
rapidjson::Value assetIdValue;
|
||||
result = ContinueStoring(assetIdValue, id, defaultId, azrtti_typeid<AssetId>(), context);
|
||||
if (result.GetOutcome() == JSR::Outcomes::Success || result.GetOutcome() == JSR::Outcomes::PartialDefaults)
|
||||
{
|
||||
if (!outputValue.IsObject())
|
||||
{
|
||||
outputValue.SetObject();
|
||||
}
|
||||
outputValue.AddMember(rapidjson::StringRef("assetId"), AZStd::move(assetIdValue), context.GetJsonAllocator());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const AZ::Data::AssetLoadBehavior autoLoadBehavior = instance->GetAutoLoadBehavior();
|
||||
const AZ::Data::AssetLoadBehavior defaultAutoLoadBehavior = defaultInstance ?
|
||||
defaultInstance->GetAutoLoadBehavior() : AZ::Data::AssetLoadBehavior::Default;
|
||||
|
||||
result.Combine(
|
||||
ContinueStoringToJsonObjectField(outputValue, "loadBehavior",
|
||||
&autoLoadBehavior, &defaultAutoLoadBehavior,
|
||||
azrtti_typeid<Data::AssetLoadBehavior>(), context));
|
||||
}
|
||||
|
||||
{
|
||||
ScopedContextPath subPathHint(context, "m_assetHint");
|
||||
const AZStd::string* hint = &instance->GetHint();
|
||||
const AZStd::string defaultHint;
|
||||
rapidjson::Value assetHintValue;
|
||||
JSR::ResultCode resultHint = ContinueStoring(assetHintValue, hint, &defaultHint, azrtti_typeid<AZStd::string>(), context);
|
||||
if (resultHint.GetOutcome() == JSR::Outcomes::Success || resultHint.GetOutcome() == JSR::Outcomes::PartialDefaults)
|
||||
{
|
||||
if (!outputValue.IsObject())
|
||||
{
|
||||
outputValue.SetObject();
|
||||
}
|
||||
outputValue.AddMember(rapidjson::StringRef("assetHint"), AZStd::move(assetHintValue), context.GetJsonAllocator());
|
||||
}
|
||||
result.Combine(resultHint);
|
||||
}
|
||||
|
||||
return context.Report(result,
|
||||
result.GetProcessing() == JSR::Processing::Completed ? "Successfully stored Asset<T>." : "Failed to store Asset<T>.");
|
||||
result.Combine(
|
||||
ContinueStoringToJsonObjectField(outputValue, "loadBehavior",
|
||||
&autoLoadBehavior, &defaultAutoLoadBehavior,
|
||||
azrtti_typeid<Data::AssetLoadBehavior>(), context));
|
||||
}
|
||||
|
||||
JsonSerializationResult::Result AssetJsonSerializer::LoadAsset(void* outputValue, const rapidjson::Value& inputValue,
|
||||
JsonDeserializerContext& context)
|
||||
{
|
||||
namespace JSR = JsonSerializationResult;
|
||||
|
||||
Asset<AssetData>* instance = reinterpret_cast<Asset<AssetData>*>(outputValue);
|
||||
AssetId id;
|
||||
JSR::ResultCode result(JSR::Tasks::ReadField);
|
||||
|
||||
SerializedAssetTracker* assetTracker =
|
||||
context.GetMetadata().Find<SerializedAssetTracker>();
|
||||
|
||||
ScopedContextPath subPathHint(context, "m_assetHint");
|
||||
const AZStd::string* hint = &instance->GetHint();
|
||||
const AZStd::string defaultHint;
|
||||
rapidjson::Value assetHintValue;
|
||||
JSR::ResultCode resultHint = ContinueStoring(assetHintValue, hint, &defaultHint, azrtti_typeid<AZStd::string>(), context);
|
||||
if (resultHint.GetOutcome() == JSR::Outcomes::Success || resultHint.GetOutcome() == JSR::Outcomes::PartialDefaults)
|
||||
{
|
||||
Data::AssetLoadBehavior loadBehavior = instance->GetAutoLoadBehavior();
|
||||
|
||||
result =
|
||||
ContinueLoadingFromJsonObjectField(&loadBehavior,
|
||||
azrtti_typeid<Data::AssetLoadBehavior>(),
|
||||
inputValue, "loadBehavior", context);
|
||||
|
||||
instance->SetAutoLoadBehavior(loadBehavior);
|
||||
if (!outputValue.IsObject())
|
||||
{
|
||||
outputValue.SetObject();
|
||||
}
|
||||
outputValue.AddMember(rapidjson::StringRef("assetHint"), AZStd::move(assetHintValue), context.GetJsonAllocator());
|
||||
}
|
||||
result.Combine(resultHint);
|
||||
}
|
||||
|
||||
auto it = inputValue.FindMember("assetId");
|
||||
if (it != inputValue.MemberEnd())
|
||||
return context.Report(result,
|
||||
result.GetProcessing() == JSR::Processing::Completed ? "Successfully stored Asset<T>." : "Failed to store Asset<T>.");
|
||||
}
|
||||
|
||||
JsonSerializationResult::Result AssetJsonSerializer::LoadAsset(void* outputValue, const rapidjson::Value& inputValue,
|
||||
JsonDeserializerContext& context)
|
||||
{
|
||||
namespace JSR = JsonSerializationResult;
|
||||
|
||||
Asset<AssetData>* instance = reinterpret_cast<Asset<AssetData>*>(outputValue);
|
||||
AssetId id;
|
||||
JSR::ResultCode result(JSR::Tasks::ReadField);
|
||||
|
||||
SerializedAssetTracker* assetTracker =
|
||||
context.GetMetadata().Find<SerializedAssetTracker>();
|
||||
|
||||
{
|
||||
Data::AssetLoadBehavior loadBehavior = instance->GetAutoLoadBehavior();
|
||||
|
||||
result =
|
||||
ContinueLoadingFromJsonObjectField(&loadBehavior,
|
||||
azrtti_typeid<Data::AssetLoadBehavior>(),
|
||||
inputValue, "loadBehavior", context);
|
||||
|
||||
instance->SetAutoLoadBehavior(loadBehavior);
|
||||
}
|
||||
|
||||
auto it = inputValue.FindMember("assetId");
|
||||
if (it != inputValue.MemberEnd())
|
||||
{
|
||||
ScopedContextPath subPath(context, "assetId");
|
||||
result.Combine(ContinueLoading(&id, azrtti_typeid<AssetId>(), it->value, context));
|
||||
if (!id.m_guid.IsNull())
|
||||
{
|
||||
ScopedContextPath subPath(context, "assetId");
|
||||
result.Combine(ContinueLoading(&id, azrtti_typeid<AssetId>(), it->value, context));
|
||||
if (!id.m_guid.IsNull())
|
||||
*instance = AssetManager::Instance().FindOrCreateAsset(id, instance->GetType(), instance->GetAutoLoadBehavior());
|
||||
if (!instance->GetId().IsValid())
|
||||
{
|
||||
*instance = AssetManager::Instance().FindOrCreateAsset(id, instance->GetType(), instance->GetAutoLoadBehavior());
|
||||
if (!instance->GetId().IsValid())
|
||||
{
|
||||
// If the asset failed to be created, FindOrCreateAsset returns an asset instance with a null
|
||||
// id. To preserve the asset id in the source json, reset the asset to an empty one, but with
|
||||
// the right id.
|
||||
const auto loadBehavior = instance->GetAutoLoadBehavior();
|
||||
*instance = Asset<AssetData>(id, instance->GetType());
|
||||
instance->SetAutoLoadBehavior(loadBehavior);
|
||||
}
|
||||
// If the asset failed to be created, FindOrCreateAsset returns an asset instance with a null
|
||||
// id. To preserve the asset id in the source json, reset the asset to an empty one, but with
|
||||
// the right id.
|
||||
const auto loadBehavior = instance->GetAutoLoadBehavior();
|
||||
*instance = Asset<AssetData>(id, instance->GetType());
|
||||
instance->SetAutoLoadBehavior(loadBehavior);
|
||||
}
|
||||
|
||||
result.Combine(context.Report(result, "Successfully created Asset<T> with id."));
|
||||
}
|
||||
else if (result.GetProcessing() == JSR::Processing::Completed)
|
||||
{
|
||||
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
|
||||
"Null Asset<T> created."));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Combine(context.Report(result, "Failed to retrieve asset id for Asset<T>."));
|
||||
}
|
||||
result.Combine(context.Report(result, "Successfully created Asset<T> with id."));
|
||||
}
|
||||
else if (result.GetProcessing() == JSR::Processing::Completed)
|
||||
{
|
||||
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
|
||||
"Null Asset<T> created."));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
|
||||
"The asset id is missing, so there's not enough information to create an Asset<T>."));
|
||||
}
|
||||
|
||||
it = inputValue.FindMember("assetHint");
|
||||
if (it != inputValue.MemberEnd())
|
||||
{
|
||||
ScopedContextPath subPath(context, "assetHint");
|
||||
AZStd::string hint;
|
||||
result.Combine(ContinueLoading(&hint, azrtti_typeid<AZStd::string>(), it->value, context));
|
||||
instance->SetHint(AZStd::move(hint));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
|
||||
"The asset hint is missing for Asset<T>, so it will be left empty."));
|
||||
}
|
||||
|
||||
if (assetTracker)
|
||||
{
|
||||
assetTracker->FixUpAsset(*instance);
|
||||
assetTracker->AddAsset(*instance);
|
||||
}
|
||||
|
||||
bool success = result.GetOutcome() <= JSR::Outcomes::PartialSkip;
|
||||
bool defaulted = result.GetOutcome() == JSR::Outcomes::DefaultsUsed || result.GetOutcome() == JSR::Outcomes::PartialDefaults;
|
||||
AZStd::string_view message =
|
||||
success ? "Successfully loaded information and created instance of Asset<T>." :
|
||||
defaulted ? "A default id was provided for Asset<T>, so no instance could be created." :
|
||||
"Not enough information was available to create an instance of Asset<T> or data was corrupted.";
|
||||
return context.Report(result, message);
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::SetAssetFixUp(AssetFixUp assetFixUpCallback)
|
||||
{
|
||||
m_assetFixUpCallback = AZStd::move(assetFixUpCallback);
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::FixUpAsset(Asset<AssetData>& asset)
|
||||
{
|
||||
if (m_assetFixUpCallback)
|
||||
{
|
||||
m_assetFixUpCallback(asset);
|
||||
result.Combine(context.Report(result, "Failed to retrieve asset id for Asset<T>."));
|
||||
}
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::AddAsset(Asset<AssetData> asset)
|
||||
else
|
||||
{
|
||||
m_serializedAssets.emplace_back(asset);
|
||||
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
|
||||
"The asset id is missing, so there's not enough information to create an Asset<T>."));
|
||||
}
|
||||
|
||||
const AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets() const
|
||||
it = inputValue.FindMember("assetHint");
|
||||
if (it != inputValue.MemberEnd())
|
||||
{
|
||||
return m_serializedAssets;
|
||||
ScopedContextPath subPath(context, "assetHint");
|
||||
AZStd::string hint;
|
||||
result.Combine(ContinueLoading(&hint, azrtti_typeid<AZStd::string>(), it->value, context));
|
||||
instance->SetHint(AZStd::move(hint));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
|
||||
"The asset hint is missing for Asset<T>, so it will be left empty."));
|
||||
}
|
||||
|
||||
AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets()
|
||||
if (assetTracker)
|
||||
{
|
||||
return m_serializedAssets;
|
||||
assetTracker->FixUpAsset(*instance);
|
||||
assetTracker->AddAsset(*instance);
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
} // namespace AZ
|
||||
bool success = result.GetOutcome() <= JSR::Outcomes::PartialSkip;
|
||||
bool defaulted = result.GetOutcome() == JSR::Outcomes::DefaultsUsed || result.GetOutcome() == JSR::Outcomes::PartialDefaults;
|
||||
AZStd::string_view message =
|
||||
success ? "Successfully loaded information and created instance of Asset<T>." :
|
||||
defaulted ? "A default id was provided for Asset<T>, so no instance could be created." :
|
||||
"Not enough information was available to create an instance of Asset<T> or data was corrupted.";
|
||||
return context.Report(result, message);
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::SetAssetFixUp(AssetFixUp assetFixUpCallback)
|
||||
{
|
||||
m_assetFixUpCallback = AZStd::move(assetFixUpCallback);
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::FixUpAsset(Asset<AssetData>& asset)
|
||||
{
|
||||
if (m_assetFixUpCallback)
|
||||
{
|
||||
m_assetFixUpCallback(asset);
|
||||
}
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::AddAsset(Asset<AssetData> asset)
|
||||
{
|
||||
m_serializedAssets.emplace_back(asset);
|
||||
}
|
||||
|
||||
const AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets() const
|
||||
{
|
||||
return m_serializedAssets;
|
||||
}
|
||||
|
||||
AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets()
|
||||
{
|
||||
return m_serializedAssets;
|
||||
}
|
||||
|
||||
} // namespace AZ::Data
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -169,14 +169,14 @@ namespace AZ
|
||||
/// Register handler with the system for a particular asset type.
|
||||
/// A handler should be registered for each asset type it handles.
|
||||
/// Please note that all the handlers are registered just once during app startup from the main thread
|
||||
/// and therefore this is not a thread safe method and should not be invoked from different threads.
|
||||
/// and therefore this is not a thread safe method and should not be invoked from different threads.
|
||||
void RegisterHandler(AssetHandler* handler, const AssetType& assetType);
|
||||
/// Unregister handler from the asset system.
|
||||
/// Please note that all the handlers are unregistered just once during app shutdown from the main thread
|
||||
/// and therefore this is not a thread safe method and should not be invoked from different threads.
|
||||
void UnregisterHandler(AssetHandler* handler);
|
||||
// @}
|
||||
|
||||
|
||||
// @{ Asset catalog management
|
||||
/// Register a catalog with the system for a particular asset type.
|
||||
/// A catalog should be registered for each asset type it is responsible for.
|
||||
@@ -295,7 +295,7 @@ namespace AZ
|
||||
/**
|
||||
* Old 'legacy' assetIds and asset hints can be automatically replaced with new ones during deserialize / assignment.
|
||||
* This operation can be somewhat costly, and its only useful if the program subsequently re-saves the files its loading so that
|
||||
* the asset hints and assetIds actually persist. Thus, it can be disabled in situations where you know you are not going to be
|
||||
* the asset hints and assetIds actually persist. Thus, it can be disabled in situations where you know you are not going to be
|
||||
* saving over or creating new source files (for example builders/background apps)
|
||||
* By default, it is enabled.
|
||||
*/
|
||||
@@ -316,7 +316,7 @@ namespace AZ
|
||||
* This method must be invoked before you start unregistering handlers manually and shutting down the asset manager.
|
||||
* This method ensures that all jobs in flight are either canceled or completed.
|
||||
* This method is automatically called in the destructor but if you are unregistering handlers manually,
|
||||
* you must invoke it yourself.
|
||||
* you must invoke it yourself.
|
||||
*/
|
||||
void PrepareShutDown();
|
||||
|
||||
@@ -366,7 +366,7 @@ namespace AZ
|
||||
/**
|
||||
* Creates a new shared AssetContainer with an optional loadFilter
|
||||
* **/
|
||||
AZStd::shared_ptr<AssetContainer> CreateAssetContainer(Asset<AssetData> asset, const AssetLoadParameters& loadParams = AssetLoadParameters{}) const;
|
||||
virtual AZStd::shared_ptr<AssetContainer> CreateAssetContainer(Asset<AssetData> asset, const AssetLoadParameters& loadParams = AssetLoadParameters{}) const;
|
||||
|
||||
|
||||
/**
|
||||
@@ -452,7 +452,7 @@ namespace AZ
|
||||
|
||||
|
||||
// Variant of RegisterAssetLoading used for jobs which have been queued and need to verify the status of the asset
|
||||
// before loading in order to prevent cases where a load is queued, then a blocking load goes through, then the queued
|
||||
// before loading in order to prevent cases where a load is queued, then a blocking load goes through, then the queued
|
||||
// load is processed. This validation step leaves the loaded (And potentially modified) data as is in that case.
|
||||
bool ValidateAndRegisterAssetLoading(const Asset<AssetData>& asset);
|
||||
|
||||
@@ -482,7 +482,7 @@ namespace AZ
|
||||
* the blocking. That will result in a single thread deadlock.
|
||||
*
|
||||
* If you need to queue work, the logic needs to be similar to this:
|
||||
*
|
||||
*
|
||||
AssetHandler::LoadResult MyAssetHandler::LoadAssetData(const Asset<AssetData>& asset, AZStd::shared_ptr<AssetDataStream> stream,
|
||||
const AZ::Data::AssetFilterCB& assetLoadFilterCB)
|
||||
{
|
||||
@@ -496,13 +496,13 @@ namespace AZ
|
||||
}
|
||||
else
|
||||
{
|
||||
// queue job to load asset in thread identified by m_loadingThreadId
|
||||
// queue job to load asset in thread identified by m_loadingThreadId
|
||||
auto* queuedJob = QueueLoadingOnOtherThread(...);
|
||||
|
||||
// block waiting for queued job to complete
|
||||
queuedJob->BlockUntilComplete();
|
||||
}
|
||||
|
||||
|
||||
.
|
||||
.
|
||||
.
|
||||
@@ -525,7 +525,7 @@ namespace AZ
|
||||
//! Result from LoadAssetData - it either finished loading, didn't finish and is waiting for more data, or had an error.
|
||||
enum class LoadResult : u8
|
||||
{
|
||||
|
||||
|
||||
Error, // The provided data failed to load correctly
|
||||
MoreDataRequired, // The provided data loaded correctly, but more data is required to finish the asset load
|
||||
LoadComplete // The provided data loaded correctly, and the asset has been created
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/IO/Path/Path_fwd.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/containers/bitset.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
@@ -129,7 +130,8 @@ namespace AZ
|
||||
/// Remove a catalog from our delta list and rebuild the catalog from remaining items
|
||||
virtual bool RemoveDeltaCatalog(AZStd::shared_ptr<AzFramework::AssetRegistry> /*deltaCatalog*/) { return true; }
|
||||
/// Creates a manifest with the given DeltaCatalog name
|
||||
virtual bool CreateBundleManifest(const AZStd::string& /*deltaCatalogPath*/, const AZStd::vector<AZStd::string>& /*dependentBundleNames*/, const AZStd::string& /*fileDirectory*/, int /*bundleVersion*/, const AZStd::vector<AZStd::string>& /*levelDirs*/) { return false; }
|
||||
virtual bool CreateBundleManifest(const AZStd::string& /*deltaCatalogPath*/, const AZStd::vector<AZStd::string>& /*dependentBundleNames*/,
|
||||
const AZStd::string& /*fileDirectory*/, int /*bundleVersion*/, const AZStd::vector<AZ::IO::Path>& /*levelDirs*/) { return false; }
|
||||
/// Creates an instance of a registry containing info for just the specified files, and writes it out to a file at the specified path
|
||||
virtual bool CreateDeltaCatalog(const AZStd::vector<AZStd::string>& /*files*/, const AZStd::string& /*filePath*/) { return false; }
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include <AzCore/Slice/SliceSystemComponent.h>
|
||||
#include <AzCore/Slice/SliceMetadataInfoComponent.h>
|
||||
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
||||
#include <AzCore/Time/TimeSystemComponent.h>
|
||||
#include <AzCore/Console/LoggerSystemComponent.h>
|
||||
#include <AzCore/EBus/EventSchedulerSystemComponent.h>
|
||||
#include <AzCore/Task/TaskGraphSystemComponent.h>
|
||||
@@ -40,7 +39,6 @@ namespace AZ
|
||||
SliceComponent::CreateDescriptor(),
|
||||
SliceSystemComponent::CreateDescriptor(),
|
||||
SliceMetadataInfoComponent::CreateDescriptor(),
|
||||
TimeSystemComponent::CreateDescriptor(),
|
||||
LoggerSystemComponent::CreateDescriptor(),
|
||||
EventSchedulerSystemComponent::CreateDescriptor(),
|
||||
TaskGraphSystemComponent::CreateDescriptor(),
|
||||
@@ -59,7 +57,6 @@ namespace AZ
|
||||
{
|
||||
return AZ::ComponentTypeList
|
||||
{
|
||||
azrtti_typeid<TimeSystemComponent>(),
|
||||
azrtti_typeid<LoggerSystemComponent>(),
|
||||
azrtti_typeid<EventSchedulerSystemComponent>(),
|
||||
azrtti_typeid<TaskGraphSystemComponent>(),
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
|
||||
#include <AzCore/Module/Environment.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzCore/Time/TimeSystem.h>
|
||||
|
||||
static void PrintEntityName(const AZ::ConsoleCommandContainer& arguments)
|
||||
{
|
||||
@@ -416,6 +417,7 @@ namespace AZ
|
||||
|
||||
ComponentApplication::ComponentApplication(int argC, char** argV)
|
||||
: m_eventLogger{}
|
||||
, m_timeSystem(AZStd::make_unique<TimeSystem>())
|
||||
{
|
||||
if (Interface<ComponentApplicationRequests>::Get() == nullptr)
|
||||
{
|
||||
@@ -485,16 +487,6 @@ namespace AZ
|
||||
constexpr bool executeRegDumpCommands = false;
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
|
||||
|
||||
// Query for the Executable Path using OS specific functions
|
||||
CalculateExecutablePath();
|
||||
|
||||
// Determine the path to the engine
|
||||
CalculateEngineRoot();
|
||||
|
||||
// If the current platform returns an engaged optional from Utils::GetDefaultAppRootPath(), that is used
|
||||
// for the application root.
|
||||
CalculateAppRoot();
|
||||
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(*m_settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, {});
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry);
|
||||
@@ -584,7 +576,6 @@ namespace AZ
|
||||
DestroyAllocator();
|
||||
}
|
||||
|
||||
|
||||
void ReportBadEngineRoot()
|
||||
{
|
||||
AZStd::string errorMessage = {"Unable to determine a valid path to the engine.\n"
|
||||
@@ -614,7 +605,8 @@ namespace AZ
|
||||
{
|
||||
AZ_Assert(!m_isStarted, "Component application already started!");
|
||||
|
||||
if (m_engineRoot.empty())
|
||||
using Type = AZ::SettingsRegistryInterface::Type;
|
||||
if (m_settingsRegistry->GetType(SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder) == Type::NoType)
|
||||
{
|
||||
ReportBadEngineRoot();
|
||||
return nullptr;
|
||||
@@ -686,7 +678,6 @@ namespace AZ
|
||||
|
||||
ComponentApplicationBus::Handler::BusConnect();
|
||||
|
||||
m_currentTime = AZStd::chrono::system_clock::now();
|
||||
TickRequestBus::Handler::BusConnect();
|
||||
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
@@ -1180,6 +1171,24 @@ namespace AZ
|
||||
return ReflectionEnvironment::GetReflectionManager() ? ReflectionEnvironment::GetReflectionManager()->GetReflectContext<JsonRegistrationContext>() : nullptr;
|
||||
}
|
||||
|
||||
/// Returns the path to the engine.
|
||||
|
||||
const char* ComponentApplication::GetEngineRoot() const
|
||||
{
|
||||
static IO::FixedMaxPathString engineRoot;
|
||||
engineRoot.clear();
|
||||
m_settingsRegistry->Get(engineRoot, SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
return engineRoot.c_str();
|
||||
}
|
||||
|
||||
const char* ComponentApplication::GetExecutableFolder() const
|
||||
{
|
||||
static IO::FixedMaxPathString exeFolder;
|
||||
exeFolder.clear();
|
||||
m_settingsRegistry->Get(exeFolder, SettingsRegistryMergeUtils::FilePathKey_BinaryFolder);
|
||||
return exeFolder.c_str();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// CreateReflectionManager
|
||||
//=========================================================================
|
||||
@@ -1404,31 +1413,23 @@ namespace AZ
|
||||
#endif
|
||||
}
|
||||
|
||||
void ComponentApplication::Tick(float deltaOverride /*= -1.f*/)
|
||||
void ComponentApplication::Tick()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(System, "Component application simulation tick");
|
||||
|
||||
{
|
||||
AZ_PROFILE_SCOPE(System, "Component application simulation tick");
|
||||
|
||||
AZStd::chrono::system_clock::time_point now = AZStd::chrono::system_clock::now();
|
||||
|
||||
m_deltaTime = 0.0f;
|
||||
|
||||
if (now >= m_currentTime)
|
||||
{
|
||||
AZStd::chrono::duration<float> delta = now - m_currentTime;
|
||||
m_deltaTime = deltaOverride >= 0.f ? deltaOverride : delta.count();
|
||||
}
|
||||
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:ExecuteQueuedEvents");
|
||||
TickBus::ExecuteQueuedEvents();
|
||||
}
|
||||
m_currentTime = now;
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:OnTick");
|
||||
EBUS_EVENT(TickBus, OnTick, m_deltaTime, ScriptTimePoint(now));
|
||||
}
|
||||
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:ExecuteQueuedEvents");
|
||||
TickBus::ExecuteQueuedEvents();
|
||||
}
|
||||
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:OnTick");
|
||||
const AZ::TimeUs deltaTimeUs = m_timeSystem->AdvanceTickDeltaTimes();
|
||||
const float deltaTimeSeconds = AZ::TimeUsToSeconds(deltaTimeUs);
|
||||
AZ::TickBus::Broadcast(&TickEvents::OnTick, deltaTimeSeconds, GetTimeAtCurrentTick());
|
||||
}
|
||||
|
||||
m_timeSystem->ApplyTickRateLimiterIfNeeded();
|
||||
}
|
||||
|
||||
void ComponentApplication::TickSystem()
|
||||
@@ -1485,27 +1486,6 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// CalculateExecutablePath
|
||||
//=========================================================================
|
||||
void ComponentApplication::CalculateExecutablePath()
|
||||
{
|
||||
m_exeDirectory = Utils::GetExecutableDirectory();
|
||||
}
|
||||
|
||||
void ComponentApplication::CalculateAppRoot()
|
||||
{
|
||||
if (AZStd::optional<AZ::StringFunc::Path::FixedString> appRootPath = Utils::GetDefaultAppRootPath(); appRootPath)
|
||||
{
|
||||
m_appRoot = AZStd::move(*appRootPath);
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentApplication::CalculateEngineRoot()
|
||||
{
|
||||
m_engineRoot = AZ::SettingsRegistryMergeUtils::FindEngineRoot(*m_settingsRegistry).Native();
|
||||
}
|
||||
|
||||
void ComponentApplication::ResolveModulePath([[maybe_unused]] AZ::OSString& modulePath)
|
||||
{
|
||||
// No special parsing of the Module Path is done by the Component Application anymore
|
||||
@@ -1531,13 +1511,10 @@ namespace AZ
|
||||
appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Invalid;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// GetFrameTime
|
||||
// [1/22/2016]
|
||||
//=========================================================================
|
||||
float ComponentApplication::GetTickDeltaTime()
|
||||
{
|
||||
return m_deltaTime;
|
||||
const AZ::TimeUs gameTickTime = m_timeSystem->GetSimulationTickDeltaTimeUs();
|
||||
return AZ::TimeUsToSeconds(gameTickTime);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
@@ -1546,7 +1523,8 @@ namespace AZ
|
||||
//=========================================================================
|
||||
ScriptTimePoint ComponentApplication::GetTimeAtCurrentTick()
|
||||
{
|
||||
return ScriptTimePoint(m_currentTime);
|
||||
const AZ::TimeUs lastGameTickTime = m_timeSystem->GetLastSimulationTickTime();
|
||||
return ScriptTimePoint(AZ::TimeUsToChrono(lastGameTickTime));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
|
||||
@@ -30,12 +30,14 @@
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzCore/std/string/osstring.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class BehaviorContext;
|
||||
class IConsole;
|
||||
class Module;
|
||||
class ModuleManager;
|
||||
class TimeSystem;
|
||||
}
|
||||
namespace AZ::Debug
|
||||
{
|
||||
@@ -221,13 +223,10 @@ namespace AZ
|
||||
BehaviorContext* GetBehaviorContext() override;
|
||||
/// Returns the json registration context that has been registered with the app, if there is one.
|
||||
JsonRegistrationContext* GetJsonRegistrationContext() override;
|
||||
/// Returns the working root folder that has been registered with the app, if there is one.
|
||||
/// It's expected that derived applications will implement an application root.
|
||||
const char* GetAppRoot() const override { return m_appRoot.c_str(); }
|
||||
/// Returns the path to the engine.
|
||||
const char* GetEngineRoot() const override { return m_engineRoot.c_str(); }
|
||||
const char* GetEngineRoot() const override;
|
||||
/// Returns the path to the folder the executable is in.
|
||||
const char* GetExecutableFolder() const override { return m_exeDirectory.c_str(); }
|
||||
const char* GetExecutableFolder() const override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// TickRequestBus
|
||||
@@ -240,7 +239,7 @@ namespace AZ
|
||||
/**
|
||||
* Ticks all components using the \ref AZ::TickBus during simulation time. May not tick if the application is not active (i.e. not in focus)
|
||||
*/
|
||||
virtual void Tick(float deltaOverride = -1.f);
|
||||
virtual void Tick();
|
||||
|
||||
/**
|
||||
* Ticks all using the \ref AZ::SystemTickBus at all times. Should always tick even if the application is not active.
|
||||
@@ -352,15 +351,6 @@ namespace AZ
|
||||
/// Adds system components requested by modules and the application to the system entity.
|
||||
void AddRequiredSystemComponents(AZ::Entity* systemEntity);
|
||||
|
||||
/// Calculates the directory the application executable comes from.
|
||||
void CalculateExecutablePath();
|
||||
|
||||
/// Calculates the root directory of the engine.
|
||||
void CalculateEngineRoot();
|
||||
|
||||
/// Deprecated: The term "AppRoot" has no meaning
|
||||
void CalculateAppRoot();
|
||||
|
||||
template<typename Iterator>
|
||||
static void NormalizePath(Iterator begin, Iterator end, bool doLowercase = true)
|
||||
{
|
||||
@@ -371,8 +361,6 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::chrono::system_clock::time_point m_currentTime{ AZStd::chrono::system_clock::time_point::max() };
|
||||
float m_deltaTime{ 0.0f };
|
||||
AZStd::unique_ptr<ModuleManager> m_moduleManager;
|
||||
AZStd::unique_ptr<SettingsRegistryInterface> m_settingsRegistry;
|
||||
EntityAddedEvent m_entityAddedEvent;
|
||||
@@ -388,14 +376,13 @@ namespace AZ
|
||||
void* m_fixedMemoryBlock{ nullptr }; //!< Pointer to the memory block allocator, so we can free it OnDestroy.
|
||||
IAllocatorAllocate* m_osAllocator{ nullptr };
|
||||
EntitySetType m_entities;
|
||||
AZ::IO::FixedMaxPath m_exeDirectory;
|
||||
AZ::IO::FixedMaxPath m_engineRoot;
|
||||
AZ::IO::FixedMaxPath m_appRoot;
|
||||
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_projectPathChangedHandler;
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_projectNameChangedHandler;
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_commandLineUpdatedHandler;
|
||||
|
||||
AZStd::unique_ptr<AZ::TimeSystem> m_timeSystem;
|
||||
|
||||
// ConsoleFunctorHandle is responsible for unregistering the Settings Registry Console
|
||||
// from the m_console member when it goes out of scope
|
||||
AZ::SettingsRegistryConsoleUtils::ConsoleFunctorHandle m_settingsRegistryConsoleFunctors;
|
||||
|
||||
@@ -175,10 +175,6 @@ namespace AZ
|
||||
//! the serializers used by the best-effort json serialization.
|
||||
virtual class JsonRegistrationContext* GetJsonRegistrationContext() = 0;
|
||||
|
||||
//! Gets the name of the working root folder that was registered with the app.
|
||||
//! @return a pointer to the name of the app's root folder, if a root folder was registered.
|
||||
virtual const char* GetAppRoot() const = 0;
|
||||
|
||||
//! Gets the path of the working engine folder that the app is a part of.
|
||||
//! @return a pointer to the engine path.
|
||||
virtual const char* GetEngineRoot() const = 0;
|
||||
|
||||
@@ -10,292 +10,289 @@
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/std/containers/fixed_vector.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::EntityUtils
|
||||
{
|
||||
namespace EntityUtils
|
||||
//=========================================================================
|
||||
// Reflect
|
||||
//=========================================================================
|
||||
void Reflect(ReflectContext* context)
|
||||
{
|
||||
//=========================================================================
|
||||
// Reflect
|
||||
//=========================================================================
|
||||
void Reflect(ReflectContext* context)
|
||||
if (auto serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
serializeContext->Class<SerializableEntityContainer>()->
|
||||
Version(1)->
|
||||
Field("Entities", &SerializableEntityContainer::m_entities);
|
||||
}
|
||||
}
|
||||
|
||||
struct StackDataType
|
||||
{
|
||||
const SerializeContext::ClassData* m_classData;
|
||||
const SerializeContext::ClassElement* m_elementData;
|
||||
void* m_dataPtr;
|
||||
bool m_isModifiedContainer;
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
// EnumerateEntityIds
|
||||
//=========================================================================
|
||||
void EnumerateEntityIds(const void* classPtr, const Uuid& classUuid, const EntityIdVisitor& visitor, SerializeContext* context)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
if (!context)
|
||||
{
|
||||
context = GetApplicationSerializeContext();
|
||||
if (!context)
|
||||
{
|
||||
serializeContext->Class<SerializableEntityContainer>()->
|
||||
Version(1)->
|
||||
Field("Entities", &SerializableEntityContainer::m_entities);
|
||||
AZ_Error("Serialization", false, "No serialize context provided! Failed to get component application default serialize context! ComponentApp is not started or input serialize context should not be null!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
AZStd::vector<const SerializeContext::ClassData*> parentStack;
|
||||
parentStack.reserve(30);
|
||||
auto beginCB = [ &](void* ptr, const SerializeContext::ClassData* classData, const SerializeContext::ClassElement* elementData) -> bool
|
||||
{
|
||||
(void)elementData;
|
||||
|
||||
struct StackDataType
|
||||
if (classData->m_typeId == SerializeTypeInfo<EntityId>::GetUuid())
|
||||
{
|
||||
// determine if this is entity ref or just entityId (please refer to the function documentation for more info)
|
||||
bool isEntityId = false;
|
||||
if (!parentStack.empty() && parentStack.back()->m_typeId == SerializeTypeInfo<Entity>::GetUuid())
|
||||
{
|
||||
// our parent in the entity (currently entity has only one EntityId member, but we can check the offset for future proof
|
||||
AZ_Assert(elementData && strcmp(elementData->m_name, "Id") == 0, "class Entity, should have only ONE EntityId member, the actual entity id!");
|
||||
isEntityId = true;
|
||||
}
|
||||
|
||||
EntityId* entityIdPtr = (elementData->m_flags & SerializeContext::ClassElement::FLG_POINTER) ?
|
||||
*reinterpret_cast<EntityId**>(ptr) : reinterpret_cast<EntityId*>(ptr);
|
||||
visitor(*entityIdPtr, isEntityId, elementData);
|
||||
}
|
||||
|
||||
parentStack.push_back(classData);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto endCB = [ &]() -> bool
|
||||
{
|
||||
parentStack.pop_back();
|
||||
return true;
|
||||
};
|
||||
|
||||
SerializeContext::EnumerateInstanceCallContext callContext(
|
||||
beginCB,
|
||||
endCB,
|
||||
context,
|
||||
SerializeContext::ENUM_ACCESS_FOR_READ,
|
||||
nullptr
|
||||
);
|
||||
|
||||
context->EnumerateInstanceConst(
|
||||
&callContext,
|
||||
classPtr,
|
||||
classUuid,
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// GetApplicationSerializeContext
|
||||
//=========================================================================
|
||||
SerializeContext* GetApplicationSerializeContext()
|
||||
{
|
||||
SerializeContext* context = nullptr;
|
||||
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
|
||||
return context;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FindFirstDerivedComponent
|
||||
//=========================================================================
|
||||
Component* FindFirstDerivedComponent(const Entity* entity, const Uuid& typeId)
|
||||
{
|
||||
for (AZ::Component* component : entity->GetComponents())
|
||||
{
|
||||
const SerializeContext::ClassData* m_classData;
|
||||
const SerializeContext::ClassElement* m_elementData;
|
||||
void* m_dataPtr;
|
||||
bool m_isModifiedContainer;
|
||||
if (azrtti_istypeof(typeId, component))
|
||||
{
|
||||
return component;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Component* FindFirstDerivedComponent(EntityId entityId, const Uuid& typeId)
|
||||
{
|
||||
Entity* entity{};
|
||||
ComponentApplicationBus::BroadcastResult(entity, &ComponentApplicationRequests::FindEntity, entityId);
|
||||
return entity ? FindFirstDerivedComponent(entity, typeId) : nullptr;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FindDerivedComponents
|
||||
//=========================================================================
|
||||
Entity::ComponentArrayType FindDerivedComponents(const Entity* entity, const Uuid& typeId)
|
||||
{
|
||||
Entity::ComponentArrayType result;
|
||||
for (AZ::Component* component : entity->GetComponents())
|
||||
{
|
||||
if (azrtti_istypeof(typeId, component))
|
||||
{
|
||||
result.push_back(component);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Entity::ComponentArrayType FindDerivedComponents(EntityId entityId, const Uuid& typeId)
|
||||
{
|
||||
Entity* entity{};
|
||||
ComponentApplicationBus::BroadcastResult(entity, &ComponentApplicationRequests::FindEntity, entityId);
|
||||
return entity ? FindDerivedComponents(entity, typeId) : Entity::ComponentArrayType();
|
||||
}
|
||||
|
||||
bool EnumerateBaseRecursive(SerializeContext* context, const EnumerateBaseRecursiveVisitor& baseClassVisitor, const TypeId& typeToExamine)
|
||||
{
|
||||
AZ_Assert(context, "CheckDeclaresSerializeBaseClass called with no serialize context.");
|
||||
if (!context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZStd::fixed_vector<TypeId, 64> knownBaseClasses = { typeToExamine }; // avoid allocating heap here if possible. 64 types are 64*sizeof(Uuid) which is only 1k.
|
||||
bool foundBaseClass = false;
|
||||
auto enumerateBaseVisitor = [&baseClassVisitor, &knownBaseClasses](const AZ::SerializeContext::ClassData* classData, const TypeId& examineTypeId)
|
||||
{
|
||||
if (!classData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AZStd::find(knownBaseClasses.begin(), knownBaseClasses.end(), classData->m_typeId) == knownBaseClasses.end())
|
||||
{
|
||||
if (knownBaseClasses.size() == 64)
|
||||
{
|
||||
// this should be pretty unlikely since a single class would have to have many other classes in its heirarchy
|
||||
// and it'd all have to be basically in one layer, as we are popping as we explore.
|
||||
AZ_WarningOnce("EntityUtils", false, "While trying to find a base class, all available slots were consumed. consider increasing the size of knownBaseClasses.\n");
|
||||
// we cannot continue any further, assume we did not find it.
|
||||
return false;
|
||||
}
|
||||
knownBaseClasses.push_back(classData->m_typeId);
|
||||
}
|
||||
|
||||
return baseClassVisitor(classData, examineTypeId);
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
// EnumerateEntityIds
|
||||
//=========================================================================
|
||||
void EnumerateEntityIds(const void* classPtr, const Uuid& classUuid, const EntityIdVisitor& visitor, SerializeContext* context)
|
||||
while (!knownBaseClasses.empty() && !foundBaseClass)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
TypeId toExamine = knownBaseClasses.back();
|
||||
knownBaseClasses.pop_back();
|
||||
|
||||
if (!context)
|
||||
{
|
||||
context = GetApplicationSerializeContext();
|
||||
if (!context)
|
||||
{
|
||||
AZ_Error("Serialization", false, "No serialize context provided! Failed to get component application default serialize context! ComponentApp is not started or input serialize context should not be null!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
AZStd::vector<const SerializeContext::ClassData*> parentStack;
|
||||
parentStack.reserve(30);
|
||||
auto beginCB = [ &](void* ptr, const SerializeContext::ClassData* classData, const SerializeContext::ClassElement* elementData) -> bool
|
||||
{
|
||||
(void)elementData;
|
||||
|
||||
if (classData->m_typeId == SerializeTypeInfo<EntityId>::GetUuid())
|
||||
{
|
||||
// determine if this is entity ref or just entityId (please refer to the function documentation for more info)
|
||||
bool isEntityId = false;
|
||||
if (!parentStack.empty() && parentStack.back()->m_typeId == SerializeTypeInfo<Entity>::GetUuid())
|
||||
{
|
||||
// our parent in the entity (currently entity has only one EntityId member, but we can check the offset for future proof
|
||||
AZ_Assert(elementData && strcmp(elementData->m_name, "Id") == 0, "class Entity, should have only ONE EntityId member, the actual entity id!");
|
||||
isEntityId = true;
|
||||
}
|
||||
|
||||
EntityId* entityIdPtr = (elementData->m_flags & SerializeContext::ClassElement::FLG_POINTER) ?
|
||||
*reinterpret_cast<EntityId**>(ptr) : reinterpret_cast<EntityId*>(ptr);
|
||||
visitor(*entityIdPtr, isEntityId, elementData);
|
||||
}
|
||||
|
||||
parentStack.push_back(classData);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto endCB = [ &]() -> bool
|
||||
{
|
||||
parentStack.pop_back();
|
||||
return true;
|
||||
};
|
||||
|
||||
SerializeContext::EnumerateInstanceCallContext callContext(
|
||||
beginCB,
|
||||
endCB,
|
||||
context,
|
||||
SerializeContext::ENUM_ACCESS_FOR_READ,
|
||||
nullptr
|
||||
);
|
||||
|
||||
context->EnumerateInstanceConst(
|
||||
&callContext,
|
||||
classPtr,
|
||||
classUuid,
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
context->EnumerateBase(enumerateBaseVisitor, toExamine);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// GetApplicationSerializeContext
|
||||
//=========================================================================
|
||||
SerializeContext* GetApplicationSerializeContext()
|
||||
{
|
||||
SerializeContext* context = nullptr;
|
||||
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
|
||||
return context;
|
||||
}
|
||||
return foundBaseClass;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FindFirstDerivedComponent
|
||||
//=========================================================================
|
||||
Component* FindFirstDerivedComponent(const Entity* entity, const Uuid& typeId)
|
||||
bool CheckIfClassIsDeprecated(SerializeContext* context, const TypeId& typeToExamine)
|
||||
{
|
||||
bool isDeprecated = false;
|
||||
auto classVisitorFn = [&isDeprecated](const AZ::SerializeContext::ClassData* classData, const TypeId& /*rttiBase*/)
|
||||
{
|
||||
for (AZ::Component* component : entity->GetComponents())
|
||||
{
|
||||
if (azrtti_istypeof(typeId, component))
|
||||
{
|
||||
return component;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Component* FindFirstDerivedComponent(EntityId entityId, const Uuid& typeId)
|
||||
{
|
||||
Entity* entity{};
|
||||
ComponentApplicationBus::BroadcastResult(entity, &ComponentApplicationRequests::FindEntity, entityId);
|
||||
return entity ? FindFirstDerivedComponent(entity, typeId) : nullptr;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FindDerivedComponents
|
||||
//=========================================================================
|
||||
Entity::ComponentArrayType FindDerivedComponents(const Entity* entity, const Uuid& typeId)
|
||||
{
|
||||
Entity::ComponentArrayType result;
|
||||
for (AZ::Component* component : entity->GetComponents())
|
||||
{
|
||||
if (azrtti_istypeof(typeId, component))
|
||||
{
|
||||
result.push_back(component);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Entity::ComponentArrayType FindDerivedComponents(EntityId entityId, const Uuid& typeId)
|
||||
{
|
||||
Entity* entity{};
|
||||
ComponentApplicationBus::BroadcastResult(entity, &ComponentApplicationRequests::FindEntity, entityId);
|
||||
return entity ? FindDerivedComponents(entity, typeId) : Entity::ComponentArrayType();
|
||||
}
|
||||
|
||||
bool EnumerateBaseRecursive(SerializeContext* context, const EnumerateBaseRecursiveVisitor& baseClassVisitor, const TypeId& typeToExamine)
|
||||
{
|
||||
AZ_Assert(context, "CheckDeclaresSerializeBaseClass called with no serialize context.");
|
||||
if (!context)
|
||||
// Stop iterating once we stop receiving SerializeContext::ClassData*.
|
||||
if (!classData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZStd::fixed_vector<TypeId, 64> knownBaseClasses = { typeToExamine }; // avoid allocating heap here if possible. 64 types are 64*sizeof(Uuid) which is only 1k.
|
||||
bool foundBaseClass = false;
|
||||
auto enumerateBaseVisitor = [&baseClassVisitor, &knownBaseClasses](const AZ::SerializeContext::ClassData* classData, const TypeId& examineTypeId)
|
||||
{
|
||||
if (!classData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AZStd::find(knownBaseClasses.begin(), knownBaseClasses.end(), classData->m_typeId) == knownBaseClasses.end())
|
||||
{
|
||||
if (knownBaseClasses.size() == 64)
|
||||
{
|
||||
// this should be pretty unlikely since a single class would have to have many other classes in its heirarchy
|
||||
// and it'd all have to be basically in one layer, as we are popping as we explore.
|
||||
AZ_WarningOnce("EntityUtils", false, "While trying to find a base class, all available slots were consumed. consider increasing the size of knownBaseClasses.\n");
|
||||
// we cannot continue any further, assume we did not find it.
|
||||
return false;
|
||||
}
|
||||
knownBaseClasses.push_back(classData->m_typeId);
|
||||
}
|
||||
|
||||
return baseClassVisitor(classData, examineTypeId);
|
||||
};
|
||||
|
||||
while (!knownBaseClasses.empty() && !foundBaseClass)
|
||||
{
|
||||
TypeId toExamine = knownBaseClasses.back();
|
||||
knownBaseClasses.pop_back();
|
||||
|
||||
context->EnumerateBase(enumerateBaseVisitor, toExamine);
|
||||
}
|
||||
|
||||
return foundBaseClass;
|
||||
}
|
||||
|
||||
bool CheckIfClassIsDeprecated(SerializeContext* context, const TypeId& typeToExamine)
|
||||
{
|
||||
bool isDeprecated = false;
|
||||
auto classVisitorFn = [&isDeprecated](const AZ::SerializeContext::ClassData* classData, const TypeId& /*rttiBase*/)
|
||||
{
|
||||
// Stop iterating once we stop receiving SerializeContext::ClassData*.
|
||||
if (!classData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stop iterating if we've found that the class is deprecated
|
||||
if (classData->IsDeprecated())
|
||||
{
|
||||
isDeprecated = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // keep iterating
|
||||
};
|
||||
|
||||
// Check if the type is deprecated
|
||||
const AZ::SerializeContext::ClassData* classData = context->FindClassData(typeToExamine);
|
||||
// Stop iterating if we've found that the class is deprecated
|
||||
if (classData->IsDeprecated())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if any of its bases are deprecated
|
||||
EnumerateBaseRecursive(context, classVisitorFn, typeToExamine);
|
||||
|
||||
return isDeprecated;
|
||||
}
|
||||
|
||||
bool CheckDeclaresSerializeBaseClass(SerializeContext* context, const TypeId& typeToFind, const TypeId& typeToExamine)
|
||||
{
|
||||
AZ_Assert(context, "CheckDeclaresSerializeBaseClass called with no serialize context.");
|
||||
if (!context)
|
||||
{
|
||||
isDeprecated = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool foundBaseClass = false;
|
||||
auto baseClassVisitorFn = [&typeToFind, &foundBaseClass](const AZ::SerializeContext::ClassData* reflectedBase, const TypeId& /*rttiBase*/)
|
||||
{
|
||||
if (!reflectedBase)
|
||||
{
|
||||
foundBaseClass = false;
|
||||
return false; // stop iterating
|
||||
}
|
||||
return true; // keep iterating
|
||||
};
|
||||
|
||||
foundBaseClass = (reflectedBase->m_typeId == typeToFind);
|
||||
if (foundBaseClass)
|
||||
{
|
||||
return false; // we have a base, stop iterating
|
||||
}
|
||||
|
||||
return true; // keep iterating
|
||||
};
|
||||
|
||||
EnumerateBaseRecursive(context, baseClassVisitorFn, typeToExamine);
|
||||
|
||||
return foundBaseClass;
|
||||
}
|
||||
|
||||
bool RemoveDuplicateServicesOfAndAfterIterator(
|
||||
const ComponentDescriptor::DependencyArrayType::iterator& iterator,
|
||||
ComponentDescriptor::DependencyArrayType& providedServiceArray,
|
||||
const Entity* entity)
|
||||
// Check if the type is deprecated
|
||||
const AZ::SerializeContext::ClassData* classData = context->FindClassData(typeToExamine);
|
||||
if (classData->IsDeprecated())
|
||||
{
|
||||
// Build types that strip out AZ_Warnings will complain that entity is unused without this.
|
||||
(void)entity;
|
||||
if (iterator == providedServiceArray.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool duplicateFound = false;
|
||||
|
||||
for (ComponentDescriptor::DependencyArrayType::iterator duplicateCheckIter = AZStd::next(iterator);
|
||||
duplicateCheckIter != providedServiceArray.end();)
|
||||
{
|
||||
if (*iterator == *duplicateCheckIter)
|
||||
{
|
||||
AZ_Warning("Entity", false, "Duplicate service %d found on entity %s [%s]",
|
||||
*duplicateCheckIter,
|
||||
entity ? entity->GetName().c_str() : "Entity not provided",
|
||||
entity ? entity->GetId().ToString().c_str() : "");
|
||||
duplicateCheckIter = providedServiceArray.erase(duplicateCheckIter);
|
||||
duplicateFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
++duplicateCheckIter;
|
||||
}
|
||||
}
|
||||
return duplicateFound;
|
||||
return true;
|
||||
}
|
||||
} // namespace EntityUtils
|
||||
} // namespace AZ
|
||||
|
||||
// Check if any of its bases are deprecated
|
||||
EnumerateBaseRecursive(context, classVisitorFn, typeToExamine);
|
||||
|
||||
return isDeprecated;
|
||||
}
|
||||
|
||||
bool CheckDeclaresSerializeBaseClass(SerializeContext* context, const TypeId& typeToFind, const TypeId& typeToExamine)
|
||||
{
|
||||
AZ_Assert(context, "CheckDeclaresSerializeBaseClass called with no serialize context.");
|
||||
if (!context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool foundBaseClass = false;
|
||||
auto baseClassVisitorFn = [&typeToFind, &foundBaseClass](const AZ::SerializeContext::ClassData* reflectedBase, const TypeId& /*rttiBase*/)
|
||||
{
|
||||
if (!reflectedBase)
|
||||
{
|
||||
foundBaseClass = false;
|
||||
return false; // stop iterating
|
||||
}
|
||||
|
||||
foundBaseClass = (reflectedBase->m_typeId == typeToFind);
|
||||
if (foundBaseClass)
|
||||
{
|
||||
return false; // we have a base, stop iterating
|
||||
}
|
||||
|
||||
return true; // keep iterating
|
||||
};
|
||||
|
||||
EnumerateBaseRecursive(context, baseClassVisitorFn, typeToExamine);
|
||||
|
||||
return foundBaseClass;
|
||||
}
|
||||
|
||||
bool RemoveDuplicateServicesOfAndAfterIterator(
|
||||
const ComponentDescriptor::DependencyArrayType::iterator& iterator,
|
||||
ComponentDescriptor::DependencyArrayType& providedServiceArray,
|
||||
const Entity* entity)
|
||||
{
|
||||
// Build types that strip out AZ_Warnings will complain that entity is unused without this.
|
||||
(void)entity;
|
||||
if (iterator == providedServiceArray.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool duplicateFound = false;
|
||||
|
||||
for (ComponentDescriptor::DependencyArrayType::iterator duplicateCheckIter = AZStd::next(iterator);
|
||||
duplicateCheckIter != providedServiceArray.end();)
|
||||
{
|
||||
if (*iterator == *duplicateCheckIter)
|
||||
{
|
||||
AZ_Warning("Entity", false, "Duplicate service %d found on entity %s [%s]",
|
||||
*duplicateCheckIter,
|
||||
entity ? entity->GetName().c_str() : "Entity not provided",
|
||||
entity ? entity->GetId().ToString().c_str() : "");
|
||||
duplicateCheckIter = providedServiceArray.erase(duplicateCheckIter);
|
||||
duplicateFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
++duplicateCheckIter;
|
||||
}
|
||||
}
|
||||
return duplicateFound;
|
||||
}
|
||||
} // namespace AZ::EntityUtils
|
||||
|
||||
@@ -57,6 +57,7 @@ void ZStd::StartCompressor(unsigned int compressionLevel)
|
||||
ZSTD_customMem customAlloc;
|
||||
customAlloc.customAlloc = reinterpret_cast<ZSTD_allocFunction>(&AllocateMem);
|
||||
customAlloc.customFree = &FreeMem;
|
||||
customAlloc.opaque = nullptr;
|
||||
|
||||
AZ_UNUSED(compressionLevel);
|
||||
m_streamCompression = (ZSTD_createCStream_advanced(customAlloc));
|
||||
|
||||
@@ -14,323 +14,313 @@
|
||||
#include <AzCore/std/containers/map.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
namespace
|
||||
{
|
||||
namespace
|
||||
struct AssetTreeNode;
|
||||
|
||||
// Per-thread data that needs to be stored.
|
||||
struct ThreadData
|
||||
{
|
||||
struct AssetTreeNode;
|
||||
|
||||
// Per-thread data that needs to be stored.
|
||||
struct ThreadData
|
||||
{
|
||||
AZStd::vector<AssetTreeNodeBase*, AZStdAssetTrackingAllocator> m_currentAssetStack;
|
||||
};
|
||||
|
||||
// Access thread data through a virtual function to ensure that the same thread-local data is being shared across DLLs.
|
||||
// Otherwise, the thread_local variables are replicated across DLLs that link the AzCore library, and you'll get a
|
||||
// different version in each module.
|
||||
class ThreadDataProvider
|
||||
{
|
||||
public:
|
||||
virtual ThreadData& GetThreadData() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
class AssetTrackingImpl final :
|
||||
public ThreadDataProvider
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(AssetTrackingImpl, "{01E2A099-3523-40BE-80E0-E0ADD861BEE1}");
|
||||
AZ_CLASS_ALLOCATOR(AssetTrackingImpl, OSAllocator, 0);
|
||||
|
||||
AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable);
|
||||
~AssetTrackingImpl();
|
||||
|
||||
void AssetBegin(const char* id, const char* file, int line);
|
||||
void AssetAttach(void* otherAllocation, const char* file, int line);
|
||||
void AssetEnd();
|
||||
|
||||
ThreadData& GetThreadData() override;
|
||||
|
||||
private:
|
||||
static EnvironmentVariable<AssetTrackingImpl*>& GetEnvironmentVariable();
|
||||
static AssetTrackingImpl* GetSharedInstance();
|
||||
static ThreadData& GetSharedThreadData();
|
||||
|
||||
using PrimaryAssets = AZStd::unordered_map<AssetTrackingId, AssetPrimaryInfo, AZStd::hash<AssetTrackingId>, AZStd::equal_to<AssetTrackingId>, AZStdAssetTrackingAllocator>;
|
||||
using ThreadData = ThreadData;
|
||||
using mutex_type = AZStd::mutex;
|
||||
using lock_type = AZStd::lock_guard<mutex_type>;
|
||||
|
||||
mutex_type m_mutex;
|
||||
PrimaryAssets m_primaryAssets;
|
||||
AssetTreeNodeBase* m_assetRoot = nullptr;
|
||||
AssetAllocationTableBase* m_allocationTable = nullptr;
|
||||
bool m_performingAnalysis = false;
|
||||
|
||||
friend class AssetTracking;
|
||||
friend class AssetTracking::Scope;
|
||||
AZStd::vector<AssetTreeNodeBase*, AZStdAssetTrackingAllocator> m_currentAssetStack;
|
||||
};
|
||||
|
||||
// Access thread data through a virtual function to ensure that the same thread-local data is being shared across DLLs.
|
||||
// Otherwise, the thread_local variables are replicated across DLLs that link the AzCore library, and you'll get a
|
||||
// different version in each module.
|
||||
class ThreadDataProvider
|
||||
{
|
||||
public:
|
||||
virtual ThreadData& GetThreadData() = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class AssetTrackingImpl final :
|
||||
public ThreadDataProvider
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(AssetTrackingImpl, "{01E2A099-3523-40BE-80E0-E0ADD861BEE1}");
|
||||
AZ_CLASS_ALLOCATOR(AssetTrackingImpl, OSAllocator, 0);
|
||||
|
||||
AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable);
|
||||
~AssetTrackingImpl();
|
||||
|
||||
void AssetBegin(const char* id, const char* file, int line);
|
||||
void AssetAttach(void* otherAllocation, const char* file, int line);
|
||||
void AssetEnd();
|
||||
|
||||
ThreadData& GetThreadData() override;
|
||||
|
||||
private:
|
||||
static EnvironmentVariable<AssetTrackingImpl*>& GetEnvironmentVariable();
|
||||
static AssetTrackingImpl* GetSharedInstance();
|
||||
static ThreadData& GetSharedThreadData();
|
||||
|
||||
using PrimaryAssets = AZStd::unordered_map<AssetTrackingId, AssetPrimaryInfo, AZStd::hash<AssetTrackingId>, AZStd::equal_to<AssetTrackingId>, AZStdAssetTrackingAllocator>;
|
||||
using ThreadData = ThreadData;
|
||||
using mutex_type = AZStd::mutex;
|
||||
using lock_type = AZStd::lock_guard<mutex_type>;
|
||||
|
||||
mutex_type m_mutex;
|
||||
PrimaryAssets m_primaryAssets;
|
||||
AssetTreeNodeBase* m_assetRoot = nullptr;
|
||||
AssetAllocationTableBase* m_allocationTable = nullptr;
|
||||
bool m_performingAnalysis = false;
|
||||
|
||||
friend class AssetTracking;
|
||||
friend class AssetTracking::Scope;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AssetTrackingImpl methods
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Debug
|
||||
AssetTrackingImpl::AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable) :
|
||||
m_assetRoot(&assetTree->GetRoot()),
|
||||
m_allocationTable(allocationTable)
|
||||
{
|
||||
AssetTrackingImpl::AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable) :
|
||||
m_assetRoot(&assetTree->GetRoot()),
|
||||
m_allocationTable(allocationTable)
|
||||
{
|
||||
AZ_Assert(!GetSharedInstance(), "Only one AssetTrackingImpl can exist!");
|
||||
AZ_Assert(!GetSharedInstance(), "Only one AssetTrackingImpl can exist!");
|
||||
|
||||
GetEnvironmentVariable().Set(this);
|
||||
AllocatorManager::Instance().EnterProfilingMode();
|
||||
GetEnvironmentVariable().Set(this);
|
||||
AllocatorManager::Instance().EnterProfilingMode();
|
||||
}
|
||||
|
||||
AssetTrackingImpl::~AssetTrackingImpl()
|
||||
{
|
||||
AllocatorManager::Instance().ExitProfilingMode();
|
||||
GetEnvironmentVariable().Reset();
|
||||
}
|
||||
|
||||
void AssetTrackingImpl::AssetBegin(const char* id, const char* file, int line)
|
||||
{
|
||||
// In the future it may be desirable to organize assets based on where in code the asset was entered into.
|
||||
// For now these are ignored.
|
||||
AZ_UNUSED(file);
|
||||
AZ_UNUSED(line);
|
||||
|
||||
using namespace Internal;
|
||||
|
||||
AssetTrackingId assetId(id);
|
||||
auto& threadData = GetSharedThreadData();
|
||||
AssetTreeNodeBase* parentAsset = threadData.m_currentAssetStack.empty() ? nullptr : threadData.m_currentAssetStack.back();
|
||||
AssetTreeNodeBase* childAsset;
|
||||
AssetPrimaryInfo* assetPrimaryInfo;
|
||||
|
||||
if (!parentAsset)
|
||||
{
|
||||
parentAsset = m_assetRoot;
|
||||
}
|
||||
|
||||
AssetTrackingImpl::~AssetTrackingImpl()
|
||||
{
|
||||
AllocatorManager::Instance().ExitProfilingMode();
|
||||
GetEnvironmentVariable().Reset();
|
||||
}
|
||||
lock_type lock(m_mutex);
|
||||
|
||||
void AssetTrackingImpl::AssetBegin(const char* id, const char* file, int line)
|
||||
{
|
||||
// In the future it may be desirable to organize assets based on where in code the asset was entered into.
|
||||
// For now these are ignored.
|
||||
AZ_UNUSED(file);
|
||||
AZ_UNUSED(line);
|
||||
// Locate or create the primary record for this asset
|
||||
auto primaryItr = m_primaryAssets.find(assetId);
|
||||
|
||||
using namespace Internal;
|
||||
|
||||
AssetTrackingId assetId(id);
|
||||
auto& threadData = GetSharedThreadData();
|
||||
AssetTreeNodeBase* parentAsset = threadData.m_currentAssetStack.empty() ? nullptr : threadData.m_currentAssetStack.back();
|
||||
AssetTreeNodeBase* childAsset;
|
||||
AssetPrimaryInfo* assetPrimaryInfo;
|
||||
|
||||
if (!parentAsset)
|
||||
if (primaryItr != m_primaryAssets.end())
|
||||
{
|
||||
parentAsset = m_assetRoot;
|
||||
}
|
||||
|
||||
{
|
||||
lock_type lock(m_mutex);
|
||||
|
||||
// Locate or create the primary record for this asset
|
||||
auto primaryItr = m_primaryAssets.find(assetId);
|
||||
|
||||
if (primaryItr != m_primaryAssets.end())
|
||||
{
|
||||
assetPrimaryInfo = &primaryItr->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto insertResult = m_primaryAssets.emplace(assetId, AssetPrimaryInfo());
|
||||
assetPrimaryInfo = &insertResult.first->second;
|
||||
assetPrimaryInfo->m_id = &insertResult.first->first;
|
||||
}
|
||||
|
||||
// Add this asset to the stack for this thread's context
|
||||
childAsset = parentAsset->FindOrAddChild(assetId, assetPrimaryInfo);
|
||||
}
|
||||
|
||||
threadData.m_currentAssetStack.push_back(childAsset);
|
||||
}
|
||||
|
||||
void AssetTrackingImpl::AssetAttach(void* otherAllocation, const char* file, int line)
|
||||
{
|
||||
AZ_UNUSED(file);
|
||||
AZ_UNUSED(line);
|
||||
|
||||
using namespace Internal;
|
||||
|
||||
AssetTreeNodeBase* assetInfo = m_allocationTable->FindAllocation(otherAllocation);
|
||||
|
||||
// We will push back a nullptr if there is no asset, this is necessary to balance the call to AssetEnd()
|
||||
GetSharedThreadData().m_currentAssetStack.push_back(assetInfo);
|
||||
}
|
||||
|
||||
void AssetTrackingImpl::AssetEnd()
|
||||
{
|
||||
AZ_Assert(!GetSharedThreadData().m_currentAssetStack.empty(), "AssetEnd() called without matching AssetBegin() or AssetAttach. Use the AZ_ASSET_NAMED_SCOPE and AZ_ASSET_ATTACH_TO_SCOPE macros to avoid this!");
|
||||
GetSharedThreadData().m_currentAssetStack.pop_back();
|
||||
}
|
||||
|
||||
AssetTrackingImpl* AssetTrackingImpl::GetSharedInstance()
|
||||
{
|
||||
auto environmentVariable = GetEnvironmentVariable();
|
||||
|
||||
if(environmentVariable)
|
||||
{
|
||||
return *environmentVariable;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ThreadData& AssetTrackingImpl::GetSharedThreadData()
|
||||
{
|
||||
// Cast to the base type so our virtual call doesn't get optimized away. We require GetThreadData() to be executed in the same DLL every time.
|
||||
return static_cast<ThreadDataProvider*>(GetSharedInstance())->GetThreadData();
|
||||
}
|
||||
|
||||
AssetTrackingImpl::ThreadData& AssetTrackingImpl::GetThreadData()
|
||||
{
|
||||
static thread_local ThreadData* data = nullptr;
|
||||
static thread_local typename AZStd::aligned_storage_t<sizeof(ThreadData), alignof(ThreadData)> storage;
|
||||
|
||||
if (!data)
|
||||
{
|
||||
data = new (&storage) ThreadData;
|
||||
}
|
||||
|
||||
return *data;
|
||||
}
|
||||
|
||||
EnvironmentVariable<AssetTrackingImpl*>& AssetTrackingImpl::GetEnvironmentVariable()
|
||||
{
|
||||
static EnvironmentVariable<AssetTrackingImpl*> assetTrackingImpl = Environment::CreateVariable<AssetTrackingImpl*>(AzTypeInfo<AssetTrackingImpl*>::Name());
|
||||
|
||||
return assetTrackingImpl;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AssetTracking::Scope functions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AssetTracking::Scope AssetTracking::Scope::ScopeFromAssetId(const char* file, int line, const char* fmt, ...)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
static const int BUFFER_SIZE = 1024;
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
impl->AssetBegin(buffer, file, line);
|
||||
}
|
||||
|
||||
return Scope();
|
||||
}
|
||||
|
||||
AssetTracking::Scope AssetTracking::Scope::ScopeFromAttachment(void* attachTo, const char* file, int line)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetAttach(attachTo, file, line);
|
||||
}
|
||||
|
||||
return Scope();
|
||||
}
|
||||
|
||||
AssetTracking::Scope::~Scope()
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetEnd();
|
||||
}
|
||||
}
|
||||
|
||||
AssetTracking::Scope::Scope()
|
||||
{
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AssetTracking functions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AssetTracking::EnterScopeByAssetId(const char* file, int line, const char* fmt, ...)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
static const int BUFFER_SIZE = 1024;
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
impl->AssetBegin(buffer, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetTracking::EnterScopeByAttachment(void* attachTo, const char* file, int line)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetAttach(attachTo, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetTracking::ExitScope()
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetEnd();
|
||||
}
|
||||
}
|
||||
|
||||
const char* AssetTracking::GetDebugScope()
|
||||
{
|
||||
// Output debug information about the current asset scope in the current thread.
|
||||
// Do not use in production code.
|
||||
#ifndef RELEASE
|
||||
static const int BUFFER_SIZE = 1024;
|
||||
static char buffer[BUFFER_SIZE];
|
||||
const auto& assetStack = AssetTrackingImpl::GetSharedInstance()->GetThreadData().m_currentAssetStack;
|
||||
|
||||
if (assetStack.empty())
|
||||
{
|
||||
azsnprintf(buffer, BUFFER_SIZE, "<none>");
|
||||
assetPrimaryInfo = &primaryItr->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
char* pos = buffer;
|
||||
for (auto itr = assetStack.rbegin(); itr != assetStack.rend(); ++itr)
|
||||
{
|
||||
pos += azsnprintf(pos, BUFFER_SIZE - (pos - buffer), "%s\n", (*itr)->GetAssetPrimaryInfo()->m_id->m_id.c_str());
|
||||
|
||||
if (pos >= buffer + BUFFER_SIZE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto insertResult = m_primaryAssets.emplace(assetId, AssetPrimaryInfo());
|
||||
assetPrimaryInfo = &insertResult.first->second;
|
||||
assetPrimaryInfo->m_id = &insertResult.first->first;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
// Add this asset to the stack for this thread's context
|
||||
childAsset = parentAsset->FindOrAddChild(assetId, assetPrimaryInfo);
|
||||
}
|
||||
|
||||
AssetTracking::AssetTracking(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable)
|
||||
threadData.m_currentAssetStack.push_back(childAsset);
|
||||
}
|
||||
|
||||
void AssetTrackingImpl::AssetAttach(void* otherAllocation, const char* file, int line)
|
||||
{
|
||||
AZ_UNUSED(file);
|
||||
AZ_UNUSED(line);
|
||||
|
||||
using namespace Internal;
|
||||
|
||||
AssetTreeNodeBase* assetInfo = m_allocationTable->FindAllocation(otherAllocation);
|
||||
|
||||
// We will push back a nullptr if there is no asset, this is necessary to balance the call to AssetEnd()
|
||||
GetSharedThreadData().m_currentAssetStack.push_back(assetInfo);
|
||||
}
|
||||
|
||||
void AssetTrackingImpl::AssetEnd()
|
||||
{
|
||||
AZ_Assert(!GetSharedThreadData().m_currentAssetStack.empty(), "AssetEnd() called without matching AssetBegin() or AssetAttach. Use the AZ_ASSET_NAMED_SCOPE and AZ_ASSET_ATTACH_TO_SCOPE macros to avoid this!");
|
||||
GetSharedThreadData().m_currentAssetStack.pop_back();
|
||||
}
|
||||
|
||||
AssetTrackingImpl* AssetTrackingImpl::GetSharedInstance()
|
||||
{
|
||||
auto environmentVariable = GetEnvironmentVariable();
|
||||
|
||||
if(environmentVariable)
|
||||
{
|
||||
m_impl.reset(aznew AssetTrackingImpl(assetTree, allocationTable));
|
||||
return *environmentVariable;
|
||||
}
|
||||
|
||||
AssetTracking::~AssetTracking()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ThreadData& AssetTrackingImpl::GetSharedThreadData()
|
||||
{
|
||||
// Cast to the base type so our virtual call doesn't get optimized away. We require GetThreadData() to be executed in the same DLL every time.
|
||||
return static_cast<ThreadDataProvider*>(GetSharedInstance())->GetThreadData();
|
||||
}
|
||||
|
||||
AssetTrackingImpl::ThreadData& AssetTrackingImpl::GetThreadData()
|
||||
{
|
||||
static thread_local ThreadData* data = nullptr;
|
||||
static thread_local typename AZStd::aligned_storage_t<sizeof(ThreadData), alignof(ThreadData)> storage;
|
||||
|
||||
if (!data)
|
||||
{
|
||||
data = new (&storage) ThreadData;
|
||||
}
|
||||
|
||||
AssetTreeNodeBase* AssetTracking::GetCurrentThreadAsset() const
|
||||
{
|
||||
const auto& assetStack = m_impl->GetThreadData().m_currentAssetStack;
|
||||
AssetTreeNodeBase* result = assetStack.empty() ? nullptr : assetStack.back();
|
||||
return *data;
|
||||
}
|
||||
|
||||
return result;
|
||||
EnvironmentVariable<AssetTrackingImpl*>& AssetTrackingImpl::GetEnvironmentVariable()
|
||||
{
|
||||
static EnvironmentVariable<AssetTrackingImpl*> assetTrackingImpl = Environment::CreateVariable<AssetTrackingImpl*>(AzTypeInfo<AssetTrackingImpl*>::Name());
|
||||
|
||||
return assetTrackingImpl;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AssetTracking::Scope functions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AssetTracking::Scope AssetTracking::Scope::ScopeFromAssetId(const char* file, int line, const char* fmt, ...)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
static const int BUFFER_SIZE = 1024;
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
impl->AssetBegin(buffer, file, line);
|
||||
}
|
||||
|
||||
return Scope();
|
||||
}
|
||||
|
||||
AssetTracking::Scope AssetTracking::Scope::ScopeFromAttachment(void* attachTo, const char* file, int line)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetAttach(attachTo, file, line);
|
||||
}
|
||||
|
||||
return Scope();
|
||||
}
|
||||
|
||||
AssetTracking::Scope::~Scope()
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetEnd();
|
||||
}
|
||||
}
|
||||
|
||||
AssetTracking::Scope::Scope()
|
||||
{
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AssetTracking functions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AssetTracking::EnterScopeByAssetId(const char* file, int line, const char* fmt, ...)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
static const int BUFFER_SIZE = 1024;
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
impl->AssetBegin(buffer, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetTracking::EnterScopeByAttachment(void* attachTo, const char* file, int line)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetAttach(attachTo, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetTracking::ExitScope()
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetEnd();
|
||||
}
|
||||
}
|
||||
|
||||
const char* AssetTracking::GetDebugScope()
|
||||
{
|
||||
// Output debug information about the current asset scope in the current thread.
|
||||
// Do not use in production code.
|
||||
#ifndef RELEASE
|
||||
static const int BUFFER_SIZE = 1024;
|
||||
static char buffer[BUFFER_SIZE];
|
||||
const auto& assetStack = AssetTrackingImpl::GetSharedInstance()->GetThreadData().m_currentAssetStack;
|
||||
|
||||
if (assetStack.empty())
|
||||
{
|
||||
azsnprintf(buffer, BUFFER_SIZE, "<none>");
|
||||
}
|
||||
else
|
||||
{
|
||||
char* pos = buffer;
|
||||
for (auto itr = assetStack.rbegin(); itr != assetStack.rend(); ++itr)
|
||||
{
|
||||
pos += azsnprintf(pos, BUFFER_SIZE - (pos - buffer), "%s\n", (*itr)->GetAssetPrimaryInfo()->m_id->m_id.c_str());
|
||||
|
||||
if (pos >= buffer + BUFFER_SIZE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
AssetTracking::AssetTracking(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable)
|
||||
{
|
||||
m_impl.reset(aznew AssetTrackingImpl(assetTree, allocationTable));
|
||||
}
|
||||
|
||||
AssetTracking::~AssetTracking()
|
||||
{
|
||||
}
|
||||
|
||||
AssetTreeNodeBase* AssetTracking::GetCurrentThreadAsset() const
|
||||
{
|
||||
const auto& assetStack = m_impl->GetThreadData().m_currentAssetStack;
|
||||
AssetTreeNodeBase* result = assetStack.empty() ? nullptr : assetStack.back();
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -11,19 +11,19 @@
|
||||
#include <AzCore/std/time.h>
|
||||
#include <AzCore/std/parallel/thread.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
EventTrace::ScopedSlice::ScopedSlice(const char* name, const char* category)
|
||||
: m_Name(name)
|
||||
, m_Category(category)
|
||||
, m_Time(AZStd::GetTimeNowMicroSecond())
|
||||
{
|
||||
EventTrace::ScopedSlice::ScopedSlice(const char* name, const char* category)
|
||||
: m_Name(name)
|
||||
, m_Category(category)
|
||||
, m_Time(AZStd::GetTimeNowMicroSecond())
|
||||
{}
|
||||
|
||||
EventTrace::ScopedSlice::~ScopedSlice()
|
||||
{
|
||||
EventTraceDrillerBus::TryQueueBroadcast(&EventTraceDrillerInterface::RecordSlice, m_Name, m_Category, AZStd::this_thread::get_id(), m_Time, (uint32_t)(AZStd::GetTimeNowMicroSecond() - m_Time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EventTrace::ScopedSlice::~ScopedSlice()
|
||||
{
|
||||
EventTraceDrillerBus::TryQueueBroadcast(
|
||||
&EventTraceDrillerInterface::RecordSlice, m_Name, m_Category, AZStd::this_thread::get_id(), m_Time,
|
||||
(uint32_t)(AZStd::GetTimeNowMicroSecond() - m_Time));
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
|
||||
@@ -11,151 +11,149 @@
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
namespace Crc
|
||||
{
|
||||
namespace Crc
|
||||
constexpr u32 EventTraceDriller = AZ_CRC_CE("EventTraceDriller");
|
||||
constexpr u32 Slice = AZ_CRC_CE("Slice");
|
||||
constexpr u32 ThreadInfo = AZ_CRC_CE("ThreadInfo");
|
||||
constexpr u32 Name = AZ_CRC_CE("Name");
|
||||
constexpr u32 Category = AZ_CRC_CE("Category");
|
||||
constexpr u32 ThreadId = AZ_CRC_CE("ThreadId");
|
||||
constexpr u32 Timestamp = AZ_CRC_CE("Timestamp");
|
||||
constexpr u32 Duration = AZ_CRC_CE("Duration");
|
||||
constexpr u32 Instant = AZ_CRC_CE("Instant");
|
||||
}
|
||||
|
||||
EventTraceDriller::EventTraceDriller()
|
||||
{
|
||||
EventTraceDrillerSetupBus::Handler::BusConnect();
|
||||
AZStd::ThreadDrillerEventBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
EventTraceDriller::~EventTraceDriller()
|
||||
{
|
||||
AZStd::ThreadDrillerEventBus::Handler::BusDisconnect();
|
||||
EventTraceDrillerSetupBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void EventTraceDriller::Start(const Param* params, int numParams)
|
||||
{
|
||||
(void)params;
|
||||
(void)numParams;
|
||||
|
||||
EventTraceDrillerBus::Handler::BusConnect();
|
||||
TickBus::Handler::BusConnect();
|
||||
|
||||
EventTraceDrillerBus::AllowFunctionQueuing(true);
|
||||
}
|
||||
|
||||
void EventTraceDriller::Stop()
|
||||
{
|
||||
EventTraceDrillerBus::AllowFunctionQueuing(false);
|
||||
EventTraceDrillerBus::ClearQueuedEvents();
|
||||
|
||||
EventTraceDrillerBus::Handler::BusDisconnect();
|
||||
TickBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void EventTraceDriller::OnTick(float deltaTime, ScriptTimePoint time)
|
||||
{
|
||||
(void)deltaTime;
|
||||
(void)time;
|
||||
|
||||
AZ_TRACE_METHOD();
|
||||
RecordThreads();
|
||||
EventTraceDrillerBus::ExecuteQueuedEvents();
|
||||
}
|
||||
|
||||
void EventTraceDriller::SetThreadName(const AZStd::thread_id& id, const char* name)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_ThreadMutex);
|
||||
m_Threads[(size_t)id.m_id] = ThreadData{ name };
|
||||
}
|
||||
|
||||
void EventTraceDriller::OnThreadEnter(const AZStd::thread::id& id, const AZStd::thread_desc* desc)
|
||||
{
|
||||
if (desc && desc->m_name)
|
||||
{
|
||||
const u32 EventTraceDriller = AZ_CRC("EventTraceDriller", 0xf7aeae55);
|
||||
const u32 Slice = AZ_CRC("Slice", 0x3dae78a5);
|
||||
const u32 ThreadInfo = AZ_CRC("ThreadInfo", 0x89bf78be);
|
||||
const u32 Name = AZ_CRC("Name", 0x5e237e06);
|
||||
const u32 Category = AZ_CRC("Category", 0x064c19c1);
|
||||
const u32 ThreadId = AZ_CRC("ThreadId", 0xd0fd9043);
|
||||
const u32 Timestamp = AZ_CRC("Timestamp", 0xa5d6e63e);
|
||||
const u32 Duration = AZ_CRC("Duration", 0x865f80c0);
|
||||
const u32 Instant = AZ_CRC("Instant", 0x0e9047ad);
|
||||
SetThreadName(id, desc->m_name);
|
||||
}
|
||||
}
|
||||
|
||||
EventTraceDriller::EventTraceDriller()
|
||||
void EventTraceDriller::OnThreadExit(const AZStd::thread::id& id)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_ThreadMutex);
|
||||
m_Threads.erase((size_t)id.m_id);
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordThreads()
|
||||
{
|
||||
if (!m_output || m_Threads.empty())
|
||||
{
|
||||
EventTraceDrillerSetupBus::Handler::BusConnect();
|
||||
AZStd::ThreadDrillerEventBus::Handler::BusConnect();
|
||||
return;
|
||||
}
|
||||
// Main bus mutex guards m_output.
|
||||
auto& context = EventTraceDrillerBus::GetOrCreateContext();
|
||||
|
||||
EventTraceDriller::~EventTraceDriller()
|
||||
{
|
||||
AZStd::ThreadDrillerEventBus::Handler::BusDisconnect();
|
||||
EventTraceDrillerSetupBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void EventTraceDriller::Start(const Param* params, int numParams)
|
||||
{
|
||||
(void)params;
|
||||
(void)numParams;
|
||||
|
||||
EventTraceDrillerBus::Handler::BusConnect();
|
||||
TickBus::Handler::BusConnect();
|
||||
|
||||
EventTraceDrillerBus::AllowFunctionQueuing(true);
|
||||
}
|
||||
|
||||
void EventTraceDriller::Stop()
|
||||
{
|
||||
EventTraceDrillerBus::AllowFunctionQueuing(false);
|
||||
EventTraceDrillerBus::ClearQueuedEvents();
|
||||
|
||||
EventTraceDrillerBus::Handler::BusDisconnect();
|
||||
TickBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void EventTraceDriller::OnTick(float deltaTime, ScriptTimePoint time)
|
||||
{
|
||||
(void)deltaTime;
|
||||
(void)time;
|
||||
|
||||
AZ_TRACE_METHOD();
|
||||
RecordThreads();
|
||||
EventTraceDrillerBus::ExecuteQueuedEvents();
|
||||
}
|
||||
|
||||
void EventTraceDriller::SetThreadName(const AZStd::thread_id& id, const char* name)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_ThreadMutex);
|
||||
m_Threads[(size_t)id.m_id] = ThreadData{ name };
|
||||
}
|
||||
|
||||
void EventTraceDriller::OnThreadEnter(const AZStd::thread::id& id, const AZStd::thread_desc* desc)
|
||||
{
|
||||
if (desc && desc->m_name)
|
||||
{
|
||||
SetThreadName(id, desc->m_name);
|
||||
}
|
||||
}
|
||||
|
||||
void EventTraceDriller::OnThreadExit(const AZStd::thread::id& id)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_ThreadMutex);
|
||||
m_Threads.erase((size_t)id.m_id);
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordThreads()
|
||||
{
|
||||
if (m_output && m_Threads.size())
|
||||
{
|
||||
// Main bus mutex guards m_output.
|
||||
auto& context = EventTraceDrillerBus::GetOrCreateContext();
|
||||
|
||||
AZStd::scoped_lock<decltype(context.m_contextMutex), decltype(m_ThreadMutex)> lock(context.m_contextMutex, m_ThreadMutex);
|
||||
for (const auto& keyValue : m_Threads)
|
||||
{
|
||||
m_output->BeginTag(Crc::EventTraceDriller);
|
||||
m_output->BeginTag(Crc::ThreadInfo);
|
||||
m_output->Write(Crc::ThreadId, keyValue.first);
|
||||
m_output->Write(Crc::Name, keyValue.second.name);
|
||||
m_output->EndTag(Crc::ThreadInfo);
|
||||
m_output->EndTag(Crc::EventTraceDriller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordSlice(
|
||||
const char* name,
|
||||
const char* category,
|
||||
const AZStd::thread_id threadId,
|
||||
AZ::u64 timestamp,
|
||||
AZ::u32 duration)
|
||||
AZStd::scoped_lock<decltype(context.m_contextMutex), decltype(m_ThreadMutex)> lock(context.m_contextMutex, m_ThreadMutex);
|
||||
for (const auto& keyValue : m_Threads)
|
||||
{
|
||||
m_output->BeginTag(Crc::EventTraceDriller);
|
||||
m_output->BeginTag(Crc::Slice);
|
||||
m_output->Write(Crc::Name, name);
|
||||
m_output->Write(Crc::Category, category);
|
||||
m_output->Write(Crc::ThreadId, (size_t)threadId.m_id);
|
||||
m_output->Write(Crc::Timestamp, timestamp);
|
||||
m_output->Write(Crc::Duration, std::max(duration, 1u));
|
||||
m_output->EndTag(Crc::Slice);
|
||||
m_output->EndTag(Crc::EventTraceDriller);
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordInstantGlobal(
|
||||
const char* name,
|
||||
const char* category,
|
||||
AZ::u64 timestamp)
|
||||
{
|
||||
m_output->BeginTag(Crc::EventTraceDriller);
|
||||
m_output->BeginTag(Crc::Instant);
|
||||
m_output->Write(Crc::Name, name);
|
||||
m_output->Write(Crc::Category, category);
|
||||
m_output->Write(Crc::Timestamp, timestamp);
|
||||
m_output->EndTag(Crc::Instant);
|
||||
m_output->EndTag(Crc::EventTraceDriller);
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordInstantThread(
|
||||
const char* name,
|
||||
const char* category,
|
||||
const AZStd::thread_id threadId,
|
||||
AZ::u64 timestamp)
|
||||
{
|
||||
m_output->BeginTag(Crc::EventTraceDriller);
|
||||
m_output->BeginTag(Crc::Instant);
|
||||
m_output->Write(Crc::Name, name);
|
||||
m_output->Write(Crc::Category, category);
|
||||
m_output->Write(Crc::ThreadId, (size_t)threadId.m_id);
|
||||
m_output->Write(Crc::Timestamp, timestamp);
|
||||
m_output->EndTag(Crc::Instant);
|
||||
m_output->BeginTag(Crc::ThreadInfo);
|
||||
m_output->Write(Crc::ThreadId, keyValue.first);
|
||||
m_output->Write(Crc::Name, keyValue.second.name);
|
||||
m_output->EndTag(Crc::ThreadInfo);
|
||||
m_output->EndTag(Crc::EventTraceDriller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordSlice(
|
||||
const char* name,
|
||||
const char* category,
|
||||
const AZStd::thread_id threadId,
|
||||
AZ::u64 timestamp,
|
||||
AZ::u32 duration)
|
||||
{
|
||||
m_output->BeginTag(Crc::EventTraceDriller);
|
||||
m_output->BeginTag(Crc::Slice);
|
||||
m_output->Write(Crc::Name, name);
|
||||
m_output->Write(Crc::Category, category);
|
||||
m_output->Write(Crc::ThreadId, (size_t)threadId.m_id);
|
||||
m_output->Write(Crc::Timestamp, timestamp);
|
||||
m_output->Write(Crc::Duration, std::max(duration, 1u));
|
||||
m_output->EndTag(Crc::Slice);
|
||||
m_output->EndTag(Crc::EventTraceDriller);
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordInstantGlobal(
|
||||
const char* name,
|
||||
const char* category,
|
||||
AZ::u64 timestamp)
|
||||
{
|
||||
m_output->BeginTag(Crc::EventTraceDriller);
|
||||
m_output->BeginTag(Crc::Instant);
|
||||
m_output->Write(Crc::Name, name);
|
||||
m_output->Write(Crc::Category, category);
|
||||
m_output->Write(Crc::Timestamp, timestamp);
|
||||
m_output->EndTag(Crc::Instant);
|
||||
m_output->EndTag(Crc::EventTraceDriller);
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordInstantThread(
|
||||
const char* name,
|
||||
const char* category,
|
||||
const AZStd::thread_id threadId,
|
||||
AZ::u64 timestamp)
|
||||
{
|
||||
m_output->BeginTag(Crc::EventTraceDriller);
|
||||
m_output->BeginTag(Crc::Instant);
|
||||
m_output->Write(Crc::Name, name);
|
||||
m_output->Write(Crc::Category, category);
|
||||
m_output->Write(Crc::ThreadId, (size_t)threadId.m_id);
|
||||
m_output->Write(Crc::Timestamp, timestamp);
|
||||
m_output->EndTag(Crc::Instant);
|
||||
m_output->EndTag(Crc::EventTraceDriller);
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
|
||||
@@ -79,6 +79,7 @@ namespace AZ::Debug
|
||||
constexpr LogLevel DefaultLogLevel = LogLevel::Info;
|
||||
|
||||
AZ_CVAR_SCOPED(int, bg_traceLogLevel, DefaultLogLevel, nullptr, ConsoleFunctorFlags::Null, "Enable trace message logging in release mode. 0=disabled, 1=errors, 2=warnings, 3=info.");
|
||||
AZ_CVAR_SCOPED(bool, bg_alwaysShowCallstack, false, nullptr, ConsoleFunctorFlags::Null, "Force stack trace output without allowing ebus interception.");
|
||||
|
||||
/**
|
||||
* If any listener returns true, store the result so we don't outputs detailed information.
|
||||
@@ -280,6 +281,13 @@ namespace AZ::Debug
|
||||
|
||||
TraceMessageResult result;
|
||||
EBUS_EVENT_RESULT(result, TraceMessageBus, OnPreAssert, fileName, line, funcName, message);
|
||||
|
||||
if (bg_alwaysShowCallstack)
|
||||
{
|
||||
// If we're always showing the callstack, print it now before there's any chance of an ebus handler interrupting
|
||||
PrintCallstack(g_dbgSystemWnd, 1);
|
||||
}
|
||||
|
||||
if (result.m_value)
|
||||
{
|
||||
g_alreadyHandlingAssertOrFatal = false;
|
||||
@@ -305,7 +313,10 @@ namespace AZ::Debug
|
||||
}
|
||||
|
||||
Output(g_dbgSystemWnd, "------------------------------------------------\n");
|
||||
PrintCallstack(g_dbgSystemWnd, 1);
|
||||
if (!bg_alwaysShowCallstack)
|
||||
{
|
||||
PrintCallstack(g_dbgSystemWnd, 1);
|
||||
}
|
||||
Output(g_dbgSystemWnd, "==================================================================\n");
|
||||
|
||||
char dialogBoxText[g_maxMessageLength];
|
||||
@@ -530,6 +541,16 @@ namespace AZ::Debug
|
||||
}*/
|
||||
}
|
||||
|
||||
RawOutput(window, message);
|
||||
}
|
||||
|
||||
void Trace::RawOutput(const char* window, const char* message)
|
||||
{
|
||||
if (!window)
|
||||
{
|
||||
window = g_dbgSystemWnd;
|
||||
}
|
||||
|
||||
// printf on Windows platforms seem to have a buffer length limit of 4096 characters
|
||||
// Therefore fwrite is used directly to write the window and message to stdout
|
||||
AZStd::string_view windowView{ window };
|
||||
@@ -574,9 +595,19 @@ namespace AZ::Debug
|
||||
}
|
||||
|
||||
azstrcat(lines[i], AZ_ARRAY_SIZE(lines[i]), "\n");
|
||||
|
||||
// Use Output instead of AZ_Printf to be consistent with the exception output code and avoid
|
||||
// this accidentally being suppressed as a normal message
|
||||
Output(window, lines[i]);
|
||||
|
||||
if (bg_alwaysShowCallstack)
|
||||
{
|
||||
// Use Raw Output as this cannot be suppressed
|
||||
RawOutput(window, lines[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Output(window, lines[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,9 @@ namespace AZ
|
||||
|
||||
static void Output(const char* window, const char* message);
|
||||
|
||||
/// Called by output to handle the actual output, does not interact with ebus or allow interception
|
||||
static void RawOutput(const char* window, const char* message);
|
||||
|
||||
static void PrintCallstack(const char* window, unsigned int suppressCount = 0, void* nativeContext = 0);
|
||||
|
||||
/// PEXCEPTION_POINTERS on Windows, always NULL on other platforms
|
||||
|
||||
@@ -9,94 +9,91 @@
|
||||
#include <AzCore/Debug/TraceMessagesDriller.h>
|
||||
#include <AzCore/Math/Crc.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
//=========================================================================
|
||||
// Start
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::Start(const Param* params, int numParams)
|
||||
{
|
||||
//=========================================================================
|
||||
// Start
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::Start(const Param* params, int numParams)
|
||||
{
|
||||
(void)params;
|
||||
(void)numParams;
|
||||
BusConnect();
|
||||
}
|
||||
(void)params;
|
||||
(void)numParams;
|
||||
BusConnect();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Stop
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::Stop()
|
||||
{
|
||||
BusDisconnect();
|
||||
}
|
||||
//=========================================================================
|
||||
// Stop
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::Stop()
|
||||
{
|
||||
BusDisconnect();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// OnAssert
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnAssert(const char* message)
|
||||
{
|
||||
// Not sure if we can really capture assert since the code will stop executing very soon.
|
||||
m_output->BeginTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
m_output->Write(AZ_CRC("OnAssert", 0xb74db4ce), message);
|
||||
m_output->EndTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
}
|
||||
//=========================================================================
|
||||
// OnAssert
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnAssert(const char* message)
|
||||
{
|
||||
// Not sure if we can really capture assert since the code will stop executing very soon.
|
||||
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
m_output->Write(AZ_CRC_CE("OnAssert"), message);
|
||||
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// OnException
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnException(const char* message)
|
||||
{
|
||||
// Not sure if we can really capture exception since the code will stop executing very soon.
|
||||
m_output->BeginTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
m_output->Write(AZ_CRC("OnException", 0xfe457d12), message);
|
||||
m_output->EndTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
}
|
||||
//=========================================================================
|
||||
// OnException
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnException(const char* message)
|
||||
{
|
||||
// Not sure if we can really capture exception since the code will stop executing very soon.
|
||||
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
m_output->Write(AZ_CRC_CE("OnException"), message);
|
||||
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// OnError
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnError(const char* window, const char* message)
|
||||
{
|
||||
m_output->BeginTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
m_output->BeginTag(AZ_CRC("OnError", 0x4993c634));
|
||||
m_output->Write(AZ_CRC("Window", 0x8be4f9dd), window);
|
||||
m_output->Write(AZ_CRC("Message", 0xb6bd307f), message);
|
||||
m_output->EndTag(AZ_CRC("OnError", 0x4993c634));
|
||||
m_output->EndTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
}
|
||||
//=========================================================================
|
||||
// OnError
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnError(const char* window, const char* message)
|
||||
{
|
||||
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
m_output->BeginTag(AZ_CRC_CE("OnError"));
|
||||
m_output->Write(AZ_CRC_CE("Window"), window);
|
||||
m_output->Write(AZ_CRC_CE("Message"), message);
|
||||
m_output->EndTag(AZ_CRC_CE("OnError"));
|
||||
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// OnWarning
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnWarning(const char* window, const char* message)
|
||||
{
|
||||
m_output->BeginTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
m_output->BeginTag(AZ_CRC("OnWarning", 0x7d90abea));
|
||||
m_output->Write(AZ_CRC("Window", 0x8be4f9dd), window);
|
||||
m_output->Write(AZ_CRC("Message", 0xb6bd307f), message);
|
||||
m_output->EndTag(AZ_CRC("OnWarning", 0x7d90abea));
|
||||
m_output->EndTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
}
|
||||
//=========================================================================
|
||||
// OnWarning
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnWarning(const char* window, const char* message)
|
||||
{
|
||||
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
m_output->BeginTag(AZ_CRC_CE("OnWarning"));
|
||||
m_output->Write(AZ_CRC_CE("Window"), window);
|
||||
m_output->Write(AZ_CRC_CE("Message"), message);
|
||||
m_output->EndTag(AZ_CRC_CE("OnWarning"));
|
||||
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// OnPrintf
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnPrintf(const char* window, const char* message)
|
||||
{
|
||||
m_output->BeginTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
m_output->BeginTag(AZ_CRC("OnPrintf", 0xd4b5c294));
|
||||
m_output->Write(AZ_CRC("Window", 0x8be4f9dd), window);
|
||||
m_output->Write(AZ_CRC("Message", 0xb6bd307f), message);
|
||||
m_output->EndTag(AZ_CRC("OnPrintf", 0xd4b5c294));
|
||||
m_output->EndTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
}
|
||||
} // namespace Debug
|
||||
//=========================================================================
|
||||
// OnPrintf
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnPrintf(const char* window, const char* message)
|
||||
{
|
||||
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
m_output->BeginTag(AZ_CRC_CE("OnPrintf"));
|
||||
m_output->Write(AZ_CRC_CE("Window"), window);
|
||||
m_output->Write(AZ_CRC_CE("Message"), message);
|
||||
m_output->EndTag(AZ_CRC_CE("OnPrintf"));
|
||||
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
@@ -12,283 +12,280 @@
|
||||
#include <AzCore/Script/ScriptContext.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
//! Trace Message Event Handler for Automation.
|
||||
//! Since TraceMessageBus will be called from multiple threads and
|
||||
//! python interpreter is single threaded, all the bus calls are
|
||||
//! queued into a list and called at the end of the frame in the main thread.
|
||||
//! @note this class is not using the usual AZ_EBUS_BEHAVIOR_BINDER
|
||||
//! macro as the signature needs to be changed to connect to Tick bus.
|
||||
class TraceMessageBusHandler
|
||||
: public AZ::Debug::TraceMessageBus::Handler
|
||||
, public AZ::BehaviorEBusHandler
|
||||
, public AZ::TickBus::Handler
|
||||
{
|
||||
//! Trace Message Event Handler for Automation.
|
||||
//! Since TraceMessageBus will be called from multiple threads and
|
||||
//! python interpreter is single threaded, all the bus calls are
|
||||
//! queued into a list and called at the end of the frame in the main thread.
|
||||
//! @note this class is not using the usual AZ_EBUS_BEHAVIOR_BINDER
|
||||
//! macro as the signature needs to be changed to connect to Tick bus.
|
||||
class TraceMessageBusHandler
|
||||
: public AZ::Debug::TraceMessageBus::Handler
|
||||
, public AZ::BehaviorEBusHandler
|
||||
, public AZ::TickBus::Handler
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(TraceMessageBusHandler, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(TraceMessageBusHandler, "{5CDBAF09-5EB0-48AC-B327-2AF8601BB550}", AZ::BehaviorEBusHandler);
|
||||
|
||||
TraceMessageBusHandler();
|
||||
|
||||
using EventFunctionsParameterPack = AZStd::Internal::pack_traits_arg_sequence<
|
||||
decltype(&TraceMessageBusHandler::OnPreAssert),
|
||||
decltype(&TraceMessageBusHandler::OnPreError),
|
||||
decltype(&TraceMessageBusHandler::OnPreWarning),
|
||||
decltype(&TraceMessageBusHandler::OnAssert),
|
||||
decltype(&TraceMessageBusHandler::OnError),
|
||||
decltype(&TraceMessageBusHandler::OnWarning),
|
||||
decltype(&TraceMessageBusHandler::OnException),
|
||||
decltype(&TraceMessageBusHandler::OnPrintf),
|
||||
decltype(&TraceMessageBusHandler::OnOutput)
|
||||
>;
|
||||
|
||||
enum
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(TraceMessageBusHandler, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(TraceMessageBusHandler, "{5CDBAF09-5EB0-48AC-B327-2AF8601BB550}", AZ::BehaviorEBusHandler);
|
||||
|
||||
TraceMessageBusHandler();
|
||||
|
||||
using EventFunctionsParameterPack = AZStd::Internal::pack_traits_arg_sequence<
|
||||
decltype(&TraceMessageBusHandler::OnPreAssert),
|
||||
decltype(&TraceMessageBusHandler::OnPreError),
|
||||
decltype(&TraceMessageBusHandler::OnPreWarning),
|
||||
decltype(&TraceMessageBusHandler::OnAssert),
|
||||
decltype(&TraceMessageBusHandler::OnError),
|
||||
decltype(&TraceMessageBusHandler::OnWarning),
|
||||
decltype(&TraceMessageBusHandler::OnException),
|
||||
decltype(&TraceMessageBusHandler::OnPrintf),
|
||||
decltype(&TraceMessageBusHandler::OnOutput)
|
||||
>;
|
||||
|
||||
enum
|
||||
{
|
||||
FN_OnPreAssert = 0,
|
||||
FN_OnPreError,
|
||||
FN_OnPreWarning,
|
||||
FN_OnAssert,
|
||||
FN_OnError,
|
||||
FN_OnWarning,
|
||||
FN_OnException,
|
||||
FN_OnPrintf,
|
||||
FN_OnOutput,
|
||||
FN_MAX
|
||||
};
|
||||
|
||||
static inline constexpr const char* m_functionNames[FN_MAX] =
|
||||
{
|
||||
"OnPreAssert",
|
||||
"OnPreError",
|
||||
"OnPreWarning",
|
||||
"OnAssert",
|
||||
"OnError",
|
||||
"OnWarning",
|
||||
"OnException",
|
||||
"OnPrintf",
|
||||
"OnOutput"
|
||||
};
|
||||
|
||||
// AZ::BehaviorEBusHandler overrides...
|
||||
int GetFunctionIndex(const char* functionName) const override;
|
||||
void Disconnect() override;
|
||||
bool Connect(AZ::BehaviorValueParameter* id = nullptr) override;
|
||||
bool IsConnected() override;
|
||||
bool IsConnectedId(AZ::BehaviorValueParameter* id) override;
|
||||
|
||||
// TraceMessageBus
|
||||
/*
|
||||
* Note: Since at editor runtime there is already have a handler, for automation (OnPreAssert, OnPreWarning, OnPreWarning)
|
||||
* must be used instead of (OnAssert, OnWarning, OnError)
|
||||
*/
|
||||
bool OnPreAssert(const char* fileName, int line, const char* func, const char* message) override;
|
||||
bool OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message) override;
|
||||
bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override;
|
||||
bool OnAssert(const char* message) override;
|
||||
bool OnError(const char* window, const char* message) override;
|
||||
bool OnWarning(const char* window, const char* message) override;
|
||||
bool OnException(const char* message) override;
|
||||
bool OnPrintf(const char* window, const char* message) override;
|
||||
bool OnOutput(const char* window, const char* message) override;
|
||||
|
||||
// AZ::TickBus::Handler overrides ...
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
int GetTickOrder() override;
|
||||
|
||||
private:
|
||||
void QueueMessageCall(AZStd::function<void()> messageCall);
|
||||
void FlushMessageCalls();
|
||||
|
||||
AZStd::list<AZStd::function<void()>> m_messageCalls;
|
||||
AZStd::mutex m_messageCallsLock;
|
||||
FN_OnPreAssert = 0,
|
||||
FN_OnPreError,
|
||||
FN_OnPreWarning,
|
||||
FN_OnAssert,
|
||||
FN_OnError,
|
||||
FN_OnWarning,
|
||||
FN_OnException,
|
||||
FN_OnPrintf,
|
||||
FN_OnOutput,
|
||||
FN_MAX
|
||||
};
|
||||
|
||||
TraceMessageBusHandler::TraceMessageBusHandler()
|
||||
static inline constexpr const char* m_functionNames[FN_MAX] =
|
||||
{
|
||||
m_events.resize(FN_MAX);
|
||||
"OnPreAssert",
|
||||
"OnPreError",
|
||||
"OnPreWarning",
|
||||
"OnAssert",
|
||||
"OnError",
|
||||
"OnWarning",
|
||||
"OnException",
|
||||
"OnPrintf",
|
||||
"OnOutput"
|
||||
};
|
||||
|
||||
SetEvent(&TraceMessageBusHandler::OnPreAssert, m_functionNames[FN_OnPreAssert]);
|
||||
SetEvent(&TraceMessageBusHandler::OnPreError, m_functionNames[FN_OnPreError]);
|
||||
SetEvent(&TraceMessageBusHandler::OnPreWarning, m_functionNames[FN_OnPreWarning]);
|
||||
SetEvent(&TraceMessageBusHandler::OnAssert, m_functionNames[FN_OnAssert]);
|
||||
SetEvent(&TraceMessageBusHandler::OnError, m_functionNames[FN_OnError]);
|
||||
SetEvent(&TraceMessageBusHandler::OnWarning, m_functionNames[FN_OnWarning]);
|
||||
SetEvent(&TraceMessageBusHandler::OnException, m_functionNames[FN_OnException]);
|
||||
SetEvent(&TraceMessageBusHandler::OnPrintf, m_functionNames[FN_OnPrintf]);
|
||||
SetEvent(&TraceMessageBusHandler::OnOutput, m_functionNames[FN_OnOutput]);
|
||||
}
|
||||
// AZ::BehaviorEBusHandler overrides...
|
||||
int GetFunctionIndex(const char* functionName) const override;
|
||||
void Disconnect() override;
|
||||
bool Connect(AZ::BehaviorValueParameter* id = nullptr) override;
|
||||
bool IsConnected() override;
|
||||
bool IsConnectedId(AZ::BehaviorValueParameter* id) override;
|
||||
|
||||
int TraceMessageBusHandler::GetFunctionIndex(const char* functionName) const
|
||||
// TraceMessageBus
|
||||
/*
|
||||
* Note: Since at editor runtime there is already have a handler, for automation (OnPreAssert, OnPreWarning, OnPreWarning)
|
||||
* must be used instead of (OnAssert, OnWarning, OnError)
|
||||
*/
|
||||
bool OnPreAssert(const char* fileName, int line, const char* func, const char* message) override;
|
||||
bool OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message) override;
|
||||
bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override;
|
||||
bool OnAssert(const char* message) override;
|
||||
bool OnError(const char* window, const char* message) override;
|
||||
bool OnWarning(const char* window, const char* message) override;
|
||||
bool OnException(const char* message) override;
|
||||
bool OnPrintf(const char* window, const char* message) override;
|
||||
bool OnOutput(const char* window, const char* message) override;
|
||||
|
||||
// AZ::TickBus::Handler overrides ...
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
int GetTickOrder() override;
|
||||
|
||||
private:
|
||||
void QueueMessageCall(AZStd::function<void()> messageCall);
|
||||
void FlushMessageCalls();
|
||||
|
||||
AZStd::list<AZStd::function<void()>> m_messageCalls;
|
||||
AZStd::mutex m_messageCallsLock;
|
||||
};
|
||||
|
||||
TraceMessageBusHandler::TraceMessageBusHandler()
|
||||
{
|
||||
m_events.resize(FN_MAX);
|
||||
|
||||
SetEvent(&TraceMessageBusHandler::OnPreAssert, m_functionNames[FN_OnPreAssert]);
|
||||
SetEvent(&TraceMessageBusHandler::OnPreError, m_functionNames[FN_OnPreError]);
|
||||
SetEvent(&TraceMessageBusHandler::OnPreWarning, m_functionNames[FN_OnPreWarning]);
|
||||
SetEvent(&TraceMessageBusHandler::OnAssert, m_functionNames[FN_OnAssert]);
|
||||
SetEvent(&TraceMessageBusHandler::OnError, m_functionNames[FN_OnError]);
|
||||
SetEvent(&TraceMessageBusHandler::OnWarning, m_functionNames[FN_OnWarning]);
|
||||
SetEvent(&TraceMessageBusHandler::OnException, m_functionNames[FN_OnException]);
|
||||
SetEvent(&TraceMessageBusHandler::OnPrintf, m_functionNames[FN_OnPrintf]);
|
||||
SetEvent(&TraceMessageBusHandler::OnOutput, m_functionNames[FN_OnOutput]);
|
||||
}
|
||||
|
||||
int TraceMessageBusHandler::GetFunctionIndex(const char* functionName) const
|
||||
{
|
||||
for (int i = 0; i < FN_MAX; ++i)
|
||||
{
|
||||
for (int i = 0; i < FN_MAX; ++i)
|
||||
if (azstricmp(functionName, m_functionNames[i]) == 0)
|
||||
{
|
||||
if (azstricmp(functionName, m_functionNames[i]) == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TraceMessageBusHandler::Disconnect()
|
||||
void TraceMessageBusHandler::Disconnect()
|
||||
{
|
||||
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
bool TraceMessageBusHandler::Connect(AZ::BehaviorValueParameter* id)
|
||||
{
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
return AZ::Internal::EBusConnector<AZ::Debug::TraceMessageBus::Handler>::Connect(this, id);
|
||||
}
|
||||
|
||||
bool TraceMessageBusHandler::IsConnected()
|
||||
{
|
||||
return AZ::Internal::EBusConnector<AZ::Debug::TraceMessageBus::Handler>::IsConnected(this);
|
||||
}
|
||||
|
||||
bool TraceMessageBusHandler::IsConnectedId(AZ::BehaviorValueParameter* id)
|
||||
{
|
||||
return AZ::Internal::EBusConnector<AZ::Debug::TraceMessageBus::Handler>::IsConnectedId(this, id);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TraceMessageBusHandler Implementation
|
||||
inline bool TraceMessageBusHandler::OnPreAssert(const char* fileName, int line, const char* func, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, fileNameString = AZStd::string(fileName), line, funcString = AZStd::string(func), messageString = AZStd::string(message)]()
|
||||
{
|
||||
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
}
|
||||
Call(FN_OnPreAssert, fileNameString.c_str(), line, funcString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TraceMessageBusHandler::Connect(AZ::BehaviorValueParameter* id)
|
||||
inline bool TraceMessageBusHandler::OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), fileNameString = AZStd::string(fileName), line, funcString = AZStd::string(func), messageString = AZStd::string(message)]()
|
||||
{
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
return AZ::Internal::EBusConnector<AZ::Debug::TraceMessageBus::Handler>::Connect(this, id);
|
||||
}
|
||||
Call(FN_OnPreError, windowString.c_str(), fileNameString.c_str(), line, funcString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TraceMessageBusHandler::IsConnected()
|
||||
inline bool TraceMessageBusHandler::OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), fileNameString = AZStd::string(fileName), line, funcString = AZStd::string(func), messageString = AZStd::string(message)]()
|
||||
{
|
||||
return AZ::Internal::EBusConnector<AZ::Debug::TraceMessageBus::Handler>::IsConnected(this);
|
||||
}
|
||||
return Call(FN_OnPreWarning, windowString.c_str(), fileNameString.c_str(), line, funcString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TraceMessageBusHandler::IsConnectedId(AZ::BehaviorValueParameter* id)
|
||||
inline bool TraceMessageBusHandler::OnAssert(const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, messageString = AZStd::string(message)]()
|
||||
{
|
||||
return AZ::Internal::EBusConnector<AZ::Debug::TraceMessageBus::Handler>::IsConnectedId(this, id);
|
||||
}
|
||||
return Call(FN_OnAssert, messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TraceMessageBusHandler Implementation
|
||||
inline bool TraceMessageBusHandler::OnPreAssert(const char* fileName, int line, const char* func, const char* message)
|
||||
inline bool TraceMessageBusHandler::OnError(const char* window, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, fileNameString = AZStd::string(fileName), line, funcString = AZStd::string(func), messageString = AZStd::string(message)]()
|
||||
{
|
||||
Call(FN_OnPreAssert, fileNameString.c_str(), line, funcString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return Call(FN_OnError, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message)
|
||||
inline bool TraceMessageBusHandler::OnWarning(const char* window, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), fileNameString = AZStd::string(fileName), line, funcString = AZStd::string(func), messageString = AZStd::string(message)]()
|
||||
{
|
||||
Call(FN_OnPreError, windowString.c_str(), fileNameString.c_str(), line, funcString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return Call(FN_OnWarning, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message)
|
||||
inline bool TraceMessageBusHandler::OnException(const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, messageString = AZStd::string(message)]()
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), fileNameString = AZStd::string(fileName), line, funcString = AZStd::string(func), messageString = AZStd::string(message)]()
|
||||
{
|
||||
return Call(FN_OnPreWarning, windowString.c_str(), fileNameString.c_str(), line, funcString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return Call(FN_OnException, messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnAssert(const char* message)
|
||||
inline bool TraceMessageBusHandler::OnPrintf(const char* window, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, messageString = AZStd::string(message)]()
|
||||
{
|
||||
return Call(FN_OnAssert, messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return Call(FN_OnPrintf, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnError(const char* window, const char* message)
|
||||
inline bool TraceMessageBusHandler::OnOutput(const char* window, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
return Call(FN_OnError, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return Call(FN_OnOutput, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnWarning(const char* window, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
return Call(FN_OnWarning, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
void TraceMessageBusHandler::OnTick(
|
||||
[[maybe_unused]] float deltaTime,
|
||||
[[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
FlushMessageCalls();
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnException(const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, messageString = AZStd::string(message)]()
|
||||
{
|
||||
return Call(FN_OnException, messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
int TraceMessageBusHandler::GetTickOrder()
|
||||
{
|
||||
return AZ::TICK_LAST;
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnPrintf(const char* window, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
return Call(FN_OnPrintf, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
void TraceMessageBusHandler::QueueMessageCall(AZStd::function<void()> messageCall)
|
||||
{
|
||||
AZStd::lock_guard<decltype(m_messageCallsLock)> lock(m_messageCallsLock);
|
||||
m_messageCalls.emplace_back(messageCall);
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnOutput(const char* window, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
return Call(FN_OnOutput, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
void TraceMessageBusHandler::OnTick(
|
||||
[[maybe_unused]] float deltaTime,
|
||||
[[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
FlushMessageCalls();
|
||||
}
|
||||
|
||||
int TraceMessageBusHandler::GetTickOrder()
|
||||
{
|
||||
return AZ::TICK_LAST;
|
||||
}
|
||||
|
||||
void TraceMessageBusHandler::QueueMessageCall(AZStd::function<void()> messageCall)
|
||||
void TraceMessageBusHandler::FlushMessageCalls()
|
||||
{
|
||||
AZStd::list<AZStd::function<void()>> messageCalls;
|
||||
{
|
||||
AZStd::lock_guard<decltype(m_messageCallsLock)> lock(m_messageCallsLock);
|
||||
m_messageCalls.push_back(messageCall);
|
||||
m_messageCalls.swap(messageCalls); // Move calls to a new list to release the lock as soon as possible
|
||||
}
|
||||
|
||||
void TraceMessageBusHandler::FlushMessageCalls()
|
||||
for (auto& messageCall : messageCalls)
|
||||
{
|
||||
AZStd::list<AZStd::function<void()>> messageCalls;
|
||||
{
|
||||
AZStd::lock_guard<decltype(m_messageCallsLock)> lock(m_messageCallsLock);
|
||||
m_messageCalls.swap(messageCalls); // Move calls to a new list to release the lock as soon as possible
|
||||
}
|
||||
|
||||
for (auto& messageCall : messageCalls)
|
||||
{
|
||||
messageCall();
|
||||
}
|
||||
}
|
||||
|
||||
void TraceReflect(ReflectContext* context)
|
||||
{
|
||||
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->EBus<AZ::Debug::TraceMessageBus>("TraceMessageBus")
|
||||
->Attribute(AZ::Script::Attributes::Module, "debug")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Handler<TraceMessageBusHandler>()
|
||||
;
|
||||
}
|
||||
messageCall();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TraceReflect(ReflectContext* context)
|
||||
{
|
||||
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->EBus<AZ::Debug::TraceMessageBus>("TraceMessageBus")
|
||||
->Attribute(AZ::Script::Attributes::Module, "debug")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Handler<TraceMessageBusHandler>()
|
||||
;
|
||||
}
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
|
||||
@@ -14,291 +14,288 @@
|
||||
#include <AzCore/Math/Crc.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
class DrillerManagerImpl
|
||||
: public DrillerManager
|
||||
{
|
||||
class DrillerManagerImpl
|
||||
: public DrillerManager
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(DrillerManagerImpl, OSAllocator, 0);
|
||||
|
||||
using SessionListType = forward_list<DrillerSession>::type;
|
||||
SessionListType m_sessions;
|
||||
using DrillerArrayType = vector<Driller*>::type;
|
||||
DrillerArrayType m_drillers;
|
||||
|
||||
~DrillerManagerImpl() override;
|
||||
|
||||
void Register(Driller* factory) override;
|
||||
void Unregister(Driller* factory) override;
|
||||
|
||||
void FrameUpdate() override;
|
||||
|
||||
DrillerSession* Start(DrillerOutputStream& output, const DrillerListType& drillerList, int numFrames = -1) override;
|
||||
void Stop(DrillerSession* session) override;
|
||||
|
||||
int GetNumDrillers() const override { return static_cast<int>(m_drillers.size()); }
|
||||
Driller* GetDriller(int index) override { return m_drillers[index]; }
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Driller
|
||||
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
AZ::u32 Driller::GetId() const
|
||||
{
|
||||
return AZ::Crc32(GetName());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Driller Manager
|
||||
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
DrillerManager* DrillerManager::Create(/*const Descriptor& desc*/)
|
||||
{
|
||||
const bool createAllocator = !AZ::AllocatorInstance<OSAllocator>::IsReady();
|
||||
if (createAllocator)
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(DrillerManagerImpl, OSAllocator, 0);
|
||||
|
||||
typedef forward_list<DrillerSession>::type SessionListType;
|
||||
SessionListType m_sessions;
|
||||
typedef vector<Driller*>::type DrillerArrayType;
|
||||
DrillerArrayType m_drillers;
|
||||
|
||||
~DrillerManagerImpl() override;
|
||||
|
||||
void Register(Driller* factory) override;
|
||||
void Unregister(Driller* factory) override;
|
||||
|
||||
void FrameUpdate() override;
|
||||
|
||||
DrillerSession* Start(DrillerOutputStream& output, const DrillerListType& drillerList, int numFrames = -1) override;
|
||||
void Stop(DrillerSession* session) override;
|
||||
|
||||
int GetNumDrillers() const override { return static_cast<int>(m_drillers.size()); }
|
||||
Driller* GetDriller(int index) override { return m_drillers[index]; }
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Driller
|
||||
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
AZ::u32 Driller::GetId() const
|
||||
{
|
||||
return AZ::Crc32(GetName());
|
||||
AZ::AllocatorInstance<OSAllocator>::Create();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
DrillerManagerImpl* impl = aznew DrillerManagerImpl;
|
||||
impl->m_ownsOSAllocator = createAllocator;
|
||||
return impl;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Driller Manager
|
||||
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
DrillerManager* DrillerManager::Create(/*const Descriptor& desc*/)
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void DrillerManager::Destroy(DrillerManager* manager)
|
||||
{
|
||||
const bool allocatorCreated = manager->m_ownsOSAllocator;
|
||||
delete manager;
|
||||
if (allocatorCreated)
|
||||
{
|
||||
const bool createAllocator = !AZ::AllocatorInstance<OSAllocator>::IsReady();
|
||||
if (createAllocator)
|
||||
{
|
||||
AZ::AllocatorInstance<OSAllocator>::Create();
|
||||
}
|
||||
AZ::AllocatorInstance<OSAllocator>::Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
DrillerManagerImpl* impl = aznew DrillerManagerImpl;
|
||||
impl->m_ownsOSAllocator = createAllocator;
|
||||
return impl;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DrillerManagerImpl
|
||||
|
||||
//=========================================================================
|
||||
// ~DrillerManagerImpl
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
DrillerManagerImpl::~DrillerManagerImpl()
|
||||
{
|
||||
while (!m_sessions.empty())
|
||||
{
|
||||
Stop(&m_sessions.front());
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void DrillerManager::Destroy(DrillerManager* manager)
|
||||
while (!m_drillers.empty())
|
||||
{
|
||||
const bool allocatorCreated = manager->m_ownsOSAllocator;
|
||||
delete manager;
|
||||
if (allocatorCreated)
|
||||
{
|
||||
AZ::AllocatorInstance<OSAllocator>::Destroy();
|
||||
}
|
||||
Driller* driller = m_drillers[0];
|
||||
Unregister(driller);
|
||||
delete driller;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DrillerManagerImpl
|
||||
|
||||
//=========================================================================
|
||||
// ~DrillerManagerImpl
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
DrillerManagerImpl::~DrillerManagerImpl()
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::Register(Driller* driller)
|
||||
{
|
||||
AZ_Assert(driller, "You must provide a valid factory!");
|
||||
for (size_t i = 0; i < m_drillers.size(); ++i)
|
||||
{
|
||||
while (!m_sessions.empty())
|
||||
{
|
||||
Stop(&m_sessions.front());
|
||||
}
|
||||
|
||||
while (!m_drillers.empty())
|
||||
{
|
||||
Driller* driller = m_drillers[0];
|
||||
Unregister(driller);
|
||||
delete driller;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::Register(Driller* driller)
|
||||
{
|
||||
AZ_Assert(driller, "You must provide a valid factory!");
|
||||
for (size_t i = 0; i < m_drillers.size(); ++i)
|
||||
{
|
||||
if (m_drillers[i]->GetId() == driller->GetId())
|
||||
{
|
||||
AZ_Error("Debug", false, "Driller with id %08x has already been registered! You can't have two factory instances for the same driller type", driller->GetId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_drillers.push_back(driller);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Unregister
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::Unregister(Driller* driller)
|
||||
{
|
||||
AZ_Assert(driller, "You must provide a valid factory!");
|
||||
for (DrillerArrayType::iterator iter = m_drillers.begin(); iter != m_drillers.end(); ++iter)
|
||||
{
|
||||
if ((*iter)->GetId() == driller->GetId())
|
||||
{
|
||||
m_drillers.erase(iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AZ_Error("Debug", false, "Failed to find driller factory with id %08x", driller->GetId());
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FrameUpdate
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::FrameUpdate()
|
||||
{
|
||||
if (m_sessions.empty())
|
||||
if (m_drillers[i]->GetId() == driller->GetId())
|
||||
{
|
||||
AZ_Error("Debug", false, "Driller with id %08x has already been registered! You can't have two factory instances for the same driller type", driller->GetId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_drillers.push_back(driller);
|
||||
}
|
||||
|
||||
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex()); ///< Make sure no driller is writing to the stream
|
||||
for (SessionListType::iterator sessionIter = m_sessions.begin(); sessionIter != m_sessions.end(); )
|
||||
//=========================================================================
|
||||
// Unregister
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::Unregister(Driller* driller)
|
||||
{
|
||||
AZ_Assert(driller, "You must provide a valid factory!");
|
||||
for (DrillerArrayType::iterator iter = m_drillers.begin(); iter != m_drillers.end(); ++iter)
|
||||
{
|
||||
if ((*iter)->GetId() == driller->GetId())
|
||||
{
|
||||
DrillerSession& s = *sessionIter;
|
||||
|
||||
// tick the drillers directly if they care.
|
||||
for (size_t i = 0; i < s.drillers.size(); ++i)
|
||||
{
|
||||
s.drillers[i]->Update();
|
||||
}
|
||||
|
||||
s.output->EndTag(AZ_CRC("Frame", 0xb5f83ccd));
|
||||
|
||||
s.output->OnEndOfFrame();
|
||||
|
||||
s.curFrame++;
|
||||
|
||||
if (s.numFrames != -1)
|
||||
{
|
||||
if (s.curFrame == s.numFrames)
|
||||
{
|
||||
Stop(&s);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
s.output->BeginTag(AZ_CRC("Frame", 0xb5f83ccd));
|
||||
s.output->Write(AZ_CRC("FrameNum", 0x85a1a919), s.curFrame);
|
||||
|
||||
++sessionIter;
|
||||
m_drillers.erase(iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Start
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
DrillerSession*
|
||||
DrillerManagerImpl::Start(DrillerOutputStream& output, const DrillerListType& drillerList, int numFrames)
|
||||
AZ_Error("Debug", false, "Failed to find driller factory with id %08x", driller->GetId());
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FrameUpdate
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::FrameUpdate()
|
||||
{
|
||||
if (m_sessions.empty())
|
||||
{
|
||||
if (drillerList.empty())
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex()); ///< Make sure no driller is writing to the stream
|
||||
for (SessionListType::iterator sessionIter = m_sessions.begin(); sessionIter != m_sessions.end(); )
|
||||
{
|
||||
DrillerSession& s = *sessionIter;
|
||||
|
||||
// tick the drillers directly if they care.
|
||||
for (size_t i = 0; i < s.drillers.size(); ++i)
|
||||
{
|
||||
return nullptr;
|
||||
s.drillers[i]->Update();
|
||||
}
|
||||
|
||||
m_sessions.push_back();
|
||||
DrillerSession& s = m_sessions.back();
|
||||
s.curFrame = 0;
|
||||
s.numFrames = numFrames;
|
||||
s.output = &output;
|
||||
s.output->EndTag(AZ_CRC("Frame", 0xb5f83ccd));
|
||||
|
||||
s.output->WriteHeader(); // first write the header in the stream
|
||||
s.output->OnEndOfFrame();
|
||||
|
||||
s.output->BeginTag(AZ_CRC("StartData", 0xecf3f53f));
|
||||
s.output->Write(AZ_CRC("Platform", 0x3952d0cb), (unsigned int)g_currentPlatform);
|
||||
for (DrillerListType::const_iterator iDriller = drillerList.begin(); iDriller != drillerList.end(); ++iDriller)
|
||||
s.curFrame++;
|
||||
|
||||
if (s.numFrames != -1)
|
||||
{
|
||||
const DrillerInfo& di = *iDriller;
|
||||
s.output->BeginTag(AZ_CRC("Driller", 0xa6e1fb73));
|
||||
s.output->Write(AZ_CRC("Name", 0x5e237e06), di.id);
|
||||
for (int iParam = 0; iParam < (int)di.params.size(); ++iParam)
|
||||
if (s.curFrame == s.numFrames)
|
||||
{
|
||||
s.output->BeginTag(AZ_CRC("Param", 0xa4fa7c89));
|
||||
s.output->Write(AZ_CRC("Name", 0x5e237e06), di.params[iParam].name);
|
||||
s.output->Write(AZ_CRC("Description", 0x6de44026), di.params[iParam].desc);
|
||||
s.output->Write(AZ_CRC("Type", 0x8cde5729), di.params[iParam].type);
|
||||
s.output->Write(AZ_CRC("Value", 0x1d775834), di.params[iParam].value);
|
||||
s.output->EndTag(AZ_CRC("Param", 0xa4fa7c89));
|
||||
Stop(&s);
|
||||
continue;
|
||||
}
|
||||
s.output->EndTag(AZ_CRC("Driller", 0xa6e1fb73));
|
||||
}
|
||||
s.output->EndTag(AZ_CRC("StartData", 0xecf3f53f));
|
||||
|
||||
s.output->BeginTag(AZ_CRC("Frame", 0xb5f83ccd));
|
||||
s.output->Write(AZ_CRC("FrameNum", 0x85a1a919), s.curFrame);
|
||||
|
||||
{
|
||||
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex()); ///< Make sure no driller is writing to the stream
|
||||
for (DrillerListType::const_iterator iDriller = drillerList.begin(); iDriller != drillerList.end(); ++iDriller)
|
||||
{
|
||||
Driller* driller = nullptr;
|
||||
const DrillerInfo& di = *iDriller;
|
||||
for (size_t iDesc = 0; iDesc < m_drillers.size(); ++iDesc)
|
||||
{
|
||||
if (m_drillers[iDesc]->GetId() == di.id)
|
||||
{
|
||||
driller = m_drillers[iDesc];
|
||||
AZ_Assert(driller->m_output == nullptr, "Driller with id %08x is already have an output stream %p (currently we support only 1 at a time)", di.id, driller->m_output);
|
||||
driller->m_output = &output;
|
||||
driller->Start(di.params.data(), static_cast<unsigned int>(di.params.size()));
|
||||
s.drillers.push_back(driller);
|
||||
break;
|
||||
}
|
||||
}
|
||||
AZ_Warning("Driller", driller != nullptr, "We can't start a driller with id %d!", di.id);
|
||||
}
|
||||
}
|
||||
return &s;
|
||||
++sessionIter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// Stop
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::Stop(DrillerSession* session)
|
||||
//=========================================================================
|
||||
// Start
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
DrillerSession*
|
||||
DrillerManagerImpl::Start(DrillerOutputStream& output, const DrillerListType& drillerList, int numFrames)
|
||||
{
|
||||
if (drillerList.empty())
|
||||
{
|
||||
SessionListType::iterator iter;
|
||||
for (iter = m_sessions.begin(); iter != m_sessions.end(); ++iter)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_sessions.push_back();
|
||||
DrillerSession& s = m_sessions.back();
|
||||
s.curFrame = 0;
|
||||
s.numFrames = numFrames;
|
||||
s.output = &output;
|
||||
|
||||
s.output->WriteHeader(); // first write the header in the stream
|
||||
|
||||
s.output->BeginTag(AZ_CRC("StartData", 0xecf3f53f));
|
||||
s.output->Write(AZ_CRC("Platform", 0x3952d0cb), (unsigned int)g_currentPlatform);
|
||||
for (DrillerListType::const_iterator iDriller = drillerList.begin(); iDriller != drillerList.end(); ++iDriller)
|
||||
{
|
||||
const DrillerInfo& di = *iDriller;
|
||||
s.output->BeginTag(AZ_CRC("Driller", 0xa6e1fb73));
|
||||
s.output->Write(AZ_CRC("Name", 0x5e237e06), di.id);
|
||||
for (int iParam = 0; iParam < (int)di.params.size(); ++iParam)
|
||||
{
|
||||
if (&*iter == session)
|
||||
{
|
||||
break;
|
||||
}
|
||||
s.output->BeginTag(AZ_CRC("Param", 0xa4fa7c89));
|
||||
s.output->Write(AZ_CRC("Name", 0x5e237e06), di.params[iParam].name);
|
||||
s.output->Write(AZ_CRC("Description", 0x6de44026), di.params[iParam].desc);
|
||||
s.output->Write(AZ_CRC("Type", 0x8cde5729), di.params[iParam].type);
|
||||
s.output->Write(AZ_CRC("Value", 0x1d775834), di.params[iParam].value);
|
||||
s.output->EndTag(AZ_CRC("Param", 0xa4fa7c89));
|
||||
}
|
||||
s.output->EndTag(AZ_CRC("Driller", 0xa6e1fb73));
|
||||
}
|
||||
s.output->EndTag(AZ_CRC("StartData", 0xecf3f53f));
|
||||
|
||||
AZ_Assert(iter != m_sessions.end(), "We did not find session ID 0x%08x in the list!", session);
|
||||
if (iter != m_sessions.end())
|
||||
s.output->BeginTag(AZ_CRC("Frame", 0xb5f83ccd));
|
||||
s.output->Write(AZ_CRC("FrameNum", 0x85a1a919), s.curFrame);
|
||||
|
||||
{
|
||||
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex()); ///< Make sure no driller is writing to the stream
|
||||
for (DrillerListType::const_iterator iDriller = drillerList.begin(); iDriller != drillerList.end(); ++iDriller)
|
||||
{
|
||||
DrillerSession& s = *session;
|
||||
|
||||
Driller* driller = nullptr;
|
||||
const DrillerInfo& di = *iDriller;
|
||||
for (size_t iDesc = 0; iDesc < m_drillers.size(); ++iDesc)
|
||||
{
|
||||
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex());
|
||||
for (size_t i = 0; i < s.drillers.size(); ++i)
|
||||
if (m_drillers[iDesc]->GetId() == di.id)
|
||||
{
|
||||
s.drillers[i]->Stop();
|
||||
s.drillers[i]->m_output = nullptr;
|
||||
driller = m_drillers[iDesc];
|
||||
AZ_Assert(driller->m_output == nullptr, "Driller with id %08x is already have an output stream %p (currently we support only 1 at a time)", di.id, driller->m_output);
|
||||
driller->m_output = &output;
|
||||
driller->Start(di.params.data(), static_cast<unsigned int>(di.params.size()));
|
||||
s.drillers.push_back(driller);
|
||||
break;
|
||||
}
|
||||
}
|
||||
s.output->EndTag(AZ_CRC("Frame", 0xb5f83ccd));
|
||||
m_sessions.erase(iter);
|
||||
AZ_Warning("Driller", driller != nullptr, "We can't start a driller with id %d!", di.id);
|
||||
}
|
||||
}
|
||||
} // namespace Debug
|
||||
} // namespace AZ
|
||||
return &s;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// Stop
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::Stop(DrillerSession* session)
|
||||
{
|
||||
SessionListType::iterator iter;
|
||||
for (iter = m_sessions.begin(); iter != m_sessions.end(); ++iter)
|
||||
{
|
||||
if (&*iter == session)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AZ_Assert(iter != m_sessions.end(), "We did not find session ID 0x%08x in the list!", session);
|
||||
if (iter != m_sessions.end())
|
||||
{
|
||||
DrillerSession& s = *session;
|
||||
|
||||
{
|
||||
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex());
|
||||
for (size_t i = 0; i < s.drillers.size(); ++i)
|
||||
{
|
||||
s.drillers[i]->Stop();
|
||||
s.drillers[i]->m_output = nullptr;
|
||||
}
|
||||
}
|
||||
s.output->EndTag(AZ_CRC("Frame", 0xb5f83ccd));
|
||||
m_sessions.erase(iter);
|
||||
}
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
|
||||
@@ -12,57 +12,54 @@
|
||||
#include <AzCore/Module/Environment.h>
|
||||
#include <AzCore/std/parallel/mutex.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Globals
|
||||
// We need to synchronize all driller evens, so we have proper order, and access to the data
|
||||
// We use a global mutex which should be used for all driller operations.
|
||||
// The mutex is held in an environment variable so it works across DLLs.
|
||||
EnvironmentVariable<AZStd::recursive_mutex> s_drillerGlobalMutex;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// lock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
void DrillerEBusMutex::lock()
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Globals
|
||||
// We need to synchronize all driller evens, so we have proper order, and access to the data
|
||||
// We use a global mutex which should be used for all driller operations.
|
||||
// The mutex is held in an environment variable so it works across DLLs.
|
||||
EnvironmentVariable<AZStd::recursive_mutex> s_drillerGlobalMutex;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
GetMutex().lock();
|
||||
}
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// lock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
void DrillerEBusMutex::lock()
|
||||
{
|
||||
GetMutex().lock();
|
||||
}
|
||||
//=========================================================================
|
||||
// try_lock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
bool DrillerEBusMutex::try_lock()
|
||||
{
|
||||
return GetMutex().try_lock();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// try_lock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
bool DrillerEBusMutex::try_lock()
|
||||
{
|
||||
return GetMutex().try_lock();
|
||||
}
|
||||
//=========================================================================
|
||||
// unlock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
void DrillerEBusMutex::unlock()
|
||||
{
|
||||
GetMutex().unlock();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// unlock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
void DrillerEBusMutex::unlock()
|
||||
//=========================================================================
|
||||
// unlock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
AZStd::recursive_mutex& DrillerEBusMutex::GetMutex()
|
||||
{
|
||||
if (!s_drillerGlobalMutex)
|
||||
{
|
||||
GetMutex().unlock();
|
||||
s_drillerGlobalMutex = Environment::CreateVariable<AZStd::recursive_mutex>(AZ_FUNCTION_SIGNATURE);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// unlock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
AZStd::recursive_mutex& DrillerEBusMutex::GetMutex()
|
||||
{
|
||||
if (!s_drillerGlobalMutex)
|
||||
{
|
||||
s_drillerGlobalMutex = Environment::CreateVariable<AZStd::recursive_mutex>(AZ_FUNCTION_SIGNATURE);
|
||||
}
|
||||
return *s_drillerGlobalMutex;
|
||||
}
|
||||
} // namespace Debug
|
||||
} // namespace AZ
|
||||
return *s_drillerGlobalMutex;
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -60,7 +60,7 @@ namespace AZ
|
||||
|
||||
void EventSchedulerSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
TimeMs startTime = GetElapsedTimeMs();
|
||||
TimeMs startTime = AZ::GetElapsedTimeMs();
|
||||
bool usingTimeslice = bg_maxScheduledEventProcessTimeMs != TimeMs{ 0 };
|
||||
|
||||
while (!m_queue.empty())
|
||||
@@ -76,7 +76,7 @@ namespace AZ
|
||||
|
||||
while (!m_pendingQueue.empty())
|
||||
{
|
||||
if (usingTimeslice && (GetElapsedTimeMs() - startTime > bg_maxScheduledEventProcessTimeMs))
|
||||
if (usingTimeslice && (AZ::GetElapsedTimeMs() - startTime > bg_maxScheduledEventProcessTimeMs))
|
||||
{
|
||||
AZLOG_WARN("Failed to trigger all pending scheduled events, %u events remain on the pending queue", aznumeric_cast<uint32_t>(m_pendingQueue.size()));
|
||||
break;
|
||||
@@ -103,7 +103,7 @@ namespace AZ
|
||||
durationMs = TimeMs{ 0 };
|
||||
}
|
||||
|
||||
TimeMs currentMilliseconds = GetElapsedTimeMs();
|
||||
TimeMs currentMilliseconds = AZ::GetElapsedTimeMs();
|
||||
if (timedEvent->m_handle == nullptr)
|
||||
{
|
||||
timedEvent->m_handle = AllocateHandle();
|
||||
@@ -122,7 +122,7 @@ namespace AZ
|
||||
durationMs = TimeMs{ 0 };
|
||||
}
|
||||
|
||||
TimeMs currentMilliseconds = GetElapsedTimeMs();
|
||||
TimeMs currentMilliseconds = AZ::GetElapsedTimeMs();
|
||||
ScheduledEvent* timedEvent = AllocateManagedEvent(callback, eventName);
|
||||
const bool ownsScheduledEvent = true;
|
||||
*(timedEvent->m_handle) = ScheduledEventHandle(TimeMs(currentMilliseconds + durationMs), durationMs, timedEvent, ownsScheduledEvent);
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace AZ
|
||||
|
||||
TimeMs ScheduledEvent::TimeInQueueMs() const
|
||||
{
|
||||
return GetElapsedTimeMs() - m_timeInserted;
|
||||
return AZ::GetElapsedTimeMs() - m_timeInserted;
|
||||
}
|
||||
|
||||
TimeMs ScheduledEvent::RemainingTimeInQueueMs() const
|
||||
|
||||
@@ -8,38 +8,35 @@
|
||||
|
||||
#include <AzCore/IO/CompressionBus.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
CompressionInfo::CompressionInfo(CompressionInfo&& rhs)
|
||||
{
|
||||
CompressionInfo::CompressionInfo(CompressionInfo&& rhs)
|
||||
{
|
||||
*this = AZStd::move(rhs);
|
||||
}
|
||||
*this = AZStd::move(rhs);
|
||||
}
|
||||
|
||||
CompressionInfo& CompressionInfo::operator=(CompressionInfo&& rhs)
|
||||
{
|
||||
m_decompressor = AZStd::move(rhs.m_decompressor);
|
||||
m_archiveFilename = AZStd::move(rhs.m_archiveFilename);
|
||||
m_compressionTag = rhs.m_compressionTag;
|
||||
m_offset = rhs.m_offset;
|
||||
m_compressedSize = rhs.m_compressedSize;
|
||||
m_uncompressedSize = rhs.m_uncompressedSize;
|
||||
m_conflictResolution = rhs.m_conflictResolution;
|
||||
m_isCompressed = rhs.m_isCompressed;
|
||||
m_isSharedPak = rhs.m_isSharedPak;
|
||||
CompressionInfo& CompressionInfo::operator=(CompressionInfo&& rhs)
|
||||
{
|
||||
m_decompressor = AZStd::move(rhs.m_decompressor);
|
||||
m_archiveFilename = AZStd::move(rhs.m_archiveFilename);
|
||||
m_compressionTag = rhs.m_compressionTag;
|
||||
m_offset = rhs.m_offset;
|
||||
m_compressedSize = rhs.m_compressedSize;
|
||||
m_uncompressedSize = rhs.m_uncompressedSize;
|
||||
m_conflictResolution = rhs.m_conflictResolution;
|
||||
m_isCompressed = rhs.m_isCompressed;
|
||||
m_isSharedPak = rhs.m_isSharedPak;
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace CompressionUtils
|
||||
namespace CompressionUtils
|
||||
{
|
||||
bool FindCompressionInfo(CompressionInfo& info, const AZStd::string_view filename)
|
||||
{
|
||||
bool FindCompressionInfo(CompressionInfo& info, const AZStd::string_view filename)
|
||||
{
|
||||
bool result = false;
|
||||
CompressionBus::Broadcast(&CompressionBus::Events::FindCompressionInfo, result, info, filename);
|
||||
return result;
|
||||
}
|
||||
bool result = false;
|
||||
CompressionBus::Broadcast(&CompressionBus::Events::FindCompressionInfo, result, info, filename);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -10,32 +10,29 @@
|
||||
#include <AzCore/IO/Compressor.h>
|
||||
#include <AzCore/IO/CompressorStream.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
//=========================================================================
|
||||
// WriteHeaderAndData
|
||||
// [12/13/2012]
|
||||
//=========================================================================
|
||||
bool Compressor::WriteHeaderAndData(CompressorStream* compressorStream)
|
||||
{
|
||||
//=========================================================================
|
||||
// WriteHeaderAndData
|
||||
// [12/13/2012]
|
||||
//=========================================================================
|
||||
bool Compressor::WriteHeaderAndData(CompressorStream* compressorStream)
|
||||
AZ_Assert(compressorStream->CanWrite(), "Stream is not open for write!");
|
||||
AZ_Assert(compressorStream->GetCompressorData(), "Stream doesn't have attached compressor, call WriteCompressed first!");
|
||||
AZ_Assert(compressorStream->GetCompressorData()->m_compressor == this, "Invalid compressor data! Data belongs to a different compressor");
|
||||
CompressorHeader header;
|
||||
header.SetAZCS();
|
||||
header.m_compressorId = GetTypeId();
|
||||
header.m_uncompressedSize = compressorStream->GetCompressorData()->m_uncompressedSize;
|
||||
AZStd::endian_swap(header.m_compressorId);
|
||||
AZStd::endian_swap(header.m_uncompressedSize);
|
||||
GenericStream* baseStream = compressorStream->GetWrappedStream();
|
||||
if (baseStream->WriteAtOffset(sizeof(CompressorHeader), &header, 0U) == sizeof(CompressorHeader))
|
||||
{
|
||||
AZ_Assert(compressorStream->CanWrite(), "Stream is not open for write!");
|
||||
AZ_Assert(compressorStream->GetCompressorData(), "Stream doesn't have attached compressor, call WriteCompressed first!");
|
||||
AZ_Assert(compressorStream->GetCompressorData()->m_compressor == this, "Invalid compressor data! Data belongs to a different compressor");
|
||||
CompressorHeader header;
|
||||
header.SetAZCS();
|
||||
header.m_compressorId = GetTypeId();
|
||||
header.m_uncompressedSize = compressorStream->GetCompressorData()->m_uncompressedSize;
|
||||
AZStd::endian_swap(header.m_compressorId);
|
||||
AZStd::endian_swap(header.m_uncompressedSize);
|
||||
GenericStream* baseStream = compressorStream->GetWrappedStream();
|
||||
if (baseStream->WriteAtOffset(sizeof(CompressorHeader), &header, 0U) == sizeof(CompressorHeader))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -5,74 +5,67 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZCORE_IO_COMPRESSOR_H
|
||||
#define AZCORE_IO_COMPRESSOR_H
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
class CompressorStream;
|
||||
|
||||
/**
|
||||
* Compressor/Decompressor base interface.
|
||||
* Used for all stream compressors.
|
||||
*/
|
||||
class Compressor
|
||||
{
|
||||
class CompressorStream;
|
||||
public:
|
||||
typedef AZ::u64 SizeType;
|
||||
static const int m_maxHeaderSize = 4096; /// When we open a stream to check if it's compressed we read the first m_maxHeaderSize bytes.
|
||||
|
||||
/**
|
||||
* Compressor/Decompressor base interface.
|
||||
* Used for all stream compressors.
|
||||
*/
|
||||
class Compressor
|
||||
{
|
||||
public:
|
||||
typedef AZ::u64 SizeType;
|
||||
static const int m_maxHeaderSize = 4096; /// When we open a stream to check if it's compressed we read the first m_maxHeaderSize bytes.
|
||||
virtual ~Compressor() {}
|
||||
/// Return compressor type id.
|
||||
virtual AZ::u32 GetTypeId() const = 0;
|
||||
/// Called when we open a stream to Read for the first time. Data contains the first. dataSize <= m_maxHeaderSize.
|
||||
virtual bool ReadHeaderAndData(CompressorStream* stream, AZ::u8* data, unsigned int dataSize) = 0;
|
||||
/// Called when we are about to start writing to a compressed stream. (Must be called first to write compressor header)
|
||||
virtual bool WriteHeaderAndData(CompressorStream* stream);
|
||||
/// Forwarded function from the Device when we from a compressed stream.
|
||||
virtual SizeType Read(CompressorStream* stream, SizeType byteSize, SizeType offset, void* buffer) = 0;
|
||||
/// Forwarded function from the Device when we write to a compressed stream.
|
||||
virtual SizeType Write(CompressorStream* stream, SizeType byteSize, const void* data, SizeType offset = SizeType(-1)) = 0;
|
||||
/// Write a seek point.
|
||||
virtual bool WriteSeekPoint(CompressorStream* stream) { (void)stream; return false; }
|
||||
/// Initializes Compressor for writing data.
|
||||
virtual bool StartCompressor(CompressorStream* stream, int compressionLevel, SizeType autoSeekDataSize) { (void)stream; (void)compressionLevel; (void)autoSeekDataSize; return false; }
|
||||
/// Called just before we close the stream. All compression data will be flushed and finalized. (You can't add data afterwards).
|
||||
virtual bool Close(CompressorStream* stream) = 0;
|
||||
};
|
||||
|
||||
virtual ~Compressor() {}
|
||||
/// Return compressor type id.
|
||||
virtual AZ::u32 GetTypeId() const = 0;
|
||||
/// Called when we open a stream to Read for the first time. Data contains the first. dataSize <= m_maxHeaderSize.
|
||||
virtual bool ReadHeaderAndData(CompressorStream* stream, AZ::u8* data, unsigned int dataSize) = 0;
|
||||
/// Called when we are about to start writing to a compressed stream. (Must be called first to write compressor header)
|
||||
virtual bool WriteHeaderAndData(CompressorStream* stream);
|
||||
/// Forwarded function from the Device when we from a compressed stream.
|
||||
virtual SizeType Read(CompressorStream* stream, SizeType byteSize, SizeType offset, void* buffer) = 0;
|
||||
/// Forwarded function from the Device when we write to a compressed stream.
|
||||
virtual SizeType Write(CompressorStream* stream, SizeType byteSize, const void* data, SizeType offset = SizeType(-1)) = 0;
|
||||
/// Write a seek point.
|
||||
virtual bool WriteSeekPoint(CompressorStream* stream) { (void)stream; return false; }
|
||||
/// Initializes Compressor for writing data.
|
||||
virtual bool StartCompressor(CompressorStream* stream, int compressionLevel, SizeType autoSeekDataSize) { (void)stream; (void)compressionLevel; (void)autoSeekDataSize; return false; }
|
||||
/// Called just before we close the stream. All compression data will be flushed and finalized. (You can't add data afterwards).
|
||||
virtual bool Close(CompressorStream* stream) = 0;
|
||||
};
|
||||
/**
|
||||
* Base compressor data assigned for all compressors.
|
||||
*/
|
||||
class CompressorData
|
||||
{
|
||||
public:
|
||||
virtual ~CompressorData() {}
|
||||
|
||||
/**
|
||||
* Base compressor data assigned for all compressors.
|
||||
*/
|
||||
class CompressorData
|
||||
{
|
||||
public:
|
||||
virtual ~CompressorData() {}
|
||||
Compressor* m_compressor;
|
||||
AZ::u64 m_uncompressedSize;
|
||||
};
|
||||
|
||||
Compressor* m_compressor;
|
||||
AZ::u64 m_uncompressedSize;
|
||||
};
|
||||
/**
|
||||
* All data is stored in network order (big endian).
|
||||
*/
|
||||
struct CompressorHeader
|
||||
{
|
||||
CompressorHeader() { m_azcs[0] = 0; m_azcs[1] = 0; m_azcs[2] = 0; m_azcs[3] = 0; }
|
||||
|
||||
/**
|
||||
* All data is stored in network order (big endian).
|
||||
*/
|
||||
struct CompressorHeader
|
||||
{
|
||||
CompressorHeader() { m_azcs[0] = 0; m_azcs[1] = 0; m_azcs[2] = 0; m_azcs[3] = 0; }
|
||||
bool IsValid() const { return (m_azcs[0] == 'A' && m_azcs[1] == 'Z' && m_azcs[2] == 'C' && m_azcs[3] == 'S'); }
|
||||
void SetAZCS() { m_azcs[0] = 'A'; m_azcs[1] = 'Z'; m_azcs[2] = 'C'; m_azcs[3] = 'S'; }
|
||||
|
||||
inline bool IsValid() const { return (m_azcs[0] == 'A' && m_azcs[1] == 'Z' && m_azcs[2] == 'C' && m_azcs[3] == 'S'); }
|
||||
void SetAZCS() { m_azcs[0] = 'A'; m_azcs[1] = 'Z'; m_azcs[2] = 'C'; m_azcs[3] = 'S'; }
|
||||
|
||||
char m_azcs[4]; ///< String contains 'AZCS' AmaZon Compressed Stream
|
||||
AZ::u32 m_compressorId; ///< Compression method.
|
||||
AZ::u64 m_uncompressedSize; ///< Uncompressed file size.
|
||||
};
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
|
||||
#endif // AZCORE_IO_COMPRESSOR_H
|
||||
#pragma once
|
||||
char m_azcs[4]; ///< String contains 'AZCS' AmaZon Compressed Stream
|
||||
AZ::u32 m_compressorId; ///< Compression method.
|
||||
AZ::u64 m_uncompressedSize; ///< Uncompressed file size.
|
||||
};
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace IO
|
||||
namespace AZ::IO
|
||||
{
|
||||
/*!
|
||||
\brief Constructs a compressor stream using the supplied filename and OpenFlags to open a file on disk
|
||||
@@ -300,7 +298,4 @@ Compressor* CompressorStream::CreateCompressor(AZ::u32 compressorId)
|
||||
return m_compressor.get();
|
||||
}
|
||||
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
|
||||
|
||||
} // namespace AZ::IO
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,478 +14,475 @@
|
||||
#include <AzCore/Math/MathUtils.h>
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
CompressorZStd::CompressorZStd(unsigned int decompressionCachePerStream, unsigned int dataBufferSize)
|
||||
: m_compressedDataBufferSize(dataBufferSize)
|
||||
, m_decompressionCachePerStream(decompressionCachePerStream)
|
||||
|
||||
{
|
||||
CompressorZStd::CompressorZStd(unsigned int decompressionCachePerStream, unsigned int dataBufferSize)
|
||||
: m_compressedDataBufferSize(dataBufferSize)
|
||||
, m_decompressionCachePerStream(decompressionCachePerStream)
|
||||
AZ_Assert((dataBufferSize % (32 * 1024)) == 0, "Data buffer size %d must be multiple of 32 KB.", dataBufferSize);
|
||||
AZ_Assert((decompressionCachePerStream % (32 * 1024)) == 0, "Decompress cache size %d must be multiple of 32 KB.", decompressionCachePerStream);
|
||||
}
|
||||
|
||||
CompressorZStd::~CompressorZStd()
|
||||
{
|
||||
AZ_Warning("IO", m_compressedDataBufferUseCount == 0, "CompressorZStd has it's data buffer still referenced, it means that %d compressed streams have NOT closed. Freeing data...", m_compressedDataBufferUseCount);
|
||||
while (m_compressedDataBufferUseCount)
|
||||
{
|
||||
AZ_Assert((dataBufferSize % (32 * 1024)) == 0, "Data buffer size %d must be multiple of 32 KB.", dataBufferSize);
|
||||
AZ_Assert((decompressionCachePerStream % (32 * 1024)) == 0, "Decompress cache size %d must be multiple of 32 KB.", decompressionCachePerStream);
|
||||
ReleaseDataBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
CompressorZStd::~CompressorZStd()
|
||||
AZ::u32 CompressorZStd::TypeId()
|
||||
{
|
||||
return AZ_CRC("ZStd", 0x72fd505e);
|
||||
}
|
||||
|
||||
bool CompressorZStd::ReadHeaderAndData(CompressorStream* stream, AZ::u8* data, unsigned int dataSize)
|
||||
{
|
||||
if (stream->GetCompressorData() != nullptr) // we already have compressor data
|
||||
{
|
||||
AZ_Warning("IO", m_compressedDataBufferUseCount == 0, "CompressorZStd has it's data buffer still referenced, it means that %d compressed streams have NOT closed. Freeing data...", m_compressedDataBufferUseCount);
|
||||
while (m_compressedDataBufferUseCount)
|
||||
{
|
||||
ReleaseDataBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
AZ::u32 CompressorZStd::TypeId()
|
||||
{
|
||||
return AZ_CRC("ZStd", 0x72fd505e);
|
||||
}
|
||||
|
||||
bool CompressorZStd::ReadHeaderAndData(CompressorStream* stream, AZ::u8* data, unsigned int dataSize)
|
||||
{
|
||||
if (stream->GetCompressorData() != nullptr) // we already have compressor data
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the ZStd header should be after the default compression header...
|
||||
// We should not be in this function otherwise.
|
||||
if (dataSize < sizeof(CompressorZStdHeader) + sizeof(ZStd::Header))
|
||||
{
|
||||
AZ_Error("CompressorZStd", false, "We did not read enough data, we have only %d bytes left in the buffer and we need %d.", dataSize, sizeof(CompressorZStdHeader) + sizeof(ZStd::Header));
|
||||
return false;
|
||||
}
|
||||
|
||||
AcquireDataBuffer();
|
||||
|
||||
CompressorZStdHeader* hdr = reinterpret_cast<CompressorZStdHeader*>(data);
|
||||
dataSize -= sizeof(CompressorZStdHeader);
|
||||
data += sizeof(CompressorZStdHeader);
|
||||
|
||||
AZStd::unique_ptr<CompressorZStdData> zstdData = AZStd::make_unique<CompressorZStdData>();
|
||||
zstdData->m_compressor = this;
|
||||
zstdData->m_uncompressedSize = 0;
|
||||
zstdData->m_zstdHeader = *reinterpret_cast<ZStd::Header*>(data);
|
||||
dataSize -= sizeof(zstdData->m_zstdHeader);
|
||||
data += sizeof(zstdData->m_zstdHeader);
|
||||
zstdData->m_decompressNextOffset = sizeof(CompressorHeader) + sizeof(CompressorZStdHeader) + sizeof(ZStd::Header); // start after the headers
|
||||
|
||||
AZ_Error("CompressorZStd", hdr->m_numSeekPoints > 0, "We should have at least one seek point for the entire stream.");
|
||||
|
||||
// go the end of the file and read all sync points.
|
||||
SizeType compressedFileEnd = stream->GetLength();
|
||||
if (compressedFileEnd == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
zstdData->m_seekPoints.resize(hdr->m_numSeekPoints);
|
||||
SizeType dataToRead = sizeof(CompressorZStdSeekPoint) * static_cast<SizeType>(hdr->m_numSeekPoints);
|
||||
SizeType seekPointOffset = compressedFileEnd - dataToRead;
|
||||
|
||||
if (seekPointOffset > compressedFileEnd)
|
||||
{
|
||||
AZ_Error("CompressorZStd", false, "We have an invalid archive, this is impossible.");
|
||||
return false;
|
||||
}
|
||||
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
if (baseStream->ReadAtOffset(dataToRead, zstdData->m_seekPoints.data(), seekPointOffset) != dataToRead)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_decompressionCachePerStream)
|
||||
{
|
||||
zstdData->m_decompressedCache = reinterpret_cast<unsigned char*>(azmalloc(m_decompressionCachePerStream, m_CompressedDataBufferAlignment, AZ::SystemAllocator, "CompressorZStd"));
|
||||
}
|
||||
|
||||
zstdData->m_decompressLastOffset = seekPointOffset; // set the start address of the seek points as the last valid read address for the compressed stream.
|
||||
|
||||
zstdData->m_zstd.StartDecompressor();
|
||||
|
||||
stream->SetCompressorData(zstdData.release());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompressorZStd::WriteHeaderAndData(CompressorStream* stream)
|
||||
{
|
||||
if (!Compressor::WriteHeaderAndData(stream))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CompressorZStdData* compressorData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
CompressorZStdHeader header;
|
||||
header.m_numSeekPoints = aznumeric_caster(compressorData->m_seekPoints.size());
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
if (baseStream->WriteAtOffset(sizeof(header), &header, sizeof(CompressorHeader)) == sizeof(header))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline CompressorZStd::SizeType CompressorZStd::FillFromDecompressCache(CompressorZStdData* zstdData, void*& buffer, SizeType& byteSize, SizeType& offset)
|
||||
// Read the ZStd header should be after the default compression header...
|
||||
// We should not be in this function otherwise.
|
||||
if (dataSize < sizeof(CompressorZStdHeader) + sizeof(ZStd::Header))
|
||||
{
|
||||
SizeType firstOffsetInCache = zstdData->m_decompressedCacheOffset;
|
||||
SizeType lastOffsetInCache = firstOffsetInCache + zstdData->m_decompressedCacheDataSize;
|
||||
SizeType firstDataOffset = offset;
|
||||
SizeType lastDataOffset = offset + byteSize;
|
||||
SizeType numCopied = 0;
|
||||
if (firstOffsetInCache < lastDataOffset && lastOffsetInCache >= firstDataOffset) // check if there is data in the cache
|
||||
{
|
||||
size_t copyOffsetStart = 0;
|
||||
size_t copyOffsetEnd = zstdData->m_decompressedCacheDataSize;
|
||||
|
||||
size_t bufferCopyOffset = 0;
|
||||
|
||||
if (firstOffsetInCache < firstDataOffset)
|
||||
{
|
||||
copyOffsetStart = aznumeric_caster(firstDataOffset - firstOffsetInCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
bufferCopyOffset = aznumeric_caster(firstOffsetInCache - firstDataOffset);
|
||||
}
|
||||
|
||||
if (lastOffsetInCache >= lastDataOffset)
|
||||
{
|
||||
copyOffsetEnd -= static_cast<size_t>(lastOffsetInCache - lastDataOffset);
|
||||
}
|
||||
else if (bufferCopyOffset > 0) // the cache block is in the middle of the data, we can't use it (since we need to split buffer request into 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
numCopied = copyOffsetEnd - copyOffsetStart;
|
||||
memcpy(static_cast<char*>(buffer) + bufferCopyOffset, zstdData->m_decompressedCache + copyOffsetStart, static_cast<size_t>(numCopied));
|
||||
|
||||
// adjust pointers and sizes
|
||||
byteSize -= numCopied;
|
||||
if (bufferCopyOffset == 0)
|
||||
{
|
||||
// copied in the start
|
||||
buffer = reinterpret_cast<char*>(buffer) + numCopied;
|
||||
offset += numCopied;
|
||||
}
|
||||
}
|
||||
|
||||
return numCopied;
|
||||
AZ_Error("CompressorZStd", false, "We did not read enough data, we have only %d bytes left in the buffer and we need %d.", dataSize, sizeof(CompressorZStdHeader) + sizeof(ZStd::Header));
|
||||
return false;
|
||||
}
|
||||
|
||||
inline CompressorZStd::SizeType CompressorZStd::FillCompressedBuffer(CompressorStream* stream)
|
||||
AcquireDataBuffer();
|
||||
|
||||
CompressorZStdHeader* hdr = reinterpret_cast<CompressorZStdHeader*>(data);
|
||||
dataSize -= sizeof(CompressorZStdHeader);
|
||||
data += sizeof(CompressorZStdHeader);
|
||||
|
||||
AZStd::unique_ptr<CompressorZStdData> zstdData = AZStd::make_unique<CompressorZStdData>();
|
||||
zstdData->m_compressor = this;
|
||||
zstdData->m_uncompressedSize = 0;
|
||||
zstdData->m_zstdHeader = *reinterpret_cast<ZStd::Header*>(data);
|
||||
dataSize -= sizeof(zstdData->m_zstdHeader);
|
||||
data += sizeof(zstdData->m_zstdHeader);
|
||||
zstdData->m_decompressNextOffset = sizeof(CompressorHeader) + sizeof(CompressorZStdHeader) + sizeof(ZStd::Header); // start after the headers
|
||||
|
||||
AZ_Error("CompressorZStd", hdr->m_numSeekPoints > 0, "We should have at least one seek point for the entire stream.");
|
||||
|
||||
// go the end of the file and read all sync points.
|
||||
SizeType compressedFileEnd = stream->GetLength();
|
||||
if (compressedFileEnd == 0)
|
||||
{
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
SizeType dataFromBuffer = 0;
|
||||
if (stream == m_lastReadStream) // if the buffer is filled with data from the current stream, try to reuse
|
||||
{
|
||||
if (zstdData->m_decompressNextOffset > m_lastReadStreamOffset)
|
||||
{
|
||||
SizeType offsetInCache = zstdData->m_decompressNextOffset - m_lastReadStreamOffset;
|
||||
if (offsetInCache < m_lastReadStreamSize) // last check if there is data overlap
|
||||
{
|
||||
// copy the usable part at the start of the buffer
|
||||
SizeType toMove = m_lastReadStreamSize - offsetInCache;
|
||||
memcpy(m_compressedDataBuffer, &m_compressedDataBuffer[static_cast<size_t>(offsetInCache)], static_cast<size_t>(toMove));
|
||||
dataFromBuffer += toMove;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SizeType toReadFromStream = m_compressedDataBufferSize - dataFromBuffer;
|
||||
SizeType readOffset = zstdData->m_decompressNextOffset + dataFromBuffer;
|
||||
if (readOffset + toReadFromStream > zstdData->m_decompressLastOffset)
|
||||
{
|
||||
// don't read past the end
|
||||
AZ_Assert(readOffset <= zstdData->m_decompressLastOffset, "Read offset should always be before the end of stream.");
|
||||
toReadFromStream = zstdData->m_decompressLastOffset - readOffset;
|
||||
}
|
||||
|
||||
SizeType numReadFromStream = 0;
|
||||
if (toReadFromStream) // if we did not reuse the whole buffer, read some data from the stream
|
||||
{
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
numReadFromStream = baseStream->ReadAtOffset(toReadFromStream, &m_compressedDataBuffer[static_cast<size_t>(dataFromBuffer)], readOffset);
|
||||
}
|
||||
|
||||
// update what's actually in the read data buffer.
|
||||
m_lastReadStream = stream;
|
||||
m_lastReadStreamOffset = zstdData->m_decompressNextOffset;
|
||||
m_lastReadStreamSize = dataFromBuffer + numReadFromStream;
|
||||
return m_lastReadStreamSize;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ZStdCompareUpper
|
||||
zstdData->m_seekPoints.resize(hdr->m_numSeekPoints);
|
||||
SizeType dataToRead = sizeof(CompressorZStdSeekPoint) * static_cast<SizeType>(hdr->m_numSeekPoints);
|
||||
SizeType seekPointOffset = compressedFileEnd - dataToRead;
|
||||
|
||||
if (seekPointOffset > compressedFileEnd)
|
||||
{
|
||||
bool operator()(const AZ::u64& offset, const CompressorZStdSeekPoint& sp) const
|
||||
{
|
||||
return offset < sp.m_uncompressedOffset;
|
||||
}
|
||||
};
|
||||
AZ_Error("CompressorZStd", false, "We have an invalid archive, this is impossible.");
|
||||
return false;
|
||||
}
|
||||
|
||||
CompressorZStd::SizeType CompressorZStd::Read(CompressorStream* stream, SizeType byteSize, SizeType offset, void* buffer)
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
if (baseStream->ReadAtOffset(dataToRead, zstdData->m_seekPoints.data(), seekPointOffset) != dataToRead)
|
||||
{
|
||||
AZ_Assert(stream->GetCompressorData(), "This stream doesn't have decompression enabled.");
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
AZ_Assert(!zstdData->m_zstd.IsCompressorStarted(), "You can't read/decompress while writing a compressed stream %s.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if the request can be finished from the decompressed cache
|
||||
SizeType numRead = FillFromDecompressCache(zstdData, buffer, byteSize, offset);
|
||||
if (byteSize == 0) // are we done
|
||||
if (m_decompressionCachePerStream)
|
||||
{
|
||||
zstdData->m_decompressedCache = reinterpret_cast<unsigned char*>(azmalloc(m_decompressionCachePerStream, m_CompressedDataBufferAlignment, AZ::SystemAllocator, "CompressorZStd"));
|
||||
}
|
||||
|
||||
zstdData->m_decompressLastOffset = seekPointOffset; // set the start address of the seek points as the last valid read address for the compressed stream.
|
||||
|
||||
zstdData->m_zstd.StartDecompressor();
|
||||
|
||||
stream->SetCompressorData(zstdData.release());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompressorZStd::WriteHeaderAndData(CompressorStream* stream)
|
||||
{
|
||||
if (!Compressor::WriteHeaderAndData(stream))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CompressorZStdData* compressorData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
CompressorZStdHeader header;
|
||||
header.m_numSeekPoints = aznumeric_caster(compressorData->m_seekPoints.size());
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
if (baseStream->WriteAtOffset(sizeof(header), &header, sizeof(CompressorHeader)) == sizeof(header))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline CompressorZStd::SizeType CompressorZStd::FillFromDecompressCache(CompressorZStdData* zstdData, void*& buffer, SizeType& byteSize, SizeType& offset)
|
||||
{
|
||||
SizeType firstOffsetInCache = zstdData->m_decompressedCacheOffset;
|
||||
SizeType lastOffsetInCache = firstOffsetInCache + zstdData->m_decompressedCacheDataSize;
|
||||
SizeType firstDataOffset = offset;
|
||||
SizeType lastDataOffset = offset + byteSize;
|
||||
SizeType numCopied = 0;
|
||||
if (firstOffsetInCache < lastDataOffset && lastOffsetInCache >= firstDataOffset) // check if there is data in the cache
|
||||
{
|
||||
size_t copyOffsetStart = 0;
|
||||
size_t copyOffsetEnd = zstdData->m_decompressedCacheDataSize;
|
||||
|
||||
size_t bufferCopyOffset = 0;
|
||||
|
||||
if (firstOffsetInCache < firstDataOffset)
|
||||
{
|
||||
return numRead;
|
||||
copyOffsetStart = aznumeric_caster(firstDataOffset - firstOffsetInCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
bufferCopyOffset = aznumeric_caster(firstOffsetInCache - firstDataOffset);
|
||||
}
|
||||
|
||||
// find the best seek point for current offset
|
||||
CompressorZStdData::SeekPointArray::iterator it = AZStd::upper_bound(zstdData->m_seekPoints.begin(), zstdData->m_seekPoints.end(), offset, ZStdCompareUpper());
|
||||
AZ_Assert(it != zstdData->m_seekPoints.begin(), "This should be impossible, we should always have a valid seek point at 0 offset.");
|
||||
const CompressorZStdSeekPoint& bestSeekPoint = *(--it); // get the previous (so it includes the current offset)
|
||||
|
||||
// if read is continuous continue with decompression
|
||||
bool isJumpToSeekPoint = false;
|
||||
SizeType lastOffsetInCache = zstdData->m_decompressedCacheOffset + zstdData->m_decompressedCacheDataSize;
|
||||
if (bestSeekPoint.m_uncompressedOffset > lastOffsetInCache) // if the best seek point is forward, jump forward to it.
|
||||
if (lastOffsetInCache >= lastDataOffset)
|
||||
{
|
||||
isJumpToSeekPoint = true;
|
||||
copyOffsetEnd -= static_cast<size_t>(lastOffsetInCache - lastDataOffset);
|
||||
}
|
||||
else if (offset < lastOffsetInCache) // if the seek point is in the past and the requested offset is not in the cache jump back to it.
|
||||
else if (bufferCopyOffset > 0) // the cache block is in the middle of the data, we can't use it (since we need to split buffer request into 2)
|
||||
{
|
||||
isJumpToSeekPoint = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (isJumpToSeekPoint)
|
||||
{
|
||||
zstdData->m_decompressNextOffset = bestSeekPoint.m_compressedOffset; // set next read point
|
||||
zstdData->m_decompressedCacheOffset = bestSeekPoint.m_uncompressedOffset; // set uncompressed offset
|
||||
zstdData->m_decompressedCacheDataSize = 0; // invalidate the cache
|
||||
zstdData->m_zstd.ResetDecompressor(&zstdData->m_zstdHeader); // reset decompressor and setup the header.
|
||||
}
|
||||
numCopied = copyOffsetEnd - copyOffsetStart;
|
||||
memcpy(static_cast<char*>(buffer) + bufferCopyOffset, zstdData->m_decompressedCache + copyOffsetStart, static_cast<size_t>(numCopied));
|
||||
|
||||
// decompress and move forward until the request is done
|
||||
while (byteSize > 0)
|
||||
// adjust pointers and sizes
|
||||
byteSize -= numCopied;
|
||||
if (bufferCopyOffset == 0)
|
||||
{
|
||||
// fill buffer with compressed data
|
||||
SizeType compressedDataSize = FillCompressedBuffer(stream);
|
||||
if (compressedDataSize == 0)
|
||||
// copied in the start
|
||||
buffer = reinterpret_cast<char*>(buffer) + numCopied;
|
||||
offset += numCopied;
|
||||
}
|
||||
}
|
||||
|
||||
return numCopied;
|
||||
}
|
||||
|
||||
inline CompressorZStd::SizeType CompressorZStd::FillCompressedBuffer(CompressorStream* stream)
|
||||
{
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
SizeType dataFromBuffer = 0;
|
||||
if (stream == m_lastReadStream) // if the buffer is filled with data from the current stream, try to reuse
|
||||
{
|
||||
if (zstdData->m_decompressNextOffset > m_lastReadStreamOffset)
|
||||
{
|
||||
SizeType offsetInCache = zstdData->m_decompressNextOffset - m_lastReadStreamOffset;
|
||||
if (offsetInCache < m_lastReadStreamSize) // last check if there is data overlap
|
||||
{
|
||||
return numRead; // we are done reading and obviously we did not managed to read all data
|
||||
// copy the usable part at the start of the buffer
|
||||
SizeType toMove = m_lastReadStreamSize - offsetInCache;
|
||||
memcpy(m_compressedDataBuffer, &m_compressedDataBuffer[static_cast<size_t>(offsetInCache)], static_cast<size_t>(toMove));
|
||||
dataFromBuffer += toMove;
|
||||
}
|
||||
unsigned int processedCompressedData = 0;
|
||||
while (byteSize > 0 && processedCompressedData < compressedDataSize) // decompressed data either until we are done with the request (byteSize == 0) or we need to fill the compression buffer again.
|
||||
{
|
||||
// if we have data in the cache move to the next offset, we always move forward by default.
|
||||
zstdData->m_decompressedCacheOffset += zstdData->m_decompressedCacheDataSize;
|
||||
|
||||
// decompress in the cache buffer
|
||||
u32 availDecompressedCacheSize = m_decompressionCachePerStream; // reset buffer size
|
||||
size_t nextBlockSize;
|
||||
unsigned int processed = zstdData->m_zstd.Decompress(&m_compressedDataBuffer[processedCompressedData],
|
||||
static_cast<unsigned int>(compressedDataSize) - processedCompressedData,
|
||||
zstdData->m_decompressedCache,
|
||||
availDecompressedCacheSize,
|
||||
&nextBlockSize);
|
||||
zstdData->m_decompressedCacheDataSize = m_decompressionCachePerStream - availDecompressedCacheSize;
|
||||
if (processed == 0)
|
||||
{
|
||||
break; // we processed everything we could, load more compressed data.
|
||||
}
|
||||
processedCompressedData += processed;
|
||||
// fill what we can from the cache
|
||||
numRead += FillFromDecompressCache(zstdData, buffer, byteSize, offset);
|
||||
}
|
||||
// update next read position the the compressed stream
|
||||
zstdData->m_decompressNextOffset += processedCompressedData;
|
||||
}
|
||||
}
|
||||
|
||||
SizeType toReadFromStream = m_compressedDataBufferSize - dataFromBuffer;
|
||||
SizeType readOffset = zstdData->m_decompressNextOffset + dataFromBuffer;
|
||||
if (readOffset + toReadFromStream > zstdData->m_decompressLastOffset)
|
||||
{
|
||||
// don't read past the end
|
||||
AZ_Assert(readOffset <= zstdData->m_decompressLastOffset, "Read offset should always be before the end of stream.");
|
||||
toReadFromStream = zstdData->m_decompressLastOffset - readOffset;
|
||||
}
|
||||
|
||||
SizeType numReadFromStream = 0;
|
||||
if (toReadFromStream) // if we did not reuse the whole buffer, read some data from the stream
|
||||
{
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
numReadFromStream = baseStream->ReadAtOffset(toReadFromStream, &m_compressedDataBuffer[static_cast<size_t>(dataFromBuffer)], readOffset);
|
||||
}
|
||||
|
||||
// update what's actually in the read data buffer.
|
||||
m_lastReadStream = stream;
|
||||
m_lastReadStreamOffset = zstdData->m_decompressNextOffset;
|
||||
m_lastReadStreamSize = dataFromBuffer + numReadFromStream;
|
||||
return m_lastReadStreamSize;
|
||||
}
|
||||
|
||||
struct ZStdCompareUpper
|
||||
{
|
||||
bool operator()(const AZ::u64& offset, const CompressorZStdSeekPoint& sp) const
|
||||
{
|
||||
return offset < sp.m_uncompressedOffset;
|
||||
}
|
||||
};
|
||||
|
||||
CompressorZStd::SizeType CompressorZStd::Read(CompressorStream* stream, SizeType byteSize, SizeType offset, void* buffer)
|
||||
{
|
||||
AZ_Assert(stream->GetCompressorData(), "This stream doesn't have decompression enabled.");
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
AZ_Assert(!zstdData->m_zstd.IsCompressorStarted(), "You can't read/decompress while writing a compressed stream %s.");
|
||||
|
||||
// check if the request can be finished from the decompressed cache
|
||||
SizeType numRead = FillFromDecompressCache(zstdData, buffer, byteSize, offset);
|
||||
if (byteSize == 0) // are we done
|
||||
{
|
||||
return numRead;
|
||||
}
|
||||
|
||||
CompressorZStd::SizeType CompressorZStd::Write(CompressorStream* stream, SizeType byteSize, const void* data, SizeType offset)
|
||||
// find the best seek point for current offset
|
||||
CompressorZStdData::SeekPointArray::iterator it = AZStd::upper_bound(zstdData->m_seekPoints.begin(), zstdData->m_seekPoints.end(), offset, ZStdCompareUpper());
|
||||
AZ_Assert(it != zstdData->m_seekPoints.begin(), "This should be impossible, we should always have a valid seek point at 0 offset.");
|
||||
const CompressorZStdSeekPoint& bestSeekPoint = *(--it); // get the previous (so it includes the current offset)
|
||||
|
||||
// if read is continuous continue with decompression
|
||||
bool isJumpToSeekPoint = false;
|
||||
SizeType lastOffsetInCache = zstdData->m_decompressedCacheOffset + zstdData->m_decompressedCacheDataSize;
|
||||
if (bestSeekPoint.m_uncompressedOffset > lastOffsetInCache) // if the best seek point is forward, jump forward to it.
|
||||
{
|
||||
AZ_UNUSED(offset);
|
||||
isJumpToSeekPoint = true;
|
||||
}
|
||||
else if (offset < lastOffsetInCache) // if the seek point is in the past and the requested offset is not in the cache jump back to it.
|
||||
{
|
||||
isJumpToSeekPoint = true;
|
||||
}
|
||||
|
||||
AZ_Assert(stream && stream->GetCompressorData(), "This stream doesn't have compression enabled. Call Stream::WriteCompressed after you create the file.");
|
||||
AZ_Assert(offset == SizeType(-1) || offset == stream->GetCurPos(), "We can write compressed data only at the end of the stream.");
|
||||
if (isJumpToSeekPoint)
|
||||
{
|
||||
zstdData->m_decompressNextOffset = bestSeekPoint.m_compressedOffset; // set next read point
|
||||
zstdData->m_decompressedCacheOffset = bestSeekPoint.m_uncompressedOffset; // set uncompressed offset
|
||||
zstdData->m_decompressedCacheDataSize = 0; // invalidate the cache
|
||||
zstdData->m_zstd.ResetDecompressor(&zstdData->m_zstdHeader); // reset decompressor and setup the header.
|
||||
}
|
||||
|
||||
m_lastReadStream = nullptr; // invalidate last read position, otherwise m_dataBuffer will be corrupted (as we are about to write in it).
|
||||
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
AZ_Assert(!zstdData->m_zstd.IsDecompressorStarted(), "You can't write while reading/decompressing a compressed stream.");
|
||||
|
||||
const u8* bytes = reinterpret_cast<const u8*>(data);
|
||||
unsigned int dataToCompress = aznumeric_caster(byteSize);
|
||||
while (dataToCompress != 0)
|
||||
// decompress and move forward until the request is done
|
||||
while (byteSize > 0)
|
||||
{
|
||||
// fill buffer with compressed data
|
||||
SizeType compressedDataSize = FillCompressedBuffer(stream);
|
||||
if (compressedDataSize == 0)
|
||||
{
|
||||
unsigned int oldDataToCompress = dataToCompress;
|
||||
unsigned int compressedSize = zstdData->m_zstd.Compress(bytes, dataToCompress, m_compressedDataBuffer, m_compressedDataBufferSize);
|
||||
if (compressedSize)
|
||||
{
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
SizeType numWritten = baseStream->Write(compressedSize, m_compressedDataBuffer);
|
||||
if (numWritten != compressedSize)
|
||||
{
|
||||
return numWritten; // error we could not write all data
|
||||
}
|
||||
}
|
||||
bytes += oldDataToCompress - dataToCompress;
|
||||
return numRead; // we are done reading and obviously we did not managed to read all data
|
||||
}
|
||||
zstdData->m_uncompressedSize += byteSize;
|
||||
|
||||
if (zstdData->m_autoSeekSize > 0)
|
||||
unsigned int processedCompressedData = 0;
|
||||
while (byteSize > 0 && processedCompressedData < compressedDataSize) // decompressed data either until we are done with the request (byteSize == 0) or we need to fill the compression buffer again.
|
||||
{
|
||||
// insert a seek point if needed.
|
||||
if (zstdData->m_seekPoints.empty())
|
||||
// if we have data in the cache move to the next offset, we always move forward by default.
|
||||
zstdData->m_decompressedCacheOffset += zstdData->m_decompressedCacheDataSize;
|
||||
|
||||
// decompress in the cache buffer
|
||||
u32 availDecompressedCacheSize = m_decompressionCachePerStream; // reset buffer size
|
||||
size_t nextBlockSize;
|
||||
unsigned int processed = zstdData->m_zstd.Decompress(&m_compressedDataBuffer[processedCompressedData],
|
||||
static_cast<unsigned int>(compressedDataSize) - processedCompressedData,
|
||||
zstdData->m_decompressedCache,
|
||||
availDecompressedCacheSize,
|
||||
&nextBlockSize);
|
||||
zstdData->m_decompressedCacheDataSize = m_decompressionCachePerStream - availDecompressedCacheSize;
|
||||
if (processed == 0)
|
||||
{
|
||||
if (zstdData->m_uncompressedSize >= zstdData->m_autoSeekSize)
|
||||
{
|
||||
WriteSeekPoint(stream);
|
||||
}
|
||||
break; // we processed everything we could, load more compressed data.
|
||||
}
|
||||
else if ((zstdData->m_uncompressedSize - zstdData->m_seekPoints.back().m_uncompressedOffset) > zstdData->m_autoSeekSize)
|
||||
processedCompressedData += processed;
|
||||
// fill what we can from the cache
|
||||
numRead += FillFromDecompressCache(zstdData, buffer, byteSize, offset);
|
||||
}
|
||||
// update next read position the the compressed stream
|
||||
zstdData->m_decompressNextOffset += processedCompressedData;
|
||||
}
|
||||
return numRead;
|
||||
}
|
||||
|
||||
CompressorZStd::SizeType CompressorZStd::Write(CompressorStream* stream, SizeType byteSize, const void* data, SizeType offset)
|
||||
{
|
||||
AZ_UNUSED(offset);
|
||||
|
||||
AZ_Assert(stream && stream->GetCompressorData(), "This stream doesn't have compression enabled. Call Stream::WriteCompressed after you create the file.");
|
||||
AZ_Assert(offset == SizeType(-1) || offset == stream->GetCurPos(), "We can write compressed data only at the end of the stream.");
|
||||
|
||||
m_lastReadStream = nullptr; // invalidate last read position, otherwise m_dataBuffer will be corrupted (as we are about to write in it).
|
||||
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
AZ_Assert(!zstdData->m_zstd.IsDecompressorStarted(), "You can't write while reading/decompressing a compressed stream.");
|
||||
|
||||
const u8* bytes = reinterpret_cast<const u8*>(data);
|
||||
unsigned int dataToCompress = aznumeric_caster(byteSize);
|
||||
while (dataToCompress != 0)
|
||||
{
|
||||
unsigned int oldDataToCompress = dataToCompress;
|
||||
unsigned int compressedSize = zstdData->m_zstd.Compress(bytes, dataToCompress, m_compressedDataBuffer, m_compressedDataBufferSize);
|
||||
if (compressedSize)
|
||||
{
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
SizeType numWritten = baseStream->Write(compressedSize, m_compressedDataBuffer);
|
||||
if (numWritten != compressedSize)
|
||||
{
|
||||
return numWritten; // error we could not write all data
|
||||
}
|
||||
}
|
||||
bytes += oldDataToCompress - dataToCompress;
|
||||
}
|
||||
zstdData->m_uncompressedSize += byteSize;
|
||||
|
||||
if (zstdData->m_autoSeekSize > 0)
|
||||
{
|
||||
// insert a seek point if needed.
|
||||
if (zstdData->m_seekPoints.empty())
|
||||
{
|
||||
if (zstdData->m_uncompressedSize >= zstdData->m_autoSeekSize)
|
||||
{
|
||||
WriteSeekPoint(stream);
|
||||
}
|
||||
}
|
||||
return byteSize;
|
||||
else if ((zstdData->m_uncompressedSize - zstdData->m_seekPoints.back().m_uncompressedOffset) > zstdData->m_autoSeekSize)
|
||||
{
|
||||
WriteSeekPoint(stream);
|
||||
}
|
||||
}
|
||||
return byteSize;
|
||||
}
|
||||
|
||||
bool CompressorZStd::WriteSeekPoint(CompressorStream* stream)
|
||||
bool CompressorZStd::WriteSeekPoint(CompressorStream* stream)
|
||||
{
|
||||
AZ_Assert(stream && stream->GetCompressorData(), "This stream doesn't have compression enabled. Call Stream::WriteCompressed after you create the file.");
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
|
||||
m_lastReadStream = nullptr; // invalidate last read position, otherwise m_dataBuffer will be corrupted (as we are about to write in it).
|
||||
|
||||
unsigned int compressedSize;
|
||||
unsigned int dataToCompress = 0;
|
||||
do
|
||||
{
|
||||
AZ_Assert(stream && stream->GetCompressorData(), "This stream doesn't have compression enabled. Call Stream::WriteCompressed after you create the file.");
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
compressedSize = zstdData->m_zstd.Compress(nullptr, dataToCompress, m_compressedDataBuffer, m_compressedDataBufferSize, ZStd::FT_FULL_FLUSH);
|
||||
if (compressedSize)
|
||||
{
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
SizeType numWritten = baseStream->Write(compressedSize, m_compressedDataBuffer);
|
||||
if (numWritten != compressedSize)
|
||||
{
|
||||
return false; // error we wrote less than than requested!
|
||||
}
|
||||
}
|
||||
} while (dataToCompress != 0);
|
||||
|
||||
CompressorZStdSeekPoint sp;
|
||||
sp.m_compressedOffset = stream->GetLength();
|
||||
sp.m_uncompressedOffset = zstdData->m_uncompressedSize;
|
||||
zstdData->m_seekPoints.push_back(sp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompressorZStd::StartCompressor(CompressorStream* stream, int compressionLevel, SizeType autoSeekDataSize)
|
||||
{
|
||||
AZ_Assert(stream && !stream->GetCompressorData(), "Stream has compressor already enabled.");
|
||||
|
||||
AcquireDataBuffer();
|
||||
|
||||
CompressorZStdData* zstdData = aznew CompressorZStdData;
|
||||
zstdData->m_compressor = this;
|
||||
zstdData->m_zstdHeader = 0; // not used for compression
|
||||
zstdData->m_uncompressedSize = 0;
|
||||
zstdData->m_autoSeekSize = autoSeekDataSize;
|
||||
compressionLevel = AZ::GetClamp(compressionLevel, 1, 9); // remap to zlib levels
|
||||
|
||||
zstdData->m_zstd.StartCompressor(compressionLevel);
|
||||
|
||||
stream->SetCompressorData(zstdData);
|
||||
|
||||
if (WriteHeaderAndData(stream))
|
||||
{
|
||||
// add the first and always present seek point at the start of the compressed stream
|
||||
CompressorZStdSeekPoint sp;
|
||||
sp.m_compressedOffset = sizeof(CompressorHeader) + sizeof(CompressorZStdHeader) + sizeof(zstdData->m_zstdHeader);
|
||||
sp.m_uncompressedOffset = 0;
|
||||
zstdData->m_seekPoints.push_back(sp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CompressorZStd::Close(CompressorStream* stream)
|
||||
{
|
||||
AZ_Assert(stream->IsOpen(), "Stream is not open to be closed.");
|
||||
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
|
||||
bool result = true;
|
||||
if (zstdData->m_zstd.IsCompressorStarted())
|
||||
{
|
||||
m_lastReadStream = nullptr; // invalidate last read position, otherwise m_dataBuffer will be corrupted (as we are about to write in it).
|
||||
|
||||
// flush all compressed data
|
||||
unsigned int compressedSize;
|
||||
unsigned int dataToCompress = 0;
|
||||
do
|
||||
{
|
||||
compressedSize = zstdData->m_zstd.Compress(nullptr, dataToCompress, m_compressedDataBuffer, m_compressedDataBufferSize, ZStd::FT_FULL_FLUSH);
|
||||
compressedSize = zstdData->m_zstd.Compress(nullptr, dataToCompress, m_compressedDataBuffer, m_compressedDataBufferSize, ZStd::FT_FINISH);
|
||||
if (compressedSize)
|
||||
{
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
SizeType numWritten = baseStream->Write(compressedSize, m_compressedDataBuffer);
|
||||
if (numWritten != compressedSize)
|
||||
{
|
||||
return false; // error we wrote less than than requested!
|
||||
}
|
||||
baseStream->Write(compressedSize, m_compressedDataBuffer);
|
||||
}
|
||||
} while (dataToCompress != 0);
|
||||
|
||||
CompressorZStdSeekPoint sp;
|
||||
sp.m_compressedOffset = stream->GetLength();
|
||||
sp.m_uncompressedOffset = zstdData->m_uncompressedSize;
|
||||
zstdData->m_seekPoints.push_back(sp);
|
||||
return true;
|
||||
result = WriteHeaderAndData(stream);
|
||||
if (result)
|
||||
{
|
||||
SizeType dataToWrite = zstdData->m_seekPoints.size() * sizeof(CompressorZStdSeekPoint);
|
||||
baseStream->Seek(0U, GenericStream::SeekMode::ST_SEEK_END);
|
||||
result = (baseStream->Write(dataToWrite, zstdData->m_seekPoints.data()) == dataToWrite);
|
||||
}
|
||||
}
|
||||
|
||||
bool CompressorZStd::StartCompressor(CompressorStream* stream, int compressionLevel, SizeType autoSeekDataSize)
|
||||
else
|
||||
{
|
||||
AZ_Assert(stream && !stream->GetCompressorData(), "Stream has compressor already enabled.");
|
||||
|
||||
AcquireDataBuffer();
|
||||
|
||||
CompressorZStdData* zstdData = aznew CompressorZStdData;
|
||||
zstdData->m_compressor = this;
|
||||
zstdData->m_zstdHeader = 0; // not used for compression
|
||||
zstdData->m_uncompressedSize = 0;
|
||||
zstdData->m_autoSeekSize = autoSeekDataSize;
|
||||
compressionLevel = AZ::GetClamp(compressionLevel, 1, 9); // remap to zlib levels
|
||||
|
||||
zstdData->m_zstd.StartCompressor(compressionLevel);
|
||||
|
||||
stream->SetCompressorData(zstdData);
|
||||
|
||||
if (WriteHeaderAndData(stream))
|
||||
if (m_lastReadStream == stream)
|
||||
{
|
||||
// add the first and always present seek point at the start of the compressed stream
|
||||
CompressorZStdSeekPoint sp;
|
||||
sp.m_compressedOffset = sizeof(CompressorHeader) + sizeof(CompressorZStdHeader) + sizeof(zstdData->m_zstdHeader);
|
||||
sp.m_uncompressedOffset = 0;
|
||||
zstdData->m_seekPoints.push_back(sp);
|
||||
return true;
|
||||
m_lastReadStream = nullptr; // invalidate the data in m_dataBuffer if it was from the current stream.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CompressorZStd::Close(CompressorStream* stream)
|
||||
// if we have decompressor cache delete it
|
||||
if (zstdData->m_decompressedCache)
|
||||
{
|
||||
AZ_Assert(stream->IsOpen(), "Stream is not open to be closed.");
|
||||
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
|
||||
bool result = true;
|
||||
if (zstdData->m_zstd.IsCompressorStarted())
|
||||
{
|
||||
m_lastReadStream = nullptr; // invalidate last read position, otherwise m_dataBuffer will be corrupted (as we are about to write in it).
|
||||
|
||||
// flush all compressed data
|
||||
unsigned int compressedSize;
|
||||
unsigned int dataToCompress = 0;
|
||||
do
|
||||
{
|
||||
compressedSize = zstdData->m_zstd.Compress(nullptr, dataToCompress, m_compressedDataBuffer, m_compressedDataBufferSize, ZStd::FT_FINISH);
|
||||
if (compressedSize)
|
||||
{
|
||||
baseStream->Write(compressedSize, m_compressedDataBuffer);
|
||||
}
|
||||
} while (dataToCompress != 0);
|
||||
|
||||
result = WriteHeaderAndData(stream);
|
||||
if (result)
|
||||
{
|
||||
SizeType dataToWrite = zstdData->m_seekPoints.size() * sizeof(CompressorZStdSeekPoint);
|
||||
baseStream->Seek(0U, GenericStream::SeekMode::ST_SEEK_END);
|
||||
result = (baseStream->Write(dataToWrite, zstdData->m_seekPoints.data()) == dataToWrite);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_lastReadStream == stream)
|
||||
{
|
||||
m_lastReadStream = nullptr; // invalidate the data in m_dataBuffer if it was from the current stream.
|
||||
}
|
||||
}
|
||||
|
||||
// if we have decompressor cache delete it
|
||||
if (zstdData->m_decompressedCache)
|
||||
{
|
||||
azfree(zstdData->m_decompressedCache, AZ::SystemAllocator, m_decompressionCachePerStream, m_CompressedDataBufferAlignment);
|
||||
}
|
||||
|
||||
ReleaseDataBuffer();
|
||||
|
||||
// last step reset strream compressor data.
|
||||
stream->SetCompressorData(nullptr);
|
||||
return result;
|
||||
azfree(zstdData->m_decompressedCache, AZ::SystemAllocator, m_decompressionCachePerStream, m_CompressedDataBufferAlignment);
|
||||
}
|
||||
|
||||
void CompressorZStd::AcquireDataBuffer()
|
||||
ReleaseDataBuffer();
|
||||
|
||||
// last step reset strream compressor data.
|
||||
stream->SetCompressorData(nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
void CompressorZStd::AcquireDataBuffer()
|
||||
{
|
||||
if (m_compressedDataBuffer == nullptr)
|
||||
{
|
||||
if (m_compressedDataBuffer == nullptr)
|
||||
{
|
||||
AZ_Assert(m_compressedDataBufferUseCount == 0, "Buffer usecount should be 0 if the buffer is NULL");
|
||||
m_compressedDataBuffer = reinterpret_cast<unsigned char*>(azmalloc(m_compressedDataBufferSize, m_CompressedDataBufferAlignment, AZ::SystemAllocator, "CompressorZStd"));
|
||||
m_lastReadStream = nullptr; // reset the cache info in the m_dataBuffer
|
||||
}
|
||||
++m_compressedDataBufferUseCount;
|
||||
AZ_Assert(m_compressedDataBufferUseCount == 0, "Buffer usecount should be 0 if the buffer is NULL");
|
||||
m_compressedDataBuffer = reinterpret_cast<unsigned char*>(azmalloc(m_compressedDataBufferSize, m_CompressedDataBufferAlignment, AZ::SystemAllocator, "CompressorZStd"));
|
||||
m_lastReadStream = nullptr; // reset the cache info in the m_dataBuffer
|
||||
}
|
||||
++m_compressedDataBufferUseCount;
|
||||
}
|
||||
|
||||
void CompressorZStd::ReleaseDataBuffer()
|
||||
void CompressorZStd::ReleaseDataBuffer()
|
||||
{
|
||||
--m_compressedDataBufferUseCount;
|
||||
if (m_compressedDataBufferUseCount == 0)
|
||||
{
|
||||
--m_compressedDataBufferUseCount;
|
||||
if (m_compressedDataBufferUseCount == 0)
|
||||
{
|
||||
AZ_Assert(m_compressedDataBuffer, "Invalid data buffer. We should have a non null pointer.");
|
||||
azfree(m_compressedDataBuffer, AZ::SystemAllocator, m_compressedDataBufferSize, m_CompressedDataBufferAlignment);
|
||||
m_compressedDataBuffer = nullptr;
|
||||
m_lastReadStream = nullptr; // reset the cache info in the m_dataBuffer
|
||||
}
|
||||
AZ_Assert(m_compressedDataBuffer, "Invalid data buffer. We should have a non null pointer.");
|
||||
azfree(m_compressedDataBuffer, AZ::SystemAllocator, m_compressedDataBufferSize, m_CompressedDataBufferAlignment);
|
||||
m_compressedDataBuffer = nullptr;
|
||||
m_lastReadStream = nullptr; // reset the cache info in the m_dataBuffer
|
||||
}
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
#endif // #if !defined(AZCORE_EXCLUDE_ZSTD)
|
||||
|
||||
@@ -21,485 +21,482 @@
|
||||
# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
|
||||
#endif
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
static EnvironmentVariable<FileIOBase*> g_fileIOInstance;
|
||||
static EnvironmentVariable<FileIOBase*> g_directFileIOInstance;
|
||||
static const char* s_EngineFileIOName = "EngineFileIO";
|
||||
static const char* s_DirectFileIOName = "DirectFileIO";
|
||||
|
||||
FileIOBase* FileIOBase::GetInstance()
|
||||
{
|
||||
static EnvironmentVariable<FileIOBase*> g_fileIOInstance;
|
||||
static EnvironmentVariable<FileIOBase*> g_directFileIOInstance;
|
||||
static const char* s_EngineFileIOName = "EngineFileIO";
|
||||
static const char* s_DirectFileIOName = "DirectFileIO";
|
||||
|
||||
FileIOBase* FileIOBase::GetInstance()
|
||||
if (!g_fileIOInstance)
|
||||
{
|
||||
if (!g_fileIOInstance)
|
||||
{
|
||||
g_fileIOInstance = Environment::FindVariable<FileIOBase*>(s_EngineFileIOName);
|
||||
}
|
||||
|
||||
return g_fileIOInstance ? (*g_fileIOInstance) : nullptr;
|
||||
g_fileIOInstance = Environment::FindVariable<FileIOBase*>(s_EngineFileIOName);
|
||||
}
|
||||
|
||||
void FileIOBase::SetInstance(FileIOBase* instance)
|
||||
return g_fileIOInstance ? (*g_fileIOInstance) : nullptr;
|
||||
}
|
||||
|
||||
void FileIOBase::SetInstance(FileIOBase* instance)
|
||||
{
|
||||
if (!g_fileIOInstance)
|
||||
{
|
||||
if (!g_fileIOInstance)
|
||||
{
|
||||
g_fileIOInstance = Environment::CreateVariable<FileIOBase*>(s_EngineFileIOName);
|
||||
(*g_fileIOInstance) = nullptr;
|
||||
}
|
||||
|
||||
// at this point we're guaranteed to have g_fileIOInstance. Its value might be null.
|
||||
|
||||
if ((instance) && (g_fileIOInstance) && (*g_fileIOInstance))
|
||||
{
|
||||
AZ_Error("FileIO", false, "FileIOBase::SetInstance was called without first destroying the old instance and setting it to nullptr");
|
||||
}
|
||||
|
||||
(*g_fileIOInstance) = instance;
|
||||
g_fileIOInstance = Environment::CreateVariable<FileIOBase*>(s_EngineFileIOName);
|
||||
(*g_fileIOInstance) = nullptr;
|
||||
}
|
||||
|
||||
FileIOBase* FileIOBase::GetDirectInstance()
|
||||
// at this point we're guaranteed to have g_fileIOInstance. Its value might be null.
|
||||
|
||||
if ((instance) && (g_fileIOInstance) && (*g_fileIOInstance))
|
||||
{
|
||||
if (!g_directFileIOInstance)
|
||||
{
|
||||
g_directFileIOInstance = Environment::FindVariable<FileIOBase*>(s_DirectFileIOName);
|
||||
}
|
||||
|
||||
// for backwards compatibilty, return the regular instance if this is not attached
|
||||
if (!g_directFileIOInstance)
|
||||
{
|
||||
return GetInstance();
|
||||
}
|
||||
|
||||
return g_directFileIOInstance ? (*g_directFileIOInstance) : nullptr;
|
||||
AZ_Error("FileIO", false, "FileIOBase::SetInstance was called without first destroying the old instance and setting it to nullptr");
|
||||
}
|
||||
|
||||
void FileIOBase::SetDirectInstance(FileIOBase* instance)
|
||||
(*g_fileIOInstance) = instance;
|
||||
}
|
||||
|
||||
FileIOBase* FileIOBase::GetDirectInstance()
|
||||
{
|
||||
if (!g_directFileIOInstance)
|
||||
{
|
||||
if (!g_directFileIOInstance)
|
||||
{
|
||||
g_directFileIOInstance = Environment::CreateVariable<FileIOBase*>(s_DirectFileIOName);
|
||||
(*g_directFileIOInstance) = nullptr;
|
||||
}
|
||||
|
||||
// at this point we're guaranteed to have g_directFileIOInstance. Its value might be null.
|
||||
|
||||
if ((instance) && (g_directFileIOInstance) && (*g_directFileIOInstance))
|
||||
{
|
||||
AZ_Error("FileIO", false, "FileIOBase::SetDirectInstance was called without first destroying the old instance and setting it to nullptr");
|
||||
}
|
||||
|
||||
(*g_directFileIOInstance) = instance;
|
||||
g_directFileIOInstance = Environment::FindVariable<FileIOBase*>(s_DirectFileIOName);
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPath> FileIOBase::ConvertToAlias(const AZ::IO::PathView& path) const
|
||||
// for backwards compatibilty, return the regular instance if this is not attached
|
||||
if (!g_directFileIOInstance)
|
||||
{
|
||||
AZ::IO::FixedMaxPath convertedPath;
|
||||
if (ConvertToAlias(convertedPath, path))
|
||||
{
|
||||
return convertedPath;
|
||||
}
|
||||
|
||||
return AZStd::nullopt;
|
||||
return GetInstance();
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPath> FileIOBase::ResolvePath(const AZ::IO::PathView& path) const
|
||||
{
|
||||
AZ::IO::FixedMaxPath resolvedPath;
|
||||
if (ResolvePath(resolvedPath, path))
|
||||
{
|
||||
return resolvedPath;
|
||||
}
|
||||
return g_directFileIOInstance ? (*g_directFileIOInstance) : nullptr;
|
||||
}
|
||||
|
||||
return AZStd::nullopt;
|
||||
void FileIOBase::SetDirectInstance(FileIOBase* instance)
|
||||
{
|
||||
if (!g_directFileIOInstance)
|
||||
{
|
||||
g_directFileIOInstance = Environment::CreateVariable<FileIOBase*>(s_DirectFileIOName);
|
||||
(*g_directFileIOInstance) = nullptr;
|
||||
}
|
||||
|
||||
SeekType GetSeekTypeFromFSeekMode(int mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case SEEK_SET:
|
||||
return SeekType::SeekFromStart;
|
||||
case SEEK_CUR:
|
||||
return SeekType::SeekFromCurrent;
|
||||
case SEEK_END:
|
||||
return SeekType::SeekFromEnd;
|
||||
}
|
||||
// at this point we're guaranteed to have g_directFileIOInstance. Its value might be null.
|
||||
|
||||
// Must have some default, hitting here means some random int mode
|
||||
if ((instance) && (g_directFileIOInstance) && (*g_directFileIOInstance))
|
||||
{
|
||||
AZ_Error("FileIO", false, "FileIOBase::SetDirectInstance was called without first destroying the old instance and setting it to nullptr");
|
||||
}
|
||||
|
||||
(*g_directFileIOInstance) = instance;
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPath> FileIOBase::ConvertToAlias(const AZ::IO::PathView& path) const
|
||||
{
|
||||
AZ::IO::FixedMaxPath convertedPath;
|
||||
if (ConvertToAlias(convertedPath, path))
|
||||
{
|
||||
return convertedPath;
|
||||
}
|
||||
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPath> FileIOBase::ResolvePath(const AZ::IO::PathView& path) const
|
||||
{
|
||||
AZ::IO::FixedMaxPath resolvedPath;
|
||||
if (ResolvePath(resolvedPath, path))
|
||||
{
|
||||
return resolvedPath;
|
||||
}
|
||||
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
SeekType GetSeekTypeFromFSeekMode(int mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case SEEK_SET:
|
||||
return SeekType::SeekFromStart;
|
||||
case SEEK_CUR:
|
||||
return SeekType::SeekFromCurrent;
|
||||
case SEEK_END:
|
||||
return SeekType::SeekFromEnd;
|
||||
}
|
||||
|
||||
int GetFSeekModeFromSeekType(SeekType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SeekType::SeekFromStart:
|
||||
return SEEK_SET;
|
||||
case SeekType::SeekFromCurrent:
|
||||
return SEEK_CUR;
|
||||
case SeekType::SeekFromEnd:
|
||||
return SEEK_END;
|
||||
}
|
||||
// Must have some default, hitting here means some random int mode
|
||||
return SeekType::SeekFromStart;
|
||||
}
|
||||
|
||||
int GetFSeekModeFromSeekType(SeekType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SeekType::SeekFromStart:
|
||||
return SEEK_SET;
|
||||
case SeekType::SeekFromCurrent:
|
||||
return SEEK_CUR;
|
||||
case SeekType::SeekFromEnd:
|
||||
return SEEK_END;
|
||||
}
|
||||
|
||||
void UpdateOpenModeForReading(OpenMode& openMode)
|
||||
return SEEK_SET;
|
||||
}
|
||||
|
||||
void UpdateOpenModeForReading(OpenMode& openMode)
|
||||
{
|
||||
if (AnyFlag(openMode & OpenMode::ModeRead))
|
||||
{
|
||||
if (AnyFlag(openMode & OpenMode::ModeRead))
|
||||
if (AnyFlag(openMode & OpenMode::ModeText))
|
||||
{
|
||||
if (AnyFlag(openMode & OpenMode::ModeText))
|
||||
{
|
||||
OpenMode extraModes = openMode & (OpenMode::ModeUpdate | OpenMode::ModeAppend);
|
||||
openMode = OpenMode::ModeRead | OpenMode::ModeBinary | extraModes;
|
||||
}
|
||||
else if (!AnyFlag(openMode & OpenMode::ModeBinary))
|
||||
{
|
||||
// if you haven't supplied any flag, supply binary
|
||||
openMode = openMode | OpenMode::ModeBinary;
|
||||
}
|
||||
OpenMode extraModes = openMode & (OpenMode::ModeUpdate | OpenMode::ModeAppend);
|
||||
openMode = OpenMode::ModeRead | OpenMode::ModeBinary | extraModes;
|
||||
}
|
||||
else if (!AnyFlag(openMode & OpenMode::ModeBinary))
|
||||
{
|
||||
// if you haven't supplied any flag, supply binary
|
||||
openMode = openMode | OpenMode::ModeBinary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OpenMode GetOpenModeFromStringMode(const char* mode)
|
||||
OpenMode GetOpenModeFromStringMode(const char* mode)
|
||||
{
|
||||
OpenMode openMode = OpenMode::Invalid;
|
||||
|
||||
if (strstr(mode, "w"))
|
||||
{
|
||||
OpenMode openMode = OpenMode::Invalid;
|
||||
|
||||
if (strstr(mode, "w"))
|
||||
{
|
||||
openMode |= OpenMode::ModeWrite;
|
||||
}
|
||||
|
||||
if (strstr(mode, "r"))
|
||||
{
|
||||
openMode |= OpenMode::ModeRead;
|
||||
}
|
||||
|
||||
if (strstr(mode, "a"))
|
||||
{
|
||||
openMode |= OpenMode::ModeAppend;
|
||||
}
|
||||
|
||||
if (strstr(mode, "b"))
|
||||
{
|
||||
openMode |= OpenMode::ModeBinary;
|
||||
}
|
||||
|
||||
if (strstr(mode, "t"))
|
||||
{
|
||||
openMode |= OpenMode::ModeText;
|
||||
}
|
||||
|
||||
if (strstr(mode, "+"))
|
||||
{
|
||||
openMode |= OpenMode::ModeUpdate;
|
||||
}
|
||||
|
||||
UpdateOpenModeForReading(openMode);
|
||||
|
||||
return openMode;
|
||||
openMode |= OpenMode::ModeWrite;
|
||||
}
|
||||
|
||||
const char* GetStringModeFromOpenMode(OpenMode mode)
|
||||
if (strstr(mode, "r"))
|
||||
{
|
||||
UpdateOpenModeForReading(mode);
|
||||
// Append is highest priority, followed by write and then read
|
||||
// APPEND
|
||||
if (AnyFlag(mode & OpenMode::ModeAppend))
|
||||
openMode |= OpenMode::ModeRead;
|
||||
}
|
||||
|
||||
if (strstr(mode, "a"))
|
||||
{
|
||||
openMode |= OpenMode::ModeAppend;
|
||||
}
|
||||
|
||||
if (strstr(mode, "b"))
|
||||
{
|
||||
openMode |= OpenMode::ModeBinary;
|
||||
}
|
||||
|
||||
if (strstr(mode, "t"))
|
||||
{
|
||||
openMode |= OpenMode::ModeText;
|
||||
}
|
||||
|
||||
if (strstr(mode, "+"))
|
||||
{
|
||||
openMode |= OpenMode::ModeUpdate;
|
||||
}
|
||||
|
||||
UpdateOpenModeForReading(openMode);
|
||||
|
||||
return openMode;
|
||||
}
|
||||
|
||||
const char* GetStringModeFromOpenMode(OpenMode mode)
|
||||
{
|
||||
UpdateOpenModeForReading(mode);
|
||||
// Append is highest priority, followed by write and then read
|
||||
// APPEND
|
||||
if (AnyFlag(mode & OpenMode::ModeAppend))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeUpdate))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeUpdate))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "a+b";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "a+t";
|
||||
}
|
||||
return "a+";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "ab";
|
||||
return "a+b";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "at";
|
||||
return "a+t";
|
||||
}
|
||||
return "a";
|
||||
return "a+";
|
||||
}
|
||||
|
||||
// WRITE
|
||||
if (AnyFlag(mode & OpenMode::ModeWrite))
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "ab";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "at";
|
||||
}
|
||||
return "a";
|
||||
}
|
||||
|
||||
// WRITE
|
||||
if (AnyFlag(mode & OpenMode::ModeWrite))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeUpdate))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeUpdate))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "w+b";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "w+t";
|
||||
}
|
||||
return "w+";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "wb";
|
||||
return "w+b";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "wt";
|
||||
return "w+t";
|
||||
}
|
||||
return "w";
|
||||
return "w+";
|
||||
}
|
||||
|
||||
// READ
|
||||
if (AnyFlag(mode & OpenMode::ModeRead))
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "wb";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "wt";
|
||||
}
|
||||
return "w";
|
||||
}
|
||||
|
||||
// READ
|
||||
if (AnyFlag(mode & OpenMode::ModeRead))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeUpdate))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeUpdate))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "r+b";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "r+t";
|
||||
}
|
||||
return "r+";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "rb";
|
||||
return "r+b";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "rt";
|
||||
return "r+t";
|
||||
}
|
||||
return "r";
|
||||
return "r+";
|
||||
}
|
||||
|
||||
// Bad open mode passed in
|
||||
AZ_Error("FileIO", false, "A bad open mode was sent to GetStringModeFromOpenMode()");
|
||||
return "";
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "rb";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "rt";
|
||||
}
|
||||
return "r";
|
||||
}
|
||||
|
||||
bool NameMatchesFilter(const char* name, const char* filter)
|
||||
// Bad open mode passed in
|
||||
AZ_Error("FileIO", false, "A bad open mode was sent to GetStringModeFromOpenMode()");
|
||||
return "";
|
||||
}
|
||||
|
||||
bool NameMatchesFilter(const char* name, const char* filter)
|
||||
{
|
||||
return AZStd::wildcard_match(filter, name);
|
||||
}
|
||||
|
||||
FileIOStream::FileIOStream()
|
||||
: m_handle(InvalidHandle)
|
||||
, m_mode(OpenMode::Invalid)
|
||||
, m_ownsHandle(true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FileIOStream::FileIOStream(HandleType fileHandle, AZ::IO::OpenMode mode, bool ownsHandle)
|
||||
: m_handle(fileHandle)
|
||||
, m_mode(mode)
|
||||
, m_ownsHandle(ownsHandle)
|
||||
{
|
||||
|
||||
FileIOBase* fileIO = FileIOBase::GetInstance();
|
||||
AZ_Assert(fileIO, "FileIO is not initialized.");
|
||||
AZStd::array<char, MaxPathLength> resolvedPath{ {0} };
|
||||
fileIO->GetFilename(m_handle, resolvedPath.data(), resolvedPath.size() - 1);
|
||||
m_filename = resolvedPath.data();
|
||||
}
|
||||
|
||||
FileIOStream::FileIOStream(const char* path, AZ::IO::OpenMode mode, bool errorOnFailure)
|
||||
: m_handle(InvalidHandle)
|
||||
, m_mode(mode)
|
||||
, m_errorOnFailure(errorOnFailure)
|
||||
{
|
||||
Open(path, mode);
|
||||
}
|
||||
|
||||
FileIOStream::~FileIOStream()
|
||||
{
|
||||
if (m_ownsHandle)
|
||||
{
|
||||
return AZStd::wildcard_match(filter, name);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
FileIOStream::FileIOStream()
|
||||
: m_handle(InvalidHandle)
|
||||
, m_mode(OpenMode::Invalid)
|
||||
, m_ownsHandle(true)
|
||||
bool FileIOStream::Open(const char* path, OpenMode mode)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
FileIOBase* fileIO = FileIOBase::GetInstance();
|
||||
|
||||
Close();
|
||||
|
||||
const Result result = fileIO->Open(path, mode, m_handle);
|
||||
m_ownsHandle = IsOpen();
|
||||
m_mode = mode;
|
||||
|
||||
if (IsOpen())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FileIOStream::FileIOStream(HandleType fileHandle, AZ::IO::OpenMode mode, bool ownsHandle)
|
||||
: m_handle(fileHandle)
|
||||
, m_mode(mode)
|
||||
, m_ownsHandle(ownsHandle)
|
||||
{
|
||||
|
||||
FileIOBase* fileIO = FileIOBase::GetInstance();
|
||||
AZ_Assert(fileIO, "FileIO is not initialized.");
|
||||
// Not using supplied path parameter as it may be unresolved
|
||||
AZStd::array<char, MaxPathLength> resolvedPath{ {0} };
|
||||
fileIO->GetFilename(m_handle, resolvedPath.data(), resolvedPath.size() - 1);
|
||||
m_filename = resolvedPath.data();
|
||||
}
|
||||
|
||||
FileIOStream::FileIOStream(const char* path, AZ::IO::OpenMode mode, bool errorOnFailure)
|
||||
: m_handle(InvalidHandle)
|
||||
, m_mode(mode)
|
||||
, m_errorOnFailure(errorOnFailure)
|
||||
else
|
||||
{
|
||||
Open(path, mode);
|
||||
// remember the file name so you can try again with ReOpen
|
||||
m_filename = path;
|
||||
}
|
||||
|
||||
FileIOStream::~FileIOStream()
|
||||
{
|
||||
if (m_ownsHandle)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
AZ_PROFILE_INTERVAL_START_COLORED(AzCore, &m_filename, 0xff0000ff, "FileIO: %s", m_filename.c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
bool FileIOStream::Open(const char* path, OpenMode mode)
|
||||
bool FileIOStream::ReOpen()
|
||||
{
|
||||
Close();
|
||||
return (m_mode != OpenMode::Invalid) ? Open(m_filename.data(), m_mode) : false;
|
||||
}
|
||||
|
||||
void FileIOStream::Close()
|
||||
{
|
||||
if (m_handle != InvalidHandle)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
FileIOBase* fileIO = FileIOBase::GetInstance();
|
||||
|
||||
Close();
|
||||
|
||||
const Result result = fileIO->Open(path, mode, m_handle);
|
||||
m_ownsHandle = IsOpen();
|
||||
m_mode = mode;
|
||||
|
||||
if (IsOpen())
|
||||
{
|
||||
// Not using supplied path parameter as it may be unresolved
|
||||
AZStd::array<char, MaxPathLength> resolvedPath{ {0} };
|
||||
fileIO->GetFilename(m_handle, resolvedPath.data(), resolvedPath.size() - 1);
|
||||
m_filename = resolvedPath.data();
|
||||
}
|
||||
else
|
||||
{
|
||||
// remember the file name so you can try again with ReOpen
|
||||
m_filename = path;
|
||||
}
|
||||
|
||||
AZ_PROFILE_INTERVAL_START_COLORED(AzCore, &m_filename, 0xff0000ff, "FileIO: %s", m_filename.c_str());
|
||||
return result;
|
||||
FileIOBase::GetInstance()->Close(m_handle);
|
||||
m_handle = InvalidHandle;
|
||||
m_ownsHandle = false;
|
||||
AZ_PROFILE_INTERVAL_END(AzCore, &m_filename);
|
||||
}
|
||||
}
|
||||
|
||||
bool FileIOStream::ReOpen()
|
||||
bool FileIOStream::IsOpen() const
|
||||
{
|
||||
return (m_handle != InvalidHandle);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Retrieves underlying FileIO Handle from file stream
|
||||
\return HandleType
|
||||
*/
|
||||
HandleType FileIOStream::GetHandle() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Retrieves filename
|
||||
\return const char*
|
||||
*/
|
||||
const char* FileIOStream::GetFilename() const
|
||||
{
|
||||
return m_filename.data();
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Retrieves OpenMode flags used to open this file
|
||||
\return OpenMode
|
||||
*/
|
||||
AZ::IO::OpenMode FileIOStream::GetModeFlags() const
|
||||
{
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
bool FileIOStream::CanSeek() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileIOStream::CanRead() const
|
||||
{
|
||||
return (m_mode & (OpenMode::ModeRead | OpenMode::ModeUpdate)) != OpenMode::Invalid;
|
||||
}
|
||||
|
||||
bool FileIOStream::CanWrite() const
|
||||
{
|
||||
return (m_mode & (OpenMode::ModeWrite | OpenMode::ModeAppend | OpenMode::ModeUpdate)) != OpenMode::Invalid;
|
||||
}
|
||||
|
||||
void FileIOStream::Seek(OffsetType bytes, SeekMode mode)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot seek on a FileIOStream that is not open.");
|
||||
|
||||
SeekType seekType = SeekType::SeekFromCurrent;
|
||||
switch (mode)
|
||||
{
|
||||
Close();
|
||||
return (m_mode != OpenMode::Invalid) ? Open(m_filename.data(), m_mode) : false;
|
||||
case GenericStream::ST_SEEK_BEGIN:
|
||||
seekType = SeekType::SeekFromStart;
|
||||
break;
|
||||
case GenericStream::ST_SEEK_CUR:
|
||||
seekType = SeekType::SeekFromCurrent;
|
||||
break;
|
||||
case GenericStream::ST_SEEK_END:
|
||||
seekType = SeekType::SeekFromEnd;
|
||||
break;
|
||||
default:
|
||||
seekType = SeekType::SeekFromCurrent;
|
||||
break;
|
||||
}
|
||||
|
||||
void FileIOStream::Close()
|
||||
const Result result = FileIOBase::GetInstance()->Seek(m_handle, static_cast<AZ::s64>(bytes), seekType);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "Seek failed.");
|
||||
}
|
||||
|
||||
SizeType FileIOStream::Read(SizeType bytes, void* oBuffer)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot read from a FileIOStream that is not open.");
|
||||
|
||||
AZ::u64 bytesRead = 0;
|
||||
const Result result = FileIOBase::GetInstance()->Read(m_handle, oBuffer, bytes, m_errorOnFailure, &bytesRead);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "Read failed in file %s.", m_filename.empty() ? "NULL" : m_filename.c_str());
|
||||
return static_cast<SizeType>(bytesRead);
|
||||
}
|
||||
|
||||
SizeType FileIOStream::Write(SizeType bytes, const void* iBuffer)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot write to a FileIOStream that is not open.");
|
||||
|
||||
AZ::u64 bytesWritten = 0;
|
||||
const Result result = FileIOBase::GetInstance()->Write(m_handle, iBuffer, bytes, &bytesWritten);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "Write failed.");
|
||||
return static_cast<SizeType>(bytesWritten);
|
||||
}
|
||||
|
||||
SizeType FileIOStream::GetCurPos() const
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot use a FileIOStream that is not open.");
|
||||
|
||||
AZ::u64 currentPosition = 0;
|
||||
const Result result = FileIOBase::GetInstance()->Tell(m_handle, currentPosition);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "GetCurPos failed.");
|
||||
return static_cast<SizeType>(currentPosition);
|
||||
}
|
||||
|
||||
SizeType FileIOStream::GetLength() const
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot use a FileIOStream that is not open.");
|
||||
|
||||
SizeType fileLengthBytes = 0;
|
||||
if (!FileIOBase::GetInstance()->Size(m_handle, fileLengthBytes))
|
||||
{
|
||||
if (m_handle != InvalidHandle)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
|
||||
FileIOBase::GetInstance()->Close(m_handle);
|
||||
m_handle = InvalidHandle;
|
||||
m_ownsHandle = false;
|
||||
AZ_PROFILE_INTERVAL_END(AzCore, &m_filename);
|
||||
}
|
||||
AZ_Error("FileIOStream", false, "GetLength failed.");
|
||||
}
|
||||
|
||||
bool FileIOStream::IsOpen() const
|
||||
{
|
||||
return (m_handle != InvalidHandle);
|
||||
}
|
||||
return fileLengthBytes;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Retrieves underlying FileIO Handle from file stream
|
||||
\return HandleType
|
||||
*/
|
||||
HandleType FileIOStream::GetHandle() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Retrieves filename
|
||||
\return const char*
|
||||
*/
|
||||
const char* FileIOStream::GetFilename() const
|
||||
{
|
||||
return m_filename.data();
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Retrieves OpenMode flags used to open this file
|
||||
\return OpenMode
|
||||
*/
|
||||
AZ::IO::OpenMode FileIOStream::GetModeFlags() const
|
||||
{
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
bool FileIOStream::CanSeek() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileIOStream::CanRead() const
|
||||
{
|
||||
return (m_mode & (OpenMode::ModeRead | OpenMode::ModeUpdate)) != OpenMode::Invalid;
|
||||
}
|
||||
|
||||
bool FileIOStream::CanWrite() const
|
||||
{
|
||||
return (m_mode & (OpenMode::ModeWrite | OpenMode::ModeAppend | OpenMode::ModeUpdate)) != OpenMode::Invalid;
|
||||
}
|
||||
|
||||
void FileIOStream::Seek(OffsetType bytes, SeekMode mode)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot seek on a FileIOStream that is not open.");
|
||||
|
||||
SeekType seekType = SeekType::SeekFromCurrent;
|
||||
switch (mode)
|
||||
{
|
||||
case GenericStream::ST_SEEK_BEGIN:
|
||||
seekType = SeekType::SeekFromStart;
|
||||
break;
|
||||
case GenericStream::ST_SEEK_CUR:
|
||||
seekType = SeekType::SeekFromCurrent;
|
||||
break;
|
||||
case GenericStream::ST_SEEK_END:
|
||||
seekType = SeekType::SeekFromEnd;
|
||||
break;
|
||||
default:
|
||||
seekType = SeekType::SeekFromCurrent;
|
||||
break;
|
||||
}
|
||||
|
||||
const Result result = FileIOBase::GetInstance()->Seek(m_handle, static_cast<AZ::s64>(bytes), seekType);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "Seek failed.");
|
||||
}
|
||||
|
||||
SizeType FileIOStream::Read(SizeType bytes, void* oBuffer)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot read from a FileIOStream that is not open.");
|
||||
|
||||
AZ::u64 bytesRead = 0;
|
||||
const Result result = FileIOBase::GetInstance()->Read(m_handle, oBuffer, bytes, m_errorOnFailure, &bytesRead);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "Read failed in file %s.", m_filename.empty() ? "NULL" : m_filename.c_str());
|
||||
return static_cast<SizeType>(bytesRead);
|
||||
}
|
||||
|
||||
SizeType FileIOStream::Write(SizeType bytes, const void* iBuffer)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot write to a FileIOStream that is not open.");
|
||||
|
||||
AZ::u64 bytesWritten = 0;
|
||||
const Result result = FileIOBase::GetInstance()->Write(m_handle, iBuffer, bytes, &bytesWritten);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "Write failed.");
|
||||
return static_cast<SizeType>(bytesWritten);
|
||||
}
|
||||
|
||||
SizeType FileIOStream::GetCurPos() const
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot use a FileIOStream that is not open.");
|
||||
|
||||
AZ::u64 currentPosition = 0;
|
||||
const Result result = FileIOBase::GetInstance()->Tell(m_handle, currentPosition);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "GetCurPos failed.");
|
||||
return static_cast<SizeType>(currentPosition);
|
||||
}
|
||||
|
||||
SizeType FileIOStream::GetLength() const
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot use a FileIOStream that is not open.");
|
||||
|
||||
SizeType fileLengthBytes = 0;
|
||||
if (!FileIOBase::GetInstance()->Size(m_handle, fileLengthBytes))
|
||||
{
|
||||
AZ_Error("FileIOStream", false, "GetLength failed.");
|
||||
}
|
||||
|
||||
return fileLengthBytes;
|
||||
}
|
||||
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -10,69 +10,64 @@
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/std/parallel/thread.h> /// this_thread sleep_for.
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
{
|
||||
int TranslateOpenModeToSystemFileMode(const char* path, OpenMode mode)
|
||||
int TranslateOpenModeToSystemFileMode(const char* path, OpenMode mode)
|
||||
{
|
||||
int systemFileMode = 0;
|
||||
bool read = AnyFlag(mode & OpenMode::ModeRead) || AnyFlag(mode & OpenMode::ModeUpdate);
|
||||
bool write = AnyFlag(mode & OpenMode::ModeWrite) || AnyFlag(mode & OpenMode::ModeUpdate) || AnyFlag(mode & OpenMode::ModeAppend);
|
||||
if (write)
|
||||
{
|
||||
int systemFileMode = 0;
|
||||
bool read = AnyFlag(mode & OpenMode::ModeRead) || AnyFlag(mode & OpenMode::ModeUpdate);
|
||||
bool write = AnyFlag(mode & OpenMode::ModeWrite) || AnyFlag(mode & OpenMode::ModeUpdate) || AnyFlag(mode & OpenMode::ModeAppend);
|
||||
if (write)
|
||||
// If writing the file, create the file in all cases (except r+)
|
||||
if (!SystemFile::Exists(path) && !(AnyFlag(mode & OpenMode::ModeRead) && AnyFlag(mode & OpenMode::ModeUpdate)))
|
||||
{
|
||||
// If writing the file, create the file in all cases (except r+)
|
||||
if (!SystemFile::Exists(path) && !(AnyFlag(mode & OpenMode::ModeRead) && AnyFlag(mode & OpenMode::ModeUpdate)))
|
||||
{
|
||||
// LocalFileIO creates by default
|
||||
systemFileMode |= SystemFile::SF_OPEN_CREATE;
|
||||
}
|
||||
|
||||
if (AnyFlag(mode & OpenMode::ModeCreatePath))
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_CREATE_PATH;
|
||||
}
|
||||
|
||||
// If appending, append.
|
||||
if (AnyFlag(mode & OpenMode::ModeAppend))
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_APPEND;
|
||||
}
|
||||
// If writing and not appending, empty the file
|
||||
else if (AnyFlag(mode & OpenMode::ModeWrite))
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_TRUNCATE;
|
||||
}
|
||||
|
||||
// If reading, set read/write, otherwise just write
|
||||
if (read)
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_READ_WRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_WRITE_ONLY;
|
||||
}
|
||||
}
|
||||
else if (read)
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_READ_ONLY;
|
||||
// LocalFileIO creates by default
|
||||
systemFileMode |= SystemFile::SF_OPEN_CREATE;
|
||||
}
|
||||
|
||||
return systemFileMode;
|
||||
if (AnyFlag(mode & OpenMode::ModeCreatePath))
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_CREATE_PATH;
|
||||
}
|
||||
|
||||
// If appending, append.
|
||||
if (AnyFlag(mode & OpenMode::ModeAppend))
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_APPEND;
|
||||
}
|
||||
// If writing and not appending, empty the file
|
||||
else if (AnyFlag(mode & OpenMode::ModeWrite))
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_TRUNCATE;
|
||||
}
|
||||
|
||||
// If reading, set read/write, otherwise just write
|
||||
if (read)
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_READ_WRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_WRITE_ONLY;
|
||||
}
|
||||
}
|
||||
else if (read)
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_READ_ONLY;
|
||||
}
|
||||
|
||||
bool RetryOpenStream(FileIOStream& stream, int numRetries, int delayBetweenRetry)
|
||||
return systemFileMode;
|
||||
}
|
||||
|
||||
bool RetryOpenStream(FileIOStream& stream, int numRetries, int delayBetweenRetry)
|
||||
{
|
||||
while ((!stream.IsOpen()) && (numRetries > 0))
|
||||
{
|
||||
while ((!stream.IsOpen()) && (numRetries > 0))
|
||||
{
|
||||
numRetries--;
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(delayBetweenRetry));
|
||||
stream.ReOpen();
|
||||
}
|
||||
return stream.IsOpen();
|
||||
numRetries--;
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(delayBetweenRetry));
|
||||
stream.ReOpen();
|
||||
}
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
|
||||
|
||||
return stream.IsOpen();
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,320 +12,317 @@
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
AZStd::shared_ptr<StreamStackEntry> DedicatedCacheConfig::AddStreamStackEntry(
|
||||
const HardwareInformation& hardware, AZStd::shared_ptr<StreamStackEntry> parent)
|
||||
{
|
||||
AZStd::shared_ptr<StreamStackEntry> DedicatedCacheConfig::AddStreamStackEntry(
|
||||
const HardwareInformation& hardware, AZStd::shared_ptr<StreamStackEntry> parent)
|
||||
size_t blockSize;
|
||||
switch (m_blockSize)
|
||||
{
|
||||
size_t blockSize;
|
||||
switch (m_blockSize)
|
||||
case BlockCacheConfig::BlockSize::MaxTransfer:
|
||||
blockSize = hardware.m_maxTransfer;
|
||||
break;
|
||||
case BlockCacheConfig::BlockSize::MemoryAlignment:
|
||||
blockSize = hardware.m_maxPhysicalSectorSize;
|
||||
break;
|
||||
case BlockCacheConfig::BlockSize::SizeAlignment:
|
||||
blockSize = hardware.m_maxLogicalSectorSize;
|
||||
break;
|
||||
default:
|
||||
blockSize = m_blockSize;
|
||||
break;
|
||||
}
|
||||
|
||||
u32 cacheSize = static_cast<AZ::u32>(m_cacheSizeMib * 1_mib);
|
||||
if (blockSize > cacheSize)
|
||||
{
|
||||
AZ_Warning("Streamer", false, "Size (%u) for DedicatedCache isn't big enough to hold at least one cache blocks of size (%zu). "
|
||||
"The cache size will be increased to fit one cache block.", cacheSize, blockSize);
|
||||
cacheSize = aznumeric_caster(blockSize);
|
||||
}
|
||||
|
||||
auto stackEntry = AZStd::make_shared<DedicatedCache>(
|
||||
cacheSize, aznumeric_cast<AZ::u32>(blockSize), aznumeric_cast<AZ::u32>(hardware.m_maxPhysicalSectorSize), m_writeOnlyEpilog);
|
||||
stackEntry->SetNext(AZStd::move(parent));
|
||||
return stackEntry;
|
||||
}
|
||||
|
||||
void DedicatedCacheConfig::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||
{
|
||||
serializeContext->Class<DedicatedCacheConfig, IStreamerStackConfig>()
|
||||
->Version(1)
|
||||
->Field("CacheSizeMib", &DedicatedCacheConfig::m_cacheSizeMib)
|
||||
->Field("BlockSize", &DedicatedCacheConfig::m_blockSize)
|
||||
->Field("WriteOnlyEpilog", &DedicatedCacheConfig::m_writeOnlyEpilog);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// DedicatedCache
|
||||
//
|
||||
|
||||
DedicatedCache::DedicatedCache(u64 cacheSize, u32 blockSize, u32 alignment, bool onlyEpilogWrites)
|
||||
: StreamStackEntry("Dedicated cache")
|
||||
, m_cacheSize(cacheSize)
|
||||
, m_alignment(alignment)
|
||||
, m_blockSize(blockSize)
|
||||
, m_onlyEpilogWrites(onlyEpilogWrites)
|
||||
{
|
||||
}
|
||||
|
||||
void DedicatedCache::SetNext(AZStd::shared_ptr<StreamStackEntry> next)
|
||||
{
|
||||
m_next = AZStd::move(next);
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->SetNext(m_next);
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::SetContext(StreamerContext& context)
|
||||
{
|
||||
StreamStackEntry::SetContext(context);
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->SetContext(context);
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::PrepareRequest(FileRequest* request)
|
||||
{
|
||||
AZ_Assert(request, "PrepareRequest was provided a null request.");
|
||||
|
||||
// Claim the requests so other entries can't claim it and make updates.
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::CreateDedicatedCacheData>)
|
||||
{
|
||||
case BlockCacheConfig::BlockSize::MaxTransfer:
|
||||
blockSize = hardware.m_maxTransfer;
|
||||
break;
|
||||
case BlockCacheConfig::BlockSize::MemoryAlignment:
|
||||
blockSize = hardware.m_maxPhysicalSectorSize;
|
||||
break;
|
||||
case BlockCacheConfig::BlockSize::SizeAlignment:
|
||||
blockSize = hardware.m_maxLogicalSectorSize;
|
||||
break;
|
||||
default:
|
||||
blockSize = m_blockSize;
|
||||
break;
|
||||
args.m_range = FileRange::CreateRangeForEntireFile();
|
||||
m_context->PushPreparedRequest(request);
|
||||
}
|
||||
|
||||
u32 cacheSize = static_cast<AZ::u32>(m_cacheSizeMib * 1_mib);
|
||||
if (blockSize > cacheSize)
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::DestroyDedicatedCacheData>)
|
||||
{
|
||||
AZ_Warning("Streamer", false, "Size (%u) for DedicatedCache isn't big enough to hold at least one cache blocks of size (%zu). "
|
||||
"The cache size will be increased to fit one cache block.", cacheSize, blockSize);
|
||||
cacheSize = aznumeric_caster(blockSize);
|
||||
}
|
||||
|
||||
auto stackEntry = AZStd::make_shared<DedicatedCache>(
|
||||
cacheSize, aznumeric_cast<AZ::u32>(blockSize), aznumeric_cast<AZ::u32>(hardware.m_maxPhysicalSectorSize), m_writeOnlyEpilog);
|
||||
stackEntry->SetNext(AZStd::move(parent));
|
||||
return stackEntry;
|
||||
}
|
||||
|
||||
void DedicatedCacheConfig::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||
{
|
||||
serializeContext->Class<DedicatedCacheConfig, IStreamerStackConfig>()
|
||||
->Version(1)
|
||||
->Field("CacheSizeMib", &DedicatedCacheConfig::m_cacheSizeMib)
|
||||
->Field("BlockSize", &DedicatedCacheConfig::m_blockSize)
|
||||
->Field("WriteOnlyEpilog", &DedicatedCacheConfig::m_writeOnlyEpilog);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// DedicatedCache
|
||||
//
|
||||
|
||||
DedicatedCache::DedicatedCache(u64 cacheSize, u32 blockSize, u32 alignment, bool onlyEpilogWrites)
|
||||
: StreamStackEntry("Dedicated cache")
|
||||
, m_cacheSize(cacheSize)
|
||||
, m_alignment(alignment)
|
||||
, m_blockSize(blockSize)
|
||||
, m_onlyEpilogWrites(onlyEpilogWrites)
|
||||
{
|
||||
}
|
||||
|
||||
void DedicatedCache::SetNext(AZStd::shared_ptr<StreamStackEntry> next)
|
||||
{
|
||||
m_next = AZStd::move(next);
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->SetNext(m_next);
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::SetContext(StreamerContext& context)
|
||||
{
|
||||
StreamStackEntry::SetContext(context);
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->SetContext(context);
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::PrepareRequest(FileRequest* request)
|
||||
{
|
||||
AZ_Assert(request, "PrepareRequest was provided a null request.");
|
||||
|
||||
// Claim the requests so other entries can't claim it and make updates.
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::CreateDedicatedCacheData>)
|
||||
{
|
||||
args.m_range = FileRange::CreateRangeForEntireFile();
|
||||
m_context->PushPreparedRequest(request);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::DestroyDedicatedCacheData>)
|
||||
{
|
||||
args.m_range = FileRange::CreateRangeForEntireFile();
|
||||
m_context->PushPreparedRequest(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
StreamStackEntry::PrepareRequest(request);
|
||||
}
|
||||
}, request->GetCommand());
|
||||
}
|
||||
|
||||
void DedicatedCache::QueueRequest(FileRequest* request)
|
||||
{
|
||||
AZ_Assert(request, "QueueRequest was provided a null request.");
|
||||
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
{
|
||||
ReadFile(request, args);
|
||||
return;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CreateDedicatedCacheData>)
|
||||
{
|
||||
CreateDedicatedCache(request, args);
|
||||
return;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::DestroyDedicatedCacheData>)
|
||||
{
|
||||
DestroyDedicatedCache(request, args);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::FlushData>)
|
||||
{
|
||||
FlushCache(args.m_path);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FlushAllData>)
|
||||
{
|
||||
FlushEntireCache();
|
||||
}
|
||||
StreamStackEntry::QueueRequest(request);
|
||||
}
|
||||
}, request->GetCommand());
|
||||
}
|
||||
|
||||
bool DedicatedCache::ExecuteRequests()
|
||||
{
|
||||
bool hasProcessedRequest = false;
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
{
|
||||
hasProcessedRequest = cache->ExecuteRequests() || hasProcessedRequest;
|
||||
}
|
||||
return StreamStackEntry::ExecuteRequests() || hasProcessedRequest;
|
||||
}
|
||||
|
||||
void DedicatedCache::UpdateStatus(Status& status) const
|
||||
{
|
||||
// Available slots are not updated because the dedicated caches are often
|
||||
// small and specific to a tiny subset of files that are loaded. It would therefore
|
||||
// return a small number of slots that would needlessly hamper streaming as it doesn't
|
||||
// apply to the majority of files.
|
||||
|
||||
bool isIdle = true;
|
||||
for (auto& cache : m_cachedFileCaches)
|
||||
{
|
||||
Status blockStatus;
|
||||
cache->UpdateStatus(blockStatus);
|
||||
isIdle = isIdle && blockStatus.m_isIdle;
|
||||
}
|
||||
status.m_isIdle = status.m_isIdle && isIdle;
|
||||
StreamStackEntry::UpdateStatus(status);
|
||||
}
|
||||
|
||||
void DedicatedCache::UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now,
|
||||
AZStd::vector<FileRequest*>& internalPending, StreamerContext::PreparedQueue::iterator pendingBegin,
|
||||
StreamerContext::PreparedQueue::iterator pendingEnd)
|
||||
{
|
||||
for (auto& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->AddDelayedRequests(internalPending);
|
||||
}
|
||||
|
||||
StreamStackEntry::UpdateCompletionEstimates(now, internalPending, pendingBegin, pendingEnd);
|
||||
|
||||
for (auto& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->UpdatePendingRequestEstimations();
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::ReadFile(FileRequest* request, FileRequest::ReadData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_offset);
|
||||
if (index == s_fileNotFound)
|
||||
{
|
||||
m_usagePercentageStat.PushSample(0.0);
|
||||
if (m_next)
|
||||
{
|
||||
m_next->QueueRequest(request);
|
||||
}
|
||||
args.m_range = FileRange::CreateRangeForEntireFile();
|
||||
m_context->PushPreparedRequest(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_usagePercentageStat.PushSample(1.0);
|
||||
BlockCache& cache = *m_cachedFileCaches[index];
|
||||
cache.QueueRequest(request);
|
||||
#if AZ_STREAMER_ADD_EXTRA_PROFILING_INFO
|
||||
m_overallHitRateStat.PushSample(cache.CalculateHitRatePercentage());
|
||||
m_overallCacheableRateStat.PushSample(cache.CalculateCacheableRatePercentage());
|
||||
#endif
|
||||
StreamStackEntry::PrepareRequest(request);
|
||||
}
|
||||
}
|
||||
}, request->GetCommand());
|
||||
}
|
||||
|
||||
void DedicatedCache::FlushCache(const RequestPath& filePath)
|
||||
void DedicatedCache::QueueRequest(FileRequest* request)
|
||||
{
|
||||
AZ_Assert(request, "QueueRequest was provided a null request.");
|
||||
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
size_t count = m_cachedFileNames.size();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
{
|
||||
if (m_cachedFileNames[i] == filePath)
|
||||
{
|
||||
// Flush the entire block cache as it's entirely dedicated to the found file.
|
||||
m_cachedFileCaches[i]->FlushEntireCache();
|
||||
}
|
||||
ReadFile(request, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::FlushEntireCache()
|
||||
{
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CreateDedicatedCacheData>)
|
||||
{
|
||||
cache->FlushEntireCache();
|
||||
CreateDedicatedCache(request, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::CollectStatistics(AZStd::vector<Statistic>& statistics) const
|
||||
{
|
||||
statistics.push_back(Statistic::CreatePercentage(m_name, "Reads from dedicated cache", m_usagePercentageStat.GetAverage()));
|
||||
#if AZ_STREAMER_ADD_EXTRA_PROFILING_INFO
|
||||
statistics.push_back(Statistic::CreatePercentage(m_name, "Overall cacheable rate", m_overallCacheableRateStat.GetAverage()));
|
||||
statistics.push_back(Statistic::CreatePercentage(m_name, "Overall hit rate", m_overallHitRateStat.GetAverage()));
|
||||
#endif
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, "Num dedicated caches", aznumeric_caster(m_cachedFileNames.size())));
|
||||
StreamStackEntry::CollectStatistics(statistics);
|
||||
}
|
||||
|
||||
void DedicatedCache::CreateDedicatedCache(FileRequest* request, FileRequest::CreateDedicatedCacheData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_range);
|
||||
if (index == s_fileNotFound)
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::DestroyDedicatedCacheData>)
|
||||
{
|
||||
index = m_cachedFileCaches.size();
|
||||
m_cachedFileNames.push_back(data.m_path);
|
||||
m_cachedFileRanges.push_back(data.m_range);
|
||||
m_cachedFileCaches.push_back(AZStd::make_unique<BlockCache>(m_cacheSize, m_blockSize, m_alignment, m_onlyEpilogWrites));
|
||||
m_cachedFileCaches[index]->SetNext(m_next);
|
||||
m_cachedFileCaches[index]->SetContext(*m_context);
|
||||
m_cachedFileRefCounts.push_back(1);
|
||||
DestroyDedicatedCache(request, args);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
++m_cachedFileRefCounts[index];
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::FlushData>)
|
||||
{
|
||||
FlushCache(args.m_path);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FlushAllData>)
|
||||
{
|
||||
FlushEntireCache();
|
||||
}
|
||||
StreamStackEntry::QueueRequest(request);
|
||||
}
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Completed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
}, request->GetCommand());
|
||||
}
|
||||
|
||||
bool DedicatedCache::ExecuteRequests()
|
||||
{
|
||||
bool hasProcessedRequest = false;
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
{
|
||||
hasProcessedRequest = cache->ExecuteRequests() || hasProcessedRequest;
|
||||
}
|
||||
return StreamStackEntry::ExecuteRequests() || hasProcessedRequest;
|
||||
}
|
||||
|
||||
void DedicatedCache::UpdateStatus(Status& status) const
|
||||
{
|
||||
// Available slots are not updated because the dedicated caches are often
|
||||
// small and specific to a tiny subset of files that are loaded. It would therefore
|
||||
// return a small number of slots that would needlessly hamper streaming as it doesn't
|
||||
// apply to the majority of files.
|
||||
|
||||
bool isIdle = true;
|
||||
for (auto& cache : m_cachedFileCaches)
|
||||
{
|
||||
Status blockStatus;
|
||||
cache->UpdateStatus(blockStatus);
|
||||
isIdle = isIdle && blockStatus.m_isIdle;
|
||||
}
|
||||
status.m_isIdle = status.m_isIdle && isIdle;
|
||||
StreamStackEntry::UpdateStatus(status);
|
||||
}
|
||||
|
||||
void DedicatedCache::UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now,
|
||||
AZStd::vector<FileRequest*>& internalPending, StreamerContext::PreparedQueue::iterator pendingBegin,
|
||||
StreamerContext::PreparedQueue::iterator pendingEnd)
|
||||
{
|
||||
for (auto& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->AddDelayedRequests(internalPending);
|
||||
}
|
||||
|
||||
void DedicatedCache::DestroyDedicatedCache(FileRequest* request, FileRequest::DestroyDedicatedCacheData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_range);
|
||||
if (index != s_fileNotFound)
|
||||
{
|
||||
if (m_cachedFileRefCounts[index] > 0)
|
||||
{
|
||||
--m_cachedFileRefCounts[index];
|
||||
if (m_cachedFileRefCounts[index] == 0)
|
||||
{
|
||||
m_cachedFileNames.erase(m_cachedFileNames.begin() + index);
|
||||
m_cachedFileRanges.erase(m_cachedFileRanges.begin() + index);
|
||||
m_cachedFileCaches.erase(m_cachedFileCaches.begin() + index);
|
||||
m_cachedFileRefCounts.erase(m_cachedFileRefCounts.begin() + index);
|
||||
}
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Completed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Failed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
}
|
||||
StreamStackEntry::UpdateCompletionEstimates(now, internalPending, pendingBegin, pendingEnd);
|
||||
|
||||
size_t DedicatedCache::FindCache(const RequestPath& filename, FileRange range)
|
||||
for (auto& cache : m_cachedFileCaches)
|
||||
{
|
||||
size_t count = m_cachedFileNames.size();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
if (m_cachedFileNames[i] == filename && m_cachedFileRanges[i] == range)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return s_fileNotFound;
|
||||
cache->UpdatePendingRequestEstimations();
|
||||
}
|
||||
}
|
||||
|
||||
size_t DedicatedCache::FindCache(const RequestPath& filename, u64 offset)
|
||||
void DedicatedCache::ReadFile(FileRequest* request, FileRequest::ReadData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_offset);
|
||||
if (index == s_fileNotFound)
|
||||
{
|
||||
size_t count = m_cachedFileNames.size();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
m_usagePercentageStat.PushSample(0.0);
|
||||
if (m_next)
|
||||
{
|
||||
if (m_cachedFileNames[i] == filename && m_cachedFileRanges[i].IsInRange(offset))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
m_next->QueueRequest(request);
|
||||
}
|
||||
return s_fileNotFound;
|
||||
}
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
else
|
||||
{
|
||||
m_usagePercentageStat.PushSample(1.0);
|
||||
BlockCache& cache = *m_cachedFileCaches[index];
|
||||
cache.QueueRequest(request);
|
||||
#if AZ_STREAMER_ADD_EXTRA_PROFILING_INFO
|
||||
m_overallHitRateStat.PushSample(cache.CalculateHitRatePercentage());
|
||||
m_overallCacheableRateStat.PushSample(cache.CalculateCacheableRatePercentage());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::FlushCache(const RequestPath& filePath)
|
||||
{
|
||||
size_t count = m_cachedFileNames.size();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
if (m_cachedFileNames[i] == filePath)
|
||||
{
|
||||
// Flush the entire block cache as it's entirely dedicated to the found file.
|
||||
m_cachedFileCaches[i]->FlushEntireCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::FlushEntireCache()
|
||||
{
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->FlushEntireCache();
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::CollectStatistics(AZStd::vector<Statistic>& statistics) const
|
||||
{
|
||||
statistics.push_back(Statistic::CreatePercentage(m_name, "Reads from dedicated cache", m_usagePercentageStat.GetAverage()));
|
||||
#if AZ_STREAMER_ADD_EXTRA_PROFILING_INFO
|
||||
statistics.push_back(Statistic::CreatePercentage(m_name, "Overall cacheable rate", m_overallCacheableRateStat.GetAverage()));
|
||||
statistics.push_back(Statistic::CreatePercentage(m_name, "Overall hit rate", m_overallHitRateStat.GetAverage()));
|
||||
#endif
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, "Num dedicated caches", aznumeric_caster(m_cachedFileNames.size())));
|
||||
StreamStackEntry::CollectStatistics(statistics);
|
||||
}
|
||||
|
||||
void DedicatedCache::CreateDedicatedCache(FileRequest* request, FileRequest::CreateDedicatedCacheData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_range);
|
||||
if (index == s_fileNotFound)
|
||||
{
|
||||
index = m_cachedFileCaches.size();
|
||||
m_cachedFileNames.push_back(data.m_path);
|
||||
m_cachedFileRanges.push_back(data.m_range);
|
||||
m_cachedFileCaches.push_back(AZStd::make_unique<BlockCache>(m_cacheSize, m_blockSize, m_alignment, m_onlyEpilogWrites));
|
||||
m_cachedFileCaches[index]->SetNext(m_next);
|
||||
m_cachedFileCaches[index]->SetContext(*m_context);
|
||||
m_cachedFileRefCounts.push_back(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
++m_cachedFileRefCounts[index];
|
||||
}
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Completed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
}
|
||||
|
||||
void DedicatedCache::DestroyDedicatedCache(FileRequest* request, FileRequest::DestroyDedicatedCacheData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_range);
|
||||
if (index != s_fileNotFound)
|
||||
{
|
||||
if (m_cachedFileRefCounts[index] > 0)
|
||||
{
|
||||
--m_cachedFileRefCounts[index];
|
||||
if (m_cachedFileRefCounts[index] == 0)
|
||||
{
|
||||
m_cachedFileNames.erase(m_cachedFileNames.begin() + index);
|
||||
m_cachedFileRanges.erase(m_cachedFileRanges.begin() + index);
|
||||
m_cachedFileCaches.erase(m_cachedFileCaches.begin() + index);
|
||||
m_cachedFileRefCounts.erase(m_cachedFileRefCounts.begin() + index);
|
||||
}
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Completed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Failed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
}
|
||||
|
||||
size_t DedicatedCache::FindCache(const RequestPath& filename, FileRange range)
|
||||
{
|
||||
size_t count = m_cachedFileNames.size();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
if (m_cachedFileNames[i] == filename && m_cachedFileRanges[i] == range)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return s_fileNotFound;
|
||||
}
|
||||
|
||||
size_t DedicatedCache::FindCache(const RequestPath& filename, u64 offset)
|
||||
{
|
||||
size_t count = m_cachedFileNames.size();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
if (m_cachedFileNames[i] == filename && m_cachedFileRanges[i].IsInRange(offset))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return s_fileNotFound;
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -8,104 +8,101 @@
|
||||
|
||||
#include <AzCore/IO/Streamer/FileRange.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
FileRange FileRange::CreateRange(u64 offset, u64 size)
|
||||
{
|
||||
FileRange FileRange::CreateRange(u64 offset, u64 size)
|
||||
{
|
||||
FileRange result;
|
||||
result.m_hasOffsetEndSet = true;
|
||||
result.m_isEntireFile = false;
|
||||
result.m_offsetBegin = offset;
|
||||
result.m_offsetEnd = offset + size;
|
||||
return result;
|
||||
}
|
||||
FileRange result;
|
||||
result.m_hasOffsetEndSet = true;
|
||||
result.m_isEntireFile = false;
|
||||
result.m_offsetBegin = offset;
|
||||
result.m_offsetEnd = offset + size;
|
||||
return result;
|
||||
}
|
||||
|
||||
FileRange FileRange::CreateRangeForEntireFile()
|
||||
{
|
||||
FileRange result;
|
||||
result.m_hasOffsetEndSet = false;
|
||||
result.m_isEntireFile = true;
|
||||
result.m_offsetBegin = 0;
|
||||
result.m_offsetEnd = (static_cast<u64>(1) << 63) - 1;
|
||||
return result;
|
||||
}
|
||||
FileRange FileRange::CreateRangeForEntireFile()
|
||||
{
|
||||
FileRange result;
|
||||
result.m_hasOffsetEndSet = false;
|
||||
result.m_isEntireFile = true;
|
||||
result.m_offsetBegin = 0;
|
||||
result.m_offsetEnd = (static_cast<u64>(1) << 63) - 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
FileRange FileRange::CreateRangeForEntireFile(u64 fileSize)
|
||||
{
|
||||
FileRange result;
|
||||
result.m_hasOffsetEndSet = true;
|
||||
result.m_isEntireFile = true;
|
||||
result.m_offsetBegin = 0;
|
||||
result.m_offsetEnd = fileSize;
|
||||
return result;
|
||||
}
|
||||
FileRange FileRange::CreateRangeForEntireFile(u64 fileSize)
|
||||
{
|
||||
FileRange result;
|
||||
result.m_hasOffsetEndSet = true;
|
||||
result.m_isEntireFile = true;
|
||||
result.m_offsetBegin = 0;
|
||||
result.m_offsetEnd = fileSize;
|
||||
return result;
|
||||
}
|
||||
|
||||
FileRange::FileRange()
|
||||
: m_isEntireFile(false)
|
||||
, m_offsetBegin(0)
|
||||
, m_hasOffsetEndSet(false)
|
||||
, m_offsetEnd(0)
|
||||
{
|
||||
}
|
||||
FileRange::FileRange()
|
||||
: m_isEntireFile(false)
|
||||
, m_offsetBegin(0)
|
||||
, m_hasOffsetEndSet(false)
|
||||
, m_offsetEnd(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool FileRange::operator==(const FileRange& rhs) const
|
||||
bool FileRange::operator==(const FileRange& rhs) const
|
||||
{
|
||||
if (m_isEntireFile)
|
||||
{
|
||||
if (m_isEntireFile)
|
||||
{
|
||||
return rhs.m_isEntireFile && m_offsetBegin == rhs.m_offsetBegin;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_offsetBegin == rhs.m_offsetBegin && m_offsetEnd == rhs.m_offsetEnd;
|
||||
}
|
||||
return rhs.m_isEntireFile && m_offsetBegin == rhs.m_offsetBegin;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_offsetBegin == rhs.m_offsetBegin && m_offsetEnd == rhs.m_offsetEnd;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileRange::operator!=(const FileRange& rhs) const
|
||||
bool FileRange::operator!=(const FileRange& rhs) const
|
||||
{
|
||||
if (m_isEntireFile)
|
||||
{
|
||||
if (m_isEntireFile)
|
||||
{
|
||||
return !rhs.m_isEntireFile || m_offsetBegin != rhs.m_offsetBegin;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_offsetBegin != rhs.m_offsetBegin || m_offsetEnd != rhs.m_offsetEnd;
|
||||
}
|
||||
return !rhs.m_isEntireFile || m_offsetBegin != rhs.m_offsetBegin;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_offsetBegin != rhs.m_offsetBegin || m_offsetEnd != rhs.m_offsetEnd;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileRange::IsEntireFile() const
|
||||
{
|
||||
return m_isEntireFile != 0;
|
||||
}
|
||||
bool FileRange::IsEntireFile() const
|
||||
{
|
||||
return m_isEntireFile != 0;
|
||||
}
|
||||
|
||||
bool FileRange::IsSizeKnown() const
|
||||
{
|
||||
// m_hasOffsetEndSet being zero has the special meaning that the file size has not
|
||||
// specifically been set yet.
|
||||
return m_hasOffsetEndSet != 0;
|
||||
}
|
||||
bool FileRange::IsSizeKnown() const
|
||||
{
|
||||
// m_hasOffsetEndSet being zero has the special meaning that the file size has not
|
||||
// specifically been set yet.
|
||||
return m_hasOffsetEndSet != 0;
|
||||
}
|
||||
|
||||
bool FileRange::IsInRange(u64 offset) const
|
||||
{
|
||||
return m_offsetBegin <= offset && offset < m_offsetEnd;
|
||||
}
|
||||
bool FileRange::IsInRange(u64 offset) const
|
||||
{
|
||||
return m_offsetBegin <= offset && offset < m_offsetEnd;
|
||||
}
|
||||
|
||||
u64 FileRange::GetOffset() const
|
||||
{
|
||||
return m_offsetBegin;
|
||||
}
|
||||
u64 FileRange::GetOffset() const
|
||||
{
|
||||
return m_offsetBegin;
|
||||
}
|
||||
|
||||
u64 FileRange::GetSize() const
|
||||
{
|
||||
AZ_Assert(m_hasOffsetEndSet, "Calling GetSize on a FileRange that doesn't have a size specified.");
|
||||
return m_offsetEnd - m_offsetBegin;
|
||||
}
|
||||
u64 FileRange::GetSize() const
|
||||
{
|
||||
AZ_Assert(m_hasOffsetEndSet, "Calling GetSize on a FileRange that doesn't have a size specified.");
|
||||
return m_offsetEnd - m_offsetBegin;
|
||||
}
|
||||
|
||||
u64 FileRange::GetEndPoint() const
|
||||
{
|
||||
AZ_Assert(m_hasOffsetEndSet, "Calling GetEndPoint on a FileRange that doesn't have an end offset specified.");
|
||||
return m_offsetEnd;
|
||||
}
|
||||
} // namespace IO
|
||||
} // namesapce AZ
|
||||
u64 FileRange::GetEndPoint() const
|
||||
{
|
||||
AZ_Assert(m_hasOffsetEndSet, "Calling GetEndPoint on a FileRange that doesn't have an end offset specified.");
|
||||
return m_offsetEnd;
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -12,469 +12,466 @@
|
||||
#include <AzCore/IO/Streamer/FileRequest.h>
|
||||
#include <AzCore/IO/Streamer/StreamerContext.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
//
|
||||
// Command structures.
|
||||
//
|
||||
|
||||
FileRequest::ExternalRequestData::ExternalRequestData(FileRequestPtr&& request)
|
||||
: m_request(AZStd::move(request))
|
||||
{}
|
||||
|
||||
FileRequest::RequestPathStoreData::RequestPathStoreData(RequestPath path)
|
||||
: m_path(AZStd::move(path))
|
||||
{}
|
||||
|
||||
FileRequest::ReadRequestData::ReadRequestData(RequestPath path, void* output, u64 outputSize, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority)
|
||||
: m_path(AZStd::move(path))
|
||||
, m_allocator(nullptr)
|
||||
, m_deadline(deadline)
|
||||
, m_output(output)
|
||||
, m_outputSize(outputSize)
|
||||
, m_offset(offset)
|
||||
, m_size(size)
|
||||
, m_priority(priority)
|
||||
, m_memoryType(IStreamerTypes::MemoryType::ReadWrite) // Only generic memory can be assigned externally.
|
||||
{}
|
||||
|
||||
FileRequest::ReadRequestData::ReadRequestData(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator,
|
||||
u64 offset, u64 size, AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority)
|
||||
: m_path(AZStd::move(path))
|
||||
, m_allocator(allocator)
|
||||
, m_deadline(deadline)
|
||||
, m_output(nullptr)
|
||||
, m_outputSize(0)
|
||||
, m_offset(offset)
|
||||
, m_size(size)
|
||||
, m_priority(priority)
|
||||
, m_memoryType(IStreamerTypes::MemoryType::ReadWrite) // Only generic memory can be assigned externally.
|
||||
{}
|
||||
|
||||
FileRequest::ReadRequestData::~ReadRequestData()
|
||||
{
|
||||
//
|
||||
// Command structures.
|
||||
//
|
||||
|
||||
FileRequest::ExternalRequestData::ExternalRequestData(FileRequestPtr&& request)
|
||||
: m_request(AZStd::move(request))
|
||||
{}
|
||||
|
||||
FileRequest::RequestPathStoreData::RequestPathStoreData(RequestPath path)
|
||||
: m_path(AZStd::move(path))
|
||||
{}
|
||||
|
||||
FileRequest::ReadRequestData::ReadRequestData(RequestPath path, void* output, u64 outputSize, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority)
|
||||
: m_path(AZStd::move(path))
|
||||
, m_allocator(nullptr)
|
||||
, m_deadline(deadline)
|
||||
, m_output(output)
|
||||
, m_outputSize(outputSize)
|
||||
, m_offset(offset)
|
||||
, m_size(size)
|
||||
, m_priority(priority)
|
||||
, m_memoryType(IStreamerTypes::MemoryType::ReadWrite) // Only generic memory can be assigned externally.
|
||||
{}
|
||||
|
||||
FileRequest::ReadRequestData::ReadRequestData(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator,
|
||||
u64 offset, u64 size, AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority)
|
||||
: m_path(AZStd::move(path))
|
||||
, m_allocator(allocator)
|
||||
, m_deadline(deadline)
|
||||
, m_output(nullptr)
|
||||
, m_outputSize(0)
|
||||
, m_offset(offset)
|
||||
, m_size(size)
|
||||
, m_priority(priority)
|
||||
, m_memoryType(IStreamerTypes::MemoryType::ReadWrite) // Only generic memory can be assigned externally.
|
||||
{}
|
||||
|
||||
FileRequest::ReadRequestData::~ReadRequestData()
|
||||
if (m_allocator != nullptr)
|
||||
{
|
||||
if (m_allocator != nullptr)
|
||||
if (m_output != nullptr)
|
||||
{
|
||||
if (m_output != nullptr)
|
||||
{
|
||||
m_allocator->Release(m_output);
|
||||
}
|
||||
m_allocator->UnlockAllocator();
|
||||
m_allocator->Release(m_output);
|
||||
}
|
||||
m_allocator->UnlockAllocator();
|
||||
}
|
||||
}
|
||||
|
||||
FileRequest::ReadData::ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead)
|
||||
: m_output(output)
|
||||
, m_outputSize(outputSize)
|
||||
, m_path(path)
|
||||
, m_offset(offset)
|
||||
, m_size(size)
|
||||
, m_sharedRead(sharedRead)
|
||||
{}
|
||||
FileRequest::ReadData::ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead)
|
||||
: m_output(output)
|
||||
, m_outputSize(outputSize)
|
||||
, m_path(path)
|
||||
, m_offset(offset)
|
||||
, m_size(size)
|
||||
, m_sharedRead(sharedRead)
|
||||
{}
|
||||
|
||||
FileRequest::CompressedReadData::CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize)
|
||||
: m_compressionInfo(AZStd::move(compressionInfo))
|
||||
, m_output(output)
|
||||
, m_readOffset(readOffset)
|
||||
, m_readSize(readSize)
|
||||
{}
|
||||
FileRequest::CompressedReadData::CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize)
|
||||
: m_compressionInfo(AZStd::move(compressionInfo))
|
||||
, m_output(output)
|
||||
, m_readOffset(readOffset)
|
||||
, m_readSize(readSize)
|
||||
{}
|
||||
|
||||
FileRequest::FileExistsCheckData::FileExistsCheckData(const RequestPath& path)
|
||||
: m_path(path)
|
||||
{}
|
||||
FileRequest::FileExistsCheckData::FileExistsCheckData(const RequestPath& path)
|
||||
: m_path(path)
|
||||
{}
|
||||
|
||||
FileRequest::FileMetaDataRetrievalData::FileMetaDataRetrievalData(const RequestPath& path)
|
||||
: m_path(path)
|
||||
{}
|
||||
FileRequest::FileMetaDataRetrievalData::FileMetaDataRetrievalData(const RequestPath& path)
|
||||
: m_path(path)
|
||||
{}
|
||||
|
||||
FileRequest::CancelData::CancelData(FileRequestPtr target)
|
||||
: m_target(AZStd::move(target))
|
||||
{}
|
||||
FileRequest::CancelData::CancelData(FileRequestPtr target)
|
||||
: m_target(AZStd::move(target))
|
||||
{}
|
||||
|
||||
FileRequest::FlushData::FlushData(RequestPath path)
|
||||
: m_path(AZStd::move(path))
|
||||
{}
|
||||
FileRequest::FlushData::FlushData(RequestPath path)
|
||||
: m_path(AZStd::move(path))
|
||||
{}
|
||||
|
||||
FileRequest::RescheduleData::RescheduleData(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline,
|
||||
IStreamerTypes::Priority newPriority)
|
||||
: m_target(AZStd::move(target))
|
||||
, m_newDeadline(newDeadline)
|
||||
, m_newPriority(newPriority)
|
||||
{}
|
||||
FileRequest::RescheduleData::RescheduleData(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline,
|
||||
IStreamerTypes::Priority newPriority)
|
||||
: m_target(AZStd::move(target))
|
||||
, m_newDeadline(newDeadline)
|
||||
, m_newPriority(newPriority)
|
||||
{}
|
||||
|
||||
FileRequest::CreateDedicatedCacheData::CreateDedicatedCacheData(RequestPath path, const FileRange& range)
|
||||
: m_path(AZStd::move(path))
|
||||
, m_range(range)
|
||||
{}
|
||||
FileRequest::CreateDedicatedCacheData::CreateDedicatedCacheData(RequestPath path, const FileRange& range)
|
||||
: m_path(AZStd::move(path))
|
||||
, m_range(range)
|
||||
{}
|
||||
|
||||
FileRequest::DestroyDedicatedCacheData::DestroyDedicatedCacheData(RequestPath path, const FileRange& range)
|
||||
: m_path(AZStd::move(path))
|
||||
, m_range(range)
|
||||
{}
|
||||
FileRequest::DestroyDedicatedCacheData::DestroyDedicatedCacheData(RequestPath path, const FileRange& range)
|
||||
: m_path(AZStd::move(path))
|
||||
, m_range(range)
|
||||
{}
|
||||
|
||||
FileRequest::ReportData::ReportData(ReportType reportType)
|
||||
: m_reportType(reportType)
|
||||
{}
|
||||
FileRequest::ReportData::ReportData(ReportType reportType)
|
||||
: m_reportType(reportType)
|
||||
{}
|
||||
|
||||
FileRequest::CustomData::CustomData(AZStd::any data, bool failWhenUnhandled)
|
||||
: m_data(AZStd::move(data))
|
||||
, m_failWhenUnhandled(failWhenUnhandled)
|
||||
{}
|
||||
FileRequest::CustomData::CustomData(AZStd::any data, bool failWhenUnhandled)
|
||||
: m_data(AZStd::move(data))
|
||||
, m_failWhenUnhandled(failWhenUnhandled)
|
||||
{}
|
||||
|
||||
|
||||
//
|
||||
// FileRequest
|
||||
//
|
||||
//
|
||||
// FileRequest
|
||||
//
|
||||
|
||||
FileRequest::FileRequest(Usage usage)
|
||||
: m_usage(usage)
|
||||
FileRequest::FileRequest(Usage usage)
|
||||
: m_usage(usage)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
FileRequest::~FileRequest()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void FileRequest::CreateRequestLink(FileRequestPtr&& request)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'RequestLink', but another task was already assigned.");
|
||||
m_parent = request->m_request.m_parent;
|
||||
request->m_request.m_parent = this;
|
||||
m_dependencies++;
|
||||
m_command.emplace<ExternalRequestData>(AZStd::move(request));
|
||||
}
|
||||
|
||||
void FileRequest::CreateRequestPathStore(FileRequest* parent, RequestPath path)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'CreateRequestPathStore', but another task was already assigned.");
|
||||
m_command.emplace<RequestPathStoreData>(AZStd::move(path));
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::CreateReadRequest(RequestPath path, void* output, u64 outputSize, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'ReadRequest', but another task was already assigned.");
|
||||
m_command.emplace<ReadRequestData>(AZStd::move(path), output, outputSize, offset, size, deadline, priority);
|
||||
}
|
||||
|
||||
void FileRequest::CreateReadRequest(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'ReadRequest', but another task was already assigned.");
|
||||
m_command.emplace<ReadRequestData>(AZStd::move(path), allocator, offset, size, deadline, priority);
|
||||
}
|
||||
|
||||
void FileRequest::CreateRead(FileRequest* parent, void* output, u64 outputSize, const RequestPath& path,
|
||||
u64 offset, u64 size, bool sharedRead)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Read', but another task was already assigned.");
|
||||
m_command.emplace<ReadData>(output, outputSize, AZStd::move(path), offset, size, sharedRead);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::CreateCompressedRead(FileRequest* parent, const CompressionInfo& compressionInfo,
|
||||
void* output, u64 readOffset, u64 readSize)
|
||||
{
|
||||
CreateCompressedRead(parent, CompressionInfo(compressionInfo), output, readOffset, readSize);
|
||||
}
|
||||
|
||||
void FileRequest::CreateCompressedRead(FileRequest* parent, CompressionInfo&& compressionInfo,
|
||||
void* output, u64 readOffset, u64 readSize)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'CompressedRead', but another task was already assigned.");
|
||||
m_command.emplace<CompressedReadData>(AZStd::move(compressionInfo), output, readOffset, readSize);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::CreateWait(FileRequest* parent)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Wait', but another task was already assigned.");
|
||||
m_command.emplace<WaitData>();
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::CreateFileExistsCheck(const RequestPath& path)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'FileExistsCheck', but another task was already assigned.");
|
||||
m_command.emplace<FileExistsCheckData>(path);
|
||||
}
|
||||
|
||||
void FileRequest::CreateFileMetaDataRetrieval(const RequestPath& path)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'FileMetaDataRetrieval', but another task was already assigned.");
|
||||
m_command.emplace<FileMetaDataRetrievalData>(path);
|
||||
}
|
||||
|
||||
void FileRequest::CreateCancel(FileRequestPtr target)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Cancel', but another task was already assigned.");
|
||||
m_command.emplace<CancelData>(AZStd::move(target));
|
||||
}
|
||||
|
||||
void FileRequest::CreateReschedule(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline,
|
||||
IStreamerTypes::Priority newPriority)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Reschedule', but another task was already assigned.");
|
||||
m_command.emplace<RescheduleData>(AZStd::move(target), newDeadline, newPriority);
|
||||
}
|
||||
|
||||
void FileRequest::CreateFlush(RequestPath path)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Flush', but another task was already assigned.");
|
||||
m_command.emplace<FlushData>(AZStd::move(path));
|
||||
}
|
||||
|
||||
void FileRequest::CreateFlushAll()
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'FlushAll', but another task was already assigned.");
|
||||
m_command.emplace<FlushAllData>();
|
||||
}
|
||||
|
||||
void FileRequest::CreateDedicatedCacheCreation(RequestPath path, const FileRange& range, FileRequest* parent)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'CreateDedicateCache', but another task was already assigned.");
|
||||
m_command.emplace<CreateDedicatedCacheData>(AZStd::move(path), range);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::CreateDedicatedCacheDestruction(RequestPath path, const FileRange& range, FileRequest* parent)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'DestroyDedicateCache', but another task was already assigned.");
|
||||
m_command.emplace<DestroyDedicatedCacheData>(AZStd::move(path), range);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::CreateReport(ReportData::ReportType reportType)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Report', but another task was already assigned.");
|
||||
m_command.emplace<ReportData>(reportType);
|
||||
}
|
||||
|
||||
void FileRequest::CreateCustom(AZStd::any data, bool failWhenUnhandled, FileRequest* parent)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Custom', but another task was already assigned.");
|
||||
m_command.emplace<CustomData>(AZStd::move(data), failWhenUnhandled);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::SetCompletionCallback(OnCompletionCallback callback)
|
||||
{
|
||||
m_onCompletion = AZStd::move(callback);
|
||||
}
|
||||
|
||||
FileRequest::CommandVariant& FileRequest::GetCommand()
|
||||
{
|
||||
return m_command;
|
||||
}
|
||||
|
||||
const FileRequest::CommandVariant& FileRequest::GetCommand() const
|
||||
{
|
||||
return m_command;
|
||||
}
|
||||
|
||||
IStreamerTypes::RequestStatus FileRequest::GetStatus() const
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
void FileRequest::SetStatus(IStreamerTypes::RequestStatus newStatus)
|
||||
{
|
||||
IStreamerTypes::RequestStatus currentStatus = m_status;
|
||||
switch (newStatus)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
FileRequest::~FileRequest()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void FileRequest::CreateRequestLink(FileRequestPtr&& request)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'RequestLink', but another task was already assigned.");
|
||||
m_parent = request->m_request.m_parent;
|
||||
request->m_request.m_parent = this;
|
||||
m_dependencies++;
|
||||
m_command.emplace<ExternalRequestData>(AZStd::move(request));
|
||||
}
|
||||
|
||||
void FileRequest::CreateRequestPathStore(FileRequest* parent, RequestPath path)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'CreateRequestPathStore', but another task was already assigned.");
|
||||
m_command.emplace<RequestPathStoreData>(AZStd::move(path));
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::CreateReadRequest(RequestPath path, void* output, u64 outputSize, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'ReadRequest', but another task was already assigned.");
|
||||
m_command.emplace<ReadRequestData>(AZStd::move(path), output, outputSize, offset, size, deadline, priority);
|
||||
}
|
||||
|
||||
void FileRequest::CreateReadRequest(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'ReadRequest', but another task was already assigned.");
|
||||
m_command.emplace<ReadRequestData>(AZStd::move(path), allocator, offset, size, deadline, priority);
|
||||
}
|
||||
|
||||
void FileRequest::CreateRead(FileRequest* parent, void* output, u64 outputSize, const RequestPath& path,
|
||||
u64 offset, u64 size, bool sharedRead)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Read', but another task was already assigned.");
|
||||
m_command.emplace<ReadData>(output, outputSize, AZStd::move(path), offset, size, sharedRead);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::CreateCompressedRead(FileRequest* parent, const CompressionInfo& compressionInfo,
|
||||
void* output, u64 readOffset, u64 readSize)
|
||||
{
|
||||
CreateCompressedRead(parent, CompressionInfo(compressionInfo), output, readOffset, readSize);
|
||||
}
|
||||
|
||||
void FileRequest::CreateCompressedRead(FileRequest* parent, CompressionInfo&& compressionInfo,
|
||||
void* output, u64 readOffset, u64 readSize)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'CompressedRead', but another task was already assigned.");
|
||||
m_command.emplace<CompressedReadData>(AZStd::move(compressionInfo), output, readOffset, readSize);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::CreateWait(FileRequest* parent)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Wait', but another task was already assigned.");
|
||||
m_command.emplace<WaitData>();
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::CreateFileExistsCheck(const RequestPath& path)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'FileExistsCheck', but another task was already assigned.");
|
||||
m_command.emplace<FileExistsCheckData>(path);
|
||||
}
|
||||
|
||||
void FileRequest::CreateFileMetaDataRetrieval(const RequestPath& path)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'FileMetaDataRetrieval', but another task was already assigned.");
|
||||
m_command.emplace<FileMetaDataRetrievalData>(path);
|
||||
}
|
||||
|
||||
void FileRequest::CreateCancel(FileRequestPtr target)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Cancel', but another task was already assigned.");
|
||||
m_command.emplace<CancelData>(AZStd::move(target));
|
||||
}
|
||||
|
||||
void FileRequest::CreateReschedule(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline,
|
||||
IStreamerTypes::Priority newPriority)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Reschedule', but another task was already assigned.");
|
||||
m_command.emplace<RescheduleData>(AZStd::move(target), newDeadline, newPriority);
|
||||
}
|
||||
|
||||
void FileRequest::CreateFlush(RequestPath path)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Flush', but another task was already assigned.");
|
||||
m_command.emplace<FlushData>(AZStd::move(path));
|
||||
}
|
||||
|
||||
void FileRequest::CreateFlushAll()
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'FlushAll', but another task was already assigned.");
|
||||
m_command.emplace<FlushAllData>();
|
||||
}
|
||||
|
||||
void FileRequest::CreateDedicatedCacheCreation(RequestPath path, const FileRange& range, FileRequest* parent)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'CreateDedicateCache', but another task was already assigned.");
|
||||
m_command.emplace<CreateDedicatedCacheData>(AZStd::move(path), range);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::CreateDedicatedCacheDestruction(RequestPath path, const FileRange& range, FileRequest* parent)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'DestroyDedicateCache', but another task was already assigned.");
|
||||
m_command.emplace<DestroyDedicatedCacheData>(AZStd::move(path), range);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::CreateReport(ReportData::ReportType reportType)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Report', but another task was already assigned.");
|
||||
m_command.emplace<ReportData>(reportType);
|
||||
}
|
||||
|
||||
void FileRequest::CreateCustom(AZStd::any data, bool failWhenUnhandled, FileRequest* parent)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Custom', but another task was already assigned.");
|
||||
m_command.emplace<CustomData>(AZStd::move(data), failWhenUnhandled);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::SetCompletionCallback(OnCompletionCallback callback)
|
||||
{
|
||||
m_onCompletion = AZStd::move(callback);
|
||||
}
|
||||
|
||||
FileRequest::CommandVariant& FileRequest::GetCommand()
|
||||
{
|
||||
return m_command;
|
||||
}
|
||||
|
||||
const FileRequest::CommandVariant& FileRequest::GetCommand() const
|
||||
{
|
||||
return m_command;
|
||||
}
|
||||
|
||||
IStreamerTypes::RequestStatus FileRequest::GetStatus() const
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
void FileRequest::SetStatus(IStreamerTypes::RequestStatus newStatus)
|
||||
{
|
||||
IStreamerTypes::RequestStatus currentStatus = m_status;
|
||||
switch (newStatus)
|
||||
case IStreamerTypes::RequestStatus::Pending:
|
||||
[[fallthrough]];
|
||||
case IStreamerTypes::RequestStatus::Queued:
|
||||
[[fallthrough]];
|
||||
case IStreamerTypes::RequestStatus::Processing:
|
||||
if (currentStatus == IStreamerTypes::RequestStatus::Failed ||
|
||||
currentStatus == IStreamerTypes::RequestStatus::Canceled ||
|
||||
currentStatus == IStreamerTypes::RequestStatus::Completed)
|
||||
{
|
||||
case IStreamerTypes::RequestStatus::Pending:
|
||||
[[fallthrough]];
|
||||
case IStreamerTypes::RequestStatus::Queued:
|
||||
[[fallthrough]];
|
||||
case IStreamerTypes::RequestStatus::Processing:
|
||||
if (currentStatus == IStreamerTypes::RequestStatus::Failed ||
|
||||
currentStatus == IStreamerTypes::RequestStatus::Canceled ||
|
||||
currentStatus == IStreamerTypes::RequestStatus::Completed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case IStreamerTypes::RequestStatus::Completed:
|
||||
if (currentStatus == IStreamerTypes::RequestStatus::Failed || currentStatus == IStreamerTypes::RequestStatus::Canceled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case IStreamerTypes::RequestStatus::Canceled:
|
||||
if (currentStatus == IStreamerTypes::RequestStatus::Failed || currentStatus == IStreamerTypes::RequestStatus::Completed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case IStreamerTypes::RequestStatus::Failed:
|
||||
[[fallthrough]];
|
||||
default:
|
||||
break;
|
||||
return;
|
||||
}
|
||||
m_status = newStatus;
|
||||
}
|
||||
|
||||
FileRequest* FileRequest::GetParent()
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
const FileRequest* FileRequest::GetParent() const
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
size_t FileRequest::GetNumDependencies() const
|
||||
{
|
||||
return m_dependencies;
|
||||
}
|
||||
|
||||
bool FileRequest::FailsWhenUnhandled() const
|
||||
{
|
||||
return AZStd::visit([](auto&& args)
|
||||
break;
|
||||
case IStreamerTypes::RequestStatus::Completed:
|
||||
if (currentStatus == IStreamerTypes::RequestStatus::Failed || currentStatus == IStreamerTypes::RequestStatus::Canceled)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, AZStd::monostate>)
|
||||
{
|
||||
AZ_Assert(false,
|
||||
"Request does not contain a valid command. It may have been reset already or was never assigned a command.");
|
||||
return true;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CustomData>)
|
||||
{
|
||||
return args.m_failWhenUnhandled;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Command::s_failWhenUnhandled;
|
||||
}
|
||||
}, m_command);
|
||||
}
|
||||
|
||||
void FileRequest::Reset()
|
||||
{
|
||||
m_command = AZStd::monostate{};
|
||||
m_onCompletion = &OnCompletionPlaceholder;
|
||||
m_estimatedCompletion = AZStd::chrono::system_clock::time_point();
|
||||
m_parent = nullptr;
|
||||
m_status = IStreamerTypes::RequestStatus::Pending;
|
||||
m_dependencies = 0;
|
||||
}
|
||||
|
||||
void FileRequest::SetOptionalParent(FileRequest* parent)
|
||||
{
|
||||
if (parent)
|
||||
{
|
||||
m_parent = parent;
|
||||
AZ_Assert(parent->m_dependencies < std::numeric_limits<decltype(parent->m_dependencies)>::max(),
|
||||
"A file request dependency was added, but the parent can't have any more dependencies.");
|
||||
++parent->m_dependencies;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileRequest::WorksOn(FileRequestPtr& request) const
|
||||
{
|
||||
const FileRequest* current = this;
|
||||
while (current)
|
||||
break;
|
||||
case IStreamerTypes::RequestStatus::Canceled:
|
||||
if (currentStatus == IStreamerTypes::RequestStatus::Failed || currentStatus == IStreamerTypes::RequestStatus::Completed)
|
||||
{
|
||||
auto* link = AZStd::get_if<ExternalRequestData>(¤t->m_command);
|
||||
if (!link)
|
||||
{
|
||||
current = current->m_parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
return link->m_request == request;
|
||||
}
|
||||
return;
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
case IStreamerTypes::RequestStatus::Failed:
|
||||
[[fallthrough]];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m_status = newStatus;
|
||||
}
|
||||
|
||||
size_t FileRequest::GetPendingId() const
|
||||
{
|
||||
return m_pendingId;
|
||||
}
|
||||
FileRequest* FileRequest::GetParent()
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
void FileRequest::SetEstimatedCompletion(AZStd::chrono::system_clock::time_point time)
|
||||
const FileRequest* FileRequest::GetParent() const
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
size_t FileRequest::GetNumDependencies() const
|
||||
{
|
||||
return m_dependencies;
|
||||
}
|
||||
|
||||
bool FileRequest::FailsWhenUnhandled() const
|
||||
{
|
||||
return AZStd::visit([](auto&& args)
|
||||
{
|
||||
FileRequest* current = this;
|
||||
do
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, AZStd::monostate>)
|
||||
{
|
||||
AZ_Assert(false,
|
||||
"Request does not contain a valid command. It may have been reset already or was never assigned a command.");
|
||||
return true;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CustomData>)
|
||||
{
|
||||
return args.m_failWhenUnhandled;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Command::s_failWhenUnhandled;
|
||||
}
|
||||
}, m_command);
|
||||
}
|
||||
|
||||
void FileRequest::Reset()
|
||||
{
|
||||
m_command = AZStd::monostate{};
|
||||
m_onCompletion = &OnCompletionPlaceholder;
|
||||
m_estimatedCompletion = AZStd::chrono::system_clock::time_point();
|
||||
m_parent = nullptr;
|
||||
m_status = IStreamerTypes::RequestStatus::Pending;
|
||||
m_dependencies = 0;
|
||||
}
|
||||
|
||||
void FileRequest::SetOptionalParent(FileRequest* parent)
|
||||
{
|
||||
if (parent)
|
||||
{
|
||||
m_parent = parent;
|
||||
AZ_Assert(parent->m_dependencies < std::numeric_limits<decltype(parent->m_dependencies)>::max(),
|
||||
"A file request dependency was added, but the parent can't have any more dependencies.");
|
||||
++parent->m_dependencies;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileRequest::WorksOn(FileRequestPtr& request) const
|
||||
{
|
||||
const FileRequest* current = this;
|
||||
while (current)
|
||||
{
|
||||
auto* link = AZStd::get_if<ExternalRequestData>(¤t->m_command);
|
||||
if (!link)
|
||||
{
|
||||
current->m_estimatedCompletion = time;
|
||||
current = current->m_parent;
|
||||
} while (current);
|
||||
}
|
||||
|
||||
AZStd::chrono::system_clock::time_point FileRequest::GetEstimatedCompletion() const
|
||||
{
|
||||
return m_estimatedCompletion;
|
||||
}
|
||||
|
||||
//
|
||||
// ExternalFileRequest
|
||||
//
|
||||
|
||||
ExternalFileRequest::ExternalFileRequest(StreamerContext* owner)
|
||||
: m_request(FileRequest::Usage::External)
|
||||
, m_owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
void ExternalFileRequest::add_ref()
|
||||
{
|
||||
m_refCount++;
|
||||
}
|
||||
|
||||
void ExternalFileRequest::release()
|
||||
{
|
||||
if (--m_refCount == 0)
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(m_owner, "No owning context set for the file request.");
|
||||
m_owner->RecycleRequest(this);
|
||||
return link->m_request == request;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator==(const FileRequestHandle& lhs, const FileRequestPtr& rhs)
|
||||
{
|
||||
return lhs.m_request == &rhs->m_request;
|
||||
}
|
||||
size_t FileRequest::GetPendingId() const
|
||||
{
|
||||
return m_pendingId;
|
||||
}
|
||||
|
||||
bool operator==(const FileRequestPtr& lhs, const FileRequestHandle& rhs)
|
||||
void FileRequest::SetEstimatedCompletion(AZStd::chrono::system_clock::time_point time)
|
||||
{
|
||||
FileRequest* current = this;
|
||||
do
|
||||
{
|
||||
return rhs == lhs;
|
||||
}
|
||||
current->m_estimatedCompletion = time;
|
||||
current = current->m_parent;
|
||||
} while (current);
|
||||
}
|
||||
|
||||
bool operator!=(const FileRequestHandle& lhs, const FileRequestPtr& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
AZStd::chrono::system_clock::time_point FileRequest::GetEstimatedCompletion() const
|
||||
{
|
||||
return m_estimatedCompletion;
|
||||
}
|
||||
|
||||
bool operator!=(const FileRequestPtr& lhs, const FileRequestHandle& rhs)
|
||||
//
|
||||
// ExternalFileRequest
|
||||
//
|
||||
|
||||
ExternalFileRequest::ExternalFileRequest(StreamerContext* owner)
|
||||
: m_request(FileRequest::Usage::External)
|
||||
, m_owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
void ExternalFileRequest::add_ref()
|
||||
{
|
||||
m_refCount++;
|
||||
}
|
||||
|
||||
void ExternalFileRequest::release()
|
||||
{
|
||||
if (--m_refCount == 0)
|
||||
{
|
||||
return !(rhs == lhs);
|
||||
AZ_Assert(m_owner, "No owning context set for the file request.");
|
||||
m_owner->RecycleRequest(this);
|
||||
}
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
}
|
||||
|
||||
bool operator==(const FileRequestHandle& lhs, const FileRequestPtr& rhs)
|
||||
{
|
||||
return lhs.m_request == &rhs->m_request;
|
||||
}
|
||||
|
||||
bool operator==(const FileRequestPtr& lhs, const FileRequestHandle& rhs)
|
||||
{
|
||||
return rhs == lhs;
|
||||
}
|
||||
|
||||
bool operator!=(const FileRequestHandle& lhs, const FileRequestPtr& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
bool operator!=(const FileRequestPtr& lhs, const FileRequestHandle& rhs)
|
||||
{
|
||||
return !(rhs == lhs);
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,376 +14,373 @@
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
AZStd::shared_ptr<StreamStackEntry> ReadSplitterConfig::AddStreamStackEntry(
|
||||
const HardwareInformation& hardware, AZStd::shared_ptr<StreamStackEntry> parent)
|
||||
{
|
||||
AZStd::shared_ptr<StreamStackEntry> ReadSplitterConfig::AddStreamStackEntry(
|
||||
const HardwareInformation& hardware, AZStd::shared_ptr<StreamStackEntry> parent)
|
||||
size_t splitSize;
|
||||
switch (m_splitSize)
|
||||
{
|
||||
size_t splitSize;
|
||||
switch (m_splitSize)
|
||||
{
|
||||
case SplitSize::MaxTransfer:
|
||||
splitSize = hardware.m_maxTransfer;
|
||||
break;
|
||||
case SplitSize::MemoryAlignment:
|
||||
splitSize = hardware.m_maxPhysicalSectorSize;
|
||||
break;
|
||||
default:
|
||||
splitSize = m_splitSize;
|
||||
break;
|
||||
}
|
||||
|
||||
size_t bufferSize = m_bufferSizeMib * 1_mib;
|
||||
if (bufferSize < splitSize)
|
||||
{
|
||||
AZ_Warning("Streamer", false, "The buffer size for the Read Splitter is smaller than the individual split size. "
|
||||
"It will be increased to fit at least one split.");
|
||||
bufferSize = splitSize;
|
||||
}
|
||||
|
||||
auto stackEntry = AZStd::make_shared<ReadSplitter>(
|
||||
splitSize,
|
||||
aznumeric_caster(hardware.m_maxPhysicalSectorSize),
|
||||
aznumeric_caster(hardware.m_maxLogicalSectorSize),
|
||||
bufferSize, m_adjustOffset, m_splitAlignedRequests);
|
||||
stackEntry->SetNext(AZStd::move(parent));
|
||||
return stackEntry;
|
||||
case SplitSize::MaxTransfer:
|
||||
splitSize = hardware.m_maxTransfer;
|
||||
break;
|
||||
case SplitSize::MemoryAlignment:
|
||||
splitSize = hardware.m_maxPhysicalSectorSize;
|
||||
break;
|
||||
default:
|
||||
splitSize = m_splitSize;
|
||||
break;
|
||||
}
|
||||
|
||||
void ReadSplitterConfig::Reflect(AZ::ReflectContext* context)
|
||||
size_t bufferSize = m_bufferSizeMib * 1_mib;
|
||||
if (bufferSize < splitSize)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||
{
|
||||
serializeContext->Enum<SplitSize>()
|
||||
->Version(1)
|
||||
->Value("MaxTransfer", SplitSize::MaxTransfer)
|
||||
->Value("MemoryAlignment", SplitSize::MemoryAlignment);
|
||||
|
||||
serializeContext->Class<ReadSplitterConfig, IStreamerStackConfig>()
|
||||
->Version(1)
|
||||
->Field("BufferSizeMib", &ReadSplitterConfig::m_bufferSizeMib)
|
||||
->Field("SplitSize", &ReadSplitterConfig::m_splitSize)
|
||||
->Field("AdjustOffset", &ReadSplitterConfig::m_adjustOffset)
|
||||
->Field("SplitAlignedRequests", &ReadSplitterConfig::m_splitAlignedRequests);
|
||||
}
|
||||
AZ_Warning("Streamer", false, "The buffer size for the Read Splitter is smaller than the individual split size. "
|
||||
"It will be increased to fit at least one split.");
|
||||
bufferSize = splitSize;
|
||||
}
|
||||
|
||||
static constexpr char AvgNumSubReadsName[] = "Avg. num sub reads";
|
||||
static constexpr char AlignedReadsName[] = "Aligned reads";
|
||||
static constexpr char NumAvailableBufferSlotsName[] = "Num available buffer slots";
|
||||
static constexpr char NumPendingReadsName[] = "Num pending reads";
|
||||
auto stackEntry = AZStd::make_shared<ReadSplitter>(
|
||||
splitSize,
|
||||
aznumeric_caster(hardware.m_maxPhysicalSectorSize),
|
||||
aznumeric_caster(hardware.m_maxLogicalSectorSize),
|
||||
bufferSize, m_adjustOffset, m_splitAlignedRequests);
|
||||
stackEntry->SetNext(AZStd::move(parent));
|
||||
return stackEntry;
|
||||
}
|
||||
|
||||
ReadSplitter::ReadSplitter(u64 maxReadSize, u32 memoryAlignment, u32 sizeAlignment, size_t bufferSize,
|
||||
bool adjustOffset, bool splitAlignedRequests)
|
||||
: StreamStackEntry("Read splitter")
|
||||
, m_buffer(nullptr)
|
||||
, m_bufferSize(bufferSize)
|
||||
, m_maxReadSize(maxReadSize)
|
||||
, m_memoryAlignment(memoryAlignment)
|
||||
, m_sizeAlignment(sizeAlignment)
|
||||
, m_adjustOffset(adjustOffset)
|
||||
, m_splitAlignedRequests(splitAlignedRequests)
|
||||
void ReadSplitterConfig::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||
{
|
||||
AZ_Assert(IStreamerTypes::IsPowerOf2(memoryAlignment), "Memory alignment needs to be a power of 2");
|
||||
AZ_Assert(IStreamerTypes::IsPowerOf2(sizeAlignment), "Size alignment needs to be a power of 2");
|
||||
AZ_Assert(IStreamerTypes::IsAlignedTo(maxReadSize, sizeAlignment),
|
||||
"Maximum read size isn't aligned to a multiple of the size alignment.");
|
||||
serializeContext->Enum<SplitSize>()
|
||||
->Version(1)
|
||||
->Value("MaxTransfer", SplitSize::MaxTransfer)
|
||||
->Value("MemoryAlignment", SplitSize::MemoryAlignment);
|
||||
|
||||
size_t numBufferSlots = bufferSize / maxReadSize;
|
||||
// Don't divide the reads up in more sub-reads than there are dependencies available.
|
||||
numBufferSlots = AZStd::min(numBufferSlots, FileRequest::GetMaxNumDependencies());
|
||||
m_bufferCopyInformation = AZStd::unique_ptr<BufferCopyInformation[]>(new BufferCopyInformation[numBufferSlots]);
|
||||
m_availableBufferSlots.reserve(numBufferSlots);
|
||||
for (u32 i = aznumeric_caster(numBufferSlots); i > 0; --i)
|
||||
{
|
||||
m_availableBufferSlots.push_back(i - 1);
|
||||
}
|
||||
serializeContext->Class<ReadSplitterConfig, IStreamerStackConfig>()
|
||||
->Version(1)
|
||||
->Field("BufferSizeMib", &ReadSplitterConfig::m_bufferSizeMib)
|
||||
->Field("SplitSize", &ReadSplitterConfig::m_splitSize)
|
||||
->Field("AdjustOffset", &ReadSplitterConfig::m_adjustOffset)
|
||||
->Field("SplitAlignedRequests", &ReadSplitterConfig::m_splitAlignedRequests);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr char AvgNumSubReadsName[] = "Avg. num sub reads";
|
||||
static constexpr char AlignedReadsName[] = "Aligned reads";
|
||||
static constexpr char NumAvailableBufferSlotsName[] = "Num available buffer slots";
|
||||
static constexpr char NumPendingReadsName[] = "Num pending reads";
|
||||
|
||||
ReadSplitter::ReadSplitter(u64 maxReadSize, u32 memoryAlignment, u32 sizeAlignment, size_t bufferSize,
|
||||
bool adjustOffset, bool splitAlignedRequests)
|
||||
: StreamStackEntry("Read splitter")
|
||||
, m_buffer(nullptr)
|
||||
, m_bufferSize(bufferSize)
|
||||
, m_maxReadSize(maxReadSize)
|
||||
, m_memoryAlignment(memoryAlignment)
|
||||
, m_sizeAlignment(sizeAlignment)
|
||||
, m_adjustOffset(adjustOffset)
|
||||
, m_splitAlignedRequests(splitAlignedRequests)
|
||||
{
|
||||
AZ_Assert(IStreamerTypes::IsPowerOf2(memoryAlignment), "Memory alignment needs to be a power of 2");
|
||||
AZ_Assert(IStreamerTypes::IsPowerOf2(sizeAlignment), "Size alignment needs to be a power of 2");
|
||||
AZ_Assert(IStreamerTypes::IsAlignedTo(maxReadSize, sizeAlignment),
|
||||
"Maximum read size isn't aligned to a multiple of the size alignment.");
|
||||
|
||||
size_t numBufferSlots = bufferSize / maxReadSize;
|
||||
// Don't divide the reads up in more sub-reads than there are dependencies available.
|
||||
numBufferSlots = AZStd::min(numBufferSlots, FileRequest::GetMaxNumDependencies());
|
||||
m_bufferCopyInformation = AZStd::unique_ptr<BufferCopyInformation[]>(new BufferCopyInformation[numBufferSlots]);
|
||||
m_availableBufferSlots.reserve(numBufferSlots);
|
||||
for (u32 i = aznumeric_caster(numBufferSlots); i > 0; --i)
|
||||
{
|
||||
m_availableBufferSlots.push_back(i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
ReadSplitter::~ReadSplitter()
|
||||
{
|
||||
if (m_buffer)
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Get().DeAllocate(m_buffer, m_bufferSize, m_memoryAlignment);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadSplitter::QueueRequest(FileRequest* request)
|
||||
{
|
||||
AZ_Assert(request, "QueueRequest was provided a null request.");
|
||||
if (!m_next)
|
||||
{
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Failed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
return;
|
||||
}
|
||||
|
||||
ReadSplitter::~ReadSplitter()
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&request->GetCommand());
|
||||
if (data == nullptr)
|
||||
{
|
||||
if (m_buffer)
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Get().DeAllocate(m_buffer, m_bufferSize, m_memoryAlignment);
|
||||
}
|
||||
StreamStackEntry::QueueRequest(request);
|
||||
return;
|
||||
}
|
||||
|
||||
void ReadSplitter::QueueRequest(FileRequest* request)
|
||||
{
|
||||
AZ_Assert(request, "QueueRequest was provided a null request.");
|
||||
if (!m_next)
|
||||
{
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Failed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
return;
|
||||
}
|
||||
m_averageNumSubReadsStat.PushSample(aznumeric_cast<double>((data->m_size / m_maxReadSize) + 1));
|
||||
Statistic::PlotImmediate(m_name, AvgNumSubReadsName, m_averageNumSubReadsStat.GetMostRecentSample());
|
||||
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&request->GetCommand());
|
||||
if (data == nullptr)
|
||||
bool isAligned = IStreamerTypes::IsAlignedTo(data->m_output, m_memoryAlignment);
|
||||
if (m_adjustOffset)
|
||||
{
|
||||
isAligned = isAligned && IStreamerTypes::IsAlignedTo(data->m_offset, m_sizeAlignment);
|
||||
}
|
||||
|
||||
if (isAligned || m_bufferSize == 0)
|
||||
{
|
||||
m_alignedReadsStat.PushSample(isAligned ? 1.0 : 0.0);
|
||||
if (!m_splitAlignedRequests)
|
||||
{
|
||||
StreamStackEntry::QueueRequest(request);
|
||||
return;
|
||||
}
|
||||
|
||||
m_averageNumSubReadsStat.PushSample(aznumeric_cast<double>((data->m_size / m_maxReadSize) + 1));
|
||||
Statistic::PlotImmediate(m_name, AvgNumSubReadsName, m_averageNumSubReadsStat.GetMostRecentSample());
|
||||
|
||||
bool isAligned = IStreamerTypes::IsAlignedTo(data->m_output, m_memoryAlignment);
|
||||
if (m_adjustOffset)
|
||||
{
|
||||
isAligned = isAligned && IStreamerTypes::IsAlignedTo(data->m_offset, m_sizeAlignment);
|
||||
}
|
||||
|
||||
if (isAligned || m_bufferSize == 0)
|
||||
{
|
||||
m_alignedReadsStat.PushSample(isAligned ? 1.0 : 0.0);
|
||||
if (!m_splitAlignedRequests)
|
||||
{
|
||||
StreamStackEntry::QueueRequest(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueAlignedRead(request);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_alignedReadsStat.PushSample(0.0);
|
||||
InitializeBuffer();
|
||||
QueueBufferedRead(request);
|
||||
QueueAlignedRead(request);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadSplitter::QueueAlignedRead(FileRequest* request)
|
||||
else
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&request->GetCommand());
|
||||
AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command.");
|
||||
m_alignedReadsStat.PushSample(0.0);
|
||||
InitializeBuffer();
|
||||
QueueBufferedRead(request);
|
||||
}
|
||||
}
|
||||
|
||||
if (data->m_size <= m_maxReadSize)
|
||||
{
|
||||
StreamStackEntry::QueueRequest(request);
|
||||
return;
|
||||
}
|
||||
void ReadSplitter::QueueAlignedRead(FileRequest* request)
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&request->GetCommand());
|
||||
AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command.");
|
||||
|
||||
PendingRead pendingRead;
|
||||
pendingRead.m_request = request;
|
||||
pendingRead.m_output = reinterpret_cast<u8*>(data->m_output);
|
||||
pendingRead.m_outputSize = data->m_outputSize;
|
||||
pendingRead.m_readSize = data->m_size;
|
||||
pendingRead.m_offset = data->m_offset;
|
||||
pendingRead.m_isBuffered = false;
|
||||
|
||||
if (!m_pendingReads.empty())
|
||||
{
|
||||
m_pendingReads.push_back(pendingRead);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!QueueAlignedRead(pendingRead))
|
||||
{
|
||||
m_pendingReads.push_back(pendingRead);
|
||||
}
|
||||
if (data->m_size <= m_maxReadSize)
|
||||
{
|
||||
StreamStackEntry::QueueRequest(request);
|
||||
return;
|
||||
}
|
||||
|
||||
bool ReadSplitter::QueueAlignedRead(PendingRead& pending)
|
||||
PendingRead pendingRead;
|
||||
pendingRead.m_request = request;
|
||||
pendingRead.m_output = reinterpret_cast<u8*>(data->m_output);
|
||||
pendingRead.m_outputSize = data->m_outputSize;
|
||||
pendingRead.m_readSize = data->m_size;
|
||||
pendingRead.m_offset = data->m_offset;
|
||||
pendingRead.m_isBuffered = false;
|
||||
|
||||
if (!m_pendingReads.empty())
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&pending.m_request->GetCommand());
|
||||
AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command.");
|
||||
m_pendingReads.push_back(pendingRead);
|
||||
return;
|
||||
}
|
||||
|
||||
while (pending.m_readSize > 0)
|
||||
if (!QueueAlignedRead(pendingRead))
|
||||
{
|
||||
m_pendingReads.push_back(pendingRead);
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadSplitter::QueueAlignedRead(PendingRead& pending)
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&pending.m_request->GetCommand());
|
||||
AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command.");
|
||||
|
||||
while (pending.m_readSize > 0)
|
||||
{
|
||||
if (pending.m_request->GetNumDependencies() >= FileRequest::GetMaxNumDependencies())
|
||||
{
|
||||
if (pending.m_request->GetNumDependencies() >= FileRequest::GetMaxNumDependencies())
|
||||
// Add a wait to make sure the read request isn't completed if all sub-reads completed before
|
||||
// the ReadSplitter has had a chance to add new sub-reads to complete the read.
|
||||
if (pending.m_wait == nullptr)
|
||||
{
|
||||
// Add a wait to make sure the read request isn't completed if all sub-reads completed before
|
||||
// the ReadSplitter has had a chance to add new sub-reads to complete the read.
|
||||
if (pending.m_wait == nullptr)
|
||||
{
|
||||
pending.m_wait = m_context->GetNewInternalRequest();
|
||||
pending.m_wait->CreateWait(pending.m_request);
|
||||
}
|
||||
return false;
|
||||
pending.m_wait = m_context->GetNewInternalRequest();
|
||||
pending.m_wait->CreateWait(pending.m_request);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 readSize = m_maxReadSize;
|
||||
size_t bufferSize = m_maxReadSize;
|
||||
if (pending.m_readSize < m_maxReadSize)
|
||||
u64 readSize = m_maxReadSize;
|
||||
size_t bufferSize = m_maxReadSize;
|
||||
if (pending.m_readSize < m_maxReadSize)
|
||||
{
|
||||
readSize = pending.m_readSize;
|
||||
// This will be the last read so give the remainder of the output buffer to the final request.
|
||||
bufferSize = pending.m_outputSize;
|
||||
}
|
||||
|
||||
FileRequest* subRequest = m_context->GetNewInternalRequest();
|
||||
subRequest->CreateRead(pending.m_request, pending.m_output, bufferSize, data->m_path, pending.m_offset, readSize, data->m_sharedRead);
|
||||
subRequest->SetCompletionCallback([this](FileRequest&)
|
||||
{
|
||||
readSize = pending.m_readSize;
|
||||
// This will be the last read so give the remainder of the output buffer to the final request.
|
||||
bufferSize = pending.m_outputSize;
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
QueuePendingRequest();
|
||||
});
|
||||
m_next->QueueRequest(subRequest);
|
||||
|
||||
pending.m_offset += readSize;
|
||||
pending.m_readSize -= readSize;
|
||||
pending.m_outputSize -= bufferSize;
|
||||
pending.m_output += readSize;
|
||||
}
|
||||
if (pending.m_wait != nullptr)
|
||||
{
|
||||
m_context->MarkRequestAsCompleted(pending.m_wait);
|
||||
pending.m_wait = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReadSplitter::QueueBufferedRead(FileRequest* request)
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&request->GetCommand());
|
||||
AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command.");
|
||||
|
||||
PendingRead pendingRead;
|
||||
pendingRead.m_request = request;
|
||||
pendingRead.m_output = reinterpret_cast<u8*>(data->m_output);
|
||||
pendingRead.m_outputSize = data->m_outputSize;
|
||||
pendingRead.m_readSize = data->m_size;
|
||||
pendingRead.m_offset = data->m_offset;
|
||||
pendingRead.m_isBuffered = true;
|
||||
|
||||
if (!m_pendingReads.empty())
|
||||
{
|
||||
m_pendingReads.push_back(pendingRead);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!QueueBufferedRead(pendingRead))
|
||||
{
|
||||
m_pendingReads.push_back(pendingRead);
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadSplitter::QueueBufferedRead(PendingRead& pending)
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&pending.m_request->GetCommand());
|
||||
AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command.");
|
||||
|
||||
while (pending.m_readSize > 0)
|
||||
{
|
||||
if (!m_availableBufferSlots.empty())
|
||||
{
|
||||
u32 bufferSlot = m_availableBufferSlots.back();
|
||||
m_availableBufferSlots.pop_back();
|
||||
|
||||
u64 readSize;
|
||||
u64 copySize;
|
||||
u64 offset;
|
||||
BufferCopyInformation& copyInfo = m_bufferCopyInformation[bufferSlot];
|
||||
copyInfo.m_target = pending.m_output;
|
||||
|
||||
if (m_adjustOffset)
|
||||
{
|
||||
offset = AZ_SIZE_ALIGN_DOWN(pending.m_offset, aznumeric_cast<u64>(m_sizeAlignment));
|
||||
size_t bufferOffset = pending.m_offset - offset;
|
||||
copyInfo.m_bufferOffset = bufferOffset;
|
||||
readSize = AZStd::min(pending.m_readSize + bufferOffset, m_maxReadSize);
|
||||
copySize = readSize - bufferOffset;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
offset = pending.m_offset;
|
||||
readSize = AZStd::min(pending.m_readSize, m_maxReadSize);
|
||||
copySize = readSize;
|
||||
}
|
||||
AZ_Assert(readSize <= m_maxReadSize, "Read size %llu in read splitter exceeds the maximum split size of %llu.",
|
||||
readSize, m_maxReadSize);
|
||||
copyInfo.m_size = copySize;
|
||||
|
||||
FileRequest* subRequest = m_context->GetNewInternalRequest();
|
||||
subRequest->CreateRead(pending.m_request, pending.m_output, bufferSize, data->m_path, pending.m_offset, readSize, data->m_sharedRead);
|
||||
subRequest->SetCompletionCallback([this](FileRequest&)
|
||||
subRequest->CreateRead(pending.m_request, GetBufferSlot(bufferSlot), m_maxReadSize, data->m_path,
|
||||
offset, readSize, data->m_sharedRead);
|
||||
subRequest->SetCompletionCallback([this, bufferSlot]([[maybe_unused]] FileRequest& request)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
BufferCopyInformation& copyInfo = m_bufferCopyInformation[bufferSlot];
|
||||
memcpy(copyInfo.m_target, GetBufferSlot(bufferSlot) + copyInfo.m_bufferOffset, copyInfo.m_size);
|
||||
m_availableBufferSlots.push_back(bufferSlot);
|
||||
|
||||
QueuePendingRequest();
|
||||
});
|
||||
m_next->QueueRequest(subRequest);
|
||||
|
||||
pending.m_offset += readSize;
|
||||
pending.m_readSize -= readSize;
|
||||
pending.m_outputSize -= bufferSize;
|
||||
pending.m_output += readSize;
|
||||
pending.m_offset += copySize;
|
||||
pending.m_readSize -= copySize;
|
||||
pending.m_outputSize -= copySize;
|
||||
pending.m_output += copySize;
|
||||
}
|
||||
if (pending.m_wait != nullptr)
|
||||
else
|
||||
{
|
||||
m_context->MarkRequestAsCompleted(pending.m_wait);
|
||||
pending.m_wait = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReadSplitter::QueueBufferedRead(FileRequest* request)
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&request->GetCommand());
|
||||
AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command.");
|
||||
|
||||
PendingRead pendingRead;
|
||||
pendingRead.m_request = request;
|
||||
pendingRead.m_output = reinterpret_cast<u8*>(data->m_output);
|
||||
pendingRead.m_outputSize = data->m_outputSize;
|
||||
pendingRead.m_readSize = data->m_size;
|
||||
pendingRead.m_offset = data->m_offset;
|
||||
pendingRead.m_isBuffered = true;
|
||||
|
||||
if (!m_pendingReads.empty())
|
||||
{
|
||||
m_pendingReads.push_back(pendingRead);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!QueueBufferedRead(pendingRead))
|
||||
{
|
||||
m_pendingReads.push_back(pendingRead);
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadSplitter::QueueBufferedRead(PendingRead& pending)
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&pending.m_request->GetCommand());
|
||||
AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command.");
|
||||
|
||||
while (pending.m_readSize > 0)
|
||||
{
|
||||
if (!m_availableBufferSlots.empty())
|
||||
// Add a wait to make sure the read request isn't completed if all sub-reads completed before
|
||||
// the ReadSplitter has had a chance to add new sub-reads to complete the read.
|
||||
if (pending.m_wait == nullptr)
|
||||
{
|
||||
u32 bufferSlot = m_availableBufferSlots.back();
|
||||
m_availableBufferSlots.pop_back();
|
||||
|
||||
u64 readSize;
|
||||
u64 copySize;
|
||||
u64 offset;
|
||||
BufferCopyInformation& copyInfo = m_bufferCopyInformation[bufferSlot];
|
||||
copyInfo.m_target = pending.m_output;
|
||||
|
||||
if (m_adjustOffset)
|
||||
{
|
||||
offset = AZ_SIZE_ALIGN_DOWN(pending.m_offset, aznumeric_cast<u64>(m_sizeAlignment));
|
||||
size_t bufferOffset = pending.m_offset - offset;
|
||||
copyInfo.m_bufferOffset = bufferOffset;
|
||||
readSize = AZStd::min(pending.m_readSize + bufferOffset, m_maxReadSize);
|
||||
copySize = readSize - bufferOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = pending.m_offset;
|
||||
readSize = AZStd::min(pending.m_readSize, m_maxReadSize);
|
||||
copySize = readSize;
|
||||
}
|
||||
AZ_Assert(readSize <= m_maxReadSize, "Read size %llu in read splitter exceeds the maximum split size of %llu.",
|
||||
readSize, m_maxReadSize);
|
||||
copyInfo.m_size = copySize;
|
||||
|
||||
FileRequest* subRequest = m_context->GetNewInternalRequest();
|
||||
subRequest->CreateRead(pending.m_request, GetBufferSlot(bufferSlot), m_maxReadSize, data->m_path,
|
||||
offset, readSize, data->m_sharedRead);
|
||||
subRequest->SetCompletionCallback([this, bufferSlot]([[maybe_unused]] FileRequest& request)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
BufferCopyInformation& copyInfo = m_bufferCopyInformation[bufferSlot];
|
||||
memcpy(copyInfo.m_target, GetBufferSlot(bufferSlot) + copyInfo.m_bufferOffset, copyInfo.m_size);
|
||||
m_availableBufferSlots.push_back(bufferSlot);
|
||||
|
||||
QueuePendingRequest();
|
||||
});
|
||||
m_next->QueueRequest(subRequest);
|
||||
|
||||
pending.m_offset += copySize;
|
||||
pending.m_readSize -= copySize;
|
||||
pending.m_outputSize -= copySize;
|
||||
pending.m_output += copySize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add a wait to make sure the read request isn't completed if all sub-reads completed before
|
||||
// the ReadSplitter has had a chance to add new sub-reads to complete the read.
|
||||
if (pending.m_wait == nullptr)
|
||||
{
|
||||
pending.m_wait = m_context->GetNewInternalRequest();
|
||||
pending.m_wait->CreateWait(pending.m_request);
|
||||
}
|
||||
return false;
|
||||
pending.m_wait = m_context->GetNewInternalRequest();
|
||||
pending.m_wait->CreateWait(pending.m_request);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (pending.m_wait != nullptr)
|
||||
}
|
||||
if (pending.m_wait != nullptr)
|
||||
{
|
||||
m_context->MarkRequestAsCompleted(pending.m_wait);
|
||||
pending.m_wait = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReadSplitter::QueuePendingRequest()
|
||||
{
|
||||
if (!m_pendingReads.empty())
|
||||
{
|
||||
PendingRead& pendingRead = m_pendingReads.front();
|
||||
if (pendingRead.m_isBuffered ? QueueBufferedRead(pendingRead) : QueueAlignedRead(pendingRead))
|
||||
{
|
||||
m_context->MarkRequestAsCompleted(pending.m_wait);
|
||||
pending.m_wait = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReadSplitter::QueuePendingRequest()
|
||||
{
|
||||
if (!m_pendingReads.empty())
|
||||
{
|
||||
PendingRead& pendingRead = m_pendingReads.front();
|
||||
if (pendingRead.m_isBuffered ? QueueBufferedRead(pendingRead) : QueueAlignedRead(pendingRead))
|
||||
{
|
||||
m_pendingReads.pop_front();
|
||||
}
|
||||
m_pendingReads.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadSplitter::UpdateStatus(Status& status) const
|
||||
void ReadSplitter::UpdateStatus(Status& status) const
|
||||
{
|
||||
StreamStackEntry::UpdateStatus(status);
|
||||
if (m_bufferSize > 0)
|
||||
{
|
||||
StreamStackEntry::UpdateStatus(status);
|
||||
if (m_bufferSize > 0)
|
||||
{
|
||||
s32 numAvailableSlots = aznumeric_cast<s32>(m_availableBufferSlots.size());
|
||||
status.m_numAvailableSlots = AZStd::min(status.m_numAvailableSlots, numAvailableSlots);
|
||||
status.m_isIdle = status.m_isIdle && m_pendingReads.empty();
|
||||
}
|
||||
s32 numAvailableSlots = aznumeric_cast<s32>(m_availableBufferSlots.size());
|
||||
status.m_numAvailableSlots = AZStd::min(status.m_numAvailableSlots, numAvailableSlots);
|
||||
status.m_isIdle = status.m_isIdle && m_pendingReads.empty();
|
||||
}
|
||||
}
|
||||
|
||||
void ReadSplitter::CollectStatistics(AZStd::vector<Statistic>& statistics) const
|
||||
{
|
||||
statistics.push_back(Statistic::CreateFloat(m_name, AvgNumSubReadsName, m_averageNumSubReadsStat.GetAverage()));
|
||||
statistics.push_back(Statistic::CreatePercentage(m_name, AlignedReadsName, m_alignedReadsStat.GetAverage()));
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, NumAvailableBufferSlotsName, aznumeric_caster(m_availableBufferSlots.size())));
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, NumPendingReadsName, aznumeric_caster(m_pendingReads.size())));
|
||||
StreamStackEntry::CollectStatistics(statistics);
|
||||
}
|
||||
void ReadSplitter::CollectStatistics(AZStd::vector<Statistic>& statistics) const
|
||||
{
|
||||
statistics.push_back(Statistic::CreateFloat(m_name, AvgNumSubReadsName, m_averageNumSubReadsStat.GetAverage()));
|
||||
statistics.push_back(Statistic::CreatePercentage(m_name, AlignedReadsName, m_alignedReadsStat.GetAverage()));
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, NumAvailableBufferSlotsName, aznumeric_caster(m_availableBufferSlots.size())));
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, NumPendingReadsName, aznumeric_caster(m_pendingReads.size())));
|
||||
StreamStackEntry::CollectStatistics(statistics);
|
||||
}
|
||||
|
||||
void ReadSplitter::InitializeBuffer()
|
||||
void ReadSplitter::InitializeBuffer()
|
||||
{
|
||||
// Lazy initialization to avoid allocating memory if it's not needed.
|
||||
if (m_bufferSize != 0 && m_buffer == nullptr)
|
||||
{
|
||||
// Lazy initialization to avoid allocating memory if it's not needed.
|
||||
if (m_bufferSize != 0 && m_buffer == nullptr)
|
||||
{
|
||||
m_buffer = reinterpret_cast<u8*>(AZ::AllocatorInstance<AZ::SystemAllocator>::Get().Allocate(
|
||||
m_bufferSize, m_memoryAlignment, 0, "AZ::IO::Streamer ReadSplitter", __FILE__, __LINE__));
|
||||
}
|
||||
m_buffer = reinterpret_cast<u8*>(AZ::AllocatorInstance<AZ::SystemAllocator>::Get().Allocate(
|
||||
m_bufferSize, m_memoryAlignment, 0, "AZ::IO::Streamer ReadSplitter", __FILE__, __LINE__));
|
||||
}
|
||||
}
|
||||
|
||||
u8* ReadSplitter::GetBufferSlot(size_t index)
|
||||
{
|
||||
AZ_Assert(m_buffer != nullptr, "A buffer slot was requested by the Read Splitter before the buffer was initialized.");
|
||||
return m_buffer + (index * m_maxReadSize);
|
||||
}
|
||||
} // namespace IO
|
||||
} // namesapce AZ
|
||||
u8* ReadSplitter::GetBufferSlot(size_t index)
|
||||
{
|
||||
AZ_Assert(m_buffer != nullptr, "A buffer slot was requested by the Read Splitter before the buffer was initialized.");
|
||||
return m_buffer + (index * m_maxReadSize);
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -13,23 +13,18 @@
|
||||
|
||||
#include <AzCore/std/parallel/spin_mutex.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Internal
|
||||
{
|
||||
namespace Internal
|
||||
struct RingData
|
||||
{
|
||||
struct RingData
|
||||
{
|
||||
AZ::u32 m_readOffset;
|
||||
AZ::u32 m_writeOffset;
|
||||
AZ::u32 m_startOffset;
|
||||
AZ::u32 m_endOffset;
|
||||
AZ::u32 m_dataToRead;
|
||||
AZ::u8 m_pad[32 - sizeof(AZStd::spin_mutex)];
|
||||
};
|
||||
} // namespace Internal
|
||||
} // namespace AZ
|
||||
|
||||
|
||||
AZ::u32 m_readOffset;
|
||||
AZ::u32 m_writeOffset;
|
||||
AZ::u32 m_startOffset;
|
||||
AZ::u32 m_endOffset;
|
||||
AZ::u32 m_dataToRead;
|
||||
AZ::u8 m_pad[32 - sizeof(AZStd::spin_mutex)];
|
||||
};
|
||||
} // namespace AZ::Internal
|
||||
|
||||
using namespace AZ;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/std/parallel/thread.h>
|
||||
#include <AzCore/std/parallel/lock.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
#include <AzCore/std/string/fixed_string.h>
|
||||
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
|
||||
@@ -83,7 +84,7 @@ AZ_THREAD_LOCAL JobManagerWorkStealing::ThreadInfo* JobManagerWorkStealing::m_cu
|
||||
|
||||
JobManagerWorkStealing::JobManagerWorkStealing(const JobManagerDesc& desc)
|
||||
: m_isAsynchronous(!desc.m_workerThreads.empty())
|
||||
, m_workerThreads(AZStd::move(CreateWorkerThreads(desc.m_workerThreads)))
|
||||
, m_workerThreads(AZStd::move(CreateWorkerThreads(desc)))
|
||||
{
|
||||
//allow workers to begin processing after they have all been created, needed to wait since they may access each others queues
|
||||
m_initSemaphore.release(static_cast<unsigned int>(desc.m_workerThreads.size()));
|
||||
@@ -618,8 +619,9 @@ JobManagerWorkStealing::ThreadInfo* JobManagerWorkStealing::FindCurrentThreadInf
|
||||
return info;
|
||||
}
|
||||
|
||||
JobManagerWorkStealing::ThreadList JobManagerWorkStealing::CreateWorkerThreads(const JobManagerDesc::DescList& workerDescList)
|
||||
JobManagerWorkStealing::ThreadList JobManagerWorkStealing::CreateWorkerThreads(const JobManagerDesc& jmDesc)
|
||||
{
|
||||
const JobManagerDesc::DescList& workerDescList = jmDesc.m_workerThreads;
|
||||
ThreadList workerThreads(workerDescList.size());
|
||||
m_threads.reserve(workerDescList.size());
|
||||
|
||||
@@ -632,8 +634,12 @@ JobManagerWorkStealing::ThreadList JobManagerWorkStealing::CreateWorkerThreads(c
|
||||
info->m_owningManager = this;
|
||||
info->m_workerId = iThread;
|
||||
|
||||
AZStd::fixed_string<128> threadName = AZStd::fixed_string<128>::format(
|
||||
"%s worker thread %d",
|
||||
jmDesc.m_jobManagerName[0] != '\0' ? jmDesc.m_jobManagerName : "AZ JobManager",
|
||||
iThread);
|
||||
AZStd::thread_desc threadDesc;
|
||||
threadDesc.m_name = "AZ JobManager worker thread";
|
||||
threadDesc.m_name = threadName.c_str();
|
||||
threadDesc.m_cpuId = desc.m_cpuId;
|
||||
threadDesc.m_priority = desc.m_priority;
|
||||
if (desc.m_stackSize != 0)
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace AZ
|
||||
void ProcessJobsAssist(ThreadInfo* info, Job* suspendedJob, AZStd::atomic<bool>* notifyFlag);
|
||||
void ProcessJobsSynchronous(ThreadInfo* info, Job* suspendedJob, AZStd::atomic<bool>* notifyFlag);
|
||||
void ProcessJobsInternal(ThreadInfo* info, Job* suspendedJob, AZStd::atomic<bool>* notifyFlag);
|
||||
ThreadList CreateWorkerThreads(const JobManagerDesc::DescList& workerDescList);
|
||||
ThreadList CreateWorkerThreads(const JobManagerDesc& jmDesc);
|
||||
#ifndef AZ_MONOLITHIC_BUILD
|
||||
ThreadInfo* CrossModuleFindAndSetWorkerThreadInfo() const;
|
||||
#endif
|
||||
|
||||
@@ -51,16 +51,18 @@ namespace AZ
|
||||
JobManagerBus::Handler::BusConnect();
|
||||
|
||||
JobManagerDesc desc;
|
||||
desc.m_jobManagerName = "Default JobManager";
|
||||
JobManagerThreadDesc threadDesc;
|
||||
|
||||
int numberOfWorkerThreads = m_numberOfWorkerThreads;
|
||||
if (numberOfWorkerThreads <= 0) // spawn default number of threads
|
||||
{
|
||||
#if (AZ_TRAIT_THREAD_NUM_JOB_MANAGER_WORKER_THREADS)
|
||||
numberOfWorkerThreads = AZ_TRAIT_THREAD_NUM_JOB_MANAGER_WORKER_THREADS;
|
||||
#else
|
||||
uint32_t scaledHardwareThreads = Threading::CalcNumWorkerThreads(cl_jobThreadsConcurrencyRatio, cl_jobThreadsMinNumber, cl_jobThreadsNumReserved);
|
||||
numberOfWorkerThreads = AZ::GetMin(static_cast<unsigned int>(desc.m_workerThreads.capacity()), scaledHardwareThreads);
|
||||
#if (AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS)
|
||||
numberOfWorkerThreads = AZ::GetMin(numberOfWorkerThreads, AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS);
|
||||
#endif // (AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS)
|
||||
#endif // (AZ_TRAIT_THREAD_NUM_JOB_MANAGER_WORKER_THREADS)
|
||||
}
|
||||
|
||||
threadDesc.m_cpuId = AFFINITY_MASK_USERTHREADS;
|
||||
|
||||
@@ -51,6 +51,8 @@ namespace AZ
|
||||
{
|
||||
JobManagerDesc() {}
|
||||
|
||||
const char* m_jobManagerName = "";
|
||||
|
||||
using DescList = AZStd::fixed_vector<JobManagerThreadDesc, 64>;
|
||||
DescList m_workerThreads; ///< List of worker threads to create
|
||||
};
|
||||
|
||||
@@ -8,138 +8,135 @@
|
||||
|
||||
#include <AzCore/Math/Geometry2DUtils.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Geometry2DUtils
|
||||
{
|
||||
namespace Geometry2DUtils
|
||||
float ShortestDistanceSqPointSegment(const Vector2& point, const Vector2& segmentStart, const Vector2& segmentEnd,
|
||||
float epsilon)
|
||||
{
|
||||
float ShortestDistanceSqPointSegment(const Vector2& point, const Vector2& segmentStart, const Vector2& segmentEnd,
|
||||
float epsilon)
|
||||
const AZ::Vector2 segmentVector = segmentEnd - segmentStart;
|
||||
|
||||
// check if the line degenerates to a point
|
||||
const float segmentLengthSq = segmentVector.GetLengthSq();
|
||||
if (segmentLengthSq < epsilon * epsilon)
|
||||
{
|
||||
const AZ::Vector2 segmentVector = segmentEnd - segmentStart;
|
||||
|
||||
// check if the line degenerates to a point
|
||||
const float segmentLengthSq = segmentVector.GetLengthSq();
|
||||
if (segmentLengthSq < epsilon * epsilon)
|
||||
{
|
||||
return (point - segmentStart).GetLengthSq();
|
||||
}
|
||||
|
||||
// if the point projects on to the line segment then the shortest distance is the perpendicular
|
||||
const float projection = (point - segmentStart).Dot(segmentVector);
|
||||
if (projection >= 0.0f && projection <= segmentLengthSq)
|
||||
{
|
||||
const Vector2 perpendicular = (point - segmentStart - projection / segmentLengthSq * segmentVector);
|
||||
return perpendicular.GetLengthSq();
|
||||
}
|
||||
|
||||
// otherwise the point must be closest to one of the end points of the segment
|
||||
return GetMin(
|
||||
(point - segmentStart).GetLengthSq(),
|
||||
(point - segmentEnd).GetLengthSq());
|
||||
return (point - segmentStart).GetLengthSq();
|
||||
}
|
||||
|
||||
float Signed2DTriangleArea(const Vector2& a, const Vector2& b, const Vector2& c)
|
||||
// if the point projects on to the line segment then the shortest distance is the perpendicular
|
||||
const float projection = (point - segmentStart).Dot(segmentVector);
|
||||
if (projection >= 0.0f && projection <= segmentLengthSq)
|
||||
{
|
||||
return 0.5f * ((a.GetX() - c.GetX()) * (b.GetY() - c.GetY()) - (a.GetY() - c.GetY()) * (b.GetX() - c.GetX()));
|
||||
const Vector2 perpendicular = (point - segmentStart - projection / segmentLengthSq * segmentVector);
|
||||
return perpendicular.GetLengthSq();
|
||||
}
|
||||
|
||||
float ShortestDistanceSqSegmentSegment(
|
||||
const Vector2& segment1Start, const Vector2& segment1End,
|
||||
const Vector2& segment2Start, const Vector2& segment2End)
|
||||
// otherwise the point must be closest to one of the end points of the segment
|
||||
return GetMin(
|
||||
(point - segmentStart).GetLengthSq(),
|
||||
(point - segmentEnd).GetLengthSq());
|
||||
}
|
||||
|
||||
float Signed2DTriangleArea(const Vector2& a, const Vector2& b, const Vector2& c)
|
||||
{
|
||||
return 0.5f * ((a.GetX() - c.GetX()) * (b.GetY() - c.GetY()) - (a.GetY() - c.GetY()) * (b.GetX() - c.GetX()));
|
||||
}
|
||||
|
||||
float ShortestDistanceSqSegmentSegment(
|
||||
const Vector2& segment1Start, const Vector2& segment1End,
|
||||
const Vector2& segment2Start, const Vector2& segment2End)
|
||||
{
|
||||
// if the segments cross, then the distance is zero
|
||||
|
||||
// if the two ends of segment 2 are on different sides of segment 1, then these two triangles will have
|
||||
// different winding orders (see Real-Time Collision Detection, Christer Ericson, ISBN 978-1558607323,
|
||||
// Chapter 5.1.9.1)
|
||||
const float area1 = Signed2DTriangleArea(segment1Start, segment1End, segment2End);
|
||||
const float area2 = Signed2DTriangleArea(segment1Start, segment1End, segment2Start);
|
||||
if (area1 * area2 < 0.0f)
|
||||
{
|
||||
// if the segments cross, then the distance is zero
|
||||
|
||||
// if the two ends of segment 2 are on different sides of segment 1, then these two triangles will have
|
||||
// different winding orders (see Real-Time Collision Detection, Christer Ericson, ISBN 978-1558607323,
|
||||
// Chapter 5.1.9.1)
|
||||
const float area1 = Signed2DTriangleArea(segment1Start, segment1End, segment2End);
|
||||
const float area2 = Signed2DTriangleArea(segment1Start, segment1End, segment2Start);
|
||||
if (area1 * area2 < 0.0f)
|
||||
// similarly we can check if the two ends of segment 1 are on different sides of segment 2
|
||||
const float area3 = Signed2DTriangleArea(segment2Start, segment2End, segment1Start);
|
||||
const float area4 = area3 + area2 - area1;
|
||||
if (area3 * area4 < 0.0f)
|
||||
{
|
||||
// similarly we can check if the two ends of segment 1 are on different sides of segment 2
|
||||
const float area3 = Signed2DTriangleArea(segment2Start, segment2End, segment1Start);
|
||||
const float area4 = area3 + area2 - area1;
|
||||
if (area3 * area4 < 0.0f)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// otherwise the shortest distance must be between one of the segment end points and the other segment
|
||||
return GetMin(
|
||||
GetMin(
|
||||
ShortestDistanceSqPointSegment(segment1Start, segment2Start, segment2End),
|
||||
ShortestDistanceSqPointSegment(segment1End, segment2Start, segment2End)),
|
||||
GetMin(
|
||||
ShortestDistanceSqPointSegment(segment2Start, segment1Start, segment1End),
|
||||
ShortestDistanceSqPointSegment(segment2End, segment1Start, segment1End))
|
||||
);
|
||||
}
|
||||
|
||||
bool IsSimplePolygon(const AZStd::vector<AZ::Vector2>& vertices, float epsilon)
|
||||
// otherwise the shortest distance must be between one of the segment end points and the other segment
|
||||
return GetMin(
|
||||
GetMin(
|
||||
ShortestDistanceSqPointSegment(segment1Start, segment2Start, segment2End),
|
||||
ShortestDistanceSqPointSegment(segment1End, segment2Start, segment2End)),
|
||||
GetMin(
|
||||
ShortestDistanceSqPointSegment(segment2Start, segment1Start, segment1End),
|
||||
ShortestDistanceSqPointSegment(segment2End, segment1Start, segment1End))
|
||||
);
|
||||
}
|
||||
|
||||
bool IsSimplePolygon(const AZStd::vector<AZ::Vector2>& vertices, float epsilon)
|
||||
{
|
||||
// note that this implementation is quadratic in the number of vertices
|
||||
// if it becomes a bottleneck, there are approaches which are O(n log n), e.g. the Bentley-Ottmann algorithm
|
||||
|
||||
const size_t vertexCount = vertices.size();
|
||||
|
||||
if (vertexCount < 3)
|
||||
{
|
||||
// note that this implementation is quadratic in the number of vertices
|
||||
// if it becomes a bottleneck, there are approaches which are O(n log n), e.g. the Bentley-Ottmann algorithm
|
||||
|
||||
const size_t vertexCount = vertices.size();
|
||||
|
||||
if (vertexCount < 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vertexCount == 3)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const float epsilonSq = epsilon * epsilon;
|
||||
|
||||
for (size_t i = 0; i < vertexCount; ++i)
|
||||
{
|
||||
// make it easy to nicely wrap indices
|
||||
const size_t safeIndex = i + vertexCount;
|
||||
|
||||
const size_t endIndex = (safeIndex - 1) % vertexCount;
|
||||
const size_t beginIndex = (safeIndex + 2) % vertexCount;
|
||||
|
||||
for (size_t j = beginIndex; j != endIndex; j = (j + 1) % vertexCount)
|
||||
{
|
||||
const float distSq = ShortestDistanceSqSegmentSegment(
|
||||
vertices[i],
|
||||
vertices[(i + 1) % vertexCount],
|
||||
vertices[j],
|
||||
vertices[(j + 1) % vertexCount]
|
||||
);
|
||||
|
||||
if (distSq < epsilonSq)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vertexCount == 3)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsConvex(const AZStd::vector<AZ::Vector2>& vertices)
|
||||
const float epsilonSq = epsilon * epsilon;
|
||||
|
||||
for (size_t i = 0; i < vertexCount; ++i)
|
||||
{
|
||||
const size_t vertexCount = vertices.size();
|
||||
// make it easy to nicely wrap indices
|
||||
const size_t safeIndex = i + vertexCount;
|
||||
|
||||
if (vertexCount < 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const size_t endIndex = (safeIndex - 1) % vertexCount;
|
||||
const size_t beginIndex = (safeIndex + 2) % vertexCount;
|
||||
|
||||
for (size_t i = 0; i < vertexCount; ++i)
|
||||
for (size_t j = beginIndex; j != endIndex; j = (j + 1) % vertexCount)
|
||||
{
|
||||
if (Signed2DTriangleArea(vertices[i], vertices[(i + 1) % vertexCount], vertices[(i + 2) % vertexCount]) < 0.0f)
|
||||
const float distSq = ShortestDistanceSqSegmentSegment(
|
||||
vertices[i],
|
||||
vertices[(i + 1) % vertexCount],
|
||||
vertices[j],
|
||||
vertices[(j + 1) % vertexCount]
|
||||
);
|
||||
|
||||
if (distSq < epsilonSq)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace Geometry2DUtils
|
||||
} // namespace AZ
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsConvex(const AZStd::vector<AZ::Vector2>& vertices)
|
||||
{
|
||||
const size_t vertexCount = vertices.size();
|
||||
|
||||
if (vertexCount < 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < vertexCount; ++i)
|
||||
{
|
||||
if (Signed2DTriangleArea(vertices[i], vertices[(i + 1) % vertexCount], vertices[(i + 2) % vertexCount]) < 0.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace AZ::Geometry2DUtils
|
||||
|
||||
@@ -14,28 +14,26 @@
|
||||
|
||||
#include <string.h> // for memset
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::SfmtInternal
|
||||
{
|
||||
namespace SfmtInternal
|
||||
{
|
||||
static const int N32 = N * 4;
|
||||
static const int N64 = N * 2;
|
||||
static const int POS1 = 122;
|
||||
static const int SL1 = 18;
|
||||
static const int SR1 = 11;
|
||||
static const int SL2 = 1;
|
||||
static const int SR2 = 1;
|
||||
static const unsigned int MSK1 = 0xdfffffefU;
|
||||
static const unsigned int MSK2 = 0xddfecb7fU;
|
||||
static const unsigned int MSK3 = 0xbffaffffU;
|
||||
static const unsigned int MSK4 = 0xbffffff6U;
|
||||
static const unsigned int PARITY1 = 0x00000001U;
|
||||
static const unsigned int PARITY2 = 0x00000000U;
|
||||
static const unsigned int PARITY3 = 0x00000000U;
|
||||
static const unsigned int PARITY4 = 0x13c9e684U;
|
||||
static const int N32 = N * 4;
|
||||
static const int N64 = N * 2;
|
||||
static const int POS1 = 122;
|
||||
static const int SL1 = 18;
|
||||
static const int SR1 = 11;
|
||||
static const int SL2 = 1;
|
||||
static const int SR2 = 1;
|
||||
static const unsigned int MSK1 = 0xdfffffefU;
|
||||
static const unsigned int MSK2 = 0xddfecb7fU;
|
||||
static const unsigned int MSK3 = 0xbffaffffU;
|
||||
static const unsigned int MSK4 = 0xbffffff6U;
|
||||
static const unsigned int PARITY1 = 0x00000001U;
|
||||
static const unsigned int PARITY2 = 0x00000000U;
|
||||
static const unsigned int PARITY3 = 0x00000000U;
|
||||
static const unsigned int PARITY4 = 0x13c9e684U;
|
||||
|
||||
/** a parity check vector which certificate the period of 2^{MEXP} */
|
||||
static unsigned int parity[4] = {PARITY1, PARITY2, PARITY3, PARITY4};
|
||||
/** a parity check vector which certificate the period of 2^{MEXP} */
|
||||
static unsigned int parity[4] = {PARITY1, PARITY2, PARITY3, PARITY4};
|
||||
|
||||
#ifdef ONLY64
|
||||
# define idxof(_i) (_i ^ 1)
|
||||
@@ -45,259 +43,257 @@ namespace AZ
|
||||
|
||||
|
||||
#if AZ_TRAIT_USE_PLATFORM_SIMD_SSE
|
||||
/**
|
||||
* This function represents the recursion formula.
|
||||
* @param a a 128-bit part of the internal state array
|
||||
* @param b a 128-bit part of the internal state array
|
||||
* @param c a 128-bit part of the internal state array
|
||||
* @param d a 128-bit part of the internal state array
|
||||
* @param mask 128-bit mask
|
||||
* @return output
|
||||
*/
|
||||
AZ_FORCE_INLINE static Simd::Vec4::Int32Type simd_recursion(Simd::Vec4::Int32Type* a, Simd::Vec4::Int32Type* b, Simd::Vec4::Int32Type c, Simd::Vec4::Int32Type d, Simd::Vec4::Int32Type mask)
|
||||
/**
|
||||
* This function represents the recursion formula.
|
||||
* @param a a 128-bit part of the internal state array
|
||||
* @param b a 128-bit part of the internal state array
|
||||
* @param c a 128-bit part of the internal state array
|
||||
* @param d a 128-bit part of the internal state array
|
||||
* @param mask 128-bit mask
|
||||
* @return output
|
||||
*/
|
||||
AZ_FORCE_INLINE static Simd::Vec4::Int32Type simd_recursion(Simd::Vec4::Int32Type* a, Simd::Vec4::Int32Type* b, Simd::Vec4::Int32Type c, Simd::Vec4::Int32Type d, Simd::Vec4::Int32Type mask)
|
||||
{
|
||||
Simd::Vec4::Int32Type v, x, y, z;
|
||||
x = *a;
|
||||
y = _mm_srli_epi32(*b, SR1);
|
||||
z = _mm_srli_si128(c, SR2);
|
||||
v = _mm_slli_epi32(d, SL1);
|
||||
z = Simd::Vec4::Xor(z, x);
|
||||
z = Simd::Vec4::Xor(z, v);
|
||||
x = _mm_slli_si128(x, SL2);
|
||||
y = Simd::Vec4::And(y, mask);
|
||||
z = Simd::Vec4::Xor(z, x);
|
||||
z = Simd::Vec4::Xor(z, y);
|
||||
return z;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function fills the internal state array with pseudorandom
|
||||
* integers.
|
||||
*/
|
||||
inline void gen_rand_all(Sfmt& g)
|
||||
{
|
||||
int i;
|
||||
Simd::Vec4::Int32Type r, r1, r2, mask;
|
||||
mask = Simd::Vec4::LoadImmediate((int32_t)MSK4, (int32_t)MSK3, (int32_t)MSK2, (int32_t)MSK1);
|
||||
|
||||
r1 = Simd::Vec4::LoadAligned((const int32_t*)&g.m_sfmt[N - 2].si);
|
||||
r2 = Simd::Vec4::LoadAligned((const int32_t*)&g.m_sfmt[N - 1].si);
|
||||
for (i = 0; i < N - POS1; i++)
|
||||
{
|
||||
Simd::Vec4::Int32Type v, x, y, z;
|
||||
x = *a;
|
||||
y = _mm_srli_epi32(*b, SR1);
|
||||
z = _mm_srli_si128(c, SR2);
|
||||
v = _mm_slli_epi32(d, SL1);
|
||||
z = Simd::Vec4::Xor(z, x);
|
||||
z = Simd::Vec4::Xor(z, v);
|
||||
x = _mm_slli_si128(x, SL2);
|
||||
y = Simd::Vec4::And(y, mask);
|
||||
z = Simd::Vec4::Xor(z, x);
|
||||
z = Simd::Vec4::Xor(z, y);
|
||||
return z;
|
||||
r = simd_recursion(&g.m_sfmt[i].si, &g.m_sfmt[i + POS1].si, r1, r2, mask);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&g.m_sfmt[i].si, r);
|
||||
r1 = r2;
|
||||
r2 = r;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function fills the internal state array with pseudorandom
|
||||
* integers.
|
||||
*/
|
||||
inline void gen_rand_all(Sfmt& g)
|
||||
for (; i < N; i++)
|
||||
{
|
||||
int i;
|
||||
Simd::Vec4::Int32Type r, r1, r2, mask;
|
||||
mask = Simd::Vec4::LoadImmediate((int32_t)MSK4, (int32_t)MSK3, (int32_t)MSK2, (int32_t)MSK1);
|
||||
|
||||
r1 = Simd::Vec4::LoadAligned((const int32_t*)&g.m_sfmt[N - 2].si);
|
||||
r2 = Simd::Vec4::LoadAligned((const int32_t*)&g.m_sfmt[N - 1].si);
|
||||
for (i = 0; i < N - POS1; i++)
|
||||
{
|
||||
r = simd_recursion(&g.m_sfmt[i].si, &g.m_sfmt[i + POS1].si, r1, r2, mask);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&g.m_sfmt[i].si, r);
|
||||
r1 = r2;
|
||||
r2 = r;
|
||||
}
|
||||
for (; i < N; i++)
|
||||
{
|
||||
r = simd_recursion(&g.m_sfmt[i].si, &g.m_sfmt[i + POS1 - N].si, r1, r2, mask);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&g.m_sfmt[i].si, r);
|
||||
r1 = r2;
|
||||
r2 = r;
|
||||
}
|
||||
r = simd_recursion(&g.m_sfmt[i].si, &g.m_sfmt[i + POS1 - N].si, r1, r2, mask);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&g.m_sfmt[i].si, r);
|
||||
r1 = r2;
|
||||
r2 = r;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function fills the user-specified array with pseudorandom
|
||||
* integers.
|
||||
*
|
||||
* @param array an 128-bit array to be filled by pseudorandom numbers.
|
||||
* @param size number of 128-bit pesudorandom numbers to be generated.
|
||||
*/
|
||||
inline void gen_rand_array(Sfmt& g, w128_t* array, int size)
|
||||
/**
|
||||
* This function fills the user-specified array with pseudorandom
|
||||
* integers.
|
||||
*
|
||||
* @param array an 128-bit array to be filled by pseudorandom numbers.
|
||||
* @param size number of 128-bit pesudorandom numbers to be generated.
|
||||
*/
|
||||
inline void gen_rand_array(Sfmt& g, w128_t* array, int size)
|
||||
{
|
||||
int i, j;
|
||||
Simd::Vec4::Int32Type r, r1, r2, mask;
|
||||
mask = Simd::Vec4::LoadImmediate((int32_t)MSK4, (int32_t)MSK3, (int32_t)MSK2, (int32_t)MSK1);
|
||||
|
||||
r1 = Simd::Vec4::LoadAligned((const int32_t*)&g.m_sfmt[N - 2].si);
|
||||
r2 = Simd::Vec4::LoadAligned((const int32_t*)&g.m_sfmt[N - 1].si);
|
||||
for (i = 0; i < N - POS1; i++)
|
||||
{
|
||||
int i, j;
|
||||
Simd::Vec4::Int32Type r, r1, r2, mask;
|
||||
mask = Simd::Vec4::LoadImmediate((int32_t)MSK4, (int32_t)MSK3, (int32_t)MSK2, (int32_t)MSK1);
|
||||
|
||||
r1 = Simd::Vec4::LoadAligned((const int32_t*)&g.m_sfmt[N - 2].si);
|
||||
r2 = Simd::Vec4::LoadAligned((const int32_t*)&g.m_sfmt[N - 1].si);
|
||||
for (i = 0; i < N - POS1; i++)
|
||||
{
|
||||
r = simd_recursion(&g.m_sfmt[i].si, &g.m_sfmt[i + POS1].si, r1, r2, mask);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&array[i].si, r);
|
||||
r1 = r2;
|
||||
r2 = r;
|
||||
}
|
||||
for (; i < N; i++)
|
||||
{
|
||||
r = simd_recursion(&g.m_sfmt[i].si, &array[i + POS1 - N].si, r1, r2, mask);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&array[i].si, r);
|
||||
r1 = r2;
|
||||
r2 = r;
|
||||
}
|
||||
/* main loop */
|
||||
for (; i < size - N; i++)
|
||||
{
|
||||
r = simd_recursion(&array[i - N].si, &array[i + POS1 - N].si, r1, r2, mask);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&array[i].si, r);
|
||||
r1 = r2;
|
||||
r2 = r;
|
||||
}
|
||||
for (j = 0; j < 2 * N - size; j++)
|
||||
{
|
||||
r = Simd::Vec4::LoadAligned((const int32_t*)&array[j + size - N].si);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&g.m_sfmt[j].si, r);
|
||||
}
|
||||
for (; i < size; i++)
|
||||
{
|
||||
r = simd_recursion(&array[i - N].si, &array[i + POS1 - N].si, r1, r2, mask);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&array[i].si, r);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&g.m_sfmt[j++].si, r);
|
||||
r1 = r2;
|
||||
r2 = r;
|
||||
}
|
||||
r = simd_recursion(&g.m_sfmt[i].si, &g.m_sfmt[i + POS1].si, r1, r2, mask);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&array[i].si, r);
|
||||
r1 = r2;
|
||||
r2 = r;
|
||||
}
|
||||
for (; i < N; i++)
|
||||
{
|
||||
r = simd_recursion(&g.m_sfmt[i].si, &array[i + POS1 - N].si, r1, r2, mask);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&array[i].si, r);
|
||||
r1 = r2;
|
||||
r2 = r;
|
||||
}
|
||||
/* main loop */
|
||||
for (; i < size - N; i++)
|
||||
{
|
||||
r = simd_recursion(&array[i - N].si, &array[i + POS1 - N].si, r1, r2, mask);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&array[i].si, r);
|
||||
r1 = r2;
|
||||
r2 = r;
|
||||
}
|
||||
for (j = 0; j < 2 * N - size; j++)
|
||||
{
|
||||
r = Simd::Vec4::LoadAligned((const int32_t*)&array[j + size - N].si);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&g.m_sfmt[j].si, r);
|
||||
}
|
||||
for (; i < size; i++)
|
||||
{
|
||||
r = simd_recursion(&array[i - N].si, &array[i + POS1 - N].si, r1, r2, mask);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&array[i].si, r);
|
||||
Simd::Vec4::StoreAligned((int32_t*)&g.m_sfmt[j++].si, r);
|
||||
r1 = r2;
|
||||
r2 = r;
|
||||
}
|
||||
}
|
||||
#else
|
||||
inline void rshift128(w128_t* out, w128_t const* in, int shift)
|
||||
inline void rshift128(w128_t* out, w128_t const* in, int shift)
|
||||
{
|
||||
AZ::u64 th, tl, oh, ol;
|
||||
#ifdef ONLY64
|
||||
th = ((AZ::u64)in->u[2] << 32) | ((AZ::u64)in->u[3]);
|
||||
tl = ((AZ::u64)in->u[0] << 32) | ((AZ::u64)in->u[1]);
|
||||
|
||||
oh = th >> (shift * 8);
|
||||
ol = tl >> (shift * 8);
|
||||
ol |= th << (64 - shift * 8);
|
||||
out->u[0] = (AZ::u32)(ol >> 32);
|
||||
out->u[1] = (AZ::u32)ol;
|
||||
out->u[2] = (AZ::u32)(oh >> 32);
|
||||
out->u[3] = (AZ::u32)oh;
|
||||
#else
|
||||
th = ((AZ::u64)in->u[3] << 32) | ((AZ::u64)in->u[2]);
|
||||
tl = ((AZ::u64)in->u[1] << 32) | ((AZ::u64)in->u[0]);
|
||||
|
||||
oh = th >> (shift * 8);
|
||||
ol = tl >> (shift * 8);
|
||||
ol |= th << (64 - shift * 8);
|
||||
out->u[1] = (AZ::u32)(ol >> 32);
|
||||
out->u[0] = (AZ::u32)ol;
|
||||
out->u[3] = (AZ::u32)(oh >> 32);
|
||||
out->u[2] = (AZ::u32)oh;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void lshift128(w128_t* out, w128_t const* in, int shift)
|
||||
{
|
||||
AZ::u64 th, tl, oh, ol;
|
||||
#ifdef ONLY64
|
||||
th = ((AZ::u64)in->u[2] << 32) | ((AZ::u64)in->u[3]);
|
||||
tl = ((AZ::u64)in->u[0] << 32) | ((AZ::u64)in->u[1]);
|
||||
|
||||
oh = th << (shift * 8);
|
||||
ol = tl << (shift * 8);
|
||||
oh |= tl >> (64 - shift * 8);
|
||||
out->u[0] = (AZ::u32)(ol >> 32);
|
||||
out->u[1] = (AZ::u32)ol;
|
||||
out->u[2] = (AZ::u32)(oh >> 32);
|
||||
out->u[3] = (AZ::u32)oh;
|
||||
#else
|
||||
th = ((AZ::u64)in->u[3] << 32) | ((AZ::u64)in->u[2]);
|
||||
tl = ((AZ::u64)in->u[1] << 32) | ((AZ::u64)in->u[0]);
|
||||
|
||||
oh = th << (shift * 8);
|
||||
ol = tl << (shift * 8);
|
||||
oh |= tl >> (64 - shift * 8);
|
||||
out->u[1] = (AZ::u32)(ol >> 32);
|
||||
out->u[0] = (AZ::u32)ol;
|
||||
out->u[3] = (AZ::u32)(oh >> 32);
|
||||
out->u[2] = (AZ::u32)oh;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void do_recursion(w128_t* r, w128_t* a, w128_t* b, w128_t* c, w128_t* d)
|
||||
{
|
||||
w128_t x;
|
||||
w128_t y;
|
||||
lshift128(&x, a, SL2);
|
||||
rshift128(&y, c, SR2);
|
||||
#ifdef ONLY64
|
||||
r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SR1) & MSK2) ^ y.u[0] ^ (d->u[0] << SL1);
|
||||
r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SR1) & MSK1) ^ y.u[1] ^ (d->u[1] << SL1);
|
||||
r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SR1) & MSK4) ^ y.u[2] ^ (d->u[2] << SL1);
|
||||
r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SR1) & MSK3) ^ y.u[3] ^ (d->u[3] << SL1);
|
||||
#else
|
||||
r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SR1) & MSK1) ^ y.u[0] ^ (d->u[0] << SL1);
|
||||
r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SR1) & MSK2) ^ y.u[1] ^ (d->u[1] << SL1);
|
||||
r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SR1) & MSK3) ^ y.u[2] ^ (d->u[2] << SL1);
|
||||
r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SR1) & MSK4) ^ y.u[3] ^ (d->u[3] << SL1);
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* This function fills the internal state array with pseudorandom
|
||||
* integers.
|
||||
*/
|
||||
inline void gen_rand_all(Sfmt& g)
|
||||
{
|
||||
int i;
|
||||
w128_t* r1, * r2;
|
||||
|
||||
r1 = &g.m_sfmt[N - 2];
|
||||
r2 = &g.m_sfmt[N - 1];
|
||||
for (i = 0; i < N - POS1; i++)
|
||||
{
|
||||
AZ::u64 th, tl, oh, ol;
|
||||
#ifdef ONLY64
|
||||
th = ((AZ::u64)in->u[2] << 32) | ((AZ::u64)in->u[3]);
|
||||
tl = ((AZ::u64)in->u[0] << 32) | ((AZ::u64)in->u[1]);
|
||||
|
||||
oh = th >> (shift * 8);
|
||||
ol = tl >> (shift * 8);
|
||||
ol |= th << (64 - shift * 8);
|
||||
out->u[0] = (AZ::u32)(ol >> 32);
|
||||
out->u[1] = (AZ::u32)ol;
|
||||
out->u[2] = (AZ::u32)(oh >> 32);
|
||||
out->u[3] = (AZ::u32)oh;
|
||||
#else
|
||||
th = ((AZ::u64)in->u[3] << 32) | ((AZ::u64)in->u[2]);
|
||||
tl = ((AZ::u64)in->u[1] << 32) | ((AZ::u64)in->u[0]);
|
||||
|
||||
oh = th >> (shift * 8);
|
||||
ol = tl >> (shift * 8);
|
||||
ol |= th << (64 - shift * 8);
|
||||
out->u[1] = (AZ::u32)(ol >> 32);
|
||||
out->u[0] = (AZ::u32)ol;
|
||||
out->u[3] = (AZ::u32)(oh >> 32);
|
||||
out->u[2] = (AZ::u32)oh;
|
||||
#endif
|
||||
do_recursion(&g.m_sfmt[i], &g.m_sfmt[i], &g.m_sfmt[i + POS1], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &g.m_sfmt[i];
|
||||
}
|
||||
|
||||
inline void lshift128(w128_t* out, w128_t const* in, int shift)
|
||||
for (; i < N; i++)
|
||||
{
|
||||
AZ::u64 th, tl, oh, ol;
|
||||
#ifdef ONLY64
|
||||
th = ((AZ::u64)in->u[2] << 32) | ((AZ::u64)in->u[3]);
|
||||
tl = ((AZ::u64)in->u[0] << 32) | ((AZ::u64)in->u[1]);
|
||||
|
||||
oh = th << (shift * 8);
|
||||
ol = tl << (shift * 8);
|
||||
oh |= tl >> (64 - shift * 8);
|
||||
out->u[0] = (AZ::u32)(ol >> 32);
|
||||
out->u[1] = (AZ::u32)ol;
|
||||
out->u[2] = (AZ::u32)(oh >> 32);
|
||||
out->u[3] = (AZ::u32)oh;
|
||||
#else
|
||||
th = ((AZ::u64)in->u[3] << 32) | ((AZ::u64)in->u[2]);
|
||||
tl = ((AZ::u64)in->u[1] << 32) | ((AZ::u64)in->u[0]);
|
||||
|
||||
oh = th << (shift * 8);
|
||||
ol = tl << (shift * 8);
|
||||
oh |= tl >> (64 - shift * 8);
|
||||
out->u[1] = (AZ::u32)(ol >> 32);
|
||||
out->u[0] = (AZ::u32)ol;
|
||||
out->u[3] = (AZ::u32)(oh >> 32);
|
||||
out->u[2] = (AZ::u32)oh;
|
||||
#endif
|
||||
do_recursion(&g.m_sfmt[i], &g.m_sfmt[i], &g.m_sfmt[i + POS1 - N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &g.m_sfmt[i];
|
||||
}
|
||||
}
|
||||
|
||||
inline void do_recursion(w128_t* r, w128_t* a, w128_t* b, w128_t* c, w128_t* d)
|
||||
/**
|
||||
* This function fills the user-specified array with pseudorandom
|
||||
* integers.
|
||||
*
|
||||
* @param array an 128-bit array to be filled by pseudorandom numbers.
|
||||
* @param size number of 128-bit pseudorandom numbers to be generated.
|
||||
*/
|
||||
inline void gen_rand_array(Sfmt& g, w128_t* array, int size)
|
||||
{
|
||||
int i, j;
|
||||
w128_t* r1, * r2;
|
||||
|
||||
r1 = &g.m_sfmt[N - 2];
|
||||
r2 = &g.m_sfmt[N - 1];
|
||||
for (i = 0; i < N - POS1; i++)
|
||||
{
|
||||
w128_t x;
|
||||
w128_t y;
|
||||
lshift128(&x, a, SL2);
|
||||
rshift128(&y, c, SR2);
|
||||
#ifdef ONLY64
|
||||
r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SR1) & MSK2) ^ y.u[0] ^ (d->u[0] << SL1);
|
||||
r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SR1) & MSK1) ^ y.u[1] ^ (d->u[1] << SL1);
|
||||
r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SR1) & MSK4) ^ y.u[2] ^ (d->u[2] << SL1);
|
||||
r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SR1) & MSK3) ^ y.u[3] ^ (d->u[3] << SL1);
|
||||
#else
|
||||
r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SR1) & MSK1) ^ y.u[0] ^ (d->u[0] << SL1);
|
||||
r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SR1) & MSK2) ^ y.u[1] ^ (d->u[1] << SL1);
|
||||
r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SR1) & MSK3) ^ y.u[2] ^ (d->u[2] << SL1);
|
||||
r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SR1) & MSK4) ^ y.u[3] ^ (d->u[3] << SL1);
|
||||
#endif
|
||||
do_recursion(&array[i], &g.m_sfmt[i], &g.m_sfmt[i + POS1], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
}
|
||||
/**
|
||||
* This function fills the internal state array with pseudorandom
|
||||
* integers.
|
||||
*/
|
||||
inline void gen_rand_all(Sfmt& g)
|
||||
for (; i < N; i++)
|
||||
{
|
||||
int i;
|
||||
w128_t* r1, * r2;
|
||||
|
||||
r1 = &g.m_sfmt[N - 2];
|
||||
r2 = &g.m_sfmt[N - 1];
|
||||
for (i = 0; i < N - POS1; i++)
|
||||
{
|
||||
do_recursion(&g.m_sfmt[i], &g.m_sfmt[i], &g.m_sfmt[i + POS1], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &g.m_sfmt[i];
|
||||
}
|
||||
for (; i < N; i++)
|
||||
{
|
||||
do_recursion(&g.m_sfmt[i], &g.m_sfmt[i], &g.m_sfmt[i + POS1 - N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &g.m_sfmt[i];
|
||||
}
|
||||
do_recursion(&array[i], &g.m_sfmt[i], &array[i + POS1 - N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* This function fills the user-specified array with pseudorandom
|
||||
* integers.
|
||||
*
|
||||
* @param array an 128-bit array to be filled by pseudorandom numbers.
|
||||
* @param size number of 128-bit pseudorandom numbers to be generated.
|
||||
*/
|
||||
inline void gen_rand_array(Sfmt& g, w128_t* array, int size)
|
||||
for (; i < size - N; i++)
|
||||
{
|
||||
int i, j;
|
||||
w128_t* r1, * r2;
|
||||
|
||||
r1 = &g.m_sfmt[N - 2];
|
||||
r2 = &g.m_sfmt[N - 1];
|
||||
for (i = 0; i < N - POS1; i++)
|
||||
{
|
||||
do_recursion(&array[i], &g.m_sfmt[i], &g.m_sfmt[i + POS1], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
}
|
||||
for (; i < N; i++)
|
||||
{
|
||||
do_recursion(&array[i], &g.m_sfmt[i], &array[i + POS1 - N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
}
|
||||
for (; i < size - N; i++)
|
||||
{
|
||||
do_recursion(&array[i], &array[i - N], &array[i + POS1 - N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
}
|
||||
for (j = 0; j < 2 * N - size; j++)
|
||||
{
|
||||
g.m_sfmt[j] = array[j + size - N];
|
||||
}
|
||||
for (; i < size; i++, j++)
|
||||
{
|
||||
do_recursion(&array[i], &array[i - N], &array[i + POS1 - N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
g.m_sfmt[j] = array[i];
|
||||
}
|
||||
do_recursion(&array[i], &array[i - N], &array[i + POS1 - N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
}
|
||||
for (j = 0; j < 2 * N - size; j++)
|
||||
{
|
||||
g.m_sfmt[j] = array[j + size - N];
|
||||
}
|
||||
for (; i < size; i++, j++)
|
||||
{
|
||||
do_recursion(&array[i], &array[i - N], &array[i + POS1 - N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
g.m_sfmt[j] = array[i];
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
} // SmftInternal
|
||||
} // AZ
|
||||
|
||||
} // namespace AZ::SfmtInternal
|
||||
|
||||
using namespace AZ;
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace AZ
|
||||
static constexpr int ValidUuidStringLength = 32; /// Number of characters (data only, no extra formatting) in a valid UUID string
|
||||
static const size_t MaxStringBuffer = 39; /// 32 Uuid + 4 dashes + 2 brackets + 1 terminate
|
||||
|
||||
Uuid() {}
|
||||
Uuid() = default;
|
||||
Uuid(const char* string, size_t stringLength = 0) { *this = CreateString(string, stringLength); }
|
||||
|
||||
static Uuid CreateNull();
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace AZ
|
||||
|
||||
Vector2() = default;
|
||||
|
||||
Vector2(const Vector2& v);
|
||||
Vector2(const Vector2& v) = default;
|
||||
|
||||
//! Constructs vector with all components set to the same specified value.
|
||||
explicit Vector2(float x);
|
||||
|
||||
@@ -8,13 +8,6 @@
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
AZ_MATH_INLINE Vector2::Vector2(const Vector2& v)
|
||||
: m_value(v.m_value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Vector2::Vector2(float x)
|
||||
: m_value(Simd::Vec2::Splat(x))
|
||||
{
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace AZ
|
||||
void Set(float x, float y, float z);
|
||||
|
||||
//! Sets components from an array of 3 floats in xyz order.
|
||||
void Set(float values[]);
|
||||
void Set(const float values[]);
|
||||
|
||||
//! Indexed access using operator(), just for convenience.
|
||||
float operator()(int32_t index) const;
|
||||
|
||||
@@ -186,7 +186,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE void Vector3::Set(float values[])
|
||||
AZ_MATH_INLINE void Vector3::Set(const float values[])
|
||||
{
|
||||
m_value = Simd::Vec3::LoadImmediate(values[0], values[1], values[2]);
|
||||
}
|
||||
|
||||
@@ -20,44 +20,42 @@
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
|
||||
using namespace AZ;
|
||||
|
||||
#if !defined(RELEASE) && !defined(AZCORE_MEMORY_ENABLE_OVERRIDES)
|
||||
# define AZCORE_MEMORY_ENABLE_OVERRIDES
|
||||
#endif
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Internal
|
||||
{
|
||||
namespace Internal
|
||||
struct AMStringHasher
|
||||
{
|
||||
struct AMStringHasher
|
||||
using is_transparent = void;
|
||||
template<typename ConvertibleToStringView>
|
||||
size_t operator()(const ConvertibleToStringView& key)
|
||||
{
|
||||
using is_transparent = void;
|
||||
template<typename ConvertibleToStringView>
|
||||
size_t operator()(const ConvertibleToStringView& key)
|
||||
{
|
||||
return AZStd::hash<AZStd::string_view>{}(key);
|
||||
}
|
||||
};
|
||||
using AMString = AZStd::basic_string<char, AZStd::char_traits<char>, AZStdIAllocator>;
|
||||
using AllocatorNameMap = AZStd::unordered_map<AMString, IAllocator*, AMStringHasher, AZStd::equal_to<>, AZStdIAllocator>;
|
||||
using AllocatorRemappings = AZStd::unordered_map<AMString, AMString, AMStringHasher, AZStd::equal_to<>, AZStdIAllocator>;
|
||||
return AZStd::hash<AZStd::string_view>{}(key);
|
||||
}
|
||||
};
|
||||
using AMString = AZStd::basic_string<char, AZStd::char_traits<char>, AZStdIAllocator>;
|
||||
using AllocatorNameMap = AZStd::unordered_map<AMString, IAllocator*, AMStringHasher, AZStd::equal_to<>, AZStdIAllocator>;
|
||||
using AllocatorRemappings = AZStd::unordered_map<AMString, AMString, AMStringHasher, AZStd::equal_to<>, AZStdIAllocator>;
|
||||
|
||||
// For allocators that are created before we have an environment, we keep some module-local data for them so that we can register them
|
||||
// properly once the environment is attached.
|
||||
struct PreEnvironmentAttachData
|
||||
{
|
||||
static const int MAX_UNREGISTERED_ALLOCATORS = 8;
|
||||
AZStd::mutex m_mutex;
|
||||
MallocSchema m_mallocSchema;
|
||||
IAllocator* m_unregisteredAllocators[MAX_UNREGISTERED_ALLOCATORS];
|
||||
int m_unregisteredAllocatorCount = 0;
|
||||
};
|
||||
// For allocators that are created before we have an environment, we keep some module-local data for them so that we can register them
|
||||
// properly once the environment is attached.
|
||||
struct PreEnvironmentAttachData
|
||||
{
|
||||
static const int MAX_UNREGISTERED_ALLOCATORS = 8;
|
||||
AZStd::mutex m_mutex;
|
||||
MallocSchema m_mallocSchema;
|
||||
IAllocator* m_unregisteredAllocators[MAX_UNREGISTERED_ALLOCATORS];
|
||||
int m_unregisteredAllocatorCount = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct AZ::AllocatorManager::InternalData
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
struct AllocatorManager::InternalData
|
||||
{
|
||||
explicit InternalData(const AZStdIAllocator& alloc)
|
||||
: m_allocatorMap(alloc)
|
||||
@@ -69,13 +67,13 @@ struct AZ::AllocatorManager::InternalData
|
||||
Internal::AllocatorRemappings m_remappingsReverse;
|
||||
};
|
||||
|
||||
static AZ::EnvironmentVariable<AllocatorManager> s_allocManager = nullptr;
|
||||
static EnvironmentVariable<AllocatorManager> s_allocManager = nullptr;
|
||||
static AllocatorManager* s_allocManagerDebug = nullptr; // For easier viewing in crash dumps
|
||||
|
||||
/// Returns a module-local instance of data to use for allocators that are created before the environment is attached.
|
||||
static AZ::Internal::PreEnvironmentAttachData& GetPreEnvironmentAttachData()
|
||||
static Internal::PreEnvironmentAttachData& GetPreEnvironmentAttachData()
|
||||
{
|
||||
static AZ::Internal::PreEnvironmentAttachData s_data;
|
||||
static Internal::PreEnvironmentAttachData s_data;
|
||||
|
||||
return s_data;
|
||||
}
|
||||
@@ -131,7 +129,7 @@ AllocatorManager& AllocatorManager::Instance()
|
||||
if (!s_allocManager)
|
||||
{
|
||||
AZ_Assert(Environment::IsReady(), "Environment must be ready before calling Instance()");
|
||||
s_allocManager = AZ::Environment::CreateVariable<AllocatorManager>(AZ_CRC("AZ::AllocatorManager::s_allocManager", 0x6bdd908c));
|
||||
s_allocManager = Environment::CreateVariable<AllocatorManager>(AZ_CRC_CE("AZ::AllocatorManager::s_allocManager"));
|
||||
|
||||
// Register any allocators that were created in this module before we attached to the environment
|
||||
auto& data = GetPreEnvironmentAttachData();
|
||||
@@ -156,9 +154,9 @@ AllocatorManager& AllocatorManager::Instance()
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Create malloc schema using custom AZ_OS_MALLOC allocator.
|
||||
AZ::MallocSchema* AllocatorManager::CreateMallocSchema()
|
||||
MallocSchema* AllocatorManager::CreateMallocSchema()
|
||||
{
|
||||
return static_cast<AZ::MallocSchema*>(new(AZ_OS_MALLOC(sizeof(AZ::MallocSchema), alignof(AZ::MallocSchema))) AZ::MallocSchema());
|
||||
return static_cast<MallocSchema*>(new(AZ_OS_MALLOC(sizeof(MallocSchema), alignof(MallocSchema))) MallocSchema());
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +166,7 @@ AZ::MallocSchema* AllocatorManager::CreateMallocSchema()
|
||||
//=========================================================================
|
||||
AllocatorManager::AllocatorManager()
|
||||
: m_profilingRefcount(0)
|
||||
, m_mallocSchema(CreateMallocSchema(), [](AZ::MallocSchema* schema)
|
||||
, m_mallocSchema(CreateMallocSchema(), [](MallocSchema* schema)
|
||||
{
|
||||
if (schema)
|
||||
{
|
||||
@@ -182,7 +180,7 @@ AllocatorManager::AllocatorManager()
|
||||
m_numAllocators = 0;
|
||||
m_isAllocatorLeaking = false;
|
||||
m_configurationFinalized = false;
|
||||
m_defaultTrackingRecordMode = AZ::Debug::AllocationRecords::RECORD_NO_RECORDS;
|
||||
m_defaultTrackingRecordMode = Debug::AllocationRecords::RECORD_NO_RECORDS;
|
||||
m_data = new (m_mallocSchema->Allocate(sizeof(InternalData), AZStd::alignment_of<InternalData>::value, 0)) InternalData(AZStdIAllocator(m_mallocSchema.get()));
|
||||
}
|
||||
|
||||
@@ -411,12 +409,12 @@ AllocatorManager::RemoveOutOfMemoryListener()
|
||||
// [9/16/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
AllocatorManager::SetTrackingMode(AZ::Debug::AllocationRecords::Mode mode)
|
||||
AllocatorManager::SetTrackingMode(Debug::AllocationRecords::Mode mode)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
|
||||
for (int i = 0; i < m_numAllocators; ++i)
|
||||
{
|
||||
AZ::Debug::AllocationRecords* records = m_allocators[i]->GetRecords();
|
||||
Debug::AllocationRecords* records = m_allocators[i]->GetRecords();
|
||||
if (records)
|
||||
{
|
||||
records->SetMode(mode);
|
||||
@@ -595,31 +593,31 @@ void AllocatorManager::GetAllocatorStats(size_t& allocatedBytes, size_t& capacit
|
||||
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
|
||||
const int allocatorCount = GetNumAllocators();
|
||||
AZStd::unordered_map<AZ::IAllocatorAllocate*, AZ::IAllocator*> existingAllocators;
|
||||
AZStd::unordered_map<AZ::IAllocatorAllocate*, AZ::IAllocator*> sourcesToAllocators;
|
||||
AZStd::unordered_map<IAllocatorAllocate*, IAllocator*> existingAllocators;
|
||||
AZStd::unordered_map<IAllocatorAllocate*, IAllocator*> sourcesToAllocators;
|
||||
|
||||
// Build a mapping of original allocator sources to their allocators
|
||||
for (int i = 0; i < allocatorCount; ++i)
|
||||
{
|
||||
AZ::IAllocator* allocator = GetAllocator(i);
|
||||
IAllocator* allocator = GetAllocator(i);
|
||||
sourcesToAllocators.emplace(allocator->GetOriginalAllocationSource(), allocator);
|
||||
}
|
||||
|
||||
for (int i = 0; i < allocatorCount; ++i)
|
||||
{
|
||||
AZ::IAllocator* allocator = GetAllocator(i);
|
||||
AZ::IAllocatorAllocate* source = allocator->GetAllocationSource();
|
||||
AZ::IAllocatorAllocate* originalSource = allocator->GetOriginalAllocationSource();
|
||||
AZ::IAllocatorAllocate* schema = allocator->GetSchema();
|
||||
AZ::IAllocator* alias = (source != originalSource) ? sourcesToAllocators[source] : nullptr;
|
||||
IAllocator* allocator = GetAllocator(i);
|
||||
IAllocatorAllocate* source = allocator->GetAllocationSource();
|
||||
IAllocatorAllocate* originalSource = allocator->GetOriginalAllocationSource();
|
||||
IAllocatorAllocate* schema = allocator->GetSchema();
|
||||
IAllocator* alias = (source != originalSource) ? sourcesToAllocators[source] : nullptr;
|
||||
|
||||
if (schema && !alias)
|
||||
{
|
||||
// Check to see if this allocator's source maps to another allocator
|
||||
// Need to check both the schema and the allocator itself, as either one might be used as the alias depending on how it's implemented
|
||||
AZStd::array<AZ::IAllocatorAllocate*, 2> checkAllocators = { { schema, allocator->GetAllocationSource() } };
|
||||
AZStd::array<IAllocatorAllocate*, 2> checkAllocators = { { schema, allocator->GetAllocationSource() } };
|
||||
|
||||
for (AZ::IAllocatorAllocate* check : checkAllocators)
|
||||
for (IAllocatorAllocate* check : checkAllocators)
|
||||
{
|
||||
auto existing = existingAllocators.emplace(check, allocator);
|
||||
|
||||
@@ -631,7 +629,7 @@ void AllocatorManager::GetAllocatorStats(size_t& allocatedBytes, size_t& capacit
|
||||
}
|
||||
}
|
||||
|
||||
static const AZ::IAllocator* OS_ALLOCATOR = &AZ::AllocatorInstance<AZ::OSAllocator>::GetAllocator();
|
||||
static const IAllocator* OS_ALLOCATOR = &AllocatorInstance<OSAllocator>::GetAllocator();
|
||||
size_t sourceAllocatedBytes = source->NumAllocatedBytes();
|
||||
size_t sourceCapacityBytes = source->Capacity();
|
||||
|
||||
@@ -742,3 +740,5 @@ AllocatorManager::DebugBreak(void* address, const Debug::AllocationInfo& info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AZ
|
||||
|
||||
@@ -8,223 +8,220 @@
|
||||
|
||||
#include <AzCore/Memory/AllocatorOverrideShim.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Internal
|
||||
{
|
||||
namespace Internal
|
||||
AllocatorOverrideShim* AllocatorOverrideShim::Create(IAllocator* owningAllocator, IAllocatorAllocate* shimAllocationSource)
|
||||
{
|
||||
AllocatorOverrideShim* AllocatorOverrideShim::Create(IAllocator* owningAllocator, IAllocatorAllocate* shimAllocationSource)
|
||||
void* memory = shimAllocationSource->Allocate(sizeof(AllocatorOverrideShim), AZStd::alignment_of<AllocatorOverrideShim>::value, 0);
|
||||
auto result = new (memory) AllocatorOverrideShim(owningAllocator, shimAllocationSource);
|
||||
return result;
|
||||
}
|
||||
|
||||
void AllocatorOverrideShim::Destroy(AllocatorOverrideShim* source)
|
||||
{
|
||||
auto shimAllocationSource = source->m_shimAllocationSource;
|
||||
source->~AllocatorOverrideShim();
|
||||
shimAllocationSource->DeAllocate(source);
|
||||
}
|
||||
|
||||
AllocatorOverrideShim::AllocatorOverrideShim(IAllocator* owningAllocator, IAllocatorAllocate* shimAllocationSource)
|
||||
: m_owningAllocator(owningAllocator)
|
||||
, m_source(owningAllocator->GetOriginalAllocationSource())
|
||||
, m_overridingSource(owningAllocator->GetOriginalAllocationSource())
|
||||
, m_shimAllocationSource(shimAllocationSource)
|
||||
, m_records(typename AllocationSet::hasher(), typename AllocationSet::key_eq(), StdAllocationSrc(shimAllocationSource))
|
||||
{
|
||||
}
|
||||
|
||||
void AllocatorOverrideShim::SetOverride(IAllocatorAllocate* source)
|
||||
{
|
||||
m_overridingSource = source;
|
||||
}
|
||||
|
||||
IAllocatorAllocate* AllocatorOverrideShim::GetOverride() const
|
||||
{
|
||||
return m_overridingSource;
|
||||
}
|
||||
|
||||
bool AllocatorOverrideShim::IsOverridden() const
|
||||
{
|
||||
return m_source != m_overridingSource;
|
||||
}
|
||||
|
||||
bool AllocatorOverrideShim::HasOrphanedAllocations() const
|
||||
{
|
||||
return !m_records.empty();
|
||||
}
|
||||
|
||||
void AllocatorOverrideShim::SetFinalizedConfiguration()
|
||||
{
|
||||
m_finalizedConfiguration = true;
|
||||
}
|
||||
|
||||
typename AllocatorOverrideShim::pointer_type AllocatorOverrideShim::Allocate(size_type byteSize, size_type alignment, int flags, const char* name, const char* fileName, int lineNum, unsigned int suppressStackRecord)
|
||||
{
|
||||
pointer_type ptr = m_overridingSource->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord);
|
||||
|
||||
if (!IsOverridden())
|
||||
{
|
||||
void* memory = shimAllocationSource->Allocate(sizeof(AllocatorOverrideShim), AZStd::alignment_of<AllocatorOverrideShim>::value, 0);
|
||||
auto result = new (memory) AllocatorOverrideShim(owningAllocator, shimAllocationSource);
|
||||
return result;
|
||||
lock_type lock(m_mutex);
|
||||
m_records.insert(ptr); // Record in case we need to orphan this allocation later
|
||||
}
|
||||
|
||||
void AllocatorOverrideShim::Destroy(AllocatorOverrideShim* source)
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void AllocatorOverrideShim::DeAllocate(pointer_type ptr, size_type byteSize, size_type alignment)
|
||||
{
|
||||
IAllocatorAllocate* source = m_overridingSource;
|
||||
bool destroy = false;
|
||||
|
||||
{
|
||||
auto shimAllocationSource = source->m_shimAllocationSource;
|
||||
source->~AllocatorOverrideShim();
|
||||
shimAllocationSource->DeAllocate(source);
|
||||
lock_type lock(m_mutex);
|
||||
|
||||
// Check to see if this came from a prior allocation source
|
||||
if (m_records.erase(ptr) && IsOverridden())
|
||||
{
|
||||
source = m_source;
|
||||
|
||||
if (m_records.empty() && m_finalizedConfiguration)
|
||||
{
|
||||
// All orphaned records are gone; we are no longer needed
|
||||
m_owningAllocator->SetAllocationSource(m_overridingSource);
|
||||
destroy = true; // Must destroy outside the lock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AllocatorOverrideShim::AllocatorOverrideShim(IAllocator* owningAllocator, IAllocatorAllocate* shimAllocationSource)
|
||||
: m_owningAllocator(owningAllocator)
|
||||
, m_source(owningAllocator->GetOriginalAllocationSource())
|
||||
, m_overridingSource(owningAllocator->GetOriginalAllocationSource())
|
||||
, m_shimAllocationSource(shimAllocationSource)
|
||||
, m_records(typename AllocationSet::hasher(), typename AllocationSet::key_eq(), StdAllocationSrc(shimAllocationSource))
|
||||
source->DeAllocate(ptr, byteSize, alignment);
|
||||
|
||||
if (destroy)
|
||||
{
|
||||
Destroy(this);
|
||||
}
|
||||
}
|
||||
|
||||
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::Resize(pointer_type ptr, size_type newSize)
|
||||
{
|
||||
IAllocatorAllocate* source = m_overridingSource;
|
||||
|
||||
if (IsOverridden())
|
||||
{
|
||||
// Determine who owns the allocation
|
||||
lock_type lock(m_mutex);
|
||||
|
||||
if (m_records.count(ptr))
|
||||
{
|
||||
source = m_source;
|
||||
}
|
||||
}
|
||||
|
||||
void AllocatorOverrideShim::SetOverride(IAllocatorAllocate* source)
|
||||
size_t result = source->Resize(ptr, newSize);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
typename AllocatorOverrideShim::pointer_type AllocatorOverrideShim::ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment)
|
||||
{
|
||||
pointer_type newPtr = nullptr;
|
||||
bool useOverride = true;
|
||||
bool destroy = false;
|
||||
|
||||
if (IsOverridden())
|
||||
{
|
||||
m_overridingSource = source;
|
||||
lock_type lock(m_mutex);
|
||||
|
||||
if (m_records.erase(ptr))
|
||||
{
|
||||
// An old allocation needs to be transferred to the new, overriding allocator.
|
||||
useOverride = false; // We'll do the reallocation here
|
||||
size_t oldSize = m_source->AllocationSize(ptr);
|
||||
|
||||
if (newSize)
|
||||
{
|
||||
newPtr = m_overridingSource->Allocate(newSize, newAlignment, 0);
|
||||
memcpy(newPtr, ptr, AZStd::min(newSize, oldSize));
|
||||
}
|
||||
|
||||
m_source->DeAllocate(ptr, oldSize);
|
||||
|
||||
if (m_records.empty() && m_finalizedConfiguration)
|
||||
{
|
||||
// All orphaned records are gone; we are no longer needed
|
||||
m_owningAllocator->SetAllocationSource(m_overridingSource);
|
||||
destroy = true; // Must destroy outside the lock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IAllocatorAllocate* AllocatorOverrideShim::GetOverride() const
|
||||
if (useOverride)
|
||||
{
|
||||
return m_overridingSource;
|
||||
}
|
||||
|
||||
bool AllocatorOverrideShim::IsOverridden() const
|
||||
{
|
||||
return m_source != m_overridingSource;
|
||||
}
|
||||
|
||||
bool AllocatorOverrideShim::HasOrphanedAllocations() const
|
||||
{
|
||||
return !m_records.empty();
|
||||
}
|
||||
|
||||
void AllocatorOverrideShim::SetFinalizedConfiguration()
|
||||
{
|
||||
m_finalizedConfiguration = true;
|
||||
}
|
||||
|
||||
typename AllocatorOverrideShim::pointer_type AllocatorOverrideShim::Allocate(size_type byteSize, size_type alignment, int flags, const char* name, const char* fileName, int lineNum, unsigned int suppressStackRecord)
|
||||
{
|
||||
pointer_type ptr = m_overridingSource->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord);
|
||||
// Default behavior, we weren't deleting an old allocation
|
||||
newPtr = m_overridingSource->ReAllocate(ptr, newSize, newAlignment);
|
||||
|
||||
if (!IsOverridden())
|
||||
{
|
||||
// Still need to do bookkeeping if we haven't been overridden yet
|
||||
lock_type lock(m_mutex);
|
||||
m_records.insert(ptr); // Record in case we need to orphan this allocation later
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void AllocatorOverrideShim::DeAllocate(pointer_type ptr, size_type byteSize, size_type alignment)
|
||||
{
|
||||
IAllocatorAllocate* source = m_overridingSource;
|
||||
bool destroy = false;
|
||||
|
||||
{
|
||||
lock_type lock(m_mutex);
|
||||
|
||||
// Check to see if this came from a prior allocation source
|
||||
if (m_records.erase(ptr) && IsOverridden())
|
||||
{
|
||||
source = m_source;
|
||||
|
||||
if (m_records.empty() && m_finalizedConfiguration)
|
||||
{
|
||||
// All orphaned records are gone; we are no longer needed
|
||||
m_owningAllocator->SetAllocationSource(m_overridingSource);
|
||||
destroy = true; // Must destroy outside the lock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
source->DeAllocate(ptr, byteSize, alignment);
|
||||
|
||||
if (destroy)
|
||||
{
|
||||
Destroy(this);
|
||||
m_records.erase(ptr);
|
||||
m_records.insert(newPtr);
|
||||
}
|
||||
}
|
||||
|
||||
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::Resize(pointer_type ptr, size_type newSize)
|
||||
if (destroy)
|
||||
{
|
||||
IAllocatorAllocate* source = m_overridingSource;
|
||||
|
||||
if (IsOverridden())
|
||||
{
|
||||
// Determine who owns the allocation
|
||||
lock_type lock(m_mutex);
|
||||
|
||||
if (m_records.count(ptr))
|
||||
{
|
||||
source = m_source;
|
||||
}
|
||||
}
|
||||
|
||||
size_t result = source->Resize(ptr, newSize);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
typename AllocatorOverrideShim::pointer_type AllocatorOverrideShim::ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment)
|
||||
{
|
||||
pointer_type newPtr = nullptr;
|
||||
bool useOverride = true;
|
||||
bool destroy = false;
|
||||
|
||||
if (IsOverridden())
|
||||
{
|
||||
lock_type lock(m_mutex);
|
||||
|
||||
if (m_records.erase(ptr))
|
||||
{
|
||||
// An old allocation needs to be transferred to the new, overriding allocator.
|
||||
useOverride = false; // We'll do the reallocation here
|
||||
size_t oldSize = m_source->AllocationSize(ptr);
|
||||
|
||||
if (newSize)
|
||||
{
|
||||
newPtr = m_overridingSource->Allocate(newSize, newAlignment, 0);
|
||||
memcpy(newPtr, ptr, AZStd::min(newSize, oldSize));
|
||||
}
|
||||
|
||||
m_source->DeAllocate(ptr, oldSize);
|
||||
|
||||
if (m_records.empty() && m_finalizedConfiguration)
|
||||
{
|
||||
// All orphaned records are gone; we are no longer needed
|
||||
m_owningAllocator->SetAllocationSource(m_overridingSource);
|
||||
destroy = true; // Must destroy outside the lock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (useOverride)
|
||||
{
|
||||
// Default behavior, we weren't deleting an old allocation
|
||||
newPtr = m_overridingSource->ReAllocate(ptr, newSize, newAlignment);
|
||||
|
||||
if (!IsOverridden())
|
||||
{
|
||||
// Still need to do bookkeeping if we haven't been overridden yet
|
||||
lock_type lock(m_mutex);
|
||||
m_records.erase(ptr);
|
||||
m_records.insert(newPtr);
|
||||
}
|
||||
}
|
||||
|
||||
if (destroy)
|
||||
{
|
||||
Destroy(this);
|
||||
}
|
||||
|
||||
return newPtr;
|
||||
}
|
||||
|
||||
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::AllocationSize(pointer_type ptr)
|
||||
{
|
||||
IAllocatorAllocate* source = m_overridingSource;
|
||||
|
||||
if (IsOverridden())
|
||||
{
|
||||
// Determine who owns the allocation
|
||||
lock_type lock(m_mutex);
|
||||
|
||||
if (m_records.count(ptr))
|
||||
{
|
||||
source = m_source;
|
||||
}
|
||||
}
|
||||
|
||||
return source->AllocationSize(ptr);
|
||||
}
|
||||
|
||||
void AllocatorOverrideShim::GarbageCollect()
|
||||
{
|
||||
m_source->GarbageCollect();
|
||||
}
|
||||
|
||||
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::NumAllocatedBytes() const
|
||||
{
|
||||
return m_source->NumAllocatedBytes();
|
||||
}
|
||||
|
||||
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::Capacity() const
|
||||
{
|
||||
return m_source->Capacity();
|
||||
}
|
||||
|
||||
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::GetMaxAllocationSize() const
|
||||
{
|
||||
return m_source->GetMaxAllocationSize();
|
||||
}
|
||||
|
||||
auto AllocatorOverrideShim::GetMaxContiguousAllocationSize() const -> size_type
|
||||
{
|
||||
return m_source->GetMaxContiguousAllocationSize();
|
||||
}
|
||||
|
||||
IAllocatorAllocate* AllocatorOverrideShim::GetSubAllocator()
|
||||
{
|
||||
return m_source->GetSubAllocator();
|
||||
Destroy(this);
|
||||
}
|
||||
|
||||
return newPtr;
|
||||
}
|
||||
}
|
||||
|
||||
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::AllocationSize(pointer_type ptr)
|
||||
{
|
||||
IAllocatorAllocate* source = m_overridingSource;
|
||||
|
||||
if (IsOverridden())
|
||||
{
|
||||
// Determine who owns the allocation
|
||||
lock_type lock(m_mutex);
|
||||
|
||||
if (m_records.count(ptr))
|
||||
{
|
||||
source = m_source;
|
||||
}
|
||||
}
|
||||
|
||||
return source->AllocationSize(ptr);
|
||||
}
|
||||
|
||||
void AllocatorOverrideShim::GarbageCollect()
|
||||
{
|
||||
m_source->GarbageCollect();
|
||||
}
|
||||
|
||||
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::NumAllocatedBytes() const
|
||||
{
|
||||
return m_source->NumAllocatedBytes();
|
||||
}
|
||||
|
||||
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::Capacity() const
|
||||
{
|
||||
return m_source->Capacity();
|
||||
}
|
||||
|
||||
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::GetMaxAllocationSize() const
|
||||
{
|
||||
return m_source->GetMaxAllocationSize();
|
||||
}
|
||||
|
||||
auto AllocatorOverrideShim::GetMaxContiguousAllocationSize() const -> size_type
|
||||
{
|
||||
return m_source->GetMaxContiguousAllocationSize();
|
||||
}
|
||||
|
||||
IAllocatorAllocate* AllocatorOverrideShim::GetSubAllocator()
|
||||
{
|
||||
return m_source->GetSubAllocator();
|
||||
}
|
||||
|
||||
} // namespace AZ::Internal
|
||||
|
||||
@@ -140,8 +140,8 @@ namespace AZ {
|
||||
class const_iterator;
|
||||
class iterator
|
||||
{
|
||||
typedef T& reference;
|
||||
typedef T* pointer;
|
||||
using reference = T&;
|
||||
using pointer = T*;
|
||||
friend class const_iterator;
|
||||
T* mPtr;
|
||||
public:
|
||||
@@ -171,8 +171,8 @@ namespace AZ {
|
||||
|
||||
class const_iterator
|
||||
{
|
||||
typedef const T& reference;
|
||||
typedef const T* pointer;
|
||||
using reference = const T &;
|
||||
using pointer = const T *;
|
||||
const T* mPtr;
|
||||
public:
|
||||
const_iterator()
|
||||
@@ -327,7 +327,7 @@ namespace AZ {
|
||||
uint64_t mSizeAndFlags;
|
||||
|
||||
public:
|
||||
typedef block_header* block_ptr;
|
||||
using block_ptr = block_header *;
|
||||
size_t size() const { return mSizeAndFlags & ~BL_FLAG_MASK; }
|
||||
block_ptr next() const {return (block_ptr)((char*)mem() + size()); }
|
||||
block_ptr prev() const {return mPrev; }
|
||||
@@ -415,7 +415,7 @@ namespace AZ {
|
||||
void dec_ref() { HPPA_ASSERT(mUseCount > 0); mUseCount--; }
|
||||
bool check_marker(size_t marker) const { return mMarker == (marker ^ ((size_t)this)); }
|
||||
};
|
||||
typedef intrusive_list<page> page_list;
|
||||
using page_list = intrusive_list<page>;
|
||||
class bucket
|
||||
{
|
||||
page_list mPageList;
|
||||
|
||||
@@ -11,149 +11,168 @@
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
|
||||
namespace AZ::Internal
|
||||
{
|
||||
struct Header
|
||||
{
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
};
|
||||
} // namespace AZ::Internal
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Internal
|
||||
//---------------------------------------------------------------------
|
||||
// MallocSchema methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
MallocSchema::MallocSchema(const Descriptor& desc)
|
||||
: m_bytesAllocated(0)
|
||||
{
|
||||
struct Header
|
||||
if (desc.m_useAZMalloc)
|
||||
{
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
};
|
||||
static const int DEFAULT_ALIGNMENT = sizeof(void*) * 2; // Default malloc alignment
|
||||
|
||||
m_mallocFn = [](size_t byteSize)
|
||||
{
|
||||
return AZ_OS_MALLOC(byteSize, DEFAULT_ALIGNMENT);
|
||||
};
|
||||
m_freeFn = [](void* ptr)
|
||||
{
|
||||
AZ_OS_FREE(ptr);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
m_mallocFn = &malloc;
|
||||
m_freeFn = &free;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// MallocSchema methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
AZ::MallocSchema::MallocSchema(const Descriptor& desc) :
|
||||
m_bytesAllocated(0)
|
||||
{
|
||||
if (desc.m_useAZMalloc)
|
||||
MallocSchema::~MallocSchema()
|
||||
{
|
||||
static const int DEFAULT_ALIGNMENT = sizeof(void*) * 2; // Default malloc alignment
|
||||
|
||||
m_mallocFn = [](size_t byteSize) { return AZ_OS_MALLOC(byteSize, DEFAULT_ALIGNMENT); };
|
||||
m_freeFn = [](void* ptr) { AZ_OS_FREE(ptr); };
|
||||
}
|
||||
else
|
||||
|
||||
MallocSchema::pointer_type MallocSchema::Allocate(
|
||||
size_type byteSize,
|
||||
size_type alignment,
|
||||
int flags,
|
||||
const char* name,
|
||||
const char* fileName,
|
||||
int lineNum,
|
||||
unsigned int suppressStackRecord)
|
||||
{
|
||||
m_mallocFn = &malloc;
|
||||
m_freeFn = &free;
|
||||
(void)flags;
|
||||
(void)name;
|
||||
(void)fileName;
|
||||
(void)lineNum;
|
||||
(void)suppressStackRecord;
|
||||
|
||||
if (!byteSize)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (alignment == 0)
|
||||
{
|
||||
alignment = sizeof(void*) * 2; // Default malloc alignment
|
||||
}
|
||||
|
||||
AZ_Assert(byteSize < 0x100000000ull, "Malloc allocator only allocates up to 4GB");
|
||||
|
||||
size_type required = byteSize + sizeof(Internal::Header) +
|
||||
((alignment > sizeof(double))
|
||||
? alignment
|
||||
: 0); // Malloc will align to a minimum boundary for native objects, so we only pad if aligning to a large value
|
||||
void* data = (*m_mallocFn)(required);
|
||||
void* result = PointerAlignUp(reinterpret_cast<void*>(reinterpret_cast<size_t>(data) + sizeof(Internal::Header)), alignment);
|
||||
Internal::Header* header = PointerAlignDown<Internal::Header>(
|
||||
(Internal::Header*)(reinterpret_cast<size_t>(result) - sizeof(Internal::Header)), AZStd::alignment_of<Internal::Header>::value);
|
||||
|
||||
header->offset = static_cast<uint32_t>(reinterpret_cast<size_type>(result) - reinterpret_cast<size_type>(data));
|
||||
header->size = static_cast<uint32_t>(byteSize);
|
||||
m_bytesAllocated += byteSize;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
AZ::MallocSchema::~MallocSchema()
|
||||
{
|
||||
}
|
||||
void MallocSchema::DeAllocate(pointer_type ptr, size_type byteSize, size_type alignment)
|
||||
{
|
||||
(void)byteSize;
|
||||
(void)alignment;
|
||||
|
||||
AZ::MallocSchema::pointer_type AZ::MallocSchema::Allocate(size_type byteSize, size_type alignment, int flags, const char* name, const char* fileName, int lineNum, unsigned int suppressStackRecord)
|
||||
{
|
||||
(void)flags;
|
||||
(void)name;
|
||||
(void)fileName;
|
||||
(void)lineNum;
|
||||
(void)suppressStackRecord;
|
||||
if (!ptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!byteSize)
|
||||
Internal::Header* header = PointerAlignDown(
|
||||
reinterpret_cast<Internal::Header*>(reinterpret_cast<size_t>(ptr) - sizeof(Internal::Header)),
|
||||
AZStd::alignment_of<Internal::Header>::value);
|
||||
void* freePtr = reinterpret_cast<void*>(reinterpret_cast<size_t>(ptr) - static_cast<size_t>(header->offset));
|
||||
|
||||
m_bytesAllocated -= header->size;
|
||||
(*m_freeFn)(freePtr);
|
||||
}
|
||||
|
||||
MallocSchema::pointer_type MallocSchema::ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment)
|
||||
{
|
||||
void* newPtr = Allocate(newSize, newAlignment, 0);
|
||||
size_t oldSize = AllocationSize(ptr);
|
||||
|
||||
memcpy(newPtr, ptr, AZStd::min(oldSize, newSize));
|
||||
DeAllocate(ptr, 0, 0);
|
||||
|
||||
return newPtr;
|
||||
}
|
||||
|
||||
MallocSchema::size_type MallocSchema::Resize(pointer_type ptr, size_type newSize)
|
||||
{
|
||||
(void)ptr;
|
||||
(void)newSize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MallocSchema::size_type MallocSchema::AllocationSize(pointer_type ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
Internal::Header* header = PointerAlignDown(
|
||||
reinterpret_cast<Internal::Header*>(reinterpret_cast<size_t>(ptr) - sizeof(Internal::Header)),
|
||||
AZStd::alignment_of<Internal::Header>::value);
|
||||
return header->size;
|
||||
}
|
||||
|
||||
MallocSchema::size_type MallocSchema::NumAllocatedBytes() const
|
||||
{
|
||||
return m_bytesAllocated;
|
||||
}
|
||||
|
||||
MallocSchema::size_type MallocSchema::Capacity() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
MallocSchema::size_type MallocSchema::GetMaxAllocationSize() const
|
||||
{
|
||||
return 0xFFFFFFFFull;
|
||||
}
|
||||
|
||||
MallocSchema::size_type MallocSchema::GetMaxContiguousAllocationSize() const
|
||||
{
|
||||
return AZ_CORE_MAX_ALLOCATOR_SIZE;
|
||||
}
|
||||
|
||||
IAllocatorAllocate* MallocSchema::GetSubAllocator()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (alignment == 0)
|
||||
void MallocSchema::GarbageCollect()
|
||||
{
|
||||
alignment = sizeof(void*) * 2; // Default malloc alignment
|
||||
}
|
||||
|
||||
AZ_Assert(byteSize < 0x100000000ull, "Malloc allocator only allocates up to 4GB");
|
||||
|
||||
size_type required = byteSize + sizeof(Internal::Header) + ((alignment > sizeof(double)) ? alignment : 0); // Malloc will align to a minimum boundary for native objects, so we only pad if aligning to a large value
|
||||
void* data = (*m_mallocFn)(required);
|
||||
void* result = PointerAlignUp(reinterpret_cast<void*>(reinterpret_cast<size_t>(data) + sizeof(Internal::Header)), alignment);
|
||||
Internal::Header* header = PointerAlignDown<Internal::Header>((Internal::Header*)(reinterpret_cast<size_t>(result) - sizeof(Internal::Header)), AZStd::alignment_of<Internal::Header>::value);
|
||||
|
||||
header->offset = static_cast<uint32_t>(reinterpret_cast<size_type>(result) - reinterpret_cast<size_type>(data));
|
||||
header->size = static_cast<uint32_t>(byteSize);
|
||||
m_bytesAllocated += byteSize;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void AZ::MallocSchema::DeAllocate(pointer_type ptr, size_type byteSize, size_type alignment)
|
||||
{
|
||||
(void)byteSize;
|
||||
(void)alignment;
|
||||
|
||||
if (!ptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Internal::Header* header = PointerAlignDown(reinterpret_cast<Internal::Header*>(reinterpret_cast<size_t>(ptr) - sizeof(Internal::Header)), AZStd::alignment_of<Internal::Header>::value);
|
||||
void* freePtr = reinterpret_cast<void*>(reinterpret_cast<size_t>(ptr) - static_cast<size_t>(header->offset));
|
||||
|
||||
m_bytesAllocated -= header->size;
|
||||
(*m_freeFn)(freePtr);
|
||||
}
|
||||
|
||||
AZ::MallocSchema::pointer_type AZ::MallocSchema::ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment)
|
||||
{
|
||||
void* newPtr = Allocate(newSize, newAlignment, 0);
|
||||
size_t oldSize = AllocationSize(ptr);
|
||||
|
||||
memcpy(newPtr, ptr, AZStd::min(oldSize, newSize));
|
||||
DeAllocate(ptr, 0, 0);
|
||||
|
||||
return newPtr;
|
||||
}
|
||||
|
||||
AZ::MallocSchema::size_type AZ::MallocSchema::Resize(pointer_type ptr, size_type newSize)
|
||||
{
|
||||
(void)ptr;
|
||||
(void)newSize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
AZ::MallocSchema::size_type AZ::MallocSchema::AllocationSize(pointer_type ptr)
|
||||
{
|
||||
size_type result = 0;
|
||||
|
||||
if (ptr)
|
||||
{
|
||||
Internal::Header* header = PointerAlignDown(reinterpret_cast<Internal::Header*>(reinterpret_cast<size_t>(ptr) - sizeof(Internal::Header)), AZStd::alignment_of<Internal::Header>::value);
|
||||
result = header->size;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
AZ::MallocSchema::size_type AZ::MallocSchema::NumAllocatedBytes() const
|
||||
{
|
||||
return m_bytesAllocated;
|
||||
}
|
||||
|
||||
AZ::MallocSchema::size_type AZ::MallocSchema::Capacity() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
AZ::MallocSchema::size_type AZ::MallocSchema::GetMaxAllocationSize() const
|
||||
{
|
||||
return 0xFFFFFFFFull;
|
||||
}
|
||||
|
||||
AZ::MallocSchema::size_type AZ::MallocSchema::GetMaxContiguousAllocationSize() const
|
||||
{
|
||||
return AZ_CORE_MAX_ALLOCATOR_SIZE;
|
||||
}
|
||||
|
||||
AZ::IAllocatorAllocate* AZ::MallocSchema::GetSubAllocator()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AZ::MallocSchema::GarbageCollect()
|
||||
{
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
@@ -15,280 +15,277 @@
|
||||
#include <AzCore/IO/GenericStreams.h>
|
||||
#include <AzCore/Debug/StackTracer.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
//=========================================================================
|
||||
// MemoryDriller
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
MemoryDriller::MemoryDriller(const Descriptor& desc)
|
||||
{
|
||||
//=========================================================================
|
||||
// MemoryDriller
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
MemoryDriller::MemoryDriller(const Descriptor& desc)
|
||||
(void)desc;
|
||||
BusConnect();
|
||||
|
||||
AllocatorManager::Instance().EnterProfilingMode();
|
||||
|
||||
{
|
||||
(void)desc;
|
||||
BusConnect();
|
||||
|
||||
AllocatorManager::Instance().EnterProfilingMode();
|
||||
|
||||
{
|
||||
// Register all allocators that were created before the driller existed
|
||||
auto allocatorLock = AllocatorManager::Instance().LockAllocators();
|
||||
|
||||
for (int i = 0; i < AllocatorManager::Instance().GetNumAllocators(); ++i)
|
||||
{
|
||||
IAllocator* allocator = AllocatorManager::Instance().GetAllocator(i);
|
||||
RegisterAllocator(allocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ~MemoryDriller
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
MemoryDriller::~MemoryDriller()
|
||||
{
|
||||
BusDisconnect();
|
||||
AllocatorManager::Instance().ExitProfilingMode();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Start
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::Start(const Param* params, int numParams)
|
||||
{
|
||||
(void)params;
|
||||
(void)numParams;
|
||||
|
||||
// dump current allocations for all allocators with tracking
|
||||
// Register all allocators that were created before the driller existed
|
||||
auto allocatorLock = AllocatorManager::Instance().LockAllocators();
|
||||
|
||||
for (int i = 0; i < AllocatorManager::Instance().GetNumAllocators(); ++i)
|
||||
{
|
||||
IAllocator* allocator = AllocatorManager::Instance().GetAllocator(i);
|
||||
if (auto records = allocator->GetRecords())
|
||||
{
|
||||
RegisterAllocatorOutput(allocator);
|
||||
const AllocationRecordsType& allocMap = records->GetMap();
|
||||
for (AllocationRecordsType::const_iterator allocIt = allocMap.begin(); allocIt != allocMap.end(); ++allocIt)
|
||||
{
|
||||
RegisterAllocationOutput(allocator, allocIt->first, &allocIt->second);
|
||||
}
|
||||
}
|
||||
RegisterAllocator(allocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Stop
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::Stop()
|
||||
//=========================================================================
|
||||
// ~MemoryDriller
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
MemoryDriller::~MemoryDriller()
|
||||
{
|
||||
BusDisconnect();
|
||||
AllocatorManager::Instance().ExitProfilingMode();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Start
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::Start(const Param* params, int numParams)
|
||||
{
|
||||
(void)params;
|
||||
(void)numParams;
|
||||
|
||||
// dump current allocations for all allocators with tracking
|
||||
auto allocatorLock = AllocatorManager::Instance().LockAllocators();
|
||||
for (int i = 0; i < AllocatorManager::Instance().GetNumAllocators(); ++i)
|
||||
{
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// RegisterAllocator
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::RegisterAllocator(IAllocator* allocator)
|
||||
{
|
||||
// Ignore if our allocator is already registered
|
||||
if (allocator->GetRecords() != nullptr)
|
||||
IAllocator* allocator = AllocatorManager::Instance().GetAllocator(i);
|
||||
if (auto records = allocator->GetRecords())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto debugConfig = allocator->GetDebugConfig();
|
||||
|
||||
if (!debugConfig.m_excludeFromDebugging)
|
||||
{
|
||||
allocator->SetRecords(aznew Debug::AllocationRecords((unsigned char)debugConfig.m_stackRecordLevels, debugConfig.m_usesMemoryGuards, debugConfig.m_marksUnallocatedMemory, allocator->GetName()));
|
||||
|
||||
m_allAllocatorRecords.push_back(allocator->GetRecords());
|
||||
|
||||
if (m_output == nullptr)
|
||||
{
|
||||
return; // we have no active output
|
||||
}
|
||||
RegisterAllocatorOutput(allocator);
|
||||
}
|
||||
}
|
||||
//=========================================================================
|
||||
// RegisterAllocatorOutput
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::RegisterAllocatorOutput(IAllocator* allocator)
|
||||
{
|
||||
auto records = allocator->GetRecords();
|
||||
m_output->BeginTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
m_output->BeginTag(AZ_CRC("RegisterAllocator", 0x19f08114));
|
||||
m_output->Write(AZ_CRC("Name", 0x5e237e06), allocator->GetName());
|
||||
m_output->Write(AZ_CRC("Id", 0xbf396750), allocator);
|
||||
m_output->Write(AZ_CRC("Capacity", 0xb5e8b174), allocator->GetAllocationSource()->Capacity());
|
||||
m_output->Write(AZ_CRC("RecordsId", 0x7caaca88), records);
|
||||
if (records)
|
||||
{
|
||||
m_output->Write(AZ_CRC("RecordsMode", 0x764c147a), (char)records->GetMode());
|
||||
m_output->Write(AZ_CRC("NumStackLevels", 0xad9cff15), records->GetNumStackLevels());
|
||||
}
|
||||
m_output->EndTag(AZ_CRC("RegisterAllocator", 0x19f08114));
|
||||
m_output->EndTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// UnregisterAllocator
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::UnregisterAllocator(IAllocator* allocator)
|
||||
{
|
||||
auto allocatorRecords = allocator->GetRecords();
|
||||
AZ_Assert(allocatorRecords, "This allocator is not registered with the memory driller!");
|
||||
for (auto records : m_allAllocatorRecords)
|
||||
{
|
||||
if (records == allocatorRecords)
|
||||
const AllocationRecordsType& allocMap = records->GetMap();
|
||||
for (AllocationRecordsType::const_iterator allocIt = allocMap.begin(); allocIt != allocMap.end(); ++allocIt)
|
||||
{
|
||||
m_allAllocatorRecords.remove(records);
|
||||
break;
|
||||
RegisterAllocationOutput(allocator, allocIt->first, &allocIt->second);
|
||||
}
|
||||
}
|
||||
delete allocatorRecords;
|
||||
allocator->SetRecords(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Stop
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::Stop()
|
||||
{
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// RegisterAllocator
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::RegisterAllocator(IAllocator* allocator)
|
||||
{
|
||||
// Ignore if our allocator is already registered
|
||||
if (allocator->GetRecords() != nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto debugConfig = allocator->GetDebugConfig();
|
||||
|
||||
if (!debugConfig.m_excludeFromDebugging)
|
||||
{
|
||||
allocator->SetRecords(aznew Debug::AllocationRecords((unsigned char)debugConfig.m_stackRecordLevels, debugConfig.m_usesMemoryGuards, debugConfig.m_marksUnallocatedMemory, allocator->GetName()));
|
||||
|
||||
m_allAllocatorRecords.push_back(allocator->GetRecords());
|
||||
|
||||
if (m_output == nullptr)
|
||||
{
|
||||
return; // we have no active output
|
||||
}
|
||||
RegisterAllocatorOutput(allocator);
|
||||
}
|
||||
}
|
||||
//=========================================================================
|
||||
// RegisterAllocatorOutput
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::RegisterAllocatorOutput(IAllocator* allocator)
|
||||
{
|
||||
auto records = allocator->GetRecords();
|
||||
m_output->BeginTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
m_output->BeginTag(AZ_CRC("RegisterAllocator", 0x19f08114));
|
||||
m_output->Write(AZ_CRC("Name", 0x5e237e06), allocator->GetName());
|
||||
m_output->Write(AZ_CRC("Id", 0xbf396750), allocator);
|
||||
m_output->Write(AZ_CRC("Capacity", 0xb5e8b174), allocator->GetAllocationSource()->Capacity());
|
||||
m_output->Write(AZ_CRC("RecordsId", 0x7caaca88), records);
|
||||
if (records)
|
||||
{
|
||||
m_output->Write(AZ_CRC("RecordsMode", 0x764c147a), (char)records->GetMode());
|
||||
m_output->Write(AZ_CRC("NumStackLevels", 0xad9cff15), records->GetNumStackLevels());
|
||||
}
|
||||
m_output->EndTag(AZ_CRC("RegisterAllocator", 0x19f08114));
|
||||
m_output->EndTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// UnregisterAllocator
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::UnregisterAllocator(IAllocator* allocator)
|
||||
{
|
||||
auto allocatorRecords = allocator->GetRecords();
|
||||
AZ_Assert(allocatorRecords, "This allocator is not registered with the memory driller!");
|
||||
for (auto records : m_allAllocatorRecords)
|
||||
{
|
||||
if (records == allocatorRecords)
|
||||
{
|
||||
m_allAllocatorRecords.remove(records);
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete allocatorRecords;
|
||||
allocator->SetRecords(nullptr);
|
||||
|
||||
if (m_output == nullptr)
|
||||
{
|
||||
return; // we have no active output
|
||||
}
|
||||
m_output->BeginTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
m_output->Write(AZ_CRC("UnregisterAllocator", 0xb2b54f93), allocator);
|
||||
m_output->EndTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// RegisterAllocation
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::RegisterAllocation(IAllocator* allocator, void* address, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, unsigned int stackSuppressCount)
|
||||
{
|
||||
auto records = allocator->GetRecords();
|
||||
if (records)
|
||||
{
|
||||
const AllocationInfo* info = records->RegisterAllocation(address, byteSize, alignment, name, fileName, lineNum, stackSuppressCount + 1);
|
||||
if (m_output == nullptr)
|
||||
{
|
||||
return; // we have no active output
|
||||
}
|
||||
RegisterAllocationOutput(allocator, address, info);
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// RegisterAllocationOutput
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::RegisterAllocationOutput(IAllocator* allocator, void* address, const AllocationInfo* info)
|
||||
{
|
||||
auto records = allocator->GetRecords();
|
||||
m_output->BeginTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
m_output->BeginTag(AZ_CRC("RegisterAllocation", 0x992a9780));
|
||||
m_output->Write(AZ_CRC("RecordsId", 0x7caaca88), records);
|
||||
m_output->Write(AZ_CRC("Address", 0x0d4e6f81), address);
|
||||
if (info)
|
||||
{
|
||||
if (info->m_name)
|
||||
{
|
||||
m_output->Write(AZ_CRC("Name", 0x5e237e06), info->m_name);
|
||||
}
|
||||
m_output->Write(AZ_CRC("Alignment", 0x2cce1e5c), info->m_alignment);
|
||||
m_output->Write(AZ_CRC("Size", 0xf7c0246a), info->m_byteSize);
|
||||
if (info->m_fileName)
|
||||
{
|
||||
m_output->Write(AZ_CRC("FileName", 0x3c0be965), info->m_fileName);
|
||||
m_output->Write(AZ_CRC("FileLine", 0xb33c2395), info->m_lineNum);
|
||||
}
|
||||
// copy the stack frames directly, resolving the stack should happen later as this is a SLOW procedure.
|
||||
if (info->m_stackFrames)
|
||||
{
|
||||
m_output->Write(AZ_CRC("Stack", 0x41a87b6a), info->m_stackFrames, info->m_stackFrames + records->GetNumStackLevels());
|
||||
}
|
||||
}
|
||||
m_output->EndTag(AZ_CRC("RegisterAllocation", 0x992a9780));
|
||||
m_output->EndTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// UnRegisterAllocation
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::UnregisterAllocation(IAllocator* allocator, void* address, size_t byteSize, size_t alignment, AllocationInfo* info)
|
||||
{
|
||||
auto records = allocator->GetRecords();
|
||||
if (records)
|
||||
{
|
||||
records->UnregisterAllocation(address, byteSize, alignment, info);
|
||||
|
||||
if (m_output == nullptr)
|
||||
{
|
||||
return; // we have no active output
|
||||
}
|
||||
m_output->BeginTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
m_output->Write(AZ_CRC("UnregisterAllocator", 0xb2b54f93), allocator);
|
||||
m_output->EndTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// RegisterAllocation
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::RegisterAllocation(IAllocator* allocator, void* address, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, unsigned int stackSuppressCount)
|
||||
{
|
||||
auto records = allocator->GetRecords();
|
||||
if (records)
|
||||
{
|
||||
const AllocationInfo* info = records->RegisterAllocation(address, byteSize, alignment, name, fileName, lineNum, stackSuppressCount + 1);
|
||||
if (m_output == nullptr)
|
||||
{
|
||||
return; // we have no active output
|
||||
}
|
||||
RegisterAllocationOutput(allocator, address, info);
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// RegisterAllocationOutput
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::RegisterAllocationOutput(IAllocator* allocator, void* address, const AllocationInfo* info)
|
||||
{
|
||||
auto records = allocator->GetRecords();
|
||||
m_output->BeginTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
m_output->BeginTag(AZ_CRC("RegisterAllocation", 0x992a9780));
|
||||
m_output->BeginTag(AZ_CRC("UnRegisterAllocation", 0xea5dc4cd));
|
||||
m_output->Write(AZ_CRC("RecordsId", 0x7caaca88), records);
|
||||
m_output->Write(AZ_CRC("Address", 0x0d4e6f81), address);
|
||||
if (info)
|
||||
{
|
||||
if (info->m_name)
|
||||
{
|
||||
m_output->Write(AZ_CRC("Name", 0x5e237e06), info->m_name);
|
||||
}
|
||||
m_output->Write(AZ_CRC("Alignment", 0x2cce1e5c), info->m_alignment);
|
||||
m_output->Write(AZ_CRC("Size", 0xf7c0246a), info->m_byteSize);
|
||||
if (info->m_fileName)
|
||||
{
|
||||
m_output->Write(AZ_CRC("FileName", 0x3c0be965), info->m_fileName);
|
||||
m_output->Write(AZ_CRC("FileLine", 0xb33c2395), info->m_lineNum);
|
||||
}
|
||||
// copy the stack frames directly, resolving the stack should happen later as this is a SLOW procedure.
|
||||
if (info->m_stackFrames)
|
||||
{
|
||||
m_output->Write(AZ_CRC("Stack", 0x41a87b6a), info->m_stackFrames, info->m_stackFrames + records->GetNumStackLevels());
|
||||
}
|
||||
}
|
||||
m_output->EndTag(AZ_CRC("RegisterAllocation", 0x992a9780));
|
||||
m_output->EndTag(AZ_CRC("UnRegisterAllocation", 0xea5dc4cd));
|
||||
m_output->EndTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// UnRegisterAllocation
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::UnregisterAllocation(IAllocator* allocator, void* address, size_t byteSize, size_t alignment, AllocationInfo* info)
|
||||
//=========================================================================
|
||||
// ReallocateAllocation
|
||||
// [10/1/2018]
|
||||
//=========================================================================
|
||||
void MemoryDriller::ReallocateAllocation(IAllocator* allocator, void* prevAddress, void* newAddress, size_t newByteSize, size_t newAlignment)
|
||||
{
|
||||
AllocationInfo info;
|
||||
UnregisterAllocation(allocator, prevAddress, 0, 0, &info);
|
||||
RegisterAllocation(allocator, newAddress, newByteSize, newAlignment, info.m_name, info.m_fileName, info.m_lineNum, 0);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ResizeAllocation
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::ResizeAllocation(IAllocator* allocator, void* address, size_t newSize)
|
||||
{
|
||||
auto records = allocator->GetRecords();
|
||||
if (records)
|
||||
{
|
||||
auto records = allocator->GetRecords();
|
||||
if (records)
|
||||
{
|
||||
records->UnregisterAllocation(address, byteSize, alignment, info);
|
||||
records->ResizeAllocation(address, newSize);
|
||||
|
||||
if (m_output == nullptr)
|
||||
{
|
||||
return; // we have no active output
|
||||
}
|
||||
m_output->BeginTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
m_output->BeginTag(AZ_CRC("UnRegisterAllocation", 0xea5dc4cd));
|
||||
m_output->Write(AZ_CRC("RecordsId", 0x7caaca88), records);
|
||||
m_output->Write(AZ_CRC("Address", 0x0d4e6f81), address);
|
||||
m_output->EndTag(AZ_CRC("UnRegisterAllocation", 0xea5dc4cd));
|
||||
m_output->EndTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
if (m_output == nullptr)
|
||||
{
|
||||
return; // we have no active output
|
||||
}
|
||||
m_output->BeginTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
m_output->BeginTag(AZ_CRC("ResizeAllocation", 0x8a9c78dc));
|
||||
m_output->Write(AZ_CRC("RecordsId", 0x7caaca88), records);
|
||||
m_output->Write(AZ_CRC("Address", 0x0d4e6f81), address);
|
||||
m_output->Write(AZ_CRC("Size", 0xf7c0246a), newSize);
|
||||
m_output->EndTag(AZ_CRC("ResizeAllocation", 0x8a9c78dc));
|
||||
m_output->EndTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryDriller::DumpAllAllocations()
|
||||
{
|
||||
// Create a copy so allocations done during the printing dont end up affecting the container
|
||||
const AZStd::list<Debug::AllocationRecords*, OSStdAllocator> allocationRecords = m_allAllocatorRecords;
|
||||
|
||||
for (auto records : allocationRecords)
|
||||
{
|
||||
// Skip if we have had no allocations made
|
||||
if (records->RequestedAllocs())
|
||||
{
|
||||
records->EnumerateAllocations(AZ::Debug::PrintAllocationsCB(true, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ReallocateAllocation
|
||||
// [10/1/2018]
|
||||
//=========================================================================
|
||||
void MemoryDriller::ReallocateAllocation(IAllocator* allocator, void* prevAddress, void* newAddress, size_t newByteSize, size_t newAlignment)
|
||||
{
|
||||
AllocationInfo info;
|
||||
UnregisterAllocation(allocator, prevAddress, 0, 0, &info);
|
||||
RegisterAllocation(allocator, newAddress, newByteSize, newAlignment, info.m_name, info.m_fileName, info.m_lineNum, 0);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ResizeAllocation
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void MemoryDriller::ResizeAllocation(IAllocator* allocator, void* address, size_t newSize)
|
||||
{
|
||||
auto records = allocator->GetRecords();
|
||||
if (records)
|
||||
{
|
||||
records->ResizeAllocation(address, newSize);
|
||||
|
||||
if (m_output == nullptr)
|
||||
{
|
||||
return; // we have no active output
|
||||
}
|
||||
m_output->BeginTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
m_output->BeginTag(AZ_CRC("ResizeAllocation", 0x8a9c78dc));
|
||||
m_output->Write(AZ_CRC("RecordsId", 0x7caaca88), records);
|
||||
m_output->Write(AZ_CRC("Address", 0x0d4e6f81), address);
|
||||
m_output->Write(AZ_CRC("Size", 0xf7c0246a), newSize);
|
||||
m_output->EndTag(AZ_CRC("ResizeAllocation", 0x8a9c78dc));
|
||||
m_output->EndTag(AZ_CRC("MemoryDriller", 0x1b31269d));
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryDriller::DumpAllAllocations()
|
||||
{
|
||||
// Create a copy so allocations done during the printing dont end up affecting the container
|
||||
const AZStd::list<Debug::AllocationRecords*, OSStdAllocator> allocationRecords = m_allAllocatorRecords;
|
||||
|
||||
for (auto records : allocationRecords)
|
||||
{
|
||||
// Skip if we have had no allocations made
|
||||
if (records->RequestedAllocs())
|
||||
{
|
||||
records->EnumerateAllocations(AZ::Debug::PrintAllocationsCB(true, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace Debug
|
||||
} // namespace AZ
|
||||
} // namespace AZ::Debug
|
||||
|
||||
@@ -35,8 +35,8 @@ namespace AZ
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PoolAllocation<Allocator>, SystemAllocator, 0)
|
||||
|
||||
typedef typename Allocator::Page PageType;
|
||||
typedef typename Allocator::Bucket BucketType;
|
||||
using PageType = typename Allocator::Page;
|
||||
using BucketType = typename Allocator::Bucket;
|
||||
|
||||
PoolAllocation(Allocator* alloc, size_t pageSize, size_t minAllocationSize, size_t maxAllocationSize);
|
||||
virtual ~PoolAllocation();
|
||||
@@ -89,7 +89,7 @@ namespace AZ
|
||||
void SetupFreeList(size_t elementSize, size_t pageDataBlockSize);
|
||||
|
||||
/// We just use a free list of nodes which we cast to the pool type.
|
||||
typedef AZStd::intrusive_slist<FakeNode, AZStd::slist_base_hook<FakeNode> > FreeListType;
|
||||
using FreeListType = AZStd::intrusive_slist<FakeNode, AZStd::slist_base_hook<FakeNode>>;
|
||||
|
||||
FreeListType m_freeList;
|
||||
u32 m_bin;
|
||||
@@ -103,7 +103,7 @@ namespace AZ
|
||||
*/
|
||||
struct Bucket
|
||||
{
|
||||
typedef AZStd::intrusive_list<Page, AZStd::list_base_hook<Page> > PageListType;
|
||||
using PageListType = AZStd::intrusive_list<Page, AZStd::list_base_hook<Page>>;
|
||||
PageListType m_pages;
|
||||
};
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace AZ
|
||||
return page;
|
||||
}
|
||||
|
||||
typedef PoolAllocation<PoolSchemaImpl> AllocatorType;
|
||||
using AllocatorType = PoolAllocation<PoolSchemaImpl>;
|
||||
IAllocatorAllocate* m_pageAllocator;
|
||||
AllocatorType m_allocator;
|
||||
void* m_staticDataBlock;
|
||||
@@ -199,7 +199,7 @@ namespace AZ
|
||||
void SetupFreeList(size_t elementSize, size_t pageDataBlockSize);
|
||||
|
||||
/// We just use a free list of nodes which we cast to the pool type.
|
||||
typedef AZStd::intrusive_slist<FakeNode, AZStd::slist_base_hook<FakeNode> > FreeListType;
|
||||
using FreeListType = AZStd::intrusive_slist<FakeNode, AZStd::slist_base_hook<FakeNode>>;
|
||||
|
||||
FreeListType m_freeList;
|
||||
AZStd::lock_free_intrusive_stack_node<Page> m_lfStack; ///< Lock Free stack node
|
||||
@@ -215,7 +215,7 @@ namespace AZ
|
||||
*/
|
||||
struct Bucket
|
||||
{
|
||||
typedef AZStd::intrusive_list<Page, AZStd::list_base_hook<Page> > PageListType;
|
||||
using PageListType = AZStd::intrusive_list<Page, AZStd::list_base_hook<Page>>;
|
||||
PageListType m_pages;
|
||||
};
|
||||
|
||||
@@ -291,7 +291,7 @@ namespace AZ
|
||||
ThreadPoolSchema::SetThreadPoolData m_threadPoolSetter;
|
||||
|
||||
// Fox X64 we push/pop pages using the m_mutex to sync. Pages are
|
||||
typedef Bucket::PageListType FreePagesType;
|
||||
using FreePagesType = Bucket::PageListType;
|
||||
FreePagesType m_freePages;
|
||||
AZStd::vector<ThreadPoolData*> m_threads; ///< Array with all separate thread data. Used to traverse end free elements.
|
||||
|
||||
@@ -313,12 +313,12 @@ namespace AZ
|
||||
ThreadPoolData(ThreadPoolSchemaImpl* alloc, size_t pageSize, size_t minAllocationSize, size_t maxAllocationSize);
|
||||
~ThreadPoolData();
|
||||
|
||||
typedef PoolAllocation<ThreadPoolSchemaImpl> AllocatorType;
|
||||
using AllocatorType = PoolAllocation<ThreadPoolSchemaImpl>;
|
||||
/**
|
||||
* Stack with freed elements from other threads. We don't need stamped stack since the ABA problem can not
|
||||
* happen here. We push from many threads and pop from only one (we don't push from it).
|
||||
*/
|
||||
typedef AZStd::lock_free_intrusive_stack<ThreadPoolSchemaImpl::Page::FakeNodeLF, AZStd::lock_free_intrusive_stack_base_hook<ThreadPoolSchemaImpl::Page::FakeNodeLF> > FreedElementsStack;
|
||||
using FreedElementsStack = AZStd::lock_free_intrusive_stack<ThreadPoolSchemaImpl::Page::FakeNodeLF, AZStd::lock_free_intrusive_stack_base_hook<ThreadPoolSchemaImpl::Page::FakeNodeLF>>;
|
||||
|
||||
AllocatorType m_allocator;
|
||||
FreedElementsStack m_freedElements;
|
||||
|
||||
@@ -24,10 +24,10 @@ namespace AZ
|
||||
class OSStdAllocator
|
||||
{
|
||||
public:
|
||||
typedef void* pointer_type;
|
||||
typedef AZStd::size_t size_type;
|
||||
typedef AZStd::ptrdiff_t difference_type;
|
||||
typedef AZStd::false_type allow_memory_leaks; ///< Regular allocators should not leak.
|
||||
using pointer_type = void *;
|
||||
using size_type = AZStd::size_t;
|
||||
using difference_type = AZStd::ptrdiff_t;
|
||||
using allow_memory_leaks = AZStd::false_type; ///< Regular allocators should not leak.
|
||||
|
||||
OSStdAllocator(Environment::AllocatorInterface* allocator)
|
||||
: m_name("GlobalEnvironmentAllocator")
|
||||
@@ -122,7 +122,7 @@ namespace AZ
|
||||
: public EnvironmentInterface
|
||||
{
|
||||
public:
|
||||
typedef AZStd::unordered_map<u32, void*, AZStd::hash<u32>, AZStd::equal_to<u32>, OSStdAllocator> MapType;
|
||||
using MapType = AZStd::unordered_map<u32, void *, AZStd::hash<u32>, AZStd::equal_to<u32>, OSStdAllocator>;
|
||||
|
||||
static EnvironmentInterface* Get();
|
||||
static void Attach(EnvironmentInstance sourceEnvironment, bool useAsGetFallback);
|
||||
|
||||
@@ -10,21 +10,18 @@
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Module/ModuleManagerBus.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Internal
|
||||
{
|
||||
namespace Internal
|
||||
AZ::OSString ModuleManagerSearchPathTool::GetModuleDirectory(const AZ::DynamicModuleDescriptor& moduleDesc)
|
||||
{
|
||||
AZ::OSString ModuleManagerSearchPathTool::GetModuleDirectory(const AZ::DynamicModuleDescriptor& moduleDesc)
|
||||
// For each module that is loaded, attempt to set the module's folder as a path for dependent module resolution
|
||||
AZ::OSString modulePath = moduleDesc.m_dynamicLibraryPath;
|
||||
AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::ResolveModulePath, modulePath);
|
||||
auto lastPathSep = modulePath.find_last_of(AZ_TRAIT_OS_PATH_SEPARATOR);
|
||||
if (lastPathSep != modulePath.npos)
|
||||
{
|
||||
// For each module that is loaded, attempt to set the module's folder as a path for dependent module resolution
|
||||
AZ::OSString modulePath = moduleDesc.m_dynamicLibraryPath;
|
||||
AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::ResolveModulePath, modulePath);
|
||||
auto lastPathSep = modulePath.find_last_of(AZ_TRAIT_OS_PATH_SEPARATOR);
|
||||
if (lastPathSep != modulePath.npos)
|
||||
{
|
||||
modulePath = modulePath.substr(0, lastPathSep);
|
||||
}
|
||||
return modulePath;
|
||||
modulePath = modulePath.substr(0, lastPathSep);
|
||||
}
|
||||
} // namespace Internal
|
||||
} // namespace AZ
|
||||
return modulePath;
|
||||
}
|
||||
} // namespace AZ::Internal
|
||||
|
||||
@@ -9,45 +9,41 @@
|
||||
#include <AzCore/Name/Internal/NameData.h>
|
||||
#include <AzCore/Name/NameDictionary.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Internal
|
||||
{
|
||||
namespace Internal
|
||||
NameData::NameData(AZStd::string&& name, Hash hash)
|
||||
: m_name{AZStd::move(name)}
|
||||
, m_hash{hash}
|
||||
{}
|
||||
|
||||
AZStd::string_view NameData::GetName() const
|
||||
{
|
||||
NameData::NameData(AZStd::string&& name, Hash hash)
|
||||
: m_name{AZStd::move(name)}
|
||||
, m_hash{hash}
|
||||
{}
|
||||
return m_name;
|
||||
}
|
||||
|
||||
AZStd::string_view NameData::GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
NameData::Hash NameData::GetHash() const
|
||||
{
|
||||
return m_hash;
|
||||
}
|
||||
|
||||
NameData::Hash NameData::GetHash() const
|
||||
{
|
||||
return m_hash;
|
||||
}
|
||||
void NameData::add_ref()
|
||||
{
|
||||
AZ_Assert(m_useCount >= 0, "NameData has been deleted");
|
||||
++m_useCount;
|
||||
}
|
||||
|
||||
void NameData::add_ref()
|
||||
void NameData::release()
|
||||
{
|
||||
// this could be released after we decrement the counter, therefore we will
|
||||
// base the release on the hash which is stable
|
||||
Hash hash = m_hash;
|
||||
AZ_Assert(m_useCount > 0, "m_useCount is already 0!");
|
||||
if (m_useCount.fetch_sub(1) == 1)
|
||||
{
|
||||
AZ_Assert(m_useCount >= 0, "NameData has been deleted");
|
||||
++m_useCount;
|
||||
}
|
||||
|
||||
void NameData::release()
|
||||
{
|
||||
// this could be released after we decrement the counter, therefore we will
|
||||
// base the release on the hash which is stable
|
||||
Hash hash = m_hash;
|
||||
AZ_Assert(m_useCount > 0, "m_useCount is already 0!");
|
||||
if (m_useCount.fetch_sub(1) == 1)
|
||||
if (AZ::NameDictionary::IsReady())
|
||||
{
|
||||
if (AZ::NameDictionary::IsReady())
|
||||
{
|
||||
AZ::NameDictionary::Instance().TryReleaseName(hash);
|
||||
}
|
||||
AZ::NameDictionary::Instance().TryReleaseName(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AZ::Internal
|
||||
|
||||
@@ -32,7 +32,12 @@ namespace AZ
|
||||
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = AZ::Environment::CreateVariable<NameDictionary>(NameDictionaryInstanceName);
|
||||
// Because the NameDictionary allocates memory using the AZ::Allocator and it is created
|
||||
// in the executable memory space, it's ownership cannot be transferred to other module memory spaces
|
||||
// Otherwise this could cause the the NameDictionary to be destroyed in static de-init
|
||||
// after the AZ::Allocators have been destroyed
|
||||
// Therefore we supply the isTransferOwnership value of false using CreateVariableEx
|
||||
s_instance = AZ::Environment::CreateVariableEx<NameDictionary>(NameDictionaryInstanceName, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +55,12 @@ namespace AZ
|
||||
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = Environment::FindVariable<NameDictionary>(NameDictionaryInstanceName);
|
||||
// Because the NameDictionary allocates memory using the AZ::Allocator and it is created
|
||||
// in the executable memory space, it's ownership cannot be transferred to other module memory spaces
|
||||
// Otherwise this could cause the the NameDictionary to be destroyed in static de-init
|
||||
// after the AZ::Allocators have been destroyed
|
||||
// Therefore we supply the isTransferOwnership value of false using CreateVariableEx
|
||||
s_instance = AZ::Environment::CreateVariableEx<NameDictionary>(NameDictionaryInstanceName, true, false);
|
||||
}
|
||||
|
||||
return s_instance.IsConstructed();
|
||||
|
||||
@@ -8,19 +8,16 @@
|
||||
|
||||
#include <AzCore/Platform.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Platform
|
||||
{
|
||||
namespace Platform
|
||||
{
|
||||
MachineId s_machineId = MachineId(0);
|
||||
MachineId s_machineId = MachineId(0);
|
||||
|
||||
void SetLocalMachineId(AZ::u32 machineId)
|
||||
void SetLocalMachineId(AZ::u32 machineId)
|
||||
{
|
||||
AZ_Assert(machineId != 0, "0 machine ID is reserved!");
|
||||
if (s_machineId != 0)
|
||||
{
|
||||
AZ_Assert(machineId != 0, "0 machine ID is reserved!");
|
||||
if (s_machineId != 0)
|
||||
{
|
||||
s_machineId = machineId;
|
||||
}
|
||||
s_machineId = machineId;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace AZ::Platform
|
||||
|
||||
@@ -117,9 +117,9 @@ namespace AZ
|
||||
|
||||
if (!explicitOverloads.m_overloads.empty())
|
||||
{
|
||||
for (auto methodAndClass : explicitOverloads.m_overloads)
|
||||
for (const auto& methodAndClass : explicitOverloads.m_overloads)
|
||||
{
|
||||
overloads.push_back({ methodAndClass.first, methodAndClass.second });
|
||||
overloads.emplace_back(methodAndClass.first, methodAndClass.second);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -128,7 +128,7 @@ namespace AZ
|
||||
|
||||
do
|
||||
{
|
||||
overloads.push_back({ overload, behaviorClass });
|
||||
overloads.emplace_back(overload, behaviorClass);
|
||||
overload = overload->m_overload;
|
||||
}
|
||||
while (overload);
|
||||
|
||||
@@ -170,61 +170,77 @@ ScriptContext* ScriptSystemComponent::AddContext(ScriptContext* context, int ga
|
||||
ScriptContext* ScriptSystemComponent::AddContextWithId(ScriptContextId id)
|
||||
{
|
||||
AZ_Assert(m_contexts.empty() || id != ScriptContextIds::DefaultScriptContextId, "Default script context ID is reserved! Please provide a Unique context ID for you ScriptContext!");
|
||||
if (GetContext(id) == nullptr)
|
||||
if (GetContext(id) != nullptr)
|
||||
{
|
||||
m_contexts.emplace_back();
|
||||
ContextContainer& cc = m_contexts.back();
|
||||
cc.m_context = aznew ScriptContext(id);
|
||||
cc.m_isOwner = true;
|
||||
cc.m_garbageCollectorSteps = m_defaultGarbageCollectorSteps;
|
||||
|
||||
cc.m_context->SetRequireHook(AZStd::bind(&ScriptSystemComponent::DefaultRequireHook, this, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3));
|
||||
|
||||
if (id != ScriptContextIds::CryScriptContextId)
|
||||
return nullptr;
|
||||
}
|
||||
m_contexts.emplace_back();
|
||||
ContextContainer& cc = m_contexts.back();
|
||||
cc.m_context = aznew ScriptContext(id);
|
||||
cc.m_isOwner = true;
|
||||
cc.m_garbageCollectorSteps = m_defaultGarbageCollectorSteps;
|
||||
cc.m_context->SetRequireHook(
|
||||
[this](lua_State* lua, ScriptContext* context, const char* module) -> int
|
||||
{
|
||||
// Reflect script classes
|
||||
ComponentApplication* app = nullptr;
|
||||
EBUS_EVENT_RESULT(app, ComponentApplicationBus, GetApplication);
|
||||
if (app && app->GetDescriptor().m_enableScriptReflection)
|
||||
return DefaultRequireHook(lua, context, module);
|
||||
});
|
||||
|
||||
if (id != ScriptContextIds::CryScriptContextId)
|
||||
{
|
||||
// Reflect script classes
|
||||
ComponentApplication* app = nullptr;
|
||||
EBUS_EVENT_RESULT(app, ComponentApplicationBus, GetApplication);
|
||||
if (app && app->GetDescriptor().m_enableScriptReflection)
|
||||
{
|
||||
if (app->GetBehaviorContext())
|
||||
{
|
||||
if (app->GetBehaviorContext())
|
||||
{
|
||||
cc.m_context->BindTo(app->GetBehaviorContext());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Script", false, "We are asked to enabled scripting, but the Applicaion has no BehaviorContext! Scripting relies on BehaviorContext!");
|
||||
}
|
||||
cc.m_context->BindTo(app->GetBehaviorContext());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Script", false, "We are asked to enabled scripting, but the Applicaion has no BehaviorContext! Scripting relies on BehaviorContext!");
|
||||
}
|
||||
}
|
||||
|
||||
return cc.m_context;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return cc.m_context;
|
||||
}
|
||||
|
||||
void ScriptSystemComponent::RestoreDefaultRequireHook(ScriptContextId id)
|
||||
{
|
||||
if (auto context = GetContext(id))
|
||||
auto context = GetContext(id);
|
||||
if (!context)
|
||||
{
|
||||
for (auto& inMemoryModule : m_inMemoryModules)
|
||||
{
|
||||
ClearAssetReferences(inMemoryModule.second->GetId());
|
||||
}
|
||||
|
||||
m_inMemoryModules.clear();
|
||||
context->SetRequireHook(AZStd::bind(&ScriptSystemComponent::DefaultRequireHook, this, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3));
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& inMemoryModule : m_inMemoryModules)
|
||||
{
|
||||
ClearAssetReferences(inMemoryModule.second->GetId());
|
||||
}
|
||||
|
||||
m_inMemoryModules.clear();
|
||||
context->SetRequireHook(
|
||||
[this](lua_State* lua, ScriptContext* context, const char* module) -> int
|
||||
{
|
||||
return DefaultRequireHook(lua, context, module);
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptSystemComponent::UseInMemoryRequireHook(const InMemoryScriptModules& modules, ScriptContextId id)
|
||||
{
|
||||
if (auto context = GetContext(id))
|
||||
auto context = GetContext(id);
|
||||
if (nullptr == context)
|
||||
{
|
||||
m_inMemoryModules = modules;
|
||||
context->SetRequireHook(AZStd::bind(&ScriptSystemComponent::InMemoryRequireHook, this, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3));
|
||||
return;
|
||||
}
|
||||
|
||||
m_inMemoryModules = modules;
|
||||
context->SetRequireHook(
|
||||
[this](lua_State* lua, ScriptContext* context, const char* module) -> int
|
||||
{
|
||||
return InMemoryRequireHook(lua, context, module);
|
||||
});
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
|
||||
@@ -28,133 +28,130 @@
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::ScriptCanvasOnDemandReflection
|
||||
{
|
||||
namespace ScriptCanvasOnDemandReflection
|
||||
// the use of this might have to come at the end of on demand reflection...instead of instantly
|
||||
// basically, it required that dependent classes are reflected first, I'm not sure they are yet.
|
||||
AZStd::string GetPrettyNameForAZTypeId(AZ::BehaviorContext& context, AZ::Uuid typeId)
|
||||
{
|
||||
// the use of this might have to come at the end of on demand reflection...instead of instantly
|
||||
// basically, it required that dependent classes are reflected first, I'm not sure they are yet.
|
||||
AZStd::string GetPrettyNameForAZTypeId(AZ::BehaviorContext& context, AZ::Uuid typeId)
|
||||
// return capitalized versions of what we need, otherwise just the regular name
|
||||
// then strip all the stuff
|
||||
if (typeId == azrtti_typeid<Aabb>())
|
||||
{
|
||||
// return capitalized versions of what we need, otherwise just the regular name
|
||||
// then strip all the stuff
|
||||
if (typeId == azrtti_typeid<Aabb>())
|
||||
return "AABB";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<bool>())
|
||||
{
|
||||
return "Boolean";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Color>())
|
||||
{
|
||||
return "Color";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Crc32>())
|
||||
{
|
||||
return "CRC";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<EntityId>())
|
||||
{
|
||||
return "EntityId";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Matrix3x3>())
|
||||
{
|
||||
return "Matrix3x3";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Matrix4x4>())
|
||||
{
|
||||
return "Matrix4x4";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::s8>())
|
||||
{
|
||||
return "Number:s8";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::s16>())
|
||||
{
|
||||
return "Number:s16";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::s32>())
|
||||
{
|
||||
return "Number:s32";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::s64>())
|
||||
{
|
||||
return "Number:s64";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::u8>())
|
||||
{
|
||||
return "Number:u8";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::u16>())
|
||||
{
|
||||
return "Number:u16";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::u32>())
|
||||
{
|
||||
return "Number:u32";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::u64>())
|
||||
{
|
||||
return "Number:u64";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<float>())
|
||||
{
|
||||
return "Number:float";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<double>())
|
||||
{
|
||||
return "Number";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Obb>())
|
||||
{
|
||||
return "OBB";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Plane>())
|
||||
{
|
||||
return "Plane";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Quaternion>())
|
||||
{
|
||||
return "Quaternion";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZStd::string>() || typeId == azrtti_typeid<AZStd::string_view>())
|
||||
{
|
||||
return "String";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Transform>())
|
||||
{
|
||||
return "Transform";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Vector2>())
|
||||
{
|
||||
return "Vector2";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Vector3>())
|
||||
{
|
||||
return "Vector3";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Vector4>())
|
||||
{
|
||||
return "Vector4";
|
||||
}
|
||||
else
|
||||
{
|
||||
auto bcClassIter = context.m_typeToClassMap.find(typeId);
|
||||
if (bcClassIter != context.m_typeToClassMap.end())
|
||||
{
|
||||
return "AABB";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<bool>())
|
||||
{
|
||||
return "Boolean";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Color>())
|
||||
{
|
||||
return "Color";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Crc32>())
|
||||
{
|
||||
return "CRC";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<EntityId>())
|
||||
{
|
||||
return "EntityId";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Matrix3x3>())
|
||||
{
|
||||
return "Matrix3x3";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Matrix4x4>())
|
||||
{
|
||||
return "Matrix4x4";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::s8>())
|
||||
{
|
||||
return "Number:s8";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::s16>())
|
||||
{
|
||||
return "Number:s16";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::s32>())
|
||||
{
|
||||
return "Number:s32";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::s64>())
|
||||
{
|
||||
return "Number:s64";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::u8>())
|
||||
{
|
||||
return "Number:u8";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::u16>())
|
||||
{
|
||||
return "Number:u16";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::u32>())
|
||||
{
|
||||
return "Number:u32";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZ::u64>())
|
||||
{
|
||||
return "Number:u64";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<float>())
|
||||
{
|
||||
return "Number:float";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<double>())
|
||||
{
|
||||
return "Number";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Obb>())
|
||||
{
|
||||
return "OBB";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Plane>())
|
||||
{
|
||||
return "Plane";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Quaternion>())
|
||||
{
|
||||
return "Quaternion";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<AZStd::string>() || typeId == azrtti_typeid<AZStd::string_view>())
|
||||
{
|
||||
return "String";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Transform>())
|
||||
{
|
||||
return "Transform";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Vector2>())
|
||||
{
|
||||
return "Vector2";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Vector3>())
|
||||
{
|
||||
return "Vector3";
|
||||
}
|
||||
else if (typeId == azrtti_typeid<Vector4>())
|
||||
{
|
||||
return "Vector4";
|
||||
const AZ::BehaviorClass& bcClass = *(bcClassIter->second);
|
||||
AZStd::string uglyName = bcClass.m_name;
|
||||
AZ::StringFunc::Replace(uglyName, "AZStd::", "", true);
|
||||
AZ::StringFunc::Replace(uglyName, "AZ::", "", true);
|
||||
AZ::StringFunc::Replace(uglyName, "::", ".", true);
|
||||
return uglyName;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto bcClassIter = context.m_typeToClassMap.find(typeId);
|
||||
if (bcClassIter != context.m_typeToClassMap.end())
|
||||
{
|
||||
const AZ::BehaviorClass& bcClass = *(bcClassIter->second);
|
||||
AZStd::string uglyName = bcClass.m_name;
|
||||
AZ::StringFunc::Replace(uglyName, "AZStd::", "", true);
|
||||
AZ::StringFunc::Replace(uglyName, "AZ::", "", true);
|
||||
AZ::StringFunc::Replace(uglyName, "::", ".", true);
|
||||
return uglyName;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Invalid";
|
||||
}
|
||||
return "Invalid";
|
||||
}
|
||||
}
|
||||
} // namespace ScriptCanvasOnDemandReflection
|
||||
} // namespace AZ
|
||||
}
|
||||
} // namespace AZ::ScriptCanvasOnDemandReflection
|
||||
|
||||
@@ -54,12 +54,8 @@ namespace AZStd
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
//template<class T>
|
||||
//class ScriptProperty;
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
|
||||
template <class ValueType>
|
||||
void SetupClassElementFromType(SerializeContext::ClassElement& classElement)
|
||||
{
|
||||
@@ -86,15 +82,14 @@ namespace AZ
|
||||
{
|
||||
auto uuid = AzTypeInfo<ValueType>::Uuid();
|
||||
|
||||
using ContainerType = AttributeContainerType<AZ::TypeId>;
|
||||
classElement.m_attributes.emplace_back(AZ_CRC("EnumType", 0xb177e1b5), CreateModuleAttribute<ContainerType>(AZStd::move(uuid)));
|
||||
classElement.m_attributes.emplace_back(AZ_CRC("EnumType", 0xb177e1b5), CreateModuleAttribute(AZStd::move(uuid)));
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
AZStd::enable_if_t<std::is_pod<T>::value> InitializeDefaultIfPodType(T& t)
|
||||
{
|
||||
t = {};
|
||||
t = T{};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@@ -648,7 +643,6 @@ namespace AZ
|
||||
|
||||
// Register our key type within an lvalue to rvalue wrapper as an attribute
|
||||
AZ::TypeId uuid = azrtti_typeid<WrappedKeyType>();
|
||||
using ContainerType = AttributeContainerType<AZ::TypeId>;
|
||||
|
||||
/**
|
||||
* This should technically bind the reference value from the GetCurrentSerializeContextModule() call
|
||||
@@ -658,7 +652,7 @@ namespace AZ
|
||||
*/
|
||||
m_classElement.m_attributes.set_allocator(AZStdFunctorAllocator([]() -> IAllocatorAllocate& { return GetCurrentSerializeContextModule().GetAllocator(); }));
|
||||
|
||||
m_classElement.m_attributes.emplace_back(AZ_CRC("KeyType", 0x15bc5303), CreateModuleAttribute<ContainerType>(AZStd::move(uuid)));
|
||||
m_classElement.m_attributes.emplace_back(AZ_CRC("KeyType", 0x15bc5303), CreateModuleAttribute(AZStd::move(uuid)));
|
||||
}
|
||||
|
||||
// Reflect our wrapped key and value types to serializeContext so that may later be used
|
||||
|
||||
@@ -19,8 +19,11 @@ namespace AZ
|
||||
nodeStack.push_back(m_dataContainer);
|
||||
|
||||
SerializeContext::EnumerateInstanceCallContext callContext(
|
||||
AZStd::bind(&DataOverlayTarget::ElementBegin, this, &nodeStack, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3),
|
||||
AZStd::bind(&DataOverlayTarget::ElementEnd, this, &nodeStack),
|
||||
[this, &nodeStack](void* instancePointer, const SerializeContext::ClassData* classData, const SerializeContext::ClassElement* classElement)->bool
|
||||
{
|
||||
return ElementBegin(&nodeStack, instancePointer, classData, classElement);
|
||||
},
|
||||
[this, &nodeStack]()->bool { return ElementEnd(&nodeStack); },
|
||||
m_sc,
|
||||
SerializeContext::ENUM_ACCESS_FOR_READ,
|
||||
m_errorLogger
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace AZ
|
||||
class DataNode
|
||||
{
|
||||
public:
|
||||
typedef AZStd::list<DataNode> ChildDataNodes;
|
||||
using ChildDataNodes = AZStd::list<DataNode>;
|
||||
|
||||
DataNode()
|
||||
{
|
||||
@@ -148,25 +148,28 @@ namespace AZ
|
||||
m_root.Reset();
|
||||
m_currentNode = nullptr;
|
||||
|
||||
if (m_context && rootClassPtr)
|
||||
if (!m_context || !rootClassPtr)
|
||||
{
|
||||
SerializeContext::EnumerateInstanceCallContext callContext(
|
||||
AZStd::bind(&DataNodeTree::BeginNode, this, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3),
|
||||
AZStd::bind(&DataNodeTree::EndNode, this),
|
||||
m_context,
|
||||
SerializeContext::ENUM_ACCESS_FOR_READ,
|
||||
nullptr
|
||||
);
|
||||
|
||||
m_context->EnumerateInstanceConst(
|
||||
&callContext,
|
||||
rootClassPtr,
|
||||
rootClassId,
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
return;
|
||||
}
|
||||
SerializeContext::EnumerateInstanceCallContext callContext(
|
||||
[this](void* instancePointer, const SerializeContext::ClassData* classData, const SerializeContext::ClassElement* classElement)->bool
|
||||
{
|
||||
return BeginNode(instancePointer, classData, classElement);
|
||||
},
|
||||
[this]()->bool { return EndNode(); },
|
||||
m_context,
|
||||
SerializeContext::ENUM_ACCESS_FOR_READ,
|
||||
nullptr
|
||||
);
|
||||
|
||||
m_context->EnumerateInstanceConst(
|
||||
&callContext,
|
||||
rootClassPtr,
|
||||
rootClassId,
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
m_currentNode = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace AZ
|
||||
}
|
||||
classIt->ClearElements();
|
||||
}
|
||||
for (auto enumIt : m_enumData)
|
||||
for (auto& enumIt : m_enumData)
|
||||
{
|
||||
enumIt.second.ClearAttributes();
|
||||
}
|
||||
@@ -103,7 +103,7 @@ namespace AZ
|
||||
//=========================================================================
|
||||
void ElementData::ClearAttributes()
|
||||
{
|
||||
for (auto attrib : m_attributes)
|
||||
for (auto& attrib : m_attributes)
|
||||
{
|
||||
delete attrib.second;
|
||||
}
|
||||
|
||||
@@ -759,6 +759,7 @@ namespace AZ
|
||||
else
|
||||
{
|
||||
typeIdResult.m_determination = JsonDeserializer::TypeIdDetermination::FailedToDetermine;
|
||||
typeIdResult.m_typeId = Uuid::CreateNull();
|
||||
}
|
||||
}
|
||||
else if (input.IsString())
|
||||
@@ -768,6 +769,7 @@ namespace AZ
|
||||
else
|
||||
{
|
||||
typeIdResult.m_determination = JsonDeserializer::TypeIdDetermination::FailedToDetermine;
|
||||
typeIdResult.m_typeId = Uuid::CreateNull();
|
||||
}
|
||||
|
||||
switch (typeIdResult.m_determination)
|
||||
|
||||
@@ -10,265 +10,264 @@
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::JsonSerializationResult::Internal
|
||||
{
|
||||
namespace JsonSerializationResult
|
||||
template<typename StringType>
|
||||
void AppendToString(AZ::JsonSerializationResult::ResultCode code,
|
||||
StringType& target, AZStd::string_view path)
|
||||
{
|
||||
namespace Internal
|
||||
if (code.GetTask() == static_cast<Tasks>(0))
|
||||
{
|
||||
template<typename StringType>
|
||||
void AppendToString(AZ::JsonSerializationResult::ResultCode code,
|
||||
StringType& target, AZStd::string_view path)
|
||||
{
|
||||
if (code.GetTask() == static_cast<Tasks>(0))
|
||||
{
|
||||
target.append("The result code wasn't initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
target.append("The operation ");
|
||||
switch (code.GetProcessing())
|
||||
{
|
||||
case Processing::Halted:
|
||||
target.append("has halted during ");
|
||||
break;
|
||||
case Processing::Altered:
|
||||
target.append("has taken an alternative approach for ");
|
||||
break;
|
||||
case Processing::PartialAlter:
|
||||
target.append("has taken a partially alternative approach for ");
|
||||
break;
|
||||
case Processing::Completed:
|
||||
target.append("has completed ");
|
||||
break;
|
||||
default:
|
||||
target.append("has unknown processing status for ");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (code.GetTask())
|
||||
{
|
||||
case Tasks::RetrieveInfo:
|
||||
target.append("a retrieve info operation ");
|
||||
break;
|
||||
case Tasks::CreateDefault:
|
||||
target.append("a create default operation ");
|
||||
break;
|
||||
case Tasks::Convert:
|
||||
target.append("a convert operation ");
|
||||
break;
|
||||
case Tasks::ReadField:
|
||||
target.append("a read field operation ");
|
||||
break;
|
||||
case Tasks::WriteValue:
|
||||
target.append("a write value operation ");
|
||||
break;
|
||||
case Tasks::Merge:
|
||||
target.append("a merge operation ");
|
||||
break;
|
||||
case Tasks::CreatePatch:
|
||||
target.append("a create patch operation ");
|
||||
break;
|
||||
case Tasks::Import:
|
||||
target.append("an import operation");
|
||||
break;
|
||||
default:
|
||||
target.append("an unknown operation ");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!path.empty())
|
||||
{
|
||||
target.append("for '");
|
||||
target.append(path.begin(), path.end());
|
||||
target.append("' ");
|
||||
}
|
||||
|
||||
switch (code.GetOutcome())
|
||||
{
|
||||
case Outcomes::Success:
|
||||
target.append("which resulted in success");
|
||||
break;
|
||||
case Outcomes::DefaultsUsed:
|
||||
target.append("by using only default values");
|
||||
break;
|
||||
case Outcomes::PartialDefaults:
|
||||
target.append("by using one or more default values");
|
||||
break;
|
||||
case Outcomes::Skipped:
|
||||
target.append("because a field or value was skipped");
|
||||
break;
|
||||
case Outcomes::PartialSkip:
|
||||
target.append("because one or more fields or values were skipped");
|
||||
break;
|
||||
case Outcomes::Unavailable:
|
||||
target.append("because the target was unavailable");
|
||||
break;
|
||||
case Outcomes::Unsupported:
|
||||
target.append("because the action was unsupported");
|
||||
break;
|
||||
case Outcomes::TypeMismatch:
|
||||
target.append("because the source and target are unrelated types");
|
||||
break;
|
||||
case Outcomes::TestFailed:
|
||||
target.append("because a test against a value failed");
|
||||
break;
|
||||
case Outcomes::Missing:
|
||||
target.append("because a required field or value was missing");
|
||||
break;
|
||||
case Outcomes::Invalid:
|
||||
target.append("because a field or element has an invalid value");
|
||||
break;
|
||||
case Outcomes::Unknown:
|
||||
target.append("because information was missing");
|
||||
break;
|
||||
case Outcomes::Catastrophic:
|
||||
target.append("because a catastrophic issue was encountered");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace JsonSerializationResultInternal
|
||||
|
||||
ResultCode::ResultCode(Tasks task)
|
||||
: m_code(0)
|
||||
{
|
||||
m_options.m_task = task;
|
||||
target.append("The result code wasn't initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
ResultCode::ResultCode(uint32_t code)
|
||||
: m_code(code)
|
||||
{}
|
||||
|
||||
ResultCode::ResultCode(Tasks task, Outcomes outcome)
|
||||
target.append("The operation ");
|
||||
switch (code.GetProcessing())
|
||||
{
|
||||
m_options.m_task = task;
|
||||
switch (outcome)
|
||||
{
|
||||
case Outcomes::Success: // fall through
|
||||
case Outcomes::Skipped: // fall through
|
||||
case Outcomes::PartialSkip: // fall through
|
||||
case Outcomes::DefaultsUsed: // fall through
|
||||
case Outcomes::PartialDefaults:
|
||||
m_options.m_processing = Processing::Completed;
|
||||
break;
|
||||
case Outcomes::Unavailable: // fall through
|
||||
case Outcomes::Unsupported:
|
||||
m_options.m_processing = Processing::Altered;
|
||||
break;
|
||||
case Outcomes::TypeMismatch: // fall through
|
||||
case Outcomes::TestFailed: // fall through
|
||||
case Outcomes::Missing: // fall through
|
||||
case Outcomes::Invalid: // fall through
|
||||
case Outcomes::Unknown: // fall through
|
||||
case Outcomes::Catastrophic: // fall through
|
||||
default:
|
||||
m_options.m_processing = Processing::Halted;
|
||||
break;
|
||||
}
|
||||
m_options.m_outcome = outcome;
|
||||
case Processing::Halted:
|
||||
target.append("has halted during ");
|
||||
break;
|
||||
case Processing::Altered:
|
||||
target.append("has taken an alternative approach for ");
|
||||
break;
|
||||
case Processing::PartialAlter:
|
||||
target.append("has taken a partially alternative approach for ");
|
||||
break;
|
||||
case Processing::Completed:
|
||||
target.append("has completed ");
|
||||
break;
|
||||
default:
|
||||
target.append("has unknown processing status for ");
|
||||
break;
|
||||
}
|
||||
|
||||
bool ResultCode::HasDoneWork() const
|
||||
switch (code.GetTask())
|
||||
{
|
||||
return m_options.m_outcome != static_cast<Outcomes>(0);
|
||||
case Tasks::RetrieveInfo:
|
||||
target.append("a retrieve info operation ");
|
||||
break;
|
||||
case Tasks::CreateDefault:
|
||||
target.append("a create default operation ");
|
||||
break;
|
||||
case Tasks::Convert:
|
||||
target.append("a convert operation ");
|
||||
break;
|
||||
case Tasks::ReadField:
|
||||
target.append("a read field operation ");
|
||||
break;
|
||||
case Tasks::WriteValue:
|
||||
target.append("a write value operation ");
|
||||
break;
|
||||
case Tasks::Merge:
|
||||
target.append("a merge operation ");
|
||||
break;
|
||||
case Tasks::CreatePatch:
|
||||
target.append("a create patch operation ");
|
||||
break;
|
||||
case Tasks::Import:
|
||||
target.append("an import operation");
|
||||
break;
|
||||
default:
|
||||
target.append("an unknown operation ");
|
||||
break;
|
||||
}
|
||||
|
||||
ResultCode& ResultCode::Combine(ResultCode other)
|
||||
if (!path.empty())
|
||||
{
|
||||
*this = Combine(*this, other);
|
||||
return *this;
|
||||
target.append("for '");
|
||||
target.append(path.begin(), path.end());
|
||||
target.append("' ");
|
||||
}
|
||||
|
||||
ResultCode& ResultCode::Combine(const Result& other)
|
||||
switch (code.GetOutcome())
|
||||
{
|
||||
*this = Combine(*this, other.GetResultCode());
|
||||
return *this;
|
||||
case Outcomes::Success:
|
||||
target.append("which resulted in success");
|
||||
break;
|
||||
case Outcomes::DefaultsUsed:
|
||||
target.append("by using only default values");
|
||||
break;
|
||||
case Outcomes::PartialDefaults:
|
||||
target.append("by using one or more default values");
|
||||
break;
|
||||
case Outcomes::Skipped:
|
||||
target.append("because a field or value was skipped");
|
||||
break;
|
||||
case Outcomes::PartialSkip:
|
||||
target.append("because one or more fields or values were skipped");
|
||||
break;
|
||||
case Outcomes::Unavailable:
|
||||
target.append("because the target was unavailable");
|
||||
break;
|
||||
case Outcomes::Unsupported:
|
||||
target.append("because the action was unsupported");
|
||||
break;
|
||||
case Outcomes::TypeMismatch:
|
||||
target.append("because the source and target are unrelated types");
|
||||
break;
|
||||
case Outcomes::TestFailed:
|
||||
target.append("because a test against a value failed");
|
||||
break;
|
||||
case Outcomes::Missing:
|
||||
target.append("because a required field or value was missing");
|
||||
break;
|
||||
case Outcomes::Invalid:
|
||||
target.append("because a field or element has an invalid value");
|
||||
break;
|
||||
case Outcomes::Unknown:
|
||||
target.append("because information was missing");
|
||||
break;
|
||||
case Outcomes::Catastrophic:
|
||||
target.append("because a catastrophic issue was encountered");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace AZ::JsonSerializationResult::Internal
|
||||
|
||||
ResultCode ResultCode::Combine(ResultCode lhs, ResultCode rhs)
|
||||
namespace AZ::JsonSerializationResult
|
||||
{
|
||||
|
||||
ResultCode::ResultCode(Tasks task)
|
||||
: m_code(0)
|
||||
{
|
||||
m_options.m_task = task;
|
||||
}
|
||||
|
||||
ResultCode::ResultCode(uint32_t code)
|
||||
: m_code(code)
|
||||
{}
|
||||
|
||||
ResultCode::ResultCode(Tasks task, Outcomes outcome)
|
||||
{
|
||||
m_options.m_task = task;
|
||||
switch (outcome)
|
||||
{
|
||||
ResultCode result = ResultCode(AZStd::max(lhs.m_code, rhs.m_code));
|
||||
|
||||
if ((lhs.m_options.m_outcome == Outcomes::Success && rhs.m_options.m_outcome == Outcomes::DefaultsUsed) ||
|
||||
(lhs.m_options.m_outcome == Outcomes::DefaultsUsed && rhs.m_options.m_outcome == Outcomes::Success))
|
||||
{
|
||||
result.m_options.m_outcome = Outcomes::PartialDefaults;
|
||||
}
|
||||
else if ((lhs.m_options.m_outcome == Outcomes::Success && rhs.m_options.m_outcome == Outcomes::Skipped) ||
|
||||
(lhs.m_options.m_outcome == Outcomes::Skipped && rhs.m_options.m_outcome == Outcomes::Success))
|
||||
{
|
||||
result.m_options.m_outcome = Outcomes::PartialSkip;
|
||||
}
|
||||
|
||||
if ((lhs.m_options.m_processing == Processing::Completed && rhs.m_options.m_processing == Processing::Altered) ||
|
||||
(lhs.m_options.m_processing == Processing::Altered && rhs.m_options.m_processing == Processing::Completed))
|
||||
{
|
||||
result.m_options.m_processing = Processing::PartialAlter;
|
||||
}
|
||||
|
||||
return result;
|
||||
case Outcomes::Success: // fall through
|
||||
case Outcomes::Skipped: // fall through
|
||||
case Outcomes::PartialSkip: // fall through
|
||||
case Outcomes::DefaultsUsed: // fall through
|
||||
case Outcomes::PartialDefaults:
|
||||
m_options.m_processing = Processing::Completed;
|
||||
break;
|
||||
case Outcomes::Unavailable: // fall through
|
||||
case Outcomes::Unsupported:
|
||||
m_options.m_processing = Processing::Altered;
|
||||
break;
|
||||
case Outcomes::TypeMismatch: // fall through
|
||||
case Outcomes::TestFailed: // fall through
|
||||
case Outcomes::Missing: // fall through
|
||||
case Outcomes::Invalid: // fall through
|
||||
case Outcomes::Unknown: // fall through
|
||||
case Outcomes::Catastrophic: // fall through
|
||||
default:
|
||||
m_options.m_processing = Processing::Halted;
|
||||
break;
|
||||
}
|
||||
m_options.m_outcome = outcome;
|
||||
}
|
||||
|
||||
Tasks ResultCode::GetTask() const
|
||||
bool ResultCode::HasDoneWork() const
|
||||
{
|
||||
return m_options.m_outcome != static_cast<Outcomes>(0);
|
||||
}
|
||||
|
||||
ResultCode& ResultCode::Combine(ResultCode other)
|
||||
{
|
||||
*this = Combine(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ResultCode& ResultCode::Combine(const Result& other)
|
||||
{
|
||||
*this = Combine(*this, other.GetResultCode());
|
||||
return *this;
|
||||
}
|
||||
|
||||
ResultCode ResultCode::Combine(ResultCode lhs, ResultCode rhs)
|
||||
{
|
||||
ResultCode result = ResultCode(AZStd::max(lhs.m_code, rhs.m_code));
|
||||
|
||||
if ((lhs.m_options.m_outcome == Outcomes::Success && rhs.m_options.m_outcome == Outcomes::DefaultsUsed) ||
|
||||
(lhs.m_options.m_outcome == Outcomes::DefaultsUsed && rhs.m_options.m_outcome == Outcomes::Success))
|
||||
{
|
||||
return m_options.m_task;
|
||||
result.m_options.m_outcome = Outcomes::PartialDefaults;
|
||||
}
|
||||
|
||||
Processing ResultCode::GetProcessing() const
|
||||
else if ((lhs.m_options.m_outcome == Outcomes::Success && rhs.m_options.m_outcome == Outcomes::Skipped) ||
|
||||
(lhs.m_options.m_outcome == Outcomes::Skipped && rhs.m_options.m_outcome == Outcomes::Success))
|
||||
{
|
||||
return m_options.m_processing == static_cast<Processing>(0) ?
|
||||
Processing::Completed : m_options.m_processing;
|
||||
result.m_options.m_outcome = Outcomes::PartialSkip;
|
||||
}
|
||||
|
||||
Outcomes ResultCode::GetOutcome() const
|
||||
if ((lhs.m_options.m_processing == Processing::Completed && rhs.m_options.m_processing == Processing::Altered) ||
|
||||
(lhs.m_options.m_processing == Processing::Altered && rhs.m_options.m_processing == Processing::Completed))
|
||||
{
|
||||
return m_options.m_outcome == static_cast<Outcomes>(0) ?
|
||||
Outcomes::DefaultsUsed : m_options.m_outcome;
|
||||
result.m_options.m_processing = Processing::PartialAlter;
|
||||
}
|
||||
|
||||
void ResultCode::AppendToString(AZ::OSString& target, AZStd::string_view path) const
|
||||
{
|
||||
Internal::AppendToString(*this, target, path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ResultCode::AppendToString(AZStd::string& target, AZStd::string_view path) const
|
||||
{
|
||||
Internal::AppendToString(*this, target, path);
|
||||
}
|
||||
Tasks ResultCode::GetTask() const
|
||||
{
|
||||
return m_options.m_task;
|
||||
}
|
||||
|
||||
AZStd::string ResultCode::ToString(AZStd::string_view path) const
|
||||
{
|
||||
AZStd::string result;
|
||||
AppendToString(result, path);
|
||||
return result;
|
||||
}
|
||||
Processing ResultCode::GetProcessing() const
|
||||
{
|
||||
return m_options.m_processing == static_cast<Processing>(0) ?
|
||||
Processing::Completed : m_options.m_processing;
|
||||
}
|
||||
|
||||
AZ::OSString ResultCode::ToOSString(AZStd::string_view path) const
|
||||
{
|
||||
AZ::OSString result;
|
||||
AppendToString(result, path);
|
||||
return result;
|
||||
}
|
||||
Outcomes ResultCode::GetOutcome() const
|
||||
{
|
||||
return m_options.m_outcome == static_cast<Outcomes>(0) ?
|
||||
Outcomes::DefaultsUsed : m_options.m_outcome;
|
||||
}
|
||||
|
||||
void ResultCode::AppendToString(AZ::OSString& target, AZStd::string_view path) const
|
||||
{
|
||||
Internal::AppendToString(*this, target, path);
|
||||
}
|
||||
|
||||
void ResultCode::AppendToString(AZStd::string& target, AZStd::string_view path) const
|
||||
{
|
||||
Internal::AppendToString(*this, target, path);
|
||||
}
|
||||
|
||||
AZStd::string ResultCode::ToString(AZStd::string_view path) const
|
||||
{
|
||||
AZStd::string result;
|
||||
AppendToString(result, path);
|
||||
return result;
|
||||
}
|
||||
|
||||
AZ::OSString ResultCode::ToOSString(AZStd::string_view path) const
|
||||
{
|
||||
AZ::OSString result;
|
||||
AppendToString(result, path);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Result::Result(const JsonIssueCallback& callback, AZStd::string_view message, ResultCode result, AZStd::string_view path)
|
||||
: m_result(callback(message, result, path))
|
||||
{}
|
||||
Result::Result(const JsonIssueCallback& callback, AZStd::string_view message, ResultCode result, AZStd::string_view path)
|
||||
: m_result(callback(message, result, path))
|
||||
{}
|
||||
|
||||
Result::Result(const JsonIssueCallback& callback, AZStd::string_view message, Tasks task, Outcomes outcome, AZStd::string_view path)
|
||||
: m_result(callback(message, ResultCode(task, outcome), path))
|
||||
{}
|
||||
Result::Result(const JsonIssueCallback& callback, AZStd::string_view message, Tasks task, Outcomes outcome, AZStd::string_view path)
|
||||
: m_result(callback(message, ResultCode(task, outcome), path))
|
||||
{}
|
||||
|
||||
Result::operator ResultCode() const
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
Result::operator ResultCode() const
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
ResultCode Result::GetResultCode() const
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
} // namespace JsonSerializationResult
|
||||
} // namespace AZ
|
||||
ResultCode Result::GetResultCode() const
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
} // namespace AZ::JsonSerializationResult
|
||||
|
||||
@@ -104,6 +104,8 @@ namespace AZ
|
||||
->HandlesType<AZStd::variant>();
|
||||
jsonContext->Serializer<JsonOptionalSerializer>()
|
||||
->HandlesType<AZStd::optional>();
|
||||
jsonContext->Serializer<JsonBitsetSerializer>()
|
||||
->HandlesType<AZStd::bitset>();
|
||||
|
||||
MathReflect(jsonContext);
|
||||
}
|
||||
|
||||
@@ -24,438 +24,435 @@
|
||||
|
||||
#include <AzCore/Serialization/Json/JsonUtils.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::JsonSerializationUtils
|
||||
{
|
||||
namespace JsonSerializationUtils
|
||||
static const char* FileTypeTag = "Type";
|
||||
static const char* FileType = "JsonSerialization";
|
||||
static const char* VersionTag = "Version";
|
||||
static const char* ClassNameTag = "ClassName";
|
||||
static const char* ClassDataTag = "ClassData";
|
||||
|
||||
AZ::Outcome<void, AZStd::string> WriteJsonString(const rapidjson::Document& document, AZStd::string& jsonText, WriteJsonSettings settings)
|
||||
{
|
||||
static const char* FileTypeTag = "Type";
|
||||
static const char* FileType = "JsonSerialization";
|
||||
static const char* VersionTag = "Version";
|
||||
static const char* ClassNameTag = "ClassName";
|
||||
static const char* ClassDataTag = "ClassData";
|
||||
AZ::IO::ByteContainerStream<AZStd::string> stream{&jsonText};
|
||||
return WriteJsonStream(document, stream, settings);
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> WriteJsonString(const rapidjson::Document& document, AZStd::string& jsonText, WriteJsonSettings settings)
|
||||
AZ::Outcome<void, AZStd::string> WriteJsonFile(const rapidjson::Document& document, AZStd::string_view filePath, WriteJsonSettings settings)
|
||||
{
|
||||
// Write the json into memory first and then write the file, rather than passing a file stream to rapidjson.
|
||||
// This should avoid creating a large number of micro-writes to the file.
|
||||
AZStd::string fileContent;
|
||||
auto outcome = WriteJsonString(document, fileContent, settings);
|
||||
if (!outcome.IsSuccess())
|
||||
{
|
||||
AZ::IO::ByteContainerStream<AZStd::string> stream{&jsonText};
|
||||
return WriteJsonStream(document, stream, settings);
|
||||
return outcome;
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> WriteJsonFile(const rapidjson::Document& document, AZStd::string_view filePath, WriteJsonSettings settings)
|
||||
{
|
||||
// Write the json into memory first and then write the file, rather than passing a file stream to rapidjson.
|
||||
// This should avoid creating a large number of micro-writes to the file.
|
||||
AZStd::string fileContent;
|
||||
auto outcome = WriteJsonString(document, fileContent, settings);
|
||||
if (!outcome.IsSuccess())
|
||||
{
|
||||
return outcome;
|
||||
}
|
||||
return AZ::Utils::WriteFile(fileContent, filePath);
|
||||
}
|
||||
|
||||
return AZ::Utils::WriteFile(fileContent, filePath);
|
||||
AZ::Outcome<void, AZStd::string> WriteJsonStream(const rapidjson::Document& document, IO::GenericStream& stream, WriteJsonSettings settings)
|
||||
{
|
||||
AZ::IO::RapidJSONStreamWriter jsonStreamWriter(&stream);
|
||||
|
||||
rapidjson::PrettyWriter<AZ::IO::RapidJSONStreamWriter> writer(jsonStreamWriter);
|
||||
|
||||
if (settings.m_maxDecimalPlaces >= 0)
|
||||
{
|
||||
writer.SetMaxDecimalPlaces(settings.m_maxDecimalPlaces);
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> WriteJsonStream(const rapidjson::Document& document, IO::GenericStream& stream, WriteJsonSettings settings)
|
||||
if (document.Accept(writer))
|
||||
{
|
||||
AZ::IO::RapidJSONStreamWriter jsonStreamWriter(&stream);
|
||||
return AZ::Success();
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(AZStd::string{"Json Writer failed"});
|
||||
}
|
||||
}
|
||||
|
||||
rapidjson::PrettyWriter<AZ::IO::RapidJSONStreamWriter> writer(jsonStreamWriter);
|
||||
|
||||
if (settings.m_maxDecimalPlaces >= 0)
|
||||
{
|
||||
writer.SetMaxDecimalPlaces(settings.m_maxDecimalPlaces);
|
||||
}
|
||||
|
||||
if (document.Accept(writer))
|
||||
{
|
||||
return AZ::Success();
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(AZStd::string{"Json Writer failed"});
|
||||
}
|
||||
AZ::Outcome<void, AZStd::string> SaveObjectToStreamByType(const void* objectPtr, const Uuid& classId, IO::GenericStream& stream,
|
||||
const void* defaultObjectPtr, const JsonSerializerSettings* settings)
|
||||
{
|
||||
if (!stream.CanWrite())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("The GenericStream can't be written to"));
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> SaveObjectToStreamByType(const void* objectPtr, const Uuid& classId, IO::GenericStream& stream,
|
||||
const void* defaultObjectPtr, const JsonSerializerSettings* settings)
|
||||
JsonSerializerSettings saveSettings;
|
||||
if (settings)
|
||||
{
|
||||
if (!stream.CanWrite())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("The GenericStream can't be written to"));
|
||||
}
|
||||
saveSettings = *settings;
|
||||
}
|
||||
|
||||
JsonSerializerSettings saveSettings;
|
||||
if (settings)
|
||||
{
|
||||
saveSettings = *settings;
|
||||
}
|
||||
|
||||
AZ::SerializeContext* serializeContext = saveSettings.m_serializeContext;
|
||||
AZ::SerializeContext* serializeContext = saveSettings.m_serializeContext;
|
||||
if (!serializeContext)
|
||||
{
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
if (!serializeContext)
|
||||
{
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
if (!serializeContext)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Need SerializeContext for saving"));
|
||||
}
|
||||
saveSettings.m_serializeContext = serializeContext;
|
||||
return AZ::Failure(AZStd::string::format("Need SerializeContext for saving"));
|
||||
}
|
||||
|
||||
rapidjson::Document jsonDocument;
|
||||
jsonDocument.SetObject();
|
||||
jsonDocument.AddMember(rapidjson::StringRef(FileTypeTag), rapidjson::StringRef(FileType), jsonDocument.GetAllocator());
|
||||
|
||||
rapidjson::Value serializedObject;
|
||||
|
||||
JsonSerializationResult::ResultCode jsonResult = JsonSerialization::Store(serializedObject, jsonDocument.GetAllocator(),
|
||||
objectPtr, defaultObjectPtr, classId, saveSettings);
|
||||
|
||||
if (jsonResult.GetProcessing() != JsonSerializationResult::Processing::Completed)
|
||||
{
|
||||
return AZ::Failure(jsonResult.ToString(""));
|
||||
}
|
||||
|
||||
const SerializeContext::ClassData* classData = serializeContext->FindClassData(classId);
|
||||
|
||||
jsonDocument.AddMember(rapidjson::StringRef(VersionTag), 1, jsonDocument.GetAllocator());
|
||||
jsonDocument.AddMember(rapidjson::StringRef(ClassNameTag), rapidjson::StringRef(classData->m_name), jsonDocument.GetAllocator());
|
||||
jsonDocument.AddMember(rapidjson::StringRef(ClassDataTag), AZStd::move(serializedObject), jsonDocument.GetAllocator());
|
||||
|
||||
AZ::IO::RapidJSONStreamWriter jsonStreamWriter(&stream);
|
||||
rapidjson::PrettyWriter<AZ::IO::RapidJSONStreamWriter> writer(jsonStreamWriter);
|
||||
bool jsonWriteResult = jsonDocument.Accept(writer);
|
||||
if (!jsonWriteResult)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Unable to write class %s with json serialization format'",
|
||||
classId.ToString<AZStd::string>().data()));
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
saveSettings.m_serializeContext = serializeContext;
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> SaveObjectToFileByType(const void* classPtr, const Uuid& classId, const AZStd::string& filePath,
|
||||
const void* defaultClassPtr, const JsonSerializerSettings* settings)
|
||||
rapidjson::Document jsonDocument;
|
||||
jsonDocument.SetObject();
|
||||
jsonDocument.AddMember(rapidjson::StringRef(FileTypeTag), rapidjson::StringRef(FileType), jsonDocument.GetAllocator());
|
||||
|
||||
rapidjson::Value serializedObject;
|
||||
|
||||
JsonSerializationResult::ResultCode jsonResult = JsonSerialization::Store(serializedObject, jsonDocument.GetAllocator(),
|
||||
objectPtr, defaultObjectPtr, classId, saveSettings);
|
||||
|
||||
if (jsonResult.GetProcessing() != JsonSerializationResult::Processing::Completed)
|
||||
{
|
||||
AZStd::vector<char> buffer;
|
||||
buffer.reserve(1024);
|
||||
AZ::IO::ByteContainerStream<AZStd::vector<char> > byteStream(&buffer);
|
||||
auto saveResult = SaveObjectToStreamByType(classPtr, classId, byteStream, defaultClassPtr, settings);
|
||||
if (saveResult.IsSuccess())
|
||||
{
|
||||
AZ::IO::FileIOStream outputFileStream;
|
||||
if (!outputFileStream.Open(filePath.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath | AZ::IO::OpenMode::ModeText))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Error opening file '%s' for writing", filePath.c_str()));
|
||||
}
|
||||
outputFileStream.Write(buffer.size(), buffer.data());
|
||||
}
|
||||
return saveResult;
|
||||
return AZ::Failure(jsonResult.ToString(""));
|
||||
}
|
||||
|
||||
// Helper function to check whether the load outcome was success (for loading json serialization file)
|
||||
bool WasLoadSuccess(JsonSerializationResult::Outcomes outcome)
|
||||
{
|
||||
return (outcome == JsonSerializationResult::Outcomes::Success
|
||||
|| outcome == JsonSerializationResult::Outcomes::DefaultsUsed
|
||||
|| outcome == JsonSerializationResult::Outcomes::PartialDefaults);
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> PrepareDeserializerSettings(const JsonDeserializerSettings* inputSettings, JsonDeserializerSettings& returnSettings
|
||||
, AZStd::string& deserializeError)
|
||||
{
|
||||
if (inputSettings)
|
||||
{
|
||||
returnSettings = *inputSettings;
|
||||
}
|
||||
const SerializeContext::ClassData* classData = serializeContext->FindClassData(classId);
|
||||
|
||||
jsonDocument.AddMember(rapidjson::StringRef(VersionTag), 1, jsonDocument.GetAllocator());
|
||||
jsonDocument.AddMember(rapidjson::StringRef(ClassNameTag), rapidjson::StringRef(classData->m_name), jsonDocument.GetAllocator());
|
||||
jsonDocument.AddMember(rapidjson::StringRef(ClassDataTag), AZStd::move(serializedObject), jsonDocument.GetAllocator());
|
||||
|
||||
AZ::IO::RapidJSONStreamWriter jsonStreamWriter(&stream);
|
||||
rapidjson::PrettyWriter<AZ::IO::RapidJSONStreamWriter> writer(jsonStreamWriter);
|
||||
bool jsonWriteResult = jsonDocument.Accept(writer);
|
||||
if (!jsonWriteResult)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Unable to write class %s with json serialization format'",
|
||||
classId.ToString<AZStd::string>().data()));
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> SaveObjectToFileByType(const void* classPtr, const Uuid& classId, const AZStd::string& filePath,
|
||||
const void* defaultClassPtr, const JsonSerializerSettings* settings)
|
||||
{
|
||||
AZStd::vector<char> buffer;
|
||||
buffer.reserve(1024);
|
||||
AZ::IO::ByteContainerStream<AZStd::vector<char> > byteStream(&buffer);
|
||||
auto saveResult = SaveObjectToStreamByType(classPtr, classId, byteStream, defaultClassPtr, settings);
|
||||
if (saveResult.IsSuccess())
|
||||
{
|
||||
AZ::IO::FileIOStream outputFileStream;
|
||||
if (!outputFileStream.Open(filePath.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath | AZ::IO::OpenMode::ModeText))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Error opening file '%s' for writing", filePath.c_str()));
|
||||
}
|
||||
outputFileStream.Write(buffer.size(), buffer.data());
|
||||
}
|
||||
return saveResult;
|
||||
}
|
||||
|
||||
// Helper function to check whether the load outcome was success (for loading json serialization file)
|
||||
bool WasLoadSuccess(JsonSerializationResult::Outcomes outcome)
|
||||
{
|
||||
return (outcome == JsonSerializationResult::Outcomes::Success
|
||||
|| outcome == JsonSerializationResult::Outcomes::DefaultsUsed
|
||||
|| outcome == JsonSerializationResult::Outcomes::PartialDefaults);
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> PrepareDeserializerSettings(const JsonDeserializerSettings* inputSettings, JsonDeserializerSettings& returnSettings
|
||||
, AZStd::string& deserializeError)
|
||||
{
|
||||
if (inputSettings)
|
||||
{
|
||||
returnSettings = *inputSettings;
|
||||
}
|
||||
|
||||
if (!returnSettings.m_serializeContext)
|
||||
{
|
||||
AZ::ComponentApplicationBus::BroadcastResult(returnSettings.m_serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
if (!returnSettings.m_serializeContext)
|
||||
{
|
||||
AZ::ComponentApplicationBus::BroadcastResult(returnSettings.m_serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
if (!returnSettings.m_serializeContext)
|
||||
return AZ::Failure(AZStd::string("Need SerializeContext for loading"));
|
||||
}
|
||||
}
|
||||
|
||||
// Report unused data field as error by default
|
||||
auto reporting = returnSettings.m_reporting;
|
||||
auto issueReportingCallback = [&deserializeError, reporting](AZStd::string_view message, JsonSerializationResult::ResultCode result, AZStd::string_view target) -> JsonSerializationResult::ResultCode
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
if (!WasLoadSuccess(result.GetOutcome()))
|
||||
{
|
||||
// This if is a hack around fault in the JSON serialization system
|
||||
// Jira: LY-106587
|
||||
if (message != "No part of the string could be interpreted as a uuid.")
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Need SerializeContext for loading"));
|
||||
deserializeError.append(message);
|
||||
deserializeError.append(AZStd::string::format(" '%s' \n", target.data()));
|
||||
}
|
||||
}
|
||||
|
||||
// Report unused data field as error by default
|
||||
auto reporting = returnSettings.m_reporting;
|
||||
auto issueReportingCallback = [&deserializeError, reporting](AZStd::string_view message, JsonSerializationResult::ResultCode result, AZStd::string_view target) -> JsonSerializationResult::ResultCode
|
||||
if (reporting)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
result = reporting(message, result, target);
|
||||
}
|
||||
|
||||
if (!WasLoadSuccess(result.GetOutcome()))
|
||||
{
|
||||
// This if is a hack around fault in the JSON serialization system
|
||||
// Jira: LY-106587
|
||||
if (message != "No part of the string could be interpreted as a uuid.")
|
||||
{
|
||||
deserializeError.append(message);
|
||||
deserializeError.append(AZStd::string::format(" '%s' \n", target.data()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
if (reporting)
|
||||
{
|
||||
result = reporting(message, result, target);
|
||||
}
|
||||
returnSettings.m_reporting = issueReportingCallback;
|
||||
|
||||
return result;
|
||||
};
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
returnSettings.m_reporting = issueReportingCallback;
|
||||
|
||||
return AZ::Success();
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonString(AZStd::string_view jsonText)
|
||||
{
|
||||
if (jsonText.empty())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Failed to parse JSON: input string is empty."));
|
||||
}
|
||||
|
||||
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonString(AZStd::string_view jsonText)
|
||||
rapidjson::Document jsonDocument;
|
||||
jsonDocument.Parse<rapidjson::kParseCommentsFlag>(jsonText.data(), jsonText.size());
|
||||
if (jsonDocument.HasParseError())
|
||||
{
|
||||
if (jsonText.empty())
|
||||
size_t lineNumber = 1;
|
||||
|
||||
const size_t errorOffset = jsonDocument.GetErrorOffset();
|
||||
for (size_t searchOffset = jsonText.find('\n');
|
||||
searchOffset < errorOffset && searchOffset < AZStd::string::npos;
|
||||
searchOffset = jsonText.find('\n', searchOffset + 1))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Failed to parse JSON: input string is empty."));
|
||||
lineNumber++;
|
||||
}
|
||||
|
||||
rapidjson::Document jsonDocument;
|
||||
jsonDocument.Parse<rapidjson::kParseCommentsFlag>(jsonText.data(), jsonText.size());
|
||||
if (jsonDocument.HasParseError())
|
||||
{
|
||||
size_t lineNumber = 1;
|
||||
return AZ::Failure(AZStd::string::format("JSON parse error at line %zu: %s", lineNumber, rapidjson::GetParseError_En(jsonDocument.GetParseError())));
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Success(AZStd::move(jsonDocument));
|
||||
}
|
||||
}
|
||||
|
||||
const size_t errorOffset = jsonDocument.GetErrorOffset();
|
||||
for (size_t searchOffset = jsonText.find('\n');
|
||||
searchOffset < errorOffset && searchOffset < AZStd::string::npos;
|
||||
searchOffset = jsonText.find('\n', searchOffset + 1))
|
||||
{
|
||||
lineNumber++;
|
||||
}
|
||||
|
||||
return AZ::Failure(AZStd::string::format("JSON parse error at line %zu: %s", lineNumber, rapidjson::GetParseError_En(jsonDocument.GetParseError())));
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Success(AZStd::move(jsonDocument));
|
||||
}
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonStream(IO::GenericStream& stream)
|
||||
{
|
||||
IO::SizeType length = stream.GetLength();
|
||||
|
||||
AZStd::vector<char> memoryBuffer;
|
||||
memoryBuffer.resize_no_construct(static_cast<AZStd::vector<char>::size_type>(static_cast<AZStd::vector<char>::size_type>(length) + 1));
|
||||
|
||||
IO::SizeType bytesRead = stream.Read(length, memoryBuffer.data());
|
||||
if (bytesRead != length)
|
||||
{
|
||||
return AZ::Failure(AZStd::string{"Cannot to read input stream."});
|
||||
}
|
||||
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonStream(IO::GenericStream& stream)
|
||||
memoryBuffer.back() = 0;
|
||||
|
||||
return ReadJsonString(AZStd::string_view{memoryBuffer.data(), memoryBuffer.size()});
|
||||
}
|
||||
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonFile(AZStd::string_view filePath, size_t maxFileSize)
|
||||
{
|
||||
// Read into memory first and then parse the json, rather than passing a file stream to rapidjson.
|
||||
// This should avoid creating a large number of micro-reads from the file.
|
||||
|
||||
auto readResult = AZ::Utils::ReadFile<AZStd::string>(filePath, maxFileSize);
|
||||
if(!readResult.IsSuccess())
|
||||
{
|
||||
IO::SizeType length = stream.GetLength();
|
||||
|
||||
AZStd::vector<char> memoryBuffer;
|
||||
memoryBuffer.resize_no_construct(static_cast<AZStd::vector<char>::size_type>(static_cast<AZStd::vector<char>::size_type>(length) + 1));
|
||||
|
||||
IO::SizeType bytesRead = stream.Read(length, memoryBuffer.data());
|
||||
if (bytesRead != length)
|
||||
{
|
||||
return AZ::Failure(AZStd::string{"Cannot to read input stream."});
|
||||
}
|
||||
|
||||
memoryBuffer.back() = 0;
|
||||
|
||||
return ReadJsonString(AZStd::string_view{memoryBuffer.data(), memoryBuffer.size()});
|
||||
return AZ::Failure(readResult.GetError());
|
||||
}
|
||||
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonFile(AZStd::string_view filePath, size_t maxFileSize)
|
||||
AZStd::string jsonContent = readResult.TakeValue();
|
||||
|
||||
auto result = ReadJsonString(jsonContent);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
// Read into memory first and then parse the json, rather than passing a file stream to rapidjson.
|
||||
// This should avoid creating a large number of micro-reads from the file.
|
||||
return AZ::Failure(AZStd::string::format("Failed to load '%.*s'. %s", AZ_STRING_ARG(filePath), result.GetError().c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
auto readResult = AZ::Utils::ReadFile<AZStd::string>(filePath, maxFileSize);
|
||||
if(!readResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(readResult.GetError());
|
||||
}
|
||||
|
||||
AZStd::string jsonContent = readResult.TakeValue();
|
||||
|
||||
auto result = ReadJsonString(jsonContent);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to load '%.*s'. %s", AZ_STRING_ARG(filePath), result.GetError().c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return result;
|
||||
}
|
||||
// Helper function to validate the JSON is structured with the standard header for a generic class
|
||||
AZ::Outcome<void, AZStd::string> ValidateJsonClassHeader(const rapidjson::Document& jsonDocument)
|
||||
{
|
||||
auto typeItr = jsonDocument.FindMember(FileTypeTag);
|
||||
if (typeItr == jsonDocument.MemberEnd() || !typeItr->value.IsString() || azstricmp(typeItr->value.GetString(), FileType) != 0)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Not a valid JsonSerialization file"));
|
||||
}
|
||||
|
||||
// Helper function to validate the JSON is structured with the standard header for a generic class
|
||||
AZ::Outcome<void, AZStd::string> ValidateJsonClassHeader(const rapidjson::Document& jsonDocument)
|
||||
auto nameItr = jsonDocument.FindMember(ClassNameTag);
|
||||
if (nameItr == jsonDocument.MemberEnd() || !nameItr->value.IsString())
|
||||
{
|
||||
auto typeItr = jsonDocument.FindMember(FileTypeTag);
|
||||
if (typeItr == jsonDocument.MemberEnd() || !typeItr->value.IsString() || azstricmp(typeItr->value.GetString(), FileType) != 0)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Not a valid JsonSerialization file"));
|
||||
}
|
||||
|
||||
auto nameItr = jsonDocument.FindMember(ClassNameTag);
|
||||
if (nameItr == jsonDocument.MemberEnd() || !nameItr->value.IsString())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("File should contain ClassName"));
|
||||
}
|
||||
|
||||
auto dataItr = jsonDocument.FindMember(ClassDataTag);
|
||||
// data can be empty but it should be an object
|
||||
if (dataItr != jsonDocument.MemberEnd() && !dataItr->value.IsObject())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("ClassData should be an object"));
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
return AZ::Failure(AZStd::string::format("File should contain ClassName"));
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> LoadObjectFromStringByType(void* objectToLoad, const Uuid& classId, AZStd::string_view stream,
|
||||
const JsonDeserializerSettings* settings)
|
||||
auto dataItr = jsonDocument.FindMember(ClassDataTag);
|
||||
// data can be empty but it should be an object
|
||||
if (dataItr != jsonDocument.MemberEnd() && !dataItr->value.IsObject())
|
||||
{
|
||||
JsonDeserializerSettings loadSettings;
|
||||
AZStd::string deserializeErrors;
|
||||
auto prepare = PrepareDeserializerSettings(settings, loadSettings, deserializeErrors);
|
||||
if (!prepare.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(prepare.GetError());
|
||||
}
|
||||
return AZ::Failure(AZStd::string::format("ClassData should be an object"));
|
||||
}
|
||||
|
||||
auto parseResult = ReadJsonString(stream);
|
||||
if (!parseResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(parseResult.GetError());
|
||||
}
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
const rapidjson::Document& jsonDocument = parseResult.GetValue();
|
||||
AZ::Outcome<void, AZStd::string> LoadObjectFromStringByType(void* objectToLoad, const Uuid& classId, AZStd::string_view stream,
|
||||
const JsonDeserializerSettings* settings)
|
||||
{
|
||||
JsonDeserializerSettings loadSettings;
|
||||
AZStd::string deserializeErrors;
|
||||
auto prepare = PrepareDeserializerSettings(settings, loadSettings, deserializeErrors);
|
||||
if (!prepare.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(prepare.GetError());
|
||||
}
|
||||
|
||||
auto validateResult = ValidateJsonClassHeader(jsonDocument);
|
||||
if (!validateResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(validateResult.GetError());
|
||||
}
|
||||
auto parseResult = ReadJsonString(stream);
|
||||
if (!parseResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(parseResult.GetError());
|
||||
}
|
||||
|
||||
const char* className = jsonDocument.FindMember(ClassNameTag)->value.GetString();
|
||||
const rapidjson::Document& jsonDocument = parseResult.GetValue();
|
||||
|
||||
// validate class name
|
||||
auto classData = loadSettings.m_serializeContext->FindClassData(classId);
|
||||
if (!classData)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Try to load class from Id %s", classId.ToString<AZStd::string>().c_str()));
|
||||
}
|
||||
auto validateResult = ValidateJsonClassHeader(jsonDocument);
|
||||
if (!validateResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(validateResult.GetError());
|
||||
}
|
||||
|
||||
if (azstricmp(classData->m_name, className) != 0)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Try to load class %s from class %s data", classData->m_name, className));
|
||||
}
|
||||
const char* className = jsonDocument.FindMember(ClassNameTag)->value.GetString();
|
||||
|
||||
JsonSerializationResult::ResultCode result = JsonSerialization::Load(objectToLoad, classId, jsonDocument.FindMember(ClassDataTag)->value, loadSettings);
|
||||
// validate class name
|
||||
auto classData = loadSettings.m_serializeContext->FindClassData(classId);
|
||||
if (!classData)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Try to load class from Id %s", classId.ToString<AZStd::string>().c_str()));
|
||||
}
|
||||
|
||||
if (azstricmp(classData->m_name, className) != 0)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Try to load class %s from class %s data", classData->m_name, className));
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode result = JsonSerialization::Load(objectToLoad, classId, jsonDocument.FindMember(ClassDataTag)->value, loadSettings);
|
||||
|
||||
if (!WasLoadSuccess(result.GetOutcome()) || !deserializeErrors.empty())
|
||||
{
|
||||
return AZ::Failure(deserializeErrors);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> LoadObjectFromStreamByType(void* objectToLoad, const Uuid& classId, IO::GenericStream& stream,
|
||||
const JsonDeserializerSettings* settings)
|
||||
{
|
||||
JsonDeserializerSettings loadSettings;
|
||||
AZStd::string deserializeErrors;
|
||||
auto prepare = PrepareDeserializerSettings(settings, loadSettings, deserializeErrors);
|
||||
if (!prepare.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(prepare.GetError());
|
||||
}
|
||||
|
||||
auto parseResult = ReadJsonStream(stream);
|
||||
if (!parseResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(parseResult.GetError());
|
||||
}
|
||||
|
||||
const rapidjson::Document& jsonDocument = parseResult.GetValue();
|
||||
|
||||
auto validateResult = ValidateJsonClassHeader(jsonDocument);
|
||||
if (!validateResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(validateResult.GetError());
|
||||
}
|
||||
|
||||
const char* className = jsonDocument.FindMember(ClassNameTag)->value.GetString();
|
||||
|
||||
// validate class name
|
||||
auto classData = loadSettings.m_serializeContext->FindClassData(classId);
|
||||
if (!classData)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Try to load class from Id %s", classId.ToString<AZStd::string>().c_str()));
|
||||
}
|
||||
|
||||
if (azstricmp(classData->m_name, className) != 0)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Try to load class %s from class %s data", classData->m_name, className));
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode result = JsonSerialization::Load(objectToLoad, classId, jsonDocument.FindMember(ClassDataTag)->value, loadSettings);
|
||||
|
||||
if (!WasLoadSuccess(result.GetOutcome()) || !deserializeErrors.empty())
|
||||
{
|
||||
return AZ::Failure(deserializeErrors);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::any, AZStd::string> LoadAnyObjectFromStream(IO::GenericStream& stream, const JsonDeserializerSettings* settings)
|
||||
{
|
||||
JsonDeserializerSettings loadSettings;
|
||||
AZStd::string deserializeErrors;
|
||||
auto prepare = PrepareDeserializerSettings(settings, loadSettings, deserializeErrors);
|
||||
if (!prepare.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(prepare.GetError());
|
||||
}
|
||||
|
||||
auto parseResult = ReadJsonStream(stream);
|
||||
if (!parseResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(parseResult.GetError());
|
||||
}
|
||||
|
||||
const rapidjson::Document& jsonDocument = parseResult.GetValue();
|
||||
|
||||
auto validateResult = ValidateJsonClassHeader(jsonDocument);
|
||||
if (!parseResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(parseResult.GetError());
|
||||
}
|
||||
|
||||
const char* className = jsonDocument.FindMember(ClassNameTag)->value.GetString();
|
||||
AZStd::vector<AZ::Uuid> ids = loadSettings.m_serializeContext->FindClassId(AZ::Crc32(className));
|
||||
|
||||
// Load with first found class id
|
||||
if (ids.size() >= 1)
|
||||
{
|
||||
auto classId = ids[0];
|
||||
AZStd::any anyData = loadSettings.m_serializeContext->CreateAny(classId);
|
||||
auto& objectData = jsonDocument.FindMember(ClassDataTag)->value;
|
||||
JsonSerializationResult::ResultCode result = JsonSerialization::Load(AZStd::any_cast<void>(&anyData), classId, objectData, loadSettings);
|
||||
|
||||
if (!WasLoadSuccess(result.GetOutcome()) || !deserializeErrors.empty())
|
||||
{
|
||||
return AZ::Failure(deserializeErrors);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
return AZ::Success(anyData);
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> LoadObjectFromStreamByType(void* objectToLoad, const Uuid& classId, IO::GenericStream& stream,
|
||||
const JsonDeserializerSettings* settings)
|
||||
return AZ::Failure(AZStd::string::format("Can't find serialize context for class %s", className));
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::any, AZStd::string> LoadAnyObjectFromFile(const AZStd::string& filePath, const JsonDeserializerSettings* settings)
|
||||
{
|
||||
AZ::IO::FileIOStream inputFileStream;
|
||||
if (!inputFileStream.Open(filePath.c_str(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeText))
|
||||
{
|
||||
JsonDeserializerSettings loadSettings;
|
||||
AZStd::string deserializeErrors;
|
||||
auto prepare = PrepareDeserializerSettings(settings, loadSettings, deserializeErrors);
|
||||
if (!prepare.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(prepare.GetError());
|
||||
}
|
||||
|
||||
auto parseResult = ReadJsonStream(stream);
|
||||
if (!parseResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(parseResult.GetError());
|
||||
}
|
||||
|
||||
const rapidjson::Document& jsonDocument = parseResult.GetValue();
|
||||
|
||||
auto validateResult = ValidateJsonClassHeader(jsonDocument);
|
||||
if (!validateResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(validateResult.GetError());
|
||||
}
|
||||
|
||||
const char* className = jsonDocument.FindMember(ClassNameTag)->value.GetString();
|
||||
|
||||
// validate class name
|
||||
auto classData = loadSettings.m_serializeContext->FindClassData(classId);
|
||||
if (!classData)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Try to load class from Id %s", classId.ToString<AZStd::string>().c_str()));
|
||||
}
|
||||
|
||||
if (azstricmp(classData->m_name, className) != 0)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Try to load class %s from class %s data", classData->m_name, className));
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode result = JsonSerialization::Load(objectToLoad, classId, jsonDocument.FindMember(ClassDataTag)->value, loadSettings);
|
||||
|
||||
if (!WasLoadSuccess(result.GetOutcome()) || !deserializeErrors.empty())
|
||||
{
|
||||
return AZ::Failure(deserializeErrors);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::any, AZStd::string> LoadAnyObjectFromStream(IO::GenericStream& stream, const JsonDeserializerSettings* settings)
|
||||
{
|
||||
JsonDeserializerSettings loadSettings;
|
||||
AZStd::string deserializeErrors;
|
||||
auto prepare = PrepareDeserializerSettings(settings, loadSettings, deserializeErrors);
|
||||
if (!prepare.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(prepare.GetError());
|
||||
}
|
||||
|
||||
auto parseResult = ReadJsonStream(stream);
|
||||
if (!parseResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(parseResult.GetError());
|
||||
}
|
||||
|
||||
const rapidjson::Document& jsonDocument = parseResult.GetValue();
|
||||
|
||||
auto validateResult = ValidateJsonClassHeader(jsonDocument);
|
||||
if (!parseResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(parseResult.GetError());
|
||||
}
|
||||
|
||||
const char* className = jsonDocument.FindMember(ClassNameTag)->value.GetString();
|
||||
AZStd::vector<AZ::Uuid> ids = loadSettings.m_serializeContext->FindClassId(AZ::Crc32(className));
|
||||
|
||||
// Load with first found class id
|
||||
if (ids.size() >= 1)
|
||||
{
|
||||
auto classId = ids[0];
|
||||
AZStd::any anyData = loadSettings.m_serializeContext->CreateAny(classId);
|
||||
auto& objectData = jsonDocument.FindMember(ClassDataTag)->value;
|
||||
JsonSerializationResult::ResultCode result = JsonSerialization::Load(AZStd::any_cast<void>(&anyData), classId, objectData, loadSettings);
|
||||
|
||||
if (!WasLoadSuccess(result.GetOutcome()) || !deserializeErrors.empty())
|
||||
{
|
||||
return AZ::Failure(deserializeErrors);
|
||||
}
|
||||
|
||||
return AZ::Success(anyData);
|
||||
}
|
||||
|
||||
return AZ::Failure(AZStd::string::format("Can't find serialize context for class %s", className));
|
||||
return AZ::Failure(AZStd::string::format("Error opening file '%s' for reading", filePath.c_str()));
|
||||
}
|
||||
return LoadAnyObjectFromStream(inputFileStream, settings);
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::any, AZStd::string> LoadAnyObjectFromFile(const AZStd::string& filePath, const JsonDeserializerSettings* settings)
|
||||
{
|
||||
AZ::IO::FileIOStream inputFileStream;
|
||||
if (!inputFileStream.Open(filePath.c_str(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeText))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Error opening file '%s' for reading", filePath.c_str()));
|
||||
}
|
||||
return LoadAnyObjectFromStream(inputFileStream, settings);
|
||||
}
|
||||
|
||||
} // namespace JsonSerializationUtils
|
||||
} // namespace AZ
|
||||
} // namespace AZ::JsonSerializationUtils
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace AZ
|
||||
AZ_CLASS_ALLOCATOR_IMPL(JsonAnySerializer, SystemAllocator, 0);
|
||||
AZ_CLASS_ALLOCATOR_IMPL(JsonVariantSerializer, SystemAllocator, 0);
|
||||
AZ_CLASS_ALLOCATOR_IMPL(JsonOptionalSerializer, SystemAllocator, 0);
|
||||
AZ_CLASS_ALLOCATOR_IMPL(JsonBitsetSerializer, SystemAllocator, 0);
|
||||
|
||||
JsonSerializationResult::Result JsonUnsupportedTypesSerializer::Load(void*, const Uuid&, const rapidjson::Value&,
|
||||
JsonDeserializerContext& context)
|
||||
@@ -49,4 +50,10 @@ namespace AZ
|
||||
return "The Json Serialization doesn't support AZStd::optional by design. No JSON format has yet been found that wasn't deemed too "
|
||||
"complex or overly verbose.";
|
||||
}
|
||||
|
||||
AZStd::string_view JsonBitsetSerializer::GetMessage() const
|
||||
{
|
||||
return "The Json Serialization doesn't support AZStd::bitset by design. No JSON format has yet been found that is content creator "
|
||||
"friendly i.e., easy to comprehend the intent.";
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
@@ -65,4 +65,14 @@ namespace AZ
|
||||
protected:
|
||||
AZStd::string_view GetMessage() const override;
|
||||
};
|
||||
|
||||
class JsonBitsetSerializer : public JsonUnsupportedTypesSerializer
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(JsonBitsetSerializer, "{10CE969D-D69E-4B3F-8593-069736F8F705}", JsonUnsupportedTypesSerializer);
|
||||
AZ_CLASS_ALLOCATOR_DECL;
|
||||
|
||||
protected:
|
||||
AZStd::string_view GetMessage() const override;
|
||||
};
|
||||
} // namespace AZ
|
||||
|
||||
@@ -1050,7 +1050,7 @@ namespace AZ
|
||||
}
|
||||
m_xmlNode = next;
|
||||
|
||||
Uuid specializedId;
|
||||
Uuid specializedId = Uuid::CreateNull();
|
||||
// now parse the node
|
||||
rapidxml::xml_attribute<char>* attr = m_xmlNode->first_attribute();
|
||||
while (attr)
|
||||
@@ -1643,12 +1643,15 @@ namespace AZ
|
||||
m_writeElementResultStack.push_back(WriteElement(ptr, classData, classElement));
|
||||
return m_writeElementResultStack.back();
|
||||
};
|
||||
auto closeElementCB = [this, classData]()
|
||||
auto closeElementCB = [this, classTypeId = classData->m_typeId]()
|
||||
{
|
||||
if (m_writeElementResultStack.empty())
|
||||
{
|
||||
AZ_UNUSED(classData); // Prevent unused warning in release builds
|
||||
AZ_Error("Serialize", false, "CloseElement is attempted to be called without a corresponding WriteElement when writing class %s", classData->m_name);
|
||||
// ClassData could be dangling pointer if it was unreflected by the ObjectStreamWriteOverrideCB
|
||||
// So use the classTypeId instead
|
||||
AZ_UNUSED(classTypeId);
|
||||
AZ_Error("Serialize", false, "CloseElement is attempted to be called without a corresponding WriteElement when writing class %s",
|
||||
classTypeId.ToString<AZStd::fixed_string<AZ::TypeId::MaxStringBuffer>>().c_str());
|
||||
return true;
|
||||
}
|
||||
if (m_writeElementResultStack.back())
|
||||
@@ -1665,16 +1668,14 @@ namespace AZ
|
||||
SerializeContext::ENUM_ACCESS_FOR_READ,
|
||||
&m_errorLogger
|
||||
);
|
||||
ObjectStreamWriteOverrideCB writeCB;
|
||||
if (objectStreamWriteOverrideCB.Read<ObjectStreamWriteOverrideCB>(writeCB))
|
||||
if (objectStreamWriteOverrideCB.Invoke<void>(callContext, objectPtr, *classData, classElement))
|
||||
{
|
||||
writeCB(callContext, objectPtr, *classData, classElement);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto objectStreamError = AZStd::string::format("Unable to invoke ObjectStream Write Element Override for class element %s of class data %s",
|
||||
classElement->m_name ? classElement->m_name : "", classData->m_name);
|
||||
classElement && classElement->m_name ? classElement->m_name : "", classData->m_name);
|
||||
m_errorLogger.ReportError(objectStreamError.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,502 +18,499 @@
|
||||
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Utils
|
||||
{
|
||||
namespace Utils
|
||||
bool LoadObjectFromStreamInPlace(IO::GenericStream& stream, AZ::SerializeContext* context, const SerializeContext::ClassData* objectClassData, void* targetPointer, const FilterDescriptor& filterDesc)
|
||||
{
|
||||
bool LoadObjectFromStreamInPlace(IO::GenericStream& stream, AZ::SerializeContext* context, const SerializeContext::ClassData* objectClassData, void* targetPointer, const FilterDescriptor& filterDesc)
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
AZ_Assert(objectClassData, "Class data is required.");
|
||||
|
||||
if (!context)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
AZ_Assert(objectClassData, "Class data is required.");
|
||||
|
||||
if (!context)
|
||||
{
|
||||
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
|
||||
AZ_Assert(context, "No serialize context");
|
||||
}
|
||||
|
||||
if (!context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ_Assert(targetPointer, "You must provide a target pointer");
|
||||
|
||||
bool foundSuccess = false;
|
||||
using CreationCallback = AZStd::function<void (void **, const SerializeContext::ClassData **, const Uuid &, SerializeContext *)>;
|
||||
auto handler = [&targetPointer, objectClassData, &foundSuccess](void** instance, const SerializeContext::ClassData** classData, const Uuid& classId, SerializeContext* context)
|
||||
{
|
||||
void* convertibleInstance{};
|
||||
if (objectClassData->ConvertFromType(convertibleInstance, classId, targetPointer, *context))
|
||||
{
|
||||
foundSuccess = true;
|
||||
if (instance)
|
||||
{
|
||||
// The ObjectStream will ask us for the address of the target to load into, so provide it.
|
||||
*instance = convertibleInstance;
|
||||
}
|
||||
if (classData)
|
||||
{
|
||||
// The ObjectStream will ask us for the class data of the target being loaded into, so provide it if needed.
|
||||
// This allows us to load directly into a generic object (templated containers, strings, etc).
|
||||
*classData = objectClassData;
|
||||
}
|
||||
}
|
||||
};
|
||||
bool readSuccess = ObjectStream::LoadBlocking(&stream, *context, ObjectStream::ClassReadyCB(), filterDesc, CreationCallback(handler, AZ::OSStdAllocator()));
|
||||
|
||||
AZ_Warning("Serialization", readSuccess, "LoadObjectFromStreamInPlace: Stream did not deserialize correctly");
|
||||
AZ_Warning("Serialization", foundSuccess, "LoadObjectFromStreamInPlace: Did not find the expected type in the stream");
|
||||
|
||||
return readSuccess && foundSuccess;
|
||||
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
|
||||
AZ_Assert(context, "No serialize context");
|
||||
}
|
||||
|
||||
bool LoadObjectFromStreamInPlace(IO::GenericStream& stream, AZ::SerializeContext* context, const Uuid& targetClassId, void* targetPointer, const FilterDescriptor& filterDesc)
|
||||
if (!context)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
if (!context)
|
||||
{
|
||||
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
|
||||
AZ_Assert(context, "No serialize context");
|
||||
}
|
||||
|
||||
if (!context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const SerializeContext::ClassData* classData = context->FindClassData(targetClassId);
|
||||
if (!classData)
|
||||
{
|
||||
AZ_Error("Serialization", false,
|
||||
"Unable to locate class data for uuid \"%s\". This object cannot be serialized as a root element. "
|
||||
"Make sure the Uuid is valid, or if this is a generic type, use the override that takes a ClassData pointer instead.",
|
||||
targetClassId.ToString<AZStd::string>().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadObjectFromStreamInPlace(stream, context, classData, targetPointer, filterDesc);
|
||||
}
|
||||
|
||||
bool LoadObjectFromFileInPlace(const AZStd::string& filePath, const Uuid& targetClassId, void* destination, AZ::SerializeContext* context /*= nullptr*/, const FilterDescriptor& filterDesc /*= FilterDescriptor()*/)
|
||||
{
|
||||
AZ::IO::FileIOStream fileStream;
|
||||
if (!fileStream.Open(filePath.c_str(), IO::OpenMode::ModeRead | IO::OpenMode::ModeBinary))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadObjectFromStreamInPlace(fileStream, context, targetClassId, destination, filterDesc);
|
||||
}
|
||||
|
||||
void* LoadObjectFromStream(IO::GenericStream& stream, AZ::SerializeContext* context, const Uuid* targetClassId, const FilterDescriptor& filterDesc)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
if (!context)
|
||||
{
|
||||
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
|
||||
AZ_Assert(context, "No serialize context");
|
||||
}
|
||||
|
||||
if (!context)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* loadedInstance = nullptr;
|
||||
bool success = ObjectStream::LoadBlocking(&stream, *context,
|
||||
[&loadedInstance, targetClassId](void* classPtr, const Uuid& classId, const SerializeContext* serializeContext)
|
||||
{
|
||||
if (targetClassId)
|
||||
{
|
||||
void* instance = serializeContext->DownCast(classPtr, classId, *targetClassId);
|
||||
|
||||
// Given a valid object
|
||||
if (instance)
|
||||
{
|
||||
AZ_Assert(!loadedInstance, "loadedInstance must be NULL, otherwise we are being invoked with multiple valid objects");
|
||||
loadedInstance = instance;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!loadedInstance)
|
||||
{
|
||||
loadedInstance = classPtr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto classData = serializeContext->FindClassData(classId);
|
||||
if (classData && classData->m_factory)
|
||||
{
|
||||
classData->m_factory->Destroy(classPtr);
|
||||
}
|
||||
},
|
||||
filterDesc,
|
||||
ObjectStream::InplaceLoadRootInfoCB()
|
||||
);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return loadedInstance;
|
||||
}
|
||||
|
||||
void* LoadObjectFromFile(const AZStd::string& filePath, const Uuid& targetClassId, SerializeContext* context, const FilterDescriptor& filterDesc, int /*platformFlags*/)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
AZ::IO::FileIOStream fileStream;
|
||||
if (!fileStream.Open(filePath.c_str(), IO::OpenMode::ModeRead | IO::OpenMode::ModeBinary))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* loadedObject = LoadObjectFromStream(fileStream, context, &targetClassId, filterDesc);
|
||||
return loadedObject;
|
||||
}
|
||||
|
||||
bool SaveObjectToStream(IO::GenericStream& stream, DataStream::StreamType streamType, const void* classPtr, const Uuid& classId, SerializeContext* context, const SerializeContext::ClassData* classData)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
if (!context)
|
||||
{
|
||||
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
|
||||
|
||||
if(!context)
|
||||
{
|
||||
AZ_Assert(false, "No serialize context");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!classPtr)
|
||||
{
|
||||
AZ_Assert(false, "SaveObjectToStream: classPtr is null, object cannot be serialized.");
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::ObjectStream* objectStream = AZ::ObjectStream::Create(&stream, *context, streamType);
|
||||
if (!objectStream)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!objectStream->WriteClass(classPtr, classId, classData))
|
||||
{
|
||||
objectStream->Finalize();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!objectStream->Finalize())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveStreamToFile(const AZStd::string& filePath, const AZStd::vector<AZ::u8>& streamData, int platformFlags)
|
||||
{
|
||||
AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance();
|
||||
AZ::IO::FixedMaxPathString resolvedPath;
|
||||
if (fileIo == nullptr || !fileIo->ResolvePath(filePath.c_str(), resolvedPath.data(), resolvedPath.capacity() + 1))
|
||||
{
|
||||
resolvedPath = filePath;
|
||||
}
|
||||
if (AZ::IO::SystemFile fileHandle; fileHandle.Open(resolvedPath.c_str(),
|
||||
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY,
|
||||
platformFlags))
|
||||
{
|
||||
AZ::IO::SizeType bytesWritten = fileHandle.Write(streamData.data(), streamData.size());
|
||||
return bytesWritten == streamData.size();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SaveObjectToFile(const AZStd::string& filePath, DataStream::StreamType fileType, const void* classPtr, const Uuid& classId, SerializeContext* context, int platformFlags)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
AZ_Assert(targetPointer, "You must provide a target pointer");
|
||||
|
||||
// \note This is ok for tools, but we should use the streamer to write objects directly (no memory store)
|
||||
AZStd::vector<AZ::u8> dstData;
|
||||
AZ::IO::ByteContainerStream<AZStd::vector<AZ::u8> > dstByteStream(&dstData);
|
||||
|
||||
if (!SaveObjectToStream(dstByteStream, fileType, classPtr, classId, context))
|
||||
bool foundSuccess = false;
|
||||
using CreationCallback = AZStd::function<void (void **, const SerializeContext::ClassData **, const Uuid &, SerializeContext *)>;
|
||||
auto handler = [&targetPointer, objectClassData, &foundSuccess](void** instance, const SerializeContext::ClassData** classData, const Uuid& classId, SerializeContext* context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return SaveStreamToFile(filePath, dstData, platformFlags);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Finds any descendant DataElementNodes of the @classElement which match each Crc32 values in the supplied elementCrcQueue
|
||||
\param context SerializeContext used for looking up ClassData
|
||||
\param classElement Top level DataElementNode to begin comparison each the Crc32 queue
|
||||
\param elementCrcQueue Container of Crc32 values in the order in which DataElementNodes should be matched as the DataElementNode tree is traversed
|
||||
\return Vector of valid pointers to DataElementNodes which match the entire element Crc32 queue
|
||||
*/
|
||||
AZStd::vector<AZ::SerializeContext::DataElementNode*> FindDescendantElements(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement,
|
||||
const AZStd::vector<AZ::Crc32>& elementCrcQueue)
|
||||
{
|
||||
AZStd::vector<AZ::SerializeContext::DataElementNode*> dataElementNodes;
|
||||
FindDescendantElements(context, classElement, dataElementNodes, elementCrcQueue.begin(), elementCrcQueue.end());
|
||||
|
||||
return dataElementNodes;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Finds any descendant DataElementNodes of the @classElement which match each Crc32 values in the supplied elementCrcQueue
|
||||
\param context SerializeContext used for looking up ClassData
|
||||
\param classElement The current DataElementNode which will be compared against be to current top Crc32 value in the Crc32 queue
|
||||
\param dataElementNodes[out] Array to populate with a DataElementNode which was found by matching all Crc32 values in the Crc32 queue
|
||||
\param first The current front of the Crc32 queue
|
||||
\param last The end of the Crc32 queue
|
||||
*/
|
||||
void FindDescendantElements(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement,
|
||||
AZStd::vector<AZ::SerializeContext::DataElementNode*>& dataElementNodes, AZStd::vector<AZ::Crc32>::const_iterator first, AZStd::vector<AZ::Crc32>::const_iterator last)
|
||||
{
|
||||
if (first == last)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < classElement.GetNumSubElements(); ++i)
|
||||
{
|
||||
auto& childElement = classElement.GetSubElement(i);
|
||||
if (*first == AZ::Crc32(childElement.GetName()))
|
||||
void* convertibleInstance{};
|
||||
if (objectClassData->ConvertFromType(convertibleInstance, classId, targetPointer, *context))
|
||||
{
|
||||
if (AZStd::distance(first, last) == 1)
|
||||
foundSuccess = true;
|
||||
if (instance)
|
||||
{
|
||||
dataElementNodes.push_back(&childElement);
|
||||
// The ObjectStream will ask us for the address of the target to load into, so provide it.
|
||||
*instance = convertibleInstance;
|
||||
}
|
||||
if (classData)
|
||||
{
|
||||
// The ObjectStream will ask us for the class data of the target being loaded into, so provide it if needed.
|
||||
// This allows us to load directly into a generic object (templated containers, strings, etc).
|
||||
*classData = objectClassData;
|
||||
}
|
||||
}
|
||||
};
|
||||
bool readSuccess = ObjectStream::LoadBlocking(&stream, *context, ObjectStream::ClassReadyCB(), filterDesc, CreationCallback(handler, AZ::OSStdAllocator()));
|
||||
|
||||
AZ_Warning("Serialization", readSuccess, "LoadObjectFromStreamInPlace: Stream did not deserialize correctly");
|
||||
AZ_Warning("Serialization", foundSuccess, "LoadObjectFromStreamInPlace: Did not find the expected type in the stream");
|
||||
|
||||
return readSuccess && foundSuccess;
|
||||
}
|
||||
|
||||
bool LoadObjectFromStreamInPlace(IO::GenericStream& stream, AZ::SerializeContext* context, const Uuid& targetClassId, void* targetPointer, const FilterDescriptor& filterDesc)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
if (!context)
|
||||
{
|
||||
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
|
||||
AZ_Assert(context, "No serialize context");
|
||||
}
|
||||
|
||||
if (!context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const SerializeContext::ClassData* classData = context->FindClassData(targetClassId);
|
||||
if (!classData)
|
||||
{
|
||||
AZ_Error("Serialization", false,
|
||||
"Unable to locate class data for uuid \"%s\". This object cannot be serialized as a root element. "
|
||||
"Make sure the Uuid is valid, or if this is a generic type, use the override that takes a ClassData pointer instead.",
|
||||
targetClassId.ToString<AZStd::string>().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadObjectFromStreamInPlace(stream, context, classData, targetPointer, filterDesc);
|
||||
}
|
||||
|
||||
bool LoadObjectFromFileInPlace(const AZStd::string& filePath, const Uuid& targetClassId, void* destination, AZ::SerializeContext* context /*= nullptr*/, const FilterDescriptor& filterDesc /*= FilterDescriptor()*/)
|
||||
{
|
||||
AZ::IO::FileIOStream fileStream;
|
||||
if (!fileStream.Open(filePath.c_str(), IO::OpenMode::ModeRead | IO::OpenMode::ModeBinary))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadObjectFromStreamInPlace(fileStream, context, targetClassId, destination, filterDesc);
|
||||
}
|
||||
|
||||
void* LoadObjectFromStream(IO::GenericStream& stream, AZ::SerializeContext* context, const Uuid* targetClassId, const FilterDescriptor& filterDesc)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
if (!context)
|
||||
{
|
||||
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
|
||||
AZ_Assert(context, "No serialize context");
|
||||
}
|
||||
|
||||
if (!context)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* loadedInstance = nullptr;
|
||||
bool success = ObjectStream::LoadBlocking(&stream, *context,
|
||||
[&loadedInstance, targetClassId](void* classPtr, const Uuid& classId, const SerializeContext* serializeContext)
|
||||
{
|
||||
if (targetClassId)
|
||||
{
|
||||
void* instance = serializeContext->DownCast(classPtr, classId, *targetClassId);
|
||||
|
||||
// Given a valid object
|
||||
if (instance)
|
||||
{
|
||||
AZ_Assert(!loadedInstance, "loadedInstance must be NULL, otherwise we are being invoked with multiple valid objects");
|
||||
loadedInstance = instance;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FindDescendantElements(context, childElement, dataElementNodes, AZStd::next(first), last);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsVectorContainerType(const AZ::Uuid& type)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
AZ::TypeId containerTypeId = type;
|
||||
|
||||
if (GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type))
|
||||
{
|
||||
// This type is a container
|
||||
containerTypeId = classInfo->GetGenericTypeId();
|
||||
}
|
||||
|
||||
if (containerTypeId == AZ::GetGenericClassInfoVectorTypeId()
|
||||
|| containerTypeId == AZ::GetGenericClassInfoFixedVectorTypeId()
|
||||
|| containerTypeId == AZ::GetGenericClassInfoArrayTypeId()
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsSetContainerType(const AZ::Uuid& type)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
AZ::TypeId containerTypeId = type;
|
||||
|
||||
if (GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type))
|
||||
{
|
||||
// This type is a container
|
||||
containerTypeId = classInfo->GetGenericTypeId();
|
||||
}
|
||||
|
||||
if (containerTypeId == AZ::GetGenericClassSetTypeId()
|
||||
|| containerTypeId == AZ::GetGenericClassUnorderedSetTypeId()
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool IsMapContainerType(const AZ::Uuid& type)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
AZ::Uuid containerTypeId = type;
|
||||
|
||||
if (GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type))
|
||||
{
|
||||
containerTypeId = classInfo->GetGenericTypeId();
|
||||
}
|
||||
|
||||
if (containerTypeId == AZ::GetGenericClassMapTypeId()
|
||||
|| containerTypeId == AZ::GetGenericClassUnorderedMapTypeId()
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsContainerType(const AZ::Uuid& type)
|
||||
{
|
||||
return IsVectorContainerType(type) || IsSetContainerType(type) || IsMapContainerType(type);
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::Uuid> GetContainedTypes(const AZ::Uuid& type)
|
||||
{
|
||||
AZStd::vector<AZ::Uuid> types;
|
||||
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
if (GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type))
|
||||
{
|
||||
for (int i = 0; i < classInfo->GetNumTemplatedArguments(); ++i)
|
||||
{
|
||||
types.push_back(classInfo->GetTemplatedTypeId(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
bool IsOutcomeType(const AZ::Uuid& type)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type);
|
||||
return classInfo && classInfo->GetGenericTypeId() == AZ::GetGenericOutcomeTypeId();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsPairContainerType(const AZ::Uuid& type)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
AZ::Uuid containerTypeId = type;
|
||||
|
||||
if (GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type))
|
||||
{
|
||||
containerTypeId = classInfo->GetGenericTypeId();
|
||||
}
|
||||
|
||||
if (containerTypeId == AZ::GetGenericClassPairTypeId())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::TypeId GetGenericContainerType(const AZ::TypeId& type)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
if (GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type))
|
||||
{
|
||||
// This type is a container
|
||||
return classInfo->GetGenericTypeId();
|
||||
}
|
||||
}
|
||||
|
||||
return azrtti_typeid<void>();
|
||||
}
|
||||
|
||||
bool IsGenericContainerType(const AZ::TypeId& type)
|
||||
{
|
||||
return IsContainerType(type) && GetGenericContainerType(type) == azrtti_typeid<void>();
|
||||
}
|
||||
|
||||
AZStd::pair<AZ::Uuid, AZ::Uuid> GetOutcomeTypes(const AZ::Uuid& type)
|
||||
{
|
||||
AZStd::vector<AZ::Uuid> types;
|
||||
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
if (GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type))
|
||||
{
|
||||
AZ_Assert(classInfo->GetNumTemplatedArguments() == 2, "Outcome template arguments must be 2, even if void, void");
|
||||
return AZStd::make_pair(classInfo->GetTemplatedTypeId(0), classInfo->GetTemplatedTypeId(1));
|
||||
}
|
||||
}
|
||||
|
||||
return AZStd::make_pair(azrtti_typeid<void>(), azrtti_typeid<void>());
|
||||
}
|
||||
|
||||
void* ResolvePointer(void* ptr, const SerializeContext::ClassElement& classElement, const SerializeContext& context)
|
||||
{
|
||||
if (classElement.m_flags & SerializeContext::ClassElement::FLG_POINTER)
|
||||
{
|
||||
// In the case of pointer-to-pointer, we'll deference.
|
||||
ptr = *(void**)(ptr);
|
||||
|
||||
// Pointer-to-pointer fields may be base class / polymorphic, so cast pointer to actual type,
|
||||
// safe for passing as 'this' to member functions.
|
||||
if (ptr && classElement.m_azRtti)
|
||||
{
|
||||
Uuid actualClassId = classElement.m_azRtti->GetActualUuid(ptr);
|
||||
if (actualClassId != classElement.m_typeId)
|
||||
{
|
||||
const SerializeContext::ClassData* classData = context.FindClassData(actualClassId);
|
||||
if (classData)
|
||||
if (!loadedInstance)
|
||||
{
|
||||
ptr = classElement.m_azRtti->Cast(ptr, classData->m_azRtti->GetTypeId());
|
||||
loadedInstance = classPtr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
auto classData = serializeContext->FindClassData(classId);
|
||||
if (classData && classData->m_factory)
|
||||
{
|
||||
classData->m_factory->Destroy(classPtr);
|
||||
}
|
||||
},
|
||||
filterDesc,
|
||||
ObjectStream::InplaceLoadRootInfoCB()
|
||||
);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
} // namespace AZ
|
||||
return loadedInstance;
|
||||
}
|
||||
|
||||
void* LoadObjectFromFile(const AZStd::string& filePath, const Uuid& targetClassId, SerializeContext* context, const FilterDescriptor& filterDesc, int /*platformFlags*/)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
AZ::IO::FileIOStream fileStream;
|
||||
if (!fileStream.Open(filePath.c_str(), IO::OpenMode::ModeRead | IO::OpenMode::ModeBinary))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* loadedObject = LoadObjectFromStream(fileStream, context, &targetClassId, filterDesc);
|
||||
return loadedObject;
|
||||
}
|
||||
|
||||
bool SaveObjectToStream(IO::GenericStream& stream, DataStream::StreamType streamType, const void* classPtr, const Uuid& classId, SerializeContext* context, const SerializeContext::ClassData* classData)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
if (!context)
|
||||
{
|
||||
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
|
||||
|
||||
if(!context)
|
||||
{
|
||||
AZ_Assert(false, "No serialize context");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!classPtr)
|
||||
{
|
||||
AZ_Assert(false, "SaveObjectToStream: classPtr is null, object cannot be serialized.");
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::ObjectStream* objectStream = AZ::ObjectStream::Create(&stream, *context, streamType);
|
||||
if (!objectStream)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!objectStream->WriteClass(classPtr, classId, classData))
|
||||
{
|
||||
objectStream->Finalize();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!objectStream->Finalize())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveStreamToFile(const AZStd::string& filePath, const AZStd::vector<AZ::u8>& streamData, int platformFlags)
|
||||
{
|
||||
AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance();
|
||||
AZ::IO::FixedMaxPathString resolvedPath;
|
||||
if (fileIo == nullptr || !fileIo->ResolvePath(filePath.c_str(), resolvedPath.data(), resolvedPath.capacity() + 1))
|
||||
{
|
||||
resolvedPath = filePath;
|
||||
}
|
||||
if (AZ::IO::SystemFile fileHandle; fileHandle.Open(resolvedPath.c_str(),
|
||||
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY,
|
||||
platformFlags))
|
||||
{
|
||||
AZ::IO::SizeType bytesWritten = fileHandle.Write(streamData.data(), streamData.size());
|
||||
return bytesWritten == streamData.size();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SaveObjectToFile(const AZStd::string& filePath, DataStream::StreamType fileType, const void* classPtr, const Uuid& classId, SerializeContext* context, int platformFlags)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
// \note This is ok for tools, but we should use the streamer to write objects directly (no memory store)
|
||||
AZStd::vector<AZ::u8> dstData;
|
||||
AZ::IO::ByteContainerStream<AZStd::vector<AZ::u8> > dstByteStream(&dstData);
|
||||
|
||||
if (!SaveObjectToStream(dstByteStream, fileType, classPtr, classId, context))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return SaveStreamToFile(filePath, dstData, platformFlags);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Finds any descendant DataElementNodes of the @classElement which match each Crc32 values in the supplied elementCrcQueue
|
||||
\param context SerializeContext used for looking up ClassData
|
||||
\param classElement Top level DataElementNode to begin comparison each the Crc32 queue
|
||||
\param elementCrcQueue Container of Crc32 values in the order in which DataElementNodes should be matched as the DataElementNode tree is traversed
|
||||
\return Vector of valid pointers to DataElementNodes which match the entire element Crc32 queue
|
||||
*/
|
||||
AZStd::vector<AZ::SerializeContext::DataElementNode*> FindDescendantElements(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement,
|
||||
const AZStd::vector<AZ::Crc32>& elementCrcQueue)
|
||||
{
|
||||
AZStd::vector<AZ::SerializeContext::DataElementNode*> dataElementNodes;
|
||||
FindDescendantElements(context, classElement, dataElementNodes, elementCrcQueue.begin(), elementCrcQueue.end());
|
||||
|
||||
return dataElementNodes;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Finds any descendant DataElementNodes of the @classElement which match each Crc32 values in the supplied elementCrcQueue
|
||||
\param context SerializeContext used for looking up ClassData
|
||||
\param classElement The current DataElementNode which will be compared against be to current top Crc32 value in the Crc32 queue
|
||||
\param dataElementNodes[out] Array to populate with a DataElementNode which was found by matching all Crc32 values in the Crc32 queue
|
||||
\param first The current front of the Crc32 queue
|
||||
\param last The end of the Crc32 queue
|
||||
*/
|
||||
void FindDescendantElements(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement,
|
||||
AZStd::vector<AZ::SerializeContext::DataElementNode*>& dataElementNodes, AZStd::vector<AZ::Crc32>::const_iterator first, AZStd::vector<AZ::Crc32>::const_iterator last)
|
||||
{
|
||||
if (first == last)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < classElement.GetNumSubElements(); ++i)
|
||||
{
|
||||
auto& childElement = classElement.GetSubElement(i);
|
||||
if (*first == AZ::Crc32(childElement.GetName()))
|
||||
{
|
||||
if (AZStd::distance(first, last) == 1)
|
||||
{
|
||||
dataElementNodes.push_back(&childElement);
|
||||
}
|
||||
else
|
||||
{
|
||||
FindDescendantElements(context, childElement, dataElementNodes, AZStd::next(first), last);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsVectorContainerType(const AZ::Uuid& type)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
AZ::TypeId containerTypeId = type;
|
||||
|
||||
if (GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type))
|
||||
{
|
||||
// This type is a container
|
||||
containerTypeId = classInfo->GetGenericTypeId();
|
||||
}
|
||||
|
||||
if (containerTypeId == AZ::GetGenericClassInfoVectorTypeId()
|
||||
|| containerTypeId == AZ::GetGenericClassInfoFixedVectorTypeId()
|
||||
|| containerTypeId == AZ::GetGenericClassInfoArrayTypeId()
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsSetContainerType(const AZ::Uuid& type)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
AZ::TypeId containerTypeId = type;
|
||||
|
||||
if (GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type))
|
||||
{
|
||||
// This type is a container
|
||||
containerTypeId = classInfo->GetGenericTypeId();
|
||||
}
|
||||
|
||||
if (containerTypeId == AZ::GetGenericClassSetTypeId()
|
||||
|| containerTypeId == AZ::GetGenericClassUnorderedSetTypeId()
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool IsMapContainerType(const AZ::Uuid& type)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
AZ::Uuid containerTypeId = type;
|
||||
|
||||
if (GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type))
|
||||
{
|
||||
containerTypeId = classInfo->GetGenericTypeId();
|
||||
}
|
||||
|
||||
if (containerTypeId == AZ::GetGenericClassMapTypeId()
|
||||
|| containerTypeId == AZ::GetGenericClassUnorderedMapTypeId()
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsContainerType(const AZ::Uuid& type)
|
||||
{
|
||||
return IsVectorContainerType(type) || IsSetContainerType(type) || IsMapContainerType(type);
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::Uuid> GetContainedTypes(const AZ::Uuid& type)
|
||||
{
|
||||
AZStd::vector<AZ::Uuid> types;
|
||||
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
if (GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type))
|
||||
{
|
||||
for (int i = 0; i < classInfo->GetNumTemplatedArguments(); ++i)
|
||||
{
|
||||
types.push_back(classInfo->GetTemplatedTypeId(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
bool IsOutcomeType(const AZ::Uuid& type)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type);
|
||||
return classInfo && classInfo->GetGenericTypeId() == AZ::GetGenericOutcomeTypeId();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsPairContainerType(const AZ::Uuid& type)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
AZ::Uuid containerTypeId = type;
|
||||
|
||||
if (GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type))
|
||||
{
|
||||
containerTypeId = classInfo->GetGenericTypeId();
|
||||
}
|
||||
|
||||
if (containerTypeId == AZ::GetGenericClassPairTypeId())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::TypeId GetGenericContainerType(const AZ::TypeId& type)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
if (GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type))
|
||||
{
|
||||
// This type is a container
|
||||
return classInfo->GetGenericTypeId();
|
||||
}
|
||||
}
|
||||
|
||||
return azrtti_typeid<void>();
|
||||
}
|
||||
|
||||
bool IsGenericContainerType(const AZ::TypeId& type)
|
||||
{
|
||||
return IsContainerType(type) && GetGenericContainerType(type) == azrtti_typeid<void>();
|
||||
}
|
||||
|
||||
AZStd::pair<AZ::Uuid, AZ::Uuid> GetOutcomeTypes(const AZ::Uuid& type)
|
||||
{
|
||||
AZStd::vector<AZ::Uuid> types;
|
||||
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
if (GenericClassInfo* classInfo = serializeContext->FindGenericClassInfo(type))
|
||||
{
|
||||
AZ_Assert(classInfo->GetNumTemplatedArguments() == 2, "Outcome template arguments must be 2, even if void, void");
|
||||
return AZStd::make_pair(classInfo->GetTemplatedTypeId(0), classInfo->GetTemplatedTypeId(1));
|
||||
}
|
||||
}
|
||||
|
||||
return AZStd::make_pair(azrtti_typeid<void>(), azrtti_typeid<void>());
|
||||
}
|
||||
|
||||
void* ResolvePointer(void* ptr, const SerializeContext::ClassElement& classElement, const SerializeContext& context)
|
||||
{
|
||||
if (classElement.m_flags & SerializeContext::ClassElement::FLG_POINTER)
|
||||
{
|
||||
// In the case of pointer-to-pointer, we'll deference.
|
||||
ptr = *(void**)(ptr);
|
||||
|
||||
// Pointer-to-pointer fields may be base class / polymorphic, so cast pointer to actual type,
|
||||
// safe for passing as 'this' to member functions.
|
||||
if (ptr && classElement.m_azRtti)
|
||||
{
|
||||
Uuid actualClassId = classElement.m_azRtti->GetActualUuid(ptr);
|
||||
if (actualClassId != classElement.m_typeId)
|
||||
{
|
||||
const SerializeContext::ClassData* classData = context.FindClassData(actualClassId);
|
||||
if (classData)
|
||||
{
|
||||
ptr = classElement.m_azRtti->Cast(ptr, classData->m_azRtti->GetTypeId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
} // namespace AZ::Utils
|
||||
|
||||
@@ -2065,11 +2065,15 @@ namespace AZ
|
||||
}
|
||||
|
||||
EnumerateInstanceCallContext callContext(
|
||||
AZStd::bind(&SerializeContext::BeginCloneElement, this, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3, &cloneData, &m_errorLogger, &scratchBuffer),
|
||||
AZStd::bind(&SerializeContext::EndCloneElement, this, &cloneData),
|
||||
this,
|
||||
SerializeContext::ENUM_ACCESS_FOR_READ,
|
||||
&m_errorLogger);
|
||||
[&](void* ptr, const ClassData* classData, const ClassElement* elementData) -> bool
|
||||
{
|
||||
return BeginCloneElement(ptr, classData, elementData, &cloneData, &m_errorLogger, &scratchBuffer);
|
||||
},
|
||||
[&]() -> bool
|
||||
{
|
||||
return EndCloneElement(&cloneData);
|
||||
},
|
||||
this, SerializeContext::ENUM_ACCESS_FOR_READ, &m_errorLogger);
|
||||
|
||||
EnumerateInstance(
|
||||
&callContext
|
||||
@@ -2098,19 +2102,17 @@ namespace AZ
|
||||
if (ptr)
|
||||
{
|
||||
EnumerateInstanceCallContext callContext(
|
||||
AZStd::bind(&SerializeContext::BeginCloneElementInplace, this, dest, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3, &cloneData, &m_errorLogger, &scratchBuffer),
|
||||
AZStd::bind(&SerializeContext::EndCloneElement, this, &cloneData),
|
||||
this,
|
||||
SerializeContext::ENUM_ACCESS_FOR_READ,
|
||||
&m_errorLogger);
|
||||
[&](void* ptr, const ClassData* classData, const ClassElement* elementData) -> bool
|
||||
{
|
||||
return BeginCloneElementInplace(dest, ptr, classData, elementData, &cloneData, &m_errorLogger, &scratchBuffer);
|
||||
},
|
||||
[&]() -> bool
|
||||
{
|
||||
return EndCloneElement(&cloneData);
|
||||
},
|
||||
this, SerializeContext::ENUM_ACCESS_FOR_READ, &m_errorLogger);
|
||||
|
||||
EnumerateInstance(
|
||||
&callContext
|
||||
, const_cast<void*>(ptr)
|
||||
, classId
|
||||
, nullptr
|
||||
, nullptr
|
||||
);
|
||||
EnumerateInstance(&callContext, const_cast<void*>(ptr), classId, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2941,14 +2943,10 @@ namespace AZ
|
||||
{
|
||||
m_errorHandler = errorHandler ? errorHandler : &m_defaultErrorHandler;
|
||||
|
||||
m_elementCallback = AZStd::bind(static_cast<bool (SerializeContext::*)(EnumerateInstanceCallContext*, void*, const Uuid&, const ClassData*, const ClassElement*) const>(&SerializeContext::EnumerateInstance)
|
||||
, m_context
|
||||
, this
|
||||
, AZStd::placeholders::_1
|
||||
, AZStd::placeholders::_2
|
||||
, AZStd::placeholders::_3
|
||||
, AZStd::placeholders::_4
|
||||
);
|
||||
m_elementCallback = [this](void* ptr, const Uuid& classId, const ClassData* classData, const ClassElement* classElement)->bool
|
||||
{
|
||||
return m_context->EnumerateInstance(this, ptr, classId, classData, classElement);
|
||||
};
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace AZ
|
||||
|
||||
using AttributePtr = AZStd::shared_ptr<Attribute>;
|
||||
using AttributeSharedPair = AZStd::pair<AttributeId, AttributePtr>;
|
||||
template <typename ContainerType, typename T>
|
||||
template <typename T, typename ContainerType = AttributeContainerType<T>>
|
||||
AttributePtr CreateModuleAttribute(T&& attrValue);
|
||||
|
||||
/**
|
||||
@@ -540,6 +540,7 @@ namespace AZ
|
||||
*/
|
||||
struct ClassElement
|
||||
{
|
||||
AZ_TYPE_INFO(ClassElement, "{7D386902-A1D9-4525-8284-F68435FE1D05}");
|
||||
enum Flags
|
||||
{
|
||||
FLG_POINTER = (1 << 0), ///< Element is stored as pointer (it's not a value).
|
||||
@@ -563,22 +564,22 @@ namespace AZ
|
||||
void ClearAttributes();
|
||||
Attribute* FindAttribute(AttributeId attributeId) const;
|
||||
|
||||
const char* m_name; ///< Used in XML output and debugging purposes
|
||||
u32 m_nameCrc; ///< CRC32 of m_name
|
||||
Uuid m_typeId;
|
||||
size_t m_dataSize;
|
||||
size_t m_offset;
|
||||
const char* m_name{ "" }; ///< Used in XML output and debugging purposes
|
||||
u32 m_nameCrc{}; ///< CRC32 of m_name
|
||||
Uuid m_typeId = AZ::TypeId::CreateNull();
|
||||
size_t m_dataSize{};
|
||||
size_t m_offset{};
|
||||
|
||||
IRttiHelper* m_azRtti; ///< Interface used to support RTTI.
|
||||
IRttiHelper* m_azRtti{}; ///< Interface used to support RTTI.
|
||||
GenericClassInfo* m_genericClassInfo = nullptr; ///< Valid when the generic class is set. So you don't search for the actual type in the class register.
|
||||
Edit::ElementData* m_editData; ///< Pointer to edit data (generated by EditContext).
|
||||
Edit::ElementData* m_editData{}; ///< Pointer to edit data (generated by EditContext).
|
||||
AZStd::vector<AttributeSharedPair, AZStdFunctorAllocator> m_attributes{
|
||||
AZStdFunctorAllocator([]() -> IAllocatorAllocate& { return AZ::AllocatorInstance<AZ::SystemAllocator>::Get(); })
|
||||
}; ///< Attributes attached to ClassElement. Lambda is required here as AZStdFunctorAllocator expects a function pointer
|
||||
///< that returns an IAllocatorAllocate& and the AZ::AllocatorInstance<AZ::SystemAllocator>::Get returns an AZ::SystemAllocator&
|
||||
/// which while it inherits from IAllocatorAllocate, does not work as function pointers do not support covariant return types
|
||||
AttributeOwnership m_attributeOwnership = AttributeOwnership::Parent;
|
||||
int m_flags; ///<
|
||||
int m_flags{}; ///<
|
||||
};
|
||||
typedef AZStd::vector<ClassElement> ClassElementArray;
|
||||
|
||||
@@ -589,6 +590,8 @@ namespace AZ
|
||||
class ClassData
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(ClassData, "{20EB8E2E-D807-4039-84E2-CE37D7647CD4}");
|
||||
|
||||
ClassData();
|
||||
~ClassData() { ClearAttributes(); }
|
||||
ClassData(ClassData&&) = default;
|
||||
@@ -1040,6 +1043,7 @@ namespace AZ
|
||||
*/
|
||||
struct EnumerateInstanceCallContext
|
||||
{
|
||||
AZ_TYPE_INFO(EnumerateInstanceCallContext, "{FCC1DB4B-72BD-4D78-9C23-C84B91589D33}");
|
||||
EnumerateInstanceCallContext(const BeginElemEnumCB& beginElemCB, const EndElemEnumCB& endElemCB, const SerializeContext* context, unsigned int accessflags, ErrorHandler* errorHandler);
|
||||
|
||||
BeginElemEnumCB m_beginElemCB; ///< Optional callback when entering an element's hierarchy.
|
||||
@@ -2539,7 +2543,7 @@ namespace AZ
|
||||
/// associated with current module
|
||||
/// @param attrValue value to store within the attribute
|
||||
/// @param ContainerType second parameter which is used for function parameter deduction
|
||||
template <typename ContainerType, typename T>
|
||||
template <typename T, typename ContainerType>
|
||||
AttributePtr CreateModuleAttribute(T&& attrValue)
|
||||
{
|
||||
IAllocatorAllocate& moduleAllocator = GetCurrentSerializeContextModule().GetAllocator();
|
||||
|
||||
@@ -433,9 +433,7 @@ namespace AZ
|
||||
m_classData.m_attributes.set_allocator(AZStd::move(dllAllocator));
|
||||
|
||||
// Create the ObjectStreamWriteOverrideCB in the current module
|
||||
using ContainerType = AttributeData<AZStd::function<void(SerializeContext::EnumerateInstanceCallContext&,
|
||||
const void*, const SerializeContext::ClassData&, const SerializeContext::ClassElement*)>>;
|
||||
m_classData.m_attributes.emplace_back(AZ_CRC("ObjectStreamWriteElementOverride", 0x35eb659f), CreateModuleAttribute<ContainerType>(&ObjectStreamWriter));
|
||||
m_classData.m_attributes.emplace_back(AZ_CRC("ObjectStreamWriteElementOverride", 0x35eb659f), CreateModuleAttribute(&ObjectStreamWriter));
|
||||
}
|
||||
|
||||
SerializeContext::ClassData* GetClassData() override
|
||||
|
||||
@@ -327,7 +327,7 @@ namespace AZ
|
||||
|
||||
while (!localNotifierQueue.empty())
|
||||
{
|
||||
for (SignalNotifierArgs notifierArgs : localNotifierQueue)
|
||||
for (const SignalNotifierArgs& notifierArgs : localNotifierQueue)
|
||||
{
|
||||
localNotifierEvent.Signal(notifierArgs.m_jsonPath, notifierArgs.m_type);
|
||||
}
|
||||
|
||||
@@ -266,7 +266,8 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
|
||||
// Step 3 locate the project root and attempt to find the engine root using the registered engine
|
||||
// for the project in the project.json file
|
||||
AZ::IO::FixedMaxPath projectRoot = FindProjectRoot(settingsRegistry);
|
||||
AZ::IO::FixedMaxPath projectRoot;
|
||||
settingsRegistry.Get(projectRoot.Native(), FilePathKey_ProjectPath);
|
||||
if (projectRoot.empty())
|
||||
{
|
||||
return {};
|
||||
@@ -668,7 +669,7 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
// NOTE: We make the project-path in the BootstrapSettingsRootKey absolute first
|
||||
|
||||
AZ::IO::FixedMaxPath projectPath = FindProjectRoot(registry);
|
||||
if (constexpr auto projectPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_path";
|
||||
if ([[maybe_unused]] constexpr auto projectPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_path";
|
||||
!projectPath.empty())
|
||||
{
|
||||
if (projectPath.IsRelative())
|
||||
@@ -693,6 +694,7 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
R"(Project path isn't set in the Settings Registry at "%.*s".)"
|
||||
" Project-related filepaths will be set relative to the executable directory\n",
|
||||
AZ_STRING_ARG(projectPathKey));
|
||||
projectPath = exePath;
|
||||
registry.Set(FilePathKey_ProjectPath, exePath.Native());
|
||||
}
|
||||
|
||||
|
||||
@@ -840,7 +840,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
SliceComponent::SliceInstance* SliceComponent::SliceReference::CreateInstanceFromExistingEntities(AZStd::vector<AZ::Entity*>& entities,
|
||||
const EntityIdToEntityIdMap assetToLiveIdMap,
|
||||
const EntityIdToEntityIdMap& assetToLiveIdMap,
|
||||
SliceInstanceId sliceInstanceId)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
@@ -443,7 +443,7 @@ namespace AZ
|
||||
* @return A pointer to the newly created slice instance. Returns nullptr on error or if the SliceComponent is not instantiated.
|
||||
*/
|
||||
SliceInstance* CreateInstanceFromExistingEntities(AZStd::vector<AZ::Entity*>& entities,
|
||||
const EntityIdToEntityIdMap assetToLiveIdMap,
|
||||
const EntityIdToEntityIdMap& assetToLiveIdMap,
|
||||
SliceInstanceId sliceInstanceId = SliceInstanceId::CreateRandom());
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,77 +8,74 @@
|
||||
|
||||
#include <AzCore/Socket/AzSocket.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::AzSock
|
||||
{
|
||||
namespace AzSock
|
||||
AzSocketAddress::AzSocketAddress()
|
||||
{
|
||||
AzSocketAddress::AzSocketAddress()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
AzSocketAddress& AzSocketAddress::operator=(const AZSOCKADDR& addr)
|
||||
{
|
||||
m_sockAddr = *reinterpret_cast<const AZSOCKADDR_IN*>(&addr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool AzSocketAddress::operator==(const AzSocketAddress& rhs) const
|
||||
{
|
||||
return m_sockAddr.sin_family == rhs.m_sockAddr.sin_family
|
||||
&& m_sockAddr.sin_addr.s_addr == rhs.m_sockAddr.sin_addr.s_addr
|
||||
&& m_sockAddr.sin_port == rhs.m_sockAddr.sin_port;
|
||||
}
|
||||
|
||||
const AZSOCKADDR* AzSocketAddress::GetTargetAddress() const
|
||||
{
|
||||
return reinterpret_cast<const AZSOCKADDR*>(&m_sockAddr);
|
||||
}
|
||||
|
||||
AZStd::string AzSocketAddress::GetIP() const
|
||||
{
|
||||
char ip[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, const_cast<in_addr*>(&m_sockAddr.sin_addr), ip, AZ_ARRAY_SIZE(ip));
|
||||
return AZStd::string(ip);
|
||||
}
|
||||
|
||||
AZStd::string AzSocketAddress::GetAddress() const
|
||||
{
|
||||
char ip[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, const_cast<in_addr*>(&m_sockAddr.sin_addr), ip, AZ_ARRAY_SIZE(ip));
|
||||
return AZStd::string::format("%s:%d", ip, AZ::AzSock::NetToHostShort(m_sockAddr.sin_port));
|
||||
}
|
||||
|
||||
AZ::u16 AzSocketAddress::GetAddrPort() const
|
||||
{
|
||||
return AZ::AzSock::NetToHostShort(m_sockAddr.sin_port);
|
||||
}
|
||||
|
||||
void AzSocketAddress::SetAddrPort(AZ::u16 port)
|
||||
{
|
||||
m_sockAddr.sin_port = AZ::AzSock::HostToNetShort(port);
|
||||
}
|
||||
|
||||
bool AzSocketAddress::SetAddress(const AZStd::string& ip, AZ::u16 port)
|
||||
{
|
||||
AZ_Assert(!ip.empty(), "Invalid address string!");
|
||||
Reset();
|
||||
return AZ::AzSock::ResolveAddress(ip, port, m_sockAddr);
|
||||
}
|
||||
|
||||
bool AzSocketAddress::SetAddress(AZ::u32 ip, AZ::u16 port)
|
||||
{
|
||||
Reset();
|
||||
m_sockAddr.sin_addr.s_addr = AZ::AzSock::HostToNetLong(ip);
|
||||
m_sockAddr.sin_port = AZ::AzSock::HostToNetShort(port);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AzSocketAddress::Reset()
|
||||
{
|
||||
memset(&m_sockAddr, 0, sizeof(m_sockAddr));
|
||||
m_sockAddr.sin_family = AF_INET;
|
||||
m_sockAddr.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
AzSocketAddress& AzSocketAddress::operator=(const AZSOCKADDR& addr)
|
||||
{
|
||||
m_sockAddr = *reinterpret_cast<const AZSOCKADDR_IN*>(&addr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool AzSocketAddress::operator==(const AzSocketAddress& rhs) const
|
||||
{
|
||||
return m_sockAddr.sin_family == rhs.m_sockAddr.sin_family
|
||||
&& m_sockAddr.sin_addr.s_addr == rhs.m_sockAddr.sin_addr.s_addr
|
||||
&& m_sockAddr.sin_port == rhs.m_sockAddr.sin_port;
|
||||
}
|
||||
|
||||
const AZSOCKADDR* AzSocketAddress::GetTargetAddress() const
|
||||
{
|
||||
return reinterpret_cast<const AZSOCKADDR*>(&m_sockAddr);
|
||||
}
|
||||
|
||||
AZStd::string AzSocketAddress::GetIP() const
|
||||
{
|
||||
char ip[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, const_cast<in_addr*>(&m_sockAddr.sin_addr), ip, AZ_ARRAY_SIZE(ip));
|
||||
return AZStd::string(ip);
|
||||
}
|
||||
|
||||
AZStd::string AzSocketAddress::GetAddress() const
|
||||
{
|
||||
char ip[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, const_cast<in_addr*>(&m_sockAddr.sin_addr), ip, AZ_ARRAY_SIZE(ip));
|
||||
return AZStd::string::format("%s:%d", ip, AZ::AzSock::NetToHostShort(m_sockAddr.sin_port));
|
||||
}
|
||||
|
||||
AZ::u16 AzSocketAddress::GetAddrPort() const
|
||||
{
|
||||
return AZ::AzSock::NetToHostShort(m_sockAddr.sin_port);
|
||||
}
|
||||
|
||||
void AzSocketAddress::SetAddrPort(AZ::u16 port)
|
||||
{
|
||||
m_sockAddr.sin_port = AZ::AzSock::HostToNetShort(port);
|
||||
}
|
||||
|
||||
bool AzSocketAddress::SetAddress(const AZStd::string& ip, AZ::u16 port)
|
||||
{
|
||||
AZ_Assert(!ip.empty(), "Invalid address string!");
|
||||
Reset();
|
||||
return AZ::AzSock::ResolveAddress(ip, port, m_sockAddr);
|
||||
}
|
||||
|
||||
bool AzSocketAddress::SetAddress(AZ::u32 ip, AZ::u16 port)
|
||||
{
|
||||
Reset();
|
||||
m_sockAddr.sin_addr.s_addr = AZ::AzSock::HostToNetLong(ip);
|
||||
m_sockAddr.sin_port = AZ::AzSock::HostToNetShort(port);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AzSocketAddress::Reset()
|
||||
{
|
||||
memset(&m_sockAddr, 0, sizeof(m_sockAddr));
|
||||
m_sockAddr.sin_family = AF_INET;
|
||||
m_sockAddr.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
} // namespace AZ::AzSock
|
||||
|
||||
@@ -10,66 +10,63 @@
|
||||
|
||||
#include "RunningStatistic.h"
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Statistics
|
||||
{
|
||||
namespace Statistics
|
||||
void RunningStatistic::Reset()
|
||||
{
|
||||
void RunningStatistic::Reset()
|
||||
m_numSamples = 0;
|
||||
m_mostRecentSample = 0.0;
|
||||
m_minimum = 0.0;
|
||||
m_maximum = 0.0;
|
||||
m_sum = 0.0;
|
||||
m_average = 0.0;
|
||||
m_varianceTracking = 0.0;
|
||||
}
|
||||
|
||||
void RunningStatistic::PushSample(double value)
|
||||
{
|
||||
m_numSamples++;
|
||||
m_mostRecentSample = value;
|
||||
m_sum += value;
|
||||
|
||||
if (m_numSamples == 1)
|
||||
{
|
||||
m_numSamples = 0;
|
||||
m_mostRecentSample = 0.0;
|
||||
m_minimum = 0.0;
|
||||
m_maximum = 0.0;
|
||||
m_sum = 0.0;
|
||||
m_average = 0.0;
|
||||
m_varianceTracking = 0.0;
|
||||
m_minimum = value;
|
||||
m_maximum = value;
|
||||
m_average = value;
|
||||
return;
|
||||
}
|
||||
|
||||
void RunningStatistic::PushSample(double value)
|
||||
if (value < m_minimum)
|
||||
{
|
||||
m_numSamples++;
|
||||
m_mostRecentSample = value;
|
||||
m_sum += value;
|
||||
|
||||
if (m_numSamples == 1)
|
||||
{
|
||||
m_minimum = value;
|
||||
m_maximum = value;
|
||||
m_average = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (value < m_minimum)
|
||||
{
|
||||
m_minimum = value;
|
||||
}
|
||||
else if (value > m_maximum)
|
||||
{
|
||||
m_maximum = value;
|
||||
}
|
||||
|
||||
//See header notes and references to understand this way of calculating
|
||||
//running average & variance.
|
||||
const double newAverage = m_average + (value - m_average) / m_numSamples;
|
||||
m_varianceTracking = m_varianceTracking + (value - m_average)*(value - newAverage);
|
||||
|
||||
m_average = newAverage;
|
||||
m_minimum = value;
|
||||
}
|
||||
else if (value > m_maximum)
|
||||
{
|
||||
m_maximum = value;
|
||||
}
|
||||
|
||||
double RunningStatistic::GetVariance(VarianceType varianceType) const
|
||||
{
|
||||
if (m_numSamples > 1)
|
||||
{
|
||||
const AZ::u64 varianceDivisor = (varianceType == VarianceType::S) ? m_numSamples - 1 : m_numSamples;
|
||||
return m_varianceTracking / varianceDivisor;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
//See header notes and references to understand this way of calculating
|
||||
//running average & variance.
|
||||
const double newAverage = m_average + (value - m_average) / m_numSamples;
|
||||
m_varianceTracking = m_varianceTracking + (value - m_average)*(value - newAverage);
|
||||
|
||||
double RunningStatistic::GetStdev(VarianceType varianceType) const
|
||||
m_average = newAverage;
|
||||
}
|
||||
|
||||
double RunningStatistic::GetVariance(VarianceType varianceType) const
|
||||
{
|
||||
if (m_numSamples > 1)
|
||||
{
|
||||
return sqrt(GetVariance(varianceType));
|
||||
const AZ::u64 varianceDivisor = (varianceType == VarianceType::S) ? m_numSamples - 1 : m_numSamples;
|
||||
return m_varianceTracking / varianceDivisor;
|
||||
}
|
||||
|
||||
}//namespace Statistics
|
||||
}//namespace AZ
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double RunningStatistic::GetStdev(VarianceType varianceType) const
|
||||
{
|
||||
return sqrt(GetVariance(varianceType));
|
||||
}
|
||||
|
||||
} // namespace AZ::Statistics
|
||||
|
||||
+39
-42
@@ -12,55 +12,52 @@
|
||||
#include "StatisticalProfilerProxySystemComponent.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
namespace AZ
|
||||
namespace AZ::Statistics
|
||||
{
|
||||
namespace Statistics
|
||||
StatisticalProfilerProxy* StatisticalProfilerProxy::TimedScope::m_profilerProxy = nullptr;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
StatisticalProfilerProxy* StatisticalProfilerProxy::TimedScope::m_profilerProxy = nullptr;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<StatisticalProfilerProxySystemComponent, AZ::Component>()
|
||||
->Version(1);
|
||||
}
|
||||
serializeContext->Class<StatisticalProfilerProxySystemComponent, AZ::Component>()
|
||||
->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC("StatisticalProfilerService", 0x20066f73));
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC("StatisticalProfilerService", 0x20066f73));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC("StatisticalProfilerService", 0x20066f73));
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC("StatisticalProfilerService", 0x20066f73));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
StatisticalProfilerProxySystemComponent::StatisticalProfilerProxySystemComponent()
|
||||
: m_StatisticalProfilerProxy(nullptr)
|
||||
{
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
StatisticalProfilerProxySystemComponent::StatisticalProfilerProxySystemComponent()
|
||||
: m_StatisticalProfilerProxy(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
StatisticalProfilerProxySystemComponent::~StatisticalProfilerProxySystemComponent()
|
||||
{
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
StatisticalProfilerProxySystemComponent::~StatisticalProfilerProxySystemComponent()
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::Activate()
|
||||
{
|
||||
m_StatisticalProfilerProxy = new StatisticalProfilerProxy;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::Activate()
|
||||
{
|
||||
m_StatisticalProfilerProxy = new StatisticalProfilerProxy;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::Deactivate()
|
||||
{
|
||||
delete m_StatisticalProfilerProxy;
|
||||
}
|
||||
} //namespace Statistics
|
||||
} // namespace AZ
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::Deactivate()
|
||||
{
|
||||
delete m_StatisticalProfilerProxy;
|
||||
}
|
||||
} // namespace AZ::Statistics
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -485,10 +485,11 @@ namespace AZ
|
||||
//! CalculateBranchToken
|
||||
/*! Calculate the branch token that is used for asset processor connection negotiations
|
||||
*
|
||||
* \param appRootPath - The absolute path of the app root to base the token calculation on
|
||||
* \param engineRootPath - The absolute path to the engine root to base the token calculation on
|
||||
* \param token - The result of the branch token calculation
|
||||
*/
|
||||
void CalculateBranchToken(const AZStd::string& appRootPath, AZStd::string& token);
|
||||
void CalculateBranchToken(AZStd::string_view engineRootPath, AZStd::string& token);
|
||||
void CalculateBranchToken(AZStd::string_view engineRootPath, AZ::IO::FixedMaxPathString& token);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -30,8 +30,13 @@ namespace AZ
|
||||
|
||||
if (Interface<TaskGraphActiveInterface>::Get() == nullptr)
|
||||
{
|
||||
#if (AZ_TRAIT_THREAD_NUM_TASK_GRAPH_WORKER_THREADS)
|
||||
const uint32_t numberOfWorkerThreads = AZ_TRAIT_THREAD_NUM_TASK_GRAPH_WORKER_THREADS;
|
||||
#else
|
||||
const uint32_t numberOfWorkerThreads = Threading::CalcNumWorkerThreads(cl_taskGraphThreadsConcurrencyRatio, cl_taskGraphThreadsMinNumber, cl_taskGraphThreadsNumReserved);
|
||||
#endif // (AZ_TRAIT_THREAD_NUM_TASK_GRAPH_WORKER_THREADS)
|
||||
Interface<TaskGraphActiveInterface>::Register(this); // small window that another thread can try to use taskgraph between this line and the set instance.
|
||||
m_taskExecutor = aznew TaskExecutor(Threading::CalcNumWorkerThreads(cl_taskGraphThreadsConcurrencyRatio, cl_taskGraphThreadsMinNumber, cl_taskGraphThreadsNumReserved));
|
||||
m_taskExecutor = aznew TaskExecutor(numberOfWorkerThreads);
|
||||
TaskExecutor::SetInstance(m_taskExecutor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/RTTI/TypeSafeIntegral.h>
|
||||
#include <AzCore/std/time.h>
|
||||
#include <AzCore/std/chrono/chrono.h>
|
||||
#include <AzCore/std/time.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -24,15 +24,21 @@ namespace AZ
|
||||
//! Using int64_t as the underlying type, this is good to represent approximately 292,471 years
|
||||
AZ_TYPE_SAFE_INTEGRAL(TimeUs, int64_t);
|
||||
|
||||
namespace Time
|
||||
{
|
||||
static const AZ::TimeMs ZeroTimeMs = AZ::TimeMs{ 0 };
|
||||
static const AZ::TimeUs ZeroTimeUs = AZ::TimeUs{ 0 };
|
||||
}
|
||||
|
||||
//! @class ITime
|
||||
//! @brief This is an AZ::Interface<> for managing time related operations.
|
||||
//! AZ::ITime and associated types may not operate in realtime. These abstractions are to allow our application
|
||||
//! simulation to operate both slower and faster than realtime in a well defined and user controllable manner
|
||||
//! The rate at which time passes for AZ::ITime is controlled by the cvar t_scale
|
||||
//! t_scale == 0 means simulation time should halt
|
||||
//! 0 < t_scale < 1 will cause time to pass slower than realtime, with t_scale 0.1 being roughly 1/10th realtime
|
||||
//! t_scale == 1 will cause time to pass at roughly realtime
|
||||
//! t_scale > 1 will cause time to pass faster than normal, with t_scale 10 being roughly 10x realtime
|
||||
//! The rate at which time passes for AZ::ITime is controlled by the cvar t_simulationTickScale
|
||||
//! t_simulationTickScale == 0 means simulation time should halt
|
||||
//! 0 < t_simulationTickScale < 1 will cause time to pass slower than realtime, with t_simulationTickScale 0.1 being roughly 1/10th realtime
|
||||
//! t_simulationTickScale == 1 will cause time to pass at roughly realtime
|
||||
//! t_simulationTickScale > 1 will cause time to pass faster than normal, with t_simulationTickScale 10 being roughly 10x realtime
|
||||
class ITime
|
||||
{
|
||||
public:
|
||||
@@ -41,15 +47,72 @@ namespace AZ
|
||||
ITime() = default;
|
||||
virtual ~ITime() = default;
|
||||
|
||||
//! Returns the number of milliseconds since application start.
|
||||
//! @return the number of milliseconds that have elapsed since application start
|
||||
//! Returns the number of milliseconds since application start scaled by t_simulationTickScale.
|
||||
//! @return The number of milliseconds that have elapsed since application start.
|
||||
virtual TimeMs GetElapsedTimeMs() const = 0;
|
||||
|
||||
//! Returns the number of microseconds since application start.
|
||||
//! Returns the number of microseconds since application start scaled by t_simulationTickScale.
|
||||
//! @return the number of microseconds that have elapsed since application start
|
||||
virtual TimeUs GetElapsedTimeUs() const = 0;
|
||||
|
||||
//! Returns the number of milliseconds since application start.
|
||||
//! This value is not affected by the t_simulationTickScale cvar.
|
||||
//! @return The number of milliseconds that have elapsed since application start.
|
||||
virtual TimeMs GetRealElapsedTimeMs() const = 0;
|
||||
|
||||
//! Returns the number of microseconds since application start.
|
||||
//! This value is not affected by the t_simulationTickScale cvar.
|
||||
//! @return The number of microseconds that have elapsed since application start.
|
||||
virtual TimeUs GetRealElapsedTimeUs() const = 0;
|
||||
|
||||
//! Returns the current simulation tick delta time.
|
||||
//! This is affected by the cvars t_simulationTickScale, t_simulationTickDeltaOverride, and t_maxGameTickDelta.
|
||||
//! @return The number of microseconds elapsed since the last game tick.
|
||||
virtual TimeUs GetSimulationTickDeltaTimeUs() const = 0;
|
||||
|
||||
//! Returns the non-manipulated tick time.
|
||||
//! @return The number of microseconds elapsed since the last game tick.
|
||||
virtual TimeUs GetRealTickDeltaTimeUs() const = 0;
|
||||
|
||||
//! Returns the time since application start of when the last simulation tick was updated.
|
||||
virtual TimeUs GetLastSimulationTickTime() const = 0;
|
||||
|
||||
//! If > 0 this will override the simulation tick delta time with the provided value.
|
||||
//! When enabled this will ignore any set simulation tick scale.
|
||||
//! Setting to 0 disables the override.
|
||||
//! @param timeMs The time in milliseconds to use for the tick delta.
|
||||
virtual void SetSimulationTickDeltaOverride(TimeMs timeMs) = 0;
|
||||
|
||||
//! Returns the current simulation tick override.
|
||||
//! 0 means disabled.
|
||||
//! @returns The current simulation tick override in milliseconds.
|
||||
virtual TimeMs GetSimulationTickDeltaOverride() const = 0;
|
||||
|
||||
//! A scalar amount to adjust the passage of time by, 1.0 == realtime, 0.5 == half realtime, 2.0 == doubletime.
|
||||
//! @param scale The scalar value to apply to the simulation time.
|
||||
virtual void SetSimulationTickScale(float scale) = 0;
|
||||
|
||||
//! Returns the current simulation tick scale.
|
||||
//! 1.0 == realtime, 0.5 == half realtime, 2.0 == doubletime.
|
||||
//! @returns The simulation tick scale value.
|
||||
virtual float GetSimulationTickScale() const = 0;
|
||||
|
||||
//! The minimum rate to force the simulation tick to run.
|
||||
//! 0 for as fast as possible. 30 = ~33ms, 60 = ~16ms.
|
||||
//! Setting to 0 will disable rate limiting.
|
||||
//! @note It is not guaranteed to hit the requested tick rate exactly.
|
||||
//! @param rate The rate in frames per second.
|
||||
virtual void SetSimulationTickRate(int rate) = 0;
|
||||
|
||||
//! Return the current simulation tick rate.
|
||||
//! 0 means disabled.
|
||||
//! @return The rate in frames per second.
|
||||
virtual int32_t GetSimulationTickRate() const = 0;
|
||||
|
||||
AZ_DISABLE_COPY_MOVE(ITime);
|
||||
|
||||
static const AZ::TimeMs ZeroTimeMs = AZ::TimeMs{ 0 };
|
||||
static const AZ::TimeUs ZeroTimeUs = AZ::TimeUs{ 0 };
|
||||
};
|
||||
|
||||
// EBus wrapper for ScriptCanvas
|
||||
@@ -74,6 +137,36 @@ namespace AZ
|
||||
return AZ::Interface<ITime>::Get()->GetElapsedTimeUs();
|
||||
}
|
||||
|
||||
//! This is a simple convenience wrapper
|
||||
inline TimeMs GetRealElapsedTimeMs()
|
||||
{
|
||||
return AZ::Interface<ITime>::Get()->GetRealElapsedTimeMs();
|
||||
}
|
||||
|
||||
//! This is a simple convenience wrapper
|
||||
inline TimeUs GetRealElapsedTimeUs()
|
||||
{
|
||||
return AZ::Interface<ITime>::Get()->GetRealElapsedTimeUs();
|
||||
}
|
||||
|
||||
//! This is a simple convenience wrapper
|
||||
inline TimeUs GetSimulationTickDeltaTimeUs()
|
||||
{
|
||||
return AZ::Interface<ITime>::Get()->GetSimulationTickDeltaTimeUs();
|
||||
}
|
||||
|
||||
//! This is a simple convenience wrapper
|
||||
inline TimeUs GetRealTickDeltaTimeUs()
|
||||
{
|
||||
return AZ::Interface<ITime>::Get()->GetRealTickDeltaTimeUs();
|
||||
}
|
||||
|
||||
//! This is a simple convenience wrapper
|
||||
inline TimeUs GetLastSimulationTickTime()
|
||||
{
|
||||
return AZ::Interface<ITime>::Get()->GetLastSimulationTickTime();
|
||||
}
|
||||
|
||||
//! Converts from milliseconds to microseconds
|
||||
inline TimeUs TimeMsToUs(TimeMs value)
|
||||
{
|
||||
@@ -92,12 +185,24 @@ namespace AZ
|
||||
return static_cast<float>(value) / 1000.0f;
|
||||
}
|
||||
|
||||
//! Converts from milliseconds to seconds
|
||||
inline double TimeMsToSecondsDouble(TimeMs value)
|
||||
{
|
||||
return static_cast<double>(value) / 1000.0;
|
||||
}
|
||||
|
||||
//! Converts from microseconds to seconds
|
||||
inline float TimeUsToSeconds(TimeUs value)
|
||||
{
|
||||
return static_cast<float>(value) / 1000000.0f;
|
||||
}
|
||||
|
||||
//! Converts from microseconds to seconds
|
||||
inline double TimeUsToSecondsDouble(TimeUs value)
|
||||
{
|
||||
return static_cast<double>(value) / 1000000.0;
|
||||
}
|
||||
|
||||
//! Converts from milliseconds to AZStd::chrono::time_point
|
||||
inline auto TimeMsToChrono(TimeMs value)
|
||||
{
|
||||
@@ -113,6 +218,20 @@ namespace AZ
|
||||
auto chronoValue = AZStd::chrono::microseconds(aznumeric_cast<int64_t>(value));
|
||||
return epoch + chronoValue;
|
||||
}
|
||||
|
||||
//! A utility function to convert from seconds to TimeMs
|
||||
inline TimeMs SecondsToTimeMs(const double value)
|
||||
{
|
||||
const double valueMs = value * 1000.0;
|
||||
return static_cast<TimeMs>(static_cast<int64_t>(valueMs));
|
||||
}
|
||||
|
||||
//! A utility function to convert from seconds to TimeUs
|
||||
inline TimeUs SecondsToTimeUs(const double value)
|
||||
{
|
||||
const double valueMs = value * 1000000.0;
|
||||
return static_cast<TimeUs>(static_cast<int64_t>(valueMs));
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(AZ::TimeMs);
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Time/TimeSystem.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/RTTI/ReflectContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void cvar_t_simulationTickScale_Changed(const float& value)
|
||||
{
|
||||
if (auto* timeSystem = AZ::Interface<ITime>::Get())
|
||||
{
|
||||
timeSystem->SetSimulationTickScale(value);
|
||||
}
|
||||
}
|
||||
|
||||
void cvar_t_simulationTickDeltaOverride_Changed(const float& value)
|
||||
{
|
||||
if (auto* timeSystem = AZ::Interface<ITime>::Get())
|
||||
{
|
||||
timeSystem->SetSimulationTickDeltaOverride(AZ::SecondsToTimeMs(value));
|
||||
}
|
||||
}
|
||||
|
||||
void cvar_t_simulationTickRate_Changed(const int& rate)
|
||||
{
|
||||
AZ_Warning("tick", false, "Simulation tick rate limiting is currently disabled. Setting will not be applied.");
|
||||
if (auto* timeSystem = AZ::Interface<ITime>::Get())
|
||||
{
|
||||
timeSystem->SetSimulationTickRate(rate);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
AZ_CVAR(float, t_simulationTickScale, 1.0f, cvar_t_simulationTickScale_Changed, AZ::ConsoleFunctorFlags::Null,
|
||||
"A scalar amount to adjust time passage by, 1.0 == realtime, 0.5 == half realtime, 2.0 == doubletime");
|
||||
|
||||
AZ_CVAR(float, t_simulationTickDeltaOverride, 0.0f, cvar_t_simulationTickDeltaOverride_Changed, AZ::ConsoleFunctorFlags::Null,
|
||||
"If > 0, overrides the simulation tick delta time with the provided value (Seconds) and ignores any t_simulationTickScale value.");
|
||||
|
||||
AZ_CVAR(int, t_simulationTickRate, 0, cvar_t_simulationTickRate_Changed, AZ::ConsoleFunctorFlags::Null,
|
||||
"The minimum rate to force the game simulation tick to run. 0 for as fast as possible. 30 = ~33ms, 60 = ~16ms");
|
||||
|
||||
void TimeSystem::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<TimeSystem, ITime>()
|
||||
->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
TimeSystem::TimeSystem()
|
||||
{
|
||||
m_lastInvokedTimeUs = static_cast<TimeUs>(AZStd::GetTimeNowMicroSecond());
|
||||
AZ::Interface<ITime>::Register(this);
|
||||
ITimeRequestBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
TimeSystem::~TimeSystem()
|
||||
{
|
||||
AZ::Interface<ITime>::Unregister(this);
|
||||
ITimeRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
TimeMs TimeSystem::GetElapsedTimeMs() const
|
||||
{
|
||||
return AZ::TimeUsToMs(GetElapsedTimeUs());
|
||||
}
|
||||
|
||||
TimeUs TimeSystem::GetElapsedTimeUs() const
|
||||
{
|
||||
TimeUs currentTime = static_cast<TimeUs>(AZStd::GetTimeNowMicroSecond());
|
||||
TimeUs deltaTime = currentTime - m_lastInvokedTimeUs;
|
||||
|
||||
if (t_simulationTickScale != 1.0f)
|
||||
{
|
||||
const float floatDelta = AZStd::GetMax(static_cast<float>(deltaTime) * t_simulationTickScale, 1.0f);
|
||||
deltaTime = static_cast<TimeUs>(static_cast<int64_t>(floatDelta));
|
||||
}
|
||||
|
||||
m_accumulatedTimeUs += deltaTime;
|
||||
m_lastInvokedTimeUs = currentTime;
|
||||
|
||||
return m_accumulatedTimeUs;
|
||||
}
|
||||
|
||||
TimeMs TimeSystem::GetRealElapsedTimeMs() const
|
||||
{
|
||||
return AZ::TimeUsToMs(GetRealElapsedTimeUs());
|
||||
}
|
||||
|
||||
TimeUs TimeSystem::GetRealElapsedTimeUs() const
|
||||
{
|
||||
return static_cast<TimeUs>(AZStd::GetTimeNowMicroSecond());
|
||||
}
|
||||
|
||||
TimeUs TimeSystem::GetSimulationTickDeltaTimeUs() const
|
||||
{
|
||||
return m_simulationTickDeltaTimeUs;
|
||||
}
|
||||
|
||||
TimeUs TimeSystem::GetRealTickDeltaTimeUs() const
|
||||
{
|
||||
return m_realTickDeltaTimeUs;
|
||||
}
|
||||
|
||||
TimeUs TimeSystem::GetLastSimulationTickTime() const
|
||||
{
|
||||
return m_lastSimulationTickTimeUs;
|
||||
}
|
||||
|
||||
TimeUs TimeSystem::AdvanceTickDeltaTimes()
|
||||
{
|
||||
const TimeUs currentTimeUs = static_cast<TimeUs>(AZStd::GetTimeNowMicroSecond());
|
||||
|
||||
//real time
|
||||
m_realTickDeltaTimeUs = currentTimeUs - m_lastSimulationTickTimeUs;
|
||||
|
||||
//game time
|
||||
if (m_simulationTickDeltaOverride > AZ::Time::ZeroTimeUs)
|
||||
{
|
||||
m_simulationTickDeltaTimeUs = m_simulationTickDeltaOverride;
|
||||
m_lastSimulationTickTimeUs = currentTimeUs;
|
||||
return m_simulationTickDeltaTimeUs;
|
||||
}
|
||||
|
||||
m_simulationTickDeltaTimeUs = currentTimeUs - m_lastSimulationTickTimeUs;
|
||||
|
||||
if (!AZ::IsClose(t_simulationTickScale, 1.0f))
|
||||
{
|
||||
const double floatDelta = AZStd::GetMax(static_cast<double>(m_simulationTickDeltaTimeUs) * static_cast<double>(t_simulationTickScale), 1.0);
|
||||
m_simulationTickDeltaTimeUs = static_cast<TimeUs>(static_cast<int64_t>(floatDelta));
|
||||
}
|
||||
m_lastSimulationTickTimeUs = currentTimeUs;
|
||||
|
||||
return m_simulationTickDeltaTimeUs;
|
||||
}
|
||||
|
||||
void TimeSystem::ApplyTickRateLimiterIfNeeded()
|
||||
{
|
||||
// Currently disabling the Tick rate limiter as there are some reported issues when using it.
|
||||
#ifdef ENABLE_TICK_RATE_LIMITER
|
||||
// If tick rate limiting is on, ensure (1 / t_simulationTickRate) ms has elapsed since the last frame,
|
||||
// sleeping if there's still time remaining.
|
||||
if (t_simulationTickRate > 0)
|
||||
{
|
||||
const TimeUs currentTimeUs = AZ::GetRealElapsedTimeUs();
|
||||
const TimeUs timeUntilNextTick = (m_lastSimulationTickTimeUs + m_simulationTickLimitTimeUs) - currentTimeUs;
|
||||
if (timeUntilNextTick > AZ::Time::ZeroTimeUs)
|
||||
{
|
||||
AZ_TracePrintf("tick", "Sleeping for %.2f", AZ::TimeUsToSecondsDouble(timeUntilNextTick));
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::microseconds(static_cast<int64_t>(timeUntilNextTick)));
|
||||
}
|
||||
}
|
||||
#endif // #ifdef ENABLE_TICK_RATE_LIMITER
|
||||
}
|
||||
|
||||
void TimeSystem::SetSimulationTickDeltaOverride(TimeMs timeMs)
|
||||
{
|
||||
const TimeUs timeUs = AZ::TimeMsToUs(timeMs);
|
||||
if (timeUs != m_simulationTickDeltaOverride)
|
||||
{
|
||||
m_simulationTickDeltaOverride = timeUs;
|
||||
t_simulationTickDeltaOverride = AZ::TimeUsToSeconds(timeUs); //update the cvar
|
||||
}
|
||||
}
|
||||
|
||||
TimeMs TimeSystem::GetSimulationTickDeltaOverride() const
|
||||
{
|
||||
return AZ::TimeUsToMs(m_simulationTickDeltaOverride);
|
||||
}
|
||||
|
||||
void TimeSystem::SetSimulationTickScale(float scale)
|
||||
{
|
||||
if (!AZ::IsClose(scale, t_simulationTickScale))
|
||||
{
|
||||
t_simulationTickScale = scale;
|
||||
}
|
||||
}
|
||||
|
||||
float TimeSystem::GetSimulationTickScale() const
|
||||
{
|
||||
return t_simulationTickScale;
|
||||
}
|
||||
|
||||
void TimeSystem::SetSimulationTickRate(int rate)
|
||||
{
|
||||
m_simulationTickLimitRate = AZStd::abs(rate);
|
||||
if (m_simulationTickLimitRate != 0)
|
||||
{
|
||||
m_simulationTickLimitTimeUs = AZ::SecondsToTimeUs(1.0f / m_simulationTickLimitRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_simulationTickLimitTimeUs = AZ::Time::ZeroTimeUs;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t TimeSystem::GetSimulationTickRate() const
|
||||
{
|
||||
return m_simulationTickLimitRate;
|
||||
}
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/Time/ITime.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class ReflectContext;
|
||||
|
||||
//! Implementation of the ITime system interface.
|
||||
class TimeSystem
|
||||
: public ITimeRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(AZ::TimeSystem, "{CE1C5E4F-7DC1-4248-B10C-AC55E8924A48}", AZ::ITime);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
TimeSystem();
|
||||
virtual ~TimeSystem();
|
||||
|
||||
//! ITime overrides.
|
||||
//! @{
|
||||
TimeMs GetElapsedTimeMs() const override;
|
||||
TimeUs GetElapsedTimeUs() const override;
|
||||
TimeMs GetRealElapsedTimeMs() const override;
|
||||
TimeUs GetRealElapsedTimeUs() const override;
|
||||
TimeUs GetSimulationTickDeltaTimeUs() const override;
|
||||
TimeUs GetRealTickDeltaTimeUs() const override;
|
||||
TimeUs GetLastSimulationTickTime() const override;
|
||||
void SetSimulationTickDeltaOverride(TimeMs timeMs) override;
|
||||
TimeMs GetSimulationTickDeltaOverride() const override;
|
||||
void SetSimulationTickScale(float scale) override;
|
||||
float GetSimulationTickScale() const override;
|
||||
void SetSimulationTickRate(int rate) override;
|
||||
int32_t GetSimulationTickRate() const override;
|
||||
//! @}
|
||||
|
||||
//! Advances the Simulation and Real tick delta time counters.
|
||||
//! This is called from the owner of the TimeSystem, ComponentApplication in Tick().
|
||||
//! @return The delta in microseconds from the last call to AdvanceTickDeltaTimes(). Value will be the same as GetSimulationTickDeltaTimeUs().
|
||||
TimeUs AdvanceTickDeltaTimes();
|
||||
|
||||
//! If t_simulationTickRate is >0 this will try to have the game delta time run at a maximum of the rate set.
|
||||
//! This is called from the owner of the TimeSystem, ComponentApplication in Tick().
|
||||
//! example. If t_simulationTickRate is set to 60Fps, and the game tick delta is <17ms(60fps), this will add a sleep for the remaining time.
|
||||
//! example. If t_simulationTickRate is set to 60Fps, and the game tick delta is >=17ms(60fps), this will not sleep at all.
|
||||
//! @note It is not guaranteed to hit the requested tick rate exactly.
|
||||
void ApplyTickRateLimiterIfNeeded();
|
||||
private:
|
||||
//! Used to calculate the delta time between calls to GetElapsedTimeMs/TimeUs().
|
||||
//! Mutable to allow GetElapsedTimeMs/TimeUs() to be a const functions.
|
||||
mutable TimeUs m_lastInvokedTimeUs = AZ::Time::ZeroTimeUs;
|
||||
|
||||
//! Accumulates the delta time of GetElapsedTimeMs/TimeUs() calls.
|
||||
//! Mutable to allow GetElapsedTimeMs/TimeUs() to be a const functions.
|
||||
mutable TimeUs m_accumulatedTimeUs = AZ::Time::ZeroTimeUs;
|
||||
|
||||
//! The current game tick delta time.
|
||||
//! Can be affected by time system cvars.
|
||||
//! Updated in AdvanceTickDeltaTimes().
|
||||
TimeUs m_simulationTickDeltaTimeUs = AZ::Time::ZeroTimeUs;
|
||||
|
||||
//! The current real tick delta time.
|
||||
//! Will not be affected by time system cvars.
|
||||
//! Updated in AdvanceTickDeltaTimes().
|
||||
TimeUs m_realTickDeltaTimeUs = AZ::Time::ZeroTimeUs;
|
||||
|
||||
TimeUs m_lastSimulationTickTimeUs = AZ::Time::ZeroTimeUs; //!< Used to determine the game tick delta time.
|
||||
|
||||
TimeUs m_simulationTickDeltaOverride = AZ::Time::ZeroTimeUs; //<! Stores the TimeUs value of the t_simulationTickDeltaOverride cvar.
|
||||
TimeUs m_simulationTickLimitTimeUs = AZ::Time::ZeroTimeUs; //<! Stores the TimeUs value of the t_simulationTickRate cvar.
|
||||
int32_t m_simulationTickLimitRate = 0; //<! Stores the simulation rate limit in frames per second.
|
||||
};
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Time/TimeSystemComponent.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
AZ_CVAR(float, t_scale, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "A scalar amount to adjust time passage by, 1.0 == realtime, 0.5 == half realtime, 2.0 == doubletime");
|
||||
|
||||
void TimeSystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<TimeSystemComponent, AZ::Component>()
|
||||
->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void TimeSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("TimeService"));
|
||||
}
|
||||
|
||||
void TimeSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC_CE("TimeService"));
|
||||
}
|
||||
|
||||
TimeSystemComponent::TimeSystemComponent()
|
||||
{
|
||||
m_lastInvokedTimeUs = static_cast<TimeUs>(AZStd::GetTimeNowMicroSecond());
|
||||
AZ::Interface<ITime>::Register(this);
|
||||
ITimeRequestBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
TimeSystemComponent::~TimeSystemComponent()
|
||||
{
|
||||
ITimeRequestBus::Handler::BusDisconnect();
|
||||
AZ::Interface<ITime>::Unregister(this);
|
||||
}
|
||||
|
||||
void TimeSystemComponent::Activate()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void TimeSystemComponent::Deactivate()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
TimeMs TimeSystemComponent::GetElapsedTimeMs() const
|
||||
{
|
||||
return TimeUsToMs(GetElapsedTimeUs());
|
||||
}
|
||||
|
||||
TimeUs TimeSystemComponent::GetElapsedTimeUs() const
|
||||
{
|
||||
TimeUs currentTime = static_cast<TimeUs>(AZStd::GetTimeNowMicroSecond());
|
||||
TimeUs deltaTime = currentTime - m_lastInvokedTimeUs;
|
||||
|
||||
if (t_scale != 1.0f)
|
||||
{
|
||||
float floatDelta = static_cast<float>(deltaTime) * t_scale;
|
||||
deltaTime = static_cast<TimeUs>(static_cast<int64_t>(floatDelta));
|
||||
}
|
||||
|
||||
m_accumulatedTimeUs += deltaTime;
|
||||
m_lastInvokedTimeUs = currentTime;
|
||||
|
||||
return m_accumulatedTimeUs;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Time/ITime.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
//! Implementation of the ITime system interface.
|
||||
class TimeSystemComponent
|
||||
: public AZ::Component
|
||||
, public ITimeRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
|
||||
AZ_COMPONENT(TimeSystemComponent, "{CE1C5E4F-7DC1-4248-B10C-AC55E8924A48}");
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
|
||||
|
||||
TimeSystemComponent();
|
||||
virtual ~TimeSystemComponent();
|
||||
|
||||
//! AZ::Component overrides.
|
||||
//! @{
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
//! @}
|
||||
|
||||
//! ITime overrides.
|
||||
//! @{
|
||||
TimeMs GetElapsedTimeMs() const override;
|
||||
TimeUs GetElapsedTimeUs() const override;
|
||||
//! @}
|
||||
|
||||
private:
|
||||
|
||||
mutable TimeUs m_lastInvokedTimeUs = TimeUs{0};
|
||||
mutable TimeUs m_accumulatedTimeUs = TimeUs{0};
|
||||
};
|
||||
}
|
||||
@@ -41,7 +41,6 @@ namespace UnitTest
|
||||
MOCK_METHOD0(GetSerializeContext, AZ::SerializeContext* ());
|
||||
MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ());
|
||||
MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ());
|
||||
MOCK_CONST_METHOD0(GetAppRoot, const char* ());
|
||||
MOCK_CONST_METHOD0(GetEngineRoot, const char* ());
|
||||
MOCK_CONST_METHOD0(GetExecutableFolder, const char* ());
|
||||
MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&));
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Time/TimeSystem.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class MockTimeSystem;
|
||||
using NiceTimeSystemMock =::testing::NiceMock<MockTimeSystem>;
|
||||
|
||||
//used if you wish to mock any of the Get time functions.
|
||||
class MockTimeSystem
|
||||
: public ITimeRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
MockTimeSystem()
|
||||
{
|
||||
AZ::Interface<ITime>::Register(this);
|
||||
ITimeRequestBus::Handler::BusConnect();
|
||||
}
|
||||
virtual ~MockTimeSystem()
|
||||
{
|
||||
AZ::Interface<ITime>::Unregister(this);
|
||||
ITimeRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
MOCK_CONST_METHOD0(GetElapsedTimeMs, TimeMs());
|
||||
MOCK_CONST_METHOD0(GetElapsedTimeUs, TimeUs());
|
||||
MOCK_CONST_METHOD0(GetRealElapsedTimeMs, TimeMs());
|
||||
MOCK_CONST_METHOD0(GetRealElapsedTimeUs, TimeUs());
|
||||
MOCK_CONST_METHOD0(GetSimulationTickDeltaTimeUs, TimeUs());
|
||||
MOCK_CONST_METHOD0(GetRealTickDeltaTimeUs, TimeUs());
|
||||
MOCK_CONST_METHOD0(GetLastSimulationTickTime, TimeUs());
|
||||
MOCK_METHOD1(SetSimulationTickDeltaOverride, void(TimeMs));
|
||||
MOCK_CONST_METHOD0(GetSimulationTickDeltaOverride, TimeMs());
|
||||
MOCK_METHOD1(SetSimulationTickScale, void(float));
|
||||
MOCK_CONST_METHOD0(GetSimulationTickScale, float());
|
||||
MOCK_METHOD1(SetSimulationTickRate, void(int));
|
||||
MOCK_CONST_METHOD0(GetSimulationTickRate, int32_t());
|
||||
};
|
||||
|
||||
//used if you wish to override any of the Get time functions with specific functionality.
|
||||
class StubTimeSystem
|
||||
: public AZ::TimeSystem
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(AZ::StubTimeSystem, "{DD5D5A6A-345F-49FD-A61E-A40E63C49CFA}", AZ::TimeSystem);
|
||||
|
||||
virtual AZ::TimeMs GetElapsedTimeMs() const override
|
||||
{
|
||||
return AZ::Time::ZeroTimeMs;
|
||||
}
|
||||
|
||||
virtual AZ::TimeUs GetElapsedTimeUs() const override
|
||||
{
|
||||
return AZ::Time::ZeroTimeUs;
|
||||
}
|
||||
|
||||
virtual AZ::TimeMs GetRealElapsedTimeMs() const override
|
||||
{
|
||||
return AZ::Time::ZeroTimeMs;
|
||||
}
|
||||
|
||||
virtual AZ::TimeUs GetRealElapsedTimeUs() const override
|
||||
{
|
||||
return AZ::Time::ZeroTimeUs;
|
||||
}
|
||||
|
||||
virtual AZ::TimeUs GetSimulationTickDeltaTimeUs() const override
|
||||
{
|
||||
return AZ::Time::ZeroTimeUs;
|
||||
}
|
||||
|
||||
virtual AZ::TimeUs GetRealTickDeltaTimeUs() const override
|
||||
{
|
||||
return AZ::Time::ZeroTimeUs;
|
||||
}
|
||||
|
||||
virtual AZ::TimeUs GetLastSimulationTickTime() const override
|
||||
{
|
||||
return AZ::Time::ZeroTimeUs;
|
||||
}
|
||||
|
||||
virtual void SetSimulationTickDeltaOverride([[maybe_unused]]TimeMs timeMs) override
|
||||
{
|
||||
}
|
||||
|
||||
virtual TimeMs GetSimulationTickDeltaOverride() const override
|
||||
{
|
||||
return AZ::Time::ZeroTimeMs;
|
||||
}
|
||||
|
||||
virtual void SetSimulationTickScale([[maybe_unused]] float scale) override
|
||||
{
|
||||
}
|
||||
|
||||
virtual float GetSimulationTickScale() const override
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
virtual void SetSimulationTickRate([[maybe_unused]] int rate) override
|
||||
{
|
||||
}
|
||||
|
||||
virtual int32_t GetSimulationTickRate() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace AZ
|
||||
@@ -60,6 +60,7 @@ namespace UnitTest
|
||||
m_isAssertTest = true;
|
||||
m_numAssertsFailed = 0;
|
||||
}
|
||||
|
||||
int StopAssertTests()
|
||||
{
|
||||
m_isAssertTest = false;
|
||||
@@ -69,6 +70,11 @@ namespace UnitTest
|
||||
}
|
||||
|
||||
bool m_isAssertTest;
|
||||
bool m_suppressErrors = true;
|
||||
bool m_suppressWarnings = true;
|
||||
bool m_suppressAsserts = true;
|
||||
bool m_suppressOutput = true;
|
||||
bool m_suppressPrintf = true;
|
||||
int m_numAssertsFailed;
|
||||
};
|
||||
|
||||
@@ -114,7 +120,7 @@ namespace UnitTest
|
||||
|
||||
// utility classes that you can derive from or contain, which suppress AZ_Asserts
|
||||
// and AZ_Errors to the below macros (processAssert, etc)
|
||||
// If TraceBusHook or TraceBusRedirector have been started in your unit tests,
|
||||
// If TraceBusHook or TraceBusRedirector have been started in your unit tests,
|
||||
// use AZ_TEST_START_TRACE_SUPPRESSION and AZ_TEST_STOP_TRACE_SUPPRESSION(numExpectedAsserts) macros to perform AZ_Assert and AZ_Error suppression
|
||||
class TraceBusRedirector
|
||||
: public AZ::Debug::TraceMessageBus::Handler
|
||||
@@ -124,16 +130,19 @@ namespace UnitTest
|
||||
if (UnitTest::TestRunner::Instance().m_isAssertTest)
|
||||
{
|
||||
UnitTest::TestRunner::Instance().ProcessAssert(message, file, line, false);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
else if (UnitTest::TestRunner::Instance().m_suppressAsserts)
|
||||
{
|
||||
GTEST_MESSAGE_AT_(file, line, message, ::testing::TestPartResult::kNonFatalFailure);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
bool OnAssert(const char* /*message*/) override
|
||||
{
|
||||
return true; // stop processing
|
||||
return UnitTest::TestRunner::Instance().m_suppressAsserts; // stop processing
|
||||
}
|
||||
bool OnPreError(const char* /*window*/, const char* file, int line, const char* /*func*/, const char* message) override
|
||||
{
|
||||
@@ -142,6 +151,7 @@ namespace UnitTest
|
||||
UnitTest::TestRunner::Instance().ProcessAssert(message, file, line, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool OnError(const char* /*window*/, const char* message) override
|
||||
@@ -149,12 +159,15 @@ namespace UnitTest
|
||||
if (UnitTest::TestRunner::Instance().m_isAssertTest)
|
||||
{
|
||||
UnitTest::TestRunner::Instance().ProcessAssert(message, __FILE__, __LINE__, UnitTest::AssertionExpr(false));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
else if (UnitTest::TestRunner::Instance().m_suppressErrors)
|
||||
{
|
||||
GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure);
|
||||
return true;
|
||||
}
|
||||
return true; // stop processing
|
||||
|
||||
return false;
|
||||
}
|
||||
bool OnPreWarning(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) override
|
||||
{
|
||||
@@ -163,21 +176,21 @@ namespace UnitTest
|
||||
}
|
||||
bool OnWarning(const char* /*window*/, const char* /*message*/) override
|
||||
{
|
||||
return true;
|
||||
return UnitTest::TestRunner::Instance().m_suppressWarnings;
|
||||
}
|
||||
|
||||
bool OnOutput(const char* /*window*/, const char* /*message*/) override
|
||||
{
|
||||
return true;
|
||||
return UnitTest::TestRunner::Instance().m_suppressOutput;
|
||||
}
|
||||
|
||||
bool OnPrintf(const char* window, const char* message) override
|
||||
{
|
||||
if (AZStd::string_view(window) == "Memory") // We want to print out the memory leak's stack traces
|
||||
{
|
||||
ColoredPrintf(COLOR_RED, "[ MEMORY ] %s", message);
|
||||
ColoredPrintf(COLOR_RED, "[ MEMORY ] %s", message);
|
||||
}
|
||||
return true;
|
||||
return UnitTest::TestRunner::Instance().m_suppressPrintf;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -259,7 +272,6 @@ namespace UnitTest
|
||||
bool m_environmentSetup = false;
|
||||
bool m_createdAllocator = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user