From 3e4ece50d3fbf60bbc241129ba11c642588f4a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Sat, 17 Oct 2020 23:08:28 +0200 Subject: [PATCH] renderer/native: Move out CoglOnscreen code to separate file To get meta-renderer-native.c down to a bit more managable size, and to isolate "onscreen" functionality from other (at least partly), move out the things related to CoglOnscreen to meta-onscreen-native.[ch]. A couple of structs are moved to a new shared header file, as abstracting those types (e.g. (primary, secondary) render devices) will be dealt with later. Part-of: --- .../native/meta-backend-native-types.h | 2 + src/backends/native/meta-onscreen-native.c | 2196 +++++++++++++++ src/backends/native/meta-onscreen-native.h | 69 + .../native/meta-renderer-native-private.h | 113 + src/backends/native/meta-renderer-native.c | 2389 +---------------- src/meson.build | 5 +- 6 files changed, 2498 insertions(+), 2276 deletions(-) create mode 100644 src/backends/native/meta-onscreen-native.c create mode 100644 src/backends/native/meta-onscreen-native.h create mode 100644 src/backends/native/meta-renderer-native-private.h diff --git a/src/backends/native/meta-backend-native-types.h b/src/backends/native/meta-backend-native-types.h index ba73b4a56..0668a84e4 100644 --- a/src/backends/native/meta-backend-native-types.h +++ b/src/backends/native/meta-backend-native-types.h @@ -25,5 +25,7 @@ typedef struct _MetaBackendNative MetaBackendNative; typedef struct _MetaSeatNative MetaSeatNative; typedef struct _MetaSeatImpl MetaSeatImpl; typedef struct _MetaKeymapNative MetaKeymapNative; +typedef struct _MetaRendererNative MetaRendererNative; +typedef struct _MetaGpuKms MetaGpuKms; #endif /* META_BACKEND_NATIVE_TYPES_H */ diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c new file mode 100644 index 000000000..1617668ce --- /dev/null +++ b/src/backends/native/meta-onscreen-native.c @@ -0,0 +1,2196 @@ +/* + * Copyright (C) 2011 Intel Corporation. + * Copyright (C) 2016-2020 Red Hat + * Copyright (c) 2018,2019 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. + * + */ + +#include "config.h" + +#include "backends/native/meta-onscreen-native.h" + +#include + +#include "backends/meta-egl-ext.h" +#include "backends/native/meta-cogl-utils.h" +#include "backends/native/meta-crtc-kms.h" +#include "backends/native/meta-drm-buffer-dumb.h" +#include "backends/native/meta-drm-buffer-gbm.h" +#include "backends/native/meta-drm-buffer-import.h" +#include "backends/native/meta-drm-buffer.h" +#include "backends/native/meta-kms-utils.h" +#include "backends/native/meta-kms.h" +#include "backends/native/meta-output-kms.h" +#include "backends/native/meta-renderer-native-gles3.h" +#include "backends/native/meta-renderer-native-private.h" + +typedef enum _MetaSharedFramebufferImportStatus +{ + /* Not tried importing yet. */ + META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE, + /* Tried before and failed. */ + META_SHARED_FRAMEBUFFER_IMPORT_STATUS_FAILED, + /* Tried before and succeeded. */ + META_SHARED_FRAMEBUFFER_IMPORT_STATUS_OK +} MetaSharedFramebufferImportStatus; + +typedef struct _MetaOnscreenNativeSecondaryGpuState +{ + MetaGpuKms *gpu_kms; + MetaRendererNativeGpuData *renderer_gpu_data; + + EGLSurface egl_surface; + + struct { + struct gbm_surface *surface; + MetaDrmBuffer *current_fb; + MetaDrmBuffer *next_fb; + } gbm; + + struct { + MetaDrmBufferDumb *current_dumb_fb; + MetaDrmBufferDumb *dumb_fbs[2]; + } cpu; + + gboolean noted_primary_gpu_copy_ok; + gboolean noted_primary_gpu_copy_failed; + MetaSharedFramebufferImportStatus import_status; +} MetaOnscreenNativeSecondaryGpuState; + +typedef struct _MetaOnscreenNative +{ + MetaRendererNative *renderer_native; + MetaGpuKms *render_gpu; + MetaOutput *output; + MetaCrtc *crtc; + + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; + + struct { + struct gbm_surface *surface; + MetaDrmBuffer *current_fb; + MetaDrmBuffer *next_fb; + } gbm; + +#ifdef HAVE_EGL_DEVICE + struct { + EGLStreamKHR stream; + + MetaDrmBufferDumb *dumb_fb; + } egl; +#endif + + MetaRendererView *view; +} MetaOnscreenNative; + +static void +swap_secondary_drm_fb (CoglOnscreen *onscreen) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; + + secondary_gpu_state = onscreen_native->secondary_gpu_state; + if (!secondary_gpu_state) + return; + + g_set_object (&secondary_gpu_state->gbm.current_fb, + secondary_gpu_state->gbm.next_fb); + g_clear_object (&secondary_gpu_state->gbm.next_fb); +} + +static void +free_current_secondary_bo (CoglOnscreen *onscreen) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; + + secondary_gpu_state = onscreen_native->secondary_gpu_state; + if (!secondary_gpu_state) + return; + + g_clear_object (&secondary_gpu_state->gbm.current_fb); +} + +static void +free_current_bo (CoglOnscreen *onscreen) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + + g_clear_object (&onscreen_native->gbm.current_fb); + free_current_secondary_bo (onscreen); +} + +static void +meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + + if (!onscreen_native->gbm.next_fb) + return; + + free_current_bo (onscreen); + + g_set_object (&onscreen_native->gbm.current_fb, onscreen_native->gbm.next_fb); + g_clear_object (&onscreen_native->gbm.next_fb); + + swap_secondary_drm_fb (onscreen); +} + +static void +maybe_update_frame_info (MetaCrtc *crtc, + CoglFrameInfo *frame_info, + int64_t time_ns) +{ + const MetaCrtcConfig *crtc_config; + const MetaCrtcModeInfo *crtc_mode_info; + float refresh_rate; + + g_return_if_fail (crtc); + + crtc_config = meta_crtc_get_config (crtc); + if (!crtc_config) + return; + + crtc_mode_info = meta_crtc_mode_get_info (crtc_config->mode); + refresh_rate = crtc_mode_info->refresh_rate; + if (refresh_rate >= frame_info->refresh_rate) + { + frame_info->presentation_time = time_ns; + frame_info->refresh_rate = refresh_rate; + } +} + +static void +meta_onscreen_native_notify_frame_complete (CoglOnscreen *onscreen) +{ + CoglFrameInfo *info; + + info = cogl_onscreen_pop_head_frame_info (onscreen); + + g_assert (!cogl_onscreen_peek_head_frame_info (onscreen)); + + _cogl_onscreen_notify_frame_sync (onscreen, info); + _cogl_onscreen_notify_complete (onscreen, info); + cogl_object_unref (info); +} + +static void +notify_view_crtc_presented (MetaRendererView *view, + MetaKmsCrtc *kms_crtc, + int64_t time_ns) +{ + ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view); + CoglFramebuffer *framebuffer = + clutter_stage_view_get_onscreen (stage_view); + CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaRendererNative *renderer_native = onscreen_native->renderer_native; + CoglFrameInfo *frame_info; + MetaCrtc *crtc; + MetaRendererNativeGpuData *renderer_gpu_data; + + frame_info = cogl_onscreen_peek_head_frame_info (onscreen); + + crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc)); + maybe_update_frame_info (crtc, frame_info, time_ns); + + meta_onscreen_native_notify_frame_complete (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 int64_t +timeval_to_nanoseconds (const struct timeval *tv) +{ + int64_t usec = ((int64_t) tv->tv_sec) * G_USEC_PER_SEC + tv->tv_usec; + int64_t nsec = usec * 1000; + + return nsec; +} + +static void +page_flip_feedback_flipped (MetaKmsCrtc *kms_crtc, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + gpointer user_data) +{ + MetaRendererView *view = user_data; + struct timeval page_flip_time; + + page_flip_time = (struct timeval) { + .tv_sec = tv_sec, + .tv_usec = tv_usec, + }; + + notify_view_crtc_presented (view, kms_crtc, + timeval_to_nanoseconds (&page_flip_time)); +} + +static void +page_flip_feedback_ready (MetaKmsCrtc *kms_crtc, + gpointer user_data) +{ + MetaRendererView *view = user_data; + CoglFramebuffer *framebuffer = + clutter_stage_view_get_onscreen (CLUTTER_STAGE_VIEW (view)); + CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); + CoglFrameInfo *frame_info; + + frame_info = cogl_onscreen_peek_head_frame_info (onscreen); + frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; + + meta_onscreen_native_notify_frame_complete (onscreen); +} + +static void +page_flip_feedback_mode_set_fallback (MetaKmsCrtc *kms_crtc, + gpointer user_data) +{ + MetaRendererView *view = user_data; + MetaCrtc *crtc; + MetaGpuKms *gpu_kms; + int64_t now_ns; + + /* + * We ended up not page flipping, thus we don't have a presentation time to + * use. Lets use the next best thing: the current time. + */ + + crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc)); + gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); + now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms); + + notify_view_crtc_presented (view, kms_crtc, now_ns); +} + +static void +page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc, + gpointer user_data, + const GError *error) +{ + MetaRendererView *view = user_data; + MetaCrtc *crtc; + MetaGpuKms *gpu_kms; + int64_t now_ns; + + /* + * Page flipping failed, but we want to fail gracefully, so to avoid freezing + * the frame clack, pretend we flipped. + */ + + if (error && + !g_error_matches (error, + G_IO_ERROR, + G_IO_ERROR_PERMISSION_DENIED)) + g_warning ("Page flip discarded: %s", error->message); + + crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc)); + gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); + now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms); + + notify_view_crtc_presented (view, kms_crtc, now_ns); +} + +static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = { + .flipped = page_flip_feedback_flipped, + .ready = page_flip_feedback_ready, + .mode_set_fallback = page_flip_feedback_mode_set_fallback, + .discarded = page_flip_feedback_discarded, +}; + +static MetaEgl * +meta_onscreen_native_get_egl (MetaOnscreenNative *onscreen_native) +{ + MetaRendererNative *renderer_native = onscreen_native->renderer_native; + + return meta_renderer_native_get_egl (renderer_native); +} + +#ifdef HAVE_EGL_DEVICE +static int +custom_egl_stream_page_flip (gpointer custom_page_flip_data, + gpointer user_data) +{ + MetaOnscreenNative *onscreen_native = custom_page_flip_data; + MetaRendererView *view = user_data; + MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); + MetaRendererNativeGpuData *renderer_gpu_data; + EGLDisplay *egl_display; + EGLAttrib *acquire_attribs; + g_autoptr (GError) error = NULL; + + acquire_attribs = (EGLAttrib[]) { + EGL_DRM_FLIP_EVENT_DATA_NV, + (EGLAttrib) view, + EGL_NONE + }; + + renderer_gpu_data = + meta_renderer_native_get_gpu_data (onscreen_native->renderer_native, + onscreen_native->render_gpu); + + 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 (g_error_matches (error, META_EGL_ERROR, EGL_RESOURCE_BUSY_EXT)) + return -EBUSY; + else + return -EINVAL; + } + + return 0; +} +#endif /* HAVE_EGL_DEVICE */ + +void +meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen) +{ + CoglFrameInfo *frame_info; + + meta_onscreen_native_swap_drm_fb (onscreen); + + frame_info = cogl_onscreen_peek_tail_frame_info (onscreen); + frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; + meta_onscreen_native_notify_frame_complete (onscreen); +} + +static void +meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, + MetaRendererView *view, + MetaCrtc *crtc, + MetaKmsPageFlipListenerFlag flags) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaRendererNative *renderer_native = onscreen_native->renderer_native; + MetaGpuKms *render_gpu = onscreen_native->render_gpu; + MetaCrtcKms *crtc_kms = META_CRTC_KMS (crtc); + MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); + MetaRendererNativeGpuData *renderer_gpu_data; + MetaGpuKms *gpu_kms; + MetaKmsDevice *kms_device; + MetaKms *kms; + MetaKmsUpdate *kms_update; + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state = NULL; + MetaDrmBuffer *buffer; + + COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeFlipCrtcs, + "Onscreen (flip CRTCs)"); + + gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); + kms_device = meta_gpu_kms_get_kms_device (gpu_kms); + kms = meta_kms_device_get_kms (kms_device); + kms_update = meta_kms_ensure_pending_update (kms, kms_device); + + g_assert (meta_gpu_kms_is_crtc_active (gpu_kms, crtc)); + + 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) + { + buffer = onscreen_native->gbm.next_fb; + } + else + { + secondary_gpu_state = onscreen_native->secondary_gpu_state; + buffer = secondary_gpu_state->gbm.next_fb; + } + + meta_crtc_kms_assign_primary_plane (crtc_kms, buffer, kms_update); + + break; +#ifdef HAVE_EGL_DEVICE + case META_RENDERER_NATIVE_MODE_EGL_DEVICE: + meta_kms_update_set_custom_page_flip (kms_update, + custom_egl_stream_page_flip, + onscreen_native); + break; +#endif + } + + meta_kms_update_add_page_flip_listener (kms_update, + kms_crtc, + &page_flip_listener_vtable, + flags, + g_object_ref (view), + g_object_unref); +} + +static void +meta_onscreen_native_set_crtc_mode (CoglOnscreen *onscreen, + MetaRendererNativeGpuData *renderer_gpu_data) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); + MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); + MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); + MetaKms *kms = meta_kms_device_get_kms (kms_device); + MetaKmsUpdate *kms_update; + + COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeSetCrtcModes, + "Onscreen (set CRTC modes)"); + + kms_update = meta_kms_ensure_pending_update (kms, kms_device); + + switch (renderer_gpu_data->mode) + { + case META_RENDERER_NATIVE_MODE_GBM: + break; +#ifdef HAVE_EGL_DEVICE + case META_RENDERER_NATIVE_MODE_EGL_DEVICE: + { + MetaDrmBuffer *buffer; + + buffer = META_DRM_BUFFER (onscreen_native->egl.dumb_fb); + meta_crtc_kms_assign_primary_plane (crtc_kms, buffer, kms_update); + break; + } +#endif + } + + meta_crtc_kms_set_mode (crtc_kms, kms_update); + meta_output_kms_set_underscan (META_OUTPUT_KMS (onscreen_native->output), + kms_update); +} + +static void +secondary_gpu_release_dumb (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) +{ + unsigned i; + + for (i = 0; i < G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); i++) + g_clear_object (&secondary_gpu_state->cpu.dumb_fbs[i]); +} + +static void +secondary_gpu_state_free (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) +{ + MetaBackend *backend = meta_get_backend (); + MetaEgl *egl = meta_backend_get_egl (backend); + + 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); + } + + g_clear_object (&secondary_gpu_state->gbm.current_fb); + g_clear_object (&secondary_gpu_state->gbm.next_fb); + g_clear_pointer (&secondary_gpu_state->gbm.surface, gbm_surface_destroy); + + secondary_gpu_release_dumb (secondary_gpu_state); + + g_free (secondary_gpu_state); +} + +static gboolean +import_shared_framebuffer (CoglOnscreen *onscreen, + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaGpuKms *gpu_kms; + MetaKmsDevice *kms_device; + struct gbm_device *gbm_device; + MetaDrmBufferGbm *buffer_gbm; + MetaDrmBufferImport *buffer_import; + g_autoptr (GError) error = NULL; + + buffer_gbm = META_DRM_BUFFER_GBM (onscreen_native->gbm.next_fb); + + gpu_kms = secondary_gpu_state->gpu_kms; + kms_device = meta_gpu_kms_get_kms_device (gpu_kms); + gbm_device = meta_gbm_device_from_gpu (gpu_kms); + buffer_import = meta_drm_buffer_import_new (kms_device, + gbm_device, + buffer_gbm, + &error); + if (!buffer_import) + { + g_debug ("Zero-copy disabled for %s, meta_drm_buffer_import_new failed: %s", + meta_gpu_kms_get_file_path (secondary_gpu_state->gpu_kms), + error->message); + + g_warn_if_fail (secondary_gpu_state->import_status == + META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE); + + /* + * Fall back. If META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE is + * in effect, we have COPY_MODE_PRIMARY prepared already, so we + * simply retry with that path. Import status cannot be FAILED, + * because we should not retry if failed once. + * + * If import status is OK, that is unexpected and we do not + * have the fallback path prepared which means this output cannot + * work anymore. + */ + secondary_gpu_state->renderer_gpu_data->secondary.copy_mode = + META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY; + + secondary_gpu_state->import_status = + META_SHARED_FRAMEBUFFER_IMPORT_STATUS_FAILED; + return FALSE; + } + + /* + * next_fb may already contain a fallback buffer, so clear it only + * when we are sure to succeed. + */ + g_clear_object (&secondary_gpu_state->gbm.next_fb); + secondary_gpu_state->gbm.next_fb = META_DRM_BUFFER (buffer_import); + + if (secondary_gpu_state->import_status == + META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE) + { + /* + * Clean up the cpu-copy part of + * init_secondary_gpu_state_cpu_copy_mode () + */ + secondary_gpu_release_dumb (secondary_gpu_state); + + g_debug ("Using zero-copy for %s succeeded once.", + meta_gpu_kms_get_file_path (secondary_gpu_state->gpu_kms)); + } + + secondary_gpu_state->import_status = + META_SHARED_FRAMEBUFFER_IMPORT_STATUS_OK; + return TRUE; +} + +static void +copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, + MetaRendererNativeGpuData *renderer_gpu_data, + gboolean *egl_context_changed) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; + MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); + MetaGles3 *gles3 = meta_renderer_native_get_gles3 (renderer_native); + GError *error = NULL; + gboolean use_modifiers; + MetaKmsDevice *kms_device; + MetaDrmBufferGbm *buffer_gbm; + struct gbm_bo *bo; + + COGL_TRACE_BEGIN_SCOPED (CopySharedFramebufferSecondaryGpu, + "FB Copy (secondary GPU)"); + + g_warn_if_fail (secondary_gpu_state->gbm.next_fb == NULL); + g_clear_object (&secondary_gpu_state->gbm.next_fb); + + 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; + + + buffer_gbm = META_DRM_BUFFER_GBM (onscreen_native->gbm.next_fb); + bo = meta_drm_buffer_gbm_get_bo (buffer_gbm); + if (!meta_renderer_native_gles3_blit_shared_bo (egl, + gles3, + renderer_gpu_data->egl_display, + renderer_gpu_data->secondary.egl_context, + secondary_gpu_state->egl_surface, + 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; + } + + use_modifiers = meta_renderer_native_use_modifiers (renderer_native); + kms_device = meta_gpu_kms_get_kms_device (secondary_gpu_state->gpu_kms); + buffer_gbm = + meta_drm_buffer_gbm_new_lock_front (kms_device, + secondary_gpu_state->gbm.surface, + use_modifiers, + &error); + if (!buffer_gbm) + { + g_warning ("meta_drm_buffer_gbm_new_lock_front failed: %s", + error->message); + g_error_free (error); + return; + } + + secondary_gpu_state->gbm.next_fb = META_DRM_BUFFER (buffer_gbm); +} + +static MetaDrmBufferDumb * +secondary_gpu_get_next_dumb_buffer (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) +{ + MetaDrmBufferDumb *current_dumb_fb; + + current_dumb_fb = secondary_gpu_state->cpu.current_dumb_fb; + if (current_dumb_fb == secondary_gpu_state->cpu.dumb_fbs[0]) + return secondary_gpu_state->cpu.dumb_fbs[1]; + else + return secondary_gpu_state->cpu.dumb_fbs[0]; +} + +static gboolean +copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscreen, + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaRendererNative *renderer_native = onscreen_native->renderer_native; + MetaGpuKms *primary_gpu; + MetaRendererNativeGpuData *primary_gpu_data; + MetaDrmBufferDumb *buffer_dumb; + MetaDrmBuffer *buffer; + int width, height, stride; + uint32_t drm_format; + CoglFramebuffer *dmabuf_fb; + int dmabuf_fd; + g_autoptr (GError) error = NULL; + CoglPixelFormat cogl_format; + int ret; + + COGL_TRACE_BEGIN_SCOPED (CopySharedFramebufferPrimaryGpu, + "FB Copy (primary GPU)"); + + primary_gpu = meta_renderer_native_get_primary_gpu (renderer_native); + primary_gpu_data = + meta_renderer_native_get_gpu_data (renderer_native, primary_gpu); + if (!primary_gpu_data->secondary.has_EGL_EXT_image_dma_buf_import_modifiers) + return FALSE; + + buffer_dumb = secondary_gpu_get_next_dumb_buffer (secondary_gpu_state); + buffer = META_DRM_BUFFER (buffer_dumb); + + width = meta_drm_buffer_get_width (buffer); + height = meta_drm_buffer_get_height (buffer); + stride = meta_drm_buffer_get_stride (buffer); + drm_format = meta_drm_buffer_get_format (buffer); + + g_assert (cogl_framebuffer_get_width (framebuffer) == width); + g_assert (cogl_framebuffer_get_height (framebuffer) == height); + + ret = meta_cogl_pixel_format_from_drm_format (drm_format, + &cogl_format, + NULL); + g_assert (ret); + + dmabuf_fd = meta_drm_buffer_dumb_ensure_dmabuf_fd (buffer_dumb, &error); + if (!dmabuf_fd) + { + g_debug ("Failed to create DMA buffer: %s", error->message); + return FALSE; + } + + dmabuf_fb = + meta_renderer_native_create_dma_buf_framebuffer (renderer_native, + dmabuf_fd, + width, + height, + stride, + 0, DRM_FORMAT_MOD_LINEAR, + drm_format, + &error); + + if (error) + { + g_debug ("%s: Failed to blit DMA buffer image: %s", + G_STRFUNC, error->message); + return FALSE; + } + + if (!cogl_blit_framebuffer (framebuffer, COGL_FRAMEBUFFER (dmabuf_fb), + 0, 0, 0, 0, + width, height, + &error)) + { + g_object_unref (dmabuf_fb); + return FALSE; + } + + g_object_unref (dmabuf_fb); + + g_clear_object (&secondary_gpu_state->gbm.next_fb); + secondary_gpu_state->gbm.next_fb = buffer; + secondary_gpu_state->cpu.current_dumb_fb = buffer_dumb; + + return TRUE; +} + +static void +copy_shared_framebuffer_cpu (CoglOnscreen *onscreen, + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, + MetaRendererNativeGpuData *renderer_gpu_data) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); + MetaDrmBufferDumb *buffer_dumb; + MetaDrmBuffer *buffer; + int width, height, stride; + uint32_t drm_format; + void *buffer_data; + CoglBitmap *dumb_bitmap; + CoglPixelFormat cogl_format; + gboolean ret; + + COGL_TRACE_BEGIN_SCOPED (CopySharedFramebufferCpu, + "FB Copy (CPU)"); + + buffer_dumb = secondary_gpu_get_next_dumb_buffer (secondary_gpu_state); + buffer = META_DRM_BUFFER (buffer_dumb); + + width = meta_drm_buffer_get_width (buffer); + height = meta_drm_buffer_get_height (buffer); + stride = meta_drm_buffer_get_stride (buffer); + drm_format = meta_drm_buffer_get_format (buffer); + buffer_data = meta_drm_buffer_dumb_get_data (buffer_dumb); + + g_assert (cogl_framebuffer_get_width (framebuffer) == width); + g_assert (cogl_framebuffer_get_height (framebuffer) == height); + + ret = meta_cogl_pixel_format_from_drm_format (drm_format, + &cogl_format, + NULL); + g_assert (ret); + + dumb_bitmap = cogl_bitmap_new_for_data (cogl_context, + width, + height, + cogl_format, + stride, + buffer_data); + + if (!cogl_framebuffer_read_pixels_into_bitmap (framebuffer, + 0 /* x */, + 0 /* y */, + COGL_READ_PIXELS_COLOR_BUFFER, + dumb_bitmap)) + g_warning ("Failed to CPU-copy to a secondary GPU output"); + + cogl_object_unref (dumb_bitmap); + + g_clear_object (&secondary_gpu_state->gbm.next_fb); + secondary_gpu_state->gbm.next_fb = buffer; + secondary_gpu_state->cpu.current_dumb_fb = buffer_dumb; +} + +static void +update_secondary_gpu_state_pre_swap_buffers (CoglOnscreen *onscreen) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; + + COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeGpuStatePreSwapBuffers, + "Onscreen (secondary gpu pre-swap-buffers)"); + + secondary_gpu_state = onscreen_native->secondary_gpu_state; + if (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_SECONDARY_GPU: + /* Done after eglSwapBuffers. */ + break; + case META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO: + /* Done after eglSwapBuffers. */ + if (secondary_gpu_state->import_status == + META_SHARED_FRAMEBUFFER_IMPORT_STATUS_OK) + break; + /* prepare fallback */ + G_GNUC_FALLTHROUGH; + case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY: + if (!copy_shared_framebuffer_primary_gpu (onscreen, + secondary_gpu_state)) + { + if (!secondary_gpu_state->noted_primary_gpu_copy_failed) + { + g_debug ("Using primary GPU to copy for %s failed once.", + meta_gpu_kms_get_file_path (secondary_gpu_state->gpu_kms)); + secondary_gpu_state->noted_primary_gpu_copy_failed = TRUE; + } + + copy_shared_framebuffer_cpu (onscreen, + secondary_gpu_state, + renderer_gpu_data); + } + else if (!secondary_gpu_state->noted_primary_gpu_copy_ok) + { + g_debug ("Using primary GPU to copy for %s succeeded once.", + meta_gpu_kms_get_file_path (secondary_gpu_state->gpu_kms)); + secondary_gpu_state->noted_primary_gpu_copy_ok = TRUE; + } + break; + } + } +} + +static void +update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen, + gboolean *egl_context_changed) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaRendererNative *renderer_native = onscreen_native->renderer_native; + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; + + COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeGpuStatePostSwapBuffers, + "Onscreen (secondary gpu post-swap-buffers)"); + + secondary_gpu_state = onscreen_native->secondary_gpu_state; + if (secondary_gpu_state) + { + MetaRendererNativeGpuData *renderer_gpu_data; + + renderer_gpu_data = + meta_renderer_native_get_gpu_data (renderer_native, + secondary_gpu_state->gpu_kms); +retry: + switch (renderer_gpu_data->secondary.copy_mode) + { + case META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO: + if (!import_shared_framebuffer (onscreen, + secondary_gpu_state)) + goto retry; + break; + case META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU: + copy_shared_framebuffer_gpu (onscreen, + secondary_gpu_state, + renderer_gpu_data, + egl_context_changed); + break; + case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY: + /* Done before eglSwapBuffers. */ + break; + } + } +} + +static void +ensure_crtc_modes (CoglOnscreen *onscreen) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); + 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; + + if (meta_renderer_native_pop_pending_mode_set (renderer_native, + onscreen_native->view)) + meta_onscreen_native_set_crtc_mode (onscreen, renderer_gpu_data); +} + +void +meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + const int *rectangles, + int n_rectangles, + CoglFrameInfo *frame_info, + gpointer user_data) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); + 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; + MetaRenderer *renderer = META_RENDERER (renderer_native); + MetaBackend *backend = meta_renderer_get_backend (renderer); + MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + MetaKms *kms = meta_backend_native_get_kms (backend_native); + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaGpuKms *render_gpu = onscreen_native->render_gpu; + MetaKmsDevice *render_kms_device = meta_gpu_kms_get_kms_device (render_gpu); + ClutterFrame *frame = user_data; + const CoglWinsysVtable *parent_vtable; + gboolean egl_context_changed = FALSE; + gboolean use_modifiers; + MetaPowerSave power_save_mode; + g_autoptr (GError) error = NULL; + MetaDrmBufferGbm *buffer_gbm; + MetaKmsCrtc *kms_crtc; + MetaKmsDevice *kms_device; + MetaKmsUpdateFlag flags; + g_autoptr (MetaKmsFeedback) kms_feedback = NULL; + const GError *feedback_error; + + COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers, + "Onscreen (swap-buffers)"); + + update_secondary_gpu_state_pre_swap_buffers (onscreen); + + parent_vtable = meta_get_renderer_native_parent_vtable (); + parent_vtable->onscreen_swap_buffers_with_damage (onscreen, + rectangles, + n_rectangles, + frame_info, + user_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: + g_warn_if_fail (onscreen_native->gbm.next_fb == NULL); + g_clear_object (&onscreen_native->gbm.next_fb); + + use_modifiers = meta_renderer_native_use_modifiers (renderer_native); + buffer_gbm = + meta_drm_buffer_gbm_new_lock_front (render_kms_device, + onscreen_native->gbm.surface, + use_modifiers, + &error); + if (!buffer_gbm) + { + g_warning ("meta_drm_buffer_gbm_new_lock_front failed: %s", + error->message); + return; + } + + onscreen_native->gbm.next_fb = META_DRM_BUFFER (buffer_gbm); + + 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); + + power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); + if (power_save_mode == META_POWER_SAVE_ON) + { + ensure_crtc_modes (onscreen); + meta_onscreen_native_flip_crtc (onscreen, + onscreen_native->view, + onscreen_native->crtc, + META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE); + } + else + { + meta_renderer_native_queue_power_save_page_flip (renderer_native, + onscreen); + clutter_frame_set_result (frame, + CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + return; + } + + /* + * 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); + + COGL_TRACE_BEGIN_SCOPED (MetaRendererNativePostKmsUpdate, + "Onscreen (post pending update)"); + kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (onscreen_native->crtc)); + kms_device = meta_kms_crtc_get_device (kms_crtc); + + switch (renderer_gpu_data->mode) + { + case META_RENDERER_NATIVE_MODE_GBM: + if (meta_renderer_native_has_pending_mode_sets (renderer_native)) + { + meta_topic (META_DEBUG_KMS, + "Postponing primary plane composite update for CRTC %u (%s)", + meta_kms_crtc_get_id (kms_crtc), + meta_kms_device_get_path (kms_device)); + + clutter_frame_set_result (frame, + CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + return; + } + else if (meta_renderer_native_has_pending_mode_set (renderer_native)) + { + meta_topic (META_DEBUG_KMS, "Posting global mode set updates on %s", + meta_kms_device_get_path (kms_device)); + + meta_renderer_native_notify_mode_sets_reset (renderer_native); + meta_renderer_native_post_mode_set_updates (renderer_native); + clutter_frame_set_result (frame, + CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + return; + } + break; +#ifdef HAVE_EGL_DEVICE + case META_RENDERER_NATIVE_MODE_EGL_DEVICE: + if (meta_renderer_native_has_pending_mode_set (renderer_native)) + { + meta_renderer_native_notify_mode_sets_reset (renderer_native); + meta_renderer_native_post_mode_set_updates (renderer_native); + clutter_frame_set_result (frame, + CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + return; + } + break; +#endif + } + + meta_topic (META_DEBUG_KMS, + "Posting primary plane composite update for CRTC %u (%s)", + meta_kms_crtc_get_id (kms_crtc), + meta_kms_device_get_path (kms_device)); + + flags = META_KMS_UPDATE_FLAG_NONE; + kms_feedback = meta_kms_post_pending_update_sync (kms, kms_device, flags); + + switch (meta_kms_feedback_get_result (kms_feedback)) + { + case META_KMS_FEEDBACK_PASSED: + clutter_frame_set_result (frame, + CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + break; + case META_KMS_FEEDBACK_FAILED: + clutter_frame_set_result (frame, + CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + + feedback_error = meta_kms_feedback_get_error (kms_feedback); + if (!g_error_matches (feedback_error, + G_IO_ERROR, + G_IO_ERROR_PERMISSION_DENIED)) + g_warning ("Failed to post KMS update: %s", feedback_error->message); + break; + } +} + +gboolean +meta_onscreen_native_is_buffer_scanout_compatible (CoglOnscreen *onscreen, + uint32_t drm_format, + uint64_t drm_modifier, + uint32_t stride) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + const MetaCrtcConfig *crtc_config; + MetaDrmBuffer *fb; + struct gbm_bo *gbm_bo; + + crtc_config = meta_crtc_get_config (onscreen_native->crtc); + if (crtc_config->transform != META_MONITOR_TRANSFORM_NORMAL) + return FALSE; + + if (onscreen_native->secondary_gpu_state) + return FALSE; + + if (!onscreen_native->gbm.surface) + return FALSE; + + fb = onscreen_native->gbm.current_fb ? onscreen_native->gbm.current_fb + : onscreen_native->gbm.next_fb; + if (!fb) + return FALSE; + + if (!META_IS_DRM_BUFFER_GBM (fb)) + return FALSE; + + gbm_bo = meta_drm_buffer_gbm_get_bo (META_DRM_BUFFER_GBM (fb)); + + if (gbm_bo_get_format (gbm_bo) != drm_format) + return FALSE; + + if (gbm_bo_get_modifier (gbm_bo) != drm_modifier) + return FALSE; + + if (gbm_bo_get_stride (gbm_bo) != stride) + return FALSE; + + return TRUE; +} + +gboolean +meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, + CoglScanout *scanout, + CoglFrameInfo *frame_info, + gpointer user_data, + GError **error) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaGpuKms *render_gpu = onscreen_native->render_gpu; + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); + 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; + MetaRenderer *renderer = META_RENDERER (renderer_native); + MetaBackend *backend = meta_renderer_get_backend (renderer); + MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); + MetaKms *kms = meta_backend_native_get_kms (backend_native); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + MetaPowerSave power_save_mode; + ClutterFrame *frame = user_data; + MetaKmsCrtc *kms_crtc; + MetaKmsDevice *kms_device; + MetaKmsUpdateFlag flags; + g_autoptr (MetaKmsFeedback) kms_feedback = NULL; + const GError *feedback_error; + + power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); + if (power_save_mode != META_POWER_SAVE_ON) + { + g_set_error_literal (error, + COGL_SCANOUT_ERROR, + COGL_SCANOUT_ERROR_INHIBITED, + NULL); + return FALSE; + } + + if (meta_renderer_native_has_pending_mode_set (renderer_native)) + { + g_set_error_literal (error, + COGL_SCANOUT_ERROR, + COGL_SCANOUT_ERROR_INHIBITED, + NULL); + return FALSE; + } + + renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, + render_gpu); + + g_warn_if_fail (renderer_gpu_data->mode == META_RENDERER_NATIVE_MODE_GBM); + g_warn_if_fail (!onscreen_native->gbm.next_fb); + + g_set_object (&onscreen_native->gbm.next_fb, META_DRM_BUFFER (scanout)); + + ensure_crtc_modes (onscreen); + meta_onscreen_native_flip_crtc (onscreen, + onscreen_native->view, + onscreen_native->crtc, + META_KMS_PAGE_FLIP_LISTENER_FLAG_NO_DISCARD); + + kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (onscreen_native->crtc)); + kms_device = meta_kms_crtc_get_device (kms_crtc); + + meta_topic (META_DEBUG_KMS, + "Posting direct scanout update for CRTC %u (%s)", + meta_kms_crtc_get_id (kms_crtc), + meta_kms_device_get_path (kms_device)); + + flags = META_KMS_UPDATE_FLAG_PRESERVE_ON_ERROR; + kms_feedback = meta_kms_post_pending_update_sync (kms, kms_device, flags); + switch (meta_kms_feedback_get_result (kms_feedback)) + { + case META_KMS_FEEDBACK_PASSED: + clutter_frame_set_result (frame, + CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + break; + case META_KMS_FEEDBACK_FAILED: + feedback_error = meta_kms_feedback_get_error (kms_feedback); + + if (g_error_matches (feedback_error, + G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) + break; + + g_clear_object (&onscreen_native->gbm.next_fb); + g_propagate_error (error, g_error_copy (feedback_error)); + return FALSE; + } + + return TRUE; +} + +static void +add_onscreen_frame_info (MetaCrtc *crtc) +{ + MetaGpu *gpu = meta_crtc_get_gpu (crtc); + MetaBackend *backend = meta_gpu_get_backend (gpu); + ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); + MetaStageNative *stage_native = + meta_clutter_backend_native_get_stage_native (clutter_backend); + MetaRenderer *renderer = meta_backend_get_renderer (backend); + MetaRendererView *view = meta_renderer_get_view_for_crtc (renderer, crtc); + + clutter_stage_cogl_add_onscreen_frame_info (CLUTTER_STAGE_COGL (stage_native), + CLUTTER_STAGE_VIEW (view)); +} + +void +meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, + ClutterFrame *frame) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaCrtc *crtc = onscreen_native->crtc; + MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (crtc)); + MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc);; + MetaKms *kms = meta_kms_device_get_kms (kms_device); + MetaKmsUpdateFlag flags; + MetaKmsUpdate *kms_update; + g_autoptr (MetaKmsFeedback) kms_feedback = NULL; + const GError *error; + + kms_update = meta_kms_get_pending_update (kms, kms_device); + if (!kms_update) + { + clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); + return; + } + + meta_kms_update_add_page_flip_listener (kms_update, + kms_crtc, + &page_flip_listener_vtable, + META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE, + g_object_ref (onscreen_native->view), + g_object_unref); + + flags = META_KMS_UPDATE_FLAG_NONE; + kms_feedback = meta_kms_post_pending_update_sync (kms, + kms_device, + flags); + switch (meta_kms_feedback_get_result (kms_feedback)) + { + case META_KMS_FEEDBACK_PASSED: + add_onscreen_frame_info (crtc); + clutter_frame_set_result (frame, + CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + break; + case META_KMS_FEEDBACK_FAILED: + add_onscreen_frame_info (crtc); + clutter_frame_set_result (frame, + CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + + error = meta_kms_feedback_get_error (kms_feedback); + if (!g_error_matches (error, + G_IO_ERROR, + G_IO_ERROR_PERMISSION_DENIED)) + g_warning ("Failed to post KMS update: %s", error->message); + break; + } +} + +static gboolean +should_surface_be_sharable (CoglOnscreen *onscreen) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + + if (META_GPU_KMS (meta_crtc_get_gpu (onscreen_native->crtc)) == + onscreen_native->render_gpu) + return FALSE; + else + return TRUE; +} + +static uint32_t +get_gbm_format_from_egl (MetaEgl *egl, + EGLDisplay egl_display, + EGLConfig egl_config) +{ + uint32_t gbm_format; + EGLint native_visual_id; + + if (meta_egl_get_config_attrib (egl, + egl_display, + egl_config, + EGL_NATIVE_VISUAL_ID, + &native_visual_id, + NULL)) + gbm_format = (uint32_t) native_visual_id; + else + g_assert_not_reached (); + + return gbm_format; +} + +static GArray * +get_supported_kms_modifiers (MetaCrtcKms *crtc_kms, + uint32_t format) +{ + GArray *modifiers; + GArray *crtc_mods; + unsigned int i; + + crtc_mods = meta_crtc_kms_get_modifiers (crtc_kms, format); + if (!crtc_mods) + return NULL; + + 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 < crtc_mods->len; i++) + { + uint64_t modifier = g_array_index (crtc_mods, uint64_t, i); + + g_array_append_val (modifiers, modifier); + } + + if (modifiers->len == 0) + { + g_array_free (modifiers, TRUE); + return NULL; + } + + return modifiers; +} + +static GArray * +get_supported_egl_modifiers (CoglOnscreen *onscreen, + MetaCrtcKms *crtc_kms, + uint32_t format) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaRendererNative *renderer_native = onscreen_native->renderer_native; + MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); + MetaGpu *gpu; + MetaRendererNativeGpuData *renderer_gpu_data; + EGLint num_modifiers; + GArray *modifiers; + GError *error = NULL; + gboolean ret; + + gpu = meta_crtc_get_gpu (META_CRTC (crtc_kms)); + 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 = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); + MetaGpu *gpu; + g_autoptr (GArray) modifiers = NULL; + + gpu = meta_crtc_get_gpu (META_CRTC (crtc_kms)); + if (gpu == META_GPU (onscreen_native->render_gpu)) + modifiers = get_supported_kms_modifiers (crtc_kms, format); + else + modifiers = get_supported_egl_modifiers (onscreen, crtc_kms, format); + + return g_steal_pointer (&modifiers); +} + +static GArray * +get_supported_kms_formats (CoglOnscreen *onscreen) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); + + return meta_crtc_kms_copy_drm_format_list (crtc_kms); +} + +static gboolean +create_surfaces_gbm (CoglOnscreen *onscreen, + int width, + int height, + struct gbm_surface **gbm_surface, + EGLSurface *egl_surface, + GError **error) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaRendererNative *renderer_native = onscreen_native->renderer_native; + MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); + 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; + GArray *modifiers; + + renderer_gpu_data = + meta_renderer_native_get_gpu_data (renderer_native, + onscreen_native->render_gpu); + + format = get_gbm_format_from_egl (egl, + cogl_renderer_egl->edpy, + cogl_display_egl->egl_config); + + if (meta_renderer_native_use_modifiers (renderer_native)) + 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 +create_surfaces_egl_device (CoglOnscreen *onscreen, + int width, + int height, + EGLStreamKHR *out_egl_stream, + EGLSurface *out_egl_surface, + GError **error) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); + 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; + 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, 0, + 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; + + output_attribs[0] = EGL_DRM_CRTC_EXT; + output_attribs[1] = meta_crtc_get_id (onscreen_native->crtc); + 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 */ + +void +meta_onscreen_native_set_view (CoglOnscreen *onscreen, + MetaRendererView *view) +{ + CoglOnscreenEgl *onscreen_egl; + MetaOnscreenNative *onscreen_native; + + onscreen_egl = cogl_onscreen_get_winsys (onscreen); + onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + onscreen_native->view = view; +} + +gboolean +meta_renderer_native_init_onscreen (CoglOnscreen *onscreen, + GError **error) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); + CoglDisplay *cogl_display = cogl_context->display; + CoglDisplayEGL *cogl_display_egl = cogl_display->winsys; + CoglOnscreenEgl *onscreen_egl; + MetaOnscreenNative *onscreen_native; + + g_return_val_if_fail (cogl_display_egl->egl_context, FALSE); + + onscreen_egl = cogl_onscreen_egl_new (); + cogl_onscreen_set_winsys (onscreen, onscreen_egl); + + onscreen_native = g_slice_new0 (MetaOnscreenNative); + cogl_onscreen_egl_set_platform (onscreen_egl, 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 +init_secondary_gpu_state_gpu_copy_mode (MetaRendererNative *renderer_native, + CoglOnscreen *onscreen, + MetaRendererNativeGpuData *renderer_gpu_data, + GError **error) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + 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; + MetaGpuKms *gpu_kms; + uint32_t format; + + width = cogl_framebuffer_get_width (framebuffer); + height = cogl_framebuffer_get_height (framebuffer); + format = get_gbm_format_from_egl (egl, + renderer_gpu_data->egl_display, + renderer_gpu_data->secondary.egl_config); + + gbm_surface = gbm_surface_create (renderer_gpu_data->gbm.device, + width, height, + format, + 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); + + gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (onscreen_native->crtc)); + 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; + + onscreen_native->secondary_gpu_state = secondary_gpu_state; + + return TRUE; +} + +static uint32_t +pick_secondary_gpu_framebuffer_format_for_cpu (CoglOnscreen *onscreen) +{ + /* + * cogl_framebuffer_read_pixels_into_bitmap () supported formats in + * preference order. Ideally these should depend on the render buffer + * format copy_shared_framebuffer_cpu () will be reading from but + * alpha channel ignored. + */ + static const uint32_t preferred_formats[] = + { + /* + * DRM_FORMAT_XBGR8888 a.k.a GL_RGBA, GL_UNSIGNED_BYTE on + * little-endian is possibly the most optimized glReadPixels + * output format. glReadPixels cannot avoid manufacturing an alpha + * channel if the render buffer does not have one and converting + * to ABGR8888 may be more optimized than ARGB8888. + */ + DRM_FORMAT_XBGR8888, + /* The rest are other fairly commonly used formats in OpenGL. */ + DRM_FORMAT_XRGB8888, + }; + g_autoptr (GArray) formats = NULL; + size_t k; + unsigned int i; + uint32_t drm_format; + + formats = get_supported_kms_formats (onscreen); + + /* Check if any of our preferred formats are supported. */ + for (k = 0; k < G_N_ELEMENTS (preferred_formats); k++) + { + g_assert (meta_cogl_pixel_format_from_drm_format (preferred_formats[k], + NULL, + NULL)); + + for (i = 0; i < formats->len; i++) + { + drm_format = g_array_index (formats, uint32_t, i); + + if (drm_format == preferred_formats[k]) + return drm_format; + } + } + + /* + * Otherwise just pick an arbitrary format we recognize. The formats + * list is not in any specific order and we don't know any better + * either. + */ + for (i = 0; i < formats->len; i++) + { + drm_format = g_array_index (formats, uint32_t, i); + + if (meta_cogl_pixel_format_from_drm_format (drm_format, NULL, NULL)) + return drm_format; + } + + return DRM_FORMAT_INVALID; +} + +static gboolean +init_secondary_gpu_state_cpu_copy_mode (MetaRendererNative *renderer_native, + CoglOnscreen *onscreen, + MetaRendererNativeGpuData *renderer_gpu_data, + GError **error) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; + MetaGpuKms *gpu_kms; + MetaKmsDevice *kms_device; + int width, height; + unsigned int i; + uint32_t drm_format; + MetaDrmFormatBuf tmp; + + drm_format = pick_secondary_gpu_framebuffer_format_for_cpu (onscreen); + if (drm_format == DRM_FORMAT_INVALID) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Could not find a suitable pixel format in CPU copy mode"); + return FALSE; + } + + width = cogl_framebuffer_get_width (framebuffer); + height = cogl_framebuffer_get_height (framebuffer); + + gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (onscreen_native->crtc)); + kms_device = meta_gpu_kms_get_kms_device (gpu_kms); + g_debug ("Secondary GPU %s using DRM format '%s' (0x%x) for a %dx%d output.", + meta_gpu_kms_get_file_path (gpu_kms), + meta_drm_format_to_string (&tmp, drm_format), + drm_format, + width, height); + + 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++) + { + secondary_gpu_state->cpu.dumb_fbs[i] = + meta_drm_buffer_dumb_new (kms_device, + width, height, + drm_format, + error); + if (!secondary_gpu_state->cpu.dumb_fbs[i]) + { + secondary_gpu_state_free (secondary_gpu_state); + return FALSE; + } + } + + /* + * This function initializes everything needed for + * META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO as well. + */ + secondary_gpu_state->import_status = + META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE; + + onscreen_native->secondary_gpu_state = secondary_gpu_state; + + return TRUE; +} + +static gboolean +init_secondary_gpu_state (MetaRendererNative *renderer_native, + CoglOnscreen *onscreen, + GError **error) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaGpu *gpu = meta_crtc_get_gpu (onscreen_native->crtc); + MetaRendererNativeGpuData *renderer_gpu_data; + + renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, + META_GPU_KMS (gpu)); + + switch (renderer_gpu_data->secondary.copy_mode) + { + case META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU: + if (!init_secondary_gpu_state_gpu_copy_mode (renderer_native, + onscreen, + renderer_gpu_data, + error)) + return FALSE; + break; + case META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO: + /* + * Initialize also the primary copy mode, so that if zero-copy + * path fails, which is quite likely, we can simply continue + * with the primary copy path on the very first frame. + */ + G_GNUC_FALLTHROUGH; + case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY: + if (!init_secondary_gpu_state_cpu_copy_mode (renderer_native, + onscreen, + renderer_gpu_data, + error)) + return FALSE; + break; + } + + return TRUE; +} + +CoglOnscreen * +meta_onscreen_native_new (MetaRendererNative *renderer_native, + MetaGpuKms *render_gpu, + MetaOutput *output, + MetaCrtc *crtc, + CoglContext *context, + int width, + int height, + GError **error) +{ + CoglOnscreen *onscreen; + CoglOnscreenEgl *onscreen_egl; + MetaOnscreenNative *onscreen_native; + + onscreen = cogl_onscreen_new (context, width, height); + + if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (onscreen), error)) + { + g_object_unref (onscreen); + return NULL; + } + + onscreen_egl = cogl_onscreen_get_winsys (onscreen); + onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + onscreen_native->renderer_native = renderer_native; + onscreen_native->render_gpu = render_gpu; + onscreen_native->output = output; + onscreen_native->crtc = crtc; + + if (META_GPU_KMS (meta_crtc_get_gpu (crtc)) != render_gpu) + { + if (!init_secondary_gpu_state (renderer_native, onscreen, error)) + { + g_object_unref (onscreen); + return NULL; + } + } + + return onscreen; +} + +gboolean +meta_onscreen_native_allocate (CoglOnscreen *onscreen, + GError **error) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaRendererNativeGpuData *renderer_gpu_data; + struct gbm_surface *gbm_surface; + EGLSurface egl_surface; + int width; + int height; +#ifdef HAVE_EGL_DEVICE + MetaKmsDevice *render_kms_device; + EGLStreamKHR egl_stream; +#endif + + /* 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 (!create_surfaces_gbm (onscreen, + width, height, + &gbm_surface, + &egl_surface, + error)) + return FALSE; + + onscreen_native->gbm.surface = gbm_surface; + cogl_onscreen_egl_set_egl_surface (onscreen_egl, egl_surface); + break; +#ifdef HAVE_EGL_DEVICE + case META_RENDERER_NATIVE_MODE_EGL_DEVICE: + render_kms_device = + meta_gpu_kms_get_kms_device (onscreen_native->render_gpu); + onscreen_native->egl.dumb_fb = + meta_drm_buffer_dumb_new (render_kms_device, + width, height, + DRM_FORMAT_XRGB8888, + error); + if (!onscreen_native->egl.dumb_fb) + return FALSE; + + if (!create_surfaces_egl_device (onscreen, + width, height, + &egl_stream, + &egl_surface, + error)) + return FALSE; + + onscreen_native->egl.stream = egl_stream; + cogl_onscreen_egl_set_egl_surface (onscreen_egl, egl_surface); + break; +#endif /* HAVE_EGL_DEVICE */ + } + + return TRUE; +} + +static void +destroy_egl_surface (CoglOnscreen *onscreen) +{ + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + EGLSurface egl_surface; + + egl_surface = cogl_onscreen_egl_get_egl_surface (onscreen_egl); + if (cogl_onscreen_egl_get_egl_surface (onscreen_egl) != EGL_NO_SURFACE) + { + MetaOnscreenNative *onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); + CoglRenderer *cogl_renderer = cogl_context->display->renderer; + CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; + + meta_egl_destroy_surface (egl, + cogl_renderer_egl->edpy, + egl_surface, + NULL); + cogl_onscreen_egl_set_egl_surface (onscreen_egl, EGL_NO_SURFACE); + } +} + +void +meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); + CoglDisplay *cogl_display = cogl_context_get_display (cogl_context); + CoglDisplayEGL *cogl_display_egl = cogl_display->winsys; + CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); + MetaOnscreenNative *onscreen_native; + MetaRendererNative *renderer_native; + MetaRendererNativeGpuData *renderer_gpu_data; + EGLSurface egl_surface; + + /* If we never successfully allocated then there's nothing to do */ + if (onscreen_egl == NULL) + return; + + onscreen_native = + cogl_onscreen_egl_get_platform (onscreen_egl); + renderer_native = onscreen_native->renderer_native; + + egl_surface = cogl_onscreen_egl_get_egl_surface (onscreen_egl); + if (egl_surface != EGL_NO_SURFACE && + (cogl_display_egl->current_draw_surface == egl_surface || + cogl_display_egl->current_read_surface == egl_surface)) + { + if (!_cogl_winsys_egl_make_current (cogl_display, + cogl_display_egl->dummy_surface, + cogl_display_egl->dummy_surface, + cogl_display_egl->egl_context)) + g_warning ("Failed to clear current context"); + } + + 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: + /* 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 == NULL); + + free_current_bo (onscreen); + + destroy_egl_surface (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: + g_clear_object (&onscreen_native->egl.dumb_fb); + + destroy_egl_surface (onscreen); + + if (onscreen_native->egl.stream != EGL_NO_STREAM_KHR) + { + MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); + CoglRenderer *cogl_renderer = cogl_context->display->renderer; + CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; + + 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_clear_pointer (&onscreen_native->secondary_gpu_state, + secondary_gpu_state_free); + + g_slice_free (MetaOnscreenNative, onscreen_native); + cogl_onscreen_egl_free (cogl_onscreen_get_winsys (onscreen)); + cogl_onscreen_set_winsys (onscreen, NULL); +} diff --git a/src/backends/native/meta-onscreen-native.h b/src/backends/native/meta-onscreen-native.h new file mode 100644 index 000000000..10a3a610f --- /dev/null +++ b/src/backends/native/meta-onscreen-native.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016-2020 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#ifndef META_ONSCREEN_NATIVE_H +#define META_ONSCREEN_NATIVE_H + +#include + +#include "backends/meta-backend-types.h" +#include "backends/native/meta-backend-native-types.h" +#include "clutter/clutter.h" +#include "cogl/cogl.h" + +gboolean +meta_renderer_native_init_onscreen (CoglOnscreen *onscreen, + GError **error); + +void meta_renderer_native_release_onscreen (CoglOnscreen *onscreen); + +gboolean meta_onscreen_native_allocate (CoglOnscreen *onscreen, + GError **error); + +void meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + const int *rectangles, + int n_rectangles, + CoglFrameInfo *frame_info, + gpointer user_data); + +gboolean meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, + CoglScanout *scanout, + CoglFrameInfo *frame_info, + gpointer user_data, + GError **error); + +void meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, + ClutterFrame *frame); + +void meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen); + +void meta_onscreen_native_set_view (CoglOnscreen *onscreen, + MetaRendererView *view); + +CoglOnscreen * meta_onscreen_native_new (MetaRendererNative *renderer_native, + MetaGpuKms *render_gpu, + MetaOutput *output, + MetaCrtc *crtc, + CoglContext *cogl_context, + int width, + int height, + GError **error); + +#endif /* META_ONSCREEN_NATIVE_H */ diff --git a/src/backends/native/meta-renderer-native-private.h b/src/backends/native/meta-renderer-native-private.h new file mode 100644 index 000000000..2193d85e4 --- /dev/null +++ b/src/backends/native/meta-renderer-native-private.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2011 Intel Corporation. + * Copyright (C) 2016-2020 Red Hat + * Copyright (c) 2018,2019 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. + * + */ + +#ifndef META_RENDERER_NATIVE_PRIVATE_H +#define META_RENDERER_NATIVE_PRIVATE_H + +#include "backends/meta-gles3.h" +#include "backends/native/meta-renderer-native.h" + +typedef enum _MetaSharedFramebufferCopyMode +{ + /* Zero-copy: primary GPU exports, secondary GPU imports as KMS FB */ + META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO, + /* the secondary GPU will make the copy */ + META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU, + /* + * The copy is made in the primary GPU rendering context, either + * as a CPU copy through Cogl read-pixels or as primary GPU copy + * using glBlitFramebuffer. + */ + META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY +} MetaSharedFramebufferCopyMode; + +typedef struct _MetaRendererNativeGpuData +{ + MetaRendererNative *renderer_native; + + struct { + struct gbm_device *device; + } gbm; + +#ifdef HAVE_EGL_DEVICE + struct { + EGLDeviceEXT device; + } egl; +#endif + + MetaRendererNativeMode mode; + + EGLDisplay egl_display; + + /* + * Fields used for blitting iGPU framebuffer content onto dGPU framebuffers. + */ + struct { + MetaSharedFramebufferCopyMode copy_mode; + gboolean is_hardware_rendering; + gboolean has_EGL_EXT_image_dma_buf_import_modifiers; + + /* For GPU blit mode */ + EGLContext egl_context; + EGLConfig egl_config; + } secondary; +} MetaRendererNativeGpuData; + +MetaEgl * meta_renderer_native_get_egl (MetaRendererNative *renderer_native); + +MetaGles3 * meta_renderer_native_get_gles3 (MetaRendererNative *renderer_native); + +MetaRendererNativeGpuData * meta_renderer_native_get_gpu_data (MetaRendererNative *renderer_native, + MetaGpuKms *gpu_kms); + +gboolean meta_renderer_native_has_pending_mode_sets (MetaRendererNative *renderer_native); + +gboolean meta_renderer_native_has_pending_mode_set (MetaRendererNative *renderer_native); + +void meta_renderer_native_notify_mode_sets_reset (MetaRendererNative *renderer_native); + +void meta_renderer_native_post_mode_set_updates (MetaRendererNative *renderer_native); + +void meta_renderer_native_queue_power_save_page_flip (MetaRendererNative *renderer_native, + CoglOnscreen *onscreen); + +CoglFramebuffer * meta_renderer_native_create_dma_buf_framebuffer (MetaRendererNative *renderer_native, + int dmabuf_fd, + uint32_t width, + uint32_t height, + uint32_t stride, + uint32_t offset, + uint64_t modifier, + uint32_t drm_format, + GError **error); + +gboolean meta_renderer_native_pop_pending_mode_set (MetaRendererNative *renderer_native, + MetaRendererView *view); + +const CoglWinsysVtable * meta_get_renderer_native_parent_vtable (void); + +#endif /* META_RENDERER_NATIVE_PRIVATE_H */ diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index a542af194..555bd413d 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -46,28 +46,14 @@ #include #include -#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-cogl-utils.h" #include "backends/native/meta-crtc-kms.h" -#include "backends/native/meta-drm-buffer-dumb.h" -#include "backends/native/meta-drm-buffer-gbm.h" -#include "backends/native/meta-drm-buffer-import.h" -#include "backends/native/meta-drm-buffer.h" -#include "backends/native/meta-gpu-kms.h" #include "backends/native/meta-kms-device.h" -#include "backends/native/meta-kms-update.h" -#include "backends/native/meta-kms-utils.h" #include "backends/native/meta-kms.h" -#include "backends/native/meta-output-kms.h" -#include "backends/native/meta-renderer-native-gles3.h" -#include "backends/native/meta-renderer-native.h" +#include "backends/native/meta-onscreen-native.h" +#include "backends/native/meta-renderer-native-private.h" #include "cogl/cogl.h" #include "core/boxes-private.h" @@ -80,111 +66,6 @@ #define DRM_FORMAT_INVALID 0 #endif -typedef enum _MetaSharedFramebufferCopyMode -{ - /* Zero-copy: primary GPU exports, secondary GPU imports as KMS FB */ - META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO, - /* the secondary GPU will make the copy */ - META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU, - /* - * The copy is made in the primary GPU rendering context, either - * as a CPU copy through Cogl read-pixels or as primary GPU copy - * using glBlitFramebuffer. - */ - META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY -} MetaSharedFramebufferCopyMode; - -typedef struct _MetaRendererNativeGpuData -{ - MetaRendererNative *renderer_native; - - struct { - struct gbm_device *device; - } gbm; - -#ifdef HAVE_EGL_DEVICE - struct { - EGLDeviceEXT device; - } egl; -#endif - - MetaRendererNativeMode mode; - - EGLDisplay egl_display; - - /* - * Fields used for blitting iGPU framebuffer content onto dGPU framebuffers. - */ - struct { - MetaSharedFramebufferCopyMode copy_mode; - gboolean is_hardware_rendering; - gboolean has_EGL_EXT_image_dma_buf_import_modifiers; - - /* For GPU blit mode */ - EGLContext egl_context; - EGLConfig egl_config; - } secondary; -} MetaRendererNativeGpuData; - -typedef enum _MetaSharedFramebufferImportStatus -{ - /* Not tried importing yet. */ - META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE, - /* Tried before and failed. */ - META_SHARED_FRAMEBUFFER_IMPORT_STATUS_FAILED, - /* Tried before and succeeded. */ - META_SHARED_FRAMEBUFFER_IMPORT_STATUS_OK -} MetaSharedFramebufferImportStatus; - -typedef struct _MetaOnscreenNativeSecondaryGpuState -{ - MetaGpuKms *gpu_kms; - MetaRendererNativeGpuData *renderer_gpu_data; - - EGLSurface egl_surface; - - struct { - struct gbm_surface *surface; - MetaDrmBuffer *current_fb; - MetaDrmBuffer *next_fb; - } gbm; - - struct { - MetaDrmBufferDumb *current_dumb_fb; - MetaDrmBufferDumb *dumb_fbs[2]; - } cpu; - - gboolean noted_primary_gpu_copy_ok; - gboolean noted_primary_gpu_copy_failed; - MetaSharedFramebufferImportStatus import_status; -} MetaOnscreenNativeSecondaryGpuState; - -typedef struct _MetaOnscreenNative -{ - MetaRendererNative *renderer_native; - MetaGpuKms *render_gpu; - MetaOutput *output; - MetaCrtc *crtc; - - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; - - struct { - struct gbm_surface *surface; - MetaDrmBuffer *current_fb; - MetaDrmBuffer *next_fb; - } gbm; - -#ifdef HAVE_EGL_DEVICE - struct { - EGLStreamKHR stream; - - MetaDrmBufferDumb *dumb_fb; - } egl; -#endif - - MetaRendererView *view; -} MetaOnscreenNative; - struct _MetaRendererNative { MetaRenderer parent; @@ -221,15 +102,15 @@ G_DEFINE_TYPE_WITH_CODE (MetaRendererNative, static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable; static const CoglWinsysVtable *parent_vtable; -static MetaEgl * -meta_renderer_native_get_egl (MetaRendererNative *renderer_native); - -static void -free_current_secondary_bo (CoglOnscreen *onscreen); - static void meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native); +const CoglWinsysVtable * +meta_get_renderer_native_parent_vtable (void) +{ + return parent_vtable; +} + static void meta_renderer_native_gpu_data_free (MetaRendererNativeGpuData *renderer_gpu_data) { @@ -243,7 +124,7 @@ meta_renderer_native_gpu_data_free (MetaRendererNativeGpuData *renderer_gpu_data g_free (renderer_gpu_data); } -static MetaRendererNativeGpuData * +MetaRendererNativeGpuData * meta_renderer_native_get_gpu_data (MetaRendererNative *renderer_native, MetaGpuKms *gpu_kms) { @@ -282,7 +163,7 @@ meta_create_renderer_native_gpu_data (MetaGpuKms *gpu_kms) return g_new0 (MetaRendererNativeGpuData, 1); } -static MetaEgl * +MetaEgl * meta_renderer_native_get_egl (MetaRendererNative *renderer_native) { MetaRenderer *renderer = META_RENDERER (renderer_native); @@ -296,415 +177,22 @@ meta_renderer_native_use_modifiers (MetaRendererNative *renderer_native) return renderer_native->use_modifiers; } -static MetaEgl * -meta_onscreen_native_get_egl (MetaOnscreenNative *onscreen_native) +MetaGles3 * +meta_renderer_native_get_gles3 (MetaRendererNative *renderer_native) { - return meta_renderer_native_get_egl (onscreen_native->renderer_native); + return renderer_native->gles3; } -static GArray * -get_supported_kms_modifiers (MetaCrtcKms *crtc_kms, - uint32_t format) +gboolean +meta_renderer_native_has_pending_mode_sets (MetaRendererNative *renderer_native) { - GArray *modifiers; - GArray *crtc_mods; - unsigned int i; - - crtc_mods = meta_crtc_kms_get_modifiers (crtc_kms, format); - if (!crtc_mods) - return NULL; - - 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 < crtc_mods->len; i++) - { - uint64_t modifier = g_array_index (crtc_mods, uint64_t, i); - - g_array_append_val (modifiers, modifier); - } - - if (modifiers->len == 0) - { - g_array_free (modifiers, TRUE); - return NULL; - } - - return modifiers; + return !!renderer_native->pending_mode_set_views; } -static GArray * -get_supported_egl_modifiers (CoglOnscreen *onscreen, - MetaCrtcKms *crtc_kms, - uint32_t format) +gboolean +meta_renderer_native_has_pending_mode_set (MetaRendererNative *renderer_native) { - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaRendererNative *renderer_native = onscreen_native->renderer_native; - MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); - MetaGpu *gpu; - MetaRendererNativeGpuData *renderer_gpu_data; - EGLint num_modifiers; - GArray *modifiers; - GError *error = NULL; - gboolean ret; - - gpu = meta_crtc_get_gpu (META_CRTC (crtc_kms)); - 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 = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); - MetaGpu *gpu; - g_autoptr (GArray) modifiers = NULL; - - gpu = meta_crtc_get_gpu (META_CRTC (crtc_kms)); - if (gpu == META_GPU (onscreen_native->render_gpu)) - modifiers = get_supported_kms_modifiers (crtc_kms, format); - else - modifiers = get_supported_egl_modifiers (onscreen, crtc_kms, format); - - return g_steal_pointer (&modifiers); -} - -static GArray * -get_supported_kms_formats (CoglOnscreen *onscreen) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); - - return meta_crtc_kms_copy_drm_format_list (crtc_kms); -} - -static uint32_t -get_gbm_format_from_egl (MetaEgl *egl, - EGLDisplay egl_display, - EGLConfig egl_config) -{ - uint32_t gbm_format; - EGLint native_visual_id; - - if (meta_egl_get_config_attrib (egl, - egl_display, - egl_config, - EGL_NATIVE_VISUAL_ID, - &native_visual_id, - NULL)) - gbm_format = (uint32_t) native_visual_id; - else - g_assert_not_reached (); - - return gbm_format; -} - -static gboolean -init_secondary_gpu_state_gpu_copy_mode (MetaRendererNative *renderer_native, - CoglOnscreen *onscreen, - MetaRendererNativeGpuData *renderer_gpu_data, - GError **error) -{ - CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - 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; - MetaGpuKms *gpu_kms; - uint32_t format; - - width = cogl_framebuffer_get_width (framebuffer); - height = cogl_framebuffer_get_height (framebuffer); - format = get_gbm_format_from_egl (egl, - renderer_gpu_data->egl_display, - renderer_gpu_data->secondary.egl_config); - - gbm_surface = gbm_surface_create (renderer_gpu_data->gbm.device, - width, height, - format, - 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); - - gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (onscreen_native->crtc)); - 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; - - onscreen_native->secondary_gpu_state = secondary_gpu_state; - - return TRUE; -} - -static void -secondary_gpu_release_dumb (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) -{ - unsigned i; - - for (i = 0; i < G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); i++) - g_clear_object (&secondary_gpu_state->cpu.dumb_fbs[i]); -} - -static void -secondary_gpu_state_free (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) -{ - MetaBackend *backend = meta_get_backend (); - MetaEgl *egl = meta_backend_get_egl (backend); - - 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); - } - - g_clear_object (&secondary_gpu_state->gbm.current_fb); - g_clear_object (&secondary_gpu_state->gbm.next_fb); - g_clear_pointer (&secondary_gpu_state->gbm.surface, gbm_surface_destroy); - - secondary_gpu_release_dumb (secondary_gpu_state); - - g_free (secondary_gpu_state); -} - -static uint32_t -pick_secondary_gpu_framebuffer_format_for_cpu (CoglOnscreen *onscreen) -{ - /* - * cogl_framebuffer_read_pixels_into_bitmap () supported formats in - * preference order. Ideally these should depend on the render buffer - * format copy_shared_framebuffer_cpu () will be reading from but - * alpha channel ignored. - */ - static const uint32_t preferred_formats[] = - { - /* - * DRM_FORMAT_XBGR8888 a.k.a GL_RGBA, GL_UNSIGNED_BYTE on - * little-endian is possibly the most optimized glReadPixels - * output format. glReadPixels cannot avoid manufacturing an alpha - * channel if the render buffer does not have one and converting - * to ABGR8888 may be more optimized than ARGB8888. - */ - DRM_FORMAT_XBGR8888, - /* The rest are other fairly commonly used formats in OpenGL. */ - DRM_FORMAT_XRGB8888, - }; - g_autoptr (GArray) formats = NULL; - size_t k; - unsigned int i; - uint32_t drm_format; - - formats = get_supported_kms_formats (onscreen); - - /* Check if any of our preferred formats are supported. */ - for (k = 0; k < G_N_ELEMENTS (preferred_formats); k++) - { - g_assert (meta_cogl_pixel_format_from_drm_format (preferred_formats[k], - NULL, - NULL)); - - for (i = 0; i < formats->len; i++) - { - drm_format = g_array_index (formats, uint32_t, i); - - if (drm_format == preferred_formats[k]) - return drm_format; - } - } - - /* - * Otherwise just pick an arbitrary format we recognize. The formats - * list is not in any specific order and we don't know any better - * either. - */ - for (i = 0; i < formats->len; i++) - { - drm_format = g_array_index (formats, uint32_t, i); - - if (meta_cogl_pixel_format_from_drm_format (drm_format, NULL, NULL)) - return drm_format; - } - - return DRM_FORMAT_INVALID; -} - -static gboolean -init_secondary_gpu_state_cpu_copy_mode (MetaRendererNative *renderer_native, - CoglOnscreen *onscreen, - MetaRendererNativeGpuData *renderer_gpu_data, - GError **error) -{ - CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; - MetaGpuKms *gpu_kms; - MetaKmsDevice *kms_device; - int width, height; - unsigned int i; - uint32_t drm_format; - MetaDrmFormatBuf tmp; - - drm_format = pick_secondary_gpu_framebuffer_format_for_cpu (onscreen); - if (drm_format == DRM_FORMAT_INVALID) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Could not find a suitable pixel format in CPU copy mode"); - return FALSE; - } - - width = cogl_framebuffer_get_width (framebuffer); - height = cogl_framebuffer_get_height (framebuffer); - - gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (onscreen_native->crtc)); - kms_device = meta_gpu_kms_get_kms_device (gpu_kms); - g_debug ("Secondary GPU %s using DRM format '%s' (0x%x) for a %dx%d output.", - meta_gpu_kms_get_file_path (gpu_kms), - meta_drm_format_to_string (&tmp, drm_format), - drm_format, - width, height); - - 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++) - { - secondary_gpu_state->cpu.dumb_fbs[i] = - meta_drm_buffer_dumb_new (kms_device, - width, height, - drm_format, - error); - if (!secondary_gpu_state->cpu.dumb_fbs[i]) - { - secondary_gpu_state_free (secondary_gpu_state); - return FALSE; - } - } - - /* - * This function initializes everything needed for - * META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO as well. - */ - secondary_gpu_state->import_status = - META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE; - - onscreen_native->secondary_gpu_state = secondary_gpu_state; - - return TRUE; -} - -static gboolean -init_secondary_gpu_state (MetaRendererNative *renderer_native, - CoglOnscreen *onscreen, - GError **error) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaGpu *gpu = meta_crtc_get_gpu (onscreen_native->crtc); - MetaRendererNativeGpuData *renderer_gpu_data; - - renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, - META_GPU_KMS (gpu)); - - switch (renderer_gpu_data->secondary.copy_mode) - { - case META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU: - if (!init_secondary_gpu_state_gpu_copy_mode (renderer_native, - onscreen, - renderer_gpu_data, - error)) - return FALSE; - break; - case META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO: - /* - * Initialize also the primary copy mode, so that if zero-copy - * path fails, which is quite likely, we can simply continue - * with the primary copy path on the very first frame. - */ - G_GNUC_FALLTHROUGH; - case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY: - if (!init_secondary_gpu_state_cpu_copy_mode (renderer_native, - onscreen, - renderer_gpu_data, - error)) - return FALSE; - break; - } - - return TRUE; + return renderer_native->pending_mode_set; } static void @@ -715,46 +203,6 @@ meta_renderer_native_disconnect (CoglRenderer *cogl_renderer) g_slice_free (CoglRendererEGL, cogl_renderer_egl); } -static void -free_current_secondary_bo (CoglOnscreen *onscreen) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; - - secondary_gpu_state = onscreen_native->secondary_gpu_state; - if (!secondary_gpu_state) - return; - - g_clear_object (&secondary_gpu_state->gbm.current_fb); -} - -static void -free_current_bo (CoglOnscreen *onscreen) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - - g_clear_object (&onscreen_native->gbm.current_fb); - free_current_secondary_bo (onscreen); -} - -static void -meta_onscreen_native_notify_frame_complete (CoglOnscreen *onscreen) -{ - CoglFrameInfo *info; - - info = cogl_onscreen_pop_head_frame_info (onscreen); - - g_assert (!cogl_onscreen_peek_head_frame_info (onscreen)); - - _cogl_onscreen_notify_frame_sync (onscreen, info); - _cogl_onscreen_notify_complete (onscreen, info); - cogl_object_unref (info); -} - static gboolean meta_renderer_native_connect (CoglRenderer *cogl_renderer, GError **error) @@ -999,563 +447,6 @@ meta_renderer_native_egl_cleanup_context (CoglDisplay *cogl_display) } } -static void -swap_secondary_drm_fb (CoglOnscreen *onscreen) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; - - secondary_gpu_state = onscreen_native->secondary_gpu_state; - if (!secondary_gpu_state) - return; - - g_set_object (&secondary_gpu_state->gbm.current_fb, - secondary_gpu_state->gbm.next_fb); - g_clear_object (&secondary_gpu_state->gbm.next_fb); -} - -static void -meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - - if (!onscreen_native->gbm.next_fb) - return; - - free_current_bo (onscreen); - - g_set_object (&onscreen_native->gbm.current_fb, onscreen_native->gbm.next_fb); - g_clear_object (&onscreen_native->gbm.next_fb); - - swap_secondary_drm_fb (onscreen); -} - -static void -maybe_update_frame_info (MetaCrtc *crtc, - CoglFrameInfo *frame_info, - int64_t time_ns) -{ - const MetaCrtcConfig *crtc_config; - const MetaCrtcModeInfo *crtc_mode_info; - float refresh_rate; - - g_return_if_fail (crtc); - - crtc_config = meta_crtc_get_config (crtc); - if (!crtc_config) - return; - - crtc_mode_info = meta_crtc_mode_get_info (crtc_config->mode); - refresh_rate = crtc_mode_info->refresh_rate; - if (refresh_rate >= frame_info->refresh_rate) - { - frame_info->presentation_time = time_ns; - frame_info->refresh_rate = refresh_rate; - } -} - -static void -notify_view_crtc_presented (MetaRendererView *view, - MetaKmsCrtc *kms_crtc, - int64_t time_ns) -{ - ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view); - CoglFramebuffer *framebuffer = - clutter_stage_view_get_onscreen (stage_view); - CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaRendererNative *renderer_native = onscreen_native->renderer_native; - CoglFrameInfo *frame_info; - MetaCrtc *crtc; - MetaRendererNativeGpuData *renderer_gpu_data; - - frame_info = cogl_onscreen_peek_head_frame_info (onscreen); - - crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc)); - maybe_update_frame_info (crtc, frame_info, time_ns); - - meta_onscreen_native_notify_frame_complete (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 int64_t -timeval_to_nanoseconds (const struct timeval *tv) -{ - int64_t usec = ((int64_t) tv->tv_sec) * G_USEC_PER_SEC + tv->tv_usec; - int64_t nsec = usec * 1000; - - return nsec; -} - -static void -page_flip_feedback_flipped (MetaKmsCrtc *kms_crtc, - unsigned int sequence, - unsigned int tv_sec, - unsigned int tv_usec, - gpointer user_data) -{ - MetaRendererView *view = user_data; - struct timeval page_flip_time; - - page_flip_time = (struct timeval) { - .tv_sec = tv_sec, - .tv_usec = tv_usec, - }; - - notify_view_crtc_presented (view, kms_crtc, - timeval_to_nanoseconds (&page_flip_time)); -} - -static void -page_flip_feedback_ready (MetaKmsCrtc *kms_crtc, - gpointer user_data) -{ - MetaRendererView *view = user_data; - CoglFramebuffer *framebuffer = - clutter_stage_view_get_onscreen (CLUTTER_STAGE_VIEW (view)); - CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); - CoglFrameInfo *frame_info; - - frame_info = cogl_onscreen_peek_head_frame_info (onscreen); - frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; - - meta_onscreen_native_notify_frame_complete (onscreen); -} - -static void -page_flip_feedback_mode_set_fallback (MetaKmsCrtc *kms_crtc, - gpointer user_data) -{ - MetaRendererView *view = user_data; - MetaCrtc *crtc; - MetaGpuKms *gpu_kms; - int64_t now_ns; - - /* - * We ended up not page flipping, thus we don't have a presentation time to - * use. Lets use the next best thing: the current time. - */ - - crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc)); - gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); - now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms); - - notify_view_crtc_presented (view, kms_crtc, now_ns); -} - -static void -page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc, - gpointer user_data, - const GError *error) -{ - MetaRendererView *view = user_data; - MetaCrtc *crtc; - MetaGpuKms *gpu_kms; - int64_t now_ns; - - /* - * Page flipping failed, but we want to fail gracefully, so to avoid freezing - * the frame clack, pretend we flipped. - */ - - if (error && - !g_error_matches (error, - G_IO_ERROR, - G_IO_ERROR_PERMISSION_DENIED)) - g_warning ("Page flip discarded: %s", error->message); - - crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc)); - gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); - now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms); - - notify_view_crtc_presented (view, kms_crtc, now_ns); -} - -static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = { - .flipped = page_flip_feedback_flipped, - .ready = page_flip_feedback_ready, - .mode_set_fallback = page_flip_feedback_mode_set_fallback, - .discarded = page_flip_feedback_discarded, -}; - -#ifdef HAVE_EGL_DEVICE -static int -custom_egl_stream_page_flip (gpointer custom_page_flip_data, - gpointer user_data) -{ - MetaOnscreenNative *onscreen_native = custom_page_flip_data; - MetaRendererView *view = user_data; - MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); - MetaRendererNativeGpuData *renderer_gpu_data; - EGLDisplay *egl_display; - EGLAttrib *acquire_attribs; - g_autoptr (GError) error = NULL; - - acquire_attribs = (EGLAttrib[]) { - EGL_DRM_FLIP_EVENT_DATA_NV, - (EGLAttrib) view, - EGL_NONE - }; - - renderer_gpu_data = - meta_renderer_native_get_gpu_data (onscreen_native->renderer_native, - onscreen_native->render_gpu); - - 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 (g_error_matches (error, META_EGL_ERROR, EGL_RESOURCE_BUSY_EXT)) - return -EBUSY; - else - return -EINVAL; - } - - return 0; -} -#endif /* HAVE_EGL_DEVICE */ - -static void -dummy_power_save_page_flip (CoglOnscreen *onscreen) -{ - CoglFrameInfo *frame_info; - - meta_onscreen_native_swap_drm_fb (onscreen); - - frame_info = cogl_onscreen_peek_tail_frame_info (onscreen); - frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; - meta_onscreen_native_notify_frame_complete (onscreen); -} - -static gboolean -dummy_power_save_page_flip_cb (gpointer user_data) -{ - MetaRendererNative *renderer_native = user_data; - - g_list_foreach (renderer_native->power_save_page_flip_onscreens, - (GFunc) dummy_power_save_page_flip, NULL); - g_list_free_full (renderer_native->power_save_page_flip_onscreens, - g_object_unref); - renderer_native->power_save_page_flip_onscreens = NULL; - renderer_native->power_save_page_flip_source_id = 0; - - return G_SOURCE_REMOVE; -} - -static void -queue_dummy_power_save_page_flip (CoglOnscreen *onscreen) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaRendererNative *renderer_native = onscreen_native->renderer_native; - const unsigned int timeout_ms = 100; - - if (!renderer_native->power_save_page_flip_source_id) - { - renderer_native->power_save_page_flip_source_id = - g_timeout_add (timeout_ms, - dummy_power_save_page_flip_cb, - renderer_native); - } - - renderer_native->power_save_page_flip_onscreens = - g_list_prepend (renderer_native->power_save_page_flip_onscreens, - g_object_ref (onscreen)); -} - -static void -meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, - MetaRendererView *view, - MetaCrtc *crtc, - MetaKmsPageFlipListenerFlag flags) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaRendererNative *renderer_native = onscreen_native->renderer_native; - MetaGpuKms *render_gpu = onscreen_native->render_gpu; - MetaCrtcKms *crtc_kms = META_CRTC_KMS (crtc); - MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); - MetaRendererNativeGpuData *renderer_gpu_data; - MetaGpuKms *gpu_kms; - MetaKmsDevice *kms_device; - MetaKms *kms; - MetaKmsUpdate *kms_update; - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state = NULL; - MetaDrmBuffer *buffer; - - COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeFlipCrtcs, - "Onscreen (flip CRTCs)"); - - gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); - kms_device = meta_gpu_kms_get_kms_device (gpu_kms); - kms = meta_kms_device_get_kms (kms_device); - kms_update = meta_kms_ensure_pending_update (kms, kms_device); - - g_assert (meta_gpu_kms_is_crtc_active (gpu_kms, crtc)); - - 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) - { - buffer = onscreen_native->gbm.next_fb; - } - else - { - secondary_gpu_state = onscreen_native->secondary_gpu_state; - buffer = secondary_gpu_state->gbm.next_fb; - } - - meta_crtc_kms_assign_primary_plane (crtc_kms, buffer, kms_update); - - break; -#ifdef HAVE_EGL_DEVICE - case META_RENDERER_NATIVE_MODE_EGL_DEVICE: - meta_kms_update_set_custom_page_flip (kms_update, - custom_egl_stream_page_flip, - onscreen_native); - break; -#endif - } - - meta_kms_update_add_page_flip_listener (kms_update, - kms_crtc, - &page_flip_listener_vtable, - flags, - g_object_ref (view), - g_object_unref); -} - -static void -meta_onscreen_native_set_crtc_mode (CoglOnscreen *onscreen, - MetaRendererNativeGpuData *renderer_gpu_data) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); - MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); - MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); - MetaKms *kms = meta_kms_device_get_kms (kms_device); - MetaKmsUpdate *kms_update; - - COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeSetCrtcModes, - "Onscreen (set CRTC modes)"); - - kms_update = meta_kms_ensure_pending_update (kms, kms_device); - - switch (renderer_gpu_data->mode) - { - case META_RENDERER_NATIVE_MODE_GBM: - break; -#ifdef HAVE_EGL_DEVICE - case META_RENDERER_NATIVE_MODE_EGL_DEVICE: - { - MetaDrmBuffer *buffer; - - buffer = META_DRM_BUFFER (onscreen_native->egl.dumb_fb); - meta_crtc_kms_assign_primary_plane (crtc_kms, buffer, kms_update); - break; - } -#endif - } - - meta_crtc_kms_set_mode (crtc_kms, kms_update); - meta_output_kms_set_underscan (META_OUTPUT_KMS (onscreen_native->output), - kms_update); -} - -static gboolean -import_shared_framebuffer (CoglOnscreen *onscreen, - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaGpuKms *gpu_kms; - MetaKmsDevice *kms_device; - struct gbm_device *gbm_device; - MetaDrmBufferGbm *buffer_gbm; - MetaDrmBufferImport *buffer_import; - g_autoptr (GError) error = NULL; - - buffer_gbm = META_DRM_BUFFER_GBM (onscreen_native->gbm.next_fb); - - gpu_kms = secondary_gpu_state->gpu_kms; - kms_device = meta_gpu_kms_get_kms_device (gpu_kms); - gbm_device = meta_gbm_device_from_gpu (gpu_kms); - buffer_import = meta_drm_buffer_import_new (kms_device, - gbm_device, - buffer_gbm, - &error); - if (!buffer_import) - { - g_debug ("Zero-copy disabled for %s, meta_drm_buffer_import_new failed: %s", - meta_gpu_kms_get_file_path (secondary_gpu_state->gpu_kms), - error->message); - - g_warn_if_fail (secondary_gpu_state->import_status == - META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE); - - /* - * Fall back. If META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE is - * in effect, we have COPY_MODE_PRIMARY prepared already, so we - * simply retry with that path. Import status cannot be FAILED, - * because we should not retry if failed once. - * - * If import status is OK, that is unexpected and we do not - * have the fallback path prepared which means this output cannot - * work anymore. - */ - secondary_gpu_state->renderer_gpu_data->secondary.copy_mode = - META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY; - - secondary_gpu_state->import_status = - META_SHARED_FRAMEBUFFER_IMPORT_STATUS_FAILED; - return FALSE; - } - - /* - * next_fb may already contain a fallback buffer, so clear it only - * when we are sure to succeed. - */ - g_clear_object (&secondary_gpu_state->gbm.next_fb); - secondary_gpu_state->gbm.next_fb = META_DRM_BUFFER (buffer_import); - - if (secondary_gpu_state->import_status == - META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE) - { - /* - * Clean up the cpu-copy part of - * init_secondary_gpu_state_cpu_copy_mode () - */ - secondary_gpu_release_dumb (secondary_gpu_state); - - g_debug ("Using zero-copy for %s succeeded once.", - meta_gpu_kms_get_file_path (secondary_gpu_state->gpu_kms)); - } - - secondary_gpu_state->import_status = - META_SHARED_FRAMEBUFFER_IMPORT_STATUS_OK; - return TRUE; -} - -static void -copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, - MetaRendererNativeGpuData *renderer_gpu_data, - gboolean *egl_context_changed) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; - MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); - GError *error = NULL; - MetaKmsDevice *kms_device; - MetaDrmBufferGbm *buffer_gbm; - struct gbm_bo *bo; - - COGL_TRACE_BEGIN_SCOPED (CopySharedFramebufferSecondaryGpu, - "FB Copy (secondary GPU)"); - - g_warn_if_fail (secondary_gpu_state->gbm.next_fb == NULL); - g_clear_object (&secondary_gpu_state->gbm.next_fb); - - 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; - - buffer_gbm = META_DRM_BUFFER_GBM (onscreen_native->gbm.next_fb); - bo = meta_drm_buffer_gbm_get_bo (buffer_gbm); - 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, - 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; - } - - kms_device = meta_gpu_kms_get_kms_device (secondary_gpu_state->gpu_kms); - buffer_gbm = - meta_drm_buffer_gbm_new_lock_front (kms_device, - secondary_gpu_state->gbm.surface, - renderer_native->use_modifiers, - &error); - if (!buffer_gbm) - { - g_warning ("meta_drm_buffer_gbm_new_lock_front failed: %s", - error->message); - g_error_free (error); - return; - } - - secondary_gpu_state->gbm.next_fb = META_DRM_BUFFER (buffer_gbm); -} - -static MetaDrmBufferDumb * -secondary_gpu_get_next_dumb_buffer (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) -{ - MetaDrmBufferDumb *current_dumb_fb; - - current_dumb_fb = secondary_gpu_state->cpu.current_dumb_fb; - if (current_dumb_fb == secondary_gpu_state->cpu.dumb_fbs[0]) - return secondary_gpu_state->cpu.dumb_fbs[1]; - else - return secondary_gpu_state->cpu.dumb_fbs[0]; -} - static CoglContext * cogl_context_from_renderer_native (MetaRendererNative *renderer_native) { @@ -1566,16 +457,16 @@ cogl_context_from_renderer_native (MetaRendererNative *renderer_native) return clutter_backend_get_cogl_context (clutter_backend); } -static CoglFramebuffer * -create_dma_buf_framebuffer (MetaRendererNative *renderer_native, - int dmabuf_fd, - uint32_t width, - uint32_t height, - uint32_t stride, - uint32_t offset, - uint64_t modifier, - uint32_t drm_format, - GError **error) +CoglFramebuffer * +meta_renderer_native_create_dma_buf_framebuffer (MetaRendererNative *renderer_native, + int dmabuf_fd, + uint32_t width, + uint32_t height, + uint32_t stride, + uint32_t offset, + uint64_t modifier, + uint32_t drm_format, + GError **error) { CoglContext *cogl_context = cogl_context_from_renderer_native (renderer_native); @@ -1642,286 +533,11 @@ create_dma_buf_framebuffer (MetaRendererNative *renderer_native, return COGL_FRAMEBUFFER (cogl_fbo); } -static gboolean -copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscreen, - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) -{ - CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaRendererNative *renderer_native = onscreen_native->renderer_native; - MetaRendererNativeGpuData *primary_gpu_data; - MetaDrmBufferDumb *buffer_dumb; - MetaDrmBuffer *buffer; - int width, height, stride; - uint32_t drm_format; - CoglFramebuffer *dmabuf_fb; - int dmabuf_fd; - g_autoptr (GError) error = NULL; - CoglPixelFormat cogl_format; - int ret; - - COGL_TRACE_BEGIN_SCOPED (CopySharedFramebufferPrimaryGpu, - "FB Copy (primary GPU)"); - - primary_gpu_data = - meta_renderer_native_get_gpu_data (renderer_native, - renderer_native->primary_gpu_kms); - if (!primary_gpu_data->secondary.has_EGL_EXT_image_dma_buf_import_modifiers) - return FALSE; - - buffer_dumb = secondary_gpu_get_next_dumb_buffer (secondary_gpu_state); - buffer = META_DRM_BUFFER (buffer_dumb); - - width = meta_drm_buffer_get_width (buffer); - height = meta_drm_buffer_get_height (buffer); - stride = meta_drm_buffer_get_stride (buffer); - drm_format = meta_drm_buffer_get_format (buffer); - - g_assert (cogl_framebuffer_get_width (framebuffer) == width); - g_assert (cogl_framebuffer_get_height (framebuffer) == height); - - ret = meta_cogl_pixel_format_from_drm_format (drm_format, - &cogl_format, - NULL); - g_assert (ret); - - dmabuf_fd = meta_drm_buffer_dumb_ensure_dmabuf_fd (buffer_dumb, &error); - if (!dmabuf_fd) - { - g_debug ("Failed to create DMA buffer: %s", error->message); - return FALSE; - } - - dmabuf_fb = create_dma_buf_framebuffer (renderer_native, - dmabuf_fd, - width, - height, - stride, - 0, DRM_FORMAT_MOD_LINEAR, - drm_format, - &error); - - if (error) - { - g_debug ("%s: Failed to blit DMA buffer image: %s", - G_STRFUNC, error->message); - return FALSE; - } - - if (!cogl_blit_framebuffer (framebuffer, COGL_FRAMEBUFFER (dmabuf_fb), - 0, 0, 0, 0, - width, height, - &error)) - { - g_object_unref (dmabuf_fb); - return FALSE; - } - - g_object_unref (dmabuf_fb); - - g_clear_object (&secondary_gpu_state->gbm.next_fb); - secondary_gpu_state->gbm.next_fb = buffer; - secondary_gpu_state->cpu.current_dumb_fb = buffer_dumb; - - return TRUE; -} - -static void -copy_shared_framebuffer_cpu (CoglOnscreen *onscreen, - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, - MetaRendererNativeGpuData *renderer_gpu_data) -{ - CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); - CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); - MetaDrmBufferDumb *buffer_dumb; - MetaDrmBuffer *buffer; - int width, height, stride; - uint32_t drm_format; - void *buffer_data; - CoglBitmap *dumb_bitmap; - CoglPixelFormat cogl_format; - gboolean ret; - - COGL_TRACE_BEGIN_SCOPED (CopySharedFramebufferCpu, - "FB Copy (CPU)"); - - buffer_dumb = secondary_gpu_get_next_dumb_buffer (secondary_gpu_state); - buffer = META_DRM_BUFFER (buffer_dumb); - - width = meta_drm_buffer_get_width (buffer); - height = meta_drm_buffer_get_height (buffer); - stride = meta_drm_buffer_get_stride (buffer); - drm_format = meta_drm_buffer_get_format (buffer); - buffer_data = meta_drm_buffer_dumb_get_data (buffer_dumb); - - g_assert (cogl_framebuffer_get_width (framebuffer) == width); - g_assert (cogl_framebuffer_get_height (framebuffer) == height); - - ret = meta_cogl_pixel_format_from_drm_format (drm_format, - &cogl_format, - NULL); - g_assert (ret); - - dumb_bitmap = cogl_bitmap_new_for_data (cogl_context, - width, - height, - cogl_format, - stride, - buffer_data); - - if (!cogl_framebuffer_read_pixels_into_bitmap (framebuffer, - 0 /* x */, - 0 /* y */, - COGL_READ_PIXELS_COLOR_BUFFER, - dumb_bitmap)) - g_warning ("Failed to CPU-copy to a secondary GPU output"); - - cogl_object_unref (dumb_bitmap); - - g_clear_object (&secondary_gpu_state->gbm.next_fb); - secondary_gpu_state->gbm.next_fb = buffer; - secondary_gpu_state->cpu.current_dumb_fb = buffer_dumb; -} - -static void -update_secondary_gpu_state_pre_swap_buffers (CoglOnscreen *onscreen) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; - - COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeGpuStatePreSwapBuffers, - "Onscreen (secondary gpu pre-swap-buffers)"); - - secondary_gpu_state = onscreen_native->secondary_gpu_state; - if (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_SECONDARY_GPU: - /* Done after eglSwapBuffers. */ - break; - case META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO: - /* Done after eglSwapBuffers. */ - if (secondary_gpu_state->import_status == - META_SHARED_FRAMEBUFFER_IMPORT_STATUS_OK) - break; - /* prepare fallback */ - G_GNUC_FALLTHROUGH; - case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY: - if (!copy_shared_framebuffer_primary_gpu (onscreen, - secondary_gpu_state)) - { - if (!secondary_gpu_state->noted_primary_gpu_copy_failed) - { - g_debug ("Using primary GPU to copy for %s failed once.", - meta_gpu_kms_get_file_path (secondary_gpu_state->gpu_kms)); - secondary_gpu_state->noted_primary_gpu_copy_failed = TRUE; - } - - copy_shared_framebuffer_cpu (onscreen, - secondary_gpu_state, - renderer_gpu_data); - } - else if (!secondary_gpu_state->noted_primary_gpu_copy_ok) - { - g_debug ("Using primary GPU to copy for %s succeeded once.", - meta_gpu_kms_get_file_path (secondary_gpu_state->gpu_kms)); - secondary_gpu_state->noted_primary_gpu_copy_ok = TRUE; - } - break; - } - } -} - -static void -update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen, - gboolean *egl_context_changed) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaRendererNative *renderer_native = onscreen_native->renderer_native; - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; - - COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeGpuStatePostSwapBuffers, - "Onscreen (secondary gpu post-swap-buffers)"); - - secondary_gpu_state = onscreen_native->secondary_gpu_state; - if (secondary_gpu_state) - { - MetaRendererNativeGpuData *renderer_gpu_data; - - renderer_gpu_data = - meta_renderer_native_get_gpu_data (renderer_native, - secondary_gpu_state->gpu_kms); -retry: - switch (renderer_gpu_data->secondary.copy_mode) - { - case META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO: - if (!import_shared_framebuffer (onscreen, - secondary_gpu_state)) - goto retry; - break; - case META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU: - copy_shared_framebuffer_gpu (onscreen, - secondary_gpu_state, - renderer_gpu_data, - egl_context_changed); - break; - case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY: - /* Done before eglSwapBuffers. */ - break; - } - } -} - -static void -ensure_crtc_modes (CoglOnscreen *onscreen) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); - CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); - 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; - MetaRenderer *renderer = META_RENDERER (renderer_native); - MetaBackend *backend = meta_renderer_get_backend (renderer); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaPowerSave power_save_mode; - GList *link; - - power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); - link = g_list_find (renderer_native->pending_mode_set_views, - onscreen_native->view); - if (link && power_save_mode == META_POWER_SAVE_ON) - { - meta_onscreen_native_set_crtc_mode (onscreen, renderer_gpu_data); - renderer_native->pending_mode_set_views = - g_list_delete_link (renderer_native->pending_mode_set_views, link); - } -} - static MetaKmsDevice * kms_device_from_view (MetaRendererView *view) { - CoglFramebuffer *framebuffer = - clutter_stage_view_get_onscreen (CLUTTER_STAGE_VIEW (view)); - CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); + MetaCrtc *crtc = meta_renderer_view_get_crtc (view); + MetaCrtcKms *crtc_kms = META_CRTC_KMS (crtc); MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); return meta_kms_crtc_get_device (kms_crtc); @@ -1930,14 +546,9 @@ kms_device_from_view (MetaRendererView *view) static MetaGpu * gpu_from_view (MetaRendererView *view) { - CoglFramebuffer *framebuffer = - clutter_stage_view_get_onscreen (CLUTTER_STAGE_VIEW (view)); - CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); + MetaCrtc *crtc = meta_renderer_view_get_crtc (view); - return meta_crtc_get_gpu (onscreen_native->crtc); + return meta_crtc_get_gpu (crtc); } static void @@ -1962,6 +573,41 @@ configure_disabled_crtcs (MetaGpu *gpu, } } +static gboolean +dummy_power_save_page_flip_cb (gpointer user_data) +{ + MetaRendererNative *renderer_native = user_data; + + g_list_foreach (renderer_native->power_save_page_flip_onscreens, + (GFunc) meta_onscreen_native_dummy_power_save_page_flip, + NULL); + g_list_free_full (renderer_native->power_save_page_flip_onscreens, + g_object_unref); + renderer_native->power_save_page_flip_onscreens = NULL; + renderer_native->power_save_page_flip_source_id = 0; + + return G_SOURCE_REMOVE; +} + +void +meta_renderer_native_queue_power_save_page_flip (MetaRendererNative *renderer_native, + CoglOnscreen *onscreen) +{ + const unsigned int timeout_ms = 100; + + if (!renderer_native->power_save_page_flip_source_id) + { + renderer_native->power_save_page_flip_source_id = + g_timeout_add (timeout_ms, + dummy_power_save_page_flip_cb, + renderer_native); + } + + renderer_native->power_save_page_flip_onscreens = + g_list_prepend (renderer_native->power_save_page_flip_onscreens, + g_object_ref (onscreen)); +} + static void clear_kept_alive_onscreens (MetaRendererNative *renderer_native) { @@ -1970,7 +616,7 @@ clear_kept_alive_onscreens (MetaRendererNative *renderer_native) renderer_native->kept_alive_onscreens = NULL; } -static void +void meta_renderer_native_post_mode_set_updates (MetaRendererNative *renderer_native) { MetaRenderer *renderer = META_RENDERER (renderer_native); @@ -2066,182 +712,6 @@ unset_disabled_crtcs (MetaBackend *backend, } } -static void -meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, - const int *rectangles, - int n_rectangles, - CoglFrameInfo *frame_info, - gpointer user_data) -{ - CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); - CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); - 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; - MetaRenderer *renderer = META_RENDERER (renderer_native); - MetaBackend *backend = meta_renderer_get_backend (renderer); - MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaKms *kms = meta_backend_native_get_kms (backend_native); - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaGpuKms *render_gpu = onscreen_native->render_gpu; - MetaKmsDevice *render_kms_device = meta_gpu_kms_get_kms_device (render_gpu); - ClutterFrame *frame = user_data; - gboolean egl_context_changed = FALSE; - MetaPowerSave power_save_mode; - g_autoptr (GError) error = NULL; - MetaDrmBufferGbm *buffer_gbm; - MetaKmsCrtc *kms_crtc; - MetaKmsDevice *kms_device; - MetaKmsUpdateFlag flags; - g_autoptr (MetaKmsFeedback) kms_feedback = NULL; - const GError *feedback_error; - - COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers, - "Onscreen (swap-buffers)"); - - update_secondary_gpu_state_pre_swap_buffers (onscreen); - - parent_vtable->onscreen_swap_buffers_with_damage (onscreen, - rectangles, - n_rectangles, - frame_info, - user_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: - g_warn_if_fail (onscreen_native->gbm.next_fb == NULL); - g_clear_object (&onscreen_native->gbm.next_fb); - - buffer_gbm = - meta_drm_buffer_gbm_new_lock_front (render_kms_device, - onscreen_native->gbm.surface, - renderer_native->use_modifiers, - &error); - if (!buffer_gbm) - { - g_warning ("meta_drm_buffer_gbm_new_lock_front failed: %s", - error->message); - return; - } - - onscreen_native->gbm.next_fb = META_DRM_BUFFER (buffer_gbm); - - 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); - - power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); - if (power_save_mode == META_POWER_SAVE_ON) - { - ensure_crtc_modes (onscreen); - meta_onscreen_native_flip_crtc (onscreen, - onscreen_native->view, - onscreen_native->crtc, - META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE); - } - else - { - queue_dummy_power_save_page_flip (onscreen); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - return; - } - - /* - * 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); - - COGL_TRACE_BEGIN_SCOPED (MetaRendererNativePostKmsUpdate, - "Onscreen (post pending update)"); - kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (onscreen_native->crtc)); - kms_device = meta_kms_crtc_get_device (kms_crtc); - - switch (renderer_gpu_data->mode) - { - case META_RENDERER_NATIVE_MODE_GBM: - if (renderer_native->pending_mode_set_views) - { - meta_topic (META_DEBUG_KMS, - "Postponing primary plane composite update for CRTC %u (%s)", - meta_kms_crtc_get_id (kms_crtc), - meta_kms_device_get_path (kms_device)); - - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - return; - } - else if (renderer_native->pending_mode_set) - { - meta_topic (META_DEBUG_KMS, "Posting global mode set updates on %s", - meta_kms_device_get_path (kms_device)); - - renderer_native->pending_mode_set = FALSE; - meta_renderer_native_post_mode_set_updates (renderer_native); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - return; - } - break; -#ifdef HAVE_EGL_DEVICE - case META_RENDERER_NATIVE_MODE_EGL_DEVICE: - if (renderer_native->pending_mode_set) - { - renderer_native->pending_mode_set = FALSE; - meta_renderer_native_post_mode_set_updates (renderer_native); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - return; - } - break; -#endif - } - - meta_topic (META_DEBUG_KMS, - "Posting primary plane composite update for CRTC %u (%s)", - meta_kms_crtc_get_id (kms_crtc), - meta_kms_device_get_path (kms_device)); - - flags = META_KMS_UPDATE_FLAG_NONE; - kms_feedback = meta_kms_post_pending_update_sync (kms, kms_device, flags); - - switch (meta_kms_feedback_get_result (kms_feedback)) - { - case META_KMS_FEEDBACK_PASSED: - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - break; - case META_KMS_FEEDBACK_FAILED: - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - - feedback_error = meta_kms_feedback_get_error (kms_feedback); - if (!g_error_matches (feedback_error, - G_IO_ERROR, - G_IO_ERROR_PERMISSION_DENIED)) - g_warning ("Failed to post KMS update: %s", feedback_error->message); - break; - } -} - static CoglDmaBufHandle * meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, int width, @@ -2288,14 +758,15 @@ meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, stride = gbm_bo_get_stride (new_bo); offset = gbm_bo_get_offset (new_bo, 0); bpp = 4; - dmabuf_fb = create_dma_buf_framebuffer (renderer_native, - dmabuf_fd, - width, height, - stride, - offset, - DRM_FORMAT_MOD_LINEAR, - DRM_FORMAT_XRGB8888, - error); + dmabuf_fb = + meta_renderer_native_create_dma_buf_framebuffer (renderer_native, + dmabuf_fd, + width, height, + stride, + offset, + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_XRGB8888, + error); if (!dmabuf_fb) return NULL; @@ -2321,146 +792,6 @@ meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, return NULL; } -gboolean -meta_onscreen_native_is_buffer_scanout_compatible (CoglOnscreen *onscreen, - uint32_t drm_format, - uint64_t drm_modifier, - uint32_t stride) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - const MetaCrtcConfig *crtc_config; - MetaDrmBuffer *fb; - struct gbm_bo *gbm_bo; - - crtc_config = meta_crtc_get_config (onscreen_native->crtc); - if (crtc_config->transform != META_MONITOR_TRANSFORM_NORMAL) - return FALSE; - - if (onscreen_native->secondary_gpu_state) - return FALSE; - - if (!onscreen_native->gbm.surface) - return FALSE; - - fb = onscreen_native->gbm.current_fb ? onscreen_native->gbm.current_fb - : onscreen_native->gbm.next_fb; - if (!fb) - return FALSE; - - if (!META_IS_DRM_BUFFER_GBM (fb)) - return FALSE; - - gbm_bo = meta_drm_buffer_gbm_get_bo (META_DRM_BUFFER_GBM (fb)); - - if (gbm_bo_get_format (gbm_bo) != drm_format) - return FALSE; - - if (gbm_bo_get_modifier (gbm_bo) != drm_modifier) - return FALSE; - - if (gbm_bo_get_stride (gbm_bo) != stride) - return FALSE; - - return TRUE; -} - -static gboolean -meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, - CoglScanout *scanout, - CoglFrameInfo *frame_info, - gpointer user_data, - GError **error) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaGpuKms *render_gpu = onscreen_native->render_gpu; - CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); - CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); - 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; - MetaRenderer *renderer = META_RENDERER (renderer_native); - MetaBackend *backend = meta_renderer_get_backend (renderer); - MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); - MetaKms *kms = meta_backend_native_get_kms (backend_native); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaPowerSave power_save_mode; - ClutterFrame *frame = user_data; - MetaKmsCrtc *kms_crtc; - MetaKmsDevice *kms_device; - MetaKmsUpdateFlag flags; - g_autoptr (MetaKmsFeedback) kms_feedback = NULL; - const GError *feedback_error; - - power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); - if (power_save_mode != META_POWER_SAVE_ON) - { - g_set_error_literal (error, - COGL_SCANOUT_ERROR, - COGL_SCANOUT_ERROR_INHIBITED, - NULL); - return FALSE; - } - - if (renderer_native->pending_mode_set_views) - { - g_set_error_literal (error, - COGL_SCANOUT_ERROR, - COGL_SCANOUT_ERROR_INHIBITED, - NULL); - return FALSE; - } - - renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, - render_gpu); - - g_warn_if_fail (renderer_gpu_data->mode == META_RENDERER_NATIVE_MODE_GBM); - g_warn_if_fail (!onscreen_native->gbm.next_fb); - - g_set_object (&onscreen_native->gbm.next_fb, META_DRM_BUFFER (scanout)); - - ensure_crtc_modes (onscreen); - meta_onscreen_native_flip_crtc (onscreen, - onscreen_native->view, - onscreen_native->crtc, - META_KMS_PAGE_FLIP_LISTENER_FLAG_NO_DISCARD); - - kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (onscreen_native->crtc)); - kms_device = meta_kms_crtc_get_device (kms_crtc); - - meta_topic (META_DEBUG_KMS, - "Posting direct scanout update for CRTC %u (%s)", - meta_kms_crtc_get_id (kms_crtc), - meta_kms_device_get_path (kms_device)); - - flags = META_KMS_UPDATE_FLAG_PRESERVE_ON_ERROR; - kms_feedback = meta_kms_post_pending_update_sync (kms, kms_device, flags); - switch (meta_kms_feedback_get_result (kms_feedback)) - { - case META_KMS_FEEDBACK_PASSED: - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - break; - case META_KMS_FEEDBACK_FAILED: - feedback_error = meta_kms_feedback_get_error (kms_feedback); - - if (g_error_matches (feedback_error, - G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) - break; - - g_clear_object (&onscreen_native->gbm.next_fb); - g_propagate_error (error, g_error_copy (feedback_error)); - return FALSE; - } - - return TRUE; -} - static gboolean meta_renderer_native_init_egl_context (CoglContext *cogl_context, GError **error) @@ -2490,415 +821,6 @@ meta_renderer_native_init_egl_context (CoglContext *cogl_context, return TRUE; } -static gboolean -should_surface_be_sharable (CoglOnscreen *onscreen) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - - if (META_GPU_KMS (meta_crtc_get_gpu (onscreen_native->crtc)) == - onscreen_native->render_gpu) - return FALSE; - else - return TRUE; -} - -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 = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaRendererNative *renderer_native = onscreen_native->renderer_native; - MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); - CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); - CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); - 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; - GArray *modifiers; - - renderer_gpu_data = - meta_renderer_native_get_gpu_data (renderer_native, - onscreen_native->render_gpu); - - format = get_gbm_format_from_egl (egl, - cogl_renderer_egl->edpy, - cogl_display_egl->egl_config); - - 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, - int width, - int height, - EGLStreamKHR *out_egl_stream, - EGLSurface *out_egl_surface, - GError **error) -{ - CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); - 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; - 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, 0, - 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; - - output_attribs[0] = EGL_DRM_CRTC_EXT; - output_attribs[1] = meta_crtc_get_id (onscreen_native->crtc); - 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 -meta_renderer_native_init_onscreen (CoglOnscreen *onscreen, - GError **error) -{ - CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); - CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); - CoglDisplay *cogl_display = cogl_context->display; - CoglDisplayEGL *cogl_display_egl = cogl_display->winsys; - CoglOnscreenEgl *onscreen_egl; - MetaOnscreenNative *onscreen_native; - - g_return_val_if_fail (cogl_display_egl->egl_context, FALSE); - - onscreen_egl = cogl_onscreen_egl_new (); - cogl_onscreen_set_winsys (onscreen, onscreen_egl); - - onscreen_native = g_slice_new0 (MetaOnscreenNative); - cogl_onscreen_egl_set_platform (onscreen_egl, 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 = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaRendererNativeGpuData *renderer_gpu_data; - struct gbm_surface *gbm_surface; - EGLSurface egl_surface; - int width; - int height; -#ifdef HAVE_EGL_DEVICE - MetaKmsDevice *render_kms_device; - EGLStreamKHR egl_stream; -#endif - - /* 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; - cogl_onscreen_egl_set_egl_surface (onscreen_egl, egl_surface); - break; -#ifdef HAVE_EGL_DEVICE - case META_RENDERER_NATIVE_MODE_EGL_DEVICE: - render_kms_device = - meta_gpu_kms_get_kms_device (onscreen_native->render_gpu); - onscreen_native->egl.dumb_fb = - meta_drm_buffer_dumb_new (render_kms_device, - width, height, - DRM_FORMAT_XRGB8888, - error); - if (!onscreen_native->egl.dumb_fb) - return FALSE; - - if (!meta_renderer_native_create_surface_egl_device (onscreen, - width, height, - &egl_stream, - &egl_surface, - error)) - return FALSE; - - onscreen_native->egl.stream = egl_stream; - cogl_onscreen_egl_set_egl_surface (onscreen_egl, egl_surface); - break; -#endif /* HAVE_EGL_DEVICE */ - } - - return TRUE; -} - -static void -destroy_egl_surface (CoglOnscreen *onscreen) -{ - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - EGLSurface egl_surface; - - egl_surface = cogl_onscreen_egl_get_egl_surface (onscreen_egl); - if (cogl_onscreen_egl_get_egl_surface (onscreen_egl) != EGL_NO_SURFACE) - { - MetaOnscreenNative *onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); - CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); - CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); - CoglRenderer *cogl_renderer = cogl_context->display->renderer; - CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; - - meta_egl_destroy_surface (egl, - cogl_renderer_egl->edpy, - egl_surface, - NULL); - cogl_onscreen_egl_set_egl_surface (onscreen_egl, EGL_NO_SURFACE); - } -} - -static void -meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) -{ - CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); - CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); - CoglDisplay *cogl_display = cogl_context_get_display (cogl_context); - CoglDisplayEGL *cogl_display_egl = cogl_display->winsys; - CoglOnscreenEgl *onscreen_egl = cogl_onscreen_get_winsys (onscreen); - MetaOnscreenNative *onscreen_native; - MetaRendererNative *renderer_native; - MetaRendererNativeGpuData *renderer_gpu_data; - EGLSurface egl_surface; - - /* If we never successfully allocated then there's nothing to do */ - if (onscreen_egl == NULL) - return; - - onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - renderer_native = onscreen_native->renderer_native; - - egl_surface = cogl_onscreen_egl_get_egl_surface (onscreen_egl); - if (egl_surface != EGL_NO_SURFACE && - (cogl_display_egl->current_draw_surface == egl_surface || - cogl_display_egl->current_read_surface == egl_surface)) - { - if (!_cogl_winsys_egl_make_current (cogl_display, - cogl_display_egl->dummy_surface, - cogl_display_egl->dummy_surface, - cogl_display_egl->egl_context)) - g_warning ("Failed to clear current context"); - } - - 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: - /* 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 == NULL); - - free_current_bo (onscreen); - - destroy_egl_surface (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: - g_clear_object (&onscreen_native->egl.dumb_fb); - - destroy_egl_surface (onscreen); - - if (onscreen_native->egl.stream != EGL_NO_STREAM_KHR) - { - MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); - CoglRenderer *cogl_renderer = cogl_context->display->renderer; - CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; - - 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_clear_pointer (&onscreen_native->secondary_gpu_state, - secondary_gpu_state_free); - - g_slice_free (MetaOnscreenNative, onscreen_native); - cogl_onscreen_egl_free (cogl_onscreen_get_winsys (onscreen)); - cogl_onscreen_set_winsys (onscreen, NULL); -} - static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable = { .add_config_attributes = meta_renderer_native_add_egl_config_attributes, @@ -2926,46 +848,36 @@ meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native) meta_topic (META_DEBUG_KMS, "Queue mode set"); } -static CoglOnscreen * -meta_renderer_native_create_onscreen (MetaRendererNative *renderer_native, - MetaGpuKms *render_gpu, - MetaOutput *output, - MetaCrtc *crtc, - CoglContext *context, - int width, - int height, - GError **error) +void +meta_renderer_native_notify_mode_sets_reset (MetaRendererNative *renderer_native) { - CoglOnscreen *onscreen; - CoglOnscreenEgl *onscreen_egl; - MetaOnscreenNative *onscreen_native; + renderer_native->pending_mode_set = FALSE; +} - onscreen = cogl_onscreen_new (context, width, height); +gboolean +meta_renderer_native_pop_pending_mode_set (MetaRendererNative *renderer_native, + MetaRendererView *view) +{ + MetaRenderer *renderer = META_RENDERER (renderer_native); + MetaBackend *backend = meta_renderer_get_backend (renderer); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + MetaPowerSave power_save_mode; + GList *link; - if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (onscreen), error)) - { - g_object_unref (onscreen); - return NULL; - } + g_assert (META_IS_RENDERER_VIEW (view)); - onscreen_egl = cogl_onscreen_get_winsys (onscreen); - onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - onscreen_native->renderer_native = renderer_native; - onscreen_native->render_gpu = render_gpu; - onscreen_native->output = output; - onscreen_native->crtc = crtc; + power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); + if (power_save_mode != META_POWER_SAVE_ON) + return FALSE; - if (META_GPU_KMS (meta_crtc_get_gpu (crtc)) != render_gpu) - { - if (!init_secondary_gpu_state (renderer_native, onscreen, error)) - { - g_object_unref (onscreen); - return NULL; - } - } + link = g_list_find (renderer_native->pending_mode_set_views, view); + if (!link) + return FALSE; - return onscreen; + renderer_native->pending_mode_set_views = + g_list_delete_link (renderer_native->pending_mode_set_views, link); + return TRUE; } static CoglOffscreen * @@ -3066,19 +978,6 @@ meta_renderer_native_create_cogl_renderer (MetaRenderer *renderer) return create_cogl_renderer_for_gpu (renderer_native->primary_gpu_kms); } -static void -meta_onscreen_native_set_view (CoglOnscreen *onscreen, - MetaRendererView *view) -{ - CoglOnscreenEgl *onscreen_egl; - MetaOnscreenNative *onscreen_native; - - onscreen_egl = cogl_onscreen_get_winsys (onscreen); - onscreen_native = - cogl_onscreen_egl_get_platform (onscreen_egl); - onscreen_native->view = view; -} - static MetaMonitorTransform calculate_view_transform (MetaMonitorManager *monitor_manager, MetaLogicalMonitor *logical_monitor, @@ -3169,14 +1068,14 @@ meta_renderer_native_create_view (MetaRenderer *renderer, onscreen_width = crtc_mode_info->width; onscreen_height = crtc_mode_info->height; - onscreen = meta_renderer_native_create_onscreen (renderer_native, - renderer_native->primary_gpu_kms, - output, - crtc, - cogl_context, - onscreen_width, - onscreen_height, - &error); + onscreen = meta_onscreen_native_new (renderer_native, + renderer_native->primary_gpu_kms, + output, + crtc, + cogl_context, + onscreen_width, + onscreen_height, + &error); if (!onscreen) g_error ("Failed to allocate onscreen framebuffer: %s", error->message); @@ -3309,78 +1208,18 @@ meta_renderer_native_prepare_frame (MetaRendererNative *renderer_native, meta_crtc_kms_maybe_set_gamma (crtc_kms, kms_device); } -static void -add_onscreen_frame_info (MetaCrtc *crtc) -{ - MetaGpu *gpu = meta_crtc_get_gpu (crtc); - MetaBackend *backend = meta_gpu_get_backend (gpu); - ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); - MetaStageNative *stage_native = - meta_clutter_backend_native_get_stage_native (clutter_backend); - MetaRenderer *renderer = meta_backend_get_renderer (backend); - MetaRendererView *view = meta_renderer_get_view_for_crtc (renderer, crtc); - - clutter_stage_cogl_add_onscreen_frame_info (CLUTTER_STAGE_COGL (stage_native), - CLUTTER_STAGE_VIEW (view)); -} - void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native, MetaRendererView *view, ClutterFrame *frame) { - MetaRenderer *renderer = META_RENDERER (renderer_native); - MetaBackend *backend = meta_renderer_get_backend (renderer); - MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); - MetaKms *kms = meta_backend_native_get_kms (backend_native); - if (!clutter_frame_has_result (frame)) { - MetaCrtc *crtc = meta_renderer_view_get_crtc (view); - MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (crtc)); - MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc);; - MetaKmsUpdateFlag flags; - MetaKmsUpdate *kms_update; - g_autoptr (MetaKmsFeedback) kms_feedback = NULL; - const GError *error; + CoglFramebuffer *framebuffer = + clutter_stage_view_get_onscreen (CLUTTER_STAGE_VIEW (view)); + CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); - kms_update = meta_kms_get_pending_update (kms, kms_device); - if (!kms_update) - { - clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); - return; - } - - meta_kms_update_add_page_flip_listener (kms_update, - kms_crtc, - &page_flip_listener_vtable, - META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE, - g_object_ref (view), - g_object_unref); - - flags = META_KMS_UPDATE_FLAG_NONE; - kms_feedback = meta_kms_post_pending_update_sync (kms, - kms_device, - flags); - switch (meta_kms_feedback_get_result (kms_feedback)) - { - case META_KMS_FEEDBACK_PASSED: - add_onscreen_frame_info (crtc); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - break; - case META_KMS_FEEDBACK_FAILED: - add_onscreen_frame_info (crtc); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); - - error = meta_kms_feedback_get_error (kms_feedback); - if (!g_error_matches (error, - G_IO_ERROR, - G_IO_ERROR_PERMISSION_DENIED)) - g_warning ("Failed to post KMS update: %s", error->message); - break; - } + meta_onscreen_native_finish_frame (onscreen, frame); } } diff --git a/src/meson.build b/src/meson.build index 8875fb2ef..3683907ff 100644 --- a/src/meson.build +++ b/src/meson.build @@ -669,7 +669,6 @@ if have_native_backend 'backends/native/meta-monitor-manager-kms.h', 'backends/native/meta-output-kms.c', 'backends/native/meta-output-kms.h', - 'backends/native/meta-renderer-native.c', 'backends/native/meta-kms-connector-private.h', 'backends/native/meta-kms-connector.c', 'backends/native/meta-kms-connector.h', @@ -703,10 +702,14 @@ if have_native_backend 'backends/native/meta-kms-utils.h', 'backends/native/meta-kms.c', 'backends/native/meta-kms.h', + 'backends/native/meta-onscreen-native.c', + 'backends/native/meta-onscreen-native.h', 'backends/native/meta-pointer-constraint-native.c', 'backends/native/meta-pointer-constraint-native.h', 'backends/native/meta-renderer-native-gles3.c', 'backends/native/meta-renderer-native-gles3.h', + 'backends/native/meta-renderer-native-private.h', + 'backends/native/meta-renderer-native.c', 'backends/native/meta-renderer-native.h', 'backends/native/meta-seat-impl.c', 'backends/native/meta-seat-impl.h',