blob: c84fad7ac48f95bd8d2292209f4c7dd58fb0e7c2 [file] [log] [blame]
//
// Copyright 2020 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.
//
// DrawBaseVertexVariantsTest: Tests variants of drawElements*BaseVertex* call from different
// extensions
#include "gpu_info_util/SystemInfo.h"
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
#include <numeric>
using namespace angle;
namespace
{
// Create a kWidth * kHeight canvas equally split into kCountX * kCountY tiles
// each containing a quad partially covering each tile
constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 256;
constexpr uint32_t kCountX = 8;
constexpr uint32_t kCountY = 8;
constexpr int kBoxCount = kCountX * kCountY;
constexpr uint32_t kIndexPatternRepeatCount = 3;
constexpr std::array<GLfloat, 2> kTileSize = {
1.f / static_cast<GLfloat>(kCountX),
1.f / static_cast<GLfloat>(kCountY),
};
constexpr std::array<uint32_t, 2> kTilePixelSize = {kWidth / kCountX, kHeight / kCountY};
constexpr std::array<GLfloat, 2> kQuadRadius = {0.25f * kTileSize[0], 0.25f * kTileSize[1]};
constexpr std::array<uint32_t, 2> kPixelCheckSize = {
static_cast<uint32_t>(kQuadRadius[0] * kWidth),
static_cast<uint32_t>(kQuadRadius[1] * kHeight)};
constexpr std::array<GLfloat, 2> GetTileCenter(uint32_t x, uint32_t y)
{
return {
kTileSize[0] * (0.5f + static_cast<GLfloat>(x)),
kTileSize[1] * (0.5f + static_cast<GLfloat>(y)),
};
}
constexpr std::array<std::array<GLfloat, 2>, 4> GetQuadVertices(uint32_t x, uint32_t y)
{
const auto center = GetTileCenter(x, y);
return {
std::array<GLfloat, 2>{center[0] - kQuadRadius[0], center[1] - kQuadRadius[1]},
std::array<GLfloat, 2>{center[0] + kQuadRadius[0], center[1] - kQuadRadius[1]},
std::array<GLfloat, 2>{center[0] + kQuadRadius[0], center[1] + kQuadRadius[1]},
std::array<GLfloat, 2>{center[0] - kQuadRadius[0], center[1] + kQuadRadius[1]},
};
}
enum class DrawCallVariants
{
DrawElementsBaseVertex,
DrawElementsInstancedBaseVertex,
DrawRangeElementsBaseVertex,
DrawElementsInstancedBaseVertexBaseInstance
};
// These tests check correctness of variants of baseVertex draw calls from different extensions
class DrawBaseVertexVariantsTest : public ANGLETest
{
protected:
DrawBaseVertexVariantsTest()
{
setWindowWidth(kWidth);
setWindowHeight(kHeight);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
std::array<GLushort, 6> indices = {0, 1, 2, 0, 2, 3};
mIndices.resize(indices.size() * kIndexPatternRepeatCount);
for (uint32_t i = 0; i < kIndexPatternRepeatCount; i++)
{
size_t o = i * indices.size();
size_t vo = i * 4; // each quad has 4 vertices, index offset by 4
for (size_t j = 0; j < indices.size(); j++)
{
mIndices[o + j] = vo + indices[j];
}
}
mColorPalette = {GLColor(0x7f, 0x7f, 0x7f, 0xff),
GLColor::red,
GLColor::green,
GLColor::yellow,
GLColor::blue,
GLColor::magenta,
GLColor::cyan,
GLColor::white};
for (uint32_t y = 0; y < kCountY; ++y)
{
for (uint32_t x = 0; x < kCountX; ++x)
{
// v3 ---- v2
// | |
// | |
// v0 ---- v1
const auto vs = ::GetQuadVertices(x, y);
for (const auto &v : vs)
{
mVertices.insert(mVertices.end(), v.begin(), v.end());
}
const auto &colorPicked = mColorPalette[(x + y) % mColorPalette.size()];
for (int i = 0; i < 4; ++i)
{
mVertexColors.push_back(colorPicked.R);
mVertexColors.push_back(colorPicked.G);
mVertexColors.push_back(colorPicked.B);
mVertexColors.push_back(colorPicked.A);
}
}
}
mRegularIndices.resize(kCountY * kCountX * mIndices.size());
for (uint32_t y = 0; y < kCountY; y++)
{
for (uint32_t x = 0; x < kCountX; x++)
{
uint32_t i = x + y * kCountX;
uint32_t oi = 6 * i;
uint32_t ov = 4 * i;
for (uint32_t j = 0; j < 6; j++)
{
mRegularIndices[oi + j] = mIndices[j] + ov;
}
}
}
}
void setupProgram(GLProgram &program)
{
constexpr char vs[] = R"(
precision mediump float;
attribute vec2 vPosition;
attribute vec4 vColor;
varying vec4 color;
void main()
{
gl_Position = vec4(vec3(vPosition, 1.0) * 2.0 - 1.0, 1.0);
color = vColor;
})";
constexpr char fs[] = R"(
precision mediump float;
varying vec4 color;
void main()
{
gl_FragColor = color;
})";
program.makeRaster(vs, fs);
EXPECT_GL_NO_ERROR();
ASSERT_TRUE(program.valid());
glUseProgram(program.get());
mPositionLoc = glGetAttribLocation(program.get(), "vPosition");
ASSERT_NE(-1, mPositionLoc);
mColorLoc = glGetAttribLocation(program.get(), "vColor");
ASSERT_NE(-1, mColorLoc);
}
void setupIndexedBuffers(GLBuffer &vertexPositionBuffer,
GLBuffer &vertexColorBuffer,
GLBuffer &indexBuffer)
{
glBindBuffer(GL_ARRAY_BUFFER, vertexColorBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLubyte) * mVertexColors.size(), mVertexColors.data(),
GL_STATIC_DRAW);
glEnableVertexAttribArray(mColorLoc);
glVertexAttribPointer(mColorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vertexPositionBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * mVertices.size(), mVertices.data(),
GL_STATIC_DRAW);
glEnableVertexAttribArray(mPositionLoc);
glVertexAttribPointer(mPositionLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * mIndices.size(), mIndices.data(),
GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR();
}
void doDrawElementsBaseVertexVariants(DrawCallVariants drawCallType)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
int baseRepetition = 0;
int i = 0;
// Start at various repetitions within the patterned index buffer to exercise base
// index.
static_assert(kIndexPatternRepeatCount >= 3, "Repeat pattern count should be at least 3");
while (i < kBoxCount)
{
int repetitionCount = std::min(3 - baseRepetition, kBoxCount - i);
switch (drawCallType)
{
case DrawCallVariants::DrawElementsInstancedBaseVertexBaseInstance:
glDrawElementsInstancedBaseVertexBaseInstanceANGLE(
GL_TRIANGLES, repetitionCount * 6, GL_UNSIGNED_SHORT,
reinterpret_cast<GLvoid *>(
static_cast<uintptr_t>(baseRepetition * 6 * sizeof(GLushort))),
1, (i - baseRepetition) * 4, 0);
break;
case DrawCallVariants::DrawElementsBaseVertex:
glDrawElementsBaseVertexEXT(
GL_TRIANGLES, repetitionCount * 6, GL_UNSIGNED_SHORT,
reinterpret_cast<GLvoid *>(
static_cast<uintptr_t>(baseRepetition * 6 * sizeof(GLushort))),
(i - baseRepetition) * 4);
break;
case DrawCallVariants::DrawElementsInstancedBaseVertex:
glDrawElementsInstancedBaseVertexEXT(
GL_TRIANGLES, repetitionCount * 6, GL_UNSIGNED_SHORT,
reinterpret_cast<GLvoid *>(
static_cast<uintptr_t>(baseRepetition * 6 * sizeof(GLushort))),
1, (i - baseRepetition) * 4);
break;
case DrawCallVariants::DrawRangeElementsBaseVertex:
glDrawRangeElementsBaseVertexEXT(
GL_TRIANGLES, baseRepetition * 4,
(baseRepetition + repetitionCount) * 4 - 1, repetitionCount * 6,
GL_UNSIGNED_SHORT,
reinterpret_cast<GLvoid *>(
static_cast<uintptr_t>(baseRepetition * 6 * sizeof(GLushort))),
(i - baseRepetition) * 4);
break;
default:
EXPECT_TRUE(false);
break;
}
baseRepetition = (baseRepetition + 1) % 3;
i += repetitionCount;
}
EXPECT_GL_NO_ERROR();
checkDrawResult();
}
void checkDrawResult()
{
for (uint32_t y = 0; y < kCountY; ++y)
{
for (uint32_t x = 0; x < kCountX; ++x)
{
uint32_t center_x = x * kTilePixelSize[0] + kTilePixelSize[0] / 2;
uint32_t center_y = y * kTilePixelSize[1] + kTilePixelSize[1] / 2;
const auto &color = mColorPalette[(x + y) % mColorPalette.size()];
EXPECT_PIXEL_NEAR(center_x - kPixelCheckSize[0] / 2,
center_y - kPixelCheckSize[1] / 2, color[0], color[1], color[2],
color[3], 1);
}
}
}
bool requestAngleBaseVertexBaseInstanceExtensions()
{
if (getClientMajorVersion() <= 2)
{
if (!EnsureGLExtensionEnabled("GL_ANGLE_instanced_arrays"))
{
return false;
}
}
return EnsureGLExtensionEnabled("GL_ANGLE_base_vertex_base_instance");
}
bool requestNativeBaseVertexExtensions()
{
return (EnsureGLExtensionEnabled("GL_OES_draw_elements_base_vertex") ||
EnsureGLExtensionEnabled("GL_EXT_draw_elements_base_vertex"));
}
std::vector<GLushort> mIndices;
std::vector<GLfloat> mVertices;
std::vector<GLubyte> mVertexColors;
std::vector<GLColor> mColorPalette;
std::vector<GLushort> mRegularIndices;
GLint mPositionLoc;
GLint mColorLoc;
};
// Test drawElementsBaseVertex from OES/EXT_draw_elements_base_vertex
TEST_P(DrawBaseVertexVariantsTest, DrawElementsBaseVertex)
{
ANGLE_SKIP_TEST_IF(!requestNativeBaseVertexExtensions());
GLProgram program;
setupProgram(program);
GLBuffer indexBuffer;
GLBuffer vertexPositionBuffer;
GLBuffer vertexColorBuffer;
setupIndexedBuffers(vertexPositionBuffer, vertexColorBuffer, indexBuffer);
doDrawElementsBaseVertexVariants(DrawCallVariants::DrawElementsBaseVertex);
}
// Test drawElementsInstancedBaseVertex from OES/EXT_draw_elements_base_vertex
TEST_P(DrawBaseVertexVariantsTest, DrawElementsInstancedBaseVertex)
{
ANGLE_SKIP_TEST_IF(!requestNativeBaseVertexExtensions());
GLProgram program;
setupProgram(program);
GLBuffer indexBuffer;
GLBuffer vertexPositionBuffer;
GLBuffer vertexColorBuffer;
setupIndexedBuffers(vertexPositionBuffer, vertexColorBuffer, indexBuffer);
doDrawElementsBaseVertexVariants(DrawCallVariants::DrawElementsInstancedBaseVertex);
}
// Test drawRangeElementsBaseVertex from OES/EXT_draw_elements_base_vertex
TEST_P(DrawBaseVertexVariantsTest, DrawRangeElementsBaseVertex)
{
ANGLE_SKIP_TEST_IF(!requestNativeBaseVertexExtensions());
GLProgram program;
setupProgram(program);
GLBuffer indexBuffer;
GLBuffer vertexPositionBuffer;
GLBuffer vertexColorBuffer;
setupIndexedBuffers(vertexPositionBuffer, vertexColorBuffer, indexBuffer);
doDrawElementsBaseVertexVariants(DrawCallVariants::DrawRangeElementsBaseVertex);
}
// Test drawElementsInstancedBaseVertexBaseInstance from ANGLE_base_vertex_base_instance
TEST_P(DrawBaseVertexVariantsTest, DrawElementsInstancedBaseVertexBaseInstance)
{
ANGLE_SKIP_TEST_IF(!requestAngleBaseVertexBaseInstanceExtensions());
GLProgram program;
setupProgram(program);
GLBuffer indexBuffer;
GLBuffer vertexPositionBuffer;
GLBuffer vertexColorBuffer;
setupIndexedBuffers(vertexPositionBuffer, vertexColorBuffer, indexBuffer);
doDrawElementsBaseVertexVariants(DrawCallVariants::DrawElementsInstancedBaseVertexBaseInstance);
}
ANGLE_INSTANTIATE_TEST_ES3(DrawBaseVertexVariantsTest);
} // namespace