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"
52 #include "GLContextProvider.h"
54 #include "nsIWidget.h"
55 #include "GLXLibrary.h"
56 #include "gfxXlibSurface.h"
57 #include "gfxContext.h"
58 #include "gfxImageSurface.h"
59 #include "gfxPlatform.h"
64 static PRBool gIsATI
= PR_FALSE
;
65 static PRBool gIsChromium
= PR_FALSE
;
66 static int gGLXVersion
= 0;
69 GLXLibrary::EnsureInitialized()
76 mOGLLibrary
= PR_LoadLibrary("libGL.so.1");
78 NS_WARNING("Couldn't load OpenGL shared library.");
83 LibrarySymbolLoader::SymLoadStruct symbols
[] = {
84 { (PRFuncPtr
*) &xDeleteContext
, { "glXDestroyContext", NULL
} },
85 { (PRFuncPtr
*) &xMakeCurrent
, { "glXMakeCurrent", NULL
} },
86 { (PRFuncPtr
*) &xGetProcAddress
, { "glXGetProcAddress", NULL
} },
87 { (PRFuncPtr
*) &xChooseVisual
, { "glXChooseVisual", NULL
} },
88 { (PRFuncPtr
*) &xChooseFBConfig
, { "glXChooseFBConfig", NULL
} },
89 { (PRFuncPtr
*) &xGetFBConfigs
, { "glXGetFBConfigs", NULL
} },
90 { (PRFuncPtr
*) &xCreatePbuffer
, { "glXCreatePbuffer", NULL
} },
91 { (PRFuncPtr
*) &xCreateNewContext
, { "glXCreateNewContext", NULL
} },
92 { (PRFuncPtr
*) &xDestroyPbuffer
, { "glXDestroyPbuffer", NULL
} },
93 { (PRFuncPtr
*) &xGetVisualFromFBConfig
, { "glXGetVisualFromFBConfig", NULL
} },
94 { (PRFuncPtr
*) &xGetFBConfigAttrib
, { "glXGetFBConfigAttrib", NULL
} },
95 { (PRFuncPtr
*) &xSwapBuffers
, { "glXSwapBuffers", NULL
} },
96 { (PRFuncPtr
*) &xQueryServerString
, { "glXQueryServerString", NULL
} },
97 { (PRFuncPtr
*) &xCreatePixmap
, { "glXCreatePixmap", NULL
} },
98 { (PRFuncPtr
*) &xDestroyPixmap
, { "glXDestroyPixmap", NULL
} },
99 { (PRFuncPtr
*) &xGetClientString
, { "glXGetClientString", NULL
} },
100 { (PRFuncPtr
*) &xCreateContext
, { "glXCreateContext", NULL
} },
104 if (!LibrarySymbolLoader::LoadSymbols(mOGLLibrary
, &symbols
[0])) {
105 NS_WARNING("Couldn't find required entry point in OpenGL shared library");
109 const char *vendor
= xQueryServerString(DefaultXDisplay(),
110 DefaultScreen(DefaultXDisplay()),
112 const char *serverVersionStr
= xQueryServerString(DefaultXDisplay(),
113 DefaultScreen(DefaultXDisplay()),
115 const char *clientVersionStr
= xGetClientString(DefaultXDisplay(),
119 int serverVersion
= 0, clientVersion
= 0;
120 if (serverVersionStr
&&
121 strlen(serverVersionStr
) >= 3 &&
122 serverVersionStr
[1] == '.')
124 serverVersion
= (serverVersionStr
[0] - '0') << 8 | (serverVersionStr
[2] - '0');
127 if (clientVersionStr
&&
128 strlen(clientVersionStr
) >= 3 &&
129 clientVersionStr
[1] == '.')
131 clientVersion
= (clientVersionStr
[0] - '0') << 8 | (clientVersionStr
[2] - '0');
134 gGLXVersion
= PR_MIN(clientVersion
, serverVersion
);
136 if (gGLXVersion
< 0x0103)
139 gIsATI
= vendor
&& strstr(vendor
, "ATI");
140 gIsChromium
= (vendor
&& strstr(vendor
, "Chromium")) ||
141 (serverVersion
&& strstr(serverVersionStr
, "Chromium"));
143 mInitialized
= PR_TRUE
;
147 GLXLibrary sGLXLibrary
;
149 static bool ctxErrorOccurred
= false;
151 ctxErrorHandler(Display
*dpy
, XErrorEvent
*ev
)
153 ctxErrorOccurred
= true;
157 class GLContextGLX
: public GLContext
160 static already_AddRefed
<GLContextGLX
>
161 CreateGLContext(const ContextFormat
& format
,
163 GLXDrawable drawable
,
166 GLContextGLX
*shareContext
,
167 PRBool deleteDrawable
,
168 gfxXlibSurface
*pixmap
= nsnull
)
171 err
= sGLXLibrary
.xGetFBConfigAttrib(display
, cfg
,
172 GLX_DOUBLEBUFFER
, &db
);
173 if (GLX_BAD_ATTRIBUTE
!= err
) {
175 printf("[GLX] FBConfig is %sdouble-buffered\n", db
? "" : "not ");
179 ctxErrorOccurred
= false;
180 int (*oldHandler
)(Display
*, XErrorEvent
*);
183 TRY_AGAIN_NO_SHARING
:
184 oldHandler
= XSetErrorHandler(&ctxErrorHandler
);
186 if (gGLXVersion
>= 0x0103) {
187 context
= sGLXLibrary
.xCreateNewContext(display
,
190 shareContext
? shareContext
->mContext
: NULL
,
193 context
= sGLXLibrary
.xCreateContext(display
,
195 shareContext
? shareContext
->mContext
: NULL
,
199 XSync(display
, False
);
200 XSetErrorHandler(oldHandler
);
202 if (!context
|| ctxErrorOccurred
) {
204 shareContext
= nsnull
;
205 goto TRY_AGAIN_NO_SHARING
;
207 NS_WARNING("Failed to create GLXContext!");
211 nsRefPtr
<GLContextGLX
> glContext(new GLContextGLX(format
,
219 if (!glContext
->Init()) {
223 return glContext
.forget();
228 sGLXLibrary
.xDeleteContext(mDisplay
, mContext
);
230 if (mDeleteDrawable
) {
231 sGLXLibrary
.xDestroyPixmap(mDisplay
, mDrawable
);
235 GLContextType
GetContextType() {
236 return ContextTypeGLX
;
242 SetupLookupFunction();
243 if (!InitWithPrefix("gl", PR_TRUE
)) {
247 return IsExtensionSupported("GL_EXT_framebuffer_object");
252 Bool succeeded
= sGLXLibrary
.xMakeCurrent(mDisplay
, mDrawable
, mContext
);
253 NS_ASSERTION(succeeded
, "Failed to make GL context current!");
257 PRBool
SetupLookupFunction()
259 mLookupFunc
= (PlatformLookupFunction
)sGLXLibrary
.xGetProcAddress
;
263 void *GetNativeData(NativeDataType aType
)
266 case NativeGLContext
:
269 case NativeThebesSurface
:
277 PRBool
IsDoubleBuffered()
279 return mDoubleBuffered
;
284 if (!mDoubleBuffered
)
286 sGLXLibrary
.xSwapBuffers(mDisplay
, mDrawable
);
290 void WindowDestroyed()
292 for (unsigned int i
=0; i
<textures
.Length(); i
++) {
293 GLContext::DestroyTexture(textures
.ElementAt(i
));
298 // NB: we could set a flag upon WindowDestroyed() to dictate an
299 // early-return from CreateTexture(), but then we would need the
300 // same check before all GL calls, and that heads down a rabbit
302 virtual GLuint
CreateTexture()
304 GLuint tex
= GLContext::CreateTexture();
305 NS_ASSERTION(!textures
.Contains(tex
), "");
306 textures
.AppendElement(tex
);
310 virtual void DestroyTexture(GLuint texture
)
312 if (textures
.Contains(texture
)) {
313 textures
.RemoveElement(texture
);
314 GLContext::DestroyTexture(texture
);
318 virtual already_AddRefed
<TextureImage
>
319 CreateBasicTextureImage(GLuint aTexture
,
320 const nsIntSize
& aSize
,
321 TextureImage::ContentType aContentType
,
322 GLContext
* aContext
);
325 friend class GLContextProviderGLX
;
327 GLContextGLX(const ContextFormat
& aFormat
,
328 GLContext
*aShareContext
,
330 GLXDrawable aDrawable
,
332 PRBool aDeleteDrawable
,
333 PRBool aDoubleBuffered
,
334 gfxXlibSurface
*aPixmap
)
335 : GLContext(aFormat
, aDeleteDrawable
? PR_TRUE
: PR_FALSE
, aShareContext
),
338 mDrawable(aDrawable
),
339 mDeleteDrawable(aDeleteDrawable
),
340 mDoubleBuffered(aDoubleBuffered
),
346 GLXDrawable mDrawable
;
347 PRPackedBool mDeleteDrawable
;
348 PRPackedBool mDoubleBuffered
;
350 nsTArray
<GLuint
> textures
;
351 nsRefPtr
<gfxXlibSurface
> mPixmap
;
354 // FIXME/bug 575505: this is a (very slow!) placeholder
355 // implementation. Much better would be to create a Pixmap, wrap that
356 // in a GLXPixmap, and then glXBindTexImage() to our texture.
357 class TextureImageGLX
: public BasicTextureImage
359 friend already_AddRefed
<TextureImage
>
360 GLContextGLX::CreateBasicTextureImage(GLuint
,
362 TextureImage::ContentType
,
366 virtual already_AddRefed
<gfxASurface
>
367 CreateUpdateSurface(const gfxIntSize
& aSize
, ImageFormat aFmt
)
369 mUpdateFormat
= aFmt
;
370 return gfxPlatform::GetPlatform()->CreateOffscreenSurface(aSize
, aFmt
);
373 virtual already_AddRefed
<gfxImageSurface
>
374 GetImageForUpload(gfxASurface
* aUpdateSurface
)
376 nsRefPtr
<gfxImageSurface
> image
=
377 new gfxImageSurface(gfxIntSize(mUpdateRect
.width
,
380 nsRefPtr
<gfxContext
> tmpContext
= new gfxContext(image
);
382 tmpContext
->SetSource(aUpdateSurface
);
383 tmpContext
->SetOperator(gfxContext::OPERATOR_SOURCE
);
386 return image
.forget();
390 TextureImageGLX(GLuint aTexture
,
391 const nsIntSize
& aSize
,
392 ContentType aContentType
,
394 : BasicTextureImage(aTexture
, aSize
, aContentType
, aContext
)
397 ImageFormat mUpdateFormat
;
400 already_AddRefed
<TextureImage
>
401 GLContextGLX::CreateBasicTextureImage(GLuint aTexture
,
402 const nsIntSize
& aSize
,
403 TextureImage::ContentType aContentType
,
406 nsRefPtr
<TextureImageGLX
> teximage(
407 new TextureImageGLX(aTexture
, aSize
, aContentType
, aContext
));
408 return teximage
.forget();
411 static GLContextGLX
*
412 GetGlobalContextGLX()
414 return static_cast<GLContextGLX
*>(GLContextProviderGLX::GetGlobalContext());
418 AreCompatibleVisuals(XVisualInfo
*one
, XVisualInfo
*two
)
420 if (one
->c_class
!= two
->c_class
) {
424 if (one
->depth
!= two
->depth
) {
428 if (one
->red_mask
!= two
->red_mask
||
429 one
->green_mask
!= two
->green_mask
||
430 one
->blue_mask
!= two
->blue_mask
) {
434 if (one
->bits_per_rgb
!= two
->bits_per_rgb
) {
441 already_AddRefed
<GLContext
>
442 GLContextProviderGLX::CreateForWindow(nsIWidget
*aWidget
)
444 if (!sGLXLibrary
.EnsureInitialized()) {
448 // Currently, we take whatever Visual the window already has, and
449 // try to create an fbconfig for that visual. This isn't
450 // necessarily what we want in the long run; an fbconfig may not
451 // be available for the existing visual, or if it is, the GL
452 // performance might be suboptimal. But using the existing visual
453 // is a relatively safe intermediate step.
455 Display
*display
= (Display
*)aWidget
->GetNativeData(NS_NATIVE_DISPLAY
);
456 int xscreen
= DefaultScreen(display
);
457 Window window
= GET_NATIVE_WINDOW(aWidget
);
460 ScopedXFree
<GLXFBConfig
> cfgs
;
462 const int attribs
[] = {
463 GLX_DOUBLEBUFFER
, False
,
466 cfgs
= sGLXLibrary
.xChooseFBConfig(display
,
471 cfgs
= sGLXLibrary
.xGetFBConfigs(display
,
477 NS_WARNING("[GLX] glXGetFBConfigs() failed");
480 NS_ASSERTION(numConfigs
> 0, "No FBConfigs found!");
482 // XXX the visual ID is almost certainly the GLX_FBCONFIG_ID, so
483 // we could probably do this first and replace the glXGetFBConfigs
484 // with glXChooseConfigs. Docs are sparklingly clear as always.
485 XWindowAttributes widgetAttrs
;
486 if (!XGetWindowAttributes(display
, window
, &widgetAttrs
)) {
487 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 nsRefPtr
<gfxXlibSurface
> xsurface
= gfxXlibSurface::Create(DefaultScreenOfDisplay(display
),
617 if (xsurface
->CairoStatus() != 0) {
622 GLXPixmap glxpixmap
= sGLXLibrary
.xCreatePixmap(display
,
624 xsurface
->XDrawable(),
626 if (glxpixmap
== 0) {
630 GLContextGLX
*shareContext
= aShare
? GetGlobalContextGLX() : nsnull
;
632 nsRefPtr
<GLContextGLX
> glContext
= GLContextGLX::CreateGLContext(aFormat
,
641 return glContext
.forget();
644 already_AddRefed
<GLContext
>
645 GLContextProviderGLX::CreateOffscreen(const gfxIntSize
& aSize
,
646 const ContextFormat
& aFormat
)
649 nsRefPtr
<GLContextGLX
> glContext
=
650 CreateOffscreenPixmapContext(aSize
, aFormat
, PR_TRUE
);
656 if (!glContext
->GetSharedContext()) {
657 // no point in returning anything if sharing failed, we can't
662 if (!glContext
->ResizeOffscreenFBO(aSize
)) {
663 // we weren't able to create the initial
664 // offscreen FBO, so this is dead
668 return glContext
.forget();
671 already_AddRefed
<GLContext
>
672 GLContextProviderGLX::CreateForNativePixmapSurface(gfxASurface
*aSurface
)
674 if (!sGLXLibrary
.EnsureInitialized()) {
678 if (aSurface
->GetType() != gfxASurface::SurfaceTypeXlib
) {
679 NS_WARNING("GLContextProviderGLX::CreateForNativePixmapSurface called with non-Xlib surface");
683 nsAutoTArray
<int, 20> attribs
;
685 #define A1_(_x) do { attribs.AppendElement(_x); } while(0)
686 #define A2_(_x,_y) do { \
687 attribs.AppendElement(_x); \
688 attribs.AppendElement(_y); \
691 A2_(GLX_DOUBLEBUFFER
, False
);
692 A2_(GLX_DRAWABLE_TYPE
, GLX_PIXMAP_BIT
);
696 Display
*display
= DefaultXDisplay();
697 int xscreen
= DefaultScreen(display
);
699 ScopedXFree
<GLXFBConfig
> cfg(sGLXLibrary
.xChooseFBConfig(display
,
706 NS_ASSERTION(numFormats
> 0,
707 "glXChooseFBConfig() failed to match our requested format and violated its spec (!)");
709 gfxXlibSurface
*xs
= static_cast<gfxXlibSurface
*>(aSurface
);
711 GLXPixmap glxpixmap
= sGLXLibrary
.xCreatePixmap(display
,
716 nsRefPtr
<GLContextGLX
> glContext
= GLContextGLX::CreateGLContext(ContextFormat(ContextFormat::BasicRGB24
),
725 if (!glContext
->Init()) {
729 return glContext
.forget();
732 static nsRefPtr
<GLContext
> gGlobalContext
;
735 GLContextProviderGLX::GetGlobalContext()
737 static bool triedToCreateContext
= false;
738 if (!triedToCreateContext
&& !gGlobalContext
) {
739 triedToCreateContext
= true;
740 gGlobalContext
= CreateOffscreenPixmapContext(gfxIntSize(1, 1),
741 ContextFormat(ContextFormat::BasicRGB24
),
745 return gGlobalContext
;
749 GLContextProviderGLX::Shutdown()
751 gGlobalContext
= nsnull
;
755 } /* namespace mozilla */