orca/src/graphics/wgl_surface.c

277 lines
8.7 KiB
C

/*************************************************************************
*
* Orca
* Copyright 2023 Martin Fouilleul and the Orca project contributors
* See LICENSE.txt for licensing information
*
**************************************************************************/
#include "app/win32_app.h"
#include "gl_loader.h"
#include "graphics_surface.h"
#include "util/macros.h"
#include <GL/wglext.h>
#define OC_WGL_PROC_LIST \
OC_WGL_PROC(WGLCHOOSEPIXELFORMATARB, wglChoosePixelFormatARB) \
OC_WGL_PROC(WGLCREATECONTEXTATTRIBSARB, wglCreateContextAttribsARB) \
OC_WGL_PROC(WGLMAKECONTEXTCURRENTARB, wglMakeContextCurrentARB) \
OC_WGL_PROC(WGLSWAPINTERVALEXT, wglSwapIntervalEXT)
//NOTE: wgl function pointers declarations
#define OC_WGL_PROC(type, name) OC_CAT3(PFN, type, PROC) name = 0;
OC_WGL_PROC_LIST
#undef OC_WGL_PROC
//NOTE: wgl loader
typedef struct oc_wgl_dummy_context
{
bool init;
HWND hWnd;
HDC hDC;
HGLRC glContext;
} oc_wgl_dummy_context;
static oc_wgl_dummy_context oc_wglDummyContext = { 0 };
static void oc_wgl_init()
{
if(!oc_wglDummyContext.init)
{
//NOTE: create a dummy window
WNDCLASS windowClass = { .style = CS_OWNDC,
.lpfnWndProc = DefWindowProc,
.hInstance = GetModuleHandleW(NULL),
.lpszClassName = "oc_wgl_helper_window_class",
.hCursor = LoadCursor(0, IDC_ARROW) };
if(!RegisterClass(&windowClass))
{
//TODO: error
goto quit;
}
oc_wglDummyContext.hWnd = CreateWindow("oc_wgl_helper_window_class",
"dummy",
WS_OVERLAPPEDWINDOW,
0, 0, 100, 100,
0, 0, windowClass.hInstance, 0);
if(!oc_wglDummyContext.hWnd)
{
//TODO: error
goto quit;
}
oc_wglDummyContext.hDC = GetDC(oc_wglDummyContext.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(oc_wglDummyContext.hDC, &pixelFormatDesc);
SetPixelFormat(oc_wglDummyContext.hDC, pixelFormat, &pixelFormatDesc);
oc_wglDummyContext.glContext = wglCreateContext(oc_wglDummyContext.hDC);
wglMakeCurrent(oc_wglDummyContext.hDC, oc_wglDummyContext.glContext);
//NOTE(martin): now load WGL extension functions
#define OC_WGL_PROC(type, name) name = (OC_CAT3(PFN, type, PROC))wglGetProcAddress(#name);
OC_WGL_PROC_LIST
#undef OC_WGL_PROC
oc_wglDummyContext.init = true;
}
else
{
wglMakeCurrent(oc_wglDummyContext.hDC, oc_wglDummyContext.glContext);
}
quit:;
}
#undef OC_WGL_PROC_LIST
typedef struct oc_wgl_surface
{
oc_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)
oc_gl_api api;
} oc_wgl_surface;
void oc_wgl_surface_destroy(oc_surface_data* interface)
{
oc_wgl_surface* surface = (oc_wgl_surface*)interface;
if(surface->glContext == wglGetCurrentContext())
{
wglMakeCurrent(NULL, NULL);
}
wglDeleteContext(surface->glContext);
oc_surface_cleanup(interface);
free(surface);
}
void oc_wgl_surface_prepare(oc_surface_data* interface)
{
oc_wgl_surface* surface = (oc_wgl_surface*)interface;
wglMakeCurrent(surface->hDC, surface->glContext);
oc_gl_select_api(&surface->api);
}
void oc_wgl_surface_present(oc_surface_data* interface)
{
oc_wgl_surface* surface = (oc_wgl_surface*)interface;
SwapBuffers(surface->hDC);
}
void oc_wgl_surface_deselect(oc_surface_data* interface)
{
wglMakeCurrent(NULL, NULL);
oc_gl_deselect_api();
}
void oc_wgl_surface_swap_interval(oc_surface_data* interface, int swap)
{
oc_wgl_surface* surface = (oc_wgl_surface*)interface;
wglSwapIntervalEXT(swap);
}
void* oc_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);
}
oc_surface_data* oc_wgl_surface_create_for_window(oc_window window)
{
oc_wgl_surface* surface = 0;
oc_window_data* windowData = oc_window_ptr_from_handle(window);
if(windowData)
{
oc_wgl_init();
//NOTE: fill surface data and load api
surface = oc_malloc_type(oc_wgl_surface);
if(surface)
{
memset(surface, 0, sizeof(oc_wgl_surface));
oc_surface_init_for_window((oc_surface_data*)surface, windowData);
surface->interface.api = OC_GL;
surface->interface.destroy = oc_wgl_surface_destroy;
surface->interface.prepare = oc_wgl_surface_prepare;
surface->interface.present = oc_wgl_surface_present;
surface->interface.swapInterval = oc_wgl_surface_swap_interval;
surface->interface.deselect = oc_wgl_surface_deselect;
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_TRANSPARENT_ARB, TRUE,
WGL_COLOR_BITS_ARB, 32,
WGL_RED_BITS_ARB, 8,
WGL_GREEN_BITS_ARB, 8,
WGL_BLUE_BITS_ARB, 8,
WGL_ALPHA_BITS_ARB, 8,
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, 4,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0
};
surface->glContext = wglCreateContextAttribsARB(surface->hDC, oc_wglDummyContext.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);
oc_gl_load_gl44(&surface->api, oc_wgl_get_proc);
}
}
return ((oc_surface_data*)surface);
}