fix updated render loop

This commit is contained in:
2025-06-05 12:04:45 -05:00
parent 79d5412fe6
commit b52edb2746
7 changed files with 151 additions and 100 deletions

View File

@@ -11,3 +11,7 @@ stack_max = 0
[actors]
[actors.prosperon/sdl_video]
main = true
[actors.prosperon/prosperon]
main = true
[actors.prosperon]
main = false

View File

@@ -186,6 +186,14 @@ else
deps += miniz_dep
endif
libuv_dep = dependency('libuv', static: true, required: false)
if not libuv_dep.found()
message('⚙ System libuv not found, building subproject...')
deps += dependency('libuv', static:true, fallback: ['libuv', 'libuv_dep'])
else
deps += libuv_dep
endif
# Try to find system-installed physfs first
physfs_dep = dependency('physfs', static: true, required: false)
if not physfs_dep.found()

View File

@@ -2,6 +2,7 @@ var os = use('os');
var io = use('io');
var transform = use('transform');
var rasterize = use('rasterize');
var time = use('time')
var game = args[0]
@@ -10,13 +11,13 @@ var video
$_.start(e => {
if (e.type !== 'greet') return
video = e.actor
loop()
graphics = use('graphics', video)
send(video, {kind:"window", op:"makeRenderer"}, e => {
log.console("MADE A WINDOW! so now renderer")
$_.start(e => {
if (gameactor) return
gameactor = e.actor
loop()
}, args[0])
})
}, 'prosperon/sdl_video', {
@@ -25,6 +26,7 @@ $_.start(e => {
height:500
})
var input = use('input')
var geometry = use('geometry')
@@ -98,7 +100,7 @@ var graphics
var gameactor
var last = os.now()
var last = time.number()
// FPS tracking
var fps_samples = []
@@ -241,14 +243,21 @@ function translate_draw_commands(commands) {
function loop(time)
{
$_.delay(loop, 1/60)
os.frame()
var now = os.now()
var dt = now - last
last = now
log.console("LOOP")
send(video, {kind:'input', op:'get'}, e => {
for (var event of e) {
if (event.type === 'quit')
$_.stop()
}
log.console(json.encode(e))
})
if (!gameactor) {
$_.clock(loop)
return
}
// Update the game
send(gameactor, {kind:'update', dt:dt}, e => {
send(gameactor, {kind:'update', dt:1/60}, e => {
// Get draw commands from game
send(gameactor, {kind:'draw'}, draw_commands => {
var batch_commands = []
@@ -278,25 +287,7 @@ function loop(time)
op: "batch",
data: batch_commands
}, _ => {
var diff = os.now() - now
// 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
}
$_.clock(loop)
})
})
})

View File

@@ -3,6 +3,7 @@ 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 input = use('input')
var ren
var win
@@ -60,6 +61,13 @@ var default_window = {
var config = Object.assign({}, default_window, arg[0] || {});
win = new video.window(config);
log.console(win.title)
log.console(win.size)
log.console(win.visible)
log.console(win.minimized)
log.console(win.position)
win.maximized = true
// Resource tracking
var resources = {
texture: {},
@@ -107,6 +115,9 @@ $_.receiver(function(msg) {
case 'keyboard':
response = handle_keyboard(msg);
break;
case 'input':
response = input.get_events();
break;
default:
response = {error: "Unknown kind: " + msg.kind};
}

View File

@@ -352,7 +352,11 @@ $_.random[cell.DOC] = "returns a number between 0 and 1. There is a 50% chance t
$_.random_fit = crypto.random_fit
$_.clock = function(fn) { return os.now() }
$_.clock = function(fn) {
$_.delay(_ => {
fn(time.number())
}, 0)
}
$_.clock[cell.DOC] = "takes a function input value that will eventually be called with the current time in number form."
var underlings = new Set() // this is more like "all actors that are notified when we die"

View File

@@ -11,6 +11,13 @@
#include <sys/stat.h>
#include <time.h>
#include <unistd.h> // pipe(), read(), write()
#include <fcntl.h> // fcntl(), O_NONBLOCK
#include <sys/select.h> // pselect(), fd_set, FD_*
#include <errno.h> // errno
#include <stdio.h> // perror(), printf()
#include <stdlib.h> // exit()
#include <SDL3/SDL_atomic.h>
#define WOTA_IMPLEMENTATION
@@ -47,13 +54,14 @@ static cell_rt **ready_queue = NULL;
static cell_rt **main_ready_queue = NULL;
static SDL_Mutex *queue_mutex = NULL; // for the ready queue
static SDL_Condition *queue_cond = NULL;
static SDL_Mutex *mainqueue_mutex = NULL;
static SDL_Condition *mainqueue_cond = NULL;
static SDL_Mutex *actors_mutex = NULL;
static struct { char *key; cell_rt *value; } *actors = NULL;
static unsigned char *zip_buffer_global = NULL;
static char *prosperon = NULL;
cell_rt *root_cell = NULL;
static SDL_AtomicInt engine_shutdown;
static SDL_Thread **runners = NULL;
// ─────────────────────────────────────────────────────────────────────────────
@@ -76,7 +84,6 @@ static Uint32 next_timer_id = 1;
// Must be initialized exactly once (e.g. in main after SDL_Init)
static SDL_Mutex *timer_mutex = NULL;
static SDL_Condition *timer_cond = NULL;
// Return monotonic time in nanoseconds
static uint64_t get_time_ns(void) {
@@ -98,8 +105,8 @@ Uint32 add_timer_ns(uint64_t delay_ns, TimerCallback callback, void *param) {
t.param = param;
SDL_LockMutex(timer_mutex);
arrput(timers, t);
SDL_SignalCondition(timer_cond);
SDL_UnlockMutex(timer_mutex);
SDL_SignalCondition(mainqueue_cond);
return t.id;
}
@@ -171,13 +178,7 @@ static inline uint64_t now_ns()
static void exit_handler(void)
{
SDL_SetAtomicInt(&engine_shutdown, 1);
/* Push a terminating event to the SDL event queue */
SDL_Event terminating_event;
terminating_event.type = SDL_EVENT_TERMINATING;
SDL_PushEvent(&terminating_event);
exit(0);
int status;
SDL_BroadcastCondition(queue_cond);
for (int i = 0; i < arrlen(runners); i++)
@@ -472,6 +473,7 @@ const char *register_actor(const char *id, cell_rt *actor, int mainthread, doubl
actor->main_thread_only = mainthread;
actor->id = strdup(id);
actor->ar_secs = ar;
actor->state = ACTOR_IDLE; // Initialize state!
shput(actors, id, actor);
SDL_UnlockMutex(actors_mutex);
return NULL;
@@ -523,7 +525,7 @@ Uint32 actor_delay_cb(SDL_TimerID id, Uint32 interval, cell_rt *actor)
JSValue cb = actor->timers[idx].value;
hmdel(actor->timers, id);
letter l ={0};
letter l = {0};
l.type = LETTER_CALLBACK;
l.callback = cb;
arrput(actor->letters, l);
@@ -541,7 +543,7 @@ void set_actor_state(cell_rt *actor)
return;
}
SDL_LockMutex(actor->msg_mutex);
switch(actor->state) {
case ACTOR_RUNNING:
case ACTOR_READY:
@@ -554,16 +556,22 @@ void set_actor_state(cell_rt *actor)
case ACTOR_IDLE:
if (arrlen(actor->letters)) {
actor->state = ACTOR_READY;
SDL_LockMutex(queue_mutex);
if (actor->main_thread_only)
arrput(main_ready_queue, actor);
else
arrput(ready_queue, actor);
SDL_SignalCondition(queue_cond);
SDL_UnlockMutex(queue_mutex);
} else if (!arrlen(actor->letters) && !hmlen(actor->timers))
if (actor->main_thread_only) {
SDL_LockMutex(mainqueue_mutex);
arrput(main_ready_queue, actor);
SDL_UnlockMutex(mainqueue_mutex);
SDL_SignalCondition(mainqueue_cond);
} else {
SDL_LockMutex(queue_mutex);
arrput(ready_queue, actor);
SDL_SignalCondition(queue_cond);
SDL_UnlockMutex(queue_mutex);
}
} else if (!arrlen(actor->letters) && !hmlen(actor->timers)) {
actor->ar = add_timer_ns(actor->ar_secs*1e9, actor_remove_cb, actor);
SDL_SignalCondition(mainqueue_cond);
}
break;
}
@@ -631,13 +639,12 @@ JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv)
SDL_UnlockMutex(actor->msg_mutex);
return JS_NewInt32(js, -1);
}
uint32_t id = add_timer_ns(seconds*1e9, actor_delay_cb, actor);
SDL_LockMutex(actor->msg_mutex);
uint32_t id = add_timer_ns(seconds*1e9, actor_delay_cb, actor);
JSValue cb = JS_DupValue(js, argv[0]);
hmput(actor->timers, id, cb);
SDL_UnlockMutex(actor->msg_mutex);
return JS_NewUint32(js, id);
}
@@ -767,7 +774,7 @@ void script_startup(cell_rt *prt)
PHYSFS_File *eng = PHYSFS_openRead(ENGINE);
if (!eng) {
printf("Could not open file! %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
printf("ERROR: Could not open file %s! %s\n", ENGINE, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return;
}
PHYSFS_Stat stat;
@@ -803,7 +810,7 @@ int uncaught_exception(JSContext *js, JSValue v)
static int actor_runner(void *data)
{
while (!SDL_GetAtomicInt(&engine_shutdown)) {
while (1) {
SDL_LockMutex(queue_mutex);
cell_rt *actor = NULL;
if (arrlen(ready_queue) > 0) {
@@ -837,6 +844,59 @@ static void signal_handler(int sig)
exit_handler();
}
static void add_runners(int n)
{
/* Launch runner threads */
for (int i = 0; i < n; i++) { // -1 to keep the main thread free
char threadname[128];
snprintf(threadname, sizeof(threadname), "actor runner %d", i);
SDL_Thread *thread = SDL_CreateThread(actor_runner, threadname, NULL);
arrput(runners, thread);
}
}
static void loop()
{
/* Initialize synchronization primitives */
queue_mutex = SDL_CreateMutex();
queue_cond = SDL_CreateCondition();
mainqueue_mutex = SDL_CreateMutex();
mainqueue_cond = SDL_CreateCondition();
actors_mutex = SDL_CreateMutex();
timer_mutex = SDL_CreateMutex();
add_runners(SDL_GetNumLogicalCPUCores()-1);
while (1) {
process_due_timers();
SDL_LockMutex(mainqueue_mutex);
cell_rt *actor = NULL;
if (arrlen(main_ready_queue) > 0) {
actor = main_ready_queue[0];
arrdel(main_ready_queue, 0);
}
SDL_UnlockMutex(mainqueue_mutex);
if (actor) {
actor_turn(actor);
continue;
}
uint64_t to_ns = next_timeout_ns();
if (to_ns == UINT64_MAX) {
// No more timers - hence, no more actors ... exit if single threaded.
}
SDL_LockMutex(mainqueue_mutex);
SDL_WaitConditionTimeout(mainqueue_cond, mainqueue_mutex, to_ns/1000000);
SDL_UnlockMutex(mainqueue_mutex);
}
}
int main(int argc, char **argv)
{
int profile_enabled = 0;
@@ -856,10 +916,17 @@ int main(int argc, char **argv)
tracy_profiling_enabled = profile_enabled;
#endif
int cores = SDL_GetNumLogicalCPUCores();
prosperon = argv[0];
PHYSFS_init(argv[0]);
/* Mount core.zip first - this is critical! */
int mounted = prosperon_mount_core();
if (!mounted) mounted = PHYSFS_mount("core.zip", NULL, 0);
if (!mounted) mounted = PHYSFS_mount("scripts", NULL, 0);
if (!mounted) {
printf("ERROR: Could not mount core. Reason: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return 1;
}
/* Search for .cell directory up the tree */
char *search_dir = SDL_GetCurrentDirectory();
@@ -909,13 +976,6 @@ int main(int argc, char **argv)
SDL_free(search_dir);
/* Initialize synchronization primitives */
queue_mutex = SDL_CreateMutex();
queue_cond = SDL_CreateCondition();
actors_mutex = SDL_CreateMutex();
timer_mutex = SDL_CreateMutex();
timer_cond = SDL_CreateCondition();
/* Create the initial actor from the command line */
int actor_argc = argc - script_start;
char **actor_argv = argv + script_start;
@@ -931,14 +991,6 @@ int main(int argc, char **argv)
wota_write_text(&startwota, actor_argv[i]);
root_cell = create_actor(startwota.data);
/* Launch runner threads */
for (int i = 0; i < cores-1; i++) { // -1 to keep the main thread free
char threadname[128];
snprintf(threadname, sizeof(threadname), "actor runner %d", i);
SDL_Thread *thread = SDL_CreateThread(actor_runner, threadname, NULL);
arrput(runners, thread);
}
/* Set up signal and exit handlers */
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
@@ -946,36 +998,7 @@ int main(int argc, char **argv)
signal(SIGABRT, signal_handler);
atexit(exit_handler);
/* Main loop: pump ready actors */
while (!SDL_GetAtomicInt(&engine_shutdown)) {
process_due_timers(); // Process any due timers first
SDL_LockMutex(queue_mutex);
cell_rt *actor = NULL;
if (arrlen(main_ready_queue) > 0) {
actor = main_ready_queue[0];
arrdel(main_ready_queue, 0);
}
SDL_UnlockMutex(queue_mutex);
if (actor)
actor_turn(actor);
uint64_t to_ns = next_timeout_ns();
if (to_ns == UINT64_MAX) {
SDL_LockMutex(timer_mutex);
SDL_WaitCondition(timer_cond, timer_mutex);
SDL_UnlockMutex(timer_mutex);
continue;
} else {
struct timespec timeout = {
.tv_sec = to_ns / 1000000000,
.tv_nsec = to_ns % 1000000000
};
pselect(0, NULL, NULL, NULL, &timeout, NULL);
}
}
loop();
return 0;
}

10
tests/clock.ce Normal file
View File

@@ -0,0 +1,10 @@
var i = 0
function loop(time)
{
log.console(`loop ${i} with time ${time}`)
i++
if (i > 60) $_.stop()
$_.clock(loop)
}
$_.clock(loop)