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