2023-03-05 15:05:43 +00:00
|
|
|
/************************************************************//**
|
|
|
|
*
|
|
|
|
* @file: wgl_surface.c
|
|
|
|
* @author: Martin Fouilleul
|
|
|
|
* @date: 01/08/2022
|
|
|
|
* @revision:
|
|
|
|
*
|
|
|
|
*****************************************************************/
|
|
|
|
#include"win32_app.h"
|
2023-04-26 12:56:02 +00:00
|
|
|
#include"graphics_surface.h"
|
2023-03-05 15:05:43 +00:00
|
|
|
#include"gl_loader.h"
|
|
|
|
|
|
|
|
#include<GL/wglext.h>
|
|
|
|
#include"macro_helpers.h"
|
|
|
|
|
|
|
|
#define WGL_PROC_LIST \
|
|
|
|
WGL_PROC(WGLCHOOSEPIXELFORMATARB, wglChoosePixelFormatARB) \
|
|
|
|
WGL_PROC(WGLCREATECONTEXTATTRIBSARB, wglCreateContextAttribsARB) \
|
|
|
|
WGL_PROC(WGLMAKECONTEXTCURRENTARB, wglMakeContextCurrentARB) \
|
|
|
|
WGL_PROC(WGLSWAPINTERVALEXT, wglSwapIntervalEXT) \
|
|
|
|
|
|
|
|
//NOTE: wgl function pointers declarations
|
|
|
|
|
|
|
|
#define WGL_PROC(type, name) _cat3_(PFN, type, PROC) name = 0;
|
|
|
|
WGL_PROC_LIST
|
|
|
|
#undef WGL_PROC
|
|
|
|
|
|
|
|
//NOTE: wgl loader
|
|
|
|
|
|
|
|
typedef struct wgl_dummy_context
|
|
|
|
{
|
|
|
|
bool init;
|
|
|
|
HWND hWnd;
|
|
|
|
HDC hDC;
|
|
|
|
HGLRC glContext;
|
|
|
|
|
|
|
|
} wgl_dummy_context;
|
|
|
|
|
|
|
|
static wgl_dummy_context __mgWGLDummyContext = {0};
|
|
|
|
|
|
|
|
static void wgl_init()
|
|
|
|
{
|
|
|
|
if(!__mgWGLDummyContext.init)
|
|
|
|
{
|
|
|
|
//NOTE: create a dummy window
|
|
|
|
WNDCLASS windowClass = {.style = CS_OWNDC,
|
|
|
|
.lpfnWndProc = DefWindowProc,
|
|
|
|
.hInstance = GetModuleHandleW(NULL),
|
|
|
|
.lpszClassName = "wgl_helper_window_class",
|
|
|
|
.hCursor = LoadCursor(0, IDC_ARROW)};
|
|
|
|
|
|
|
|
if(!RegisterClass(&windowClass))
|
|
|
|
{
|
|
|
|
//TODO: error
|
|
|
|
goto quit;
|
|
|
|
}
|
|
|
|
|
|
|
|
__mgWGLDummyContext.hWnd = CreateWindow("wgl_helper_window_class",
|
|
|
|
"dummy",
|
|
|
|
WS_OVERLAPPEDWINDOW,
|
|
|
|
0, 0, 100, 100,
|
|
|
|
0, 0, windowClass.hInstance, 0);
|
|
|
|
|
|
|
|
if(!__mgWGLDummyContext.hWnd)
|
|
|
|
{
|
|
|
|
//TODO: error
|
|
|
|
goto quit;
|
|
|
|
}
|
|
|
|
__mgWGLDummyContext.hDC = GetDC(__mgWGLDummyContext.hWnd);
|
|
|
|
|
|
|
|
PIXELFORMATDESCRIPTOR pixelFormatDesc =
|
|
|
|
{
|
|
|
|
sizeof(PIXELFORMATDESCRIPTOR),
|
|
|
|
1,
|
|
|
|
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // Flags
|
|
|
|
PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette.
|
|
|
|
32, // Colordepth of the framebuffer.
|
|
|
|
0, 0, 0, 0, 0, 0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0, 0, 0, 0,
|
|
|
|
24, // Number of bits for the depthbuffer
|
|
|
|
8, // Number of bits for the stencilbuffer
|
|
|
|
0, // Number of Aux buffers in the framebuffer.
|
|
|
|
PFD_MAIN_PLANE,
|
|
|
|
0,
|
|
|
|
0, 0, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
int pixelFormat = ChoosePixelFormat(__mgWGLDummyContext.hDC, &pixelFormatDesc);
|
|
|
|
SetPixelFormat(__mgWGLDummyContext.hDC, pixelFormat, &pixelFormatDesc);
|
|
|
|
|
|
|
|
__mgWGLDummyContext.glContext = wglCreateContext(__mgWGLDummyContext.hDC);
|
|
|
|
wglMakeCurrent(__mgWGLDummyContext.hDC, __mgWGLDummyContext.glContext);
|
|
|
|
|
|
|
|
//NOTE(martin): now load WGL extension functions
|
|
|
|
#define WGL_PROC(type, name) name = (_cat3_(PFN, type, PROC))wglGetProcAddress( #name );
|
|
|
|
WGL_PROC_LIST
|
|
|
|
#undef WGL_PROC
|
|
|
|
|
|
|
|
__mgWGLDummyContext.init = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wglMakeCurrent(__mgWGLDummyContext.hDC, __mgWGLDummyContext.glContext);
|
|
|
|
}
|
|
|
|
quit:;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef WGL_PROC_LIST
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct mg_wgl_surface
|
|
|
|
{
|
|
|
|
mg_surface_data interface;
|
|
|
|
|
|
|
|
HDC hDC;
|
|
|
|
HGLRC glContext;
|
|
|
|
|
|
|
|
//NOTE: this may be a bit wasteful to have one api struct per surface, but win32 docs says that loading procs
|
|
|
|
// from different contexts might select different implementations (eg. depending on context version/pixel format)
|
|
|
|
mg_gl_api api;
|
|
|
|
} mg_wgl_surface;
|
|
|
|
|
|
|
|
void mg_wgl_surface_destroy(mg_surface_data* interface)
|
|
|
|
{
|
|
|
|
mg_wgl_surface* surface = (mg_wgl_surface*)interface;
|
|
|
|
|
|
|
|
if(surface->glContext == wglGetCurrentContext())
|
|
|
|
{
|
|
|
|
wglMakeCurrent(NULL, NULL);
|
|
|
|
}
|
|
|
|
wglDeleteContext(surface->glContext);
|
|
|
|
|
|
|
|
mg_surface_cleanup(interface);
|
|
|
|
|
|
|
|
free(surface);
|
|
|
|
}
|
|
|
|
|
|
|
|
void mg_wgl_surface_prepare(mg_surface_data* interface)
|
|
|
|
{
|
|
|
|
mg_wgl_surface* surface = (mg_wgl_surface*)interface;
|
|
|
|
|
|
|
|
wglMakeCurrent(surface->hDC, surface->glContext);
|
|
|
|
mg_gl_select_api(&surface->api);
|
|
|
|
}
|
|
|
|
|
|
|
|
void mg_wgl_surface_present(mg_surface_data* interface)
|
|
|
|
{
|
|
|
|
mg_wgl_surface* surface = (mg_wgl_surface*)interface;
|
|
|
|
SwapBuffers(surface->hDC);
|
|
|
|
}
|
|
|
|
|
|
|
|
void mg_wgl_surface_swap_interval(mg_surface_data* interface, int swap)
|
|
|
|
{
|
|
|
|
mg_wgl_surface* surface = (mg_wgl_surface*)interface;
|
|
|
|
wglSwapIntervalEXT(swap);
|
|
|
|
}
|
|
|
|
|
|
|
|
void* mg_wgl_get_proc(const char* name)
|
|
|
|
{
|
|
|
|
void* p = wglGetProcAddress(name);
|
|
|
|
if( p == 0
|
|
|
|
|| p == (void*)0x01
|
|
|
|
|| p == (void*)0x02
|
|
|
|
|| p == (void*)0x03
|
|
|
|
|| p == (void*)(-1))
|
|
|
|
{
|
|
|
|
//TODO: should we avoid re-loading every time?
|
|
|
|
HMODULE module = LoadLibrary("opengl32.dll");
|
|
|
|
p = (void*)GetProcAddress(module, name);
|
|
|
|
}
|
|
|
|
return(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
mg_surface_data* mg_wgl_surface_create_for_window(mp_window window)
|
|
|
|
{
|
2023-03-09 16:38:41 +00:00
|
|
|
mg_wgl_surface* surface = 0;
|
2023-03-05 15:05:43 +00:00
|
|
|
|
|
|
|
mp_window_data* windowData = mp_window_ptr_from_handle(window);
|
|
|
|
if(windowData)
|
|
|
|
{
|
|
|
|
wgl_init();
|
|
|
|
|
|
|
|
//NOTE: fill surface data and load api
|
2023-03-09 16:38:41 +00:00
|
|
|
surface = malloc_type(mg_wgl_surface);
|
2023-03-05 15:05:43 +00:00
|
|
|
if(surface)
|
|
|
|
{
|
|
|
|
mg_surface_init_for_window((mg_surface_data*)surface, windowData);
|
|
|
|
|
2023-04-25 12:54:52 +00:00
|
|
|
surface->interface.api = MG_GL;
|
2023-03-05 15:05:43 +00:00
|
|
|
surface->interface.destroy = mg_wgl_surface_destroy;
|
|
|
|
surface->interface.prepare = mg_wgl_surface_prepare;
|
|
|
|
surface->interface.present = mg_wgl_surface_present;
|
|
|
|
surface->interface.swapInterval = mg_wgl_surface_swap_interval;
|
|
|
|
|
|
|
|
surface->hDC = GetDC(surface->interface.layer.hWnd);
|
|
|
|
|
|
|
|
//NOTE(martin): create the pixel format and gl context
|
|
|
|
PIXELFORMATDESCRIPTOR pixelFormatDesc =
|
|
|
|
{
|
|
|
|
sizeof(PIXELFORMATDESCRIPTOR),
|
|
|
|
1,
|
|
|
|
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // Flags
|
|
|
|
PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette.
|
|
|
|
32, // Colordepth of the framebuffer.
|
|
|
|
0, 0, 0, 0, 0, 0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0, 0, 0, 0,
|
|
|
|
24, // Number of bits for the depthbuffer
|
|
|
|
8, // Number of bits for the stencilbuffer
|
|
|
|
0, // Number of Aux buffers in the framebuffer.
|
|
|
|
PFD_MAIN_PLANE,
|
|
|
|
0,
|
|
|
|
0, 0, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
int pixelFormatAttrs[] = {
|
|
|
|
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
|
|
|
|
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
|
|
|
|
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
|
|
|
|
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
|
|
|
|
WGL_COLOR_BITS_ARB, 32,
|
|
|
|
WGL_DEPTH_BITS_ARB, 24,
|
|
|
|
WGL_STENCIL_BITS_ARB, 8,
|
|
|
|
0};
|
|
|
|
|
|
|
|
u32 numFormats = 0;
|
|
|
|
int pixelFormat = 0;
|
|
|
|
|
|
|
|
wglChoosePixelFormatARB(surface->hDC, pixelFormatAttrs, 0, 1, &pixelFormat, &numFormats);
|
|
|
|
|
|
|
|
if(!pixelFormat)
|
|
|
|
{
|
|
|
|
//TODO: error
|
|
|
|
}
|
|
|
|
SetPixelFormat(surface->hDC, pixelFormat, &pixelFormatDesc);
|
|
|
|
|
|
|
|
int contextAttrs[] = {
|
|
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
|
|
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
|
|
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
|
|
0};
|
|
|
|
|
|
|
|
surface->glContext = wglCreateContextAttribsARB(surface->hDC, __mgWGLDummyContext.glContext, contextAttrs);
|
|
|
|
|
|
|
|
if(!surface->glContext)
|
|
|
|
{
|
|
|
|
//TODO error
|
|
|
|
int error = GetLastError();
|
|
|
|
printf("error: %i\n", error);
|
|
|
|
}
|
|
|
|
|
|
|
|
//NOTE: make gl context current and load api
|
|
|
|
wglMakeCurrent(surface->hDC, surface->glContext);
|
|
|
|
wglSwapIntervalEXT(1);
|
|
|
|
mg_gl_load_gl43(&surface->api, mg_wgl_get_proc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return((mg_surface_data*)surface);
|
|
|
|
}
|