BloomExampleComponent.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. /*
  2. * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
  3. * its licensors.
  4. *
  5. * For complete copyright and license terms please see the LICENSE at the root of this
  6. * distribution (the "License"). All use of this software is governed by the License,
  7. * or, if provided, by the license below or the license accompanying this file. Do not
  8. * remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
  9. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. *
  11. */
  12. #include <BloomExampleComponent.h>
  13. #include <AzCore/Component/Entity.h>
  14. #include <AzCore/IO/Path/Path.h>
  15. #include <AzFramework/Components/TransformComponent.h>
  16. #include <AzFramework/Entity/EntityContextBus.h>
  17. #include <SampleComponentManager.h>
  18. #include <SampleComponentConfig.h>
  19. #include <EntityUtilityFunctions.h>
  20. #include <Atom/Feature/Utils/FrameCaptureBus.h>
  21. #include <Atom/RHI/DrawPacketBuilder.h>
  22. #include <Atom/RPI.Public/RPISystemInterface.h>
  23. #include <Atom/RPI.Public/Shader/Shader.h>
  24. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  25. #include <RHI/BasicRHIComponent.h>
  26. namespace AtomSampleViewer
  27. {
  28. using namespace AZ;
  29. void BloomExampleComponent::Reflect(AZ::ReflectContext* context)
  30. {
  31. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  32. {
  33. serializeContext->Class<BloomExampleComponent, AZ::Component>()
  34. ->Version(0)
  35. ;
  36. }
  37. }
  38. BloomExampleComponent::BloomExampleComponent()
  39. : m_imageBrowser("@user@/BloomExampleComponent/image_browser.xml")
  40. {
  41. }
  42. void BloomExampleComponent::Activate()
  43. {
  44. m_dynamicDraw = RPI::GetDynamicDraw();
  45. RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get();
  46. m_postProcessFeatureProcessor = scene->GetFeatureProcessor<AZ::Render::PostProcessFeatureProcessorInterface>();
  47. CreateBloomEntity();
  48. m_imguiSidebar.Activate();
  49. AZ::TickBus::Handler::BusConnect();
  50. PrepareRenderData();
  51. m_imageBrowser.SetFilter([this](const AZ::Data::AssetInfo& assetInfo)
  52. {
  53. if (!AzFramework::StringFunc::Path::IsExtension(assetInfo.m_relativePath.c_str(), "streamingimage"))
  54. {
  55. return false;
  56. }
  57. AZStd::string assetPath(assetInfo.m_relativePath);
  58. if (!AzFramework::StringFunc::Path::Normalize(assetPath))
  59. {
  60. return false;
  61. }
  62. if (!Utils::IsFileUnderFolder(assetPath, InputImageFolder))
  63. {
  64. return false;
  65. }
  66. return true;
  67. });
  68. m_imageBrowser.Activate();
  69. // Load a default image
  70. QueueAssetPathForLoad("textures/tonemapping/hdr_test_pattern.exr.streamingimage");
  71. const char* engineRoot = nullptr;
  72. AZStd::string screenshotFolder;
  73. AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
  74. if (engineRoot)
  75. {
  76. AzFramework::StringFunc::Path::Join(engineRoot, "Screenshots", screenshotFolder, true, false);
  77. }
  78. Data::Asset<RPI::AnyAsset> displayMapperAsset = RPI::AssetUtils::LoadAssetByProductPath<RPI::AnyAsset>("passes/DisplayMapperConfiguration.azasset", RPI::AssetUtils::TraceLevel::Error);
  79. const Render::DisplayMapperConfigurationDescriptor* displayMapperConfigurationDescriptor = RPI::GetDataFromAnyAsset<Render::DisplayMapperConfigurationDescriptor>(displayMapperAsset);
  80. if (displayMapperConfigurationDescriptor == nullptr)
  81. {
  82. AZ_Error("DisplayMapperPass", false, "Failed to load display mapper configuration file.");
  83. return;
  84. }
  85. m_displayMapperConfiguration = *displayMapperConfigurationDescriptor;
  86. }
  87. void BloomExampleComponent::Deactivate()
  88. {
  89. AZ::TickBus::Handler::BusDisconnect();
  90. AZ::EntityBus::MultiHandler::BusDisconnect();
  91. if (m_bloomEntity)
  92. {
  93. DestroyEntity(m_bloomEntity, GetEntityContextId());
  94. m_postProcessFeatureProcessor = nullptr;
  95. }
  96. m_imguiSidebar.Deactivate();
  97. m_imageBrowser.Deactivate();
  98. }
  99. void BloomExampleComponent::OnEntityDestruction(const AZ::EntityId& entityId)
  100. {
  101. AZ::EntityBus::MultiHandler::BusDisconnect(entityId);
  102. if (m_bloomEntity && m_bloomEntity->GetId() == entityId)
  103. {
  104. m_postProcessFeatureProcessor->RemoveSettingsInterface(m_bloomEntity->GetId());
  105. m_bloomEntity = nullptr;
  106. }
  107. else
  108. {
  109. AZ_Assert(false, "unexpected entity destruction is signaled.");
  110. }
  111. }
  112. void BloomExampleComponent::OnTick([[maybe_unused]] float deltaTime, AZ::ScriptTimePoint)
  113. {
  114. if (m_drawImage.m_image && !m_drawImage.m_wasStreamed)
  115. {
  116. m_drawImage.m_wasStreamed = m_drawImage.m_image->GetResidentMipLevel() == 0;
  117. m_drawImage.m_srg->Compile();
  118. }
  119. DrawSidebar();
  120. DrawImage(&m_drawImage);
  121. }
  122. void BloomExampleComponent::DrawSidebar()
  123. {
  124. using namespace AZ::Render;
  125. const char* items[] = { "White", "Red", "Green", "Blue" };
  126. static int item_current0 = 0;
  127. static int item_current1 = 0;
  128. static int item_current2 = 0;
  129. static int item_current3 = 0;
  130. static int item_current4 = 0;
  131. auto ColorPicker = [](int index)->Vector3 {
  132. switch (index)
  133. {
  134. case 0: return Vector3(1.0, 1.0, 1.0); break;
  135. case 1: return Vector3(1.0, 0.5, 0.5); break;
  136. case 2: return Vector3(0.5, 1.0, 0.5); break;
  137. case 3: return Vector3(0.5, 0.5, 1.0); break;
  138. default: return Vector3(1.0, 1.0, 1.0);
  139. }
  140. };
  141. if (!m_imguiSidebar.Begin())
  142. {
  143. return;
  144. }
  145. ImGui::Spacing();
  146. bool enabled = m_bloomSettings->GetEnabled();
  147. if (ImGui::Checkbox("Enable", &enabled) || m_isInitParameters)
  148. {
  149. m_bloomSettings->SetEnabled(enabled);
  150. m_bloomSettings->OnConfigChanged();
  151. }
  152. ImGui::Spacing();
  153. float threshold = m_bloomSettings->GetThreshold();
  154. if (ImGui::SliderFloat("Threshold", &threshold, 0.0f, 20.0f, "%0.1f") || !m_isInitParameters)
  155. {
  156. m_bloomSettings->SetThreshold(threshold);
  157. m_bloomSettings->OnConfigChanged();
  158. }
  159. float knee = m_bloomSettings->GetKnee();
  160. if (ImGui::SliderFloat("Knee", &knee, 0.0f, 1.0f) || m_isInitParameters)
  161. {
  162. m_bloomSettings->SetKnee(knee);
  163. m_bloomSettings->OnConfigChanged();
  164. }
  165. ImGui::Spacing();
  166. float sizeScale = m_bloomSettings->GetKernelSizeScale();
  167. if (ImGui::SliderFloat("KernelSizeScale", &sizeScale, 0.0f, 2.0f) || m_isInitParameters)
  168. {
  169. m_bloomSettings->SetKernelSizeScale(sizeScale);
  170. m_bloomSettings->OnConfigChanged();
  171. }
  172. if (ImGui::CollapsingHeader("KernelSize", ImGuiTreeNodeFlags_DefaultOpen))
  173. {
  174. ImGui::Indent();
  175. float kernelSize0 = m_bloomSettings->GetKernelSizeStage0();
  176. if (ImGui::SliderFloat("Size0", &kernelSize0, 0.0f, 1.0f) || m_isInitParameters)
  177. {
  178. m_bloomSettings->SetKernelSizeStage0(kernelSize0);
  179. m_bloomSettings->OnConfigChanged();
  180. }
  181. float kernelSize1 = m_bloomSettings->GetKernelSizeStage1();
  182. if (ImGui::SliderFloat("Size1", &kernelSize1, 0.0f, 1.0f) || m_isInitParameters)
  183. {
  184. m_bloomSettings->SetKernelSizeStage1(kernelSize1);
  185. m_bloomSettings->OnConfigChanged();
  186. }
  187. float kernelSize2 = m_bloomSettings->GetKernelSizeStage2();
  188. if (ImGui::SliderFloat("Size2", &kernelSize2, 0.0f, 1.0f) || m_isInitParameters)
  189. {
  190. m_bloomSettings->SetKernelSizeStage2(kernelSize2);
  191. m_bloomSettings->OnConfigChanged();
  192. }
  193. float kernelSize3 = m_bloomSettings->GetKernelSizeStage3();
  194. if (ImGui::SliderFloat("Size3", &kernelSize3, 0.0f, 1.0f) || m_isInitParameters)
  195. {
  196. m_bloomSettings->SetKernelSizeStage3(kernelSize3);
  197. m_bloomSettings->OnConfigChanged();
  198. }
  199. float kernelSize4 = m_bloomSettings->GetKernelSizeStage4();
  200. if (ImGui::SliderFloat("Size4", &kernelSize4, 0.0f, 1.0f) || m_isInitParameters)
  201. {
  202. m_bloomSettings->SetKernelSizeStage4(kernelSize4);
  203. m_bloomSettings->OnConfigChanged();
  204. }
  205. ImGui::Unindent();
  206. }
  207. ImGui::Spacing();
  208. float intensity = m_bloomSettings->GetIntensity();
  209. if (ImGui::SliderFloat("Intensity", &intensity, 0.0f, 1.0f) || m_isInitParameters)
  210. {
  211. m_bloomSettings->SetIntensity(intensity);
  212. m_bloomSettings->OnConfigChanged();
  213. }
  214. if (ImGui::CollapsingHeader("Tint", ImGuiTreeNodeFlags_DefaultOpen))
  215. {
  216. ImGui::Indent();
  217. if (ImGui::ListBox("Tint0", &item_current0, items, IM_ARRAYSIZE(items), 4) || m_isInitParameters)
  218. {
  219. m_bloomSettings->SetTintStage0(ColorPicker(item_current0));
  220. m_bloomSettings->OnConfigChanged();
  221. }
  222. if (ImGui::ListBox("Tint1", &item_current1, items, IM_ARRAYSIZE(items), 4) || m_isInitParameters)
  223. {
  224. m_bloomSettings->SetTintStage1(ColorPicker(item_current1));
  225. m_bloomSettings->OnConfigChanged();
  226. }
  227. if (ImGui::ListBox("Tint2", &item_current2, items, IM_ARRAYSIZE(items), 4) || m_isInitParameters)
  228. {
  229. m_bloomSettings->SetTintStage2(ColorPicker(item_current2));
  230. m_bloomSettings->OnConfigChanged();
  231. }
  232. if (ImGui::ListBox("Tint3", &item_current3, items, IM_ARRAYSIZE(items), 4) || m_isInitParameters)
  233. {
  234. m_bloomSettings->SetTintStage3(ColorPicker(item_current3));
  235. m_bloomSettings->OnConfigChanged();
  236. }
  237. if (ImGui::ListBox("Tint4", &item_current4, items, IM_ARRAYSIZE(items), 4) || m_isInitParameters)
  238. {
  239. m_bloomSettings->SetTintStage4(ColorPicker(item_current4));
  240. m_bloomSettings->OnConfigChanged();
  241. }
  242. ImGui::Unindent();
  243. }
  244. ImGui::Spacing();
  245. bool bicubicEnabled = m_bloomSettings->GetBicubicEnabled();
  246. if (ImGui::Checkbox("Bicubic upsampling", &bicubicEnabled) || m_isInitParameters)
  247. {
  248. m_bloomSettings->SetBicubicEnabled(bicubicEnabled);
  249. m_bloomSettings->OnConfigChanged();
  250. }
  251. m_imguiSidebar.End();
  252. }
  253. void BloomExampleComponent::PrepareRenderData()
  254. {
  255. const auto CreatePipeline = [](const char* shaderFilepath,
  256. const char* srgFilepath,
  257. Data::Asset<AZ::RPI::ShaderResourceGroupAsset>& srgAsset,
  258. RHI::ConstPtr<RHI::PipelineState>& pipelineState,
  259. RHI::DrawListTag& drawListTag)
  260. {
  261. // Since the shader is using SV_VertexID and SV_InstanceID as VS input, we won't need to have vertex buffer.
  262. // Also, the index buffer is not needed with DrawLinear.
  263. RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
  264. Data::Asset<RPI::ShaderAsset> shaderAsset = RPI::AssetUtils::LoadAssetByProductPath<RPI::ShaderAsset>(shaderFilepath, RPI::AssetUtils::TraceLevel::Error);
  265. Data::Instance<RPI::Shader> shader = RPI::Shader::FindOrCreate(shaderAsset);
  266. if (!shader)
  267. {
  268. AZ_Error("Render", false, "Failed to find or create shader instance from shader asset with path %s", shaderFilepath);
  269. return;
  270. }
  271. const RPI::ShaderVariant& shaderVariant = shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
  272. shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
  273. drawListTag = shader->GetDrawListTag();
  274. RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get();
  275. scene->ConfigurePipelineState(shader->GetDrawListTag(), pipelineStateDescriptor);
  276. pipelineStateDescriptor.m_inputStreamLayout.SetTopology(AZ::RHI::PrimitiveTopology::TriangleStrip);
  277. pipelineStateDescriptor.m_inputStreamLayout.Finalize();
  278. pipelineState = shader->AcquirePipelineState(pipelineStateDescriptor);
  279. if (!pipelineState)
  280. {
  281. AZ_Error("Render", false, "Failed to acquire default pipeline state for shader %s", shaderFilepath);
  282. }
  283. // Load shader resource group asset
  284. srgAsset = RPI::AssetUtils::LoadAssetByProductPath<RPI::ShaderResourceGroupAsset>(srgFilepath, RPI::AssetUtils::TraceLevel::Error);
  285. };
  286. // Create the example's main pipeline object
  287. {
  288. CreatePipeline("Shaders/tonemappingexample/renderimage.azshader", "Shaders/tonemappingexample/renderimage_renderimagesrg.azsrg", m_srgAsset, m_pipelineState, m_drawListTag);
  289. // Set the input indices
  290. m_imageInputIndex = m_srgAsset->GetLayout()->FindShaderInputImageIndex(Name("m_texture"));
  291. m_positionInputIndex = m_srgAsset->GetLayout()->FindShaderInputConstantIndex(Name("m_position"));
  292. m_sizeInputIndex = m_srgAsset->GetLayout()->FindShaderInputConstantIndex(Name("m_size"));
  293. m_colorSpaceIndex = m_srgAsset->GetLayout()->FindShaderInputConstantIndex(Name("m_colorSpace"));
  294. }
  295. m_drawImage.m_srg = RPI::ShaderResourceGroup::Create(m_srgAsset);
  296. m_drawImage.m_wasStreamed = false;
  297. // Set the image to occupy the full screen.
  298. // The window's left bottom is (-1, -1). The window size is (2, 2)
  299. AZStd::array<float, 2> position, size;
  300. position[0] = -1.f;
  301. position[1] = -1.f;
  302. size[0] = 2.0f;
  303. size[1] = 2.0f;
  304. m_drawImage.m_srg->SetConstant(m_positionInputIndex, position);
  305. m_drawImage.m_srg->SetConstant(m_sizeInputIndex, size);
  306. m_drawImage.m_srg->SetConstant<int>(m_colorSpaceIndex, static_cast<int>(m_inputColorSpace));
  307. }
  308. void BloomExampleComponent::DrawImage(const ImageToDraw* imageInfo)
  309. {
  310. // Build draw packet
  311. RHI::DrawPacketBuilder drawPacketBuilder;
  312. drawPacketBuilder.Begin(nullptr);
  313. RHI::DrawLinear drawLinear;
  314. drawLinear.m_vertexCount = 4;
  315. drawPacketBuilder.SetDrawArguments(drawLinear);
  316. RHI::DrawPacketBuilder::DrawRequest drawRequest;
  317. drawRequest.m_listTag = m_drawListTag;
  318. drawRequest.m_pipelineState = m_pipelineState.get();
  319. drawRequest.m_sortKey = 0;
  320. drawRequest.m_uniqueShaderResourceGroup = imageInfo->m_srg->GetRHIShaderResourceGroup();
  321. drawPacketBuilder.AddDrawItem(drawRequest);
  322. // Submit draw packet
  323. AZStd::unique_ptr<const RHI::DrawPacket> drawPacket(drawPacketBuilder.End());
  324. m_dynamicDraw->AddDrawPacket(RPI::RPISystemInterface::Get()->GetDefaultScene().get(), AZStd::move(drawPacket));
  325. }
  326. RPI::ColorSpaceId BloomExampleComponent::GetColorSpaceIdForIndex(uint8_t colorSpaceIndex) const
  327. {
  328. const AZStd::vector<AZ::RPI::ColorSpaceId> colorSpaces =
  329. {
  330. RPI::ColorSpaceId::SRGB,
  331. RPI::ColorSpaceId::LinearSRGB,
  332. RPI::ColorSpaceId::ACEScg,
  333. RPI::ColorSpaceId::ACES2065
  334. };
  335. if (colorSpaceIndex >= colorSpaces.size())
  336. {
  337. AZ_Assert(false, "Invalid colorSpaceIndex");
  338. return RPI::ColorSpaceId::SRGB;
  339. }
  340. return colorSpaces[colorSpaceIndex];
  341. }
  342. void BloomExampleComponent::CreateBloomEntity()
  343. {
  344. m_bloomEntity = CreateEntity("Bloom", GetEntityContextId());
  345. // Bloom
  346. auto* bloomSettings = m_postProcessFeatureProcessor->GetOrCreateSettingsInterface(m_bloomEntity->GetId());
  347. m_bloomSettings = bloomSettings->GetOrCreateBloomSettingsInterface();
  348. m_bloomSettings->SetEnabled(false);
  349. m_bloomEntity->Activate();
  350. AZ::EntityBus::MultiHandler::BusConnect(m_bloomEntity->GetId());
  351. }
  352. void BloomExampleComponent::QueueAssetPathForLoad(const AZStd::string& filePath)
  353. {
  354. AZ::Data::AssetId imageAssetId;
  355. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  356. imageAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, filePath.c_str(),
  357. azrtti_typeid <AZ::RPI::StreamingImageAsset>(), false);
  358. AZ_Assert(imageAssetId.IsValid(), "Unable to load file %s", filePath.c_str());
  359. QueueAssetIdForLoad(imageAssetId);
  360. }
  361. void BloomExampleComponent::QueueAssetIdForLoad(const AZ::Data::AssetId& imageAssetId)
  362. {
  363. if (imageAssetId.IsValid())
  364. {
  365. Data::Asset<AZ::RPI::StreamingImageAsset> imageAsset;
  366. if (!imageAsset.Create(imageAssetId))
  367. {
  368. auto assetId = imageAssetId.ToString<AZStd::string>();
  369. AZ_Assert(false, "Unable to create image asset for asset ID %s", assetId.c_str());
  370. return;
  371. }
  372. auto image = AZ::RPI::StreamingImage::FindOrCreate(imageAsset);
  373. if (image == nullptr)
  374. {
  375. auto imageAssetName = imageAssetId.ToString<AZStd::string>();
  376. AZ_Assert(false, "Failed to find or create an image instance from image asset %s", imageAssetName.c_str());
  377. return;
  378. }
  379. m_drawImage.m_assetId = imageAssetId;
  380. m_drawImage.m_image = image;
  381. m_drawImage.m_wasStreamed = false;
  382. m_drawImage.m_srg->SetImage(m_imageInputIndex, m_drawImage.m_image);
  383. }
  384. }
  385. } // namespace AtomSampleViewer