From 7b4d70dc7f5024e80656d9facdb0bc834d91adb9 Mon Sep 17 00:00:00 2001 From: "Miguel A. Vico" Date: Thu, 18 May 2017 14:35:36 -0700 Subject: [PATCH 6/6] compositor: Process stream attach requests with wl_eglstream_controller X-NVConfidentiality: public So far, the EGLStream implementation of the Wayland client-side driver has been using wl_surface_attach + commit in order to make the server create its stream endpoint and attach a consumer to it. However, no actual buffer would be actually shared between client and server, which goes against many of the assumptions behind wl_surface_attach + commit. This has caused different interaction issues in the past. In order to properly resolve this once and for all, a new wl_eglstream_controller protocol has been added which will let clients request the compositor to create its stream. This change adds the required code for weston to create a wl_eglstream_controller global and process attach_eglstream_consumer requests. [mvicomoya: - Dynamically load libnvidia-egl-wayland.so.1 instead linking against it - Add wayland-eglstream-protocols package dependency and generate server header for wayland-eglstream-controller at build time] Signed-off-by: Ashutosh Agarwal Signed-off-by: Miguel A Vico Moya --- compositor/meson.build | 1 + include/libweston/libweston.h | 4 + libweston/compositor.c | 78 +++++++++++++++ libweston/renderer-gl/gl-renderer.c | 147 ++++++++++++++++++---------- protocol/meson.build | 7 ++ 5 files changed, 183 insertions(+), 54 deletions(-) diff --git a/compositor/meson.build b/compositor/meson.build index 61860a7a..f11d55e8 100644 --- a/compositor/meson.build +++ b/compositor/meson.build @@ -9,6 +9,7 @@ srcs_weston = [ input_method_unstable_v1_protocol_c, weston_screenshooter_server_protocol_h, weston_screenshooter_protocol_c, + wayland_eglstream_controller_server_protocol_h, ] deps_weston = [ dep_libshared, diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 5be8ef05..178bb071 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -917,6 +917,10 @@ struct weston_renderer { void (*query_dmabuf_modifiers)(struct weston_compositor *ec, int format, uint64_t **modifiers, int *num_modifiers); + + /** Create weston endpoint of an EGLStream & attach a consumer to it */ + bool (*attach_eglstream_consumer)(struct weston_surface *es, + struct wl_resource *wl_eglstream); }; enum weston_capability { diff --git a/libweston/compositor.c b/libweston/compositor.c index 85327159..0a6a56e2 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -3,6 +3,7 @@ * Copyright © 2008-2011 Kristian Høgsberg * Copyright © 2012-2018 Collabora, Ltd. * Copyright © 2017, 2018 General Electric Company + * Copyright © 2017-2018 NVIDIA Corporation * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -74,6 +75,7 @@ #include "pixel-formats.h" #include "backend.h" #include "libweston-internal.h" +#include "wayland-eglstream-controller-server-protocol.h" #include "weston-log-internal.h" @@ -6850,6 +6852,80 @@ bind_presentation(struct wl_client *client, wp_presentation_send_clock_id(resource, compositor->presentation_clock); } +static struct wl_interface *eglstream_controller_interface = NULL; + +static void +attach_eglstream_consumer(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *wl_surface, + struct wl_resource *wl_eglstream) +{ + struct weston_surface *surface = + wl_resource_get_user_data(wl_surface); + + surface->compositor->renderer->attach_eglstream_consumer(surface, + wl_eglstream); +} + +static const struct wl_eglstream_controller_interface +eglstream_controller_implementation = { + attach_eglstream_consumer +}; + +static void +bind_eglstream_controller(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create(client, eglstream_controller_interface, + version, id); + + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, + &eglstream_controller_implementation, + data, + NULL); +} + +static void +init_eglstream_controller(struct wl_display *display) +{ + /* + * wl_eglstream_controller_interface is provided by + * libnvidia-egl-wayland.so.1 + * + * Since it might not be available on the + * system, dynamically load it at runtime and resolve the needed + * symbols. If available, it should be found under any of the search + * directories of dlopen() + * + * Failure to initialize wl_eglstream_controller is non-fatal + */ + + void *lib = dlopen("libnvidia-egl-wayland.so.1", RTLD_NOW | RTLD_LAZY); + if (!lib) + goto fail; + + eglstream_controller_interface = + dlsym(lib, "wl_eglstream_controller_interface"); + + if (!eglstream_controller_interface) + goto fail; + + if (wl_global_create(display, + eglstream_controller_interface, 1, + NULL, bind_eglstream_controller)) + return; /* success */ +fail: + if (lib) + dlclose(lib); + weston_log("warning: Unable to initialize wl_eglstream_controller.\n"); +} + static void compositor_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) @@ -7230,6 +7306,8 @@ weston_compositor_create(struct wl_display *display, ec, bind_presentation)) goto fail; + init_eglstream_controller(ec->wl_display); + if (weston_log_ctx_compositor_setup(ec, log_ctx) < 0) goto fail; diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 22a64f81..b4a88da3 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1,7 +1,7 @@ /* * Copyright © 2012 Intel Corporation * Copyright © 2015 Collabora, Ltd. - * Copyright © 2016 NVIDIA Corporation + * Copyright © 2016-2017 NVIDIA Corporation * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -2626,18 +2626,9 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, surface->is_opaque = dmabuf_is_opaque(dmabuf); } -/* - * 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) +static EGLint +gl_renderer_attach_eglstream_consumer_common(struct weston_surface *es, + struct wl_resource *wl_eglstream) { #ifdef EGL_NV_stream_attrib struct weston_compositor *ec = es->compositor; @@ -2646,65 +2637,111 @@ gl_renderer_attach_stream_texture(struct weston_surface *es, EGLStreamKHR stream = EGL_NO_STREAM_KHR; EGLAttrib stream_attribs[] = { #ifdef EGL_WL_wayland_eglstream - EGL_WAYLAND_EGLSTREAM_WL, (EGLAttrib)buffer->resource, + EGL_WAYLAND_EGLSTREAM_WL, (EGLAttrib)wl_eglstream, #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 */ + * way the given resource corresponds to an EGLStream */ if (!gr->has_egl_stream_attrib || !gr->has_egl_stream_consumer_gltexture || !gr->has_egl_wayland_eglstream) - return false; + return EGL_BAD_ACCESS; stream = gr->create_stream_attrib(gr->egl_display, stream_attribs); - if (stream == EGL_NO_STREAM_KHR) { + + if (stream == EGL_NO_STREAM_KHR) + return eglGetError(); + + 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) { 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; + weston_log("failed to set stream consumer\n"); + weston_log("EGL error state: %s (0x%04lx)\n", + egl_error_string(err), (long)err); - 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; + gr->destroy_stream(gr->egl_display, gs->egl_stream); + gs->egl_stream = EGL_NO_STREAM_KHR; + return err; + } - 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); + return EGL_SUCCESS; +#else + return EGL_BAD_ACCESS; +#endif +} - gs->egl_stream = stream; - gs->shader = &gr->texture_shader_egl_external; - gs->target = GL_TEXTURE_EXTERNAL_OES; +static bool +gl_renderer_attach_eglstream_consumer(struct weston_surface *es, + struct wl_resource *stream) +{ + EGLint err = gl_renderer_attach_eglstream_consumer_common(es, stream); + return (err == EGL_SUCCESS); +} + +/* + * 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); + EGLint stream_state = EGL_STREAM_STATE_EMPTY_KHR; - glActiveTexture(GL_TEXTURE0); - ensure_textures(gs, 2); - glBindTexture(gs->target, gs->textures[1]); + EGLint err; - gs->new_stream = (gr->stream_consumer_gltexture( - gr->egl_display, - gs->egl_stream) == EGL_TRUE); + err = gl_renderer_attach_eglstream_consumer_common(es, buffer->resource); - 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 */ - } + 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; + + case EGL_SUCCESS: + /* EGL_SUCCESS is returned when the server stream endpoint is + * created and a consumer is attached successfully. This path is + * hit with old drivers which use wl_surface::attach + commit to + * request stream creation. */ + return true; /* buffer->resource is EGLStream */ + + default: + /* An unknown error was generated */ + assert(0); + return false; } /* At this point we should have a valid stream handle */ @@ -4138,6 +4175,8 @@ gl_renderer_display_create(struct weston_compositor *ec, EGLenum platform, gr->base.surface_get_content_size = gl_renderer_surface_get_content_size; gr->base.surface_copy_content = gl_renderer_surface_copy_content; + gr->base.attach_eglstream_consumer = + gl_renderer_attach_eglstream_consumer; gr->egl_display = NULL; /* extension_suffix is supported */ diff --git a/protocol/meson.build b/protocol/meson.build index 1228d15b..e47d0a6c 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -4,6 +4,9 @@ prog_scanner = find_program(dep_scanner.get_pkgconfig_variable('wayland_scanner' dep_wp = dependency('wayland-protocols', version: '>= 1.18') dir_wp_base = dep_wp.get_pkgconfig_variable('pkgdatadir') +dep_wep = dependency('wayland-eglstream-protocols') +dir_wep_base = dep_wep.get_pkgconfig_variable('pkgdatadir') + install_data( [ 'weston-debug.xml', @@ -35,6 +38,7 @@ generated_protocols = [ [ 'xdg-output', 'v1' ], [ 'xdg-shell', 'v6' ], [ 'xdg-shell', 'stable' ], + [ 'wayland-eglstream-controller', '3rdparty', dir_wep_base ], ] foreach proto: generated_protocols @@ -42,6 +46,9 @@ foreach proto: generated_protocols if proto[1] == 'internal' base_file = proto_name xml_path = '@0@.xml'.format(proto_name) + elif proto[1] == '3rdparty' + base_file = proto_name + xml_path = '@0@/@1@.xml'.format(proto[2], base_file) elif proto[1] == 'stable' base_file = proto_name xml_path = '@0@/stable/@1@/@1@.xml'.format(dir_wp_base, base_file) -- 2.21.0