1 /*
2 * Copyright © 2009, 2013 VMware, Inc.
3 * Copyright © 2015 Intel Corporation
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 /*
26 * Test that the correct provoking vertex is used when a tri/quad/polygon
27 * is clipped for glShadeModel(GL_FLAT).
28 *
29 * This is based on the glean clipFlat test.
30 *
31 * Test with glDrawArrays and glBegin/End. Test GL_CCW and GL_CW winding.
32 * Back-face polygon culling is enabled so if the winding order of any
33 * primitive is incorrect, nothing may be drawn.
34 *
35 * XXX We should also test with two-sided lighting.
36 *
37 * If GL_ARB/EXT_provoking_vertex is supported, that feature is tested as well.
38 *
39 * Author: Brian Paul
40 */
41
42
43 #include "piglit-util-gl.h"
44
45 PIGLIT_GL_TEST_CONFIG_BEGIN
46 config.supports_gl_compat_version = 10;
47 config.window_visual = PIGLIT_GL_VISUAL_RGB | PIGLIT_GL_VISUAL_DOUBLE;
48 config.khr_no_error_support = PIGLIT_NO_ERRORS;
49 PIGLIT_GL_TEST_CONFIG_END
50
51
52 /* Note: all correctly rendered tris/quad/polygons will be green.
53 * Any other color indicates that the wrong vertex color was used.
54 */
55
56 // GL_TRIANGLES: provoking vertex = last of tri
57 static const GLfloat TriVerts[6][5] =
58 {
59 // R G B X Y
60 { 1, 0, 0, -1, -1 },
61 { 0, 0, 1, 1, -1 },
62 { 0, 1, 0, 1, 1 }, // PV
63
64 { 0, 0, 1, 1, 1 },
65 { 1, 0, 0, -1, 1 },
66 { 0, 1, 0, -1, -1 } // PV
67 };
68
69 // GL_TRIANGLES: first provoking vertex
70 static const GLfloat TriVertsFirstPV[6][5] =
71 {
72 { 0, 1, 0, 1, 1 }, // PV
73 { 1, 0, 0, -1, -1 },
74 { 0, 0, 1, 1, -1 },
75
76 { 0, 1, 0, -1, -1 }, // PV
77 { 0, 0, 1, 1, 1 },
78 { 1, 0, 0, -1, 1 }
79 };
80
81
82 // GL_TRIANGLE_STRIP: provoking vertex = last of tri
83 static const GLfloat TriStripVerts[6][5] =
84 {
85 { 1, 0, 0, -1, -1 },
86 { 0, 0, 1, 1, -1 },
87 { 0, 1, 0, -1, 0 }, // PV
88 { 0, 1, 0, 1, 0 }, // PV
89 { 0, 1, 0, -1, 1 }, // PV
90 { 0, 1, 0, 1, 1 } // PV
91 };
92
93 // GL_TRIANGLE_STRIP: first provoking vertex
94 static const GLfloat TriStripVertsFirstPV[6][5] =
95 {
96 { 0, 1, 0, -1, -1 }, // PV
97 { 0, 1, 0, 1, -1 }, // PV
98 { 0, 1, 0, -1, 0 }, // PV
99 { 0, 1, 0, 1, 0 }, // PV
100 { 1, 0, 0, -1, 1 },
101 { 0, 0, 1, 1, 1 }
102 };
103
104
105 // GL_TRIANGLE_FAN: provoking vertex = last of tri
106 static const GLfloat TriFanVerts[4][5] =
107 {
108 { 1, 0, 0, -1, -1 },
109 { 0, 0, 1, 1, -1 },
110 { 0, 1, 0, 1, 1 }, // PV
111 { 0, 1, 0, -1, 1 } // PV
112 };
113
114 // GL_TRIANGLE_FAN: first provoking vertex
115 static const GLfloat TriFanVertsFirstPV[4][5] =
116 {
117 { 0, 0, 1, 1, -1 },
118 { 0, 1, 0, 1, 1 }, // PV
119 { 0, 1, 0, -1, 1 }, // PV
120 { 1, 0, 0, -1, -1 }
121 };
122
123
124 // GL_QUADS: provoking vertex = last of quad
125 static const GLfloat QuadVerts[4][5] =
126 {
127 { 1, 0, 0, -1, -1 },
128 { 0, 0, 1, 1, -1 },
129 { 1, 1, 0, 1, 1 },
130 { 0, 1, 0, -1, 1 } // PV
131 };
132
133 // GL_QUADS: first provoking vertex
134 static const GLfloat QuadVertsFirstPV[4][5] =
135 {
136 { 0, 1, 0, -1, 1 }, // PV
137 { 1, 0, 0, -1, -1 },
138 { 0, 0, 1, 1, -1 },
139 { 1, 1, 0, 1, 1 }
140 };
141
142
143 // GL_QUAD_STRIP: provoking vertex = last of quad
144 static const GLfloat QuadStripVerts[6][5] =
145 {
146 { 1, 0, 0, -1, -1 },
147 { 0, 0, 1, 1, -1 },
148 { 1, 1, 0, -1, 0 },
149 { 0, 1, 0, 1, 0 }, // PV
150 { 1, 1, 0, -1, 1 },
151 { 0, 1, 0, 1, 1 } // PV
152 };
153
154 // GL_QUAD_STRIP: first provoking vertex
155 static const GLfloat QuadStripVertsFirstPV[6][5] =
156 {
157 { 0, 1, 0, -1, -1 }, // PV
158 { 1, 1, 0, 1, -1 },
159 { 0, 1, 0, -1, 0 }, // PV
160 { 1, 0, 0, 1, 0 },
161 { 0, 0, 1, -1, 1 },
162 { 1, 0, 0, 1, 1 }
163 };
164
165
166 // GL_POLYGON: provoking vertex = first vertex
167 static const GLfloat PolygonVerts[4][5] =
168 {
169 { 0, 1, 0, -1, -1 }, // PV
170 { 1, 0, 0, 1, -1 },
171 { 0, 0, 1, 1, 1 },
172 { 1, 1, 0, -1, 1 }
173 };
174
175
176 enum draw_mode {
177 BEGIN_END,
178 DRAW_ARRAYS,
179 DRAW_ELEMENTS,
180 NUM_DRAW_MODES
181 };
182
183
184 static bool provoking_vertex_first;
185 static bool quads_follows_pv_convention;
186 static bool testing_first_pv;
187
188
189 void
piglit_init(int argc,char ** argv)190 piglit_init(int argc, char **argv)
191 {
192 glMatrixMode(GL_PROJECTION);
193 glLoadIdentity();
194 glOrtho(-1.25, 1.25, -1.25, 1.25, -1, 1);
195 glMatrixMode(GL_MODELVIEW);
196 glLoadIdentity();
197
198 glShadeModel(GL_FLAT);
199 glPixelStorei(GL_PACK_ALIGNMENT, 1);
200
201 glFrontFace(GL_CW);
202 glCullFace(GL_FRONT);
203 glEnable(GL_CULL_FACE);
204
205 if (piglit_is_extension_supported("GL_ARB_provoking_vertex")) {
206 provoking_vertex_first = true;
207 }
208 else if (piglit_is_extension_supported("GL_EXT_provoking_vertex")) {
209 provoking_vertex_first = true;
210 }
211
212 printf("Have GL_ARB/EXT_provoking_vertex: %s\n",
213 provoking_vertex_first ? "yes" : "no");
214
215 if (provoking_vertex_first) {
216 GLboolean k;
217 glGetBooleanv(GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT, &k);
218 quads_follows_pv_convention = k;
219
220 printf("Quads follow provoking vertex convention: %s\n",
221 k ? "yes" : "no");
222 }
223 }
224
225
226 // Draw with glDrawArrays()
227 static void
drawArrays(GLenum mode,const GLfloat * verts,GLuint count)228 drawArrays(GLenum mode, const GLfloat *verts, GLuint count)
229 {
230 glColorPointer(3, GL_FLOAT, 5 * sizeof(GLfloat), verts + 0);
231 glVertexPointer(2, GL_FLOAT, 5 * sizeof(GLfloat), verts + 3);
232 glEnableClientState(GL_COLOR_ARRAY);
233 glEnableClientState(GL_VERTEX_ARRAY);
234
235 glDrawArrays(mode, 0, count);
236
237 glDisableClientState(GL_COLOR_ARRAY);
238 glDisableClientState(GL_VERTEX_ARRAY);
239 }
240
241
242 // Draw with glDrawElements()
243 static void
drawElements(GLenum mode,const GLfloat * verts,GLuint count)244 drawElements(GLenum mode, const GLfloat *verts, GLuint count)
245 {
246 static const GLuint elements[6] = { 0, 1, 2, 3, 4, 5 };
247 glColorPointer(3, GL_FLOAT, 5 * sizeof(GLfloat), verts + 0);
248 glVertexPointer(2, GL_FLOAT, 5 * sizeof(GLfloat), verts + 3);
249 glEnableClientState(GL_COLOR_ARRAY);
250 glEnableClientState(GL_VERTEX_ARRAY);
251
252 assert(count <= ARRAY_SIZE(elements));
253
254 glDrawElements(mode, count, GL_UNSIGNED_INT, elements);
255
256 glDisableClientState(GL_COLOR_ARRAY);
257 glDisableClientState(GL_VERTEX_ARRAY);
258 }
259
260
261 // Draw with glBegin/End()
262 static void
drawBeginEnd(GLenum mode,const GLfloat * verts,GLuint count)263 drawBeginEnd(GLenum mode, const GLfloat *verts, GLuint count)
264 {
265 GLuint i;
266
267 glBegin(mode);
268 for (i = 0; i < count; i++) {
269 glColor3fv(verts + i * 5);
270 glVertex2fv(verts + i * 5 + 3);
271 }
272 glEnd();
273 }
274
275
276 // Read pixels and check pixels. All pixels should be green or black.
277 // Any other color indicates a failure.
278 static bool
checkResult(GLfloat badColor[3])279 checkResult(GLfloat badColor[3])
280 {
281 GLubyte *image;
282 GLuint i, j;
283 bool anyGreen = false;
284
285 image = malloc(piglit_width * piglit_height * 3);
286
287 badColor[0] = badColor[1] = badColor[2] = 0.0f;
288
289 glReadPixels(0, 0, piglit_width, piglit_height,
290 GL_RGB, GL_UNSIGNED_BYTE, image);
291
292 if (!piglit_automatic)
293 piglit_present_results();
294
295 for (i = 0; i < piglit_height; i++) {
296 for (j = 0; j < piglit_width; j++) {
297 GLuint k = (i * piglit_width + j) * 3;
298
299 if (image[k + 0] == 0 &&
300 image[k + 1] == 0 &&
301 image[k + 2] == 0) {
302 // black - OK
303 }
304 else if (image[k + 0] == 0 &&
305 image[k + 1] >= 254 &&
306 image[k + 2] == 0) {
307 // green - OK
308 anyGreen = true;
309 }
310 else {
311 // any other color = failure
312 badColor[0] = image[k + 0] / 255.0;
313 badColor[1] = image[k + 1] / 255.0;
314 badColor[2] = image[k + 2] / 255.0;
315 free(image);
316 return false;
317 }
318 }
319 }
320
321 free(image);
322
323 return anyGreen;
324 }
325
326
327 char *
calcQuadrant(GLfloat x,GLfloat y)328 calcQuadrant(GLfloat x, GLfloat y)
329 {
330 const char *strx, *stry;
331 char *ret;
332 int _x, _y;
333 _x = (int) x;
334 _y = (int) y;
335
336 switch (_x) {
337 case -1:
338 strx = "left";
339 break;
340 case 0:
341 strx = "center";
342 break;
343 case 1:
344 strx = "right";
345 break;
346 default:
347 assert(0);
348 }
349
350 switch (_y) {
351 case 1:
352 stry = "top";
353 break;
354 case 0:
355 stry = "middle";
356 break;
357 case -1:
358 stry = "bottom";
359 break;
360 default:
361 assert(0);
362 }
363
364 (void)!asprintf(&ret, "%s %s", strx, stry);
365 return ret;
366 }
367
368
369 static void
reportSubtest(GLenum mode,int drawMode,const GLenum order,const GLenum poly_mode,const GLfloat badColor[3],GLfloat x,GLfloat y,bool pass)370 reportSubtest(GLenum mode, int drawMode,
371 const GLenum order, const GLenum poly_mode,
372 const GLfloat badColor[3], GLfloat x, GLfloat y,
373 bool pass)
374 {
375 const char *m, *d, *f, *p;
376 char *q;
377
378 m = piglit_get_prim_name(mode);
379
380 switch (drawMode) {
381 case BEGIN_END:
382 d = "glBegin/End";
383 break;
384 case DRAW_ARRAYS:
385 d = "glDrawArrays";
386 break;
387 case DRAW_ELEMENTS:
388 d = "glDrawElements";
389 break;
390 default:
391 assert(0);
392 d = "???";
393 }
394
395 f = piglit_get_gl_enum_name(order);
396
397 p = piglit_get_gl_enum_name(poly_mode);
398
399 q = calcQuadrant(x, y);
400
401 if (!pass) {
402 printf("clipflat: Failure for %s(%s), glFrontFace(%s), "
403 "glPolygonMode(%s), quadrant: %s\n",
404 d, m, f, p, q);
405
406 if (testing_first_pv) {
407 printf("\tGL_EXT_provoking_vertex test: "
408 "GL_FIRST_VERTEX_CONVENTION_EXT mode\n");
409 }
410
411 printf("Expected color (0, 1, 0) but found (%g, %g, %g)\n",
412 badColor[0], badColor[1], badColor[2]);
413 printf("\n");
414 }
415
416 piglit_report_subtest_result(pass ? PIGLIT_PASS : PIGLIT_FAIL,
417 "%s(%s), glFrontFace(%s), glPolygonMode(%s), "
418 "quadrant: %s",
419 d, m, f, p, q);
420
421 free(q);
422 }
423
424
425 // Test a particular primitive mode for one drawing mode, filled/unfilled
426 // state and CW/CCW winding.
427 static bool
testPrimCombo(GLenum mode,const GLfloat * verts,GLuint count,bool fill,enum draw_mode drawMode,GLuint facing)428 testPrimCombo(GLenum mode, const GLfloat *verts, GLuint count,
429 bool fill, enum draw_mode drawMode, GLuint facing)
430 {
431 GLfloat x, y;
432 bool pass = true;
433 const GLenum poly_mode = fill ? GL_LINE : GL_FILL;
434 const GLenum order = facing ? GL_CW : GL_CCW;
435 const GLenum cull_face = facing ? GL_FRONT : GL_BACK;
436
437 glPolygonMode(GL_FRONT_AND_BACK, poly_mode);
438
439 glFrontFace(order);
440 glCullFace(cull_face);
441
442 // Position the geometry at 9 different locations to test
443 // clipping against the left, right, bottom and top edges of
444 // the window.
445 // Only the center location will be unclipped.
446 for (y = -1.0; y <= 1.0; y += 1.0) {
447 for (x = -1.0; x <= 1.0; x += 1.0) {
448 bool quad_pass;
449 GLfloat badColor[3];
450
451 glPushMatrix();
452 glTranslatef(x, y, 0.0);
453
454 glClear(GL_COLOR_BUFFER_BIT);
455
456 switch (drawMode) {
457 case BEGIN_END:
458 drawBeginEnd(mode, verts, count);
459 break;
460 case DRAW_ARRAYS:
461 drawArrays(mode, verts, count);
462 break;
463 case DRAW_ELEMENTS:
464 drawElements(mode, verts, count);
465 break;
466 default:
467 assert(0);
468 }
469
470 glPopMatrix();
471
472 quad_pass = checkResult(badColor);
473 pass = pass && quad_pass;
474 reportSubtest(mode, drawMode, order, poly_mode,
475 badColor, x, y, quad_pass);
476 }
477 }
478
479 return pass;
480 }
481
482
483 // Test a particular primitive mode for all drawing modes, filled/unfilled
484 // and CW/CCW winding.
485 static bool
testPrim(GLenum mode,const GLfloat * verts,GLuint count)486 testPrim(GLenum mode, const GLfloat *verts, GLuint count)
487 {
488 GLuint facing, fill;
489 int drawMode;
490 bool pass = true;
491
492 // Loop over polygon mode: filled vs. outline
493 for (fill = 0; fill < 2; fill++) {
494
495 // Loop over drawing mode: glBegin/End vs glDrawArrays vs glDrawElements
496 for (drawMode = 0; drawMode < NUM_DRAW_MODES; drawMode++) {
497
498 // Loop over CW vs. CCW winding (should make no difference)
499 for (facing = 0; facing < 2; facing++) {
500 pass = testPrimCombo(mode, verts, count,
501 fill, drawMode, facing) && pass;
502 }
503 }
504 }
505 return pass;
506 }
507
508
509 enum piglit_result
piglit_display(void)510 piglit_display(void)
511 {
512 bool pass = true;
513
514 testing_first_pv = false;
515
516 pass = testPrim(GL_TRIANGLES,
517 (GLfloat *) TriVerts,
518 ARRAY_SIZE(TriVerts)) && pass;
519
520 pass = testPrim(GL_TRIANGLE_STRIP,
521 (GLfloat *) TriStripVerts,
522 ARRAY_SIZE(TriStripVerts)) && pass;
523
524 pass = testPrim(GL_TRIANGLE_FAN,
525 (GLfloat *) TriFanVerts,
526 ARRAY_SIZE(TriFanVerts)) && pass;
527
528 pass = testPrim(GL_QUADS,
529 (GLfloat *) QuadVerts,
530 ARRAY_SIZE(QuadVerts)) && pass;
531
532 pass = testPrim(GL_QUAD_STRIP,
533 (GLfloat *) QuadStripVerts,
534 ARRAY_SIZE(QuadStripVerts)) && pass;
535
536 pass = testPrim(GL_POLYGON,
537 (GLfloat *) PolygonVerts,
538 ARRAY_SIZE(PolygonVerts)) && pass;
539
540 if (provoking_vertex_first) {
541 glProvokingVertex(GL_FIRST_VERTEX_CONVENTION_EXT);
542 testing_first_pv = true;
543
544 pass = testPrim(GL_TRIANGLES,
545 (GLfloat *) TriVertsFirstPV,
546 ARRAY_SIZE(TriVertsFirstPV)) && pass;
547
548 pass = testPrim(GL_TRIANGLE_STRIP,
549 (GLfloat *) TriStripVertsFirstPV,
550 ARRAY_SIZE(TriStripVertsFirstPV)) && pass;
551
552 pass = testPrim(GL_TRIANGLE_FAN,
553 (GLfloat *) TriFanVertsFirstPV,
554 ARRAY_SIZE(TriFanVertsFirstPV)) && pass;
555
556 if (quads_follows_pv_convention)
557 pass = testPrim(GL_QUADS,
558 (GLfloat *) QuadVertsFirstPV,
559 ARRAY_SIZE(QuadVertsFirstPV)) && pass;
560 else
561 pass = testPrim(GL_QUADS,
562 (GLfloat *) QuadVerts,
563 ARRAY_SIZE(QuadVerts)) && pass;
564
565 if (quads_follows_pv_convention)
566 pass = testPrim(GL_QUAD_STRIP,
567 (GLfloat *) QuadStripVertsFirstPV,
568 ARRAY_SIZE(QuadStripVertsFirstPV)) && pass;
569 else
570 pass = testPrim(GL_QUAD_STRIP,
571 (GLfloat *) QuadStripVerts,
572 ARRAY_SIZE(QuadStripVerts)) && pass;
573
574 pass = testPrim(GL_POLYGON,
575 (GLfloat *) PolygonVerts,
576 ARRAY_SIZE(PolygonVerts)) && pass;
577 }
578
579 return pass ? PIGLIT_PASS : PIGLIT_FAIL;
580 }
581