/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Endless Mobile * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ /** * SECTION:meta-wayland-buffer * @title: MetaWaylandBuffer * @short_description: A wrapper for wayland buffers * * #MetaWaylandBuffer is a general wrapper around wl_buffer, the basic way of * passing rendered data from Wayland clients to the compositor. Note that a * buffer can be backed by several types of memory, as specified by * #MetaWaylandBufferType. */ /** * MetaWaylandBufferType: * @META_WAYLAND_BUFFER_TYPE_UNKNOWN: Unknown type. * @META_WAYLAND_BUFFER_TYPE_SHM: wl_buffer backed by shared memory * @META_WAYLAND_BUFFER_TYPE_EGL_IMAGE: wl_buffer backed by an EGLImage * @META_WAYLAND_BUFFER_TYPE_EGL_STREAM: wl_buffer backed by an EGLStream (NVIDIA-specific) * @META_WAYLAND_BUFFER_TYPE_DMA_BUF: wl_buffer backed by a Linux DMA-BUF * * Specifies the backing memory for a #MetaWaylandBuffer. Depending on the type * of buffer, this will lead to different handling for the compositor. For * example, a shared-memory buffer will still need to be uploaded to the GPU. */ #include "config.h" #include "wayland/meta-wayland-buffer.h" #include #include "backends/meta-backend-private.h" #include "clutter/clutter.h" #include "cogl/cogl-egl.h" #include "meta/util.h" #include "wayland/meta-wayland-dma-buf.h" #include "wayland/meta-wayland-private.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-drm-buffer-gbm.h" #include "backends/native/meta-kms-utils.h" #include "backends/native/meta-onscreen-native.h" #include "backends/native/meta-renderer-native.h" #endif #ifndef DRM_FORMAT_MOD_INVALID #define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1) #endif enum { RESOURCE_DESTROYED, LAST_SIGNAL }; guint signals[LAST_SIGNAL]; G_DEFINE_TYPE (MetaWaylandBuffer, meta_wayland_buffer, G_TYPE_OBJECT); static void meta_wayland_buffer_destroy_handler (struct wl_listener *listener, void *data) { MetaWaylandBuffer *buffer = wl_container_of (listener, buffer, destroy_listener); buffer->resource = NULL; wl_list_remove (&buffer->destroy_listener.link); g_signal_emit (buffer, signals[RESOURCE_DESTROYED], 0); g_object_unref (buffer); } MetaWaylandBuffer * meta_wayland_buffer_from_resource (MetaWaylandCompositor *compositor, struct wl_resource *resource) { MetaWaylandBuffer *buffer; struct wl_listener *listener; listener = wl_resource_get_destroy_listener (resource, meta_wayland_buffer_destroy_handler); if (listener) { buffer = wl_container_of (listener, buffer, destroy_listener); } else { buffer = g_object_new (META_TYPE_WAYLAND_BUFFER, NULL); buffer->resource = resource; buffer->compositor = compositor; buffer->destroy_listener.notify = meta_wayland_buffer_destroy_handler; wl_resource_add_destroy_listener (resource, &buffer->destroy_listener); } return buffer; } struct wl_resource * meta_wayland_buffer_get_resource (MetaWaylandBuffer *buffer) { return buffer->resource; } gboolean meta_wayland_buffer_is_realized (MetaWaylandBuffer *buffer) { return buffer->type != META_WAYLAND_BUFFER_TYPE_UNKNOWN; } gboolean meta_wayland_buffer_realize (MetaWaylandBuffer *buffer) { #ifdef HAVE_WAYLAND_EGLSTREAM MetaWaylandEglStream *stream; #endif MetaWaylandDmaBufBuffer *dma_buf; MetaWaylandSinglePixelBuffer *single_pixel_buffer; if (wl_shm_buffer_get (buffer->resource) != NULL) { buffer->type = META_WAYLAND_BUFFER_TYPE_SHM; return TRUE; } #ifdef HAVE_WAYLAND_EGLSTREAM stream = meta_wayland_egl_stream_new (buffer, NULL); if (stream) { CoglTexture2D *texture; texture = meta_wayland_egl_stream_create_texture (stream, NULL); if (!texture) return FALSE; buffer->egl_stream.stream = stream; buffer->type = META_WAYLAND_BUFFER_TYPE_EGL_STREAM; buffer->egl_stream.texture = COGL_TEXTURE (texture); buffer->is_y_inverted = meta_wayland_egl_stream_is_y_inverted (stream); return TRUE; } #endif /* HAVE_WAYLAND_EGLSTREAM */ if (meta_wayland_compositor_is_egl_display_bound (buffer->compositor)) { MetaContext *context = meta_wayland_compositor_get_context (buffer->compositor); MetaBackend *backend = meta_context_get_backend (context); EGLint format; 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 (meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource, EGL_TEXTURE_FORMAT, &format, NULL)) { buffer->type = META_WAYLAND_BUFFER_TYPE_EGL_IMAGE; buffer->dma_buf.dma_buf = meta_wayland_dma_buf_fds_for_wayland_buffer (buffer); return TRUE; } } dma_buf = meta_wayland_dma_buf_from_buffer (buffer); if (dma_buf) { buffer->dma_buf.dma_buf = dma_buf; buffer->type = META_WAYLAND_BUFFER_TYPE_DMA_BUF; return TRUE; } single_pixel_buffer = meta_wayland_single_pixel_buffer_from_buffer (buffer); if (single_pixel_buffer) { buffer->single_pixel.single_pixel_buffer = single_pixel_buffer; buffer->type = META_WAYLAND_BUFFER_TYPE_SINGLE_PIXEL; return TRUE; } return FALSE; } static gboolean shm_format_to_cogl_pixel_format (enum wl_shm_format shm_format, CoglPixelFormat *format_out, CoglTextureComponents *components_out) { CoglPixelFormat format; CoglTextureComponents components = COGL_TEXTURE_COMPONENTS_RGBA; switch (shm_format) { #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; case WL_SHM_FORMAT_XBGR8888: components = COGL_TEXTURE_COMPONENTS_RGB; G_GNUC_FALLTHROUGH; case WL_SHM_FORMAT_ABGR8888: format = COGL_PIXEL_FORMAT_ABGR_8888_PRE; break; #elif G_BYTE_ORDER == G_LITTLE_ENDIAN case WL_SHM_FORMAT_RGB565: format = COGL_PIXEL_FORMAT_RGB_565; components = COGL_TEXTURE_COMPONENTS_RGB; break; 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; case WL_SHM_FORMAT_XBGR8888: components = COGL_TEXTURE_COMPONENTS_RGB; G_GNUC_FALLTHROUGH; case WL_SHM_FORMAT_ABGR8888: format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; break; case WL_SHM_FORMAT_XRGB2101010: components = COGL_TEXTURE_COMPONENTS_RGB; G_GNUC_FALLTHROUGH; case WL_SHM_FORMAT_ARGB2101010: format = COGL_PIXEL_FORMAT_ARGB_2101010_PRE; break; case WL_SHM_FORMAT_XBGR2101010: components = COGL_TEXTURE_COMPONENTS_RGB; G_GNUC_FALLTHROUGH; case WL_SHM_FORMAT_ABGR2101010: format = COGL_PIXEL_FORMAT_ABGR_2101010_PRE; break; case WL_SHM_FORMAT_XRGB16161616F: components = COGL_TEXTURE_COMPONENTS_RGB; G_GNUC_FALLTHROUGH; case WL_SHM_FORMAT_ARGB16161616F: format = COGL_PIXEL_FORMAT_BGRA_FP_16161616_PRE; break; case WL_SHM_FORMAT_XBGR16161616F: components = COGL_TEXTURE_COMPONENTS_RGB; G_GNUC_FALLTHROUGH; case WL_SHM_FORMAT_ABGR16161616F: format = COGL_PIXEL_FORMAT_RGBA_FP_16161616_PRE; break; #endif default: return FALSE; } if (format_out) *format_out = format; if (components_out) *components_out = components; return TRUE; } static gboolean shm_buffer_get_cogl_pixel_format (MetaWaylandBuffer *buffer, struct wl_shm_buffer *shm_buffer, CoglPixelFormat *format_out, CoglTextureComponents *components_out) { MetaContext *context = meta_wayland_compositor_get_context (buffer->compositor); MetaBackend *backend = meta_context_get_backend (context); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); CoglPixelFormat cogl_format; CoglTextureComponents cogl_components; if (!shm_format_to_cogl_pixel_format (wl_shm_buffer_get_format (shm_buffer), &cogl_format, &cogl_components)) return FALSE; if (!cogl_context_format_supports_upload (cogl_context, cogl_format)) return FALSE; if (format_out) *format_out = cogl_format; if (components_out) *components_out = cogl_components; return TRUE; } static const char * shm_format_to_string (MetaDrmFormatBuf *format_buf, uint32_t shm_format) { const char *result; switch (shm_format) { case WL_SHM_FORMAT_ARGB8888: result = "ARGB8888"; break; case WL_SHM_FORMAT_XRGB8888: result = "XRGB8888"; break; default: result = meta_drm_format_to_string (format_buf, shm_format); break; } return result; } static gboolean shm_buffer_attach (MetaWaylandBuffer *buffer, CoglTexture **texture, GError **error) { MetaContext *context = meta_wayland_compositor_get_context (buffer->compositor); MetaBackend *backend = meta_context_get_backend (context); 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 *new_texture; MetaDrmFormatBuf format_buf; 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_get_cogl_pixel_format (buffer, shm_buffer, &format, &components)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid shm pixel format"); return FALSE; } meta_topic (META_DEBUG_WAYLAND, "[wl-shm] wl_buffer@%u wl_shm_format %s -> CoglPixelFormat %s", wl_resource_get_id (meta_wayland_buffer_get_resource (buffer)), shm_format_to_string (&format_buf, wl_shm_buffer_get_format (shm_buffer)), cogl_pixel_format_to_string (format)); if (*texture && cogl_texture_get_width (*texture) == width && cogl_texture_get_height (*texture) == height && cogl_texture_get_components (*texture) == components && _cogl_texture_get_format (*texture) == format) { buffer->is_y_inverted = TRUE; return TRUE; } cogl_clear_object (texture); wl_shm_buffer_begin_access (shm_buffer); bitmap = cogl_bitmap_new_for_data (cogl_context, width, height, format, stride, wl_shm_buffer_get_data (shm_buffer)); new_texture = COGL_TEXTURE (cogl_texture_2d_new_from_bitmap (bitmap)); cogl_texture_set_components (new_texture, components); if (!cogl_texture_allocate (new_texture, error)) { g_clear_pointer (&new_texture, cogl_object_unref); if (g_error_matches (*error, COGL_TEXTURE_ERROR, COGL_TEXTURE_ERROR_SIZE)) { CoglTexture2DSliced *texture_sliced; g_clear_error (error); texture_sliced = cogl_texture_2d_sliced_new_from_bitmap (bitmap, COGL_TEXTURE_MAX_WASTE); new_texture = COGL_TEXTURE (texture_sliced); cogl_texture_set_components (new_texture, components); if (!cogl_texture_allocate (new_texture, error)) g_clear_pointer (&new_texture, cogl_object_unref); } } cogl_object_unref (bitmap); wl_shm_buffer_end_access (shm_buffer); if (!new_texture) return FALSE; *texture = new_texture; buffer->is_y_inverted = TRUE; return TRUE; } static gboolean egl_image_buffer_attach (MetaWaylandBuffer *buffer, CoglTexture **texture, GError **error) { MetaContext *context = meta_wayland_compositor_get_context (buffer->compositor); MetaBackend *backend = meta_context_get_backend (context); 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); int format, width, height, y_inverted; CoglPixelFormat cogl_format; EGLImageKHR egl_image; CoglEglImageFlags flags; CoglTexture2D *texture_2d; if (buffer->egl_image.texture) { cogl_clear_object (texture); *texture = cogl_object_ref (buffer->egl_image.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; if (!meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource, EGL_WAYLAND_Y_INVERTED_WL, &y_inverted, NULL)) y_inverted = EGL_TRUE; 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; } /* The WL_bind_wayland_display spec states that EGL_NO_CONTEXT is to be used * in conjunction with the EGL_WAYLAND_BUFFER_WL target. */ egl_image = meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT, EGL_WAYLAND_BUFFER_WL, buffer->resource, NULL, error); if (egl_image == EGL_NO_IMAGE_KHR) return FALSE; flags = COGL_EGL_IMAGE_FLAG_NONE; texture_2d = cogl_egl_texture_2d_new_from_image (cogl_context, width, height, cogl_format, egl_image, flags, error); meta_egl_destroy_image (egl, egl_display, egl_image, NULL); if (!texture_2d) return FALSE; buffer->egl_image.texture = COGL_TEXTURE (texture_2d); buffer->is_y_inverted = !!y_inverted; cogl_clear_object (texture); *texture = cogl_object_ref (buffer->egl_image.texture); return TRUE; } #ifdef HAVE_WAYLAND_EGLSTREAM static gboolean egl_stream_buffer_attach (MetaWaylandBuffer *buffer, CoglTexture **texture, GError **error) { MetaWaylandEglStream *stream = buffer->egl_stream.stream; g_assert (stream); if (!meta_wayland_egl_stream_attach (stream, error)) return FALSE; cogl_clear_object (texture); *texture = cogl_object_ref (buffer->egl_stream.texture); return TRUE; } #endif /* HAVE_WAYLAND_EGLSTREAM */ static void on_onscreen_destroyed (gpointer user_data, GObject *where_the_onscreen_was) { MetaWaylandBuffer *buffer = user_data; g_hash_table_remove (buffer->tainted_scanout_onscreens, where_the_onscreen_was); } static void on_scanout_failed (CoglScanout *scanout, CoglOnscreen *onscreen, MetaWaylandBuffer *buffer) { if (!buffer->tainted_scanout_onscreens) buffer->tainted_scanout_onscreens = g_hash_table_new (NULL, NULL); g_hash_table_add (buffer->tainted_scanout_onscreens, onscreen); g_object_weak_ref (G_OBJECT (onscreen), on_onscreen_destroyed, buffer); } static void clear_tainted_scanout_onscreens (MetaWaylandBuffer *buffer) { GHashTableIter iter; CoglOnscreen *onscreen; if (!buffer->tainted_scanout_onscreens) return; g_hash_table_iter_init (&iter, buffer->tainted_scanout_onscreens); while (g_hash_table_iter_next (&iter, (gpointer *) &onscreen, NULL)) g_object_weak_unref (G_OBJECT (onscreen), on_onscreen_destroyed, buffer); g_hash_table_remove_all (buffer->tainted_scanout_onscreens); } /** * meta_wayland_buffer_attach: * @buffer: a pointer to a #MetaWaylandBuffer * @texture: (inout) (transfer full): a #CoglTexture representing the surface * content * @error: return location for error or %NULL * * This function should be passed a pointer to the texture used to draw the * surface content. The texture will either be replaced by a new texture, or * stay the same, in which case, it may later be updated with new content when * processing damage. The new texture might be newly created, or it may be a * reference to an already existing one. * * If replaced, the old texture will have its reference count decreased by one, * potentially freeing it. When a new texture is set, the caller (i.e. the * surface) will be the owner of one reference count. It must free it, either * using g_object_unref() or have it updated again using * meta_wayland_buffer_attach(), which also might free it, as described above. */ gboolean meta_wayland_buffer_attach (MetaWaylandBuffer *buffer, CoglTexture **texture, GError **error) { COGL_TRACE_BEGIN_SCOPED (MetaWaylandBufferAttach, "WaylandBuffer (attach)"); clear_tainted_scanout_onscreens (buffer); if (!meta_wayland_buffer_is_realized (buffer)) { /* The buffer should have been realized at surface commit time */ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown buffer type"); return FALSE; } switch (buffer->type) { case META_WAYLAND_BUFFER_TYPE_SHM: return shm_buffer_attach (buffer, texture, error); case META_WAYLAND_BUFFER_TYPE_EGL_IMAGE: return egl_image_buffer_attach (buffer, texture, error); #ifdef HAVE_WAYLAND_EGLSTREAM case META_WAYLAND_BUFFER_TYPE_EGL_STREAM: return egl_stream_buffer_attach (buffer, texture, error); #endif case META_WAYLAND_BUFFER_TYPE_DMA_BUF: return meta_wayland_dma_buf_buffer_attach (buffer, texture, error); case META_WAYLAND_BUFFER_TYPE_SINGLE_PIXEL: return meta_wayland_single_pixel_buffer_attach (buffer, texture, error); case META_WAYLAND_BUFFER_TYPE_UNKNOWN: g_assert_not_reached (); return FALSE; } g_assert_not_reached (); return FALSE; } /** * meta_wayland_buffer_create_snippet: * @buffer: A #MetaWaylandBuffer object * * If needed, this method creates a #CoglSnippet to make sure the buffer can be * dealt with appropriately in a #CoglPipeline that renders it. * * Returns: (transfer full) (nullable): A new #CoglSnippet, or %NULL. */ CoglSnippet * meta_wayland_buffer_create_snippet (MetaWaylandBuffer *buffer) { #ifdef HAVE_WAYLAND_EGLSTREAM if (!buffer->egl_stream.stream) return NULL; return meta_wayland_egl_stream_create_snippet (buffer->egl_stream.stream); #else return NULL; #endif /* HAVE_WAYLAND_EGLSTREAM */ } void meta_wayland_buffer_inc_use_count (MetaWaylandBuffer *buffer) { g_warn_if_fail (buffer->resource); buffer->use_count++; } void meta_wayland_buffer_dec_use_count (MetaWaylandBuffer *buffer) { g_return_if_fail (buffer->use_count > 0); buffer->use_count--; if (buffer->use_count == 0 && buffer->resource) wl_buffer_send_release (buffer->resource); } gboolean meta_wayland_buffer_is_y_inverted (MetaWaylandBuffer *buffer) { return buffer->is_y_inverted; } static gboolean process_shm_buffer_damage (MetaWaylandBuffer *buffer, CoglTexture *texture, cairo_region_t *region, GError **error) { struct wl_shm_buffer *shm_buffer; int i, n_rectangles; gboolean set_texture_failed = FALSE; CoglPixelFormat format; n_rectangles = cairo_region_num_rectangles (region); shm_buffer = wl_shm_buffer_get (buffer->resource); shm_buffer_get_cogl_pixel_format (buffer, shm_buffer, &format, NULL); g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, FALSE); 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); cairo_rectangle_int_t rect; int bpp; bpp = cogl_pixel_format_get_bytes_per_pixel (format, 0); cairo_region_get_rectangle (region, i, &rect); if (!_cogl_texture_set_region (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, CoglTexture *texture, cairo_region_t *region) { gboolean res = FALSE; GError *error = NULL; g_return_if_fail (buffer->resource); switch (buffer->type) { case META_WAYLAND_BUFFER_TYPE_SHM: res = process_shm_buffer_damage (buffer, texture, region, &error); break; case META_WAYLAND_BUFFER_TYPE_EGL_IMAGE: #ifdef HAVE_WAYLAND_EGLSTREAM case META_WAYLAND_BUFFER_TYPE_EGL_STREAM: #endif case META_WAYLAND_BUFFER_TYPE_DMA_BUF: case META_WAYLAND_BUFFER_TYPE_SINGLE_PIXEL: 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; break; } if (!res) { g_warning ("Failed to process Wayland buffer damage: %s", error->message); g_error_free (error); } } static CoglScanout * try_acquire_egl_image_scanout (MetaWaylandBuffer *buffer, CoglOnscreen *onscreen) { #ifdef HAVE_NATIVE_BACKEND MetaContext *context = meta_wayland_compositor_get_context (buffer->compositor); MetaBackend *backend = meta_context_get_backend (context); MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); MetaGpuKms *gpu_kms; MetaDeviceFile *device_file; struct gbm_device *gbm_device; struct gbm_bo *gbm_bo; MetaDrmBufferFlags flags; g_autoptr (MetaDrmBufferGbm) fb = NULL; g_autoptr (GError) error = NULL; gpu_kms = meta_renderer_native_get_primary_gpu (renderer_native); device_file = meta_renderer_native_get_primary_device_file (renderer_native); gbm_device = meta_gbm_device_from_gpu (gpu_kms); gbm_bo = gbm_bo_import (gbm_device, GBM_BO_IMPORT_WL_BUFFER, buffer->resource, GBM_BO_USE_SCANOUT); if (!gbm_bo) return NULL; flags = META_DRM_BUFFER_FLAG_NONE; if (gbm_bo_get_modifier (gbm_bo) == DRM_FORMAT_MOD_INVALID) flags |= META_DRM_BUFFER_FLAG_DISABLE_MODIFIERS; fb = meta_drm_buffer_gbm_new_take (device_file, gbm_bo, flags, &error); if (!fb) { g_debug ("Failed to create scanout buffer: %s", error->message); gbm_bo_destroy (gbm_bo); return NULL; } if (!meta_onscreen_native_is_buffer_scanout_compatible (onscreen, META_DRM_BUFFER (fb))) return NULL; return COGL_SCANOUT (g_steal_pointer (&fb)); #else return NULL; #endif } CoglScanout * meta_wayland_buffer_try_acquire_scanout (MetaWaylandBuffer *buffer, CoglOnscreen *onscreen) { CoglScanout *scanout = NULL; COGL_TRACE_BEGIN_SCOPED (MetaWaylandBufferTryScanout, "WaylandBuffer (try scanout)"); if (buffer->tainted_scanout_onscreens && g_hash_table_lookup (buffer->tainted_scanout_onscreens, onscreen)) { meta_topic (META_DEBUG_RENDER, "Buffer scanout capability tainted"); return NULL; } switch (buffer->type) { case META_WAYLAND_BUFFER_TYPE_SHM: case META_WAYLAND_BUFFER_TYPE_SINGLE_PIXEL: #ifdef HAVE_WAYLAND_EGLSTREAM case META_WAYLAND_BUFFER_TYPE_EGL_STREAM: #endif meta_topic (META_DEBUG_RENDER, "Buffer type not scanout compatible"); return NULL; case META_WAYLAND_BUFFER_TYPE_EGL_IMAGE: scanout = try_acquire_egl_image_scanout (buffer, onscreen); break; case META_WAYLAND_BUFFER_TYPE_DMA_BUF: { MetaWaylandDmaBufBuffer *dma_buf; dma_buf = meta_wayland_dma_buf_from_buffer (buffer); if (!dma_buf) return NULL; scanout = meta_wayland_dma_buf_try_acquire_scanout (dma_buf, onscreen); break; } case META_WAYLAND_BUFFER_TYPE_UNKNOWN: g_warn_if_reached (); return NULL; } if (scanout) g_signal_connect (scanout, "scanout-failed", G_CALLBACK (on_scanout_failed), buffer); return scanout; } static void meta_wayland_buffer_finalize (GObject *object) { MetaWaylandBuffer *buffer = META_WAYLAND_BUFFER (object); g_warn_if_fail (buffer->use_count == 0); clear_tainted_scanout_onscreens (buffer); g_clear_pointer (&buffer->tainted_scanout_onscreens, g_hash_table_unref); g_clear_pointer (&buffer->egl_image.texture, cogl_object_unref); #ifdef HAVE_WAYLAND_EGLSTREAM g_clear_pointer (&buffer->egl_stream.texture, cogl_object_unref); g_clear_object (&buffer->egl_stream.stream); #endif g_clear_pointer (&buffer->dma_buf.texture, cogl_object_unref); g_clear_object (&buffer->dma_buf.dma_buf); g_clear_pointer (&buffer->single_pixel.single_pixel_buffer, meta_wayland_single_pixel_buffer_free); cogl_clear_object (&buffer->single_pixel.texture); G_OBJECT_CLASS (meta_wayland_buffer_parent_class)->finalize (object); } static void meta_wayland_buffer_init (MetaWaylandBuffer *buffer) { } static void meta_wayland_buffer_class_init (MetaWaylandBufferClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_buffer_finalize; /** * MetaWaylandBuffer::resource-destroyed: * * Called when the underlying wl_resource was destroyed. */ signals[RESOURCE_DESTROYED] = g_signal_new ("resource-destroyed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } void meta_wayland_init_shm (MetaWaylandCompositor *compositor) { MetaContext *context = meta_wayland_compositor_get_context (compositor); MetaBackend *backend = meta_context_get_backend (context); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); static const enum wl_shm_format shm_formats[] = { WL_SHM_FORMAT_ABGR8888, WL_SHM_FORMAT_XBGR8888, #if G_BYTE_ORDER == G_LITTLE_ENDIAN WL_SHM_FORMAT_RGB565, WL_SHM_FORMAT_ARGB2101010, WL_SHM_FORMAT_XRGB2101010, WL_SHM_FORMAT_ABGR2101010, WL_SHM_FORMAT_XBGR2101010, WL_SHM_FORMAT_ARGB16161616F, WL_SHM_FORMAT_XRGB16161616F, WL_SHM_FORMAT_ABGR16161616F, WL_SHM_FORMAT_XBGR16161616F, #endif }; int i; wl_display_init_shm (compositor->wayland_display); for (i = 0; i < G_N_ELEMENTS (shm_formats); i++) { CoglPixelFormat cogl_format; if (!shm_format_to_cogl_pixel_format (shm_formats[i], &cogl_format, NULL)) continue; if (!cogl_context_format_supports_upload (cogl_context, cogl_format)) continue; wl_display_add_shm_format (compositor->wayland_display, shm_formats[i]); } }