[surface/canvas cleanup]

- Make build-time and runtime backend selection a bit easier
- Reorganized backend-specific files with slightly more coherent naming scheme
This commit is contained in:
martinfouilleul 2023-02-10 16:56:10 +01:00
parent f54f639db5
commit 80ea8db687
34 changed files with 13006 additions and 13501 deletions

View File

@ -6,5 +6,5 @@ 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 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
cl /we4013 /Zi /Zc:preprocessor /DMG_IMPLEMENTS_BACKEND_GL /std:c11 %INCLUDES% /c /Fo:bin/milepost.obj src/milepost.c cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% /c /Fo:bin/milepost.obj src/milepost.c
lib bin/milepost.obj /OUT:bin/milepost.lib lib bin/milepost.obj /OUT:bin/milepost.lib

164
build.sh
View File

@ -1,82 +1,82 @@
#!/bin/bash #!/bin/bash
DEBUG_FLAGS="-g -DDEBUG -DLOG_COMPILE_DEBUG" DEBUG_FLAGS="-g -DDEBUG -DLOG_COMPILE_DEBUG"
#DEBUG_FLAGS="-O3" #DEBUG_FLAGS="-O3"
#-------------------------------------------------------------- #--------------------------------------------------------------
# set target # set target
#-------------------------------------------------------------- #--------------------------------------------------------------
target="$1" target="$1"
if [ -z $target ] ; then if [ -z $target ] ; then
target='lib' target='lib'
fi fi
shaderFlagParam="$2" shaderFlagParam="$2"
#-------------------------------------------------------------- #--------------------------------------------------------------
# Detect OS and set environment variables accordingly # Detect OS and set environment variables accordingly
#-------------------------------------------------------------- #--------------------------------------------------------------
OS=$(uname -s) OS=$(uname -s)
if [ $OS = "Darwin" ] ; then if [ $OS = "Darwin" ] ; then
#echo "Target '$target' for macOS" #echo "Target '$target' for macOS"
CC=clang CC=clang
CXX=clang++ CXX=clang++
DYLIB_SUFFIX='dylib' DYLIB_SUFFIX='dylib'
SYS_LIBS='' SYS_LIBS=''
FLAGS="-mmacos-version-min=10.15.4 -DMG_IMPLEMENTS_BACKEND_METAL -DMG_IMPLEMENTS_BACKEND_GLES -maes" FLAGS="-mmacos-version-min=10.15.4 -maes"
CFLAGS="-std=c11" CFLAGS="-std=c11"
elif [ $OS = "Linux" ] ; then elif [ $OS = "Linux" ] ; then
echo "Error: Linux is not supported yet" echo "Error: Linux is not supported yet"
exit -1 exit -1
else else
echo "Error: Unsupported OS $OS" echo "Error: Unsupported OS $OS"
exit -1 exit -1
fi fi
#-------------------------------------------------------------- #--------------------------------------------------------------
# Set paths # Set paths
#-------------------------------------------------------------- #--------------------------------------------------------------
BINDIR="./bin" BINDIR="./bin"
SRCDIR="./src" SRCDIR="./src"
EXTDIR="./ext" EXTDIR="./ext"
RESDIR="./resources" RESDIR="./resources"
INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$EXTDIR/angle_headers" INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$EXTDIR/angle_headers"
#-------------------------------------------------------------- #--------------------------------------------------------------
# Build # Build
#-------------------------------------------------------------- #--------------------------------------------------------------
if [ ! \( -e bin \) ] ; then if [ ! \( -e bin \) ] ; then
mkdir ./bin mkdir ./bin
fi fi
if [ $target = 'lib' ] ; then if [ $target = 'lib' ] ; then
# compile metal shader # compile metal shader
xcrun -sdk macosx metal $shaderFlagParam -c -o $BINDIR/metal_shader.air $SRCDIR/metal_shader.metal xcrun -sdk macosx metal $shaderFlagParam -c -o $BINDIR/metal_shader.air $SRCDIR/metal_shader.metal
xcrun -sdk macosx metallib -o $RESDIR/metal_shader.metallib $BINDIR/metal_shader.air xcrun -sdk macosx metallib -o $RESDIR/metal_shader.metallib $BINDIR/metal_shader.air
# compile milepost. We use one compilation unit for all C code, and one compilation # compile milepost. We use one compilation unit for all C code, and one compilation
# unit for all ObjectiveC code # unit for all ObjectiveC code
$CC $DEBUG_FLAGS -c -o $BINDIR/milepost_c.o $CFLAGS $FLAGS $INCLUDES $SRCDIR/milepost.c $CC $DEBUG_FLAGS -c -o $BINDIR/milepost_c.o $CFLAGS $FLAGS $INCLUDES $SRCDIR/milepost.c
$CC $DEBUG_FLAGS -c -o $BINDIR/milepost_objc.o $FLAGS $INCLUDES $SRCDIR/milepost.m $CC $DEBUG_FLAGS -c -o $BINDIR/milepost_objc.o $FLAGS $INCLUDES $SRCDIR/milepost.m
# build the static library # build the static library
libtool -static -o $BINDIR/libmilepost.a $BINDIR/milepost_c.o $BINDIR/milepost_objc.o libtool -static -o $BINDIR/libmilepost.a $BINDIR/milepost_c.o $BINDIR/milepost_objc.o
else else
# additional targets # additional targets
if [ $target = 'test' ] ; then if [ $target = 'test' ] ; then
pushd examples/test_app pushd examples/test_app
./build.sh ./build.sh
popd popd
elif [ $target = 'clean' ] ; then elif [ $target = 'clean' ] ; then
rm -r ./bin rm -r ./bin
else else
echo "unrecognized target $target" echo "unrecognized target $target"
exit -1 exit -1
fi fi
fi fi

View File

@ -1,3 +1,4 @@
set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext /I ../../ext/angle_headers set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext /I ../../ext/angle_headers
cl /we4013 /Zi /Zc:preprocessor /DMG_IMPLEMENTS_BACKEND_GLES /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.lib /LIBPATH:../../bin libEGL.dll.lib libGLESv2.dll.lib user32.lib opengl32.lib gdi32.lib /out:../../bin/example_canvas.exe
cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.lib user32.lib opengl32.lib gdi32.lib shcore.lib /out:../../bin/example_canvas.exe

View File

@ -1,11 +1,11 @@
#!/bin/bash #!/bin/bash
BINDIR=../../bin BINDIR=../../bin
RESDIR=../../resources RESDIR=../../resources
SRCDIR=../../src SRCDIR=../../src
INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$SRCDIR/app" INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$SRCDIR/app"
LIBS="-L$BINDIR -lmilepost -framework Carbon -framework Cocoa -framework Metal -framework QuartzCore" LIBS="-L$BINDIR -lmilepost -framework Carbon -framework Cocoa -framework Metal -framework QuartzCore"
FLAGS="-mmacos-version-min=10.15.4 -DDEBUG -DLOG_COMPILE_DEBUG -Wl,-dead_strip -DMG_IMPLEMENTS_BACKEND_METAL" FLAGS="-mmacos-version-min=10.15.4 -DDEBUG -DLOG_COMPILE_DEBUG -Wl,-dead_strip"
clang -g $FLAGS $LIBS $INCLUDES -o $BINDIR/example_canvas main.c clang -g $FLAGS $LIBS $INCLUDES -o $BINDIR/example_canvas main.c

View File

@ -8,6 +8,7 @@
*****************************************************************/ *****************************************************************/
#include<stdlib.h> #include<stdlib.h>
#include<string.h> #include<string.h>
#include<errno.h>
#define _USE_MATH_DEFINES //NOTE: necessary for MSVC #define _USE_MATH_DEFINES //NOTE: necessary for MSVC
#include<math.h> #include<math.h>
@ -16,6 +17,7 @@
#define LOG_SUBSYSTEM "Main" #define LOG_SUBSYSTEM "Main"
mg_font create_font() mg_font create_font()
{ {
//NOTE(martin): create font //NOTE(martin): create font
@ -25,7 +27,7 @@ mg_font create_font()
FILE* fontFile = fopen(fontPathCString, "r"); FILE* fontFile = fopen(fontPathCString, "r");
if(!fontFile) if(!fontFile)
{ {
LOG_ERROR("Could not load font file '%s'\n", fontPathCString); LOG_ERROR("Could not load font file '%s': %s\n", fontPathCString, strerror(errno));
return(mg_font_nil()); return(mg_font_nil());
} }
unsigned char* fontData = 0; unsigned char* fontData = 0;
@ -53,22 +55,26 @@ int main()
LogLevel(LOG_LEVEL_DEBUG); LogLevel(LOG_LEVEL_DEBUG);
mp_init(); mp_init();
mp_clock_init(); mp_clock_init(); //TODO put that in mp_init()?
mp_rect rect = {.x = 100, .y = 100, .w = 800, .h = 600}; mp_rect windowRect = {.x = 100, .y = 100, .w = 810, .h = 610};
mp_window window = mp_window_create(rect, "test", 0); mp_window window = mp_window_create(windowRect, "test", 0);
mp_rect contentRect = mp_window_get_content_rect(window);
//NOTE: create surface //NOTE: create surface
#if defined(OS_MACOS) mg_surface surface = mg_surface_create_for_window(window, MG_BACKEND_DEFAULT);
mg_surface surface = mg_metal_surface_create_for_window(window); mg_surface_swap_interval(surface, 0);
#elif defined(OS_WIN64)
mg_surface surface = mg_gl_surface_create_for_window(window);
#else
#error "unsupported OS"
#endif
//TODO: create canvas //TODO: create canvas
mg_canvas canvas = mg_canvas_create(surface); mg_canvas canvas = mg_canvas_create(surface);
if(mg_canvas_is_nil(canvas))
{
printf("Error: couldn't create canvas\n");
return(-1);
}
mg_font font = create_font(); mg_font font = create_font();
// start app // start app
@ -76,7 +82,8 @@ int main()
mp_window_focus(window); mp_window_focus(window);
f32 x = 400, y = 300; f32 x = 400, y = 300;
f32 dx = 5, dy = 5; f32 speed = 0;
f32 dx = speed, dy = speed;
f64 frameTime = 0; f64 frameTime = 0;
while(!mp_should_quit()) while(!mp_should_quit())
@ -94,37 +101,39 @@ int main()
mp_request_quit(); mp_request_quit();
} break; } break;
case MP_EVENT_WINDOW_RESIZE:
{
printf("resized, rect = {%f, %f, %f, %f}\n",
event.frame.rect.x,
event.frame.rect.y,
event.frame.rect.w,
event.frame.rect.h);
} break;
case MP_EVENT_KEYBOARD_KEY: case MP_EVENT_KEYBOARD_KEY:
{ {
if(event.key.action == MP_KEY_PRESS || event.key.action == MP_KEY_REPEAT) if(event.key.action == MP_KEY_PRESS || event.key.action == MP_KEY_REPEAT)
{ {
/*
if(event.key.code == MP_KEY_LEFT) if(event.key.code == MP_KEY_LEFT)
{ {
dx-=5.1; if(x - 200 > 0)
{
x-=5;
}
} }
else if(event.key.code == MP_KEY_RIGHT) else if(event.key.code == MP_KEY_RIGHT)
{ {
dx+=5.1; if(x + 200 < contentRect.w)
{
x+=5;
}
} }
else if(event.key.code == MP_KEY_UP) else if(event.key.code == MP_KEY_UP)
{ {
dy+=5.1; if(y + 200 < contentRect.h)
{
y+=5;
}
} }
else if(event.key.code == MP_KEY_DOWN) else if(event.key.code == MP_KEY_DOWN)
{ {
dy-=5.1; if(y - 200 > 0)
{
y-=5;
}
} }
*/ //*/
} }
} break; } break;
@ -135,19 +144,23 @@ int main()
if(x-200 < 0) if(x-200 < 0)
{ {
dx = 5; x = 200;
dx = speed;
} }
if(x+200 > 800) if(x+200 > contentRect.w)
{ {
dx = -5; x = contentRect.w - 200;
dx = -speed;
} }
if(y-200 < 0) if(y-200 < 0)
{ {
dy = 5; y = 200;
dy = speed;
} }
if(y+200 > 550) if(y+200 > contentRect.h)
{ {
dy = -5; y = contentRect.h - 200;
dy = -speed;
} }
x += dx; x += dx;
y += dy; y += dy;
@ -166,7 +179,6 @@ int main()
f32 frown = frameTime > 0.033 ? 100 : 0; f32 frown = frameTime > 0.033 ? 100 : 0;
mg_set_color_rgba(0, 0, 0, 1); mg_set_color_rgba(0, 0, 0, 1);
mg_set_width(20); mg_set_width(20);
mg_move_to(x-100, y-100); mg_move_to(x-100, y-100);
mg_cubic_to(x-50, y-150+frown, x+50, y-150+frown, x+100, y-100); mg_cubic_to(x-50, y-150+frown, x+50, y-150+frown, x+100, y-100);
@ -198,9 +210,13 @@ int main()
mem_arena_clear(mem_scratch()); mem_arena_clear(mem_scratch());
frameTime = mp_get_time(MP_CLOCK_MONOTONIC) - startTime; frameTime = mp_get_time(MP_CLOCK_MONOTONIC) - startTime;
} }
mg_font_destroy(font);
mg_canvas_destroy(canvas);
mg_surface_destroy(surface);
mp_window_destroy(window);
mp_terminate(); mp_terminate();
return(0); return(0);

View File

@ -1,4 +1,4 @@
set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext
cl /we4013 /Zi /Zc:preprocessor /DMG_IMPLEMENTS_BACKEND_GL /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.lib user32.lib opengl32.lib gdi32.lib shcore.lib /out:../../bin/perf_text.exe cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.lib user32.lib opengl32.lib gdi32.lib shcore.lib /out:../../bin/perf_text.exe

View File

@ -63,10 +63,8 @@ static const char* TEST_STRING =
mg_font create_font() mg_font create_font()
{ {
//NOTE(martin): create font //NOTE(martin): create font
/* str8 fontPath = mp_app_get_resource_path(mem_scratch(), "../resources/OpenSansLatinSubset.ttf"); str8 fontPath = mp_app_get_resource_path(mem_scratch(), "../resources/OpenSansLatinSubset.ttf");
char* fontPathCString = str8_to_cstring(mem_scratch(), fontPath); char* fontPathCString = str8_to_cstring(mem_scratch(), fontPath);
*/
char* fontPathCString = "resources/OpenSansLatinSubset.ttf";
FILE* fontFile = fopen(fontPathCString, "r"); FILE* fontFile = fopen(fontPathCString, "r");
if(!fontFile) if(!fontFile)
@ -107,13 +105,7 @@ int main()
//NOTE: create surface, canvas and font //NOTE: create surface, canvas and font
#if defined(OS_MACOS) mg_surface surface = mg_surface_create_for_window(window, MG_BACKEND_DEFAULT);
mg_surface surface = mg_metal_surface_create_for_window(window);
#elif defined(OS_WIN64)
mg_surface surface = mg_gl_surface_create_for_window(window);
#else
#error "unsupported OS"
#endif
mg_surface_swap_interval(surface, 0); mg_surface_swap_interval(surface, 0);
mg_canvas canvas = mg_canvas_create(surface); mg_canvas canvas = mg_canvas_create(surface);
@ -300,6 +292,11 @@ int main()
mem_arena_clear(mem_scratch()); mem_arena_clear(mem_scratch());
} }
mg_font_destroy(font);
mg_canvas_destroy(canvas);
mg_surface_destroy(surface);
mp_window_destroy(window);
mp_terminate(); mp_terminate();
return(0); return(0);

View File

@ -1,3 +0,0 @@
set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext /I ../../ext/angle_headers
cl /we4013 /Zi /Zc:preprocessor /DMG_IMPLEMENTS_BACKEND_GL /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.lib user32.lib opengl32.lib gdi32.lib shcore.lib /out:../../bin/example_canvas.exe

View File

@ -1,11 +0,0 @@
#!/bin/bash
BINDIR=../../bin
RESDIR=../../resources
SRCDIR=../../src
INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$SRCDIR/app"
LIBS="-L$BINDIR -lmilepost -framework Carbon -framework Cocoa -framework Metal -framework QuartzCore"
FLAGS="-mmacos-version-min=10.15.4 -DDEBUG -DLOG_COMPILE_DEBUG -Wl,-dead_strip"
clang -g $FLAGS $LIBS $INCLUDES -o $BINDIR/example_canvas main.c

View File

@ -1,247 +0,0 @@
/************************************************************//**
*
* @file: main.cpp
* @author: Martin Fouilleul
* @date: 30/07/2022
* @revision:
*
*****************************************************************/
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#define _USE_MATH_DEFINES //NOTE: necessary for MSVC
#include<math.h>
#include"milepost.h"
#define LOG_SUBSYSTEM "Main"
mg_font create_font()
{
//NOTE(martin): create font
str8 fontPath = mp_app_get_resource_path(mem_scratch(), "../resources/OpenSansLatinSubset.ttf");
char* fontPathCString = str8_to_cstring(mem_scratch(), fontPath);
FILE* fontFile = fopen(fontPathCString, "r");
if(!fontFile)
{
LOG_ERROR("Could not load font file '%s': %s\n", fontPathCString, strerror(errno));
return(mg_font_nil());
}
unsigned char* fontData = 0;
fseek(fontFile, 0, SEEK_END);
u32 fontDataSize = ftell(fontFile);
rewind(fontFile);
fontData = (unsigned char*)malloc(fontDataSize);
fread(fontData, 1, fontDataSize, fontFile);
fclose(fontFile);
unicode_range ranges[5] = {UNICODE_RANGE_BASIC_LATIN,
UNICODE_RANGE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT,
UNICODE_RANGE_LATIN_EXTENDED_A,
UNICODE_RANGE_LATIN_EXTENDED_B,
UNICODE_RANGE_SPECIALS};
mg_font font = mg_font_create_from_memory(fontDataSize, fontData, 5, ranges);
free(fontData);
return(font);
}
int main()
{
LogLevel(LOG_LEVEL_DEBUG);
mp_init();
mp_clock_init(); //TODO put that in mp_init()?
mp_rect windowRect = {.x = 100, .y = 100, .w = 810, .h = 610};
mp_window window = mp_window_create(windowRect, "test", 0);
mp_rect contentRect = mp_window_get_content_rect(window);
//NOTE: create surface
#if defined(OS_MACOS)
mg_surface surface = mg_metal_surface_create_for_window(window);
#elif defined(OS_WIN64)
mg_surface surface = mg_gl_surface_create_for_window(window);
#else
#error "unsupported OS"
#endif
mg_surface_swap_interval(surface, 0);
//TODO: create canvas
mg_canvas canvas = mg_canvas_create(surface);
if(mg_canvas_is_nil(canvas))
{
printf("Error: couldn't create canvas\n");
return(-1);
}
mg_font font = create_font();
// start app
mp_window_bring_to_front(window);
mp_window_focus(window);
f32 x = 400, y = 300;
f32 speed = 0;
f32 dx = speed, dy = speed;
f64 frameTime = 0;
while(!mp_should_quit())
{
f64 startTime = mp_get_time(MP_CLOCK_MONOTONIC);
mp_pump_events(0);
mp_event event = {0};
while(mp_next_event(&event))
{
switch(event.type)
{
case MP_EVENT_WINDOW_CLOSE:
{
mp_request_quit();
} break;
case MP_EVENT_WINDOW_RESIZE:
{
printf("resized, rect = {%f, %f, %f, %f}\n",
event.frame.rect.x,
event.frame.rect.y,
event.frame.rect.w,
event.frame.rect.h);
} break;
case MP_EVENT_KEYBOARD_KEY:
{
if(event.key.action == MP_KEY_PRESS || event.key.action == MP_KEY_REPEAT)
{
//*
if(event.key.code == MP_KEY_LEFT)
{
if(x - 200 > 0)
{
x-=5;
}
}
else if(event.key.code == MP_KEY_RIGHT)
{
if(x + 200 < contentRect.w)
{
x+=5;
}
}
else if(event.key.code == MP_KEY_UP)
{
if(y + 200 < contentRect.h)
{
y+=5;
}
}
else if(event.key.code == MP_KEY_DOWN)
{
if(y - 200 > 0)
{
y-=5;
}
}
//*/
}
} break;
default:
break;
}
}
if(x-200 < 0)
{
x = 200;
dx = speed;
}
if(x+200 > contentRect.w)
{
x = contentRect.w - 200;
dx = -speed;
}
if(y-200 < 0)
{
y = 200;
dy = speed;
}
if(y+200 > contentRect.h)
{
y = contentRect.h - 200;
dy = -speed;
}
x += dx;
y += dy;
mg_surface_prepare(surface);
// background
mg_set_color_rgba(0, 1, 1, 1);
mg_clear();
// mg_clip_push(100, 100, contentRect.w - 200, contentRect.h - 200);
// head
mg_set_color_rgba(1, 1, 0, 1);
mg_circle_fill(x, y, 200);
/*
mg_clip_pop();
mg_set_width(2);
mg_set_color_rgba(1, 0, 0, 1);
mg_rectangle_stroke(100, 100, contentRect.w - 200, contentRect.h - 200);
*/
// smile
f32 frown = frameTime > 0.033 ? 100 : 0;
mg_set_color_rgba(0, 0, 0, 1);
mg_set_width(20);
mg_move_to(x-100, y-100);
mg_cubic_to(x-50, y-150+frown, x+50, y-150+frown, x+100, y-100);
mg_stroke();
// eyes
mg_ellipse_fill(x-70, y+50, 30, 50);
mg_ellipse_fill(x+70, y+50, 30, 50);
// text
mg_set_color_rgba(0, 0, 1, 1);
mg_set_font(font);
mg_set_font_size(12);
mg_move_to(50, 50);
str8 text = str8_pushf(mem_scratch(),
"Milepost vector graphics test program (frame time = %fs, fps = %f)...",
frameTime,
1./frameTime);
mg_text_outlines(text);
mg_fill();
printf("Milepost vector graphics test program (frame time = %fs, fps = %f)...\n",
frameTime,
1./frameTime);
mg_flush();
mg_surface_present(surface);
mem_arena_clear(mem_scratch());
frameTime = mp_get_time(MP_CLOCK_MONOTONIC) - startTime;
}
mg_font_destroy(font);
mg_canvas_destroy(canvas);
mg_surface_destroy(surface);
mp_window_destroy(window);
mp_terminate();
return(0);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -38,17 +38,6 @@ typedef struct mg_gl_canvas_backend
} mg_gl_canvas_backend; } mg_gl_canvas_backend;
mg_gl_surface* mg_gl_canvas_get_surface(mg_gl_canvas_backend* canvas)
{
mg_gl_surface* res = 0;
mg_surface_data* data = mg_surface_data_from_handle(canvas->surface);
if(data && data->backend == MG_BACKEND_GL)
{
res = (mg_gl_surface*)data;
}
return(res);
}
//NOTE: debugger //NOTE: debugger
typedef struct debug_vertex typedef struct debug_vertex
{ {
@ -140,11 +129,6 @@ void mg_gl_send_buffers(mg_gl_canvas_backend* backend, int shapeCount, int verte
void mg_gl_canvas_begin(mg_canvas_backend* interface) void mg_gl_canvas_begin(mg_canvas_backend* interface)
{ {
mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface; mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface;
mg_gl_surface* surface = mg_gl_canvas_get_surface(backend);
if(!surface)
{
return;
}
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} }
@ -157,11 +141,6 @@ void mg_gl_canvas_end(mg_canvas_backend* interface)
void mg_gl_canvas_clear(mg_canvas_backend* interface, mg_color clearColor) void mg_gl_canvas_clear(mg_canvas_backend* interface, mg_color clearColor)
{ {
mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface; mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface;
mg_gl_surface* surface = mg_gl_canvas_get_surface(backend);
if(!surface)
{
return;
}
glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
@ -170,11 +149,6 @@ void mg_gl_canvas_clear(mg_canvas_backend* interface, mg_color clearColor)
void mg_gl_canvas_draw_batch(mg_canvas_backend* interface, u32 shapeCount, u32 vertexCount, u32 indexCount) void mg_gl_canvas_draw_batch(mg_canvas_backend* interface, u32 shapeCount, u32 vertexCount, u32 indexCount)
{ {
mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface; mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface;
mg_gl_surface* surface = mg_gl_canvas_get_surface(backend);
if(!surface)
{
return;
}
/*NOTE: if we want debug_vertex while debugging, the following ensures the struct def doesn't get stripped away /*NOTE: if we want debug_vertex while debugging, the following ensures the struct def doesn't get stripped away
debug_vertex vertex; debug_vertex vertex;
@ -390,8 +364,6 @@ mg_canvas_backend* mg_gl_canvas_create(mg_surface surface)
if(surfaceData && surfaceData->backend == MG_BACKEND_GL) if(surfaceData && surfaceData->backend == MG_BACKEND_GL)
{ {
mg_gl_surface* glSurface = (mg_gl_surface*)surfaceData;
backend = malloc_type(mg_gl_canvas_backend); backend = malloc_type(mg_gl_canvas_backend);
memset(backend, 0, sizeof(mg_gl_canvas_backend)); memset(backend, 0, sizeof(mg_gl_canvas_backend));
backend->surface = surface; backend->surface = surface;

View File

@ -290,6 +290,89 @@ mg_font_data* mg_font_data_from_handle(mg_font font)
// surface API // surface API
//--------------------------------------------------------------- //---------------------------------------------------------------
#if MG_COMPILE_BACKEND_GL
#if defined(OS_WIN64)
#include"wgl_surface.h"
#define gl_surface_create_for_window mg_wgl_surface_create_for_window
#elif defined(OS_MACOS)
/*
#include"nsgl_surface.h"
#define gl_surface_create_for_window nsgl_surface_create_for_window
*/
#endif
#endif
#if MG_COMPILE_BACKEND_METAL
#include"mtl_surface.h"
#endif
bool mg_is_surface_backend_available(mg_backend_id backend)
{
bool result = false;
switch(backend)
{
#if MG_COMPILE_BACKEND_METAL
case MG_BACKEND_METAL:
#endif
#if MG_COMPILE_BACKEND_GL
case MG_BACKEND_GL:
#endif
#if MG_COMPILE_BACKEND_GLES
case MG_BACKEND_GLES:
#endif
result = true;
break;
default:
break;
}
return(result);
}
bool mg_is_canvas_backend_available(mg_backend_id backend)
{
bool result = false;
switch(backend)
{
#if MG_COMPILE_BACKEND_METAL
case MG_BACKEND_METAL:
#endif
#if MG_COMPILE_BACKEND_GL && defined(OS_WIN64)
case MG_BACKEND_GL:
#endif
result = true;
break;
default:
break;
}
return(result);
}
mg_surface mg_surface_create_for_window(mp_window window, mg_backend_id backend)
{
mg_surface surface = mg_surface_nil();
switch(backend)
{
#if MG_COMPILE_BACKEND_GL
case MG_BACKEND_GL:
surface = gl_surface_create_for_window(window);
break;
#endif
#if MG_COMPILE_BACKEND_METAL
case MG_METAL_BACKEND:
surface = mg_mtl_surface_create_for_window(window);
break;
#endif
default:
break;
}
return(surface);
}
void mg_surface_destroy(mg_surface handle) void mg_surface_destroy(mg_surface handle)
{ {
DEBUG_ASSERT(__mgData.init); DEBUG_ASSERT(__mgData.init);
@ -2563,19 +2646,14 @@ mp_rect mg_text_bounding_box(mg_font font, f32 fontSize, str8 text)
//NOTE(martin): graphics canvas API //NOTE(martin): graphics canvas API
//------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------
#ifdef MG_IMPLEMENTS_BACKEND_METAL #ifdef MG_COMPILE_BACKEND_METAL
mg_canvas_backend* mg_metal_canvas_create(mg_surface surface); mg_canvas_backend* mg_mtl_canvas_create(mg_surface surface);
#endif #endif
#ifdef MG_IMPLEMENTS_BACKEND_GLES #ifdef MG_COMPILE_BACKEND_GL
mg_canvas_backend* mg_gles_canvas_create(mg_surface surface);
#endif
#ifdef MG_IMPLEMENTS_BACKEND_GL
mg_canvas_backend* mg_gl_canvas_create(mg_surface surface); mg_canvas_backend* mg_gl_canvas_create(mg_surface surface);
#endif #endif
mg_canvas mg_canvas_create(mg_surface surface) mg_canvas mg_canvas_create(mg_surface surface)
{ {
mg_canvas canvas = mg_canvas_nil(); mg_canvas canvas = mg_canvas_nil();
@ -2585,20 +2663,13 @@ mg_canvas mg_canvas_create(mg_surface surface)
mg_canvas_backend* backend = 0; mg_canvas_backend* backend = 0;
switch(surfaceData->backend) switch(surfaceData->backend)
{ {
#ifdef MG_IMPLEMENTS_BACKEND_METAL #ifdef MG_COMPILE_BACKEND_METAL
case MG_BACKEND_METAL: case MG_BACKEND_METAL:
backend = mg_metal_canvas_create(surface); backend = mg_mtl_canvas_create(surface);
break; break;
#endif #endif
/* #ifdef MG_COMPILE_BACKEND_GL
#ifdef MG_IMPLEMENTS_BACKEND_GLES
case MG_BACKEND_GLES:
backend = mg_gles_canvas_create(surface);
break;
#endif
*/
#ifdef MG_IMPLEMENTS_BACKEND_GL
case MG_BACKEND_GL: case MG_BACKEND_GL:
backend = mg_gl_canvas_create(surface); backend = mg_gl_canvas_create(surface);
break; break;

View File

@ -9,6 +9,72 @@
#ifndef __GRAPHICS_H_ #ifndef __GRAPHICS_H_
#define __GRAPHICS_H_ #define __GRAPHICS_H_
#include"typedefs.h"
#include"platform.h"
//------------------------------------------------------------------------------------------
//NOTE(martin): backends selection
//------------------------------------------------------------------------------------------
typedef enum {
MG_BACKEND_NONE,
MG_BACKEND_METAL,
MG_BACKEND_GL,
MG_BACKEND_GLES } mg_backend_id;
//NOTE: these macros are used to select which backend to include when building milepost
// they can be overridden by passing them to the compiler command line
#if defined(OS_MACOS)
#ifndef MG_COMPILE_BACKEND_METAL
#define MG_COMPILE_BACKEND_METAL 1
#endif
#ifndef MG_COMPILE_BACKEND_GL
#define MG_COMPILE_BACKEND_GL 1
#endif
#if MG_COMPILE_BACKEND_METAL
#define MG_BACKEND_DEFAULT MG_BACKEND_METAL
#elif MG_COMPILE_BACKEND_GL
#define MG_BACKEND_DEFAULT MG_BACKEND_GL
#else
#define MG_BACKEND_DEFAULT MG_BACKEND_NONE
#endif
#elif defined(OS_WIN64)
#ifndef MG_COMPILE_BACKEND_GL
#define MG_COMPILE_BACKEND_GL 1
#endif
#if MG_COMPILE_BACKEND_GL
#define MG_BACKEND_DEFAULT MG_BACKEND_GL
#else
#define MG_BACKEND_DEFAULT MG_BACKEND_NONE
#endif
#elif defined(OS_LINUX)
#ifndef MG_COMPILE_BACKEND_GL
#define MG_COMPILE_BACKEND_GL 1
#endif
#endif
//NOTE: these macros are used to select backend-specific APIs to include when using milepost
#ifdef MG_EXPOSE_SURFACE_METAL
#include"mtl_surface.h"
#endif
#ifdef MG_EXPOSE_SURFACE_WGL
#include"wgl_surface.h"
#endif
//TODO: expose nsgl surface when supported, expose egl surface, etc...
//TODO: add MG_INCLUDE_OPENGL/GLES/etc, once we know how we make different gl versions co-exist
bool mg_is_surface_backend_available(mg_backend_id id);
bool mg_is_canvas_backend_available(mg_backend_id id);
//------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------
//NOTE(martin): graphics surface //NOTE(martin): graphics surface
//------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------
@ -17,6 +83,7 @@ typedef struct mg_surface { u64 h; } mg_surface;
mg_surface mg_surface_nil(); mg_surface mg_surface_nil();
bool mg_surface_is_nil(mg_surface surface); bool mg_surface_is_nil(mg_surface surface);
mg_surface mg_surface_create_for_window(mp_window window, mg_backend_id backend);
void mg_surface_destroy(mg_surface surface); void mg_surface_destroy(mg_surface surface);
void mg_surface_prepare(mg_surface surface); void mg_surface_prepare(mg_surface surface);
void mg_surface_present(mg_surface surface); void mg_surface_present(mg_surface surface);

View File

@ -15,13 +15,6 @@
extern "C" { extern "C" {
#endif #endif
typedef enum { MG_BACKEND_DUMMY,
MG_BACKEND_METAL,
MG_BACKEND_GL,
MG_BACKEND_GLES,
//...
} mg_backend_id;
typedef struct mg_surface_data mg_surface_data; typedef struct mg_surface_data mg_surface_data;
typedef void (*mg_surface_destroy_proc)(mg_surface_data* surface); typedef void (*mg_surface_destroy_proc)(mg_surface_data* surface);

View File

@ -1,26 +0,0 @@
/************************************************************//**
*
* @file: metal_surface.h
* @author: Martin Fouilleul
* @date: 25/12/2022
* @revision:
*
*****************************************************************/
#ifndef __METAL_SURFACE_H_
#define __METAL_SURFACE_H_
#include"graphics.h"
#ifdef __OBJC__
#import<Metal/Metal.h>
#endif
mg_surface mg_metal_surface_create_for_window(mp_window window);
void* mg_metal_surface_render_encoder(mg_surface surface);
void* mg_metal_surface_compute_encoder(mg_surface surface);
void* mg_metal_surface_layer(mg_surface surface);
void* mg_metal_surface_drawable(mg_surface surface);
void* mg_metal_surface_command_buffer(mg_surface surface);
#endif //__METAL_SURFACE_H_

View File

@ -48,23 +48,21 @@
#endif #endif
//--------------------------------------------------------------- //---------------------------------------------------------------
// application layer // app/graphics layer
//--------------------------------------------------------------- //---------------------------------------------------------------
#if defined(OS_WIN64) #if defined(OS_WIN64)
#include"win32_app.c" #include"win32_app.c"
#include"win32_gl_surface.c" #include"graphics.c"
#include"gl_canvas.c"
// #include"win32_gles_surface.c" #if MG_COMPILE_BACKEND_GL
// #include"gles_canvas.c" #include"wgl_surface.c"
#include"gl_canvas.c"
#endif
#elif defined(OS_MACOS) #elif defined(OS_MACOS)
//NOTE: macos application layer is defined in milepost.m //NOTE: macos application layer and graphics backends are defined in milepost.m
#include"graphics.c"
#else #else
#error "Unsupported platform" #error "Unsupported platform"
#endif #endif
//---------------------------------------------------------------
// graphics/ui layer
//---------------------------------------------------------------
#include"graphics.c"
//#include"ui.c"

View File

@ -33,34 +33,17 @@
*/ */
//---------------------------------------------------------------- //----------------------------------------------------------------
// application layer // application/graphics layer
//---------------------------------------------------------------- //----------------------------------------------------------------
#include"mp_app.h" #include"mp_app.h"
#if defined(OS_WIN64) || defined(OS_WIN32)
#if MG_IMPLEMENTS_BACKEND_GLES
#include"win32_gles_surface.h"
#endif
#if MG_IMPLEMENTS_BACKEND_GL
#define WIN32_GL_LOADER_API
#include"win32_gl_loader.h"
#undef WIN32_GL_LOADER_API
#include"win32_gl_surface.h"
#endif
#endif
#if MG_IMPLEMENTS_BACKEND_METAL
#include"metal_surface.h"
#endif
//----------------------------------------------------------------
// graphics/ui layer
//----------------------------------------------------------------
#include"graphics.h" #include"graphics.h"
#if defined(OS_WIN64)
#define WIN32_GL_LOADER_API
#include"wgl_loader.h"
#undef WIN32_GL_LOADER_API
#endif
//#include"ui.h" //#include"ui.h"
#endif //__MILEPOST_H_ #endif //__MILEPOST_H_

View File

@ -1,19 +1,24 @@
/************************************************************//** /************************************************************//**
* *
* @file: milepost.m * @file: milepost.m
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 13/02/2021 * @date: 13/02/2021
* @revision: * @revision:
* *
*****************************************************************/ *****************************************************************/
#include"osx_app.m" #include"osx_app.m"
#include"metal_surface.m" #include"graphics.h"
#include"metal_canvas.m"
#if MG_COMPILE_BACKEND_METAL
#pragma clang diagnostic push #include"mtl_surface.m"
#pragma clang diagnostic ignored "-Wdeprecated-declarations" #include"mtl_canvas.m"
#include"osx_gles_surface.m" #endif
#pragma clang diagnostic pop
/*
//#include"osx_surface_client.m" #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include"osx_gles_surface.m"
#pragma clang diagnostic pop
*/
//#include"osx_surface_client.m"

View File

@ -1,6 +1,6 @@
/************************************************************//** /************************************************************//**
* *
* @file: metal_canvas.m * @file: mtl_canvas.m
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 12/07/2020 * @date: 12/07/2020
* @revision: 24/01/2023 * @revision: 24/01/2023
@ -14,13 +14,13 @@
#include"macro_helpers.h" #include"macro_helpers.h"
#include"osx_app.h" #include"osx_app.h"
#include"metal_shader.h" #include"mtl_shader.h"
#define LOG_SUBSYSTEM "Graphics" #define LOG_SUBSYSTEM "Graphics"
static const int MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH = 4<<20; static const int MG_MTL_CANVAS_DEFAULT_BUFFER_LENGTH = 4<<20;
typedef struct mg_metal_canvas_backend typedef struct mg_mtl_canvas_backend
{ {
mg_canvas_backend interface; mg_canvas_backend interface;
mg_surface surface; mg_surface surface;
@ -47,36 +47,36 @@ typedef struct mg_metal_canvas_backend
id<MTLBuffer> triangleArray; id<MTLBuffer> triangleArray;
id<MTLBuffer> boxArray; id<MTLBuffer> boxArray;
} mg_metal_canvas_backend; } mg_mtl_canvas_backend;
mg_metal_surface* mg_metal_canvas_get_surface(mg_metal_canvas_backend* canvas) mg_mtl_surface* mg_mtl_canvas_get_surface(mg_mtl_canvas_backend* canvas)
{ {
mg_metal_surface* res = 0; mg_mtl_surface* res = 0;
mg_surface_data* data = mg_surface_data_from_handle(canvas->surface); mg_surface_data* data = mg_surface_data_from_handle(canvas->surface);
if(data && data->backend == MG_BACKEND_METAL) if(data && data->backend == MG_BACKEND_METAL)
{ {
res = (mg_metal_surface*)data; res = (mg_mtl_surface*)data;
} }
return(res); return(res);
} }
void mg_metal_canvas_begin(mg_canvas_backend* interface) void mg_mtl_canvas_begin(mg_canvas_backend* interface)
{} {}
void mg_metal_canvas_end(mg_canvas_backend* interface) void mg_mtl_canvas_end(mg_canvas_backend* interface)
{} {}
void mg_metal_canvas_clear(mg_canvas_backend* interface, mg_color clearColor) void mg_mtl_canvas_clear(mg_canvas_backend* interface, mg_color clearColor)
{ {
//TODO //TODO
mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface; mg_mtl_canvas_backend* backend = (mg_mtl_canvas_backend*)interface;
backend->clearColor = clearColor; backend->clearColor = clearColor;
} }
void mg_metal_canvas_draw_batch(mg_canvas_backend* interface, u32 vertexCount, u32 indexCount, u32 shapeCount) void mg_mtl_canvas_draw_batch(mg_canvas_backend* interface, u32 vertexCount, u32 indexCount, u32 shapeCount)
{ {
mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface; mg_mtl_canvas_backend* backend = (mg_mtl_canvas_backend*)interface;
mg_metal_surface* surface = mg_metal_canvas_get_surface(backend); mg_mtl_surface* surface = mg_mtl_canvas_get_surface(backend);
if(!surface) if(!surface)
{ {
return; return;
@ -86,7 +86,7 @@ void mg_metal_canvas_draw_batch(mg_canvas_backend* interface, u32 vertexCount, u
{ {
if(surface->commandBuffer == nil || surface->commandBuffer == nil) if(surface->commandBuffer == nil || surface->commandBuffer == nil)
{ {
mg_metal_surface_acquire_drawable_and_command_buffer(surface); mg_mtl_surface_acquire_drawable_and_command_buffer(surface);
} }
ASSERT(indexCount * sizeof(i32) < [backend->indexBuffer length]); ASSERT(indexCount * sizeof(i32) < [backend->indexBuffer length]);
@ -205,10 +205,10 @@ void mg_metal_canvas_draw_batch(mg_canvas_backend* interface, u32 vertexCount, u
} }
/* /*
void mg_metal_canvas_viewport(mg_canvas_backend* interface, mp_rect viewPort) void mg_mtl_canvas_viewport(mg_canvas_backend* interface, mp_rect viewPort)
{ {
mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface; mg_mtl_canvas_backend* backend = (mg_mtl_canvas_backend*)interface;
mg_metal_surface* surface = mg_metal_canvas_get_surface(backend); mg_mtl_surface* surface = mg_mtl_canvas_get_surface(backend);
if(!surface) if(!surface)
{ {
return; return;
@ -236,15 +236,15 @@ void mg_metal_canvas_viewport(mg_canvas_backend* interface, mp_rect viewPort)
} }
*/ */
void mg_metal_canvas_update_vertex_layout(mg_metal_canvas_backend* backend) void mg_mtl_canvas_update_vertex_layout(mg_mtl_canvas_backend* backend)
{ {
char* vertexBase = (char*)[backend->vertexBuffer contents]; char* vertexBase = (char*)[backend->vertexBuffer contents];
char* shapeBase = (char*)[backend->shapeBuffer contents]; char* shapeBase = (char*)[backend->shapeBuffer contents];
char* indexBase = (char*)[backend->indexBuffer contents]; char* indexBase = (char*)[backend->indexBuffer contents];
backend->interface.vertexLayout = (mg_vertex_layout){ backend->interface.vertexLayout = (mg_vertex_layout){
.maxVertexCount = MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH, .maxVertexCount = MG_MTL_CANVAS_DEFAULT_BUFFER_LENGTH,
.maxIndexCount = MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH, .maxIndexCount = MG_MTL_CANVAS_DEFAULT_BUFFER_LENGTH,
.cubicBuffer = vertexBase + offsetof(mg_vertex, cubic), .cubicBuffer = vertexBase + offsetof(mg_vertex, cubic),
.cubicStride = sizeof(mg_vertex), .cubicStride = sizeof(mg_vertex),
.posBuffer = vertexBase + offsetof(mg_vertex, pos), .posBuffer = vertexBase + offsetof(mg_vertex, pos),
@ -263,9 +263,9 @@ void mg_metal_canvas_update_vertex_layout(mg_metal_canvas_backend* backend)
.indexStride = sizeof(int)}; .indexStride = sizeof(int)};
} }
void mg_metal_canvas_destroy(mg_canvas_backend* interface) void mg_mtl_canvas_destroy(mg_canvas_backend* interface)
{ {
mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface; mg_mtl_canvas_backend* backend = (mg_mtl_canvas_backend*)interface;
@autoreleasepool @autoreleasepool
{ {
@ -280,9 +280,9 @@ void mg_metal_canvas_destroy(mg_canvas_backend* interface)
} }
} }
void mg_metal_canvas_atlas_upload(mg_canvas_backend* interface, mp_rect rect, u8* bytes) void mg_mtl_canvas_atlas_upload(mg_canvas_backend* interface, mp_rect rect, u8* bytes)
{@autoreleasepool{ {@autoreleasepool{
mg_metal_canvas_backend* backend = (mg_metal_canvas_backend*)interface; mg_mtl_canvas_backend* backend = (mg_mtl_canvas_backend*)interface;
MTLRegion region = MTLRegionMake2D(rect.x, rect.y, rect.w, rect.h); MTLRegion region = MTLRegionMake2D(rect.x, rect.y, rect.w, rect.h);
[backend->atlasTexture replaceRegion:region [backend->atlasTexture replaceRegion:region
@ -291,25 +291,25 @@ void mg_metal_canvas_atlas_upload(mg_canvas_backend* interface, mp_rect rect, u8
bytesPerRow: 4 * rect.w]; bytesPerRow: 4 * rect.w];
}} }}
mg_canvas_backend* mg_metal_canvas_create(mg_surface surface) mg_canvas_backend* mg_mtl_canvas_create(mg_surface surface)
{ {
mg_metal_canvas_backend* backend = 0; mg_mtl_canvas_backend* backend = 0;
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL) if(surfaceData && surfaceData->backend == MG_BACKEND_METAL)
{ {
mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData; mg_mtl_surface* metalSurface = (mg_mtl_surface*)surfaceData;
backend = malloc_type(mg_metal_canvas_backend); backend = malloc_type(mg_mtl_canvas_backend);
backend->surface = surface; backend->surface = surface;
//NOTE(martin): setup interface functions //NOTE(martin): setup interface functions
backend->interface.destroy = mg_metal_canvas_destroy; backend->interface.destroy = mg_mtl_canvas_destroy;
backend->interface.begin = mg_metal_canvas_begin; backend->interface.begin = mg_mtl_canvas_begin;
backend->interface.end = mg_metal_canvas_end; backend->interface.end = mg_mtl_canvas_end;
backend->interface.clear = mg_metal_canvas_clear; backend->interface.clear = mg_mtl_canvas_clear;
backend->interface.drawBatch = mg_metal_canvas_draw_batch; backend->interface.drawBatch = mg_mtl_canvas_draw_batch;
backend->interface.atlasUpload = mg_metal_canvas_atlas_upload; backend->interface.atlasUpload = mg_mtl_canvas_atlas_upload;
mp_rect frame = mg_surface_get_frame(surface); mp_rect frame = mg_surface_get_frame(surface);
backend->viewPort = (mp_rect){0, 0, frame.w, frame.h}; backend->viewPort = (mp_rect){0, 0, frame.w, frame.h};
@ -352,22 +352,22 @@ mg_canvas_backend* mg_metal_canvas_create(mg_surface surface)
MTLResourceOptions bufferOptions = MTLResourceCPUCacheModeWriteCombined MTLResourceOptions bufferOptions = MTLResourceCPUCacheModeWriteCombined
| MTLResourceStorageModeShared; | MTLResourceStorageModeShared;
backend->indexBuffer = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(int) backend->indexBuffer = [metalSurface->device newBufferWithLength: MG_MTL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(int)
options: bufferOptions]; options: bufferOptions];
backend->vertexBuffer = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(mg_vertex) backend->vertexBuffer = [metalSurface->device newBufferWithLength: MG_MTL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(mg_vertex)
options: bufferOptions]; options: bufferOptions];
backend->shapeBuffer = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(mg_shape) backend->shapeBuffer = [metalSurface->device newBufferWithLength: MG_MTL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(mg_shape)
options: bufferOptions]; options: bufferOptions];
backend->tilesArray = [metalSurface->device newBufferWithLength: RENDERER_TILE_BUFFER_SIZE*sizeof(int)*RENDERER_MAX_TILES backend->tilesArray = [metalSurface->device newBufferWithLength: RENDERER_TILE_BUFFER_SIZE*sizeof(int)*RENDERER_MAX_TILES
options: MTLResourceStorageModePrivate]; options: MTLResourceStorageModePrivate];
backend->triangleArray = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(mg_triangle_data) backend->triangleArray = [metalSurface->device newBufferWithLength: MG_MTL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(mg_triangle_data)
options: MTLResourceStorageModePrivate]; options: MTLResourceStorageModePrivate];
backend->boxArray = [metalSurface->device newBufferWithLength: MG_METAL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(vector_float4) backend->boxArray = [metalSurface->device newBufferWithLength: MG_MTL_CANVAS_DEFAULT_BUFFER_LENGTH*sizeof(vector_float4)
options: MTLResourceStorageModePrivate]; options: MTLResourceStorageModePrivate];
//TODO(martin): retain ? //TODO(martin): retain ?
@ -387,7 +387,7 @@ mg_canvas_backend* mg_metal_canvas_create(mg_surface surface)
//----------------------------------------------------------- //-----------------------------------------------------------
//TODO(martin): filepath magic to find metallib path when not in the working directory //TODO(martin): filepath magic to find metallib path when not in the working directory
str8 shaderPath = mp_app_get_resource_path(mem_scratch(), "../resources/metal_shader.metallib"); str8 shaderPath = mp_app_get_resource_path(mem_scratch(), "../resources/mtl_shader.metallib");
NSString* metalFileName = [[NSString alloc] initWithBytes: shaderPath.ptr length:shaderPath.len encoding: NSUTF8StringEncoding]; NSString* metalFileName = [[NSString alloc] initWithBytes: shaderPath.ptr length:shaderPath.len encoding: NSUTF8StringEncoding];
NSError* err = 0; NSError* err = 0;
id<MTLLibrary> library = [metalSurface->device newLibraryWithFile: metalFileName error:&err]; id<MTLLibrary> library = [metalSurface->device newLibraryWithFile: metalFileName error:&err];
@ -460,7 +460,7 @@ mg_canvas_backend* mg_metal_canvas_create(mg_surface surface)
} }
} }
mg_metal_canvas_update_vertex_layout(backend); mg_mtl_canvas_update_vertex_layout(backend);
} }
return((mg_canvas_backend*)backend); return((mg_canvas_backend*)backend);

View File

@ -1,13 +1,13 @@
/************************************************************//** /************************************************************//**
* *
* @file: metal_shader.h * @file: mtl_shader.h
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 01/08/2022 * @date: 01/08/2022
* @revision: * @revision:
* *
*****************************************************************/ *****************************************************************/
#ifndef __METAL_RENDERER_H_ #ifndef __MTL_RENDERER_H_
#define __METAL_RENDERER_H_ #define __MTL_RENDERER_H_
#include<simd/simd.h> #include<simd/simd.h>
@ -50,4 +50,4 @@ typedef struct mg_triangle_data
} mg_triangle_data; } mg_triangle_data;
#endif //__METAL_RENDERER_H_ #endif //__MTL_RENDERER_H_

View File

@ -2,7 +2,7 @@
#include<metal_stdlib> #include<metal_stdlib>
#include<simd/simd.h> #include<simd/simd.h>
#include"metal_shader.h" #include"mtl_shader.h"
using namespace metal; using namespace metal;

26
src/mtl_surface.h Normal file
View File

@ -0,0 +1,26 @@
/************************************************************//**
*
* @file: mtl_surface.h
* @author: Martin Fouilleul
* @date: 25/12/2022
* @revision:
*
*****************************************************************/
#ifndef __MTL_SURFACE_H_
#define __MTL_SURFACE_H_
#include"graphics.h"
#ifdef __OBJC__
#import<Mtl/Metal.h>
#endif
mg_surface mg_mtl_surface_create_for_window(mp_window window);
void* mg_mtl_surface_render_encoder(mg_surface surface);
void* mg_mtl_surface_compute_encoder(mg_surface surface);
void* mg_mtl_surface_layer(mg_surface surface);
void* mg_mtl_surface_drawable(mg_surface surface);
void* mg_mtl_surface_command_buffer(mg_surface surface);
#endif //__MTL_SURFACE_H_

View File

@ -17,16 +17,16 @@
#define LOG_SUBSYSTEM "Graphics" #define LOG_SUBSYSTEM "Graphics"
static const u32 MP_METAL_MAX_DRAWABLES_IN_FLIGHT = 3; static const u32 MP_MTL_MAX_DRAWABLES_IN_FLIGHT = 3;
typedef struct mg_metal_surface typedef struct mg_mtl_surface
{ {
mg_surface_data interface; mg_surface_data interface;
// permanent metal resources // permanent mtl resources
NSView* view; NSView* view;
id<MTLDevice> device; id<MTLDevice> device;
CAMetalLayer* metalLayer; CAMetalLayer* mtlLayer;
id<MTLCommandQueue> commandQueue; id<MTLCommandQueue> commandQueue;
// transient metal resources // transient metal resources
@ -35,21 +35,21 @@ typedef struct mg_metal_surface
dispatch_semaphore_t drawableSemaphore; dispatch_semaphore_t drawableSemaphore;
} mg_metal_surface; } mg_mtl_surface;
void mg_metal_surface_destroy(mg_surface_data* interface) void mg_mtl_surface_destroy(mg_surface_data* interface)
{ {
mg_metal_surface* surface = (mg_metal_surface*)interface; mg_mtl_surface* surface = (mg_mtl_surface*)interface;
@autoreleasepool @autoreleasepool
{ {
[surface->commandQueue release]; [surface->commandQueue release];
[surface->metalLayer release]; [surface->mtlLayer release];
[surface->device release]; [surface->device release];
} }
} }
void mg_metal_surface_acquire_drawable_and_command_buffer(mg_metal_surface* surface) void mg_mtl_surface_acquire_drawable_and_command_buffer(mg_mtl_surface* surface)
{@autoreleasepool{ {@autoreleasepool{
/*WARN(martin): this is super important /*WARN(martin): this is super important
When the app is put in the background, it seems that if there are buffers in flight, the drawables to When the app is put in the background, it seems that if there are buffers in flight, the drawables to
@ -76,7 +76,7 @@ void mg_metal_surface_acquire_drawable_and_command_buffer(mg_metal_surface* surf
*/ */
dispatch_semaphore_wait(surface->drawableSemaphore, DISPATCH_TIME_FOREVER); dispatch_semaphore_wait(surface->drawableSemaphore, DISPATCH_TIME_FOREVER);
surface->drawable = [surface->metalLayer nextDrawable]; surface->drawable = [surface->mtlLayer nextDrawable];
ASSERT(surface->drawable != nil); ASSERT(surface->drawable != nil);
//TODO: make this a weak reference if we use ARC //TODO: make this a weak reference if we use ARC
@ -92,10 +92,10 @@ void mg_metal_surface_acquire_drawable_and_command_buffer(mg_metal_surface* surf
[surface->drawable retain]; [surface->drawable retain];
}} }}
void mg_metal_surface_prepare(mg_surface_data* interface) void mg_mtl_surface_prepare(mg_surface_data* interface)
{ {
mg_metal_surface* surface = (mg_metal_surface*)interface; mg_mtl_surface* surface = (mg_mtl_surface*)interface;
mg_metal_surface_acquire_drawable_and_command_buffer(surface); mg_mtl_surface_acquire_drawable_and_command_buffer(surface);
@autoreleasepool @autoreleasepool
{ {
@ -109,9 +109,9 @@ void mg_metal_surface_prepare(mg_surface_data* interface)
} }
} }
void mg_metal_surface_present(mg_surface_data* interface) void mg_mtl_surface_present(mg_surface_data* interface)
{ {
mg_metal_surface* surface = (mg_metal_surface*)interface; mg_mtl_surface* surface = (mg_mtl_surface*)interface;
@autoreleasepool @autoreleasepool
{ {
//NOTE(martin): present drawable and commit command buffer //NOTE(martin): present drawable and commit command buffer
@ -127,32 +127,32 @@ void mg_metal_surface_present(mg_surface_data* interface)
} }
} }
void mg_metal_surface_swap_interval(mg_surface_data* interface, int swap) void mg_mtl_surface_swap_interval(mg_surface_data* interface, int swap)
{ {
mg_metal_surface* surface = (mg_metal_surface*)interface; mg_mtl_surface* surface = (mg_mtl_surface*)interface;
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
//TODO //TODO
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
} }
void mg_metal_surface_set_frame(mg_surface_data* interface, mp_rect frame) void mg_mtl_surface_set_frame(mg_surface_data* interface, mp_rect frame)
{ {
mg_metal_surface* surface = (mg_metal_surface*)interface; mg_mtl_surface* surface = (mg_mtl_surface*)interface;
@autoreleasepool @autoreleasepool
{ {
CGRect cgFrame = {{frame.x, frame.y}, {frame.w, frame.h}}; CGRect cgFrame = {{frame.x, frame.y}, {frame.w, frame.h}};
[surface->view setFrame: cgFrame]; [surface->view setFrame: cgFrame];
f32 scale = surface->metalLayer.contentsScale; f32 scale = surface->mtlLayer.contentsScale;
CGSize drawableSize = (CGSize){.width = frame.w * scale, .height = frame.h * scale}; CGSize drawableSize = (CGSize){.width = frame.w * scale, .height = frame.h * scale};
surface->metalLayer.drawableSize = drawableSize; surface->mtlLayer.drawableSize = drawableSize;
} }
} }
mp_rect mg_metal_surface_get_frame(mg_surface_data* interface) mp_rect mg_mtl_surface_get_frame(mg_surface_data* interface)
{ {
mg_metal_surface* surface = (mg_metal_surface*)interface; mg_mtl_surface* surface = (mg_mtl_surface*)interface;
@autoreleasepool @autoreleasepool
{ {
@ -161,31 +161,31 @@ mp_rect mg_metal_surface_get_frame(mg_surface_data* interface)
} }
} }
void mg_metal_surface_set_hidden(mg_surface_data* interface, bool hidden) void mg_mtl_surface_set_hidden(mg_surface_data* interface, bool hidden)
{ {
mg_metal_surface* surface = (mg_metal_surface*)interface; mg_mtl_surface* surface = (mg_mtl_surface*)interface;
@autoreleasepool @autoreleasepool
{ {
[CATransaction begin]; [CATransaction begin];
[CATransaction setDisableActions:YES]; [CATransaction setDisableActions:YES];
[surface->metalLayer setHidden:hidden]; [surface->mtlLayer setHidden:hidden];
[CATransaction commit]; [CATransaction commit];
} }
} }
bool mg_metal_surface_get_hidden(mg_surface_data* interface) bool mg_mtl_surface_get_hidden(mg_surface_data* interface)
{ {
mg_metal_surface* surface = (mg_metal_surface*)interface; mg_mtl_surface* surface = (mg_mtl_surface*)interface;
@autoreleasepool @autoreleasepool
{ {
return([surface->metalLayer isHidden]); return([surface->mtlLayer isHidden]);
} }
} }
//TODO fix that according to real scaling, depending on the monitor settings //TODO fix that according to real scaling, depending on the monitor settings
static const f32 MG_METAL_SURFACE_CONTENTS_SCALING = 2; static const f32 MG_MTL_SURFACE_CONTENTS_SCALING = 2;
mg_surface mg_metal_surface_create_for_window(mp_window window) mg_surface mg_mtl_surface_create_for_window(mp_window window)
{ {
mp_window_data* windowData = mp_window_ptr_from_handle(window); mp_window_data* windowData = mp_window_ptr_from_handle(window);
if(!windowData) if(!windowData)
@ -194,18 +194,18 @@ mg_surface mg_metal_surface_create_for_window(mp_window window)
} }
else else
{ {
mg_metal_surface* surface = (mg_metal_surface*)malloc(sizeof(mg_metal_surface)); mg_mtl_surface* surface = (mg_mtl_surface*)malloc(sizeof(mg_mtl_surface));
//NOTE(martin): setup interface functions //NOTE(martin): setup interface functions
surface->interface.backend = MG_BACKEND_METAL; surface->interface.backend = MG_BACKEND_MTL;
surface->interface.destroy = mg_metal_surface_destroy; surface->interface.destroy = mg_mtl_surface_destroy;
surface->interface.prepare = mg_metal_surface_prepare; surface->interface.prepare = mg_mtl_surface_prepare;
surface->interface.present = mg_metal_surface_present; surface->interface.present = mg_mtl_surface_present;
surface->interface.swapInterval = mg_metal_surface_swap_interval; surface->interface.swapInterval = mg_mtl_surface_swap_interval;
surface->interface.getFrame = mg_metal_surface_get_frame; surface->interface.getFrame = mg_mtl_surface_get_frame;
surface->interface.setFrame = mg_metal_surface_set_frame; surface->interface.setFrame = mg_mtl_surface_set_frame;
surface->interface.getHidden = mg_metal_surface_get_hidden; surface->interface.getHidden = mg_mtl_surface_get_hidden;
surface->interface.setHidden = mg_metal_surface_set_hidden; surface->interface.setHidden = mg_mtl_surface_set_hidden;
@autoreleasepool @autoreleasepool
{ {
@ -215,37 +215,37 @@ mg_surface mg_metal_surface_create_for_window(mp_window window)
[[windowData->osx.nsWindow contentView] addSubview: surface->view]; [[windowData->osx.nsWindow contentView] addSubview: surface->view];
surface->drawableSemaphore = dispatch_semaphore_create(MP_METAL_MAX_DRAWABLES_IN_FLIGHT); surface->drawableSemaphore = dispatch_semaphore_create(MP_MTL_MAX_DRAWABLES_IN_FLIGHT);
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): create a metal device and a metal layer and //NOTE(martin): create a mtl device and a mtl layer and
//----------------------------------------------------------- //-----------------------------------------------------------
surface->device = MTLCreateSystemDefaultDevice(); surface->device = MTLCreateSystemDefaultDevice();
[surface->device retain]; [surface->device retain];
surface->metalLayer = [CAMetalLayer layer]; surface->mtlLayer = [CAMtlLayer layer];
[surface->metalLayer retain]; [surface->mtlLayer retain];
[surface->metalLayer setOpaque:NO]; [surface->mtlLayer setOpaque:NO];
surface->metalLayer.device = surface->device; surface->mtlLayer.device = surface->device;
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): set the size and scaling //NOTE(martin): set the size and scaling
//----------------------------------------------------------- //-----------------------------------------------------------
CGSize size = surface->view.bounds.size; CGSize size = surface->view.bounds.size;
size.width *= MG_METAL_SURFACE_CONTENTS_SCALING; size.width *= MG_MTL_SURFACE_CONTENTS_SCALING;
size.height *= MG_METAL_SURFACE_CONTENTS_SCALING; size.height *= MG_MTL_SURFACE_CONTENTS_SCALING;
surface->metalLayer.drawableSize = size; surface->mtlLayer.drawableSize = size;
surface->metalLayer.contentsScale = MG_METAL_SURFACE_CONTENTS_SCALING; surface->mtlLayer.contentsScale = MG_MTL_SURFACE_CONTENTS_SCALING;
surface->metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm; surface->mtlLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
surface->view.wantsLayer = YES; surface->view.wantsLayer = YES;
surface->view.layer = surface->metalLayer; surface->view.layer = surface->mtlLayer;
//NOTE(martin): handling resizing //NOTE(martin): handling resizing
surface->view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize; surface->view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
surface->metalLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable; surface->mtlLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable;
surface->metalLayer.needsDisplayOnBoundsChange = YES; surface->mtlLayer.needsDisplayOnBoundsChange = YES;
//----------------------------------------------------------- //-----------------------------------------------------------
//NOTE(martin): create a command queue //NOTE(martin): create a command queue
@ -263,13 +263,13 @@ mg_surface mg_metal_surface_create_for_window(mp_window window)
} }
} }
void* mg_metal_surface_layer(mg_surface surface) void* mg_mtl_surface_layer(mg_surface surface)
{ {
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL) if(surfaceData && surfaceData->backend == MG_BACKEND_MTL)
{ {
mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData; mg_mtl_surface* mtlSurface = (mg_mtl_surface*)surfaceData;
return(metalSurface->metalLayer); return(mtlSurface->mtlLayer);
} }
else else
{ {
@ -277,13 +277,13 @@ void* mg_metal_surface_layer(mg_surface surface)
} }
} }
void* mg_metal_surface_drawable(mg_surface surface) void* mg_mtl_surface_drawable(mg_surface surface)
{ {
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL) if(surfaceData && surfaceData->backend == MG_BACKEND_MTL)
{ {
mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData; mg_mtl_surface* mtlSurface = (mg_mtl_surface*)surfaceData;
return(metalSurface->drawable); return(mtlSurface->drawable);
} }
else else
{ {
@ -291,13 +291,13 @@ void* mg_metal_surface_drawable(mg_surface surface)
} }
} }
void* mg_metal_surface_command_buffer(mg_surface surface) void* mg_mtl_surface_command_buffer(mg_surface surface)
{ {
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData && surfaceData->backend == MG_BACKEND_METAL) if(surfaceData && surfaceData->backend == MG_BACKEND_MTL)
{ {
mg_metal_surface* metalSurface = (mg_metal_surface*)surfaceData; mg_mtl_surface* mtlSurface = (mg_mtl_surface*)surfaceData;
return(metalSurface->commandBuffer); return(mtlSurface->commandBuffer);
} }
else else
{ {

View File

@ -1,32 +0,0 @@
/************************************************************//**
*
* @file: osx_gles_surface.cpp
* @author: Martin Fouilleul
* @date: 18/08/2022
* @revision:
*
*****************************************************************/
//#include<Cocoa/Cocoa.h>
//#include <Foundation/Foundation.h>
#include <QuartzCore/QuartzCore.h>
#include<GLES3/gl3.h>
#define EGL_EGLEXT_PROTOTYPES
#include<EGL/egl.h>
#include<EGL/eglext.h>
#include"graphics_internal.h"
typedef struct mg_gles_surface
{
mg_surface_data interface;
NSView* view;
CALayer* layer;
EGLDisplay eglDisplay;
EGLConfig eglConfig;
EGLContext eglContext;
EGLSurface eglSurface;
EGLint numConfigs;
} mg_gles_surface;

View File

@ -1,184 +0,0 @@
/************************************************************//**
*
* @file: osx_gles_surface.cpp
* @author: Martin Fouilleul
* @date: 18/08/2022
* @revision:
*
*****************************************************************/
//#include<Cocoa/Cocoa.h>
//#include <Foundation/Foundation.h>
#include <QuartzCore/QuartzCore.h>
#include<GLES3/gl3.h>
#define EGL_EGLEXT_PROTOTYPES
#include<EGL/egl.h>
#include<EGL/eglext.h>
#include"graphics_internal.h"
#include"osx_gles_surface.h"
void mg_gles_surface_destroy(mg_surface_data* interface)
{
//////////////////////////////////////////////////
//TODO
//////////////////////////////////////////////////
}
void mg_gles_surface_prepare(mg_surface_data* interface)
{
mg_gles_surface* surface = (mg_gles_surface*)interface;
eglMakeCurrent(surface->eglDisplay, surface->eglSurface, surface->eglSurface, surface->eglContext);
}
void mg_gles_surface_present(mg_surface_data* interface)
{
//TODO: eglSwapBuffers seem to never block in macOS (ie eglSwapInterval doesn't seem to have any effect)
// We need to use a CVDisplayLink to time this if we want surface present to block
mg_gles_surface* surface = (mg_gles_surface*)interface;
eglSwapBuffers(surface->eglDisplay, surface->eglSurface);
}
void mg_gles_surface_set_frame(mg_surface_data* interface, mp_rect frame)
{
mg_gles_surface* surface = (mg_gles_surface*)interface;
f32 scale = surface->layer.contentsScale;
CGRect bounds = (CGRect){{frame.x * scale, frame.y * scale}, {.width = frame.w*scale, .height = frame.h*scale}};
[surface->layer setBounds: bounds];
}
mp_rect mg_gles_surface_get_frame(mg_surface_data* interface)
{
mg_gles_surface* surface = (mg_gles_surface*)interface;
f32 scale = surface->layer.contentsScale;
CGRect bounds = [surface->layer bounds];
mp_rect rect = {bounds.origin.x / scale, bounds.origin.y / scale, bounds.size.width / scale, bounds.size.height / scale};
return(rect);
}
void mg_gles_surface_set_hidden(mg_surface_data* interface, bool hidden)
{
//TODO: doesn't make sense for an offscreen surface?
}
bool mg_gles_surface_get_hidden(mg_surface_data* interface)
{
//TODO: doesn't make sense for an offscreen surface?
return(false);
}
vec2 mg_gles_surface_get_size(mg_surface_data* interface)
{
mg_gles_surface* surface = (mg_gles_surface*)interface;
CGRect bounds = [surface->layer bounds];
f32 scale = surface->layer.contentsScale;
vec2 res = {bounds.size.width/scale, bounds.size.height/scale};
return(res);
}
mg_surface mg_gles_surface_create_with_view(u32 width, u32 height, NSView* view)
{
mg_gles_surface* surface = malloc_type(mg_gles_surface);
memset(surface, 0, sizeof(mg_gles_surface));
surface->interface.backend = MG_BACKEND_GLES;
surface->interface.destroy = mg_gles_surface_destroy;
surface->interface.prepare = mg_gles_surface_prepare;
surface->interface.present = mg_gles_surface_present;
surface->interface.getFrame = mg_gles_surface_get_frame;
surface->interface.setFrame = mg_gles_surface_set_frame;
surface->interface.getHidden = mg_gles_surface_get_hidden;
surface->interface.setHidden = mg_gles_surface_set_hidden;
surface->view = view;
@autoreleasepool
{
surface->layer = [[CALayer alloc] init];
[surface->layer retain];
[surface->layer setBounds:CGRectMake(0, 0, width, height)];
if(surface->view)
{
[surface->view setWantsLayer: YES];
surface->view.layer = surface->layer;
}
}
EGLAttrib displayAttribs[] = {
//NOTE: we need to explicitly set EGL_PLATFORM_ANGLE_TYPE_ANGLE to EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE, because
// EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE defaults to using CGL, and eglSetSwapInterval is broken for this backend
EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE,
EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
EGL_NONE};
surface->eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE, (void*)EGL_DEFAULT_DISPLAY, displayAttribs);
eglInitialize(surface->eglDisplay, NULL, NULL);
EGLint const configAttributes[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 24,
EGL_STENCIL_SIZE, 8,
EGL_SAMPLE_BUFFERS, 0,
EGL_SAMPLES, EGL_DONT_CARE,
EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FIXED_EXT,
EGL_NONE };
eglChooseConfig(surface->eglDisplay, configAttributes, &surface->eglConfig, 1, &surface->numConfigs);
EGLint const surfaceAttributes[] = {EGL_NONE};
surface->eglSurface = eglCreateWindowSurface(surface->eglDisplay, surface->eglConfig, surface->layer, surfaceAttributes);
eglBindAPI(EGL_OPENGL_ES_API);
EGLint contextAttributes[] = {
EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
EGL_CONTEXT_MINOR_VERSION_KHR, 0, //NOTE: Angle can't create a GLES 3.1 context on macOS
EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM, EGL_TRUE,
EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE, EGL_TRUE,
EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE, EGL_FALSE,
EGL_NONE};
surface->eglContext = eglCreateContext(surface->eglDisplay, surface->eglConfig, EGL_NO_CONTEXT, contextAttributes);
eglMakeCurrent(surface->eglDisplay, surface->eglSurface, surface->eglSurface, surface->eglContext);
eglSwapInterval(surface->eglDisplay, 1);
mg_surface handle = mg_surface_alloc_handle((mg_surface_data*)surface);
return(handle);
}
mg_surface mg_gles_surface_create_offscreen(u32 width, u32 height)
{
return(mg_gles_surface_create_with_view(width, height, 0));
}
mg_surface mg_gles_surface_create_for_window(mp_window window)
{
mg_surface res = mg_surface_nil();
mp_window_data* windowData = mp_window_ptr_from_handle(window);
if(windowData)
{
@autoreleasepool
{
NSRect frame = [[windowData->osx.nsWindow contentView] frame];
NSView* view = [[NSView alloc] initWithFrame: frame];
res = mg_gles_surface_create_with_view(frame.size.width, frame.size.height, view);
if(!mg_surface_is_nil(res))
{
[[windowData->osx.nsWindow contentView] addSubview: view];
}
}
}
return(res);
}

View File

@ -1,6 +1,6 @@
/************************************************************//** /************************************************************//**
* *
* @file: win32_gl_loader.h * @file: wgl_loader.h
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 01/08/2022 * @date: 01/08/2022
* @revision: * @revision:
@ -56,7 +56,7 @@
GL_PROC(GLUNMAPBUFFER, glUnmapBuffer) GL_PROC(GLUNMAPBUFFER, glUnmapBuffer)
#ifdef WIN32_GL_LOADER_API #ifdef WGL_LOADER_API
//NOTE: pointer declarations //NOTE: pointer declarations
#define GL_PROC(type, name) extern _cat3_(PFN, type, PROC) name; #define GL_PROC(type, name) extern _cat3_(PFN, type, PROC) name;
GL_PROC_LIST GL_PROC_LIST
@ -64,7 +64,7 @@
#endif #endif
#ifdef WIN32_GL_LOADER_IMPL #ifdef WGL_LOADER_IMPL
#define GL_PROC(type, name) _cat3_(PFN, type, PROC) name = 0; #define GL_PROC(type, name) _cat3_(PFN, type, PROC) name = 0;
GL_PROC_LIST GL_PROC_LIST
#undef GL_PROC #undef GL_PROC

View File

@ -1,18 +1,18 @@
/************************************************************//** /************************************************************//**
* *
* @file: win32_gl_surface.c * @file: wgl_surface.c
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 01/08/2022 * @date: 01/08/2022
* @revision: * @revision:
* *
*****************************************************************/ *****************************************************************/
#define WIN32_GL_LOADER_IMPL #define WGL_LOADER_IMPL
#include"win32_gl_loader.h" #include"wgl_loader.h"
#include"win32_app.h" #include"win32_app.h"
#include"graphics_internal.h" #include"graphics_internal.h"
typedef struct mg_gl_surface typedef struct mg_wgl_surface
{ {
mg_surface_data interface; mg_surface_data interface;
@ -21,11 +21,11 @@ typedef struct mg_gl_surface
HGLRC glContext; HGLRC glContext;
vec2 contentsScaling; vec2 contentsScaling;
} mg_gl_surface; } mg_wgl_surface;
void mg_gl_surface_destroy(mg_surface_data* interface) void mg_wgl_surface_destroy(mg_surface_data* interface)
{ {
mg_gl_surface* surface = (mg_gl_surface*)interface; mg_wgl_surface* surface = (mg_wgl_surface*)interface;
if(surface->glContext == wglGetCurrentContext()) if(surface->glContext == wglGetCurrentContext())
{ {
@ -35,34 +35,34 @@ void mg_gl_surface_destroy(mg_surface_data* interface)
free(surface); free(surface);
} }
void mg_gl_surface_prepare(mg_surface_data* interface) void mg_wgl_surface_prepare(mg_surface_data* interface)
{ {
mg_gl_surface* surface = (mg_gl_surface*)interface; mg_wgl_surface* surface = (mg_wgl_surface*)interface;
wglMakeCurrent(surface->hDC, surface->glContext); wglMakeCurrent(surface->hDC, surface->glContext);
} }
void mg_gl_surface_present(mg_surface_data* interface) void mg_wgl_surface_present(mg_surface_data* interface)
{ {
mg_gl_surface* surface = (mg_gl_surface*)interface; mg_wgl_surface* surface = (mg_wgl_surface*)interface;
SwapBuffers(surface->hDC); SwapBuffers(surface->hDC);
} }
void mg_gl_surface_swap_interval(mg_surface_data* interface, int swap) void mg_wgl_surface_swap_interval(mg_surface_data* interface, int swap)
{ {
mg_gl_surface* surface = (mg_gl_surface*)interface; mg_wgl_surface* surface = (mg_wgl_surface*)interface;
wglSwapIntervalEXT(swap); wglSwapIntervalEXT(swap);
} }
vec2 mg_gl_contents_scaling(mg_surface_data* interface) vec2 mg_wgl_contents_scaling(mg_surface_data* interface)
{ {
mg_gl_surface* surface = (mg_gl_surface*)interface; mg_wgl_surface* surface = (mg_wgl_surface*)interface;
return(surface->contentsScaling); return(surface->contentsScaling);
} }
mp_rect mg_gl_surface_get_frame(mg_surface_data* interface) mp_rect mg_wgl_surface_get_frame(mg_surface_data* interface)
{ {
mg_gl_surface* surface = (mg_gl_surface*)interface; mg_wgl_surface* surface = (mg_wgl_surface*)interface;
RECT rect = {0}; RECT rect = {0};
GetClientRect(surface->hWnd, &rect); GetClientRect(surface->hWnd, &rect);
@ -75,7 +75,7 @@ mp_rect mg_gl_surface_get_frame(mg_surface_data* interface)
return(res); return(res);
} }
mg_surface mg_gl_surface_create_for_window(mp_window window) mg_surface mg_wgl_surface_create_for_window(mp_window window)
{ {
mg_surface surfaceHandle = mg_surface_nil(); mg_surface surfaceHandle = mg_surface_nil();
@ -211,14 +211,14 @@ mg_surface mg_gl_surface_create_for_window(mp_window window)
wglSwapIntervalEXT(1); wglSwapIntervalEXT(1);
//TODO save important info in surface_data and return a handle //TODO save important info in surface_data and return a handle
mg_gl_surface* surface = malloc_type(mg_gl_surface); mg_wgl_surface* surface = malloc_type(mg_wgl_surface);
surface->interface.backend = MG_BACKEND_GL; surface->interface.backend = MG_BACKEND_GL;
surface->interface.destroy = mg_gl_surface_destroy; surface->interface.destroy = mg_wgl_surface_destroy;
surface->interface.prepare = mg_gl_surface_prepare; surface->interface.prepare = mg_wgl_surface_prepare;
surface->interface.present = mg_gl_surface_present; surface->interface.present = mg_wgl_surface_present;
surface->interface.swapInterval = mg_gl_surface_swap_interval; surface->interface.swapInterval = mg_wgl_surface_swap_interval;
surface->interface.contentsScaling = mg_gl_contents_scaling; surface->interface.contentsScaling = mg_wgl_contents_scaling;
surface->interface.getFrame = mg_gl_surface_get_frame; surface->interface.getFrame = mg_wgl_surface_get_frame;
//TODO: get/set frame/hidden //TODO: get/set frame/hidden
surface->hWnd = windowData->win32.hWnd; surface->hWnd = windowData->win32.hWnd;

View File

@ -1,16 +1,16 @@
/************************************************************//** /************************************************************//**
* *
* @file: win32_gl_surface.c * @file: wgl_surface.c
* @author: Martin Fouilleul * @author: Martin Fouilleul
* @date: 28/01/2023 * @date: 28/01/2023
* @revision: * @revision:
* *
*****************************************************************/ *****************************************************************/
#ifndef __WIN32_GL_SURFACE_H_ #ifndef __WGL_SURFACE_H_
#define __WIN32_GL_SURFACE_H_ #define __WGL_SURFACE_H_
#include"graphics.h" #include"graphics.h"
mg_surface mg_gl_surface_create_for_window(mp_window window); mg_surface mg_wgl_surface_create_for_window(mp_window window);
#endif // __WIN32_GL_SURFACE_H_ #endif // __WIN32_GL_SURFACE_H_

View File

@ -1,133 +0,0 @@
/************************************************************//**
*
* @file: osx_gles_surface.cpp
* @author: Martin Fouilleul
* @date: 18/08/2022
* @revision:
*
*****************************************************************/
#include<GLES3/gl31.h>
#define EGL_EGLEXT_PROTOTYPES
#include<EGL/egl.h>
#include<EGL/eglext.h>
#include"graphics_internal.h"
typedef struct mg_gles_surface
{
mg_surface_data interface;
HWND hWnd;
EGLDisplay eglDisplay;
EGLConfig eglConfig;
EGLContext eglContext;
EGLSurface eglSurface;
} mg_gles_surface;
void mg_gles_surface_destroy(mg_surface_data* interface)
{
//////////////////////////////////////////////////
//TODO
//////////////////////////////////////////////////
}
void mg_gles_surface_prepare(mg_surface_data* interface)
{
mg_gles_surface* surface = (mg_gles_surface*)interface;
eglMakeCurrent(surface->eglDisplay, surface->eglSurface, surface->eglSurface, surface->eglContext);
}
void mg_gles_surface_present(mg_surface_data* interface)
{
mg_gles_surface* surface = (mg_gles_surface*)interface;
eglSwapBuffers(surface->eglDisplay, surface->eglSurface);
}
/*
void mg_gles_surface_set_frame(mg_surface_data* interface, mp_rect frame);
void mg_gles_surface_set_hidden(mg_surface_data* interface, bool hidden);
bool mg_gles_surface_get_hidden(mg_surface_data* interface);
*/
mp_rect mg_gles_surface_get_frame(mg_surface_data* interface)
{
mg_gles_surface* surface = (mg_gles_surface*)interface;
RECT rect = {0};
GetClientRect(surface->hWnd, &rect);
mp_rect res = {rect.left, rect.bottom, rect.right - rect.left, rect.bottom - rect.top};
return(res);
}
mg_surface mg_gles_surface_create_for_window(mp_window window)
{
mg_surface res = mg_surface_nil();
mp_window_data* windowData = mp_window_ptr_from_handle(window);
if(windowData)
{
mg_gles_surface* surface = malloc_type(mg_gles_surface);
memset(surface, 0, sizeof(mg_gles_surface));
surface->interface.backend = MG_BACKEND_GLES;
surface->interface.destroy = mg_gles_surface_destroy;
surface->interface.prepare = mg_gles_surface_prepare;
surface->interface.present = mg_gles_surface_present;
surface->interface.getFrame = mg_gles_surface_get_frame;
/*TODO
surface->interface.setFrame = mg_gles_surface_set_frame;
surface->interface.getHidden = mg_gles_surface_get_hidden;
surface->interface.setHidden = mg_gles_surface_set_hidden;
*/
surface->hWnd = windowData->win32.hWnd;
EGLAttrib displayAttribs[] = {
EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE,
EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
EGL_NONE};
surface->eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE, (void*)EGL_DEFAULT_DISPLAY, displayAttribs);
eglInitialize(surface->eglDisplay, NULL, NULL);
EGLint const configAttributes[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 24,
EGL_STENCIL_SIZE, 8,
EGL_SAMPLE_BUFFERS, 0,
EGL_SAMPLES, EGL_DONT_CARE,
EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FIXED_EXT,
EGL_NONE };
int numConfigs = 0;
eglChooseConfig(surface->eglDisplay, configAttributes, &surface->eglConfig, 1, &numConfigs);
EGLint const surfaceAttributes[] = {EGL_NONE};
surface->eglSurface = eglCreateWindowSurface(surface->eglDisplay, surface->eglConfig, surface->hWnd, surfaceAttributes);
eglBindAPI(EGL_OPENGL_ES_API);
EGLint contextAttributes[] = {
EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
EGL_CONTEXT_MINOR_VERSION_KHR, 1,
EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM, EGL_TRUE,
EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE, EGL_TRUE,
EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE, EGL_FALSE,
EGL_NONE};
surface->eglContext = eglCreateContext(surface->eglDisplay, surface->eglConfig, EGL_NO_CONTEXT, contextAttributes);
eglMakeCurrent(surface->eglDisplay, surface->eglSurface, surface->eglSurface, surface->eglContext);
//TODO: reactivate this when finished testing!
// eglSwapInterval(surface->eglDisplay, 1);
eglSwapInterval(surface->eglDisplay, 0);
res = mg_surface_alloc_handle((mg_surface_data*)surface);
}
return(res);
}

View File

@ -1,17 +0,0 @@
/************************************************************//**
*
* @file: win32_gles_surface.h
* @author: Martin Fouilleul
* @date: 28/01/2023
* @revision:
*
*****************************************************************/
#ifndef __WIN32_GLES_SURFACE_H_
#define __WIN32_GLES_SURFACE_H_
#include"graphics.h"
#include"mp_app.h"
mg_surface mg_gles_surface_create_for_window(mp_window window);
#endif // __WIN32_GLES_SURFACE_H_

View File

@ -5,6 +5,13 @@ Overview
[.] Clean+Fixes of canvas code and examples [.] 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 [ ] Build image atlas on top
@ -17,6 +24,11 @@ Overview
[ ] Delegated drawing API+Impl [ ] Delegated drawing API+Impl
[ ] Make building apps simpler
[ ] single include path
[ ] script for embedding dependencies / create app bundle
Clean+Fixes Clean+Fixes
----------- -----------

View File

@ -94,3 +94,20 @@ Quick measurement on perf_text.exe
-> May be worth it to try persistently mapped buffers later. -> May be worth it to try persistently mapped buffers later.
* Splitting vertex data and shape data (using glBufferData) --> ~10ms * Splitting vertex data and shape data (using glBufferData) --> ~10ms
Backend Selection
-----------------
* We need define macros to select which backends are compiled into milepost.lib
-> define default backends and allow override with user compile defines
* We also need define macros to select which backend-specific functions and/or graphics APIs to include when building an app with milepost (e.g.\ include opengl headers or not, etc.).
* For now, we intend to statically link milepost.lib (or dynamically load to an embedded dll), so at application compile time the supported backends are known and calling a backend-specific function for a backend that has not been compiled in milepost results in a link error. (No need to defines for that)
* However, if we want to provide a nice abstraction for creation function, this means it is possible for user to pass a backend that does not exists in the lib (resulting in a runtime error). It would be nice to have some way to runtime check which backends are available. That can be a function compiled in the lib.
* We also might want to access backend-specific functions in the app, so we need to include those conditionally.
* We also might want to include graphics API, eg OpenGL, in a uniform way. -> See later how we manage GLES/GL co-existence