LibreOffice Module vcl (master)  1
OpenGLContext.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 <chrono>
11 
15 #include <vcl/syschild.hxx>
16 #include <vcl/sysdata.hxx>
17 
18 #include <osl/thread.hxx>
19 #include <sal/log.hxx>
20 
21 #include <svdata.hxx>
22 #include <salgdi.hxx>
23 #include <salinst.hxx>
24 
25 #include <opengl/zone.hxx>
26 
27 #include <config_features.h>
28 
29 using namespace com::sun::star;
30 
31 static sal_Int64 nBufferSwapCounter = 0;
32 
34 {
35 }
36 
37 bool GLWindow::Synchronize(bool /*bOnoff*/) const
38 {
39  return false;
40 }
41 
43  mpWindow(nullptr),
44  m_pChildWindow(nullptr),
45  mbInitialized(false),
46  mnRefCount(0),
47  mbRequestLegacyContext(false),
48  mpPrevContext(nullptr),
49  mpNextContext(nullptr)
50 {
51  VCL_GL_INFO("new context: " << this);
52 
53  ImplSVData* pSVData = ImplGetSVData();
54  if( pSVData->maGDIData.mpLastContext )
55  {
56  pSVData->maGDIData.mpLastContext->mpNextContext = this;
58  }
59  pSVData->maGDIData.mpLastContext = this;
60 
61  // FIXME: better hope we call 'makeCurrent' soon to preserve
62  // the invariant that the last item is the current context.
63 }
64 
66 {
67  assert (mnRefCount == 0);
68 
69  mnRefCount = 1; // guard the shutdown paths.
70  VCL_GL_INFO("delete context: " << this);
71 
72  reset();
73 
74  ImplSVData* pSVData = ImplGetSVData();
75  if( mpPrevContext )
77  if( mpNextContext )
79  else
81 
83  assert (mnRefCount == 1);
84 }
85 
86 // release associated child-window if we have one
88 {
89  reset();
91 }
92 
94 {
96 }
97 
99 {
100  mbRequestLegacyContext = true;
101 }
102 
103 #ifdef DBG_UTIL
104 
105 namespace {
106 
107 const char* getSeverityString(GLenum severity)
108 {
109  switch(severity)
110  {
111  case GL_DEBUG_SEVERITY_LOW:
112  return "low";
113  case GL_DEBUG_SEVERITY_MEDIUM:
114  return "medium";
115  case GL_DEBUG_SEVERITY_HIGH:
116  return "high";
117  default:
118  ;
119  }
120 
121  return "unknown";
122 }
123 
124 const char* getSourceString(GLenum source)
125 {
126  switch(source)
127  {
128  case GL_DEBUG_SOURCE_API:
129  return "API";
130  case GL_DEBUG_SOURCE_SHADER_COMPILER:
131  return "shader compiler";
132  case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
133  return "window system";
134  case GL_DEBUG_SOURCE_THIRD_PARTY:
135  return "third party";
136  case GL_DEBUG_SOURCE_APPLICATION:
137  return "Libreoffice";
138  case GL_DEBUG_SOURCE_OTHER:
139  return "unknown";
140  default:
141  ;
142  }
143 
144  return "unknown";
145 }
146 
147 const char* getTypeString(GLenum type)
148 {
149  switch(type)
150  {
151  case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
152  return "deprecated behavior";
153  case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
154  return "undefined behavior";
155  case GL_DEBUG_TYPE_PERFORMANCE:
156  return "performance";
157  case GL_DEBUG_TYPE_PORTABILITY:
158  return "portability";
159  case GL_DEBUG_TYPE_MARKER:
160  return "marker";
161  case GL_DEBUG_TYPE_PUSH_GROUP:
162  return "push group";
163  case GL_DEBUG_TYPE_POP_GROUP:
164  return "pop group";
165  case GL_DEBUG_TYPE_OTHER:
166  return "other";
167  case GL_DEBUG_TYPE_ERROR:
168  return "error";
169  default:
170  ;
171  }
172 
173  return "unknown";
174 }
175 
176 extern "C" void
177 #if defined _WIN32
178 APIENTRY
179 #endif
180 debug_callback(GLenum source, GLenum type, GLuint id,
181  GLenum severity, GLsizei , const GLchar* message,
182  const GLvoid*)
183 {
184  // ignore Nvidia's 131218: "Program/shader state performance warning: Fragment Shader is going to be recompiled because the shader key based on GL state mismatches."
185  // the GLSL compiler is a bit too aggressive in optimizing the state based on the current OpenGL state
186 
187  // ignore 131185: "Buffer detailed info: Buffer object x (bound to GL_ARRAY_BUFFER_ARB,
188  // usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations."
189  if (id == 131218 || id == 131185)
190  return;
191 
192  SAL_WARN("vcl.opengl", "OpenGL debug message: source: " << getSourceString(source) << ", type: "
193  << getTypeString(type) << ", id: " << id << ", severity: " << getSeverityString(severity) << ", with message: " << message);
194 }
195 
196 }
197 
198 #endif
199 
201 {
202  if(mbInitialized)
203  return true;
204 
205  OpenGLZone aZone;
206 
208  mpWindow = pParent ? pParent : m_xWindow.get();
209  if(m_xWindow)
210  m_xWindow->setPosSizePixel(0,0,0,0);
211  //tdf#108069 we may be initted twice, so dispose earlier effort
213  initWindow();
214  return ImplInit();
215 }
216 
218 {
219  VCL_GL_INFO("OpenGLContext not implemented for this platform");
220  return false;
221 }
222 
223 static OUString getGLString(GLenum eGlEnum)
224 {
225  OUString sString;
226  const GLubyte* pString = glGetString(eGlEnum);
227  if (pString)
228  {
229  sString = OUString::createFromAscii(reinterpret_cast<const char*>(pString));
230  }
231 
232  CHECK_GL_ERROR();
233  return sString;
234 }
235 
237 {
238  VCL_GL_INFO("OpenGLContext::ImplInit----end");
239  VCL_GL_INFO("Vendor: " << getGLString(GL_VENDOR) << " Renderer: " << getGLString(GL_RENDERER) << " GL version: " << OpenGLHelper::getGLVersion());
240  mbInitialized = true;
241 
242  // I think we need at least GL 3.0
243  if (epoxy_gl_version() < 30)
244  {
245  SAL_WARN("vcl.opengl", "We don't have at least OpenGL 3.0");
246  return false;
247  }
248 
249  // Check that some "optional" APIs that we use unconditionally are present
250  if (!glBindFramebuffer)
251  {
252  SAL_WARN("vcl.opengl", "We don't have glBindFramebuffer");
253  return false;
254  }
255 
256  return true;
257 }
258 
260 {
261 #ifdef DBG_UTIL
262  // only enable debug output in dbgutil build
263  if (epoxy_has_gl_extension("GL_ARB_debug_output"))
264  {
265  OpenGLZone aZone;
266 
267  if (glDebugMessageCallbackARB)
268  {
269  glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
270  glDebugMessageCallbackARB(&debug_callback, nullptr);
271 
272 #ifdef GL_DEBUG_SEVERITY_NOTIFICATION_ARB
273  // Ignore i965’s shader compiler notification flood.
274  glDebugMessageControlARB(GL_DEBUG_SOURCE_SHADER_COMPILER_ARB, GL_DEBUG_TYPE_OTHER_ARB, GL_DEBUG_SEVERITY_NOTIFICATION_ARB, 0, nullptr, true);
275 #endif
276  }
277  else if ( glDebugMessageCallback )
278  {
279  glEnable(GL_DEBUG_OUTPUT);
280  glDebugMessageCallback(&debug_callback, nullptr);
281 
282  // Ignore i965’s shader compiler notification flood.
283  glDebugMessageControl(GL_DEBUG_SOURCE_SHADER_COMPILER, GL_DEBUG_TYPE_OTHER, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, true);
284  }
285  }
286 
287  // Test hooks for inserting tracing messages into the stream
288  VCL_GL_INFO("LibreOffice GLContext initialized");
289 #endif
290 }
291 
293 {
294  glBindFramebuffer(GL_FRAMEBUFFER, 0);
295 }
296 
297 void OpenGLContext::setWinPosAndSize(const Point &rPos, const Size& rSize)
298 {
299  if (m_xWindow)
300  m_xWindow->SetPosSizePixel(rPos, rSize);
301  if (m_pChildWindow)
302  m_pChildWindow->SetPosSizePixel(rPos, rSize);
303 
305  rGLWin.Width = rSize.Width();
306  rGLWin.Height = rSize.Height();
307  adjustToNewSize();
308 }
309 
311 {
312  const GLWindow& rGLWin = getOpenGLWindow();
313  glViewport(0, 0, rGLWin.Width, rGLWin.Height);
314 }
315 
317 {
318  pChildWindow->SetMouseTransparent(true);
320  pChildWindow->EnableEraseBackground(false);
321  pChildWindow->SetControlForeground();
322  pChildWindow->SetControlBackground();
323 }
324 
326 {
327 }
328 
330 {
331  //nothing by default
332 }
333 
335 {
336  if( !mbInitialized )
337  return;
338 
339  OpenGLZone aZone;
340 
341  if( isCurrent() )
342  resetCurrent();
343 
344  mbInitialized = false;
345 
346  // destroy the context itself
348 }
349 
350 SystemWindowData OpenGLContext::generateWinData(vcl::Window* /*pParent*/, bool /*bRequestLegacyContext*/)
351 {
352  return {};
353 }
354 
356 {
357  (void) this; // loplugin:staticmethods
358  return false;
359 }
360 
362 {
363  if (isCurrent())
364  return;
365 
366  OpenGLZone aZone;
367 
368  clearCurrent();
369 
370  // by default nothing else to do
371 
373 }
374 
376 {
377  return false;
378 }
379 
381 {
382  ImplSVData* pSVData = ImplGetSVData();
384  return pCurrentCtx.is() && pCurrentCtx->isAnyCurrent();
385 }
386 
388 {
389 }
390 
392 {
393  ImplSVData* pSVData = ImplGetSVData();
394 
395  // release all framebuffers from the old context so we can re-attach the
396  // texture in the new context
398 
399  if ( !pCurrentCtx.is() )
400  return; // Not using OpenGL
401 
402  SAL_INFO("vcl.opengl", "Unbinding contexts in preparation for yield");
403 
404  // Find the first context that is current and reset it.
405  // Usually the last context is the current, but not in case a new
406  // OpenGLContext is created already but not yet initialized.
407  while (pCurrentCtx.is())
408  {
409  if (pCurrentCtx->isCurrent())
410  {
411  pCurrentCtx->resetCurrent();
412  break;
413  }
414 
415  pCurrentCtx = pCurrentCtx->mpPrevContext;
416  }
417 
418  assert (!hasCurrent());
419 }
420 
422 {
423  ImplSVData* pSVData = ImplGetSVData();
424 
425  // move the context to the end of the contexts list
426  static int nSwitch = 0;
427  VCL_GL_INFO("******* CONTEXT SWITCH " << ++nSwitch << " *********");
428  if( mpNextContext )
429  {
430  if( mpPrevContext )
433 
435  mpNextContext = nullptr;
436  pSVData->maGDIData.mpLastContext->mpNextContext = this;
437  pSVData->maGDIData.mpLastContext = this;
438  }
439 }
440 
442 {
443  clearCurrent();
444  // by default nothing else to do
445 }
446 
448 {
449  // by default nothing else to do
450  BuffersSwapped();
451 }
452 
454 {
456 
457  static bool bSleep = getenv("SAL_GL_SLEEP_ON_SWAP");
458  if (bSleep)
459  {
460  // half a second.
461  osl::Thread::wait( std::chrono::milliseconds(500) );
462  }
463 }
464 
465 
466 sal_Int64 OpenGLWrapper::getBufferSwapCounter()
467 {
468  return nBufferSwapCounter;
469 }
470 
472 {
473  // default is nothing
474  (void) this; // loplugin:staticmethods
475 }
476 
478 {
479  if (m_pChildWindow)
480  m_pChildWindow->Show();
481  else if (m_xWindow)
482  m_xWindow->Show();
483 }
484 
486 {
487  return m_pChildWindow;
488 }
489 
491 {
492  return m_pChildWindow;
493 }
494 
495 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static rtl::Reference< OpenGLContext > Create()
static void clearCurrent()
release bound resources from the current context
virtual void sync()
virtual void restoreDefaultFramebuffer()
unbind the GL_FRAMEBUFFER to its default state, needed for gtk3
void setWinPosAndSize(const Point &rPos, const Size &rSize)
A thin wrapper around rtl::Reference to implement the acquire and dispose semantics we want for refer...
Definition: button.hxx:34
WinBits const WB_NODIALOGCONTROL
OpenGLContext * mpNextContext
void disposeAndClear()
Definition: vclptr.hxx:200
unsigned int Height
static void BuffersSwapped()
bool mbInitialized
void registerAsCurrent()
Put this GL context to the end of the context list.
static void InitChildWindow(SystemChildWindow *pChildWindow)
void SetParentClipMode(ParentClipMode nMode=ParentClipMode::NONE)
static float getGLVersion()
Get OpenGL version (needs a context)
virtual void initWindow()
ImplSVGDIData maGDIData
Definition: svdata.hxx:394
virtual bool isAnyCurrent()
Is any GL context the current context ?
constexpr tools::Long Width() const
void requestLegacyContext()
static OUString getGLString(GLenum eGlEnum)
virtual OpenGLContext * CreateOpenGLContext()
Definition: salvtables.cxx:126
bool mbRequestLegacyContext
void EnableEraseBackground(bool bEnable)
Definition: syschild.cxx:138
virtual GLWindow & getModifiableOpenGLWindow()=0
static sal_Int64 nBufferSwapCounter
virtual bool Synchronize(bool bOnoff) const
ImplSVData * ImplGetSVData()
Definition: svdata.cxx:75
virtual void resetCurrent()
reset the GL context so this context is not implicit in subsequent GL calls.
VclPtr< vcl::Window > mpWindow
Holds the information of our new child window.
#define VCL_GL_INFO(stream)
Helper to do a SAL_INFO as well as a GL log.
static void InitGLDebugging()
virtual ~GLWindow()
void SetControlBackground()
Definition: window2.cxx:491
static bool hasCurrent()
Is there a current GL context ?
void SetControlForeground()
Definition: window2.cxx:451
virtual void adjustToNewSize()
virtual void swapBuffers()
virtual const GLWindow & getOpenGLWindow() const =0
WinBits const WB_NOBORDER
virtual void SetPosSizePixel(const Point &rNewPos, const Size &rNewSize)
Definition: window2.cxx:1262
void reset(reference_type *pBody)
Definition: vclptr.hxx:153
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:22
virtual void makeCurrent()
make this GL context current - so it is implicit in subsequent GL calls
virtual void setPosSizePixel(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, PosSizeFlags nFlags=PosSizeFlags::All)
Definition: window.cxx:2656
OpenGLContext * mpPrevContext
void SetMouseTransparent(bool bTransparent)
Definition: mouse.cxx:433
virtual void destroyCurrentContext()
virtual ~OpenGLContext()
constexpr tools::Long Height() const
unsigned int Width
#define SAL_INFO(area, stream)
VclPtr< vcl::Window > m_xWindow
SystemChildWindow * getChildWindow()
static void prepareForYield()
release contexts etc. before (potentially) allowing another thread run.
reference_type * get() const
Get the body.
Definition: vclptr.hxx:143
#define SAL_WARN(area, stream)
virtual SystemWindowData generateWinData(vcl::Window *pParent, bool bRequestLegacyContext)
#define CHECK_GL_ERROR()
VclPtr< SystemChildWindow > m_pChildWindow
bool init(vcl::Window *pParent)
OpenGLContext * mpLastContext
Definition: svdata.hxx:221
SalInstance * mpDefInst
Definition: svdata.hxx:385
virtual bool isCurrent()
Is this GL context the current context ?
void Show(bool bVisible=true, ShowFlags nFlags=ShowFlags::NONE)
Definition: window.cxx:2177
VclPtr< vcl::Window > mpWindow
virtual bool ImplInit()
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo