SwapChainPass.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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 <Atom/RHI/FrameScheduler.h>
  9. #include <Atom/RPI.Public/RenderPipeline.h>
  10. #include <Atom/RPI.Public/Pass/AttachmentReadback.h>
  11. #include <Atom/RPI.Public/Pass/PassFilter.h>
  12. #include <Atom/RPI.Public/Pass/PassSystemInterface.h>
  13. #include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
  14. #include <Atom/RHI/RHISystemInterface.h>
  15. #include <Atom/RPI.Public/RPISystemInterface.h>
  16. namespace AZ
  17. {
  18. namespace RPI
  19. {
  20. SwapChainPass::SwapChainPass(const PassDescriptor& descriptor, const WindowContext* windowContext, const ViewType viewType)
  21. : ParentPass(descriptor)
  22. , m_windowContext(windowContext)
  23. , m_viewType(viewType)
  24. {
  25. AzFramework::WindowNotificationBus::Handler::BusConnect(m_windowContext->GetWindowHandle());
  26. }
  27. Ptr<ParentPass> SwapChainPass::Recreate() const
  28. {
  29. PassDescriptor desc = GetPassDescriptor();
  30. Ptr<ParentPass> pass = aznew SwapChainPass(desc, m_windowContext, m_viewType);
  31. return pass;
  32. }
  33. SwapChainPass::~SwapChainPass()
  34. {
  35. AzFramework::WindowNotificationBus::Handler::BusDisconnect();
  36. }
  37. RHI::Format SwapChainPass::GetSwapChainFormat() const
  38. {
  39. if (m_swapChainAttachment)
  40. {
  41. return m_swapChainAttachment->m_descriptor.m_image.m_format;
  42. }
  43. return RHI::Format::Unknown;
  44. }
  45. const RHI::Scissor& SwapChainPass::GetScissor() const
  46. {
  47. return m_scissorState;
  48. }
  49. const RHI::Viewport& SwapChainPass::GetViewport() const
  50. {
  51. return m_viewportState;
  52. }
  53. void SwapChainPass::SetupSwapChainAttachment()
  54. {
  55. // Find "PipelineOutput" slot from the render pipeline's root pass
  56. PassAttachmentBinding* pipelineOutput = FindAttachmentBinding(Name("PipelineOutput"));
  57. AZ_Assert(pipelineOutput != nullptr &&
  58. pipelineOutput->m_slotType == PassSlotType::InputOutput,
  59. "PassTemplate used to create SwapChainPass must have an InputOutput called PipelineOutput");
  60. AzFramework::WindowSize renderSize;
  61. AzFramework::WindowRequestBus::EventResult(
  62. renderSize, m_windowContext->GetWindowHandle(), &AzFramework::WindowRequestBus::Events::GetRenderResolution);
  63. RHI::SwapChainDimensions swapChainDimensions = m_windowContext->GetSwapChain(m_viewType)->GetDescriptor().m_dimensions;
  64. // If the swapchain doesn't support scaling and render resolution is different than swapchain size
  65. m_needResize = m_windowContext->GetSwapChainScalingMode() == RHI::Scaling::None &&
  66. (swapChainDimensions.m_imageWidth != renderSize.m_width || swapChainDimensions.m_imageHeight != renderSize.m_height);
  67. // Note: we can't add m_swapChainAttachment to m_ownedAttachments then it would be imported to frame graph's attachment database as a regular image.
  68. m_swapChainAttachment = aznew PassAttachment();
  69. m_swapChainAttachment->m_name = "SwapChainOutput";
  70. m_swapChainAttachment->m_path = m_windowContext->GetSwapChainAttachmentId(m_viewType);
  71. RHI::ImageDescriptor swapChainImageDesc;
  72. swapChainImageDesc.m_bindFlags = RHI::ImageBindFlags::Color | RHI::ImageBindFlags::ShaderRead | RHI::ImageBindFlags::CopyWrite;
  73. swapChainImageDesc.m_size.m_width = swapChainDimensions.m_imageWidth;
  74. swapChainImageDesc.m_size.m_height = swapChainDimensions.m_imageHeight;
  75. swapChainImageDesc.m_format = swapChainDimensions.m_imageFormat;
  76. m_swapChainAttachment->m_descriptor = swapChainImageDesc;
  77. if (m_needResize)
  78. {
  79. // Create a new binding for swapchain output
  80. // It's used to connect to child pass's Output slot
  81. PassAttachmentBinding outputBinding;
  82. outputBinding.m_name = "SwapChainOutput";
  83. outputBinding.m_slotType = PassSlotType::Output;
  84. outputBinding.m_scopeAttachmentUsage = RHI::ScopeAttachmentUsage::RenderTarget;
  85. outputBinding.SetAttachment(m_swapChainAttachment);
  86. AddAttachmentBinding(outputBinding);
  87. // create an intermediate attachment which has window render resolution
  88. RHI::ImageDescriptor outputImageDesc;
  89. outputImageDesc = swapChainImageDesc;
  90. outputImageDesc.m_size.m_width = renderSize.m_width;
  91. outputImageDesc.m_size.m_height = renderSize.m_height;
  92. m_pipelinOutputAttachment = aznew PassAttachment();
  93. m_pipelinOutputAttachment->m_lifetime = RHI::AttachmentLifetimeType::Transient;
  94. m_pipelinOutputAttachment->m_descriptor = outputImageDesc;
  95. m_pipelinOutputAttachment->m_name = "PipelineOutput";
  96. m_pipelinOutputAttachment->ComputePathName(GetPathName());
  97. // Note: do not add m_pipelinOutputAttachment to m_ownedAttachments so it won't be imported to frame graph's attachment database.
  98. // This is because this attachment is only used for pass reference, but not used for RHI frame graph
  99. // use the intermediate attachment as pipeline's output
  100. pipelineOutput->SetAttachment(m_pipelinOutputAttachment);
  101. }
  102. else
  103. {
  104. // use the intermediate attachment as pipeline's output
  105. pipelineOutput->SetAttachment(m_swapChainAttachment);
  106. }
  107. }
  108. void SwapChainPass::BuildInternal()
  109. {
  110. if (m_windowContext->GetSwapChainsSize() == 0 || m_windowContext->GetSwapChain(m_viewType) == nullptr)
  111. {
  112. return;
  113. }
  114. m_scissorState = m_windowContext->GetScissor(m_viewType);
  115. m_viewportState = m_windowContext->GetViewport(m_viewType);
  116. SetupSwapChainAttachment();
  117. ParentPass::BuildInternal();
  118. // use the SwapchainOutput as the output of CopyToSwapChain pass to support swapchain scaling
  119. if (m_needResize)
  120. {
  121. RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(Name("CopyToSwapChain"), GetRenderPipeline());
  122. RPI::Ptr<RPI::Pass> copyPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
  123. if (copyPass)
  124. {
  125. auto outputBinding = copyPass->FindAttachmentBinding(Name("Output"));
  126. outputBinding->SetAttachment(m_swapChainAttachment);
  127. // set connected binding to nullptr so it won't use the output attachment specified in the pipeline's pass template
  128. outputBinding->m_connectedBinding = nullptr;
  129. }
  130. else
  131. {
  132. AZ_Error("SwapChainPass", false, "Render pipeline requires 'CopyToSwapChain' pass with 'Output' slot to support swapchain resize");
  133. }
  134. }
  135. if (m_pipeline)
  136. {
  137. m_pipeline->UpdateViewportScissor();
  138. }
  139. }
  140. void SwapChainPass::FrameBeginInternal(FramePrepareParams params)
  141. {
  142. if (m_windowContext->GetSwapChainsSize() == 0 || m_windowContext->GetSwapChain(m_viewType) == nullptr ||
  143. m_windowContext->GetSwapChain(m_viewType)->GetImageCount() == 0)
  144. {
  145. return;
  146. }
  147. RHI::FrameGraphAttachmentInterface attachmentDatabase = params.m_frameGraphBuilder->GetAttachmentDatabase();
  148. AZ::RPI::RPISystemInterface* rpiSystem = AZ::RPI::RPISystemInterface::Get();
  149. if (rpiSystem->GetXRSystem())
  150. {
  151. switch (m_viewType)
  152. {
  153. case ViewType::XrLeft:
  154. {
  155. rpiSystem->GetXRSystem()->AcquireSwapChainImage(0);
  156. break;
  157. }
  158. case ViewType::XrRight:
  159. {
  160. rpiSystem->GetXRSystem()->AcquireSwapChainImage(1);
  161. break;
  162. }
  163. default:
  164. {
  165. // No need to do anything for non-xr swapchain
  166. break;
  167. }
  168. }
  169. }
  170. // Import the SwapChain
  171. attachmentDatabase.ImportSwapChain(
  172. m_windowContext->GetSwapChainAttachmentId(m_viewType), m_windowContext->GetSwapChain(m_viewType));
  173. ParentPass::FrameBeginInternal(params);
  174. }
  175. void SwapChainPass::OnResolutionChanged([[maybe_unused]] uint32_t width, [[maybe_unused]] uint32_t height)
  176. {
  177. QueueForBuildAndInitialization();
  178. }
  179. void SwapChainPass::OnWindowResized([[maybe_unused]] uint32_t width, [[maybe_unused]] uint32_t height)
  180. {
  181. QueueForBuildAndInitialization();
  182. }
  183. void SwapChainPass::ReadbackSwapChain(AZStd::shared_ptr<AttachmentReadback> readback)
  184. {
  185. if (m_swapChainAttachment)
  186. {
  187. m_readbackOption = PassAttachmentReadbackOption::Output;
  188. m_attachmentReadback = readback;
  189. AZStd::string readbackName = AZStd::string::format("%s_%s", m_swapChainAttachment->GetAttachmentId().GetCStr(), GetName().GetCStr());
  190. m_attachmentReadback->ReadPassAttachment(m_swapChainAttachment.get(), AZ::Name(readbackName));
  191. }
  192. }
  193. AzFramework::NativeWindowHandle SwapChainPass::GetWindowHandle() const
  194. {
  195. return m_windowContext->GetWindowHandle();
  196. }
  197. } // namespace RPI
  198. } // namespace AZ