This commit is contained in:
2025-05-28 02:28:20 -05:00
parent 3667d53eae
commit aa70dcbdc2
12 changed files with 122 additions and 45 deletions

View File

@@ -53,6 +53,11 @@ const libraries = [
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
const benchmarks = [ const benchmarks = [
{
name: "Empty object",
data: [{}, {}, {}, {}],
iterations: 10000
},
{ {
name: "Small Integers", name: "Small Integers",
data: [0, 42, -1, 2023], data: [0, 42, -1, 2023],
@@ -125,8 +130,8 @@ function runBenchmarkForLibrary(lib, bench) {
let encodeTime = measureTime(() => { let encodeTime = measureTime(() => {
for (let i = 0; i < bench.iterations; i++) { for (let i = 0; i < bench.iterations; i++) {
// For each data item, encode it // For each data item, encode it
for (let d of bench.data) { for (let j = 0; j < bench.data.length; j++) {
let e = lib.encode(d); let e = lib.encode(bench.data[j]);
// store only in the very first iteration, so we can decode them later // 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. // but do not store them every iteration or we blow up memory.
if (i === 0) { if (i === 0) {
@@ -171,8 +176,8 @@ for (let bench of benchmarks) {
let decOpsPerSec = (totalOps / decodeTime).toFixed(1); let decOpsPerSec = (totalOps / decodeTime).toFixed(1);
console.log(` ${lib.name}:`); console.log(` ${lib.name}:`);
console.log(` Encode time: ${encodeTime.toFixed(3)}s => ${encOpsPerSec} encodes/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`); 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(` Total size: ${totalSize} bytes (or code units for JSON)`);
console.log(""); console.log("");
} }
@@ -180,3 +185,5 @@ for (let bench of benchmarks) {
} }
console.log("Benchmark complete.\n"); console.log("Benchmark complete.\n");
os.exit()

View File

@@ -353,8 +353,8 @@ function handle_renderer(msg) {
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"}; if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
ren.texture( ren.texture(
resources.texture[tex_id], resources.texture[tex_id],
msg.data.src || {x:0, y:0, w:1, h:1}, msg.data.src,
msg.data.dst || {x:0, y:0, w:100, h:100}, msg.data.dst,
msg.data.angle || 0, msg.data.angle || 0,
msg.data.anchor || {x:0.5, y:0.5} msg.data.anchor || {x:0.5, y:0.5}
); );

View File

@@ -183,15 +183,6 @@ draw.slice9 = function slice9(image, rect = [0,0], slice = 0, info = slice9_info
material: material 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) { 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.') 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} 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 return draw

View File

@@ -159,11 +159,8 @@ function create_image(path){
let raw = decode_image(bytes, path.ext()); let raw = decode_image(bytes, path.ext());
/* ── Case A: static image ─────────────────────────────────── */ /* ── Case A: static image ─────────────────────────────────── */
if(raw.surface) { if(raw.surface)
console.log("STATIC!")
console.log(json.encode(raw.surface))
return make_handle(raw.surface); return make_handle(raw.surface);
}
/* ── Case B: GIF helpers returned array [surf, …] ─────────── */ /* ── Case B: GIF helpers returned array [surf, …] ─────────── */
if(Array.isArray(raw)) if(Array.isArray(raw))

View File

@@ -1569,6 +1569,9 @@ void ffi_load(JSContext *js)
arrput(rt->module_registry, MISTLINE(sprite)); arrput(rt->module_registry, MISTLINE(sprite));
arrput(rt->module_registry, MISTLINE(transform)); arrput(rt->module_registry, MISTLINE(transform));
arrput(rt->module_registry, MISTLINE(wota));
arrput(rt->module_registry, MISTLINE(nota));
#ifndef NSTEAM #ifndef NSTEAM
arrput(rt->module_registry, MISTLINE(steam)); arrput(rt->module_registry, MISTLINE(steam));
#endif #endif

View File

@@ -38,6 +38,8 @@
#define unlikely(x) __builtin_expect(!!(x), 0) #define unlikely(x) __builtin_expect(!!(x), 0)
#endif #endif
static int tracy_profiling_enabled = 0;
#define ENGINE "scripts/core/engine.js" #define ENGINE "scripts/core/engine.js"
static prosperon_rt **ready_queue = NULL; static prosperon_rt **ready_queue = NULL;
@@ -331,7 +333,8 @@ void actor_turn(prosperon_rt *actor, int greedy)
{ {
SDL_LockMutex(actor->turn); SDL_LockMutex(actor->turn);
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
TracyCFiberEnter(actor->name); if (tracy_profiling_enabled)
TracyCFiberEnter(actor->name);
#endif #endif
SDL_LockMutex(actor->msg_mutex); SDL_LockMutex(actor->msg_mutex);
@@ -420,7 +423,8 @@ void actor_turn(prosperon_rt *actor, int greedy)
END: END:
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
TracyCFiberLeave(actor->name); if (tracy_profiling_enabled)
TracyCFiberLeave(actor->name);
#endif #endif
SDL_UnlockMutex(actor->turn); SDL_UnlockMutex(actor->turn);
set_actor_state(actor); set_actor_state(actor);
@@ -428,7 +432,8 @@ void actor_turn(prosperon_rt *actor, int greedy)
KILL: KILL:
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
TracyCFiberLeave(actor->id); if (tracy_profiling_enabled)
TracyCFiberLeave(actor->name);
#endif #endif
SDL_UnlockMutex(actor->turn); SDL_UnlockMutex(actor->turn);
actor_free(actor); actor_free(actor);
@@ -614,6 +619,9 @@ static tracy_stack_t *get_tracy_stack(void)
void tracy_call_hook(JSContext *js, JSValue fn) void tracy_call_hook(JSContext *js, JSValue fn)
{ {
if (!tracy_profiling_enabled)
return;
tracy_stack_t *stack = get_tracy_stack(); tracy_stack_t *stack = get_tracy_stack();
js_debug debug; js_debug debug;
js_debug_info(js, fn, &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) void tracy_end_hook(JSContext *js, JSValue fn)
{ {
if (!tracy_profiling_enabled)
return;
tracy_stack_t *stack = get_tracy_stack(); tracy_stack_t *stack = get_tracy_stack();
if (arrlen(stack->arr) > 0) if (arrlen(stack->arr) > 0)
___tracy_emit_zone_end(arrpop(stack->arr)); ___tracy_emit_zone_end(arrpop(stack->arr));
@@ -635,15 +646,20 @@ void script_startup(prosperon_rt *prt, void (*hook)(JSContext*))
{ {
JSRuntime *rt; JSRuntime *rt;
#ifdef TRACY_ENABLE #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 #else
rt = JS_NewRuntime(); rt = JS_NewRuntime();
#endif #endif
JSContext *js = JS_NewContextRaw(rt); JSContext *js = JS_NewContextRaw(rt);
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
js_debug_sethook(js, tracy_call_hook, JS_HOOK_CALL); if (tracy_profiling_enabled) {
js_debug_sethook(js, tracy_end_hook, JS_HOOK_RET); js_debug_sethook(js, tracy_call_hook, JS_HOOK_CALL);
js_debug_sethook(js, tracy_end_hook, JS_HOOK_RET);
}
#endif #endif
JS_AddIntrinsicBaseObjects(js); JS_AddIntrinsicBaseObjects(js);
@@ -1455,7 +1471,17 @@ bool event_watch(void *data, SDL_Event *e)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
const char *new_cwd = NULL; 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) { if (strcmp(argv[i], "--cwd") == 0) {
i++; i++;
if (i < argc) new_cwd = argv[i]; if (i < argc) new_cwd = argv[i];
@@ -1469,6 +1495,10 @@ int main(int argc, char **argv)
} }
queue_event = SDL_RegisterEvents(1); queue_event = SDL_RegisterEvents(1);
#ifdef TRACY_ENABLE
tracy_profiling_enabled = profile_enabled;
#endif
main_thread = SDL_GetCurrentThreadID(); main_thread = SDL_GetCurrentThreadID();
int cores = SDL_GetNumLogicalCPUCores(); int cores = SDL_GetNumLogicalCPUCores();
@@ -1495,7 +1525,11 @@ int main(int argc, char **argv)
event_watchers_mutex = SDL_CreateMutex(); event_watchers_mutex = SDL_CreateMutex();
/* Create the initial actor from the main command line. */ /* 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; cores = 1;

View File

@@ -97,9 +97,9 @@ static void wota_encode_value(WotaEncodeContext *enc, JSValueConst val, JSValueC
int tag = JS_VALUE_GET_TAG(replaced); int tag = JS_VALUE_GET_TAG(replaced);
switch (tag) { switch (tag) {
case JS_TAG_INT: { case JS_TAG_INT: {
double d; int32_t d;
JS_ToFloat64(ctx, &d, replaced); JS_ToInt32(ctx, &d, replaced);
wota_write_number(&enc->wb, d); wota_write_int_word(&enc->wb, d);
break; break;
} }
case JS_TAG_FLOAT64: 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); wota_write_sym(&enc->wb, WOTA_NULL);
break; break;
} }
wota_write_number(&enc->wb, d); wota_write_float_word(&enc->wb, d);
break; break;
} }
case JS_TAG_STRING: { case JS_TAG_STRING: {

View File

@@ -79,6 +79,8 @@ void wota_write_record (WotaBuffer *wb, unsigned long long count);
void wota_write_number (WotaBuffer *wb, double n); void wota_write_number (WotaBuffer *wb, double n);
/* Symbol codes (WOTA_NULL, WOTA_FALSE, etc.) */ /* Symbol codes (WOTA_NULL, WOTA_FALSE, etc.) */
void wota_write_sym (WotaBuffer *wb, int sym_code); 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 #ifdef WOTA_IMPLEMENTATION
@@ -496,7 +498,7 @@ static int fits_in_56_bits(long long x)
Write a WOTA_INT (single 64-bit word): Write a WOTA_INT (single 64-bit word):
top 56 bits = signed integer (arithmetic shift), LSB=0x00 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, /* shift 'val' left by 8 bits into the top 56,
then OR the type code in the bottom byte. */ 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 first word => type=0x01 in LSB, top 56 bits=0
second word => raw IEEE 754 double bits 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); uint64_t *p = wota_buffer_alloc(wb, 2);
p[0] = (uint64_t)WOTA_FLOAT; /* top 56 bits=0, LSB=0x01 */ p[0] = (uint64_t)WOTA_FLOAT; /* top 56 bits=0, LSB=0x01 */

View File

@@ -9,6 +9,8 @@ var transform = use('transform');
var rasterize = use('rasterize'); var rasterize = use('rasterize');
var video_actor = use('sdl_video') var video_actor = use('sdl_video')
var graphics
var window var window
var render var render
@@ -42,12 +44,18 @@ send(video_actor, {
} }
render = e.id render = e.id
graphics = use('graphics', video_actor, e.id)
console.log(`Created window and renderer id ${render}`) console.log(`Created window and renderer id ${render}`)
}) })
}) })
var last = os.now() var last = os.now()
// FPS tracking
var fps_samples = []
var fps_sample_count = 60
var fps_sum = 0
// Engine state // Engine state
var camera = { var camera = {
x: 0, x: 0,
@@ -56,8 +64,12 @@ var camera = {
rotation: 0 rotation: 0
} }
var images = {}
// Convert high-level draw commands to low-level renderer commands // Convert high-level draw commands to low-level renderer commands
function translate_draw_commands(commands) { function translate_draw_commands(commands) {
if (!graphics) return
var renderer_commands = [] var renderer_commands = []
commands.forEach(function(cmd) { commands.forEach(function(cmd) {
@@ -152,10 +164,16 @@ function translate_draw_commands(commands) {
break break
case "draw_image": case "draw_image":
var img = graphics.texture(cmd.image)
if (!img.gpu) break
// TODO: Handle image loading and texture management // TODO: Handle image loading and texture management
renderer_commands.push({ renderer_commands.push({
op: "texture", 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 break
@@ -215,7 +233,27 @@ function loop()
data: batch_commands data: batch_commands
}, _ => { }, _ => {
var diff = os.now() - now 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()
}) })
}) })
}) })

View File

@@ -50,6 +50,8 @@ function draw()
{color: color.yellow} {color: color.yellow}
) )
draw2d.image("tests/bunny")
// Return the draw commands // Return the draw commands
return draw2d.get_commands() return draw2d.get_commands()
} }

1
tests/spawnee.js Normal file
View File

@@ -0,0 +1 @@
console.log("im alive")

12
tests/spawner.js Normal file
View File

@@ -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)