/* * Copyright (c) 2011 VMware, Inc. * * 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 * on 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 THEIR 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. */ /** * @file getteximage-formats.c * * Test glGetTexImage with a variety of formats. * Brian Paul * Sep 2011 */ #include "piglit-util-gl.h" #include "../fbo/fbo-formats.h" PIGLIT_GL_TEST_CONFIG_BEGIN config.supports_gl_compat_version = 10; config.window_width = 600; config.window_height = 200; config.window_visual = PIGLIT_GL_VISUAL_RGBA | PIGLIT_GL_VISUAL_DOUBLE; config.khr_no_error_support = PIGLIT_NO_ERRORS; PIGLIT_GL_TEST_CONFIG_END static const char *TestName = "getteximage-formats"; static const GLfloat clearColor[4] = { 0.4, 0.4, 0.4, 0.0 }; static GLuint texture_id; static GLboolean init_by_rendering; static GLboolean init_by_clearing_first; #define TEX_SIZE 128 #define DO_BLEND 1 /** * Make a simple texture image where red increases from left to right, * green increases from bottom to top, blue stays constant (50%) and * the alpha channel is a checkerboard pattern. * \return GL_TRUE for success, GL_FALSE if unsupported format */ static GLboolean make_texture_image(GLenum intFormat, GLubyte upperRightTexel[4]) { GLubyte tex[TEX_SIZE][TEX_SIZE][4]; int i, j; GLuint fb, status; for (i = 0; i < TEX_SIZE; i++) { for (j = 0; j < TEX_SIZE; j++) { tex[i][j][0] = j * 255 / TEX_SIZE; tex[i][j][1] = i * 255 / TEX_SIZE; tex[i][j][2] = 128; if (((i >> 4) ^ (j >> 4)) & 1) tex[i][j][3] = 255; /* opaque */ else tex[i][j][3] = 125; /* transparent */ } } memcpy(upperRightTexel, tex[TEX_SIZE-1][TEX_SIZE-1], 4); if (init_by_rendering || init_by_clearing_first) { /* Initialize the mipmap levels. */ for (i = TEX_SIZE, j = 0; i; i >>= 1, j++) { glTexImage2D(GL_TEXTURE_2D, j, intFormat, i, i, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); } /* Initialize the texture with glDrawPixels. */ glGenFramebuffers(1, &fb); glBindFramebuffer(GL_FRAMEBUFFER, fb); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0); status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, piglit_winsys_fbo); glDeleteFramebuffers(1, &fb); return GL_FALSE; } if (init_by_clearing_first) { glClearColor(1, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); } glViewport(0, 0, TEX_SIZE, TEX_SIZE); /* Disable dithering during glDrawPixels to prevent * disagreement with glGenerateMipmap. The spec requires * glDrawPixels to respect the dither state, but the spec * strongly implies that glGenerateMipmap should not dither. * * On dithering and glDrawPixels, see the OpenGL 4.5 * Compatibility Specification, Section 18.1 Drawing Pixels, * p620: * * Once pixels are transferred, DrawPixels performs final * conversion on pixel values [...] which are processed in * the same fashion as fragments generated by rasterization * (see chapters 15 and 16). * * On dithering and glGenerateMipmap, see the Mesa commit * message in: * * commit c4b87f129eb036c9615df3adcc1cebd9df10fc84 * Author: Chad Versace * Date: Thu Dec 29 13:05:27 2016 -0800 * Subject: meta: Disable dithering during glGenerateMipmap */ glDisable(GL_DITHER); glWindowPos2iARB(0, 0); glDrawPixels(TEX_SIZE, TEX_SIZE, GL_RGBA, GL_UNSIGNED_BYTE, tex); glEnable(GL_DITHER); glGenerateMipmap(GL_TEXTURE_2D); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, piglit_winsys_fbo); glDeleteFramebuffers(1, &fb); glViewport(0, 0, piglit_width, piglit_height); } else { glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); glTexImage2D(GL_TEXTURE_2D, 0, intFormat, TEX_SIZE, TEX_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex); } return glGetError() == GL_NO_ERROR; } static GLfloat ubyte_to_float(GLubyte b, GLint bits) { if (bits <= 8) { GLint b2 = b >> (8 - bits); GLint max = 255 >> (8 - bits); return b2 / (float) max; } else { return b / 255.0; } } static GLfloat bits_to_tolerance(GLint bits, GLboolean compressed) { GLfloat t; if (bits == 0) { return 0.25; } else if (bits == 1) { return 0.5; } else if (bits > 8) { /* The original texture was specified as GLubyte and we * assume that the window/surface is 8-bits/channel. */ t = 4.0 / 255; } else { t = 4.0 / (1 << (bits - 1)); } if (compressed) { /* Use a fudge factor. The queries for GL_TEXTURE_RED/ * GREEN/BLUE/ALPHA_SIZE don't return well-defined values for * compressed formats so using them is unreliable. This is * pretty loose, but good enough to catch some Mesa bugs during * development. */ t = 0.3; } return t; } static void compute_expected_color(const struct format_desc *fmt, const GLubyte upperRightTexel[4], GLfloat expected[4], GLfloat tolerance[4]) { GLfloat texel[4]; GLint compressed; int bits[4]; bits[0] = bits[1] = bits[2] = bits[3] = 0; /* Handle special cases first */ if (fmt->internalformat == GL_R11F_G11F_B10F_EXT) { bits[0] = bits[1] = bits[2] = 8; bits[3] = 0; texel[0] = ubyte_to_float(upperRightTexel[0], bits[0]); texel[1] = ubyte_to_float(upperRightTexel[1], bits[1]); texel[2] = ubyte_to_float(upperRightTexel[2], bits[2]); texel[3] = 1.0; compressed = GL_FALSE; } else { GLint r, g, b, a, l, i; GLenum baseFormat = 0; glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_RED_SIZE, &r); glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_GREEN_SIZE, &g); glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_BLUE_SIZE, &b); glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_ALPHA_SIZE, &a); glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_LUMINANCE_SIZE, &l); glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTENSITY_SIZE, &i); glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED, &compressed); if (0) printf("r=%d g=%d b=%d a=%d l=%d i=%d\n", r, g, b, a, l, i); if (i > 0) { baseFormat = GL_INTENSITY; bits[0] = i; bits[1] = 0; bits[2] = 0; bits[3] = i; texel[0] = ubyte_to_float(upperRightTexel[0], bits[0]); texel[1] = 0.0; texel[2] = 0.0; texel[3] = ubyte_to_float(upperRightTexel[0], bits[3]); } else if (a > 0) { if (l > 0) { baseFormat = GL_LUMINANCE_ALPHA; bits[0] = l; bits[1] = 0; bits[2] = 0; bits[3] = a; texel[0] = ubyte_to_float(upperRightTexel[0], bits[0]); texel[1] = 0.0; texel[2] = 0.0; texel[3] = ubyte_to_float(upperRightTexel[3], bits[3]); } else if (r > 0 && g > 0 && b > 0) { baseFormat = GL_RGBA; bits[0] = r; bits[1] = g; bits[2] = b; bits[3] = a; texel[0] = ubyte_to_float(upperRightTexel[0], bits[0]); texel[1] = ubyte_to_float(upperRightTexel[1], bits[1]); texel[2] = ubyte_to_float(upperRightTexel[2], bits[2]); texel[3] = ubyte_to_float(upperRightTexel[3], bits[3]); } else if (r == 0 && g == 0 && b == 0) { baseFormat = GL_ALPHA; bits[0] = 0; bits[1] = 0; bits[2] = 0; bits[3] = a; texel[0] = 0.0; texel[1] = 0.0; texel[2] = 0.0; texel[3] = ubyte_to_float(upperRightTexel[3], bits[3]); } else { baseFormat = 0; /* ??? */ texel[0] = 0.0; texel[1] = 0.0; texel[2] = 0.0; texel[3] = 0.0; } } else if (l > 0) { baseFormat = GL_LUMINANCE; bits[0] = l; bits[1] = 0; bits[2] = 0; bits[3] = 0; texel[0] = ubyte_to_float(upperRightTexel[0], bits[0]); texel[1] = 0.0; texel[2] = 0.0; texel[3] = 1.0; } else if (r > 0) { if (g > 0) { if (b > 0) { baseFormat = GL_RGB; bits[0] = r; bits[1] = g; bits[2] = b; bits[3] = 0; texel[0] = ubyte_to_float(upperRightTexel[0], bits[0]); texel[1] = ubyte_to_float(upperRightTexel[1], bits[1]); texel[2] = ubyte_to_float(upperRightTexel[2], bits[2]); texel[3] = 1.0; } else { baseFormat = GL_RG; bits[0] = r; bits[1] = g; bits[2] = 0; bits[3] = 0; texel[0] = ubyte_to_float(upperRightTexel[0], bits[0]); texel[1] = ubyte_to_float(upperRightTexel[1], bits[1]); texel[2] = 0.0; texel[3] = 1.0; } } else { baseFormat = GL_RED; bits[0] = r; bits[1] = 0; bits[2] = 0; bits[3] = 0; texel[0] = ubyte_to_float(upperRightTexel[0], bits[0]); texel[1] = 0.0; texel[2] = 0.0; texel[3] = 1.0; } } else { assert(!"Unexpected texture component sizes"); texel[0] = 0.0; texel[1] = 0.0; texel[2] = 0.0; texel[3] = 0.0; } (void) baseFormat; /* not used, at this time */ } /* compute texel color blended with background color */ #if DO_BLEND expected[0] = texel[0] * texel[3] + clearColor[0] * (1.0 - texel[3]); expected[1] = texel[1] * texel[3] + clearColor[1] * (1.0 - texel[3]); expected[2] = texel[2] * texel[3] + clearColor[2] * (1.0 - texel[3]); expected[3] = texel[3] * texel[3] + clearColor[3] * (1.0 - texel[3]); #else expected[0] = texel[0]; expected[1] = texel[1]; expected[2] = texel[2]; expected[3] = texel[3]; #endif assert(expected[0] == expected[0]); tolerance[0] = bits_to_tolerance(bits[0], compressed); tolerance[1] = bits_to_tolerance(bits[1], compressed); tolerance[2] = bits_to_tolerance(bits[2], compressed); tolerance[3] = bits_to_tolerance(bits[3], compressed); } static GLboolean colors_equal(const GLfloat expected[4], const GLfloat pix[4], GLfloat tolerance[4]) { if (fabsf(expected[0] - pix[0]) > tolerance[0] || fabsf(expected[1] - pix[1]) > tolerance[1] || fabsf(expected[2] - pix[2]) > tolerance[2] || fabsf(expected[3] - pix[3]) > tolerance[3]) { return GL_FALSE; } return GL_TRUE; } static GLboolean test_format(const struct test_desc *test, const struct format_desc *fmt) { int x, y; int w = TEX_SIZE, h = TEX_SIZE; GLfloat readback[TEX_SIZE][TEX_SIZE][4]; GLubyte upperRightTexel[4]; int level; GLfloat expected[4], pix[4], tolerance[4]; GLboolean pass = GL_TRUE; glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); glClear(GL_COLOR_BUFFER_BIT); /* The RGBA_DXT1 formats seem to expose a Mesa/libtxc_dxtn bug. * Just skip them for now. Testing the other compressed formats * is good enough. */ if (fmt->internalformat != GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && fmt->internalformat != GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) { /* init texture image */ if (!make_texture_image(fmt->internalformat, upperRightTexel)) return GL_TRUE; /* unsupported = OK */ x = 10; y = 40; compute_expected_color(fmt, upperRightTexel, expected, tolerance); /* Draw with the texture */ glEnable(GL_TEXTURE_2D); #if DO_BLEND glEnable(GL_BLEND); #endif piglit_draw_rect_tex(x, y, w, h, 0.0, 0.0, 1.0, 1.0); glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); x += TEX_SIZE + 20; level = 0; while (w > 0) { /* Get the texture image */ assert(!glIsEnabled(GL_TEXTURE_2D)); glGetTexImage(GL_TEXTURE_2D, level, GL_RGBA, GL_FLOAT, readback); assert(!glIsEnabled(GL_TEXTURE_2D)); /* Draw the texture image */ glWindowPos2iARB(x, y); #if DO_BLEND glEnable(GL_BLEND); #endif assert(!glIsEnabled(GL_TEXTURE_2D)); glDrawPixels(w, h, GL_RGBA, GL_FLOAT, readback); glDisable(GL_BLEND); assert(!glIsEnabled(GL_TEXTURE_2D)); if (level <= 2) { GLint rx = x + w-1; GLint ry = y + h-1; glReadPixels(rx, ry, 1, 1, GL_RGBA, GL_FLOAT, pix); if (!colors_equal(expected, pix, tolerance)) { printf("%s failure: format: %s, level %d at pixel(%d, %d)\n", TestName, get_format_name( fmt->internalformat), level, rx, ry); printf(" Expected (%f, %f, %f, %f)\n", expected[0], expected[1], expected[2], expected[3]); printf(" Found (%f, %f, %f, %f)\n", pix[0], pix[1], pix[2], pix[3]); printf("Tolerance (%f, %f, %f, %f)\n", tolerance[0], tolerance[1], tolerance[2], tolerance[3]); pass = GL_FALSE; } } x += w + 20; w /= 2; h /= 2; level++; } } piglit_present_results(); return pass; } /** * Is the given set of formats supported? * This checks if required extensions are present and if this piglit test * can actually grok the formats. */ static GLboolean supported_format_set(const struct test_desc *set) { if (!supported(set)) return GL_FALSE; if (set->format == ext_texture_integer || set->format == ext_packed_depth_stencil || set->format == arb_texture_stencil8 || set->format == arb_texture_rg_int || set->format == arb_depth_texture || set->format == arb_depth_buffer_float) { /* * texture_integer requires a fragment shader, different * glTexImage calls. Depth/stencil formats not implemented. */ return GL_FALSE; } return GL_TRUE; } static GLboolean test_all_formats(void) { GLboolean pass = GL_TRUE; int i, j; for (i = 0; i < ARRAY_SIZE(test_sets); i++) { const struct test_desc *set = &test_sets[i]; if (supported_format_set(set)) { for (j = 0; j < set->num_formats; j++) { if (!test_format(set, &set->format[j])) { pass = GL_FALSE; } } } } return pass; } enum piglit_result piglit_display(void) { GLboolean pass; piglit_ortho_projection(piglit_width, piglit_height, GL_FALSE); if (piglit_automatic) { pass = test_all_formats(); } else { const struct test_desc *set = &test_sets[test_index]; if (supported_format_set(set)) { pass = test_format(set, &set->format[format_index]); } else { /* unsupported format - not a failure */ pass = GL_TRUE; glClear(GL_COLOR_BUFFER_BIT); piglit_present_results(); } } return pass ? PIGLIT_PASS : PIGLIT_FAIL; } void piglit_init(int argc, char **argv) { bool found_test_set = false; int i; if ((piglit_get_gl_version() < 14) && !piglit_is_extension_supported("GL_ARB_window_pos")) { printf("Requires GL 1.4 or GL_ARB_window_pos"); piglit_report_result(PIGLIT_SKIP); } (void) fbo_formats_display; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "init-by-rendering") == 0) { init_by_rendering = GL_TRUE; puts("The textures will be initialized by rendering " "to them using glDrawPixels."); break; } else if (strcmp(argv[i], "init-by-clear-and-render") == 0) { init_by_clearing_first = GL_TRUE; puts("The textures will be initialized by rendering " "to them using glClear and glDrawPixels."); break; } else if (!found_test_set) { found_test_set = fbo_use_test_set(argv[i], true); if (!found_test_set) { printf("Bad test set name: %s\n", argv[i]); } } } glGenTextures(1, &texture_id); glBindTexture(GL_TEXTURE_2D, texture_id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); }