[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