Use a custom GL loader to allow different versions of GL/GLES APIs to co-exist. The loader fills an api struct with GL functions pointers.

We have a per-thread pointer to select which api struct is used, and GL functions are pound-defined to call the versions in that struct.
GL/GLES surfaces can use one of the loader function to load their api, and the prepare proc of the surface sets its api as the current one
before gl calls are issued.
This commit is contained in:
martinfouilleul 2023-02-16 19:34:22 +01:00
parent 54df26232c
commit 20e425494f
13 changed files with 8925 additions and 155 deletions

View File

@ -5,6 +5,6 @@ set glsl_shaders=src\glsl_shaders\common.glsl src\glsl_shaders\blit_vertex.glsl
call python3 scripts\embed_text.py %glsl_shaders% --prefix=glsl_ --output src\glsl_shaders.h
set INCLUDES=/I src /I src/util /I src/platform /I ext
set INCLUDES=/I src /I src/util /I src/platform /I ext /I ext/angle_headers
cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% /c /Fo:bin/milepost.obj src/milepost.c
lib bin/milepost.obj /OUT:bin/milepost.lib

5991
ext/GL/glcorearb.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,12 +7,13 @@ from datetime import datetime
#---------------------------------------------------------------
parser = ArgumentParser()
parser.add_argument("-s", "--spec")
parser.add_argument("-d", "--directory")
args = parser.parse_args()
apiName = 'glapi'
loaderName = 'glloader'
apiName = 'gl_api'
loaderName = 'gl_loader'
apiPath = args.directory + '/' + apiName + '.h'
loaderHeaderPath = args.directory + '/' + loaderName + '.h'
@ -29,6 +30,8 @@ def gather_api(tree, api, version):
break
for require in feature.iter('require'):
if require.get('profile') == 'compatibility':
continue
for command in require.iter('command'):
procs.append(command.get('name'))
@ -37,7 +40,7 @@ def gather_api(tree, api, version):
procs.remove(command.get('name'))
return(procs)
tree = et.parse('gl.xml')
tree = et.parse(args.spec)
gl41 = gather_api(tree, 'gl', 4.1)
gl43 = gather_api(tree, 'gl', 4.3)
@ -87,13 +90,13 @@ f.write('#include"GL/glcorearb.h"\n')
f.write('#include"GLES3/gl32.h"\n\n')
# generate interface struct
f.write('typedef struct mg_gl\n{\n')
f.write('typedef struct mg_gl_api\n{\n')
for func in glall:
f.write('\t' + 'PFN' + func.upper() + 'PROC ' + remove_prefix(func, 'gl') + ';\n')
f.write('} mg_gl;\n\n')
f.write('extern mp_thread_local mg_gl* __mgGLAPI;\n\n');
f.write('} mg_gl_api;\n\n')
f.write('extern mp_thread_local mg_gl_api* __mgGLAPI;\n\n');
# generate interface macros
# TODO guard for different api/versions and only #define functions present in desired version
@ -107,19 +110,21 @@ f.close()
# Generate GL loader header
#---------------------------------------------------------------
f = open(loaderHeaderPath)
f = open(loaderHeaderPath, 'w')
emit_doc(f, loaderName, '.h')
emit_begin_guard(f, loaderName)
f.write("typedef void*(mg_gl_load_proc*)(const char* name);\n\n")
f.write('#include"gl_api.h"\n\n')
f.write("mg_gl* mg_gl_load_gl41(mg_gl_load_proc loadProc);\n")
f.write("mg_gl* mg_gl_load_gl43(mg_gl_load_proc loadProc);\n")
f.write("mg_gl* mg_gl_load_gles31(mg_gl_load_proc loadProc);\n")
f.write("mg_gl* mg_gl_load_gles32(mg_gl_load_proc loadProc);\n\n")
f.write("typedef void*(*mg_gl_load_proc)(const char* name);\n\n")
f.write("void mg_gl_select_api(mg_gl* api);\n\n")
f.write("void mg_gl_load_gl41(mg_gl_api* api, mg_gl_load_proc loadProc);\n")
f.write("void mg_gl_load_gl43(mg_gl_api* api, mg_gl_load_proc loadProc);\n")
f.write("void mg_gl_load_gles31(mg_gl_api* api, mg_gl_load_proc loadProc);\n")
f.write("void mg_gl_load_gles32(mg_gl_api* api, mg_gl_load_proc loadProc);\n\n")
f.write("void mg_gl_select_api(mg_gl_api* api);\n\n")
emit_end_guard(f, loaderName)
f.close()
@ -128,15 +133,10 @@ f.close()
#---------------------------------------------------------------
def emit_loader(f, name, procs):
f.write('mg_gl* mg_gl_load_'+ name +'(mg_gl_load_proc loadProc)\n')
f.write('void mg_gl_load_'+ name +'(mg_gl_api* api, mg_gl_load_proc loadProc)\n')
f.write("{\n")
f.write("\tif(!__mg"+ name.upper() +".init)\n")
f.write("\t{\n")
for proc in procs:
f.write('\t\t__mg' + name.upper() + '.' + remove_prefix(proc, 'gl') + ' = loadProc("' + proc + '");\n')
f.write("\t\t__mg"+ name.upper() +".init = true;\n")
f.write("\t}\n")
f.write("\treturn(&__mg"+ name.upper() +");\n")
f.write('\tapi->' + remove_prefix(proc, 'gl') + ' = loadProc("' + proc + '");\n')
f.write("}\n\n")
f = open(loaderCPath, 'w')
@ -146,18 +146,13 @@ emit_doc(f, loaderName, '.c')
f.write('#include"' + apiName + '.h"\n')
f.write('#include"platform.h"\n\n')
f.write("mp_thread_local mg_gl* __mgGLAPI = 0;\n\n")
f.write("static mg_gl __mgGL41 = {0};\n")
f.write("static mg_gl __mgGL43 = {0};\n")
f.write("static mg_gl __mgGLES31 = {0};\n")
f.write("static mg_gl __mgGLES32 = {0};\n\n")
f.write("mp_thread_local mg_gl_api* __mgGLAPI = 0;\n\n")
emit_loader(f, 'gl41', gl41)
emit_loader(f, 'gl43', gl43)
emit_loader(f, 'gles31', gles31)
emit_loader(f, 'gles32', gles32)
f.write("void mg_gl_select_api(mg_gl* api){ __mgGLAPI = api; }\n\n")
f.write("void mg_gl_select_api(mg_gl_api* api){ __mgGLAPI = api; }\n\n")
f.close()

