meta-renderer-native.c 135 KB
Newer Older
Jonas Ådahl's avatar
Jonas Ådahl committed
1
2
3
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */

/*
4
 * Copyright (C) 2011 Intel Corporation.
Jonas Ådahl's avatar
Jonas Ådahl committed
5
 * Copyright (C) 2016 Red Hat
6
 * Copyright (c) 2018,2019 DisplayLink (UK) Ltd.
Jonas Ådahl's avatar
Jonas Ådahl committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 *
 * 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.
 *
28
29
30
31
32
33
34
35
 * Authors:
 *   Rob Bradford <rob@linux.intel.com> (from cogl-winsys-egl-kms.c)
 *   Kristian Høgsberg (from eglkms.c)
 *   Benjamin Franzke (from eglkms.c)
 *   Robert Bragg <robert@linux.intel.com> (from cogl-winsys-egl-kms.c)
 *   Neil Roberts <neil@linux.intel.com> (from cogl-winsys-egl-kms.c)
 *   Jonas Ådahl <jadahl@redhat.com>
 *
Jonas Ådahl's avatar
Jonas Ådahl committed
36
37
38
39
 */

#include "config.h"

Jonas Ådahl's avatar
Jonas Ådahl committed
40
#include <drm_fourcc.h>
41
42
43
#include <errno.h>
#include <fcntl.h>
#include <gbm.h>
44
45
#include <gio/gio.h>
#include <glib-object.h>
46
47
#include <stdlib.h>
#include <string.h>
48
#include <sys/mman.h>
49
50
#include <unistd.h>
#include <xf86drm.h>
Jonas Ådahl's avatar
Jonas Ådahl committed
51

52
#include "backends/meta-backend-private.h"
53
#include "backends/meta-crtc.h"
54
#include "backends/meta-egl-ext.h"
Jonas Ådahl's avatar
Jonas Ådahl committed
55
56
#include "backends/meta-egl.h"
#include "backends/meta-gles3.h"
57
#include "backends/meta-logical-monitor.h"
Jonas Ådahl's avatar
Jonas Ådahl committed
58
#include "backends/meta-output.h"
59
#include "backends/meta-renderer-view.h"
60
#include "backends/native/meta-crtc-kms.h"
61
62
#include "backends/native/meta-drm-buffer-dumb.h"
#include "backends/native/meta-drm-buffer-gbm.h"
63
#include "backends/native/meta-drm-buffer-import.h"
64
#include "backends/native/meta-drm-buffer.h"
65
#include "backends/native/meta-gpu-kms.h"
66
#include "backends/native/meta-kms-update.h"
67
#include "backends/native/meta-kms-utils.h"
68
69
#include "backends/native/meta-kms.h"
#include "backends/native/meta-output-kms.h"
70
#include "backends/native/meta-renderer-native-gles3.h"
Jonas Ådahl's avatar
Jonas Ådahl committed
71
#include "backends/native/meta-renderer-native.h"
72
//#include "cogl/cogl-framebuffer.h"
Jonas Ådahl's avatar
Jonas Ådahl committed
73
#include "cogl/cogl.h"
74
#include "core/boxes-private.h"
Jonas Ådahl's avatar
Jonas Ådahl committed
75

76
77
78
79
#ifndef EGL_DRM_MASTER_FD_EXT
#define EGL_DRM_MASTER_FD_EXT 0x333C
#endif

80
81
82
83
84
/* added in libdrm 2.4.95 */
#ifndef DRM_FORMAT_INVALID
#define DRM_FORMAT_INVALID 0
#endif

85
86
typedef enum _MetaSharedFramebufferCopyMode
{
87
88
  /* Zero-copy: primary GPU exports, secondary GPU imports as KMS FB */
  META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO,
89
90
91
92
93
94
95
96
  /* 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
97
98
} MetaSharedFramebufferCopyMode;

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
typedef struct _MetaRendererNativeGpuData
{
  MetaRendererNative *renderer_native;

  struct {
    struct gbm_device *device;
  } gbm;

#ifdef HAVE_EGL_DEVICE
  struct {
    EGLDeviceEXT device;
  } egl;
#endif

  MetaRendererNativeMode mode;

115
116
117
118
119
120
121
  EGLDisplay egl_display;

  /*
   * Fields used for blitting iGPU framebuffer content onto dGPU framebuffers.
   */
  struct {
    MetaSharedFramebufferCopyMode copy_mode;
122
    gboolean is_hardware_rendering;
123
    gboolean has_EGL_EXT_image_dma_buf_import_modifiers;
124
125
126
127
128

    /* For GPU blit mode */
    EGLContext egl_context;
    EGLConfig egl_config;
  } secondary;
129
130
} MetaRendererNativeGpuData;

