Files
o3de/Gems/Atom/RPI/Code/Tests/ShaderResourceGroup/ShaderResourceGroupConstantBufferTests.cpp
T
lumberyard-employee-dm b9824ed172 Updated all array_view uses with the C++20 span. (#7157)
* Updated all array_view uses with the C++20 span.

The updates were done in the following order
1. `AZStd::array_view<([^>].+)\* ?>`  -> `AZStd::span<\1 const>`
2. `AZStd::array_view<(?:const )(.+)>` -> `AZStd::span<const \1>`
3. `AZStd::array_view` -> `AZStd::span`

Removed the implementation of array_view.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Added missing whitespace between `const` and the typename for spans.

Updated the ShaderTest comparison of the ShaderResourceGroupLayout span
to compare the sizes as well

Updated comments on some of the methods that stated that they return "an
array" to mention they return "a span".

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
2022-01-26 16:15:47 -06:00

437 lines
16 KiB
C++

/*
* 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 <Common/RPITestFixture.h>
#include <Common/ShaderAssetTestUtils.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
namespace UnitTest
{
class ShaderResourceGroupConstantBufferTests
: public RPITestFixture
{
protected:
struct SimpleStruct
{
SimpleStruct() = default;
SimpleStruct(float f, uint32_t u)
: m_float{f}
, m_uint{u}
{}
float m_float = 0;
uint32_t m_uint = 0;
};
AZ::Data::Asset<AZ::RPI::ShaderAsset> m_shaderAsset;
AZ::RHI::Ptr<AZ::RHI::ShaderResourceGroupLayout> m_srgLayout;
AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> m_srg;
void SetUp() override
{
RPITestFixture::SetUp();
// This provides the high-level metadata and low-level srg layout
m_srgLayout = BuildSrgLayoutWithShaderConstants(m_shaderAsset);
ASSERT_TRUE(m_srgLayout);
ASSERT_TRUE(m_shaderAsset.IsReady());
m_srg = AZ::RPI::ShaderResourceGroup::Create(m_shaderAsset, AZ::RPI::DefaultSupervariantIndex, m_srgLayout->GetName());
ASSERT_TRUE(m_srg != nullptr);
}
void TearDown() override
{
m_srg.reset();
m_srgLayout = nullptr;
m_shaderAsset.Release();
RPITestFixture::TearDown();
}
template<typename T>
void ExpectEqual(AZStd::initializer_list<T> expectedValues, AZStd::span<const T> arrayView)
{
EXPECT_EQ(expectedValues.size(), arrayView.size());
const T* expected = expectedValues.begin();
for (int i = 0; i < expectedValues.size() && i < arrayView.size(); ++i)
{
EXPECT_EQ(expected[i], arrayView[i]);
}
}
AZ::RHI::Ptr<AZ::RHI::ShaderResourceGroupLayout> BuildSrgLayoutWithShaderConstants(
AZ::Data::Asset<AZ::RPI::ShaderAsset>& shaderAsset, [[maybe_unused]] bool includeMetadata = true)
{
using namespace AZ;
AZ::RHI::Ptr<AZ::RHI::ShaderResourceGroupLayout> srgLayout = RHI::ShaderResourceGroupLayout::Create();
srgLayout->SetName(Name{"TestSrg"});
uint32_t offset = 0;
uint32_t count;
uint32_t size;
uint32_t registerIndex = 0;
uint32_t sizeOfBool = 4;
srgLayout->SetBindingSlot(0);
// bool, binding index 0
count = 1;
size = count * sizeOfBool;
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyBool"), offset, size, registerIndex });
offset += size;
// bool2, binding index 1
count = 2;
size = count * sizeOfBool;
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyBool2"), offset, size, registerIndex });
offset += size;
// bool3, binding index 2
count = 3;
size = count * sizeOfBool;
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyBool3"), offset, size, registerIndex });
offset += size;
// bool4, binding index 3
count = 4;
size = count * sizeOfBool;
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyBool4"), offset, size, registerIndex });
offset += size;
// int, binding index 4
count = 1;
size = count * sizeof(int32_t);
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyInt"), offset, size, registerIndex });
offset += size;
// int2, binding index 5
count = 2;
size = count * sizeof(int32_t);
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyInt2"), offset, size, registerIndex });
offset += size;
// int3, binding index 6
count = 3;
size = count * sizeof(int32_t);
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyInt3"), offset, size, registerIndex });
offset += size;
// int4, binding index 7
count = 4;
size = count * sizeof(int32_t);
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyInt4"), offset, size, registerIndex });
offset += size;
// uint, binding index 8
count = 1;
size = count * sizeof(uint32_t);
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyUint"), offset, size, registerIndex });
offset += size;
// uint2, binding index 9
count = 2;
size = count * sizeof(uint32_t);
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyUint2"), offset, size, registerIndex });
offset += size;
// uint3, binding index 10
count = 3;
size = count * sizeof(uint32_t);
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyUint3"), offset, size, registerIndex });
offset += size;
// uint4, binding index 11
count = 4;
size = count * sizeof(uint32_t);
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyUint4"), offset, size, registerIndex });
offset += size;
// float, binding index 12
count = 1;
size = count * sizeof(float);
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyFloat"), offset, size, registerIndex });
offset += size;
// float2, binding index 13
count = 2;
size = count * sizeof(float);
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyFloat2"), offset, size, registerIndex });
offset += size;
// float3, binding index 14
count = 3;
size = count * sizeof(float);
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyFloat3"), offset, size, registerIndex });
offset += size;
// float4, binding index 15
count = 4;
size = count * sizeof(float);
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MyFloat4"), offset, size, registerIndex });
offset += size;
// simple struct, binding index 16
// [GFX TODO][ATOM-111] This is not very fleshed out right now. We still need to do more to support structs, but at least I want to verify that SRG templatized setters and getters can work with structs
count = 1;
size = 8;
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MySimpleStruct"), offset, size, registerIndex });
offset += size;
// array of 2 simple structs, binding index 17
// [GFX TODO][ATOM-111] This is not very fleshed out right now. We still need to do more to support structs, but at least I want to verify that SRG templatized setters and getters can work with structs
count = 2;
size = 16;
srgLayout->AddShaderInput(RHI::ShaderInputConstantDescriptor{Name("MySimpleStructArray2"), offset, size, registerIndex });
offset += size;
srgLayout->SetBindingSlot(0);
EXPECT_TRUE(srgLayout->Finalize());
shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), srgLayout);
return srgLayout;
}
};
TEST_F(ShaderResourceGroupConstantBufferTests, SetConstant_GetConstant_ValidInput_Bool)
{
using namespace AZ;
{
const RHI::ShaderInputConstantIndex inputIndex(0);
// Check using inputIndex
EXPECT_TRUE(m_srg->SetConstant(inputIndex, true));
EXPECT_EQ(true, m_srg->GetConstant<bool>(inputIndex));
AZStd::span<const uint8_t> result = m_srg->GetConstantRaw(inputIndex);
AZStd::span<const uint32_t> resultInUint = AZStd::span<const uint32_t>(reinterpret_cast<const uint32_t*>(result.data()), 1);
ExpectEqual<uint32_t>({ 1 /*true*/ }, resultInUint);
EXPECT_TRUE(m_srg->SetConstant(inputIndex, false));
EXPECT_EQ(false, m_srg->GetConstant<bool>(inputIndex));
result = m_srg->GetConstantRaw(inputIndex);
resultInUint = AZStd::span<const uint32_t>(reinterpret_cast<const uint32_t*>(result.data()), 1);
ExpectEqual<uint32_t>({ 0 /*false*/ }, resultInUint);
}
{
const RHI::ShaderInputConstantIndex inputIndex(1);
// Check using inputIndex
EXPECT_TRUE(m_srg->SetConstantArray<bool>(inputIndex, AZStd::array<bool, 2>({ true, false })));
AZStd::span<const uint8_t> result = m_srg->GetConstantRaw(inputIndex);
AZStd::span<const uint32_t> resultInUint = AZStd::span<const uint32_t>(reinterpret_cast<const uint32_t*>(result.data()), 2);
ExpectEqual<uint32_t>({ 1 /*true*/, 0 /*false*/ }, resultInUint);
EXPECT_TRUE(m_srg->SetConstantArray<bool>(inputIndex, AZStd::array<bool, 2>({ false, true })));
result = m_srg->GetConstantRaw(inputIndex);
resultInUint = AZStd::span<const uint32_t>(reinterpret_cast<const uint32_t*>(result.data()), 2);
ExpectEqual<uint32_t>({ 0 /*false*/, 1 /*true*/ }, resultInUint);
}
}
TEST_F(ShaderResourceGroupConstantBufferTests, SetConstant_GetConstant_FalsePackedInGarbage_Bool)
{
using namespace AZ;
uint32_t falsePackedInGarbage = 0xab00cdef;
bool* asBools = reinterpret_cast<bool*>(&falsePackedInGarbage);
{
const RHI::ShaderInputConstantIndex inputIndex(0);
// Check using inputIndex
EXPECT_TRUE(m_srg->SetConstant<bool>(inputIndex, asBools[2]));
EXPECT_EQ(false, m_srg->GetConstant<bool>(inputIndex));
}
{
// Check using inputIndex
const RHI::ShaderInputConstantIndex inputIndex(1);
EXPECT_TRUE(m_srg->SetConstantArray<bool>(inputIndex, AZStd::array<bool, 2>({ asBools[1], asBools[2] })));
AZStd::span<const uint8_t> result = m_srg->GetConstantRaw(inputIndex);
AZStd::span<const uint32_t> resultInUint = AZStd::span<const uint32_t>(reinterpret_cast<const uint32_t*>(result.data()), 2);
EXPECT_THAT(resultInUint, testing::ElementsAre(testing::IsTrue(), testing::IsFalse()));
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Test valid inputs for SetConstant and GetConstant
TEST_F(ShaderResourceGroupConstantBufferTests, SetConstant_GetConstant_ValidInput_Int)
{
using namespace AZ;
{
const RHI::ShaderInputConstantIndex inputIndex(4);
// Check using inputIndex
EXPECT_TRUE(m_srg->SetConstant(inputIndex, 51));
EXPECT_EQ(51, m_srg->GetConstant<int32_t>(inputIndex));
ExpectEqual<int32_t>({ 51 }, m_srg->GetConstantArray<int32_t>(inputIndex));
}
{
const RHI::ShaderInputConstantIndex inputIndex(5);
// Check using inputIndex
EXPECT_TRUE(m_srg->SetConstantArray<int32_t>(inputIndex, AZStd::array<int32_t, 2>({ 54, 55 })));
ExpectEqual<int32_t>({ 54, 55 }, m_srg->GetConstantArray<int32_t>(inputIndex));
}
}
TEST_F(ShaderResourceGroupConstantBufferTests, SetConstant_GetConstant_ValidInput_Float)
{
using namespace AZ;
{
const RHI::ShaderInputConstantIndex inputIndex(12);
// Check using inputIndex
EXPECT_TRUE(m_srg->SetConstant(inputIndex, 1.1f));
EXPECT_EQ(1.1f, m_srg->GetConstant<float>(inputIndex));
ExpectEqual<float>({ 1.1f }, m_srg->GetConstantArray<float>(inputIndex));
}
{
const RHI::ShaderInputConstantIndex inputIndex(13);
// Check using inputIndex
EXPECT_TRUE(m_srg->SetConstantArray<float>(inputIndex, AZStd::array<float, 2>({ 1.4f, 1.5f })));
ExpectEqual<float>({ 1.4f, 1.5f }, m_srg->GetConstantArray<float>(inputIndex));
}
}
TEST_F(ShaderResourceGroupConstantBufferTests, SetConstant_GetConstant_ValidInput_Vector4)
{
using namespace AZ;
AZ::Vector4 value;
const RHI::ShaderInputConstantIndex inputIndex(15);
// Check using inputIndex
EXPECT_TRUE(m_srg->SetConstant(inputIndex, AZ::Vector4(2.6f, 2.7f, 2.8f, 2.9f)));
value = m_srg->GetConstant<AZ::Vector4>(inputIndex);
EXPECT_EQ(2.6f, static_cast<float>(value.GetX()));
EXPECT_EQ(2.7f, static_cast<float>(value.GetY()));
EXPECT_EQ(2.8f, static_cast<float>(value.GetZ()));
EXPECT_EQ(2.9f, static_cast<float>(value.GetW()));
}
TEST_F(ShaderResourceGroupConstantBufferTests, SetConstant_GetConstant_ValidInput_SimpleStruct)
{
using namespace AZ;
SimpleStruct value;
const RHI::ShaderInputConstantIndex inputIndex(16);
// Demonstrate the syntax of setting with a variable, and inputIndex
{
SimpleStruct inputValues = { 2.1f, 101 };
EXPECT_TRUE(m_srg->SetConstant(inputIndex, inputValues));
value = m_srg->GetConstant<SimpleStruct>(inputIndex);
EXPECT_EQ(2.1f, value.m_float);
EXPECT_EQ(101, value.m_uint);
}
}
TEST_F(ShaderResourceGroupConstantBufferTests, SetConstant_GetConstant_ValidInput_SimpleStruct_Array)
{
using namespace AZ;
AZStd::span<const SimpleStruct> values;
const RHI::ShaderInputConstantIndex inputIndex(17);
// Demonstrate the syntax of setting with a variable, and inputIndex...
// Unfortunately, with arrays of custom types, you have to specify the element type explicitly
{
AZStd::vector<SimpleStruct> inputValues;
inputValues.push_back({ 0.3f, 3 });
inputValues.push_back({ 0.4f, 4 });
EXPECT_TRUE(m_srg->SetConstantArray<SimpleStruct>(inputIndex, inputValues));
values = m_srg->GetConstantArray<SimpleStruct>(inputIndex);
EXPECT_EQ(2, values.size());
EXPECT_EQ(0.3f, values[0].m_float);
EXPECT_EQ(3, values[0].m_uint);
EXPECT_EQ(0.4f, values[1].m_float);
EXPECT_EQ(4, values[1].m_uint);
}
}
TEST_F(ShaderResourceGroupConstantBufferTests, TestErrorReporting_SetConstant_WrongNumberOfElements_ArrayInput)
{
using namespace AZ;
{
AZ_TEST_START_ASSERTTEST;
// MyFloat2
EXPECT_FALSE(m_srg->SetConstantArray<float>(RHI::ShaderInputConstantIndex(13), AZStd::array<float, 3>({ 0.1f, 0.2f, 0.3f })));
AZ_TEST_STOP_ASSERTTEST(1);
}
}
TEST_F(ShaderResourceGroupConstantBufferTests, TestErrorReporting_GetConstants_WrongNumberOfElements_ArrayOutput)
{
using namespace AZ;
{
AZ_TEST_START_ASSERTTEST;
// MyFloat2
m_srg->GetConstantArray<AZ::Vector4>(RHI::ShaderInputConstantIndex(13));
AZ_TEST_STOP_ASSERTTEST(1);
}
}
TEST_F(ShaderResourceGroupConstantBufferTests, TestErrorReporting_SetConstant_WrongNumberOfElements_SingleInput)
{
using namespace AZ;
{
AZ_TEST_START_ASSERTTEST;
// MyBool2
EXPECT_FALSE(m_srg->SetConstant<bool>(RHI::ShaderInputConstantIndex(1), false));
AZ_TEST_STOP_ASSERTTEST(1);
}
}
TEST_F(ShaderResourceGroupConstantBufferTests, TestErrorReporting_GetConstant_WrongNumberOfElements_SingleOutput)
{
using namespace AZ;
{
AZ_TEST_START_ASSERTTEST;
// MyBool3
EXPECT_FALSE(m_srg->GetConstant<bool>(RHI::ShaderInputConstantIndex(2)));
AZ_TEST_STOP_ASSERTTEST(1);
}
}
}