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
testSetUp()39 void testSetUp() override
40 {
41 constexpr char kVS[] =
42 "#version 300 es\n"
43 "in int intAttrib;\n"
44 "in vec2 position;\n"
45 "flat out int attrib;\n"
46 "void main() {\n"
47 " gl_Position = vec4(position, 0, 1);\n"
48 " attrib = intAttrib;\n"
49 "}";
50
51 constexpr char kFS[] =
52 "#version 300 es\n"
53 "flat in int attrib;\n"
54 "out int fragColor;\n"
55 "void main() {\n"
56 " fragColor = attrib;\n"
57 "}";
58
59 std::vector<std::string> tfVaryings;
60 tfVaryings.push_back("attrib");
61 mProgram = CompileProgramWithTransformFeedback(kVS, kFS, tfVaryings, GL_SEPARATE_ATTRIBS);
62 ASSERT_NE(0u, mProgram);
63
64 glGenTextures(1, &mTexture);
65 glBindTexture(GL_TEXTURE_2D, mTexture);
66 glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32I, getWindowWidth(), getWindowHeight());
67
68 glGenFramebuffers(1, &mFramebuffer);
69 glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
70 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
71
72 mIntAttribLocation = glGetAttribLocation(mProgram, "intAttrib");
73 ASSERT_NE(-1, mIntAttribLocation);
74 glEnableVertexAttribArray(mIntAttribLocation);
75
76 ASSERT_GL_NO_ERROR();
77 }
78
testTearDown()79 void testTearDown() override
80 {
81 if (mProgram != 0)
82 {
83 glDeleteProgram(mProgram);
84 mProgram = 0;
85 }
86
87 if (mFramebuffer != 0)
88 {
89 glDeleteFramebuffers(1, &mFramebuffer);
90 mFramebuffer = 0;
91 }
92
93 if (mTexture != 0)
94 {
95 glDeleteTextures(1, &mTexture);
96 mTexture = 0;
97 }
98
99 if (mTransformFeedback != 0)
100 {
101 glDeleteTransformFeedbacks(1, &mTransformFeedback);
102 mTransformFeedback = 0;
103 }
104
105 if (mBuffer != 0)
106 {
107 glDeleteBuffers(1, &mBuffer);
108 mBuffer = 0;
109 }
110 }
111
112 GLuint mProgram;
113 GLuint mFramebuffer;
114 GLuint mTexture;
115 GLuint mTransformFeedback;
116 GLuint mBuffer;
117 GLint mIntAttribLocation;
118 };
119
120 // Test drawing a simple triangle with flat shading, and different valued vertices.
TEST_P(ProvokingVertexTest,FlatTriangle)121 TEST_P(ProvokingVertexTest, FlatTriangle)
122 {
123 GLint vertexData[] = {1, 2, 3, 1, 2, 3};
124 glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
125
126 drawQuad(mProgram, "position", 0.5f);
127
128 GLint pixelValue[4] = {0};
129 glReadPixels(0, 0, 1, 1, GL_RGBA_INTEGER, GL_INT, &pixelValue);
130
131 ASSERT_GL_NO_ERROR();
132 EXPECT_EQ(vertexData[2], pixelValue[0]);
133 }
134
135 // Ensure that any provoking vertex shenanigans still gives correct vertex streams.
TEST_P(ProvokingVertexTest,FlatTriWithTransformFeedback)136 TEST_P(ProvokingVertexTest, FlatTriWithTransformFeedback)
137 {
138 // TODO(cwallez) figure out why it is broken on AMD on Mac
139 ANGLE_SKIP_TEST_IF(IsOSX() && IsAMD());
140
141 glGenTransformFeedbacks(1, &mTransformFeedback);
142 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback);
143
144 glGenBuffers(1, &mBuffer);
145 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mBuffer);
146 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 128, nullptr, GL_STREAM_DRAW);
147
148 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mBuffer);
149
150 GLint vertexData[] = {1, 2, 3, 1, 2, 3};
151 glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
152
153 glUseProgram(mProgram);
154 glBeginTransformFeedback(GL_TRIANGLES);
155 drawQuad(mProgram, "position", 0.5f);
156 glEndTransformFeedback();
157 glUseProgram(0);
158
159 GLint pixelValue[4] = {0};
160 glReadPixels(0, 0, 1, 1, GL_RGBA_INTEGER, GL_INT, &pixelValue);
161
162 ASSERT_GL_NO_ERROR();
163 EXPECT_EQ(vertexData[2], pixelValue[0]);
164
165 void *mapPointer =
166 glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(int) * 6, GL_MAP_READ_BIT);
167 ASSERT_NE(nullptr, mapPointer);
168
169 int *mappedInts = static_cast<int *>(mapPointer);
170 for (unsigned int cnt = 0; cnt < 6; ++cnt)
171 {
172 EXPECT_EQ(vertexData[cnt], mappedInts[cnt]);
173 }
174 }
175
176 // Test drawing a simple line with flat shading, and different valued vertices.
TEST_P(ProvokingVertexTest,FlatLine)177 TEST_P(ProvokingVertexTest, FlatLine)
178 {
179 GLfloat halfPixel = 1.0f / static_cast<GLfloat>(getWindowWidth());
180
181 GLint vertexData[] = {1, 2};
182 GLfloat positionData[] = {-1.0f + halfPixel, -1.0f, -1.0f + halfPixel, 1.0f};
183
184 glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
185
186 GLint positionLocation = glGetAttribLocation(mProgram, "position");
187 glEnableVertexAttribArray(positionLocation);
188 glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
189
190 glUseProgram(mProgram);
191 glDrawArrays(GL_LINES, 0, 2);
192
193 GLint pixelValue[4] = {0};
194 glReadPixels(0, 0, 1, 1, GL_RGBA_INTEGER, GL_INT, &pixelValue);
195
196 ASSERT_GL_NO_ERROR();
197 EXPECT_EQ(vertexData[1], pixelValue[0]);
198 }
199
200 // Test drawing a simple line with flat shading, and different valued vertices.
TEST_P(ProvokingVertexTest,FlatLineWithFirstIndex)201 TEST_P(ProvokingVertexTest, FlatLineWithFirstIndex)
202 {
203 GLfloat halfPixel = 1.0f / static_cast<GLfloat>(getWindowWidth());
204
205 GLint vertexData[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2};
206 GLfloat positionData[] = {0,
207 0,
208 0,
209 0,
210 0,
211 0,
212 0,
213 0,
214 0,
215 0,
216 0,
217 0,
218 0,
219 0,
220 0,
221 0,
222 0,
223 0,
224 0,
225 0,
226 -1.0f + halfPixel,
227 -1.0f,
228 -1.0f + halfPixel,
229 1.0f};
230
231 glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
232
233 GLint positionLocation = glGetAttribLocation(mProgram, "position");
234 glEnableVertexAttribArray(positionLocation);
235 glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
236
237 glUseProgram(mProgram);
238 glDrawArrays(GL_LINES, 10, 2);
239
240 GLint pixelValue[4] = {0};
241 glReadPixels(0, 0, 1, 1, GL_RGBA_INTEGER, GL_INT, &pixelValue);
242
243 ASSERT_GL_NO_ERROR();
244 EXPECT_EQ(vertexData[11], pixelValue[0]);
245 }
246
247 // Test drawing a simple triangle strip with flat shading, and different valued vertices.
TEST_P(ProvokingVertexTest,FlatTriStrip)248 TEST_P(ProvokingVertexTest, FlatTriStrip)
249 {
250 GLint vertexData[] = {1, 2, 3, 4, 5, 6};
251 GLfloat positionData[] = {-1.0f, -1.0f, -1.0f, 1.0f, 0.0f, -1.0f,
252 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
253
254 glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
255
256 GLint positionLocation = glGetAttribLocation(mProgram, "position");
257 glEnableVertexAttribArray(positionLocation);
258 glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
259
260 glUseProgram(mProgram);
261 glDrawArrays(GL_TRIANGLE_STRIP, 0, 6);
262
263 std::vector<GLint> pixelBuffer(getWindowWidth() * getWindowHeight() * 4, 0);
264 glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA_INTEGER, GL_INT,
265 &pixelBuffer[0]);
266
267 ASSERT_GL_NO_ERROR();
268
269 for (unsigned int triIndex = 0; triIndex < 4; ++triIndex)
270 {
271 GLfloat sumX = positionData[triIndex * 2 + 0] + positionData[triIndex * 2 + 2] +
272 positionData[triIndex * 2 + 4];
273 GLfloat sumY = positionData[triIndex * 2 + 1] + positionData[triIndex * 2 + 3] +
274 positionData[triIndex * 2 + 5];
275
276 float centerX = sumX / 3.0f * 0.5f + 0.5f;
277 float centerY = sumY / 3.0f * 0.5f + 0.5f;
278 unsigned int pixelX =
279 static_cast<unsigned int>(centerX * static_cast<GLfloat>(getWindowWidth()));
280 unsigned int pixelY =
281 static_cast<unsigned int>(centerY * static_cast<GLfloat>(getWindowHeight()));
282 unsigned int pixelIndex = pixelY * getWindowWidth() + pixelX;
283
284 unsigned int provokingVertexIndex = triIndex + 2;
285
286 EXPECT_EQ(vertexData[provokingVertexIndex], pixelBuffer[pixelIndex * 4]);
287 }
288 }
289
290 // Test drawing an indexed triangle strip with flat shading and primitive restart.
TEST_P(ProvokingVertexTest,FlatTriStripPrimitiveRestart)291 TEST_P(ProvokingVertexTest, FlatTriStripPrimitiveRestart)
292 {
293 // TODO(jmadill): Implement on the D3D back-end.
294 ANGLE_SKIP_TEST_IF(IsD3D11());
295
296 GLint indexData[] = {0, 1, 2, -1, 1, 2, 3, 4, -1, 3, 4, 5};
297 GLint vertexData[] = {1, 2, 3, 4, 5, 6};
298 GLfloat positionData[] = {-1.0f, -1.0f, -1.0f, 1.0f, 0.0f, -1.0f,
299 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
300
301 glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
302
303 GLint positionLocation = glGetAttribLocation(mProgram, "position");
304 glEnableVertexAttribArray(positionLocation);
305 glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
306
307 glDisable(GL_CULL_FACE);
308 glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
309 glUseProgram(mProgram);
310 glDrawElements(GL_TRIANGLE_STRIP, 12, GL_UNSIGNED_INT, indexData);
311
312 std::vector<GLint> pixelBuffer(getWindowWidth() * getWindowHeight() * 4, 0);
313 glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA_INTEGER, GL_INT,
314 &pixelBuffer[0]);
315
316 ASSERT_GL_NO_ERROR();
317
318 // Account for primitive restart when checking the tris.
319 GLint triOffsets[] = {0, 4, 5, 9};
320
321 for (unsigned int triIndex = 0; triIndex < 4; ++triIndex)
322 {
323 GLint vertexA = indexData[triOffsets[triIndex] + 0];
324 GLint vertexB = indexData[triOffsets[triIndex] + 1];
325 GLint vertexC = indexData[triOffsets[triIndex] + 2];
326
327 GLfloat sumX =
328 positionData[vertexA * 2] + positionData[vertexB * 2] + positionData[vertexC * 2];
329 GLfloat sumY = positionData[vertexA * 2 + 1] + positionData[vertexB * 2 + 1] +
330 positionData[vertexC * 2 + 1];
331
332 float centerX = sumX / 3.0f * 0.5f + 0.5f;
333 float centerY = sumY / 3.0f * 0.5f + 0.5f;
334 unsigned int pixelX =
335 static_cast<unsigned int>(centerX * static_cast<GLfloat>(getWindowWidth()));
336 unsigned int pixelY =
337 static_cast<unsigned int>(centerY * static_cast<GLfloat>(getWindowHeight()));
338 unsigned int pixelIndex = pixelY * getWindowWidth() + pixelX;
339
340 unsigned int provokingVertexIndex = triIndex + 2;
341
342 EXPECT_EQ(vertexData[provokingVertexIndex], pixelBuffer[pixelIndex * 4]);
343 }
344 }
345
346 // Test with FRONT_CONVENTION if we have ANGLE_provoking_vertex.
TEST_P(ProvokingVertexTest,ANGLEProvokingVertex)347 TEST_P(ProvokingVertexTest, ANGLEProvokingVertex)
348 {
349 int32_t vertexData[] = {1, 2, 3};
350 float positionData[] = {-1.0f, -1.0f, 3.0f, -1.0f, -1.0f, 3.0f};
351
352 glEnableVertexAttribArray(mIntAttribLocation);
353 glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
354
355 GLint positionLocation = glGetAttribLocation(mProgram, "position");
356 glEnableVertexAttribArray(positionLocation);
357 glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
358
359 glUseProgram(mProgram);
360 ASSERT_GL_NO_ERROR();
361
362 const auto &fnExpectId = [&](int id) {
363 const int32_t zero[4] = {};
364 glClearBufferiv(GL_COLOR, 0, zero);
365 glDrawArrays(GL_TRIANGLES, 0, 3);
366
367 int32_t pixelValue[4] = {0};
368 glReadPixels(0, 0, 1, 1, GL_RGBA_INTEGER, GL_INT, &pixelValue);
369
370 ASSERT_GL_NO_ERROR();
371 EXPECT_EQ(vertexData[id], pixelValue[0]);
372 };
373
374 fnExpectId(2);
375
376 const bool hasExt = IsGLExtensionEnabled("GL_ANGLE_provoking_vertex");
377 if (IsD3D11())
378 {
379 EXPECT_TRUE(hasExt);
380 }
381 if (hasExt)
382 {
383 glProvokingVertexANGLE(GL_FIRST_VERTEX_CONVENTION);
384 fnExpectId(0);
385 }
386 }
387
388 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ProvokingVertexTest);
389 ANGLE_INSTANTIATE_TEST(ProvokingVertexTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES(), ES3_METAL());
390
391 } // anonymous namespace
392