/* * Copyright © 2009-2011 Intel Corporation * * 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. * * Authors: * Eric Anholt * */ /** @file fbo-clear-formats.c * * Tests that glClear works correctly on all levels of 2D * texture-based FBOs of various internalformats. */ #include "piglit-util-gl.h" #include "fbo-formats.h" #define TEX_WIDTH 256 #define TEX_HEIGHT 256 PIGLIT_GL_TEST_CONFIG_BEGIN config.supports_gl_compat_version = 10; config.window_width = 700; config.window_height = 300; config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_STENCIL | PIGLIT_GL_VISUAL_RGBA; config.khr_no_error_support = PIGLIT_NO_ERRORS; PIGLIT_GL_TEST_CONFIG_END static bool clear_stencil = false; /* Do piglit_rgbw_texture() image but using glClear */ static bool do_rgba_clear(GLenum format, GLuint tex, int level, int size) { float red[4] = {1.0, 0.0, 0.0, 0.0}; float green[4] = {0.0, 1.0, 0.0, 0.25}; float blue[4] = {0.0, 0.0, 1.0, 0.5}; float white[4] = {1.0, 1.0, 1.0, 1.0}; float black[4] = {0.0, 0.0, 0.0, 0.0}; float *color; GLuint fb; GLenum status; glGenFramebuffersEXT(1, &fb); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, level); status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, piglit_winsys_fbo); glDeleteFramebuffersEXT(1, &fb); return false; } /* Handle the small sizes of compressed mipmap blocks */ switch (format) { case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: case GL_COMPRESSED_RGB_FXT1_3DFX: case GL_COMPRESSED_RGBA_FXT1_3DFX: if (size == 4) color = red; else if (size == 2) color = green; else if (size == 1) color = blue; else { assert(0); color = black; } glClearColor(color[0], color[1], color[2], color[3]); glClear(GL_COLOR_BUFFER_BIT); return true; } /* Do the first clear unscissored, to give a bit of exposure * to hardware fast-clearing paths on tiled renderers that * require unscissored clears. */ glClearColor(red[0], red[1], red[2], red[3]); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_SCISSOR_TEST); glScissor(size / 2, 0, size / 2, size / 2); glClearColor(green[0], green[1], green[2], green[3]); glClear(GL_COLOR_BUFFER_BIT); glScissor(0, size / 2, size / 2, size / 2); glClearColor(blue[0], blue[1], blue[2], blue[3]); glClear(GL_COLOR_BUFFER_BIT); glScissor(size / 2, size / 2, size / 2, size / 2); glClearColor(white[0], white[1], white[2], white[3]); glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_SCISSOR_TEST); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, piglit_winsys_fbo); glDeleteFramebuffersEXT(1, &fb); return true; } static bool do_depth_clear(GLenum format, GLuint tex, int level, int size) { GLuint fb; GLenum status; GLint draw_buffer, read_buffer; int x; glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer); glGetIntegerv(GL_READ_BUFFER, &read_buffer); glGenFramebuffersEXT(1, &fb); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, tex, level); status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, piglit_winsys_fbo); glDeleteFramebuffersEXT(1, &fb); return false; } glEnable(GL_SCISSOR_TEST); for (x = 0; x < size; x++) { float val = (x + 0.5) / (size); glScissor(x, 0, 1, size); glClearDepth(val); glClear(GL_DEPTH_BUFFER_BIT); } glDisable(GL_SCISSOR_TEST); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, piglit_winsys_fbo); glDeleteFramebuffersEXT(1, &fb); glDrawBuffer(draw_buffer); glReadBuffer(read_buffer); return true; } static bool do_stencil_clear(GLenum format, GLuint tex, int level, int size) { GLuint fb; GLenum status; GLint draw_buffer, read_buffer; int x; glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer); glGetIntegerv(GL_READ_BUFFER, &read_buffer); glGenFramebuffersEXT(1, &fb); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, tex, level); status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, piglit_winsys_fbo); glDeleteFramebuffersEXT(1, &fb); return false; } glEnable(GL_SCISSOR_TEST); for (x = 0; x < size; x++) { unsigned val = ((x + 0.5) / (size)) * 0xff; glScissor(x, 0, 1, size); glClearStencil(val); glClear(GL_STENCIL_BUFFER_BIT); } glDisable(GL_SCISSOR_TEST); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, piglit_winsys_fbo); glDeleteFramebuffersEXT(1, &fb); glDrawBuffer(draw_buffer); glReadBuffer(read_buffer); if (!piglit_check_gl_error(GL_NO_ERROR)) { /* Should be no error at this point. If there is, report failure */ piglit_report_result(PIGLIT_FAIL); } return true; } static GLuint create_tex(GLenum internalformat, GLenum baseformat) { GLuint tex; int level, dim; GLenum type, format; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); if (internalformat == GL_DEPTH32F_STENCIL8) { format = GL_DEPTH_STENCIL; type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; } else if (baseformat == GL_DEPTH_COMPONENT) { format = GL_DEPTH_COMPONENT; type = GL_FLOAT; } else if (baseformat == GL_DEPTH_STENCIL) { format = GL_DEPTH_STENCIL_EXT; type = GL_UNSIGNED_INT_24_8_EXT; } else { format = GL_RGBA; type = GL_FLOAT; } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); for (level = 0, dim = TEX_WIDTH; dim > 0; level++, dim /= 2) { glTexImage2D(GL_TEXTURE_2D, level, internalformat, dim, dim, 0, format, type, NULL); } for (level = 0, dim = TEX_WIDTH; dim > 0; level++, dim /= 2) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, level); if (clear_stencil || baseformat == GL_STENCIL_INDEX) { if (!do_stencil_clear(format, tex, level, dim)) { glDeleteTextures(1, &tex); return 0; } } else if (baseformat == GL_DEPTH_COMPONENT || baseformat == GL_DEPTH_STENCIL) { if (!do_depth_clear(format, tex, level, dim)) { glDeleteTextures(1, &tex); return 0; } } else { if (!do_rgba_clear(format, tex, level, dim)) { glDeleteTextures(1, &tex); return 0; } } } glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level - 1); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); if (!piglit_check_gl_error(GL_NO_ERROR)) { /* Should be no error at this point. If there is, report failure */ piglit_report_result(PIGLIT_FAIL); } return tex; } static void draw_mipmap(int x, int y, int dim) { glEnable(GL_TEXTURE_2D); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); piglit_draw_rect_tex(x, y, dim, dim, 0, 0, 1, 1); glDisable(GL_TEXTURE_2D); } static void draw_stencil_mipmap(int x, int y, int dim, GLuint tex, GLuint level) { GLuint fbo; GLint draw_buffer, read_buffer; glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer); glGetIntegerv(GL_READ_BUFFER, &read_buffer); glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, tex, level); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, piglit_winsys_fbo); glWindowPos2i(x, y); glCopyPixels(0, 0, dim, dim, GL_STENCIL); if (!piglit_check_gl_error(GL_NO_ERROR)) { /* The blit shouldn't generate an error. If it does, report failure */ piglit_report_result(PIGLIT_FAIL); } glBindFramebuffer(GL_FRAMEBUFFER, piglit_winsys_fbo); glDeleteFramebuffers(1, &fbo); glDrawBuffer(draw_buffer); glReadBuffer(read_buffer); } static void visualize_stencil() { unsigned i; glEnable(GL_STENCIL_TEST); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); for (i = 0; i <= 0xff; i++) { glStencilFunc(GL_EQUAL, i, ~0); glColor4ub(i, i, i, 255); piglit_draw_rect(0, 0, piglit_width, piglit_height); } glDisable(GL_STENCIL_TEST); glColor4ub(255, 255, 255, 255); } static GLboolean test_mipmap_drawing(int x, int y, int dim, int level, GLuint internalformat) { GLboolean pass = GL_TRUE; int half = dim / 2; int x1 = x, y1 = y, x2 = x + half, y2 = y + half; float r[] = {1, 0, 0, 0}; float g[] = {0, 1, 0, 0.25}; float b[] = {0, 0, 1, 0.5}; float w[] = {1, 1, 1, 1}; GLint r_size, g_size, b_size, l_size, a_size, d_size, i_size; GLint compressed; glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_COMPRESSED, &compressed); if (compressed && dim < 8) return GL_TRUE; if (piglit_is_extension_supported("GL_ARB_depth_texture")) { glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_DEPTH_SIZE, &d_size); } else { d_size = 0; } glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_LUMINANCE_SIZE, &l_size); glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_ALPHA_SIZE, &a_size); glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_INTENSITY_SIZE, &i_size); glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_RED_SIZE, &r_size); glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_GREEN_SIZE, &g_size); glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_BLUE_SIZE, &b_size); if (d_size) { for (x1 = x; x1 < x + dim; x1++) { float val = (x1 - x + 0.5) / (dim); float color[3] = {val, val, val}; pass = pass && piglit_probe_rect_rgb(x1, y, 1, dim, color); } return pass; } if (i_size || l_size) { r[0] = 1.0; r[1] = 1.0; r[2] = 1.0; g[0] = 0.0; g[1] = 0.0; g[2] = 0.0; b[0] = 0.0; b[1] = 0.0; b[2] = 0.0; if (i_size) { r[3] = 1.0; g[3] = 0.0; b[3] = 0.0; } else if (l_size && !a_size) { r[3] = 1.0; g[3] = 1.0; b[3] = 1.0; w[3] = 1.0; } } else if (a_size && !r_size && !l_size) { r[0] = 1.0; r[1] = 1.0; r[2] = 1.0; g[0] = 1.0; g[1] = 1.0; g[2] = 1.0; b[0] = 1.0; b[1] = 1.0; b[2] = 1.0; } else { if (!r_size) { r[0] = 0.0; w[0] = 0.0; } if (!g_size) { g[1] = 0.0; w[1] = 0.0; } if (!b_size) { b[2] = 0.0; w[2] = 0.0; } if (!a_size) { r[3] = 1.0; g[3] = 1.0; b[3] = 1.0; w[3] = 1.0; } } /* Clamp the bits for the framebuffer, except we aren't checking * the actual framebuffer bits. */ if (l_size > 8) l_size = 8; if (i_size > 8) i_size = 8; if (r_size > 8) r_size = 8; if (g_size > 8) g_size = 8; if (b_size > 8) b_size = 8; if (a_size > 8) a_size = 8; if (d_size) { piglit_set_tolerance_for_bits(8, 8, 8, 8); } else if (i_size) { piglit_set_tolerance_for_bits(i_size, i_size, i_size, i_size); } else if (l_size) { piglit_set_tolerance_for_bits(l_size, l_size, l_size, a_size); } else { piglit_set_tolerance_for_bits(r_size, g_size, b_size, a_size); } if (internalformat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || internalformat == GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) { /* If alpha in DXT1 is < 0.5, the whole pixel should be black. */ r[0] = r[1] = r[2] = r[3] = 0; g[0] = g[1] = g[2] = g[3] = 0; /* If alpha in DXT1 is >= 0.5, it should be white. */ b[3] = 1; } pass = pass && piglit_probe_rect_rgba(x1, y1, half, half, r); pass = pass && piglit_probe_rect_rgba(x2, y1, half, half, g); pass = pass && piglit_probe_rect_rgba(x1, y2, half, half, b); pass = pass && piglit_probe_rect_rgba(x2, y2, half, half, w); return pass; } static enum piglit_result test_format(const struct format_desc *format) { int dim; GLuint tex; int x; int level; GLboolean pass = GL_TRUE; const char *name = get_format_name(format->internalformat); printf("Testing %s", name); if (format->base_internal_format == GL_STENCIL_INDEX) clear_stencil = true; if (clear_stencil && format->base_internal_format != GL_DEPTH_STENCIL && format->base_internal_format != GL_STENCIL_INDEX) { printf(" - no stencil.\n"); return PIGLIT_SKIP; } tex = create_tex(format->internalformat, format->base_internal_format); if (tex == 0) { printf(" - FBO incomplete\n"); piglit_report_subtest_result(PIGLIT_SKIP, "%s (fbo incomplete)", name); return PIGLIT_SKIP; } printf("\n"); if (clear_stencil) { glClearStencil(0x0); glClear(GL_STENCIL_BUFFER_BIT); } glViewport(0, 0, piglit_width, piglit_height); piglit_ortho_projection(piglit_width, piglit_height, GL_FALSE); x = 1; level = 0; for (dim = TEX_WIDTH; dim > 1; dim /= 2) { if (clear_stencil) draw_stencil_mipmap(x, 1, dim, tex, level); else draw_mipmap(x, 1, dim); x += dim + 1; level++; } if (clear_stencil) visualize_stencil(); x = 1; level = 0; for (dim = TEX_WIDTH; dim > 1; dim /= 2) { pass = pass && test_mipmap_drawing(x, 1, dim, level, format->internalformat); x += dim + 1; level++; } glDeleteTextures(1, &tex); piglit_report_subtest_result(pass ? PIGLIT_PASS : PIGLIT_FAIL, "%s", name); return pass ? PIGLIT_PASS : PIGLIT_FAIL; } enum piglit_result piglit_display(void) { return fbo_formats_display(test_format); } void piglit_init(int argc, char **argv) { if (argc == 3 && strcmp(argv[2], "stencil") == 0) clear_stencil = true; if (clear_stencil) piglit_require_extension("GL_ARB_framebuffer_object"); fbo_formats_init(clear_stencil ? 2 : argc, argv, GL_TRUE); }