131
132
133
134
135
136
typedef struct _MetaDumbBuffer
{
  uint32_t fb_id;
  uint32_t handle;
  void *map;
  uint64_t map_size;
137
138
  int width;
  int height;
139
  int stride_bytes;
140
  uint32_t drm_format;
141
  int dmabuf_fd;
142
143
} MetaDumbBuffer;

144
145
146
147
148
149
150
151
152
153
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;

154
typedef struct _MetaOnscreenNativeSecondaryGpuState
155
{
156
  MetaGpuKms *gpu_kms;
157
158
159
160
161
162
  MetaRendererNativeGpuData *renderer_gpu_data;

  EGLSurface egl_surface;

  struct {
    struct gbm_surface *surface;
163
164
    MetaDrmBuffer *current_fb;
    MetaDrmBuffer *next_fb;
165
166
167
168
169
170
171
172
  } gbm;

  struct {
    MetaDumbBuffer *dumb_fb;
    MetaDumbBuffer dumb_fbs[2];
  } cpu;

  int pending_flips;
173
174
175

  gboolean noted_primary_gpu_copy_ok;
  gboolean noted_primary_gpu_copy_failed;
176
  MetaSharedFramebufferImportStatus import_status;
177
178
179
180
181
182
} MetaOnscreenNativeSecondaryGpuState;

typedef struct _MetaOnscreenNative
{
  MetaRendererNative *renderer_native;
  MetaGpuKms *render_gpu;
183
184
  MetaOutput *output;
  MetaCrtc *crtc;
185

186
  MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
187

188
189
  struct {
    struct gbm_surface *surface;
190
191
    MetaDrmBuffer *current_fb;
    MetaDrmBuffer *next_fb;
192
193
  } gbm;

194
195
196
197
#ifdef HAVE_EGL_DEVICE
  struct {
    EGLStreamKHR stream;

198
    MetaDumbBuffer dumb_fb;
199
200
201
  } egl;
#endif

202
203
  gboolean pending_swap_notify;

204
205
  gboolean pending_set_crtc;

206
207
208
  int64_t pending_queue_swap_notify_frame_count;
  int64_t pending_swap_notify_frame_count;

209
  MetaRendererView *view;
210
  int total_pending_flips;
211
212
} MetaOnscreenNative;

Jonas Ådahl's avatar
Jonas Ådahl committed
213
214
215
struct _MetaRendererNative
{
  MetaRenderer parent;
Jonas Ådahl's avatar
Jonas Ådahl committed
216

217
  MetaGpuKms *primary_gpu_kms;
218

219
  MetaGles3 *gles3;
220

221
222
  gboolean use_modifiers;

223
  GHashTable *gpu_datas;
224

225
  CoglClosure *swap_notify_idle;
226

227
  int64_t frame_counter;
228
  gboolean pending_unset_disabled_crtcs;
229

230
  GList *power_save_page_flip_onscreens;
231
  guint power_save_page_flip_source_id;
Jonas Ådahl's avatar
Jonas Ådahl committed
232
233
};

234
235
236
237
238
239
240
241
static void
initable_iface_init (GInitableIface *initable_iface);

G_DEFINE_TYPE_WITH_CODE (MetaRendererNative,
                         meta_renderer_native,
                         META_TYPE_RENDERER,
                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
                                                initable_iface_init))
Jonas Ådahl's avatar
Jonas Ådahl committed
242

243
244
245
static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable;
static const CoglWinsysVtable *parent_vtable;

246
247
248
249
250
251
252
253
254
255
256
257
static void
release_dumb_fb (MetaDumbBuffer *dumb_fb,
                 MetaGpuKms     *gpu_kms);

static gboolean
init_dumb_fb (MetaDumbBuffer *dumb_fb,
              MetaGpuKms     *gpu_kms,
              int             width,
              int             height,
              uint32_t        format,
              GError        **error);

