[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