mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-07 19:58:32 +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,25 @@
|
|||||||
|
# WGL smoke tests
|
||||||
|
|
||||||
|
Manual bring-up tests for the Windows host layer (drop-in `opengl32.dll`).
|
||||||
|
Both replicate the GLFW/LWJGL bootstrap Minecraft uses and verify a rendered
|
||||||
|
triangle via `glReadPixels`; exit code 0 means pass.
|
||||||
|
|
||||||
|
- `wgl_smoke.cpp` - hand-rolled Win32/WGL sequence (dummy context on a hidden
|
||||||
|
helper window, ARB pixel format + 3.2 core forward-compatible context,
|
||||||
|
gdi32 pixel-format forwarding, swap loop, readback).
|
||||||
|
- `glfw_smoke.cpp` - the same flow driven by a real GLFW binary, which also
|
||||||
|
exercises GLFW's zero-area helper window (the case that needs the
|
||||||
|
DirectVulkan no-swapchain-at-init path).
|
||||||
|
|
||||||
|
Build (x64 Native Tools prompt), with GLFW binaries for the second test:
|
||||||
|
|
||||||
|
cl /nologo /EHsc /W3 wgl_smoke.cpp user32.lib gdi32.lib
|
||||||
|
cl /nologo /EHsc /W3 glfw_smoke.cpp /I%GLFW%\include %GLFW%\lib-vc2022\glfw3dll.lib user32.lib gdi32.lib
|
||||||
|
|
||||||
|
Run with the MobileGL `opengl32.dll` copy (and `glfw3.dll`, plus ANGLE's
|
||||||
|
`libEGL.dll`/`libGLESv2.dll`/`d3dcompiler_47.dll` for DirectGLES) in the exe
|
||||||
|
directory:
|
||||||
|
|
||||||
|
set MOBILEGL_BACKEND_TYPE=DirectVulkan (or DirectGLES)
|
||||||
|
set MOBILEGL_LOG_FILE_PATH=smoke.log
|
||||||
|
wgl_smoke.exe && glfw_smoke.exe
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,472 @@
|
|||||||
|
// WGL smoke test for the MobileGL opengl32.dll drop-in.
|
||||||
|
// Replicates GLFW 3.3.3's WGL bootstrap exactly as Minecraft 1.21.4 drives it:
|
||||||
|
// 1. dummy window: gdi32 ChoosePixelFormat/SetPixelFormat -> wglCreateContext
|
||||||
|
// -> wglMakeCurrent -> probe wglGetProcAddress/extension strings
|
||||||
|
// -> wglMakeCurrent(NULL) -> wglDeleteContext
|
||||||
|
// 2. real window: SetPixelFormat -> wglCreateContextAttribsARB (3.2 core,
|
||||||
|
// forward-compatible) -> wglMakeCurrent -> render loop with SwapBuffers
|
||||||
|
// Verifies rendering via glReadPixels. Exit code 0 = pass.
|
||||||
|
//
|
||||||
|
// Build: cl /nologo /EHsc /W3 wgl_smoke.cpp user32.lib gdi32.lib
|
||||||
|
// Run with opengl32.dll (MobileGL) in the exe directory.
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
typedef unsigned int GLenum;
|
||||||
|
typedef unsigned int GLuint;
|
||||||
|
typedef int GLint;
|
||||||
|
typedef int GLsizei;
|
||||||
|
typedef float GLfloat;
|
||||||
|
typedef unsigned char GLubyte;
|
||||||
|
typedef char GLchar;
|
||||||
|
typedef signed long long GLsizeiptr;
|
||||||
|
typedef unsigned int GLbitfield;
|
||||||
|
|
||||||
|
#define GL_COLOR_BUFFER_BIT 0x00004000
|
||||||
|
#define GL_DEPTH_BUFFER_BIT 0x00000100
|
||||||
|
#define GL_TRIANGLES 0x0004
|
||||||
|
#define GL_FLOAT 0x1406
|
||||||
|
#define GL_FALSE 0
|
||||||
|
#define GL_ARRAY_BUFFER 0x8892
|
||||||
|
#define GL_STATIC_DRAW 0x88E4
|
||||||
|
#define GL_FRAGMENT_SHADER 0x8B30
|
||||||
|
#define GL_VERTEX_SHADER 0x8B31
|
||||||
|
#define GL_COMPILE_STATUS 0x8B81
|
||||||
|
#define GL_LINK_STATUS 0x8B82
|
||||||
|
#define GL_VENDOR 0x1F00
|
||||||
|
#define GL_RENDERER 0x1F01
|
||||||
|
#define GL_VERSION 0x1F02
|
||||||
|
#define GL_RGBA 0x1908
|
||||||
|
#define GL_UNSIGNED_BYTE 0x1401
|
||||||
|
#define GL_MAJOR_VERSION 0x821B
|
||||||
|
#define GL_MINOR_VERSION 0x821C
|
||||||
|
#define GL_NO_ERROR 0
|
||||||
|
|
||||||
|
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
||||||
|
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
|
||||||
|
#define WGL_CONTEXT_FLAGS_ARB 0x2094
|
||||||
|
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
|
||||||
|
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
|
||||||
|
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
|
||||||
|
#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3
|
||||||
|
#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
|
||||||
|
#define WGL_SUPPORT_OPENGL_ARB 0x2010
|
||||||
|
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
|
||||||
|
#define WGL_PIXEL_TYPE_ARB 0x2013
|
||||||
|
#define WGL_TYPE_RGBA_ARB 0x202B
|
||||||
|
#define WGL_DEPTH_BITS_ARB 0x2022
|
||||||
|
#define WGL_STENCIL_BITS_ARB 0x2023
|
||||||
|
#define WGL_DOUBLE_BUFFER_ARB 0x2011
|
||||||
|
|
||||||
|
static HMODULE g_opengl32;
|
||||||
|
|
||||||
|
typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC);
|
||||||
|
typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC);
|
||||||
|
typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR);
|
||||||
|
typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC);
|
||||||
|
typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)(void);
|
||||||
|
typedef HDC(WINAPI* PFN_wglGetCurrentDC)(void);
|
||||||
|
|
||||||
|
static PFN_wglCreateContext p_wglCreateContext;
|
||||||
|
static PFN_wglDeleteContext p_wglDeleteContext;
|
||||||
|
static PFN_wglGetProcAddress p_wglGetProcAddress;
|
||||||
|
static PFN_wglMakeCurrent p_wglMakeCurrent;
|
||||||
|
static PFN_wglGetCurrentContext p_wglGetCurrentContext;
|
||||||
|
static PFN_wglGetCurrentDC p_wglGetCurrentDC;
|
||||||
|
|
||||||
|
typedef HGLRC(WINAPI* PFN_wglCreateContextAttribsARB)(HDC, HGLRC, const int*);
|
||||||
|
typedef const char*(WINAPI* PFN_wglGetExtensionsStringARB)(HDC);
|
||||||
|
typedef BOOL(WINAPI* PFN_wglSwapIntervalEXT)(int);
|
||||||
|
typedef BOOL(WINAPI* PFN_wglGetPixelFormatAttribivARB)(HDC, int, int, UINT, const int*, int*);
|
||||||
|
|
||||||
|
// GL function pointers, loaded LWJGL-style: wglGetProcAddress first, then
|
||||||
|
// GetProcAddress on the opengl32 module.
|
||||||
|
static void* GetGLProc(const char* name) {
|
||||||
|
PROC proc = p_wglGetProcAddress(name);
|
||||||
|
if (!proc) {
|
||||||
|
proc = ::GetProcAddress(g_opengl32, name);
|
||||||
|
}
|
||||||
|
return reinterpret_cast<void*>(proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef const GLubyte*(WINAPI* PFN_glGetString)(GLenum);
|
||||||
|
typedef void(WINAPI* PFN_glGetIntegerv)(GLenum, GLint*);
|
||||||
|
typedef void(WINAPI* PFN_glClearColor)(GLfloat, GLfloat, GLfloat, GLfloat);
|
||||||
|
typedef void(WINAPI* PFN_glClear)(GLbitfield);
|
||||||
|
typedef void(WINAPI* PFN_glViewport)(GLint, GLint, GLsizei, GLsizei);
|
||||||
|
typedef GLenum(WINAPI* PFN_glGetError)(void);
|
||||||
|
typedef void(WINAPI* PFN_glReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, void*);
|
||||||
|
typedef void(WINAPI* PFN_glFinish)(void);
|
||||||
|
typedef GLuint(WINAPI* PFN_glCreateShader)(GLenum);
|
||||||
|
typedef void(WINAPI* PFN_glShaderSource)(GLuint, GLsizei, const GLchar* const*, const GLint*);
|
||||||
|
typedef void(WINAPI* PFN_glCompileShader)(GLuint);
|
||||||
|
typedef void(WINAPI* PFN_glGetShaderiv)(GLuint, GLenum, GLint*);
|
||||||
|
typedef void(WINAPI* PFN_glGetShaderInfoLog)(GLuint, GLsizei, GLsizei*, GLchar*);
|
||||||
|
typedef GLuint(WINAPI* PFN_glCreateProgram)(void);
|
||||||
|
typedef void(WINAPI* PFN_glAttachShader)(GLuint, GLuint);
|
||||||
|
typedef void(WINAPI* PFN_glLinkProgram)(GLuint);
|
||||||
|
typedef void(WINAPI* PFN_glGetProgramiv)(GLuint, GLenum, GLint*);
|
||||||
|
typedef void(WINAPI* PFN_glGetProgramInfoLog)(GLuint, GLsizei, GLsizei*, GLchar*);
|
||||||
|
typedef void(WINAPI* PFN_glUseProgram)(GLuint);
|
||||||
|
typedef void(WINAPI* PFN_glGenVertexArrays)(GLsizei, GLuint*);
|
||||||
|
typedef void(WINAPI* PFN_glBindVertexArray)(GLuint);
|
||||||
|
typedef void(WINAPI* PFN_glGenBuffers)(GLsizei, GLuint*);
|
||||||
|
typedef void(WINAPI* PFN_glBindBuffer)(GLenum, GLuint);
|
||||||
|
typedef void(WINAPI* PFN_glBufferData)(GLenum, GLsizeiptr, const void*, GLenum);
|
||||||
|
typedef void(WINAPI* PFN_glVertexAttribPointer)(GLuint, GLint, GLenum, unsigned char, GLsizei, const void*);
|
||||||
|
typedef void(WINAPI* PFN_glEnableVertexAttribArray)(GLuint);
|
||||||
|
typedef void(WINAPI* PFN_glDrawArrays)(GLenum, GLint, GLsizei);
|
||||||
|
|
||||||
|
static PFN_glGetString glGetString_;
|
||||||
|
static PFN_glGetIntegerv glGetIntegerv_;
|
||||||
|
static PFN_glClearColor glClearColor_;
|
||||||
|
static PFN_glClear glClear_;
|
||||||
|
static PFN_glViewport glViewport_;
|
||||||
|
static PFN_glGetError glGetError_;
|
||||||
|
static PFN_glReadPixels glReadPixels_;
|
||||||
|
static PFN_glFinish glFinish_;
|
||||||
|
static PFN_glCreateShader glCreateShader_;
|
||||||
|
static PFN_glShaderSource glShaderSource_;
|
||||||
|
static PFN_glCompileShader glCompileShader_;
|
||||||
|
static PFN_glGetShaderiv glGetShaderiv_;
|
||||||
|
static PFN_glGetShaderInfoLog glGetShaderInfoLog_;
|
||||||
|
static PFN_glCreateProgram glCreateProgram_;
|
||||||
|
static PFN_glAttachShader glAttachShader_;
|
||||||
|
static PFN_glLinkProgram glLinkProgram_;
|
||||||
|
static PFN_glGetProgramiv glGetProgramiv_;
|
||||||
|
static PFN_glGetProgramInfoLog glGetProgramInfoLog_;
|
||||||
|
static PFN_glUseProgram glUseProgram_;
|
||||||
|
static PFN_glGenVertexArrays glGenVertexArrays_;
|
||||||
|
static PFN_glBindVertexArray glBindVertexArray_;
|
||||||
|
static PFN_glGenBuffers glGenBuffers_;
|
||||||
|
static PFN_glBindBuffer glBindBuffer_;
|
||||||
|
static PFN_glBufferData glBufferData_;
|
||||||
|
static PFN_glVertexAttribPointer glVertexAttribPointer_;
|
||||||
|
static PFN_glEnableVertexAttribArray glEnableVertexAttribArray_;
|
||||||
|
static PFN_glDrawArrays glDrawArrays_;
|
||||||
|
|
||||||
|
#define FAIL(...) \
|
||||||
|
do { \
|
||||||
|
std::fprintf(stderr, "SMOKE FAIL: " __VA_ARGS__); \
|
||||||
|
std::fprintf(stderr, "\n"); \
|
||||||
|
return 1; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// CRT-free crash dumper: runs during process teardown when stdio is already
|
||||||
|
// dead, so it may only touch raw Win32.
|
||||||
|
static void RawPrint(HANDLE f, const char* s) {
|
||||||
|
DWORD written = 0;
|
||||||
|
WriteFile(f, s, (DWORD)lstrlenA(s), &written, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RawHex(HANDLE f, DWORD64 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(
|
||||||
|
"C:\\Users\\yello\\AndroidStudioProjects\\FoldCraftLauncher\\MobileGL\\.claude\\worktrees\\wgl-host\\wgl-smoke\\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, (DWORD64)ep->ExceptionRecord->ExceptionAddress);
|
||||||
|
RawPrint(f, "\n");
|
||||||
|
const DWORD64* sp = reinterpret_cast<const DWORD64*>(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;
|
||||||
|
DWORD64 v = sp[i];
|
||||||
|
HMODULE mod = nullptr;
|
||||||
|
if (v > 0x10000 &&
|
||||||
|
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||||
|
reinterpret_cast<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 - (DWORD64)mod);
|
||||||
|
RawPrint(f, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloseHandle(f);
|
||||||
|
TerminateProcess(GetCurrentProcess(), 0x42);
|
||||||
|
return EXCEPTION_EXECUTE_HANDLER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
|
||||||
|
return DefWindowProcA(hwnd, msg, wp, lp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HWND MakeWindow(const char* title, int w, int h, bool visible) {
|
||||||
|
WNDCLASSA wc{};
|
||||||
|
wc.style = CS_OWNDC;
|
||||||
|
wc.lpfnWndProc = WndProc;
|
||||||
|
wc.hInstance = GetModuleHandleA(nullptr);
|
||||||
|
wc.lpszClassName = "MGWGLSmoke";
|
||||||
|
static bool registered = false;
|
||||||
|
if (!registered) {
|
||||||
|
RegisterClassA(&wc);
|
||||||
|
registered = true;
|
||||||
|
}
|
||||||
|
DWORD style = WS_OVERLAPPEDWINDOW | (visible ? WS_VISIBLE : 0);
|
||||||
|
RECT rect{0, 0, w, h};
|
||||||
|
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
|
||||||
|
return CreateWindowExA(0, "MGWGLSmoke", title, style, CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left,
|
||||||
|
rect.bottom - rect.top, nullptr, nullptr, GetModuleHandleA(nullptr), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
AddVectoredExceptionHandler(1, CrashDump);
|
||||||
|
// Load exactly like GLFW: the app-dir opengl32.dll must win the search.
|
||||||
|
g_opengl32 = LoadLibraryA("opengl32.dll");
|
||||||
|
if (!g_opengl32) FAIL("LoadLibraryA(opengl32.dll) failed");
|
||||||
|
char modulePath[MAX_PATH]{};
|
||||||
|
GetModuleFileNameA(g_opengl32, modulePath, MAX_PATH);
|
||||||
|
std::printf("opengl32.dll -> %s\n", modulePath);
|
||||||
|
|
||||||
|
p_wglCreateContext = (PFN_wglCreateContext)::GetProcAddress(g_opengl32, "wglCreateContext");
|
||||||
|
p_wglDeleteContext = (PFN_wglDeleteContext)::GetProcAddress(g_opengl32, "wglDeleteContext");
|
||||||
|
p_wglGetProcAddress = (PFN_wglGetProcAddress)::GetProcAddress(g_opengl32, "wglGetProcAddress");
|
||||||
|
p_wglMakeCurrent = (PFN_wglMakeCurrent)::GetProcAddress(g_opengl32, "wglMakeCurrent");
|
||||||
|
p_wglGetCurrentContext = (PFN_wglGetCurrentContext)::GetProcAddress(g_opengl32, "wglGetCurrentContext");
|
||||||
|
p_wglGetCurrentDC = (PFN_wglGetCurrentDC)::GetProcAddress(g_opengl32, "wglGetCurrentDC");
|
||||||
|
if (!p_wglCreateContext || !p_wglDeleteContext || !p_wglGetProcAddress || !p_wglMakeCurrent ||
|
||||||
|
!p_wglGetCurrentContext || !p_wglGetCurrentDC)
|
||||||
|
FAIL("missing classic wgl exports");
|
||||||
|
|
||||||
|
// ---- Phase 1: GLFW-style dummy context on a hidden helper window ----
|
||||||
|
HWND helper = MakeWindow("helper", 1, 1, false);
|
||||||
|
if (!helper) FAIL("helper window creation failed");
|
||||||
|
HDC helperDC = GetDC(helper);
|
||||||
|
|
||||||
|
PIXELFORMATDESCRIPTOR pfd{};
|
||||||
|
pfd.nSize = sizeof(pfd);
|
||||||
|
pfd.nVersion = 1;
|
||||||
|
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
||||||
|
pfd.iPixelType = PFD_TYPE_RGBA;
|
||||||
|
pfd.cColorBits = 24;
|
||||||
|
|
||||||
|
int dummyFormat = ChoosePixelFormat(helperDC, &pfd); // gdi32 -> wglChoosePixelFormat
|
||||||
|
if (dummyFormat <= 0) FAIL("gdi32 ChoosePixelFormat returned %d", dummyFormat);
|
||||||
|
if (!SetPixelFormat(helperDC, dummyFormat, &pfd)) FAIL("gdi32 SetPixelFormat failed");
|
||||||
|
std::printf("dummy pixel format: %d (DescribePixelFormat count=%d)\n", dummyFormat,
|
||||||
|
DescribePixelFormat(helperDC, 1, sizeof(pfd), nullptr));
|
||||||
|
|
||||||
|
HGLRC dummyCtx = p_wglCreateContext(helperDC);
|
||||||
|
if (!dummyCtx) FAIL("wglCreateContext (dummy) failed");
|
||||||
|
if (!p_wglMakeCurrent(helperDC, dummyCtx)) {
|
||||||
|
typedef int(WINAPI * PFN_eglGetError)(void);
|
||||||
|
auto eglGetError_ = (PFN_eglGetError)::GetProcAddress(g_opengl32, "eglGetError");
|
||||||
|
FAIL("wglMakeCurrent (dummy) failed (GetLastError=%lu eglError=0x%x)", GetLastError(),
|
||||||
|
eglGetError_ ? eglGetError_() : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wglGetExtensionsStringARB =
|
||||||
|
(PFN_wglGetExtensionsStringARB)p_wglGetProcAddress("wglGetExtensionsStringARB");
|
||||||
|
auto wglCreateContextAttribsARB =
|
||||||
|
(PFN_wglCreateContextAttribsARB)p_wglGetProcAddress("wglCreateContextAttribsARB");
|
||||||
|
auto wglSwapIntervalEXT = (PFN_wglSwapIntervalEXT)p_wglGetProcAddress("wglSwapIntervalEXT");
|
||||||
|
auto wglGetPixelFormatAttribivARB =
|
||||||
|
(PFN_wglGetPixelFormatAttribivARB)p_wglGetProcAddress("wglGetPixelFormatAttribivARB");
|
||||||
|
if (!wglGetExtensionsStringARB) FAIL("wglGetExtensionsStringARB unavailable");
|
||||||
|
if (!wglCreateContextAttribsARB) FAIL("wglCreateContextAttribsARB unavailable");
|
||||||
|
if (!wglSwapIntervalEXT) FAIL("wglSwapIntervalEXT unavailable");
|
||||||
|
if (!wglGetPixelFormatAttribivARB) FAIL("wglGetPixelFormatAttribivARB unavailable");
|
||||||
|
std::printf("WGL extensions: %s\n", wglGetExtensionsStringARB(helperDC));
|
||||||
|
|
||||||
|
glGetString_ = (PFN_glGetString)GetGLProc("glGetString");
|
||||||
|
if (!glGetString_) FAIL("glGetString unavailable");
|
||||||
|
std::printf("dummy ctx GL_VERSION: %s\n", glGetString_(GL_VERSION));
|
||||||
|
std::printf("dummy ctx GL_RENDERER: %s\n", glGetString_(GL_RENDERER));
|
||||||
|
|
||||||
|
int nfmtAttrib = WGL_NUMBER_PIXEL_FORMATS_ARB;
|
||||||
|
int nativeCount = 0;
|
||||||
|
if (!wglGetPixelFormatAttribivARB(helperDC, 1, 0, 1, &nfmtAttrib, &nativeCount) || nativeCount < 1)
|
||||||
|
FAIL("WGL_NUMBER_PIXEL_FORMATS_ARB query failed (%d)", nativeCount);
|
||||||
|
std::printf("ARB pixel formats: %d\n", nativeCount);
|
||||||
|
|
||||||
|
if (!p_wglMakeCurrent(nullptr, nullptr)) FAIL("wglMakeCurrent(NULL) failed");
|
||||||
|
if (!p_wglDeleteContext(dummyCtx)) FAIL("wglDeleteContext (dummy) failed");
|
||||||
|
|
||||||
|
// ---- Phase 2: real window + 3.2 core forward-compatible context ----
|
||||||
|
HWND window = MakeWindow("MobileGL WGL smoke", 640, 480, true);
|
||||||
|
if (!window) FAIL("main window creation failed");
|
||||||
|
HDC dc = GetDC(window);
|
||||||
|
|
||||||
|
// Pick the depth24/stencil8 RGBA format via the ARB query, GLFW-style.
|
||||||
|
int chosenFormat = 0;
|
||||||
|
for (int i = 1; i <= nativeCount; ++i) {
|
||||||
|
const int attribs[] = {WGL_SUPPORT_OPENGL_ARB, WGL_DRAW_TO_WINDOW_ARB, WGL_PIXEL_TYPE_ARB,
|
||||||
|
WGL_DOUBLE_BUFFER_ARB, WGL_DEPTH_BITS_ARB, WGL_STENCIL_BITS_ARB};
|
||||||
|
int values[6] = {};
|
||||||
|
if (!wglGetPixelFormatAttribivARB(dc, i, 0, 6, attribs, values)) continue;
|
||||||
|
if (values[0] && values[1] && values[2] == WGL_TYPE_RGBA_ARB && values[3] && values[4] >= 24 &&
|
||||||
|
values[5] >= 8) {
|
||||||
|
chosenFormat = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!chosenFormat) FAIL("no ARB pixel format with depth24+stencil8");
|
||||||
|
PIXELFORMATDESCRIPTOR realPfd{};
|
||||||
|
if (!DescribePixelFormat(dc, chosenFormat, sizeof(realPfd), &realPfd))
|
||||||
|
FAIL("DescribePixelFormat(%d) failed", chosenFormat);
|
||||||
|
if (!SetPixelFormat(dc, chosenFormat, &realPfd)) FAIL("SetPixelFormat (real) failed");
|
||||||
|
|
||||||
|
const int ctxAttribs[] = {
|
||||||
|
WGL_CONTEXT_OPENGL_NO_ERROR_ARB, 1,
|
||||||
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
||||||
|
WGL_CONTEXT_MINOR_VERSION_ARB, 2,
|
||||||
|
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
|
||||||
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
HGLRC ctx = wglCreateContextAttribsARB(dc, nullptr, ctxAttribs);
|
||||||
|
if (!ctx) FAIL("wglCreateContextAttribsARB failed (err=0x%lx)", GetLastError());
|
||||||
|
if (!p_wglMakeCurrent(dc, ctx)) FAIL("wglMakeCurrent (real) failed");
|
||||||
|
if (p_wglGetCurrentContext() != ctx || p_wglGetCurrentDC() != dc) FAIL("current context/DC mismatch");
|
||||||
|
if (!wglSwapIntervalEXT(0)) FAIL("wglSwapIntervalEXT(0) failed");
|
||||||
|
|
||||||
|
#define LOAD(fn) \
|
||||||
|
fn##_ = (PFN_##fn)GetGLProc(#fn); \
|
||||||
|
if (!fn##_) FAIL("missing GL function " #fn);
|
||||||
|
LOAD(glGetIntegerv)
|
||||||
|
LOAD(glClearColor)
|
||||||
|
LOAD(glClear)
|
||||||
|
LOAD(glViewport)
|
||||||
|
LOAD(glGetError)
|
||||||
|
LOAD(glReadPixels)
|
||||||
|
LOAD(glFinish)
|
||||||
|
LOAD(glCreateShader)
|
||||||
|
LOAD(glShaderSource)
|
||||||
|
LOAD(glCompileShader)
|
||||||
|
LOAD(glGetShaderiv)
|
||||||
|
LOAD(glGetShaderInfoLog)
|
||||||
|
LOAD(glCreateProgram)
|
||||||
|
LOAD(glAttachShader)
|
||||||
|
LOAD(glLinkProgram)
|
||||||
|
LOAD(glGetProgramiv)
|
||||||
|
LOAD(glGetProgramInfoLog)
|
||||||
|
LOAD(glUseProgram)
|
||||||
|
LOAD(glGenVertexArrays)
|
||||||
|
LOAD(glBindVertexArray)
|
||||||
|
LOAD(glGenBuffers)
|
||||||
|
LOAD(glBindBuffer)
|
||||||
|
LOAD(glBufferData)
|
||||||
|
LOAD(glVertexAttribPointer)
|
||||||
|
LOAD(glEnableVertexAttribArray)
|
||||||
|
LOAD(glDrawArrays)
|
||||||
|
#undef LOAD
|
||||||
|
|
||||||
|
GLint major = 0, minor = 0;
|
||||||
|
glGetIntegerv_(GL_MAJOR_VERSION, &major);
|
||||||
|
glGetIntegerv_(GL_MINOR_VERSION, &minor);
|
||||||
|
std::printf("real ctx: GL %d.%d | %s | %s\n", major, minor, glGetString_(GL_RENDERER),
|
||||||
|
glGetString_(GL_VERSION));
|
||||||
|
if (major < 3 || (major == 3 && minor < 2)) FAIL("context version below 3.2");
|
||||||
|
|
||||||
|
const char* vsSrc = "#version 150 core\n"
|
||||||
|
"in vec2 aPos;\n"
|
||||||
|
"void main() { gl_Position = vec4(aPos, 0.0, 1.0); }\n";
|
||||||
|
const char* fsSrc = "#version 150 core\n"
|
||||||
|
"out vec4 fragColor;\n"
|
||||||
|
"void main() { fragColor = vec4(1.0, 0.5, 0.0, 1.0); }\n";
|
||||||
|
|
||||||
|
GLuint vs = glCreateShader_(GL_VERTEX_SHADER);
|
||||||
|
glShaderSource_(vs, 1, &vsSrc, nullptr);
|
||||||
|
glCompileShader_(vs);
|
||||||
|
GLint ok = 0;
|
||||||
|
glGetShaderiv_(vs, GL_COMPILE_STATUS, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
char log[2048];
|
||||||
|
glGetShaderInfoLog_(vs, sizeof(log), nullptr, log);
|
||||||
|
FAIL("vertex shader compile failed: %s", log);
|
||||||
|
}
|
||||||
|
GLuint fs = glCreateShader_(GL_FRAGMENT_SHADER);
|
||||||
|
glShaderSource_(fs, 1, &fsSrc, nullptr);
|
||||||
|
glCompileShader_(fs);
|
||||||
|
glGetShaderiv_(fs, GL_COMPILE_STATUS, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
char log[2048];
|
||||||
|
glGetShaderInfoLog_(fs, sizeof(log), nullptr, log);
|
||||||
|
FAIL("fragment shader compile failed: %s", log);
|
||||||
|
}
|
||||||
|
GLuint prog = glCreateProgram_();
|
||||||
|
glAttachShader_(prog, vs);
|
||||||
|
glAttachShader_(prog, fs);
|
||||||
|
glLinkProgram_(prog);
|
||||||
|
glGetProgramiv_(prog, GL_LINK_STATUS, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
char log[2048];
|
||||||
|
glGetProgramInfoLog_(prog, sizeof(log), nullptr, log);
|
||||||
|
FAIL("program link failed: %s", log);
|
||||||
|
}
|
||||||
|
|
||||||
|
const GLfloat verts[] = {-0.8f, -0.8f, 0.8f, -0.8f, 0.0f, 0.8f};
|
||||||
|
GLuint 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, GL_FALSE, 0, nullptr);
|
||||||
|
glEnableVertexAttribArray_(0);
|
||||||
|
|
||||||
|
glViewport_(0, 0, 640, 480);
|
||||||
|
glUseProgram_(prog);
|
||||||
|
|
||||||
|
unsigned char centerPixel[4] = {}, cornerPixel[4] = {};
|
||||||
|
for (int frame = 0; frame < 60; ++frame) {
|
||||||
|
MSG msg;
|
||||||
|
while (PeekMessageA(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessageA(&msg);
|
||||||
|
}
|
||||||
|
glClearColor_(0.1f, 0.2f, 0.4f, 1.0f);
|
||||||
|
glClear_(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
glDrawArrays_(GL_TRIANGLES, 0, 3);
|
||||||
|
if (frame == 58) {
|
||||||
|
glFinish_();
|
||||||
|
glReadPixels_(320, 240, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, centerPixel);
|
||||||
|
glReadPixels_(5, 5, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, cornerPixel);
|
||||||
|
}
|
||||||
|
if (!SwapBuffers(dc)) FAIL("gdi32 SwapBuffers failed at frame %d", frame); // gdi32 -> wglSwapBuffers
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum err = glGetError_();
|
||||||
|
std::printf("center pixel: %u,%u,%u,%u corner pixel: %u,%u,%u,%u glGetError=0x%x\n", centerPixel[0],
|
||||||
|
centerPixel[1], centerPixel[2], centerPixel[3], cornerPixel[0], cornerPixel[1], cornerPixel[2],
|
||||||
|
cornerPixel[3], err);
|
||||||
|
|
||||||
|
if (err != GL_NO_ERROR) FAIL("GL error 0x%x", err);
|
||||||
|
// Orange triangle at center (255,127±3,0), clear color at corner (~25,51,102).
|
||||||
|
if (!(centerPixel[0] > 200 && centerPixel[1] > 90 && centerPixel[1] < 160 && centerPixel[2] < 50))
|
||||||
|
FAIL("center pixel is not the triangle color");
|
||||||
|
if (!(cornerPixel[2] > 60 && cornerPixel[0] < 60)) FAIL("corner pixel is not the clear color");
|
||||||
|
|
||||||
|
if (!p_wglMakeCurrent(nullptr, nullptr)) FAIL("final wglMakeCurrent(NULL) failed");
|
||||||
|
if (!p_wglDeleteContext(ctx)) FAIL("final wglDeleteContext failed");
|
||||||
|
|
||||||
|
std::printf("SMOKE PASS\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user