3
0

SceneTests.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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 <Atom/RPI.Public/Base.h>
  9. #include <Atom/RPI.Public/FeatureProcessor.h>
  10. #include <Atom/RPI.Public/FeatureProcessorFactory.h>
  11. #include <Atom/RPI.Public/Pass/RasterPass.h>
  12. #include <Atom/RPI.Public/RenderPipeline.h>
  13. #include <Atom/RPI.Public/Scene.h>
  14. #include <Atom/RPI.Public/View.h>
  15. #include <Atom/RPI.Reflect/Pass/RasterPassData.h>
  16. #include <AzFramework/Visibility/OctreeSystemComponent.h>
  17. #include <AzTest/AzTest.h>
  18. #include <Common/RPITestFixture.h>
  19. #include <Common/SerializeTester.h>
  20. #include <Common/ErrorMessageFinder.h>
  21. #include <Common/TestFeatureProcessors.h>
  22. namespace UnitTest
  23. {
  24. using namespace AZ;
  25. using namespace AZ::RPI;
  26. class SceneTests
  27. : public RPITestFixture
  28. {
  29. protected:
  30. AzFramework::OctreeSystemComponent* m_octreeSystemComponent;
  31. void SetUp() override
  32. {
  33. RPITestFixture::SetUp();
  34. m_octreeSystemComponent = new AzFramework::OctreeSystemComponent;
  35. TestFeatureProcessor1::Reflect(GetSerializeContext());
  36. TestFeatureProcessor2::Reflect(GetSerializeContext());
  37. TestFeatureProcessorImplementation::Reflect(GetSerializeContext());
  38. TestFeatureProcessorImplementation2::Reflect(GetSerializeContext());
  39. AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor<TestFeatureProcessor1>();
  40. AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor<TestFeatureProcessor2>();
  41. AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessorWithInterface<TestFeatureProcessorImplementation, TestFeatureProcessorInterface>();
  42. AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessorWithInterface<TestFeatureProcessorImplementation2, TestFeatureProcessorInterface>();
  43. }
  44. void TearDown() override
  45. {
  46. AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor<TestFeatureProcessor1>();
  47. AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor<TestFeatureProcessor2>();
  48. AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor<TestFeatureProcessorImplementation>();
  49. AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor<TestFeatureProcessorImplementation2>();
  50. delete m_octreeSystemComponent;
  51. RPITestFixture::TearDown();
  52. }
  53. };
  54. /**
  55. * Unit test to test RPI::Scene's feature processor management functions
  56. */
  57. TEST_F(SceneTests, FeatureProcessorManagement)
  58. {
  59. AZ::Test::ScopedAutoTempDirectory tempDirectory;
  60. AZ::IO::FileIOBase::GetInstance()->SetAlias("@user@", tempDirectory.GetDirectory());
  61. // Create scene with two test feature processors
  62. SceneDescriptor sceneDesc;
  63. sceneDesc.m_featureProcessorNames.push_back(AZStd::string(TestFeatureProcessor1::RTTI_TypeName()));
  64. ScenePtr testScene = Scene::CreateScene(sceneDesc);
  65. EXPECT_TRUE(testScene->GetFeatureProcessor(FeatureProcessorId{ TestFeatureProcessor1::RTTI_TypeName() }) != nullptr);
  66. EXPECT_TRUE(testScene->GetFeatureProcessor(FeatureProcessorId{ TestFeatureProcessor2::RTTI_TypeName() }) == nullptr);
  67. testScene->DisableAllFeatureProcessors();
  68. EXPECT_TRUE(testScene->GetFeatureProcessor(FeatureProcessorId{ TestFeatureProcessor1::RTTI_TypeName() }) == nullptr);
  69. // Enable feature processors
  70. FeatureProcessor* testFeature1 = testScene->EnableFeatureProcessor(FeatureProcessorId{ TestFeatureProcessor1::RTTI_TypeName() });
  71. EXPECT_TRUE(testFeature1 != nullptr);
  72. TestFeatureProcessor2* testFeature2 = testScene->EnableFeatureProcessor<TestFeatureProcessor2>();
  73. EXPECT_TRUE(testFeature2 != nullptr);
  74. testScene->DisableFeatureProcessor< TestFeatureProcessor1>();
  75. EXPECT_TRUE(testScene->GetFeatureProcessor(FeatureProcessorId{ TestFeatureProcessor1::RTTI_TypeName() }) == nullptr);
  76. testScene->DisableFeatureProcessor(FeatureProcessorId{ testFeature2->RTTI_GetTypeName() });
  77. EXPECT_TRUE(testScene->GetFeatureProcessor< TestFeatureProcessor2>() == nullptr);
  78. }
  79. /**
  80. * Unit test to test RPI::Scene's render pipeline management functions
  81. */
  82. TEST_F(SceneTests, RenderPipelineManagement)
  83. {
  84. RenderPipelineId pipelineId1 = RenderPipelineId("pipeline1");
  85. RenderPipelineId pipelineId2 = RenderPipelineId("pipeline2");
  86. RenderPipelineDescriptor pipelineDesc;
  87. pipelineDesc.m_name = pipelineId1.GetCStr();
  88. RenderPipelinePtr pipeline1 = RenderPipeline::CreateRenderPipeline(pipelineDesc);
  89. RenderPipelinePtr pipeline2 = RenderPipeline::CreateRenderPipeline(pipelineDesc);
  90. SceneDescriptor sceneDesc;
  91. ScenePtr testScene = Scene::CreateScene(sceneDesc);
  92. testScene->AddRenderPipeline(pipeline1);
  93. EXPECT_TRUE(testScene->GetRenderPipeline(pipelineId1) == pipeline1);
  94. EXPECT_TRUE(testScene->GetRenderPipeline(pipelineId2) == nullptr);
  95. // Asset false with pipeline have same name. And the pipeline won't be added
  96. AZ_TEST_START_ASSERTTEST;
  97. testScene->AddRenderPipeline(pipeline2);
  98. AZ_TEST_STOP_ASSERTTEST(1);
  99. EXPECT_TRUE(testScene->GetRenderPipeline(pipelineId2) == nullptr);
  100. pipelineDesc.m_name = pipelineId2.GetCStr();
  101. pipeline2 = RenderPipeline::CreateRenderPipeline(pipelineDesc);
  102. testScene->AddRenderPipeline(pipeline2);
  103. EXPECT_TRUE(testScene->GetRenderPipeline(pipelineId2) != nullptr);
  104. EXPECT_TRUE(pipeline2->GetId() == pipelineId2);
  105. EXPECT_TRUE(testScene->GetRenderPipeline(pipelineId2) == pipeline2);
  106. // create another pipeline with same name as pipeline2
  107. RenderPipelinePtr pipeline3 = RenderPipeline::CreateRenderPipeline(pipelineDesc);
  108. AZ_TEST_START_ASSERTTEST;
  109. pipeline3->RemoveFromScene();
  110. AZ_TEST_STOP_ASSERTTEST(1);
  111. EXPECT_TRUE(testScene->GetRenderPipeline(pipelineId2) == pipeline2);
  112. pipeline2->RemoveFromScene();
  113. EXPECT_TRUE(testScene->GetRenderPipeline(pipelineId2) == nullptr);
  114. }
  115. TEST_F(SceneTests, SceneNotificationTest)
  116. {
  117. // Create scene with feature processor which enables scene notification handler
  118. SceneDescriptor sceneDesc;
  119. sceneDesc.m_featureProcessorNames.push_back(AZStd::string(TestFeatureProcessor1::RTTI_TypeName()));
  120. ScenePtr testScene = Scene::CreateScene(sceneDesc);
  121. testScene->Activate();
  122. auto feature = testScene->GetFeatureProcessor< TestFeatureProcessor1>();
  123. RenderPipelineId pipelineId1 = RenderPipelineId("pipeline1");
  124. RenderPipelineId pipelineId2 = RenderPipelineId("pipeline2");
  125. RenderPipelineDescriptor pipelineDesc;
  126. pipelineDesc.m_name = pipelineId1.GetCStr();
  127. RenderPipelinePtr pipeline1 = RenderPipeline::CreateRenderPipeline(pipelineDesc);
  128. pipelineDesc.m_name = pipelineId2.GetCStr();
  129. RenderPipelinePtr pipeline2 = RenderPipeline::CreateRenderPipeline(pipelineDesc);
  130. // Add one render pipeline
  131. testScene->AddRenderPipeline(pipeline1);
  132. EXPECT_TRUE(feature->m_pipelineCount == 1);
  133. EXPECT_TRUE(pipeline1.get() == feature->m_lastPipeline);
  134. // Add another render pipeline
  135. testScene->AddRenderPipeline(pipeline2);
  136. EXPECT_TRUE(pipeline2.get() == feature->m_lastPipeline);
  137. EXPECT_TRUE(feature->m_pipelineCount == 2);
  138. // Remove the first render pipeline which was added
  139. testScene->RemoveRenderPipeline(pipelineId1);
  140. EXPECT_TRUE(feature->m_pipelineCount == 1);
  141. EXPECT_TRUE(pipeline1.get() == feature->m_lastPipeline);
  142. // Create a raster pass with viewTag "mainCamera"
  143. const RPI::PipelineViewTag viewTag{ "mainCamera" };
  144. PassDescriptor passDesc;
  145. AZStd::shared_ptr<PassTemplate> passTemplate = AZStd::make_shared<PassTemplate>();
  146. AZStd::shared_ptr<RasterPassData> passData = AZStd::make_shared<RasterPassData>();
  147. passData->m_drawListTag = "forward";
  148. passData->m_pipelineViewTag = viewTag.GetCStr();
  149. passTemplate->m_passData = passData;
  150. passDesc.m_passName = "raster";
  151. passDesc.m_passTemplate = passTemplate;
  152. Ptr<RasterPass> newPass = RasterPass::Create(passDesc);
  153. // Add new pass to render pipeline which should trigger render pipeline pass modify notification
  154. pipeline2->GetRootPass()->AddChild(newPass, true);
  155. // This function is called in every RPISystem render tick. We call it manually here to trigger the pass change notification
  156. pipeline2->UpdatePasses();
  157. EXPECT_TRUE(feature->m_pipelineChangedCount == 1);
  158. EXPECT_TRUE(pipeline2.get() == feature->m_lastPipeline);
  159. ViewPtr view = View::CreateView(AZ::Name("TestView"), RPI::View::UsageCamera);
  160. pipeline2->SetPersistentView(viewTag, view);
  161. EXPECT_TRUE(feature->m_viewSetCount == 1);
  162. pipeline2->SetPersistentView(viewTag, nullptr);
  163. EXPECT_TRUE(feature->m_viewSetCount == 2);
  164. testScene->Deactivate();
  165. }
  166. TEST_F(SceneTests, SceneNotificationTest_ConnectAfterRenderPipelineAdded)
  167. {
  168. // Create scene with feature processor which enables scene notification handler
  169. SceneDescriptor sceneDesc;
  170. ScenePtr testScene = Scene::CreateScene(sceneDesc);
  171. testScene->Activate();
  172. RenderPipelineId pipelineId1 = RenderPipelineId("pipeline1");
  173. RenderPipelineId pipelineId2 = RenderPipelineId("pipeline2");
  174. RenderPipelineDescriptor pipelineDesc;
  175. pipelineDesc.m_name = pipelineId1.GetCStr();
  176. RenderPipelinePtr pipeline1 = RenderPipeline::CreateRenderPipeline(pipelineDesc);
  177. pipelineDesc.m_name = pipelineId2.GetCStr();
  178. RenderPipelinePtr pipeline2 = RenderPipeline::CreateRenderPipeline(pipelineDesc);
  179. // Do some render pipeline operations and keep render pipeline1 added
  180. testScene->AddRenderPipeline(pipeline1);
  181. testScene->AddRenderPipeline(pipeline2);
  182. testScene->RemoveRenderPipeline(pipelineId2);
  183. // Create a raster pass with viewTag "mainCamera"
  184. const RPI::PipelineViewTag viewTag{ "mainCamera" };
  185. PassDescriptor passDesc;
  186. AZStd::shared_ptr<PassTemplate> passTemplate = AZStd::make_shared<PassTemplate>();
  187. AZStd::shared_ptr<RasterPassData> passData = AZStd::make_shared<RasterPassData>();
  188. passData->m_drawListTag = "forward";
  189. passData->m_pipelineViewTag = viewTag.GetCStr();
  190. passTemplate->m_passData = passData;
  191. passDesc.m_passName = "raster";
  192. passDesc.m_passTemplate = passTemplate;
  193. Ptr<RasterPass> newPass = RasterPass::Create(passDesc);
  194. // Add new pass to render pipeline which should trigger render pipeline pass modify notification
  195. pipeline1->GetRootPass()->AddChild(newPass, true);
  196. // This function is called in every RPISystem render tick. We call it manually here to trigger the pass change notification
  197. pipeline1->UpdatePasses();
  198. // Set view to pipeline
  199. ViewPtr view = View::CreateView(AZ::Name("TestView"), RPI::View::UsageCamera);
  200. pipeline1->SetPersistentView(viewTag, view);
  201. // Enable the feature processor which has notification handler enabled
  202. TestFeatureProcessor1* feature = testScene->EnableFeatureProcessor<TestFeatureProcessor1>();
  203. EXPECT_TRUE(feature->m_pipelineCount == 1);
  204. EXPECT_TRUE(feature->m_lastPipeline == pipeline1.get());
  205. EXPECT_TRUE(feature->m_viewSetCount == 1);
  206. EXPECT_TRUE(feature->m_pipelineChangedCount == 0);
  207. testScene->Deactivate();
  208. }
  209. TEST_F(SceneTests, GetFeatureProcessor_ByInterface_CanModifyFeatureProcessorViaInterface)
  210. {
  211. // Create scene with one test feature processor
  212. SceneDescriptor sceneDesc;
  213. sceneDesc.m_featureProcessorNames.push_back(AZStd::string(TestFeatureProcessorImplementation::RTTI_TypeName()));
  214. ScenePtr testScene = Scene::CreateScene(sceneDesc);
  215. testScene->Activate();
  216. TestFeatureProcessorInterface* featureProcessorInterface = testScene->GetFeatureProcessor<TestFeatureProcessorInterface>();
  217. EXPECT_TRUE(featureProcessorInterface != nullptr);
  218. // Check that the pointer is valid
  219. int testValue = 7;
  220. featureProcessorInterface->SetValue(testValue);
  221. EXPECT_EQ(featureProcessorInterface->GetValue(), testValue);
  222. // Check that changes made by the interface apply to the underlying implemented feature processor
  223. TestFeatureProcessorImplementation* featureProcessorImplementation = testScene->GetFeatureProcessor<TestFeatureProcessorImplementation>();
  224. EXPECT_TRUE(featureProcessorInterface == featureProcessorImplementation);
  225. EXPECT_TRUE(featureProcessorImplementation->GetValue() == testValue);
  226. // Check that changes made by the implementation apply to the interface
  227. int anotherTestValue = 21;
  228. featureProcessorImplementation->SetValue(anotherTestValue);
  229. EXPECT_TRUE(featureProcessorInterface->GetValue() == anotherTestValue);
  230. // Check that the feature processor can be disabled
  231. testScene->DisableFeatureProcessor<TestFeatureProcessorImplementation>();
  232. EXPECT_TRUE(testScene->GetFeatureProcessor<TestFeatureProcessorInterface>() == nullptr);
  233. EXPECT_TRUE(testScene->GetFeatureProcessor<TestFeatureProcessorImplementation>() == nullptr);
  234. testScene->Deactivate();
  235. }
  236. TEST_F(SceneTests, GetFeatureProcessorByNameId_UsingStringForFeatureProcessorId_ReturnsValidFeatureProcessor)
  237. {
  238. // Create scene with one test feature processor
  239. SceneDescriptor sceneDesc;
  240. sceneDesc.m_featureProcessorNames.push_back(AZStd::string(TestFeatureProcessor1::RTTI_TypeName()));
  241. ScenePtr testScene = Scene::CreateScene(sceneDesc);
  242. testScene->Activate();
  243. EXPECT_TRUE(testScene->GetFeatureProcessor(FeatureProcessorId{ "TestFeatureProcessor1" }) != nullptr);
  244. testScene->Deactivate();
  245. }
  246. //
  247. // Two implementations of the same interface
  248. //
  249. TEST_F(SceneTests, EnableDisableFeatureProcessorByNameId_MultipleImplmentationsOfTheSameInterface_ReturnsValidFeatureProcessor)
  250. {
  251. SceneDescriptor sceneDesc;
  252. ScenePtr testScene = Scene::CreateScene(sceneDesc);
  253. testScene->Activate();
  254. // You can enable either implementation, as long as they are not both active in the same scene
  255. FeatureProcessor* firstImplementation = testScene->EnableFeatureProcessor(FeatureProcessorId{ TestFeatureProcessorImplementation::RTTI_TypeName() });
  256. EXPECT_TRUE(firstImplementation != nullptr);
  257. testScene->DisableFeatureProcessor(FeatureProcessorId{ TestFeatureProcessorImplementation::RTTI_TypeName() });
  258. FeatureProcessor* secondImplementation = testScene->EnableFeatureProcessor(FeatureProcessorId{ TestFeatureProcessorImplementation2::RTTI_TypeName() });
  259. EXPECT_TRUE(secondImplementation != nullptr);
  260. testScene->Deactivate();
  261. }
  262. TEST_F(SceneTests, EnableDisableFeatureProcessorByType_MultipleImplmentationsOfTheSameInterface_ReturnsValidFeatureProcessor)
  263. {
  264. SceneDescriptor sceneDesc;
  265. ScenePtr testScene = Scene::CreateScene(sceneDesc);
  266. testScene->Activate();
  267. // You can enable either implementation, as long as they are not both active in the same scene
  268. FeatureProcessor* firstImplementation = testScene->EnableFeatureProcessor<TestFeatureProcessorImplementation>();
  269. EXPECT_TRUE(firstImplementation != nullptr);
  270. testScene->DisableFeatureProcessor<TestFeatureProcessorImplementation>();
  271. FeatureProcessor* secondImplementation = testScene->EnableFeatureProcessor<TestFeatureProcessorImplementation2>();
  272. EXPECT_TRUE(secondImplementation != nullptr);
  273. testScene->Deactivate();
  274. }
  275. TEST_F(SceneTests, GetFeatureProcessorByNameId_MultipleImplmentationsOfTheSameInterface_ReturnsValidFeatureProcessor)
  276. {
  277. SceneDescriptor sceneDesc;
  278. ScenePtr testScene = Scene::CreateScene(sceneDesc);
  279. testScene->Activate();
  280. // You can get a feature processor via its name ID, no matter which implementation is enabled by the scene
  281. FeatureProcessor* firstImplementation = testScene->EnableFeatureProcessor<TestFeatureProcessorImplementation>();
  282. FeatureProcessor* featureProcessor = testScene->GetFeatureProcessor(FeatureProcessorId{ TestFeatureProcessorImplementation::RTTI_TypeName() });
  283. EXPECT_TRUE(firstImplementation == featureProcessor);
  284. testScene->DisableFeatureProcessor(FeatureProcessorId{ TestFeatureProcessorImplementation::RTTI_TypeName() });
  285. FeatureProcessor* secondImplementation = testScene->EnableFeatureProcessor<TestFeatureProcessorImplementation2>();
  286. featureProcessor = testScene->GetFeatureProcessor(FeatureProcessorId{ TestFeatureProcessorImplementation2::RTTI_TypeName() });
  287. EXPECT_TRUE(secondImplementation == featureProcessor);
  288. testScene->Deactivate();
  289. }
  290. TEST_F(SceneTests, GetFeatureProcessorByInterface_MultipleImplmentationsOfTheSameInterface_ReturnsValidFeatureProcessor)
  291. {
  292. SceneDescriptor sceneDesc;
  293. ScenePtr testScene = Scene::CreateScene(sceneDesc);
  294. testScene->Activate();
  295. // You can get a feature processor via its interface, no matter which implementation is enabled by the scene
  296. // as long as only one is enabled by the scene
  297. FeatureProcessor* firstImplementation = testScene->EnableFeatureProcessor<TestFeatureProcessorImplementation>();
  298. TestFeatureProcessorInterface* featureProcessorInterface = testScene->GetFeatureProcessor<TestFeatureProcessorInterface>();
  299. EXPECT_TRUE(firstImplementation == featureProcessorInterface);
  300. testScene->DisableFeatureProcessor(FeatureProcessorId{ TestFeatureProcessorImplementation::RTTI_TypeName() });
  301. FeatureProcessor* secondImplementation = testScene->EnableFeatureProcessor<TestFeatureProcessorImplementation2>();
  302. featureProcessorInterface = testScene->GetFeatureProcessor<TestFeatureProcessorInterface>();
  303. EXPECT_TRUE(secondImplementation == featureProcessorInterface);
  304. testScene->Deactivate();
  305. }
  306. //
  307. // Invalid cases
  308. //
  309. TEST_F(SceneTests, EnableFeatureProcessor_ByInterfaceName_FailsToEnable)
  310. {
  311. SceneDescriptor sceneDesc;
  312. ScenePtr testScene = Scene::CreateScene(sceneDesc);
  313. testScene->Activate();
  314. EXPECT_TRUE(testScene->GetFeatureProcessor<TestFeatureProcessorImplementation>() == nullptr);
  315. EXPECT_TRUE(testScene->GetFeatureProcessor<TestFeatureProcessorInterface>() == nullptr);
  316. testScene->Deactivate();
  317. }
  318. TEST_F(SceneTests, DisableFeatureProcessor_ByInterface_FailsToDisable)
  319. {
  320. SceneDescriptor sceneDesc;
  321. sceneDesc.m_featureProcessorNames.push_back(AZStd::string(TestFeatureProcessorImplementation::RTTI_TypeName()));
  322. ScenePtr testScene = Scene::CreateScene(sceneDesc);
  323. testScene->Activate();
  324. testScene->DisableFeatureProcessor(FeatureProcessorId{ TestFeatureProcessorInterface::RTTI_TypeName() });
  325. EXPECT_TRUE(testScene->GetFeatureProcessor<TestFeatureProcessorImplementation>() != nullptr);
  326. EXPECT_TRUE(testScene->GetFeatureProcessor<TestFeatureProcessorInterface>() != nullptr);
  327. testScene->Deactivate();
  328. }
  329. TEST_F(SceneTests, EnableFeatureProcessor_MultipleImplmentationsOfTheSameInterface_FailsToEnable)
  330. {
  331. SceneDescriptor sceneDesc;
  332. ScenePtr testScene = Scene::CreateScene(sceneDesc);
  333. testScene->Activate();
  334. // You can enable one implementation of a feature processor on a scene
  335. FeatureProcessor* firstImplementation = testScene->EnableFeatureProcessor(FeatureProcessorId{ TestFeatureProcessorImplementation::RTTI_TypeName() });
  336. EXPECT_TRUE(firstImplementation != nullptr);
  337. // But you can't enable two implementations of the same interface in one scene at the same time.
  338. // Otherwise, when you get a feature processor by its interface, the scene wouldn't know which one to return.
  339. AZ_TEST_START_ASSERTTEST;
  340. FeatureProcessor* secondImplementation = testScene->EnableFeatureProcessor(FeatureProcessorId{ TestFeatureProcessorImplementation2::RTTI_TypeName() });
  341. AZ_TEST_STOP_ASSERTTEST(1);
  342. // If another implementation that uses the same interface exists, that will be the feature processor that is returned
  343. EXPECT_TRUE(secondImplementation == firstImplementation);
  344. testScene->Deactivate();
  345. }
  346. } // namespace UnitTest