mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-26 05:41:45 +00:00
889 lines
30 KiB
C++
889 lines
30 KiB
C++
/*
|
|
** BuilderNative Renderer
|
|
** Copyright (c) 2019 Magnus Norddahl
|
|
**
|
|
** This software is provided 'as-is', without any express or implied
|
|
** warranty. In no event will the authors be held liable for any damages
|
|
** arising from the use of this software.
|
|
**
|
|
** Permission is granted to anyone to use this software for any purpose,
|
|
** including commercial applications, and to alter it and redistribute it
|
|
** freely, subject to the following restrictions:
|
|
**
|
|
** 1. The origin of this software must not be misrepresented; you must not
|
|
** claim that you wrote the original software. If you use this software
|
|
** in a product, an acknowledgment in the product documentation would be
|
|
** appreciated but is not required.
|
|
** 2. Altered source versions must be plainly marked as such, and must not be
|
|
** misrepresented as being the original software.
|
|
** 3. This notice may not be removed or altered from any source distribution.
|
|
*/
|
|
|
|
#include "Precomp.h"
|
|
#include "OpenGLContext.h"
|
|
#include "../Backend.h"
|
|
#include <stdexcept>
|
|
|
|
class OpenGLLoadFunctions
|
|
{
|
|
public:
|
|
OpenGLLoadFunctions() { ogl_LoadFunctions(); }
|
|
};
|
|
|
|
#ifdef WIN32
|
|
|
|
#include <CommCtrl.h>
|
|
|
|
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
|
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
|
|
#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
|
|
#define WGL_CONTEXT_FLAGS_ARB 0x2094
|
|
#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001
|
|
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
|
|
#define ERROR_INVALID_VERSION_ARB 0x2095
|
|
|
|
#define WGL_NUMBER_CL_PIXEL_FORMATS 0x2000
|
|
#define WGL_DRAW_TO_WINDOW 0x2001
|
|
#define WGL_DRAW_TO_BITMAP 0x2002
|
|
#define WGL_ACCELERATION 0x2003
|
|
#define WGL_NEED_PALETTE 0x2004
|
|
#define WGL_NEED_SYSTEM_PALETTE 0x2005
|
|
#define WGL_SWAP_LAYER_BUFFERS 0x2006
|
|
#define WGL_SWAP_METHOD 0x2007
|
|
#define WGL_NUMBER_OVERLAYS 0x2008
|
|
#define WGL_NUMBER_UNDERLAYS 0x2009
|
|
#define WGL_TRANSPARENT 0x200A
|
|
#define WGL_TRANSPARENT_VALUE 0x200B
|
|
#define WGL_SHARE_DEPTH 0x200C
|
|
#define WGL_SHARE_STENCIL 0x200D
|
|
#define WGL_SHARE_ACCUM 0x200E
|
|
#define WGL_SUPPORT_GDI 0x200F
|
|
#define WGL_SUPPORT_OPENGL 0x2010
|
|
#define WGL_DOUBLE_BUFFER 0x2011
|
|
#define WGL_STEREO 0x2012
|
|
#define WGL_PIXEL_TYPE 0x2013
|
|
#define WGL_COLOR_BITS 0x2014
|
|
#define WGL_RED_BITS 0x2015
|
|
#define WGL_RED_SHIFT 0x2016
|
|
#define WGL_GREEN_BITS 0x2017
|
|
#define WGL_GREEN_SHIFT 0x2018
|
|
#define WGL_BLUE_BITS 0x2019
|
|
#define WGL_BLUE_SHIFT 0x201A
|
|
#define WGL_ALPHA_BITS 0x201B
|
|
#define WGL_ALPHA_SHIFT 0x201C
|
|
#define WGL_ACCUM_BITS 0x201D
|
|
#define WGL_ACCUM_RED_BITS 0x201E
|
|
#define WGL_ACCUM_GREEN_BITS 0x201F
|
|
#define WGL_ACCUM_BLUE_BITS 0x2020
|
|
#define WGL_ACCUM_ALPHA_BITS 0x2021
|
|
#define WGL_DEPTH_BITS 0x2022
|
|
#define WGL_STENCIL_BITS 0x2023
|
|
#define WGL_AUX_BUFFERS 0x2024
|
|
#define WGL_NO_ACCELERATION 0x2025
|
|
#define WGL_GENERIC_ACCELERATION 0x2026
|
|
#define WGL_FULL_ACCELERATION 0x2027
|
|
#define WGL_SWAP_EXCHANGE 0x2028
|
|
#define WGL_SWAP_COPY 0x2029
|
|
#define WGL_SWAP_UNDEFINED 0x202A
|
|
#define WGL_TYPE_RGBA 0x202B
|
|
#define WGL_TYPE_COLORINDEX 0x202C
|
|
#define WGL_SAMPLE_BUFFERS 0x2041
|
|
#define WGL_SAMPLES 0x2042
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
class OpenGLContext : public IOpenGLContext
|
|
{
|
|
public:
|
|
OpenGLContext(void* window);
|
|
~OpenGLContext();
|
|
|
|
void MakeCurrent() override;
|
|
void ClearCurrent() override;
|
|
void SwapBuffers() override;
|
|
bool IsCurrent() override;
|
|
|
|
int GetWidth() const override;
|
|
int GetHeight() const override;
|
|
|
|
bool IsValid() const { return context != 0; }
|
|
|
|
private:
|
|
HWND window;
|
|
HDC dc;
|
|
HGLRC context;
|
|
};
|
|
|
|
class OpenGLCreationHelper
|
|
{
|
|
public:
|
|
OpenGLCreationHelper(HWND window);
|
|
~OpenGLCreationHelper();
|
|
|
|
HGLRC CreateContext(HDC hdc, HGLRC share_context = 0);
|
|
|
|
private:
|
|
HWND window;
|
|
HDC hdc;
|
|
HWND query_window = 0;
|
|
HDC query_dc = 0;
|
|
HGLRC query_context = 0;
|
|
|
|
typedef HGLRC(WINAPI* ptr_wglCreateContextAttribsARB)(HDC, HGLRC, const int*);
|
|
|
|
typedef BOOL(WINAPI* ptr_wglGetPixelFormatAttribivEXT)(HDC, int, int, UINT, int*, int*);
|
|
typedef BOOL(WINAPI* ptr_wglGetPixelFormatAttribfvEXT)(HDC, int, int, UINT, int*, FLOAT*);
|
|
typedef BOOL(WINAPI* ptr_wglChoosePixelFormatEXT)(HDC, const int*, const FLOAT*, UINT, int*, UINT*);
|
|
};
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
OpenGLContext::OpenGLContext(void* windowptr) : window((HWND)windowptr)
|
|
{
|
|
dc = GetDC(window);
|
|
OpenGLCreationHelper helper(window);
|
|
context = helper.CreateContext(dc);
|
|
if (context)
|
|
{
|
|
MakeCurrent();
|
|
static OpenGLLoadFunctions loadFunctions;
|
|
ClearCurrent();
|
|
}
|
|
}
|
|
|
|
OpenGLContext::~OpenGLContext()
|
|
{
|
|
if (context)
|
|
wglDeleteContext(context);
|
|
if (dc)
|
|
ReleaseDC(window, dc);
|
|
}
|
|
|
|
void OpenGLContext::MakeCurrent()
|
|
{
|
|
if (!IsCurrent())
|
|
wglMakeCurrent(dc, context);
|
|
}
|
|
|
|
bool OpenGLContext::IsCurrent()
|
|
{
|
|
return (wglGetCurrentContext() == context);
|
|
}
|
|
|
|
void OpenGLContext::ClearCurrent()
|
|
{
|
|
wglMakeCurrent(0, 0);
|
|
}
|
|
|
|
void OpenGLContext::SwapBuffers()
|
|
{
|
|
wglMakeCurrent(dc, context);
|
|
::SwapBuffers(dc);
|
|
}
|
|
|
|
int OpenGLContext::GetWidth() const
|
|
{
|
|
RECT box = { 0 };
|
|
GetClientRect(window, &box);
|
|
return box.right - box.left;
|
|
}
|
|
|
|
int OpenGLContext::GetHeight() const
|
|
{
|
|
RECT box = { 0 };
|
|
GetClientRect(window, &box);
|
|
return box.bottom - box.top;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
OpenGLCreationHelper::OpenGLCreationHelper(HWND window) : window(window)
|
|
{
|
|
WINDOWINFO window_info;
|
|
memset(&window_info, 0, sizeof(WINDOWINFO));
|
|
window_info.cbSize = sizeof(WINDOWINFO);
|
|
GetWindowInfo(window, &window_info);
|
|
|
|
query_window = CreateWindowEx(
|
|
0,
|
|
WC_STATIC,
|
|
TEXT(""),
|
|
WS_CHILD,
|
|
window_info.rcWindow.left,
|
|
window_info.rcWindow.top,
|
|
window_info.rcWindow.right - window_info.rcWindow.left,
|
|
window_info.rcWindow.bottom - window_info.rcWindow.top,
|
|
window, 0, GetModuleHandle(0), 0);
|
|
if (query_window == 0)
|
|
return;
|
|
|
|
query_dc = GetDC(query_window);
|
|
if (query_dc == 0)
|
|
{
|
|
DestroyWindow(query_window);
|
|
query_window = 0;
|
|
return;
|
|
}
|
|
|
|
PIXELFORMATDESCRIPTOR pfd;
|
|
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
|
|
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
|
|
pfd.nVersion = 1;
|
|
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
|
pfd.iPixelType = PFD_TYPE_RGBA;
|
|
pfd.cColorBits = 32;
|
|
pfd.cDepthBits = 16;
|
|
pfd.cStencilBits = 8;
|
|
|
|
int pixelformat = ChoosePixelFormat(query_dc, &pfd);
|
|
SetPixelFormat(query_dc, pixelformat, &pfd);
|
|
|
|
query_context = wglCreateContext(query_dc);
|
|
if (query_context == 0)
|
|
{
|
|
DeleteDC(query_dc);
|
|
DestroyWindow(query_window);
|
|
query_dc = 0;
|
|
query_window = 0;
|
|
}
|
|
}
|
|
|
|
OpenGLCreationHelper::~OpenGLCreationHelper()
|
|
{
|
|
wglDeleteContext(query_context);
|
|
DeleteDC(query_dc);
|
|
DestroyWindow(query_window);
|
|
}
|
|
|
|
HGLRC OpenGLCreationHelper::CreateContext(HDC hdc, HGLRC share_context)
|
|
{
|
|
if (query_context == 0)
|
|
return 0;
|
|
|
|
PIXELFORMATDESCRIPTOR pfd;
|
|
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
|
|
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
|
|
pfd.nVersion = 1;
|
|
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
|
pfd.iPixelType = PFD_TYPE_RGBA;
|
|
pfd.cColorBits = 32;
|
|
pfd.cDepthBits = 16;
|
|
pfd.cStencilBits = 8;
|
|
|
|
int pixelformat = ChoosePixelFormat(hdc, &pfd);
|
|
SetPixelFormat(hdc, pixelformat, &pfd);
|
|
|
|
wglMakeCurrent(query_dc, query_context);
|
|
|
|
ptr_wglCreateContextAttribsARB wglCreateContextAttribsARB = (ptr_wglCreateContextAttribsARB)wglGetProcAddress("wglCreateContextAttribsARB");
|
|
|
|
HGLRC opengl3_context = 0;
|
|
if (wglCreateContextAttribsARB)
|
|
{
|
|
for (int profile : { 1/*WGL_CONTEXT_CORE_PROFILE_BIT_ARB*/, 2 /*WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB*/ })
|
|
{
|
|
for (int version : { 46, 45, 44, 43, 42, 41, 40, 33, 32 })
|
|
{
|
|
std::vector<int> int_attributes;
|
|
int_attributes.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB);
|
|
int_attributes.push_back(version / 10);
|
|
int_attributes.push_back(WGL_CONTEXT_MINOR_VERSION_ARB);
|
|
int_attributes.push_back(version % 10);
|
|
int_attributes.push_back(0x9126); // WGL_CONTEXT_PROFILE_MASK_ARB
|
|
int_attributes.push_back(profile);
|
|
int_attributes.push_back(0);
|
|
opengl3_context = wglCreateContextAttribsARB(hdc, share_context, int_attributes.data());
|
|
|
|
if (opengl3_context)
|
|
break;
|
|
}
|
|
|
|
if (opengl3_context)
|
|
break;
|
|
}
|
|
|
|
// Grab the error from the last create attempt
|
|
if (!opengl3_context)
|
|
{
|
|
GLenum error = glGetError();
|
|
SetError("No OpenGL 3.2 support found: %d", error);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetError("No OpenGL driver supporting OpenGL 3 found");
|
|
}
|
|
|
|
wglMakeCurrent(0, 0);
|
|
|
|
return opengl3_context;
|
|
}
|
|
|
|
std::unique_ptr<IOpenGLContext> IOpenGLContext::Create(void* disp, void* window)
|
|
{
|
|
auto ctx = std::make_unique<OpenGLContext>(window);
|
|
if (!ctx->IsValid()) return nullptr;
|
|
return ctx;
|
|
}
|
|
|
|
#else
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <GL/glx.h>
|
|
|
|
#define GLFUNC
|
|
|
|
typedef GLXContext(*ptr_glXCreateContextAttribs)(::Display* dpy, GLXFBConfig config, GLXContext share_list, Bool direct, const int* attrib_list);
|
|
|
|
class GL_GLXFunctions
|
|
{
|
|
public:
|
|
typedef XVisualInfo* (GLFUNC* ptr_glXChooseVisual)(::Display* dpy, int screen, int* attrib_list);
|
|
typedef void (GLFUNC* ptr_glXCopyContext)(::Display* dpy, GLXContext src, GLXContext dst, unsigned long mask);
|
|
typedef GLXContext(GLFUNC* ptr_glXCreateContext)(::Display* dpy, XVisualInfo* vis, GLXContext share_list, Bool direct);
|
|
typedef GLXPixmap(GLFUNC* ptr_glXCreateGLXPixmap)(::Display* dpy, XVisualInfo* vis, Pixmap pixmap);
|
|
typedef void (GLFUNC* ptr_glXDestroyContext)(::Display* dpy, GLXContext ctx);
|
|
typedef void (GLFUNC* ptr_glXDestroyGLXPixmap)(::Display* dpy, GLXPixmap pix);
|
|
typedef int (GLFUNC* ptr_glXGetConfig)(::Display* dpy, XVisualInfo* vis, int attrib, int* value);
|
|
typedef GLXContext(GLFUNC* ptr_glXGetCurrentContext)(void);
|
|
typedef GLXDrawable(GLFUNC* ptr_glXGetCurrentDrawable)(void);
|
|
typedef Bool(GLFUNC* ptr_glXIsDirect)(::Display* dpy, GLXContext ctx);
|
|
typedef Bool(GLFUNC* ptr_glXMakeCurrent)(::Display* dpy, GLXDrawable drawable, GLXContext ctx);
|
|
typedef Bool(GLFUNC* ptr_glXQueryExtension)(::Display* dpy, int* error_base, int* event_base);
|
|
typedef Bool(GLFUNC* ptr_glXQueryVersion)(::Display* dpy, int* major, int* minor);
|
|
typedef void (GLFUNC* ptr_glXSwapBuffers)(::Display* dpy, GLXDrawable drawable);
|
|
typedef void (GLFUNC* ptr_glXUseXFont)(Font font, int first, int count, int list_base);
|
|
typedef void (GLFUNC* ptr_glXWaitGL)(void);
|
|
typedef void (GLFUNC* ptr_glXWaitX)(void);
|
|
typedef const char* (GLFUNC* ptr_glXGetClientString)(::Display* dpy, int name);
|
|
typedef const char* (GLFUNC* ptr_glXQueryServerString)(::Display* dpy, int screen, int name);
|
|
typedef const char* (GLFUNC* ptr_glXQueryExtensionsString)(::Display* dpy, int screen);
|
|
typedef ::Display* (GLFUNC* ptr_glXGetCurrentDisplay)(void);
|
|
typedef GLXFBConfig* (GLFUNC* ptr_glXChooseFBConfig)(::Display* dpy, int screen, const int* attrib_list, int* nelements);
|
|
typedef GLXContext(GLFUNC* ptr_glXCreateNewContext)(::Display* dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct);
|
|
typedef GLXPbuffer(GLFUNC* ptr_glXCreatePbuffer)(::Display* dpy, GLXFBConfig config, const int* attrib_list);
|
|
typedef GLXPixmap(GLFUNC* ptr_glXCreatePixmap)(::Display* dpy, GLXFBConfig config, Pixmap pixmap, const int* attrib_list);
|
|
typedef GLXWindow(GLFUNC* ptr_glXCreateWindow)(::Display* dpy, GLXFBConfig config, Window win, const int* attrib_list);
|
|
typedef void (GLFUNC* ptr_glXDestroyPbuffer)(::Display* dpy, GLXPbuffer pbuf);
|
|
typedef void (GLFUNC* ptr_glXDestroyPixmap)(::Display* dpy, GLXPixmap pixmap);
|
|
typedef void (GLFUNC* ptr_glXDestroyWindow)(::Display* dpy, GLXWindow win);
|
|
typedef GLXDrawable(GLFUNC* ptr_glXGetCurrentReadDrawable)(void);
|
|
typedef int (GLFUNC* ptr_glXGetFBConfigAttrib)(::Display* dpy, GLXFBConfig config, int attribute, int* value);
|
|
typedef GLXFBConfig* (GLFUNC* ptr_glXGetFBConfigs)(::Display* dpy, int screen, int* nelements);
|
|
typedef void (GLFUNC* ptr_glXGetSelectedEvent)(::Display* dpy, GLXDrawable draw, unsigned long* event_mask);
|
|
typedef XVisualInfo* (GLFUNC* ptr_glXGetVisualFromFBConfig)(::Display* dpy, GLXFBConfig config);
|
|
typedef Bool(GLFUNC* ptr_glXMakeContextCurrent)(::Display* display, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
|
|
typedef int (GLFUNC* ptr_glXQueryContext)(::Display* dpy, GLXContext ctx, int attribute, int* value);
|
|
typedef void (GLFUNC* ptr_glXQueryDrawable)(::Display* dpy, GLXDrawable draw, int attribute, unsigned int* value);
|
|
typedef void (GLFUNC* ptr_glXSelectEvent)(::Display* dpy, GLXDrawable draw, unsigned long event_mask);
|
|
|
|
typedef __GLXextFuncPtr(GLFUNC* ptr_glXGetProcAddress) (const GLubyte*);
|
|
typedef void (*(GLFUNC* ptr_glXGetProcAddressARB)(const GLubyte* procName))(void);
|
|
|
|
public:
|
|
ptr_glXChooseVisual glXChooseVisual;
|
|
ptr_glXCopyContext glXCopyContext;
|
|
ptr_glXCreateContext glXCreateContext;
|
|
ptr_glXCreateGLXPixmap glXCreateGLXPixmap;
|
|
ptr_glXDestroyContext glXDestroyContext;
|
|
ptr_glXDestroyGLXPixmap glXDestroyGLXPixmap;
|
|
ptr_glXGetConfig glXGetConfig;
|
|
ptr_glXGetCurrentContext glXGetCurrentContext;
|
|
ptr_glXGetCurrentDrawable glXGetCurrentDrawable;
|
|
ptr_glXIsDirect glXIsDirect;
|
|
ptr_glXMakeCurrent glXMakeCurrent;
|
|
ptr_glXQueryExtension glXQueryExtension;
|
|
ptr_glXQueryVersion glXQueryVersion;
|
|
ptr_glXSwapBuffers glXSwapBuffers;
|
|
ptr_glXUseXFont glXUseXFont;
|
|
ptr_glXWaitGL glXWaitGL;
|
|
ptr_glXWaitX glXWaitX;
|
|
ptr_glXGetClientString glXGetClientString;
|
|
ptr_glXQueryServerString glXQueryServerString;
|
|
ptr_glXQueryExtensionsString glXQueryExtensionsString;
|
|
ptr_glXGetCurrentDisplay glXGetCurrentDisplay;
|
|
ptr_glXChooseFBConfig glXChooseFBConfig;
|
|
ptr_glXCreateNewContext glXCreateNewContext;
|
|
ptr_glXCreatePbuffer glXCreatePbuffer;
|
|
ptr_glXCreatePixmap glXCreatePixmap;
|
|
ptr_glXCreateWindow glXCreateWindow;
|
|
ptr_glXDestroyPbuffer glXDestroyPbuffer;
|
|
ptr_glXDestroyPixmap glXDestroyPixmap;
|
|
ptr_glXDestroyWindow glXDestroyWindow;
|
|
ptr_glXGetCurrentReadDrawable glXGetCurrentReadDrawable;
|
|
ptr_glXGetFBConfigAttrib glXGetFBConfigAttrib;
|
|
ptr_glXGetFBConfigs glXGetFBConfigs;
|
|
ptr_glXGetSelectedEvent glXGetSelectedEvent;
|
|
ptr_glXGetVisualFromFBConfig glXGetVisualFromFBConfig;
|
|
ptr_glXMakeContextCurrent glXMakeContextCurrent;
|
|
ptr_glXQueryContext glXQueryContext;
|
|
ptr_glXQueryDrawable glXQueryDrawable;
|
|
ptr_glXSelectEvent glXSelectEvent;
|
|
ptr_glXGetProcAddress glXGetProcAddress;
|
|
ptr_glXGetProcAddressARB glXGetProcAddressARB;
|
|
};
|
|
|
|
class OpenGLContext : public IOpenGLContext
|
|
{
|
|
public:
|
|
OpenGLContext(void* display, void* window);
|
|
~OpenGLContext();
|
|
|
|
void MakeCurrent() override;
|
|
void ClearCurrent() override;
|
|
void SwapBuffers() override;
|
|
|
|
int GetWidth() const override;
|
|
int GetHeight() const override;
|
|
|
|
bool IsValid() const { return opengl_context != 0; }
|
|
|
|
private:
|
|
void CreateContext();
|
|
|
|
::Display* disp = nullptr;
|
|
::Window window = 0;
|
|
GLXContext opengl_context = 0;
|
|
|
|
XVisualInfo* opengl_visual_info = nullptr;
|
|
GLXFBConfig fbconfig;
|
|
|
|
GL_GLXFunctions glx;
|
|
|
|
typedef void(ProcAddress)();
|
|
ProcAddress* get_proc_address(const char *function_name) const;
|
|
|
|
GLXContext create_context_glx_1_3(GLXContext shared_context);
|
|
void create_glx_1_3(::Display* disp);
|
|
|
|
bool is_glx_extension_supported(const char* ext_name);
|
|
|
|
int major_version = 3;
|
|
int minor_version = 2;
|
|
|
|
void* opengl_lib_handle = nullptr;
|
|
};
|
|
|
|
GL_GLXFunctions glx_global;
|
|
|
|
#include <cstdio>
|
|
|
|
#define GL_USE_DLOPEN // Using dlopen for linux by default
|
|
|
|
#ifdef GL_USE_DLOPEN
|
|
#define GL_OPENGL_LIBRARY "libGL.so.1"
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
#ifdef GL_USE_DLOPEN
|
|
#define GL_LOAD_GLFUNC(x) dlsym(opengl_lib_handle, # x)
|
|
#else
|
|
#define GL_LOAD_GLFUNC(x) &x
|
|
#endif
|
|
|
|
OpenGLContext::OpenGLContext(void* display, void* window) : disp((::Display*)display), window((::Window)window)
|
|
{
|
|
try
|
|
{
|
|
CreateContext();
|
|
glx_global = glx;
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
// to do: maybe provide a way to query what the creation error was
|
|
}
|
|
|
|
if (opengl_context)
|
|
{
|
|
MakeCurrent();
|
|
static OpenGLLoadFunctions loadFunctions;
|
|
ClearCurrent();
|
|
}
|
|
}
|
|
|
|
OpenGLContext::~OpenGLContext()
|
|
{
|
|
if (opengl_visual_info)
|
|
{
|
|
XFree(opengl_visual_info);
|
|
opengl_visual_info = nullptr;
|
|
}
|
|
|
|
if (opengl_context)
|
|
{
|
|
if (glx.glXGetCurrentContext() == opengl_context)
|
|
ClearCurrent();
|
|
|
|
if (disp)
|
|
glx.glXDestroyContext(disp, opengl_context);
|
|
|
|
opengl_context = nullptr;
|
|
}
|
|
}
|
|
|
|
void OpenGLContext::MakeCurrent()
|
|
{
|
|
glx.glXMakeCurrent(disp, window, opengl_context);
|
|
}
|
|
|
|
void OpenGLContext::ClearCurrent()
|
|
{
|
|
glx.glXMakeCurrent(0, 0, opengl_context);
|
|
}
|
|
|
|
void OpenGLContext::SwapBuffers()
|
|
{
|
|
glx.glXSwapBuffers(disp, window);
|
|
}
|
|
|
|
int OpenGLContext::GetWidth() const
|
|
{
|
|
::Window root_window;
|
|
int x, y;
|
|
unsigned int width, height, border_width, depth;
|
|
Status result = XGetGeometry(disp, window, &root_window, &x, &y, &width, &height, &border_width, &depth);
|
|
return (result != 0) ? width: 0;
|
|
}
|
|
|
|
int OpenGLContext::GetHeight() const
|
|
{
|
|
::Window root_window;
|
|
int x, y;
|
|
unsigned int width, height, border_width, depth;
|
|
Status result = XGetGeometry(disp, window, &root_window, &x, &y, &width, &height, &border_width, &depth);
|
|
return (result != 0) ? height : 0;
|
|
}
|
|
|
|
OpenGLContext::ProcAddress* OpenGLContext::get_proc_address(const char *function_name) const
|
|
{
|
|
if (glx.glXGetProcAddressARB)
|
|
return glx.glXGetProcAddressARB((GLubyte*)function_name);
|
|
else if (glx.glXGetProcAddress)
|
|
return glx.glXGetProcAddress((GLubyte*)function_name);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
void OpenGLContext::CreateContext()
|
|
{
|
|
#ifdef GL_USE_DLOPEN
|
|
opengl_lib_handle = dlopen(GL_OPENGL_LIBRARY, RTLD_NOW | RTLD_GLOBAL);
|
|
if (!opengl_lib_handle)
|
|
throw std::runtime_error(std::string("Cannot open opengl library: ") + GL_OPENGL_LIBRARY);
|
|
#endif
|
|
|
|
glx.glXChooseVisual = (GL_GLXFunctions::ptr_glXChooseVisual) GL_LOAD_GLFUNC(glXChooseVisual);
|
|
glx.glXCopyContext = (GL_GLXFunctions::ptr_glXCopyContext) GL_LOAD_GLFUNC(glXCopyContext);
|
|
glx.glXCreateContext = (GL_GLXFunctions::ptr_glXCreateContext) GL_LOAD_GLFUNC(glXCreateContext);
|
|
glx.glXCreateGLXPixmap = (GL_GLXFunctions::ptr_glXCreateGLXPixmap) GL_LOAD_GLFUNC(glXCreateGLXPixmap);
|
|
glx.glXDestroyContext = (GL_GLXFunctions::ptr_glXDestroyContext) GL_LOAD_GLFUNC(glXDestroyContext);
|
|
glx.glXDestroyGLXPixmap = (GL_GLXFunctions::ptr_glXDestroyGLXPixmap) GL_LOAD_GLFUNC(glXDestroyGLXPixmap);
|
|
glx.glXGetConfig = (GL_GLXFunctions::ptr_glXGetConfig) GL_LOAD_GLFUNC(glXGetConfig);
|
|
glx.glXGetCurrentContext = (GL_GLXFunctions::ptr_glXGetCurrentContext) GL_LOAD_GLFUNC(glXGetCurrentContext);
|
|
glx.glXGetCurrentDrawable = (GL_GLXFunctions::ptr_glXGetCurrentDrawable) GL_LOAD_GLFUNC(glXGetCurrentDrawable);
|
|
glx.glXIsDirect = (GL_GLXFunctions::ptr_glXIsDirect) GL_LOAD_GLFUNC(glXIsDirect);
|
|
glx.glXMakeCurrent = (GL_GLXFunctions::ptr_glXMakeCurrent) GL_LOAD_GLFUNC(glXMakeCurrent);
|
|
glx.glXQueryExtension = (GL_GLXFunctions::ptr_glXQueryExtension) GL_LOAD_GLFUNC(glXQueryExtension);
|
|
glx.glXQueryVersion = (GL_GLXFunctions::ptr_glXQueryVersion) GL_LOAD_GLFUNC(glXQueryVersion);
|
|
glx.glXSwapBuffers = (GL_GLXFunctions::ptr_glXSwapBuffers) GL_LOAD_GLFUNC(glXSwapBuffers);
|
|
glx.glXUseXFont = (GL_GLXFunctions::ptr_glXUseXFont) GL_LOAD_GLFUNC(glXUseXFont);
|
|
glx.glXWaitGL = (GL_GLXFunctions::ptr_glXWaitGL) GL_LOAD_GLFUNC(glXWaitGL);
|
|
glx.glXWaitX = (GL_GLXFunctions::ptr_glXWaitX) GL_LOAD_GLFUNC(glXWaitX);
|
|
glx.glXGetClientString = (GL_GLXFunctions::ptr_glXGetClientString) GL_LOAD_GLFUNC(glXGetClientString);
|
|
glx.glXQueryServerString = (GL_GLXFunctions::ptr_glXQueryServerString) GL_LOAD_GLFUNC(glXQueryServerString);
|
|
glx.glXQueryExtensionsString = (GL_GLXFunctions::ptr_glXQueryExtensionsString) GL_LOAD_GLFUNC(glXQueryExtensionsString);
|
|
glx.glXGetCurrentDisplay = (GL_GLXFunctions::ptr_glXGetCurrentDisplay) GL_LOAD_GLFUNC(glXGetCurrentDisplay);
|
|
glx.glXChooseFBConfig = (GL_GLXFunctions::ptr_glXChooseFBConfig) GL_LOAD_GLFUNC(glXChooseFBConfig);
|
|
glx.glXCreateNewContext = (GL_GLXFunctions::ptr_glXCreateNewContext) GL_LOAD_GLFUNC(glXCreateNewContext);
|
|
glx.glXCreatePbuffer = (GL_GLXFunctions::ptr_glXCreatePbuffer) GL_LOAD_GLFUNC(glXCreatePbuffer);
|
|
glx.glXCreatePixmap = (GL_GLXFunctions::ptr_glXCreatePixmap) GL_LOAD_GLFUNC(glXCreatePixmap);
|
|
glx.glXCreateWindow = (GL_GLXFunctions::ptr_glXCreateWindow) GL_LOAD_GLFUNC(glXCreateWindow);
|
|
glx.glXDestroyPbuffer = (GL_GLXFunctions::ptr_glXDestroyPbuffer) GL_LOAD_GLFUNC(glXDestroyPbuffer);
|
|
glx.glXDestroyPixmap = (GL_GLXFunctions::ptr_glXDestroyPixmap) GL_LOAD_GLFUNC(glXDestroyPixmap);
|
|
glx.glXDestroyWindow = (GL_GLXFunctions::ptr_glXDestroyWindow) GL_LOAD_GLFUNC(glXDestroyWindow);
|
|
glx.glXGetCurrentReadDrawable = (GL_GLXFunctions::ptr_glXGetCurrentReadDrawable) GL_LOAD_GLFUNC(glXGetCurrentReadDrawable);
|
|
glx.glXGetFBConfigAttrib = (GL_GLXFunctions::ptr_glXGetFBConfigAttrib) GL_LOAD_GLFUNC(glXGetFBConfigAttrib);
|
|
glx.glXGetFBConfigs = (GL_GLXFunctions::ptr_glXGetFBConfigs) GL_LOAD_GLFUNC(glXGetFBConfigs);
|
|
glx.glXGetSelectedEvent = (GL_GLXFunctions::ptr_glXGetSelectedEvent) GL_LOAD_GLFUNC(glXGetSelectedEvent);
|
|
glx.glXGetVisualFromFBConfig = (GL_GLXFunctions::ptr_glXGetVisualFromFBConfig) GL_LOAD_GLFUNC(glXGetVisualFromFBConfig);
|
|
glx.glXMakeContextCurrent = (GL_GLXFunctions::ptr_glXMakeContextCurrent) GL_LOAD_GLFUNC(glXMakeContextCurrent);
|
|
glx.glXQueryContext = (GL_GLXFunctions::ptr_glXQueryContext) GL_LOAD_GLFUNC(glXQueryContext);
|
|
glx.glXQueryDrawable = (GL_GLXFunctions::ptr_glXQueryDrawable) GL_LOAD_GLFUNC(glXQueryDrawable);
|
|
glx.glXSelectEvent = (GL_GLXFunctions::ptr_glXSelectEvent) GL_LOAD_GLFUNC(glXSelectEvent);
|
|
|
|
glx.glXGetProcAddressARB = (GL_GLXFunctions::ptr_glXGetProcAddressARB) GL_LOAD_GLFUNC(glXGetProcAddressARB);
|
|
glx.glXGetProcAddress = (GL_GLXFunctions::ptr_glXGetProcAddress) GL_LOAD_GLFUNC(glXGetProcAddress);
|
|
|
|
if ((glx.glXDestroyContext == nullptr) ||
|
|
(glx.glXMakeCurrent == nullptr) ||
|
|
(glx.glXGetCurrentContext == nullptr) ||
|
|
(glx.glXChooseVisual == nullptr) ||
|
|
(glx.glXIsDirect == nullptr) ||
|
|
(glx.glXGetConfig == nullptr) ||
|
|
(glx.glXQueryExtensionsString == nullptr) ||
|
|
(glx.glXQueryVersion == nullptr) ||
|
|
(glx.glXGetVisualFromFBConfig == nullptr) ||
|
|
(glx.glXCreateNewContext == nullptr) ||
|
|
(glx.glXCreateContext == nullptr))
|
|
{
|
|
throw std::runtime_error("Cannot obtain required OpenGL GLX functions");
|
|
}
|
|
|
|
if ((glx.glXGetProcAddressARB == nullptr) && (glx.glXGetProcAddress == nullptr))
|
|
{
|
|
throw std::runtime_error("Cannot obtain required OpenGL GLX functions");
|
|
}
|
|
|
|
// FBConfigs were added in GLX version 1.3.
|
|
int glx_major, glx_minor;
|
|
if (!glx.glXQueryVersion(disp, &glx_major, &glx_minor) || ((glx_major == 1) && (glx_minor < 3)) || (glx_major < 1))
|
|
throw std::runtime_error("GLX 1.3 or better is required");
|
|
|
|
create_glx_1_3(disp);
|
|
|
|
//if (!glx.glXIsDirect(disp, opengl_context))
|
|
// printf("No hardware acceleration available. I hope you got a really fast machine.\n");
|
|
}
|
|
|
|
void OpenGLContext::create_glx_1_3(::Display* disp)
|
|
{
|
|
if (glx.glXChooseFBConfig == nullptr)
|
|
throw std::runtime_error("Cannot find the glXChooseFBConfig function");
|
|
|
|
// Setup OpenGL:
|
|
int gl_attribs_single[] =
|
|
{
|
|
GLX_X_RENDERABLE, True,
|
|
//GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
|
GLX_DEPTH_SIZE, 16,
|
|
GLX_STENCIL_SIZE, 8,
|
|
GLX_BUFFER_SIZE, 24,
|
|
None
|
|
};
|
|
|
|
std::vector<int> gl_attribs;
|
|
gl_attribs.reserve(64);
|
|
|
|
gl_attribs.push_back(GLX_X_RENDERABLE);
|
|
gl_attribs.push_back(True);
|
|
gl_attribs.push_back(GLX_DRAWABLE_TYPE);
|
|
gl_attribs.push_back(GLX_WINDOW_BIT);
|
|
//gl_attribs.push_back(GLX_RENDER_TYPE);
|
|
//gl_attribs.push_back(GLX_RGBA_BIT);
|
|
gl_attribs.push_back(GLX_X_VISUAL_TYPE);
|
|
gl_attribs.push_back(GLX_TRUE_COLOR);
|
|
gl_attribs.push_back(GLX_RED_SIZE);
|
|
gl_attribs.push_back(8);
|
|
gl_attribs.push_back(GLX_GREEN_SIZE);
|
|
gl_attribs.push_back(8);
|
|
gl_attribs.push_back(GLX_BLUE_SIZE);
|
|
gl_attribs.push_back(8);
|
|
gl_attribs.push_back(GLX_ALPHA_SIZE);
|
|
gl_attribs.push_back(0);
|
|
gl_attribs.push_back(GLX_DEPTH_SIZE);
|
|
gl_attribs.push_back(24);
|
|
gl_attribs.push_back(GLX_STENCIL_SIZE);
|
|
gl_attribs.push_back(8);
|
|
gl_attribs.push_back(GLX_DOUBLEBUFFER);
|
|
gl_attribs.push_back(True);
|
|
gl_attribs.push_back(GLX_STEREO);
|
|
gl_attribs.push_back(False);
|
|
gl_attribs.push_back(None);
|
|
|
|
// get an appropriate visual
|
|
int fb_count;
|
|
GLXFBConfig* fbc = glx.glXChooseFBConfig(disp, DefaultScreen(disp), &gl_attribs[0], &fb_count);
|
|
|
|
if (!fbc)
|
|
{
|
|
printf("Requested visual not supported by your OpenGL implementation. Falling back on singlebuffered Visual!\n");
|
|
fbc = glx.glXChooseFBConfig(disp, DefaultScreen(disp), gl_attribs_single, &fb_count);
|
|
if (!fbc)
|
|
throw std::runtime_error(" glxChooseFBConfig failed");
|
|
fbconfig = fbc[0];
|
|
}
|
|
else
|
|
{
|
|
if (!glx.glXGetFBConfigAttrib)
|
|
throw std::runtime_error("Cannot find function glXGetFBConfigAttrib");
|
|
|
|
int required_samples = 1; // desc.multisampling();
|
|
int desired_config = 0;
|
|
int max_sample_buffers = 0;
|
|
int max_samples = 0;
|
|
// Find the best fitting multisampling option
|
|
for (int i = 0; i < fb_count; i++)
|
|
{
|
|
int samp_buf, samples;
|
|
glx.glXGetFBConfigAttrib(disp, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
|
|
glx.glXGetFBConfigAttrib(disp, fbc[i], GLX_SAMPLES, &samples);
|
|
|
|
// Samples are most important, because they are variable
|
|
if (max_samples < required_samples)
|
|
{
|
|
if (samples > max_samples)
|
|
{
|
|
max_samples = samples;
|
|
desired_config = i;
|
|
}
|
|
}
|
|
|
|
// Use the maximum sample buffer
|
|
if (max_samples == samples) // Only check if the sample is valid
|
|
{
|
|
if (max_sample_buffers < samp_buf)
|
|
{
|
|
max_sample_buffers = samp_buf;
|
|
desired_config = i;
|
|
}
|
|
}
|
|
}
|
|
fbconfig = fbc[desired_config];
|
|
}
|
|
|
|
XFree(fbc);
|
|
|
|
if (opengl_visual_info) XFree(opengl_visual_info);
|
|
opengl_visual_info = glx.glXGetVisualFromFBConfig(disp, fbconfig);
|
|
if (opengl_visual_info == nullptr)
|
|
{
|
|
throw std::runtime_error("glXGetVisualFromFBConfig failed");
|
|
}
|
|
|
|
// create a GLX context
|
|
opengl_context = create_context_glx_1_3(nullptr);
|
|
}
|
|
|
|
bool OpenGLContext::is_glx_extension_supported(const char* ext_name)
|
|
{
|
|
const char* ext_string = glx.glXQueryExtensionsString(disp, opengl_visual_info->screen);
|
|
if (ext_string)
|
|
{
|
|
const char* start;
|
|
const char* where, * terminator;
|
|
|
|
// Extension names should not have spaces.
|
|
where = strchr(ext_name, ' ');
|
|
if (where || *ext_name == '\0')
|
|
return false;
|
|
|
|
int ext_len = strlen(ext_name);
|
|
|
|
// It takes a bit of care to be fool-proof about parsing the OpenGL extensions string. Don't be fooled by sub-strings, etc.
|
|
for (start = ext_string; ; )
|
|
{
|
|
where = strstr(start, ext_name);
|
|
|
|
if (!where)
|
|
break;
|
|
|
|
terminator = where + ext_len;
|
|
|
|
if (where == start || *(where - 1) == ' ')
|
|
if (*terminator == ' ' || *terminator == '\0')
|
|
return true;
|
|
|
|
start = terminator;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool cl_ctxErrorOccurred = false;
|
|
static int cl_ctxErrorHandler(::Display* dpy, XErrorEvent* ev)
|
|
{
|
|
cl_ctxErrorOccurred = true;
|
|
return 0;
|
|
}
|
|
|
|
GLXContext OpenGLContext::create_context_glx_1_3(GLXContext shared_context)
|
|
{
|
|
GLXContext context;
|
|
|
|
context = glx.glXCreateNewContext(disp, fbconfig, GLX_RGBA_TYPE, shared_context, True);
|
|
if (context == nullptr)
|
|
throw std::runtime_error("glXCreateContext failed");
|
|
|
|
ptr_glXCreateContextAttribs glXCreateContextAttribs = nullptr;
|
|
|
|
if (is_glx_extension_supported("GLX_ARB_create_context"))
|
|
{
|
|
if (glx.glXGetProcAddressARB)
|
|
glXCreateContextAttribs = (ptr_glXCreateContextAttribs)glx.glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB");
|
|
if (glx.glXGetProcAddress)
|
|
glXCreateContextAttribs = (ptr_glXCreateContextAttribs)glx.glXGetProcAddress((GLubyte*) "glXCreateContextAttribsARB");
|
|
}
|
|
|
|
if (glXCreateContextAttribs)
|
|
{
|
|
// Install an X error handler so the application won't exit if GL 3.0 context allocation fails.
|
|
//
|
|
// Note this error handler is global. All display connections in all threads
|
|
// of a process use the same error handler, so be sure to guard against other
|
|
// threads issuing X commands while this code is running.
|
|
int (*oldHandler)(::Display*, XErrorEvent*) = XSetErrorHandler(&cl_ctxErrorHandler);
|
|
|
|
GLXContext context_gl3 = 0;
|
|
for (int profile : { 1/*GLX_CONTEXT_CORE_PROFILE_BIT_ARB*/, 2 /*GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB*/ })
|
|
{
|
|
for (int version : { 46, 45, 44, 43, 42, 41, 40, 33, 32 })
|
|
{
|
|
std::vector<int> int_attributes;
|
|
int_attributes.push_back(0x2091); // GLX_CONTEXT_MAJOR_VERSION_ARB
|
|
int_attributes.push_back(version / 10);
|
|
int_attributes.push_back(0x2092); // GLX_CONTEXT_MINOR_VERSION_ARB
|
|
int_attributes.push_back(version % 10);
|
|
int_attributes.push_back(0x9126); // GLX_CONTEXT_PROFILE_MASK_ARB
|
|
int_attributes.push_back(profile);
|
|
int_attributes.push_back(None);
|
|
|
|
cl_ctxErrorOccurred = false;
|
|
|
|
context_gl3 = glXCreateContextAttribs(disp, fbconfig, shared_context, True, int_attributes.data());
|
|
|
|
if (cl_ctxErrorOccurred && context_gl3)
|
|
{
|
|
glx.glXDestroyContext(disp, context_gl3);
|
|
context_gl3 = nullptr;
|
|
}
|
|
|
|
if (context_gl3)
|
|
break;
|
|
}
|
|
|
|
if (context_gl3)
|
|
break;
|
|
}
|
|
|
|
// Restore the original error handler
|
|
XSetErrorHandler(oldHandler);
|
|
|
|
if (context_gl3)
|
|
{
|
|
glx.glXDestroyContext(disp, context);
|
|
context = context_gl3;
|
|
}
|
|
}
|
|
return context;
|
|
}
|
|
|
|
std::unique_ptr<IOpenGLContext> IOpenGLContext::Create(void* disp, void* window)
|
|
{
|
|
auto ctx = std::make_unique<OpenGLContext>(disp, window);
|
|
if (!ctx->IsValid()) return nullptr;
|
|
return ctx;
|
|
}
|
|
|
|
void* GL_GetProcAddress(const char* function_name)
|
|
{
|
|
if (glx_global.glXGetProcAddressARB)
|
|
return (void*)glx_global.glXGetProcAddressARB((GLubyte*)function_name);
|
|
else if (glx_global.glXGetProcAddress)
|
|
return (void*)glx_global.glXGetProcAddress((GLubyte*)function_name);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
#endif
|