diff options
author | Paul Berry <stereotype441@gmail.com> | 2012-05-16 17:03:57 -0700 |
---|---|---|
committer | Paul Berry <stereotype441@gmail.com> | 2012-06-07 10:58:23 -0700 |
commit | 9929b82f40edc2823b7055233caf9024860eac56 (patch) | |
tree | 2d6177cbb34faaff21e4a49a2b853944acc51ee3 | |
parent | 2cbabcd22eac2f265e79457b6965880dbac570cd (diff) |
msaa: Test that MSAA works properly for all core FBO color formats.piglit/master
This test builds on the existing FBO and MSAA testing infrastructure
to verify proper functioning of MSAA for various formats.
All core formats which are framebufferbuffer complete are tested. All
formats which are not framebuffer complete are skipped.
Tests for non-core and depth/stencil formats will be added in later
patches.
Verified using the proprietary nVidia driver for Linux--all formats
pass except the LUMINANCE_ALPHA formats, which fail due to a driver
bug (when rendering to these formats, the nVidia driver maps the red
channel to both luminance and alpha, instead of mapping red to
luminance and alpha to alpha).
Reviewed-by: Anuj Phogat <anuj.phogat@gmail.com>
-rw-r--r-- | tests/all.tests | 6 | ||||
-rw-r--r-- | tests/spec/ext_framebuffer_multisample/CMakeLists.gl.txt | 1 | ||||
-rw-r--r-- | tests/spec/ext_framebuffer_multisample/formats.cpp | 440 |
3 files changed, 447 insertions, 0 deletions
diff --git a/tests/all.tests b/tests/all.tests index 93716a86d..129a923d5 100644 --- a/tests/all.tests +++ b/tests/all.tests @@ -1373,6 +1373,12 @@ for num_samples in (2, 4, 8, 16, 32): test_name) ext_framebuffer_multisample[test_name] = PlainExecTest(executable) +for num_samples in (2, 4, 8, 16, 32): + test_name = ' '.join(['formats', str(num_samples)]) + executable = 'ext_framebuffer_multisample-{0} -auto'.format( + test_name) + ext_framebuffer_multisample[test_name] = PlainExecTest(executable) + ext_framebuffer_object = Group() spec['EXT_framebuffer_object'] = ext_framebuffer_object add_fbo_stencil_tests(ext_framebuffer_object, 'GL_STENCIL_INDEX1') diff --git a/tests/spec/ext_framebuffer_multisample/CMakeLists.gl.txt b/tests/spec/ext_framebuffer_multisample/CMakeLists.gl.txt index 155190fee..16600679f 100644 --- a/tests/spec/ext_framebuffer_multisample/CMakeLists.gl.txt +++ b/tests/spec/ext_framebuffer_multisample/CMakeLists.gl.txt @@ -11,6 +11,7 @@ link_libraries ( piglit_add_executable (ext_framebuffer_multisample-accuracy common.cpp accuracy.cpp) piglit_add_executable (ext_framebuffer_multisample-dlist dlist.c) +piglit_add_executable (ext_framebuffer_multisample-formats common.cpp formats.cpp) piglit_add_executable (ext_framebuffer_multisample-line-smooth common.cpp line-smooth.cpp) piglit_add_executable (ext_framebuffer_multisample-minmax minmax.c) piglit_add_executable (ext_framebuffer_multisample-multisample-blit common.cpp multisample-blit.cpp) diff --git a/tests/spec/ext_framebuffer_multisample/formats.cpp b/tests/spec/ext_framebuffer_multisample/formats.cpp new file mode 100644 index 000000000..220de798a --- /dev/null +++ b/tests/spec/ext_framebuffer_multisample/formats.cpp @@ -0,0 +1,440 @@ +/* + * Copyright © 2012 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 formats.cpp + * + * Verify the proper functioning of multisample antialiasing for all + * possible buffer formats. + * + * This test operates by rendering an MSAA image twice: once in a + * standard RGBA buffer (the behaviour of which is well tested by the + * other MSAA tests), and once in a buffer with some other format. + * Then it blits both images to corresponding single-sample buffers + * and uses glReadPixels to make sure the same image was drawn in both + * cases (to within the expected tolerance considering the bit depth + * of the two images). + * + * Finally, the images that were compared are drawn on screen to make + * it easier to diagnose failures. + */ + +#include "common.h" +#include "../../fbo/fbo-formats.h" + +int piglit_width = 512, piglit_height = 256; +int piglit_window_mode = GLUT_DOUBLE | GLUT_RGBA | GLUT_ALPHA; + +namespace { + +const int pattern_width = 256; const int pattern_height = 256; + +int num_samples; + +TestPattern *test_pattern; + + +/** + * This class encapsulates the code necessary to draw the test pattern + * in either the reference GL_RGBA format or the format under test, + * downsample it, read the rendered pixels into memory, and draw a + * visualization of the result. + */ +class PatternRenderer +{ +public: + bool try_setup(GLenum internalformat); + void set_piglit_tolerance(); + void draw(); + float *read_image(GLenum base_format); + + /** + * Number of bits in each color channel. E.g. color_bits[2] + * == number of bits in blue color channel. + */ + GLint color_bits[4]; + + Fbo fbo_msaa; + Fbo fbo_downsampled; +}; + + +/** + * Try to set up the necessary framebuffers to render to the given + * MSAA format. Return false if one or more of the framebuffers is + * incomplete. + */ +bool +PatternRenderer::try_setup(GLenum internalformat) +{ + FboConfig config_downsampled(0, pattern_width, pattern_height); + config_downsampled.color_internalformat = internalformat; + + FboConfig config_msaa = config_downsampled; + config_msaa.num_samples = num_samples; + + if (!(fbo_downsampled.try_setup(config_downsampled) && + fbo_msaa.try_setup(config_msaa))) + return false; + + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_downsampled.handle); + glGetFramebufferAttachmentParameteriv( + GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, &color_bits[0]); + glGetFramebufferAttachmentParameteriv( + GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, &color_bits[1]); + glGetFramebufferAttachmentParameteriv( + GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, &color_bits[2]); + glGetFramebufferAttachmentParameteriv( + GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, &color_bits[3]); + + return true; +} + + +/** + * Set the piglit tolerance appropriately based on the number of bits + * in each channel. + */ +void PatternRenderer::set_piglit_tolerance() +{ + int tolerance_bits[4]; + + for (int i = 0; i < 4; ++i) { + if (color_bits[i] == 0) { + /* For channels that have 0 bits, test to 8 + * bits precision so we can verify that the + * blit puts in the appropriate value. + */ + tolerance_bits[i] = 8; + } else if (color_bits[i] > 8) { + /* For channels that have >8 bits, test to 8 + * bits precision because we only use an 8-bit + * reference image. + */ + tolerance_bits[i] = 8; + } else { + tolerance_bits[i] = color_bits[i]; + } + } + + piglit_set_tolerance_for_bits(tolerance_bits[0], tolerance_bits[1], + tolerance_bits[2], tolerance_bits[3]); +} + + +/** + * Draw the test pattern into the MSAA framebuffer, and then blit it + * to the downsampled FBO to force an MSAA resolve. + */ +void +PatternRenderer::draw() +{ + /* Draw into the MSAA fbo */ + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_msaa.handle); + fbo_msaa.set_viewport(); + test_pattern->draw(TestPattern::no_projection); + + /* Blit to the downsampled fbo, forcing the image to be downsampled */ + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_msaa.handle); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_downsampled.handle); + glBlitFramebuffer(0, 0, pattern_width, pattern_height, + 0, 0, pattern_width, pattern_height, + GL_COLOR_BUFFER_BIT, GL_NEAREST); +} + + +/** + * Read the image from the downsampled FBO into a newly allocated + * array of floats and return it. + */ +float * +PatternRenderer::read_image(GLenum base_format) +{ + unsigned components = piglit_num_components(base_format); + unsigned size = sizeof(float)*components*pattern_width*pattern_height; + float *image = (float *) malloc(size); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_downsampled.handle); + if (base_format == GL_INTENSITY) { + /* GL_INTENSITY is not allowed for ReadPixels so + * substitute GL_LUMINANCE. + */ + base_format = GL_LUMINANCE; + } + glReadPixels(0, 0, pattern_width, pattern_height, base_format, GL_FLOAT, + image); + return image; +} + + +/** + * PatternRenderer used to render the image under test. + */ +PatternRenderer test_renderer; + + +/** + * PatternRenderer used to render the reference image (in GL_RGBA + * format). + */ +PatternRenderer ref_renderer; + + +/** + * Convert the image into a format that can be easily understood by + * visual inspection, and display it on the screen. + * + * Luminance and intensity values are converted to a grayscale value. + * Alpha values are visualized by blending the image with a grayscale + * checkerboard. + */ +void +visualize_image(float *img, GLenum base_internal_format, bool rhs) +{ + unsigned components = piglit_num_components(base_internal_format); + float *visualization = + (float *) malloc(sizeof(float)*3*pattern_width*pattern_height); + for (int y = 0; y < pattern_height; ++y) { + for (int x = 0; x < pattern_width; ++x) { + float r = 0, g = 0, b = 0, a = 1; + float *pixel = + &img[(y * pattern_width + x) * components]; + switch (base_internal_format) { + case GL_ALPHA: + a = pixel[0]; + break; + case GL_RGBA: + a = pixel[3]; + /* Fall through */ + case GL_RGB: + r = pixel[0]; + g = pixel[1]; + b = pixel[2]; + break; + case GL_LUMINANCE_ALPHA: + a = pixel[1]; + /* Fall through */ + case GL_INTENSITY: + case GL_LUMINANCE: + r = pixel[0]; + g = pixel[0]; + b = pixel[0]; + break; + } + float checker = ((x ^ y) & 0x10) ? 0.75 : 0.25; + r = r * a + checker * (1 - a); + g = g * a + checker * (1 - a); + b = b * a + checker * (1 - a); + visualization[(y * pattern_width + x) * 3] = r; + visualization[(y * pattern_width + x) * 3 + 1] = g; + visualization[(y * pattern_width + x) * 3 + 2] = b; + } + } + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glViewport(0, 0, piglit_width, piglit_height); + glUseProgram(0); + glRasterPos2f(rhs ? 0 : -1, -1); + glDrawPixels(pattern_width, pattern_height, GL_RGB, GL_FLOAT, + visualization); + free(visualization); +} + + +/** + * Transform the reference image (which is in GL_RGBA format) to an + * expected image for a given base internal format, using the the + * transformation described in the GL 3.0 spec, table 3.15 (Conversion + * from RGBA, depth, and stencil pixel components to internal texture, + * table, or filter components). In short, the mapping is as follows: + * + * base_internal_format mapping + * GL_ALPHA A -> A + * GL_LUMINANCE R -> L + * GL_LUMINANCE_ALPHA R,A -> L,A + * GL_INTENSITY R -> I + * GL_RED R -> R + * GL_RG R,G -> R,G + * GL_RGB R,G,B -> R,G,B + * GL_RGBA R,G,B,A -> R,G,B,A + */ +float * +compute_expected_image(const float *ref_image, GLenum base_internal_format) +{ + unsigned components = piglit_num_components(base_internal_format); + unsigned num_pixels = pattern_width*pattern_height; + unsigned size = sizeof(float)*components*num_pixels; + float *expected_image = (float *) malloc(size); + for (unsigned i = 0; i < num_pixels; ++i) { + float *expected = &expected_image[components*i]; + const float *ref = &ref_image[4*i]; + for (unsigned j = 0; j < components; ++j) { + switch (base_internal_format) { + case GL_ALPHA: + expected[j] = ref[3]; + break; + case GL_LUMINANCE_ALPHA: + expected[j] = ref[j ? 3 : 0]; + break; + default: + expected[j] = ref[j]; + break; + } + } + } + return expected_image; +} + + +/** + * Test a given internal format. + */ +enum piglit_result +test_format(const struct format_desc *format) +{ + bool pass = true; + + /* Caller messes with the clear color. Reset it to the + * default. + */ + glClearColor(0, 0, 0, 0); + + printf("Testing %s\n", format->name); + + /* Set up the framebuffers for rendering the reference image. + * This shouldn't fail. + */ + bool setup_success = ref_renderer.try_setup(GL_RGBA); + if (!piglit_check_gl_error(GL_NO_ERROR)) { + printf("Error setting up reference renderbuffers\n"); + return PIGLIT_FAIL; + } + if (!setup_success) { + printf("Reference framebuffer combination is unsupported\n"); + return PIGLIT_FAIL; + } + + /* Set up the framebuffers for rendering the test image. This + * might fail if the format we're testing isn't supported as a + * render target, and that's ok. + * + * Note: in order to be sure we test all formats which the + * implementations supports as render targets, we try all of + * them, even formats that the spec doesn't define as + * color-renderable (e.g. GL_LUMINANCE8, which is supported as + * a render target format by some drivers even though it's not + * officially color-renderable). If we tried to request a + * color-renderable format and it wasn't supported, we would + * expect the framebuffer to be incomplete. If we tried to + * request a non-color-renderable format and it wasn't + * supported, we might have received a GL error. In either + * case just skip to the next format. + */ + setup_success = test_renderer.try_setup(format->internalformat); + if (glGetError() != GL_NO_ERROR) { + printf("Error setting up test renderbuffers\n"); + return PIGLIT_SKIP; + } + if (!setup_success) { + printf("Unsupported framebuffer combination\n"); + return PIGLIT_SKIP; + } + + /* Draw test and reference images, and read them into memory */ + test_renderer.set_piglit_tolerance(); + test_renderer.draw(); + float *test_image = + test_renderer.read_image(format->base_internal_format); + ref_renderer.draw(); + float *ref_image = ref_renderer.read_image(GL_RGBA); + + /* Compute the expected image from the reference image */ + float *expected_image = + compute_expected_image(ref_image, + format->base_internal_format); + + /* Check that the test image was correct */ + glBindFramebuffer(GL_READ_FRAMEBUFFER, + test_renderer.fbo_downsampled.handle); + pass = piglit_probe_image_color(0, 0, pattern_width, pattern_height, + format->base_internal_format, + expected_image) && pass; + + /* Show both the test and expected images on screen so that + * the user can diagnose problems. + */ + visualize_image(test_image, format->base_internal_format, false); + visualize_image(expected_image, format->base_internal_format, true); + + /* Finally, if any error occurred, count that as a failure. */ + pass = piglit_check_gl_error(GL_NO_ERROR) && pass; + + free(test_image); + free(ref_image); + free(expected_image); + + return pass ? PIGLIT_PASS : PIGLIT_FAIL; +} + + +void +print_usage_and_exit(char *prog_name) +{ + printf("Usage: %s <num_samples>\n", prog_name); + piglit_report_result(PIGLIT_FAIL); +} + + +extern "C" void +piglit_init(int argc, char **argv) +{ + if (argc < 2) + print_usage_and_exit(argv[0]); + char *endptr = NULL; + num_samples = strtol(argv[1], &endptr, 0); + if (endptr != argv[1] + strlen(argv[1])) + print_usage_and_exit(argv[0]); + + piglit_require_gl_version(30); + + /* Skip the test if num_samples > GL_MAX_SAMPLES */ + GLint max_samples; + glGetIntegerv(GL_MAX_SAMPLES, &max_samples); + if (num_samples > max_samples) + piglit_report_result(PIGLIT_SKIP); + + fbo_formats_init_test_set(0 /* core formats */, + GL_TRUE /* print_options */); + test_pattern = new ColorGradientSunburst(); + test_pattern->compile(); +} + +extern "C" enum piglit_result +piglit_display() +{ + return fbo_formats_display(test_format); +} + +}; |