Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for RGFW Under the Hood: OpenGL Context Creation
Colleague Riley
Colleague Riley

Posted on

RGFW Under the Hood: OpenGL Context Creation

Tutorial for creating an accelerated opengl context for X11, Windows, and MacOS. Based on my project: RGFW.

Introduction

In my experience working on RGFW, one of the most annoying parts of working with low-level APIs is creating an OpenGL context.
This is not because it's hard, but because there are many not-so-obvious steps that you must do correctly to create your OpenGL context.
This tutorial explains how to create an OpenGL context for Windows, MacOS and Linux so that way others don't have to struggle with figuring it out.

NOTE: MacOS code will be written with a Cocoa C Wrapper in mind (see the RGFW.h or Silicon.h)

Overview

A quick overview of the steps required
1) Load OpenGL context creation functions (if you need to)
2) Create an OpenGL pixel format (or Visual on X11) using an attribute list
3) Create your OpenGL context using an attribute array to set the OpenGL version

4) Free the OpenGL context

On MacOS steps 2 and 3 are one step.
EGL will not be included in this article because the setup is far easier.

Step 1 (Load OpenGL context creation functions)

First RGFW needs to load these functions

//X11 (GLX):glXCreateContextAttribsARBglXSwapIntervalEXT//(optional)//Windows (WGL):wglCreateContextAttribsARBwglChoosePixelFormatARBwglSwapIntervalEXT//(optional)//Cocoa (NSOpenGL)(none)
Enter fullscreen modeExit fullscreen mode

It needs to load these functions because they're extension functions provided by the hardware vendor. By default,wglCreateContext orglXCreateContext will create an OpenGL ~1.0 context that probably uses software rendering.

To load the extension functions RGFW has to start by defining them.

//X11 (GLX):typedefGLXContext(*glXCreateContextAttribsARBProc)(Display*,GLXFBConfig,GLXContext,Bool,constint*);staticglXCreateContextAttribsARBProcglXCreateContextAttribsARB=0;//(optional)typedefvoid(*PFNGLXSWAPINTERVALEXTPROC)(Display*dpy,GLXDrawabledrawable,intinterval);PFNGLXSWAPINTERVALEXTPROCglXSwapIntervalEXT=NULL;//Windows (WGL):typedefHGLRC(WINAPI*PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDChdc,HGLRChglrc,constint*attribList);PFNWGLCREATECONTEXTATTRIBSARBPROCwglCreateContextAttribsARB=NULL;typedefHRESULT(APIENTRY*PFNWGLCHOOSEPIXELFORMATARBPROC)(HDChdc,constint*piAttribIList,constFLOAT*pfAttribFList,UINTnMaxFormats,int*piFormats,UINT*nNumFormats);staticPFNWGLCHOOSEPIXELFORMATARBPROCwglChoosePixelFormatARB=NULL;//(optional)typedefBOOL(APIENTRY*PFNWGLSWAPINTERVALEXTPROC)(intinterval);staticPFNWGLSWAPINTERVALEXTPROCwglSwapIntervalEXT=NULL;
Enter fullscreen modeExit fullscreen mode

Once the functions are defined, RGFW loads the functions with these API calls:

/*X11 (GLX):*/glXGetProcAddressaadglXGetProcAddressARB/*Windows (WGL):*/wglGetProcAddress
Enter fullscreen modeExit fullscreen mode

For example using GLX,

glXCreateContextAttribsARB=glXGetProcAddressARB((GLubyte*)"glXCreateContextAttribsARB");;//(optional)glXSwapIntervalEXT=(PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress((GLubyte*)"glXSwapIntervalEXT");
Enter fullscreen modeExit fullscreen mode

WGL is a little bit more complicated because it needs to start by creating a dummy context.
This dummy context is used by WGL to load the functions.

WGL dummy

First, RGFW has to create a dummy window and device context, RGFW also uses this dummy window to get the height offset for the title bar.

HWNDdummyWin=CreateWindowA(Class.lpszClassName,name,window_style,x,y,w,h,0,0,inh,0);HDCdummy_dc=GetDC(dummyWin);
Enter fullscreen modeExit fullscreen mode

After that, RGFW creates a dummy pixel format for the dummy window and dummy OpenGL context.

u32pfd_flags=PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER;PIXELFORMATDESCRIPTORpfd={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};intpixel_format=ChoosePixelFormat(dummy_dc,&pfd);SetPixelFormat(dummy_dc,pixel_format,&pfd);
Enter fullscreen modeExit fullscreen mode

Now RGFW can create a dummy context and set it to be the current context.

This context will be using the default OpenGL ~1.0 context, OpenGL.

HGLRCdummy_context=wglCreateContext(dummy_dc);wglMakeCurrent(dummy_dc,dummy_context);
Enter fullscreen modeExit fullscreen mode

