From aa70dcbdc2bf777248106012cfe761e0c75614a3 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Wed, 28 May 2025 02:28:20 -0500 Subject: [PATCH] update --- benchmarks/wota_nota_json.js | 15 ++++++++--- scripts/core/_sdl_video.js | 4 +-- scripts/modules/draw2d.js | 19 -------------- scripts/modules/graphics.js | 5 +--- source/jsffi.c | 3 +++ source/prosperon.c | 50 ++++++++++++++++++++++++++++++------ source/qjs_wota.c | 8 +++--- source/wota.h | 6 +++-- tests/moth.js | 42 ++++++++++++++++++++++++++++-- tests/prosperon.js | 2 ++ tests/spawnee.js | 1 + tests/spawner.js | 12 +++++++++ 12 files changed, 122 insertions(+), 45 deletions(-) create mode 100644 tests/spawnee.js create mode 100644 tests/spawner.js diff --git a/benchmarks/wota_nota_json.js b/benchmarks/wota_nota_json.js index 9fde30d8..d13694ef 100644 --- a/benchmarks/wota_nota_json.js +++ b/benchmarks/wota_nota_json.js @@ -53,6 +53,11 @@ const libraries = [ //////////////////////////////////////////////////////////////////////////////// const benchmarks = [ + { + name: "Empty object", + data: [{}, {}, {}, {}], + iterations: 10000 + }, { name: "Small Integers", data: [0, 42, -1, 2023], @@ -125,8 +130,8 @@ function runBenchmarkForLibrary(lib, bench) { let encodeTime = measureTime(() => { for (let i = 0; i < bench.iterations; i++) { // For each data item, encode it - for (let d of bench.data) { - let e = lib.encode(d); + for (let j = 0; j < bench.data.length; j++) { + let e = lib.encode(bench.data[j]); // store only in the very first iteration, so we can decode them later // but do not store them every iteration or we blow up memory. if (i === 0) { @@ -171,8 +176,8 @@ for (let bench of benchmarks) { let decOpsPerSec = (totalOps / decodeTime).toFixed(1); console.log(` ${lib.name}:`); - console.log(` Encode time: ${encodeTime.toFixed(3)}s => ${encOpsPerSec} encodes/sec`); - console.log(` Decode time: ${decodeTime.toFixed(3)}s => ${decOpsPerSec} decodes/sec`); + console.log(` Encode time: ${encodeTime.toFixed(3)}s => ${encOpsPerSec} encodes/sec [${(encodeTime/bench.iterations)*1000000000} ns/try]`); + console.log(` Decode time: ${decodeTime.toFixed(3)}s => ${decOpsPerSec} decodes/sec [${(decodeTime/bench.iterations)*1000000000}/try]`); console.log(` Total size: ${totalSize} bytes (or code units for JSON)`); console.log(""); } @@ -180,3 +185,5 @@ for (let bench of benchmarks) { } console.log("Benchmark complete.\n"); + +os.exit() diff --git a/scripts/core/_sdl_video.js b/scripts/core/_sdl_video.js index cfcd3b07..1b061be4 100644 --- a/scripts/core/_sdl_video.js +++ b/scripts/core/_sdl_video.js @@ -353,8 +353,8 @@ function handle_renderer(msg) { if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"}; ren.texture( resources.texture[tex_id], - msg.data.src || {x:0, y:0, w:1, h:1}, - msg.data.dst || {x:0, y:0, w:100, h:100}, + msg.data.src, + msg.data.dst, msg.data.angle || 0, msg.data.anchor || {x:0.5, y:0.5} ); diff --git a/scripts/modules/draw2d.js b/scripts/modules/draw2d.js index 3eae7fa1..55fd4378 100644 --- a/scripts/modules/draw2d.js +++ b/scripts/modules/draw2d.js @@ -183,15 +183,6 @@ draw.slice9 = function slice9(image, rect = [0,0], slice = 0, info = slice9_info material: material }) } -draw.slice9[prosperon.DOC] = ` -:param image: An image object or string path to a texture. -:param rect: A rectangle specifying draw location/size, default [0, 0]. -:param slice: The pixel inset or spacing for the 9-slice (number or object). -:param info: A slice9 info object controlling tiling of edges/corners. -:param material: Material/styling information -:return: None -:raises Error: If no image is provided. -` draw.image = function image(image, rect = [0,0], rotation = 0, anchor = [0,0], shear = [0,0], info = {}, material) { if (!image) throw Error('Need an image to render.') @@ -228,15 +219,5 @@ draw.text = function text(text, rect, font = 'fonts/c64.ttf', size = 8, color = material: {color} }) } -draw.text[prosperon.DOC] = ` -:param text: The string to draw. -:param rect: A rectangle specifying draw position (and possibly wrapping area). -:param font: A font object or string path, default sysfont. -:param size: Font size in pixels. -:param color: The text color, default color.white. -:param wrap: Pixel width for text wrapping, default 0 (no wrap). -:param material: Material/styling information -:return: None -` return draw \ No newline at end of file diff --git a/scripts/modules/graphics.js b/scripts/modules/graphics.js index fbdcae58..92f1ddbc 100644 --- a/scripts/modules/graphics.js +++ b/scripts/modules/graphics.js @@ -159,11 +159,8 @@ function create_image(path){ let raw = decode_image(bytes, path.ext()); /* ── Case A: static image ─────────────────────────────────── */ - if(raw.surface) { - console.log("STATIC!") - console.log(json.encode(raw.surface)) + if(raw.surface) return make_handle(raw.surface); - } /* ── Case B: GIF helpers returned array [surf, …] ─────────── */ if(Array.isArray(raw)) diff --git a/source/jsffi.c b/source/jsffi.c index fbfc1f6e..06fd58f8 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -1568,6 +1568,9 @@ void ffi_load(JSContext *js) arrput(rt->module_registry, MISTLINE(rtree)); arrput(rt->module_registry, MISTLINE(sprite)); arrput(rt->module_registry, MISTLINE(transform)); + + arrput(rt->module_registry, MISTLINE(wota)); + arrput(rt->module_registry, MISTLINE(nota)); #ifndef NSTEAM arrput(rt->module_registry, MISTLINE(steam)); diff --git a/source/prosperon.c b/source/prosperon.c index f8e9839a..e6c5e5bf 100644 --- a/source/prosperon.c +++ b/source/prosperon.c @@ -38,6 +38,8 @@ #define unlikely(x) __builtin_expect(!!(x), 0) #endif +static int tracy_profiling_enabled = 0; + #define ENGINE "scripts/core/engine.js" static prosperon_rt **ready_queue = NULL; @@ -331,7 +333,8 @@ void actor_turn(prosperon_rt *actor, int greedy) { SDL_LockMutex(actor->turn); #ifdef TRACY_ENABLE - TracyCFiberEnter(actor->name); + if (tracy_profiling_enabled) + TracyCFiberEnter(actor->name); #endif SDL_LockMutex(actor->msg_mutex); @@ -420,7 +423,8 @@ void actor_turn(prosperon_rt *actor, int greedy) END: #ifdef TRACY_ENABLE - TracyCFiberLeave(actor->name); + if (tracy_profiling_enabled) + TracyCFiberLeave(actor->name); #endif SDL_UnlockMutex(actor->turn); set_actor_state(actor); @@ -428,7 +432,8 @@ void actor_turn(prosperon_rt *actor, int greedy) KILL: #ifdef TRACY_ENABLE - TracyCFiberLeave(actor->id); + if (tracy_profiling_enabled) + TracyCFiberLeave(actor->name); #endif SDL_UnlockMutex(actor->turn); actor_free(actor); @@ -614,6 +619,9 @@ static tracy_stack_t *get_tracy_stack(void) void tracy_call_hook(JSContext *js, JSValue fn) { + if (!tracy_profiling_enabled) + return; + tracy_stack_t *stack = get_tracy_stack(); js_debug debug; js_debug_info(js, fn, &debug); @@ -626,6 +634,9 @@ void tracy_call_hook(JSContext *js, JSValue fn) void tracy_end_hook(JSContext *js, JSValue fn) { + if (!tracy_profiling_enabled) + return; + tracy_stack_t *stack = get_tracy_stack(); if (arrlen(stack->arr) > 0) ___tracy_emit_zone_end(arrpop(stack->arr)); @@ -635,15 +646,20 @@ void script_startup(prosperon_rt *prt, void (*hook)(JSContext*)) { JSRuntime *rt; #ifdef TRACY_ENABLE - rt = JS_NewRuntime2(&tracy_malloc_funcs, NULL); + if (tracy_profiling_enabled) + rt = JS_NewRuntime2(&tracy_malloc_funcs, NULL); + else + rt = JS_NewRuntime(); #else rt = JS_NewRuntime(); #endif JSContext *js = JS_NewContextRaw(rt); #ifdef TRACY_ENABLE - js_debug_sethook(js, tracy_call_hook, JS_HOOK_CALL); - js_debug_sethook(js, tracy_end_hook, JS_HOOK_RET); + if (tracy_profiling_enabled) { + js_debug_sethook(js, tracy_call_hook, JS_HOOK_CALL); + js_debug_sethook(js, tracy_end_hook, JS_HOOK_RET); + } #endif JS_AddIntrinsicBaseObjects(js); @@ -1455,7 +1471,17 @@ bool event_watch(void *data, SDL_Event *e) int main(int argc, char **argv) { const char *new_cwd = NULL; - for (int i = 1; i < argc; i++) { + int profile_enabled = 0; + int script_start = 1; + + // Check for --profile flag first + if (argc > 1 && strcmp(argv[1], "--profile") == 0) { + profile_enabled = 1; + script_start = 2; + } + + // Check for --cwd flag + for (int i = script_start; i < argc; i++) { if (strcmp(argv[i], "--cwd") == 0) { i++; if (i < argc) new_cwd = argv[i]; @@ -1468,6 +1494,10 @@ int main(int argc, char **argv) exit(1); } queue_event = SDL_RegisterEvents(1); + +#ifdef TRACY_ENABLE + tracy_profiling_enabled = profile_enabled; +#endif main_thread = SDL_GetCurrentThreadID(); @@ -1495,7 +1525,11 @@ int main(int argc, char **argv) event_watchers_mutex = SDL_CreateMutex(); /* Create the initial actor from the main command line. */ - create_actor(argc, argv, NULL); + /* Adjust argc and argv to skip --profile if present */ + int actor_argc = argc - (script_start - 1); + char **actor_argv = argv + (script_start - 1); + actor_argv[0] = argv[0]; // Keep the program name + create_actor(actor_argc, actor_argv, NULL); cores = 1; diff --git a/source/qjs_wota.c b/source/qjs_wota.c index 43b365d1..ddc93d70 100644 --- a/source/qjs_wota.c +++ b/source/qjs_wota.c @@ -97,9 +97,9 @@ static void wota_encode_value(WotaEncodeContext *enc, JSValueConst val, JSValueC int tag = JS_VALUE_GET_TAG(replaced); switch (tag) { case JS_TAG_INT: { - double d; - JS_ToFloat64(ctx, &d, replaced); - wota_write_number(&enc->wb, d); + int32_t d; + JS_ToInt32(ctx, &d, replaced); + wota_write_int_word(&enc->wb, d); break; } case JS_TAG_FLOAT64: @@ -109,7 +109,7 @@ static void wota_encode_value(WotaEncodeContext *enc, JSValueConst val, JSValueC wota_write_sym(&enc->wb, WOTA_NULL); break; } - wota_write_number(&enc->wb, d); + wota_write_float_word(&enc->wb, d); break; } case JS_TAG_STRING: { diff --git a/source/wota.h b/source/wota.h index 98765f61..5fa56e1f 100644 --- a/source/wota.h +++ b/source/wota.h @@ -79,6 +79,8 @@ void wota_write_record (WotaBuffer *wb, unsigned long long count); void wota_write_number (WotaBuffer *wb, double n); /* Symbol codes (WOTA_NULL, WOTA_FALSE, etc.) */ void wota_write_sym (WotaBuffer *wb, int sym_code); +void wota_write_int_word(WotaBuffer *wb, long long val); +void wota_write_float_word(WotaBuffer *wb, double val); #ifdef WOTA_IMPLEMENTATION @@ -496,7 +498,7 @@ static int fits_in_56_bits(long long x) Write a WOTA_INT (single 64-bit word): top 56 bits = signed integer (arithmetic shift), LSB=0x00 */ -static void wota_write_int_word(WotaBuffer *wb, long long val) +void wota_write_int_word(WotaBuffer *wb, long long val) { /* shift 'val' left by 8 bits into the top 56, then OR the type code in the bottom byte. */ @@ -511,7 +513,7 @@ static void wota_write_int_word(WotaBuffer *wb, long long val) first word => type=0x01 in LSB, top 56 bits=0 second word => raw IEEE 754 double bits */ -static void wota_write_float_word(WotaBuffer *wb, double val) +void wota_write_float_word(WotaBuffer *wb, double val) { uint64_t *p = wota_buffer_alloc(wb, 2); p[0] = (uint64_t)WOTA_FLOAT; /* top 56 bits=0, LSB=0x01 */ diff --git a/tests/moth.js b/tests/moth.js index a722d2de..bb3c99b2 100644 --- a/tests/moth.js +++ b/tests/moth.js @@ -9,6 +9,8 @@ var transform = use('transform'); var rasterize = use('rasterize'); var video_actor = use('sdl_video') +var graphics + var window var render @@ -42,12 +44,18 @@ send(video_actor, { } render = e.id + graphics = use('graphics', video_actor, e.id) console.log(`Created window and renderer id ${render}`) }) }) var last = os.now() +// FPS tracking +var fps_samples = [] +var fps_sample_count = 60 +var fps_sum = 0 + // Engine state var camera = { x: 0, @@ -56,8 +64,12 @@ var camera = { rotation: 0 } +var images = {} + + // Convert high-level draw commands to low-level renderer commands function translate_draw_commands(commands) { + if (!graphics) return var renderer_commands = [] commands.forEach(function(cmd) { @@ -152,10 +164,16 @@ function translate_draw_commands(commands) { break case "draw_image": + var img = graphics.texture(cmd.image) + if (!img.gpu) break // TODO: Handle image loading and texture management renderer_commands.push({ op: "texture", - data: cmd + data: { + texture_id: img.gpu.id, + dst: {x: cmd.rect.x, y: cmd.rect.y, width: img.width, height:img.height}, + src: {x:0,y:0,width:img.width,height:img.height}, + } }) break @@ -215,7 +233,27 @@ function loop() data: batch_commands }, _ => { var diff = os.now() - now - $_.delay(loop,1/240) + + // Calculate and track FPS + var frame_time = os.now() - last + if (frame_time > 0) { + var current_fps = 1 / frame_time + + // Add to samples + fps_samples.push(current_fps) + fps_sum += current_fps + + // Keep only the last N samples + if (fps_samples.length > fps_sample_count) { + fps_sum -= fps_samples.shift() + } + + // Calculate average FPS + var avg_fps = fps_sum / fps_samples.length + console.log(`FPS: ${avg_fps.toFixed(1)}`) + } + + loop() }) }) }) diff --git a/tests/prosperon.js b/tests/prosperon.js index 05ec9812..4f1467cc 100644 --- a/tests/prosperon.js +++ b/tests/prosperon.js @@ -50,6 +50,8 @@ function draw() {color: color.yellow} ) + draw2d.image("tests/bunny") + // Return the draw commands return draw2d.get_commands() } diff --git a/tests/spawnee.js b/tests/spawnee.js new file mode 100644 index 00000000..cc0b9a66 --- /dev/null +++ b/tests/spawnee.js @@ -0,0 +1 @@ +console.log("im alive") \ No newline at end of file diff --git a/tests/spawner.js b/tests/spawner.js new file mode 100644 index 00000000..3d98c85a --- /dev/null +++ b/tests/spawner.js @@ -0,0 +1,12 @@ + +function spawnem() +{ + for (var i = 0; i < 10; i++) + $_.start(_ => {}, "tests/spawnee.js") +} + +with ([1,2,3]) { + console.log(toString()) +} + +$_.delay(spawnem, 3) \ No newline at end of file