[ATOM-15472] Shader Build Pipeline: Remove Deprecated Files And Funct… (#1079)

* [ATOM-15472] Shader Build Pipeline: Remove Deprecated Files And Functions That
Predate The Shader Supervariants

These are the essential impactful changes as a result of deprecating the
ShaderResourceGroupAsset.

* Addressed feedback by @moudgils. Better comments in header files.

* More updates related with deprecation of ShaderResourceGroupAsset

* Deleted the temporary version 2 classes.

* Updated version of the shader asset builders.

* Updated version of all the shader related classes impacted
by the Supervariant concept and deprecation of ShaderResourceGroupAsset

* Changes to *.pass and DGI, Reflections and RayTracing.

* changes to material related assets

* changes to core lights

* Changes to auxgeom/dynamic draw.

* changes to decals, lyshine, imguipass

* changes to RPI Pass classes

* Shader for SceneSrg, ViewSrg and ForwardPass Srgs.

* changes to mesh, skinned mesh, Morphtarget.

* Fixes to RayTracingPass.cpp & now allow empty srg in shaders.

* Updated Atom_RPI.Tests

* Simplified InstanceDatabase by removing AddHandler

------------------------------------------------------------------------------------
* Updated DiffuseGI precompiled shaders.
Added RayTracingSceneSrg and RayTracingMaterialSrg shader asset.
Updated ShaderAssetCreator::Clone to handle the supervariant when processing root variants.
Co-authored-by: Doug McDiarmid <dmcdiar@amazon.com>
------------------------------------------------------------------------------------

* Changed semantics for some PassSrg to SRG_PerPass_WithFallback.

AuxGeom/FixedShapeProcessor.cpp requires SRG_PerDraw on ObjectSrg.

Removed names of SceneSrg and ViewSrg from RPISystemDescriptor.cpp

* Moved ShaderLib/Atom/Features/DummyEntryFunctions.azsli
To  Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/DummyEntryFunctions.azsli

Removed redundant checking for finalization in
ShaderResourceGroupLayout.cpp

* Fixed race condition bug for Shader::FindOrCreate.
InstanceDatabase<>::CreateInstance() needs to be atomic
for instance creation and initialization.

Added optional InstanceHandler::CreateFunctionWithParams to accomodate
to the needs of Instances that need more than an asset reference
to be able to be created an initialzed.

Removed ShaderResourceGroup::FindOrCreate() only ::Create is available
now.

* Renamed scene_and_view_srgs.* as SceneAndViewSrgs.*

Changed GetAzslFileOfOrigin for GetUniqueId

* Fixed unit tests.

* Reverted the serialization name of m_uniqueId back to
"m_azslFileOfOrigin" so precompiled shaders don't fail
in layout comparison.

* Fixed AtomCore.Tests
Removed non-applicable test. InstanceDatabase.AddHandler() is not
available anymore.

* The Null rhi is re-enabled for shader compilation.

Signed-off-by: garrieta <garrieta@amazon.com>
This commit is contained in:
galibzon
2021-06-11 07:48:23 -05:00
committed by GitHub
parent 1435982330
commit cc615a8f32
253 changed files with 3389 additions and 65834 deletions
@@ -76,7 +76,7 @@ namespace AZ
template <typename Type>
friend struct AZStd::IntrusivePtrCountPolicy;
template <typename Type>
template<typename Type>
friend class InstanceDatabase;
// Pointer to the InstanceDatabase that owns this instance. Will be null if the InstanceData object
@@ -46,14 +46,22 @@ namespace AZ
*/
using CreateFunction = AZStd::function<Instance<Type>(AssetData*)>;
using CreateFunctionWithParam = AZStd::function<Instance<Type>(AssetData*, const AZStd::any* param)>;
/**
* Deletion takes an asset as input and transfers ownership to the method.
*/
using DeleteFunction = AZStd::function<void(Type*)>;
/// [Required] The function to use when creating an instance.
/// The system will assert if no creation function is provided.
CreateFunction m_createFunction;
/// A function to use when creating an instance.
/// The system will assert if both @m_createFunction and @m_createFunctionWithParam
/// creation functions are invalid.
CreateFunction m_createFunction = nullptr;
/// A function with an additional custom param to use when creating an instance.
/// The system will assert if both @m_createFunction and @m_createFunctionWithParam
/// creation functions are invalid.
CreateFunctionWithParam m_createFunctionWithParam = nullptr;
/// [Optional] The function to use when deleting an instance.
DeleteFunction m_deleteFunction = [](Type* t) { delete t; };
@@ -156,28 +164,14 @@ namespace AZ
* Use this function when creating an InstanceDatabase that will handle concrete classes of @ref Type.
* \param assetType - All instances will be based on subclasses of this asset type.
* \param handler - An InstanceHandler that creates instances of @ref assetType assets.
* \param checkAssetIds - If true, it will be validated that "instance->m_assetId == asset.GetId()"
*/
static void Create(const AssetType& assetType, const InstanceHandler<Type>& handler);
/**
* Create the InstanceDatabase with no handlers. Individual handlers must be added using @ref AddHandler().
* Use this function when creating an InstanceDatabase that will handle subclasses of @ref Type.
* \param assetType - All instances will be based on subclasses of this asset type.
*/
static void Create(const AssetType& assetType);
static void Create(const AssetType& assetType, const InstanceHandler<Type>& handler, bool checkAssetIds = true);
static void Destroy();
static bool IsReady();
static InstanceDatabase& Instance();
/**
* Add an InstanceHandler that will create instances for assets of type @ref assetType.
*/
void AddHandler(const AssetType& assetType, const InstanceHandler<Type>& handler);
void AddHandler(const AssetType& assetType, typename InstanceHandler<Type>::CreateFunction createFunction);
void RemoveHandler(const AssetType& assetType);
/**
* Attempts to find an instance associated with the provided id. If the instance exists, it
* is returned. If no instance is found, nullptr is returned. If is safe to call this from
@@ -205,18 +199,20 @@ namespace AZ
* when acquiring an instance.
* @return Returns a smart pointer to the instance, which was either found or created.
*/
Data::Instance<Type> FindOrCreate(const InstanceId& id, const Asset<AssetData>& asset);
Data::Instance<Type> FindOrCreate(const InstanceId& id, const Asset<AssetData>& asset, const AZStd::any* param = nullptr);
//! Calls the above FindOrCreate using an InstanceId created from the asset
Data::Instance<Type> FindOrCreate(const Asset<AssetData>& asset);
Data::Instance<Type> FindOrCreate(const Asset<AssetData>& asset, const AZStd::any* param = nullptr);
//! Calls FindOrCreate using a random InstanceId
Data::Instance<Type> Create(const Asset<AssetData>& asset);
Data::Instance<Type> Create(const Asset<AssetData>& asset, const AZStd::any* param = nullptr);
private:
InstanceDatabase(const AssetType& assetType);
~InstanceDatabase();
bool m_checkAssetIds = true;
//useAssetTypeAsKeyForHandlers;
static const char* GetEnvironmentName();
// Utility function called by InstanceData to remove the instance from the database.
@@ -224,11 +220,7 @@ namespace AZ
void ValidateSameAsset(InstanceData* instance, const Data::Asset<AssetData>& asset) const;
// Performs a thread-safe search for the InstanceHandler for a given asset type.
bool FindHandler(const AssetType& assetType, InstanceHandler<Type>& handlerOut);
mutable AZStd::shared_mutex m_handlersMutex;
AZStd::unordered_map<AssetType, InstanceHandler<Type>> m_handlers;
InstanceHandler<Type> m_instanceHandler;
// m_database uses a recursive_mutex instead of a shared_mutex because it's possible to recursively
// create or destroy instances on the same thread while in the midst of creating or destroying an instance.
@@ -241,10 +233,11 @@ namespace AZ
static EnvironmentVariable<InstanceDatabase*> ms_instance;
};
template <typename Type>
EnvironmentVariable<InstanceDatabase<Type>*> InstanceDatabase<Type>::ms_instance = nullptr;
template<typename Type>
EnvironmentVariable<InstanceDatabase<Type>*>
InstanceDatabase<Type>::ms_instance = nullptr;
template <typename Type>
template<typename Type>
InstanceDatabase<Type>::~InstanceDatabase()
{
#ifdef AZ_DEBUG_BUILD
@@ -261,52 +254,7 @@ namespace AZ
"AZ::Data::%s still has active references.", Type::GetDatabaseName());
}
template <typename Type>
void InstanceDatabase<Type>::AddHandler(const AssetType& assetType, const InstanceHandler<Type>& handler)
{
AZ_Assert(handler.m_createFunction, "You are required to provide a create function to InstanceDatabase.");
AZStd::unique_lock<AZStd::shared_mutex> lock(m_handlersMutex);
auto result = m_handlers.emplace(assetType, handler);
AZ_Assert(result.second, "An InstanceHandler already exists for this AssetType");
}
template <typename Type>
void InstanceDatabase<Type>::AddHandler(const AssetType& assetType, typename InstanceHandler<Type>::CreateFunction createFunction)
{
InstanceHandler<Type> instanceHandler;
instanceHandler.m_createFunction = createFunction;
AddHandler(assetType, instanceHandler);
}
template <typename Type>
void InstanceDatabase<Type>::RemoveHandler(const AssetType& assetType)
{
AZStd::unique_lock<AZStd::shared_mutex> lock(m_handlersMutex);
m_handlers.erase(assetType);
}
template <typename Type>
bool InstanceDatabase<Type>::FindHandler(const AssetType& assetType, InstanceHandler<Type>& handlerOut)
{
AZStd::shared_lock<AZStd::shared_mutex> lock(m_handlersMutex);
auto handlerIter = m_handlers.find(assetType);
if (handlerIter != m_handlers.end())
{
// Since the handler is just a couple pointers, we copy the handler so we can
// release the lock right away.
handlerOut = handlerIter->second;
return true;
}
else
{
return false;
}
}
template <typename Type>
template<typename Type>
Data::Instance<Type> InstanceDatabase<Type>::Find(const InstanceId& id) const
{
AZStd::scoped_lock<AZStd::recursive_mutex> lock(m_databaseMutex);
@@ -318,8 +266,9 @@ namespace AZ
return nullptr;
}
template <typename Type>
Data::Instance<Type> InstanceDatabase<Type>::FindOrCreate(const InstanceId& id, const Asset<AssetData>& asset)
template<typename Type>
Data::Instance<Type> InstanceDatabase<Type>::FindOrCreate(
const InstanceId& id, const Asset<AssetData>& asset, const AZStd::any* param)
{
if (!id.IsValid())
{
@@ -358,24 +307,6 @@ namespace AZ
}
}
if (!azrtti_istypeof(m_baseAssetType, assetLocal.Get()))
{
InstanceHandler<Type> instanceHandler;
// If a handler was incorrectly registered for an unrelated asset type, this is the
// first chance we have to discover that fact, because up until now all we had was two
// TypeIds.
if (FindHandler(assetLocal.GetType(), instanceHandler))
{
AZ_Assert(false, "An InstanceHandler was added for asset type %s which is not a subclass of the base asset type %s.",
assetLocal.GetType().ToString<AZStd::string>().data(),
m_baseAssetType.ToString<AZStd::string>().data()
);
return nullptr;
}
}
// Take a lock to guard the insertion. Note that this will not guard against recursive insertions on the same thread.
AZStd::scoped_lock<AZStd::recursive_mutex> lock(m_databaseMutex);
@@ -390,48 +321,46 @@ namespace AZ
}
// Emplace a new instance and return it.
InstanceHandler<Type> instanceHandler;
if (FindHandler(assetLocal.GetType(), instanceHandler))
// It's possible for the m_createFunction call to recursively trigger another FindOrCreate call, so be aware that
// the contents of m_database may change within this call.
Data::Instance<Type> instance = nullptr;
if (!param)
{
// It's possible for the m_createFunction call to recursively trigger another FindOrCreate call, so be aware that
// the contents of m_database may change within this call.
Data::Instance<Type> instance = instanceHandler.m_createFunction(assetLocal.Get());
if (instance)
{
AZ_Assert(m_database.find(id) == m_database.end(),
"Instance creation for asset id %s resulted in a recursive creation of that asset, which was unexpected. "
"This asset might be erroneously referencing itself as a dependent asset.", id.ToString<AZStd::string>().c_str());
instance->m_id = id;
instance->m_parentDatabase = this;
instance->m_assetId = assetLocal.GetId();
instance->m_assetType = assetLocal.GetType();
m_database.emplace(id, instance.get());
}
return AZStd::move(instance);
instance = m_instanceHandler.m_createFunction(assetLocal.Get());
}
else
{
AZ_Warning(
"InstanceDatabase", false,
"No InstanceHandler found for asset type %s", assetLocal.GetType().ToString<AZStd::string>().data());
return nullptr;
instance = m_instanceHandler.m_createFunctionWithParam(assetLocal.Get(), param);
}
if (instance)
{
AZ_Assert(m_database.find(id) == m_database.end(),
"Instance creation for asset id %s resulted in a recursive creation of that asset, which was unexpected. "
"This asset might be erroneously referencing itself as a dependent asset.", id.ToString<AZStd::string>().c_str());
instance->m_id = id;
instance->m_parentDatabase = this;
instance->m_assetId = assetLocal.GetId();
instance->m_assetType = assetLocal.GetType();
m_database.emplace(id, instance.get());
}
return AZStd::move(instance);
}
template <typename Type>
Data::Instance<Type> InstanceDatabase<Type>::FindOrCreate(const Asset<AssetData>& asset)
template<typename Type>
Data::Instance<Type> InstanceDatabase<Type>::FindOrCreate(const Asset<AssetData>& asset, const AZStd::any* param)
{
return FindOrCreate(Data::InstanceId::CreateFromAssetId(asset.GetId()), asset);
return FindOrCreate(Data::InstanceId::CreateFromAssetId(asset.GetId()), asset, param);
}
template <typename Type>
Data::Instance<Type> InstanceDatabase<Type>::Create(const Asset<AssetData>& asset)
template<typename Type>
Data::Instance<Type> InstanceDatabase<Type>::Create(const Asset<AssetData>& asset, const AZStd::any* param)
{
return FindOrCreate(Data::InstanceId::CreateRandom(), asset);
return FindOrCreate(Data::InstanceId::CreateRandom(), asset, param);
}
template <typename Type>
template<typename Type>
void InstanceDatabase<Type>::ReleaseInstance(InstanceData* instance, const InstanceId& instanceId)
{
AZStd::scoped_lock<AZStd::recursive_mutex> lock(m_databaseMutex);
@@ -447,22 +376,13 @@ namespace AZ
instance->m_useCount.compare_exchange_strong(expectedRefCount, -1))
{
m_database.erase(instance->GetId());
InstanceHandler<Type> instanceHandler;
if (FindHandler(instance->GetAssetType(), instanceHandler))
{
instanceHandler.m_deleteFunction(static_cast<Type*>(instance));
}
else
{
AZ_Assert(false,
"Cannot delete Instance. No InstanceHandler found for asset type %s", instance->GetAssetType().ToString<AZStd::string>().data());
}
m_instanceHandler.m_deleteFunction(static_cast<Type*>(instance));
}
}
template <typename Type>
void InstanceDatabase<Type>::ValidateSameAsset(InstanceData* instance, const Data::Asset<AssetData>& asset) const
template<typename Type>
void InstanceDatabase<Type>::ValidateSameAsset(
InstanceData* instance, const Data::Asset<AssetData>& asset) const
{
/**
* The following validation layer is disabled in release, but is designed to catch a couple related edge cases
@@ -476,46 +396,47 @@ namespace AZ
*/
#if defined (AZ_DEBUG_BUILD)
AZ_Error("InstanceDatabase", instance->m_assetId == asset.GetId(),
"InstanceDatabase::FindOrCreate found the requested instance, but a different asset was used to create it. "
"Instances of a specific id should be acquired using the same asset. Either make sure the instance id "
"is actually unique, or that you are using the same asset each time for that particular id.");
if (m_checkAssetIds)
{
AZ_Error(
"InstanceDatabase", (instance->m_assetId == asset.GetId()),
"InstanceDatabase::FindOrCreate found the requested instance, but a different asset was used to create it. "
"Instances of a specific id should be acquired using the same asset. Either make sure the instance id "
"is actually unique, or that you are using the same asset each time for that particular id.");
}
#else
AZ_UNUSED(instance);
AZ_UNUSED(asset);
#endif
}
template <typename Type>
template<typename Type>
InstanceDatabase<Type>::InstanceDatabase(const AssetType& assetType)
: m_baseAssetType(assetType)
{
}
template <typename Type>
void InstanceDatabase<Type>::Create(const AssetType& assetType)
template<typename Type>
void InstanceDatabase<Type>::Create(const AssetType& assetType, const InstanceHandler<Type>& handler, bool checkAssetIds)
{
AZ_Assert(!ms_instance || !ms_instance.Get(), "InstanceDatabase already created!");
if (!ms_instance)
{
ms_instance = Environment::CreateVariable<InstanceDatabase*>(GetEnvironmentName());
ms_instance = Environment::CreateVariable<InstanceDatabase<Type>*>(GetEnvironmentName());
}
if (!ms_instance.Get())
{
ms_instance.Set(aznew InstanceDatabase<Type>(assetType));
}
AZ_Assert(handler.m_createFunction || handler.m_createFunctionWithParam, "At least one create function must be valid");
ms_instance.Get()->m_instanceHandler = handler;
ms_instance.Get()->m_checkAssetIds = checkAssetIds;
}
template <typename Type>
void InstanceDatabase<Type>::Create(const AssetType& assetType, const InstanceHandler<Type>& handler)
{
Create(assetType);
Instance().AddHandler(assetType, handler);
}
template <typename Type>
template<typename Type>
void InstanceDatabase<Type>::Destroy()
{
AZ_Assert(ms_instance, "InstanceDatabase not created!");
@@ -523,7 +444,7 @@ namespace AZ
*ms_instance = nullptr;
}
template <typename Type>
template<typename Type>
bool InstanceDatabase<Type>::IsReady()
{
if (!ms_instance)
@@ -534,7 +455,7 @@ namespace AZ
return ms_instance && *ms_instance;
}
template <typename Type>
template<typename Type>
InstanceDatabase<Type>& InstanceDatabase<Type>::Instance()
{
if (!ms_instance)
@@ -546,10 +467,12 @@ namespace AZ
return *(*ms_instance);
}
template <typename Type>
template<typename Type>
const char* InstanceDatabase<Type>::GetEnvironmentName()
{
static_assert(HasInstanceDatabaseName<Type>::value, "All classes used as instances in an InstanceDatabase need to define AZ_INSTANCE_DATA in the class.");
static_assert(
HasInstanceDatabaseName<Type>::value,
"All classes used as instances in an InstanceDatabase need to define AZ_INSTANCE_DATA in the class.");
return Type::GetDatabaseName();
}
}
@@ -34,8 +34,7 @@ namespace UnitTest
static const AssetId s_assetId3{ Uuid("{D9CDAB04-D206-431E-BDC0-1DD615D56197}") };
// test asset type
class TestAssetType
: public AssetData
class TestAssetType : public AssetData
{
public:
AZ_CLASS_ALLOCATOR(TestAssetType, AZ::SystemAllocator, 0);
@@ -47,30 +46,30 @@ namespace UnitTest
}
};
class TestInstanceA
: public InstanceData
class TestInstanceA : public InstanceData
{
public:
AZ_INSTANCE_DATA(TestInstanceA, "{65CBF1C8-F65F-4A84-8A11-B510BC435DB0}");
AZ_CLASS_ALLOCATOR(TestInstanceA, AZ::SystemAllocator, 0);
TestInstanceA(TestAssetType* asset)
: m_asset{asset, AZ::Data::AssetLoadBehavior::Default}
{}
: m_asset{ asset, AZ::Data::AssetLoadBehavior::Default }
{
}
Asset<TestAssetType> m_asset;
};
class TestInstanceB
: public InstanceData
class TestInstanceB : public InstanceData
{
public:
AZ_INSTANCE_DATA(TestInstanceB, "{4ED0A8BF-7800-44B2-AC73-2CB759C61C37}");
AZ_CLASS_ALLOCATOR(TestInstanceB, AZ::SystemAllocator, 0);
TestInstanceB(TestAssetType* asset)
: m_asset{asset, AZ::Data::AssetLoadBehavior::Default }
{}
: m_asset{ asset, AZ::Data::AssetLoadBehavior::Default }
{
}
~TestInstanceB()
{
@@ -86,8 +85,7 @@ namespace UnitTest
// test asset handler
template<typename AssetDataT>
class MyAssetHandler
: public AssetHandler
class MyAssetHandler : public AssetHandler
{
public:
AZ_CLASS_ALLOCATOR(MyAssetHandler, AZ::SystemAllocator, 0);
@@ -120,13 +118,12 @@ namespace UnitTest
}
};
class InstanceDatabaseTest
: public AllocatorsFixture
class InstanceDatabaseTest : public AllocatorsFixture
{
protected:
MyAssetHandler<TestAssetType>* m_assetHandler;
public:
public:
void SetUp() override
{
AllocatorsFixture::SetUp();
@@ -200,7 +197,7 @@ namespace UnitTest
AZStd::vector<Uuid> guids;
AZStd::vector<Asset<TestAssetType>> assets;
for (size_t i = 0; i < assetIdCount; ++i)
{
Uuid guid = Uuid::CreateRandom();
@@ -211,7 +208,6 @@ namespace UnitTest
assets.emplace_back(assetManager.CreateAsset<TestAssetType>(guid, AZ::Data::AssetLoadBehavior::Default));
}
AZStd::vector<AZStd::thread> threads;
AZStd::mutex mutex;
AZStd::atomic<int> threadCount((int)threadCountMax);
@@ -232,27 +228,29 @@ namespace UnitTest
for (size_t i = 0; i < threadCountMax; ++i)
{
threads.emplace_back([&instanceManager, &threadCount, &cv, &guids, &assets, &durationSeconds]()
{
AZ::Debug::Timer timer;
timer.Stamp();
while(timer.GetDeltaTimeInSeconds() < durationSeconds)
threads.emplace_back(
[&instanceManager, &threadCount, &cv, &guids, &assets, &durationSeconds]()
{
const size_t index = rand() % guids.size();
const Uuid uuid = guids[index];
const InstanceId instanceId{uuid};
const AssetId assetId{uuid};
AZ::Debug::Timer timer;
timer.Stamp();
Instance<TestInstanceA> instance = instanceManager.FindOrCreate(instanceId, Asset<TestAssetType>(assetId, azrtti_typeid<TestAssetType>()));
EXPECT_NE(instance, nullptr);
EXPECT_EQ(instance->GetId(), instanceId);
EXPECT_EQ(instance->m_asset, assets[index]);
}
while (timer.GetDeltaTimeInSeconds() < durationSeconds)
{
const size_t index = rand() % guids.size();
const Uuid uuid = guids[index];
const InstanceId instanceId{ uuid };
const AssetId assetId{ uuid };
threadCount--;
cv.notify_one();
});
Instance<TestInstanceA> instance =
instanceManager.FindOrCreate(instanceId, Asset<TestAssetType>(assetId, azrtti_typeid<TestAssetType>()));
EXPECT_NE(instance, nullptr);
EXPECT_EQ(instance->GetId(), instanceId);
EXPECT_EQ(instance->m_asset, assets[index]);
}
threadCount--;
cv.notify_one();
});
}
bool timedOut = false;
@@ -261,7 +259,9 @@ namespace UnitTest
while (threadCount > 0 && !timedOut)
{
AZStd::unique_lock<AZStd::mutex> lock(mutex);
timedOut = (AZStd::cv_status::timeout == cv.wait_until(lock, AZStd::chrono::system_clock::now() + AZStd::chrono::seconds(durationSeconds * 2)));
timedOut =
(AZStd::cv_status::timeout ==
cv.wait_until(lock, AZStd::chrono::system_clock::now() + AZStd::chrono::seconds(durationSeconds * 2)));
}
EXPECT_TRUE(threadCount == 0) << "One or more threads appear to be deadlocked at " << timer.GetDeltaTimeInSeconds() << " seconds";
@@ -280,8 +280,8 @@ namespace UnitTest
TEST_F(InstanceDatabaseTest, ParallelInstanceCreate)
{
// This is the original test scenario from when InstanceDatabase was first implemented
// threads, AssetIds, seconds
ParallelInstanceCreateHelper( 8, 100, 5 );
// threads, AssetIds, seconds
ParallelInstanceCreateHelper(8, 100, 5);
// This value is checked in as 1 so this test doesn't take too much time, but can be increased locally to soak the test.
const size_t attempts = 1;
@@ -291,10 +291,10 @@ namespace UnitTest
printf("Attempt %zu of %zu... \n", i, attempts);
// The idea behind this series of tests is that there are two threads sharing one Instance, and both threads try to
// create or release that instance at the same time.
// create or release that instance at the same time.
// At the time, this set of scenarios has something like a 10% failure rate.
const size_t duration = 2;
// threads, AssetIds, seconds
// threads, AssetIds, seconds
ParallelInstanceCreateHelper(2, 1, duration);
ParallelInstanceCreateHelper(4, 1, duration);
ParallelInstanceCreateHelper(8, 1, duration);
@@ -306,7 +306,7 @@ namespace UnitTest
// Here we try a bunch of different threadCount:assetCount ratios to be thorough
const size_t duration = 2;
// threads, AssetIds, seconds
// threads, AssetIds, seconds
ParallelInstanceCreateHelper(2, 1, duration);
ParallelInstanceCreateHelper(4, 1, duration);
ParallelInstanceCreateHelper(4, 2, duration);
@@ -328,7 +328,10 @@ namespace UnitTest
// Tests whether the deleter actually calls delete properly without
// a parent database.
instance->m_onDeleteCallback = [this, &m_deleted] () { m_deleted = true; };
instance->m_onDeleteCallback = [this, &m_deleted]()
{
m_deleted = true;
};
}
EXPECT_TRUE(m_deleted);
@@ -386,242 +389,4 @@ namespace UnitTest
InstanceDatabase<TestInstanceB>::Destroy();
}
class InstanceDatabaseTestWithMultipleSubclasses
: public AllocatorsFixture
{
protected:
// We have "BaseAsset" with subclasses "FooAsset" and "BarAsset",
// and corresponding "BaseInstance" with subclasses "FooInstance" and "BarInstance".
// There is one "InstanceDatabse<BaseInstance>" that can create instances of both subtypes.
class BaseAsset
: public AssetData
{
public:
AZ_CLASS_ALLOCATOR(BaseAsset, AZ::SystemAllocator, 0);
AZ_RTTI(FooAsset, "{35B443A6-D8ED-4C3C-A3F0-D642251F0AA5}", AssetData);
BaseAsset()
{
m_status = AssetStatus::Ready;
}
};
class BaseInstance
: public InstanceData
{
public:
AZ_INSTANCE_DATA(BaseInstance, "{EFEC3406-2CB7-462E-A676-C22177E143E6}");
AZ_CLASS_ALLOCATOR(BaseInstance, AZ::SystemAllocator, 0);
BaseInstance(BaseAsset* asset)
: m_asset{ asset, AZ::Data::AssetLoadBehavior::Default }
{}
Asset<BaseAsset> m_asset;
};
class FooAsset
: public BaseAsset
{
public:
AZ_CLASS_ALLOCATOR(FooAsset, AZ::SystemAllocator, 0);
AZ_RTTI(FooAsset, "{74BAE278-3DCA-4ADD-807E-2A6873F9EA3C}", BaseAsset);
};
class BarAsset
: public BaseAsset
{
public:
AZ_CLASS_ALLOCATOR(BarAsset, AZ::SystemAllocator, 0);
AZ_RTTI(FooAsset, "{2BCD66F5-768B-4569-9FC2-DE92ABC9C0BF}", BaseAsset);
};
class FooInstance
: public BaseInstance
{
public:
AZ_RTTI(FooInstance, "{B5487509-5518-4591-AC96-03E623A584B7}", BaseInstance);
AZ_CLASS_ALLOCATOR(FooInstance, AZ::SystemAllocator, 0);
FooInstance(BaseAsset* asset)
: BaseInstance(asset)
{
EXPECT_TRUE(azrtti_typeid<FooAsset>() == asset->GetType());
}
};
class BarInstance
: public BaseInstance
{
public:
AZ_RTTI(BarInstance, "{CE9C844A-625D-4899-B7DB-8127D4618D25}", BaseInstance);
AZ_CLASS_ALLOCATOR(BarInstance, AZ::SystemAllocator, 0);
BarInstance(BaseAsset* asset)
: BaseInstance(asset)
{
EXPECT_TRUE(azrtti_typeid<BarAsset>() == asset->GetType());
}
};
MyAssetHandler<FooAsset> m_fooAssetHandler;
MyAssetHandler<BarAsset> m_barAssetHandler;
public:
void SetUp() override
{
AllocatorsFixture::SetUp();
AllocatorInstance<PoolAllocator>::Create();
AllocatorInstance<ThreadPoolAllocator>::Create();
// create the asset database
{
AssetManager::Descriptor desc;
AssetManager::Create(desc);
}
// create the instance database
{
InstanceDatabase<BaseInstance>::Create(azrtti_typeid<BaseAsset>());
InstanceHandler<BaseInstance> fooHandler;
fooHandler.m_createFunction = [](AssetData* assetData)
{
EXPECT_TRUE(azrtti_istypeof<FooAsset>(assetData));
return aznew FooInstance(static_cast<FooAsset*>(assetData));
};
InstanceDatabase<BaseInstance>::Instance().AddHandler(azrtti_typeid<FooAsset>(), fooHandler);
// Using a different overload of AddHandler()
InstanceDatabase<BaseInstance>::Instance().AddHandler(azrtti_typeid<BarAsset>(), [](AssetData* assetData)
{
EXPECT_TRUE(azrtti_istypeof<BarAsset>(assetData));
return aznew BarInstance(static_cast<BarAsset*>(assetData));
});
}
AssetManager::Instance().RegisterHandler(&m_fooAssetHandler, AzTypeInfo<FooAsset>::Uuid());
AssetManager::Instance().RegisterHandler(&m_barAssetHandler, AzTypeInfo<BarAsset>::Uuid());
}
void TearDown() override
{
AssetManager::Instance().UnregisterHandler(&m_fooAssetHandler);
AssetManager::Instance().UnregisterHandler(&m_barAssetHandler);
AssetManager::Destroy();
InstanceDatabase<BaseInstance>::Destroy();
AllocatorInstance<ThreadPoolAllocator>::Destroy();
AllocatorInstance<PoolAllocator>::Destroy();
AllocatorsFixture::TearDown();
}
};
TEST_F(InstanceDatabaseTestWithMultipleSubclasses, InstanceCreate)
{
auto& assetManager = AssetManager::Instance();
auto& instanceDatabase = InstanceDatabase<BaseInstance>::Instance();
Asset<FooAsset> fooAsset = assetManager.CreateAsset<FooAsset>(s_assetId0, AZ::Data::AssetLoadBehavior::Default);
Asset<BarAsset> barAsset = assetManager.CreateAsset<BarAsset>(s_assetId1, AZ::Data::AssetLoadBehavior::Default);
// Run the creation tests on 'A' first.
Instance<BaseInstance> fooInstanceA = instanceDatabase.Find(s_instanceId0);
EXPECT_EQ(fooInstanceA, nullptr);
Instance<BaseInstance> barInstanceA = instanceDatabase.Find(s_instanceId1);
EXPECT_EQ(barInstanceA, nullptr);
fooInstanceA = instanceDatabase.FindOrCreate(s_instanceId0, fooAsset);
EXPECT_NE(fooInstanceA, nullptr);
EXPECT_EQ(fooInstanceA->m_asset, fooAsset);
EXPECT_TRUE(azrtti_typeid<FooInstance>() == fooInstanceA->RTTI_GetType());
EXPECT_EQ(fooInstanceA, instanceDatabase.Find(s_instanceId0));
barInstanceA = instanceDatabase.FindOrCreate(s_instanceId1, barAsset);
EXPECT_NE(barInstanceA, nullptr);
EXPECT_EQ(barInstanceA->m_asset, barAsset);
EXPECT_TRUE(azrtti_typeid<BarInstance>() == barInstanceA->RTTI_GetType());
EXPECT_EQ(barInstanceA, instanceDatabase.Find(s_instanceId1));
// Run the same test on 'B' to make sure it works independently.
Instance<BaseInstance> fooInstanceB = instanceDatabase.Find(s_instanceId2);
EXPECT_EQ(fooInstanceB, nullptr);
Instance<BaseInstance> barInstanceB = instanceDatabase.Find(s_instanceId3);
EXPECT_EQ(barInstanceB, nullptr);
fooInstanceB = instanceDatabase.FindOrCreate(s_instanceId2, fooAsset);
EXPECT_NE(fooInstanceB, nullptr);
EXPECT_EQ(fooInstanceB->m_asset, fooAsset);
EXPECT_TRUE(azrtti_typeid<FooInstance>() == fooInstanceB->RTTI_GetType());
EXPECT_EQ(fooInstanceB, instanceDatabase.Find(s_instanceId2));
barInstanceB = instanceDatabase.FindOrCreate(s_instanceId3, barAsset);
EXPECT_NE(barInstanceB, nullptr);
EXPECT_EQ(barInstanceB->m_asset, barAsset);
EXPECT_TRUE(azrtti_typeid<BarInstance>() == barInstanceB->RTTI_GetType());
EXPECT_EQ(barInstanceB, instanceDatabase.Find(s_instanceId3));
// Make sure the instances are unique
EXPECT_NE(fooInstanceA, fooInstanceB);
EXPECT_NE(barInstanceA, barInstanceB);
}
TEST_F(InstanceDatabaseTestWithMultipleSubclasses, TestError_AddHandler_AssetTypeIsNotSubclass)
{
MyAssetHandler<TestAssetType> testAssetHandler;
AssetManager::Instance().RegisterHandler(&testAssetHandler, azrtti_typeid<TestAssetType>());
// Register an instance handler with an unrelated asset type. This can't actually
// check the AssetType yet because all it has are AssetType GUIDs, no actual data.
{
InstanceHandler<BaseInstance> instanceHandler;
instanceHandler.m_createFunction = [](AssetData* assetData)
{
return aznew BaseInstance(static_cast<BaseAsset*>(assetData));
};
AssetType unrelatedAssetType = azrtti_typeid<TestAssetType>();
InstanceDatabase<BaseInstance>::Instance().AddHandler(unrelatedAssetType, instanceHandler);
}
// Try to use the unrelated handler. This is where we'll actually get an error.
{
AZ_TEST_START_ASSERTTEST;
Asset<TestAssetType> testAsset = AssetManager::Instance().CreateAsset<TestAssetType>(s_assetId0, AZ::Data::AssetLoadBehavior::Default);
EXPECT_EQ(nullptr, InstanceDatabase<BaseInstance>::Instance().FindOrCreate(s_instanceId0, testAsset));
AZ_TEST_STOP_ASSERTTEST(1);
}
AssetManager::Instance().UnregisterHandler(&testAssetHandler);
}
TEST_F(InstanceDatabaseTestWithMultipleSubclasses, TestError_AddHandler_AlreadyExists)
{
InstanceHandler<BaseInstance> instanceHandler;
instanceHandler.m_createFunction = [](AssetData*)
{
return nullptr; // Doesn't matter
};
AZ_TEST_START_ASSERTTEST;
// The SetUp() function already registered a handler for FooAsset so this should fail
InstanceDatabase<BaseInstance>::Instance().AddHandler(azrtti_typeid<FooAsset>(), instanceHandler);
InstanceDatabase<BaseInstance>::Instance().AddHandler(azrtti_typeid<FooAsset>(), [](AssetData*) { return nullptr; });
AZ_TEST_STOP_ASSERTTEST(2);
}
}
} // namespace UnitTest