Skip to content

Instantly share code, notes, and snippets.

@yanpeng
Last active April 5, 2022 10:13
Show Gist options
  • Save yanpeng/b55590c7467f3b3615ce425f1a2a7da1 to your computer and use it in GitHub Desktop.
Save yanpeng/b55590c7467f3b3615ce425f1a2a7da1 to your computer and use it in GitHub Desktop.
Create OpenGL Rendering Context from Scratch (Win32)
// To run this program.
// 1. SUBSYSTEM:WINDOWS
// 2. Link glad.cpp
// 3. Set include directories that includes glad.h and wglext.h
// https://www.khronos.org/opengl/wiki/Creating_an_OpenGL_Context_(WGL)
// https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_create_context.txt
#include <Windows.h>
#include <glad/glad.h>
// #include <GL/glext.h> // No use, don't use gl extensions here.
#include <GL/wglext.h>
#include <stdexcept>
#pragma comment(lib, "opengl32.lib")
static GLint g_majorVersion;
static GLint g_minorVersion;
// WGL extensions
static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
static void* GetAnyGLFuncAddress(const char* name);
static void LoadWGLExtensions();
static bool CreateFakeWindow();
static bool CreateRealWindow(HWND& hWnd);
void FatalError(const TCHAR* msg, const TCHAR* caption)
{
MessageBox(NULL, msg, caption, MB_ICONERROR);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ PSTR szCmdLine, _In_ int iCmdShow)
{
if (!CreateFakeWindow())
{
return -1;
}
HWND hWnd;
bool result = CreateRealWindow(hWnd);
if (!result)
{
return -1;
}
ShowWindow(hWnd, SW_SHOWNORMAL);
SetForegroundWindow(hWnd);
HDC hDC = GetDC(hWnd);
bool active = true;
while (active)
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
active = false;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
SwapBuffers(hDC);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hWnd, message, wParam, lParam);
}
return 0;
}
void* GetAnyGLFuncAddress(const char* name)
{
void* p = (void*)wglGetProcAddress(name);
if (p == 0 || (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) || (p == (void*)-1))
{
HMODULE module = LoadLibraryA("opengl32.dll");
if (module != NULL)
{
p = (void*)GetProcAddress(module, name);
}
else
{
throw std::invalid_argument("Cannot load opengl32.dll.");
}
}
return p;
}
void LoadWGLExtensions()
{
wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
}
bool CreateFakeWindow()
{
static TCHAR appName[] = TEXT("Fake Window");
WNDCLASSEX wndClass;
memset(&wndClass, 0, sizeof(WNDCLASSEXW));
wndClass.cbSize = sizeof(WNDCLASSEXW);
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wndClass.lpfnWndProc = DefWindowProc;
wndClass.hInstance = (HINSTANCE)GetModuleHandleW(NULL);
wndClass.lpszClassName = appName;
if (!RegisterClassEx(&wndClass))
{
FatalError(TEXT("Cannot register window class for fake window."), appName);
return false;
}
HWND fakeHWnd = CreateWindowEx(
0,
wndClass.lpszClassName,
TEXT("Fake Window"),
0,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
wndClass.hInstance,
NULL);
if (!fakeHWnd)
{
FatalError(TEXT("Failed to create fake window."), appName);
return false;
}
PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, //Flags
PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette.
32, // Colordepth of the framebuffer.
0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0, 0,
24, // Number of bits for the depthbuffer
8, // Number of bits for the stencilbuffer
0, // Number of Aux buffers in the framebuffer.
PFD_MAIN_PLANE,
0,
0, 0, 0
};
HDC fakeHDC = GetDC(fakeHWnd);
int pixelFormatIndex = ChoosePixelFormat(fakeHDC, &pfd);
if (pixelFormatIndex == 0)
{
FatalError(TEXT("Cannot acquire a pixel format."), appName);
ReleaseDC(fakeHWnd, fakeHDC);
DestroyWindow(fakeHWnd);
return false;
}
if (!SetPixelFormat(fakeHDC, pixelFormatIndex, &pfd))
{
FatalError(TEXT("Cannot set the pixel format."), appName);
ReleaseDC(fakeHWnd, fakeHDC);
DestroyWindow(fakeHWnd);
return false;
}
HGLRC fakeOpenGLRenderingContext = wglCreateContext(fakeHDC);
if (!fakeOpenGLRenderingContext)
{
FatalError(TEXT("Failed to create the fake OpenGL context."), appName);
ReleaseDC(fakeHWnd, fakeHDC);
DestroyWindow(fakeHWnd);
return false;
}
if (!wglMakeCurrent(fakeHDC, fakeOpenGLRenderingContext))
{
wglDeleteContext(fakeOpenGLRenderingContext);
FatalError(TEXT("Failed to make current."), appName);
ReleaseDC(fakeHWnd, fakeHDC);
DestroyWindow(fakeHWnd);
return false;
}
// Use GLAD to load opengl functions(only core here).
// GLAD checks OpenGL version internally.
gladLoadGLLoader((GLADloadproc)GetAnyGLFuncAddress);
LoadWGLExtensions();
glGetIntegerv(GL_MAJOR_VERSION, &g_majorVersion);
glGetIntegerv(GL_MINOR_VERSION, &g_minorVersion);
//// Check OpenGL extensions here, which method is selected depending on
//// the OpenGL version.
//// GLint numExtensions;
//// glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
//// auto ex0 = (const char*)glGetStringi(GL_EXTENSIONS, 0);
//// Check wgl extensions here
///*PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
//const char* wglExtensions = (const char*)wglGetExtensionsStringARB(hDC);*/
wglDeleteContext(fakeOpenGLRenderingContext);
ReleaseDC(fakeHWnd, fakeHDC);
DestroyWindow(fakeHWnd);
return true;
}
bool CreateRealWindow(HWND& hWnd)
{
static TCHAR appName[] = TEXT("OpenGL Window");
WNDCLASSEX wndClass;
memset(&wndClass, 0, sizeof(WNDCLASSEXW));
wndClass.cbSize = sizeof(WNDCLASSEXW);
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wndClass.lpfnWndProc = WndProc;
wndClass.hInstance = (HINSTANCE)GetModuleHandleW(NULL);
wndClass.hIcon = NULL;
wndClass.hIconSm = NULL;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.lpszClassName = appName;
if (!RegisterClassEx(&wndClass))
{
FatalError(TEXT("Cannot register window class for OpenGL window."), appName);
return NULL;
}
MONITORINFO monitorInfo;
monitorInfo.cbSize = sizeof(monitorInfo);
GetMonitorInfo(MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY), &monitorInfo);
long x = monitorInfo.rcMonitor.left;
long y = monitorInfo.rcMonitor.top;
const long monitorWidth = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;
const long monitorHeight = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top;
DWORD style = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
DWORD exStyle = WS_EX_APPWINDOW;
RECT rect;
SetRect(&rect, 0, 0, 800, 600);
AdjustWindowRectEx(&rect, style, FALSE, exStyle);
long width = rect.right - rect.left;
long height = rect.bottom - rect.top;
x += (monitorWidth - width) / 2;
y += (monitorHeight - height) / 2;
hWnd = CreateWindowEx(
exStyle,
wndClass.lpszClassName,
TEXT("OpenGL Window"),
style,
x, y, width, height,
NULL,
NULL,
wndClass.hInstance,
NULL);
if (!hWnd)
{
FatalError(TEXT("Failed to create OpenGL window."), appName);
return false;
}
HDC hDC = GetDC(hWnd);
const int attribList[] =
{
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, 32,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
0, // End
};
int pixelFormat;
UINT numFormats;
UINT maxFormats = 1;
wglChoosePixelFormatARB(hDC, attribList, NULL, maxFormats, &pixelFormat, &numFormats);
if (numFormats == 0)
{
FatalError(TEXT("Cannot get pixel format."), appName);
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd);
return false;
}
PIXELFORMATDESCRIPTOR pfd;
DescribePixelFormat(hDC, pixelFormat, sizeof(pfd), &pfd);
if (!SetPixelFormat(hDC, pixelFormat, &pfd))
{
FatalError(TEXT("Failed to set the pixel format."), appName);
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd);
return false;
}
const int contextAttribList[] =
{
WGL_CONTEXT_MAJOR_VERSION_ARB, g_majorVersion,
WGL_CONTEXT_MINOR_VERSION_ARB, g_minorVersion,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0
};
HGLRC openglRenderingContext = wglCreateContextAttribsARB(hDC, NULL, contextAttribList);
if (!openglRenderingContext)
{
FatalError(TEXT("Failed to create OpenGL rendering context."), appName);
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd);
return false;
}
if (!wglMakeCurrent(hDC, openglRenderingContext))
{
wglDeleteContext(openglRenderingContext);
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd);
return false;
}
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment