StreamingImageExampleComponent.cpp 29 KB

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