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>
2014-04-15 00:55:07 +02:00
# include <config_folders.h>
2014-04-18 15:24:25 +02:00
# include <vcl/salbtype.hxx>
2016-02-06 21:03:41 +11:00
# include <vcl/bitmapaccess.hxx>
2015-06-15 17:58:15 +09:00
# include <memory>
2014-05-09 00:31:05 +02:00
# include <vcl/pngwrite.hxx>
# include <vcl/graph.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 <deque>
# include <unordered_map>
2014-04-15 00:55:07 +02:00
2015-08-31 21:29:21 +01:00
# include "svdata.hxx"
2015-09-16 09:17:37 +01:00
# include "salgdi.hxx"
2015-09-02 11:18:42 +01:00
# include "salinst.hxx"
2015-08-20 17:03:30 +01:00
# include "opengl/zone.hxx"
2015-08-25 11:44:41 +01:00
# include "opengl/watchdog.hxx"
# include <osl/conditn.h>
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>
2015-08-20 17:03:30 +01:00
2014-11-20 09:52:03 +01:00
# if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID
# include "opengl/x11/X11DeviceInfo.hxx"
2014-11-24 16:46:15 +01:00
# elif defined (_WIN32)
# 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 ;
sal_uInt64 volatile OpenGLZone : : gnEnterCount = 0 ;
sal_uInt64 volatile OpenGLZone : : gnLeaveCount = 0 ;
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 ) ;
2014-05-23 02:01:03 +02:00
return OString ( 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 )
{
static std : : unordered_map < OUString , OString , OUStringHash > aMap ;
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 {
int LogCompilerError ( GLuint nId , const rtl : : OUString & rDetail ,
const rtl : : OUString & rName , bool bShaderNotProgram )
{
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 )
2015-11-10 10:28:29 +01:00
glGetShaderInfoLog ( nId , InfoLogLength , nullptr , & ErrorMessage [ 0 ] ) ;
2014-11-18 21:17:52 +00:00
else
2015-11-10 10:28:29 +01:00
glGetProgramInfoLog ( nId , InfoLogLength , nullptr , & ErrorMessage [ 0 ] ) ;
2014-11-18 21:17:52 +00:00
CHECK_GL_ERROR ( ) ;
ErrorMessage . push_back ( ' \0 ' ) ;
SAL_WARN ( " vcl.opengl " , rDetail < < " shader " < < nId < < " compile for " < < rName < < " failed : " < < & ErrorMessage [ 0 ] ) ;
}
else
2015-03-13 15:48:05 +01:00
SAL_WARN ( " vcl.opengl " , rDetail < < " shader: " < < rName < < " compile " < < nId < < " failed without error log " ) ;
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 ;
OString aVersionStr ( " #version " ) ;
2015-08-11 08:42:56 +02:00
int nVersionStrStartPos = rShaderSource . indexOf ( aVersionStr ) ;
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 )
{
static const char * pHexData = " 0123456789ABCDEF " ;
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 ) ;
}
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
# if defined( SAL_UNX ) && !defined( MACOSX ) && !defined( IOS )&& !defined( ANDROID )
static const X11OpenGLDeviceInfo aInfo ;
static const OString aDeviceInfo (
aInfo . GetOS ( ) +
aInfo . GetOSRelease ( ) +
aInfo . GetRenderer ( ) +
aInfo . GetVendor ( ) +
aInfo . GetVersion ( ) ) ;
# elif defined( _WIN32 )
static const WinOpenGLDeviceInfo aInfo ;
static const OString aDeviceInfo (
OUStringToOString ( aInfo . GetAdapterVendorID ( ) , RTL_TEXTENCODING_UTF8 ) +
OUStringToOString ( aInfo . GetAdapterDeviceID ( ) , RTL_TEXTENCODING_UTF8 ) +
OUStringToOString ( aInfo . GetDriverVersion ( ) , RTL_TEXTENCODING_UTF8 ) +
OString : : number ( aInfo . GetWindowsVersion ( ) ) ) ;
# else
static const OString aDeviceInfo (
2015-09-15 21:51:10 +02:00
OString ( reinterpret_cast < const char * > ( glGetString ( GL_VENDOR ) ) ) +
OString ( reinterpret_cast < const char * > ( glGetString ( GL_RENDERER ) ) ) +
OString ( reinterpret_cast < const char * > ( glGetString ( GL_VERSION ) ) ) ) ;
2015-09-13 12:15:13 +02:00
# endif
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 ) ;
return rtl : : OUStringToOString ( url , RTL_TEXTENCODING_UTF8 ) ;
}
bool writeProgramBinary ( const OString & rBinaryFileName ,
const std : : vector < sal_uInt8 > & rBinary )
{
osl : : File aFile ( rtl : : OStringToOUString ( rBinaryFileName , RTL_TEXTENCODING_UTF8 ) ) ;
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 )
{
osl : : File aFile ( rtl : : OStringToOUString ( rBinaryFileName , RTL_TEXTENCODING_UTF8 ) ) ;
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 ( ) ;
aFileName + = rtl : : OUStringToOString ( rVertexShaderName , RTL_TEXTENCODING_UTF8 ) + " - " ;
aFileName + = rtl : : OUStringToOString ( rFragmentShaderName , RTL_TEXTENCODING_UTF8 ) + " - " ;
2015-12-09 21:39:31 +00:00
if ( ! rGeometryShaderName . isEmpty ( ) )
aFileName + = rtl : : 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 ) ;
if ( ! ( nBinaryLength > 0 ) )
{
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
}
}
rtl : : OString OpenGLHelper : : GetDigest ( const OUString & rVertexShaderName ,
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 ;
2015-09-13 12:15:13 +02:00
if ( GLEW_ARB_get_program_binary & & ! rDigest . isEmpty ( ) )
{
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
if ( GLEW_ARB_get_program_binary & & ! rDigest . isEmpty ( ) )
{
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-09-17 08:04:36 +02:00
void OpenGLHelper : : ConvertBitmapExToRGBATextureBuffer ( const BitmapEx & rBitmapEx , sal_uInt8 * o_pRGBABuffer , const bool bFlip )
2014-04-18 15:24:25 +02:00
{
long nBmpWidth = rBitmapEx . GetSizePixel ( ) . Width ( ) ;
long nBmpHeight = rBitmapEx . GetSizePixel ( ) . Height ( ) ;
Bitmap aBitmap ( rBitmapEx . GetBitmap ( ) ) ;
AlphaMask aAlpha ( rBitmapEx . GetAlpha ( ) ) ;
Bitmap : : ScopedReadAccess pReadAccces ( aBitmap ) ;
AlphaMask : : ScopedReadAccess pAlphaReadAccess ( aAlpha ) ;
size_t i = 0 ;
2014-09-17 08:04:36 +02:00
for ( long ny = ( bFlip ? nBmpHeight - 1 : 0 ) ; ( bFlip ? ny > = 0 : ny < nBmpHeight ) ; ( bFlip ? ny - - : ny + + ) )
2014-04-18 15:24:25 +02:00
{
2015-11-10 10:28:29 +01:00
Scanline pAScan = pAlphaReadAccess ? pAlphaReadAccess - > GetScanline ( ny ) : nullptr ;
2014-04-18 15:24:25 +02:00
for ( long nx = 0 ; nx < nBmpWidth ; nx + + )
{
BitmapColor aCol = pReadAccces - > GetColor ( ny , nx ) ;
2014-08-04 12:04:10 +02:00
o_pRGBABuffer [ i + + ] = aCol . GetRed ( ) ;
o_pRGBABuffer [ i + + ] = aCol . GetGreen ( ) ;
o_pRGBABuffer [ i + + ] = aCol . GetBlue ( ) ;
o_pRGBABuffer [ i + + ] = pAScan ? 255 - * pAScan + + : 255 ;
2014-04-18 15:24:25 +02:00
}
}
}
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 ] ) ;
2014-05-09 00:31:05 +02:00
glReadPixels ( 0 , 0 , nWidth , nHeight , GL_BGRA , GL_UNSIGNED_BYTE , pBuffer . get ( ) ) ;
BitmapEx aBitmap = ConvertBGRABufferToBitmapEx ( pBuffer . get ( ) , nWidth , nHeight ) ;
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
}
2014-04-27 20:25:52 +02:00
BitmapEx OpenGLHelper : : ConvertBGRABufferToBitmapEx ( const sal_uInt8 * const pBuffer , long nWidth , long nHeight )
{
assert ( pBuffer ) ;
Bitmap aBitmap ( Size ( nWidth , nHeight ) , 24 ) ;
AlphaMask aAlpha ( Size ( nWidth , nHeight ) ) ;
{
Bitmap : : ScopedWriteAccess pWriteAccess ( aBitmap ) ;
AlphaMask : : ScopedWriteAccess pAlphaWriteAccess ( aAlpha ) ;
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
{
Scanline pScan = pWriteAccess - > GetScanline ( y ) ;
Scanline pAlphaScan = pAlphaWriteAccess - > GetScanline ( y ) ;
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 {
GLenum code ;
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 ,
GLuint & nRenderbufferDepthId , GLuint & nRenderbufferColorId , bool bRenderbuffer )
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 ) ;
2014-08-08 04:52:38 +02:00
if ( bRenderbuffer )
{
// create a renderbuffer for color attachment
glGenRenderbuffers ( 1 , & nRenderbufferColorId ) ;
glBindRenderbuffer ( GL_RENDERBUFFER , nRenderbufferColorId ) ;
glRenderbufferStorage ( GL_RENDERBUFFER , GL_RGBA8 , nWidth , nHeight ) ;
glBindRenderbuffer ( GL_RENDERBUFFER , 0 ) ;
}
else
{
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 ,
2015-11-10 10:28:29 +01:00
GL_RGBA , GL_UNSIGNED_BYTE , nullptr ) ;
2014-08-08 04:52:38 +02:00
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 ;
2014-11-20 09:52:03 +01:00
# if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID
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 ( ) ;
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
2014-11-20 09:52:03 +01:00
if ( bDisableGL | | bBlacklisted )
2014-11-10 16:02:31 +00:00
return false ;
else
return true ;
}
2015-08-25 11:44:41 +01:00
void OpenGLZone : : enter ( ) { gnEnterCount + + ; }
void OpenGLZone : : leave ( ) { gnLeaveCount + + ; }
namespace {
static volatile bool gbWatchdogFiring = false ;
2015-11-10 10:28:29 +01:00
static oslCondition gpWatchdogExit = nullptr ;
2015-08-25 11:44:41 +01:00
static rtl : : Reference < OpenGLWatchdogThread > gxWatchdog ;
}
OpenGLWatchdogThread : : OpenGLWatchdogThread ( )
: salhelper : : Thread ( " OpenGL Watchdog " )
{
}
void OpenGLWatchdogThread : : execute ( )
{
2015-09-02 11:18:42 +01:00
// delays to take various actions in 1/4 of a second increments.
static const int nDisableEntries [ 2 ] = { 6 /* 1.5s */ , 20 /* 5s */ } ;
static const int nAbortAfter [ 2 ] = { 20 /* 10s */ , 120 /* 30s */ } ;
2015-08-25 11:44:41 +01:00
int nUnchanged = 0 ; // how many unchanged nEnters
2015-09-02 11:18:42 +01:00
TimeValue aHalfSecond ( 0 , 1000 * 1000 * 1000 * 0.25 ) ;
bool bAbortFired = false ;
2015-08-25 11:44:41 +01:00
do {
sal_uInt64 nLastEnters = OpenGLZone : : gnEnterCount ;
osl_waitCondition ( gpWatchdogExit , & aHalfSecond ) ;
if ( OpenGLZone : : isInZone ( ) )
{
2015-09-02 11:18:42 +01:00
int nType = 0 ;
// The shader compiler can take a long time, first time.
if ( gbInShaderCompile )
nType = 1 ;
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 " < <
( nType ? " in shader " : " normal gl " ) < <
" breakpoints mid: " < < nDisableEntries [ nType ] < <
" max " < < nAbortAfter [ nType ] ) ;
2015-08-25 11:44:41 +01:00
// Not making progress
2015-09-02 11:18:42 +01:00
if ( nUnchanged > = nDisableEntries [ nType ] )
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
if ( nUnchanged > = nAbortAfter [ nType ] )
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 ;
}
} while ( ! osl_checkCondition ( gpWatchdogExit ) ) ;
}
void OpenGLWatchdogThread : : start ( )
{
2015-11-10 10:28:29 +01:00
assert ( gxWatchdog = = nullptr ) ;
2015-08-25 11:44:41 +01:00
gpWatchdogExit = osl_createCondition ( ) ;
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 )
osl_setCondition ( gpWatchdogExit ) ;
if ( gxWatchdog . is ( ) )
{
gxWatchdog - > join ( ) ;
gxWatchdog . clear ( ) ;
}
if ( gpWatchdogExit )
osl_destroyCondition ( 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-01-05 16:17:41 +00:00
OpenGLVCLContextZone : : OpenGLVCLContextZone ( )
{
OpenGLContext : : makeVCLCurrent ( ) ;
}
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 .
*/
static bool bSet = false ;
static bool bEnable = false ;
static bool bForceOpenGL = false ;
2015-01-24 14:26:25 +11:00
// If we are a console app, then we don't use OpenGL
if ( Application : : IsConsoleOnly ( ) )
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
* * SAL_ENABLEGL overrides VCL_HIDE_WINDOWS and the configuration variable
* * the configuration variable is checked if no environment variable is 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 ;
2014-12-01 01:18:25 +01: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
}
2015-08-25 11:44:41 +01:00
else if ( ! supportsVCLOpenGL ( ) )
2015-11-19 17:03:51 +01:00
{
2015-08-25 11:44:41 +01:00
bRet = false ;
2015-11-19 17:03:51 +01:00
}
2015-08-25 11:44:41 +01:00
else
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
2015-08-25 11:44:41 +01:00
static bool bDuringBuild = getenv ( " VCL_HIDE_WINDOWS " ) ;
if ( bDuringBuild & & ! bEnable /* env. enable overrides */ )
bEnable = false ;
else if ( officecfg : : Office : : Common : : VCL : : UseOpenGL : : get ( ) )
bEnable = true ;
2014-11-16 16:59:04 +01:00
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
}
2016-02-27 14:47:12 +01: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 )
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
2015-09-02 17:28:39 +01:00
if ( GLEW_KHR_debug )
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 ) ;
else if ( GLEW_AMD_debug_output )
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 ) ;
}
2015-09-16 09:17:37 +01:00
OutputDevice : : PaintScope : : PaintScope ( OutputDevice * pDev )
2015-11-10 10:28:29 +01:00
: pHandle ( nullptr )
2015-09-16 09:17:37 +01:00
{
if ( pDev - > mpGraphics | | pDev - > AcquireGraphics ( ) )
{
}
}
/**
* Flush all the queued rendering commands to the screen for this context .
*/
void OutputDevice : : PaintScope : : flush ( )
{
if ( pHandle )
{
OpenGLContext * pContext = static_cast < OpenGLContext * > ( pHandle ) ;
2015-11-10 10:28:29 +01:00
pHandle = nullptr ;
2015-09-16 09:17:37 +01:00
pContext - > mnPainting - - ;
assert ( pContext - > mnPainting > = 0 ) ;
if ( pContext - > mnPainting = = 0 )
{
pContext - > makeCurrent ( ) ;
pContext - > AcquireDefaultFramebuffer ( ) ;
glFlush ( ) ;
pContext - > swapBuffers ( ) ;
CHECK_GL_ERROR ( ) ;
}
pContext - > release ( ) ;
}
}
OutputDevice : : PaintScope : : ~ PaintScope ( )
{
flush ( ) ;
}
2014-04-15 00:55:07 +02:00
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */