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
12#include <thread>
16#include <vcl/syschild.hxx>
17#include <vcl/sysdata.hxx>
18
19#include <osl/thread.hxx>
20#include <sal/log.hxx>
21
22#include <svdata.hxx>
23#include <salgdi.hxx>
24#include <salinst.hxx>
25
26#include <opengl/zone.hxx>
27
28#include <config_features.h>
29
30using namespace com::sun::star;
31
32static sal_Int64 nBufferSwapCounter = 0;
33
35{
36}
37
38bool GLWindow::Synchronize(bool /*bOnoff*/) const
39{
40 return false;
41}
42
44 mpWindow(nullptr),
45 m_pChildWindow(nullptr),
46 mbInitialized(false),
47 mnRefCount(0),
48 mbRequestLegacyContext(false),
49 mpPrevContext(nullptr),
50 mpNextContext(nullptr)
51{
52 VCL_GL_INFO("new context: " << this);
53
54 ImplSVData* pSVData = ImplGetSVData();
55 if( pSVData->maGDIData.mpLastContext )
56 {
57 pSVData->maGDIData.mpLastContext->mpNextContext = this;
59 }
60 pSVData->maGDIData.mpLastContext = this;
61
62 // FIXME: better hope we call 'makeCurrent' soon to preserve
63 // the invariant that the last item is the current context.
64}
65
67{
68 assert (mnRefCount == 0);
69
70 mnRefCount = 1; // guard the shutdown paths.
71 VCL_GL_INFO("delete context: " << this);
72
73 reset();
74
75 ImplSVData* pSVData = ImplGetSVData();
76 if( mpPrevContext )
78 if( mpNextContext )
80 else
82
84 assert (mnRefCount == 1);
85}
86
87// release associated child-window if we have one
89{
90 reset();
92}
93
95{
96 return rtl::Reference<OpenGLContext>(ImplGetSVData()->mpDefInst->CreateOpenGLContext());
97}
98
100{
102}
103
104#ifdef DBG_UTIL
105
106namespace {
107
108const char* getSeverityString(GLenum severity)
109{
110 switch(severity)
111 {
112 case GL_DEBUG_SEVERITY_LOW:
113 return "low";
114 case GL_DEBUG_SEVERITY_MEDIUM:
115 return "medium";
116 case GL_DEBUG_SEVERITY_HIGH:
117 return "high";
118 default:
119 ;
120 }
121
122 return "unknown";
123}
124
125const char* getSourceString(GLenum source)
126{
127 switch(source)
128 {
129 case GL_DEBUG_SOURCE_API:
130 return "API";
131 case GL_DEBUG_SOURCE_SHADER_COMPILER:
132 return "shader compiler";
133 case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
134 return "window system";
135 case GL_DEBUG_SOURCE_THIRD_PARTY:
136 return "third party";
137 case GL_DEBUG_SOURCE_APPLICATION:
138 return "Libreoffice";
139 case GL_DEBUG_SOURCE_OTHER:
140 return "unknown";
141 default:
142 ;
143 }
144
145 return "unknown";
146}
147
148const char* getTypeString(GLenum type)
149{
150 switch(type)
151 {
152 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
153 return "deprecated behavior";
154 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
155 return "undefined behavior";
156 case GL_DEBUG_TYPE_PERFORMANCE:
157 return "performance";
158 case GL_DEBUG_TYPE_PORTABILITY:
159 return "portability";
160 case GL_DEBUG_TYPE_MARKER:
161 return "marker";
162 case GL_DEBUG_TYPE_PUSH_GROUP:
163 return "push group";
164 case GL_DEBUG_TYPE_POP_GROUP:
165 return "pop group";
166 case GL_DEBUG_TYPE_OTHER:
167 return "other";
168 case GL_DEBUG_TYPE_ERROR:
169 return "error";
170 default:
171 ;
172 }
173
174 return "unknown";
175}
176
177extern "C" void
178#if defined _WIN32
179APIENTRY
180#endif
181debug_callback(GLenum source, GLenum type, GLuint id,
182 GLenum severity, GLsizei , const GLchar* message,
183 const GLvoid*)
184{
185 // 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."
186 // the GLSL compiler is a bit too aggressive in optimizing the state based on the current OpenGL state
187
188 // ignore 131185: "Buffer detailed info: Buffer object x (bound to GL_ARRAY_BUFFER_ARB,
189 // usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations."
190 if (id == 131218 || id == 131185)
191 return;
192
193 SAL_WARN("vcl.opengl", "OpenGL debug message: source: " << getSourceString(source) << ", type: "
194 << getTypeString(type) << ", id: " << id << ", severity: " << getSeverityString(severity) << ", with message: " << message);
195}
196
197}
198
199#endif
200
202{
203 if(mbInitialized)
204 return true;
205
206 OpenGLZone aZone;
207
209 mpWindow = pParent ? pParent : m_xWindow.get();
210 if(m_xWindow)
211 m_xWindow->setPosSizePixel(0,0,0,0);
212 //tdf#108069 we may be initted twice, so dispose earlier effort
214 initWindow();
215 return ImplInit();
216}
217
219{
220 VCL_GL_INFO("OpenGLContext not implemented for this platform");
221 return false;
222}
223
224static OUString getGLString(GLenum eGlEnum)
225{
226 OUString sString;
227 const GLubyte* pString = glGetString(eGlEnum);
228 if (pString)
229 {
230 sString = OUString::createFromAscii(reinterpret_cast<const char*>(pString));
231 }
232
234 return sString;
235}
236
238{
239 VCL_GL_INFO("OpenGLContext::ImplInit----end");
240 VCL_GL_INFO("Vendor: " << getGLString(GL_VENDOR) << " Renderer: " << getGLString(GL_RENDERER) << " GL version: " << OpenGLHelper::getGLVersion());
241 mbInitialized = true;
242
243 // I think we need at least GL 3.0
244 if (epoxy_gl_version() < 30)
245 {
246 SAL_WARN("vcl.opengl", "We don't have at least OpenGL 3.0");
247 return false;
248 }
249
250 // Check that some "optional" APIs that we use unconditionally are present
251 if (!glBindFramebuffer)
252 {
253 SAL_WARN("vcl.opengl", "We don't have glBindFramebuffer");
254 return false;
255 }
256
257 return true;
258}
259
261{
262#ifdef DBG_UTIL
263 // only enable debug output in dbgutil build
264 if (epoxy_has_gl_extension("GL_ARB_debug_output"))
265 {
266 OpenGLZone aZone;
267
268 if (glDebugMessageCallbackARB)
269 {
270 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
271 glDebugMessageCallbackARB(&debug_callback, nullptr);
272
273#ifdef GL_DEBUG_SEVERITY_NOTIFICATION_ARB
274 // Ignore i965’s shader compiler notification flood.
275 glDebugMessageControlARB(GL_DEBUG_SOURCE_SHADER_COMPILER_ARB, GL_DEBUG_TYPE_OTHER_ARB, GL_DEBUG_SEVERITY_NOTIFICATION_ARB, 0, nullptr, true);
276#endif
277 }
278 else if ( glDebugMessageCallback )
279 {
280 glEnable(GL_DEBUG_OUTPUT);
281 glDebugMessageCallback(&debug_callback, nullptr);
282
283 // Ignore i965’s shader compiler notification flood.
284 glDebugMessageControl(GL_DEBUG_SOURCE_SHADER_COMPILER, GL_DEBUG_TYPE_OTHER, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, true);
285 }
286 }
287
288 // Test hooks for inserting tracing messages into the stream
289 VCL_GL_INFO("LibreOffice GLContext initialized");
290#endif
291}
292
294{
295 glBindFramebuffer(GL_FRAMEBUFFER, 0);
296}
297
298void OpenGLContext::setWinPosAndSize(const Point &rPos, const Size& rSize)
299{
300 if (m_xWindow)
301 m_xWindow->SetPosSizePixel(rPos, rSize);
302 if (m_pChildWindow)
303 m_pChildWindow->SetPosSizePixel(rPos, rSize);
304
306 rGLWin.Width = rSize.Width();
307 rGLWin.Height = rSize.Height();
309}
310
312{
313 const GLWindow& rGLWin = getOpenGLWindow();
314 glViewport(0, 0, rGLWin.Width, rGLWin.Height);
315}
316
318{
319 pChildWindow->SetMouseTransparent(true);
321 pChildWindow->EnableEraseBackground(false);
322 pChildWindow->SetControlForeground();
323 pChildWindow->SetControlBackground();
324}
325
327{
328}
329
331{
332 //nothing by default
333}
334
336{
337 if( !mbInitialized )
338 return;
339
340 OpenGLZone aZone;
341
342 if( isCurrent() )
343 resetCurrent();
344
345 mbInitialized = false;
346
347 // destroy the context itself
349}
350
351SystemWindowData OpenGLContext::generateWinData(vcl::Window* /*pParent*/, bool /*bRequestLegacyContext*/)
352{
353 return {};
354}
355
357{
358 (void) this; // loplugin:staticmethods
359 return false;
360}
361
363{
364 if (isCurrent())
365 return;
366
367 OpenGLZone aZone;
368
369 clearCurrent();
370
371 // by default nothing else to do
372
374}
375
377{
378 return false;
379}
380
382{
383 ImplSVData* pSVData = ImplGetSVData();
385 return pCurrentCtx.is() && pCurrentCtx->isAnyCurrent();
386}
387
389{
390}
391
393{
394 ImplSVData* pSVData = ImplGetSVData();
395
396 // release all framebuffers from the old context so we can re-attach the
397 // texture in the new context
399
400 if ( !pCurrentCtx.is() )
401 return; // Not using OpenGL
402
403 SAL_INFO("vcl.opengl", "Unbinding contexts in preparation for yield");
404
405 // Find the first context that is current and reset it.
406 // Usually the last context is the current, but not in case a new
407 // OpenGLContext is created already but not yet initialized.
408 while (pCurrentCtx.is())
409 {
410 if (pCurrentCtx->isCurrent())
411 {
412 pCurrentCtx->resetCurrent();
413 break;
414 }
415
416 pCurrentCtx = pCurrentCtx->mpPrevContext;
417 }
418
419 assert (!hasCurrent());
420}
421
423{
424 ImplSVData* pSVData = ImplGetSVData();
425
426 // move the context to the end of the contexts list
427 static int nSwitch = 0;
428 VCL_GL_INFO("******* CONTEXT SWITCH " << ++nSwitch << " *********");
429 if( mpNextContext )
430 {
431 if( mpPrevContext )
434
436 mpNextContext = nullptr;
437 pSVData->maGDIData.mpLastContext->mpNextContext = this;
438 pSVData->maGDIData.mpLastContext = this;
439 }
440}
441
443{
444 clearCurrent();
445 // by default nothing else to do
446}
447
449{
450 // by default nothing else to do
452}
453
455{
457
458 static bool bSleep = getenv("SAL_GL_SLEEP_ON_SWAP");
459 if (bSleep)
460 {
461 // half a second.
462 std::this_thread::sleep_for(std::chrono::milliseconds(500) );
463 }
464}
465
466
467sal_Int64 OpenGLWrapper::getBufferSwapCounter()
468{
469 return nBufferSwapCounter;
470}
471
473{
474 // default is nothing
475 (void) this; // loplugin:staticmethods
476}
477
479{
480 if (m_pChildWindow)
482 else if (m_xWindow)
483 m_xWindow->Show();
484}
485
487{
488 return m_pChildWindow;
489}
490
492{
493 return m_pChildWindow;
494}
495
496/* 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:486
void SetParentClipMode(ParentClipMode nMode=ParentClipMode::NONE)
void SetControlBackground()
Definition: window2.cxx:526
void Show(bool bVisible=true, ShowFlags nFlags=ShowFlags::NONE)
Definition: window.cxx:2187
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:2666
virtual void SetPosSizePixel(const Point &rNewPos, const Size &rNewSize)
Definition: window2.cxx:1294
#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:398
OpenGLContext * mpLastContext
Definition: svdata.hxx:222
static float getGLVersion()
Get OpenGL version (needs a context)
ImplSVData * ImplGetSVData()
Definition: svdata.cxx:77
WinBits const WB_NODIALOGCONTROL
Definition: wintypes.hxx:114
WinBits const WB_NOBORDER
Definition: wintypes.hxx:116
VclPtr< vcl::Window > mpWindow