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 // http://anglebug.com/4092
139 ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
140
141 // TODO(cwallez) figure out why it is broken on AMD on Mac
142 ANGLE_SKIP_TEST_IF(IsOSX() && IsAMD());
143
144 glGenTransformFeedbacks(1, &mTransformFeedback);
145 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback);
146
147 glGenBuffers(1, &mBuffer);
148 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mBuffer);
149 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 128, nullptr, GL_STREAM_DRAW);
150
151 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mBuffer);
152
153 GLint vertexData[] = {1, 2, 3, 1, 2, 3};
154 glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
155
156 glUseProgram(mProgram);
157 glBeginTransformFeedback(GL_TRIANGLES);
158 drawQuad(mProgram, "position", 0.5f);
159 glEndTransformFeedback();
160 glUseProgram(0);
161
162 GLint pixelValue[4] = {0};
163 glReadPixels(0, 0, 1, 1, GL_RGBA_INTEGER, GL_INT, &pixelValue);
164
165 ASSERT_GL_NO_ERROR();
166 EXPECT_EQ(vertexData[2], pixelValue[0]);
167
168 void *mapPointer =
169 glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(int) * 6, GL_MAP_READ_BIT);
170 ASSERT_NE(nullptr, mapPointer);
171
172 int *mappedInts = static_cast<int *>(mapPointer);
173 for (unsigned int cnt = 0; cnt < 6; ++cnt)
174 {
175 EXPECT_EQ(vertexData[cnt], mappedInts[cnt]);
176 }
177 }
178
179 // Test drawing a simple line with flat shading, and different valued vertices.
TEST_P(ProvokingVertexTest,FlatLine)180 TEST_P(ProvokingVertexTest, FlatLine)
181 {
182 // http://anglebug.com/4092
183 ANGLE_SKIP_TEST_IF((IsWindows() || IsLinux()) && IsVulkan());
184 GLfloat halfPixel = 1.0f / static_cast<GLfloat>(getWindowWidth());
185
186 GLint vertexData[] = {1, 2};
187 GLfloat positionData[] = {-1.0f + halfPixel, -1.0f, -1.0f + halfPixel, 1.0f};
188
189 glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
190
191 GLint positionLocation = glGetAttribLocation(mProgram, "position");
192 glEnableVertexAttribArray(positionLocation);
193 glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
194
195 glUseProgram(mProgram);
196 glDrawArrays(GL_LINES, 0, 2);
197
198 GLint pixelValue[4] = {0};
199 glReadPixels(0, 0, 1, 1, GL_RGBA_INTEGER, GL_INT, &pixelValue);
200
201 ASSERT_GL_NO_ERROR();
202 EXPECT_EQ(vertexData[1], pixelValue[0]);
203 }
204
205 // Test drawing a simple triangle strip with flat shading, and different valued vertices.
TEST_P(ProvokingVertexTest,FlatTriStrip)206 TEST_P(ProvokingVertexTest, FlatTriStrip)
207 {
208 GLint vertexData[] = {1, 2, 3, 4, 5, 6};
209 GLfloat positionData[] = {-1.0f, -1.0f, -1.0f, 1.0f, 0.0f, -1.0f,
210 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
211
212 glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
213
214 GLint positionLocation = glGetAttribLocation(mProgram, "position");
215 glEnableVertexAttribArray(positionLocation);
216 glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
217
218 glUseProgram(mProgram);
219 glDrawArrays(GL_TRIANGLE_STRIP, 0, 6);
220
221 std::vector<GLint> pixelBuffer(getWindowWidth() * getWindowHeight() * 4, 0);
222 glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA_INTEGER, GL_INT,
223 &pixelBuffer[0]);
224
225 ASSERT_GL_NO_ERROR();
226
227 for (unsigned int triIndex = 0; triIndex < 4; ++triIndex)
228 {
229 GLfloat sumX = positionData[triIndex * 2 + 0] + positionData[triIndex * 2 + 2] +
230 positionData[triIndex * 2 + 4];
231 GLfloat sumY = positionData[triIndex * 2 + 1] + positionData[triIndex * 2 + 3] +
232 positionData[triIndex * 2 + 5];
233
234 float centerX = sumX / 3.0f * 0.5f + 0.5f;
235 float centerY = sumY / 3.0f * 0.5f + 0.5f;
236 unsigned int pixelX =
237 static_cast<unsigned int>(centerX * static_cast<GLfloat>(getWindowWidth()));
238 unsigned int pixelY =
239 static_cast<unsigned int>(centerY * static_cast<GLfloat>(getWindowHeight()));
240 unsigned int pixelIndex = pixelY * getWindowWidth() + pixelX;
241
242 unsigned int provokingVertexIndex = triIndex + 2;
243
244 EXPECT_EQ(vertexData[provokingVertexIndex], pixelBuffer[pixelIndex * 4]);
245 }
246 }
247
248 // Test drawing an indexed triangle strip with flat shading and primitive restart.
TEST_P(ProvokingVertexTest,FlatTriStripPrimitiveRestart)249 TEST_P(ProvokingVertexTest, FlatTriStripPrimitiveRestart)
250 {
251 // TODO(jmadill): Implement on the D3D back-end.
252 ANGLE_SKIP_TEST_IF(IsD3D11());
253
254 GLint indexData[] = {0, 1, 2, -1, 1, 2, 3, 4, -1, 3, 4, 5};
255 GLint vertexData[] = {1, 2, 3, 4, 5, 6};
256 GLfloat positionData[] = {-1.0f, -1.0f, -1.0f, 1.0f, 0.0f, -1.0f,
257 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
258
259 glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
260
261 GLint positionLocation = glGetAttribLocation(mProgram, "position");
262 glEnableVertexAttribArray(positionLocation);
263 glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
264
265 glDisable(GL_CULL_FACE);
266 glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
267 glUseProgram(mProgram);
268 glDrawElements(GL_TRIANGLE_STRIP, 12, GL_UNSIGNED_INT, indexData);
269
270 std::vector<GLint> pixelBuffer(getWindowWidth() * getWindowHeight() * 4, 0);
271 glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA_INTEGER, GL_INT,
272 &pixelBuffer[0]);
273
274 ASSERT_GL_NO_ERROR();
275
276 // Account for primitive restart when checking the tris.
277 GLint triOffsets[] = {0, 4, 5, 9};
278
279 for (unsigned int triIndex = 0; triIndex < 4; ++triIndex)
280 {
281 GLint vertexA = indexData[triOffsets[triIndex] + 0];
282 GLint vertexB = indexData[triOffsets[triIndex] + 1];
283 GLint vertexC = indexData[triOffsets[triIndex] + 2];
284
285 GLfloat sumX =
286 positionData[vertexA * 2] + positionData[vertexB * 2] + positionData[vertexC * 2];
287 GLfloat sumY = positionData[vertexA * 2 + 1] + positionData[vertexB * 2 + 1] +
288 positionData[vertexC * 2 + 1];
289
290 float centerX = sumX / 3.0f * 0.5f + 0.5f;
291 float centerY = sumY / 3.0f * 0.5f + 0.5f;
292 unsigned int pixelX =
293 static_cast<unsigned int>(centerX * static_cast<GLfloat>(getWindowWidth()));
294 unsigned int pixelY =
295 static_cast<unsigned int>(centerY * static_cast<GLfloat>(getWindowHeight()));
296 unsigned int pixelIndex = pixelY * getWindowWidth() + pixelX;
297
298 unsigned int provokingVertexIndex = triIndex + 2;
299
300 EXPECT_EQ(vertexData[provokingVertexIndex], pixelBuffer[pixelIndex * 4]);
301 }
302 }
303
304 // Test with FRONT_CONVENTION if we have ANGLE_provoking_vertex.
TEST_P(ProvokingVertexTest,ANGLEProvokingVertex)305 TEST_P(ProvokingVertexTest, ANGLEProvokingVertex)
306 {
307 int32_t vertexData[] = {1, 2, 3};
308 float positionData[] = {-1.0f, -1.0f, 3.0f, -1.0f, -1.0f, 3.0f};
309
310 glEnableVertexAttribArray(mIntAttribLocation);
311 glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData);
312
313 GLint positionLocation = glGetAttribLocation(mProgram, "position");
314 glEnableVertexAttribArray(positionLocation);
315 glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData);
316
317 glUseProgram(mProgram);
318 ASSERT_GL_NO_ERROR();
319
320 const auto &fnExpectId = [&](int id) {
321 const int32_t zero[4] = {};
322 glClearBufferiv(GL_COLOR, 0, zero);
323 glDrawArrays(GL_TRIANGLES, 0, 3);
324
325 int32_t pixelValue[4] = {0};
326 glReadPixels(0, 0, 1, 1, GL_RGBA_INTEGER, GL_INT, &pixelValue);
327
328 ASSERT_GL_NO_ERROR();
329 EXPECT_EQ(vertexData[id], pixelValue[0]);
330 };
331
332 fnExpectId(2);
333
334 const bool hasExt = IsGLExtensionEnabled("GL_ANGLE_provoking_vertex");
335 if (IsD3D11())
336 {
337 EXPECT_TRUE(hasExt);
338 }
339 if (hasExt)
340 {
341 glProvokingVertexANGLE(GL_FIRST_VERTEX_CONVENTION);
342 fnExpectId(0);
343 }
344 }
345
346 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ProvokingVertexTest);
347 ANGLE_INSTANTIATE_TEST(ProvokingVertexTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
348
349 } // anonymous namespace
350