/* * Copyright © 2015 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. */ /** * \file fast-clear.c * * Clears a multisample texture with various formats to various * different colors and then samples from it in a shader to ensure * that the expected color is returned. This includes verifying that * when there are components missing they are overriden to the right * value (such as GL_RED should report 0 for green and blue and 1 for * the alpha). The main reason to do this is that the i965 driver has * various different code paths to implement a fast clear optimisation * and the path taken depends on the color chosen to a certain * degree. * * The test can take the following additional arguments: * * enable-fb-srgb: This will cause it to enable GL_FRAMEBUFFER_SRGB * before clearing the buffer so that it can test that the color * gets correctly converted to SRGB before being stored in the * color buffer. * single-sample: A single sample texture will be created instead. */ #include "piglit-util-gl.h" #include "../../fbo/fbo-formats.h" PIGLIT_GL_TEST_CONFIG_BEGIN config.supports_gl_compat_version = 21; 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 vertex_source[] = "attribute vec4 piglit_vertex;\n" "\n" "void\n" "main()\n" "{\n" " gl_Position = piglit_vertex;\n" "}\n"; static const char fragment_source_float[] = "#version 130\n" "%s\n" "\n" "uniform %s tex;\n" "\n" "void\n" "main()\n" "{\n" " gl_FragColor = texelFetch(tex, ivec2(0), 0);\n" "}\n"; static const char fragment_source_int[] = "#version 130\n" "%s\n" "\n" "uniform i%s tex;\n" "\n" "void\n" "main()\n" "{\n" " gl_FragColor = vec4(texelFetch(tex, ivec2(0), 0));\n" "}\n"; static const char fragment_source_uint[] = "#version 130\n" "%s\n" "\n" "uniform u%s tex;\n" "\n" "void\n" "main()\n" "{\n" " gl_FragColor = vec4(texelFetch(tex, ivec2(0), 0));\n" "}\n"; static const struct test_desc * test_set = &test_sets[0]; static const float clear_colors[][4] = { { 0.0f, 0.0f, 0.0f, 0.0f }, { 1.0f, 1.0f, 1.0f, 1.0f }, { 0.0f, 0.0f, 1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f }, { 0.25f, 0.5f, 0.75f, 1.0f }, { 0.75f, 0.5f, 0.25f, 0.0f }, { 0.5f, 0.25f, 0.75f, 0.5f }, { 2.0f, 3.0f, 0.5f, 1.0f }, { -2.0f, 0.0f, 0.25f, 1.0f }, { -0.5f, 0.0f, 0.25f, 1.0f }, }; struct component_sizes { int r, g, b; int a; int l; int i; }; static GLuint prog_float, prog_int, prog_uint; static GLuint result_fbo; static bool enable_fb_srgb = false; static bool single_sample = false; static int num_samples = 2; static void convert_srgb_color(const struct format_desc *format, float *color) { int i; const char *format_name = get_format_name(format->internalformat); /* If the texture is not an sRGB format then no conversion is * needed regardless of the sRGB settings. */ if (strstr(format_name, "SRGB") == NULL && strstr(format_name, "SLUMINANCE") == NULL) return; /* If GL_FRAMEBUFFER_SRGB was enabled when we did the clear * then the clear color would have been converted to SRGB * before being written. When it is sampled it will be * converted back to linear. The two conversions cancel each * other out so we don't need to do anything. */ if (enable_fb_srgb) return; /* Otherwise we need to compensate for the color being * converted to linear when sampled. */ for (i = 0; i < 3; i++) color[i] = piglit_srgb_to_linear(color[i]); } static int clamp_signed(int value, int size) { return CLAMP(value, -1 << (size - 1), (int) (UINT32_MAX >> (33 - size))); } static unsigned int clamp_unsigned(int value, int size) { if (value < 0) return 0; return CLAMP((unsigned int) value, 0, UINT32_MAX >> (32 - size)); } static enum piglit_result test_color(GLuint test_fbo, int offset, const struct format_desc *format, GLenum clear_type, const struct component_sizes *sizes, const float *clear_color) { float expected_color[4]; int i; glBindFramebuffer(GL_FRAMEBUFFER, test_fbo); if (enable_fb_srgb) glEnable(GL_FRAMEBUFFER_SRGB); memcpy(expected_color, clear_color, sizeof expected_color); switch (clear_type) { case GL_INT: { GLint clear_color_int[4]; for (i = 0; i < 4; i++) { expected_color[i] *= 127; clear_color_int[i] = expected_color[i]; } if (prog_int == 0) return PIGLIT_SKIP; glUseProgram(prog_int); glClearBufferiv(GL_COLOR, 0, /* draw buffer */ clear_color_int); break; } case GL_UNSIGNED_INT: { GLuint clear_color_uint[4]; for (i = 0; i < 4; i++) { expected_color[i] *= 255; clear_color_uint[i] = MAX2(expected_color[i], 0); } if (prog_uint == 0) return PIGLIT_SKIP; glUseProgram(prog_uint); glClearBufferuiv(GL_COLOR, 0, /* draw buffer */ clear_color_uint); break; } default: glUseProgram(prog_float); glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); glClear(GL_COLOR_BUFFER_BIT); break; } if (enable_fb_srgb) glDisable(GL_FRAMEBUFFER_SRGB); switch (format->base_internal_format) { case GL_ALPHA: expected_color[0] = 0.0f; expected_color[1] = 0.0f; expected_color[2] = 0.0f; break; case GL_INTENSITY: expected_color[1] = expected_color[0]; expected_color[2] = expected_color[0]; expected_color[3] = expected_color[0]; break; case GL_LUMINANCE: expected_color[1] = expected_color[0]; expected_color[2] = expected_color[0]; expected_color[3] = 1.0f; break; case GL_LUMINANCE_ALPHA: expected_color[1] = expected_color[0]; expected_color[2] = expected_color[0]; break; case GL_RED: expected_color[1] = 0.0f; expected_color[2] = 0.0f; expected_color[3] = 1.0f; break; case GL_RG: expected_color[2] = 0.0f; expected_color[3] = 1.0f; break; case GL_RGB: expected_color[3] = 1.0f; break; } convert_srgb_color(format, expected_color); if (clear_type == GL_UNSIGNED_NORMALIZED) { for (i = 0; i < 4; i++) { expected_color[i] = CLAMP(expected_color[i], 0.0f, 1.0f); } } else if (clear_type == GL_SIGNED_NORMALIZED) { for (i = 0; i < 4; i++) { expected_color[i] = CLAMP(expected_color[i], -1.0f, 1.0f); } } else if (clear_type == GL_INT) { /* The clear colors are multiplied by 127 for integer * formats so some of them will be large values. The * GL spec states that out-of-range integer values * written to the framebuffer will be clamped so we * need to replicate this in the expected values. For * example, the -2.0 color will be set to -254, and * this will be clamped to -128 for an 8-bit integer * surface. */ expected_color[0] = clamp_signed(expected_color[0], sizes->r); expected_color[1] = clamp_signed(expected_color[1], sizes->g); expected_color[2] = clamp_signed(expected_color[2], sizes->b); expected_color[3] = clamp_signed(expected_color[3], sizes->a); } else if (clear_type == GL_UNSIGNED_INT) { expected_color[0] = clamp_unsigned(expected_color[0], sizes->r); expected_color[1] = clamp_unsigned(expected_color[1], sizes->g); expected_color[2] = clamp_unsigned(expected_color[2], sizes->b); expected_color[3] = clamp_unsigned(expected_color[3], sizes->a); } else if (test_sets[test_index].format == ext_packed_float) { /* These formats can't store negative values */ for (i = 0; i < 4; i++) expected_color[i] = MAX2(expected_color[i], 0.0f); } /* Display something on the winsys FBO just so that something * will be shown. This isn't used for the test results because * the winsys buffer is a normalised format and some of the * values we want to detect will be out of the range [0,1]. */ glBindFramebuffer(GL_FRAMEBUFFER, piglit_winsys_fbo); piglit_draw_rect(offset * 16 * 2.0f / piglit_width - 1.0f, -1.0f, 16 * 2.0f / piglit_width, 16 * 2.0f / piglit_height); glBindFramebuffer(GL_FRAMEBUFFER, result_fbo); piglit_draw_rect(-1, -1, 2, 2); return piglit_probe_rect_rgba(0, 0, 1, 1, expected_color) ? PIGLIT_PASS : PIGLIT_FAIL; } static enum piglit_result test_format(const struct format_desc *format) { enum piglit_result result = PIGLIT_PASS; enum piglit_result color_result; struct component_sizes sizes; GLenum type_param; GLenum tex_target; GLenum tex_error; GLint type; GLuint tex; GLuint fbo; int i; const char *name = get_format_name(format->internalformat); if (format->internalformat == 3 || format->internalformat == 4) return PIGLIT_SKIP; /* Compressed formats aren't supported for multisampling */ if (strstr("COMPRESSED", name)) return PIGLIT_SKIP; printf("Testing %s\n", name); if (single_sample) tex_target = GL_TEXTURE_2D; else tex_target = GL_TEXTURE_2D_MULTISAMPLE; glGenTextures(1, &tex); glBindTexture(tex_target, tex); if (single_sample) { glTexParameteri(tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(tex_target, GL_TEXTURE_MAX_LEVEL, 0); /* The pitch of the texture needs to at least as wide * as a tile and taller than 1 pixel so that it will * be y-tiled in the i965 driver. Otherwise fast * clears will be disabled and the test will be * pointless. */ glTexImage2D(tex_target, 0, /* level */ format->internalformat, 128, 128, /* width/height */ 0, /* border */ GL_RGBA, GL_UNSIGNED_BYTE, NULL); } else { if (piglit_khr_no_error) return PIGLIT_SKIP; piglit_reset_gl_error(); /* The size doesn't matter on the i965 driver for * multisample surfaces because it will always * allocate an MCS buffer and so it will always do * fast clears. */ glTexImage2DMultisample(tex_target, num_samples, format->internalformat, 1, 1, /* width/height */ GL_FALSE /* fixed sample locations */); tex_error = glGetError(); if (tex_error != GL_NO_ERROR) { glDeleteTextures(1, &tex); if (tex_error == GL_INVALID_ENUM) { /* You're only supposed to pass color * renderable formats to * glTexImage2DMultisample. */ printf("Format is not color renderable\n"); return PIGLIT_SKIP; } else { printf("Unexpected GL error: %s 0x%x\n", piglit_get_gl_error_name(tex_error), tex_error); return PIGLIT_FAIL; } } } glGetTexLevelParameteriv(tex_target, 0, GL_TEXTURE_LUMINANCE_SIZE, &sizes.l); glGetTexLevelParameteriv(tex_target, 0, GL_TEXTURE_ALPHA_SIZE, &sizes.a); glGetTexLevelParameteriv(tex_target, 0, GL_TEXTURE_INTENSITY_SIZE, &sizes.i); glGetTexLevelParameteriv(tex_target, 0, GL_TEXTURE_RED_SIZE, &sizes.r); glGetTexLevelParameteriv(tex_target, 0, GL_TEXTURE_GREEN_SIZE, &sizes.g); glGetTexLevelParameteriv(tex_target, 0, GL_TEXTURE_BLUE_SIZE, &sizes.b); if (sizes.l > 0) type_param = GL_TEXTURE_LUMINANCE_TYPE; else if (sizes.i > 0) type_param = GL_TEXTURE_INTENSITY_TYPE; else if (sizes.r > 0) type_param = GL_TEXTURE_RED_TYPE; else if (sizes.a > 0) type_param = GL_TEXTURE_ALPHA_TYPE; else { assert(0); type_param = GL_NONE; } glGetTexLevelParameteriv(tex_target, 0, /* level */ type_param, &type); switch (format->base_internal_format) { case GL_ALPHA: sizes.r = sizes.g = sizes.b = 8; break; case GL_INTENSITY: sizes.r = sizes.g = sizes.b = sizes.a = sizes.i; break; case GL_LUMINANCE: sizes.r = sizes.g = sizes.b = sizes.l; sizes.a = 8; break; case GL_LUMINANCE_ALPHA: sizes.r = sizes.g = sizes.b = sizes.l; break; case GL_RED: sizes.g = sizes.b = sizes.a = 8; break; case GL_RG: sizes.b = sizes.a = 8; break; case GL_RGB: sizes.a = 8; break; } piglit_set_tolerance_for_bits(sizes.r, sizes.g, sizes.b, sizes.a); glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_target, tex, 0); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { for (i = 0; i < ARRAY_SIZE(clear_colors); i++) { color_result = test_color(fbo, i, format, type, &sizes, clear_colors[i]); if (color_result == PIGLIT_SKIP) { if (result == PIGLIT_PASS) result = PIGLIT_SKIP; break; } else if (color_result == PIGLIT_FAIL) { result = PIGLIT_FAIL; } } } else { printf("FBO not complete\n"); result = PIGLIT_SKIP; } glDeleteFramebuffers(1, &fbo); glDeleteTextures(1, &tex); return result; } enum piglit_result piglit_display() { return fbo_formats_display(test_format); } static GLuint build_program(const char *fragment_source) { GLint tex_location; GLuint prog; char *source; (void)!asprintf(&source, fragment_source, single_sample ? "" : "#extension GL_ARB_texture_multisample : require\n", single_sample ? "sampler2D" : "sampler2DMS"); prog = piglit_build_simple_program(vertex_source, source); free(source); glUseProgram(prog); tex_location = glGetUniformLocation(prog, "tex"); glUniform1i(tex_location, 0); return prog; } void piglit_init(int argc, char **argv) { int test_set_index = 0; GLuint rb; int i; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "enable-fb-srgb")) { enable_fb_srgb = true; piglit_require_extension("GL_ARB_framebuffer_sRGB"); } else if (!strcmp(argv[i], "single-sample")) { single_sample = true; } else { test_set_index = fbo_lookup_test_set(argv[i]); } } if (!single_sample) { piglit_require_extension("GL_ARB_texture_multisample"); /* Use the max number of samples for testing */ glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &num_samples); printf("Testing %d samples\n", num_samples); } piglit_require_extension("GL_ARB_texture_float"); piglit_require_GLSL_version(130); test_set = test_set + test_set_index; fbo_formats_init_test_set(test_set_index, GL_TRUE /* print_options */); /* Create a floating point fbo to store the result of * sampling. It is only used to store a single color sampled * from the texture so it doesn't need to bigger than 1x1. */ glGenFramebuffers(1, &result_fbo); glBindFramebuffer(GL_FRAMEBUFFER, result_fbo); glGenRenderbuffers(1, &rb); glBindRenderbuffer(GL_RENDERBUFFER, rb); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA32F, 1, 1 /* width/height */); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { printf("Couldn't create RGBA32F FBO\n"); piglit_report_result(PIGLIT_SKIP); } glBindFramebuffer(GL_FRAMEBUFFER, piglit_winsys_fbo); prog_float = build_program(fragment_source_float); prog_int = build_program(fragment_source_int); prog_uint = build_program(fragment_source_uint); }