Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/thebes/gfxPlatformGtk.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
 * This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#define PANGO_ENABLE_BACKEND
7
#define PANGO_ENABLE_ENGINE
8
9
#include "gfxPlatformGtk.h"
10
#include "prenv.h"
11
12
#include "nsUnicharUtils.h"
13
#include "nsUnicodeProperties.h"
14
#include "gfx2DGlue.h"
15
#include "gfxFcPlatformFontList.h"
16
#include "gfxConfig.h"
17
#include "gfxContext.h"
18
#include "gfxUserFontSet.h"
19
#include "gfxUtils.h"
20
#include "gfxFT2FontBase.h"
21
#include "gfxPrefs.h"
22
#include "gfxTextRun.h"
23
#include "VsyncSource.h"
24
#include "mozilla/Atomics.h"
25
#include "mozilla/Monitor.h"
26
#include "base/task.h"
27
#include "base/thread.h"
28
#include "base/message_loop.h"
29
#include "mozilla/FontPropertyTypes.h"
30
#include "mozilla/gfx/Logging.h"
31
32
#include "mozilla/gfx/2D.h"
33
34
#include "cairo.h"
35
#include <gtk/gtk.h>
36
37
#include "gfxImageSurface.h"
38
#ifdef MOZ_X11
39
#include <gdk/gdkx.h>
40
#include "gfxXlibSurface.h"
41
#include "cairo-xlib.h"
42
#include "mozilla/Preferences.h"
43
#include "mozilla/X11Util.h"
44
45
#include "GLContextProvider.h"
46
#include "GLContextGLX.h"
47
#include "GLXLibrary.h"
48
49
/* Undefine the Status from Xlib since it will conflict with system headers on OSX */
50
#if defined(__APPLE__) && defined(Status)
51
#undef Status
52
#endif
53
54
#ifdef MOZ_WAYLAND
55
#include <gdk/gdkwayland.h>
56
#endif
57
58
#endif /* MOZ_X11 */
59
60
#include <fontconfig/fontconfig.h>
61
62
#include "nsMathUtils.h"
63
64
#define GDK_PIXMAP_SIZE_MAX 32767
65
66
0
#define GFX_PREF_MAX_GENERIC_SUBSTITUTIONS "gfx.font_rendering.fontconfig.max_generic_substitutions"
67
68
using namespace mozilla;
69
using namespace mozilla::gfx;
70
using namespace mozilla::unicode;
71
using mozilla::dom::SystemFontListEntry;
72
73
gfxPlatformGtk::gfxPlatformGtk()
74
0
{
75
0
    if (!gfxPlatform::IsHeadless()) {
76
0
        gtk_init(nullptr, nullptr);
77
0
    }
78
0
79
0
    mMaxGenericSubstitutions = UNINITIALIZED_VALUE;
80
0
81
0
#ifdef MOZ_X11
82
0
    if (!gfxPlatform::IsHeadless() && XRE_IsParentProcess()) {
83
0
      if (GDK_IS_X11_DISPLAY(gdk_display_get_default()) &&
84
0
          mozilla::Preferences::GetBool("gfx.xrender.enabled"))
85
0
      {
86
0
          gfxVars::SetUseXRender(true);
87
0
      }
88
0
    }
89
0
#endif
90
0
91
0
    InitBackendPrefs(GetBackendPrefs());
92
0
93
0
#ifdef MOZ_X11
94
0
    if (gfxPlatform::IsHeadless() && GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
95
0
      mCompositorDisplay = XOpenDisplay(nullptr);
96
0
      MOZ_ASSERT(mCompositorDisplay, "Failed to create compositor display!");
97
0
    } else {
98
0
      mCompositorDisplay = nullptr;
99
0
    }
100
0
#endif // MOZ_X11
101
#ifdef MOZ_WAYLAND
102
    // Wayland compositors use g_get_monotonic_time() to get timestamps.
103
    mWaylandLastVsyncTimestamp = (g_get_monotonic_time() / 1000);
104
    // Set default display fps to 60
105
    mWaylandFrameDelay = 1000/60;
106
#endif
107
}
108
109
gfxPlatformGtk::~gfxPlatformGtk()
110
0
{
111
0
#ifdef MOZ_X11
112
0
    if (mCompositorDisplay) {
113
0
      XCloseDisplay(mCompositorDisplay);
114
0
    }
115
0
#endif // MOZ_X11
116
0
}
117
118
void
119
gfxPlatformGtk::FlushContentDrawing()
120
0
{
121
0
    if (gfxVars::UseXRender()) {
122
0
        XFlush(DefaultXDisplay());
123
0
    }
124
0
}
125
126
already_AddRefed<gfxASurface>
127
gfxPlatformGtk::CreateOffscreenSurface(const IntSize& aSize,
128
                                       gfxImageFormat aFormat)
