LibreOffice Module vcl (master) 1
x11/context.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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 */
9
10#include <memory>
11#include <vcl/lazydelete.hxx>
12#include <vcl/syschild.hxx>
13
14#include <svdata.hxx>
15
16#include <unx/saldisp.hxx>
17#include <unx/salframe.h>
18#include <unx/salgdi.h>
19#include <unx/salinst.h>
20#include <unx/salvd.h>
21#include <unx/x11/xlimits.hxx>
22
23#include <opengl/zone.hxx>
24
27#include <sal/log.hxx>
28#include <o3tl/string_view.hxx>
29
30static std::vector<GLXContext> g_vShareList;
31static bool g_bAnyCurrent;
32
33namespace {
34
35class X11OpenGLContext : public OpenGLContext
36{
37public:
38 virtual void initWindow() override;
39private:
40 GLX11Window m_aGLWin;
41 virtual const GLWindow& getOpenGLWindow() const override { return m_aGLWin; }
42 virtual GLWindow& getModifiableOpenGLWindow() override { return m_aGLWin; }
43 virtual bool ImplInit() override;
44 void initGLWindow(Visual* pVisual);
45 virtual SystemWindowData generateWinData(vcl::Window* pParent, bool bRequestLegacyContext) override;
46 virtual void makeCurrent() override;
47 virtual void destroyCurrentContext() override;
48 virtual bool isCurrent() override;
49 virtual bool isAnyCurrent() override;
50 virtual void sync() override;
51 virtual void resetCurrent() override;
52 virtual void swapBuffers() override;
53};
54
55#ifdef DBG_UTIL
56 int unxErrorHandler(Display* dpy, XErrorEvent* event)
57 {
58 char err[256];
59 char req[256];
60 char minor[256];
61 XGetErrorText(dpy, event->error_code, err, 256);
62 XGetErrorText(dpy, event->request_code, req, 256);
63 XGetErrorText(dpy, event->minor_code, minor, 256);
64 SAL_WARN("vcl.opengl", "Error: " << err << ", Req: " << req << ", Minor: " << minor);
65 return 0;
66 }
67#endif
68
69 typedef int (*errorHandler)(Display* /*dpy*/, XErrorEvent* /*evnt*/);
70
71 class TempErrorHandler
72 {
73 private:
74 errorHandler oldErrorHandler;
75 Display* mdpy;
76
77 public:
78 TempErrorHandler(Display* dpy, errorHandler newErrorHandler)
79 : oldErrorHandler(nullptr)
80 , mdpy(dpy)
81 {
82 if (mdpy)
83 {
84 XLockDisplay(dpy);
85 XSync(dpy, false);
86 oldErrorHandler = XSetErrorHandler(newErrorHandler);
87 }
88 }
89
90 ~TempErrorHandler()
91 {
92 if (mdpy)
93 {
94 // sync so that we possibly get an XError
95 glXWaitGL();
96 XSync(mdpy, false);
97 XSetErrorHandler(oldErrorHandler);
98 XUnlockDisplay(mdpy);
99 }
100 }
101 };
102
103 bool errorTriggered;
104 int oglErrorHandler( Display* /*dpy*/, XErrorEvent* /*evnt*/ )
105 {
106 errorTriggered = true;
107
108 return 0;
109 }
110
111 GLXFBConfig* getFBConfig(Display* dpy, Window win, int& nBestFBC)
112 {
113 OpenGLZone aZone;
114
115 if( dpy == nullptr || !glXQueryExtension( dpy, nullptr, nullptr ) )
116 return nullptr;
117
118 VCL_GL_INFO("window: " << win);
119
120 XWindowAttributes xattr;
121 if( !XGetWindowAttributes( dpy, win, &xattr ) )
122 {
123 SAL_WARN("vcl.opengl", "Failed to get window attributes for fbconfig " << win);
124 xattr.screen = nullptr;
125 xattr.visual = nullptr;
126 }
127
128 int screen = XScreenNumberOfScreen( xattr.screen );
129
130 // TODO: moggi: Select colour channel depth based on visual attributes, not hardcoded */
131 static int visual_attribs[] =
132 {
133 GLX_DOUBLEBUFFER, True,
134 GLX_X_RENDERABLE, True,
135 GLX_RED_SIZE, 8,
136 GLX_GREEN_SIZE, 8,
137 GLX_BLUE_SIZE, 8,
138 GLX_ALPHA_SIZE, 8,
139 GLX_DEPTH_SIZE, 24,
140 GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
141 None
142 };
143
144 int fbCount = 0;
145 GLXFBConfig* pFBC = glXChooseFBConfig( dpy,
146 screen,
147 visual_attribs, &fbCount );
148
149 if(!pFBC)
150 {
151 SAL_WARN("vcl.opengl", "no suitable fb format found");
152 return nullptr;
153 }
154
155 int best_num_samp = -1;
156 for(int i = 0; i < fbCount; ++i)
157 {
158 XVisualInfo* pVi = glXGetVisualFromFBConfig( dpy, pFBC[i] );
159 if(pVi && (xattr.visual && pVi->visualid == xattr.visual->visualid) )
160 {
161 // pick the one with the most samples per pixel
162 int nSampleBuf = 0;
163 int nSamples = 0;
164 glXGetFBConfigAttrib( dpy, pFBC[i], GLX_SAMPLE_BUFFERS, &nSampleBuf );
165 glXGetFBConfigAttrib( dpy, pFBC[i], GLX_SAMPLES , &nSamples );
166
167 if ( nBestFBC < 0 || (nSampleBuf && ( nSamples > best_num_samp )) )
168 {
169 nBestFBC = i;
170 best_num_samp = nSamples;
171 }
172 }
173 XFree( pVi );
174 }
175
176 return pFBC;
177 }
178}
179
180void X11OpenGLContext::sync()
181{
182 OpenGLZone aZone;
183 glXWaitGL();
184 XSync(m_aGLWin.dpy, false);
185}
186
187void X11OpenGLContext::swapBuffers()
188{
189 OpenGLZone aZone;
190
191 glXSwapBuffers(m_aGLWin.dpy, m_aGLWin.win);
192
193 BuffersSwapped();
194}
195
196void X11OpenGLContext::resetCurrent()
197{
198 clearCurrent();
199
200 OpenGLZone aZone;
201
202 if (m_aGLWin.dpy)
203 {
204 glXMakeCurrent(m_aGLWin.dpy, None, nullptr);
205 g_bAnyCurrent = false;
206 }
207}
208
209bool X11OpenGLContext::isCurrent()
210{
211 OpenGLZone aZone;
212 return g_bAnyCurrent && m_aGLWin.ctx && glXGetCurrentContext() == m_aGLWin.ctx &&
213 glXGetCurrentDrawable() == m_aGLWin.win;
214}
215
216bool X11OpenGLContext::isAnyCurrent()
217{
218 return g_bAnyCurrent && glXGetCurrentContext() != None;
219}
220
221SystemWindowData X11OpenGLContext::generateWinData(vcl::Window* pParent, bool /*bRequestLegacyContext*/)
222{
223 OpenGLZone aZone;
224
225 SystemWindowData aWinData;
226 aWinData.pVisual = nullptr;
227 aWinData.bClipUsingNativeWidget = false;
228
229 const SystemEnvData* sysData(pParent->GetSystemData());
230
231 Display *dpy = static_cast<Display*>(sysData->pDisplay);
232 Window win = sysData->GetWindowHandle(pParent->ImplGetFrame());
233
234 if( dpy == nullptr || !glXQueryExtension( dpy, nullptr, nullptr ) )
235 return aWinData;
236
237 int best_fbc = -1;
238 GLXFBConfig* pFBC = getFBConfig(dpy, win, best_fbc);
239
240 if (!pFBC)
241 return aWinData;
242
243 XVisualInfo* vi = nullptr;
244 if( best_fbc != -1 )
245 vi = glXGetVisualFromFBConfig( dpy, pFBC[best_fbc] );
246
247 XFree(pFBC);
248
249 if( vi )
250 {
251 VCL_GL_INFO("using VisualID " << vi->visualid);
252 aWinData.pVisual = static_cast<void*>(vi->visual);
253 }
254
255 return aWinData;
256}
257
258bool X11OpenGLContext::ImplInit()
259{
260 if (!m_aGLWin.dpy)
261 return false;
262
263 OpenGLZone aZone;
264
265 GLXContext pSharedCtx( nullptr );
266#ifdef DBG_UTIL
267 TempErrorHandler aErrorHandler(m_aGLWin.dpy, unxErrorHandler);
268#endif
269
270 VCL_GL_INFO("OpenGLContext::ImplInit----start");
271
272 if (!g_vShareList.empty())
273 pSharedCtx = g_vShareList.front();
274
275 //tdf#112166 for, e.g. VirtualBox GL, claiming OpenGL 2.1
276 static bool hasCreateContextAttribsARB = glXGetProcAddress(reinterpret_cast<const GLubyte*>("glXCreateContextAttribsARB")) != nullptr;
277 if (hasCreateContextAttribsARB && !mbRequestLegacyContext)
278 {
279 int best_fbc = -1;
280 GLXFBConfig* pFBC = getFBConfig(m_aGLWin.dpy, m_aGLWin.win, best_fbc);
281
282 if (pFBC && best_fbc != -1)
283 {
284 int const pContextAttribs[] =
285 {
286#if 0 // defined(DBG_UTIL)
287 GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
288 GLX_CONTEXT_MINOR_VERSION_ARB, 2,
289#endif
290 None
291
292 };
293 m_aGLWin.ctx = glXCreateContextAttribsARB(m_aGLWin.dpy, pFBC[best_fbc], pSharedCtx, /* direct, not via X */ GL_TRUE, pContextAttribs);
294 SAL_INFO_IF(m_aGLWin.ctx, "vcl.opengl", "created a 3.2 core context");
295 }
296 else
297 SAL_WARN("vcl.opengl", "unable to find correct FBC");
298 }
299
300 if (!m_aGLWin.ctx)
301 {
302 if (!m_aGLWin.vi)
303 return false;
304
305 SAL_WARN("vcl.opengl", "attempting to create a non-double-buffered "
306 "visual matching the context");
307
308 m_aGLWin.ctx = glXCreateContext(m_aGLWin.dpy,
309 m_aGLWin.vi,
310 pSharedCtx,
311 GL_TRUE /* direct, not via X server */);
312 }
313
314 if( m_aGLWin.ctx )
315 {
316 g_vShareList.push_back( m_aGLWin.ctx );
317 }
318 else
319 {
320 SAL_WARN("vcl.opengl", "unable to create GLX context");
321 return false;
322 }
323
324 if( !glXMakeCurrent( m_aGLWin.dpy, m_aGLWin.win, m_aGLWin.ctx ) )
325 {
326 g_bAnyCurrent = false;
327 SAL_WARN("vcl.opengl", "unable to select current GLX context");
328 return false;
329 }
330
331 g_bAnyCurrent = true;
332
333 int glxMinor, glxMajor;
334 double nGLXVersion = 0;
335 if( glXQueryVersion( m_aGLWin.dpy, &glxMajor, &glxMinor ) )
336 nGLXVersion = glxMajor + 0.1*glxMinor;
337 SAL_INFO("vcl.opengl", "available GLX version: " << nGLXVersion);
338
339 SAL_INFO("vcl.opengl", "available GL extensions: " << glGetString(GL_EXTENSIONS));
340
341 XWindowAttributes aWinAttr;
342 if( !XGetWindowAttributes( m_aGLWin.dpy, m_aGLWin.win, &aWinAttr ) )
343 {
344 SAL_WARN("vcl.opengl", "Failed to get window attributes on " << m_aGLWin.win);
345 m_aGLWin.Width = 0;
346 m_aGLWin.Height = 0;
347 }
348 else
349 {
350 m_aGLWin.Width = aWinAttr.width;
351 m_aGLWin.Height = aWinAttr.height;
352 }
353
354 if( m_aGLWin.HasGLXExtension("GLX_SGI_swap_control" ) )
355 {
356 // enable vsync
357 typedef GLint (*glXSwapIntervalProc)(GLint);
358 glXSwapIntervalProc glXSwapInterval = reinterpret_cast<glXSwapIntervalProc>(glXGetProcAddress( reinterpret_cast<const GLubyte*>("glXSwapIntervalSGI") ));
359 if( glXSwapInterval )
360 {
361 TempErrorHandler aLocalErrorHandler(m_aGLWin.dpy, oglErrorHandler);
362
363 errorTriggered = false;
364
365 glXSwapInterval( 1 );
366
367 if( errorTriggered )
368 SAL_WARN("vcl.opengl", "error when trying to set swap interval, NVIDIA or Mesa bug?");
369 else
370 VCL_GL_INFO("set swap interval to 1 (enable vsync)");
371 }
372 }
373
374 bool bRet = InitGL();
375 InitGLDebugging();
376
377 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
378
379 registerAsCurrent();
380
381 return bRet;
382}
383
384void X11OpenGLContext::makeCurrent()
385{
386 if (isCurrent())
387 return;
388
389 OpenGLZone aZone;
390
391 clearCurrent();
392
393#ifdef DBG_UTIL
394 TempErrorHandler aErrorHandler(m_aGLWin.dpy, unxErrorHandler);
395#endif
396
397 if (m_aGLWin.dpy)
398 {
399 if (!glXMakeCurrent( m_aGLWin.dpy, m_aGLWin.win, m_aGLWin.ctx ))
400 {
401 g_bAnyCurrent = false;
402 SAL_WARN("vcl.opengl", "OpenGLContext::makeCurrent failed "
403 "on drawable " << m_aGLWin.win);
404 return;
405 }
406 g_bAnyCurrent = true;
407 }
408
409 registerAsCurrent();
410}
411
412void X11OpenGLContext::destroyCurrentContext()
413{
414 if(!m_aGLWin.ctx)
415 return;
416
417 std::vector<GLXContext>::iterator itr = std::remove( g_vShareList.begin(), g_vShareList.end(), m_aGLWin.ctx );
418 if (itr != g_vShareList.end())
419 g_vShareList.erase(itr);
420
421 glXMakeCurrent(m_aGLWin.dpy, None, nullptr);
422 g_bAnyCurrent = false;
423 if( glGetError() != GL_NO_ERROR )
424 {
425 SAL_WARN("vcl.opengl", "glError: " << glGetError());
426 }
427 glXDestroyContext(m_aGLWin.dpy, m_aGLWin.ctx);
428 m_aGLWin.ctx = nullptr;
429}
430
431void X11OpenGLContext::initGLWindow(Visual* pVisual)
432{
433 OpenGLZone aZone;
434
435 // Get visual info
436 {
437 XVisualInfo aTemplate;
438 aTemplate.visualid = XVisualIDFromVisual( pVisual );
439 int nVisuals = 0;
440 XVisualInfo* pInfo = XGetVisualInfo( m_aGLWin.dpy, VisualIDMask, &aTemplate, &nVisuals );
441 if( nVisuals != 1 )
442 SAL_WARN( "vcl.opengl", "match count for visual id is not 1" );
443 m_aGLWin.vi = pInfo;
444 }
445
446 // Check multisample support
447 /* TODO: moggi: This is not necessarily correct in the DBG_UTIL path, as it picks
448 * an FBConfig instead ... */
449 int nSamples = 0;
450 glXGetConfig(m_aGLWin.dpy, m_aGLWin.vi, GLX_SAMPLES, &nSamples);
451 if( nSamples > 0 )
452 m_aGLWin.bMultiSampleSupported = true;
453
454 m_aGLWin.GLXExtensions = glXQueryExtensionsString( m_aGLWin.dpy, m_aGLWin.screen );
455 SAL_INFO("vcl.opengl", "available GLX extensions: " << m_aGLWin.GLXExtensions);
456}
457
458void X11OpenGLContext::initWindow()
459{
460 const SystemEnvData* pChildSysData = nullptr;
461 SystemWindowData winData = generateWinData(mpWindow, false);
462 if( winData.pVisual )
463 {
464 if( !m_pChildWindow )
465 {
466 m_pChildWindow = VclPtr<SystemChildWindow>::Create(mpWindow, 0, &winData, false);
467 }
468 pChildSysData = m_pChildWindow->GetSystemData();
469 }
470
471 if (!m_pChildWindow || !pChildSysData)
472 return;
473
474 InitChildWindow(m_pChildWindow.get());
475
476 m_aGLWin.dpy = static_cast<Display*>(pChildSysData->pDisplay);
477 m_aGLWin.win = pChildSysData->GetWindowHandle(m_pChildWindow->ImplGetFrame());
478 m_aGLWin.screen = pChildSysData->nScreen;
479
480 Visual* pVisual = static_cast<Visual*>(pChildSysData->pVisual);
481 initGLWindow(pVisual);
482}
483
485 : dpy(nullptr)
486 , screen(0)
487 , win(0)
488 , vi(nullptr)
489 , ctx(nullptr)
490{
491}
492
493bool GLX11Window::HasGLXExtension( const char* name ) const
494{
495 for (sal_Int32 i = 0; i != -1;) {
496 if (o3tl::getToken(GLXExtensions, 0, ' ', i) == name) {
497 return true;
498 }
499 }
500 return false;
501}
502
504{
505 XFree(vi);
506}
507
508bool GLX11Window::Synchronize(bool bOnoff) const
509{
510 XSynchronize(dpy, bOnoff);
511 return true;
512}
513
515{
516 return new X11OpenGLContext;
517}
518
519/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define VCL_GL_INFO(stream)
Helper to do a SAL_INFO as well as a GL log.
virtual bool Synchronize(bool bOnoff) const override
Display * dpy
Definition: saldisp.hxx:214
virtual ~GLX11Window() override
bool HasGLXExtension(const char *name) const
XVisualInfo * vi
Definition: saldisp.hxx:217
OString GLXExtensions
Definition: saldisp.hxx:219
virtual const GLWindow & getOpenGLWindow() const =0
virtual void makeCurrent()
make this GL context current - so it is implicit in subsequent GL calls
virtual void destroyCurrentContext()
virtual SystemWindowData generateWinData(vcl::Window *pParent, bool bRequestLegacyContext)
virtual bool ImplInit()
virtual void initWindow()
virtual bool isAnyCurrent()
Is any GL context the current context ?
virtual void swapBuffers()
virtual void resetCurrent()
reset the GL context so this context is not implicit in subsequent GL calls.
virtual GLWindow & getModifiableOpenGLWindow()=0
virtual void sync()
virtual bool isCurrent()
Is this GL context the current context ?
We want to be able to detect if a given crash came from the OpenGL code, so use this helper to track ...
Definition: opengl/zone.hxx:23
static VclPtr< reference_type > Create(Arg &&... arg)
A construction helper for VclPtr.
Definition: vclptr.hxx:127
virtual OpenGLContext * CreateOpenGLContext() override
SalFrame * ImplGetFrame() const
Definition: window2.cxx:867
virtual const SystemEnvData * GetSystemData() const
Definition: window.cxx:3853
const char * name
#define SAL_INFO_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
err
int i
ctx
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
None
Display
const wchar_t *typedef int(__stdcall *DllNativeUnregProc)(int
Holds the information of our new child window.
sal_uIntPtr GetWindowHandle(const SalFrame *pReference) const
Definition: syschild.cxx:192
void * pVisual
Definition: sysdata.hxx:70
void * pDisplay
Definition: sysdata.hxx:67
bool bClipUsingNativeWidget
Definition: sysdata.hxx:195
static std::vector< GLXContext > g_vShareList
Definition: x11/context.cxx:30
static bool g_bAnyCurrent
Definition: x11/context.cxx:31