1104
src/gl_api.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@
#include"graphics_internal.h"
#include"macro_helpers.h"
#include"glsl_shaders.h"
#include"gl_api.h"
#define LOG_SUBSYSTEM "Graphics"

1722
src/gl_loader.c Normal file

File diff suppressed because it is too large Load Diff

22
src/gl_loader.h Normal file
View File

@ -0,0 +1,22 @@
/********************************************************
*
* @file: gl_loader.h
* @note: auto-generated by glapi.py from gl.xml
* @date: 16/022023
*
/********************************************************/
#ifndef __GL_LOADER_H__
#define __GL_LOADER_H__
#include"gl_api.h"
typedef void*(*mg_gl_load_proc)(const char* name);
void mg_gl_load_gl41(mg_gl_api* api, mg_gl_load_proc loadProc);
void mg_gl_load_gl43(mg_gl_api* api, mg_gl_load_proc loadProc);
void mg_gl_load_gles31(mg_gl_api* api, mg_gl_load_proc loadProc);
void mg_gl_load_gles32(mg_gl_api* api, mg_gl_load_proc loadProc);
void mg_gl_select_api(mg_gl_api* api);
#endif // __GL_LOADER_H__

View File

@ -2,7 +2,7 @@
*
* file: glsl_shaders.h
* note: string literals auto-generated by embed_text.py
* date: 10/022023
* date: 16/022023
*
**********************************************************************/
#ifndef __GLSL_SHADERS_H__

View File

@ -58,6 +58,7 @@
#if MG_COMPILE_BACKEND_GL
#include"wgl_surface.c"
#include"gl_canvas.c"
#include"gl_loader.c"
#endif
#elif defined(OS_MACOS)

View File

@ -39,9 +39,7 @@
#include"graphics.h"
#if defined(OS_WIN64)
#define WIN32_GL_LOADER_API
#include"wgl_loader.h"
#undef WIN32_GL_LOADER_API
#include"gl_api.h"
#endif
//#include"ui.h"

View File

