From 5d7d6950d6ad2c731e81da82cde0afb72e1f3ab0 Mon Sep 17 00:00:00 2001 From: Valtteri Koskivuori Date: Sun, 3 Nov 2024 00:57:07 +0200 Subject: [PATCH] lib: Replace various bools with a single enum Renderer state management is still generally a mess, I want to delete most of that code by restructuring things, but this is a nice start. --- src/lib/api/c-ray.c | 7 ++++--- src/lib/datatypes/tile.c | 2 +- src/lib/protocol/server.c | 2 +- src/lib/protocol/worker.c | 18 ++++++++--------- src/lib/renderer/renderer.c | 40 ++++++++++++++++++------------------- src/lib/renderer/renderer.h | 12 +++++++---- 6 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/lib/api/c-ray.c b/src/lib/api/c-ray.c index 4fc6e233..82b2da91 100644 --- a/src/lib/api/c-ray.c +++ b/src/lib/api/c-ray.c @@ -174,10 +174,11 @@ bool cr_renderer_set_str_pref(struct cr_renderer *ext, enum cr_renderer_param p, void cr_renderer_stop(struct cr_renderer *ext) { if (!ext) return; struct renderer *r = (struct renderer *)ext; - r->state.render_aborted = true; - while (r->prefs.iterative && !r->state.exit_done) { + r->state.s = r_exiting; + // TODO: use pthread_cond instead of silly busy-waiting loops like this + do { timer_sleep_ms(10); - } + } while (r->prefs.iterative && r->state.s == r_exiting); } void cr_renderer_toggle_pause(struct cr_renderer *ext) { diff --git a/src/lib/datatypes/tile.c b/src/lib/datatypes/tile.c index f7cd31f1..8cb3b764 100644 --- a/src/lib/datatypes/tile.c +++ b/src/lib/datatypes/tile.c @@ -63,7 +63,7 @@ struct render_tile *tile_next_interactive(struct renderer *r, struct tile_set *s } if (!tile) { // FIXME: shared state to indicate pause instead of accessing worker state - if (r->state.render_aborted || r->state.workers.items[0].paused) { + if (r->state.s != r_rendering || r->state.workers.items[0].paused) { mutex_release(set->tile_mutex); return NULL; } diff --git a/src/lib/protocol/server.c b/src/lib/protocol/server.c index ed7664e5..e800f72b 100644 --- a/src/lib/protocol/server.c +++ b/src/lib/protocol/server.c @@ -225,7 +225,7 @@ void *client_connection_thread(void *arg) { } // And just wait for commands. - while (r->state.rendering && !state->thread_complete) { + while (r->state.s == r_rendering && !state->thread_complete) { cJSON *request = readJSON(client->socket); if (containsStats(request)) { cJSON *array = cJSON_GetObjectItem(request, "tiles"); diff --git a/src/lib/protocol/worker.c b/src/lib/protocol/worker.c index acf40ab6..86634824 100644 --- a/src/lib/protocol/worker.c +++ b/src/lib/protocol/worker.c @@ -136,7 +136,7 @@ static void *workerThread(void *arg) { thread->completedSamples = 1; struct texture *tileBuffer = NULL; - while (thread->current && r->state.rendering) { + while (thread->current && r->state.s == r_rendering) { if (!tileBuffer || tileBuffer->width != thread->current->width || tileBuffer->height != thread->current->height) { tex_destroy(tileBuffer); tileBuffer = tex_new(float_p, thread->current->width, thread->current->height, 3); @@ -144,11 +144,11 @@ static void *workerThread(void *arg) { long totalUsec = 0; long samples = 0; - while (thread->completedSamples < r->prefs.sampleCount+1 && r->state.rendering) { + while (thread->completedSamples < r->prefs.sampleCount+1 && r->state.s == r_rendering) { timer_start(&timer); for (int y = thread->current->end.y - 1; y > thread->current->begin.y - 1; --y) { for (int x = thread->current->begin.x; x < thread->current->end.x; ++x) { - if (r->state.render_aborted || !g_running) goto bail; + if (r->state.s != r_rendering || !g_running) goto bail; uint32_t pixIdx = (uint32_t)(y * cam->width + x); initSampler(sampler, SAMPLING_STRATEGY, thread->completedSamples - 1, r->prefs.sampleCount, pixIdx); @@ -207,8 +207,7 @@ static void *workerThread(void *arg) { #define active_msec 16 static cJSON *startRender(int connectionSocket, size_t thread_limit) { - g_worker_renderer->state.rendering = true; - g_worker_renderer->state.render_aborted = false; + g_worker_renderer->state.s = r_rendering; logr(info, "Starting network render job\n"); size_t threadCount = thread_limit ? thread_limit : g_worker_renderer->prefs.threads; @@ -268,7 +267,7 @@ static cJSON *startRender(int connectionSocket, size_t thread_limit) { } int pauser = 0; - while (g_worker_renderer->state.rendering) { + while (g_worker_renderer->state.s == r_rendering) { // Send stats about 4x/s if (pauser == 256 / active_msec) { cJSON *stats = newAction("stats"); @@ -288,7 +287,7 @@ static cJSON *startRender(int connectionSocket, size_t thread_limit) { if (!sendJSON(connectionSocket, stats, NULL)) { logr(debug, "Connection lost, bailing out.\n"); // Setting this flag also kills the threads. - g_worker_renderer->state.rendering = false; + g_worker_renderer->state.s = r_exiting; } mutex_release(g_worker_socket_mutex); pauser = 0; @@ -299,12 +298,13 @@ static cJSON *startRender(int connectionSocket, size_t thread_limit) { for (size_t t = 0; t < threadCount; ++t) { if (workerThreadStates[t].threadComplete) inactive++; } - if (g_worker_renderer->state.render_aborted || inactive == threadCount) - g_worker_renderer->state.rendering = false; + if (inactive == threadCount) break; timer_sleep_ms(active_msec); } + g_worker_renderer->state.s = r_exiting; + //Make sure workder threads are terminated before continuing (This blocks) for (size_t t = 0; t < threadCount; ++t) { thread_wait(&worker_threads[t]); diff --git a/src/lib/renderer/renderer.c b/src/lib/renderer/renderer.c index 5fc22f45..01747718 100644 --- a/src/lib/renderer/renderer.c +++ b/src/lib/renderer/renderer.c @@ -35,6 +35,8 @@ static bool g_aborted = false; +// FIXME: c-ray is a library now, so just setting upa global handler like this +// is a bit ugly. void sigHandler(int sig) { if (sig == 2) { //SIGINT logr(plain, "\n"); @@ -226,8 +228,7 @@ void renderer_render(struct renderer *r) { logr(info, "Pathtracing%s...\n", r->prefs.iterative ? " iteratively" : ""); - r->state.rendering = true; - r->state.render_aborted = false; + r->state.s = r_rendering; if (r->state.clients.count) logr(info, "Using %lu render worker%s totaling %lu thread%s.\n", r->state.clients.count, PLURAL(r->state.clients.count), r->state.clients.count, PLURAL(r->state.clients.count)); @@ -267,10 +268,12 @@ void renderer_render(struct renderer *r) { } //Start main thread loop to handle renderer feedback and state management - while (r->state.rendering) { - if (g_aborted) { - r->state.render_aborted = true; + while (r->state.s == r_rendering) { + size_t inactive = 0; + for (size_t w = 0; w < r->state.workers.count; ++w) { + if (r->state.workers.items[w].thread_complete) inactive++; } + if (g_aborted || inactive == r->state.workers.count) break; struct callback status = r->state.callbacks[cr_cb_status_update]; if (status.fn) { @@ -278,21 +281,16 @@ void renderer_render(struct renderer *r) { status.fn(&cb_info, status.user_data); } - size_t inactive = 0; - for (size_t w = 0; w < r->state.workers.count; ++w) { - if (r->state.workers.items[w].thread_complete) inactive++; - } - if (r->state.render_aborted || inactive == r->state.workers.count) - r->state.rendering = false; timer_sleep_ms(r->state.workers.items[0].paused ? paused_msec : active_msec); } + r->state.s = r_exiting; r->state.current_set = NULL; //Make sure render threads are terminated before continuing (This blocks) - for (size_t w = 0; w < r->state.workers.count; ++w) { + for (size_t w = 0; w < r->state.workers.count; ++w) thread_wait(&r->state.workers.items[w].thread); - } + struct callback stop = r->state.callbacks[cr_cb_on_stop]; if (stop.fn) { update_cb_info(r, &set, &cb_info); @@ -301,7 +299,7 @@ void renderer_render(struct renderer *r) { if (info_tiles) free(info_tiles); tile_set_free(&set); logr(info, "Renderer exiting\n"); - r->state.exit_done = true; + r->state.s = r_idle; } // An interactive render thread that progressively @@ -322,13 +320,13 @@ void *render_thread_interactive(void *arg) { struct timeval timer = {0}; - while (tile && r->state.rendering) { + while (tile && r->state.s == r_rendering) { long total_us = 0; timer_start(&timer); for (int y = tile->end.y - 1; y > tile->begin.y - 1; --y) { for (int x = tile->begin.x; x < tile->end.x; ++x) { - if (r->state.render_aborted) goto exit; + if (r->state.s != r_rendering) goto exit; uint32_t pixIdx = (uint32_t)(y * (*buf)->width + x); //FIXME: This does not converge to the same result as with regular renderThread. //I assume that's because we'd have to init the sampler differently when we render all @@ -362,7 +360,7 @@ void *render_thread_interactive(void *arg) { threadState->currentTile = NULL; tile = tile_next_interactive(r, threadState->tiles); //Pause rendering when bool is set - while (threadState->paused && !r->state.render_aborted) { + while (threadState->paused && r->state.s == r_rendering) { threadState->in_pause_loop = true; timer_sleep_ms(100); } @@ -401,14 +399,14 @@ void *render_thread(void *arg) { struct timeval timer = { 0 }; size_t samples = 1; - while (tile && r->state.rendering) { + while (tile && r->state.s == r_rendering) { long total_us = 0; - while (samples < r->prefs.sampleCount + 1 && r->state.rendering) { + while (samples < r->prefs.sampleCount + 1 && r->state.s == r_rendering) { timer_start(&timer); for (int y = tile->end.y - 1; y > tile->begin.y - 1; --y) { for (int x = tile->begin.x; x < tile->end.x; ++x) { - if (r->state.render_aborted) goto exit; + if (r->state.s != r_rendering) goto exit; uint32_t pixIdx = (uint32_t)(y * (*buf)->width + x); initSampler(sampler, SAMPLING_STRATEGY, samples - 1, r->prefs.sampleCount, pixIdx); @@ -436,7 +434,7 @@ void *render_thread(void *arg) { samples++; tile->completed_samples++; //Pause rendering when bool is set - while (threadState->paused && !r->state.render_aborted) { + while (threadState->paused && r->state.s == r_rendering) { timer_sleep_ms(100); } threadState->avg_per_sample_us = total_us / samples; diff --git a/src/lib/renderer/renderer.h b/src/lib/renderer/renderer.h index 0677e687..75ab19d6 100644 --- a/src/lib/renderer/renderer.h +++ b/src/lib/renderer/renderer.h @@ -41,13 +41,17 @@ struct callback { void *user_data; }; +enum renderer_state { + r_idle = 0, + r_rendering, + r_restarting, + r_exiting, +}; + /// Renderer state data struct state { size_t finishedPasses; // For interactive mode - bool rendering; - // TODO: Rename to request_abort, and use an enum for actual state - bool render_aborted; //SDL listens for X key pressed, which sets this - bool exit_done; + enum renderer_state s; struct worker_arr workers; struct render_client_arr clients; struct callback callbacks[5];