diff options
| author | kkard2 <[email protected]> | 2025-09-22 17:21:30 +0200 |
|---|---|---|
| committer | kkard2 <[email protected]> | 2025-09-22 17:21:30 +0200 |
| commit | a17e7369bfe9cb4a23225084a396ec92823bad2b (patch) | |
| tree | b229b07c1a1c131467f61010eec985b62293897e /examples/platform/platform_win32.c | |
| parent | 2d0894f9887f9e100d7546b6ca93934eced93e39 (diff) | |
reorganize examples
Diffstat (limited to 'examples/platform/platform_win32.c')
| -rw-r--r-- | examples/platform/platform_win32.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/examples/platform/platform_win32.c b/examples/platform/platform_win32.c new file mode 100644 index 0000000..5984e75 --- /dev/null +++ b/examples/platform/platform_win32.c @@ -0,0 +1,219 @@ +#define UNICODE +#include <windows.h> +#include <windowsx.h> +#include <assert.h> +#include <stdio.h> +#include <io.h> + +#include <fcntl.h> + +#define SPONGE_IMPLEMENTATION +#include "../../sponge.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "../vendor/stb_image.h" + +#include "../example.h" + +#define DEFAULT_WIDTH 1280 +#define DEFAULT_HEIGHT 720 + +#define TARGET_FRAME_TIME_US 16666 + +static size_t pixels_buffer_count; +static sponge_Texture canvas; +static float *depths; +static BITMAPINFO bmi; + +int update_canvas(sponge_Texture *canvas, float **depths, uint32_t new_width, uint32_t new_height) { + size_t new_count = new_width * new_height; + if (new_count > pixels_buffer_count) { + sponge_Color32 *new_pixels_buffer = malloc(new_count * sizeof(uint32_t)); + float *new_depths = malloc(new_count * sizeof(float)); + if (!new_pixels_buffer || !new_depths) { + free(new_pixels_buffer); + free(new_pixels_buffer); + return 0; + } + free(canvas->pixels); + free(*depths); + canvas->pixels = new_pixels_buffer; + *depths = new_depths; + pixels_buffer_count = new_count; + } + canvas->width = new_width; + canvas->height = new_height; + canvas->stride_pixels = new_width; // TODO(kard): think about this if we want image to stay the same without redraw + + ZeroMemory(&bmi, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = new_width; + bmi.bmiHeader.biHeight = -(int32_t)new_height; // negative = top-down + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + return 1; +} + +// TODO(kard): MSVC complains about freopen (should be freopen_s apparently) +void redirect_io_to_console() { + AllocConsole(); + FILE *fp; + + assert(!freopen_s(&fp, "CONOUT$", "w", stdout)); + setvbuf(stdout, NULL, _IONBF, 0); + + assert(!freopen_s(&fp, "CONOUT$", "w", stderr)); + setvbuf(stderr, NULL, _IONBF, 0); + + assert(!freopen_s(&fp, "CONIN$", "r", stdin)); + setvbuf(stdin, NULL, _IONBF, 0); +} + +LRESULT __stdcall WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch (uMsg) { + case WM_DESTROY: { + PostQuitMessage(0); + return 0; + } + case WM_SIZE: { + uint32_t width = LOWORD(lParam); + uint32_t height = HIWORD(lParam); + // TODO(kard): handle properly? (what does that even mean) + assert(update_canvas(&canvas, &depths, width, height)); + } + case WM_MOUSEMOVE: { + int32_t x = GET_X_LPARAM(lParam); + int32_t y = GET_Y_LPARAM(lParam); + mouse_move(x, y); + } + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } +} + +int __stdcall WinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nShowCmd +) { + redirect_io_to_console(); + + const wchar_t CLASS_NAME[] = L"sponge"; + + WNDCLASS wc = { 0 }; + + wc.lpfnWndProc = WindowProc; + wc.hInstance = hInstance; + wc.lpszClassName = CLASS_NAME; + + RegisterClass(&wc); + + HWND hwnd = CreateWindowEx( + 0, + CLASS_NAME, + L"sponge", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, DEFAULT_WIDTH, DEFAULT_HEIGHT, + NULL, + NULL, + hInstance, + NULL + ); + + if (hwnd == NULL) + return 1; + + update_canvas(&canvas, &depths, DEFAULT_WIDTH, DEFAULT_HEIGHT); + + ShowWindow(hwnd, nShowCmd); + + MSG msg = {0}; + BOOL running = TRUE; + + init(); + + LARGE_INTEGER qpc_freq; + LARGE_INTEGER qpc_start; + LARGE_INTEGER qpc_end; + LARGE_INTEGER qpc_draw; + LARGE_INTEGER qpc_frame; + + LARGE_INTEGER qpc_running_draw = { 0 }; + size_t qpc_running_draw_count = 0; + + QueryPerformanceFrequency(&qpc_freq); + QueryPerformanceCounter(&qpc_start); + + while (running) { + QueryPerformanceCounter(&qpc_start); + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + running = FALSE; + break; + } + + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + if (!running) break; + + if (sponge_texture_valid(canvas)) + draw_frame_3d(canvas, depths); + + QueryPerformanceCounter(&qpc_end); + qpc_draw.QuadPart = qpc_end.QuadPart - qpc_start.QuadPart; + qpc_draw.QuadPart *= 1000000; // microseconds + qpc_draw.QuadPart /= qpc_freq.QuadPart; + qpc_running_draw.QuadPart += qpc_draw.QuadPart; + qpc_running_draw_count++; + + HDC hdc = GetDC(hwnd); + StretchDIBits( + hdc, + // TODO(kard): check what happens if canvas is bigger than window (it shouldn't differ currently) + 0, 0, canvas.width, canvas.height, // dest rectangle + 0, 0, canvas.width, canvas.height, // src rectangle + canvas.pixels, + &bmi, + DIB_RGB_COLORS, + SRCCOPY + ); + + InvalidateRect(hwnd, NULL, FALSE); + UpdateWindow(hwnd); + + QueryPerformanceCounter(&qpc_end); + qpc_frame.QuadPart = qpc_end.QuadPart - qpc_start.QuadPart; + qpc_frame.QuadPart *= 1000000; // microseconds + qpc_frame.QuadPart /= qpc_freq.QuadPart; + + fprintf(stderr, "draw time: %3lld.%03lldms (avg: %3lld.%03lldms), frame time: %3lld.%03lldms\n", + qpc_draw.QuadPart / 1000, qpc_draw.QuadPart % 1000, + qpc_running_draw.QuadPart / qpc_running_draw_count / 1000, qpc_running_draw.QuadPart / qpc_running_draw_count % 1000, + qpc_frame.QuadPart / 1000, qpc_frame.QuadPart % 1000); + + // TODO(kard): + // 1. this can probably be done cleaner + // 2. resetting after every frame is wrong, it should be a value across frames (see 1.) + if (qpc_frame.QuadPart < TARGET_FRAME_TIME_US) { + LARGE_INTEGER remaining; + LARGE_INTEGER actual_end; + actual_end.QuadPart = qpc_start.QuadPart + (TARGET_FRAME_TIME_US * qpc_freq.QuadPart / 1000000); + remaining.QuadPart = (actual_end.QuadPart - qpc_end.QuadPart) * 1000000 / qpc_freq.QuadPart; + if (remaining.QuadPart > 2000) { + Sleep((DWORD)((remaining.QuadPart - 1000) / 1000)); + } + + while (qpc_end.QuadPart < actual_end.QuadPart) { + QueryPerformanceCounter(&qpc_end); + } + } + + Sleep(16); + } + + return (int) msg.wParam; +} |
