Last active
May 14, 2025 09:03
-
-
Save gszauer/03fa4d1fbd72bfc169f70e78eabe3c4c to your computer and use it in GitHub Desktop.
OpenGLInitWin32.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define WIN32_LEAN_AND_MEAN | |
#include <windows.h> | |
#include <ShellScalingAPI.h> // Shcore.lib | |
#include "glad.h" // OpenGL32.lib | |
#pragma comment(lib, "OpenGL32.lib") | |
#pragma comment(linker, "/SUBSYSTEM:WINDOWS") | |
#define WINDOW_WIDTH 800 | |
#define WINDOW_HEIGHT 600 | |
#define WINDOW_CLASS "OGL_Sample" | |
#define WINDOW_TITLE "OpenGL Sample" | |
// Used for setting up a modern OpenGL Context | |
typedef HGLRC(WINAPI* PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShareContext, const int* attribList); | |
typedef BOOL(WINAPI* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats); | |
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 | |
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 | |
#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 | |
#define WGL_CONTEXT_FLAGS_ARB 0x2094 | |
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 | |
#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 | |
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 | |
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 | |
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 | |
#define WGL_DRAW_TO_WINDOW_ARB 0x2001 | |
#define WGL_SUPPORT_OPENGL_ARB 0x2010 | |
#define WGL_DOUBLE_BUFFER_ARB 0x2011 | |
#define WGL_PIXEL_TYPE_ARB 0x2013 | |
#define WGL_COLOR_BITS_ARB 0x2014 | |
#define WGL_DEPTH_BITS_ARB 0x2022 | |
#define WGL_STENCIL_BITS_ARB 0x2023 | |
#define WGL_TYPE_RGBA_ARB 0x202B | |
#define WGL_SAMPLE_BUFFERS_ARB 0x2041 | |
#define WGL_SAMPLES_ARB 0x2042 | |
// Demo framework | |
struct WindowData { | |
HWND hwnd; | |
HDC hdc; | |
HGLRC hglrc; | |
HINSTANCE hInstance; // Window instance | |
bool closeWindow; | |
}; | |
void* Initialize(const WindowData& windowData); | |
void Update(void* userData, float deltatTime); | |
void Render(void* userData, RECT clientRect); | |
void Shutdown(void* userData); | |
// Forward declared callbacks | |
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam); | |
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow); | |
// Command line entry point | |
int main(int argc, char** argv) { | |
return WinMain(GetModuleHandleA(NULL), NULL, GetCommandLineA(), SW_SHOWDEFAULT); | |
} | |
HDC gHdc; | |
// Windowed entry point | |
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { | |
// Make process DPI aware | |
HRESULT hr = SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); | |
float dpi = (float)GetDpiForSystem() / 96.0f; | |
// Two windows. A temp one to create a legacy opengl context, and a real one with a msaa context. | |
WNDCLASSA wndclass, tmpclass; | |
ZeroMemory(&wndclass, sizeof(WNDCLASSA)); | |
ZeroMemory(&tmpclass, sizeof(WNDCLASSA)); | |
tmpclass.style = wndclass.style = CS_HREDRAW | CS_VREDRAW; | |
wndclass.lpfnWndProc = WndProc; | |
tmpclass.lpfnWndProc = DefWindowProcA; | |
tmpclass.hInstance = wndclass.hInstance = hInstance; | |
tmpclass.hIcon = wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); | |
tmpclass.hCursor = wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); | |
tmpclass.hbrBackground = wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); | |
wndclass.lpszClassName = WINDOW_CLASS; | |
tmpclass.lpszClassName = "tmp_gl_context_window"; | |
RegisterClassA(&wndclass); | |
RegisterClassA(&tmpclass); | |
// Figure out window size | |
int windowWidth = (float)WINDOW_WIDTH * dpi; | |
int windowHeight = (float)WINDOW_HEIGHT * dpi; | |
RECT rClient; | |
SetRect(&rClient, 0, 0, windowWidth, windowHeight); | |
AdjustWindowRect(&rClient, WS_OVERLAPPEDWINDOW | WS_VISIBLE, FALSE); | |
int screenWidth = GetSystemMetrics(SM_CXSCREEN); | |
int screenHeight = GetSystemMetrics(SM_CYSCREEN); | |
windowWidth = rClient.right - rClient.left; | |
windowHeight = rClient.right - rClient.left; | |
// Create dummy window | |
HWND hwnd = CreateWindowA(tmpclass.lpszClassName, (char*)(WINDOW_TITLE), WS_OVERLAPPEDWINDOW, | |
(screenWidth / 2) - (windowWidth / 2), (screenHeight / 2) - (windowHeight / 2), | |
windowWidth, windowHeight, NULL, NULL, hInstance, 0); | |
HDC hdc = GetDC(hwnd); | |
// Make legacy OpenGL request | |
PIXELFORMATDESCRIPTOR pfd; | |
ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR)); | |
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); | |
pfd.nVersion = 1; | |
pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; | |
pfd.iPixelType = PFD_TYPE_RGBA; | |
pfd.cColorBits = 32; | |
pfd.cDepthBits = 24; | |
pfd.cStencilBits = 8; | |
pfd.iLayerType = PFD_MAIN_PLANE; | |
int pixelFormat = ChoosePixelFormat(hdc, &pfd); | |
SetPixelFormat(hdc, pixelFormat, &pfd); | |
HGLRC hglrc = wglCreateContext(hdc); | |
wglMakeCurrent(hdc, hglrc); | |
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB"); // Recognised (Why this and not the function name?) | |
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); | |
// Delete the legacy context and destroy the temp window | |
wglMakeCurrent(hdc, NULL); | |
wglDeleteContext(hglrc); | |
ReleaseDC(hwnd, hdc); | |
DestroyWindow(hwnd); | |
// Create new (final) window | |
hwnd = CreateWindowA(wndclass.lpszClassName, (char*)(WINDOW_TITLE), WS_OVERLAPPEDWINDOW | WS_VISIBLE, | |
(screenWidth / 2) - (windowWidth / 2), (screenHeight / 2) - (windowHeight / 2), | |
windowWidth, windowHeight, NULL, NULL, hInstance, 0); | |
hdc = GetDC(hwnd); | |
gHdc = hdc; | |
// Create a modern OpenGL context | |
const int iPixelFormatAttribList[] = { | |
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, | |
WGL_SAMPLE_BUFFERS_ARB,1, | |
WGL_SAMPLES_ARB,8, | |
0, 0 // End of attributes list | |
}; | |
int nPixelFormat = 0; | |
UINT iNumFormats = 0; | |
wglChoosePixelFormatARB(hdc, iPixelFormatAttribList, NULL, 1, &nPixelFormat, (UINT*)&iNumFormats); | |
ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR)); | |
DescribePixelFormat(hdc, nPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd); | |
SetPixelFormat(hdc, nPixelFormat, &pfd); | |
const int attributes[] = { | |
WGL_CONTEXT_MAJOR_VERSION_ARB, 3, | |
WGL_CONTEXT_MINOR_VERSION_ARB, 2, | |
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, | |
NULL | |
}; | |
hglrc = wglCreateContextAttribsARB(hdc, NULL, attributes); | |
wglMakeCurrent(hdc, hglrc); | |
// Load all extensions | |
if (!gladLoadGL()) { | |
exit(-1); | |
} | |
// OpenGL is now initialized | |
WindowData windowData = { 0 }; | |
windowData.hwnd = hwnd; | |
windowData.hglrc = hglrc; | |
windowData.hdc = hdc; | |
windowData.hInstance = hInstance; | |
windowData.closeWindow = false; | |
LONG_PTR lptr = (LONG_PTR)(&windowData); | |
SetWindowLongPtrA(hwnd, GWLP_USERDATA, lptr); | |
ShowWindow(hwnd, SW_NORMAL); | |
UpdateWindow(hwnd); | |
void* userData = Initialize(windowData); | |
MSG msg; | |
bool running = true; | |
bool quit = false; | |
LARGE_INTEGER timerFrequency; | |
LARGE_INTEGER thisTick; | |
LARGE_INTEGER lastTick; | |
LONGLONG timeDelta; | |
if (!QueryPerformanceFrequency(&timerFrequency)) { | |
OutputDebugStringA("WinMain: QueryPerformanceFrequency failed\n"); | |
} | |
QueryPerformanceCounter(&thisTick); | |
lastTick = thisTick; | |
while (!quit) { | |
while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) { | |
if (msg.message == WM_QUIT) { | |
quit = true; | |
break; | |
} | |
TranslateMessage(&msg); | |
DispatchMessageA(&msg); | |
} | |
if (windowData.closeWindow && running) { | |
Shutdown(userData); | |
running = false; | |
} | |
else if (running) { | |
RECT clientRect = {}; | |
GetClientRect(hwnd, &clientRect); | |
QueryPerformanceCounter(&thisTick); | |
timeDelta = thisTick.QuadPart - lastTick.QuadPart; | |
double deltaTime = (double)timeDelta * 1000.0 / (double)timerFrequency.QuadPart; | |
Update(userData, (float)deltaTime); | |
Render(userData, clientRect); | |
lastTick = thisTick; | |
SwapBuffers(hdc); | |
Sleep(1); // TODO: VSynch | |
} | |
} | |
return (int)msg.wParam; | |
} | |
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { | |
switch (iMsg) { | |
case WM_CLOSE: | |
{ | |
LONG_PTR lptr = GetWindowLongPtrA(hwnd, GWLP_USERDATA); | |
WindowData* windowData = (WindowData*)lptr; | |
windowData->closeWindow = true; | |
} | |
DestroyWindow(hwnd); | |
return 0; | |
case WM_DESTROY: | |
PostQuitMessage(0); | |
return 0; | |
} | |
return DefWindowProcA(hwnd, iMsg, wParam, lParam); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment