From 5076208fd90be98deff790f0f4429cfe54f1457a Mon Sep 17 00:00:00 2001 From: "Miguel A. Vico" Date: Thu, 18 May 2017 14:35:36 -0700 Subject: [PATCH 07/10] 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 --- Makefile.am | 14 ++++ compositor/meson.build | 1 + configure.ac | 4 ++ libweston/compositor.c | 78 +++++++++++++++++++++ libweston/compositor.h | 4 ++ libweston/gl-renderer.c | 147 +++++++++++++++++++++++++--------------- protocol/meson.build | 7 ++ 7 files changed, 201 insertions(+), 54 deletions(-) diff --git a/Makefile.am b/Makefile.am index 5407b593..0bad6976 100644 --- a/Makefile.am +++ b/Makefile.am @@ -192,6 +192,11 @@ nodist_libweston_@LIBWESTON_MAJOR@_la_SOURCES = \ BUILT_SOURCES += $(nodist_libweston_@LIBWESTON_MAJOR@_la_SOURCES) +nodist_libweston_eglstreams_@LIBWESTON_MAJOR@_la_SOURCES = \ + protocol/wayland-eglstream-controller-server-protocol.h + +BUILT_SOURCES += $(nodist_libweston_eglstreams_@LIBWESTON_MAJOR@_la_SOURCES) + bin_PROGRAMS += weston weston_LDFLAGS = -export-dynamic @@ -1736,6 +1741,15 @@ protocol/%-server-protocol.h : $(WAYLAND_PROTOCOLS_DATADIR)/$$(call protostabili protocol/%-client-protocol.h : $(WAYLAND_PROTOCOLS_DATADIR)/$$(call protostability,$$*)/$$(call protoname,$$*)/$$*.xml $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) client-header < $< > $@ +protocol/%-protocol.c : $(WAYLAND_EGLSTREAM_PROTOCOLS_DATADIR)/%.xml + $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) code < $< > $@ + +protocol/%-server-protocol.h : $(WAYLAND_EGLSTREAM_PROTOCOLS_DATADIR)/%.xml + $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) server-header < $< > $@ + +protocol/%-client-protocol.h : $(WAYLAND_EGLSTREAM_PROTOCOLS_DATADIR)/%.xml + $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) client-header < $< > $@ + protocol/%-protocol.c : $(top_srcdir)/protocol/%.xml $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) code < $< > $@ diff --git a/compositor/meson.build b/compositor/meson.build index d5d7282f..7655ef5a 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/configure.ac b/configure.ac index 425ba238..5c1932bc 100644 --- a/configure.ac +++ b/configure.ac @@ -261,6 +261,10 @@ PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.17], [ac_wayland_protocols_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`]) AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, $ac_wayland_protocols_pkgdatadir) +PKG_CHECK_MODULES(WAYLAND_EGLSTREAM_PROTOCOLS, [wayland-eglstream-protocols], + [ac_wayland_eglstream_protocols_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir wayland-eglstream-protocols`]) +AC_SUBST(WAYLAND_EGLSTREAM_PROTOCOLS_DATADIR, $ac_wayland_eglstream_protocols_pkgdatadir) + AC_ARG_ENABLE(wayland-compositor, [ --enable-wayland-compositor],, enable_wayland_compositor=yes) AM_CONDITIONAL(ENABLE_WAYLAND_COMPOSITOR, diff --git a/libweston/compositor.c b/libweston/compositor.c index d87522e7..947739f8 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 @@ -71,6 +72,7 @@ #include "version.h" #include "plugin-registry.h" #include "pixel-formats.h" +#include "wayland-eglstream-controller-server-protocol.h" #define DEFAULT_REPAINT_WINDOW 7 /* milliseconds */ @@ -6473,6 +6475,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) @@ -6836,6 +6912,8 @@ weston_compositor_create(struct wl_display *display, void *user_data) ec, bind_presentation)) goto fail; + init_eglstream_controller(ec->wl_display); + if (weston_debug_compositor_create(ec) < 0) goto fail; diff --git a/libweston/compositor.h b/libweston/compositor.h index a5223c28..1c39c50c 100644 --- a/libweston/compositor.h +++ b/libweston/compositor.h @@ -925,6 +925,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/gl-renderer.c b/libweston/gl-renderer.c index 5e594535..8265a712 100644 --- a/libweston/gl-renderer.c +++ b/libweston/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 @@ -2537,18 +2537,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; @@ -2557,65 +2548,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 */ @@ -4041,6 +4078,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 34026ff9..37642298 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -7,6 +7,9 @@ prog_scanner = find_program(dep_scanner.get_pkgconfig_variable('wayland_scanner' dep_wp = dependency('wayland-protocols', version: '>= 1.17') 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', @@ -37,6 +40,7 @@ generated_protocols = [ [ 'xdg-output', 'v1' ], [ 'xdg-shell', 'v6' ], [ 'xdg-shell', 'stable' ], + [ 'wayland-eglstream-controller', '3rdparty', dir_wep_base ], ] foreach proto: generated_protocols @@ -44,6 +48,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