129
0
{
130
0
    if (!Factory::AllowedSurfaceSize(aSize)) {
131
0
        return nullptr;
132
0
    }
133
0
134
0
    RefPtr<gfxASurface> newSurface;
135
0
    bool needsClear = true;
136
0
#ifdef MOZ_X11
137
0
    // XXX we really need a different interface here, something that passes
138
0
    // in more context, including the display and/or target surface type that
139
0
    // we should try to match
140
0
    GdkScreen *gdkScreen = gdk_screen_get_default();
141
0
    if (gdkScreen) {
142
0
        // When forcing PaintedLayers to use image surfaces for content,
143
0
        // force creation of gfxImageSurface surfaces.
144
0
        if (gfxVars::UseXRender() && !UseImageOffscreenSurfaces()) {
145
0
            Screen *screen = gdk_x11_screen_get_xscreen(gdkScreen);
146
0
            XRenderPictFormat* xrenderFormat =
147
0
                gfxXlibSurface::FindRenderFormat(DisplayOfScreen(screen),
148
0
                                                 aFormat);
149
0
150
0
            if (xrenderFormat) {
151
0
                newSurface = gfxXlibSurface::Create(screen, xrenderFormat,
152
0
                                                    aSize);
153
0
            }
154
0
        } else {
155
0
            // We're not going to use XRender, so we don't need to
156
0
            // search for a render format
157
0
            newSurface = new gfxImageSurface(aSize, aFormat);
158
0
            // The gfxImageSurface ctor zeroes this for us, no need to
159
0
            // waste time clearing again
160
0
            needsClear = false;
161
0
        }
162
0
    }
163
0
#endif
164
0
165
0
    if (!newSurface) {
166
0
        // We couldn't create a native surface for whatever reason;
167
0
        // e.g., no display, no RENDER, bad size, etc.
168
0
        // Fall back to image surface for the data.
169
0
        newSurface = new gfxImageSurface(aSize, aFormat);
170
0
    }
171
0
172
0
    if (newSurface->CairoStatus()) {
173
0
        newSurface = nullptr; // surface isn't valid for some reason
174
0
    }
175
0
176
0
    if (newSurface && needsClear) {
177
0
        gfxUtils::ClearThebesSurface(newSurface);
178
0
    }
179
0
180
0
    return newSurface.forget();
181
0
}
182
183
nsresult
184
gfxPlatformGtk::GetFontList(nsAtom *aLangGroup,
185
                            const nsACString& aGenericFamily,
186
                            nsTArray<nsString>& aListOfFonts)
187
0
{
188
0
    gfxPlatformFontList::PlatformFontList()->GetFontList(aLangGroup,
189
0
                                                         aGenericFamily,
190
0
                                                         aListOfFonts);
191
0
    return NS_OK;
192
0
}
193
194
nsresult
195
gfxPlatformGtk::UpdateFontList()
196
0
{
197
0
    gfxPlatformFontList::PlatformFontList()->UpdateFontList();
198
0
    return NS_OK;
199
0
}
200
201
// xxx - this is ubuntu centric, need to go through other distros and flesh
202
// out a more general list
203
static const char kFontDejaVuSans[] = "DejaVu Sans";
204
static const char kFontDejaVuSerif[] = "DejaVu Serif";
205
static const char kFontFreeSans[] = "FreeSans";
206
static const char kFontFreeSerif[] = "FreeSerif";
207
static const char kFontTakaoPGothic[] = "TakaoPGothic";
208
static const char kFontTwemojiMozilla[] = "Twemoji Mozilla";
209
static const char kFontDroidSansFallback[] = "Droid Sans Fallback";
210
static const char kFontWenQuanYiMicroHei[] = "WenQuanYi Micro Hei";
211
static const char kFontNanumGothic[] = "NanumGothic";
212
static const char kFontSymbola[] = "Symbola";
213
214
void
215
gfxPlatformGtk::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
216
                                       Script aRunScript,
217
                                       nsTArray<const char*>& aFontList)
218
0
{
219
0
    EmojiPresentation emoji = GetEmojiPresentation(aCh);
220
0
    if (emoji != EmojiPresentation::TextOnly) {
221
0
        if (aNextCh == kVariationSelector16 ||
222
0
           (aNextCh != kVariationSelector15 &&
223
0
            emoji == EmojiPresentation::EmojiDefault)) {
224
0
            // if char is followed by VS16, try for a color emoji glyph
225
0
            aFontList.AppendElement(kFontTwemojiMozilla);
226
0
        }
227
0
    }
228
0
229
0
    aFontList.AppendElement(kFontDejaVuSerif);
230
0
    aFontList.AppendElement(kFontFreeSerif);
231
0
    aFontList.AppendElement(kFontDejaVuSans);
232
0
    aFontList.AppendElement(kFontFreeSans);
233
0
    aFontList.AppendElement(kFontSymbola);
234
0
235
0
    // add fonts for CJK ranges
236
0
    // xxx - this isn't really correct, should use the same CJK font ordering
237
0
    // as the pref font code
238
0
    if (aCh >= 0x3000 &&
239
0
        ((aCh < 0xe000) ||
240
0
         (aCh >= 0xf900 && aCh < 0xfff0) ||
241
0
         ((aCh >> 16) == 2))) {
242
0
        aFontList.AppendElement(kFontTakaoPGothic);
243
0
        aFontList.AppendElement(kFontDroidSansFallback);
244
0
        aFontList.AppendElement(kFontWenQuanYiMicroHei);
245
0
        aFontList.AppendElement(kFontNanumGothic);
246
0
    }
247
0
}
248
249
void
250
gfxPlatformGtk::ReadSystemFontList(
251
    InfallibleTArray<SystemFontListEntry>* retValue)
252
0
{
253
0
    gfxFcPlatformFontList::PlatformFontList()->ReadSystemFontList(retValue);
254
0
}
255
256
gfxPlatformFontList*
257
gfxPlatformGtk::CreatePlatformFontList()
258
0
{
259
0
    gfxPlatformFontList* list = new gfxFcPlatformFontList();
260
0
    if (NS_SUCCEEDED(list->InitFontList())) {
261
0
        return list;
262
0
    }
263
0
    gfxPlatformFontList::Shutdown();
264
0
    return nullptr;
265
0
}
266
267
gfxFontGroup *
268
gfxPlatformGtk::CreateFontGroup(const FontFamilyList& aFontFamilyList,
269
                                const gfxFontStyle* aStyle,
270
                                gfxTextPerfMetrics* aTextPerf,
271
                                gfxUserFontSet* aUserFontSet,
272
                                gfxFloat aDevToCssSize)
273
0
{
274
0
    return new gfxFontGroup(aFontFamilyList, aStyle, aTextPerf,
275
0
                            aUserFontSet, aDevToCssSize);
276
0
}
277
278
FT_Library
279
gfxPlatformGtk::GetFTLibrary()
280
0
{
281
0
    return gfxFcPlatformFontList::GetFTLibrary();
282
0
}
283
284
static int32_t sDPI = 0;
285
286
int32_t
287
gfxPlatformGtk::GetFontScaleDPI()
288
0
{
289
0
    if (!sDPI) {
290
0
        // Make sure init is run so we have a resolution
291
0
        GdkScreen *screen = gdk_screen_get_default();
292
0
        gtk_settings_get_for_screen(screen);
293
0
        sDPI = int32_t(round(gdk_screen_get_resolution(screen)));
294
0
        if (sDPI <= 0) {
295
0
            // Fall back to something sane
296
0
            sDPI = 96;
297
0
        }
298
0
    }
299
0
    return sDPI;
300
0
}
301
302
double
303
gfxPlatformGtk::GetFontScaleFactor()
304
0
{
305
0
    // Integer scale factors work well with GTK window scaling, image scaling,
306
0
    // and pixel alignment, but there is a range where 1 is too small and 2 is
307
0
    // too big.  An additional step of 1.5 is added because this is common
308
0
    // scale on WINNT and at this ratio the advantages of larger rendering
309
0
    // outweigh the disadvantages from scaling and pixel mis-alignment.
310
0
    int32_t dpi = GetFontScaleDPI();
311
0
    if (dpi < 132) {
312
0
        return 1.0;
313
0
    }
314
0
    if (dpi < 168) {
315
0
        return 1.5;
316
0
    }
317
0
    return round(dpi/96.0);
318
0
319
0
}
320
321
bool
322
gfxPlatformGtk::UseImageOffscreenSurfaces()
323
0
{
324
0
    return GetDefaultContentBackend() != mozilla::gfx::BackendType::CAIRO ||
325
0
           gfxPrefs::UseImageOffscreenSurfaces();
326
0
}
327
328
gfxImageFormat
329
gfxPlatformGtk::GetOffscreenFormat()
330
0
{
331
0
    // Make sure there is a screen
332
0
    GdkScreen *screen = gdk_screen_get_default();
333
0
    if (screen && gdk_visual_get_depth(gdk_visual_get_system()) == 16) {
334
0
        return SurfaceFormat::R5G6B5_UINT16;
335
0
    }
336
0
337
0
    return SurfaceFormat::X8R8G8B8_UINT32;
338
0
}
339
340
void gfxPlatformGtk::FontsPrefsChanged(const char *aPref)
341
0
{
342
0
    // only checking for generic substitions, pass other changes up
343
0
    if (strcmp(GFX_PREF_MAX_GENERIC_SUBSTITUTIONS, aPref)) {
344
0
        gfxPlatform::FontsPrefsChanged(aPref);
345
0
        return;
346
0
    }
347
0
348
0
    mMaxGenericSubstitutions = UNINITIALIZED_VALUE;
349
0
    gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
350
0
    pfl->ClearGenericMappings();
351
0
    FlushFontAndWordCaches();
352
0
}
353
354
uint32_t gfxPlatformGtk::MaxGenericSubstitions()
355
0
{
356
0
    if (mMaxGenericSubstitutions == UNINITIALIZED_VALUE) {
357
0
        mMaxGenericSubstitutions =
358
0
            Preferences::GetInt(GFX_PREF_MAX_GENERIC_SUBSTITUTIONS, 3);
359
0
        if (mMaxGenericSubstitutions < 0) {
360
0
            mMaxGenericSubstitutions = 3;
361
0
        }
362
0
    }
363
0
364
0
    return uint32_t(mMaxGenericSubstitutions);
365
0
}
366
367
bool
368
gfxPlatformGtk::AccelerateLayersByDefault()
369
0
{
370
0
    return gfxPrefs::WebRenderAll();
371
0
}
372
373
void
374
gfxPlatformGtk::GetPlatformCMSOutputProfile(void *&mem, size_t &size)
375
0
{
376
0
    mem = nullptr;
377
0
    size = 0;
378
0
379
0
#ifdef MOZ_X11
380
0
    GdkDisplay *display = gdk_display_get_default();
381
0
    if (!GDK_IS_X11_DISPLAY(display))
382
0
        return;
383
0
384
0
    const char EDID1_ATOM_NAME[] = "XFree86_DDC_EDID1_RAWDATA";
385
0
    const char ICC_PROFILE_ATOM_NAME[] = "_ICC_PROFILE";
386
0
387
0
    Atom edidAtom, iccAtom;
388
0
    Display *dpy = GDK_DISPLAY_XDISPLAY(display);
389
0
    // In xpcshell tests, we never initialize X and hence don't have a Display.
390
0
    // In this case, there's no output colour management to be done, so we just
391
0
    // return with nullptr.
392
0
    if (!dpy)
393
0
        return;
394
0
395
0
    Window root = gdk_x11_get_default_root_xwindow();
396
0
397
0
    Atom retAtom;
398
0
    int retFormat;
399
0
    unsigned long retLength, retAfter;
400
0
    unsigned char *retProperty ;
401
0
402
0
    iccAtom = XInternAtom(dpy, ICC_PROFILE_ATOM_NAME, TRUE);
403
0
    if (iccAtom) {
404
0
        // read once to get size, once for the data
405
0
        if (Success == XGetWindowProperty(dpy, root, iccAtom,
406
0
                                          0, INT_MAX /* length */,
407
0
                                          False, AnyPropertyType,
408
0
                                          &retAtom, &retFormat, &retLength,
409
0
                                          &retAfter, &retProperty)) {
410
0
411
0
            if (retLength > 0) {
412
0
                void *buffer = malloc(retLength);
413
0
                if (buffer) {
414
0
                    memcpy(buffer, retProperty, retLength);
415
0
                    mem = buffer;
416
0
                    size = retLength;
417
0
                }
418
0
            }
419
0
420
0
            XFree(retProperty);
421
0
            if (size > 0) {
422
#ifdef DEBUG_tor
423
                fprintf(stderr,
424
                        "ICM profile read from %s successfully\n",
425
                        ICC_PROFILE_ATOM_NAME);
426
#endif
427
                return;
428
0
            }
429
0
        }
430
0
    }
431
0
432
0
    edidAtom = XInternAtom(dpy, EDID1_ATOM_NAME, TRUE);
433
0
    if (edidAtom) {
434
0
        if (Success == XGetWindowProperty(dpy, root, edidAtom, 0, 32,
435
0
                                          False, AnyPropertyType,
436
0
                                          &retAtom, &retFormat, &retLength,
437
0
                                          &retAfter, &retProperty)) {
438
0
            double gamma;
439
0
            qcms_CIE_xyY whitePoint;
440
0
            qcms_CIE_xyYTRIPLE primaries;
441
0
442
0
            if (retLength != 128) {
443
#ifdef DEBUG_tor
444
                fprintf(stderr, "Short EDID data\n");
445
#endif
446
                return;
447
0
            }
448
0
449
0
            // Format documented in "VESA E-EDID Implementation Guide"
450
0
451
0
            gamma = (100 + retProperty[0x17]) / 100.0;
452
0
            whitePoint.x = ((retProperty[0x21] << 2) |
453
0
                            (retProperty[0x1a] >> 2 & 3)) / 1024.0;
454
0
            whitePoint.y = ((retProperty[0x22] << 2) |
455
0
                            (retProperty[0x1a] >> 0 & 3)) / 1024.0;
456
0
            whitePoint.Y = 1.0;
457
0
458
0
            primaries.red.x = ((retProperty[0x1b] << 2) |
459
0
                               (retProperty[0x19] >> 6 & 3)) / 1024.0;
460
0
            primaries.red.y = ((retProperty[0x1c] << 2) |
461
0
                               (retProperty[0x19] >> 4 & 3)) / 1024.0;
462
0
            primaries.red.Y = 1.0;
463
0
464
0
            primaries.green.x = ((retProperty[0x1d] << 2) |
465
0
                                 (retProperty[0x19] >> 2 & 3)) / 1024.0;
466
0
            primaries.green.y = ((retProperty[0x1e] << 2) |
467
0
                                 (retProperty[0x19] >> 0 & 3)) / 1024.0;
468
0
            primaries.green.Y = 1.0;
469
0
470
0
            primaries.blue.x = ((retProperty[0x1f] << 2) |
471
0
                               (retProperty[0x1a] >> 6 & 3)) / 1024.0;
472
0
            primaries.blue.y = ((retProperty[0x20] << 2) |
473
0
                               (retProperty[0x1a] >> 4 & 3)) / 1024.0;
474
0
            primaries.blue.Y = 1.0;
475
0
476
0
            XFree(retProperty);
477
0
478
#ifdef DEBUG_tor
479
            fprintf(stderr, "EDID gamma: %f\n", gamma);
480
            fprintf(stderr, "EDID whitepoint: %f %f %f\n",
481
                    whitePoint.x, whitePoint.y, whitePoint.Y);
482
            fprintf(stderr, "EDID primaries: [%f %f %f] [%f %f %f] [%f %f %f]\n",
483
                    primaries.Red.x, primaries.Red.y, primaries.Red.Y,
484
                    primaries.Green.x, primaries.Green.y, primaries.Green.Y,
485
                    primaries.Blue.x, primaries.Blue.y, primaries.Blue.Y);
486
#endif
487
488
0
            qcms_data_create_rgb_with_gamma(whitePoint, primaries, gamma, &mem, &size);
489
0
490
#ifdef DEBUG_tor
491
            if (size > 0) {
492
                fprintf(stderr,
493
                        "ICM profile read from %s successfully\n",
494
                        EDID1_ATOM_NAME);
495
            }
496
#endif
497
        }
498
0
    }
499
0
#endif
500
0
}
501
502
bool
503
gfxPlatformGtk::CheckVariationFontSupport()
504
0
{
505
0
  // Although there was some variation/multiple-master support in FreeType
506
0
  // in older versions, it seems too incomplete/unstable for us to use
507
0
  // until at least 2.7.1.
508
0
  FT_Int major, minor, patch;
509
0
  FT_Library_Version(GetFTLibrary(), &major, &minor, &patch);
510
0
  return major * 1000000 + minor * 1000 + patch >= 2007001;
511
0
}
512
513
#ifdef MOZ_X11
514
515
class GtkVsyncSource final : public VsyncSource
516
{
517
public:
518
  GtkVsyncSource()
519
0
  {
520
0
    MOZ_ASSERT(NS_IsMainThread());
521
0
    mGlobalDisplay = new GLXDisplay();
522
0
  }
523
524
  virtual ~GtkVsyncSource()
525
0
  {
526
0
    MOZ_ASSERT(NS_IsMainThread());
527
0
  }
528
529
  virtual Display& GetGlobalDisplay() override
530
0
  {
531
0
    return *mGlobalDisplay;
532
0
  }
533
534
  class GLXDisplay final : public VsyncSource::Display
535
  {
536
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GLXDisplay)
537
538
  public:
539
    GLXDisplay() : mGLContext(nullptr)
540
                 , mXDisplay(nullptr)
541
                 , mSetupLock("GLXVsyncSetupLock")
542
                 , mVsyncThread("GLXVsyncThread")
543
                 , mVsyncTask(nullptr)
544
                 , mVsyncEnabledLock("GLXVsyncEnabledLock")
545
                 , mVsyncEnabled(false)
546
#ifdef MOZ_WAYLAND
547
                 , mIsWaylandDisplay(false)
548
#endif
549
0
    {
550
0
    }
551
552
    // Sets up the display's GL context on a worker thread.
553
    // Required as GLContexts may only be used by the creating thread.
554
    // Returns true if setup was a success.
555
    bool Setup()
556
0
    {
557
0
      MonitorAutoLock lock(mSetupLock);
558
0
      MOZ_ASSERT(NS_IsMainThread());
559
0
      if (!mVsyncThread.Start())
560
0
        return false;
561
0
562
0
      RefPtr<Runnable> vsyncSetup =
563
0
        NewRunnableMethod("GtkVsyncSource::GLXDisplay::SetupGLContext",
564
0
                          this,
565
0
                          &GLXDisplay::SetupGLContext);
566
0
      mVsyncThread.message_loop()->PostTask(vsyncSetup.forget());
567
0
      // Wait until the setup has completed.
568
0
      lock.Wait();
569
0
      return mGLContext != nullptr;
570
0
    }
571
572
#ifdef MOZ_WAYLAND
573
    bool SetupWayland()
574
    {
575
      MonitorAutoLock lock(mSetupLock);
576
      MOZ_ASSERT(NS_IsMainThread());
577
      mIsWaylandDisplay = true;
578
      return mVsyncThread.Start();
579
    }
580
#endif
581
582
    // Called on the Vsync thread to setup the GL context.
583
    void SetupGLContext()
584
0
    {
585
0
        MonitorAutoLock lock(mSetupLock);
586
0
        MOZ_ASSERT(!NS_IsMainThread());
587
0
        MOZ_ASSERT(!mGLContext, "GLContext already setup!");
588
0
589
0
        // Create video sync timer on a separate Display to prevent locking the
590
0
        // main thread X display.
591
0
        mXDisplay = XOpenDisplay(nullptr);
592
0
        if (!mXDisplay) {
593
0
          lock.NotifyAll();
594
0
          return;
595
0
        }
596
0
597
0
        // Most compositors wait for vsync events on the root window.
598
0
        Window root = DefaultRootWindow(mXDisplay);
599
0
        int screen = DefaultScreen(mXDisplay);
600
0
601
0
        ScopedXFree<GLXFBConfig> cfgs;
602
0
        GLXFBConfig config;
603
0
        int visid;
604
0
        bool forWebRender = false;
605
0
        if (!gl::GLContextGLX::FindFBConfigForWindow(mXDisplay, screen, root,
606
0
                                                     &cfgs, &config, &visid,
607
0
                                                     forWebRender)) {
608
0
          lock.NotifyAll();
609
0
          return;
610
0
        }
611
0
612
0
        mGLContext = gl::GLContextGLX::CreateGLContext(gl::CreateContextFlags::NONE,
613
0
                                                       gl::SurfaceCaps::Any(), false,
614
0
                                                       mXDisplay, root, config, false,
615
0
                                                       nullptr);
616
0
617
0
        if (!mGLContext) {
618
0
          lock.NotifyAll();
619
0
          return;
620
0
        }
621
0
622
0
        mGLContext->MakeCurrent();
623
0
624
0
        // Test that SGI_video_sync lets us get the counter.
625
0
        unsigned int syncCounter = 0;
626
0
        if (gl::sGLXLibrary.fGetVideoSync(&syncCounter) != 0) {
627
0
          mGLContext = nullptr;
628
0
        }
629
0
630
0
        lock.NotifyAll();
631
0
    }
632
633
    virtual void EnableVsync() override
634
0
    {
635
0
      MOZ_ASSERT(NS_IsMainThread());
636
0
#if !defined(MOZ_WAYLAND)
637
0
      MOZ_ASSERT(mGLContext, "GLContext not setup!");
638
0
#endif
639
0
640
0
      MonitorAutoLock lock(mVsyncEnabledLock);
641
0
      if (mVsyncEnabled) {
642
0
        return;
643
0
      }
644
0
      mVsyncEnabled = true;
645
0
646
0
      // If the task has not nulled itself out, it hasn't yet realized
647
0
      // that vsync was disabled earlier, so continue its execution.
648
0
      if (!mVsyncTask) {
649
0
        mVsyncTask = NewRunnableMethod(
650
0
          "GtkVsyncSource::GLXDisplay::RunVsync", this,
651
#if defined(MOZ_WAYLAND)
652
          mIsWaylandDisplay ? &GLXDisplay::RunVsyncWayland :
653
#endif
654
          &GLXDisplay::RunVsync);
655
0
        RefPtr<Runnable> addrefedTask = mVsyncTask;
656
0
        mVsyncThread.message_loop()->PostTask(addrefedTask.forget());
657
0
      }
658
0
    }
659
660
    virtual void DisableVsync() override
661
0
    {
662
0
      MonitorAutoLock lock(mVsyncEnabledLock);
663
0
      mVsyncEnabled = false;
664
0
    }
665
666
    virtual bool IsVsyncEnabled() override
667
0
    {
668
0
      MonitorAutoLock lock(mVsyncEnabledLock);
669
0
      return mVsyncEnabled;
670
0
    }
671
672
    virtual void Shutdown() override
673
0
    {
674
0
      MOZ_ASSERT(NS_IsMainThread());
675
0
      DisableVsync();
676
0
677
0
      // Cleanup thread-specific resources before shutting down.
678
0
      RefPtr<Runnable> shutdownTask = NewRunnableMethod(
679
0
        "GtkVsyncSource::GLXDisplay::Cleanup", this, &GLXDisplay::Cleanup);
680
0
      mVsyncThread.message_loop()->PostTask(shutdownTask.forget());
681
0
682
0
      // Stop, waiting for the cleanup task to finish execution.
683
0
      mVsyncThread.Stop();
684
0
    }
685
686
  private:
687
    virtual ~GLXDisplay()
688
0
    {
689
0
    }
690
691
    void RunVsync()
692
0
    {
693
0
      MOZ_ASSERT(!NS_IsMainThread());
694
0
695
0
      mGLContext->MakeCurrent();
696
0
697
0
      unsigned int syncCounter = 0;
698
0
      gl::sGLXLibrary.fGetVideoSync(&syncCounter);
699
0
      for (;;) {
700
0
        {
701
0
          MonitorAutoLock lock(mVsyncEnabledLock);
702
0
          if (!mVsyncEnabled) {
703
0
            mVsyncTask = nullptr;
704
0
            return;
705
0
          }
706
0
        }
707
0
708
0
        TimeStamp lastVsync = TimeStamp::Now();
709
0
        bool useSoftware = false;
710
0
711
0
        // Wait until the video sync counter reaches the next value by waiting
712
0
        // until the parity of the counter value changes.
713
0
        unsigned int nextSync = syncCounter + 1;
714
0
        int status;
715
0
        if ((status = gl::sGLXLibrary.fWaitVideoSync(2, nextSync % 2, &syncCounter)) != 0) {
716
0
          gfxWarningOnce() << "glXWaitVideoSync returned " << status;
717
0
          useSoftware = true;
718
0
        }
719
0
720
0
        if (syncCounter == (nextSync - 1)) {
721
0
          gfxWarningOnce() << "glXWaitVideoSync failed to increment the sync counter.";
722
0
          useSoftware = true;
723
0
        }
724
0
725
0
        if (useSoftware) {
726
0
          double remaining = (1000.f / 60.f) -
727
0
            (TimeStamp::Now() - lastVsync).ToMilliseconds();
728
0
          if (remaining > 0) {
729
0
            PlatformThread::Sleep(remaining);
730
0
          }
731
0
        }
732
0
733
0
        lastVsync = TimeStamp::Now();
734
0
        NotifyVsync(lastVsync);
735
0
      }
736
0
    }
737
738
#ifdef MOZ_WAYLAND
739
    /* VSync on Wayland is tricky as we can get only "last VSync" event signal.
740
     * That means we should draw next frame at "last Vsync + frame delay" time.
741
     */
742
    void RunVsyncWayland()
743
    {
744
      MOZ_ASSERT(!NS_IsMainThread());
745
746
      for (;;) {
747
        {
748
          MonitorAutoLock lock(mVsyncEnabledLock);
749
          if (!mVsyncEnabled) {
750
            mVsyncTask = nullptr;
751
            return;
752
          }
753
        }
754
755
        gint64 lastVsync = gfxPlatformGtk::GetPlatform()->GetWaylandLastVsync();
756
        gint64 currTime = (g_get_monotonic_time() / 1000);
757
758
        gint64 remaining = gfxPlatformGtk::GetPlatform()->GetWaylandFrameDelay() -
759
          (currTime - lastVsync);
760
        if (remaining > 0) {
761
          PlatformThread::Sleep(remaining);
762
        } else {
763
          // Time from last HW Vsync is longer than our frame delay,
764
          // use our approximation then.
765
          gfxPlatformGtk::GetPlatform()->SetWaylandLastVsync(currTime);
766
        }
767
768
        NotifyVsync(TimeStamp::Now());
769
      }
770
    }
771
#endif
772
773
0
    void Cleanup() {
774
0
      MOZ_ASSERT(!NS_IsMainThread());
775
0
776
0
      mGLContext = nullptr;
777
0
      if (mXDisplay)
778
0
        XCloseDisplay(mXDisplay);
779
0
    }
780
781
    // Owned by the vsync thread.
782
    RefPtr<gl::GLContextGLX> mGLContext;
783
    _XDisplay* mXDisplay;
784
    Monitor mSetupLock;
785
    base::Thread mVsyncThread;
786
    RefPtr<Runnable> mVsyncTask;
787
    Monitor mVsyncEnabledLock;
788
    bool mVsyncEnabled;
789
#ifdef MOZ_WAYLAND
790
    bool mIsWaylandDisplay;
791
#endif
792
  };
