diff --git a/build.sh b/build.sh index 318d2ae..407a277 100755 --- a/build.sh +++ b/build.sh @@ -51,7 +51,7 @@ elif [ $target = orca ] ; then ./scripts/bindgen.py core ./src ./scripts/bindgen.py gles ./src - ./scripts/bindgen2.py canvas \ + python3 ./scripts/bindgen2.py canvas \ src/canvas_api.json \ --guest-stubs sdk/graphics.c \ --guest-include graphics.h \ diff --git a/milepost b/milepost index c347e10..f36e144 160000 --- a/milepost +++ b/milepost @@ -1 +1 @@ -Subproject commit c347e105772072618d683214e76a21ca6229ac9a +Subproject commit f36e144bc0fed66470aff0612098dba29c552604 diff --git a/resources/OpenSansLatinSubset.ttf b/resources/OpenSansLatinSubset.ttf new file mode 100644 index 0000000..acfe2d8 Binary files /dev/null and b/resources/OpenSansLatinSubset.ttf differ diff --git a/samples/pong/build.sh b/samples/pong/build.sh index 225b524..f0535a0 100755 --- a/samples/pong/build.sh +++ b/samples/pong/build.sh @@ -8,8 +8,8 @@ wasmFlags="--target=wasm32 \ -Wl,--allow-undefined \ -g \ -D__ORCA__ \ - -I ../../sdk -I ../../milepost/src" + -I ../../sdk -I ../../milepost/src -I ../../milepost/src/util -I ../../milepost/src/platform" -/usr/local/opt/llvm/bin/clang $wasmFlags -o ./module.wasm ../../sdk/graphics.c ../../sdk/orca.c main.c +/usr/local/opt/llvm/bin/clang $wasmFlags -o ./module.wasm ../../sdk/graphics.c ../../sdk/orca.c src/main.c -#python3 ../../scripts/mkapp.py --orca-dir ../.. --name Pong --icon icon.png module.wasm +python3 ../../scripts/mkapp.py --orca-dir ../.. --name Pong --icon icon.png module.wasm diff --git a/samples/pong/main.c b/samples/pong/src/main.c similarity index 100% rename from samples/pong/main.c rename to samples/pong/src/main.c diff --git a/scripts/bindgen2.py b/scripts/bindgen2.py new file mode 100644 index 0000000..107234d --- /dev/null +++ b/scripts/bindgen2.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 + +from argparse import ArgumentParser +import json + +parser = ArgumentParser(prog='bindgen.py') +parser.add_argument('api') +parser.add_argument('spec') +parser.add_argument('-g', '--guest-stubs') +parser.add_argument('--guest-include') +parser.add_argument('--wasm3-bindings') + +args = parser.parse_args() + +apiName = args.api +spec = args.spec +guest_stubs_path = args.guest_stubs +if guest_stubs_path == None: + guest_stubs_path = 'bindgen_' + apiName + '_guest_stubs.c' + +wasm3_bindings_path = args.wasm3_bindings +if wasm3_bindings_path == None: + wasm3_bindings_path = 'bindgen_' + apiName + '_wasm3_bindings.c' + +host_bindings = open(wasm3_bindings_path, 'w') +guest_bindings = None + +specFile = open(spec, 'r') +data = json.load(specFile) + +def needs_arg_ptr_stub(decl): + res = (decl['ret']['tag'] == 'S') + for arg in decl['args']: + if arg['type']['tag'] == 'S': + res = True + return(res) + +for decl in data: + if needs_arg_ptr_stub(decl): + guest_bindings = open(guest_stubs_path, 'w') + if args.guest_include != None: + s = '#include"' + args.guest_include + '"\n\n' + print(s, file=guest_bindings) + break + +for decl in data: + + name = decl['name'] + cname = decl.get('cname', name) + + if needs_arg_ptr_stub(decl): + argPtrStubName = name + '_argptr_stub' + # pointer arg stub declaration + s = '' + if decl['ret']['tag'] == 'S': + s += 'void' + else: + s += decl['ret']['name'] + + s += ' ' + argPtrStubName + '(' + + if decl['ret']['tag'] == 'S': + s += decl['ret']['name'] + '* __retArg' + if len(decl['args']) > 0: + s += ', ' + + for i, arg in enumerate(decl['args']): + s += arg['type']['name'] + if arg['type']['tag'] == 'S': + s += '*' + s += ' ' + arg['name'] + if i+1 < len(decl['args']): + s += ', ' + s += ');\n\n' + + # forward function to pointer arg stub declaration + s += decl['ret']['name'] + ' ' + name + '(' + for i, arg in enumerate(decl['args']): + s += arg['type']['name'] + ' ' + arg['name'] + if i+1 < len(decl['args']): + s += ', ' + s += ')\n' + s += '{\n' + s += '\t' + if decl['ret']['tag'] == 'S': + s += decl['ret']['name'] + ' __ret;\n\t' + elif decl['ret']['tag'] != 'v': + s += decl['ret']['name'] + s += ' __ret = ' + s += argPtrStubName + '(' + + if decl['ret']['tag'] == 'S': + s += '&__ret' + if len(decl['args']) > 0: + s += ', ' + + for i, arg in enumerate(decl['args']): + if arg['type']['tag'] == 'S': + s += '&' + + s += arg['name'] + if i+1 < len(decl['args']): + s += ', ' + s += ');\n' + if decl['ret']['tag'] != 'v': + s += '\treturn(__ret);\n' + s += '}\n\n' + + print(s, file=guest_bindings) + + # host-side stub + s = 'const void* ' + cname + '_stub(IM3Runtime runtime, IM3ImportContext _ctx, uint64_t* _sp, void* _mem)' + + gen_stub = decl.get('gen_stub', True) + if gen_stub == False: + s += ';\n\n' + else: + s += '\n{\n\t' + retTag = decl['ret']['tag'] + + if retTag == 'i': + s += '*((i32*)&_sp[0]) = ' + elif retTag == 'I': + s += '*((i64*)&_sp[0]) = ' + elif retTag == 'f': + s += '*((f32*)&_sp[0]) = ' + elif retTag == 'd': + s += '*((f64*)&_sp[0]) = ' + elif retTag == 'S': + retTypeName = decl['ret']['name'] + retTypeCName = decl['ret'].get('cname', retTypeName) + s += '*(' + retTypeCName + '*)((char*)_mem + *(i32*)&_sp[0]) = ' + + s += cname + '(' + + firstArgIndex = 0 + if retTag != 'v': + firstArgIndex = 1 + + for i, arg in enumerate(decl['args']): + typeName = arg['type']['name'] + typeCName = arg['type'].get('cname', typeName) + argTag = arg['type']['tag'] + if argTag == 'i': + s += '*(i32*)&_sp[' + str(firstArgIndex + i) + ']' + elif argTag == 'I': + s += '*(i64*)&_sp[' + str(firstArgIndex + i) + ']' + elif argTag == 'f': + s += '*(f32*)&_sp[' + str(firstArgIndex + i) + ']' + elif argTag == 'd': + s += '*(f64*)&_sp[' + str(firstArgIndex + i) + ']' + elif argTag == 'p': + s += '(void*)((char*)_mem + *(i32*)&_sp[' + str(firstArgIndex + i) + '])' + elif argTag == 'S': + s += '*(' + typeCName + '*)((char*)_mem + *(i32*)&_sp[' + str(firstArgIndex + i) + '])' + else: + print('unrecognized type ' + c + ' in procedure signature\n') + break + + if i+1 < len(decl['args']): + s += ', ' + + s += ');\n\treturn(0);\n}\n\n' + + print(s, file=host_bindings) + +# link function +s = 'int bindgen_link_' + apiName + '_api(IM3Module module)\n{\n\t' +s += 'M3Result res;\n' + +for decl in data: + name = decl['name'] + cname = decl.get('cname', name) + + if needs_arg_ptr_stub(decl): + name = name + '_argptr_stub' + + m3Sig = '' + if decl['ret']['tag'] == 'S': + m3Sig += 'v' + else: + m3Sig += decl['ret']['tag'] + + m3Sig += '(' + if decl['ret']['tag'] == 'S': + m3Sig += 'i' + for arg in decl['args']: + tag = arg['type']['tag'] + if tag == 'p' or tag == 'S': + tag = 'i' + m3Sig += tag + m3Sig += ')' + + + s += '\tres = m3_LinkRawFunction(module, "*", "' + name + '", "' + m3Sig + '", ' + cname + '_stub);\n' + s += '\tif(res != m3Err_none && res != m3Err_functionLookupFailed) { LOG_ERROR("error: %s\\n", res); return(-1); }\n\n' + + +s += '\treturn(0);\n}\n' + +print(s, file=host_bindings) diff --git a/sdk/graphics.c b/sdk/graphics.c new file mode 100644 index 0000000..8892751 --- /dev/null +++ b/sdk/graphics.c @@ -0,0 +1,45 @@ +#include"graphics.h" + + +void g_set_color_argptr_stub(g_color* color); + +void g_set_color(g_color color) +{ + g_set_color_argptr_stub(&color); +} + + +void g_matrix_push_argptr_stub(g_mat2x3* m); + +void g_matrix_push(g_mat2x3 m) +{ + g_matrix_push_argptr_stub(&m); +} + + +void g_font_create_default_argptr_stub(g_font* __retArg); + +g_font g_font_create_default() +{ + g_font __ret; + g_font_create_default_argptr_stub(&__ret); + return(__ret); +} + + +void g_set_font_argptr_stub(g_font* font); + +void g_set_font(g_font font) +{ + g_set_font_argptr_stub(&font); +} + + +void g_text_outlines_argptr_stub(str8* text); + +void g_text_outlines(str8 text) +{ + g_text_outlines_argptr_stub(&text); +} + + diff --git a/sdk/graphics.h b/sdk/graphics.h new file mode 100644 index 0000000..1743f9d --- /dev/null +++ b/sdk/graphics.h @@ -0,0 +1,222 @@ +//***************************************************************** +// +// $file: graphics.h $ +// $author: Martin Fouilleul $ +// $date: 23/36/2015 $ +// $revision: $ +// +//***************************************************************** +#ifndef __GRAPHICS_H_ +#define __GRAPHICS_H_ + +#include"typedefs.h" + +typedef struct g_mat2x3 +{ + f32 m[6]; +} g_mat2x3; + +typedef struct g_color +{ + union + { + struct + { + f32 r; + f32 g; + f32 b; + f32 a; + }; + f32 c[4]; + }; +} g_color; + +typedef enum {G_JOINT_MITER = 0, + G_JOINT_BEVEL, + G_JOINT_NONE } g_joint_type; + +typedef enum {G_CAP_NONE = 0, + G_CAP_SQUARE } g_cap_type; + +typedef struct g_font { u64 h; } g_font; + +typedef struct g_font_extents +{ + f32 ascent; // the extent above the baseline (by convention a positive value extends above the baseline) + f32 descent; // the extent below the baseline (by convention, positive value extends below the baseline) + f32 leading; // spacing between one row's descent and the next row's ascent + f32 xHeight; // height of the lower case letter 'x' + f32 capHeight; // height of the upper case letter 'M' + f32 width; // maximum width of the font + +} g_font_extents; + +typedef struct g_text_extents +{ + f32 xBearing; + f32 yBearing; + f32 width; + f32 height; + f32 xAdvance; + f32 yAdvance; + +} g_text_extents; + +//------------------------------------------------------------------------------------------ +//NOTE(martin): fonts +//------------------------------------------------------------------------------------------ + +g_font g_font_nil(); +g_font g_font_create_default(); +//g_font g_font_create_from_memory(u32 size, byte* buffer, u32 rangeCount, unicode_range* ranges); +void g_font_destroy(g_font font); + +//NOTE(martin): the following int valued functions return -1 if font is invalid or codepoint is not present in font// +//TODO(martin): add enum error codes +/* +g_font_extents g_font_get_extents(g_font font); +g_font_extents g_font_get_scaled_extents(g_font font, f32 emSize); +f32 g_font_get_scale_for_em_pixels(g_font font, f32 emSize); + +//NOTE(martin): if you need to process more than one codepoint, first convert your codepoints to glyph indices, then use the +// glyph index versions of the functions, which can take an array of glyph indices. + +str32 g_font_get_glyph_indices(g_font font, str32 codePoints, str32 backing); +str32 g_font_push_glyph_indices(g_font font, mem_arena* arena, str32 codePoints); +u32 g_font_get_glyph_index(g_font font, utf32 codePoint); + +int g_font_get_codepoint_extents(g_font font, utf32 codePoint, g_text_extents* outExtents); + +int g_font_get_glyph_extents(g_font font, str32 glyphIndices, g_text_extents* outExtents); + +mp_rect g_text_bounding_box_utf32(g_font font, f32 fontSize, str32 text); +mp_rect g_text_bounding_box(g_font font, f32 fontSize, str8 text); +*/ +//------------------------------------------------------------------------------------------ +//NOTE(martin): images +//------------------------------------------------------------------------------------------ +/* +g_image g_image_nil(); +bool g_image_is_nil(g_image a); + +g_image g_image_create(u32 width, u32 height); +g_image g_image_create_from_rgba8(u32 width, u32 height, u8* pixels); +g_image g_image_create_from_data(str8 data, bool flip); +g_image g_image_create_from_file(str8 path, bool flip); + +void g_image_destroy(g_image image); + +void g_image_upload_region_rgba8(g_image image, mp_rect region, u8* pixels); +vec2 g_image_size(g_image image); +*/ +//------------------------------------------------------------------------------------------ +//NOTE(martin): atlasing +//------------------------------------------------------------------------------------------ +/* +//NOTE: rectangle allocator +typedef struct g_rect_atlas g_rect_atlas; + +g_rect_atlas* g_rect_atlas_create(mem_arena* arena, i32 width, i32 height); +mp_rect g_rect_atlas_alloc(g_rect_atlas* atlas, i32 width, i32 height); +void g_rect_atlas_recycle(g_rect_atlas* atlas, mp_rect rect); + +//NOTE: image atlas helpers +typedef struct g_image_region +{ + g_image image; + mp_rect rect; +} g_image_region; + +g_image_region g_image_atlas_alloc_from_rgba8(g_rect_atlas* atlas, g_image backingImage, u32 width, u32 height, u8* pixels); +g_image_region g_image_atlas_alloc_from_data(g_rect_atlas* atlas, g_image backingImage, str8 data, bool flip); +g_image_region g_image_atlas_alloc_from_file(g_rect_atlas* atlas, g_image backingImage, str8 path, bool flip); +void g_image_atlas_recycle(g_rect_atlas* atlas, g_image_region imageRgn); +*/ +//------------------------------------------------------------------------------------------ +//NOTE(martin): transform, viewport and clipping +//------------------------------------------------------------------------------------------ +void g_viewport(mp_rect viewPort); + +void g_matrix_push(g_mat2x3 matrix); +void g_matrix_pop(); + +void g_clip_push(f32 x, f32 y, f32 w, f32 h); +void g_clip_pop(); + +//------------------------------------------------------------------------------------------ +//NOTE(martin): graphics attributes setting/getting +//------------------------------------------------------------------------------------------ +void g_set_color(g_color color); +void g_set_color_rgba(f32 r, f32 g, f32 b, f32 a); +void g_set_width(f32 width); +void g_set_tolerance(f32 tolerance); +void g_set_joint(g_joint_type joint); +void g_set_max_joint_excursion(f32 maxJointExcursion); +void g_set_cap(g_cap_type cap); +void g_set_font(g_font font); +void g_set_font_size(f32 size); +void g_set_text_flip(bool flip); + +/* +void g_set_image(g_image image); +void g_set_image_source_region(mp_rect region); +*/ + +g_color g_get_color(); +f32 g_get_width(); +f32 g_get_tolerance(); +g_joint_type g_get_joint(); +f32 g_get_max_joint_excursion(); +g_cap_type g_get_cap(); +g_font g_get_font(); +f32 g_get_font_size(); +bool g_get_text_flip(); + +//------------------------------------------------------------------------------------------ +//NOTE(martin): path construction +//------------------------------------------------------------------------------------------ +vec2 g_get_position(); +void g_move_to(f32 x, f32 y); +void g_line_to(f32 x, f32 y); +void g_quadratic_to(f32 x1, f32 y1, f32 x2, f32 y2); +void g_cubic_to(f32 x1, f32 y1, f32 x2, f32 y2, f32 x3, f32 y3); +void g_close_path(); + +/* +mp_rect g_glyph_outlines(str32 glyphIndices); +void g_codepoints_outlines(str32 string); +*/ +typedef struct str8 +{ + u64 len; + char* ptr; +} str8; + +void g_text_outlines(str8 string); + +//------------------------------------------------------------------------------------------ +//NOTE(martin): clear/fill/stroke +//------------------------------------------------------------------------------------------ +void g_clear(); +void g_fill(); +void g_stroke(); + +//------------------------------------------------------------------------------------------ +//NOTE(martin): simple shapes helpers +//------------------------------------------------------------------------------------------ +void g_rectangle_fill(f32 x, f32 y, f32 w, f32 h); +void g_rectangle_stroke(f32 x, f32 y, f32 w, f32 h); +void g_rounded_rectangle_fill(f32 x, f32 y, f32 w, f32 h, f32 r); +void g_rounded_rectangle_stroke(f32 x, f32 y, f32 w, f32 h, f32 r); +void g_ellipse_fill(f32 x, f32 y, f32 rx, f32 ry); +void g_ellipse_stroke(f32 x, f32 y, f32 rx, f32 ry); +void g_circle_fill(f32 x, f32 y, f32 r); +void g_circle_stroke(f32 x, f32 y, f32 r); +void g_arc(f32 x, f32 y, f32 r, f32 arcAngle, f32 startAngle); + +//NOTE: image helpers +/* +void g_image_draw(g_image image, mp_rect rect); +void g_image_draw_region(g_image image, mp_rect srcRegion, mp_rect dstRegion); +*/ +#endif //__GRAPHICS_H_ diff --git a/sdk/orca.c b/sdk/orca.c new file mode 100644 index 0000000..2ba3379 --- /dev/null +++ b/sdk/orca.c @@ -0,0 +1,9 @@ +/************************************************************//** +* +* @file: orca.c +* @author: Martin Fouilleul +* @date: 13/04/2023 +* +*****************************************************************/ + +//#include"util/memory.c" diff --git a/sdk/orca.h b/sdk/orca.h new file mode 100644 index 0000000..683b4e2 --- /dev/null +++ b/sdk/orca.h @@ -0,0 +1,14 @@ +/************************************************************//** +* +* @file: orca.h +* @author: Martin Fouilleul +* @date: 13/04/2023 +* +*****************************************************************/ +#ifndef __ORCA_H_ +#define __ORCA_H_ + +#include"util/typedefs.h" +#include"util/lists.h" + +#endif //__ORCA_H_ diff --git a/src/canvas_api.json b/src/canvas_api.json new file mode 100644 index 0000000..e77bc6d --- /dev/null +++ b/src/canvas_api.json @@ -0,0 +1,124 @@ +[ +{ + "name": "g_clear", + "cname": "mg_clear", + "ret": {"name": "void", "tag": "v"}, + "args": [] +}, +{ + "name": "g_set_color_rgba", + "cname": "mg_set_color_rgba", + "ret": {"name": "void", "tag": "v"}, + "args": [ + {"name": "r", + "type": {"name": "float", "tag": "f"}}, + {"name": "g", + "type": {"name": "float", "tag": "f"}}, + {"name": "b", + "type": {"name": "float", "tag": "f"}}, + {"name": "a", + "type": {"name": "float", "tag": "f"}}] +}, +{ + "name": "g_set_color", + "cname": "mg_set_color", + "ret": {"name": "void", "tag": "v"}, + "args": [ + {"name": "color", + "type": {"name": "g_color", "cname": "mg_color", "tag": "S"}}] +}, +{ + "name": "g_matrix_push", + "cname": "mg_matrix_push", + "ret": {"name": "void", "tag": "v"}, + "args": [ + {"name": "m", + "type": {"name": "g_mat2x3", "cname": "mg_mat2x3", "tag": "S"}}] +}, +{ + "name": "g_matrix_pop", + "cname": "mg_matrix_pop", + "ret": {"name": "void", "tag": "v"}, + "args": [] +}, +{ + "name": "g_circle_fill", + "cname": "mg_circle_fill", + "ret": {"name": "void", "tag": "v"}, + "args": [ + {"name": "x", + "type": {"name": "float", "tag": "f"}}, + {"name": "y", + "type": {"name": "float", "tag": "f"}}, + {"name": "r", + "type": {"name": "float", "tag": "f"}}] +}, +{ + "name": "g_rectangle_fill", + "cname": "mg_rectangle_fill", + "ret": {"name": "void", "tag": "v"}, + "args": [ + {"name": "x", + "type": {"name": "float", "tag": "f"}}, + {"name": "y", + "type": {"name": "float", "tag": "f"}}, + {"name": "w", + "type": {"name": "float", "tag": "f"}}, + {"name": "h", + "type": {"name": "float", "tag": "f"}}] +}, +{ + "name": "g_font_create_default", + "cname": "mg_font_create_default", + "ret": {"name": "g_font", "cname": "mg_font", "tag": "S"}, + "args": [] +}, +{ + "name": "g_set_font", + "cname": "mg_set_font", + "ret": {"name": "void", "tag": "v"}, + "args": [ + {"name": "font", + "type": {"name": "g_font", "cname": "mg_font" , "tag": "S"}}] +}, +{ + "name": "g_set_font_size", + "cname": "mg_set_font_size", + "ret": {"name": "void", "tag": "v"}, + "args": [ + {"name": "size", + "type": {"name": "float", "cname": "float" , "tag": "f"}}] +}, +{ + "gen_stub": false, + "name": "g_text_outlines", + "cname": "mg_text_outlines", + "ret": {"name": "void", "tag": "v"}, + "args": [ + {"name": "text", + "type": {"name": "str8", "cname": "str8" , "tag": "S"}}] +}, +{ + "name": "g_fill", + "cname": "mg_fill", + "ret": {"name": "void", "tag": "v"}, + "args": [] +}, +{ + "name": "g_move_to", + "cname": "mg_move_to", + "ret": {"name": "void", "tag": "v"}, + "args": [ + {"name": "x", + "type": {"name": "f32", "tag":"f"}}, + {"name": "y", + "type": {"name": "f32", "tag":"f"}}] +}, +{ + "name": "g_set_text_flip", + "cname": "mg_set_text_flip", + "ret": {"name": "void", "tag": "v"}, + "args": [ + {"name": "flip", + "type": {"name": "bool", "tag":"i"}}] +}] diff --git a/src/canvas_api_bind.c b/src/canvas_api_bind.c new file mode 100644 index 0000000..9c7175f --- /dev/null +++ b/src/canvas_api_bind.c @@ -0,0 +1,21 @@ +#include"canvas_api_bind_gen.c" + +typedef struct wasm_str8 +{ + i64 len; + i32 offset; +} wasm_str8; + +const void* mg_text_outlines_stub(IM3Runtime runtime, IM3ImportContext _ctx, uint64_t* _sp, void* _mem) +{ + wasm_str8 wasmStr = *(wasm_str8*)((char*)_mem + *(i32*)&_sp[0]); + + ///////////////////////////////////////////////////////////////////// + //TODO: bound checks + str8 str = {.len = wasmStr.len, + .ptr = (char*)_mem + wasmStr.offset}; + ///////////////////////////////////////////////////////////////////// + + mg_text_outlines(str); + return(0); +}