1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // ProvkingVertexTest:
7 //   Tests on the conformance of the provoking vertex, which applies to flat
8 //   shading and compatibility with D3D. See the section on 'flatshading'
9 //   in the ES 3 specs.
10 //
11 
12 #include "test_utils/ANGLETest.h"
13 
14 using namespace angle;
15 
16 namespace
17 {
18 
19 class ProvokingVertexTest : public ANGLETest
20 {
21   protected:
ProvokingVertexTest()22     ProvokingVertexTest()
23         : mProgram(0),
24           mFramebuffer(0),
25           mTexture(0),
26           mTransformFeedback(0),
27           mBuffer(0),
28           mIntAttribLocation(-1)
29     {
30         setWindowWidth(64);
31         setWindowHeight(64);
32         setConfigRedBits(8);
33         setConfigGreenBits(8);
34         setConfigBlueBits(8);
35         setConfigAlphaBits(8);
36         setConfigDepthBits(24);
37     }
38 
SetUp()39     void SetUp() override
40     {
41         ANGLETest::SetUp();
42 
43         const std::string &vertexShader =
44             "#version 300 es\n"
45             "in int intAttrib;\n"
46             "in vec2 position;\n"
47             "flat out int attrib;\n"
48             "void main() {\n"
49             "  gl_Position = vec4(position, 0, 1);\n"
50             "  attrib = intAttrib;\n"
51             "}";
52 
53         const std::string &fragmentShader =
54             "#version 300 es\n"
55             "flat in int attrib;\n"
56             "out int fragColor;\n"
57             "void main() {\n"
58             "  fragColor = attrib;\n"
59             "}";
60 
61         std::vector<std::string> tfVaryings;
62         tfVaryings.push_back("attrib");
63         mProgram = CompileProgramWithTransformFeedback(vertexShader, fragmentShader, tfVaryings,
64                                                        GL_SEPARATE_ATTRIBS);
65         ASSERT_NE(0u, mProgram);
66 
67         glGenTextures(1, &mTexture);
68         glBindTexture(GL_TEXTURE_2D, mTexture);
69         glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32I, getWindowWidth(), getWindowHeight());
70 
71         glGenFramebuffers(1, &mFramebuffer);
72         glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
73         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
74 
75         mIntAttribLocation = glGetAttribLocation(mProgram, "intAttrib");
76         ASSERT_NE(-1, mIntAttribLocation);
77         glEnableVertexAttribArray(mIntAttribLocation);
78 
79         ASSERT_GL_NO_ERROR();
80     }
81 
TearDown()82     void TearDown() override
83     {
84         if (mProgram != 0)
85         {
86             glDeleteProgram(mProgram);
87             mProgram = 0;
88         }
89 
90         if (mFramebuffer != 0)
91         {
92             glDeleteFramebuffers(1, &mFramebuffer);
93             mFramebuffer = 0;
94         }
95 
96         if (mTexture != 0)
97         {
98             glDeleteTextures(1, &mTexture);
99             mTexture = 0;
100         }
101 
102         if (mTransformFeedback != 0)
103         {
104             glDeleteTransformFeedbacks(1, &mTransformFeedback);
105             mTransformFeedback = 0;
106         }
107 
108         if (mBuffer != 0)
109         {
110             glDeleteBuffers(1, &mBuffer);
111             mBuffer = 0;
112         }
113 
114         ANGLETest::TearDown();
115     }
116 
117     GLuint mProgram;
118     GLuint mFramebuffer;
119     GLuint mTexture;
120     GLuint mTransformFeedback;
121     GLuint mBuffer;
122     GLint mIntAttribLocation;
123 };
124 
125 // Test drawing a simple triangle with flat shading, and different valued vertices.
TEST_P(ProvokingVertexTest,FlatTriangle)126 TEST_P(ProvokingVertexTest, FlatTriangle)
127 {
128     GLint vertexData[] = {1, 2, 3, 1, 2, 3};
129     glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
130 
131     drawQuad(mProgram, "position", 0.5f);
132 
133     GLint pixelValue = 0;
134     glReadPixels(0, 0, 1, 1, GL_RED_INTEGER, GL_INT, &pixelValue);
135 
136     ASSERT_GL_NO_ERROR();
137     EXPECT_EQ(vertexData[2], pixelValue);
138 }
139 
140 // Ensure that any provoking vertex shenanigans still gives correct vertex streams.
TEST_P(ProvokingVertexTest,FlatTriWithTransformFeedback)141 TEST_P(ProvokingVertexTest, FlatTriWithTransformFeedback)
142 {
143     // TODO(cwallez) figure out why it is broken on AMD on Mac
144 #if defined(ANGLE_PLATFORM_APPLE)
145     if (IsAMD())
146     {
147         std::cout << "Test skipped on AMD on Mac." << std::endl;
148         return;
149     }
150 #endif
151 
152     glGenTransformFeedbacks(1, &mTransformFeedback);
153     glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback);
154 
155     glGenBuffers(1, &mBuffer);
156     glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mBuffer);
157     glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 128, nullptr, GL_STREAM_DRAW);
158 
159     glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mBuffer);
160 
161     GLint vertexData[] = {1, 2, 3, 1, 2, 3};
162     glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
163 
164     glUseProgram(mProgram);
165     glBeginTransformFeedback(GL_TRIANGLES);
166     drawQuad(mProgram, "position", 0.5f);
167     glEndTransformFeedback();
168     glUseProgram(0);
169 
170     GLint pixelValue = 0;
171     glReadPixels(0, 0, 1, 1, GL_RED_INTEGER, GL_INT, &pixelValue);
172 
173     ASSERT_GL_NO_ERROR();
174     EXPECT_EQ(vertexData[2], pixelValue);
175 
176     GLvoid *mapPointer =
177         glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(int) * 6, GL_MAP_READ_BIT);
178     ASSERT_NE(nullptr, mapPointer);
179 
180     int *mappedInts = static_cast<int *>(mapPointer);
181     for (unsigned int cnt = 0; cnt < 6; ++cnt)
182     {
183         EXPECT_EQ(vertexData[cnt], mappedInts[cnt]);
184     }
185 }
186 
187 // Test drawing a simple line with flat shading, and different valued vertices.
TEST_P(ProvokingVertexTest,FlatLine)188 TEST_P(ProvokingVertexTest, FlatLine)
189 {
190     GLfloat halfPixel = 1.0f / static_cast<GLfloat>(getWindowWidth());
191 
192     GLint vertexData[]     = {1, 2};
193     GLfloat positionData[] = {-1.0f + halfPixel, -1.0f, -1.0f + halfPixel, 1.0f};
194 
195     glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
196 
197     GLint positionLocation = glGetAttribLocation(mProgram, "position");
198     glEnableVertexAttribArray(positionLocation);
199     glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
200 
201     glUseProgram(mProgram);
202     glDrawArrays(GL_LINES, 0, 2);
203 
204     GLint pixelValue = 0;
205     glReadPixels(0, 0, 1, 1, GL_RED_INTEGER, GL_INT, &pixelValue);
206 
207     ASSERT_GL_NO_ERROR();
208     EXPECT_EQ(vertexData[1], pixelValue);
209 }
210 
211 // Test drawing a simple triangle strip with flat shading, and different valued vertices.
TEST_P(ProvokingVertexTest,FlatTriStrip)212 TEST_P(ProvokingVertexTest, FlatTriStrip)
213 {
214     GLint vertexData[]     = {1, 2, 3, 4, 5, 6};
215     GLfloat positionData[] = {-1.0f, -1.0f, -1.0f, 1.0f,  0.0f, -1.0f,
216                               0.0f,  1.0f,  1.0f,  -1.0f, 1.0f, 1.0f};
217 
218     glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
219 
220     GLint positionLocation = glGetAttribLocation(mProgram, "position");
221     glEnableVertexAttribArray(positionLocation);
222     glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
223 
224     glUseProgram(mProgram);
225     glDrawArrays(GL_TRIANGLE_STRIP, 0, 6);
226 
227     std::vector<GLint> pixelBuffer(getWindowWidth() * getWindowHeight(), 0);
228     glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RED_INTEGER, GL_INT,
229                  &pixelBuffer[0]);
230 
231     ASSERT_GL_NO_ERROR();
232 
233     for (unsigned int triIndex = 0; triIndex < 4; ++triIndex)
234     {
235         GLfloat sumX = positionData[triIndex * 2 + 0] + positionData[triIndex * 2 + 2] +
236                        positionData[triIndex * 2 + 4];
237         GLfloat sumY = positionData[triIndex * 2 + 1] + positionData[triIndex * 2 + 3] +
238                        positionData[triIndex * 2 + 5];
239 
240         float centerX = sumX / 3.0f * 0.5f + 0.5f;
241         float centerY = sumY / 3.0f * 0.5f + 0.5f;
242         unsigned int pixelX =
243             static_cast<unsigned int>(centerX * static_cast<GLfloat>(getWindowWidth()));
244         unsigned int pixelY =
245             static_cast<unsigned int>(centerY * static_cast<GLfloat>(getWindowHeight()));
246         unsigned int pixelIndex = pixelY * getWindowWidth() + pixelX;
247 
248         unsigned int provokingVertexIndex = triIndex + 2;
249 
250         EXPECT_EQ(vertexData[provokingVertexIndex], pixelBuffer[pixelIndex]);
251     }
252 }
253 
254 // Test drawing an indexed triangle strip with flat shading and primitive restart.
TEST_P(ProvokingVertexTest,FlatTriStripPrimitiveRestart)255 TEST_P(ProvokingVertexTest, FlatTriStripPrimitiveRestart)
256 {
257     // TODO(jmadill): Implement on the D3D back-end.
258     if (IsD3D11())
259     {
260         std::cout << "Test disabled on D3D11." << std::endl;
261         return;
262     }
263 
264     GLint indexData[]      = {0, 1, 2, -1, 1, 2, 3, 4, -1, 3, 4, 5};
265     GLint vertexData[]     = {1, 2, 3, 4, 5, 6};
266     GLfloat positionData[] = {-1.0f, -1.0f, -1.0f, 1.0f,  0.0f, -1.0f,
267                               0.0f,  1.0f,  1.0f,  -1.0f, 1.0f, 1.0f};
268 
269     glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
270 
271     GLint positionLocation = glGetAttribLocation(mProgram, "position");
272     glEnableVertexAttribArray(positionLocation);
273     glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
274 
275     glDisable(GL_CULL_FACE);
276     glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
277     glUseProgram(mProgram);
278     glDrawElements(GL_TRIANGLE_STRIP, 12, GL_UNSIGNED_INT, indexData);
279 
280     std::vector<GLint> pixelBuffer(getWindowWidth() * getWindowHeight(), 0);
281     glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RED_INTEGER, GL_INT,
282                  &pixelBuffer[0]);
283 
284     ASSERT_GL_NO_ERROR();
285 
286     // Account for primitive restart when checking the tris.
287     GLint triOffsets[] = {0, 4, 5, 9};
288 
289     for (unsigned int triIndex = 0; triIndex < 4; ++triIndex)
290     {
291         GLint vertexA = indexData[triOffsets[triIndex] + 0];
292         GLint vertexB = indexData[triOffsets[triIndex] + 1];
293         GLint vertexC = indexData[triOffsets[triIndex] + 2];
294 
295         GLfloat sumX =
296             positionData[vertexA * 2] + positionData[vertexB * 2] + positionData[vertexC * 2];
297         GLfloat sumY = positionData[vertexA * 2 + 1] + positionData[vertexB * 2 + 1] +
298                        positionData[vertexC * 2 + 1];
299 
300         float centerX = sumX / 3.0f * 0.5f + 0.5f;
301         float centerY = sumY / 3.0f * 0.5f + 0.5f;
302         unsigned int pixelX =
303             static_cast<unsigned int>(centerX * static_cast<GLfloat>(getWindowWidth()));
304         unsigned int pixelY =
305             static_cast<unsigned int>(centerY * static_cast<GLfloat>(getWindowHeight()));
306         unsigned int pixelIndex = pixelY * getWindowWidth() + pixelX;
307 
308         unsigned int provokingVertexIndex = triIndex + 2;
309 
310         EXPECT_EQ(vertexData[provokingVertexIndex], pixelBuffer[pixelIndex]);
311     }
312 }
313 
314 ANGLE_INSTANTIATE_TEST(ProvokingVertexTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
315 
316 }  // anonymous namespace
317