• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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