1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Initial Developer of the Original Code is Mozilla Foundation.
16 * Portions created by the Initial Developer are Copyright (C) 2010
17 * the Initial Developer. All Rights Reserved.
20 * Matt Woodrow <mwoodrow@mozilla.com>
21 * Bas Schouten <bschouten@mozilla.com>
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #ifdef MOZ_WIDGET_GTK2
40 #define GET_NATIVE_WINDOW(aWidget) GDK_WINDOW_XID((GdkWindow *) aWidget->GetNativeData(NS_NATIVE_WINDOW))
41 #elif defined(MOZ_WIDGET_QT)
44 #define GET_NATIVE_WINDOW(aWidget) static_cast<QWidget*>(aWidget->GetNativeData(NS_NATIVE_SHELLWIDGET))->handle()
48 #include <X11/Xutil.h>
50 #include "mozilla/X11Util.h"
53 #include "GLContextProvider.h"
55 #include "nsIWidget.h"
56 #include "GLXLibrary.h"
57 #include "gfxXlibSurface.h"
58 #include "gfxContext.h"
59 #include "gfxImageSurface.h"
60 #include "gfxPlatform.h"
61 #include "GLContext.h"
66 static PRBool gIsATI
= PR_FALSE
;
67 static PRBool gIsChromium
= PR_FALSE
;
68 static int gGLXMajorVersion
= 0, gGLXMinorVersion
= 0;
70 // Check that we have at least version aMajor.aMinor .
72 GLXVersionCheck(int aMajor
, int aMinor
)
74 return aMajor
< gGLXMajorVersion
||
75 (aMajor
== gGLXMajorVersion
&& aMinor
<= gGLXMinorVersion
);
79 HasExtension(const char* aExtensions
, const char* aRequiredExtension
)
81 return GLContext::ListHasExtension(
82 reinterpret_cast<const GLubyte
*>(aExtensions
), aRequiredExtension
);
86 GLXLibrary::EnsureInitialized()
92 // Don't repeatedly try to initialize.
93 if (mTriedInitializing
) {
96 mTriedInitializing
= PR_TRUE
;
99 mOGLLibrary
= PR_LoadLibrary("libGL.so.1");
101 NS_WARNING("Couldn't load OpenGL shared library.");
106 LibrarySymbolLoader::SymLoadStruct symbols
[] = {
107 /* functions that were in GLX 1.0 */
108 { (PRFuncPtr
*) &xDestroyContext
, { "glXDestroyContext", NULL
} },
109 { (PRFuncPtr
*) &xMakeCurrent
, { "glXMakeCurrent", NULL
} },
110 { (PRFuncPtr
*) &xSwapBuffers
, { "glXSwapBuffers", NULL
} },
111 { (PRFuncPtr
*) &xQueryVersion
, { "glXQueryVersion", NULL
} },
112 { (PRFuncPtr
*) &xGetCurrentContext
, { "glXGetCurrentContext", NULL
} },
113 /* functions introduced in GLX 1.1 */
114 { (PRFuncPtr
*) &xQueryExtensionsString
, { "glXQueryExtensionsString", NULL
} },
115 { (PRFuncPtr
*) &xQueryServerString
, { "glXQueryServerString", NULL
} },
119 LibrarySymbolLoader::SymLoadStruct symbols13
[] = {
120 /* functions introduced in GLX 1.3 */
121 { (PRFuncPtr
*) &xChooseFBConfig
, { "glXChooseFBConfig", NULL
} },
122 { (PRFuncPtr
*) &xGetFBConfigAttrib
, { "glXGetFBConfigAttrib", NULL
} },
123 // WARNING: xGetFBConfigs not set in symbols13_ext
124 { (PRFuncPtr
*) &xGetFBConfigs
, { "glXGetFBConfigs", NULL
} },
125 { (PRFuncPtr
*) &xGetVisualFromFBConfig
, { "glXGetVisualFromFBConfig", NULL
} },
126 // WARNING: symbols13_ext sets xCreateGLXPixmapWithConfig instead
127 { (PRFuncPtr
*) &xCreatePixmap
, { "glXCreatePixmap", NULL
} },
128 { (PRFuncPtr
*) &xDestroyPixmap
, { "glXDestroyPixmap", NULL
} },
129 { (PRFuncPtr
*) &xCreateNewContext
, { "glXCreateNewContext", NULL
} },
133 LibrarySymbolLoader::SymLoadStruct symbols13_ext
[] = {
134 /* extension equivalents for functions introduced in GLX 1.3 */
135 // GLX_SGIX_fbconfig extension
136 { (PRFuncPtr
*) &xChooseFBConfig
, { "glXChooseFBConfigSGIX", NULL
} },
137 { (PRFuncPtr
*) &xGetFBConfigAttrib
, { "glXGetFBConfigAttribSGIX", NULL
} },
138 // WARNING: no xGetFBConfigs equivalent in extensions
139 { (PRFuncPtr
*) &xGetVisualFromFBConfig
, { "glXGetVisualFromFBConfig", NULL
} },
140 // WARNING: different from symbols13:
141 { (PRFuncPtr
*) &xCreateGLXPixmapWithConfig
, { "glXCreateGLXPixmapWithConfigSGIX", NULL
} },
142 { (PRFuncPtr
*) &xDestroyPixmap
, { "glXDestroyGLXPixmap", NULL
} }, // not from ext
143 { (PRFuncPtr
*) &xCreateNewContext
, { "glXCreateContextWithConfigSGIX", NULL
} },
147 LibrarySymbolLoader::SymLoadStruct symbols14
[] = {
148 /* functions introduced in GLX 1.4 */
149 { (PRFuncPtr
*) &xGetProcAddress
, { "glXGetProcAddress", NULL
} },
153 LibrarySymbolLoader::SymLoadStruct symbols14_ext
[] = {
154 /* extension equivalents for functions introduced in GLX 1.4 */
155 // GLX_ARB_get_proc_address extension
156 { (PRFuncPtr
*) &xGetProcAddress
, { "glXGetProcAddressARB", NULL
} },
160 if (!LibrarySymbolLoader::LoadSymbols(mOGLLibrary
, &symbols
[0])) {
161 NS_WARNING("Couldn't find required entry point in OpenGL shared library");
165 Display
*display
= DefaultXDisplay();
166 int screen
= DefaultScreen(display
);
167 if (!xQueryVersion(display
, &gGLXMajorVersion
, &gGLXMinorVersion
)) {
168 gGLXMajorVersion
= 0;
169 gGLXMinorVersion
= 0;
173 const char *vendor
= xQueryServerString(display
, screen
, GLX_VENDOR
);
174 const char *serverVersionStr
= xQueryServerString(display
, screen
, GLX_VERSION
);
176 if (!GLXVersionCheck(1, 1))
177 // Not possible to query for extensions.
180 const char *extensionsStr
= xQueryExtensionsString(display
, screen
);
182 LibrarySymbolLoader::SymLoadStruct
*sym13
;
183 if (!GLXVersionCheck(1, 3)) {
184 // Even if we don't have 1.3, we might have equivalent extensions
185 // (as on the Intel X server).
186 if (!HasExtension(extensionsStr
, "GLX_SGIX_fbconfig")) {
189 sym13
= symbols13_ext
;
193 if (!LibrarySymbolLoader::LoadSymbols(mOGLLibrary
, sym13
)) {
194 NS_WARNING("Couldn't find required entry point in OpenGL shared library");
198 LibrarySymbolLoader::SymLoadStruct
*sym14
;
199 if (!GLXVersionCheck(1, 4)) {
200 // Even if we don't have 1.4, we might have equivalent extensions
201 // (as on the Intel X server).
202 if (!HasExtension(extensionsStr
, "GLX_ARB_get_proc_address")) {
205 sym14
= symbols14_ext
;
209 if (!LibrarySymbolLoader::LoadSymbols(mOGLLibrary
, sym14
)) {
210 NS_WARNING("Couldn't find required entry point in OpenGL shared library");
214 gIsATI
= vendor
&& DoesVendorStringMatch(vendor
, "ATI");
215 gIsChromium
= (vendor
&& DoesVendorStringMatch(vendor
, "Chromium")) ||
216 (serverVersionStr
&& DoesVendorStringMatch(serverVersionStr
, "Chromium"));
218 mInitialized
= PR_TRUE
;
222 GLXLibrary sGLXLibrary
;
224 class GLContextGLX
: public GLContext
227 static already_AddRefed
<GLContextGLX
>
228 CreateGLContext(const ContextFormat
& format
,
230 GLXDrawable drawable
,
233 GLContextGLX
*shareContext
,
234 PRBool deleteDrawable
,
235 gfxXlibSurface
*pixmap
= nsnull
)
237 const char *glxVendorString
= sGLXLibrary
.xQueryServerString(display
, DefaultScreen(display
), GLX_VENDOR
);
238 if (strcmp(glxVendorString
, "NVIDIA Corporation") &&
239 !PR_GetEnv("MOZ_GLX_IGNORE_BLACKLIST"))
241 printf("[GLX] currently only allowing the NVIDIA proprietary driver, as other drivers are giving too many crashes. "
242 "To bypass this, define the MOZ_GLX_IGNORE_BLACKLIST environment variable.\n");
247 err
= sGLXLibrary
.xGetFBConfigAttrib(display
, cfg
,
248 GLX_DOUBLEBUFFER
, &db
);
249 if (GLX_BAD_ATTRIBUTE
!= err
) {
251 printf("[GLX] FBConfig is %sdouble-buffered\n", db
? "" : "not ");
256 nsRefPtr
<GLContextGLX
> glContext
;
259 ScopedXErrorHandler xErrorHandler
;
261 TRY_AGAIN_NO_SHARING
:
263 context
= sGLXLibrary
.xCreateNewContext(display
,
266 shareContext
? shareContext
->mContext
: NULL
,
270 glContext
= new GLContextGLX(format
,
278 if (!glContext
->Init())
285 if (error
|| xErrorHandler
.SyncAndGetError(display
)) {
286 shareContext
= nsnull
;
287 goto TRY_AGAIN_NO_SHARING
;
291 // at this point, if shareContext != null, we know there's no error.
292 // it's important to minimize the number of XSyncs for startup performance.
294 if (error
|| // earlier recorded error
295 xErrorHandler
.SyncAndGetError(display
))
297 NS_WARNING("Failed to create GLXContext!");
298 glContext
= nsnull
; // note: this must be done while the graceful X error handler is set,
299 // because glxMakeCurrent can give a GLXBadDrawable error
303 return glContext
.forget();
310 sGLXLibrary
.xDestroyContext(mDisplay
, mContext
);
312 if (mDeleteDrawable
) {
313 sGLXLibrary
.xDestroyPixmap(mDisplay
, mDrawable
);
317 GLContextType
GetContextType() {
318 return ContextTypeGLX
;
324 SetupLookupFunction();
325 if (!InitWithPrefix("gl", PR_TRUE
)) {
329 return IsExtensionSupported("GL_EXT_framebuffer_object");
332 PRBool
MakeCurrentImpl(PRBool aForce
= PR_FALSE
)
334 PRBool succeeded
= PR_TRUE
;
336 // With the ATI FGLRX driver, glxMakeCurrent is very slow even when the context doesn't change.
337 // (This is not the case with other drivers such as NVIDIA).
338 // So avoid calling it more than necessary. Since GLX documentation says that:
339 // "glXGetCurrentContext returns client-side information.
340 // It does not make a round trip to the server."
341 // I assume that it's not worth using our own TLS slot here.
342 if (aForce
|| sGLXLibrary
.xGetCurrentContext() != mContext
) {
343 succeeded
= sGLXLibrary
.xMakeCurrent(mDisplay
, mDrawable
, mContext
);
344 NS_ASSERTION(succeeded
, "Failed to make GL context current!");
350 PRBool
SetupLookupFunction()
352 mLookupFunc
= (PlatformLookupFunction
)sGLXLibrary
.xGetProcAddress
;
356 void *GetNativeData(NativeDataType aType
)
359 case NativeGLContext
:
362 case NativeThebesSurface
:
370 PRBool
IsDoubleBuffered()
372 return mDoubleBuffered
;
377 if (!mDoubleBuffered
)
379 sGLXLibrary
.xSwapBuffers(mDisplay
, mDrawable
);
384 friend class GLContextProviderGLX
;
386 GLContextGLX(const ContextFormat
& aFormat
,
387 GLContext
*aShareContext
,
389 GLXDrawable aDrawable
,
391 PRBool aDeleteDrawable
,
392 PRBool aDoubleBuffered
,
393 gfxXlibSurface
*aPixmap
)
394 : GLContext(aFormat
, aDeleteDrawable
? PR_TRUE
: PR_FALSE
, aShareContext
),
397 mDrawable(aDrawable
),
398 mDeleteDrawable(aDeleteDrawable
),
399 mDoubleBuffered(aDoubleBuffered
),
405 GLXDrawable mDrawable
;
406 PRPackedBool mDeleteDrawable
;
407 PRPackedBool mDoubleBuffered
;
409 nsRefPtr
<gfxXlibSurface
> mPixmap
;
412 static GLContextGLX
*
413 GetGlobalContextGLX()
415 return static_cast<GLContextGLX
*>(GLContextProviderGLX::GetGlobalContext());
419 AreCompatibleVisuals(XVisualInfo
*one
, XVisualInfo
*two
)
421 if (one
->c_class
!= two
->c_class
) {
425 if (one
->depth
!= two
->depth
) {
429 if (one
->red_mask
!= two
->red_mask
||
430 one
->green_mask
!= two
->green_mask
||
431 one
->blue_mask
!= two
->blue_mask
) {
435 if (one
->bits_per_rgb
!= two
->bits_per_rgb
) {
442 already_AddRefed
<GLContext
>
443 GLContextProviderGLX::CreateForWindow(nsIWidget
*aWidget
)
445 if (!sGLXLibrary
.EnsureInitialized()) {
449 // Currently, we take whatever Visual the window already has, and
450 // try to create an fbconfig for that visual. This isn't
451 // necessarily what we want in the long run; an fbconfig may not
452 // be available for the existing visual, or if it is, the GL
453 // performance might be suboptimal. But using the existing visual
454 // is a relatively safe intermediate step.
456 Display
*display
= (Display
*)aWidget
->GetNativeData(NS_NATIVE_DISPLAY
);
457 int xscreen
= DefaultScreen(display
);
458 Window window
= GET_NATIVE_WINDOW(aWidget
);
461 ScopedXFree
<GLXFBConfig
> cfgs
;
462 if (gIsATI
|| !GLXVersionCheck(1, 3)) {
463 const int attribs
[] = {
464 GLX_DOUBLEBUFFER
, False
,
467 cfgs
= sGLXLibrary
.xChooseFBConfig(display
,
472 cfgs
= sGLXLibrary
.xGetFBConfigs(display
,
478 NS_WARNING("[GLX] glXGetFBConfigs() failed");
481 NS_ASSERTION(numConfigs
> 0, "No FBConfigs found!");
483 // XXX the visual ID is almost certainly the GLX_FBCONFIG_ID, so
484 // we could probably do this first and replace the glXGetFBConfigs
485 // with glXChooseConfigs. Docs are sparklingly clear as always.
486 XWindowAttributes widgetAttrs
;
487 if (!XGetWindowAttributes(display
, window
, &widgetAttrs
)) {
488 NS_WARNING("[GLX] XGetWindowAttributes() failed");
491 const VisualID widgetVisualID
= XVisualIDFromVisual(widgetAttrs
.visual
);
493 printf("[GLX] widget has VisualID 0x%lx\n", widgetVisualID
);
496 ScopedXFree
<XVisualInfo
> vi
;
498 XVisualInfo vinfo_template
;
500 vinfo_template
.visual
= widgetAttrs
.visual
;
501 vinfo_template
.visualid
= XVisualIDFromVisual(vinfo_template
.visual
);
502 vinfo_template
.depth
= widgetAttrs
.depth
;
503 vinfo_template
.screen
= xscreen
;
504 vi
= XGetVisualInfo(display
, VisualIDMask
|VisualDepthMask
|VisualScreenMask
,
505 &vinfo_template
, &nvisuals
);
506 NS_ASSERTION(vi
&& nvisuals
== 1, "Could not locate unique matching XVisualInfo for Visual");
510 ScopedXFree
<XVisualInfo
> vinfo
;
512 for (int i
= 0; i
< numConfigs
; i
++) {
513 vinfo
= sGLXLibrary
.xGetVisualFromFBConfig(display
, cfgs
[i
]);
518 if (AreCompatibleVisuals(vi
, vinfo
)) {
523 if (widgetVisualID
== vinfo
->visualid
) {
530 if (matchIndex
== -1) {
531 NS_WARNING("[GLX] Couldn't find a FBConfig matching widget visual");
535 GLContextGLX
*shareContext
= GetGlobalContextGLX();
537 nsRefPtr
<GLContextGLX
> glContext
= GLContextGLX::CreateGLContext(ContextFormat(ContextFormat::BasicRGB24
),
544 return glContext
.forget();
547 static already_AddRefed
<GLContextGLX
>
548 CreateOffscreenPixmapContext(const gfxIntSize
& aSize
,
549 const ContextFormat
& aFormat
,
552 if (!sGLXLibrary
.EnsureInitialized()) {
556 Display
*display
= DefaultXDisplay();
557 int xscreen
= DefaultScreen(display
);
560 GLX_DOUBLEBUFFER
, False
,
561 GLX_DRAWABLE_TYPE
, GLX_PIXMAP_BIT
,
562 GLX_X_RENDERABLE
, True
,
572 ScopedXFree
<GLXFBConfig
> cfgs
;
573 cfgs
= sGLXLibrary
.xChooseFBConfig(display
,
581 NS_ASSERTION(numConfigs
> 0,
582 "glXChooseFBConfig() failed to match our requested format and violated its spec (!)");
584 ScopedXFree
<XVisualInfo
> vinfo
;
587 for (int i
= 0; i
< numConfigs
; ++i
) {
590 if (sGLXLibrary
.xGetFBConfigAttrib(display
, cfgs
[i
], GLX_DRAWABLE_TYPE
, &dtype
) != Success
591 || !(dtype
& GLX_PIXMAP_BIT
))
595 if (sGLXLibrary
.xGetFBConfigAttrib(display
, cfgs
[i
], GLX_VISUAL_ID
, &visid
) != Success
601 vinfo
= sGLXLibrary
.xGetVisualFromFBConfig(display
, cfgs
[i
]);
610 NS_WARNING("glXChooseFBConfig() didn't give us any configs with visuals!");
614 ScopedXErrorHandler xErrorHandler
;
615 GLXPixmap glxpixmap
= 0;
618 nsRefPtr
<gfxXlibSurface
> xsurface
= gfxXlibSurface::Create(DefaultScreenOfDisplay(display
),
621 if (xsurface
->CairoStatus() != 0) {
623 goto DONE_CREATING_PIXMAP
;
626 // Handle slightly different signature between glXCreatePixmap and
627 // its pre-GLX-1.3 extension equivalent (though given the ABI, we
628 // might not need to).
629 if (GLXVersionCheck(1, 3)) {
630 glxpixmap
= sGLXLibrary
.xCreatePixmap(display
,
632 xsurface
->XDrawable(),
635 glxpixmap
= sGLXLibrary
.xCreateGLXPixmapWithConfig(display
,
640 if (glxpixmap
== 0) {
644 DONE_CREATING_PIXMAP
:
646 nsRefPtr
<GLContextGLX
> glContext
;
647 bool serverError
= xErrorHandler
.SyncAndGetError(display
);
649 if (!error
&& // earlier recorded error
652 glContext
= GLContextGLX::CreateGLContext(
658 aShare
? GetGlobalContextGLX() : nsnull
,
663 return glContext
.forget();
666 already_AddRefed
<GLContext
>
667 GLContextProviderGLX::CreateOffscreen(const gfxIntSize
& aSize
,
668 const ContextFormat
& aFormat
)
671 nsRefPtr
<GLContextGLX
> glContext
=
672 CreateOffscreenPixmapContext(aSize
, aFormat
, PR_TRUE
);
678 if (!glContext
->GetSharedContext()) {
679 // no point in returning anything if sharing failed, we can't
684 if (!glContext
->ResizeOffscreenFBO(aSize
)) {
685 // we weren't able to create the initial
686 // offscreen FBO, so this is dead
690 return glContext
.forget();
693 already_AddRefed
<GLContext
>
694 GLContextProviderGLX::CreateForNativePixmapSurface(gfxASurface
*aSurface
)
696 if (!sGLXLibrary
.EnsureInitialized()) {
700 if (aSurface
->GetType() != gfxASurface::SurfaceTypeXlib
) {
701 NS_WARNING("GLContextProviderGLX::CreateForNativePixmapSurface called with non-Xlib surface");
705 nsAutoTArray
<int, 20> attribs
;
707 #define A1_(_x) do { attribs.AppendElement(_x); } while(0)
708 #define A2_(_x,_y) do { \
709 attribs.AppendElement(_x); \
710 attribs.AppendElement(_y); \
713 A2_(GLX_DOUBLEBUFFER
, False
);
714 A2_(GLX_DRAWABLE_TYPE
, GLX_PIXMAP_BIT
);
718 Display
*display
= DefaultXDisplay();
719 int xscreen
= DefaultScreen(display
);
721 ScopedXFree
<GLXFBConfig
> cfg(sGLXLibrary
.xChooseFBConfig(display
,
728 NS_ASSERTION(numFormats
> 0,
729 "glXChooseFBConfig() failed to match our requested format and violated its spec (!)");
731 gfxXlibSurface
*xs
= static_cast<gfxXlibSurface
*>(aSurface
);
733 GLXPixmap glxpixmap
= sGLXLibrary
.xCreatePixmap(display
,
738 nsRefPtr
<GLContextGLX
> glContext
= GLContextGLX::CreateGLContext(ContextFormat(ContextFormat::BasicRGB24
),
747 return glContext
.forget();
750 static nsRefPtr
<GLContext
> gGlobalContext
;
753 GLContextProviderGLX::GetGlobalContext()
755 static bool triedToCreateContext
= false;
756 if (!triedToCreateContext
&& !gGlobalContext
) {
757 triedToCreateContext
= true;
758 gGlobalContext
= CreateOffscreenPixmapContext(gfxIntSize(1, 1),
759 ContextFormat(ContextFormat::BasicRGB24
),
762 gGlobalContext
->SetIsGlobalSharedContext(PR_TRUE
);
765 return gGlobalContext
;
769 GLContextProviderGLX::Shutdown()
771 gGlobalContext
= nsnull
;
775 } /* namespace mozilla */