3
0

ImageSystem.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  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/AttachmentImage.h>
  9. #include <Atom/RPI.Public/Image/AttachmentImagePool.h>
  10. #include <Atom/RPI.Public/Image/ImageSystem.h>
  11. #include <Atom/RPI.Public/Image/StreamingImage.h>
  12. #include <Atom/RPI.Public/Image/StreamingImagePool.h>
  13. #include <Atom/RPI.Reflect/Asset/AssetHandler.h>
  14. #include <Atom/RHI.Reflect/ImagePoolDescriptor.h>
  15. #include <Atom/RPI.Reflect/Image/AttachmentImageAssetCreator.h>
  16. #include <Atom/RPI.Reflect/Image/ImageMipChainAsset.h>
  17. #include <Atom/RPI.Reflect/Image/StreamingImageAssetHandler.h>
  18. #include <Atom/RPI.Reflect/Image/StreamingImagePoolAssetCreator.h>
  19. #include <Atom/RPI.Reflect/ResourcePoolAssetCreator.h>
  20. #include <Atom/RHI/ImagePool.h>
  21. #include <Atom/RHI/RHISystemInterface.h>
  22. #include <AtomCore/Instance/InstanceDatabase.h>
  23. #include <AzCore/Asset/AssetManager.h>
  24. #include <AzCore/Console/IConsole.h>
  25. #include <AzCore/Interface/Interface.h>
  26. #include <AzCore/Math/Color.h>
  27. #include <AzCore/Settings/SettingsRegistry.h>
  28. AZ_DECLARE_BUDGET(RPI);
  29. namespace AZ
  30. {
  31. namespace
  32. {
  33. const char* MemoryBudgetSettingPath = "/O3DE/Atom/RPI/Initialization/ImageSystemDescriptor/SystemStreamingImagePoolSize";
  34. const char* MipBiasSettingPath = "/O3DE/Atom/RPI/Initialization/ImageSystemDescriptor/SystemStreamingImagePoolMipBias";
  35. size_t cvar_r_streamingImagePoolBudgetMb_Init()
  36. {
  37. u64 value = 0;
  38. auto settingsRegistry = AZ::SettingsRegistry::Get();
  39. if (settingsRegistry)
  40. {
  41. settingsRegistry->Get(value, MemoryBudgetSettingPath);
  42. }
  43. return aznumeric_cast<size_t>(value);
  44. }
  45. int16_t cvar_r_streamingImageMipBias_Init()
  46. {
  47. s64 value = 0;
  48. auto settingsRegistry = AZ::SettingsRegistry::Get();
  49. if (settingsRegistry)
  50. {
  51. settingsRegistry->Get(value, MipBiasSettingPath);
  52. }
  53. return aznumeric_cast<int16_t>(value);
  54. }
  55. void cvar_r_streamingImagePoolBudgetMb_Changed(const size_t& value)
  56. {
  57. if (auto* imageSystem = RPI::ImageSystemInterface::Get())
  58. {
  59. Data::Instance<RPI::StreamingImagePool> pool = imageSystem->GetSystemStreamingPool();
  60. size_t newBudget = value * 1024 * 1024;
  61. [[maybe_unused]] bool success = pool->SetMemoryBudget(newBudget);
  62. AZ_Warning("StreamingImagePool", success, "Can't update StreamingImagePool's memory budget to %uM", value);
  63. }
  64. else
  65. {
  66. // Update setting registry value which is used for image system initialization
  67. if (auto settingsRegistry = AZ::SettingsRegistry::Get())
  68. {
  69. settingsRegistry->Set(MemoryBudgetSettingPath, aznumeric_cast<u64>(value));
  70. }
  71. }
  72. }
  73. void cvar_r_streamingImageMipBias_Changed(const int16_t& value)
  74. {
  75. if (auto* imageSystem = RPI::ImageSystemInterface::Get())
  76. {
  77. Data::Instance<RPI::StreamingImagePool> pool = imageSystem->GetSystemStreamingPool();
  78. pool->SetMipBias(value);
  79. }
  80. else
  81. {
  82. // Update setting registry value which is used for image system initialization
  83. if (auto settingsRegistry = AZ::SettingsRegistry::Get())
  84. {
  85. settingsRegistry->Set(MipBiasSettingPath, aznumeric_cast<s64>(value));
  86. }
  87. }
  88. }
  89. }
  90. // cvars for changing streaming image pool budget and setup mip bias of streaming controller
  91. AZ_CVAR(size_t, r_streamingImagePoolBudgetMb, cvar_r_streamingImagePoolBudgetMb_Init(), cvar_r_streamingImagePoolBudgetMb_Changed, ConsoleFunctorFlags::DontReplicate, "Change gpu memory budget for the RPI system streaming image pool");
  92. AZ_CVAR(int16_t, r_streamingImageMipBias, cvar_r_streamingImageMipBias_Init(), cvar_r_streamingImageMipBias_Changed, ConsoleFunctorFlags::DontReplicate, "Set a mipmap bias for all streamable images created from the system streaming image pool");
  93. namespace RPI
  94. {
  95. ImageSystemInterface* ImageSystemInterface::Get()
  96. {
  97. return Interface<ImageSystemInterface>::Get();
  98. }
  99. void ImageSystem::Reflect(AZ::ReflectContext* context)
  100. {
  101. ImageAsset::Reflect(context);
  102. ImageMipChainAsset::Reflect(context);
  103. ImageSystemDescriptor::Reflect(context);
  104. StreamingImageAsset::Reflect(context);
  105. StreamingImagePoolAsset::Reflect(context);
  106. StreamingImageControllerAsset::Reflect(context);
  107. AttachmentImageAsset::Reflect(context);
  108. }
  109. void ImageSystem::GetAssetHandlers(AssetHandlerPtrList& assetHandlers)
  110. {
  111. assetHandlers.emplace_back(MakeAssetHandler<ImageAssetHandler>());
  112. assetHandlers.emplace_back(MakeAssetHandler<AttachmentImageAssetHandler>());
  113. assetHandlers.emplace_back(MakeAssetHandler<ImageMipChainAssetHandler>());
  114. assetHandlers.emplace_back(MakeAssetHandler<StreamingImageAssetHandler>());
  115. assetHandlers.emplace_back(MakeAssetHandler<StreamingImagePoolAssetHandler>());
  116. }
  117. void ImageSystem::Init(const ImageSystemDescriptor& desc)
  118. {
  119. RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
  120. // Register attachment image instance database.
  121. {
  122. Data::InstanceHandler<AttachmentImage> handler;
  123. handler.m_createFunction = [](Data::AssetData* imageAsset)
  124. {
  125. return AttachmentImage::CreateInternal(*(azrtti_cast<AttachmentImageAsset*>(imageAsset)));
  126. };
  127. Data::InstanceDatabase<AttachmentImage>::Create(azrtti_typeid<AttachmentImageAsset>(), handler);
  128. }
  129. {
  130. Data::InstanceHandler<AttachmentImagePool> handler;
  131. handler.m_createFunction = [device](Data::AssetData* poolAsset)
  132. {
  133. return AttachmentImagePool::CreateInternal(*device, *(azrtti_cast<ResourcePoolAsset*>(poolAsset)));
  134. };
  135. Data::InstanceDatabase<AttachmentImagePool>::Create(azrtti_typeid<ResourcePoolAsset>(), handler);
  136. }
  137. // Register streaming image instance database.
  138. {
  139. Data::InstanceHandler<StreamingImage> handler;
  140. handler.m_createFunction = [](Data::AssetData* imageAsset)
  141. {
  142. return StreamingImage::CreateInternal(*(azrtti_cast<StreamingImageAsset*>(imageAsset)));
  143. };
  144. Data::InstanceDatabase<StreamingImage>::Create(azrtti_typeid<StreamingImageAsset>(), handler);
  145. }
  146. {
  147. Data::InstanceHandler<StreamingImagePool> handler;
  148. handler.m_createFunction = [this, device](Data::AssetData* poolAsset)
  149. {
  150. Data::Instance<StreamingImagePool> instance = StreamingImagePool::CreateInternal(*device, *(azrtti_cast<StreamingImagePoolAsset*>(poolAsset)));
  151. if (instance)
  152. {
  153. m_activeStreamingPoolMutex.lock();
  154. m_activeStreamingPools.emplace_back(instance.get());
  155. m_activeStreamingPoolMutex.unlock();
  156. }
  157. return instance;
  158. };
  159. handler.m_deleteFunction = [this](StreamingImagePool* pool)
  160. {
  161. m_activeStreamingPoolMutex.lock();
  162. auto findIt = AZStd::find(m_activeStreamingPools.begin(), m_activeStreamingPools.end(), pool);
  163. AZ_Assert(findIt != m_activeStreamingPools.end(), "Pool must exist in the container.");
  164. m_activeStreamingPools.erase(findIt);
  165. m_activeStreamingPoolMutex.unlock();
  166. delete pool;
  167. };
  168. Data::InstanceDatabase<StreamingImagePool>::Create(azrtti_typeid<StreamingImagePoolAsset>(), handler);
  169. }
  170. CreateDefaultResources(desc);
  171. Interface<ImageSystemInterface>::Register(this);
  172. m_initialized = true;
  173. }
  174. void ImageSystem::Shutdown()
  175. {
  176. if (!m_initialized)
  177. {
  178. return;
  179. }
  180. Interface<ImageSystemInterface>::Unregister(this);
  181. m_systemImages.clear();
  182. m_systemAttachmentImages.clear();
  183. m_systemStreamingPool = nullptr;
  184. m_systemAttachmentPool = nullptr;
  185. Data::InstanceDatabase<AttachmentImage>::Destroy();
  186. Data::InstanceDatabase<AttachmentImagePool>::Destroy();
  187. Data::InstanceDatabase<StreamingImage>::Destroy();
  188. Data::InstanceDatabase<StreamingImagePool>::Destroy();
  189. m_activeStreamingPools.clear();
  190. m_initialized = false;
  191. }
  192. void ImageSystem::Update()
  193. {
  194. AZ_PROFILE_SCOPE(RPI, "ImageSystem: Update");
  195. AZStd::lock_guard<AZStd::mutex> lock(m_activeStreamingPoolMutex);
  196. for (StreamingImagePool* imagePool : m_activeStreamingPools)
  197. {
  198. imagePool->Update();
  199. }
  200. }
  201. const Data::Instance<StreamingImagePool>& ImageSystem::GetSystemStreamingPool() const
  202. {
  203. return m_systemStreamingPool;
  204. }
  205. const Data::Instance<StreamingImagePool>& ImageSystem::GetStreamingPool() const
  206. {
  207. return GetSystemStreamingPool();
  208. }
  209. const Data::Instance<AttachmentImagePool>& ImageSystem::GetSystemAttachmentPool() const
  210. {
  211. return m_systemAttachmentPool;
  212. }
  213. const Data::Instance<Image>& ImageSystem::GetSystemImage(SystemImage simpleImage) const
  214. {
  215. return m_systemImages[static_cast<size_t>(simpleImage)];
  216. }
  217. const Data::Instance<AttachmentImage>& ImageSystem::GetSystemAttachmentImage(RHI::Format format)
  218. {
  219. {
  220. AZStd::shared_lock<AZStd::shared_mutex> lock(m_systemAttachmentImagesUpdateMutex);
  221. auto it = m_systemAttachmentImages.find(format);
  222. if (it != m_systemAttachmentImages.end())
  223. {
  224. return it->second;
  225. }
  226. }
  227. // Take a full lock while the map is updated.
  228. AZStd::lock_guard<AZStd::shared_mutex> lock(m_systemAttachmentImagesUpdateMutex);
  229. // Double check map in case another thread created an attachment image for this format while this
  230. // thread waited on the lock.
  231. auto it = m_systemAttachmentImages.find(format);
  232. if (it != m_systemAttachmentImages.end())
  233. {
  234. return it->second;
  235. }
  236. RHI::ImageBindFlags formatBindFlag = RHI::ImageBindFlags::Color | RHI::ImageBindFlags::ShaderReadWrite;
  237. switch (format)
  238. {
  239. case RHI::Format::D16_UNORM:
  240. case RHI::Format::D32_FLOAT:
  241. formatBindFlag = RHI::ImageBindFlags::Depth | RHI::ImageBindFlags::ShaderRead;
  242. break;
  243. case RHI::Format::D16_UNORM_S8_UINT:
  244. case RHI::Format::D24_UNORM_S8_UINT:
  245. case RHI::Format::D32_FLOAT_S8X24_UINT:
  246. formatBindFlag = RHI::ImageBindFlags::DepthStencil | RHI::ImageBindFlags::ShaderRead;
  247. break;
  248. }
  249. RHI::ImageDescriptor imageDescriptor;
  250. imageDescriptor.m_size = RHI::Size(1, 1, 1);
  251. imageDescriptor.m_format = format;
  252. imageDescriptor.m_arraySize = 1;
  253. imageDescriptor.m_bindFlags = formatBindFlag;
  254. imageDescriptor.m_sharedQueueMask = RHI::HardwareQueueClassMask::All;
  255. RPI::CreateAttachmentImageRequest createImageRequest;
  256. createImageRequest.m_imagePool = m_systemAttachmentPool.get();
  257. createImageRequest.m_imageDescriptor = imageDescriptor;
  258. createImageRequest.m_imageName = "SystemAttachmentImage";
  259. createImageRequest.m_isUniqueName = false;
  260. auto systemAttachmentImage = RPI::AttachmentImage::Create(createImageRequest);
  261. m_systemAttachmentImages[format] = systemAttachmentImage;
  262. return m_systemAttachmentImages[format];
  263. }
  264. bool ImageSystem::RegisterAttachmentImage(AttachmentImage* attachmentImage)
  265. {
  266. if (!attachmentImage)
  267. {
  268. return false;
  269. }
  270. auto itr = m_registeredAttachmentImages.find(attachmentImage->GetAttachmentId());
  271. if (itr != m_registeredAttachmentImages.end())
  272. {
  273. AZ_Assert(false, "AttachmangeImage with name '%s' was already registered", attachmentImage->GetAttachmentId().GetCStr());
  274. return false;
  275. }
  276. m_registeredAttachmentImages[attachmentImage->GetAttachmentId()] = attachmentImage;
  277. return true;
  278. }
  279. void ImageSystem::UnregisterAttachmentImage(AttachmentImage* attachmentImage)
  280. {
  281. if (!attachmentImage)
  282. {
  283. return;
  284. }
  285. auto itr = m_registeredAttachmentImages.find(attachmentImage->GetAttachmentId());
  286. if (itr != m_registeredAttachmentImages.end())
  287. {
  288. m_registeredAttachmentImages.erase(itr);
  289. }
  290. }
  291. Data::Instance<AttachmentImage> ImageSystem::FindRegisteredAttachmentImage(const Name& uniqueName) const
  292. {
  293. auto itr = m_registeredAttachmentImages.find(uniqueName);
  294. if (itr != m_registeredAttachmentImages.end())
  295. {
  296. return itr->second;
  297. }
  298. return nullptr;
  299. }
  300. void ImageSystem::CreateDefaultResources(const ImageSystemDescriptor& desc)
  301. {
  302. struct SystemImageDescriptor
  303. {
  304. SystemImageDescriptor(const Color color, const char* name)
  305. : m_color{color}
  306. , m_name{name}
  307. , m_assetId{Uuid::CreateName(name)}
  308. {}
  309. Color m_color;
  310. const char* m_name;
  311. Data::AssetId m_assetId;
  312. };
  313. const SystemImageDescriptor systemImageDescriptors[static_cast<uint32_t>(SystemImage::Count)] =
  314. {
  315. { Color(1.0f, 1.0f, 1.0f, 1.0f), "Image_White" },
  316. { Color(0.0f, 0.0f, 0.0f, 1.0f), "Image_Black" },
  317. { Color(0.5f, 0.5f, 0.5f, 1.0f), "Image_Grey" },
  318. { Color(1.0f, 0.0f, 1.0f, 1.0f), "Image_Magenta" }
  319. };
  320. static_assert(AZ_ARRAY_SIZE(systemImageDescriptors) == static_cast<size_t>(SystemImage::Count), "System image arrays do not match.");
  321. struct SystemImagePoolDescriptor
  322. {
  323. SystemImagePoolDescriptor(size_t budgetInBytes, const char* name)
  324. : m_budgetInBytes{budgetInBytes}
  325. , m_name{name}
  326. , m_assetId{Uuid::CreateName(name)}
  327. {}
  328. size_t m_budgetInBytes;
  329. const char* m_name;
  330. Data::AssetId m_assetId;
  331. };
  332. // Sync values from ImageSystemDescriptor back to the cvars
  333. // Note 1: we need the sync here because one instance of the cvars might be initialized early than setting registry,
  334. // so it can't be initialized properly. See cvar_r_streamingImagePoolBudgetMb_Init and cvar_r_streamingImageMipBias_Init
  335. // Note 2: we need to use PerformCommand instead of assign value directly because of this issue https://github.com/o3de/o3de/issues/5537
  336. AZ::IConsole* console = AZ::Interface<AZ::IConsole>::Get();
  337. if (console)
  338. {
  339. AZ::CVarFixedString commandString = AZ::CVarFixedString::format("r_streamingImagePoolBudgetMb %" PRIu64, desc.m_systemStreamingImagePoolSize);
  340. console->PerformCommand(commandString.c_str());
  341. commandString = AZ::CVarFixedString::format("r_streamingImageMipBias %" PRId16, desc.m_systemStreamingImagePoolMipBias);
  342. console->PerformCommand(commandString.c_str());
  343. }
  344. const SystemImagePoolDescriptor systemStreamingPoolDescriptor{ desc.m_systemStreamingImagePoolSize, "ImageSystem::SystemStreamingImagePool" };
  345. const SystemImagePoolDescriptor systemAttachmentPoolDescriptor{desc.m_systemAttachmentImagePoolSize, "ImageSystem::AttachmentImagePool" };
  346. // Create the system streaming pool
  347. {
  348. AZStd::unique_ptr<RHI::StreamingImagePoolDescriptor> imagePoolDescriptor = AZStd::make_unique<RHI::StreamingImagePoolDescriptor>();
  349. imagePoolDescriptor->m_budgetInBytes = systemStreamingPoolDescriptor.m_budgetInBytes;
  350. Data::Asset<StreamingImagePoolAsset> poolAsset;
  351. StreamingImagePoolAssetCreator poolAssetCreator;
  352. poolAssetCreator.Begin(systemStreamingPoolDescriptor.m_assetId);
  353. poolAssetCreator.SetPoolDescriptor(AZStd::move(imagePoolDescriptor));
  354. poolAssetCreator.SetPoolName(systemStreamingPoolDescriptor.m_name);
  355. [[maybe_unused]] const bool created = poolAssetCreator.End(poolAsset);
  356. AZ_Assert(created, "Failed to build streaming image pool");
  357. m_systemStreamingPool = StreamingImagePool::FindOrCreate(poolAsset);
  358. m_systemStreamingPool->SetMipBias(desc.m_systemStreamingImagePoolMipBias);
  359. }
  360. // Create the system attachment pool.
  361. {
  362. AZStd::unique_ptr<RHI::ImagePoolDescriptor> imagePoolDescriptor = AZStd::make_unique<RHI::ImagePoolDescriptor>();
  363. imagePoolDescriptor->m_budgetInBytes = systemAttachmentPoolDescriptor.m_budgetInBytes;
  364. imagePoolDescriptor->m_bindFlags =
  365. RHI::ImageBindFlags::ShaderRead | RHI::ImageBindFlags::ShaderWrite |
  366. RHI::ImageBindFlags::Color | RHI::ImageBindFlags::DepthStencil |
  367. RHI::ImageBindFlags::CopyRead | RHI::ImageBindFlags::CopyWrite;
  368. RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
  369. if (RHI::CheckBitsAll(device->GetFeatures().m_shadingRateTypeMask, RHI::ShadingRateTypeFlags::PerRegion))
  370. {
  371. imagePoolDescriptor->m_bindFlags |= RHI::ImageBindFlags::ShadingRate;
  372. }
  373. Data::Asset<ResourcePoolAsset> poolAsset;
  374. ResourcePoolAssetCreator poolAssetCreator;
  375. poolAssetCreator.Begin(systemAttachmentPoolDescriptor.m_assetId);
  376. poolAssetCreator.SetPoolDescriptor(AZStd::move(imagePoolDescriptor));
  377. poolAssetCreator.SetPoolName(systemAttachmentPoolDescriptor.m_name);
  378. [[maybe_unused]] const bool created = poolAssetCreator.End(poolAsset);
  379. AZ_Assert(created, "Failed to build attachment image pool");
  380. m_systemAttachmentPool = AttachmentImagePool::FindOrCreate(poolAsset);
  381. }
  382. // Create the set of system images.
  383. {
  384. const size_t systemImageCount = static_cast<size_t>(SystemImage::Count);
  385. m_systemImages.resize(systemImageCount);
  386. for (size_t imageIndex = 0; imageIndex < systemImageCount; imageIndex++)
  387. {
  388. const uint32_t colorU32 = systemImageDescriptors[imageIndex].m_color.ToU32();
  389. m_systemImages[imageIndex] = StreamingImage::CreateFromCpuData(
  390. *m_systemStreamingPool,
  391. RHI::ImageDimension::Image2D,
  392. RHI::Size{ 1, 1, 1 },
  393. RHI::Format::R8G8B8A8_UNORM_SRGB,
  394. &colorU32,
  395. sizeof(uint32_t));
  396. }
  397. }
  398. }
  399. } // namespace RPI
  400. }// namespace AZ