AtomOutputFrameCapture.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "AtomOutputFrameCapture.h"
  9. #include <Atom/RPI.Public/Pass/Specific/RenderToTexturePass.h>
  10. #include <Atom/RPI.Public/RenderPipeline.h>
  11. #include <Atom/RPI.Public/RPISystemInterface.h>
  12. #include <Atom/RPI.Public/Scene.h>
  13. #include <Atom/RPI.Public/View.h>
  14. #include <Atom/RPI.Reflect/System/RenderPipelineDescriptor.h>
  15. #include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
  16. #include <AzCore/Component/TransformBus.h>
  17. #include <AzCore/Math/MatrixUtils.h>
  18. #include <AzCore/Name/Name.h>
  19. #include <AzFramework/Entity/GameEntityContextBus.h>
  20. #include <AzFramework/Scene/Scene.h>
  21. #include <AzFramework/Scene/SceneSystemInterface.h>
  22. namespace TrackView
  23. {
  24. void AtomOutputFrameCapture::CreatePipeline(
  25. AZ::RPI::Scene& scene, const AZStd::string& pipelineName, const uint32_t width, const uint32_t height)
  26. {
  27. AZ::RPI::RenderPipelineDescriptor pipelineDesc;
  28. pipelineDesc.m_mainViewTagName = "MainCamera"; // must be "MainCamera"
  29. pipelineDesc.m_name = pipelineName;
  30. pipelineDesc.m_rootPassTemplate = "MainPipelineRenderToTexture";
  31. pipelineDesc.m_renderSettings.m_multisampleState = AZ::RPI::RPISystemInterface::Get()->GetApplicationMultisampleState();
  32. m_renderPipeline = AZ::RPI::RenderPipeline::CreateRenderPipeline(pipelineDesc);
  33. if (auto renderToTexturePass = azrtti_cast<AZ::RPI::RenderToTexturePass*>(m_renderPipeline->GetRootPass().get()))
  34. {
  35. renderToTexturePass->ResizeOutput(width, height);
  36. }
  37. scene.AddRenderPipeline(m_renderPipeline);
  38. // rendering pipeline has a tree structure
  39. m_passHierarchy.push_back(pipelineName);
  40. m_passHierarchy.push_back("CopyToSwapChain");
  41. // retrieve View from the camera that's animating
  42. AZ::Name viewName = AZ::Name("MainCamera");
  43. m_view = AZ::RPI::View::CreateView(viewName, AZ::RPI::View::UsageCamera);
  44. m_renderPipeline->SetDefaultView(m_view);
  45. m_targetView = scene.GetDefaultRenderPipeline()->GetDefaultView();
  46. if (auto* fp = scene.GetFeatureProcessor<AZ::Render::PostProcessFeatureProcessorInterface>())
  47. {
  48. // This will be set again to mimic the active camera in UpdateView
  49. fp->SetViewAlias(m_view, m_targetView);
  50. }
  51. m_pipelineCreated = true;
  52. }
  53. void AtomOutputFrameCapture::DestroyPipeline(AZ::RPI::Scene& scene)
  54. {
  55. if (!m_pipelineCreated)
  56. {
  57. return;
  58. }
  59. if (auto* fp = scene.GetFeatureProcessor<AZ::Render::PostProcessFeatureProcessorInterface>())
  60. {
  61. // Remove view alias introduced in CreatePipeline and UpdateView
  62. fp->RemoveViewAlias(m_view);
  63. }
  64. scene.RemoveRenderPipeline(m_renderPipeline->GetId());
  65. m_passHierarchy.clear();
  66. m_renderPipeline.reset();
  67. m_view.reset();
  68. m_targetView.reset();
  69. m_pipelineCreated = false;
  70. }
  71. void AtomOutputFrameCapture::UpdateView(const AZ::Matrix3x4& cameraTransform, const AZ::Matrix4x4& cameraProjection, const AZ::RPI::ViewPtr targetView)
  72. {
  73. if (targetView && targetView != m_targetView)
  74. {
  75. if (AZ::RPI::Scene* scene = SceneFromGameEntityContext())
  76. {
  77. if (auto* fp = scene->GetFeatureProcessor<AZ::Render::PostProcessFeatureProcessorInterface>())
  78. {
  79. fp->SetViewAlias(m_view, targetView);
  80. m_targetView = targetView;
  81. }
  82. }
  83. }
  84. m_view->SetCameraTransform(cameraTransform);
  85. m_view->SetViewToClipMatrix(cameraProjection);
  86. }
  87. bool AtomOutputFrameCapture::BeginCapture(
  88. const AZ::RPI::AttachmentReadback::CallbackFunction& attachmentReadbackCallback, CaptureFinishedCallback captureFinishedCallback)
  89. {
  90. m_captureFinishedCallback = AZStd::move(captureFinishedCallback);
  91. // note: "Output" (slot name) maps to MainPipeline.pass CopyToSwapChain
  92. AZ::Render::FrameCaptureOutcome captureOutcome;
  93. AZ::Render::FrameCaptureRequestBus::BroadcastResult(
  94. captureOutcome,
  95. &AZ::Render::FrameCaptureRequestBus::Events::CapturePassAttachmentWithCallback,
  96. attachmentReadbackCallback,
  97. m_passHierarchy,
  98. AZStd::string("Output"),
  99. AZ::RPI::PassAttachmentReadbackOption::Output);
  100. if (captureOutcome.IsSuccess())
  101. {
  102. AZ::Render::FrameCaptureNotificationBus::Handler::BusConnect(captureOutcome.GetValue());
  103. return true;
  104. }
  105. AZ_Error("AtomOutputFrameCapture", captureOutcome.IsSuccess(),
  106. "Frame capture initialization failed. %s", captureOutcome.GetError().m_errorMessage.c_str());
  107. return false;
  108. }
  109. void AtomOutputFrameCapture::OnFrameCaptureFinished(
  110. [[maybe_unused]] AZ::Render::FrameCaptureResult result, [[maybe_unused]] const AZStd::string& info)
  111. {
  112. AZ::Render::FrameCaptureNotificationBus::Handler::BusDisconnect();
  113. m_captureFinishedCallback();
  114. }
  115. AZ::Matrix3x4 TransformFromEntityId(const AZ::EntityId entityId)
  116. {
  117. AZ::Transform cameraTransform = AZ::Transform::CreateIdentity();
  118. AZ::TransformBus::EventResult(cameraTransform, entityId, &AZ::TransformBus::Events::GetWorldTM);
  119. return AZ::Matrix3x4::CreateFromTransform(cameraTransform);
  120. }
  121. AZ::Matrix4x4 ProjectionFromCameraEntityId(const AZ::EntityId entityId, const float outputWidth, const float outputHeight)
  122. {
  123. float nearDist = 0.0f;
  124. Camera::CameraRequestBus::EventResult(nearDist, entityId, &Camera::CameraRequestBus::Events::GetNearClipDistance);
  125. float farDist = 0.0f;
  126. Camera::CameraRequestBus::EventResult(farDist, entityId, &Camera::CameraRequestBus::Events::GetFarClipDistance);
  127. float fovRad = 0.0f;
  128. Camera::CameraRequestBus::EventResult(fovRad, entityId, &Camera::CameraRequestBus::Events::GetFovRadians);
  129. const float aspectRatio = outputWidth / outputHeight;
  130. AZ::Matrix4x4 viewToClipMatrix;
  131. AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix, fovRad, aspectRatio, nearDist, farDist, /*reverseDepth=*/true);
  132. return viewToClipMatrix;
  133. }
  134. AZ::RPI::Scene* SceneFromGameEntityContext()
  135. {
  136. AzFramework::EntityContextId entityContextId;
  137. AzFramework::GameEntityContextRequestBus::BroadcastResult(
  138. entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId);
  139. return AZ::RPI::Scene::GetSceneForEntityContextId(entityContextId);
  140. }
  141. } // namespace TrackView