Now RGFW can load the functions and delete the dummy

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);
Enter fullscreen modeExit fullscreen mode

Step 2 (Create an OpenGL pixel format (or Visual on X11) using an attribute list)

RGFW needs to create a pixel format/visual so the window knows how to draw the data it gets and so OpenGL knows the draw format we want.

Step 2.1: creating the attribute list

To create an OpenGL pixel format using an attribute list, RGFW has a function that creates an attribute list,RGFW_initFormatAttribs for the pixel format.

This function uses macros based on the target OS's API, supporting all of the APIs with a single function.

For this tutorial, I'll be separating an array for each OS rather than using one big array.

//linux:staticu32attribs[]={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 insteadstaticu32attribs[]={0x2003,// WGL_ACCELERATION_ARB0x2027,// WGL_FULL_ACCELERATION_ARB0x201b,8,// WGL_ALPHA_BITS_ARB0x2022,24,// WGL_DEPTH_BITS_ARB0x2001,1,// WGL_DRAW_TO_WINDOW_ARB0x2015,8,// WGL_RED_BITS_ARB0x2017,8,// WGL_GREEN_BITS_ARB0x2019,8,// WGL_BLUE_BITS_ARB0x2013,0x202B,// WGL_PIXEL_TYPE_ARB,  WGL_TYPE_RGBA_ARB0x2010,1,// WGL_SUPPORT_OPENGL_ARB0x2014,32,// WGL_COLOR_BITS_ARB0,0,0,0,0,0,0,0,0,0,0,0,0};//macos:staticu32attribs[]={11,8,// alpha size24,24,// depth size72,// NSOpenGLPFANoRecovery8,24,// NSOpenGLPFAColorSize0,0,0,0,0,0,0,0,0,0,0,0,0};
Enter fullscreen modeExit fullscreen mode

You may notice the extra 0s at the bottom of the array, that's for optional arguments.

To fill in the optional arguments, first RGFW sets the index to the first 0.

size_tindex=(sizeof(attribs)/sizeof(attribs[0]))-13;
Enter fullscreen modeExit fullscreen mode

RGFW uses a macro to fill in optional arguments

#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \        if (attVal) { \            attribs[index] = attrib;\            attribs[index + 1] = attVal;\            index += 2;\        }
Enter fullscreen modeExit fullscreen mode

RGFW defines variables that can be changed by the user to fill in these arguments.

i32RGFW_STENCIL=0,RGFW_SAMPLES=0,RGFW_STEREO=GL_FALSE,RGFW_AUX_BUFFERS=0,RGFW_DOUBLE_BUFFER=1;
Enter fullscreen modeExit fullscreen mode

I'm going to split the arguments up between each platform.

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 laterwindows:if(RGFW_DOUBLE_BUFFER)RGFW_GL_ADD_ATTRIB(0x2011,1);// double bufferRGFW_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 bufferRGFW_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;}
Enter fullscreen modeExit fullscreen mode

On macOS RGFW also sets the version here.

attribs[index]=99;attribs[index+1]=0x1000;if(RGFW_majorVersion>=4||RGFW_majorVersion>=3){attribs[index+1]=(u32)((RGFW_majorVersion>=4)?0x4100:0x3200);}
Enter fullscreen modeExit fullscreen mode

Make sure the final two arguments are set to 0, this is how OpenGL/WGL/GLX/NSOpenGL knows to stop reading.

RGFW_GL_ADD_ATTRIB(0,0);
Enter fullscreen modeExit fullscreen mode

Step 2.2 creating the pixel format

Now that the list is created, it can be used to create the pixel format.

GLX

GLX Handles this by creating an array ofGLXFBConfig based on the attributes.

i32fbcount;GLXFBConfig*fbc=glXChooseFBConfig((Display*)display,DefaultScreen(display),(i32*)attribs,&fbcount);i32best_fbc=-1;if(fbcount==0){printf("Failed to find any valid GLX visual configs\n");returnNULL;}
Enter fullscreen modeExit fullscreen mode

Then it uses the generated array to find the closest matching FBConfig object. (This is where RGFW_SAMPLES comes in)

