| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726 |
- /*
- * 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 <Atom/RPI.Reflect/Image/ImageMipChainAsset.h>
- #include <Atom/RPI.Reflect/Image/ImageMipChainAssetCreator.h>
- #include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
- #include <Atom/RPI.Reflect/Image/StreamingImageAssetHandler.h>
- #include <Atom/RPI.Reflect/Image/StreamingImageAssetCreator.h>
- #include <Atom/RPI.Reflect/Image/StreamingImagePoolAsset.h>
- #include <Atom/RPI.Reflect/Image/StreamingImagePoolAssetCreator.h>
- #include <Atom/RPI.Reflect/Asset/BuiltInAssetHandler.h>
- #include <Atom/RPI.Public/Image/ImageSystemInterface.h>
- #include <Atom/RPI.Public/Image/StreamingImage.h>
- #include <Atom/RPI.Public/Image/StreamingImagePool.h>
- #include <Atom/RPI.Public/RPIUtils.h>
- #include <AtomCore/Instance/InstanceDatabase.h>
- #include <AzCore/Interface/Interface.h>
- #include <AzCore/std/containers/intrusive_list.h>
- #include <Common/RPITestFixture.h>
- #include <Common/ErrorMessageFinder.h>
- #include <Common/SerializeTester.h>
- namespace AZ
- {
- namespace RPI
- {
- class StreamingImageAssetTester
- : public UnitTest::AssetTester<StreamingImageAsset>
- {
- public:
- StreamingImageAssetTester() = default;
- ~StreamingImageAssetTester() override = default;
- void SetAssetReady(Data::Asset<StreamingImageAsset>& asset) override
- {
- asset->SetReady();
- }
- };
- class ImageMipChainAssetTester
- : public UnitTest::AssetTester<ImageMipChainAsset>
- {
- public:
- ImageMipChainAssetTester() = default;
- ~ImageMipChainAssetTester() override = default;
- void SetAssetReady(Data::Asset<ImageMipChainAsset>& asset) override
- {
- asset->SetReady();
- }
- };
- class StreamingImagePoolAssetTester
- : public UnitTest::SerializeTester<StreamingImagePoolAsset>
- {
- using Base = UnitTest::SerializeTester<StreamingImagePoolAsset>;
- public:
- StreamingImagePoolAssetTester(AZ::SerializeContext* serializeContext)
- : Base(serializeContext)
- {}
- AZ::Data::Asset<StreamingImagePoolAsset> SerializeInHelper(const AZ::Data::AssetId& assetId)
- {
- AZ::Data::Asset<StreamingImagePoolAsset> asset = Base::SerializeIn(assetId);
- asset->SetReady();
- return asset;
- }
- };
- }
- }
- namespace UnitTest
- {
- struct TestStreamingImagePoolDescriptor
- : public AZ::RHI::StreamingImagePoolDescriptor
- {
- AZ_CLASS_ALLOCATOR(TestStreamingImagePoolDescriptor, AZ::SystemAllocator);
- AZ_RTTI(TestStreamingImagePoolDescriptor, "{8D0CA5A2-F886-42EF-9B00-09E6C9F6B90B}", AZ::RHI::StreamingImagePoolDescriptor);
- static constexpr uint32_t Magic = 0x1234;
- static void Reflect(AZ::ReflectContext* context)
- {
- if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
- {
- serializeContext->Class<TestStreamingImagePoolDescriptor, AZ::RHI::StreamingImagePoolDescriptor>()
- ->Version(0)
- ->Field("m_magic", &TestStreamingImagePoolDescriptor::m_magic)
- ;
- }
- }
- TestStreamingImagePoolDescriptor() = default;
- TestStreamingImagePoolDescriptor(size_t budgetInBytes)
- {
- m_budgetInBytes = budgetInBytes;
- }
- // A test value to ensure that serialization occurred correctly.
- uint32_t m_magic = Magic;
- };
- class TestStreamingImageContext
- : public AZ::RPI::StreamingImageContext
- , public AZStd::intrusive_list_node<TestStreamingImageContext>
- {
- public:
- AZ_CLASS_ALLOCATOR(TestStreamingImageContext, AZ::SystemAllocator);
- AZ_RTTI(TestStreamingImageContext, "{E2FC3EB5-4F66-41D0-9ABE-6EDD2622DD88}", AZ::RPI::StreamingImageContext);
- };
- class StreamingImageTests
- : public RPITestFixture
- {
- protected:
- AZ::Data::AssetHandler* m_imageHandler = nullptr;
- AZ::Data::AssetHandler* m_mipChainHandler = nullptr;
- AZ::Data::Instance<AZ::RPI::StreamingImagePool> m_defaultPool = nullptr;
- StreamingImageTests()
- {}
- void SetUp() override
- {
- using namespace AZ;
- RPITestFixture::SetUp();
- auto* serializeContext = GetSerializeContext();
- TestStreamingImagePoolDescriptor::Reflect(serializeContext);
- m_imageHandler = Data::AssetManager::Instance().GetHandler(RPI::StreamingImageAsset::RTTI_Type());
- m_mipChainHandler = Data::AssetManager::Instance().GetHandler(RPI::ImageMipChainAsset::RTTI_Type());
- AZ::Data::Asset<AZ::RPI::StreamingImagePoolAsset> poolAsset = BuildImagePoolAsset(16 * 1024 * 1024);
- m_defaultPool = AZ::RPI::StreamingImagePool::FindOrCreate(poolAsset);
- }
- void TearDown() override
- {
- using namespace AZ;
- RHI::ResourceInvalidateBus::ExecuteQueuedEvents();
- m_defaultPool = nullptr;
- RPITestFixture::TearDown();
- }
- AZStd::vector<uint8_t> BuildImageData(uint32_t width, uint32_t height, uint32_t pixelSize)
- {
- const size_t imageSize = width * height * pixelSize;
- AZStd::vector<uint8_t> image;
- image.reserve(imageSize);
- uint8_t testValue = 0;
- for (uint32_t y = 0; y < height; ++y)
- {
- for (uint32_t x = 0; x < width; ++x)
- {
- for (uint32_t channel = 0; channel < pixelSize; ++channel)
- {
- image.push_back(testValue);
- testValue++;
- }
- }
- }
- EXPECT_EQ(image.size(), imageSize);
- return image;
- }
- void ValidateImageData(AZStd::span<const uint8_t> data, const AZ::RHI::ImageSubresourceLayout& layout)
- {
- const uint32_t pixelSize = layout.m_size.m_width / layout.m_bytesPerRow;
- uint32_t byteOffset = 0;
- for (uint32_t y = 0; y < layout.m_size.m_height; ++y)
- {
- for (uint32_t x = 0; x < layout.m_size.m_width; ++x)
- {
- for (uint32_t channel = 0; channel < pixelSize; ++channel)
- {
- uint8_t value = data[byteOffset];
- EXPECT_EQ(value, static_cast<uint8_t>(byteOffset));
- byteOffset++;
- }
- }
- }
- }
- void ValidateMipChainAsset(
- AZ::RPI::ImageMipChainAsset* mipChain,
- uint16_t expectedMipLevels,
- uint16_t expectedArraySize,
- uint32_t expectedPixelSize)
- {
- using namespace AZ;
- EXPECT_NE(mipChain, nullptr);
- EXPECT_EQ(expectedMipLevels, mipChain->GetMipLevelCount());
- EXPECT_EQ(expectedArraySize, mipChain->GetArraySize());
- EXPECT_EQ(expectedMipLevels * expectedArraySize, mipChain->GetSubImageCount());
- const uint32_t imageSize = 1 << mipChain->GetMipLevelCount();
- for (uint16_t mipLevel = 0; mipLevel < mipChain->GetMipLevelCount(); ++mipLevel)
- {
- RHI::ImageSubresourceLayout layout = BuildSubImageLayout(imageSize >> mipLevel, expectedPixelSize);
- EXPECT_EQ(memcmp(&layout, &mipChain->GetSubImageLayout(mipLevel), sizeof(RHI::ImageSubresourceLayout)), 0);
- for (uint16_t arrayIndex = 0; arrayIndex < mipChain->GetArraySize(); ++arrayIndex)
- {
- AZStd::span<const uint8_t> imageData = mipChain->GetSubImageData(mipLevel, arrayIndex);
- ValidateImageData(imageData, layout);
- }
- }
- }
- void ValidateImageAsset(AZ::RPI::StreamingImageAsset* imageAsset)
- {
- using namespace AZ;
- EXPECT_NE(imageAsset, nullptr);
- RHI::ImageDescriptor imageDesc = imageAsset->GetImageDescriptor();
- size_t mipCountTotal = 0;
- for (size_t i = 0; i < imageAsset->GetMipChainCount(); ++i)
- {
- // The last mip chain asset (tail mip chain) is expected to be empty since the actual mip chain asset data is embedded in StreamingImageAsset
- if (i != imageAsset->GetMipChainCount() - 1)
- {
- EXPECT_TRUE(imageAsset->GetMipChainAsset(i).GetId().IsValid());
- }
- EXPECT_EQ(imageAsset->GetMipLevel(i), mipCountTotal);
- mipCountTotal += imageAsset->GetMipCount(i);
- }
- EXPECT_EQ(imageDesc.m_mipLevels, mipCountTotal);
- }
- void ValidateImagePoolAsset(AZ::RPI::StreamingImagePoolAsset* poolAsset, size_t budgetInBytes)
- {
- using namespace AZ;
- EXPECT_EQ(poolAsset->GetPoolDescriptor().m_budgetInBytes, budgetInBytes);
- {
- const auto* desc = azrtti_cast<const TestStreamingImagePoolDescriptor*>(&poolAsset->GetPoolDescriptor());
- EXPECT_NE(desc, nullptr);
- EXPECT_EQ(desc->m_magic, UnitTest::TestStreamingImagePoolDescriptor::Magic);
- }
- }
- void ValidateImageResidency(AZ::RPI::StreamingImage* imageInstance, AZ::RPI::StreamingImageAsset* imageAsset)
- {
- using namespace AZ;
- auto imageSystem = RPI::ImageSystemInterface::Get();
- const size_t mipChainTailIndex = imageAsset->GetMipChainCount() - 1;
- RHI::Ptr<RHI::Image> rhiImage = imageInstance->GetRHIImage();
- // This should no-op.
- imageInstance->TrimToMipChainLevel(mipChainTailIndex);
- // Validate that nothing was actually evicted, since we've set to NoEvict.
- for (size_t i = 0; i < mipChainTailIndex; ++i)
- {
- EXPECT_TRUE(imageAsset->GetMipChainAsset(i).IsReady());
- }
- EXPECT_EQ(rhiImage->GetResidentMipLevel(), imageAsset->GetMipLevel(mipChainTailIndex));
- // Expand to the most detailed mip chain.
- imageInstance->QueueExpandToMipChainLevel(0);
- // We should still be the same residency level, since it's queued.
- EXPECT_EQ(rhiImage->GetResidentMipLevel(), imageAsset->GetMipLevel(mipChainTailIndex));
- // Tick the streaming system.
- imageSystem->Update();
- // Now we should be at the desired residency level.
- EXPECT_EQ(rhiImage->GetResidentMipLevel(), 0);
- // Expanding 'down' is a no-op.
- imageInstance->QueueExpandToMipChainLevel(1);
- imageSystem->Update();
- EXPECT_EQ(rhiImage->GetResidentMipLevel(), 0);
- // Trimming down a notch. This happens instantly.
- imageInstance->TrimToMipChainLevel(1);
- EXPECT_EQ(rhiImage->GetResidentMipLevel(), imageAsset->GetMipLevel(1));
- // Trim down again.
- imageInstance->TrimToMipChainLevel(2);
- EXPECT_EQ(rhiImage->GetResidentMipLevel(), imageAsset->GetMipLevel(2));
- // Expanding back up to 1.
- imageInstance->QueueExpandToMipChainLevel(1);
- imageSystem->Update();
- EXPECT_EQ(rhiImage->GetResidentMipLevel(), imageAsset->GetMipLevel(1));
- // Expanding back up to 0.
- imageInstance->QueueExpandToMipChainLevel(0);
- imageSystem->Update();
- EXPECT_EQ(rhiImage->GetResidentMipLevel(), 0);
- }
- AZ::RHI::ImageSubresourceLayout BuildSubImageLayout(uint32_t imageSize, uint32_t pixelSize)
- {
- using namespace AZ;
- RHI::ImageSubresourceLayout layout;
- layout.m_size = RHI::Size{ imageSize, imageSize, 1 };
- layout.m_rowCount = imageSize;
- layout.m_bytesPerRow = imageSize * pixelSize;
- layout.m_bytesPerImage = imageSize * imageSize * pixelSize;
- return layout;
- }
- AZ::Data::Asset<AZ::RPI::ImageMipChainAsset> BuildMipChainAsset(uint16_t mipOffset, uint16_t mipLevels, uint16_t arraySize, uint32_t pixelSize)
- {
- using namespace AZ;
- RPI::ImageMipChainAssetCreator assetCreator;
- const uint32_t imageSize = 1 << (mipLevels + mipOffset);
- assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), mipLevels, arraySize);
- for (uint32_t mipLevel = 0; mipLevel < mipLevels; ++mipLevel)
- {
- const uint32_t mipSize = imageSize >> mipLevel;
- RHI::ImageSubresourceLayout layout = BuildSubImageLayout(mipSize, pixelSize);
- assetCreator.BeginMip(layout);
- for (uint32_t arrayIndex = 0; arrayIndex < arraySize; ++arrayIndex)
- {
- AZStd::vector<uint8_t> data = BuildImageData(mipSize, mipSize, pixelSize);
- assetCreator.AddSubImage(data.data(), data.size());
- }
- assetCreator.EndMip();
- }
- Data::Asset<RPI::ImageMipChainAsset> asset;
- EXPECT_TRUE(assetCreator.End(asset));
- EXPECT_TRUE(asset.IsReady());
- EXPECT_NE(asset.Get(), nullptr);
- return asset;
- }
- AZ::Data::Asset<AZ::RPI::StreamingImagePoolAsset> BuildImagePoolAsset(size_t budgetInBytes)
- {
- using namespace AZ;
- RPI::StreamingImagePoolAssetCreator assetCreator;
- assetCreator.Begin(Data::AssetId(Uuid::CreateRandom()));
- assetCreator.SetPoolDescriptor(AZStd::make_unique<TestStreamingImagePoolDescriptor>(budgetInBytes));
- Data::Asset<RPI::StreamingImagePoolAsset> poolAsset;
- EXPECT_TRUE(assetCreator.End(poolAsset));
- EXPECT_TRUE(poolAsset.IsReady());
- EXPECT_NE(poolAsset.Get(), nullptr);
- return poolAsset;
- }
- AZ::Data::Asset<AZ::RPI::StreamingImageAsset> BuildTestImage(AZ::RHI::Format format = AZ::RHI::Format::R8G8B8A8_UNORM)
- {
- using namespace AZ;
- const uint32_t arraySize = 2;
- const uint32_t pixelSize = RHI::GetFormatSize(format);
- const uint32_t mipCountHead = 1;
- const uint32_t mipCountMiddle = 2;
- const uint32_t mipCountTail = 3;
- const uint32_t mipCountTotal = mipCountHead + mipCountMiddle + mipCountTail;
- const uint32_t imageWidth = 1 << mipCountTotal;
- const uint32_t imageHeight = 1 << mipCountTotal;
- Data::Asset<RPI::ImageMipChainAsset> mipTail = BuildMipChainAsset(0, mipCountTail, arraySize, pixelSize);
- Data::Asset<RPI::ImageMipChainAsset> mipMiddle = BuildMipChainAsset(mipCountTail, mipCountMiddle, arraySize, pixelSize);
- Data::Asset<RPI::ImageMipChainAsset> mipHead = BuildMipChainAsset(mipCountTail + mipCountMiddle, mipCountHead, arraySize, pixelSize);
- RPI::StreamingImageAssetCreator assetCreator;
- assetCreator.Begin(Data::AssetId(Uuid::CreateRandom()));
- RHI::ImageDescriptor imageDesc = RHI::ImageDescriptor::Create2DArray(RHI::ImageBindFlags::ShaderRead, imageWidth, imageHeight, arraySize, format);
- imageDesc.m_mipLevels = static_cast<uint16_t>(mipCountTotal);
- assetCreator.SetImageDescriptor(imageDesc);
- assetCreator.AddMipChainAsset(*mipHead.Get());
- assetCreator.AddMipChainAsset(*mipMiddle.Get());
- assetCreator.AddMipChainAsset(*mipTail.Get());
- assetCreator.SetPoolAssetId(m_defaultPool->GetAssetId());
- Data::Asset<RPI::StreamingImageAsset> imageAsset;
- EXPECT_TRUE(assetCreator.End(imageAsset));
- EXPECT_TRUE(imageAsset.IsReady());
- EXPECT_NE(imageAsset.Get(), nullptr);
- return imageAsset;
- }
- };
- TEST_F(StreamingImageTests, MipChainCreate)
- {
- using namespace AZ;
- const uint16_t mipLevels = 5;
- const uint16_t arraySize = 4;
- const uint16_t pixelSize = 4;
- Data::Asset<RPI::ImageMipChainAsset> mipChain = BuildMipChainAsset(0, mipLevels, arraySize, pixelSize);
- ValidateMipChainAsset(mipChain.Get(), mipLevels, arraySize, pixelSize);
- }
- TEST_F(StreamingImageTests, MipChainAssetSuccessAfterErrorCases)
- {
- using namespace AZ;
- const uint16_t mipLevels = 1;
- const uint16_t arraySize = 1;
- Data::Asset<RPI::ImageMipChainAsset> mipChain;
- {
- RPI::ImageMipChainAssetCreator assetCreator;
- ErrorMessageFinder messageFinder("Begin() was not called");
- assetCreator.EndMip();
- }
- {
- RPI::ImageMipChainAssetCreator assetCreator;
- ErrorMessageFinder messageFinder("Begin() was not called");
- assetCreator.End(mipChain);
- }
- {
- RPI::ImageMipChainAssetCreator assetCreator;
- assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), mipLevels, arraySize);
- assetCreator.BeginMip(RHI::ImageSubresourceLayout());
- ErrorMessageFinder messageFinder("Expected 1 sub-images in mip, but got 0.");
- assetCreator.EndMip();
- }
- {
- RPI::ImageMipChainAssetCreator assetCreator;
- assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), mipLevels, arraySize);
- assetCreator.BeginMip(RHI::ImageSubresourceLayout());
- ErrorMessageFinder messageFinder("You must supply a valid data payload.");
- assetCreator.AddSubImage(nullptr, 0);
- }
- {
- RPI::ImageMipChainAssetCreator assetCreator;
- assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), mipLevels, arraySize);
- assetCreator.BeginMip(RHI::ImageSubresourceLayout());
- ErrorMessageFinder messageFinder("You must supply a valid data payload.");
- assetCreator.AddSubImage(nullptr, 10);
- }
- uint8_t data[4] = { 0, 5, 10, 15 };
- const uint8_t dataSize = AZ_ARRAY_SIZE(data);
- {
- RPI::ImageMipChainAssetCreator assetCreator;
- assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), mipLevels, arraySize);
- assetCreator.BeginMip(RHI::ImageSubresourceLayout());
- assetCreator.AddSubImage(data, dataSize);
- ErrorMessageFinder messageFinder("Exceeded the 1 array slices declared in Begin().");
- assetCreator.AddSubImage(data, dataSize);
- }
- {
- RPI::ImageMipChainAssetCreator assetCreator;
- assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), mipLevels, arraySize);
- assetCreator.BeginMip(RHI::ImageSubresourceLayout());
- assetCreator.AddSubImage(data, dataSize);
- ErrorMessageFinder messageFinder("Already building a mip. You must call EndMip() first.");
- assetCreator.BeginMip(RHI::ImageSubresourceLayout());
- }
- // Finally, build a valid one
- {
- RPI::ImageMipChainAssetCreator assetCreator;
- assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), mipLevels, arraySize);
- assetCreator.BeginMip(RHI::ImageSubresourceLayout());
- assetCreator.AddSubImage(data, dataSize);
- assetCreator.EndMip();
- EXPECT_TRUE(assetCreator.End(mipChain));
- EXPECT_NE(mipChain.Get(), nullptr);
- EXPECT_EQ(mipChain->GetMipLevelCount(), mipLevels);
- EXPECT_EQ(mipChain->GetArraySize(), arraySize);
- EXPECT_EQ(mipChain->GetSubImageCount(), mipLevels * arraySize);
- AZStd::span<const uint8_t> dataView = mipChain->GetSubImageData(0);
- EXPECT_EQ(dataView[0], data[0]);
- EXPECT_EQ(dataView[1], data[1]);
- EXPECT_EQ(dataView[2], data[2]);
- EXPECT_EQ(dataView[3], data[3]);
- }
- }
- TEST_F(StreamingImageTests, MipChainAssetSerialize)
- {
- using namespace AZ;
- const uint16_t mipLevels = 6;
- const uint16_t arraySize = 2;
- const uint16_t pixelSize = 2;
-
- Data::Asset<RPI::ImageMipChainAsset> mipChain = BuildMipChainAsset(0, mipLevels, arraySize, pixelSize);
- RPI::ImageMipChainAssetTester tester;
- tester.SerializeOut(mipChain);
- Data::Asset<RPI::ImageMipChainAsset> serializedMipChain = tester.SerializeIn(Data::AssetId(Uuid::CreateRandom()));
- ValidateMipChainAsset(serializedMipChain.Get(), mipLevels, arraySize, pixelSize);
- }
- TEST_F(StreamingImageTests, PoolAssetCreation)
- {
- using namespace AZ;
- const size_t budgetInBytes = 16 * 1024 * 1024;
- Data::Asset<RPI::StreamingImagePoolAsset> poolAsset = BuildImagePoolAsset(budgetInBytes);
- ValidateImagePoolAsset(poolAsset.Get(), budgetInBytes);
- }
- TEST_F(StreamingImageTests, PoolAssetSerialize)
- {
- using namespace AZ;
- const size_t budgetInBytes = 16 * 1024 * 1024;
- Data::Asset<RPI::StreamingImagePoolAsset> poolAsset = BuildImagePoolAsset(budgetInBytes);
- RPI::StreamingImagePoolAssetTester tester(GetSerializeContext());
- tester.SerializeOut(poolAsset.Get());
- Data::Asset<RPI::StreamingImagePoolAsset> serializedPoolAsset = tester.SerializeInHelper(Data::AssetId(Uuid::CreateRandom()));
- ValidateImagePoolAsset(serializedPoolAsset.Get(), budgetInBytes);
- }
- TEST_F(StreamingImageTests, PoolInstanceCreation)
- {
- using namespace AZ;
- const size_t budgetInBytes = 16 * 1024 * 1024;
- Data::Asset<RPI::StreamingImagePoolAsset> poolAsset = BuildImagePoolAsset(budgetInBytes);
- Data::Instance<RPI::StreamingImagePool> poolInstance = RPI::StreamingImagePool::FindOrCreate(poolAsset);
- EXPECT_NE(poolInstance.get(), nullptr);
- EXPECT_NE(poolInstance->GetRHIPool(), nullptr);
- }
- TEST_F(StreamingImageTests, ImageAssetCreation)
- {
- using namespace AZ;
- Data::Asset<RPI::StreamingImageAsset> imageAsset = BuildTestImage();
- ValidateImageAsset(imageAsset.Get());
- }
- TEST_F(StreamingImageTests, ImageAssetSerialize)
- {
- using namespace AZ;
- Data::Asset<RPI::StreamingImageAsset> imageAsset = BuildTestImage();
-
- RPI::StreamingImageAssetTester tester;
- tester.SerializeOut(imageAsset);
- Data::Asset<RPI::StreamingImageAsset> serializedImageAsset = tester.SerializeIn(Data::AssetId(Uuid::CreateRandom()));
- ValidateImageAsset(serializedImageAsset.Get());
- }
- TEST_F(StreamingImageTests, ImageInstanceCreation)
- {
- using namespace AZ;
- Data::Asset<RPI::StreamingImageAsset> imageAsset = BuildTestImage();
- Data::Instance<RPI::StreamingImage> imageInstance = RPI::StreamingImage::FindOrCreate(imageAsset);
- EXPECT_NE(imageInstance.get(), nullptr);
- EXPECT_NE(imageInstance->GetRHIImage(), nullptr);
- EXPECT_NE(imageInstance->GetImageView(), nullptr);
- EXPECT_GE(imageAsset->GetMipChainCount(), 0);
- const size_t mipChainTailIndex = imageAsset->GetMipChainCount() - 1;
- EXPECT_EQ(imageInstance->GetRHIImage()->GetResidentMipLevel(), imageAsset->GetMipLevel(mipChainTailIndex));
- for (size_t i = 0; i < mipChainTailIndex; ++i)
- {
- Data::Asset<RPI::ImageMipChainAsset> mipChainAsset = imageAsset->GetMipChainAsset(i);
- EXPECT_TRUE(mipChainAsset.IsReady());
- }
- }
- TEST_F(StreamingImageTests, ImageInstanceResidency)
- {
- using namespace AZ;
- Data::Asset<RPI::StreamingImageAsset> imageAsset = BuildTestImage();
- Data::Instance<RPI::StreamingImage> imageInstance = RPI::StreamingImage::FindOrCreate(imageAsset);
- ValidateImageResidency(imageInstance.get(), imageAsset.Get());
- }
- TEST_F(StreamingImageTests, ImageInstanceResidencyWithSerialization)
- {
- using namespace AZ;
- // Keep the original around, which holds references to the image mip chain assets and pool asset. We need to
- // keep them in memory to avoid the asset manager trying to hit the catalog.
- Data::Asset<RPI::StreamingImageAsset> imageAsset = BuildTestImage();
- RPI::StreamingImageAssetTester tester;
- tester.SerializeOut(imageAsset);
- Data::Asset<RPI::StreamingImageAsset> serializedImageAsset = tester.SerializeIn(Data::AssetId(Uuid::CreateRandom()));
-
- Data::Instance<RPI::StreamingImage> imageInstance = RPI::StreamingImage::FindOrCreate(serializedImageAsset);
- ValidateImageResidency(imageInstance.get(), imageAsset.Get());
- }
- TEST_F(StreamingImageTests, ImageInternalReferenceTracking)
- {
- using namespace AZ;
- Data::Asset<RPI::StreamingImageAsset> imageAsset = BuildTestImage();
- Data::Instance<RPI::StreamingImagePool> imagePoolInstance;
- {
- Data::Instance<RPI::StreamingImage> imageInstance = RPI::StreamingImage::FindOrCreate(imageAsset);
- // Hold the pool instance to keep it around.
- imagePoolInstance = imageInstance->GetPool();
- // Tests that we can safely destroy an image after queueing something to the system,
- // and the system will properly avoid touching that data.
- imageInstance->QueueExpandToMipChainLevel(0);
- }
- RPI::ImageSystemInterface::Get()->Update();
- }
- TEST_F(StreamingImageTests, GetSubImagePixelValues)
- {
- using namespace AZ;
- Data::Asset<RPI::StreamingImageAsset> imageAsset = BuildTestImage(AZ::RHI::Format::R8_UNORM);
- auto streamingImageAsset = imageAsset.Get();
- EXPECT_NE(streamingImageAsset, nullptr);
- // Validate retrieving one pixel at a time
- auto size = streamingImageAsset->GetImageDescriptor().m_size;
- for (uint32_t y = 0; y < size.m_height; ++y)
- {
- for (uint32_t x = 0; x < size.m_width; ++x)
- {
- auto pixelDataValue = RPI::GetSubImagePixelValue<float>(imageAsset, x, y);
- auto pixelExpectedValue = static_cast<uint8_t>(y * size.m_width + x) / static_cast<float>(std::numeric_limits<AZ::u8>::max());
- EXPECT_NEAR(pixelDataValue, pixelExpectedValue, Constants::Tolerance);
- }
- }
- // Validate retrieving a region of pixels
- AZStd::vector<float> pixelValues;
- pixelValues.reserve(size.m_width * size.m_height);
- auto topLeft = AZStd::make_pair(0U, 0U);
- auto bottomRight = AZStd::make_pair(size.m_width, size.m_height);
- RPI::GetSubImagePixelValues(imageAsset, topLeft, bottomRight, [&pixelValues]([[maybe_unused]] const AZ::u32& x, [[maybe_unused]] const AZ::u32& y, const float& value) {
- pixelValues.push_back(value);
- });
- for (uint32_t index = 0; index < pixelValues.size(); ++index)
- {
- auto pixelDataValue = pixelValues[index];
- auto pixelExpectedValue = static_cast<uint8_t>(index) / static_cast<float>(std::numeric_limits<AZ::u8>::max());
- EXPECT_NEAR(pixelDataValue, pixelExpectedValue, Constants::Tolerance);
- }
- }
- }
|