Commit bc4c4bc5 by Jamie Madill Committed by Commit Bot

Re-land "Validate that attrib offsets don't overflow buffers."

During draw calls, we wouldn't add the current attrib offset to the required draw call size when checking attributes. This could lead to us producing warnings in the D3D11 runtime, and miss returning some errors. Re-land with a test data initialization fix, and with some extra tests merged from another CL. BUG=angleproject:1339 Change-Id: Ifd549c3b1f6871417dc1693cb3ec534414e92cfd Reviewed-on: https://chromium-review.googlesource.com/333723Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 71dfb369
...@@ -68,13 +68,16 @@ bool ValidateDrawAttribs(ValidationContext *context, GLint primcount, GLint maxV ...@@ -68,13 +68,16 @@ bool ValidateDrawAttribs(ValidationContext *context, GLint primcount, GLint maxV
GLint64 attribSize = GLint64 attribSize =
static_cast<GLint64>(ComputeVertexAttributeTypeSize(attrib)); static_cast<GLint64>(ComputeVertexAttributeTypeSize(attrib));
GLint64 attribDataSize = (maxVertexElement - 1) * attribStride + attribSize; GLint64 attribDataSize = (maxVertexElement - 1) * attribStride + attribSize;
GLint64 attribOffset = static_cast<GLint64>(attrib.offset);
// [OpenGL ES 3.0.2] section 2.9.4 page 40: // [OpenGL ES 3.0.2] section 2.9.4 page 40:
// We can return INVALID_OPERATION if our vertex attribute does not have // We can return INVALID_OPERATION if our vertex attribute does not have
// enough backing data. // enough backing data.
if (attribDataSize > buffer->getSize()) if (attribDataSize + attribOffset > buffer->getSize())
{ {
context->recordError(Error(GL_INVALID_OPERATION)); context->recordError(
Error(GL_INVALID_OPERATION,
"Vertex buffer is not big enough for the draw call"));
return false; return false;
} }
} }
...@@ -1995,7 +1998,7 @@ bool ValidateDrawElements(ValidationContext *context, ...@@ -1995,7 +1998,7 @@ bool ValidateDrawElements(ValidationContext *context,
return false; return false;
} }
if (!ValidateDrawAttribs(context, primcount, static_cast<GLsizei>(indexRangeOut->end))) if (!ValidateDrawAttribs(context, primcount, static_cast<GLint>(indexRangeOut->vertexCount())))
{ {
return false; return false;
} }
......
...@@ -67,42 +67,45 @@ class VertexAttributeTest : public ANGLETest ...@@ -67,42 +67,45 @@ class VertexAttributeTest : public ANGLETest
IMMEDIATE, IMMEDIATE,
}; };
struct TestData 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; GLenum type;
GLboolean normalized; GLboolean normalized;
size_t bufferOffset;
Source source; Source source;
const void *inputData; const void *inputData;
const GLfloat *expectedData; const GLfloat *expectedData;
}; };
void runTest(const TestData &test) void setupTest(const TestData &test, GLint typeSize)
{ {
// TODO(geofflang): Figure out why this is broken on AMD OpenGL if (mProgram == 0)
if (IsAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
{ {
std::cout << "Test skipped on AMD OpenGL." << std::endl; initBasicProgram();
return;
} }
GLint viewportSize[4];
glGetIntegerv(GL_VIEWPORT, viewportSize);
GLint midPixelX = (viewportSize[0] + viewportSize[2]) / 2;
GLint midPixelY = (viewportSize[1] + viewportSize[3]) / 2;
for (GLint i = 0; i < 4; i++)
{
GLint typeSize = i + 1;
if (test.source == Source::BUFFER) if (test.source == Source::BUFFER)
{ {
GLsizei dataSize = mVertexCount * TypeStride(test.type) * typeSize; GLsizei dataSize = mVertexCount * TypeStride(test.type) * typeSize;
glBindBuffer(GL_ARRAY_BUFFER, mBuffer); glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
glBufferData(GL_ARRAY_BUFFER, dataSize, test.inputData, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, dataSize, test.inputData, GL_STATIC_DRAW);
glVertexAttribPointer(mTestAttrib, typeSize, test.type, test.normalized, 0, glVertexAttribPointer(mTestAttrib, typeSize, test.type, test.normalized, 0,
nullptr); reinterpret_cast<GLvoid *>(test.bufferOffset));
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
} }
else else
...@@ -113,11 +116,36 @@ class VertexAttributeTest : public ANGLETest ...@@ -113,11 +116,36 @@ class VertexAttributeTest : public ANGLETest
test.inputData); test.inputData);
} }
glVertexAttribPointer(mExpectedAttrib, typeSize, GL_FLOAT, GL_FALSE, 0, glVertexAttribPointer(mExpectedAttrib, typeSize, GL_FLOAT, GL_FALSE, 0, test.expectedData);
test.expectedData);
glEnableVertexAttribArray(mTestAttrib); glEnableVertexAttribArray(mTestAttrib);
glEnableVertexAttribArray(mExpectedAttrib); glEnableVertexAttribArray(mExpectedAttrib);
}
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;
}
if (mProgram == 0)
{
initBasicProgram();
}
GLint viewportSize[4];
glGetIntegerv(GL_VIEWPORT, viewportSize);
GLint midPixelX = (viewportSize[0] + viewportSize[2]) / 2;
GLint midPixelY = (viewportSize[1] + viewportSize[3]) / 2;
for (GLint i = 0; i < 4; i++)
{
GLint typeSize = i + 1;
setupTest(test, typeSize);
drawQuad(mProgram, "position", 0.5f); drawQuad(mProgram, "position", 0.5f);
...@@ -137,42 +165,6 @@ class VertexAttributeTest : public ANGLETest ...@@ -137,42 +165,6 @@ class VertexAttributeTest : public ANGLETest
{ {
ANGLETest::SetUp(); ANGLETest::SetUp();
const std::string testVertexShaderSource = SHADER_SOURCE
(
attribute highp vec4 position;
attribute highp vec4 test;
attribute highp vec4 expected;
varying highp vec4 color;
void main(void)
{
gl_Position = position;
vec4 threshold = max(abs(expected) * 0.01, 1.0 / 64.0);
color = vec4(lessThanEqual(abs(test - expected), threshold));
}
);
const std::string testFragmentShaderSource = SHADER_SOURCE
(
varying highp vec4 color;
void main(void)
{
gl_FragColor = color;
}
);
mProgram = CompileProgram(testVertexShaderSource, testFragmentShaderSource);
if (mProgram == 0)
{
FAIL() << "shader compilation failed.";
}
mTestAttrib = glGetAttribLocation(mProgram, "test");
mExpectedAttrib = glGetAttribLocation(mProgram, "expected");
glUseProgram(mProgram);
glClearColor(0, 0, 0, 0); glClearColor(0, 0, 0, 0);
glClearDepthf(0.0); glClearDepthf(0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
...@@ -194,12 +186,12 @@ class VertexAttributeTest : public ANGLETest ...@@ -194,12 +186,12 @@ class VertexAttributeTest : public ANGLETest
{ {
std::stringstream shaderStream; std::stringstream shaderStream;
shaderStream << "attribute highp vec4 position;" << std::endl; shaderStream << "attribute mediump vec4 position;" << std::endl;
for (GLint attribIndex = 0; attribIndex < attribCount; ++attribIndex) for (GLint attribIndex = 0; attribIndex < attribCount; ++attribIndex)
{ {
shaderStream << "attribute float a" << attribIndex << ";" << std::endl; shaderStream << "attribute float a" << attribIndex << ";" << std::endl;
} }
shaderStream << "varying highp float color;" << std::endl shaderStream << "varying mediump float color;" << std::endl
<< "void main() {" << std::endl << "void main() {" << std::endl
<< " gl_Position = position;" << std::endl << " gl_Position = position;" << std::endl
<< " color = 0.0;" << std::endl; << " color = 0.0;" << std::endl;
...@@ -209,14 +201,11 @@ class VertexAttributeTest : public ANGLETest ...@@ -209,14 +201,11 @@ class VertexAttributeTest : public ANGLETest
} }
shaderStream << "}" << std::endl; shaderStream << "}" << std::endl;
const std::string testFragmentShaderSource = SHADER_SOURCE const std::string testFragmentShaderSource =
( SHADER_SOURCE(varying mediump float color; void main(void)
varying highp float color;
void main(void)
{ {
gl_FragColor = vec4(color, 0.0, 0.0, 1.0); gl_FragColor = vec4(color, 0.0, 0.0, 1.0);
} });
);
return CompileProgram(shaderStream.str(), testFragmentShaderSource); return CompileProgram(shaderStream.str(), testFragmentShaderSource);
} }
...@@ -235,6 +224,38 @@ class VertexAttributeTest : public ANGLETest ...@@ -235,6 +224,38 @@ class VertexAttributeTest : public ANGLETest
} }
} }
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; static const size_t mVertexCount = 24;
GLuint mProgram; GLuint mProgram;
...@@ -252,7 +273,7 @@ TEST_P(VertexAttributeTest, UnsignedByteUnnormalized) ...@@ -252,7 +273,7 @@ TEST_P(VertexAttributeTest, UnsignedByteUnnormalized)
expectedData[i] = inputData[i]; expectedData[i] = inputData[i];
} }
TestData data = {GL_UNSIGNED_BYTE, GL_FALSE, Source::IMMEDIATE, inputData, expectedData}; TestData data(GL_UNSIGNED_BYTE, GL_FALSE, Source::IMMEDIATE, inputData, expectedData);
runTest(data); runTest(data);
} }
...@@ -265,7 +286,7 @@ TEST_P(VertexAttributeTest, UnsignedByteNormalized) ...@@ -265,7 +286,7 @@ TEST_P(VertexAttributeTest, UnsignedByteNormalized)
expectedData[i] = Normalize(inputData[i]); expectedData[i] = Normalize(inputData[i]);
} }
TestData data = {GL_UNSIGNED_BYTE, GL_TRUE, Source::IMMEDIATE, inputData, expectedData}; TestData data(GL_UNSIGNED_BYTE, GL_TRUE, Source::IMMEDIATE, inputData, expectedData);
runTest(data); runTest(data);
} }
...@@ -278,7 +299,7 @@ TEST_P(VertexAttributeTest, ByteUnnormalized) ...@@ -278,7 +299,7 @@ TEST_P(VertexAttributeTest, ByteUnnormalized)
expectedData[i] = inputData[i]; expectedData[i] = inputData[i];
} }
TestData data = {GL_BYTE, GL_FALSE, Source::IMMEDIATE, inputData, expectedData}; TestData data(GL_BYTE, GL_FALSE, Source::IMMEDIATE, inputData, expectedData);
runTest(data); runTest(data);
} }
...@@ -291,7 +312,7 @@ TEST_P(VertexAttributeTest, ByteNormalized) ...@@ -291,7 +312,7 @@ TEST_P(VertexAttributeTest, ByteNormalized)
expectedData[i] = Normalize(inputData[i]); expectedData[i] = Normalize(inputData[i]);
} }
TestData data = {GL_BYTE, GL_TRUE, Source::IMMEDIATE, inputData, expectedData}; TestData data(GL_BYTE, GL_TRUE, Source::IMMEDIATE, inputData, expectedData);
runTest(data); runTest(data);
} }
...@@ -304,7 +325,7 @@ TEST_P(VertexAttributeTest, UnsignedShortUnnormalized) ...@@ -304,7 +325,7 @@ TEST_P(VertexAttributeTest, UnsignedShortUnnormalized)
expectedData[i] = inputData[i]; expectedData[i] = inputData[i];
} }
TestData data = {GL_UNSIGNED_SHORT, GL_FALSE, Source::IMMEDIATE, inputData, expectedData}; TestData data(GL_UNSIGNED_SHORT, GL_FALSE, Source::IMMEDIATE, inputData, expectedData);
runTest(data); runTest(data);
} }
...@@ -317,7 +338,7 @@ TEST_P(VertexAttributeTest, UnsignedShortNormalized) ...@@ -317,7 +338,7 @@ TEST_P(VertexAttributeTest, UnsignedShortNormalized)
expectedData[i] = Normalize(inputData[i]); expectedData[i] = Normalize(inputData[i]);
} }
TestData data = {GL_UNSIGNED_SHORT, GL_TRUE, Source::IMMEDIATE, inputData, expectedData}; TestData data(GL_UNSIGNED_SHORT, GL_TRUE, Source::IMMEDIATE, inputData, expectedData);
runTest(data); runTest(data);
} }
...@@ -330,7 +351,7 @@ TEST_P(VertexAttributeTest, ShortUnnormalized) ...@@ -330,7 +351,7 @@ TEST_P(VertexAttributeTest, ShortUnnormalized)
expectedData[i] = inputData[i]; expectedData[i] = inputData[i];
} }
TestData data = {GL_SHORT, GL_FALSE, Source::IMMEDIATE, inputData, expectedData}; TestData data(GL_SHORT, GL_FALSE, Source::IMMEDIATE, inputData, expectedData);
runTest(data); runTest(data);
} }
...@@ -343,7 +364,7 @@ TEST_P(VertexAttributeTest, ShortNormalized) ...@@ -343,7 +364,7 @@ TEST_P(VertexAttributeTest, ShortNormalized)
expectedData[i] = Normalize(inputData[i]); expectedData[i] = Normalize(inputData[i]);
} }
TestData data = {GL_SHORT, GL_TRUE, Source::IMMEDIATE, inputData, expectedData}; TestData data(GL_SHORT, GL_TRUE, Source::IMMEDIATE, inputData, expectedData);
runTest(data); runTest(data);
} }
...@@ -364,7 +385,7 @@ TEST_P(VertexAttributeTestES3, IntUnnormalized) ...@@ -364,7 +385,7 @@ TEST_P(VertexAttributeTestES3, IntUnnormalized)
expectedData[i] = static_cast<GLfloat>(inputData[i]); expectedData[i] = static_cast<GLfloat>(inputData[i]);
} }
TestData data = {GL_INT, GL_FALSE, Source::BUFFER, inputData, expectedData}; TestData data(GL_INT, GL_FALSE, Source::BUFFER, inputData, expectedData);
runTest(data); runTest(data);
} }
...@@ -379,7 +400,7 @@ TEST_P(VertexAttributeTestES3, IntNormalized) ...@@ -379,7 +400,7 @@ TEST_P(VertexAttributeTestES3, IntNormalized)
expectedData[i] = Normalize(inputData[i]); expectedData[i] = Normalize(inputData[i]);
} }
TestData data = {GL_INT, GL_TRUE, Source::BUFFER, inputData, expectedData}; TestData data(GL_INT, GL_TRUE, Source::BUFFER, inputData, expectedData);
runTest(data); runTest(data);
} }
...@@ -395,7 +416,7 @@ TEST_P(VertexAttributeTestES3, UnsignedIntUnnormalized) ...@@ -395,7 +416,7 @@ TEST_P(VertexAttributeTestES3, UnsignedIntUnnormalized)
expectedData[i] = static_cast<GLfloat>(inputData[i]); expectedData[i] = static_cast<GLfloat>(inputData[i]);
} }
TestData data = {GL_UNSIGNED_INT, GL_FALSE, Source::BUFFER, inputData, expectedData}; TestData data(GL_UNSIGNED_INT, GL_FALSE, Source::BUFFER, inputData, expectedData);
runTest(data); runTest(data);
} }
...@@ -411,7 +432,7 @@ TEST_P(VertexAttributeTestES3, UnsignedIntNormalized) ...@@ -411,7 +432,7 @@ TEST_P(VertexAttributeTestES3, UnsignedIntNormalized)
expectedData[i] = Normalize(inputData[i]); expectedData[i] = Normalize(inputData[i]);
} }
TestData data = {GL_UNSIGNED_INT, GL_TRUE, Source::BUFFER, inputData, expectedData}; TestData data(GL_UNSIGNED_INT, GL_TRUE, Source::BUFFER, inputData, expectedData);
runTest(data); runTest(data);
} }
...@@ -486,17 +507,115 @@ TEST_P(VertexAttributeTest, SimpleBindAttribLocation) ...@@ -486,17 +507,115 @@ TEST_P(VertexAttributeTest, SimpleBindAttribLocation)
EXPECT_PIXEL_NEAR(0, 0, 128, 0, 0, 255, 1); 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);
}
class VertexAttributeCachingTest : public VertexAttributeTest class VertexAttributeCachingTest : public VertexAttributeTest
{ {
protected: protected:
VertexAttributeCachingTest() {} VertexAttributeCachingTest() {}
void SetUp() override;
template <typename DestT> template <typename DestT>
static std::vector<GLfloat> GetExpectedData(const std::vector<GLubyte> &srcData, static std::vector<GLfloat> GetExpectedData(const std::vector<GLubyte> &srcData,
GLenum attribType, GLenum attribType,
GLboolean normalized); 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 // static
template <typename DestT> template <typename DestT>
std::vector<GLfloat> VertexAttributeCachingTest::GetExpectedData( std::vector<GLfloat> VertexAttributeCachingTest::GetExpectedData(
...@@ -527,17 +646,9 @@ std::vector<GLfloat> VertexAttributeCachingTest::GetExpectedData( ...@@ -527,17 +646,9 @@ std::vector<GLfloat> VertexAttributeCachingTest::GetExpectedData(
return expectedData; return expectedData;
} }
// In D3D11, we must sometimes translate buffer data into static attribute caches. We also use a void VertexAttributeCachingTest::SetUp()
// 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() && isOpenGL()) VertexAttributeTest::SetUp();
{
std::cout << "Test skipped on AMD OpenGL." << std::endl;
return;
}
glBindBuffer(GL_ARRAY_BUFFER, mBuffer); glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
...@@ -555,19 +666,6 @@ TEST_P(VertexAttributeCachingTest, BufferMulticaching) ...@@ -555,19 +666,6 @@ TEST_P(VertexAttributeCachingTest, BufferMulticaching)
GLint viewportSize[4]; GLint viewportSize[4];
glGetIntegerv(GL_VIEWPORT, viewportSize); glGetIntegerv(GL_VIEWPORT, viewportSize);
struct AttribData
{
AttribData(GLenum typeIn, GLint sizeIn, GLboolean normalizedIn, GLsizei strideIn)
: type(typeIn), size(sizeIn), normalized(normalizedIn), stride(strideIn)
{
}
GLenum type;
GLint size;
GLboolean normalized;
GLsizei stride;
};
std::vector<GLenum> attribTypes; std::vector<GLenum> attribTypes;
attribTypes.push_back(GL_BYTE); attribTypes.push_back(GL_BYTE);
attribTypes.push_back(GL_UNSIGNED_BYTE); attribTypes.push_back(GL_UNSIGNED_BYTE);
...@@ -580,8 +678,6 @@ TEST_P(VertexAttributeCachingTest, BufferMulticaching) ...@@ -580,8 +678,6 @@ TEST_P(VertexAttributeCachingTest, BufferMulticaching)
attribTypes.push_back(GL_UNSIGNED_INT); attribTypes.push_back(GL_UNSIGNED_INT);
} }
std::vector<AttribData> datas;
const GLint maxSize = 4; const GLint maxSize = 4;
const GLsizei maxStride = 4; const GLsizei maxStride = 4;
...@@ -591,44 +687,108 @@ TEST_P(VertexAttributeCachingTest, BufferMulticaching) ...@@ -591,44 +687,108 @@ TEST_P(VertexAttributeCachingTest, BufferMulticaching)
{ {
for (GLsizei stride = 1; stride <= maxStride; ++stride) for (GLsizei stride = 1; stride <= maxStride; ++stride)
{ {
datas.push_back(AttribData(attribType, attribSize, GL_FALSE, stride)); mTestData.push_back(AttribData(attribType, attribSize, GL_FALSE, stride));
if (attribType != GL_FLOAT) if (attribType != GL_FLOAT)
{ {
datas.push_back(AttribData(attribType, attribSize, GL_TRUE, stride)); mTestData.push_back(AttribData(attribType, attribSize, GL_TRUE, stride));
} }
} }
} }
} }
std::map<GLenum, std::vector<GLfloat>> expectedData; mExpectedData[GL_BYTE] = GetExpectedData<GLbyte>(srcData, GL_BYTE, GL_FALSE);
std::map<GLenum, std::vector<GLfloat>> normExpectedData; mExpectedData[GL_UNSIGNED_BYTE] = GetExpectedData<GLubyte>(srcData, GL_UNSIGNED_BYTE, GL_FALSE);
mExpectedData[GL_SHORT] = GetExpectedData<GLshort>(srcData, GL_SHORT, GL_FALSE);
expectedData[GL_BYTE] = GetExpectedData<GLbyte>(srcData, GL_BYTE, GL_FALSE); mExpectedData[GL_UNSIGNED_SHORT] =
expectedData[GL_UNSIGNED_BYTE] = GetExpectedData<GLubyte>(srcData, GL_UNSIGNED_BYTE, GL_FALSE);
expectedData[GL_SHORT] = GetExpectedData<GLshort>(srcData, GL_SHORT, GL_FALSE);
expectedData[GL_UNSIGNED_SHORT] =
GetExpectedData<GLushort>(srcData, GL_UNSIGNED_SHORT, GL_FALSE); GetExpectedData<GLushort>(srcData, GL_UNSIGNED_SHORT, GL_FALSE);
expectedData[GL_INT] = GetExpectedData<GLint>(srcData, GL_INT, GL_FALSE); mExpectedData[GL_INT] = GetExpectedData<GLint>(srcData, GL_INT, GL_FALSE);
expectedData[GL_UNSIGNED_INT] = GetExpectedData<GLuint>(srcData, GL_UNSIGNED_INT, GL_FALSE); mExpectedData[GL_UNSIGNED_INT] = GetExpectedData<GLuint>(srcData, GL_UNSIGNED_INT, GL_FALSE);
normExpectedData[GL_BYTE] = GetExpectedData<GLbyte>(srcData, GL_BYTE, GL_TRUE); mNormExpectedData[GL_BYTE] = GetExpectedData<GLbyte>(srcData, GL_BYTE, GL_TRUE);
normExpectedData[GL_UNSIGNED_BYTE] = mNormExpectedData[GL_UNSIGNED_BYTE] =
GetExpectedData<GLubyte>(srcData, GL_UNSIGNED_BYTE, GL_TRUE); GetExpectedData<GLubyte>(srcData, GL_UNSIGNED_BYTE, GL_TRUE);
normExpectedData[GL_SHORT] = GetExpectedData<GLshort>(srcData, GL_SHORT, GL_TRUE); mNormExpectedData[GL_SHORT] = GetExpectedData<GLshort>(srcData, GL_SHORT, GL_TRUE);
normExpectedData[GL_UNSIGNED_SHORT] = mNormExpectedData[GL_UNSIGNED_SHORT] =
GetExpectedData<GLushort>(srcData, GL_UNSIGNED_SHORT, GL_TRUE); GetExpectedData<GLushort>(srcData, GL_UNSIGNED_SHORT, GL_TRUE);
normExpectedData[GL_INT] = GetExpectedData<GLint>(srcData, GL_INT, GL_TRUE); mNormExpectedData[GL_INT] = GetExpectedData<GLint>(srcData, GL_INT, GL_TRUE);
normExpectedData[GL_UNSIGNED_INT] = GetExpectedData<GLuint>(srcData, GL_UNSIGNED_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() && isOpenGL())
{
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() && isOpenGL())
{
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(mTestAttrib);
glEnableVertexAttribArray(mExpectedAttrib); glEnableVertexAttribArray(mExpectedAttrib);
glEnableVertexAttribArray(testAttrib2Location);
glEnableVertexAttribArray(expectedAttrib2Location);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
for (const auto &data : datas) // 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 = const auto &expected =
(data.normalized) ? normExpectedData[data.type] : expectedData[data.type]; (data.normalized) ? mNormExpectedData[data.type] : mExpectedData[data.type];
GLsizei baseStride = static_cast<GLsizei>(data.size) * data.stride; GLsizei baseStride = static_cast<GLsizei>(data.size) * data.stride;
GLsizei stride = TypeStride(data.type) * baseStride; GLsizei stride = TypeStride(data.type) * baseStride;
...@@ -639,6 +799,7 @@ TEST_P(VertexAttributeCachingTest, BufferMulticaching) ...@@ -639,6 +799,7 @@ TEST_P(VertexAttributeCachingTest, BufferMulticaching)
glVertexAttribPointer(mExpectedAttrib, data.size, GL_FLOAT, GL_FALSE, glVertexAttribPointer(mExpectedAttrib, data.size, GL_FLOAT, GL_FALSE,
sizeof(GLfloat) * baseStride, expected.data()); sizeof(GLfloat) * baseStride, expected.data());
drawQuad(mProgram, "position", 0.5f); drawQuad(mProgram, "position", 0.5f);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 255, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 255, 255, 255);
} }
......
...@@ -65,7 +65,11 @@ std::ostream &operator<<(std::ostream &ostream, const GLColor &color) ...@@ -65,7 +65,11 @@ std::ostream &operator<<(std::ostream &ostream, const GLColor &color)
} // namespace angle } // namespace angle
ANGLETest::ANGLETest() ANGLETest::ANGLETest()
: mEGLWindow(nullptr), mWidth(16), mHeight(16), mIgnoreD3D11SDKLayersWarnings(false) : mEGLWindow(nullptr),
mWidth(16),
mHeight(16),
mIgnoreD3D11SDKLayersWarnings(false),
mQuadVertexBuffer(0)
{ {
mEGLWindow = mEGLWindow =
new EGLWindow(GetParam().majorVersion, GetParam().minorVersion, GetParam().eglParameters); new EGLWindow(GetParam().majorVersion, GetParam().minorVersion, GetParam().eglParameters);
...@@ -73,6 +77,10 @@ ANGLETest::ANGLETest() ...@@ -73,6 +77,10 @@ ANGLETest::ANGLETest()
ANGLETest::~ANGLETest() ANGLETest::~ANGLETest()
{ {
if (mQuadVertexBuffer)
{
glDeleteBuffers(1, &mQuadVertexBuffer);
}
SafeDelete(mEGLWindow); SafeDelete(mEGLWindow);
} }
...@@ -183,6 +191,58 @@ void ANGLETest::drawQuad(GLuint program, ...@@ -183,6 +191,58 @@ void ANGLETest::drawQuad(GLuint program,
glUseProgram(0); glUseProgram(0);
} }
void ANGLETest::drawIndexedQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ)
{
drawIndexedQuad(program, positionAttribName, positionAttribZ, 1.0f);
}
void ANGLETest::drawIndexedQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ,
GLfloat positionAttribXYScale)
{
GLint positionLocation = glGetAttribLocation(program, positionAttribName.c_str());
glUseProgram(program);
GLuint prevBinding = 0;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, reinterpret_cast<GLint *>(&prevBinding));
if (mQuadVertexBuffer == 0)
{
glGenBuffers(1, &mQuadVertexBuffer);
const GLfloat vertices[] = {
-1.0f * positionAttribXYScale, 1.0f * positionAttribXYScale, positionAttribZ,
-1.0f * positionAttribXYScale, -1.0f * positionAttribXYScale, positionAttribZ,
1.0f * positionAttribXYScale, -1.0f * positionAttribXYScale, positionAttribZ,
1.0f * positionAttribXYScale, 1.0f * positionAttribXYScale, positionAttribZ,
};
glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 4, vertices, GL_STATIC_DRAW);
}
else
{
glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer);
}
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLocation);
glBindBuffer(GL_ARRAY_BUFFER, prevBinding);
const GLushort indices[] = {
0, 1, 2, 0, 2, 3,
};
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
glDisableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, NULL);
glUseProgram(0);
}
GLuint ANGLETest::compileShader(GLenum type, const std::string &source) GLuint ANGLETest::compileShader(GLenum type, const std::string &source)
{ {
GLuint shader = glCreateShader(type); GLuint shader = glCreateShader(type);
......
...@@ -114,6 +114,14 @@ class ANGLETest : public ::testing::TestWithParam<angle::PlatformParameters> ...@@ -114,6 +114,14 @@ class ANGLETest : public ::testing::TestWithParam<angle::PlatformParameters>
const std::string &positionAttribName, const std::string &positionAttribName,
GLfloat positionAttribZ, GLfloat positionAttribZ,
GLfloat positionAttribXYScale); GLfloat positionAttribXYScale);
void drawIndexedQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ);
void drawIndexedQuad(GLuint program,
const std::string &positionAttribName,
GLfloat positionAttribZ,
GLfloat positionAttribXYScale);
static GLuint compileShader(GLenum type, const std::string &source); static GLuint compileShader(GLenum type, const std::string &source);
static bool extensionEnabled(const std::string &extName); static bool extensionEnabled(const std::string &extName);
static bool eglClientExtensionEnabled(const std::string &extName); static bool eglClientExtensionEnabled(const std::string &extName);
...@@ -154,6 +162,9 @@ class ANGLETest : public ::testing::TestWithParam<angle::PlatformParameters> ...@@ -154,6 +162,9 @@ class ANGLETest : public ::testing::TestWithParam<angle::PlatformParameters>
bool mIgnoreD3D11SDKLayersWarnings; bool mIgnoreD3D11SDKLayersWarnings;
// Used for indexed quad rendering
GLuint mQuadVertexBuffer;
static OSWindow *mOSWindow; static OSWindow *mOSWindow;
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment