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