mutter/src/backends/native/meta-render-device.c

412 lines
12 KiB
C
Raw Normal View History

/*
* Copyright (C) 2021 Red Hat Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include "backends/native/meta-render-device-private.h"
#include "backends/meta-backend-private.h"
#include "backends/meta-egl.h"
#include "backends/native/meta-backend-native-types.h"
#include "backends/native/meta-drm-buffer-dumb.h"
enum
{
PROP_0,
PROP_BACKEND,
PROP_DEVICE_FILE,
N_PROPS
};
static GParamSpec *obj_props[N_PROPS];
typedef struct _MetaRenderDevicePrivate
{
MetaBackend *backend;
MetaDeviceFile *device_file;
EGLDisplay egl_display;
EGLConfig egl_config;
gboolean is_hardware_rendering;
} MetaRenderDevicePrivate;
static void
initable_iface_init (GInitableIface *initable_iface);
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MetaRenderDevice, meta_render_device,
G_TYPE_OBJECT,
G_ADD_PRIVATE (MetaRenderDevice)
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
initable_iface_init))
static EGLDisplay
meta_render_device_create_egl_display (MetaRenderDevice *render_device,
GError **error)
{
MetaRenderDeviceClass *klass = META_RENDER_DEVICE_GET_CLASS (render_device);
return klass->create_egl_display (render_device, error);
}
static void
detect_hardware_rendering (MetaRenderDevice *render_device)
{
MetaRenderDevicePrivate *priv =
meta_render_device_get_instance_private (render_device);
MetaEgl *egl = meta_backend_get_egl (priv->backend);
g_autoptr (GError) error = NULL;
EGLint *attributes;
EGLContext egl_context;
const char *renderer_str;
attributes = (EGLint[]) {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
egl_context = meta_egl_create_context (egl,
priv->egl_display,
EGL_NO_CONFIG_KHR,
EGL_NO_CONTEXT,
attributes,
&error);
if (egl_context == EGL_NO_CONTEXT)
{
meta_topic (META_DEBUG_RENDER, "Failed to create EGLContext for %s: %s",
meta_device_file_get_path (priv->device_file),
error->message);
return;
}
if (!meta_egl_make_current (egl,
priv->egl_display,
EGL_NO_SURFACE,
EGL_NO_SURFACE,
egl_context,
&error))
{
g_warning ("Failed to detect hardware rendering: eglMakeCurrent(): %s",
error->message);
goto out_has_context;
}
renderer_str = (const char *) glGetString (GL_RENDERER);
if (g_str_has_prefix (renderer_str, "llvmpipe") ||
g_str_has_prefix (renderer_str, "softpipe") ||
g_str_has_prefix (renderer_str, "swrast"))
goto out_current_context;
priv->is_hardware_rendering = TRUE;
out_current_context:
meta_egl_make_current (egl, priv->egl_display,
EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT, NULL);
out_has_context:
meta_egl_destroy_context (egl, priv->egl_display, egl_context, NULL);
}
static void
init_egl (MetaRenderDevice *render_device)
{
MetaRenderDevicePrivate *priv =
meta_render_device_get_instance_private (render_device);
MetaEgl *egl = meta_backend_get_egl (priv->backend);
g_autoptr (GError) error = NULL;
EGLDisplay egl_display;
meta_egl_bind_api (egl, EGL_OPENGL_ES_API, NULL);
egl_display = meta_render_device_create_egl_display (render_device, &error);
if (egl_display == EGL_NO_DISPLAY)
{
meta_topic (META_DEBUG_RENDER, "Failed to create EGLDisplay for %s: %s",
meta_device_file_get_path (priv->device_file),
error->message);
return;
}
priv->egl_display = egl_display;
detect_hardware_rendering (render_device);
}
static gboolean
meta_render_device_initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
MetaRenderDevice *render_device = META_RENDER_DEVICE (initable);
init_egl (render_device);
return TRUE;
}
static void
initable_iface_init (GInitableIface *initable_iface)
{
initable_iface->init = meta_render_device_initable_init;
}
static void
meta_render_device_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaRenderDevice *render_device = META_RENDER_DEVICE (object);
MetaRenderDevicePrivate *priv =
meta_render_device_get_instance_private (render_device);
switch (prop_id)
{
case PROP_BACKEND:
g_value_set_object (value, priv->backend);
break;
case PROP_DEVICE_FILE:
g_value_set_pointer (value, priv->device_file);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_render_device_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MetaRenderDevice *render_device = META_RENDER_DEVICE (object);
MetaRenderDevicePrivate *priv =
meta_render_device_get_instance_private (render_device);
switch (prop_id)
{
case PROP_BACKEND:
priv->backend = g_value_get_object (value);
break;
case PROP_DEVICE_FILE:
priv->device_file = g_value_get_pointer (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_render_device_dispose (GObject *object)
{
MetaRenderDevice *render_device = META_RENDER_DEVICE (object);
MetaRenderDevicePrivate *priv =
meta_render_device_get_instance_private (render_device);
MetaEgl *egl = meta_backend_get_egl (priv->backend);
if (priv->egl_display != EGL_NO_DISPLAY)
{
meta_egl_terminate (egl, priv->egl_display, NULL);
priv->egl_display = EGL_NO_DISPLAY;
}
G_OBJECT_CLASS (meta_render_device_parent_class)->dispose (object);
}
static void
meta_render_device_finalize (GObject *object)
{
MetaRenderDevice *render_device = META_RENDER_DEVICE (object);
MetaRenderDevicePrivate *priv =
meta_render_device_get_instance_private (render_device);
g_clear_pointer (&priv->device_file, meta_device_file_release);
G_OBJECT_CLASS (meta_render_device_parent_class)->finalize (object);
}
static void
meta_render_device_constructed (GObject *object)
{
MetaRenderDevice *render_device = META_RENDER_DEVICE (object);
MetaRenderDevicePrivate *priv =
meta_render_device_get_instance_private (render_device);
if (priv->device_file)
meta_device_file_acquire (priv->device_file);
G_OBJECT_CLASS (meta_render_device_parent_class)->constructed (object);
}
static void
meta_render_device_class_init (MetaRenderDeviceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = meta_render_device_get_property;
object_class->set_property = meta_render_device_set_property;
object_class->constructed = meta_render_device_constructed;
object_class->dispose = meta_render_device_dispose;
object_class->finalize = meta_render_device_finalize;
obj_props[PROP_BACKEND] =
g_param_spec_object ("backend", NULL, NULL,
META_TYPE_BACKEND,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_props[PROP_DEVICE_FILE] =
g_param_spec_pointer ("device-file", NULL, NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, obj_props);
}
static void
meta_render_device_init (MetaRenderDevice *render_device)
{
MetaRenderDevicePrivate *priv =
meta_render_device_get_instance_private (render_device);
priv->egl_display = EGL_NO_DISPLAY;
priv->egl_config = EGL_NO_CONFIG_KHR;
}
MetaBackend *
meta_render_device_get_backend (MetaRenderDevice *render_device)
{
MetaRenderDevicePrivate *priv =
meta_render_device_get_instance_private (render_device);
return priv->backend;
}
MetaDeviceFile *
meta_render_device_get_device_file (MetaRenderDevice *render_device)
{
MetaRenderDevicePrivate *priv =
meta_render_device_get_instance_private (render_device);
return priv->device_file;
}
EGLDisplay
meta_render_device_get_egl_display (MetaRenderDevice *render_device)
{
MetaRenderDevicePrivate *priv =
meta_render_device_get_instance_private (render_device);
return priv->egl_display;
}
gboolean
meta_render_device_is_hardware_accelerated (MetaRenderDevice *render_device)
{
MetaRenderDevicePrivate *priv =
meta_render_device_get_instance_private (render_device);
return priv->is_hardware_rendering;
}
const char *
meta_render_device_get_name (MetaRenderDevice *render_device)
{
MetaRenderDevicePrivate *priv =
meta_render_device_get_instance_private (render_device);
if (priv->device_file)
return meta_device_file_get_path (priv->device_file);
else
return "(device-less)";
}
MetaDrmBuffer *
meta_render_device_allocate_dma_buf (MetaRenderDevice *render_device,
int width,
int height,
uint32_t format,
MetaDrmBufferFlags flags,
GError **error)
{
MetaRenderDeviceClass *klass = META_RENDER_DEVICE_GET_CLASS (render_device);
if (klass->allocate_dma_buf)
{
return klass->allocate_dma_buf (render_device,
width, height, format,
flags,
error);
}
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Render device '%s' doesn't support allocating DMA buffers",
meta_render_device_get_name (render_device));
return NULL;
}
MetaDrmBuffer *
meta_render_device_import_dma_buf (MetaRenderDevice *render_device,
MetaDrmBuffer *buffer,
GError **error)
{
MetaRenderDeviceClass *klass = META_RENDER_DEVICE_GET_CLASS (render_device);
if (klass->import_dma_buf)
return klass->import_dma_buf (render_device, buffer, error);
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Render device '%s' doesn't importing DMA buffers",
meta_render_device_get_name (render_device));
return NULL;
}
MetaDrmBuffer *
meta_render_device_allocate_dumb_buf (MetaRenderDevice *render_device,
int width,
int height,
uint32_t format,
GError **error)
{
MetaRenderDevicePrivate *priv =
meta_render_device_get_instance_private (render_device);
MetaDrmBufferDumb *buffer_dumb;
if (!priv->device_file)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"No device file to allocate from");
return NULL;
}
buffer_dumb = meta_drm_buffer_dumb_new (priv->device_file,
width, height,
format,
error);
if (!buffer_dumb)
return NULL;
return META_DRM_BUFFER (buffer_dumb);
}