StreamingImage.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  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/RPI.Public/Image/ImageSystemInterface.h>
  9. #include <Atom/RPI.Public/Image/StreamingImage.h>
  10. #include <Atom/RPI.Public/Image/StreamingImagePool.h>
  11. #include <Atom/RPI.Public/Image/StreamingImageController.h>
  12. #include <Atom/RPI.Reflect/Image/ImageMipChainAssetCreator.h>
  13. #include <Atom/RPI.Reflect/Image/StreamingImageAssetCreator.h>
  14. #include <Atom/RHI/Factory.h>
  15. #include <AtomCore/Instance/InstanceDatabase.h>
  16. // Enable this define to debug output streaming image initialization and expanding process.
  17. //#define AZ_RPI_STREAMING_IMAGE_DEBUG_LOG
  18. AZ_DECLARE_BUDGET(RPI);
  19. namespace AZ
  20. {
  21. namespace RPI
  22. {
  23. Data::Instance<StreamingImage> StreamingImage::FindOrCreate(const Data::Asset<StreamingImageAsset>& streamingImageAsset)
  24. {
  25. return Data::InstanceDatabase<StreamingImage>::Instance().FindOrCreate(
  26. Data::InstanceId::CreateFromAsset(streamingImageAsset), streamingImageAsset);
  27. }
  28. Data::Instance<StreamingImage> StreamingImage::CreateFromCpuData(
  29. const StreamingImagePool& streamingImagePool,
  30. RHI::ImageDimension imageDimension,
  31. RHI::Size imageSize,
  32. RHI::Format imageFormat,
  33. const void* imageData,
  34. size_t imageDataSize,
  35. Uuid id)
  36. {
  37. const Data::InstanceId instanceId = Data::InstanceId::CreateUuid(id);
  38. Data::Instance<StreamingImage> existingImage = Data::InstanceDatabase<StreamingImage>::Instance().Find(instanceId);
  39. AZ_Error("StreamingImage", !existingImage, "StreamingImage::CreateFromCpuData found an existing entry in the instance database for the provided id.");
  40. RHI::ImageDescriptor imageDescriptor;
  41. imageDescriptor.m_bindFlags = RHI::ImageBindFlags::ShaderRead;
  42. imageDescriptor.m_dimension = imageDimension;
  43. imageDescriptor.m_size = imageSize;
  44. imageDescriptor.m_format = imageFormat;
  45. const RHI::ImageSubresourceLayout imageSubresourceLayout = RHI::GetImageSubresourceLayout(imageDescriptor, RHI::ImageSubresource{});
  46. const size_t expectedImageDataSize = imageSubresourceLayout.m_bytesPerImage * imageDescriptor.m_size.m_depth;
  47. if (expectedImageDataSize != imageDataSize)
  48. {
  49. AZ_Error("StreamingImage", false, "StreamingImage::CreateFromCpuData expected '%d' bytes of image data, but got '%d' instead.", expectedImageDataSize, imageDataSize);
  50. return nullptr;
  51. }
  52. Data::Asset<ImageMipChainAsset> mipChainAsset;
  53. // Construct the mip chain asset.
  54. {
  55. ImageMipChainAssetCreator assetCreator;
  56. assetCreator.Begin(Uuid::CreateRandom(), 1, 1);
  57. assetCreator.BeginMip(imageSubresourceLayout);
  58. assetCreator.AddSubImage(imageData, expectedImageDataSize);
  59. assetCreator.EndMip();
  60. if (!assetCreator.End(mipChainAsset))
  61. {
  62. AZ_Error("StreamingImage", false, "Failed to initialize mip chain asset");
  63. return nullptr;
  64. }
  65. }
  66. Data::Asset<StreamingImageAsset> streamingImageAsset;
  67. // Construct the streaming image asset.
  68. {
  69. StreamingImageAssetCreator assetCreator;
  70. assetCreator.Begin(id);
  71. assetCreator.SetImageDescriptor(imageDescriptor);
  72. assetCreator.AddMipChainAsset(*mipChainAsset.Get());
  73. assetCreator.SetFlags(StreamingImageFlags::NotStreamable);
  74. assetCreator.SetPoolAssetId(streamingImagePool.GetAssetId());
  75. if (!assetCreator.End(streamingImageAsset))
  76. {
  77. AZ_Error("StreamingImage", false, "Failed to initialize streaming image asset");
  78. return nullptr;
  79. }
  80. }
  81. return Data::InstanceDatabase<StreamingImage>::Instance().FindOrCreate(instanceId, streamingImageAsset);
  82. }
  83. Data::Instance<StreamingImage> StreamingImage::CreateInternal(StreamingImageAsset& streamingImageAsset)
  84. {
  85. Data::Instance<StreamingImage> streamingImage = aznew StreamingImage();
  86. const RHI::ResultCode resultCode = streamingImage->Init(streamingImageAsset);
  87. return resultCode == RHI::ResultCode::Success ? streamingImage : nullptr;
  88. }
  89. RHI::ResultCode StreamingImage::Init(StreamingImageAsset& imageAsset)
  90. {
  91. AZ_PROFILE_FUNCTION(RPI);
  92. Data::Instance<StreamingImagePool> pool;
  93. if (imageAsset.GetPoolAssetId().IsValid())
  94. {
  95. Data::Asset<RPI::StreamingImagePoolAsset> poolAsset(imageAsset.GetPoolAssetId(), AZ::AzTypeInfo<RPI::StreamingImagePoolAsset>::Uuid());
  96. pool = StreamingImagePool::FindOrCreate(poolAsset);
  97. }
  98. else
  99. {
  100. pool = ImageSystemInterface::Get()->GetSystemStreamingPool();
  101. }
  102. if (!pool)
  103. {
  104. AZ_Error("StreamingImage", false, "Failed to acquire the streaming image pool instance.");
  105. return RHI::ResultCode::Fail;
  106. }
  107. // Cache off the RHI streaming image pool instance.
  108. RHI::StreamingImagePool* rhiPool = pool->GetRHIPool();
  109. /**
  110. * NOTE: The tail mip-chain is required to exist as a dependency of this asset. This allows
  111. * the image to initialize with well-defined content.
  112. */
  113. const uint16_t mipChainTailIndex = static_cast<uint16_t>(imageAsset.GetMipChainCount() - 1);
  114. RHI::ResultCode resultCode = RHI::ResultCode::Success;
  115. const ImageMipChainAsset& mipChainTailAsset = imageAsset.GetTailMipChain();
  116. {
  117. RHI::StreamingImageInitRequest initRequest;
  118. initRequest.m_image = GetRHIImage();
  119. initRequest.m_descriptor = imageAsset.GetImageDescriptor();
  120. initRequest.m_tailMipSlices = mipChainTailAsset.GetMipSlices();
  121. // NOTE: Initialization can fail due to out-of-memory errors. Need to handle it at runtime.
  122. resultCode = rhiPool->InitImage(initRequest);
  123. }
  124. if (resultCode == RHI::ResultCode::Success)
  125. {
  126. // Set rhi image name
  127. m_imageAsset = { &imageAsset, AZ::Data::AssetLoadBehavior::PreLoad };
  128. m_image->SetName(Name(m_imageAsset.GetHint()));
  129. m_imageView = m_image->GetImageView(imageAsset.GetImageViewDescriptor());
  130. if(!m_imageView.get())
  131. {
  132. AZ_Error("Image", false, "Failed to initialize RHI image view. This is not a recoverable error and is likely a bug.");
  133. return RHI::ResultCode::Fail;
  134. }
  135. // Build a local set of mip chain asset handles.
  136. for (size_t mipChainIndex = 0; mipChainIndex < imageAsset.GetMipChainCount(); ++mipChainIndex)
  137. {
  138. const Data::AssetId& assetId = imageAsset.GetMipChainAsset(mipChainIndex).GetId();
  139. // We want to store off the id, not the AssetData instance. This simplifies the fetch / evict logic which
  140. // can do more strict assertions.
  141. m_mipChains.push_back(Data::Asset<ImageMipChainAsset>(assetId, azrtti_typeid<ImageMipChainAsset>()));
  142. }
  143. // Initialize the streaming state to have the tail mip active and ready.
  144. m_mipChainState.m_residencyTarget = mipChainTailIndex;
  145. m_mipChainState.m_streamingTarget = mipChainTailIndex;
  146. // Setup masks for tail mip chain
  147. const uint16_t mipChainBit = static_cast<uint16_t>(1 << mipChainTailIndex);
  148. m_mipChainState.m_maskActive |= mipChainBit;
  149. m_mipChainState.m_maskEvictable &= ~mipChainBit;
  150. m_mipChainState.m_maskReady |= mipChainBit;
  151. // Take references on dependent assets
  152. m_rhiPool = rhiPool;
  153. m_pool = pool;
  154. m_pool->AttachImage(this);
  155. // queue expand mipmaps if it's not managed by the streaming controller
  156. if (!m_streamingController)
  157. {
  158. QueueExpandToMipChainLevel(0);
  159. }
  160. #ifdef AZ_RPI_STREAMING_IMAGE_DEBUG_LOG
  161. AZ_TracePrintf("StreamingImage", "Init image [%s]\n", m_image->GetName().GetCStr());
  162. #endif
  163. #if defined (AZ_RPI_STREAMING_IMAGE_HOT_RELOADING)
  164. BusConnect(imageAsset.GetId());
  165. #endif
  166. return RHI::ResultCode::Success;
  167. }
  168. AZ_Warning("StreamingImagePool", false, "Failed to initialize RHI::Image on RHI::StreamingImagePool.");
  169. return resultCode;
  170. }
  171. void StreamingImage::Shutdown()
  172. {
  173. if (IsInitialized())
  174. {
  175. #if defined (AZ_RPI_STREAMING_IMAGE_HOT_RELOADING)
  176. Data::AssetBus::MultiHandler::BusDisconnect(GetAssetId());
  177. #endif
  178. if(m_pool)
  179. {
  180. m_pool->DetachImage(this);
  181. m_pool = nullptr;
  182. }
  183. m_rhiPool = nullptr;
  184. GetRHIImage()->Shutdown();
  185. // Evict all active mip chains
  186. for (size_t mipChainIndex = 0; mipChainIndex < m_mipChains.size(); ++mipChainIndex)
  187. {
  188. EvictMipChainAsset(mipChainIndex);
  189. }
  190. m_mipChains.clear();
  191. m_mipChainState = {};
  192. }
  193. }
  194. StreamingImage::~StreamingImage()
  195. {
  196. Shutdown();
  197. }
  198. void StreamingImage::SetTargetMip(uint16_t targetMipLevel)
  199. {
  200. if (m_streamingController)
  201. {
  202. // find the mipchain which contains the target mip
  203. size_t mipChainIndex = m_imageAsset->GetMipChainIndex(targetMipLevel);
  204. // adjust target mip level to the highest detailed mip of the mipchain
  205. size_t clampedMipLevel = m_imageAsset->GetMipLevel(mipChainIndex);
  206. m_streamingController->OnSetTargetMip(this, aznumeric_cast<uint16_t>(clampedMipLevel));
  207. }
  208. }
  209. uint16_t StreamingImage::GetResidentMipLevel()
  210. {
  211. return static_cast<uint16_t>(m_image->GetResidentMipLevel());
  212. }
  213. Color StreamingImage::GetAverageColor() const
  214. {
  215. return m_imageAsset->GetAverageColor();
  216. }
  217. StreamingImage::Priority StreamingImage::GetStreamingPriority() const
  218. {
  219. return m_streamingPriority;
  220. }
  221. void StreamingImage::SetStreamingPriority(Priority priority)
  222. {
  223. m_streamingPriority = priority;
  224. }
  225. bool StreamingImage::IsTrimmable() const
  226. {
  227. // the streaming image is trimmable when it has more mipchains other than the tail mipchain (the last mipchain)
  228. return IsStreamable() && m_mipChainState.m_streamingTarget < m_mipChains.size()-1;
  229. }
  230. RHI::ResultCode StreamingImage::TrimOneMipChain()
  231. {
  232. return TrimToMipChainLevel(m_mipChainState.m_streamingTarget + 1);
  233. }
  234. RHI::ResultCode StreamingImage::TrimToMipChainLevel(size_t mipChainIndex)
  235. {
  236. AZ_Assert(mipChainIndex < m_mipChains.size(), "Exceeded number of mip chains.");
  237. const size_t mipChainBegin = m_mipChainState.m_streamingTarget;
  238. const size_t mipChainEnd = mipChainIndex;
  239. RHI::ResultCode resultCode = RHI::ResultCode::Success;
  240. // We only evict if the current target is higher detail than our requested target.
  241. if (mipChainBegin < mipChainEnd)
  242. {
  243. const uint32_t mipLevel = static_cast<uint32_t>(m_imageAsset->GetMipLevel(mipChainEnd));
  244. resultCode = m_rhiPool->TrimImage(*m_image.get(), mipLevel);
  245. // Start from the most detailed chain, evict all in-flight or loaded assets.
  246. // Note: this should only happened after TrimImage which all the possible backend asset data referencing were removed
  247. for (size_t chainIdx = mipChainBegin; chainIdx < mipChainEnd; ++chainIdx)
  248. {
  249. EvictMipChainAsset(chainIdx);
  250. }
  251. // Reset tracked state to match the new target.
  252. m_mipChainState.m_residencyTarget = static_cast<uint16_t>(mipChainEnd);
  253. m_mipChainState.m_streamingTarget = static_cast<uint16_t>(mipChainEnd);
  254. }
  255. return resultCode;
  256. }
  257. void StreamingImage::QueueExpandToMipChainLevel(size_t mipChainIndex)
  258. {
  259. AZ_Assert(mipChainIndex < m_mipChains.size(), "Exceeded number of mip chains.");
  260. // Expand operation - queue streaming of mip chains up to target mip chain index.
  261. if (m_mipChainState.m_streamingTarget > mipChainIndex)
  262. {
  263. // Start on the next-detailed chain from the streaming target.
  264. const size_t mipChainBegin = m_mipChainState.m_streamingTarget - 1;
  265. // The streaming target need to set before fetching mip chain asset since it's possible the
  266. // asset is ready when fetching which may trigger expanding directly
  267. m_mipChainState.m_streamingTarget = static_cast<uint16_t>(mipChainIndex);
  268. // Iterate through to the end chain and queue loading operations on the mip assets.
  269. size_t offset = mipChainBegin - mipChainIndex;
  270. for (size_t i = 0; i<=offset; i++)
  271. {
  272. FetchMipChainAsset(mipChainBegin - i);
  273. }
  274. }
  275. }
  276. void StreamingImage::QueueExpandToNextMipChainLevel()
  277. {
  278. // Return if already reach the end
  279. if (m_mipChainState.m_streamingTarget == 0)
  280. {
  281. return;
  282. }
  283. QueueExpandToMipChainLevel(m_mipChainState.m_streamingTarget - 1);
  284. }
  285. void StreamingImage::CancelExpanding()
  286. {
  287. TrimToMipChainLevel(m_mipChainState.m_residencyTarget);
  288. }
  289. RHI::ResultCode StreamingImage::ExpandMipChain()
  290. {
  291. AZ_Assert(m_mipChainState.m_streamingTarget <= m_mipChainState.m_residencyTarget, "The target mip chain cannot be less detailed than the resident mip chain.")
  292. RHI::ResultCode resultCode = RHI::ResultCode::Success;
  293. if (m_mipChainState.m_streamingTarget < m_mipChainState.m_residencyTarget)
  294. {
  295. #ifdef AZ_RPI_STREAMING_IMAGE_DEBUG_LOG
  296. AZ_TracePrintf("StreamingImage", "Expand image [%s]\n", m_image->GetName().GetCStr());
  297. #endif
  298. // Start by assuming we can expand residency to the full target range.
  299. uint16_t mipChainIndexFound = m_mipChainState.m_streamingTarget;
  300. // Walk the mip chains from most to least detailed, and track the latest instance
  301. // of an unloaded mip chain. This incrementally shortens the interval.
  302. for (uint16_t i = m_mipChainState.m_streamingTarget; i < m_mipChainState.m_residencyTarget; ++i)
  303. {
  304. // Can't expand to this chain, select the next one as the candidate.
  305. if (!IsMipChainAssetReady(i))
  306. {
  307. mipChainIndexFound = i + 1;
  308. }
  309. }
  310. // If we found a range of loaded mip chains, upload them from the low level mipchain to high level mipchain
  311. // which the index should be from higher value to lower value
  312. if (mipChainIndexFound != m_mipChainState.m_residencyTarget)
  313. {
  314. for (uint16_t mipChainIndex = m_mipChainState.m_residencyTarget-1;
  315. mipChainIndex >= mipChainIndexFound && resultCode == RHI::ResultCode::Success;
  316. mipChainIndex--)
  317. {
  318. resultCode = UploadMipChain(mipChainIndex);
  319. if (mipChainIndex == 0)
  320. {
  321. break;
  322. }
  323. }
  324. m_mipChainState.m_residencyTarget = mipChainIndexFound;
  325. }
  326. }
  327. return resultCode;
  328. }
  329. void StreamingImage::EvictMipChainAsset(size_t mipChainIndex)
  330. {
  331. AZ_Assert(mipChainIndex < m_mipChains.size(), "Exceeded total number of mip chains.");
  332. const uint16_t mipChainBit = static_cast<uint16_t>(1 << mipChainIndex);
  333. const bool isMipChainActive = RHI::CheckBitsAll(m_mipChainState.m_maskActive, mipChainBit);
  334. const bool isMipChainEvictable = RHI::CheckBitsAll(m_mipChainState.m_maskEvictable, mipChainBit);
  335. if (isMipChainActive && isMipChainEvictable)
  336. {
  337. const uint32_t mipChainMask = ~mipChainBit;
  338. m_mipChainState.m_maskActive &= mipChainMask;
  339. m_mipChainState.m_maskReady &= mipChainMask;
  340. Data::Asset<ImageMipChainAsset>& mipChainAsset = m_mipChains[mipChainIndex];
  341. AZ_Assert(mipChainAsset.GetStatus() != Data::AssetData::AssetStatus::NotLoaded, "Asset marked as active, but mipChainAsset in 'NotLoaded' state.");
  342. Data::AssetBus::MultiHandler::BusDisconnect(mipChainAsset.GetId());
  343. mipChainAsset.Release();
  344. }
  345. }
  346. void StreamingImage::FetchMipChainAsset(size_t mipChainIndex)
  347. {
  348. AZ_Assert(mipChainIndex < m_mipChains.size(), "Exceeded total number of mip chains.");
  349. const uint16_t mipChainBit = static_cast<uint16_t>(1 << mipChainIndex);
  350. const bool isMipChainActive = RHI::CheckBitsAll(m_mipChainState.m_maskActive, mipChainBit);
  351. if (!isMipChainActive)
  352. {
  353. m_mipChainState.m_maskActive |= mipChainBit;
  354. Data::Asset<ImageMipChainAsset>& mipChainAsset = m_mipChains[mipChainIndex];
  355. AZ_Assert(mipChainAsset.Get() == nullptr, "Asset marked as inactive, but has a valid reference.");
  356. // And we request that the asset be loaded in case it isn't already.
  357. mipChainAsset.QueueLoad();
  358. // Connect to the AssetBus so we are ready to receive OnAssetReady(), which will call OnMipChainAssetReady().
  359. // If the asset happens to already be loaded, OnAssetReady() will be called immediately.
  360. // Note: call BusConnect after QueueLoad() so that OnAssetReady won't be called during QueueLoad() which the asset data in mipChainAsset wasn't ready
  361. Data::AssetBus::MultiHandler::BusConnect(mipChainAsset.GetId());
  362. #ifdef AZ_RPI_STREAMING_IMAGE_DEBUG_LOG
  363. AZ_TracePrintf("StreamingImage", "Fetch mip chain asset [%s]\n", mipChainAsset.GetHint().c_str());
  364. #endif
  365. }
  366. else
  367. {
  368. AZ_Assert(false, "FetchMipChainAsset called for a mip chain that was already active.");
  369. }
  370. }
  371. bool StreamingImage::IsMipChainAssetReady(size_t mipChainIndex) const
  372. {
  373. AZ_Assert(mipChainIndex < m_mipChains.size(), "Exceeded total number of mip chains.");
  374. return RHI::CheckBitsAny(m_mipChainState.m_maskReady, static_cast<uint16_t>(1 << mipChainIndex));
  375. }
  376. void StreamingImage::OnMipChainAssetReady(size_t mipChainIndex)
  377. {
  378. AZ_Assert(mipChainIndex < m_mipChains.size(), "Exceeded total number of mip chains.");
  379. const uint16_t mipChainBit = static_cast<uint16_t>(1 << mipChainIndex);
  380. AZ_Assert(RHI::CheckBitsAll(m_mipChainState.m_maskActive, mipChainBit), "Mip chain should be marked as active.");
  381. m_mipChainState.m_maskReady |= mipChainBit;
  382. if (m_streamingController)
  383. {
  384. m_streamingController->OnMipChainAssetReady(this);
  385. }
  386. else
  387. {
  388. ExpandMipChain();
  389. }
  390. }
  391. RHI::ResultCode StreamingImage::UploadMipChain(size_t mipChainIndex)
  392. {
  393. if (const Data::Asset<ImageMipChainAsset>& mipChainAsset = m_mipChains[mipChainIndex])
  394. {
  395. const auto& mipSlices = mipChainAsset->GetMipSlices();
  396. RHI::StreamingImageExpandRequest request;
  397. request.m_image = GetRHIImage();
  398. request.m_mipSlices = mipSlices;
  399. request.m_completeCallback = [=]()
  400. {
  401. #ifdef AZ_RPI_STREAMING_IMAGE_DEBUG_LOG
  402. AZ_TracePrintf("StreamingImage", "Upload mipchain done [%s]\n", mipChainAsset.GetHint().c_str());
  403. #endif
  404. EvictMipChainAsset(mipChainIndex);
  405. };
  406. #ifdef AZ_RPI_STREAMING_IMAGE_DEBUG_LOG
  407. AZ_TracePrintf("StreamingImage", "Start Upload mipchain [%d] [%s] start [%d], resident [%d]\n", mipChainIndex,
  408. mipChainAsset.GetHint().c_str(), m_image->GetResidentMipLevel());
  409. #endif
  410. return m_rhiPool->ExpandImage(request);
  411. }
  412. return RHI::ResultCode::InvalidOperation;
  413. }
  414. void StreamingImage::OnAssetReady(Data::Asset<Data::AssetData> asset)
  415. {
  416. size_t mipChainIndex = 0;
  417. const size_t mipChainCount = m_mipChains.size();
  418. for (; mipChainIndex < mipChainCount; ++mipChainIndex)
  419. {
  420. if (m_mipChains[mipChainIndex] == asset)
  421. {
  422. #ifdef AZ_RPI_STREAMING_IMAGE_DEBUG_LOG
  423. AZ_TracePrintf("StreamingImage", "mip chain asset ready [%s]\n", asset.GetHint().c_str());
  424. #endif
  425. OnMipChainAssetReady(mipChainIndex);
  426. break;
  427. }
  428. }
  429. }
  430. void StreamingImage::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
  431. {
  432. #if defined (AZ_RPI_STREAMING_IMAGE_HOT_RELOADING)
  433. if (asset.GetId() == GetAssetId())
  434. {
  435. StreamingImageAsset* imageAsset = azrtti_cast<StreamingImageAsset*>(asset.GetData());
  436. // Re-initialize the image.
  437. Shutdown();
  438. [[maybe_unused]] RHI::ResultCode resultCode = Init(*imageAsset);
  439. AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to re-initialize streaming image");
  440. }
  441. #endif
  442. }
  443. const Data::Instance<StreamingImagePool>& StreamingImage::GetPool() const
  444. {
  445. return m_pool;
  446. }
  447. bool StreamingImage::IsStreamable() const
  448. {
  449. return (RHI::CheckBitsAny(m_imageAsset->GetFlags(), StreamingImageFlags::NotStreamable) == false) && m_image->IsStreamable();
  450. }
  451. bool StreamingImage::IsExpanding() const
  452. {
  453. return m_mipChainState.m_residencyTarget > m_mipChainState.m_streamingTarget;
  454. }
  455. bool StreamingImage::IsStreamed() const
  456. {
  457. if (m_streamingController)
  458. {
  459. return m_streamingController->GetImageTargetMip(this) >= m_image->GetResidentMipLevel();
  460. }
  461. return m_image->GetResidentMipLevel() == 0;
  462. }
  463. }
  464. }