So far this code apparently has not been invoked except in the very special use cases for which it has been developed, and which presumably aren't such that one would come across in "normal" LibreOffice use. But now it gets invoked unconditionally, in some unit tests even, and letting it call glewInit(), which calls glGetString(), without any OpenGL context, leads to a crash. So instead, explicitly fail early on OS X (and iOS and Android). Change-Id: Ia4547cc1ddff1aaa0190fcdb69506ad444214b7a
799 lines
22 KiB
C++
799 lines
22 KiB
C++
/* -*- 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/.
|
|
*/
|
|
|
|
#include <vcl/opengl/OpenGLContext.hxx>
|
|
#include <vcl/opengl/OpenGLHelper.hxx>
|
|
#include <vcl/syschild.hxx>
|
|
#include <vcl/sysdata.hxx>
|
|
|
|
#include <boost/scoped_array.hpp>
|
|
#include <vcl/pngwrite.hxx>
|
|
#include <vcl/bmpacc.hxx>
|
|
#include <vcl/graph.hxx>
|
|
|
|
using namespace com::sun::star;
|
|
|
|
OpenGLContext::OpenGLContext():
|
|
mpWindow(NULL),
|
|
m_pChildWindow(NULL),
|
|
mbInitialized(false)
|
|
{
|
|
}
|
|
|
|
OpenGLContext::~OpenGLContext()
|
|
{
|
|
#if defined( WNT )
|
|
if (m_aGLWin.hRC)
|
|
{
|
|
wglMakeCurrent( m_aGLWin.hDC, 0 );
|
|
wglDeleteContext( m_aGLWin.hRC );
|
|
ReleaseDC( m_aGLWin.hWnd, m_aGLWin.hDC );
|
|
}
|
|
#elif defined( MACOSX ) || defined( IOS ) || defined( ANDROID )
|
|
// nothing
|
|
#elif defined( UNX )
|
|
if(m_aGLWin.ctx)
|
|
{
|
|
glXMakeCurrent(m_aGLWin.dpy, None, NULL);
|
|
if( glGetError() != GL_NO_ERROR )
|
|
{
|
|
SAL_WARN("vcl.opengl", "glError: " << (char *)gluErrorString(glGetError()));
|
|
}
|
|
glXDestroyContext(m_aGLWin.dpy, m_aGLWin.ctx);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if defined( _WIN32 )
|
|
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_CREATE:
|
|
return 0;
|
|
case WM_CLOSE:
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
case WM_DESTROY:
|
|
return 0;
|
|
case WM_KEYDOWN:
|
|
switch(wParam)
|
|
{
|
|
case VK_ESCAPE:
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
|
|
case VK_SPACE:
|
|
break;
|
|
}
|
|
default:
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
int InitTempWindow(HWND *hwnd, int width, int height, PIXELFORMATDESCRIPTOR inPfd, GLWindow glWin)
|
|
{
|
|
PIXELFORMATDESCRIPTOR pfd = inPfd;
|
|
int pfmt;
|
|
int ret;
|
|
WNDCLASS wc;
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = WndProc;
|
|
wc.cbClsExtra = wc.cbWndExtra = 0;
|
|
wc.hInstance = NULL;
|
|
wc.hIcon = NULL;
|
|
wc.hCursor = NULL;
|
|
wc.hbrBackground = NULL;
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = (LPCSTR)"GLRenderer";
|
|
RegisterClass(&wc);
|
|
*hwnd = CreateWindow(wc.lpszClassName, NULL, WS_DISABLED, 0, 0, width, height, NULL, NULL, wc.hInstance, NULL);
|
|
glWin.hDC = GetDC(*hwnd);
|
|
pfmt = ChoosePixelFormat(glWin.hDC, &pfd);
|
|
if (!pfmt)
|
|
{
|
|
return -1;
|
|
}
|
|
ret = SetPixelFormat(glWin.hDC, pfmt, &pfd);
|
|
if(!ret)
|
|
{
|
|
return -1;
|
|
}
|
|
glWin.hRC = wglCreateContext(glWin.hDC);
|
|
if(!(glWin.hRC))
|
|
{
|
|
return -1;
|
|
}
|
|
ret = wglMakeCurrent(glWin.hDC, glWin.hRC);
|
|
if(!ret)
|
|
{
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool WGLisExtensionSupported(const char *extension)
|
|
{
|
|
const size_t extlen = strlen(extension);
|
|
const char *supported = NULL;
|
|
|
|
// Try To Use wglGetExtensionStringARB On Current DC, If Possible
|
|
PROC wglGetExtString = wglGetProcAddress("wglGetExtensionsStringARB");
|
|
|
|
if (wglGetExtString)
|
|
supported = ((char*(__stdcall*)(HDC))wglGetExtString)(wglGetCurrentDC());
|
|
// If That Failed, Try Standard Opengl Extensions String
|
|
if (supported == NULL)
|
|
supported = (char*)glGetString(GL_EXTENSIONS);
|
|
// If That Failed Too, Must Be No Extensions Supported
|
|
if (supported == NULL)
|
|
return 0;
|
|
|
|
// Begin Examination At Start Of String, Increment By 1 On False Match
|
|
for (const char* p = supported; ; p++)
|
|
{
|
|
// Advance p Up To The Next Possible Match
|
|
p = strstr(p, extension);
|
|
|
|
if (p == NULL)
|
|
return 0; // No Match
|
|
|
|
// Make Sure That Match Is At The Start Of The String Or That
|
|
// The Previous Char Is A Space, Or Else We Could Accidentally
|
|
// Match "wglFunkywglExtension" With "wglExtension"
|
|
|
|
// Also, Make Sure That The Following Character Is Space Or NULL
|
|
// Or Else "wglExtensionTwo" Might Match "wglExtension"
|
|
if ((p==supported || p[-1]==' ') && (p[extlen]=='\0' || p[extlen]==' '))
|
|
return 1; // Match
|
|
}
|
|
}
|
|
|
|
bool InitMultisample(PIXELFORMATDESCRIPTOR pfd, int& rPixelFormat)
|
|
{
|
|
HWND hWnd = NULL;
|
|
GLWindow glWin;
|
|
//create a temp windwo to check whether support multi-sample, if support, get the format
|
|
if (InitTempWindow(&hWnd, 1, 1, pfd, glWin) < 0)
|
|
{
|
|
SAL_WARN("vcl.opengl", "Can't create temp window to test");
|
|
return false;
|
|
}
|
|
|
|
// See If The String Exists In WGL!
|
|
if (!WGLisExtensionSupported("WGL_ARB_multisample"))
|
|
{
|
|
SAL_WARN("vcl.opengl", "Device doesn't support multi sample");
|
|
return false;
|
|
}
|
|
// Get Our Pixel Format
|
|
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
|
|
if (!wglChoosePixelFormatARB)
|
|
{
|
|
return false;
|
|
}
|
|
// Get Our Current Device Context
|
|
HDC hDC = GetDC(hWnd);
|
|
|
|
int pixelFormat;
|
|
int valid;
|
|
UINT numFormats;
|
|
float fAttributes[] = {0,0};
|
|
// These Attributes Are The Bits We Want To Test For In Our Sample
|
|
// Everything Is Pretty Standard, The Only One We Want To
|
|
// Really Focus On Is The SAMPLE BUFFERS ARB And WGL SAMPLES
|
|
// These Two Are Going To Do The Main Testing For Whether Or Not
|
|
// We Support Multisampling On This Hardware.
|
|
int iAttributes[] =
|
|
{
|
|
WGL_DRAW_TO_WINDOW_ARB,GL_TRUE,
|
|
WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
|
|
WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
|
|
WGL_COLOR_BITS_ARB,24,
|
|
WGL_ALPHA_BITS_ARB,8,
|
|
WGL_DEPTH_BITS_ARB,16,
|
|
WGL_STENCIL_BITS_ARB,0,
|
|
WGL_DOUBLE_BUFFER_ARB,GL_TRUE,
|
|
WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
|
|
WGL_SAMPLES_ARB,8,
|
|
0,0
|
|
};
|
|
|
|
bool bArbMultisampleSupported = true;
|
|
|
|
// First We Check To See If We Can Get A Pixel Format For 4 Samples
|
|
valid = wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
|
|
// If We Returned True, And Our Format Count Is Greater Than 1
|
|
if (valid && numFormats >= 1)
|
|
{
|
|
bArbMultisampleSupported = true;
|
|
rPixelFormat = pixelFormat;
|
|
wglMakeCurrent(NULL, NULL);
|
|
wglDeleteContext(glWin.hRC);
|
|
ReleaseDC(hWnd, glWin.hDC);
|
|
DestroyWindow(hWnd);
|
|
return bArbMultisampleSupported;
|
|
}
|
|
// Our Pixel Format With 4 Samples Failed, Test For 2 Samples
|
|
iAttributes[19] = 2;
|
|
valid = wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
|
|
if (valid && numFormats >= 1)
|
|
{
|
|
bArbMultisampleSupported = true;
|
|
rPixelFormat = pixelFormat;
|
|
wglMakeCurrent(NULL, NULL);
|
|
wglDeleteContext(glWin.hRC);
|
|
ReleaseDC(hWnd, glWin.hDC);
|
|
DestroyWindow(hWnd);
|
|
return bArbMultisampleSupported;
|
|
}
|
|
// Return The Valid Format
|
|
wglMakeCurrent(NULL, NULL);
|
|
wglDeleteContext(glWin.hRC);
|
|
ReleaseDC(hWnd, glWin.hDC);
|
|
DestroyWindow(hWnd);
|
|
|
|
return bArbMultisampleSupported;
|
|
}
|
|
#endif
|
|
|
|
#ifdef DBG_UTIL
|
|
|
|
namespace {
|
|
|
|
const char* getSeverityString(GLenum severity)
|
|
{
|
|
switch(severity)
|
|
{
|
|
case GL_DEBUG_SEVERITY_LOW:
|
|
return "low";
|
|
case GL_DEBUG_SEVERITY_MEDIUM:
|
|
return "medium";
|
|
case GL_DEBUG_SEVERITY_HIGH:
|
|
return "high";
|
|
default:
|
|
;
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
const char* getSourceString(GLenum source)
|
|
{
|
|
switch(source)
|
|
{
|
|
case GL_DEBUG_SOURCE_API:
|
|
return "API";
|
|
case GL_DEBUG_SOURCE_SHADER_COMPILER:
|
|
return "shader compiler";
|
|
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
|
|
return "window system";
|
|
case GL_DEBUG_SOURCE_THIRD_PARTY:
|
|
return "third party";
|
|
case GL_DEBUG_SOURCE_APPLICATION:
|
|
return "Libreoffice";
|
|
case GL_DEBUG_SOURCE_OTHER:
|
|
return "unknown";
|
|
default:
|
|
;
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
const char* getTypeString(GLenum type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
|
|
return "deprecated behavior";
|
|
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
|
|
return "undefined behavior";
|
|
case GL_DEBUG_TYPE_PERFORMANCE:
|
|
return "performance";
|
|
case GL_DEBUG_TYPE_PORTABILITY:
|
|
return "portability";
|
|
case GL_DEBUG_TYPE_MARKER:
|
|
return "marker";
|
|
case GL_DEBUG_TYPE_PUSH_GROUP:
|
|
return "push group";
|
|
case GL_DEBUG_TYPE_POP_GROUP:
|
|
return "pop group";
|
|
case GL_DEBUG_TYPE_OTHER:
|
|
return "other";
|
|
case GL_DEBUG_TYPE_ERROR:
|
|
return "error";
|
|
default:
|
|
;
|
|
}
|
|
|
|
return "unkown";
|
|
}
|
|
|
|
extern "C" void
|
|
#if defined _WIN32
|
|
APIENTRY
|
|
#endif
|
|
debug_callback(GLenum source, GLenum type, GLuint id,
|
|
GLenum severity, GLsizei , const GLchar* message, GLvoid* )
|
|
{
|
|
SAL_WARN("vcl.opengl", "OpenGL debug message: source: " << getSourceString(source) << ", type: "
|
|
<< getTypeString(type) << ", id: " << id << ", severity: " << getSeverityString(severity) << " with message: " << message);
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID
|
|
|
|
namespace {
|
|
|
|
static bool errorTriggered;
|
|
int oglErrorHandler( Display* /*dpy*/, XErrorEvent* /*evnt*/ )
|
|
{
|
|
errorTriggered = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
bool OpenGLContext::init( Window* pParent )
|
|
{
|
|
if(mbInitialized)
|
|
return true;
|
|
|
|
m_pWindow.reset(pParent ? NULL : new Window(0, WB_NOBORDER|WB_NODIALOGCONTROL));
|
|
mpWindow = pParent ? pParent : m_pWindow.get();
|
|
m_pChildWindow = 0;
|
|
initWindow();
|
|
return ImplInit();
|
|
}
|
|
|
|
bool OpenGLContext::init(SystemChildWindow* pChildWindow)
|
|
{
|
|
if(mbInitialized)
|
|
return true;
|
|
|
|
if( !pChildWindow )
|
|
return false;
|
|
|
|
mpWindow = pChildWindow->GetParent();
|
|
m_pChildWindow = pChildWindow;
|
|
initWindow();
|
|
return ImplInit();
|
|
}
|
|
|
|
bool OpenGLContext::ImplInit()
|
|
{
|
|
SAL_INFO("vcl.opengl", "OpenGLContext::ImplInit----start");
|
|
if(m_pWindow)
|
|
m_pWindow->setPosSizePixel(0,0,0,0);
|
|
m_aGLWin.Width = 0;
|
|
m_aGLWin.Height = 0;
|
|
|
|
#if defined( WNT )
|
|
m_aGLWin.hDC = GetDC(m_aGLWin.hWnd);
|
|
#elif defined( MACOSX )
|
|
|
|
SAL_INFO("vcl.opengl", "OpenGLContext not implemented yet for OS X");
|
|
return false;
|
|
|
|
#elif defined( IOS )
|
|
|
|
SAL_INFO("vcl.opengl", "OpenGLContext not implemented yet for iOS");
|
|
return false;
|
|
|
|
#elif defined( ANDROID )
|
|
|
|
SAL_INFO("vcl.opengl", "OpenGLContext not implemented yet for Android");
|
|
return false;
|
|
|
|
#elif defined( UNX )
|
|
m_aGLWin.ctx = m_aGLWin.dpy == 0 ? 0 : glXCreateContext(m_aGLWin.dpy,
|
|
m_aGLWin.vi,
|
|
0,
|
|
GL_TRUE);
|
|
if( m_aGLWin.ctx == NULL )
|
|
{
|
|
SAL_INFO("vcl.opengl", "unable to create GLX context");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#if defined( WNT )
|
|
PIXELFORMATDESCRIPTOR PixelFormatFront = // PixelFormat Tells Windows How We Want Things To Be
|
|
{
|
|
sizeof(PIXELFORMATDESCRIPTOR),
|
|
1, // Version Number
|
|
PFD_DRAW_TO_WINDOW |
|
|
PFD_SUPPORT_OPENGL |
|
|
PFD_DOUBLEBUFFER,
|
|
PFD_TYPE_RGBA, // Request An RGBA Format
|
|
(BYTE)32, // Select Our Color Depth
|
|
0, 0, 0, 0, 0, 0, // Color Bits Ignored
|
|
0, // No Alpha Buffer
|
|
0, // Shift Bit Ignored
|
|
0, // No Accumulation Buffer
|
|
0, 0, 0, 0, // Accumulation Bits Ignored
|
|
64, // 32 bit Z-BUFFER
|
|
0, // 0 bit stencil buffer
|
|
0, // No Auxiliary Buffer
|
|
0, // now ignored
|
|
0, // Reserved
|
|
0, 0, 0 // Layer Masks Ignored
|
|
};
|
|
|
|
// we must check whether can set the MSAA
|
|
int WindowPix = 0;
|
|
bool bMultiSampleSupport = InitMultisample(PixelFormatFront, WindowPix);
|
|
if (bMultiSampleSupport && WindowPix != 0)
|
|
{
|
|
m_aGLWin.bMultiSampleSupported = true;
|
|
}
|
|
else
|
|
{
|
|
WindowPix = ChoosePixelFormat(m_aGLWin.hDC, &PixelFormatFront);
|
|
}
|
|
|
|
if (WindowPix == 0)
|
|
{
|
|
SAL_WARN("vcl.opengl", "Invalid pixelformat");
|
|
return false;
|
|
}
|
|
|
|
SetPixelFormat(m_aGLWin.hDC, WindowPix, &PixelFormatFront);
|
|
m_aGLWin.hRC = wglCreateContext(m_aGLWin.hDC);
|
|
if (m_aGLWin.hRC == NULL)
|
|
{
|
|
SAL_WARN("vcl.opengl", "wglCreateContext failed");
|
|
return false;
|
|
}
|
|
|
|
if (!wglMakeCurrent(m_aGLWin.hDC, m_aGLWin.hRC))
|
|
{
|
|
SAL_WARN("vcl.opengl", "wglMakeCurrent failed: " << GetLastError());
|
|
return false;
|
|
}
|
|
|
|
#elif defined( MACOSX )
|
|
|
|
#elif defined( IOS )
|
|
|
|
#elif defined( ANDROID )
|
|
|
|
#elif defined( UNX )
|
|
if( !glXMakeCurrent( m_aGLWin.dpy, m_aGLWin.win, m_aGLWin.ctx ) )
|
|
{
|
|
SAL_INFO("vcl.opengl", "unable to select current GLX context");
|
|
return false;
|
|
}
|
|
|
|
int glxMinor, glxMajor;
|
|
double nGLXVersion = 0;
|
|
if( glXQueryVersion( m_aGLWin.dpy, &glxMajor, &glxMinor ) )
|
|
nGLXVersion = glxMajor + 0.1*glxMinor;
|
|
SAL_INFO("vcl.opengl", "available GLX version: " << nGLXVersion);
|
|
|
|
m_aGLWin.GLExtensions = glGetString( GL_EXTENSIONS );
|
|
SAL_INFO("vcl.opengl", "available GL extensions: " << m_aGLWin.GLExtensions);
|
|
|
|
if( m_aGLWin.HasGLXExtension("GLX_SGI_swap_control" ) )
|
|
{
|
|
// enable vsync
|
|
typedef GLint (*glXSwapIntervalProc)(GLint);
|
|
glXSwapIntervalProc glXSwapInterval = (glXSwapIntervalProc) glXGetProcAddress( (const GLubyte*) "glXSwapIntervalSGI" );
|
|
if( glXSwapInterval ) {
|
|
int (*oldHandler)(Display* /*dpy*/, XErrorEvent* /*evnt*/);
|
|
|
|
// replace error handler temporarily
|
|
oldHandler = XSetErrorHandler( oglErrorHandler );
|
|
|
|
errorTriggered = false;
|
|
|
|
glXSwapInterval( 1 );
|
|
|
|
// sync so that we possibly get an XError
|
|
glXWaitGL();
|
|
XSync(m_aGLWin.dpy, false);
|
|
|
|
if( errorTriggered )
|
|
SAL_INFO("vcl.opengl", "error when trying to set swap interval, NVIDIA or Mesa bug?");
|
|
else
|
|
SAL_INFO("vcl.opengl", "set swap interval to 1 (enable vsync)");
|
|
|
|
// restore the error handler
|
|
XSetErrorHandler( oldHandler );
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
//rGLRender.InitOpenGL(m_aGLWin);
|
|
|
|
static bool bGlewInit = false;
|
|
if(!bGlewInit)
|
|
{
|
|
glewExperimental = GL_TRUE;
|
|
GLenum err = glewInit();
|
|
if (err != GLEW_OK)
|
|
{
|
|
SAL_WARN("vcl.opengl", "Failed to initialize GLEW: " << glewGetErrorString(err));
|
|
return false;
|
|
}
|
|
else
|
|
bGlewInit = true;
|
|
}
|
|
|
|
#ifdef DBG_UTIL
|
|
// only enable debug output in dbgutil build
|
|
// somehow there are implementations where the feature is present and the function
|
|
// pointer is still NULL
|
|
if( GLEW_ARB_debug_output && glDebugMessageCallback )
|
|
{
|
|
glEnable(GL_DEBUG_OUTPUT);
|
|
glDebugMessageCallback(&debug_callback, NULL);
|
|
}
|
|
|
|
#endif
|
|
|
|
SAL_INFO("vcl.opengl", "OpenGLContext::ImplInit----end");
|
|
mbInitialized = true;
|
|
return true;
|
|
}
|
|
|
|
void OpenGLContext::setWinSize(const Size& rSize)
|
|
{
|
|
if(m_pWindow)
|
|
m_pWindow->SetSizePixel(rSize);
|
|
if( m_pChildWindow )
|
|
m_pChildWindow->SetSizePixel(rSize);
|
|
|
|
m_aGLWin.Width = rSize.Width();
|
|
m_aGLWin.Height = rSize.Height();
|
|
}
|
|
|
|
GLWindow& OpenGLContext::getOpenGLWindow()
|
|
{
|
|
return m_aGLWin;
|
|
}
|
|
|
|
void OpenGLContext::renderToFile()
|
|
{
|
|
int iWidth = m_aGLWin.Width;
|
|
int iHeight = m_aGLWin.Height;
|
|
static int nIdx = 0;
|
|
OUString aName = OUString( "file:///home/moggi/Documents/work/output" ) + OUString::number( nIdx++ ) + ".png";
|
|
OpenGLHelper::renderToFile(iWidth, iHeight, aName);
|
|
}
|
|
|
|
#if defined( WNT )
|
|
|
|
bool OpenGLContext::initWindow()
|
|
{
|
|
if( !m_pChildWindow )
|
|
{
|
|
SystemWindowData winData = generateWinData(mpWindow);
|
|
m_pChildWindow = new SystemChildWindow(mpWindow, 0, &winData, false);
|
|
m_pChildWindowGC.reset(m_pChildWindow);
|
|
}
|
|
|
|
if( m_pChildWindow )
|
|
{
|
|
m_pChildWindow->SetMouseTransparent( true );
|
|
m_pChildWindow->SetParentClipMode( PARENTCLIPMODE_NOCLIP );
|
|
m_pChildWindow->EnableEraseBackground( false );
|
|
m_pChildWindow->SetControlForeground();
|
|
m_pChildWindow->SetControlBackground();
|
|
m_pChildWindow->EnablePaint(false);
|
|
|
|
const SystemEnvData* sysData(m_pChildWindow->GetSystemData());
|
|
m_aGLWin.hWnd = sysData->hWnd;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#elif defined( MACOSX ) || defined( IOS ) || defined( ANDROID )
|
|
|
|
bool OpenGLContext::initWindow()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#elif defined( UNX )
|
|
|
|
bool OpenGLContext::initWindow()
|
|
{
|
|
const SystemEnvData* pChildSysData = 0;
|
|
SystemWindowData winData = generateWinData(mpWindow);
|
|
if( winData.pVisual )
|
|
{
|
|
if( !m_pChildWindow )
|
|
{
|
|
m_pChildWindow = new SystemChildWindow(mpWindow, 0, &winData, false);
|
|
m_pChildWindowGC.reset(m_pChildWindow);
|
|
}
|
|
pChildSysData = m_pChildWindow->GetSystemData();
|
|
}
|
|
|
|
if (!m_pChildWindow || !pChildSysData)
|
|
return false;
|
|
|
|
m_pChildWindow->SetMouseTransparent( true );
|
|
m_pChildWindow->SetParentClipMode( PARENTCLIPMODE_NOCLIP );
|
|
m_pChildWindow->EnableEraseBackground( false );
|
|
m_pChildWindow->SetControlForeground();
|
|
m_pChildWindow->SetControlBackground();
|
|
|
|
m_aGLWin.dpy = reinterpret_cast<Display*>(pChildSysData->pDisplay);
|
|
m_aGLWin.win = pChildSysData->aWindow;
|
|
m_aGLWin.screen = pChildSysData->nScreen;
|
|
|
|
// Get visual info
|
|
{
|
|
Visual* pVisual = (Visual*)pChildSysData->pVisual;
|
|
XVisualInfo aTemplate;
|
|
aTemplate.visualid = XVisualIDFromVisual( pVisual );;
|
|
int nVisuals = 0;
|
|
XVisualInfo* pInfos = XGetVisualInfo( m_aGLWin.dpy, VisualIDMask, &aTemplate, &nVisuals );
|
|
if( nVisuals != 1 )
|
|
SAL_WARN( "vcl.opengl", "match count for visual id is not 1" );
|
|
m_aGLWin.vi = pInfos;
|
|
}
|
|
|
|
// Check multi sample support
|
|
int nSamples = 0;
|
|
glXGetConfig(m_aGLWin.dpy, m_aGLWin.vi, GLX_SAMPLES, &nSamples);
|
|
if( nSamples > 0 )
|
|
m_aGLWin.bMultiSampleSupported = true;
|
|
|
|
m_aGLWin.GLXExtensions = glXQueryExtensionsString( m_aGLWin.dpy, m_aGLWin.screen );
|
|
SAL_INFO("vcl.opengl", "available GLX extensions: " << m_aGLWin.GLXExtensions);
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined( WNT ) || defined( MACOSX ) || defined( IOS ) || defined( ANDROID )
|
|
|
|
SystemWindowData OpenGLContext::generateWinData(Window* /*pParent*/)
|
|
{
|
|
SystemWindowData aWinData;
|
|
aWinData.nSize = sizeof(aWinData);
|
|
return aWinData;
|
|
}
|
|
|
|
#elif defined( UNX )
|
|
|
|
namespace {
|
|
|
|
// we need them before glew can initialize them
|
|
// glew needs an OpenGL context so we need to get the address manually
|
|
void initOpenGLFunctionPointers()
|
|
{
|
|
glXChooseFBConfig = (GLXFBConfig*(*)(Display *dpy, int screen, const int *attrib_list, int *nelements))glXGetProcAddressARB((GLubyte*)"glXChooseFBConfig");
|
|
glXGetVisualFromFBConfig = (XVisualInfo*(*)(Display *dpy, GLXFBConfig config))glXGetProcAddressARB((GLubyte*)"glXGetVisualFromFBConfig"); // try to find a visual for the current set of attributes
|
|
glXGetFBConfigAttrib = (int(*)(Display *dpy, GLXFBConfig config, int attribute, int* value))glXGetProcAddressARB((GLubyte*)"glXGetFBConfigAttrib");
|
|
}
|
|
|
|
}
|
|
|
|
SystemWindowData OpenGLContext::generateWinData(Window* pParent)
|
|
{
|
|
SystemWindowData aWinData;
|
|
aWinData.nSize = sizeof(aWinData);
|
|
aWinData.pVisual = NULL;
|
|
|
|
const SystemEnvData* sysData(pParent->GetSystemData());
|
|
|
|
Display *dpy = reinterpret_cast<Display*>(sysData->pDisplay);
|
|
|
|
if( dpy == 0 || !glXQueryExtension( dpy, NULL, NULL ) )
|
|
return aWinData;
|
|
|
|
XLIB_Window win = sysData->aWindow;
|
|
|
|
SAL_INFO("vcl.opengl", "parent window: " << win);
|
|
|
|
XWindowAttributes xattr;
|
|
XGetWindowAttributes( dpy, win, &xattr );
|
|
|
|
int screen = XScreenNumberOfScreen( xattr.screen );
|
|
|
|
static int visual_attribs[] =
|
|
{
|
|
GLX_DOUBLEBUFFER, True,
|
|
GLX_X_RENDERABLE, True,
|
|
GLX_RED_SIZE, 8,
|
|
GLX_GREEN_SIZE, 8,
|
|
GLX_BLUE_SIZE, 8,
|
|
GLX_ALPHA_SIZE, 8,
|
|
GLX_DEPTH_SIZE, 24,
|
|
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
|
|
None
|
|
};
|
|
|
|
initOpenGLFunctionPointers();
|
|
|
|
int fbCount = 0;
|
|
GLXFBConfig* pFBC = glXChooseFBConfig( dpy,
|
|
screen,
|
|
visual_attribs, &fbCount );
|
|
|
|
if(!pFBC)
|
|
{
|
|
SAL_WARN("vcl.opengl", "no suitable fb format found");
|
|
return aWinData;
|
|
}
|
|
|
|
int best_fbc = -1, best_num_samp = -1;
|
|
for(int i = 0; i < fbCount; ++i)
|
|
{
|
|
XVisualInfo* pVi = glXGetVisualFromFBConfig( dpy, pFBC[i] );
|
|
if(pVi)
|
|
{
|
|
// pick the one with the most samples per pixel
|
|
int nSampleBuf = 0;
|
|
int nSamples = 0;
|
|
glXGetFBConfigAttrib( dpy, pFBC[i], GLX_SAMPLE_BUFFERS, &nSampleBuf );
|
|
glXGetFBConfigAttrib( dpy, pFBC[i], GLX_SAMPLES , &nSamples );
|
|
|
|
if ( best_fbc < 0 || (nSampleBuf && ( nSamples > best_num_samp )) )
|
|
{
|
|
best_fbc = i;
|
|
best_num_samp = nSamples;
|
|
}
|
|
}
|
|
XFree( pVi );
|
|
}
|
|
|
|
XVisualInfo* vi = glXGetVisualFromFBConfig( dpy, pFBC[best_fbc] );
|
|
if( vi )
|
|
{
|
|
SAL_INFO("vcl.opengl", "using VisualID " << vi->visualid);
|
|
aWinData.pVisual = (void*)(vi->visual);
|
|
}
|
|
|
|
return aWinData;
|
|
}
|
|
|
|
#endif
|
|
|
|
void OpenGLContext::makeCurrent()
|
|
{
|
|
#if defined( WNT )
|
|
if (!wglMakeCurrent(m_aGLWin.hDC, m_aGLWin.hRC))
|
|
{
|
|
SAL_WARN("vcl.opengl", "OpenGLContext::makeCurrent(): wglMakeCurrent failed: " << GetLastError());
|
|
}
|
|
#elif defined( MACOSX ) || defined( IOS ) || defined( ANDROID )
|
|
// nothing
|
|
#elif defined( UNX )
|
|
glXMakeCurrent( m_aGLWin.dpy, m_aGLWin.win, m_aGLWin.ctx );
|
|
#endif
|
|
}
|
|
|
|
void OpenGLContext::swapBuffers()
|
|
{
|
|
#if defined( WNT )
|
|
SwapBuffers(m_aGLWin.hDC);
|
|
#elif defined( MACOSX ) || defined( IOS ) || defined( ANDROID )
|
|
// nothing
|
|
#elif defined( UNX )
|
|
glXSwapBuffers(m_aGLWin.dpy, m_aGLWin.win);
|
|
#endif
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|