ReadbackExampleComponent.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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 <ReadbackExampleComponent.h>
  9. #include <Atom/RPI.Public/Scene.h>
  10. #include <Atom/RPI.Public/RenderPipeline.h>
  11. #include <Atom/RPI.Public/Pass/FullscreenTrianglePass.h>
  12. #include <Atom/RPI.Public/Image/ImageSystemInterface.h>
  13. #include <Atom/RPI.Public/Image/AttachmentImagePool.h>
  14. #include <Atom/RPI.Public/RPIUtils.h>
  15. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  16. #include <Atom/RPI.Reflect/Pass/FullscreenTrianglePassData.h>
  17. #include <Automation/ScriptableImGui.h>
  18. namespace AtomSampleViewer
  19. {
  20. static const char* s_readbackPipelineTemplate = "ReadbackPipelineTemplate";
  21. static const char* s_fillerPassTemplate = "ReadbackFillerPassTemplate";
  22. static const char* s_previewPassTemplate = "ReadbackPreviewPassTemplate";
  23. static const char* s_fillerShaderPath = "Shaders/Readback/Filler.azshader";
  24. static const char* s_previewShaderPath = "Shaders/Readback/Preview.azshader";
  25. static const char* s_readbackImageName = "ReadbackImage";
  26. static const char* s_previewImageName = "PreviewImage";
  27. void ReadbackExampleComponent::Reflect(AZ::ReflectContext* context)
  28. {
  29. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  30. {
  31. serializeContext->Class<ReadbackExampleComponent, AZ::Component>()->Version(0);
  32. }
  33. }
  34. ReadbackExampleComponent::ReadbackExampleComponent()
  35. {
  36. }
  37. void ReadbackExampleComponent::Activate()
  38. {
  39. AZ::TickBus::Handler::BusConnect();
  40. AZ::Render::Bootstrap::DefaultWindowNotificationBus::Handler::BusConnect();
  41. ActivatePipeline();
  42. CreatePasses();
  43. m_imguiSidebar.Activate();
  44. }
  45. void ReadbackExampleComponent::Deactivate()
  46. {
  47. m_imguiSidebar.Deactivate();
  48. DestroyPasses();
  49. DeactivatePipeline();
  50. AZ::Render::Bootstrap::DefaultWindowNotificationBus::Handler::BusDisconnect();
  51. AZ::TickBus::Handler::BusDisconnect();
  52. }
  53. void ReadbackExampleComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint scriptTime)
  54. {
  55. // Readback was completed, we need to update the preview image
  56. if (m_textureNeedsUpdate)
  57. {
  58. UploadReadbackResult();
  59. AZ_Error("ReadbackExample", m_resourceWidth == m_readbackStat.m_descriptor.m_size.m_width, "Incorrect resource width read back.");
  60. AZ_Error("ReadbackExample", m_resourceHeight == m_readbackStat.m_descriptor.m_size.m_height, "Incorrect resource height read back.");
  61. m_textureNeedsUpdate = false;
  62. }
  63. DrawSidebar();
  64. }
  65. void ReadbackExampleComponent::DefaultWindowCreated()
  66. {
  67. AZ::Render::Bootstrap::DefaultWindowBus::BroadcastResult(m_windowContext, &AZ::Render::Bootstrap::DefaultWindowBus::Events::GetDefaultWindowContext);
  68. }
  69. void ReadbackExampleComponent::CreatePipeline()
  70. {
  71. // Create the pipeline shell
  72. AZ::RPI::RenderPipelineDescriptor readbackPipelineDesc;
  73. readbackPipelineDesc.m_mainViewTagName = "MainCamera";
  74. readbackPipelineDesc.m_name = "ReadbackPipeline";
  75. readbackPipelineDesc.m_rootPassTemplate = s_readbackPipelineTemplate;
  76. m_readbackPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(readbackPipelineDesc, *m_windowContext);
  77. }
  78. void ReadbackExampleComponent::ActivatePipeline()
  79. {
  80. // Create the pipeline
  81. CreatePipeline();
  82. // Setup the pipeline
  83. m_originalPipeline = m_scene->GetDefaultRenderPipeline();
  84. m_scene->AddRenderPipeline(m_readbackPipeline);
  85. m_scene->RemoveRenderPipeline(m_originalPipeline->GetId());
  86. m_scene->SetDefaultRenderPipeline(m_readbackPipeline->GetId());
  87. // Create an ImGuiActiveContextScope to ensure the ImGui context on the new pipeline's ImGui pass is activated.
  88. m_imguiScope = AZ::Render::ImGuiActiveContextScope::FromPass({ m_readbackPipeline->GetId().GetCStr(), "ImGuiPass" });
  89. }
  90. void ReadbackExampleComponent::DeactivatePipeline()
  91. {
  92. m_imguiScope = {}; // restores previous ImGui context.
  93. m_scene->AddRenderPipeline(m_originalPipeline);
  94. m_scene->RemoveRenderPipeline(m_readbackPipeline->GetId());
  95. m_readbackPipeline = nullptr;
  96. }
  97. void ReadbackExampleComponent::CreatePasses()
  98. {
  99. DestroyPasses();
  100. CreateResources();
  101. CreateFillerPass();
  102. CreatePreviewPass();
  103. // Add the filler and preview passes
  104. AZ::RPI::Ptr<AZ::RPI::ParentPass> rootPass = m_readbackPipeline->GetRootPass();
  105. rootPass->InsertChild(m_fillerPass, AZ::RPI::ParentPass::ChildPassIndex(0));
  106. rootPass->InsertChild(m_previewPass, AZ::RPI::ParentPass::ChildPassIndex(1));
  107. }
  108. void ReadbackExampleComponent::DestroyPasses()
  109. {
  110. if (!m_fillerPass)
  111. {
  112. return;
  113. }
  114. m_fillerPass->QueueForRemoval();
  115. m_fillerPass = nullptr;
  116. m_previewPass->QueueForRemoval();
  117. m_previewPass = nullptr;
  118. }
  119. void ReadbackExampleComponent::PassesChanged()
  120. {
  121. DestroyPasses();
  122. CreatePasses();
  123. }
  124. void ReadbackExampleComponent::CreateFillerPass()
  125. {
  126. // Load the shader
  127. AZ::Data::AssetId shaderAssetId;
  128. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  129. shaderAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
  130. s_fillerShaderPath, azrtti_typeid<AZ::RPI::ShaderAsset>(), false);
  131. if (!shaderAssetId.IsValid())
  132. {
  133. AZ_Assert(false, "[DisplayMapperPass] Unable to obtain asset id for %s.", s_fillerShaderPath);
  134. }
  135. // Create the compute filler pass
  136. AZ::RPI::PassRequest createPassRequest;
  137. createPassRequest.m_templateName = AZ::Name(s_fillerPassTemplate);
  138. createPassRequest.m_passName = AZ::Name("RenderTargetPass");
  139. // Fill the pass data
  140. AZStd::shared_ptr<AZ::RPI::FullscreenTrianglePassData> passData = AZStd::make_shared<AZ::RPI::FullscreenTrianglePassData>();
  141. passData->m_shaderAsset.m_assetId = shaderAssetId;
  142. passData->m_shaderAsset.m_filePath = s_fillerShaderPath;
  143. createPassRequest.m_passData = AZStd::move(passData);
  144. // Create the connection for the output slot
  145. AZ::RPI::PassConnection connection = { AZ::Name("Output"), {AZ::Name("This"), AZ::Name(s_readbackImageName)} };
  146. createPassRequest.m_connections.push_back(connection);
  147. // Register the imported attachment
  148. AZ::RPI::PassImageAttachmentDesc imageAttachment;
  149. imageAttachment.m_name = s_readbackImageName;
  150. imageAttachment.m_lifetime = AZ::RHI::AttachmentLifetimeType::Imported;
  151. imageAttachment.m_assetRef.m_assetId = m_readbackImage->GetAssetId();
  152. createPassRequest.m_imageAttachmentOverrides.push_back(imageAttachment);
  153. // Create the pass
  154. m_fillerPass = AZ::RPI::PassSystemInterface::Get()->CreatePassFromRequest(&createPassRequest);
  155. }
  156. void ReadbackExampleComponent::CreatePreviewPass()
  157. {
  158. // Load the shader
  159. AZ::Data::AssetId shaderAssetId;
  160. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  161. shaderAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
  162. s_previewShaderPath, azrtti_typeid<AZ::RPI::ShaderAsset>(), false);
  163. if (!shaderAssetId.IsValid())
  164. {
  165. AZ_Assert(false, "[DisplayMapperPass] Unable to obtain asset id for %s.", s_previewShaderPath);
  166. }
  167. // Create the compute filler pass
  168. AZ::RPI::PassRequest createPassRequest;
  169. createPassRequest.m_templateName = AZ::Name(s_previewPassTemplate);
  170. createPassRequest.m_passName = AZ::Name("PreviewPass");
  171. AZStd::shared_ptr<AZ::RPI::FullscreenTrianglePassData> passData = AZStd::make_shared<AZ::RPI::FullscreenTrianglePassData>();
  172. passData->m_shaderAsset.m_assetId = shaderAssetId;
  173. passData->m_shaderAsset.m_filePath = s_previewShaderPath;
  174. createPassRequest.m_passData = AZStd::move(passData);
  175. // Create the connection for the output slot
  176. AZ::RPI::PassConnection outputConnection = { AZ::Name("Output"), {AZ::Name("Parent"), AZ::Name("PipelineOutput")} };
  177. createPassRequest.m_connections.push_back(outputConnection);
  178. AZ::RPI::PassConnection inputConnection = { AZ::Name("Input"), {AZ::Name("This"), AZ::Name(s_previewImageName)} };
  179. createPassRequest.m_connections.push_back(inputConnection);
  180. // Register the imported attachment
  181. AZ::RPI::PassImageAttachmentDesc imageAttachment;
  182. imageAttachment.m_name = s_previewImageName;
  183. imageAttachment.m_lifetime = AZ::RHI::AttachmentLifetimeType::Imported;
  184. imageAttachment.m_assetRef.m_assetId = m_previewImage->GetAssetId();
  185. createPassRequest.m_imageAttachmentOverrides.push_back(imageAttachment);
  186. m_previewPass = AZ::RPI::PassSystemInterface::Get()->CreatePassFromRequest(&createPassRequest);
  187. }
  188. void ReadbackExampleComponent::CreateResources()
  189. {
  190. AZ::Data::Instance<AZ::RPI::AttachmentImagePool> pool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool();
  191. // Create the readback target
  192. {
  193. AZ::RPI::CreateAttachmentImageRequest createRequest;
  194. createRequest.m_imageName = AZ::Name(s_readbackImageName);
  195. createRequest.m_isUniqueName = false;
  196. createRequest.m_imagePool = pool.get();
  197. createRequest.m_imageDescriptor = AZ::RHI::ImageDescriptor::Create2D(AZ::RHI::ImageBindFlags::Color | AZ::RHI::ImageBindFlags::ShaderWrite | AZ::RHI::ImageBindFlags::CopyRead | AZ::RHI::ImageBindFlags::CopyWrite, m_resourceWidth, m_resourceHeight, AZ::RHI::Format::R8G8B8A8_UNORM);
  198. m_readbackImage = AZ::RPI::AttachmentImage::Create(createRequest);
  199. }
  200. // Create the preview image
  201. {
  202. AZ::RPI::CreateAttachmentImageRequest createRequest;
  203. createRequest.m_imageName = AZ::Name(s_previewImageName);
  204. createRequest.m_isUniqueName = false;
  205. createRequest.m_imagePool = pool.get();
  206. createRequest.m_imageDescriptor = AZ::RHI::ImageDescriptor::Create2D(AZ::RHI::ImageBindFlags::ShaderRead | AZ::RHI::ImageBindFlags::CopyRead | AZ::RHI::ImageBindFlags::CopyWrite, m_resourceWidth, m_resourceHeight, AZ::RHI::Format::R8G8B8A8_UNORM);
  207. m_previewImage = AZ::RPI::AttachmentImage::Create(createRequest);
  208. }
  209. }
  210. void ReadbackExampleComponent::PerformReadback()
  211. {
  212. AZ_Assert(m_fillerPass, "Render target pass is null.");
  213. if (!m_readback)
  214. {
  215. m_readback = AZStd::make_shared<AZ::RPI::AttachmentReadback>(AZ::RHI::ScopeId{ "RenderTargetCapture" });
  216. m_readback->SetCallback(AZStd::bind(&ReadbackExampleComponent::ReadbackCallback, this, AZStd::placeholders::_1));
  217. }
  218. m_fillerPass->ReadbackAttachment(m_readback, 0, AZ::Name("Output"));
  219. }
  220. void ReadbackExampleComponent::ReadbackCallback(const AZ::RPI::AttachmentReadback::ReadbackResult& result)
  221. {
  222. AZ::RHI::ImageSubresourceLayout layout;
  223. m_previewImage->GetRHIImage()->GetSubresourceLayout(layout);
  224. m_textureNeedsUpdate = true;
  225. m_resultData = result.m_dataBuffer;
  226. // Fill the readback stats
  227. m_readbackStat.m_name = result.m_name;
  228. m_readbackStat.m_bytesRead = result.m_dataBuffer->size();
  229. m_readbackStat.m_descriptor = result.m_imageDescriptor;
  230. }
  231. void ReadbackExampleComponent::UploadReadbackResult() const
  232. {
  233. AZ::RHI::ImageSubresourceLayout layout;
  234. m_previewImage->GetRHIImage()->GetSubresourceLayout(layout);
  235. AZ::RHI::ImageUpdateRequest updateRequest;
  236. updateRequest.m_image = m_previewImage->GetRHIImage();
  237. updateRequest.m_sourceSubresourceLayout = layout;
  238. updateRequest.m_sourceData = m_resultData->begin();
  239. updateRequest.m_imageSubresourcePixelOffset = AZ::RHI::Origin(0, 0, 0);
  240. m_previewImage->UpdateImageContents(updateRequest);
  241. }
  242. void ReadbackExampleComponent::DrawSidebar()
  243. {
  244. if (m_imguiSidebar.Begin())
  245. {
  246. ImGui::Text("Readback resource dimensions:");
  247. if (ScriptableImGui::SliderInt("Width", reinterpret_cast<int*>(&m_resourceWidth), 1, 2048))
  248. {
  249. PassesChanged();
  250. }
  251. if (ScriptableImGui::SliderInt("Height", reinterpret_cast<int*>(&m_resourceHeight), 1, 2048))
  252. {
  253. PassesChanged();
  254. }
  255. ImGui::Separator();
  256. ImGui::NewLine();
  257. if (ScriptableImGui::Button("Readback")) {
  258. PerformReadback();
  259. }
  260. ImGui::NewLine();
  261. if (m_resultData)
  262. {
  263. ImGui::Separator();
  264. ImGui::Text("Readback statistics");
  265. ImGui::NewLine();
  266. ImGui::Text("Name: %s", m_readbackStat.m_name.GetCStr());
  267. ImGui::Text("Bytes read: %zu", m_readbackStat.m_bytesRead);
  268. ImGui::Text("[%i; %i; %i]", m_readbackStat.m_descriptor.m_size.m_width, m_readbackStat.m_descriptor.m_size.m_height, m_readbackStat.m_descriptor.m_size.m_depth);
  269. ImGui::Text("%s", AZ::RHI::ToString(m_readbackStat.m_descriptor.m_format));
  270. }
  271. m_imguiSidebar.End();
  272. }
  273. }
  274. }