wintoys

wintoys.git
git clone git://git.lenczewski.org/wintoys.git
Log | Files | Refs | README

win32_gldemo.c (5713B)


      1 #define WIN32_LEAN_AND_MEAN 1
      2 #include <windows.h>
      3 
      4 #include <EGL/egl.h>
      5 // #include <EGL/eglplatform.h>
      6 // #include <GLES2/gl2.h>
      7 
      8 static void
      9 win32_process_messages(void);
     10 
     11 LRESULT CALLBACK
     12 win32_window_proc(HWND window, UINT msg, WPARAM wparam, LPARAM lparam);
     13 
     14 static void
     15 render_init(void);
     16 
     17 static void
     18 render(UINT width, UINT height, EGLDisplay egl_display, EGLSurface egl_surface);
     19 
     20 static bool win32_running;
     21 static HWND win32_window;
     22 
     23 int WINAPI
     24 WinMain(HINSTANCE instance, HINSTANCE prev_instance, PSTR cmdline, int cmdshown)
     25 {
     26 	(void) instance;
     27 	(void) prev_instance;
     28 	(void) cmdline;
     29 	(void) cmdshown;
     30 
     31 	const char classname[] = "gldemo_wndcls";
     32 
     33 	// https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassexa
     34 	WNDCLASSEXA cls = {
     35 		.cbSize = sizeof(WNDCLASSEXA),
     36 		.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW,
     37 		.lpfnWndProc = win32_window_proc,
     38 		.hInstance = instance,
     39 		.lpszClassName = classname,
     40 	};
     41 
     42 	// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerclassexa
     43 	RegisterClassExA(&cls);
     44 
     45 	// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexa
     46 	win32_window = CreateWindowExA(
     47 			0,
     48 			classname,
     49 			"gldemo",
     50 			WS_OVERLAPPEDWINDOW, 
     51 			CW_USEDEFAULT,
     52 			CW_USEDEFAULT,
     53 			CW_USEDEFAULT,
     54 			CW_USEDEFAULT,
     55 			NULL,
     56 			NULL,
     57 			instance,
     58 			NULL);
     59 
     60 	if (!win32_window) {
     61 		fprintf(stderr, "Failed to create Win32 window!\n");
     62 		return 1;
     63 	}
     64 
     65 	ShowWindow(win32_window, 1);
     66 
     67 	// get a handle to the win32 display context
     68 	HDC egl_hdc = GetDC(win32_window);
     69 
     70 	// wrap the HDC on the EGL side
     71 	EGLDisplay egl_display = eglGetDisplay(egl_hdc);
     72 
     73 	// find out what version of EGL we support
     74 	EGLint major, minor;
     75 	eglInitialize(egl_display, &major, &minor);
     76 	fprintf(stderr, "EGL version: %d.%d\n", major, minor);
     77 
     78 	// initialise the OpenGL ES version of the GL api
     79 	// NOTE: this is an alternative version of the OpenGL API that is more
     80 	// limited, but runs on more devices. consider using this only if
     81 	// learning OpenGL as it is more transferrable and closer to bare metal
     82 	// programming! If you would want to move to Vulkan later, it might be
     83 	// easier to do so having learnt OpenGL ES
     84 	eglBindAPI(EGL_OPENGL_ES_API);
     85 
     86 	// NOTE: if you want the original OpenGL api, use this. it may require
     87 	// changing some of the context attrs we define later as well to avoid
     88 	// errors
     89 	// eglBindAPI(EGL_OPENGL_API);
     90 
     91 	// setup some parameters for the API that we expect to hold
     92 	EGLint config_attrs[] = {
     93 		EGL_RED_SIZE, 8,
     94 		EGL_GREEN_SIZE, 8,
     95 		EGL_BLUE_SIZE, 8,
     96 		EGL_ALPHA_SIZE, 8,
     97 		EGL_NONE,
     98 	};
     99 
    100 	// select an API config that matches the parameters we wanted (if any)
    101 	EGLConfig config;
    102 	EGLint num_config;
    103 	eglChooseConfig(egl_display, config_attrs, &config, 1, &num_config);
    104 
    105 	// create a surface that allows egl to write to our win32 window
    106 	EGLint surface_attrs[] = {
    107 		EGL_NONE,
    108 	};
    109 	EGLSurfaec egl_surface = eglCreateWindowSurface(egl_display, config, win32_window, surface_attrs);
    110 
    111 	// create an egl context that our GL commands will operate on
    112 	EGLint context_attrs[] = {
    113 		EGL_CONTEXT_CLIENT_VERSION, 2,
    114 		EGL_NONE,
    115 	};
    116 	EGLContext egl_context = eglCreateContext(egl_display, config, EGL_NO_CONTEXT, context_attrs);
    117 
    118 	// set our current egl display, surface, and context as the ones all
    119 	// GL commands from the current thread should operate on
    120 	eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
    121 
    122 	render_init();
    123 
    124 	LARGE_INTEGER perf_frequency, start_perf_count, end_perf_count;
    125 	QueryPerformanceFrequency(&perf_frequency);
    126 
    127 	win32_running = true;
    128 	while (win32_running) {
    129 		QueryPerformanceCounter(&start_perf_count);
    130 		uint64_t start_cycles = __rdtsc();
    131 
    132 		win32_process_messages();
    133 
    134 		GetClientRect(win32_window, &rect);
    135 		render(rect.right - rect.left, recv.bottom - rect.top);
    136 
    137 		uint64_t end_cycles = __rdtsc();
    138 		QueryPerformanceCounter(&end_perf_count);
    139 
    140 		const uint64_t micros = 1000 * 1000;
    141 		uint64_t elapsed_micros = (end_perf_count.QuadPart - start_perf_count.QuadPart) * micros / perf_frequency.QuadPart;
    142 		uint64_t elapsed_cycles = end_cycles - start_cycles;
    143 
    144 		char buf[256];
    145 		snprintf(buf, sizeof buf, "Frame Timings: %" PRIu64 "us, %" PRIu64 " Cy\n", elapsed_micros, elapsed_cycles);
    146 		OutputDebugString(buf);
    147 	}
    148 
    149 	return 0;
    150 }
    151 
    152 static void
    153 win32_process_messages(void)
    154 {
    155 	MSG msg;
    156 	while (PeekMessageA(&msg, win32_window, 0, 0, PM_REMOVE)) {
    157 		switch (msg.message) {
    158 		default:
    159 			break;
    160 		}
    161 
    162 		TranslateMessage(&msg);
    163 		DispatchMessageA(&msg);
    164 	}
    165 }
    166 
    167 LRESULT CALLBACK
    168 win32_window_proc(HWND window, UINT msg, WPARAM wparam, LPARAM lparam)
    169 {
    170 	switch (msg) {
    171 	case WM_QUIT:
    172 	case WM_DESTROY: {
    173 		win32_running = false;
    174 	} break;
    175 
    176 	default:
    177 		return DefWindowProcA(window, msg, wparam, lparam);
    178 	}
    179 
    180 	return 0;
    181 }
    182 
    183 // actual rendering implementation
    184 // ===========================================================================
    185 
    186 // this performs one-time setup for our GL render pipeline
    187 static void
    188 render_init(void)
    189 {
    190 	glClearColor(0.0f, 0.0f, 0.2f, 1.0f);
    191 }
    192 
    193 // this is called for every frame that we want to render with our pipeline
    194 static void
    195 render(UINT width, UINT height, EGLDisplay egl_display, EGLSurface egl_surface)
    196 {
    197 	// select our entire window as our render viewport
    198 	glViewport(0, 0, width, height);
    199 
    200 	// clear the current viewport to the previously set glClearColor()
    201 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    202 
    203 	// log any errors encountered during rendering this frame
    204 	GLuint error = glGetError();
    205 	if (error != GL_NO_ERROR) {
    206 		fprintf(stderr, "OpenGL Error : %d\n", error);
    207 	}
    208 
    209 	// finish rendering, and wait for vsync before drawing this frame
    210 	eglSwapBuffer(egl_display, egl_surface);
    211 
    212 	fprintf(stderr, "Rendered frame!\n");
    213 }
    214