/* * 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 . * */ #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); }