3
0

CullingTests.cpp 10 KB

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