You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
424 lines
14 KiB
C++
424 lines
14 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include <Atom/RPI.Public/RPISystem.h>
|
|
|
|
#include <Atom/RPI.Reflect/Asset/AssetReference.h>
|
|
#include <Atom/RPI.Reflect/Asset/AssetHandler.h>
|
|
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
|
#include <Atom/RPI.Reflect/ResourcePoolAsset.h>
|
|
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
|
#include <Atom/RPI.Reflect/System/AnyAsset.h>
|
|
#include <Atom/RPI.Reflect/System/AssetAliases.h>
|
|
#include <Atom/RPI.Reflect/System/PipelineRenderSettings.h>
|
|
#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>
|
|
#include <Atom/RPI.Public/RenderPipeline.h>
|
|
#include <Atom/RPI.Public/View.h>
|
|
#include <Atom/RPI.Public/Pass/PassFactory.h>
|
|
|
|
#include <Atom/RHI/Factory.h>
|
|
#include <Atom/RHI/Device.h>
|
|
#include <Atom/RHI.Reflect/PlatformLimitsDescriptor.h>
|
|
|
|
#include <AzCore/Debug/EventTrace.h>
|
|
#include <AzCore/Interface/Interface.h>
|
|
|
|
#include <AzFramework/Asset/AssetSystemBus.h>
|
|
|
|
AZ_DEFINE_BUDGET(AzRender);
|
|
AZ_DEFINE_BUDGET(RPI);
|
|
|
|
// This will cause the RPI System to print out global state (like the current pass hierarchy) when an assert is hit
|
|
// This is useful for rendering engineers debugging a crash in the RPI/RHI layers
|
|
#define AZ_RPI_PRINT_GLOBAL_STATE_ON_ASSERT 0
|
|
|
|
namespace AZ
|
|
{
|
|
namespace RPI
|
|
{
|
|
RPISystemInterface* RPISystemInterface::Get()
|
|
{
|
|
return Interface<RPISystemInterface>::Get();
|
|
}
|
|
|
|
void RPISystem::Reflect(ReflectContext* context)
|
|
{
|
|
AssetReference::Reflect(context);
|
|
|
|
BufferSystem::Reflect(context);
|
|
ImageSystem::Reflect(context);
|
|
MaterialSystem::Reflect(context);
|
|
ModelSystem::Reflect(context);
|
|
ShaderSystem::Reflect(context);
|
|
PassSystem::Reflect(context);
|
|
|
|
ResourcePoolAsset::Reflect(context);
|
|
|
|
SceneDescriptor::Reflect(context);
|
|
PipelineRenderSettings::Reflect(context);
|
|
RenderPipelineDescriptor::Reflect(context);
|
|
AssetAliases::Reflect(context);
|
|
|
|
RPISystemDescriptor::Reflect(context);
|
|
GpuQuerySystemDescriptor::Reflect(context);
|
|
ShaderMetricsSystem::Reflect(context);
|
|
|
|
PipelineStatisticsResult::Reflect(context);
|
|
}
|
|
|
|
void RPISystem::Initialize(const RPISystemDescriptor& rpiSystemDescriptor)
|
|
{
|
|
m_rhiSystem.InitDevice();
|
|
|
|
// Gather asset handlers from sub-systems.
|
|
ImageSystem::GetAssetHandlers(m_assetHandlers);
|
|
BufferSystem::GetAssetHandlers(m_assetHandlers);
|
|
MaterialSystem::GetAssetHandlers(m_assetHandlers);
|
|
ModelSystem::GetAssetHandlers(m_assetHandlers);
|
|
PassSystem::GetAssetHandlers(m_assetHandlers);
|
|
ShaderSystem::GetAssetHandlers(m_assetHandlers);
|
|
m_assetHandlers.emplace_back(MakeAssetHandler<ResourcePoolAssetHandler>());
|
|
m_assetHandlers.emplace_back(MakeAssetHandler<AnyAssetHandler>());
|
|
|
|
m_materialSystem.Init();
|
|
m_modelSystem.Init();
|
|
m_shaderSystem.Init();
|
|
m_shaderMetricsSystem.Init();
|
|
m_passSystem.Init();
|
|
m_featureProcessorFactory.Init();
|
|
m_querySystem.Init(m_descriptor.m_gpuQuerySystemDescriptor);
|
|
|
|
Interface<RPISystemInterface>::Register(this);
|
|
|
|
SystemTickBus::Handler::BusConnect();
|
|
|
|
#if AZ_RPI_PRINT_GLOBAL_STATE_ON_ASSERT
|
|
Debug::TraceMessageBus::Handler::BusConnect();
|
|
#endif
|
|
m_descriptor = rpiSystemDescriptor;
|
|
}
|
|
|
|
void RPISystem::Shutdown()
|
|
{
|
|
m_viewportContextManager.Shutdown();
|
|
m_viewSrgLayout = nullptr;
|
|
m_sceneSrgLayout = nullptr;
|
|
m_commonShaderAssetForSrgs.Reset();
|
|
|
|
#if AZ_RPI_PRINT_GLOBAL_STATE_ON_ASSERT
|
|
Debug::TraceMessageBus::Handler::BusDisconnect();
|
|
#endif
|
|
SystemTickBus::Handler::BusDisconnect();
|
|
|
|
Interface<RPISystemInterface>::Unregister(this);
|
|
|
|
m_featureProcessorFactory.Shutdown();
|
|
m_passSystem.Shutdown();
|
|
m_dynamicDraw.Shutdown();
|
|
m_bufferSystem.Shutdown();
|
|
m_materialSystem.Shutdown();
|
|
m_modelSystem.Shutdown();
|
|
m_shaderSystem.Shutdown();
|
|
m_shaderMetricsSystem.Shutdown();
|
|
m_imageSystem.Shutdown();
|
|
m_querySystem.Shutdown();
|
|
m_rhiSystem.Shutdown();
|
|
|
|
/**
|
|
* [LY-86745] We need to pump the asset manager queue here, because
|
|
* it uses AZStd::function<> with vtable pointers embedded from this DLL.
|
|
* If we allow the DLL to shutdown with queued events, they will be pumped
|
|
* later by the asset manager component, which will then reference garbage
|
|
* vtable pointers.
|
|
*
|
|
* Note that it's necessary to pump before *and* after we clear the handlers,
|
|
* since the handler clear could result in more events dispatched.
|
|
*/
|
|
Data::AssetManager::Instance().DispatchEvents();
|
|
m_assetHandlers.clear();
|
|
Data::AssetManager::Instance().DispatchEvents();
|
|
}
|
|
|
|
void RPISystem::RegisterScene(ScenePtr scene)
|
|
{
|
|
for (auto& sceneItem : m_scenes)
|
|
{
|
|
if (sceneItem == scene)
|
|
{
|
|
AZ_Assert(false, "Scene was already registered");
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_scenes.push_back(scene);
|
|
}
|
|
|
|
void RPISystem::UnregisterScene(ScenePtr scene)
|
|
{
|
|
for (auto itr = m_scenes.begin(); itr != m_scenes.end(); itr++)
|
|
{
|
|
if (*itr == scene)
|
|
{
|
|
m_scenes.erase(itr);
|
|
return;
|
|
}
|
|
}
|
|
AZ_Assert(false, "Can't unregister scene which wasn't registered");
|
|
}
|
|
|
|
ScenePtr RPISystem::GetScene(const SceneId& sceneId) const
|
|
{
|
|
for (const auto& scene : m_scenes)
|
|
{
|
|
if (scene->GetId() == sceneId)
|
|
{
|
|
return scene;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
ScenePtr RPISystem::GetDefaultScene() const
|
|
{
|
|
if (m_scenes.size() > 0)
|
|
{
|
|
return m_scenes[0];
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
RenderPipelinePtr RPISystem::GetRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle)
|
|
{
|
|
RenderPipelinePtr renderPipeline;
|
|
for (auto& scene : m_scenes)
|
|
{
|
|
renderPipeline = scene->FindRenderPipelineForWindow(windowHandle);
|
|
if (renderPipeline)
|
|
{
|
|
return renderPipeline;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Data::Asset<ShaderAsset> RPISystem::GetCommonShaderAssetForSrgs() const
|
|
{
|
|
AZ_Assert(m_systemAssetsInitialized, "InitializeSystemAssets() should be called once when asset catalog loaded'");
|
|
return m_commonShaderAssetForSrgs;
|
|
}
|
|
|
|
RHI::Ptr<RHI::ShaderResourceGroupLayout> RPISystem::GetSceneSrgLayout() const
|
|
{
|
|
AZ_Assert(m_systemAssetsInitialized, "InitializeSystemAssets() should be called once when asset catalog loaded'");
|
|
return m_sceneSrgLayout;
|
|
}
|
|
|
|
RHI::Ptr<RHI::ShaderResourceGroupLayout> RPISystem::GetViewSrgLayout() const
|
|
{
|
|
AZ_Assert(m_systemAssetsInitialized, "InitializeSystemAssets() should be called once when asset catalog loaded'");
|
|
return m_viewSrgLayout;
|
|
}
|
|
|
|
void RPISystem::OnSystemTick()
|
|
{
|
|
AZ_ATOM_PROFILE_FUNCTION("RPI", "RPISystem: OnSystemTick");
|
|
|
|
// Image system update is using system tick but not game tick so it can stream images in background even game is pausing
|
|
m_imageSystem.Update();
|
|
}
|
|
|
|
void RPISystem::SimulationTick()
|
|
{
|
|
if (!m_systemAssetsInitialized)
|
|
{
|
|
return;
|
|
}
|
|
AZ_ATOM_PROFILE_FUNCTION("RPI", "RPISystem: SimulationTick");
|
|
|
|
AssetInitBus::Broadcast(&AssetInitBus::Events::PostLoadInit);
|
|
|
|
// Update tick time info
|
|
FillTickTimeInfo();
|
|
|
|
for (auto& scene : m_scenes)
|
|
{
|
|
scene->Simulate(m_tickTime, m_simulationJobPolicy);
|
|
}
|
|
}
|
|
|
|
void RPISystem::FillTickTimeInfo()
|
|
{
|
|
AZ::TickRequestBus::BroadcastResult(m_tickTime.m_gameDeltaTime, &AZ::TickRequestBus::Events::GetTickDeltaTime);
|
|
ScriptTimePoint currentTime;
|
|
AZ::TickRequestBus::BroadcastResult(currentTime, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick);
|
|
m_tickTime.m_currentGameTime = static_cast<float>(currentTime.GetMilliseconds());
|
|
}
|
|
|
|
void RPISystem::RenderTick()
|
|
{
|
|
if (!m_systemAssetsInitialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AZ_PROFILE_FUNCTION(RPI);
|
|
AZ_ATOM_PROFILE_FUNCTION("RPI", "RPISystem: RenderTick");
|
|
|
|
// Query system update is to increment the frame count
|
|
m_querySystem.Update();
|
|
|
|
// Collect draw packets for each scene and prepare RPI system SRGs
|
|
// [GFX TODO] We may parallel scenes' prepare render.
|
|
for (auto& scenePtr : m_scenes)
|
|
{
|
|
scenePtr->PrepareRender(m_tickTime, m_prepareRenderJobPolicy);
|
|
}
|
|
|
|
m_rhiSystem.FrameUpdate(
|
|
[this](RHI::FrameGraphBuilder& frameGraphBuilder)
|
|
{
|
|
// Pass system's frame update, which includes the logic of adding scope producers, has to be added here since the
|
|
// scope producers only can be added to the frame when frame started which cleans up previous scope producers.
|
|
m_passSystem.FrameUpdate(frameGraphBuilder);
|
|
|
|
// Update Scene and View Srgs
|
|
for (auto& scenePtr : m_scenes)
|
|
{
|
|
scenePtr->UpdateSrgs();
|
|
}
|
|
});
|
|
|
|
{
|
|
AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "RPISystem: FrameEnd");
|
|
m_dynamicDraw.FrameEnd();
|
|
m_passSystem.FrameEnd();
|
|
|
|
for (auto& scenePtr : m_scenes)
|
|
{
|
|
scenePtr->OnFrameEnd();
|
|
}
|
|
}
|
|
|
|
m_renderTick++;
|
|
}
|
|
|
|
void RPISystem::SetSimulationJobPolicy(RHI::JobPolicy jobPolicy)
|
|
{
|
|
m_simulationJobPolicy = jobPolicy;
|
|
}
|
|
|
|
RHI::JobPolicy RPISystem::GetSimulationJobPolicy() const
|
|
{
|
|
return m_simulationJobPolicy;
|
|
}
|
|
|
|
void RPISystem::SetRenderPrepareJobPolicy(RHI::JobPolicy jobPolicy)
|
|
{
|
|
m_prepareRenderJobPolicy = jobPolicy;
|
|
}
|
|
|
|
RHI::JobPolicy RPISystem::GetRenderPrepareJobPolicy() const
|
|
{
|
|
return m_prepareRenderJobPolicy;
|
|
}
|
|
|
|
const RPISystemDescriptor& RPISystem::GetDescriptor() const
|
|
{
|
|
return m_descriptor;
|
|
}
|
|
|
|
Name RPISystem::GetRenderApiName() const
|
|
{
|
|
return RHI::Factory::Get().GetName();
|
|
}
|
|
|
|
void RPISystem::InitializeSystemAssets()
|
|
{
|
|
if (m_systemAssetsInitialized)
|
|
{
|
|
AZ_Warning("RPISystem", false , "InitializeSystemAssets should only be called once'");
|
|
return;
|
|
}
|
|
|
|
m_commonShaderAssetForSrgs = AssetUtils::LoadCriticalAsset<ShaderAsset>( m_descriptor.m_commonSrgsShaderAssetPath.c_str());
|
|
if (!m_commonShaderAssetForSrgs.IsReady())
|
|
{
|
|
return;
|
|
}
|
|
m_sceneSrgLayout = m_commonShaderAssetForSrgs->FindShaderResourceGroupLayout(SrgBindingSlot::Scene);
|
|
if (!m_sceneSrgLayout)
|
|
{
|
|
AZ_Error("RPISystem", false, "Failed to find SceneSrg by slot=<%u> from shader asset at path <%s>", SrgBindingSlot::Scene,
|
|
m_descriptor.m_commonSrgsShaderAssetPath.c_str());
|
|
return;
|
|
}
|
|
m_viewSrgLayout = m_commonShaderAssetForSrgs->FindShaderResourceGroupLayout(SrgBindingSlot::View);
|
|
if (!m_viewSrgLayout)
|
|
{
|
|
AZ_Error("RPISystem", false, "Failed to find ViewSrg by slot=<%u> from shader asset at path <%s>", SrgBindingSlot::View,
|
|
m_descriptor.m_commonSrgsShaderAssetPath.c_str());
|
|
return;
|
|
}
|
|
|
|
m_rhiSystem.Init();
|
|
m_imageSystem.Init(m_descriptor.m_imageSystemDescriptor);
|
|
m_bufferSystem.Init();
|
|
m_dynamicDraw.Init(m_descriptor.m_dynamicDrawSystemDescriptor);
|
|
|
|
m_passSystem.InitPassTemplates();
|
|
|
|
m_systemAssetsInitialized = true;
|
|
}
|
|
|
|
bool RPISystem::IsInitialized() const
|
|
{
|
|
return m_systemAssetsInitialized;
|
|
}
|
|
|
|
void RPISystem::InitializeSystemAssetsForTests()
|
|
{
|
|
if (m_systemAssetsInitialized)
|
|
{
|
|
AZ_Warning("RPISystem", false, "InitializeSystemAssets should only be called once'");
|
|
return;
|
|
}
|
|
|
|
//Init rhi/image/buffer systems to match InitializeSystemAssets
|
|
m_rhiSystem.Init();
|
|
m_imageSystem.Init(m_descriptor.m_imageSystemDescriptor);
|
|
m_bufferSystem.Init();
|
|
|
|
// Assets aren't actually available or needed for tests, but the m_systemAssetsInitialized flag still needs to be flipped.
|
|
m_systemAssetsInitialized = true;
|
|
return;
|
|
}
|
|
|
|
bool RPISystem::OnPreAssert([[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message)
|
|
{
|
|
#if AZ_RPI_PRINT_GLOBAL_STATE_ON_ASSERT
|
|
AZ_Printf("RPI System", "\n--- Assert hit! Dumping RPI state ---\n\n");
|
|
m_passSystem.DebugPrintPassHierarchy();
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
uint64_t RPISystem::GetCurrentTick() const
|
|
{
|
|
return m_renderTick;
|
|
}
|
|
|
|
} //namespace RPI
|
|
} //namespace AZ
|