FastNoise GetValues() specialization (#7009)

* Add comparison operator for use from unit tests.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* First version of FastNoise benchmarks.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Simplified unit tests and added initial benchmarks.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Add GetValue vs GetValues unit test.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Moved Gradient test code into helper files for use from FastNoise.
Also added benchmarks for each type of FastNoise so that we can have some comparative values handy.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Specialization for GetValues().

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>
This commit is contained in:
Mike Balfour
2022-01-19 17:10:37 -06:00
committed by GitHub
parent 7adccd0d48
commit 2b43ad8029
14 changed files with 672 additions and 479 deletions
+27 -19
View File
@@ -104,7 +104,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
NAME FastNoise.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE Gem
FILES_CMAKE
fastnoise_tests_files.cmake
fastnoise_editor_tests_files.cmake
COMPILE_DEFINITIONS
PUBLIC
FASTNOISE_EDITOR
@@ -120,23 +120,31 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_googletest(
NAME Gem::FastNoise.Editor.Tests
)
else()
ly_add_target(
NAME FastNoise.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE Gem
FILES_CMAKE
fastnoise_tests_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Tests
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
Gem::FastNoise.Static
Gem::LmbrCentral
)
ly_add_googletest(
NAME Gem::FastNoise.Tests
)
endif()
ly_add_target(
NAME FastNoise.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE Gem
FILES_CMAKE
fastnoise_tests_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Tests
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
Gem::FastNoise.Static
Gem::GradientSignal
Gem::GradientSignal.Tests.Static
Gem::LmbrCentral
)
ly_add_googletest(
NAME Gem::FastNoise.Tests
)
ly_add_googlebenchmark(
NAME Gem::FastNoise.Benchmarks
TARGET Gem::FastNoise.Tests
)
endif()
@@ -54,6 +54,21 @@ namespace FastNoiseGem
return AZ::Edit::PropertyVisibility::Hide;
}
bool FastNoiseGradientConfig::operator==(const FastNoiseGradientConfig& rhs) const
{
return (m_cellularDistanceFunction == rhs.m_cellularDistanceFunction)
&& (m_cellularJitter == rhs.m_cellularJitter)
&& (m_cellularReturnType == rhs.m_cellularReturnType)
&& (m_fractalType == rhs.m_fractalType)
&& (m_frequency == rhs.m_frequency)
&& (m_gain == rhs.m_gain)
&& (m_interp == rhs.m_interp)
&& (m_lacunarity == rhs.m_lacunarity)
&& (m_noiseType == rhs.m_noiseType)
&& (m_octaves == rhs.m_octaves)
&& (m_seed == rhs.m_seed);
}
void FastNoiseGradientConfig::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
@@ -306,7 +321,7 @@ namespace FastNoiseGem
float FastNoiseGradientComponent::GetValue(const GradientSignal::GradientSampleParams& sampleParams) const
{
AZ::Vector3 uvw = sampleParams.m_position;
AZ::Vector3 uvw;
bool wasPointRejected = false;
{
@@ -314,13 +329,34 @@ namespace FastNoiseGem
m_gradientTransform.TransformPositionToUVW(sampleParams.m_position, uvw, wasPointRejected);
}
if (!wasPointRejected)
// Generator returns a range between [-1, 1], map that to [0, 1]
return wasPointRejected ?
0.0f :
AZ::GetClamp((m_generator.GetNoise(uvw.GetX(), uvw.GetY(), uvw.GetZ()) + 1.0f) / 2.0f, 0.0f, 1.0f);
}
void FastNoiseGradientComponent::GetValues(AZStd::span<const AZ::Vector3> positions, AZStd::span<float> outValues) const
{
if (positions.size() != outValues.size())
{
// Generator returns a range between [-1, 1], map that to [0, 1]
return AZ::GetClamp((m_generator.GetNoise(uvw.GetX(), uvw.GetY(), uvw.GetZ()) + 1.0f) / 2.0f, 0.0f, 1.0f);
AZ_Assert(false, "input and output lists are different sizes (%zu vs %zu).", positions.size(), outValues.size());
return;
}
return 0.0f;
AZStd::shared_lock<decltype(m_transformMutex)> lock(m_transformMutex);
AZ::Vector3 uvw;
for (size_t index = 0; index < positions.size(); index++)
{
bool wasPointRejected = false;
m_gradientTransform.TransformPositionToUVW(positions[index], uvw, wasPointRejected);
// Generator returns a range between [-1, 1], map that to [0, 1]
outValues[index] = wasPointRejected ?
0.0f :
AZ::GetClamp((m_generator.GetNoise(uvw.GetX(), uvw.GetY(), uvw.GetZ()) + 1.0f) / 2.0f, 0.0f, 1.0f);
}
}
template <typename TValueType, TValueType FastNoiseGradientConfig::*TConfigMember, void (FastNoise::*TMethod)(TValueType)>
@@ -47,6 +47,8 @@ namespace FastNoiseGem
AZ::u32 GetFrequencyParameterVisbility() const;
AZ::u32 GetInterpParameterVisibility() const;
bool operator==(const FastNoiseGradientConfig& rhs) const;
int m_seed = 1;
float m_frequency = 1.f;
FastNoise::Interp m_interp = FastNoise::Interp::Quintic;
@@ -90,6 +92,7 @@ namespace FastNoiseGem
// GradientRequestBus overrides...
float GetValue(const GradientSignal::GradientSampleParams& sampleParams) const override;
void GetValues(AZStd::span<const AZ::Vector3> positions, AZStd::span<float> outValues) const override;
protected:
FastNoiseGradientConfig m_configuration;
@@ -0,0 +1,116 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifdef HAVE_BENCHMARK
#include <AzTest/AzTest.h>
#include <AzCore/Math/Vector3.h>
#include <FastNoiseGradientComponent.h>
#include <FastNoiseTest.h>
#include <GradientSignalTestHelpers.h>
#include <GradientSignal/Components/GradientTransformComponent.h>
#include <GradientSignal/Ebuses/GradientRequestBus.h>
#include <GradientSignal/Ebuses/GradientTransformModifierRequestBus.h>
#include <GradientSignal/GradientSampler.h>
#include <LmbrCentral/Shape/BoxShapeComponentBus.h>
namespace UnitTest
{
class FastNoiseGetValues
: public ::benchmark::Fixture
{
public:
void RunGetValueOrGetValuesBenchmark(benchmark::State& state, FastNoise::NoiseType noiseType)
{
AZ::Entity* noiseEntity = aznew AZ::Entity("noise_entity");
ASSERT_TRUE(noiseEntity != nullptr);
noiseEntity->CreateComponent<AzFramework::TransformComponent>();
noiseEntity->CreateComponent(LmbrCentral::BoxShapeComponentTypeId);
noiseEntity->CreateComponent<GradientSignal::GradientTransformComponent>();
// Set up a FastNoise component with the requested noise type
FastNoiseGem::FastNoiseGradientConfig cfg;
cfg.m_frequency = 0.01f;
cfg.m_noiseType = noiseType;
noiseEntity->CreateComponent<FastNoiseGem::FastNoiseGradientComponent>(cfg);
noiseEntity->Init();
noiseEntity->Activate();
UnitTest::GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, noiseEntity->GetId());
}
};
BENCHMARK_DEFINE_F(FastNoiseGetValues, BM_FastNoiseGradient_Value)(benchmark::State& state)
{
RunGetValueOrGetValuesBenchmark(state, FastNoise::NoiseType::Value);
}
BENCHMARK_DEFINE_F(FastNoiseGetValues, BM_FastNoiseGradient_ValueFractal)(benchmark::State& state)
{
RunGetValueOrGetValuesBenchmark(state, FastNoise::NoiseType::ValueFractal);
}
BENCHMARK_DEFINE_F(FastNoiseGetValues, BM_FastNoiseGradient_Perlin)(benchmark::State& state)
{
RunGetValueOrGetValuesBenchmark(state, FastNoise::NoiseType::Perlin);
}
BENCHMARK_DEFINE_F(FastNoiseGetValues, BM_FastNoiseGradient_PerlinFractal)(benchmark::State& state)
{
RunGetValueOrGetValuesBenchmark(state, FastNoise::NoiseType::PerlinFractal);
}
BENCHMARK_DEFINE_F(FastNoiseGetValues, BM_FastNoiseGradient_Simplex)(benchmark::State& state)
{
RunGetValueOrGetValuesBenchmark(state, FastNoise::NoiseType::Simplex);
}
BENCHMARK_DEFINE_F(FastNoiseGetValues, BM_FastNoiseGradient_SimplexFractal)(benchmark::State& state)
{
RunGetValueOrGetValuesBenchmark(state, FastNoise::NoiseType::SimplexFractal);
}
BENCHMARK_DEFINE_F(FastNoiseGetValues, BM_FastNoiseGradient_Cellular)(benchmark::State& state)
{
RunGetValueOrGetValuesBenchmark(state, FastNoise::NoiseType::Cellular);
}
BENCHMARK_DEFINE_F(FastNoiseGetValues, BM_FastNoiseGradient_WhiteNoise)(benchmark::State& state)
{
RunGetValueOrGetValuesBenchmark(state, FastNoise::NoiseType::WhiteNoise);
}
BENCHMARK_DEFINE_F(FastNoiseGetValues, BM_FastNoiseGradient_Cubic)(benchmark::State& state)
{
RunGetValueOrGetValuesBenchmark(state, FastNoise::NoiseType::Cubic);
}
BENCHMARK_DEFINE_F(FastNoiseGetValues, BM_FastNoiseGradient_CubicFractal)(benchmark::State& state)
{
RunGetValueOrGetValuesBenchmark(state, FastNoise::NoiseType::CubicFractal);
}
GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(FastNoiseGetValues, BM_FastNoiseGradient_Value);
GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(FastNoiseGetValues, BM_FastNoiseGradient_ValueFractal);
GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(FastNoiseGetValues, BM_FastNoiseGradient_Perlin);
GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(FastNoiseGetValues, BM_FastNoiseGradient_PerlinFractal);
GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(FastNoiseGetValues, BM_FastNoiseGradient_Simplex);
GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(FastNoiseGetValues, BM_FastNoiseGradient_SimplexFractal);
GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(FastNoiseGetValues, BM_FastNoiseGradient_Cellular);
GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(FastNoiseGetValues, BM_FastNoiseGradient_WhiteNoise);
GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(FastNoiseGetValues, BM_FastNoiseGradient_Cubic);
GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(FastNoiseGetValues, BM_FastNoiseGradient_CubicFractal);
#endif
}
@@ -0,0 +1,44 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzTest/AzTest.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <EditorFastNoiseGradientComponent.h>
#include <FastNoiseTest.h>
class FastNoiseEditorTestApp : public ::testing::Test
{
};
TEST_F(FastNoiseEditorTestApp, FastNoise_EditorCreateGameEntity)
{
AZStd::unique_ptr<AZ::Entity> noiseEntity(aznew AZ::Entity("editor_noise_entity"));
ASSERT_TRUE(noiseEntity != nullptr);
FastNoiseGem::EditorFastNoiseGradientComponent editor;
auto* editorBase = static_cast<AzToolsFramework::Components::EditorComponentBase*>(&editor);
editorBase->BuildGameEntity(noiseEntity.get());
// the new game entity's FastNoise component should look like the default one
FastNoiseGem::FastNoiseGradientConfig defaultConfig;
FastNoiseGem::FastNoiseGradientConfig gameComponentConfig;
FastNoiseGem::FastNoiseGradientComponent* noiseComp = noiseEntity->FindComponent<FastNoiseGem::FastNoiseGradientComponent>();
ASSERT_TRUE(noiseComp != nullptr);
// Change a value in the gameComponentConfig just to verify that it got overwritten instead of simply matching the default.
gameComponentConfig.m_seed++;
noiseComp->WriteOutConfig(&gameComponentConfig);
ASSERT_EQ(defaultConfig, gameComponentConfig);
}
// This uses custom test / benchmark hooks so that we can load LmbrCentral and GradientSignal Gems.
AZ_UNIT_TEST_HOOK(new UnitTest::FastNoiseTestEnvironment, UnitTest::FastNoiseBenchmarkEnvironment);
+52 -202
View File
@@ -10,199 +10,61 @@
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Math/Random.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/Script/ScriptContext.h>
#include <AzCore/std/chrono/clocks.h>
#include <FastNoiseSystemComponent.h>
#include <AzFramework/Components/TransformComponent.h>
#include <FastNoiseGradientComponent.h>
#include <FastNoiseModule.h>
#include <FastNoiseTest.h>
#include <GradientSignalTestHelpers.h>
#include <GradientSignal/Components/GradientTransformComponent.h>
#include <GradientSignal/Ebuses/GradientRequestBus.h>
#include <GradientSignal/Ebuses/GradientTransformModifierRequestBus.h>
#include <GradientSignal/Ebuses/GradientTransformRequestBus.h>
#include <GradientSignal/GradientSampler.h>
#include <LmbrCentral/Shape/BoxShapeComponentBus.h>
class MockGradientTransformComponent
: public AZ::Component
, private GradientSignal::GradientTransformRequestBus::Handler
, private GradientSignal::GradientTransformModifierRequestBus::Handler
class FastNoiseTest : public ::testing::Test
{
public:
AZ_COMPONENT(MockGradientTransformComponent, "{464CF47B-7E10-4E1B-BD06-79BD2AC91399}");
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("GradientTransformService", 0x8c8c5ecc));
}
static void Reflect([[maybe_unused]] AZ::ReflectContext* context) {}
MockGradientTransformComponent() = default;
~MockGradientTransformComponent() = default;
// AZ::Component interface
void Activate() override {}
void Deactivate() override {}
////////////////////////////////////////////////////////////////////////////
//// GradientTransformRequestBus
const GradientSignal::GradientTransform& GetGradientTransform() const override
{
return m_gradientTransform;
}
//////////////////////////////////////////////////////////////////////////
// GradientTransformModifierRequestBus
bool GetAllowReference() const override { return false; }
void SetAllowReference([[maybe_unused]] bool value) override {}
AZ::EntityId GetShapeReference() const override { return AZ::EntityId(); }
void SetShapeReference([[maybe_unused]] AZ::EntityId shapeReference) override {}
bool GetOverrideBounds() const override { return false; }
void SetOverrideBounds([[maybe_unused]] bool value) override {}
AZ::Vector3 GetBounds() const override { return AZ::Vector3(); }
void SetBounds([[maybe_unused]] AZ::Vector3 bounds) override {}
GradientSignal::TransformType GetTransformType() const override { return static_cast<GradientSignal::TransformType>(0); }
void SetTransformType([[maybe_unused]] GradientSignal::TransformType type) override {}
bool GetOverrideTranslate() const override { return false; }
void SetOverrideTranslate([[maybe_unused]] bool value) override {}
AZ::Vector3 GetTranslate() const override { return AZ::Vector3(); }
void SetTranslate([[maybe_unused]] AZ::Vector3 translate) override {}
bool GetOverrideRotate() const override { return false; }
void SetOverrideRotate([[maybe_unused]] bool value) override {}
AZ::Vector3 GetRotate() const override { return AZ::Vector3(); }
void SetRotate([[maybe_unused]] AZ::Vector3 rotate) override {}
bool GetOverrideScale() const override { return false; }
void SetOverrideScale([[maybe_unused]] bool value) override {}
AZ::Vector3 GetScale() const override { return AZ::Vector3(); }
void SetScale([[maybe_unused]] AZ::Vector3 scale) override {}
float GetFrequencyZoom() const override { return false; }
void SetFrequencyZoom([[maybe_unused]] float frequencyZoom) override {}
GradientSignal::WrappingType GetWrappingType() const override { return static_cast<GradientSignal::WrappingType>(0); }
void SetWrappingType([[maybe_unused]] GradientSignal::WrappingType type) override {}
bool GetIs3D() const override { return false; }
void SetIs3D([[maybe_unused]] bool value) override {}
bool GetAdvancedMode() const override { return false; }
void SetAdvancedMode([[maybe_unused]] bool value) override {}
GradientSignal::GradientTransform m_gradientTransform;
};
TEST(FastNoiseTest, ComponentsWithComponentApplication)
{
AZ::ComponentApplication::Descriptor appDesc;
appDesc.m_memoryBlocksByteSize = 10 * 1024 * 1024;
appDesc.m_recordingMode = AZ::Debug::AllocationRecords::RECORD_FULL;
appDesc.m_stackRecordLevels = 20;
AZ::ComponentApplication app;
AZ::Entity* systemEntity = app.Create(appDesc);
ASSERT_TRUE(systemEntity != nullptr);
app.RegisterComponentDescriptor(FastNoiseGem::FastNoiseSystemComponent::CreateDescriptor());
systemEntity->CreateComponent<FastNoiseGem::FastNoiseSystemComponent>();
systemEntity->Init();
systemEntity->Activate();
AZ::Entity* noiseEntity = aznew AZ::Entity("fastnoise_entity");
noiseEntity->CreateComponent<FastNoiseGem::FastNoiseGradientComponent>();
app.AddEntity(noiseEntity);
app.Destroy();
ASSERT_TRUE(true);
}
class FastNoiseTestApp
: public ::testing::Test
{
public:
FastNoiseTestApp()
: m_application()
, m_systemEntity(nullptr)
{
}
void SetUp() override
{
AZ::ComponentApplication::Descriptor appDesc;
appDesc.m_memoryBlocksByteSize = 10 * 1024 * 1024;
appDesc.m_recordingMode = AZ::Debug::AllocationRecords::RECORD_FULL;
appDesc.m_stackRecordLevels = 20;
AZ::ComponentApplication::StartupParameters appStartup;
appStartup.m_createStaticModulesCallback =
[](AZStd::vector<AZ::Module*>& modules)
{
modules.emplace_back(new FastNoiseGem::FastNoiseModule);
};
m_systemEntity = m_application.Create(appDesc, appStartup);
m_application.RegisterComponentDescriptor(MockGradientTransformComponent::CreateDescriptor());
m_systemEntity->Init();
m_systemEntity->Activate();
}
void TearDown() override
{
m_application.Destroy();
}
AZ::ComponentApplication m_application;
AZ::Entity* m_systemEntity;
};
//////////////////////////////////////////////////////////////////////////
// testing class to inspect protected data members in the FastNoiseGradientComponent
struct FastNoiseGradientComponentTester : public FastNoiseGem::FastNoiseGradientComponent
{
const FastNoiseGem::FastNoiseGradientConfig& GetConfig() const { return m_configuration; }
void AssertTrue(const FastNoiseGem::FastNoiseGradientConfig& cfg)
{
ASSERT_TRUE(m_configuration.m_cellularDistanceFunction == cfg.m_cellularDistanceFunction);
ASSERT_TRUE(m_configuration.m_cellularJitter == cfg.m_cellularJitter);
ASSERT_TRUE(m_configuration.m_cellularReturnType == cfg.m_cellularReturnType);
ASSERT_TRUE(m_configuration.m_fractalType == cfg.m_fractalType);
ASSERT_TRUE(m_configuration.m_frequency == cfg.m_frequency);
ASSERT_TRUE(m_configuration.m_gain == cfg.m_gain);
ASSERT_TRUE(m_configuration.m_interp == cfg.m_interp);
ASSERT_TRUE(m_configuration.m_lacunarity == cfg.m_lacunarity);
ASSERT_TRUE(m_configuration.m_noiseType == cfg.m_noiseType);
ASSERT_TRUE(m_configuration.m_octaves == cfg.m_octaves);
ASSERT_TRUE(m_configuration.m_seed == cfg.m_seed);
}
};
TEST_F(FastNoiseTestApp, FastNoise_Component)
TEST_F(FastNoiseTest, FastNoise_ComponentCreatesSuccessfully)
{
AZ::Entity* noiseEntity = aznew AZ::Entity("noise_entity");
ASSERT_TRUE(noiseEntity != nullptr);
noiseEntity->CreateComponent<FastNoiseGem::FastNoiseGradientComponent>();
m_application.AddEntity(noiseEntity);
FastNoiseGem::FastNoiseGradientComponent* noiseComp = noiseEntity->FindComponent<FastNoiseGem::FastNoiseGradientComponent>();
ASSERT_TRUE(noiseComp != nullptr);
}
TEST_F(FastNoiseTestApp, FastNoise_ComponentEbus)
TEST_F(FastNoiseTest, FastNoise_ComponentMatchesConfiguration)
{
AZ::Entity* noiseEntity = aznew AZ::Entity("noise_entity");
ASSERT_TRUE(noiseEntity != nullptr);
FastNoiseGem::FastNoiseGradientConfig cfg;
FastNoiseGem::FastNoiseGradientConfig componentConfig;
noiseEntity->CreateComponent<AzFramework::TransformComponent>();
noiseEntity->CreateComponent(LmbrCentral::BoxShapeComponentTypeId);
noiseEntity->CreateComponent<GradientSignal::GradientTransformComponent>();
noiseEntity->CreateComponent<FastNoiseGem::FastNoiseGradientComponent>(cfg);
FastNoiseGem::FastNoiseGradientComponent* noiseComp = noiseEntity->FindComponent<FastNoiseGem::FastNoiseGradientComponent>();
ASSERT_TRUE(noiseComp != nullptr);
noiseComp->WriteOutConfig(&componentConfig);
ASSERT_EQ(cfg, componentConfig);
}
TEST_F(FastNoiseTest, FastNoise_ComponentEbusWorksSuccessfully)
{
AZ::Entity* noiseEntity = aznew AZ::Entity("noise_entity");
ASSERT_TRUE(noiseEntity != nullptr);
noiseEntity->CreateComponent<AzFramework::TransformComponent>();
noiseEntity->CreateComponent(LmbrCentral::BoxShapeComponentTypeId);
noiseEntity->CreateComponent<GradientSignal::GradientTransformComponent>();
noiseEntity->CreateComponent<FastNoiseGem::FastNoiseGradientComponent>();
noiseEntity->CreateComponent<MockGradientTransformComponent>();
noiseEntity->Init();
noiseEntity->Activate();
@@ -210,51 +72,39 @@ TEST_F(FastNoiseTestApp, FastNoise_ComponentEbus)
GradientSignal::GradientSampleParams params;
float sample = -1.0f;
GradientSignal::GradientRequestBus::EventResult(sample, noiseEntity->GetId(), &GradientSignal::GradientRequestBus::Events::GetValue, params);
GradientSignal::GradientRequestBus::EventResult(sample, noiseEntity->GetId(),
&GradientSignal::GradientRequestBus::Events::GetValue, params);
ASSERT_TRUE(sample >= 0.0f);
ASSERT_TRUE(sample <= 1.0f);
}
TEST_F(FastNoiseTestApp, FastNoise_ComponentMatchesConfiguration)
TEST_F(FastNoiseTest, FastNoise_VerifyGetValueAndGetValuesMatch)
{
const float shapeHalfBounds = 128.0f;
AZ::Entity* noiseEntity = aznew AZ::Entity("noise_entity");
ASSERT_TRUE(noiseEntity != nullptr);
noiseEntity->CreateComponent<AzFramework::TransformComponent>();
noiseEntity->CreateComponent<GradientSignal::GradientTransformComponent>();
AZ::SimpleLcgRandom rand(AZStd::GetTimeNowMicroSecond());
// Create a Box Shape to map our gradient into
LmbrCentral::BoxShapeConfig boxConfig(AZ::Vector3(shapeHalfBounds * 2.0f));
auto boxComponent = noiseEntity->CreateComponent(LmbrCentral::BoxShapeComponentTypeId);
boxComponent->SetConfiguration(boxConfig);
// Create a Fast Noise component with an adjusted frequency. (The defaults of Perlin noise with frequency=1.0 would cause us
// to always get back the same noise value)
FastNoiseGem::FastNoiseGradientConfig cfg;
cfg.m_frequency = 0.01f;
noiseEntity->CreateComponent<FastNoiseGem::FastNoiseGradientComponent>(cfg);
noiseEntity->CreateComponent<MockGradientTransformComponent>();
m_application.AddEntity(noiseEntity);
noiseEntity->Init();
noiseEntity->Activate();
FastNoiseGem::FastNoiseGradientComponent* noiseComp = noiseEntity->FindComponent<FastNoiseGem::FastNoiseGradientComponent>();
ASSERT_TRUE(noiseComp != nullptr);
reinterpret_cast<FastNoiseGradientComponentTester*>(noiseComp)->AssertTrue(cfg);
// Create a gradient sampler and run through a series of points to see if they match expectations.
UnitTest::GradientSignalTestHelpers::CompareGetValueAndGetValues(noiseEntity->GetId(), shapeHalfBounds);
}
#if FASTNOISE_EDITOR
#include <EditorFastNoiseGradientComponent.h>
TEST_F(FastNoiseTestApp, FastNoise_EditorCreateGameEntity)
{
AZStd::unique_ptr<AZ::Entity> noiseEntity(aznew AZ::Entity("editor_noise_entity"));
ASSERT_TRUE(noiseEntity != nullptr);
FastNoiseGem::EditorFastNoiseGradientComponent editor;
auto* editorBase = static_cast<AzToolsFramework::Components::EditorComponentBase*>(&editor);
editorBase->BuildGameEntity(noiseEntity.get());
// the new game entity's ocean component should look like the default one
FastNoiseGem::FastNoiseGradientConfig cfg;
FastNoiseGem::FastNoiseGradientComponent* noiseComp = noiseEntity->FindComponent<FastNoiseGem::FastNoiseGradientComponent>();
ASSERT_TRUE(noiseComp != nullptr);
reinterpret_cast<FastNoiseGradientComponentTester*>(noiseComp)->AssertTrue(cfg);
}
#endif
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);
// This uses custom test / benchmark hooks so that we can load LmbrCentral and GradientSignal Gems.
AZ_UNIT_TEST_HOOK(new UnitTest::FastNoiseTestEnvironment, UnitTest::FastNoiseBenchmarkEnvironment);
+60
View File
@@ -0,0 +1,60 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzFramework/Components/TransformComponent.h>
#include <AzTest/GemTestEnvironment.h>
#include <FastNoiseGradientComponent.h>
#include <FastNoiseSystemComponent.h>
namespace UnitTest
{
// The FastNoise unit tests need to use the GemTestEnvironment to load the GradientSignal and LmbrCentral Gems so that
// GradientTransform components can be used in the unit tests and benchmarks.
class FastNoiseTestEnvironment
: public AZ::Test::GemTestEnvironment
{
public:
void AddGemsAndComponents() override
{
AddDynamicModulePaths({ "GradientSignal" });
AddDynamicModulePaths({ "LmbrCentral" });
AddComponentDescriptors({
AzFramework::TransformComponent::CreateDescriptor(),
FastNoiseGem::FastNoiseSystemComponent::CreateDescriptor(),
FastNoiseGem::FastNoiseGradientComponent::CreateDescriptor()
});
AddRequiredComponents({ FastNoiseGem::FastNoiseSystemComponent::TYPEINFO_Uuid() });
}
};
#ifdef HAVE_BENCHMARK
//! The Benchmark environment is used for one time setup and tear down of shared resources
class FastNoiseBenchmarkEnvironment
: public AZ::Test::BenchmarkEnvironmentBase
, public FastNoiseTestEnvironment
{
protected:
void SetUpBenchmark() override
{
SetupEnvironment();
}
void TearDownBenchmark() override
{
TeardownEnvironment();
}
};
#endif
} // namespace UnitTest
@@ -0,0 +1,13 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
set(FILES
Tests/FastNoiseEditorTest.cpp
Source/FastNoiseModule.h
Source/FastNoiseModule.cpp
)
@@ -7,6 +7,7 @@
#
set(FILES
Tests/FastNoiseBenchmarks.cpp
Tests/FastNoiseTest.cpp
Source/FastNoiseModule.h
Source/FastNoiseModule.cpp
@@ -9,6 +9,7 @@
#ifdef HAVE_BENCHMARK
#include <Tests/GradientSignalTestFixtures.h>
#include <Tests/GradientSignalTestHelpers.h>
#include <AzTest/AzTest.h>
#include <AzCore/Memory/PoolAllocator.h>
@@ -21,220 +22,42 @@ namespace UnitTest
class GradientGetValues : public GradientSignalBenchmarkFixture
{
public:
// We use an enum to list out the different types of GetValue() benchmarks to run so that way we can condense our test cases
// to just take the value in as a benchmark argument and switch on it. Otherwise, we would need to write a different benchmark
// function for each test case for each gradient.
enum GetValuePermutation : int64_t
{
EBUS_GET_VALUE,
EBUS_GET_VALUES,
SAMPLER_GET_VALUE,
SAMPLER_GET_VALUES,
};
// Create an arbitrary size shape for creating our gradients for benchmark runs.
const float TestShapeHalfBounds = 128.0f;
void FillQueryPositions(AZStd::vector<AZ::Vector3>& positions, float height, float width)
{
size_t index = 0;
for (float y = 0.0f; y < height; y += 1.0f)
{
for (float x = 0.0f; x < width; x += 1.0f)
{
positions[index++] = AZ::Vector3(x, y, 0.0f);
}
}
}
void RunEBusGetValueBenchmark(benchmark::State& state, const AZ::EntityId& gradientId, int64_t queryRange)
{
AZ_PROFILE_FUNCTION(Entity);
GradientSignal::GradientSampleParams params;
// Get the height and width ranges for querying from our benchmark parameters
const float height = aznumeric_cast<float>(queryRange);
const float width = aznumeric_cast<float>(queryRange);
// Call GetValue() on the EBus for every height and width in our ranges.
for (auto _ : state)
{
for (float y = 0.0f; y < height; y += 1.0f)
{
for (float x = 0.0f; x < width; x += 1.0f)
{
float value = 0.0f;
params.m_position = AZ::Vector3(x, y, 0.0f);
GradientSignal::GradientRequestBus::EventResult(
value, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params);
benchmark::DoNotOptimize(value);
}
}
}
}
void RunEBusGetValuesBenchmark(benchmark::State& state, const AZ::EntityId& gradientId, int64_t queryRange)
{
AZ_PROFILE_FUNCTION(Entity);
// Get the height and width ranges for querying from our benchmark parameters
float height = aznumeric_cast<float>(queryRange);
float width = aznumeric_cast<float>(queryRange);
int64_t totalQueryPoints = queryRange * queryRange;
// Call GetValues() for every height and width in our ranges.
for (auto _ : state)
{
// Set up our vector of query positions. This is done inside the benchmark timing since we're counting the work to create
// each query position in the single GetValue() call benchmarks, and will make the timing more directly comparable.
AZStd::vector<AZ::Vector3> positions(totalQueryPoints);
FillQueryPositions(positions, height, width);
// Query and get the results.
AZStd::vector<float> results(totalQueryPoints);
GradientSignal::GradientRequestBus::Event(
gradientId, &GradientSignal::GradientRequestBus::Events::GetValues, positions, results);
}
}
void RunSamplerGetValueBenchmark(benchmark::State& state, const AZ::EntityId& gradientId, int64_t queryRange)
{
AZ_PROFILE_FUNCTION(Entity);
// Create a gradient sampler to use for querying our gradient.
GradientSignal::GradientSampler gradientSampler;
gradientSampler.m_gradientId = gradientId;
// Get the height and width ranges for querying from our benchmark parameters
const float height = aznumeric_cast<float>(queryRange);
const float width = aznumeric_cast<float>(queryRange);
// Call GetValue() through the GradientSampler for every height and width in our ranges.
for (auto _ : state)
{
for (float y = 0.0f; y < height; y += 1.0f)
{
for (float x = 0.0f; x < width; x += 1.0f)
{
GradientSignal::GradientSampleParams params;
params.m_position = AZ::Vector3(x, y, 0.0f);
float value = gradientSampler.GetValue(params);
benchmark::DoNotOptimize(value);
}
}
}
}
void RunSamplerGetValuesBenchmark(benchmark::State& state, const AZ::EntityId& gradientId, int64_t queryRange)
{
AZ_PROFILE_FUNCTION(Entity);
// Create a gradient sampler to use for querying our gradient.
GradientSignal::GradientSampler gradientSampler;
gradientSampler.m_gradientId = gradientId;
// Get the height and width ranges for querying from our benchmark parameters
const float height = aznumeric_cast<float>(queryRange);
const float width = aznumeric_cast<float>(queryRange);
const int64_t totalQueryPoints = queryRange * queryRange;
// Call GetValues() through the GradientSampler for every height and width in our ranges.
for (auto _ : state)
{
// Set up our vector of query positions. This is done inside the benchmark timing since we're counting the work to create
// each query position in the single GetValue() call benchmarks, and will make the timing more directly comparable.
AZStd::vector<AZ::Vector3> positions(totalQueryPoints);
FillQueryPositions(positions, height, width);
// Query and get the results.
AZStd::vector<float> results(totalQueryPoints);
gradientSampler.GetValues(positions, results);
}
}
void RunGetValueOrGetValuesBenchmark(benchmark::State& state, const AZ::EntityId& gradientId)
{
switch (state.range(0))
{
case GetValuePermutation::EBUS_GET_VALUE:
RunEBusGetValueBenchmark(state, gradientId, state.range(1));
break;
case GetValuePermutation::EBUS_GET_VALUES:
RunEBusGetValuesBenchmark(state, gradientId, state.range(1));
break;
case GetValuePermutation::SAMPLER_GET_VALUE:
RunSamplerGetValueBenchmark(state, gradientId, state.range(1));
break;
case GetValuePermutation::SAMPLER_GET_VALUES:
RunSamplerGetValuesBenchmark(state, gradientId, state.range(1));
break;
default:
AZ_Assert(false, "Benchmark permutation type not supported.");
}
}
};
// Because there's no good way to label different enums in the output results (they just appear as integer values), we work around it by
// registering one set of benchmark runs for each enum value and use ArgNames() to give it a friendly name in the results.
#define GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(Fixture, Func) \
BENCHMARK_REGISTER_F(Fixture, Func) \
->Args({ GradientGetValues::GetValuePermutation::EBUS_GET_VALUE, 1024 }) \
->Args({ GradientGetValues::GetValuePermutation::EBUS_GET_VALUE, 2048 }) \
->Args({ GradientGetValues::GetValuePermutation::EBUS_GET_VALUE, 4096 }) \
->ArgNames({ "EbusGetValue", "size" }) \
->Unit(::benchmark::kMillisecond); \
BENCHMARK_REGISTER_F(Fixture, Func) \
->Args({ GradientGetValues::GetValuePermutation::EBUS_GET_VALUES, 1024 }) \
->Args({ GradientGetValues::GetValuePermutation::EBUS_GET_VALUES, 2048 }) \
->Args({ GradientGetValues::GetValuePermutation::EBUS_GET_VALUES, 4096 }) \
->ArgNames({ "EbusGetValues", "size" }) \
->Unit(::benchmark::kMillisecond); \
BENCHMARK_REGISTER_F(Fixture, Func) \
->Args({ GradientGetValues::GetValuePermutation::SAMPLER_GET_VALUE, 1024 }) \
->Args({ GradientGetValues::GetValuePermutation::SAMPLER_GET_VALUE, 2048 }) \
->Args({ GradientGetValues::GetValuePermutation::SAMPLER_GET_VALUE, 4096 }) \
->ArgNames({ "SamplerGetValue", "size" }) \
->Unit(::benchmark::kMillisecond); \
BENCHMARK_REGISTER_F(Fixture, Func) \
->Args({ GradientGetValues::GetValuePermutation::SAMPLER_GET_VALUES, 1024 }) \
->Args({ GradientGetValues::GetValuePermutation::SAMPLER_GET_VALUES, 2048 }) \
->Args({ GradientGetValues::GetValuePermutation::SAMPLER_GET_VALUES, 4096 }) \
->ArgNames({ "SamplerGetValues", "size" }) \
->Unit(::benchmark::kMillisecond);
// --------------------------------------------------------------------------------------
// Base Gradients
BENCHMARK_DEFINE_F(GradientGetValues, BM_ConstantGradient)(benchmark::State& state)
{
auto entity = BuildTestConstantGradient(TestShapeHalfBounds);
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
BENCHMARK_DEFINE_F(GradientGetValues, BM_ImageGradient)(benchmark::State& state)
{
auto entity = BuildTestImageGradient(TestShapeHalfBounds);
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
BENCHMARK_DEFINE_F(GradientGetValues, BM_PerlinGradient)(benchmark::State& state)
{
auto entity = BuildTestPerlinGradient(TestShapeHalfBounds);
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
BENCHMARK_DEFINE_F(GradientGetValues, BM_RandomGradient)(benchmark::State& state)
{
auto entity = BuildTestRandomGradient(TestShapeHalfBounds);
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
BENCHMARK_DEFINE_F(GradientGetValues, BM_ShapeAreaFalloffGradient)(benchmark::State& state)
{
auto entity = BuildTestShapeAreaFalloffGradient(TestShapeHalfBounds);
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(GradientGetValues, BM_ConstantGradient);
@@ -250,21 +73,21 @@ namespace UnitTest
{
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto entity = BuildTestDitherGradient(TestShapeHalfBounds, baseEntity->GetId());
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
BENCHMARK_DEFINE_F(GradientGetValues, BM_InvertGradient)(benchmark::State& state)
{
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto entity = BuildTestDitherGradient(TestShapeHalfBounds, baseEntity->GetId());
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
BENCHMARK_DEFINE_F(GradientGetValues, BM_LevelsGradient)(benchmark::State& state)
{
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto entity = BuildTestLevelsGradient(TestShapeHalfBounds, baseEntity->GetId());
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
BENCHMARK_DEFINE_F(GradientGetValues, BM_MixedGradient)(benchmark::State& state)
@@ -272,35 +95,35 @@ namespace UnitTest
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto mixedEntity = BuildTestConstantGradient(TestShapeHalfBounds);
auto entity = BuildTestMixedGradient(TestShapeHalfBounds, baseEntity->GetId(), mixedEntity->GetId());
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
BENCHMARK_DEFINE_F(GradientGetValues, BM_PosterizeGradient)(benchmark::State& state)
{
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto entity = BuildTestPosterizeGradient(TestShapeHalfBounds, baseEntity->GetId());
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
BENCHMARK_DEFINE_F(GradientGetValues, BM_ReferenceGradient)(benchmark::State& state)
{
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto entity = BuildTestReferenceGradient(TestShapeHalfBounds, baseEntity->GetId());
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
BENCHMARK_DEFINE_F(GradientGetValues, BM_SmoothStepGradient)(benchmark::State& state)
{
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto entity = BuildTestSmoothStepGradient(TestShapeHalfBounds, baseEntity->GetId());
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
BENCHMARK_DEFINE_F(GradientGetValues, BM_ThresholdGradient)(benchmark::State& state)
{
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto entity = BuildTestThresholdGradient(TestShapeHalfBounds, baseEntity->GetId());
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(GradientGetValues, BM_DitherGradient);
@@ -321,7 +144,7 @@ namespace UnitTest
CreateMockSurfaceDataSystem(AZ::Aabb::CreateFromMinMax(AZ::Vector3(-TestShapeHalfBounds), AZ::Vector3(TestShapeHalfBounds)));
auto entity = BuildTestSurfaceAltitudeGradient(TestShapeHalfBounds);
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
BENCHMARK_DEFINE_F(GradientGetValues, BM_SurfaceMaskGradient)(benchmark::State& state)
@@ -330,7 +153,7 @@ namespace UnitTest
CreateMockSurfaceDataSystem(AZ::Aabb::CreateFromMinMax(AZ::Vector3(-TestShapeHalfBounds), AZ::Vector3(TestShapeHalfBounds)));
auto entity = BuildTestSurfaceMaskGradient(TestShapeHalfBounds);
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
BENCHMARK_DEFINE_F(GradientGetValues, BM_SurfaceSlopeGradient)(benchmark::State& state)
@@ -339,7 +162,7 @@ namespace UnitTest
CreateMockSurfaceDataSystem(AZ::Aabb::CreateFromMinMax(AZ::Vector3(-TestShapeHalfBounds), AZ::Vector3(TestShapeHalfBounds)));
auto entity = BuildTestSurfaceSlopeGradient(TestShapeHalfBounds);
RunGetValueOrGetValuesBenchmark(state, entity->GetId());
GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(state, entity->GetId());
}
GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(GradientGetValues, BM_SurfaceAltitudeGradient);
@@ -8,6 +8,7 @@
#include <Tests/GradientSignalTestFixtures.h>
#include <Tests/GradientSignalTestHelpers.h>
#include <AzTest/AzTest.h>
namespace UnitTest
@@ -18,79 +19,36 @@ namespace UnitTest
// Create an arbitrary size shape for comparing values within. It should be large enough that we detect any value anomalies
// but small enough that the tests run quickly.
const float TestShapeHalfBounds = 128.0f;
void CompareGetValueAndGetValues(AZ::EntityId gradientEntityId)
{
// Create a gradient sampler and run through a series of points to see if they match expectations.
const AZ::Aabb queryRegion = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-TestShapeHalfBounds), AZ::Vector3(TestShapeHalfBounds));
const AZ::Vector2 stepSize(1.0f, 1.0f);
GradientSignal::GradientSampler gradientSampler;
gradientSampler.m_gradientId = gradientEntityId;
const size_t numSamplesX = aznumeric_cast<size_t>(ceil(queryRegion.GetExtents().GetX() / stepSize.GetX()));
const size_t numSamplesY = aznumeric_cast<size_t>(ceil(queryRegion.GetExtents().GetY() / stepSize.GetY()));
// Build up the list of positions to query.
AZStd::vector<AZ::Vector3> positions(numSamplesX * numSamplesY);
size_t index = 0;
for (size_t yIndex = 0; yIndex < numSamplesY; yIndex++)
{
float y = queryRegion.GetMin().GetY() + (stepSize.GetY() * yIndex);
for (size_t xIndex = 0; xIndex < numSamplesX; xIndex++)
{
float x = queryRegion.GetMin().GetX() + (stepSize.GetX() * xIndex);
positions[index++] = AZ::Vector3(x, y, 0.0f);
}
}
// Get the results from GetValues
AZStd::vector<float> results(numSamplesX * numSamplesY);
gradientSampler.GetValues(positions, results);
// For each position, call GetValue and verify that the values match.
for (size_t positionIndex = 0; positionIndex < positions.size(); positionIndex++)
{
GradientSignal::GradientSampleParams params;
params.m_position = positions[positionIndex];
float value = gradientSampler.GetValue(params);
// We use ASSERT_NEAR instead of EXPECT_NEAR because if one value doesn't match, they probably all won't, so there's no
// reason to keep running and printing failures for every value.
ASSERT_NEAR(value, results[positionIndex], 0.000001f);
}
}
};
TEST_F(GradientSignalGetValuesTestsFixture, ImageGradientComponent_VerifyGetValueAndGetValuesMatch)
{
auto entity = BuildTestImageGradient(TestShapeHalfBounds);
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, PerlinGradientComponent_VerifyGetValueAndGetValuesMatch)
{
auto entity = BuildTestPerlinGradient(TestShapeHalfBounds);
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, RandomGradientComponent_VerifyGetValueAndGetValuesMatch)
{
auto entity = BuildTestRandomGradient(TestShapeHalfBounds);
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, ConstantGradientComponent_VerifyGetValueAndGetValuesMatch)
{
auto entity = BuildTestConstantGradient(TestShapeHalfBounds);
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, ShapeAreaFalloffGradientComponent_VerifyGetValueAndGetValuesMatch)
{
auto entity = BuildTestShapeAreaFalloffGradient(TestShapeHalfBounds);
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, DitherGradientComponent_VerifyGetValueAndGetValuesMatch)
@@ -98,21 +56,21 @@ namespace UnitTest
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto entity = BuildTestDitherGradient(TestShapeHalfBounds, baseEntity->GetId());
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, InvertGradientComponent_VerifyGetValueAndGetValuesMatch)
{
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto entity = BuildTestInvertGradient(TestShapeHalfBounds, baseEntity->GetId());
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, LevelsGradientComponent_VerifyGetValueAndGetValuesMatch)
{
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto entity = BuildTestLevelsGradient(TestShapeHalfBounds, baseEntity->GetId());
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, MixedGradientComponent_VerifyGetValueAndGetValuesMatch)
@@ -120,35 +78,35 @@ namespace UnitTest
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto mixedEntity = BuildTestConstantGradient(TestShapeHalfBounds);
auto entity = BuildTestMixedGradient(TestShapeHalfBounds, baseEntity->GetId(), mixedEntity->GetId());
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, PosterizeGradientComponent_VerifyGetValueAndGetValuesMatch)
{
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto entity = BuildTestPosterizeGradient(TestShapeHalfBounds, baseEntity->GetId());
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, ReferenceGradientComponent_VerifyGetValueAndGetValuesMatch)
{
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto entity = BuildTestReferenceGradient(TestShapeHalfBounds, baseEntity->GetId());
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, SmoothStepGradientComponent_VerifyGetValueAndGetValuesMatch)
{
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto entity = BuildTestSmoothStepGradient(TestShapeHalfBounds, baseEntity->GetId());
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, ThresholdGradientComponent_VerifyGetValueAndGetValuesMatch)
{
auto baseEntity = BuildTestRandomGradient(TestShapeHalfBounds);
auto entity = BuildTestThresholdGradient(TestShapeHalfBounds, baseEntity->GetId());
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, SurfaceAltitudeGradientComponent_VerifyGetValueAndGetValuesMatch)
@@ -157,7 +115,7 @@ namespace UnitTest
CreateMockSurfaceDataSystem(AZ::Aabb::CreateFromMinMax(AZ::Vector3(-TestShapeHalfBounds), AZ::Vector3(TestShapeHalfBounds)));
auto entity = BuildTestSurfaceAltitudeGradient(TestShapeHalfBounds);
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, SurfaceMaskGradientComponent_VerifyGetValueAndGetValuesMatch)
@@ -166,7 +124,7 @@ namespace UnitTest
CreateMockSurfaceDataSystem(AZ::Aabb::CreateFromMinMax(AZ::Vector3(-TestShapeHalfBounds), AZ::Vector3(TestShapeHalfBounds)));
auto entity = BuildTestSurfaceMaskGradient(TestShapeHalfBounds);
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
TEST_F(GradientSignalGetValuesTestsFixture, SurfaceSlopeGradientComponent_VerifyGetValueAndGetValuesMatch)
@@ -175,7 +133,7 @@ namespace UnitTest
CreateMockSurfaceDataSystem(AZ::Aabb::CreateFromMinMax(AZ::Vector3(-TestShapeHalfBounds), AZ::Vector3(TestShapeHalfBounds)));
auto entity = BuildTestSurfaceSlopeGradient(TestShapeHalfBounds);
CompareGetValueAndGetValues(entity->GetId());
GradientSignalTestHelpers::CompareGetValueAndGetValues(entity->GetId(), TestShapeHalfBounds);
}
}
@@ -0,0 +1,203 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Tests/GradientSignalTestHelpers.h>
#include <AzCore/Math/Aabb.h>
#include <GradientSignal/GradientSampler.h>
namespace UnitTest
{
void GradientSignalTestHelpers::CompareGetValueAndGetValues(AZ::EntityId gradientEntityId, float shapeHalfBounds)
{
// Create a gradient sampler and run through a series of points to see if they match expectations.
const AZ::Aabb queryRegion = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-shapeHalfBounds), AZ::Vector3(shapeHalfBounds));
const AZ::Vector2 stepSize(1.0f, 1.0f);
GradientSignal::GradientSampler gradientSampler;
gradientSampler.m_gradientId = gradientEntityId;
const size_t numSamplesX = aznumeric_cast<size_t>(ceil(queryRegion.GetExtents().GetX() / stepSize.GetX()));
const size_t numSamplesY = aznumeric_cast<size_t>(ceil(queryRegion.GetExtents().GetY() / stepSize.GetY()));
// Build up the list of positions to query.
AZStd::vector<AZ::Vector3> positions(numSamplesX * numSamplesY);
size_t index = 0;
for (size_t yIndex = 0; yIndex < numSamplesY; yIndex++)
{
float y = queryRegion.GetMin().GetY() + (stepSize.GetY() * yIndex);
for (size_t xIndex = 0; xIndex < numSamplesX; xIndex++)
{
float x = queryRegion.GetMin().GetX() + (stepSize.GetX() * xIndex);
positions[index++] = AZ::Vector3(x, y, 0.0f);
}
}
// Get the results from GetValues
AZStd::vector<float> results(numSamplesX * numSamplesY);
gradientSampler.GetValues(positions, results);
// For each position, call GetValue and verify that the values match.
for (size_t positionIndex = 0; positionIndex < positions.size(); positionIndex++)
{
GradientSignal::GradientSampleParams params;
params.m_position = positions[positionIndex];
float value = gradientSampler.GetValue(params);
// We use ASSERT_NEAR instead of EXPECT_NEAR because if one value doesn't match, they probably all won't, so there's no
// reason to keep running and printing failures for every value.
ASSERT_NEAR(value, results[positionIndex], 0.000001f);
}
}
#ifdef HAVE_BENCHMARK
void GradientSignalTestHelpers::FillQueryPositions(AZStd::vector<AZ::Vector3>& positions, float height, float width)
{
size_t index = 0;
for (float y = 0.0f; y < height; y += 1.0f)
{
for (float x = 0.0f; x < width; x += 1.0f)
{
positions[index++] = AZ::Vector3(x, y, 0.0f);
}
}
}
void GradientSignalTestHelpers::RunEBusGetValueBenchmark(benchmark::State& state, const AZ::EntityId& gradientId, int64_t queryRange)
{
AZ_PROFILE_FUNCTION(Entity);
GradientSignal::GradientSampleParams params;
// Get the height and width ranges for querying from our benchmark parameters
const float height = aznumeric_cast<float>(queryRange);
const float width = aznumeric_cast<float>(queryRange);
// Call GetValue() on the EBus for every height and width in our ranges.
for (auto _ : state)
{
for (float y = 0.0f; y < height; y += 1.0f)
{
for (float x = 0.0f; x < width; x += 1.0f)
{
float value = 0.0f;
params.m_position = AZ::Vector3(x, y, 0.0f);
GradientSignal::GradientRequestBus::EventResult(
value, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params);
benchmark::DoNotOptimize(value);
}
}
}
}
void GradientSignalTestHelpers::RunEBusGetValuesBenchmark(benchmark::State& state, const AZ::EntityId& gradientId, int64_t queryRange)
{
AZ_PROFILE_FUNCTION(Entity);
// Get the height and width ranges for querying from our benchmark parameters
float height = aznumeric_cast<float>(queryRange);
float width = aznumeric_cast<float>(queryRange);
int64_t totalQueryPoints = queryRange * queryRange;
// Call GetValues() for every height and width in our ranges.
for (auto _ : state)
{
// Set up our vector of query positions. This is done inside the benchmark timing since we're counting the work to create
// each query position in the single GetValue() call benchmarks, and will make the timing more directly comparable.
AZStd::vector<AZ::Vector3> positions(totalQueryPoints);
FillQueryPositions(positions, height, width);
// Query and get the results.
AZStd::vector<float> results(totalQueryPoints);
GradientSignal::GradientRequestBus::Event(
gradientId, &GradientSignal::GradientRequestBus::Events::GetValues, positions, results);
}
}
void GradientSignalTestHelpers::RunSamplerGetValueBenchmark(benchmark::State& state, const AZ::EntityId& gradientId, int64_t queryRange)
{
AZ_PROFILE_FUNCTION(Entity);
// Create a gradient sampler to use for querying our gradient.
GradientSignal::GradientSampler gradientSampler;
gradientSampler.m_gradientId = gradientId;
// Get the height and width ranges for querying from our benchmark parameters
const float height = aznumeric_cast<float>(queryRange);
const float width = aznumeric_cast<float>(queryRange);
// Call GetValue() through the GradientSampler for every height and width in our ranges.
for (auto _ : state)
{
for (float y = 0.0f; y < height; y += 1.0f)
{
for (float x = 0.0f; x < width; x += 1.0f)
{
GradientSignal::GradientSampleParams params;
params.m_position = AZ::Vector3(x, y, 0.0f);
float value = gradientSampler.GetValue(params);
benchmark::DoNotOptimize(value);
}
}
}
}
void GradientSignalTestHelpers::RunSamplerGetValuesBenchmark(
benchmark::State& state, const AZ::EntityId& gradientId, int64_t queryRange)
{
AZ_PROFILE_FUNCTION(Entity);
// Create a gradient sampler to use for querying our gradient.
GradientSignal::GradientSampler gradientSampler;
gradientSampler.m_gradientId = gradientId;
// Get the height and width ranges for querying from our benchmark parameters
const float height = aznumeric_cast<float>(queryRange);
const float width = aznumeric_cast<float>(queryRange);
const int64_t totalQueryPoints = queryRange * queryRange;
// Call GetValues() through the GradientSampler for every height and width in our ranges.
for (auto _ : state)
{
// Set up our vector of query positions. This is done inside the benchmark timing since we're counting the work to create
// each query position in the single GetValue() call benchmarks, and will make the timing more directly comparable.
AZStd::vector<AZ::Vector3> positions(totalQueryPoints);
FillQueryPositions(positions, height, width);
// Query and get the results.
AZStd::vector<float> results(totalQueryPoints);
gradientSampler.GetValues(positions, results);
}
}
void GradientSignalTestHelpers::RunGetValueOrGetValuesBenchmark(benchmark::State& state, const AZ::EntityId& gradientId)
{
switch (state.range(0))
{
case GetValuePermutation::EBUS_GET_VALUE:
RunEBusGetValueBenchmark(state, gradientId, state.range(1));
break;
case GetValuePermutation::EBUS_GET_VALUES:
RunEBusGetValuesBenchmark(state, gradientId, state.range(1));
break;
case GetValuePermutation::SAMPLER_GET_VALUE:
RunSamplerGetValueBenchmark(state, gradientId, state.range(1));
break;
case GetValuePermutation::SAMPLER_GET_VALUES:
RunSamplerGetValuesBenchmark(state, gradientId, state.range(1));
break;
default:
AZ_Assert(false, "Benchmark permutation type not supported.");
}
}
#endif
}
@@ -0,0 +1,76 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Component/EntityId.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/std/containers/vector.h>
#include <AzTest/AzTest.h>
namespace UnitTest
{
class GradientSignalTestHelpers
{
public:
static void CompareGetValueAndGetValues(AZ::EntityId gradientEntityId, float shapeHalfBounds);
#ifdef HAVE_BENCHMARK
// We use an enum to list out the different types of GetValue() benchmarks to run so that way we can condense our test cases
// to just take the value in as a benchmark argument and switch on it. Otherwise, we would need to write a different benchmark
// function for each test case for each gradient.
enum GetValuePermutation : int64_t
{
EBUS_GET_VALUE,
EBUS_GET_VALUES,
SAMPLER_GET_VALUE,
SAMPLER_GET_VALUES,
};
static void FillQueryPositions(AZStd::vector<AZ::Vector3>& positions, float height, float width);
static void RunEBusGetValueBenchmark(benchmark::State& state, const AZ::EntityId& gradientId, int64_t queryRange);
static void RunEBusGetValuesBenchmark(benchmark::State& state, const AZ::EntityId& gradientId, int64_t queryRange);
static void RunSamplerGetValueBenchmark(benchmark::State& state, const AZ::EntityId& gradientId, int64_t queryRange);
static void RunSamplerGetValuesBenchmark(benchmark::State& state, const AZ::EntityId& gradientId, int64_t queryRange);
static void RunGetValueOrGetValuesBenchmark(benchmark::State& state, const AZ::EntityId& gradientId);
// Because there's no good way to label different enums in the output results (they just appear as integer values), we work around it by
// registering one set of benchmark runs for each enum value and use ArgNames() to give it a friendly name in the results.
#ifndef GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F
#define GRADIENT_SIGNAL_GET_VALUES_BENCHMARK_REGISTER_F(Fixture, Func) \
BENCHMARK_REGISTER_F(Fixture, Func) \
->Args({ GradientSignalTestHelpers::GetValuePermutation::EBUS_GET_VALUE, 1024 }) \
->Args({ GradientSignalTestHelpers::GetValuePermutation::EBUS_GET_VALUE, 2048 }) \
->Args({ GradientSignalTestHelpers::GetValuePermutation::EBUS_GET_VALUE, 4096 }) \
->ArgNames({ "EbusGetValue", "size" }) \
->Unit(::benchmark::kMillisecond); \
BENCHMARK_REGISTER_F(Fixture, Func) \
->Args({ GradientSignalTestHelpers::GetValuePermutation::EBUS_GET_VALUES, 1024 }) \
->Args({ GradientSignalTestHelpers::GetValuePermutation::EBUS_GET_VALUES, 2048 }) \
->Args({ GradientSignalTestHelpers::GetValuePermutation::EBUS_GET_VALUES, 4096 }) \
->ArgNames({ "EbusGetValues", "size" }) \
->Unit(::benchmark::kMillisecond); \
BENCHMARK_REGISTER_F(Fixture, Func) \
->Args({ GradientSignalTestHelpers::GetValuePermutation::SAMPLER_GET_VALUE, 1024 }) \
->Args({ GradientSignalTestHelpers::GetValuePermutation::SAMPLER_GET_VALUE, 2048 }) \
->Args({ GradientSignalTestHelpers::GetValuePermutation::SAMPLER_GET_VALUE, 4096 }) \
->ArgNames({ "SamplerGetValue", "size" }) \
->Unit(::benchmark::kMillisecond); \
BENCHMARK_REGISTER_F(Fixture, Func) \
->Args({ GradientSignalTestHelpers::GetValuePermutation::SAMPLER_GET_VALUES, 1024 }) \
->Args({ GradientSignalTestHelpers::GetValuePermutation::SAMPLER_GET_VALUES, 2048 }) \
->Args({ GradientSignalTestHelpers::GetValuePermutation::SAMPLER_GET_VALUES, 4096 }) \
->ArgNames({ "SamplerGetValues", "size" }) \
->Unit(::benchmark::kMillisecond);
#endif
#endif
};
}
@@ -7,6 +7,8 @@
#
set(FILES
Tests/GradientSignalTestHelpers.cpp
Tests/GradientSignalTestHelpers.h
Tests/GradientSignalTestFixtures.cpp
Tests/GradientSignalTestFixtures.h
Tests/GradientSignalTestMocks.cpp