Files
o3de/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp
T
lumberyard-employee-dm b3b646dad9 Moved the Asset Catalog loading from LmbrCentral to the AzFramework::Application (#4568)
* Moved the loading of the AssetCatalog from LmbrCentralSystemComponent to AzFramework Application

Modified the AssetCatalog::InitializeCatalog function to no longer rely on the TickBus to send out the `AssetCatalogEventBus::OnCatalogLoaded` event.
It now queues a function on the AssetCatalogRequestBus to send the OnCatalogLoaded event as soon as the dispatching for the AssetCatalogRequestBus has completed on the current thread.
This is done by updating the AssetCatalogRequestBus to use EBus ThreadDispatchPolicy to add a callback to invoke any queued function has soon a thread has finished dispatching and has released its DispatchMutex

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated the AssetCatalogRequestBus to add a custom DispatchLockGuard

The AssetCatalogRequestBus uses the custom lock guard to dispatch queued
events after it has unlocked it's context mutex for the current thread.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Removed GetContext call from the
AssetCatalogRequests::PostThreadDispatchInvoker

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated the definition of FileTagQueryManager::GetDefaultFileTagFilePath
function to return a path

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated the AZ_CONSOLEFREEFUNC macro to actually use the _NAME

The _NAME parameter was not being used before, resulting in the Console
stringified name of the function being used.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Removed CrySystem dependencies from the BundlingSystemComponent

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Moved the loading of the AssetCatalog from LmbrCentralSystemComponent to AzFramework Application

Modified the AssetCatalog::InitializeCatalog function to no longer rely on the TickBus to send out the `AssetCatalogEventBus::OnCatalogLoaded` event.
It now queues a function on the AssetCatalogRequestBus to send the OnCatalogLoaded event as soon as the dispatching for the AssetCatalogRequestBus has completed on the current thread.
This is done by updating the AssetCatalogRequestBus to use EBus ThreadDispatchPolicy to add a callback to invoke any queued function has soon a thread has finished dispatching and has released its DispatchMutex

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated the AssetCatalogRequestBus to add a custom DispatchLockGuard

The AssetCatalogRequestBus uses the custom lock guard to dispatch queued
events after it has unlocked it's context mutex for the current thread.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Removed GetContext call from the
AssetCatalogRequests::PostThreadDispatchInvoker

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated the definition of FileTagQueryManager::GetDefaultFileTagFilePath
function to return a path

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated the AZ_CONSOLEFREEFUNC macro to actually use the _NAME

The _NAME parameter was not being used before, resulting in the Console
stringified name of the function being used.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Removed CrySystem dependencies from the BundlingSystemComponent

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Addded missing template parameter to AssetCatalogRequests

The fixes the compile error.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Adding AssetBus::MultiHandler::BusDisconnect call

The BlastSystemComponent was connecting to the Bus, but not
disconnecting from it, causing an assert to fire to it being a
multi-thread bus

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Added support for DataDrive lifecycle events to the ComponentApplication

The events are using the SettingsRegistry NotifyEvent to track when
certain keys are modified to signal handlers.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Corrected invalid JSON creation in ModuleManager::DeactivateEntities

Resolved clang warning about used type alias

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Fix for dangling reference in lambda registered to the SettingsRegistry
Notifier event

This was causing the EditorPythonBinding tests to crash due to the
following circumstances.

First Python has created an instance of a SettingsRegistryProxy
Second the SettingsRegistry sends an event during the time when the
SettingsRegistryProxy exists.

This issue was exposed due to the ComponentApplication Lifecycle events
using the SettingsRegistry to dispatch during various times of the
application workflow.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
2021-10-26 10:31:31 -05:00

247 lines
10 KiB
C++

/*
* 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 <AzFramework/Asset/AssetRegistry.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/IO/SystemFile.h> // for max path
namespace AssetRegistryInternal
{
// to prevent people from shooting themselves in the foot here we are going to normalize the path.
// since sha1 is sensitive to both slash direction and case!
// note that this is not generating asset IDs, its merely creating UUIDs that map from
// asset path -> asset Id, so that storing asset path as strings is not necessary.
AZ::Uuid CreateUUIDForName(const char* name)
{
if (!name)
{
return AZ::Uuid::CreateNull();
}
// avoid allocating memory. Note that the input paths are expected to be relative paths
// from the root and thus should be much shorter than AZ_MAX_PATH_LEN
char tempBuffer[AZ_MAX_PATH_LEN] = { 0 };
tempBuffer[AZ_ARRAY_SIZE(tempBuffer) - 1] = 0;
// here we try to pass over the memory only once.
for (AZStd::size_t pos = 0; pos < AZ_ARRAY_SIZE(tempBuffer) - 1; ++pos)
{
char currentValue = name[pos];
if (!currentValue)
{
tempBuffer[pos] = 0;
break;
}
else if (currentValue == '\\')
{
tempBuffer[pos] = '/';
}
else
{
tempBuffer[pos] = (char)tolower(currentValue);
}
}
return AZ::Uuid::CreateName(tempBuffer);
}
}
namespace AzFramework
{
using namespace AssetRegistryInternal;
//=========================================================================
// AssetRegistry::Clear
//=========================================================================
void AssetRegistry::Clear()
{
m_assetDependencies = {};
m_assetIdToInfo = AssetIdToInfoMap();
m_assetPathToId = AssetPathToIdMap();
}
//=========================================================================
// AssetRegistry::ReflectSerialize
//=========================================================================
void AssetRegistry::ReflectSerialize(AZ::SerializeContext* serializeContext)
{
if (serializeContext)
{
serializeContext->Class<AZ::Data::AssetInfo>()
->Version(2)
->Field("assetId", &AZ::Data::AssetInfo::m_assetId)
->Field("relativePath", &AZ::Data::AssetInfo::m_relativePath)
->Field("sizeBytes", &AZ::Data::AssetInfo::m_sizeBytes)
->Field("assetType", &AZ::Data::AssetInfo::m_assetType);
serializeContext->Class<AZ::Data::ProductDependency>()
->Version(1)
->Field("assetId", &AZ::Data::ProductDependency::m_assetId)
->Field("flags", &AZ::Data::ProductDependency::m_flags);
serializeContext->Class<AssetRegistry>()
->Version(5)
->Field("m_assetIdToInfo", &AssetRegistry::m_assetIdToInfo)
->Field("m_assetPathToIdMap", &AssetRegistry::m_assetPathToId)
->Field("m_legacyAssetIdToRealAssetId", &AssetRegistry::m_legacyAssetIdToRealAssetId)
->Field("m_assetDependencies", &AssetRegistry::m_assetDependencies);
// note that the above m_assetPathToIdMap used to be called m_assetPathToId in prior serialization
// and m_assetPathToIdByUUID prior to that, so do not rename it to those more obvious fields in the future.
}
}
//=========================================================================
// AssetRegistry::RegisterAsset
//=========================================================================
void AssetRegistry::RegisterAsset(AZ::Data::AssetId id, const AZ::Data::AssetInfo& assetInfo)
{
// One day we'd like to remove the reverse lookup of name -> id since nothing should be recording asset names for purposes of logic or lookup.
// But for now, we still support legacy systems which store asset relative pathnames instead of storing asset ID's.
// to achieve this we have two maps
// one map which maps from [asset relative path] -> [Asset ID]
// one map which maps from [Asset ID] -> [AssetInfo struct]
// whats important to note is that the [asset relative path] map, for performance and memory purposes, uses the
// SHA1 hash of the [asset relative path] instead of storing strings.
// this makes it take a fixed amount of space and a fixed amount of time to do a lookup
// whats also important to note is that sometimes, there are aliases for assets due to legacy systems.
// in other words, two different AssetID's can map to the same file info about an asset, because they were known via a different ID-assignment system in the past.
// Its not worth worrying about the case here where two source files (blah.png, blah.tif) output the same asset (blah.dds)
// because either way, there is an asset there, so it has to be added to both maps. there will just be two entries
// in the [Asset ID] -> [AssetInfo struct] that points at the same output file. Notifying the user that this has occurred
// should happen at a much higher level.
SetAssetIdByPath(assetInfo.m_relativePath.c_str(), id);
m_assetIdToInfo.insert_key(id).first->second = assetInfo;
}
//=========================================================================
// AssetRegistry::UnregisterAsset
//=========================================================================
void AssetRegistry::UnregisterAsset(AZ::Data::AssetId id)
{
// just as with RegisterAsset, its not worth worrying about the balance of these two maps.
// When you delete an asset (ie, its actual data is gone) it MUST be removed from the [Id] -> [AssetInfo] map
// and its irrelevant whether it gets removed from the [Path] -> [Id] map because that hash is only used
// to resolve Ids, and if that resolved Id points at a missing asset, the [Path] -> [Id] map will know that.
auto existingAsset = m_assetIdToInfo.find(id);
if (existingAsset != m_assetIdToInfo.end())
{
m_assetPathToId.erase(CreateUUIDForName(existingAsset->second.m_relativePath.c_str()));
}
m_assetIdToInfo.erase(id);
m_assetDependencies.erase(id);
}
void AssetRegistry::RegisterLegacyAssetMapping(const AZ::Data::AssetId& legacyId, const AZ::Data::AssetId& newId)
{
m_legacyAssetIdToRealAssetId[legacyId] = newId;
}
void AssetRegistry::UnregisterLegacyAssetMapping(const AZ::Data::AssetId& legacyId)
{
m_legacyAssetIdToRealAssetId.erase(legacyId);
}
void AssetRegistry::SetAssetDependencies(const AZ::Data::AssetId& id, const AZStd::vector<AZ::Data::ProductDependency>& dependencies)
{
m_assetDependencies[id] = dependencies;
}
void AssetRegistry::RegisterAssetDependency(const AZ::Data::AssetId& id, const AZ::Data::ProductDependency& dependency)
{
m_assetDependencies[id].push_back(dependency);
}
AZStd::vector<AZ::Data::ProductDependency> AssetRegistry::GetAssetDependencies(const AZ::Data::AssetId& id)
{
return m_assetDependencies[id];
}
AZ::Data::AssetId AssetRegistry::GetAssetIdByLegacyAssetId(const AZ::Data::AssetId& legacyAssetId) const
{
auto found = m_legacyAssetIdToRealAssetId.find(legacyAssetId);
if (found != m_legacyAssetIdToRealAssetId.end())
{
return found->second;
}
return AZ::Data::AssetId();
}
AzFramework::AssetRegistry::LegacyAssetIdToRealAssetIdMap AssetRegistry::GetLegacyMappingSubsetFromRealIds(const AZStd::vector<AZ::Data::AssetId>& realIds) const
{
LegacyAssetIdToRealAssetIdMap subset;
auto realIdsBeginItr = realIds.begin();
auto realIdsEndItr = realIds.end();
for (const auto& legacyToRealPair : m_legacyAssetIdToRealAssetId)
{
if (AZStd::find(realIdsBeginItr, realIdsEndItr, legacyToRealPair.second) != realIdsEndItr)
{
subset.insert(legacyToRealPair);
}
}
return subset;
}
AZ::Data::AssetId AssetRegistry::GetAssetIdByPath(const char* assetPath) const
{
if ((!assetPath) || (assetPath[0] == 0))
{
// the empty path has no asset ID.
return AZ::Data::AssetId();
}
auto entry = m_assetPathToId.find(CreateUUIDForName(assetPath));
if (entry != m_assetPathToId.end())
{
return entry->second;
}
return AZ::Data::AssetId();
}
void AssetRegistry::SetAssetIdByPath(const char* assetPath, const AZ::Data::AssetId& id)
{
AZ_Assert(assetPath, "Invalid asset path provided to SetAssetID!\n");
AZ_Assert(id.IsValid(), "Invalid asset id provided to SetAssetID!\n");
if ((!assetPath) || (!id.IsValid()))
{
return;
}
m_assetPathToId.insert_key(CreateUUIDForName(assetPath)).first->second = AZStd::move(id);
}
void AssetRegistry::AddRegistry(AZStd::shared_ptr<AssetRegistry> assetRegistry)
{
for (const auto& element : assetRegistry->m_assetIdToInfo)
{
m_assetIdToInfo[element.first] = element.second;
// remove dependency info that exists for this asset, as the change could have removed any dependenices this asset had.
m_assetDependencies.erase(element.first);
}
for (const auto& element : assetRegistry->m_assetDependencies)
{
m_assetDependencies[element.first] = element.second;
}
for (const auto& element : assetRegistry->m_assetPathToId)
{
m_assetPathToId[element.first] = element.second;
}
for (const auto& element : assetRegistry->m_legacyAssetIdToRealAssetId)
{
m_legacyAssetIdToRealAssetId[element.first] = element.second;
}
}
} // namespace AzFramework