Commit d55d283c by Jamie Madill

Add a test for flat shader 'provoking vertex'.

The provoking vertex, or leading vertex, is the vertex in a triangle which produces the value that gets used for a flat-shaded triangle. Flat shading means the same value gets used for the entire triangle. It seems as though D3D and OpenGL have different definitions of which vertex is the leading/provoking one; GL says it is the *last* vertex of a primitive, while while D3D says it is the first. Additional complexity comes about from triangle strips, which switch winding order, and primitive restart, which defines new primitives in the middle of prior primitives. BUG=angleproject:754 Change-Id: If1217cba33dabf3b1e4ad13fd639d049cb16fe44 Reviewed-on: https://chromium-review.googlesource.com/309152Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Tested-by: 's avatarJamie Madill <jmadill@chromium.org>
parent f541f529
......@@ -44,6 +44,7 @@
'<(angle_path)/src/tests/gl_tests/PbufferTest.cpp',
'<(angle_path)/src/tests/gl_tests/PBOExtensionTest.cpp',
'<(angle_path)/src/tests/gl_tests/PointSpritesTest.cpp',
'<(angle_path)/src/tests/gl_tests/ProvokingVertexTest.cpp',
'<(angle_path)/src/tests/gl_tests/OcclusionQueriesTest.cpp',
'<(angle_path)/src/tests/gl_tests/ProgramBinaryTest.cpp',
'<(angle_path)/src/tests/gl_tests/ReadPixelsTest.cpp',
......
//
// 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.
//
// ProvkingVertexTest:
// Tests on the conformance of the provoking vertex, which applies to flat
// shading and compatibility with D3D. See the section on 'flatshading'
// in the ES 3 specs.
//
#include "test_utils/ANGLETest.h"
using namespace angle;
namespace
{
class ProvokingVertexTest : public ANGLETest
{
protected:
ProvokingVertexTest()
: mProgram(0),
mFramebuffer(0),
mTexture(0),
mTransformFeedback(0),
mBuffer(0),
mIntAttribLocation(-1)
{
setWindowWidth(64);
setWindowHeight(64);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
setConfigDepthBits(24);
}
void SetUp() override
{
ANGLETest::SetUp();
const std::string &vertexShader =
"#version 300 es\n"
"in int intAttrib;\n"
"in vec2 position;\n"
"flat out int attrib;\n"
"void main() {\n"
" gl_Position = vec4(position, 0, 1);\n"
" attrib = intAttrib;\n"
"}";
const std::string &fragmentShader =
"#version 300 es\n"
"flat in int attrib;\n"
"out int fragColor;\n"
"void main() {\n"
" fragColor = attrib;\n"
"}";
std::vector<std::string> tfVaryings;
tfVaryings.push_back("attrib");
mProgram = CompileProgramWithTransformFeedback(vertexShader, fragmentShader, tfVaryings,
GL_SEPARATE_ATTRIBS);
ASSERT_NE(0u, mProgram);
glGenTextures(1, &mTexture);
glBindTexture(GL_TEXTURE_2D, mTexture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32I, getWindowWidth(), getWindowHeight());
glGenFramebuffers(1, &mFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
mIntAttribLocation = glGetAttribLocation(mProgram, "intAttrib");
ASSERT_NE(-1, mIntAttribLocation);
glEnableVertexAttribArray(mIntAttribLocation);
ASSERT_GL_NO_ERROR();
}
void TearDown() override
{
if (mProgram != 0)
{
glDeleteProgram(mProgram);
mProgram = 0;
}
if (mFramebuffer != 0)
{
glDeleteFramebuffers(1, &mFramebuffer);
mFramebuffer = 0;
}
if (mTexture != 0)
{
glDeleteTextures(1, &mTexture);
mTexture = 0;
}
if (mTransformFeedback != 0)
{
glDeleteTransformFeedbacks(1, &mTransformFeedback);
mTransformFeedback = 0;
}
if (mBuffer != 0)
{
glDeleteBuffers(1, &mBuffer);
mBuffer = 0;
}
ANGLETest::TearDown();
}
GLuint mProgram;
GLuint mFramebuffer;
GLuint mTexture;
GLuint mTransformFeedback;
GLuint mBuffer;
GLint mIntAttribLocation;
};
// Test drawing a simple triangle with flat shading, and different valued vertices.
TEST_P(ProvokingVertexTest, FlatTriangle)
{
// TODO(jmadill): Implement on the D3D back-end.
if (isD3D11())
{
std::cout << "Test disabled on D3D11." << std::endl;
return;
}
GLint vertexData[] = {1, 2, 3, 1, 2, 3};
glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
drawQuad(mProgram, "position", 0.5f);
GLint pixelValue = 0;
glReadPixels(0, 0, 1, 1, GL_RED_INTEGER, GL_INT, &pixelValue);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(vertexData[2], pixelValue);
}
// Ensure that any provoking vertex shenanigans still gives correct vertex streams.
TEST_P(ProvokingVertexTest, FlatTriWithTransformFeedback)
{
// TODO(jmadill): Implement on the D3D back-end.
if (isD3D11())
{
std::cout << "Test disabled on D3D11." << std::endl;
return;
}
glGenTransformFeedbacks(1, &mTransformFeedback);
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback);
glGenBuffers(1, &mBuffer);
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mBuffer);
glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 128, nullptr, GL_STREAM_DRAW);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mBuffer);
GLint vertexData[] = {1, 2, 3, 1, 2, 3};
glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
glBeginTransformFeedback(GL_TRIANGLES);
drawQuad(mProgram, "position", 0.5f);
glEndTransformFeedback();
GLint pixelValue = 0;
glReadPixels(0, 0, 1, 1, GL_RED_INTEGER, GL_INT, &pixelValue);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(vertexData[2], pixelValue);
GLvoid *mapPointer =
glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(int) * 6, GL_MAP_READ_BIT);
ASSERT_NE(nullptr, mapPointer);
int *mappedInts = static_cast<int *>(mapPointer);
for (unsigned int cnt = 0; cnt < 6; ++cnt)
{
EXPECT_EQ(vertexData[cnt], mappedInts[cnt]);
}
}
// Test drawing a simple line with flat shading, and different valued vertices.
TEST_P(ProvokingVertexTest, FlatLine)
{
// TODO(jmadill): Implement on the D3D back-end.
if (isD3D11())
{
std::cout << "Test disabled on D3D11." << std::endl;
return;
}
GLfloat halfPixel = 1.0f / static_cast<GLfloat>(getWindowWidth());
GLint vertexData[] = {1, 2};
GLfloat positionData[] = {-1.0f + halfPixel, -1.0f, -1.0f + halfPixel, 1.0f};
glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
GLint positionLocation = glGetAttribLocation(mProgram, "position");
glEnableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
glUseProgram(mProgram);
glDrawArrays(GL_LINES, 0, 2);
GLint pixelValue = 0;
glReadPixels(0, 0, 1, 1, GL_RED_INTEGER, GL_INT, &pixelValue);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(vertexData[1], pixelValue);
}
// Test drawing a simple triangle strip with flat shading, and different valued vertices.
TEST_P(ProvokingVertexTest, FlatTriStrip)
{
// TODO(jmadill): Implement on the D3D back-end.
if (isD3D11())
{
std::cout << "Test disabled on D3D11." << std::endl;
return;
}
GLint vertexData[] = {1, 2, 3, 4, 5, 6};
GLfloat positionData[] = {-1.0f, -1.0f, -1.0f, 1.0f, 0.0f, -1.0f,
0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
GLint positionLocation = glGetAttribLocation(mProgram, "position");
glEnableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
glUseProgram(mProgram);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 6);
std::vector<GLint> pixelBuffer(getWindowWidth() * getWindowHeight(), 0);
glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RED_INTEGER, GL_INT,
&pixelBuffer[0]);
ASSERT_GL_NO_ERROR();
for (unsigned int triIndex = 0; triIndex < 4; ++triIndex)
{
GLfloat sumX = positionData[triIndex * 2 + 0] + positionData[triIndex * 2 + 2] +
positionData[triIndex * 2 + 4];
GLfloat sumY = positionData[triIndex * 2 + 1] + positionData[triIndex * 2 + 3] +
positionData[triIndex * 2 + 5];
float centerX = sumX / 3.0f * 0.5f + 0.5f;
float centerY = sumY / 3.0f * 0.5f + 0.5f;
unsigned int pixelX =
static_cast<unsigned int>(centerX * static_cast<GLfloat>(getWindowWidth()));
unsigned int pixelY =
static_cast<unsigned int>(centerY * static_cast<GLfloat>(getWindowHeight()));
unsigned int pixelIndex = pixelY * getWindowWidth() + pixelX;
unsigned int provokingVertexIndex = triIndex + 2;
EXPECT_EQ(vertexData[provokingVertexIndex], pixelBuffer[pixelIndex]);
}
}
// Test drawing an indexed triangle strip with flat shading and primitive restart.
TEST_P(ProvokingVertexTest, FlatTriStripPrimitiveRestart)
{
// TODO(jmadill): Implement on the D3D back-end.
if (isD3D11())
{
std::cout << "Test disabled on D3D11." << std::endl;
return;
}
GLint indexData[] = {0, 1, 2, -1, 1, 2, 3, 4, -1, 3, 4, 5};
GLint vertexData[] = {1, 2, 3, 4, 5, 6};
GLfloat positionData[] = {-1.0f, -1.0f, -1.0f, 1.0f, 0.0f, -1.0f,
0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
GLint positionLocation = glGetAttribLocation(mProgram, "position");
glEnableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
glDisable(GL_CULL_FACE);
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glUseProgram(mProgram);
glDrawElements(GL_TRIANGLE_STRIP, 12, GL_UNSIGNED_INT, indexData);
std::vector<GLint> pixelBuffer(getWindowWidth() * getWindowHeight(), 0);
glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RED_INTEGER, GL_INT,
&pixelBuffer[0]);
ASSERT_GL_NO_ERROR();
// Account for primitive restart when checking the tris.
GLint triOffsets[] = {0, 4, 5, 9};
for (unsigned int triIndex = 0; triIndex < 4; ++triIndex)
{
GLint vertexA = indexData[triOffsets[triIndex] + 0];
GLint vertexB = indexData[triOffsets[triIndex] + 1];
GLint vertexC = indexData[triOffsets[triIndex] + 2];
GLfloat sumX =
positionData[vertexA * 2] + positionData[vertexB * 2] + positionData[vertexC * 2];
GLfloat sumY = positionData[vertexA * 2 + 1] + positionData[vertexB * 2 + 1] +
positionData[vertexC * 2 + 1];
float centerX = sumX / 3.0f * 0.5f + 0.5f;
float centerY = sumY / 3.0f * 0.5f + 0.5f;
unsigned int pixelX =
static_cast<unsigned int>(centerX * static_cast<GLfloat>(getWindowWidth()));
unsigned int pixelY =
static_cast<unsigned int>(centerY * static_cast<GLfloat>(getWindowHeight()));
unsigned int pixelIndex = pixelY * getWindowWidth() + pixelX;
unsigned int provokingVertexIndex = triIndex + 2;
EXPECT_EQ(vertexData[provokingVertexIndex], pixelBuffer[pixelIndex]);
}
}
ANGLE_INSTANTIATE_TEST(ProvokingVertexTest, ES3_D3D11(), ES3_OPENGL());
} // anonymous namespace
......@@ -292,6 +292,12 @@ bool ANGLETest::isNVidia() const
return (rendererString.find("NVIDIA") != std::string::npos);
}
bool ANGLETest::isD3D11() const
{
std::string rendererString(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
return (rendererString.find("Direct3D11 vs_5_0") != std::string::npos);
}
EGLint ANGLETest::getPlatformRenderer() const
{
assert(mEGLWindow);
......
......@@ -98,6 +98,8 @@ class ANGLETest : public ::testing::TestWithParam<angle::PlatformParameters>
bool isIntel() const;
bool isAMD() const;
bool isNVidia() const;
// Note: FL9_3 is explicitly *not* considered D3D11.
bool isD3D11() const;
EGLint getPlatformRenderer() const;
private:
......
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