no longer need to send ids to window renderer; per actor config

This commit is contained in:
2025-06-03 16:13:54 -05:00
parent 709f2459e4
commit c887bcf7b9
12 changed files with 112 additions and 213 deletions

View File

@@ -1,21 +1,13 @@
sdl_video = "main"
[dependencies]
extramath = "https://gitea.pockle.world/john/extramath@master"
[system]
# seconds before idle actor reclamation
ar_timer = 60
# MB of memory an actor can use; 0 for unbounded
actor_memory = 0
# seconds per net service pull
net_service = 0.1
# seconds to hold callback for reply messages; 0 for unbounded
reply_timeout = 60
# max number of simultaneous actors
actor_max = 10_000
# MB of memory each actor's stack can grow to
actor_memory = 0
net_service = 0.1
reply_timeout = 60
actor_max = "10_000"
stack_max = 0
[actors]
[actors.prosperon/sdl_video]
main = true

View File

@@ -5,6 +5,8 @@ var draw2d = use('prosperon/draw2d')
var blob = use('blob')
log.console("HERE")
/*──── import our pieces + systems ───────────────────────────────────*/
var Grid = use('grid'); // your new ctor
var MovementSystem = use('movement').MovementSystem;
@@ -277,7 +279,6 @@ function draw()
draw2d.clear()
drawBoard()
drawPieces()
draw2d.text("HELL", {x: 100, y: 100}, 'fonts/c64.ttf', 16, [1,1,1,1])
return draw2d.get_commands()
}

View File

@@ -7,7 +7,6 @@ rectangle packing, etc.
`
var renderer_actor = arg[0]
var renderer_id = arg[1]
var io = use('io')
var time = use('time')
@@ -43,7 +42,6 @@ Object.defineProperties(graphics.Image.prototype, {
// Send message to load texture
send(renderer_actor, {
kind: "renderer",
id: renderer_id,
op: "loadTexture",
data: this[CPU]
}, function(response) {
@@ -349,7 +347,6 @@ graphics.get_font = function get_font(path, size) {
// Load font texture via renderer actor (async)
send(renderer_actor, {
kind: "renderer",
id: renderer_id,
op: "loadTexture",
data: font.surface
}, function(response) {

View File

@@ -2,7 +2,28 @@ var os = use('os');
var io = use('io');
var transform = use('transform');
var rasterize = use('rasterize');
var video_actor = use('sdl_video')
var game = args[0]
var video
$_.start(e => {
if (e.type !== 'greet') return
video = e.actor
graphics = use('graphics', video)
send(video, {kind:"window", op:"makeRenderer"}, e => {
$_.start(e => {
if (gameactor) return
gameactor = e.actor
loop()
}, args[0])
})
}, 'prosperon/sdl_video', {
title: "Prosperon",
width:500,
height:500
})
var input = use('input')
input.watch($_)
@@ -77,49 +98,9 @@ var cammy = util.camera_globals(camera)
var graphics
var window
var render
var gameactor
var game = args[0]
$_.start(e => {
if (gameactor) return
gameactor = e.actor
loop()
}, args[0])
send(video_actor, {
kind: "window",
op:"create",
data: {
title: "Moth Test",
width: 500,
height: 500
}
}, e => {
if (e.error) {
log.error(e.error)
os.exit(1)
}
window = e.id
send(video_actor,{
kind:"window",
op:"makeRenderer",
id:window
}, e => {
if (e.error) {
log.error(e.error)
os.exit(1)
}
render = e.id
graphics = use('graphics', video_actor, e.id)
})
})
var last = os.now()
@@ -262,8 +243,9 @@ function translate_draw_commands(commands) {
return renderer_commands
}
function loop()
function loop(time)
{
$_.delay(loop, 1/60)
os.frame()
var now = os.now()
var dt = now - last
@@ -295,9 +277,8 @@ function loop()
op: "present"
})
send(video_actor, {
send(video, {
kind: "renderer",
id: render,
op: "batch",
data: batch_commands
}, _ => {
@@ -320,14 +301,13 @@ function loop()
// Calculate average FPS
var avg_fps = fps_sum / fps_samples.length
}
loop()
})
})
})
}
$_.receiver(e => {
log.console(json.encode(e))
if (e.type === 'quit')
$_.stop()

View File

@@ -1,3 +1,5 @@
var io = use('io')
Object.defineProperty(Function.prototype, "hashify", {
value: function () {
var hash = new Map()

View File

@@ -1,8 +1,12 @@
var video = use('sdl_video');
// SDL Video Actor
// This actor runs on the main thread and handles all SDL video operations
var surface = use('surface')
var surface = use('surface');
var ren
var win
// Default window configuration - documents all available window options
var default_window = {
// Basic properties
title: "Prosperon Window",
@@ -53,10 +57,11 @@ var default_window = {
textInput: true, // Enable text input on creation
};
var config = Object.assign({}, default_window, arg[0] || {});
win = new video.window(config);
// Resource tracking
var resources = {
window: {},
renderer: {},
texture: {},
surface: {},
cursor: {}
@@ -115,26 +120,10 @@ $_.receiver(function(msg) {
// Window operations
function handle_window(msg) {
// Special case: create doesn't need an existing window
if (msg.op === 'create') {
var config = Object.assign({}, default_window, msg.data || {});
var id = allocate_id();
var window = new prosperon.endowments.window(config);
resources.window[id] = window;
return {id: id, data: {size: window.size}};
}
// All other operations require a valid window ID
if (!msg.id || !resources.window[msg.id]) {
return {error: "Invalid window id: " + msg.id};
}
var win = resources.window[msg.id];
switch (msg.op) {
case 'destroy':
win.destroy();
delete resources.window[msg.id];
win = undefined
return {success: true};
case 'show':
@@ -211,10 +200,10 @@ function handle_window(msg) {
return {success: true};
case 'makeRenderer':
var renderer = win.make_renderer();
var renderer_id = allocate_id();
resources.renderer[renderer_id] = renderer;
return {id: renderer_id};
if (ren)
return {reason: "Already made a renderer"}
ren = win.make_renderer()
return {success:true};
default:
return {error: "Unknown window operation: " + msg.op};
@@ -223,33 +212,11 @@ function handle_window(msg) {
// Renderer operations
function handle_renderer(msg) {
// Special case: createWindowAndRenderer creates both
if (msg.op === 'createWindowAndRenderer') {
var data = msg.data || {};
var result = prosperon.endowments.createWindowAndRenderer(
data.title || "Prosperon Window",
data.width || 640,
data.height || 480,
data.flags || 0
);
var win_id = allocate_id();
var ren_id = allocate_id();
resources.window[win_id] = result.window;
resources.renderer[ren_id] = result.renderer;
return {window_id: win_id, renderer_id: ren_id};
}
// All other operations require a valid renderer ID
if (!msg.id || !resources.renderer[msg.id]) {
return {error: "Invalid renderer id: " + msg.id};
}
var ren = resources.renderer[msg.id];
if (!ren) return{reason:'no renderer!'}
switch (msg.op) {
case 'destroy':
delete resources.renderer[msg.id];
// Renderer is automatically destroyed when all references are gone
ren = undefined
return {success: true};
case 'clear':
@@ -268,22 +235,6 @@ function handle_renderer(msg) {
var prop = msg.data ? msg.data.property : null;
if (!prop) return {error: "Missing property name"};
// Handle special cases
if (prop === 'window') {
var win = ren.window;
if (!win) return {data: null};
// Find window ID
for (var id in resources.window) {
if (resources.window[id] === win) {
return {data: id};
}
}
// Window not tracked, add it
var win_id = allocate_id();
resources.window[win_id] = win;
return {data: win_id};
}
// Handle special getters that might return objects
if (prop === 'drawColor') {
var color = ren[prop];
@@ -300,6 +251,8 @@ function handle_renderer(msg) {
var value = msg.value
if (!prop) return {error: "Missing property name"};
if (!value) return {error: "No value to set"}
// Validate property is settable
var readonly = ['window', 'name', 'outputSize', 'currentOutputSize', 'logicalPresentationRect', 'safeArea'];
if (readonly.indexOf(prop) !== -1) {
@@ -462,29 +415,11 @@ function handle_renderer(msg) {
return {data: ren.coordsToWindow(msg.data.pos)};
case 'batch':
// Execute a batch of operations
if (!msg.data || !Array.isArray(msg.data)) return {error: "Missing or invalid data array"};
var results = [];
for (var i = 0; i < msg.data.length; i++) {
var cmd = msg.data[i];
if (!cmd.op) {
results.push({error: "Command at index " + i + " missing op"});
continue;
}
// Create a temporary message object for the command
var temp_msg = {
kind: 'renderer',
id: msg.id,
op: cmd.op,
prop: cmd.prop,
value: cmd.value,
data: cmd.data
};
// Recursively call handle_renderer for each command
var result = handle_renderer(temp_msg);
var result = handle_renderer(msg.data[i]);
results.push(result);
}
@@ -510,11 +445,11 @@ function handle_texture(msg) {
if (msg.data.surface_id) {
var surf = resources.surface[msg.data.surface_id];
if (!surf) return {error: "Invalid surface id"};
tex = new prosperon.endowments.texture(renderer, surf);
tex = new video.texture(renderer, surf);
}
// Create from properties
else if (msg.data.width && msg.data.height) {
tex = new prosperon.endowments.texture(renderer, {
tex = new video.texture(renderer, {
width: msg.data.width,
height: msg.data.height,
format: msg.data.format || 'rgba8888',
@@ -611,7 +546,7 @@ function handle_cursor(msg) {
var surf = new surface(msg.data)
var hotspot = msg.data.hotspot || [0, 0];
var cursor = prosperon.endowments.createCursor(surf, hotspot);
var cursor = video.createCursor(surf, hotspot);
var cursor_id = allocate_id();
resources.cursor[cursor_id] = cursor;
@@ -622,7 +557,7 @@ function handle_cursor(msg) {
if (msg.id && resources.cursor[msg.id]) {
cursor = resources.cursor[msg.id];
}
prosperon.endowments.setCursor(cursor);
video.setCursor(cursor);
return {success: true};
case 'destroy':
@@ -642,7 +577,7 @@ prosperon.endowments = prosperon.endowments || {};
// Mouse operations
function handle_mouse(msg) {
var mouse = prosperon.endowments.mouse;
var mouse = video.mouse;
switch (msg.op) {
case 'show':
@@ -736,7 +671,7 @@ function handle_mouse(msg) {
// Keyboard operations
function handle_keyboard(msg) {
var keyboard = prosperon.endowments.keyboard;
var keyboard = video.keyboard;
switch (msg.op) {
case 'get_state':

View File

@@ -256,6 +256,24 @@ globalThis.json = use('json')
globalThis.text = use('text')
var time = use('time')
// Load actor-specific configuration
function load_actor_config(program) {
// Extract actor name from program path
// e.g., "prosperon/_sdl_video" or "extramath/spline"
var actor_name = program
if (program.includes('/')) {
actor_name = program
}
if (config.actors && config.actors[actor_name]) {
// Merge actor config into cell.args
for (var key in config.actors[actor_name]) {
log.console(`setting ${key}`)
cell.args[key] = config.actors[actor_name][key]
}
}
}
var blob = use('blob')
function deepFreeze(object) {
@@ -627,6 +645,10 @@ function turn(msg)
send_messages()
}
load_actor_config(cell.args.program)
log.console(`actor ${cell.args.program} is ${cell.args.main}`)
actor_mod.register_actor(cell.id, turn, cell.args.main, config.system.ar_timer)
if (config.system.actor_memory)
@@ -751,6 +773,9 @@ function enet_check()
// Finally, run the program
actor_mod.setname(cell.args.program)
// Load actor-specific configuration before running
var prog = null
var progPath = cell.args.program

View File

@@ -36,6 +36,7 @@ if (io.exists(cell_man)) {
log.console(" vendor Copy all dependencies locally")
log.console(" build Compile all modules to bytecode")
log.console(" patch Create a patch for a module")
log.console(" config Manage system and actor configurations")
log.console(" help Show this help message")
log.console("")
log.console("Run 'cell help <command>' for more information on a command.")

View File

@@ -323,7 +323,7 @@ int prosperon_mount_core(void)
return ret;
}
cell_rt *create_actor(void *wota, void (*hook)(JSContext*))
cell_rt *create_actor(void *wota)
{
cell_rt *actor = calloc(sizeof(*actor), 1);
actor->init_wota = wota;
@@ -341,7 +341,7 @@ cell_rt *create_actor(void *wota, void (*hook)(JSContext*))
/* Lock actor->mutex while initializing JS runtime. */
SDL_LockMutex(actor->mutex);
script_startup(actor, hook);
script_startup(actor);
SDL_UnlockMutex(actor->mutex);
set_actor_state(actor);
@@ -702,7 +702,7 @@ static int actor_interrupt_cb(JSRuntime *rt, cell_rt *crt)
return 0;
}
void script_startup(cell_rt *prt, void (*hook)(JSContext*))
void script_startup(cell_rt *prt)
{
JSRuntime *rt;
#ifdef TRACY_ENABLE
@@ -747,10 +747,6 @@ void script_startup(cell_rt *prt, void (*hook)(JSContext*))
PHYSFS_close(eng);
data[stat.filesize] = 0;
/* Call hook function if provided before evaluating engine */
if (hook)
hook(js);
/* Called with actor->mutex locked by create_actor(). */
JSValue v = JS_Eval(js, data, (size_t)stat.filesize, ENGINE, JS_EVAL_FLAG_STRICT);
uncaught_exception(js, v);
@@ -1582,7 +1578,7 @@ int main(int argc, char **argv)
wota_write_array(&startwota, actor_argc - 1);
for (int i = 1; i < actor_argc; i++)
wota_write_text(&startwota, actor_argv[i]);
root_cell = create_actor(startwota.data, NULL);
root_cell = create_actor(startwota.data);
/* Launch runner threads */
for (int i = 0; i < cores; i++) {

View File

@@ -79,7 +79,7 @@ extern SDL_TLSID prosperon_id;
extern cell_rt *root_cell; // first actor in the system
cell_rt *create_actor(void *wota, void (*hook)(JSContext*));
cell_rt *create_actor(void *wota);
const char *register_actor(const char *id, cell_rt *actor, int mainthread, double ar);
void actor_disrupt(cell_rt *actor);
@@ -87,7 +87,7 @@ const char *send_message(const char *id, void *msg);
Uint32 actor_timer_cb(cell_rt *actor, SDL_TimerID id, Uint32 interval);
JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv);
JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *argv);
void script_startup(cell_rt *rt, void (*hook)(JSContext*));
void script_startup(cell_rt *rt);
void script_evalf(JSContext *js, const char *format, ...);
JSValue script_eval(JSContext *js, const char *file, const char *script);
int uncaught_exception(JSContext *js, JSValue v);

View File

@@ -75,7 +75,7 @@ JSValue actor2js(JSContext *js, cell_rt *actor)
JSC_CCALL(os_createactor,
void *startup = value2wota(js, argv[0], JS_UNDEFINED);
create_actor(startup, NULL);
create_actor(startup);
)
JSC_CCALL(os_mailbox_push,

View File

@@ -1744,12 +1744,13 @@ JSC_CCALL(sdl_createWindowAndRenderer,
return ret;
)
// Hook function to set up endowments for the video actor
static void video_actor_hook(JSContext *js) {
// Get prosperon object
JSValue global = JS_GetGlobalObject(js);
JSValue prosperon = JS_GetPropertyStr(js, global, "prosperon");
JS_FreeValue(js,global);
#include "qjs_wota.h"
JSValue js_sdl_video_use(JSContext *js) {
if (!SDL_Init(SDL_INIT_VIDEO))
return JS_ThrowInternalError(js, "Unable to initialize video subsystem: %s", SDL_GetError());
JSValue ret = JS_NewObject(js);
// Initialize classes
QJSCLASSPREP_FUNCS(SDL_Window)
@@ -1769,50 +1770,19 @@ static void video_actor_hook(JSContext *js) {
// Set prototype on constructor
JS_SetConstructor(js, texture_ctor, SDL_Texture_proto);
// Get or create endowments object
JSValue endowments = JS_GetPropertyStr(js, prosperon, "endowments");
if (JS_IsUndefined(endowments)) {
endowments = JS_NewObject(js);
JS_SetPropertyStr(js, prosperon, "endowments", JS_DupValue(js, endowments));
}
// Set constructors in endowments
JS_SetPropertyStr(js, endowments, "window", window_ctor);
JS_SetPropertyStr(js, endowments, "texture", texture_ctor);
JS_SetPropertyStr(js, ret, "window", window_ctor);
JS_SetPropertyStr(js, ret, "texture", texture_ctor);
// Add utility function
JS_SetPropertyStr(js, endowments, "createWindowAndRenderer",
JS_SetPropertyStr(js, ret, "createWindowAndRenderer",
JS_NewCFunction(js, js_sdl_createWindowAndRenderer, "createWindowAndRenderer", 4));
// Add cursor functions
JS_SetPropertyStr(js, endowments, "createCursor",
JS_NewCFunction(js, js_sdl_create_cursor, "createCursor", 2));
JS_SetPropertyStr(js, endowments, "setCursor",
JS_NewCFunction(js, js_sdl_set_cursor, "setCursor", 1));
JS_SetPropertyStr(js, ret, "createCursor",
JS_NewCFunction(js, js_sdl_create_cursor, "createCursor", 2));
JS_SetPropertyStr(js, ret, "setCursor",
JS_NewCFunction(js, js_sdl_set_cursor, "setCursor", 1));
JS_FreeValue(js, endowments);
JS_FreeValue(js, prosperon);
}
#include "qjs_wota.h"
JSValue js_sdl_video_use(JSContext *js) {
if (!SDL_Init(SDL_INIT_VIDEO))
return JS_ThrowInternalError(js, "Unable to initialize video subsystem: %s", SDL_GetError());
// Generate a unique ID for the video actor
char id[64];
snprintf(id, sizeof(id), "video_%llu", (unsigned long long)SDL_GetTicks());
JSValue startup = JS_NewObject(js);
JS_SetPropertyStr(js,startup, "id", JS_NewStringLen(js,id,64));
JS_SetPropertyStr(js,startup, "program", JS_NewString(js,"prosperon/_sdl_video"));
JS_SetPropertyStr(js,startup,"main",JS_NewBool(js,1));
void *wota = value2wota(js,startup, JS_UNDEFINED);
JS_FreeValue(js,startup);
cell_rt *actor = create_actor(wota, video_actor_hook);
return actor2js(js,actor);
return ret;
}