1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 #include <vcl/opengl/GLMHelper.hxx>
11 #include <vcl/opengl/OpenGLHelper.hxx>
13 #include <osl/file.hxx>
14 #include <osl/mutex.hxx>
15 #include <rtl/bootstrap.hxx>
16 #include <rtl/digest.h>
17 #include <rtl/strbuf.hxx>
18 #include <rtl/ustring.hxx>
19 #include <config_folders.h>
20 #include <vcl/salbtype.hxx>
21 #include <vcl/bmpacc.hxx>
22 #include <boost/scoped_array.hpp>
23 #include <vcl/pngwrite.hxx>
24 #include <vcl/graph.hxx>
25 #include <vcl/svapp.hxx>
26 #include <officecfg/Office/Common.hxx>
27 #include <com/sun/star/util/XFlushable.hpp>
28 #include <com/sun/star/configuration/theDefaultProvider.hpp>
33 #include <unordered_map>
37 #include "salinst.hxx"
38 #include "opengl/zone.hxx"
39 #include "opengl/watchdog.hxx"
40 #include <osl/conditn.h>
41 #include <vcl/opengl/OpenGLWrapper.hxx>
42 #include <vcl/opengl/OpenGLContext.hxx>
44 #if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID
45 #include "opengl/x11/X11DeviceInfo.hxx"
46 #elif defined (_WIN32)
47 #include "opengl/win/WinDeviceInfo.hxx"
50 static bool volatile gbInShaderCompile
= false;
51 sal_uInt64
volatile OpenGLZone::gnEnterCount
= 0;
52 sal_uInt64
volatile OpenGLZone::gnLeaveCount
= 0;
58 OUString
getShaderFolder()
60 OUString
aUrl("$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
);
61 rtl::Bootstrap::expandMacros(aUrl
);
63 return aUrl
+ "/opengl/";
66 OString
loadShader(const OUString
& rFilename
)
68 OUString aFileURL
= getShaderFolder() + rFilename
+".glsl";
69 osl::File
aFile(aFileURL
);
70 SAL_INFO("vcl.opengl", "Reading " << aFileURL
);
71 if(aFile
.open(osl_File_OpenFlag_Read
) == osl::FileBase::E_None
)
75 boost::scoped_array
<char> content(new char[nSize
+1]);
76 sal_uInt64 nBytesRead
= 0;
77 aFile
.read(content
.get(), nSize
, nBytesRead
);
78 assert(nSize
== nBytesRead
);
79 content
.get()[nBytesRead
] = 0;
80 return OString(content
.get());
84 SAL_WARN("vcl.opengl", "could not load the file: " << aFileURL
);
90 OString
& getShaderSource(const OUString
& rFilename
)
92 static std::unordered_map
<OUString
, OString
, OUStringHash
> aMap
;
93 static osl::Mutex aMutex
;
94 osl::MutexGuard
aGuard(aMutex
);
96 if (aMap
.find(rFilename
) == aMap
.end())
98 aMap
[rFilename
] = loadShader(rFilename
);
101 return aMap
[rFilename
];
107 int LogCompilerError(GLuint nId
, const rtl::OUString
&rDetail
,
108 const rtl::OUString
&rName
, bool bShaderNotProgram
)
112 int InfoLogLength
= 0;
116 if (bShaderNotProgram
)
117 glGetShaderiv (nId
, GL_INFO_LOG_LENGTH
, &InfoLogLength
);
119 glGetProgramiv(nId
, GL_INFO_LOG_LENGTH
, &InfoLogLength
);
123 if ( InfoLogLength
> 0 )
125 std::vector
<char> ErrorMessage(InfoLogLength
+1);
126 if (bShaderNotProgram
)
127 glGetShaderInfoLog (nId
, InfoLogLength
, NULL
, &ErrorMessage
[0]);
129 glGetProgramInfoLog(nId
, InfoLogLength
, NULL
, &ErrorMessage
[0]);
132 ErrorMessage
.push_back('\0');
133 SAL_WARN("vcl.opengl", rDetail
<< " shader " << nId
<< " compile for " << rName
<< " failed : " << &ErrorMessage
[0]);
136 SAL_WARN("vcl.opengl", rDetail
<< " shader: " << rName
<< " compile " << nId
<< " failed without error log");
141 static void addPreamble(OString
& rShaderSource
, const OString
& rPreamble
)
143 if (rPreamble
.isEmpty())
146 OString
aVersionStr("#version");
147 int nVersionStrStartPos
= rShaderSource
.indexOf(aVersionStr
, 0);
149 if (nVersionStrStartPos
== -1)
151 rShaderSource
= rPreamble
+ "\n" + rShaderSource
;
155 int nVersionStrEndPos
= rShaderSource
.indexOf('\n', nVersionStrStartPos
);
157 SAL_WARN_IF(nVersionStrEndPos
== -1, "vcl.opengl", "syntax error in shader");
159 if (nVersionStrEndPos
== -1)
160 nVersionStrEndPos
= nVersionStrStartPos
+ 8;
162 OString aVersionLine
= rShaderSource
.copy(0, nVersionStrEndPos
- nVersionStrStartPos
);
163 OString aShaderBody
= rShaderSource
.copy(nVersionStrEndPos
- nVersionStrStartPos
);
165 rShaderSource
= aVersionLine
+ "\n" + rPreamble
+ "\n" + aShaderBody
;
171 static const sal_uInt32 GLenumSize
= sizeof(GLenum
);
173 OString
getHexString(const sal_uInt8
* pData
, sal_uInt32 nLength
)
175 static const char* pHexData
= "0123456789ABCDEF";
178 OStringBuffer aHexStr
;
179 for(size_t i
= 0; i
< nLength
; ++i
)
181 sal_uInt8 val
= pData
[i
];
184 aHexStr
.append( pHexData
[ val
& 0xf ] );
185 aHexStr
.append( pHexData
[ val
>> 4 ] );
190 return aHexStr
.makeStringAndClear();
193 OString
generateMD5(const void* pData
, size_t length
)
195 sal_uInt8 pBuffer
[RTL_DIGEST_LENGTH_MD5
];
196 rtlDigestError aError
= rtl_digest_MD5(pData
, length
,
197 pBuffer
, RTL_DIGEST_LENGTH_MD5
);
198 SAL_WARN_IF(aError
!= rtl_Digest_E_None
, "vcl.opengl", "md5 generation failed");
200 return getHexString(pBuffer
, RTL_DIGEST_LENGTH_MD5
);
203 OString
getStringDigest( const OUString
& rVertexShaderName
,
204 const OUString
& rFragmentShaderName
,
205 const OString
& rPreamble
)
207 // read shaders source
208 OString aVertexShaderSource
= getShaderSource( rVertexShaderName
);
209 OString aFragmentShaderSource
= getShaderSource( rFragmentShaderName
);
211 // get info about the graphic device
212 #if defined( SAL_UNX ) && !defined( MACOSX ) && !defined( IOS )&& !defined( ANDROID )
213 static const X11OpenGLDeviceInfo aInfo
;
214 static const OString
aDeviceInfo (
216 aInfo
.GetOSRelease() +
217 aInfo
.GetRenderer() +
219 aInfo
.GetVersion() );
220 #elif defined( _WIN32 )
221 static const WinOpenGLDeviceInfo aInfo
;
222 static const OString
aDeviceInfo (
223 OUStringToOString( aInfo
.GetAdapterVendorID(), RTL_TEXTENCODING_UTF8
) +
224 OUStringToOString( aInfo
.GetAdapterDeviceID(), RTL_TEXTENCODING_UTF8
) +
225 OUStringToOString( aInfo
.GetDriverVersion(), RTL_TEXTENCODING_UTF8
) +
226 OString::number( aInfo
.GetWindowsVersion() ) );
228 static const OString
aDeviceInfo (
229 OString( (const char*)(glGetString(GL_VENDOR
)) ) +
230 OString( (const char*)(glGetString(GL_RENDERER
)) ) +
231 OString( (const char*)(glGetString(GL_VERSION
)) ) );
235 aMessage
+= rPreamble
;
236 aMessage
+= aVertexShaderSource
;
237 aMessage
+= aFragmentShaderSource
;
238 aMessage
+= aDeviceInfo
;
240 return generateMD5(aMessage
.getStr(), aMessage
.getLength());
243 OString
getCacheFolder()
245 OUString
url("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
"/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache/");
246 rtl::Bootstrap::expandMacros(url
);
248 osl::Directory::create(url
);
250 return rtl::OUStringToOString(url
, RTL_TEXTENCODING_UTF8
);
254 bool writeProgramBinary( const OString
& rBinaryFileName
,
255 const std::vector
<sal_uInt8
>& rBinary
)
257 osl::File
aFile(rtl::OStringToOUString(rBinaryFileName
, RTL_TEXTENCODING_UTF8
));
258 osl::FileBase::RC eStatus
= aFile
.open(
259 osl_File_OpenFlag_Write
| osl_File_OpenFlag_Create
);
261 if( eStatus
!= osl::FileBase::E_None
)
263 // when file already exists we do not have to save it:
264 // we can be sure that the binary to save is exactly equal
265 // to the already saved binary, since they have the same hash value
266 if( eStatus
== osl::FileBase::E_EXIST
)
268 SAL_WARN( "vcl.opengl",
269 "No binary program saved. A file with the same hash already exists: '" << rBinaryFileName
<< "'" );
275 sal_uInt64 nBytesWritten
= 0;
276 aFile
.write( rBinary
.data(), rBinary
.size(), nBytesWritten
);
278 assert( rBinary
.size() == nBytesWritten
);
283 bool readProgramBinary( const OString
& rBinaryFileName
,
284 std::vector
<sal_uInt8
>& rBinary
)
286 osl::File
aFile( rtl::OStringToOUString( rBinaryFileName
, RTL_TEXTENCODING_UTF8
) );
287 if(aFile
.open( osl_File_OpenFlag_Read
) == osl::FileBase::E_None
)
289 sal_uInt64 nSize
= 0;
290 aFile
.getSize( nSize
);
291 rBinary
.resize( nSize
);
292 sal_uInt64 nBytesRead
= 0;
293 aFile
.read( rBinary
.data(), nSize
, nBytesRead
);
294 assert( nSize
== nBytesRead
);
295 SAL_WARN("vcl.opengl", "Loading file: '" << rBinaryFileName
<< "': success" );
300 SAL_WARN("vcl.opengl", "Loading file: '" << rBinaryFileName
<< "': FAIL");
306 OString
createFileName( const OUString
& rVertexShaderName
,
307 const OUString
& rFragmentShaderName
,
308 const OString
& rDigest
)
311 aFileName
+= getCacheFolder();
312 aFileName
+= rtl::OUStringToOString( rVertexShaderName
, RTL_TEXTENCODING_UTF8
) + "-";
313 aFileName
+= rtl::OUStringToOString( rFragmentShaderName
, RTL_TEXTENCODING_UTF8
) + "-";
314 aFileName
+= rDigest
+ ".bin";
318 GLint
loadProgramBinary( GLuint nProgramID
, const OString
& rBinaryFileName
)
320 GLint nResult
= GL_FALSE
;
321 GLenum nBinaryFormat
;
322 std::vector
<sal_uInt8
> aBinary
;
323 if( readProgramBinary( rBinaryFileName
, aBinary
) && aBinary
.size() > GLenumSize
)
325 GLint nBinaryLength
= aBinary
.size() - GLenumSize
;
327 // Extract binary format
328 sal_uInt8
* pBF
= (sal_uInt8
*)(&nBinaryFormat
);
329 for( size_t i
= 0; i
< GLenumSize
; ++i
)
331 pBF
[i
] = aBinary
[nBinaryLength
+ i
];
335 glProgramBinary( nProgramID
, nBinaryFormat
, (void*)(aBinary
.data()), nBinaryLength
);
338 glGetProgramiv(nProgramID
, GL_LINK_STATUS
, &nResult
);
343 void saveProgramBinary( GLint nProgramID
, const OString
& rBinaryFileName
)
345 GLint nBinaryLength
= 0;
346 GLenum nBinaryFormat
= GL_NONE
;
348 glGetProgramiv( nProgramID
, GL_PROGRAM_BINARY_LENGTH
, &nBinaryLength
);
349 if( !( nBinaryLength
> 0 ) )
351 SAL_WARN( "vcl.opengl", "Binary size is zero" );
355 std::vector
<sal_uInt8
> aBinary( nBinaryLength
+ GLenumSize
);
357 glGetProgramBinary( nProgramID
, nBinaryLength
, NULL
, &nBinaryFormat
, (void*)(aBinary
.data()) );
359 const sal_uInt8
* pBF
= (const sal_uInt8
*)(&nBinaryFormat
);
360 aBinary
.insert( aBinary
.end(), pBF
, pBF
+ GLenumSize
);
362 SAL_INFO("vcl.opengl", "Program id: " << nProgramID
);
363 SAL_INFO("vcl.opengl", "Binary length: " << nBinaryLength
);
364 SAL_INFO("vcl.opengl", "Binary format: " << nBinaryFormat
);
366 if( !writeProgramBinary( rBinaryFileName
, aBinary
) )
367 SAL_WARN("vcl.opengl", "Writing binary file '" << rBinaryFileName
<< "': FAIL");
369 SAL_WARN("vcl.opengl", "Writing binary file '" << rBinaryFileName
<< "': success");
373 rtl::OString
OpenGLHelper::GetDigest( const OUString
& rVertexShaderName
,
374 const OUString
& rFragmentShaderName
,
375 const OString
& rPreamble
)
377 return getStringDigest(rVertexShaderName
, rFragmentShaderName
, rPreamble
);
380 GLint
OpenGLHelper::LoadShaders(const OUString
& rVertexShaderName
,
381 const OUString
& rFragmentShaderName
,
382 const OString
& preamble
,
383 const OString
& rDigest
)
387 gbInShaderCompile
= true;
389 // create the program object
390 GLint ProgramID
= glCreateProgram();
392 // read shaders from file
393 OString aVertexShaderSource
= getShaderSource(rVertexShaderName
);
394 OString aFragmentShaderSource
= getShaderSource(rFragmentShaderName
);
397 GLint bBinaryResult
= GL_FALSE
;
398 if( GLEW_ARB_get_program_binary
&& !rDigest
.isEmpty() )
401 createFileName(rVertexShaderName
, rFragmentShaderName
, rDigest
);
402 bBinaryResult
= loadProgramBinary(ProgramID
, aFileName
);
403 VCL_GL_INFO("vcl.opengl", "Load binary shader from '" << aFileName
<< "'" << bBinaryResult
);
407 if( bBinaryResult
!= GL_FALSE
)
410 VCL_GL_INFO("vcl.opengl", "Load shader: vertex " << rVertexShaderName
<< " fragment " << rFragmentShaderName
);
411 // Create the shaders
412 GLuint VertexShaderID
= glCreateShader(GL_VERTEX_SHADER
);
413 GLuint FragmentShaderID
= glCreateShader(GL_FRAGMENT_SHADER
);
415 GLint Result
= GL_FALSE
;
417 // Compile Vertex Shader
418 if( !preamble
.isEmpty())
419 addPreamble( aVertexShaderSource
, preamble
);
420 char const * VertexSourcePointer
= aVertexShaderSource
.getStr();
421 glShaderSource(VertexShaderID
, 1, &VertexSourcePointer
, NULL
);
422 glCompileShader(VertexShaderID
);
424 // Check Vertex Shader
425 glGetShaderiv(VertexShaderID
, GL_COMPILE_STATUS
, &Result
);
427 return LogCompilerError(VertexShaderID
, "vertex",
428 rVertexShaderName
, true);
430 // Compile Fragment Shader
431 if( !preamble
.isEmpty())
432 addPreamble( aFragmentShaderSource
, preamble
);
433 char const * FragmentSourcePointer
= aFragmentShaderSource
.getStr();
434 glShaderSource(FragmentShaderID
, 1, &FragmentSourcePointer
, NULL
);
435 glCompileShader(FragmentShaderID
);
437 // Check Fragment Shader
438 glGetShaderiv(FragmentShaderID
, GL_COMPILE_STATUS
, &Result
);
440 return LogCompilerError(FragmentShaderID
, "fragment",
441 rFragmentShaderName
, true);
444 glAttachShader(ProgramID
, VertexShaderID
);
445 glAttachShader(ProgramID
, FragmentShaderID
);
447 if( GLEW_ARB_get_program_binary
&& !rDigest
.isEmpty() )
449 glProgramParameteri(ProgramID
, GL_PROGRAM_BINARY_RETRIEVABLE_HINT
, GL_TRUE
);
450 glLinkProgram(ProgramID
);
451 glGetProgramiv(ProgramID
, GL_LINK_STATUS
, &Result
);
454 SAL_WARN("vcl.opengl", "linking failed: " << Result
);
455 return LogCompilerError(ProgramID
, "program", "<both>", false);
458 createFileName(rVertexShaderName
, rFragmentShaderName
, rDigest
);
459 saveProgramBinary(ProgramID
, aFileName
);
463 glLinkProgram(ProgramID
);
466 glDeleteShader(VertexShaderID
);
467 glDeleteShader(FragmentShaderID
);
470 glGetProgramiv(ProgramID
, GL_LINK_STATUS
, &Result
);
472 return LogCompilerError(ProgramID
, "program", "<both>", false);
476 // Ensure we bump our counts before we leave the shader zone.
477 { OpenGLZone aMakeProgress
; }
478 gbInShaderCompile
= false;
483 void OpenGLHelper::ConvertBitmapExToRGBATextureBuffer(const BitmapEx
& rBitmapEx
, sal_uInt8
* o_pRGBABuffer
, const bool bFlip
)
485 long nBmpWidth
= rBitmapEx
.GetSizePixel().Width();
486 long nBmpHeight
= rBitmapEx
.GetSizePixel().Height();
488 Bitmap
aBitmap (rBitmapEx
.GetBitmap());
489 AlphaMask
aAlpha (rBitmapEx
.GetAlpha());
490 Bitmap::ScopedReadAccess
pReadAccces( aBitmap
);
491 AlphaMask::ScopedReadAccess
pAlphaReadAccess( aAlpha
);
493 for (long ny
= (bFlip
? nBmpHeight
- 1 : 0); (bFlip
? ny
>= 0 : ny
< nBmpHeight
); (bFlip
? ny
-- : ny
++))
495 Scanline pAScan
= pAlphaReadAccess
? pAlphaReadAccess
->GetScanline(ny
) : 0;
496 for(long nx
= 0; nx
< nBmpWidth
; nx
++)
498 BitmapColor aCol
= pReadAccces
->GetColor( ny
, nx
);
499 o_pRGBABuffer
[i
++] = aCol
.GetRed();
500 o_pRGBABuffer
[i
++] = aCol
.GetGreen();
501 o_pRGBABuffer
[i
++] = aCol
.GetBlue();
502 o_pRGBABuffer
[i
++] = pAScan
? 255 - *pAScan
++ : 255;
507 void OpenGLHelper::renderToFile(long nWidth
, long nHeight
, const OUString
& rFileName
)
511 boost::scoped_array
<sal_uInt8
> pBuffer(new sal_uInt8
[nWidth
*nHeight
*4]);
512 glReadPixels(0, 0, nWidth
, nHeight
, GL_BGRA
, GL_UNSIGNED_BYTE
, pBuffer
.get());
513 BitmapEx aBitmap
= ConvertBGRABufferToBitmapEx(pBuffer
.get(), nWidth
, nHeight
);
515 vcl::PNGWriter
aWriter( aBitmap
);
516 SvFileStream
sOutput( rFileName
, StreamMode::WRITE
);
517 aWriter
.Write( sOutput
);
520 SAL_WARN("vcl.opengl", "Error writing png to " << rFileName
);
526 BitmapEx
OpenGLHelper::ConvertBGRABufferToBitmapEx(const sal_uInt8
* const pBuffer
, long nWidth
, long nHeight
)
529 Bitmap
aBitmap( Size(nWidth
, nHeight
), 24 );
530 AlphaMask
aAlpha( Size(nWidth
, nHeight
) );
533 Bitmap::ScopedWriteAccess
pWriteAccess( aBitmap
);
534 AlphaMask::ScopedWriteAccess
pAlphaWriteAccess( aAlpha
);
537 for( int y
= 0; y
< nHeight
; ++y
)
539 Scanline pScan
= pWriteAccess
->GetScanline(y
);
540 Scanline pAlphaScan
= pAlphaWriteAccess
->GetScanline(y
);
541 for( int x
= 0; x
< nWidth
; ++x
)
543 *pScan
++ = pBuffer
[nCurPos
];
544 *pScan
++ = pBuffer
[nCurPos
+1];
545 *pScan
++ = pBuffer
[nCurPos
+2];
548 *pAlphaScan
++ = static_cast<sal_uInt8
>( 255 - pBuffer
[nCurPos
++] );
552 return BitmapEx(aBitmap
, aAlpha
);
555 const char* OpenGLHelper::GLErrorString(GLenum errorCode
)
557 static const struct {
563 {GL_NO_ERROR
, "no error"},
564 {GL_INVALID_ENUM
, "invalid enumerant"},
565 {GL_INVALID_VALUE
, "invalid value"},
566 {GL_INVALID_OPERATION
, "invalid operation"},
567 {GL_STACK_OVERFLOW
, "stack overflow"},
568 {GL_STACK_UNDERFLOW
, "stack underflow"},
569 {GL_OUT_OF_MEMORY
, "out of memory"},
570 {GL_INVALID_FRAMEBUFFER_OPERATION
, "invalid framebuffer operation"},
577 for (i
=0; errors
[i
].string
; i
++)
579 if (errors
[i
].code
== errorCode
)
581 return errors
[i
].string
;
588 std::ostream
& operator<<(std::ostream
& rStrm
, const glm::vec4
& rPos
)
590 rStrm
<< "( " << rPos
[0] << ", " << rPos
[1] << ", " << rPos
[2] << ", " << rPos
[3] << ")";
594 std::ostream
& operator<<(std::ostream
& rStrm
, const glm::vec3
& rPos
)
596 rStrm
<< "( " << rPos
[0] << ", " << rPos
[1] << ", " << rPos
[2] << ")";
600 std::ostream
& operator<<(std::ostream
& rStrm
, const glm::mat4
& rMatrix
)
602 for(int i
= 0; i
< 4; ++i
)
605 for(int j
= 0; j
< 4; ++j
)
607 rStrm
<< rMatrix
[j
][i
];
615 void OpenGLHelper::createFramebuffer(long nWidth
, long nHeight
, GLuint
& nFramebufferId
,
616 GLuint
& nRenderbufferDepthId
, GLuint
& nRenderbufferColorId
, bool bRenderbuffer
)
620 // create a renderbuffer for depth attachment
621 glGenRenderbuffers(1, &nRenderbufferDepthId
);
622 glBindRenderbuffer(GL_RENDERBUFFER
, nRenderbufferDepthId
);
623 glRenderbufferStorage(GL_RENDERBUFFER
, GL_DEPTH_COMPONENT
, nWidth
, nHeight
);
624 glBindRenderbuffer(GL_RENDERBUFFER
, 0);
628 // create a renderbuffer for color attachment
629 glGenRenderbuffers(1, &nRenderbufferColorId
);
630 glBindRenderbuffer(GL_RENDERBUFFER
, nRenderbufferColorId
);
631 glRenderbufferStorage(GL_RENDERBUFFER
, GL_RGBA8
, nWidth
, nHeight
);
632 glBindRenderbuffer(GL_RENDERBUFFER
, 0);
636 glGenTextures(1, &nRenderbufferColorId
);
637 glBindTexture(GL_TEXTURE_2D
, nRenderbufferColorId
);
638 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
639 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR_MIPMAP_LINEAR
);
640 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
641 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
642 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA8
, nWidth
, nHeight
, 0,
643 GL_RGBA
, GL_UNSIGNED_BYTE
, 0);
644 glBindTexture(GL_TEXTURE_2D
, 0);
646 glFramebufferTexture2D(GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
,
647 GL_TEXTURE_2D
, nRenderbufferColorId
, 0);
650 // create a framebuffer object and attach renderbuffer
651 glGenFramebuffers(1, &nFramebufferId
);
652 glCheckFramebufferStatus(GL_FRAMEBUFFER
);
653 glBindFramebuffer(GL_FRAMEBUFFER
, nFramebufferId
);
654 // attach a renderbuffer to FBO color attachment point
655 glBindRenderbuffer(GL_RENDERBUFFER
, nRenderbufferColorId
);
656 glFramebufferRenderbuffer(GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
, GL_RENDERBUFFER
, nRenderbufferColorId
);
657 glCheckFramebufferStatus(GL_FRAMEBUFFER
);
658 // attach a renderbuffer to depth attachment point
659 glBindRenderbuffer(GL_RENDERBUFFER
, nRenderbufferDepthId
);
660 glFramebufferRenderbuffer(GL_FRAMEBUFFER
, GL_DEPTH_ATTACHMENT
, GL_RENDERBUFFER
, nRenderbufferDepthId
);
661 GLenum status
= glCheckFramebufferStatus(GL_FRAMEBUFFER
);
662 if (status
!= GL_FRAMEBUFFER_COMPLETE
)
664 SAL_WARN("vcl.opengl", "invalid framebuffer status");
666 glBindRenderbuffer(GL_RENDERBUFFER
, 0);
667 glBindFramebuffer(GL_FRAMEBUFFER
, 0);
672 float OpenGLHelper::getGLVersion()
674 float fVersion
= 1.0;
675 const GLubyte
* aVersion
= glGetString( GL_VERSION
);
676 if( aVersion
&& aVersion
[0] )
678 fVersion
= aVersion
[0] - '0';
679 if( aVersion
[1] == '.' && aVersion
[2] )
681 fVersion
+= (aVersion
[2] - '0')/10.0;
689 void OpenGLHelper::checkGLError(const char* pFile
, size_t nLine
)
696 GLenum glErr
= glGetError();
697 if (glErr
== GL_NO_ERROR
)
701 const char* sError
= OpenGLHelper::GLErrorString(glErr
);
704 SAL_WARN("vcl.opengl", "GL Error #" << glErr
<< "(" << sError
<< ") in File " << pFile
<< " at line: " << nLine
);
706 SAL_WARN("vcl.opengl", "GL Error #" << glErr
<< " (no message available) in File " << pFile
<< " at line: " << nLine
);
708 // tdf#93798 - apitrace appears to sometimes cause issues with an infinite loop here.
711 SAL_WARN("vcl.opengl", "Breaking potentially recursive glGetError loop");
717 bool OpenGLHelper::isDeviceBlacklisted()
719 static bool bSet
= false;
720 static bool bBlacklisted
= true; // assume the worst
725 #if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID
726 X11OpenGLDeviceInfo aInfo
;
727 bBlacklisted
= aInfo
.isDeviceBlocked();
728 SAL_INFO("vcl.opengl", "blacklisted: " << bBlacklisted
);
729 #elif defined( _WIN32 )
730 WinOpenGLDeviceInfo aInfo
;
731 bBlacklisted
= aInfo
.isDeviceBlocked();
733 bBlacklisted
= false;
741 bool OpenGLHelper::supportsVCLOpenGL()
743 static bool bDisableGL
= !!getenv("SAL_DISABLEGL");
744 bool bBlacklisted
= isDeviceBlacklisted();
746 if (bDisableGL
|| bBlacklisted
)
752 void OpenGLZone::enter() { gnEnterCount
++; }
753 void OpenGLZone::leave() { gnLeaveCount
++; }
756 static volatile bool gbWatchdogFiring
= false;
757 static oslCondition gpWatchdogExit
= NULL
;
758 static rtl::Reference
<OpenGLWatchdogThread
> gxWatchdog
;
761 OpenGLWatchdogThread::OpenGLWatchdogThread()
762 : salhelper::Thread("OpenGL Watchdog")
766 void OpenGLWatchdogThread::execute()
768 // delays to take various actions in 1/4 of a second increments.
769 static const int nDisableEntries
[2] = { 6 /* 1.5s */, 20 /* 5s */ };
770 static const int nAbortAfter
[2] = { 20 /* 10s */, 120 /* 30s */ };
772 int nUnchanged
= 0; // how many unchanged nEnters
774 TimeValue aHalfSecond
;
775 aHalfSecond
.Seconds
= 0;
776 aHalfSecond
.Nanosec
= 1000*1000*1000/4;
778 bool bAbortFired
= false;
781 sal_uInt64 nLastEnters
= OpenGLZone::gnEnterCount
;
783 osl_waitCondition(gpWatchdogExit
, &aHalfSecond
);
785 if (OpenGLZone::isInZone())
788 // The shader compiler can take a long time, first time.
789 if (gbInShaderCompile
)
792 if (nLastEnters
== OpenGLZone::gnEnterCount
)
796 SAL_INFO("vcl.opengl", "GL watchdog - unchanged " <<
797 nUnchanged
<< " enter count " <<
798 OpenGLZone::gnEnterCount
<< " type " <<
799 (nType
? "in shader" : "normal gl") <<
800 "breakpoints mid: " << nDisableEntries
[nType
] <<
801 " max " << nAbortAfter
[nType
]);
803 // Not making progress
804 if (nUnchanged
>= nDisableEntries
[nType
])
806 static bool bFired
= false;
809 gbWatchdogFiring
= true;
810 SAL_WARN("vcl.opengl", "Watchdog triggered: hard disable GL");
811 OpenGLZone::hardDisable();
812 gbWatchdogFiring
= false;
816 // we can hang using VCL in the abort handling -> be impatient
819 SAL_WARN("vcl.opengl", "Watchdog gave up: hard exiting");
824 // Not making even more progress
825 if (nUnchanged
>= nAbortAfter
[nType
])
829 SAL_WARN("vcl.opengl", "Watchdog gave up: aborting");
830 gbWatchdogFiring
= true;
841 } while (!osl_checkCondition(gpWatchdogExit
));
844 void OpenGLWatchdogThread::start()
846 assert (gxWatchdog
== NULL
);
847 gpWatchdogExit
= osl_createCondition();
848 gxWatchdog
= rtl::Reference
<OpenGLWatchdogThread
>(new OpenGLWatchdogThread());
849 gxWatchdog
->launch();
852 void OpenGLWatchdogThread::stop()
854 if (gbWatchdogFiring
)
855 return; // in watchdog thread
858 osl_setCondition(gpWatchdogExit
);
867 osl_destroyCondition(gpWatchdogExit
);
868 gpWatchdogExit
= NULL
;
872 * Called from a signal handler or watchdog thread if we get
873 * a crash or hang in some GL code.
875 void OpenGLZone::hardDisable()
877 // protect ourselves from double calling etc.
878 static bool bDisabled
= false;
883 // Disable the OpenGL support
884 std::shared_ptr
<comphelper::ConfigurationChanges
> xChanges(
885 comphelper::ConfigurationChanges::create());
886 officecfg::Office::Common::VCL::UseOpenGL::set(false, xChanges
);
889 // Force synchronous config write
890 css::uno::Reference
< css::util::XFlushable
>(
891 css::configuration::theDefaultProvider::get(
892 comphelper::getProcessComponentContext()),
893 css::uno::UNO_QUERY_THROW
)->flush();
895 OpenGLWatchdogThread::stop();
899 bool OpenGLHelper::isVCLOpenGLEnabled()
902 * The !bSet part should only be called once! Changing the results in the same
903 * run will mix OpenGL and normal rendering.
905 static bool bSet
= false;
906 static bool bEnable
= false;
907 static bool bForceOpenGL
= false;
909 // If we are a console app, then we don't use OpenGL
910 if ( Application::IsConsoleOnly() )
915 return bForceOpenGL
|| bEnable
;
918 * There are a number of cases that these environment variables cover:
919 * * SAL_FORCEGL forces OpenGL independent of any other option
920 * * SAL_DISABLEGL or a blacklisted driver avoid the use of OpenGL if SAL_FORCEGL is not set
921 * * SAL_ENABLEGL overrides VCL_HIDE_WINDOWS and the configuration variable
922 * * the configuration variable is checked if no environment variable is set
926 bForceOpenGL
= !!getenv("SAL_FORCEGL") || officecfg::Office::Common::VCL::ForceOpenGL::get();
932 else if (!supportsVCLOpenGL())
936 static bool bEnableGLEnv
= !!getenv("SAL_ENABLEGL");
938 bEnable
= bEnableGLEnv
;
940 static bool bDuringBuild
= getenv("VCL_HIDE_WINDOWS");
941 if (bDuringBuild
&& !bEnable
/* env. enable overrides */)
943 else if (officecfg::Office::Common::VCL::UseOpenGL::get())
950 if (!getenv("SAL_DISABLE_GL_WATCHDOG"))
951 OpenGLWatchdogThread::start();
952 ImplGetSVData()->maWinData
.mbNoSaveBackground
= true;
958 bool OpenGLWrapper::isVCLOpenGLEnabled()
960 return OpenGLHelper::isVCLOpenGLEnabled();
963 void OpenGLHelper::debugMsgStream(const char *pArea
, std::ostringstream
const &pStream
)
965 debugMsgPrint(pArea
, "%s", pStream
.str().c_str());
968 void OpenGLHelper::debugMsgPrint(const char *pArea
, const char *pFormat
, ...)
971 va_start (aArgs
, pFormat
);
975 #define vsnprintf _vsnprintf
977 vsnprintf(pStr
, sizeof(pStr
), pFormat
, aArgs
);
978 pStr
[sizeof(pStr
)-20] = '\0';
980 bool bHasContext
= OpenGLContext::hasCurrent();
982 strcat(pStr
, "- no GL context");
984 SAL_INFO(pArea
, pStr
);
991 glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION
,
993 1, // one[sic] id is as good as another ?
994 // GL_DEBUG_SEVERITY_NOTIFICATION for >= GL4.3 ?
995 GL_DEBUG_SEVERITY_LOW
,
997 else if (GLEW_AMD_debug_output
)
998 glDebugMessageInsertAMD(GL_DEBUG_CATEGORY_APPLICATION_AMD
,
999 GL_DEBUG_SEVERITY_LOW_AMD
,
1000 1, // one[sic] id is as good as another ?
1001 strlen(pStr
), pStr
);
1007 #if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID && !defined(LIBO_HEADLESS)
1009 bool OpenGLHelper::GetVisualInfo(Display
* pDisplay
, int nScreen
, XVisualInfo
& rVI
)
1014 int aAttrib
[] = { GLX_RGBA
,
1019 GLX_STENCIL_SIZE
, 8,
1022 pVI
= glXChooseVisual( pDisplay
, nScreen
, aAttrib
);
1033 GLXFBConfig
OpenGLHelper::GetPixmapFBConfig( Display
* pDisplay
, bool& bInverted
)
1037 int nScreen
= DefaultScreen( pDisplay
);
1038 GLXFBConfig
*aFbConfigs
;
1039 int i
, nFbConfigs
, nValue
;
1041 aFbConfigs
= glXGetFBConfigs( pDisplay
, nScreen
, &nFbConfigs
);
1042 for( i
= 0; i
< nFbConfigs
; i
++ )
1044 glXGetFBConfigAttrib( pDisplay
, aFbConfigs
[i
], GLX_DRAWABLE_TYPE
, &nValue
);
1045 if( !(nValue
& GLX_PIXMAP_BIT
) )
1048 glXGetFBConfigAttrib( pDisplay
, aFbConfigs
[i
], GLX_BIND_TO_TEXTURE_TARGETS_EXT
, &nValue
);
1049 if( !(nValue
& GLX_TEXTURE_2D_BIT_EXT
) )
1052 glXGetFBConfigAttrib( pDisplay
, aFbConfigs
[i
], GLX_DEPTH_SIZE
, &nValue
);
1056 glXGetFBConfigAttrib( pDisplay
, aFbConfigs
[i
], GLX_RED_SIZE
, &nValue
);
1059 SAL_INFO( "vcl.opengl", "Red is " << nValue
);
1061 // TODO: lfrb: Make it configurable wrt RGB/RGBA
1062 glXGetFBConfigAttrib( pDisplay
, aFbConfigs
[i
], GLX_BIND_TO_TEXTURE_RGB_EXT
, &nValue
);
1063 if( nValue
== False
)
1065 glXGetFBConfigAttrib( pDisplay
, aFbConfigs
[i
], GLX_BIND_TO_TEXTURE_RGBA_EXT
, &nValue
);
1066 if( nValue
== False
)
1070 glXGetFBConfigAttrib( pDisplay
, aFbConfigs
[i
], GLX_Y_INVERTED_EXT
, &nValue
);
1072 // Looks like that X sends GLX_DONT_CARE but this usually means "true" for most
1073 // of the X implementations. Investigation on internet pointed that this could be
1074 // safely "true" all the time (for example gnome-shell always assumes "true").
1075 bInverted
= nValue
== True
|| nValue
== int(GLX_DONT_CARE
);
1080 if( i
== nFbConfigs
)
1082 SAL_WARN( "vcl.opengl", "Unable to find FBconfig for pixmap texturing" );
1087 return aFbConfigs
[i
];
1092 OutputDevice::PaintScope::PaintScope(OutputDevice
*pDev
)
1095 if( pDev
->mpGraphics
|| pDev
->AcquireGraphics() )
1097 OpenGLContext
*pContext
= pDev
->mpGraphics
->BeginPaint();
1100 assert( pContext
->mnPainting
>= 0 );
1101 pContext
->mnPainting
++;
1102 pContext
->acquire();
1103 pHandle
= static_cast<void *>( pContext
);
1109 * Flush all the queued rendering commands to the screen for this context.
1111 void OutputDevice::PaintScope::flush()
1115 OpenGLContext
*pContext
= static_cast<OpenGLContext
*>( pHandle
);
1117 pContext
->mnPainting
--;
1118 assert( pContext
->mnPainting
>= 0 );
1119 if( pContext
->mnPainting
== 0 )
1121 pContext
->makeCurrent();
1122 pContext
->AcquireDefaultFramebuffer();
1124 pContext
->swapBuffers();
1127 pContext
->release();
1131 OutputDevice::PaintScope::~PaintScope()
1136 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */