mirror of
https://github.com/brl/mutter.git
synced 2024-12-01 04:10:43 -05:00
10c4bc6e3f
The DRM buffers aren't really tied to mode setting, so they shouldn't need to have an associated mode setting device. Now that we have a device file level object that can fill this role, port over MetaDrmBuffer and friends away from MetaKmsDevice to MetaDeviceFile. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1828>
2097 lines
73 KiB
C
2097 lines
73 KiB
C
/*
|
|
* 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 <drm_fourcc.h>
|
|
|
|
#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-device.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;
|
|
|
|
struct _MetaOnscreenNative
|
|
{
|
|
CoglOnscreenEgl parent;
|
|
|
|
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;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native,
|
|
COGL_TYPE_ONSCREEN_EGL)
|
|
|
|
static gboolean
|
|
init_secondary_gpu_state (MetaRendererNative *renderer_native,
|
|
CoglOnscreen *onscreen,
|
|
GError **error);
|
|
|
|
static void
|
|
swap_secondary_drm_fb (CoglOnscreen *onscreen)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
|
|
g_clear_object (&onscreen_native->gbm.current_fb);
|
|
free_current_secondary_bo (onscreen);
|
|
}
|
|
|
|
static void
|
|
meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
|
|
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_us,
|
|
CoglFrameInfoFlag flags,
|
|
unsigned int sequence)
|
|
{
|
|
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_us = time_us;
|
|
frame_info->refresh_rate = refresh_rate;
|
|
frame_info->flags |= flags;
|
|
frame_info->sequence = sequence;
|
|
}
|
|
}
|
|
|
|
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_us,
|
|
CoglFrameInfoFlag flags,
|
|
unsigned int sequence)
|
|
{
|
|
ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view);
|
|
CoglFramebuffer *framebuffer =
|
|
clutter_stage_view_get_onscreen (stage_view);
|
|
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
|
|
CoglFrameInfo *frame_info;
|
|
MetaCrtc *crtc;
|
|
|
|
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_us, flags, sequence);
|
|
|
|
meta_onscreen_native_notify_frame_complete (onscreen);
|
|
meta_onscreen_native_swap_drm_fb (onscreen);
|
|
}
|
|
|
|
static int64_t
|
|
timeval_to_microseconds (const struct timeval *tv)
|
|
{
|
|
return ((int64_t) tv->tv_sec) * G_USEC_PER_SEC + tv->tv_usec;
|
|
}
|
|
|
|
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;
|
|
MetaKmsDevice *kms_device;
|
|
int64_t presentation_time_us;
|
|
CoglFrameInfoFlag flags = COGL_FRAME_INFO_FLAG_VSYNC;
|
|
|
|
page_flip_time = (struct timeval) {
|
|
.tv_sec = tv_sec,
|
|
.tv_usec = tv_usec,
|
|
};
|
|
|
|
kms_device = meta_kms_crtc_get_device (kms_crtc);
|
|
if (meta_kms_device_uses_monotonic_clock (kms_device))
|
|
{
|
|
presentation_time_us = timeval_to_microseconds (&page_flip_time);
|
|
flags |= COGL_FRAME_INFO_FLAG_HW_CLOCK;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Other parts of the code assume MONOTONIC timestamps. So, if the device
|
|
* timestamp isn't MONOTONIC, don't use it.
|
|
*/
|
|
presentation_time_us = g_get_monotonic_time ();
|
|
}
|
|
|
|
notify_view_crtc_presented (view, kms_crtc,
|
|
presentation_time_us,
|
|
flags,
|
|
sequence);
|
|
}
|
|
|
|
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;
|
|
int64_t now_us;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
now_us = g_get_monotonic_time ();
|
|
|
|
notify_view_crtc_presented (view,
|
|
kms_crtc,
|
|
now_us,
|
|
COGL_FRAME_INFO_FLAG_NONE,
|
|
0);
|
|
}
|
|
|
|
static void
|
|
page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc,
|
|
gpointer user_data,
|
|
const GError *error)
|
|
{
|
|
MetaRendererView *view = user_data;
|
|
CoglFramebuffer *framebuffer =
|
|
clutter_stage_view_get_onscreen (CLUTTER_STAGE_VIEW (view));
|
|
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
|
|
CoglFrameInfo *frame_info;
|
|
|
|
/*
|
|
* Page flipping failed, but we want to fail gracefully, so to avoid freezing
|
|
* the frame clock, emit a symbolic flip.
|
|
*/
|
|
|
|
if (error &&
|
|
!g_error_matches (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_PERMISSION_DENIED))
|
|
g_warning ("Page flip discarded: %s", error->message);
|
|
|
|
frame_info = cogl_onscreen_peek_head_frame_info (onscreen);
|
|
frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
|
|
|
|
meta_onscreen_native_notify_frame_complete (onscreen);
|
|
meta_onscreen_native_swap_drm_fb (onscreen);
|
|
}
|
|
|
|
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)
|
|
{
|
|
CoglOnscreen *onscreen = custom_page_flip_data;
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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;
|
|
case META_RENDERER_NATIVE_MODE_SURFACELESS:
|
|
g_assert_not_reached ();
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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;
|
|
case META_RENDERER_NATIVE_MODE_SURFACELESS:
|
|
g_assert_not_reached ();
|
|
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)
|
|
{
|
|
MetaGpu *gpu = META_GPU (secondary_gpu_state->gpu_kms);
|
|
MetaBackend *backend = meta_gpu_get_backend (gpu);
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
MetaGpuKms *gpu_kms;
|
|
MetaDeviceFile *device_file;
|
|
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;
|
|
device_file = secondary_gpu_state->renderer_gpu_data->device_file;
|
|
gbm_device = meta_gbm_device_from_gpu (gpu_kms);
|
|
buffer_import = meta_drm_buffer_import_new (device_file,
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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;
|
|
MetaDeviceFile *device_file;
|
|
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);
|
|
device_file = secondary_gpu_state->renderer_gpu_data->device_file;
|
|
buffer_gbm =
|
|
meta_drm_buffer_gbm_new_lock_front (device_file,
|
|
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);
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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_set_object (&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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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);
|
|
}
|
|
|
|
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);
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
MetaGpuKms *render_gpu = onscreen_native->render_gpu;
|
|
MetaDeviceFile *render_device_file;
|
|
ClutterFrame *frame = user_data;
|
|
CoglOnscreenClass *parent_class;
|
|
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_class = COGL_ONSCREEN_CLASS (meta_onscreen_native_parent_class);
|
|
parent_class->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);
|
|
render_device_file = renderer_gpu_data->device_file;
|
|
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_device_file,
|
|
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;
|
|
case META_RENDERER_NATIVE_MODE_SURFACELESS:
|
|
g_assert_not_reached ();
|
|
break;
|
|
#ifdef HAVE_EGL_DEVICE
|
|
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
update_secondary_gpu_state_post_swap_buffers (onscreen, &egl_context_changed);
|
|
|
|
/*
|
|
* If 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);
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
case META_RENDERER_NATIVE_MODE_SURFACELESS:
|
|
g_assert_not_reached ();
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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,
|
|
"Direct scanout is inhibited during power saving mode");
|
|
return FALSE;
|
|
}
|
|
|
|
if (meta_renderer_native_has_pending_mode_set (renderer_native))
|
|
{
|
|
g_set_error_literal (error,
|
|
COGL_SCANOUT_ERROR,
|
|
COGL_SCANOUT_ERROR_INHIBITED,
|
|
"Direct scanout is inhibited when a mode set is pending");
|
|
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_DROP_ON_ERROR);
|
|
|
|
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);
|
|
ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
|
|
ClutterStageWindow *stage_window = _clutter_stage_get_window (stage);
|
|
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_window),
|
|
CLUTTER_STAGE_VIEW (view));
|
|
}
|
|
|
|
void
|
|
meta_onscreen_native_finish_frame (CoglOnscreen *onscreen,
|
|
ClutterFrame *frame)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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);
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (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;
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
onscreen_native->view = view;
|
|
}
|
|
|
|
static gboolean
|
|
meta_onscreen_native_allocate (CoglFramebuffer *framebuffer,
|
|
GError **error)
|
|
{
|
|
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
CoglOnscreenEgl *onscreen_egl = COGL_ONSCREEN_EGL (onscreen);
|
|
MetaRendererNativeGpuData *renderer_gpu_data;
|
|
struct gbm_surface *gbm_surface;
|
|
EGLSurface egl_surface;
|
|
int width;
|
|
int height;
|
|
#ifdef HAVE_EGL_DEVICE
|
|
MetaDeviceFile *render_device_file;
|
|
EGLStreamKHR egl_stream;
|
|
#endif
|
|
CoglFramebufferClass *parent_class;
|
|
|
|
if (META_GPU_KMS (meta_crtc_get_gpu (onscreen_native->crtc)) !=
|
|
onscreen_native->render_gpu)
|
|
{
|
|
if (!init_secondary_gpu_state (onscreen_native->renderer_native,
|
|
onscreen, error))
|
|
return FALSE;
|
|
}
|
|
|
|
width = cogl_framebuffer_get_width (framebuffer);
|
|
height = cogl_framebuffer_get_height (framebuffer);
|
|
|
|
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;
|
|
case META_RENDERER_NATIVE_MODE_SURFACELESS:
|
|
g_assert_not_reached ();
|
|
break;
|
|
#ifdef HAVE_EGL_DEVICE
|
|
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
|
|
render_device_file = renderer_gpu_data->device_file;
|
|
onscreen_native->egl.dumb_fb =
|
|
meta_drm_buffer_dumb_new (render_device_file,
|
|
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 */
|
|
}
|
|
|
|
parent_class = COGL_FRAMEBUFFER_CLASS (meta_onscreen_native_parent_class);
|
|
return parent_class->allocate (framebuffer, error);
|
|
}
|
|
|
|
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);
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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);
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
|
|
MetaGpuKms *gpu_kms;
|
|
MetaDeviceFile *device_file;
|
|
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));
|
|
device_file = renderer_gpu_data->device_file;
|
|
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 (device_file,
|
|
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)
|
|
{
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
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;
|
|
}
|
|
|
|
MetaOnscreenNative *
|
|
meta_onscreen_native_new (MetaRendererNative *renderer_native,
|
|
MetaGpuKms *render_gpu,
|
|
MetaOutput *output,
|
|
MetaCrtc *crtc,
|
|
CoglContext *cogl_context,
|
|
int width,
|
|
int height)
|
|
{
|
|
MetaOnscreenNative *onscreen_native;
|
|
CoglFramebufferDriverConfig driver_config;
|
|
|
|
driver_config = (CoglFramebufferDriverConfig) {
|
|
.type = COGL_FRAMEBUFFER_DRIVER_TYPE_BACK,
|
|
};
|
|
onscreen_native = g_object_new (META_TYPE_ONSCREEN_NATIVE,
|
|
"context", cogl_context,
|
|
"driver-config", &driver_config,
|
|
"width", width,
|
|
"height", height,
|
|
NULL);
|
|
|
|
onscreen_native->renderer_native = renderer_native;
|
|
onscreen_native->render_gpu = render_gpu;
|
|
onscreen_native->output = output;
|
|
onscreen_native->crtc = crtc;
|
|
|
|
return onscreen_native;
|
|
}
|
|
|
|
static void
|
|
meta_onscreen_native_dispose (GObject *object)
|
|
{
|
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (object);
|
|
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
|
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
|
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
|
|
MetaRendererNativeGpuData *renderer_gpu_data;
|
|
|
|
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_warn_if_fail (onscreen_native->gbm.next_fb == NULL);
|
|
|
|
free_current_bo (onscreen);
|
|
break;
|
|
case META_RENDERER_NATIVE_MODE_SURFACELESS:
|
|
g_assert_not_reached ();
|
|
break;
|
|
#ifdef HAVE_EGL_DEVICE
|
|
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
|
|
g_clear_object (&onscreen_native->egl.dumb_fb);
|
|
|
|
if (onscreen_native->egl.stream != EGL_NO_STREAM_KHR)
|
|
{
|
|
MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
|
|
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_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_OBJECT_CLASS (meta_onscreen_native_parent_class)->dispose (object);
|
|
|
|
g_clear_pointer (&onscreen_native->gbm.surface, gbm_surface_destroy);
|
|
g_clear_pointer (&onscreen_native->secondary_gpu_state,
|
|
secondary_gpu_state_free);
|
|
}
|
|
|
|
static void
|
|
meta_onscreen_native_init (MetaOnscreenNative *onscreen_native)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_onscreen_native_class_init (MetaOnscreenNativeClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
CoglFramebufferClass *framebuffer_class = COGL_FRAMEBUFFER_CLASS (klass);
|
|
CoglOnscreenClass *onscreen_class = COGL_ONSCREEN_CLASS (klass);
|
|
|
|
object_class->dispose = meta_onscreen_native_dispose;
|
|
|
|
framebuffer_class->allocate = meta_onscreen_native_allocate;
|
|
|
|
onscreen_class->swap_buffers_with_damage =
|
|
meta_onscreen_native_swap_buffers_with_damage;
|
|
onscreen_class->direct_scanout = meta_onscreen_native_direct_scanout;
|
|
}
|