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 <rtl/bootstrap.hxx>
15 #include <rtl/digest.h>
16 #include <rtl/strbuf.hxx>
17 #include <rtl/ustring.hxx>
18 #include <sal/log.hxx>
19 #include <tools/stream.hxx>
20 #include <config_folders.h>
22 #include <vcl/pngwrite.hxx>
23 #include <vcl/svapp.hxx>
24 #include <officecfg/Office/Common.hxx>
25 #include <com/sun/star/util/XFlushable.hpp>
26 #include <com/sun/star/configuration/theDefaultProvider.hpp>
30 #include <unordered_map>
32 #include <opengl/zone.hxx>
33 #include <opengl/watchdog.hxx>
34 #include <osl/conditn.hxx>
35 #include <vcl/opengl/OpenGLWrapper.hxx>
36 #include <vcl/opengl/OpenGLContext.hxx>
37 #include <desktop/crashreport.hxx>
38 #include <bitmapwriteaccess.hxx>
40 #if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID && !defined HAIKU
41 #include <opengl/x11/X11DeviceInfo.hxx>
42 #elif defined (_WIN32)
43 #include <opengl/win/WinDeviceInfo.hxx>
46 static bool volatile gbInShaderCompile
= false;
47 OpenGLZone::AtomicCounter
OpenGLZone::gnEnterCount
= 0;
48 OpenGLZone::AtomicCounter
OpenGLZone::gnLeaveCount
= 0;
54 OUString
getShaderFolder()
56 OUString
aUrl("$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
);
57 rtl::Bootstrap::expandMacros(aUrl
);
59 return aUrl
+ "/opengl/";
62 OString
loadShader(const OUString
& rFilename
)
64 OUString aFileURL
= getShaderFolder() + rFilename
+".glsl";
65 osl::File
aFile(aFileURL
);
66 if(aFile
.open(osl_File_OpenFlag_Read
) == osl::FileBase::E_None
)
70 std::unique_ptr
<char[]> content(new char[nSize
+1]);
71 sal_uInt64 nBytesRead
= 0;
72 aFile
.read(content
.get(), nSize
, nBytesRead
);
73 assert(nSize
== nBytesRead
);
74 content
.get()[nBytesRead
] = 0;
75 SAL_INFO("vcl.opengl", "Read " << nBytesRead
<< " bytes from " << aFileURL
);
80 SAL_WARN("vcl.opengl", "Could not open " << aFileURL
);
86 OString
& getShaderSource(const OUString
& rFilename
)
88 static std::unordered_map
<OUString
, OString
> aMap
;
90 if (aMap
.find(rFilename
) == aMap
.end())
92 aMap
[rFilename
] = loadShader(rFilename
);
95 return aMap
[rFilename
];
101 int LogCompilerError(GLuint nId
, const OUString
&rDetail
,
102 const OUString
&rName
, bool bShaderNotProgram
)
106 int InfoLogLength
= 0;
110 if (bShaderNotProgram
)
111 glGetShaderiv (nId
, GL_INFO_LOG_LENGTH
, &InfoLogLength
);
113 glGetProgramiv(nId
, GL_INFO_LOG_LENGTH
, &InfoLogLength
);
117 if ( InfoLogLength
> 0 )
119 std::vector
<char> ErrorMessage(InfoLogLength
+1);
120 if (bShaderNotProgram
)
121 glGetShaderInfoLog (nId
, InfoLogLength
, nullptr, ErrorMessage
.data());
123 glGetProgramInfoLog(nId
, InfoLogLength
, nullptr, ErrorMessage
.data());
126 ErrorMessage
.push_back('\0');
127 SAL_WARN("vcl.opengl", rDetail
<< " shader " << nId
<< " compile for " << rName
<< " failed : " << ErrorMessage
.data());
130 SAL_WARN("vcl.opengl", rDetail
<< " shader: " << rName
<< " compile " << nId
<< " failed without error log");
139 static void addPreamble(OString
& rShaderSource
, const OString
& rPreamble
)
141 if (rPreamble
.isEmpty())
144 int nVersionStrStartPos
= rShaderSource
.indexOf("#version");
146 if (nVersionStrStartPos
== -1)
148 rShaderSource
= rPreamble
+ "\n" + rShaderSource
;
152 int nVersionStrEndPos
= rShaderSource
.indexOf('\n', nVersionStrStartPos
);
154 SAL_WARN_IF(nVersionStrEndPos
== -1, "vcl.opengl", "syntax error in shader");
156 if (nVersionStrEndPos
== -1)
157 nVersionStrEndPos
= nVersionStrStartPos
+ 8;
159 OString aVersionLine
= rShaderSource
.copy(0, nVersionStrEndPos
);
160 OString aShaderBody
= rShaderSource
.copy(nVersionStrEndPos
+ 1);
162 rShaderSource
= aVersionLine
+ "\n" + rPreamble
+ "\n" + aShaderBody
;
168 static const sal_uInt32 GLenumSize
= sizeof(GLenum
);
170 OString
getHexString(const sal_uInt8
* pData
, sal_uInt32 nLength
)
172 static const char* const pHexData
= "0123456789ABCDEF";
175 OStringBuffer aHexStr
;
176 for(size_t i
= 0; i
< nLength
; ++i
)
178 sal_uInt8 val
= pData
[i
];
181 aHexStr
.append( pHexData
[ val
& 0xf ] );
182 aHexStr
.append( pHexData
[ val
>> 4 ] );
187 return aHexStr
.makeStringAndClear();
190 OString
generateMD5(const void* pData
, size_t length
)
192 sal_uInt8 pBuffer
[RTL_DIGEST_LENGTH_MD5
];
193 rtlDigestError aError
= rtl_digest_MD5(pData
, length
,
194 pBuffer
, RTL_DIGEST_LENGTH_MD5
);
195 SAL_WARN_IF(aError
!= rtl_Digest_E_None
, "vcl.opengl", "md5 generation failed");
197 return getHexString(pBuffer
, RTL_DIGEST_LENGTH_MD5
);
200 OString
getDeviceInfoString()
202 #if defined( SAL_UNX ) && !defined( MACOSX ) && !defined( IOS )&& !defined( ANDROID ) && !defined( HAIKU )
203 const X11OpenGLDeviceInfo aInfo
;
204 return aInfo
.GetOS() +
205 aInfo
.GetOSRelease() +
206 aInfo
.GetRenderer() +
209 #elif defined( _WIN32 )
210 const WinOpenGLDeviceInfo aInfo
;
211 return OUStringToOString(aInfo
.GetAdapterVendorID(), RTL_TEXTENCODING_UTF8
) +
212 OUStringToOString(aInfo
.GetAdapterDeviceID(), RTL_TEXTENCODING_UTF8
) +
213 OUStringToOString(aInfo
.GetDriverVersion(), RTL_TEXTENCODING_UTF8
) +
214 OString::number(aInfo
.GetWindowsVersion());
216 return rtl::OStringView(reinterpret_cast<const char*>(glGetString(GL_VENDOR
))) +
217 reinterpret_cast<const char*>(glGetString(GL_RENDERER
)) +
218 reinterpret_cast<const char*>(glGetString(GL_VERSION
));
222 OString
getStringDigest( const OUString
& rVertexShaderName
,
223 const OUString
& rFragmentShaderName
,
224 const OString
& rPreamble
)
226 // read shaders source
227 OString aVertexShaderSource
= getShaderSource( rVertexShaderName
);
228 OString aFragmentShaderSource
= getShaderSource( rFragmentShaderName
);
230 // get info about the graphic device
231 static const OString
aDeviceInfo (getDeviceInfoString());
233 OString aMessage
= rPreamble
+
234 aVertexShaderSource
+
235 aFragmentShaderSource
+
238 return generateMD5(aMessage
.getStr(), aMessage
.getLength());
241 OString
getCacheFolder()
243 OUString
url("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
"/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache/");
244 rtl::Bootstrap::expandMacros(url
);
246 osl::Directory::create(url
);
248 return OUStringToOString(url
, RTL_TEXTENCODING_UTF8
);
252 bool writeProgramBinary( const OString
& rBinaryFileName
,
253 const std::vector
<sal_uInt8
>& rBinary
)
255 osl::File
aFile(OStringToOUString(rBinaryFileName
, RTL_TEXTENCODING_UTF8
));
256 osl::FileBase::RC eStatus
= aFile
.open(
257 osl_File_OpenFlag_Write
| osl_File_OpenFlag_Create
);
259 if( eStatus
!= osl::FileBase::E_None
)
261 // when file already exists we do not have to save it:
262 // we can be sure that the binary to save is exactly equal
263 // to the already saved binary, since they have the same hash value
264 if( eStatus
== osl::FileBase::E_EXIST
)
266 SAL_INFO( "vcl.opengl",
267 "No binary program saved. A file with the same hash already exists: '" << rBinaryFileName
<< "'" );
273 sal_uInt64 nBytesWritten
= 0;
274 aFile
.write( rBinary
.data(), rBinary
.size(), nBytesWritten
);
276 assert( rBinary
.size() == nBytesWritten
);
281 bool readProgramBinary( const OString
& rBinaryFileName
,
282 std::vector
<sal_uInt8
>& rBinary
)
284 osl::File
aFile( OStringToOUString( rBinaryFileName
, RTL_TEXTENCODING_UTF8
) );
285 if(aFile
.open( osl_File_OpenFlag_Read
) == osl::FileBase::E_None
)
287 sal_uInt64 nSize
= 0;
288 aFile
.getSize( nSize
);
289 rBinary
.resize( nSize
);
290 sal_uInt64 nBytesRead
= 0;
291 aFile
.read( rBinary
.data(), nSize
, nBytesRead
);
292 assert( nSize
== nBytesRead
);
293 VCL_GL_INFO("Loading file: '" << rBinaryFileName
<< "': success" );
298 VCL_GL_INFO("Loading file: '" << rBinaryFileName
<< "': FAIL");
304 OString
createFileName( const OUString
& rVertexShaderName
,
305 const OUString
& rFragmentShaderName
,
306 const OUString
& rGeometryShaderName
,
307 const OString
& rDigest
)
309 OString aFileName
= getCacheFolder() +
310 OUStringToOString( rVertexShaderName
, RTL_TEXTENCODING_UTF8
) + "-" +
311 OUStringToOString( rFragmentShaderName
, RTL_TEXTENCODING_UTF8
) + "-";
312 if (!rGeometryShaderName
.isEmpty())
313 aFileName
+= OUStringToOString( rGeometryShaderName
, 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
= reinterpret_cast<sal_uInt8
*>(&nBinaryFormat
);
329 for( size_t i
= 0; i
< GLenumSize
; ++i
)
331 pBF
[i
] = aBinary
[nBinaryLength
+ i
];
335 glProgramBinary( nProgramID
, nBinaryFormat
, 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
, nullptr, &nBinaryFormat
, aBinary
.data() );
359 const sal_uInt8
* pBF
= reinterpret_cast<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_INFO("vcl.opengl", "Writing binary file '" << rBinaryFileName
<< "': success");
373 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 OUString
& rGeometryShaderName
,
383 const OString
& preamble
,
384 const OString
& rDigest
)
388 gbInShaderCompile
= true;
390 bool bHasGeometryShader
= !rGeometryShaderName
.isEmpty();
392 // create the program object
393 GLint ProgramID
= glCreateProgram();
395 // read shaders from file
396 OString aVertexShaderSource
= getShaderSource(rVertexShaderName
);
397 OString aFragmentShaderSource
= getShaderSource(rFragmentShaderName
);
398 OString aGeometryShaderSource
;
399 if (bHasGeometryShader
)
400 aGeometryShaderSource
= getShaderSource(rGeometryShaderName
);
402 GLint bBinaryResult
= GL_FALSE
;
403 if (epoxy_has_gl_extension("GL_ARB_get_program_binary") && !rDigest
.isEmpty())
406 createFileName(rVertexShaderName
, rFragmentShaderName
, rGeometryShaderName
, rDigest
);
407 bBinaryResult
= loadProgramBinary(ProgramID
, aFileName
);
411 if( bBinaryResult
!= GL_FALSE
)
414 if (bHasGeometryShader
)
415 VCL_GL_INFO("Load shader: vertex " << rVertexShaderName
<< " fragment " << rFragmentShaderName
<< " geometry " << rGeometryShaderName
);
417 VCL_GL_INFO("Load shader: vertex " << rVertexShaderName
<< " fragment " << rFragmentShaderName
);
418 // Create the shaders
419 GLuint VertexShaderID
= glCreateShader(GL_VERTEX_SHADER
);
420 GLuint FragmentShaderID
= glCreateShader(GL_FRAGMENT_SHADER
);
421 GLuint GeometryShaderID
= 0;
422 if (bHasGeometryShader
)
423 GeometryShaderID
= glCreateShader(GL_GEOMETRY_SHADER
);
425 GLint Result
= GL_FALSE
;
427 // Compile Vertex Shader
428 if( !preamble
.isEmpty())
429 addPreamble( aVertexShaderSource
, preamble
);
430 char const * VertexSourcePointer
= aVertexShaderSource
.getStr();
431 glShaderSource(VertexShaderID
, 1, &VertexSourcePointer
, nullptr);
432 glCompileShader(VertexShaderID
);
434 // Check Vertex Shader
435 glGetShaderiv(VertexShaderID
, GL_COMPILE_STATUS
, &Result
);
437 return LogCompilerError(VertexShaderID
, "vertex",
438 rVertexShaderName
, true);
440 // Compile Fragment Shader
441 if( !preamble
.isEmpty())
442 addPreamble( aFragmentShaderSource
, preamble
);
443 char const * FragmentSourcePointer
= aFragmentShaderSource
.getStr();
444 glShaderSource(FragmentShaderID
, 1, &FragmentSourcePointer
, nullptr);
445 glCompileShader(FragmentShaderID
);
447 // Check Fragment Shader
448 glGetShaderiv(FragmentShaderID
, GL_COMPILE_STATUS
, &Result
);
450 return LogCompilerError(FragmentShaderID
, "fragment",
451 rFragmentShaderName
, true);
453 if (bHasGeometryShader
)
455 // Compile Geometry Shader
456 if( !preamble
.isEmpty())
457 addPreamble( aGeometryShaderSource
, preamble
);
458 char const * GeometrySourcePointer
= aGeometryShaderSource
.getStr();
459 glShaderSource(GeometryShaderID
, 1, &GeometrySourcePointer
, nullptr);
460 glCompileShader(GeometryShaderID
);
462 // Check Geometry Shader
463 glGetShaderiv(GeometryShaderID
, GL_COMPILE_STATUS
, &Result
);
465 return LogCompilerError(GeometryShaderID
, "geometry",
466 rGeometryShaderName
, true);
470 glAttachShader(ProgramID
, VertexShaderID
);
471 glAttachShader(ProgramID
, FragmentShaderID
);
472 if (bHasGeometryShader
)
473 glAttachShader(ProgramID
, GeometryShaderID
);
475 if (epoxy_has_gl_extension("GL_ARB_get_program_binary") && !rDigest
.isEmpty())
477 glProgramParameteri(ProgramID
, GL_PROGRAM_BINARY_RETRIEVABLE_HINT
, GL_TRUE
);
478 glLinkProgram(ProgramID
);
479 glGetProgramiv(ProgramID
, GL_LINK_STATUS
, &Result
);
482 SAL_WARN("vcl.opengl", "linking failed: " << Result
);
483 return LogCompilerError(ProgramID
, "program", "<both>", false);
486 createFileName(rVertexShaderName
, rFragmentShaderName
, rGeometryShaderName
, rDigest
);
487 saveProgramBinary(ProgramID
, aFileName
);
491 glLinkProgram(ProgramID
);
494 glDeleteShader(VertexShaderID
);
495 glDeleteShader(FragmentShaderID
);
496 if (bHasGeometryShader
)
497 glDeleteShader(GeometryShaderID
);
500 glGetProgramiv(ProgramID
, GL_LINK_STATUS
, &Result
);
502 return LogCompilerError(ProgramID
, "program", "<both>", false);
506 // Ensure we bump our counts before we leave the shader zone.
507 { OpenGLZone aMakeProgress
; }
508 gbInShaderCompile
= false;
513 GLint
OpenGLHelper::LoadShaders(const OUString
& rVertexShaderName
,
514 const OUString
& rFragmentShaderName
,
515 const OString
& preamble
,
516 const OString
& rDigest
)
518 return LoadShaders(rVertexShaderName
, rFragmentShaderName
, OUString(), preamble
, rDigest
);
521 GLint
OpenGLHelper::LoadShaders(const OUString
& rVertexShaderName
,
522 const OUString
& rFragmentShaderName
,
523 const OUString
& rGeometryShaderName
)
525 return LoadShaders(rVertexShaderName
, rFragmentShaderName
, rGeometryShaderName
, OString(), OString());
528 GLint
OpenGLHelper::LoadShaders(const OUString
& rVertexShaderName
,
529 const OUString
& rFragmentShaderName
)
531 return LoadShaders(rVertexShaderName
, rFragmentShaderName
, OUString(), "", "");
534 void OpenGLHelper::renderToFile(long nWidth
, long nHeight
, const OUString
& rFileName
)
538 std::unique_ptr
<sal_uInt8
[]> pBuffer(new sal_uInt8
[nWidth
*nHeight
*4]);
539 glReadPixels(0, 0, nWidth
, nHeight
, OptimalBufferFormat(), GL_UNSIGNED_BYTE
, pBuffer
.get());
540 BitmapEx aBitmap
= ConvertBufferToBitmapEx(pBuffer
.get(), nWidth
, nHeight
);
542 vcl::PNGWriter
aWriter( aBitmap
);
543 SvFileStream
sOutput( rFileName
, StreamMode::WRITE
);
544 aWriter
.Write( sOutput
);
547 SAL_WARN("vcl.opengl", "Error writing png to " << rFileName
);
553 GLenum
OpenGLHelper::OptimalBufferFormat()
556 return GL_BGRA
; // OpenGLSalBitmap is internally ScanlineFormat::N24BitTcBgr
558 return GL_RGBA
; // OpenGLSalBitmap is internally ScanlineFormat::N24BitTcRgb
562 BitmapEx
OpenGLHelper::ConvertBufferToBitmapEx(const sal_uInt8
* const pBuffer
, long nWidth
, long nHeight
)
565 Bitmap
aBitmap( Size(nWidth
, nHeight
), 24 );
566 AlphaMask
aAlpha( Size(nWidth
, nHeight
) );
569 BitmapScopedWriteAccess
pWriteAccess( aBitmap
);
570 AlphaScopedWriteAccess
pAlphaWriteAccess( aAlpha
);
572 assert(pWriteAccess
->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr
);
573 assert(pWriteAccess
->IsTopDown());
574 assert(pAlphaWriteAccess
->IsTopDown());
576 assert(pWriteAccess
->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb
);
577 assert(!pWriteAccess
->IsTopDown());
578 assert(!pAlphaWriteAccess
->IsTopDown());
580 assert(pAlphaWriteAccess
->GetScanlineFormat() == ScanlineFormat::N8BitPal
);
583 for( long y
= 0; y
< nHeight
; ++y
)
586 Scanline pScan
= pWriteAccess
->GetScanline(y
);
587 Scanline pAlphaScan
= pAlphaWriteAccess
->GetScanline(y
);
589 Scanline pScan
= pWriteAccess
->GetScanline(nHeight
-1-y
);
590 Scanline pAlphaScan
= pAlphaWriteAccess
->GetScanline(nHeight
-1-y
);
592 for( long x
= 0; x
< nWidth
; ++x
)
594 *pScan
++ = pBuffer
[nCurPos
];
595 *pScan
++ = pBuffer
[nCurPos
+1];
596 *pScan
++ = pBuffer
[nCurPos
+2];
599 *pAlphaScan
++ = static_cast<sal_uInt8
>( 255 - pBuffer
[nCurPos
++] );
603 return BitmapEx(aBitmap
, aAlpha
);
606 const char* OpenGLHelper::GLErrorString(GLenum errorCode
)
608 static const struct {
614 {GL_NO_ERROR
, "no error"},
615 {GL_INVALID_ENUM
, "invalid enumerant"},
616 {GL_INVALID_VALUE
, "invalid value"},
617 {GL_INVALID_OPERATION
, "invalid operation"},
618 {GL_STACK_OVERFLOW
, "stack overflow"},
619 {GL_STACK_UNDERFLOW
, "stack underflow"},
620 {GL_OUT_OF_MEMORY
, "out of memory"},
621 {GL_INVALID_FRAMEBUFFER_OPERATION
, "invalid framebuffer operation"},
628 for (i
=0; errors
[i
].string
; i
++)
630 if (errors
[i
].code
== errorCode
)
632 return errors
[i
].string
;
639 std::ostream
& operator<<(std::ostream
& rStrm
, const glm::vec4
& rPos
)
641 rStrm
<< "( " << rPos
[0] << ", " << rPos
[1] << ", " << rPos
[2] << ", " << rPos
[3] << ")";
645 std::ostream
& operator<<(std::ostream
& rStrm
, const glm::vec3
& rPos
)
647 rStrm
<< "( " << rPos
[0] << ", " << rPos
[1] << ", " << rPos
[2] << ")";
651 std::ostream
& operator<<(std::ostream
& rStrm
, const glm::mat4
& rMatrix
)
653 for(int i
= 0; i
< 4; ++i
)
656 for(int j
= 0; j
< 4; ++j
)
658 rStrm
<< rMatrix
[j
][i
];
666 void OpenGLHelper::createFramebuffer(long nWidth
, long nHeight
, GLuint
& nFramebufferId
,
667 GLuint
& nRenderbufferDepthId
, GLuint
& nRenderbufferColorId
)
671 // create a renderbuffer for depth attachment
672 glGenRenderbuffers(1, &nRenderbufferDepthId
);
673 glBindRenderbuffer(GL_RENDERBUFFER
, nRenderbufferDepthId
);
674 glRenderbufferStorage(GL_RENDERBUFFER
, GL_DEPTH_COMPONENT
, nWidth
, nHeight
);
675 glBindRenderbuffer(GL_RENDERBUFFER
, 0);
677 glGenTextures(1, &nRenderbufferColorId
);
678 glBindTexture(GL_TEXTURE_2D
, nRenderbufferColorId
);
679 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
680 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR_MIPMAP_LINEAR
);
681 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
682 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
683 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA8
, nWidth
, nHeight
, 0,
684 GL_RGBA
, GL_UNSIGNED_BYTE
, nullptr);
685 glBindTexture(GL_TEXTURE_2D
, 0);
687 glFramebufferTexture2D(GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
,
688 GL_TEXTURE_2D
, nRenderbufferColorId
, 0);
690 // create a framebuffer object and attach renderbuffer
691 glGenFramebuffers(1, &nFramebufferId
);
692 glCheckFramebufferStatus(GL_FRAMEBUFFER
);
693 glBindFramebuffer(GL_FRAMEBUFFER
, nFramebufferId
);
694 // attach a renderbuffer to FBO color attachment point
695 glBindRenderbuffer(GL_RENDERBUFFER
, nRenderbufferColorId
);
696 glFramebufferRenderbuffer(GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
, GL_RENDERBUFFER
, nRenderbufferColorId
);
697 glCheckFramebufferStatus(GL_FRAMEBUFFER
);
698 // attach a renderbuffer to depth attachment point
699 glBindRenderbuffer(GL_RENDERBUFFER
, nRenderbufferDepthId
);
700 glFramebufferRenderbuffer(GL_FRAMEBUFFER
, GL_DEPTH_ATTACHMENT
, GL_RENDERBUFFER
, nRenderbufferDepthId
);
701 GLenum status
= glCheckFramebufferStatus(GL_FRAMEBUFFER
);
702 if (status
!= GL_FRAMEBUFFER_COMPLETE
)
704 SAL_WARN("vcl.opengl", "invalid framebuffer status");
706 glBindRenderbuffer(GL_RENDERBUFFER
, 0);
707 glBindFramebuffer(GL_FRAMEBUFFER
, 0);
712 float OpenGLHelper::getGLVersion()
714 float fVersion
= 1.0;
715 const GLubyte
* aVersion
= glGetString( GL_VERSION
);
716 if( aVersion
&& aVersion
[0] )
718 fVersion
= aVersion
[0] - '0';
719 if( aVersion
[1] == '.' && aVersion
[2] )
721 fVersion
+= (aVersion
[2] - '0')/10.0;
729 void OpenGLHelper::checkGLError(const char* pFile
, size_t nLine
)
736 GLenum glErr
= glGetError();
737 if (glErr
== GL_NO_ERROR
)
741 const char* sError
= OpenGLHelper::GLErrorString(glErr
);
743 sError
= "no message available";
745 SAL_WARN("vcl.opengl", "GL Error " << std::hex
<< std::setw(4) << std::setfill('0') << glErr
<< std::dec
<< std::setw(0) << std::setfill(' ') << " (" << sError
<< ") in file " << pFile
<< " at line " << nLine
);
747 // tdf#93798 - apitrace appears to sometimes cause issues with an infinite loop here.
750 SAL_WARN("vcl.opengl", "Breaking potentially recursive glGetError loop");
756 bool OpenGLHelper::isDeviceBlacklisted()
758 static bool bSet
= false;
759 static bool bBlacklisted
= true; // assume the worst
764 #if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID && !defined HAIKU
765 X11OpenGLDeviceInfo aInfo
;
766 bBlacklisted
= aInfo
.isDeviceBlocked();
767 SAL_INFO("vcl.opengl", "blacklisted: " << bBlacklisted
);
768 #elif defined( _WIN32 )
769 WinOpenGLDeviceInfo aInfo
;
770 bBlacklisted
= aInfo
.isDeviceBlocked();
772 if (aInfo
.GetWindowsVersion() == 0x00060001 && /* Windows 7 */
773 (aInfo
.GetAdapterVendorID() == "0x1002" || aInfo
.GetAdapterVendorID() == "0x1022")) /* AMD */
775 SAL_INFO("vcl.opengl", "Relaxing watchdog timings.");
776 OpenGLZone::relaxWatchdogTimings();
779 bBlacklisted
= false;
787 bool OpenGLHelper::supportsVCLOpenGL()
789 static bool bDisableGL
= !!getenv("SAL_DISABLEGL");
790 bool bBlacklisted
= isDeviceBlacklisted();
792 return !bDisableGL
&& !bBlacklisted
;
796 static volatile bool gbWatchdogFiring
= false;
797 static osl::Condition
* gpWatchdogExit
= nullptr;
798 static WatchdogTimings gWatchdogTimings
;
799 static rtl::Reference
<OpenGLWatchdogThread
> gxWatchdog
;
802 WatchdogTimings::WatchdogTimings()
804 {{6, 20} /* 1.5s, 5s */, {20, 120} /* 5s, 30s */,
805 {60, 240} /* 15s, 60s */, {60, 240} /* 15s, 60s */}
811 OpenGLWatchdogThread::OpenGLWatchdogThread()
812 : salhelper::Thread("OpenGL Watchdog")
816 void OpenGLWatchdogThread::execute()
818 int nUnchanged
= 0; // how many unchanged nEnters
819 TimeValue
aQuarterSecond(0, 1000*1000*1000*0.25);
820 bool bAbortFired
= false;
823 sal_uInt64 nLastEnters
= OpenGLZone::gnEnterCount
;
825 gpWatchdogExit
->wait(&aQuarterSecond
);
827 if (OpenGLZone::isInZone())
829 // The shader compiler can take a long time, first time.
830 WatchdogTimingMode eMode
= gbInShaderCompile
? WatchdogTimingMode::SHADER_COMPILE
: WatchdogTimingMode::NORMAL
;
831 WatchdogTimingsValues aTimingValues
= gWatchdogTimings
.getWatchdogTimingsValues(eMode
);
833 if (nLastEnters
== OpenGLZone::gnEnterCount
)
837 SAL_INFO("vcl.opengl", "GL watchdog - unchanged " <<
838 nUnchanged
<< " enter count " <<
839 OpenGLZone::gnEnterCount
<< " type " <<
840 (eMode
== WatchdogTimingMode::SHADER_COMPILE
? "in shader" : "normal gl") <<
841 "breakpoints mid: " << aTimingValues
.mnDisableEntries
<<
842 " max " << aTimingValues
.mnAbortAfter
);
844 // Not making progress
845 if (nUnchanged
>= aTimingValues
.mnDisableEntries
)
847 static bool bFired
= false;
850 gbWatchdogFiring
= true;
851 SAL_WARN("vcl.opengl", "Watchdog triggered: hard disable GL");
852 OpenGLZone::hardDisable();
853 gbWatchdogFiring
= false;
857 // we can hang using VCL in the abort handling -> be impatient
860 SAL_WARN("vcl.opengl", "Watchdog gave up: hard exiting");
865 // Not making even more progress
866 if (nUnchanged
>= aTimingValues
.mnAbortAfter
)
870 SAL_WARN("vcl.opengl", "Watchdog gave up: aborting");
871 gbWatchdogFiring
= true;
874 // coverity[dead_error_line] - we might have caught SIGABRT and failed to exit yet
882 } while (!gpWatchdogExit
->check());
885 void OpenGLWatchdogThread::start()
887 assert (gxWatchdog
== nullptr);
888 gpWatchdogExit
= new osl::Condition();
889 gxWatchdog
.set(new OpenGLWatchdogThread());
890 gxWatchdog
->launch();
893 void OpenGLWatchdogThread::stop()
895 if (gbWatchdogFiring
)
896 return; // in watchdog thread
899 gpWatchdogExit
->set();
907 delete gpWatchdogExit
;
908 gpWatchdogExit
= nullptr;
912 * Called from a signal handler or watchdog thread if we get
913 * a crash or hang in some GL code.
915 void OpenGLZone::hardDisable()
917 // protect ourselves from double calling etc.
918 static bool bDisabled
= false;
923 // Disable the OpenGL support
924 std::shared_ptr
<comphelper::ConfigurationChanges
> xChanges(
925 comphelper::ConfigurationChanges::create());
926 officecfg::Office::Common::VCL::UseOpenGL::set(false, xChanges
);
929 // Force synchronous config write
930 css::uno::Reference
< css::util::XFlushable
>(
931 css::configuration::theDefaultProvider::get(
932 comphelper::getProcessComponentContext()),
933 css::uno::UNO_QUERY_THROW
)->flush();
935 OpenGLWatchdogThread::stop();
939 void OpenGLZone::relaxWatchdogTimings()
941 gWatchdogTimings
.setRelax(true);
944 OpenGLVCLContextZone::OpenGLVCLContextZone()
946 OpenGLContext::makeVCLCurrent();
951 bool bTempOpenGLDisabled
= false;
954 PreDefaultWinNoOpenGLZone::PreDefaultWinNoOpenGLZone()
956 bTempOpenGLDisabled
= true;
959 PreDefaultWinNoOpenGLZone::~PreDefaultWinNoOpenGLZone()
961 bTempOpenGLDisabled
= false;
964 bool OpenGLHelper::isVCLOpenGLEnabled()
967 * The !bSet part should only be called once! Changing the results in the same
968 * run will mix OpenGL and normal rendering.
971 static bool bSet
= false;
972 static bool bEnable
= false;
973 static bool bForceOpenGL
= false;
975 // No hardware rendering, so no OpenGL
976 if (Application::IsBitmapRendering())
979 //tdf#106155, disable GL while loading certain bitmaps needed for the initial toplevel windows
980 //under raw X (kde) vclplug
981 if (bTempOpenGLDisabled
)
986 return bForceOpenGL
|| bEnable
;
989 * There are a number of cases that these environment variables cover:
990 * * SAL_FORCEGL forces OpenGL independent of any other option
991 * * SAL_DISABLEGL or a blacklisted driver avoid the use of OpenGL if SAL_FORCEGL is not set
995 bForceOpenGL
= !!getenv("SAL_FORCEGL") || officecfg::Office::Common::VCL::ForceOpenGL::get();
998 bool bSupportsVCLOpenGL
= supportsVCLOpenGL();
999 // always call supportsVCLOpenGL to de-zombie the glxtest child process on X11
1004 else if (bSupportsVCLOpenGL
)
1006 static bool bEnableGLEnv
= !!getenv("SAL_ENABLEGL");
1008 bEnable
= bEnableGLEnv
;
1010 if (officecfg::Office::Common::VCL::UseOpenGL::get())
1013 // Force disable in safe mode
1014 if (Application::IsSafeModeEnabled())
1022 if (!getenv("SAL_DISABLE_GL_WATCHDOG"))
1023 OpenGLWatchdogThread::start();
1025 CrashReporter::addKeyValue("UseOpenGL", OUString::boolean(bRet
), CrashReporter::Write
);
1030 bool OpenGLWrapper::isVCLOpenGLEnabled()
1032 return OpenGLHelper::isVCLOpenGLEnabled();
1035 void OpenGLHelper::debugMsgStream(std::ostringstream
const &pStream
)
1038 0, "%" SAL_PRIxUINT32
": %s", osl_getThreadIdentifier(nullptr), pStream
.str().c_str());
1041 void OpenGLHelper::debugMsgStreamWarn(std::ostringstream
const &pStream
)
1044 1, "%" SAL_PRIxUINT32
": %s", osl_getThreadIdentifier(nullptr), pStream
.str().c_str());
1047 void OpenGLHelper::debugMsgPrint(const int nType
, const char *pFormat
, ...)
1050 va_start (aArgs
, pFormat
);
1054 #define vsnprintf _vsnprintf
1056 vsnprintf(pStr
, sizeof(pStr
), pFormat
, aArgs
);
1057 pStr
[sizeof(pStr
)-20] = '\0';
1059 bool bHasContext
= OpenGLContext::hasCurrent();
1061 strcat(pStr
, " (no GL context)");
1065 SAL_INFO("vcl.opengl", pStr
);
1067 else if (nType
== 1)
1069 SAL_WARN("vcl.opengl", pStr
);
1076 if (epoxy_has_gl_extension("GL_KHR_debug"))
1077 glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION
,
1078 GL_DEBUG_TYPE_OTHER
,
1079 1, // one[sic] id is as good as another ?
1080 // GL_DEBUG_SEVERITY_NOTIFICATION for >= GL4.3 ?
1081 GL_DEBUG_SEVERITY_LOW
,
1082 strlen(pStr
), pStr
);
1083 else if (epoxy_has_gl_extension("GL_AMD_debug_output"))
1084 glDebugMessageInsertAMD(GL_DEBUG_CATEGORY_APPLICATION_AMD
,
1085 GL_DEBUG_SEVERITY_LOW_AMD
,
1086 1, // one[sic] id is as good as another ?
1087 strlen(pStr
), pStr
);
1093 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */