CullingTests.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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 <AzCore/Math/MatrixUtils.h>
  9. #include <AzCore/Task/TaskExecutor.h>
  10. #include <AzCore/Task/TaskGraph.h>
  11. #include <AzFramework/Scene/SceneSystemComponent.h>
  12. #include <AzFramework/Visibility/OctreeSystemComponent.h>
  13. #include <Atom/RPI.Public/Culling.h>
  14. #include <Atom/RPI.Public/Scene.h>
  15. #include <Atom/RPI.Public/View.h>
  16. #include <Common/RPITestFixture.h>
  17. namespace UnitTest
  18. {
  19. using namespace AZ;
  20. using namespace RPI;
  21. // The CullingTests fixture sets up a culling scene for testing culling.
  22. // It also creates some views and a varying number of cullable objects visible in each view.
  23. // It does not register the cullables with the culling scene, so their properties can be overridden
  24. // before registering in order to test different scenarios
  25. class CullingTests : public RPITestFixture
  26. {
  27. void SetUp() override
  28. {
  29. RPITestFixture::SetUp();
  30. m_executor = aznew TaskExecutor{};
  31. TaskExecutor::SetInstance(m_executor);
  32. m_octreeSystemComponent = new AzFramework::OctreeSystemComponent;
  33. m_sceneSystemComponent = new AzFramework::SceneSystemComponent;
  34. m_testScene = Scene::CreateScene(SceneDescriptor{});
  35. m_cullingScene = m_testScene->GetCullingScene();
  36. m_cullingScene->Activate(m_testScene.get());
  37. CreateTestViews();
  38. CreateTestObjects();
  39. }
  40. void TearDown() override
  41. {
  42. m_views.clear();
  43. m_cullingScene->Deactivate();
  44. m_testScene = nullptr;
  45. delete m_octreeSystemComponent;
  46. delete m_sceneSystemComponent;
  47. if (&TaskExecutor::Instance() == m_executor) // if this test created the default instance unset it before destroying it
  48. {
  49. TaskExecutor::SetInstance(nullptr);
  50. }
  51. azdestroy(m_executor);
  52. RPITestFixture::TearDown();
  53. }
  54. protected:
  55. static constexpr size_t testCameraCount = 4;
  56. static constexpr size_t visibleObjectUserDataOffset = 100;
  57. using TestCameraList = AZStd::vector<ViewPtr>;
  58. void Cull(TestCameraList& views)
  59. {
  60. m_cullingScene->BeginCulling(*m_testScene, views);
  61. // Create and submit work to the culling scene in a similar style as RPI::Scene::PrepareRender
  62. static const TaskDescriptor processCullablesDescriptor{ "RPI::Scene::ProcessCullables", "Graphics" };
  63. TaskGraphEvent processCullablesTGEvent{ "ProcessCullables Wait" };
  64. TaskGraph processCullablesTG{ "ProcessCullables" };
  65. for (ViewPtr& viewPtr : views)
  66. {
  67. processCullablesTG.AddTask(
  68. processCullablesDescriptor,
  69. [this, &viewPtr, &processCullablesTGEvent]()
  70. {
  71. TaskGraph subTaskGraph{ "ProcessCullables Subgraph" };
  72. m_cullingScene->ProcessCullablesTG(*m_testScene, *viewPtr, subTaskGraph, processCullablesTGEvent);
  73. if (!subTaskGraph.IsEmpty())
  74. {
  75. subTaskGraph.Detach();
  76. subTaskGraph.Submit(&processCullablesTGEvent);
  77. }
  78. });
  79. }
  80. processCullablesTG.Submit(&processCullablesTGEvent);
  81. processCullablesTGEvent.Wait();
  82. m_cullingScene->EndCulling(*m_testScene, views);
  83. for (ViewPtr& viewPtr : views)
  84. {
  85. viewPtr->FinalizeVisibleObjectList();
  86. }
  87. }
  88. enum ViewIndex
  89. {
  90. YPositive = 0,
  91. XNegative,
  92. YNegative,
  93. XPositive
  94. };
  95. // Create four test cameras
  96. // Top down view of the cameras
  97. // ___ +y
  98. // \0/ |
  99. // |1>X<3| |
  100. // /2\ |_____+x
  101. // ---
  102. //
  103. void CreateTestViews()
  104. {
  105. ViewPtr viewYPositive = View::CreateView(Name("TestViewYPositive"), RPI::View::UsageCamera);
  106. ViewPtr viewXNegative = View::CreateView(Name("TestViewXNegative"), RPI::View::UsageShadow);
  107. ViewPtr viewYNegative = View::CreateView(Name("TestViewYNegative"), RPI::View::UsageShadow);
  108. ViewPtr viewXPositive = View::CreateView(Name("TestViewXPositive"), RPI::View::UsageReflectiveCubeMap);
  109. // Render everything by default
  110. RHI::DrawListMask drawListMask;
  111. drawListMask.reset();
  112. drawListMask.flip();
  113. viewYPositive->SetDrawListMask(drawListMask);
  114. viewXNegative->SetDrawListMask(drawListMask);
  115. viewYNegative->SetDrawListMask(drawListMask);
  116. viewXPositive->SetDrawListMask(drawListMask);
  117. const float fovY = DegToRad(90.0f);
  118. const float aspectRatio = 1.0f;
  119. const float nearDist = 0.1f;
  120. const float farDist = 100.0f;
  121. // These rotations represent a right-handed rotation around the z-up axis.
  122. // Starting with a view pointed straight down the y-forward axis,
  123. // these will rotate counter clockwise
  124. viewYPositive->SetCameraTransform(Matrix3x4::CreateIdentity());
  125. viewXNegative->SetCameraTransform(Matrix3x4::CreateRotationZ(DegToRad(90.0f)));
  126. viewYNegative->SetCameraTransform(Matrix3x4::CreateRotationZ(DegToRad(180.0f)));
  127. viewXPositive->SetCameraTransform(Matrix3x4::CreateRotationZ(DegToRad(270.0f)));
  128. // Matrix4x4::CreateProjection creates a view pointing up the positive z-axis.
  129. // Combine that with the rotation matrices above to get the 4 views.
  130. Matrix4x4 viewToClipZPositive = Matrix4x4::CreateIdentity();
  131. bool reverseDepth = true;
  132. MakePerspectiveFovMatrixRH(viewToClipZPositive, fovY, aspectRatio, nearDist, farDist, reverseDepth);
  133. viewYPositive->SetViewToClipMatrix(viewToClipZPositive);
  134. viewXNegative->SetViewToClipMatrix(viewToClipZPositive);
  135. viewYNegative->SetViewToClipMatrix(viewToClipZPositive);
  136. viewXPositive->SetViewToClipMatrix(viewToClipZPositive);
  137. m_views.resize(testCameraCount);
  138. m_views[YPositive] = viewYPositive;
  139. m_views[XNegative] = viewXNegative;
  140. m_views[YNegative] = viewYNegative;
  141. m_views[XPositive] = viewXPositive;
  142. }
  143. static void InitializeCullableFromAabb(Cullable& cullable, const Aabb& aabb, size_t index)
  144. {
  145. cullable.m_cullData.m_boundingObb = Obb::CreateFromAabb(aabb);
  146. cullable.m_cullData.m_boundingSphere = Sphere::CreateFromAabb(aabb);
  147. cullable.m_cullData.m_visibilityEntry.m_boundingVolume = aabb;
  148. cullable.m_cullData.m_visibilityEntry.m_typeFlags = AzFramework::VisibilityEntry::TYPE_RPI_VisibleObjectList;
  149. // Set all bits in the draw list mask by default, so everything will be rendered
  150. cullable.m_cullData.m_drawListMask.reset();
  151. cullable.m_cullData.m_drawListMask.flip();
  152. cullable.m_cullData.m_visibilityEntry.m_userData = &cullable;
  153. cullable.m_lodData.m_lodSelectionRadius = 0.5f * aabb.GetExtents().GetMaxElement();
  154. Cullable::LodData::Lod lod;
  155. lod.m_screenCoverageMin = 0.0f;
  156. lod.m_screenCoverageMax = 1.0f;
  157. // We're not actually using the user data for anything, but it needs to be non-null or the
  158. // VisibleObjectContext will assert. We'll use the index here, which could potentially be
  159. // used for validation in the tests (e.g., validate the Nth object was culled/visible).
  160. // However, we must also add an offset, or the 0th object will be treated as a nullptr.
  161. lod.m_visibleObjectUserData = reinterpret_cast<void*>(index + visibleObjectUserDataOffset);
  162. cullable.m_lodData.m_lods.push_back(lod);
  163. }
  164. // Create test objects visible to the cameras
  165. // (objects represented as dots in the diagram below)
  166. // Top down view of the cameras:
  167. // ....
  168. // ___ +y
  169. // \0/ |
  170. // ... |1> X <3| . |
  171. // /2\ |_____+x
  172. // ---
  173. // ..
  174. //
  175. void CreateTestObjects()
  176. {
  177. // Four objects visible by the first camera
  178. Aabb aabb = Aabb::CreateCenterRadius(Vector3::CreateAxisY(10.0), 1.0);
  179. for (size_t i = 0; i < 10; ++i)
  180. {
  181. if (i == 4)
  182. {
  183. // Three objects visible by the second camera
  184. aabb = Aabb::CreateCenterRadius(Vector3::CreateAxisX(-10.0), 1.0);
  185. }
  186. if (i == 7)
  187. {
  188. // Two objects visible by the third camera
  189. aabb = Aabb::CreateCenterRadius(Vector3::CreateAxisY(-10.0), 1.0);
  190. }
  191. if (i == 9)
  192. {
  193. // One object visible by the final camera
  194. aabb = Aabb::CreateCenterRadius(Vector3::CreateAxisX(10.0), 1.0);
  195. }
  196. // We're initializing these cullables in place because the copy constructor for RPI::Cullbable is implicitely deleted
  197. InitializeCullableFromAabb(m_testObjects[i], aabb, i);
  198. }
  199. }
  200. TaskExecutor* m_executor;
  201. AzFramework::OctreeSystemComponent* m_octreeSystemComponent;
  202. AzFramework::SceneSystemComponent* m_sceneSystemComponent;
  203. ScenePtr m_testScene;
  204. CullingScene* m_cullingScene;
  205. AZStd::vector<ViewPtr> m_views;
  206. AZStd::array<Cullable, 10> m_testObjects;
  207. };
  208. TEST_F(CullingTests, VisibleObjectListTest)
  209. {
  210. for (Cullable& object : m_testObjects)
  211. {
  212. m_cullingScene->RegisterOrUpdateCullable(object);
  213. }
  214. Cull(m_views);
  215. EXPECT_EQ(m_views[YPositive]->GetVisibleObjectList().size(), 4);
  216. EXPECT_EQ(m_views[XNegative]->GetVisibleObjectList().size(), 3);
  217. EXPECT_EQ(m_views[YNegative]->GetVisibleObjectList().size(), 2);
  218. EXPECT_EQ(m_views[XPositive]->GetVisibleObjectList().size(), 1);
  219. for (Cullable& object : m_testObjects)
  220. {
  221. m_cullingScene->UnregisterCullable(object);
  222. }
  223. }
  224. }