Fixed race conditions that could prevent successful hot-reload of shaders.

ATOM-15728 Shader Hot Reload Fails in Debug Build

The main change was to add OnAssetReady handlers to each of the asset classes. See comments in ShaderAsset::OnAssetReady for a detailed explanation. In short, OnAssetReloaded gets missed while assets are being reloaded at the same time on multiple threads, but OnAssetReady is always called whenever connecting to the AssetBus because of its AssetConnectionPolicy.

The above change required the addition of a new AssetInitBus to call the PostLoadInit() functions. Because OnAssetReady connects to buses that are not mutex-protected, they have to be connected on the main thread. AssetInitBus::PostLoadInit is called every frame in RPISystem::SimulationTick. All Atom's asset handlers that need to do post-load initialization must connect to the AssetInitBus, and the asset will disconnect itself after initialization is complete.

We also need the Shader class to handle OnShaderAssetReinitialized to properly handle the shader reload.

With these changes I can click back and forth between "Blending On" and "Blending Off" many times (like 20 times) without issue.
main
Chris Santora 5 years ago
parent 406792606b
commit 982406d4d5

@ -0,0 +1,41 @@
/*
* 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/EBus/EBus.h>
namespace AZ
{
namespace RPI
{
//! Bus for post-load initialization of assets.
//! Assets that need to do post-load initialization should connect to this bus in their asset handler's LoadAssetData() function.
//! Be sure to disconnect from this bus as soon as initialization is complete, as it will be called every frame.
class AssetInitEvents
: public EBusTraits
{
public:
// EBus Configuration
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
typedef AZStd::recursive_mutex MutexType;
//! This function is called every frame on the main thread to perform any necessary post-load initialization.
//! Connect to the bus after loading the asset data, and disconnect when initialization is complete.
//! @return whether initialization was successful
virtual bool PostLoadInit() = 0;
};
using AssetInitBus = AZ::EBus<AssetInitEvents>;
} // namespace RPI
} // namespace AZ

@ -12,6 +12,7 @@
#pragma once
#include <Atom/RPI.Public/Shader/ShaderVariant.h>
#include <Atom/RPI.Public/Shader/ShaderReloadNotificationBus.h>
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
@ -57,6 +58,7 @@ namespace AZ
: public Data::InstanceData
, public Data::AssetBus::Handler
, public ShaderVariantFinderNotificationBus::Handler
, public ShaderReloadNotificationBus::Handler
{
friend class ShaderSystem;
public:
@ -149,6 +151,15 @@ namespace AZ
void OnShaderVariantTreeAssetReady(Data::Asset<ShaderVariantTreeAsset> /*shaderVariantTreeAsset*/, bool /*isError*/) override {};
void OnShaderVariantAssetReady(Data::Asset<ShaderVariantAsset> shaderVariantAsset, bool IsError) override;
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
// ShaderReloadNotificationBus overrides...
void OnShaderAssetReinitialized(const Data::Asset<ShaderAsset>& shaderAsset) override;
// Note we don't need OnShaderVariantReinitialized because the Shader class doesn't do anything with the data inside
// the ShaderVariant object. The only thing we might want to do is propagate the message upward, but that's unnecessary
// because the ShaderReloadNotificationBus uses the Shader's AssetId as the ID for all messages including those from the variants.
// And of course we don't need to handle OnShaderReinitialized because this *is* this Shader.
///////////////////////////////////////////////////////////////////
/// Returns the path to the pipeline library cache file.
AZStd::string GetPipelineLibraryPath() const;

@ -15,6 +15,7 @@
#include <AzCore/std/containers/vector.h>
#include <AtomCore/std/containers/array_view.h>
#include <Atom/RPI.Public/AssetInitBus.h>
#include <Atom/RPI.Reflect/Base.h>
#include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertyValue.h>
@ -38,6 +39,7 @@ namespace AZ
: public AZ::Data::AssetData
, public Data::AssetBus::Handler
, public MaterialReloadNotificationBus::Handler
, public AssetInitBus::Handler
{
friend class MaterialAssetCreator;
friend class MaterialAssetHandler;
@ -90,13 +92,17 @@ namespace AZ
AZStd::array_view<MaterialPropertyValue> GetPropertyValues() const;
private:
bool PostLoadInit();
bool PostLoadInit() override;
//! Called by asset creators to assign the asset to a ready state.
void SetReady();
// AssetBus overrides...
void OnAssetReloaded(Data::Asset<Data::AssetData> asset) override;
void OnAssetReady(Data::Asset<Data::AssetData> asset) override;
//! Replaces the MaterialTypeAsset when a reload occurs
void ReinitializeMaterialTypeAsset(Data::Asset<Data::AssetData> asset);
// MaterialReloadNotificationBus overrides...
void OnMaterialTypeAssetReinitialized(const Data::Asset<MaterialTypeAsset>& materialTypeAsset) override;

@ -17,6 +17,7 @@
#include <AzCore/EBus/Event.h>
#include <AtomCore/std/containers/array_view.h>
#include <Atom/RPI.Public/AssetInitBus.h>
#include <Atom/RPI.Reflect/Base.h>
#include <Atom/RPI.Reflect/Material/ShaderCollection.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
@ -52,6 +53,7 @@ namespace AZ
class MaterialTypeAsset
: public AZ::Data::AssetData
, public Data::AssetBus::MultiHandler
, public AssetInitBus::Handler
{
friend class MaterialTypeAssetCreator;
friend class MaterialTypeAssetHandler;
@ -100,13 +102,17 @@ namespace AZ
MaterialUvNameMap GetUvNameMap() const;
private:
bool PostLoadInit();
bool PostLoadInit() override;
//! Called by asset creators to assign the asset to a ready state.
void SetReady();
// AssetBus overrides...
void OnAssetReloaded(Data::Asset<Data::AssetData> asset) override;
void OnAssetReady(Data::Asset<Data::AssetData> asset) override;
//! Replaces the appropriate asset members when a reload occurs
void ReinitializeAsset(Data::Asset<Data::AssetData> asset);
//! Holds values for each material property, used to initialize Material instances.
//! This is indexed by MaterialPropertyIndex and aligns with entries in m_materialPropertiesLayout.

@ -15,6 +15,7 @@
#include <AzCore/std/optional.h>
#include <AzCore/EBus/Event.h>
#include <Atom/RPI.Public/AssetInitBus.h>
#include <Atom/RPI.Reflect/Asset/AssetHandler.h>
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroupLayout.h>
#include <Atom/RPI.Reflect/Shader/ShaderVariantAsset.h>
@ -40,6 +41,7 @@ namespace AZ
: public Data::AssetData
, public ShaderVariantFinderNotificationBus::Handler
, public Data::AssetBus::Handler
, public AssetInitBus::Handler
{
friend class ShaderAssetCreator;
friend class ShaderAssetHandler;
@ -134,8 +136,11 @@ namespace AZ
///////////////////////////////////////////////////////////////////
/// AssetBus overrides
void OnAssetReloaded(Data::Asset<Data::AssetData> asset) override;
void OnAssetReady(Data::Asset<Data::AssetData> asset) override;
///////////////////////////////////////////////////////////////////
void ReinitializeRootShaderVariant(Data::Asset<Data::AssetData> asset);
///////////////////////////////////////////////////////////////////
/// ShaderVariantFinderNotificationBus overrides
void OnShaderVariantTreeAssetReady(Data::Asset<ShaderVariantTreeAsset> shaderVariantTreeAsset, bool isError) override;
@ -165,7 +170,7 @@ namespace AZ
RHI::ShaderStageAttributeMapList m_attributeMaps;
};
bool FinalizeAfterLoad();
bool PostLoadInit() override;
void SetReady();
ShaderApiDataContainer& GetCurrentShaderApiData();
const ShaderApiDataContainer& GetCurrentShaderApiData() const;
@ -216,7 +221,6 @@ namespace AZ
const Data::Asset<Data::AssetData>& asset,
AZStd::shared_ptr<Data::AssetDataStream> stream,
const Data::AssetFilterCB& assetLoadFilterCB) override;
Data::AssetHandler::LoadResult PostLoadInit(const Data::Asset<Data::AssetData>& asset);
};
//////////////////////////////////////////////////////////////////////////

