RGFW 幕后:OpenGL 上下文创建






4.50/5 (2投票s)
本教程介绍如何为 X11、Windows 和 MacOS 创建加速的 OpenGL 上下文。基于我的项目:RGFW。
本教程介绍如何为 X11、Windows 和 MacOS 创建加速的 OpenGL 上下文。基于我的项目:RGFW。
引言
根据我开发 RGFW 的经验,处理低级 API 时最令人头疼的部分之一就是创建 OpenGL 上下文。
这并不是因为很难,而是因为有许多不那么明显的步骤,你必须正确执行它们才能创建你的 OpenGL 上下文。
本教程将解释如何在 Windows、MacOS 和 Linux 上创建 OpenGL 上下文,以便其他人不必为弄清楚这些而苦恼。
注意:MacOS 代码将考虑使用 Cocoa C Wrapper(请参阅 RGFW.h 或 Silicon.h)
概述
所需步骤的快速概述
1) 加载 OpenGL 上下文创建函数(如果需要)
2) 使用属性列表创建 OpenGL 像素格式(或 X11 上的 Visual)
3) 使用属性数组创建 OpenGL 上下文,以设置 OpenGL 版本
4) 释放 OpenGL 上下文
在 MacOS 上,步骤 2 和 3 合并为一步。
本文将不包括 EGL,因为它的设置要容易得多。
步骤 1(加载 OpenGL 上下文创建函数)
首先 RGFW 需要加载这些函数
X11 (GLX):
glXCreateContextAttribsARB
glXSwapIntervalEXT (optional)
Windows (WGL):
wglCreateContextAttribsARB
wglChoosePixelFormatARB
wglSwapIntervalEXT (optional)
Cocoa (NSOpenGL)
(none)
它需要加载这些函数,因为它们是由硬件供应商提供的扩展函数。默认情况下,wglCreateContext
或 glXCreateContext
会创建一个可能使用软件渲染的 OpenGL ~1.0 上下文。
要加载扩展函数,RGFW 必须首先定义它们。
X11 (GLX):
typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
static glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
(optional)
typedef void ( *PFNGLXSWAPINTERVALEXTPROC) (Display *dpy, GLXDrawable drawable, int interval);
PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = NULL;
Windows (WGL):
typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList);
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);
static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
(optional)
typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval);
static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
一旦函数定义完成,RGFW 将使用以下 API 调用加载函数
X11 (GLX): glXGetProcAddress aad glXGetProcAddressARB
Windows (WGL): wglGetProcAddress
例如,使用 GLX,
glXCreateContextAttribsARB = glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB");;
(optional)
glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT");
WGL 稍微复杂一些,因为它需要先创建一个虚拟上下文。
这个虚拟上下文用于 WGL 加载函数。
WGL 虚拟上下文
首先,RGFW 必须创建一个虚拟窗口和设备上下文,RGFW 也使用此虚拟窗口来获取标题栏的高度偏移量。
首先,RGFW 必须创建一个虚拟窗口和设备上下文,RGFW 也使用此虚拟窗口来获取标题栏的高度偏移量。
HWND dummyWin = CreateWindowA(Class.lpszClassName, name, window_style, x, y, w, h, 0, 0, inh, 0);
HDC dummy_dc = GetDC(dummyWin);
在此之后,RGFW 为虚拟窗口和虚拟 OpenGL 上下文创建一个虚拟像素格式。
u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
PIXELFORMATDESCRIPTOR pfd = {
sizeof(pfd),
1, /* version */
pfd_flags,
PFD_TYPE_RGBA, /* ipixel type */
24, /* color bits */
0, 0, 0, 0, 0, 0,
8, /* alpha bits */
0, 0, 0, 0, 0, 0,
32, /* depth bits */
8, /* stencil bits */
0,
PFD_MAIN_PLANE, /* Layer type */
0, 0, 0, 0
};
int pixel_format = ChoosePixelFormat(dummy_dc, &pfd
现在 RGFW 可以创建一个虚拟上下文并将其设置为当前上下文。
此上下文将使用默认的 OpenGL ~1.0 上下文,OpenGL。
HGLRC dummy_context = wglCreateContext(dummy_dc);
wglMakeCurrent(dummy_dc, dummy_context);
现在 RGFW 可以加载函数并删除虚拟上下文
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) (void*) wglGetProcAddress("wglCreateContextAttribsARB");
wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (void*)wglGetProcAddress("wglChoosePixelFormatARB");
(optional)
wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT");
wglMakeCurrent(dummy_dc, 0);
wglDeleteContext(dummy_context);
ReleaseDC(dummyWin, dummy_dc);
DestroyWindow(dummyWin);
步骤 2(使用属性列表创建 OpenGL 像素格式(或 X11 上的 Visual))
RGFW 需要创建一个像素格式/Visual,以便窗口知道如何绘制它获取的数据,并让 OpenGL 知道我们想要的绘制格式。
步骤 2.1:创建属性列表
要使用属性列表创建 OpenGL 像素格式,RGFW 有一个创建属性列表的函数,用于像素格式的 RGFW_initFormatAttribs
。
此函数使用基于目标操作系统 API 的宏,支持所有 API,只需一个函数。
在本教程中,我将为每个操作系统分离一个数组,而不是使用一个大数组。
linux:
static u32 attribs[] = {
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
GLX_DEPTH_SIZE, 24,
GLX_X_RENDERABLE, 1,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
windows:
// windows makes you define the macros yourself (or you can use wglext.h (which may or may not be installed on your system)
// https://registry.khronos.org/OpenGL/api/GL/wglext.h
// so I'll be using the hardcoded instead
static u32 attribs[] = {
0x2003, // WGL_ACCELERATION_ARB
0x2027, // WGL_FULL_ACCELERATION_ARB
0x201b, 8, // WGL_ALPHA_BITS_ARB
0x2022, 24, // WGL_DEPTH_BITS_ARB
0x2001, 1, // WGL_DRAW_TO_WINDOW_ARB
0x2015, 8, // WGL_RED_BITS_ARB
0x2017, 8, // WGL_GREEN_BITS_ARB
0x2019, 8, // WGL_BLUE_BITS_ARB
0x2013, 0x202B, // WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB
0x2010, 1, // WGL_SUPPORT_OPENGL_ARB
0x2014, 32, // WGL_COLOR_BITS_ARB
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
macos:
static u32 attribs[] = {
11 , 8, // alpha size
24 , 24, // depth size
72, // NSOpenGLPFANoRecovery
8, 24, // NSOpenGLPFAColorSize
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
你可能会注意到数组底部有多余的 0,那是用于可选参数的。
要填充可选参数,RGFW 首先将索引设置为第一个 0。
size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13;
RGFW 使用宏来填充可选参数
i32 RGFW_STENCIL = 0, RGFW_SAMPLES = 0, RGFW_STEREO = GL_FALSE, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1;
我将把参数分成每个平台。
linux:
if (RGFW_DOUBLE_BUFFER)
RGFW_GL_ADD_ATTRIB(GLX_DOUBLEBUFFER, 1);
RGFW_GL_ADD_ATTRIB(GLX_STENCIL_SIZE, RGFW_STENCIL);
RGFW_GL_ADD_ATTRIB(GLX_STEREO, RGFW_STEREO);
RGFW_GL_ADD_ATTRIB(GLX_AUX_BUFFERS, RGFW_AUX_BUFFERS);
// samples are handled by GLX later
windows:
if (RGFW_DOUBLE_BUFFER)
RGFW_GL_ADD_ATTRIB(0x2011, 1); // double buffer
RGFW_GL_ADD_ATTRIB(0x2023, RGFW_STENCIL);
RGFW_GL_ADD_ATTRIB(0x2012, RGFW_STEREO);
RGFW_GL_ADD_ATTRIB(0x2024, RGFW_AUX_BUFFERS);
RGFW_GL_ADD_ATTRIB(0x2042, RGFW_SAMPLES);
macOS:
if (RGFW_DOUBLE_BUFFER)
RGFW_GL_ADD_ATTRIB(5, 1); // double buffer
RGFW_GL_ADD_ATTRIB(13, RGFW_STENCIL);
RGFW_GL_ADD_ATTRIB(6, RGFW_STEREO);
RGFW_GL_ADD_ATTRIB(7, RGFW_AUX_BUFFERS);
RGFW_GL_ADD_ATTRIB(55, RGFW_SAMPLES);
/* this is here because macOS has a specific way to handle using software rendering */
if (useSoftware) {
RGFW_GL_ADD_ATTRIB(70, kCGLRendererGenericFloatID);
} else {
attribs[index] = 73;
index += 1;
}
在 macOS 上,RGFW 也会在此设置版本。
attribs[index] = 99;
attribs[index + 1] = 0x1000;
if (RGFW_majorVersion >= 4 || RGFW_majorVersion >= 3) {
attribs[index + 1] = (u32) ((RGFW_majorVersion >= 4) ? 0x4100 : 0x3200);
}
确保最后两个参数设置为 0,OpenGL/WGL/GLX/NSOpenGL 通过此方式知道停止读取。
RGFW_GL_ADD_ATTRIB(0, 0);
步骤 2.2 创建像素格式
列表创建完成后,就可以用来创建像素格式了。
GLX
GLX 通过创建基于属性的 GLXFBConfig
数组来处理。
i32 fbcount;
GLXFBConfig* fbc = glXChooseFBConfig((Display*) display, DefaultScreen(display), (i32*) attribs, &fbcount);
i32 best_fbc = -1;
if (fbcount == 0) {
printf("Failed to find any valid GLX visual configs\n");
return NULL;
}
然后它使用生成的数组查找最匹配的 FBConfig 对象。(这就是 RGFW_SAMPLES 的作用)
u32 i;
for (i = 0; i < (u32)fbcount; i++) {
XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) display, fbc[i]);
if (vi == NULL)
continue;
XFree(vi);
i32 samp_buf, samples;
glXGetFBConfigAttrib((Display*) display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
glXGetFBConfigAttrib((Display*) display, fbc[i], GLX_SAMPLES, &samples);
if ((best_fbc < 0 || samp_buf) && (samples == RGFW_SAMPLES || best_fbc == -1)) {
best_fbc = i;
}
}
if (best_fbc == -1) {
printf("Failed to get a valid GLX visual\n");
return NULL;
}
GLXFBConfig bestFbc = fbc[best_fbc];
一旦找到最匹配的对象,它就从数组中获取 X11 Visual(或像素格式)并释放数组。
/* Get a visual */
XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) display, bestFbc);
XFree(fbc);
现在这个 Visual 可以用来创建窗口和/或颜色映射表。
XSetWindowAttributes swa;
Colormap cmap;
swa.colormap = cmap = XCreateColormap((Display*) display, DefaultRootWindow(display), vi->visual, AllocNone);
swa.background_pixmap = None;
swa.border_pixel = 0;
swa.event_mask = event_mask;
swa.background_pixel = 0;
Window window = XCreateWindow((Display*) display, DefaultRootWindow((Display*) display), x, y, w, h,
0, vi->depth, InputOutput, vi->visual,
CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa);
WGL
RGFW 需要一些 WGL 定义来创建上下文
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
对于 WGL,RGFW 首先需要创建一个 win32 像素格式。
PIXELFORMATDESCRIPTOR pfd2 = (PIXELFORMATDESCRIPTOR){ sizeof(pfd2), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 24, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
然后 RGFW 可以基于 attribs 数组创建一个 WGL 像素格式。
int pixel_format2;
UINT num_formats;
wglChoosePixelFormatARB(hdc, attribs, 0, 1, &pixel_format2, &num_formats);
if (!num_formats) {
printf("Failed to create a pixel format for WGL.\n");
}
现在 RGFW 可以将两者合并为一个大的格式,并将其设置为你的窗口的格式。
DescribePixelFormat(hdc, pixel_format2, sizeof(pfd2), &pfd2);
if (!SetPixelFormat(hdc, pixel_format2, &pfd2)) {
printf("Failed to set the WGL pixel format.\n");
}
NSOpenGL
对于 MacOS,RGFW 只需要创建像素格式,然后根据 OpenGL 的格式初始化一个视图。
void* format = NSOpenGLPixelFormat_initWithAttributes(attribs);
/* the pixel format can be passed directly to opengl context creation to create a context
this is because the format also includes information about the opengl version (which may be a bad thing) */
view = NSOpenGLView_initWithFrame((NSRect){{0, 0}, {w, h}}, format);
objc_msgSend_void(view, sel_registerName("prepareOpenGL"));
ctx = objc_msgSend_id(view, sel_registerName("openGLContext"))
objc_msgSend_void(ctx, sel_registerName("makeCurrentContext"));
如果你是按照 MacOS 的说明操作,可以跳过步骤 3。
步骤 3(使用属性数组创建 OpenGL 上下文,以设置 OpenGL 版本)
注意:RGFW 定义了这个枚举和这些变量,以便用户可以控制 OpenGL 版本
typedef u8 RGFW_GL_profile; enum { RGFW_GL_CORE = 0, RGFW_GL_COMPATIBILITY };
i32 RGFW_majorVersion = 0, RGFW_minorVersion = 0;
b8 RGFW_profile = RGFW_GL_CORE;
glx
现在是时候为 GL 上下文创建创建属性数组并加载你想要的 OpenGL 版本了
i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 };
context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB;
if (RGFW_profile == RGFW_GL_CORE)
context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
else
context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
if (RGFW_majorVersion || RGFW_minorVersion) {
context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB;
context_attribs[3] = RGFW_majorVersion;
context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB;
context_attribs[5] = RGFW_minorVersion;
}
最后,可以使用 context_attribs 数组创建上下文
ctx = glXCreateContextAttribsARB((Display*) display, bestFbc, NULL, True, context_attribs);
glXMakeCurrent(display, window, ctx); // make the window the current so it can be rendered to in the same thread
WGL
首先 WGL 需要创建一个 attribs 数组来设置 OpenGL 版本
它还使用一个名为 SET_ATTRIB 的辅助宏
#define SET_ATTRIB(a, v) { \
assert(((size_t) index + 1) < sizeof(context_attribs) / sizeof(context_attribs[0])); \
context_attribs[index++] = a; \
context_attribs[index++] = v; \
}
/* create opengl/WGL context for the specified version */
u32 index = 0;
i32 context_attribs[40];
if (RGFW_profile == RGFW_GL_CORE) {
SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB);
}
else {
SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB);
}
if (RGFW_majorVersion || RGFW_minorVersion) {
SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion);
SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion);
}
SET_ATTRIB(0, 0);
现在可以创建上下文了
ctx = (HGLRC)wglCreateContextAttribsARB(hdc, NULL, context_attribs);
wglMakeCurrent(hdc, ctx); // make the window the current so it can be rendered to in the same thread
步骤 4(释放 OpenGL 上下文)
现在 RGFW 已经创建了它的 OpenGL 上下文,它必须在完成后释放该上下文。
这是简单的一部分。
Linux (GLX):
glXDestroyContext((Display*) display, ctx);
windows (WGL):
wglDeleteContext((HGLRC) ctx);
macOS (NSOpenGL):
// I think macOS NSOpenGL stuff is freed automatically when everything else is freed
完整的代码示例
X11
// compile with gcc x11.c -lX11 -lGL
#include <X11/Xlib.h>
#include <GL/glx.h>
#include <stdio.h>
#include <stdint.h>
typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
typedef void ( *PFNGLXSWAPINTERVALEXTPROC) (Display *dpy, GLXDrawable drawable, int interval);
#define GL_ADD_ATTRIB(attrib, attVal) \
if (attVal) { \
attribs[index] = attrib;\
attribs[index + 1] = attVal;\
index += 2;\
}
typedef uint8_t GL_profile; enum { GL_CORE = 0, GL_COMPATIBILITY };
int32_t majorVersion = 0, minorVersion = 0;
Bool profile = GL_CORE;
int32_t STENCIL = 0, SAMPLES = 0, STEREO = GL_FALSE, AUX_BUFFERS = 0, DOUBLE_BUFFER = 1;
int main(void) {
typedef void ( *PFNGLXSWAPINTERVALEXTPROC) (Display *dpy, GLXDrawable drawable, int interval);
PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = NULL;
glXCreateContextAttribsARBProc glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB");
glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT");
static uint32_t attribs[] = {
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
GLX_DEPTH_SIZE, 24,
GLX_X_RENDERABLE, 1,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13;
if (DOUBLE_BUFFER)
GL_ADD_ATTRIB(GLX_DOUBLEBUFFER, 1);
GL_ADD_ATTRIB(GLX_STENCIL_SIZE, STENCIL);
GL_ADD_ATTRIB(GLX_STEREO, STEREO);
GL_ADD_ATTRIB(GLX_AUX_BUFFERS, AUX_BUFFERS);
Display *display;
XEvent event;
display = XOpenDisplay(NULL);
if (display == NULL) {
fprintf(stderr, "Cannot open display\n");
return -1;
}
int s = DefaultScreen(display);
int32_t fbcount;
GLXFBConfig* fbc = glXChooseFBConfig((Display*) display, DefaultScreen(display), (int32_t*) attribs, &fbcount);
int32_t best_fbc = -1;
if (fbcount == 0) {
printf("Failed to find any valid GLX visual configs\n");
return -1;
}
uint32_t i;
for (i = 0; i < (uint32_t)fbcount; i++) {
XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) display, fbc[i]);
if (vi == NULL)
continue;
XFree(vi);
int32_t samp_buf, samples;
glXGetFBConfigAttrib((Display*) display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
glXGetFBConfigAttrib((Display*) display, fbc[i], GLX_SAMPLES, &samples);
if ((best_fbc < 0 || samp_buf) && (samples == SAMPLES || best_fbc == -1)) {
best_fbc = i;
}
}
if (best_fbc == -1) {
printf("Failed to get a valid GLX visual\n");
return -1;
}
GLXFBConfig bestFbc = fbc[best_fbc];
XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) display, bestFbc);
XFree(fbc);
XSetWindowAttributes swa;
Colormap cmap;
swa.colormap = cmap = XCreateColormap((Display*) display,
DefaultRootWindow(display),
vi->visual, AllocNone);
swa.background_pixmap = None;
swa.border_pixel = 0;
swa.event_mask = CWColormap | CWBorderPixel | CWBackPixel | CWEventMask;
swa.background_pixel = 0;
Window window = XCreateWindow((Display*) display, DefaultRootWindow((Display*) display), 400, 400, 200, 200,
0, vi->depth, InputOutput, vi->visual,
CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa);
XSelectInput(display, window, ExposureMask | KeyPressMask);
int32_t context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 };
context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB;
if (profile == GL_CORE)
context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
else
context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
if (majorVersion || minorVersion) {
context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB;
context_attribs[3] = majorVersion;
context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB;
context_attribs[5] = minorVersion;
}
GLXContext ctx = glXCreateContextAttribsARB((Display*) display, bestFbc, NULL, True, context_attribs);
glXMakeCurrent(display, window, ctx);
XMapWindow(display, window);
for (;;) {
XNextEvent(display, &event);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glXSwapBuffers(display, window);
}
glXDestroyContext((Display*) display, ctx);
XCloseDisplay(display);
return 0;
}
Windows
// compile with gcc winapi.c -lopengl32 -lgdi32
#include <windows.h>
#include <GL/gl.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
typedef uint8_t u8;
typedef int8_t i8;
typedef uint16_t u16;
typedef int16_t i16;
typedef uint32_t u32;
typedef int32_t i32;
typedef uint64_t u64;
typedef int64_t i64;
typedef u8 b8;
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
int main() {
typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList);
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);
static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval);
static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
WNDCLASS wc = {0};
wc.lpfnWndProc = DefWindowProc; // Default window procedure
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = "SampleWindowClass";
RegisterClass(&wc);
HWND dummyWin = CreateWindowA(wc.lpszClassName, "Sample Window", 0, 200, 200, 300, 300, 0, 0, wc.hInstance, 0);
HDC dummy_dc = GetDC(dummyWin);
u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
PIXELFORMATDESCRIPTOR pfd = {
sizeof(pfd),
1, /* version */
pfd_flags,
PFD_TYPE_RGBA, /* ipixel type */
24, /* color bits */
0, 0, 0, 0, 0, 0,
8, /* alpha bits */
0, 0, 0, 0, 0, 0,
32, /* depth bits */
8, /* stencil bits */
0,
PFD_MAIN_PLANE, /* Layer type */
0, 0, 0, 0
};
int pixel_format = ChoosePixelFormat(dummy_dc, &pfd);
SetPixelFormat(dummy_dc, pixel_format, &pfd);
HGLRC dummy_context = wglCreateContext(dummy_dc);
wglMakeCurrent(dummy_dc, dummy_context);
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) (void*) wglGetProcAddress("wglCreateContextAttribsARB");
wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (void*)wglGetProcAddress("wglChoosePixelFormatARB");
wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT");
wglMakeCurrent(dummy_dc, 0);
wglDeleteContext(dummy_context);
ReleaseDC(dummyWin, dummy_dc);
DestroyWindow(dummyWin);
// windows makes you define the macros yourself, so I'll be using the hardcoded instead
static u32 attribs[] = {
0x2003, // WGL_ACCELERATION_ARB
0x2027, // WGL_FULL_ACCELERATION_ARB
0x201b, 8, // WGL_ALPHA_BITS_ARB
0x2022, 24, // WGL_DEPTH_BITS_ARB
0x2001, 1, // WGL_DRAW_TO_WINDOW_ARB
0x2015, 8, // WGL_RED_BITS_ARB
0x2017, 8, // WGL_GREEN_BITS_ARB
0x2019, 8, // WGL_BLUE_BITS_ARB
0x2013, 0x202B, // WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB
0x2010, 1, // WGL_SUPPORT_OPENGL_ARB
0x2014, 32, // WGL_COLOR_BITS_ARB
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13;
#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \
if (attVal) { \
attribs[index] = attrib;\
attribs[index + 1] = attVal;\
index += 2;\
}
i32 RGFW_STENCIL = 0, RGFW_SAMPLES = 0, RGFW_STEREO = GL_FALSE, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1;
if (RGFW_DOUBLE_BUFFER)
RGFW_GL_ADD_ATTRIB(0x2011, 1); // double buffer
RGFW_GL_ADD_ATTRIB(0x2023, RGFW_STENCIL);
RGFW_GL_ADD_ATTRIB(0x2012, RGFW_STEREO);
RGFW_GL_ADD_ATTRIB(0x2024, RGFW_AUX_BUFFERS);
RGFW_GL_ADD_ATTRIB(0x2042, RGFW_SAMPLES);
RGFW_GL_ADD_ATTRIB(0, 0);
typedef u8 RGFW_GL_profile; enum { RGFW_GL_CORE = 0, RGFW_GL_COMPATIBILITY };
i32 RGFW_majorVersion = 0, RGFW_minorVersion = 0;
b8 RGFW_profile = RGFW_GL_CORE;
HWND hwnd = CreateWindowA(wc.lpszClassName, "Sample Window",
0,
400, 400, 300, 300,
NULL, NULL, wc.hInstance, NULL);
HDC hdc = GetDC(hwnd);
PIXELFORMATDESCRIPTOR pfd2 = (PIXELFORMATDESCRIPTOR){ sizeof(pfd2), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 24, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int pixel_format2;
UINT num_formats;
wglChoosePixelFormatARB(hdc, attribs, 0, 1, &pixel_format2, &num_formats);
if (!num_formats) {
printf("Failed to create a pixel format for WGL.\n");
}
DescribePixelFormat(hdc, pixel_format2, sizeof(pfd2), &pfd2);
if (!SetPixelFormat(hdc, pixel_format2, &pfd2)) {
printf("Failed to set the WGL pixel format.\n");
}
#define SET_ATTRIB(a, v) { \
assert(((size_t) index + 1) < sizeof(context_attribs) / sizeof(context_attribs[0])); \
context_attribs[index++] = a; \
context_attribs[index++] = v; \
}
/* create opengl/WGL context for the specified version */
index = 0;
i32 context_attribs[40];
if (RGFW_profile == RGFW_GL_CORE) {
SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB);
}
else {
SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB);
}
if (RGFW_majorVersion || RGFW_minorVersion) {
SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion);
SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion);
}
SET_ATTRIB(0, 0);
HGLRC ctx = (HGLRC)wglCreateContextAttribsARB(hdc, NULL, context_attribs);
wglMakeCurrent(hdc, ctx);
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
MSG msg;
BOOL running = TRUE;
while (running) {
if (PeekMessageA(&msg, hwnd, 0u, 0u, PM_REMOVE)) {
switch (msg.message) {
case WM_CLOSE:
case WM_QUIT:
running = FALSE;
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
running = IsWindow(hwnd);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
SwapBuffers(hdc);
}
DeleteDC(hdc);
DestroyWindow(hwnd);
return 0;
}