From 3667d53eaede89ff1a7bc31ffe8a20b39d02e87e Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 27 May 2025 17:06:03 -0500 Subject: [PATCH] tracy is cell level now --- meson.build | 1 - scripts/core/engine.js | 35 ++- scripts/modules/rasterize.js | 10 +- source/jsffi.c | 8 - source/prosperon.c | 64 +++- source/prosperon.h | 2 + source/qjs_actor.c | 6 + source/qjs_os.c | 32 ++ source/qjs_tracy.c | 591 ----------------------------------- tests/moth.js | 8 +- 10 files changed, 133 insertions(+), 624 deletions(-) delete mode 100644 source/qjs_tracy.c diff --git a/meson.build b/meson.build index 0a677d5f..00a0bd83 100644 --- a/meson.build +++ b/meson.build @@ -148,7 +148,6 @@ if host_machine.system() != 'emscripten' deps += dependency('enet', static:true) src += 'qjs_enet.c' - src += 'qjs_tracy.c' tracy_opts = ['fibers=true', 'on_demand=true'] add_project_arguments('-DTRACY_ENABLE', language:['c','cpp']) deps += dependency('tracy', static:true, default_options:tracy_opts) diff --git a/scripts/core/engine.js b/scripts/core/engine.js index ee1edcd8..2ced0d80 100644 --- a/scripts/core/engine.js +++ b/scripts/core/engine.js @@ -440,12 +440,12 @@ $_.contact = function(callback, record) { $_.contact[prosperon.DOC] = `The contact function sends a message to a portal on another machine to obtain an actor object. The callback is a function with a actor input and a reason input. If successful, actor is bound to an actor object. If not successful, actor is null and reason may contain an explanation.` -$_.receiver = function(fn) { +$_.receiver = function receiver(fn) { receive_fn = fn } $_.receiver[prosperon.DOC] = "registers a function that will receive all messages..." -$_.start = function(cb, prg, arg) { +$_.start = function start(cb, prg, arg) { if (dying) { console.warn(`Cannot start an underling in the same turn as we're stopping`) return @@ -460,7 +460,7 @@ $_.start = function(cb, prg, arg) { } $_.start[prosperon.DOC] = "The start function creates a new actor..." -$_.stop = function(actor) { +$_.stop = function stop(actor) { if (!actor) { destroyself() return @@ -474,19 +474,23 @@ $_.stop = function(actor) { } $_.stop[prosperon.DOC] = "The stop function stops an underling." -$_.unneeded = function(fn, seconds) { +$_.unneeded = function unneeded(fn, seconds) { actor_mod.unneeded(fn, seconds) } $_.unneeded[prosperon.DOC] = "registers a function that is called when the actor..." -$_.delay = function(fn, seconds) { - var id = actor_mod.delay(fn, seconds) +$_.delay = function delay(fn, seconds) { + function delay_turn() { + fn() + send_messages() + } + var id = actor_mod.delay(delay_turn, seconds) return function() { actor_mod.removetimer(id) } } $_.delay[prosperon.DOC] = "used to schedule the invocation of a function..." var couplings = new Set() -$_.couple = function(actor) { +$_.couple = function couple(actor) { console.log(`coupled to ${actor.__ACTORDATA__.id}`) couplings.add(actor.__ACTORDATA__.id) } @@ -604,15 +608,18 @@ else prosperon.id = prosperon.args.id $_.__ACTORDATA__.id = prosperon.id -actor_mod.register_actor(prosperon.id, function(msg) { +function turn(msg) +{ try { handle_message(msg) send_messages() } catch (err) { message_queue = [] throw err - } -}, prosperon.args.main) + } +} + +actor_mod.register_actor(prosperon.id, turn, prosperon.args.main) if (prosperon.args.overling) overling = json.decode(prosperon.args.overling) @@ -626,6 +633,8 @@ if (!prosperon.args.program) if (typeof prosperon.args.program !== 'string') prosperon.args.program = 'main.js'; + +actor_mod.setname(prosperon.args.program) function destroyself() { console.log(`Got the message to destroy self.`) @@ -699,12 +708,10 @@ function enet_check() { if (portal) portal.service(handle_host) - send_messages(); - $_.delay(enet_check, service_delay); } -enet_check(); +//enet_check(); // Finally, run the program var prog = io.slurp(prosperon.args.program) @@ -712,5 +719,7 @@ var prog_script = `(function ${prosperon.args.program.name()}($_) { ${prog} })` var val = js.eval(prosperon.args.program, prog_script)($_) if (val) throw new Error('Program must not return anything'); + +send_messages() })() \ No newline at end of file diff --git a/scripts/modules/rasterize.js b/scripts/modules/rasterize.js index e69b4efa..3ced0429 100644 --- a/scripts/modules/rasterize.js +++ b/scripts/modules/rasterize.js @@ -18,7 +18,7 @@ function within_wedge(dx, dy, start, end, full_circle) { return t >= start || t <= end } -rasterize.ellipse = function(pos, radii, opt) { +rasterize.ellipse = function ellipse(pos, radii, opt) { opt = opt || {} var rx = radii[0], ry = radii[1] if (rx <= 0 || ry <= 0) return [] @@ -104,11 +104,11 @@ rasterize.ellipse = function(pos, radii, opt) { return {type: 'rects', data: strips} } -rasterize.circle = function(pos, radius, opt) { +rasterize.circle = function circle(pos, radius, opt) { return rasterize.ellipse(pos, [radius, radius], opt) } -rasterize.outline_rect = function(rect, thickness) { +rasterize.outline_rect = function outline_rect(rect, thickness) { if (thickness <= 0) { return {type: 'rect', data: rect} } @@ -133,7 +133,7 @@ rasterize.outline_rect = function(rect, thickness) { ]} } -rasterize.round_rect = function(rect, radius, thickness) { +rasterize.round_rect = function round_rect(rect, radius, thickness) { thickness = thickness || 1 if (thickness <= 0) { @@ -187,7 +187,7 @@ rasterize.round_rect = function(rect, radius, thickness) { return {type: 'rects', data: rects.concat(strips)} } -rasterize.fill_round_rect = function(rect, radius) { +rasterize.fill_round_rect = function fill_round_rect(rect, radius) { radius = Math.min(radius, rect.width >> 1, rect.height >> 1) var x0 = rect.x, diff --git a/source/jsffi.c b/source/jsffi.c index 448e601a..fbfc1f6e 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -1487,10 +1487,6 @@ JS_SetPrototype(js, js_##NAME, PARENT); \ JSValue js_layout_use(JSContext *js); JSValue js_miniz_use(JSContext *js); -#ifdef TRACY_ENABLE -JSValue js_tracy_use(JSContext *js); -#endif - JSValue js_graphics_use(JSContext *js) { JSValue mod = JS_NewObject(js); JS_SetPropertyFunctionList(js,mod,js_graphics_funcs,countof(js_graphics_funcs)); @@ -1573,10 +1569,6 @@ void ffi_load(JSContext *js) arrput(rt->module_registry, MISTLINE(sprite)); arrput(rt->module_registry, MISTLINE(transform)); -#ifdef TRACY_ENABLE - arrput(rt->module_registry, MISTLINE(tracy)); -#endif - #ifndef NSTEAM arrput(rt->module_registry, MISTLINE(steam)); #endif diff --git a/source/prosperon.c b/source/prosperon.c index a726a096..f8e9839a 100644 --- a/source/prosperon.c +++ b/source/prosperon.c @@ -331,7 +331,7 @@ void actor_turn(prosperon_rt *actor, int greedy) { SDL_LockMutex(actor->turn); #ifdef TRACY_ENABLE -// TracyCFiberEnter(actor->id); + TracyCFiberEnter(actor->name); #endif SDL_LockMutex(actor->msg_mutex); @@ -420,7 +420,7 @@ void actor_turn(prosperon_rt *actor, int greedy) END: #ifdef TRACY_ENABLE -// TracyCFiberLeave(actor->id); + TracyCFiberLeave(actor->name); #endif SDL_UnlockMutex(actor->turn); set_actor_state(actor); @@ -428,7 +428,7 @@ void actor_turn(prosperon_rt *actor, int greedy) KILL: #ifdef TRACY_ENABLE -// TracyCFiberLeave(actor->id); + TracyCFiberLeave(actor->id); #endif SDL_UnlockMutex(actor->turn); actor_free(actor); @@ -581,6 +581,56 @@ JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *arg return JS_UNDEFINED; } +// Wrapper struct to keep the array pointer stable +typedef struct { + TracyCZoneCtx *arr; // stb_ds dynamic array +} tracy_stack_t; + +// Global TLS ID for the Tracy stack +static SDL_TLSID tracy_stack_id = {0}; + +// Cleanup function for the wrapper struct +static void tracy_cleanup_stack(void *value) +{ + tracy_stack_t *stack = value; + if (stack) { + arrfree(stack->arr); + free(stack); + } +} + +// Get or initialize the thread-local Tracy stack +static tracy_stack_t *get_tracy_stack(void) +{ + tracy_stack_t *stack = SDL_GetTLS(&tracy_stack_id); + if (!stack) { + stack = malloc(sizeof(tracy_stack_t)); + stack->arr = NULL; // stb_ds starts with NULL + arrsetcap(stack->arr, 5); // Initial capacity + SDL_SetTLS(&tracy_stack_id, stack, tracy_cleanup_stack); + } + return stack; +} + +void tracy_call_hook(JSContext *js, JSValue fn) +{ + tracy_stack_t *stack = get_tracy_stack(); + js_debug debug; + js_debug_info(js, fn, &debug); + + uint64_t srcloc = ___tracy_alloc_srcloc(debug.line, debug.filename, strlen(debug.filename), debug.name, strlen(debug.name), debug.unique); + arrput(stack->arr, ___tracy_emit_zone_begin_alloc(srcloc, 1)); + + free_js_debug_info(js, &debug); +} + +void tracy_end_hook(JSContext *js, JSValue fn) +{ + tracy_stack_t *stack = get_tracy_stack(); + if (arrlen(stack->arr) > 0) + ___tracy_emit_zone_end(arrpop(stack->arr)); +} + void script_startup(prosperon_rt *prt, void (*hook)(JSContext*)) { JSRuntime *rt; @@ -590,6 +640,12 @@ void script_startup(prosperon_rt *prt, void (*hook)(JSContext*)) 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); +#endif + JS_AddIntrinsicBaseObjects(js); JS_AddIntrinsicEval(js); JS_AddIntrinsicRegExp(js); @@ -1440,6 +1496,8 @@ int main(int argc, char **argv) /* Create the initial actor from the main command line. */ create_actor(argc, argv, NULL); + + cores = 1; /* Start the thread that pumps ready actors, one per logical core. */ for (int i = 0; i < cores; i++) { diff --git a/source/prosperon.h b/source/prosperon.h index d5c3e743..e89a3af7 100644 --- a/source/prosperon.h +++ b/source/prosperon.h @@ -67,6 +67,8 @@ typedef struct prosperon_rt { int main_thread_only; JSValue actor_sym; + + const char *name; // human friendly name } prosperon_rt; extern SDL_ThreadID main_thread; diff --git a/source/qjs_actor.c b/source/qjs_actor.c index 619d6c1a..efc46969 100644 --- a/source/qjs_actor.c +++ b/source/qjs_actor.c @@ -156,6 +156,11 @@ JSC_CCALL(os_destroy, rt->need_stop = 1; ) +JSC_SCALL(actor_setname, + prosperon_rt *rt = JS_GetContextOpaque(js); + rt->name = strdup(str); +) + static const JSCFunctionListEntry js_actor_funcs[] = { MIST_FUNC_DEF(os, createactor, 1), MIST_FUNC_DEF(os, mailbox_push, 2), @@ -165,6 +170,7 @@ static const JSCFunctionListEntry js_actor_funcs[] = { MIST_FUNC_DEF(os, register_actor, 2), MIST_FUNC_DEF(os, unneeded, 2), MIST_FUNC_DEF(os, destroy, 0), + MIST_FUNC_DEF(actor, setname, 1), }; JSValue js_actor_use(JSContext *js) { diff --git a/source/qjs_os.c b/source/qjs_os.c index 6c981c64..4eb362da 100644 --- a/source/qjs_os.c +++ b/source/qjs_os.c @@ -260,6 +260,37 @@ static JSValue js_os_rusage(JSContext *js, JSValue self, int argc, JSValue *argv return ret; } +#ifdef TRACY_ENABLE +#include +JSC_CCALL(os_frame, + TracyCFrameMark +) + +JSC_CCALL(os_trace_img, + size_t len; + double width, height; + JS_ToFloat64(js,&width,argv[1]); + JS_ToFloat64(js,&height,argv[2]); + ___tracy_emit_frame_image(JS_GetArrayBuffer(js, &len, argv[0]), width, height, 0, 0); +) + +JSC_CCALL(os_trace_message, + size_t len; + const char *str = JS_ToCStringLen(js, &len, argv[0]); + TracyCMessage(str,len); + JS_FreeCString(js,str); +) +#else +JSC_CCALL(os_frame, +) + +JSC_CCALL(os_trace_img, +) + +JSC_CCALL(os_trace_message, +) +#endif + static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, platform, 0), MIST_FUNC_DEF(os, arch, 0), @@ -275,6 +306,7 @@ static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, rusage, 0), MIST_FUNC_DEF(os, mallinfo, 0), MIST_FUNC_DEF(os, buffer2string, 1), + MIST_FUNC_DEF(os, frame, 0), }; JSValue js_os_use(JSContext *js) { diff --git a/source/qjs_tracy.c b/source/qjs_tracy.c deleted file mode 100644 index 70c9dc71..00000000 --- a/source/qjs_tracy.c +++ /dev/null @@ -1,591 +0,0 @@ -#include -#include -#include -#include -#include "render.h" -#include -#include "stb_ds.h" - -static JSValue js_tracy_fiber_enter(JSContext *js, JSValue self, int argc, JSValue *argv) -{ - return JS_UNDEFINED; -#ifdef TRACY_ON_DEMAND - if (!TracyCIsConnected) { - JS_Call(js,argv[0], JS_UNDEFINED,0,NULL); - return JS_UNDEFINED; - } -#endif - - JSValue jsname = JS_GetPropertyStr(js, argv[0], "name"); - const char *fnname = JS_ToCString(js,jsname); - TracyCFiberEnter(fnname); - JS_Call(js, argv[0], JS_UNDEFINED, 0, NULL); - TracyCFiberLeave(fnname); - JS_FreeCString(js, fnname); - JS_FreeValue(js,jsname); -} - -static JSValue js_tracy_fiber_leave(JSContext *js, JSValue self, int argc, JSValue *argv) -{ - return JS_UNDEFINED; -#ifdef TRACY_ON_DEMAND - if (!TracyCIsConnected) - return JS_UNDEFINED; -#endif - - JSAtom atom = JS_ValueToAtom(js,argv[0]); - const char *str = JS_AtomToCString(js, atom); - TracyCFiberLeave(str); - JS_FreeAtom(js,atom); - return JS_UNDEFINED; -} - -static JSValue js_tracy_plot(JSContext *js, JSValue self, int argc, JSValue *argv) -{ -#ifdef TRACY_ON_DEMAND - if (!TracyCIsConnected) - return JS_UNDEFINED; -#endif - - const char *str = JS_ToCString(js, argv[0]); - double n; - JS_ToFloat64(js, &n, argv[1]); - TracyCPlot(str, n); - JS_FreeCString(js,str); - return JS_UNDEFINED; -} - -static JSValue js_tracy_plot_config(JSContext *js, JSValue self, int argc, JSValue *argv) -{ -#ifdef TRACY_ON_DEMAND - if (!TracyCIsConnected) - return JS_UNDEFINED; -#endif - - const char *str = JS_ToCString(js,argv[0]); - - uint32_t type, color; - - JS_ToUint32(js,&type, argv[1]); - JS_ToUint32(js,&color,argv[4]); - TracyCPlotConfig(str, type, JS_ToBool(js,argv[2]), JS_ToBool(js,argv[3]), color); - JS_FreeCString(js,str); - - return JS_UNDEFINED; -} - -static JSValue js_tracy_frame_mark(JSContext *js, JSValue self, int argc, JSValue *argv) -{ -#ifdef TRACY_ON_DEMAND - if (!TracyCIsConnected) - return JS_UNDEFINED; -#endif - - TracyCFrameMark - - return JS_UNDEFINED; -} - -static JSValue js_tracy_message(JSContext *js, JSValue self, int argc, JSValue *argv) -{ -#ifdef TRACY_ON_DEMAND - if (!TracyCIsConnected) - return JS_UNDEFINED; -#endif - - size_t len; - const char *str = JS_ToCStringLen(js, &len, argv[0]); - TracyCMessage(str,len); - JS_FreeCString(js,str); - return JS_UNDEFINED; -} - -static JSValue js_tracy_zone_begin(JSContext *js, JSValue self, int argc, JSValue *argv) -{ -#ifdef TRACY_ON_DEMAND - if (!TracyCIsConnected) { - JS_Call(js,argv[0], JS_UNDEFINED,0,NULL); - return JS_UNDEFINED; - } -#endif - - const char *fn_src = JS_AtomToCString(js, js_fn_filename(js, argv[0])); - const char *js_fn_name = get_func_name(js, argv[0]); - const char *fn_name; - if (!js_fn_name || js_fn_name[0] == 0) - fn_name = ""; - else - fn_name = js_fn_name; -// TracyCZoneCtx TCTX = ___tracy_emit_zone_begin_alloc(___tracy_alloc_srcloc(js_fn_linenum(js,argv[0]), fn_src, strlen(fn_src), fn_name, strlen(fn_name), (int)fn_src),1); - JS_FreeCString(js,js_fn_name); - - JS_Call(js, argv[0], JS_UNDEFINED, 0, NULL); -// TracyCZoneEnd(TCTX); - return JS_UNDEFINED; -} - -#ifdef SOKOL_GLCORE -#include "glad.h" -GLuint *ids; -static GLsizei query_count = 64*1024; -static int qhead = 0; -static int qtail = 0; - -static JSValue js_tracy_gpu_init(JSContext *js, JSValue self, int argc, JSValue *argv) -{ - printf("GLAD LOAD %d\n", gladLoadGL()); - ids = malloc(sizeof(GLuint)*query_count); - glGenQueries(query_count, ids); // generate new query ids - int64_t tgpu; - glGetInteger64v(GL_TIMESTAMP, &tgpu); - - float period = 1.f; - struct ___tracy_gpu_new_context_data gpuctx = { - .gpuTime = tgpu, - .period = period, - .context = 0, - .flags = 0, - .type = 1 - }; - ___tracy_emit_gpu_new_context(gpuctx); - - return JS_UNDEFINED; -} - -static JSValue js_tracy_gpu_zone_begin(JSContext *js, JSValue self, int argc, JSValue *argv) -{ -#ifdef TRACY_ON_DEMAND - if (!TracyCIsConnected) { - JS_Call(js,argv[0], JS_UNDEFINED, 0, NULL); - return JS_UNDEFINED; - } -#endif - - glQueryCounter(ids[qhead], GL_TIMESTAMP); - struct ___tracy_gpu_zone_begin_data data; - - const char *fn_src = JS_AtomToCString(js, js_fn_filename(js, argv[0])); - const char *js_fn_name = get_func_name(js, argv[0]); - const char *fn_name; - if (!js_fn_name || js_fn_name[0] == 0) - fn_name = ""; - else - fn_name = js_fn_name; - data.srcloc = ___tracy_alloc_srcloc(js_fn_linenum(js,argv[0]), fn_src, strlen(fn_src), fn_name, strlen(fn_name), (int)fn_src); - - data.queryId = ids[qhead]; - data.context = 0; - ___tracy_emit_gpu_zone_begin_alloc(data); - JS_FreeCString(js,js_fn_name); - JS_FreeCString(js, fn_src); - - qhead = (qhead+1)%query_count; - - JSValue ret = JS_Call(js, argv[0], JS_UNDEFINED, 0, NULL); - - glQueryCounter(ids[qhead], GL_TIMESTAMP); - struct ___tracy_gpu_zone_end_data enddata = { - .queryId = ids[qhead], - .context = 0 - }; - ___tracy_emit_gpu_zone_end(enddata); - qhead = (qhead+1)%query_count; - - return ret; -} - -static JSValue js_tracy_gpu_sync(JSContext *js, JSValue self, int argc, JSValue *argv) -{ -#ifdef TRACY_ON_DEMAND - if (!TracyCIsConnected) - return JS_UNDEFINED; -#endif - - int64_t tgpu; - glGetInteger64v(GL_TIMESTAMP, &tgpu); - - struct ___tracy_gpu_time_sync_data data = { - .context = 0, - .gpuTime = tgpu - }; - ___tracy_emit_gpu_time_sync(data); - return JS_UNDEFINED; -} - -static JSValue js_tracy_gpu_collect(JSContext *js, JSValue self, int argc, JSValue *argv) -{ -#ifdef TRACY_ON_DEMAND - if (!TracyCIsConnected) - return JS_UNDEFINED; -#endif - - GLint available; - while (qtail != qhead) { - glGetQueryObjectiv(ids[qtail], GL_QUERY_RESULT_AVAILABLE, &available); - if (!available) return JS_UNDEFINED; - - uint64_t time; - glGetQueryObjectui64v(ids[qtail], GL_QUERY_RESULT, &time); - struct ___tracy_gpu_time_data gtime = { - .gpuTime = time, - .queryId = ids[qtail], - .context = 0 - }; - qtail = (qtail+1)%query_count; - ___tracy_emit_gpu_time(gtime); - } - return JS_UNDEFINED; -} - -static JSValue js_tracy_image(JSContext *js, JSValue self, int argc, JSValue *argv) -{ - size_t len; - double width, height; - JS_ToFloat64(js,&width,argv[1]); - JS_ToFloat64(js,&height,argv[2]); - ___tracy_emit_frame_image(JS_GetArrayBuffer(js, &len, argv[0]), width, height, 0, 0); - return JS_UNDEFINED; -} - -#elif defined(SOKOL_D3D11) -#include -static int query_count = 64*1024; - -ID3D11Device *device = NULL; -ID3D11DeviceContext *context = NULL; -ID3D11RenderTargetView *render_view = NULL; -ID3D11Query *disjoint_query = NULL; -ID3D11Query **queries; - -int qtail = 0; -int qhead = 1; - -static JSValue js_tracy_gpu_init(JSContext *js, JSValue self, int argc, JSValue *argv) -{ - HRESULT hr; - queries = malloc(sizeof(ID3D11Query*) * query_count); - device = sapp_d3d11_get_device(); - context = sapp_d3d11_get_device_context(); - render_view = sapp_d3d11_get_render_view(); - - D3D11_QUERY_DESC disjoint_desc = {0}; - disjoint_desc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT; - - if (FAILED(hr = device->lpVtbl->CreateQuery(device, &disjoint_desc, &disjoint_query))) - return JS_ThrowReferenceError(js,"Failed to create disjoint query, HRESULT: 0x%X\n", hr); - - D3D11_QUERY_DESC timestamp_desc = {0}; - timestamp_desc.Query = D3D11_QUERY_TIMESTAMP; - - for (int i = 0; i < query_count; ++i) - if (FAILED(hr = device->lpVtbl->CreateQuery(device, ×tamp_desc, &queries[i]))) - return JS_ThrowReferenceError(js,"Failed to create query %d, HRESULT: 0x%X\n", i, hr); - - UINT64 tgpu = 0; - D3D11_QUERY_DATA_TIMESTAMP_DISJOINT disjoint_data; - - context->lpVtbl->Begin(context, disjoint_query); - context->lpVtbl->End(context, queries[0]); - context->lpVtbl->End(context, disjoint_query); - while (context->lpVtbl->GetData(context, disjoint_query, &disjoint_data, sizeof(disjoint_data), 0) != S_OK); - while (context->lpVtbl->GetData(context, queries[0], &tgpu, sizeof(tgpu), 0) != S_OK); - - struct ___tracy_gpu_new_context_data gpuctx = { - .gpuTime = tgpu * (1000000000 / disjoint_data.Frequency), - .period = 1.0f, - .context = 0, - .flags = 0, - .type = 2 - }; - ___tracy_emit_gpu_new_context(gpuctx); - - context->lpVtbl->Begin(context, disjoint_query); - qtail = qhead = 0; - - return JS_UNDEFINED; -} - -static JSValue js_tracy_gpu_zone_begin(JSContext *js, JSValue self, int argc, JSValue *argv) -{ -#ifdef TRACY_ON_DEMAND - if (!TracyCIsConnected) { - JS_Call(js,argv[0], JS_UNDEFINED, 0, NULL); - return JS_UNDEFINED; - } -#endif - - context->lpVtbl->End(context, queries[qhead]); - struct ___tracy_gpu_zone_begin_data data; - - const char *fn_src = JS_AtomToCString(js, js_fn_filename(js, argv[0])); - const char *js_fn_name = get_func_name(js, argv[0]); - const char *fn_name; - if (!js_fn_name || js_fn_name[0] == 0) - fn_name = ""; - else - fn_name = js_fn_name; - data.srcloc = ___tracy_alloc_srcloc(js_fn_linenum(js,argv[0]), fn_src, strlen(fn_src), fn_name, strlen(fn_name), (int)fn_src); - - data.queryId = qhead; - data.context = 0; - ___tracy_emit_gpu_zone_begin_alloc(data); - JS_FreeCString(js,js_fn_name); - JS_FreeCString(js, fn_src); - qhead = (qhead+1)%query_count; - - JSValue ret = JS_Call(js, argv[0], JS_UNDEFINED, 0, NULL); - context->lpVtbl->End(context, queries[qhead]); - struct ___tracy_gpu_zone_end_data enddata = { - .queryId = qhead, - .context = 0 - }; - ___tracy_emit_gpu_zone_end(enddata); - qhead = (qhead+1)%query_count; - - return ret; -} - -static JSValue js_tracy_gpu_sync(JSContext *js, JSValue self, int argc, JSValue *argv) -{ -#ifdef TRACY_ON_DEMAND - if (!TracyCIsConnected) - return JS_UNDEFINED; -#endif - - return JS_UNDEFINED; -} - -static JSValue js_tracy_gpu_collect(JSContext *js, JSValue self, int argc, JSValue *argv) -{ -#ifdef TRACY_ON_DEMAND - if (!TracyCIsConnected) - return JS_UNDEFINED; -#endif - HRESULT hr; - // Use the current head to get a sync value - context->lpVtbl->End(context, queries[qhead]); - context->lpVtbl->End(context, disjoint_query); - - D3D11_QUERY_DATA_TIMESTAMP_DISJOINT disjoint_data; - while(context->lpVtbl->GetData(context, disjoint_query, &disjoint_data, sizeof(disjoint_data), 0) != S_OK); - - if (disjoint_data.Disjoint) { - qtail = qhead; - return JS_ThrowReferenceError(js,"disjoint timestamps detected; dropping."); - } - -/* uint64_t synctime = 0; - while (context->lpVtbl->GetData(context, queries[qhead], &synctime, sizeof(synctime), 0) != S_OK); - ___tracy_emit_gpu_time_sync((struct ___tracy_gpu_time_sync_data) { - .context = 0, - .gpuTime = synctime * 1000000000 / disjoint_data.Frequency - }); -*/ - - while (qtail != qhead) { - uint64_t time = 0; - while(context->lpVtbl->GetData(context, queries[qtail], &time, sizeof(time), 0) != S_OK); - - struct ___tracy_gpu_time_data gtime = { - .gpuTime = time * (1000000000 / disjoint_data.Frequency), - .queryId = qtail, - .context = 0 - }; - qtail = (qtail+1)%query_count; - ___tracy_emit_gpu_time(gtime); - } - - qtail = qhead; - context->lpVtbl->Begin(context, disjoint_query); - - return JS_UNDEFINED; -} - -#include -#include - -static JSValue js_tracy_image(JSContext *js, JSValue self, int argc, JSValue *argv) -{ - if (!TracyCIsConnected) return JS_UNDEFINED; - - HRESULT hr; - // create staging texture - // Get render target texture - ID3D11Texture2D *render_target_tex = NULL; - render_view->lpVtbl->GetResource(sapp_d3d11_get_render_view(), &render_target_tex); -// if (FAILED(hr)) return JS_ThrowReferenceError(js, "Failed to query texture from render target"); - - D3D11_TEXTURE2D_DESC render_tex_desc; - render_target_tex->lpVtbl->GetDesc(render_target_tex, &render_tex_desc); - - render_tex_desc.Usage = D3D11_USAGE_STAGING; - render_tex_desc.BindFlags = 0; - render_tex_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - render_tex_desc.MiscFlags = 0; - render_tex_desc.SampleDesc = (DXGI_SAMPLE_DESC){ .Count = 1, .Quality = 0, }; - - ID3D11Texture2D *staging_tex = NULL; - hr = device->lpVtbl->CreateTexture2D(device, &render_tex_desc, NULL, &staging_tex); - if (FAILED(hr)) return JS_ThrowReferenceError(js, "Failed to create staging texture: 0x%08X\n", hr); - - context->lpVtbl->CopyResource(context, staging_tex, render_target_tex); - - D3D11_MAPPED_SUBRESOURCE mapped_res; - hr = context->lpVtbl->Map(context, staging_tex, 0, D3D11_MAP_READ, 0, &mapped_res); - if (FAILED(hr)) return JS_ThrowReferenceError(js, "Failed to map subresource"); - -// resize - int newwidth = 320; - int newheight = 180; - void *newdata = malloc(320*180*4); - stbir_resize_uint8_linear(mapped_res.pData, render_tex_desc.Width, render_tex_desc.Height, 0, newdata, newwidth, newheight, 0, 4); - - ___tracy_emit_frame_image(newdata, newwidth, newheight, 0, 0); - - context->lpVtbl->Unmap(context, staging_tex, 0); - staging_tex->lpVtbl->Release(staging_tex); - render_target_tex->lpVtbl->Release(render_target_tex); - free(newdata); - - return JS_UNDEFINED; -} - -#else - -static JSValue js_tracy_gpu_init(JSContext *js, JSValue self, int argc, JSValue *argv) -{ - return JS_UNDEFINED; -} - -static JSValue js_tracy_gpu_zone_begin(JSContext *js, JSValue self, int argc, JSValue *argv) -{ - return JS_Call(js,argv[0], JS_UNDEFINED, 0, NULL); -} - -static JSValue js_tracy_gpu_sync(JSContext *js, JSValue self, int argc, JSValue *argv) -{ - return JS_UNDEFINED; -} - -static JSValue js_tracy_gpu_collect(JSContext *js, JSValue self, int argc, JSValue *argv) -{ - return JS_UNDEFINED; -} - -static JSValue js_tracy_image(JSContext *js, JSValue self, int argc, JSValue *argv) -{ -/* SDL_Surface *img = js2SDL_Surface(js,argv[0]); - SDL_Surface *scaled = SDL_ScaleSurface(img, 320,180,SDL_SCALEMODE_LINEAR); - ___tracy_emit_frame_image(scaled->pixels, scaled->w,scaled->h, 0,0); - SDL_DestroySurface(scaled);*/ - return JS_UNDEFINED; -} - -#endif - -// Wrapper struct to keep the array pointer stable -typedef struct { - TracyCZoneCtx *arr; // stb_ds dynamic array -} tracy_stack_t; - -// Global TLS ID for the Tracy stack -static SDL_TLSID tracy_stack_id = {0}; - -// Cleanup function for the wrapper struct -static void tracy_cleanup_stack(void *value) -{ - tracy_stack_t *stack = value; - if (stack) { - arrfree(stack->arr); - free(stack); - } -} - -// Get or initialize the thread-local Tracy stack -static tracy_stack_t *get_tracy_stack(void) -{ - tracy_stack_t *stack = SDL_GetTLS(&tracy_stack_id); - if (!stack) { - stack = malloc(sizeof(tracy_stack_t)); - stack->arr = NULL; // stb_ds starts with NULL - arrsetcap(stack->arr, 5); // Initial capacity - SDL_SetTLS(&tracy_stack_id, stack, tracy_cleanup_stack); - } - return stack; -} - -void tracy_call_hook(JSContext *js, JSValue fn) -{ - tracy_stack_t *stack = get_tracy_stack(); - js_debug debug; - js_debug_info(js, fn, &debug); - - uint64_t srcloc = ___tracy_alloc_srcloc(debug.line, debug.filename, strlen(debug.filename), debug.name, strlen(debug.name), debug.unique); - arrput(stack->arr, ___tracy_emit_zone_begin_alloc(srcloc, 1)); - - free_js_debug_info(js, &debug); -} - -void tracy_end_hook(JSContext *js, JSValue fn) -{ - tracy_stack_t *stack = get_tracy_stack(); - if (arrlen(stack->arr) > 0) - ___tracy_emit_zone_end(arrpop(stack->arr)); -} - -JSValue js_tracy_level(JSContext *js, JSValue self, int argc, JSValue *argv) -{ - int32_t level; - JS_ToInt32(js,&level, argv[0]); - if (JS_IsUndefined(argv[0]) || level == 0) { - js_debug_sethook(js,NULL,JS_HOOK_CALL); - js_debug_sethook(js,NULL,JS_HOOK_RET); - } else { - js_debug_sethook(js, tracy_call_hook, JS_HOOK_CALL); - js_debug_sethook(js, tracy_end_hook, JS_HOOK_RET); - } - - return JS_UNDEFINED; -} - -static const JSCFunctionListEntry js_tracy_funcs[] = { - JS_CFUNC_DEF("fiber", 1, js_tracy_fiber_enter), - JS_CFUNC_DEF("fiber_leave", 1, js_tracy_fiber_leave), - JS_CFUNC_DEF("gpu_zone", 1, js_tracy_gpu_zone_begin), - JS_CFUNC_DEF("gpu_collect", 1, js_tracy_gpu_collect), - JS_CFUNC_DEF("gpu_init", 0, js_tracy_gpu_init), - JS_CFUNC_DEF("gpu_sync", 0, js_tracy_gpu_sync), - JS_CFUNC_DEF("end_frame", 0, js_tracy_frame_mark), - JS_CFUNC_DEF("zone", 1, js_tracy_zone_begin), - JS_CFUNC_DEF("message", 1, js_tracy_message), - JS_CFUNC_DEF("plot", 2, js_tracy_plot), - JS_CFUNC_DEF("image", 3, js_tracy_image), - JS_CFUNC_DEF("level", 1, js_tracy_level), - JS_CFUNC_DEF("plot_config", 5, js_tracy_plot_config), -}; - -JSValue js_tracy_use(JSContext *js) -{ - JSValue expy = JS_NewObject(js); - JS_SetPropertyFunctionList(js, expy, js_tracy_funcs, sizeof(js_tracy_funcs)/sizeof(JSCFunctionListEntry)); - return expy; -} - -static int js_tracy_init(JSContext *js, JSModuleDef *m) { - js_tracy_use(js); - JS_SetModuleExportList(js, m, js_tracy_funcs, sizeof(js_tracy_funcs)/sizeof(JSCFunctionListEntry)); - return 0; -} - -#ifdef JS_SHARED_LIBRARY -#define JS_INIT_MODULE js_init_module -#else -#define JS_INIT_MODULE js_init_module_tracy -#endif - -JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) { - JSModuleDef *m = JS_NewCModule(ctx, module_name, js_tracy_init); - if (!m) return NULL; - JS_AddModuleExportList(ctx, m, js_tracy_funcs, sizeof(js_tracy_funcs)/sizeof(JSCFunctionListEntry)); - return m; -} diff --git a/tests/moth.js b/tests/moth.js index ab35ff75..a722d2de 100644 --- a/tests/moth.js +++ b/tests/moth.js @@ -7,7 +7,6 @@ var os = use('os'); var io = use('io'); var transform = use('transform'); var rasterize = use('rasterize'); - var video_actor = use('sdl_video') var window @@ -119,6 +118,7 @@ function translate_draw_commands(commands) { case "draw_circle": case "draw_ellipse": + break // Rasterize ellipse to points or rects var radii = cmd.radii || [cmd.radius, cmd.radius] var raster_result = rasterize.ellipse(cmd.pos, radii, cmd.opt || {}) @@ -177,6 +177,7 @@ function translate_draw_commands(commands) { function loop() { + os.frame() var now = os.now() var dt = now - last last = now @@ -212,9 +213,10 @@ function loop() id: render, op: "batch", data: batch_commands + }, _ => { + var diff = os.now() - now + $_.delay(loop,1/240) }) }) }) - - $_.delay(loop, 1/60) }