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. v2: - skip on high priority failure (Ken) - remove useless check (Tapani) --- tests/egl/egl-context-preemption.c | 242 ++++++++++++++++++++++++++++- 1 file changed, 239 insertions(+), 3 deletions(-) diff --git a/tests/egl/egl-context-preemption.c b/tests/egl/egl-context-preemption.c index 5265bccc4..8f0245e49 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 { @@ -71,10 +76,10 @@ check_priority(EGLDisplay dpy, EGLContext ctx, EGLint *expected) } if (expected && value != *expected) { - piglit_loge("%s fail: priority value 0x%x, expected 0x%x\n", + piglit_logd("%s fail: priority value 0x%x, expected 0x%x\n", __func__, value, *expected); - piglit_loge("Can't create high priority context.\n"); - piglit_report_result(PIGLIT_FAIL); + piglit_logd("Can't create high priority context.\n"); + piglit_report_result(PIGLIT_SKIP); } return value; } @@ -197,6 +202,217 @@ 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; + } + + *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 +448,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 +487,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 +512,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.1 _______________________________________________ Piglit mailing list Piglit@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/piglit