258
259
260
261
static int
meta_dumb_buffer_ensure_dmabuf_fd (MetaDumbBuffer *dumb_fb,
                                   MetaGpuKms     *gpu_kms);

262
263
264
265
static MetaEgl *
meta_renderer_native_get_egl (MetaRendererNative *renderer_native);

static void
266
free_current_secondary_bo (CoglOnscreen *onscreen);
267

268
269
270
271
272
static gboolean
cogl_pixel_format_from_drm_format (uint32_t               drm_format,
                                   CoglPixelFormat       *out_format,
                                   CoglTextureComponents *out_components);

273
274
275
static void
meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native);

276
277
278
static void
meta_renderer_native_gpu_data_free (MetaRendererNativeGpuData *renderer_gpu_data)
{
279
280
281
282
283
284
  MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
  MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);

  if (renderer_gpu_data->egl_display != EGL_NO_DISPLAY)
    meta_egl_terminate (egl, renderer_gpu_data->egl_display, NULL);

Carlos Garnacho's avatar
Carlos Garnacho committed
285
  g_clear_pointer (&renderer_gpu_data->gbm.device, gbm_device_destroy);
286
287
288
289
290
291
292
293
294
295
296
297
298
  g_free (renderer_gpu_data);
}

static MetaRendererNativeGpuData *
meta_renderer_native_get_gpu_data (MetaRendererNative *renderer_native,
                                   MetaGpuKms         *gpu_kms)
{
  return g_hash_table_lookup (renderer_native->gpu_datas, gpu_kms);
}

static MetaRendererNative *
meta_renderer_native_from_gpu (MetaGpuKms *gpu_kms)
{
299
  MetaBackend *backend = meta_gpu_get_backend (META_GPU (gpu_kms));
300
301
302
303
304
305
306

  return META_RENDERER_NATIVE (meta_backend_get_renderer (backend));
}

struct gbm_device *
meta_gbm_device_from_gpu (MetaGpuKms *gpu_kms)
{
307
  MetaRendererNative *renderer_native = meta_renderer_native_from_gpu (gpu_kms);
308
309
310
311
312
313
314
315
  MetaRendererNativeGpuData *renderer_gpu_data;

  renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
                                                         gpu_kms);

  return renderer_gpu_data->gbm.device;
}

316
317
318
319
320
321
MetaGpuKms *
meta_renderer_native_get_primary_gpu (MetaRendererNative *renderer_native)
{
  return renderer_native->primary_gpu_kms;
}

322
323
324
325
326
327
static MetaRendererNativeGpuData *
meta_create_renderer_native_gpu_data (MetaGpuKms *gpu_kms)
{
  return g_new0 (MetaRendererNativeGpuData, 1);
}

328
329
330
static MetaEgl *
meta_renderer_native_get_egl (MetaRendererNative *renderer_native)
{
331
332
333
  MetaRenderer *renderer = META_RENDERER (renderer_native);

  return meta_backend_get_egl (meta_renderer_get_backend (renderer));
334
335
}

336
337
338
339
340
341
static MetaEgl *
meta_onscreen_native_get_egl (MetaOnscreenNative *onscreen_native)
{
  return meta_renderer_native_get_egl (onscreen_native->renderer_native);
}

342
static GArray *
343
344
get_supported_kms_modifiers (MetaCrtc *crtc,
                             uint32_t  format)
345
346
{
  GArray *modifiers;
347
  GArray *crtc_mods;
348
349
  unsigned int i;

350
351
  crtc_mods = meta_crtc_kms_get_modifiers (crtc, format);
  if (!crtc_mods)
352
353
354
355
356
357
358
359
    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.
   */
360
  for (i = 0; i < crtc_mods->len; i++)
361
    {
362
      uint64_t modifier = g_array_index (crtc_mods, uint64_t, i);
363

364
      g_array_append_val (modifiers, modifier);
365
366
367
368
369
    }

  if (modifiers->len == 0)
    {
      g_array_free (modifiers, TRUE);
370
      return NULL;
371
372
373
374
375
376
377
    }

  return modifiers;
}

static GArray *
get_supported_egl_modifiers (CoglOnscreen *onscreen,
378
                             MetaCrtc     *crtc,
379
380
381
382
383
384
                             uint32_t      format)
{
  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
  MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
385
  MetaGpu *gpu;
386
387
388
389
390
391
  MetaRendererNativeGpuData *renderer_gpu_data;
  EGLint num_modifiers;
  GArray *modifiers;
  GError *error = NULL;
  gboolean ret;

392
  gpu = meta_crtc_get_gpu (crtc);
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
  renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
                                                         META_GPU_KMS (gpu));

  if (!meta_egl_has_extensions (egl, renderer_gpu_data->egl_display, NULL,
                                "EGL_EXT_image_dma_buf_import_modifiers",
                                NULL))
    return NULL;

  ret = meta_egl_query_dma_buf_modifiers (egl, renderer_gpu_data->egl_display,
                                          format, 0, NULL, NULL,
                                          &num_modifiers, NULL);
  if (!ret || num_modifiers == 0)
    return NULL;

  modifiers = g_array_sized_new (FALSE, FALSE, sizeof (uint64_t),
                                 num_modifiers);
  ret = meta_egl_query_dma_buf_modifiers (egl, renderer_gpu_data->egl_display,
                                          format, num_modifiers,
                                          (EGLuint64KHR *) modifiers->data, NULL,
                                          &num_modifiers, &error);

  if (!ret)
    {
      g_warning ("Failed to query DMABUF modifiers: %s", error->message);
      g_error_free (error);
      g_array_free (modifiers, TRUE);
      return NULL;
    }

  return modifiers;
}

static GArray *
get_supported_modifiers (CoglOnscreen *onscreen,
                         uint32_t      format)
{
  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
431
  MetaCrtc *crtc = onscreen_native->crtc;
432
  MetaGpu *gpu;
433
  g_autoptr (GArray) modifiers = NULL;
434

435
436
437
438
439
  gpu = meta_crtc_get_gpu (crtc);
  if (gpu == META_GPU (onscreen_native->render_gpu))
    modifiers = get_supported_kms_modifiers (crtc, format);
  else
    modifiers = get_supported_egl_modifiers (onscreen, crtc, format);
440

441
  return g_steal_pointer (&modifiers);
442
443
444
}

static GArray *
445
get_supported_kms_formats (CoglOnscreen *onscreen)
446
447
448
{
  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
449
  MetaCrtc *crtc = onscreen_native->crtc;
450

451
  return meta_crtc_kms_copy_drm_format_list (crtc);
452
453
}

454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
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 = onscreen->winsys;
  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
  MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
  int width, height;
  EGLNativeWindowType egl_native_window;
  struct gbm_surface *gbm_surface;
  EGLSurface egl_surface;
  MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
469
  MetaGpuKms *gpu_kms;
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500

  width = cogl_framebuffer_get_width (framebuffer);
  height = cogl_framebuffer_get_height (framebuffer);

  gbm_surface = gbm_surface_create (renderer_gpu_data->gbm.device,
                                    width, height,
                                    GBM_FORMAT_XRGB8888,
                                    GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
  if (!gbm_surface)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Failed to create gbm_surface: %s", strerror (errno));
      return FALSE;
    }

  egl_native_window = (EGLNativeWindowType) gbm_surface;
  egl_surface =
    meta_egl_create_window_surface (egl,
                                    renderer_gpu_data->egl_display,
                                    renderer_gpu_data->secondary.egl_config,
                                    egl_native_window,
                                    NULL,
                                    error);
  if (egl_surface == EGL_NO_SURFACE)
    {
      gbm_surface_destroy (gbm_surface);
      return FALSE;
    }

  secondary_gpu_state = g_new0 (MetaOnscreenNativeSecondaryGpuState, 1);

501
  gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (onscreen_native->crtc));
502
503
504
505
506
  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;

507
  onscreen_native->secondary_gpu_state = secondary_gpu_state;
508
509
510
511

  return TRUE;
}

512
513
514
515
516
517
518
519
520
521
static void
secondary_gpu_release_dumb (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
{
  MetaGpuKms *gpu_kms = secondary_gpu_state->gpu_kms;
  unsigned i;

  for (i = 0; i < G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); i++)
    release_dumb_fb (&secondary_gpu_state->cpu.dumb_fbs[i], gpu_kms);
}

522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
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);
    }

539
  g_clear_object (&secondary_gpu_state->gbm.current_fb);
540
  g_clear_object (&secondary_gpu_state->gbm.next_fb);
541
542
  g_clear_pointer (&secondary_gpu_state->gbm.surface, gbm_surface_destroy);

543
  secondary_gpu_release_dumb (secondary_gpu_state);
544
545
546
547

  g_free (secondary_gpu_state);
}

548
static uint32_t
549
pick_secondary_gpu_framebuffer_format_for_cpu (CoglOnscreen *onscreen)
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
{
  /*
   * 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,
    };
570
  g_autoptr (GArray) formats = NULL;
571
572
573
574
  size_t k;
  unsigned int i;
  uint32_t drm_format;

575
  formats = get_supported_kms_formats (onscreen);
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608

  /* Check if any of our preferred formats are supported. */
  for (k = 0; k < G_N_ELEMENTS (preferred_formats); k++)
    {
      g_assert (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 (cogl_pixel_format_from_drm_format (drm_format, NULL, NULL))
        return drm_format;
    }

  return DRM_FORMAT_INVALID;
}

609
610
611
612
613
614
615
616
617
618
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 = onscreen->winsys;
  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
  MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
619
  MetaGpuKms *gpu_kms;
620
621
  int width, height;
  unsigned int i;
622
  uint32_t drm_format;
623
  MetaDrmFormatBuf tmp;
624

625
  drm_format = pick_secondary_gpu_framebuffer_format_for_cpu (onscreen);
626
627
628
629
630
631
  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;
    }
632
633
634
635

  width = cogl_framebuffer_get_width (framebuffer);
  height = cogl_framebuffer_get_height (framebuffer);

636
  gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (onscreen_native->crtc));
637
638
639
640
641
642
  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);

643
644
645
646
647
648
649
650
651
652
653
654
  secondary_gpu_state = g_new0 (MetaOnscreenNativeSecondaryGpuState, 1);
  secondary_gpu_state->renderer_gpu_data = renderer_gpu_data;
  secondary_gpu_state->gpu_kms = gpu_kms;
  secondary_gpu_state->egl_surface = EGL_NO_SURFACE;

  for (i = 0; i < G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); i++)
    {
      MetaDumbBuffer *dumb_fb = &secondary_gpu_state->cpu.dumb_fbs[i];

      if (!init_dumb_fb (dumb_fb,
                         gpu_kms,
                         width, height,
655
                         drm_format,
656
657
658
659
660
661
662
                         error))
        {
          secondary_gpu_state_free (secondary_gpu_state);
          return FALSE;
        }
    }

663
664
665
666
667
668
669
  /*
   * 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;

670
  onscreen_native->secondary_gpu_state = secondary_gpu_state;
671
672
673
674
675
676
677
678
679

  return TRUE;
}

static gboolean
init_secondary_gpu_state (MetaRendererNative  *renderer_native,
                          CoglOnscreen        *onscreen,
                          GError             **error)
{
680
681
682
  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
  MetaGpu *gpu = meta_crtc_get_gpu (onscreen_native->crtc);
683
684
685
  MetaRendererNativeGpuData *renderer_gpu_data;

  renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
686
                                                         META_GPU_KMS (gpu));
687
688
689

  switch (renderer_gpu_data->secondary.copy_mode)
    {
690
    case META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU:
691
692
693
694
695
696
      if (!init_secondary_gpu_state_gpu_copy_mode (renderer_native,
                                                   onscreen,
                                                   renderer_gpu_data,
                                                   error))
        return FALSE;
      break;
697
698
699
700
701
702
703
    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;
704
    case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY:
705
706
707
708
709
710
711
712
713
714
715
      if (!init_secondary_gpu_state_cpu_copy_mode (renderer_native,
                                                   onscreen,
                                                   renderer_gpu_data,
                                                   error))
        return FALSE;
      break;
    }

  return TRUE;
}

716
static void
717
meta_renderer_native_disconnect (CoglRenderer *cogl_renderer)
718
{
719
  CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
720

721
  g_slice_free (CoglRendererEGL, cogl_renderer_egl);
722
723
724
}

static void
725
flush_pending_swap_notify (CoglFramebuffer *framebuffer)
726
727
728
729
{
  if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
    {
      CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
730
731
      CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
      MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
732

733
      if (onscreen_native->pending_swap_notify)
734
        {
735
          CoglFrameInfo *info;
736

737
738
739
740
741
742
743
744
          while ((info = g_queue_peek_head (&onscreen->pending_frame_infos)) &&
                 info->global_frame_counter <= onscreen_native->pending_swap_notify_frame_count)
            {
              _cogl_onscreen_notify_frame_sync (onscreen, info);
              _cogl_onscreen_notify_complete (onscreen, info);
              cogl_object_unref (info);
              g_queue_pop_head (&onscreen->pending_frame_infos);
            }
745

746
          onscreen_native->pending_swap_notify = FALSE;
747
          cogl_object_unref (onscreen);
748
749
750
751
752
753
754
        }
    }
}

static void
flush_pending_swap_notify_idle (void *user_data)
{
755
  CoglContext *cogl_context = user_data;
756
  CoglRendererEGL *cogl_renderer_egl = cogl_context->display->renderer->winsys;
757
758
  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
  MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
759
  GList *l;
760
761
762

  /* This needs to be disconnected before invoking the callbacks in
   * case the callbacks cause it to be queued again */
