fix updated render loop
This commit is contained in:
@@ -11,3 +11,7 @@ stack_max = 0
|
||||
[actors]
|
||||
[actors.prosperon/sdl_video]
|
||||
main = true
|
||||
[actors.prosperon/prosperon]
|
||||
main = true
|
||||
[actors.prosperon]
|
||||
main = false
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
167
source/cell.c
167
source/cell.c
@@ -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
10
tests/clock.ce
Normal 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)
|
||||
Reference in New Issue
Block a user