[gles] Support for exposing GLES surfaces to orca apps

- update gles surface example
- add mp_dispatch_on_main_thread_sync() to osx threads
- add /experimental:c11atomics to glesTriangle build.bat
- mp_dispatch_on_main_thread_sync: win32 impl
- move mp_dispatch_on_main_thread_sync() on app layer
- configure dlmalloc to not assume contiguity. This allows us to reserve blocks on our end without trashing malloc-owned zones. This way we can add a block to store GL static strings when needed.
- implement deselect interface
- Log an error when an OpenGL function is called while no OpenGL API is selected, or if that function is not part of the selected API
- avoid dispatching calls to image or canvas API if surface is not selected. However, we could later allow it and temporarily select the surface _on behalf of the user_.
- Adding support for format string and optional parameters in assert macros
This commit is contained in:
Martin Fouilleul 2023-08-03 11:34:19 +02:00
parent c103c001f7
commit d01dc832fb
18 changed files with 10832 additions and 3456 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ build
Debug/*
src/glsl_shaders.h
scripts/__pycache__

View File

@ -1,3 +1,3 @@
set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext /I ../../ext/angle_headers
cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.dll.lib /out:../../bin/example_gles_triangle.exe
cl /we4013 /Zi /Zc:preprocessor /std:c11 /experimental:c11atomics %INCLUDES% main.c /link /LIBPATH:../../bin milepost.dll.lib /out:../../bin/example_gles_triangle.exe

View File

@ -6,6 +6,7 @@
* @revision:
*
*****************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
@ -15,8 +16,6 @@
#define MG_INCLUDE_GL_API 1
#include"milepost.h"
#define LOG_SUBSYSTEM "Main"
unsigned int program;
const char* vshaderSource =
@ -60,15 +59,13 @@ void compile_shader(GLuint shader, const char* source)
int main()
{
LogLevel(LOG_LEVEL_DEBUG);
mp_init();
mp_rect rect = {.x = 100, .y = 100, .w = 800, .h = 600};
mp_window window = mp_window_create(rect, "test", 0);
//NOTE: create surface
mg_surface surface = mg_surface_create_for_window(window, MG_BACKEND_GLES);
mg_surface surface = mg_surface_create_for_window(window, MG_GLES);
mg_surface_prepare(surface);
//NOTE: init shader and gl state
@ -115,10 +112,10 @@ int main()
while(!mp_should_quit())
{
mp_pump_events(0);
mp_event event = {0};
while(mp_next_event(&event))
mp_event* event = 0;
while((event = mp_next_event(mem_scratch())) != 0)
{
switch(event.type)
switch(event->type)
{
case MP_EVENT_WINDOW_CLOSE:
{
@ -130,7 +127,7 @@ int main()
}
}
mg_surface_prepare(surface);
// mg_surface_prepare(surface);
glClearColor(0.3, 0.3, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
@ -156,8 +153,12 @@ int main()
glDrawArrays(GL_TRIANGLES, 0, 3);
mg_surface_present(surface);
mem_arena_clear(mem_scratch());
}
mg_surface_destroy(surface);
mp_window_destroy(window);
mp_terminate();
return(0);

View File

@ -42,13 +42,21 @@ def gather_api(tree, api, version):
tree = et.parse(args.spec)
# put all GL commands in a dict
commands = dict()
commandsSpec = tree.find('./commands')
for command in commandsSpec.iter('command'):
name = command.find('proto/name')
commands[name.text] = command
#gather command names per API
gl41 = gather_api(tree, 'gl', 4.1)
gl43 = gather_api(tree, 'gl', 4.3)
gl44 = gather_api(tree, 'gl', 4.4)
gles30 = gather_api(tree, 'gles2', 3.1)
gles31 = gather_api(tree, 'gles2', 3.2)
gles31 = gather_api(tree, 'gles2', 3.1)
gles32 = gather_api(tree, 'gles2', 3.2)
glall = list(set().union(gl41, gl43, gl44, gles30, gles31))
glall = list(set().union(gl41, gl43, gl44, gles31, gles32))
#---------------------------------------------------------------
@ -93,6 +101,8 @@ f.write('#include"GLES3/gl32.h"\n\n')
# generate interface struct
f.write('typedef struct mg_gl_api\n{\n')
f.write(' const char* name;\n')
for func in glall:
f.write('\t' + 'PFN' + func.upper() + 'PROC ' + remove_prefix(func, 'gl') + ';\n')
@ -129,7 +139,6 @@ f.write("void mg_gl_load_gles31(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()
#---------------------------------------------------------------
@ -139,10 +148,88 @@ f.close()
def emit_loader(f, name, procs):
f.write('void mg_gl_load_'+ name +'(mg_gl_api* api, mg_gl_load_proc loadProc)\n')
f.write("{\n")
for proc in procs:
f.write('\tapi->' + remove_prefix(proc, 'gl') + ' = loadProc("' + proc + '");\n')
f.write(' api->name = "'+ name +'";\n')
for proc in glall:
if proc in procs:
f.write(' api->' + remove_prefix(proc, 'gl') + ' = loadProc("' + proc + '");\n')
else:
f.write(' api->' + remove_prefix(proc, 'gl') + ' = mg_' + proc + '_noimpl;\n')
f.write("}\n\n")
def emit_null_api(f, procs):
f.write('mg_gl_api __mgGLNoAPI;\n\n')
for name in procs:
command = commands.get(name)
if command == None:
print("Couldn't find definition for required command '" + name + "'")
exit(-1)
proto = command.find("proto")
ptype = proto.find("ptype")
retType = ''
if proto.text != None:
retType += proto.text
if ptype != None:
if ptype.text != None:
retType += ptype.text
if ptype.tail != None:
retType += ptype.tail
retType = retType.strip()
f.write(retType + ' mg_' + name + '_noimpl(')
params = command.findall('param')
for i, param in enumerate(params):
argName = param.find('name').text
typeNode = param.find('ptype')
typeName = ''
if param.text != None:
typeName += param.text
if typeNode != None:
if typeNode.text != None:
typeName += typeNode.text
if typeNode.tail != None:
typeName += typeNode.tail
typeName = typeName.strip()
f.write(typeName + ' ' + argName)
if i < len(params)-1:
f.write(', ')
f.write(')\n')
f.write('{\n')
f.write(' if(__mgGLAPI == &__mgGLNoAPI)\n')
f.write(' {\n')
f.write(' log_error("No GL or GLES API is selected. Make sure you call mg_surface_prepare() before calling OpenGL API functions.\\n");\n')
f.write(' }\n')
f.write(' else\n')
f.write(' {\n')
f.write(' log_error("'+ name +' is not part of currently selected %s API\\n", __mgGLAPI->name);\n')
f.write(' }\n')
if retType != 'void':
f.write(' return(('+ retType +')0);\n')
f.write('}\n')
f.write('mg_gl_api __mgGLNoAPI = {\n')
for proc in procs:
f.write(' .' + remove_prefix(proc, 'gl') + ' = mg_' + proc + '_noimpl,\n')
f.write("};\n\n")
f = open(loaderCPath, 'w')
emit_doc(f, loaderName, '.c')
@ -150,15 +237,17 @@ emit_doc(f, loaderName, '.c')
f.write('#include"' + loaderName + '.h"\n')
f.write('#include"platform.h"\n\n')
f.write("mp_thread_local mg_gl_api* __mgGLAPI = 0;\n\n")
f.write("mp_thread_local mg_gl_api* __mgGLAPI = 0;\n")
emit_null_api(f, glall)
emit_loader(f, 'gl41', gl41)
emit_loader(f, 'gl43', gl43)
emit_loader(f, 'gl44', gl44)
emit_loader(f, 'gles30', gles30)
emit_loader(f, 'gles31', gles31)
emit_loader(f, 'gles32', gles32)
f.write("void mg_gl_select_api(mg_gl_api* api){ __mgGLAPI = api; }\n")
f.write("void mg_gl_deselect_api(){ __mgGLAPI = &__mgGLNoAPI; }\n")
f.write("mg_gl_api* mg_gl_get_api(void) { return(__mgGLAPI); }\n\n")
f.close()

View File

@ -22,12 +22,12 @@
//TODO: use version hints, once we have all api versions correctly categorized by glapi.py
#define MG_GLES_VERSION_MAJOR 3
#define MG_GLES_VERSION_MINOR 0
#define mg_gl_load_gles mg_gl_load_gles30
#define mg_gl_load_gles mg_gl_load_gles31
#else
#define MG_EGL_PLATFORM_ANGLE_TYPE EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE
#define MG_GLES_VERSION_MAJOR 3
#define MG_GLES_VERSION_MINOR 1
#define mg_gl_load_gles mg_gl_load_gles31
#define mg_gl_load_gles mg_gl_load_gles32
#endif
@ -50,7 +50,7 @@ void mg_egl_surface_destroy(mg_surface_data* interface)
if(&surface->api == mg_gl_get_api())
{
mg_gl_select_api(0);
mg_gl_deselect_api();
}
if(eglGetCurrentContext() == surface->eglContext)
{
@ -76,6 +76,13 @@ void mg_egl_surface_present(mg_surface_data* interface)
eglSwapBuffers(surface->eglDisplay, surface->eglSurface);
}
void mg_egl_surface_deselect(mg_surface_data* interface)
{
mg_egl_surface* surface = (mg_egl_surface*)interface;
eglMakeCurrent(surface->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
mg_gl_deselect_api();
}
void mg_egl_surface_swap_interval(mg_surface_data* interface, int swap)
{
mg_egl_surface* surface = (mg_egl_surface*)interface;
@ -91,6 +98,7 @@ void mg_egl_surface_init(mg_egl_surface* surface)
surface->interface.destroy = mg_egl_surface_destroy;
surface->interface.prepare = mg_egl_surface_prepare;
surface->interface.present = mg_egl_surface_present;
surface->interface.deselect = mg_egl_surface_deselect;
surface->interface.swapInterval = mg_egl_surface_swap_interval;
EGLAttrib displayAttribs[] = {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
*
* @file: gl_loader.h
* @note: auto-generated by glapi.py from gl.xml
* @date: 12/072023
* @date: 07/082023
*
*********************************************************/
#ifndef __GL_LOADER_H__