u32i;for(i=0;i<(u32)fbcount;i++){XVisualInfo*vi=glXGetVisualFromFBConfig((Display*)display,fbc[i]);if(vi==NULL)continue;XFree(vi);i32samp_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");returnNULL;}GLXFBConfigbestFbc=fbc[best_fbc];
Enter fullscreen modeExit fullscreen mode

Once it finds the closest matching object, it gets the X11 visual (or pixel format) from the array and frees the array.

/* Get a visual */XVisualInfo*vi=glXGetVisualFromFBConfig((Display*)display,bestFbc);XFree(fbc);
Enter fullscreen modeExit fullscreen mode

Now this Visual can be used to create a window and/or colormap.

XSetWindowAttributesswa;Colormapcmap;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;Windowwindow=XCreateWindow((Display*)display,DefaultRootWindow((Display*)display),x,y,w,h,0,vi->depth,InputOutput,vi->visual,CWColormap|CWBorderPixel|CWBackPixel|CWEventMask,&swa);
Enter fullscreen modeExit fullscreen mode

WGL

RGFW needs some WGL defines for creating the context:

#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
Enter fullscreen modeExit fullscreen mode

For WGL, RGFW has to first create a win32 pixel format.

PIXELFORMATDESCRIPTORpfd2=(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};
Enter fullscreen modeExit fullscreen mode

Then RGFW can create a WGL pixel format based on the attribs array.

intpixel_format2;UINTnum_formats;wglChoosePixelFormatARB(hdc,attribs,0,1,&pixel_format2,&num_formats);if(!num_formats){printf("Failed to create a pixel format for WGL.\n");}
Enter fullscreen modeExit fullscreen mode

Now RGFW can merge the two as one big format and set it as the format for your window.

DescribePixelFormat(hdc,pixel_format2,sizeof(pfd2),&pfd2);if(!SetPixelFormat(hdc,pixel_format2,&pfd2)){printf("Failed to set the WGL pixel format.\n");}
Enter fullscreen modeExit fullscreen mode

NSOpenGL

For MacOS, RGFW just has to create the pixel format and then init a view based on the format for 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"));
Enter fullscreen modeExit fullscreen mode

If you're following along for MacOS, you can skip step 3.

Step 3 (Create your OpenGL context using an attribute array to set the OpenGL version)

NOTE: RGFW defines this enum and these variables so the user can control the OpenGL version:

typedefu8RGFW_GL_profile;enum{RGFW_GL_CORE=0,RGFW_GL_COMPATIBILITY};i32RGFW_majorVersion=0,RGFW_minorVersion=0;b8RGFW_profile=RGFW_GL_CORE;
Enter fullscreen modeExit fullscreen mode

glx

Now it's time to create the attribute array for the GL context creation and load the OpenGL version you want:

i32context_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;elsecontext_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;}
Enter fullscreen modeExit fullscreen mode

Finally, the context can be created using the context_attribs array:

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
Enter fullscreen modeExit fullscreen mode

WGL

First WGL needs to create an attribs array for setting the OpenGL Version

It also uses a helper macro called 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 */u32index=0;i32context_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);
Enter fullscreen modeExit fullscreen mode

Now the context can be created:

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
Enter fullscreen modeExit fullscreen mode

Step 4 (Free the OpenGL context)

Now that RGFW has created its OpenGL context, it has to free the context when it's done using it.

This is the easy part.

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
Enter fullscreen modeExit fullscreen mode

Full code examples

X11

// compile with gcc x11.c -lX11 -lGL#include<X11/Xlib.h>#include<GL/glx.h>#include<stdio.h>#include<stdint.h>typedefGLXContext(*glXCreateContextAttribsARBProc)(Display*,GLXFBConfig,GLXContext,Bool,constint*);typedefvoid(*PFNGLXSWAPINTERVALEXTPROC)(Display*dpy,GLXDrawabledrawable,intinterval);#define GL_ADD_ATTRIB(attrib, attVal) \        if (attVal) { \            attribs[index] = attrib;\            attribs[index + 1] = attVal;\            index += 2;\        }typedefuint8_tGL_profile;enum{GL_CORE=0,GL_COMPATIBILITY};int32_tmajorVersion=0,minorVersion=0;Boolprofile=GL_CORE;int32_tSTENCIL=0,SAMPLES=0,STEREO=GL_FALSE,AUX_BUFFERS=0,DOUBLE_BUFFER=1;intmain(void){typedefvoid(*PFNGLXSWAPINTERVALEXTPROC)(Display*dpy,GLXDrawabledrawable,intinterval);PFNGLXSWAPINTERVALEXTPROCglXSwapIntervalEXT=NULL;glXCreateContextAttribsARBProcglXCreateContextAttribsARB=(glXCreateContextAttribsARBProc)glXGetProcAddressARB((GLubyte*)"glXCreateContextAttribsARB");glXSwapIntervalEXT=(PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress((GLubyte*)"glXSwapIntervalEXT");staticuint32_tattribs[]={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_tindex=(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;XEventevent;display=XOpenDisplay(NULL);if(display==NULL){fprintf(stderr,"Cannot open display\n");return-1;}ints=DefaultScreen(display);int32_tfbcount;GLXFBConfig*fbc=glXChooseFBConfig((Display*)display,DefaultScreen(display),(int32_t*)attribs,&fbcount);int32_tbest_fbc=-1;if(fbcount==0){printf("Failed to find any valid GLX visual configs\n");return-1;}uint32_ti;for(i=0;i<(uint32_t)fbcount;i++){XVisualInfo*vi=glXGetVisualFromFBConfig((Display*)display,fbc[i]);if(vi==NULL)continue;XFree(vi);int32_tsamp_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;}GLXFBConfigbestFbc=fbc[best_fbc];XVisualInfo*vi=glXGetVisualFromFBConfig((Display*)display,bestFbc);XFree(fbc);XSetWindowAttributesswa;Colormapcmap;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;Windowwindow=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_tcontext_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;elsecontext_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;}GLXContextctx=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);return0;}
Enter fullscreen modeExit fullscreen mode