763
764
  _cogl_closure_disconnect (renderer_native->swap_notify_idle);
  renderer_native->swap_notify_idle = NULL;
765

766
767
768
769
770
771
772
773
774
775
  l = cogl_context->framebuffers;
  while (l)
    {
      GList *next = l->next;
      CoglFramebuffer *framebuffer = l->data;

      flush_pending_swap_notify (framebuffer);

      l = next;
    }
776
777
}

778
static void
779
free_current_secondary_bo (CoglOnscreen *onscreen)
780
{
781
782
783
784
785
786
787
788
  CoglOnscreenEGL *onscreen_egl =  onscreen->winsys;
  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
  MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;

  secondary_gpu_state = onscreen_native->secondary_gpu_state;
  if (!secondary_gpu_state)
    return;

789
  g_clear_object (&secondary_gpu_state->gbm.current_fb);
790
791
}

792
793
794
static void
free_current_bo (CoglOnscreen *onscreen)
{
795
796
  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
797

798
  g_clear_object (&onscreen_native->gbm.current_fb);
799
  free_current_secondary_bo (onscreen);
800
801
802
}

static void
803
meta_onscreen_native_queue_swap_notify (CoglOnscreen *onscreen)
804
{
805
806
  CoglOnscreenEGL *onscreen_egl =  onscreen->winsys;
  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
807
  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
808

809
810
811
  onscreen_native->pending_swap_notify_frame_count =
    onscreen_native->pending_queue_swap_notify_frame_count;

812
813
814
  if (onscreen_native->pending_swap_notify)
    return;

815
816
817
  /* We only want to notify that the swap is complete when the
   * application calls cogl_context_dispatch so instead of
   * immediately notifying we queue an idle callback */
818
  if (!renderer_native->swap_notify_idle)
819
    {
820
821
822
823
      CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
      CoglContext *cogl_context = framebuffer->context;
      CoglRenderer *cogl_renderer = cogl_context->display->renderer;

824
      renderer_native->swap_notify_idle =
825
        _cogl_poll_renderer_add_idle (cogl_renderer,
826
                                      flush_pending_swap_notify_idle,
827
                                      cogl_context,
828
829
830
                                      NULL);
    }

831
832
833
834
835
836
837
  /*
   * The framebuffer will have its own referenc while the swap notify is
   * pending. Otherwise when destroying the view would drop the pending
   * notification with if the destruction happens before the idle callback
   * is invoked.
   */
  cogl_object_ref (onscreen);
838
  onscreen_native->pending_swap_notify = TRUE;
839
840
}

841
static gboolean
842
meta_renderer_native_connect (CoglRenderer *cogl_renderer,
843
                              GError      **error)
844
{
845
  CoglRendererEGL *cogl_renderer_egl;
846
847
848
  MetaGpuKms *gpu_kms = cogl_renderer->custom_winsys_user_data;
  MetaRendererNative *renderer_native = meta_renderer_native_from_gpu (gpu_kms);
  MetaRendererNativeGpuData *renderer_gpu_data;
849
850

  cogl_renderer->winsys = g_slice_new0 (CoglRendererEGL);
851
  cogl_renderer_egl = cogl_renderer->winsys;
852

853
854
855
  renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
                                                         gpu_kms);

856
  cogl_renderer_egl->platform_vtable = &_cogl_winsys_egl_vtable;