@ -23,6 +23,7 @@
#include <Atom/RPI.Reflect/System/RenderPipelineDescriptor.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <Atom/RPI.Public/AssetInitBus.h>
#include <Atom/RPI.Public/FeatureProcessor.h>
#include <Atom/RPI.Public/GpuQuery/GpuQueryTypes.h>
#include <Atom/RPI.Public/Scene.h>
@ -240,6 +241,8 @@ namespace AZ
}
AZ_ATOM_PROFILE_FUNCTION("RPI", "RPISystem: SimulationTick");
AssetInitBus::Broadcast(&AssetInitBus::Events::PostLoadInit);
// Update tick time info
FillTickTimeInfo();

@ -21,7 +21,6 @@
#include <AtomCore/Instance/InstanceDatabase.h>
#include <AzCore/Interface/Interface.h>
#include <Atom/RPI.Public/Shader/ShaderReloadNotificationBus.h>
#include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
namespace AZ
@ -55,8 +54,9 @@ namespace AZ
RHI::ResultCode Shader::Init(ShaderAsset& shaderAsset)
{
Data::AssetBus::Handler::BusDisconnect();
ShaderReloadNotificationBus::Handler::BusDisconnect();
ShaderVariantFinderNotificationBus::Handler::BusDisconnect();
ShaderVariantFinderNotificationBus::Handler::BusConnect(shaderAsset.GetId());
RHI::RHISystemInterface* rhiSystem = RHI::RHISystemInterface::Get();
RHI::DrawListTagRegistry* drawListTagRegistry = rhiSystem->GetDrawListTagRegistry();
@ -100,8 +100,10 @@ namespace AZ
AZ_Error("Shader", false, "Failed to acquire a DrawListTag. Entries are full.");
}
}
ShaderVariantFinderNotificationBus::Handler::BusConnect(m_asset.GetId());
Data::AssetBus::Handler::BusConnect(m_asset.GetId());
ShaderReloadNotificationBus::Handler::BusConnect(m_asset.GetId());
return RHI::ResultCode::Success;
}
@ -110,6 +112,7 @@ namespace AZ
{
ShaderVariantFinderNotificationBus::Handler::BusDisconnect();
Data::AssetBus::Handler::BusDisconnect();
ShaderReloadNotificationBus::Handler::BusDisconnect();
if (m_pipelineLibraryHandle.IsValid())
{
@ -139,7 +142,6 @@ namespace AZ
Data::Asset<ShaderAsset> newAsset = { asset.GetAs<ShaderAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };
AZ_Assert(newAsset, "Reloaded ShaderAsset is null");
Data::AssetBus::Handler::BusDisconnect();
Init(*newAsset.Get());
ShaderReloadNotificationBus::Event(asset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderReinitialized, *this);
}
@ -196,6 +198,19 @@ namespace AZ
}
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
// ShaderReloadNotificationBus overrides...
void Shader::OnShaderAssetReinitialized(const Data::Asset<ShaderAsset>& shaderAsset)
{
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Shader::OnShaderAssetReinitialized %s", this, shaderAsset.GetHint().c_str());
Init(*m_asset.Get());
ShaderReloadNotificationBus::Event(shaderAsset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderReinitialized, *this);
}
///////////////////////////////////////////////////////////////////
ConstPtr<RHI::PipelineLibraryData> Shader::LoadPipelineLibrary() const
{
if (IO::FileIOBase::GetInstance())

@ -18,6 +18,7 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Component/TickBus.h>
namespace AZ
{
@ -47,6 +48,7 @@ namespace AZ
{
MaterialReloadNotificationBus::Handler::BusDisconnect();
Data::AssetBus::Handler::BusDisconnect();
AssetInitBus::Handler::BusDisconnect();
}
const Data::Asset<MaterialTypeAsset>& MaterialAsset::GetMaterialTypeAsset() const
@ -97,6 +99,8 @@ namespace AZ
{
if (!m_materialTypeAsset.Get())
{
AssetInitBus::Handler::BusDisconnect();
// Any MaterialAsset with invalid MaterialTypeAsset is not a successfully-loaded asset.
return false;
}
@ -104,6 +108,8 @@ namespace AZ
{
Data::AssetBus::Handler::BusConnect(m_materialTypeAsset.GetId());
MaterialReloadNotificationBus::Handler::BusConnect(m_materialTypeAsset.GetId());
AssetInitBus::Handler::BusDisconnect();
return true;
}
@ -116,11 +122,9 @@ namespace AZ
// Ultimately it's the Material that cares about these changes, so we just forward any signal we get.
MaterialReloadNotificationBus::Event(GetId(), &MaterialReloadNotifications::OnMaterialAssetReinitialized, Data::Asset<MaterialAsset>{this, AZ::Data::AssetLoadBehavior::PreLoad});
}
void MaterialAsset::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
void MaterialAsset::ReinitializeMaterialTypeAsset(Data::Asset<Data::AssetData> asset)
{
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialAsset::OnAssetReloaded %s", this, asset.GetHint().c_str());
Data::Asset<MaterialTypeAsset> newMaterialTypeAsset = { asset.GetAs<MaterialTypeAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };
if (newMaterialTypeAsset)
@ -135,16 +139,31 @@ namespace AZ
}
}
void MaterialAsset::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
{
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialAsset::OnAssetReloaded %s", this, asset.GetHint().c_str());
ReinitializeMaterialTypeAsset(asset);
}
void MaterialAsset::OnAssetReady(Data::Asset<Data::AssetData> asset)
{
// Regarding why we listen to both OnAssetReloaded and OnAssetReady, see explanation in ShaderAsset::OnAssetReady.
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialAsset::OnAssetReady %s", this, asset.GetHint().c_str());
ReinitializeMaterialTypeAsset(asset);
}
Data::AssetHandler::LoadResult MaterialAssetHandler::LoadAssetData(
const AZ::Data::Asset<AZ::Data::AssetData>& asset,
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
const AZ::Data::AssetFilterCB& assetLoadFilterCB)
{
Data::AssetHandler::LoadResult baseResult = Base::LoadAssetData(asset, stream, assetLoadFilterCB);
bool postLoadResult = asset.GetAs<MaterialAsset>()->PostLoadInit();
return ((baseResult == Data::AssetHandler::LoadResult::LoadComplete) && postLoadResult) ?
Data::AssetHandler::LoadResult::LoadComplete :
Data::AssetHandler::LoadResult::Error;
if (Base::LoadAssetData(asset, stream, assetLoadFilterCB) == Data::AssetHandler::LoadResult::LoadComplete)
{
asset.GetAs<MaterialAsset>()->AssetInitBus::Handler::BusConnect();
return Data::AssetHandler::LoadResult::LoadComplete;
}
return Data::AssetHandler::LoadResult::Error;
}
} // namespace RPI