Windows

// compile with gcc winapi.c -lopengl32 -lgdi32#include<windows.h>#include<GL/gl.h>#include<stdio.h>#include<stdint.h>#include<assert.h>typedefuint8_tu8;typedefint8_ti8;typedefuint16_tu16;typedefint16_ti16;typedefuint32_tu32;typedefint32_ti32;typedefuint64_tu64;typedefint64_ti64;typedefu8b8;#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              0x9126intmain(){typedefHGLRC(WINAPI*PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDChdc,HGLRChglrc,constint*attribList);PFNWGLCREATECONTEXTATTRIBSARBPROCwglCreateContextAttribsARB=NULL;typedefHRESULT(APIENTRY*PFNWGLCHOOSEPIXELFORMATARBPROC)(HDChdc,constint*piAttribIList,constFLOAT*pfAttribFList,UINTnMaxFormats,int*piFormats,UINT*nNumFormats);staticPFNWGLCHOOSEPIXELFORMATARBPROCwglChoosePixelFormatARB=NULL;typedefBOOL(APIENTRY*PFNWGLSWAPINTERVALEXTPROC)(intinterval);staticPFNWGLSWAPINTERVALEXTPROCwglSwapIntervalEXT=NULL;WNDCLASSwc={0};wc.lpfnWndProc=DefWindowProc;// Default window procedurewc.hInstance=GetModuleHandle(NULL);wc.lpszClassName="SampleWindowClass";RegisterClass(&wc);HWNDdummyWin=CreateWindowA(wc.lpszClassName,"Sample Window",0,200,200,300,300,0,0,wc.hInstance,0);HDCdummy_dc=GetDC(dummyWin);u32pfd_flags=PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER;PIXELFORMATDESCRIPTORpfd={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};intpixel_format=ChoosePixelFormat(dummy_dc,&pfd);SetPixelFormat(dummy_dc,pixel_format,&pfd);HGLRCdummy_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);staticu32attribs[]={0x2003,// WGL_ACCELERATION_ARB0x2027,// WGL_FULL_ACCELERATION_ARB0x201b,8,// WGL_ALPHA_BITS_ARB0x2022,24,// WGL_DEPTH_BITS_ARB0x2001,1,// WGL_DRAW_TO_WINDOW_ARB0x2015,8,// WGL_RED_BITS_ARB0x2017,8,// WGL_GREEN_BITS_ARB0x2019,8,// WGL_BLUE_BITS_ARB0x2013,0x202B,// WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB0x2010,1,// WGL_SUPPORT_OPENGL_ARB0x2014,32,// WGL_COLOR_BITS_ARB0,0,0,0,0,0,0,0,0,0,0,0,0};size_tindex=(sizeof(attribs)/sizeof(attribs[0]))-13;#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \        if (attVal) { \            attribs[index] = attrib;\            attribs[index + 1] = attVal;\            index += 2;\        }i32RGFW_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 bufferRGFW_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);typedefu8RGFW_GL_profile;enum{RGFW_GL_CORE=0,RGFW_GL_COMPATIBILITY};i32RGFW_majorVersion=0,RGFW_minorVersion=0;b8RGFW_profile=RGFW_GL_CORE;HWNDhwnd=CreateWindowA(wc.lpszClassName,"Sample Window",0,400,400,300,300,NULL,NULL,wc.hInstance,NULL);HDChdc=GetDC(hwnd);PIXELFORMATDESCRIPTORpfd2=(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};intpixel_format2;UINTnum_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;i32context_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);HGLRCctx=(HGLRC)wglCreateContextAttribsARB(hdc,NULL,context_attribs);wglMakeCurrent(hdc,ctx);ShowWindow(hwnd,SW_SHOW);UpdateWindow(hwnd);MSGmsg;BOOLrunning=TRUE;while(running){if(PeekMessageA(&msg,hwnd,0u,0u,PM_REMOVE)){switch(msg.message){caseWM_CLOSE:caseWM_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);return0;}
Enter fullscreen modeExit fullscreen mode

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

An objectivist, C programmer who's been programming for 4-5 years. I specialize in system programming, mainly with low level tech currently.
  • Joined

More fromColleague Riley

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp