summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnuj Phogat <anuj.phogat@gmail.com>2012-05-04 15:02:31 -0700
committerPaul Berry <stereotype441@gmail.com>2012-05-08 07:10:33 -0700
commitd8c31aeaacadc2b1bb883a44cab91ca622cca53f (patch)
treee64aea23e270f07338167674c3d691c74fa2fd3a
parentcc116484b0bd1dabeac792d160ad7c0d41aff57d (diff)
Split accuracy test to allow new multisample tests utilize this code
This patch splits accuracy.cpp in to three files: common.h, common.cpp and accuracy.c. common.cpp defines the functions which can be utilized to develop new multisample test cases. Functions can be utilized to: - Draw a test image to default framebuffer. - Initialize a FBO with specified samples count. - Draw a test image to FBO. - Draw a reference image. - Verify the accuracy of multisample antialiasing in FBO. V2 (Paul Berry <stereotype441@gmail.com>): - Rather than making accuracy a .c file and providing C wrappers, move the class declarations from common.h so that accuracy.cpp can make use of them. - Instead of using the global variable render_to_default to alter the behavior of Test::draw_test_image(), add a new function Test::draw_to_default_framebuffer(). - Instead of using the global variable attach_render_buffer to cause Fbo::setup() to allocate a render buffer, add the parameter attach_texture to Fbo::init(), and only set it in the one place where a texture is needed (in the supersample_fbo). - Move other global variables (pattern_width, pattern_height, and supersample_factor) into the Test class. - Use an enum to select the type of test rather than a string parameter. - Remove unnecessary special-case behavior in Test::draw_test_image() when samples == 0.
-rw-r--r--tests/spec/ext_framebuffer_multisample/CMakeLists.gl.txt2
-rw-r--r--tests/spec/ext_framebuffer_multisample/accuracy.cpp1366
-rw-r--r--tests/spec/ext_framebuffer_multisample/common.cpp1132
-rw-r--r--tests/spec/ext_framebuffer_multisample/common.h361
4 files changed, 1528 insertions, 1333 deletions
diff --git a/tests/spec/ext_framebuffer_multisample/CMakeLists.gl.txt b/tests/spec/ext_framebuffer_multisample/CMakeLists.gl.txt
index c451f9f26..97be3d53b 100644
--- a/tests/spec/ext_framebuffer_multisample/CMakeLists.gl.txt
+++ b/tests/spec/ext_framebuffer_multisample/CMakeLists.gl.txt
@@ -11,7 +11,7 @@ link_libraries (
${GLUT_glut_LIBRARY}
)
-piglit_add_executable (ext_framebuffer_multisample-accuracy accuracy.cpp)
+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-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
index 345fd0b67..1b7ac1c46 100644
--- a/tests/spec/ext_framebuffer_multisample/accuracy.cpp
+++ b/tests/spec/ext_framebuffer_multisample/accuracy.cpp
@@ -20,72 +20,15 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
+#include "common.h"
/**
- * \file accuracy.cpp
+ * \file accuracy.c
*
* 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.
+ * This test utilizes the functions defined in common.cpp to verfify the
+ * accuracy of MSAA.
*
* The test also accepts the following flags:
*
@@ -100,1255 +43,11 @@
* driver for Linux) this is necessary for framebuffer completeness.
* On others (e.g. i965), this is an important corner case to test.
*/
+int piglit_width = 512; int piglit_height = 256;
+int piglit_window_mode = GLUT_DOUBLE | GLUT_RGBA | GLUT_ALPHA;
-#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 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((double)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
@@ -1368,34 +67,24 @@ print_usage_and_exit(char *prog_name)
piglit_report_result(PIGLIT_FAIL);
}
-extern "C" void
+void
piglit_init(int argc, char **argv)
{
+ GLint max_samples;
+ int i, num_samples;
+ bool small = false;
+ bool combine_depth_stencil = false;
+
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) {
+
+ for (i = 3; i < argc; ++i) {
if (strcmp(argv[i], "small") == 0) {
small = true;
} else if (strcmp(argv[i], "depthstencil") == 0) {
@@ -1409,22 +98,35 @@ piglit_init(int argc, char **argv)
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);
+ test_type_enum test_type;
+ if (strcmp(argv[2], "color") == 0) {
+ test_type = TEST_TYPE_COLOR;
+ } else if (strcmp(argv[2], "stencil_draw") == 0) {
+ test_type = TEST_TYPE_STENCIL_DRAW;
+ } else if (strcmp(argv[2], "stencil_resolve") == 0) {
+ test_type = TEST_TYPE_STENCIL_RESOLVE;
+ } else if (strcmp(argv[2], "depth_draw") == 0) {
+ test_type = TEST_TYPE_DEPTH_DRAW;
+ } else if (strcmp(argv[2], "depth_resolve") == 0) {
+ test_type = TEST_TYPE_DEPTH_RESOLVE;
+ } else {
+ print_usage_and_exit(argv[0]);
+ }
+ test = create_test(test_type, num_samples, small,
+ combine_depth_stencil,
+ pattern_width, pattern_height, supersample_factor);
}
-extern "C" piglit_result
+enum piglit_result
piglit_display()
{
- piglit_result result = test->run();
+ enum piglit_result result = test->run() ? PIGLIT_PASS : PIGLIT_FAIL;
piglit_present_results();
return result;
}
-
-};
diff --git a/tests/spec/ext_framebuffer_multisample/common.cpp b/tests/spec/ext_framebuffer_multisample/common.cpp
new file mode 100644
index 000000000..d1e6bcd9b
--- /dev/null
+++ b/tests/spec/ext_framebuffer_multisample/common.cpp
@@ -0,0 +1,1132 @@
+/*
+ * 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 common.cpp
+ *
+ * This file defines the functions which can be utilized to develop new
+ * multisample test cases. Functions can be utilized to:
+ *
+ * - Draw a test image to default framebuffer.
+ * - Initialize test_fbo with specified sample count.
+ * - Draw a test image to test_fbo.
+ * - Draw a reference image.
+ * - Verify the accuracy of multisample antialiasing in FBO.
+ *
+ * Accuracy verification is done 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), there are functions
+ * which 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.
+ *
+ * Functions 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 "common.h"
+
+/**
+ * \param attach_texture, if true, means to use a texture as color
+ * attachment instead of a renderbuffer.
+ */
+void
+Fbo::init(int num_samples, int width, int height, bool combine_depth_stencil,
+ bool attach_texture)
+{
+ generate();
+ this->width = width;
+ this->height = height;
+ this->combine_depth_stencil = combine_depth_stencil;
+ this->attach_texture = attach_texture;
+ set_samples(num_samples);
+}
+
+void
+Fbo::generate(void)
+{
+ glGenFramebuffers(1, &handle);
+}
+
+void
+Fbo::set_samples(int num_samples)
+{
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, handle);
+
+ this->color_tex = 0;
+
+ /* Color buffer */
+ if (!attach_texture) {
+ 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);
+}
+
+void
+DownsampleProg::compile(int supersample_factor)
+{
+ 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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+ }
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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;
+}
+
+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,
+ int pattern_width, int pattern_height, int supersample_factor)
+{
+ this->num_samples = num_samples;
+ this->pattern_width = pattern_width;
+ this->pattern_height = pattern_height;
+ this->supersample_factor = supersample_factor;
+
+ test_fbo.init(0,
+ small ? 16 : pattern_width,
+ small ? 16 : pattern_height,
+ combine_depth_stencil,
+ false);
+
+ multisample_fbo.init(num_samples,
+ small ? 16 : pattern_width,
+ small ? 16 : pattern_height,
+ combine_depth_stencil,
+ false);
+ resolve_fbo.init(0,
+ small ? 16 : pattern_width,
+ small ? 16 : pattern_height,
+ combine_depth_stencil,
+ false);
+ supersample_fbo.init(0 /* num_samples */,
+ 1024, 1024, combine_depth_stencil, true);
+ downsample_fbo.init(0 /* num_samples */,
+ 1024 / supersample_factor,
+ 1024 / supersample_factor,
+ combine_depth_stencil, false);
+
+ pattern->compile();
+ downsample_prog.compile(supersample_factor);
+ 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(Fbo *fbo, GLbitfield which_buffers)
+{
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo->handle);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo.handle);
+ resolve_fbo.set_viewport();
+ glBlitFramebuffer(0, 0, fbo->width, 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(Fbo *fbo)
+{
+ int num_h_tiles = pattern_width / fbo->width;
+ int num_v_tiles = pattern_height / fbo->height;
+ for (int h = 0; h < num_h_tiles; ++h) {
+ for (int v = 0; v < num_v_tiles; ++v) {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER,
+ fbo->handle);
+ fbo->set_viewport();
+ int x_offset = h * fbo->width;
+ int y_offset = v * fbo->height;
+ draw_pattern(x_offset, y_offset,
+ fbo->width,
+ fbo->height);
+ if (test_resolve) {
+ resolve(fbo, blit_type);
+ if (manifest_program)
+ manifest_program->run();
+ } else {
+ if (manifest_program)
+ manifest_program->run();
+ resolve(fbo,
+ GL_COLOR_BUFFER_BIT);
+ }
+
+ show(&resolve_fbo, x_offset, y_offset);
+ }
+ }
+}
+
+/**
+ * Draw the test image to the default framebuffer
+ */
+void
+Test::draw_to_default_framebuffer()
+{
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ glViewport(0, 0, pattern_width, pattern_height);
+ draw_pattern(0, 0, pattern_width, pattern_height);
+}
+
+/**
+ * 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.
+ */
+bool
+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((double)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;
+}
+
+bool
+Test::run()
+{
+ draw_test_image(&multisample_fbo);
+ draw_reference_image();
+ return measure_accuracy();
+}
+
+
+Test *
+create_test(test_type_enum test_type, int n_samples, bool small,
+ bool combine_depth_stencil, int pattern_width, int pattern_height,
+ int supersample_factor)
+{
+ Test *test = NULL;
+ switch (test_type) {
+ case TEST_TYPE_COLOR:
+ test = new Test(new Triangles(), NULL, false, 0);
+ break;
+ case TEST_TYPE_STENCIL_DRAW:
+ test = new Test(new StencilSunburst(),
+ new ManifestStencil(),
+ false, 0);
+ break;
+ case TEST_TYPE_STENCIL_RESOLVE:
+ test = new Test(new StencilSunburst(),
+ new ManifestStencil(),
+ true,
+ GL_STENCIL_BUFFER_BIT);
+ break;
+ case TEST_TYPE_DEPTH_DRAW:
+ test = new Test(new DepthSunburst(),
+ new ManifestDepth(),
+ false, 0);
+ break;
+ case TEST_TYPE_DEPTH_RESOLVE:
+ test = new Test(new DepthSunburst(),
+ new ManifestDepth(),
+ true,
+ GL_DEPTH_BUFFER_BIT);
+ break;
+ default:
+ printf("Unrecognized test type\n");
+ piglit_report_result(PIGLIT_FAIL);
+ break;
+ }
+
+ test->init(n_samples, small, combine_depth_stencil, pattern_width,
+ pattern_height, supersample_factor);
+ return test;
+}
diff --git a/tests/spec/ext_framebuffer_multisample/common.h b/tests/spec/ext_framebuffer_multisample/common.h
new file mode 100644
index 000000000..d86cb956b
--- /dev/null
+++ b/tests/spec/ext_framebuffer_multisample/common.h
@@ -0,0 +1,361 @@
+/* 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 common.h
+ * This file declares functions which can be utilized to develop new multisample
+ * test cases.
+ */
+
+#include "piglit-util.h"
+#include "math.h"
+
+enum test_type_enum {
+ TEST_TYPE_COLOR,
+ TEST_TYPE_STENCIL_DRAW,
+ TEST_TYPE_STENCIL_RESOLVE,
+ TEST_TYPE_DEPTH_DRAW,
+ TEST_TYPE_DEPTH_RESOLVE,
+};
+
+/**
+ * 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, bool attach_texture);
+ void generate();
+ void set_samples(int num_samples);
+
+ void set_viewport();
+
+ int width;
+ int height;
+ bool combine_depth_stencil;
+ bool attach_texture;
+ GLuint handle;
+
+ /**
+ * If attach_texture is true, the backing store for the color
+ * buffer.
+ */
+ GLuint color_tex;
+};
+
+/**
+ * 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(int supersample_factor);
+ void run(const Fbo *src_fbo, int dest_width, int dest_height);
+
+private:
+ GLint prog;
+ GLuint vertex_buf;
+ GLuint vao;
+};
+
+/**
+ * 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;
+};
+
+/**
+ * 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;
+};
+
+/**
+ * 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;
+};
+
+/**
+ * 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;
+};
+
+/**
+ * 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]);
+};
+
+/**
+ * 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]);
+};
+
+/**
+ * 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;
+};
+
+/**
+ * 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,
+ int pattern_width, int pattern_height,
+ int supersample_factor);
+ bool run();
+ void draw_test_image(Fbo *fbo);
+ void draw_to_default_framebuffer();
+ void draw_reference_image();
+ bool measure_accuracy();
+
+ /**
+ * Fbo that we use to just draw test image
+ */
+ Fbo test_fbo;
+
+private:
+ void resolve(Fbo *fbo, 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;
+
+ int num_samples;
+ int pattern_width;
+ int pattern_height;
+ int supersample_factor;
+ DownsampleProg downsample_prog;
+};
+
+Test *
+create_test(test_type_enum test_type, int n_samples, bool small,
+ bool combine_depth_stencil, int pattern_width,
+ int pattern_height, int supersample_factor);