You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp

250 lines
10 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#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_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