ImageTests.cpp 15 KB

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