You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/Atom/RHI/Code/Tests/InputStreamLayoutBuilderTes...

294 lines
11 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 "RHITestFixture.h"
#include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
#include <AzCore/Name/NameDictionary.h>
namespace UnitTest
{
using namespace AZ;
using namespace RHI;
class InputStreamLayoutBuilderTests
: public RHITestFixture
{
protected:
void ExpectEq(AZStd::array_view<StreamBufferDescriptor> expected, AZStd::array_view<StreamBufferDescriptor> actual)
{
EXPECT_EQ(expected.size(), actual.size());
for (int i = 0; i < expected.size() && i < actual.size(); ++i)
{
EXPECT_EQ(expected[i].m_stepRate, actual[i].m_stepRate);
EXPECT_EQ(expected[i].m_stepFunction, actual[i].m_stepFunction);
EXPECT_EQ(expected[i].m_byteStride, actual[i].m_byteStride);
}
}
void ExpectEq(AZStd::array_view<StreamChannelDescriptor> expected, AZStd::array_view<StreamChannelDescriptor> actual)
{
EXPECT_EQ(expected.size(), actual.size());
for (int i = 0; i < expected.size() && i < actual.size(); ++i)
{
EXPECT_EQ(expected[i].m_bufferIndex, actual[i].m_bufferIndex);
EXPECT_EQ(expected[i].m_byteOffset, actual[i].m_byteOffset);
EXPECT_EQ(expected[i].m_format, actual[i].m_format);
EXPECT_EQ(expected[i].m_semantic, actual[i].m_semantic);
}
}
void ExpectEq(const InputStreamLayout& expected, const InputStreamLayout& actual)
{
EXPECT_EQ(expected.IsFinalized(), actual.IsFinalized());
EXPECT_EQ(expected.GetTopology(), actual.GetTopology());
ExpectEq(expected.GetStreamBuffers(), actual.GetStreamBuffers());
ExpectEq(expected.GetStreamChannels(), actual.GetStreamChannels());
}
};
TEST_F(InputStreamLayoutBuilderTests, TestDefault)
{
InputStreamLayout expected;
expected.SetTopology(PrimitiveTopology::TriangleList);
expected.Finalize();
InputStreamLayout actual = InputStreamLayoutBuilder().End();
ExpectEq(expected, actual);
}
TEST_F(InputStreamLayoutBuilderTests, TestInterleavedBuffer)
{
InputStreamLayout expected;
{
expected.SetTopology(RHI::PrimitiveTopology::TriangleList);
RHI::StreamChannelDescriptor positionDescriptor;
positionDescriptor.m_bufferIndex = 0;
positionDescriptor.m_byteOffset = 0;
positionDescriptor.m_format = RHI::Format::R32G32_FLOAT;
positionDescriptor.m_semantic.m_name = Name{ "POSITION" };
expected.AddStreamChannel(positionDescriptor);
RHI::StreamChannelDescriptor uvDescriptor;
uvDescriptor.m_bufferIndex = 0;
uvDescriptor.m_byteOffset = sizeof(float) * 2;
uvDescriptor.m_format = RHI::Format::R32G32_FLOAT;
uvDescriptor.m_semantic.m_name = Name{ "UV" };
expected.AddStreamChannel(uvDescriptor);
RHI::StreamChannelDescriptor colorDescriptor;
colorDescriptor.m_bufferIndex = 0;
colorDescriptor.m_byteOffset = sizeof(float) * 4;
colorDescriptor.m_format = RHI::Format::R8G8B8A8_UNORM;
colorDescriptor.m_semantic.m_name = Name{ "COLOR" };
expected.AddStreamChannel(colorDescriptor);
RHI::StreamBufferDescriptor bufferDescriptor;
bufferDescriptor.m_byteStride = sizeof(float) * 4 + 4;
expected.AddStreamBuffer(bufferDescriptor);
expected.Finalize();
}
InputStreamLayout actual;
{
RHI::InputStreamLayoutBuilder layoutBuilder;
layoutBuilder.AddBuffer()
->Channel("POSITION", RHI::Format::R32G32_FLOAT)
->Channel("UV", RHI::Format::R32G32_FLOAT)
->Channel("COLOR", RHI::Format::R8G8B8A8_UNORM);
actual = layoutBuilder.End();
}
ExpectEq(expected, actual);
}
TEST_F(InputStreamLayoutBuilderTests, TestIndependentBuffers)
{
InputStreamLayout expected;
{
expected.SetTopology(RHI::PrimitiveTopology::TriangleList);
RHI::StreamChannelDescriptor positionDescriptor;
positionDescriptor.m_bufferIndex = 0;
positionDescriptor.m_byteOffset = 0;
positionDescriptor.m_format = RHI::Format::R32G32B32_FLOAT;
positionDescriptor.m_semantic.m_name = Name{ "POSITION" };
expected.AddStreamChannel(positionDescriptor);
RHI::StreamChannelDescriptor colorDescriptor;
colorDescriptor.m_bufferIndex = 1;
colorDescriptor.m_byteOffset = 0;
colorDescriptor.m_format = RHI::Format::R32G32B32A32_FLOAT;
colorDescriptor.m_semantic.m_name = Name{ "COLOR" };
expected.AddStreamChannel(colorDescriptor);
RHI::StreamChannelDescriptor uvDescriptor;
uvDescriptor.m_bufferIndex = 2;
uvDescriptor.m_byteOffset = 0;
uvDescriptor.m_format = RHI::Format::R32G32_FLOAT;
uvDescriptor.m_semantic.m_name = Name{ "UV" };
expected.AddStreamChannel(uvDescriptor);
RHI::StreamBufferDescriptor bufferDescriptor;
bufferDescriptor.m_byteStride = 3 * sizeof(float);
expected.AddStreamBuffer(bufferDescriptor);
bufferDescriptor.m_byteStride = 4 * sizeof(float);
expected.AddStreamBuffer(bufferDescriptor);
bufferDescriptor.m_byteStride = 2 * sizeof(float);
expected.AddStreamBuffer(bufferDescriptor);
expected.Finalize();
}
InputStreamLayout actual;
{
RHI::InputStreamLayoutBuilder layoutBuilder;
layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
layoutBuilder.AddBuffer()->Channel("COLOR", RHI::Format::R32G32B32A32_FLOAT);
layoutBuilder.AddBuffer()->Channel("UV", RHI::Format::R32G32_FLOAT);
actual = layoutBuilder.End();
}
ExpectEq(expected, actual);
}
TEST_F(InputStreamLayoutBuilderTests, TestMultipleInterleavedBuffersWithPadding)
{
InputStreamLayout expected;
{
expected.SetTopology(RHI::PrimitiveTopology::TriangleList);
// Buffer 0 ...
RHI::StreamChannelDescriptor positionDescriptor;
positionDescriptor.m_bufferIndex = 0;
positionDescriptor.m_byteOffset = 0;
positionDescriptor.m_format = RHI::Format::R32G32B32_FLOAT;
positionDescriptor.m_semantic.m_name = Name{ "POSITION" };
expected.AddStreamChannel(positionDescriptor);
RHI::StreamChannelDescriptor colorDescriptor;
colorDescriptor.m_bufferIndex = 0;
colorDescriptor.m_byteOffset = sizeof(float) * 4; // Includes 4 bytes of padding between channels
colorDescriptor.m_format = RHI::Format::R32G32B32A32_FLOAT;
colorDescriptor.m_semantic.m_name = Name{ "COLOR" };
expected.AddStreamChannel(colorDescriptor);
RHI::StreamBufferDescriptor bufferDescriptor;
bufferDescriptor.m_byteStride = 8 * sizeof(float);
expected.AddStreamBuffer(bufferDescriptor);
// Buffer 1 ...
RHI::StreamChannelDescriptor uvDescriptor;
uvDescriptor.m_bufferIndex = 1;
uvDescriptor.m_byteOffset = 0;
uvDescriptor.m_format = RHI::Format::R32G32_FLOAT;
uvDescriptor.m_semantic = ShaderSemantic{ "UV", 0 };
expected.AddStreamChannel(uvDescriptor);
uvDescriptor.m_byteOffset = sizeof(float) * 2;
uvDescriptor.m_format = RHI::Format::R32G32_FLOAT;
uvDescriptor.m_semantic = ShaderSemantic{ "UV", 1 };
expected.AddStreamChannel(uvDescriptor);
// UV1 is present in the buffer but not used for this shader
uvDescriptor.m_byteOffset = sizeof(float) * 6;
uvDescriptor.m_format = RHI::Format::R32G32_FLOAT;
uvDescriptor.m_semantic = ShaderSemantic{ "UV", 3 };
expected.AddStreamChannel(uvDescriptor);
uvDescriptor.m_byteOffset = sizeof(float) * 8;
uvDescriptor.m_format = RHI::Format::R32G32_FLOAT;
uvDescriptor.m_semantic = ShaderSemantic{ "UV", 4 };
expected.AddStreamChannel(uvDescriptor);
bufferDescriptor.m_byteStride = 10 * sizeof(float);
expected.AddStreamBuffer(bufferDescriptor);
expected.Finalize();
}
InputStreamLayout actual;
{
RHI::InputStreamLayoutBuilder layoutBuilder;
layoutBuilder.AddBuffer()
->Channel("POSITION", RHI::Format::R32G32B32_FLOAT)
->Padding(sizeof(float))
->Channel("COLOR", RHI::Format::R32G32B32A32_FLOAT);
layoutBuilder.AddBuffer()
->Channel("UV0", RHI::Format::R32G32_FLOAT)
->Channel("UV1", RHI::Format::R32G32_FLOAT)
->Padding(sizeof(float) * 2)
->Channel("UV3", RHI::Format::R32G32_FLOAT)
->Channel("UV4", RHI::Format::R32G32_FLOAT);
actual = layoutBuilder.End();
}
ExpectEq(expected, actual);
}
TEST_F(InputStreamLayoutBuilderTests, TestTooManyBuffers)
{
const uint32_t maxBuffers = RHI::Limits::Pipeline::StreamCountMax;
// The expected layout will have exactly the max number of buffers, which demonstrates that
// InputStreamLayoutBuilder attempts to recover from the error.
InputStreamLayout expected;
{
expected.SetTopology(RHI::PrimitiveTopology::TriangleList);
for (uint32_t i = 0; i < maxBuffers; ++i)
{
RHI::StreamChannelDescriptor positionDescriptor;
positionDescriptor.m_bufferIndex = i;
positionDescriptor.m_byteOffset = 0;
positionDescriptor.m_format = RHI::Format::R32G32_FLOAT;
positionDescriptor.m_semantic = ShaderSemantic{ "UV", i };
expected.AddStreamChannel(positionDescriptor);
RHI::StreamBufferDescriptor bufferDescriptor;
bufferDescriptor.m_byteStride = 2 * sizeof(float);
expected.AddStreamBuffer(bufferDescriptor);
}
expected.Finalize();
}
InputStreamLayout actual;
{
RHI::InputStreamLayoutBuilder layoutBuilder;
for (uint32_t i = 0; i < maxBuffers; ++i)
{
layoutBuilder.AddBuffer()->Channel(ShaderSemantic{ "UV", i }, RHI::Format::R32G32_FLOAT);
}
AZ_TEST_START_ASSERTTEST;
// Registering a channel on the failed buffer should not crash, is ignored.
layoutBuilder.AddBuffer()->Channel(ShaderSemantic{ "UV", maxBuffers }, RHI::Format::R32G32_FLOAT);
AZ_TEST_STOP_ASSERTTEST(1);
actual = layoutBuilder.End();
}
ExpectEq(expected, actual);
}
}