MultiDeviceImageTests.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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 "RHITestFixture.h"
  9. #include <Atom/RHI/ImagePool.h>
  10. #include <Atom/RHI/ResourceInvalidateBus.h>
  11. #include <Tests/Device.h>
  12. namespace UnitTest
  13. {
  14. using namespace AZ;
  15. class MultiDeviceImageTests : public MultiDeviceRHITestFixture
  16. {
  17. public:
  18. MultiDeviceImageTests()
  19. : MultiDeviceRHITestFixture()
  20. {}
  21. void SetUp() override
  22. {
  23. MultiDeviceRHITestFixture::SetUp();
  24. }
  25. void TearDown() override
  26. {
  27. MultiDeviceRHITestFixture::TearDown();
  28. }
  29. };
  30. TEST_F(MultiDeviceImageTests, TestNoop)
  31. {
  32. RHI::Ptr<RHI::Image> noopImage;
  33. noopImage = aznew RHI::Image;
  34. }
  35. TEST_F(MultiDeviceImageTests, TestAll)
  36. {
  37. RHI::Ptr<RHI::Image> imageA;
  38. imageA = aznew RHI::Image;
  39. imageA->SetName(Name("ImageA"));
  40. ASSERT_TRUE(imageA->GetName().GetStringView() == "ImageA");
  41. ASSERT_TRUE(imageA->use_count() == 1);
  42. {
  43. RHI::Ptr<RHI::Image> imageB;
  44. imageB = aznew RHI::Image;
  45. ASSERT_TRUE(imageB->use_count() == 1);
  46. RHI::Ptr<RHI::ImagePool> imagePool;
  47. imagePool = aznew RHI::ImagePool;
  48. ASSERT_TRUE(imagePool->use_count() == 1);
  49. RHI::ImagePoolDescriptor imagePoolDesc;
  50. imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::Color;
  51. imagePool->Init(DeviceMask, imagePoolDesc);
  52. ASSERT_TRUE(imageA->IsInitialized() == false);
  53. ASSERT_TRUE(imageB->IsInitialized() == false);
  54. RHI::ImageInitRequest initRequest;
  55. initRequest.m_image = imageA.get();
  56. initRequest.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::Color, 16, 16, RHI::Format::R8G8B8A8_UNORM_SRGB);
  57. imagePool->InitImage(initRequest);
  58. ASSERT_TRUE(imageA->use_count() == 1);
  59. for(auto deviceIndex{0}; deviceIndex < DeviceCount; ++deviceIndex)
  60. {
  61. RHI::Ptr<RHI::DeviceImageView> imageView;
  62. imageView = imageA->GetDeviceImage(deviceIndex)->GetImageView(RHI::ImageViewDescriptor(RHI::Format::R8G8B8A8_UINT));
  63. AZ_TEST_ASSERT(imageView->IsStale() == false);
  64. ASSERT_TRUE(imageView->IsInitialized());
  65. ASSERT_TRUE(imageA->GetDeviceImage(deviceIndex)->use_count() == 3);
  66. }
  67. ASSERT_TRUE(imageA->use_count() == 1);
  68. ASSERT_TRUE(imageA->IsInitialized());
  69. initRequest.m_image = imageB.get();
  70. initRequest.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::Color, 8, 8, RHI::Format::R8G8B8A8_UNORM_SRGB);
  71. imagePool->InitImage(initRequest);
  72. ASSERT_TRUE(imageB->IsInitialized());
  73. ASSERT_TRUE(imageA->GetPool() == imagePool.get());
  74. ASSERT_TRUE(imageB->GetPool() == imagePool.get());
  75. ASSERT_TRUE(imagePool->GetResourceCount() == 2);
  76. {
  77. uint32_t imageIndex = 0;
  78. const RHI::Image* images[] =
  79. {
  80. imageA.get(),
  81. imageB.get()
  82. };
  83. imagePool->ForEach<RHI::Image>([&imageIndex, &images]([[maybe_unused]] const RHI::Image& image)
  84. {
  85. AZ_UNUSED(images); // Prevent unused warning in release builds
  86. AZ_Assert(images[imageIndex] == &image, "images don't match");
  87. imageIndex++;
  88. });
  89. }
  90. imageB->Shutdown();
  91. ASSERT_TRUE(imageB->GetPool() == nullptr);
  92. RHI::Ptr<RHI::ImagePool> imagePoolB;
  93. imagePoolB = aznew RHI::ImagePool;
  94. imagePoolB->Init(DeviceMask, imagePoolDesc);
  95. initRequest.m_image = imageB.get();
  96. initRequest.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::Color, 8, 8, RHI::Format::R8G8B8A8_UNORM_SRGB);
  97. imagePoolB->InitImage(initRequest);
  98. ASSERT_TRUE(imageB->GetPool() == imagePoolB.get());
  99. //Since we are switching imagePools for imageB it adds a refcount and invalidates the views.
  100. //We need this to ensure the views are fully invalidated in order to release the refcount and avoid a leak.
  101. RHI::ResourceInvalidateBus::ExecuteQueuedEvents();
  102. imagePoolB->Shutdown();
  103. ASSERT_TRUE(imagePoolB->GetResourceCount() == 0);
  104. }
  105. ASSERT_TRUE(imageA->GetPool() == nullptr);
  106. ASSERT_TRUE(imageA->use_count() == 1);
  107. }
  108. TEST_F(MultiDeviceImageTests, TestViews)
  109. {
  110. RHI::Ptr<RHI::DeviceImageView> imageViewA;
  111. {
  112. RHI::Ptr<RHI::ImagePool> imagePool;
  113. imagePool = aznew RHI::ImagePool;
  114. RHI::ImagePoolDescriptor imagePoolDesc;
  115. imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::Color;
  116. imagePool->Init(DeviceMask, imagePoolDesc);
  117. RHI::Ptr<RHI::Image> image;
  118. image = aznew RHI::Image;
  119. RHI::ImageInitRequest initRequest;
  120. initRequest.m_image = image.get();
  121. initRequest.m_descriptor = RHI::ImageDescriptor::Create2DArray(RHI::ImageBindFlags::Color, 8, 8, 2, RHI::Format::R8G8B8A8_UNORM_SRGB);
  122. imagePool->InitImage(initRequest);
  123. // Should report initialized and not stale.
  124. for(auto deviceIndex{0}; deviceIndex < DeviceCount; ++deviceIndex)
  125. {
  126. imageViewA = image->GetDeviceImage(deviceIndex)->GetImageView(RHI::ImageViewDescriptor{});
  127. AZ_TEST_ASSERT(imageViewA->IsInitialized());
  128. AZ_TEST_ASSERT(imageViewA->IsStale() == false);
  129. AZ_TEST_ASSERT(imageViewA->IsFullView());
  130. }
  131. // Should report as still initialized and also stale.
  132. image->Shutdown();
  133. for(auto deviceIndex{0}; deviceIndex < DeviceCount; ++deviceIndex)
  134. {
  135. AZ_TEST_ASSERT(imageViewA->IsStale());
  136. AZ_TEST_ASSERT(imageViewA->IsInitialized());
  137. }
  138. // Should *still* report as stale since resource invalidation events are queued.
  139. imagePool->InitImage(initRequest);
  140. for(auto deviceIndex{0}; deviceIndex < DeviceCount; ++deviceIndex)
  141. {
  142. AZ_TEST_ASSERT(imageViewA->IsStale());
  143. AZ_TEST_ASSERT(imageViewA->IsInitialized());
  144. }
  145. // This should re-initialize the views.
  146. RHI::ResourceInvalidateBus::ExecuteQueuedEvents();
  147. for(auto deviceIndex{0}; deviceIndex < DeviceCount; ++deviceIndex)
  148. {
  149. AZ_TEST_ASSERT(imageViewA->IsInitialized());
  150. AZ_TEST_ASSERT(imageViewA->IsStale() == false);
  151. }
  152. // Explicit invalidation should mark it stale.
  153. image->InvalidateViews();
  154. for(auto deviceIndex{0}; deviceIndex < DeviceCount; ++deviceIndex)
  155. {
  156. AZ_TEST_ASSERT(imageViewA->IsStale());
  157. AZ_TEST_ASSERT(imageViewA->IsInitialized());
  158. }
  159. // This should re-initialize the views.
  160. RHI::ResourceInvalidateBus::ExecuteQueuedEvents();
  161. for(auto deviceIndex{0}; deviceIndex < DeviceCount; ++deviceIndex)
  162. {
  163. AZ_TEST_ASSERT(imageViewA->IsInitialized());
  164. AZ_TEST_ASSERT(imageViewA->IsStale() == false);
  165. }
  166. // Test re-initialization.
  167. RHI::ImageViewDescriptor imageViewDesc = RHI::ImageViewDescriptor::Create(RHI::Format::Unknown, 0, 0, 0, 0);
  168. for(auto deviceIndex{0}; deviceIndex < DeviceCount; ++deviceIndex)
  169. {
  170. imageViewA = image->GetDeviceImage(deviceIndex)->GetImageView(imageViewDesc);
  171. AZ_TEST_ASSERT(imageViewA->IsFullView() == false);
  172. AZ_TEST_ASSERT(imageViewA->IsInitialized());
  173. AZ_TEST_ASSERT(imageViewA->IsStale() == false);
  174. }
  175. // Test re-initialization.
  176. imageViewDesc = RHI::ImageViewDescriptor::Create(RHI::Format::Unknown, 0, 0, 0, 1);
  177. for(auto deviceIndex{0}; deviceIndex < DeviceCount; ++deviceIndex)
  178. {
  179. imageViewA = image->GetDeviceImage(deviceIndex)->GetImageView(imageViewDesc);
  180. AZ_TEST_ASSERT(imageViewA->IsFullView());
  181. AZ_TEST_ASSERT(imageViewA->IsInitialized());
  182. AZ_TEST_ASSERT(imageViewA->IsStale() == false);
  183. }
  184. }
  185. // The parent image was shut down. This should report as being stale.
  186. AZ_TEST_ASSERT(imageViewA->IsStale());
  187. }
  188. struct MultiDeviceImageAndViewBindFlags
  189. {
  190. RHI::ImageBindFlags imageBindFlags;
  191. RHI::ImageBindFlags viewBindFlags;
  192. };
  193. class MultiDeviceImageBindFlagTests
  194. : public MultiDeviceImageTests
  195. , public ::testing::WithParamInterface <MultiDeviceImageAndViewBindFlags>
  196. {
  197. public:
  198. void SetUp() override
  199. {
  200. MultiDeviceImageTests::SetUp();
  201. // Create a pool and image with the image bind flags from the parameterized test
  202. m_imagePool = aznew RHI::ImagePool;
  203. RHI::ImagePoolDescriptor imagePoolDesc;
  204. imagePoolDesc.m_bindFlags = GetParam().imageBindFlags;
  205. m_imagePool->Init(DeviceMask, imagePoolDesc);
  206. RHI::ImageDescriptor imageDescriptor;
  207. imageDescriptor.m_bindFlags = GetParam().imageBindFlags;
  208. m_image = aznew RHI::Image;
  209. RHI::ImageInitRequest initRequest;
  210. initRequest.m_image = m_image.get();
  211. initRequest.m_descriptor = imageDescriptor;
  212. m_imagePool->InitImage(initRequest);
  213. }
  214. void TearDown() override
  215. {
  216. m_imagePool.reset();
  217. m_image.reset();
  218. m_imageView.reset();
  219. MultiDeviceImageTests::TearDown();
  220. }
  221. RHI::Ptr<RHI::ImagePool> m_imagePool;
  222. RHI::Ptr<RHI::Image> m_image;
  223. RHI::Ptr<RHI::DeviceImageView> m_imageView;
  224. };
  225. TEST_P(MultiDeviceImageBindFlagTests, InitView_ViewIsCreated)
  226. {
  227. RHI::ImageViewDescriptor imageViewDescriptor;
  228. imageViewDescriptor.m_overrideBindFlags = GetParam().viewBindFlags;
  229. for(auto deviceIndex{0}; deviceIndex < DeviceCount; ++deviceIndex)
  230. {
  231. m_imageView = m_image->GetDeviceImage(deviceIndex)->GetImageView(imageViewDescriptor);
  232. EXPECT_EQ(m_imageView.get()!=nullptr, true);
  233. }
  234. }
  235. // This test fixture is the same as MultiDeviceImageBindFlagTests, but exists separately so that
  236. // we can instantiate different test cases that are expected to fail
  237. class MultiDeviceImageBindFlagFailureCases
  238. : public MultiDeviceImageBindFlagTests
  239. {
  240. };
  241. TEST_P(MultiDeviceImageBindFlagFailureCases, InitView_ViewIsNotCreated)
  242. {
  243. RHI::ImageViewDescriptor imageViewDescriptor;
  244. imageViewDescriptor.m_overrideBindFlags = GetParam().viewBindFlags;
  245. for(auto deviceIndex{0}; deviceIndex < DeviceCount; ++deviceIndex)
  246. {
  247. m_imageView = m_image->GetDeviceImage(deviceIndex)->GetImageView(imageViewDescriptor);
  248. EXPECT_EQ(m_imageView.get()==nullptr, true);
  249. }
  250. }
  251. // These combinations should result in a successful creation of the image view
  252. std::vector<MultiDeviceImageAndViewBindFlags> GenerateCompatibleMultiDeviceImageBindFlagCombinations()
  253. {
  254. std::vector<MultiDeviceImageAndViewBindFlags> testCases;
  255. MultiDeviceImageAndViewBindFlags flags;
  256. // When the image bind flags are equal to or a superset of the image view bind flags, the view is compatible with the image
  257. flags.imageBindFlags = RHI::ImageBindFlags::Color;
  258. flags.viewBindFlags = RHI::ImageBindFlags::Color;
  259. testCases.push_back(flags);
  260. flags.imageBindFlags = RHI::ImageBindFlags::ShaderReadWrite;
  261. flags.viewBindFlags = RHI::ImageBindFlags::ShaderRead;
  262. testCases.push_back(flags);
  263. flags.imageBindFlags = RHI::ImageBindFlags::ShaderReadWrite;
  264. flags.viewBindFlags = RHI::ImageBindFlags::ShaderWrite;
  265. testCases.push_back(flags);
  266. flags.imageBindFlags = RHI::ImageBindFlags::ShaderReadWrite;
  267. flags.viewBindFlags = RHI::ImageBindFlags::ShaderReadWrite;
  268. testCases.push_back(flags);
  269. flags.imageBindFlags = RHI::ImageBindFlags::ShaderRead;
  270. flags.viewBindFlags = RHI::ImageBindFlags::ShaderRead;
  271. testCases.push_back(flags);
  272. flags.imageBindFlags = RHI::ImageBindFlags::ShaderWrite;
  273. flags.viewBindFlags = RHI::ImageBindFlags::ShaderWrite;
  274. testCases.push_back(flags);
  275. // When the image view bind flags are None, they have no effect and should work with any bind flag used by the image
  276. flags.imageBindFlags = RHI::ImageBindFlags::ShaderRead;
  277. flags.viewBindFlags = RHI::ImageBindFlags::None;
  278. testCases.push_back(flags);
  279. flags.imageBindFlags = RHI::ImageBindFlags::ShaderWrite;
  280. flags.viewBindFlags = RHI::ImageBindFlags::None;
  281. testCases.push_back(flags);
  282. flags.imageBindFlags = RHI::ImageBindFlags::ShaderReadWrite;
  283. flags.viewBindFlags = RHI::ImageBindFlags::None;
  284. testCases.push_back(flags);
  285. flags.imageBindFlags = RHI::ImageBindFlags::None;
  286. flags.viewBindFlags = RHI::ImageBindFlags::None;
  287. testCases.push_back(flags);
  288. flags.imageBindFlags = RHI::ImageBindFlags::Color;
  289. flags.viewBindFlags = RHI::ImageBindFlags::None;
  290. testCases.push_back(flags);
  291. return testCases;
  292. };
  293. // These combinations should fail during ImageView::Init
  294. std::vector<MultiDeviceImageAndViewBindFlags> GenerateIncompatibleMultiDeviceImageBindFlagCombinations()
  295. {
  296. std::vector<MultiDeviceImageAndViewBindFlags> testCases;
  297. MultiDeviceImageAndViewBindFlags flags;
  298. flags.imageBindFlags = RHI::ImageBindFlags::Color;
  299. flags.viewBindFlags = RHI::ImageBindFlags::ShaderRead;
  300. testCases.push_back(flags);
  301. flags.imageBindFlags = RHI::ImageBindFlags::ShaderRead;
  302. flags.viewBindFlags = RHI::ImageBindFlags::ShaderWrite;
  303. testCases.push_back(flags);
  304. flags.imageBindFlags = RHI::ImageBindFlags::ShaderRead;
  305. flags.viewBindFlags = RHI::ImageBindFlags::ShaderReadWrite;
  306. testCases.push_back(flags);
  307. flags.imageBindFlags = RHI::ImageBindFlags::ShaderWrite;
  308. flags.viewBindFlags = RHI::ImageBindFlags::ShaderRead;
  309. testCases.push_back(flags);
  310. flags.imageBindFlags = RHI::ImageBindFlags::ShaderWrite;
  311. flags.viewBindFlags = RHI::ImageBindFlags::ShaderReadWrite;
  312. testCases.push_back(flags);
  313. flags.imageBindFlags = RHI::ImageBindFlags::None;
  314. flags.viewBindFlags = RHI::ImageBindFlags::ShaderRead;
  315. testCases.push_back(flags);
  316. flags.imageBindFlags = RHI::ImageBindFlags::None;
  317. flags.viewBindFlags = RHI::ImageBindFlags::ShaderWrite;
  318. testCases.push_back(flags);
  319. flags.imageBindFlags = RHI::ImageBindFlags::None;
  320. flags.viewBindFlags = RHI::ImageBindFlags::ShaderReadWrite;
  321. testCases.push_back(flags);
  322. return testCases;
  323. }
  324. std::string MultiDeviceImageBindFlagsToString(RHI::ImageBindFlags bindFlags)
  325. {
  326. switch (bindFlags)
  327. {
  328. case RHI::ImageBindFlags::None:
  329. return "None";
  330. case RHI::ImageBindFlags::Color:
  331. return "Color";
  332. case RHI::ImageBindFlags::ShaderRead:
  333. return "ShaderRead";
  334. case RHI::ImageBindFlags::ShaderWrite:
  335. return "ShaderWrite";
  336. case RHI::ImageBindFlags::ShaderReadWrite:
  337. return "ShaderReadWrite";
  338. default:
  339. AZ_Assert(false, "No string conversion was created for this bind flag setting.");
  340. }
  341. return "";
  342. }
  343. std::string GenerateMultiDeviceImageBindFlagTestCaseName(const ::testing::TestParamInfo<MultiDeviceImageAndViewBindFlags>& info)
  344. {
  345. return MultiDeviceImageBindFlagsToString(info.param.imageBindFlags) + "ImageWith" + MultiDeviceImageBindFlagsToString(info.param.viewBindFlags) + "ImageView";
  346. }
  347. INSTANTIATE_TEST_CASE_P(ImageView, MultiDeviceImageBindFlagTests, ::testing::ValuesIn(GenerateCompatibleMultiDeviceImageBindFlagCombinations()), GenerateMultiDeviceImageBindFlagTestCaseName);
  348. INSTANTIATE_TEST_CASE_P(ImageView, MultiDeviceImageBindFlagFailureCases, ::testing::ValuesIn(GenerateIncompatibleMultiDeviceImageBindFlagCombinations()), GenerateMultiDeviceImageBindFlagTestCaseName);
  349. }