Working with Core OpenGL in WX Wigets
I’ve been working for some time with wxWidgets. The only thing I’ve missed, regarding OpenGL, is the ability to define my own OpenGL context, in particular Core profile and Debug contexts.
To be able to set a context we, or the toolkit we’re using, must use the wglCreateContextAttribsARB
function, as defined in the WGL_ARB_create_context extension. As it happens, wxWidgets uses wglCreateContext
, hence no OpenGL context can be explicitly defined using the provided source code for the current release (2.9.4).
As OpenGL context setting is not yet in the roadmap for future releases of wxWidgets, nor is it in its Todo List, I’m sharing a solution for this issue.
The solution is for the Windows platform, but other platforms should be as easy to change as well.
Note: I make no claim regarding the quality of the solution, it worked for me, and that’s all I claim. If anyone knows of a better way of doing this comments are most welcome, as they may prove useful for other readers (and me as well 🙂 ).
What I want is the possibility to specify the context attributes as a parameter when calling the wxGLcanvas
constructor.
The solution involves changing two files in the standard distribution: glcanvas.cpp and glcanvas.h.
For the Windows platform, these files can be located in (starting from the root of your installation):
- glcanvas.h : include/wx/msw
- glcanvas.cpp : src/msw
glcanvas.h
In wxGLContext
class add a public static variable to store the context attribute list:
static const int* m_glContextAttribs;
This var will store the attrib list, and it will be set in the wxGLCanvas
constructor. Later, in wxGLContext
constructor it will be used to set the required context.
In the wxGLCanvas
class, we must modify the constructor and Create
method so that they’ll receive an extra parameter, the context attribute list.
wxGLCanvas(wxWindow *parent, wxWindowID id = wxID_ANY, const int *attribList = NULL, const int *contextAttribs = NULL, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0, const wxString& name = wxGLCanvasName, const wxPalette& palette = wxNullPalette); bool Create(wxWindow *parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0, const wxString& name = wxGLCanvasName, const int *attribList = NULL, const int *contextAttribs = NULL, const wxPalette& palette = wxNullPalette);
glcanvas.cpp
In this file we must first add some definitions to be able to use the wglCreateContextAttribsARB
function.
I placed the following two lines just before the wxGLContext
constructor:
extern HGLRC WINAPI wglCreateContextAttribsARB (HDC hDC, HGLRC hShareContext, const int *attribList); typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShareContext, const int *attribList);
It is also necessary to initialize the static variable m_glContextAttribs
:
const int* wxGLContext::m_glContextAttribs = NULL;
Setting it to NULL by default it will provide the default context.
The wxGLContext constructor must be rewritten as follows:
wxGLContext::wxGLContext(wxGLCanvas *win, const wxGLContext* other) { if (m_glContextAttribs == NULL) { m_glContext = wglCreateContext(win->GetHDC()); } else { HGLRC hTempRC = wglCreateContext(win->GetHDC()); wglMakeCurrent(win->GetHDC(), hTempRC); PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress("wglCreateContextAttribsARB"); wglMakeCurrent(win->GetHDC(), NULL); wglDeleteContext(hTempRC); m_glContext = wglCreateContextAttribsARB(win->GetHDC(), 0, m_glContextAttribs); } wxCHECK_RET( m_glContext, wxT("Couldn't create OpenGL context") ); if ( other ) { if ( !wglShareLists(other->m_glContext, m_glContext) ) { wxLogLastError(wxT("wglShareLists")); } } }
Moving on the wxGLCanvas
class, in the same file, we need to change two methods: the constructor and one of the Create
methods.
The constructor receives an extra argument, the context attribute list contextAttribs
, and calls the Create
, also with the extra argument.
wxGLCanvas::wxGLCanvas(wxWindow *parent, wxWindowID id, const int *attribList, const int *contextAttribs, const wxPoint& pos, const wxSize& size, long style, const wxString& name, const wxPalette& palette) { Init(); (void)Create(parent, id, pos, size, style, name, attribList, contextAttribs, palette); }
Changes to the Creat
method, from wxGLCanvas
class are few, only the attribute list, to receive the extra argument, and a call to initialize the argument list static variable, m_glContextAttribs
, we declared for the wxGLContext
class. The remaining of the method stays as is.
bool wxGLCanvas::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name, const int *attribList, const int *contextAttribs, const wxPalette& palette) { wxGLContext::m_glContextAttribs = contextAttribs; ... }
That’s it! Now, from our application, when calling the wxGLCanvas constructor, we just need to add the context attribute list after the attribute list for the pixel format.