2014-04-15 00:55:07 +02:00
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2.0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
*/
2014-07-21 14:37:25 +01:00
# include <vcl/opengl/GLMHelper.hxx>
2014-04-15 00:55:07 +02:00
# include <vcl/opengl/OpenGLHelper.hxx>
# include <osl/file.hxx>
# include <rtl/bootstrap.hxx>
2015-09-13 12:15:13 +02:00
# include <rtl/digest.h>
# include <rtl/strbuf.hxx>
# include <rtl/ustring.hxx>
2018-07-28 15:57:23 +02:00
# include <sal/log.hxx>
2018-11-19 01:06:09 +01:00
# include <tools/stream.hxx>
2014-04-15 00:55:07 +02:00
# include <config_folders.h>
2015-06-15 17:58:15 +09:00
# include <memory>
2014-05-09 00:31:05 +02:00
# include <vcl/pngwrite.hxx>
2015-01-24 14:26:25 +11:00
# include <vcl/svapp.hxx>
2014-11-10 16:02:31 +00:00
# include <officecfg/Office/Common.hxx>
2015-08-20 17:03:30 +01:00
# include <com/sun/star/util/XFlushable.hpp>
# include <com/sun/star/configuration/theDefaultProvider.hpp>
2014-04-15 00:55:07 +02:00
2015-08-28 11:28:13 +01:00
# include <stdarg.h>
2014-04-15 00:55:07 +02:00
# include <vector>
2015-09-13 12:15:13 +02:00
# include <unordered_map>
2014-04-15 00:55:07 +02:00
2017-10-23 22:28:18 +02:00
# include <opengl/zone.hxx>
# include <opengl/watchdog.hxx>
2017-03-18 19:21:50 +11:00
# include <osl/conditn.hxx>
2015-09-03 00:30:28 +01:00
# include <vcl/opengl/OpenGLWrapper.hxx>
2015-09-02 17:28:39 +01:00
# include <vcl/opengl/OpenGLContext.hxx>
2016-02-27 14:47:12 +01:00
# include <desktop/crashreport.hxx>
2018-03-15 10:24:30 +02:00
# include <bitmapwriteaccess.hxx>
2015-08-20 17:03:30 +01:00
2018-02-25 02:55:15 +01:00
# if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID && !defined HAIKU
2017-10-23 22:28:18 +02:00
# include <opengl/x11/X11DeviceInfo.hxx>
2014-11-24 16:46:15 +01:00
# elif defined (_WIN32)
2017-10-27 19:24:49 +02:00
# include <opengl/win/WinDeviceInfo.hxx>
2014-11-20 09:52:03 +01:00
# endif
2015-09-02 11:18:42 +01:00
static bool volatile gbInShaderCompile = false ;
2019-09-17 20:39:43 +02:00
OpenGLZone : : AtomicCounter OpenGLZone : : gnEnterCount = 0 ;
OpenGLZone : : AtomicCounter OpenGLZone : : gnLeaveCount = 0 ;
2015-09-02 11:18:42 +01:00
2014-04-15 00:55:07 +02:00
namespace {
2015-09-13 12:15:13 +02:00
using namespace rtl ;
2014-04-15 00:55:07 +02:00
OUString getShaderFolder ( )
{
OUString aUrl ( " $BRAND_BASE_DIR/ " LIBO_ETC_FOLDER ) ;
rtl : : Bootstrap : : expandMacros ( aUrl ) ;
return aUrl + " /opengl/ " ;
}
OString loadShader ( const OUString & rFilename )
{
2014-04-15 09:53:30 +03:00
OUString aFileURL = getShaderFolder ( ) + rFilename + " .glsl " ;
2014-04-15 00:55:07 +02:00
osl : : File aFile ( aFileURL ) ;
if ( aFile . open ( osl_File_OpenFlag_Read ) = = osl : : FileBase : : E_None )
{
sal_uInt64 nSize = 0 ;
aFile . getSize ( nSize ) ;
2015-06-15 17:58:15 +09:00
std : : unique_ptr < char [ ] > content ( new char [ nSize + 1 ] ) ;
2014-04-15 00:55:07 +02:00
sal_uInt64 nBytesRead = 0 ;
2014-05-23 02:01:03 +02:00
aFile . read ( content . get ( ) , nSize , nBytesRead ) ;
2015-01-25 14:36:14 +00:00
assert ( nSize = = nBytesRead ) ;
2015-01-31 21:14:39 +00:00
content . get ( ) [ nBytesRead ] = 0 ;
2016-05-25 15:23:22 +03:00
SAL_INFO ( " vcl.opengl " , " Read " < < nBytesRead < < " bytes from " < < aFileURL ) ;
2019-07-30 17:45:37 +02:00
return content . get ( ) ;
2014-04-15 00:55:07 +02:00
}
else
{
2016-05-25 15:23:22 +03:00
SAL_WARN ( " vcl.opengl " , " Could not open " < < aFileURL ) ;
2014-04-15 00:55:07 +02:00
}
return OString ( ) ;
}
2015-10-08 13:24:12 +03:00
OString & getShaderSource ( const OUString & rFilename )
{
2017-10-19 17:18:17 +02:00
static std : : unordered_map < OUString , OString > aMap ;
2015-10-08 13:24:12 +03:00
if ( aMap . find ( rFilename ) = = aMap . end ( ) )
{
aMap [ rFilename ] = loadShader ( rFilename ) ;
}
return aMap [ rFilename ] ;
}
2014-04-15 00:55:07 +02:00
}
2014-11-18 21:17:52 +00:00
namespace {
2018-10-23 12:06:00 +02:00
int LogCompilerError ( GLuint nId , const OUString & rDetail ,
const OUString & rName , bool bShaderNotProgram )
2014-11-18 21:17:52 +00:00
{
2015-08-20 17:03:30 +01:00
OpenGLZone aZone ;
2014-11-18 21:17:52 +00:00
int InfoLogLength = 0 ;
CHECK_GL_ERROR ( ) ;
if ( bShaderNotProgram )
glGetShaderiv ( nId , GL_INFO_LOG_LENGTH , & InfoLogLength ) ;
else
glGetProgramiv ( nId , GL_INFO_LOG_LENGTH , & InfoLogLength ) ;
CHECK_GL_ERROR ( ) ;
if ( InfoLogLength > 0 )
{
std : : vector < char > ErrorMessage ( InfoLogLength + 1 ) ;
if ( bShaderNotProgram )
2019-04-12 16:38:26 +01:00
glGetShaderInfoLog ( nId , InfoLogLength , nullptr , ErrorMessage . data ( ) ) ;
2014-11-18 21:17:52 +00:00
else
2019-04-12 16:38:26 +01:00
glGetProgramInfoLog ( nId , InfoLogLength , nullptr , ErrorMessage . data ( ) ) ;
2014-11-18 21:17:52 +00:00
CHECK_GL_ERROR ( ) ;
ErrorMessage . push_back ( ' \0 ' ) ;
2019-04-12 16:38:26 +01:00
SAL_WARN ( " vcl.opengl " , rDetail < < " shader " < < nId < < " compile for " < < rName < < " failed : " < < ErrorMessage . data ( ) ) ;
2014-11-18 21:17:52 +00:00
}
else
2015-03-13 15:48:05 +01:00
SAL_WARN ( " vcl.opengl " , rDetail < < " shader: " < < rName < < " compile " < < nId < < " failed without error log " ) ;
2019-04-14 18:01:59 +02:00
# ifdef DBG_UTIL
abort ( ) ;
# endif
2014-11-18 21:17:52 +00:00
return 0 ;
}
}
2015-01-30 01:34:17 +11:00
static void addPreamble ( OString & rShaderSource , const OString & rPreamble )
{
if ( rPreamble . isEmpty ( ) )
return ;
2017-06-27 14:06:38 +02:00
int nVersionStrStartPos = rShaderSource . indexOf ( " #version " ) ;
2015-01-30 01:34:17 +11:00
if ( nVersionStrStartPos = = - 1 )
{
rShaderSource = rPreamble + " \n " + rShaderSource ;
}
else
{
int nVersionStrEndPos = rShaderSource . indexOf ( ' \n ' , nVersionStrStartPos ) ;
2015-03-09 20:59:47 +00:00
SAL_WARN_IF ( nVersionStrEndPos = = - 1 , " vcl.opengl " , " syntax error in shader " ) ;
2015-01-30 01:34:17 +11:00
if ( nVersionStrEndPos = = - 1 )
nVersionStrEndPos = nVersionStrStartPos + 8 ;
2016-03-06 23:54:52 +01:00
OString aVersionLine = rShaderSource . copy ( 0 , nVersionStrEndPos ) ;
OString aShaderBody = rShaderSource . copy ( nVersionStrEndPos + 1 ) ;
2015-01-30 01:34:17 +11:00
rShaderSource = aVersionLine + " \n " + rPreamble + " \n " + aShaderBody ;
}
}
2015-09-13 12:15:13 +02:00
namespace
{
static const sal_uInt32 GLenumSize = sizeof ( GLenum ) ;
OString getHexString ( const sal_uInt8 * pData , sal_uInt32 nLength )
{
2017-01-10 08:09:43 +01:00
static const char * const pHexData = " 0123456789ABCDEF " ;
2015-09-13 12:15:13 +02:00
bool bIsZero = true ;
OStringBuffer aHexStr ;
for ( size_t i = 0 ; i < nLength ; + + i )
{
sal_uInt8 val = pData [ i ] ;
if ( val ! = 0 )
bIsZero = false ;
aHexStr . append ( pHexData [ val & 0xf ] ) ;
aHexStr . append ( pHexData [ val > > 4 ] ) ;
}
if ( bIsZero )
return OString ( ) ;
else
return aHexStr . makeStringAndClear ( ) ;
}
OString generateMD5 ( const void * pData , size_t length )
{
sal_uInt8 pBuffer [ RTL_DIGEST_LENGTH_MD5 ] ;
rtlDigestError aError = rtl_digest_MD5 ( pData , length ,
pBuffer , RTL_DIGEST_LENGTH_MD5 ) ;
SAL_WARN_IF ( aError ! = rtl_Digest_E_None , " vcl.opengl " , " md5 generation failed " ) ;
return getHexString ( pBuffer , RTL_DIGEST_LENGTH_MD5 ) ;
}
2017-09-28 12:40:42 +03:00
OString getDeviceInfoString ( )
{
2018-02-25 02:55:15 +01:00
# if defined( SAL_UNX ) && !defined( MACOSX ) && !defined( IOS )&& !defined( ANDROID ) && !defined( HAIKU )
2017-09-28 12:40:42 +03:00
const X11OpenGLDeviceInfo aInfo ;
return aInfo . GetOS ( ) +
aInfo . GetOSRelease ( ) +
aInfo . GetRenderer ( ) +
aInfo . GetVendor ( ) +
aInfo . GetVersion ( ) ;
# elif defined( _WIN32 )
const WinOpenGLDeviceInfo aInfo ;
return OUStringToOString ( aInfo . GetAdapterVendorID ( ) , RTL_TEXTENCODING_UTF8 ) +
OUStringToOString ( aInfo . GetAdapterDeviceID ( ) , RTL_TEXTENCODING_UTF8 ) +
OUStringToOString ( aInfo . GetDriverVersion ( ) , RTL_TEXTENCODING_UTF8 ) +
OString : : number ( aInfo . GetWindowsVersion ( ) ) ;
# else
return OString ( reinterpret_cast < const char * > ( glGetString ( GL_VENDOR ) ) ) +
OString ( reinterpret_cast < const char * > ( glGetString ( GL_RENDERER ) ) ) +
OString ( reinterpret_cast < const char * > ( glGetString ( GL_VERSION ) ) ) ;
# endif
}
2015-09-13 12:15:13 +02:00
OString getStringDigest ( const OUString & rVertexShaderName ,
const OUString & rFragmentShaderName ,
const OString & rPreamble )
{
// read shaders source
2015-10-08 13:24:12 +03:00
OString aVertexShaderSource = getShaderSource ( rVertexShaderName ) ;
OString aFragmentShaderSource = getShaderSource ( rFragmentShaderName ) ;
2015-09-13 12:15:13 +02:00
// get info about the graphic device
2017-09-28 12:40:42 +03:00
static const OString aDeviceInfo ( getDeviceInfoString ( ) ) ;
2015-09-13 12:15:13 +02:00
OString aMessage ;
aMessage + = rPreamble ;
aMessage + = aVertexShaderSource ;
aMessage + = aFragmentShaderSource ;
aMessage + = aDeviceInfo ;
return generateMD5 ( aMessage . getStr ( ) , aMessage . getLength ( ) ) ;
}
OString getCacheFolder ( )
{
OUString url ( " ${$BRAND_BASE_DIR/ " LIBO_ETC_FOLDER " / " SAL_CONFIGFILE ( " bootstrap " ) " :UserInstallation}/cache/ " ) ;
rtl : : Bootstrap : : expandMacros ( url ) ;
osl : : Directory : : create ( url ) ;
2018-10-23 12:06:00 +02:00
return OUStringToOString ( url , RTL_TEXTENCODING_UTF8 ) ;
2015-09-13 12:15:13 +02:00
}
bool writeProgramBinary ( const OString & rBinaryFileName ,
const std : : vector < sal_uInt8 > & rBinary )
{
2018-10-23 12:06:00 +02:00
osl : : File aFile ( OStringToOUString ( rBinaryFileName , RTL_TEXTENCODING_UTF8 ) ) ;
2015-09-13 12:15:13 +02:00
osl : : FileBase : : RC eStatus = aFile . open (
osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ) ;
if ( eStatus ! = osl : : FileBase : : E_None )
{
// when file already exists we do not have to save it:
// we can be sure that the binary to save is exactly equal
// to the already saved binary, since they have the same hash value
if ( eStatus = = osl : : FileBase : : E_EXIST )
{
2015-10-18 10:59:19 +03:00
SAL_INFO ( " vcl.opengl " ,
2015-09-13 12:15:13 +02:00
" No binary program saved. A file with the same hash already exists: ' " < < rBinaryFileName < < " ' " ) ;
return true ;
}
return false ;
}
sal_uInt64 nBytesWritten = 0 ;
aFile . write ( rBinary . data ( ) , rBinary . size ( ) , nBytesWritten ) ;
assert ( rBinary . size ( ) = = nBytesWritten ) ;
return true ;
}
bool readProgramBinary ( const OString & rBinaryFileName ,
std : : vector < sal_uInt8 > & rBinary )
{
2018-10-23 12:06:00 +02:00
osl : : File aFile ( OStringToOUString ( rBinaryFileName , RTL_TEXTENCODING_UTF8 ) ) ;
2015-09-13 12:15:13 +02:00
if ( aFile . open ( osl_File_OpenFlag_Read ) = = osl : : FileBase : : E_None )
{
sal_uInt64 nSize = 0 ;
aFile . getSize ( nSize ) ;
rBinary . resize ( nSize ) ;
sal_uInt64 nBytesRead = 0 ;
aFile . read ( rBinary . data ( ) , nSize , nBytesRead ) ;
assert ( nSize = = nBytesRead ) ;
2016-05-25 15:33:17 +03:00
VCL_GL_INFO ( " Loading file: ' " < < rBinaryFileName < < " ': success " ) ;
2015-09-13 12:15:13 +02:00
return true ;
}
else
{
2016-05-25 15:33:17 +03:00
VCL_GL_INFO ( " Loading file: ' " < < rBinaryFileName < < " ': FAIL " ) ;
2015-09-13 12:15:13 +02:00
}
return false ;
}
OString createFileName ( const OUString & rVertexShaderName ,
const OUString & rFragmentShaderName ,
2015-12-09 21:39:31 +00:00
const OUString & rGeometryShaderName ,
2015-09-13 12:15:13 +02:00
const OString & rDigest )
{
OString aFileName ;
aFileName + = getCacheFolder ( ) ;
2018-10-23 12:06:00 +02:00
aFileName + = OUStringToOString ( rVertexShaderName , RTL_TEXTENCODING_UTF8 ) + " - " ;
aFileName + = OUStringToOString ( rFragmentShaderName , RTL_TEXTENCODING_UTF8 ) + " - " ;
2015-12-09 21:39:31 +00:00
if ( ! rGeometryShaderName . isEmpty ( ) )
2018-10-23 12:06:00 +02:00
aFileName + = OUStringToOString ( rGeometryShaderName , RTL_TEXTENCODING_UTF8 ) + " - " ;
2015-09-13 12:15:13 +02:00
aFileName + = rDigest + " .bin " ;
return aFileName ;
}
GLint loadProgramBinary ( GLuint nProgramID , const OString & rBinaryFileName )
{
GLint nResult = GL_FALSE ;
GLenum nBinaryFormat ;
std : : vector < sal_uInt8 > aBinary ;
if ( readProgramBinary ( rBinaryFileName , aBinary ) & & aBinary . size ( ) > GLenumSize )
{
GLint nBinaryLength = aBinary . size ( ) - GLenumSize ;
// Extract binary format
2015-09-15 18:51:50 +03:00
sal_uInt8 * pBF = reinterpret_cast < sal_uInt8 * > ( & nBinaryFormat ) ;
2015-09-13 12:15:13 +02:00
for ( size_t i = 0 ; i < GLenumSize ; + + i )
{
pBF [ i ] = aBinary [ nBinaryLength + i ] ;
}
// Load the program
2015-09-15 18:51:50 +03:00
glProgramBinary ( nProgramID , nBinaryFormat , aBinary . data ( ) , nBinaryLength ) ;
2015-09-13 12:15:13 +02:00
// Check the program
glGetProgramiv ( nProgramID , GL_LINK_STATUS , & nResult ) ;
}
return nResult ;
}
void saveProgramBinary ( GLint nProgramID , const OString & rBinaryFileName )
{
GLint nBinaryLength = 0 ;
GLenum nBinaryFormat = GL_NONE ;
glGetProgramiv ( nProgramID , GL_PROGRAM_BINARY_LENGTH , & nBinaryLength ) ;
2017-11-22 09:33:32 +02:00
if ( nBinaryLength < = 0 )
2015-09-13 12:15:13 +02:00
{
SAL_WARN ( " vcl.opengl " , " Binary size is zero " ) ;
return ;
}
std : : vector < sal_uInt8 > aBinary ( nBinaryLength + GLenumSize ) ;
2015-11-10 10:28:29 +01:00
glGetProgramBinary ( nProgramID , nBinaryLength , nullptr , & nBinaryFormat , aBinary . data ( ) ) ;
2015-09-13 12:15:13 +02:00
2015-09-15 18:51:50 +03:00
const sal_uInt8 * pBF = reinterpret_cast < const sal_uInt8 * > ( & nBinaryFormat ) ;
2015-09-13 12:15:13 +02:00
aBinary . insert ( aBinary . end ( ) , pBF , pBF + GLenumSize ) ;
SAL_INFO ( " vcl.opengl " , " Program id: " < < nProgramID ) ;
SAL_INFO ( " vcl.opengl " , " Binary length: " < < nBinaryLength ) ;
SAL_INFO ( " vcl.opengl " , " Binary format: " < < nBinaryFormat ) ;
if ( ! writeProgramBinary ( rBinaryFileName , aBinary ) )
SAL_WARN ( " vcl.opengl " , " Writing binary file ' " < < rBinaryFileName < < " ': FAIL " ) ;
else
2015-10-18 10:59:19 +03:00
SAL_INFO ( " vcl.opengl " , " Writing binary file ' " < < rBinaryFileName < < " ': success " ) ;
2015-09-13 12:15:13 +02:00
}
}
2018-10-23 12:06:00 +02:00
OString OpenGLHelper : : GetDigest ( const OUString & rVertexShaderName ,
2015-09-13 12:15:13 +02:00
const OUString & rFragmentShaderName ,
const OString & rPreamble )
{
return getStringDigest ( rVertexShaderName , rFragmentShaderName , rPreamble ) ;
}
GLint OpenGLHelper : : LoadShaders ( const OUString & rVertexShaderName ,
const OUString & rFragmentShaderName ,
2015-12-09 21:39:31 +00:00
const OUString & rGeometryShaderName ,
2015-09-13 12:15:13 +02:00
const OString & preamble ,
const OString & rDigest )
2014-04-15 00:55:07 +02:00
{
2015-08-20 17:03:30 +01:00
OpenGLZone aZone ;
2015-09-02 11:18:42 +01:00
gbInShaderCompile = true ;
2015-12-09 21:39:31 +00:00
bool bHasGeometryShader = ! rGeometryShaderName . isEmpty ( ) ;
2015-09-13 12:15:13 +02:00
// create the program object
GLint ProgramID = glCreateProgram ( ) ;
// read shaders from file
2015-10-08 13:24:12 +03:00
OString aVertexShaderSource = getShaderSource ( rVertexShaderName ) ;
OString aFragmentShaderSource = getShaderSource ( rFragmentShaderName ) ;
2015-12-09 21:39:31 +00:00
OString aGeometryShaderSource ;
if ( bHasGeometryShader )
aGeometryShaderSource = getShaderSource ( rGeometryShaderName ) ;
2015-09-13 12:15:13 +02:00
2015-09-16 13:11:59 +01:00
GLint bBinaryResult = GL_FALSE ;
2016-11-25 20:25:27 +00:00
if ( epoxy_has_gl_extension ( " GL_ARB_get_program_binary " ) & & ! rDigest . isEmpty ( ) )
2015-09-13 12:15:13 +02:00
{
OString aFileName =
2015-12-09 21:39:31 +00:00
createFileName ( rVertexShaderName , rFragmentShaderName , rGeometryShaderName , rDigest ) ;
2015-09-16 13:11:59 +01:00
bBinaryResult = loadProgramBinary ( ProgramID , aFileName ) ;
2015-09-13 12:15:13 +02:00
CHECK_GL_ERROR ( ) ;
}
2015-09-16 13:11:59 +01:00
if ( bBinaryResult ! = GL_FALSE )
2015-09-13 12:15:13 +02:00
return ProgramID ;
2015-12-09 21:39:31 +00:00
if ( bHasGeometryShader )
VCL_GL_INFO ( " Load shader: vertex " < < rVertexShaderName < < " fragment " < < rFragmentShaderName < < " geometry " < < rGeometryShaderName ) ;
else
VCL_GL_INFO ( " Load shader: vertex " < < rVertexShaderName < < " fragment " < < rFragmentShaderName ) ;
2014-04-15 00:55:07 +02:00
// Create the shaders
GLuint VertexShaderID = glCreateShader ( GL_VERTEX_SHADER ) ;
GLuint FragmentShaderID = glCreateShader ( GL_FRAGMENT_SHADER ) ;
2015-12-09 21:39:31 +00:00
GLuint GeometryShaderID = 0 ;
if ( bHasGeometryShader )
GeometryShaderID = glCreateShader ( GL_GEOMETRY_SHADER ) ;
2014-04-15 00:55:07 +02:00
GLint Result = GL_FALSE ;
// Compile Vertex Shader
2015-01-20 14:48:48 +01:00
if ( ! preamble . isEmpty ( ) )
2015-01-30 01:34:17 +11:00
addPreamble ( aVertexShaderSource , preamble ) ;
2014-04-15 00:55:07 +02:00
char const * VertexSourcePointer = aVertexShaderSource . getStr ( ) ;
2015-11-10 10:28:29 +01:00
glShaderSource ( VertexShaderID , 1 , & VertexSourcePointer , nullptr ) ;
2014-04-15 00:55:07 +02:00
glCompileShader ( VertexShaderID ) ;
// Check Vertex Shader
glGetShaderiv ( VertexShaderID , GL_COMPILE_STATUS , & Result ) ;
2014-11-18 21:17:52 +00:00
if ( ! Result )
return LogCompilerError ( VertexShaderID , " vertex " ,
rVertexShaderName , true ) ;
2014-04-15 00:55:07 +02:00
// Compile Fragment Shader
2015-01-20 14:48:48 +01:00
if ( ! preamble . isEmpty ( ) )
2015-01-30 01:34:17 +11:00
addPreamble ( aFragmentShaderSource , preamble ) ;
2014-04-15 00:55:07 +02:00
char const * FragmentSourcePointer = aFragmentShaderSource . getStr ( ) ;
2015-11-10 10:28:29 +01:00
glShaderSource ( FragmentShaderID , 1 , & FragmentSourcePointer , nullptr ) ;
2014-04-15 00:55:07 +02:00
glCompileShader ( FragmentShaderID ) ;
// Check Fragment Shader
glGetShaderiv ( FragmentShaderID , GL_COMPILE_STATUS , & Result ) ;
2014-11-18 21:17:52 +00:00
if ( ! Result )
return LogCompilerError ( FragmentShaderID , " fragment " ,
rFragmentShaderName , true ) ;
2014-04-15 00:55:07 +02:00
2015-12-09 21:39:31 +00:00
if ( bHasGeometryShader )
{
// Compile Geometry Shader
if ( ! preamble . isEmpty ( ) )
addPreamble ( aGeometryShaderSource , preamble ) ;
char const * GeometrySourcePointer = aGeometryShaderSource . getStr ( ) ;
glShaderSource ( GeometryShaderID , 1 , & GeometrySourcePointer , nullptr ) ;
glCompileShader ( GeometryShaderID ) ;
// Check Geometry Shader
glGetShaderiv ( GeometryShaderID , GL_COMPILE_STATUS , & Result ) ;
if ( ! Result )
return LogCompilerError ( GeometryShaderID , " geometry " ,
rGeometryShaderName , true ) ;
}
2014-04-15 00:55:07 +02:00
// Link the program
glAttachShader ( ProgramID , VertexShaderID ) ;
glAttachShader ( ProgramID , FragmentShaderID ) ;
2015-12-09 21:39:31 +00:00
if ( bHasGeometryShader )
glAttachShader ( ProgramID , GeometryShaderID ) ;
2015-09-13 12:15:13 +02:00
2016-11-25 20:25:27 +00:00
if ( epoxy_has_gl_extension ( " GL_ARB_get_program_binary " ) & & ! rDigest . isEmpty ( ) )
2015-09-13 12:15:13 +02:00
{
glProgramParameteri ( ProgramID , GL_PROGRAM_BINARY_RETRIEVABLE_HINT , GL_TRUE ) ;
glLinkProgram ( ProgramID ) ;
glGetProgramiv ( ProgramID , GL_LINK_STATUS , & Result ) ;
if ( ! Result )
{
SAL_WARN ( " vcl.opengl " , " linking failed: " < < Result ) ;
return LogCompilerError ( ProgramID , " program " , " <both> " , false ) ;
}
OString aFileName =
2015-12-09 21:39:31 +00:00
createFileName ( rVertexShaderName , rFragmentShaderName , rGeometryShaderName , rDigest ) ;
2015-09-13 12:15:13 +02:00
saveProgramBinary ( ProgramID , aFileName ) ;
}
else
{
glLinkProgram ( ProgramID ) ;
}
2014-04-15 00:55:07 +02:00
2014-08-06 23:49:30 +02:00
glDeleteShader ( VertexShaderID ) ;
glDeleteShader ( FragmentShaderID ) ;
2015-12-09 21:39:31 +00:00
if ( bHasGeometryShader )
glDeleteShader ( GeometryShaderID ) ;
2014-08-06 23:49:30 +02:00
2014-04-15 00:55:07 +02:00
// Check the program
glGetProgramiv ( ProgramID , GL_LINK_STATUS , & Result ) ;
2014-11-18 21:17:52 +00:00
if ( ! Result )
return LogCompilerError ( ProgramID , " program " , " <both> " , false ) ;
2014-04-15 00:55:07 +02:00
2014-11-14 15:43:44 +01:00
CHECK_GL_ERROR ( ) ;
2015-09-02 11:18:42 +01:00
// Ensure we bump our counts before we leave the shader zone.
{ OpenGLZone aMakeProgress ; }
gbInShaderCompile = false ;
2014-04-15 00:55:07 +02:00
return ProgramID ;
}
2015-12-09 21:39:31 +00:00
GLint OpenGLHelper : : LoadShaders ( const OUString & rVertexShaderName ,
const OUString & rFragmentShaderName ,
const OString & preamble ,
const OString & rDigest )
{
return LoadShaders ( rVertexShaderName , rFragmentShaderName , OUString ( ) , preamble , rDigest ) ;
}
GLint OpenGLHelper : : LoadShaders ( const OUString & rVertexShaderName ,
const OUString & rFragmentShaderName ,
const OUString & rGeometryShaderName )
{
return LoadShaders ( rVertexShaderName , rFragmentShaderName , rGeometryShaderName , OString ( ) , OString ( ) ) ;
}
GLint OpenGLHelper : : LoadShaders ( const OUString & rVertexShaderName ,
const OUString & rFragmentShaderName )
{
return LoadShaders ( rVertexShaderName , rFragmentShaderName , OUString ( ) , " " , " " ) ;
}
2014-05-09 00:31:05 +02:00
void OpenGLHelper : : renderToFile ( long nWidth , long nHeight , const OUString & rFileName )
{
2015-08-20 17:03:30 +01:00
OpenGLZone aZone ;
2015-06-15 17:58:15 +09:00
std : : unique_ptr < sal_uInt8 [ ] > pBuffer ( new sal_uInt8 [ nWidth * nHeight * 4 ] ) ;
2019-04-14 18:21:04 +02:00
glReadPixels ( 0 , 0 , nWidth , nHeight , OptimalBufferFormat ( ) , GL_UNSIGNED_BYTE , pBuffer . get ( ) ) ;
BitmapEx aBitmap = ConvertBufferToBitmapEx ( pBuffer . get ( ) , nWidth , nHeight ) ;
2014-05-09 00:31:05 +02:00
try {
vcl : : PNGWriter aWriter ( aBitmap ) ;
2015-01-07 09:28:42 +02:00
SvFileStream sOutput ( rFileName , StreamMode : : WRITE ) ;
2014-05-09 00:31:05 +02:00
aWriter . Write ( sOutput ) ;
sOutput . Close ( ) ;
} catch ( . . . ) {
SAL_WARN ( " vcl.opengl " , " Error writing png to " < < rFileName ) ;
}
2014-11-14 15:43:44 +01:00
CHECK_GL_ERROR ( ) ;
2014-05-09 00:31:05 +02:00
}
2019-04-14 18:21:04 +02:00
GLenum OpenGLHelper : : OptimalBufferFormat ( )
{
# ifdef _WIN32
return GL_BGRA ; // OpenGLSalBitmap is internally ScanlineFormat::N24BitTcBgr
# else
return GL_RGBA ; // OpenGLSalBitmap is internally ScanlineFormat::N24BitTcRgb
# endif
}
BitmapEx OpenGLHelper : : ConvertBufferToBitmapEx ( const sal_uInt8 * const pBuffer , long nWidth , long nHeight )
2014-04-27 20:25:52 +02:00
{
assert ( pBuffer ) ;
Bitmap aBitmap ( Size ( nWidth , nHeight ) , 24 ) ;
AlphaMask aAlpha ( Size ( nWidth , nHeight ) ) ;
{
2018-03-15 10:24:30 +02:00
BitmapScopedWriteAccess pWriteAccess ( aBitmap ) ;
AlphaScopedWriteAccess pAlphaWriteAccess ( aAlpha ) ;
2019-04-14 18:21:04 +02:00
# ifdef _WIN32
assert ( pWriteAccess - > GetScanlineFormat ( ) = = ScanlineFormat : : N24BitTcBgr ) ;
assert ( pWriteAccess - > IsTopDown ( ) ) ;
assert ( pAlphaWriteAccess - > IsTopDown ( ) ) ;
# else
assert ( pWriteAccess - > GetScanlineFormat ( ) = = ScanlineFormat : : N24BitTcRgb ) ;
assert ( ! pWriteAccess - > IsTopDown ( ) ) ;
assert ( ! pAlphaWriteAccess - > IsTopDown ( ) ) ;
# endif
assert ( pAlphaWriteAccess - > GetScanlineFormat ( ) = = ScanlineFormat : : N8BitPal ) ;
2014-04-27 20:25:52 +02:00
size_t nCurPos = 0 ;
2015-05-27 16:33:44 +02:00
for ( long y = 0 ; y < nHeight ; + + y )
2014-04-27 20:25:52 +02:00
{
2019-04-14 18:21:04 +02:00
# ifdef _WIN32
2014-04-27 20:25:52 +02:00
Scanline pScan = pWriteAccess - > GetScanline ( y ) ;
Scanline pAlphaScan = pAlphaWriteAccess - > GetScanline ( y ) ;
2019-04-14 18:21:04 +02:00
# else
Scanline pScan = pWriteAccess - > GetScanline ( nHeight - 1 - y ) ;
Scanline pAlphaScan = pAlphaWriteAccess - > GetScanline ( nHeight - 1 - y ) ;
# endif
2015-05-27 16:33:44 +02:00
for ( long x = 0 ; x < nWidth ; + + x )
2014-04-27 20:25:52 +02:00
{
* pScan + + = pBuffer [ nCurPos ] ;
* pScan + + = pBuffer [ nCurPos + 1 ] ;
* pScan + + = pBuffer [ nCurPos + 2 ] ;
nCurPos + = 3 ;
* pAlphaScan + + = static_cast < sal_uInt8 > ( 255 - pBuffer [ nCurPos + + ] ) ;
}
}
}
return BitmapEx ( aBitmap , aAlpha ) ;
}
2014-04-29 08:27:58 +03:00
const char * OpenGLHelper : : GLErrorString ( GLenum errorCode )
{
static const struct {
2018-09-13 13:08:33 +02:00
GLenum const code ;
2014-04-29 08:27:58 +03:00
const char * string ;
} errors [ ] =
{
/* GL */
{ GL_NO_ERROR , " no error " } ,
{ GL_INVALID_ENUM , " invalid enumerant " } ,
{ GL_INVALID_VALUE , " invalid value " } ,
{ GL_INVALID_OPERATION , " invalid operation " } ,
{ GL_STACK_OVERFLOW , " stack overflow " } ,
{ GL_STACK_UNDERFLOW , " stack underflow " } ,
{ GL_OUT_OF_MEMORY , " out of memory " } ,
2014-08-26 19:09:04 +02:00
{ GL_INVALID_FRAMEBUFFER_OPERATION , " invalid framebuffer operation " } ,
2014-04-29 08:27:58 +03:00
2015-11-10 10:28:29 +01:00
{ 0 , nullptr }
2014-04-29 08:27:58 +03:00
} ;
int i ;
for ( i = 0 ; errors [ i ] . string ; i + + )
{
if ( errors [ i ] . code = = errorCode )
{
return errors [ i ] . string ;
}
}
2015-11-10 10:28:29 +01:00
return nullptr ;
2014-04-29 08:27:58 +03:00
}
2014-05-11 18:04:01 +02:00
std : : ostream & operator < < ( std : : ostream & rStrm , const glm : : vec4 & rPos )
{
rStrm < < " ( " < < rPos [ 0 ] < < " , " < < rPos [ 1 ] < < " , " < < rPos [ 2 ] < < " , " < < rPos [ 3 ] < < " ) " ;
return rStrm ;
}
2014-05-23 22:30:24 +02:00
std : : ostream & operator < < ( std : : ostream & rStrm , const glm : : vec3 & rPos )
{
rStrm < < " ( " < < rPos [ 0 ] < < " , " < < rPos [ 1 ] < < " , " < < rPos [ 2 ] < < " ) " ;
return rStrm ;
}
2014-05-11 18:04:01 +02:00
std : : ostream & operator < < ( std : : ostream & rStrm , const glm : : mat4 & rMatrix )
{
for ( int i = 0 ; i < 4 ; + + i )
{
rStrm < < " \n ( " ;
for ( int j = 0 ; j < 4 ; + + j )
{
rStrm < < rMatrix [ j ] [ i ] ;
rStrm < < " " ;
}
rStrm < < " ) \n " ;
}
return rStrm ;
}
2014-08-08 04:52:38 +02:00
void OpenGLHelper : : createFramebuffer ( long nWidth , long nHeight , GLuint & nFramebufferId ,
2018-07-18 15:03:30 +02:00
GLuint & nRenderbufferDepthId , GLuint & nRenderbufferColorId )
2014-05-21 00:41:37 +02:00
{
2015-08-20 17:03:30 +01:00
OpenGLZone aZone ;
2014-05-21 14:23:07 +02:00
// create a renderbuffer for depth attachment
glGenRenderbuffers ( 1 , & nRenderbufferDepthId ) ;
glBindRenderbuffer ( GL_RENDERBUFFER , nRenderbufferDepthId ) ;
2014-05-21 00:41:37 +02:00
glRenderbufferStorage ( GL_RENDERBUFFER , GL_DEPTH_COMPONENT , nWidth , nHeight ) ;
glBindRenderbuffer ( GL_RENDERBUFFER , 0 ) ;
2018-07-18 15:03:30 +02:00
glGenTextures ( 1 , & nRenderbufferColorId ) ;
glBindTexture ( GL_TEXTURE_2D , nRenderbufferColorId ) ;
glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR_MIPMAP_LINEAR ) ;
glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE ) ;
glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE ) ;
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA8 , nWidth , nHeight , 0 ,
GL_RGBA , GL_UNSIGNED_BYTE , nullptr ) ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
glFramebufferTexture2D ( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 ,
GL_TEXTURE_2D , nRenderbufferColorId , 0 ) ;
2014-05-21 00:41:37 +02:00
2014-08-08 04:52:38 +02:00
// create a framebuffer object and attach renderbuffer
2014-05-21 00:41:37 +02:00
glGenFramebuffers ( 1 , & nFramebufferId ) ;
glCheckFramebufferStatus ( GL_FRAMEBUFFER ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , nFramebufferId ) ;
2014-12-23 14:39:22 +02:00
// attach a renderbuffer to FBO color attachment point
2014-05-21 14:23:07 +02:00
glBindRenderbuffer ( GL_RENDERBUFFER , nRenderbufferColorId ) ;
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_RENDERBUFFER , nRenderbufferColorId ) ;
2014-05-21 00:41:37 +02:00
glCheckFramebufferStatus ( GL_FRAMEBUFFER ) ;
// attach a renderbuffer to depth attachment point
2014-05-21 14:23:07 +02:00
glBindRenderbuffer ( GL_RENDERBUFFER , nRenderbufferDepthId ) ;
glFramebufferRenderbuffer ( GL_FRAMEBUFFER , GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER , nRenderbufferDepthId ) ;
2014-08-26 18:02:58 +02:00
GLenum status = glCheckFramebufferStatus ( GL_FRAMEBUFFER ) ;
if ( status ! = GL_FRAMEBUFFER_COMPLETE )
{
SAL_WARN ( " vcl.opengl " , " invalid framebuffer status " ) ;
}
2014-05-21 00:41:37 +02:00
glBindRenderbuffer ( GL_RENDERBUFFER , 0 ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , 0 ) ;
2014-11-14 15:43:44 +01:00
CHECK_GL_ERROR ( ) ;
2014-05-21 00:41:37 +02:00
}
2014-07-20 14:20:35 +02:00
float OpenGLHelper : : getGLVersion ( )
{
float fVersion = 1.0 ;
const GLubyte * aVersion = glGetString ( GL_VERSION ) ;
if ( aVersion & & aVersion [ 0 ] )
{
fVersion = aVersion [ 0 ] - ' 0 ' ;
if ( aVersion [ 1 ] = = ' . ' & & aVersion [ 2 ] )
{
fVersion + = ( aVersion [ 2 ] - ' 0 ' ) / 10.0 ;
}
}
2014-11-14 15:43:44 +01:00
CHECK_GL_ERROR ( ) ;
2014-07-20 14:20:35 +02:00
return fVersion ;
}
2014-08-18 15:38:04 +02:00
void OpenGLHelper : : checkGLError ( const char * pFile , size_t nLine )
{
2015-08-20 17:03:30 +01:00
OpenGLZone aZone ;
2015-08-31 11:18:19 +01:00
int nErrors = 0 ;
2015-08-27 12:13:05 +02:00
for ( ; ; )
2014-08-18 15:38:04 +02:00
{
2015-08-27 12:13:05 +02:00
GLenum glErr = glGetError ( ) ;
if ( glErr = = GL_NO_ERROR )
{
break ;
}
2014-08-18 15:38:04 +02:00
const char * sError = OpenGLHelper : : GLErrorString ( glErr ) ;
2015-11-13 18:47:11 +02:00
if ( ! sError )
sError = " no message available " ;
2014-08-18 15:38:04 +02:00
2015-11-13 18:47:11 +02:00
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 ) ;
2015-08-31 11:18:19 +01:00
// tdf#93798 - apitrace appears to sometimes cause issues with an infinite loop here.
if ( + + nErrors > = 8 )
{
SAL_WARN ( " vcl.opengl " , " Breaking potentially recursive glGetError loop " ) ;
break ;
}
2014-08-18 15:38:04 +02:00
}
}
2014-05-11 18:04:01 +02:00
2014-11-20 09:52:03 +01:00
bool OpenGLHelper : : isDeviceBlacklisted ( )
{
static bool bSet = false ;
static bool bBlacklisted = true ; // assume the worst
if ( ! bSet )
{
2015-08-20 17:03:30 +01:00
OpenGLZone aZone ;
2018-02-25 02:55:15 +01:00
# if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID && !defined HAIKU
2014-11-20 09:52:03 +01:00
X11OpenGLDeviceInfo aInfo ;
bBlacklisted = aInfo . isDeviceBlocked ( ) ;
SAL_INFO ( " vcl.opengl " , " blacklisted: " < < bBlacklisted ) ;
2014-11-24 16:46:15 +01:00
# elif defined( _WIN32 )
WinOpenGLDeviceInfo aInfo ;
bBlacklisted = aInfo . isDeviceBlocked ( ) ;
2016-09-19 18:03:47 +02:00
if ( aInfo . GetWindowsVersion ( ) = = 0x00060001 & & /* Windows 7 */
2016-09-20 09:47:36 +02:00
( aInfo . GetAdapterVendorID ( ) = = " 0x1002 " | | aInfo . GetAdapterVendorID ( ) = = " 0x1022 " ) ) /* AMD */
2016-09-19 18:03:47 +02:00
{
SAL_INFO ( " vcl.opengl " , " Relaxing watchdog timings. " ) ;
OpenGLZone : : relaxWatchdogTimings ( ) ;
}
2014-11-20 09:52:03 +01:00
# else
bBlacklisted = false ;
# endif
bSet = true ;
}
return bBlacklisted ;
}
2014-11-02 00:12:21 +01:00
bool OpenGLHelper : : supportsVCLOpenGL ( )
{
2014-11-10 16:02:31 +00:00
static bool bDisableGL = ! ! getenv ( " SAL_DISABLEGL " ) ;
2014-11-20 09:52:03 +01:00
bool bBlacklisted = isDeviceBlacklisted ( ) ;
2014-11-10 16:02:31 +00:00
2017-04-25 10:33:32 +02:00
return ! bDisableGL & & ! bBlacklisted ;
2014-11-10 16:02:31 +00:00
}
2015-08-25 11:44:41 +01:00
namespace {
static volatile bool gbWatchdogFiring = false ;
2017-03-18 19:21:50 +11:00
static osl : : Condition * gpWatchdogExit = nullptr ;
2016-09-19 18:03:47 +02:00
static WatchdogTimings gWatchdogTimings ;
2015-08-25 11:44:41 +01:00
static rtl : : Reference < OpenGLWatchdogThread > gxWatchdog ;
}
2016-09-19 18:03:47 +02:00
WatchdogTimings : : WatchdogTimings ( )
2017-03-03 11:10:18 +00:00
: maTimingValues {
2016-09-23 20:17:42 +02:00
{ { 6 , 20 } /* 1.5s, 5s */ , { 20 , 120 } /* 5s, 30s */ ,
{ 60 , 240 } /* 15s, 60s */ , { 60 , 240 } /* 15s, 60s */ }
}
2016-09-20 18:36:46 +09:00
, mbRelaxed ( false )
2016-09-19 18:03:47 +02:00
{
}
2015-08-25 11:44:41 +01:00
OpenGLWatchdogThread : : OpenGLWatchdogThread ( )
: salhelper : : Thread ( " OpenGL Watchdog " )
{
}
void OpenGLWatchdogThread : : execute ( )
{
int nUnchanged = 0 ; // how many unchanged nEnters
2016-09-19 18:03:47 +02:00
TimeValue aQuarterSecond ( 0 , 1000 * 1000 * 1000 * 0.25 ) ;
2015-09-02 11:18:42 +01:00
bool bAbortFired = false ;
2015-08-25 11:44:41 +01:00
do {
sal_uInt64 nLastEnters = OpenGLZone : : gnEnterCount ;
2017-03-18 19:21:50 +11:00
gpWatchdogExit - > wait ( & aQuarterSecond ) ;
2015-08-25 11:44:41 +01:00
if ( OpenGLZone : : isInZone ( ) )
{
2015-09-02 11:18:42 +01:00
// The shader compiler can take a long time, first time.
2016-09-20 18:36:46 +09:00
WatchdogTimingMode eMode = gbInShaderCompile ? WatchdogTimingMode : : SHADER_COMPILE : WatchdogTimingMode : : NORMAL ;
WatchdogTimingsValues aTimingValues = gWatchdogTimings . getWatchdogTimingsValues ( eMode ) ;
2015-09-02 11:18:42 +01:00
2015-08-25 11:44:41 +01:00
if ( nLastEnters = = OpenGLZone : : gnEnterCount )
nUnchanged + + ;
else
nUnchanged = 0 ;
SAL_INFO ( " vcl.opengl " , " GL watchdog - unchanged " < <
nUnchanged < < " enter count " < <
2015-09-02 11:18:42 +01:00
OpenGLZone : : gnEnterCount < < " type " < <
2016-09-20 18:36:46 +09:00
( eMode = = WatchdogTimingMode : : SHADER_COMPILE ? " in shader " : " normal gl " ) < <
" breakpoints mid: " < < aTimingValues . mnDisableEntries < <
" max " < < aTimingValues . mnAbortAfter ) ;
2015-08-25 11:44:41 +01:00
// Not making progress
2016-09-20 18:36:46 +09:00
if ( nUnchanged > = aTimingValues . mnDisableEntries )
2015-08-25 11:44:41 +01:00
{
2015-09-02 11:18:42 +01:00
static bool bFired = false ;
if ( ! bFired )
{
gbWatchdogFiring = true ;
SAL_WARN ( " vcl.opengl " , " Watchdog triggered: hard disable GL " ) ;
OpenGLZone : : hardDisable ( ) ;
gbWatchdogFiring = false ;
}
bFired = true ;
// we can hang using VCL in the abort handling -> be impatient
if ( bAbortFired )
{
SAL_WARN ( " vcl.opengl " , " Watchdog gave up: hard exiting " ) ;
_exit ( 1 ) ;
}
2015-08-25 11:44:41 +01:00
}
2015-09-02 11:18:42 +01:00
// Not making even more progress
2016-09-20 18:36:46 +09:00
if ( nUnchanged > = aTimingValues . mnAbortAfter )
2015-08-25 11:44:41 +01:00
{
2015-09-02 11:18:42 +01:00
if ( ! bAbortFired )
{
SAL_WARN ( " vcl.opengl " , " Watchdog gave up: aborting " ) ;
gbWatchdogFiring = true ;
std : : abort ( ) ;
}
2015-09-11 09:40:15 +01:00
// coverity[dead_error_line] - we might have caught SIGABRT and failed to exit yet
2015-09-02 11:18:42 +01:00
bAbortFired = true ;
2015-08-25 11:44:41 +01:00
}
}
else
{
nUnchanged = 0 ;
}
2017-03-18 19:21:50 +11:00
} while ( ! gpWatchdogExit - > check ( ) ) ;
2015-08-25 11:44:41 +01:00
}
void OpenGLWatchdogThread : : start ( )
{
2015-11-10 10:28:29 +01:00
assert ( gxWatchdog = = nullptr ) ;
2017-03-18 19:21:50 +11:00
gpWatchdogExit = new osl : : Condition ( ) ;
2015-11-05 12:55:32 +02:00
gxWatchdog . set ( new OpenGLWatchdogThread ( ) ) ;
2015-08-25 11:44:41 +01:00
gxWatchdog - > launch ( ) ;
}
void OpenGLWatchdogThread : : stop ( )
{
if ( gbWatchdogFiring )
return ; // in watchdog thread
if ( gpWatchdogExit )
2017-03-18 19:21:50 +11:00
gpWatchdogExit - > set ( ) ;
2015-08-25 11:44:41 +01:00
if ( gxWatchdog . is ( ) )
{
gxWatchdog - > join ( ) ;
gxWatchdog . clear ( ) ;
}
2017-05-31 15:40:25 +02:00
delete gpWatchdogExit ;
2015-11-10 10:28:29 +01:00
gpWatchdogExit = nullptr ;
2015-08-25 11:44:41 +01:00
}
2015-08-20 17:03:30 +01:00
/**
2015-08-25 11:44:41 +01:00
* Called from a signal handler or watchdog thread if we get
* a crash or hang in some GL code .
2015-08-20 17:03:30 +01:00
*/
void OpenGLZone : : hardDisable ( )
{
// protect ourselves from double calling etc.
static bool bDisabled = false ;
if ( ! bDisabled )
{
bDisabled = true ;
// Disable the OpenGL support
std : : shared_ptr < comphelper : : ConfigurationChanges > xChanges (
comphelper : : ConfigurationChanges : : create ( ) ) ;
officecfg : : Office : : Common : : VCL : : UseOpenGL : : set ( false , xChanges ) ;
xChanges - > commit ( ) ;
// Force synchronous config write
css : : uno : : Reference < css : : util : : XFlushable > (
css : : configuration : : theDefaultProvider : : get (
comphelper : : getProcessComponentContext ( ) ) ,
css : : uno : : UNO_QUERY_THROW ) - > flush ( ) ;
2015-08-25 11:44:41 +01:00
OpenGLWatchdogThread : : stop ( ) ;
2015-08-20 17:03:30 +01:00
}
}
2016-09-19 18:03:47 +02:00
void OpenGLZone : : relaxWatchdogTimings ( )
{
2016-09-20 18:36:46 +09:00
gWatchdogTimings . setRelax ( true ) ;
2016-09-19 18:03:47 +02:00
}
2016-01-05 16:17:41 +00:00
OpenGLVCLContextZone : : OpenGLVCLContextZone ( )
{
OpenGLContext : : makeVCLCurrent ( ) ;
}
2017-03-28 14:15:37 +01:00
namespace
{
bool bTempOpenGLDisabled = false ;
}
PreDefaultWinNoOpenGLZone : : PreDefaultWinNoOpenGLZone ( )
{
bTempOpenGLDisabled = true ;
}
PreDefaultWinNoOpenGLZone : : ~ PreDefaultWinNoOpenGLZone ( )
{
bTempOpenGLDisabled = false ;
}
2014-11-10 16:02:31 +00:00
bool OpenGLHelper : : isVCLOpenGLEnabled ( )
{
2014-12-18 10:13:40 +01:00
/**
* The ! bSet part should only be called once ! Changing the results in the same
* run will mix OpenGL and normal rendering .
*/
2017-03-28 14:15:37 +01:00
2014-12-18 10:13:40 +01:00
static bool bSet = false ;
static bool bEnable = false ;
static bool bForceOpenGL = false ;
2015-01-24 14:26:25 +11:00
2018-11-23 08:43:04 +01:00
// No hardware rendering, so no OpenGL
if ( Application : : IsBitmapRendering ( ) )
2015-01-24 14:26:25 +11:00
return false ;
2017-03-28 14:15:37 +01:00
//tdf#106155, disable GL while loading certain bitmaps needed for the initial toplevel windows
2018-09-05 02:53:07 +02:00
//under raw X (kde) vclplug
2017-03-28 14:15:37 +01:00
if ( bTempOpenGLDisabled )
return false ;
2014-12-18 10:13:40 +01:00
if ( bSet )
{
return bForceOpenGL | | bEnable ;
}
2014-12-01 01:18:25 +01:00
/*
* There are a number of cases that these environment variables cover :
* * SAL_FORCEGL forces OpenGL independent of any other option
* * SAL_DISABLEGL or a blacklisted driver avoid the use of OpenGL if SAL_FORCEGL is not set
*/
2014-12-18 10:13:40 +01:00
bSet = true ;
bForceOpenGL = ! ! getenv ( " SAL_FORCEGL " ) | | officecfg : : Office : : Common : : VCL : : ForceOpenGL : : get ( ) ;
2015-08-25 11:44:41 +01:00
bool bRet = false ;
2016-09-24 23:50:13 +02:00
bool bSupportsVCLOpenGL = supportsVCLOpenGL ( ) ;
2016-09-01 12:15:44 +02:00
// always call supportsVCLOpenGL to de-zombie the glxtest child process on X11
2016-09-24 23:50:13 +02:00
if ( bForceOpenGL )
2015-11-19 17:03:51 +01:00
{
2015-08-25 11:44:41 +01:00
bRet = true ;
2015-11-19 17:03:51 +01:00
}
2016-09-24 23:50:13 +02:00
else if ( bSupportsVCLOpenGL )
2014-12-18 10:13:40 +01:00
{
2015-08-25 11:44:41 +01:00
static bool bEnableGLEnv = ! ! getenv ( " SAL_ENABLEGL " ) ;
2014-11-12 06:02:48 +01:00
2015-08-25 11:44:41 +01:00
bEnable = bEnableGLEnv ;
2014-11-16 16:59:04 +01:00
2018-10-09 19:29:54 +02:00
if ( officecfg : : Office : : Common : : VCL : : UseOpenGL : : get ( ) )
2015-08-25 11:44:41 +01:00
bEnable = true ;
2014-11-16 16:59:04 +01:00
2018-11-23 08:43:04 +01:00
// Force disable in safe mode
if ( Application : : IsSafeModeEnabled ( ) )
2016-10-17 08:57:46 +02:00
bEnable = false ;
2015-08-25 11:44:41 +01:00
bRet = bEnable ;
}
2016-02-27 14:47:12 +01:00
2015-08-25 11:44:41 +01:00
if ( bRet )
2015-08-31 21:29:21 +01:00
{
2015-09-02 16:08:11 +01:00
if ( ! getenv ( " SAL_DISABLE_GL_WATCHDOG " ) )
OpenGLWatchdogThread : : start ( ) ;
2015-08-31 21:29:21 +01:00
}
2019-09-25 11:21:17 +02:00
CrashReporter : : addKeyValue ( " UseOpenGL " , OUString : : boolean ( bRet ) ) ;
2014-11-16 16:59:04 +01:00
2015-08-25 11:44:41 +01:00
return bRet ;
2014-11-02 00:12:21 +01:00
}
2015-09-03 00:30:28 +01:00
bool OpenGLWrapper : : isVCLOpenGLEnabled ( )
{
return OpenGLHelper : : isVCLOpenGLEnabled ( ) ;
}
2015-11-20 10:14:49 +01:00
void OpenGLHelper : : debugMsgStream ( std : : ostringstream const & pStream )
2015-08-28 11:28:13 +01:00
{
2016-07-25 13:58:44 +09:00
debugMsgPrint ( 0 , " %x: %s " , osl_getThreadIdentifier ( nullptr ) , pStream . str ( ) . c_str ( ) ) ;
2015-08-28 11:28:13 +01:00
}
2016-07-25 13:58:44 +09:00
void OpenGLHelper : : debugMsgStreamWarn ( std : : ostringstream const & pStream )
{
debugMsgPrint ( 1 , " %x: %s " , osl_getThreadIdentifier ( nullptr ) , pStream . str ( ) . c_str ( ) ) ;
}
void OpenGLHelper : : debugMsgPrint ( const int nType , const char * pFormat , . . . )
2015-08-28 11:28:13 +01:00
{
va_list aArgs ;
va_start ( aArgs , pFormat ) ;
2015-09-02 17:28:39 +01:00
char pStr [ 1044 ] ;
2015-08-28 11:28:13 +01:00
# ifdef _WIN32
# define vsnprintf _vsnprintf
# endif
vsnprintf ( pStr , sizeof ( pStr ) , pFormat , aArgs ) ;
2015-09-02 17:28:39 +01:00
pStr [ sizeof ( pStr ) - 20 ] = ' \0 ' ;
2015-09-02 22:14:10 +01:00
bool bHasContext = OpenGLContext : : hasCurrent ( ) ;
2015-09-02 17:28:39 +01:00
if ( ! bHasContext )
2016-07-28 04:17:00 +09:00
strcat ( pStr , " (no GL context) " ) ;
2015-08-28 11:28:13 +01:00
2016-07-25 13:58:44 +09:00
if ( nType = = 0 )
{
SAL_INFO ( " vcl.opengl " , pStr ) ;
}
else if ( nType = = 1 )
{
SAL_WARN ( " vcl.opengl " , pStr ) ;
}
2015-08-28 11:28:13 +01:00
2015-09-02 17:28:39 +01:00
if ( bHasContext )
{
OpenGLZone aZone ;
2015-08-28 11:28:13 +01:00
2016-11-25 20:25:27 +00:00
if ( epoxy_has_gl_extension ( " GL_KHR_debug " ) )
2015-09-02 17:28:39 +01:00
glDebugMessageInsert ( GL_DEBUG_SOURCE_APPLICATION ,
GL_DEBUG_TYPE_OTHER ,
1 , // one[sic] id is as good as another ?
// GL_DEBUG_SEVERITY_NOTIFICATION for >= GL4.3 ?
GL_DEBUG_SEVERITY_LOW ,
strlen ( pStr ) , pStr ) ;
2016-11-25 20:25:27 +00:00
else if ( epoxy_has_gl_extension ( " GL_AMD_debug_output " ) )
2015-09-02 17:28:39 +01:00
glDebugMessageInsertAMD ( GL_DEBUG_CATEGORY_APPLICATION_AMD ,
GL_DEBUG_SEVERITY_LOW_AMD ,
1 , // one[sic] id is as good as another ?
strlen ( pStr ) , pStr ) ;
}
2015-08-28 11:28:13 +01:00
va_end ( aArgs ) ;
}
2014-04-15 00:55:07 +02:00
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */