Terrain ray cast benchmarks and optimization. (#7303)
* Terrain ray cast benchmarks and optimization: - Added some benchmarks that exercise terrain ray casting. - Optimized terrain ray casting by removing an unnecessary AABB intersection check (this was suggested by @invertednormal in the original review, but I forgot to actually remove it until now). - Fixed a bug where we were not normalizing the ray direction before performing the Moller-Trumbore ray<->triangle intersection calculations. Signed-off-by: bosnichd <bosnichd@amazon.com> * Update to make work with changes pulled down from mainline. Signed-off-by: bosnichd <bosnichd@amazon.com>
This commit is contained in:
@@ -35,7 +35,6 @@ namespace
|
||||
inline void FindNearestIntersection(const AZ::Aabb& aabb,
|
||||
const AZ::Vector3& rayStart,
|
||||
const AZ::Vector3& rayDirection,
|
||||
const AZ::Vector3& rayDirectionReciprocal,
|
||||
AzFramework::RenderGeometry::RayResult& result)
|
||||
{
|
||||
float intersectionT;
|
||||
@@ -43,7 +42,7 @@ namespace
|
||||
AZ::Vector3 intersectionNormal;
|
||||
const int intersectionResult = AZ::Intersect::IntersectRayAABB(rayStart,
|
||||
rayDirection,
|
||||
rayDirectionReciprocal,
|
||||
rayDirection.GetReciprocal(),
|
||||
aabb,
|
||||
intersectionT,
|
||||
intersectionEndT,
|
||||
@@ -117,7 +116,6 @@ namespace
|
||||
const AZ::Aabb& aabb,
|
||||
const AZ::Vector3& rayStart,
|
||||
const AZ::Vector3& rayDirection,
|
||||
const AZ::Vector3& rayDirectionReciprocal,
|
||||
AzFramework::RenderGeometry::RayResult& result)
|
||||
{
|
||||
// Obtain the height values at each corner of the AABB.
|
||||
@@ -132,26 +130,6 @@ namespace
|
||||
point2.SetZ(terrainSystem.GetHeight(point2, AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT));
|
||||
point3.SetZ(terrainSystem.GetHeight(point3, AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT));
|
||||
|
||||
// Construct a smaller AABB that tightly encloses the four terrain points.
|
||||
const float refinedMinZ = AZStd::GetMin(AZStd::GetMin(AZStd::GetMin(point0.GetZ(), point1.GetZ()), point2.GetZ()), point3.GetZ());
|
||||
const float refinedMaxZ = AZStd::GetMax(AZStd::GetMax(AZStd::GetMax(point0.GetZ(), point1.GetZ()), point2.GetZ()), point3.GetZ());
|
||||
const AZ::Vector3 refinedMin(aabbMin.GetX(), aabbMin.GetY(), refinedMinZ);
|
||||
const AZ::Vector3 refinedMax(aabbMax.GetX(), aabbMax.GetY(), refinedMaxZ);
|
||||
const AZ::Aabb refinedAABB = AZ::Aabb::CreateFromMinMax(refinedMin, refinedMax);
|
||||
|
||||
// Check for a hit against the refined AABB.
|
||||
float intersectionT;
|
||||
float intersectionEndT;
|
||||
const int intersectionResult = AZ::Intersect::IntersectRayAABB2(rayStart,
|
||||
rayDirectionReciprocal,
|
||||
refinedAABB,
|
||||
intersectionT,
|
||||
intersectionEndT);
|
||||
if (intersectionResult == AZ::Intersect::ISECT_RAY_AABB_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Finally, triangulate the four terrain points and check for a hit,
|
||||
// splitting using the top-left -> bottom-right diagonal so to match
|
||||
// the current behavior of the terrain physics and rendering systems.
|
||||
@@ -218,12 +196,10 @@ namespace
|
||||
{
|
||||
// Find the nearest intersection (if any) between the ray and terrain world bounds.
|
||||
// Note that the ray might (and often will) start inside the terrain world bounds.
|
||||
const AZ::Vector3 rayDirection = rayEnd - rayStart;
|
||||
const AZ::Vector3 rayDirectionReciprocal = rayDirection.GetReciprocal();
|
||||
const AZ::Vector3 rayDirection = (rayEnd - rayStart).GetNormalized();
|
||||
FindNearestIntersection(terrainWorldBounds,
|
||||
rayStart,
|
||||
rayDirection,
|
||||
rayDirectionReciprocal,
|
||||
result);
|
||||
if (!result)
|
||||
{
|
||||
@@ -327,7 +303,6 @@ namespace
|
||||
currentVoxel,
|
||||
rayStart,
|
||||
rayDirection,
|
||||
rayDirectionReciprocal,
|
||||
result);
|
||||
if (result)
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Math/Random.h>
|
||||
#include <AzCore/Memory/PoolAllocator.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
@@ -682,6 +683,89 @@ namespace UnitTest
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_GetClosestIntersectionRandom)(benchmark::State& state)
|
||||
{
|
||||
// Run the benchmark
|
||||
const uint32_t numRays = aznumeric_cast<uint32_t>(state.range(1));
|
||||
RunTerrainApiBenchmark(
|
||||
state,
|
||||
[numRays]([[maybe_unused]] float queryResolution, const AZ::Aabb& worldBounds,
|
||||
[[maybe_unused]] AzFramework::Terrain::TerrainDataRequests::Sampler sampler)
|
||||
{
|
||||
// Cast rays starting at random positions above the terrain,
|
||||
// and ending at a random positions below the terrain.
|
||||
AZ::SimpleLcgRandom random;
|
||||
AzFramework::RenderGeometry::RayRequest ray;
|
||||
AzFramework::RenderGeometry::RayResult result;
|
||||
for (uint32_t i = 0; i < numRays; ++i)
|
||||
{
|
||||
ray.m_startWorldPosition.SetX(worldBounds.GetMin().GetX() + (random.GetRandomFloat() * worldBounds.GetXExtent()));
|
||||
ray.m_startWorldPosition.SetY(worldBounds.GetMin().GetY() + (random.GetRandomFloat() * worldBounds.GetYExtent()));
|
||||
ray.m_startWorldPosition.SetZ(worldBounds.GetMax().GetZ());
|
||||
ray.m_endWorldPosition.SetX(worldBounds.GetMin().GetX() + (random.GetRandomFloat() * worldBounds.GetXExtent()));
|
||||
ray.m_endWorldPosition.SetY(worldBounds.GetMin().GetY() + (random.GetRandomFloat() * worldBounds.GetYExtent()));
|
||||
ray.m_endWorldPosition.SetZ(worldBounds.GetMin().GetZ());
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
|
||||
result, &AzFramework::Terrain::TerrainDataRequests::GetClosestIntersection, ray);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_GetClosestIntersectionRandom)
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 4096, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 1024, 10, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 10, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 4096, 10, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 1024, 100, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 100, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 4096, 100, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 1024, 1000, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 1000, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 4096, 1000, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_GetClosestIntersectionWorstCase)(benchmark::State& state)
|
||||
{
|
||||
// Run the benchmark
|
||||
const uint32_t numRays = aznumeric_cast<uint32_t>(state.range(1));
|
||||
RunTerrainApiBenchmark(
|
||||
state,
|
||||
[numRays]([[maybe_unused]] float queryResolution, const AZ::Aabb& worldBounds,
|
||||
[[maybe_unused]] AzFramework::Terrain::TerrainDataRequests::Sampler sampler)
|
||||
{
|
||||
// Cast rays starting at an upper corner of the terrain world,
|
||||
// and ending at the opposite top corner of the terrain world,
|
||||
// traversing the entire grid without finding an intersection.
|
||||
AzFramework::RenderGeometry::RayRequest ray;
|
||||
AzFramework::RenderGeometry::RayResult result;
|
||||
ray.m_startWorldPosition = worldBounds.GetMax();
|
||||
ray.m_endWorldPosition = worldBounds.GetMin();
|
||||
ray.m_endWorldPosition.SetZ(ray.m_startWorldPosition.GetZ());
|
||||
for (uint32_t i = 0; i < numRays; ++i)
|
||||
{
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
|
||||
result, &AzFramework::Terrain::TerrainDataRequests::GetClosestIntersection, ray);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_GetClosestIntersectionWorstCase)
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 4096, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 1024, 10, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 10, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 4096, 10, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 1024, 100, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 100, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 4096, 100, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 1024, 1000, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 1000, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 4096, 1000, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user