// GLFW-driven smoke test: exercises the exact GLFW WGL bootstrap Minecraft uses // (helper-window dummy context, ARB pixel format, 3.2 core forward-compatible // no-error context) against the MobileGL opengl32.dll drop-in. // // Build: cl /nologo /EHsc /W3 glfw_smoke.cpp /I/include /lib-vc2022/glfw3dll.lib user32.lib gdi32.lib // Run with opengl32.dll + glfw3.dll in the exe directory. #include #include #include typedef const unsigned char*(WINAPI* PFN_glGetString)(unsigned int); typedef void(WINAPI* PFN_glClearColor)(float, float, float, float); typedef void(WINAPI* PFN_glClear)(unsigned int); typedef void(WINAPI* PFN_glReadPixels)(int, int, int, int, unsigned int, unsigned int, void*); typedef void(WINAPI* PFN_glFinish)(void); typedef unsigned int(WINAPI* PFN_glCreateShader)(unsigned int); typedef void(WINAPI* PFN_glShaderSource)(unsigned int, int, const char* const*, const int*); typedef void(WINAPI* PFN_glCompileShader)(unsigned int); typedef unsigned int(WINAPI* PFN_glCreateProgram)(void); typedef void(WINAPI* PFN_glAttachShader)(unsigned int, unsigned int); typedef void(WINAPI* PFN_glLinkProgram)(unsigned int); typedef void(WINAPI* PFN_glUseProgram)(unsigned int); typedef void(WINAPI* PFN_glGenVertexArrays)(int, unsigned int*); typedef void(WINAPI* PFN_glBindVertexArray)(unsigned int); typedef void(WINAPI* PFN_glGenBuffers)(int, unsigned int*); typedef void(WINAPI* PFN_glBindBuffer)(unsigned int, unsigned int); typedef void(WINAPI* PFN_glBufferData)(unsigned int, long long, const void*, unsigned int); typedef void(WINAPI* PFN_glVertexAttribPointer)(unsigned int, int, unsigned int, unsigned char, int, const void*); typedef void(WINAPI* PFN_glEnableVertexAttribArray)(unsigned int); typedef void(WINAPI* PFN_glDrawArrays)(unsigned int, int, int); #define GL_COLOR_BUFFER_BIT 0x00004000 #define GL_VERSION 0x1F02 #define GL_RENDERER 0x1F01 #define GL_RGBA 0x1908 #define GL_UNSIGNED_BYTE 0x1401 #define GL_TRIANGLES 0x0004 #define GL_FLOAT 0x1406 #define GL_ARRAY_BUFFER 0x8892 #define GL_STATIC_DRAW 0x88E4 #define GL_VERTEX_SHADER 0x8B31 #define GL_FRAGMENT_SHADER 0x8B30 static void RawPrint(HANDLE f, const char* s) { DWORD written = 0; WriteFile(f, s, (DWORD)lstrlenA(s), &written, nullptr); } static void RawHex(HANDLE f, unsigned long long v) { char buf[20]; for (int i = 0; i < 16; ++i) { int nib = (int)((v >> ((15 - i) * 4)) & 0xF); buf[i] = (char)(nib < 10 ? '0' + nib : 'a' + nib - 10); } buf[16] = 0; RawPrint(f, buf); } static LONG WINAPI CrashDump(EXCEPTION_POINTERS* ep) { static volatile LONG entered = 0; if (ep->ExceptionRecord->ExceptionCode != 0xC0000005) return EXCEPTION_CONTINUE_SEARCH; if (InterlockedExchange(&entered, 1)) return EXCEPTION_CONTINUE_SEARCH; HANDLE f = CreateFileA("glfw-crash-stack.txt", GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (f == INVALID_HANDLE_VALUE) TerminateProcess(GetCurrentProcess(), 0x43); RawPrint(f, "AV at "); RawHex(f, (unsigned long long)ep->ExceptionRecord->ExceptionAddress); RawPrint(f, " reading "); RawHex(f, (unsigned long long)ep->ExceptionRecord->ExceptionInformation[1]); RawPrint(f, "\n"); const unsigned long long* sp = (const unsigned long long*)ep->ContextRecord->Rsp; for (int i = 0; i < 512; ++i) { MEMORY_BASIC_INFORMATION mbi; if (!VirtualQuery(sp + i, &mbi, sizeof(mbi)) || mbi.State != MEM_COMMIT || (mbi.Protect & (PAGE_READONLY | PAGE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE)) == 0) break; unsigned long long v = sp[i]; HMODULE mod = nullptr; if (v > 0x10000 && GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)v, &mod) && mod) { char name[MAX_PATH]; name[0] = 0; GetModuleFileNameA(mod, name, MAX_PATH); RawPrint(f, "stack "); RawHex(f, v); RawPrint(f, " "); RawPrint(f, name); RawPrint(f, "+"); RawHex(f, v - (unsigned long long)mod); RawPrint(f, "\n"); } } CloseHandle(f); TerminateProcess(GetCurrentProcess(), 0x42); return EXCEPTION_EXECUTE_HANDLER; } int main() { AddVectoredExceptionHandler(1, CrashDump); if (!glfwInit()) { std::fprintf(stderr, "GLFW-SMOKE FAIL: glfwInit\n"); return 1; } glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); glfwWindowHint(GLFW_CONTEXT_NO_ERROR, GLFW_TRUE); glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); GLFWwindow* win = glfwCreateWindow(854, 480, "MobileGL GLFW smoke", nullptr, nullptr); if (!win) { const char* desc = nullptr; int code = glfwGetError(&desc); std::fprintf(stderr, "GLFW-SMOKE FAIL: glfwCreateWindow (0x%x %s)\n", code, desc ? desc : "?"); return 1; } glfwMakeContextCurrent(win); glfwShowWindow(win); glfwSwapInterval(0); auto glGetString_ = (PFN_glGetString)glfwGetProcAddress("glGetString"); auto glClearColor_ = (PFN_glClearColor)glfwGetProcAddress("glClearColor"); auto glClear_ = (PFN_glClear)glfwGetProcAddress("glClear"); auto glReadPixels_ = (PFN_glReadPixels)glfwGetProcAddress("glReadPixels"); auto glFinish_ = (PFN_glFinish)glfwGetProcAddress("glFinish"); std::printf("GL_VERSION: %s\nGL_RENDERER: %s\n", glGetString_(GL_VERSION), glGetString_(GL_RENDERER)); auto glCreateShader_ = (PFN_glCreateShader)glfwGetProcAddress("glCreateShader"); auto glShaderSource_ = (PFN_glShaderSource)glfwGetProcAddress("glShaderSource"); auto glCompileShader_ = (PFN_glCompileShader)glfwGetProcAddress("glCompileShader"); auto glCreateProgram_ = (PFN_glCreateProgram)glfwGetProcAddress("glCreateProgram"); auto glAttachShader_ = (PFN_glAttachShader)glfwGetProcAddress("glAttachShader"); auto glLinkProgram_ = (PFN_glLinkProgram)glfwGetProcAddress("glLinkProgram"); auto glUseProgram_ = (PFN_glUseProgram)glfwGetProcAddress("glUseProgram"); auto glGenVertexArrays_ = (PFN_glGenVertexArrays)glfwGetProcAddress("glGenVertexArrays"); auto glBindVertexArray_ = (PFN_glBindVertexArray)glfwGetProcAddress("glBindVertexArray"); auto glGenBuffers_ = (PFN_glGenBuffers)glfwGetProcAddress("glGenBuffers"); auto glBindBuffer_ = (PFN_glBindBuffer)glfwGetProcAddress("glBindBuffer"); auto glBufferData_ = (PFN_glBufferData)glfwGetProcAddress("glBufferData"); auto glVertexAttribPointer_ = (PFN_glVertexAttribPointer)glfwGetProcAddress("glVertexAttribPointer"); auto glEnableVertexAttribArray_ = (PFN_glEnableVertexAttribArray)glfwGetProcAddress("glEnableVertexAttribArray"); auto glDrawArrays_ = (PFN_glDrawArrays)glfwGetProcAddress("glDrawArrays"); const char* vsSrc = "#version 150 core\nin vec2 aPos;\nvoid main(){ gl_Position = vec4(aPos, 0.0, 1.0); }\n"; const char* fsSrc = "#version 150 core\nout vec4 c;\nvoid main(){ c = vec4(1.0, 0.5, 0.0, 1.0); }\n"; unsigned int vs = glCreateShader_(GL_VERTEX_SHADER); glShaderSource_(vs, 1, &vsSrc, nullptr); glCompileShader_(vs); unsigned int fs = glCreateShader_(GL_FRAGMENT_SHADER); glShaderSource_(fs, 1, &fsSrc, nullptr); glCompileShader_(fs); unsigned int prog = glCreateProgram_(); glAttachShader_(prog, vs); glAttachShader_(prog, fs); glLinkProgram_(prog); glUseProgram_(prog); const float verts[] = {-0.9f, -0.9f, 0.9f, -0.9f, 0.0f, 0.9f}; unsigned int vao = 0, vbo = 0; glGenVertexArrays_(1, &vao); glBindVertexArray_(vao); glGenBuffers_(1, &vbo); glBindBuffer_(GL_ARRAY_BUFFER, vbo); glBufferData_(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); glVertexAttribPointer_(0, 2, GL_FLOAT, 0, 0, nullptr); glEnableVertexAttribArray_(0); unsigned char px[4] = {}; for (int i = 0; i < 60 && !glfwWindowShouldClose(win); ++i) { glClearColor_(0.1f, 0.2f, 0.4f, 1.0f); glClear_(GL_COLOR_BUFFER_BIT); glDrawArrays_(GL_TRIANGLES, 0, 3); if (i == 58) { glFinish_(); glReadPixels_(427, 240, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, px); } glfwSwapBuffers(win); glfwPollEvents(); } std::printf("pixel: %u,%u,%u,%u\n", px[0], px[1], px[2], px[3]); glfwDestroyWindow(win); glfwTerminate(); const bool ok = px[0] > 200 && px[1] > 90 && px[1] < 160 && px[2] < 50; std::printf(ok ? "GLFW-SMOKE PASS\n" : "GLFW-SMOKE FAIL: wrong pixel\n"); return ok ? 0 : 1; }