/src/mozilla-central/gfx/gl/GLContextProviderGLX.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 | | #ifdef MOZ_WIDGET_GTK |
7 | | #include <gdk/gdk.h> |
8 | | #include <gdk/gdkx.h> |
9 | 0 | #define GET_NATIVE_WINDOW(aWidget) GDK_WINDOW_XID((GdkWindow*) aWidget->GetNativeData(NS_NATIVE_WINDOW)) |
10 | | #endif |
11 | | |
12 | | #include <X11/Xlib.h> |
13 | | #include <X11/Xutil.h> |
14 | | #include "X11UndefineNone.h" |
15 | | |
16 | | #include "mozilla/MathAlgorithms.h" |
17 | | #include "mozilla/StaticPtr.h" |
18 | | #include "mozilla/layers/CompositorOptions.h" |
19 | | #include "mozilla/Range.h" |
20 | | #include "mozilla/ScopeExit.h" |
21 | | #include "mozilla/widget/CompositorWidget.h" |
22 | | #include "mozilla/widget/GtkCompositorWidget.h" |
23 | | #include "mozilla/Unused.h" |
24 | | |
25 | | #include "prenv.h" |
26 | | #include "GLContextProvider.h" |
27 | | #include "GLLibraryLoader.h" |
28 | | #include "nsDebug.h" |
29 | | #include "nsIWidget.h" |
30 | | #include "GLXLibrary.h" |
31 | | #include "gfxXlibSurface.h" |
32 | | #include "gfxContext.h" |
33 | | #include "gfxEnv.h" |
34 | | #include "gfxPlatform.h" |
35 | | #include "GLContextGLX.h" |
36 | | #include "gfxUtils.h" |
37 | | #include "gfx2DGlue.h" |
38 | | #include "GLScreenBuffer.h" |
39 | | #include "gfxPrefs.h" |
40 | | |
41 | | #include "gfxCrashReporterUtils.h" |
42 | | |
43 | | #ifdef MOZ_WIDGET_GTK |
44 | | #include "gfxPlatformGtk.h" |
45 | | #endif |
46 | | |
47 | | namespace mozilla { |
48 | | namespace gl { |
49 | | |
50 | | using namespace mozilla::gfx; |
51 | | using namespace mozilla::widget; |
52 | | |
53 | | GLXLibrary sGLXLibrary; |
54 | | |
55 | | static inline bool |
56 | | HasExtension(const char* aExtensions, const char* aRequiredExtension) |
57 | 0 | { |
58 | 0 | return GLContext::ListHasExtension( |
59 | 0 | reinterpret_cast<const GLubyte*>(aExtensions), aRequiredExtension); |
60 | 0 | } |
61 | | |
62 | | bool |
63 | | GLXLibrary::EnsureInitialized() |
64 | 0 | { |
65 | 0 | if (mInitialized) { |
66 | 0 | return true; |
67 | 0 | } |
68 | 0 | |
69 | 0 | // Don't repeatedly try to initialize. |
70 | 0 | if (mTriedInitializing) { |
71 | 0 | return false; |
72 | 0 | } |
73 | 0 | mTriedInitializing = true; |
74 | 0 |
|
75 | 0 | // Force enabling s3 texture compression. (Bug 774134) |
76 | 0 | PR_SetEnv("force_s3tc_enable=true"); |
77 | 0 |
|
78 | 0 | if (!mOGLLibrary) { |
79 | 0 | const char* libGLfilename = nullptr; |
80 | 0 | bool forceFeatureReport = false; |
81 | 0 |
|
82 | 0 | // see e.g. bug 608526: it is intrinsically interesting to know whether we have dynamically linked to libGL.so.1 |
83 | 0 | // because at least the NVIDIA implementation requires an executable stack, which causes mprotect calls, |
84 | 0 | // which trigger glibc bug http://sourceware.org/bugzilla/show_bug.cgi?id=12225 |
85 | | #ifdef __OpenBSD__ |
86 | | libGLfilename = "libGL.so"; |
87 | | #else |
88 | | libGLfilename = "libGL.so.1"; |
89 | 0 | #endif |
90 | 0 |
|
91 | 0 | ScopedGfxFeatureReporter reporter(libGLfilename, forceFeatureReport); |
92 | 0 | mOGLLibrary = PR_LoadLibrary(libGLfilename); |
93 | 0 | if (!mOGLLibrary) { |
94 | 0 | NS_WARNING("Couldn't load OpenGL shared library."); |
95 | 0 | return false; |
96 | 0 | } |
97 | 0 | reporter.SetSuccessful(); |
98 | 0 | } |
99 | 0 |
|
100 | 0 | if (gfxEnv::GlxDebug()) { |
101 | 0 | mDebug = true; |
102 | 0 | } |
103 | 0 |
|
104 | 0 | #define SYMBOL(X) { (PRFuncPtr*)&mSymbols.f##X, { "glX" #X, nullptr } } |
105 | 0 | #define END_OF_SYMBOLS { nullptr, { nullptr } } |
106 | 0 |
|
107 | 0 | const GLLibraryLoader::SymLoadStruct symbols[] = { |
108 | 0 | /* functions that were in GLX 1.0 */ |
109 | 0 | SYMBOL(DestroyContext), |
110 | 0 | SYMBOL(MakeCurrent), |
111 | 0 | SYMBOL(SwapBuffers), |
112 | 0 | SYMBOL(QueryVersion), |
113 | 0 | SYMBOL(GetConfig), |
114 | 0 | SYMBOL(GetCurrentContext), |
115 | 0 | SYMBOL(WaitGL), |
116 | 0 | SYMBOL(WaitX), |
117 | 0 |
|
118 | 0 | /* functions introduced in GLX 1.1 */ |
119 | 0 | SYMBOL(QueryExtensionsString), |
120 | 0 | SYMBOL(GetClientString), |
121 | 0 | SYMBOL(QueryServerString), |
122 | 0 |
|
123 | 0 | /* functions introduced in GLX 1.3 */ |
124 | 0 | SYMBOL(ChooseFBConfig), |
125 | 0 | SYMBOL(ChooseVisual), |
126 | 0 | SYMBOL(GetFBConfigAttrib), |
127 | 0 | SYMBOL(GetFBConfigs), |
128 | 0 | SYMBOL(CreatePixmap), |
129 | 0 | SYMBOL(DestroyPixmap), |
130 | 0 | SYMBOL(CreateNewContext), |
131 | 0 |
|
132 | 0 | // Core in GLX 1.4, ARB extension before. |
133 | 0 | { (PRFuncPtr*)&mSymbols.fGetProcAddress, { "glXGetProcAddress", |
134 | 0 | "glXGetProcAddressARB", |
135 | 0 | nullptr } }, |
136 | 0 | END_OF_SYMBOLS |
137 | 0 | }; |
138 | 0 | if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, symbols)) { |
139 | 0 | NS_WARNING("Couldn't load required GLX symbols."); |
140 | 0 | return false; |
141 | 0 | } |
142 | 0 |
|
143 | 0 | Display* display = DefaultXDisplay(); |
144 | 0 | int screen = DefaultScreen(display); |
145 | 0 |
|
146 | 0 | { |
147 | 0 | int major, minor; |
148 | 0 | if (!fQueryVersion(display, &major, &minor) || |
149 | 0 | major != 1 || minor < 3) |
150 | 0 | { |
151 | 0 | NS_ERROR("GLX version older than 1.3. (released in 1998)"); |
152 | 0 | return false; |
153 | 0 | } |
154 | 0 | } |
155 | 0 |
|
156 | 0 | const GLLibraryLoader::SymLoadStruct symbols_texturefrompixmap[] = { |
157 | 0 | SYMBOL(BindTexImageEXT), |
158 | 0 | SYMBOL(ReleaseTexImageEXT), |
159 | 0 | END_OF_SYMBOLS |
160 | 0 | }; |
161 | 0 |
|
162 | 0 | const GLLibraryLoader::SymLoadStruct symbols_createcontext[] = { |
163 | 0 | SYMBOL(CreateContextAttribsARB), |
164 | 0 | END_OF_SYMBOLS |
165 | 0 | }; |
166 | 0 |
|
167 | 0 | const GLLibraryLoader::SymLoadStruct symbols_videosync[] = { |
168 | 0 | SYMBOL(GetVideoSyncSGI), |
169 | 0 | SYMBOL(WaitVideoSyncSGI), |
170 | 0 | END_OF_SYMBOLS |
171 | 0 | }; |
172 | 0 |
|
173 | 0 | const GLLibraryLoader::SymLoadStruct symbols_swapcontrol[] = { |
174 | 0 | SYMBOL(SwapIntervalEXT), |
175 | 0 | END_OF_SYMBOLS |
176 | 0 | }; |
177 | 0 |
|
178 | 0 | const auto lookupFunction = |
179 | 0 | (GLLibraryLoader::PlatformLookupFunction)mSymbols.fGetProcAddress; |
180 | 0 |
|
181 | 0 | const auto fnLoadSymbols = [&](const GLLibraryLoader::SymLoadStruct* symbols) { |
182 | 0 | if (GLLibraryLoader::LoadSymbols(mOGLLibrary, symbols, lookupFunction)) |
183 | 0 | return true; |
184 | 0 | |
185 | 0 | GLLibraryLoader::ClearSymbols(symbols); |
186 | 0 | return false; |
187 | 0 | }; |
188 | 0 |
|
189 | 0 | const char* clientVendor = fGetClientString(display, LOCAL_GLX_VENDOR); |
190 | 0 | const char* serverVendor = fQueryServerString(display, screen, LOCAL_GLX_VENDOR); |
191 | 0 | const char* extensionsStr = fQueryExtensionsString(display, screen); |
192 | 0 |
|
193 | 0 | if (HasExtension(extensionsStr, "GLX_EXT_texture_from_pixmap") && |
194 | 0 | fnLoadSymbols(symbols_texturefrompixmap)) |
195 | 0 | { |
196 | 0 | mUseTextureFromPixmap = gfxPrefs::UseGLXTextureFromPixmap(); |
197 | 0 | } else { |
198 | 0 | mUseTextureFromPixmap = false; |
199 | 0 | NS_WARNING("Texture from pixmap disabled"); |
200 | 0 | } |
201 | 0 |
|
202 | 0 | if (HasExtension(extensionsStr, "GLX_ARB_create_context") && |
203 | 0 | HasExtension(extensionsStr, "GLX_ARB_create_context_profile") && |
204 | 0 | fnLoadSymbols(symbols_createcontext)) |
205 | 0 | { |
206 | 0 | mHasCreateContextAttribs = true; |
207 | 0 | } |
208 | 0 |
|
209 | 0 | if (HasExtension(extensionsStr, "GLX_ARB_create_context_robustness")) { |
210 | 0 | mHasRobustness = true; |
211 | 0 | } |
212 | 0 |
|
213 | 0 | if (HasExtension(extensionsStr, "GLX_NV_robustness_video_memory_purge")) { |
214 | 0 | mHasVideoMemoryPurge = true; |
215 | 0 | } |
216 | 0 |
|
217 | 0 | if (HasExtension(extensionsStr, "GLX_SGI_video_sync") && |
218 | 0 | fnLoadSymbols(symbols_videosync)) |
219 | 0 | { |
220 | 0 | mHasVideoSync = true; |
221 | 0 | } |
222 | 0 |
|
223 | 0 | if (!HasExtension(extensionsStr, "GLX_EXT_swap_control") || |
224 | 0 | !fnLoadSymbols(symbols_swapcontrol)) |
225 | 0 | { |
226 | 0 | NS_WARNING("GLX_swap_control unsupported, ASAP mode may still block on buffer swaps."); |
227 | 0 | } |
228 | 0 |
|
229 | 0 | mIsATI = serverVendor && DoesStringMatch(serverVendor, "ATI"); |
230 | 0 | mIsNVIDIA = serverVendor && DoesStringMatch(serverVendor, "NVIDIA Corporation"); |
231 | 0 | mClientIsMesa = clientVendor && DoesStringMatch(clientVendor, "Mesa"); |
232 | 0 |
|
233 | 0 | mInitialized = true; |
234 | 0 |
|
235 | 0 | return true; |
236 | 0 | } |
237 | | |
238 | | bool |
239 | | GLXLibrary::SupportsTextureFromPixmap(gfxASurface* aSurface) |
240 | 0 | { |
241 | 0 | if (!EnsureInitialized()) { |
242 | 0 | return false; |
243 | 0 | } |
244 | 0 | |
245 | 0 | if (aSurface->GetType() != gfxSurfaceType::Xlib || !mUseTextureFromPixmap) { |
246 | 0 | return false; |
247 | 0 | } |
248 | 0 | |
249 | 0 | return true; |
250 | 0 | } |
251 | | |
252 | | bool |
253 | | GLXLibrary::SupportsVideoSync() |
254 | 0 | { |
255 | 0 | if (!EnsureInitialized()) { |
256 | 0 | return false; |
257 | 0 | } |
258 | 0 | |
259 | 0 | return mHasVideoSync; |
260 | 0 | } |
261 | | |
262 | | GLXPixmap |
263 | | GLXLibrary::CreatePixmap(gfxASurface* aSurface) |
264 | 0 | { |
265 | 0 | if (!SupportsTextureFromPixmap(aSurface)) { |
266 | 0 | return X11None; |
267 | 0 | } |
268 | 0 |
|
269 | 0 | gfxXlibSurface* xs = static_cast<gfxXlibSurface*>(aSurface); |
270 | 0 | const XRenderPictFormat* format = xs->XRenderFormat(); |
271 | 0 | if (!format || format->type != PictTypeDirect) { |
272 | 0 | return X11None; |
273 | 0 | } |
274 | 0 | const XRenderDirectFormat& direct = format->direct; |
275 | 0 | int alphaSize = FloorLog2(direct.alphaMask + 1); |
276 | 0 | NS_ASSERTION((1 << alphaSize) - 1 == direct.alphaMask, |
277 | 0 | "Unexpected render format with non-adjacent alpha bits"); |
278 | 0 |
|
279 | 0 | int attribs[] = { LOCAL_GLX_DOUBLEBUFFER, False, |
280 | 0 | LOCAL_GLX_DRAWABLE_TYPE, LOCAL_GLX_PIXMAP_BIT, |
281 | 0 | LOCAL_GLX_ALPHA_SIZE, alphaSize, |
282 | 0 | (alphaSize ? LOCAL_GLX_BIND_TO_TEXTURE_RGBA_EXT |
283 | 0 | : LOCAL_GLX_BIND_TO_TEXTURE_RGB_EXT), True, |
284 | 0 | LOCAL_GLX_RENDER_TYPE, LOCAL_GLX_RGBA_BIT, |
285 | 0 | X11None }; |
286 | 0 |
|
287 | 0 | int numConfigs = 0; |
288 | 0 | Display* display = xs->XDisplay(); |
289 | 0 | int xscreen = DefaultScreen(display); |
290 | 0 |
|
291 | 0 | ScopedXFree<GLXFBConfig> cfgs(fChooseFBConfig(display, |
292 | 0 | xscreen, |
293 | 0 | attribs, |
294 | 0 | &numConfigs)); |
295 | 0 |
|
296 | 0 | // Find an fbconfig that matches the pixel format used on the Pixmap. |
297 | 0 | int matchIndex = -1; |
298 | 0 | unsigned long redMask = |
299 | 0 | static_cast<unsigned long>(direct.redMask) << direct.red; |
300 | 0 | unsigned long greenMask = |
301 | 0 | static_cast<unsigned long>(direct.greenMask) << direct.green; |
302 | 0 | unsigned long blueMask = |
303 | 0 | static_cast<unsigned long>(direct.blueMask) << direct.blue; |
304 | 0 | // This is true if the Pixmap has bits for alpha or unused bits. |
305 | 0 | bool haveNonColorBits = |
306 | 0 | ~(redMask | greenMask | blueMask) != -1UL << format->depth; |
307 | 0 |
|
308 | 0 | for (int i = 0; i < numConfigs; i++) { |
309 | 0 | int id = X11None; |
310 | 0 | sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &id); |
311 | 0 | Visual* visual; |
312 | 0 | int depth; |
313 | 0 | FindVisualAndDepth(display, id, &visual, &depth); |
314 | 0 | if (!visual || |
315 | 0 | visual->c_class != TrueColor || |
316 | 0 | visual->red_mask != redMask || |
317 | 0 | visual->green_mask != greenMask || |
318 | 0 | visual->blue_mask != blueMask ) { |
319 | 0 | continue; |
320 | 0 | } |
321 | 0 | |
322 | 0 | // Historically Xlib Visuals did not try to represent an alpha channel |
323 | 0 | // and there was no means to use an alpha channel on a Pixmap. The |
324 | 0 | // Xlib Visual from the fbconfig was not intended to have any |
325 | 0 | // information about alpha bits. |
326 | 0 | // |
327 | 0 | // Since then, RENDER has added formats for 32 bit depth Pixmaps. |
328 | 0 | // Some of these formats have bits for alpha and some have unused |
329 | 0 | // bits. |
330 | 0 | // |
331 | 0 | // Then the Composite extension added a 32 bit depth Visual intended |
332 | 0 | // for Windows with an alpha channel, so bits not in the visual color |
333 | 0 | // masks were expected to be treated as alpha bits. |
334 | 0 | // |
335 | 0 | // Usually GLX counts only color bits in the Visual depth, but the |
336 | 0 | // depth of Composite's ARGB Visual includes alpha bits. However, |
337 | 0 | // bits not in the color masks are not necessarily alpha bits because |
338 | 0 | // sometimes (NVIDIA) 32 bit Visuals are added for fbconfigs with 32 |
339 | 0 | // bit BUFFER_SIZE but zero alpha bits and 24 color bits (NVIDIA |
340 | 0 | // again). |
341 | 0 | // |
342 | 0 | // This checks that the depth matches in one of the two ways. |
343 | 0 | // NVIDIA now forces format->depth == depth so only the first way |
344 | 0 | // is checked for NVIDIA |
345 | 0 | if (depth != format->depth && |
346 | 0 | (mIsNVIDIA || depth != format->depth - alphaSize) ) { |
347 | 0 | continue; |
348 | 0 | } |
349 | 0 | |
350 | 0 | // If all bits of the Pixmap are color bits and the Pixmap depth |
351 | 0 | // matches the depth of the fbconfig visual, then we can assume that |
352 | 0 | // the driver will do whatever is necessary to ensure that any |
353 | 0 | // GLXPixmap alpha bits are treated as set. We can skip the |
354 | 0 | // ALPHA_SIZE check in this situation. We need to skip this check for |
355 | 0 | // situations (ATI) where there are no fbconfigs without alpha bits. |
356 | 0 | // |
357 | 0 | // glXChooseFBConfig should prefer configs with smaller |
358 | 0 | // LOCAL_GLX_BUFFER_SIZE, so we should still get zero alpha bits if |
359 | 0 | // available, except perhaps with NVIDIA drivers where buffer size is |
360 | 0 | // not the specified sum of the component sizes. |
361 | 0 | if (haveNonColorBits) { |
362 | 0 | // There are bits in the Pixmap format that haven't been matched |
363 | 0 | // against the fbconfig visual. These bits could either represent |
364 | 0 | // alpha or be unused, so just check that the number of alpha bits |
365 | 0 | // matches. |
366 | 0 | int size = 0; |
367 | 0 | sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], |
368 | 0 | LOCAL_GLX_ALPHA_SIZE, &size); |
369 | 0 | if (size != alphaSize) { |
370 | 0 | continue; |
371 | 0 | } |
372 | 0 | } |
373 | 0 | |
374 | 0 | matchIndex = i; |
375 | 0 | break; |
376 | 0 | } |
377 | 0 | if (matchIndex == -1) { |
378 | 0 | // GLX can't handle A8 surfaces, so this is not really unexpected. The |
379 | 0 | // caller should deal with this situation. |
380 | 0 | NS_WARNING_ASSERTION( |
381 | 0 | format->depth == 8, |
382 | 0 | "[GLX] Couldn't find a FBConfig matching Pixmap format"); |
383 | 0 | return X11None; |
384 | 0 | } |
385 | 0 |
|
386 | 0 | int pixmapAttribs[] = { LOCAL_GLX_TEXTURE_TARGET_EXT, LOCAL_GLX_TEXTURE_2D_EXT, |
387 | 0 | LOCAL_GLX_TEXTURE_FORMAT_EXT, |
388 | 0 | (alphaSize ? LOCAL_GLX_TEXTURE_FORMAT_RGBA_EXT |
389 | 0 | : LOCAL_GLX_TEXTURE_FORMAT_RGB_EXT), |
390 | 0 | X11None}; |
391 | 0 |
|
392 | 0 | GLXPixmap glxpixmap = fCreatePixmap(display, |
393 | 0 | cfgs[matchIndex], |
394 | 0 | xs->XDrawable(), |
395 | 0 | pixmapAttribs); |
396 | 0 |
|
397 | 0 | return glxpixmap; |
398 | 0 | } |
399 | | |
400 | | void |
401 | | GLXLibrary::DestroyPixmap(Display* aDisplay, GLXPixmap aPixmap) |
402 | 0 | { |
403 | 0 | if (!mUseTextureFromPixmap) { |
404 | 0 | return; |
405 | 0 | } |
406 | 0 | |
407 | 0 | fDestroyPixmap(aDisplay, aPixmap); |
408 | 0 | } |
409 | | |
410 | | void |
411 | | GLXLibrary::BindTexImage(Display* aDisplay, GLXPixmap aPixmap) |
412 | 0 | { |
413 | 0 | if (!mUseTextureFromPixmap) { |
414 | 0 | return; |
415 | 0 | } |
416 | 0 | |
417 | 0 | // Make sure all X drawing to the surface has finished before binding to a texture. |
418 | 0 | if (mClientIsMesa) { |
419 | 0 | // Using XSync instead of Mesa's glXWaitX, because its glxWaitX is a |
420 | 0 | // noop when direct rendering unless the current drawable is a |
421 | 0 | // single-buffer window. |
422 | 0 | FinishX(aDisplay); |
423 | 0 | } else { |
424 | 0 | fWaitX(); |
425 | 0 | } |
426 | 0 | fBindTexImage(aDisplay, aPixmap, LOCAL_GLX_FRONT_LEFT_EXT, nullptr); |
427 | 0 | } |
428 | | |
429 | | void |
430 | | GLXLibrary::ReleaseTexImage(Display* aDisplay, GLXPixmap aPixmap) |
431 | 0 | { |
432 | 0 | if (!mUseTextureFromPixmap) { |
433 | 0 | return; |
434 | 0 | } |
435 | 0 | |
436 | 0 | fReleaseTexImage(aDisplay, aPixmap, LOCAL_GLX_FRONT_LEFT_EXT); |
437 | 0 | } |
438 | | |
439 | | void |
440 | | GLXLibrary::UpdateTexImage(Display* aDisplay, GLXPixmap aPixmap) |
441 | 0 | { |
442 | 0 | // NVIDIA drivers don't require a rebind of the pixmap in order |
443 | 0 | // to display an updated image, and it's faster not to do it. |
444 | 0 | if (mIsNVIDIA) { |
445 | 0 | fWaitX(); |
446 | 0 | return; |
447 | 0 | } |
448 | 0 | |
449 | 0 | ReleaseTexImage(aDisplay, aPixmap); |
450 | 0 | BindTexImage(aDisplay, aPixmap); |
451 | 0 | } |
452 | | |
453 | | static int (*sOldErrorHandler)(Display*, XErrorEvent*); |
454 | | ScopedXErrorHandler::ErrorEvent sErrorEvent; |
455 | | static int GLXErrorHandler(Display* display, XErrorEvent* ev) |
456 | 0 | { |
457 | 0 | if (!sErrorEvent.mError.error_code) { |
458 | 0 | sErrorEvent.mError = *ev; |
459 | 0 | } |
460 | 0 | return 0; |
461 | 0 | } |
462 | | |
463 | | void |
464 | | GLXLibrary::BeforeGLXCall() const |
465 | 0 | { |
466 | 0 | if (mDebug) { |
467 | 0 | sOldErrorHandler = XSetErrorHandler(GLXErrorHandler); |
468 | 0 | } |
469 | 0 | } |
470 | | |
471 | | void |
472 | | GLXLibrary::AfterGLXCall() const |
473 | 0 | { |
474 | 0 | if (mDebug) { |
475 | 0 | FinishX(DefaultXDisplay()); |
476 | 0 | if (sErrorEvent.mError.error_code) { |
477 | 0 | char buffer[2048]; |
478 | 0 | XGetErrorText(DefaultXDisplay(), sErrorEvent.mError.error_code, buffer, sizeof(buffer)); |
479 | 0 | printf_stderr("X ERROR: %s (%i) - Request: %i.%i, Serial: %lu", |
480 | 0 | buffer, |
481 | 0 | sErrorEvent.mError.error_code, |
482 | 0 | sErrorEvent.mError.request_code, |
483 | 0 | sErrorEvent.mError.minor_code, |
484 | 0 | sErrorEvent.mError.serial); |
485 | 0 | MOZ_ASSERT_UNREACHABLE("AfterGLXCall sErrorEvent"); |
486 | 0 | } |
487 | 0 | XSetErrorHandler(sOldErrorHandler); |
488 | 0 | } |
489 | 0 | } |
490 | | |
491 | | already_AddRefed<GLContextGLX> |
492 | | GLContextGLX::CreateGLContext(CreateContextFlags flags, const SurfaceCaps& caps, |
493 | | bool isOffscreen, Display* display, GLXDrawable drawable, |
494 | | GLXFBConfig cfg, bool deleteDrawable, |
495 | | gfxXlibSurface* pixmap) |
496 | 0 | { |
497 | 0 | GLXLibrary& glx = sGLXLibrary; |
498 | 0 |
|
499 | 0 | int db = 0; |
500 | 0 | int err = glx.fGetFBConfigAttrib(display, cfg, |
501 | 0 | LOCAL_GLX_DOUBLEBUFFER, &db); |
502 | 0 | if (LOCAL_GLX_BAD_ATTRIBUTE != err) { |
503 | 0 | if (ShouldSpew()) { |
504 | 0 | printf("[GLX] FBConfig is %sdouble-buffered\n", db ? "" : "not "); |
505 | 0 | } |
506 | 0 | } |
507 | 0 |
|
508 | 0 | GLXContext context; |
509 | 0 | RefPtr<GLContextGLX> glContext; |
510 | 0 | bool error; |
511 | 0 |
|
512 | 0 | OffMainThreadScopedXErrorHandler xErrorHandler; |
513 | 0 |
|
514 | 0 | do { |
515 | 0 | error = false; |
516 | 0 |
|
517 | 0 | if (glx.HasCreateContextAttribs()) { |
518 | 0 | AutoTArray<int, 13> attrib_list; |
519 | 0 | if (glx.HasRobustness()) { |
520 | 0 | const int robust_attribs[] = { |
521 | 0 | LOCAL_GLX_CONTEXT_FLAGS_ARB, |
522 | 0 | LOCAL_GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB, |
523 | 0 | LOCAL_GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, |
524 | 0 | LOCAL_GLX_LOSE_CONTEXT_ON_RESET_ARB, |
525 | 0 | }; |
526 | 0 | attrib_list.AppendElements(robust_attribs, MOZ_ARRAY_LENGTH(robust_attribs)); |
527 | 0 | } |
528 | 0 | if (glx.HasVideoMemoryPurge()) { |
529 | 0 | const int memory_purge_attribs[] = { |
530 | 0 | LOCAL_GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV, |
531 | 0 | LOCAL_GL_TRUE, |
532 | 0 | }; |
533 | 0 | attrib_list.AppendElements(memory_purge_attribs, MOZ_ARRAY_LENGTH(memory_purge_attribs)); |
534 | 0 | } |
535 | 0 | if (!(flags & CreateContextFlags::REQUIRE_COMPAT_PROFILE)) { |
536 | 0 | int core_attribs[] = { |
537 | 0 | LOCAL_GLX_CONTEXT_MAJOR_VERSION_ARB, 3, |
538 | 0 | LOCAL_GLX_CONTEXT_MINOR_VERSION_ARB, 2, |
539 | 0 | LOCAL_GLX_CONTEXT_PROFILE_MASK_ARB, LOCAL_GLX_CONTEXT_CORE_PROFILE_BIT_ARB, |
540 | 0 | }; |
541 | 0 | attrib_list.AppendElements(core_attribs, MOZ_ARRAY_LENGTH(core_attribs)); |
542 | 0 | }; |
543 | 0 | attrib_list.AppendElement(0); |
544 | 0 |
|
545 | 0 | context = glx.fCreateContextAttribs( |
546 | 0 | display, |
547 | 0 | cfg, |
548 | 0 | nullptr, |
549 | 0 | True, |
550 | 0 | attrib_list.Elements()); |
551 | 0 | } else { |
552 | 0 | context = glx.fCreateNewContext( |
553 | 0 | display, |
554 | 0 | cfg, |
555 | 0 | LOCAL_GLX_RGBA_TYPE, |
556 | 0 | nullptr, |
557 | 0 | True); |
558 | 0 | } |
559 | 0 |
|
560 | 0 | if (context) { |
561 | 0 | glContext = new GLContextGLX(flags, caps, isOffscreen, display, drawable, |
562 | 0 | context, deleteDrawable, db, pixmap); |
563 | 0 | if (!glContext->Init()) |
564 | 0 | error = true; |
565 | 0 | } else { |
566 | 0 | error = true; |
567 | 0 | } |
568 | 0 |
|
569 | 0 | error |= xErrorHandler.SyncAndGetError(display); |
570 | 0 |
|
571 | 0 | if (error) { |
572 | 0 | NS_WARNING("Failed to create GLXContext!"); |
573 | 0 | glContext = nullptr; // note: this must be done while the graceful X error handler is set, |
574 | 0 | // because glxMakeCurrent can give a GLXBadDrawable error |
575 | 0 | } |
576 | 0 |
|
577 | 0 | return glContext.forget(); |
578 | 0 | } while (true); |
579 | 0 | } |
580 | | |
581 | | GLContextGLX::~GLContextGLX() |
582 | 0 | { |
583 | 0 | MarkDestroyed(); |
584 | 0 |
|
585 | 0 | // Wrapped context should not destroy glxContext/Surface |
586 | 0 | if (!mOwnsContext) { |
587 | 0 | return; |
588 | 0 | } |
589 | 0 | |
590 | 0 | // see bug 659842 comment 76 |
591 | | #ifdef DEBUG |
592 | | bool success = |
593 | | #endif |
594 | 0 | mGLX->fMakeCurrent(mDisplay, X11None, nullptr); |
595 | 0 | MOZ_ASSERT(success, |
596 | 0 | "glXMakeCurrent failed to release GL context before we call " |
597 | 0 | "glXDestroyContext!"); |
598 | 0 |
|
599 | 0 | mGLX->fDestroyContext(mDisplay, mContext); |
600 | 0 |
|
601 | 0 | if (mDeleteDrawable) { |
602 | 0 | mGLX->fDestroyPixmap(mDisplay, mDrawable); |
603 | 0 | } |
604 | 0 | } |
605 | | |
606 | | |
607 | | bool |
608 | | GLContextGLX::Init() |
609 | 0 | { |
610 | 0 | SetupLookupFunction(); |
611 | 0 | if (!InitWithPrefix("gl", true)) { |
612 | 0 | return false; |
613 | 0 | } |
614 | 0 | |
615 | 0 | // EXT_framebuffer_object is not supported on Core contexts |
616 | 0 | // so we'll also check for ARB_framebuffer_object |
617 | 0 | if (!IsExtensionSupported(EXT_framebuffer_object) && !IsSupported(GLFeature::framebuffer_object)) |
618 | 0 | return false; |
619 | 0 | |
620 | 0 | return true; |
621 | 0 | } |
622 | | |
623 | | bool |
624 | | GLContextGLX::MakeCurrentImpl() const |
625 | 0 | { |
626 | 0 | if (mGLX->IsMesa()) { |
627 | 0 | // Read into the event queue to ensure that Mesa receives a |
628 | 0 | // DRI2InvalidateBuffers event before drawing. See bug 1280653. |
629 | 0 | Unused << XPending(mDisplay); |
630 | 0 | } |
631 | 0 |
|
632 | 0 | const bool succeeded = mGLX->fMakeCurrent(mDisplay, mDrawable, mContext); |
633 | 0 | NS_ASSERTION(succeeded, "Failed to make GL context current!"); |
634 | 0 |
|
635 | 0 | if (!IsOffscreen() && mGLX->SupportsSwapControl()) { |
636 | 0 | // Many GLX implementations default to blocking until the next |
637 | 0 | // VBlank when calling glXSwapBuffers. We want to run unthrottled |
638 | 0 | // in ASAP mode. See bug 1280744. |
639 | 0 | const bool isASAP = (gfxPrefs::LayoutFrameRate() == 0); |
640 | 0 | mGLX->fSwapInterval(mDisplay, mDrawable, isASAP ? 0 : 1); |
641 | 0 | } |
642 | 0 | return succeeded; |
643 | 0 | } |
644 | | |
645 | | bool |
646 | | GLContextGLX::IsCurrentImpl() const |
647 | 0 | { |
648 | 0 | return mGLX->fGetCurrentContext() == mContext; |
649 | 0 | } |
650 | | |
651 | | bool |
652 | | GLContextGLX::SetupLookupFunction() |
653 | 0 | { |
654 | 0 | mLookupFunc = (PlatformLookupFunction)sGLXLibrary.GetGetProcAddress(); |
655 | 0 | return true; |
656 | 0 | } |
657 | | |
658 | | bool |
659 | | GLContextGLX::IsDoubleBuffered() const |
660 | 0 | { |
661 | 0 | return mDoubleBuffered; |
662 | 0 | } |
663 | | |
664 | | bool |
665 | | GLContextGLX::SwapBuffers() |
666 | 0 | { |
667 | 0 | if (!mDoubleBuffered) |
668 | 0 | return false; |
669 | 0 | mGLX->fSwapBuffers(mDisplay, mDrawable); |
670 | 0 | return true; |
671 | 0 | } |
672 | | |
673 | | void |
674 | | GLContextGLX::GetWSIInfo(nsCString* const out) const |
675 | 0 | { |
676 | 0 | Display* display = DefaultXDisplay(); |
677 | 0 | int screen = DefaultScreen(display); |
678 | 0 |
|
679 | 0 | int majorVersion, minorVersion; |
680 | 0 | sGLXLibrary.fQueryVersion(display, &majorVersion, &minorVersion); |
681 | 0 |
|
682 | 0 | out->Append(nsPrintfCString("GLX %u.%u", majorVersion, minorVersion)); |
683 | 0 |
|
684 | 0 | out->AppendLiteral("\nGLX_VENDOR(client): "); |
685 | 0 | out->Append(sGLXLibrary.fGetClientString(display, LOCAL_GLX_VENDOR)); |
686 | 0 |
|
687 | 0 | out->AppendLiteral("\nGLX_VENDOR(server): "); |
688 | 0 | out->Append(sGLXLibrary.fQueryServerString(display, screen, LOCAL_GLX_VENDOR)); |
689 | 0 |
|
690 | 0 | out->AppendLiteral("\nExtensions: "); |
691 | 0 | out->Append(sGLXLibrary.fQueryExtensionsString(display, screen)); |
692 | 0 | } |
693 | | |
694 | | bool |
695 | | GLContextGLX::OverrideDrawable(GLXDrawable drawable) |
696 | 0 | { |
697 | 0 | if (Screen()) |
698 | 0 | Screen()->AssureBlitted(); |
699 | 0 | Bool result = mGLX->fMakeCurrent(mDisplay, drawable, mContext); |
700 | 0 | return result; |
701 | 0 | } |
702 | | |
703 | | bool |
704 | | GLContextGLX::RestoreDrawable() |
705 | 0 | { |
706 | 0 | return mGLX->fMakeCurrent(mDisplay, mDrawable, mContext); |
707 | 0 | } |
708 | | |
709 | | GLContextGLX::GLContextGLX( |
710 | | CreateContextFlags flags, |
711 | | const SurfaceCaps& caps, |
712 | | bool isOffscreen, |
713 | | Display* aDisplay, |
714 | | GLXDrawable aDrawable, |
715 | | GLXContext aContext, |
716 | | bool aDeleteDrawable, |
717 | | bool aDoubleBuffered, |
718 | | gfxXlibSurface* aPixmap) |
719 | | : GLContext(flags, caps, nullptr, isOffscreen), |
720 | | mContext(aContext), |
721 | | mDisplay(aDisplay), |
722 | | mDrawable(aDrawable), |
723 | | mDeleteDrawable(aDeleteDrawable), |
724 | | mDoubleBuffered(aDoubleBuffered), |
725 | | mGLX(&sGLXLibrary), |
726 | | mPixmap(aPixmap) |
727 | 0 | { |
728 | 0 | } |
729 | | |
730 | | static bool |
731 | | AreCompatibleVisuals(Visual* one, Visual* two) |
732 | 0 | { |
733 | 0 | if (one->c_class != two->c_class) { |
734 | 0 | return false; |
735 | 0 | } |
736 | 0 | |
737 | 0 | if (one->red_mask != two->red_mask || |
738 | 0 | one->green_mask != two->green_mask || |
739 | 0 | one->blue_mask != two->blue_mask) { |
740 | 0 | return false; |
741 | 0 | } |
742 | 0 | |
743 | 0 | if (one->bits_per_rgb != two->bits_per_rgb) { |
744 | 0 | return false; |
745 | 0 | } |
746 | 0 | |
747 | 0 | return true; |
748 | 0 | } |
749 | | |
750 | | already_AddRefed<GLContext> |
751 | | GLContextProviderGLX::CreateWrappingExisting(void* aContext, void* aSurface) |
752 | 0 | { |
753 | 0 | if (!sGLXLibrary.EnsureInitialized()) { |
754 | 0 | return nullptr; |
755 | 0 | } |
756 | 0 | |
757 | 0 | if (aContext && aSurface) { |
758 | 0 | SurfaceCaps caps = SurfaceCaps::Any(); |
759 | 0 | RefPtr<GLContextGLX> glContext = |
760 | 0 | new GLContextGLX(CreateContextFlags::NONE, caps, |
761 | 0 | false, // Offscreen |
762 | 0 | (Display*)DefaultXDisplay(), // Display |
763 | 0 | (GLXDrawable)aSurface, (GLXContext)aContext, |
764 | 0 | false, // aDeleteDrawable, |
765 | 0 | true, |
766 | 0 | (gfxXlibSurface*)nullptr); |
767 | 0 |
|
768 | 0 | glContext->mOwnsContext = false; |
769 | 0 | return glContext.forget(); |
770 | 0 | } |
771 | 0 | |
772 | 0 | return nullptr; |
773 | 0 | } |
774 | | |
775 | | already_AddRefed<GLContext> |
776 | | CreateForWidget(Display* aXDisplay, Window aXWindow, |
777 | | bool aWebRender, |
778 | | bool aForceAccelerated) |
779 | 0 | { |
780 | 0 | if (!sGLXLibrary.EnsureInitialized()) { |
781 | 0 | return nullptr; |
782 | 0 | } |
783 | 0 | |
784 | 0 | // Currently, we take whatever Visual the window already has, and |
785 | 0 | // try to create an fbconfig for that visual. This isn't |
786 | 0 | // necessarily what we want in the long run; an fbconfig may not |
787 | 0 | // be available for the existing visual, or if it is, the GL |
788 | 0 | // performance might be suboptimal. But using the existing visual |
789 | 0 | // is a relatively safe intermediate step. |
790 | 0 | |
791 | 0 | if (!aXDisplay) { |
792 | 0 | NS_ERROR("X Display required for GLX Context provider"); |
793 | 0 | return nullptr; |
794 | 0 | } |
795 | 0 |
|
796 | 0 | int xscreen = DefaultScreen(aXDisplay); |
797 | 0 |
|
798 | 0 | ScopedXFree<GLXFBConfig> cfgs; |
799 | 0 | GLXFBConfig config; |
800 | 0 | int visid; |
801 | 0 | if (!GLContextGLX::FindFBConfigForWindow(aXDisplay, xscreen, aXWindow, &cfgs, |
802 | 0 | &config, &visid, aWebRender)) |
803 | 0 | { |
804 | 0 | return nullptr; |
805 | 0 | } |
806 | 0 | |
807 | 0 | CreateContextFlags flags; |
808 | 0 | if (aWebRender) { |
809 | 0 | flags = CreateContextFlags::NONE; // WR needs GL3.2+ |
810 | 0 | } else { |
811 | 0 | flags = CreateContextFlags::REQUIRE_COMPAT_PROFILE; |
812 | 0 | } |
813 | 0 | return GLContextGLX::CreateGLContext(flags, SurfaceCaps::Any(), false, aXDisplay, |
814 | 0 | aXWindow, config, false, nullptr); |
815 | 0 | } |
816 | | |
817 | | already_AddRefed<GLContext> |
818 | | GLContextProviderGLX::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated) |
819 | 0 | { |
820 | 0 | GtkCompositorWidget* compWidget = aCompositorWidget->AsX11(); |
821 | 0 | MOZ_ASSERT(compWidget); |
822 | 0 |
|
823 | 0 | return CreateForWidget(compWidget->XDisplay(), |
824 | 0 | compWidget->XWindow(), |
825 | 0 | compWidget->GetCompositorOptions().UseWebRender(), |
826 | 0 | aForceAccelerated); |
827 | 0 | } |
828 | | |
829 | | already_AddRefed<GLContext> |
830 | | GLContextProviderGLX::CreateForWindow(nsIWidget* aWidget, |
831 | | bool aWebRender, |
832 | | bool aForceAccelerated) |
833 | 0 | { |
834 | 0 | Display* display = (Display*)aWidget->GetNativeData(NS_NATIVE_COMPOSITOR_DISPLAY); |
835 | 0 | Window window = GET_NATIVE_WINDOW(aWidget); |
836 | 0 |
|
837 | 0 | return CreateForWidget(display, |
838 | 0 | window, |
839 | 0 | aWebRender, |
840 | 0 | aForceAccelerated); |
841 | 0 | } |
842 | | |
843 | | static bool |
844 | | ChooseConfig(GLXLibrary* glx, Display* display, int screen, const SurfaceCaps& minCaps, |
845 | | ScopedXFree<GLXFBConfig>* const out_scopedConfigArr, |
846 | | GLXFBConfig* const out_config, int* const out_visid) |
847 | 0 | { |
848 | 0 | ScopedXFree<GLXFBConfig>& scopedConfigArr = *out_scopedConfigArr; |
849 | 0 |
|
850 | 0 | if (minCaps.antialias) |
851 | 0 | return false; |
852 | 0 | |
853 | 0 | int attribs[] = { |
854 | 0 | LOCAL_GLX_DRAWABLE_TYPE, LOCAL_GLX_PIXMAP_BIT, |
855 | 0 | LOCAL_GLX_X_RENDERABLE, True, |
856 | 0 | LOCAL_GLX_RED_SIZE, 8, |
857 | 0 | LOCAL_GLX_GREEN_SIZE, 8, |
858 | 0 | LOCAL_GLX_BLUE_SIZE, 8, |
859 | 0 | LOCAL_GLX_ALPHA_SIZE, minCaps.alpha ? 8 : 0, |
860 | 0 | LOCAL_GLX_DEPTH_SIZE, minCaps.depth ? 16 : 0, |
861 | 0 | LOCAL_GLX_STENCIL_SIZE, minCaps.stencil ? 8 : 0, |
862 | 0 | 0 |
863 | 0 | }; |
864 | 0 |
|
865 | 0 | int numConfigs = 0; |
866 | 0 | scopedConfigArr = glx->fChooseFBConfig(display, screen, attribs, &numConfigs); |
867 | 0 | if (!scopedConfigArr || !numConfigs) |
868 | 0 | return false; |
869 | 0 | |
870 | 0 | // Issues with glxChooseFBConfig selection and sorting: |
871 | 0 | // * ALPHA_SIZE is sorted as 'largest total RGBA bits first'. If we don't request |
872 | 0 | // alpha bits, we'll probably get RGBA anyways, since 32 is more than 24. |
873 | 0 | // * DEPTH_SIZE is sorted largest first, including for `0` inputs. |
874 | 0 | // * STENCIL_SIZE is smallest first, but it might return `8` even though we ask for |
875 | 0 | // `0`. |
876 | 0 | |
877 | 0 | // For now, we don't care about these. We *will* care when we do XPixmap sharing. |
878 | 0 | |
879 | 0 | for (int i = 0; i < numConfigs; ++i) { |
880 | 0 | GLXFBConfig curConfig = scopedConfigArr[i]; |
881 | 0 |
|
882 | 0 | int visid; |
883 | 0 | if (glx->fGetFBConfigAttrib(display, curConfig, LOCAL_GLX_VISUAL_ID, &visid) |
884 | 0 | != Success) |
885 | 0 | { |
886 | 0 | continue; |
887 | 0 | } |
888 | 0 | |
889 | 0 | if (!visid) |
890 | 0 | continue; |
891 | 0 | |
892 | 0 | *out_config = curConfig; |
893 | 0 | *out_visid = visid; |
894 | 0 | return true; |
895 | 0 | } |
896 | 0 |
|
897 | 0 | return false; |
898 | 0 | } |
899 | | |
900 | | bool |
901 | | GLContextGLX::FindVisual(Display* display, int screen, bool useWebRender, |
902 | | bool useAlpha, int* const out_visualId) |
903 | 0 | { |
904 | 0 | if (!sGLXLibrary.EnsureInitialized()) { |
905 | 0 | return false; |
906 | 0 | } |
907 | 0 | |
908 | 0 | XVisualInfo visualTemplate; |
909 | 0 | visualTemplate.screen = screen; |
910 | 0 |
|
911 | 0 | // Get all visuals of screen |
912 | 0 |
|
913 | 0 | int visualsLen = 0; |
914 | 0 | XVisualInfo* xVisuals = XGetVisualInfo(display, VisualScreenMask, &visualTemplate, &visualsLen); |
915 | 0 | if (!xVisuals) { |
916 | 0 | return false; |
917 | 0 | } |
918 | 0 | const Range<XVisualInfo> visualInfos(xVisuals, visualsLen); |
919 | 0 | auto cleanupVisuals = MakeScopeExit([&] { |
920 | 0 | XFree(xVisuals); |
921 | 0 | }); |
922 | 0 |
|
923 | 0 | // Get default visual info |
924 | 0 |
|
925 | 0 | Visual* defaultVisual = DefaultVisual(display, screen); |
926 | 0 | const auto defaultVisualInfo = [&]() -> const XVisualInfo* { |
927 | 0 | for (const auto& cur : visualInfos) { |
928 | 0 | if (cur.visual == defaultVisual) { |
929 | 0 | return &cur; |
930 | 0 | } |
931 | 0 | } |
932 | 0 | return nullptr; |
933 | 0 | }(); |
934 | 0 | if (!defaultVisualInfo) { |
935 | 0 | MOZ_ASSERT(false); |
936 | 0 | return false; |
937 | 0 | } |
938 | 0 |
|
939 | 0 | const int bpp = useAlpha ? 32 : 24; |
940 | 0 | const int alphaSize = useAlpha ? 8 : 0; |
941 | 0 | const int depthSize = useWebRender ? 24 : 0; |
942 | 0 |
|
943 | 0 | for (auto& cur : visualInfos) { |
944 | 0 |
|
945 | 0 | const auto fnConfigMatches = [&](const int pname, const int expected) { |
946 | 0 | int actual; |
947 | 0 | if (sGLXLibrary.fGetConfig(display, &cur, pname, &actual)) { |
948 | 0 | return false; |
949 | 0 | } |
950 | 0 | return actual == expected; |
951 | 0 | }; |
952 | 0 |
|
953 | 0 | // Check if visual is compatible. |
954 | 0 | if (cur.depth != bpp || |
955 | 0 | cur.c_class != defaultVisualInfo->c_class) { |
956 | 0 | continue; |
957 | 0 | } |
958 | 0 | |
959 | 0 | // Check if visual is compatible to GL requests. |
960 | 0 | if (fnConfigMatches(LOCAL_GLX_USE_GL, 1) && |
961 | 0 | fnConfigMatches(LOCAL_GLX_DOUBLEBUFFER, 1) && |
962 | 0 | fnConfigMatches(LOCAL_GLX_RED_SIZE, 8) && |
963 | 0 | fnConfigMatches(LOCAL_GLX_GREEN_SIZE, 8) && |
964 | 0 | fnConfigMatches(LOCAL_GLX_BLUE_SIZE, 8) && |
965 | 0 | fnConfigMatches(LOCAL_GLX_ALPHA_SIZE, alphaSize) && |
966 | 0 | fnConfigMatches(LOCAL_GLX_DEPTH_SIZE, depthSize)) |
967 | 0 | { |
968 | 0 | *out_visualId = cur.visualid; |
969 | 0 | return true; |
970 | 0 | } |
971 | 0 | } |
972 | 0 |
|
973 | 0 | return false; |
974 | 0 | } |
975 | | |
976 | | bool |
977 | | GLContextGLX::FindFBConfigForWindow(Display* display, int screen, Window window, |
978 | | ScopedXFree<GLXFBConfig>* const out_scopedConfigArr, |
979 | | GLXFBConfig* const out_config, int* const out_visid, |
980 | | bool aWebRender) |
981 | 0 | { |
982 | 0 | // XXX the visual ID is almost certainly the LOCAL_GLX_FBCONFIG_ID, so |
983 | 0 | // we could probably do this first and replace the glXGetFBConfigs |
984 | 0 | // with glXChooseConfigs. Docs are sparklingly clear as always. |
985 | 0 | XWindowAttributes windowAttrs; |
986 | 0 | if (!XGetWindowAttributes(display, window, &windowAttrs)) { |
987 | 0 | NS_WARNING("[GLX] XGetWindowAttributes() failed"); |
988 | 0 | return false; |
989 | 0 | } |
990 | 0 |
|
991 | 0 | ScopedXFree<GLXFBConfig>& cfgs = *out_scopedConfigArr; |
992 | 0 | int numConfigs; |
993 | 0 | const int webrenderAttribs[] = { |
994 | 0 | LOCAL_GLX_ALPHA_SIZE, windowAttrs.depth == 32 ? 8 : 0, |
995 | 0 | LOCAL_GLX_DEPTH_SIZE, 24, |
996 | 0 | LOCAL_GLX_DOUBLEBUFFER, True, |
997 | 0 | 0 |
998 | 0 | }; |
999 | 0 |
|
1000 | 0 | if (aWebRender) { |
1001 | 0 | cfgs = sGLXLibrary.fChooseFBConfig(display, |
1002 | 0 | screen, |
1003 | 0 | webrenderAttribs, |
1004 | 0 | &numConfigs); |
1005 | 0 | } else { |
1006 | 0 | cfgs = sGLXLibrary.fGetFBConfigs(display, |
1007 | 0 | screen, |
1008 | 0 | &numConfigs); |
1009 | 0 | } |
1010 | 0 |
|
1011 | 0 | if (!cfgs) { |
1012 | 0 | NS_WARNING("[GLX] glXGetFBConfigs() failed"); |
1013 | 0 | return false; |
1014 | 0 | } |
1015 | 0 | NS_ASSERTION(numConfigs > 0, "No FBConfigs found!"); |
1016 | 0 |
|
1017 | 0 | const VisualID windowVisualID = XVisualIDFromVisual(windowAttrs.visual); |
1018 | | #ifdef DEBUG |
1019 | | printf("[GLX] window %lx has VisualID 0x%lx\n", window, windowVisualID); |
1020 | | #endif |
1021 | |
|
1022 | 0 | for (int i = 0; i < numConfigs; i++) { |
1023 | 0 | int visid = X11None; |
1024 | 0 | sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid); |
1025 | 0 | if (visid) { |
1026 | 0 | // WebRender compatible GLX visual is configured |
1027 | 0 | // at nsWindow::Create() by GLContextGLX::FindVisual(), |
1028 | 0 | // just reuse it here. |
1029 | 0 | if (windowVisualID == static_cast<VisualID>(visid)) { |
1030 | 0 | *out_config = cfgs[i]; |
1031 | 0 | *out_visid = visid; |
1032 | 0 | return true; |
1033 | 0 | } |
1034 | 0 | } |
1035 | 0 | } |
1036 | 0 |
|
1037 | 0 | // We don't have a frame buffer visual which matches the GLX visual |
1038 | 0 | // from GLContextGLX::FindVisual(). Let's try to find a near one and hope |
1039 | 0 | // we're not on NVIDIA (Bug 1478454) as it causes X11 BadMatch error there. |
1040 | 0 | for (int i = 0; i < numConfigs; i++) { |
1041 | 0 | int visid = X11None; |
1042 | 0 | sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid); |
1043 | 0 | if (visid) { |
1044 | 0 | int depth; |
1045 | 0 | Visual* visual; |
1046 | 0 | FindVisualAndDepth(display, visid, &visual, &depth); |
1047 | 0 | if (depth == windowAttrs.depth && |
1048 | 0 | AreCompatibleVisuals(windowAttrs.visual, visual)) { |
1049 | 0 | *out_config = cfgs[i]; |
1050 | 0 | *out_visid = visid; |
1051 | 0 | return true; |
1052 | 0 | } |
1053 | 0 | } |
1054 | 0 | } |
1055 | 0 |
|
1056 | 0 |
|
1057 | 0 | NS_WARNING("[GLX] Couldn't find a FBConfig matching window visual"); |
1058 | 0 | return false; |
1059 | 0 | } |
1060 | | |
1061 | | static already_AddRefed<GLContextGLX> |
1062 | | CreateOffscreenPixmapContext(CreateContextFlags flags, const IntSize& size, |
1063 | | const SurfaceCaps& minCaps, nsACString* const out_failureId) |
1064 | 0 | { |
1065 | 0 | GLXLibrary* glx = &sGLXLibrary; |
1066 | 0 | if (!glx->EnsureInitialized()) |
1067 | 0 | return nullptr; |
1068 | 0 | |
1069 | 0 | Display* display = DefaultXDisplay(); |
1070 | 0 | int screen = DefaultScreen(display); |
1071 | 0 |
|
1072 | 0 | ScopedXFree<GLXFBConfig> scopedConfigArr; |
1073 | 0 | GLXFBConfig config; |
1074 | 0 | int visid; |
1075 | 0 | if (!ChooseConfig(glx, display, screen, minCaps, &scopedConfigArr, &config, &visid)) { |
1076 | 0 | NS_WARNING("Failed to find a compatible config."); |
1077 | 0 | return nullptr; |
1078 | 0 | } |
1079 | 0 |
|
1080 | 0 | Visual* visual; |
1081 | 0 | int depth; |
1082 | 0 | FindVisualAndDepth(display, visid, &visual, &depth); |
1083 | 0 |
|
1084 | 0 | OffMainThreadScopedXErrorHandler xErrorHandler; |
1085 | 0 | bool error = false; |
1086 | 0 |
|
1087 | 0 | gfx::IntSize dummySize(16, 16); |
1088 | 0 | RefPtr<gfxXlibSurface> surface = gfxXlibSurface::Create(DefaultScreenOfDisplay(display), |
1089 | 0 | visual, |
1090 | 0 | dummySize); |
1091 | 0 | if (surface->CairoStatus() != 0) { |
1092 | 0 | mozilla::Unused << xErrorHandler.SyncAndGetError(display); |
1093 | 0 | return nullptr; |
1094 | 0 | } |
1095 | 0 | |
1096 | 0 | // Handle slightly different signature between glXCreatePixmap and |
1097 | 0 | // its pre-GLX-1.3 extension equivalent (though given the ABI, we |
1098 | 0 | // might not need to). |
1099 | 0 | const auto drawable = surface->XDrawable(); |
1100 | 0 | const auto pixmap = glx->fCreatePixmap(display, config, drawable, nullptr); |
1101 | 0 | if (pixmap == 0) { |
1102 | 0 | error = true; |
1103 | 0 | } |
1104 | 0 |
|
1105 | 0 | bool serverError = xErrorHandler.SyncAndGetError(display); |
1106 | 0 | if (error || serverError) |
1107 | 0 | return nullptr; |
1108 | 0 | |
1109 | 0 | return GLContextGLX::CreateGLContext(flags, minCaps, true, display, pixmap, config, |
1110 | 0 | true, surface); |
1111 | 0 | } |
1112 | | |
1113 | | /*static*/ already_AddRefed<GLContext> |
1114 | | GLContextProviderGLX::CreateHeadless(CreateContextFlags flags, |
1115 | | nsACString* const out_failureId) |
1116 | 0 | { |
1117 | 0 | IntSize dummySize = IntSize(16, 16); |
1118 | 0 | SurfaceCaps dummyCaps = SurfaceCaps::Any(); |
1119 | 0 | return CreateOffscreenPixmapContext(flags, dummySize, dummyCaps, out_failureId); |
1120 | 0 | } |
1121 | | |
1122 | | /*static*/ already_AddRefed<GLContext> |
1123 | | GLContextProviderGLX::CreateOffscreen(const IntSize& size, |
1124 | | const SurfaceCaps& minCaps, |
1125 | | CreateContextFlags flags, |
1126 | | nsACString* const out_failureId) |
1127 | 0 | { |
1128 | 0 | SurfaceCaps minBackbufferCaps = minCaps; |
1129 | 0 | if (minCaps.antialias) { |
1130 | 0 | minBackbufferCaps.antialias = false; |
1131 | 0 | minBackbufferCaps.depth = false; |
1132 | 0 | minBackbufferCaps.stencil = false; |
1133 | 0 | } |
1134 | 0 |
|
1135 | 0 | RefPtr<GLContext> gl; |
1136 | 0 | gl = CreateOffscreenPixmapContext(flags, size, minBackbufferCaps, out_failureId); |
1137 | 0 | if (!gl) |
1138 | 0 | return nullptr; |
1139 | 0 | |
1140 | 0 | if (!gl->InitOffscreen(size, minCaps)) { |
1141 | 0 | *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_GLX_INIT"); |
1142 | 0 | return nullptr; |
1143 | 0 | } |
1144 | 0 |
|
1145 | 0 | return gl.forget(); |
1146 | 0 | } |
1147 | | |
1148 | | /*static*/ GLContext* |
1149 | | GLContextProviderGLX::GetGlobalContext() |
1150 | 0 | { |
1151 | 0 | // Context sharing not supported. |
1152 | 0 | return nullptr; |
1153 | 0 | } |
1154 | | |
1155 | | /*static*/ void |
1156 | | GLContextProviderGLX::Shutdown() |
1157 | 0 | { |
1158 | 0 | } |
1159 | | |
1160 | | } /* namespace gl */ |
1161 | | } /* namespace mozilla */ |