Hello, everyone,

Attached is a new test for piglit which exposes a bug in Mesa's
software rendering (and another bug in hardware rendering, but that's
not its main purpose).

The bug is as follows.  With software rendering, after doing a buffer
swap, glBlitFrameBufferEXT() appears to to copy the whole framebuffer
instead of just the specified region.  This breaks clutter and cogl,
since they keep track of a dirty region themselves, and they use blits
instead of full buffer swaps to avoid updating the whole display on
every frame unnecessarily.

What is happening is actually a bit more complicated.
glBlitFrameBufferEXT()'s basic machinery works correctly, but if there
has been a buffer swap before it, the following happens:

1. Draw some stuff (say, to GL_BACK)

2. Swap buffers.  As far as I can tell, this just causes an
XPutImage() from the GL_BACK buffer to the X window.

3. Draw some stuff to GL_BACK.

4. Do glBlitFrameBufferEXT() from GL_BACK to GL_FRONT with the area you
are interested in.

5. Internally, Mesa sees that the buffer for GL_FRONT has not been
created yet, so it creates it and does the blit.

6. Do glFlush() so that GL_FRONT actually gets sent to the screen.  This
causes an XPutImage() of the *whole* of GL_FRONT, thus giving
incorrect results - the area that should have been updated is the one
from (4), i.e. just the blit.

If you run the test program with hardware acceleration, it will work
correctly.  But if you run it with LIBGL_ALWAYS_SOFTWARE=1, it will
fail.

For a related bug, do the following:  in the test program change the
line that says

  #define SWAP_BUFFERS_BEFORE_BLIT 1

from 1 to 0.  Run the program again; this time it will work correctly
with software rendering, but at least on my box it fails with hardware
rendering (Intel).

I don't know enough about Mesa's internals to fix this quickly.  Any
help is appreciated.

Thanks,

  Federico

>From 5c565b6cb053b3917be826276b8e0d2254699a8f Mon Sep 17 00:00:00 2001
From: Federico Mena Quintero <feder...@suse.com>
Date: Thu, 17 Oct 2013 14:52:31 -0500
Subject: [PATCH] fbo-blit-after-swap: New test for partial blits after a
 buffer swap

The clutter/cogl libraries try to minimize the area that gets updated on every frame.
They do this by doing glBlitFramebufferEXT() from the back buffer to the front buffer.

However, this is buggy with software rendering if there has been a buffer swap
*before* the first blit from the back buffer to the front buffer.  In this case,
Mesa copies the whole back buffer into the front buffer, instead of just the
requested region.
---
 tests/all.tests                 |   1 +
 tests/fbo/CMakeLists.gl.txt     |   1 +
 tests/fbo/fbo-blit-after-swap.c | 136 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 138 insertions(+)
 create mode 100644 tests/fbo/fbo-blit-after-swap.c

diff --git a/tests/all.tests b/tests/all.tests
index 7ab841e..6c92ebf 100644
--- a/tests/all.tests
+++ b/tests/all.tests
@@ -1163,6 +1163,7 @@ for format in ('rgba', 'depth', 'stencil'):
         test_name = ' '.join(['framebuffer-blit-levels', test_mode, format])
         arb_framebuffer_object[test_name] = PlainExecTest(test_name + ' -auto')
 add_plain_test(arb_framebuffer_object, 'fbo-alpha')
+add_plain_test(arb_framebuffer_object, 'fbo-blit-after-swap')
 add_plain_test(arb_framebuffer_object, 'fbo-blit-stretch')
 add_plain_test(arb_framebuffer_object, 'fbo-blit-scaled-linear')
 add_plain_test(arb_framebuffer_object, 'fbo-attachments-blit-scaled-linear')
diff --git a/tests/fbo/CMakeLists.gl.txt b/tests/fbo/CMakeLists.gl.txt
index 588fe26..3ad9ec0 100644
--- a/tests/fbo/CMakeLists.gl.txt
+++ b/tests/fbo/CMakeLists.gl.txt
@@ -31,6 +31,7 @@ piglit_add_executable (fbo-alpha fbo-alpha.c)
 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-after-swap fbo-blit-after-swap.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)
