Naive GetValues() implementation. (#6741)
* Naive GetValues() implementation. Added the method itself, and the benchmarks which show that even the naive version is currently 10-50% faster than calling GetValue() for multiple values. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Added comments documenting why the const_cast is there. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed link errors by creating new Shared.Tests lib. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Addressed PR feedback. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed incorrect comparison. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>
This commit is contained in:
@@ -124,6 +124,26 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
|||||||
Mocks
|
Mocks
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ly_add_target(
|
||||||
|
NAME GradientSignal.Tests.Static STATIC
|
||||||
|
NAMESPACE Gem
|
||||||
|
FILES_CMAKE
|
||||||
|
gradientsignal_shared_tests_files.cmake
|
||||||
|
INCLUDE_DIRECTORIES
|
||||||
|
PUBLIC
|
||||||
|
Tests
|
||||||
|
PRIVATE
|
||||||
|
.
|
||||||
|
Source
|
||||||
|
BUILD_DEPENDENCIES
|
||||||
|
PRIVATE
|
||||||
|
AZ::AzTest
|
||||||
|
AZ::AzTestShared
|
||||||
|
Gem::GradientSignal.Static
|
||||||
|
Gem::LmbrCentral
|
||||||
|
Gem::GradientSignal.Mocks
|
||||||
|
)
|
||||||
|
|
||||||
ly_add_target(
|
ly_add_target(
|
||||||
NAME GradientSignal.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
NAME GradientSignal.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||||
NAMESPACE Gem
|
NAMESPACE Gem
|
||||||
@@ -137,6 +157,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
|||||||
PRIVATE
|
PRIVATE
|
||||||
AZ::AzTest
|
AZ::AzTest
|
||||||
AZ::AzTestShared
|
AZ::AzTestShared
|
||||||
|
Gem::GradientSignal.Tests.Static
|
||||||
Gem::GradientSignal.Static
|
Gem::GradientSignal.Static
|
||||||
Gem::LmbrCentral
|
Gem::LmbrCentral
|
||||||
Gem::GradientSignal.Mocks
|
Gem::GradientSignal.Mocks
|
||||||
@@ -165,6 +186,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
|||||||
PRIVATE
|
PRIVATE
|
||||||
AZ::AzTest
|
AZ::AzTest
|
||||||
AZ::AzTestShared
|
AZ::AzTestShared
|
||||||
|
Gem::GradientSignal.Tests.Static
|
||||||
Gem::GradientSignal.Static
|
Gem::GradientSignal.Static
|
||||||
Gem::GradientSignal.Editor.Static
|
Gem::GradientSignal.Editor.Static
|
||||||
Gem::LmbrCentral.Editor
|
Gem::LmbrCentral.Editor
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
#include <AzCore/Component/EntityId.h>
|
#include <AzCore/Component/EntityId.h>
|
||||||
#include <AzCore/Math/Vector3.h>
|
#include <AzCore/Math/Vector3.h>
|
||||||
|
|
||||||
|
#include <AtomCore/std/containers/array_view.h>
|
||||||
|
|
||||||
namespace GradientSignal
|
namespace GradientSignal
|
||||||
{
|
{
|
||||||
struct GradientSampleParams final
|
struct GradientSampleParams final
|
||||||
@@ -49,6 +51,36 @@ namespace GradientSignal
|
|||||||
*/
|
*/
|
||||||
virtual float GetValue(const GradientSampleParams& sampleParams) const = 0;
|
virtual float GetValue(const GradientSampleParams& sampleParams) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a list of positions, generate values. Implementations of this need to be thread-safe without using locks,
|
||||||
|
* as it can get called from multiple threads simultaneously and has the potential to cause lock inversion deadlocks.
|
||||||
|
* \param positions The input list of positions to query.
|
||||||
|
* \param outValues The output list of values. This list is expected to be the same size as the positions list.
|
||||||
|
*/
|
||||||
|
virtual void GetValues(AZStd::array_view<AZ::Vector3> positions, AZStd::array_view<float> outValues) const
|
||||||
|
{
|
||||||
|
// Reference implementation of GetValues for any gradients that don't have their own optimized implementations.
|
||||||
|
// This is 10%-60% faster than calling GetValue via EBus many times due to the per-call EBus overhead.
|
||||||
|
|
||||||
|
AZ_Assert(
|
||||||
|
positions.size() == outValues.size(), "input and output lists are different sizes (%zu vs %zu).",
|
||||||
|
positions.size(), outValues.size());
|
||||||
|
|
||||||
|
if (positions.size() == outValues.size())
|
||||||
|
{
|
||||||
|
GradientSampleParams sampleParams;
|
||||||
|
for (size_t index = 0; index < positions.size(); index++)
|
||||||
|
{
|
||||||
|
sampleParams.m_position = positions[index];
|
||||||
|
|
||||||
|
// The const_cast is necessary for now since array_view currently only supports const entries.
|
||||||
|
// If/when array_view is fixed to support non-const, or AZStd::span gets created, the const_cast can get removed.
|
||||||
|
auto& outValue = const_cast<float&>(outValues[index]);
|
||||||
|
outValue = GetValue(sampleParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call to check the hierarchy to see if a given entityId exists in the gradient signal chain
|
* Call to check the hierarchy to see if a given entityId exists in the gradient signal chain
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ namespace GradientSignal
|
|||||||
static void Reflect(AZ::ReflectContext* context);
|
static void Reflect(AZ::ReflectContext* context);
|
||||||
|
|
||||||
inline float GetValue(const GradientSampleParams& sampleParams) const;
|
inline float GetValue(const GradientSampleParams& sampleParams) const;
|
||||||
|
inline void GetValues(AZStd::array_view<AZ::Vector3> positions, AZStd::array_view<float> outValues) const;
|
||||||
|
|
||||||
bool IsEntityInHierarchy(const AZ::EntityId& entityId) const;
|
bool IsEntityInHierarchy(const AZ::EntityId& entityId) const;
|
||||||
|
|
||||||
@@ -145,4 +146,93 @@ namespace GradientSignal
|
|||||||
|
|
||||||
return output * m_opacity;
|
return output * m_opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void GradientSampler::GetValues(AZStd::array_view<AZ::Vector3> positions, AZStd::array_view<float> outValues) const
|
||||||
|
{
|
||||||
|
auto ClearOutputValues = [](AZStd::array_view<float> outValues)
|
||||||
|
{
|
||||||
|
// If we don't have a valid gradient (or it is fully transparent), clear out all the output values.
|
||||||
|
for (size_t index = 0; index < outValues.size(); index++)
|
||||||
|
{
|
||||||
|
// The const_cast is necessary for now since array_view currently only supports const entries.
|
||||||
|
// If/when array_view is fixed to support non-const, or AZStd::span gets created, the const_cast can get removed.
|
||||||
|
auto& outValue = const_cast<float&>(outValues[index]);
|
||||||
|
outValue = 0.0f;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (m_opacity <= 0.0f || !m_gradientId.IsValid())
|
||||||
|
{
|
||||||
|
ClearOutputValues(outValues);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AZStd::vector<AZ::Vector3> transformedPositions;
|
||||||
|
bool useTransformedPositions = false;
|
||||||
|
|
||||||
|
// apply transform if set
|
||||||
|
if (m_enableTransform && GradientSamplerUtil::AreTransformParamsSet(*this))
|
||||||
|
{
|
||||||
|
AZ::Matrix3x4 matrix3x4;
|
||||||
|
matrix3x4.SetFromEulerDegrees(m_rotate);
|
||||||
|
matrix3x4.MultiplyByScale(m_scale);
|
||||||
|
matrix3x4.SetTranslation(m_translate);
|
||||||
|
|
||||||
|
useTransformedPositions = true;
|
||||||
|
transformedPositions.resize(positions.size());
|
||||||
|
for (size_t index = 0; index < positions.size(); index++)
|
||||||
|
{
|
||||||
|
transformedPositions[index] = matrix3x4 * positions[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Block other threads from accessing the surface data bus while we are in GetValue (which may call into the SurfaceData bus).
|
||||||
|
// We lock our surface data mutex *before* checking / setting "isRequestInProgress" so that we prevent race conditions
|
||||||
|
// that create false detection of cyclic dependencies when multiple requests occur on different threads simultaneously.
|
||||||
|
// (One case where this was previously able to occur was in rapid updating of the Preview widget on the
|
||||||
|
// GradientSurfaceDataComponent in the Editor when moving the threshold sliders back and forth rapidly)
|
||||||
|
auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false);
|
||||||
|
typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex);
|
||||||
|
|
||||||
|
if (m_isRequestInProgress)
|
||||||
|
{
|
||||||
|
AZ_ErrorOnce("GradientSignal", !m_isRequestInProgress, "Detected cyclic dependences with gradient entity references");
|
||||||
|
ClearOutputValues(outValues);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_isRequestInProgress = true;
|
||||||
|
|
||||||
|
GradientRequestBus::Event(
|
||||||
|
m_gradientId, &GradientRequestBus::Events::GetValues, useTransformedPositions ? transformedPositions : positions,
|
||||||
|
outValues);
|
||||||
|
|
||||||
|
m_isRequestInProgress = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform any post-fetch transformations on the gradient values (invert, levels, opacity).
|
||||||
|
for (size_t index = 0; index < outValues.size(); index++)
|
||||||
|
{
|
||||||
|
// The const_cast is necessary for now since array_view currently only supports const entries.
|
||||||
|
// If/when array_view is fixed to support non-const, or AZStd::span gets created, the const_cast can get removed.
|
||||||
|
auto& outValue = const_cast<float&>(outValues[index]);
|
||||||
|
|
||||||
|
if (m_invertInput)
|
||||||
|
{
|
||||||
|
outValue = 1.0f - outValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply levels if set
|
||||||
|
if (m_enableLevels && GradientSamplerUtil::AreLevelParamsSet(*this))
|
||||||
|
{
|
||||||
|
outValue = GetLevels(outValue, m_inputMid, m_inputMin, m_inputMax, m_outputMin, m_outputMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
outValue = outValue * m_opacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,74 +23,145 @@
|
|||||||
|
|
||||||
namespace UnitTest
|
namespace UnitTest
|
||||||
{
|
{
|
||||||
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_ImageGradientGetValue)(benchmark::State& state)
|
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_ImageGradientEBusGetValue)(benchmark::State& state)
|
||||||
{
|
{
|
||||||
// Create the Image Gradient Component with some default sizes and parameters.
|
CreateTestImageGradient(m_testEntity.get());
|
||||||
GradientSignal::ImageGradientConfig config;
|
RunEBusGetValueBenchmark(state);
|
||||||
const uint32_t imageSize = 4096;
|
|
||||||
const int32_t imageSeed = 12345;
|
|
||||||
config.m_imageAsset = ImageAssetMockAssetHandler::CreateImageAsset(imageSize, imageSize, imageSeed);
|
|
||||||
config.m_tilingX = 1.0f;
|
|
||||||
config.m_tilingY = 1.0f;
|
|
||||||
CreateComponent<GradientSignal::ImageGradientComponent>(m_testEntity.get(), config);
|
|
||||||
|
|
||||||
// Create the Gradient Transform Component with some default parameters.
|
|
||||||
GradientSignal::GradientTransformConfig gradientTransformConfig;
|
|
||||||
gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None;
|
|
||||||
CreateComponent<GradientSignal::GradientTransformComponent>(m_testEntity.get(), gradientTransformConfig);
|
|
||||||
|
|
||||||
// Run the benchmark
|
|
||||||
RunGetValueBenchmark(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_ImageGradientGetValue)
|
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_ImageGradientEBusGetValue)
|
||||||
->Args({ 1024, 1024 })
|
->Args({ 1024, 1024 })
|
||||||
->Args({ 2048, 2048 })
|
->Args({ 2048, 2048 })
|
||||||
->Args({ 4096, 4096 })
|
->Args({ 4096, 4096 })
|
||||||
->Unit(::benchmark::kMillisecond);
|
->Unit(::benchmark::kMillisecond);
|
||||||
|
|
||||||
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_PerlinGradientGetValue)(benchmark::State& state)
|
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_ImageGradientEBusGetValues)(benchmark::State& state)
|
||||||
{
|
{
|
||||||
// Create the Perlin Gradient Component with some default sizes and parameters.
|
CreateTestImageGradient(m_testEntity.get());
|
||||||
GradientSignal::PerlinGradientConfig config;
|
RunEBusGetValuesBenchmark(state);
|
||||||
config.m_amplitude = 1.0f;
|
|
||||||
config.m_frequency = 1.1f;
|
|
||||||
config.m_octave = 4;
|
|
||||||
config.m_randomSeed = 12345;
|
|
||||||
CreateComponent<GradientSignal::PerlinGradientComponent>(m_testEntity.get(), config);
|
|
||||||
|
|
||||||
// Create the Gradient Transform Component with some default parameters.
|
|
||||||
GradientSignal::GradientTransformConfig gradientTransformConfig;
|
|
||||||
gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None;
|
|
||||||
CreateComponent<GradientSignal::GradientTransformComponent>(m_testEntity.get(), gradientTransformConfig);
|
|
||||||
|
|
||||||
// Run the benchmark
|
|
||||||
RunGetValueBenchmark(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_PerlinGradientGetValue)
|
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_ImageGradientEBusGetValues)
|
||||||
->Args({ 1024, 1024 })
|
->Args({ 1024, 1024 })
|
||||||
->Args({ 2048, 2048 })
|
->Args({ 2048, 2048 })
|
||||||
->Args({ 4096, 4096 })
|
->Args({ 4096, 4096 })
|
||||||
->Unit(::benchmark::kMillisecond);
|
->Unit(::benchmark::kMillisecond);
|
||||||
|
|
||||||
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_RandomGradientGetValue)(benchmark::State& state)
|
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_ImageGradientSamplerGetValue)(benchmark::State& state)
|
||||||
{
|
{
|
||||||
// Create the Random Gradient Component with some default parameters.
|
CreateTestImageGradient(m_testEntity.get());
|
||||||
GradientSignal::RandomGradientConfig config;
|
RunSamplerGetValueBenchmark(state);
|
||||||
config.m_randomSeed = 12345;
|
|
||||||
CreateComponent<GradientSignal::RandomGradientComponent>(m_testEntity.get(), config);
|
|
||||||
|
|
||||||
// Create the Gradient Transform Component with some default parameters.
|
|
||||||
GradientSignal::GradientTransformConfig gradientTransformConfig;
|
|
||||||
gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None;
|
|
||||||
CreateComponent<GradientSignal::GradientTransformComponent>(m_testEntity.get(), gradientTransformConfig);
|
|
||||||
|
|
||||||
// Run the benchmark
|
|
||||||
RunGetValueBenchmark(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_RandomGradientGetValue)
|
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_ImageGradientSamplerGetValue)
|
||||||
|
->Args({ 1024, 1024 })
|
||||||
|
->Args({ 2048, 2048 })
|
||||||
|
->Args({ 4096, 4096 })
|
||||||
|
->Unit(::benchmark::kMillisecond);
|
||||||
|
|
||||||
|
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_ImageGradientSamplerGetValues)(benchmark::State& state)
|
||||||
|
{
|
||||||
|
CreateTestImageGradient(m_testEntity.get());
|
||||||
|
RunSamplerGetValuesBenchmark(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_ImageGradientSamplerGetValues)
|
||||||
|
->Args({ 1024, 1024 })
|
||||||
|
->Args({ 2048, 2048 })
|
||||||
|
->Args({ 4096, 4096 })
|
||||||
|
->Unit(::benchmark::kMillisecond);
|
||||||
|
|
||||||
|
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_PerlinGradientEBusGetValue)(benchmark::State& state)
|
||||||
|
{
|
||||||
|
CreateTestPerlinGradient(m_testEntity.get());
|
||||||
|
RunEBusGetValueBenchmark(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_PerlinGradientEBusGetValue)
|
||||||
|
->Args({ 1024, 1024 })
|
||||||
|
->Args({ 2048, 2048 })
|
||||||
|
->Args({ 4096, 4096 })
|
||||||
|
->Unit(::benchmark::kMillisecond);
|
||||||
|
|
||||||
|
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_PerlinGradientEBusGetValues)(benchmark::State& state)
|
||||||
|
{
|
||||||
|
CreateTestPerlinGradient(m_testEntity.get());
|
||||||
|
RunEBusGetValuesBenchmark(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_PerlinGradientEBusGetValues)
|
||||||
|
->Args({ 1024, 1024 })
|
||||||
|
->Args({ 2048, 2048 })
|
||||||
|
->Args({ 4096, 4096 })
|
||||||
|
->Unit(::benchmark::kMillisecond);
|
||||||
|
|
||||||
|
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_PerlinGradientSamplerGetValue)(benchmark::State& state)
|
||||||
|
{
|
||||||
|
CreateTestPerlinGradient(m_testEntity.get());
|
||||||
|
RunSamplerGetValueBenchmark(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_PerlinGradientSamplerGetValue)
|
||||||
|
->Args({ 1024, 1024 })
|
||||||
|
->Args({ 2048, 2048 })
|
||||||
|
->Args({ 4096, 4096 })
|
||||||
|
->Unit(::benchmark::kMillisecond);
|
||||||
|
|
||||||
|
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_PerlinGradientSamplerGetValues)(benchmark::State& state)
|
||||||
|
{
|
||||||
|
CreateTestPerlinGradient(m_testEntity.get());
|
||||||
|
RunSamplerGetValuesBenchmark(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_PerlinGradientSamplerGetValues)
|
||||||
|
->Args({ 1024, 1024 })
|
||||||
|
->Args({ 2048, 2048 })
|
||||||
|
->Args({ 4096, 4096 })
|
||||||
|
->Unit(::benchmark::kMillisecond);
|
||||||
|
|
||||||
|
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_RandomGradientEBusGetValue)(benchmark::State& state)
|
||||||
|
{
|
||||||
|
CreateTestRandomGradient(m_testEntity.get());
|
||||||
|
RunEBusGetValueBenchmark(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_RandomGradientEBusGetValue)
|
||||||
|
->Args({ 1024, 1024 })
|
||||||
|
->Args({ 2048, 2048 })
|
||||||
|
->Args({ 4096, 4096 })
|
||||||
|
->Unit(::benchmark::kMillisecond);
|
||||||
|
|
||||||
|
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_RandomGradientEBusGetValues)(benchmark::State& state)
|
||||||
|
{
|
||||||
|
CreateTestRandomGradient(m_testEntity.get());
|
||||||
|
RunEBusGetValuesBenchmark(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_RandomGradientEBusGetValues)
|
||||||
|
->Args({ 1024, 1024 })
|
||||||
|
->Args({ 2048, 2048 })
|
||||||
|
->Args({ 4096, 4096 })
|
||||||
|
->Unit(::benchmark::kMillisecond);
|
||||||
|
|
||||||
|
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_RandomGradientSamplerGetValue)(benchmark::State& state)
|
||||||
|
{
|
||||||
|
CreateTestRandomGradient(m_testEntity.get());
|
||||||
|
RunSamplerGetValueBenchmark(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_RandomGradientSamplerGetValue)
|
||||||
|
->Args({ 1024, 1024 })
|
||||||
|
->Args({ 2048, 2048 })
|
||||||
|
->Args({ 4096, 4096 })
|
||||||
|
->Unit(::benchmark::kMillisecond);
|
||||||
|
|
||||||
|
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_RandomGradientSamplerGetValues)(benchmark::State& state)
|
||||||
|
{
|
||||||
|
CreateTestRandomGradient(m_testEntity.get());
|
||||||
|
RunSamplerGetValuesBenchmark(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_RandomGradientSamplerGetValues)
|
||||||
->Args({ 1024, 1024 })
|
->Args({ 1024, 1024 })
|
||||||
->Args({ 2048, 2048 })
|
->Args({ 2048, 2048 })
|
||||||
->Args({ 4096, 4096 })
|
->Args({ 4096, 4096 })
|
||||||
|
|||||||
@@ -9,6 +9,11 @@
|
|||||||
|
|
||||||
#include <Tests/GradientSignalTestFixtures.h>
|
#include <Tests/GradientSignalTestFixtures.h>
|
||||||
|
|
||||||
|
#include <GradientSignal/Components/GradientTransformComponent.h>
|
||||||
|
#include <GradientSignal/Components/ImageGradientComponent.h>
|
||||||
|
#include <GradientSignal/Components/PerlinGradientComponent.h>
|
||||||
|
#include <GradientSignal/Components/RandomGradientComponent.h>
|
||||||
|
|
||||||
namespace UnitTest
|
namespace UnitTest
|
||||||
{
|
{
|
||||||
void GradientSignalBaseFixture::SetupCoreSystems()
|
void GradientSignalBaseFixture::SetupCoreSystems()
|
||||||
@@ -86,8 +91,56 @@ namespace UnitTest
|
|||||||
m_testEntity.reset();
|
m_testEntity.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GradientSignalBenchmarkFixture::RunGetValueBenchmark(benchmark::State& state)
|
void GradientSignalBenchmarkFixture::CreateTestImageGradient(AZ::Entity* entity)
|
||||||
{
|
{
|
||||||
|
// Create the Image Gradient Component with some default sizes and parameters.
|
||||||
|
GradientSignal::ImageGradientConfig config;
|
||||||
|
const uint32_t imageSize = 4096;
|
||||||
|
const int32_t imageSeed = 12345;
|
||||||
|
config.m_imageAsset = ImageAssetMockAssetHandler::CreateImageAsset(imageSize, imageSize, imageSeed);
|
||||||
|
config.m_tilingX = 1.0f;
|
||||||
|
config.m_tilingY = 1.0f;
|
||||||
|
CreateComponent<GradientSignal::ImageGradientComponent>(entity, config);
|
||||||
|
|
||||||
|
// Create the Gradient Transform Component with some default parameters.
|
||||||
|
GradientSignal::GradientTransformConfig gradientTransformConfig;
|
||||||
|
gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None;
|
||||||
|
CreateComponent<GradientSignal::GradientTransformComponent>(entity, gradientTransformConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GradientSignalBenchmarkFixture::CreateTestPerlinGradient(AZ::Entity* entity)
|
||||||
|
{
|
||||||
|
// Create the Perlin Gradient Component with some default sizes and parameters.
|
||||||
|
GradientSignal::PerlinGradientConfig config;
|
||||||
|
config.m_amplitude = 1.0f;
|
||||||
|
config.m_frequency = 1.1f;
|
||||||
|
config.m_octave = 4;
|
||||||
|
config.m_randomSeed = 12345;
|
||||||
|
CreateComponent<GradientSignal::PerlinGradientComponent>(entity, config);
|
||||||
|
|
||||||
|
// Create the Gradient Transform Component with some default parameters.
|
||||||
|
GradientSignal::GradientTransformConfig gradientTransformConfig;
|
||||||
|
gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None;
|
||||||
|
CreateComponent<GradientSignal::GradientTransformComponent>(entity, gradientTransformConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GradientSignalBenchmarkFixture::CreateTestRandomGradient(AZ::Entity* entity)
|
||||||
|
{
|
||||||
|
// Create the Random Gradient Component with some default parameters.
|
||||||
|
GradientSignal::RandomGradientConfig config;
|
||||||
|
config.m_randomSeed = 12345;
|
||||||
|
CreateComponent<GradientSignal::RandomGradientComponent>(entity, config);
|
||||||
|
|
||||||
|
// Create the Gradient Transform Component with some default parameters.
|
||||||
|
GradientSignal::GradientTransformConfig gradientTransformConfig;
|
||||||
|
gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None;
|
||||||
|
CreateComponent<GradientSignal::GradientTransformComponent>(entity, gradientTransformConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GradientSignalBenchmarkFixture::RunSamplerGetValueBenchmark(benchmark::State& state)
|
||||||
|
{
|
||||||
|
AZ_PROFILE_FUNCTION(Entity);
|
||||||
|
|
||||||
// All components are created, so activate the entity
|
// All components are created, so activate the entity
|
||||||
ActivateEntity(m_testEntity.get());
|
ActivateEntity(m_testEntity.get());
|
||||||
|
|
||||||
@@ -115,6 +168,107 @@ namespace UnitTest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GradientSignalBenchmarkFixture::RunSamplerGetValuesBenchmark(benchmark::State& state)
|
||||||
|
{
|
||||||
|
AZ_PROFILE_FUNCTION(Entity);
|
||||||
|
|
||||||
|
// All components are created, so activate the entity
|
||||||
|
ActivateEntity(m_testEntity.get());
|
||||||
|
|
||||||
|
// Create a gradient sampler and run through a series of points to see if they match expectations.
|
||||||
|
GradientSignal::GradientSampler gradientSampler;
|
||||||
|
gradientSampler.m_gradientId = m_testEntity->GetId();
|
||||||
|
|
||||||
|
// Get the height and width ranges for querying from our benchmark parameters
|
||||||
|
float height = aznumeric_cast<float>(state.range(0));
|
||||||
|
float width = aznumeric_cast<float>(state.range(1));
|
||||||
|
int64_t totalQueryPoints = state.range(0) * state.range(1);
|
||||||
|
|
||||||
|
// Call GetValues() for every height and width in our ranges.
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
// Set up our vector of query positions.
|
||||||
|
AZStd::vector<AZ::Vector3> positions(totalQueryPoints);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query and get the results.
|
||||||
|
AZStd::vector<float> results(totalQueryPoints);
|
||||||
|
gradientSampler.GetValues(positions, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GradientSignalBenchmarkFixture::RunEBusGetValueBenchmark(benchmark::State& state)
|
||||||
|
{
|
||||||
|
AZ_PROFILE_FUNCTION(Entity);
|
||||||
|
|
||||||
|
// All components are created, so activate the entity
|
||||||
|
ActivateEntity(m_testEntity.get());
|
||||||
|
|
||||||
|
GradientSignal::GradientSampleParams params;
|
||||||
|
|
||||||
|
// Get the height and width ranges for querying from our benchmark parameters
|
||||||
|
float height = aznumeric_cast<float>(state.range(0));
|
||||||
|
float width = aznumeric_cast<float>(state.range(1));
|
||||||
|
|
||||||
|
// Call GetValue() 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, m_testEntity->GetId(), &GradientSignal::GradientRequestBus::Events::GetValue, params);
|
||||||
|
benchmark::DoNotOptimize(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GradientSignalBenchmarkFixture::RunEBusGetValuesBenchmark(benchmark::State& state)
|
||||||
|
{
|
||||||
|
AZ_PROFILE_FUNCTION(Entity);
|
||||||
|
|
||||||
|
// All components are created, so activate the entity
|
||||||
|
ActivateEntity(m_testEntity.get());
|
||||||
|
|
||||||
|
GradientSignal::GradientSampler gradientSampler;
|
||||||
|
gradientSampler.m_gradientId = m_testEntity->GetId();
|
||||||
|
|
||||||
|
// Get the height and width ranges for querying from our benchmark parameters
|
||||||
|
float height = aznumeric_cast<float>(state.range(0));
|
||||||
|
float width = aznumeric_cast<float>(state.range(1));
|
||||||
|
int64_t totalQueryPoints = state.range(0) * state.range(1);
|
||||||
|
|
||||||
|
// Call GetValues() for every height and width in our ranges.
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
// Set up our vector of query positions.
|
||||||
|
AZStd::vector<AZ::Vector3> positions(totalQueryPoints);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query and get the results.
|
||||||
|
AZStd::vector<float> results(totalQueryPoints);
|
||||||
|
GradientSignal::GradientRequestBus::Event(
|
||||||
|
m_testEntity->GetId(), &GradientSignal::GradientRequestBus::Events::GetValues, positions, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,15 @@ namespace UnitTest
|
|||||||
void CreateTestEntity(float shapeHalfBounds);
|
void CreateTestEntity(float shapeHalfBounds);
|
||||||
void DestroyTestEntity();
|
void DestroyTestEntity();
|
||||||
|
|
||||||
void RunGetValueBenchmark(benchmark::State& state);
|
void CreateTestImageGradient(AZ::Entity* entity);
|
||||||
|
void CreateTestPerlinGradient(AZ::Entity* entity);
|
||||||
|
void CreateTestRandomGradient(AZ::Entity* entity);
|
||||||
|
|
||||||
|
void RunSamplerGetValueBenchmark(benchmark::State& state);
|
||||||
|
void RunSamplerGetValuesBenchmark(benchmark::State& state);
|
||||||
|
|
||||||
|
void RunEBusGetValueBenchmark(benchmark::State& state);
|
||||||
|
void RunEBusGetValuesBenchmark(benchmark::State& state);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void SetUp(const benchmark::State& state) override
|
void SetUp(const benchmark::State& state) override
|
||||||
|
|||||||
@@ -13,13 +13,14 @@ namespace UnitTest
|
|||||||
{
|
{
|
||||||
AZ::Data::Asset<GradientSignal::ImageAsset> ImageAssetMockAssetHandler::CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed)
|
AZ::Data::Asset<GradientSignal::ImageAsset> ImageAssetMockAssetHandler::CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed)
|
||||||
{
|
{
|
||||||
GradientSignal::ImageAsset* imageData =
|
auto imageAsset = AZ::Data::AssetManager::Instance().CreateAsset<GradientSignal::ImageAsset>(
|
||||||
aznew GradientSignal::ImageAsset(AZ::Data::AssetId(AZ::Uuid::CreateRandom()), AZ::Data::AssetData::AssetStatus::Ready);
|
AZ::Data::AssetId(AZ::Uuid::CreateRandom()), AZ::Data::AssetLoadBehavior::Default);
|
||||||
imageData->m_imageWidth = width;
|
|
||||||
imageData->m_imageHeight = height;
|
imageAsset->m_imageWidth = width;
|
||||||
imageData->m_bytesPerPixel = 1;
|
imageAsset->m_imageHeight = height;
|
||||||
imageData->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8;
|
imageAsset->m_bytesPerPixel = 1;
|
||||||
imageData->m_imageData.reserve(width * height);
|
imageAsset->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8;
|
||||||
|
imageAsset->m_imageData.reserve(width * height);
|
||||||
|
|
||||||
size_t value = 0;
|
size_t value = 0;
|
||||||
AZStd::hash_combine(value, seed);
|
AZStd::hash_combine(value, seed);
|
||||||
@@ -30,23 +31,24 @@ namespace UnitTest
|
|||||||
{
|
{
|
||||||
AZStd::hash_combine(value, x);
|
AZStd::hash_combine(value, x);
|
||||||
AZStd::hash_combine(value, y);
|
AZStd::hash_combine(value, y);
|
||||||
imageData->m_imageData.push_back(static_cast<AZ::u8>(value));
|
imageAsset->m_imageData.push_back(static_cast<AZ::u8>(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return AZ::Data::Asset<GradientSignal::ImageAsset>(imageData, AZ::Data::AssetLoadBehavior::Default);
|
return imageAsset;
|
||||||
}
|
}
|
||||||
|
|
||||||
AZ::Data::Asset<GradientSignal::ImageAsset> ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset(
|
AZ::Data::Asset<GradientSignal::ImageAsset> ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset(
|
||||||
AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY)
|
AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY)
|
||||||
{
|
{
|
||||||
GradientSignal::ImageAsset* imageData =
|
auto imageAsset = AZ::Data::AssetManager::Instance().CreateAsset<GradientSignal::ImageAsset>(
|
||||||
aznew GradientSignal::ImageAsset(AZ::Data::AssetId(AZ::Uuid::CreateRandom()), AZ::Data::AssetData::AssetStatus::Ready);
|
AZ::Data::AssetId(AZ::Uuid::CreateRandom()), AZ::Data::AssetLoadBehavior::Default);
|
||||||
imageData->m_imageWidth = width;
|
|
||||||
imageData->m_imageHeight = height;
|
imageAsset->m_imageWidth = width;
|
||||||
imageData->m_bytesPerPixel = 1;
|
imageAsset->m_imageHeight = height;
|
||||||
imageData->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8;
|
imageAsset->m_bytesPerPixel = 1;
|
||||||
imageData->m_imageData.reserve(width * height);
|
imageAsset->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8;
|
||||||
|
imageAsset->m_imageData.reserve(width * height);
|
||||||
|
|
||||||
const AZ::u8 pixelValue = 255;
|
const AZ::u8 pixelValue = 255;
|
||||||
|
|
||||||
@@ -57,16 +59,16 @@ namespace UnitTest
|
|||||||
{
|
{
|
||||||
if ((x == static_cast<int>(pixelX)) && (y == static_cast<int>(pixelY)))
|
if ((x == static_cast<int>(pixelX)) && (y == static_cast<int>(pixelY)))
|
||||||
{
|
{
|
||||||
imageData->m_imageData.push_back(pixelValue);
|
imageAsset->m_imageData.push_back(pixelValue);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
imageData->m_imageData.push_back(0);
|
imageAsset->m_imageData.push_back(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return AZ::Data::Asset<GradientSignal::ImageAsset>(imageData, AZ::Data::AssetLoadBehavior::Default);
|
return imageAsset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,10 +47,10 @@ namespace UnitTest
|
|||||||
static AZ::Data::Asset<GradientSignal::ImageAsset> CreateSpecificPixelImageAsset(
|
static AZ::Data::Asset<GradientSignal::ImageAsset> CreateSpecificPixelImageAsset(
|
||||||
AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY);
|
AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY);
|
||||||
|
|
||||||
AZ::Data::AssetPtr CreateAsset(
|
AZ::Data::AssetPtr CreateAsset(const AZ::Data::AssetId& id, [[maybe_unused]] const AZ::Data::AssetType& type) override
|
||||||
[[maybe_unused]] const AZ::Data::AssetId& id, [[maybe_unused]] const AZ::Data::AssetType& type) override
|
|
||||||
{
|
{
|
||||||
return AZ::Data::AssetPtr();
|
// For our mock handler, always mark our assets as immediately ready.
|
||||||
|
return aznew GradientSignal::ImageAsset(id, AZ::Data::AssetData::AssetStatus::Ready);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DestroyAsset(AZ::Data::AssetPtr ptr) override
|
void DestroyAsset(AZ::Data::AssetPtr ptr) override
|
||||||
|
|||||||
@@ -7,6 +7,5 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
set(FILES
|
set(FILES
|
||||||
Tests/GradientSignalTestFixtures.cpp
|
|
||||||
Tests/EditorGradientSignalPreviewTests.cpp
|
Tests/EditorGradientSignalPreviewTests.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#
|
||||||
|
# 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/GradientSignalTestFixtures.cpp
|
||||||
|
Tests/GradientSignalTestFixtures.h
|
||||||
|
Tests/GradientSignalTestMocks.cpp
|
||||||
|
Tests/GradientSignalTestMocks.h
|
||||||
|
)
|
||||||
@@ -13,10 +13,6 @@ set(FILES
|
|||||||
Tests/GradientSignalServicesTests.cpp
|
Tests/GradientSignalServicesTests.cpp
|
||||||
Tests/GradientSignalSurfaceTests.cpp
|
Tests/GradientSignalSurfaceTests.cpp
|
||||||
Tests/GradientSignalTransformTests.cpp
|
Tests/GradientSignalTransformTests.cpp
|
||||||
Tests/GradientSignalTestFixtures.cpp
|
|
||||||
Tests/GradientSignalTestFixtures.h
|
|
||||||
Tests/GradientSignalTestMocks.cpp
|
|
||||||
Tests/GradientSignalTestMocks.h
|
|
||||||
Tests/GradientSignalTest.cpp
|
Tests/GradientSignalTest.cpp
|
||||||
Tests/ImageAssetTests.cpp
|
Tests/ImageAssetTests.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user