HashingTests.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  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 <AzCore/Math/Random.h>
  11. #include <AzCore/Utils/TypeHash.h>
  12. #include <Atom/RHI.Reflect/BufferDescriptor.h>
  13. #include <Atom/RHI.Reflect/BufferViewDescriptor.h>
  14. #include <Atom/RHI.Reflect/ImageDescriptor.h>
  15. #include <Atom/RHI.Reflect/ImageViewDescriptor.h>
  16. #include <Atom/RHI.Reflect/TransientBufferDescriptor.h>
  17. #include <Atom/RHI.Reflect/TransientImageDescriptor.h>
  18. #include <Atom/RHI.Reflect/PipelineLayoutDescriptor.h>
  19. #include <Atom/RHI.Reflect/SamplerState.h>
  20. #include <Atom/RHI.Reflect/InputStreamLayout.h>
  21. #include <Atom/RHI.Reflect/ShaderStageFunction.h>
  22. #include <Atom/RHI/PipelineStateDescriptor.h>
  23. namespace UnitTest
  24. {
  25. using namespace AZ;
  26. class ShaderStageFunction : public RHI::ShaderStageFunction
  27. {
  28. public:
  29. AZ_CLASS_ALLOCATOR(ShaderStageFunction, SystemAllocator);
  30. ShaderStageFunction(uint64_t hash, RHI::ShaderStage shaderStage)
  31. : RHI::ShaderStageFunction(shaderStage)
  32. , m_declaredHash{hash}
  33. {}
  34. RHI::ResultCode FinalizeInternal() override
  35. {
  36. SetHash(HashValue64(m_declaredHash));
  37. return RHI::ResultCode::Success;
  38. }
  39. private:
  40. uint64_t m_declaredHash;
  41. };
  42. struct BaselineStructWithPadding
  43. {
  44. BaselineStructWithPadding()
  45. {
  46. m_b = 1;
  47. m_c = reinterpret_cast<const uint32_t*>(0x12345);
  48. }
  49. uint8_t m_b;
  50. // 7 bytes of padding.
  51. const uint32_t* m_c;
  52. HashValue64 GetHash() const
  53. {
  54. return TypeHash64(*this);
  55. }
  56. };
  57. static_assert(sizeof(BaselineStructWithPadding) == 16, "Baseline struct does not exhibit expected padding.");
  58. /**
  59. * These tests validate that RHI structs which expose a 'GetHash()' method
  60. * will produce a reliable hash. Specifically, it tests the hash under conditions
  61. * where the underlying memory includes pad bytes which are uninitialized.
  62. */
  63. class HashingTests
  64. : public RHITestFixture
  65. {
  66. public:
  67. HashingTests()
  68. : RHITestFixture()
  69. {
  70. }
  71. protected:
  72. enum class Expect
  73. {
  74. Success = 0,
  75. Failure
  76. };
  77. void ScrambleMemory(void* memory, size_t size)
  78. {
  79. uint8_t* bytes = reinterpret_cast<uint8_t*>(memory);
  80. for (size_t i = 0; i < size; ++i)
  81. {
  82. bytes[i] = static_cast<uint8_t>(m_random.GetRandom());
  83. }
  84. }
  85. template <typename T, Expect expect = Expect::Success>
  86. void TestHash(AZStd::function<void(T&)> initFunction = nullptr)
  87. {
  88. void* memory = azmalloc(sizeof(T));
  89. T* type = nullptr;
  90. uint64_t hashPrev = 0;
  91. bool isFirstHash = true;
  92. const size_t IterationCount = 10;
  93. for (size_t i = 0; i < IterationCount; ++i)
  94. {
  95. ScrambleMemory(memory, sizeof(T));
  96. type = new (memory) T();
  97. if (initFunction)
  98. {
  99. initFunction(*type);
  100. }
  101. uint64_t hashCurr = static_cast<uint64_t>(type->GetHash());
  102. if (!isFirstHash)
  103. {
  104. if constexpr (expect == Expect::Success)
  105. {
  106. EXPECT_EQ(hashCurr, hashPrev);
  107. }
  108. else
  109. {
  110. EXPECT_NE(hashCurr, hashPrev);
  111. }
  112. }
  113. isFirstHash = false;
  114. hashPrev = hashCurr;
  115. type->~T();
  116. }
  117. azfree(memory);
  118. }
  119. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> CreateShaderResourceGroupLayout()
  120. {
  121. RHI::Ptr<RHI::ShaderResourceGroupLayout> layout = RHI::ShaderResourceGroupLayout::Create();
  122. // The values here are just arbitrary.
  123. layout->SetBindingSlot(0);
  124. layout->AddShaderInput(RHI::ShaderInputConstantDescriptor{ Name("InputA"), 0, 12, 0, 0});
  125. layout->AddShaderInput(RHI::ShaderInputConstantDescriptor{ Name("InputB"), 12, 12, 0, 0});
  126. layout->AddShaderInput(RHI::ShaderInputConstantDescriptor{ Name("InputC"), 24, 76, 0, 0});
  127. layout->AddStaticSampler(RHI::ShaderInputStaticSamplerDescriptor{ Name("StaticSamplerA"), RHI::SamplerState::CreateAnisotropic(16, RHI::AddressMode::Wrap), 1, 1});
  128. layout->Finalize();
  129. return layout;
  130. }
  131. RHI::ShaderResourceGroupBindingInfo CreateShaderResourceGroupBindingInfo()
  132. {
  133. RHI::ShaderResourceGroupBindingInfo bindingInfo;
  134. bindingInfo.m_constantDataBindingInfo.m_registerId = 0;
  135. bindingInfo.m_resourcesRegisterMap.insert({ "StaticSamplerA", RHI::ResourceBindingInfo{RHI::ShaderStageMask::Vertex, 1, 1} });
  136. return bindingInfo;
  137. }
  138. void InitStreamLayout(RHI::InputStreamLayout& layout)
  139. {
  140. // The values here are just arbitrary (Except for buffer index which is validated in Finalize())
  141. layout.SetTopology(RHI::PrimitiveTopology::TriangleList);
  142. layout.AddStreamBuffer(RHI::StreamBufferDescriptor{ RHI::StreamStepFunction::PerVertex, 1, 8 });
  143. layout.AddStreamBuffer(RHI::StreamBufferDescriptor{ RHI::StreamStepFunction::PerInstance, 3, 16 });
  144. layout.AddStreamChannel(RHI::StreamChannelDescriptor{ RHI::ShaderSemantic{Name("ChannelA"), 1}, RHI::Format::R8G8B8A8_UINT, 4, 0});
  145. layout.AddStreamChannel(RHI::StreamChannelDescriptor{ RHI::ShaderSemantic{Name("ChannelB"), 1}, RHI::Format::R10G10B10A2_UINT, 7, 1});
  146. layout.Finalize();
  147. }
  148. void InitRenderAttachmentLayout(RHI::RenderAttachmentLayout& layout)
  149. {
  150. // The values here are just arbitrary
  151. layout.m_subpassCount = 2;
  152. layout.m_attachmentFormats[0] = RHI::Format::ASTC_10x6_UNORM;
  153. layout.m_attachmentFormats[1] = RHI::Format::ASTC_8x8_UNORM;
  154. layout.m_attachmentFormats[2] = RHI::Format::R32G32B32_FLOAT;
  155. layout.m_attachmentFormats[3] = RHI::Format::R16G16_UINT;
  156. {
  157. auto& subpassLayout = layout.m_subpassLayouts[0];
  158. subpassLayout.m_rendertargetCount = 2;
  159. subpassLayout.m_subpassInputCount = 0;
  160. subpassLayout.m_depthStencilDescriptor = RHI::RenderAttachmentDescriptor{ 0, RHI::InvalidRenderAttachmentIndex, RHI::AttachmentLoadStoreAction() };
  161. subpassLayout.m_rendertargetDescriptors[0] = RHI::RenderAttachmentDescriptor{ 1, RHI::InvalidRenderAttachmentIndex, RHI::AttachmentLoadStoreAction() };
  162. subpassLayout.m_rendertargetDescriptors[1] = RHI::RenderAttachmentDescriptor{ 2, 3, RHI::AttachmentLoadStoreAction(RHI::ClearValue(), RHI::AttachmentLoadAction::DontCare) };
  163. }
  164. {
  165. auto& subpassLayout = layout.m_subpassLayouts[1];
  166. subpassLayout.m_rendertargetCount = 1;
  167. subpassLayout.m_subpassInputCount = 2;
  168. subpassLayout.m_rendertargetDescriptors[0] = RHI::RenderAttachmentDescriptor{ 0, 1, RHI::AttachmentLoadStoreAction() };
  169. subpassLayout.m_subpassInputDescriptors[0].m_attachmentIndex = 3;
  170. }
  171. }
  172. void InitRenderAttachmentConfiguration(RHI::RenderAttachmentConfiguration& configuration)
  173. {
  174. InitRenderAttachmentLayout(configuration.m_renderAttachmentLayout);
  175. configuration.m_subpassIndex = 1;
  176. }
  177. private:
  178. void SetUp() override
  179. {
  180. RHITestFixture::SetUp();
  181. m_factory.reset(aznew Factory);
  182. }
  183. void TearDown() override
  184. {
  185. m_factory = nullptr;
  186. RHITestFixture::TearDown();
  187. }
  188. SimpleLcgRandom m_random;
  189. AZStd::unique_ptr<Factory> m_factory;
  190. };
  191. TEST_F(HashingTests, BaselineTest)
  192. {
  193. // We expect this to fail, because padding exists in the baseline structure.
  194. TestHash<BaselineStructWithPadding, Expect::Failure>();
  195. }
  196. TEST_F(HashingTests, RenderStatesTest)
  197. {
  198. TestHash<RHI::RenderStates>();
  199. }
  200. TEST_F(HashingTests, ImageTest)
  201. {
  202. TestHash<RHI::ImageViewDescriptor>();
  203. TestHash<RHI::ImageDescriptor>();
  204. TestHash<RHI::ClearValue>();
  205. RHI::ClearValue clearValue;
  206. TestHash<RHI::TransientImageDescriptor>([&clearValue](RHI::TransientImageDescriptor& desc)
  207. {
  208. desc.m_attachmentId = RHI::AttachmentId{"ABC"};
  209. desc.m_optimizedClearValue = &clearValue;
  210. });
  211. }
  212. TEST_F(HashingTests, BufferTest)
  213. {
  214. TestHash<RHI::BufferViewDescriptor>();
  215. TestHash<RHI::BufferDescriptor>();
  216. TestHash<RHI::TransientBufferDescriptor>([](RHI::TransientBufferDescriptor& desc)
  217. {
  218. desc.m_attachmentId = RHI::AttachmentId{"EFG"};
  219. });
  220. }
  221. TEST_F(HashingTests, SamplerStateTest)
  222. {
  223. TestHash<RHI::SamplerState>();
  224. }
  225. TEST_F(HashingTests, ShaderResourceGroupLayoutTest)
  226. {
  227. TestHash<RHI::ShaderInputBufferDescriptor>([](RHI::ShaderInputBufferDescriptor& input)
  228. {
  229. input.m_name = "InputA";
  230. });
  231. TestHash<RHI::ShaderInputImageDescriptor>([](RHI::ShaderInputImageDescriptor& input)
  232. {
  233. input.m_name = "InputA";
  234. });
  235. TestHash<RHI::ShaderInputSamplerDescriptor>([](RHI::ShaderInputSamplerDescriptor& input)
  236. {
  237. input.m_name = "InputA";
  238. });
  239. TestHash<RHI::ShaderInputConstantDescriptor>([](RHI::ShaderInputConstantDescriptor& input)
  240. {
  241. input.m_name = "InputA";
  242. });
  243. }
  244. TEST_F(HashingTests, StreamLayoutTest)
  245. {
  246. TestHash<RHI::ShaderSemantic>([](RHI::ShaderSemantic& semantic)
  247. {
  248. semantic.m_name = Name{"COLOR"};
  249. semantic.m_index = 1;
  250. });
  251. TestHash<RHI::StreamChannelDescriptor>([](RHI::StreamChannelDescriptor& layout)
  252. {
  253. layout.m_semantic = RHI::ShaderSemantic{Name("UV"), 1};
  254. });
  255. TestHash<RHI::StreamBufferDescriptor>();
  256. TestHash<RHI::InputStreamLayout>([this](RHI::InputStreamLayout& layout)
  257. {
  258. InitStreamLayout(layout);
  259. });
  260. }
  261. TEST_F(HashingTests, RenderAttachmentLayoutTest)
  262. {
  263. TestHash<RHI::RenderAttachmentLayout>([this](RHI::RenderAttachmentLayout& layout)
  264. {
  265. InitRenderAttachmentLayout(layout);
  266. });
  267. }
  268. TEST_F(HashingTests, RenderAttachmentConfigurationTest)
  269. {
  270. TestHash<RHI::RenderAttachmentConfiguration>([this](RHI::RenderAttachmentConfiguration& configuration)
  271. {
  272. InitRenderAttachmentConfiguration(configuration);
  273. });
  274. }
  275. TEST_F(HashingTests, PipelineStateTest)
  276. {
  277. // These are assigned by us, so just pick something arbitrary.
  278. const uint64_t vertFunctionHash = 0xABCDEF00;
  279. const uint64_t fragFunctionHash = 0xABCDEF01;
  280. RHI::Ptr<ShaderStageFunction> vertFunction = aznew ShaderStageFunction(vertFunctionHash, RHI::ShaderStage::Vertex);
  281. vertFunction->Finalize();
  282. RHI::Ptr<ShaderStageFunction> fragFunction = aznew ShaderStageFunction(fragFunctionHash, RHI::ShaderStage::Fragment);
  283. fragFunction->Finalize();
  284. RHI::InputStreamLayout inputStreamLayout;
  285. InitStreamLayout(inputStreamLayout);
  286. RHI::RenderAttachmentConfiguration renderAttachmentConfiguration;
  287. InitRenderAttachmentConfiguration(renderAttachmentConfiguration);
  288. RHI::ConstPtr<RHI::ShaderResourceGroupLayout> srgLayout = CreateShaderResourceGroupLayout();
  289. RHI::ShaderResourceGroupBindingInfo bindingInfo = CreateShaderResourceGroupBindingInfo();
  290. RHI::Ptr<RHI::PipelineLayoutDescriptor> pipelineLayoutDesc = RHI::PipelineLayoutDescriptor::Create();
  291. pipelineLayoutDesc->AddShaderResourceGroupLayoutInfo(*srgLayout, bindingInfo);
  292. pipelineLayoutDesc->Finalize();
  293. TestHash<RHI::PipelineStateDescriptorForDraw>([&] (RHI::PipelineStateDescriptorForDraw& desc)
  294. {
  295. desc.m_vertexFunction = vertFunction;
  296. desc.m_fragmentFunction = fragFunction;
  297. desc.m_pipelineLayoutDescriptor = pipelineLayoutDesc;
  298. desc.m_inputStreamLayout = inputStreamLayout;
  299. desc.m_renderAttachmentConfiguration = renderAttachmentConfiguration;
  300. });
  301. const uint64_t computeFunctionHash = 0xABCDEF02;
  302. RHI::Ptr<ShaderStageFunction> computeFunction = aznew ShaderStageFunction(computeFunctionHash, RHI::ShaderStage::Compute);
  303. TestHash<RHI::PipelineStateDescriptorForDispatch>([&](RHI::PipelineStateDescriptorForDispatch& desc)
  304. {
  305. desc.m_pipelineLayoutDescriptor = pipelineLayoutDesc;
  306. desc.m_computeFunction = computeFunction;
  307. });
  308. }
  309. }