@ -65,6 +65,7 @@ namespace AZ
MaterialTypeAsset::~MaterialTypeAsset()
{
Data::AssetBus::MultiHandler::BusDisconnect();
AssetInitBus::Handler::BusDisconnect();
}
const ShaderCollection& MaterialTypeAsset::GetShaderCollection() const
@ -116,6 +117,8 @@ namespace AZ
Data::AssetBus::MultiHandler::BusConnect(shaderItem.GetShaderAsset().GetId());
}
AssetInitBus::Handler::BusDisconnect();
return true;
}
@ -127,11 +130,9 @@ namespace AZ
assetToReplace = newAsset;
}
}
void MaterialTypeAsset::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
void MaterialTypeAsset::ReinitializeAsset(Data::Asset<Data::AssetData> asset)
{
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialTypeAsset::OnAssetReloaded %s", this, asset.GetHint().c_str());
// The order of asset reloads is non-deterministic. If the MaterialTypeAsset reloads before these
// dependency assets, this will make sure the MaterialTypeAsset gets the latest ones when they reload.
// Or in some cases a these assets could get updated and reloaded without reloading the MaterialTypeAsset at all.
@ -146,16 +147,31 @@ namespace AZ
MaterialReloadNotificationBus::Event(GetId(), &MaterialReloadNotifications::OnMaterialTypeAssetReinitialized, Data::Asset<MaterialTypeAsset>{this, AZ::Data::AssetLoadBehavior::PreLoad});
}
void MaterialTypeAsset::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
{
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialTypeAsset::OnAssetReloaded %s", this, asset.GetHint().c_str());
ReinitializeAsset(asset);
}
void MaterialTypeAsset::OnAssetReady(Data::Asset<Data::AssetData> asset)
{
// Regarding why we listen to both OnAssetReloaded and OnAssetReady, see explanation in ShaderAsset::OnAssetReady.
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialTypeAsset::OnAssetReady %s", this, asset.GetHint().c_str());
ReinitializeAsset(asset);
}
AZ::Data::AssetHandler::LoadResult MaterialTypeAssetHandler::LoadAssetData(
const AZ::Data::Asset<AZ::Data::AssetData>& asset,
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
const AZ::Data::AssetFilterCB& assetLoadFilterCB)
{
Data::AssetHandler::LoadResult baseResult = Base::LoadAssetData(asset, stream, assetLoadFilterCB);
bool postLoadResult = asset.GetAs<MaterialTypeAsset>()->PostLoadInit();
return ((baseResult == Data::AssetHandler::LoadResult::LoadComplete) && postLoadResult) ?
Data::AssetHandler::LoadResult::LoadComplete :
Data::AssetHandler::LoadResult::Error;
if (Base::LoadAssetData(asset, stream, assetLoadFilterCB) == Data::AssetHandler::LoadResult::LoadComplete)
{
asset.GetAs<MaterialTypeAsset>()->AssetInitBus::Handler::BusConnect();
return Data::AssetHandler::LoadResult::LoadComplete;
}
return Data::AssetHandler::LoadResult::Error;
}
}

