[3/7] egl-context-preemption: Add a high priority thread/context.

Submitted by Rafael Antognolli on Oct. 4, 2018, 3:35 p.m.

Details

Message ID 20181004153535.17863-4-rafael.antognolli@intel.com
State New
Headers show
Series "New preemption test." ( rev: 1 ) in Piglit

Not browsing as part of any series.

Commit Message

Rafael Antognolli Oct. 4, 2018, 3:35 p.m.
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(+)

Patch hide | download patch | download mbox

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;

Comments

On 10/4/18 6:35 PM, Rafael Antognolli wrote:
> 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;

This seems unnecessary, we are already skipping the test if extension 
was not supported in piglit_init?

> +
> +	}
> +
> +	*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;
>
On Tue, Oct 09, 2018 at 01:23:03PM +0300, Tapani Pälli wrote:
> 
> 
> On 10/4/18 6:35 PM, Rafael Antognolli wrote:
> > 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;
> 
> This seems unnecessary, we are already skipping the test if extension was
> not supported in piglit_init?

Agreed, I'll remove it. I think it was copy & pasted from another test,
sorry for that.

> > +
> > +	}
> > +
> > +	*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;
> >