1 /**************************************************************************
3 * Copyright 2012 VMware, Inc.
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 **************************************************************************/
29 * Triangle Rasterization Test
31 * This tests OpenGL triangle rasterization by comparing it with a software rasteriser
33 * There are 2 components to the test;
34 * 1. Predefined sanity tests ensuring bounding box calculations are correct
35 * 2. Randomised triangle drawing to attempt to test all possible triangles
38 #include "piglit-util-gl.h"
39 #include "mersenne.hpp"
53 Vector(float x
, float y
)
66 Triangle(const Vector
& v0
, const Vector
& v1
, const Vector
& v2
)
73 Vector
& operator[](int i
)
78 const Vector
& operator[](int i
) const
87 /* Command line arguments */
89 bool break_on_fail
= false;
90 bool print_triangle
= false;
91 int random_test_count
= 100;
93 /* filling convention */
94 static enum filling_convention_t
{
103 } filling_convention
;
105 /* Fixed point format */
106 static int FIXED_SHIFT
;
107 static int FIXED_ONE
;
109 /* Default test size */
111 int fbo_height
= 256;
113 /* Piglit variables */
115 PIGLIT_GL_TEST_CONFIG_BEGIN
117 config
.supports_gl_compat_version
= 10;
119 config
.window_width
= fbo_width
;
120 config
.window_height
= fbo_height
;
121 config
.window_visual
= PIGLIT_GL_VISUAL_RGBA
| PIGLIT_GL_VISUAL_DOUBLE
;
122 config
.khr_no_error_support
= PIGLIT_NO_ERRORS
;
124 PIGLIT_GL_TEST_CONFIG_END
129 std::vector
<Triangle
> fixed_tests
;
132 /* std::algorithm min/max with 3 arguments! :D */
135 T
min(T
& a
, T
& b
, T
& c
)
137 return (a
< b
) ? std::min(a
, c
) : std::min(b
, c
);
141 T
max(T
& a
, T
& b
, T
& c
)
143 return (a
> b
) ? std::max(a
, c
) : std::max(b
, c
);
147 /* Proper rounding of float to integer */
148 int64_t iround(float v
)
157 /* Based on http://devmaster.net/forums/topic/1145-advanced-rasterization */
158 void rast_triangle(uint8_t* buffer
, uint32_t stride
, const Triangle
& tri
)
160 float center_offset
= -0.5f
;
162 /* fixed point coordinates */
163 int64_t x1
= iround(FIXED_ONE
* (tri
[0].x
+ center_offset
));
164 int64_t x2
= iround(FIXED_ONE
* (tri
[1].x
+ center_offset
));
165 int64_t x3
= iround(FIXED_ONE
* (tri
[2].x
+ center_offset
));
167 int64_t y1
= iround(FIXED_ONE
* (tri
[0].y
+ center_offset
));
168 int64_t y2
= iround(FIXED_ONE
* (tri
[1].y
+ center_offset
));
169 int64_t y3
= iround(FIXED_ONE
* (tri
[2].y
+ center_offset
));
171 /* Force correct vertex order */
172 const int64_t cross
= (x2
- x1
) * (y3
- y2
) - (y2
- y1
) * (x3
- x2
);
179 const int64_t dx12
= x1
- x2
;
180 const int64_t dx23
= x2
- x3
;
181 const int64_t dx31
= x3
- x1
;
183 const int64_t dy12
= y1
- y2
;
184 const int64_t dy23
= y2
- y3
;
185 const int64_t dy31
= y3
- y1
;
187 /* Fixed-point deltas */
188 const int64_t fdx12
= dx12
<< FIXED_SHIFT
;
189 const int64_t fdx23
= dx23
<< FIXED_SHIFT
;
190 const int64_t fdx31
= dx31
<< FIXED_SHIFT
;
192 const int64_t fdy12
= dy12
<< FIXED_SHIFT
;
193 const int64_t fdy23
= dy23
<< FIXED_SHIFT
;
194 const int64_t fdy31
= dy31
<< FIXED_SHIFT
;
196 /* Bounding rectangle */
197 int64_t minx
= std::min(x1
, x2
, x3
) >> FIXED_SHIFT
;
198 int64_t maxx
= (std::max(x1
, x2
, x3
)) >> FIXED_SHIFT
;
200 int64_t miny
= (std::min(y1
, y2
, y3
)) >> FIXED_SHIFT
;
201 int64_t maxy
= std::max(y1
, y2
, y3
) >> FIXED_SHIFT
;
203 minx
= std::max(minx
, (int64_t)0);
204 maxx
= std::min(maxx
, (int64_t)fbo_width
- 1);
206 miny
= std::max(miny
, (int64_t)0);
207 maxy
= std::min(maxy
, (int64_t)fbo_height
- 1);
209 /* Half-edge constants */
210 int64_t c1
= dy12
* x1
- dx12
* y1
;
211 int64_t c2
= dy23
* x2
- dx23
* y2
;
212 int64_t c3
= dy31
* x3
- dx31
* y3
;
214 /* Correct for filling convention */
215 switch (filling_convention
) {
217 if (dy12
> 0 || (dy12
== 0 && dx12
> 0)) c1
++;
218 if (dy23
> 0 || (dy23
== 0 && dx23
> 0)) c2
++;
219 if (dy31
> 0 || (dy31
== 0 && dx31
> 0)) c3
++;
222 if (dx12
< 0 || (dx12
== 0 && dy12
< 0)) c1
++;
223 if (dx23
< 0 || (dx23
== 0 && dy23
< 0)) c2
++;
224 if (dx31
< 0 || (dx31
== 0 && dy31
< 0)) c3
++;
227 if (dx12
< 0 || (dx12
== 0 && dy12
> 0)) c1
++;
228 if (dx23
< 0 || (dx23
== 0 && dy23
> 0)) c2
++;
229 if (dx31
< 0 || (dx31
== 0 && dy31
> 0)) c3
++;
232 if (dy12
< 0 || (dy12
== 0 && dx12
> 0)) c1
++;
233 if (dy23
< 0 || (dy23
== 0 && dx23
> 0)) c2
++;
234 if (dy31
< 0 || (dy31
== 0 && dx31
> 0)) c3
++;
237 if (dy12
< 0 || (dy12
== 0 && dx12
< 0)) c1
++;
238 if (dy23
< 0 || (dy23
== 0 && dx23
< 0)) c2
++;
239 if (dy31
< 0 || (dy31
== 0 && dx31
< 0)) c3
++;
242 if (dx12
> 0 || (dx12
== 0 && dy12
> 0)) c1
++;
243 if (dx23
> 0 || (dx23
== 0 && dy23
> 0)) c2
++;
244 if (dx31
> 0 || (dx31
== 0 && dy31
> 0)) c3
++;
247 if (dx12
> 0 || (dx12
== 0 && dy12
< 0)) c1
++;
248 if (dx23
> 0 || (dx23
== 0 && dy23
< 0)) c2
++;
249 if (dx31
> 0 || (dx31
== 0 && dy31
< 0)) c3
++;
252 if (dy12
> 0 || (dy12
== 0 && dx12
< 0)) c1
++;
253 if (dy23
> 0 || (dy23
== 0 && dx23
< 0)) c2
++;
254 if (dy31
> 0 || (dy31
== 0 && dx31
< 0)) c3
++;
258 int64_t cy1
= c1
+ dx12
* (miny
<< FIXED_SHIFT
) - dy12
* (minx
<< FIXED_SHIFT
);
259 int64_t cy2
= c2
+ dx23
* (miny
<< FIXED_SHIFT
) - dy23
* (minx
<< FIXED_SHIFT
);
260 int64_t cy3
= c3
+ dx31
* (miny
<< FIXED_SHIFT
) - dy31
* (minx
<< FIXED_SHIFT
);
262 /* Perform rasterization */
263 buffer
+= miny
* stride
;
264 for (int64_t y
= miny
; y
<= maxy
; y
++) {
269 for (int64_t x
= minx
; x
<= maxx
; x
++) {
270 if (cx1
> 0 && cx2
> 0 && cx3
> 0) {
271 ((uint32_t*)buffer
)[x
] = 0x00FF00FF;
288 /* Prints an ascii representation of the triangle */
289 void triangle_art(uint32_t* buffer
)
291 int minx
= fbo_width
- 1, miny
= fbo_height
- 1;
292 int maxx
= 0, maxy
= 0;
294 /* Find bounds so we dont have to print whole screen */
295 for (int y
= 0; y
< fbo_height
; ++y
) {
296 for (int x
= 0; x
< fbo_width
; ++x
) {
297 if (buffer
[y
*fbo_width
+ x
] & 0xFFFFFF00) {
298 if (x
< minx
) minx
= x
;
299 if (y
< miny
) miny
= y
;
300 if (x
> maxx
) maxx
= x
;
301 if (y
> maxy
) maxy
= y
;
307 if (minx
> maxx
|| miny
> maxy
)
313 /* Print an ascii representation of triangle */
314 for (int y
= maxy
; y
>= miny
; --y
) {
315 for (int x
= minx
; x
<= maxx
; ++x
) {
316 uint32_t val
= buffer
[y
*fbo_width
+ x
] & 0xFFFFFF00;
318 if (val
== 0xFF000000) {
320 } else if (val
== 0x00FF0000) {
322 } else if (val
== 0xFFFF0000) {
324 } else if (val
== 0) {
338 /* Reads buffer from OpenGL and checks for any colour other than black or yellow
339 * (black = background, yellow = both opengl AND software rast drew to that pixel)
341 uint32_t* check_triangle()
343 const float black
[] = { 0, 0, 0 };
344 const float yellow
[] = { 1, 1, 0 };
346 if (piglit_probe_rect_two_rgb(0, 0, fbo_width
, fbo_height
, black
,
350 static uint32_t* buffer
= 0;
351 if (!buffer
) buffer
= new uint32_t[fbo_width
* fbo_height
];
353 glReadPixels(0, 0, fbo_width
, fbo_height
, GL_RGBA
, GL_UNSIGNED_INT_8_8_8_8
, buffer
);
359 /* Performs test using tri */
360 GLboolean
test_triangle(const Triangle
& tri
)
362 static uint32_t* buffer
= 0;
363 if (!buffer
) buffer
= new uint32_t[fbo_width
* fbo_height
];
365 /* Clear OpenGL and software buffer */
366 glClear(GL_COLOR_BUFFER_BIT
);
367 memset(buffer
, 0, sizeof(uint32_t) * fbo_width
* fbo_height
);
369 /* Software rasterise triangle and blit it to OpenGL */
370 rast_triangle((uint8_t*)buffer
, fbo_width
* 4, tri
);
371 glDrawPixels(fbo_width
, fbo_height
, GL_RGBA
, GL_UNSIGNED_INT_8_8_8_8
, buffer
);
373 /* Draw OpenGL triangle */
374 glEnableClientState(GL_VERTEX_ARRAY
);
375 glVertexPointer(2, GL_FLOAT
, 0, tri
.v
);
376 glDrawArrays(GL_TRIANGLES
, 0, 3);
377 glDisableClientState(GL_VERTEX_ARRAY
);
379 /* Check the result and print relevant error messages */
380 if (uint32_t* result
= check_triangle()) {
381 printf("FAIL: %d. (%f, %f), (%f, %f), (%f, %f)\n", test_id
,
382 tri
[0].x
, tri
[0].y
, tri
[1].x
, tri
[1].y
, tri
[2].x
, tri
[2].y
);
384 if (print_triangle
) {
385 triangle_art(result
);
396 /* Generate a random triangle */
397 void random_triangle(Triangle
& tri
)
399 int size
= 1 << (mersenne
.value() % (log2u(fbo_width
) + 1));
401 for (int i
= 0; i
< 3; ++i
) {
402 tri
[i
].x
= (mersenne
.value() % (size
* FIXED_ONE
)) * (1.0f
/ FIXED_ONE
);
403 tri
[i
].y
= (mersenne
.value() % (size
* FIXED_ONE
)) * (1.0f
/ FIXED_ONE
);
410 /* From the OpenGL 1.4 spec page 78 (page 91 of PDF):
411 * "Special treatment is given to a fragment whose center lies on a polygon
412 * boundary edge. In such a case we require that if two polygons lie on either
413 * side of a common edge (with identical endpoints) on which a fragment center
414 * lies, then exactly one of the polygons results in the production of the
415 * fragment during rasterization."
416 * Additionally rasterization is required to be invariant under translation
417 * along either axis by a multiple of the pixel size (page 63/76).
419 * We assume that the implementation adheres to a more stringent convention in
420 * which either top, left, bottom or right edges of a triangles 'belong' to it,
421 * that is, if one of those edges intersects with a fragment center, the
422 * fragment is produced. Additionally, for 'top' and 'bottom'-type triangles
423 * either left or right vertical edges 'belong' to it. Similarly the same is
424 * true with horizontal edges and 'left' and 'right'-type triangles.
426 * For example: consider these 8 triangles centered around a fragment center:
434 * The rasterizer should produce exactly one fragment. If triangle no. 0
435 * produces the fragment, the rasterizer is said to follow the bottom-left
436 * convention (bottom because bottom horizontal edges 'belong' to the triangle
437 * and left because all left facing edges 'belong' to it).
439 * This function determines the convention by drawing the 8 triangles shown
440 * above in sub-pixel-size into 8 separate pixels and checks which
443 void get_filling_convention(void)
447 const float mid
= 0.5f
;
448 const float size
= 3.0f
/ FIXED_ONE
;
451 Vector(mid
+ size
, mid
),
452 Vector(mid
+ size
, mid
+ size
),
453 Vector(mid
, mid
+ size
),
454 Vector(mid
- size
, mid
+ size
),
455 Vector(mid
- size
, mid
),
456 Vector(mid
- size
, mid
- size
),
457 Vector(mid
, mid
- size
),
458 Vector(mid
+ size
, mid
- size
),
459 Vector(mid
+ size
, mid
),
461 const Vector
vm(mid
, mid
);
463 for (int i
= 0; i
< 8; ++i
) {
465 tests
[i
][1] = v
[i
+1];
469 glColor4f(1.0f
, 1.0f
, 1.0f
, 1.0f
);
470 glClearColor(0.0f
, 0.0f
, 0.0f
, 1.0f
);
471 glClear(GL_COLOR_BUFFER_BIT
);
472 glEnableClientState(GL_VERTEX_ARRAY
);
473 piglit_ortho_projection(1, 1, GL_FALSE
);
475 assert(piglit_width
>= 8);
476 for (int i
= 0; i
< 8; ++i
) {
477 glViewport(i
, 0, 1, 1);
479 /* Draw OpenGL triangle */
480 glVertexPointer(2, GL_FLOAT
, 0, tests
[i
].v
);
481 glDrawArrays(GL_TRIANGLES
, 0, 3);
484 glDisableClientState(GL_VERTEX_ARRAY
);
485 glViewport(0, 0, fbo_width
, fbo_height
);
486 piglit_ortho_projection(fbo_width
, fbo_height
, GL_FALSE
);
489 glReadPixels(0, 0, 8, 1, GL_RGBA
, GL_UNSIGNED_INT_8_8_8_8
, buffer
);
491 int produced_fragment_count
= 0;
492 for (int i
= 0; i
< 8; ++i
) {
493 if ((buffer
[i
] & 0xFFFFFF00) == 0xFFFFFF00) {
494 filling_convention
= (filling_convention_t
)i
;
495 produced_fragment_count
++;
499 if (produced_fragment_count
!= 1) {
500 printf("Unable to determine filling convention.\n");
501 piglit_report_result(PIGLIT_SKIP
);
512 /* If using FBO, set it up */
514 glDisable(GL_CULL_FACE
);
516 glGenTextures(1, &tex
);
517 glBindTexture(GL_TEXTURE_2D
, tex
);
518 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
519 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
520 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, fbo_width
, fbo_height
, 0, GL_BGRA
, GL_UNSIGNED_BYTE
, NULL
);
522 glGenFramebuffersEXT(1, &fb
);
523 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, fb
);
524 glViewport(0, 0, fbo_width
, fbo_height
);
526 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT
,
527 GL_COLOR_ATTACHMENT0_EXT
,
532 if (!piglit_check_gl_error(GL_NO_ERROR
))
533 piglit_report_result(PIGLIT_FAIL
);
534 assert(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT
) == GL_FRAMEBUFFER_COMPLETE_EXT
);
536 glBindTexture(GL_TEXTURE_2D
, 0);
539 get_filling_convention();
541 /* Set render state */
542 glColor4f(1.0f
, 0.0f
, 0.0f
, 1.0f
);
543 glClearColor(0.0f
, 0.0f
, 0.0f
, 1.0f
);
546 glBlendEquation(GL_FUNC_ADD
);
547 glBlendFunc(GL_ONE
, GL_ONE
);
549 glViewport(0, 0, fbo_width
, fbo_height
);
550 piglit_ortho_projection(fbo_width
, fbo_height
, GL_FALSE
);
553 GLboolean pass
= GL_TRUE
;
554 if (piglit_automatic
) {
557 printf("Running %d fixed tests\n", (int)fixed_tests
.size());
558 for (std::vector
<Triangle
>::iterator itr
= fixed_tests
.begin(); itr
!= fixed_tests
.end() && !(fail_count
&& break_on_fail
); ++itr
) {
559 if (!test_triangle(*itr
))
563 printf("Running %d random tests\n", random_test_count
);
564 for (int i
= 0; i
< random_test_count
&& !(fail_count
&& break_on_fail
); ++i
) {
566 random_triangle(tri
);
568 if (!test_triangle(tri
))
572 printf("Failed %d tests\n", fail_count
);
579 random_triangle(tri
);
580 pass
&= test_triangle(tri
);
584 /* If using FBO, draw the fbo to screen */
586 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, piglit_winsys_fbo
);
587 glViewport(0, 0, piglit_width
, piglit_height
);
588 piglit_ortho_projection(piglit_width
, piglit_height
, GL_FALSE
);
590 glTexEnvi(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_COMBINE
);
591 glTexEnvi(GL_TEXTURE_ENV
, GL_COMBINE_RGB
, GL_REPLACE
);
592 glTexEnvi(GL_TEXTURE_ENV
, GL_COMBINE_ALPHA
, GL_REPLACE
);
594 glColor4f(1.0f
, 1.0f
, 1.0f
, 1.0f
);
595 glEnable(GL_TEXTURE_2D
);
596 glBindTexture(GL_TEXTURE_2D
, tex
);
598 piglit_draw_rect_tex(0, 0, piglit_width
, piglit_height
, 0, 0, 1, 1);
600 glDisable(GL_TEXTURE_2D
);
603 piglit_present_results();
606 /* Cleanup FBO if necessary */
608 glDeleteTextures(1, &tex
);
609 glDeleteFramebuffersEXT(1, &fb
);
610 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, piglit_winsys_fbo
);
613 if (!piglit_check_gl_error(GL_NO_ERROR
))
614 piglit_report_result(PIGLIT_FAIL
);
616 return pass
? PIGLIT_PASS
: PIGLIT_FAIL
;
620 /* Create some fixed tests to test bounding box in/exclusivity,
623 * /_|_\ Tests these 4 triangles but shifting them from -1/16 to +1/16
624 * \ | / around the center point
627 void init_fixed_tests()
629 const float mid
= 0.5f
;
630 const float shift
= 1.0f
/ FIXED_ONE
;
631 const float size
= 3.0f
/ FIXED_ONE
;
634 Vector(mid
, mid
+ size
),
635 Vector(mid
, mid
- size
),
639 Vector(mid
- size
, mid
),
640 Vector(mid
+ size
, mid
),
645 /* Loop through the 4 possible triangles */
646 for (int vy
= 0; vy
< 2; ++vy
) {
647 for (int vx
= 0; vx
< 2; ++vx
) {
654 /* Loop through the x and y shifts */
655 for (int y
= -1; y
<= 1; ++y
) {
656 for (int x
= -1; x
<= 1; ++x
) {
657 Triangle shifted
= tri
;
659 for (int i
= 0; i
< 3; ++i
) {
660 shifted
[i
].x
+= x
* shift
;
661 shifted
[i
].y
+= y
* shift
;
664 fixed_tests
.push_back(shifted
);
672 /* Read command line arguments! */
674 piglit_init(int argc
, char **argv
)
676 uint32_t seed
= 0xfacebeef ^ time(NULL
);
677 GLint gl_subpixel_bits
, in_subpixel_bits
;
678 glGetIntegerv(GL_SUBPIXEL_BITS
, &gl_subpixel_bits
);
679 in_subpixel_bits
= gl_subpixel_bits
;
681 for (int i
= 1; i
< argc
; ++i
) {
682 if (strcmp(argv
[i
], "-break_on_fail") == 0){
683 break_on_fail
= true;
684 printf("Execution will stop on first fail\n");
685 } else if (strcmp(argv
[i
], "-print_triangle") == 0){
686 print_triangle
= true;
687 } else if (strcmp(argv
[i
], "-max_size") == 0){
688 glGetIntegerv(GL_MAX_TEXTURE_SIZE
, &fbo_width
);
690 fbo_height
= fbo_width
;
691 piglit_width
= fbo_width
;
692 piglit_height
= fbo_height
;
693 } else if (strcmp(argv
[i
], "-use_fbo") == 0){
695 printf("FBOs are in use\n");
696 } else if (i
+ 1 < argc
) {
697 if (strcmp(argv
[i
], "-count") == 0) {
698 random_test_count
= strtoul(argv
[++i
], NULL
, 0);
699 } else if (strcmp(argv
[i
], "-seed") == 0) {
700 seed
= strtoul(argv
[++i
], NULL
, 0);
701 } else if (strcmp(argv
[i
], "-subpixel_bits") == 0) {
702 in_subpixel_bits
= strtoul(argv
[++i
], NULL
, 0);
707 FIXED_SHIFT
= in_subpixel_bits
;
708 FIXED_ONE
= 1 << FIXED_SHIFT
;
710 printf("GL indicates %d subpixel bits, using %d subpixel bits\n",
711 gl_subpixel_bits
, in_subpixel_bits
);
712 printf("Random seed: 0x%08X\n", seed
);