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 <mvicom...@nvidia.com>
Reviewed-by: Adam Cheney <ache...@nvidia.com>
Reviewed-by: James Jones <jajo...@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

_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to