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.
428 lines
20 KiB
C++
428 lines
20 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 <BootstrapSystemComponent.h>
|
|
|
|
#include <AzCore/Asset/AssetCommon.h>
|
|
#include <AzCore/Component/ComponentApplicationBus.h>
|
|
#include <AzCore/Component/Entity.h>
|
|
#include <AzCore/NativeUI/NativeUIRequests.h>
|
|
#include <AzCore/Serialization/SerializeContext.h>
|
|
#include <AzCore/std/smart_ptr/make_shared.h>
|
|
|
|
#include <AzFramework/API/ApplicationAPI.h>
|
|
#include <AzFramework/Components/TransformComponent.h>
|
|
#include <AzFramework/Entity/GameEntityContextBus.h>
|
|
#include <AzFramework/Asset/AssetSystemBus.h>
|
|
|
|
#include <ISystem.h>
|
|
|
|
#include <Atom/RHI/RHISystemInterface.h>
|
|
|
|
#include <Atom/RPI.Reflect/Image/AttachmentImageAsset.h>
|
|
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
|
|
|
|
#include <Atom/RPI.Public/Pass/Pass.h>
|
|
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
|
|
#include <Atom/RPI.Public/RenderPipeline.h>
|
|
#include <Atom/RPI.Public/ViewportContextBus.h>
|
|
#include <Atom/RPI.Public/RPISystemInterface.h>
|
|
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
|
|
|
#include <Atom/Bootstrap/DefaultWindowBus.h>
|
|
#include <Atom/Bootstrap/BootstrapNotificationBus.h>
|
|
|
|
#include <Atom/RPI.Reflect/System/AnyAsset.h>
|
|
#include <AzCore/Console/IConsole.h>
|
|
#include <BootstrapSystemComponent_Traits_Platform.h>
|
|
|
|
AZ_CVAR(AZ::CVarFixedString, r_default_pipeline_name, AZ_TRAIT_BOOTSTRAPSYSTEMCOMPONENT_PIPELINE_NAME, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Default Render pipeline name");
|
|
|
|
namespace AZ
|
|
{
|
|
namespace Render
|
|
{
|
|
namespace Bootstrap
|
|
{
|
|
void BootstrapSystemComponent::Reflect(ReflectContext* context)
|
|
{
|
|
if (SerializeContext* serialize = azrtti_cast<SerializeContext*>(context))
|
|
{
|
|
serialize->Class<BootstrapSystemComponent, Component>()
|
|
->Version(1)
|
|
;
|
|
|
|
if (EditContext* ec = serialize->GetEditContext())
|
|
{
|
|
ec->Class<BootstrapSystemComponent>("Atom RPI", "Atom Renderer")
|
|
->ClassElement(Edit::ClassElements::EditorData, "")
|
|
->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
|
|
->Attribute(Edit::Attributes::AutoExpand, true)
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BootstrapSystemComponent::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
|
|
{
|
|
provided.push_back(AZ_CRC("BootstrapSystemComponent", 0xb8f32711));
|
|
}
|
|
|
|
void BootstrapSystemComponent::GetRequiredServices(ComponentDescriptor::DependencyArrayType& required)
|
|
{
|
|
required.push_back(AZ_CRC("RPISystem", 0xf2add773));
|
|
required.push_back(AZ_CRC("SceneSystemComponentService", 0xd8975435));
|
|
}
|
|
|
|
void BootstrapSystemComponent::GetDependentServices(ComponentDescriptor::DependencyArrayType& dependent)
|
|
{
|
|
dependent.push_back(AZ_CRC("ImGuiSystemComponent", 0x2f08b9a7));
|
|
dependent.push_back(AZ_CRC("PrimitiveSystemComponent", 0xc860fa59));
|
|
dependent.push_back(AZ_CRC("MeshSystemComponent", 0x21e5bbb6));
|
|
dependent.push_back(AZ_CRC("CoreLightsService", 0x91932ef6));
|
|
dependent.push_back(AZ_CRC("DynamicDrawService", 0x023c1673));
|
|
dependent.push_back(AZ_CRC("CommonService", 0x6398eec4));
|
|
}
|
|
|
|
void BootstrapSystemComponent::GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible)
|
|
{
|
|
incompatible.push_back(AZ_CRC("BootstrapSystemComponent", 0xb8f32711));
|
|
}
|
|
|
|
BootstrapSystemComponent::BootstrapSystemComponent()
|
|
{
|
|
}
|
|
|
|
BootstrapSystemComponent::~BootstrapSystemComponent()
|
|
{
|
|
m_viewportContext.reset();
|
|
}
|
|
|
|
void BootstrapSystemComponent::Activate()
|
|
{
|
|
// Create a native window only if it's a launcher (or standalone)
|
|
// LY editor create its own window which we can get its handle through AzFramework::WindowSystemNotificationBus::Handler's OnWindowCreated() function
|
|
AZ::ApplicationTypeQuery appType;
|
|
ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType);
|
|
if (!appType.IsValid() || appType.IsGame())
|
|
{
|
|
// GFX TODO - investigate window creation being part of the GameApplication.
|
|
|
|
m_nativeWindow = AZStd::make_unique<AzFramework::NativeWindow>("O3DELauncher", AzFramework::WindowGeometry(0, 0, 1920, 1080));
|
|
AZ_Assert(m_nativeWindow, "Failed to create the game window\n");
|
|
|
|
m_nativeWindow->Activate();
|
|
|
|
m_windowHandle = m_nativeWindow->GetWindowHandle();
|
|
}
|
|
else
|
|
{
|
|
// Disable default scene creation for non-games projects
|
|
// This can be manually overridden via the DefaultWindowBus.
|
|
m_createDefaultScene = false;
|
|
}
|
|
|
|
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
|
|
TickBus::Handler::BusConnect();
|
|
|
|
// Listen for window system requests (e.g. requests for default window handle)
|
|
AzFramework::WindowSystemRequestBus::Handler::BusConnect();
|
|
|
|
// Listen for window system notifications (e.g. window being created by Editor)
|
|
AzFramework::WindowSystemNotificationBus::Handler::BusConnect();
|
|
|
|
Render::Bootstrap::DefaultWindowBus::Handler::BusConnect();
|
|
Render::Bootstrap::RequestBus::Handler::BusConnect();
|
|
}
|
|
|
|
void BootstrapSystemComponent::Deactivate()
|
|
{
|
|
Render::Bootstrap::RequestBus::Handler::BusDisconnect();
|
|
Render::Bootstrap::DefaultWindowBus::Handler::BusDisconnect();
|
|
|
|
AzFramework::WindowSystemRequestBus::Handler::BusDisconnect();
|
|
AzFramework::WindowSystemNotificationBus::Handler::BusDisconnect();
|
|
TickBus::Handler::BusDisconnect();
|
|
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
|
|
|
|
m_brdfTexture = nullptr;
|
|
RemoveRenderPipeline();
|
|
DestroyDefaultScene();
|
|
|
|
m_viewportContext.reset();
|
|
m_nativeWindow = nullptr;
|
|
m_windowHandle = nullptr;
|
|
}
|
|
|
|
void BootstrapSystemComponent::OnCatalogLoaded(const char* /*catalogFile*/)
|
|
{
|
|
if (m_isAssetCatalogLoaded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_isAssetCatalogLoaded = true;
|
|
|
|
RPI::RPISystemInterface::Get()->InitializeSystemAssets();
|
|
|
|
if (!RPI::RPISystemInterface::Get()->IsInitialized())
|
|
{
|
|
AZ::OSString msgBoxMessage;
|
|
msgBoxMessage.append("RPI System could not initialize correctly. Check log for detail.");
|
|
|
|
AZ::NativeUI::NativeUIRequestBus::Broadcast(
|
|
&AZ::NativeUI::NativeUIRequestBus::Events::DisplayOkDialog, "O3DE Fatal Error", msgBoxMessage.c_str(), false);
|
|
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::ExitMainLoop);
|
|
|
|
return;
|
|
}
|
|
|
|
// In the case of the game we want to call create and register the scene as a soon as we can
|
|
// because a level could be loaded in autoexec.cfg and that will assert if there is no scene registered
|
|
// to get the feature processors for the components. So we can't wait until the tick (whereas the Editor wants to wait)
|
|
|
|
if (m_createDefaultScene)
|
|
{
|
|
CreateDefaultScene();
|
|
}
|
|
|
|
if (m_windowHandle)
|
|
{
|
|
CreateWindowContext();
|
|
if (m_createDefaultScene)
|
|
{
|
|
CreateDefaultRenderPipeline();
|
|
}
|
|
}
|
|
}
|
|
|
|
void BootstrapSystemComponent::OnWindowCreated(AzFramework::NativeWindowHandle windowHandle)
|
|
{
|
|
// only handle the first window (default) created
|
|
if (m_windowHandle == nullptr)
|
|
{
|
|
m_windowHandle = windowHandle;
|
|
|
|
if (m_isAssetCatalogLoaded)
|
|
{
|
|
CreateWindowContext();
|
|
if (m_createDefaultScene)
|
|
{
|
|
CreateDefaultRenderPipeline();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void BootstrapSystemComponent::CreateWindowContext()
|
|
{
|
|
RHI::Device* device = RHI::RHISystemInterface::Get()->GetDevice();
|
|
RPI::ViewportContextRequestsInterface::CreationParameters params;
|
|
params.device = device;
|
|
params.windowHandle = m_windowHandle;
|
|
params.renderScene = m_defaultScene;
|
|
// Setting the default ViewportContextID to an arbitrary and otherwise invalid (negative) value to ensure its uniqueness
|
|
params.id = -10;
|
|
|
|
auto viewContextManager = AZ::Interface<RPI::ViewportContextRequestsInterface>::Get();
|
|
m_viewportContext = viewContextManager->CreateViewportContext(
|
|
viewContextManager->GetDefaultViewportContextName(), params);
|
|
|
|
DefaultWindowNotificationBus::Broadcast(&DefaultWindowNotificationBus::Events::DefaultWindowCreated);
|
|
|
|
// Listen to window notification so we can request exit application when window closes
|
|
AzFramework::WindowNotificationBus::Handler::BusConnect(GetDefaultWindowHandle());
|
|
}
|
|
|
|
AZ::RPI::ScenePtr BootstrapSystemComponent::GetOrCreateAtomSceneFromAzScene(AzFramework::Scene* scene)
|
|
{
|
|
// Get or create a weak pointer to our scene
|
|
// If it's valid, we're done, if not we need to create an Atom scene and update our scene map
|
|
auto& atomSceneHandle = m_azSceneToAtomSceneMap[scene];
|
|
if (!atomSceneHandle.expired())
|
|
{
|
|
return atomSceneHandle.lock();
|
|
}
|
|
|
|
// Create and register a scene with all available feature processors
|
|
RPI::SceneDescriptor sceneDesc;
|
|
AZ::RPI::ScenePtr atomScene = RPI::Scene::CreateScene(sceneDesc);
|
|
atomScene->EnableAllFeatureProcessors();
|
|
atomScene->Activate();
|
|
|
|
// Register scene to RPI system so it will be processed/rendered per tick
|
|
RPI::RPISystemInterface::Get()->RegisterScene(atomScene);
|
|
scene->SetSubsystem(atomScene);
|
|
|
|
atomSceneHandle = atomScene;
|
|
|
|
return atomScene;
|
|
}
|
|
|
|
void BootstrapSystemComponent::CreateDefaultScene()
|
|
{
|
|
// Bind atomScene to the GameEntityContext's AzFramework::Scene
|
|
m_defaultFrameworkScene = AzFramework::SceneSystemInterface::Get()->GetScene(AzFramework::Scene::MainSceneName);
|
|
// This should never happen unless scene creation has changed.
|
|
AZ_Assert(m_defaultFrameworkScene, "Error: Scenes missing during system component initialization");
|
|
m_sceneRemovalHandler = AzFramework::Scene::RemovalEvent::Handler(
|
|
[this](AzFramework::Scene&, AzFramework::Scene::RemovalEventType eventType)
|
|
{
|
|
if (eventType == AzFramework::Scene::RemovalEventType::Zombified)
|
|
{
|
|
m_defaultFrameworkScene.reset();
|
|
}
|
|
});
|
|
m_defaultFrameworkScene->ConnectToEvents(m_sceneRemovalHandler);
|
|
m_defaultScene = GetOrCreateAtomSceneFromAzScene(m_defaultFrameworkScene.get());
|
|
}
|
|
|
|
bool BootstrapSystemComponent::EnsureDefaultRenderPipelineInstalledForScene(AZ::RPI::ScenePtr scene, AZ::RPI::ViewportContextPtr viewportContext)
|
|
{
|
|
// Create a render pipeline from the specified asset for the window context and add the pipeline to the scene.
|
|
// When running with no Asset Processor (for example in release), CompileAssetSync will return AssetStatus_Unknown.
|
|
AzFramework::AssetSystem::AssetStatus status = AzFramework::AssetSystem::AssetStatus_Unknown;
|
|
const AZ::CVarFixedString pipelineName = static_cast<AZ::CVarFixedString>(r_default_pipeline_name);
|
|
AzFramework::AssetSystemRequestBus::BroadcastResult(status, &AzFramework::AssetSystemRequestBus::Events::CompileAssetSync, pipelineName.data());
|
|
|
|
AZ_Assert(status == AzFramework::AssetSystem::AssetStatus_Compiled || status == AzFramework::AssetSystem::AssetStatus_Unknown, "Could not compile the default render pipeline at '%s'", pipelineName.c_str());
|
|
|
|
Data::Asset<RPI::AnyAsset> pipelineAsset = RPI::AssetUtils::LoadAssetByProductPath<RPI::AnyAsset>(pipelineName.data(), RPI::AssetUtils::TraceLevel::Error);
|
|
RPI::RenderPipelineDescriptor renderPipelineDescriptor = *RPI::GetDataFromAnyAsset<RPI::RenderPipelineDescriptor>(pipelineAsset);
|
|
renderPipelineDescriptor.m_name = AZStd::string::format("%s_%i", renderPipelineDescriptor.m_name.c_str(), viewportContext->GetId());
|
|
|
|
if (!scene->GetRenderPipeline(AZ::Name(renderPipelineDescriptor.m_name)))
|
|
{
|
|
RPI::RenderPipelinePtr renderPipeline = RPI::RenderPipeline::CreateRenderPipelineForWindow(renderPipelineDescriptor, *viewportContext->GetWindowContext().get());
|
|
pipelineAsset.Release();
|
|
scene->AddRenderPipeline(renderPipeline);
|
|
}
|
|
|
|
// As part of our initialization we need to create the BRDF texture generation pipeline
|
|
AZ::RPI::RenderPipelineDescriptor pipelineDesc;
|
|
pipelineDesc.m_mainViewTagName = "MainCamera";
|
|
pipelineDesc.m_name = AZStd::string::format("BRDFTexturePipeline_%i", viewportContext->GetId());
|
|
pipelineDesc.m_rootPassTemplate = "BRDFTexturePipeline";
|
|
pipelineDesc.m_executeOnce = true;
|
|
|
|
// Save a reference to the generated BRDF texture so it doesn't get deleted if all the passes refering to it get deleted and it's ref count goes to zero
|
|
if (!m_brdfTexture)
|
|
{
|
|
const AZStd::shared_ptr<RPI::PassTemplate> brdfTextureTemplate = RPI::PassSystemInterface::Get()->GetPassTemplate(Name("BRDFTextureTemplate"));
|
|
Data::Asset<RPI::AttachmentImageAsset> brdfImageAsset = RPI::AssetUtils::LoadAssetById<RPI::AttachmentImageAsset>(
|
|
brdfTextureTemplate->m_imageAttachments[0].m_assetRef.m_assetId, RPI::AssetUtils::TraceLevel::Error);
|
|
if (brdfImageAsset.IsReady())
|
|
{
|
|
m_brdfTexture = RPI::AttachmentImage::FindOrCreate(brdfImageAsset);
|
|
}
|
|
}
|
|
|
|
if (!scene->GetRenderPipeline(AZ::Name(pipelineDesc.m_name)))
|
|
{
|
|
RPI::RenderPipelinePtr brdfTexturePipeline = AZ::RPI::RenderPipeline::CreateRenderPipeline(pipelineDesc);
|
|
scene->AddRenderPipeline(brdfTexturePipeline);
|
|
}
|
|
|
|
// Send notification when the scene and its pipeline are ready.
|
|
// Use the first created pipeline's scene as our default scene for now to allow
|
|
// consumers waiting on scene availability to initialize.
|
|
if (!m_defaultSceneReady)
|
|
{
|
|
m_defaultScene = scene;
|
|
Render::Bootstrap::NotificationBus::Broadcast(
|
|
&Render::Bootstrap::NotificationBus::Handler::OnBootstrapSceneReady, m_defaultScene.get());
|
|
m_defaultSceneReady = true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void BootstrapSystemComponent::CreateDefaultRenderPipeline()
|
|
{
|
|
EnsureDefaultRenderPipelineInstalledForScene(m_defaultScene, m_viewportContext);
|
|
|
|
const auto pipeline = m_defaultScene->FindRenderPipelineForWindow(m_viewportContext->GetWindowHandle());
|
|
AZ_Error("AtomBootstrap", pipeline, "No pipeline found for the default viewport window! Did the default render pipeline fail to compile?");
|
|
if (pipeline)
|
|
{
|
|
m_renderPipelineId = pipeline->GetId();
|
|
}
|
|
}
|
|
|
|
void BootstrapSystemComponent::DestroyDefaultScene()
|
|
{
|
|
if (m_defaultScene)
|
|
{
|
|
RPI::RPISystemInterface::Get()->UnregisterScene(m_defaultScene);
|
|
|
|
// Unbind m_defaultScene to the GameEntityContext's AzFramework::Scene
|
|
if (m_defaultFrameworkScene)
|
|
{
|
|
m_defaultFrameworkScene->UnsetSubsystem<RPI::Scene>();
|
|
}
|
|
|
|
m_defaultScene = nullptr;
|
|
m_defaultFrameworkScene = nullptr;
|
|
}
|
|
}
|
|
|
|
void BootstrapSystemComponent::RemoveRenderPipeline()
|
|
{
|
|
if (m_defaultScene && m_defaultScene->GetRenderPipeline(m_renderPipelineId))
|
|
{
|
|
m_defaultScene->RemoveRenderPipeline(m_renderPipelineId);
|
|
}
|
|
m_renderPipelineId = "";
|
|
}
|
|
|
|
void BootstrapSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time)
|
|
{
|
|
// Temp: When running in the launcher without the legacy renderer
|
|
// we need to call RenderTick on the viewport context each frame.
|
|
if (m_viewportContext)
|
|
{
|
|
AZ::ApplicationTypeQuery appType;
|
|
ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType);
|
|
if (appType.IsGame())
|
|
{
|
|
m_viewportContext->RenderTick();
|
|
}
|
|
}
|
|
}
|
|
|
|
int BootstrapSystemComponent::GetTickOrder()
|
|
{
|
|
return TICK_LAST;
|
|
}
|
|
|
|
void BootstrapSystemComponent::OnWindowClosed()
|
|
{
|
|
m_windowHandle = nullptr;
|
|
m_viewportContext.reset();
|
|
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::ExitMainLoop);
|
|
AzFramework::WindowNotificationBus::Handler::BusDisconnect();
|
|
}
|
|
|
|
AzFramework::NativeWindowHandle BootstrapSystemComponent::GetDefaultWindowHandle()
|
|
{
|
|
return m_windowHandle;
|
|
}
|
|
|
|
AZStd::shared_ptr<RPI::WindowContext> BootstrapSystemComponent::GetDefaultWindowContext()
|
|
{
|
|
return m_viewportContext ? m_viewportContext->GetWindowContext() : nullptr;
|
|
}
|
|
|
|
void BootstrapSystemComponent::SetCreateDefaultScene(bool create)
|
|
{
|
|
m_createDefaultScene = create;
|
|
}
|
|
} // namespace Bootstrap
|
|
} // namespace Render
|
|
} // namespace AZ
|