Dqn/misc/MinimalWin32ModernOpenGL/Win32.cpp

428 lines
13 KiB
C++

#define UNICODE
#define _UNICODE
#include <Windows.h>
#include <Windowsx.h> // For GET_X_PARAM()/GET_Y_PARAM() macro
#include <Psapi.h> // For GetProcessMemoryInfo()
#include <gl/gl.h>
#include "external/glext.h"
#include "external/wglext.h"
#define DQN_IMPLEMENTATION
#define DQN_WIN32_IMPLEMENTATION
#include "../../dqn.h"
FILE_SCOPE bool globalRunning = true;
FILE_SCOPE LRESULT CALLBACK Win32MainProcCallback(HWND window, UINT msg,
WPARAM wParam, LPARAM lParam)
{
const LRESULT MESSAGE_HANDLED = 0;
LRESULT result = MESSAGE_HANDLED;
switch (msg)
{
case WM_CLOSE:
{
globalRunning = false;
}
break;
default:
{
result = DefWindowProcW(window, msg, wParam, lParam);
}
break;
}
return result;
}
FILE_SCOPE void Win32ProcessMessages(HWND window)
{
MSG msg;
while (PeekMessage(&msg, window, 0, 0, PM_REMOVE))
{
switch (msg.message)
{
case WM_COMMAND:
{
}
break;
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONUP:
{
bool isDown = (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN);
(void)isDown;
if (msg.message == WM_LBUTTONDOWN || msg.message == WM_LBUTTONUP)
{
}
else if (msg.message == WM_RBUTTONDOWN || msg.message == WM_RBUTTONUP)
{
}
}
break;
case WM_MOUSEMOVE:
{
LONG height;
DqnWin32_GetClientDim(window, NULL, &height);
f32 mouseX = (f32)(GET_X_LPARAM(msg.lParam));
f32 mouseY = (f32)(height - GET_Y_LPARAM(msg.lParam));
(void)mouseX; (void)mouseY;
}
break;
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP:
{
bool isDown = (msg.message == WM_KEYDOWN);
switch (msg.wParam)
{
case VK_UP: break;
case VK_DOWN: break;
case VK_LEFT: break;
case VK_RIGHT: break;
case '1': break;
case '2': break;
case '3': break;
case '4': break;
case 'Q': break;
case 'W': break;
case 'E': break;
case 'R': break;
case 'A': break;
case 'S': break;
case 'D': break;
case 'F': break;
case 'Z': break;
case 'X': break;
case 'C': break;
case 'V': break;
case VK_ESCAPE:
{
if (isDown) globalRunning = false;
}
break;
default: break;
}
}
break;
default:
{
TranslateMessage(&msg);
DispatchMessage(&msg);
};
}
}
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
{
////////////////////////////////////////////////////////////////////////////
// App Init
////////////////////////////////////////////////////////////////////////////
// Main Loop Config
const f32 TARGET_FRAMES_PER_S = 60.0f;
f32 targetSecondsPerFrame = 1 / TARGET_FRAMES_PER_S;
f64 frameTimeInS = 0.0f;
// Window Config
const char WINDOW_TITLE_A[] = u8"MinimalWin32OpenGL";
const char WINDOW_CLASS_A[] = u8"MinimalWin32OpenGLClass";
// UTF-8 Version
wchar_t windowTitleW[DQN_ARRAY_COUNT(WINDOW_TITLE_A)] = {};
wchar_t windowClassW[DQN_ARRAY_COUNT(WINDOW_CLASS_A)] = {};
DQN_ASSERT(DqnWin32_UTF8ToWChar(WINDOW_TITLE_A, windowTitleW, DQN_ARRAY_COUNT(windowTitleW)));
DQN_ASSERT(DqnWin32_UTF8ToWChar(WINDOW_CLASS_A, windowClassW, DQN_ARRAY_COUNT(windowClassW)));
const u32 BUFFER_WIDTH = 800;
const u32 BUFFER_HEIGHT = 600;
(void)nShowCmd;
(void)lpCmdLine;
(void)hPrevInstance;
////////////////////////////////////////////////////////////////////////////
// Setup OpenGL
////////////////////////////////////////////////////////////////////////////
HWND mainWindow;
{
////////////////////////////////////////////////////////////////////////
// Create Temp Win32 Window For Temp OGL Rendering Context
////////////////////////////////////////////////////////////////////////
WNDCLASSEXW windowClass = {
sizeof(WNDCLASSEX),
CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
Win32MainProcCallback,
0, // int cbClsExtra
0, // int cbWndExtra
hInstance,
LoadIcon(NULL, IDI_APPLICATION),
LoadCursor(NULL, IDC_ARROW),
GetSysColorBrush(COLOR_3DFACE),
L"", // LPCTSTR lpszMenuName
windowClassW,
NULL, // HICON hIconSm
};
// Register and create tmp window
HWND tmpWindow;
{
if (!RegisterClassExW(&windowClass))
{
DqnWin32_DisplayLastError("RegisterClassEx() failed.");
return -1;
}
tmpWindow =
CreateWindowExW(0, windowClass.lpszClassName, windowTitleW, 0, CW_USEDEFAULT,
CW_USEDEFAULT, 0, 0, NULL, NULL, hInstance, NULL);
if (!tmpWindow)
{
DqnWin32_DisplayLastError("CreateWindowEx() failed.");
return -1;
}
}
////////////////////////////////////////////////////////////////////////
// Setup A Temp OGL Context To Use For Getting Extended Functionality
////////////////////////////////////////////////////////////////////////
HDC tmpDeviceContext = GetDC(tmpWindow);
HGLRC tmpOGLRenderingContext;
{
PIXELFORMATDESCRIPTOR tmpDesiredPixelFormat = {};
tmpDesiredPixelFormat.nSize = sizeof(tmpDesiredPixelFormat);
tmpDesiredPixelFormat.nVersion = 1;
tmpDesiredPixelFormat.iPixelType = PFD_TYPE_RGBA;
tmpDesiredPixelFormat.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
tmpDesiredPixelFormat.cColorBits = 32;
tmpDesiredPixelFormat.cAlphaBits = 8;
tmpDesiredPixelFormat.iLayerType = PFD_MAIN_PLANE;
// ChoosePixelFormat finds the closest matching pixelFormat available on the machine
i32 tmpSuggestedPixelFormatIndex =
ChoosePixelFormat(tmpDeviceContext, &tmpDesiredPixelFormat);
// Create the PixelFormat based on the closest matching one
PIXELFORMATDESCRIPTOR tmpSuggestedPixelFormat = {};
DescribePixelFormat(tmpDeviceContext, tmpSuggestedPixelFormatIndex,
sizeof(PIXELFORMATDESCRIPTOR), &tmpSuggestedPixelFormat);
// Set it and assign the OpenGL context to our program
if (!SetPixelFormat(tmpDeviceContext, tmpSuggestedPixelFormatIndex,
&tmpSuggestedPixelFormat))
{
DqnWin32_DisplayLastError("SetPixelFormat() failed");
return -1;
}
tmpOGLRenderingContext = wglCreateContext(tmpDeviceContext);
if (!wglMakeCurrent(tmpDeviceContext, tmpOGLRenderingContext))
{
DqnWin32_DisplayLastError("wglMakeCurrent() failed");
return -1;
}
}
////////////////////////////////////////////////////////////////////////
// Load WGL Functions For Creating Modern OGL Windows
////////////////////////////////////////////////////////////////////////
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (wglGetProcAddress("wglChoosePixelFormatARB"));
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)(wglGetProcAddress("wglCreateContextAttribsARB"));
{
if (!wglChoosePixelFormatARB)
{
DqnWin32_DisplayLastError("wglGetProcAddress() failed");
return -1;
}
if (!wglCreateContextAttribsARB)
{
DqnWin32_DisplayLastError("wglGetProcAddress() failed");
return -1;
}
}
////////////////////////////////////////////////////////////////////////
// Create Window Using Modern OGL Functions
////////////////////////////////////////////////////////////////////////
{
// NOTE: Regarding Window Sizes
// If you specify a window size, e.g. 800x600, Windows regards the 800x600
// region to be inclusive of the toolbars and side borders. So in actuality,
// when you blit to the screen blackness, the area that is being blitted to
// is slightly smaller than 800x600. Windows provides a function to help
// calculate the size you'd need by accounting for the window style.
RECT windowRect = {};
windowRect.right = BUFFER_WIDTH;
windowRect.bottom = BUFFER_HEIGHT;
const bool HAS_MENU_BAR = false;
const DWORD WINDOW_STYLE = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
AdjustWindowRect(&windowRect, WINDOW_STYLE, HAS_MENU_BAR);
mainWindow =
CreateWindowExW(0, windowClass.lpszClassName, windowTitleW, WINDOW_STYLE,
CW_USEDEFAULT, CW_USEDEFAULT, windowRect.right - windowRect.left,
windowRect.bottom - windowRect.top, NULL, NULL, hInstance, NULL);
if (!mainWindow)
{
DqnWin32_DisplayLastError("CreateWindowEx() failed.");
return -1;
}
const i32 DESIRED_PIXEL_FORMAT[] = {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_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_COLOR_BITS_ARB, 32,
WGL_ALPHA_BITS_ARB, 8,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
WGL_SAMPLES_ARB, 4,
0};
HDC deviceContext = GetDC(mainWindow);
i32 suggestedPixelFormatIndex;
u32 numFormatsSuggested;
bool wglResult =
wglChoosePixelFormatARB(deviceContext, DESIRED_PIXEL_FORMAT, NULL, 1,
&suggestedPixelFormatIndex, &numFormatsSuggested);
if (!wglResult || numFormatsSuggested == 0)
{
DQN_WIN32_ERROR_BOX("wglChoosePixelFormatARB() failed", NULL);
return -1;
}
PIXELFORMATDESCRIPTOR suggestedPixelFormat = {};
DescribePixelFormat(deviceContext, suggestedPixelFormatIndex,
sizeof(suggestedPixelFormat), &suggestedPixelFormat);
if (!SetPixelFormat(deviceContext, suggestedPixelFormatIndex, &suggestedPixelFormat))
{
DqnWin32_DisplayLastError("SetPixelFormat() Modern OGL Creation failed");
return -1;
}
const i32 OGL_MAJOR_MIN = 4, OGL_MINOR_MIN = 5;
const i32 CONTEXT_ATTRIBS[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, OGL_MAJOR_MIN,
WGL_CONTEXT_MINOR_VERSION_ARB, OGL_MINOR_MIN,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0};
HGLRC oglRenderingContext =
wglCreateContextAttribsARB(deviceContext, 0, CONTEXT_ATTRIBS);
if (!oglRenderingContext)
{
DQN_WIN32_ERROR_BOX("wglCreateContextAttribsARB() failed", NULL);
return -1;
}
wglMakeCurrent(NULL, NULL);
wglDeleteContext(tmpOGLRenderingContext);
ReleaseDC(tmpWindow, tmpDeviceContext);
DestroyWindow(tmpWindow);
if (!wglMakeCurrent(deviceContext, oglRenderingContext))
{
DqnWin32_DisplayLastError("wglMakeCurrent() Modern OGL Creation failed");
return -1;
}
ReleaseDC(mainWindow, deviceContext);
// TODO: Load all the OGL function pointers we need here
}
}
while (globalRunning)
{
f64 startFrameTimeInS = DqnTimer_NowInS();
Win32ProcessMessages(mainWindow);
////////////////////////////////////////////////////////////////////////
// Rendering
////////////////////////////////////////////////////////////////////////
if (1)
{
HDC deviceContext = GetDC(mainWindow);
glViewport(0, 0, BUFFER_WIDTH, BUFFER_HEIGHT);
glClearColor(1.0f, 0.0f, 1.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
SwapBuffers(deviceContext);
ReleaseDC(mainWindow, deviceContext);
}
////////////////////////////////////////////////////////////////////////
// Frame Limiting
////////////////////////////////////////////////////////////////////////
if (1)
{
f64 workTimeInS = DqnTimer_NowInS() - startFrameTimeInS;
if (workTimeInS < targetSecondsPerFrame)
{
DWORD remainingTimeInMs =
(DWORD)((targetSecondsPerFrame - workTimeInS) * 1000);
Sleep(remainingTimeInMs);
}
}
frameTimeInS = DqnTimer_NowInS() - startFrameTimeInS;
f32 msPerFrame = 1000.0f * (f32)frameTimeInS;
f32 framesPerSecond = 1.0f / (f32)frameTimeInS;
////////////////////////////////////////////////////////////////////////
// Misc
////////////////////////////////////////////////////////////////////////
// Update title bar
if (1)
{
const f32 titleUpdateFrequencyInS = 0.1f;
LOCAL_PERSIST f32 titleUpdateTimer = titleUpdateFrequencyInS;
titleUpdateTimer += (f32)frameTimeInS;
if (titleUpdateTimer > titleUpdateFrequencyInS)
{
titleUpdateTimer = 0;
// Get Win32 reported mem usage
PROCESS_MEMORY_COUNTERS memCounter = {};
GetProcessMemoryInfo(GetCurrentProcess(), &memCounter, sizeof(memCounter));
// Create UTF-8 buffer string
const char formatStr[] = "%s - dev - %5.2f ms/f - %5.2f fps - working set mem %'dkb - page file touched mem %'dkb";
const u32 windowTitleBufSize = DQN_ARRAY_COUNT(formatStr) + DQN_ARRAY_COUNT(WINDOW_TITLE_A) + 32;
char windowTitleBufA[windowTitleBufSize] = {};
// Form UTF-8 buffer string
Dqn_sprintf(windowTitleBufA, formatStr, WINDOW_TITLE_A, msPerFrame, framesPerSecond,
(u32)(memCounter.WorkingSetSize / 1024.0f),
(u32)(memCounter.PagefileUsage / 1024.0f));
// Convert to wchar_t for windows
wchar_t windowTitleBufW[windowTitleBufSize] = {};
DQN_ASSERT(DqnWin32_UTF8ToWChar(windowTitleBufA, windowTitleBufW, windowTitleBufSize));
SetWindowTextW(mainWindow, windowTitleBufW);
}
}
}
return 0;
}