[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:
@@ -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
|
||||
|
||||
@@ -1,402 +0,0 @@
|
||||
/*
|
||||
* 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 "AzslBuilder.h"
|
||||
#include "ShaderBuilderUtility.h"
|
||||
#include "ShaderPlatformInterfaceRequest.h"
|
||||
#include <CommonFiles/GlobalBuildOptions.h>
|
||||
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
enum class JobParameterIndices : uint32_t
|
||||
{
|
||||
ApiName,
|
||||
PreprocessedCode,
|
||||
PreprocessorError,
|
||||
SkipJob
|
||||
};
|
||||
|
||||
AZ::Uuid AzslBuilder::GetUUID()
|
||||
{
|
||||
return AZ::Uuid::CreateString("{72DCFC95-1B9E-4A8D-8633-D497CACD98AB}");
|
||||
}
|
||||
|
||||
RHI::ShaderPlatformInterface* GetShaderPlatformInterfaceForApi(const AZStd::string& apiNameFilter, const AssetBuilderSDK::PlatformInfo& currentPlatform)
|
||||
{
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces;
|
||||
ShaderPlatformInterfaceRequestBus::BroadcastResult(platformInterfaces, &ShaderPlatformInterfaceRequest::GetShaderPlatformInterface, currentPlatform);
|
||||
for (RHI::ShaderPlatformInterface* oneInterface : platformInterfaces)
|
||||
{
|
||||
if (oneInterface && oneInterface->GetAPIName().GetStringView() == apiNameFilter)
|
||||
{
|
||||
return oneInterface;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PreprocessorData PreprocessSource(const AZStd::string& inputFile, const AZStd::string& originalPath, const PreprocessorOptions& options)
|
||||
{
|
||||
// run mcpp
|
||||
PreprocessorData output;
|
||||
PreprocessFile(inputFile, output, options, true, true);
|
||||
// do not let the filename.api.azsl.prepend be the filename that will be regarded as the source.
|
||||
// because SRG assets are located using the 'containingFile' so we need to preserve the true origin:
|
||||
MutateLineDirectivesFileOrigin(output.code, originalPath);
|
||||
RHI::ReportErrorMessages(AzslBuilder::BuilderName, output.diagnostics);
|
||||
return output;
|
||||
}
|
||||
|
||||
void AddAzslBuilderJobDependency(AssetBuilderSDK::JobDescriptor& jobDescriptor, const AZStd::string& platformInfoIdentifier, AZStd::string_view apiName, AZStd::string_view fullFilePath)
|
||||
{
|
||||
AssetBuilderSDK::SourceFileDependency fileDependency;
|
||||
fileDependency.m_sourceFileDependencyPath = fullFilePath;
|
||||
|
||||
AssetBuilderSDK::JobDependency dependency;
|
||||
dependency.m_jobKey = AzslBuilder::JobKey;
|
||||
dependency.m_jobKey += " ";
|
||||
dependency.m_jobKey += apiName;
|
||||
dependency.m_platformIdentifier = platformInfoIdentifier;
|
||||
dependency.m_sourceFile = fileDependency;
|
||||
dependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
jobDescriptor.m_jobDependencyList.emplace_back(dependency);
|
||||
}
|
||||
|
||||
void AzslBuilder::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const
|
||||
{
|
||||
AZStd::string fullPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.data(), request.m_sourceFile.data(), fullPath, true);
|
||||
|
||||
// this builder may take as input:
|
||||
// .shader .azsl .azsli .srgi
|
||||
// it will not behave strictly exactly the same for each type
|
||||
|
||||
// Only *.srgi files are supposed to include files that define "partial" qualified SRGs.
|
||||
const bool isSrgi = AzFramework::StringFunc::Path::IsExtension(fullPath.c_str(), SrgIncludeExtension);
|
||||
|
||||
// .azsli needs "skip check"
|
||||
const bool isAzsli = AzFramework::StringFunc::Path::IsExtension(fullPath.c_str(), "azsli");
|
||||
|
||||
// .shader files must be opened to get their build options and the referenced azsl file
|
||||
const bool isShader = AzFramework::StringFunc::Path::IsExtension(fullPath.c_str(), RPI::ShaderSourceData::Extension);
|
||||
|
||||
// .azsl must not be skipped, otherwise we're creating a risk of two .shader referring to the same .azsl racing for its output product
|
||||
|
||||
// To avoid the warning:
|
||||
// "No job was found to match the job dependency criteria declared by file "..."
|
||||
// We will schedule the job, but will do nothing
|
||||
//bool shouldSkipFile = false;
|
||||
|
||||
// We treat some issues as warnings and return "Success" from CreateJobs allows us to report the dependency.
|
||||
// If/when a valid dependency file appears, that will trigger the ShaderVariantAssetBuilder to run again.
|
||||
// Since CreateJobs will pass, we forward this message to ProcessJob which will report it as an error.
|
||||
//bool gotPreprocessingError = false;
|
||||
|
||||
|
||||
// The following if-block will be removed once [GFX TODO][ATOM-5302] is addressed, and
|
||||
// azslc allows redundant SrgSemantics for "partial" qualified SRGs.
|
||||
if (isAzsli)
|
||||
{
|
||||
auto skipCheck = ShaderBuilderUtility::ShouldSkipFileForSrgProcessing(BuilderName, fullPath);
|
||||
if (skipCheck != ShaderBuilderUtility::SrgSkipFileResult::ContinueProcess)
|
||||
{
|
||||
response.m_result = skipCheck == ShaderBuilderUtility::SrgSkipFileResult::Error ?
|
||||
AssetBuilderSDK::CreateJobsResultCode::Failed : AssetBuilderSDK::CreateJobsResultCode::Success;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isShader)
|
||||
{
|
||||
// Need to get the path to the shader file from the template, so that we can preprocess the shader data and setup
|
||||
// source file dependencies.
|
||||
auto descriptorParseOutput = ShaderBuilderUtility::LoadShaderDataJson(fullPath);
|
||||
if (!descriptorParseOutput.IsSuccess())
|
||||
{
|
||||
AZ_Error(BuilderName, false, "Failed to parse Shader Descriptor JSON: %s", descriptorParseOutput.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
// update the value of fullPath to mean directly, the azsl file:
|
||||
ShaderBuilderUtility::GetAbsolutePathToAzslFile(fullPath, descriptorParseOutput.GetValue().m_source, fullPath);
|
||||
}
|
||||
|
||||
GlobalBuildOptions buildOptions = ReadBuildOptions(BuilderName);
|
||||
|
||||
for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
|
||||
{
|
||||
AZ_TraceContext("For platform", info.m_identifier.data());
|
||||
|
||||
// Get the platform interfaces to be able to access the prepend file
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces = ShaderBuilderUtility::DiscoverValidShaderPlatformInterfaces(info);
|
||||
|
||||
// Preprocess the shader file, per activated platform.
|
||||
for (RHI::ShaderPlatformInterface* shaderPlatformInterface : platformInterfaces)
|
||||
{
|
||||
auto apiNameAsStringView = shaderPlatformInterface->GetAPIName().GetStringView();
|
||||
|
||||
AssetBuilderSDK::JobDescriptor jobDescriptor;
|
||||
jobDescriptor.m_priority = 2;
|
||||
// [GFX TODO][ATOM-2830] Set 'm_critical' back to 'false' once proper fix for Atom startup issues are in
|
||||
jobDescriptor.m_critical = true;
|
||||
jobDescriptor.m_jobKey = JobKey;
|
||||
jobDescriptor.m_jobKey += " ";
|
||||
jobDescriptor.m_jobKey += apiNameAsStringView;
|
||||
jobDescriptor.SetPlatformIdentifier(info.m_identifier.data());
|
||||
jobDescriptor.m_jobParameters[(u32)JobParameterIndices::ApiName] = apiNameAsStringView;
|
||||
if (isShader)
|
||||
{
|
||||
// add a job dependency on the azsl run (of that same job: AzslBuilder - because it also runs on .azsl)
|
||||
AddAzslBuilderJobDependency(jobDescriptor, info.m_identifier, apiNameAsStringView, fullPath);
|
||||
}
|
||||
|
||||
// execute azsl prepending here, before preprocess, in order to support macros in AzslcHeader.azsli
|
||||
AZStd::string prependedAzslSourceCode;
|
||||
RHI::PrependArguments args;
|
||||
args.m_sourceFile = fullPath.c_str();
|
||||
args.m_prependFile = shaderPlatformInterface->GetAzslHeader(info);
|
||||
args.m_addSuffixToFileName = shaderPlatformInterface->GetAPIName().GetCStr();
|
||||
args.m_destinationStringOpt = &prependedAzslSourceCode;
|
||||
|
||||
if (RHI::PrependFile(args) == fullPath) // error case. it returns the combined-file's name on success, or original path on failure, but here we use the "direct to string" mode so we don't need to store the returned name.
|
||||
{
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Failed;
|
||||
return;
|
||||
}
|
||||
AZStd::string originalLocation;
|
||||
// extract the (full) directory chain from the path: (eg from "d:/p/f.e" extract "d:/p/")
|
||||
AzFramework::StringFunc::Path::GetFullPath(fullPath.c_str(), originalLocation);
|
||||
// have to go through filesystem because we have no way to pipe data through mcpp (because of single threaded static link call and buffer limits)
|
||||
// we can't use a temporary folder because CreateJobs API does not warrant side effects, and does not prepare a temp folder.
|
||||
// we can't use the OS temp folder anyway, because many includes (eg #include "../RPI/Shadow.h") are relative and will only work from the original location
|
||||
AZStd::string prependedPath = ShaderBuilderUtility::DumpAzslPrependedCode(
|
||||
BuilderName, prependedAzslSourceCode, originalLocation, ShaderBuilderUtility::ExtractStemName(fullPath.c_str()),
|
||||
shaderPlatformInterface->GetAPIName().GetStringView());
|
||||
// run mcpp
|
||||
PreprocessorData preprocessorData = PreprocessSource(prependedPath, fullPath, buildOptions.m_preprocessorSettings);
|
||||
jobDescriptor.m_jobParameters[(u32)JobParameterIndices::PreprocessorError] = preprocessorData.diagnostics; // save for ProcessJob
|
||||
jobDescriptor.m_jobParameters[(u32)JobParameterIndices::PreprocessedCode] = preprocessorData.code; // save for ProcessJob
|
||||
AZ::IO::SystemFile::Delete(prependedPath.c_str()); // don't let that intermediate file dirty a folder under source version control.
|
||||
|
||||
for (AZStd::string includePath : preprocessorData.includedPaths)
|
||||
{
|
||||
// m_sourceFileDependencyList does not support paths with "." or ".." for relative lookup, but the preprocessor
|
||||
// may produce path strings like "C:/a/b/c/../../d/file.azsli" so we have to normalize
|
||||
AzFramework::StringFunc::Path::Normalize(includePath);
|
||||
|
||||
AssetBuilderSDK::SourceFileDependency includeFileDependency;
|
||||
includeFileDependency.m_sourceFileDependencyPath = includePath;
|
||||
response.m_sourceFileDependencyList.emplace_back(includeFileDependency);
|
||||
}
|
||||
response.m_createJobOutputs.push_back(jobDescriptor);
|
||||
} // all RHI platforms
|
||||
} // for all request.m_enabledPlatforms
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
|
||||
}
|
||||
|
||||
enum class ErrorStrategy { HardFailIfAbsent, ReturnEmptyIfAbsent };
|
||||
static AZStd::string GetJobParameterValue(const AssetBuilderSDK::ProcessJobRequest& request, JobParameterIndices index, [[maybe_unused]] ErrorStrategy failBehavior)
|
||||
{
|
||||
auto iterator = request.m_jobDescription.m_jobParameters.find(static_cast<u32>(index));
|
||||
if (iterator == request.m_jobDescription.m_jobParameters.end())
|
||||
{
|
||||
AZ_Error(AzslBuilder::BuilderName, failBehavior != ErrorStrategy::HardFailIfAbsent, "Saved data is missing in job parameters. for index [%d]", index);
|
||||
return {};
|
||||
}
|
||||
return iterator->second;
|
||||
}
|
||||
|
||||
// eg: ("D:/p/x.a", "D:/p/x.b") -> yes
|
||||
static bool HasSameFileName(const AZStd::string& lhsPath, const AZStd::string& rhsPath)
|
||||
{
|
||||
using namespace StringFunc::Path;
|
||||
AZStd::string stem1;
|
||||
GetFileName(lhsPath.c_str(), stem1);
|
||||
AZStd::string stem2;
|
||||
GetFileName(rhsPath.c_str(), stem2);
|
||||
return stem1 == stem2;
|
||||
}
|
||||
|
||||
void AzslBuilder::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const
|
||||
{
|
||||
if (request.m_jobDescription.m_jobParameters.find((u32)JobParameterIndices::SkipJob) != request.m_jobDescription.m_jobParameters.end())
|
||||
{
|
||||
AZ_TracePrintf(BuilderName, "Early out because this file was determined to not need an independent build\n");
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
return;
|
||||
}
|
||||
// report the deffered diagnostics:
|
||||
const AZStd::string& preprocessorErrors = GetJobParameterValue(request, JobParameterIndices::PreprocessorError, ErrorStrategy::ReturnEmptyIfAbsent);
|
||||
if (!preprocessorErrors.empty())
|
||||
{
|
||||
bool foundErrors = RHI::ReportErrorMessages(BuilderName, preprocessorErrors.c_str());
|
||||
if (foundErrors)
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const AZStd::sys_time_t startTime = AZStd::GetTimeNowTicks();
|
||||
AZStd::string fullSourcePath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.c_str(), request.m_sourceFile.c_str(), fullSourcePath, true);
|
||||
// extract "name" from "P:/F/name.x"
|
||||
AZStd::string sourceStemName;
|
||||
AzFramework::StringFunc::Path::GetFileName(fullSourcePath.c_str(), sourceStemName);
|
||||
|
||||
GlobalBuildOptions buildOptions = ReadBuildOptions(BuilderName);
|
||||
|
||||
// get the shader platform interface that matches this job's API
|
||||
const AZStd::string& apiName = GetJobParameterValue(request, JobParameterIndices::ApiName, ErrorStrategy::HardFailIfAbsent);
|
||||
const AZStd::string& preprocessedCode = GetJobParameterValue(request, JobParameterIndices::PreprocessedCode, ErrorStrategy::ReturnEmptyIfAbsent);
|
||||
RHI::ShaderPlatformInterface* platformInterface = GetShaderPlatformInterfaceForApi(apiName, request.m_platformInfo);
|
||||
if (!platformInterface)
|
||||
{
|
||||
AZ_Error(BuilderName, false, "Could not retreive Shader Platform Interface");
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
const bool isSrgi = AzFramework::StringFunc::Path::IsExtension(fullSourcePath.c_str(), SrgIncludeExtension);
|
||||
const bool isAzsli = AzFramework::StringFunc::Path::IsExtension(fullSourcePath.c_str(), "azsli");
|
||||
const bool isShader = AzFramework::StringFunc::Path::IsExtension(fullSourcePath.c_str(), RPI::ShaderSourceData::Extension);
|
||||
if (isShader)
|
||||
{
|
||||
// read .shader -> access azsl path -> make absolute
|
||||
RPI::ShaderSourceData shaderAssetSource;
|
||||
AZStd::shared_ptr<ShaderFiles> inputFiles = ShaderBuilderUtility::PrepareSourceInput(BuilderName, fullSourcePath, shaderAssetSource);
|
||||
if (!inputFiles)
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (shaderAssetSource.IsRhiBackendDisabled(platformInterface->GetAPIName()))
|
||||
{
|
||||
// Gracefully do nothing and return success.
|
||||
AZ_TracePrintf(
|
||||
BuilderName, "Skipping shader compilation [%s] for API [%s]\n", fullSourcePath.c_str(),
|
||||
platformInterface->GetAPIName().GetCStr());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Save .shader file name
|
||||
AzFramework::StringFunc::Path::GetFileName(request.m_sourceFile.data(), inputFiles->m_shaderFileName);
|
||||
|
||||
// Verify the presence of potential differences between the global options, and local options:
|
||||
bool mustRebuild = buildOptions.m_compilerArguments.HasDifferentAzslcArguments(shaderAssetSource.m_compiler);
|
||||
|
||||
// Merge compiler options coming from 2 source: global options (from project Config/), and .shader options.
|
||||
// We define a merge behavior that is: ".shader wins if set" (local overrides global)
|
||||
buildOptions.m_compilerArguments.Merge(shaderAssetSource.m_compiler);
|
||||
|
||||
// Earlier, we declared a job dependency on the .azsl's job, let's access the produced assets:
|
||||
uint32_t subId = ShaderBuilderUtility::MakeAzslBuildProductSubId(
|
||||
RPI::ShaderAssetSubId::GeneratedHlslSource, platformInterface->GetAPIType());
|
||||
auto assetIdOutcome = RPI::AssetUtils::MakeAssetId(inputFiles->m_azslSourceFullPath, subId);
|
||||
AZ_Warning(BuilderName, assetIdOutcome.IsSuccess(), "Product of dependency %s not found: this is an oddity but build can continue.", inputFiles->m_azslSourceFullPath.c_str());
|
||||
if (assetIdOutcome.IsSuccess())
|
||||
{
|
||||
// The .azsl build job didn't know about the build options listed in the .shader
|
||||
// so it produced "generic" artifacts xxx.ia.json, xxx.hlsl, etc.
|
||||
if (!mustRebuild)
|
||||
{
|
||||
// They are in fact sufficient. nothing more to do
|
||||
AZ_TracePrintf(BuilderName, "Product output already built by %s. exiting.", inputFiles->m_azslSourceFullPath.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
return;
|
||||
}
|
||||
// Otherwise, let's go again, but we need to modify the output's name to avoid product conflicts.*
|
||||
AZ_TracePrintf(BuilderName, "Product output already built by %s is not reusable because of incompatible azslc CompilerHints: launching independent build", inputFiles->m_azslSourceFullPath.c_str());
|
||||
}
|
||||
|
||||
if (HasSameFileName(fullSourcePath, inputFiles->m_azslSourceFullPath))
|
||||
{
|
||||
// let's add a "distinguisher" to the names of the outproduct artifacts of this build round.*
|
||||
// Because otherwise the asset processor is not going to accept an overwrite of the ones output by the .azsl job
|
||||
static constexpr char RebuildSuffix[] = ".shader-w-diff-azslc-opts";
|
||||
sourceStemName += RebuildSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string preprocessedPath = ShaderBuilderUtility::DumpPreprocessedCode(
|
||||
BuilderName,
|
||||
preprocessedCode,
|
||||
request.m_tempDirPath,
|
||||
sourceStemName,
|
||||
apiName);
|
||||
|
||||
AZ_TraceContext("Platform API", apiName);
|
||||
|
||||
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
// compiler setup
|
||||
ShaderBuilder::AzslCompiler azslc(preprocessedPath);
|
||||
AZStd::string compilerParameters = platformInterface->GetAzslCompilerParameters(buildOptions.m_compilerArguments);
|
||||
compilerParameters += " ";
|
||||
compilerParameters += platformInterface->GetAzslCompilerWarningParameters(buildOptions.m_compilerArguments);
|
||||
AtomShaderConfig::AddParametersFromConfigFile(compilerParameters, request.m_platformInfo);
|
||||
if (isSrgi || isAzsli)
|
||||
{
|
||||
// When compiling srgi or azsli files, the SRGs may appear as unused. It is necessary
|
||||
// to remove the flag --strip-unused-srgs in case it is present in the compiler parameters.
|
||||
AzFramework::StringFunc::Replace(compilerParameters, " --strip-unused-srgs", "");
|
||||
}
|
||||
|
||||
AZStd::string outputName = AZStd::string::format("%s.%s.hlsl", sourceStemName.c_str(), apiName.c_str());
|
||||
AzFramework::StringFunc::Path::Join(request.m_tempDirPath.c_str(), outputName.c_str(), outputName, true);
|
||||
|
||||
auto emitFullOutcome = azslc.EmitFullData(compilerParameters, outputName);
|
||||
if (!emitFullOutcome.IsSuccess())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < emitFullOutcome.GetValue().size(); ++i)
|
||||
{
|
||||
AssetBuilderSDK::JobProduct jobProduct;
|
||||
jobProduct.m_productFileName = emitFullOutcome.GetValue()[i];
|
||||
static const AZ::Uuid AzslOutcomeType = "{6977AEB1-17AD-4992-957B-23BB2E85B18B}";
|
||||
jobProduct.m_productAssetType = AzslOutcomeType;
|
||||
jobProduct.m_productSubID = ShaderBuilderUtility::MakeAzslBuildProductSubId(ShaderBuilderUtility::AzslSubProducts::SubList[i], platformInterface->GetAPIType());
|
||||
jobProduct.m_dependenciesHandled = true;
|
||||
// Note that the output products are not traditional product assets that will be used by the game project.
|
||||
// They are artifacts that are produced once, cached, and used later by other AssetBuilders as a way to centralize build organization.
|
||||
response.m_outputProducts.push_back(AZStd::move(jobProduct));
|
||||
}
|
||||
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
|
||||
const AZStd::sys_time_t endTime = AZStd::GetTimeNowTicks();
|
||||
const AZStd::sys_time_t deltaTime = endTime - startTime;
|
||||
const float elapsedTimeSeconds = (float)(deltaTime) / (float)AZStd::GetTimeTicksPerSecond();
|
||||
|
||||
AZ_TracePrintf(BuilderName, "Finished compiling %s in %.2f seconds\n", request.m_sourceFile.c_str(), elapsedTimeSeconds);
|
||||
|
||||
ShaderBuilderUtility::LogProfilingData(BuilderName, sourceStemName);
|
||||
} // end ProcessJob
|
||||
} // ShaderBuilder
|
||||
} // AZ
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <Atom/RHI.Edit/Utils.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
|
||||
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
|
||||
#include <AzFramework/Asset/AssetSystemBus.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
|
||||
#include <CommonFiles/Preprocessor.h>
|
||||
#include <AzslCompiler.h>
|
||||
#include "AtomShaderConfig.h"
|
||||
#include "ShaderBuilderUtility.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
static constexpr char AzslBuilderName[] = "AzslBuilder";
|
||||
|
||||
//! This builder is the forerunner of the shader build pipeline.
|
||||
//! It will perform the first 3 transformations on shader files (.shader) and AZSL-containing files (.azsl/.azsli/.srgi)
|
||||
//! Which are: [prepend common header -> preprocess -> transpile AZSL to HLSL & reflect all shader program properties of interest into json files]
|
||||
//! The next builders in the chain: SRG/Shader/Variant, will consume the output products of this builder, without having to re-run Azslc nor Mcpp
|
||||
//! Note that the output products are not traditional product assets that will be used by the game project.
|
||||
//! They are artifacts that are produced once, cached, and used later by other AssetBuilders as a way to centralize build organization.
|
||||
class AzslBuilder
|
||||
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
|
||||
{
|
||||
public:
|
||||
|
||||
static constexpr const char* BuilderName = AzslBuilderName;
|
||||
static constexpr char JobKey[] = "AZSL Build";
|
||||
static constexpr char SrgIncludeExtension[] = "srgi";
|
||||
|
||||
// Asset Builder Callback Functions
|
||||
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const;
|
||||
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AssetBuilderSDK::AssetBuilderCommandBus interface
|
||||
void ShutDown() override {};
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static AZ::Uuid GetUUID();
|
||||
};
|
||||
|
||||
//! Helper for dependent builders
|
||||
void AddAzslBuilderJobDependency(AssetBuilderSDK::JobDescriptor& jobDescriptor, const AZStd::string& platformInfoIdentifier, AZStd::string_view apiName, AZStd::string_view fullFilePath);
|
||||
} // ShaderBuilder
|
||||
} // AZ
|
||||
@@ -123,7 +123,7 @@ namespace AZ
|
||||
|
||||
namespace SubProducts = ShaderBuilderUtility::AzslSubProducts;
|
||||
|
||||
Outcome<SubProducts::Paths> AzslCompiler::EmitFullData(const AZStd::string& parameters, const AZStd::string& outputFile /* = ""*/, const char * addSuffix) const
|
||||
Outcome<SubProducts::Paths> AzslCompiler::EmitFullData(const AZStd::string& parameters, const AZStd::string& outputFile /* = ""*/) const
|
||||
{
|
||||
bool success = Compile("--full " + parameters, outputFile);
|
||||
if (!success)
|
||||
@@ -139,16 +139,6 @@ namespace AZ
|
||||
// append .json if it's one of those subs:
|
||||
auto listOfJsons = { SubProducts::ia, SubProducts::om, SubProducts::srg, SubProducts::options, SubProducts::bindingdep };
|
||||
subProductFilePath += AZStd::any_of(AZ_BEGIN_END(listOfJsons), [&](auto v) { return v == subProduct.m_value; }) ? ".json" : "";
|
||||
|
||||
// [GFX TODO] Remove when [ATOM-15472]
|
||||
if (addSuffix)
|
||||
{
|
||||
// Rename the product file.
|
||||
AZStd::string finalSubProductFilePath = AZStd::string::format("%s%s", subProductFilePath.c_str(), addSuffix);
|
||||
AZ::IO::Move(subProductFilePath.c_str(), finalSubProductFilePath.c_str());
|
||||
subProductFilePath = finalSubProductFilePath;
|
||||
}
|
||||
|
||||
productPaths[subProduct.m_value] = subProductFilePath;
|
||||
}
|
||||
productPaths[SubProducts::azslin] = GetInputFilePath(); // post-fixup this one after the loop, because it's not an output of azslc, it's an output of the builder though.
|
||||
|
||||
@@ -38,9 +38,8 @@ namespace AZ
|
||||
//! @param inputFilePath The target input file to compile. Should be a valid AZSL file with no preprocessing directives.
|
||||
AzslCompiler(const AZStd::string& inputFilePath);
|
||||
|
||||
//! [GFX TODO] Remove @addSuffix when [ATOM-15472]
|
||||
//! compile with --full and generate all .json files
|
||||
Outcome<ShaderBuilderUtility::AzslSubProducts::Paths> EmitFullData(const AZStd::string& parameters, const AZStd::string& outputFile = "", const char * addSuffix = nullptr) const;
|
||||
Outcome<ShaderBuilderUtility::AzslSubProducts::Paths> EmitFullData(const AZStd::string& parameters, const AZStd::string& outputFile = "") const;
|
||||
//! compile to HLSL independently
|
||||
bool EmitShader(AZ::IO::GenericStream& outputStream, const AZStd::string& extraCompilerParams) const;
|
||||
//! compile with --ia independently and populate document @output
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
#include <Atom/RPI.Edit/Shader/ShaderSourceData.h>
|
||||
#include <Atom/RPI.Edit/Shader/ShaderVariantListSourceData.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderResourceGroupAsset.h>
|
||||
|
||||
#include <AzToolsFramework/ToolsComponents/ToolsAssetCatalogBus.h>
|
||||
|
||||
@@ -83,42 +82,10 @@ namespace AZ
|
||||
RHI::ShaderPlatformInterfaceRegisterBus::Handler::BusConnect();
|
||||
ShaderPlatformInterfaceRequestBus::Handler::BusConnect();
|
||||
|
||||
// Register AZSL's compilation products Builder
|
||||
AssetBuilderSDK::AssetBuilderDesc azslBuilderDescriptor;
|
||||
azslBuilderDescriptor.m_name = "AZSL Builder";
|
||||
azslBuilderDescriptor.m_version = 8; // ATOM-15276
|
||||
// register all extensions thay may carry azsl code. header. main shader. or SRG
|
||||
azslBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", RPI::ShaderSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
azslBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.azsl", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
azslBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.azsli", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
azslBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", SrgLayoutBuilder::MergedPartialSrgsExtension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
azslBuilderDescriptor.m_busId = AzslBuilder::GetUUID();
|
||||
azslBuilderDescriptor.m_createJobFunction = AZStd::bind(&AzslBuilder::CreateJobs, &m_azslBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
azslBuilderDescriptor.m_processJobFunction = AZStd::bind(&AzslBuilder::ProcessJob, &m_azslBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
|
||||
m_azslBuilder.BusConnect(azslBuilderDescriptor.m_busId);
|
||||
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, azslBuilderDescriptor);
|
||||
|
||||
// Register Shader Resource Group Layout Builder
|
||||
AssetBuilderSDK::AssetBuilderDesc srgLayoutBuilderDescriptor;
|
||||
srgLayoutBuilderDescriptor.m_name = "Shader Resource Group Layout Builder";
|
||||
srgLayoutBuilderDescriptor.m_version = 55; // ATOM-15276
|
||||
|
||||
srgLayoutBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.azsl", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
srgLayoutBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.azsli", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
srgLayoutBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", SrgLayoutBuilder::MergedPartialSrgsExtension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
srgLayoutBuilderDescriptor.m_busId = SrgLayoutBuilder::GetUUID();
|
||||
srgLayoutBuilderDescriptor.m_createJobFunction = AZStd::bind(&SrgLayoutBuilder::CreateJobs, &m_srgLayoutBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
srgLayoutBuilderDescriptor.m_processJobFunction = AZStd::bind(&SrgLayoutBuilder::ProcessJob, &m_srgLayoutBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
|
||||
m_srgLayoutBuilder.BusConnect(srgLayoutBuilderDescriptor.m_busId);
|
||||
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, srgLayoutBuilderDescriptor);
|
||||
m_srgLayoutBuilder.Activate();
|
||||
|
||||
// Register Shader Asset Builder
|
||||
AssetBuilderSDK::AssetBuilderDesc shaderAssetBuilderDescriptor;
|
||||
shaderAssetBuilderDescriptor.m_name = "Shader Asset Builder";
|
||||
shaderAssetBuilderDescriptor.m_version = 100; // ATOM-14298
|
||||
shaderAssetBuilderDescriptor.m_version = 101; // ATOM-15472
|
||||
// .shader file changes trigger rebuilds
|
||||
shaderAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern( AZStd::string::format("*.%s", RPI::ShaderSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
shaderAssetBuilderDescriptor.m_busId = azrtti_typeid<ShaderAssetBuilder>();
|
||||
@@ -133,7 +100,7 @@ namespace AZ
|
||||
shaderVariantAssetBuilderDescriptor.m_name = "Shader Variant Asset Builder";
|
||||
// Both "Shader Variant Asset Builder" and "Shader Asset Builder" produce ShaderVariantAsset products. If you update
|
||||
// ShaderVariantAsset you will need to update BOTH version numbers, not just "Shader Variant Asset Builder".
|
||||
shaderVariantAssetBuilderDescriptor.m_version = 21; // ATOM-14298
|
||||
shaderVariantAssetBuilderDescriptor.m_version = 22; // ATOM-15472
|
||||
shaderVariantAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", RPI::ShaderVariantListSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
shaderVariantAssetBuilderDescriptor.m_busId = azrtti_typeid<ShaderVariantAssetBuilder>();
|
||||
shaderVariantAssetBuilderDescriptor.m_createJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::CreateJobs, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
@@ -145,7 +112,7 @@ namespace AZ
|
||||
// Register Precompiled Shader Builder
|
||||
AssetBuilderSDK::AssetBuilderDesc precompiledShaderBuilderDescriptor;
|
||||
precompiledShaderBuilderDescriptor.m_name = "Precompiled Shader Builder";
|
||||
precompiledShaderBuilderDescriptor.m_version = 8; // ATOM-15276
|
||||
precompiledShaderBuilderDescriptor.m_version = 9; // ATOM-15472
|
||||
precompiledShaderBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", AZ::PrecompiledShaderBuilder::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
precompiledShaderBuilderDescriptor.m_busId = azrtti_typeid<PrecompiledShaderBuilder>();
|
||||
precompiledShaderBuilderDescriptor.m_createJobFunction = AZStd::bind(&PrecompiledShaderBuilder::CreateJobs, &m_precompiledShaderBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
@@ -153,53 +120,13 @@ namespace AZ
|
||||
|
||||
m_precompiledShaderBuilder.BusConnect(precompiledShaderBuilderDescriptor.m_busId);
|
||||
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, precompiledShaderBuilderDescriptor);
|
||||
|
||||
// Register Shader Asset Builder 2
|
||||
AssetBuilderSDK::AssetBuilderDesc shaderAssetBuilder2Descriptor;
|
||||
shaderAssetBuilder2Descriptor.m_name = "Shader Asset Builder 2";
|
||||
shaderAssetBuilder2Descriptor.m_version = 1; // ATOM-15276
|
||||
// .shader2 file changes trigger rebuilds
|
||||
shaderAssetBuilder2Descriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(
|
||||
AZStd::string::format("*.%s", RPI::ShaderSourceData::Extension2),
|
||||
AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
shaderAssetBuilder2Descriptor.m_busId = azrtti_typeid<ShaderAssetBuilder2>();
|
||||
shaderAssetBuilder2Descriptor.m_createJobFunction =
|
||||
AZStd::bind(&ShaderAssetBuilder2::CreateJobs, &m_shaderAssetBuilder2, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
shaderAssetBuilder2Descriptor.m_processJobFunction =
|
||||
AZStd::bind(&ShaderAssetBuilder2::ProcessJob, &m_shaderAssetBuilder2, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
|
||||
m_shaderAssetBuilder2.BusConnect(shaderAssetBuilder2Descriptor.m_busId);
|
||||
AssetBuilderSDK::AssetBuilderBus::Broadcast(
|
||||
&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, shaderAssetBuilder2Descriptor);
|
||||
|
||||
// Register Shader Variant Asset Builder 2
|
||||
AssetBuilderSDK::AssetBuilderDesc shaderVariantAssetBuilder2Descriptor;
|
||||
shaderVariantAssetBuilder2Descriptor.m_name = "Shader Variant Asset Builder 2";
|
||||
// Both "Shader Variant Asset Builder" and "Shader Asset Builder" produce ShaderVariantAsset products. If you update
|
||||
// ShaderVariantAsset you will need to update BOTH version numbers, not just "Shader Variant Asset Builder".
|
||||
shaderVariantAssetBuilder2Descriptor.m_version = 1; // ATOM-15276
|
||||
shaderVariantAssetBuilder2Descriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(
|
||||
AZStd::string::format("*.%s", RPI::ShaderVariantListSourceData::Extension2),
|
||||
AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
shaderVariantAssetBuilder2Descriptor.m_busId = azrtti_typeid<ShaderVariantAssetBuilder2>();
|
||||
shaderVariantAssetBuilder2Descriptor.m_createJobFunction = AZStd::bind(
|
||||
&ShaderVariantAssetBuilder2::CreateJobs, &m_shaderVariantAssetBuilder2, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
shaderVariantAssetBuilder2Descriptor.m_processJobFunction = AZStd::bind(
|
||||
&ShaderVariantAssetBuilder2::ProcessJob, &m_shaderVariantAssetBuilder2, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
|
||||
m_shaderVariantAssetBuilder2.BusConnect(shaderVariantAssetBuilder2Descriptor.m_busId);
|
||||
AssetBuilderSDK::AssetBuilderBus::Broadcast(
|
||||
&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, shaderVariantAssetBuilder2Descriptor);
|
||||
}
|
||||
|
||||
void AzslShaderBuilderSystemComponent::Deactivate()
|
||||
{
|
||||
m_shaderAssetBuilder.BusDisconnect();
|
||||
m_srgLayoutBuilder.BusDisconnect();
|
||||
m_shaderVariantAssetBuilder.BusDisconnect();
|
||||
m_precompiledShaderBuilder.BusDisconnect();
|
||||
m_shaderAssetBuilder2.BusDisconnect();
|
||||
m_shaderVariantAssetBuilder2.BusDisconnect();
|
||||
|
||||
RHI::ShaderPlatformInterfaceRegisterBus::Handler::BusDisconnect();
|
||||
ShaderPlatformInterfaceRequestBus::Handler::BusDisconnect();
|
||||
|
||||
@@ -18,14 +18,10 @@
|
||||
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
|
||||
#include "AzslBuilder.h"
|
||||
#include "SrgLayoutBuilder.h"
|
||||
#include "ShaderAssetBuilder.h"
|
||||
#include "ShaderVariantAssetBuilder.h"
|
||||
#include "PrecompiledShaderBuilder.h"
|
||||
#include "ShaderPlatformInterfaceRequest.h"
|
||||
#include "ShaderAssetBuilder2.h"
|
||||
#include "ShaderVariantAssetBuilder2.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -68,13 +64,9 @@ namespace AZ
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> GetShaderPlatformInterface(const AssetBuilderSDK::PlatformInfo& platformInfo) override;
|
||||
|
||||
private:
|
||||
AzslBuilder m_azslBuilder;
|
||||
SrgLayoutBuilder m_srgLayoutBuilder;
|
||||
ShaderAssetBuilder m_shaderAssetBuilder;
|
||||
ShaderVariantAssetBuilder m_shaderVariantAssetBuilder;
|
||||
PrecompiledShaderBuilder m_precompiledShaderBuilder;
|
||||
ShaderAssetBuilder2 m_shaderAssetBuilder2;
|
||||
ShaderVariantAssetBuilder2 m_shaderVariantAssetBuilder2;
|
||||
|
||||
/// Contains the ShaderPlatformInterface for all registered RHIs
|
||||
AZStd::unordered_map<RHI::APIType, RHI::ShaderPlatformInterface*> m_shaderPlatformInterfaces;
|
||||
|
||||
@@ -72,23 +72,6 @@ namespace AZ
|
||||
{
|
||||
AZStd::vector<AssetBuilderSDK::JobDependency> jobDependencyList;
|
||||
|
||||
// setup dependencies on the azsrg asset file names
|
||||
for (const auto& srgFileName : precompiledShaderAsset.m_srgAssetFileNames)
|
||||
{
|
||||
AZStd::string srgAssetPath = RPI::AssetUtils::ResolvePathReference(request.m_sourceFile.c_str(), srgFileName);
|
||||
|
||||
AssetBuilderSDK::SourceFileDependency sourceDependency;
|
||||
sourceDependency.m_sourceFileDependencyPath = srgAssetPath;
|
||||
response.m_sourceFileDependencyList.push_back(sourceDependency);
|
||||
|
||||
AssetBuilderSDK::JobDependency jobDependency;
|
||||
jobDependency.m_jobKey = "azsrg";
|
||||
jobDependency.m_platformIdentifier = platformInfo.m_identifier;
|
||||
jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
jobDependency.m_sourceFile = sourceDependency;
|
||||
jobDependencyList.push_back(jobDependency);
|
||||
}
|
||||
|
||||
// setup dependencies on the root azshadervariant asset file names
|
||||
for (const auto& rootShaderVariantAsset : precompiledShaderAsset.m_rootShaderVariantAssets)
|
||||
{
|
||||
@@ -158,25 +141,6 @@ namespace AZ
|
||||
|
||||
AssetBuilderSDK::JobProduct jobProduct;
|
||||
|
||||
// load Srg product assets
|
||||
// these are the dependency Srg asset products that were processed prior to running this job
|
||||
RPI::ShaderAssetCreator::ShaderResourceGroupAssets srgProductAssets;
|
||||
for (const auto& srgAssetFileName : precompiledShaderAsset.m_srgAssetFileNames)
|
||||
{
|
||||
auto assetOutcome = RPI::AssetUtils::LoadAsset<RPI::ShaderResourceGroupAsset>(request.m_fullPath, srgAssetFileName, 0);
|
||||
if (!assetOutcome)
|
||||
{
|
||||
AZ_Error(PrecompiledShaderBuilderName, false, "Failed to retrieve Srg asset for file [%s]", srgAssetFileName.c_str());
|
||||
return;
|
||||
}
|
||||
srgProductAssets.push_back(assetOutcome.GetValue());
|
||||
|
||||
AssetBuilderSDK::ProductDependency productDependency;
|
||||
productDependency.m_dependencyId = assetOutcome.GetValue().GetId();
|
||||
productDependency.m_flags = AZ::Data::ProductDependencyInfo::CreateFlags(AZ::Data::AssetLoadBehavior::PreLoad);
|
||||
jobProduct.m_dependencies.push_back(productDependency);
|
||||
}
|
||||
|
||||
// load the variant product assets
|
||||
// these are the dependency root variant asset products that were processed prior to running this job
|
||||
RPI::ShaderAssetCreator::ShaderRootVariantAssets rootVariantProductAssets;
|
||||
@@ -202,8 +166,7 @@ namespace AZ
|
||||
// Note that the Srg and Variant assets do not have embedded asset references and are processed with the RC Copy functionality
|
||||
RPI::ShaderAssetCreator shaderAssetCreator;
|
||||
shaderAssetCreator.Clone(Uuid::CreateRandom(),
|
||||
shaderAsset,
|
||||
srgProductAssets,
|
||||
*shaderAsset,
|
||||
rootVariantProductAssets);
|
||||
|
||||
Data::Asset<RPI::ShaderAsset> outputShaderAsset;
|
||||
|
||||
@@ -47,8 +47,7 @@
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
|
||||
#include "AzslBuilder.h"
|
||||
#include "SrgLayoutBuilder.h"
|
||||
#include "AzslCompiler.h"
|
||||
#include "ShaderVariantAssetBuilder.h"
|
||||
#include "ShaderBuilderUtility.h"
|
||||
#include "ShaderPlatformInterfaceRequest.h"
|
||||
@@ -63,19 +62,6 @@ namespace AZ
|
||||
static constexpr char ShaderAssetBuilderName[] = "ShaderAssetBuilder";
|
||||
static constexpr uint32_t ShaderAssetBuildTimestampParam = 0;
|
||||
|
||||
static void AddSrgLayoutJobDependency(AssetBuilderSDK::JobDescriptor& jobDescriptor, const AssetBuilderSDK::PlatformInfo& platformInfo, AZStd::string_view fullFilePath)
|
||||
{
|
||||
AssetBuilderSDK::SourceFileDependency fileDependency;
|
||||
fileDependency.m_sourceFileDependencyPath = fullFilePath;
|
||||
|
||||
AssetBuilderSDK::JobDependency srgLayoutJobDependency;
|
||||
srgLayoutJobDependency.m_jobKey = SrgLayoutBuilder::SrgLayoutBuilderJobKey;
|
||||
srgLayoutJobDependency.m_platformIdentifier = platformInfo.m_identifier;
|
||||
srgLayoutJobDependency.m_sourceFile = fileDependency;
|
||||
srgLayoutJobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
jobDescriptor.m_jobDependencyList.emplace_back(srgLayoutJobDependency);
|
||||
}
|
||||
|
||||
void ShaderAssetBuilder::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const
|
||||
{
|
||||
AZStd::string fullPath;
|
||||
@@ -90,20 +76,58 @@ namespace AZ
|
||||
// the PC's ShaderAsset).
|
||||
AZStd::sys_time_t shaderAssetBuildTimestamp = AZStd::GetTimeNowMicroSecond();
|
||||
|
||||
// *** block (remove all this when [ATOM-4225] addressed)
|
||||
AZStd::string azslFullPath;
|
||||
// Need to get the name of the azsl file from the .shader source asset, to be able to declare a dependency to SRG Layout Job.
|
||||
// and the macro options to preprocess.
|
||||
auto descriptorParseOutput = ShaderBuilderUtility::LoadShaderDataJson(fullPath);
|
||||
if (!descriptorParseOutput.IsSuccess())
|
||||
auto descriptorParseOutcome = ShaderBuilderUtility::LoadShaderDataJson(fullPath);
|
||||
if (!descriptorParseOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilderName, false, "Failed to parse Shader Descriptor JSON: %s", descriptorParseOutput.GetError().c_str());
|
||||
AZ_Error(
|
||||
ShaderAssetBuilderName, false, "Failed to parse Shader Descriptor JSON: %s",
|
||||
descriptorParseOutcome.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
ShaderBuilderUtility::GetAbsolutePathToAzslFile(fullPath, descriptorParseOutput.GetValue().m_source, azslFullPath);
|
||||
AZ_Warning(ShaderAssetBuilderName, IO::FileIOBase::GetInstance()->Exists(azslFullPath.c_str()), "Shader program listed as the source entry does not exist: %s.", azslFullPath.c_str());
|
||||
|
||||
RPI::ShaderSourceData shaderSourceData = descriptorParseOutcome.TakeValue();
|
||||
|
||||
AZStd::string azslFullPath;
|
||||
ShaderBuilderUtility::GetAbsolutePathToAzslFile(fullPath, shaderSourceData.m_source, azslFullPath);
|
||||
if (!IO::FileIOBase::GetInstance()->Exists(azslFullPath.c_str()))
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilderName, false, "Shader program listed as the source entry does not exist: %s.", azslFullPath.c_str());
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
GlobalBuildOptions buildOptions = ReadBuildOptions(ShaderAssetBuilderName);
|
||||
// *** end block (remove when [Atom-4225])
|
||||
|
||||
// [GFX TODO] [ATOM-14966] In principle, based on macro definitions, included files can change per supervariant.
|
||||
// So, the list of source asset dependencies must be collected by running MCPP on each supervariant.
|
||||
// For now, we will run MCPP only once because CreateJobs() should be as light as possible.
|
||||
//
|
||||
// Regardless of the PlatformInfo and enabled ShaderPlatformInterfaces, the azsl file will be preprocessed
|
||||
// with the sole purpose of extracting all included files. For each included file a SourceDependency will be declared.
|
||||
PreprocessorData output;
|
||||
buildOptions.m_compilerArguments.Merge(shaderSourceData.m_compiler);
|
||||
PreprocessFile(azslFullPath, output, buildOptions.m_preprocessorSettings, true, true);
|
||||
for (auto includePath : output.includedPaths)
|
||||
{
|
||||
// m_sourceFileDependencyList does not support paths with "." or ".." for relative lookup, but the preprocessor
|
||||
// may produce path strings like "C:/a/b/c/../../d/file.azsli" so we have to normalize
|
||||
AzFramework::StringFunc::Path::Normalize(includePath);
|
||||
|
||||
AssetBuilderSDK::SourceFileDependency includeFileDependency;
|
||||
includeFileDependency.m_sourceFileDependencyPath = includePath;
|
||||
response.m_sourceFileDependencyList.emplace_back(includeFileDependency);
|
||||
}
|
||||
|
||||
{
|
||||
// Add the AZSL as source dependency
|
||||
AssetBuilderSDK::SourceFileDependency azslFileDependency;
|
||||
azslFileDependency.m_sourceFileDependencyPath = azslFullPath;
|
||||
response.m_sourceFileDependencyList.emplace_back(azslFileDependency);
|
||||
}
|
||||
|
||||
for (const AssetBuilderSDK::PlatformInfo& platformInfo : request.m_enabledPlatforms)
|
||||
{
|
||||
@@ -111,6 +135,10 @@ namespace AZ
|
||||
|
||||
// Get the platform interfaces to be able to access the prepend file
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces = ShaderBuilderUtility::DiscoverValidShaderPlatformInterfaces(platformInfo);
|
||||
if (platformInterfaces.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AssetBuilderSDK::JobDescriptor jobDescriptor;
|
||||
jobDescriptor.m_priority = 2;
|
||||
@@ -118,46 +146,6 @@ namespace AZ
|
||||
jobDescriptor.m_critical = true;
|
||||
jobDescriptor.m_jobKey = ShaderAssetBuilderJobKey;
|
||||
jobDescriptor.SetPlatformIdentifier(platformInfo.m_identifier.c_str());
|
||||
|
||||
// queue up AzslBuilder dependencies:
|
||||
for (RHI::ShaderPlatformInterface* shaderPlatformInterface : platformInterfaces)
|
||||
{
|
||||
AddAzslBuilderJobDependency(jobDescriptor, platformInfo.m_identifier, shaderPlatformInterface->GetAPIName().GetCStr(), fullPath);
|
||||
|
||||
{// *** block (remove all this when [ATOM-4225] addressed)
|
||||
// this is the same code as in AzslBuilder.cpp's CreateJobs. This is not supposed to be repeated here (temporary hack), so not factorized. refer to AzslBuilder.cpp for code comments
|
||||
AZStd::string prependedAzslSourceCode;
|
||||
RHI::PrependArguments args;
|
||||
args.m_sourceFile = fullPath.c_str();
|
||||
args.m_prependFile = shaderPlatformInterface->GetAzslHeader(platformInfo);
|
||||
args.m_addSuffixToFileName = shaderPlatformInterface->GetAPIName().GetCStr();
|
||||
args.m_destinationStringOpt = &prependedAzslSourceCode;
|
||||
|
||||
if (RHI::PrependFile(args) == fullPath)
|
||||
{
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Failed;
|
||||
return;
|
||||
}
|
||||
AZStd::string originalLocation;
|
||||
AzFramework::StringFunc::Path::GetFullPath(fullPath.c_str(), originalLocation);
|
||||
AZStd::string prependedPath = ShaderBuilderUtility::DumpAzslPrependedCode(
|
||||
ShaderAssetBuilderName, prependedAzslSourceCode, originalLocation, ShaderBuilderUtility::ExtractStemName(fullPath.c_str()), shaderPlatformInterface->GetAPIName().GetStringView());
|
||||
PreprocessorData output;
|
||||
buildOptions.m_compilerArguments.Merge(descriptorParseOutput.GetValue().m_compiler);
|
||||
PreprocessFile(azslFullPath, output, buildOptions.m_preprocessorSettings, true, true);
|
||||
// srg layout builder job dependency on the azsl file itself.
|
||||
// If there's a ShaderResourceGroup defined in this azsl file
|
||||
// then its azsrg asset must be ready before We compile it. The azsrg requirement
|
||||
// is only for diagnostics, because a preprocessed (flattened) azsl file contains
|
||||
// all the SRG data it needs.
|
||||
AddSrgLayoutJobDependency(jobDescriptor, platformInfo, azslFullPath);
|
||||
for (auto included : output.includedPaths)
|
||||
{
|
||||
AddSrgLayoutJobDependency(jobDescriptor, platformInfo, included.c_str());
|
||||
}
|
||||
AZ::IO::SystemFile::Delete(prependedPath.c_str()); // don't let that intermediate file dirty a folder under source version control.
|
||||
}// *** end block remove when [Atom-4225]
|
||||
}
|
||||
jobDescriptor.m_jobParameters.emplace(ShaderAssetBuildTimestampParam, AZStd::to_string(shaderAssetBuildTimestamp));
|
||||
|
||||
response.m_createJobOutputs.push_back(jobDescriptor);
|
||||
@@ -166,141 +154,55 @@ namespace AZ
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
|
||||
}
|
||||
|
||||
static AssetBuilderSDK::ProcessJobResultCode CompileForAPI(
|
||||
const ShaderBuilderUtility::AzslSubProducts::Paths& pathOfProductFiles,
|
||||
RPI::ShaderAssetCreator& shaderAssetCreator,
|
||||
RHI::ShaderPlatformInterface* shaderPlatformInterface,
|
||||
AssetBuilderSDK::ProcessJobResponse& response,
|
||||
AzslData& azslData,
|
||||
const RHI::ShaderCompilerArguments& shaderCompilerArguments,
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout,
|
||||
const RPI::ShaderSourceData& shaderSourceDataDescriptor,
|
||||
AZStd::sys_time_t shaderAssetBuildTimestamp,
|
||||
const ShaderResourceGroupAssets& srgAssets,
|
||||
BindingDependencies& bindingDependencies,
|
||||
const RootConstantData& rootConstantData,
|
||||
const AssetBuilderSDK::ProcessJobRequest& request)
|
||||
static bool SerializeOutShaderAsset(Data::Asset<RPI::ShaderAsset> shaderAsset,
|
||||
const AZStd::string& tempDirPath,
|
||||
AssetBuilderSDK::ProcessJobResponse& response)
|
||||
{
|
||||
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
||||
|
||||
const AZStd::string& tempDirPath = request.m_tempDirPath;
|
||||
|
||||
// discover entry points
|
||||
MapOfStringToStageType shaderEntryPoints;
|
||||
if (shaderSourceDataDescriptor.m_programSettings.m_entryPoints.empty())
|
||||
AZStd::string shaderAssetFileName = AZStd::string::format("%s.%s", shaderAsset->GetName().GetCStr(), RPI::ShaderAsset::Extension);
|
||||
AZStd::string shaderAssetOutputPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(tempDirPath.data(), shaderAssetFileName.data(), shaderAssetOutputPath, true);
|
||||
|
||||
if (!Utils::SaveObjectToFile(shaderAssetOutputPath, DataStream::ST_BINARY, shaderAsset.Get()))
|
||||
{
|
||||
AZ_TracePrintf(ShaderAssetBuilderName, "ProgramSettings do not specify entry points, will use GetDefaultEntryPointsFromShader()\n");
|
||||
ShaderBuilderUtility::GetDefaultEntryPointsFromFunctionDataList(azslData.m_functions, shaderEntryPoints);
|
||||
AZ_Error(ShaderAssetBuilderName, false, "Failed to output Shader Descriptor");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
||||
AssetBuilderSDK::JobProduct shaderJobProduct;
|
||||
if (!AssetBuilderSDK::OutputObject(shaderAsset.Get(), shaderAssetOutputPath, azrtti_typeid<RPI::ShaderAsset>(),
|
||||
aznumeric_cast<uint32_t>(RPI::ShaderAssetSubId::ShaderAsset), shaderJobProduct))
|
||||
{
|
||||
for (auto& iter : shaderSourceDataDescriptor.m_programSettings.m_entryPoints)
|
||||
{
|
||||
shaderEntryPoints[iter.m_name] = iter.m_type;
|
||||
}
|
||||
AZ_Error(ShaderAssetBuilderName, false, "Failed to output product dependencies.");
|
||||
return false;
|
||||
}
|
||||
response.m_outputProducts.push_back(AZStd::move(shaderJobProduct));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if we were canceled before we do any heavy processing of
|
||||
// the shader data (compiling the shader kernels, processing SRG
|
||||
// and pipeline layout data, etc.).
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
return AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
}
|
||||
|
||||
// Signal the begin of shader data for an RHI API.
|
||||
shaderAssetCreator.BeginAPI(shaderPlatformInterface->GetAPIType());
|
||||
RHI::Ptr<RHI::PipelineLayoutDescriptor> pipelineLayoutDescriptor = ShaderBuilderUtility::BuildPipelineLayoutDescriptorForApi(
|
||||
ShaderAssetBuilderName, shaderPlatformInterface, bindingDependencies, srgAssets, shaderEntryPoints, shaderCompilerArguments, &rootConstantData);
|
||||
if (!pipelineLayoutDescriptor)
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilderName, false, "Failed to build pipeline layout descriptor for api=[%s]",
|
||||
shaderPlatformInterface->GetAPIName().GetCStr());
|
||||
AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
for (const auto& srgAsset : srgAssets)
|
||||
{
|
||||
shaderAssetCreator.AddShaderResourceGroupAsset(srgAsset);
|
||||
}
|
||||
shaderAssetCreator.SetPipelineLayout(pipelineLayoutDescriptor);
|
||||
|
||||
// Generate shader source.
|
||||
AZStd::string hlslSourcePath = pathOfProductFiles[ShaderBuilderUtility::AzslSubProducts::hlsl];
|
||||
Outcome<AZStd::string, AZStd::string> hlslSourceContent = Utils::ReadFile(hlslSourcePath);
|
||||
if (!hlslSourceContent.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilderName, false, "Failed to obtain shader source from %s. [%s]", hlslSourcePath.c_str(), hlslSourceContent.TakeError().c_str());
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// The root ShaderVariantAsset needs to be created with the known uuid of the source .shader asset because
|
||||
// the ShaderAsset owns a Data::Asset<> reference that gets serialized. It must have the correct uuid
|
||||
// so the root ShaderVariantAsset is found when the ShaderAsset is deserialized.
|
||||
AZStd::string fullSourcePath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.c_str(), request.m_sourceFile.c_str(), fullSourcePath, true);
|
||||
const uint32_t productSubID = RPI::ShaderAsset::MakeAssetProductSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(),
|
||||
aznumeric_cast<uint32_t>(RPI::ShaderAssetSubId::RootShaderVariantAsset));
|
||||
auto assetIdOutcome = RPI::AssetUtils::MakeAssetId(fullSourcePath, productSubID);
|
||||
AZ_Assert(assetIdOutcome.IsSuccess(), "Failed to get AssetId from shader %s", fullSourcePath.c_str());
|
||||
const Data::AssetId variantAssetId = assetIdOutcome.TakeValue();
|
||||
|
||||
// We always include the root shader variant (all options are unspecified) in the ShaderAsset itself.
|
||||
ShaderVariantCreationContext variantCreationContext = { variantAssetId, hlslSourcePath, hlslSourceContent.GetValue(), shaderSourceDataDescriptor,
|
||||
tempDirPath, request.m_platformInfo, *shaderOptionGroupLayout, shaderEntryPoints, shaderAssetBuildTimestamp };
|
||||
AZ::Outcome<Data::Asset<RPI::ShaderVariantAsset>, AZStd::string> outcomeForShaderVariantAsset = ShaderVariantAssetBuilder::CreateShaderVariantAssetForAPI(
|
||||
RPI::ShaderVariantListSourceData::VariantInfo(),
|
||||
variantCreationContext,
|
||||
*shaderPlatformInterface,
|
||||
azslData,
|
||||
shaderCompilerArguments,
|
||||
pathOfProductFiles[ShaderBuilderUtility::AzslSubProducts::om],
|
||||
pathOfProductFiles[ShaderBuilderUtility::AzslSubProducts::ia]);
|
||||
if (!outcomeForShaderVariantAsset.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilderName, false, "Failed to serialize out the root shader variant for API [%s]: %s"
|
||||
, shaderPlatformInterface->GetAPIName().GetCStr(), outcomeForShaderVariantAsset.GetError().c_str());
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// Time to save, for the given rhi::api, the root shader variant as an asset.
|
||||
Data::Asset<RPI::ShaderVariantAsset> shaderVariantAsset = outcomeForShaderVariantAsset.TakeValue();
|
||||
|
||||
AssetBuilderSDK::JobProduct variantAssetProduct;
|
||||
if (!ShaderVariantAssetBuilder::SerializeOutShaderVariantAsset(shaderVariantAsset, fullSourcePath, tempDirPath,
|
||||
*shaderPlatformInterface, productSubID, variantAssetProduct))
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilderName, false, "Failed to serialize out the root shader variant for API [%s]"
|
||||
, shaderPlatformInterface->GetAPIName().GetCStr());
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
response.m_outputProducts.push_back(variantAssetProduct);
|
||||
|
||||
// add byproducts as job output products:
|
||||
if (variantCreationContext.m_outputByproducts)
|
||||
{
|
||||
uint32_t subProductType = aznumeric_cast<uint32_t>(RPI::ShaderAssetSubId::GeneratedHlslSource) + 1;
|
||||
for (const AZStd::string& byproduct : variantCreationContext.m_outputByproducts->m_intermediatePaths)
|
||||
{
|
||||
AssetBuilderSDK::JobProduct jobProduct;
|
||||
jobProduct.m_productFileName = byproduct;
|
||||
jobProduct.m_productAssetType = Uuid::CreateName("DebugInfoByProduct-PdbOrDxilTxt");
|
||||
jobProduct.m_productSubID = RPI::ShaderAsset::MakeAssetProductSubId(shaderPlatformInterface->GetAPIUniqueIndex(), subProductType++);
|
||||
response.m_outputProducts.push_back(AZStd::move(jobProduct));
|
||||
}
|
||||
}
|
||||
|
||||
shaderAssetCreator.SetRootShaderVariantAsset(shaderVariantAsset);
|
||||
|
||||
// Populate the shader asset with all entry stage attributes
|
||||
static AZ::Outcome<RHI::ShaderStageAttributeMapList, AZStd::string> BuildAttributesMap(
|
||||
const RHI::ShaderPlatformInterface* shaderPlatformInterface,
|
||||
const AzslData& azslData,
|
||||
const MapOfStringToStageType& shaderEntryPoints,
|
||||
bool& hasRasterProgram)
|
||||
{
|
||||
hasRasterProgram = false;
|
||||
bool hasComputeProgram = false;
|
||||
bool hasRayTracingProgram = false;
|
||||
RHI::ShaderStageAttributeMapList attributeMaps;
|
||||
attributeMaps.resize(RHI::ShaderStageCount);
|
||||
for (const auto& shaderEntry : shaderSourceDataDescriptor.m_programSettings.m_entryPoints)
|
||||
for (const auto& shaderEntryPoint : shaderEntryPoints)
|
||||
{
|
||||
auto findId = AZStd::find_if(AZ_BEGIN_END(azslData.m_functions), [&shaderEntry](const auto& func)
|
||||
{
|
||||
return func.m_name == shaderEntry.m_name;
|
||||
});
|
||||
auto shaderEntryName = shaderEntryPoint.first;
|
||||
auto shaderStageType = shaderEntryPoint.second;
|
||||
auto assetBuilderShaderType = ShaderBuilderUtility::ToAssetBuilderShaderType(shaderStageType);
|
||||
hasRasterProgram |= shaderPlatformInterface->IsShaderStageForRaster(assetBuilderShaderType);
|
||||
hasComputeProgram |= shaderPlatformInterface->IsShaderStageForCompute(assetBuilderShaderType);
|
||||
hasRayTracingProgram |= shaderPlatformInterface->IsShaderStageForRayTracing(assetBuilderShaderType);
|
||||
|
||||
auto findId = AZStd::find_if(AZ_BEGIN_END(azslData.m_functions), [&shaderEntryPoint](const auto& func) {
|
||||
return func.m_name == shaderEntryPoint.first;
|
||||
});
|
||||
|
||||
if (findId == azslData.m_functions.end())
|
||||
{
|
||||
@@ -309,7 +211,7 @@ namespace AZ
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto shaderStage = ToRHIShaderStage(ShaderBuilderUtility::ToAssetBuilderShaderType(shaderEntry.m_type));
|
||||
const auto shaderStage = ToRHIShaderStage(assetBuilderShaderType);
|
||||
for (const auto& attr : findId->attributesList)
|
||||
{
|
||||
// Some stages like RHI::ShaderStage::Tessellation are compound and consist of two or more shader entries
|
||||
@@ -320,221 +222,461 @@ namespace AZ
|
||||
attributeMaps[stageIndex][attributeName] = args;
|
||||
}
|
||||
}
|
||||
shaderAssetCreator.SetShaderStageAttributeMapList(attributeMaps);
|
||||
|
||||
shaderAssetCreator.EndAPI();
|
||||
|
||||
return AssetBuilderSDK::ProcessJobResult_Success;
|
||||
}
|
||||
|
||||
static bool SerializeOutShaderAsset(Data::Asset<RPI::ShaderAsset> shaderAsset,
|
||||
const AZStd::string& tempDirPath,
|
||||
AssetBuilderSDK::ProcessJobResponse& response)
|
||||
{
|
||||
AZStd::string shaderAssetFileName = AZStd::string::format("%s.%s", shaderAsset->GetName().GetCStr(), RPI::ShaderAsset::Extension);
|
||||
AZStd::string shaderAssetOutputPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(tempDirPath.data(), shaderAssetFileName.data(), shaderAssetOutputPath, true);
|
||||
|
||||
if (!Utils::SaveObjectToFile(shaderAssetOutputPath, DataStream::ST_BINARY, shaderAsset.Get()))
|
||||
if (hasRasterProgram && hasComputeProgram)
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilderName, false, "Failed to output Shader Descriptor");
|
||||
return false;
|
||||
return AZ::Failure(AZStd::string(" Shader asset descriptor defines both a raster entry point and a compute entry point."));
|
||||
}
|
||||
|
||||
AssetBuilderSDK::JobProduct shaderJobProduct;
|
||||
if (!AssetBuilderSDK::OutputObject(shaderAsset.Get(), shaderAssetOutputPath, azrtti_typeid<RPI::ShaderAsset>(),
|
||||
aznumeric_cast<uint32_t>(RPI::ShaderAssetSubId::ShaderAsset), shaderJobProduct))
|
||||
if (!hasRasterProgram && !hasComputeProgram && !hasRayTracingProgram)
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilderName, false, "Failed to output product dependencies.");
|
||||
return false;
|
||||
AZStd::string entryPointNames = ShaderBuilderUtility::GetAcceptableDefaultEntryPointNames(azslData);
|
||||
return AZ::Failure(
|
||||
AZStd::string::format( "Shader asset descriptor has a program variant that does not define any entry points. Either declare entry "
|
||||
"points in the .shader file, or use one of the available default names (not case-sensitive): [%s]",
|
||||
entryPointNames.c_str()));
|
||||
}
|
||||
response.m_outputProducts.push_back(AZStd::move(shaderJobProduct));
|
||||
|
||||
return true;
|
||||
return AZ::Success(attributeMaps);
|
||||
}
|
||||
|
||||
void ShaderAssetBuilder::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const
|
||||
{
|
||||
const AZStd::sys_time_t startTime = AZStd::GetTimeNowTicks();
|
||||
AZStd::string fullSourcePath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.c_str(), request.m_sourceFile.c_str(), fullSourcePath, true);
|
||||
AZStd::string shaderFullPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.c_str(), request.m_sourceFile.c_str(), shaderFullPath, true);
|
||||
// Save .shader file name (no extension and no parent directory path)
|
||||
AZStd::string shaderFileName;
|
||||
AzFramework::StringFunc::Path::GetFileName(request.m_sourceFile.c_str(), shaderFileName);
|
||||
|
||||
RPI::ShaderAssetCreator shaderAssetCreator;
|
||||
shaderAssetCreator.Begin(Uuid::CreateRandom());
|
||||
// No error checking because the same calls were already executed during CreateJobs()
|
||||
auto descriptorParseOutcome = ShaderBuilderUtility::LoadShaderDataJson(shaderFullPath);
|
||||
RPI::ShaderSourceData shaderSourceData = descriptorParseOutcome.TakeValue();
|
||||
AZStd::string azslFullPath;
|
||||
ShaderBuilderUtility::GetAbsolutePathToAzslFile(shaderFullPath, shaderSourceData.m_source, azslFullPath);
|
||||
AZ_TracePrintf(ShaderAssetBuilderName, "Original AZSL File: %s \n", azslFullPath.c_str());
|
||||
|
||||
// read .shader -> access azsl path -> make absolute
|
||||
RPI::ShaderSourceData shaderAssetSource;
|
||||
AZStd::shared_ptr<ShaderFiles> inputFiles = ShaderBuilderUtility::PrepareSourceInput(ShaderAssetBuilderName, fullSourcePath, shaderAssetSource);
|
||||
if (!inputFiles)
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
GlobalBuildOptions buildOptions = ReadBuildOptions(ShaderAssetBuilderName);
|
||||
|
||||
// Save .shader file name
|
||||
AzFramework::StringFunc::Path::GetFileName(request.m_sourceFile.data(), inputFiles->m_shaderFileName);
|
||||
// The directory where the Azsl file was found must be added to the list of include paths
|
||||
AZStd::string azslFolderPath;
|
||||
AzFramework::StringFunc::Path::GetFolderPath(azslFullPath.c_str(), azslFolderPath);
|
||||
GlobalBuildOptions buildOptions = ReadBuildOptions(ShaderAssetBuilderName, azslFolderPath.c_str());
|
||||
|
||||
// Request the list of valid shader platform interfaces for the target platform.
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces;
|
||||
ShaderPlatformInterfaceRequestBus::BroadcastResult(platformInterfaces, &ShaderPlatformInterfaceRequest::GetShaderPlatformInterface, request.m_platformInfo);
|
||||
// Generate shaders for each of those ShaderPlatformInterfaces.
|
||||
uint32_t countOfCompiledPlatformInterfaces = 0;
|
||||
for (RHI::ShaderPlatformInterface* shaderPlatformInterface : platformInterfaces)
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces = ShaderBuilderUtility::DiscoverEnabledShaderPlatformInterfaces(
|
||||
request.m_platformInfo, shaderSourceData);
|
||||
if (platformInterfaces.empty())
|
||||
{
|
||||
ShaderResourceGroupAssets srgAssets;
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
|
||||
|
||||
if (!shaderPlatformInterface)
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilderName, false, "ShaderPlatformInterface for [%s] is not registered, can't compile [%s]", request.m_platformInfo.m_identifier.c_str(), request.m_sourceFile.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (shaderAssetSource.IsRhiBackendDisabled(shaderPlatformInterface->GetAPIName()))
|
||||
{
|
||||
// Gracefully do nothing and continue with the next shaderPlatformInterface.
|
||||
AZ_TracePrintf(
|
||||
ShaderAssetBuilderName, "Skipping shader compilation [%s] for API [%s]\n", inputFiles->m_shaderFileName.c_str(),
|
||||
shaderPlatformInterface->GetAPIName().GetCStr());
|
||||
continue;
|
||||
}
|
||||
|
||||
AZStd::sys_time_t shaderAssetBuildTimestamp = 0;
|
||||
auto shaderAssetBuildTimestampIterator = request.m_jobDescription.m_jobParameters.find(ShaderAssetBuildTimestampParam);
|
||||
if (shaderAssetBuildTimestampIterator != request.m_jobDescription.m_jobParameters.end())
|
||||
{
|
||||
shaderAssetBuildTimestamp = AZStd::stoull(shaderAssetBuildTimestampIterator->second);
|
||||
|
||||
if (AZStd::to_string(shaderAssetBuildTimestamp) != shaderAssetBuildTimestampIterator->second)
|
||||
{
|
||||
AZ_Assert(false, "Incorrect conversion of ShaderAssetBuildTimestampParam");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AzslData azslData(inputFiles);
|
||||
|
||||
AZ_TraceContext("Platform API", AZStd::string{ shaderPlatformInterface->GetAPIName().GetStringView() });
|
||||
AZ_TracePrintf(ShaderAssetBuilderName, "Preprocessed AZSL File: %s \n", azslData.m_preprocessedFullPath.c_str());
|
||||
|
||||
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
// obtain the build artifacts from the azsl builder:
|
||||
auto azslArtifactsOutcome = ShaderBuilderUtility::ObtainBuildArtifactsFromAzslBuilder(ShaderAssetBuilderName, fullSourcePath, shaderPlatformInterface->GetAPIType(), request.m_platformInfo.m_identifier);
|
||||
if (!azslArtifactsOutcome.IsSuccess())
|
||||
{
|
||||
// If it failed, it may be because the .shader source file created no products! (this happens when the build options are similar)
|
||||
// (the .azsl file *always* produces AzslBuilder artifacts. The.shader file *sometimes* produces AzslBuilder artifacts, when it has build options that have to be accounted for)
|
||||
// If there are no artifacts from the .shader, then we fall back to the ones from the .azsl:
|
||||
azslArtifactsOutcome = ShaderBuilderUtility::ObtainBuildArtifactsFromAzslBuilder(ShaderAssetBuilderName, inputFiles->m_azslSourceFullPath, shaderPlatformInterface->GetAPIType(), request.m_platformInfo.m_identifier);
|
||||
if (!azslArtifactsOutcome.IsSuccess())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
shaderAssetCreator.SetName(Name(azslData.m_sources->m_shaderFileName));
|
||||
shaderAssetCreator.SetDrawListName(Name(shaderAssetSource.m_drawListName));
|
||||
|
||||
shaderAssetCreator.SetShaderAssetBuildTimestamp(shaderAssetBuildTimestamp);
|
||||
|
||||
BindingDependencies bindingDependencies;
|
||||
RootConstantData rootConstantData;
|
||||
AssetBuilderSDK::ProcessJobResultCode azslJsonReadResult = ShaderBuilderUtility::PopulateAzslDataFromJsonFiles(
|
||||
ShaderAssetBuilderName,
|
||||
azslArtifactsOutcome.GetValue(),
|
||||
azslData,
|
||||
srgAssets,
|
||||
shaderOptionGroupLayout,
|
||||
bindingDependencies,
|
||||
rootConstantData);
|
||||
if (azslJsonReadResult != AssetBuilderSDK::ProcessJobResult_Success)
|
||||
|
||||
{
|
||||
response.m_resultCode = azslJsonReadResult;
|
||||
return;
|
||||
}
|
||||
|
||||
shaderAssetCreator.SetName(Name(azslData.m_sources->m_shaderFileName));
|
||||
shaderAssetCreator.SetDrawListName(Name(shaderAssetSource.m_drawListName));
|
||||
shaderAssetCreator.SetShaderAssetBuildTimestamp(shaderAssetBuildTimestamp);
|
||||
|
||||
// The ShaderOptionGroupLayout dictates what options/range can be used to create variants
|
||||
shaderAssetCreator.SetShaderOptionGroupLayout(shaderOptionGroupLayout);
|
||||
|
||||
AZ_TracePrintf(ShaderAssetBuilderName, "Original AZSL File: %s \n", azslData.m_sources->m_azslSourceFullPath.c_str());
|
||||
|
||||
const uint32_t usedShaderOptionBits = shaderOptionGroupLayout->GetBitSize();
|
||||
AZ_TracePrintf(ShaderAssetBuilderName, "Note: This shader uses %u of %u available shader variant key bits. \n", usedShaderOptionBits, RPI::ShaderVariantKeyBitCount);
|
||||
|
||||
// The idea of this merge is that we have compiler options coming from 2 source:
|
||||
// global options (from project Config/), and .shader options.
|
||||
// We define a merge behavior that is: ".shader wins if set"
|
||||
RHI::ShaderCompilerArguments mergedArguments = buildOptions.m_compilerArguments;
|
||||
mergedArguments.Merge(shaderAssetSource.m_compiler);
|
||||
|
||||
AssetBuilderSDK::ProcessJobResultCode compileResult =
|
||||
CompileForAPI(
|
||||
azslArtifactsOutcome.GetValue(),
|
||||
shaderAssetCreator,
|
||||
shaderPlatformInterface,
|
||||
response,
|
||||
azslData,
|
||||
mergedArguments,
|
||||
shaderOptionGroupLayout,
|
||||
shaderAssetSource,
|
||||
shaderAssetBuildTimestamp,
|
||||
srgAssets,
|
||||
bindingDependencies,
|
||||
rootConstantData,
|
||||
request);
|
||||
if (compileResult != AssetBuilderSDK::ProcessJobResult_Success)
|
||||
{
|
||||
response.m_resultCode = compileResult;
|
||||
return;
|
||||
}
|
||||
++countOfCompiledPlatformInterfaces;
|
||||
} // end for all platforms
|
||||
|
||||
if (!countOfCompiledPlatformInterfaces)
|
||||
{
|
||||
AZ_TracePrintf(
|
||||
ShaderAssetBuilderName, "No azshader is produced on behalf of %s because all valid RHI backends were disabled for this shader.\n", request.m_sourceFile.c_str());
|
||||
//No work to do. Exit gracefully.
|
||||
AZ_TracePrintf(ShaderAssetBuilderName,
|
||||
"No azshader is produced on behalf of %s because all valid RHI backends were disabled for this shader.\n",
|
||||
shaderFullPath.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the time stamp string as sys_time_t, and also convert back to string to make sure it was converted correctly.
|
||||
AZStd::sys_time_t shaderAssetBuildTimestamp = 0;
|
||||
auto shaderAssetBuildTimestampIterator = request.m_jobDescription.m_jobParameters.find(ShaderAssetBuildTimestampParam);
|
||||
if (shaderAssetBuildTimestampIterator != request.m_jobDescription.m_jobParameters.end())
|
||||
{
|
||||
shaderAssetBuildTimestamp = AZStd::stoull(shaderAssetBuildTimestampIterator->second);
|
||||
|
||||
if (AZStd::to_string(shaderAssetBuildTimestamp) != shaderAssetBuildTimestampIterator->second)
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
AZ_Assert(false, "Incorrect conversion of ShaderAssetBuildTimestampParam");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto supervariantList = ShaderBuilderUtility::GetSupervariantListFromShaderSourceData(shaderSourceData);
|
||||
|
||||
RPI::ShaderAssetCreator shaderAssetCreator;
|
||||
shaderAssetCreator.Begin(Uuid::CreateRandom());
|
||||
|
||||
shaderAssetCreator.SetName(AZ::Name{shaderFileName.c_str()});
|
||||
shaderAssetCreator.SetDrawListName(Name(shaderSourceData.m_drawListName));
|
||||
shaderAssetCreator.SetShaderAssetBuildTimestamp(shaderAssetBuildTimestamp);
|
||||
|
||||
// The ShaderOptionGroupLayout must be the same across all supervariants because
|
||||
// there can be only a single ShaderVariantTreeAsset per ShaderAsset.
|
||||
// We will store here the one that results when the *.azslin file is
|
||||
// compiled for the default, nameless, supervariant.
|
||||
// For all other supervariants we just make sure the hashes are the same
|
||||
// as this one.
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> finalShaderOptionGroupLayout = nullptr;
|
||||
|
||||
|
||||
// Time to describe the big picture.
|
||||
// 1- Preprocess an AZSL file with MCPP (a C-Preprocessor), and generate a flat AZSL file without #include lines and any macros in it.
|
||||
// Let's call it the Flat-AZSL file. There are two levels of macro definition that need to be merged before we can invoke MCPP:
|
||||
// 1.1- From <GameProject>/Config/shader_global_build_options.json, which we have stored in the local variable @buildOptions.
|
||||
// 1.2- From the "Supervariant" definition key, which can be different for each supervariant.
|
||||
// 2- There will be one Flat-AZSL per supervariant. Each Flat-AZSL will be transpiled to HLSL with AZSLc. This means there will be one HLSL file
|
||||
// per supervariant.
|
||||
// 3- The generated HLSL (one HLSL per supervariant) file may contain C-Preprocessor Macros inserted by AZSLc. And that file will be given to DXC.
|
||||
// DXC has a preprocessor embedded in it. DXC will be executed once for each entry function listed in the .shader file.
|
||||
// There will be one DXIL compiled binary for each entry function. All the DXIL compiled binaries for each supervariant will be combined
|
||||
// in the ROOT ShaderVariantAsset.
|
||||
|
||||
// Remark: In general, the work done by the ShaderVariantAssetBuilder is similar, but it will start from the HLSL file created; in step 2, mentioned above; by this builder,
|
||||
// for each supervariant.
|
||||
|
||||
// At this moment We have global build options that should be merged with the build options that are common
|
||||
// to all the supervariants of this shader.
|
||||
buildOptions.m_compilerArguments.Merge(shaderSourceData.m_compiler);
|
||||
|
||||
for (RHI::ShaderPlatformInterface* shaderPlatformInterface : platformInterfaces)
|
||||
{
|
||||
AZStd::string apiName(shaderPlatformInterface->GetAPIName().GetCStr());
|
||||
AZ_TraceContext("Platform API", apiName);
|
||||
// Signal the begin of shader data for an RHI API.
|
||||
shaderAssetCreator.BeginAPI(shaderPlatformInterface->GetAPIType());
|
||||
|
||||
// Each shaderPlatformInterface has its own azsli header that needs to be prepended to the AZSL file before
|
||||
// preprocessing. We will create a new temporary file that contains the combined data.
|
||||
RHI::PrependArguments args;
|
||||
args.m_sourceFile = azslFullPath.c_str();
|
||||
args.m_prependFile = shaderPlatformInterface->GetAzslHeader(request.m_platformInfo);
|
||||
args.m_addSuffixToFileName = apiName.c_str();
|
||||
args.m_destinationFolder = request.m_tempDirPath.c_str();
|
||||
|
||||
AZStd::string prependedAzslFilePath = RHI::PrependFile(args);
|
||||
if (prependedAzslFilePath == azslFullPath)
|
||||
{
|
||||
// The specific error is already reported by RHI::PrependFile().
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache common AZSLC invokation arguments related with the current RHI Backend.
|
||||
// Each supervariant can, optionally, remove or add more arguments for AZSLc.
|
||||
AZStd::string commonAzslcCompilerParameters =
|
||||
shaderPlatformInterface->GetAzslCompilerParameters(buildOptions.m_compilerArguments);
|
||||
commonAzslcCompilerParameters += " ";
|
||||
commonAzslcCompilerParameters +=
|
||||
shaderPlatformInterface->GetAzslCompilerWarningParameters(buildOptions.m_compilerArguments);
|
||||
AtomShaderConfig::AddParametersFromConfigFile(commonAzslcCompilerParameters, request.m_platformInfo);
|
||||
|
||||
// The register number only makes sense if the platform uses "spaces",
|
||||
// since the register Id of the resource will not change even if the pipeline layout changes.
|
||||
// We can pass in a default ShaderCompilerArguments because all we care about is whether the shaderPlatformInterface
|
||||
// appends the "--use-spaces" flag.
|
||||
const bool platformUsesRegisterSpaces =
|
||||
(AzFramework::StringFunc::Find(commonAzslcCompilerParameters, "--use-spaces") != AZStd::string::npos);
|
||||
|
||||
uint32_t supervariantIndex = 0;
|
||||
for (const auto& supervariantInfo : supervariantList)
|
||||
{
|
||||
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
shaderAssetCreator.BeginSupervariant(supervariantInfo.m_name);
|
||||
|
||||
// Let's combine the global macro definitions, with the macro definitions particular to this
|
||||
// supervariant. Two steps:
|
||||
// 1- Supervariants can specify which macros to remove from the global definitions.
|
||||
AZStd::vector<AZStd::string> macroDefinitionNamesToRemove = supervariantInfo.GetCombinedListOfMacroDefinitionNamesToRemove();
|
||||
PreprocessorOptions preprocessorOptions = buildOptions.m_preprocessorSettings;
|
||||
preprocessorOptions.RemovePredefinedMacros(macroDefinitionNamesToRemove);
|
||||
// 2- Supervariants can specify which macros to add.
|
||||
AZStd::vector<AZStd::string> macroDefinitionsToAdd = supervariantInfo.GetMacroDefinitionsToAdd();
|
||||
preprocessorOptions.m_predefinedMacros.insert(
|
||||
preprocessorOptions.m_predefinedMacros.end(), macroDefinitionsToAdd.begin(), macroDefinitionsToAdd.end());
|
||||
// Run the preprocessor.
|
||||
PreprocessorData output;
|
||||
PreprocessFile(prependedAzslFilePath, output, preprocessorOptions, true, true);
|
||||
RHI::ReportErrorMessages(ShaderAssetBuilderName, output.diagnostics);
|
||||
// Dump the preprocessed string as a flat AZSL file with extension .azslin, which will be given to AZSLc to generate the HLSL file.
|
||||
AZStd::string superVariantAzslinStemName = shaderFileName;
|
||||
if (!supervariantInfo.m_name.IsEmpty())
|
||||
{
|
||||
superVariantAzslinStemName += AZStd::string::format("-%s", supervariantInfo.m_name.GetCStr());
|
||||
}
|
||||
AZStd::string azslinFullPath = ShaderBuilderUtility::DumpPreprocessedCode(
|
||||
ShaderAssetBuilderName, output.code, request.m_tempDirPath, superVariantAzslinStemName,
|
||||
apiName);
|
||||
if (azslinFullPath.empty())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
AZ_TracePrintf(ShaderAssetBuilderName, "Preprocessed AZSL File: %s \n", prependedAzslFilePath.c_str());
|
||||
|
||||
// Before transpiling the flat-AZSL(.azslin) file into HLSL it is necessary
|
||||
// to setup the AZSLc arguments as required by the current supervariant.
|
||||
AZStd::string azslcCompilerParameters = supervariantInfo.GetCustomizedArgumentsForAzslc(commonAzslcCompilerParameters);
|
||||
|
||||
// Ready to transpile the azslin file into HLSL.
|
||||
ShaderBuilder::AzslCompiler azslc(azslinFullPath);
|
||||
AZStd::string hlslFullPath = AZStd::string::format("%s_%s.hlsl", superVariantAzslinStemName.c_str(), apiName.c_str());
|
||||
AzFramework::StringFunc::Path::Join(request.m_tempDirPath.c_str(), hlslFullPath.c_str(), hlslFullPath, true);
|
||||
auto emitFullOutcome = azslc.EmitFullData(azslcCompilerParameters, hlslFullPath);
|
||||
if (!emitFullOutcome.IsSuccess())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
ShaderBuilderUtility::AzslSubProducts::Paths subProductsPaths = emitFullOutcome.TakeValue();
|
||||
|
||||
// In addition to the hlsl file, there are other json files that were generated.
|
||||
// Each output file will become a product.
|
||||
for (int i = 0; i < subProductsPaths.size(); ++i)
|
||||
{
|
||||
AssetBuilderSDK::JobProduct jobProduct;
|
||||
jobProduct.m_productFileName = subProductsPaths[i];
|
||||
static const AZ::Uuid AzslOutcomeType = "{6977AEB1-17AD-4992-957B-23BB2E85B18B}";
|
||||
jobProduct.m_productAssetType = AzslOutcomeType;
|
||||
// uint32_t rhiApiUniqueIndex, uint32_t supervariantIndex, uint32_t subProductType
|
||||
jobProduct.m_productSubID = RPI::ShaderAsset::MakeProductAssetSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), supervariantIndex,
|
||||
aznumeric_cast<uint32_t>(ShaderBuilderUtility::AzslSubProducts::SubList[i]));
|
||||
jobProduct.m_dependenciesHandled = true;
|
||||
// Note that the output products are not traditional product assets that will be used by the game project.
|
||||
// They are artifacts that are produced once, cached, and used later by other AssetBuilders as a way to centralize
|
||||
// build organization.
|
||||
response.m_outputProducts.push_back(AZStd::move(jobProduct));
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<ShaderFiles> files(new ShaderFiles);
|
||||
AzslData azslData(files);
|
||||
azslData.m_preprocessedFullPath = azslinFullPath;
|
||||
RPI::ShaderResourceGroupLayoutList srgLayoutList;
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
|
||||
BindingDependencies bindingDependencies;
|
||||
RootConstantData rootConstantData;
|
||||
AssetBuilderSDK::ProcessJobResultCode azslJsonReadResult = ShaderBuilderUtility::PopulateAzslDataFromJsonFiles(
|
||||
ShaderAssetBuilderName, subProductsPaths, platformUsesRegisterSpaces, azslData, srgLayoutList, shaderOptionGroupLayout,
|
||||
bindingDependencies, rootConstantData);
|
||||
if (azslJsonReadResult != AssetBuilderSDK::ProcessJobResult_Success)
|
||||
|
||||
{
|
||||
response.m_resultCode = azslJsonReadResult;
|
||||
return;
|
||||
}
|
||||
|
||||
shaderAssetCreator.SetSrgLayoutList(srgLayoutList);
|
||||
|
||||
if (!finalShaderOptionGroupLayout)
|
||||
{
|
||||
finalShaderOptionGroupLayout = shaderOptionGroupLayout;
|
||||
shaderAssetCreator.SetShaderOptionGroupLayout(finalShaderOptionGroupLayout);
|
||||
const uint32_t usedShaderOptionBits = shaderOptionGroupLayout->GetBitSize();
|
||||
AZ_TracePrintf(
|
||||
ShaderAssetBuilderName, "Note: This shader uses %u of %u available shader variant key bits. \n",
|
||||
usedShaderOptionBits, RPI::ShaderVariantKeyBitCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (finalShaderOptionGroupLayout->GetHash() != shaderOptionGroupLayout->GetHash())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilderName, false, "Supervariant %s has a different ShaderOptionGroupLayout",
|
||||
supervariantInfo.m_name.GetCStr())
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Discover entry points & type of programs.
|
||||
MapOfStringToStageType shaderEntryPoints;
|
||||
if (shaderSourceData.m_programSettings.m_entryPoints.empty())
|
||||
{
|
||||
AZ_TracePrintf(
|
||||
ShaderAssetBuilderName,
|
||||
"ProgramSettings do not specify entry points, will use GetDefaultEntryPointsFromShader()\n");
|
||||
ShaderBuilderUtility::GetDefaultEntryPointsFromFunctionDataList(azslData.m_functions, shaderEntryPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& entryPoint : shaderSourceData.m_programSettings.m_entryPoints)
|
||||
{
|
||||
shaderEntryPoints[entryPoint.m_name] = entryPoint.m_type;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasRasterProgram = false;
|
||||
auto attributeMapsOutcome = BuildAttributesMap(shaderPlatformInterface, azslData, shaderEntryPoints, hasRasterProgram);
|
||||
if (!attributeMapsOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilderName, false, "%s\n", attributeMapsOutcome.GetError().c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
shaderAssetCreator.SetShaderStageAttributeMapList(attributeMapsOutcome.TakeValue());
|
||||
|
||||
// Check if we were canceled before we do any heavy processing of
|
||||
// the shader data (compiling the shader kernels, processing SRG
|
||||
// and pipeline layout data, etc.).
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
RHI::Ptr<RHI::PipelineLayoutDescriptor> pipelineLayoutDescriptor =
|
||||
ShaderBuilderUtility::BuildPipelineLayoutDescriptorForApi(
|
||||
ShaderAssetBuilderName, srgLayoutList, shaderEntryPoints, buildOptions.m_compilerArguments, rootConstantData,
|
||||
shaderPlatformInterface, bindingDependencies);
|
||||
if (!pipelineLayoutDescriptor)
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilderName, false, "Failed to build pipeline layout descriptor for api=[%s]",
|
||||
shaderPlatformInterface->GetAPIName().GetCStr());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
shaderAssetCreator.SetPipelineLayout(pipelineLayoutDescriptor);
|
||||
|
||||
|
||||
RPI::ShaderInputContract shaderInputContract;
|
||||
RPI::ShaderOutputContract shaderOutputContract;
|
||||
size_t colorAttachmentCount = 0;
|
||||
ShaderBuilderUtility::CreateShaderInputAndOutputContracts(
|
||||
azslData, shaderEntryPoints, *shaderOptionGroupLayout.get(),
|
||||
subProductsPaths[ShaderBuilderUtility::AzslSubProducts::om],
|
||||
subProductsPaths[ShaderBuilderUtility::AzslSubProducts::ia],
|
||||
shaderInputContract, shaderOutputContract, colorAttachmentCount);
|
||||
shaderAssetCreator.SetInputContract(shaderInputContract);
|
||||
shaderAssetCreator.SetOutputContract(shaderOutputContract);
|
||||
|
||||
if (hasRasterProgram)
|
||||
{
|
||||
// Set the various states to what is in the descriptor.
|
||||
const RHI::TargetBlendState& targetBlendState = shaderSourceData.m_blendState;
|
||||
RHI::RenderStates renderStates;
|
||||
renderStates.m_rasterState = shaderSourceData.m_rasterState;
|
||||
renderStates.m_depthStencilState = shaderSourceData.m_depthStencilState;
|
||||
// [GFX TODO][ATOM-930] We should support unique blend states per RT
|
||||
for (size_t i = 0; i < colorAttachmentCount; ++i)
|
||||
{
|
||||
renderStates.m_blendState.m_targets[i] = targetBlendState;
|
||||
}
|
||||
|
||||
shaderAssetCreator.SetRenderStates(renderStates);
|
||||
}
|
||||
|
||||
Outcome<AZStd::string, AZStd::string> hlslSourceCodeOutcome = Utils::ReadFile(hlslFullPath);
|
||||
if (!hlslSourceCodeOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilderName, false, "Failed to obtain shader source from %s. [%s]", hlslFullPath.c_str(),
|
||||
hlslSourceCodeOutcome.GetError().c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
AZStd::string hlslSourceCode = hlslSourceCodeOutcome.TakeValue();
|
||||
|
||||
// The root ShaderVariantAsset needs to be created with the known uuid of the source .shader asset because
|
||||
// the ShaderAsset owns a Data::Asset<> reference that gets serialized. It must have the correct uuid
|
||||
// so the root ShaderVariantAsset is found when the ShaderAsset is deserialized.
|
||||
uint32_t rootVariantProductSubId = RPI::ShaderAsset::MakeProductAssetSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), supervariantIndex,
|
||||
aznumeric_cast<uint32_t>(RPI::ShaderAssetSubId::RootShaderVariantAsset));
|
||||
auto assetIdOutcome = RPI::AssetUtils::MakeAssetId(shaderFullPath, rootVariantProductSubId);
|
||||
AZ_Assert(assetIdOutcome.IsSuccess(), "Failed to get AssetId from shader %s", shaderFullPath.c_str());
|
||||
const Data::AssetId variantAssetId = assetIdOutcome.TakeValue();
|
||||
|
||||
RPI::ShaderVariantListSourceData::VariantInfo rootVariantInfo;
|
||||
ShaderVariantCreationContext shaderVariantCreationContext = {
|
||||
*shaderPlatformInterface,
|
||||
request.m_platformInfo,
|
||||
buildOptions.m_compilerArguments,
|
||||
request.m_tempDirPath,
|
||||
startTime,
|
||||
shaderSourceData,
|
||||
*shaderOptionGroupLayout.get(),
|
||||
shaderEntryPoints,
|
||||
variantAssetId,
|
||||
superVariantAzslinStemName,
|
||||
hlslFullPath,
|
||||
hlslSourceCode};
|
||||
|
||||
|
||||
AZStd::optional<RHI::ShaderPlatformInterface::ByProducts> outputByproducts;
|
||||
auto rootShaderVariantAssetOutcome = ShaderVariantAssetBuilder::CreateShaderVariantAsset(rootVariantInfo, shaderVariantCreationContext, outputByproducts);
|
||||
if (!rootShaderVariantAssetOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilderName, false, "%s\n", rootShaderVariantAssetOutcome.GetError().c_str())
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
Data::Asset<RPI::ShaderVariantAsset> rootShaderVariantAsset = rootShaderVariantAssetOutcome.TakeValue();
|
||||
|
||||
shaderAssetCreator.SetRootShaderVariantAsset(rootShaderVariantAsset);
|
||||
|
||||
if (!shaderAssetCreator.EndSupervariant())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilderName, false, "Failed to create shader asset for supervariant [%s]", supervariantInfo.m_name.GetCStr())
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
// Time to save the root variant related assets in the cache.
|
||||
AssetBuilderSDK::JobProduct assetProduct;
|
||||
if (!ShaderVariantAssetBuilder::SerializeOutShaderVariantAsset(
|
||||
rootShaderVariantAsset, superVariantAzslinStemName, request.m_tempDirPath, *shaderPlatformInterface,
|
||||
rootVariantProductSubId,
|
||||
assetProduct))
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
response.m_outputProducts.push_back(assetProduct);
|
||||
|
||||
if (outputByproducts)
|
||||
{
|
||||
// add byproducts as job output products:
|
||||
uint32_t subProductType = aznumeric_cast<uint32_t>(RPI::ShaderAssetSubId::FirstByProduct);
|
||||
for (const AZStd::string& byproduct : outputByproducts.value().m_intermediatePaths)
|
||||
{
|
||||
AssetBuilderSDK::JobProduct jobProduct;
|
||||
jobProduct.m_productFileName = byproduct;
|
||||
jobProduct.m_productAssetType = Uuid::CreateName("DebugInfoByProduct-PdbOrDxilTxt");
|
||||
jobProduct.m_productSubID = RPI::ShaderAsset::MakeProductAssetSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), supervariantIndex,
|
||||
subProductType++);
|
||||
response.m_outputProducts.push_back(AZStd::move(jobProduct));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
supervariantIndex++;
|
||||
|
||||
} // end for the supervariant
|
||||
|
||||
shaderAssetCreator.EndAPI();
|
||||
|
||||
} // end for all ShaderPlatformInterfaces
|
||||
|
||||
Data::Asset<RPI::ShaderAsset> shaderAsset;
|
||||
if (!shaderAssetCreator.End(shaderAsset))
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!SerializeOutShaderAsset(shaderAsset, request.m_tempDirPath, response))
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
|
||||
|
||||
const AZStd::sys_time_t endTime = AZStd::GetTimeNowTicks();
|
||||
const AZStd::sys_time_t deltaTime = endTime - startTime;
|
||||
const float elapsedTimeSeconds = (float)(deltaTime) / (float)AZStd::GetTimeTicksPerSecond();
|
||||
|
||||
|
||||
AZ_TracePrintf(ShaderAssetBuilderName, "Finished processing %s in %.2f seconds\n", request.m_sourceFile.c_str(), elapsedTimeSeconds);
|
||||
|
||||
ShaderBuilderUtility::LogProfilingData(ShaderAssetBuilderName, inputFiles->m_azslFileName);
|
||||
|
||||
ShaderBuilderUtility::LogProfilingData(ShaderAssetBuilderName, shaderFileName);
|
||||
}
|
||||
|
||||
} // ShaderBuilder
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace AZ
|
||||
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(ShaderAssetBuilder, "{AEC304A9-940C-4851-AEF0-7D00482598F9}");
|
||||
AZ_TYPE_INFO(ShaderAssetBuilder, "{C94DA151-82BC-4475-86FA-E6C92A0BD6F8}");
|
||||
|
||||
static constexpr const char* ShaderAssetBuilderJobKey = "Shader Asset";
|
||||
|
||||
|
||||
@@ -1,683 +0,0 @@
|
||||
/*
|
||||
* 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 "ShaderAssetBuilder2.h"
|
||||
|
||||
#include <CommonFiles/Preprocessor.h>
|
||||
#include <CommonFiles/GlobalBuildOptions.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAssetCreator2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantKey.h>
|
||||
|
||||
#include <Atom/RHI.Edit/Utils.h>
|
||||
#include <Atom/RHI.Edit/ShaderPlatformInterface.h>
|
||||
#include <Atom/RPI.Edit/Common/JsonReportingHelper.h>
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <Atom/RHI.Reflect/ConstantsLayout.h>
|
||||
#include <Atom/RHI.Reflect/PipelineLayoutDescriptor.h>
|
||||
#include <Atom/RHI.Reflect/ShaderStageFunction.h>
|
||||
|
||||
#include <AtomCore/Serialization/Json/JsonUtils.h>
|
||||
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
#include <AzFramework/Platform/PlatformDefaults.h>
|
||||
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/JSON/document.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/IOUtils.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
|
||||
#include "AzslBuilder.h"
|
||||
#include "ShaderVariantAssetBuilder2.h"
|
||||
#include "ShaderBuilderUtility.h"
|
||||
#include "ShaderPlatformInterfaceRequest.h"
|
||||
#include "AtomShaderConfig.h"
|
||||
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
#include <AssetBuilderSDK/SerializationDependencies.h>
|
||||
namespace AZ
|
||||
{
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
static constexpr char ShaderAssetBuilder2Name[] = "ShaderAssetBuilder2";
|
||||
static constexpr uint32_t ShaderAssetBuildTimestampParam = 0;
|
||||
|
||||
void ShaderAssetBuilder2::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const
|
||||
{
|
||||
AZStd::string fullPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.data(), request.m_sourceFile.data(), fullPath, true);
|
||||
|
||||
AZ_TracePrintf(ShaderAssetBuilder2Name, "CreateJobs for Shader \"%s\"\n", fullPath.data());
|
||||
|
||||
// Used to synchronize versions of the ShaderAsset and ShaderVariantTreeAsset, especially during hot-reload.
|
||||
// Note it's probably important for this to be set once outside the platform loop so every platform's ShaderAsset
|
||||
// has the same value, because later the ShaderVariantTreeAsset job will fetch this value from the local ShaderAsset
|
||||
// which could cross platforms (i.e. building an android ShaderVariantTreeAsset on PC would fetch the tiemstamp from
|
||||
// the PC's ShaderAsset).
|
||||
AZStd::sys_time_t shaderAssetBuildTimestamp = AZStd::GetTimeNowMicroSecond();
|
||||
|
||||
// Need to get the name of the azsl file from the .shader source asset, to be able to declare a dependency to SRG Layout Job.
|
||||
// and the macro options to preprocess.
|
||||
auto descriptorParseOutcome = ShaderBuilderUtility::LoadShaderDataJson(fullPath);
|
||||
if (!descriptorParseOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilder2Name, false, "Failed to parse Shader Descriptor JSON: %s",
|
||||
descriptorParseOutcome.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
RPI::ShaderSourceData shaderSourceData = descriptorParseOutcome.TakeValue();
|
||||
|
||||
AZStd::string azslFullPath;
|
||||
ShaderBuilderUtility::GetAbsolutePathToAzslFile(fullPath, shaderSourceData.m_source, azslFullPath);
|
||||
if (!IO::FileIOBase::GetInstance()->Exists(azslFullPath.c_str()))
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilder2Name, false, "Shader program listed as the source entry does not exist: %s.", azslFullPath.c_str());
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
GlobalBuildOptions buildOptions = ReadBuildOptions(ShaderAssetBuilder2Name);
|
||||
|
||||
// [GFX TODO] [ATOM-14966] In principle, based on macro definitions, included files can change per supervariant.
|
||||
// So, the list of source asset dependencies must be collected by running MCPP on each supervariant.
|
||||
// For now, we will run MCPP only once because CreateJobs() should be as light as possible.
|
||||
//
|
||||
// Regardless of the PlatformInfo and enabled ShaderPlatformInterfaces, the azsl file will be preprocessed
|
||||
// with the sole purpose of extracting all included files. For each included file a SourceDependency will be declared.
|
||||
PreprocessorData output;
|
||||
buildOptions.m_compilerArguments.Merge(shaderSourceData.m_compiler);
|
||||
PreprocessFile(azslFullPath, output, buildOptions.m_preprocessorSettings, true, true);
|
||||
for (auto includePath : output.includedPaths)
|
||||
{
|
||||
// m_sourceFileDependencyList does not support paths with "." or ".." for relative lookup, but the preprocessor
|
||||
// may produce path strings like "C:/a/b/c/../../d/file.azsli" so we have to normalize
|
||||
AzFramework::StringFunc::Path::Normalize(includePath);
|
||||
|
||||
AssetBuilderSDK::SourceFileDependency includeFileDependency;
|
||||
includeFileDependency.m_sourceFileDependencyPath = includePath;
|
||||
response.m_sourceFileDependencyList.emplace_back(includeFileDependency);
|
||||
}
|
||||
|
||||
{
|
||||
// Add the AZSL as source dependency
|
||||
AssetBuilderSDK::SourceFileDependency azslFileDependency;
|
||||
azslFileDependency.m_sourceFileDependencyPath = azslFullPath;
|
||||
response.m_sourceFileDependencyList.emplace_back(azslFileDependency);
|
||||
}
|
||||
|
||||
for (const AssetBuilderSDK::PlatformInfo& platformInfo : request.m_enabledPlatforms)
|
||||
{
|
||||
AZ_TraceContext("For platform", platformInfo.m_identifier.data());
|
||||
|
||||
// Get the platform interfaces to be able to access the prepend file
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces = ShaderBuilderUtility::DiscoverValidShaderPlatformInterfaces(platformInfo);
|
||||
if (platformInterfaces.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AssetBuilderSDK::JobDescriptor jobDescriptor;
|
||||
jobDescriptor.m_priority = 2;
|
||||
// [GFX TODO][ATOM-2830] Set 'm_critical' back to 'false' once proper fix for Atom startup issues are in
|
||||
jobDescriptor.m_critical = true;
|
||||
jobDescriptor.m_jobKey = ShaderAssetBuilder2JobKey;
|
||||
jobDescriptor.SetPlatformIdentifier(platformInfo.m_identifier.c_str());
|
||||
jobDescriptor.m_jobParameters.emplace(ShaderAssetBuildTimestampParam, AZStd::to_string(shaderAssetBuildTimestamp));
|
||||
|
||||
response.m_createJobOutputs.push_back(jobDescriptor);
|
||||
} // for all request.m_enabledPlatforms
|
||||
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
|
||||
}
|
||||
|
||||
static bool SerializeOutShaderAsset(Data::Asset<RPI::ShaderAsset2> shaderAsset,
|
||||
const AZStd::string& tempDirPath,
|
||||
AssetBuilderSDK::ProcessJobResponse& response)
|
||||
{
|
||||
AZStd::string shaderAssetFileName = AZStd::string::format("%s.%s", shaderAsset->GetName().GetCStr(), RPI::ShaderAsset2::Extension);
|
||||
AZStd::string shaderAssetOutputPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(tempDirPath.data(), shaderAssetFileName.data(), shaderAssetOutputPath, true);
|
||||
|
||||
if (!Utils::SaveObjectToFile(shaderAssetOutputPath, DataStream::ST_BINARY, shaderAsset.Get()))
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilder2Name, false, "Failed to output Shader Descriptor");
|
||||
return false;
|
||||
}
|
||||
|
||||
AssetBuilderSDK::JobProduct shaderJobProduct;
|
||||
if (!AssetBuilderSDK::OutputObject(shaderAsset.Get(), shaderAssetOutputPath, azrtti_typeid<RPI::ShaderAsset2>(),
|
||||
aznumeric_cast<uint32_t>(RPI::ShaderAsset2ProductSubId::ShaderAsset2), shaderJobProduct))
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilder2Name, false, "Failed to output product dependencies.");
|
||||
return false;
|
||||
}
|
||||
response.m_outputProducts.push_back(AZStd::move(shaderJobProduct));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static AZ::Outcome<RHI::ShaderStageAttributeMapList, AZStd::string> BuildAttributesMap(
|
||||
const RHI::ShaderPlatformInterface* shaderPlatformInterface,
|
||||
const AzslData& azslData,
|
||||
const MapOfStringToStageType& shaderEntryPoints,
|
||||
bool& hasRasterProgram)
|
||||
{
|
||||
hasRasterProgram = false;
|
||||
bool hasComputeProgram = false;
|
||||
bool hasRayTracingProgram = false;
|
||||
RHI::ShaderStageAttributeMapList attributeMaps;
|
||||
attributeMaps.resize(RHI::ShaderStageCount);
|
||||
for (const auto& shaderEntryPoint : shaderEntryPoints)
|
||||
{
|
||||
auto shaderEntryName = shaderEntryPoint.first;
|
||||
auto shaderStageType = shaderEntryPoint.second;
|
||||
auto assetBuilderShaderType = ShaderBuilderUtility::ToAssetBuilderShaderType(shaderStageType);
|
||||
hasRasterProgram |= shaderPlatformInterface->IsShaderStageForRaster(assetBuilderShaderType);
|
||||
hasComputeProgram |= shaderPlatformInterface->IsShaderStageForCompute(assetBuilderShaderType);
|
||||
hasRayTracingProgram |= shaderPlatformInterface->IsShaderStageForRayTracing(assetBuilderShaderType);
|
||||
|
||||
auto findId = AZStd::find_if(AZ_BEGIN_END(azslData.m_functions), [&shaderEntryPoint](const auto& func) {
|
||||
return func.m_name == shaderEntryPoint.first;
|
||||
});
|
||||
|
||||
if (findId == azslData.m_functions.end())
|
||||
{
|
||||
// shaderData.m_functions only contains Vertex, Fragment and Compute entries for now
|
||||
// Tessellation shaders will need to be handled too
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto shaderStage = ToRHIShaderStage(assetBuilderShaderType);
|
||||
for (const auto& attr : findId->attributesList)
|
||||
{
|
||||
// Some stages like RHI::ShaderStage::Tessellation are compound and consist of two or more shader entries
|
||||
const Name& attributeName = attr.first;
|
||||
const RHI::ShaderStageAttributeArguments& args = attr.second;
|
||||
const auto stageIndex = static_cast<uint32_t>(shaderStage);
|
||||
AZ_Assert(stageIndex < RHI::ShaderStageCount, "Invalid shader stage specified!");
|
||||
attributeMaps[stageIndex][attributeName] = args;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasRasterProgram && hasComputeProgram)
|
||||
{
|
||||
return AZ::Failure(AZStd::string(" Shader asset descriptor defines both a raster entry point and a compute entry point."));
|
||||
}
|
||||
|
||||
if (!hasRasterProgram && !hasComputeProgram && !hasRayTracingProgram)
|
||||
{
|
||||
AZStd::string entryPointNames = ShaderBuilderUtility::GetAcceptableDefaultEntryPointNames(azslData);
|
||||
return AZ::Failure(
|
||||
AZStd::string::format( "Shader asset descriptor has a program variant that does not define any entry points. Either declare entry "
|
||||
"points in the .shader file, or use one of the available default names (not case-sensitive): [%s]",
|
||||
entryPointNames.c_str()));
|
||||
}
|
||||
|
||||
return AZ::Success(attributeMaps);
|
||||
}
|
||||
|
||||
void ShaderAssetBuilder2::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const
|
||||
{
|
||||
const AZStd::sys_time_t startTime = AZStd::GetTimeNowTicks();
|
||||
AZStd::string shaderFullPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.c_str(), request.m_sourceFile.c_str(), shaderFullPath, true);
|
||||
// Save .shader file name (no extension and no parent directory path)
|
||||
AZStd::string shaderFileName;
|
||||
AzFramework::StringFunc::Path::GetFileName(request.m_sourceFile.c_str(), shaderFileName);
|
||||
|
||||
// No error checking because the same calls were already executed during CreateJobs()
|
||||
auto descriptorParseOutcome = ShaderBuilderUtility::LoadShaderDataJson(shaderFullPath);
|
||||
RPI::ShaderSourceData shaderSourceData = descriptorParseOutcome.TakeValue();
|
||||
AZStd::string azslFullPath;
|
||||
ShaderBuilderUtility::GetAbsolutePathToAzslFile(shaderFullPath, shaderSourceData.m_source, azslFullPath);
|
||||
AZ_TracePrintf(ShaderAssetBuilder2Name, "Original AZSL File: %s \n", azslFullPath.c_str());
|
||||
|
||||
// The directory where the Azsl file was found must be added to the list of include paths
|
||||
AZStd::string azslFolderPath;
|
||||
AzFramework::StringFunc::Path::GetFolderPath(azslFullPath.c_str(), azslFolderPath);
|
||||
GlobalBuildOptions buildOptions = ReadBuildOptions(ShaderAssetBuilder2Name, azslFolderPath.c_str());
|
||||
|
||||
// Request the list of valid shader platform interfaces for the target platform.
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces = ShaderBuilderUtility::DiscoverEnabledShaderPlatformInterfaces(
|
||||
request.m_platformInfo, shaderSourceData);
|
||||
if (platformInterfaces.empty())
|
||||
{
|
||||
//No work to do. Exit gracefully.
|
||||
AZ_TracePrintf(ShaderAssetBuilder2Name,
|
||||
"No azshader is produced on behalf of %s because all valid RHI backends were disabled for this shader.\n",
|
||||
shaderFullPath.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the time stamp string as sys_time_t, and also convert back to string to make sure it was converted correctly.
|
||||
AZStd::sys_time_t shaderAssetBuildTimestamp = 0;
|
||||
auto shaderAssetBuildTimestampIterator = request.m_jobDescription.m_jobParameters.find(ShaderAssetBuildTimestampParam);
|
||||
if (shaderAssetBuildTimestampIterator != request.m_jobDescription.m_jobParameters.end())
|
||||
{
|
||||
shaderAssetBuildTimestamp = AZStd::stoull(shaderAssetBuildTimestampIterator->second);
|
||||
|
||||
if (AZStd::to_string(shaderAssetBuildTimestamp) != shaderAssetBuildTimestampIterator->second)
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
AZ_Assert(false, "Incorrect conversion of ShaderAssetBuildTimestampParam");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto supervariantList = ShaderBuilderUtility::GetSupervariantListFromShaderSourceData(shaderSourceData);
|
||||
|
||||
RPI::ShaderAssetCreator2 shaderAssetCreator;
|
||||
shaderAssetCreator.Begin(Uuid::CreateRandom());
|
||||
|
||||
shaderAssetCreator.SetName(AZ::Name{shaderFileName.c_str()});
|
||||
shaderAssetCreator.SetDrawListName(Name(shaderSourceData.m_drawListName));
|
||||
shaderAssetCreator.SetShaderAssetBuildTimestamp(shaderAssetBuildTimestamp);
|
||||
|
||||
// The ShaderOptionGroupLayout must be the same across all supervariants because
|
||||
// there can be only a single ShaderVariantTreeAsset per ShaderAsset.
|
||||
// We will store here the one that results when the *.azslin file is
|
||||
// compiled for the default, nameless, supervariant.
|
||||
// For all other supervariants we just make sure the hashes are the same
|
||||
// as this one.
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> finalShaderOptionGroupLayout = nullptr;
|
||||
|
||||
|
||||
// Time to describe the big picture.
|
||||
// 1- Preprocess an AZSL file with MCPP (a C-Preprocessor), and generate a flat AZSL file without #include lines and any macros in it.
|
||||
// Let's call it the Flat-AZSL file. There are two levels of macro definition that need to be merged before we can invoke MCPP:
|
||||
// 1.1- From <GameProject>/Config/shader_global_build_options.json, which we have stored in the local variable @buildOptions.
|
||||
// 1.2- From the "Supervariant" definition key, which can be different for each supervariant.
|
||||
// 2- There will be one Flat-AZSL per supervariant. Each Flat-AZSL will be transpiled to HLSL with AZSLc. This means there will be one HLSL file
|
||||
// per supervariant.
|
||||
// 3- The generated HLSL (one HLSL per supervariant) file may contain C-Preprocessor Macros inserted by AZSLc. And that file will be given to DXC.
|
||||
// DXC has a preprocessor embedded in it. DXC will be executed once for each entry function listed in the .shader file.
|
||||
// There will be one DXIL compiled binary for each entry function. All the DXIL compiled binaries for each supervariant will be combined
|
||||
// in the ROOT ShaderVariantAsset.
|
||||
|
||||
// Remark: In general, the work done by the ShaderVariantAssetBuilder is similar, but it will start from the HLSL file created; in step 2, mentioned above; by this builder,
|
||||
// for each supervariant.
|
||||
|
||||
// At this moment We have global build options that should be merged with the build options that are common
|
||||
// to all the supervariants of this shader.
|
||||
buildOptions.m_compilerArguments.Merge(shaderSourceData.m_compiler);
|
||||
|
||||
for (RHI::ShaderPlatformInterface* shaderPlatformInterface : platformInterfaces)
|
||||
{
|
||||
AZStd::string apiName(shaderPlatformInterface->GetAPIName().GetCStr());
|
||||
AZ_TraceContext("Platform API", apiName);
|
||||
// Signal the begin of shader data for an RHI API.
|
||||
shaderAssetCreator.BeginAPI(shaderPlatformInterface->GetAPIType());
|
||||
|
||||
// Each shaderPlatformInterface has its own azsli header that needs to be prepended to the AZSL file before
|
||||
// preprocessing. We will create a new temporary file that contains the combined data.
|
||||
RHI::PrependArguments args;
|
||||
args.m_sourceFile = azslFullPath.c_str();
|
||||
args.m_prependFile = shaderPlatformInterface->GetAzslHeader(request.m_platformInfo);
|
||||
args.m_addSuffixToFileName = apiName.c_str();
|
||||
args.m_destinationFolder = request.m_tempDirPath.c_str();
|
||||
|
||||
AZStd::string prependedAzslFilePath = RHI::PrependFile(args);
|
||||
if (prependedAzslFilePath == azslFullPath)
|
||||
{
|
||||
// The specific error is already reported by RHI::PrependFile().
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache common AZSLC invokation arguments related with the current RHI Backend.
|
||||
// Each supervariant can, optionally, remove or add more arguments for AZSLc.
|
||||
AZStd::string commonAzslcCompilerParameters =
|
||||
shaderPlatformInterface->GetAzslCompilerParameters(buildOptions.m_compilerArguments);
|
||||
commonAzslcCompilerParameters += " ";
|
||||
commonAzslcCompilerParameters +=
|
||||
shaderPlatformInterface->GetAzslCompilerWarningParameters(buildOptions.m_compilerArguments);
|
||||
AtomShaderConfig::AddParametersFromConfigFile(commonAzslcCompilerParameters, request.m_platformInfo);
|
||||
|
||||
// The register number only makes sense if the platform uses "spaces",
|
||||
// since the register Id of the resource will not change even if the pipeline layout changes.
|
||||
// We can pass in a default ShaderCompilerArguments because all we care about is whether the shaderPlatformInterface
|
||||
// appends the "--use-spaces" flag.
|
||||
const bool platformUsesRegisterSpaces =
|
||||
(AzFramework::StringFunc::Find(commonAzslcCompilerParameters, "--use-spaces") != AZStd::string::npos);
|
||||
|
||||
uint32_t supervariantIndex = 0;
|
||||
for (const auto& supervariantInfo : supervariantList)
|
||||
{
|
||||
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
shaderAssetCreator.BeginSupervariant(supervariantInfo.m_name);
|
||||
|
||||
// Let's combine the global macro definitions, with the macro definitions particular to this
|
||||
// supervariant. Two steps:
|
||||
// 1- Supervariants can specify which macros to remove from the global definitions.
|
||||
AZStd::vector<AZStd::string> macroDefinitionNamesToRemove = supervariantInfo.GetCombinedListOfMacroDefinitionNamesToRemove();
|
||||
PreprocessorOptions preprocessorOptions = buildOptions.m_preprocessorSettings;
|
||||
preprocessorOptions.RemovePredefinedMacros(macroDefinitionNamesToRemove);
|
||||
// 2- Supervariants can specify which macros to add.
|
||||
AZStd::vector<AZStd::string> macroDefinitionsToAdd = supervariantInfo.GetMacroDefinitionsToAdd();
|
||||
preprocessorOptions.m_predefinedMacros.insert(
|
||||
preprocessorOptions.m_predefinedMacros.end(), macroDefinitionsToAdd.begin(), macroDefinitionsToAdd.end());
|
||||
// Run the preprocessor.
|
||||
PreprocessorData output;
|
||||
PreprocessFile(prependedAzslFilePath, output, preprocessorOptions, true, true);
|
||||
RHI::ReportErrorMessages(ShaderAssetBuilder2Name, output.diagnostics);
|
||||
// Dump the preprocessed string as a flat AZSL file with extension .azslin, which will be given to AZSLc to generate the HLSL file.
|
||||
AZStd::string superVariantAzslinStemName = shaderFileName;
|
||||
if (!supervariantInfo.m_name.IsEmpty())
|
||||
{
|
||||
superVariantAzslinStemName += AZStd::string::format("-%s", supervariantInfo.m_name.GetCStr());
|
||||
}
|
||||
AZStd::string azslinFullPath = ShaderBuilderUtility::DumpPreprocessedCode(
|
||||
ShaderAssetBuilder2Name, output.code, request.m_tempDirPath, superVariantAzslinStemName,
|
||||
apiName, true /*add2*/);
|
||||
if (azslinFullPath.empty())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
AZ_TracePrintf(ShaderAssetBuilder2Name, "Preprocessed AZSL File: %s \n", prependedAzslFilePath.c_str());
|
||||
|
||||
// Before transpiling the flat-AZSL(.azslin) file into HLSL it is necessary
|
||||
// to setup the AZSLc arguments as required by the current supervariant.
|
||||
AZStd::string azslcCompilerParameters = supervariantInfo.GetCustomizedArgumentsForAzslc(commonAzslcCompilerParameters);
|
||||
|
||||
// Ready to transpile the azslin file into HLSL.
|
||||
ShaderBuilder::AzslCompiler azslc(azslinFullPath);
|
||||
AZStd::string hlslFullPath = AZStd::string::format("%s_%s.hlsl2", superVariantAzslinStemName.c_str(), apiName.c_str());
|
||||
AzFramework::StringFunc::Path::Join(request.m_tempDirPath.c_str(), hlslFullPath.c_str(), hlslFullPath, true);
|
||||
auto emitFullOutcome = azslc.EmitFullData(azslcCompilerParameters, hlslFullPath, "2");
|
||||
if (!emitFullOutcome.IsSuccess())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
ShaderBuilderUtility::AzslSubProducts::Paths subProductsPaths = emitFullOutcome.TakeValue();
|
||||
|
||||
// In addition to the hlsl file, there are other json files that were generated.
|
||||
// Each output file will become a product.
|
||||
for (int i = 0; i < subProductsPaths.size(); ++i)
|
||||
{
|
||||
AssetBuilderSDK::JobProduct jobProduct;
|
||||
jobProduct.m_productFileName = subProductsPaths[i];
|
||||
static const AZ::Uuid AzslOutcomeType = "{6977AEB1-17AD-4992-957B-23BB2E85B18B}";
|
||||
jobProduct.m_productAssetType = AzslOutcomeType;
|
||||
// uint32_t rhiApiUniqueIndex, uint32_t supervariantIndex, uint32_t subProductType
|
||||
jobProduct.m_productSubID = RPI::ShaderAsset2::MakeProductAssetSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), supervariantIndex,
|
||||
aznumeric_cast<uint32_t>(ShaderBuilderUtility::AzslSubProducts::SubList[i]));
|
||||
jobProduct.m_dependenciesHandled = true;
|
||||
// Note that the output products are not traditional product assets that will be used by the game project.
|
||||
// They are artifacts that are produced once, cached, and used later by other AssetBuilders as a way to centralize
|
||||
// build organization.
|
||||
response.m_outputProducts.push_back(AZStd::move(jobProduct));
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<ShaderFiles> files(new ShaderFiles);
|
||||
AzslData azslData(files);
|
||||
azslData.m_preprocessedFullPath = azslinFullPath;
|
||||
RPI::ShaderResourceGroupLayoutList srgLayoutList;
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
|
||||
BindingDependencies bindingDependencies;
|
||||
RootConstantData rootConstantData;
|
||||
AssetBuilderSDK::ProcessJobResultCode azslJsonReadResult = ShaderBuilderUtility::PopulateAzslDataFromJsonFiles(
|
||||
ShaderAssetBuilder2Name, subProductsPaths, platformUsesRegisterSpaces, azslData, srgLayoutList, shaderOptionGroupLayout,
|
||||
bindingDependencies, rootConstantData);
|
||||
if (azslJsonReadResult != AssetBuilderSDK::ProcessJobResult_Success)
|
||||
|
||||
{
|
||||
response.m_resultCode = azslJsonReadResult;
|
||||
return;
|
||||
}
|
||||
|
||||
shaderAssetCreator.SetSrgLayoutList(srgLayoutList);
|
||||
|
||||
if (!finalShaderOptionGroupLayout)
|
||||
{
|
||||
finalShaderOptionGroupLayout = shaderOptionGroupLayout;
|
||||
shaderAssetCreator.SetShaderOptionGroupLayout(finalShaderOptionGroupLayout);
|
||||
const uint32_t usedShaderOptionBits = shaderOptionGroupLayout->GetBitSize();
|
||||
AZ_TracePrintf(
|
||||
ShaderAssetBuilder2Name, "Note: This shader uses %u of %u available shader variant key bits. \n",
|
||||
usedShaderOptionBits, RPI::ShaderVariantKeyBitCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (finalShaderOptionGroupLayout->GetHash() != shaderOptionGroupLayout->GetHash())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilder2Name, false, "Supervariant %s has a different ShaderOptionGroupLayout",
|
||||
supervariantInfo.m_name.GetCStr())
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Discover entry points & type of programs.
|
||||
MapOfStringToStageType shaderEntryPoints;
|
||||
if (shaderSourceData.m_programSettings.m_entryPoints.empty())
|
||||
{
|
||||
AZ_TracePrintf(
|
||||
ShaderAssetBuilder2Name,
|
||||
"ProgramSettings do not specify entry points, will use GetDefaultEntryPointsFromShader()\n");
|
||||
ShaderBuilderUtility::GetDefaultEntryPointsFromFunctionDataList(azslData.m_functions, shaderEntryPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& entryPoint : shaderSourceData.m_programSettings.m_entryPoints)
|
||||
{
|
||||
shaderEntryPoints[entryPoint.m_name] = entryPoint.m_type;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasRasterProgram = false;
|
||||
auto attributeMapsOutcome = BuildAttributesMap(shaderPlatformInterface, azslData, shaderEntryPoints, hasRasterProgram);
|
||||
if (!attributeMapsOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilder2Name, false, "%s\n", attributeMapsOutcome.GetError().c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
shaderAssetCreator.SetShaderStageAttributeMapList(attributeMapsOutcome.TakeValue());
|
||||
|
||||
// Check if we were canceled before we do any heavy processing of
|
||||
// the shader data (compiling the shader kernels, processing SRG
|
||||
// and pipeline layout data, etc.).
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
RHI::Ptr<RHI::PipelineLayoutDescriptor> pipelineLayoutDescriptor =
|
||||
ShaderBuilderUtility::BuildPipelineLayoutDescriptorForApi(
|
||||
ShaderAssetBuilder2Name, srgLayoutList, shaderEntryPoints, buildOptions.m_compilerArguments, rootConstantData,
|
||||
shaderPlatformInterface, bindingDependencies);
|
||||
if (!pipelineLayoutDescriptor)
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilder2Name, false, "Failed to build pipeline layout descriptor for api=[%s]",
|
||||
shaderPlatformInterface->GetAPIName().GetCStr());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
shaderAssetCreator.SetPipelineLayout(pipelineLayoutDescriptor);
|
||||
|
||||
|
||||
RPI::ShaderInputContract shaderInputContract;
|
||||
RPI::ShaderOutputContract shaderOutputContract;
|
||||
size_t colorAttachmentCount = 0;
|
||||
ShaderBuilderUtility::CreateShaderInputAndOutputContracts(
|
||||
azslData, shaderEntryPoints, *shaderOptionGroupLayout.get(),
|
||||
subProductsPaths[ShaderBuilderUtility::AzslSubProducts::om],
|
||||
subProductsPaths[ShaderBuilderUtility::AzslSubProducts::ia],
|
||||
shaderInputContract, shaderOutputContract, colorAttachmentCount);
|
||||
shaderAssetCreator.SetInputContract(shaderInputContract);
|
||||
shaderAssetCreator.SetOutputContract(shaderOutputContract);
|
||||
|
||||
if (hasRasterProgram)
|
||||
{
|
||||
// Set the various states to what is in the descriptor.
|
||||
const RHI::TargetBlendState& targetBlendState = shaderSourceData.m_blendState;
|
||||
RHI::RenderStates renderStates;
|
||||
renderStates.m_rasterState = shaderSourceData.m_rasterState;
|
||||
renderStates.m_depthStencilState = shaderSourceData.m_depthStencilState;
|
||||
// [GFX TODO][ATOM-930] We should support unique blend states per RT
|
||||
for (size_t i = 0; i < colorAttachmentCount; ++i)
|
||||
{
|
||||
renderStates.m_blendState.m_targets[i] = targetBlendState;
|
||||
}
|
||||
|
||||
shaderAssetCreator.SetRenderStates(renderStates);
|
||||
}
|
||||
|
||||
Outcome<AZStd::string, AZStd::string> hlslSourceCodeOutcome = Utils::ReadFile(hlslFullPath);
|
||||
if (!hlslSourceCodeOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilder2Name, false, "Failed to obtain shader source from %s. [%s]", hlslFullPath.c_str(),
|
||||
hlslSourceCodeOutcome.GetError().c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
AZStd::string hlslSourceCode = hlslSourceCodeOutcome.TakeValue();
|
||||
|
||||
// The root ShaderVariantAsset needs to be created with the known uuid of the source .shader asset because
|
||||
// the ShaderAsset owns a Data::Asset<> reference that gets serialized. It must have the correct uuid
|
||||
// so the root ShaderVariantAsset is found when the ShaderAsset is deserialized.
|
||||
uint32_t rootVariantProductSubId = RPI::ShaderAsset2::MakeProductAssetSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), supervariantIndex,
|
||||
aznumeric_cast<uint32_t>(RPI::ShaderAsset2ProductSubId::RootShaderVariantAsset));
|
||||
auto assetIdOutcome = RPI::AssetUtils::MakeAssetId(shaderFullPath, rootVariantProductSubId);
|
||||
AZ_Assert(assetIdOutcome.IsSuccess(), "Failed to get AssetId from shader %s", shaderFullPath.c_str());
|
||||
const Data::AssetId variantAssetId = assetIdOutcome.TakeValue();
|
||||
|
||||
RPI::ShaderVariantListSourceData::VariantInfo rootVariantInfo;
|
||||
ShaderVariantCreationContext2 shaderVariantCreationContext = {
|
||||
*shaderPlatformInterface,
|
||||
request.m_platformInfo,
|
||||
buildOptions.m_compilerArguments,
|
||||
request.m_tempDirPath,
|
||||
startTime,
|
||||
shaderSourceData,
|
||||
*shaderOptionGroupLayout.get(),
|
||||
shaderEntryPoints,
|
||||
variantAssetId,
|
||||
superVariantAzslinStemName,
|
||||
hlslFullPath,
|
||||
hlslSourceCode};
|
||||
|
||||
|
||||
AZStd::optional<RHI::ShaderPlatformInterface::ByProducts> outputByproducts;
|
||||
auto rootShaderVariantAssetOutcome = ShaderVariantAssetBuilder2::CreateShaderVariantAsset(rootVariantInfo, shaderVariantCreationContext, outputByproducts);
|
||||
if (!rootShaderVariantAssetOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilder2Name, false, "%s\n", rootShaderVariantAssetOutcome.GetError().c_str())
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
Data::Asset<RPI::ShaderVariantAsset2> rootShaderVariantAsset = rootShaderVariantAssetOutcome.TakeValue();
|
||||
|
||||
shaderAssetCreator.SetRootShaderVariantAsset(rootShaderVariantAsset);
|
||||
|
||||
if (!shaderAssetCreator.EndSupervariant())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilder2Name, false, "Failed to create shader asset for supervariant [%s]", supervariantInfo.m_name.GetCStr())
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
// Time to save the root variant related assets in the cache.
|
||||
AssetBuilderSDK::JobProduct assetProduct;
|
||||
if (!ShaderVariantAssetBuilder2::SerializeOutShaderVariantAsset(
|
||||
rootShaderVariantAsset, superVariantAzslinStemName, request.m_tempDirPath, *shaderPlatformInterface,
|
||||
rootVariantProductSubId,
|
||||
assetProduct))
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
response.m_outputProducts.push_back(assetProduct);
|
||||
|
||||
if (outputByproducts)
|
||||
{
|
||||
// add byproducts as job output products:
|
||||
uint32_t subProductType = aznumeric_cast<uint32_t>(RPI::ShaderAsset2ProductSubId::FirstByProduct);
|
||||
for (const AZStd::string& byproduct : outputByproducts.value().m_intermediatePaths)
|
||||
{
|
||||
AssetBuilderSDK::JobProduct jobProduct;
|
||||
jobProduct.m_productFileName = byproduct;
|
||||
jobProduct.m_productAssetType = Uuid::CreateName("DebugInfoByProduct-PdbOrDxilTxt");
|
||||
jobProduct.m_productSubID = RPI::ShaderAsset2::MakeProductAssetSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), supervariantIndex,
|
||||
subProductType++);
|
||||
response.m_outputProducts.push_back(AZStd::move(jobProduct));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
supervariantIndex++;
|
||||
|
||||
} // end for the supervariant
|
||||
|
||||
shaderAssetCreator.EndAPI();
|
||||
|
||||
} // end for all ShaderPlatformInterfaces
|
||||
|
||||
Data::Asset<RPI::ShaderAsset2> shaderAsset;
|
||||
if (!shaderAssetCreator.End(shaderAsset))
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SerializeOutShaderAsset(shaderAsset, request.m_tempDirPath, response))
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
|
||||
const AZStd::sys_time_t endTime = AZStd::GetTimeNowTicks();
|
||||
const AZStd::sys_time_t deltaTime = endTime - startTime;
|
||||
const float elapsedTimeSeconds = (float)(deltaTime) / (float)AZStd::GetTimeTicksPerSecond();
|
||||
|
||||
AZ_TracePrintf(ShaderAssetBuilder2Name, "Finished processing %s in %.2f seconds\n", request.m_sourceFile.c_str(), elapsedTimeSeconds);
|
||||
|
||||
ShaderBuilderUtility::LogProfilingData(ShaderAssetBuilder2Name, shaderFileName);
|
||||
}
|
||||
|
||||
} // ShaderBuilder
|
||||
} // AZ
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AssetBuilderSDK/AssetBuilderBusses.h>
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
|
||||
#include <Atom/RHI.Reflect/Base.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Data
|
||||
{
|
||||
class AssetHandler;
|
||||
}
|
||||
|
||||
namespace RHI
|
||||
{
|
||||
class ShaderPlatformInterface;
|
||||
}
|
||||
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
struct AzslData;
|
||||
|
||||
class ShaderAssetBuilder2
|
||||
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(ShaderAssetBuilder2, "{C94DA151-82BC-4475-86FA-E6C92A0BD6F8}");
|
||||
|
||||
static constexpr const char* ShaderAssetBuilder2JobKey = "Shader Asset 2";
|
||||
|
||||
ShaderAssetBuilder2() = default;
|
||||
~ShaderAssetBuilder2() = default;
|
||||
|
||||
// Asset Builder Callback Functions ...
|
||||
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const;
|
||||
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const;
|
||||
|
||||
// AssetBuilderSDK::AssetBuilderCommandBus interface overrides ...
|
||||
void ShutDown() override { };
|
||||
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(ShaderAssetBuilder2);
|
||||
};
|
||||
|
||||
} // ShaderBuilder
|
||||
} // AZ
|
||||
@@ -29,8 +29,7 @@
|
||||
|
||||
#include <Atom/RPI.Edit/Common/JsonReportingHelper.h>
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h> // DEPRECATED - [ATOM-15472]
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
|
||||
|
||||
#include <Atom/RHI.Edit/Utils.h>
|
||||
@@ -87,68 +86,6 @@ namespace AZ
|
||||
AzFramework::StringFunc::Path::ReplaceExtension(absoluteAzslPath, "azsl");
|
||||
}
|
||||
|
||||
static bool LoadShaderResourceGroupAssets(
|
||||
[[maybe_unused]] const char* builderName,
|
||||
const SrgDataContainer& resourceGroups,
|
||||
ShaderResourceGroupAssets& srgAssets)
|
||||
{
|
||||
bool readSRGsSuccessfuly = true;
|
||||
|
||||
// Load all SRGs included in source file
|
||||
for (const SrgData& srgData : resourceGroups)
|
||||
{
|
||||
Data::AssetId assetId = {};
|
||||
AZStd::string srgFilePath = "";
|
||||
|
||||
srgFilePath = srgData.m_containingFileName;
|
||||
AzFramework::StringFunc::Path::Normalize(srgFilePath);
|
||||
|
||||
bool assetFound = false;
|
||||
Data::AssetInfo sourceInfo;
|
||||
AZStd::string watchFolder;
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(assetFound, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, srgFilePath.c_str(), sourceInfo, watchFolder);
|
||||
|
||||
if (!assetFound)
|
||||
{
|
||||
AZ_Error(builderName, false, "Could not find asset identified by path '%s'", srgFilePath.c_str());
|
||||
readSRGsSuccessfuly = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
assetId.m_guid = sourceInfo.m_assetId.m_guid;
|
||||
assetId.m_subId = static_cast<uint32_t>(AZStd::hash<AZStd::string>()(srgData.m_name) & 0xFFFFFFFF);
|
||||
|
||||
Data::Asset<RPI::ShaderResourceGroupAsset> asset = Data::AssetManager::Instance().GetAsset<RPI::ShaderResourceGroupAsset>(
|
||||
assetId, AZ::Data::AssetLoadBehavior::PreLoad);
|
||||
asset.BlockUntilLoadComplete();
|
||||
if (!asset.IsReady())
|
||||
{
|
||||
using Status = Data::AssetData::AssetStatus;
|
||||
AZStd::string statusString = asset.GetStatus() == Status::Loading ? "loading"
|
||||
: asset.GetStatus() == Status::ReadyPreNotify ? "ready-pre-notify"
|
||||
: asset.GetStatus() == Status::Error ? "error" : "not-loaded/ready/unknown";
|
||||
|
||||
AZ_Error(builderName, false, "Searching SRG [%s]: Could not load SRG asset. (asset status [%s]) AssetId='%s' Path='%s'",
|
||||
srgData.m_name.c_str(),
|
||||
statusString.c_str(),
|
||||
assetId.ToString<AZStd::string>().c_str(), srgFilePath.c_str());
|
||||
readSRGsSuccessfuly = false;
|
||||
continue;
|
||||
}
|
||||
else if (!asset->IsValid())
|
||||
{
|
||||
AZ_Error(builderName, false, "SRG asset has no layout information. AssetId='%s' Path='%s'",
|
||||
assetId.ToString<AZStd::string>().c_str(), srgFilePath.c_str());
|
||||
readSRGsSuccessfuly = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
srgAssets.push_back(asset);
|
||||
}
|
||||
|
||||
return readSRGsSuccessfuly;
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<ShaderFiles> PrepareSourceInput(
|
||||
[[maybe_unused]] const char* builderName,
|
||||
const AZStd::string& shaderAssetSourcePath,
|
||||
@@ -171,87 +108,6 @@ namespace AZ
|
||||
return files;
|
||||
}
|
||||
|
||||
|
||||
//! [GFX TODO] [ATOM-15472] Deprecated, remove when this ticket is addressed.
|
||||
AssetBuilderSDK::ProcessJobResultCode PopulateAzslDataFromJsonFiles(
|
||||
const char* builderName,
|
||||
const AzslSubProducts::Paths& pathOfJsonFiles,
|
||||
AzslData& azslData,
|
||||
ShaderResourceGroupAssets& srgAssets,
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout,
|
||||
BindingDependencies& bindingDependencies,
|
||||
RootConstantData& rootConstantData)
|
||||
{
|
||||
AzslCompiler azslc(azslData.m_preprocessedFullPath); // set the input file for eventual error messages, but the compiler won't be called on it.
|
||||
bool allReadSuccess = true;
|
||||
// read: input assembly reflection
|
||||
// shader resource group reflection
|
||||
// options reflection
|
||||
// binding dependencies reflection
|
||||
int indicesOfInterest[] = { AzslSubProducts::ia, AzslSubProducts::srg, AzslSubProducts::options, AzslSubProducts::bindingdep };
|
||||
AZStd::unordered_map<int, Outcome<rapidjson::Document, AZStd::string>> outcomes;
|
||||
for (int i : indicesOfInterest)
|
||||
{
|
||||
outcomes[i] = JsonSerializationUtils::ReadJsonFile(pathOfJsonFiles[i]);
|
||||
if (!outcomes[i].IsSuccess())
|
||||
{
|
||||
AZ_Error(builderName, false, "%s", outcomes[i].GetError().c_str());
|
||||
allReadSuccess = false;
|
||||
}
|
||||
}
|
||||
if (!allReadSuccess)
|
||||
{
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// Get full list of functions eligible for vertex shader entry points
|
||||
// along with metadata for constructing the InputAssembly for each of them
|
||||
if (!azslc.ParseIaPopulateFunctionData(outcomes[AzslSubProducts::ia].GetValue(), azslData.m_functions))
|
||||
{
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// Each SRG is built as a separate asset in the SrgLayoutBuilder, here we just
|
||||
// build the list and load the data from multiple dependency assets.
|
||||
if (!azslc.ParseSrgPopulateSrgData(outcomes[AzslSubProducts::srg].GetValue(), azslData.m_srgData))
|
||||
{
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// Add all Shader Resource Group Assets that were defined in the shader code to the shader asset
|
||||
if (!LoadShaderResourceGroupAssets(builderName, azslData.m_srgData, srgAssets))
|
||||
{
|
||||
AZ_Error(builderName, false, "Failed to obtain shader resource group assets");
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// The shader options define what options are available, what are the allowed values/range
|
||||
// for each option and what is its default value.
|
||||
if (!azslc.ParseOptionsPopulateOptionGroupLayout(outcomes[AzslSubProducts::options].GetValue(), shaderOptionGroupLayout))
|
||||
{
|
||||
AZ_Error(builderName, false, "Failed to find a valid list of shader options!");
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// It analyzes the shader external bindings (all SRG contents)
|
||||
// and informs us on register indexes and shader stages using these resources
|
||||
if (!azslc.ParseBindingdepPopulateBindingDependencies(outcomes[AzslSubProducts::bindingdep].GetValue(), bindingDependencies)) // consuming data from binding-dep
|
||||
{
|
||||
AZ_Error(builderName, false, "Failed to obtain shader resource binding reflection");
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// access the root constants reflection
|
||||
if (!azslc.ParseSrgPopulateRootConstantData(outcomes[AzslSubProducts::srg].GetValue(), rootConstantData)) // consuming data from --srg ("InlineConstantBuffer" subjson section)
|
||||
{
|
||||
AZ_Error(builderName, false, "Failed to obtain root constant data reflection");
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
return AssetBuilderSDK::ProcessJobResult_Success;
|
||||
}
|
||||
|
||||
|
||||
AssetBuilderSDK::ProcessJobResultCode PopulateAzslDataFromJsonFiles(
|
||||
const char* builderName,
|
||||
const AzslSubProducts::Paths& pathOfJsonFiles,
|
||||
@@ -361,7 +217,8 @@ namespace AZ
|
||||
}
|
||||
|
||||
//! the binding dependency structure may store lots of high level function names which are not entry points
|
||||
static void PruneNonEntryFunctions(BindingDependencies& bindingDependencies /*inout*/, const MapOfStringToStageType& shaderEntryPoints)
|
||||
static void PruneNonEntryFunctions(
|
||||
BindingDependencies& bindingDependencies /*inout*/, const MapOfStringToStageType& shaderEntryPoints)
|
||||
{
|
||||
auto cleaner = [&shaderEntryPoints](BindingDependencies::FunctionsNameVector& functionVector)
|
||||
{
|
||||
@@ -387,120 +244,6 @@ namespace AZ
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
RHI::Ptr<RHI::PipelineLayoutDescriptor> BuildPipelineLayoutDescriptorForApi(
|
||||
[[maybe_unused]] const char* builderName,
|
||||
RHI::ShaderPlatformInterface* shaderPlatformInterface,
|
||||
BindingDependencies& bindingDependencies /*inout*/,
|
||||
const ShaderResourceGroupAssets& srgAssets,
|
||||
const MapOfStringToStageType& shaderEntryPoints,
|
||||
const RHI::ShaderCompilerArguments& shaderCompilerArguments,
|
||||
const RootConstantData* rootConstantData /*= nullptr*/)
|
||||
{
|
||||
PruneNonEntryFunctions(bindingDependencies, shaderEntryPoints);
|
||||
|
||||
// Translates from a list of function names that use a resource to a shader stage mask.
|
||||
auto getRHIShaderStageMask = [&shaderEntryPoints](const BindingDependencies::FunctionsNameVector& functions)
|
||||
{
|
||||
RHI::ShaderStageMask mask = RHI::ShaderStageMask::None;
|
||||
// Iterate through all the functions that are using the resource.
|
||||
for (const auto& functionName : functions)
|
||||
{
|
||||
// Search the function name into the list of valid entry points into the shader.
|
||||
auto findId = AZStd::find_if(shaderEntryPoints.begin(), shaderEntryPoints.end(), [&functionName, &mask](const auto& item)
|
||||
{
|
||||
return item.first == functionName;
|
||||
});
|
||||
|
||||
if (findId != shaderEntryPoints.end())
|
||||
{
|
||||
// Use the entry point shader stage type to calculate the mask.
|
||||
RHI::ShaderHardwareStage hardwareStage = ToAssetBuilderShaderType(findId->second);
|
||||
mask |= static_cast<RHI::ShaderStageMask>(AZ_BIT(static_cast<uint32_t>(RHI::ToRHIShaderStage(hardwareStage))));
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
};
|
||||
|
||||
// Build general PipelineLayoutDescriptor data that is provided for all platforms
|
||||
RHI::Ptr<RHI::PipelineLayoutDescriptor> pipelineLayoutDescriptor = shaderPlatformInterface->CreatePipelineLayoutDescriptor();
|
||||
RHI::ShaderPlatformInterface::ShaderResourceGroupInfoList srgInfos;
|
||||
for (const auto& srgAsset : srgAssets)
|
||||
{
|
||||
// Search the binding info for a Shader Resource Group.
|
||||
AZStd::string_view srgName = srgAsset->GetName().GetStringView();
|
||||
const BindingDependencies::SrgResources* srgResources = bindingDependencies.GetSrg(srgName);
|
||||
if (!srgResources)
|
||||
{
|
||||
AZ_Error(builderName, false, "SRG %s not found in the dependency dataset", srgName.data());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RHI::ShaderResourceGroupBindingInfo srgBindingInfo;
|
||||
srgBindingInfo.m_spaceId = srgResources->m_registerSpace;
|
||||
const RHI::ShaderResourceGroupLayout* layout = srgAsset->GetLayout(shaderPlatformInterface->GetAPIType());
|
||||
// Calculate the binding in for the constant data. All constant data share the same binding info.
|
||||
srgBindingInfo.m_constantDataBindingInfo = {
|
||||
getRHIShaderStageMask(srgResources->m_srgConstantsDependencies.m_binding.m_dependentFunctions),
|
||||
srgResources->m_srgConstantsDependencies.m_binding.m_registerId };
|
||||
// Calculate the binding info for each resource of the Shader Resource Group.
|
||||
for (auto const& resource : srgResources->m_resources)
|
||||
{
|
||||
auto const& resourceInfo = resource.second;
|
||||
srgBindingInfo.m_resourcesRegisterMap.insert(
|
||||
{ AZ::Name(resourceInfo.m_selfName),
|
||||
RHI::ResourceBindingInfo(getRHIShaderStageMask(resourceInfo.m_dependentFunctions), resourceInfo.m_registerId) });
|
||||
}
|
||||
pipelineLayoutDescriptor->AddShaderResourceGroupLayoutInfo(*layout, srgBindingInfo);
|
||||
srgInfos.push_back(RHI::ShaderPlatformInterface::ShaderResourceGroupInfo{ layout, srgBindingInfo });
|
||||
}
|
||||
|
||||
RHI::Ptr<RHI::ConstantsLayout> rootConstantsLayout = RHI::ConstantsLayout::Create();
|
||||
if (rootConstantData)
|
||||
{
|
||||
for (const auto& constantData : rootConstantData->m_constants)
|
||||
{
|
||||
RHI::ShaderInputConstantDescriptor rootConstantDesc(
|
||||
constantData.m_nameId, constantData.m_constantByteOffset, constantData.m_constantByteSize,
|
||||
rootConstantData->m_bindingInfo.m_registerId);
|
||||
|
||||
rootConstantsLayout->AddShaderInput(rootConstantDesc);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rootConstantsLayout->Finalize())
|
||||
{
|
||||
AZ_Error(builderName, false, "Failed to finalize root constants layout");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pipelineLayoutDescriptor->SetRootConstantsLayout(*rootConstantsLayout);
|
||||
|
||||
RHI::ShaderPlatformInterface::RootConstantsInfo rootConstantInfo;
|
||||
if (rootConstantData)
|
||||
{
|
||||
rootConstantInfo.m_spaceId = rootConstantData->m_bindingInfo.m_space;
|
||||
rootConstantInfo.m_registerId = rootConstantData->m_bindingInfo.m_registerId;
|
||||
}
|
||||
else
|
||||
{
|
||||
RootConstantData dummyRootConstantData;
|
||||
rootConstantInfo.m_spaceId = dummyRootConstantData.m_bindingInfo.m_space;
|
||||
rootConstantInfo.m_registerId = dummyRootConstantData.m_bindingInfo.m_registerId;
|
||||
}
|
||||
rootConstantInfo.m_totalSizeInBytes = rootConstantsLayout->GetDataSize();
|
||||
|
||||
// Build platform-specific PipelineLayoutDescriptor data, and finalize
|
||||
if (!shaderPlatformInterface->BuildPipelineLayoutDescriptor(
|
||||
pipelineLayoutDescriptor, srgInfos, rootConstantInfo, shaderCompilerArguments))
|
||||
{
|
||||
AZ_Error(builderName, false, "Failed to build pipeline layout descriptor");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return pipelineLayoutDescriptor;
|
||||
}
|
||||
|
||||
static AZStd::string DumpCode(
|
||||
[[maybe_unused]] const char* builderName,
|
||||
@@ -539,14 +282,8 @@ namespace AZ
|
||||
return finalFilePath;
|
||||
}
|
||||
|
||||
// [GFX TODO] Remove 'add2' when [ATOM-15472]
|
||||
AZStd::string DumpPreprocessedCode(const char* builderName, const AZStd::string& preprocessedCode, const AZStd::string& tempDirPath, const AZStd::string& stemName, const AZStd::string& apiTypeString, bool add2)
|
||||
AZStd::string DumpPreprocessedCode(const char* builderName, const AZStd::string& preprocessedCode, const AZStd::string& tempDirPath, const AZStd::string& stemName, const AZStd::string& apiTypeString)
|
||||
{
|
||||
if (add2)
|
||||
{
|
||||
return DumpCode(builderName, preprocessedCode, tempDirPath, stemName, apiTypeString, "azslin2");
|
||||
}
|
||||
|
||||
return DumpCode(builderName, preprocessedCode, tempDirPath, stemName, apiTypeString, "azslin");
|
||||
}
|
||||
|
||||
@@ -584,8 +321,7 @@ namespace AZ
|
||||
AZStd::remove_if(AZ_BEGIN_END(platformInterfaces),
|
||||
[&](const RHI::ShaderPlatformInterface* shaderPlatformInterface) {
|
||||
return !shaderPlatformInterface ||
|
||||
shaderSourceData.IsRhiBackendDisabled(shaderPlatformInterface->GetAPIName()) ||
|
||||
(shaderPlatformInterface->GetAPIUniqueIndex() == static_cast<uint32_t>(AZ::RHI::APIIndex::Null));
|
||||
shaderSourceData.IsRhiBackendDisabled(shaderPlatformInterface->GetAPIName());
|
||||
}),
|
||||
platformInterfaces.end());
|
||||
return platformInterfaces;
|
||||
@@ -718,15 +454,7 @@ namespace AZ
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t MakeAzslBuildProductSubId(RPI::ShaderAssetSubId subId, RHI::APIType apiType)
|
||||
{
|
||||
auto subIdMaxEnumerator = RPI::ShaderAssetSubId::GeneratedHlslSource;
|
||||
// separate bit space between subid enum, and api-type:
|
||||
int shiftLeft = static_cast<uint32_t>(log2(static_cast<uint32_t>(subIdMaxEnumerator))) + 1;
|
||||
return static_cast<uint32_t>(subId) + (apiType << shiftLeft);
|
||||
}
|
||||
|
||||
Outcome<AZStd::string, AZStd::string> ObtainBuildArtifactPathFromShaderAssetBuilder2(
|
||||
Outcome<AZStd::string, AZStd::string> ObtainBuildArtifactPathFromShaderAssetBuilder(
|
||||
const uint32_t rhiUniqueIndex, const AZStd::string& platformIdentifier, const AZStd::string& shaderJsonPath,
|
||||
const uint32_t supervariantIndex, RPI::ShaderAssetSubId shaderAssetSubId)
|
||||
{
|
||||
@@ -749,12 +477,12 @@ namespace AZ
|
||||
platformId = AzFramework::PlatformId::IOS;
|
||||
}
|
||||
|
||||
uint32_t assetSubId = RPI::ShaderAsset2::MakeProductAssetSubId(rhiUniqueIndex, supervariantIndex, aznumeric_cast<uint32_t>(shaderAssetSubId));
|
||||
uint32_t assetSubId = RPI::ShaderAsset::MakeProductAssetSubId(rhiUniqueIndex, supervariantIndex, aznumeric_cast<uint32_t>(shaderAssetSubId));
|
||||
auto assetIdOutcome = RPI::AssetUtils::MakeAssetId(shaderJsonPath, assetSubId);
|
||||
if (!assetIdOutcome.IsSuccess())
|
||||
{
|
||||
return Failure(AZStd::string::format(
|
||||
"Missing ShaderAssetBuilder2 product %s, for sub %d", shaderJsonPath.c_str(), (uint32_t)shaderAssetSubId));
|
||||
"Missing ShaderAssetBuilder product %s, for sub %d", shaderJsonPath.c_str(), (uint32_t)shaderAssetSubId));
|
||||
}
|
||||
|
||||
Data::AssetId assetId = assetIdOutcome.TakeValue();
|
||||
@@ -778,113 +506,6 @@ namespace AZ
|
||||
return AZ::Success(assetFullPath);
|
||||
}
|
||||
|
||||
Outcome<AzslSubProducts::Paths> ObtainBuildArtifactsFromAzslBuilder([[maybe_unused]] const char* builderName, const AZStd::string& sourceFullPath, RHI::APIType apiType, const AZStd::string& platform)
|
||||
{
|
||||
AzslSubProducts::Paths products;
|
||||
|
||||
// platform id from identifier
|
||||
AzFramework::PlatformId platformId = AzFramework::PlatformId::PC;
|
||||
if (platform == "pc")
|
||||
{
|
||||
platformId = AzFramework::PlatformId::PC;
|
||||
}
|
||||
else if (platform == "mac")
|
||||
{
|
||||
platformId = AzFramework::PlatformId::MAC_ID;
|
||||
}
|
||||
else if (platform == "android")
|
||||
{
|
||||
platformId = AzFramework::PlatformId::ANDROID_ID;
|
||||
}
|
||||
else if (platform == "ios")
|
||||
{
|
||||
platformId = AzFramework::PlatformId::IOS;
|
||||
}
|
||||
|
||||
for (RPI::ShaderAssetSubId sub : AzslSubProducts::SubList)
|
||||
{
|
||||
uint32_t assetSubId = MakeAzslBuildProductSubId(sub, apiType);
|
||||
auto assetIdOutcome = RPI::AssetUtils::MakeAssetId(sourceFullPath, assetSubId);
|
||||
AZ_Error(builderName, assetIdOutcome.IsSuccess(), "Missing AZSL product %s, for sub %d", sourceFullPath.c_str(), (uint32_t)sub);
|
||||
if (!assetIdOutcome.IsSuccess())
|
||||
{
|
||||
return Failure();
|
||||
}
|
||||
Data::AssetId assetId = assetIdOutcome.TakeValue();
|
||||
// get the relative path:
|
||||
AZStd::string assetPath;
|
||||
Data::AssetCatalogRequestBus::BroadcastResult(assetPath, &Data::AssetCatalogRequests::GetAssetPathById, assetId);
|
||||
|
||||
// get the root:
|
||||
AZStd::string assetRoot = AzToolsFramework::PlatformAddressedAssetCatalog::GetAssetRootForPlatform(platformId);
|
||||
// join
|
||||
AZStd::string assetFullPath;
|
||||
AzFramework::StringFunc::Path::Join(assetRoot.c_str(), assetPath.c_str(), assetFullPath);
|
||||
bool fileExists = IO::FileIOBase::GetInstance()->Exists(assetFullPath.c_str()) && !IO::FileIOBase::GetInstance()->IsDirectory(assetFullPath.c_str());
|
||||
if (!fileExists)
|
||||
{
|
||||
return Failure();
|
||||
}
|
||||
products.push_back(assetFullPath);
|
||||
}
|
||||
return AZ::Success(products);
|
||||
}
|
||||
|
||||
// DEPRECATED [ATOM-15472]
|
||||
// See header for info.
|
||||
// REMARK: The approach to string searching and matching done in this function is kind of naive
|
||||
// because the strings can match text within a comment block, etc. So it is not 100% fool proof.
|
||||
// We would need proper grammar parsing to reach 100% confidence.
|
||||
// [GFX TODO][ATOM-5302][ATOM-5308] The following function will be removed once, both, [ATOM-5302] & [ATOM-5308] are addressed, and
|
||||
// azslc allows redundant SrgSemantics for "partial" qualified SRGs.
|
||||
SrgSkipFileResult ShouldSkipFileForSrgProcessing([[maybe_unused]] const char* builderName, const AZStd::string_view fullPath)
|
||||
{
|
||||
AZ::IO::FileIOStream stream(fullPath.data(), AZ::IO::OpenMode::ModeRead);
|
||||
if (!stream.IsOpen())
|
||||
{
|
||||
AZ_Warning(builderName, false, "\"%s\" source file could not be opened.", fullPath.data());
|
||||
return SrgSkipFileResult::Error;
|
||||
}
|
||||
|
||||
if (!stream.CanRead())
|
||||
{
|
||||
AZ_Warning(builderName, false, "\"%s\" source file could not be read.", fullPath.data());
|
||||
return SrgSkipFileResult::Error;
|
||||
}
|
||||
|
||||
// Do a quick check for "ShaderResourceGroup" to determine if this file might even have a ShaderResourceGroup to parse.
|
||||
AZStd::string fileContents;
|
||||
fileContents.resize(stream.GetLength());
|
||||
stream.Read(stream.GetLength(), fileContents.data());
|
||||
|
||||
static const AZStd::regex partialSrgRegex("\n\\s*partial\\s+ShaderResourceGroup\\s+", AZStd::regex::ECMAScript);
|
||||
if (AZStd::regex_search(fileContents.data(), partialSrgRegex))
|
||||
{
|
||||
// It is considered a programmer's error if a file declares both, non-partial and partial SRGs.
|
||||
static const AZStd::regex srgRegex("\n\\s*ShaderResourceGroup\\s+", AZStd::regex::ECMAScript);
|
||||
if (AZStd::regex_search(fileContents.data(), srgRegex))
|
||||
{
|
||||
AZ_Error(builderName, false, "\"%s\" defines both partial and non-partial SRGs.", fullPath.data());
|
||||
return SrgSkipFileResult::Error;
|
||||
}
|
||||
// We should skip files that define partial Srgs because an srgi file will eventually
|
||||
// include it.
|
||||
return SrgSkipFileResult::SkipFile;
|
||||
}
|
||||
|
||||
// This is an optimization to avoid unnecessary preprocessing a whole tree of azsli files; we can detect when a
|
||||
// ShaderResourceGroupAsset wouldn't be produced and return early. Note, we could remove this early-return check
|
||||
// if the preprocessing code below is updated to not follow include paths [ATOM-5302].
|
||||
// (Note this optimization is not valid for srgi files because those do require scanning all include paths)"
|
||||
if (fileContents.find("ShaderResourceGroup") == AZStd::string::npos)
|
||||
{
|
||||
// No ShaderResourceGroup in this file, so there's nothing to do. Create no jobs and report success.
|
||||
return SrgSkipFileResult::SkipFile;
|
||||
}
|
||||
|
||||
return SrgSkipFileResult::ContinueProcess;
|
||||
}
|
||||
|
||||
RHI::Ptr<RHI::PipelineLayoutDescriptor> BuildPipelineLayoutDescriptorForApi(
|
||||
[[maybe_unused]] const char* builderName, const RPI::ShaderResourceGroupLayoutList& srgLayoutList, const MapOfStringToStageType& shaderEntryPoints,
|
||||
const RHI::ShaderCompilerArguments& shaderCompilerArguments, const RootConstantData& rootConstantData,
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
|
||||
#include <Atom/RPI.Edit/Shader/ShaderSourceData.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderResourceGroupAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
|
||||
#include "AzslData.h"
|
||||
|
||||
@@ -32,7 +31,6 @@ namespace AZ
|
||||
struct BindingDependencies;
|
||||
struct RootConstantData;
|
||||
|
||||
using ShaderResourceGroupAssets = AZStd::fixed_vector<Data::Asset<RPI::ShaderResourceGroupAsset>, RHI::Limits::Pipeline::ShaderResourceGroupCountMax>;
|
||||
using MapOfStringToStageType = AZStd::unordered_map<AZStd::string, RPI::ShaderStageType>;
|
||||
|
||||
namespace ShaderBuilderUtility
|
||||
@@ -53,7 +51,7 @@ namespace AZ
|
||||
|
||||
using SubId = RPI::ShaderAssetSubId;
|
||||
// product sub id enumerators:
|
||||
static constexpr SubId SubList[] = {SubId::PostPreprocessingPureAzsl,
|
||||
static constexpr SubId SubList[] = {SubId::FlatAzsl,
|
||||
SubId::IaJson,
|
||||
SubId::OmJson,
|
||||
SubId::SrgJson,
|
||||
@@ -66,20 +64,6 @@ namespace AZ
|
||||
using Paths = AZStd::fixed_vector<AZStd::string, AZ_ARRAY_SIZE(SubList)>;
|
||||
};
|
||||
|
||||
//! [GFX TODO] [ATOM-15472] Deprecated, remove when this ticket is addressed.
|
||||
//! Collects and generates the necessary data for compiling a shader.
|
||||
//! @azslData must have paths correctly set.
|
||||
//! shaderOptionGroupLayout, azslData, srgAssets get the output data.
|
||||
AssetBuilderSDK::ProcessJobResultCode PopulateAzslDataFromJsonFiles(
|
||||
const char* builderName,
|
||||
const AzslSubProducts::Paths& pathOfJsonFiles,
|
||||
AzslData& azslData,
|
||||
ShaderResourceGroupAssets& srgAssets,
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout,
|
||||
BindingDependencies& bindingDependencies,
|
||||
RootConstantData& rootConstantData
|
||||
);
|
||||
|
||||
//! Collects all the JSON files generated during AZSL compilation and loads the data as objects.
|
||||
//! @azslData must have paths correctly set.
|
||||
//! @azslData, @srgLayoutList, @shaderOptionGroupLayout, @bindingDependencies and @rootConstantData get the output data.
|
||||
@@ -92,22 +76,6 @@ namespace AZ
|
||||
|
||||
RHI::ShaderHardwareStage ToAssetBuilderShaderType(RPI::ShaderStageType stageType);
|
||||
|
||||
//! Must be called before shaderPlatformInterface->CompilePlatformInternal()
|
||||
//! This function will prune non entry functions from BindingDependencies and use the
|
||||
//! rest of input data to create a pipeline layout descriptor.
|
||||
//! The pipeline layout descriptor is returned, but the same data will also be set into the @shaderPlatformInterface
|
||||
//! object, which is why it is important to call this method before calling shaderPlatformInterface->CompilePlatformInternal().
|
||||
RHI::Ptr<RHI::PipelineLayoutDescriptor> BuildPipelineLayoutDescriptorForApi(
|
||||
const char* builderName,
|
||||
RHI::ShaderPlatformInterface* shaderPlatformInterface,
|
||||
BindingDependencies& bindingDependencies /*inout*/,
|
||||
const ShaderResourceGroupAssets& srgAssets,
|
||||
const MapOfStringToStageType& shaderEntryPoints,
|
||||
const RHI::ShaderCompilerArguments& shaderCompilerArguments,
|
||||
const RootConstantData* rootConstantData = nullptr
|
||||
);
|
||||
|
||||
|
||||
//! Must be called before shaderPlatformInterface->CompilePlatformInternal()
|
||||
//! This function will prune non entry functions from BindingDependencies and use the
|
||||
//! rest of input data to create a pipeline layout descriptor.
|
||||
@@ -145,8 +113,7 @@ namespace AZ
|
||||
const AZStd::string& preprocessedCode,
|
||||
const AZStd::string& tempDirPath,
|
||||
const AZStd::string& preprocessedFileName,
|
||||
const AZStd::string& apiTypeString = "",
|
||||
bool add2 = false); // [GFX TODO] Remove add2 when [ATOM-15472]
|
||||
const AZStd::string& apiTypeString = "");
|
||||
|
||||
//! Create a file from a string's content.
|
||||
//! That file will be named filename.api.azsl.prepend
|
||||
@@ -181,25 +148,11 @@ namespace AZ
|
||||
|
||||
void LogProfilingData(const char* builderName, AZStd::string_view shaderPath);
|
||||
|
||||
//! Job products sub id generation helper for AzslBuilder
|
||||
uint32_t MakeAzslBuildProductSubId(RPI::ShaderAssetSubId subId, RHI::APIType apiType);
|
||||
|
||||
//! Returns the asset path of a product artifact produced by ShaderAssetBuilder2.
|
||||
Outcome<AZStd::string, AZStd::string> ObtainBuildArtifactPathFromShaderAssetBuilder2(
|
||||
//! Returns the asset path of a product artifact produced by ShaderAssetBuilder.
|
||||
Outcome<AZStd::string, AZStd::string> ObtainBuildArtifactPathFromShaderAssetBuilder(
|
||||
const uint32_t rhiUniqueIndex, const AZStd::string& platformIdentifier, const AZStd::string& shaderJsonPath,
|
||||
const uint32_t supervariantIndex, RPI::ShaderAssetSubId shaderAssetSubId);
|
||||
|
||||
//! Reconstructs the expected output product paths of the AzslBuilder (from the 2 arguments @azslSourceFullPath and @apiType)
|
||||
Outcome<AzslSubProducts::Paths> ObtainBuildArtifactsFromAzslBuilder(const char* builderName, const AZStd::string& azslSourceFullPath, RHI::APIType apiType, const AZStd::string& platform);
|
||||
|
||||
//! Returns true if a file should skip processing.
|
||||
//! Should be called only for non *.srgi files.
|
||||
//! If the file contains "partial ShaderResourceGroup" (validated through a proper regular expression)
|
||||
//! then it is skippable. If this same file also defines non-partial SRGs it will be skipped with error.
|
||||
//! If the file doesn't contain "ShaderResourceGroup" then it does not define a ShaderResourceGroup
|
||||
//! and it is skippable too.
|
||||
enum class SrgSkipFileResult { Error, SkipFile, ContinueProcess };
|
||||
SrgSkipFileResult ShouldSkipFileForSrgProcessing(const char* builderName, const AZStd::string_view fullPath);
|
||||
} // ShaderBuilderUtility namespace
|
||||
} // ShaderBuilder namespace
|
||||
} // AZ
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,25 +29,34 @@ namespace AZ
|
||||
{
|
||||
struct AzslData;
|
||||
|
||||
//! This is nothing more than a class to help consolidate all
|
||||
//! the data needed to generate a shader variant and prevent
|
||||
//! all the functions involved in the process to have too many
|
||||
//! arguments.
|
||||
struct ShaderVariantCreationContext
|
||||
{
|
||||
const Data::AssetId m_assetId;
|
||||
const AZStd::string& m_hlslSourcePath;
|
||||
const AZStd::string& m_hlslSourceContent;
|
||||
const RPI::ShaderSourceData& m_shaderSourceDataDescriptor;
|
||||
const AZStd::string& m_tempDirPath; //! Used to write temporary files during shader compilation, like *.hlsl, or *.air, or *.metallib, etc.
|
||||
RHI::ShaderPlatformInterface& m_shaderPlatformInterface;
|
||||
const AssetBuilderSDK::PlatformInfo& m_platformInfo;
|
||||
const RHI::ShaderCompilerArguments& m_shaderCompilerArguments;
|
||||
//! Used to write temporary files during shader compilation, like *.hlsl, or *.air, or *.metallib, etc.
|
||||
const AZStd::string& m_tempDirPath;
|
||||
//! Used to synchronize versions of the ShaderAsset and ShaderVariantAsset,
|
||||
//! especially during hot-reload. A (ShaderVariantAsset.timestamp) >= (ShaderAsset.timestamp).
|
||||
const AZStd::sys_time_t m_assetBuildTimestamp;
|
||||
const RPI::ShaderSourceData& m_shaderSourceDataDescriptor;
|
||||
const RPI::ShaderOptionGroupLayout& m_shaderOptionGroupLayout;
|
||||
const MapOfStringToStageType& m_shaderEntryPoints;
|
||||
AZStd::sys_time_t m_shaderAssetBuildTimestamp; //!< Copied from the ShaderAsset, used to synchronize versions of the ShaderAsset and ShaderVariantAsset, especially during hot-reload.
|
||||
AZStd::optional<RHI::ShaderPlatformInterface::ByProducts> m_outputByproducts;
|
||||
const Data::AssetId m_shaderVariantAssetId;
|
||||
const AZStd::string& m_shaderStemNamePrefix; //<shaderName>-<supervariantName>
|
||||
const AZStd::string& m_hlslSourcePath;
|
||||
const AZStd::string& m_hlslSourceContent;
|
||||
};
|
||||
|
||||
class ShaderVariantAssetBuilder
|
||||
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(ShaderVariantAssetBuilder, "{2F84C802-95AF-4B73-9017-2DA9AA8C0C89}");
|
||||
AZ_TYPE_INFO(ShaderVariantAssetBuilder, "{C959AEC2-2083-4488-AD88-F61B1144535B}");
|
||||
|
||||
static constexpr char ShaderVariantAssetBuilderJobKey[] = "Shader Variant Asset";
|
||||
|
||||
@@ -61,16 +70,14 @@ namespace AZ
|
||||
//! The ShaderVariantAsset returned by this function won't be written to the filesystem.
|
||||
//! You should call SerializeOutShaderVariantAsset to write it to the temp folder assigned
|
||||
//! by the asset processor.
|
||||
static AZ::Outcome<Data::Asset<RPI::ShaderVariantAsset>, AZStd::string> CreateShaderVariantAssetForAPI(
|
||||
static AZ::Outcome<Data::Asset<RPI::ShaderVariantAsset>, AZStd::string> CreateShaderVariantAsset(
|
||||
const RPI::ShaderVariantListSourceData::VariantInfo& shaderVariantInfo,
|
||||
ShaderVariantCreationContext& context,
|
||||
RHI::ShaderPlatformInterface& shaderPlatformInterface,
|
||||
AzslData& azslData,
|
||||
const RHI::ShaderCompilerArguments& shaderCompilerArguments,
|
||||
const AZStd::string& pathToOmJson,
|
||||
const AZStd::string& pathToIaJson);
|
||||
ShaderVariantCreationContext& creationContext,
|
||||
AZStd::optional<RHI::ShaderPlatformInterface::ByProducts>& outputByproducts);
|
||||
|
||||
static bool SerializeOutShaderVariantAsset(const Data::Asset<RPI::ShaderVariantAsset> shaderVariantAsset, const AZStd::string& shaderFullPath, const AZStd::string& tempDirPath,
|
||||
static bool SerializeOutShaderVariantAsset(
|
||||
const Data::Asset<RPI::ShaderVariantAsset> shaderVariantAsset,
|
||||
const AZStd::string& shaderStemNamePrefix, const AZStd::string& tempDirPath,
|
||||
const RHI::ShaderPlatformInterface& shaderPlatformInterface, const uint32_t productSubID, AssetBuilderSDK::JobProduct& assetProduct);
|
||||
|
||||
// AssetBuilderSDK::AssetBuilderCommandBus interface overrides ...
|
||||
@@ -79,6 +86,11 @@ namespace AZ
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(ShaderVariantAssetBuilder);
|
||||
|
||||
static constexpr uint32_t ShaderVariantLoadErrorParam = 0;
|
||||
static constexpr uint32_t ShaderSourceFilePathJobParam = 2;
|
||||
static constexpr uint32_t ShaderVariantJobVariantParam = 3;
|
||||
static constexpr uint32_t ShouldExitEarlyFromProcessJobParam = 4;
|
||||
|
||||
//! Called from ProcessJob when the job is supposed to create a ShaderVariantTreeAsset.
|
||||
void ProcessShaderVariantTreeJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const;
|
||||
|
||||
|
||||
@@ -1,978 +0,0 @@
|
||||
/*
|
||||
* 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 <ShaderVariantAssetBuilder2.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantTreeAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
|
||||
|
||||
#include <Atom/RPI.Edit/Shader/ShaderVariantListSourceData.h>
|
||||
#include <Atom/RPI.Edit/Shader/ShaderVariantAssetCreator2.h>
|
||||
#include <Atom/RPI.Edit/Shader/ShaderVariantTreeAssetCreator.h>
|
||||
#include <Atom/RPI.Edit/Common/JsonUtils.h>
|
||||
|
||||
#include <AtomCore/Serialization/Json/JsonUtils.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderResourceGroupAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantKey.h>
|
||||
|
||||
#include <Atom/RHI.Edit/Utils.h>
|
||||
#include <Atom/RHI.Edit/ShaderPlatformInterface.h>
|
||||
#include <Atom/RPI.Edit/Common/JsonReportingHelper.h>
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <Atom/RHI.Reflect/ConstantsLayout.h>
|
||||
#include <Atom/RHI.Reflect/PipelineLayoutDescriptor.h>
|
||||
#include <Atom/RHI.Reflect/ShaderStageFunction.h>
|
||||
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
#include <AzFramework/Platform/PlatformDefaults.h>
|
||||
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/JSON/document.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/IOUtils.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
|
||||
#include "ShaderAssetBuilder2.h"
|
||||
#include "ShaderBuilderUtility.h"
|
||||
#include "AzslData.h"
|
||||
#include "AzslCompiler.h"
|
||||
#include "AzslBuilder.h"
|
||||
#include <CommonFiles/Preprocessor.h>
|
||||
#include <CommonFiles/GlobalBuildOptions.h>
|
||||
#include <ShaderPlatformInterfaceRequest.h>
|
||||
#include "AtomShaderConfig.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
static constexpr char ShaderVariantAssetBuilder2Name[] = "ShaderVariantAssetBuilder2";
|
||||
|
||||
static void AddShaderAssetJobDependency2(
|
||||
AssetBuilderSDK::JobDescriptor& jobDescriptor, const AssetBuilderSDK::PlatformInfo& platformInfo,
|
||||
const AZStd::string& shaderVariantListFilePath, const AZStd::string& shaderFilePath)
|
||||
{
|
||||
AZStd::vector<AZStd::string> possibleDependencies =
|
||||
AZ::RPI::AssetUtils::GetPossibleDepenencyPaths(shaderVariantListFilePath, shaderFilePath);
|
||||
for (auto& file : possibleDependencies)
|
||||
{
|
||||
AssetBuilderSDK::JobDependency jobDependency;
|
||||
jobDependency.m_jobKey = ShaderAssetBuilder2::ShaderAssetBuilder2JobKey;
|
||||
jobDependency.m_platformIdentifier = platformInfo.m_identifier;
|
||||
jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
jobDependency.m_sourceFile.m_sourceFileDependencyPath = file;
|
||||
jobDescriptor.m_jobDependencyList.push_back(jobDependency);
|
||||
}
|
||||
}
|
||||
|
||||
//! Returns true if @sourceFileFullPath starts with a valid asset processor scan folder, false otherwise.
|
||||
//! In case of true, it splits @sourceFileFullPath into @scanFolderFullPath and @filePathFromScanFolder.
|
||||
//! @sourceFileFullPath The full path to a source asset file.
|
||||
//! @scanFolderFullPath [out] Gets the full path of the scan folder where the source file is located.
|
||||
//! @filePathFromScanFolder [out] Get the file path relative to @scanFolderFullPath.
|
||||
static bool SplitSourceAssetPathIntoScanFolderFullPathAndRelativeFilePath2(const AZStd::string& sourceFileFullPath, AZStd::string& scanFolderFullPath, AZStd::string& filePathFromScanFolder)
|
||||
{
|
||||
AZStd::vector<AZStd::string> scanFolders;
|
||||
bool success = false;
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(success, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAssetSafeFolders, scanFolders);
|
||||
if (!success)
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Couldn't get the scan folders");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AZStd::string scanFolder : scanFolders)
|
||||
{
|
||||
AzFramework::StringFunc::Path::Normalize(scanFolder);
|
||||
if (!AZ::StringFunc::StartsWith(sourceFileFullPath, scanFolder))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const size_t scanFolderSize = scanFolder.size();
|
||||
const size_t sourcePathSize = sourceFileFullPath.size();
|
||||
scanFolderFullPath = scanFolder;
|
||||
filePathFromScanFolder = sourceFileFullPath.substr(scanFolderSize + 1, sourcePathSize - scanFolderSize - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Validates if a given .shadervariantlist file is located at the correct path for a given .shader full path.
|
||||
//! There are two valid paths:
|
||||
//! 1- Lower Precedence: The same folder where the .shader file is located.
|
||||
//! 2- Higher Precedence: <DEVROOT>/<GAME>/ShaderVariants/<Same Scan Folder Subpath as the .shader file>.
|
||||
//! The "Higher Precedence" path gives the option to game projects to override what variants to generate. If this
|
||||
//! file exists then the "Lower Precedence" path is disregarded.
|
||||
//! A .shader full path is located under an AP scan folder.
|
||||
//! Example: "<DEVROOT>/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.shader"
|
||||
//! - In this example the Scan Folder is "<DEVROOT>/Gems/Atom/Feature/Common/Assets", while the subfolder is "Materials/Types".
|
||||
//! The "Higher Precedence" expected valid location for the .shadervariantlist would be:
|
||||
//! - <DEVROOT>/<GameProject>/ShaderVariants/Materials/Types/StandardPBR_ForwardPass.shadervariantlist.
|
||||
//! The "Lower Precedence" valid location would be:
|
||||
//! - <DEVROOT>/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.shadervariantlist.
|
||||
//! @shouldExitEarlyFromProcessJob [out] Set to true if ProcessJob should do no work but return successfully.
|
||||
//! Set to false if ProcessJob should do work and create assets.
|
||||
//! When @shaderVariantListFileFullPath is provided by a Gem/Feature instead of the Game Project
|
||||
//! We check if the game project already defined the shader variant list, and if it did it means
|
||||
//! ProcessJob should do no work, but return successfully nonetheless.
|
||||
static bool ValidateShaderVariantListLocation2(const AZStd::string& shaderVariantListFileFullPath,
|
||||
const AZStd::string& shaderFileFullPath, bool& shouldExitEarlyFromProcessJob)
|
||||
{
|
||||
AZStd::string scanFolderFullPath;
|
||||
AZStd::string shaderProductFileRelativePath;
|
||||
if (!SplitSourceAssetPathIntoScanFolderFullPathAndRelativeFilePath2(shaderFileFullPath, scanFolderFullPath, shaderProductFileRelativePath))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Couldn't get the scan folder for shader [%s]", shaderFileFullPath.c_str());
|
||||
return false;
|
||||
}
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "For shader [%s], Scan folder full path [%s], relative file path [%s]", shaderFileFullPath.c_str(), scanFolderFullPath.c_str(), shaderProductFileRelativePath.c_str());
|
||||
|
||||
AZStd::string shaderVariantListFileRelativePath = shaderProductFileRelativePath;
|
||||
AzFramework::StringFunc::Path::ReplaceExtension(shaderVariantListFileRelativePath, RPI::ShaderVariantListSourceData::Extension);
|
||||
|
||||
const char * gameProjectPath = nullptr;
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(gameProjectPath, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAbsoluteDevGameFolderPath);
|
||||
|
||||
AZStd::string expectedHigherPrecedenceFileFullPath;
|
||||
AzFramework::StringFunc::Path::Join(gameProjectPath, RPI::ShaderVariantTreeAsset::CommonSubFolder, expectedHigherPrecedenceFileFullPath, false /* handle directory overlap? */, false /* be case insensitive? */);
|
||||
AzFramework::StringFunc::Path::Join(expectedHigherPrecedenceFileFullPath.c_str(), shaderProductFileRelativePath.c_str(), expectedHigherPrecedenceFileFullPath, false /* handle directory overlap? */, false /* be case insensitive? */);
|
||||
AzFramework::StringFunc::Path::ReplaceExtension(expectedHigherPrecedenceFileFullPath, AZ::RPI::ShaderVariantListSourceData::Extension);
|
||||
AzFramework::StringFunc::Path::Normalize(expectedHigherPrecedenceFileFullPath);
|
||||
|
||||
AZStd::string normalizedShaderVariantListFileFullPath = shaderVariantListFileFullPath;
|
||||
AzFramework::StringFunc::Path::Normalize(normalizedShaderVariantListFileFullPath);
|
||||
|
||||
if (expectedHigherPrecedenceFileFullPath == normalizedShaderVariantListFileFullPath)
|
||||
{
|
||||
// Whenever the Game Project declares a *.shadervariantlist file we always do work.
|
||||
shouldExitEarlyFromProcessJob = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
AZStd::string watchFolder;
|
||||
bool foundHigherPrecedenceAsset = false;
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(foundHigherPrecedenceAsset
|
||||
, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath
|
||||
, expectedHigherPrecedenceFileFullPath.c_str(), assetInfo, watchFolder);
|
||||
if (foundHigherPrecedenceAsset)
|
||||
{
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "The shadervariantlist [%s] has been overriden by the game project with [%s]",
|
||||
normalizedShaderVariantListFileFullPath.c_str(), expectedHigherPrecedenceFileFullPath.c_str());
|
||||
shouldExitEarlyFromProcessJob = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the "Lower Precedence" case, .shader path == .shadervariantlist path.
|
||||
AZStd::string normalizedShaderFileFullPath = shaderFileFullPath;
|
||||
AzFramework::StringFunc::Path::Normalize(normalizedShaderFileFullPath);
|
||||
|
||||
AZStd::string normalizedShaderFileFullPathWithoutExtension = normalizedShaderFileFullPath;
|
||||
AzFramework::StringFunc::Path::StripExtension(normalizedShaderFileFullPathWithoutExtension);
|
||||
|
||||
AZStd::string normalizedShaderVariantListFileFullPathWithoutExtension = normalizedShaderVariantListFileFullPath;
|
||||
AzFramework::StringFunc::Path::StripExtension(normalizedShaderVariantListFileFullPathWithoutExtension);
|
||||
|
||||
#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
|
||||
//In certain circumstances, the capitalization of the drive letter may not match
|
||||
const bool caseSensitive = false;
|
||||
#else
|
||||
//On the other platforms there's no drive letter, so it should be a non-issue.
|
||||
const bool caseSensitive = true;
|
||||
#endif
|
||||
if (!StringFunc::Equal(normalizedShaderFileFullPathWithoutExtension.c_str(), normalizedShaderVariantListFileFullPathWithoutExtension.c_str(), caseSensitive))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "For shader file at path [%s], the shader variant list [%s] is expected to be located at [%s.%s] or [%s]"
|
||||
, normalizedShaderFileFullPath.c_str(), normalizedShaderVariantListFileFullPath.c_str(),
|
||||
normalizedShaderFileFullPathWithoutExtension.c_str(), RPI::ShaderVariantListSourceData::Extension,
|
||||
expectedHigherPrecedenceFileFullPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
shouldExitEarlyFromProcessJob = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We treat some issues as warnings and return "Success" from CreateJobs allows us to report the dependency.
|
||||
// If/when a valid dependency file appears, that will trigger the ShaderVariantAssetBuilder2 to run again.
|
||||
// Since CreateJobs will pass, we forward this message to ProcessJob which will report it as an error.
|
||||
struct LoadResult2
|
||||
{
|
||||
enum class Code
|
||||
{
|
||||
Error,
|
||||
DeferredError,
|
||||
Success
|
||||
};
|
||||
|
||||
Code m_code;
|
||||
AZStd::string m_deferredMessage; // Only used when m_code == DeferredError
|
||||
};
|
||||
|
||||
static LoadResult2 LoadShaderVariantList2(const AZStd::string& variantListFullPath, RPI::ShaderVariantListSourceData& shaderVariantList, AZStd::string& shaderSourceFileFullPath,
|
||||
bool& shouldExitEarlyFromProcessJob)
|
||||
{
|
||||
// Need to get the name of the shader file from the template so that we can preprocess the shader data and setup
|
||||
// source file dependencies.
|
||||
if (!RPI::JsonUtils::LoadObjectFromFile(variantListFullPath, shaderVariantList))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Failed to parse Shader Variant List Descriptor JSON from [%s]", variantListFullPath.c_str());
|
||||
return LoadResult2{LoadResult2::Code::Error};
|
||||
}
|
||||
|
||||
const AZStd::string resolvedShaderPath = AZ::RPI::AssetUtils::ResolvePathReference(variantListFullPath, shaderVariantList.m_shaderFilePath);
|
||||
if (!AZ::IO::LocalFileIO::GetInstance()->Exists(resolvedShaderPath.c_str()))
|
||||
{
|
||||
return LoadResult2{LoadResult2::Code::DeferredError, AZStd::string::format("The shader path [%s] was not found.", resolvedShaderPath.c_str())};
|
||||
}
|
||||
|
||||
shaderSourceFileFullPath = resolvedShaderPath;
|
||||
|
||||
if (!ValidateShaderVariantListLocation2(variantListFullPath, shaderSourceFileFullPath, shouldExitEarlyFromProcessJob))
|
||||
{
|
||||
return LoadResult2{LoadResult2::Code::Error};
|
||||
}
|
||||
|
||||
if (shouldExitEarlyFromProcessJob)
|
||||
{
|
||||
return LoadResult2{LoadResult2::Code::Success};
|
||||
}
|
||||
|
||||
auto resultOutcome = RPI::ShaderVariantTreeAssetCreator::ValidateStableIdsAreUnique(shaderVariantList.m_shaderVariants);
|
||||
if (!resultOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Variant info validation error: %s", resultOutcome.GetError().c_str());
|
||||
return LoadResult2{LoadResult2::Code::Error};
|
||||
}
|
||||
|
||||
if (!IO::FileIOBase::GetInstance()->Exists(shaderSourceFileFullPath.c_str()))
|
||||
{
|
||||
return LoadResult2{LoadResult2::Code::DeferredError, AZStd::string::format("ShaderSourceData file does not exist: %s.", shaderSourceFileFullPath.c_str())};
|
||||
}
|
||||
|
||||
return LoadResult2{LoadResult2::Code::Success};
|
||||
} // LoadShaderVariantListAndAzslSource
|
||||
|
||||
void ShaderVariantAssetBuilder2::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const
|
||||
{
|
||||
AZStd::string variantListFullPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.data(), request.m_sourceFile.data(), variantListFullPath, true);
|
||||
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "CreateJobs for Shader Variant List \"%s\"\n", variantListFullPath.data());
|
||||
|
||||
RPI::ShaderVariantListSourceData shaderVariantList;
|
||||
AZStd::string shaderSourceFileFullPath;
|
||||
bool shouldExitEarlyFromProcessJob = false;
|
||||
const LoadResult2 loadResult = LoadShaderVariantList2(variantListFullPath, shaderVariantList, shaderSourceFileFullPath, shouldExitEarlyFromProcessJob);
|
||||
|
||||
if (loadResult.m_code == LoadResult2::Code::Error)
|
||||
{
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (loadResult.m_code == LoadResult2::Code::DeferredError || shouldExitEarlyFromProcessJob)
|
||||
{
|
||||
for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
|
||||
{
|
||||
// Let's create fake jobs that will fail ProcessJob, but are useful to establish dependency on the shader file.
|
||||
AssetBuilderSDK::JobDescriptor jobDescriptor;
|
||||
|
||||
jobDescriptor.m_priority = -5000;
|
||||
jobDescriptor.m_critical = false;
|
||||
jobDescriptor.m_jobKey = ShaderVariantAssetBuilder2JobKey;
|
||||
jobDescriptor.SetPlatformIdentifier(info.m_identifier.data());
|
||||
|
||||
AddShaderAssetJobDependency2(jobDescriptor, info, variantListFullPath, shaderVariantList.m_shaderFilePath);
|
||||
|
||||
if (loadResult.m_code == LoadResult2::Code::DeferredError)
|
||||
{
|
||||
jobDescriptor.m_jobParameters.emplace(ShaderVariantLoadErrorParam, loadResult.m_deferredMessage);
|
||||
}
|
||||
|
||||
if (shouldExitEarlyFromProcessJob)
|
||||
{
|
||||
// The value doesn't matter, what matters is the presence of the key which will
|
||||
// signal that no assets should be produced on behalf of this shadervariantlist because
|
||||
// the game project overrode it.
|
||||
jobDescriptor.m_jobParameters.emplace(ShouldExitEarlyFromProcessJobParam, variantListFullPath);
|
||||
}
|
||||
|
||||
response.m_createJobOutputs.push_back(jobDescriptor);
|
||||
}
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
|
||||
return;
|
||||
}
|
||||
|
||||
for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
|
||||
{
|
||||
AZ_TraceContext("For platform", info.m_identifier.data());
|
||||
|
||||
// First job is for the ShaderVariantTreeAsset.
|
||||
{
|
||||
AssetBuilderSDK::JobDescriptor jobDescriptor;
|
||||
|
||||
// The ShaderVariantTreeAsset is high priority, but must be generated after the ShaderAsset
|
||||
jobDescriptor.m_priority = 1;
|
||||
jobDescriptor.m_critical = false;
|
||||
|
||||
jobDescriptor.m_jobKey = GetShaderVariantTreeAssetJobKey();
|
||||
jobDescriptor.SetPlatformIdentifier(info.m_identifier.data());
|
||||
|
||||
AddShaderAssetJobDependency2(jobDescriptor, info, variantListFullPath, shaderVariantList.m_shaderFilePath);
|
||||
|
||||
jobDescriptor.m_jobParameters.emplace(ShaderSourceFilePathJobParam, shaderSourceFileFullPath);
|
||||
|
||||
response.m_createJobOutputs.push_back(jobDescriptor);
|
||||
}
|
||||
|
||||
// One job for each variant. Each job will produce one ".azshadervariant" per RHI per supervariant.
|
||||
for (const AZ::RPI::ShaderVariantListSourceData::VariantInfo& variantInfo : shaderVariantList.m_shaderVariants)
|
||||
{
|
||||
AZStd::string variantInfoAsJsonString;
|
||||
const bool convertSuccess = AZ::RPI::JsonUtils::SaveObjectToJsonString(variantInfo, variantInfoAsJsonString);
|
||||
AZ_Assert(convertSuccess, "Failed to convert VariantInfo to json string");
|
||||
|
||||
AssetBuilderSDK::JobDescriptor jobDescriptor;
|
||||
|
||||
// There can be tens/hundreds of thousands of shader variants. By default each shader will get
|
||||
// a root variant that can be used at runtime. In order to prevent the AssetProcessor from
|
||||
// being overtaken by shader variant compilation We mark all non-root shader variant generation
|
||||
// as non critical and very low priority.
|
||||
jobDescriptor.m_priority = -5000;
|
||||
jobDescriptor.m_critical = false;
|
||||
|
||||
jobDescriptor.m_jobKey = GetShaderVariantAssetJobKey(RPI::ShaderVariantStableId{variantInfo.m_stableId});
|
||||
jobDescriptor.SetPlatformIdentifier(info.m_identifier.data());
|
||||
|
||||
// The ShaderVariantAssets are job dependent on the ShaderVariantTreeAsset.
|
||||
AssetBuilderSDK::SourceFileDependency fileDependency;
|
||||
fileDependency.m_sourceFileDependencyPath = variantListFullPath;
|
||||
AssetBuilderSDK::JobDependency variantTreeJobDependency;
|
||||
variantTreeJobDependency.m_jobKey = GetShaderVariantTreeAssetJobKey();
|
||||
variantTreeJobDependency.m_platformIdentifier = info.m_identifier;
|
||||
variantTreeJobDependency.m_sourceFile = fileDependency;
|
||||
variantTreeJobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
jobDescriptor.m_jobDependencyList.emplace_back(variantTreeJobDependency);
|
||||
|
||||
jobDescriptor.m_jobParameters.emplace(ShaderVariantJobVariantParam, variantInfoAsJsonString);
|
||||
jobDescriptor.m_jobParameters.emplace(ShaderSourceFilePathJobParam, shaderSourceFileFullPath);
|
||||
|
||||
response.m_createJobOutputs.push_back(jobDescriptor);
|
||||
}
|
||||
|
||||
}
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
|
||||
} // CreateJobs
|
||||
|
||||
void ShaderVariantAssetBuilder2::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const
|
||||
{
|
||||
const auto& jobParameters = request.m_jobDescription.m_jobParameters;
|
||||
|
||||
if (jobParameters.find(ShaderVariantLoadErrorParam) != jobParameters.end())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Error during CreateJobs: %s", jobParameters.at(ShaderVariantLoadErrorParam).c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (jobParameters.find(ShouldExitEarlyFromProcessJobParam) != jobParameters.end())
|
||||
{
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "Doing nothing on behalf of [%s] because it's been overridden by game project.", jobParameters.at(ShaderVariantLoadErrorParam).c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
return;
|
||||
}
|
||||
|
||||
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.m_jobDescription.m_jobKey == GetShaderVariantTreeAssetJobKey())
|
||||
{
|
||||
ProcessShaderVariantTreeJob(request, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessShaderVariantJob(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static RPI::Ptr<RPI::ShaderOptionGroupLayout> LoadShaderOptionsGroupLayoutFromShaderAssetBuilder2(
|
||||
const RHI::ShaderPlatformInterface* shaderPlatformInterface,
|
||||
const AssetBuilderSDK::PlatformInfo& platformInfo,
|
||||
const AzslCompiler& azslCompiler,
|
||||
const AZStd::string& shaderSourceFileFullPath,
|
||||
const RPI::SupervariantIndex supervariantIndex)
|
||||
{
|
||||
auto optionsGroupPathOutcome = ShaderBuilderUtility::ObtainBuildArtifactPathFromShaderAssetBuilder2(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), platformInfo.m_identifier, shaderSourceFileFullPath, supervariantIndex.GetIndex(),
|
||||
AZ::RPI::ShaderAssetSubId::OptionsJson);
|
||||
if (!optionsGroupPathOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "%s", optionsGroupPathOutcome.GetError().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
auto optionsGroupJsonPath = optionsGroupPathOutcome.TakeValue();
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
|
||||
// The shader options define what options are available, what are the allowed values/range
|
||||
// for each option and what is its default value.
|
||||
auto jsonOutcome = JsonSerializationUtils::ReadJsonFile(optionsGroupJsonPath);
|
||||
if (!jsonOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "%s", jsonOutcome.GetError().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
if (!azslCompiler.ParseOptionsPopulateOptionGroupLayout(jsonOutcome.GetValue(), shaderOptionGroupLayout))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Failed to find a valid list of shader options!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return shaderOptionGroupLayout;
|
||||
}
|
||||
|
||||
static void LoadShaderFunctionsFromShaderAssetBuilder2(
|
||||
const RHI::ShaderPlatformInterface* shaderPlatformInterface, const AssetBuilderSDK::PlatformInfo& platformInfo,
|
||||
const AzslCompiler& azslCompiler, const AZStd::string& shaderSourceFileFullPath,
|
||||
const RPI::SupervariantIndex supervariantIndex,
|
||||
AzslFunctions& functions)
|
||||
{
|
||||
auto functionsJsonPathOutcome = ShaderBuilderUtility::ObtainBuildArtifactPathFromShaderAssetBuilder2(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), platformInfo.m_identifier, shaderSourceFileFullPath, supervariantIndex.GetIndex(),
|
||||
AZ::RPI::ShaderAssetSubId::IaJson);
|
||||
if (!functionsJsonPathOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "%s", functionsJsonPathOutcome.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto functionsJsonPath = functionsJsonPathOutcome.TakeValue();
|
||||
auto jsonOutcome = JsonSerializationUtils::ReadJsonFile(functionsJsonPath);
|
||||
if (!jsonOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "%s", jsonOutcome.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
if (!azslCompiler.ParseIaPopulateFunctionData(jsonOutcome.GetValue(), functions))
|
||||
{
|
||||
functions.clear();
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Failed to find shader functions.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns the content of the hlsl file for the given supervariant as produced by ShaderAsssetBuilder2.
|
||||
// In addition to the content it also returns the full path of the hlsl file in @hlslSourcePath.
|
||||
static AZStd::string LoadHlslFileFromShaderAssetBuilder2(
|
||||
const RHI::ShaderPlatformInterface* shaderPlatformInterface, const AssetBuilderSDK::PlatformInfo& platformInfo,
|
||||
const AZStd::string& shaderSourceFileFullPath, const RPI::SupervariantIndex supervariantIndex, AZStd::string& hlslSourcePath)
|
||||
{
|
||||
auto hlslSourcePathOutcome = ShaderBuilderUtility::ObtainBuildArtifactPathFromShaderAssetBuilder2(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), platformInfo.m_identifier, shaderSourceFileFullPath, supervariantIndex.GetIndex(),
|
||||
AZ::RPI::ShaderAssetSubId::GeneratedHlslSource);
|
||||
if (!hlslSourcePathOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "%s", hlslSourcePathOutcome.GetError().c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
hlslSourcePath = hlslSourcePathOutcome.TakeValue();
|
||||
Outcome<AZStd::string, AZStd::string> hlslSourceOutcome = Utils::ReadFile(hlslSourcePath);
|
||||
if (!hlslSourceOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderVariantAssetBuilder2Name, false, "Failed to obtain shader source from %s. [%s]", hlslSourcePath.c_str(),
|
||||
hlslSourceOutcome.TakeError().c_str());
|
||||
return "";
|
||||
}
|
||||
return hlslSourceOutcome.TakeValue();
|
||||
}
|
||||
|
||||
void ShaderVariantAssetBuilder2::ProcessShaderVariantTreeJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const
|
||||
{
|
||||
AZStd::string variantListFullPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.data(), request.m_sourceFile.data(), variantListFullPath, true);
|
||||
|
||||
RPI::ShaderVariantListSourceData shaderVariantListDescriptor;
|
||||
if (!RPI::JsonUtils::LoadObjectFromFile(variantListFullPath, shaderVariantListDescriptor))
|
||||
{
|
||||
AZ_Assert(false, "Failed to parse Shader Variant List Descriptor JSON [%s]", variantListFullPath.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
const AZStd::string& shaderSourceFileFullPath = request.m_jobDescription.m_jobParameters.at(ShaderSourceFilePathJobParam);
|
||||
|
||||
//For debugging purposes will create a dummy azshadervarianttree file.
|
||||
AZStd::string shaderName;
|
||||
AzFramework::StringFunc::Path::GetFileName(shaderSourceFileFullPath.c_str(), shaderName);
|
||||
|
||||
// No error checking because the same calls were already executed during CreateJobs()
|
||||
auto descriptorParseOutcome = ShaderBuilderUtility::LoadShaderDataJson(shaderSourceFileFullPath);
|
||||
RPI::ShaderSourceData shaderSourceDescriptor = descriptorParseOutcome.TakeValue();
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout;
|
||||
|
||||
// Request the list of valid shader platform interfaces for the target platform.
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces =
|
||||
ShaderBuilderUtility::DiscoverEnabledShaderPlatformInterfaces(request.m_platformInfo, shaderSourceDescriptor);
|
||||
if (platformInterfaces.empty())
|
||||
{
|
||||
// No work to do. Exit gracefully.
|
||||
AZ_TracePrintf(
|
||||
ShaderVariantAssetBuilder2Name,
|
||||
"No azshadervarianttree is produced on behalf of %s because all valid RHI backends were disabled for this shader.\n",
|
||||
shaderSourceFileFullPath.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// set the input file for eventual error messages, but the compiler won't be called on it.
|
||||
AZStd::string azslFullPath;
|
||||
ShaderBuilderUtility::GetAbsolutePathToAzslFile(shaderSourceFileFullPath, shaderSourceDescriptor.m_source, azslFullPath);
|
||||
AzslCompiler azslc(azslFullPath);
|
||||
|
||||
AZStd::string previousLoopApiName;
|
||||
for (RHI::ShaderPlatformInterface* shaderPlatformInterface : platformInterfaces)
|
||||
{
|
||||
auto thisLoopApiName = shaderPlatformInterface->GetAPIName().GetStringView();
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> loopLocal_ShaderOptionGroupLayout =
|
||||
LoadShaderOptionsGroupLayoutFromShaderAssetBuilder2(
|
||||
shaderPlatformInterface, request.m_platformInfo, azslc, shaderSourceFileFullPath, RPI::DefaultSupervariantIndex);
|
||||
if (!loopLocal_ShaderOptionGroupLayout)
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
if (shaderOptionGroupLayout && shaderOptionGroupLayout->GetHash() != loopLocal_ShaderOptionGroupLayout->GetHash())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "There was a discrepancy in shader options between %s and %s", previousLoopApiName.c_str(), thisLoopApiName.data());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
shaderOptionGroupLayout = loopLocal_ShaderOptionGroupLayout;
|
||||
previousLoopApiName = thisLoopApiName;
|
||||
}
|
||||
|
||||
RPI::ShaderVariantTreeAssetCreator shaderVariantTreeAssetCreator;
|
||||
shaderVariantTreeAssetCreator.Begin(Uuid::CreateRandom());
|
||||
shaderVariantTreeAssetCreator.SetShaderOptionGroupLayout(*shaderOptionGroupLayout);
|
||||
shaderVariantTreeAssetCreator.SetVariantInfos(shaderVariantListDescriptor.m_shaderVariants);
|
||||
Data::Asset<RPI::ShaderVariantTreeAsset> shaderVariantTreeAsset;
|
||||
if (!shaderVariantTreeAssetCreator.End(shaderVariantTreeAsset))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Failed to build Shader Variant Tree Asset");
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::string filename = AZStd::string::format("%s.%s", shaderName.c_str(), RPI::ShaderVariantTreeAsset::Extension);
|
||||
AZStd::string assetPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_tempDirPath.c_str(), filename.c_str(), assetPath, true);
|
||||
if (!AZ::Utils::SaveObjectToFile(assetPath, AZ::DataStream::ST_BINARY, shaderVariantTreeAsset.Get()))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Failed to save Shader Variant Tree Asset to \"%s\"", assetPath.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
AssetBuilderSDK::JobProduct assetProduct;
|
||||
assetProduct.m_productSubID = RPI::ShaderVariantTreeAsset::ProductSubID;
|
||||
assetProduct.m_productFileName = assetPath;
|
||||
assetProduct.m_productAssetType = azrtti_typeid<RPI::ShaderVariantTreeAsset>();
|
||||
assetProduct.m_dependenciesHandled = true; // This builder has no dependencies to output
|
||||
response.m_outputProducts.push_back(assetProduct);
|
||||
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "Shader Variant Tree Asset [%s] compiled successfully.\n", assetPath.c_str());
|
||||
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
}
|
||||
|
||||
void ShaderVariantAssetBuilder2::ProcessShaderVariantJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const
|
||||
{
|
||||
const AZStd::sys_time_t startTime = AZStd::GetTimeNowTicks();
|
||||
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
||||
|
||||
AZStd::string fullPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.data(), request.m_sourceFile.data(), fullPath, true);
|
||||
|
||||
const auto& jobParameters = request.m_jobDescription.m_jobParameters;
|
||||
const AZStd::string& shaderSourceFileFullPath = jobParameters.at(ShaderSourceFilePathJobParam);
|
||||
AZStd::string shaderFileName;
|
||||
AzFramework::StringFunc::Path::GetFileName(shaderSourceFileFullPath.c_str(), shaderFileName);
|
||||
|
||||
const AZStd::string& variantJsonString = jobParameters.at(ShaderVariantJobVariantParam);
|
||||
RPI::ShaderVariantListSourceData::VariantInfo variantInfo;
|
||||
const bool fromJsonStringSuccess = AZ::RPI::JsonUtils::LoadObjectFromJsonString(variantJsonString, variantInfo);
|
||||
AZ_Assert(fromJsonStringSuccess, "Failed to convert json string to VariantInfo");
|
||||
|
||||
RPI::ShaderSourceData shaderSourceDescriptor;
|
||||
AZStd::shared_ptr<ShaderFiles> sources = ShaderBuilderUtility::PrepareSourceInput(ShaderVariantAssetBuilder2Name, shaderSourceFileFullPath, shaderSourceDescriptor);
|
||||
|
||||
// set the input file for eventual error messages, but the compiler won't be called on it.
|
||||
AzslCompiler azslc(sources->m_azslSourceFullPath);
|
||||
|
||||
// Request the list of valid shader platform interfaces for the target platform.
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces =
|
||||
ShaderBuilderUtility::DiscoverEnabledShaderPlatformInterfaces(request.m_platformInfo, shaderSourceDescriptor);
|
||||
if (platformInterfaces.empty())
|
||||
{
|
||||
// No work to do. Exit gracefully.
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name,
|
||||
"No azshader is produced on behalf of %s because all valid RHI backends were disabled for this shader.\n",
|
||||
shaderSourceFileFullPath.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
return;
|
||||
}
|
||||
|
||||
auto supervariantList = ShaderBuilderUtility::GetSupervariantListFromShaderSourceData(shaderSourceDescriptor);
|
||||
|
||||
GlobalBuildOptions buildOptions = ReadBuildOptions(ShaderVariantAssetBuilder2Name);
|
||||
// At this moment We have global build options that should be merged with the build options that are common
|
||||
// to all the supervariants of this shader.
|
||||
buildOptions.m_compilerArguments.Merge(shaderSourceDescriptor.m_compiler);
|
||||
|
||||
//! The ShaderOptionGroupLayout is common across all RHIs & Supervariants
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = nullptr;
|
||||
|
||||
// Generate shaders for each of those ShaderPlatformInterfaces.
|
||||
for (RHI::ShaderPlatformInterface* shaderPlatformInterface : platformInterfaces)
|
||||
{
|
||||
AZ_TraceContext("ShaderPlatformInterface", shaderPlatformInterface->GetAPIName().GetCStr());
|
||||
|
||||
// Loop through all the Supervariants.
|
||||
uint32_t supervariantIndexCounter = 0;
|
||||
for (const auto& supervariantInfo : supervariantList)
|
||||
{
|
||||
RPI::SupervariantIndex supervariantIndex(supervariantIndexCounter);
|
||||
|
||||
// Check if we were canceled before we do any heavy processing of
|
||||
// the shader variant data.
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::string shaderStemNamePrefix = shaderFileName;
|
||||
if (supervariantIndex.GetIndex() > 0)
|
||||
{
|
||||
shaderStemNamePrefix += supervariantInfo.m_name.GetStringView();
|
||||
}
|
||||
|
||||
// We need these additional pieces of information To build a shader variant asset:
|
||||
// 1- ShaderOptionsGroupLayout (Need to load it once, because it's the same acrosss all supervariants + RHIs)
|
||||
// 2- entryFunctions
|
||||
// 3- hlsl code.
|
||||
|
||||
// 1- ShaderOptionsGroupLayout
|
||||
if (!shaderOptionGroupLayout)
|
||||
{
|
||||
shaderOptionGroupLayout =
|
||||
LoadShaderOptionsGroupLayoutFromShaderAssetBuilder2(
|
||||
shaderPlatformInterface, request.m_platformInfo, azslc, shaderSourceFileFullPath, supervariantIndex);
|
||||
if (!shaderOptionGroupLayout)
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2- entryFunctions.
|
||||
AzslFunctions azslFunctions;
|
||||
LoadShaderFunctionsFromShaderAssetBuilder2(
|
||||
shaderPlatformInterface, request.m_platformInfo, azslc, shaderSourceFileFullPath, supervariantIndex, azslFunctions);
|
||||
if (azslFunctions.empty())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
MapOfStringToStageType shaderEntryPoints;
|
||||
if (shaderSourceDescriptor.m_programSettings.m_entryPoints.empty())
|
||||
{
|
||||
AZ_TracePrintf(
|
||||
ShaderVariantAssetBuilder2Name,
|
||||
"ProgramSettings do not specify entry points, will use GetDefaultEntryPointsFromShader()\n");
|
||||
ShaderBuilderUtility::GetDefaultEntryPointsFromFunctionDataList(azslFunctions, shaderEntryPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& entryPoint : shaderSourceDescriptor.m_programSettings.m_entryPoints)
|
||||
{
|
||||
shaderEntryPoints[entryPoint.m_name] = entryPoint.m_type;
|
||||
}
|
||||
}
|
||||
|
||||
// 3- hlslCode
|
||||
AZStd::string hlslSourcePath;
|
||||
AZStd::string hlslCode = LoadHlslFileFromShaderAssetBuilder2(
|
||||
shaderPlatformInterface, request.m_platformInfo, shaderSourceFileFullPath, supervariantIndex, hlslSourcePath);
|
||||
if (hlslCode.empty() || hlslSourcePath.empty())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup the shader variant creation context:
|
||||
ShaderVariantCreationContext2 shaderVariantCreationContext =
|
||||
{
|
||||
*shaderPlatformInterface, request.m_platformInfo, buildOptions.m_compilerArguments, request.m_tempDirPath,
|
||||
startTime,
|
||||
shaderSourceDescriptor,
|
||||
*shaderOptionGroupLayout.get(),
|
||||
shaderEntryPoints,
|
||||
Uuid::CreateRandom(),
|
||||
shaderStemNamePrefix,
|
||||
hlslSourcePath, hlslCode
|
||||
};
|
||||
|
||||
AZStd::optional<RHI::ShaderPlatformInterface::ByProducts> outputByproducts;
|
||||
auto shaderVariantAssetOutcome = CreateShaderVariantAsset(variantInfo, shaderVariantCreationContext, outputByproducts);
|
||||
if (!shaderVariantAssetOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "%s\n", shaderVariantAssetOutcome.GetError().c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
Data::Asset<RPI::ShaderVariantAsset2> shaderVariantAsset = shaderVariantAssetOutcome.TakeValue();
|
||||
|
||||
|
||||
// Time to save the asset in the tmp folder so it ends up in the Cache folder.
|
||||
const uint32_t productSubID = RPI::ShaderVariantAsset2::MakeAssetProductSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), supervariantIndex.GetIndex(),
|
||||
shaderVariantAsset->GetStableId());
|
||||
AssetBuilderSDK::JobProduct assetProduct;
|
||||
if (!SerializeOutShaderVariantAsset(shaderVariantAsset, shaderStemNamePrefix,
|
||||
request.m_tempDirPath, *shaderPlatformInterface, productSubID,
|
||||
assetProduct))
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
response.m_outputProducts.push_back(assetProduct);
|
||||
|
||||
if (outputByproducts)
|
||||
{
|
||||
// add byproducts as job output products:
|
||||
uint32_t subProductType = RPI::ShaderVariantAsset2::ShaderVariantAsset2SubProductType;
|
||||
for (const AZStd::string& byproduct : outputByproducts.value().m_intermediatePaths)
|
||||
{
|
||||
AssetBuilderSDK::JobProduct jobProduct;
|
||||
jobProduct.m_productFileName = byproduct;
|
||||
jobProduct.m_productAssetType = Uuid::CreateName("DebugInfoByProduct-PdbOrDxilTxt");
|
||||
jobProduct.m_productSubID = RPI::ShaderVariantAsset2::MakeAssetProductSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), supervariantIndex.GetIndex(), shaderVariantAsset->GetStableId(),
|
||||
subProductType++);
|
||||
response.m_outputProducts.push_back(AZStd::move(jobProduct));
|
||||
}
|
||||
}
|
||||
supervariantIndexCounter++;
|
||||
} // End of supervariant for block
|
||||
|
||||
}
|
||||
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
}
|
||||
|
||||
bool ShaderVariantAssetBuilder2::SerializeOutShaderVariantAsset(
|
||||
const Data::Asset<RPI::ShaderVariantAsset2> shaderVariantAsset, const AZStd::string& shaderStemNamePrefix,
|
||||
const AZStd::string& tempDirPath,
|
||||
const RHI::ShaderPlatformInterface& shaderPlatformInterface, const uint32_t productSubID, AssetBuilderSDK::JobProduct& assetProduct)
|
||||
{
|
||||
AZStd::string filename = AZStd::string::format(
|
||||
"%s_%s_%u.%s", shaderStemNamePrefix.c_str(), shaderPlatformInterface.GetAPIName().GetCStr(),
|
||||
shaderVariantAsset->GetStableId().GetIndex(), RPI::ShaderVariantAsset2::Extension);
|
||||
|
||||
AZStd::string assetPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(tempDirPath.c_str(), filename.c_str(), assetPath, true);
|
||||
|
||||
if (!AZ::Utils::SaveObjectToFile(assetPath, AZ::DataStream::ST_BINARY, shaderVariantAsset.Get()))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Failed to save Shader Variant Asset to \"%s\"", assetPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
assetProduct.m_productSubID = productSubID;
|
||||
assetProduct.m_productFileName = assetPath;
|
||||
assetProduct.m_productAssetType = azrtti_typeid<RPI::ShaderVariantAsset2>();
|
||||
assetProduct.m_dependenciesHandled = true; // This builder has no dependencies to output
|
||||
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "Shader Variant Asset [%s] compiled successfully.\n", assetPath.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
AZ::Outcome<Data::Asset<RPI::ShaderVariantAsset2>, AZStd::string> ShaderVariantAssetBuilder2::CreateShaderVariantAsset(
|
||||
const RPI::ShaderVariantListSourceData::VariantInfo& shaderVariantInfo,
|
||||
ShaderVariantCreationContext2& creationContext,
|
||||
AZStd::optional<RHI::ShaderPlatformInterface::ByProducts>& outputByproducts)
|
||||
{
|
||||
// Temporary structure used for sorting and caching intermediate results
|
||||
struct OptionCache
|
||||
{
|
||||
AZ::Name m_optionName;
|
||||
AZ::Name m_valueName;
|
||||
RPI::ShaderOptionIndex m_optionIndex; // Cached m_optionName
|
||||
RPI::ShaderOptionValue m_value; // Cached m_valueName
|
||||
};
|
||||
AZStd::vector<OptionCache> optionList;
|
||||
// We can not have more options than the number of options in the layout:
|
||||
optionList.reserve(creationContext.m_shaderOptionGroupLayout.GetShaderOptionCount());
|
||||
|
||||
// This loop will validate and cache the indices for each option value:
|
||||
for (const auto& shaderOption : shaderVariantInfo.m_options)
|
||||
{
|
||||
Name optionName{shaderOption.first};
|
||||
Name optionValue{shaderOption.second};
|
||||
|
||||
RPI::ShaderOptionIndex optionIndex = creationContext.m_shaderOptionGroupLayout.FindShaderOptionIndex(optionName);
|
||||
if (optionIndex.IsNull())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Invalid shader option: %s", optionName.GetCStr()));
|
||||
}
|
||||
|
||||
const RPI::ShaderOptionDescriptor& option = creationContext.m_shaderOptionGroupLayout.GetShaderOption(optionIndex);
|
||||
RPI::ShaderOptionValue value = option.FindValue(optionValue);
|
||||
if (value.IsNull())
|
||||
{
|
||||
return AZ::Failure(
|
||||
AZStd::string::format("Invalid value (%s) for shader option: %s", optionValue.GetCStr(), optionName.GetCStr()));
|
||||
}
|
||||
|
||||
optionList.push_back(OptionCache{optionName, optionValue, optionIndex, value});
|
||||
}
|
||||
|
||||
// Create one instance of the shader variant
|
||||
RPI::ShaderOptionGroup optionGroup(&creationContext.m_shaderOptionGroupLayout);
|
||||
|
||||
//! Contains the series of #define macro values that define a variant. Can be empty (root variant).
|
||||
//! If this string is NOT empty, a new temporary hlsl file will be created that will be the combination
|
||||
//! of this string + @m_hlslSourceContent.
|
||||
AZStd::string hlslCodeToPrependForVariant;
|
||||
|
||||
// We want to go over all options listed in the variant and set their respective values
|
||||
// This loop will populate the optionGroup and m_shaderCodePrefix in order of the option priority
|
||||
for (const auto& optionCache : optionList)
|
||||
{
|
||||
const RPI::ShaderOptionDescriptor& option = creationContext.m_shaderOptionGroupLayout.GetShaderOption(optionCache.m_optionIndex);
|
||||
|
||||
// Assign the option value specified in the variant:
|
||||
option.Set(optionGroup, optionCache.m_value);
|
||||
|
||||
// Populate all shader option defines. We have already confirmed they're valid.
|
||||
hlslCodeToPrependForVariant += AZStd::string::format(
|
||||
"#define %s_OPTION_DEF %s\n", optionCache.m_optionName.GetCStr(), optionCache.m_valueName.GetCStr());
|
||||
}
|
||||
|
||||
AZStd::string variantShaderSourcePath;
|
||||
// Check if we need to prepend any code prefix
|
||||
if (!hlslCodeToPrependForVariant.empty())
|
||||
{
|
||||
// Prepend any shader code prefix that we should apply to this variant
|
||||
// and save it back to a file.
|
||||
AZStd::string variantShaderSourceString(hlslCodeToPrependForVariant);
|
||||
variantShaderSourceString += creationContext.m_hlslSourceContent;
|
||||
|
||||
AZStd::string shaderAssetName = AZStd::string::format(
|
||||
"%s_%s_%u.hlsl", creationContext.m_shaderStemNamePrefix.c_str(),
|
||||
creationContext.m_shaderPlatformInterface.GetAPIName().GetCStr(), shaderVariantInfo.m_stableId);
|
||||
AzFramework::StringFunc::Path::Join(
|
||||
creationContext.m_tempDirPath.c_str(), shaderAssetName.c_str(), variantShaderSourcePath, true, true);
|
||||
|
||||
auto outcome = Utils::WriteFile(variantShaderSourceString, variantShaderSourcePath);
|
||||
if (!outcome.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to create file %s", variantShaderSourcePath.c_str()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
variantShaderSourcePath = creationContext.m_hlslSourcePath;
|
||||
}
|
||||
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "Variant StableId: %u", shaderVariantInfo.m_stableId);
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "Variant Shader Options: %s", optionGroup.ToString().c_str());
|
||||
|
||||
const RPI::ShaderVariantStableId shaderVariantStableId{shaderVariantInfo.m_stableId};
|
||||
|
||||
// By this time the optionGroup was populated with all option values for the variant and
|
||||
// the m_shaderCodePrefix contains all option related preprocessing macros
|
||||
// Let's add the requested variant:
|
||||
RPI::ShaderVariantAssetCreator2 variantCreator;
|
||||
RPI::ShaderOptionGroup shaderOptions{&creationContext.m_shaderOptionGroupLayout, optionGroup.GetShaderVariantId()};
|
||||
variantCreator.Begin(
|
||||
creationContext.m_shaderVariantAssetId, optionGroup.GetShaderVariantId(), shaderVariantStableId,
|
||||
shaderOptions.IsFullySpecified());
|
||||
|
||||
const AZStd::unordered_map<AZStd::string, RPI::ShaderStageType>& shaderEntryPoints = creationContext.m_shaderEntryPoints;
|
||||
for (const auto& shaderEntryPoint : shaderEntryPoints)
|
||||
{
|
||||
auto shaderEntryName = shaderEntryPoint.first;
|
||||
auto shaderStageType = shaderEntryPoint.second;
|
||||
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "Entry Point: %s", shaderEntryName.c_str());
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "Begin compiling shader function \"%s\"", shaderEntryName.c_str());
|
||||
|
||||
auto assetBuilderShaderType = ShaderBuilderUtility::ToAssetBuilderShaderType(shaderStageType);
|
||||
|
||||
// Compile HLSL to the platform specific shader.
|
||||
RHI::ShaderPlatformInterface::StageDescriptor descriptor;
|
||||
bool shaderWasCompiled = creationContext.m_shaderPlatformInterface.CompilePlatformInternal(
|
||||
creationContext.m_platformInfo, variantShaderSourcePath, shaderEntryName, assetBuilderShaderType,
|
||||
creationContext.m_tempDirPath, descriptor, creationContext.m_shaderCompilerArguments);
|
||||
|
||||
if (!shaderWasCompiled)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Could not compile the shader function %s", shaderEntryName.c_str()));
|
||||
}
|
||||
// bubble up the byproducts to the caller by moving them to the context.
|
||||
outputByproducts.emplace(AZStd::move(descriptor.m_byProducts));
|
||||
|
||||
RHI::Ptr<RHI::ShaderStageFunction> shaderStageFunction = creationContext.m_shaderPlatformInterface.CreateShaderStageFunction(descriptor);
|
||||
variantCreator.SetShaderFunction(ToRHIShaderStage(assetBuilderShaderType), shaderStageFunction);
|
||||
|
||||
if (descriptor.m_byProducts.m_dynamicBranchCount != AZ::RHI::ShaderPlatformInterface::ByProducts::UnknownDynamicBranchCount)
|
||||
{
|
||||
AZ_TracePrintf(
|
||||
ShaderVariantAssetBuilder2Name, "Finished compiling shader function. Number of dynamic branches: %u",
|
||||
descriptor.m_byProducts.m_dynamicBranchCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_TracePrintf(
|
||||
ShaderVariantAssetBuilder2Name, "Finished compiling shader function. Number of dynamic branches: unknown");
|
||||
}
|
||||
}
|
||||
|
||||
Data::Asset<RPI::ShaderVariantAsset2> shaderVariantAsset;
|
||||
variantCreator.End(shaderVariantAsset);
|
||||
return AZ::Success(AZStd::move(shaderVariantAsset));
|
||||
}
|
||||
|
||||
} // ShaderBuilder
|
||||
} // AZ
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AssetBuilderSDK/AssetBuilderBusses.h>
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
|
||||
#include <Atom/RHI.Reflect/Base.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Edit/Shader/ShaderSourceData.h>
|
||||
#include <Atom/RPI.Edit/Shader/ShaderVariantListSourceData.h>
|
||||
|
||||
#include "ShaderBuilderUtility.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
struct AzslData;
|
||||
|
||||
//! This is nothing more than a class to help consolidate all
|
||||
//! the data needed to generate a shader variant and prevent
|
||||
//! all the functions involved in the process to have too many
|
||||
//! arguments.
|
||||
struct ShaderVariantCreationContext2
|
||||
{
|
||||
RHI::ShaderPlatformInterface& m_shaderPlatformInterface;
|
||||
const AssetBuilderSDK::PlatformInfo& m_platformInfo;
|
||||
const RHI::ShaderCompilerArguments& m_shaderCompilerArguments;
|
||||
//! Used to write temporary files during shader compilation, like *.hlsl, or *.air, or *.metallib, etc.
|
||||
const AZStd::string& m_tempDirPath;
|
||||
//! Used to synchronize versions of the ShaderAsset and ShaderVariantAsset,
|
||||
//! especially during hot-reload. A (ShaderVariantAsset.timestamp) >= (ShaderAsset.timestamp).
|
||||
const AZStd::sys_time_t m_assetBuildTimestamp;
|
||||
const RPI::ShaderSourceData& m_shaderSourceDataDescriptor;
|
||||
const RPI::ShaderOptionGroupLayout& m_shaderOptionGroupLayout;
|
||||
const MapOfStringToStageType& m_shaderEntryPoints;
|
||||
const Data::AssetId m_shaderVariantAssetId;
|
||||
const AZStd::string& m_shaderStemNamePrefix; //<shaderName>-<supervariantName>
|
||||
const AZStd::string& m_hlslSourcePath;
|
||||
const AZStd::string& m_hlslSourceContent;
|
||||
};
|
||||
|
||||
class ShaderVariantAssetBuilder2
|
||||
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(ShaderVariantAssetBuilder2, "{C959AEC2-2083-4488-AD88-F61B1144535B}");
|
||||
|
||||
static constexpr char ShaderVariantAssetBuilder2JobKey[] = "Shader Variant Asset 2";
|
||||
|
||||
ShaderVariantAssetBuilder2() = default;
|
||||
~ShaderVariantAssetBuilder2() = default;
|
||||
|
||||
// Asset Builder Callback Functions ...
|
||||
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const;
|
||||
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const;
|
||||
|
||||
//! The ShaderVariantAsset returned by this function won't be written to the filesystem.
|
||||
//! You should call SerializeOutShaderVariantAsset to write it to the temp folder assigned
|
||||
//! by the asset processor.
|
||||
static AZ::Outcome<Data::Asset<RPI::ShaderVariantAsset2>, AZStd::string> CreateShaderVariantAsset(
|
||||
const RPI::ShaderVariantListSourceData::VariantInfo& shaderVariantInfo,
|
||||
ShaderVariantCreationContext2& creationContext,
|
||||
AZStd::optional<RHI::ShaderPlatformInterface::ByProducts>& outputByproducts);
|
||||
|
||||
static bool SerializeOutShaderVariantAsset(
|
||||
const Data::Asset<RPI::ShaderVariantAsset2> shaderVariantAsset,
|
||||
const AZStd::string& shaderStemNamePrefix, const AZStd::string& tempDirPath,
|
||||
const RHI::ShaderPlatformInterface& shaderPlatformInterface, const uint32_t productSubID, AssetBuilderSDK::JobProduct& assetProduct);
|
||||
|
||||
// AssetBuilderSDK::AssetBuilderCommandBus interface overrides ...
|
||||
void ShutDown() override { };
|
||||
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(ShaderVariantAssetBuilder2);
|
||||
|
||||
static constexpr uint32_t ShaderVariantLoadErrorParam = 0;
|
||||
static constexpr uint32_t ShaderSourceFilePathJobParam = 2;
|
||||
static constexpr uint32_t ShaderVariantJobVariantParam = 3;
|
||||
static constexpr uint32_t ShouldExitEarlyFromProcessJobParam = 4;
|
||||
|
||||
//! Called from ProcessJob when the job is supposed to create a ShaderVariantTreeAsset.
|
||||
void ProcessShaderVariantTreeJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const;
|
||||
|
||||
//! Called from ProcessJob when the job is supposed to create ShaderVariantAssets. One ShaderVariantAsset will be produced per RHI::APIType
|
||||
//! supported by the platform.
|
||||
void ProcessShaderVariantJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const;
|
||||
|
||||
static AZStd::string GetShaderVariantTreeAssetJobKey() { return AZStd::string::format("%s_varianttree", ShaderVariantAssetBuilder2JobKey); }
|
||||
static AZStd::string GetShaderVariantAssetJobKey(RPI::ShaderVariantStableId variantStableId) { return AZStd::string::format("%s_variant_%u", ShaderVariantAssetBuilder2JobKey, variantStableId.GetIndex()); }
|
||||
|
||||
};
|
||||
|
||||
} // ShaderBuilder
|
||||
} // AZ
|
||||
@@ -1,541 +0,0 @@
|
||||
/*
|
||||
* 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 <regex>
|
||||
|
||||
#include <Atom/RHI.Edit/ShaderPlatformInterface.h>
|
||||
#include <Atom/RHI.Edit/Utils.h>
|
||||
#include <Atom/RHI.Reflect/ShaderResourceGroupLayoutDescriptor.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderResourceGroupAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderResourceGroupAssetCreator.h>
|
||||
|
||||
#include <AtomCore/Serialization/Json/JsonUtils.h>
|
||||
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/Asset/AssetSystemBus.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderResourceGroupAsset.h>
|
||||
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
|
||||
#include <AzslData.h>
|
||||
#include <AzslCompiler.h>
|
||||
#include <CommonFiles/Preprocessor.h>
|
||||
#include <CommonFiles/GlobalBuildOptions.h>
|
||||
#include <SrgLayoutBuilder.h>
|
||||
#include <ShaderPlatformInterfaceRequest.h>
|
||||
#include <ShaderBuilderUtility.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
AZ::Uuid SrgLayoutBuilder::GetUUID()
|
||||
{
|
||||
return AZ::Uuid::CreateString("{ABC78905-B3FC-497A-916A-217D1460E52F}");
|
||||
}
|
||||
|
||||
void SrgLayoutBuilder::Reflect([[maybe_unused]] AZ::ReflectContext* context)
|
||||
{
|
||||
}
|
||||
|
||||
SrgLayoutBuilder::SrgLayoutBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
SrgLayoutBuilder::~SrgLayoutBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
void SrgLayoutBuilder::Activate()
|
||||
{
|
||||
}
|
||||
|
||||
void SrgLayoutBuilder::Deactivate()
|
||||
{
|
||||
}
|
||||
|
||||
void SrgLayoutBuilder::ShutDown()
|
||||
{
|
||||
}
|
||||
|
||||
RHI::ShaderInputBufferType ToShaderInputBufferType(BufferType bufferType)
|
||||
{
|
||||
switch (bufferType)
|
||||
{
|
||||
case BufferType::Buffer:
|
||||
case BufferType::RwBuffer:
|
||||
case BufferType::RasterizerOrderedBuffer:
|
||||
return RHI::ShaderInputBufferType::Typed;
|
||||
case BufferType::AppendStructuredBuffer:
|
||||
case BufferType::ConsumeStructuredBuffer:
|
||||
case BufferType::RasterizerOrderedStructuredBuffer:
|
||||
case BufferType::RwStructuredBuffer:
|
||||
case BufferType::StructuredBuffer:
|
||||
return RHI::ShaderInputBufferType::Structured;
|
||||
case BufferType::RasterizerOrderedByteAddressBuffer:
|
||||
case BufferType::ByteAddressBuffer:
|
||||
case BufferType::RwByteAddressBuffer:
|
||||
return RHI::ShaderInputBufferType::Raw;
|
||||
case BufferType::RaytracingAccelerationStructure:
|
||||
return RHI::ShaderInputBufferType::AccelerationStructure;
|
||||
default:
|
||||
AZ_Assert(false, "Unhandled BufferType");
|
||||
return RHI::ShaderInputBufferType::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
RHI::ShaderInputBufferAccess ToShaderInputBufferAccess(BufferType bufferType)
|
||||
{
|
||||
switch (bufferType)
|
||||
{
|
||||
case BufferType::Buffer:
|
||||
case BufferType::ByteAddressBuffer:
|
||||
case BufferType::ConsumeStructuredBuffer:
|
||||
case BufferType::StructuredBuffer:
|
||||
return RHI::ShaderInputBufferAccess::Read;
|
||||
case BufferType::AppendStructuredBuffer:
|
||||
case BufferType::RasterizerOrderedStructuredBuffer:
|
||||
case BufferType::RasterizerOrderedByteAddressBuffer:
|
||||
case BufferType::RasterizerOrderedBuffer:
|
||||
case BufferType::RwByteAddressBuffer:
|
||||
case BufferType::RwStructuredBuffer:
|
||||
case BufferType::RwBuffer:
|
||||
return RHI::ShaderInputBufferAccess::ReadWrite;
|
||||
default:
|
||||
AZ_Assert(false, "Unhandled BufferType");
|
||||
return RHI::ShaderInputBufferAccess::Read;
|
||||
}
|
||||
}
|
||||
|
||||
RHI::ShaderInputImageType ToShaderInputImageType(TextureType textureType)
|
||||
{
|
||||
switch (textureType)
|
||||
{
|
||||
case TextureType::Texture1D: return RHI::ShaderInputImageType::Image1D;
|
||||
case TextureType::Texture1DArray: return RHI::ShaderInputImageType::Image1DArray;
|
||||
case TextureType::Texture2D: return RHI::ShaderInputImageType::Image2D;
|
||||
case TextureType::Texture2DArray: return RHI::ShaderInputImageType::Image2DArray;
|
||||
case TextureType::Texture2DMS: return RHI::ShaderInputImageType::Image2DMultisample;
|
||||
case TextureType::Texture2DMSArray: return RHI::ShaderInputImageType::Image2DMultisampleArray;
|
||||
case TextureType::Texture3D: return RHI::ShaderInputImageType::Image3D;
|
||||
case TextureType::TextureCube: return RHI::ShaderInputImageType::ImageCube;
|
||||
case TextureType::RwTexture1D: return RHI::ShaderInputImageType::Image1D;
|
||||
case TextureType::RwTexture1DArray: return RHI::ShaderInputImageType::Image1DArray;
|
||||
case TextureType::RwTexture2D: return RHI::ShaderInputImageType::Image2D;
|
||||
case TextureType::RwTexture2DArray: return RHI::ShaderInputImageType::Image2DArray;
|
||||
case TextureType::RwTexture3D: return RHI::ShaderInputImageType::Image3D;
|
||||
case TextureType::RasterizerOrderedTexture1D: return RHI::ShaderInputImageType::Image1D;
|
||||
case TextureType::RasterizerOrderedTexture1DArray: return RHI::ShaderInputImageType::Image1DArray;
|
||||
case TextureType::RasterizerOrderedTexture2D: return RHI::ShaderInputImageType::Image2D;
|
||||
case TextureType::RasterizerOrderedTexture2DArray: return RHI::ShaderInputImageType::Image2DArray;
|
||||
case TextureType::RasterizerOrderedTexture3D: return RHI::ShaderInputImageType::Image3D;
|
||||
case TextureType::SubpassInput: return RHI::ShaderInputImageType::SubpassInput;
|
||||
default:
|
||||
AZ_Assert(false, "Unhandled TextureType");
|
||||
return RHI::ShaderInputImageType::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
RHI::ShaderInputImageAccess ToShaderInputImageAccess(TextureType textureType)
|
||||
{
|
||||
switch (textureType)
|
||||
{
|
||||
case TextureType::Texture1D:
|
||||
case TextureType::Texture1DArray:
|
||||
case TextureType::Texture2D:
|
||||
case TextureType::Texture2DArray:
|
||||
case TextureType::Texture2DMS:
|
||||
case TextureType::Texture2DMSArray:
|
||||
case TextureType::Texture3D:
|
||||
case TextureType::TextureCube:
|
||||
return RHI::ShaderInputImageAccess::Read;
|
||||
case TextureType::RwTexture1D:
|
||||
case TextureType::RwTexture1DArray:
|
||||
case TextureType::RwTexture2D:
|
||||
case TextureType::RwTexture2DArray:
|
||||
case TextureType::RwTexture3D:
|
||||
case TextureType::RasterizerOrderedTexture1D:
|
||||
case TextureType::RasterizerOrderedTexture1DArray:
|
||||
case TextureType::RasterizerOrderedTexture2D:
|
||||
case TextureType::RasterizerOrderedTexture2DArray:
|
||||
case TextureType::RasterizerOrderedTexture3D:
|
||||
return RHI::ShaderInputImageAccess::ReadWrite;
|
||||
default:
|
||||
AZ_Assert(false, "Unhandled TextureType");
|
||||
return RHI::ShaderInputImageAccess::Read;
|
||||
}
|
||||
}
|
||||
|
||||
void SrgLayoutBuilder::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const
|
||||
{
|
||||
AZStd::string fullPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.data(), request.m_sourceFile.data(), fullPath, false);
|
||||
AzFramework::StringFunc::Path::Normalize(fullPath);
|
||||
|
||||
AZ_TracePrintf(SrgLayoutBuilderName, "CreateJobs for Srg Layouts \"%s\"\n", fullPath.data());
|
||||
|
||||
for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
|
||||
{
|
||||
AssetBuilderSDK::JobDescriptor jobDescriptor;
|
||||
jobDescriptor.m_priority = 2;
|
||||
// [GFX TODO][ATOM-2830] Set 'm_critical' back to 'false' once proper fix for Atom startup issues are in
|
||||
jobDescriptor.m_critical = true;
|
||||
jobDescriptor.m_jobKey = SrgLayoutBuilderJobKey;
|
||||
jobDescriptor.SetPlatformIdentifier(info.m_identifier.data());
|
||||
|
||||
// Get the platform interfaces to be able to access the prepend file
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces = ShaderBuilderUtility::DiscoverValidShaderPlatformInterfaces(info);
|
||||
// queue up AzslBuilder dependencies:
|
||||
for (RHI::ShaderPlatformInterface* shaderPlatformInterface : platformInterfaces)
|
||||
{
|
||||
AddAzslBuilderJobDependency(jobDescriptor, info.m_identifier, shaderPlatformInterface->GetAPIName().GetCStr(), fullPath);
|
||||
}
|
||||
response.m_createJobOutputs.push_back(jobDescriptor);
|
||||
}
|
||||
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
|
||||
}
|
||||
|
||||
void SrgLayoutBuilder::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const
|
||||
{
|
||||
AZStd::string sourcePath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.c_str(), request.m_sourceFile.c_str(), sourcePath, false);
|
||||
AzFramework::StringFunc::Path::Normalize(sourcePath);
|
||||
|
||||
if (!AzFramework::StringFunc::Path::IsExtension(sourcePath.c_str(), MergedPartialSrgsExtension)) // not .srgi
|
||||
{
|
||||
auto skipCheck = ShaderBuilderUtility::ShouldSkipFileForSrgProcessing(SrgLayoutBuilderName, sourcePath);
|
||||
if (skipCheck != ShaderBuilderUtility::SrgSkipFileResult::ContinueProcess)
|
||||
{
|
||||
response.m_resultCode = skipCheck == ShaderBuilderUtility::SrgSkipFileResult::Error ?
|
||||
AssetBuilderSDK::ProcessJobResult_Failed : AssetBuilderSDK::ProcessJobResult_Success;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AZ_TracePrintf(SrgLayoutBuilderName, "Processing Shader Resource Group \"%s\".\n", sourcePath.c_str());
|
||||
|
||||
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
// Note this will update response.m_resultCode
|
||||
CreateSRGAsset(sourcePath, request, response, jobCancelListener);
|
||||
|
||||
AZ_TracePrintf(SrgLayoutBuilderName, "Finished processing %s\n", sourcePath.c_str());
|
||||
}
|
||||
|
||||
void SrgLayoutBuilder::CreateSRGAsset(
|
||||
AZStd::string fullSourcePath,
|
||||
const AssetBuilderSDK::ProcessJobRequest& request,
|
||||
AssetBuilderSDK::ProcessJobResponse& response,
|
||||
AssetBuilderSDK::JobCancelListener& jobCancelListener)
|
||||
{
|
||||
// Request the list of valid shader platform interfaces for the target platform.
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces;
|
||||
ShaderPlatformInterfaceRequestBus::BroadcastResult(platformInterfaces, &ShaderPlatformInterfaceRequest::GetShaderPlatformInterface, request.m_platformInfo);
|
||||
if (platformInterfaces.empty())
|
||||
{
|
||||
AZ_Error(SrgLayoutBuilderName, false, "No ShaderPlatformInterfaces found.");
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
using SrgDataEntry = AZStd::pair<RHI::ShaderPlatformInterface*, SrgData>;
|
||||
// List with all SRGs that need to be processed.
|
||||
AZStd::unordered_map<AZStd::string, AZStd::vector<SrgDataEntry>> srgsToProcess;
|
||||
// Emit all SRGs per each of the platform interfaces.
|
||||
for (RHI::ShaderPlatformInterface* shaderPlatformInterface : platformInterfaces)
|
||||
{
|
||||
if (!shaderPlatformInterface)
|
||||
{
|
||||
AZ_Error(SrgLayoutBuilderName, false, "ShaderPlatformInterface for [%s] is not registered, can't compile [%s]", request.m_platformInfo.m_identifier.c_str(), request.m_sourceFile.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
SrgDataContainer srgDataContainer;
|
||||
|
||||
auto azslArtifactsOutcome = ShaderBuilderUtility::ObtainBuildArtifactsFromAzslBuilder(SrgLayoutBuilderName, fullSourcePath, shaderPlatformInterface->GetAPIType(), request.m_platformInfo.m_identifier);
|
||||
if (!azslArtifactsOutcome.IsSuccess())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
// create an AzslCompiler instance to use it's json parsing facilities, but we won't call any emit facility. So the compiler actually never runs.
|
||||
AzslCompiler azslc(azslArtifactsOutcome.GetValue()[ShaderBuilderUtility::AzslSubProducts::azslin]); // set the input file for eventual error messages.
|
||||
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> outcome;
|
||||
outcome = JsonSerializationUtils::ReadJsonFile(azslArtifactsOutcome.GetValue()[ShaderBuilderUtility::AzslSubProducts::srg]);
|
||||
if (!outcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(SrgLayoutBuilderName, false, "%s", outcome.GetError().c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!azslc.ParseSrgPopulateSrgData(outcome.GetValue(), srgDataContainer))
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
for (const SrgData& srgData : srgDataContainer)
|
||||
{
|
||||
// Ignore the SRGs included from other files.
|
||||
AZStd::string normalizedContainer = srgData.m_containingFileName;
|
||||
AzFramework::StringFunc::Path::Normalize(normalizedContainer);
|
||||
if (normalizedContainer != fullSourcePath)
|
||||
{
|
||||
AZ_TracePrintf(SrgLayoutBuilderName, "SRG [%s] found in [%s] but is foreign to [%s]. skipped.",
|
||||
srgData.m_name.c_str(), normalizedContainer.c_str(), fullSourcePath.c_str());
|
||||
continue;
|
||||
}
|
||||
AZ_TracePrintf(SrgLayoutBuilderName, "SRG [%s] found in [%s] (native to this file). added.",
|
||||
srgData.m_name.c_str(), normalizedContainer.c_str());
|
||||
srgsToProcess[srgData.m_name].push_back(SrgDataEntry(shaderPlatformInterface, AZStd::move(srgData)));
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string fileNameOnly;
|
||||
AzFramework::StringFunc::Path::GetFileName(request.m_sourceFile.c_str(), fileNameOnly);
|
||||
|
||||
if (srgsToProcess.empty())
|
||||
{
|
||||
AZ_TracePrintf(SrgLayoutBuilderName, "No ShaderResourceGroups found in '%s'.", fullSourcePath.c_str());
|
||||
}
|
||||
|
||||
// Process all SRGs that were emitted.
|
||||
for (const auto& entry : srgsToProcess)
|
||||
{
|
||||
const auto& srgName = entry.first;
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::string fullFileName = fileNameOnly + "_" + srgName;
|
||||
AZStd::string shaderResourceGroupAssetPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_tempDirPath.c_str(), fullFileName.c_str(), shaderResourceGroupAssetPath, true);
|
||||
AzFramework::StringFunc::Path::ReplaceExtension(shaderResourceGroupAssetPath, "azsrg");
|
||||
|
||||
RPI::ShaderResourceGroupAssetCreator srgAssetCreator;
|
||||
srgAssetCreator.Begin(Uuid::CreateRandom(), Name{ srgName });
|
||||
|
||||
bool success = true;
|
||||
// Process the SRG per each shader platform interface.
|
||||
for(const SrgDataEntry& srgDataEntry : entry.second)
|
||||
{
|
||||
RHI::ShaderPlatformInterface* shaderPlatformInterface = srgDataEntry.first;
|
||||
|
||||
// The register number only makes sense if the platform uses "spaces",
|
||||
// since the register Id of the resource will not change even if the pipeline layout changes.
|
||||
// We can pass in a default ShaderCompilerArguments because all we care about is whether the shaderPlatformInterface
|
||||
// appends the
|
||||
// "--use-spaces" flag.
|
||||
AZStd::string azslCompilerParameters =
|
||||
shaderPlatformInterface->GetAzslCompilerParameters(RHI::ShaderCompilerArguments{});
|
||||
bool useRegisterId = (AzFramework::StringFunc::Find(azslCompilerParameters, "--use-spaces") != AZStd::string::npos);
|
||||
|
||||
const SrgData& srgData = srgDataEntry.second;
|
||||
|
||||
srgAssetCreator.BeginAPI(shaderPlatformInterface->GetAPIType());
|
||||
srgAssetCreator.SetBindingSlot(srgData.m_bindingSlot.m_index);
|
||||
|
||||
// Samplers
|
||||
for (const SamplerSrgData& samplerData : srgData.m_samplers)
|
||||
{
|
||||
if (samplerData.m_isDynamic)
|
||||
{
|
||||
srgAssetCreator.AddShaderInput({
|
||||
samplerData.m_nameId,
|
||||
samplerData.m_count,
|
||||
useRegisterId ? samplerData.m_registerId : RHI::UndefinedRegisterSlot });
|
||||
}
|
||||
else
|
||||
{
|
||||
srgAssetCreator.AddStaticSampler({
|
||||
samplerData.m_nameId,
|
||||
samplerData.m_descriptor,
|
||||
useRegisterId ? samplerData.m_registerId : RHI::UndefinedRegisterSlot });
|
||||
}
|
||||
}
|
||||
|
||||
// Images
|
||||
for (const TextureSrgData& textureData : srgData.m_textures)
|
||||
{
|
||||
const RHI::ShaderInputImageAccess imageAccess =
|
||||
textureData.m_isReadOnlyType ?
|
||||
RHI::ShaderInputImageAccess::Read :
|
||||
RHI::ShaderInputImageAccess::ReadWrite;
|
||||
|
||||
const RHI::ShaderInputImageType imageType = ToShaderInputImageType(textureData.m_type);
|
||||
|
||||
if (imageType != RHI::ShaderInputImageType::Unknown)
|
||||
{
|
||||
if (textureData.m_count != aznumeric_cast<uint32_t>(-1))
|
||||
{
|
||||
srgAssetCreator.AddShaderInput({
|
||||
textureData.m_nameId,
|
||||
imageAccess,
|
||||
imageType,
|
||||
textureData.m_count,
|
||||
useRegisterId ? textureData.m_registerId : RHI::UndefinedRegisterSlot });
|
||||
}
|
||||
else
|
||||
{
|
||||
// unbounded array
|
||||
srgAssetCreator.AddShaderInput({
|
||||
textureData.m_nameId,
|
||||
imageAccess,
|
||||
imageType,
|
||||
useRegisterId ? textureData.m_registerId : RHI::UndefinedRegisterSlot });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(SrgLayoutBuilderName, false, "Failed to build Shader Resource Group Asset: Image %s has an unknown type.", textureData.m_nameId.GetCStr());
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Buffers
|
||||
{
|
||||
for (const ConstantBufferData& cbData : srgData.m_constantBuffers)
|
||||
{
|
||||
srgAssetCreator.AddShaderInput({
|
||||
cbData.m_nameId,
|
||||
RHI::ShaderInputBufferAccess::Constant,
|
||||
RHI::ShaderInputBufferType::Constant,
|
||||
cbData.m_count,
|
||||
cbData.m_strideSize,
|
||||
useRegisterId ? cbData.m_registerId : RHI::UndefinedRegisterSlot });
|
||||
}
|
||||
|
||||
for (const BufferSrgData& bufferData : srgData.m_buffers)
|
||||
{
|
||||
const RHI::ShaderInputBufferAccess bufferAccess =
|
||||
bufferData.m_isReadOnlyType ?
|
||||
RHI::ShaderInputBufferAccess::Read :
|
||||
RHI::ShaderInputBufferAccess::ReadWrite;
|
||||
|
||||
const RHI::ShaderInputBufferType bufferType = ToShaderInputBufferType(bufferData.m_type);
|
||||
|
||||
if (bufferType != RHI::ShaderInputBufferType::Unknown)
|
||||
{
|
||||
if (bufferData.m_count != aznumeric_cast<uint32_t>(-1))
|
||||
{
|
||||
srgAssetCreator.AddShaderInput({
|
||||
bufferData.m_nameId,
|
||||
bufferAccess,
|
||||
bufferType,
|
||||
bufferData.m_count,
|
||||
bufferData.m_strideSize,
|
||||
useRegisterId ? bufferData.m_registerId : RHI::UndefinedRegisterSlot });
|
||||
}
|
||||
else
|
||||
{
|
||||
// unbounded array
|
||||
srgAssetCreator.AddShaderInput({
|
||||
bufferData.m_nameId,
|
||||
bufferAccess,
|
||||
bufferType,
|
||||
bufferData.m_strideSize,
|
||||
useRegisterId ? bufferData.m_registerId : RHI::UndefinedRegisterSlot });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(SrgLayoutBuilderName, false, "Failed to build Shader Resource Group Asset: Buffer %s has un unknown type.", bufferData.m_nameId.GetCStr());
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SRG Constants
|
||||
uint32_t constantDataRegisterId = useRegisterId ? srgData.m_srgConstantDataRegisterId : RHI::UndefinedRegisterSlot;
|
||||
for (const SrgConstantData& srgConstants : srgData.m_srgConstantData)
|
||||
{
|
||||
srgAssetCreator.AddShaderInput({
|
||||
srgConstants.m_nameId,
|
||||
srgConstants.m_constantByteOffset,
|
||||
srgConstants.m_constantByteSize,
|
||||
constantDataRegisterId });
|
||||
}
|
||||
|
||||
// Shader Variant Key fallback
|
||||
if (srgData.m_fallbackSize > 0)
|
||||
{
|
||||
// Designates this SRG as a ShaderVariantKey fallback
|
||||
srgAssetCreator.SetShaderVariantKeyFallback(srgData.m_fallbackName, srgData.m_fallbackSize);
|
||||
}
|
||||
|
||||
if (!srgAssetCreator.EndAPI())
|
||||
{
|
||||
AZ_Error(SrgLayoutBuilderName, false, "Failed to End API.");
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Data::Asset<RPI::ShaderResourceGroupAsset> shaderResourceGroupAsset;
|
||||
if (!srgAssetCreator.End(shaderResourceGroupAsset))
|
||||
{
|
||||
AZ_Error(SrgLayoutBuilderName, false, "Failed to build Shader Resource Group Asset");
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (AZ::IO::FileIOBase::GetInstance()->Exists(shaderResourceGroupAssetPath.c_str()))
|
||||
{
|
||||
// This would indicate a problem above, making sure each product SRG asset file path is unique.
|
||||
AZ_Error(SrgLayoutBuilderName, false, "Cannot overwrite existing file [%s]. This likely indicates conflicting SRG names.", shaderResourceGroupAssetPath.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
success = AZ::Utils::SaveObjectToFile(shaderResourceGroupAssetPath, AZ::DataStream::ST_JSON, shaderResourceGroupAsset.Get());
|
||||
if (!success)
|
||||
{
|
||||
AZ_Error(SrgLayoutBuilderName, false, "Failed to save Shader Resource Group Asset to \"%s\"", shaderResourceGroupAssetPath.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
AssetBuilderSDK::JobProduct srgAssetProduct;
|
||||
srgAssetProduct.m_productSubID = static_cast<uint32_t>(AZStd::hash<AZStd::string>()(srgName) & 0xFFFFFFFF);
|
||||
srgAssetProduct.m_productFileName = shaderResourceGroupAssetPath;
|
||||
srgAssetProduct.m_productAssetType = azrtti_typeid<RPI::ShaderResourceGroupAsset>();
|
||||
srgAssetProduct.m_dependenciesHandled = true; // This builder has no dependencies to output
|
||||
response.m_outputProducts.push_back(srgAssetProduct);
|
||||
|
||||
AZ_TracePrintf(SrgLayoutBuilderName, "Shader Resource Group Asset compiled successfully.\n");
|
||||
}
|
||||
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
}
|
||||
} // namespace ShaderBuilder
|
||||
} // namespace AZ
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AssetBuilderSDK/AssetBuilderBusses.h>
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
|
||||
#include <CommonFiles/CommonTypes.h>
|
||||
#include <AzslData.h>
|
||||
|
||||
#include "AzslBuilder.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Data
|
||||
{
|
||||
class AssetHandler;
|
||||
}
|
||||
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
struct SrgData;
|
||||
struct TypeIdPair;
|
||||
|
||||
class SrgLayoutBuilder
|
||||
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
|
||||
{
|
||||
public:
|
||||
SrgLayoutBuilder();
|
||||
~SrgLayoutBuilder();
|
||||
|
||||
// An *.srgi file is nothing more than a regular azsl file that simply includes a set of srg/azsli
|
||||
// files, and in turn each one of those included files define "partial ShaderResourceGroup"s, which are
|
||||
// merged into a single ShaderResourceGroup by the shader compiler.
|
||||
// So, *.srgi are supposed to include files that only define "partial" SRGs.
|
||||
// And any file that defines a ShaderResourceGroup it should not be "partial" unless it is supposed
|
||||
// to be included by a *.srgi file.
|
||||
static constexpr const char* MergedPartialSrgsExtension = AzslBuilder::SrgIncludeExtension;
|
||||
static constexpr const char* SrgLayoutBuilderName = "SrgLayoutBuilder";
|
||||
static constexpr const char* SrgLayoutBuilderJobKey = "Shader Resource Group Layout";
|
||||
|
||||
// Asset Builder Callback Functions
|
||||
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const;
|
||||
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AssetBuilderSDK::AssetBuilderCommandBus interface
|
||||
void ShutDown() override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Activate();
|
||||
void Deactivate();
|
||||
|
||||
static AZ::Uuid GetUUID();
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(SrgLayoutBuilder);
|
||||
|
||||
static void CreateSRGAsset(
|
||||
AZStd::string fullSourcePath,
|
||||
const AssetBuilderSDK::ProcessJobRequest& request,
|
||||
AssetBuilderSDK::ProcessJobResponse& response,
|
||||
AssetBuilderSDK::JobCancelListener& jobCancelListener);
|
||||
|
||||
};
|
||||
|
||||
} // ShaderBuilder
|
||||
} // AZ
|
||||
@@ -109,6 +109,7 @@ namespace AZ
|
||||
{
|
||||
RHI::Ptr<RHI::ShaderResourceGroupLayout> newSrgLayout = RHI::ShaderResourceGroupLayout::Create();
|
||||
newSrgLayout->SetName(AZ::Name{srgData.m_name.c_str()});
|
||||
newSrgLayout->SetUniqueId(srgData.m_containingFileName);
|
||||
newSrgLayout->SetBindingSlot(srgData.m_bindingSlot.m_index);
|
||||
|
||||
// Samplers
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <AzCore/base.h>
|
||||
|
||||
#include "CommonFiles/CommonTypes.h"
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
#include "ShaderBuilderUtility.h"
|
||||
|
||||
namespace AZ
|
||||
|
||||
@@ -21,27 +21,19 @@ set(FILES
|
||||
Source/Editor/AzslData.h
|
||||
Source/Editor/AzslShaderBuilderSystemComponent.cpp
|
||||
Source/Editor/AzslShaderBuilderSystemComponent.h
|
||||
Source/Editor/AzslBuilder.cpp
|
||||
Source/Editor/AzslBuilder.h
|
||||
Source/Editor/ShaderAssetBuilder.cpp
|
||||
Source/Editor/ShaderAssetBuilder.h
|
||||
Source/Editor/ShaderBuilderUtility.cpp
|
||||
Source/Editor/ShaderBuilderUtility.h
|
||||
Source/Editor/ShaderPlatformInterfaceRequest.h
|
||||
Source/Editor/SrgLayoutBuilder.cpp
|
||||
Source/Editor/SrgLayoutBuilder.h
|
||||
Source/Editor/AzslCompiler.cpp
|
||||
Source/Editor/AzslCompiler.h
|
||||
Source/Editor/ShaderVariantAssetBuilder.cpp
|
||||
Source/Editor/ShaderVariantAssetBuilder.h
|
||||
Source/Editor/ShaderVariantAssetBuilder2.cpp
|
||||
Source/Editor/ShaderVariantAssetBuilder2.h
|
||||
Source/Editor/AtomShaderConfig.cpp
|
||||
Source/Editor/AtomShaderConfig.h
|
||||
Source/Editor/PrecompiledShaderBuilder.cpp
|
||||
Source/Editor/PrecompiledShaderBuilder.h
|
||||
Source/Editor/ShaderAssetBuilder2.cpp
|
||||
Source/Editor/ShaderAssetBuilder2.h
|
||||
Source/Editor/SrgLayoutUtility.cpp
|
||||
Source/Editor/SrgLayoutUtility.h
|
||||
)
|
||||
|
||||
@@ -206,8 +206,8 @@
|
||||
"$type": "RasterPassData",
|
||||
"DrawListTag": "forward",
|
||||
"PipelineViewTag": "MainCamera",
|
||||
"PassSrgAsset": {
|
||||
"FilePath": "shaderlib/atom/features/pbr/forwardpasssrg.azsli:PassSrg"
|
||||
"PassSrgShaderAsset": {
|
||||
"FilePath": "Shaders/ForwardPassSrg.shader"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -158,8 +158,8 @@
|
||||
"$type": "RasterPassData",
|
||||
"DrawListTag": "lowEndForward",
|
||||
"PipelineViewTag": "MainCamera",
|
||||
"PassSrgAsset": {
|
||||
"FilePath": "shaderlib/atom/features/pbr/forwardpasssrg.azsli:PassSrg"
|
||||
"PassSrgShaderAsset": {
|
||||
"FilePath": "Shaders/ForwardPassSrg.shader"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -122,8 +122,8 @@
|
||||
"$type": "RasterPassData",
|
||||
"DrawListTag": "forward",
|
||||
"PipelineViewTag": "MainCamera",
|
||||
"PassSrgAsset": {
|
||||
"FilePath": "shaderlib/atom/features/pbr/forwardpasssrg.azsli:PassSrg"
|
||||
"PassSrgShaderAsset": {
|
||||
"FilePath": "Shaders/ForwardPassSrg.shader"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -222,8 +222,8 @@
|
||||
"$type": "RasterPassData",
|
||||
"DrawListTag": "forwardWithSubsurfaceOutput",
|
||||
"PipelineViewTag": "MainCamera",
|
||||
"PassSrgAsset": {
|
||||
"FilePath": "shaderlib/atom/features/pbr/forwardpasssrg.azsli:PassSrg"
|
||||
"PassSrgShaderAsset": {
|
||||
"FilePath": "Shaders/ForwardPassSrg.shader"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -89,8 +89,8 @@
|
||||
"$type": "RasterPassData",
|
||||
"DrawListTag": "reflectionprobeblendweight",
|
||||
"PipelineViewTag": "MainCamera",
|
||||
"PassSrgAsset": {
|
||||
"FilePath": "shaders/reflections/reflectionprobeblendweight.azsl:PassSrg"
|
||||
"PassSrgShaderAsset": {
|
||||
"FilePath": "shaders/reflections/reflectionprobeblendweight.shader"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -203,8 +203,8 @@
|
||||
"$type": "RasterPassData",
|
||||
"DrawListTag": "reflectionproberenderouter",
|
||||
"PipelineViewTag": "MainCamera",
|
||||
"PassSrgAsset": {
|
||||
"FilePath": "shaders/reflections/reflectionproberenderouter.azsl:PassSrg"
|
||||
"PassSrgShaderAsset": {
|
||||
"FilePath": "shaders/reflections/reflectionproberenderouter.shader"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -253,8 +253,8 @@
|
||||
"$type": "RasterPassData",
|
||||
"DrawListTag": "reflectionproberenderinner",
|
||||
"PipelineViewTag": "MainCamera",
|
||||
"PassSrgAsset": {
|
||||
"FilePath": "shaders/reflections/reflectionproberenderinner.azsl:PassSrg"
|
||||
"PassSrgShaderAsset": {
|
||||
"FilePath": "shaders/reflections/reflectionproberenderinner.shader"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -84,8 +84,8 @@
|
||||
"$type": "RasterPassData",
|
||||
"DrawListTag": "reflectionprobeblendweight",
|
||||
"PipelineViewTag": "MainCamera",
|
||||
"PassSrgAsset": {
|
||||
"FilePath": "shaders/reflections/reflectionprobeblendweight.azsl:PassSrg"
|
||||
"PassSrgShaderAsset": {
|
||||
"FilePath": "shaders/reflections/reflectionprobeblendweight.shader"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -191,8 +191,8 @@
|
||||
"$type": "RasterPassData",
|
||||
"DrawListTag": "reflectionproberenderouter",
|
||||
"PipelineViewTag": "MainCamera",
|
||||
"PassSrgAsset": {
|
||||
"FilePath": "shaders/reflections/reflectionproberenderouter.azsl:PassSrg"
|
||||
"PassSrgShaderAsset": {
|
||||
"FilePath": "shaders/reflections/reflectionproberenderouter.shader"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -241,8 +241,8 @@
|
||||
"$type": "RasterPassData",
|
||||
"DrawListTag": "reflectionproberenderinner",
|
||||
"PipelineViewTag": "MainCamera",
|
||||
"PassSrgAsset": {
|
||||
"FilePath": "shaders/reflections/reflectionproberenderinner.azsl:PassSrg"
|
||||
"PassSrgShaderAsset": {
|
||||
"FilePath": "shaders/reflections/reflectionproberenderinner.shader"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -123,8 +123,8 @@
|
||||
"DrawListTag": "transparent",
|
||||
"DrawListSortType": "KeyThenReverseDepth",
|
||||
"PipelineViewTag": "MainCamera",
|
||||
"PassSrgAsset": {
|
||||
"FilePath": "shaderlib/atom/features/pbr/forwardpasssrg.azsli:PassSrg"
|
||||
"PassSrgShaderAsset": {
|
||||
"FilePath": "Shaders/ForwardPassSrg.shader"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// This shader is not used for rendering.
|
||||
// The only purpose of this shader is to have a shader asset that can be used
|
||||
// at runtime to find the RayTracingSceneSrg and RayTracingMaterialSrg layouts.
|
||||
|
||||
#include <Atom/Features/RayTracing/RayTracingSceneSrg.azsli>
|
||||
#include <Atom/Features/RayTracing/RayTracingMaterialSrg.azsli>
|
||||
#include <Atom/RPI/DummyEntryFunctions.azsli>
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"Source" : "RayTracingSrgs.azsl",
|
||||
|
||||
"CompilerHints":
|
||||
{
|
||||
"DxcAdditionalFreeArguments" : "-fspv-target-env=vulkan1.2"
|
||||
},
|
||||
|
||||
"DepthStencilState" :
|
||||
{
|
||||
"Depth" :
|
||||
{
|
||||
"Enable" : false
|
||||
},
|
||||
"Stencil" :
|
||||
{
|
||||
"Enable" : false
|
||||
}
|
||||
},
|
||||
|
||||
"ProgramSettings":
|
||||
{
|
||||
"EntryPoints":
|
||||
[
|
||||
{
|
||||
"name": "MainVS",
|
||||
"type": "Vertex"
|
||||
},
|
||||
{
|
||||
"name": "MainPS",
|
||||
"type": "Fragment"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"Supervariants":
|
||||
[
|
||||
{
|
||||
"Name": "",
|
||||
"PlusArguments": "",
|
||||
"MinusArguments": "--strip-unused-srgs"
|
||||
}
|
||||
]
|
||||
}
|
||||
-4
@@ -9,10 +9,6 @@
|
||||
[
|
||||
"pc"
|
||||
],
|
||||
"ShaderResourceGroupAssets":
|
||||
[
|
||||
"diffuseprobegridblenddistance_passsrg.azsrg"
|
||||
],
|
||||
"RootShaderVariantAssets":
|
||||
[
|
||||
{
|
||||
|
||||
-4
@@ -9,10 +9,6 @@
|
||||
[
|
||||
"pc"
|
||||
],
|
||||
"ShaderResourceGroupAssets":
|
||||
[
|
||||
"diffuseprobegridblendirradiance_passsrg.azsrg"
|
||||
],
|
||||
"RootShaderVariantAssets":
|
||||
[
|
||||
{
|
||||
|
||||
-4
@@ -9,10 +9,6 @@
|
||||
[
|
||||
"pc"
|
||||
],
|
||||
"ShaderResourceGroupAssets":
|
||||
[
|
||||
"diffuseprobegridborderupdate_passsrg.azsrg"
|
||||
],
|
||||
"RootShaderVariantAssets":
|
||||
[
|
||||
{
|
||||
|
||||
-4
@@ -9,10 +9,6 @@
|
||||
[
|
||||
"pc"
|
||||
],
|
||||
"ShaderResourceGroupAssets":
|
||||
[
|
||||
"diffuseprobegridborderupdate_passsrg.azsrg"
|
||||
],
|
||||
"RootShaderVariantAssets":
|
||||
[
|
||||
{
|
||||
|
||||
-4
@@ -9,10 +9,6 @@
|
||||
[
|
||||
"pc"
|
||||
],
|
||||
"ShaderResourceGroupAssets":
|
||||
[
|
||||
"diffuseprobegridclassification_passsrg.azsrg"
|
||||
],
|
||||
"RootShaderVariantAssets":
|
||||
[
|
||||
{
|
||||
|
||||
-4
@@ -9,10 +9,6 @@
|
||||
[
|
||||
"pc"
|
||||
],
|
||||
"ShaderResourceGroupAssets":
|
||||
[
|
||||
"diffuseprobegridraytracingcommon_raytracingglobalsrg.azsrg"
|
||||
],
|
||||
"RootShaderVariantAssets":
|
||||
[
|
||||
{
|
||||
|
||||
-4
@@ -9,10 +9,6 @@
|
||||
[
|
||||
"pc"
|
||||
],
|
||||
"ShaderResourceGroupAssets":
|
||||
[
|
||||
"diffuseprobegridraytracingcommon_raytracingglobalsrg.azsrg"
|
||||
],
|
||||
"RootShaderVariantAssets":
|
||||
[
|
||||
{
|
||||
|
||||
-4
@@ -9,10 +9,6 @@
|
||||
[
|
||||
"pc"
|
||||
],
|
||||
"ShaderResourceGroupAssets":
|
||||
[
|
||||
"diffuseprobegridraytracingcommon_raytracingglobalsrg.azsrg"
|
||||
],
|
||||
"RootShaderVariantAssets":
|
||||
[
|
||||
{
|
||||
|
||||
-4
@@ -9,10 +9,6 @@
|
||||
[
|
||||
"pc"
|
||||
],
|
||||
"ShaderResourceGroupAssets":
|
||||
[
|
||||
"diffuseprobegridrelocation_passsrg.azsrg"
|
||||
],
|
||||
"RootShaderVariantAssets":
|
||||
[
|
||||
{
|
||||
|
||||
-5
@@ -9,11 +9,6 @@
|
||||
[
|
||||
"pc"
|
||||
],
|
||||
"ShaderResourceGroupAssets":
|
||||
[
|
||||
"diffuseprobegridrender_passsrg.azsrg",
|
||||
"diffuseprobegridrender_objectsrg.azsrg"
|
||||
],
|
||||
"RootShaderVariantAssets":
|
||||
[
|
||||
{
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
-8329
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
-8329
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
-1333
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
-8071
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
-9925
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
-8551
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
-9787
File diff suppressed because it is too large
Load Diff
-1675
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// This shader is not used for rendering.
|
||||
// The only purpose of this shader is to have a shader asset that can be used
|
||||
// at runtime to find the PassSrg required by most Passes.
|
||||
|
||||
#include <Atom/Features/PBR/ForwardPassSrg.azsli>
|
||||
#include <Atom/RPI/DummyEntryFunctions.azsli>
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"Source" : "ForwardPassSrg.azsl",
|
||||
|
||||
"DepthStencilState" :
|
||||
{
|
||||
"Depth" :
|
||||
{
|
||||
"Enable" : false
|
||||
},
|
||||
"Stencil" :
|
||||
{
|
||||
"Enable" : false
|
||||
}
|
||||
},
|
||||
|
||||
"ProgramSettings":
|
||||
{
|
||||
"EntryPoints":
|
||||
[
|
||||
{
|
||||
"name": "MainVS",
|
||||
"type": "Vertex"
|
||||
},
|
||||
{
|
||||
"name": "MainPS",
|
||||
"type": "Fragment"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"Supervariants":
|
||||
[
|
||||
{
|
||||
"Name": "",
|
||||
"PlusArguments": "",
|
||||
"MinusArguments": "--strip-unused-srgs"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <scenesrg.srgi>
|
||||
#include <viewsrg.srgi>
|
||||
|
||||
ShaderResourceGroup PassSrg : SRG_PerDraw
|
||||
ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback
|
||||
{
|
||||
Texture2D<float4> m_colorAndDofFactorTexture;
|
||||
float m_radiusMin;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include <viewsrg.srgi>
|
||||
|
||||
ShaderResourceGroup PassSrg : SRG_PerDraw
|
||||
ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback
|
||||
{
|
||||
Texture2D<float4> m_InputColor;
|
||||
Texture2D<float4> m_InputDepth;
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
#include <Atom/Features/PostProcessing/PostProcessUtil.azsli>
|
||||
#include "EyeAdaptationUtil.azsli"
|
||||
|
||||
ShaderResourceGroup PassSrg : SRG_PerDraw
|
||||
ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback
|
||||
{
|
||||
Texture2D<float4> m_framebuffer;
|
||||
Sampler LinearSampler
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include <Atom/Features/PostProcessing/PostProcessUtil.azsli>
|
||||
#include <Atom/Features/PostProcessing/Aces.azsli>
|
||||
|
||||
ShaderResourceGroup PassSrg : SRG_PerDraw
|
||||
ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback
|
||||
{
|
||||
Texture2D<float4> m_framebuffer;
|
||||
Sampler LinearSampler
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ struct VSOutputBlendingWeightCalculation
|
||||
float4 m_offset[3] : TEXCOORD2;
|
||||
};
|
||||
|
||||
ShaderResourceGroup PassSrg : SRG_PerDraw
|
||||
ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback
|
||||
{
|
||||
Texture2D<float4> m_framebuffer;
|
||||
Texture2D<float4> m_areaTexture;
|
||||
|
||||
@@ -24,7 +24,7 @@ struct VSOutputSMAAEdgeDetection
|
||||
float4 m_offset[3] : TEXCOORD1;
|
||||
};
|
||||
|
||||
ShaderResourceGroup PassSrg : SRG_PerDraw
|
||||
ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback
|
||||
{
|
||||
Texture2D<float4> m_framebuffer;
|
||||
Texture2D<float4> m_depthTexture;
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ struct VSOutputNeighborhoodBlending
|
||||
float4 m_offset : TEXCOORD1;
|
||||
};
|
||||
|
||||
ShaderResourceGroup PassSrg : SRG_PerDraw
|
||||
ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback
|
||||
{
|
||||
Texture2D<float4> m_framebuffer;
|
||||
Texture2D<float4> m_framebufferPassThrough;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user