[Piglit] [PATCH 3/7] egl-context-preemption: Add a high priority thread/context.
Rafael Antognolli
rafael.antognolli at intel.com
Thu Oct 4 15:35:31 UTC 2018
Start a new thread right after dispatching the render commands from the
main thread, after glFlush().
This second thread creates a high priority EGL context, and uses it to
render to a frame buffer multiple times. These draw calls are supposed
to finish before the one in the main thread.
---
tests/egl/egl-context-preemption.c | 243 +++++++++++++++++++++++++++++
1 file changed, 243 insertions(+)
diff --git a/tests/egl/egl-context-preemption.c b/tests/egl/egl-context-preemption.c
index 9d5aa42ee..566416312 100644
--- a/tests/egl/egl-context-preemption.c
+++ b/tests/egl/egl-context-preemption.c
@@ -41,11 +41,16 @@
*/
#define HIGH_PRIO_RUNS 50
+static const int hp_width = 80, hp_height = 80;
struct test_data {
bool main_finished;
int64_t main_tstarted, main_tfinished;
+ int64_t tstarted[HIGH_PRIO_RUNS];
+ int64_t tfinished[HIGH_PRIO_RUNS];
+ int nruns;
EGLDisplay dpy;
+ EGLContext ctx;
};
struct test_profile {
@@ -197,6 +202,224 @@ draw_objects(unsigned int shader_program, GLenum mode, GLfloat *vertices,
glDisableVertexAttribArray(0);
}
+static enum piglit_result
+init_display(EGLenum platform, EGLDisplay *out_dpy)
+{
+ enum piglit_result result = PIGLIT_PASS;
+ EGLDisplay dpy;
+ EGLint egl_major, egl_minor;
+ bool ok;
+
+ dpy = piglit_egl_get_default_display(platform);
+ if (!dpy) {
+ result = PIGLIT_SKIP;
+ goto error;
+ }
+
+ ok = eglInitialize(dpy, &egl_major, &egl_minor);
+ if (!ok) {
+ result = PIGLIT_SKIP;
+ goto error;
+ }
+
+ if (!piglit_is_egl_extension_supported(dpy, "EGL_IMG_context_priority")) {
+ piglit_loge("display does not support EGL_IMG_context_priority");
+ result = PIGLIT_SKIP;
+ goto error;
+
+ }
+
+ *out_dpy = dpy;
+ return result;
+
+error:
+ if (dpy) {
+ eglTerminate(dpy);
+ }
+ return result;
+}
+
+static enum piglit_result
+init_other_display(EGLDisplay *out_other_dpy)
+{
+ enum piglit_result result = PIGLIT_SKIP;
+ EGLDisplay other_dpy = 0;
+ int i;
+
+ static const EGLint platforms[] = {
+ EGL_PLATFORM_GBM_MESA,
+ EGL_PLATFORM_SURFACELESS_MESA,
+ EGL_PLATFORM_X11_EXT,
+ EGL_PLATFORM_WAYLAND_EXT,
+ 0,
+ };
+
+ for (i = 0; platforms[i] != 0; ++i) {
+ result = init_display(platforms[i], &other_dpy);
+ switch (result) {
+ case PIGLIT_SKIP:
+ break;
+ case PIGLIT_PASS:
+ *out_other_dpy = other_dpy;
+ return PIGLIT_PASS;
+ default:
+ break;
+ }
+ }
+
+ return result;
+}
+
+static enum piglit_result
+setup_thread_context(struct test_data *d)
+{
+ enum piglit_result result = PIGLIT_PASS;
+ bool ok = false;
+ EGLContext ctx2 = EGL_NO_CONTEXT;
+
+ EGLDisplay dpy;
+ if (init_other_display(&dpy) == PIGLIT_SKIP) {
+ piglit_loge("failed to get display\n");
+ result = PIGLIT_FAIL;
+ return result;
+ }
+
+ eglBindAPI(EGL_OPENGL_API);
+ if (!piglit_check_egl_error(EGL_SUCCESS)) {
+ piglit_loge("failed to set OpenGL API.\n");
+ result = PIGLIT_FAIL;
+ return result;
+ }
+
+ EGLint attr[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 3,
+ EGL_CONTEXT_MINOR_VERSION_KHR, 3,
+ EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG,
+ EGL_NONE };
+ ctx2 = eglCreateContext(dpy, EGL_NO_CONFIG_MESA, EGL_NO_CONTEXT, attr);
+ if (ctx2 == EGL_NO_CONTEXT) {
+ piglit_loge("failed to create context");
+ piglit_check_egl_error(EGL_SUCCESS);
+ result = PIGLIT_FAIL;
+ goto cleanup;
+ }
+
+ ok = eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx2);
+ if (!ok) {
+ piglit_loge("failed to make context current without surface");
+ result = PIGLIT_FAIL;
+ goto cleanup;
+ }
+
+ GLuint VertexArrayID;
+ glGenVertexArrays(1, &VertexArrayID);
+ glBindVertexArray(VertexArrayID);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glViewport(0, 0, hp_width, hp_height);
+
+ d->ctx = ctx2;
+ d->dpy = dpy;
+
+ return result;
+
+cleanup:
+ if (ctx2 != EGL_NO_CONTEXT)
+ eglDestroyContext(dpy, ctx2);
+ eglTerminate(dpy);
+
+ return result;
+}
+
+/* Allocate and attach textures and FBOs */
+static void
+setup_render_target(GLuint *fbos, GLuint *textures, unsigned n,
+ int width, int height)
+{
+ glGenFramebuffers(n, fbos);
+ glGenTextures(n, textures);
+
+ for (int i = 0; i < n; i++) {
+ glBindFramebuffer(GL_FRAMEBUFFER, fbos[i]);
+
+ glBindTexture(GL_TEXTURE_2D, textures[i]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, hp_width, hp_height, 0,
+ GL_RGB, GL_UNSIGNED_BYTE, NULL);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // attach texture to the currently bound framebuffer
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, textures[i], 0);
+
+ if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+ piglit_loge("Framebuffer is not complete!\n");
+
+ if (!piglit_check_gl_error(GL_NO_ERROR)) {
+ piglit_report_result(PIGLIT_FAIL);
+ }
+ }
+}
+
+static void
+draw_high_priority(struct test_data *d, unsigned int shader_program, int iter)
+{
+ GLint nbits;
+ glGetQueryiv(GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, &nbits);
+ /* Ready to draw */
+ draw_objects(shader_program, GL_TRIANGLES, triangle_vertices,
+ sizeof(triangle_vertices), 1);
+ glFlush();
+
+ GLuint query;
+ glGenQueries(1, &query);
+ glGetInteger64v(GL_TIMESTAMP, &d->tstarted[iter]);
+ glQueryCounter(query, GL_TIMESTAMP);
+
+ glFinish();
+ glGetQueryObjecti64v(query, GL_QUERY_RESULT, &d->tfinished[iter]);
+ d->nruns++;
+}
+
+static void*
+thread2_create_high_priority_context(void *data)
+{
+ enum piglit_result *result = malloc(sizeof(*result));
+ struct test_data *d = data;
+
+ *result = setup_thread_context(d);
+ if (*result != PIGLIT_PASS)
+ return result;
+
+ GLuint fbos[HIGH_PRIO_RUNS];
+ GLuint textures[HIGH_PRIO_RUNS];
+ setup_render_target(fbos, textures, HIGH_PRIO_RUNS,
+ hp_width, hp_height);
+
+ unsigned int program = setup_shaders();
+
+ for (int i = 0; i < HIGH_PRIO_RUNS; i++) {
+ /* It's fine to have a little race condition here, because we
+ * will discard results that finished after the main thread
+ * based on GL_TIMESTAMP anyway.
+ */
+ if (d->main_finished)
+ break;
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[i]);
+ draw_high_priority(d, program, i);
+ }
+
+ if (d->ctx != EGL_NO_CONTEXT)
+ eglDestroyContext(d->dpy, d->ctx);
+ eglTerminate(d->dpy);
+
+ return result;
+}
+
static GLfloat *
read_pixels_float(GLint x, GLint y, GLsizei width, GLsizei height,
GLenum format, GLfloat *pixels)
@@ -232,6 +455,7 @@ test_preemption(void *data)
const struct test_profile *profile = data;
struct test_data d = {
.main_finished = false,
+ .nruns = 0,
};
d.dpy = eglGetCurrentDisplay();
@@ -270,6 +494,18 @@ test_preemption(void *data)
glGetInteger64v(GL_TIMESTAMP, &d.main_tstarted);
glQueryCounter(query, GL_TIMESTAMP);
+ /* Start second thread with high priority */
+ pthread_t thread2;
+ int err = pthread_create(
+ &thread2, NULL,
+ thread2_create_high_priority_context,
+ &d);
+ if (err) {
+ piglit_loge("failed to create second thread");
+ result = PIGLIT_FAIL;
+ goto cleanup;
+ }
+
glFinish();
glGetQueryObjecti64v(query, GL_QUERY_RESULT, &d.main_tfinished);
d.main_finished = true;
@@ -283,6 +519,13 @@ test_preemption(void *data)
glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ err = pthread_join(thread2, NULL);
+ if (err) {
+ piglit_loge("failed to join thread %"PRIuMAX, (uintmax_t) thread2);
+ result = PIGLIT_FAIL;
+ goto cleanup;
+ }
+
cleanup:
free(ref_image);
return result;
--
2.19.0
More information about the Piglit
mailing list