StreamingImageExampleComponent.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  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/DrawPacket.h>
  9. #include <Atom/RHI/DrawPacketBuilder.h>
  10. #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
  11. #include <Atom/RPI.Public/Image/ImageSystemInterface.h>
  12. #include <Atom/RPI.Public/Image/StreamingImagePool.h>
  13. #include <Atom/RPI.Public/Image/StreamingImageController.h>
  14. #include <Atom/RPI.Public/RPISystemInterface.h>
  15. #include <Atom/RPI.Public/Scene.h>
  16. #include <Atom/RPI.Public/Shader/Shader.h>
  17. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  18. #include <Atom/RPI.Reflect/Image/StreamingImageAssetHandler.h>
  19. #include <Automation/ScriptableImGui.h>
  20. #include <Automation/ScriptRunnerBus.h>
  21. #include <AzCore/Asset/AssetManagerBus.h>
  22. #include <AzCore/IO/FileIO.h>
  23. #include <AzCore/IO/SystemFile.h>
  24. #include <AzCore/std/time.h>
  25. #include <AzCore/Utils/Utils.h>
  26. #include <AzFramework/API/ApplicationAPI.h>
  27. #include <RHI/BasicRHIComponent.h>
  28. #include <SampleComponentConfig.h>
  29. #include <StreamingImageExampleComponent.h>
  30. namespace AtomSampleViewer
  31. {
  32. using namespace AZ;
  33. void StreamingImageExampleComponent::Reflect(ReflectContext* context)
  34. {
  35. if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
  36. {
  37. serializeContext->Class < StreamingImageExampleComponent, Component>()
  38. ->Version(0)
  39. ;
  40. }
  41. }
  42. void StreamingImageExampleComponent::PrepareRenderData()
  43. {
  44. const auto CreatePipeline = [](const char* shaderFilepath,
  45. const char* srgName,
  46. Data::Asset<AZ::RPI::ShaderAsset>& shaderAsset,
  47. RHI::Ptr<AZ::RHI::ShaderResourceGroupLayout>& srgLayout,
  48. RHI::ConstPtr<RHI::PipelineState>& pipelineState,
  49. RHI::DrawListTag& drawListTag,
  50. RPI::Scene* scene)
  51. {
  52. // Since the shader is using SV_VertexID and SV_InstanceID as VS input, we won't need to have vertex buffer.
  53. // Also, the index buffer is not needed with DrawLinear.
  54. RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
  55. shaderAsset = RPI::AssetUtils::LoadAssetByProductPath<RPI::ShaderAsset>(shaderFilepath, RPI::AssetUtils::TraceLevel::Error);
  56. Data::Instance<RPI::Shader> shader = RPI::Shader::FindOrCreate(shaderAsset);
  57. if (!shader)
  58. {
  59. AZ_Error("Render", false, "Failed to find or create shader instance from shader asset with path %s", shaderFilepath);
  60. return;
  61. }
  62. const RPI::ShaderVariant& shaderVariant = shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
  63. shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
  64. drawListTag = shader->GetDrawListTag();
  65. scene->ConfigurePipelineState(shader->GetDrawListTag(), pipelineStateDescriptor);
  66. pipelineStateDescriptor.m_inputStreamLayout.SetTopology(AZ::RHI::PrimitiveTopology::TriangleStrip);
  67. pipelineStateDescriptor.m_inputStreamLayout.Finalize();
  68. pipelineState = shader->AcquirePipelineState(pipelineStateDescriptor);
  69. if (!pipelineState)
  70. {
  71. AZ_Error("Render", false, "Failed to acquire default pipeline state for shader %s", shaderFilepath);
  72. }
  73. // Load shader resource group layout
  74. srgLayout = shaderAsset->FindShaderResourceGroupLayout(AZ::Name(srgName));
  75. };
  76. // Create the example's main pipeline object
  77. {
  78. CreatePipeline("Shaders/streamingimageexample/imagemips.azshader", "ImageMipsSrg", m_shaderAsset, m_srgLayout, m_pipelineState, m_drawListTag, m_scene);
  79. // Set the input indices
  80. m_imageInputIndex = m_srgLayout->FindShaderInputImageIndex(Name("m_texture"));
  81. m_positionInputIndex = m_srgLayout->FindShaderInputConstantIndex(Name("m_position"));
  82. m_sizeInputIndex = m_srgLayout->FindShaderInputConstantIndex(Name("m_size"));
  83. m_residentMipInputIndex = m_srgLayout->FindShaderInputConstantIndex(Name("m_residentMip"));
  84. // Create an SRG instance for the hot reloaded image
  85. m_imageHotReload.m_srg = RPI::ShaderResourceGroup::Create(m_shaderAsset, m_srgLayout->GetName());
  86. }
  87. // Create the 3D pipeline object
  88. {
  89. CreatePipeline("Shaders/streamingimageexample/image3d.azshader", "ImageSrg", m_image3dShaderAsset, m_image3dSrgLayout, m_image3dPipelineState,
  90. m_image3dDrawListTag, m_scene);
  91. }
  92. }
  93. void StreamingImageExampleComponent::Activate()
  94. {
  95. m_geometryView.SetDrawArguments(RHI::DrawLinear(4, 0));
  96. // Save the streaming image pool's budget and streaming image pool controller's mip bias
  97. // These would be recovered when exist the example
  98. Data::Instance<RPI::StreamingImagePool> streamingImagePool = RPI::ImageSystemInterface::Get()->GetSystemStreamingPool();
  99. m_cachedPoolBudget = streamingImagePool->GetMemoryBudget();
  100. m_cachedMipBias = streamingImagePool->GetMipBias();
  101. m_enableHotReloadTest = IsHotReloadTestSupported();
  102. m_dynamicDraw = RPI::GetDynamicDraw();
  103. m_imguiSidebar.Activate();
  104. PrepareRenderData();
  105. if (m_enableHotReloadTest)
  106. {
  107. DeleteHotReloadImage();
  108. }
  109. m_loadImageStart = AZStd::GetTimeUTCMilliSecond();
  110. // Queue load all the textures under Textures\Streaming folder
  111. for (uint32_t index = 0; index < TestDDSCount; index++)
  112. {
  113. AZ::IO::Path filePath = TestImageFolder / AZStd::string::format("streaming%d.dds.streamingimage", index);
  114. QueueForLoad(filePath.Native());
  115. }
  116. // All Images loaded here have non-power-of-two sizes
  117. for (uint32_t index = 0; index < TestPNGCount; index++)
  118. {
  119. AZ::IO::Path filePath = TestImageFolder / AZStd::string::format("streaming%d.png.streamingimage", index);
  120. QueueForLoad(filePath.Native());
  121. }
  122. AZ::TickBus::Handler::BusConnect();
  123. if (m_enableHotReloadTest)
  124. {
  125. AzFramework::AssetCatalogEventBus::Handler::BusConnect();
  126. SwitchHotReloadImage();
  127. }
  128. // Pause lua script (automation) until all the images loaded
  129. // Use a 10 seconds timeout instead of default one in case of longer loading time
  130. ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::PauseScriptWithTimeout, 10.0f);
  131. m_automationPaused = true;
  132. }
  133. void StreamingImageExampleComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  134. {
  135. // Skip processing if the asset is the asset for hot reloading test
  136. // The original asset was already processed.
  137. if (m_reloadingAsset == asset.GetId())
  138. {
  139. return;
  140. }
  141. AZ::Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());;
  142. const float ratioYtoX = 1.0f;
  143. // Create the image for hot reload test and setup its srg for draw
  144. if (m_imageHotReload.m_assetId == asset.GetId())
  145. {
  146. m_imageHotReload.m_image = AZ::RPI::StreamingImage::FindOrCreate(asset);
  147. m_imageHotReload.m_srg->SetImage(m_imageInputIndex, m_imageHotReload.m_image);
  148. AZStd::array<float, 2> position{
  149. { 0.33f, 0.22f }
  150. };
  151. AZStd::array<float, 2> size{
  152. { 0.2f, 0.2f / ratioYtoX }
  153. };
  154. m_imageHotReload.m_srg->SetConstant(m_positionInputIndex, position);
  155. m_imageHotReload.m_srg->SetConstant(m_sizeInputIndex, size);
  156. m_imageHotReload.m_srg->SetConstant(m_residentMipInputIndex, static_cast<int>(m_imageHotReload.m_image->GetResidentMipLevel()));
  157. // Note: no need to queue the srg for compile since the OnTick would compile it anyway
  158. return;
  159. }
  160. auto iter = AZStd::find_if(m_images.begin(), m_images.end(), [&asset](const ImageToDraw& image)
  161. {
  162. return image.m_assetId == asset.GetId();
  163. });
  164. if (iter == m_images.end())
  165. {
  166. return;
  167. }
  168. ImageToDraw* imageToDraw = iter;
  169. ptrdiff_t index = AZStd::distance(m_images.begin(), iter);
  170. AZ::u64 startTime = AZStd::GetTimeUTCMilliSecond();
  171. imageToDraw->m_image = AZ::RPI::StreamingImage::FindOrCreate(asset);
  172. AZ::u64 endTime = AZStd::GetTimeUTCMilliSecond();
  173. m_createImageTime += endTime - startTime;
  174. AZ::Data::AssetInfo info;
  175. AZ::Data::AssetCatalogRequestBus::BroadcastResult(info, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, imageToDraw->m_image->GetAssetId());
  176. m_initialImageAssetSize += info.m_sizeBytes;
  177. imageToDraw->m_srg->SetImage(m_imageInputIndex, imageToDraw->m_image);
  178. size_t rows = 6;
  179. size_t columns = (m_images.size() + rows - 1) / rows;
  180. float xOffset = AreaWidth / columns;
  181. float yOffset = AreaHeight / rows;
  182. AZStd::array<float, 2> mipSize;
  183. if (yOffset * ratioYtoX > xOffset / 1.5f) // the xOffset should be 1.5 times of mip size so it can show lower mips
  184. {
  185. mipSize[0] = xOffset / 1.5f;
  186. mipSize[1] = mipSize[0] / ratioYtoX;
  187. }
  188. else
  189. {
  190. mipSize[0] = yOffset * ratioYtoX;
  191. mipSize[1] = yOffset;
  192. }
  193. AZStd::array<float, 2> position;
  194. position[0] = (index / rows) * xOffset + AreaLeft;
  195. position[1] = (index % rows) * yOffset + AreaBottom;
  196. imageToDraw->m_srg->SetConstant(m_positionInputIndex, position);
  197. imageToDraw->m_srg->SetConstant(m_sizeInputIndex, mipSize);
  198. imageToDraw->m_srg->SetConstant<int>(m_residentMipInputIndex, imageToDraw->m_image->GetResidentMipLevel());
  199. // no need to queue for compile since the OnTick would compile it anyway
  200. m_numImageCreated++;
  201. if (m_numImageAssetQueued == m_numImageCreated)
  202. {
  203. // All images created.
  204. m_loadImageEnd = AZStd::GetTimeUTCMilliSecond();
  205. }
  206. }
  207. void StreamingImageExampleComponent::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
  208. {
  209. if (m_reloadingAsset == asset.GetId())
  210. {
  211. m_reloadingAsset.SetInvalid();
  212. AZ::Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
  213. }
  214. }
  215. void StreamingImageExampleComponent::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
  216. {
  217. OnCatalogAssetAdded(assetId);
  218. }
  219. void StreamingImageExampleComponent::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
  220. {
  221. if (!m_imageHotReload.m_assetId.IsValid())
  222. {
  223. AZ::IO::Path filePath = TestImageFolder / (ReloadTestImageName.Native() + ".streamingimage");
  224. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  225. m_imageHotReload.m_assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, filePath.c_str(),
  226. azrtti_typeid <AZ::RPI::StreamingImageAsset>(), false);
  227. }
  228. if (m_imageHotReload.m_assetId == assetId)
  229. {
  230. m_imageHotReload.m_asset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::StreamingImageAsset>(assetId, AZ::Data::AssetLoadBehavior::PreLoad);
  231. AZ::Data::AssetBus::MultiHandler::BusConnect(assetId);
  232. AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
  233. }
  234. }
  235. void StreamingImageExampleComponent::Deactivate()
  236. {
  237. if (AzFramework::AssetCatalogEventBus::Handler::BusIsConnected())
  238. {
  239. AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
  240. }
  241. AZ::TickBus::Handler::BusDisconnect();
  242. // If there are any assets that haven't finished loading yet, and thus haven't been disconnected, disconnect now.
  243. AZ::Data::AssetBus::MultiHandler::BusDisconnect();
  244. if (m_enableHotReloadTest)
  245. {
  246. DeleteHotReloadImage();
  247. }
  248. m_imguiSidebar.Deactivate();
  249. m_images.clear();
  250. m_numImageAssetQueued = 0;
  251. m_numImageCreated = 0;
  252. m_imageHotReload.Reset();
  253. m_pipelineState = nullptr;
  254. m_drawListTag.Reset();
  255. m_shaderAsset = {};
  256. m_srgLayout = nullptr;
  257. m_3dImages.clear();
  258. m_image3dShaderAsset = {};
  259. m_image3dSrgLayout = nullptr;
  260. m_image3dPipelineState = nullptr;
  261. m_image3dDrawListTag.Reset();
  262. // Recover pool budget and mip bias
  263. Data::Instance<RPI::StreamingImagePool> streamingImagePool = RPI::ImageSystemInterface::Get()->GetSystemStreamingPool();
  264. streamingImagePool->SetMemoryBudget(m_cachedPoolBudget);
  265. streamingImagePool->SetMipBias(m_cachedMipBias);
  266. }
  267. void StreamingImageExampleComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint timePoint)
  268. {
  269. // update image streaming states
  270. uint32_t numStreamed = 0;
  271. for (auto& imageInfo : m_images)
  272. {
  273. if (imageInfo.m_image)
  274. {
  275. imageInfo.m_srg->SetConstant<int>(m_residentMipInputIndex, imageInfo.m_image->GetResidentMipLevel());
  276. imageInfo.m_srg->Compile();
  277. if (imageInfo.m_image->IsStreamed())
  278. {
  279. numStreamed ++;
  280. }
  281. }
  282. }
  283. // only need to set the image the first time all images were streamed
  284. if (m_streamingImageEnd == 0 && numStreamed == m_numImageCreated && m_numImageCreated > 0 && m_numImageAssetQueued == m_numImageCreated)
  285. {
  286. m_streamingImageEnd = AZStd::GetTimeUTCMilliSecond();
  287. for (auto& imageInfo : m_images)
  288. {
  289. m_imageAssetSize += GetImageAssetSize(imageInfo.m_image.get());
  290. }
  291. }
  292. if (m_imageHotReload.m_image)
  293. {
  294. m_imageHotReload.m_srg->SetConstant<int>(m_residentMipInputIndex, m_imageHotReload.m_image->GetResidentMipLevel());
  295. m_imageHotReload.m_srg->Compile();
  296. }
  297. bool streamingFinished = m_streamingImageEnd > 0;
  298. // Resume automation when all image streamed and hot reload finished
  299. if (m_automationPaused && streamingFinished && m_imageHotReload.m_image && m_imageHotReload.m_image->IsStreamed() && !m_reloadingAsset.IsValid())
  300. {
  301. ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::ResumeScript);
  302. m_automationPaused = false;
  303. }
  304. if (m_imguiSidebar.Begin())
  305. {
  306. Data::Instance<RPI::StreamingImagePool> streamingImagePool = RPI::ImageSystemInterface::Get()->GetSystemStreamingPool();
  307. const auto* rhiPool = streamingImagePool->GetRHIPool();
  308. const RHI::HeapMemoryUsage& memoryUsage = rhiPool->GetHeapMemoryUsage(RHI::HeapMemoryLevel::Device);
  309. const size_t MB = 1024*1024;
  310. // streaming image pool info
  311. ImGui::BeginGroup();
  312. ImGui::Text("Streaming image pool");
  313. ImGui::Indent();
  314. ImGui::Text("Image count: %u", streamingImagePool->GetImageCount());
  315. ImGui::Text("Streamable image count: %u", streamingImagePool->GetStreamableImageCount());
  316. size_t totalResident = memoryUsage.m_totalResidentInBytes.load();
  317. ImGui::Text("Allocated GPU memory: %zu MB (%zu)", totalResident/MB, totalResident);
  318. size_t usedResident = memoryUsage.m_usedResidentInBytes.load();
  319. ImGui::Text("Used GPU memory: %zu MB (%zu)", usedResident/MB, usedResident);
  320. size_t budgetInBytes = memoryUsage.m_budgetInBytes;
  321. int budgetInMB = aznumeric_cast<int>(budgetInBytes/MB);
  322. ImGui::Text("GPU memory budget: %d MB", budgetInMB);
  323. if (ScriptableImGui::SliderInt("MB", &budgetInMB, RHI::DeviceStreamingImagePool::ImagePoolMininumSizeInBytes / MB, 512))
  324. {
  325. streamingImagePool->SetMemoryBudget(budgetInMB * (1024*1024));
  326. }
  327. int mipBias = streamingImagePool->GetMipBias();
  328. ImGui::Text("Mip bias");
  329. if (ScriptableImGui::SliderInt("", &mipBias, -aznumeric_cast<int>(RHI::Limits::Image::MipCountMax), RHI::Limits::Image::MipCountMax))
  330. {
  331. streamingImagePool->SetMipBias(aznumeric_cast<int16_t>(mipBias));
  332. }
  333. ImGui::Unindent();
  334. ImGui::EndGroup();
  335. // Draw menus and info when streaming is finished
  336. if (streamingFinished)
  337. {
  338. // For switching rendering content between streamed images or 3d images
  339. ImGui::Text("Image type view");
  340. ImGui::Indent();
  341. const char* buttonLabel = m_viewStreamedImages ? "View 3D Images" : "View Streamed Images";
  342. if (ScriptableImGui::Button(buttonLabel))
  343. {
  344. m_viewStreamedImages = !m_viewStreamedImages;
  345. }
  346. ImGui::Unindent();
  347. ImGui::Separator();
  348. ImGui::NewLine();
  349. // For StreamingImageAsset hot reloading test
  350. ImGui::Text("Hot Reload Test");
  351. ImGui::Indent();
  352. if (m_enableHotReloadTest && m_imageHotReload.m_image && m_imageHotReload.m_image->IsStreamed())
  353. {
  354. if (ScriptableImGui::Button("Switch texture"))
  355. {
  356. SwitchHotReloadImage();
  357. ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::PauseScriptWithTimeout, 10.0f);
  358. m_automationPaused = true;
  359. m_reloadingAsset = m_imageHotReload.m_assetId;
  360. AZ::Data::AssetBus::MultiHandler::BusConnect(m_reloadingAsset);
  361. }
  362. }
  363. ImGui::Unindent();
  364. // Display streaming profile result
  365. ImGui::Separator();
  366. ImGui::NewLine();
  367. DisplayStreamingProfileData();
  368. }
  369. m_imguiSidebar.End();
  370. }
  371. if (m_viewStreamedImages)
  372. {
  373. DrawImages();
  374. }
  375. else
  376. {
  377. if (m_3dImages.size() == 0)
  378. {
  379. Create3dimages();
  380. if (m_3dImages.size() == 0)
  381. {
  382. m_viewStreamedImages = true;
  383. return;
  384. }
  385. }
  386. Draw3dImages();
  387. }
  388. }
  389. void StreamingImageExampleComponent::DrawImages()
  390. {
  391. for (auto& imageInfo : m_images)
  392. {
  393. if (imageInfo.m_image != nullptr)
  394. {
  395. DrawImage(&imageInfo);
  396. }
  397. }
  398. if (m_imageHotReload.m_image != nullptr)
  399. {
  400. DrawImage(&m_imageHotReload);
  401. }
  402. }
  403. void StreamingImageExampleComponent::Draw3dImages()
  404. {
  405. for (Image3dToDraw& image3d : m_3dImages)
  406. {
  407. // Build draw packet...
  408. RHI::DrawPacketBuilder drawPacketBuilder{RHI::MultiDevice::DefaultDevice};
  409. drawPacketBuilder.Begin(nullptr);
  410. drawPacketBuilder.SetGeometryView(&m_geometryView);
  411. drawPacketBuilder.SetDrawInstanceArguments(RHI::DrawInstanceArguments(image3d.m_sliceCount, 0));
  412. RHI::DrawPacketBuilder::DrawRequest drawRequest;
  413. drawRequest.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
  414. drawRequest.m_listTag = m_image3dDrawListTag;
  415. drawRequest.m_pipelineState = m_image3dPipelineState.get();
  416. drawRequest.m_sortKey = 0;
  417. drawRequest.m_uniqueShaderResourceGroup = image3d.m_srg->GetRHIShaderResourceGroup();
  418. drawPacketBuilder.AddDrawItem(drawRequest);
  419. // Submit draw packet...
  420. auto drawPacket{drawPacketBuilder.End()};
  421. m_dynamicDraw->AddDrawPacket(m_scene, AZStd::move(drawPacket));
  422. }
  423. }
  424. void StreamingImageExampleComponent::DrawImage(ImageToDraw* imageInfo)
  425. {
  426. // Build draw packet...
  427. RHI::DrawPacketBuilder drawPacketBuilder{RHI::MultiDevice::DefaultDevice};
  428. drawPacketBuilder.Begin(nullptr);
  429. drawPacketBuilder.SetGeometryView(&m_geometryView);
  430. drawPacketBuilder.SetDrawInstanceArguments(RHI::DrawInstanceArguments(imageInfo->m_image->GetMipLevelCount(), 0));
  431. RHI::DrawPacketBuilder::DrawRequest drawRequest;
  432. drawRequest.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
  433. drawRequest.m_listTag = m_drawListTag;
  434. drawRequest.m_pipelineState = m_pipelineState.get();
  435. drawRequest.m_sortKey = 0;
  436. drawRequest.m_uniqueShaderResourceGroup = imageInfo->m_srg->GetRHIShaderResourceGroup();
  437. drawPacketBuilder.AddDrawItem(drawRequest);
  438. // Submit draw packet...
  439. auto drawPacket{drawPacketBuilder.End()};
  440. m_dynamicDraw->AddDrawPacket(m_scene, AZStd::move(drawPacket));
  441. }
  442. void StreamingImageExampleComponent::DisplayStreamingProfileData()
  443. {
  444. ImGui::BeginGroup();
  445. ImGui::Text("Streaming Profile");
  446. ImGui::Indent();
  447. ImGui::Text("Streaming Image Count: %d", m_numImageCreated);
  448. ImGui::Text("Load and Create Images: %llu ms", m_loadImageEnd - m_loadImageStart);
  449. ImGui::Text("Create Images: %llu ms", m_createImageTime);
  450. ImGui::Text("Streaming Image Mips: %llu ms", m_streamingImageEnd - m_loadImageEnd);
  451. ImGui::Text("Initial image asset size: %.3f MB", m_initialImageAssetSize / (1024 * 1024.0f));
  452. ImGui::Text("Total image asset size: %.3f MB", m_imageAssetSize / (1024 * 1024.0f));
  453. ImGui::Unindent();
  454. ImGui::EndGroup();
  455. }
  456. bool StreamingImageExampleComponent::CopyFile(const AZStd::string& destFile, const AZStd::string& sourceFile)
  457. {
  458. IO::FileIOStream fileRead(sourceFile.c_str(), IO::OpenMode::ModeRead | IO::OpenMode::ModeBinary);
  459. IO::FileIOStream fileWrite(destFile.c_str(), IO::OpenMode::ModeWrite | IO::OpenMode::ModeCreatePath | IO::OpenMode::ModeBinary);
  460. if (!fileRead.IsOpen() || !fileWrite.IsOpen())
  461. {
  462. AZ_Assert(fileRead.IsOpen(), "Failed to open file [%s] for read", sourceFile.c_str());
  463. AZ_Assert(fileWrite.IsOpen(), "Failed to open file [%s] for write", destFile.c_str());
  464. return false;
  465. }
  466. auto fileLength = fileRead.GetLength();
  467. AZStd::vector<u8> buffer;
  468. buffer.resize_no_construct(fileLength);
  469. fileRead.Read(fileLength, buffer.data());
  470. fileWrite.Write(fileLength, buffer.data());
  471. return true;
  472. }
  473. void StreamingImageExampleComponent::SwitchHotReloadImage()
  474. {
  475. m_curSourceImage = 1 - m_curSourceImage;
  476. // uses smaller size images for the hot reloading test so the lua automation test won't take very long time
  477. AZStd::string sourceImageFiles[] = {
  478. {"streaming1.png"}, {"streaming2.png" }
  479. };
  480. AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath();
  481. AZ::IO::FixedMaxPath srcPath = projectPath / TestImageFolder / sourceImageFiles[m_curSourceImage];
  482. AZ::IO::FixedMaxPath destPath = projectPath / TestImageFolder / ReloadTestImageName;
  483. CopyFile(destPath.String(), srcPath.String());
  484. }
  485. void StreamingImageExampleComponent::DeleteHotReloadImage()
  486. {
  487. const auto testFileFullPath = AZ::IO::FixedMaxPath(AZ::Utils::GetProjectPath()) / TestImageFolder / ReloadTestImageName;
  488. if (AZ::IO::SystemFile::Exists(testFileFullPath.c_str()))
  489. {
  490. AZ::IO::SystemFile::Delete(testFileFullPath.c_str());
  491. }
  492. }
  493. u64 StreamingImageExampleComponent::GetImageAssetSize(AZ::RPI::StreamingImage* image)
  494. {
  495. u64 totalSize = 0;
  496. AZ::Data::AssetInfo info;
  497. AZ::Data::AssetCatalogRequestBus::BroadcastResult(info, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, image->GetAssetId());
  498. totalSize += info.m_sizeBytes;
  499. AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> result = AZ::Failure<AZStd::string>("No response");
  500. AZ::Data::AssetCatalogRequestBus::BroadcastResult(result, &AZ::Data::AssetCatalogRequestBus::Events::GetDirectProductDependencies, image->GetAssetId());
  501. if (result.IsSuccess())
  502. {
  503. auto& dependencies = result.GetValue();
  504. for (const auto& dependency : dependencies)
  505. {
  506. info.m_sizeBytes = 0;
  507. AZ::Data::AssetCatalogRequestBus::BroadcastResult(info, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, dependency.m_assetId);
  508. if (info.m_assetType == azrtti_typeid<RPI::ImageMipChainAsset>())
  509. {
  510. totalSize += info.m_sizeBytes;
  511. }
  512. }
  513. }
  514. return totalSize;
  515. }
  516. void StreamingImageExampleComponent::QueueForLoad(const AZStd::string& filePath)
  517. {
  518. AZ::Data::AssetId imageAssetId;
  519. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  520. imageAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, filePath.c_str(),
  521. azrtti_typeid <AZ::RPI::StreamingImageAsset>(), false);
  522. AZ_Assert(imageAssetId.IsValid(), "Unable to load file %s", filePath.c_str());
  523. if (imageAssetId.IsValid())
  524. {
  525. m_numImageAssetQueued++;
  526. ImageToDraw img;
  527. img.m_asset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::StreamingImageAsset>(imageAssetId, AZ::Data::AssetLoadBehavior::PreLoad);
  528. img.m_srg = RPI::ShaderResourceGroup::Create(m_shaderAsset, m_srgLayout->GetName());
  529. img.m_assetId = imageAssetId;
  530. m_images.push_back(img);
  531. AZ::Data::AssetBus::MultiHandler::BusConnect(imageAssetId);
  532. }
  533. }
  534. void StreamingImageExampleComponent::Create3dimages()
  535. {
  536. // Use this pool to create the streaming images from
  537. const Data::Instance<RPI::StreamingImagePool>& imagePool = RPI::ImageSystemInterface::Get()->GetSystemStreamingPool();
  538. // Fetch the shader input indices
  539. const RHI::ShaderInputImageIndex image3DInputIndex = m_image3dSrgLayout->FindShaderInputImageIndex(Name("m_texture3D"));
  540. const RHI::ShaderInputConstantIndex sliceCountInputIndex = m_image3dSrgLayout->FindShaderInputConstantIndex(Name("m_sliceCount"));
  541. const RHI::ShaderInputConstantIndex positionInputIndex = m_image3dSrgLayout->FindShaderInputConstantIndex(Name("m_position"));
  542. const RHI::ShaderInputConstantIndex sizeInputIndex = m_image3dSrgLayout->FindShaderInputConstantIndex(Name("m_size"));
  543. // Create a small 3D image, where the row count of an image isn't the same as the height (e.g BC formats). A single slice of the image
  544. // will be uploaded with a single command
  545. {
  546. AZStd::vector<uint8_t> imageData;
  547. RHI::DeviceImageSubresourceLayout layout;
  548. RHI::Format format = {};
  549. BasicRHIComponent::CreateImage3dData(imageData, layout, format, {
  550. "textures/streaming/streaming13.dds.streamingimage",
  551. "textures/streaming/streaming14.dds.streamingimage",
  552. "textures/streaming/streaming15.dds.streamingimage",
  553. "textures/streaming/streaming16.dds.streamingimage",
  554. "textures/streaming/streaming17.dds.streamingimage",
  555. "textures/streaming/streaming19.dds.streamingimage",
  556. "textures/streaming/streaming20.dds.streamingimage" });
  557. auto image = RPI::StreamingImage::CreateFromCpuData(*imagePool.get(),
  558. RHI::ImageDimension::Image3D,
  559. layout.m_size,
  560. format, imageData.data(), imageData.size());
  561. // The image may not be created successfully if the memory budget was set to a very low value
  562. if (image)
  563. {
  564. // Create the srg
  565. Image3dToDraw image3d;
  566. image3d.m_srg = RPI::ShaderResourceGroup::Create(m_image3dShaderAsset, m_image3dSrgLayout->GetName());
  567. image3d.m_srg->SetImage(image3DInputIndex, image.get(), 0);
  568. image3d.m_sliceCount = layout.m_size.m_depth;
  569. m_3dImages.emplace_back(image3d);
  570. }
  571. };
  572. // Create large 3D images, where a single image slice is staged/uploaded with multiple commands
  573. {
  574. struct V4u32
  575. {
  576. float x, y, z, w;
  577. };
  578. const auto createColorImageData = [](V4u32 color, uint32_t width, uint32_t height, AZStd::vector<uint8_t>& data)
  579. {
  580. const uint32_t imageSize = width * height * sizeof(color);
  581. data.resize(data.size() + imageSize);
  582. V4u32* sourceData = reinterpret_cast<V4u32*>(data.data() + data.size() - imageSize);
  583. for (uint32_t y = 0; y < height; y++)
  584. {
  585. for (uint32_t x = 0; x < width; x++)
  586. {
  587. memcpy(sourceData, &color, sizeof(color));
  588. sourceData++;
  589. }
  590. }
  591. };
  592. AZStd::vector<uint8_t> imageData;
  593. const uint32_t side = 1001;
  594. const uint32_t depth = 3;
  595. createColorImageData({ 1.0f,0.0f,0.0f,0.0f }, side, side, imageData);
  596. createColorImageData({ 0.0f,1.0f,0.0f,0.0f }, side, side, imageData);
  597. createColorImageData({ 0.0f,0.0f,1.0f,0.0f }, side, side, imageData);
  598. auto image = RPI::StreamingImage::CreateFromCpuData(*imagePool.get(),
  599. RHI::ImageDimension::Image3D,
  600. RHI::Size(side, side, depth),
  601. RHI::Format::R32G32B32A32_FLOAT, imageData.data(), imageData.size());
  602. // Create the srg
  603. Image3dToDraw image3d;
  604. image3d.m_srg = RPI::ShaderResourceGroup::Create(m_image3dShaderAsset, m_image3dSrgLayout->GetName());
  605. image3d.m_srg->SetImage(image3DInputIndex, image.get(), 0);
  606. image3d.m_sliceCount = depth;
  607. m_3dImages.emplace_back(image3d);
  608. }
  609. // Set the constants for all 3d images
  610. {
  611. // Rendering area, use the same surface area of the streaming images
  612. const float ratioYtoX = 1;
  613. const float seam = 0.01f;
  614. const uint32_t rows = 6;
  615. for (uint32_t i = 0; i < m_3dImages.size(); i++)
  616. {
  617. Image3dToDraw& image3d = m_3dImages[i];
  618. const uint32_t columns = image3d.m_sliceCount;
  619. // Calculate the size
  620. const float xOffset = AreaWidth / static_cast<float>(columns);
  621. const float yOffset = AreaHeight / static_cast<float>(rows);
  622. const float minSize = AZStd::min(xOffset, yOffset);
  623. const AZStd::array<float, 2> size = { {minSize * ratioYtoX, minSize } };
  624. // Calculate the position
  625. AZStd::array<float, 2> position;
  626. position[0] = AreaLeft;
  627. position[1] = static_cast<float>(i) * (yOffset + seam) + AreaBottom;
  628. image3d.m_srg->SetConstant<uint32_t>(sliceCountInputIndex, image3d.m_sliceCount);
  629. image3d.m_srg->SetConstant(positionInputIndex, position);
  630. image3d.m_srg->SetConstant(sizeInputIndex, size);
  631. image3d.m_srg->Compile();
  632. }
  633. }
  634. }
  635. }