Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / opengl / x11 / gdiimpl.cxx
blobf238422ff441cba0efe15814f2ecdc90a6d37b67
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include <memory>
11 #include <vcl/lazydelete.hxx>
13 #include <svdata.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
40 public:
41 void init(Display* dpy, Window win, int screen);
42 virtual void initWindow() override;
43 private:
44 GLX11Window m_aGLWin;
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;
59 namespace
62 #ifdef DBG_UTIL
63 int unxErrorHandler(Display* dpy, XErrorEvent* event)
65 char err[256];
66 char req[256];
67 char minor[256];
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);
72 return 0;
74 #endif
76 typedef int (*errorHandler)(Display* /*dpy*/, XErrorEvent* /*evnt*/);
78 class TempErrorHandler
80 private:
81 errorHandler oldErrorHandler;
82 Display* const mdpy;
84 public:
85 TempErrorHandler(Display* dpy, errorHandler newErrorHandler)
86 : oldErrorHandler(nullptr)
87 , mdpy(dpy)
89 if (mdpy)
91 XLockDisplay(dpy);
92 XSync(dpy, false);
93 oldErrorHandler = XSetErrorHandler(newErrorHandler);
97 ~TempErrorHandler()
99 if (mdpy)
101 // sync so that we possibly get an XError
102 glXWaitGL();
103 XSync(mdpy, false);
104 XSetErrorHandler(oldErrorHandler);
105 XUnlockDisplay(mdpy);
110 static bool errorTriggered;
111 int oglErrorHandler( Display* /*dpy*/, XErrorEvent* /*evnt*/ )
113 errorTriggered = true;
115 return 0;
118 GLXFBConfig* getFBConfig(Display* dpy, Window win, int& nBestFBC)
120 OpenGLZone aZone;
122 if( dpy == nullptr || !glXQueryExtension( dpy, nullptr, nullptr ) )
123 return 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,
142 GLX_RED_SIZE, 8,
143 GLX_GREEN_SIZE, 8,
144 GLX_BLUE_SIZE, 8,
145 GLX_ALPHA_SIZE, 8,
146 GLX_DEPTH_SIZE, 24,
147 GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
148 None
151 int fbCount = 0;
152 GLXFBConfig* pFBC = glXChooseFBConfig( dpy,
153 screen,
154 visual_attribs, &fbCount );
156 if(!pFBC)
158 SAL_WARN("vcl.opengl", "no suitable fb format found");
159 return nullptr;
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
169 int nSampleBuf = 0;
170 int nSamples = 0;
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 )) )
176 nBestFBC = i;
177 best_num_samp = nSamples;
180 XFree( pVi );
183 return pFBC;
186 Visual* getVisual(Display* dpy, Window win)
188 OpenGLZone aZone;
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);
197 return xattr.visual;
201 void X11OpenGLContext::sync()
203 OpenGLZone aZone;
204 glXWaitGL();
205 XSync(m_aGLWin.dpy, false);
208 void X11OpenGLContext::swapBuffers()
210 OpenGLZone aZone;
212 glXSwapBuffers(m_aGLWin.dpy, m_aGLWin.win);
214 BuffersSwapped();
217 void X11OpenGLContext::resetCurrent()
219 clearCurrent();
221 OpenGLZone aZone;
223 if (m_aGLWin.dpy)
225 glXMakeCurrent(m_aGLWin.dpy, None, nullptr);
226 g_bAnyCurrent = false;
230 bool X11OpenGLContext::isCurrent()
232 OpenGLZone aZone;
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*/)
244 OpenGLZone aZone;
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 ) )
255 return aWinData;
257 int best_fbc = -1;
258 GLXFBConfig* pFBC = getFBConfig(dpy, win, best_fbc);
260 if (!pFBC)
261 return aWinData;
263 XVisualInfo* vi = nullptr;
264 if( best_fbc != -1 )
265 vi = glXGetVisualFromFBConfig( dpy, pFBC[best_fbc] );
267 XFree(pFBC);
269 if( vi )
271 VCL_GL_INFO("using VisualID " << vi->visualid);
272 aWinData.pVisual = static_cast<void*>(vi->visual);
275 return aWinData;
278 bool X11OpenGLContext::ImplInit()
280 if (!m_aGLWin.dpy)
281 return false;
283 OpenGLZone aZone;
285 GLXContext pSharedCtx( nullptr );
286 #ifdef DBG_UTIL
287 TempErrorHandler aErrorHandler(m_aGLWin.dpy, unxErrorHandler);
288 #endif
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)
299 int best_fbc = -1;
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,
309 #endif
310 None
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");
316 else
317 SAL_WARN("vcl.opengl", "unable to find correct FBC");
320 if (!m_aGLWin.ctx)
322 if (!m_aGLWin.vi)
323 return false;
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,
329 m_aGLWin.vi,
330 pSharedCtx,
331 GL_TRUE /* direct, not via X server */);
334 if( m_aGLWin.ctx )
336 g_vShareList.push_back( m_aGLWin.ctx );
338 else
340 SAL_WARN("vcl.opengl", "unable to create GLX context");
341 return false;
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");
348 return false;
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);
365 m_aGLWin.Width = 0;
366 m_aGLWin.Height = 0;
368 else
370 m_aGLWin.Width = aWinAttr.width;
371 m_aGLWin.Height = aWinAttr.height;
374 if( m_aGLWin.HasGLXExtension("GLX_SGI_swap_control" ) )
376 // enable vsync
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 );
387 if( errorTriggered )
388 SAL_WARN("vcl.opengl", "error when trying to set swap interval, NVIDIA or Mesa bug?");
389 else
390 VCL_GL_INFO("set swap interval to 1 (enable vsync)");
394 bool bRet = InitGL();
395 InitGLDebugging();
397 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
399 registerAsCurrent();
401 return bRet;
404 void X11OpenGLContext::makeCurrent()
406 if (isCurrent())
407 return;
409 OpenGLZone aZone;
411 clearCurrent();
413 #ifdef DBG_UTIL
414 TempErrorHandler aErrorHandler(m_aGLWin.dpy, unxErrorHandler);
415 #endif
417 if (m_aGLWin.dpy)
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);
424 return;
426 g_bAnyCurrent = true;
429 registerAsCurrent();
432 void X11OpenGLContext::destroyCurrentContext()
434 if(m_aGLWin.ctx)
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)
453 if (isInitialized())
454 return;
456 if (!dpy)
457 return;
459 OpenGLZone aZone;
461 m_aGLWin.dpy = dpy;
462 m_aGLWin.win = win;
463 m_aGLWin.screen = screen;
465 Visual* pVisual = getVisual(dpy, win);
467 initGLWindow(pVisual);
469 ImplInit();
472 void X11OpenGLContext::initGLWindow(Visual* pVisual)
474 OpenGLZone aZone;
476 // Get visual info
478 XVisualInfo aTemplate;
479 aTemplate.visualid = XVisualIDFromVisual( pVisual );
480 int nVisuals = 0;
481 XVisualInfo* pInfo = XGetVisualInfo( m_aGLWin.dpy, VisualIDMask, &aTemplate, &nVisuals );
482 if( nVisuals != 1 )
483 SAL_WARN( "vcl.opengl", "match count for visual id is not 1" );
484 m_aGLWin.vi = pInfo;
487 // Check multisample support
488 /* TODO: moggi: This is not necessarily correct in the DBG_UTIL path, as it picks
489 * an FBConfig instead ... */
490 int nSamples = 0;
491 glXGetConfig(m_aGLWin.dpy, m_aGLWin.vi, GLX_SAMPLES, &nSamples);
492 if( nSamples > 0 )
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)
513 return;
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()
526 : dpy(nullptr)
527 , screen(0)
528 , win(0)
529 , vi(nullptr)
530 , ctx(nullptr)
534 bool GLX11Window::HasGLXExtension( const char* name ) const
536 for (sal_Int32 i = 0; i != -1;) {
537 if (GLXExtensions.getToken(0, ' ', i) == name) {
538 return true;
541 return false;
544 GLX11Window::~GLX11Window()
546 XFree(vi);
549 bool GLX11Window::Synchronize(bool bOnoff) const
551 XSynchronize(dpy, bOnoff);
552 return true;
555 OpenGLContext* X11SalInstance::CreateOpenGLContext()
557 return new X11OpenGLContext;
560 X11OpenGLSalGraphicsImpl::X11OpenGLSalGraphicsImpl( X11SalGraphics& rParent ):
561 OpenGLSalGraphicsImpl(rParent,rParent.GetGeometryProvider()),
562 mrX11Parent(rParent)
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);
581 if( !pProvider )
582 return nullptr;
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: */