• 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     // 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