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
29using namespace com::sun::star;
30
31static sal_Int64 nBufferSwapCounter = 0;
32
34{
35}
36
37bool 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{
95 return rtl::Reference<OpenGLContext>(ImplGetSVData()->mpDefInst->CreateOpenGLContext());
96}
97
99{
101}
102
103#ifdef DBG_UTIL
104
105namespace {
106
107const 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
124const 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
147const 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
176extern "C" void
177#if defined _WIN32
178APIENTRY
179#endif
180debug_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
223static 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
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
297void 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();
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
350SystemWindowData 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
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
466sal_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)
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 OUString getGLString(GLenum eGlEnum)
static sal_Int64 nBufferSwapCounter
#define VCL_GL_INFO(stream)
Helper to do a SAL_INFO as well as a GL log.
#define CHECK_GL_ERROR()
bool mbInitialized
virtual const GLWindow & getOpenGLWindow() const =0
virtual void makeCurrent()
make this GL context current - so it is implicit in subsequent GL calls
VclPtr< SystemChildWindow > m_pChildWindow
virtual void destroyCurrentContext()
OpenGLContext * mpPrevContext
virtual void adjustToNewSize()
static void BuffersSwapped()
virtual void restoreDefaultFramebuffer()
unbind the GL_FRAMEBUFFER to its default state, needed for gtk3
bool init(vcl::Window *pParent)
VclPtr< vcl::Window > m_xWindow
virtual SystemWindowData generateWinData(vcl::Window *pParent, bool bRequestLegacyContext)
bool mbRequestLegacyContext
virtual bool ImplInit()
virtual void initWindow()
virtual bool isAnyCurrent()
Is any GL context the current context ?
VclPtr< vcl::Window > mpWindow
virtual void swapBuffers()
static rtl::Reference< OpenGLContext > Create()
static void InitChildWindow(SystemChildWindow *pChildWindow)
virtual void resetCurrent()
reset the GL context so this context is not implicit in subsequent GL calls.
static void InitGLDebugging()
virtual ~OpenGLContext()
static void prepareForYield()
release contexts etc. before (potentially) allowing another thread run.
void requestLegacyContext()
virtual GLWindow & getModifiableOpenGLWindow()=0
void setWinPosAndSize(const Point &rPos, const Size &rSize)
static bool hasCurrent()
Is there a current GL context ?
virtual void sync()
virtual bool isCurrent()
Is this GL context the current context ?
void registerAsCurrent()
Put this GL context to the end of the context list.
SystemChildWindow * getChildWindow()
OpenGLContext * mpNextContext
static void clearCurrent()
release bound resources from 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
constexpr tools::Long Height() const
constexpr tools::Long Width() const
void EnableEraseBackground(bool bEnable)
Definition: syschild.cxx:138
A thin wrapper around rtl::Reference to implement the acquire and dispose semantics we want for refer...
Definition: vclptr.hxx:58
void disposeAndClear()
Definition: vclptr.hxx:200
void reset(reference_type *pBody)
Definition: vclptr.hxx:153
reference_type * get() const
Get the body.
Definition: vclptr.hxx:143
void SetControlForeground()
Definition: window2.cxx:481
void SetParentClipMode(ParentClipMode nMode=ParentClipMode::NONE)
void SetControlBackground()
Definition: window2.cxx:521
void Show(bool bVisible=true, ShowFlags nFlags=ShowFlags::NONE)
Definition: window.cxx:2189
void SetMouseTransparent(bool bTransparent)
Definition: mouse.cxx:434
virtual void setPosSizePixel(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, PosSizeFlags nFlags=PosSizeFlags::All)
Definition: window.cxx:2668
virtual void SetPosSizePixel(const Point &rNewPos, const Size &rNewSize)
Definition: window2.cxx:1292
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
Holds the information of our new child window.
unsigned int Width
unsigned int Height
virtual ~GLWindow()
virtual bool Synchronize(bool bOnoff) const
ImplSVGDIData maGDIData
Definition: svdata.hxx:401
OpenGLContext * mpLastContext
Definition: svdata.hxx:226
static float getGLVersion()
Get OpenGL version (needs a context)
ImplSVData * ImplGetSVData()
Definition: svdata.cxx:76
WinBits const WB_NODIALOGCONTROL
Definition: wintypes.hxx:114
WinBits const WB_NOBORDER
Definition: wintypes.hxx:116
VclPtr< vcl::Window > mpWindow