1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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/. */
6 #define PANGO_ENABLE_BACKEND
7 #define PANGO_ENABLE_ENGINE
9 #include "gfxPlatformGtk.h"
12 #include <fontconfig/fontconfig.h>
14 #include "base/task.h"
15 #include "base/thread.h"
16 #include "base/message_loop.h"
18 #include "gfx2DGlue.h"
19 #include "gfxFcPlatformFontList.h"
20 #include "gfxConfig.h"
21 #include "gfxContext.h"
22 #include "gfxImageSurface.h"
23 #include "gfxUserFontSet.h"
25 #include "gfxFT2FontBase.h"
26 #include "gfxTextRun.h"
27 #include "GLContextProvider.h"
28 #include "mozilla/Atomics.h"
29 #include "mozilla/Components.h"
30 #include "mozilla/dom/ContentChild.h"
31 #include "mozilla/FontPropertyTypes.h"
32 #include "mozilla/gfx/2D.h"
33 #include "mozilla/gfx/Logging.h"
34 #include "mozilla/Monitor.h"
35 #include "mozilla/Preferences.h"
36 #include "mozilla/StaticPrefs_gfx.h"
37 #include "mozilla/StaticPrefs_layers.h"
38 #include "mozilla/StaticPrefs_media.h"
39 #include "nsAppRunner.h"
40 #include "nsIGfxInfo.h"
41 #include "nsMathUtils.h"
42 #include "nsUnicharUtils.h"
43 #include "nsUnicodeProperties.h"
45 #include "VsyncSource.h"
46 #include "mozilla/WidgetUtilsGtk.h"
49 # include "mozilla/gfx/XlibDisplay.h"
50 # include <gdk/gdkx.h>
51 # include <X11/extensions/Xrandr.h>
52 # include "cairo-xlib.h"
53 # include "gfxXlibSurface.h"
54 # include "GLContextGLX.h"
55 # include "GLXLibrary.h"
56 # include "mozilla/X11Util.h"
57 # include "SoftwareVsyncSource.h"
59 /* Undefine the Status from Xlib since it will conflict with system headers on
61 # if defined(__APPLE__) && defined(Status)
67 # include <gdk/gdkwayland.h>
68 # include "mozilla/widget/nsWaylandDisplay.h"
71 # include "mozilla/widget/DMABufLibWrapper.h"
72 # include "mozilla/StaticPrefs_widget.h"
75 #define GDK_PIXMAP_SIZE_MAX 32767
77 #define GFX_PREF_MAX_GENERIC_SUBSTITUTIONS \
78 "gfx.font_rendering.fontconfig.max_generic_substitutions"
80 using namespace mozilla
;
81 using namespace mozilla::gfx
;
82 using namespace mozilla::unicode
;
83 using namespace mozilla::widget
;
85 static FT_Library gPlatformFTLibrary
= nullptr;
88 static void screen_resolution_changed(GdkScreen
* aScreen
, GParamSpec
* aPspec
,
94 // TODO(aosmond): The envvar is deprecated. We should remove it once EGL is the
95 // default in release.
96 static bool IsX11EGLEnvvarEnabled() {
97 const char* eglPref
= PR_GetEnv("MOZ_X11_EGL");
98 return (eglPref
&& *eglPref
);
102 gfxPlatformGtk::gfxPlatformGtk() {
103 if (!gfxPlatform::IsHeadless()) {
104 if (!gtk_init_check(nullptr, nullptr)) {
105 gfxCriticalNote
<< "Failed to init Gtk, missing display? DISPLAY="
107 << " WAYLAND_DISPLAY=" << getenv("WAYLAND_DISPLAY")
113 mIsX11Display
= gfxPlatform::IsHeadless() ? false : GdkIsX11Display();
114 if (XRE_IsParentProcess()) {
116 if (IsWaylandDisplay() || gfxConfig::IsEnabled(Feature::X11_EGL
)) {
117 gfxVars::SetUseEGL(true);
120 if (gfxConfig::IsEnabled(Feature::DMABUF
)) {
121 gfxVars::SetUseDMABuf(true);
125 InitBackendPrefs(GetBackendPrefs());
127 gPlatformFTLibrary
= Factory::NewFTLibrary();
128 MOZ_RELEASE_ASSERT(gPlatformFTLibrary
);
129 Factory::SetFTLibrary(gPlatformFTLibrary
);
131 GdkScreen
* gdkScreen
= gdk_screen_get_default();
133 g_signal_connect(gdkScreen
, "notify::resolution",
134 G_CALLBACK(screen_resolution_changed
), nullptr);
137 // Bug 1714483: Force disable FXAA Antialiasing on NV drivers. This is a
138 // temporary workaround for a driver bug.
139 PR_SetEnv("__GL_ALLOW_FXAA_USAGE=0");
142 gfxPlatformGtk::~gfxPlatformGtk() {
143 Factory::ReleaseFTLibrary(gPlatformFTLibrary
);
144 gPlatformFTLibrary
= nullptr;
147 void gfxPlatformGtk::InitAcceleration() {
148 gfxPlatform::InitAcceleration();
150 if (XRE_IsContentProcess()) {
151 ImportCachedContentDeviceData();
155 void gfxPlatformGtk::InitX11EGLConfig() {
156 FeatureState
& feature
= gfxConfig::GetFeature(Feature::X11_EGL
);
158 feature
.EnableByDefault();
160 if (StaticPrefs::gfx_x11_egl_force_enabled_AtStartup()) {
161 feature
.UserForceEnable("Force enabled by pref");
162 } else if (IsX11EGLEnvvarEnabled()) {
163 feature
.UserForceEnable("Force enabled by envvar");
164 } else if (StaticPrefs::gfx_x11_egl_force_disabled_AtStartup()) {
165 feature
.UserDisable("Force disabled by pref",
166 "FEATURE_FAILURE_USER_FORCE_DISABLED"_ns
);
171 nsCOMPtr
<nsIGfxInfo
> gfxInfo
= components::GfxInfo::Service();
172 if (NS_FAILED(gfxInfo
->GetFeatureStatus(nsIGfxInfo::FEATURE_X11_EGL
,
173 failureId
, &status
))) {
174 feature
.Disable(FeatureStatus::BlockedNoGfxInfo
, "gfxInfo is broken",
175 "FEATURE_FAILURE_NO_GFX_INFO"_ns
);
176 } else if (status
!= nsIGfxInfo::FEATURE_STATUS_OK
) {
177 feature
.Disable(FeatureStatus::Blocklisted
, "Blocklisted by gfxInfo",
181 nsAutoString testType
;
182 gfxInfo
->GetTestType(testType
);
183 // We can only use X11/EGL if we actually found the EGL library and
184 // successfully use it to determine system information in glxtest.
185 if (testType
!= u
"EGL") {
186 feature
.ForceDisable(FeatureStatus::Broken
, "glxtest could not use EGL",
187 "FEATURE_FAILURE_GLXTEST_NO_EGL"_ns
);
190 if (feature
.IsEnabled() && IsX11Display()) {
191 // Enabling glthread crashes on X11/EGL, see bug 1670545
192 PR_SetEnv("mesa_glthread=false");
195 feature
.DisableByDefault(FeatureStatus::Unavailable
, "X11 support missing",
196 "FEATURE_FAILURE_NO_X11"_ns
);
200 void gfxPlatformGtk::InitDmabufConfig() {
201 FeatureState
& feature
= gfxConfig::GetFeature(Feature::DMABUF
);
202 feature
.EnableByDefault();
206 nsCOMPtr
<nsIGfxInfo
> gfxInfo
= components::GfxInfo::Service();
207 if (NS_FAILED(gfxInfo
->GetFeatureStatus(nsIGfxInfo::FEATURE_DMABUF
, failureId
,
209 feature
.Disable(FeatureStatus::BlockedNoGfxInfo
, "gfxInfo is broken",
210 "FEATURE_FAILURE_NO_GFX_INFO"_ns
);
211 } else if (status
!= nsIGfxInfo::FEATURE_STATUS_OK
) {
212 feature
.Disable(FeatureStatus::Blocklisted
, "Blocklisted by gfxInfo",
216 if (StaticPrefs::widget_dmabuf_force_enabled_AtStartup()) {
217 feature
.UserForceEnable("Force enabled by pref");
218 } else if (!StaticPrefs::widget_dmabuf_enabled_AtStartup()) {
219 feature
.UserDisable("Force disable by pref",
220 "FEATURE_FAILURE_USER_FORCE_DISABLED"_ns
);
223 if (!gfxVars::UseEGL()) {
224 feature
.ForceDisable(FeatureStatus::Unavailable
, "Requires EGL",
225 "FEATURE_FAILURE_REQUIRES_EGL"_ns
);
228 if (!gfxVars::WebglUseHardware()) {
229 feature
.Disable(FeatureStatus::Blocklisted
,
230 "DMABuf disabled with software rendering", failureId
);
233 nsAutoCString drmRenderDevice
;
234 gfxInfo
->GetDrmRenderDevice(drmRenderDevice
);
235 gfxVars::SetDrmRenderDevice(drmRenderDevice
);
237 if (feature
.IsEnabled()) {
238 if (!GetDMABufDevice()->IsEnabled(failureId
)) {
239 feature
.ForceDisable(FeatureStatus::Failed
, "Failed to configure",
245 bool gfxPlatformGtk::InitVAAPIConfig(bool aForceEnabledByUser
) {
246 FeatureState
& feature
=
247 gfxConfig::GetFeature(Feature::HARDWARE_VIDEO_DECODING
);
248 // We're already configured in parent process
249 if (!XRE_IsParentProcess()) {
250 return feature
.IsEnabled();
252 feature
.EnableByDefault();
254 if (aForceEnabledByUser
) {
255 feature
.UserForceEnable("Force enabled by pref");
258 int32_t status
= nsIGfxInfo::FEATURE_STATUS_UNKNOWN
;
259 nsCOMPtr
<nsIGfxInfo
> gfxInfo
= components::GfxInfo::Service();
261 if (NS_FAILED(gfxInfo
->GetFeatureStatus(
262 nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING
, failureId
, &status
))) {
263 feature
.Disable(FeatureStatus::BlockedNoGfxInfo
, "gfxInfo is broken",
264 "FEATURE_FAILURE_NO_GFX_INFO"_ns
);
265 } else if (status
== nsIGfxInfo::FEATURE_BLOCKED_PLATFORM_TEST
) {
266 feature
.ForceDisable(FeatureStatus::Unavailable
,
267 "Force disabled by gfxInfo", failureId
);
268 } else if (status
!= nsIGfxInfo::FEATURE_STATUS_OK
) {
269 feature
.Disable(FeatureStatus::Blocklisted
, "Blocklisted by gfxInfo",
272 if (!gfxVars::UseEGL()) {
273 feature
.ForceDisable(FeatureStatus::Unavailable
, "Requires EGL",
274 "FEATURE_FAILURE_REQUIRES_EGL"_ns
);
277 // Configure zero-copy playback feature.
278 if (feature
.IsEnabled()) {
279 FeatureState
& featureZeroCopy
=
280 gfxConfig::GetFeature(Feature::HW_DECODED_VIDEO_ZERO_COPY
);
282 featureZeroCopy
.EnableByDefault();
284 StaticPrefs::media_ffmpeg_vaapi_force_surface_zero_copy_AtStartup();
286 featureZeroCopy
.UserDisable("Force disable by pref",
287 "FEATURE_FAILURE_USER_FORCE_DISABLED"_ns
);
288 } else if (state
== 1) {
289 featureZeroCopy
.UserEnable("Force enabled by pref");
292 int32_t status
= nsIGfxInfo::FEATURE_STATUS_UNKNOWN
;
293 nsCOMPtr
<nsIGfxInfo
> gfxInfo
= components::GfxInfo::Service();
294 if (NS_FAILED(gfxInfo
->GetFeatureStatus(
295 nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY
, failureId
,
297 featureZeroCopy
.Disable(FeatureStatus::BlockedNoGfxInfo
,
299 "FEATURE_FAILURE_NO_GFX_INFO"_ns
);
300 } else if (status
== nsIGfxInfo::FEATURE_BLOCKED_PLATFORM_TEST
) {
301 featureZeroCopy
.ForceDisable(FeatureStatus::Unavailable
,
302 "Force disabled by gfxInfo", failureId
);
303 } else if (status
!= nsIGfxInfo::FEATURE_ALLOW_ALWAYS
) {
304 featureZeroCopy
.Disable(FeatureStatus::Blocklisted
,
305 "Blocklisted by gfxInfo", failureId
);
308 if (featureZeroCopy
.IsEnabled()) {
309 gfxVars::SetHwDecodedVideoZeroCopy(true);
312 return feature
.IsEnabled();
315 void gfxPlatformGtk::InitWebRenderConfig() {
316 gfxPlatform::InitWebRenderConfig();
318 if (!XRE_IsParentProcess()) {
322 FeatureState
& feature
= gfxConfig::GetFeature(Feature::WEBRENDER_COMPOSITOR
);
323 #ifdef RELEASE_OR_BETA
324 feature
.ForceDisable(FeatureStatus::Blocked
,
325 "Cannot be enabled in release or beta",
326 "FEATURE_FAILURE_DISABLE_RELEASE_OR_BETA"_ns
);
328 if (feature
.IsEnabled()) {
329 if (!IsWaylandDisplay()) {
330 feature
.ForceDisable(FeatureStatus::Unavailable
,
331 "Wayland support missing",
332 "FEATURE_FAILURE_NO_WAYLAND"_ns
);
335 else if (gfxConfig::IsEnabled(Feature::WEBRENDER
) &&
336 !gfxConfig::IsEnabled(Feature::DMABUF
)) {
337 // We use zwp_linux_dmabuf_v1 and GBM directly to manage FBOs. In theory
338 // this is also possible vie EGLstreams, but we don't bother to implement
339 // it as recent NVidia drivers support GBM and DMABuf as well.
340 feature
.ForceDisable(FeatureStatus::Unavailable
,
341 "Hardware Webrender requires DMAbuf support",
342 "FEATURE_FAILURE_NO_DMABUF"_ns
);
343 } else if (!widget::WaylandDisplayGet()->GetViewporter()) {
344 feature
.ForceDisable(FeatureStatus::Unavailable
,
345 "Requires wp_viewporter protocol support",
346 "FEATURE_FAILURE_REQUIRES_WPVIEWPORTER"_ns
);
348 # endif // MOZ_WAYLAND
350 #endif // RELEASE_OR_BETA
352 gfxVars::SetUseWebRenderCompositor(feature
.IsEnabled());
355 void gfxPlatformGtk::InitPlatformGPUProcessPrefs() {
357 if (IsWaylandDisplay()) {
358 FeatureState
& gpuProc
= gfxConfig::GetFeature(Feature::GPU_PROCESS
);
359 gpuProc
.ForceDisable(FeatureStatus::Blocked
,
360 "Wayland does not work in the GPU process",
361 "FEATURE_FAILURE_WAYLAND"_ns
);
366 already_AddRefed
<gfxASurface
> gfxPlatformGtk::CreateOffscreenSurface(
367 const IntSize
& aSize
, gfxImageFormat aFormat
) {
368 if (!Factory::AllowedSurfaceSize(aSize
)) {
372 RefPtr
<gfxASurface
> newSurface
;
373 bool needsClear
= true;
374 // XXX we really need a different interface here, something that passes
375 // in more context, including the display and/or target surface type that
376 // we should try to match
377 GdkScreen
* gdkScreen
= gdk_screen_get_default();
379 newSurface
= new gfxImageSurface(aSize
, aFormat
);
380 // The gfxImageSurface ctor zeroes this for us, no need to
381 // waste time clearing again
386 // We couldn't create a native surface for whatever reason;
387 // e.g., no display, no RENDER, bad size, etc.
388 // Fall back to image surface for the data.
389 newSurface
= new gfxImageSurface(aSize
, aFormat
);
392 if (newSurface
->CairoStatus()) {
393 newSurface
= nullptr; // surface isn't valid for some reason
396 if (newSurface
&& needsClear
) {
397 gfxUtils::ClearThebesSurface(newSurface
);
400 return newSurface
.forget();
403 nsresult
gfxPlatformGtk::GetFontList(nsAtom
* aLangGroup
,
404 const nsACString
& aGenericFamily
,
405 nsTArray
<nsString
>& aListOfFonts
) {
406 gfxPlatformFontList::PlatformFontList()->GetFontList(
407 aLangGroup
, aGenericFamily
, aListOfFonts
);
411 // xxx - this is ubuntu centric, need to go through other distros and flesh
412 // out a more general list
413 static const char kFontDejaVuSans
[] = "DejaVu Sans";
414 static const char kFontDejaVuSerif
[] = "DejaVu Serif";
415 static const char kFontFreeSans
[] = "FreeSans";
416 static const char kFontFreeSerif
[] = "FreeSerif";
417 static const char kFontTakaoPGothic
[] = "TakaoPGothic";
418 static const char kFontTwemojiMozilla
[] = "Twemoji Mozilla";
419 static const char kFontDroidSansFallback
[] = "Droid Sans Fallback";
420 static const char kFontWenQuanYiMicroHei
[] = "WenQuanYi Micro Hei";
421 static const char kFontNanumGothic
[] = "NanumGothic";
422 static const char kFontSymbola
[] = "Symbola";
423 static const char kFontNotoSansSymbols
[] = "Noto Sans Symbols";
424 static const char kFontNotoSansSymbols2
[] = "Noto Sans Symbols2";
426 void gfxPlatformGtk::GetCommonFallbackFonts(uint32_t aCh
, Script aRunScript
,
427 eFontPresentation aPresentation
,
428 nsTArray
<const char*>& aFontList
) {
429 if (PrefersColor(aPresentation
)) {
430 aFontList
.AppendElement(kFontTwemojiMozilla
);
433 aFontList
.AppendElement(kFontDejaVuSerif
);
434 aFontList
.AppendElement(kFontFreeSerif
);
435 aFontList
.AppendElement(kFontDejaVuSans
);
436 aFontList
.AppendElement(kFontFreeSans
);
437 aFontList
.AppendElement(kFontSymbola
);
438 aFontList
.AppendElement(kFontNotoSansSymbols
);
439 aFontList
.AppendElement(kFontNotoSansSymbols2
);
441 // add fonts for CJK ranges
442 // xxx - this isn't really correct, should use the same CJK font ordering
443 // as the pref font code
444 if (aCh
>= 0x3000 && ((aCh
< 0xe000) || (aCh
>= 0xf900 && aCh
< 0xfff0) ||
445 ((aCh
>> 16) == 2))) {
446 aFontList
.AppendElement(kFontTakaoPGothic
);
447 aFontList
.AppendElement(kFontDroidSansFallback
);
448 aFontList
.AppendElement(kFontWenQuanYiMicroHei
);
449 aFontList
.AppendElement(kFontNanumGothic
);
453 void gfxPlatformGtk::ReadSystemFontList(
454 mozilla::dom::SystemFontList
* retValue
) {
455 gfxFcPlatformFontList::PlatformFontList()->ReadSystemFontList(retValue
);
458 bool gfxPlatformGtk::CreatePlatformFontList() {
459 return gfxPlatformFontList::Initialize(new gfxFcPlatformFontList
);
462 int32_t gfxPlatformGtk::GetFontScaleDPI() {
463 MOZ_ASSERT(XRE_IsParentProcess(),
464 "You can access this via LookAndFeel if you need it in child "
466 if (MOZ_LIKELY(sDPI
!= 0)) {
469 GdkScreen
* screen
= gdk_screen_get_default();
470 // Ensure settings in config files are processed.
471 gtk_settings_get_for_screen(screen
);
472 int32_t dpi
= int32_t(round(gdk_screen_get_resolution(screen
)));
474 // Fall back to something reasonable
481 double gfxPlatformGtk::GetFontScaleFactor() {
482 // Integer scale factors work well with GTK window scaling, image scaling, and
483 // pixel alignment, but there is a range where 1 is too small and 2 is too
486 // An additional step of 1.5 is added because this is common scale on WINNT
487 // and at this ratio the advantages of larger rendering outweigh the
488 // disadvantages from scaling and pixel mis-alignment.
490 // A similar step for 1.25 is added as well, because this is the scale that
491 // "Large text" settings use in gnome, and it seems worth to allow, especially
492 // on already-hidpi environments.
493 int32_t dpi
= GetFontScaleDPI();
503 return round(dpi
/ 96.0);
506 gfxImageFormat
gfxPlatformGtk::GetOffscreenFormat() {
507 // Make sure there is a screen
508 GdkScreen
* screen
= gdk_screen_get_default();
509 if (screen
&& gdk_visual_get_depth(gdk_visual_get_system()) == 16) {
510 return SurfaceFormat::R5G6B5_UINT16
;
513 return SurfaceFormat::X8R8G8B8_UINT32
;
516 void gfxPlatformGtk::FontsPrefsChanged(const char* aPref
) {
517 // only checking for generic substitions, pass other changes up
518 if (strcmp(GFX_PREF_MAX_GENERIC_SUBSTITUTIONS
, aPref
) != 0) {
519 gfxPlatform::FontsPrefsChanged(aPref
);
523 gfxFcPlatformFontList
* pfl
= gfxFcPlatformFontList::PlatformFontList();
524 pfl
->ClearGenericMappings();
525 FlushFontAndWordCaches();
528 bool gfxPlatformGtk::AccelerateLayersByDefault() { return true; }
532 static nsTArray
<uint8_t> GetDisplayICCProfile(Display
* dpy
, Window
& root
) {
533 const char kIccProfileAtomName
[] = "_ICC_PROFILE";
534 Atom iccAtom
= XInternAtom(dpy
, kIccProfileAtomName
, TRUE
);
536 return nsTArray
<uint8_t>();
541 unsigned long retLength
, retAfter
;
542 unsigned char* retProperty
;
544 if (XGetWindowProperty(dpy
, root
, iccAtom
, 0, INT_MAX
/* length */, X11False
,
545 AnyPropertyType
, &retAtom
, &retFormat
, &retLength
,
546 &retAfter
, &retProperty
) != Success
) {
547 return nsTArray
<uint8_t>();
550 nsTArray
<uint8_t> result
;
553 result
.AppendElements(static_cast<uint8_t*>(retProperty
), retLength
);
561 nsTArray
<uint8_t> gfxPlatformGtk::GetPlatformCMSOutputProfileData() {
562 nsTArray
<uint8_t> prefProfileData
= GetPrefCMSOutputProfileData();
563 if (!prefProfileData
.IsEmpty()) {
564 return prefProfileData
;
567 if (XRE_IsContentProcess()) {
568 auto& cmsOutputProfileData
= GetCMSOutputProfileData();
569 // We should have set our profile data when we received our initial
570 // ContentDeviceData.
571 MOZ_ASSERT(cmsOutputProfileData
.isSome(),
572 "Should have created output profile data when we received "
573 "initial content device data.");
574 if (cmsOutputProfileData
.isSome()) {
575 return cmsOutputProfileData
.ref().Clone();
577 return nsTArray
<uint8_t>();
580 if (!mIsX11Display
) {
581 return nsTArray
<uint8_t>();
584 GdkDisplay
* display
= gdk_display_get_default();
585 Display
* dpy
= GDK_DISPLAY_XDISPLAY(display
);
586 // In xpcshell tests, we never initialize X and hence don't have a Display.
587 // In this case, there's no output colour management to be done, so we just
588 // return with nullptr.
590 return nsTArray
<uint8_t>();
593 Window root
= gdk_x11_get_default_root_xwindow();
595 // First try ICC Profile
596 nsTArray
<uint8_t> iccResult
= GetDisplayICCProfile(dpy
, root
);
597 if (!iccResult
.IsEmpty()) {
601 // If ICC doesn't work, then try EDID
602 const char kEdid1AtomName
[] = "XFree86_DDC_EDID1_RAWDATA";
603 Atom edidAtom
= XInternAtom(dpy
, kEdid1AtomName
, TRUE
);
605 return nsTArray
<uint8_t>();
610 unsigned long retLength
, retAfter
;
611 unsigned char* retProperty
;
613 if (XGetWindowProperty(dpy
, root
, edidAtom
, 0, 32, X11False
, AnyPropertyType
,
614 &retAtom
, &retFormat
, &retLength
, &retAfter
,
615 &retProperty
) != Success
) {
616 return nsTArray
<uint8_t>();
619 if (retLength
!= 128) {
620 return nsTArray
<uint8_t>();
623 // Format documented in "VESA E-EDID Implementation Guide"
624 float gamma
= (100 + (float)retProperty
[0x17]) / 100.0f
;
626 qcms_CIE_xyY whitePoint
;
628 ((retProperty
[0x21] << 2) | (retProperty
[0x1a] >> 2 & 3)) / 1024.0;
630 ((retProperty
[0x22] << 2) | (retProperty
[0x1a] >> 0 & 3)) / 1024.0;
633 qcms_CIE_xyYTRIPLE primaries
;
635 ((retProperty
[0x1b] << 2) | (retProperty
[0x19] >> 6 & 3)) / 1024.0;
637 ((retProperty
[0x1c] << 2) | (retProperty
[0x19] >> 4 & 3)) / 1024.0;
638 primaries
.red
.Y
= 1.0;
641 ((retProperty
[0x1d] << 2) | (retProperty
[0x19] >> 2 & 3)) / 1024.0;
643 ((retProperty
[0x1e] << 2) | (retProperty
[0x19] >> 0 & 3)) / 1024.0;
644 primaries
.green
.Y
= 1.0;
647 ((retProperty
[0x1f] << 2) | (retProperty
[0x1a] >> 6 & 3)) / 1024.0;
649 ((retProperty
[0x20] << 2) | (retProperty
[0x1a] >> 4 & 3)) / 1024.0;
650 primaries
.blue
.Y
= 1.0;
656 qcms_data_create_rgb_with_gamma(whitePoint
, primaries
, gamma
, &mem
, &size
);
658 return nsTArray
<uint8_t>();
661 nsTArray
<uint8_t> result
;
662 result
.AppendElements(static_cast<uint8_t*>(mem
), size
);
665 // XXX: It seems like we get wrong colors when using this constructed profile:
666 // See bug 1696819. For now just forget that we made it.
667 return nsTArray
<uint8_t>();
670 #else // defined(MOZ_X11)
672 nsTArray
<uint8_t> gfxPlatformGtk::GetPlatformCMSOutputProfileData() {
673 return nsTArray
<uint8_t>();
678 bool gfxPlatformGtk::CheckVariationFontSupport() {
679 // Although there was some variation/multiple-master support in FreeType
680 // in older versions, it seems too incomplete/unstable for us to use
681 // until at least 2.7.1.
682 FT_Int major
, minor
, patch
;
683 FT_Library_Version(Factory::GetFTLibrary(), &major
, &minor
, &patch
);
684 return major
* 1000000 + minor
* 1000 + patch
>= 2007001;
689 class GtkVsyncSource final
: public VsyncSource
{
692 : mGLContext(nullptr),
694 mSetupLock("GLXVsyncSetupLock"),
695 mVsyncThread("GLXVsyncThread"),
697 mVsyncEnabledLock("GLXVsyncEnabledLock"),
698 mVsyncEnabled(false) {
699 MOZ_ASSERT(NS_IsMainThread());
702 virtual ~GtkVsyncSource() { MOZ_ASSERT(NS_IsMainThread()); }
704 // Sets up the display's GL context on a worker thread.
705 // Required as GLContexts may only be used by the creating thread.
706 // Returns true if setup was a success.
708 MonitorAutoLock
lock(mSetupLock
);
709 MOZ_ASSERT(NS_IsMainThread());
710 if (!mVsyncThread
.Start()) return false;
712 RefPtr
<Runnable
> vsyncSetup
=
713 NewRunnableMethod("GtkVsyncSource::SetupGLContext", this,
714 &GtkVsyncSource::SetupGLContext
);
715 mVsyncThread
.message_loop()->PostTask(vsyncSetup
.forget());
716 // Wait until the setup has completed.
718 return mGLContext
!= nullptr;
721 // Called on the Vsync thread to setup the GL context.
722 void SetupGLContext() {
723 MonitorAutoLock
lock(mSetupLock
);
724 MOZ_ASSERT(!NS_IsMainThread());
725 MOZ_ASSERT(!mGLContext
, "GLContext already setup!");
727 // Create video sync timer on a separate Display to prevent locking the
728 // main thread X display.
729 mXDisplay
= XOpenDisplay(nullptr);
735 // Most compositors wait for vsync events on the root window.
736 Window root
= DefaultRootWindow(mXDisplay
);
737 int screen
= DefaultScreen(mXDisplay
);
741 bool forWebRender
= false;
742 if (!gl::GLContextGLX::FindFBConfigForWindow(
743 mXDisplay
, screen
, root
, &config
, &visid
, forWebRender
)) {
748 mGLContext
= gl::GLContextGLX::CreateGLContext(
749 {}, gfx::XlibDisplay::Borrow(mXDisplay
), root
, config
);
756 mGLContext
->MakeCurrent();
758 // Test that SGI_video_sync lets us get the counter.
759 unsigned int syncCounter
= 0;
760 if (gl::sGLXLibrary
.fGetVideoSync(&syncCounter
) != 0) {
761 mGLContext
= nullptr;
767 virtual void EnableVsync() override
{
768 MOZ_ASSERT(NS_IsMainThread());
769 MOZ_ASSERT(mGLContext
, "GLContext not setup!");
771 MonitorAutoLock
lock(mVsyncEnabledLock
);
775 mVsyncEnabled
= true;
777 // If the task has not nulled itself out, it hasn't yet realized
778 // that vsync was disabled earlier, so continue its execution.
780 mVsyncTask
= NewRunnableMethod("GtkVsyncSource::RunVsync", this,
781 &GtkVsyncSource::RunVsync
);
782 RefPtr
<Runnable
> addrefedTask
= mVsyncTask
;
783 mVsyncThread
.message_loop()->PostTask(addrefedTask
.forget());
787 virtual void DisableVsync() override
{
788 MonitorAutoLock
lock(mVsyncEnabledLock
);
789 mVsyncEnabled
= false;
792 virtual bool IsVsyncEnabled() override
{
793 MonitorAutoLock
lock(mVsyncEnabledLock
);
794 return mVsyncEnabled
;
797 virtual void Shutdown() override
{
798 MOZ_ASSERT(NS_IsMainThread());
801 // Cleanup thread-specific resources before shutting down.
802 RefPtr
<Runnable
> shutdownTask
= NewRunnableMethod(
803 "GtkVsyncSource::Cleanup", this, &GtkVsyncSource::Cleanup
);
804 mVsyncThread
.message_loop()->PostTask(shutdownTask
.forget());
806 // Stop, waiting for the cleanup task to finish execution.
812 MOZ_ASSERT(!NS_IsMainThread());
814 mGLContext
->MakeCurrent();
816 unsigned int syncCounter
= 0;
817 gl::sGLXLibrary
.fGetVideoSync(&syncCounter
);
820 MonitorAutoLock
lock(mVsyncEnabledLock
);
821 if (!mVsyncEnabled
) {
822 mVsyncTask
= nullptr;
827 TimeStamp lastVsync
= TimeStamp::Now();
828 bool useSoftware
= false;
830 // Wait until the video sync counter reaches the next value by waiting
831 // until the parity of the counter value changes.
832 unsigned int nextSync
= syncCounter
+ 1;
834 if ((status
= gl::sGLXLibrary
.fWaitVideoSync(2, (int)nextSync
% 2,
835 &syncCounter
)) != 0) {
836 gfxWarningOnce() << "glXWaitVideoSync returned " << status
;
840 if (syncCounter
== (nextSync
- 1)) {
842 << "glXWaitVideoSync failed to increment the sync counter.";
848 (1000.f
/ 60.f
) - (TimeStamp::Now() - lastVsync
).ToMilliseconds();
850 AUTO_PROFILER_THREAD_SLEEP
;
851 PlatformThread::Sleep((int)remaining
);
855 lastVsync
= TimeStamp::Now();
856 TimeStamp outputTime
= lastVsync
+ GetVsyncRate();
857 NotifyVsync(lastVsync
, outputTime
);
862 MOZ_ASSERT(!NS_IsMainThread());
864 mGLContext
= nullptr;
865 if (mXDisplay
) XCloseDisplay(mXDisplay
);
868 // Owned by the vsync thread.
869 RefPtr
<gl::GLContextGLX
> mGLContext
;
870 _XDisplay
* mXDisplay
;
871 Monitor mSetupLock MOZ_UNANNOTATED
;
872 base::Thread mVsyncThread
;
873 RefPtr
<Runnable
> mVsyncTask
;
874 Monitor mVsyncEnabledLock MOZ_UNANNOTATED
;
878 class XrandrSoftwareVsyncSource final
879 : public mozilla::gfx::SoftwareVsyncSource
{
881 XrandrSoftwareVsyncSource() : SoftwareVsyncSource(ComputeVsyncRate()) {
882 MOZ_ASSERT(NS_IsMainThread());
884 GdkScreen
* defaultScreen
= gdk_screen_get_default();
885 g_signal_connect(defaultScreen
, "monitors-changed",
886 G_CALLBACK(monitors_changed
), this);
890 // Request the current refresh rate via xrandr. It is hard to find the
891 // "correct" one, thus choose the highest one, assuming this will usually
892 // give the best user experience.
893 static mozilla::TimeDuration
ComputeVsyncRate() {
894 struct _XDisplay
* dpy
= gdk_x11_get_default_xdisplay();
896 // Use the default software refresh rate as lower bound. Allowing lower
897 // rates makes a bunch of tests start to fail on CI. The main goal of this
898 // VsyncSource is to support refresh rates greater than the default one.
899 double highestRefreshRate
= gfxPlatform::GetSoftwareVsyncRate();
901 // When running on remote X11 the xrandr version may be stuck on an
902 // ancient version. There are still setups using remote X11 out there, so
903 // make sure we don't crash.
904 int eventBase
, errorBase
, major
, minor
;
905 if (XRRQueryExtension(dpy
, &eventBase
, &errorBase
) &&
906 XRRQueryVersion(dpy
, &major
, &minor
) &&
907 (major
> 1 || (major
== 1 && minor
>= 3))) {
908 Window root
= gdk_x11_get_default_root_xwindow();
909 XRRScreenResources
* res
= XRRGetScreenResourcesCurrent(dpy
, root
);
912 // We can't use refresh rates far below the default one (60Hz) because
913 // otherwise random CI tests start to fail. However, many users have
914 // screens just below the default rate, e.g. 59.95Hz. So slightly
915 // decrease the lower bound.
916 highestRefreshRate
-= 1.0;
918 for (int i
= 0; i
< res
->noutput
; i
++) {
919 XRROutputInfo
* outputInfo
=
920 XRRGetOutputInfo(dpy
, res
, res
->outputs
[i
]);
922 if (outputInfo
->crtc
) {
923 XRRCrtcInfo
* crtcInfo
=
924 XRRGetCrtcInfo(dpy
, res
, outputInfo
->crtc
);
926 for (int j
= 0; j
< res
->nmode
; j
++) {
927 if (res
->modes
[j
].id
== crtcInfo
->mode
) {
928 double refreshRate
= mode_refresh(&res
->modes
[j
]);
929 if (refreshRate
> highestRefreshRate
) {
930 highestRefreshRate
= refreshRate
;
936 XRRFreeCrtcInfo(crtcInfo
);
940 XRRFreeOutputInfo(outputInfo
);
944 XRRFreeScreenResources(res
);
947 const double rate
= 1000.0 / highestRefreshRate
;
948 return mozilla::TimeDuration::FromMilliseconds(rate
);
951 static void monitors_changed(GdkScreen
* aScreen
, gpointer aClosure
) {
952 XrandrSoftwareVsyncSource
* self
=
953 static_cast<XrandrSoftwareVsyncSource
*>(aClosure
);
954 self
->SetVsyncRate(ComputeVsyncRate());
958 static double mode_refresh(const XRRModeInfo
* mode_info
) {
960 double vTotal
= mode_info
->vTotal
;
962 if (mode_info
->modeFlags
& RR_DoubleScan
) {
963 /* doublescan doubles the number of lines */
967 if (mode_info
->modeFlags
& RR_Interlace
) {
968 /* interlace splits the frame into two fields */
969 /* the field rate is what is typically reported by monitors */
973 if (mode_info
->hTotal
&& vTotal
) {
974 rate
= ((double)mode_info
->dotClock
/
975 ((double)mode_info
->hTotal
* (double)vTotal
));
984 already_AddRefed
<gfx::VsyncSource
>
985 gfxPlatformGtk::CreateGlobalHardwareVsyncSource() {
987 if (IsHeadless() || IsWaylandDisplay()) {
988 // On Wayland we can not create a global hardware based vsync source, thus
989 // use a software based one here. We create window specific ones later.
990 return GetSoftwareVsyncSource();
993 nsCOMPtr
<nsIGfxInfo
> gfxInfo
= components::GfxInfo::Service();
994 nsString windowProtocol
;
995 gfxInfo
->GetWindowProtocol(windowProtocol
);
996 bool isXwayland
= windowProtocol
.Find(u
"xwayland") != -1;
997 nsString adapterDriverVendor
;
998 gfxInfo
->GetAdapterDriverVendor(adapterDriverVendor
);
999 bool isMesa
= adapterDriverVendor
.Find(u
"mesa") != -1;
1001 // Only use GLX vsync when the OpenGL compositor / WebRender is being used.
1002 // The extra cost of initializing a GLX context while blocking the main thread
1003 // is not worth it when using basic composition. Do not use it on Xwayland, as
1004 // Xwayland will give us a software timer as we are listening for the root
1005 // window, which does not have a Wayland equivalent. Don't call
1006 // gl::sGLXLibrary.SupportsVideoSync() when EGL is used as NVIDIA drivers
1007 // refuse to use EGL GL context when GLX was initialized first and fail
1009 if (StaticPrefs::gfx_x11_glx_sgi_video_sync_AtStartup() &&
1010 gfxConfig::IsEnabled(Feature::HW_COMPOSITING
) && !isXwayland
&&
1011 (!gfxVars::UseEGL() || isMesa
) &&
1012 gl::sGLXLibrary
.SupportsVideoSync(DefaultXDisplay())) {
1013 RefPtr
<GtkVsyncSource
> vsyncSource
= new GtkVsyncSource();
1014 if (!vsyncSource
->Setup()) {
1015 NS_WARNING("Failed to setup GLContext, falling back to software vsync.");
1016 return GetSoftwareVsyncSource();
1018 return vsyncSource
.forget();
1021 RefPtr
<VsyncSource
> softwareVsync
= new XrandrSoftwareVsyncSource();
1022 return softwareVsync
.forget();
1024 return GetSoftwareVsyncSource();
1028 void gfxPlatformGtk::BuildContentDeviceData(ContentDeviceData
* aOut
) {
1029 gfxPlatform::BuildContentDeviceData(aOut
);
1031 aOut
->cmsOutputProfileData() = GetPlatformCMSOutputProfileData();
1034 // Wrapper for third party code (WebRTC for instance) where
1035 // gfxVars can't be included.
1036 namespace mozilla::gfx
{
1037 bool IsDMABufEnabled() { return gfxVars::UseDMABuf(); }
1038 } // namespace mozilla::gfx