mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Test] (WGL): add manual Windows smoke tests - hand-rolled WGL bootstrap and GLFW-driven variant covering the zero-area helper-window path
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
// 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<GLFW>/include <GLFW>/lib-vc2022/glfw3dll.lib user32.lib gdi32.lib
|
||||
// Run with opengl32.dll + glfw3.dll in the exe directory.
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <windows.h>
|
||||
#include <cstdio>
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user