From f209c4260ddde1648eb176aadfea9c3b37abebdc Mon Sep 17 00:00:00 2001 From: Daniel J Sebald Date: Wed, 25 Mar 2015 11:27:53 -0500 Subject: [PATCH] pixelzoom: New test for glPixelZoom()/glDrawPixels() validation This test brings to light inconsistencies in both legacy and Gallium swrast drivers when fractionally zooming large images. --- tests/spec/gl-1.0/CMakeLists.gl.txt | 1 + tests/spec/gl-1.0/pixelzoom.c | 514 +++++++++++++++++++++++++++++++++++ 2 files changed, 515 insertions(+), 0 deletions(-) create mode 100644 tests/spec/gl-1.0/pixelzoom.c diff --git a/tests/spec/gl-1.0/CMakeLists.gl.txt b/tests/spec/gl-1.0/CMakeLists.gl.txt index e2e6642..e619a6a 100644 --- a/tests/spec/gl-1.0/CMakeLists.gl.txt +++ b/tests/spec/gl-1.0/CMakeLists.gl.txt @@ -25,5 +25,6 @@ piglit_add_executable (gl-1.0-ortho-pos orthpos.c) piglit_add_executable (gl-1.0-fpexceptions fpexceptions.c) piglit_add_executable (gl-1.0-readpixsanity readpix.c) piglit_add_executable (gl-1.0-logicop logicop.c) +piglit_add_executable (gl-1.0-pixelzoom pixelzoom.c) # vim: ft=cmake: diff --git a/tests/spec/gl-1.0/pixelzoom.c b/tests/spec/gl-1.0/pixelzoom.c new file mode 100644 index 0000000..8f06de5 --- /dev/null +++ b/tests/spec/gl-1.0/pixelzoom.c @@ -0,0 +1,514 @@ +/* + * Copyright (C) 2015 Dan Sebald + * + * 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. + */ + +/** + * \file pixelzoom.c + * + * Test that a reasonably valid zoomed image is created, especially + * in the case that the dimension of the input image exceeds that + * of the texture maximum size, i.e., GL_MAX_TEXTURE_SIZE. Do the + * following for positive xfactor/yfactor and negative xfactor/yfactor: + * + * 1. Create a ramp gradient and fractionally zoom to display size. + * Check that frame buffer is monotonically increasing. + * 2. Choose the zoom factor xfactor/yfactor such that the last pixel + * of the frame buffer is 1.0. Check whether that is the case. + * 3. With an input image all of the same intensity/color display + * alternating image intensities (0.25 and 0.75) at successively + * smaller zoom factors so that one pixel is left behind from each + * drawing underneath the successor. The result should be alternating + * color lines. Do this for both a magnification and a condensation. + */ + +#include "piglit-util-gl.h" + +PIGLIT_GL_TEST_CONFIG_BEGIN + + config.supports_gl_compat_version = 10; + config.window_visual = PIGLIT_GL_VISUAL_RGB | PIGLIT_GL_VISUAL_DOUBLE; + +PIGLIT_GL_TEST_CONFIG_END + +GLint prog, color, zflip; + +enum piglit_result +piglit_display(void) +{ + bool pass_posmonx = true, pass_posedgex = true, pass_posoverunderrunx = true; + bool pass_negmonx = true, pass_negedgex = true, pass_negoverunderrunx = true; + bool pass_posmony = true, pass_posedgey = true, pass_posoverunderruny = true; + bool pass_negmony = true, pass_negedgey = true, pass_negoverunderruny = true; + + GLint glmaxtexturesize; + int large_dim_size; + GLfloat *pixels; + +#define SMALL_DIM_SIZE 20 + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glmaxtexturesize); + large_dim_size = 5 * glmaxtexturesize / 2; + + pixels = (GLfloat *) malloc(sizeof(GLfloat) * large_dim_size * SMALL_DIM_SIZE * 4); + + if (pixels) { + + int i; + GLfloat background_color[4] = {1.0, 0.0, 0.0, 1.0}; + + /* Positive Monotonicity X Test: Place a gradient in one direction into the + * input image and the zoomed resultant image must be a gradient. + */ + glRasterPos2i(0, 0); + glPixelZoom(piglit_width, piglit_height); + glDrawPixels(1, 1, GL_RGBA, GL_FLOAT, background_color); + for (i = 0; i < SMALL_DIM_SIZE; i++) { + int j; + long int n = 4 * large_dim_size * i; + for (j = 0; j < large_dim_size; j++) { + GLfloat pval = j / (GLfloat) (large_dim_size - 1); + pixels[n++] = pval; + pixels[n++] = pval; + pixels[n++] = pval; + pixels[n++] = 1.0; + } + } + glRasterPos2i(0, 0); + glPixelZoom((piglit_width - 1) / (GLfloat) (large_dim_size - 1), (piglit_height - 1) / (GLfloat) (SMALL_DIM_SIZE - 1)); + glDrawPixels(large_dim_size, SMALL_DIM_SIZE, GL_RGBA, GL_FLOAT, pixels); + for (i = 0; i < piglit_height; i++) { + int j; + GLfloat prev_pval = 0.0; + for (j = 0; j < piglit_width; j++) { + GLfloat pval[4]; + glReadPixels(j, i, 1, 1, GL_RGBA, GL_FLOAT, pval); + if ((pval[0] + pval[1] + pval[2]) < prev_pval) { + pass_posmonx = false; + break; + } + prev_pval = (pval[0] + pval[1] + pval[2]); + } + } + + /* Positive Edge Value X Test: The positive zoom xfactor was chosen such + * that the last pixel of the output image should equal 1.0. + * + * Given xr + n * xfactor <= pix_center < xr + (n+1) * xfactor, + * and N is input image width, for pixel piglit_width-1 to be in + * the box associated with 1.0 requires + * + * 0 + (N-1) * xfactor = pixel_width-1 + * + * or + * + * xfactor = (pixel_width - 1) / (N - 1) + */ + for (i = 0; i < piglit_height; i++) { + GLfloat pval[4]; + glReadPixels(0, i, 1, 1, GL_RGBA, GL_FLOAT, pval); + if ((pval[0] + pval[1] + pval[2]) != 0.0) { + pass_posedgex = false; + break; + } + glReadPixels(piglit_width - 1, i, 1, 1, GL_RGBA, GL_FLOAT, pval); + if ((pval[0] + pval[1] + pval[2]) != 3.0) { + pass_posedgex = false; + break; + } + } + + /* Negative Monotonicity X Test: Use the same gradient input image as for + * the positive monotonicity test. + */ + glRasterPos2i(0, 0); + glPixelZoom(piglit_width, piglit_height); + glDrawPixels(1, 1, GL_RGBA, GL_FLOAT, background_color); + glRasterPos2i(piglit_width, piglit_height); + glPixelZoom(-piglit_width / (GLfloat) large_dim_size, -piglit_height / (GLfloat) SMALL_DIM_SIZE); + glDrawPixels(large_dim_size, SMALL_DIM_SIZE, GL_RGBA, GL_FLOAT, pixels); + for (i = 0; i < piglit_height; i++) { + int j; + GLfloat prev_pval = 0.0; + for (j = piglit_width-1; j >= 0; j--) { + GLfloat pval[4]; + glReadPixels(j, i, 1, 1, GL_RGBA, GL_FLOAT, pval); + if ((pval[0] + pval[1] + pval[2]) < prev_pval) { + pass_negmonx = false; + break; + } + prev_pval = (pval[0] + pval[1] + pval[2]); + } + } + + /* Negative Edge Value X Test: The negative zoom xfactor was chosen such + * that the last pixel of the output image should equal 1.0. + * + * Given xr + (n+1) * xfactor <= pix_center < xr + n * xfactor, + * and N is input image width, for pixel 0 to be in box associated + * with 1.0 requires + * + * pixel_width + ((N-1)+1) * xfactor = 0 + * + * or + * + * xfactor = -pixel_width / N + */ + for (i = 0; i < piglit_height; i++) { + GLfloat pval[4]; + glReadPixels(0, i, 1, 1, GL_RGBA, GL_FLOAT, pval); + if ((pval[0] + pval[1] + pval[2]) != 3.0) { + pass_negedgex = false; + break; + } +#if 0 + /* One might think this first "negative" pixel would be + * 0.0, but there is no formula to establish this. The + * reason is really tied to the fact that the pixel at + * (xr,yr) is not inclusive when xfactor and yfactor are + * negative because its center falls on the right and top + * edges of the [xr+xfactor,xr) x [yr+yfactor,yr) box. + */ + glReadPixels(piglit_width - 1, i, 1, 1, GL_RGBA, GL_FLOAT, pval); + if ((pval[0] + pval[1] + pval[2]) != ##) { + pass_negedgex = false; + break; + } +#endif + } + +#define PIXEL_TOLERANCE 0.01 + + /* Postivie and Negative Over/Underrun X Test: Scale a single-valued + * block of pixels with multiple xfactor to leave behind a single line + * effectively alternating lines of two intensities. Do once with + * magnification, once with condensation. + */ + for (i = 0; i < 4; i++) { + GLfloat *pixels_1 = 0, *pixels_2 = 0; + GLint input_width, input_height; + bool *test_flag; + GLfloat x_sign; + glRasterPos2i(0, 0); + glPixelZoom(piglit_width, piglit_height); + glDrawPixels(1, 1, GL_RGBA, GL_FLOAT, background_color); + input_height = 1; + if (i == ((i >> 1) << 1)) + input_width = 5; + else + input_width = 5 * piglit_height / 2; + if (i == 0 || i == 1) { + glRasterPos2i(0, 0); + test_flag = &pass_posoverunderrunx; + x_sign = 1.0; + } + else { + glRasterPos2i(piglit_width, 0); + test_flag = &pass_negoverunderrunx; + x_sign = -1.0; + } + pixels_1 = (GLfloat *) malloc(sizeof(GLfloat) * input_width * input_height * 4); + pixels_2 = (GLfloat *) malloc(sizeof(GLfloat) * input_width * input_height * 4); + if (pixels_1 && pixels_2) { + long int n = 0; + int j; + for (j = 0; j < input_width; j++) { + pixels_1[n] = 0.25; + pixels_2[n++] = 0.75; + pixels_1[n] = 0.25; + pixels_2[n++] = 0.75; + pixels_1[n] = 0.25; + pixels_2[n++] = 0.75; + pixels_1[n] = 1.0; + pixels_2[n++] = 0.75; + } + for (j = 0; j < piglit_width; j++) { + GLfloat *pixel_array; + if (j == ((j >> 1) << 1)) + pixel_array = pixels_1; + else + pixel_array = pixels_2; + glPixelZoom(x_sign * (piglit_width - j) / (GLfloat) input_width, piglit_height / (GLfloat) input_height); + glDrawPixels(input_width, input_height, GL_RGBA, GL_FLOAT, pixel_array); + } + for (j = 0; j < piglit_width; j++) { + GLfloat pval[4]; + glReadPixels((x_sign > 0) ? piglit_width - 1 - j : j, piglit_height/2, 1, 1, GL_RGBA, GL_FLOAT, pval); + if (j == ((j >> 1) << 1)) { + if (fabs((pval[0] + pval[1] + pval[2]) - 3 * 0.25) > PIXEL_TOLERANCE) { + *test_flag = false; + break; + } + } + else { + if (fabs((pval[0] + pval[1] + pval[2]) - 3 * 0.75) > PIXEL_TOLERANCE) { + *test_flag = false; + break; + } + } + } + } + if (pixels_1) + free(pixels_1); + if (pixels_2) + free(pixels_2); + } + + + /* Positive Monotonicity Y Test: Place a gradient in one direction into the + * input image and the zoomed resultant image must be a gradient. + */ + glRasterPos2i(0, 0); + glPixelZoom(piglit_width, piglit_height); + glDrawPixels(1, 1, GL_RGBA, GL_FLOAT, background_color); + for (i = 0; i < large_dim_size; i++) { + int j; + long int n = 4 * SMALL_DIM_SIZE * i; + GLfloat pval = i / (GLfloat) (large_dim_size - 1); + for (j = 0; j < SMALL_DIM_SIZE; j++) { + pixels[n++] = pval; + pixels[n++] = pval; + pixels[n++] = pval; + pixels[n++] = 1.0; + } + } + glRasterPos2i(0, 0); + glPixelZoom((piglit_width - 1) / (GLfloat) (SMALL_DIM_SIZE - 1), (piglit_height - 1) / (GLfloat) (large_dim_size - 1)); + glDrawPixels(SMALL_DIM_SIZE, large_dim_size, GL_RGBA, GL_FLOAT, pixels); + for (i = 0; i < piglit_width; i++) { + int j; + GLfloat prev_pval = 0.0; + for (j = 0; j < piglit_height; j++) { + GLfloat pval[4]; + glReadPixels(i, j, 1, 1, GL_RGBA, GL_FLOAT, pval); + if ((pval[0] + pval[1] + pval[2]) < prev_pval) { + pass_posmony = false; + break; + } + prev_pval = (pval[0] + pval[1] + pval[2]); + } + } + + /* Positive Edge Value Y Test: The positive zoom yfactor was chosen such + * that the last pixel of the output image should equal 1.0. + * + * Given yr + m * yfactor <= pix_center < yr + (n+1) * yfactor, + * and M is input image width, for pixel piglit_height-1 to be in + * the box associated with 1.0 requires + * + * 0 + (M-1) * yfactor = pixel_height-1 + * + * or + * + * yfactor = (pixel_height - 1) / (M - 1) + */ + for (i = 0; i < piglit_width; i++) { + GLfloat pval[4]; + glReadPixels(i, 0, 1, 1, GL_RGBA, GL_FLOAT, pval); + if ((pval[0] + pval[1] + pval[2]) != 0.0) { + pass_posedgey = false; + break; + } + glReadPixels(i, piglit_height - 1, 1, 1, GL_RGBA, GL_FLOAT, pval); + if ((pval[0] + pval[1] + pval[2]) != 3.0) { + pass_posedgey = false; + break; + } + } + + /* Negative Monotonicity Y Test: Use the same gradient input image as for + * the positive monotonicity test. + */ + glRasterPos2i(0, 0); + glPixelZoom(piglit_width, piglit_height); + glDrawPixels(1, 1, GL_RGBA, GL_FLOAT, background_color); + glRasterPos2i(piglit_width, piglit_height); + glPixelZoom(-piglit_width / (GLfloat) SMALL_DIM_SIZE, -piglit_height / (GLfloat) large_dim_size); + glDrawPixels(SMALL_DIM_SIZE, large_dim_size, GL_RGBA, GL_FLOAT, pixels); + for (i = 0; i < piglit_width; i++) { + int j; + GLfloat prev_pval = 0.0; + for (j = piglit_height-1; j >= 0; j--) { + GLfloat pval[4]; + glReadPixels(i, j, 1, 1, GL_RGBA, GL_FLOAT, pval); + if ((pval[0] + pval[1] + pval[2]) < prev_pval) { + pass_negmony = false; + break; + } + prev_pval = (pval[0] + pval[1] + pval[2]); + } + } + + /* Negative Edge Value Y Test: The positive zoom xfactor was chosen such + * that the last pixel of the output image should equal 1.0. + * + * Given yr + (m+1) * yfactor <= pix_center < yr + m * yfactor, + * and M is input image width, for pixel 0 to be in box associated + * with 1.0 requires + * + * pixel_height + ((M-1)+1) * yfactor = 0 + * + * or + * + * yfactor = -pixel_height / M + */ + for (i = 0; i < piglit_width; i++) { + GLfloat pval[4]; + glReadPixels(i, 0, 1, 1, GL_RGBA, GL_FLOAT, pval); + if ((pval[0] + pval[1] + pval[2]) != 3.0) { + pass_negedgey = false; + break; + } +#if 0 + glReadPixels(i, piglit_height - 1, 1, 1, GL_RGBA, GL_FLOAT, pval); + if ((pval[0] + pval[1] + pval[2]) != ##) { + pass_negedgex = false; + break; + } +#endif + } + + /* Postivie and Negative Over/Underrun Y Test: Scale a single-valued + * block of pixels with multiple xfactor to leave behind a single line + * effectively alternating lines of two intensities. Do once with + * magnification, once with condensation. + */ + for (i = 0; i < 4; i++) { + GLfloat *pixels_1 = 0, *pixels_2 = 0; + GLint input_width, input_height; + bool *test_flag; + GLfloat y_sign; + glRasterPos2i(0, 0); + glPixelZoom(piglit_width, piglit_height); + glDrawPixels(1, 1, GL_RGBA, GL_FLOAT, background_color); + input_width = 1; + if (i == ((i >> 1) << 1)) + input_height = 5; + else + input_height = 5 * piglit_height / 2; + if (i == 0 || i == 1) { + glRasterPos2i(0, 0); + test_flag = &pass_posoverunderruny; + y_sign = 1.0; + } + else { + glRasterPos2i(0, piglit_height); + test_flag = &pass_negoverunderruny; + y_sign = -1.0; + } + pixels_1 = (GLfloat *) malloc(sizeof(GLfloat) * input_width * input_height * 4); + pixels_2 = (GLfloat *) malloc(sizeof(GLfloat) * input_width * input_height * 4); + if (pixels_1 && pixels_2) { + long int n = 0; + int j; + for (j = 0; j < (input_width * input_height); j++) { + pixels_1[n] = 0.25; + pixels_2[n++] = 0.75; + pixels_1[n] = 0.25; + pixels_2[n++] = 0.75; + pixels_1[n] = 0.25; + pixels_2[n++] = 0.75; + pixels_1[n] = 1.0; + pixels_2[n++] = 0.75; + } + for (j = 0; j < piglit_height; j++) { + GLfloat *pixel_array; + if (j == ((j >> 1) << 1)) + pixel_array = pixels_1; + else + pixel_array = pixels_2; + glPixelZoom(piglit_width / (GLfloat) input_width, y_sign * (piglit_height - j) / (GLfloat) input_height); + glDrawPixels(input_width, input_height, GL_RGBA, GL_FLOAT, pixel_array); + } + for (j = 0; j < piglit_height; j++) { + GLfloat pval[4]; + glReadPixels(piglit_width/2, (y_sign > 0) ? piglit_height - 1 - j : j, 1, 1, GL_RGBA, GL_FLOAT, pval); + if (j == ((j >> 1) << 1)) { + if (fabs((pval[0] + pval[1] + pval[2]) - 3 * 0.25) > PIXEL_TOLERANCE) { + *test_flag = false; + break; + } + } + else { + if (fabs((pval[0] + pval[1] + pval[2]) - 3 * 0.75) > PIXEL_TOLERANCE) { + *test_flag = false; + break; + } + } + } + } + if (pixels_1) + free(pixels_1); + if (pixels_2) + free(pixels_2); + } + + free(pixels); + } + + piglit_report_subtest_result(pass_posmonx ? PIGLIT_PASS : PIGLIT_FAIL, + "positive monotonic x"); + piglit_report_subtest_result(pass_posedgex ? PIGLIT_PASS : PIGLIT_FAIL, + "positive edge x"); + piglit_report_subtest_result(pass_posoverunderrunx ? PIGLIT_PASS : PIGLIT_FAIL, + "positive over/underrun x"); + piglit_report_subtest_result(pass_negmonx ? PIGLIT_PASS : PIGLIT_FAIL, + "negative monotonic x"); + piglit_report_subtest_result(pass_negedgex ? PIGLIT_PASS : PIGLIT_FAIL, + "negative edge x"); + piglit_report_subtest_result(pass_negoverunderrunx ? PIGLIT_PASS : PIGLIT_FAIL, + "negative over/underrun x"); + piglit_report_subtest_result(pass_posmony ? PIGLIT_PASS : PIGLIT_FAIL, + "positive monotonic y"); + piglit_report_subtest_result(pass_posedgey ? PIGLIT_PASS : PIGLIT_FAIL, + "positive edge y"); + piglit_report_subtest_result(pass_posoverunderruny ? PIGLIT_PASS : PIGLIT_FAIL, + "positive over/underrun y"); + piglit_report_subtest_result(pass_negmony ? PIGLIT_PASS : PIGLIT_FAIL, + "negative monotonic y"); + piglit_report_subtest_result(pass_negedgey ? PIGLIT_PASS : PIGLIT_FAIL, + "negative edge y"); + piglit_report_subtest_result(pass_negoverunderruny ? PIGLIT_PASS : PIGLIT_FAIL, + "negative over/underrun y"); + piglit_report_subtest_result(PIGLIT_PASS, "END OF PIXELZOOM TESTS"); + + piglit_present_results(); + + return (pass_posmonx && pass_posedgex && pass_posoverunderrunx && + pass_negmonx && pass_negedgex && pass_negoverunderrunx && + pass_posmony && pass_posedgey && pass_posoverunderruny && + pass_negmony && pass_negedgey && pass_negoverunderruny) + ? PIGLIT_PASS : PIGLIT_FAIL; +} + +void +piglit_init(int argc, char **argv) +{ + prog = piglit_build_simple_program( + "#version 120\n" + "uniform float zflip;\n" + "void main() { gl_Position = gl_Vertex * vec4(1, 1, zflip, 1); }\n", + + "#version 120\n" + "uniform vec4 color;\n" + "void main() { gl_FragColor = color; }\n"); + + piglit_ortho_projection(piglit_width, piglit_height, GL_FALSE); +} -- 1.7.4.4