[PATCH weston 3/3] gl-renderer: Add EGL client support for EGLStream frame presentation

Miguel A. Vico mvicomoya at nvidia.com
Wed May 11 12:53:06 UTC 2016


By attaching a GLTexture consumer to a stream, a producer (wayland EGL
client) could feed frames to a texture, which in turn can be used by a
compositor to prepare the final frame to be presented.

This change adds required logic to support presentation approach
described above.

Note that some unpublished EGL extensions were needed:

 - EGL_NV_stream_attrib:
   https://github.com/aritger/eglstreams-kms-example/blob/master/proposed-extensions/EGL_NV_stream_attrib.txt

 - EGL_WL_wayland_eglstream:
   https://github.com/aritger/eglstreams-kms-example/blob/master/proposed-extensions/EGL_WL_wayland_eglstream.txt

Signed-off-by: Miguel A Vico Moya <mvicomoya at nvidia.com>
Reviewed-by: Adam Cheney <acheney at nvidia.com>
Reviewed-by: James Jones <jajones at nvidia.com>
---
 src/gl-renderer.c    | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/weston-egl-ext.h |   5 ++
 2 files changed, 186 insertions(+), 1 deletion(-)

diff --git a/src/gl-renderer.c b/src/gl-renderer.c
index 9380371..2e457b5 100644
--- a/src/gl-renderer.c
+++ b/src/gl-renderer.c
@@ -161,6 +161,9 @@ struct gl_surface_state {
 	int height; /* in pixels */
 	int y_inverted;
 
+	EGLStreamKHR egl_stream;
+	bool new_stream;
+
 	struct weston_surface *surface;
 
 	struct wl_listener surface_destroy_listener;
@@ -212,6 +215,7 @@ struct gl_renderer {
 
 	PFNEGLCREATESTREAMKHRPROC create_stream;
 	PFNEGLDESTROYSTREAMKHRPROC destroy_stream;
+	PFNEGLQUERYSTREAMKHRPROC query_stream;
 	int has_egl_stream;
 
 	PFNEGLCREATESTREAMPRODUCERSURFACEKHRPROC create_stream_producer_surface;
@@ -221,11 +225,16 @@ struct gl_renderer {
 	int has_egl_stream_consumer_egloutput;
 
 #ifdef EGL_NV_stream_attrib
+	PFNEGLCREATESTREAMATTRIBNVPROC create_stream_attrib;
 	PFNEGLSTREAMCONSUMERACQUIREATTRIBNVPROC stream_consumer_acquire_attrib;
 #endif
 	int has_egl_stream_attrib;
 	int has_egl_stream_acquire_mode;
 
+	PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALKHRPROC stream_consumer_gltexture;
+	int has_egl_stream_consumer_gltexture;
+	int has_egl_wayland_eglstream;
+
 	int has_dmabuf_import;
 	struct wl_list dmabuf_images;
 
@@ -1938,6 +1947,144 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface,
 	gs->y_inverted = buffer->y_inverted;
 }
 
+/*
+ * gl_renderer_attach_stream_texture
+ *
+ * Try to bind given <buffer> to an EGLStream. If the given buffer was already
+ * bound, it will acquire next frame on the stream.
+ *
+ * Return true if the given <buffer> corresponds to an EGLStream; otherwise,
+ * return false (if might be another kind of buffer).
+ */
+static bool
+gl_renderer_attach_stream_texture(struct weston_surface *es,
+                                  struct weston_buffer *buffer)
+{
+#ifdef EGL_NV_stream_attrib
+	struct weston_compositor *ec = es->compositor;
+	struct gl_renderer *gr = get_renderer(ec);
+	struct gl_surface_state *gs = get_surface_state(es);
+	EGLStreamKHR stream = EGL_NO_STREAM_KHR;
+	EGLAttrib stream_attribs[] = {
+#ifdef EGL_WL_wayland_eglstream
+		EGL_WAYLAND_EGLSTREAM_WL, (EGLAttrib)buffer->resource,
+#endif
+		EGL_NONE
+	};
+	EGLint stream_state = EGL_STREAM_STATE_EMPTY_KHR;
+
+	/* Check for required extensions. If they arent supported, there's no way
+	 * the given buffer corresponds to an EGLStream */
+	if (!gr->has_egl_stream_attrib ||
+	    !gr->has_egl_stream_consumer_gltexture ||
+	    !gr->has_egl_wayland_eglstream)
+		return false;
+
+	stream = gr->create_stream_attrib(gr->egl_display, stream_attribs);
+	if (stream == EGL_NO_STREAM_KHR) {
+		EGLint err = eglGetError();
+
+		switch (err) {
+		case EGL_BAD_ACCESS:
+			/* EGL_BAD_ACCESS is generated whenever buffer->resource does not
+			 * corresponds to a stream */
+			return false;
+
+		case EGL_BAD_STREAM_KHR:
+			/* EGL_BAD_STREAM_KHR is generated whenever buffer->resource
+			 * corresponds to a previously created stream so we must have a
+			 * valid stream handle already we can use to acquire next frame */
+			break;
+
+		default:
+			/* An unknown error was generated */
+			assert(0);
+			return false;
+		}
+	} else {
+		/* Clean up current stream resources if needed */
+		if (gs->egl_stream != EGL_NO_STREAM_KHR)
+			gr->destroy_stream(gr->egl_display, gs->egl_stream);
+
+		gs->egl_stream = stream;
+		gs->shader = &gr->texture_shader_egl_external;
+		gs->target = GL_TEXTURE_EXTERNAL_OES;
+
+		glActiveTexture(GL_TEXTURE0);
+		ensure_textures(gs, 2);
+		glBindTexture(gs->target, gs->textures[1]);
+
+		gs->new_stream = (EGL_TRUE == gr->stream_consumer_gltexture(
+		                                  gr->egl_display,
+		                                  gs->egl_stream));
+
+		if (!gs->new_stream) {
+			weston_log("failed to set stream consumer\n");
+			gl_renderer_print_egl_error_state();
+			gr->destroy_stream(gr->egl_display, gs->egl_stream);
+			gs->egl_stream = EGL_NO_STREAM_KHR;
+			return true; /* buffer->resource is EGLStream */
+		}
+	}
+
+	/* At this point we should have a valid stream handle */
+	assert(gs->egl_stream != EGL_NO_STREAM_KHR);
+
+	/* Check whether there are new frames available */
+	if (gr->query_stream(gr->egl_display,
+	                     gs->egl_stream,
+	                     EGL_STREAM_STATE_KHR,
+	                     &stream_state) != EGL_TRUE) {
+		weston_log("failed to query stream state\n");
+		gl_renderer_print_egl_error_state();
+		return true; /* buffer->resource is EGLStream */
+	}
+
+	/* If no new frame available, re-use last one */
+	if (stream_state != EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) {
+		/* Fake size of last frame */
+		buffer->width = gs->pitch;
+		buffer->height = gs->height;
+		return true; /* buffer->resource is EGLStream */
+	}
+
+	if (gr->stream_consumer_acquire_attrib(gr->egl_display,
+	                                       gs->egl_stream,
+	                                       NULL) != EGL_TRUE) {
+		weston_log("failed to acquire buffer\n");
+		gl_renderer_print_egl_error_state();
+		return true; /* buffer->resource is EGLStream */
+	}
+
+	/* Swap textures if new stream was created */
+	if (gs->new_stream) {
+		GLuint tmp = gs->textures[0];
+
+		gs->textures[0] = gs->textures[1];
+		gs->textures[1] = tmp;
+		gs->new_stream = false;
+	}
+
+	/* Update buffer and surface data */
+	buffer->legacy_buffer = (void *)buffer->resource;
+	gr->query_buffer(gr->egl_display, buffer->legacy_buffer,
+	                 EGL_WIDTH, &buffer->width);
+	gr->query_buffer(gr->egl_display, buffer->legacy_buffer,
+	                 EGL_HEIGHT, &buffer->height);
+	gr->query_buffer(gr->egl_display, buffer->legacy_buffer,
+	                 EGL_WAYLAND_Y_INVERTED_WL, &buffer->y_inverted);
+
+	gs->pitch = buffer->width;
+	gs->height = buffer->height;
+	gs->buffer_type = BUFFER_TYPE_EGL;
+	gs->y_inverted = buffer->y_inverted;
+
+	return true; /* buffer->resource is EGLStream */
+#else
+	return false;
+#endif
+}
+
 static void
 gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
 {
@@ -1961,6 +2108,12 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
 		gs->num_textures = 0;
 		gs->buffer_type = BUFFER_TYPE_NULL;
 		gs->y_inverted = 1;
+
+		if (gs->egl_stream != EGL_NO_STREAM_KHR) {
+			gr->destroy_stream(gr->egl_display, gs->egl_stream);
+			gs->egl_stream = EGL_NO_STREAM_KHR;
+		}
+
 		return;
 	}
 
@@ -1973,7 +2126,7 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
 		gl_renderer_attach_egl(es, buffer, format);
 	else if ((dmabuf = linux_dmabuf_buffer_get(buffer->resource)))
 		gl_renderer_attach_dmabuf(es, buffer, dmabuf);
-	else {
+	else if (!gl_renderer_attach_stream_texture(es, buffer)) {
 		weston_log("unhandled buffer type!\n");
 		weston_buffer_reference(&gs->buffer_ref, NULL);
 		gs->buffer_type = BUFFER_TYPE_NULL;
@@ -2161,6 +2314,10 @@ surface_state_destroy(struct gl_surface_state *gs, struct gl_renderer *gr)
 
 	weston_buffer_reference(&gs->buffer_ref, NULL);
 	pixman_region32_fini(&gs->texture_damage);
+
+	if (gs->egl_stream != EGL_NO_STREAM_KHR)
+		gr->destroy_stream(gr->egl_display, gs->egl_stream);
+
 	free(gs);
 }
 
@@ -2211,6 +2368,8 @@ gl_renderer_create_surface(struct weston_surface *surface)
 
 	gs->surface = surface;
 
+	gs->egl_stream = EGL_NO_STREAM_KHR;
+
 	pixman_region32_init(&gs->texture_damage);
 	surface->renderer_state = gs;
 
@@ -2936,14 +3095,19 @@ gl_renderer_setup_egl_extensions(struct weston_compositor *ec)
 		(void *) eglGetProcAddress("eglQueryOutputLayerAttribEXT");
 	gr->create_stream = (void *) eglGetProcAddress("eglCreateStreamKHR");
 	gr->destroy_stream = (void *) eglGetProcAddress("eglDestroyStreamKHR");
+	gr->query_stream = (void *) eglGetProcAddress("eglQueryStreamKHR");
 	gr->create_stream_producer_surface =
 		(void *) eglGetProcAddress("eglCreateStreamProducerSurfaceKHR");
 	gr->stream_consumer_output =
 		(void *) eglGetProcAddress("eglStreamConsumerOutputEXT");
 #ifdef EGL_NV_stream_attrib
+	gr->create_stream_attrib =
+		(void *) eglGetProcAddress("eglCreateStreamAttribNV");
 	gr->stream_consumer_acquire_attrib =
 		(void *) eglGetProcAddress("eglStreamConsumerAcquireAttribNV");
 #endif
+	gr->stream_consumer_gltexture =
+		(void *) eglGetProcAddress("eglStreamConsumerGLTextureExternalKHR");
 
 	extensions =
 		(const char *) eglQueryString(gr->egl_display, EGL_EXTENSIONS);
@@ -3009,6 +3173,12 @@ gl_renderer_setup_egl_extensions(struct weston_compositor *ec)
 	if (check_extension(extensions, "EGL_EXT_stream_acquire_mode"))
 		gr->has_egl_stream_acquire_mode = 1;
 
+	if (check_extension(extensions, "EGL_KHR_stream_consumer_gltexture"))
+		gr->has_egl_stream_consumer_gltexture = 1;
+
+	if (check_extension(extensions, "EGL_WL_wayland_eglstream"))
+		gr->has_egl_wayland_eglstream = 1;
+
 	renderer_setup_egl_client_extensions(gr);
 
 	return 0;
@@ -3226,6 +3396,16 @@ gl_renderer_display_create(struct weston_compositor *ec, EGLenum platform,
 			goto fail_terminate;
 		}
 
+		if (!gr->has_egl_stream_consumer_gltexture ||
+		    !gr->has_egl_wayland_eglstream)
+			weston_log("warning: following required extensions for EGL client "
+			    "frame presentation through EGLDevice not supported:\n"
+			    "%s%s",
+			    (gr->has_egl_stream_consumer_gltexture ?
+			         "    EGL_KHR_stream_consumer_gltexture\n" : ""),
+			    (gr->has_egl_wayland_eglstream ?
+			         "    EGL_WL_wayland_eglstream\n"          : ""));
+
 		if (!gr->has_egl_output_drm_flip_event)
 			weston_log("warning: EGL page flip event notification not"
 			           " supported\n");
diff --git a/src/weston-egl-ext.h b/src/weston-egl-ext.h
index ab01030..b43f49c 100644
--- a/src/weston-egl-ext.h
+++ b/src/weston-egl-ext.h
@@ -151,4 +151,9 @@ EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerAcquireAttribEXT (EGLDisplay dpy,
 #define EGL_DRM_FLIP_EVENT_DATA_NV            0x333E
 #endif /* EGL_NV_output_drm_flip_event */
 
+#ifndef EGL_WL_wayland_eglstream
+#define EGL_WL_wayland_eglstream 1
+#define EGL_WAYLAND_EGLSTREAM_WL              0x334B
+#endif /* EGL_WL_wayland_eglstream */
+
 #endif
-- 
2.8.0



More information about the wayland-devel mailing list