diff --git a/tests/fbo/fbo-blit-after-swap.c b/tests/fbo/fbo-blit-after-swap.c
new file mode 100644
index 0000000..38fc870
--- /dev/null
+++ b/tests/fbo/fbo-blit-after-swap.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2013 Suse, Inc. 
+ * Copyright © 2011 Henri Verbeet <hverb...@gmail.com>
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * 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 fbo-blit-after-swap.c
+ *
+ * Test a glBlitFrameBuffer() with a smaller-than-the-window region after doing a buffer swap
+ */
+
+#include "piglit-util-gl-common.h"
+
+PIGLIT_GL_TEST_CONFIG_BEGIN
+
+	config.supports_gl_compat_version = 10;
+
+	config.window_width = 128;
+	config.window_height = 128;
+	config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_RGB;
+	config.requires_displayed_window = true;
+
+PIGLIT_GL_TEST_CONFIG_END
+
+#define SWAP_BUFFERS_BEFORE_BLIT 1
+
+static const float red[]   = {1.0f, 0.0f, 0.0f, 1.0f};
+static const float blue[]  = {0.0f, 0.0f, 1.0f, 1.0f};
+static const float black[] = {0.0f, 0.0f, 0.0f, 1.0f};
+
+static void
+setup_front_buffer(void)
+{
+	glDrawBuffer(GL_BACK);
+
+	glClearColor(blue[0], blue[1], blue[2], blue[3]);
+	glClear(GL_COLOR_BUFFER_BIT);
+
+#if SWAP_BUFFERS_BEFORE_BLIT
+	piglit_swap_buffers();
+#else
+	glDrawBuffer(GL_FRONT);
+	glReadBuffer(GL_BACK);
+	glBlitFramebufferEXT(0, 0, piglit_width, piglit_height,
+			     0, 0, piglit_width, piglit_height,
+			     GL_COLOR_BUFFER_BIT, GL_NEAREST);
+#endif
+}
+
+static void
+setup_back_buffer(void)
+{
+	int w = piglit_width;
+	int h = piglit_height;
+
+	glDrawBuffer(GL_BACK);
+
+	/* Clear to black */
+	glClearColor(black[0], black[1], black[2], black[3]);
+	glClear(GL_COLOR_BUFFER_BIT);
+
+	/* Paint a red square in the middle of the back buffer */
+	glColor4f(red[0], red[1], red[2], red[3]);
+	piglit_draw_rect(w / 4, h / 4, w / 2, h / 2);
+}
+
+static void
+blit_from_back_to_front(void)
+{
+	int w = piglit_width;
+	int h = piglit_height;
+
+	/* Copy just the red square from the back buffer to the blue front buffer */
+	glDrawBuffer(GL_FRONT);
+	glReadBuffer(GL_BACK);
+	glBlitFramebufferEXT(w / 4, h / 4, 3 * w / 4, 3 * h / 4,
+			     w / 4, h / 4, 3 * w / 4, 3 * h / 4,
+			     GL_COLOR_BUFFER_BIT, GL_NEAREST);
+}
+
+enum piglit_result piglit_display(void)
+{
+	int w = piglit_width;
+	int h = piglit_height;
+	bool success = 1;
+
+	piglit_ortho_projection(w, h, GL_FALSE);
+
+	setup_front_buffer();
+	setup_back_buffer();
+
+	blit_from_back_to_front();
+
+	glFlush();
+
+	/* Now see how we did... */
+
+	glReadBuffer(GL_FRONT);
+
+	/* the middle should be red */
+	success &= piglit_probe_pixel_rgb(w / 2, h / 2, red);
+
+	/* the corners should be blue */
+	success &= piglit_probe_pixel_rgb(0, 0, blue);
+	success &= piglit_probe_pixel_rgb(w - 1, 0, blue);
+	success &= piglit_probe_pixel_rgb(0, h - 1, blue);
+	success &= piglit_probe_pixel_rgb(w - 1, h - 1, blue);
+
+	return success ? PIGLIT_PASS : PIGLIT_FAIL;
+}
+
+void piglit_init(int argc, char **argv)
+{
+	piglit_require_extension("GL_EXT_framebuffer_object");
+	piglit_require_extension("GL_EXT_framebuffer_blit");
+}
-- 
1.8.1.4

_______________________________________________
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-dev

Reply via email to