1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 #include <vcl/lazydelete.hxx>
15 #include <unx/saldisp.hxx>
16 #include <unx/salframe.h>
17 #include <unx/salgdi.h>
18 #include <unx/salinst.h>
19 #include <unx/salvd.h>
20 #include <unx/x11/xlimits.hxx>
22 #include <opengl/texture.hxx>
23 #include <opengl/zone.hxx>
24 #include <opengl/RenderState.hxx>
25 #include <opengl/x11/gdiimpl.hxx>
26 #include <opengl/x11/salvd.hxx>
28 #include <vcl/opengl/OpenGLContext.hxx>
29 #include <vcl/opengl/OpenGLHelper.hxx>
30 #include <sal/log.hxx>
32 #include <o3tl/lru_map.hxx>
33 #include <ControlCacheKey.hxx>
35 static std::vector
<GLXContext
> g_vShareList
;
36 static bool g_bAnyCurrent
;
38 class X11OpenGLContext
: public OpenGLContext
41 void init(Display
* dpy
, Window win
, int screen
);
42 virtual void initWindow() override
;
45 virtual const GLWindow
& getOpenGLWindow() const override
{ return m_aGLWin
; }
46 virtual GLWindow
& getModifiableOpenGLWindow() override
{ return m_aGLWin
; }
47 virtual bool ImplInit() override
;
48 void initGLWindow(Visual
* pVisual
);
49 virtual SystemWindowData
generateWinData(vcl::Window
* pParent
, bool bRequestLegacyContext
) override
;
50 virtual void makeCurrent() override
;
51 virtual void destroyCurrentContext() override
;
52 virtual bool isCurrent() override
;
53 virtual bool isAnyCurrent() override
;
54 virtual void sync() override
;
55 virtual void resetCurrent() override
;
56 virtual void swapBuffers() override
;
63 int unxErrorHandler(Display
* dpy
, XErrorEvent
* event
)
68 XGetErrorText(dpy
, event
->error_code
, err
, 256);
69 XGetErrorText(dpy
, event
->request_code
, req
, 256);
70 XGetErrorText(dpy
, event
->minor_code
, minor
, 256);
71 SAL_WARN("vcl.opengl", "Error: " << err
<< ", Req: " << req
<< ", Minor: " << minor
);
76 typedef int (*errorHandler
)(Display
* /*dpy*/, XErrorEvent
* /*evnt*/);
78 class TempErrorHandler
81 errorHandler oldErrorHandler
;
85 TempErrorHandler(Display
* dpy
, errorHandler newErrorHandler
)
86 : oldErrorHandler(nullptr)
93 oldErrorHandler
= XSetErrorHandler(newErrorHandler
);
101 // sync so that we possibly get an XError
104 XSetErrorHandler(oldErrorHandler
);
105 XUnlockDisplay(mdpy
);
110 static bool errorTriggered
;
111 int oglErrorHandler( Display
* /*dpy*/, XErrorEvent
* /*evnt*/ )
113 errorTriggered
= true;
118 GLXFBConfig
* getFBConfig(Display
* dpy
, Window win
, int& nBestFBC
)
122 if( dpy
== nullptr || !glXQueryExtension( dpy
, nullptr, nullptr ) )
125 VCL_GL_INFO("window: " << win
);
127 XWindowAttributes xattr
;
128 if( !XGetWindowAttributes( dpy
, win
, &xattr
) )
130 SAL_WARN("vcl.opengl", "Failed to get window attributes for fbconfig " << win
);
131 xattr
.screen
= nullptr;
132 xattr
.visual
= nullptr;
135 int screen
= XScreenNumberOfScreen( xattr
.screen
);
137 // TODO: moggi: Select colour channel depth based on visual attributes, not hardcoded */
138 static int visual_attribs
[] =
140 GLX_DOUBLEBUFFER
, True
,
141 GLX_X_RENDERABLE
, True
,
147 GLX_X_VISUAL_TYPE
, GLX_TRUE_COLOR
,
152 GLXFBConfig
* pFBC
= glXChooseFBConfig( dpy
,
154 visual_attribs
, &fbCount
);
158 SAL_WARN("vcl.opengl", "no suitable fb format found");
162 int best_num_samp
= -1;
163 for(int i
= 0; i
< fbCount
; ++i
)
165 XVisualInfo
* pVi
= glXGetVisualFromFBConfig( dpy
, pFBC
[i
] );
166 if(pVi
&& (xattr
.visual
&& pVi
->visualid
== xattr
.visual
->visualid
) )
168 // pick the one with the most samples per pixel
171 glXGetFBConfigAttrib( dpy
, pFBC
[i
], GLX_SAMPLE_BUFFERS
, &nSampleBuf
);
172 glXGetFBConfigAttrib( dpy
, pFBC
[i
], GLX_SAMPLES
, &nSamples
);
174 if ( nBestFBC
< 0 || (nSampleBuf
&& ( nSamples
> best_num_samp
)) )
177 best_num_samp
= nSamples
;
186 Visual
* getVisual(Display
* dpy
, Window win
)
190 XWindowAttributes xattr
;
191 if( !XGetWindowAttributes( dpy
, win
, &xattr
) )
193 SAL_WARN("vcl.opengl", "Failed to get window attributes for getVisual " << win
);
194 xattr
.visual
= nullptr;
196 VCL_GL_INFO("using VisualID " << xattr
.visual
);
201 void X11OpenGLContext::sync()
205 XSync(m_aGLWin
.dpy
, false);
208 void X11OpenGLContext::swapBuffers()
212 glXSwapBuffers(m_aGLWin
.dpy
, m_aGLWin
.win
);
217 void X11OpenGLContext::resetCurrent()
225 glXMakeCurrent(m_aGLWin
.dpy
, None
, nullptr);
226 g_bAnyCurrent
= false;
230 bool X11OpenGLContext::isCurrent()
233 return g_bAnyCurrent
&& m_aGLWin
.ctx
&& glXGetCurrentContext() == m_aGLWin
.ctx
&&
234 glXGetCurrentDrawable() == m_aGLWin
.win
;
237 bool X11OpenGLContext::isAnyCurrent()
239 return g_bAnyCurrent
&& glXGetCurrentContext() != None
;
242 SystemWindowData
X11OpenGLContext::generateWinData(vcl::Window
* pParent
, bool /*bRequestLegacyContext*/)
246 SystemWindowData aWinData
;
247 aWinData
.pVisual
= nullptr;
249 const SystemEnvData
* sysData(pParent
->GetSystemData());
251 Display
*dpy
= static_cast<Display
*>(sysData
->pDisplay
);
252 Window win
= sysData
->aWindow
;
254 if( dpy
== nullptr || !glXQueryExtension( dpy
, nullptr, nullptr ) )
258 GLXFBConfig
* pFBC
= getFBConfig(dpy
, win
, best_fbc
);
263 XVisualInfo
* vi
= nullptr;
265 vi
= glXGetVisualFromFBConfig( dpy
, pFBC
[best_fbc
] );
271 VCL_GL_INFO("using VisualID " << vi
->visualid
);
272 aWinData
.pVisual
= static_cast<void*>(vi
->visual
);
278 bool X11OpenGLContext::ImplInit()
285 GLXContext
pSharedCtx( nullptr );
287 TempErrorHandler
aErrorHandler(m_aGLWin
.dpy
, unxErrorHandler
);
290 VCL_GL_INFO("OpenGLContext::ImplInit----start");
292 if (!g_vShareList
.empty())
293 pSharedCtx
= g_vShareList
.front();
295 //tdf#112166 for, e.g. VirtualBox GL, claiming OpenGL 2.1
296 static bool hasCreateContextAttribsARB
= glXGetProcAddress(reinterpret_cast<const GLubyte
*>("glXCreateContextAttribsARB")) != nullptr;
297 if (hasCreateContextAttribsARB
&& !mbRequestLegacyContext
)
300 GLXFBConfig
* pFBC
= getFBConfig(m_aGLWin
.dpy
, m_aGLWin
.win
, best_fbc
);
302 if (pFBC
&& best_fbc
!= -1)
304 int const pContextAttribs
[] =
306 #if 0 // defined(DBG_UTIL)
307 GLX_CONTEXT_MAJOR_VERSION_ARB
, 3,
308 GLX_CONTEXT_MINOR_VERSION_ARB
, 2,
313 m_aGLWin
.ctx
= glXCreateContextAttribsARB(m_aGLWin
.dpy
, pFBC
[best_fbc
], pSharedCtx
, /* direct, not via X */ GL_TRUE
, pContextAttribs
);
314 SAL_INFO_IF(m_aGLWin
.ctx
, "vcl.opengl", "created a 3.2 core context");
317 SAL_WARN("vcl.opengl", "unable to find correct FBC");
325 SAL_WARN("vcl.opengl", "attempting to create a non-double-buffered "
326 "visual matching the context");
328 m_aGLWin
.ctx
= glXCreateContext(m_aGLWin
.dpy
,
331 GL_TRUE
/* direct, not via X server */);
336 g_vShareList
.push_back( m_aGLWin
.ctx
);
340 SAL_WARN("vcl.opengl", "unable to create GLX context");
344 if( !glXMakeCurrent( m_aGLWin
.dpy
, m_aGLWin
.win
, m_aGLWin
.ctx
) )
346 g_bAnyCurrent
= false;
347 SAL_WARN("vcl.opengl", "unable to select current GLX context");
351 g_bAnyCurrent
= true;
353 int glxMinor
, glxMajor
;
354 double nGLXVersion
= 0;
355 if( glXQueryVersion( m_aGLWin
.dpy
, &glxMajor
, &glxMinor
) )
356 nGLXVersion
= glxMajor
+ 0.1*glxMinor
;
357 SAL_INFO("vcl.opengl", "available GLX version: " << nGLXVersion
);
359 SAL_INFO("vcl.opengl", "available GL extensions: " << glGetString(GL_EXTENSIONS
));
361 XWindowAttributes aWinAttr
;
362 if( !XGetWindowAttributes( m_aGLWin
.dpy
, m_aGLWin
.win
, &aWinAttr
) )
364 SAL_WARN("vcl.opengl", "Failed to get window attributes on " << m_aGLWin
.win
);
370 m_aGLWin
.Width
= aWinAttr
.width
;
371 m_aGLWin
.Height
= aWinAttr
.height
;
374 if( m_aGLWin
.HasGLXExtension("GLX_SGI_swap_control" ) )
377 typedef GLint (*glXSwapIntervalProc
)(GLint
);
378 glXSwapIntervalProc glXSwapInterval
= reinterpret_cast<glXSwapIntervalProc
>(glXGetProcAddress( reinterpret_cast<const GLubyte
*>("glXSwapIntervalSGI") ));
379 if( glXSwapInterval
)
381 TempErrorHandler
aLocalErrorHandler(m_aGLWin
.dpy
, oglErrorHandler
);
383 errorTriggered
= false;
385 glXSwapInterval( 1 );
388 SAL_WARN("vcl.opengl", "error when trying to set swap interval, NVIDIA or Mesa bug?");
390 VCL_GL_INFO("set swap interval to 1 (enable vsync)");
394 bool bRet
= InitGL();
397 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
| GL_STENCIL_BUFFER_BIT
);
404 void X11OpenGLContext::makeCurrent()
414 TempErrorHandler
aErrorHandler(m_aGLWin
.dpy
, unxErrorHandler
);
419 if (!glXMakeCurrent( m_aGLWin
.dpy
, m_aGLWin
.win
, m_aGLWin
.ctx
))
421 g_bAnyCurrent
= false;
422 SAL_WARN("vcl.opengl", "OpenGLContext::makeCurrent failed "
423 "on drawable " << m_aGLWin
.win
);
426 g_bAnyCurrent
= true;
432 void X11OpenGLContext::destroyCurrentContext()
436 std::vector
<GLXContext
>::iterator itr
= std::remove( g_vShareList
.begin(), g_vShareList
.end(), m_aGLWin
.ctx
);
437 if (itr
!= g_vShareList
.end())
438 g_vShareList
.erase(itr
);
440 glXMakeCurrent(m_aGLWin
.dpy
, None
, nullptr);
441 g_bAnyCurrent
= false;
442 if( glGetError() != GL_NO_ERROR
)
444 SAL_WARN("vcl.opengl", "glError: " << glGetError());
446 glXDestroyContext(m_aGLWin
.dpy
, m_aGLWin
.ctx
);
447 m_aGLWin
.ctx
= nullptr;
451 void X11OpenGLContext::init(Display
* dpy
, Window win
, int screen
)
463 m_aGLWin
.screen
= screen
;
465 Visual
* pVisual
= getVisual(dpy
, win
);
467 initGLWindow(pVisual
);
472 void X11OpenGLContext::initGLWindow(Visual
* pVisual
)
478 XVisualInfo aTemplate
;
479 aTemplate
.visualid
= XVisualIDFromVisual( pVisual
);
481 XVisualInfo
* pInfo
= XGetVisualInfo( m_aGLWin
.dpy
, VisualIDMask
, &aTemplate
, &nVisuals
);
483 SAL_WARN( "vcl.opengl", "match count for visual id is not 1" );
487 // Check multisample support
488 /* TODO: moggi: This is not necessarily correct in the DBG_UTIL path, as it picks
489 * an FBConfig instead ... */
491 glXGetConfig(m_aGLWin
.dpy
, m_aGLWin
.vi
, GLX_SAMPLES
, &nSamples
);
493 m_aGLWin
.bMultiSampleSupported
= true;
495 m_aGLWin
.GLXExtensions
= glXQueryExtensionsString( m_aGLWin
.dpy
, m_aGLWin
.screen
);
496 SAL_INFO("vcl.opengl", "available GLX extensions: " << m_aGLWin
.GLXExtensions
);
499 void X11OpenGLContext::initWindow()
501 const SystemEnvData
* pChildSysData
= nullptr;
502 SystemWindowData winData
= generateWinData(mpWindow
, false);
503 if( winData
.pVisual
)
505 if( !m_pChildWindow
)
507 m_pChildWindow
= VclPtr
<SystemChildWindow
>::Create(mpWindow
, 0, &winData
, false);
509 pChildSysData
= m_pChildWindow
->GetSystemData();
512 if (!m_pChildWindow
|| !pChildSysData
)
515 InitChildWindow(m_pChildWindow
.get());
517 m_aGLWin
.dpy
= static_cast<Display
*>(pChildSysData
->pDisplay
);
518 m_aGLWin
.win
= pChildSysData
->aWindow
;
519 m_aGLWin
.screen
= pChildSysData
->nScreen
;
521 Visual
* pVisual
= static_cast<Visual
*>(pChildSysData
->pVisual
);
522 initGLWindow(pVisual
);
525 GLX11Window::GLX11Window()
534 bool GLX11Window::HasGLXExtension( const char* name
) const
536 for (sal_Int32 i
= 0; i
!= -1;) {
537 if (GLXExtensions
.getToken(0, ' ', i
) == name
) {
544 GLX11Window::~GLX11Window()
549 bool GLX11Window::Synchronize(bool bOnoff
) const
551 XSynchronize(dpy
, bOnoff
);
555 OpenGLContext
* X11SalInstance::CreateOpenGLContext()
557 return new X11OpenGLContext
;
560 X11OpenGLSalGraphicsImpl::X11OpenGLSalGraphicsImpl( X11SalGraphics
& rParent
):
561 OpenGLSalGraphicsImpl(rParent
,rParent
.GetGeometryProvider()),
566 X11OpenGLSalGraphicsImpl::~X11OpenGLSalGraphicsImpl()
570 void X11OpenGLSalGraphicsImpl::Init()
572 // The m_pFrame and m_pVDev pointers are updated late in X11
573 mpProvider
= mrX11Parent
.GetGeometryProvider();
574 OpenGLSalGraphicsImpl::Init();
577 rtl::Reference
<OpenGLContext
> X11OpenGLSalGraphicsImpl::CreateWinContext()
579 NativeWindowHandleProvider
*pProvider
= dynamic_cast<NativeWindowHandleProvider
*>(mrX11Parent
.m_pFrame
);
584 sal_uIntPtr aWin
= pProvider
->GetNativeWindowHandle();
585 rtl::Reference
<X11OpenGLContext
> xContext
= new X11OpenGLContext
;
586 xContext
->setVCLOnly();
587 xContext
->init( mrX11Parent
.GetXDisplay(), aWin
,
588 mrX11Parent
.m_nXScreen
.getXScreen() );
589 return rtl::Reference
<OpenGLContext
>(xContext
.get());
592 void X11OpenGLSalGraphicsImpl::copyBits( const SalTwoRect
& rPosAry
, SalGraphics
* pSrcGraphics
)
594 OpenGLSalGraphicsImpl
*pImpl
= pSrcGraphics
? static_cast< OpenGLSalGraphicsImpl
* >(pSrcGraphics
->GetImpl()) : static_cast< OpenGLSalGraphicsImpl
*>(mrX11Parent
.GetImpl());
595 OpenGLSalGraphicsImpl::DoCopyBits( rPosAry
, *pImpl
);
598 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */