Path: blob/main_old/src/tests/gl_tests/AttributeLayoutTest.cpp
1693 views
//1// Copyright 2018 The ANGLE Project Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4//5// AttributeLayoutTest:6// Test various layouts of vertex attribute data:7// - in memory, in buffer object, or combination of both8// - sequential or interleaved9// - various combinations of data types1011#include <vector>1213#include "test_utils/ANGLETest.h"14#include "test_utils/gl_raii.h"1516using namespace angle;1718namespace19{2021// Test will draw these four triangles.22// clang-format off23constexpr double kTriangleData[] = {24// xy rgb250,0, 1,1,0,26-1,+1, 1,1,0,27+1,+1, 1,1,0,28290,0, 0,1,0,30+1,+1, 0,1,0,31+1,-1, 0,1,0,32330,0, 0,1,1,34+1,-1, 0,1,1,35-1,-1, 0,1,1,36370,0, 1,0,1,38-1,-1, 1,0,1,39-1,+1, 1,0,1,40};41// clang-format on4243constexpr size_t kNumVertices = ArraySize(kTriangleData) / 5;4445// Vertex data source description.46class VertexData47{48public:49VertexData(int dimension, const double *data, unsigned offset, unsigned stride)50: mDimension(dimension), mData(data), mOffset(offset), mStride(stride)51{}52int getDimension() const { return mDimension; }53double getValue(unsigned vertexNumber, int component) const54{55return mData[mOffset + mStride * vertexNumber + component];56}5758private:59int mDimension;60const double *mData;61// offset and stride in doubles62unsigned mOffset;63unsigned mStride;64};6566// A container for one or more vertex attributes.67class Container68{69public:70static constexpr size_t kSize = 1024;7172void open(void) { memset(mMemory, 0xff, kSize); }73void *getDestination(size_t offset) { return mMemory + offset; }74virtual void close(void) {}75virtual ~Container() {}76virtual const char *getAddress() = 0;77virtual GLuint getBuffer() = 0;7879protected:80char mMemory[kSize];81};8283// Vertex attribute data in client memory.84class Memory : public Container85{86public:87const char *getAddress() override { return mMemory; }88GLuint getBuffer() override { return 0; }89};9091// Vertex attribute data in buffer object.92class Buffer : public Container93{94public:95void close(void) override96{97glBindBuffer(GL_ARRAY_BUFFER, mBuffer);98glBufferData(GL_ARRAY_BUFFER, sizeof(mMemory), mMemory, GL_STATIC_DRAW);99}100101const char *getAddress() override { return nullptr; }102GLuint getBuffer() override { return mBuffer; }103104protected:105GLBuffer mBuffer;106};107108// Encapsulate the storage, layout, format and data of a vertex attribute.109struct Attrib110{111void openContainer(void) const { mContainer->open(); }112113void fillContainer(void) const114{115for (unsigned i = 0; i < kNumVertices; ++i)116{117for (int j = 0; j < mData.getDimension(); ++j)118{119size_t destOffset = mOffset + mStride * i + mCTypeSize * j;120if (destOffset + mCTypeSize > Container::kSize)121FAIL() << "test case does not fit container";122123double value = mData.getValue(i, j);124if (mGLType == GL_FIXED)125value *= 1 << 16;126else if (mNormalized)127{128if (value < mMinIn || value > mMaxIn)129FAIL() << "test data does not fit format";130value = (value - mMinIn) * mScale + mMinOut;131}132133mStore(value, mContainer->getDestination(destOffset));134}135}136}137138void closeContainer(void) const { mContainer->close(); }139140void enable(unsigned index) const141{142glBindBuffer(GL_ARRAY_BUFFER, mContainer->getBuffer());143glVertexAttribPointer(index, mData.getDimension(), mGLType, mNormalized, mStride,144mContainer->getAddress() + mOffset);145EXPECT_GL_NO_ERROR();146glEnableVertexAttribArray(index);147}148149bool inClientMemory(void) const { return mContainer->getAddress() != nullptr; }150151std::shared_ptr<Container> mContainer;152unsigned mOffset;153unsigned mStride;154const VertexData &mData;155void (*mStore)(double value, void *dest);156GLenum mGLType;157GLboolean mNormalized;158size_t mCTypeSize;159double mMinIn;160double mMaxIn;161double mMinOut;162double mScale;163};164165// Change type and store.166template <class T>167void Store(double value, void *dest)168{169T v = static_cast<T>(value);170memcpy(dest, &v, sizeof(v));171}172173// Function object that makes Attrib structs according to a vertex format.174template <class CType, GLenum GLType, bool Normalized>175class Format176{177static_assert(!(Normalized && GLType == GL_FLOAT), "Normalized float does not make sense.");178179public:180Format(bool es3) : mES3(es3) {}181182Attrib operator()(std::shared_ptr<Container> container,183unsigned offset,184unsigned stride,185const VertexData &data) const186{187double minIn = 0;188double maxIn = 1;189double minOut = std::numeric_limits<CType>::min();190double rangeOut = std::numeric_limits<CType>::max() - minOut;191192if (std::is_signed<CType>::value)193{194minIn = -1;195maxIn = +1;196if (mES3)197{198minOut += 1;199rangeOut -= 1;200}201}202203return {204container, offset, stride, data, Store<CType>, GLType,205Normalized, sizeof(CType), minIn, maxIn, minOut, rangeOut / (maxIn - minIn),206};207}208209protected:210const bool mES3;211};212213typedef std::vector<Attrib> TestCase;214215void PrepareTestCase(const TestCase &tc)216{217for (const Attrib &a : tc)218a.openContainer();219for (const Attrib &a : tc)220a.fillContainer();221for (const Attrib &a : tc)222a.closeContainer();223unsigned i = 0;224for (const Attrib &a : tc)225a.enable(i++);226}227228class AttributeLayoutTest : public ANGLETest229{230protected:231AttributeLayoutTest()232: mProgram(0), mCoord(2, kTriangleData, 0, 5), mColor(3, kTriangleData, 2, 5)233{234setWindowWidth(128);235setWindowHeight(128);236setConfigRedBits(8);237setConfigGreenBits(8);238setConfigBlueBits(8);239setConfigAlphaBits(8);240}241242void GetTestCases(void);243244void testSetUp() override245{246glClearColor(.2f, .2f, .2f, .0f);247glClear(GL_COLOR_BUFFER_BIT);248249glDisable(GL_DEPTH_TEST);250251constexpr char kVS[] =252"attribute mediump vec2 coord;\n"253"attribute mediump vec3 color;\n"254"varying mediump vec3 vcolor;\n"255"void main(void)\n"256"{\n"257" gl_Position = vec4(coord, 0, 1);\n"258" vcolor = color;\n"259"}\n";260261constexpr char kFS[] =262"varying mediump vec3 vcolor;\n"263"void main(void)\n"264"{\n"265" gl_FragColor = vec4(vcolor, 0);\n"266"}\n";267268mProgram = CompileProgram(kVS, kFS);269ASSERT_NE(0u, mProgram);270glUseProgram(mProgram);271272glGenBuffers(1, &mIndexBuffer);273274GetTestCases();275}276277void testTearDown() override278{279mTestCases.clear();280glDeleteProgram(mProgram);281glDeleteBuffers(1, &mIndexBuffer);282}283284virtual bool Skip(const TestCase &) { return false; }285virtual void Draw(int firstVertex, unsigned vertexCount, const GLushort *indices) = 0;286287void Run(bool drawFirstTriangle)288{289glViewport(0, 0, getWindowWidth(), getWindowHeight());290glUseProgram(mProgram);291292for (unsigned i = 0; i < mTestCases.size(); ++i)293{294if (mTestCases[i].size() == 0 || Skip(mTestCases[i]))295continue;296297PrepareTestCase(mTestCases[i]);298299glClear(GL_COLOR_BUFFER_BIT);300301std::string testCase;302if (drawFirstTriangle)303{304Draw(0, kNumVertices, mIndices);305testCase = "draw";306}307else308{309Draw(3, kNumVertices - 3, mIndices + 3);310testCase = "skip";311}312313testCase += " first triangle case ";314int w = getWindowWidth() / 4;315int h = getWindowHeight() / 4;316if (drawFirstTriangle)317{318EXPECT_PIXEL_EQ(w * 2, h * 3, 255, 255, 0, 0) << testCase << i;319}320else321{322EXPECT_PIXEL_EQ(w * 2, h * 3, 51, 51, 51, 0) << testCase << i;323}324EXPECT_PIXEL_EQ(w * 3, h * 2, 0, 255, 0, 0) << testCase << i;325EXPECT_PIXEL_EQ(w * 2, h * 1, 0, 255, 255, 0) << testCase << i;326EXPECT_PIXEL_EQ(w * 1, h * 2, 255, 0, 255, 0) << testCase << i;327}328}329330static const GLushort mIndices[kNumVertices];331332GLuint mProgram;333GLuint mIndexBuffer;334335std::vector<TestCase> mTestCases;336337VertexData mCoord;338VertexData mColor;339};340const GLushort AttributeLayoutTest::mIndices[kNumVertices] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};341342void AttributeLayoutTest::GetTestCases(void)343{344const bool es3 = getClientMajorVersion() >= 3;345346Format<GLfloat, GL_FLOAT, false> Float(es3);347Format<GLint, GL_FIXED, false> Fixed(es3);348349Format<GLbyte, GL_BYTE, false> SByte(es3);350Format<GLubyte, GL_UNSIGNED_BYTE, false> UByte(es3);351Format<GLshort, GL_SHORT, false> SShort(es3);352Format<GLushort, GL_UNSIGNED_SHORT, false> UShort(es3);353Format<GLint, GL_INT, false> SInt(es3);354Format<GLuint, GL_UNSIGNED_INT, false> UInt(es3);355356Format<GLbyte, GL_BYTE, true> NormSByte(es3);357Format<GLubyte, GL_UNSIGNED_BYTE, true> NormUByte(es3);358Format<GLshort, GL_SHORT, true> NormSShort(es3);359Format<GLushort, GL_UNSIGNED_SHORT, true> NormUShort(es3);360Format<GLint, GL_INT, true> NormSInt(es3);361Format<GLuint, GL_UNSIGNED_INT, true> NormUInt(es3);362363std::shared_ptr<Container> M0 = std::make_shared<Memory>();364std::shared_ptr<Container> M1 = std::make_shared<Memory>();365std::shared_ptr<Container> B0 = std::make_shared<Buffer>();366std::shared_ptr<Container> B1 = std::make_shared<Buffer>();367368// 0. two buffers369mTestCases.push_back({Float(B0, 0, 8, mCoord), Float(B1, 0, 12, mColor)});370371// 1. two memory372mTestCases.push_back({Float(M0, 0, 8, mCoord), Float(M1, 0, 12, mColor)});373374// 2. one memory, sequential375mTestCases.push_back({Float(M0, 0, 8, mCoord), Float(M0, 96, 12, mColor)});376377// 3. one memory, interleaved378mTestCases.push_back({Float(M0, 0, 20, mCoord), Float(M0, 8, 20, mColor)});379380// 4. buffer and memory381mTestCases.push_back({Float(B0, 0, 8, mCoord), Float(M0, 0, 12, mColor)});382383// 5. stride != size384mTestCases.push_back({Float(B0, 0, 16, mCoord), Float(B1, 0, 12, mColor)});385386// 6-7. same stride and format, switching data between memory and buffer387mTestCases.push_back({Float(M0, 0, 16, mCoord), Float(M1, 0, 12, mColor)});388mTestCases.push_back({Float(B0, 0, 16, mCoord), Float(B1, 0, 12, mColor)});389390// 8-9. same stride and format, offset change391mTestCases.push_back({Float(B0, 0, 8, mCoord), Float(B1, 0, 12, mColor)});392mTestCases.push_back({Float(B0, 3, 8, mCoord), Float(B1, 4, 12, mColor)});393394// 10-11. unaligned buffer data395mTestCases.push_back({Float(M0, 0, 8, mCoord), Float(B0, 1, 13, mColor)});396mTestCases.push_back({Float(M0, 0, 8, mCoord), Float(B1, 1, 13, mColor)});397398// 12-15. byte/short399mTestCases.push_back({SByte(M0, 0, 20, mCoord), UByte(M0, 10, 20, mColor)});400mTestCases.push_back({SShort(M0, 0, 20, mCoord), UShort(M0, 8, 20, mColor)});401mTestCases.push_back({NormSByte(M0, 0, 8, mCoord), NormUByte(M0, 4, 8, mColor)});402mTestCases.push_back({NormSShort(M0, 0, 20, mCoord), NormUShort(M0, 8, 20, mColor)});403404// 16. one buffer, sequential405mTestCases.push_back({Fixed(B0, 0, 8, mCoord), Float(B0, 96, 12, mColor)});406407// 17. one buffer, interleaved408mTestCases.push_back({Fixed(B0, 0, 20, mCoord), Float(B0, 8, 20, mColor)});409410// 18. memory and buffer, float and integer411mTestCases.push_back({Float(M0, 0, 8, mCoord), SByte(B0, 0, 12, mColor)});412413// 19. buffer and memory, unusual offset and stride414mTestCases.push_back({Float(B0, 11, 13, mCoord), Float(M0, 23, 17, mColor)});415416// 20-21. remaining ES3 formats417if (es3)418{419mTestCases.push_back({SInt(M0, 0, 40, mCoord), UInt(M0, 16, 40, mColor)});420// Fails on Nexus devices (anglebug.com/2641)421if (!IsNexus5X())422mTestCases.push_back({NormSInt(M0, 0, 40, mCoord), NormUInt(M0, 16, 40, mColor)});423}424}425426class AttributeLayoutNonIndexed : public AttributeLayoutTest427{428void Draw(int firstVertex, unsigned vertexCount, const GLushort *indices) override429{430glDrawArrays(GL_TRIANGLES, firstVertex, vertexCount);431}432};433434class AttributeLayoutMemoryIndexed : public AttributeLayoutTest435{436void Draw(int firstVertex, unsigned vertexCount, const GLushort *indices) override437{438glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);439glDrawElements(GL_TRIANGLES, vertexCount, GL_UNSIGNED_SHORT, indices);440}441};442443class AttributeLayoutBufferIndexed : public AttributeLayoutTest444{445void Draw(int firstVertex, unsigned vertexCount, const GLushort *indices) override446{447glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);448glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(*mIndices) * vertexCount, indices,449GL_STATIC_DRAW);450glDrawElements(GL_TRIANGLES, vertexCount, GL_UNSIGNED_SHORT, nullptr);451}452};453454TEST_P(AttributeLayoutNonIndexed, Test)455{456Run(true);457ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsOpenGL());458Run(false);459}460461TEST_P(AttributeLayoutMemoryIndexed, Test)462{463Run(true);464ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && (IsOpenGL() || IsD3D11_FL93()));465Run(false);466}467468TEST_P(AttributeLayoutBufferIndexed, Test)469{470Run(true);471ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && (IsOpenGL() || IsD3D11_FL93()));472Run(false);473}474475ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(AttributeLayoutNonIndexed);476ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(AttributeLayoutMemoryIndexed);477ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(AttributeLayoutBufferIndexed);478479} // anonymous namespace480481482