[Piglit] [PATCH] Add a test of the accuracy of MSAA rendering.
Paul Berry
stereotype441 at gmail.com
Wed Apr 18 11:39:25 PDT 2012
This is the test I've been using to prototype MSAA on the i965
driver. Unlike the existing EXT_framebuffer_multisample
tests (which so far mostly check corner cases at the API level),
this test verifies that correct pixels are rendered when MSAA is
enabled. It's a bit long for a typical piglit test, since in
order to verify that the MSAA color, depth, and stencil buffers
are all configured correctly, it has to render some fairly
complex scenes.
---
This test partially overlaps the functionality of the test Anuj
sent to the list on Monday ([PATCH] Add test to switch on/off
MSAA in FBO). Anuj: I'm interested in hearing how my test
performs on your AMD system. Everyone: I'm interested in hearing
opinions about whether this test is too big, and if so, how to
effectively split it up into smaller tests.
tests/all.tests | 21 +
.../ext_framebuffer_multisample/CMakeLists.gl.txt | 1 +
.../spec/ext_framebuffer_multisample/accuracy.cpp | 1430 ++++++++++++++++++++
3 files changed, 1452 insertions(+), 0 deletions(-)
create mode 100644 tests/spec/ext_framebuffer_multisample/accuracy.cpp
diff --git a/tests/all.tests b/tests/all.tests
index 0d1d660..19f7bac 100644
--- a/tests/all.tests
+++ b/tests/all.tests
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# All tests that come with piglit, using default settings
+import itertools
import os
import os.path as path
@@ -40,6 +41,16 @@ def concurrent_test(args):
def add_concurrent_test(group, args):
group[args] = concurrent_test(args)
+# Generate all possible subsets of the given set, including the empty set.
+def power_set(s):
+ if len(s) == 0:
+ return [[]]
+ result = []
+ for p in power_set(s[:-1]):
+ result.append(p)
+ result.append(p + [s[-1]])
+ return result
+
######
# Collecting all tests
profile = TestProfile()
@@ -1286,6 +1297,16 @@ ext_framebuffer_multisample['renderbufferstorage-samples'] = concurrent_test('ex
ext_framebuffer_multisample['renderbuffer-samples'] = concurrent_test('ext_framebuffer_multisample-renderbuffer-samples')
ext_framebuffer_multisample['samples'] = concurrent_test('ext_framebuffer_multisample-samples')
+for num_samples in (2, 4, 8, 16, 32):
+ for test_type in ('color', 'stencil_draw', 'stencil_resolve',
+ 'depth_draw', 'depth_resolve'):
+ for options in power_set(('small', 'depthstencil')):
+ test_name = ' '.join(['accuracy', str(num_samples), test_type]
+ + options)
+ 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 d6b85e1..c451f9f 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 (
${GLUT_glut_LIBRARY}
)
+piglit_add_executable (ext_framebuffer_multisample-accuracy accuracy.cpp)
piglit_add_executable (ext_framebuffer_multisample-dlist dlist.c)
piglit_add_executable (ext_framebuffer_multisample-minmax minmax.c)
piglit_add_executable (ext_framebuffer_multisample-negative-copypixels negative-copypixels.c)
diff --git a/tests/spec/ext_framebuffer_multisample/accuracy.cpp b/tests/spec/ext_framebuffer_multisample/accuracy.cpp
new file mode 100644
index 0000000..bb9483e
--- /dev/null
+++ b/tests/spec/ext_framebuffer_multisample/accuracy.cpp
@@ -0,0 +1,1430 @@
+/*
+ * 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 accuracy.cpp
+ *
+ * Verify the accuracy of multisample antialiasing.
+ *
+ * This test operates by rendering a scene consisting of triangles
+ * that aren't perfectly aligned to pixel coordinates. Every triangle
+ * in the scene is rendered using a solid color whose color components
+ * are all 0.0 or 1.0. The scene is renederd in two ways:
+ *
+ * - At normal resoluation, using MSAA.
+ *
+ * - At very high resolution ("supersampled" by a factor of 16 in both
+ * X and Y dimensions), without MSAA.
+ *
+ * Then, the supersampled image is scaled down to match the resolution
+ * of the MSAA image, using a fragment shader to manually blend each
+ * block of 16x16 pixels down to 1 pixel. This produces a reference
+ * image, which is then compared to the MSAA image to measure the
+ * error introduced by MSAA.
+ *
+ * (Note: the supersampled image is actually larger than the maximum
+ * texture size that GL 3.0 requires all implementations to support
+ * (1024x1024), so it is actually done in 1024x1024 tiles that are
+ * then stitched together to form the reference image).
+ *
+ * In the piglit window, the MSAA image appears on the left; the
+ * reference image is on the right.
+ *
+ * For each color component of each pixel, if the reference image has
+ * a value of exactly 0.0 or 1.0, that pixel is presumed to be
+ * completely covered by a triangle, so the test verifies that the
+ * corresponding pixel in the MSAA image is exactly 0.0 or 1.0. Where
+ * the reference image has a value between 0.0 and 1.0, we know there
+ * is a triangle boundary that MSAA should smooth out, so the test
+ * estimates the accuracy of MSAA rendering by computing the RMS error
+ * between the reference image and the MSAA image for these pixels.
+ *
+ * In addition to the above test (the "color" test), this test can
+ * also verify the proper behavior of the stencil MSAA buffer. This
+ * can be done in two ways:
+ *
+ * - "stencil_draw" test: after drawing the scene, we clear the MSAA
+ * color buffer and run a "manifest" pass which uses stencil
+ * operations to make a visual representation of the contents of the
+ * stencil buffer show up in the color buffer. The rest of the test
+ * operates as usual. This allows us to verify that drawing
+ * operations that use the stencil buffer operate correctly in MSAA
+ * mode.
+ *
+ * - "stencil_resolve" test: same as above, except that we blit the
+ * MSAA stencil buffer to a single-sampled FBO before running the
+ * "manifest" pass. This allows us to verify that the
+ * implementation properly downsamples the MSAA stencil buffer.
+ *
+ * There are similar variants "depth_draw" and "depth_resolve" for
+ * testing the MSAA depth buffer.
+ *
+ * Note that when downsampling the MSAA color buffer, implementations
+ * are expected to blend the values of each of the color samples;
+ * but when downsampling the stencil and depth buffers, they are
+ * expected to just choose one representative sample (this is because
+ * an intermediate stencil or depth value would not be meaningful).
+ * Therefore, the pass threshold is relaxed for the "stencil_resolve"
+ * and "depth_resolve" tests.
+ *
+ * The test also accepts the following flags:
+ *
+ * - "small": Causes the MSAA image to be renedered in extremely tiny
+ * (16x16) tiles that are then stitched together. This verifies
+ * that MSAA works properly on very small buffers (a critical corner
+ * case on i965).
+ *
+ * - "depthstencil": Causes the framebuffers to use a combined
+ * depth/stencil buffer (as opposed to separate depth and stencil
+ * buffers). On some implementations (e.g. the nVidia proprietary
+ * driver for Linux) this is necessary for framebuffer completeness.
+ * On others (e.g. i965), this is an important corner case to test.
+ */
+
+#include "piglit-util.h"
+#include <math.h>
+
+int piglit_width = 512;
+int piglit_height = 256;
+int piglit_window_mode = GLUT_RGBA | GLUT_ALPHA | GLUT_DOUBLE;
+
+namespace {
+
+const int pattern_width = 256;
+const int pattern_height = 256;
+const int supersample_factor = 16;
+
+/**
+ * Data structure representing one of the framebuffer objects used in
+ * the test.
+ *
+ * For the supersampled framebuffer object we use a texture as the
+ * backing store for the color buffer so that we can use a fragment
+ * shader to blend down to the reference image.
+ */
+class Fbo
+{
+public:
+ void init(int num_samples, int width, int height,
+ bool combine_depth_stencil);
+ void set_viewport();
+
+ int width;
+ int height;
+ GLuint handle;
+
+ /**
+ * If this Fbo is not multisampled, the texture that is the
+ * backing store for the color buffer.
+ */
+ GLuint color_tex;
+};
+
+void
+Fbo::init(int num_samples, int width, int height, bool combine_depth_stencil)
+{
+ this->color_tex = 0;
+ this->width = width;
+ this->height = height;
+
+ glGenFramebuffers(1, &handle);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, handle);
+
+ /* Color buffer */
+ if (num_samples != 0) {
+ GLuint rb;
+ glGenRenderbuffers(1, &rb);
+ glBindRenderbuffer(GL_RENDERBUFFER, rb);
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples,
+ GL_RGBA, width, height);
+ glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, rb);
+ } else {
+ glGenTextures(1, &color_tex);
+ glBindTexture(GL_TEXTURE_2D, color_tex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+ GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D,
+ 0 /* level */,
+ GL_RGBA /* internalformat */,
+ width,
+ height,
+ 0 /* border */,
+ GL_RGBA /* format */,
+ GL_BYTE /* type */,
+ NULL /* data */);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ color_tex,
+ 0 /* level */);
+ }
+
+ /* Depth/stencil buffer(s) */
+ if (combine_depth_stencil) {
+ GLuint depth_stencil;
+ glGenRenderbuffers(1, &depth_stencil);
+ glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil);
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples,
+ GL_DEPTH_STENCIL, width,
+ height);
+ glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,
+ GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, depth_stencil);
+ } else {
+ GLuint stencil;
+ glGenRenderbuffers(1, &stencil);
+ glBindRenderbuffer(GL_RENDERBUFFER, stencil);
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples,
+ GL_STENCIL_INDEX8,
+ width, height);
+ glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,
+ GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, stencil);
+
+ GLuint depth;
+ glGenRenderbuffers(1, &depth);
+ glBindRenderbuffer(GL_RENDERBUFFER, depth);
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples,
+ GL_DEPTH_COMPONENT24,
+ width, height);
+ glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,
+ GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER, depth);
+ }
+
+ if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ printf("Framebuffer not complete\n");
+ if (!combine_depth_stencil) {
+ /* Some implementations do not support
+ * separate depth and stencil attachments, so
+ * don't consider it an error if we fail to
+ * make a complete framebuffer using separate
+ * depth and stencil attachments.
+ */
+ piglit_report_result(PIGLIT_SKIP);
+ } else {
+ piglit_report_result(PIGLIT_FAIL);
+ }
+ }
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+}
+
+void
+Fbo::set_viewport()
+{
+ glViewport(0, 0, width, height);
+}
+
+/**
+ * Fragment shader program we apply to the supersampled color buffer
+ * to produce the reference image. This program manually blends each
+ * 16x16 block of samples in the supersampled color buffer down to a
+ * single sample in the downsampled buffer.
+ */
+class DownsampleProg
+{
+public:
+ void compile();
+ void run(const Fbo *src_fbo, int dest_width, int dest_height);
+
+private:
+ GLint prog;
+ GLuint vertex_buf;
+ GLuint vao;
+};
+
+void
+DownsampleProg::compile()
+{
+ static const char *vert =
+ "#version 130\n"
+ "in vec2 pos;\n"
+ "in vec2 texCoord;\n"
+ "out vec2 texCoordVarying;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(pos, 0.0, 1.0);\n"
+ " texCoordVarying = texCoord;\n"
+ "}\n";
+
+ static const char *frag =
+ "#version 130\n"
+ "uniform sampler2D samp;\n"
+ "uniform int supersample_factor;\n"
+ "in vec2 texCoordVarying;\n"
+ "void main()\n"
+ "{\n"
+ " vec4 sum = vec4(0.0);\n"
+ " ivec2 pixel = ivec2(texCoordVarying);\n"
+ " for (int i = 0; i < supersample_factor; ++i) {\n"
+ " for (int j = 0; j < supersample_factor; ++j) {\n"
+ " sum += texelFetch(\n"
+ " samp, pixel * supersample_factor + ivec2(i, j), 0);\n"
+ " }\n"
+ " }\n"
+ " gl_FragColor = sum / (supersample_factor * supersample_factor);\n"
+ "}\n";
+
+ /* Compile program */
+ prog = glCreateProgram();
+ GLint vs = piglit_compile_shader_text(GL_VERTEX_SHADER, vert);
+ glAttachShader(prog, vs);
+ GLint fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER, frag);
+ glAttachShader(prog, fs);
+ glBindAttribLocation(prog, 0, "pos");
+ glBindAttribLocation(prog, 1, "texCoord");
+ glLinkProgram(prog);
+ if (!piglit_link_check_status(prog)) {
+ piglit_report_result(PIGLIT_FAIL);
+ }
+
+ /* Set up uniforms */
+ glUseProgram(prog);
+ glUniform1i(glGetUniformLocation(prog, "supersample_factor"),
+ supersample_factor);
+ glUniform1i(glGetUniformLocation(prog, "samp"), 0);
+
+ /* Set up vertex array object */
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+
+ /* Set up vertex input buffer */
+ glGenBuffers(1, &vertex_buf);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buf);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float),
+ (void *) 0);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float),
+ (void *) (2*sizeof(float)));
+
+ /* Set up element input buffer to tesselate a quad into
+ * triangles
+ */
+ unsigned int indices[6] = { 0, 1, 2, 0, 2, 3 };
+ GLuint element_buf;
+ glGenBuffers(1, &element_buf);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buf);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,
+ GL_STATIC_DRAW);
+}
+
+void
+DownsampleProg::run(const Fbo *src_fbo, int dest_width, int dest_height)
+{
+ float w = dest_width;
+ float h = dest_height;
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, src_fbo->color_tex);
+
+ glUseProgram(prog);
+ glBindVertexArray(vao);
+
+ float vertex_data[4][4] = {
+ { -1, -1, 0, 0 },
+ { -1, 1, 0, h },
+ { 1, 1, w, h },
+ { 1, -1, w, 0 }
+ };
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buf);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data,
+ GL_STREAM_DRAW);
+
+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void *) 0);
+}
+
+/**
+ * There are two programs used to "manifest" an auxiliary buffer,
+ * turning it into visible colors: one for manifesting the stencil
+ * buffer, and one for manifesting the depth buffer. This is the base
+ * class that they both derive from.
+ */
+class ManifestProgram
+{
+public:
+ virtual void compile() = 0;
+ virtual void run() = 0;
+};
+
+/**
+ * Program we use to manifest the stencil buffer.
+ *
+ * This program operates by repeatedly drawing over the entire buffer
+ * using the stencil function "EQUAL", and a different color each
+ * time. This causes stencil values from 0 to 7 to manifest as colors
+ * (black, blue, green, cyan, red, magenta, yellow, white).
+ */
+class ManifestStencil : public ManifestProgram
+{
+public:
+ virtual void compile();
+ virtual void run();
+
+private:
+ GLint prog;
+ GLint color_loc;
+ GLuint vertex_buf;
+ GLuint vao;
+};
+
+void
+ManifestStencil::compile()
+{
+ static const char *vert =
+ "#version 130\n"
+ "in vec2 pos;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(pos, 0.0, 1.0);\n"
+ "}\n";
+
+ static const char *frag =
+ "#version 130\n"
+ "uniform vec4 color;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = color;\n"
+ "}\n";
+
+ /* Compile program */
+ prog = glCreateProgram();
+ GLint vs = piglit_compile_shader_text(GL_VERTEX_SHADER, vert);
+ glAttachShader(prog, vs);
+ GLint fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER, frag);
+ glAttachShader(prog, fs);
+ glBindAttribLocation(prog, 0, "pos");
+ glLinkProgram(prog);
+ if (!piglit_link_check_status(prog)) {
+ piglit_report_result(PIGLIT_FAIL);
+ }
+
+ /* Set up uniforms */
+ glUseProgram(prog);
+ color_loc = glGetUniformLocation(prog, "color");
+
+ /* Set up vertex array object */
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+
+ /* Set up vertex input buffer */
+ float vertex_data[4][2] = {
+ { -1, -1 },
+ { -1, 1 },
+ { 1, 1 },
+ { 1, -1 }
+ };
+ glGenVertexArrays(1, &vertex_buf);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buf);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data,
+ GL_STATIC_DRAW);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_data[0]),
+ (void *) 0);
+
+ /* Set up element input buffer to tesselate a quad into
+ * triangles
+ */
+ unsigned int indices[6] = { 0, 1, 2, 0, 2, 3 };
+ GLuint element_buf;
+ glGenBuffers(1, &element_buf);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buf);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,
+ GL_STATIC_DRAW);
+}
+
+void
+ManifestStencil::run()
+{
+ static float colors[8][4] = {
+ { 0.0, 0.0, 0.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 1.0, 1.0 },
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 1.0, 1.0, 1.0, 1.0 }
+ };
+
+ glUseProgram(prog);
+ glBindVertexArray(vao);
+
+ glEnable(GL_STENCIL_TEST);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+
+ /* Clear the color buffer to 0, in case the stencil buffer
+ * contains any values outside the range 0..7
+ */
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ for (int i = 0; i < 8; ++i) {
+ glStencilFunc(GL_EQUAL, i, 0xff);
+ glUniform4fv(color_loc, 1, colors[i]);
+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void *) 0);
+ }
+
+ glDisable(GL_STENCIL_TEST);
+}
+
+/**
+ * Program we use to manifest the depth buffer.
+ *
+ * This program operates by repeatedly drawing over the entire buffer
+ * at decreasing depth values with depth test enabled; the stencil
+ * function is configured to "EQUAL" with a stencil op of "INCR", so
+ * that after a sample passes the depth test, its stencil value will
+ * be incremented and it will fail the stencil test on later draws.
+ * As a result, depth values from back to front will manifest as
+ * colors (black, blue, green, cyan, red, magenta, yellow, white).
+ */
+class ManifestDepth : public ManifestProgram
+{
+public:
+ virtual void compile();
+ virtual void run();
+
+private:
+ GLint prog;
+ GLint color_loc;
+ GLint depth_loc;
+ GLuint vertex_buf;
+ GLuint vao;
+};
+
+void
+ManifestDepth::compile()
+{
+ static const char *vert =
+ "#version 130\n"
+ "in vec2 pos;\n"
+ "uniform float depth;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(pos, depth, 1.0);\n"
+ "}\n";
+
+ static const char *frag =
+ "#version 130\n"
+ "uniform vec4 color;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = color;\n"
+ "}\n";
+
+ /* Compile program */
+ prog = glCreateProgram();
+ GLint vs = piglit_compile_shader_text(GL_VERTEX_SHADER, vert);
+ glAttachShader(prog, vs);
+ GLint fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER, frag);
+ glAttachShader(prog, fs);
+ glBindAttribLocation(prog, 0, "pos");
+ glLinkProgram(prog);
+ if (!piglit_link_check_status(prog)) {
+ piglit_report_result(PIGLIT_FAIL);
+ }
+
+ /* Set up uniforms */
+ glUseProgram(prog);
+ color_loc = glGetUniformLocation(prog, "color");
+ depth_loc = glGetUniformLocation(prog, "depth");
+
+ /* Set up vertex array object */
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+
+ /* Set up vertex input buffer */
+ float vertex_data[4][2] = {
+ { -1, -1 },
+ { -1, 1 },
+ { 1, 1 },
+ { 1, -1 }
+ };
+ glGenVertexArrays(1, &vertex_buf);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buf);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data,
+ GL_STATIC_DRAW);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_data[0]),
+ (void *) 0);
+
+ /* Set up element input buffer to tesselate a quad into
+ * triangles
+ */
+ unsigned int indices[6] = { 0, 1, 2, 0, 2, 3 };
+ GLuint element_buf;
+ glGenBuffers(1, &element_buf);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buf);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,
+ GL_STATIC_DRAW);
+}
+
+void
+ManifestDepth::run()
+{
+ static float colors[8][4] = {
+ { 0.0, 0.0, 0.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 1.0, 1.0 },
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 1.0, 1.0, 1.0, 1.0 }
+ };
+
+ glUseProgram(prog);
+ glBindVertexArray(vao);
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glEnable(GL_STENCIL_TEST);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
+ glStencilFunc(GL_EQUAL, 0, 0xff);
+
+ /* Clear the stencil buffer to 0, leaving depth and color
+ * buffers unchanged.
+ */
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ for (int i = 0; i < 8; ++i) {
+ glUniform4fv(color_loc, 1, colors[i]);
+ glUniform1f(depth_loc, float(7 - 2*i)/8);
+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void *) 0);
+ }
+
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_DEPTH_TEST);
+}
+
+/**
+ * There are three programs used to draw a test pattern, depending on
+ * whether we are testing the color buffer, the depth buffer, or the
+ * stencil buffer. This is the base class that they all derive from.
+ */
+class TestPattern
+{
+public:
+ virtual void compile() = 0;
+
+ /**
+ * Draw the test pattern, applying the given projection matrix
+ * to vertex coordinates. The projection matrix is in
+ * row-major order.
+ */
+ virtual void draw(float (*proj)[4]) = 0;
+};
+
+/**
+ * Program we use to draw a test pattern into the color buffer.
+ *
+ * This program draws a sequence of small triangles, each rotated at a
+ * different angle. This ensures that the image will have a large
+ * number of edges at different angles, so that we'll thoroughly
+ * exercise antialiasing.
+ */
+class Triangles : public TestPattern
+{
+public:
+ virtual void compile();
+ virtual void draw(float (*proj)[4]);
+
+private:
+ GLint prog;
+ GLuint vertex_buf;
+ GLuint vao;
+ GLint proj_loc;
+ GLint tri_num_loc;
+ int num_tris;
+};
+
+void Triangles::compile()
+{
+ /* Triangle coords within (-1,-1) to (1,1) rect */
+ static const float pos_within_tri[][2] = {
+ { -0.5, -1.0 },
+ { 0.0, 1.0 },
+ { 0.5, -1.0 }
+ };
+
+ /* Number of triangle instances across (and down) */
+ int tris_across = 8;
+
+ /* Total number of triangles drawn */
+ num_tris = tris_across * tris_across;
+
+ /* Scaling factor uniformly applied to triangle coords */
+ float tri_scale = 0.8 / tris_across;
+
+ /* Amount each triangle should be rotated compared to prev */
+ float rotation_delta = M_PI * 2.0 / num_tris;
+
+ /* Final scaling factor */
+ float final_scale = 0.95;
+
+ static const char *vert =
+ "#version 130\n"
+ "in vec2 pos_within_tri;\n"
+ "uniform float tri_scale;\n"
+ "uniform float rotation_delta;\n"
+ "uniform int tris_across;\n"
+ "uniform float final_scale;\n"
+ "uniform mat4 proj;\n"
+ "uniform int tri_num;\n"
+ "\n"
+ "void main()\n"
+ "{\n"
+ " vec2 pos = tri_scale * pos_within_tri;\n"
+ " float rotation = rotation_delta * tri_num;\n"
+ " pos = mat2(cos(rotation), sin(rotation),\n"
+ " -sin(rotation), cos(rotation)) * pos;\n"
+ " int i = tri_num % tris_across;\n"
+ " int j = tris_across - 1 - tri_num / tris_across;\n"
+ " pos += (vec2(i, j) * 2.0 + 1.0) / tris_across - 1.0;\n"
+ " pos *= final_scale;\n"
+ " gl_Position = proj * vec4(pos, 0.0, 1.0);\n"
+ "}\n";
+
+ static const char *frag =
+ "#version 130\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = vec4(1.0);\n"
+ "}\n";
+
+ /* Compile program */
+ prog = glCreateProgram();
+ GLint vs = piglit_compile_shader_text(GL_VERTEX_SHADER, vert);
+ glAttachShader(prog, vs);
+ GLint fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER, frag);
+ glAttachShader(prog, fs);
+ glBindAttribLocation(prog, 0, "pos_within_tri");
+ glLinkProgram(prog);
+ if (!piglit_link_check_status(prog)) {
+ piglit_report_result(PIGLIT_FAIL);
+ }
+
+ /* Set up uniforms */
+ glUseProgram(prog);
+ glUniform1f(glGetUniformLocation(prog, "tri_scale"), tri_scale);
+ glUniform1f(glGetUniformLocation(prog, "rotation_delta"),
+ rotation_delta);
+ glUniform1i(glGetUniformLocation(prog, "tris_across"), tris_across);
+ glUniform1f(glGetUniformLocation(prog, "final_scale"), final_scale);
+ proj_loc = glGetUniformLocation(prog, "proj");
+ tri_num_loc = glGetUniformLocation(prog, "tri_num");
+
+ /* Set up vertex array object */
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+
+ /* Set up vertex input buffer */
+ glGenBuffers(1, &vertex_buf);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buf);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(pos_within_tri), pos_within_tri,
+ GL_STATIC_DRAW);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, ARRAY_SIZE(pos_within_tri[0]), GL_FLOAT,
+ GL_FALSE, sizeof(pos_within_tri[0]), (void *) 0);
+}
+
+void Triangles::draw(float (*proj)[4])
+{
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glUseProgram(prog);
+ glUniformMatrix4fv(proj_loc, 1, GL_TRUE, &proj[0][0]);
+ glBindVertexArray(vao);
+ for (int tri_num = 0; tri_num < num_tris; ++tri_num) {
+ glUniform1i(tri_num_loc, tri_num);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ }
+}
+
+/**
+ * Program we use to draw a test pattern into the depth and stencil
+ * buffers.
+ *
+ * This program draws a "sunburst" pattern consisting of 7 overlapping
+ * triangles, each at a different angle. This ensures that the
+ * triangles overlap in a complex way, with the edges between them
+ * covering a a large number of different angles, so that we'll
+ * thoroughly exercise antialiasing.
+ *
+ * This program is further specialized into depth and stencil variants.
+ */
+class Sunburst : public TestPattern
+{
+public:
+ virtual void compile();
+
+protected:
+ GLint prog;
+ GLint rotation_loc;
+ GLint depth_loc;
+ GLint proj_loc;
+ GLuint vao;
+ int num_tris;
+
+private:
+ GLuint vertex_buf;
+};
+
+void Sunburst::compile()
+{
+ /* Triangle coords within (-1,-1) to (1,1) rect */
+ static const float pos_within_tri[][2] = {
+ { -0.3, -0.8 },
+ { 0.0, 1.0 },
+ { 0.3, -0.8 }
+ };
+
+ /* Total number of triangles drawn */
+ num_tris = 7;
+
+ static const char *vert =
+ "#version 130\n"
+ "in vec2 pos_within_tri;\n"
+ "uniform float rotation;\n"
+ "uniform float depth;\n"
+ "uniform mat4 proj;\n"
+ "\n"
+ "void main()\n"
+ "{\n"
+ " vec2 pos = pos_within_tri;\n"
+ " pos = mat2(cos(rotation), sin(rotation),\n"
+ " -sin(rotation), cos(rotation)) * pos;\n"
+ " gl_Position = proj * vec4(pos, depth, 1.0);\n"
+ "}\n";
+
+ static const char *frag =
+ "#version 130\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = vec4(0.0);\n"
+ "}\n";
+
+ /* Compile program */
+ prog = glCreateProgram();
+ GLint vs = piglit_compile_shader_text(GL_VERTEX_SHADER, vert);
+ glAttachShader(prog, vs);
+ GLint fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER, frag);
+ glAttachShader(prog, fs);
+ glBindAttribLocation(prog, 0, "pos_within_tri");
+ glLinkProgram(prog);
+ if (!piglit_link_check_status(prog)) {
+ piglit_report_result(PIGLIT_FAIL);
+ }
+
+ /* Set up uniforms */
+ glUseProgram(prog);
+ rotation_loc = glGetUniformLocation(prog, "rotation");
+ depth_loc = glGetUniformLocation(prog, "depth");
+ glUniform1f(depth_loc, 0.0);
+ proj_loc = glGetUniformLocation(prog, "proj");
+
+ /* Set up vertex array object */
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+
+ /* Set up vertex input buffer */
+ glGenBuffers(1, &vertex_buf);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buf);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(pos_within_tri), pos_within_tri,
+ GL_STATIC_DRAW);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, ARRAY_SIZE(pos_within_tri[0]), GL_FLOAT,
+ GL_FALSE, sizeof(pos_within_tri[0]), (void *) 0);
+}
+
+/**
+ * Program we use to draw a test pattern into the stencil buffer.
+ *
+ * The triangles in this sunburst are drawn back-to-front, using no
+ * depth testing. Each triangle is drawn using a different stencil
+ * value.
+ */
+class StencilSunburst : public Sunburst
+{
+public:
+ virtual void draw(float (*proj)[4]);
+};
+
+void
+StencilSunburst::draw(float (*proj)[4])
+{
+ glEnable(GL_STENCIL_TEST);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ glUseProgram(prog);
+ glUniformMatrix4fv(proj_loc, 1, GL_TRUE, &proj[0][0]);
+ glBindVertexArray(vao);
+ for (int i = 0; i < num_tris; ++i) {
+ glStencilFunc(GL_ALWAYS, i+1, 0xff);
+ glUniform1f(rotation_loc, M_PI * 2.0 * i / num_tris);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ }
+
+ glDisable(GL_STENCIL_TEST);
+}
+
+/**
+ * Program we use to draw a test pattern into the depth buffer.
+ *
+ * The triangles in this sunburst are drawn at a series of different
+ * depth values, with depth testing enabled. They are drawn in an
+ * arbitrary non-consecutive order, to verify that depth testing
+ * properly sorts the surfaces into front-to-back order.
+ */
+class DepthSunburst : public Sunburst
+{
+public:
+ virtual void draw(float (*proj)[4]);
+};
+
+void
+DepthSunburst::draw(float (*proj)[4])
+{
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glUseProgram(prog);
+ glUniformMatrix4fv(proj_loc, 1, GL_TRUE, &proj[0][0]);
+ glBindVertexArray(vao);
+ for (int i = 0; i < num_tris; ++i) {
+ /* Draw triangles in a haphazard order so we can
+ * verify that depth comparisons sort them out
+ * properly.
+ */
+ int triangle_to_draw = (i * 3) % num_tris;
+
+ /* Note: with num_tris == 7, this causes us to draw
+ * triangles at depths of 3/4, 1/2, -1/4, 0, 1/4, 1/2,
+ * and 3/4.
+ */
+ glUniform1f(depth_loc,
+ float(num_tris - triangle_to_draw * 2 - 1)
+ / (num_tris + 1));
+
+ glUniform1f(rotation_loc,
+ M_PI * 2.0 * triangle_to_draw / num_tris);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ }
+
+ glDisable(GL_DEPTH_TEST);
+}
+
+/**
+ * Data structure for keeping track of statistics on pixel accuracy.
+ *
+ * We keep track of the number of pixels tested, and the sum of the
+ * squared error, so that we can summarize the RMS error at the
+ * conclusion of the test.
+ */
+class Stats
+{
+public:
+ Stats();
+
+ void record(float error)
+ {
+ ++count;
+ sum_squared_error += error * error;
+ }
+
+ void summarize();
+
+ bool is_perfect();
+
+ bool is_better_than(double rms_error_threshold);
+
+private:
+ int count;
+ double sum_squared_error;
+};
+
+Stats::Stats()
+ : count(0), sum_squared_error(0.0)
+{
+}
+
+void
+Stats::summarize()
+{
+ printf(" count = %d\n", count);
+ if (count != 0) {
+ if (sum_squared_error != 0.0) {
+ printf(" RMS error = %f\n",
+ sqrt(sum_squared_error / count));
+ } else {
+ printf(" Perfect output\n");
+ }
+ }
+}
+
+bool
+Stats::is_perfect()
+{
+ return sum_squared_error == 0.0;
+}
+
+bool
+Stats::is_better_than(double rms_error_threshold)
+{
+ return sqrt(sum_squared_error / count) < rms_error_threshold;
+}
+
+/**
+ * This data structure wraps up all the data we need to keep track of
+ * to run the test.
+ */
+class Test
+{
+public:
+ Test(TestPattern *pattern, ManifestProgram *manifest_program,
+ bool test_resolve, GLbitfield blit_type);
+ void init(int num_samples, bool small, bool combine_depth_stencil);
+ piglit_result run();
+
+private:
+ void draw_test_image();
+ void draw_reference_image();
+ piglit_result measure_accuracy();
+ void resolve(GLbitfield which_buffers);
+ void downsample_color(int downsampled_width, int downsampled_height);
+ void show(Fbo *src_fbo, int x_offset, int y_offset);
+ void draw_pattern(int x_offset, int y_offset, int width, int height);
+
+ /** The test pattern to draw. */
+ TestPattern *pattern;
+
+ /**
+ * The program to use to manifest depth or stencil into color,
+ * or NULL if we're just testing color rendering.
+ */
+ ManifestProgram *manifest_program;
+
+ /**
+ * True if we are testing the resolve pass, so we should
+ * downsample before manifesting; false if we should manifest
+ * before downsampling.
+ */
+ bool test_resolve;
+
+ /**
+ * The buffer under test--this should be compatible with the
+ * "mask" argument of
+ * glBlitFramebuffer--i.e. GL_COLOR_BUFFER_BIT,
+ * GL_STENCIL_BUFFER_BIT, or GL_DEPTH_BUFFER_BIT.
+ */
+ GLbitfield blit_type;
+
+ /**
+ * Fbo that we perform MSAA rendering into.
+ */
+ Fbo multisample_fbo;
+
+ /**
+ * Single-sampled fbo that we blit into to force the
+ * implementation to resolve MSAA buffers.
+ */
+ Fbo resolve_fbo;
+
+ /**
+ * Large fbo that we perform high-resolution ("supersampled")
+ * rendering into.
+ */
+ Fbo supersample_fbo;
+
+ /**
+ * Normal-sized fbo that we manually downsample the
+ * supersampled render result into, to create the reference
+ * image.
+ */
+ Fbo downsample_fbo;
+
+ DownsampleProg downsample_prog;
+ int num_samples;
+};
+
+Test::Test(TestPattern *pattern, ManifestProgram *manifest_program,
+ bool test_resolve, GLbitfield blit_type)
+ : pattern(pattern),
+ manifest_program(manifest_program),
+ test_resolve(test_resolve),
+ blit_type(blit_type)
+{
+}
+
+void
+Test::init(int num_samples, bool small, bool combine_depth_stencil)
+{
+ this->num_samples = num_samples;
+
+ multisample_fbo.init(num_samples,
+ small ? 16 : pattern_width,
+ small ? 16 : pattern_height,
+ combine_depth_stencil);
+ resolve_fbo.init(0,
+ small ? 16 : pattern_width,
+ small ? 16 : pattern_height,
+ combine_depth_stencil);
+ supersample_fbo.init(0 /* num_samples */,
+ 1024, 1024, combine_depth_stencil);
+ downsample_fbo.init(0 /* num_samples */,
+ 1024 / supersample_factor,
+ 1024 / supersample_factor,
+ combine_depth_stencil);
+
+ pattern->compile();
+ downsample_prog.compile();
+ if (manifest_program)
+ manifest_program->compile();
+
+ /* Only do depth testing in those parts of the test where we
+ * explicitly want it
+ */
+ glDisable(GL_DEPTH_TEST);
+}
+
+/**
+ * Blit the data from multisample_fbo to resolve_fbo, forcing the
+ * implementation to do an MSAA resolve.
+ */
+void
+Test::resolve(GLbitfield which_buffers)
+{
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, multisample_fbo.handle);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo.handle);
+ resolve_fbo.set_viewport();
+ glBlitFramebuffer(0, 0, multisample_fbo.width, multisample_fbo.height,
+ 0, 0, resolve_fbo.width, resolve_fbo.height,
+ which_buffers, GL_NEAREST);
+}
+
+/**
+ * Use downsample_prog to blend 16x16 blocks of samples in
+ * supersample_fbo, to produce a reference image in downsample_fbo.
+ */
+void
+Test::downsample_color(int downsampled_width, int downsampled_height)
+{
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, downsample_fbo.handle);
+ downsample_fbo.set_viewport();
+ downsample_prog.run(&supersample_fbo,
+ downsample_fbo.width, downsample_fbo.height);
+}
+
+/**
+ * Blit the color data from src_fbo to the given location in the
+ * windowsystem buffer, so that the user can see it and we can read it
+ * using glReadPixels.
+ */
+void
+Test::show(Fbo *src_fbo, int x_offset, int y_offset)
+{
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, src_fbo->handle);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ glViewport(0, 0, piglit_width, piglit_height);
+ glBlitFramebuffer(0, 0, src_fbo->width, src_fbo->height,
+ x_offset, y_offset,
+ x_offset + src_fbo->width,
+ y_offset + src_fbo->height,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+}
+
+/**
+ * Draw a portion of the test pattern by setting up an appropriate
+ * projection matrix to map that portion of the test pattern to the
+ * full FBO.
+ */
+void
+Test::draw_pattern(int x_offset, int y_offset, int width, int height)
+{
+ /* Need a projection matrix such that:
+ * xc = ((xe + 1) * pattern_width/2 - x_offset) * 2/width - 1
+ * yc = ((ye + 1) * pattern_height/2 - y_offset) * 2/height - 1
+ * zc = ze
+ * wc = we = 1.0
+ *
+ * Therefore
+ * xc = pattern_width / width * xe
+ * + pattern_width / width - x_offset * 2 / width - 1
+ * yc = pattern_height / height * ye
+ * + pattern_height / height - y_offset * 2 / height - 1
+ * zc = ze
+ * wc = we = 1.0
+ */
+ float x_scale = float(pattern_width) / width;
+ float x_delta = x_scale - x_offset * 2.0 / width - 1.0;
+ float y_scale = float(pattern_height) / height;
+ float y_delta = y_scale - y_offset * 2.0 / height - 1.0;
+ float proj[4][4] = {
+ { x_scale, 0, 0, x_delta },
+ { 0, y_scale, 0, y_delta },
+ { 0, 0, 1, 0 },
+ { 0, 0, 0, 1 }
+ };
+
+ pattern->draw(proj);
+}
+
+/**
+ * Draw the entire test image, rendering it a piece at a time if
+ * multisample_fbo is very small.
+ */
+void
+Test::draw_test_image()
+{
+ int num_h_tiles = pattern_width / multisample_fbo.width;
+ int num_v_tiles = pattern_height / multisample_fbo.height;
+ for (int h = 0; h < num_h_tiles; ++h) {
+ for (int v = 0; v < num_v_tiles; ++v) {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER,
+ multisample_fbo.handle);
+ multisample_fbo.set_viewport();
+ int x_offset = h * multisample_fbo.width;
+ int y_offset = v * multisample_fbo.height;
+ draw_pattern(x_offset, y_offset,
+ multisample_fbo.width,
+ multisample_fbo.height);
+
+ if (test_resolve) {
+ resolve(blit_type);
+ if (manifest_program)
+ manifest_program->run();
+ } else {
+ if (manifest_program)
+ manifest_program->run();
+ resolve(GL_COLOR_BUFFER_BIT);
+ }
+
+ show(&resolve_fbo, x_offset, y_offset);
+ }
+ }
+}
+
+/**
+ * Draw the entire test image, rendering it a piece at a time.
+ */
+void
+Test::draw_reference_image()
+{
+ int downsampled_width = supersample_fbo.width / supersample_factor;
+ int downsampled_height = supersample_fbo.height / supersample_factor;
+ int num_h_tiles = pattern_width / downsampled_width;
+ int num_v_tiles = pattern_height / downsampled_height;
+ for (int h = 0; h < num_h_tiles; ++h) {
+ for (int v = 0; v < num_v_tiles; ++v) {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER,
+ supersample_fbo.handle);
+ supersample_fbo.set_viewport();
+ int x_offset = h * downsampled_width;
+ int y_offset = v * downsampled_height;
+ draw_pattern(x_offset, y_offset,
+ downsampled_width, downsampled_height);
+
+ if (manifest_program)
+ manifest_program->run();
+
+ downsample_color(downsampled_width, downsampled_height);
+ show(&downsample_fbo,
+ pattern_width + x_offset, y_offset);
+ }
+ }
+}
+
+/**
+ * Measure the accuracy of MSAA downsampling. Pixels that are fully
+ * on or off in the reference image are required to be fully on or off
+ * in the test image. Pixels that are not fully on or off in the
+ * reference image may be at any grayscale level; we mesaure the RMS
+ * error between the reference image and the test image.
+ */
+piglit_result
+Test::measure_accuracy()
+{
+ bool pass = true;
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ glViewport(0, 0, piglit_width, piglit_height);
+
+ float *reference_data = new float[pattern_width * pattern_height * 4];
+ glReadPixels(pattern_width, 0, pattern_width, pattern_height, GL_RGBA,
+ GL_FLOAT, reference_data);
+
+ float *test_data = new float[pattern_width * pattern_height * 4];
+ glReadPixels(0, 0, pattern_width, pattern_height, GL_RGBA,
+ GL_FLOAT, test_data);
+
+ Stats unlit_stats;
+ Stats partially_lit_stats;
+ Stats totally_lit_stats;
+ for (int y = 0; y < pattern_height; ++y) {
+ for (int x = 0; x < pattern_width; ++x) {
+ for (int c = 0; c < 4; ++c) {
+ int pixel_pos = 4*(y*pattern_width + x) + c;
+ float ref = reference_data[pixel_pos];
+ float test = test_data[pixel_pos];
+ if (ref <= 0.0)
+ unlit_stats.record(test - ref);
+ else if (ref >= 1.0)
+ totally_lit_stats.record(test - ref);
+ else
+ partially_lit_stats.record(test - ref);
+ }
+ }
+ }
+
+ printf("Pixels that should be unlit\n");
+ unlit_stats.summarize();
+ pass = unlit_stats.is_perfect() && pass;
+ printf("Pixels that should be totally lit\n");
+ totally_lit_stats.summarize();
+ pass = totally_lit_stats.is_perfect() && pass;
+ printf("Pixels that should be partially lit\n");
+ partially_lit_stats.summarize();
+
+ double error_threshold;
+ if (test_resolve) {
+ /* For depth and stencil resolves, the implementation
+ * typically just picks one of the N multisamples, so
+ * we have to allow for a generous amount of error.
+ */
+ error_threshold = 0.4;
+ } else {
+ /* Empirically, the RMS error for no oversampling is
+ * about 0.25, and each additional factor of 2
+ * overampling reduces the error by a factor of about
+ * 0.6. Leaving some room for variation, we'll set
+ * the error threshold to 0.333 * 0.6 ^
+ * log2(num_samples).
+ */
+ int effective_num_samples = num_samples == 0 ? 1 : num_samples;
+ error_threshold = 0.333 *
+ pow(0.6, log(effective_num_samples) / log(2.0));
+ }
+ printf("The error threshold for this test is %f\n", error_threshold);
+ pass = partially_lit_stats.is_better_than(error_threshold) && pass;
+ // TODO: deal with sRGB.
+ return pass ? PIGLIT_PASS : PIGLIT_FAIL;
+}
+
+piglit_result
+Test::run()
+{
+ draw_test_image();
+ draw_reference_image();
+ return measure_accuracy();
+}
+
+Test *test = NULL;
+
+void
+print_usage_and_exit(char *prog_name)
+{
+ printf("Usage: %s <num_samples> <test_type> [options]\n"
+ " where <test_type> is one of:\n"
+ " color: test downsampling of color buffer\n"
+ " stencil_draw: test drawing using MSAA stencil buffer\n"
+ " stencil_resolve: test resolve of MSAA stencil buffer\n"
+ " depth_draw: test drawing using MSAA depth buffer\n"
+ " depth_resolve: test resolve of MSAA depth buffer\n"
+ "Available options:\n"
+ " small: use a very small (16x16) MSAA buffer\n"
+ " depthstencil: use a combined depth/stencil buffer\n",
+ prog_name);
+ piglit_report_result(PIGLIT_FAIL);
+}
+
+extern "C" void
+piglit_init(int argc, char **argv)
+{
+ if (argc < 3)
+ print_usage_and_exit(argv[0]);
+ int num_samples;
+ {
+ char *endptr = NULL;
+ num_samples = strtol(argv[1], &endptr, 0);
+ if (endptr != argv[1] + strlen(argv[1]))
+ print_usage_and_exit(argv[0]);
+ }
+ if (strcmp(argv[2], "color") == 0) {
+ test = new Test(new Triangles(), NULL, false, 0);
+ } else if (strcmp(argv[2], "stencil_draw") == 0) {
+ test = new Test(new StencilSunburst(), new ManifestStencil(), false, 0);
+ } else if (strcmp(argv[2], "stencil_resolve") == 0) {
+ test = new Test(new StencilSunburst(), new ManifestStencil(), true, GL_STENCIL_BUFFER_BIT);
+ } else if (strcmp(argv[2], "depth_draw") == 0) {
+ test = new Test(new DepthSunburst(), new ManifestDepth(), false, 0);
+ } else if (strcmp(argv[2], "depth_resolve") == 0) {
+ test = new Test(new DepthSunburst(), new ManifestDepth(), true, GL_DEPTH_BUFFER_BIT);
+ } else {
+ print_usage_and_exit(argv[0]);
+ }
+ bool small = false;
+ bool combine_depth_stencil = false;
+ for (int i = 3; i < argc; ++i) {
+ if (strcmp(argv[i], "small") == 0) {
+ small = true;
+ } else if (strcmp(argv[i], "depthstencil") == 0) {
+ combine_depth_stencil = true;
+ } else {
+ print_usage_and_exit(argv[0]);
+ }
+ }
+
+ piglit_require_gl_version(30);
+ piglit_require_GLSL_version(130);
+
+ /* 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);
+
+ test->init(num_samples, small, combine_depth_stencil);
+}
+
+extern "C" piglit_result
+piglit_display()
+{
+ piglit_result result = test->run();
+
+ piglit_present_results();
+
+ return result;
+}
+
+};
--
1.7.7.6
More information about the Piglit
mailing list