Add benchmarks to GradientSignal. (#6616)

* Add benchmarks to GradientSignal.
Cleaned up and rearranged a bit of the unit testing code to make it reusable from a benchmark suite as well.  Added set of benchmarks for measuring GetValue(), so that we can compare against GetValues() when it gets added.

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

* Fixed Editor unit tests.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>
This commit is contained in:
Mike Balfour
2022-01-03 16:07:31 -06:00
committed by GitHub
parent 5913a64e92
commit a000198b1b
16 changed files with 487 additions and 186 deletions
+6
View File
@@ -145,6 +145,12 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
NAME Gem::GradientSignal.Tests
)
ly_add_googlebenchmark(
NAME Gem::GradientSignal.Benchmarks
TARGET Gem::GradientSignal.Tests
)
if(PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME GradientSignal.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
@@ -35,6 +35,13 @@ namespace GradientSignal
static bool VersionConverter(AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& classElement);
ImageAsset() = default;
ImageAsset(const AZ::Data::AssetId& assetId, AZ::Data::AssetData::AssetStatus status)
: AssetData(assetId, status)
{
}
AZ::u32 m_imageWidth = 0;
AZ::u32 m_imageHeight = 0;
AZ::u8 m_bytesPerPixel = 0;
@@ -6,7 +6,7 @@
*
*/
#include "Tests/GradientSignalTestMocks.h"
#include <Tests/GradientSignalTestFixtures.h>
#include <GradientSignal/Editor/EditorGradientPreviewRenderer.h>
#include <AzTest/AzTest.h>
@@ -28,7 +28,6 @@ namespace UnitTest
void SetUp() override
{
GradientSignalTest::SetUp();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create();
// Set up job manager with two threads so that we can run and test the preview job logic.
AZ::JobManagerDesc desc;
@@ -46,7 +45,6 @@ namespace UnitTest
delete m_jobContext;
delete m_jobManager;
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Destroy();
GradientSignalTest::TearDown();
}
@@ -0,0 +1,106 @@
/*
* 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 <Tests/GradientSignalTestFixtures.h>
#include <AzTest/AzTest.h>
#include <AzCore/Memory/PoolAllocator.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <Source/Components/ImageGradientComponent.h>
#include <Source/Components/PerlinGradientComponent.h>
#include <Source/Components/RandomGradientComponent.h>
#include <Source/Components/GradientTransformComponent.h>
namespace UnitTest
{
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_ImageGradientGetValue)(benchmark::State& state)
{
// 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>(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)
->Args({ 1024, 1024 })
->Args({ 2048, 2048 })
->Args({ 4096, 4096 })
->Unit(::benchmark::kMillisecond);
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_PerlinGradientGetValue)(benchmark::State& state)
{
// 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>(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)
->Args({ 1024, 1024 })
->Args({ 2048, 2048 })
->Args({ 4096, 4096 })
->Unit(::benchmark::kMillisecond);
BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_RandomGradientGetValue)(benchmark::State& state)
{
// Create the Random Gradient Component with some default parameters.
GradientSignal::RandomGradientConfig config;
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)
->Args({ 1024, 1024 })
->Args({ 2048, 2048 })
->Args({ 4096, 4096 })
->Unit(::benchmark::kMillisecond);
#endif
}
@@ -7,7 +7,7 @@
*/
#include "Tests/GradientSignalTestMocks.h"
#include <Tests/GradientSignalTestFixtures.h>
#include <AzTest/AzTest.h>
#include <AzCore/Asset/AssetManager.h>
@@ -23,69 +23,6 @@ namespace UnitTest
struct GradientSignalImageTestsFixture
: public GradientSignalTest
{
struct MockAssetHandler
: public AZ::Data::AssetHandler
{
AZ::Data::AssetPtr CreateAsset([[maybe_unused]] const AZ::Data::AssetId& id, [[maybe_unused]] const AZ::Data::AssetType& type) override
{
return AZ::Data::AssetPtr();
}
void DestroyAsset(AZ::Data::AssetPtr ptr) override
{
if (ptr)
{
delete ptr;
}
}
void GetHandledAssetTypes([[maybe_unused]] AZStd::vector<AZ::Data::AssetType>& assetTypes) override
{
}
AZ::Data::AssetHandler::LoadResult LoadAssetData(
[[maybe_unused]] const AZ::Data::Asset<AZ::Data::AssetData>& asset,
[[maybe_unused]] AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
[[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) override
{
return AZ::Data::AssetHandler::LoadResult::LoadComplete;
}
};
MockAssetHandler* m_mockHandler = nullptr;
GradientSignal::ImageAsset* m_imageData = nullptr;
void SetUp() override
{
GradientSignalTest::SetUp();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create();
AZ::Data::AssetManager::Descriptor desc;
AZ::Data::AssetManager::Create(desc);
m_mockHandler = new MockAssetHandler();
AZ::Data::AssetManager::Instance().RegisterHandler(m_mockHandler, azrtti_typeid<GradientSignal::ImageAsset>());
}
void TearDown() override
{
AZ::Data::AssetManager::Instance().UnregisterHandler(m_mockHandler);
delete m_mockHandler; // delete after removing from the asset manager
AzFramework::LegacyAssetEventBus::ClearQueuedEvents();
AZ::Data::AssetManager::Destroy();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Destroy();
GradientSignalTest::TearDown();
}
struct AssignIdToAsset
: public AZ::Data::AssetData
{
void MakeReady()
{
m_assetId = AZ::Data::AssetId(AZ::Uuid::CreateRandom());
m_status.store(AZ::Data::AssetData::AssetStatus::Ready);
}
};
struct PixelTestSetup
{
// How to create the source image
@@ -105,61 +42,6 @@ namespace UnitTest
static const AZ::Vector2 EndOfList;
};
AZ::Data::Asset<GradientSignal::ImageAsset> CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed)
{
m_imageData = aznew GradientSignal::ImageAsset();
m_imageData->m_imageWidth = width;
m_imageData->m_imageHeight = height;
m_imageData->m_bytesPerPixel = 1;
m_imageData->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8;
size_t value = 0;
AZStd::hash_combine(value, seed);
for (AZ::u32 x = 0; x < width; ++x)
{
for (AZ::u32 y = 0; y < height; ++y)
{
AZStd::hash_combine(value, x);
AZStd::hash_combine(value, y);
m_imageData->m_imageData.push_back(static_cast<AZ::u8>(value));
}
}
reinterpret_cast<AssignIdToAsset*>(m_imageData)->MakeReady();
return AZ::Data::Asset<GradientSignal::ImageAsset>(m_imageData, AZ::Data::AssetLoadBehavior::Default);
}
AZ::Data::Asset<GradientSignal::ImageAsset> CreateSpecificPixelImageAsset(AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY)
{
m_imageData = aznew GradientSignal::ImageAsset();
m_imageData->m_imageWidth = width;
m_imageData->m_imageHeight = height;
m_imageData->m_bytesPerPixel = 1;
m_imageData->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8;
const AZ::u8 pixelValue = 255;
// Image data should be stored inverted on the y axis relative to our engine, so loop backwards through y.
for (int y = static_cast<int>(height) - 1; y >= 0; --y)
{
for (AZ::u32 x = 0; x < width; ++x)
{
if ((x == static_cast<int>(pixelX)) && (y == static_cast<int>(pixelY)))
{
m_imageData->m_imageData.push_back(pixelValue);
}
else
{
m_imageData->m_imageData.push_back(0);
}
}
}
reinterpret_cast<AssignIdToAsset*>(m_imageData)->MakeReady();
return AZ::Data::Asset<GradientSignal::ImageAsset>(m_imageData, AZ::Data::AssetLoadBehavior::Default);
}
void TestPixels(GradientSignal::GradientSampler& sampler, AZ::u32 width, AZ::u32 height, float stepSize, const AZStd::vector<AZ::Vector3>& expectedPoints)
{
AZStd::vector<AZ::Vector3> foundPoints;
@@ -203,7 +85,8 @@ namespace UnitTest
// Create the Image Gradient Component.
GradientSignal::ImageGradientConfig config;
config.m_imageAsset = CreateSpecificPixelImageAsset(test.m_imageSize, test.m_imageSize, static_cast<AZ::u32>(test.m_pixel.GetX()), static_cast<AZ::u32>(test.m_pixel.GetY()));
config.m_imageAsset = ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset(
test.m_imageSize, test.m_imageSize, static_cast<AZ::u32>(test.m_pixel.GetX()), static_cast<AZ::u32>(test.m_pixel.GetY()));
config.m_tilingX = test.m_tiling;
config.m_tilingY = test.m_tiling;
CreateComponent<GradientSignal::ImageGradientComponent>(entity.get(), config);
@@ -495,7 +378,7 @@ namespace UnitTest
// Create an ImageGradient with a 3x3 asset with the center pixel set.
GradientSignal::ImageGradientConfig gradientConfig;
gradientConfig.m_imageAsset = CreateSpecificPixelImageAsset(3, 3, 1, 1);
gradientConfig.m_imageAsset = ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset(3, 3, 1, 1);
CreateComponent<GradientSignal::ImageGradientComponent>(entity.get(), gradientConfig);
// Create the test GradientTransform
@@ -10,7 +10,7 @@
#include <AzTest/AzTest.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/Math/MathUtils.h>
#include "Tests/GradientSignalTestMocks.h"
#include <Tests/GradientSignalTestFixtures.h>
#include <Source/Components/MixedGradientComponent.h>
#include <Source/Components/ReferenceGradientComponent.h>
@@ -6,7 +6,7 @@
*
*/
#include "Tests/GradientSignalTestMocks.h"
#include <Tests/GradientSignalTestFixtures.h>
#include <AzTest/AzTest.h>
@@ -9,7 +9,7 @@
#include <AzTest/AzTest.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/Math/MathUtils.h>
#include "Tests/GradientSignalTestMocks.h"
#include <Tests/GradientSignalTestFixtures.h>
#include <Source/Components/ConstantGradientComponent.h>
#include <Source/Components/GradientSurfaceDataComponent.h>
@@ -7,7 +7,7 @@
*/
#include "Tests/GradientSignalTestMocks.h"
#include <Tests/GradientSignalTestFixtures.h>
#include <GradientSignal/PerlinImprovedNoise.h>
#include <GradientSignal/Ebuses/GradientRequestBus.h>
@@ -0,0 +1,121 @@
/*
* 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/GradientSignalTestFixtures.h>
namespace UnitTest
{
void GradientSignalBaseFixture::SetupCoreSystems()
{
m_app = AZStd::make_unique<AZ::ComponentApplication>();
ASSERT_TRUE(m_app != nullptr);
AZ::ComponentApplication::Descriptor componentAppDesc;
m_systemEntity = m_app->Create(componentAppDesc);
ASSERT_TRUE(m_systemEntity != nullptr);
m_app->AddEntity(m_systemEntity);
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create();
AZ::Data::AssetManager::Descriptor desc;
AZ::Data::AssetManager::Create(desc);
m_mockHandler = new ImageAssetMockAssetHandler();
AZ::Data::AssetManager::Instance().RegisterHandler(m_mockHandler, azrtti_typeid<GradientSignal::ImageAsset>());
}
void GradientSignalBaseFixture::TearDownCoreSystems()
{
AZ::Data::AssetManager::Instance().UnregisterHandler(m_mockHandler);
delete m_mockHandler; // delete after removing from the asset manager
AzFramework::LegacyAssetEventBus::ClearQueuedEvents();
AZ::Data::AssetManager::Destroy();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Destroy();
m_app->Destroy();
m_app.reset();
m_systemEntity = nullptr;
}
void GradientSignalTest::TestFixedDataSampler(const AZStd::vector<float>& expectedOutput, int size, AZ::EntityId gradientEntityId)
{
GradientSignal::GradientSampler gradientSampler;
gradientSampler.m_gradientId = gradientEntityId;
for (int y = 0; y < size; ++y)
{
for (int x = 0; x < size; ++x)
{
GradientSignal::GradientSampleParams params;
params.m_position = AZ::Vector3(static_cast<float>(x), static_cast<float>(y), 0.0f);
const int index = y * size + x;
float actualValue = gradientSampler.GetValue(params);
float expectedValue = expectedOutput[index];
EXPECT_NEAR(actualValue, expectedValue, 0.01f);
}
}
}
#ifdef HAVE_BENCHMARK
void GradientSignalBenchmarkFixture::CreateTestEntity(float shapeHalfBounds)
{
// Create the base entity
m_testEntity = CreateEntity();
// Create a mock Shape component that describes the bounds that we're using to map our gradient into world space.
CreateComponent<MockShapeComponent>(m_testEntity.get());
MockShapeComponentHandler mockShapeHandler(m_testEntity->GetId());
mockShapeHandler.m_GetLocalBounds = AZ::Aabb::CreateCenterRadius(AZ::Vector3(shapeHalfBounds), shapeHalfBounds);
// Create a mock Transform component that locates our gradient in the center of our desired mock Shape.
MockTransformHandler mockTransformHandler;
mockTransformHandler.m_GetLocalTMOutput = AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds));
mockTransformHandler.m_GetWorldTMOutput = AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds));
mockTransformHandler.BusConnect(m_testEntity->GetId());
}
void GradientSignalBenchmarkFixture::DestroyTestEntity()
{
m_testEntity.reset();
}
void GradientSignalBenchmarkFixture::RunGetValueBenchmark(benchmark::State& state)
{
// 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));
// 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)
{
GradientSignal::GradientSampleParams params;
params.m_position = AZ::Vector3(x, y, 0.0f);
float value = gradientSampler.GetValue(params);
benchmark::DoNotOptimize(value);
}
}
}
}
#endif
}
@@ -0,0 +1,124 @@
/*
* 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 <Tests/GradientSignalTestMocks.h>
namespace UnitTest
{
// Base test fixture used for GradientSignal unit tests and benchmark tests
class GradientSignalBaseFixture
{
public:
void SetupCoreSystems();
void TearDownCoreSystems();
AZStd::unique_ptr<AZ::Entity> CreateEntity()
{
return AZStd::make_unique<AZ::Entity>();
}
void ActivateEntity(AZ::Entity* entity)
{
entity->Init();
entity->Activate();
}
template<typename Component, typename Configuration>
AZ::Component* CreateComponent(AZ::Entity* entity, const Configuration& config)
{
m_app->RegisterComponentDescriptor(Component::CreateDescriptor());
return entity->CreateComponent<Component>(config);
}
template<typename Component>
AZ::Component* CreateComponent(AZ::Entity* entity)
{
m_app->RegisterComponentDescriptor(Component::CreateDescriptor());
return entity->CreateComponent<Component>();
}
AZStd::unique_ptr<AZ::ComponentApplication> m_app;
AZ::Entity* m_systemEntity = nullptr;
ImageAssetMockAssetHandler* m_mockHandler = nullptr;
};
struct GradientSignalTest
: public GradientSignalBaseFixture
, public UnitTest::AllocatorsTestFixture
{
protected:
void SetUp() override
{
UnitTest::AllocatorsTestFixture::SetUp();
SetupCoreSystems();
}
void TearDown() override
{
TearDownCoreSystems();
UnitTest::AllocatorsTestFixture::TearDown();
}
void TestFixedDataSampler(const AZStd::vector<float>& expectedOutput, int size, AZ::EntityId gradientEntityId);
};
#ifdef HAVE_BENCHMARK
class GradientSignalBenchmarkFixture
: public GradientSignalBaseFixture
, public UnitTest::AllocatorsBenchmarkFixture
, public UnitTest::TraceBusRedirector
{
public:
void internalSetUp(const benchmark::State& state)
{
AZ::Debug::TraceMessageBus::Handler::BusConnect();
UnitTest::AllocatorsBenchmarkFixture::SetUp(state);
SetupCoreSystems();
// Create a default test entity with bounds of 256 m x 256 m x 256 m.
const float shapeHalfBounds = 128.0f;
CreateTestEntity(shapeHalfBounds);
}
void internalTearDown(const benchmark::State& state)
{
DestroyTestEntity();
TearDownCoreSystems();
UnitTest::AllocatorsBenchmarkFixture::TearDown(state);
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
}
void CreateTestEntity(float shapeHalfBounds);
void DestroyTestEntity();
void RunGetValueBenchmark(benchmark::State& state);
protected:
void SetUp(const benchmark::State& state) override
{
internalSetUp(state);
}
void SetUp(benchmark::State& state) override
{
internalSetUp(state);
}
void TearDown(const benchmark::State& state) override
{
internalTearDown(state);
}
void TearDown(benchmark::State& state) override
{
internalTearDown(state);
}
AZStd::unique_ptr<AZ::Entity> m_testEntity;
};
#endif
}
@@ -0,0 +1,72 @@
/*
* 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/GradientSignalTestMocks.h>
namespace UnitTest
{
AZ::Data::Asset<GradientSignal::ImageAsset> ImageAssetMockAssetHandler::CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed)
{
GradientSignal::ImageAsset* imageData =
aznew GradientSignal::ImageAsset(AZ::Data::AssetId(AZ::Uuid::CreateRandom()), AZ::Data::AssetData::AssetStatus::Ready);
imageData->m_imageWidth = width;
imageData->m_imageHeight = height;
imageData->m_bytesPerPixel = 1;
imageData->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8;
imageData->m_imageData.reserve(width * height);
size_t value = 0;
AZStd::hash_combine(value, seed);
for (AZ::u32 x = 0; x < width; ++x)
{
for (AZ::u32 y = 0; y < height; ++y)
{
AZStd::hash_combine(value, x);
AZStd::hash_combine(value, y);
imageData->m_imageData.push_back(static_cast<AZ::u8>(value));
}
}
return AZ::Data::Asset<GradientSignal::ImageAsset>(imageData, AZ::Data::AssetLoadBehavior::Default);
}
AZ::Data::Asset<GradientSignal::ImageAsset> ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset(
AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY)
{
GradientSignal::ImageAsset* imageData =
aznew GradientSignal::ImageAsset(AZ::Data::AssetId(AZ::Uuid::CreateRandom()), AZ::Data::AssetData::AssetStatus::Ready);
imageData->m_imageWidth = width;
imageData->m_imageHeight = height;
imageData->m_bytesPerPixel = 1;
imageData->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8;
imageData->m_imageData.reserve(width * height);
const AZ::u8 pixelValue = 255;
// Image data should be stored inverted on the y axis relative to our engine, so loop backwards through y.
for (int y = static_cast<int>(height) - 1; y >= 0; --y)
{
for (AZ::u32 x = 0; x < width; ++x)
{
if ((x == static_cast<int>(pixelX)) && (y == static_cast<int>(pixelY)))
{
imageData->m_imageData.push_back(pixelValue);
}
else
{
imageData->m_imageData.push_back(0);
}
}
}
return AZ::Data::Asset<GradientSignal::ImageAsset>(imageData, AZ::Data::AssetLoadBehavior::Default);
}
}
@@ -8,90 +8,69 @@
#pragma once
#include <AzTest/AzTest.h>
#include <AzCore/std/hash.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Casting/lossy_cast.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Memory/PoolAllocator.h>
#include <AzCore/std/hash.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <GradientSignal/Ebuses/GradientRequestBus.h>
#include <GradientSignal/Ebuses/GradientPreviewContextRequestBus.h>
#include <GradientSignal/GradientSampler.h>
#include <GradientSignal/ImageAsset.h>
#include <LmbrCentral/Shape/ShapeComponentBus.h>
#include <SurfaceData/SurfaceDataSystemRequestBus.h>
#include <AzCore/Casting/lossy_cast.h>
#include <SurfaceData/Tests/SurfaceDataTestMocks.h>
namespace UnitTest
{
struct GradientSignalTest
: public ::testing::Test
// Mock asset handler for GradientSignal::ImageAsset that we can use in unit tests to pretend to load an image asset with.
// Also includes utility functions for creating image assets with specific testable patterns.
struct ImageAssetMockAssetHandler : public AZ::Data::AssetHandler
{
protected:
AZ::ComponentApplication m_app;
AZ::Entity* m_systemEntity = nullptr;
//! Creates a deterministically random set of pixel data as an ImageAsset.
//! \param width The width of the ImageAsset
//! \param height The height of the ImageAsset
//! \param seed The random seed to use for generating the random data
//! \return The ImageAsset in a loaded ready state
static AZ::Data::Asset<GradientSignal::ImageAsset> CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed);
void SetUp() override
//! Creates an ImageAsset where all the pixels are 0 except for the one pixel at the given coordinates, which is set to 1.
//! \param width The width of the ImageAsset
//! \param height The height of the ImageAsset
//! \param pixelX The X coordinate of the pixel to set to 1
//! \param pixelY The Y coordinate of the pixel to set to 1
//! \return The ImageAsset in a loaded ready state
static AZ::Data::Asset<GradientSignal::ImageAsset> CreateSpecificPixelImageAsset(
AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY);
AZ::Data::AssetPtr CreateAsset(
[[maybe_unused]] const AZ::Data::AssetId& id, [[maybe_unused]] const AZ::Data::AssetType& type) override
{
AZ::ComponentApplication::Descriptor appDesc;
appDesc.m_memoryBlocksByteSize = 128 * 1024 * 1024;
m_systemEntity = m_app.Create(appDesc);
m_app.AddEntity(m_systemEntity);
return AZ::Data::AssetPtr();
}
void TearDown() override
void DestroyAsset(AZ::Data::AssetPtr ptr) override
{
m_app.Destroy();
m_systemEntity = nullptr;
}
void TestFixedDataSampler(const AZStd::vector<float>& expectedOutput, int size, AZ::EntityId gradientEntityId)
{
GradientSignal::GradientSampler gradientSampler;
gradientSampler.m_gradientId = gradientEntityId;
for(int y = 0; y < size; ++y)
if (ptr)
{
for (int x = 0; x < size; ++x)
{
GradientSignal::GradientSampleParams params;
params.m_position = AZ::Vector3(static_cast<float>(x), static_cast<float>(y), 0.0f);
const int index = y * size + x;
float actualValue = gradientSampler.GetValue(params);
float expectedValue = expectedOutput[index];
EXPECT_NEAR(actualValue, expectedValue, 0.01f);
}
delete ptr;
}
}
AZStd::unique_ptr<AZ::Entity> CreateEntity()
void GetHandledAssetTypes([[maybe_unused]] AZStd::vector<AZ::Data::AssetType>& assetTypes) override
{
return AZStd::make_unique<AZ::Entity>();
}
void ActivateEntity(AZ::Entity* entity)
AZ::Data::AssetHandler::LoadResult LoadAssetData(
[[maybe_unused]] const AZ::Data::Asset<AZ::Data::AssetData>& asset,
[[maybe_unused]] AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
[[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) override
{
entity->Init();
EXPECT_EQ(AZ::Entity::State::Init, entity->GetState());
entity->Activate();
EXPECT_EQ(AZ::Entity::State::Active, entity->GetState());
}
template <typename Component, typename Configuration>
AZ::Component* CreateComponent(AZ::Entity* entity, const Configuration& config)
{
m_app.RegisterComponentDescriptor(Component::CreateDescriptor());
return entity->CreateComponent<Component>(config);
}
template <typename Component>
AZ::Component* CreateComponent(AZ::Entity* entity)
{
m_app.RegisterComponentDescriptor(Component::CreateDescriptor());
return entity->CreateComponent<Component>();
return AZ::Data::AssetHandler::LoadResult::LoadComplete;
}
};
@@ -7,7 +7,7 @@
*/
#include "Tests/GradientSignalTestMocks.h"
#include <Tests/GradientSignalTestFixtures.h>
#include <AzTest/AzTest.h>
#include <AzCore/Asset/AssetManager.h>
@@ -7,5 +7,6 @@
#
set(FILES
Tests/GradientSignalTestFixtures.cpp
Tests/EditorGradientSignalPreviewTests.cpp
)
@@ -7,11 +7,15 @@
#
set(FILES
Tests/GradientSignalBenchmarks.cpp
Tests/GradientSignalImageTests.cpp
Tests/GradientSignalReferencesTests.cpp
Tests/GradientSignalServicesTests.cpp
Tests/GradientSignalSurfaceTests.cpp
Tests/GradientSignalTransformTests.cpp
Tests/GradientSignalTestFixtures.cpp
Tests/GradientSignalTestFixtures.h
Tests/GradientSignalTestMocks.cpp
Tests/GradientSignalTestMocks.h
Tests/GradientSignalTest.cpp
Tests/ImageAssetTests.cpp