@ -85,6 +85,7 @@ namespace AZ
{
Data::AssetBus::Handler::BusDisconnect();
ShaderVariantFinderNotificationBus::Handler::BusDisconnect();
AssetInitBus::Handler::BusDisconnect();
}
const Name& ShaderAsset::GetName() const
@ -332,7 +333,7 @@ namespace AZ
return m_perAPIShaderData[0];
}
bool ShaderAsset::FinalizeAfterLoad()
bool ShaderAsset::PostLoadInit()
{
// Use the current RHI that is active to select which shader data to use.
// We don't assert if the Factory is not available because this method could be called during build time,
@ -373,25 +374,43 @@ namespace AZ
// Once the ShaderAsset is loaded, it is necessary to listen for changes in the Root Variant Asset.
Data::AssetBus::Handler::BusConnect(GetRootVariant().GetId());
ShaderVariantFinderNotificationBus::Handler::BusConnect(GetId());
AssetInitBus::Handler::BusDisconnect();
return true;
}
void ShaderAsset::ReinitializeRootShaderVariant(Data::Asset<Data::AssetData> asset)
{
Data::Asset<ShaderVariantAsset> shaderVariantAsset = { asset.GetAs<ShaderVariantAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };
AZ_Assert(shaderVariantAsset->GetStableId() == RootShaderVariantStableId, "Was expecting to update the root variant");
GetCurrentShaderApiData().m_rootShaderVariantAsset = asset;
ShaderReloadNotificationBus::Event(GetId(), &ShaderReloadNotificationBus::Events::OnShaderAssetReinitialized, Data::Asset<ShaderAsset>{ this, AZ::Data::AssetLoadBehavior::PreLoad } );
}
///////////////////////////////////////////////////////////////////////
// AssetBus overrides...
void ShaderAsset::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
{
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderAsset::OnAssetReloaded %s", this, asset.GetHint().c_str());
Data::Asset<ShaderVariantAsset> shaderVariantAsset = { asset.GetAs<ShaderVariantAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };
AZ_Assert(shaderVariantAsset->GetStableId() == RootShaderVariantStableId,
"Was expecting to update the root variant");
GetCurrentShaderApiData().m_rootShaderVariantAsset = asset;
ShaderReloadNotificationBus::Event(GetId(), &ShaderReloadNotificationBus::Events::OnShaderAssetReinitialized, Data::Asset<ShaderAsset>{ this, AZ::Data::AssetLoadBehavior::PreLoad } );
ReinitializeRootShaderVariant(asset);
}
void ShaderAsset::OnAssetReady(Data::Asset<Data::AssetData> asset)
{
// We have to listen to OnAssetReady, OnAssetReloaded isn't enough, because of the following scenario:
// The user changes a .shader file, which causes the AP to rebuild the ShaderAsset and root ShaderVariantAsset.
// 1) Thread A creates the new ShaderAsset, loads it, and gets the old ShaderVariantAsset.
// 2) Thread B creates the new ShaderVariantAsset, loads it, and calls OnAssetReloaded.
// 3) Main thread calls ShaderAsset::PostLoadInit which connects to the AssetBus but it's too late to receive OnAssetReloaded,
// so it continues using the old ShaderVariantAsset instead of the new one.
// The OnAssetReady bus function is called automatically whenever a connection to AssetBus is made, so listening to this gives
// us the opportunity to assign the appropriate ShaderVariantAsset.
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderAsset::OnAssetReady %s", this, asset.GetHint().c_str());
ReinitializeRootShaderVariant(asset);
}
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
/// ShaderVariantFinderNotificationBus overrides
void ShaderAsset::OnShaderVariantTreeAssetReady(Data::Asset<ShaderVariantTreeAsset> shaderVariantTreeAsset, bool isError)
@ -425,25 +444,13 @@ namespace AZ
{
if (Base::LoadAssetData(asset, stream, assetLoadFilterCB) == Data::AssetHandler::LoadResult::LoadComplete)
{
return PostLoadInit(asset);
}
return Data::AssetHandler::LoadResult::Error;
}
Data::AssetHandler::LoadResult ShaderAssetHandler::PostLoadInit(const Data::Asset<Data::AssetData>& asset)
{
if (ShaderAsset* shaderAsset = asset.GetAs<ShaderAsset>())
{
if (!shaderAsset->FinalizeAfterLoad())
{
AZ_Error("ShaderAssetHandler", false, "Shader asset failed to finalize.");
return Data::AssetHandler::LoadResult::Error;
}
asset.GetAs<ShaderAsset>()->AssetInitBus::Handler::BusConnect();
return Data::AssetHandler::LoadResult::LoadComplete;
}
return Data::AssetHandler::LoadResult::Error;
}
///////////////////////////////////////////////////////////////////////
} // namespace RPI

@ -218,7 +218,7 @@ namespace AZ
return false;
}
if (!m_asset->FinalizeAfterLoad())
if (!m_asset->PostLoadInit())
{
ReportError("Failed to finalize the ShaderAsset.");
return false;

@ -10,6 +10,7 @@
#
set(FILES
Include/Atom/RPI.Public/AssetInitBus.h
Include/Atom/RPI.Public/Base.h
Include/Atom/RPI.Public/Culling.h
Include/Atom/RPI.Public/FeatureProcessor.h

Loading…
Cancel
Save