diff options
Diffstat (limited to 'gfx/angle/src/tests/gl_tests/VertexAttributeTest.cpp')
-rwxr-xr-x | gfx/angle/src/tests/gl_tests/VertexAttributeTest.cpp | 894 |
1 files changed, 894 insertions, 0 deletions
diff --git a/gfx/angle/src/tests/gl_tests/VertexAttributeTest.cpp b/gfx/angle/src/tests/gl_tests/VertexAttributeTest.cpp new file mode 100755 index 000000000..b1b9dcd1f --- /dev/null +++ b/gfx/angle/src/tests/gl_tests/VertexAttributeTest.cpp @@ -0,0 +1,894 @@ +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// + +#include "test_utils/ANGLETest.h" + +using namespace angle; + +namespace +{ + +GLsizei TypeStride(GLenum attribType) +{ + switch (attribType) + { + case GL_UNSIGNED_BYTE: + case GL_BYTE: + return 1; + case GL_UNSIGNED_SHORT: + case GL_SHORT: + return 2; + case GL_UNSIGNED_INT: + case GL_INT: + case GL_FLOAT: + return 4; + default: + UNREACHABLE(); + return 0; + } +} + +template <typename T> +GLfloat Normalize(T value) +{ + static_assert(std::is_integral<T>::value, "Integer required."); + if (std::is_signed<T>::value) + { + typedef typename std::make_unsigned<T>::type unsigned_type; + return (2.0f * static_cast<GLfloat>(value) + 1.0f) / + static_cast<GLfloat>(std::numeric_limits<unsigned_type>::max()); + } + else + { + return static_cast<GLfloat>(value) / static_cast<GLfloat>(std::numeric_limits<T>::max()); + } +} + +class VertexAttributeTest : public ANGLETest +{ + protected: + VertexAttributeTest() + : mProgram(0), mTestAttrib(-1), mExpectedAttrib(-1), mBuffer(0), mQuadBuffer(0) + { + setWindowWidth(128); + setWindowHeight(128); + setConfigRedBits(8); + setConfigGreenBits(8); + setConfigBlueBits(8); + setConfigAlphaBits(8); + setConfigDepthBits(24); + } + + enum class Source + { + BUFFER, + IMMEDIATE, + }; + + struct TestData final : angle::NonCopyable + { + TestData(GLenum typeIn, + GLboolean normalizedIn, + Source sourceIn, + const void *inputDataIn, + const GLfloat *expectedDataIn) + : type(typeIn), + normalized(normalizedIn), + bufferOffset(0), + source(sourceIn), + inputData(inputDataIn), + expectedData(expectedDataIn) + { + } + + GLenum type; + GLboolean normalized; + size_t bufferOffset; + Source source; + + const void *inputData; + const GLfloat *expectedData; + }; + + void setupTest(const TestData &test, GLint typeSize) + { + if (mProgram == 0) + { + initBasicProgram(); + } + + if (test.source == Source::BUFFER) + { + GLsizei dataSize = mVertexCount * TypeStride(test.type); + glBindBuffer(GL_ARRAY_BUFFER, mBuffer); + glBufferData(GL_ARRAY_BUFFER, dataSize, test.inputData, GL_STATIC_DRAW); + glVertexAttribPointer(mTestAttrib, typeSize, test.type, test.normalized, 0, + reinterpret_cast<GLvoid *>(test.bufferOffset)); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + else + { + ASSERT_EQ(Source::IMMEDIATE, test.source); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glVertexAttribPointer(mTestAttrib, typeSize, test.type, test.normalized, 0, + test.inputData); + } + + glVertexAttribPointer(mExpectedAttrib, typeSize, GL_FLOAT, GL_FALSE, 0, test.expectedData); + + glEnableVertexAttribArray(mTestAttrib); + glEnableVertexAttribArray(mExpectedAttrib); + } + + void checkPixels() + { + GLint viewportSize[4]; + glGetIntegerv(GL_VIEWPORT, viewportSize); + + GLint midPixelX = (viewportSize[0] + viewportSize[2]) / 2; + GLint midPixelY = (viewportSize[1] + viewportSize[3]) / 2; + + // We need to offset our checks from triangle edges to ensure we don't fall on a single tri + // Avoid making assumptions of drawQuad with four checks to check the four possible tri + // regions + EXPECT_PIXEL_EQ((midPixelX + viewportSize[0]) / 2, midPixelY, 255, 255, 255, 255); + EXPECT_PIXEL_EQ((midPixelX + viewportSize[2]) / 2, midPixelY, 255, 255, 255, 255); + EXPECT_PIXEL_EQ(midPixelX, (midPixelY + viewportSize[1]) / 2, 255, 255, 255, 255); + EXPECT_PIXEL_EQ(midPixelX, (midPixelY + viewportSize[3]) / 2, 255, 255, 255, 255); + } + + void runTest(const TestData &test) + { + // TODO(geofflang): Figure out why this is broken on AMD OpenGL + if (IsAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test skipped on AMD OpenGL." << std::endl; + return; + } + + for (GLint i = 0; i < 4; i++) + { + GLint typeSize = i + 1; + setupTest(test, typeSize); + + drawQuad(mProgram, "position", 0.5f); + + glDisableVertexAttribArray(mTestAttrib); + glDisableVertexAttribArray(mExpectedAttrib); + + checkPixels(); + } + } + + void SetUp() override + { + ANGLETest::SetUp(); + + glClearColor(0, 0, 0, 0); + glClearDepthf(0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glDisable(GL_DEPTH_TEST); + + glGenBuffers(1, &mBuffer); + } + + void TearDown() override + { + glDeleteProgram(mProgram); + glDeleteBuffers(1, &mBuffer); + glDeleteBuffers(1, &mQuadBuffer); + + ANGLETest::TearDown(); + } + + GLuint compileMultiAttribProgram(GLint attribCount) + { + std::stringstream shaderStream; + + shaderStream << "attribute mediump vec4 position;" << std::endl; + for (GLint attribIndex = 0; attribIndex < attribCount; ++attribIndex) + { + shaderStream << "attribute float a" << attribIndex << ";" << std::endl; + } + shaderStream << "varying mediump float color;" << std::endl + << "void main() {" << std::endl + << " gl_Position = position;" << std::endl + << " color = 0.0;" << std::endl; + for (GLint attribIndex = 0; attribIndex < attribCount; ++attribIndex) + { + shaderStream << " color += a" << attribIndex << ";" << std::endl; + } + shaderStream << "}" << std::endl; + + const std::string testFragmentShaderSource = + SHADER_SOURCE(varying mediump float color; void main(void) + { + gl_FragColor = vec4(color, 0.0, 0.0, 1.0); + }); + + return CompileProgram(shaderStream.str(), testFragmentShaderSource); + } + + void setupMultiAttribs(GLuint program, GLint attribCount, GLfloat value) + { + glUseProgram(program); + for (GLint attribIndex = 0; attribIndex < attribCount; ++attribIndex) + { + std::stringstream attribStream; + attribStream << "a" << attribIndex; + GLint location = glGetAttribLocation(program, attribStream.str().c_str()); + ASSERT_NE(-1, location); + glVertexAttrib1f(location, value); + glDisableVertexAttribArray(location); + } + } + + void initBasicProgram() + { + const std::string testVertexShaderSource = + "attribute mediump vec4 position;\n" + "attribute mediump vec4 test;\n" + "attribute mediump vec4 expected;\n" + "varying mediump vec4 color;\n" + "void main(void)\n" + "{\n" + " gl_Position = position;\n" + " vec4 threshold = max(abs(expected) * 0.01, 1.0 / 64.0);\n" + " color = vec4(lessThanEqual(abs(test - expected), threshold));\n" + "}\n"; + + const std::string testFragmentShaderSource = + "varying mediump vec4 color;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = color;\n" + "}\n"; + + mProgram = CompileProgram(testVertexShaderSource, testFragmentShaderSource); + ASSERT_NE(0u, mProgram); + + mTestAttrib = glGetAttribLocation(mProgram, "test"); + ASSERT_NE(-1, mTestAttrib); + mExpectedAttrib = glGetAttribLocation(mProgram, "expected"); + ASSERT_NE(-1, mExpectedAttrib); + + glUseProgram(mProgram); + } + + static const size_t mVertexCount = 24; + + GLuint mProgram; + GLint mTestAttrib; + GLint mExpectedAttrib; + GLuint mBuffer; + GLuint mQuadBuffer; +}; + +TEST_P(VertexAttributeTest, UnsignedByteUnnormalized) +{ + GLubyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, 5, 6, 7, 125, 126, 127, 128, 129, 250, 251, 252, 253, 254, 255 }; + GLfloat expectedData[mVertexCount]; + for (size_t i = 0; i < mVertexCount; i++) + { + expectedData[i] = inputData[i]; + } + + TestData data(GL_UNSIGNED_BYTE, GL_FALSE, Source::IMMEDIATE, inputData, expectedData); + runTest(data); +} + +TEST_P(VertexAttributeTest, UnsignedByteNormalized) +{ + GLubyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, 5, 6, 7, 125, 126, 127, 128, 129, 250, 251, 252, 253, 254, 255 }; + GLfloat expectedData[mVertexCount]; + for (size_t i = 0; i < mVertexCount; i++) + { + expectedData[i] = Normalize(inputData[i]); + } + + TestData data(GL_UNSIGNED_BYTE, GL_TRUE, Source::IMMEDIATE, inputData, expectedData); + runTest(data); +} + +TEST_P(VertexAttributeTest, ByteUnnormalized) +{ + GLbyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, -1, -2, -3, -4, 125, 126, 127, -128, -127, -126 }; + GLfloat expectedData[mVertexCount]; + for (size_t i = 0; i < mVertexCount; i++) + { + expectedData[i] = inputData[i]; + } + + TestData data(GL_BYTE, GL_FALSE, Source::IMMEDIATE, inputData, expectedData); + runTest(data); +} + +TEST_P(VertexAttributeTest, ByteNormalized) +{ + GLbyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, -1, -2, -3, -4, 125, 126, 127, -128, -127, -126 }; + GLfloat expectedData[mVertexCount]; + for (size_t i = 0; i < mVertexCount; i++) + { + expectedData[i] = Normalize(inputData[i]); + } + + TestData data(GL_BYTE, GL_TRUE, Source::IMMEDIATE, inputData, expectedData); + runTest(data); +} + +TEST_P(VertexAttributeTest, UnsignedShortUnnormalized) +{ + GLushort inputData[mVertexCount] = { 0, 1, 2, 3, 254, 255, 256, 32766, 32767, 32768, 65533, 65534, 65535 }; + GLfloat expectedData[mVertexCount]; + for (size_t i = 0; i < mVertexCount; i++) + { + expectedData[i] = inputData[i]; + } + + TestData data(GL_UNSIGNED_SHORT, GL_FALSE, Source::IMMEDIATE, inputData, expectedData); + runTest(data); +} + +TEST_P(VertexAttributeTest, UnsignedShortNormalized) +{ + GLushort inputData[mVertexCount] = { 0, 1, 2, 3, 254, 255, 256, 32766, 32767, 32768, 65533, 65534, 65535 }; + GLfloat expectedData[mVertexCount]; + for (size_t i = 0; i < mVertexCount; i++) + { + expectedData[i] = Normalize(inputData[i]); + } + + TestData data(GL_UNSIGNED_SHORT, GL_TRUE, Source::IMMEDIATE, inputData, expectedData); + runTest(data); +} + +TEST_P(VertexAttributeTest, ShortUnnormalized) +{ + GLshort inputData[mVertexCount] = { 0, 1, 2, 3, -1, -2, -3, -4, 32766, 32767, -32768, -32767, -32766 }; + GLfloat expectedData[mVertexCount]; + for (size_t i = 0; i < mVertexCount; i++) + { + expectedData[i] = inputData[i]; + } + + TestData data(GL_SHORT, GL_FALSE, Source::IMMEDIATE, inputData, expectedData); + runTest(data); +} + +TEST_P(VertexAttributeTest, ShortNormalized) +{ + GLshort inputData[mVertexCount] = { 0, 1, 2, 3, -1, -2, -3, -4, 32766, 32767, -32768, -32767, -32766 }; + GLfloat expectedData[mVertexCount]; + for (size_t i = 0; i < mVertexCount; i++) + { + expectedData[i] = Normalize(inputData[i]); + } + + TestData data(GL_SHORT, GL_TRUE, Source::IMMEDIATE, inputData, expectedData); + runTest(data); +} + +class VertexAttributeTestES3 : public VertexAttributeTest +{ + protected: + VertexAttributeTestES3() {} +}; + +TEST_P(VertexAttributeTestES3, IntUnnormalized) +{ + GLint lo = std::numeric_limits<GLint>::min(); + GLint hi = std::numeric_limits<GLint>::max(); + GLint inputData[mVertexCount] = {0, 1, 2, 3, -1, -2, -3, -4, -1, hi, hi - 1, lo, lo + 1}; + GLfloat expectedData[mVertexCount]; + for (size_t i = 0; i < mVertexCount; i++) + { + expectedData[i] = static_cast<GLfloat>(inputData[i]); + } + + TestData data(GL_INT, GL_FALSE, Source::BUFFER, inputData, expectedData); + runTest(data); +} + +TEST_P(VertexAttributeTestES3, IntNormalized) +{ + GLint lo = std::numeric_limits<GLint>::min(); + GLint hi = std::numeric_limits<GLint>::max(); + GLint inputData[mVertexCount] = {0, 1, 2, 3, -1, -2, -3, -4, -1, hi, hi - 1, lo, lo + 1}; + GLfloat expectedData[mVertexCount]; + for (size_t i = 0; i < mVertexCount; i++) + { + expectedData[i] = Normalize(inputData[i]); + } + + TestData data(GL_INT, GL_TRUE, Source::BUFFER, inputData, expectedData); + runTest(data); +} + +TEST_P(VertexAttributeTestES3, UnsignedIntUnnormalized) +{ + GLuint mid = std::numeric_limits<GLuint>::max() >> 1; + GLuint hi = std::numeric_limits<GLuint>::max(); + GLuint inputData[mVertexCount] = {0, 1, 2, 3, 254, 255, 256, + mid - 1, mid, mid + 1, hi - 2, hi - 1, hi}; + GLfloat expectedData[mVertexCount]; + for (size_t i = 0; i < mVertexCount; i++) + { + expectedData[i] = static_cast<GLfloat>(inputData[i]); + } + + TestData data(GL_UNSIGNED_INT, GL_FALSE, Source::BUFFER, inputData, expectedData); + runTest(data); +} + +TEST_P(VertexAttributeTestES3, UnsignedIntNormalized) +{ + GLuint mid = std::numeric_limits<GLuint>::max() >> 1; + GLuint hi = std::numeric_limits<GLuint>::max(); + GLuint inputData[mVertexCount] = {0, 1, 2, 3, 254, 255, 256, + mid - 1, mid, mid + 1, hi - 2, hi - 1, hi}; + GLfloat expectedData[mVertexCount]; + for (size_t i = 0; i < mVertexCount; i++) + { + expectedData[i] = Normalize(inputData[i]); + } + + TestData data(GL_UNSIGNED_INT, GL_TRUE, Source::BUFFER, inputData, expectedData); + runTest(data); +} + +// Validate that we can support GL_MAX_ATTRIBS attribs +TEST_P(VertexAttributeTest, MaxAttribs) +{ + // TODO(jmadill): Figure out why we get this error on AMD/OpenGL and Intel. + if ((IsIntel() || IsAMD()) && GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test skipped on Intel and AMD." << std::endl; + return; + } + + GLint maxAttribs; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttribs); + ASSERT_GL_NO_ERROR(); + + // Reserve one attrib for position + GLint drawAttribs = maxAttribs - 1; + + GLuint program = compileMultiAttribProgram(drawAttribs); + ASSERT_NE(0u, program); + + setupMultiAttribs(program, drawAttribs, 0.5f / static_cast<float>(drawAttribs)); + drawQuad(program, "position", 0.5f); + + EXPECT_GL_NO_ERROR(); + EXPECT_PIXEL_NEAR(0, 0, 128, 0, 0, 255, 1); +} + +// Validate that we cannot support GL_MAX_ATTRIBS+1 attribs +TEST_P(VertexAttributeTest, MaxAttribsPlusOne) +{ + // TODO(jmadill): Figure out why we get this error on AMD/ES2/OpenGL + if (IsAMD() && GetParam() == ES2_OPENGL()) + { + std::cout << "Test disabled on AMD/ES2/OpenGL" << std::endl; + return; + } + + GLint maxAttribs; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttribs); + ASSERT_GL_NO_ERROR(); + + // Exceed attrib count by one (counting position) + GLint drawAttribs = maxAttribs; + + GLuint program = compileMultiAttribProgram(drawAttribs); + ASSERT_EQ(0u, program); +} + +// Simple test for when we use glBindAttribLocation +TEST_P(VertexAttributeTest, SimpleBindAttribLocation) +{ + // TODO(jmadill): Figure out why this fails on Intel. + if (IsIntel() && GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test skipped on Intel." << std::endl; + return; + } + + // Re-use the multi-attrib program, binding attribute 0 + GLuint program = compileMultiAttribProgram(1); + glBindAttribLocation(program, 2, "position"); + glBindAttribLocation(program, 3, "a0"); + glLinkProgram(program); + + // Setup and draw the quad + setupMultiAttribs(program, 1, 0.5f); + drawQuad(program, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + EXPECT_PIXEL_NEAR(0, 0, 128, 0, 0, 255, 1); +} + +// Verify that drawing with a large out-of-range offset generates INVALID_OPERATION. +TEST_P(VertexAttributeTest, DrawArraysBufferTooSmall) +{ + GLfloat inputData[mVertexCount]; + GLfloat expectedData[mVertexCount]; + for (size_t count = 0; count < mVertexCount; ++count) + { + inputData[count] = static_cast<GLfloat>(count); + expectedData[count] = inputData[count]; + } + + TestData data(GL_FLOAT, GL_FALSE, Source::BUFFER, inputData, expectedData); + data.bufferOffset = mVertexCount * TypeStride(GL_FLOAT); + + setupTest(data, 1); + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_ERROR(GL_INVALID_OPERATION); +} + +// Verify that index draw with an out-of-range offset generates INVALID_OPERATION. +TEST_P(VertexAttributeTest, DrawElementsBufferTooSmall) +{ + GLfloat inputData[mVertexCount]; + GLfloat expectedData[mVertexCount]; + for (size_t count = 0; count < mVertexCount; ++count) + { + inputData[count] = static_cast<GLfloat>(count); + expectedData[count] = inputData[count]; + } + + TestData data(GL_FLOAT, GL_FALSE, Source::BUFFER, inputData, expectedData); + data.bufferOffset = (mVertexCount - 3) * TypeStride(GL_FLOAT); + + setupTest(data, 1); + drawIndexedQuad(mProgram, "position", 0.5f); + EXPECT_GL_ERROR(GL_INVALID_OPERATION); +} + +// Verify that using a different start vertex doesn't mess up the draw. +TEST_P(VertexAttributeTest, DrawArraysWithBufferOffset) +{ + // TODO(jmadill): Diagnose this failure. + if (IsD3D11_FL93()) + { + std::cout << "Test disabled on D3D11 FL 9_3" << std::endl; + return; + } + + // TODO(geofflang): Figure out why this is broken on AMD OpenGL + if (IsAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) + { + std::cout << "Test skipped on AMD OpenGL." << std::endl; + return; + } + + initBasicProgram(); + glUseProgram(mProgram); + + GLfloat inputData[mVertexCount]; + GLfloat expectedData[mVertexCount]; + for (size_t count = 0; count < mVertexCount; ++count) + { + inputData[count] = static_cast<GLfloat>(count); + expectedData[count] = inputData[count]; + } + + auto quadVertices = GetQuadVertices(); + GLsizei quadVerticesSize = static_cast<GLsizei>(quadVertices.size() * sizeof(quadVertices[0])); + + glGenBuffers(1, &mQuadBuffer); + glBindBuffer(GL_ARRAY_BUFFER, mQuadBuffer); + glBufferData(GL_ARRAY_BUFFER, quadVerticesSize + sizeof(Vector3), nullptr, GL_STATIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, quadVerticesSize, quadVertices.data()); + + GLint positionLocation = glGetAttribLocation(mProgram, "position"); + ASSERT_NE(-1, positionLocation); + glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + glEnableVertexAttribArray(positionLocation); + + GLsizei dataSize = mVertexCount * TypeStride(GL_FLOAT); + glBindBuffer(GL_ARRAY_BUFFER, mBuffer); + glBufferData(GL_ARRAY_BUFFER, dataSize + TypeStride(GL_FLOAT), nullptr, GL_STATIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, dataSize, inputData); + glVertexAttribPointer(mTestAttrib, 1, GL_FLOAT, GL_FALSE, 0, nullptr); + glEnableVertexAttribArray(mTestAttrib); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glVertexAttribPointer(mExpectedAttrib, 1, GL_FLOAT, GL_FALSE, 0, expectedData); + glEnableVertexAttribArray(mExpectedAttrib); + + // Vertex draw with no start vertex offset (second argument is zero). + glDrawArrays(GL_TRIANGLES, 0, 6); + checkPixels(); + + // Draw offset by one vertex. + glDrawArrays(GL_TRIANGLES, 1, 6); + checkPixels(); + + EXPECT_GL_NO_ERROR(); +} + +class VertexAttributeCachingTest : public VertexAttributeTest +{ + protected: + VertexAttributeCachingTest() {} + + void SetUp() override; + + template <typename DestT> + static std::vector<GLfloat> GetExpectedData(const std::vector<GLubyte> &srcData, + GLenum attribType, + GLboolean normalized); + + void initDoubleAttribProgram() + { + const std::string testVertexShaderSource = + "attribute mediump vec4 position;\n" + "attribute mediump vec4 test;\n" + "attribute mediump vec4 expected;\n" + "attribute mediump vec4 test2;\n" + "attribute mediump vec4 expected2;\n" + "varying mediump vec4 color;\n" + "void main(void)\n" + "{\n" + " gl_Position = position;\n" + " vec4 threshold = max(abs(expected) * 0.01, 1.0 / 64.0);\n" + " color = vec4(lessThanEqual(abs(test - expected), threshold));\n" + " vec4 threshold2 = max(abs(expected2) * 0.01, 1.0 / 64.0);\n" + " color += vec4(lessThanEqual(abs(test2 - expected2), threshold2));\n" + "}\n"; + + const std::string testFragmentShaderSource = + "varying mediump vec4 color;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = color;\n" + "}\n"; + + mProgram = CompileProgram(testVertexShaderSource, testFragmentShaderSource); + ASSERT_NE(0u, mProgram); + + mTestAttrib = glGetAttribLocation(mProgram, "test"); + ASSERT_NE(-1, mTestAttrib); + mExpectedAttrib = glGetAttribLocation(mProgram, "expected"); + ASSERT_NE(-1, mExpectedAttrib); + + glUseProgram(mProgram); + } + + struct AttribData + { + AttribData(GLenum typeIn, GLint sizeIn, GLboolean normalizedIn, GLsizei strideIn); + + GLenum type; + GLint size; + GLboolean normalized; + GLsizei stride; + }; + + std::vector<AttribData> mTestData; + std::map<GLenum, std::vector<GLfloat>> mExpectedData; + std::map<GLenum, std::vector<GLfloat>> mNormExpectedData; +}; + +VertexAttributeCachingTest::AttribData::AttribData(GLenum typeIn, + GLint sizeIn, + GLboolean normalizedIn, + GLsizei strideIn) + : type(typeIn), size(sizeIn), normalized(normalizedIn), stride(strideIn) +{ +} + +// static +template <typename DestT> +std::vector<GLfloat> VertexAttributeCachingTest::GetExpectedData( + const std::vector<GLubyte> &srcData, + GLenum attribType, + GLboolean normalized) +{ + std::vector<GLfloat> expectedData; + + const DestT *typedSrcPtr = reinterpret_cast<const DestT *>(srcData.data()); + size_t iterations = srcData.size() / TypeStride(attribType); + + if (normalized) + { + for (size_t index = 0; index < iterations; ++index) + { + expectedData.push_back(Normalize(typedSrcPtr[index])); + } + } + else + { + for (size_t index = 0; index < iterations; ++index) + { + expectedData.push_back(static_cast<GLfloat>(typedSrcPtr[index])); + } + } + + return expectedData; +} + +void VertexAttributeCachingTest::SetUp() +{ + VertexAttributeTest::SetUp(); + + glBindBuffer(GL_ARRAY_BUFFER, mBuffer); + + std::vector<GLubyte> srcData; + for (size_t count = 0; count < 4; ++count) + { + for (GLubyte i = 0; i < std::numeric_limits<GLubyte>::max(); ++i) + { + srcData.push_back(i); + } + } + + glBufferData(GL_ARRAY_BUFFER, srcData.size(), srcData.data(), GL_STATIC_DRAW); + + GLint viewportSize[4]; + glGetIntegerv(GL_VIEWPORT, viewportSize); + + std::vector<GLenum> attribTypes; + attribTypes.push_back(GL_BYTE); + attribTypes.push_back(GL_UNSIGNED_BYTE); + attribTypes.push_back(GL_SHORT); + attribTypes.push_back(GL_UNSIGNED_SHORT); + + if (getClientMajorVersion() >= 3) + { + attribTypes.push_back(GL_INT); + attribTypes.push_back(GL_UNSIGNED_INT); + } + + const GLint maxSize = 4; + const GLsizei maxStride = 4; + + for (GLenum attribType : attribTypes) + { + for (GLint attribSize = 1; attribSize <= maxSize; ++attribSize) + { + for (GLsizei stride = 1; stride <= maxStride; ++stride) + { + mTestData.push_back(AttribData(attribType, attribSize, GL_FALSE, stride)); + if (attribType != GL_FLOAT) + { + mTestData.push_back(AttribData(attribType, attribSize, GL_TRUE, stride)); + } + } + } + } + + mExpectedData[GL_BYTE] = GetExpectedData<GLbyte>(srcData, GL_BYTE, GL_FALSE); + mExpectedData[GL_UNSIGNED_BYTE] = GetExpectedData<GLubyte>(srcData, GL_UNSIGNED_BYTE, GL_FALSE); + mExpectedData[GL_SHORT] = GetExpectedData<GLshort>(srcData, GL_SHORT, GL_FALSE); + mExpectedData[GL_UNSIGNED_SHORT] = + GetExpectedData<GLushort>(srcData, GL_UNSIGNED_SHORT, GL_FALSE); + mExpectedData[GL_INT] = GetExpectedData<GLint>(srcData, GL_INT, GL_FALSE); + mExpectedData[GL_UNSIGNED_INT] = GetExpectedData<GLuint>(srcData, GL_UNSIGNED_INT, GL_FALSE); + + mNormExpectedData[GL_BYTE] = GetExpectedData<GLbyte>(srcData, GL_BYTE, GL_TRUE); + mNormExpectedData[GL_UNSIGNED_BYTE] = + GetExpectedData<GLubyte>(srcData, GL_UNSIGNED_BYTE, GL_TRUE); + mNormExpectedData[GL_SHORT] = GetExpectedData<GLshort>(srcData, GL_SHORT, GL_TRUE); + mNormExpectedData[GL_UNSIGNED_SHORT] = + GetExpectedData<GLushort>(srcData, GL_UNSIGNED_SHORT, GL_TRUE); + mNormExpectedData[GL_INT] = GetExpectedData<GLint>(srcData, GL_INT, GL_TRUE); + mNormExpectedData[GL_UNSIGNED_INT] = GetExpectedData<GLuint>(srcData, GL_UNSIGNED_INT, GL_TRUE); +} + +// In D3D11, we must sometimes translate buffer data into static attribute caches. We also use a +// cache management scheme which garbage collects old attributes after we start using too much +// cache data. This test tries to make as many attribute caches from a single buffer as possible +// to stress-test the caching code. +TEST_P(VertexAttributeCachingTest, BufferMulticaching) +{ + if (IsAMD() && IsDesktopOpenGL()) + { + std::cout << "Test skipped on AMD OpenGL." << std::endl; + return; + } + + initBasicProgram(); + + glEnableVertexAttribArray(mTestAttrib); + glEnableVertexAttribArray(mExpectedAttrib); + + ASSERT_GL_NO_ERROR(); + + for (const auto &data : mTestData) + { + const auto &expected = + (data.normalized) ? mNormExpectedData[data.type] : mExpectedData[data.type]; + + GLsizei baseStride = static_cast<GLsizei>(data.size) * data.stride; + GLsizei stride = TypeStride(data.type) * baseStride; + + glBindBuffer(GL_ARRAY_BUFFER, mBuffer); + glVertexAttribPointer(mTestAttrib, data.size, data.type, data.normalized, stride, nullptr); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glVertexAttribPointer(mExpectedAttrib, data.size, GL_FLOAT, GL_FALSE, + sizeof(GLfloat) * baseStride, expected.data()); + drawQuad(mProgram, "position", 0.5f); + ASSERT_GL_NO_ERROR(); + EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 255, 255, 255); + } +} + +// With D3D11 dirty bits for VertxArray11, we can leave vertex state unchanged if there aren't any +// GL calls that affect it. This test targets leaving one vertex attribute unchanged between draw +// calls while changing another vertex attribute enough that it clears the static buffer cache +// after enough iterations. It validates the unchanged attributes don't get deleted incidentally. +TEST_P(VertexAttributeCachingTest, BufferMulticachingWithOneUnchangedAttrib) +{ + if (IsAMD() && IsDesktopOpenGL()) + { + std::cout << "Test skipped on AMD OpenGL." << std::endl; + return; + } + + initDoubleAttribProgram(); + + GLint testAttrib2Location = glGetAttribLocation(mProgram, "test2"); + ASSERT_NE(-1, testAttrib2Location); + GLint expectedAttrib2Location = glGetAttribLocation(mProgram, "expected2"); + ASSERT_NE(-1, expectedAttrib2Location); + + glEnableVertexAttribArray(mTestAttrib); + glEnableVertexAttribArray(mExpectedAttrib); + glEnableVertexAttribArray(testAttrib2Location); + glEnableVertexAttribArray(expectedAttrib2Location); + + ASSERT_GL_NO_ERROR(); + + // Use an attribute that we know must be converted. This is a bit sensitive. + glBindBuffer(GL_ARRAY_BUFFER, mBuffer); + glVertexAttribPointer(testAttrib2Location, 3, GL_UNSIGNED_SHORT, GL_FALSE, 6, nullptr); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glVertexAttribPointer(expectedAttrib2Location, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, + mExpectedData[GL_UNSIGNED_SHORT].data()); + + for (const auto &data : mTestData) + { + const auto &expected = + (data.normalized) ? mNormExpectedData[data.type] : mExpectedData[data.type]; + + GLsizei baseStride = static_cast<GLsizei>(data.size) * data.stride; + GLsizei stride = TypeStride(data.type) * baseStride; + + glBindBuffer(GL_ARRAY_BUFFER, mBuffer); + glVertexAttribPointer(mTestAttrib, data.size, data.type, data.normalized, stride, nullptr); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glVertexAttribPointer(mExpectedAttrib, data.size, GL_FLOAT, GL_FALSE, + sizeof(GLfloat) * baseStride, expected.data()); + drawQuad(mProgram, "position", 0.5f); + + ASSERT_GL_NO_ERROR(); + EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 255, 255, 255); + } +} + +// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. +// D3D11 Feature Level 9_3 uses different D3D formats for vertex attribs compared to Feature Levels 10_0+, so we should test them separately. +ANGLE_INSTANTIATE_TEST(VertexAttributeTest, + ES2_D3D9(), + ES2_D3D11(), + ES2_D3D11_FL9_3(), + ES2_OPENGL(), + ES3_OPENGL(), + ES2_OPENGLES(), + ES3_OPENGLES()); + +ANGLE_INSTANTIATE_TEST(VertexAttributeTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); + +ANGLE_INSTANTIATE_TEST(VertexAttributeCachingTest, + ES2_D3D9(), + ES2_D3D11(), + ES3_D3D11(), + ES3_OPENGL()); + +} // anonymous namespace |