TerrainBulkQueryTests.cpp 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007
  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/std/parallel/semaphore.h>
  9. #include <AzTest/AzTest.h>
  10. #include <TerrainSystem/TerrainSystem.h>
  11. #include <TerrainTestFixtures.h>
  12. namespace UnitTest::TerrainTest
  13. {
  14. /* The TerrainBulkQueryTest suite of tests exist to verify that all of our different query APIs produce the same results.
  15. * These tests were added after discovering that the async queries could sometimes produce intermittently incorrect results due
  16. * to a lack of proper thread safety. However, it's also possible that optimizations to the different queries could accidentally
  17. * produce different results as well, so it's good to have this safety net here.
  18. */
  19. class TerrainBulkQueryTest
  20. : public TerrainTestFixture
  21. {
  22. protected:
  23. // Use the ProcessHeightsFromRegion API as our baseline that we'll compare the other query APIs against.
  24. void GenerateBaselineHeightData(
  25. const AZ::Aabb& inputQueryRegion,
  26. const AZ::Vector2& inputQueryStepSize,
  27. AzFramework::Terrain::TerrainDataRequests::Sampler sampler,
  28. AZStd::vector<AZ::Vector3>& queryPositions,
  29. AZStd::vector<AZ::Vector3>& resultPositions,
  30. AZStd::vector<bool>& resultExistsFlags)
  31. {
  32. queryPositions.clear();
  33. resultPositions.clear();
  34. resultExistsFlags.clear();
  35. auto perPositionCallback = [&queryPositions, &resultPositions, &resultExistsFlags](
  36. [[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex,
  37. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  38. {
  39. queryPositions.emplace_back(surfacePoint.m_position.GetX(), surfacePoint.m_position.GetY(), 0.0f);
  40. resultPositions.emplace_back(surfacePoint.m_position);
  41. resultExistsFlags.emplace_back(terrainExists);
  42. };
  43. AzFramework::Terrain::TerrainQueryRegion queryRegion =
  44. AzFramework::Terrain::TerrainQueryRegion::CreateFromAabbAndStepSize(inputQueryRegion, inputQueryStepSize);
  45. AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
  46. &AzFramework::Terrain::TerrainDataRequests::QueryRegion, queryRegion,
  47. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::Heights, perPositionCallback, sampler);
  48. EXPECT_EQ(queryPositions.size(), resultPositions.size());
  49. }
  50. // Compare two sets of output data and verify that they match.
  51. void ComparePositionData(const AZStd::vector<AZ::Vector3>& baselineValues, const AZStd::vector<bool>& baselineExistsFlags,
  52. const AZStd::vector<AZ::Vector3>& comparisonValues, const AZStd::vector<bool>& comparisonExistsFlags)
  53. {
  54. // Verify that we have the same quantity of results in both sets.
  55. ASSERT_EQ(baselineValues.size(), comparisonValues.size());
  56. // Verify that every value is found exactly once in each set. The two sets might not have the values in the same order though,
  57. // so we need to search for each value, verify it's found, and verify that it hadn't previously been found.
  58. AZStd::vector<bool> matchFound(baselineValues.size(), false);
  59. for (size_t comparisonIndex = 0; comparisonIndex < comparisonValues.size(); comparisonIndex++)
  60. {
  61. auto foundValue = AZStd::find(baselineValues.begin(), baselineValues.end(), comparisonValues[comparisonIndex]);
  62. EXPECT_NE(foundValue, baselineValues.end());
  63. if (foundValue != baselineValues.end())
  64. {
  65. size_t foundIndex = foundValue - baselineValues.begin();
  66. EXPECT_FALSE(matchFound[foundIndex]);
  67. EXPECT_EQ(baselineExistsFlags[foundIndex], comparisonExistsFlags[comparisonIndex]);
  68. matchFound[foundIndex] = true;
  69. }
  70. }
  71. }
  72. // Use the ProcessNormalsFromRegion API as our baseline that we'll compare the other query APIs against.
  73. void GenerateBaselineNormalData(
  74. const AZ::Aabb& inputQueryRegion,
  75. const AZ::Vector2& inputQueryStepSize,
  76. AzFramework::Terrain::TerrainDataRequests::Sampler sampler,
  77. AZStd::vector<AZ::Vector3>& queryPositions,
  78. AZStd::vector<AZ::Vector3>& resultNormals,
  79. AZStd::vector<bool>& resultExistsFlags)
  80. {
  81. queryPositions.clear();
  82. resultNormals.clear();
  83. resultExistsFlags.clear();
  84. auto perPositionCallback = [&queryPositions, &resultNormals, &resultExistsFlags](
  85. [[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex,
  86. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  87. {
  88. queryPositions.emplace_back(surfacePoint.m_position.GetX(), surfacePoint.m_position.GetY(), 0.0f);
  89. resultNormals.emplace_back(surfacePoint.m_normal);
  90. resultExistsFlags.emplace_back(terrainExists);
  91. };
  92. AzFramework::Terrain::TerrainQueryRegion queryRegion =
  93. AzFramework::Terrain::TerrainQueryRegion::CreateFromAabbAndStepSize(inputQueryRegion, inputQueryStepSize);
  94. AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
  95. &AzFramework::Terrain::TerrainDataRequests::QueryRegion, queryRegion,
  96. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::Normals, perPositionCallback, sampler);
  97. EXPECT_EQ(queryPositions.size(), resultNormals.size());
  98. }
  99. // Compare two sets of output data and verify that they match.
  100. void CompareNormalData(
  101. const AZStd::vector<AZ::Vector3>& baselineQueryPositions,
  102. const AZStd::vector<AZ::Vector3>& baselineValues,
  103. const AZStd::vector<bool>& baselineExistsFlags,
  104. const AZStd::vector<AZ::Vector3>& comparisonQueryPositions,
  105. const AZStd::vector<AZ::Vector3>& comparisonValues,
  106. const AZStd::vector<bool>& comparisonExistsFlags)
  107. {
  108. // Verify that we have the same quantity of results in both sets.
  109. ASSERT_EQ(baselineValues.size(), comparisonValues.size());
  110. // Verify that every value is found exactly once in each set. The two sets might not have the values in the same order though,
  111. // so we need to search for each value, verify it's found, and verify that it hadn't previously been found.
  112. // Also, since normals are easy to duplicate, we'll search by query positions to find the normals to compare.
  113. AZStd::vector<bool> matchFound(baselineValues.size(), false);
  114. for (size_t comparisonIndex = 0; comparisonIndex < comparisonQueryPositions.size(); comparisonIndex++)
  115. {
  116. auto& comparisonPosition = comparisonQueryPositions[comparisonIndex];
  117. auto foundPosition = AZStd::find(baselineQueryPositions.begin(), baselineQueryPositions.end(), comparisonPosition);
  118. EXPECT_NE(foundPosition, baselineQueryPositions.end());
  119. if (foundPosition != baselineQueryPositions.end())
  120. {
  121. size_t foundIndex = foundPosition - baselineQueryPositions.begin();
  122. EXPECT_FALSE(matchFound[foundIndex]);
  123. EXPECT_EQ(baselineValues[foundIndex], comparisonValues[comparisonIndex]);
  124. EXPECT_EQ(baselineExistsFlags[foundIndex], comparisonExistsFlags[comparisonIndex]);
  125. matchFound[foundIndex] = true;
  126. }
  127. }
  128. }
  129. // Use the ProcessSurfaceWeightsFromRegion API as our baseline that we'll compare the other query APIs against.
  130. void GenerateBaselineSurfaceWeightData(
  131. const AZ::Aabb& inputQueryRegion,
  132. const AZ::Vector2& inputQueryStepSize,
  133. AzFramework::Terrain::TerrainDataRequests::Sampler sampler,
  134. AZStd::vector<AZ::Vector3>& queryPositions,
  135. AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList>& resultWeights)
  136. {
  137. queryPositions.clear();
  138. resultWeights.clear();
  139. auto perPositionCallback = [&queryPositions, &resultWeights](
  140. [[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex,
  141. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  142. {
  143. queryPositions.emplace_back(surfacePoint.m_position.GetX(), surfacePoint.m_position.GetY(), 0.0f);
  144. resultWeights.emplace_back(surfacePoint.m_surfaceTags);
  145. // For these unit tests, we expect every point queried to have valid terrain data.
  146. EXPECT_TRUE(terrainExists);
  147. };
  148. AzFramework::Terrain::TerrainQueryRegion queryRegion =
  149. AzFramework::Terrain::TerrainQueryRegion::CreateFromAabbAndStepSize(inputQueryRegion, inputQueryStepSize);
  150. AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
  151. &AzFramework::Terrain::TerrainDataRequests::QueryRegion, queryRegion,
  152. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::SurfaceData, perPositionCallback, sampler);
  153. EXPECT_EQ(queryPositions.size(), resultWeights.size());
  154. }
  155. // Compare two sets of output data and verify that they match.
  156. void CompareSurfaceWeightData(
  157. const AZStd::vector<AZ::Vector3>& baselineQueryPositions,
  158. const AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList>& baselineValues,
  159. const AZStd::vector<AZ::Vector3>& comparisonQueryPositions,
  160. const AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList>& comparisonValues)
  161. {
  162. // Verify that we have the same quantity of results in both sets.
  163. ASSERT_EQ(baselineValues.size(), comparisonValues.size());
  164. // Verify that every value is found exactly once in each set. The two sets might not have the values in the same order though,
  165. // so we need to search for each value, verify it's found, and verify that it hadn't previously been found.
  166. // Also, since surface weight lists are easy to duplicate, we'll search by query positions to find the weight lists to compare.
  167. AZStd::vector<bool> matchFound(baselineValues.size(), false);
  168. for (size_t comparisonIndex = 0; comparisonIndex < comparisonQueryPositions.size(); comparisonIndex++)
  169. {
  170. auto& comparisonPosition = comparisonQueryPositions[comparisonIndex];
  171. auto foundPosition = AZStd::find(baselineQueryPositions.begin(), baselineQueryPositions.end(), comparisonPosition);
  172. EXPECT_NE(foundPosition, baselineQueryPositions.end());
  173. if (foundPosition != baselineQueryPositions.end())
  174. {
  175. size_t foundIndex = foundPosition - baselineQueryPositions.begin();
  176. EXPECT_FALSE(matchFound[foundIndex]);
  177. EXPECT_EQ(baselineValues[foundIndex], comparisonValues[comparisonIndex]);
  178. matchFound[foundIndex] = true;
  179. }
  180. }
  181. }
  182. // Use the ProcessSurfacePointsFromRegion API as our baseline that we'll compare the other query APIs against.
  183. void GenerateBaselineSurfacePointData(
  184. const AZ::Aabb& inputQueryRegion,
  185. const AZ::Vector2& inputQueryStepSize,
  186. AzFramework::Terrain::TerrainDataRequests::Sampler sampler,
  187. AZStd::vector<AZ::Vector3>& queryPositions,
  188. AZStd::vector<AzFramework::SurfaceData::SurfacePoint>& resultPoints,
  189. AZStd::vector<bool>& resultExistsFlags)
  190. {
  191. queryPositions.clear();
  192. resultPoints.clear();
  193. resultExistsFlags.clear();
  194. auto perPositionCallback = [&queryPositions, &resultPoints, &resultExistsFlags](
  195. [[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex,
  196. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  197. {
  198. queryPositions.emplace_back(surfacePoint.m_position.GetX(), surfacePoint.m_position.GetY(), 0.0f);
  199. resultPoints.emplace_back(surfacePoint);
  200. resultExistsFlags.emplace_back(terrainExists);
  201. };
  202. AzFramework::Terrain::TerrainQueryRegion queryRegion =
  203. AzFramework::Terrain::TerrainQueryRegion::CreateFromAabbAndStepSize(inputQueryRegion, inputQueryStepSize);
  204. AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
  205. &AzFramework::Terrain::TerrainDataRequests::QueryRegion, queryRegion,
  206. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::All, perPositionCallback, sampler);
  207. EXPECT_EQ(queryPositions.size(), resultPoints.size());
  208. }
  209. // Compare two sets of output data and verify that they match.
  210. void CompareSurfacePointData(
  211. const AZStd::vector<AzFramework::SurfaceData::SurfacePoint>& baselineValues,
  212. const AZStd::vector<bool>& baselineExistsFlags,
  213. const AZStd::vector<AzFramework::SurfaceData::SurfacePoint>& comparisonValues,
  214. const AZStd::vector<bool>& comparisonExistsFlags)
  215. {
  216. // Verify that we have the same quantity of results in both sets.
  217. ASSERT_EQ(baselineValues.size(), comparisonValues.size());
  218. // Verify that every value is found exactly once in each set. The two sets might not have the values in the same order though,
  219. // so we need to search for each value, verify it's found, and verify that it hadn't previously been found.
  220. AZStd::vector<bool> matchFound(baselineValues.size(), false);
  221. for (size_t comparisonIndex = 0; comparisonIndex < comparisonValues.size(); comparisonIndex++)
  222. {
  223. const auto& comparisonValue = comparisonValues[comparisonIndex];
  224. auto foundValue = AZStd::find_if(
  225. baselineValues.begin(), baselineValues.end(),
  226. [&comparisonValue](const AzFramework::SurfaceData::SurfacePoint& baselineValue) -> bool
  227. {
  228. return (baselineValue.m_position == comparisonValue.m_position)
  229. && (baselineValue.m_normal == comparisonValue.m_normal)
  230. && (baselineValue.m_surfaceTags == comparisonValue.m_surfaceTags);
  231. });
  232. EXPECT_NE(foundValue, baselineValues.end());
  233. if (foundValue != baselineValues.end())
  234. {
  235. size_t foundIndex = foundValue - baselineValues.begin();
  236. EXPECT_FALSE(matchFound[foundIndex]);
  237. EXPECT_EQ(baselineExistsFlags[foundIndex], comparisonExistsFlags[comparisonIndex]);
  238. matchFound[foundIndex] = true;
  239. if (baselineExistsFlags[foundIndex] != comparisonExistsFlags[comparisonIndex])
  240. {
  241. matchFound[foundIndex] = true;
  242. }
  243. }
  244. }
  245. }
  246. AZStd::shared_ptr<AzFramework::Terrain::QueryAsyncParams> CreateTestAsyncParams()
  247. {
  248. auto params = AZStd::make_shared<AzFramework::Terrain::QueryAsyncParams>();
  249. // Set the number of jobs > 1 so that we have parallel queries that execute.
  250. params->m_desiredNumberOfJobs = 4;
  251. params->m_completionCallback =
  252. [this]([[maybe_unused]] AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> context)
  253. {
  254. // Notify the main test thread that the query has completed.
  255. m_queryCompletionEvent.release();
  256. };
  257. return params;
  258. }
  259. // Set up the arbitrary terrain world parameters that we'll use for verifying our queries match.
  260. const static inline float TerrainSize = 32.0f;
  261. const static inline float TerrainQueryResolution = 1.0f;
  262. const static inline uint32_t TerrainNumSurfaces = 3;
  263. const static inline AZ::Aabb TerrainWorldBounds =
  264. AZ::Aabb::CreateFromMinMax(AZ::Vector3(-TerrainSize / 2.0f), AZ::Vector3(TerrainSize / 2.0f));
  265. // Set up the query parameters that we'll use for all our queries
  266. const static inline AZ::Aabb QueryBounds = TerrainWorldBounds;
  267. const static inline float QueryStepSizeX = TerrainQueryResolution / 2.0f;
  268. const static inline float QueryStepSizeY = TerrainQueryResolution / 2.0f;
  269. const static inline uint32_t ExpectedResultCount =
  270. aznumeric_cast<uint32_t>((TerrainSize / QueryStepSizeX) * (TerrainSize / QueryStepSizeY));
  271. AZ::Vector2 QueryStepSize = AZ::Vector2(QueryStepSizeX, QueryStepSizeY);
  272. // Semaphore for use in async tests.
  273. AZStd::binary_semaphore m_queryCompletionEvent;
  274. };
  275. // -----------------------------------------------------------------------------
  276. // Compare Height Query APIs
  277. TEST_F(TerrainBulkQueryTest, ProcessHeightsFromRegionAndProcessHeightsFromListProduceSameResults)
  278. {
  279. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  280. for (auto sampler : {
  281. AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR,
  282. AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  283. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT
  284. })
  285. {
  286. // Gather all our initial results from calling Process*FromRegion
  287. AZStd::vector<AZ::Vector3> queryPositions;
  288. AZStd::vector<AZ::Vector3> baselineResultPositions;
  289. AZStd::vector<bool> baselineExistsFlags;
  290. GenerateBaselineHeightData(QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultPositions, baselineExistsFlags);
  291. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  292. // Gather results from Process*FromList
  293. AZStd::vector<AZ::Vector3> comparisonResultPositions;
  294. AZStd::vector<bool> comparisonExistsFlags;
  295. auto listPositionCallback = [&comparisonResultPositions, &comparisonExistsFlags]
  296. (const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  297. {
  298. comparisonResultPositions.emplace_back(surfacePoint.m_position);
  299. comparisonExistsFlags.emplace_back(terrainExists);
  300. };
  301. AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
  302. &AzFramework::Terrain::TerrainDataRequests::QueryList,
  303. queryPositions, AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::Heights, listPositionCallback, sampler);
  304. // Compare the results
  305. ComparePositionData(baselineResultPositions, baselineExistsFlags, comparisonResultPositions, comparisonExistsFlags);
  306. }
  307. DestroyTestTerrainSystem();
  308. }
  309. TEST_F(TerrainBulkQueryTest, ProcessHeightsFromRegionAndGetHeightProduceSameResults)
  310. {
  311. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  312. for (auto sampler :
  313. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  314. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  315. {
  316. // Gather all our initial results from calling Process*FromRegion
  317. AZStd::vector<AZ::Vector3> queryPositions;
  318. AZStd::vector<AZ::Vector3> baselineResultPositions;
  319. AZStd::vector<bool> baselineExistsFlags;
  320. GenerateBaselineHeightData(QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultPositions, baselineExistsFlags);
  321. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  322. // Gather results from Get*
  323. AZStd::vector<AZ::Vector3> comparisonResultPositions;
  324. AZStd::vector<bool> comparisonExistsFlags;
  325. float worldMinZ = TerrainWorldBounds.GetMin().GetZ();
  326. for (auto& position : queryPositions)
  327. {
  328. float terrainHeight = worldMinZ;
  329. bool terrainExists = false;
  330. AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
  331. terrainHeight, &AzFramework::Terrain::TerrainDataRequests::GetHeight, position, sampler, &terrainExists);
  332. comparisonResultPositions.emplace_back(position.GetX(), position.GetY(), terrainHeight);
  333. comparisonExistsFlags.emplace_back(terrainExists);
  334. }
  335. // Compare the results
  336. ComparePositionData(baselineResultPositions, baselineExistsFlags, comparisonResultPositions, comparisonExistsFlags);
  337. }
  338. DestroyTestTerrainSystem();
  339. }
  340. TEST_F(TerrainBulkQueryTest, ProcessHeightsFromRegionAndProcessHeightsFromRegionAsyncProduceSameResults)
  341. {
  342. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  343. for (auto sampler :
  344. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  345. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  346. {
  347. // Gather all our initial results from calling Process*FromRegion
  348. AZStd::vector<AZ::Vector3> queryPositions;
  349. AZStd::vector<AZ::Vector3> baselineResultPositions;
  350. AZStd::vector<bool> baselineExistsFlags;
  351. GenerateBaselineHeightData(QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultPositions, baselineExistsFlags);
  352. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  353. // Gather results from Process*FromRegionAsync
  354. AZStd::vector<AZ::Vector3> comparisonResultPositions;
  355. AZStd::vector<bool> comparisonExistsFlags;
  356. AZStd::mutex outputMutex;
  357. auto regionPositionCallback = [&comparisonResultPositions, &comparisonExistsFlags, &outputMutex](
  358. [[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex,
  359. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  360. {
  361. // Make sure only one thread can add its result at a time.
  362. AZStd::scoped_lock lock(outputMutex);
  363. comparisonResultPositions.emplace_back(surfacePoint.m_position);
  364. comparisonExistsFlags.emplace_back(terrainExists);
  365. };
  366. auto params = CreateTestAsyncParams();
  367. AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> jobContext;
  368. AzFramework::Terrain::TerrainQueryRegion queryRegion =
  369. AzFramework::Terrain::TerrainQueryRegion::CreateFromAabbAndStepSize(QueryBounds, QueryStepSize);
  370. AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
  371. jobContext, &AzFramework::Terrain::TerrainDataRequests::QueryRegionAsync, queryRegion,
  372. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::Heights, regionPositionCallback, sampler, params);
  373. // Wait for the async query to complete
  374. m_queryCompletionEvent.acquire();
  375. // Compare the results
  376. ComparePositionData(baselineResultPositions, baselineExistsFlags, comparisonResultPositions, comparisonExistsFlags);
  377. }
  378. DestroyTestTerrainSystem();
  379. }
  380. TEST_F(TerrainBulkQueryTest, ProcessHeightsFromRegionAndProcessHeightsFromListAsyncProduceSameResults)
  381. {
  382. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  383. for (auto sampler :
  384. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  385. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  386. {
  387. // Gather all our initial results from calling Process*FromRegion
  388. AZStd::vector<AZ::Vector3> queryPositions;
  389. AZStd::vector<AZ::Vector3> baselineResultPositions;
  390. AZStd::vector<bool> baselineExistsFlags;
  391. GenerateBaselineHeightData(QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultPositions, baselineExistsFlags);
  392. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  393. // Gather results from Process*FromListAsync
  394. AZStd::vector<AZ::Vector3> comparisonResultPositions;
  395. AZStd::vector<bool> comparisonExistsFlags;
  396. AZStd::mutex outputMutex;
  397. auto listPositionCallback = [&comparisonResultPositions, &comparisonExistsFlags, &outputMutex](
  398. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  399. {
  400. // Make sure only one thread can add its result at a time.
  401. AZStd::scoped_lock lock(outputMutex);
  402. comparisonResultPositions.emplace_back(surfacePoint.m_position);
  403. comparisonExistsFlags.emplace_back(terrainExists);
  404. };
  405. auto params = CreateTestAsyncParams();
  406. AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> jobContext;
  407. AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
  408. jobContext, &AzFramework::Terrain::TerrainDataRequests::QueryListAsync, queryPositions,
  409. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::Heights,
  410. listPositionCallback, sampler, params);
  411. // Wait for the async query to complete
  412. m_queryCompletionEvent.acquire();
  413. // Compare the results
  414. ComparePositionData(baselineResultPositions, baselineExistsFlags, comparisonResultPositions, comparisonExistsFlags);
  415. }
  416. DestroyTestTerrainSystem();
  417. }
  418. // -----------------------------------------------------------------------------
  419. // Compare Normal Query APIs
  420. TEST_F(TerrainBulkQueryTest, ProcessNormalsFromRegionAndProcessNormalsFromListProduceSameResults)
  421. {
  422. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  423. for (auto sampler :
  424. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  425. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  426. {
  427. // Gather all our initial results from calling Process*FromRegion
  428. AZStd::vector<AZ::Vector3> queryPositions;
  429. AZStd::vector<AZ::Vector3> baselineResultNormals;
  430. AZStd::vector<bool> baselineExistsFlags;
  431. GenerateBaselineNormalData(QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultNormals, baselineExistsFlags);
  432. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  433. // Gather results from Process*FromList
  434. AZStd::vector<AZ::Vector3> comparisonResultPositions;
  435. AZStd::vector<AZ::Vector3> comparisonResultNormals;
  436. AZStd::vector<bool> comparisonExistsFlags;
  437. auto listNormalCallback = [&comparisonResultPositions, &comparisonResultNormals, &comparisonExistsFlags](
  438. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  439. {
  440. comparisonResultPositions.emplace_back(surfacePoint.m_position);
  441. comparisonResultNormals.emplace_back(surfacePoint.m_normal);
  442. comparisonExistsFlags.emplace_back(terrainExists);
  443. };
  444. AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
  445. &AzFramework::Terrain::TerrainDataRequests::QueryList, queryPositions,
  446. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::Normals, listNormalCallback, sampler);
  447. // Compare the results
  448. CompareNormalData(queryPositions, baselineResultNormals, baselineExistsFlags,
  449. comparisonResultPositions, comparisonResultNormals, comparisonExistsFlags);
  450. }
  451. DestroyTestTerrainSystem();
  452. }
  453. TEST_F(TerrainBulkQueryTest, ProcessNormalsFromRegionAndGetNormalProduceSameResults)
  454. {
  455. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  456. for (auto sampler :
  457. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  458. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  459. {
  460. // Gather all our initial results from calling Process*FromRegion
  461. AZStd::vector<AZ::Vector3> queryPositions;
  462. AZStd::vector<AZ::Vector3> baselineResultNormals;
  463. AZStd::vector<bool> baselineExistsFlags;
  464. GenerateBaselineNormalData(QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultNormals, baselineExistsFlags);
  465. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  466. // Gather results from Get*
  467. AZStd::vector<AZ::Vector3> comparisonResultNormals;
  468. AZStd::vector<bool> comparisonExistsFlags;
  469. for (auto& position : queryPositions)
  470. {
  471. AZ::Vector3 terrainNormal = AZ::Vector3::CreateZero();
  472. bool terrainExists = false;
  473. AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
  474. terrainNormal, &AzFramework::Terrain::TerrainDataRequests::GetNormal, position, sampler, &terrainExists);
  475. comparisonResultNormals.emplace_back(terrainNormal);
  476. comparisonExistsFlags.emplace_back(terrainExists);
  477. }
  478. // Compare the results
  479. CompareNormalData(queryPositions, baselineResultNormals, baselineExistsFlags,
  480. queryPositions, comparisonResultNormals, comparisonExistsFlags);
  481. }
  482. DestroyTestTerrainSystem();
  483. }
  484. TEST_F(TerrainBulkQueryTest, ProcessNormalsFromRegionAndProcessNormalsFromRegionAsyncProduceSameResults)
  485. {
  486. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  487. for (auto sampler :
  488. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  489. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  490. {
  491. // Gather all our initial results from calling Process*FromRegion
  492. AZStd::vector<AZ::Vector3> queryPositions;
  493. AZStd::vector<AZ::Vector3> baselineResultNormals;
  494. AZStd::vector<bool> baselineExistsFlags;
  495. GenerateBaselineNormalData(QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultNormals, baselineExistsFlags);
  496. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  497. // Gather results from Process*FromRegionAsync
  498. AZStd::vector<AZ::Vector3> comparisonResultPositions;
  499. AZStd::vector<AZ::Vector3> comparisonResultNormals;
  500. AZStd::vector<bool> comparisonExistsFlags;
  501. AZStd::mutex outputMutex;
  502. auto regionPositionCallback = [&comparisonResultPositions, &comparisonResultNormals, &comparisonExistsFlags, &outputMutex](
  503. [[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex,
  504. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  505. {
  506. // Make sure only one thread can add its result at a time.
  507. AZStd::scoped_lock lock(outputMutex);
  508. comparisonResultPositions.emplace_back(surfacePoint.m_position);
  509. comparisonResultNormals.emplace_back(surfacePoint.m_normal);
  510. comparisonExistsFlags.emplace_back(terrainExists);
  511. };
  512. auto params = CreateTestAsyncParams();
  513. AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> jobContext;
  514. AzFramework::Terrain::TerrainQueryRegion queryRegion =
  515. AzFramework::Terrain::TerrainQueryRegion::CreateFromAabbAndStepSize(QueryBounds, QueryStepSize);
  516. AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
  517. jobContext, &AzFramework::Terrain::TerrainDataRequests::QueryRegionAsync, queryRegion,
  518. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::Normals, regionPositionCallback, sampler, params);
  519. // Wait for the async query to complete
  520. m_queryCompletionEvent.acquire();
  521. // Compare the results
  522. CompareNormalData(queryPositions, baselineResultNormals, baselineExistsFlags,
  523. comparisonResultPositions, comparisonResultNormals, comparisonExistsFlags);
  524. }
  525. DestroyTestTerrainSystem();
  526. }
  527. TEST_F(TerrainBulkQueryTest, ProcessNormalsFromRegionAndProcessNormalsFromListAsyncProduceSameResults)
  528. {
  529. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  530. for (auto sampler :
  531. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  532. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  533. {
  534. // Gather all our initial results from calling Process*FromRegion
  535. AZStd::vector<AZ::Vector3> queryPositions;
  536. AZStd::vector<AZ::Vector3> baselineResultNormals;
  537. AZStd::vector<bool> baselineExistsFlags;
  538. GenerateBaselineNormalData(QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultNormals, baselineExistsFlags);
  539. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  540. // Gather results from Process*FromListAsync
  541. AZStd::vector<AZ::Vector3> comparisonResultPositions;
  542. AZStd::vector<AZ::Vector3> comparisonResultNormals;
  543. AZStd::vector<bool> comparisonExistsFlags;
  544. AZStd::mutex outputMutex;
  545. auto listPositionCallback = [&comparisonResultPositions, &comparisonResultNormals, &comparisonExistsFlags, &outputMutex](
  546. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  547. {
  548. // Make sure only one thread can add its result at a time.
  549. AZStd::scoped_lock lock(outputMutex);
  550. comparisonResultPositions.emplace_back(surfacePoint.m_position);
  551. comparisonResultNormals.emplace_back(surfacePoint.m_normal);
  552. comparisonExistsFlags.emplace_back(terrainExists);
  553. };
  554. auto params = CreateTestAsyncParams();
  555. AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> jobContext;
  556. AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
  557. jobContext, &AzFramework::Terrain::TerrainDataRequests::QueryListAsync, queryPositions,
  558. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::Normals, listPositionCallback,
  559. sampler, params);
  560. // Wait for the async query to complete
  561. m_queryCompletionEvent.acquire();
  562. // Compare the results
  563. CompareNormalData(queryPositions, baselineResultNormals, baselineExistsFlags,
  564. comparisonResultPositions, comparisonResultNormals, comparisonExistsFlags);
  565. }
  566. DestroyTestTerrainSystem();
  567. }
  568. // -----------------------------------------------------------------------------
  569. // Compare Surface Weight Query APIs
  570. TEST_F(TerrainBulkQueryTest, ProcessSurfaceWeightsFromRegionAndProcessSurfaceWeightsFromListProduceSameResults)
  571. {
  572. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  573. for (auto sampler :
  574. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  575. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  576. {
  577. // Gather all our initial results from calling Process*FromRegion
  578. AZStd::vector<AZ::Vector3> queryPositions;
  579. AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList> baselineResultWeights;
  580. GenerateBaselineSurfaceWeightData(QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultWeights);
  581. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  582. // Gather results from Process*FromList
  583. AZStd::vector<AZ::Vector3> comparisonResultPositions;
  584. AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList> comparisonResultWeights;
  585. auto listWeightsCallback = [&comparisonResultPositions, &comparisonResultWeights](
  586. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  587. {
  588. comparisonResultPositions.emplace_back(surfacePoint.m_position);
  589. comparisonResultWeights.emplace_back(surfacePoint.m_surfaceTags);
  590. EXPECT_TRUE(terrainExists);
  591. };
  592. AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
  593. &AzFramework::Terrain::TerrainDataRequests::QueryList, queryPositions,
  594. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::SurfaceData, listWeightsCallback, sampler);
  595. // Compare the results
  596. CompareSurfaceWeightData(queryPositions, baselineResultWeights, comparisonResultPositions, comparisonResultWeights);
  597. }
  598. DestroyTestTerrainSystem();
  599. }
  600. TEST_F(TerrainBulkQueryTest, ProcessSurfaceWeightsFromRegionAndGetSurfaceWeightsProduceSameResults)
  601. {
  602. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  603. for (auto sampler :
  604. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  605. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  606. {
  607. // Gather all our initial results from calling Process*FromRegion
  608. AZStd::vector<AZ::Vector3> queryPositions;
  609. AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList> baselineResultWeights;
  610. GenerateBaselineSurfaceWeightData(QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultWeights);
  611. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  612. // Gather results from Get*
  613. AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList> comparisonResultWeights;
  614. AzFramework::SurfaceData::SurfaceTagWeightList terrainWeights;
  615. for (auto& position : queryPositions)
  616. {
  617. bool terrainExists = false;
  618. AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
  619. &AzFramework::Terrain::TerrainDataRequests::GetSurfaceWeights, position, terrainWeights, sampler, &terrainExists);
  620. comparisonResultWeights.emplace_back(terrainWeights);
  621. EXPECT_TRUE(terrainExists);
  622. }
  623. // Compare the results
  624. CompareSurfaceWeightData(queryPositions, baselineResultWeights, queryPositions, comparisonResultWeights);
  625. }
  626. DestroyTestTerrainSystem();
  627. }
  628. TEST_F(TerrainBulkQueryTest, ProcessSurfaceWeightsFromRegionAndProcessSurfaceWeightsFromRegionAsyncProduceSameResults)
  629. {
  630. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  631. for (auto sampler :
  632. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  633. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  634. {
  635. // Gather all our initial results from calling Process*FromRegion
  636. AZStd::vector<AZ::Vector3> queryPositions;
  637. AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList> baselineResultWeights;
  638. GenerateBaselineSurfaceWeightData(QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultWeights);
  639. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  640. // Gather results from Process*FromRegionAsync
  641. AZStd::vector<AZ::Vector3> comparisonResultPositions;
  642. AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList> comparisonResultWeights;
  643. AZStd::mutex outputMutex;
  644. auto perPositionCallback = [&comparisonResultPositions, &comparisonResultWeights, &outputMutex](
  645. [[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex,
  646. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  647. {
  648. // Make sure only one thread can add its result at a time.
  649. AZStd::scoped_lock lock(outputMutex);
  650. comparisonResultPositions.emplace_back(surfacePoint.m_position);
  651. comparisonResultWeights.emplace_back(surfacePoint.m_surfaceTags);
  652. EXPECT_TRUE(terrainExists);
  653. };
  654. auto params = CreateTestAsyncParams();
  655. AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> jobContext;
  656. AzFramework::Terrain::TerrainQueryRegion queryRegion =
  657. AzFramework::Terrain::TerrainQueryRegion::CreateFromAabbAndStepSize(QueryBounds, QueryStepSize);
  658. AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
  659. jobContext, &AzFramework::Terrain::TerrainDataRequests::QueryRegionAsync, queryRegion,
  660. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::SurfaceData, perPositionCallback, sampler, params);
  661. // Wait for the async query to complete
  662. m_queryCompletionEvent.acquire();
  663. // Compare the results
  664. CompareSurfaceWeightData(queryPositions, baselineResultWeights, comparisonResultPositions, comparisonResultWeights);
  665. }
  666. DestroyTestTerrainSystem();
  667. }
  668. TEST_F(TerrainBulkQueryTest, ProcessSurfaceWeightsFromRegionAndProcessSurfaceWeightsFromListAsyncProduceSameResults)
  669. {
  670. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  671. for (auto sampler :
  672. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  673. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  674. {
  675. // Gather all our initial results from calling Process*FromRegion
  676. AZStd::vector<AZ::Vector3> queryPositions;
  677. AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList> baselineResultWeights;
  678. GenerateBaselineSurfaceWeightData(QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultWeights);
  679. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  680. // Gather results from Process*FromListAsync
  681. AZStd::vector<AZ::Vector3> comparisonResultPositions;
  682. AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList> comparisonResultWeights;
  683. AZStd::mutex outputMutex;
  684. auto listPositionCallback = [&comparisonResultPositions, &comparisonResultWeights, &outputMutex](
  685. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  686. {
  687. // Make sure only one thread can add its result at a time.
  688. AZStd::scoped_lock lock(outputMutex);
  689. comparisonResultPositions.emplace_back(surfacePoint.m_position);
  690. comparisonResultWeights.emplace_back(surfacePoint.m_surfaceTags);
  691. EXPECT_TRUE(terrainExists);
  692. };
  693. auto params = CreateTestAsyncParams();
  694. AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> jobContext;
  695. AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
  696. jobContext, &AzFramework::Terrain::TerrainDataRequests::QueryListAsync, queryPositions,
  697. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::SurfaceData, listPositionCallback, sampler, params);
  698. // Wait for the async query to complete
  699. m_queryCompletionEvent.acquire();
  700. // Compare the results
  701. CompareSurfaceWeightData(queryPositions, baselineResultWeights, comparisonResultPositions, comparisonResultWeights);
  702. }
  703. DestroyTestTerrainSystem();
  704. }
  705. // -----------------------------------------------------------------------------
  706. // Compare Surface Point Query APIs
  707. TEST_F(TerrainBulkQueryTest, ProcessSurfacePointsFromRegionAndProcessSurfacePointsFromListProduceSameResults)
  708. {
  709. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  710. for (auto sampler :
  711. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  712. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  713. {
  714. // Gather all our initial results from calling Process*FromRegion
  715. AZStd::vector<AZ::Vector3> queryPositions;
  716. AZStd::vector<AzFramework::SurfaceData::SurfacePoint> baselineResultPoints;
  717. AZStd::vector<bool> baselineExistsFlags;
  718. GenerateBaselineSurfacePointData(
  719. QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultPoints, baselineExistsFlags);
  720. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  721. // Gather results from Process*FromList
  722. AZStd::vector<AzFramework::SurfaceData::SurfacePoint> comparisonResultPoints;
  723. AZStd::vector<bool> comparisonExistsFlags;
  724. auto listPositionCallback = [&comparisonResultPoints, &comparisonExistsFlags](
  725. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  726. {
  727. comparisonResultPoints.emplace_back(surfacePoint);
  728. comparisonExistsFlags.emplace_back(terrainExists);
  729. };
  730. AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
  731. &AzFramework::Terrain::TerrainDataRequests::QueryList, queryPositions,
  732. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::All, listPositionCallback, sampler);
  733. // Compare the results
  734. CompareSurfacePointData(baselineResultPoints, baselineExistsFlags, comparisonResultPoints, comparisonExistsFlags);
  735. }
  736. DestroyTestTerrainSystem();
  737. }
  738. TEST_F(TerrainBulkQueryTest, ProcessSurfacePointsFromRegionAndGetSurfacePointProduceSameResults)
  739. {
  740. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  741. for (auto sampler :
  742. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  743. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  744. {
  745. // Gather all our initial results from calling Process*FromRegion
  746. AZStd::vector<AZ::Vector3> queryPositions;
  747. AZStd::vector<AzFramework::SurfaceData::SurfacePoint> baselineResultPoints;
  748. AZStd::vector<bool> baselineExistsFlags;
  749. GenerateBaselineSurfacePointData(
  750. QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultPoints, baselineExistsFlags);
  751. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  752. // Gather results from Get*
  753. AZStd::vector<AzFramework::SurfaceData::SurfacePoint> comparisonResultPoints;
  754. AZStd::vector<bool> comparisonExistsFlags;
  755. AzFramework::SurfaceData::SurfacePoint surfacePoint;
  756. for (auto& position : queryPositions)
  757. {
  758. bool terrainExists = false;
  759. AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
  760. &AzFramework::Terrain::TerrainDataRequests::GetSurfacePoint, position, surfacePoint, sampler, &terrainExists);
  761. comparisonResultPoints.emplace_back(surfacePoint);
  762. comparisonExistsFlags.emplace_back(terrainExists);
  763. }
  764. // Compare the results
  765. CompareSurfacePointData(baselineResultPoints, baselineExistsFlags, comparisonResultPoints, comparisonExistsFlags);
  766. }
  767. DestroyTestTerrainSystem();
  768. }
  769. TEST_F(TerrainBulkQueryTest, ProcessSurfacePointsFromRegionAndProcessSurfacePointsFromRegionAsyncProduceSameResults)
  770. {
  771. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  772. for (auto sampler :
  773. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  774. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  775. {
  776. // Gather all our initial results from calling Process*FromRegion
  777. AZStd::vector<AZ::Vector3> queryPositions;
  778. AZStd::vector<AzFramework::SurfaceData::SurfacePoint> baselineResultPoints;
  779. AZStd::vector<bool> baselineExistsFlags;
  780. GenerateBaselineSurfacePointData(
  781. QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultPoints, baselineExistsFlags);
  782. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  783. // Gather results from Process*FromRegionAsync
  784. AZStd::vector<AzFramework::SurfaceData::SurfacePoint> comparisonResultPoints;
  785. AZStd::vector<bool> comparisonExistsFlags;
  786. AZStd::mutex outputMutex;
  787. auto regionPositionCallback = [&comparisonResultPoints, &comparisonExistsFlags, &outputMutex](
  788. [[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex,
  789. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  790. {
  791. // Make sure only one thread can add its result at a time.
  792. AZStd::scoped_lock lock(outputMutex);
  793. comparisonResultPoints.emplace_back(surfacePoint);
  794. comparisonExistsFlags.emplace_back(terrainExists);
  795. };
  796. auto params = CreateTestAsyncParams();
  797. AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> jobContext;
  798. AzFramework::Terrain::TerrainQueryRegion queryRegion =
  799. AzFramework::Terrain::TerrainQueryRegion::CreateFromAabbAndStepSize(QueryBounds, QueryStepSize);
  800. AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
  801. jobContext, &AzFramework::Terrain::TerrainDataRequests::QueryRegionAsync, queryRegion,
  802. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::All, regionPositionCallback, sampler, params);
  803. // Wait for the async query to complete
  804. m_queryCompletionEvent.acquire();
  805. // Compare the results
  806. CompareSurfacePointData(baselineResultPoints, baselineExistsFlags, comparisonResultPoints, comparisonExistsFlags);
  807. }
  808. DestroyTestTerrainSystem();
  809. }
  810. TEST_F(TerrainBulkQueryTest, ProcessSurfacePointsFromRegionAndProcessSurfacePointsFromListAsyncProduceSameResults)
  811. {
  812. CreateTestTerrainSystem(TerrainWorldBounds, TerrainQueryResolution, TerrainNumSurfaces);
  813. for (auto sampler :
  814. { AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP,
  815. AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT })
  816. {
  817. // Gather all our initial results from calling Process*FromRegion
  818. AZStd::vector<AZ::Vector3> queryPositions;
  819. AZStd::vector<AzFramework::SurfaceData::SurfacePoint> baselineResultPoints;
  820. AZStd::vector<bool> baselineExistsFlags;
  821. GenerateBaselineSurfacePointData(
  822. QueryBounds, QueryStepSize, sampler, queryPositions, baselineResultPoints, baselineExistsFlags);
  823. ASSERT_EQ(queryPositions.size(), ExpectedResultCount);
  824. // Gather results from Process*FromListAsync
  825. AZStd::vector<AzFramework::SurfaceData::SurfacePoint> comparisonResultPoints;
  826. AZStd::vector<bool> comparisonExistsFlags;
  827. AZStd::mutex outputMutex;
  828. auto listPositionCallback = [&comparisonResultPoints, &comparisonExistsFlags, &outputMutex](
  829. const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  830. {
  831. // Make sure only one thread can add its result at a time.
  832. AZStd::scoped_lock lock(outputMutex);
  833. comparisonResultPoints.emplace_back(surfacePoint);
  834. comparisonExistsFlags.emplace_back(terrainExists);
  835. };
  836. auto params = CreateTestAsyncParams();
  837. AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> jobContext;
  838. AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
  839. jobContext, &AzFramework::Terrain::TerrainDataRequests::QueryListAsync, queryPositions,
  840. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::All, listPositionCallback, sampler, params);
  841. // Wait for the async query to complete
  842. m_queryCompletionEvent.acquire();
  843. // Compare the results
  844. CompareSurfacePointData(baselineResultPoints, baselineExistsFlags, comparisonResultPoints, comparisonExistsFlags);
  845. }
  846. DestroyTestTerrainSystem();
  847. }
  848. } // namespace UnitTest