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>
monroegm-disable-blank-issue-2
bosnichd 4 years ago committed by GitHub
parent 9d002860f7
commit ff4529fc60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -35,7 +35,6 @@ namespace
inline void FindNearestIntersection(const AZ::Aabb& aabb, inline void FindNearestIntersection(const AZ::Aabb& aabb,
const AZ::Vector3& rayStart, const AZ::Vector3& rayStart,
const AZ::Vector3& rayDirection, const AZ::Vector3& rayDirection,
const AZ::Vector3& rayDirectionReciprocal,
AzFramework::RenderGeometry::RayResult& result) AzFramework::RenderGeometry::RayResult& result)
{ {
float intersectionT; float intersectionT;
@ -43,7 +42,7 @@ namespace
AZ::Vector3 intersectionNormal; AZ::Vector3 intersectionNormal;
const int intersectionResult = AZ::Intersect::IntersectRayAABB(rayStart, const int intersectionResult = AZ::Intersect::IntersectRayAABB(rayStart,
rayDirection, rayDirection,
rayDirectionReciprocal, rayDirection.GetReciprocal(),
aabb, aabb,
intersectionT, intersectionT,
intersectionEndT, intersectionEndT,
@ -117,7 +116,6 @@ namespace
const AZ::Aabb& aabb, const AZ::Aabb& aabb,
const AZ::Vector3& rayStart, const AZ::Vector3& rayStart,
const AZ::Vector3& rayDirection, const AZ::Vector3& rayDirection,
const AZ::Vector3& rayDirectionReciprocal,
AzFramework::RenderGeometry::RayResult& result) AzFramework::RenderGeometry::RayResult& result)
{ {
// Obtain the height values at each corner of the AABB. // 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)); point2.SetZ(terrainSystem.GetHeight(point2, AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT));
point3.SetZ(terrainSystem.GetHeight(point3, 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, // Finally, triangulate the four terrain points and check for a hit,
// splitting using the top-left -> bottom-right diagonal so to match // splitting using the top-left -> bottom-right diagonal so to match
// the current behavior of the terrain physics and rendering systems. // 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. // 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. // Note that the ray might (and often will) start inside the terrain world bounds.
const AZ::Vector3 rayDirection = rayEnd - rayStart; const AZ::Vector3 rayDirection = (rayEnd - rayStart).GetNormalized();
const AZ::Vector3 rayDirectionReciprocal = rayDirection.GetReciprocal();
FindNearestIntersection(terrainWorldBounds, FindNearestIntersection(terrainWorldBounds,
rayStart, rayStart,
rayDirection, rayDirection,
rayDirectionReciprocal,
result); result);
if (!result) if (!result)
{ {
@ -327,7 +303,6 @@ namespace
currentVoxel, currentVoxel,
rayStart, rayStart,
rayDirection, rayDirection,
rayDirectionReciprocal,
result); result);
if (result) if (result)
{ {

@ -11,6 +11,7 @@
#include <AzCore/Component/ComponentApplication.h> #include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/Entity.h> #include <AzCore/Component/Entity.h>
#include <AzCore/Component/TransformBus.h> #include <AzCore/Component/TransformBus.h>
#include <AzCore/Math/Random.h>
#include <AzCore/Memory/PoolAllocator.h> #include <AzCore/Memory/PoolAllocator.h>
#include <AzCore/UnitTest/TestTypes.h> #include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/Components/TransformComponent.h> #include <AzFramework/Components/TransformComponent.h>
@ -682,6 +683,89 @@ namespace UnitTest
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) ->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) ->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
->Unit(::benchmark::kMillisecond); ->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 #endif
} }

Loading…
Cancel
Save