From ac135245b43c99dfbad084b52393da9d5ba76f2a Mon Sep 17 00:00:00 2001 From: "Miguel A. Vico" Date: Thu, 29 Mar 2018 00:15:49 -0700 Subject: [PATCH 3/6] gl-renderer: Add EGL client support for EGLStream frame presentation 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 [ekurzinger: - Try realizing EGLStream before EGLImage buffer since libnvidia-egl-wayland now reports EGL_TEXTURE_FORMAT] Signed-off-by: Miguel A Vico Moya Signed-off-by: Erik Kurzinger Reviewed-by: Adam Cheney Reviewed-by: James Jones --- libweston/renderer-gl/egl-glue.c | 11 ++ libweston/renderer-gl/gl-renderer-internal.h | 6 + libweston/renderer-gl/gl-renderer.c | 167 +++++++++++++++++++ shared/weston-egl-ext.h | 5 + 4 files changed, 189 insertions(+) diff --git a/libweston/renderer-gl/egl-glue.c b/libweston/renderer-gl/egl-glue.c index 24d647b8..90575d29 100644 --- a/libweston/renderer-gl/egl-glue.c +++ b/libweston/renderer-gl/egl-glue.c @@ -509,14 +509,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); @@ -630,6 +635,12 @@ gl_renderer_setup_egl_extensions(struct weston_compositor *ec) if (weston_check_egl_extension(extensions, "EGL_EXT_stream_acquire_mode")) gr->has_egl_stream_acquire_mode = true; + if (weston_check_egl_extension(extensions, "EGL_KHR_stream_consumer_gltexture")) + gr->has_egl_stream_consumer_gltexture = true; + + if (weston_check_egl_extension(extensions, "EGL_WL_wayland_eglstream")) + gr->has_egl_wayland_eglstream = true; + renderer_setup_egl_client_extensions(gr); return 0; diff --git a/libweston/renderer-gl/gl-renderer-internal.h b/libweston/renderer-gl/gl-renderer-internal.h index b7c374a2..0827500e 100644 --- a/libweston/renderer-gl/gl-renderer-internal.h +++ b/libweston/renderer-gl/gl-renderer-internal.h @@ -95,6 +95,7 @@ struct gl_renderer { PFNEGLCREATESTREAMKHRPROC create_stream; PFNEGLDESTROYSTREAMKHRPROC destroy_stream; + PFNEGLQUERYSTREAMKHRPROC query_stream; bool has_egl_stream; PFNEGLCREATESTREAMPRODUCERSURFACEKHRPROC create_stream_producer_surface; @@ -104,11 +105,16 @@ struct gl_renderer { bool has_egl_stream_consumer_egloutput; #ifdef EGL_NV_stream_attrib + PFNEGLCREATESTREAMATTRIBNVPROC create_stream_attrib; PFNEGLSTREAMCONSUMERACQUIREATTRIBNVPROC stream_consumer_acquire_attrib; #endif bool has_egl_stream_attrib; bool has_egl_stream_acquire_mode; + PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALKHRPROC stream_consumer_gltexture; + bool has_egl_stream_consumer_gltexture; + bool has_egl_wayland_eglstream; + bool has_gl_texture_rg; struct gl_shader texture_shader_rgba; diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index fe4f1087..1b6a219b 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -187,6 +187,9 @@ struct gl_surface_state { int hsub[3]; /* horizontal subsampling per plane */ int vsub[3]; /* vertical subsampling per plane */ + EGLStreamKHR egl_stream; + bool new_stream; + struct weston_surface *surface; /* Whether this surface was used in the current output repaint. @@ -2544,6 +2547,145 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, gs->shader = image->shader; } +/* + * gl_renderer_attach_stream_texture + * + * Try to bind given to an EGLStream. If the given buffer was already + * bound, it will acquire next frame on the stream. + * + * Return true if the given 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 = (gr->stream_consumer_gltexture( + gr->egl_display, + gs->egl_stream) == EGL_TRUE); + + 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) { @@ -2571,6 +2713,12 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) gs->y_inverted = true; gs->direct_display = false; es->is_opaque = false; + + 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; } @@ -2578,6 +2726,9 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) if (shm_buffer) gl_renderer_attach_shm(es, buffer, shm_buffer); + else if (gl_renderer_attach_stream_texture(es, buffer)) + /* The stream texture is attached. Nothing else to be done here */ + ((void)0); else if (gr->has_bind_display && gr->query_buffer(gr->egl_display, (void *)buffer->resource, EGL_TEXTURE_FORMAT, &format)) @@ -2781,6 +2932,10 @@ surface_state_destroy(struct gl_surface_state *gs, struct gl_renderer *gr) weston_buffer_reference(&gs->buffer_ref, NULL); weston_buffer_release_reference(&gs->buffer_release_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); } @@ -2832,6 +2987,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; @@ -3730,6 +3887,16 @@ gl_renderer_display_create(struct weston_compositor *ec, 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/shared/weston-egl-ext.h b/shared/weston-egl-ext.h index dc07c04a..d348a5c0 100644 --- a/shared/weston-egl-ext.h +++ b/shared/weston-egl-ext.h @@ -247,6 +247,11 @@ 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 */ + #else /* ENABLE_EGL */ /* EGL platform definition are keept to allow compositor-xx.c to build */ -- 2.20.1