blob: 69232cdda853378c3723b051b0d860eb0fc32c3c [file] [log] [blame]
//
// 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<uint32_t> 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<GLubyte> 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<void *>(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<void *>(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<void *>(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<void *>(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<void *>(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();
}
// Uses un-aligned index buffer to draw, the draw call should be ignored
TEST_P(IndexBufferOffsetTest, DrawAtUnAlignedIndexBuffer)
{
constexpr GLushort indices[6] = {0, 1, 2, 2, 3, 0};
GLubyte indicesUnaligned[1 + sizeof(indices)];
/* unalign indices */
indicesUnaligned[0] = 0;
for (unsigned long i = 0; i < sizeof(indices); ++i)
{
indicesUnaligned[i + 1] = ((GLubyte *)indices)[i];
}
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);
GLBuffer buffer;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicesUnaligned), indicesUnaligned,
GL_DYNAMIC_DRAW);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, reinterpret_cast<void *>(1));
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
// The draw should be ignored, nothing should been drawn
EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::black);
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<void *>(0));
// Draw with 16 bits index buffer
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, reinterpret_cast<void *>(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<void *>(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<void *>(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<void *>(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<void *>(1));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, reinterpret_cast<void *>(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);