/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 |