diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalImageTests.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalImageTests.cpp index 8ec190000d..c6656d589e 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalImageTests.cpp +++ b/Gems/GradientSignal/Code/Tests/GradientSignalImageTests.cpp @@ -8,6 +8,7 @@ #include +#include #include #include @@ -88,7 +89,7 @@ namespace UnitTest // Create the Image Gradient Component. GradientSignal::ImageGradientConfig config; - config.m_imageAsset = ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset( + config.m_imageAsset = UnitTest::CreateSpecificPixelImageAsset( test.m_imageSize, test.m_imageSize, static_cast(test.m_pixel.GetX()), static_cast(test.m_pixel.GetY())); config.m_tilingX = test.m_tiling; config.m_tilingY = test.m_tiling; @@ -379,7 +380,7 @@ namespace UnitTest // Create an ImageGradient with a 3x3 asset with the center pixel set. GradientSignal::ImageGradientConfig gradientConfig; - gradientConfig.m_imageAsset = ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset(3, 3, 1, 1); + gradientConfig.m_imageAsset = UnitTest::CreateSpecificPixelImageAsset(3, 3, 1, 1); entity->CreateComponent(gradientConfig); // Create the test GradientTransform diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp index 402438ee88..d07d2aae50 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp +++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp @@ -8,7 +8,10 @@ #include +#include +#include +#include #include #include #include @@ -70,14 +73,16 @@ namespace UnitTest void GradientSignalBaseFixture::SetupCoreSystems() { - m_mockHandler = new UnitTest::ImageAssetMockAssetHandler(); - AZ::Data::AssetManager::Instance().RegisterHandler(m_mockHandler, azrtti_typeid()); + // Using the AZ::RPI::MakeAssetHandler will both create the asset handlers, + // and register them with the AssetManager + m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler()); + m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler()); } void GradientSignalBaseFixture::TearDownCoreSystems() { - AZ::Data::AssetManager::Instance().UnregisterHandler(m_mockHandler); - delete m_mockHandler; // delete after removing from the asset manager + // This will delete the asset handlers, which will unregister themselves on deletion + m_assetHandlers.clear(); AzFramework::LegacyAssetEventBus::ClearQueuedEvents(); } @@ -147,7 +152,7 @@ namespace UnitTest GradientSignal::ImageGradientConfig config; const uint32_t imageSize = 4096; const int32_t imageSeed = 12345; - config.m_imageAsset = ImageAssetMockAssetHandler::CreateImageAsset(imageSize, imageSize, imageSeed); + config.m_imageAsset = UnitTest::CreateImageAsset(imageSize, imageSize, imageSeed); config.m_tilingX = 1.0f; config.m_tilingY = 1.0f; entity->CreateComponent(config); diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h index 5fda88ea27..1c703964e7 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h +++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h @@ -9,6 +9,7 @@ #include #include +#include #include namespace UnitTest @@ -89,7 +90,7 @@ namespace UnitTest AZStd::unique_ptr BuildTestSurfaceMaskGradient(float shapeHalfBounds); AZStd::unique_ptr BuildTestSurfaceSlopeGradient(float shapeHalfBounds); - UnitTest::ImageAssetMockAssetHandler* m_mockHandler = nullptr; + AZ::RPI::AssetHandlerPtrList m_assetHandlers; }; struct GradientSignalTest diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestHelpers.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalTestHelpers.cpp index 46cc3475e8..560aa09f48 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTestHelpers.cpp +++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestHelpers.cpp @@ -8,11 +8,189 @@ #include +#include +#include #include #include namespace UnitTest { + AZ::RHI::ImageSubresourceLayout BuildSubImageLayout(AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize) + { + AZ::RHI::ImageSubresourceLayout layout; + layout.m_size = AZ::RHI::Size{ width, height, 1 }; + layout.m_rowCount = width; + layout.m_bytesPerRow = width * pixelSize; + layout.m_bytesPerImage = width * height * pixelSize; + return layout; + } + + AZStd::vector BuildBasicImageData(AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::s32 seed) + { + const size_t imageSize = width * height * pixelSize; + + AZStd::vector image; + image.reserve(imageSize); + + 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); + image.push_back(static_cast(value)); + } + } + + EXPECT_EQ(image.size(), imageSize); + return image; + } + + AZ::Data::Asset BuildBasicMipChainAsset(AZ::u16 mipLevels, AZ::u16 arraySize, AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::s32 seed) + { + using namespace AZ; + + RPI::ImageMipChainAssetCreator assetCreator; + + assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), mipLevels, arraySize); + + RHI::ImageSubresourceLayout layout = BuildSubImageLayout(width, height, pixelSize); + + assetCreator.BeginMip(layout); + + for (AZ::u32 arrayIndex = 0; arrayIndex < arraySize; ++arrayIndex) + { + AZStd::vector data = BuildBasicImageData(width, height, pixelSize, seed); + assetCreator.AddSubImage(data.data(), data.size()); + } + + assetCreator.EndMip(); + + Data::Asset asset; + EXPECT_TRUE(assetCreator.End(asset)); + EXPECT_TRUE(asset.IsReady()); + EXPECT_NE(asset.Get(), nullptr); + + return asset; + } + + AZStd::vector BuildSpecificPixelImageData(AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::u32 pixelX, AZ::u32 pixelY) + { + const size_t imageSize = width * height * pixelSize; + + AZStd::vector image; + image.reserve(imageSize); + + 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(height) - 1; y >= 0; --y) + { + for (AZ::u32 x = 0; x < width; ++x) + { + if ((x == static_cast(pixelX)) && (y == static_cast(pixelY))) + { + image.push_back(pixelValue); + } + else + { + image.push_back(0); + } + } + } + + EXPECT_EQ(image.size(), imageSize); + return image; + } + + AZ::Data::Asset BuildSpecificPixelMipChainAsset(AZ::u16 mipLevels, AZ::u16 arraySize, AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::u32 pixelX, AZ::u32 pixelY) + { + using namespace AZ; + + RPI::ImageMipChainAssetCreator assetCreator; + + assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), mipLevels, arraySize); + + RHI::ImageSubresourceLayout layout = BuildSubImageLayout(width, height, pixelSize); + + assetCreator.BeginMip(layout); + + for (AZ::u32 arrayIndex = 0; arrayIndex < arraySize; ++arrayIndex) + { + AZStd::vector data = BuildSpecificPixelImageData(width, height, pixelSize, pixelX, pixelY); + assetCreator.AddSubImage(data.data(), data.size()); + } + + assetCreator.EndMip(); + + Data::Asset asset; + EXPECT_TRUE(assetCreator.End(asset)); + EXPECT_TRUE(asset.IsReady()); + EXPECT_NE(asset.Get(), nullptr); + + return asset; + } + + AZ::Data::Asset CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed) + { + auto randomAssetId = AZ::Data::AssetId(AZ::Uuid::CreateRandom()); + auto imageAsset = AZ::Data::AssetManager::Instance().CreateAsset( + randomAssetId, AZ::Data::AssetLoadBehavior::Default); + + const AZ::u32 arraySize = 1; + const AZ::u32 mipCountTotal = 1; + const auto format = AZ::RHI::Format::R8_UNORM; + const AZ::u32 pixelSize = AZ::RHI::GetFormatComponentCount(format); + + AZ::Data::Asset mipChain = BuildBasicMipChainAsset(mipCountTotal, arraySize, width, height, pixelSize, seed); + + AZ::RPI::StreamingImageAssetCreator assetCreator; + assetCreator.Begin(randomAssetId); + + AZ::RHI::ImageDescriptor imageDesc = AZ::RHI::ImageDescriptor::Create2DArray(AZ::RHI::ImageBindFlags::ShaderRead, width, height, arraySize, format); + imageDesc.m_mipLevels = static_cast(mipCountTotal); + + assetCreator.SetImageDescriptor(imageDesc); + assetCreator.AddMipChainAsset(*mipChain.Get()); + + EXPECT_TRUE(assetCreator.End(imageAsset)); + EXPECT_TRUE(imageAsset.IsReady()); + EXPECT_NE(imageAsset.Get(), nullptr); + + return imageAsset; + } + + AZ::Data::Asset CreateSpecificPixelImageAsset(AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY) + { + auto randomAssetId = AZ::Data::AssetId(AZ::Uuid::CreateRandom()); + auto imageAsset = AZ::Data::AssetManager::Instance().CreateAsset( + randomAssetId, AZ::Data::AssetLoadBehavior::Default); + + const AZ::u32 arraySize = 1; + const AZ::u32 mipCountTotal = 1; + const auto format = AZ::RHI::Format::R8_UNORM; + const AZ::u32 pixelSize = AZ::RHI::GetFormatComponentCount(format); + + AZ::Data::Asset mipChain = BuildSpecificPixelMipChainAsset(mipCountTotal, arraySize, width, height, pixelSize, pixelX, pixelY); + + AZ::RPI::StreamingImageAssetCreator assetCreator; + assetCreator.Begin(randomAssetId); + + AZ::RHI::ImageDescriptor imageDesc = AZ::RHI::ImageDescriptor::Create2DArray(AZ::RHI::ImageBindFlags::ShaderRead, width, height, arraySize, format); + imageDesc.m_mipLevels = static_cast(mipCountTotal); + + assetCreator.SetImageDescriptor(imageDesc); + assetCreator.AddMipChainAsset(*mipChain.Get()); + + EXPECT_TRUE(assetCreator.End(imageAsset)); + EXPECT_TRUE(imageAsset.IsReady()); + EXPECT_NE(imageAsset.Get(), nullptr); + return imageAsset; + } + 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. diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestHelpers.h b/Gems/GradientSignal/Code/Tests/GradientSignalTestHelpers.h index 8a175939ee..5a13167975 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTestHelpers.h +++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestHelpers.h @@ -12,8 +12,72 @@ #include #include +#include +#include +#include + namespace UnitTest { + //! Helper method to build a AZ::RHI::ImageSubresourceLayout + //! @param width The width of the image + //! @param height The height of the image + //! @param pixelSize Number of bytes per pixel + //! @return The AZ::RHI::ImageSubresourceLayout that has been filled out appropriately + AZ::RHI::ImageSubresourceLayout BuildSubImageLayout(AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize); + + //! Build a deterministic random set of image pixel data + //! @param width Width of the image + //! @param height Height of the image + //! @param pixelSize Number of bytes per pixel + //! @param seed The random seed for generating the data + //! @return A vector of bytes for the image data + AZStd::vector BuildBasicImageData(AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::s32 seed); + + //! Build a mip chain asset that contains the basic image data from BuildBasicImageData + //! @param mipLevels Number of mip levels in the chain + //! @param arraySize Number of sub images within a mip level + //! @param width The width of the image + //! @param height The height of the image + //! @param pixelSize The number of bytes per pixel + //! @param seed The random seed for generating the data + //! @return A mip chain asset with the specified basic image data + AZ::Data::Asset BuildBasicMipChainAsset(AZ::u16 mipLevels, AZ::u16 arraySize, AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::s32 seed); + + //! Construct an array of image data where all the pixels are 0 except for one at the given coordinate + //! @param width Width of the image + //! @param height Height of the image + //! @param pixelSize Number of bytes per pixel + //! @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 A vector of bytes for the image data + AZStd::vector BuildSpecificPixelImageData(AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::u32 pixelX, AZ::u32 pixelY); + + //! Build a mip chain asset that contains the specific image data from BuildSpecificPixelImageData + //! @param mipLevels Number of mip levels in the chain + //! @param arraySize Number of sub images within a mip level + //! @param width The width of the image + //! @param height The height of the image + //! @param pixelSize The number of bytes per pixel + //! @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 A mip chain asset with the specific pixel image data + AZ::Data::Asset BuildSpecificPixelMipChainAsset(AZ::u16 mipLevels, AZ::u16 arraySize, AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::u32 pixelX, AZ::u32 pixelY); + + //! Creates a deterministically random set of pixel data as an AZ::RPI::StreamingImageAsset. + //! \param width The width of the AZ::RPI::StreamingImageAsset + //! \param height The height of the AZ::RPI::StreamingImageAsset + //! \param seed The random seed to use for generating the random data + //! \return The AZ::RPI::StreamingImageAsset in a loaded ready state + AZ::Data::Asset CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed); + + //! Creates an AZ::RPI::StreamingImageAsset 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 AZ::RPI::StreamingImageAsset + //! \param height The height of the AZ::RPI::StreamingImageAsset + //! \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 AZ::RPI::StreamingImageAsset in a loaded ready state + AZ::Data::Asset CreateSpecificPixelImageAsset(AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY); + class GradientSignalTestHelpers { public: diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.cpp deleted file mode 100644 index 7ac6ef1ecc..0000000000 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 - -namespace UnitTest -{ - AZ::Data::Asset ImageAssetMockAssetHandler::CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed) - { - auto imageAsset = AZ::Data::AssetManager::Instance().CreateAsset( - AZ::Data::AssetId(AZ::Uuid::CreateRandom()), AZ::Data::AssetLoadBehavior::Default); - - imageAsset->m_imageWidth = width; - imageAsset->m_imageHeight = height; - imageAsset->m_bytesPerPixel = 1; - imageAsset->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8; - imageAsset->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); - imageAsset->m_imageData.push_back(static_cast(value)); - } - } - - return imageAsset; - } - - AZ::Data::Asset ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset( - AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY) - { - auto imageAsset = AZ::Data::AssetManager::Instance().CreateAsset( - AZ::Data::AssetId(AZ::Uuid::CreateRandom()), AZ::Data::AssetLoadBehavior::Default); - - imageAsset->m_imageWidth = width; - imageAsset->m_imageHeight = height; - imageAsset->m_bytesPerPixel = 1; - imageAsset->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8; - imageAsset->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(height) - 1; y >= 0; --y) - { - for (AZ::u32 x = 0; x < width; ++x) - { - if ((x == static_cast(pixelX)) && (y == static_cast(pixelY))) - { - imageAsset->m_imageData.push_back(pixelValue); - } - else - { - imageAsset->m_imageData.push_back(0); - } - } - } - - return imageAsset; - } -} - diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h b/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h index 30f262d9b4..997e88ce17 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h +++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h @@ -27,53 +27,6 @@ namespace UnitTest { - // 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 - { - //! 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 CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed); - - //! 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 CreateSpecificPixelImageAsset( - AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY); - - AZ::Data::AssetPtr CreateAsset(const AZ::Data::AssetId& id, [[maybe_unused]] const AZ::Data::AssetType& type) override - { - // 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 - { - if (ptr) - { - delete ptr; - } - } - - void GetHandledAssetTypes([[maybe_unused]] AZStd::vector& assetTypes) override - { - } - - AZ::Data::AssetHandler::LoadResult LoadAssetData( - [[maybe_unused]] const AZ::Data::Asset& asset, - [[maybe_unused]] AZStd::shared_ptr stream, - [[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) override - { - return AZ::Data::AssetHandler::LoadResult::LoadComplete; - } - }; - struct MockGradientRequestsBus : public GradientSignal::GradientRequestBus::Handler { diff --git a/Gems/GradientSignal/Code/gradientsignal_shared_tests_files.cmake b/Gems/GradientSignal/Code/gradientsignal_shared_tests_files.cmake index 98ab57b7b0..5e11406bae 100644 --- a/Gems/GradientSignal/Code/gradientsignal_shared_tests_files.cmake +++ b/Gems/GradientSignal/Code/gradientsignal_shared_tests_files.cmake @@ -11,6 +11,5 @@ set(FILES Tests/GradientSignalTestHelpers.h Tests/GradientSignalTestFixtures.cpp Tests/GradientSignalTestFixtures.h - Tests/GradientSignalTestMocks.cpp Tests/GradientSignalTestMocks.h )