[Piglit] [PATCH piglit] Time the performance difference with GLX_ARB_context_flush_control
Neil Roberts
neil at linux.intel.com
Fri Oct 10 04:38:32 PDT 2014
This adds a test with a heuristic to check whether disabling the
context flush on release is working. The hope is that a regression
would give a good indication that a flush has snuck back in.
The test creates a pair of contexts each with its own window and
renders to both of them with interleaved rendering by switching
contexts with glXMakeCurrent within each frame. This is done twice,
once with the flush control of both of the contexts set to flush and
once with it set to none. The number of frames that can be rendered in
one second is checked and then compared between the two versions. The
assumption is that the version which doesn't flush when the context is
released should be noticeably faster. The test fails if the FPS isn't
more than 50% faster for the non-flushing version.
---
I've only tested this on my Haswell laptop so I'm not sure whether the
test makes a lot of sense for other drivers. However I think it would
be useful to have at least to catch regressions where we accidentally
introduce a flush if we land the i965 patches for the extension. If
anyone is able to test it on other platforms I'd be interested to know
if it works.
tests/all.py | 2 +
tests/glx/CMakeLists.gl.txt | 1 +
tests/glx/glx-context-flush-control-performance.c | 305 ++++++++++++++++++++++
3 files changed, 308 insertions(+)
create mode 100644 tests/glx/glx-context-flush-control-performance.c
diff --git a/tests/all.py b/tests/all.py
index 14d65dc..7b3b275 100644
--- a/tests/all.py
+++ b/tests/all.py
@@ -543,6 +543,8 @@ add_plain_test(glx, 'glx-swap-singlebuffer')
add_plain_test(glx, 'glx-make-current')
add_plain_test(glx, 'glx-make-glxdrawable-current')
add_concurrent_test(glx, 'glx-context-flush-control')
+# This test probably shouldn't be made concurrent because it relies on timing
+add_plain_test(glx, 'glx-context-flush-control-performance')
add_plain_test(glx, 'glx-buffer-age')
glx['glx-buffer-age vblank_mode=0'] = plain_test('glx-buffer-age')
glx['glx-buffer-age vblank_mode=0'].env['vblank_mode'] = '0'
diff --git a/tests/glx/CMakeLists.gl.txt b/tests/glx/CMakeLists.gl.txt
index f66a0cc..65a4b2a 100644
--- a/tests/glx/CMakeLists.gl.txt
+++ b/tests/glx/CMakeLists.gl.txt
@@ -33,6 +33,7 @@ IF(PIGLIT_BUILD_GLX_TESTS)
piglit_add_executable (glx-multi-context-ib-1 glx-multi-context-ib-1.c)
piglit_add_executable (glx-multithread glx-multithread.c)
piglit_add_executable (glx-context-flush-control glx-context-flush-control.c)
+ piglit_add_executable (glx-context-flush-control-performance glx-context-flush-control-performance.c)
target_link_libraries(glx-multithread pthread)
piglit_add_executable (glx-multithread-texture glx-multithread-texture.c)
target_link_libraries(glx-multithread-texture pthread)
diff --git a/tests/glx/glx-context-flush-control-performance.c b/tests/glx/glx-context-flush-control-performance.c
new file mode 100644
index 0000000..6ee4fcc
--- /dev/null
+++ b/tests/glx/glx-context-flush-control-performance.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright © 2014 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:
+ * Neil Roberts <neil at linux.intel.com>
+ *
+ */
+
+/** @file glx-context-flush-control-performance.c
+ *
+ * Creates a pair of contexts each with its own window and renders to
+ * both of them with interleaved rendering by switching contexts with
+ * glXMakeCurrent within each frame. This is done twice, once with the
+ * flush control of both of the contexts set to flush and once with it
+ * set to none. The number of frames that can be rendered in one
+ * second is checked and then compared between the two versions. The
+ * assumption is that the version which doesn't flush when the context
+ * is released should be noticably faster. We can use this to be
+ * fairly confident that the non-flusing version is successfully
+ * avoiding the flush and track regressions. However it is only a
+ * heuristic so a failure that is not a regression is also likely to
+ * be a problem with the test itself.
+ */
+
+#include "piglit-util-gl.h"
+#include "piglit-glx-util.h"
+
+int piglit_width = 100;
+int piglit_height = 100;
+
+#ifndef GLX_ARB_context_flush_control
+#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
+#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0
+#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098
+#endif
+
+#ifndef GL_CONTEXT_RELEASE_BEHAVIOR
+#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB
+#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC
+#endif
+
+struct window {
+ GLXContext context;
+ Window window;
+ GLXWindow glx_window;
+ GLuint buffer;
+};
+
+struct vertex {
+ float x, y;
+};
+
+static PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribs = NULL;
+
+static void
+set_swap_interval(Display *display)
+{
+ PFNGLXSWAPINTERVALMESAPROC swap_interval_mesa;
+ PFNGLXSWAPINTERVALMESAPROC swap_interval_sgi;
+
+ if (piglit_is_glx_extension_supported(display,
+ "GLX_MESA_swap_control")) {
+ swap_interval_mesa =
+ (void *) glXGetProcAddress((const GLubyte *)
+ "glXSwapIntervalMESA");
+ if (swap_interval_mesa(0) == 0)
+ return;
+ }
+
+ /* Try with the SGI extension. Technically this shouldn't work
+ * because the spec disallows swap interval 0 */
+ if (piglit_is_glx_extension_supported(display,
+ "GLX_SGI_swap_control")) {
+ swap_interval_sgi =
+ (void *) glXGetProcAddress((const GLubyte *)
+ "glXSwapIntervalSGI");
+ if (swap_interval_sgi(0) == 0)
+ return;
+ }
+
+ fprintf(stderr,
+ "note: failed to set swap interval to 0 with either "
+ "GLX_MESA_swap_control or GLX_SGI_swap_control\n");
+
+ piglit_report_result(PIGLIT_SKIP);
+}
+
+static void
+make_grid(GLuint *buffer,
+ int width,
+ int height)
+{
+ struct vertex *vertex;
+ float sh;
+ float blx, bly;
+ int x, y;
+
+ /* Makes a grid of triangles where each line of quads is
+ * represented as a triangle strip. Each line is intended to
+ * be drawn separately */
+
+ glGenBuffers(1, buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, *buffer);
+ glBufferData(GL_ARRAY_BUFFER,
+ sizeof (struct vertex) * (width * 2 + 2) * height,
+ NULL,
+ GL_STATIC_DRAW);
+ vertex = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
+
+ sh = 2.0f / height;
+
+ for (y = 0; y < height; y++) {
+ for (x = 0; x <= width; x++) {
+ blx = x * 2.0f / width - 1.0f;
+ bly = y * 2.0f / height - 1.0f;
+
+ vertex[0].x = blx;
+ vertex[0].y = bly + sh;
+
+ vertex[1].x = blx;
+ vertex[1].y = bly;
+
+ vertex += 2;
+ }
+ }
+
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, /* size */
+ GL_FLOAT,
+ sizeof (struct vertex),
+ (void *) offsetof(struct vertex, x));
+}
+
+static void
+create_window(Display *display,
+ GLenum release_behavior,
+ struct window *window)
+{
+ GLXFBConfig config;
+ XVisualInfo *xvi;
+ GLint actual_release_behavior;
+ int ctx_attribs[] = {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, 1,
+ GLX_CONTEXT_MINOR_VERSION_ARB, 5,
+ GLX_CONTEXT_RELEASE_BEHAVIOR_ARB,
+ release_behavior,
+ 0
+ };
+
+ xvi = piglit_get_glx_visual(display);
+ config = piglit_glx_get_fbconfig_for_visinfo(display, xvi);
+
+ window->window = piglit_get_glx_window(display, xvi);
+ window->glx_window = glXCreateWindow(display, config,
+ window->window, NULL);
+
+ window->context =
+ CreateContextAttribs(display, config, 0, True, ctx_attribs);
+
+ assert(window->context);
+
+ glXMakeCurrent(display, window->glx_window, window->context);
+
+ piglit_dispatch_default_init(PIGLIT_DISPATCH_GL);
+
+ piglit_require_extension("GL_KHR_context_flush_control");
+
+ glGetIntegerv(GL_CONTEXT_RELEASE_BEHAVIOR, &actual_release_behavior);
+
+ switch (release_behavior) {
+ case GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB:
+ assert(actual_release_behavior ==
+ GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH);
+ break;
+ case GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB:
+ assert(actual_release_behavior == GL_NONE);
+ break;
+ default:
+ assert(0);
+ }
+
+ set_swap_interval(display);
+
+ make_grid(&window->buffer, piglit_width, piglit_height);
+}
+
+static void
+draw_frames(Display *display, struct window *windows, int n_windows)
+{
+ int y, win;
+
+ for (y = 0; y < piglit_height; y++) {
+ for (win = 0; win < n_windows; win++) {
+ glXMakeCurrent(display,
+ windows[win].glx_window,
+ windows[win].context);
+ glDrawArrays(GL_TRIANGLE_STRIP,
+ y * (piglit_width * 2 + 2),
+ piglit_width * 2 + 2);
+ }
+ }
+}
+
+static void
+destroy_window(Display *display,
+ struct window *window)
+{
+ glXMakeCurrent(display, None, NULL);
+ glXDestroyWindow(display, window->glx_window);
+ glXDestroyContext(display, window->context);
+ XDestroyWindow(display, window->window);
+}
+
+int
+count_frames(Display *display,
+ GLenum release_behavior)
+{
+ struct window window[2];
+ int64_t start_time = 0;
+ int frame_count = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(window); i++)
+ create_window(display, release_behavior, window + i);
+
+ while (true) {
+ draw_frames(display, window, ARRAY_SIZE(window));
+
+ for (i = 0; i < ARRAY_SIZE(window); i++) {
+ glXMakeCurrent(display,
+ window[i].glx_window,
+ window[i].context);
+ glXSwapBuffers(display, window[i].glx_window);
+ }
+
+ /* Ignore the first frame to avoid initialisation time
+ * in the driver */
+ if (start_time == 0) {
+ start_time = piglit_get_microseconds();
+ } else {
+ if (piglit_get_microseconds() - start_time > 1000000)
+ break;
+
+ frame_count++;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(window); i++)
+ destroy_window(display, window + i);
+
+ return frame_count;
+}
+
+int
+main(int argc, char **argv)
+{
+ Display *display;
+ int frames_flush, frames_none;
+
+ display = piglit_get_glx_display();
+
+ piglit_require_glx_extension(display, "GLX_ARB_get_proc_address");
+ piglit_require_glx_extension(display, "GLX_ARB_create_context");
+ piglit_require_glx_extension(display, "GLX_ARB_context_flush_control");
+
+ CreateContextAttribs = (PFNGLXCREATECONTEXTATTRIBSARBPROC)
+ glXGetProcAddressARB((GLubyte *) "glXCreateContextAttribsARB");
+
+ frames_none =
+ count_frames(display, GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB);
+ printf("FPS without flush on release: %i\n", frames_none);
+ frames_flush =
+ count_frames(display, GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB);
+ printf("FPS with flush on release: %i\n", frames_flush);
+
+ /* If the none version is not 50% faster than the flush
+ * version then we'll assume something is wrong */
+ if (frames_none > frames_flush * 1.5f)
+ piglit_report_result(PIGLIT_PASS);
+ else
+ piglit_report_result(PIGLIT_FAIL);
+
+ return 0;
+}
--
1.9.3
More information about the Piglit
mailing list