2021-05-03 19:23:52 +02:00
|
|
|
/*
|
|
|
|
* 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
|
2023-08-07 11:50:23 +02:00
|
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
2021-05-03 19:23:52 +02:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "backends/native/meta-render-device-private.h"
|
|
|
|
|
|
|
|
#include "backends/meta-backend-private.h"
|
|
|
|
#include "backends/meta-egl.h"
|
2021-05-05 15:35:59 +02:00
|
|
|
#include "backends/native/meta-backend-native-types.h"
|
2021-05-05 15:40:23 +02:00
|
|
|
#include "backends/native/meta-drm-buffer-dumb.h"
|
2021-05-03 19:23:52 +02:00
|
|
|
|
|
|
|
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"))
|
render-device: Unmake the EGLContext after checking whether hw accelerated
When creating a render device, we create a temporary EGLContext where we
then query the `GL_RENDERER` string to check whether the renderer is any
of the known software renderers. After we're done, we destroy the
context and move on.
This should be fine as according to specification eglDestroyContext(),
with the context being actually destroyed a bit later when it's no
longer current, but mesa, when running RK3399 (Pinebook Pro), this
results in a crash in a future eglMakeCurrent():
#0 in dri_unbind_context () at ../src/gallium/frontends/dri/dri_context.c:266
#1 in driUnbindContext () at ../src/gallium/frontends/dri/dri_util.c:763
#2 in dri2_make_current () at ../src/egl/drivers/dri2/egl_dri2.c:1814
#3 in eglMakeCurrent () at ../src/egl/main/eglapi.c:907
...
We can avoid this, however, by calling eglMakeCurrent() with
EGL_NO_CONTEXT on the EGLDisplay, prior to destroying, effectively
avoiding the crash, so lets do that.
Related: https://gitlab.freedesktop.org/mesa/mesa/-/issues/7194
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2414
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2612>
2022-09-03 18:34:49 +02:00
|
|
|
goto out_current_context;
|
2021-05-03 19:23:52 +02:00
|
|
|
|
|
|
|
priv->is_hardware_rendering = TRUE;
|
|
|
|
|
render-device: Unmake the EGLContext after checking whether hw accelerated
When creating a render device, we create a temporary EGLContext where we
then query the `GL_RENDERER` string to check whether the renderer is any
of the known software renderers. After we're done, we destroy the
context and move on.
This should be fine as according to specification eglDestroyContext(),
with the context being actually destroyed a bit later when it's no
longer current, but mesa, when running RK3399 (Pinebook Pro), this
results in a crash in a future eglMakeCurrent():
#0 in dri_unbind_context () at ../src/gallium/frontends/dri/dri_context.c:266
#1 in driUnbindContext () at ../src/gallium/frontends/dri/dri_util.c:763
#2 in dri2_make_current () at ../src/egl/drivers/dri2/egl_dri2.c:1814
#3 in eglMakeCurrent () at ../src/egl/main/eglapi.c:907
...
We can avoid this, however, by calling eglMakeCurrent() with
EGL_NO_CONTEXT on the EGLDisplay, prior to destroying, effectively
avoiding the crash, so lets do that.
Related: https://gitlab.freedesktop.org/mesa/mesa/-/issues/7194
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2414
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2612>
2022-09-03 18:34:49 +02:00
|
|
|
out_current_context:
|
|
|
|
meta_egl_make_current (egl, priv->egl_display,
|
|
|
|
EGL_NO_SURFACE, EGL_NO_SURFACE,
|
|
|
|
EGL_NO_CONTEXT, NULL);
|
|
|
|
|
2021-05-03 19:23:52 +02:00
|
|
|
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] =
|
2023-06-28 14:02:43 +02:00
|
|
|
g_param_spec_object ("backend", NULL, NULL,
|
2021-05-03 19:23:52 +02:00
|
|
|
META_TYPE_BACKEND,
|
|
|
|
G_PARAM_READWRITE |
|
|
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
obj_props[PROP_DEVICE_FILE] =
|
2023-06-28 14:02:43 +02:00
|
|
|
g_param_spec_pointer ("device-file", NULL, NULL,
|
2021-05-03 19:23:52 +02:00
|
|
|
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)";
|
|
|
|
}
|
2021-05-05 15:35:59 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2021-05-05 15:38:22 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2021-05-05 15:40:23 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|