/* * BEGIN_COPYRIGHT -*- glean -*- * * Copyright (C) 2001 Allen Akin All Rights Reserved. * Copyright (C) 2014 Intel Corporation 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, sublicense, * 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 NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS 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. * * END_COPYRIGHT */ /** @file readpix.c * * Checks to make sure glReadPixels is functioning properly. * * This test performs a sanity check of glReadPixels, using as * few other portions of the GL as possible. If this test fails, * it may be pointless to run other tests, since so many of them * depend on reading the contents of the framebuffer to determine * if they pass. * * The test works by using glClear to fill the framebuffer with a * randomly-chosen value, reading the contents of the * framebuffer, and comparing the actual contents with the * expected contents. RGB, RGBA, color index, stencil, and depth * buffers (whichever are applicable to the current rendering * context) are checked. The test passes if the actual contents * are within 1 LSB of the expected contents. */ #include "piglit-util-gl.h" #include #include #include PIGLIT_GL_TEST_CONFIG_BEGIN config.supports_gl_compat_version = 13; config.window_visual = PIGLIT_GL_VISUAL_RGBA | PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_STENCIL | PIGLIT_GL_VISUAL_DEPTH; config.khr_no_error_support = PIGLIT_NO_ERRORS; PIGLIT_GL_TEST_CONFIG_END void piglit_init(int argc, char **argv) { srand(0); } /* piglit_init */ /** Returns a float in the range [0.0, 1.0]. */ float random_float(void) { return (float) rand() / RAND_MAX; } /** * (Copied from Glean's misc.cpp.) * Utility routine to compute error, expressed in bits * Typically used to convert a floating-point error (in the range [0,1]) * to the number of bits in the representation of a color. */ double error_bits(double abs_error, int rep_bits) { double log2_error; if (abs_error <= 0.0) return 0.0; log2_error = log2(abs_error) + rep_bits; return (log2_error <= 0.0)? 0.0: log2_error; } /* error_bits */ bool check_rgba(void) { int i, x, y, j; int thresh = 1; bool pass = true; double current_error = 0.0; double err; int xerr = 0, yerr = 0; float expected[4], expected_rgba[4] = { 0 }, actual_rgba[4] = { 0 }; const int w = piglit_width; const int h = piglit_height; GLfloat *buf = malloc(h * w * 4 * sizeof *buf); GLfloat dr, dg, db, da; GLint rbits, gbits, bbits, abits; glGetIntegerv(GL_RED_BITS, &rbits); glGetIntegerv(GL_GREEN_BITS, &gbits); glGetIntegerv(GL_BLUE_BITS, &bbits); glGetIntegerv(GL_ALPHA_BITS, &abits); for (i = 0; i < 100 && pass; ++i) { /* Generate a random color and clear the color buffer: */ expected[0] = random_float(); expected[1] = random_float(); expected[2] = random_float(); expected[3] = random_float(); glClearColor(expected[0], expected[1], expected[2], expected[3]); glClear(GL_COLOR_BUFFER_BIT); /* Read the buffer: */ glReadPixels(0, 0, w, h, GL_RGBA, GL_FLOAT, buf); /* * Now compute the error for each pixel, and record the * worst one we find: */ for (y = 0; y < h; ++y) { for (x = 0; x < w; ++x) { dr = fabs(buf[y*w*4 + x*4 + 0] - expected[0]); dg = fabs(buf[y*w*4 + x*4 + 1] - expected[1]); db = fabs(buf[y*w*4 + x*4 + 2] - expected[2]); da = fabs(buf[y*w*4 + x*4 + 3] - expected[3]); err = fmax(error_bits(dr, rbits), fmax(error_bits(dg, gbits), fmax(error_bits(db, bbits), error_bits(da, abits? abits: thresh+1)))); /* * The "thresh+1" fudge above is * needed to force the error to * be greater than the threshold * in the case where there is no * alpha channel. Without it the * error would be just equal to * the threshold, and the test * would spuriously pass. */ if (err > current_error) { current_error = err; xerr = x; yerr = y; for (j = 0; j < 4; ++j) { expected_rgba[j] = expected[j]; actual_rgba[j] = buf[y*w*4 + x*4 + j]; } } } } if (current_error > thresh) pass = false; /* Show the image */ if (!piglit_automatic) { piglit_present_results(); } } if (!pass) { printf("\tRGB(A) worst-case error was %f bits at (%i, %i)\n", current_error, xerr, yerr); printf("\t\texpected (%f, %f, %f, %f)\n", expected_rgba[0], expected_rgba[1], expected_rgba[2], expected_rgba[3]); printf("\t\tgot (%f, %f, %f, %f)\n", actual_rgba[0], actual_rgba[1], actual_rgba[2], actual_rgba[3]); } free(buf); return pass; } /* check_rgba */ bool check_depth(void) { int i, x, y; int thresh = 1; bool pass = true; GLdouble expected, expected_depth = 0.0, actual, actual_depth = 0.0; const int w = piglit_width; const int h = piglit_height; GLuint *buf = malloc(h * w * sizeof *buf); double current_error = 0.0; GLfloat dd; double err; int xerr = 0, yerr = 0; GLint dbits; glGetIntegerv(GL_DEPTH_BITS, &dbits); for (i = 0; i < 100 && pass; ++i) { /* Generate a random depth and clear the depth buffer */ expected = random_float(); glClearDepth(expected); glClear(GL_DEPTH_BUFFER_BIT); /* * Because glReadPixels won't return data of type GLdouble, * there's no straightforward portable way to deal with * integer depth buffers that are deeper than 32 bits or * floating-point depth buffers that have higher precision * than a GLfloat. Since this is just a sanity check, we'll * use integer readback and settle for 32 bits at best. */ glReadPixels(0, 0, w, h, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, buf); /* * Now compute the error for each pixel, and record the * worst one we find: */ for (y = 0; y < h; ++y) { for (x = 0; x < w; ++x) { actual = buf[y*w + x]/(double)0xffffffffU; dd = fabs(actual - expected); err = error_bits(dd, dbits); if (err > current_error) { current_error = err; xerr = x; yerr = y; expected_depth = expected; actual_depth = actual; } } } if (current_error > thresh) pass = false; /* Show the image */ if (!piglit_automatic) { piglit_present_results(); } } if (!pass) { printf("\tDepth worst-case error was %f bits at (%i, %i)\n", current_error, xerr, yerr); printf("\t\texpected %f; got %f.\n", expected_depth, actual_depth); } free(buf); return pass; } /* check_depth */ /** Generate a random number with the number of bits specified */ unsigned int random_bits(unsigned int bits) { assert(bits <= 32); assert(bits > 0); if (bits == 32) return rand(); else return rand() % (1 << bits); } bool check_stencil(void) { int i, x = 0, y = 0; bool pass = true; const int w = piglit_width; const int h = piglit_height; GLuint *buf = malloc(h * w * sizeof *buf); GLuint expected = 0; GLint sbits; glGetIntegerv(GL_STENCIL_BITS, &sbits); pass &= piglit_check_gl_error(GL_NO_ERROR); for (i = 0; i < 100 && pass; ++i) { expected = random_bits(sbits); glClearStencil(expected); pass &= piglit_check_gl_error(GL_NO_ERROR); glClear(GL_STENCIL_BUFFER_BIT); pass &= piglit_check_gl_error(GL_NO_ERROR); glReadPixels(0, 0, w, h, GL_STENCIL_INDEX, GL_UNSIGNED_INT, buf); pass &= piglit_check_gl_error(GL_NO_ERROR); for (y = 0; y < h && pass; ++y) { for (x = 0; x < w; ++x) { if (buf[y*w + x] != expected) { pass = false; break; } } } /* Show the image */ if (!piglit_automatic) { piglit_present_results(); } } if (!pass) { printf("\tStencil failed at (%i, %i).\n", x, y); printf("\t\tExpected %i; got %i.\n", expected, buf[y*w + x]); } free(buf); return pass; } /* check_stencil */ enum piglit_result piglit_display(void) { /* * Many (if not most) other tests need to read the contents of * the framebuffer to determine if the correct image has been * drawn. Obviously this is a waste of time if the basic * functionality of glReadPixels isn't working. * * This test does a "sanity" check of glReadPixels. Using as * little of the GL as practicable, it writes a random value * in the framebuffer, reads it, and compares the value read * with the value written. */ bool pass = true; glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); glPixelStorei(GL_PACK_LSB_FIRST, GL_FALSE); glPixelStorei(GL_PACK_ROW_LENGTH, 0); glPixelStorei(GL_PACK_SKIP_ROWS, 0); glPixelStorei(GL_PACK_SKIP_PIXELS, 0); glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelTransferi(GL_MAP_COLOR, GL_FALSE); glPixelTransferi(GL_MAP_STENCIL, GL_FALSE); glPixelTransferi(GL_INDEX_SHIFT, 0); glPixelTransferi(GL_INDEX_OFFSET, 0); glPixelTransferf(GL_RED_SCALE, 1.0); glPixelTransferf(GL_GREEN_SCALE, 1.0); glPixelTransferf(GL_BLUE_SCALE, 1.0); glPixelTransferf(GL_ALPHA_SCALE, 1.0); glPixelTransferf(GL_DEPTH_SCALE, 1.0); glPixelTransferf(GL_RED_BIAS, 0.0); glPixelTransferf(GL_GREEN_BIAS, 0.0); glPixelTransferf(GL_BLUE_BIAS, 0.0); glPixelTransferf(GL_ALPHA_BIAS, 0.0); glPixelTransferf(GL_DEPTH_BIAS, 0.0); glDisable(GL_SCISSOR_TEST); glDisable(GL_DITHER); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); glStencilMask(~0); pass &= check_rgba(); pass &= check_depth(); pass &= check_stencil(); return pass ? PIGLIT_PASS : PIGLIT_FAIL; } /* piglit_display */