Skip to content

Commit

Permalink
api+python: Redo callbacks API
Browse files Browse the repository at this point in the history
The python side isn't working yet, but the API now allows changing
callbacks one at a time, and each callback has a distinct user_data,
instead of a shared one like before.

Surprised how little info there seems to be about wrapping async C
callback APIs in CPython. I've been searching for a while, and nothing
seems to quite work!
  • Loading branch information
vkoskiv committed Dec 7, 2023
1 parent 82a6899 commit a44043b
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 69 deletions.
18 changes: 10 additions & 8 deletions include/c-ray/c-ray.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ struct cr_tile {
};

struct cr_renderer_cb_info {
void *user_data;
const struct cr_bitmap *fb;
const struct cr_tile *tiles;
size_t tiles_count;
Expand All @@ -99,17 +98,20 @@ struct cr_renderer_cb_info {
bool paused;
};

struct cr_renderer_callbacks {
void (*cr_renderer_on_start)(struct cr_renderer_cb_info *info);
void (*cr_renderer_on_stop)(struct cr_renderer_cb_info *info);
void (*cr_renderer_status)(struct cr_renderer_cb_info *info);
void (*cr_renderer_on_state_changed)(struct cr_renderer_cb_info *info);
void *user_data;
enum cr_renderer_callback {
cr_cb_on_start = 0,
cr_cb_on_stop,
cr_cb_status_update,
cr_cb_on_state_changed
};

CR_EXPORT bool cr_renderer_set_callback(struct cr_renderer *ext,
enum cr_renderer_callback t,
void (*callback_fn)(struct cr_renderer_cb_info *, void *),
void *user_data);

CR_EXPORT bool cr_renderer_set_num_pref(struct cr_renderer *ext, enum cr_renderer_param p, uint64_t num);
CR_EXPORT bool cr_renderer_set_str_pref(struct cr_renderer *ext, enum cr_renderer_param p, const char *str);
CR_EXPORT bool cr_renderer_set_callbacks(struct cr_renderer *ext, struct cr_renderer_callbacks cb);
CR_EXPORT void cr_renderer_stop(struct cr_renderer *ext, bool should_save);
CR_EXPORT void cr_renderer_toggle_pause(struct cr_renderer *ext);
CR_EXPORT const char *cr_renderer_get_str_pref(struct cr_renderer *ext, enum cr_renderer_param p);
Expand Down
29 changes: 14 additions & 15 deletions src/driver/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,20 @@ struct usr_data {
struct sdl_prefs p;
};

static void on_start(struct cr_renderer_cb_info *info) {
struct usr_data *d = info->user_data;
static void on_start(struct cr_renderer_cb_info *info, void *user_data) {
struct usr_data *d = user_data;
if (d->p.enabled && info->fb) d->w = win_try_init(&d->p, info->fb->width, info->fb->height);
}

static void on_stop(struct cr_renderer_cb_info *info) {
struct usr_data *d = info->user_data;
static void on_stop(struct cr_renderer_cb_info *info, void *user_data) {
(void)info;
struct usr_data *d = user_data;
if (d->w) win_destroy(d->w);
}

static void status(struct cr_renderer_cb_info *state) {
static void status(struct cr_renderer_cb_info *state, void *user_data) {
static int pauser = 0;
struct usr_data *d = state->user_data;
struct usr_data *d = user_data;
if (!d) return;
struct input_state in = win_update(d->w, state->tiles, state->tiles_count, (struct texture *)state->fb);
if (in.stop_render) cr_renderer_stop(d->r, in.should_save);
Expand Down Expand Up @@ -178,15 +179,13 @@ int main(int argc, char *argv[]) {
}
}

cr_renderer_set_callbacks(renderer, (struct cr_renderer_callbacks){
.cr_renderer_on_start = on_start,
.cr_renderer_on_stop = on_stop,
.cr_renderer_status = status,
.user_data = &(struct usr_data){
.p = sdl_parse(cJSON_GetObjectItem(input_json, "display")),
.r = renderer
}
});
struct usr_data usrdata = (struct usr_data){
.p = sdl_parse(cJSON_GetObjectItem(input_json, "display")),
.r = renderer,
};
cr_renderer_set_callback(renderer, cr_cb_on_start, on_start, &usrdata);
cr_renderer_set_callback(renderer, cr_cb_on_stop, on_stop, &usrdata);
cr_renderer_set_callback(renderer, cr_cb_status_update, status, &usrdata);

const cJSON *r = cJSON_GetObjectItem(input_json, "renderer");
const cJSON *file_type = cJSON_GetObjectItem(r, "fileType");
Expand Down
19 changes: 12 additions & 7 deletions src/lib/api/c-ray.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ struct cr_renderer *cr_new_renderer() {
return (struct cr_renderer *)renderer_new();
}

bool cr_renderer_set_callback(struct cr_renderer *ext,
enum cr_renderer_callback t,
void (*callback_fn)(struct cr_renderer_cb_info *, void *),
void *user_data) {
if (!ext) return false;
if (t > cr_cb_on_state_changed) return false;
struct renderer *r = (struct renderer *)ext;
r->state.callbacks[t].fn = callback_fn;
r->state.callbacks[t].user_data = user_data;
return true;
}

bool cr_renderer_set_num_pref(struct cr_renderer *ext, enum cr_renderer_param p, uint64_t num) {
if (!ext) return false;
struct renderer *r = (struct renderer *)ext;
Expand Down Expand Up @@ -149,13 +161,6 @@ bool cr_renderer_set_str_pref(struct cr_renderer *ext, enum cr_renderer_param p,
return false;
}

bool cr_renderer_set_callbacks(struct cr_renderer *ext, struct cr_renderer_callbacks cb) {
if (!ext) return false;
struct renderer *r = (struct renderer *)ext;
r->state.cb = cb;
return true;
}

void cr_renderer_stop(struct cr_renderer *ext, bool should_save) {
if (!ext) return;
struct renderer *r = (struct renderer *)ext;
Expand Down
21 changes: 12 additions & 9 deletions src/lib/renderer/renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ void update_cb_info(struct renderer *r, struct tile_set *set, struct cr_renderer
static uint64_t ctr = 1;
static uint64_t avg_per_sample_us = 0;
static uint64_t avg_tile_pass_us = 0;
i->user_data = r->state.cb.user_data;
// Notice: Casting away const here
memcpy((struct cr_tile *)i->tiles, set->tiles.items, sizeof(*i->tiles) * i->tiles_count);
if (!r->state.workers.count) return;
Expand Down Expand Up @@ -181,12 +180,14 @@ struct cr_bitmap *renderer_render(struct renderer *r) {
struct cr_tile *info_tiles = calloc(set.tiles.count, sizeof(*info_tiles));
struct cr_renderer_cb_info cb_info = {
.tiles = info_tiles,
.tiles_count = set.tiles.count
.tiles_count = set.tiles.count,
.fb = (struct cr_bitmap *)output
};
cb_info.fb = (struct cr_bitmap *)output;
if (r->state.cb.cr_renderer_on_start) {

struct callback start = r->state.callbacks[cr_cb_on_start];
if (start.fn) {
update_cb_info(r, &set, &cb_info);
r->state.cb.cr_renderer_on_start(&cb_info);
start.fn(&cb_info, start.user_data);
}

logr(info, "Pathtracing%s...\n", r->prefs.iterative ? " iteratively" : "");
Expand Down Expand Up @@ -245,9 +246,10 @@ struct cr_bitmap *renderer_render(struct renderer *r) {
r->state.render_aborted = true;
}

if (r->state.cb.cr_renderer_status) {
struct callback status = r->state.callbacks[cr_cb_status_update];
if (status.fn) {
update_cb_info(r, &set, &cb_info);
r->state.cb.cr_renderer_status(&cb_info);
status.fn(&cb_info, status.user_data);
}

size_t inactive = 0;
Expand All @@ -263,9 +265,10 @@ struct cr_bitmap *renderer_render(struct renderer *r) {
for (size_t w = 0; w < r->state.workers.count; ++w) {
thread_wait(&r->state.workers.items[w].thread);
}
if (r->state.cb.cr_renderer_on_stop) {
struct callback stop = r->state.callbacks[cr_cb_on_stop];
if (stop.fn) {
update_cb_info(r, &set, &cb_info);
r->state.cb.cr_renderer_on_stop(&cb_info);
stop.fn(&cb_info, stop.user_data);
}
if (info_tiles) free(info_tiles);
destroyTexture(render_buf);
Expand Down
7 changes: 6 additions & 1 deletion src/lib/renderer/renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ struct worker {
typedef struct worker worker;
dyn_array_def(worker);

struct callback {
void (*fn)(struct cr_renderer_cb_info *, void *);
void *user_data;
};

/// Renderer state data
struct state {
size_t finishedPasses; // For interactive mode
Expand All @@ -44,7 +49,7 @@ struct state {
bool saveImage;
struct worker_arr workers;
struct render_client_arr clients;
struct cr_renderer_callbacks cb;
struct callback callbacks[4];
};

/// Preferences data (Set by user)
Expand Down
21 changes: 19 additions & 2 deletions wrappers/c_ray.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,21 @@ def instance_new(self):
def set_background(self, material):
return _lib.scene_set_background(self.s_ptr, material)

class cr_cb_info(ct.Structure):
_fields_ = [
("fb", ct.POINTER(cr_bitmap)),
("tiles", ct.POINTER(ct.c_void_p)), # TODO
("tiles_count", ct.c_size_t),
("active_threads", ct.c_size_t),
("avg_per_ray_us", ct.c_double),
("samples_per_sec", ct.c_int64),
("eta_ms", ct.c_int64),
("completion", ct.c_double),
("paused", ct.c_bool),
]

cr_cb_func = ct.CFUNCTYPE(ct.c_void_p, ct.POINTER(cr_cb_info), ct.POINTER(ct.c_void_p))

class renderer:
def __init__(self, path = None):
self.obj_ptr = _lib.new_renderer()
Expand All @@ -277,8 +292,10 @@ def __init__(self, path = None):
def close(self):
del(self.obj_ptr)

def set_callbacks(self, on_start, on_stop, on_status, on_state_changed, user_data):
_lib.renderer_set_callbacks(self.obj_ptr, on_start, on_stop, on_status, on_state_changed, user_data)
def set_callback(self, type, callback_fn, user_data):
if not callable(callback_fn):
raise TypeError("callback_fn not callable")
_lib.renderer_set_callback(self.obj_ptr, type, cr_cb_func(callback_fn), user_data)

def stop(should_save):
_lib.renderer_stop(self.obj_ptr, should_save)
Expand Down
39 changes: 12 additions & 27 deletions wrappers/cray_wrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,42 +67,27 @@ static PyObject *py_cr_renderer_set_str_pref(PyObject *self, PyObject *args) {
return PyBool_FromLong(ret);
}

static PyObject *py_cr_renderer_set_callbacks(PyObject *self, PyObject *args) {
static PyObject *py_cr_renderer_set_callback(PyObject *self, PyObject *args) {
(void)self; (void)args;
PyObject *r_ext;
PyObject *cb_on_start;
PyObject *cb_on_stop;
PyObject *cb_status;
PyObject *cb_on_state_changed;
enum cr_renderer_callback callback_type;
PyObject *callback_fn;
void *user_data = NULL;

if (!PyArg_ParseTuple(args, "OOOOO|p", &r_ext, &cb_on_start, &cb_on_stop, &cb_status, &cb_on_state_changed, &user_data)) {
if (!PyArg_ParseTuple(args, "OIO|p", &r_ext, &callback_type, &callback_fn, &user_data)) {
return NULL;
}
struct cr_renderer *r = PyCapsule_GetPointer(r_ext, "cray.cr_renderer");
if (!PyCallable_Check(cb_on_start)) {
PyErr_SetString(PyExc_ValueError, "on_start must be callable");
return NULL;
}
if (!PyCallable_Check(cb_on_stop)) {
PyErr_SetString(PyExc_ValueError, "on_stop must be callable");
return NULL;
}
if (!PyCallable_Check(cb_status)) {
PyErr_SetString(PyExc_ValueError, "status must be callable");
if (callback_type > cr_cb_status_update) {
PyErr_SetString(PyExc_ValueError, "Unknown callback type");
return NULL;
}
if (!PyCallable_Check(cb_on_state_changed)) {
PyErr_SetString(PyExc_ValueError, "on_state_changed must be callable");
struct cr_renderer *r = PyCapsule_GetPointer(r_ext, "cray.cr_renderer");
if (!PyCallable_Check(callback_fn)) {
PyErr_SetString(PyExc_ValueError, "callback must be callable");
return NULL;
}
bool ret = cr_renderer_set_callbacks(r, (struct cr_renderer_callbacks){
.cr_renderer_on_start = PyCapsule_GetPointer(cb_on_start, NULL),
.cr_renderer_on_stop = PyCapsule_GetPointer(cb_on_stop, NULL),
.cr_renderer_status = PyCapsule_GetPointer(cb_status, NULL),
.cr_renderer_on_state_changed = PyCapsule_GetPointer(cb_on_state_changed, NULL),
.user_data = user_data
});

bool ret = cr_renderer_set_callback(r, callback_type, PyCapsule_GetPointer(callback_fn, NULL), user_data);
return PyBool_FromLong(ret);
}

Expand Down Expand Up @@ -566,7 +551,7 @@ static PyMethodDef cray_methods[] = {
// { "destroy_renderer", py_cr_destroy_renderer, METH_VARARGS, "" },
{ "renderer_set_num_pref", py_cr_renderer_set_num_pref, METH_VARARGS, "" },
{ "renderer_set_str_pref", py_cr_renderer_set_str_pref, METH_VARARGS, "" },
{ "renderer_set_callbacks", py_cr_renderer_set_callbacks, METH_VARARGS, "" },
{ "renderer_set_callback", py_cr_renderer_set_callback, METH_VARARGS, "" },
{ "renderer_stop", py_cr_renderer_stop, METH_VARARGS, "" },
{ "renderer_toggle_pause", py_cr_renderer_toggle_pause, METH_VARARGS, "" },
{ "renderer_get_str_pref", py_cr_renderer_get_str_pref, METH_VARARGS, "" },
Expand Down

0 comments on commit a44043b

Please sign in to comment.