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