[Piglit] [Review Request 2/2] Added triangle rasterisation overdraw test based off Brian Paul's suggestion.

Brian Paul brianp at vmware.com
Mon May 14 08:12:15 PDT 2012


On 05/14/2012 08:49 AM, jbenton at vmware.com wrote:
> From: James Benton<jbenton at vmware.com>
>
> Draws a circle using a triangle fan from a random point using logic op xor,
> can also test clipping by drawing a circle which contains the window.
>
> Ensures all pixels are drawn only once thus testing that all triangles are
> rasterised correctly.
>
> V2: Added xor logic op method&  argument for max texture size
> ---
>   tests/general/CMakeLists.gl.txt                   |    1 +
>   tests/general/triangle-rasterisation-overdraw.cpp |  305 +++++++++++++++++++++

Another case of "rasterization" vs. "rasterisation"

Also, add the test to tests/all.tests with various invocations of the 
command line parameters.

The test looks good.  Just a couple minor things below.

Reviewed-by: Brian Paul <brianp at vmware.com>


>   2 files changed, 306 insertions(+)
>   create mode 100644 tests/general/triangle-rasterisation-overdraw.cpp
>
> diff --git a/tests/general/CMakeLists.gl.txt b/tests/general/CMakeLists.gl.txt
> index 96841c6..d17a371 100644
> --- a/tests/general/CMakeLists.gl.txt
> +++ b/tests/general/CMakeLists.gl.txt
> @@ -107,6 +107,7 @@ piglit_add_executable (texgen texgen.c)
>   piglit_add_executable (texunits texunits.c)
>   piglit_add_executable (timer_query timer_query.c)
>   piglit_add_executable (triangle-rasterisation triangle-rasterisation.cpp)
> +piglit_add_executable (triangle-rasterisation-overdraw triangle-rasterisation-overdraw.cpp)
>   piglit_add_executable (two-sided-lighting two-sided-lighting.c)
>   piglit_add_executable (two-sided-lighting-separate-specular two-sided-lighting-separate-specular.c)
>   piglit_add_executable (user-clip user-clip.c)
> diff --git a/tests/general/triangle-rasterisation-overdraw.cpp b/tests/general/triangle-rasterisation-overdraw.cpp
> new file mode 100644
> index 0000000..116d869
> --- /dev/null
> +++ b/tests/general/triangle-rasterisation-overdraw.cpp
> @@ -0,0 +1,305 @@
> +/**************************************************************************
> + *
> + * Copyright 2012 VMware, Inc.
> + * All Rights Reserved.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sub license, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the
> + * next paragraph) shall be included in all copies or substantial portions
> + * of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
> + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
> + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + *
> + **************************************************************************/
> +
> +/**
> + * Triangle Rasterisation Overdraw Test
> + *
> + * Draws a triangle fan to fill the screen and ensures every pixel was drawn only once
> + * Based on idea from Brian Paul
> + *
> + *
> + * Contains two methods of drawing to cover both clipped and unclipped triangles:
> + *
> + *   1. No-Clip: Picks a random point in the window and walks the perimeter of the window
> + *               adding vertices to the triangle fan at non integer steps.
> + *
> + *   2. Clip:    Picks a random point in the window and adds vertices to the triangle fan
> + *               around a circle that contains the entire window, thus going off screen.
> + */
> +
> +#include "piglit-util.h"
> +#include "mersenne.hpp"
> +
> +#include<time.h>
> +#include<vector>
> +#include<algorithm>
> +
> +/* Data structures */
> +struct Vector
> +{
> +   Vector()
> +   {
> +   }
> +
> +   Vector(float x, float y)
> +      : x(x), y(y)
> +   {
> +   }
> +
> +   float x, y;
> +};
> +
> +/* Command line arguments */
> +bool rect = false;
> +bool clips = false;
> +bool break_on_fail = false;
> +int random_test_count = 10;
> +
> +/* Piglit variables */
> +int piglit_width = 1000;
> +int piglit_height = 1000;
> +int piglit_window_mode = GLUT_RGB | GLUT_DOUBLE;
> +
> +/* Globals */
> +int test_id = 0;
> +Mersenne mersenne;
> +
> +
> +/* Random floating point number between 0 and 1. */
> +static inline float
> +random_float(void)
> +{
> +   const int float_range = 1<<  23;
> +   return (mersenne.value() % float_range) * (1.0 / float_range);
> +}
> +
> +/* Random float from [a to b) */
> +static inline float
> +random_float(float a, float b)
> +{
> +   return a + (b - a - 1) * random_float();
> +}
> +
> +
> +struct TestCase
> +{
> +   Vector mid;
> +   std::vector<Vector>  triangle_fan;
> +
> +   struct {
> +      int x;
> +      int y;
> +      int w;
> +      int h;
> +   } probe_rect;
> +
> +   void generate(void);
> +   bool run(void) const;
> +};
> +
> +
> +/* Generates a random triangle fan with a random origin, and contouring a rectangle or circle */
> +void TestCase::generate(void)
> +{
> +   /* Random center point */
> +   if (clips) {
> +      mid.x = random_float(-0.5*piglit_width, 1.5*piglit_width);
> +      mid.y = random_float(-0.5*piglit_height, 1.5*piglit_height);
> +   } else {
> +      mid.x = random_float(0, piglit_width);
> +      mid.y = random_float(0, piglit_height);
> +   }
> +
> +   if (clips) {
> +      probe_rect.x = 0;
> +      probe_rect.y = 0;
> +      probe_rect.w = piglit_width;
> +      probe_rect.h = piglit_height;
> +   } else {
> +      probe_rect.x = piglit_width/4;
> +      probe_rect.y = piglit_height/4;
> +      probe_rect.w = piglit_width/2;
> +      probe_rect.h = piglit_height/2;
> +   }
> +
> +   triangle_fan.clear();
> +   triangle_fan.push_back(mid);
> +
> +   if (rect) {
> +      /* Step around the window perimeter adding triangles */
> +      double perimeter = probe_rect.w*2 + probe_rect.h*2;
> +      double pos = 0.0;
> +
> +      while (pos<  perimeter) {
> +         Vector vertex;
> +
> +         if (pos<  probe_rect.w) {
> +            /* bottom */
> +            vertex.x = probe_rect.x + pos;
> +            vertex.y = probe_rect.y + 0.0f;
> +         } else if (pos<  probe_rect.w + probe_rect.h) {
> +            /* right */
> +            vertex.x = probe_rect.x + probe_rect.w;
> +            vertex.y = probe_rect.y + pos - probe_rect.w;
> +         } else if(pos<  probe_rect.w*2 + probe_rect.h) {
> +            /* top */
> +            vertex.x = probe_rect.x + probe_rect.w - (pos - (probe_rect.w + probe_rect.h));
> +            vertex.y = probe_rect.y + probe_rect.h;
> +         } else {
> +            /* left */
> +            vertex.x = probe_rect.x + 0.0f;
> +            vertex.y = probe_rect.y + probe_rect.h - (pos - (probe_rect.w*2 + probe_rect.h));
> +         }
> +
> +         triangle_fan.push_back(vertex);
> +         pos += random_float();
> +      }
> +   } else {
> +      /* Step around a circle that contains the window */
> +      double radius = (sqrt(probe_rect.w*probe_rect.w + probe_rect.h*probe_rect.h) / 2.0) + 5.0;
> +      double perimeter = 2.0 * M_PI * radius;
> +      double pos = 0.0;
> +
> +      while (pos<  perimeter) {
> +         double theta = pos / radius;
> +
> +         float x = probe_rect.x + 0.5 * probe_rect.w + cos(theta) * radius;
> +         float y = probe_rect.y + 0.5 * probe_rect.h + sin(theta) * radius;
> +
> +         triangle_fan.push_back(Vector(x, y));
> +         pos += random_float();
> +      }
> +   }
> +
> +   /* Complete the fan! */
> +   triangle_fan.push_back(triangle_fan.at(1));
> +   ++test_id;
> +}
> +
> +/* Tests a triangle fan */
> +bool TestCase::run(void) const
> +{
> +   glViewport(0, 0, piglit_width, piglit_height);
> +   piglit_ortho_projection(piglit_width, piglit_height, GL_FALSE);
> +
> +   /* Set render state */
> +   float colour[4];
> +   if (rect) {
> +      glEnable(GL_BLEND);
> +      glBlendEquation(GL_FUNC_ADD);
> +      glBlendFunc(GL_ONE, GL_ONE);
> +      colour[0] = colour[1] = colour[2] = 127.0f / 255.0f;
> +   } else {
> +      /* When contouring a circle with very small steps, some overdraw occurs
> +       * naturally, but it should cancel itself out, i.e., there should be an
> +       * odd number of overdraw inside the shape, and an even number outside.
> +       * */
> +      glEnable(GL_COLOR_LOGIC_OP);
> +      glLogicOp(GL_INVERT);

I'd consider implementing this with the stencil buffer rather than 
logicops.  Logicops might be a little flakey/missing in some drivers. 
  And if we ever want to run this test with ES/ES2 we'd have to remove 
the logicop code.

When possible, I'm trying to write piglit tests with an eye toward 
portability to other GL/ES profiles.

But it's not a big deal for now.


> +      colour[0] = colour[1] = colour[2] = 1.0f;
> +   }
> +
> +   colour[3] = 1.0f;
> +   glColor4fv(colour);
> +
> +   glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
> +   glClear(GL_COLOR_BUFFER_BIT);
> +
> +   /* Draw triangle fan */
> +   glEnableClientState(GL_VERTEX_ARRAY);
> +   glVertexPointer(2, GL_FLOAT, 0, triangle_fan.data());
> +   glDrawArrays(GL_TRIANGLE_FAN, 0, triangle_fan.size());
> +   glDisableClientState(GL_VERTEX_ARRAY);
> +
> +   if (!piglit_probe_rect_rgb(probe_rect.x, probe_rect.y, probe_rect.w, probe_rect.y, colour)) {
> +      printf("%d. Triangle Fan with %d triangles around (%f, %f)\n",
> +             test_id, (int)triangle_fan.size(), mid.x, mid.y);
> +
> +      fflush(stdout);
> +      return false;
> +   }
> +
> +   return true;
> +}
> +
> +/* Render */
> +enum piglit_result
> +piglit_display(void)
> +{
> +   /* Perform test */
> +   GLboolean pass = GL_TRUE;
> +
> +   TestCase test_case;
> +
> +   if (piglit_automatic) {
> +      int fail_count = 0;
> +
> +      printf("Running %d random tests\n", random_test_count);
> +      fflush(stdout);
> +
> +      for (int i = 0; i<  random_test_count&&  !(fail_count&&  break_on_fail); ++i) {
> +         test_case.generate();
> +         if (!test_case.run())
> +            fail_count++;
> +      }
> +
> +      printf("Failed %d random tests\n", fail_count);
> +      fflush(stdout);
> +
> +      if (fail_count)
> +         pass = GL_FALSE;
> +   } else {
> +      test_case.generate();
> +      pass&= test_case.run();
> +
> +      glutSwapBuffers();

piglit_present_results() is preferred over glutSwapBuffers.  I think I 
missed that in your other test too.


> +   }
> +
> +   assert(glGetError() == 0);
> +   return pass ? PIGLIT_PASS : PIGLIT_FAIL;
> +}
> +
> +/* Read command line arguments! */
> +void
> +piglit_init(int argc, char **argv)
> +{
> +   uint32_t seed = 0xfacebeef ^ time(NULL);
> +
> +   for (int i = 1; i<  argc; ++i) {
> +      if (strcmp(argv[i], "-break_on_fail") == 0){
> +         break_on_fail = true;
> +         printf("Execution will stop on first fail\n");
> +      } else if (strcmp(argv[i], "-rect") == 0){
> +         rect = true;
> +      } else if (strcmp(argv[i], "-max_size") == 0){
> +         glGetIntegerv(GL_MAX_TEXTURE_SIZE,&piglit_width);
> +         piglit_height = piglit_width;
> +      } else if (strcmp(argv[i], "-clip") == 0){
> +         clips = true;
> +         printf("Clipped triangles are being tested\n");
> +      } else if (i + 1<  argc) {
> +         if (strcmp(argv[i], "-count") == 0) {
> +            random_test_count = strtoul(argv[++i], NULL, 0);
> +         } else if (strcmp(argv[i], "-seed") == 0) {
> +            seed = strtoul(argv[++i], NULL, 0);
> +         }
> +      }
> +   }
> +
> +   printf("Random seed: 0x%08X\n", seed);
> +   mersenne.init(seed);
> +}



More information about the Piglit mailing list