b3dffb43b3
It might not be needed by the user of the buffer, so don't always require it up front. Instead make sure that any user that needs it first calls "meta_drm_buffer_ensure_fb_id()" to create the ID. Only the plain gbm implementation creates the ID lazilly, the other still does it on construction due to the objects used to create them only existing during construction. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1854>
517 lines
16 KiB
C
517 lines
16 KiB
C
/*
|
|
* Copyright (C) 2011 Intel Corporation.
|
|
* Copyright (C) 2016 Red Hat
|
|
* Copyright (C) 2018 DisplayLink (UK) Ltd.
|
|
* Copyright (C) 2018 Canonical Ltd.
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "backends/native/meta-drm-buffer-gbm.h"
|
|
|
|
#include <drm_fourcc.h>
|
|
#include <errno.h>
|
|
#include <gio/gio.h>
|
|
#include <xf86drm.h>
|
|
#include <xf86drmMode.h>
|
|
|
|
#include "backends/meta-backend-private.h"
|
|
#include "backends/native/meta-cogl-utils.h"
|
|
#include "backends/native/meta-drm-buffer-private.h"
|
|
|
|
struct _MetaDrmBufferGbm
|
|
{
|
|
MetaDrmBuffer parent;
|
|
|
|
struct gbm_surface *surface;
|
|
|
|
struct gbm_bo *bo;
|
|
};
|
|
|
|
static void
|
|
cogl_scanout_iface_init (CoglScanoutInterface *iface);
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (MetaDrmBufferGbm, meta_drm_buffer_gbm, META_TYPE_DRM_BUFFER,
|
|
G_IMPLEMENT_INTERFACE (COGL_TYPE_SCANOUT,
|
|
cogl_scanout_iface_init))
|
|
|
|
struct gbm_bo *
|
|
meta_drm_buffer_gbm_get_bo (MetaDrmBufferGbm *buffer_gbm)
|
|
{
|
|
return buffer_gbm->bo;
|
|
}
|
|
|
|
static int
|
|
meta_drm_buffer_gbm_export_fd (MetaDrmBuffer *buffer,
|
|
GError **error)
|
|
{
|
|
MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (buffer);
|
|
int fd;
|
|
|
|
fd = gbm_bo_get_fd (buffer_gbm->bo);
|
|
if (fd == -1)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
|
|
"Failed to export buffer fd: %s", g_strerror (errno));
|
|
return -1;
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
static int
|
|
meta_drm_buffer_gbm_get_width (MetaDrmBuffer *buffer)
|
|
{
|
|
MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (buffer);
|
|
|
|
return gbm_bo_get_width (buffer_gbm->bo);
|
|
}
|
|
|
|
static int
|
|
meta_drm_buffer_gbm_get_height (MetaDrmBuffer *buffer)
|
|
{
|
|
MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (buffer);
|
|
|
|
return gbm_bo_get_height (buffer_gbm->bo);
|
|
}
|
|
|
|
static int
|
|
meta_drm_buffer_gbm_get_stride (MetaDrmBuffer *buffer)
|
|
{
|
|
MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (buffer);
|
|
|
|
return gbm_bo_get_stride (buffer_gbm->bo);
|
|
}
|
|
|
|
static int
|
|
meta_drm_buffer_gbm_get_bpp (MetaDrmBuffer *buffer)
|
|
{
|
|
MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (buffer);
|
|
|
|
return gbm_bo_get_bpp (buffer_gbm->bo);
|
|
}
|
|
|
|
static uint32_t
|
|
meta_drm_buffer_gbm_get_format (MetaDrmBuffer *buffer)
|
|
{
|
|
MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (buffer);
|
|
|
|
return gbm_bo_get_format (buffer_gbm->bo);
|
|
}
|
|
|
|
static int
|
|
meta_drm_buffer_gbm_get_offset (MetaDrmBuffer *buffer,
|
|
int plane)
|
|
{
|
|
MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (buffer);
|
|
|
|
return gbm_bo_get_offset (buffer_gbm->bo, plane);
|
|
}
|
|
|
|
static uint32_t
|
|
meta_drm_buffer_gbm_get_modifier (MetaDrmBuffer *buffer)
|
|
{
|
|
MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (buffer);
|
|
|
|
return gbm_bo_get_modifier (buffer_gbm->bo);
|
|
}
|
|
|
|
static gboolean
|
|
meta_drm_buffer_gbm_ensure_fb_id (MetaDrmBuffer *buffer,
|
|
GError **error)
|
|
{
|
|
MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (buffer);
|
|
MetaDrmFbArgs fb_args = { 0, };
|
|
struct gbm_bo *bo = buffer_gbm->bo;
|
|
|
|
if (gbm_bo_get_handle_for_plane (bo, 0).s32 == -1)
|
|
{
|
|
/* Failed to fetch handle to plane, falling back to old method */
|
|
fb_args.strides[0] = gbm_bo_get_stride (bo);
|
|
fb_args.handles[0] = gbm_bo_get_handle (bo).u32;
|
|
fb_args.offsets[0] = 0;
|
|
fb_args.modifiers[0] = DRM_FORMAT_MOD_INVALID;
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < gbm_bo_get_plane_count (bo); i++)
|
|
{
|
|
fb_args.strides[i] = gbm_bo_get_stride_for_plane (bo, i);
|
|
fb_args.handles[i] = gbm_bo_get_handle_for_plane (bo, i).u32;
|
|
fb_args.offsets[i] = gbm_bo_get_offset (bo, i);
|
|
fb_args.modifiers[i] = gbm_bo_get_modifier (bo);
|
|
}
|
|
}
|
|
|
|
fb_args.width = gbm_bo_get_width (bo);
|
|
fb_args.height = gbm_bo_get_height (bo);
|
|
fb_args.format = gbm_bo_get_format (bo);
|
|
|
|
if (!meta_drm_buffer_do_ensure_fb_id (META_DRM_BUFFER (buffer_gbm),
|
|
&fb_args, error))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
lock_front_buffer (MetaDrmBufferGbm *buffer_gbm,
|
|
GError **error)
|
|
{
|
|
buffer_gbm->bo = gbm_surface_lock_front_buffer (buffer_gbm->surface);
|
|
if (!buffer_gbm->bo)
|
|
{
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_FAILED,
|
|
"gbm_surface_lock_front_buffer failed");
|
|
return FALSE;
|
|
}
|
|
|
|
return meta_drm_buffer_gbm_ensure_fb_id (META_DRM_BUFFER (buffer_gbm), error);
|
|
}
|
|
|
|
MetaDrmBufferGbm *
|
|
meta_drm_buffer_gbm_new_lock_front (MetaDeviceFile *device_file,
|
|
struct gbm_surface *gbm_surface,
|
|
MetaDrmBufferFlags flags,
|
|
GError **error)
|
|
{
|
|
MetaDrmBufferGbm *buffer_gbm;
|
|
|
|
buffer_gbm = g_object_new (META_TYPE_DRM_BUFFER_GBM,
|
|
"device-file", device_file,
|
|
"flags", flags,
|
|
NULL);
|
|
buffer_gbm->surface = gbm_surface;
|
|
|
|
if (!lock_front_buffer (buffer_gbm, error))
|
|
{
|
|
g_object_unref (buffer_gbm);
|
|
return NULL;
|
|
}
|
|
|
|
return buffer_gbm;
|
|
}
|
|
|
|
MetaDrmBufferGbm *
|
|
meta_drm_buffer_gbm_new_take (MetaDeviceFile *device_file,
|
|
struct gbm_bo *bo,
|
|
MetaDrmBufferFlags flags,
|
|
GError **error)
|
|
{
|
|
MetaDrmBufferGbm *buffer_gbm;
|
|
|
|
buffer_gbm = g_object_new (META_TYPE_DRM_BUFFER_GBM,
|
|
"device-file", device_file,
|
|
"flags", flags,
|
|
NULL);
|
|
buffer_gbm->bo = bo;
|
|
|
|
return buffer_gbm;
|
|
}
|
|
|
|
static gboolean
|
|
meta_drm_buffer_gbm_fill_timings (MetaDrmBuffer *buffer,
|
|
CoglFrameInfo *info,
|
|
GError **error)
|
|
{
|
|
MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (buffer);
|
|
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);
|
|
CoglDisplay *cogl_display = cogl_context->display;
|
|
CoglRenderer *cogl_renderer = cogl_display->renderer;
|
|
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
|
|
EGLDisplay egl_display = cogl_renderer_egl->edpy;
|
|
EGLImageKHR egl_image;
|
|
CoglPixelFormat cogl_format;
|
|
CoglEglImageFlags flags;
|
|
g_autoptr (CoglOffscreen) cogl_fbo = NULL;
|
|
CoglTexture2D *cogl_tex;
|
|
uint32_t n_planes;
|
|
uint64_t *modifiers;
|
|
uint32_t *strides;
|
|
uint32_t *offsets;
|
|
uint32_t width;
|
|
uint32_t height;
|
|
uint32_t drm_format;
|
|
int *fds;
|
|
gboolean result;
|
|
int dmabuf_fd = -1;
|
|
uint32_t i;
|
|
|
|
dmabuf_fd = gbm_bo_get_fd (buffer_gbm->bo);
|
|
if (dmabuf_fd == -1)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to export buffer's DMA fd: %s",
|
|
g_strerror (errno));
|
|
return FALSE;
|
|
}
|
|
|
|
drm_format = gbm_bo_get_format (buffer_gbm->bo);
|
|
result = meta_cogl_pixel_format_from_drm_format (drm_format,
|
|
&cogl_format,
|
|
NULL);
|
|
g_assert (result);
|
|
|
|
width = gbm_bo_get_width (buffer_gbm->bo);
|
|
height = gbm_bo_get_height (buffer_gbm->bo);
|
|
n_planes = gbm_bo_get_plane_count (buffer_gbm->bo);
|
|
fds = g_alloca (sizeof (int) * n_planes);
|
|
strides = g_alloca (sizeof (uint32_t) * n_planes);
|
|
offsets = g_alloca (sizeof (uint32_t) * n_planes);
|
|
modifiers = g_alloca (sizeof (uint64_t) * n_planes);
|
|
|
|
for (i = 0; i < n_planes; i++)
|
|
{
|
|
fds[i] = dmabuf_fd;
|
|
strides[i] = gbm_bo_get_stride_for_plane (buffer_gbm->bo, i);
|
|
offsets[i] = gbm_bo_get_offset (buffer_gbm->bo, i);
|
|
modifiers[i] = gbm_bo_get_modifier (buffer_gbm->bo);
|
|
}
|
|
|
|
egl_image = meta_egl_create_dmabuf_image (egl,
|
|
egl_display,
|
|
width,
|
|
height,
|
|
drm_format,
|
|
n_planes,
|
|
fds,
|
|
strides,
|
|
offsets,
|
|
modifiers,
|
|
error);
|
|
if (egl_image == EGL_NO_IMAGE_KHR)
|
|
goto out;
|
|
|
|
flags = COGL_EGL_IMAGE_FLAG_NO_GET_DATA;
|
|
cogl_tex = 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 (!cogl_tex)
|
|
goto out;
|
|
|
|
cogl_fbo = cogl_offscreen_new_with_texture (COGL_TEXTURE (cogl_tex));
|
|
cogl_object_unref (cogl_tex);
|
|
|
|
if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_GET_GPU_TIME))
|
|
{
|
|
info->gpu_time_before_buffer_swap_ns =
|
|
cogl_context_get_gpu_time_ns (cogl_context);
|
|
}
|
|
|
|
info->cpu_time_before_buffer_swap_us = g_get_monotonic_time ();
|
|
|
|
/* Set up a timestamp query for when all rendering will be finished. */
|
|
if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_TIMESTAMP_QUERY))
|
|
{
|
|
info->timestamp_query =
|
|
cogl_framebuffer_create_timestamp_query (COGL_FRAMEBUFFER (cogl_fbo));
|
|
}
|
|
|
|
out:
|
|
close (dmabuf_fd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
meta_drm_buffer_gbm_blit_to_framebuffer (CoglScanout *scanout,
|
|
CoglFramebuffer *framebuffer,
|
|
int x,
|
|
int y,
|
|
GError **error)
|
|
{
|
|
MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (scanout);
|
|
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);
|
|
CoglDisplay *cogl_display = cogl_context->display;
|
|
CoglRenderer *cogl_renderer = cogl_display->renderer;
|
|
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
|
|
EGLDisplay egl_display = cogl_renderer_egl->edpy;
|
|
EGLImageKHR egl_image;
|
|
CoglPixelFormat cogl_format;
|
|
CoglEglImageFlags flags;
|
|
CoglOffscreen *cogl_fbo = NULL;
|
|
CoglTexture2D *cogl_tex;
|
|
uint32_t n_planes;
|
|
uint64_t *modifiers;
|
|
uint32_t *strides;
|
|
uint32_t *offsets;
|
|
uint32_t width;
|
|
uint32_t height;
|
|
uint32_t drm_format;
|
|
int *fds;
|
|
gboolean result;
|
|
int dmabuf_fd = -1;
|
|
uint32_t i;
|
|
|
|
dmabuf_fd = gbm_bo_get_fd (buffer_gbm->bo);
|
|
if (dmabuf_fd == -1)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
|
|
"Failed to export buffer's DMA fd: %s",
|
|
g_strerror (errno));
|
|
return FALSE;
|
|
}
|
|
|
|
drm_format = gbm_bo_get_format (buffer_gbm->bo);
|
|
result = meta_cogl_pixel_format_from_drm_format (drm_format,
|
|
&cogl_format,
|
|
NULL);
|
|
g_assert (result);
|
|
|
|
width = gbm_bo_get_width (buffer_gbm->bo);
|
|
height = gbm_bo_get_height (buffer_gbm->bo);
|
|
n_planes = gbm_bo_get_plane_count (buffer_gbm->bo);
|
|
fds = g_alloca (sizeof (int) * n_planes);
|
|
strides = g_alloca (sizeof (uint32_t) * n_planes);
|
|
offsets = g_alloca (sizeof (uint32_t) * n_planes);
|
|
modifiers = g_alloca (sizeof (uint64_t) * n_planes);
|
|
|
|
for (i = 0; i < n_planes; i++)
|
|
{
|
|
fds[i] = dmabuf_fd;
|
|
strides[i] = gbm_bo_get_stride_for_plane (buffer_gbm->bo, i);
|
|
offsets[i] = gbm_bo_get_offset (buffer_gbm->bo, i);
|
|
modifiers[i] = gbm_bo_get_modifier (buffer_gbm->bo);
|
|
}
|
|
|
|
egl_image = meta_egl_create_dmabuf_image (egl,
|
|
egl_display,
|
|
width,
|
|
height,
|
|
drm_format,
|
|
n_planes,
|
|
fds,
|
|
strides,
|
|
offsets,
|
|
modifiers,
|
|
error);
|
|
if (egl_image == EGL_NO_IMAGE_KHR)
|
|
{
|
|
result = FALSE;
|
|
goto out;
|
|
}
|
|
|
|
flags = COGL_EGL_IMAGE_FLAG_NO_GET_DATA;
|
|
cogl_tex = 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 (!cogl_tex)
|
|
{
|
|
result = FALSE;
|
|
goto out;
|
|
}
|
|
|
|
cogl_fbo = cogl_offscreen_new_with_texture (COGL_TEXTURE (cogl_tex));
|
|
cogl_object_unref (cogl_tex);
|
|
|
|
if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (cogl_fbo), error))
|
|
{
|
|
result = FALSE;
|
|
goto out;
|
|
}
|
|
|
|
result = cogl_blit_framebuffer (COGL_FRAMEBUFFER (cogl_fbo),
|
|
framebuffer,
|
|
0, 0,
|
|
x, y,
|
|
width, height,
|
|
error);
|
|
|
|
out:
|
|
g_clear_object (&cogl_fbo);
|
|
close (dmabuf_fd);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
cogl_scanout_iface_init (CoglScanoutInterface *iface)
|
|
{
|
|
iface->blit_to_framebuffer = meta_drm_buffer_gbm_blit_to_framebuffer;
|
|
}
|
|
|
|
static void
|
|
meta_drm_buffer_gbm_finalize (GObject *object)
|
|
{
|
|
MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (object);
|
|
|
|
if (buffer_gbm->bo)
|
|
{
|
|
if (buffer_gbm->surface)
|
|
gbm_surface_release_buffer (buffer_gbm->surface, buffer_gbm->bo);
|
|
else
|
|
gbm_bo_destroy (buffer_gbm->bo);
|
|
}
|
|
|
|
G_OBJECT_CLASS (meta_drm_buffer_gbm_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
meta_drm_buffer_gbm_init (MetaDrmBufferGbm *buffer_gbm)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_drm_buffer_gbm_class_init (MetaDrmBufferGbmClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
MetaDrmBufferClass *buffer_class = META_DRM_BUFFER_CLASS (klass);
|
|
|
|
object_class->finalize = meta_drm_buffer_gbm_finalize;
|
|
|
|
buffer_class->export_fd = meta_drm_buffer_gbm_export_fd;
|
|
buffer_class->ensure_fb_id = meta_drm_buffer_gbm_ensure_fb_id;
|
|
buffer_class->get_width = meta_drm_buffer_gbm_get_width;
|
|
buffer_class->get_height = meta_drm_buffer_gbm_get_height;
|
|
buffer_class->get_stride = meta_drm_buffer_gbm_get_stride;
|
|
buffer_class->get_bpp = meta_drm_buffer_gbm_get_bpp;
|
|
buffer_class->get_format = meta_drm_buffer_gbm_get_format;
|
|
buffer_class->get_offset = meta_drm_buffer_gbm_get_offset;
|
|
buffer_class->get_modifier = meta_drm_buffer_gbm_get_modifier;
|
|
buffer_class->fill_timings = meta_drm_buffer_gbm_fill_timings;
|
|
}
|