StreamingImage.cpp 24 KB

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