summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosé Fonseca <jose.r.fonseca@gmail.com>2012-07-07 13:08:49 +0100
committerJosé Fonseca <jfonseca@vmware.com>2012-07-20 17:41:47 +0100
commiteea36e1dc092610df8ec9b071c44a2d566bac225 (patch)
tree5a38fa82084360b04d60073c83f4945e4650e16c
parent66bd9c3baad957c9830b5b9e22758d305eff7599 (diff)
fbo-blit-stretch: test for stretched blits
v2: More clip tests and comments.
-rw-r--r--tests/all.tests1
-rw-r--r--tests/fbo/CMakeLists.gl.txt1
-rw-r--r--tests/fbo/fbo-blit-stretch.cpp556
3 files changed, 558 insertions, 0 deletions
diff --git a/tests/all.tests b/tests/all.tests
index 74ddae5eb..ccf3eeeed 100644
--- a/tests/all.tests
+++ b/tests/all.tests
@@ -195,6 +195,7 @@ add_plain_test(fbo, 'fbo-blending-formats')
add_plain_test(fbo, 'fbo-bind-renderbuffer')
add_plain_test(fbo, 'fbo-blit')
add_plain_test(fbo, 'fbo-blit-d24s8')
+add_plain_test(fbo, 'fbo-blit-stretch')
add_plain_test(fbo, 'fbo-cubemap')
add_plain_test(fbo, 'fbo-clearmipmap')
add_plain_test(fbo, 'fbo-clear-formats')
diff --git a/tests/fbo/CMakeLists.gl.txt b/tests/fbo/CMakeLists.gl.txt
index de25b3883..c4d0168a5 100644
--- a/tests/fbo/CMakeLists.gl.txt
+++ b/tests/fbo/CMakeLists.gl.txt
@@ -32,6 +32,7 @@ piglit_add_executable (fbo-luminance-alpha fbo-luminance-alpha.c)
piglit_add_executable (fbo-bind-renderbuffer fbo-bind-renderbuffer.c)
piglit_add_executable (fbo-blit fbo-blit.c)
piglit_add_executable (fbo-blit-d24s8 fbo-blit-d24s8.c)
+piglit_add_executable (fbo-blit-stretch fbo-blit-stretch.cpp)
piglit_add_executable (fbo-blending-formats fbo-blending-formats.c)
piglit_add_executable (fbo-copypix fbo-copypix.c)
piglit_add_executable (fbo-readdrawpix fbo-readdrawpix.c)
diff --git a/tests/fbo/fbo-blit-stretch.cpp b/tests/fbo/fbo-blit-stretch.cpp
new file mode 100644
index 000000000..f9909546f
--- /dev/null
+++ b/tests/fbo/fbo-blit-stretch.cpp
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2012 VMware, Inc.
+ * Copyright (C) 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ * Jose Fonseca
+ * Eric Anholt <eric@anholt.net>
+ * Brian Paul
+ */
+
+/** @file fbo-blit-stretch.c
+ *
+ * Tests EXT_framebuffer_blit with various combinations of window system and
+ * FBO objects. Because FBOs are generally stored inverted relative to
+ * window system frambuffers, this could catch flipping failures in blit paths.
+ *
+ * See also fbo-blit.c
+ */
+
+#include <algorithm>
+
+#include "piglit-util-gl-common.h"
+
+
+/*
+ * XXX: Checkerboard is not a good test pattern, because OpenGL spec allows the
+ * implementation to clamp against source rectangle edge, as oposed to clamp
+ * against the source edges, causing different results along the edge.
+ */
+#define CHECKERBOARD 0
+
+#define DSTW 200
+#define DSTH 150
+
+PIGLIT_GL_TEST_MAIN(
+ DSTW /*window_width*/,
+ DSTH /*window_height*/,
+ GLUT_RGBA | GLUT_DOUBLE)
+
+struct TestCase
+{
+ GLint srcW, srcH;
+ GLint srcX0; GLint srcY0; GLint srcX1; GLint srcY1;
+ GLint dstX0; GLint dstY0; GLint dstX1; GLint dstY1;
+ GLenum filter;
+};
+
+static void
+describe(const TestCase &test)
+{
+ GLint dstW = piglit_width;
+ GLint dstH = piglit_height;
+
+ printf("%ix%i (%i, %i)-(%i, %i) => %ix%i (%i, %i)-(%i, %i)",
+ test.srcW, test.srcH,
+ test.srcX0, test.srcY0, test.srcX1, test.srcY1,
+ dstW, dstH,
+ test.dstX0, test.dstY0, test.dstX1, test.dstY1);
+
+ GLint srcDX = test.srcX1 - test.srcX0;
+ GLint srcDY = test.srcY1 - test.srcY0;
+ GLint dstDX = test.dstX1 - test.dstX0;
+ GLint dstDY = test.dstY1 - test.dstY0;
+
+ if (srcDX < 0) {
+ printf(" flip_src_x");
+ srcDX = -srcDX;
+ }
+ if (srcDY < 0) {
+ printf(" flip_src_y");
+ srcDY = -srcDY;
+ }
+ if (dstDX < 0) {
+ printf(" flip_dst_x");
+ dstDX = -dstDX;
+ }
+ if (dstDY < 0) {
+ printf(" flip_dst_y");
+ dstDY = -dstDY;
+ }
+
+ if (dstDX > srcDX)
+ printf(" stretch_x");
+ if (dstDX < srcDX)
+ printf(" shrink_x");
+
+ if (dstDY > srcDY)
+ printf(" stretch_y");
+ if (dstDY < srcDY)
+ printf(" shrink_y");
+
+ if (test.srcX0 < 0 || test.srcX0 > test.srcW ||
+ test.srcX1 < 0 || test.srcX1 > test.srcW)
+ printf(" clamp_src_x");
+ if (test.srcY0 < 0 || test.srcY0 > test.srcH ||
+ test.srcY1 < 0 || test.srcY1 > test.srcH)
+ printf(" clamp_src_y");
+
+ if (test.dstX0 < 0 || test.dstX0 > dstW ||
+ test.dstX1 < 0 || test.dstX1 > dstW)
+ printf(" clamp_dst_x");
+ if (test.dstY0 < 0 || test.dstY0 > dstH ||
+ test.dstY1 < 0 || test.dstY1 > dstH)
+ printf(" clamp_dst_y");
+
+ switch (test.filter) {
+ case GL_NEAREST:
+ printf(" nearest");
+ break;
+ case GL_LINEAR:
+ printf(" linear");
+ break;
+ default:
+ assert(0);
+ }
+
+ printf("\n");
+}
+
+static void
+filter(const TestCase &test, float coord, GLint &coord0, GLint &coord1, float &weight)
+{
+ switch (test.filter) {
+ case GL_NEAREST:
+ coord0 = roundf(coord);
+ // ambigious
+ assert(fabsf(coord0 - coord) != 0.5f);
+ weight = 0.0f;
+ break;
+ case GL_LINEAR:
+ coord0 = floorf(coord);
+ weight = coord - (float)coord0;
+ break;
+ default:
+ assert(0);
+ coord0 = 0;
+ weight = 0.0f;
+ }
+
+ assert(weight >= 0.0f);
+ assert(weight < 1.0f);
+
+ coord1 = coord0 + 1;
+}
+
+static void
+clamp(GLint &x, GLint xmin, GLint xmax)
+{
+ if (x < xmin) {
+ x = xmin;
+ }
+ if (x > xmax) {
+ x = xmax;
+ }
+}
+
+static float
+lerp(float x0, float x1, float w)
+{
+ return x0 + (x1 - x0) * w;
+}
+
+static float
+lerp2d(float xy00, float xy01, float xy10, float xy11, float wx, float wy)
+{
+ float y0 = lerp(xy00, xy01, wx);
+ float y1 = lerp(xy10, xy11, wx);
+ return lerp(y0, y1, wy);
+}
+
+static float clearColor[4] = {
+#if CHECKERBOARD
+ 0.0, 0.0, 1.0, 1.0
+#else
+ 0.5, 0.5, 0.5, 0.5
+#endif
+};
+
+static GLboolean
+verify(const TestCase &test, GLuint srcFBO, GLuint dstFBO, GLuint numChannels)
+{
+ GLint srcX0 = test.srcX0;
+ GLint srcY0 = test.srcY0;
+ GLint srcX1 = test.srcX1;
+ GLint srcY1 = test.srcY1;
+ GLint dstX0 = test.dstX0;
+ GLint dstY0 = test.dstY0;
+ GLint dstX1 = test.dstX1;
+ GLint dstY1 = test.dstY1;
+
+ if (dstX1 < dstX0) {
+ std::swap(srcX0, srcX1);
+ std::swap(dstX0, dstX1);
+ }
+ if (dstY1 < dstY0) {
+ std::swap(srcY0, srcY1);
+ std::swap(dstY0, dstY1);
+ }
+
+ GLint srcDX = srcX1 - srcX0;
+ GLint srcDY = srcY1 - srcY0;
+ GLint dstDX = dstX1 - dstX0;
+ GLint dstDY = dstY1 - dstY0;
+
+ GLint dstW = piglit_width;
+ GLint dstH = piglit_height;
+
+ float *srcPixels = new float[test.srcH * test.srcW * numChannels];
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFBO);
+ glReadPixels(0, 0, test.srcW, test.srcH, GL_RGB, GL_FLOAT, srcPixels);
+
+ float *expectedDstPixels = new float[dstH * dstW * numChannels];
+
+ for (GLint dstY = 0; dstY < dstH; ++dstY) {
+ for (GLint dstX = 0; dstX < dstW; ++dstX) {
+ float *dstPixel = expectedDstPixels + (dstY * dstW + dstX) * numChannels;
+ for (GLuint c = 0; c < numChannels; ++c) {
+ dstPixel[c] = clearColor[c];
+ }
+ }
+ }
+
+ GLint dstX0clamped = std::max(dstX0, 0);
+ GLint dstY0clamped = std::max(dstY0, 0);
+ GLint dstX1clamped = std::min(dstX1, dstW);
+ GLint dstY1clamped = std::min(dstY1, dstH);
+
+ for (GLint dstY = dstY0clamped; dstY < dstY1clamped; ++dstY) {
+ float srcY = srcY0 + (dstY - dstY0 + 0.5) * srcDY / dstDY;
+ if (srcY < 0 || srcY >= test.srcH) {
+ continue;
+ }
+
+ srcY -= 0.5f;
+
+ GLint srcPixelY0, srcPixelY1;
+ float weightY;
+ filter(test, srcY, srcPixelY0, srcPixelY1, weightY);
+ clamp(srcPixelY0, 0, test.srcH - 1);
+ clamp(srcPixelY1, 0, test.srcH - 1);
+
+ for (GLint dstX = dstX0clamped; dstX < dstX1clamped; ++dstX) {
+ float srcX = srcX0 + (dstX - dstX0 + 0.5) * srcDX / dstDX;
+ if (srcX < 0 || srcX >= test.srcW) {
+ continue;
+ }
+
+ srcX -= 0.5f;
+
+ GLint srcPixelX0, srcPixelX1;
+ float weightX;
+ filter(test, srcX, srcPixelX0, srcPixelX1, weightX);
+ clamp(srcPixelX0, 0, test.srcW - 1);
+ clamp(srcPixelX1, 0, test.srcW - 1);
+
+ float *srcPixel00 = srcPixels + (srcPixelY0 * test.srcW + srcPixelX0) * numChannels;
+ float *srcPixel01 = srcPixels + (srcPixelY0 * test.srcW + srcPixelX1) * numChannels;
+ float *srcPixel10 = srcPixels + (srcPixelY1 * test.srcW + srcPixelX0) * numChannels;
+ float *srcPixel11 = srcPixels + (srcPixelY1 * test.srcW + srcPixelX1) * numChannels;
+
+ float *dstPixel = expectedDstPixels
+ + (dstY * dstW + dstX) * numChannels;
+
+ for (GLuint c = 0; c < numChannels; ++c) {
+ dstPixel[c] = lerp2d(srcPixel00[c],
+ srcPixel01[c],
+ srcPixel10[c],
+ srcPixel11[c],
+ weightX, weightY);
+ }
+ }
+ }
+
+ delete [] srcPixels;
+
+ float *observedDstPixels = new float[dstH * dstW * numChannels];
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, dstFBO);
+ glReadPixels(0, 0, dstW, dstH, GL_RGB, GL_FLOAT, observedDstPixels);
+
+ GLboolean pass;
+ pass = piglit_compare_images_color(0, 0, dstW, dstH, numChannels,
+ piglit_tolerance,
+ expectedDstPixels,
+ observedDstPixels);
+
+ delete [] observedDstPixels;
+ delete [] expectedDstPixels;
+
+ return pass;
+}
+
+static void
+blit(const TestCase &test)
+{
+ glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glBlitFramebuffer(test.srcX0, test.srcY0, test.srcX1, test.srcY1,
+ test.dstX0, test.dstY0, test.dstX1, test.dstY1,
+ GL_COLOR_BUFFER_BIT, test.filter);
+}
+
+
+static GLboolean
+run_test(const TestCase &test)
+{
+ describe(test);
+
+ GLboolean pass;
+
+ GLuint tex;
+ GLuint fbo;
+ GLenum status;
+
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+#if CHECKERBOARD
+ const float color1[4] = {1.0f, 0.0f, 0.0f, 1.0f};
+ const float color2[4] = {0.0f, 1.0f, 0.0f, 1.0f};
+ tex = piglit_checkerboard_texture(0, 0, test.srcW, test.srcH, 1, 1, color1, color2);
+#else
+ tex = piglit_rgbw_texture(GL_RGBA, test.srcW, test.srcH, GL_FALSE, GL_TRUE, GL_UNSIGNED_NORMALIZED);
+#endif
+ glBindTexture(GL_TEXTURE_2D, tex);
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ tex,
+ 0);
+ assert(glGetError() == 0);
+
+ status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ pass = GL_TRUE;
+ } else {
+ glViewport(0, 0, piglit_width, piglit_height);
+ piglit_ortho_projection(piglit_width, piglit_height, GL_FALSE);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+
+ blit(test);
+
+ pass = verify(test, fbo, 0, 3);
+
+ if (!piglit_automatic) {
+ piglit_present_results();
+ if (!pass) {
+ //getchar();
+ }
+ }
+ }
+
+ glDeleteFramebuffers(1, &fbo);
+ glDeleteTextures(1, &tex);
+
+ return pass;
+}
+
+/*
+ * Constants to help define several test cases.
+ */
+
+#define SRCW 45
+#define SRCH 79
+#define DX 17
+#define DY 11
+#define SRCXMIN 13
+#define SRCYMIN 33
+#define SRCXMAX (SRCXMIN + DX)
+#define SRCYMAX (SRCYMIN + DY)
+
+#define DSTXMIN 19
+#define DSTYMIN 23
+#define DSTXMAX (DSTXMIN + DX)
+#define DSTYMAX (DSTYMIN + DY)
+
+const TestCase
+tests[] = {
+ /*
+ * Basic 1:1 copy
+ */
+
+ {
+ SRCW, SRCH,
+ SRCXMIN, SRCYMIN, SRCXMAX, SRCYMAX,
+ DSTXMIN, DSTYMIN, DSTXMAX, DSTYMAX,
+ GL_NEAREST,
+ },
+
+ /*
+ * Flip tests
+ */
+
+ {
+ SRCW, SRCH,
+ SRCXMAX, SRCYMAX, SRCXMIN, SRCYMIN, // flip xy
+ DSTXMAX, DSTYMAX, DSTXMIN, DSTYMIN, // flip xy
+ GL_NEAREST,
+ },
+ {
+ SRCW, SRCH,
+ SRCXMAX, SRCYMIN, SRCXMIN, SRCYMAX, // fliped x
+ DSTXMIN, DSTYMAX, DSTXMAX, DSTYMIN, // fliped y
+ GL_NEAREST,
+ },
+ {
+ SRCW, SRCH,
+ SRCXMIN, SRCYMAX, SRCXMAX, SRCYMIN, // fliped y
+ DSTXMAX, DSTYMIN, DSTXMIN, DSTYMAX, // fliped x
+ GL_NEAREST,
+ },
+
+ /*
+ * Stretch.
+ */
+
+ {
+ SRCW, SRCH,
+ SRCXMIN, SRCYMIN, SRCXMAX, SRCYMAX,
+ DSTXMIN, DSTYMIN, DSTXMAX + 3*DX, DSTYMAX + 3*DY, // strech x y
+ GL_NEAREST,
+ },
+ {
+ SRCW, SRCH,
+ SRCXMIN, SRCYMIN, SRCXMAX, SRCYMAX,
+ DSTXMIN, DSTYMIN, DSTXMAX + 3*DX, DSTYMAX + 3*DY, // strech x y
+ GL_NEAREST,
+ },
+
+ /*
+ * Clip
+ */
+
+ {
+ SRCW, SRCH,
+ SRCXMIN, SRCYMIN, SRCXMAX, SRCYMAX,
+ -DX/2, -DY/2, -DX/2 + DX, -DY/2 + DY, // clip dst left bottom
+ GL_NEAREST,
+ },
+ {
+ SRCW, SRCH,
+ SRCXMIN, SRCYMIN, SRCXMAX, SRCYMAX,
+ DSTW - DX/2, DSTH - DY/2, DSTW - DX/2 + DX, DSTH - DY/2 + DY, // clip dst top right
+ GL_NEAREST,
+ },
+ {
+ SRCW, SRCH,
+ -DX/2, -DY/2, -DX/2 + DX, -DY/2 + DY, // clip src left bottom
+ DSTXMIN, DSTYMIN, DSTXMAX, DSTYMAX,
+ GL_NEAREST,
+ },
+ {
+ SRCW, SRCH,
+ SRCW - DX/2, SRCH - DY/2, SRCW - DX/2 + DX, SRCH - DY/2 + DY, // clip src top right
+ DSTXMIN, DSTYMIN, DSTXMAX, DSTYMAX,
+ GL_NEAREST,
+ },
+
+ /*
+ * Clip & stretch.
+ *
+ * XXX: These tests are disabled for now, because Mesa clips in integer
+ * coordinates, instead of floats, which ends up affecting how the
+ * whole surface is interpolated, which goes against the spec.
+ */
+#if 0
+ {
+ SRCW, SRCH,
+ SRCXMIN, SRCYMIN, SRCXMAX, SRCYMAX,
+ -DSTXMIN, -DSTYMIN, DSTXMAX, DSTYMAX, // clip dst left bottom
+ GL_NEAREST,
+ },
+ {
+ SRCW, SRCH,
+ SRCXMIN, SRCYMIN, SRCXMAX, SRCYMAX,
+ DSTXMIN, DSTYMIN, DSTW + DSTXMIN, DSTH + DSTYMIN, // clip dst top right
+ GL_NEAREST,
+ },
+ {
+ SRCW, SRCH,
+ -SRCXMIN, -SRCYMIN, SRCXMAX, SRCYMAX, // clip src left bottom
+ DSTXMIN, DSTYMIN, DSTXMAX, DSTYMAX,
+ GL_NEAREST,
+ },
+ {
+ SRCW, SRCH,
+ SRCXMIN, SRCYMIN, SRCW + SRCXMIN, SRCH + SRCYMIN, // clip src top right
+ DSTXMIN, DSTYMIN, DSTXMAX, DSTYMAX,
+ GL_NEAREST,
+ },
+ {
+ SRCW, SRCH,
+ SRCXMAX, SRCYMIN, SRCXMIN, SRCYMAX, // fliped x
+ -DSTXMIN, DSTH + DSTYMIN, DSTW + DSTXMIN, -DSTYMIN, // fliped y, cliped x y
+ GL_NEAREST,
+ },
+#endif
+
+ /*
+ * Full stretch
+ */
+
+ {
+ SRCW, SRCH,
+ 0, 0, SRCW, SRCH,
+ 0, 0, DSTW, DSTH,
+ GL_NEAREST,
+ },
+};
+
+enum piglit_result
+piglit_display(void)
+{
+ GLboolean pass = GL_TRUE;
+ for (unsigned i = 0; i < ARRAY_SIZE(tests); i++) {
+ TestCase test = tests[i];
+
+ test.filter = GL_NEAREST;
+ pass = run_test(test) && pass;
+
+ test.filter = GL_LINEAR;
+ pass = run_test(test) && pass;
+ }
+ return pass ? PIGLIT_PASS : PIGLIT_FAIL;
+}
+
+
+void
+piglit_init(int argc, char **argv)
+{
+ piglit_ortho_projection(piglit_width, piglit_height, GL_FALSE);
+
+ piglit_require_extension("GL_ARB_framebuffer_object");
+}