65.9K
CodeProject 正在变化。 阅读更多。
Home

RGFW 幕后:OpenGL 上下文创建

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (2投票s)

2024 年 8 月 9 日

CPOL

4分钟阅读

viewsIcon

3615

本教程介绍如何为 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)

它需要加载这些函数,因为它们是由硬件供应商提供的扩展函数。默认情况下,wglCreateContextglXCreateContext 会创建一个可能使用软件渲染的 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;
}
© . All rights reserved.