| // |
| // 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. |
| // |
| // DrawElementsTest: |
| // Tests for indexed draws. |
| // |
| |
| #include "test_utils/ANGLETest.h" |
| #include "test_utils/gl_raii.h" |
| |
| using namespace angle; |
| |
| namespace |
| { |
| |
| class DrawElementsTest : public ANGLETest |
| { |
| protected: |
| DrawElementsTest() : mProgram(0u) |
| { |
| setWindowWidth(64); |
| setWindowHeight(64); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| } |
| |
| ~DrawElementsTest() |
| { |
| for (GLuint indexBuffer : mIndexBuffers) |
| { |
| if (indexBuffer != 0) |
| { |
| glDeleteBuffers(1, &indexBuffer); |
| } |
| } |
| |
| for (GLuint vertexArray : mVertexArrays) |
| { |
| if (vertexArray != 0) |
| { |
| glDeleteVertexArrays(1, &vertexArray); |
| } |
| } |
| |
| for (GLuint vertexBuffer : mVertexBuffers) |
| { |
| if (vertexBuffer != 0) |
| { |
| glDeleteBuffers(1, &vertexBuffer); |
| } |
| } |
| |
| if (mProgram != 0u) |
| { |
| glDeleteProgram(mProgram); |
| } |
| } |
| |
| std::vector<GLuint> mIndexBuffers; |
| std::vector<GLuint> mVertexArrays; |
| std::vector<GLuint> mVertexBuffers; |
| GLuint mProgram; |
| }; |
| |
| class WebGLDrawElementsTest : public DrawElementsTest |
| { |
| public: |
| WebGLDrawElementsTest() { setWebGLCompatibilityEnabled(true); } |
| }; |
| |
| // Test no error is generated when using client-side arrays, indices = nullptr and count = 0 |
| TEST_P(DrawElementsTest, ClientSideNullptrArrayZeroCount) |
| { |
| constexpr char kVS[] = |
| "attribute vec3 a_pos;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(a_pos, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, essl1_shaders::fs::Blue()); |
| |
| GLint posLocation = glGetAttribLocation(program.get(), "a_pos"); |
| ASSERT_NE(-1, posLocation); |
| glUseProgram(program.get()); |
| |
| const auto &vertices = GetQuadVertices(); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get()); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(), |
| GL_STATIC_DRAW); |
| |
| glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); |
| glEnableVertexAttribArray(posLocation); |
| ASSERT_GL_NO_ERROR(); |
| |
| // "If drawElements is called with a count greater than zero, and no WebGLBuffer is bound to the |
| // ELEMENT_ARRAY_BUFFER binding point, an INVALID_OPERATION error is generated." |
| glDrawElements(GL_TRIANGLES, 1, GL_UNSIGNED_BYTE, nullptr); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| |
| // count == 0 so it's fine to have no element array buffer bound. |
| glDrawElements(GL_TRIANGLES, 0, GL_UNSIGNED_BYTE, nullptr); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test a state desync that can occur when using a streaming index buffer in GL in concert with |
| // deleting the applied index buffer. |
| TEST_P(DrawElementsTest, DeletingAfterStreamingIndexes) |
| { |
| // http://anglebug.com/4092 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsD3D11()); |
| // Init program |
| constexpr char kVS[] = |
| "attribute vec2 position;\n" |
| "attribute vec2 testFlag;\n" |
| "varying vec2 v_data;\n" |
| "void main() {\n" |
| " gl_Position = vec4(position, 0, 1);\n" |
| " v_data = testFlag;\n" |
| "}"; |
| |
| constexpr char kFS[] = |
| "varying highp vec2 v_data;\n" |
| "void main() {\n" |
| " gl_FragColor = vec4(v_data, 0, 1);\n" |
| "}"; |
| |
| mProgram = CompileProgram(kVS, kFS); |
| ASSERT_NE(0u, mProgram); |
| glUseProgram(mProgram); |
| |
| GLint positionLocation = glGetAttribLocation(mProgram, "position"); |
| ASSERT_NE(-1, positionLocation); |
| |
| GLint testFlagLocation = glGetAttribLocation(mProgram, "testFlag"); |
| ASSERT_NE(-1, testFlagLocation); |
| |
| mIndexBuffers.resize(3u); |
| glGenBuffers(3, &mIndexBuffers[0]); |
| |
| mVertexArrays.resize(2); |
| glGenVertexArrays(2, &mVertexArrays[0]); |
| |
| mVertexBuffers.resize(2); |
| glGenBuffers(2, &mVertexBuffers[0]); |
| |
| std::vector<GLuint> indexData[2]; |
| indexData[0].push_back(0); |
| indexData[0].push_back(1); |
| indexData[0].push_back(2); |
| indexData[0].push_back(2); |
| indexData[0].push_back(3); |
| indexData[0].push_back(0); |
| |
| indexData[1] = indexData[0]; |
| for (GLuint &item : indexData[1]) |
| { |
| item += 4u; |
| } |
| |
| std::vector<GLfloat> positionData; |
| // quad verts |
| positionData.push_back(-1.0f); |
| positionData.push_back(1.0f); |
| positionData.push_back(-1.0f); |
| positionData.push_back(-1.0f); |
| positionData.push_back(1.0f); |
| positionData.push_back(-1.0f); |
| positionData.push_back(1.0f); |
| positionData.push_back(1.0f); |
| |
| // Repeat position data |
| positionData.push_back(-1.0f); |
| positionData.push_back(1.0f); |
| positionData.push_back(-1.0f); |
| positionData.push_back(-1.0f); |
| positionData.push_back(1.0f); |
| positionData.push_back(-1.0f); |
| positionData.push_back(1.0f); |
| positionData.push_back(1.0f); |
| |
| std::vector<GLfloat> testFlagData; |
| // red |
| testFlagData.push_back(1.0f); |
| testFlagData.push_back(0.0f); |
| testFlagData.push_back(1.0f); |
| testFlagData.push_back(0.0f); |
| testFlagData.push_back(1.0f); |
| testFlagData.push_back(0.0f); |
| testFlagData.push_back(1.0f); |
| testFlagData.push_back(0.0f); |
| |
| // green |
| testFlagData.push_back(0.0f); |
| testFlagData.push_back(1.0f); |
| testFlagData.push_back(0.0f); |
| testFlagData.push_back(1.0f); |
| testFlagData.push_back(0.0f); |
| testFlagData.push_back(1.0f); |
| testFlagData.push_back(0.0f); |
| testFlagData.push_back(1.0f); |
| |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffers[0]); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indexData[0].size(), &indexData[0][0], |
| GL_STATIC_DRAW); |
| |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffers[2]); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indexData[0].size(), &indexData[0][0], |
| GL_STATIC_DRAW); |
| |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffers[1]); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indexData[1].size(), &indexData[1][0], |
| GL_STATIC_DRAW); |
| |
| // Initialize first vertex array with second index buffer |
| glBindVertexArray(mVertexArrays[0]); |
| |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffers[1]); |
| glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffers[0]); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * positionData.size(), &positionData[0], |
| GL_STATIC_DRAW); |
| glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr); |
| glEnableVertexAttribArray(positionLocation); |
| |
| glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffers[1]); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * testFlagData.size(), &testFlagData[0], |
| GL_STATIC_DRAW); |
| glVertexAttribPointer(testFlagLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr); |
| glEnableVertexAttribArray(testFlagLocation); |
| |
| // Initialize second vertex array with first index buffer |
| glBindVertexArray(mVertexArrays[1]); |
| |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffers[0]); |
| |
| glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffers[0]); |
| glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr); |
| glEnableVertexAttribArray(positionLocation); |
| |
| glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffers[1]); |
| glVertexAttribPointer(testFlagLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr); |
| glEnableVertexAttribArray(testFlagLocation); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| glBindVertexArray(mVertexArrays[0]); |
| glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); |
| EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); |
| |
| glBindVertexArray(mVertexArrays[1]); |
| glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| glBindVertexArray(mVertexArrays[0]); |
| glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); |
| EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); |
| |
| // Trigger the bug here. |
| glDeleteBuffers(1, &mIndexBuffers[2]); |
| |
| glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); |
| EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| // Test that the offset in the index buffer is forced to be a multiple of the element size |
| TEST_P(WebGLDrawElementsTest, DrawElementsTypeAlignment) |
| { |
| constexpr char kVS[] = |
| "attribute vec3 a_pos;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(a_pos, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, essl1_shaders::fs::Blue()); |
| |
| GLint posLocation = glGetAttribLocation(program, "a_pos"); |
| ASSERT_NE(-1, posLocation); |
| glUseProgram(program); |
| |
| const auto &vertices = GetQuadVertices(); |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(), |
| GL_STATIC_DRAW); |
| |
| glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); |
| glEnableVertexAttribArray(posLocation); |
| |
| GLBuffer indexBuffer; |
| const GLubyte indices1[] = {0, 0, 0, 0, 0, 0}; |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices1), indices1, GL_STATIC_DRAW); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| const char *zeroIndices = nullptr; |
| |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices); |
| ASSERT_GL_NO_ERROR(); |
| |
| const GLushort indices2[] = {0, 0, 0, 0, 0, 0}; |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices2), indices2, GL_STATIC_DRAW); |
| |
| glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices + 1); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| |
| ANGLE_INSTANTIATE_TEST_ES3(DrawElementsTest); |
| ANGLE_INSTANTIATE_TEST_ES2(WebGLDrawElementsTest); |
| } // namespace |