From 5b504086f9f630ac7841ef26cf2662096d1dc567 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Thu, 4 Nov 2021 15:27:00 -0500 Subject: [PATCH] Prevent infinite recursion with Altitude Gradient. Using the Altitude Gradient as an input to the Height Gradient List can cause infinite recursion since it is both setting and fetching the same height value. Added guards to warn if this occurs and gracefully handles the situation. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../TerrainHeightGradientListComponent.cpp | 39 +++++++++++-------- .../TerrainHeightGradientListComponent.h | 3 ++ 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp index 231d5abc28..06b7320a06 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp @@ -151,24 +151,29 @@ namespace Terrain { float maxSample = 0.0f; terrainExists = false; - - GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f)); - - // Right now, when the list contains multiple entries, we will use the highest point from each gradient. - // This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value - // of 0 outside their data bounds if they're using bounded data. We should examine the possibility of extending the gradient API - // to provide actual bounds so that it's possible to detect if the gradient even 'exists' in an area, at which point we could just - // make this list a prioritized list from top to bottom for any points that overlap. - for (auto& gradientId : m_configuration.m_gradientEntities) + AZ_WarningOnce("Terrain", !m_isRequestInProgress, "Detected cyclic dependences with terrain height entity references"); + if (!m_isRequestInProgress) { - // If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain - // to *not* exist at a specific point. - terrainExists = true; - - float sample = 0.0f; - GradientSignal::GradientRequestBus::EventResult( - sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); - maxSample = AZ::GetMax(maxSample, sample); + m_isRequestInProgress = true; + GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f)); + + // Right now, when the list contains multiple entries, we will use the highest point from each gradient. + // This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value + // of 0 outside their data bounds if they're using bounded data. We should examine the possibility of extending the gradient + // API to provide actual bounds so that it's possible to detect if the gradient even 'exists' in an area, at which point we + // could just make this list a prioritized list from top to bottom for any points that overlap. + for (auto& gradientId : m_configuration.m_gradientEntities) + { + // If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain + // to *not* exist at a specific point. + terrainExists = true; + + float sample = 0.0f; + GradientSignal::GradientRequestBus::EventResult( + sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); + maxSample = AZ::GetMax(maxSample, sample); + } + m_isRequestInProgress = false; } const float height = AZ::Lerp(m_cachedShapeBounds.GetMin().GetZ(), m_cachedShapeBounds.GetMax().GetZ(), maxSample); diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h index 6c3fd7b820..b5b1a44192 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h @@ -91,6 +91,9 @@ namespace Terrain AZ::Vector2 m_cachedHeightQueryResolution{ 1.0f, 1.0f }; AZ::Aabb m_cachedShapeBounds; + // prevent recursion in case user attaches cyclic dependences + mutable bool m_isRequestInProgress{ false }; + LmbrCentral::DependencyMonitor m_dependencyMonitor; }; }