857
858
  cogl_renderer_egl->platform = renderer_gpu_data;
  cogl_renderer_egl->edpy = renderer_gpu_data->egl_display;
859
860
861
862
863
864
865

  if (!_cogl_winsys_egl_renderer_connect_common (cogl_renderer, error))
    goto fail;

  return TRUE;

fail:
866
  meta_renderer_native_disconnect (cogl_renderer);
867
868
869
870

  return FALSE;
}

871
872
873
874
875
static int
meta_renderer_native_add_egl_config_attributes (CoglDisplay           *cogl_display,
                                                CoglFramebufferConfig *config,
                                                EGLint                *attributes)
{
876
  CoglRendererEGL *cogl_renderer_egl = cogl_display->renderer->winsys;
877
  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
878
879
  int i = 0;

880
  switch (renderer_gpu_data->mode)
881
882
883
884
885
886
887
888
889
890
891
892
    {
    case META_RENDERER_NATIVE_MODE_GBM:
      attributes[i++] = EGL_SURFACE_TYPE;
      attributes[i++] = EGL_WINDOW_BIT;
      break;
#ifdef HAVE_EGL_DEVICE
    case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
      attributes[i++] = EGL_SURFACE_TYPE;
      attributes[i++] = EGL_STREAM_BIT_KHR;
      break;
#endif
    }
893
894
895
896

  return i;
}

897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
static gboolean
choose_egl_config_from_gbm_format (MetaEgl       *egl,
                                   EGLDisplay     egl_display,
                                   const EGLint  *attributes,
                                   uint32_t       gbm_format,
                                   EGLConfig     *out_config,
                                   GError       **error)
{
  EGLConfig *egl_configs;
  EGLint n_configs;
  EGLint i;

  egl_configs = meta_egl_choose_all_configs (egl, egl_display,
                                             attributes,
                                             &n_configs,
                                             error);
  if (!egl_configs)
    return FALSE;

  for (i = 0; i < n_configs; i++)
    {
      EGLint visual_id;

      if (!meta_egl_get_config_attrib (egl, egl_display,
                                       egl_configs[i],
                                       EGL_NATIVE_VISUAL_ID,
                                       &visual_id,
                                       error))
        {
          g_free (egl_configs);
          return FALSE;
        }

      if ((uint32_t) visual_id == gbm_format)
        {
          *out_config = egl_configs[i];
          g_free (egl_configs);
          return TRUE;
        }
    }

  g_free (egl_configs);
  g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
               "No EGL config matching supported GBM format found");
  return FALSE;
}

static gboolean
meta_renderer_native_choose_egl_config (CoglDisplay  *cogl_display,
                                        EGLint       *attributes,
                                        EGLConfig    *out_config,
                                        GError      **error)
{
  CoglRenderer *cogl_renderer = cogl_display->renderer;
  CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
  MetaBackend *backend = meta_get_backend ();
  MetaEgl *egl = meta_backend_get_egl (backend);
954
  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
955
956
  EGLDisplay egl_display = cogl_renderer_egl->edpy;

957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
  switch (renderer_gpu_data->mode)
    {
    case META_RENDERER_NATIVE_MODE_GBM:
      return choose_egl_config_from_gbm_format (egl,
                                                egl_display,
                                                attributes,
                                                GBM_FORMAT_XRGB8888,
                                                out_config,
                                                error);
#ifdef HAVE_EGL_DEVICE
    case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
      return meta_egl_choose_first_config (egl,
                                           egl_display,
                                           attributes,
                                           out_config,
                                           error);
#endif
    }

  return FALSE;
977
978
}

979
static gboolean
980
meta_renderer_native_setup_egl_display (CoglDisplay *cogl_display,
981
                                        GError     **error)
982
{
983
984
  CoglDisplayEGL *cogl_display_egl = cogl_display->winsys;
  CoglRendererEGL *cogl_renderer_egl = cogl_display->renderer->winsys;
985
986
  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
  MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
987

988
  cogl_display_egl->platform = renderer_native;
989
990
991
992

  /* Force a full modeset / drmModeSetCrtc on
   * the first swap buffers call.
   */
993
  meta_renderer_native_queue_modes_reset (renderer_native);
994
995
996
997
998

  return TRUE;
}

static void
999
meta_renderer_native_destroy_egl_display (CoglDisplay *cogl_display)
1000
{