mutter/src/backends/native/meta-renderer-native.c

3529 lines
114 KiB
C
Raw Normal View History

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2011 Intel Corporation.
* Copyright (C) 2016 Red Hat
* Copyright (c) 2018 DisplayLink (UK) Ltd.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Authors:
* Rob Bradford <rob@linux.intel.com> (from cogl-winsys-egl-kms.c)
* Kristian Høgsberg (from eglkms.c)
* Benjamin Franzke (from eglkms.c)
* Robert Bragg <robert@linux.intel.com> (from cogl-winsys-egl-kms.c)
* Neil Roberts <neil@linux.intel.com> (from cogl-winsys-egl-kms.c)
* Jonas Ådahl <jadahl@redhat.com>
*
*/
#include "config.h"
#include <drm_fourcc.h>
#include <errno.h>
#include <fcntl.h>
#include <gbm.h>
#include <gio/gio.h>
#include <glib-object.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <xf86drm.h>
#include "backends/meta-backend-private.h"
#include "backends/meta-crtc.h"
#include "backends/meta-egl-ext.h"
#include "backends/meta-egl.h"
#include "backends/meta-gles3.h"
#include "backends/meta-logical-monitor.h"
#include "backends/meta-output.h"
#include "backends/meta-renderer-view.h"
#include "backends/native/meta-crtc-kms.h"
#include "backends/native/meta-gpu-kms.h"
#include "backends/native/meta-monitor-manager-kms.h"
#include "backends/native/meta-renderer-native-gles3.h"
#include "backends/native/meta-renderer-native.h"
#include "cogl/cogl.h"
#include "core/boxes-private.h"
#ifndef EGL_DRM_MASTER_FD_EXT
#define EGL_DRM_MASTER_FD_EXT 0x333C
#endif
enum
{
PROP_0,
PROP_MONITOR_MANAGER,
PROP_LAST
};
static GParamSpec *obj_props[PROP_LAST];
typedef enum _MetaSharedFramebufferCopyMode
{
META_SHARED_FRAMEBUFFER_COPY_MODE_GPU,
META_SHARED_FRAMEBUFFER_COPY_MODE_CPU
} MetaSharedFramebufferCopyMode;
typedef struct _MetaRendererNativeGpuData
{
MetaRendererNative *renderer_native;
struct {
struct gbm_device *device;
} gbm;
#ifdef HAVE_EGL_DEVICE
struct {
EGLDeviceEXT device;
gboolean no_egl_output_drm_flip_event;
} egl;
#endif
MetaRendererNativeMode mode;
EGLDisplay egl_display;
/*
* Fields used for blitting iGPU framebuffer content onto dGPU framebuffers.
*/
struct {
MetaSharedFramebufferCopyMode copy_mode;
/* For GPU blit mode */
EGLContext egl_context;
EGLConfig egl_config;
} secondary;
} MetaRendererNativeGpuData;
typedef struct _MetaDumbBuffer
{
uint32_t fb_id;
uint32_t handle;
void *map;
uint64_t map_size;
int width;
int height;
int stride_bytes;
uint32_t drm_format;
} MetaDumbBuffer;
typedef struct _MetaOnscreenNativeSecondaryGpuState
{
MetaGpuKms *gpu_kms;
MetaRendererNativeGpuData *renderer_gpu_data;
EGLSurface egl_surface;
struct {
struct gbm_surface *surface;
uint32_t current_fb_id;
uint32_t next_fb_id;
struct gbm_bo *current_bo;
struct gbm_bo *next_bo;
} gbm;
struct {
MetaDumbBuffer *dumb_fb;
MetaDumbBuffer dumb_fbs[2];
} cpu;
int pending_flips;
} MetaOnscreenNativeSecondaryGpuState;
typedef struct _MetaOnscreenNative
{
MetaRendererNative *renderer_native;
MetaGpuKms *render_gpu;
MetaLogicalMonitor *logical_monitor;
GHashTable *secondary_gpu_states;
struct {
struct gbm_surface *surface;
uint32_t current_fb_id;
uint32_t next_fb_id;
struct gbm_bo *current_bo;
struct gbm_bo *next_bo;
} gbm;
#ifdef HAVE_EGL_DEVICE
struct {
EGLStreamKHR stream;
MetaDumbBuffer dumb_fb;
} egl;
#endif
gboolean pending_queue_swap_notify;
gboolean pending_swap_notify;
gboolean pending_set_crtc;
int64_t pending_queue_swap_notify_frame_count;
int64_t pending_swap_notify_frame_count;
MetaRendererView *view;
int total_pending_flips;
} MetaOnscreenNative;
struct _MetaRendererNative
{
MetaRenderer parent;
MetaMonitorManagerKms *monitor_manager_kms;
MetaGles3 *gles3;
gboolean use_modifiers;
GHashTable *gpu_datas;
CoglClosure *swap_notify_idle;
int64_t frame_counter;
gboolean pending_unset_disabled_crtcs;
};
static void
initable_iface_init (GInitableIface *initable_iface);
G_DEFINE_TYPE_WITH_CODE (MetaRendererNative,
meta_renderer_native,
META_TYPE_RENDERER,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
initable_iface_init))
static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable;
static const CoglWinsysVtable *parent_vtable;
static void
release_dumb_fb (MetaDumbBuffer *dumb_fb,
MetaGpuKms *gpu_kms);
static gboolean
init_dumb_fb (MetaDumbBuffer *dumb_fb,
MetaGpuKms *gpu_kms,
int width,
int height,
uint32_t format,
GError **error);
static MetaEgl *
meta_renderer_native_get_egl (MetaRendererNative *renderer_native);
static void
free_current_secondary_bo (MetaGpuKms *gpu_kms,
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state);
static void
free_next_secondary_bo (MetaGpuKms *gpu_kms,
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state);
static void
meta_renderer_native_gpu_data_free (MetaRendererNativeGpuData *renderer_gpu_data)
{
MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
if (renderer_gpu_data->egl_display != EGL_NO_DISPLAY)
meta_egl_terminate (egl, renderer_gpu_data->egl_display, NULL);
g_clear_pointer (&renderer_gpu_data->gbm.device, gbm_device_destroy);
g_free (renderer_gpu_data);
}
static MetaRendererNativeGpuData *
meta_renderer_native_get_gpu_data (MetaRendererNative *renderer_native,
MetaGpuKms *gpu_kms)
{
return g_hash_table_lookup (renderer_native->gpu_datas, gpu_kms);
}
static MetaRendererNative *
meta_renderer_native_from_gpu (MetaGpuKms *gpu_kms)
{
MetaMonitorManager *monitor_manager =
meta_gpu_get_monitor_manager (META_GPU (gpu_kms));
MetaBackend *backend = meta_monitor_manager_get_backend (monitor_manager);
return META_RENDERER_NATIVE (meta_backend_get_renderer (backend));
}
struct gbm_device *
meta_gbm_device_from_gpu (MetaGpuKms *gpu_kms)
{
MetaRendererNative *renderer_native = meta_renderer_native_from_gpu (gpu_kms);
MetaRendererNativeGpuData *renderer_gpu_data;
renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
gpu_kms);
return renderer_gpu_data->gbm.device;
}
static MetaRendererNativeGpuData *
meta_create_renderer_native_gpu_data (MetaGpuKms *gpu_kms)
{
return g_new0 (MetaRendererNativeGpuData, 1);
}
static MetaOnscreenNativeSecondaryGpuState *
get_secondary_gpu_state (CoglOnscreen *onscreen,
MetaGpuKms *gpu_kms)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
return g_hash_table_lookup (onscreen_native->secondary_gpu_states, gpu_kms);
}
static MetaEgl *
meta_renderer_native_get_egl (MetaRendererNative *renderer_native)
{
MetaMonitorManager *monitor_manager =
META_MONITOR_MANAGER (renderer_native->monitor_manager_kms);
MetaBackend *backend = meta_monitor_manager_get_backend (monitor_manager);
return meta_backend_get_egl (backend);
}
static MetaEgl *
meta_onscreen_native_get_egl (MetaOnscreenNative *onscreen_native)
{
return meta_renderer_native_get_egl (onscreen_native->renderer_native);
}
static GArray *
get_supported_kms_modifiers (CoglOnscreen *onscreen,
MetaGpu *gpu,
uint32_t format)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaLogicalMonitor *logical_monitor = onscreen_native->logical_monitor;
GArray *modifiers;
GArray *base_mods;
GList *l_crtc;
MetaCrtc *base_crtc = NULL;
GList *other_crtcs = NULL;
unsigned int i;
if (!logical_monitor)
return NULL;
/* Find our base CRTC to intersect against. */
for (l_crtc = meta_gpu_get_crtcs (gpu); l_crtc; l_crtc = l_crtc->next)
{
MetaCrtc *crtc = l_crtc->data;
if (crtc->logical_monitor != logical_monitor)
continue;
if (!base_crtc)
base_crtc = crtc;
else if (crtc == base_crtc)
continue;
else if (g_list_index (other_crtcs, crtc) == -1)
other_crtcs = g_list_append (other_crtcs, crtc);
}
if (!base_crtc)
goto out;
base_mods = meta_crtc_kms_get_modifiers (base_crtc, format);
if (!base_mods)
goto out;
/*
* If this is the only CRTC we have, we don't need to intersect the sets of
* modifiers.
*/
if (other_crtcs == NULL)
{
modifiers = g_array_sized_new (FALSE, FALSE, sizeof (uint64_t),
base_mods->len);
g_array_append_vals (modifiers, base_mods->data, base_mods->len);
return modifiers;
}
modifiers = g_array_new (FALSE, FALSE, sizeof (uint64_t));
/*
* For each modifier from base_crtc, check if it's available on all other
* CRTCs.
*/
for (i = 0; i < base_mods->len; i++)
{
uint64_t modifier = g_array_index (base_mods, uint64_t, i);
gboolean found_everywhere = TRUE;
GList *k;
/* Check if we have the same modifier available for all CRTCs. */
for (k = other_crtcs; k; k = k->next)
{
MetaCrtc *crtc = k->data;
GArray *crtc_mods;
unsigned int m;
gboolean found_here = FALSE;
if (crtc->logical_monitor != logical_monitor)
continue;
crtc_mods = meta_crtc_kms_get_modifiers (crtc, format);
if (!crtc_mods)
{
g_array_free (modifiers, TRUE);
goto out;
}
for (m = 0; m < crtc_mods->len; m++)
{
uint64_t local_mod = g_array_index (crtc_mods, uint64_t, m);
if (local_mod == modifier)
{
found_here = TRUE;
break;
}
}
if (!found_here)
{
found_everywhere = FALSE;
break;
}
}
if (found_everywhere)
g_array_append_val (modifiers, modifier);
}
if (modifiers->len == 0)
{
g_array_free (modifiers, TRUE);
goto out;
}
return modifiers;
out:
g_list_free (other_crtcs);
return NULL;
}
static GArray *
get_supported_egl_modifiers (CoglOnscreen *onscreen,
MetaGpu *gpu,
uint32_t format)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
MetaRendererNativeGpuData *renderer_gpu_data;
EGLint num_modifiers;
GArray *modifiers;
GError *error = NULL;
gboolean ret;
renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
META_GPU_KMS (gpu));
if (!meta_egl_has_extensions (egl, renderer_gpu_data->egl_display, NULL,
"EGL_EXT_image_dma_buf_import_modifiers",
NULL))
return NULL;
ret = meta_egl_query_dma_buf_modifiers (egl, renderer_gpu_data->egl_display,
format, 0, NULL, NULL,
&num_modifiers, NULL);
if (!ret || num_modifiers == 0)
return NULL;
modifiers = g_array_sized_new (FALSE, FALSE, sizeof (uint64_t),
num_modifiers);
ret = meta_egl_query_dma_buf_modifiers (egl, renderer_gpu_data->egl_display,
format, num_modifiers,
(EGLuint64KHR *) modifiers->data, NULL,
&num_modifiers, &error);
if (!ret)
{
g_warning ("Failed to query DMABUF modifiers: %s", error->message);
g_error_free (error);
g_array_free (modifiers, TRUE);
return NULL;
}
return modifiers;
}
static GArray *
get_supported_modifiers (CoglOnscreen *onscreen,
uint32_t format)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaLogicalMonitor *logical_monitor = onscreen_native->logical_monitor;
GArray *modifiers = NULL;
GArray *gpu_mods;
GList *l_monitor;
unsigned int i;
if (!logical_monitor)
return NULL;
/* Find our base CRTC to intersect against. */
for (l_monitor = meta_logical_monitor_get_monitors (logical_monitor);
l_monitor;
l_monitor = l_monitor->next)
{
MetaMonitor *monitor = l_monitor->data;
MetaGpu *gpu = meta_monitor_get_gpu (monitor);
if (gpu == META_GPU (onscreen_native->render_gpu))
gpu_mods = get_supported_kms_modifiers (onscreen, gpu, format);
else
gpu_mods = get_supported_egl_modifiers (onscreen, gpu, format);
if (!gpu_mods)
{
g_array_free (modifiers, TRUE);
return NULL;
}
if (!modifiers)
{
modifiers = gpu_mods;
continue;
}
for (i = 0; i < modifiers->len; i++)
{
uint64_t modifier = g_array_index (modifiers, uint64_t, i);
gboolean found = FALSE;
unsigned int m;
for (m = 0; m < gpu_mods->len; m++)
{
uint64_t gpu_mod = g_array_index (gpu_mods, uint64_t, m);
if (gpu_mod == modifier)
{
found = TRUE;
break;
}
}
if (!found)
{
g_array_remove_index_fast (modifiers, i);
i--;
}
}
g_array_free (gpu_mods, TRUE);
}
if (modifiers && modifiers->len == 0)
{
g_array_free (modifiers, TRUE);
return NULL;
}
return modifiers;
}
static gboolean
init_secondary_gpu_state_gpu_copy_mode (MetaRendererNative *renderer_native,
CoglOnscreen *onscreen,
MetaRendererNativeGpuData *renderer_gpu_data,
MetaGpuKms *gpu_kms,
GError **error)
{
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
int width, height;
EGLNativeWindowType egl_native_window;
struct gbm_surface *gbm_surface;
EGLSurface egl_surface;
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
width = cogl_framebuffer_get_width (framebuffer);
height = cogl_framebuffer_get_height (framebuffer);
gbm_surface = gbm_surface_create (renderer_gpu_data->gbm.device,
width, height,
GBM_FORMAT_XRGB8888,
GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
if (!gbm_surface)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to create gbm_surface: %s", strerror (errno));
return FALSE;
}
egl_native_window = (EGLNativeWindowType) gbm_surface;
egl_surface =
meta_egl_create_window_surface (egl,
renderer_gpu_data->egl_display,
renderer_gpu_data->secondary.egl_config,
egl_native_window,
NULL,
error);
if (egl_surface == EGL_NO_SURFACE)
{
gbm_surface_destroy (gbm_surface);
return FALSE;
}
secondary_gpu_state = g_new0 (MetaOnscreenNativeSecondaryGpuState, 1);
secondary_gpu_state->gpu_kms = gpu_kms;
secondary_gpu_state->renderer_gpu_data = renderer_gpu_data;
secondary_gpu_state->gbm.surface = gbm_surface;
secondary_gpu_state->egl_surface = egl_surface;
g_hash_table_insert (onscreen_native->secondary_gpu_states,
gpu_kms, secondary_gpu_state);
return TRUE;
}
static void
secondary_gpu_state_free (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
{
MetaBackend *backend = meta_get_backend ();
MetaEgl *egl = meta_backend_get_egl (backend);
MetaGpuKms *gpu_kms = secondary_gpu_state->gpu_kms;
unsigned int i;
if (secondary_gpu_state->egl_surface != EGL_NO_SURFACE)
{
MetaRendererNativeGpuData *renderer_gpu_data;
renderer_gpu_data = secondary_gpu_state->renderer_gpu_data;
meta_egl_destroy_surface (egl,
renderer_gpu_data->egl_display,
secondary_gpu_state->egl_surface,
NULL);
}
free_current_secondary_bo (gpu_kms, secondary_gpu_state);
free_next_secondary_bo (gpu_kms, secondary_gpu_state);
g_clear_pointer (&secondary_gpu_state->gbm.surface, gbm_surface_destroy);
for (i = 0; i < G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); i++)
{
MetaDumbBuffer *dumb_fb = &secondary_gpu_state->cpu.dumb_fbs[i];
if (dumb_fb->fb_id)
release_dumb_fb (dumb_fb, gpu_kms);
}
g_free (secondary_gpu_state);
}
static gboolean
init_secondary_gpu_state_cpu_copy_mode (MetaRendererNative *renderer_native,
CoglOnscreen *onscreen,
MetaRendererNativeGpuData *renderer_gpu_data,
MetaGpuKms *gpu_kms,
GError **error)
{
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
int width, height;
unsigned int i;
width = cogl_framebuffer_get_width (framebuffer);
height = cogl_framebuffer_get_height (framebuffer);
secondary_gpu_state = g_new0 (MetaOnscreenNativeSecondaryGpuState, 1);
secondary_gpu_state->renderer_gpu_data = renderer_gpu_data;
secondary_gpu_state->gpu_kms = gpu_kms;
secondary_gpu_state->egl_surface = EGL_NO_SURFACE;
for (i = 0; i < G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); i++)
{
MetaDumbBuffer *dumb_fb = &secondary_gpu_state->cpu.dumb_fbs[i];
if (!init_dumb_fb (dumb_fb,
gpu_kms,
width, height,
DRM_FORMAT_XBGR8888,
error))
{
secondary_gpu_state_free (secondary_gpu_state);
return FALSE;
}
}
g_hash_table_insert (onscreen_native->secondary_gpu_states,
gpu_kms, secondary_gpu_state);
return TRUE;
}
static gboolean
init_secondary_gpu_state (MetaRendererNative *renderer_native,
CoglOnscreen *onscreen,
MetaGpuKms *gpu_kms,
GError **error)
{
MetaRendererNativeGpuData *renderer_gpu_data;
renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
gpu_kms);
switch (renderer_gpu_data->secondary.copy_mode)
{
case META_SHARED_FRAMEBUFFER_COPY_MODE_GPU:
if (!init_secondary_gpu_state_gpu_copy_mode (renderer_native,
onscreen,
renderer_gpu_data,
gpu_kms,
error))
return FALSE;
break;
case META_SHARED_FRAMEBUFFER_COPY_MODE_CPU:
if (!init_secondary_gpu_state_cpu_copy_mode (renderer_native,
onscreen,
renderer_gpu_data,
gpu_kms,
error))
return FALSE;
break;
}
return TRUE;
}
static void
meta_renderer_native_disconnect (CoglRenderer *cogl_renderer)
{
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
g_slice_free (CoglRendererEGL, cogl_renderer_egl);
}
static void
flush_pending_swap_notify (CoglFramebuffer *framebuffer)
{
if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
{
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
if (onscreen_native->pending_swap_notify)
{
CoglFrameInfo *info;
while ((info = g_queue_peek_head (&onscreen->pending_frame_infos)) &&
info->global_frame_counter <= onscreen_native->pending_swap_notify_frame_count)
{
_cogl_onscreen_notify_frame_sync (onscreen, info);
_cogl_onscreen_notify_complete (onscreen, info);
cogl_object_unref (info);
g_queue_pop_head (&onscreen->pending_frame_infos);
}
onscreen_native->pending_swap_notify = FALSE;
cogl_object_unref (onscreen);
}
}
}
static void
flush_pending_swap_notify_idle (void *user_data)
{
CoglContext *cogl_context = user_data;
CoglRendererEGL *cogl_renderer_egl = cogl_context->display->renderer->winsys;
MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
GList *l;
/* This needs to be disconnected before invoking the callbacks in
* case the callbacks cause it to be queued again */
_cogl_closure_disconnect (renderer_native->swap_notify_idle);
renderer_native->swap_notify_idle = NULL;
l = cogl_context->framebuffers;
while (l)
{
GList *next = l->next;
CoglFramebuffer *framebuffer = l->data;
flush_pending_swap_notify (framebuffer);
l = next;
}
}
static void
free_current_secondary_bo (MetaGpuKms *gpu_kms,
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
{
MetaRendererNativeGpuData *renderer_gpu_data;
int kms_fd;
kms_fd = meta_gpu_kms_get_fd (gpu_kms);
renderer_gpu_data = secondary_gpu_state->renderer_gpu_data;
switch (renderer_gpu_data->secondary.copy_mode)
{
case META_SHARED_FRAMEBUFFER_COPY_MODE_GPU:
if (secondary_gpu_state->gbm.current_fb_id)
{
drmModeRmFB (kms_fd, secondary_gpu_state->gbm.current_fb_id);
secondary_gpu_state->gbm.current_fb_id = 0;
}
if (secondary_gpu_state->gbm.current_bo)
{
gbm_surface_release_buffer (secondary_gpu_state->gbm.surface,
secondary_gpu_state->gbm.current_bo);
secondary_gpu_state->gbm.current_bo = NULL;
}
break;
case META_SHARED_FRAMEBUFFER_COPY_MODE_CPU:
break;
}
}
static void
free_current_bo (CoglOnscreen *onscreen)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaGpuKms *render_gpu = onscreen_native->render_gpu;
int kms_fd;
kms_fd = meta_gpu_kms_get_fd (render_gpu);
if (onscreen_native->gbm.current_fb_id)
{
drmModeRmFB (kms_fd, onscreen_native->gbm.current_fb_id);
onscreen_native->gbm.current_fb_id = 0;
}
if (onscreen_native->gbm.current_bo)
{
gbm_surface_release_buffer (onscreen_native->gbm.surface,
onscreen_native->gbm.current_bo);
onscreen_native->gbm.current_bo = NULL;
}
g_hash_table_foreach (onscreen_native->secondary_gpu_states,
(GHFunc) free_current_secondary_bo,
NULL);
}
static void
meta_onscreen_native_queue_swap_notify (CoglOnscreen *onscreen)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
onscreen_native->pending_swap_notify_frame_count =
onscreen_native->pending_queue_swap_notify_frame_count;
if (onscreen_native->pending_swap_notify)
return;
/* We only want to notify that the swap is complete when the
* application calls cogl_context_dispatch so instead of
* immediately notifying we queue an idle callback */
if (!renderer_native->swap_notify_idle)
{
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglContext *cogl_context = framebuffer->context;
CoglRenderer *cogl_renderer = cogl_context->display->renderer;
renderer_native->swap_notify_idle =
_cogl_poll_renderer_add_idle (cogl_renderer,
flush_pending_swap_notify_idle,
cogl_context,
NULL);
}
/*
* The framebuffer will have its own referenc while the swap notify is
* pending. Otherwise when destroying the view would drop the pending
* notification with if the destruction happens before the idle callback
* is invoked.
*/
cogl_object_ref (onscreen);
onscreen_native->pending_swap_notify = TRUE;
}
static gboolean
meta_renderer_native_connect (CoglRenderer *cogl_renderer,
GError **error)
{
CoglRendererEGL *cogl_renderer_egl;
MetaGpuKms *gpu_kms = cogl_renderer->custom_winsys_user_data;
MetaRendererNative *renderer_native = meta_renderer_native_from_gpu (gpu_kms);
MetaRendererNativeGpuData *renderer_gpu_data;
cogl_renderer->winsys = g_slice_new0 (CoglRendererEGL);
cogl_renderer_egl = cogl_renderer->winsys;
renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
gpu_kms);
cogl_renderer_egl->platform_vtable = &_cogl_winsys_egl_vtable;
cogl_renderer_egl->platform = renderer_gpu_data;
cogl_renderer_egl->edpy = renderer_gpu_data->egl_display;
if (!_cogl_winsys_egl_renderer_connect_common (cogl_renderer, error))
goto fail;
return TRUE;
fail:
meta_renderer_native_disconnect (cogl_renderer);
return FALSE;
}
static int
meta_renderer_native_add_egl_config_attributes (CoglDisplay *cogl_display,
CoglFramebufferConfig *config,
EGLint *attributes)
{
CoglRendererEGL *cogl_renderer_egl = cogl_display->renderer->winsys;
MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
int i = 0;
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
attributes[i++] = EGL_SURFACE_TYPE;
attributes[i++] = EGL_WINDOW_BIT;
break;
#ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
attributes[i++] = EGL_SURFACE_TYPE;
attributes[i++] = EGL_STREAM_BIT_KHR;
break;
#endif
}
return i;
}
static gboolean
choose_egl_config_from_gbm_format (MetaEgl *egl,
EGLDisplay egl_display,
const EGLint *attributes,
uint32_t gbm_format,
EGLConfig *out_config,
GError **error)
{
EGLConfig *egl_configs;
EGLint n_configs;
EGLint i;
egl_configs = meta_egl_choose_all_configs (egl, egl_display,
attributes,
&n_configs,
error);
if (!egl_configs)
return FALSE;
for (i = 0; i < n_configs; i++)
{
EGLint visual_id;
if (!meta_egl_get_config_attrib (egl, egl_display,
egl_configs[i],
EGL_NATIVE_VISUAL_ID,
&visual_id,
error))
{
g_free (egl_configs);
return FALSE;
}
if ((uint32_t) visual_id == gbm_format)
{
*out_config = egl_configs[i];
g_free (egl_configs);
return TRUE;
}
}
g_free (egl_configs);
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"No EGL config matching supported GBM format found");
return FALSE;
}
static gboolean
meta_renderer_native_choose_egl_config (CoglDisplay *cogl_display,
EGLint *attributes,
EGLConfig *out_config,
GError **error)
{
CoglRenderer *cogl_renderer = cogl_display->renderer;
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
MetaBackend *backend = meta_get_backend ();
MetaEgl *egl = meta_backend_get_egl (backend);
MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
EGLDisplay egl_display = cogl_renderer_egl->edpy;
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
return choose_egl_config_from_gbm_format (egl,
egl_display,
attributes,
GBM_FORMAT_XRGB8888,
out_config,
error);
#ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
return meta_egl_choose_first_config (egl,
egl_display,
attributes,
out_config,
error);
#endif
}
return FALSE;
}
static gboolean
meta_renderer_native_setup_egl_display (CoglDisplay *cogl_display,
GError **error)
{
CoglDisplayEGL *cogl_display_egl = cogl_display->winsys;
CoglRendererEGL *cogl_renderer_egl = cogl_display->renderer->winsys;
MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
cogl_display_egl->platform = renderer_native;
/* Force a full modeset / drmModeSetCrtc on
* the first swap buffers call.
*/
meta_renderer_native_queue_modes_reset (renderer_native);
return TRUE;
}
static void
meta_renderer_native_destroy_egl_display (CoglDisplay *cogl_display)
{
}
static EGLSurface
create_dummy_pbuffer_surface (EGLDisplay egl_display,
GError **error)
{
MetaBackend *backend = meta_get_backend ();
MetaEgl *egl = meta_backend_get_egl (backend);
EGLConfig pbuffer_config;
static const EGLint pbuffer_config_attribs[] = {
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 0,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
static const EGLint pbuffer_attribs[] = {
EGL_WIDTH, 16,
EGL_HEIGHT, 16,
EGL_NONE
};
if (!meta_egl_choose_first_config (egl, egl_display, pbuffer_config_attribs,
&pbuffer_config, error))
return EGL_NO_SURFACE;
return meta_egl_create_pbuffer_surface (egl, egl_display,
pbuffer_config, pbuffer_attribs,
error);
}
static gboolean
meta_renderer_native_egl_context_created (CoglDisplay *cogl_display,
GError **error)
{
CoglDisplayEGL *cogl_display_egl = cogl_display->winsys;
CoglRenderer *cogl_renderer = cogl_display->renderer;
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
if ((cogl_renderer_egl->private_features &
COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT) == 0)
{
cogl_display_egl->dummy_surface =
create_dummy_pbuffer_surface (cogl_renderer_egl->edpy, error);
if (cogl_display_egl->dummy_surface == EGL_NO_SURFACE)
return FALSE;
}
if (!_cogl_winsys_egl_make_current (cogl_display,
cogl_display_egl->dummy_surface,
cogl_display_egl->dummy_surface,
cogl_display_egl->egl_context))
{
_cogl_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_CREATE_CONTEXT,
"Failed to make context current");
return FALSE;
}
return TRUE;
}
static void
meta_renderer_native_egl_cleanup_context (CoglDisplay *cogl_display)
{
CoglDisplayEGL *cogl_display_egl = cogl_display->winsys;
CoglRenderer *cogl_renderer = cogl_display->renderer;
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
if (cogl_display_egl->dummy_surface != EGL_NO_SURFACE)
{
meta_egl_destroy_surface (egl,
cogl_renderer_egl->edpy,
cogl_display_egl->dummy_surface,
NULL);
cogl_display_egl->dummy_surface = EGL_NO_SURFACE;
}
}
static void
swap_secondary_drm_fb (MetaGpuKms *gpu_kms,
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
{
secondary_gpu_state->gbm.current_fb_id = secondary_gpu_state->gbm.next_fb_id;
secondary_gpu_state->gbm.next_fb_id = 0;
secondary_gpu_state->gbm.current_bo = secondary_gpu_state->gbm.next_bo;
secondary_gpu_state->gbm.next_bo = NULL;
}
static void
meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
free_current_bo (onscreen);
onscreen_native->gbm.current_fb_id = onscreen_native->gbm.next_fb_id;
onscreen_native->gbm.next_fb_id = 0;
onscreen_native->gbm.current_bo = onscreen_native->gbm.next_bo;
onscreen_native->gbm.next_bo = NULL;
g_hash_table_foreach (onscreen_native->secondary_gpu_states,
(GHFunc) swap_secondary_drm_fb,
NULL);
}
static void
on_crtc_flipped (GClosure *closure,
MetaGpuKms *gpu_kms,
MetaRendererView *view)
{
ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view);
CoglFramebuffer *framebuffer =
clutter_stage_view_get_onscreen (stage_view);
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
MetaGpuKms *render_gpu = onscreen_native->render_gpu;
if (gpu_kms != render_gpu)
{
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
secondary_gpu_state = get_secondary_gpu_state (onscreen, gpu_kms);
secondary_gpu_state->pending_flips--;
}
onscreen_native->total_pending_flips--;
if (onscreen_native->total_pending_flips == 0)
{
MetaRendererNativeGpuData *renderer_gpu_data;
onscreen_native->pending_queue_swap_notify = FALSE;
meta_onscreen_native_queue_swap_notify (onscreen);
renderer_gpu_data =
meta_renderer_native_get_gpu_data (renderer_native,
onscreen_native->render_gpu);
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
meta_onscreen_native_swap_drm_fb (onscreen);
break;
#ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
break;
#endif
}
}
}
static void
free_next_secondary_bo (MetaGpuKms *gpu_kms,
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
{
MetaRendererNativeGpuData *renderer_gpu_data;
renderer_gpu_data = secondary_gpu_state->renderer_gpu_data;
switch (renderer_gpu_data->secondary.copy_mode)
{
case META_SHARED_FRAMEBUFFER_COPY_MODE_GPU:
if (secondary_gpu_state->gbm.next_fb_id)
{
int kms_fd;
kms_fd = meta_gpu_kms_get_fd (gpu_kms);
drmModeRmFB (kms_fd, secondary_gpu_state->gbm.next_fb_id);
gbm_surface_release_buffer (secondary_gpu_state->gbm.surface,
secondary_gpu_state->gbm.next_bo);
secondary_gpu_state->gbm.next_fb_id = 0;
secondary_gpu_state->gbm.next_bo = NULL;
}
break;
case META_SHARED_FRAMEBUFFER_COPY_MODE_CPU:
break;
}
}
static void
flip_closure_destroyed (MetaRendererView *view)
{
ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view);
CoglFramebuffer *framebuffer =
clutter_stage_view_get_onscreen (stage_view);
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
MetaGpuKms *render_gpu = onscreen_native->render_gpu;
MetaRendererNativeGpuData *renderer_gpu_data;
renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
render_gpu);
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
if (onscreen_native->gbm.next_fb_id)
{
int kms_fd;
kms_fd = meta_gpu_kms_get_fd (render_gpu);
drmModeRmFB (kms_fd, onscreen_native->gbm.next_fb_id);
gbm_surface_release_buffer (onscreen_native->gbm.surface,
onscreen_native->gbm.next_bo);
onscreen_native->gbm.next_bo = NULL;
onscreen_native->gbm.next_fb_id = 0;
}
g_hash_table_foreach (onscreen_native->secondary_gpu_states,
(GHFunc) free_next_secondary_bo,
NULL);
break;
#ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
break;
#endif
}
if (onscreen_native->pending_queue_swap_notify)
{
meta_onscreen_native_queue_swap_notify (onscreen);
onscreen_native->pending_queue_swap_notify = FALSE;
}
g_object_unref (view);
}
#ifdef HAVE_EGL_DEVICE
static gboolean
flip_egl_stream (MetaOnscreenNative *onscreen_native,
GClosure *flip_closure)
{
MetaRendererNativeGpuData *renderer_gpu_data;
EGLDisplay *egl_display;
MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
MetaGpuKmsFlipClosureContainer *closure_container;
EGLAttrib *acquire_attribs;
GError *error = NULL;
renderer_gpu_data =
meta_renderer_native_get_gpu_data (onscreen_native->renderer_native,
onscreen_native->render_gpu);
if (renderer_gpu_data->egl.no_egl_output_drm_flip_event)
return FALSE;
closure_container =
meta_gpu_kms_wrap_flip_closure (onscreen_native->render_gpu, flip_closure);
acquire_attribs = (EGLAttrib[]) {
EGL_DRM_FLIP_EVENT_DATA_NV,
(EGLAttrib) closure_container,
EGL_NONE
};
egl_display = renderer_gpu_data->egl_display;
if (!meta_egl_stream_consumer_acquire_attrib (egl,
egl_display,
onscreen_native->egl.stream,
acquire_attribs,
&error))
{
if (error->domain != META_EGL_ERROR ||
error->code != EGL_RESOURCE_BUSY_EXT)
{
g_warning ("Failed to flip EGL stream (%s), relying on clock from "
"now on", error->message);
renderer_gpu_data->egl.no_egl_output_drm_flip_event = TRUE;
}
g_error_free (error);
meta_gpu_kms_flip_closure_container_free (closure_container);
return FALSE;
}
g_closure_ref (flip_closure);
return TRUE;
}
#endif /* HAVE_EGL_DEVICE */
static void
meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
GClosure *flip_closure,
MetaCrtc *crtc,
int x,
int y,
gboolean *fb_in_use)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
MetaGpuKms *render_gpu = onscreen_native->render_gpu;
MetaRendererNativeGpuData *renderer_gpu_data;
MetaGpuKms *gpu_kms;
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state = NULL;
uint32_t fb_id;
gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
if (!meta_gpu_kms_is_crtc_active (gpu_kms, crtc))
{
*fb_in_use = FALSE;
return;
}
renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
render_gpu);
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
if (gpu_kms == render_gpu)
{
fb_id = onscreen_native->gbm.next_fb_id;
}
else
{
secondary_gpu_state = get_secondary_gpu_state (onscreen, gpu_kms);
fb_id = secondary_gpu_state->gbm.next_fb_id;
}
if (!meta_gpu_kms_flip_crtc (gpu_kms,
crtc,
x, y,
fb_id,
flip_closure,
fb_in_use))
return;
onscreen_native->total_pending_flips++;
if (secondary_gpu_state)
secondary_gpu_state->pending_flips++;
break;
#ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
if (flip_egl_stream (onscreen_native,
flip_closure))
onscreen_native->total_pending_flips++;
*fb_in_use = TRUE;
break;
#endif
}
}
typedef struct _SetCrtcFbData
{
MetaGpuKms *render_gpu;
CoglOnscreen *onscreen;
uint32_t fb_id;
} SetCrtcFbData;
static void
set_crtc_fb (MetaLogicalMonitor *logical_monitor,
MetaCrtc *crtc,
gpointer user_data)
{
SetCrtcFbData *data = user_data;
MetaGpuKms *render_gpu = data->render_gpu;
CoglOnscreen *onscreen = data->onscreen;
MetaGpuKms *gpu_kms;
uint32_t fb_id;
int x, y;
gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
if (gpu_kms == render_gpu)
{
fb_id = data->fb_id;
}
else
{
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
secondary_gpu_state = get_secondary_gpu_state (onscreen, gpu_kms);
if (!secondary_gpu_state)
return;
fb_id = secondary_gpu_state->gbm.next_fb_id;
}
x = crtc->rect.x - logical_monitor->rect.x;
y = crtc->rect.y - logical_monitor->rect.y;
meta_gpu_kms_apply_crtc_mode (gpu_kms, crtc, x, y, fb_id);
}
static void
meta_onscreen_native_set_crtc_modes (CoglOnscreen *onscreen)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
MetaGpuKms *render_gpu = onscreen_native->render_gpu;
MetaRendererNativeGpuData *renderer_gpu_data;
MetaRendererView *view = onscreen_native->view;
uint32_t fb_id = 0;
MetaLogicalMonitor *logical_monitor;
renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
render_gpu);
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
fb_id = onscreen_native->gbm.next_fb_id;
break;
#ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
fb_id = onscreen_native->egl.dumb_fb.fb_id;
break;
#endif
}
g_assert (fb_id != 0);
logical_monitor = meta_renderer_view_get_logical_monitor (view);
if (logical_monitor)
{
SetCrtcFbData data = {
.render_gpu = render_gpu,
.onscreen = onscreen,
.fb_id = fb_id
};
meta_logical_monitor_foreach_crtc (logical_monitor,
set_crtc_fb,
&data);
}
else
{
GList *l;
for (l = meta_gpu_get_crtcs (META_GPU (render_gpu)); l; l = l->next)
{
MetaCrtc *crtc = l->data;
meta_gpu_kms_apply_crtc_mode (render_gpu,
crtc,
crtc->rect.x, crtc->rect.y,
fb_id);
}
}
}
typedef struct _FlipCrtcData
{
CoglOnscreen *onscreen;
GClosure *flip_closure;
gboolean out_fb_in_use;
} FlipCrtcData;
static void
flip_crtc (MetaLogicalMonitor *logical_monitor,
MetaCrtc *crtc,
gpointer user_data)
{
FlipCrtcData *data = user_data;
int x, y;
x = crtc->rect.x - logical_monitor->rect.x;
y = crtc->rect.y - logical_monitor->rect.y;
meta_onscreen_native_flip_crtc (data->onscreen,
data->flip_closure,
crtc, x, y,
&data->out_fb_in_use);
}
static void
meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaGpuKms *render_gpu = onscreen_native->render_gpu;
MetaRendererView *view = onscreen_native->view;
GClosure *flip_closure;
MetaLogicalMonitor *logical_monitor;
gboolean fb_in_use = FALSE;
/*
* Create a closure that either will be invoked or destructed.
* Invoking the closure represents a completed flip. If the closure
* is destructed before being invoked, the framebuffer references will be
* cleaned up accordingly.
*
* Each successful flip will each own one reference to the closure, thus keep
* it alive until either invoked or destructed. If flipping failed, the
* closure will be destructed before this function goes out of scope.
*/
flip_closure = g_cclosure_new (G_CALLBACK (on_crtc_flipped),
g_object_ref (view),
(GClosureNotify) flip_closure_destroyed);
g_closure_set_marshal (flip_closure, g_cclosure_marshal_VOID__OBJECT);
/* Either flip the CRTC's of the monitor info, if we are drawing just part
* of the stage, or all of the CRTC's if we are drawing the whole stage.
*/
logical_monitor = meta_renderer_view_get_logical_monitor (view);
if (logical_monitor)
{
FlipCrtcData data = {
.onscreen = onscreen,
.flip_closure = flip_closure,
};
meta_logical_monitor_foreach_crtc (logical_monitor,
flip_crtc,
&data);
fb_in_use = data.out_fb_in_use;
}
else
{
GList *l;
for (l = meta_gpu_get_crtcs (META_GPU (render_gpu)); l; l = l->next)
{
MetaCrtc *crtc = l->data;
meta_onscreen_native_flip_crtc (onscreen, flip_closure,
crtc, crtc->rect.x, crtc->rect.y,
&fb_in_use);
}
}
/*
* If the framebuffer is in use, but we don't have any pending flips it means
* that flipping is not supported and we set the next framebuffer directly.
* Since we won't receive a flip callback, lets just notify listeners
* directly.
*/
if (fb_in_use && onscreen_native->total_pending_flips == 0)
{
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
MetaRendererNativeGpuData *renderer_gpu_data;
renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
render_gpu);
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
meta_onscreen_native_swap_drm_fb (onscreen);
break;
#ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
break;
#endif
}
}
onscreen_native->pending_queue_swap_notify = TRUE;
g_closure_unref (flip_closure);
}
static gboolean
gbm_get_next_fb_id (MetaGpuKms *gpu_kms,
struct gbm_surface *gbm_surface,
struct gbm_bo **out_next_bo,
uint32_t *out_next_fb_id)
{
MetaRendererNative *renderer_native = meta_renderer_native_from_gpu (gpu_kms);
struct gbm_bo *next_bo;
uint32_t next_fb_id;
int kms_fd;
uint32_t handles[4] = { 0, };
uint32_t strides[4] = { 0, };
uint32_t offsets[4] = { 0, };
uint64_t modifiers[4] = { 0, };
int i;
/* Now we need to set the CRTC to whatever is the front buffer */
next_bo = gbm_surface_lock_front_buffer (gbm_surface);
if (!next_bo)
{
g_error ("Impossible to lock surface front buffer: %m");
return FALSE;
}
if (gbm_bo_get_handle_for_plane (next_bo, 0).s32 == -1)
{
/* Failed to fetch handle to plane, falling back to old method */
strides[0] = gbm_bo_get_stride (next_bo);
handles[0] = gbm_bo_get_handle (next_bo).u32;
offsets[0] = 0;
modifiers[0] = DRM_FORMAT_MOD_INVALID;
}
else
{
for (i = 0; i < gbm_bo_get_plane_count (next_bo); i++)
{
strides[i] = gbm_bo_get_stride_for_plane (next_bo, i);
handles[i] = gbm_bo_get_handle_for_plane (next_bo, i).u32;
offsets[i] = gbm_bo_get_offset (next_bo, i);
modifiers[i] = gbm_bo_get_modifier (next_bo);
}
}
kms_fd = meta_gpu_kms_get_fd (gpu_kms);
if (renderer_native->use_modifiers &&
modifiers[0] != DRM_FORMAT_MOD_INVALID)
{
if (drmModeAddFB2WithModifiers (kms_fd,
gbm_bo_get_width (next_bo),
gbm_bo_get_height (next_bo),
gbm_bo_get_format (next_bo),
handles,
strides,
offsets,
modifiers,
&next_fb_id,
DRM_MODE_FB_MODIFIERS))
{
g_warning ("Failed to create new back buffer handle: %m");
gbm_surface_release_buffer (gbm_surface, next_bo);
return FALSE;
}
}
else if (drmModeAddFB2 (kms_fd,
gbm_bo_get_width (next_bo),
gbm_bo_get_height (next_bo),
gbm_bo_get_format (next_bo),
handles,
strides,
offsets,
&next_fb_id,
0))
{
if (drmModeAddFB (kms_fd,
gbm_bo_get_width (next_bo),
gbm_bo_get_height (next_bo),
24, /* depth */
32, /* bpp */
strides[0],
handles[0],
&next_fb_id))
{
g_warning ("Failed to create new back buffer handle: %m");
gbm_surface_release_buffer (gbm_surface, next_bo);
return FALSE;
}
}
*out_next_bo = next_bo;
*out_next_fb_id = next_fb_id;
return TRUE;
}
static void
wait_for_pending_flips (CoglOnscreen *onscreen)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
GHashTableIter iter;
g_hash_table_iter_init (&iter, onscreen_native->secondary_gpu_states);
while (g_hash_table_iter_next (&iter,
NULL,
(gpointer *) &secondary_gpu_state))
{
while (secondary_gpu_state->pending_flips)
meta_gpu_kms_wait_for_flip (secondary_gpu_state->gpu_kms, NULL);
}
while (onscreen_native->total_pending_flips)
meta_gpu_kms_wait_for_flip (onscreen_native->render_gpu, NULL);
}
static void
copy_shared_framebuffer_gpu (CoglOnscreen *onscreen,
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state,
MetaRendererNativeGpuData *renderer_gpu_data,
gboolean *egl_context_changed)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
GError *error = NULL;
if (!meta_egl_make_current (egl,
renderer_gpu_data->egl_display,
secondary_gpu_state->egl_surface,
secondary_gpu_state->egl_surface,
renderer_gpu_data->secondary.egl_context,
&error))
{
g_warning ("Failed to make current: %s", error->message);
g_error_free (error);
return;
}
*egl_context_changed = TRUE;
if (!meta_renderer_native_gles3_blit_shared_bo (egl,
renderer_native->gles3,
renderer_gpu_data->egl_display,
renderer_gpu_data->secondary.egl_context,
secondary_gpu_state->egl_surface,
onscreen_native->gbm.next_bo,
&error))
{
g_warning ("Failed to blit shared framebuffer: %s", error->message);
g_error_free (error);
return;
}
if (!meta_egl_swap_buffers (egl,
renderer_gpu_data->egl_display,
secondary_gpu_state->egl_surface,
&error))
{
g_warning ("Failed to swap buffers: %s", error->message);
g_error_free (error);
return;
}
gbm_get_next_fb_id (secondary_gpu_state->gpu_kms,
secondary_gpu_state->gbm.surface,
&secondary_gpu_state->gbm.next_bo,
&secondary_gpu_state->gbm.next_fb_id);
}
static void
copy_shared_framebuffer_cpu (CoglOnscreen *onscreen,
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state,
MetaRendererNativeGpuData *renderer_gpu_data)
{
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
int width, height;
uint8_t *target_data;
int target_stride_bytes;
uint32_t target_fb_id;
MetaDumbBuffer *next_dumb_fb;
MetaDumbBuffer *current_dumb_fb;
width = cogl_framebuffer_get_width (framebuffer);
height = cogl_framebuffer_get_height (framebuffer);
current_dumb_fb = secondary_gpu_state->cpu.dumb_fb;
if (current_dumb_fb == &secondary_gpu_state->cpu.dumb_fbs[0])
next_dumb_fb = &secondary_gpu_state->cpu.dumb_fbs[1];
else
next_dumb_fb = &secondary_gpu_state->cpu.dumb_fbs[0];
secondary_gpu_state->cpu.dumb_fb = next_dumb_fb;
g_assert (width == secondary_gpu_state->cpu.dumb_fb->width);
g_assert (height == secondary_gpu_state->cpu.dumb_fb->height);
target_data = secondary_gpu_state->cpu.dumb_fb->map;
target_stride_bytes = secondary_gpu_state->cpu.dumb_fb->stride_bytes;
target_fb_id = secondary_gpu_state->cpu.dumb_fb->fb_id;
meta_renderer_native_gles3_read_pixels (egl,
renderer_native->gles3,
width, height,
target_data,
target_stride_bytes);
secondary_gpu_state->gbm.next_fb_id = target_fb_id;
}
static void
update_secondary_gpu_state_pre_swap_buffers (CoglOnscreen *onscreen)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
GHashTableIter iter;
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
g_hash_table_iter_init (&iter, onscreen_native->secondary_gpu_states);
while (g_hash_table_iter_next (&iter,
NULL,
(gpointer *) &secondary_gpu_state))
{
MetaRendererNativeGpuData *renderer_gpu_data;
renderer_gpu_data = secondary_gpu_state->renderer_gpu_data;
switch (renderer_gpu_data->secondary.copy_mode)
{
case META_SHARED_FRAMEBUFFER_COPY_MODE_GPU:
/* Done after eglSwapBuffers. */
break;
case META_SHARED_FRAMEBUFFER_COPY_MODE_CPU:
copy_shared_framebuffer_cpu (onscreen,
secondary_gpu_state,
renderer_gpu_data);
break;
}
}
}
static void
update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen,
gboolean *egl_context_changed)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
GHashTableIter iter;
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
g_hash_table_iter_init (&iter, onscreen_native->secondary_gpu_states);
while (g_hash_table_iter_next (&iter,
NULL,
(gpointer *) &secondary_gpu_state))
{
MetaRendererNativeGpuData *renderer_gpu_data;
renderer_gpu_data =
meta_renderer_native_get_gpu_data (renderer_native,
secondary_gpu_state->gpu_kms);
switch (renderer_gpu_data->secondary.copy_mode)
{
case META_SHARED_FRAMEBUFFER_COPY_MODE_GPU:
copy_shared_framebuffer_gpu (onscreen,
secondary_gpu_state,
renderer_gpu_data,
egl_context_changed);
break;
case META_SHARED_FRAMEBUFFER_COPY_MODE_CPU:
/* Done before eglSwapBuffers. */
break;
}
}
}
static void
meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
const int *rectangles,
int n_rectangles)
{
CoglContext *cogl_context = COGL_FRAMEBUFFER (onscreen)->context;
CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
CoglRenderer *cogl_renderer = cogl_context->display->renderer;
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaGpuKms *render_gpu = onscreen_native->render_gpu;
CoglFrameInfo *frame_info;
gboolean egl_context_changed = FALSE;
renderer/native: fix next_fb_id race on CPU copy path There was a race in setting next_fb_id when a secondary GPU was using the CPU copy path. Losing this race caused the attempt to drmModePageFlip () to FB ID 0 which is invalid and always fails. Failing to flip causes Mutter to fall back to drmModeSetCrtc () permanently. In meta_onscreen_native_swap_buffers_with_damage (): - update_secondary_gpu_state_pre_swap_buffers () - copy_shared_framebuffer_cpu () but only on the CPU copy path - secondary_gpu_state->gbm.next_fb_id is set - wait_for_pending_flips () - Waits for any remaining page flip events and executes and destroys the related page flip closures. - on_crtc_flipped () - meta_onscreen_native_swap_drm_fb () - swap_secondary_drm_fb () - secondary_gpu_state->gbm.next_fb_id = 0; - meta_onscreen_native_flip_crtcs () - meta_onscreen_native_flip_crtc () - meta_gpu_kms_flip_crtc () gets called with fb_id = 0 This race was observed lost when running 'mutter --wayland' on a machine with two outputs on Intel and one output on DisplayLink USB dock, and wiggling around a weston-terminal window between the Intel and DisplayLink outputs. It took from a second to a minute to trigger. For testing with DisplayLink outputs Mutter also needed a patch to take the DisplayLink output into use, as it would have otherwise been ignored being a platform device rather than a PCI device. Fix this race by first waiting for pending flips and only then proceeding with the swap operations. This should be safe, because the pending flips could have completed already before entering meta_onscreen_native_swap_buffers_with_damage ().
2018-10-04 06:53:17 -04:00
/*
* Wait for the flip callback before continuing, as we might have started the
* animation earlier due to the animation being driven by some other monitor.
*/
wait_for_pending_flips (onscreen);
frame_info = g_queue_peek_tail (&onscreen->pending_frame_infos);
frame_info->global_frame_counter = renderer_native->frame_counter;
update_secondary_gpu_state_pre_swap_buffers (onscreen);
parent_vtable->onscreen_swap_buffers_with_damage (onscreen,
rectangles,
n_rectangles);
renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
render_gpu);
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
g_warn_if_fail (onscreen_native->gbm.next_bo == NULL &&
onscreen_native->gbm.next_fb_id == 0);
if (!gbm_get_next_fb_id (render_gpu,
onscreen_native->gbm.surface,
&onscreen_native->gbm.next_bo,
&onscreen_native->gbm.next_fb_id))
return;
break;
#ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
break;
#endif
}
update_secondary_gpu_state_post_swap_buffers (onscreen, &egl_context_changed);
/* If this is the first framebuffer to be presented then we now setup the
* crtc modes, else we flip from the previous buffer */
if (onscreen_native->pending_set_crtc)
{
meta_onscreen_native_set_crtc_modes (onscreen);
onscreen_native->pending_set_crtc = FALSE;
}
onscreen_native->pending_queue_swap_notify_frame_count = renderer_native->frame_counter;
meta_onscreen_native_flip_crtcs (onscreen);
/*
* If we changed EGL context, cogl will have the wrong idea about what is
* current, making it fail to set it when it needs to. Avoid that by making
* EGL_NO_CONTEXT current now, making cogl eventually set the correct
* context.
*/
if (egl_context_changed)
_cogl_winsys_egl_ensure_current (cogl_display);
}
static gboolean
meta_renderer_native_init_egl_context (CoglContext *cogl_context,
GError **error)
{
#ifdef HAVE_EGL_DEVICE
CoglRenderer *cogl_renderer = cogl_context->display->renderer;
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
#endif
COGL_FLAGS_SET (cogl_context->features,
COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, TRUE);
/* TODO: remove this deprecated feature */
COGL_FLAGS_SET (cogl_context->winsys_features,
COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT,
TRUE);
COGL_FLAGS_SET (cogl_context->winsys_features,
COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT,
TRUE);
COGL_FLAGS_SET (cogl_context->winsys_features,
COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN,
TRUE);
#ifdef HAVE_EGL_DEVICE
if (renderer_gpu_data->mode == META_RENDERER_NATIVE_MODE_EGL_DEVICE)
COGL_FLAGS_SET (cogl_context->features,
COGL_FEATURE_ID_TEXTURE_EGL_IMAGE_EXTERNAL, TRUE);
#endif
return TRUE;
}
static gboolean
should_surface_be_sharable (CoglOnscreen *onscreen)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
CoglContext *cogl_context = COGL_FRAMEBUFFER (onscreen)->context;
CoglRenderer *cogl_renderer = cogl_context->display->renderer;
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
GList *l;
if (!onscreen_native->logical_monitor)
return FALSE;
for (l = meta_logical_monitor_get_monitors (onscreen_native->logical_monitor);
l;
l = l->next)
{
MetaMonitor *monitor = l->data;
MetaGpuKms *gpu_kms = META_GPU_KMS (meta_monitor_get_gpu (monitor));
if (renderer_gpu_data != meta_renderer_native_get_gpu_data (renderer_native,
gpu_kms))
return TRUE;
}
return FALSE;
}
static gboolean
meta_renderer_native_create_surface_gbm (CoglOnscreen *onscreen,
int width,
int height,
struct gbm_surface **gbm_surface,
EGLSurface *egl_surface,
GError **error)
{
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglContext *cogl_context = framebuffer->context;
CoglDisplay *cogl_display = cogl_context->display;
CoglDisplayEGL *cogl_display_egl = cogl_display->winsys;
CoglRenderer *cogl_renderer = cogl_display->renderer;
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
struct gbm_surface *new_gbm_surface = NULL;
EGLNativeWindowType egl_native_window;
EGLSurface new_egl_surface;
uint32_t format = GBM_FORMAT_XRGB8888;
GArray *modifiers;
renderer_gpu_data =
meta_renderer_native_get_gpu_data (renderer_native,
onscreen_native->render_gpu);
if (renderer_native->use_modifiers)
modifiers = get_supported_modifiers (onscreen, format);
else
modifiers = NULL;
if (modifiers)
{
new_gbm_surface =
gbm_surface_create_with_modifiers (renderer_gpu_data->gbm.device,
width, height, format,
(uint64_t *) modifiers->data,
modifiers->len);
g_array_free (modifiers, TRUE);
}
if (!new_gbm_surface)
{
uint32_t flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
if (should_surface_be_sharable (onscreen))
flags |= GBM_BO_USE_LINEAR;
new_gbm_surface = gbm_surface_create (renderer_gpu_data->gbm.device,
width, height,
format,
flags);
}
if (!new_gbm_surface)
{
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_CREATE_ONSCREEN,
"Failed to allocate surface");
return FALSE;
}
egl_native_window = (EGLNativeWindowType) new_gbm_surface;
new_egl_surface =
meta_egl_create_window_surface (egl,
cogl_renderer_egl->edpy,
cogl_display_egl->egl_config,
egl_native_window,
NULL,
error);
if (new_egl_surface == EGL_NO_SURFACE)
{
gbm_surface_destroy (new_gbm_surface);
return FALSE;
}
*gbm_surface = new_gbm_surface;
*egl_surface = new_egl_surface;
return TRUE;
}
#ifdef HAVE_EGL_DEVICE
static gboolean
meta_renderer_native_create_surface_egl_device (CoglOnscreen *onscreen,
MetaLogicalMonitor *logical_monitor,
int width,
int height,
EGLStreamKHR *out_egl_stream,
EGLSurface *out_egl_surface,
GError **error)
{
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglContext *cogl_context = framebuffer->context;
CoglDisplay *cogl_display = cogl_context->display;
CoglDisplayEGL *cogl_display_egl = cogl_display->winsys;
CoglRenderer *cogl_renderer = cogl_display->renderer;
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
MetaEgl *egl =
meta_renderer_native_get_egl (renderer_gpu_data->renderer_native);
EGLDisplay egl_display = renderer_gpu_data->egl_display;
MetaMonitor *monitor;
MetaOutput *output;
MetaCrtc *crtc;
EGLConfig egl_config;
EGLStreamKHR egl_stream;
EGLSurface egl_surface;
EGLint num_layers;
EGLOutputLayerEXT output_layer;
EGLAttrib output_attribs[3];
EGLint stream_attribs[] = {
EGL_STREAM_FIFO_LENGTH_KHR, 1,
EGL_CONSUMER_AUTO_ACQUIRE_EXT, EGL_FALSE,
EGL_NONE
};
EGLint stream_producer_attribs[] = {
EGL_WIDTH, width,
EGL_HEIGHT, height,
EGL_NONE
};
egl_stream = meta_egl_create_stream (egl, egl_display, stream_attribs, error);
if (egl_stream == EGL_NO_STREAM_KHR)
return FALSE;
monitor = meta_logical_monitor_get_monitors (logical_monitor)->data;
output = meta_monitor_get_main_output (monitor);
crtc = meta_output_get_assigned_crtc (output);
/*
* An "logical_monitor" may have multiple outputs/crtcs in case its tiled,
* but as far as I can tell, EGL only allows you to pass one crtc_id, so
* lets pass the first one.
*/
output_attribs[0] = EGL_DRM_CRTC_EXT;
output_attribs[1] = crtc->crtc_id;
output_attribs[2] = EGL_NONE;
if (!meta_egl_get_output_layers (egl, egl_display,
output_attribs,
&output_layer, 1, &num_layers,
error))
{
meta_egl_destroy_stream (egl, egl_display, egl_stream, NULL);
return FALSE;
}
if (num_layers < 1)
{
meta_egl_destroy_stream (egl, egl_display, egl_stream, NULL);
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"Unable to find output layers.");
return FALSE;
}
if (!meta_egl_stream_consumer_output (egl, egl_display,
egl_stream, output_layer,
error))
{
meta_egl_destroy_stream (egl, egl_display, egl_stream, NULL);
return FALSE;
}
egl_config = cogl_display_egl->egl_config;
egl_surface = meta_egl_create_stream_producer_surface (egl,
egl_display,
egl_config,
egl_stream,
stream_producer_attribs,
error);
if (egl_surface == EGL_NO_SURFACE)
{
meta_egl_destroy_stream (egl, egl_display, egl_stream, NULL);
return FALSE;
}
*out_egl_stream = egl_stream;
*out_egl_surface = egl_surface;
return TRUE;
}
#endif /* HAVE_EGL_DEVICE */
static gboolean
init_dumb_fb (MetaDumbBuffer *dumb_fb,
MetaGpuKms *gpu_kms,
int width,
int height,
uint32_t format,
GError **error)
{
struct drm_mode_create_dumb create_arg;
struct drm_mode_destroy_dumb destroy_arg;
struct drm_mode_map_dumb map_arg;
uint32_t fb_id = 0;
void *map;
int kms_fd;
uint32_t handles[4] = { 0, };
uint32_t pitches[4] = { 0, };
uint32_t offsets[4] = { 0, };
kms_fd = meta_gpu_kms_get_fd (gpu_kms);
create_arg = (struct drm_mode_create_dumb) {
.bpp = 32, /* RGBX8888 */
.width = width,
.height = height
};
if (drmIoctl (kms_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg) != 0)
{
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"Failed to create dumb drm buffer: %s",
g_strerror (errno));
goto err_ioctl;
}
handles[0] = create_arg.handle;
pitches[0] = create_arg.pitch;
if (drmModeAddFB2 (kms_fd, width, height, format,
handles, pitches, offsets,
&fb_id, 0) != 0)
{
g_debug ("drmModeAddFB2 failed (%s), falling back to drmModeAddFB",
g_strerror (errno));
}
if (fb_id == 0)
{
if (format != DRM_FORMAT_XRGB8888)
{
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"drmModeAddFB does not support format 0x%x",
format);
goto err_add_fb;
}
if (drmModeAddFB (kms_fd, width, height,
24 /* depth of RGBX8888 */,
32 /* bpp of RGBX8888 */,
create_arg.pitch,
create_arg.handle,
&fb_id) != 0)
{
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"drmModeAddFB failed: %s",
g_strerror (errno));
goto err_add_fb;
}
}
map_arg = (struct drm_mode_map_dumb) {
.handle = create_arg.handle
};
if (drmIoctl (kms_fd, DRM_IOCTL_MODE_MAP_DUMB,
&map_arg) != 0)
{
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"Failed to map dumb drm buffer: %s",
g_strerror (errno));
goto err_map_dumb;
}
map = mmap (NULL, create_arg.size, PROT_WRITE, MAP_SHARED,
kms_fd, map_arg.offset);
if (map == MAP_FAILED)
{
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"Failed to mmap dumb drm buffer memory: %s",
g_strerror (errno));
goto err_mmap;
}
dumb_fb->fb_id = fb_id;
dumb_fb->handle = create_arg.handle;
dumb_fb->map = map;
dumb_fb->map_size = create_arg.size;
dumb_fb->width = width;
dumb_fb->height = height;
dumb_fb->stride_bytes = create_arg.pitch;
dumb_fb->drm_format = format;
return TRUE;
err_mmap:
err_map_dumb:
drmModeRmFB (kms_fd, fb_id);
err_add_fb:
destroy_arg = (struct drm_mode_destroy_dumb) {
.handle = create_arg.handle
};
drmIoctl (kms_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
err_ioctl:
return FALSE;
}
static void
release_dumb_fb (MetaDumbBuffer *dumb_fb,
MetaGpuKms *gpu_kms)
{
struct drm_mode_destroy_dumb destroy_arg;
int kms_fd;
if (!dumb_fb->map)
return;
munmap (dumb_fb->map, dumb_fb->map_size);
dumb_fb->map = NULL;
kms_fd = meta_gpu_kms_get_fd (gpu_kms);
drmModeRmFB (kms_fd, dumb_fb->fb_id);
destroy_arg = (struct drm_mode_destroy_dumb) {
.handle = dumb_fb->handle
};
drmIoctl (kms_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
}
static gboolean
meta_renderer_native_init_onscreen (CoglOnscreen *onscreen,
GError **error)
{
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglContext *cogl_context = framebuffer->context;
CoglDisplay *cogl_display = cogl_context->display;
CoglDisplayEGL *cogl_display_egl = cogl_display->winsys;
CoglOnscreenEGL *onscreen_egl;
MetaOnscreenNative *onscreen_native;
_COGL_RETURN_VAL_IF_FAIL (cogl_display_egl->egl_context, FALSE);
onscreen->winsys = g_slice_new0 (CoglOnscreenEGL);
onscreen_egl = onscreen->winsys;
onscreen_native = g_slice_new0 (MetaOnscreenNative);
onscreen_egl->platform = onscreen_native;
/*
* Don't actually initialize anything here, since we may not have the
* information available yet, and there is no way to pass it at this stage.
* To properly allocate a MetaOnscreenNative, the caller must call
* meta_onscreen_native_allocate() after cogl_framebuffer_allocate().
*
* TODO: Turn CoglFramebuffer/CoglOnscreen into GObjects, so it's possible
* to add backend specific properties.
*/
return TRUE;
}
static gboolean
meta_onscreen_native_allocate (CoglOnscreen *onscreen,
GError **error)
{
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaRendererNativeGpuData *renderer_gpu_data;
struct gbm_surface *gbm_surface;
EGLSurface egl_surface;
int width;
int height;
#ifdef HAVE_EGL_DEVICE
MetaRendererView *view;
MetaLogicalMonitor *logical_monitor;
EGLStreamKHR egl_stream;
#endif
onscreen_native->pending_set_crtc = TRUE;
/* If a kms_fd is set then the display width and height
* won't be available until meta_renderer_native_set_layout
* is called. In that case, defer creating the surface
* until then.
*/
width = cogl_framebuffer_get_width (framebuffer);
height = cogl_framebuffer_get_height (framebuffer);
if (width == 0 || height == 0)
return TRUE;
renderer_gpu_data =
meta_renderer_native_get_gpu_data (onscreen_native->renderer_native,
onscreen_native->render_gpu);
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
if (!meta_renderer_native_create_surface_gbm (onscreen,
width, height,
&gbm_surface,
&egl_surface,
error))
return FALSE;
onscreen_native->gbm.surface = gbm_surface;
onscreen_egl->egl_surface = egl_surface;
break;
#ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
if (!init_dumb_fb (&onscreen_native->egl.dumb_fb,
onscreen_native->render_gpu,
width, height,
DRM_FORMAT_XRGB8888,
error))
return FALSE;
view = onscreen_native->view;
logical_monitor = meta_renderer_view_get_logical_monitor (view);
if (!meta_renderer_native_create_surface_egl_device (onscreen,
logical_monitor,
width, height,
&egl_stream,
&egl_surface,
error))
return FALSE;
onscreen_native->egl.stream = egl_stream;
onscreen_egl->egl_surface = egl_surface;
break;
#endif /* HAVE_EGL_DEVICE */
}
return TRUE;
}
static void
meta_renderer_native_release_onscreen (CoglOnscreen *onscreen)
{
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglContext *cogl_context = framebuffer->context;
CoglRenderer *cogl_renderer = cogl_context->display->renderer;
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native;
MetaRendererNativeGpuData *renderer_gpu_data;
/* If we never successfully allocated then there's nothing to do */
if (onscreen_egl == NULL)
return;
onscreen_native = onscreen_egl->platform;
if (onscreen_egl->egl_surface != EGL_NO_SURFACE)
{
MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
meta_egl_destroy_surface (egl,
cogl_renderer_egl->edpy,
onscreen_egl->egl_surface,
NULL);
onscreen_egl->egl_surface = EGL_NO_SURFACE;
}
renderer_gpu_data =
meta_renderer_native_get_gpu_data (onscreen_native->renderer_native,
onscreen_native->render_gpu);
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
/* flip state takes a reference on the onscreen so there should
* never be outstanding flips when we reach here. */
g_return_if_fail (onscreen_native->gbm.next_fb_id == 0);
free_current_bo (onscreen);
if (onscreen_native->gbm.surface)
{
gbm_surface_destroy (onscreen_native->gbm.surface);
onscreen_native->gbm.surface = NULL;
}
break;
#ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
release_dumb_fb (&onscreen_native->egl.dumb_fb,
onscreen_native->render_gpu);
if (onscreen_native->egl.stream != EGL_NO_STREAM_KHR)
{
MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
meta_egl_destroy_stream (egl,
cogl_renderer_egl->edpy,
onscreen_native->egl.stream,
NULL);
onscreen_native->egl.stream = EGL_NO_STREAM_KHR;
}
break;
#endif /* HAVE_EGL_DEVICE */
}
g_hash_table_destroy (onscreen_native->secondary_gpu_states);
g_slice_free (MetaOnscreenNative, onscreen_native);
g_slice_free (CoglOnscreenEGL, onscreen->winsys);
onscreen->winsys = NULL;
}
static const CoglWinsysEGLVtable
_cogl_winsys_egl_vtable = {
.add_config_attributes = meta_renderer_native_add_egl_config_attributes,
.choose_config = meta_renderer_native_choose_egl_config,
.display_setup = meta_renderer_native_setup_egl_display,
.display_destroy = meta_renderer_native_destroy_egl_display,
.context_created = meta_renderer_native_egl_context_created,
.cleanup_context = meta_renderer_native_egl_cleanup_context,
.context_init = meta_renderer_native_init_egl_context
};
gboolean
meta_renderer_native_supports_mirroring (MetaRendererNative *renderer_native)
{
MetaMonitorManager *monitor_manager =
META_MONITOR_MANAGER (renderer_native->monitor_manager_kms);
GList *l;
for (l = monitor_manager->gpus; l; l = l->next)
{
MetaGpuKms *gpu_kms = META_GPU_KMS (l->data);
MetaRendererNativeGpuData *renderer_gpu_data;
renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
gpu_kms);
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
break;
#ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
return FALSE;
#endif
}
}
return TRUE;
}
void
meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native)
{
MetaRenderer *renderer = META_RENDERER (renderer_native);
GList *l;
for (l = meta_renderer_get_views (renderer); l; l = l->next)
{
ClutterStageView *stage_view = l->data;
CoglFramebuffer *framebuffer =
clutter_stage_view_get_onscreen (stage_view);
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
onscreen_native->pending_set_crtc = TRUE;
}
renderer_native->pending_unset_disabled_crtcs = TRUE;
}
static CoglOnscreen *
meta_renderer_native_create_onscreen (MetaRendererNative *renderer_native,
MetaGpuKms *render_gpu,
MetaLogicalMonitor *logical_monitor,
CoglContext *context,
MetaMonitorTransform transform,
gint view_width,
gint view_height,
GError **error)
{
CoglOnscreen *onscreen;
CoglOnscreenEGL *onscreen_egl;
MetaOnscreenNative *onscreen_native;
gint width, height;
GList *l;
if (meta_monitor_transform_is_rotated (transform))
{
width = view_height;
height = view_width;
}
else
{
width = view_width;
height = view_height;
}
onscreen = cogl_onscreen_new (context, width, height);
cogl_onscreen_set_swap_throttled (onscreen,
_clutter_get_sync_to_vblank ());
if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (onscreen), error))
{
cogl_object_unref (onscreen);
return NULL;
}
onscreen_egl = onscreen->winsys;
onscreen_native = onscreen_egl->platform;
onscreen_native->renderer_native = renderer_native;
onscreen_native->render_gpu = render_gpu;
onscreen_native->logical_monitor = logical_monitor;
onscreen_native->secondary_gpu_states =
g_hash_table_new_full (NULL, NULL,
NULL,
(GDestroyNotify) secondary_gpu_state_free);
for (l = meta_logical_monitor_get_monitors (logical_monitor); l; l = l->next)
{
MetaMonitor *monitor = l->data;
MetaGpuKms *gpu_kms = META_GPU_KMS (meta_monitor_get_gpu (monitor));
if (gpu_kms == render_gpu)
continue;
if (get_secondary_gpu_state (onscreen, gpu_kms))
continue;
if (!init_secondary_gpu_state (renderer_native, onscreen, gpu_kms, error))
{
cogl_object_unref (onscreen);
return NULL;
}
}
return onscreen;
}
static CoglOffscreen *
meta_renderer_native_create_offscreen (MetaRendererNative *renderer,
CoglContext *context,
MetaMonitorTransform transform,
gint view_width,
gint view_height,
GError **error)
{
CoglOffscreen *fb;
CoglTexture2D *tex;
tex = cogl_texture_2d_new_with_size (context, view_width, view_height);
cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (tex), FALSE);
if (!cogl_texture_allocate (COGL_TEXTURE (tex), error))
{
cogl_object_unref (tex);
return FALSE;
}
fb = cogl_offscreen_new_with_texture (COGL_TEXTURE (tex));
cogl_object_unref (tex);
if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (fb), error))
{
cogl_object_unref (fb);
return FALSE;
}
return fb;
}
static const CoglWinsysVtable *
get_native_cogl_winsys_vtable (CoglRenderer *cogl_renderer)
{
static gboolean vtable_inited = FALSE;
static CoglWinsysVtable vtable;
if (!vtable_inited)
{
/* The this winsys is a subclass of the EGL winsys so we
start by copying its vtable */
parent_vtable = _cogl_winsys_egl_get_vtable ();
vtable = *parent_vtable;
vtable.id = COGL_WINSYS_ID_CUSTOM;
vtable.name = "EGL_KMS";
vtable.renderer_connect = meta_renderer_native_connect;
vtable.renderer_disconnect = meta_renderer_native_disconnect;
vtable.onscreen_init = meta_renderer_native_init_onscreen;
vtable.onscreen_deinit = meta_renderer_native_release_onscreen;
/* The KMS winsys doesn't support swap region */
vtable.onscreen_swap_region = NULL;
vtable.onscreen_swap_buffers_with_damage =
meta_onscreen_native_swap_buffers_with_damage;
vtable_inited = TRUE;
}
return &vtable;
}
static CoglRenderer *
create_cogl_renderer_for_gpu (MetaGpuKms *gpu_kms)
{
CoglRenderer *cogl_renderer;
cogl_renderer = cogl_renderer_new ();
cogl_renderer_set_custom_winsys (cogl_renderer,
get_native_cogl_winsys_vtable,
gpu_kms);
return cogl_renderer;
}
static CoglRenderer *
meta_renderer_native_create_cogl_renderer (MetaRenderer *renderer)
{
MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
MetaMonitorManagerKms *monitor_manager_kms =
renderer_native->monitor_manager_kms;
MetaGpuKms *primary_gpu =
meta_monitor_manager_kms_get_primary_gpu (monitor_manager_kms);
return create_cogl_renderer_for_gpu (primary_gpu);
}
static void
meta_onscreen_native_set_view (CoglOnscreen *onscreen,
MetaRendererView *view)
{
CoglOnscreenEGL *onscreen_egl;
MetaOnscreenNative *onscreen_native;
onscreen_egl = onscreen->winsys;
onscreen_native = onscreen_egl->platform;
onscreen_native->view = view;
}
static MetaMonitorTransform
calculate_view_transform (MetaMonitorManager *monitor_manager,
MetaLogicalMonitor *logical_monitor)
{
MetaMonitor *main_monitor;
MetaOutput *main_output;
MetaCrtc *crtc;
MetaMonitorTransform crtc_transform;
main_monitor = meta_logical_monitor_get_monitors (logical_monitor)->data;
main_output = meta_monitor_get_main_output (main_monitor);
crtc = meta_output_get_assigned_crtc (main_output);
crtc_transform =
meta_monitor_logical_to_crtc_transform (main_monitor,
logical_monitor->transform);
/*
* Pick any monitor and output and check; all CRTCs of a logical monitor will
* always have the same transform assigned to them.
*/
if (meta_monitor_manager_is_transform_handled (monitor_manager,
crtc,
crtc_transform))
return META_MONITOR_TRANSFORM_NORMAL;
else
return crtc_transform;
}
static MetaRendererView *
meta_renderer_native_create_view (MetaRenderer *renderer,
MetaLogicalMonitor *logical_monitor)
{
MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
MetaMonitorManagerKms *monitor_manager_kms =
renderer_native->monitor_manager_kms;
MetaMonitorManager *monitor_manager =
META_MONITOR_MANAGER (monitor_manager_kms);
MetaBackend *backend = meta_monitor_manager_get_backend (monitor_manager);
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
CoglContext *cogl_context =
clutter_backend_get_cogl_context (clutter_backend);
CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
MetaGpuKms *primary_gpu;
CoglDisplayEGL *cogl_display_egl;
CoglOnscreenEGL *onscreen_egl;
MetaMonitorTransform view_transform;
CoglOnscreen *onscreen = NULL;
CoglOffscreen *offscreen = NULL;
float scale;
int width, height;
MetaRendererView *view;
GError *error = NULL;
view_transform = calculate_view_transform (monitor_manager, logical_monitor);
if (meta_is_stage_views_scaled ())
scale = meta_logical_monitor_get_scale (logical_monitor);
else
scale = 1.0;
width = roundf (logical_monitor->rect.width * scale);
height = roundf (logical_monitor->rect.height * scale);
primary_gpu = meta_monitor_manager_kms_get_primary_gpu (monitor_manager_kms);
onscreen = meta_renderer_native_create_onscreen (renderer_native,
primary_gpu,
logical_monitor,
cogl_context,
view_transform,
width,
height,
&error);
if (!onscreen)
g_error ("Failed to allocate onscreen framebuffer: %s", error->message);
if (view_transform != META_MONITOR_TRANSFORM_NORMAL)
{
offscreen = meta_renderer_native_create_offscreen (renderer_native,
cogl_context,
view_transform,
width,
height,
&error);
if (!offscreen)
g_error ("Failed to allocate back buffer texture: %s", error->message);
}
view = g_object_new (META_TYPE_RENDERER_VIEW,
"layout", &logical_monitor->rect,
"scale", scale,
"framebuffer", onscreen,
"offscreen", offscreen,
"logical-monitor", logical_monitor,
"transform", view_transform,
NULL);
g_clear_pointer (&offscreen, cogl_object_unref);
meta_onscreen_native_set_view (onscreen, view);
if (!meta_onscreen_native_allocate (onscreen, &error))
{
g_warning ("Could not create onscreen: %s", error->message);
cogl_object_unref (onscreen);
g_object_unref (view);
g_error_free (error);
return NULL;
}
cogl_object_unref (onscreen);
/* Ensure we don't point to stale surfaces when creating the offscreen */
onscreen_egl = onscreen->winsys;
cogl_display_egl = cogl_display->winsys;
_cogl_winsys_egl_make_current (cogl_display,
onscreen_egl->egl_surface,
onscreen_egl->egl_surface,
cogl_display_egl->egl_context);
return view;
}
void
meta_renderer_native_finish_frame (MetaRendererNative *renderer_native)
{
renderer_native->frame_counter++;
if (renderer_native->pending_unset_disabled_crtcs)
{
MetaMonitorManager *monitor_manager =
META_MONITOR_MANAGER (renderer_native->monitor_manager_kms);
GList *l;
for (l = meta_monitor_manager_get_gpus (monitor_manager); l; l = l->next)
{
MetaGpu *gpu = l->data;
MetaGpuKms *gpu_kms = META_GPU_KMS (gpu);
GList *k;
for (k = meta_gpu_get_crtcs (gpu); k; k = k->next)
{
MetaCrtc *crtc = k->data;
if (crtc->current_mode)
continue;
meta_gpu_kms_apply_crtc_mode (gpu_kms, crtc, 0, 0, 0);
}
}
renderer_native->pending_unset_disabled_crtcs = FALSE;
}
}
int64_t
meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native)
{
return renderer_native->frame_counter;
}
static void
meta_renderer_native_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object);
switch (prop_id)
{
case PROP_MONITOR_MANAGER:
g_value_set_object (value, renderer_native->monitor_manager_kms);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_renderer_native_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object);
switch (prop_id)
{
case PROP_MONITOR_MANAGER:
renderer_native->monitor_manager_kms = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
create_secondary_egl_config (MetaEgl *egl,
MetaRendererNativeMode mode,
EGLDisplay egl_display,
EGLConfig *egl_config,
GError **error)
{
EGLint attributes[] = {
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, EGL_DONT_CARE,
EGL_BUFFER_SIZE, EGL_DONT_CARE,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE
};
switch (mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
return choose_egl_config_from_gbm_format (egl,
egl_display,
attributes,
GBM_FORMAT_XRGB8888,
egl_config,
error);
#ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
return meta_egl_choose_first_config (egl,
egl_display,
attributes,
egl_config,
error);
#endif
}
return FALSE;
}
static EGLContext
create_secondary_egl_context (MetaEgl *egl,
EGLDisplay egl_display,
EGLConfig egl_config,
GError **error)
{
EGLint attributes[] = {
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
};
return meta_egl_create_context (egl,
egl_display,
egl_config,
EGL_NO_CONTEXT,
attributes,
error);
}
static void
meta_renderer_native_ensure_gles3 (MetaRendererNative *renderer_native)
{
MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
if (renderer_native->gles3)
return;
renderer_native->gles3 = meta_gles3_new (egl);
}
static gboolean
init_secondary_gpu_data_gpu (MetaRendererNativeGpuData *renderer_gpu_data,
GError **error)
{
MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
EGLDisplay egl_display = renderer_gpu_data->egl_display;
EGLConfig egl_config;
EGLContext egl_context;
char **missing_gl_extensions;
if (!create_secondary_egl_config (egl, renderer_gpu_data->mode, egl_display,
&egl_config, error))
return FALSE;
egl_context = create_secondary_egl_context (egl, egl_display, egl_config, error);
if (egl_context == EGL_NO_CONTEXT)
return FALSE;
meta_renderer_native_ensure_gles3 (renderer_native);
if (!meta_egl_make_current (egl,
egl_display,
EGL_NO_SURFACE,
EGL_NO_SURFACE,
egl_context,
error))
{
meta_egl_destroy_context (egl, egl_display, egl_context, NULL);
return FALSE;
}
if (!meta_gles3_has_extensions (renderer_native->gles3,
&missing_gl_extensions,
"GL_OES_EGL_image_external",
NULL))
{
char *missing_gl_extensions_str;
missing_gl_extensions_str = g_strjoinv (", ", missing_gl_extensions);
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Missing OpenGL ES extensions: %s",
missing_gl_extensions_str);
g_free (missing_gl_extensions_str);
g_free (missing_gl_extensions);
}
renderer_gpu_data->secondary.egl_context = egl_context;
renderer_gpu_data->secondary.egl_config = egl_config;
renderer_gpu_data->secondary.copy_mode = META_SHARED_FRAMEBUFFER_COPY_MODE_GPU;
return TRUE;
}
static void
init_secondary_gpu_data_cpu (MetaRendererNativeGpuData *renderer_gpu_data)
{
renderer_gpu_data->secondary.copy_mode = META_SHARED_FRAMEBUFFER_COPY_MODE_CPU;
}
static void
init_secondary_gpu_data (MetaRendererNativeGpuData *renderer_gpu_data)
{
GError *error = NULL;
if (init_secondary_gpu_data_gpu (renderer_gpu_data, &error))
return;
g_warning ("Failed to initialize accelerated iGPU/dGPU framebuffer sharing: %s",
error->message);
g_error_free (error);
init_secondary_gpu_data_cpu (renderer_gpu_data);
}
static MetaRendererNativeGpuData *
create_renderer_gpu_data_gbm (MetaRendererNative *renderer_native,
MetaGpuKms *gpu_kms,
GError **error)
{
MetaMonitorManagerKms *monitor_manager_kms;
MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
struct gbm_device *gbm_device;
EGLDisplay egl_display;
int kms_fd;
MetaRendererNativeGpuData *renderer_gpu_data;
MetaGpuKms *primary_gpu;
if (!meta_egl_has_extensions (egl, EGL_NO_DISPLAY, NULL,
"EGL_MESA_platform_gbm",
NULL) &&
!meta_egl_has_extensions (egl, EGL_NO_DISPLAY, NULL,
"EGL_KHR_platform_gbm",
NULL))
{
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"Missing extension for GBM renderer: EGL_KHR_platform_gbm");
return NULL;
}
kms_fd = meta_gpu_kms_get_fd (gpu_kms);
gbm_device = gbm_create_device (kms_fd);
if (!gbm_device)
{
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"Failed to create gbm device: %s", g_strerror (errno));
return NULL;
}
egl_display = meta_egl_get_platform_display (egl,
EGL_PLATFORM_GBM_KHR,
gbm_device, NULL, error);
if (egl_display == EGL_NO_DISPLAY)
{
gbm_device_destroy (gbm_device);
return NULL;
}
if (!meta_egl_initialize (egl, egl_display, error))
return NULL;
renderer_gpu_data = meta_create_renderer_native_gpu_data (gpu_kms);
renderer_gpu_data->renderer_native = renderer_native;
renderer_gpu_data->gbm.device = gbm_device;
renderer_gpu_data->mode = META_RENDERER_NATIVE_MODE_GBM;
renderer_gpu_data->egl_display = egl_display;
monitor_manager_kms = renderer_native->monitor_manager_kms;
primary_gpu = meta_monitor_manager_kms_get_primary_gpu (monitor_manager_kms);
if (gpu_kms != primary_gpu)
init_secondary_gpu_data (renderer_gpu_data);
return renderer_gpu_data;
}
#ifdef HAVE_EGL_DEVICE
static const char *
get_drm_device_file (MetaEgl *egl,
EGLDeviceEXT device,
GError **error)
{
if (!meta_egl_egl_device_has_extensions (egl, device,
NULL,
"EGL_EXT_device_drm",
NULL))
{
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"Missing required EGLDevice extension EGL_EXT_device_drm");
return NULL;
}
return meta_egl_query_device_string (egl, device,
EGL_DRM_DEVICE_FILE_EXT,
error);
}
static EGLDeviceEXT
find_egl_device (MetaRendererNative *renderer_native,
MetaGpuKms *gpu_kms,
GError **error)
{
MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
char **missing_extensions;
EGLint num_devices;
EGLDeviceEXT *devices;
const char *kms_file_path;
EGLDeviceEXT device;
EGLint i;
if (!meta_egl_has_extensions (egl,
EGL_NO_DISPLAY,
&missing_extensions,
"EGL_EXT_device_base",
NULL))
{
char *missing_extensions_str;
missing_extensions_str = g_strjoinv (", ", missing_extensions);
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"Missing EGL extensions required for EGLDevice renderer: %s",
missing_extensions_str);
g_free (missing_extensions_str);
g_free (missing_extensions);
return EGL_NO_DEVICE_EXT;
}
if (!meta_egl_query_devices (egl, 0, NULL, &num_devices, error))
return EGL_NO_DEVICE_EXT;
devices = g_new0 (EGLDeviceEXT, num_devices);
if (!meta_egl_query_devices (egl, num_devices, devices, &num_devices,
error))
{
g_free (devices);
return EGL_NO_DEVICE_EXT;
}
kms_file_path = meta_gpu_kms_get_file_path (gpu_kms);
device = EGL_NO_DEVICE_EXT;
for (i = 0; i < num_devices; i++)
{
const char *egl_device_drm_path;
g_clear_error (error);
egl_device_drm_path = get_drm_device_file (egl, devices[i], error);
if (!egl_device_drm_path)
continue;
if (g_str_equal (egl_device_drm_path, kms_file_path))
{
device = devices[i];
break;
}
}
g_free (devices);
if (device == EGL_NO_DEVICE_EXT)
{
if (!*error)
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"Failed to find matching EGLDeviceEXT");
return EGL_NO_DEVICE_EXT;
}
return device;
}
static EGLDisplay
get_egl_device_display (MetaRendererNative *renderer_native,
MetaGpuKms *gpu_kms,
EGLDeviceEXT egl_device,
GError **error)
{
MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
int kms_fd = meta_gpu_kms_get_fd (gpu_kms);
EGLint platform_attribs[] = {
EGL_DRM_MASTER_FD_EXT, kms_fd,
EGL_NONE
};
return meta_egl_get_platform_display (egl, EGL_PLATFORM_DEVICE_EXT,
(void *) egl_device,
platform_attribs,
error);
}
static MetaRendererNativeGpuData *
create_renderer_gpu_data_egl_device (MetaRendererNative *renderer_native,
MetaGpuKms *gpu_kms,
GError **error)
{
MetaMonitorManagerKms *monitor_manager_kms =
renderer_native->monitor_manager_kms;
MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
MetaGpuKms *primary_gpu;
char **missing_extensions;
EGLDeviceEXT egl_device;
EGLDisplay egl_display;
MetaRendererNativeGpuData *renderer_gpu_data;
if (!meta_is_stage_views_enabled())
{
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"EGLDevice requires stage views enabled");
return NULL;
}
primary_gpu = meta_monitor_manager_kms_get_primary_gpu (monitor_manager_kms);
if (gpu_kms != primary_gpu)
{
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"EGLDevice currently only works with single GPU systems");
return NULL;
}
egl_device = find_egl_device (renderer_native, gpu_kms, error);
if (egl_device == EGL_NO_DEVICE_EXT)
return NULL;
egl_display = get_egl_device_display (renderer_native, gpu_kms,
egl_device, error);
if (egl_display == EGL_NO_DISPLAY)
return NULL;
if (!meta_egl_initialize (egl, egl_display, error))
return NULL;
if (!meta_egl_has_extensions (egl,
egl_display,
&missing_extensions,
"EGL_NV_output_drm_flip_event",
"EGL_EXT_output_base",
"EGL_EXT_output_drm",
"EGL_KHR_stream",
"EGL_KHR_stream_producer_eglsurface",
"EGL_EXT_stream_consumer_egloutput",
"EGL_EXT_stream_acquire_mode",
NULL))
{
char *missing_extensions_str;
missing_extensions_str = g_strjoinv (", ", missing_extensions);
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"Missing EGL extensions required for EGLDevice renderer: %s",
missing_extensions_str);
g_free (missing_extensions_str);
g_free (missing_extensions);
return NULL;
}
renderer_gpu_data = meta_create_renderer_native_gpu_data (gpu_kms);
renderer_gpu_data->renderer_native = renderer_native;
renderer_gpu_data->egl.device = egl_device;
renderer_gpu_data->mode = META_RENDERER_NATIVE_MODE_EGL_DEVICE;
renderer_gpu_data->egl_display = egl_display;
return renderer_gpu_data;
}
#endif /* HAVE_EGL_DEVICE */
static MetaRendererNativeGpuData *
meta_renderer_native_create_renderer_gpu_data (MetaRendererNative *renderer_native,
MetaGpuKms *gpu_kms,
GError **error)
{
MetaRendererNativeGpuData *renderer_gpu_data;
GError *gbm_error = NULL;
#ifdef HAVE_EGL_DEVICE
GError *egl_device_error = NULL;
#endif
#ifdef HAVE_EGL_DEVICE
/* Try to initialize the EGLDevice backend first. Whenever we use a
* non-NVIDIA GPU, the EGLDevice enumeration function won't find a match, and
* we'll fall back to GBM (which will always succeed as it has a software
* rendering fallback)
*/
renderer_gpu_data = create_renderer_gpu_data_egl_device (renderer_native,
gpu_kms,
&egl_device_error);
if (renderer_gpu_data)
return renderer_gpu_data;
#endif
renderer_gpu_data = create_renderer_gpu_data_gbm (renderer_native,
gpu_kms,
&gbm_error);
if (renderer_gpu_data)
{
#ifdef HAVE_EGL_DEVICE
g_error_free (egl_device_error);
#endif
return renderer_gpu_data;
}
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"Failed to initialize renderer: "
"%s"
#ifdef HAVE_EGL_DEVICE
", %s"
#endif
, gbm_error->message
#ifdef HAVE_EGL_DEVICE
, egl_device_error->message
#endif
);
g_error_free (gbm_error);
#ifdef HAVE_EGL_DEVICE
g_error_free (egl_device_error);
#endif
return NULL;
}
static gboolean
meta_renderer_native_initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
MetaRendererNative *renderer_native = META_RENDERER_NATIVE (initable);
MetaMonitorManagerKms *monitor_manager_kms =
renderer_native->monitor_manager_kms;
MetaMonitorManager *monitor_manager =
META_MONITOR_MANAGER (monitor_manager_kms);
GList *gpus;
GList *l;
gpus = meta_monitor_manager_get_gpus (monitor_manager);
for (l = gpus; l; l = l->next)
{
MetaGpuKms *gpu_kms = META_GPU_KMS (l->data);
MetaRendererNativeGpuData *renderer_gpu_data;
renderer_gpu_data =
meta_renderer_native_create_renderer_gpu_data (renderer_native,
gpu_kms,
error);
if (!renderer_gpu_data)
return FALSE;
g_hash_table_insert (renderer_native->gpu_datas,
gpu_kms,
renderer_gpu_data);
}
return TRUE;
}
static void
initable_iface_init (GInitableIface *initable_iface)
{
initable_iface->init = meta_renderer_native_initable_init;
}
static void
meta_renderer_native_finalize (GObject *object)
{
MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object);
g_hash_table_destroy (renderer_native->gpu_datas);
g_clear_object (&renderer_native->gles3);
G_OBJECT_CLASS (meta_renderer_native_parent_class)->finalize (object);
}
static void
meta_renderer_native_constructed (GObject *object)
{
MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object);
MetaMonitorManager *monitor_manager =
META_MONITOR_MANAGER (renderer_native->monitor_manager_kms);
MetaBackend *backend = meta_monitor_manager_get_backend (monitor_manager);
MetaSettings *settings = meta_backend_get_settings (backend);
if (meta_settings_is_experimental_feature_enabled (
settings, META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS))
renderer_native->use_modifiers = TRUE;
G_OBJECT_CLASS (meta_renderer_native_parent_class)->constructed (object);
}
static void
meta_renderer_native_init (MetaRendererNative *renderer_native)
{
renderer_native->gpu_datas =
g_hash_table_new_full (NULL, NULL,
NULL,
(GDestroyNotify) meta_renderer_native_gpu_data_free);
}
static void
meta_renderer_native_class_init (MetaRendererNativeClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
MetaRendererClass *renderer_class = META_RENDERER_CLASS (klass);
object_class->get_property = meta_renderer_native_get_property;
object_class->set_property = meta_renderer_native_set_property;
object_class->finalize = meta_renderer_native_finalize;
object_class->constructed = meta_renderer_native_constructed;
renderer_class->create_cogl_renderer = meta_renderer_native_create_cogl_renderer;
renderer_class->create_view = meta_renderer_native_create_view;
obj_props[PROP_MONITOR_MANAGER] =
g_param_spec_object ("monitor-manager",
"monitor-manager",
"MetaMonitorManagerKms",
META_TYPE_MONITOR_MANAGER_KMS,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, PROP_LAST, obj_props);
}
MetaRendererNative *
meta_renderer_native_new (MetaMonitorManagerKms *monitor_manager_kms,
GError **error)
{
return g_initable_new (META_TYPE_RENDERER_NATIVE,
NULL,
error,
"monitor-manager", monitor_manager_kms,
NULL);
}