Commit c950a572 by Frank Henigman Committed by Commit Bot

Vertex format support in AttributeLayoutTest.

Make it easy to test with different vertex data formats. Add test cases for all formats. BUG=angleproject:2405 Change-Id: I147643a0912e97588588e67946ac326ed449cf07 Reviewed-on: https://chromium-review.googlesource.com/1053564 Commit-Queue: Frank Henigman <fjhenigman@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent a4a3c19a
...@@ -6,10 +6,9 @@ ...@@ -6,10 +6,9 @@
// AttributeLayoutTest: // AttributeLayoutTest:
// Test various layouts of vertex attribute data: // Test various layouts of vertex attribute data:
// - in memory, in buffer object, or combination of both // - in memory, in buffer object, or combination of both
// - float, integer, or combination of both
// - sequential or interleaved // - sequential or interleaved
// - various combinations of data types
#include <array>
#include <vector> #include <vector>
#include "test_utils/ANGLETest.h" #include "test_utils/ANGLETest.h"
...@@ -22,7 +21,7 @@ namespace ...@@ -22,7 +21,7 @@ namespace
// Test will draw these four triangles. // Test will draw these four triangles.
// clang-format off // clang-format off
const GLfloat triangleData[] = { constexpr double kTriangleData[] = {
// xy rgb // xy rgb
0,0, 1,1,0, 0,0, 1,1,0,
-1,+1, 1,1,0, -1,+1, 1,1,0,
...@@ -42,7 +41,29 @@ const GLfloat triangleData[] = { ...@@ -42,7 +41,29 @@ const GLfloat triangleData[] = {
}; };
// clang-format on // clang-format on
constexpr size_t kNumVertices = ArraySize(triangleData) / 5; constexpr size_t kNumVertices = ArraySize(kTriangleData) / 5;
// Vertex data source description.
class VertexData
{
public:
VertexData(int dimension, const double *data, unsigned offset, unsigned stride)
: mDimension(dimension), mData(data), mOffset(offset), mStride(stride)
{
}
int getDimension() const { return mDimension; }
double getValue(unsigned vertexNumber, int component) const
{
return mData[mOffset + mStride * vertexNumber + component];
}
private:
int mDimension;
const double *mData;
// offset and stride in doubles
unsigned mOffset;
unsigned mStride;
};
// A container for one or more vertex attributes. // A container for one or more vertex attributes.
class Container class Container
...@@ -51,18 +72,7 @@ class Container ...@@ -51,18 +72,7 @@ class Container
static constexpr size_t kSize = 1024; static constexpr size_t kSize = 1024;
void open(void) { memset(mMemory, 0xff, kSize); } void open(void) { memset(mMemory, 0xff, kSize); }
void *getDestination(size_t offset) { return mMemory + offset; }
void fill(size_t numItem, size_t itemSize, const char *src, unsigned offset, unsigned stride)
{
while (numItem--)
{
ASSERT(offset + itemSize <= kSize);
memcpy(mMemory + offset, src, itemSize);
src += itemSize;
offset += stride;
}
}
virtual void close(void) {} virtual void close(void) {}
virtual ~Container() {} virtual ~Container() {}
virtual const char *getAddress() = 0; virtual const char *getAddress() = 0;
...@@ -97,62 +107,109 @@ class Buffer : public Container ...@@ -97,62 +107,109 @@ class Buffer : public Container
GLBuffer mBuffer; GLBuffer mBuffer;
}; };
// clang-format off // Encapsulate the storage, layout, format and data of a vertex attribute.
template<class Type> struct GLType {}; struct Attrib
template<> struct GLType<GLbyte > { static constexpr GLenum kGLType = GL_BYTE; };
template<> struct GLType<GLubyte > { static constexpr GLenum kGLType = GL_UNSIGNED_BYTE; };
template<> struct GLType<GLshort > { static constexpr GLenum kGLType = GL_SHORT; };
template<> struct GLType<GLushort> { static constexpr GLenum kGLType = GL_UNSIGNED_SHORT; };
template<> struct GLType<GLfloat > { static constexpr GLenum kGLType = GL_FLOAT; };
// clang-format on
// Encapsulates the data for one vertex attribute, where it lives, and how it is layed out.
class Attrib
{ {
public:
template <class T>
Attrib(std::shared_ptr<Container> container, unsigned offset, unsigned stride, const T &data)
: mContainer(container),
mOffset(offset),
mStride(stride),
mData(reinterpret_cast<const char *>(data.data())),
mDimension(data.size() / kNumVertices),
mAttribSize(mDimension * sizeof(typename T::value_type)),
mGLType(GLType<typename T::value_type>::kGLType)
{
// Compiler complains about unused variable without these.
(void)GLType<GLbyte>::kGLType;
(void)GLType<GLubyte>::kGLType;
(void)GLType<GLshort>::kGLType;
(void)GLType<GLushort>::kGLType;
(void)GLType<GLfloat>::kGLType;
}
void openContainer(void) const { mContainer->open(); } void openContainer(void) const { mContainer->open(); }
void fillContainer(void) const void fillContainer(void) const
{ {
mContainer->fill(kNumVertices, mAttribSize, mData, mOffset, mStride); for (unsigned i = 0; i < kNumVertices; ++i)
{
for (int j = 0; j < mData.getDimension(); ++j)
{
size_t destOffset = mOffset + mStride * i + mCTypeSize * j;
if (destOffset + mCTypeSize > Container::kSize)
FAIL() << "test case does not fit container";
double value = mData.getValue(i, j);
if (mGLType == GL_FIXED)
value *= 1 << 16;
else if (mNormalized)
{
if (value < mMinIn || value > mMaxIn)
FAIL() << "test data does not fit format";
value = (value - mMinIn) * mScale + mMinOut;
}
mStore(value, mContainer->getDestination(destOffset));
}
}
} }
void closeContainer(void) const { mContainer->close(); } void closeContainer(void) const { mContainer->close(); }
void enable(unsigned index) const void enable(unsigned index) const
{ {
glBindBuffer(GL_ARRAY_BUFFER, mContainer->getBuffer()); glBindBuffer(GL_ARRAY_BUFFER, mContainer->getBuffer());
glVertexAttribPointer(index, static_cast<int>(mDimension), mGLType, GL_FALSE, mStride, glVertexAttribPointer(index, mData.getDimension(), mGLType, mNormalized, mStride,
mContainer->getAddress() + mOffset); mContainer->getAddress() + mOffset);
EXPECT_GL_NO_ERROR();
glEnableVertexAttribArray(index); glEnableVertexAttribArray(index);
} }
bool inClientMemory(void) const { return mContainer->getAddress() != nullptr; } bool inClientMemory(void) const { return mContainer->getAddress() != nullptr; }
protected:
std::shared_ptr<Container> mContainer; std::shared_ptr<Container> mContainer;
unsigned mOffset; unsigned mOffset;
unsigned mStride; unsigned mStride;
const char *mData; const VertexData &mData;
size_t mDimension; void (*mStore)(double value, void *dest);
size_t mAttribSize;
GLenum mGLType; GLenum mGLType;
GLboolean mNormalized;
size_t mCTypeSize;
double mMinIn;
double mMaxIn;
double mMinOut;
double mScale;
};
// Change type and store.
template <class T>
void Store(double value, void *dest)
{
T v = value;
memcpy(dest, &v, sizeof(v));
}
// Function object that makes Attrib structs according to a vertex format.
template <class CType, GLenum GLType, bool Normalized>
class Format
{
static_assert(!(Normalized && GLType == GL_FLOAT), "Normalized float does not make sense.");
public:
Format(bool es3) : mES3(es3) {}
Attrib operator()(std::shared_ptr<Container> container,
unsigned offset,
unsigned stride,
const VertexData &data) const
{
double minIn = 0;
double maxIn = 1;
double minOut = std::numeric_limits<CType>::min();
double rangeOut = std::numeric_limits<CType>::max() - minOut;
if (std::is_signed<CType>::value)
{
minIn = -1;
maxIn = +1;
if (mES3)
{
minOut += 1;
rangeOut -= 1;
}
}
return {
container, offset, stride, data, Store<CType>, GLType,
Normalized, sizeof(CType), minIn, maxIn, minOut, rangeOut / (maxIn - minIn),
};
}
protected:
const bool mES3;
}; };
typedef std::vector<Attrib> TestCase; typedef std::vector<Attrib> TestCase;
...@@ -160,31 +217,21 @@ typedef std::vector<Attrib> TestCase; ...@@ -160,31 +217,21 @@ typedef std::vector<Attrib> TestCase;
void PrepareTestCase(const TestCase &tc) void PrepareTestCase(const TestCase &tc)
{ {
for (const Attrib &a : tc) for (const Attrib &a : tc)
{
a.openContainer(); a.openContainer();
}
for (const Attrib &a : tc) for (const Attrib &a : tc)
{
a.fillContainer(); a.fillContainer();
}
for (const Attrib &a : tc) for (const Attrib &a : tc)
{
a.closeContainer(); a.closeContainer();
}
unsigned i = 0; unsigned i = 0;
for (const Attrib &a : tc) for (const Attrib &a : tc)
{
a.enable(i++); a.enable(i++);
}
} }
template <class Type, size_t Dimension>
using VertexData = std::array<Type, Dimension * kNumVertices>;
class AttributeLayoutTest : public ANGLETest class AttributeLayoutTest : public ANGLETest
{ {
protected: protected:
AttributeLayoutTest() : mProgram(0) AttributeLayoutTest()
: mProgram(0), mCoord(2, kTriangleData, 0, 5), mColor(3, kTriangleData, 2, 5)
{ {
setWindowWidth(128); setWindowWidth(128);
setWindowHeight(128); setWindowHeight(128);
...@@ -194,7 +241,6 @@ class AttributeLayoutTest : public ANGLETest ...@@ -194,7 +241,6 @@ class AttributeLayoutTest : public ANGLETest
setConfigAlphaBits(8); setConfigAlphaBits(8);
} }
void PrepareVertexData(void);
void GetTestCases(void); void GetTestCases(void);
void SetUp() override void SetUp() override
...@@ -229,7 +275,6 @@ class AttributeLayoutTest : public ANGLETest ...@@ -229,7 +275,6 @@ class AttributeLayoutTest : public ANGLETest
glGenBuffers(1, &mIndexBuffer); glGenBuffers(1, &mIndexBuffer);
PrepareVertexData();
GetTestCases(); GetTestCases();
} }
...@@ -294,63 +339,54 @@ class AttributeLayoutTest : public ANGLETest ...@@ -294,63 +339,54 @@ class AttributeLayoutTest : public ANGLETest
std::vector<TestCase> mTestCases; std::vector<TestCase> mTestCases;
VertexData<GLfloat, 2> mCoord; VertexData mCoord;
VertexData<GLfloat, 3> mColor; VertexData mColor;
VertexData<GLbyte, 3> mBColor;
}; };
const GLushort AttributeLayoutTest::mIndices[kNumVertices] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; const GLushort AttributeLayoutTest::mIndices[kNumVertices] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
void AttributeLayoutTest::PrepareVertexData(void) void AttributeLayoutTest::GetTestCases(void)
{ {
mCoord.fill(0); const bool es3 = getClientMajorVersion() >= 3;
mColor.fill(0);
mBColor.fill(0);
for (unsigned i = 0; i < kNumVertices; ++i) Format<GLfloat, GL_FLOAT, false> Float(es3);
{ Format<GLint, GL_FIXED, false> Fixed(es3);
GLfloat x = triangleData[i * 5 + 0];
GLfloat y = triangleData[i * 5 + 1]; Format<GLbyte, GL_BYTE, false> SByte(es3);
GLfloat r = triangleData[i * 5 + 2]; Format<GLubyte, GL_UNSIGNED_BYTE, false> UByte(es3);
GLfloat g = triangleData[i * 5 + 3]; Format<GLshort, GL_SHORT, false> SShort(es3);
GLfloat b = triangleData[i * 5 + 4]; Format<GLushort, GL_UNSIGNED_SHORT, false> UShort(es3);
Format<GLint, GL_INT, false> SInt(es3);
mCoord[i * 2 + 0] = x; Format<GLuint, GL_UNSIGNED_INT, false> UInt(es3);
mCoord[i * 2 + 1] = y;
Format<GLbyte, GL_BYTE, true> NormSByte(es3);
mColor[i * 3 + 0] = r; Format<GLubyte, GL_UNSIGNED_BYTE, true> NormUByte(es3);
mColor[i * 3 + 1] = g; Format<GLshort, GL_SHORT, true> NormSShort(es3);
mColor[i * 3 + 2] = b; Format<GLushort, GL_UNSIGNED_SHORT, true> NormUShort(es3);
Format<GLint, GL_INT, true> NormSInt(es3);
mBColor[i * 3 + 0] = r; Format<GLuint, GL_UNSIGNED_INT, true> NormUInt(es3);
mBColor[i * 3 + 1] = g;
mBColor[i * 3 + 2] = b;
}
}
void AttributeLayoutTest::GetTestCases(void)
{
std::shared_ptr<Container> M0 = std::make_shared<Memory>(); std::shared_ptr<Container> M0 = std::make_shared<Memory>();
std::shared_ptr<Container> M1 = std::make_shared<Memory>(); std::shared_ptr<Container> M1 = std::make_shared<Memory>();
std::shared_ptr<Container> B0 = std::make_shared<Buffer>(); std::shared_ptr<Container> B0 = std::make_shared<Buffer>();
std::shared_ptr<Container> B1 = std::make_shared<Buffer>(); std::shared_ptr<Container> B1 = std::make_shared<Buffer>();
// 0. two buffers // 0. two buffers
mTestCases.push_back({Attrib(B0, 0, 8, mCoord), Attrib(B1, 0, 12, mColor)}); mTestCases.push_back({Float(B0, 0, 8, mCoord), Float(B1, 0, 12, mColor)});
// 1. two memory // 1. two memory
mTestCases.push_back({Attrib(M0, 0, 8, mCoord), Attrib(M1, 0, 12, mColor)}); mTestCases.push_back({Float(M0, 0, 8, mCoord), Float(M1, 0, 12, mColor)});
// 2. one memory, sequential // 2. one memory, sequential
mTestCases.push_back({Attrib(M0, 0, 8, mCoord), Attrib(M0, 96, 12, mColor)}); mTestCases.push_back({Float(M0, 0, 8, mCoord), Float(M0, 96, 12, mColor)});
// 3. one memory, interleaved // 3. one memory, interleaved
mTestCases.push_back({Attrib(M0, 0, 20, mCoord), Attrib(M0, 8, 20, mColor)}); mTestCases.push_back({Float(M0, 0, 20, mCoord), Float(M0, 8, 20, mColor)});
// 4. buffer and memory // 4. buffer and memory
mTestCases.push_back({Attrib(B0, 0, 8, mCoord), Attrib(M0, 0, 12, mColor)}); mTestCases.push_back({Float(B0, 0, 8, mCoord), Float(M0, 0, 12, mColor)});
// 5. stride != size // 5. stride != size
mTestCases.push_back({Attrib(B0, 0, 16, mCoord), Attrib(B1, 0, 12, mColor)}); mTestCases.push_back({Float(B0, 0, 16, mCoord), Float(B1, 0, 12, mColor)});
if (IsVulkan()) if (IsVulkan())
{ {
...@@ -359,16 +395,31 @@ void AttributeLayoutTest::GetTestCases(void) ...@@ -359,16 +395,31 @@ void AttributeLayoutTest::GetTestCases(void)
} }
// 6. one buffer, sequential // 6. one buffer, sequential
mTestCases.push_back({Attrib(B0, 0, 8, mCoord), Attrib(B0, 96, 12, mColor)}); mTestCases.push_back({Float(B0, 0, 8, mCoord), Float(B0, 96, 12, mColor)});
// 7. one buffer, interleaved // 7. one buffer, interleaved
mTestCases.push_back({Attrib(B0, 0, 20, mCoord), Attrib(B0, 8, 20, mColor)}); mTestCases.push_back({Float(B0, 0, 20, mCoord), Float(B0, 8, 20, mColor)});
// 8. memory and buffer, float and integer // 8. memory and buffer, float and integer
mTestCases.push_back({Attrib(M0, 0, 8, mCoord), Attrib(B0, 0, 12, mBColor)}); mTestCases.push_back({Float(M0, 0, 8, mCoord), SByte(B0, 0, 12, mColor)});
// 9. buffer and memory, unusual offset and stride // 9. buffer and memory, unusual offset and stride
mTestCases.push_back({Attrib(B0, 11, 13, mCoord), Attrib(M0, 23, 17, mColor)}); mTestCases.push_back({Float(B0, 11, 13, mCoord), Float(M0, 23, 17, mColor)});
// 10-13. remaining ES2 types
mTestCases.push_back({Fixed(M0, 0, 20, mCoord), UByte(M0, 16, 20, mColor)});
mTestCases.push_back({SShort(M0, 0, 20, mCoord), UShort(M0, 8, 20, mColor)});
mTestCases.push_back({NormSByte(M0, 0, 8, mCoord), NormUByte(M0, 4, 8, mColor)});
mTestCases.push_back({NormSShort(M0, 0, 20, mCoord), NormUShort(M0, 8, 20, mColor)});
// 14-15. remaining ES3 types
if (es3)
{
mTestCases.push_back({SInt(M0, 0, 40, mCoord), UInt(M0, 16, 40, mColor)});
if (!IsAndroid())
mTestCases.push_back(
{NormSInt(M0, 0, 40, mCoord), NormUInt(M0, 16, 40, mColor)}); // anglebug.com/2641
}
} }
class AttributeLayoutNonIndexed : public AttributeLayoutTest class AttributeLayoutNonIndexed : public AttributeLayoutTest
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment