From 23455985cdc867772e9ad2dc78c2a29641b52473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 20 Oct 2016 15:35:29 +0800 Subject: [PATCH] wayland: Make MetaWaylandBuffer handle texture generation itself Don't rely on the Cogl layer having Wayland specific paths by determining the buffer type and creating the EGLImage ourself, while using the newly exposed CoglTexture from EGLImage API. This changes the API used by MetaWaylandSurface to make the MetaWaylandBuffer API be aware when the buffer is being attached. For SHM and EGL buffers, only the first time it is attached will result in a new texture being allocated, but later for EGLStream's, more logic on every attach is needed. https://bugzilla.gnome.org/show_bug.cgi?id=773629 --- cogl/cogl/Makefile.am | 2 +- src/wayland/meta-wayland-buffer.c | 324 ++++++++++++++++++++++++----- src/wayland/meta-wayland-buffer.h | 4 +- src/wayland/meta-wayland-surface.c | 26 ++- 4 files changed, 295 insertions(+), 61 deletions(-) diff --git a/cogl/cogl/Makefile.am b/cogl/cogl/Makefile.am index fef656a1e..88712a9ca 100644 --- a/cogl/cogl/Makefile.am +++ b/cogl/cogl/Makefile.am @@ -452,7 +452,7 @@ libmutter_cogl_la_LDFLAGS = \ -avoid-version \ -export-dynamic \ -rpath $(mutterlibdir) \ - -export-symbols-regex "^(cogl|_cogl_debug_flags|_cogl_atlas_new|_cogl_atlas_add_reorganize_callback|_cogl_atlas_reserve_space|_cogl_callback|_cogl_util_get_eye_planes_for_screen_poly|_cogl_atlas_texture_remove_reorganize_callback|_cogl_atlas_texture_add_reorganize_callback|_cogl_texture_get_format|_cogl_texture_foreach_sub_texture_in_region|_cogl_profile_trace_message|_cogl_context_get_default|_cogl_framebuffer_get_stencil_bits|_cogl_clip_stack_push_rectangle|_cogl_framebuffer_get_modelview_stack|_cogl_object_default_unref|_cogl_pipeline_foreach_layer_internal|_cogl_clip_stack_push_primitive|_cogl_buffer_unmap_for_fill_or_fallback|_cogl_framebuffer_draw_primitive|_cogl_debug_instances|_cogl_framebuffer_get_projection_stack|_cogl_pipeline_layer_get_texture|_cogl_buffer_map_for_fill_or_fallback|_cogl_texture_can_hardware_repeat|_cogl_pipeline_prune_to_n_layers|_cogl_primitive_draw|test_|unit_test_|_cogl_winsys_glx_get_vtable|_cogl_winsys_egl_xlib_get_vtable|_cogl_winsys_egl_get_vtable|_cogl_closure_disconnect|_cogl_onscreen_notify_complete|_cogl_onscreen_notify_frame_sync|_cogl_winsys_egl_renderer_connect_common|_cogl_winsys_error_quark|_cogl_set_error|_cogl_poll_renderer_add_fd|_cogl_poll_renderer_add_idle|_cogl_framebuffer_winsys_update_size|_cogl_winsys_egl_make_current).*" + -export-symbols-regex "^(cogl|_cogl_debug_flags|_cogl_atlas_new|_cogl_atlas_add_reorganize_callback|_cogl_atlas_reserve_space|_cogl_callback|_cogl_util_get_eye_planes_for_screen_poly|_cogl_atlas_texture_remove_reorganize_callback|_cogl_atlas_texture_add_reorganize_callback|_cogl_texture_get_format|_cogl_texture_foreach_sub_texture_in_region|_cogl_texture_set_region|_cogl_profile_trace_message|_cogl_context_get_default|_cogl_framebuffer_get_stencil_bits|_cogl_clip_stack_push_rectangle|_cogl_framebuffer_get_modelview_stack|_cogl_object_default_unref|_cogl_pipeline_foreach_layer_internal|_cogl_clip_stack_push_primitive|_cogl_buffer_unmap_for_fill_or_fallback|_cogl_framebuffer_draw_primitive|_cogl_debug_instances|_cogl_framebuffer_get_projection_stack|_cogl_pipeline_layer_get_texture|_cogl_buffer_map_for_fill_or_fallback|_cogl_texture_can_hardware_repeat|_cogl_pipeline_prune_to_n_layers|_cogl_primitive_draw|test_|unit_test_|_cogl_winsys_glx_get_vtable|_cogl_winsys_egl_xlib_get_vtable|_cogl_winsys_egl_get_vtable|_cogl_closure_disconnect|_cogl_onscreen_notify_complete|_cogl_onscreen_notify_frame_sync|_cogl_winsys_egl_renderer_connect_common|_cogl_winsys_error_quark|_cogl_set_error|_cogl_poll_renderer_add_fd|_cogl_poll_renderer_add_idle|_cogl_framebuffer_winsys_update_size|_cogl_winsys_egl_make_current|_cogl_pixel_format_get_bytes_per_pixel).*" libmutter_cogl_la_SOURCES = $(cogl_sources_c) nodist_libmutter_cogl_la_SOURCES = $(BUILT_SOURCES) diff --git a/src/wayland/meta-wayland-buffer.c b/src/wayland/meta-wayland-buffer.c index b23ca3509..02c3923dc 100644 --- a/src/wayland/meta-wayland-buffer.c +++ b/src/wayland/meta-wayland-buffer.c @@ -27,9 +27,11 @@ #include "meta-wayland-buffer.h" #include -#include +#include #include +#include "backends/meta-backend-private.h" + enum { RESOURCE_DESTROYED, @@ -79,79 +81,301 @@ meta_wayland_buffer_from_resource (struct wl_resource *resource) return buffer; } -CoglTexture * -meta_wayland_buffer_ensure_texture (MetaWaylandBuffer *buffer) +typedef enum _MetaWaylandBufferType { - CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); - CoglError *catch_error = NULL; - CoglTexture *texture; - struct wl_shm_buffer *shm_buffer; + META_WAYLAND_BUFFER_TYPE_UNKNOWN, + META_WAYLAND_BUFFER_TYPE_SHM, + META_WAYLAND_BUFFER_TYPE_EGL_IMAGE, +} MetaWaylandBufferType; - g_return_val_if_fail (buffer->resource, NULL); +static MetaWaylandBufferType +determine_buffer_type (MetaWaylandBuffer *buffer) +{ + EGLint format; + MetaBackend *backend = meta_get_backend (); + MetaEgl *egl = meta_backend_get_egl (backend); + ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); + CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); + EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context); + + if (wl_shm_buffer_get (buffer->resource) != NULL) + return META_WAYLAND_BUFFER_TYPE_SHM; + + + if (meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource, + EGL_TEXTURE_FORMAT, &format, + NULL)) + return META_WAYLAND_BUFFER_TYPE_EGL_IMAGE; + + return META_WAYLAND_BUFFER_TYPE_UNKNOWN; +} + +static void +shm_buffer_get_cogl_pixel_format (struct wl_shm_buffer *shm_buffer, + CoglPixelFormat *format_out, + CoglTextureComponents *components_out) +{ + CoglPixelFormat format; + CoglTextureComponents components = COGL_TEXTURE_COMPONENTS_RGBA; + + switch (wl_shm_buffer_get_format (shm_buffer)) + { +#if G_BYTE_ORDER == G_BIG_ENDIAN + case WL_SHM_FORMAT_ARGB8888: + format = COGL_PIXEL_FORMAT_ARGB_8888_PRE; + break; + case WL_SHM_FORMAT_XRGB8888: + format = COGL_PIXEL_FORMAT_ARGB_8888; + components = COGL_TEXTURE_COMPONENTS_RGB; + break; +#elif G_BYTE_ORDER == G_LITTLE_ENDIAN + case WL_SHM_FORMAT_ARGB8888: + format = COGL_PIXEL_FORMAT_BGRA_8888_PRE; + break; + case WL_SHM_FORMAT_XRGB8888: + format = COGL_PIXEL_FORMAT_BGRA_8888; + components = COGL_TEXTURE_COMPONENTS_RGB; + break; +#endif + default: + g_warn_if_reached (); + format = COGL_PIXEL_FORMAT_ARGB_8888; + } + + if (format_out) + *format_out = format; + if (components_out) + *components_out = components; +} + +static gboolean +shm_buffer_attach (MetaWaylandBuffer *buffer, + GError **error) +{ + MetaBackend *backend = meta_get_backend (); + ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); + CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); + struct wl_shm_buffer *shm_buffer; + int stride, width, height; + CoglPixelFormat format; + CoglTextureComponents components; + CoglBitmap *bitmap; + CoglTexture *texture; if (buffer->texture) - goto out; + return TRUE; shm_buffer = wl_shm_buffer_get (buffer->resource); + stride = wl_shm_buffer_get_stride (shm_buffer); + width = wl_shm_buffer_get_width (shm_buffer); + height = wl_shm_buffer_get_height (shm_buffer); - if (shm_buffer) - wl_shm_buffer_begin_access (shm_buffer); + wl_shm_buffer_begin_access (shm_buffer); - texture = COGL_TEXTURE (cogl_wayland_texture_2d_new_from_buffer (ctx, - buffer->resource, - &catch_error)); + shm_buffer_get_cogl_pixel_format (shm_buffer, &format, &components); - if (shm_buffer) - wl_shm_buffer_end_access (shm_buffer); + bitmap = cogl_bitmap_new_for_data (cogl_context, + width, height, + format, + stride, + wl_shm_buffer_get_data (shm_buffer)); - if (!texture) - { - meta_warning ("Could not import pending buffer, ignoring commit: %s\n", - catch_error->message); - cogl_error_free (catch_error); - goto out; - } + texture = COGL_TEXTURE (cogl_texture_2d_new_from_bitmap (bitmap)); + cogl_texture_set_components (COGL_TEXTURE (texture), components); + + cogl_object_unref (bitmap); + + if (!cogl_texture_allocate (COGL_TEXTURE (texture), error)) + g_clear_pointer (&texture, cogl_object_unref); + + wl_shm_buffer_end_access (shm_buffer); buffer->texture = texture; - out: + if (!buffer->texture) + return FALSE; + + return TRUE; +} + +static gboolean +egl_image_buffer_attach (MetaWaylandBuffer *buffer, + GError **error) +{ + MetaBackend *backend = meta_get_backend (); + MetaEgl *egl = meta_backend_get_egl (backend); + ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); + CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); + EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context); + EGLContext egl_context = cogl_egl_context_get_egl_context (cogl_context); + int format, width, height; + CoglPixelFormat cogl_format; + EGLImageKHR egl_image; + CoglTexture2D *texture; + + if (buffer->texture) + return TRUE; + + if (!meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource, + EGL_TEXTURE_FORMAT, &format, + error)) + return FALSE; + + if (!meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource, + EGL_WIDTH, &width, + error)) + return FALSE; + + if (!meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource, + EGL_HEIGHT, &height, + error)) + return FALSE; + + switch (format) + { + case EGL_TEXTURE_RGB: + cogl_format = COGL_PIXEL_FORMAT_RGB_888; + break; + case EGL_TEXTURE_RGBA: + cogl_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; + break; + default: + g_set_error (error, G_IO_ERROR, + G_IO_ERROR_FAILED, + "Unsupported buffer format %d", format); + return FALSE; + } + + egl_image = meta_egl_create_image (egl, egl_display, egl_context, + EGL_WAYLAND_BUFFER_WL, buffer->resource, + NULL, + error); + if (egl_image == EGL_NO_IMAGE_KHR) + return FALSE; + + texture = cogl_egl_texture_2d_new_from_image (cogl_context, + width, height, + cogl_format, + egl_image, + error); + + meta_egl_destroy_image (egl, egl_display, egl_image, NULL); + + if (!texture) + return FALSE; + + buffer->texture = COGL_TEXTURE (texture); + + return TRUE; +} + +gboolean +meta_wayland_buffer_attach (MetaWaylandBuffer *buffer, + GError **error) +{ + MetaWaylandBufferType buffer_type; + + g_return_val_if_fail (buffer->resource, FALSE); + + buffer_type = determine_buffer_type (buffer); + + switch (buffer_type) + { + case META_WAYLAND_BUFFER_TYPE_SHM: + return shm_buffer_attach (buffer, error); + case META_WAYLAND_BUFFER_TYPE_EGL_IMAGE: + return egl_image_buffer_attach (buffer, error); + case META_WAYLAND_BUFFER_TYPE_UNKNOWN: + g_set_error (error, G_IO_ERROR, + G_IO_ERROR_FAILED, + "Unknown buffer type"); + return FALSE; + } + + g_assert_not_reached (); +} + +CoglTexture * +meta_wayland_buffer_get_texture (MetaWaylandBuffer *buffer) +{ return buffer->texture; } +static gboolean +process_shm_buffer_damage (MetaWaylandBuffer *buffer, + cairo_region_t *region, + GError **error) +{ + struct wl_shm_buffer *shm_buffer; + int i, n_rectangles; + gboolean set_texture_failed = FALSE; + + n_rectangles = cairo_region_num_rectangles (region); + + shm_buffer = wl_shm_buffer_get (buffer->resource); + wl_shm_buffer_begin_access (shm_buffer); + + for (i = 0; i < n_rectangles; i++) + { + const uint8_t *data = wl_shm_buffer_get_data (shm_buffer); + int32_t stride = wl_shm_buffer_get_stride (shm_buffer); + CoglPixelFormat format; + int bpp; + cairo_rectangle_int_t rect; + + shm_buffer_get_cogl_pixel_format (shm_buffer, &format, NULL); + bpp = _cogl_pixel_format_get_bytes_per_pixel (format); + cairo_region_get_rectangle (region, i, &rect); + + if (!_cogl_texture_set_region (buffer->texture, + rect.width, rect.height, + format, + stride, + data + rect.x * bpp + rect.y * stride, + rect.x, rect.y, + 0, + error)) + { + set_texture_failed = TRUE; + break; + } + } + + wl_shm_buffer_end_access (shm_buffer); + + return !set_texture_failed; +} + void meta_wayland_buffer_process_damage (MetaWaylandBuffer *buffer, cairo_region_t *region) { - struct wl_shm_buffer *shm_buffer; + MetaWaylandBufferType buffer_type; + gboolean res = FALSE; + GError *error = NULL; - shm_buffer = wl_shm_buffer_get (buffer->resource); + g_return_if_fail (buffer->resource); - if (shm_buffer) + buffer_type = determine_buffer_type (buffer); + + switch (buffer_type) { - int i, n_rectangles; + case META_WAYLAND_BUFFER_TYPE_SHM: + res = process_shm_buffer_damage (buffer, region, &error); + case META_WAYLAND_BUFFER_TYPE_EGL_IMAGE: + res = TRUE; + break; + case META_WAYLAND_BUFFER_TYPE_UNKNOWN: + g_set_error (&error, G_IO_ERROR, + G_IO_ERROR_FAILED, + "Unknown buffer type"); + res = FALSE; + } - n_rectangles = cairo_region_num_rectangles (region); - - wl_shm_buffer_begin_access (shm_buffer); - - for (i = 0; i < n_rectangles; i++) - { - CoglError *error = NULL; - cairo_rectangle_int_t rect; - cairo_region_get_rectangle (region, i, &rect); - cogl_wayland_texture_set_region_from_shm_buffer (buffer->texture, - rect.x, rect.y, rect.width, rect.height, - shm_buffer, - rect.x, rect.y, 0, &error); - - if (error) - { - meta_warning ("Failed to set texture region: %s\n", error->message); - cogl_error_free (error); - } - } - - wl_shm_buffer_end_access (shm_buffer); + if (!res) + { + g_warning ("Failed to process Wayland buffer damage: %s", error->message); + g_error_free (error); } } diff --git a/src/wayland/meta-wayland-buffer.h b/src/wayland/meta-wayland-buffer.h index 977892b7a..286c19abf 100644 --- a/src/wayland/meta-wayland-buffer.h +++ b/src/wayland/meta-wayland-buffer.h @@ -46,7 +46,9 @@ G_DECLARE_FINAL_TYPE (MetaWaylandBuffer, meta_wayland_buffer, META, WAYLAND_BUFFER, GObject); MetaWaylandBuffer * meta_wayland_buffer_from_resource (struct wl_resource *resource); -CoglTexture * meta_wayland_buffer_ensure_texture (MetaWaylandBuffer *buffer); +gboolean meta_wayland_buffer_attach (MetaWaylandBuffer *buffer, + GError **error); +CoglTexture * meta_wayland_buffer_get_texture (MetaWaylandBuffer *buffer); void meta_wayland_buffer_process_damage (MetaWaylandBuffer *buffer, cairo_region_t *region); diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index 99e3dfd9e..e089ab72d 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -738,21 +738,29 @@ apply_pending_state (MetaWaylandSurface *surface, if (pending->buffer) meta_wayland_surface_ref_buffer_use_count (surface); - if (switched_buffer && pending->buffer) + if (pending->buffer) { - CoglTexture *texture; + GError *error = NULL; - texture = meta_wayland_buffer_ensure_texture (pending->buffer); - if (!texture) + if (!meta_wayland_buffer_attach (pending->buffer, &error)) { + g_warning ("Could not import pending buffer: %s", error->message); wl_resource_post_error (surface->resource, WL_DISPLAY_ERROR_NO_MEMORY, - "Failed to create a texture for surface %i", - wl_resource_get_id (surface->resource)); - + "Failed to create a texture for surface %i: %s", + wl_resource_get_id (surface->resource), + error->message); + g_error_free (error); goto cleanup; } - meta_surface_actor_wayland_set_texture (surface_actor_wayland, - texture); + + if (switched_buffer) + { + CoglTexture *texture; + + texture = meta_wayland_buffer_get_texture (pending->buffer); + meta_surface_actor_wayland_set_texture (surface_actor_wayland, + texture); + } } /* If the newly attached buffer is going to be accessed directly without