793
private:
794
  // We need a refcounted VsyncSource::Display to use chromium IPC runnables.
795
  RefPtr<GLXDisplay> mGlobalDisplay;
796
};
797
798
already_AddRefed<gfx::VsyncSource>
799
gfxPlatformGtk::CreateHardwareVsyncSource()
800
0
{
801
#ifdef MOZ_WAYLAND
802
  if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
803
    RefPtr<VsyncSource> vsyncSource = new GtkVsyncSource();
804
    VsyncSource::Display& display = vsyncSource->GetGlobalDisplay();
805
    static_cast<GtkVsyncSource::GLXDisplay&>(display).SetupWayland();
806
    return vsyncSource.forget();
807
  }
808
#endif
809
810
0
  // Only use GLX vsync when the OpenGL compositor is being used.
811
0
  // The extra cost of initializing a GLX context while blocking the main
812
0
  // thread is not worth it when using basic composition.
813
0
  if (gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
814
0
    if (gl::sGLXLibrary.SupportsVideoSync()) {
815
0
      RefPtr<VsyncSource> vsyncSource = new GtkVsyncSource();
816
0
      VsyncSource::Display& display = vsyncSource->GetGlobalDisplay();
817
0
      if (!static_cast<GtkVsyncSource::GLXDisplay&>(display).Setup()) {
818
0
        NS_WARNING("Failed to setup GLContext, falling back to software vsync.");
819
0
        return gfxPlatform::CreateHardwareVsyncSource();
820
0
      }
821
0
      return vsyncSource.forget();
822
0
    }
823
0
    NS_WARNING("SGI_video_sync unsupported. Falling back to software vsync.");
824
0
  }
825
0
  return gfxPlatform::CreateHardwareVsyncSource();
826
0
}
827
828
#endif