// // 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. // // IndexBufferOffsetTest.cpp: Test glDrawElements with an offset and an index buffer #include "test_utils/ANGLETest.h" #include "test_utils/gl_raii.h" #include "util/test_utils.h" using namespace angle; enum class UpdateType { SmallUpdate, SmallThenBigUpdate, BigThenSmallUpdate, FullUpdate, }; class IndexBufferOffsetTest : public ANGLETest<> { protected: IndexBufferOffsetTest() { setWindowWidth(128); setWindowHeight(128); setConfigRedBits(8); setConfigGreenBits(8); setConfigBlueBits(8); setConfigAlphaBits(8); } void testSetUp() override { constexpr char kVS[] = R"(precision highp float; attribute vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); })"; constexpr char kFS[] = R"(precision highp float; uniform vec4 color; void main() { gl_FragColor = color; })"; mProgram = CompileProgram(kVS, kFS); ASSERT_NE(0u, mProgram); mColorUniformLocation = glGetUniformLocation(mProgram, "color"); mPositionAttributeLocation = glGetAttribLocation(mProgram, "position"); const GLfloat vertices[] = {-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f}; glGenBuffers(1, &mVertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices[0], GL_STATIC_DRAW); glGenBuffers(1, &mIndexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer); } void testTearDown() override { glDeleteBuffers(1, &mVertexBuffer); glDeleteBuffers(1, &mIndexBuffer); glDeleteProgram(mProgram); } void preTestUpdateBuffer(GLuint framebuffer, GLuint texture, GLuint buffer, GLsizei size) { GLsizei uboSize = std::max(size, 16); const std::vector initialData((uboSize + 3) / 4, 0x1234567u); glBindTexture(GL_TEXTURE_2D, texture); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); glBindBuffer(GL_UNIFORM_BUFFER, buffer); glBufferData(GL_UNIFORM_BUFFER, uboSize, initialData.data(), GL_DYNAMIC_DRAW); glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer); constexpr char kVerifyUBO[] = R"(#version 300 es precision mediump float; uniform block { uint data; } ubo; out vec4 colorOut; void main() { if (ubo.data == 0x1234567u) colorOut = vec4(0, 1.0, 0, 1.0); else colorOut = vec4(1.0, 0, 0, 1.0); })"; ANGLE_GL_PROGRAM(verifyUbo, essl3_shaders::vs::Simple(), kVerifyUBO); glDisable(GL_BLEND); drawQuad(verifyUbo, essl3_shaders::PositionAttrib(), 0.5); EXPECT_GL_NO_ERROR(); glBindFramebuffer(GL_FRAMEBUFFER, 0); } void runTest(GLenum type, int typeWidth, void *indexDataIn, UpdateType updateType, bool useBuffersAsUboFirst) { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); size_t indexDataWidth = 6 * typeWidth; std::vector indexData(6 * 3 * sizeof(GLuint), 0); memcpy(indexData.data() + indexDataWidth, indexDataIn, indexDataWidth); GLFramebuffer elementUpdateFbo; GLTexture elementUpdateTex; if (useBuffersAsUboFirst) { preTestUpdateBuffer(elementUpdateFbo, elementUpdateTex, mIndexBuffer, 3 * indexDataWidth); } else { glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * indexDataWidth, nullptr, GL_DYNAMIC_DRAW); } if (updateType == UpdateType::SmallUpdate) { glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexDataWidth, indexData.data()); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth, indexDataWidth, indexData.data() + indexDataWidth); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 2 * indexDataWidth, indexDataWidth, indexData.data() + 2 * indexDataWidth); } else if (updateType == UpdateType::SmallThenBigUpdate) { glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, 4, indexData.data()); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 4, 3 * indexDataWidth - 4, indexData.data() + 4); } else if (updateType == UpdateType::BigThenSmallUpdate) { glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, 3 * indexDataWidth - 4, indexData.data()); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 3 * indexDataWidth - 4, 4, indexData.data() + 3 * indexDataWidth - 4); } else { ASSERT_EQ(updateType, UpdateType::FullUpdate); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, 3 * indexDataWidth, indexData.data()); } glUseProgram(mProgram); glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); glVertexAttribPointer(mPositionAttributeLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(mPositionAttributeLocation); glUniform4f(mColorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f); for (int i = 0; i < 16; i++) { glDrawElements(GL_TRIANGLES, 6, type, reinterpret_cast(indexDataWidth)); EXPECT_PIXEL_COLOR_EQ(64, 64, GLColor::red); } if (updateType == UpdateType::SmallUpdate) { glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth, indexDataWidth, indexData.data()); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 2 * indexDataWidth, indexDataWidth, indexData.data() + indexDataWidth); } else if (updateType == UpdateType::SmallThenBigUpdate) { glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth, 4, indexData.data()); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth + 4, 2 * indexDataWidth - 4, indexData.data() + 4); } else if (updateType == UpdateType::BigThenSmallUpdate) { glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth, 2 * indexDataWidth - 4, indexData.data()); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 3 * indexDataWidth - 4, 4, indexData.data() + 2 * indexDataWidth - 4); } else { glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth, 2 * indexDataWidth, indexData.data()); } glUniform4f(mColorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f); glDrawElements(GL_TRIANGLES, 6, type, reinterpret_cast(indexDataWidth * 2)); EXPECT_PIXEL_COLOR_EQ(64, 64, GLColor::green); if (useBuffersAsUboFirst) { glBindFramebuffer(GL_FRAMEBUFFER, elementUpdateFbo); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); } EXPECT_GL_NO_ERROR(); swapBuffers(); } GLuint mProgram; GLint mColorUniformLocation; GLint mPositionAttributeLocation; GLuint mVertexBuffer; GLuint mIndexBuffer; }; class IndexBufferOffsetTestES3 : public IndexBufferOffsetTest {}; // Test using an offset for an UInt8 index buffer TEST_P(IndexBufferOffsetTest, UInt8Index) { GLubyte indexData[] = {0, 1, 2, 1, 2, 3}; runTest(GL_UNSIGNED_BYTE, 1, indexData, UpdateType::FullUpdate, false); } // Test using an offset for an UInt16 index buffer TEST_P(IndexBufferOffsetTest, UInt16Index) { GLushort indexData[] = {0, 1, 2, 1, 2, 3}; runTest(GL_UNSIGNED_SHORT, 2, indexData, UpdateType::FullUpdate, false); } // Test using an offset for an UInt32 index buffer TEST_P(IndexBufferOffsetTest, UInt32Index) { ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_OES_element_index_uint")); GLuint indexData[] = {0, 1, 2, 1, 2, 3}; runTest(GL_UNSIGNED_INT, 4, indexData, UpdateType::FullUpdate, false); } // Test using an offset for an UInt8 index buffer with small buffer updates TEST_P(IndexBufferOffsetTest, UInt8IndexSmallUpdates) { GLubyte indexData[] = {0, 1, 2, 1, 2, 3}; runTest(GL_UNSIGNED_BYTE, 1, indexData, UpdateType::SmallUpdate, false); } // Test using an offset for an UInt16 index buffer with small buffer updates TEST_P(IndexBufferOffsetTest, UInt16IndexSmallUpdates) { GLushort indexData[] = {0, 1, 2, 1, 2, 3}; runTest(GL_UNSIGNED_SHORT, 2, indexData, UpdateType::SmallUpdate, false); } // Test using an offset for an UInt32 index buffer with small buffer updates TEST_P(IndexBufferOffsetTest, UInt32IndexSmallUpdates) { ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_OES_element_index_uint")); GLuint indexData[] = {0, 1, 2, 1, 2, 3}; runTest(GL_UNSIGNED_INT, 4, indexData, UpdateType::SmallUpdate, false); } // Test using an offset for an UInt8 index buffer after uploading data to a buffer that is in use TEST_P(IndexBufferOffsetTestES3, UseAsUBOThenUpdateThenUInt8Index) { // http://anglebug.com/42264483 ANGLE_SKIP_TEST_IF(IsAMD() && IsVulkan() && IsWindows()); // http://anglebug.com/42264490 ANGLE_SKIP_TEST_IF(IsVulkan() && (IsPixel2() || IsPixel2XL())); GLubyte indexData[] = {0, 1, 2, 1, 2, 3}; runTest(GL_UNSIGNED_BYTE, 1, indexData, UpdateType::FullUpdate, true); } // Test using an offset for an UInt16 index buffer after uploading data to a buffer that is in use TEST_P(IndexBufferOffsetTestES3, UseAsUBOThenUpdateThenUInt16Index) { // http://anglebug.com/42264490 ANGLE_SKIP_TEST_IF(IsVulkan() && (IsPixel2() || IsPixel2XL())); GLushort indexData[] = {0, 1, 2, 1, 2, 3}; runTest(GL_UNSIGNED_SHORT, 2, indexData, UpdateType::FullUpdate, true); } // Test using an offset for an UInt32 index buffer after uploading data to a buffer that is in use TEST_P(IndexBufferOffsetTestES3, UseAsUBOThenUpdateThenUInt32Index) { ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_OES_element_index_uint")); // http://anglebug.com/42264490 ANGLE_SKIP_TEST_IF(IsVulkan() && (IsPixel2() || IsPixel2XL())); GLuint indexData[] = {0, 1, 2, 1, 2, 3}; runTest(GL_UNSIGNED_INT, 4, indexData, UpdateType::FullUpdate, true); } // Test using an offset for an UInt8 index buffer after uploading data to a buffer that is in use, // with small buffer updates TEST_P(IndexBufferOffsetTestES3, UseAsUBOThenUpdateThenUInt8IndexSmallUpdates) { // http://anglebug.com/42264483 ANGLE_SKIP_TEST_IF(IsAMD() && IsVulkan() && IsWindows()); // http://anglebug.com/42264490 ANGLE_SKIP_TEST_IF(IsVulkan() && (IsPixel2() || IsPixel2XL())); GLubyte indexData[] = {0, 1, 2, 1, 2, 3}; runTest(GL_UNSIGNED_BYTE, 1, indexData, UpdateType::SmallUpdate, true); } // Test using an offset for an UInt16 index buffer after uploading data to a buffer that is in use, // with small buffer updates TEST_P(IndexBufferOffsetTestES3, UseAsUBOThenUpdateThenUInt16IndexSmallUpdates) { // http://anglebug.com/42264490 ANGLE_SKIP_TEST_IF(IsVulkan() && (IsPixel2() || IsPixel2XL())); GLushort indexData[] = {0, 1, 2, 1, 2, 3}; runTest(GL_UNSIGNED_SHORT, 2, indexData, UpdateType::SmallUpdate, true); } // Test using an offset for an UInt32 index buffer after uploading data to a buffer that is in use, // with small buffer updates TEST_P(IndexBufferOffsetTestES3, UseAsUBOThenUpdateThenUInt32IndexSmallUpdates) { ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_OES_element_index_uint")); // http://anglebug.com/42264490 ANGLE_SKIP_TEST_IF(IsVulkan() && (IsPixel2() || IsPixel2XL())); GLuint indexData[] = {0, 1, 2, 1, 2, 3}; runTest(GL_UNSIGNED_INT, 4, indexData, UpdateType::SmallUpdate, true); // Also test with one subData call with more than half updates runTest(GL_UNSIGNED_INT, 4, indexData, UpdateType::SmallThenBigUpdate, true); runTest(GL_UNSIGNED_INT, 4, indexData, UpdateType::BigThenSmallUpdate, true); } // Uses index buffer offset and 2 drawElement calls one of the other, makes sure the second // drawElement call will use the correct offset. TEST_P(IndexBufferOffsetTest, DrawAtDifferentOffsets) { GLushort indexData[] = {0, 1, 2, 1, 2, 3}; glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); size_t indexDataWidth = 6 * sizeof(GLushort); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth, indexData, GL_DYNAMIC_DRAW); glUseProgram(mProgram); glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); glVertexAttribPointer(mPositionAttributeLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(mPositionAttributeLocation); glUniform4f(mColorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f); glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0); glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, reinterpret_cast(indexDataWidth / 2)); // Check the upper left triangle EXPECT_PIXEL_COLOR_EQ(0, getWindowHeight() / 4, GLColor::red); // Check the down right triangle EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red); EXPECT_GL_NO_ERROR(); } // Uses index buffer offset and 2 drawElement calls one of the other, one has aligned // offset and one doesn't TEST_P(IndexBufferOffsetTest, DrawAtDifferentOffsetAlignments) { GLubyte indexData8[] = {0, 1, 0, 1, 2, 3}; GLushort indexData16[] = {0, 1, 2, 1, 2, 3}; glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); size_t indexDataWidth16 = 6 * sizeof(GLushort); GLuint buffer[2]; glGenBuffers(2, buffer); glUseProgram(mProgram); glUniform4f(mColorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f); glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); glVertexAttribPointer(mPositionAttributeLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(mPositionAttributeLocation); // 8 bit index with aligned offset glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[0]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexData8), indexData8, GL_DYNAMIC_DRAW); glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, reinterpret_cast(2)); // 16 bits index buffer, which unaligned offset glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth16, indexData16, GL_DYNAMIC_DRAW); glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, reinterpret_cast(indexDataWidth16 / 2)); // Check the upper left triangle EXPECT_PIXEL_COLOR_EQ(0, getWindowHeight() / 4, GLColor::red); // Check the down right triangle EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red); EXPECT_GL_NO_ERROR(); } // Draw with the same element buffer, but with two different types of data. TEST_P(IndexBufferOffsetTest, DrawWithSameBufferButDifferentTypes) { GLubyte indexData8[] = {0, 1, 2}; GLushort indexData16[] = {1, 2, 3}; glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(mProgram); glUniform4f(mColorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f); glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); glVertexAttribPointer(mPositionAttributeLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(mPositionAttributeLocation); // Create element buffer and fill offset 0 with data from indexData8 and offset 512 with data // from indexData16 GLBuffer buffer; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, 4096, nullptr, GL_DYNAMIC_DRAW); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(indexData8), indexData8); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 512, sizeof(indexData16), indexData16); // Draw with 8 bit index data glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, reinterpret_cast(0)); // Draw with 16 bits index buffer glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, reinterpret_cast(512)); // Check the upper left triangle EXPECT_PIXEL_COLOR_EQ(0, getWindowHeight() / 4, GLColor::red); // Check the down right triangle EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red); EXPECT_GL_NO_ERROR(); } // Draw with GL_LINE_LOOP and followed by GL_TRIANGLES, all using the same element buffer. TEST_P(IndexBufferOffsetTest, DrawWithSameBufferButDifferentModes) { GLushort indexDataLineLoop[] = {0, 1, 2}; GLushort indexDataTriangle[] = {1, 2, 3}; glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(mProgram); glUniform4f(mColorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f); glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); glVertexAttribPointer(mPositionAttributeLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(mPositionAttributeLocation); // Create element buffer and fill offset 0 with data from indexData8 and offset 512 with data // from indexData16 GLBuffer buffer; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, 4096, nullptr, GL_DYNAMIC_DRAW); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(indexDataLineLoop), indexDataLineLoop); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 512, sizeof(indexDataTriangle), indexDataTriangle); // Draw line loop glDrawElements(GL_LINE_LOOP, 3, GL_UNSIGNED_SHORT, 0); // Draw triangle glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, reinterpret_cast(512)); // Check the down right triangle EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red); EXPECT_GL_NO_ERROR(); } // Draw with GL_LINE_LOOP and followed by GL_TRIANGLES, all using the same element buffer. TEST_P(IndexBufferOffsetTest, DrawArraysLineLoopFollowedByDrawElementsTriangle) { GLuint indexDataLineLoop[] = {0, 1, 2}; GLuint indexDataTriangle[] = {1, 2, 3}; glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(mProgram); glUniform4f(mColorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f); glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); glVertexAttribPointer(mPositionAttributeLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(mPositionAttributeLocation); // Create element buffer and fill offset 0 with data from indexDataLineLoop and offset 512 with // data from indexDataTriangle GLBuffer buffer; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, 4096, nullptr, GL_DYNAMIC_DRAW); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(indexDataLineLoop), indexDataLineLoop); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 512, sizeof(indexDataTriangle), indexDataTriangle); // First call drawElements with the same primitive and type as the final drawElement call. glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, reinterpret_cast(0)); // Then drawArray with line loop to trigger the special handling of line loop. glDrawArrays(GL_LINE_LOOP, 0, 3); // Finally drawElements with triangle and same type to ensure the element buffer state that was // modified by line loop draw call gets restored properly. glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, reinterpret_cast(512)); // Check the down right triangle EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red); EXPECT_GL_NO_ERROR(); } // Uses index buffer offset and 2 drawElement calls one of the other with different counts, // makes sure the second drawElement call will have its data available. TEST_P(IndexBufferOffsetTest, DrawWithDifferentCountsSameOffset) { GLubyte indexData[] = {99, 0, 1, 2, 1, 2, 3}; glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); size_t indexDataWidth = 7 * sizeof(GLubyte); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth, indexData, GL_DYNAMIC_DRAW); glUseProgram(mProgram); glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); glVertexAttribPointer(mPositionAttributeLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(mPositionAttributeLocation); glUniform4f(mColorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f); // The first draw draws the first triangle, and the second draws a quad. glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, reinterpret_cast(1)); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, reinterpret_cast(1)); // Check the upper left triangle EXPECT_PIXEL_COLOR_EQ(0, getWindowHeight() / 4, GLColor::red); // Check the down right triangle EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red); EXPECT_GL_NO_ERROR(); } ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(IndexBufferOffsetTest); ANGLE_INSTANTIATE_TEST_ES3(IndexBufferOffsetTestES3);