@ -1,81 +0,0 @@
/************************************************************//**
*
* @file: wgl_loader.h
* @author: Martin Fouilleul
* @date: 01/08/2022
* @revision:
*
*****************************************************************/
#define WIN32_LEAN_AND_MEAN
#include<windows.h>
#include<GL/gl.h>
#include<GL/glext.h>
#include<GL/wglext.h>
#include"macro_helpers.h"
#define GL_PROC_LIST \
GL_PROC(WGLCHOOSEPIXELFORMATARB, wglChoosePixelFormatARB) \
GL_PROC(WGLCREATECONTEXTATTRIBSARB, wglCreateContextAttribsARB) \
GL_PROC(WGLMAKECONTEXTCURRENTARB, wglMakeContextCurrentARB) \
GL_PROC(WGLSWAPINTERVALEXT, wglSwapIntervalEXT) \
GL_PROC(GLCREATESHADER, glCreateShader) \
GL_PROC(GLCREATEPROGRAM, glCreateProgram) \
GL_PROC(GLATTACHSHADER, glAttachShader) \
GL_PROC(GLCOMPILESHADER, glCompileShader) \
GL_PROC(GLGETSHADERIV, glGetShaderiv) \
GL_PROC(GLGETSHADERINFOLOG, glGetShaderInfoLog) \
GL_PROC(GLSHADERSOURCE, glShaderSource) \
GL_PROC(GLLINKPROGRAM, glLinkProgram) \
GL_PROC(GLGETPROGRAMIV, glGetProgramiv) \
GL_PROC(GLGETPROGRAMINFOLOG, glGetProgramInfoLog) \
GL_PROC(GLUSEPROGRAM, glUseProgram) \
GL_PROC(GLGENVERTEXARRAYS, glGenVertexArrays) \
GL_PROC(GLBINDVERTEXARRAY, glBindVertexArray) \
GL_PROC(GLGENBUFFERS, glGenBuffers) \
GL_PROC(GLBINDBUFFER, glBindBuffer) \
GL_PROC(GLBUFFERDATA, glBufferData) \
GL_PROC(GLBUFFERSUBDATA, glBufferSubData) \
GL_PROC(GLDELETEVERTEXARRAYS, glDeleteVertexArrays) \
GL_PROC(GLDELETEBUFFERS, glDeleteBuffers) \
GL_PROC(GLUNIFORMMATRIX4FV, glUniformMatrix4fv) \
GL_PROC(GLVERTEXATTRIBPOINTER, glVertexAttribPointer) \
GL_PROC(GLENABLEVERTEXATTRIBARRAY, glEnableVertexAttribArray) \
GL_PROC(GLBINDBUFFERBASE, glBindBufferBase) \
GL_PROC(GLDISPATCHCOMPUTE, glDispatchCompute) \
GL_PROC(GLUNIFORM1UI, glUniform1ui) \
GL_PROC(GLUNIFORM2UI, glUniform2ui) \
GL_PROC(GLUNIFORM1F, glUniform1f) \
GL_PROC(GLUNIFORM2F, glUniform2f) \
GL_PROC(GLBINDIMAGETEXTURE, glBindImageTexture) \
GL_PROC(GLACTIVETEXTURE, glActiveTexture) \
GL_PROC(GLUNIFORM1I, glUniform1i) \
GL_PROC(GLTEXSTORAGE2D, glTexStorage2D) \
GL_PROC(GLMAPBUFFERRANGE, glMapBufferRange) \
GL_PROC(GLUNMAPBUFFER, glUnmapBuffer)
#ifdef WGL_LOADER_API
//NOTE: pointer declarations
#define GL_PROC(type, name) extern _cat3_(PFN, type, PROC) name;
GL_PROC_LIST
#undef GL_PROC
#endif
#ifdef WGL_LOADER_IMPL
#define GL_PROC(type, name) _cat3_(PFN, type, PROC) name = 0;
GL_PROC_LIST
#undef GL_PROC
void mp_gl_load_procs()
{
#define GL_PROC(type, name) name = (_cat3_(PFN, type, PROC))wglGetProcAddress( #name );
GL_PROC_LIST
#undef GL_PROC
}
#endif
#undef GL_PROC_LIST

View File

@ -6,11 +6,33 @@
* @revision:
*
*****************************************************************/
#define WGL_LOADER_IMPL
#include"wgl_loader.h"
#include"win32_app.h"
#include"graphics_internal.h"
#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
static void wgl_load_procs()
{
#define WGL_PROC(type, name) name = (_cat3_(PFN, type, PROC))wglGetProcAddress( #name );
WGL_PROC_LIST
#undef WGL_PROC
}
#undef WGL_PROC_LIST
typedef struct mg_wgl_surface
{
@ -21,6 +43,9 @@ typedef struct mg_wgl_surface
HGLRC glContext;
vec2 contentsScaling;
//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)
@ -40,6 +65,7 @@ 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)
@ -75,6 +101,22 @@ mp_rect mg_wgl_surface_get_frame(mg_surface_data* interface)
return(res);
}
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 mg_wgl_surface_create_for_window(mp_window window)
{
mg_surface surfaceHandle = mg_surface_nil();
@ -135,34 +177,8 @@ mg_surface mg_wgl_surface_create_for_window(mp_window window)
HGLRC dummyGLContext = wglCreateContext(dummyDC);
wglMakeCurrent(dummyDC, dummyGLContext);
//NOTE(martin): now load extension functions
/*
wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
wglMakeContextCurrentARB = (PFNWGLMAKECONTEXTCURRENTARBPROC)wglGetProcAddress("wglMakeContextCurrentARB");
wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
glCreateShader = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader");
glCreateProgram = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram");
glAttachShader = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader");
glShaderSource = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource");
glCompileShader = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader");
glGetShaderiv = (PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv");
glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog");
glLinkProgram = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram");
glGetProgramiv = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv");
glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog");
glUseProgram = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram");
glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)wglGetProcAddress("glGenVertexArrays");
glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)wglGetProcAddress("glBindVertexArray");
glGenBuffers = (PFNGLGENBUFFERSPROC)wglGetProcAddress("glGenBuffers");
glBindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer");
glBufferData = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData");
glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)wglGetProcAddress("glUniformMatrix4fv");
glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer");
glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glEnableVertexAttribArray");
*/
mp_gl_load_procs();
//NOTE(martin): now load WGL extension functions
wgl_load_procs();
//NOTE(martin): now create the true pixel format and gl context
int pixelFormatAttrs[] = {
@ -210,8 +226,9 @@ mg_surface mg_wgl_surface_create_for_window(mp_window window)
wglMakeCurrent(hDC, glContext);
wglSwapIntervalEXT(1);
//TODO save important info in surface_data and return a handle
//NOTE: fill surface data and load api
mg_wgl_surface* surface = malloc_type(mg_wgl_surface);
surface->interface.backend = MG_BACKEND_GL;
surface->interface.destroy = mg_wgl_surface_destroy;
surface->interface.prepare = mg_wgl_surface_prepare;
@ -219,8 +236,8 @@ mg_surface mg_wgl_surface_create_for_window(mp_window window)
surface->interface.swapInterval = mg_wgl_surface_swap_interval;
surface->interface.contentsScaling = mg_wgl_contents_scaling;
surface->interface.getFrame = mg_wgl_surface_get_frame;
//TODO: get/set frame/hidden
surface->hWnd = windowData->win32.hWnd;
surface->hDC = hDC;
surface->glContext = glContext;
@ -228,6 +245,8 @@ mg_surface mg_wgl_surface_create_for_window(mp_window window)
u32 dpi = GetDpiForWindow(windowData->win32.hWnd);
surface->contentsScaling = (vec2){(float)dpi/96., (float)dpi/96.};
mg_gl_load_gl43(&surface->api, mg_wgl_get_proc);
surfaceHandle = mg_surface_alloc_handle((mg_surface_data*)surface);
}

View File

@ -4,26 +4,22 @@ Overview
[x] Pan/Zoom on text example
[.] Clean+Fixes of canvas code and examples
[>] Make backend selection easier
[.] Make backend selection easier
[x] rename backend-specific files with api prefix (e.g. egl_, nsgl_, wgl_, mtl_, ...)
[x] option macros to select surface/canvas backends to compile into milepost lib
[/] option macros to select backend-specific APIs to include when building an app
[x] surface/canvas functions that take a backend id
[x] feature-detection functions to know what surface/canvas backends are available at run-time
[>] drop the "mg_" prefix for internal functions (and declare them static)
[>] write doc about these options
[ ] Image API and backend
[>] Image API and backend
[ ] Build image atlas on top
[ ] Baked fonts?
[ ] Allow different versions of GL to co-exist
- we can load GL (4.1 or 4.3) or GLES (3.1 or 3.2) functions
- surface creation function chooses a loader function
- loader function fills api struct or return pointer to already filled api struct
- surface prepare function sets the proper api struct
[x] Allow different versions of GL/GLES to co-exist
[!] Keep dummy window/dummy context around for gl context creation, and don't reload wgl functions every time
[>] Reintroduce GLES surface
[?] Backport canvas to GLES
[ ] Back surface by child windows and implement moving frame/hiding/overlay
@ -38,6 +34,8 @@ Overview
Clean+Fixes
-----------
[>] drop the "mg_" prefix for internal functions (and declare them static)
[x] Rename MG_GL_CANVAS_TILE_ARRAY_SIZE/LENGTH unambiguously and make it consistent between C and glsl code
[x] Clean shaders (folder/filenames, version string, debug flags, ...)
[x] Simplify shader names, prefix embedded strings by "glsl_"