View File

@ -207,6 +207,11 @@ void mg_surface_destroy(mg_surface handle)
mg_surface_data* surface = mg_surface_data_from_handle(handle);
if(surface)
{
if(__mgSelectedSurface.h == handle.h)
{
mg_surface_deselect();
}
if(surface->backend && surface->backend->destroy)
{
surface->backend->destroy(surface->backend);
@ -350,7 +355,12 @@ void mg_surface_render_commands(mg_surface surface,
mg_path_elt* elements)
{
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData && surfaceData->backend)
if(surface.h != __mgSelectedSurface.h)
{
log_error("surface is not selected. Make sure to call mg_surface_prepare() before drawing onto a surface.\n");
}
else if(surfaceData && surfaceData->backend)
{
surfaceData->backend->render(surfaceData->backend,
clearColor,
@ -380,7 +390,12 @@ mg_image mg_image_create(mg_surface surface, u32 width, u32 height)
{
mg_image image = mg_image_nil();
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData && surfaceData->backend)
if(surface.h != __mgSelectedSurface.h)
{
log_error("surface is not selected. Make sure to call mg_surface_prepare() before modifying graphics resources.\n");
}
else if(surfaceData && surfaceData->backend)
{
DEBUG_ASSERT(surfaceData->api == MG_CANVAS);
@ -397,7 +412,14 @@ mg_image mg_image_create(mg_surface surface, u32 width, u32 height)
void mg_image_destroy(mg_image image)
{
mg_image_data* imageData = mg_image_data_from_handle(image);
if(imageData)
{
if(imageData->surface.h != __mgSelectedSurface.h)
{
log_error("surface is not selected. Make sure to call mg_surface_prepare() before modifying graphics resources.\n");
}
else
{
mg_surface_data* surface = mg_surface_data_from_handle(imageData->surface);
if(surface && surface->backend)
@ -406,12 +428,20 @@ void mg_image_destroy(mg_image image)
mg_handle_recycle(image.h);
}
}
}
}
void mg_image_upload_region_rgba8(mg_image image, mp_rect region, u8* pixels)
{
mg_image_data* imageData = mg_image_data_from_handle(image);
if(imageData)
{
if(imageData->surface.h != __mgSelectedSurface.h)
{
log_error("surface is not selected. Make sure to call mg_surface_prepare() before modifying graphics resources.\n");
}
else
{
mg_surface_data* surfaceData = mg_surface_data_from_handle(imageData->surface);
if(surfaceData)
@ -420,4 +450,5 @@ void mg_image_upload_region_rgba8(mg_image image, mp_rect region, u8* pixels)
surfaceData->backend->imageUploadRegion(surfaceData->backend, imageData, region, pixels);
}
}
}
}

View File

@ -27,6 +27,7 @@
#include"platform/osx_clock.c"
#include"platform/posix_io.c"
#include"platform/posix_thread.c"
/*
#include"platform/unix_rng.c"
#include"platform/posix_socket.c"

View File

@ -330,6 +330,14 @@ MP_API void mp_window_center(mp_window window);
MP_API mp_rect mp_window_content_rect_for_frame_rect(mp_rect frameRect, mp_window_style style);
MP_API mp_rect mp_window_frame_rect_for_content_rect(mp_rect contentRect, mp_window_style style);
//---------------------------------------------------------------
// Dispatching stuff to the main thread
//---------------------------------------------------------------
typedef i32 (*mp_dispatch_proc)(void* user);
MP_API i32 mp_dispatch_on_main_thread_sync(mp_window main_window, mp_dispatch_proc proc, void* user);
//--------------------------------------------------------------------
// Clipboard
//--------------------------------------------------------------------

View File

@ -1629,7 +1629,6 @@ void mg_surface_cleanup(mg_surface_data* surface)
void mg_surface_init_for_window(mg_surface_data* surface, mp_window_data* window)
{@autoreleasepool{
surface->nativeLayer = mg_osx_surface_native_layer;
surface->contentsScaling = mg_osx_surface_contents_scaling;
surface->getSize = mg_osx_surface_get_size;
@ -1901,6 +1900,25 @@ void mp_pump_events(f64 timeout)
}
}
i32 mp_dispatch_on_main_thread_sync(mp_window main_window, mp_dispatch_proc proc, void* user)
{
__block i32 result = 0;
dispatch_block_t block = ^{
result = proc(user);
};
if([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
return(result);
}
//--------------------------------------------------------------------
// system dialogs windows
//--------------------------------------------------------------------

View File

@ -8,7 +8,7 @@
extern void* orca_mem_grow(u64 size);
#define MORECORE orca_mem_grow
#define MORECORE_CONTIGUOUS 0
/*
This is a version (aka dlmalloc) of malloc/free/realloc written by
Doug Lea and released to the public domain. Use, modify, and

View File

@ -3,13 +3,12 @@
* @file: win32_thread.c
* @author: Reuben Dunnington
* @date: 7/30/2023
* @revision:
*
*****************************************************************/
#include<processthreadsapi.h>
#include<synchapi.h>
#include<math.h> //INFINITY
#include<winuser.h> // PostMessage
#include"platform_thread.h"

View File

@ -120,4 +120,24 @@ static inline u64 next_pow2_u64(u64 x)
#define defer_loop(begin, end) begin; for(int __i__=0; __i__<1; __i__++, end)
#define ORCA_COMMA ,
#define ORCA_PASS(A, ...) A(__VA_ARGS__)
#define ORCA_EXPAND(...) __VA_ARGS__
#define ORCA_EXPAND_NIL(...)
#define ORCA_PASTE(a , b) a##b
#define ORCA_ARG1_UTIL(a, ...) a
#define ORCA_ARG1(...) ORCA_ARG1_UTIL(__VA_ARGS__)
#define ORCA_VA_COMMA_TAIL(a, ...) , ##__VA_ARGS__
//NOTE: this expands to opt if __VA_ARGS__ is empty, and to , va1, va2, ... opt otherwise
#define ORCA_VA_NOPT_UTIL(opt, ...) ,##__VA_ARGS__ opt
//NOTE: this expands to opt if __VA_ARGS__ is empty, and to nothing otherwise
#define ORCA_VA_NOPT(opt, ...) ORCA_PASS(ORCA_ARG1, ORCA_VA_NOPT_UTIL(opt, ##__VA_ARGS__))
//NOTE: this expands to opt if __VA_ARGS__ is non empty, and to nothing otherwise
#define ORCA_VA_OPT(opt, ...) ORCA_PASS(ORCA_PASTE, ORCA_EXPAND , ORCA_VA_NOPT(_NIL, ##__VA_ARGS__))(opt)
#endif //__MACRO_HELPERS_H_

View File

@ -156,6 +156,7 @@ void mg_wgl_surface_present(mg_surface_data* interface)
void mg_wgl_surface_deselect(mg_surface_data* interface)
{
wglMakeCurrent(NULL, NULL);
mg_gl_deselect_api();
}
void mg_wgl_surface_swap_interval(mg_surface_data* interface, int swap)

View File

@ -9,6 +9,7 @@
#include<dwmapi.h>
#include"mp_app.c"
#include"platform_thread.h"
void mp_init_keys()
{
@ -504,6 +505,13 @@ LRESULT WinProc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam)
//TODO
} break;
case MP_WM_USER_DISPATCH_PROC:
{
mp_dispatch_proc proc = (mp_dispatch_proc)wParam;
void* user = (void*)lParam;
result = proc(user);
} break;
default:
{
result = DefWindowProc(windowHandle, message, wParam, lParam);
@ -552,6 +560,15 @@ void mp_pump_events(f64 timeout)
}
}
i32 mp_dispatch_on_main_thread_sync(mp_window main_window, mp_dispatch_proc proc, void* user)
{
mp_window_data* window_data = mp_window_ptr_from_handle(main_window);
DEBUG_ASSERT(window_data != NULL);
LRESULT result = SendMessage(window_data->win32.hWnd, MP_WM_USER_DISPATCH_PROC, (WPARAM)proc, (LPARAM)user);
return result;
}
//--------------------------------------------------------------------
// window management
//--------------------------------------------------------------------

View File

@ -45,4 +45,9 @@ typedef struct win32_app_data
#define MP_PLATFORM_APP_DATA win32_app_data win32;
enum MP_WM_USER
{
MP_WM_USER_DISPATCH_PROC = 0x0400, // WM_USER messages are defined from 0x400 to 0x7FFF
};
#endif __WIN32_APP_H_