diff --git a/src/datatypes/tile.c b/src/datatypes/tile.c index 88c58d5e..9d1a437c 100644 --- a/src/datatypes/tile.c +++ b/src/datatypes/tile.c @@ -18,36 +18,36 @@ static void tiles_reorder(struct render_tile_arr *tiles, enum render_order tileOrder); -struct render_tile *tile_next(struct renderer *r) { +struct render_tile *tile_next(struct renderer *r, struct tile_set *set) { struct render_tile *tile = NULL; - mutex_lock(r->state.tile_mutex); - if (r->state.finishedTileCount < r->state.tiles.count) { - tile = &r->state.tiles.items[r->state.finishedTileCount]; + mutex_lock(set->tile_mutex); + if (r->state.finishedTileCount < set->tiles.count) { + tile = &set->tiles.items[r->state.finishedTileCount]; tile->state = rendering; tile->index = r->state.finishedTileCount++; } else { // If a network worker disappeared during render, finish those tiles locally here at the end - for (size_t t = 0; t < r->state.tiles.count; ++t) { - if (r->state.tiles.items[t].state == rendering && r->state.tiles.items[t].network_renderer) { - r->state.tiles.items[t].network_renderer = false; - tile = &r->state.tiles.items[t]; + for (size_t t = 0; t < set->tiles.count; ++t) { + if (set->tiles.items[t].state == rendering && set->tiles.items[t].network_renderer) { + set->tiles.items[t].network_renderer = false; + tile = &set->tiles.items[t]; tile->state = rendering; tile->index = t; break; } } } - mutex_release(r->state.tile_mutex); + mutex_release(set->tile_mutex); return tile; } -struct render_tile *tile_next_interactive(struct renderer *r) { +struct render_tile *tile_next_interactive(struct renderer *r, struct tile_set *set) { struct render_tile *tile = NULL; - mutex_lock(r->state.tile_mutex); + mutex_lock(set->tile_mutex); again: if (r->state.finishedPasses < r->prefs.sampleCount + 1) { - if (r->state.finishedTileCount < r->state.tiles.count) { - tile = &r->state.tiles.items[r->state.finishedTileCount]; + if (r->state.finishedTileCount < set->tiles.count) { + tile = &set->tiles.items[r->state.finishedTileCount]; tile->state = rendering; tile->index = r->state.finishedTileCount++; } else { @@ -56,41 +56,44 @@ struct render_tile *tile_next_interactive(struct renderer *r) { goto again; } } - mutex_release(r->state.tile_mutex); + mutex_release(set->tile_mutex); return tile; } -unsigned tile_quantize(struct render_tile_arr *tiles, unsigned width, unsigned height, unsigned tileWidth, unsigned tileHeight, enum render_order tileOrder) { - +struct tile_set tile_quantize(unsigned width, unsigned height, unsigned tile_w, unsigned tile_h, enum render_order order) { + logr(info, "Quantizing render plane\n"); - + + struct tile_set set = { 0 }; + set.tile_mutex = mutex_create(); + //Sanity check on tilesizes - if (tileWidth >= width) tileWidth = width; - if (tileHeight >= height) tileHeight = height; - if (tileWidth <= 0) tileWidth = 1; - if (tileHeight <= 0) tileHeight = 1; - - unsigned tilesX = width / tileWidth; - unsigned tilesY = height / tileHeight; - - tilesX = (width % tileWidth) != 0 ? tilesX + 1: tilesX; - tilesY = (height % tileHeight) != 0 ? tilesY + 1: tilesY; + if (tile_w >= width) tile_w = width; + if (tile_h >= height) tile_h = height; + if (tile_w <= 0) tile_w = 1; + if (tile_h <= 0) tile_h = 1; + + unsigned tiles_x = width / tile_w; + unsigned tiles_y = height / tile_h; + + tiles_x = (width % tile_w) != 0 ? tiles_x + 1: tiles_x; + tiles_y = (height % tile_h) != 0 ? tiles_y + 1: tiles_y; int tileCount = 0; - for (unsigned y = 0; y < tilesY; ++y) { - for (unsigned x = 0; x < tilesX; ++x) { + for (unsigned y = 0; y < tiles_y; ++y) { + for (unsigned x = 0; x < tiles_x; ++x) { struct render_tile tile = { 0 }; - tile.width = tileWidth; - tile.height = tileHeight; + tile.width = tile_w; + tile.height = tile_h; - tile.begin.x = x * tileWidth; - tile.end.x = (x + 1) * tileWidth; + tile.begin.x = x * tile_w; + tile.end.x = (x + 1) * tile_w; - tile.begin.y = y * tileHeight; - tile.end.y = (y + 1) * tileHeight; + tile.begin.y = y * tile_h; + tile.end.y = (y + 1) * tile_h; - tile.end.x = min((x + 1) * tileWidth, width); - tile.end.y = min((y + 1) * tileHeight, height); + tile.end.x = min((x + 1) * tile_w, width); + tile.end.y = min((y + 1) * tile_h, height); tile.width = tile.end.x - tile.begin.x; tile.height = tile.end.y - tile.begin.y; @@ -98,16 +101,21 @@ unsigned tile_quantize(struct render_tile_arr *tiles, unsigned width, unsigned h tile.state = ready_to_render; tile.index = tileCount++; - render_tile_arr_add(tiles, tile); + render_tile_arr_add(&set.tiles, tile); } } - logr(info, "Quantized image into %i tiles. (%ix%i)\n", (tilesX*tilesY), tilesX, tilesY); - - tiles_reorder(tiles, tileOrder); - - return tileCount; + logr(info, "Quantized image into %i tiles. (%ix%i)\n", (tiles_x * tiles_y), tiles_x, tiles_y); + + tiles_reorder(&set.tiles, order); + + return set; } +void tile_set_free(struct tile_set *set) { + render_tile_arr_free(&set->tiles); + if (set->tile_mutex) free(set->tile_mutex); + set->tile_mutex = NULL; +} static void reorder_top_to_bottom(struct render_tile_arr *tiles) { struct render_tile_arr temp = { 0 }; diff --git a/src/datatypes/tile.h b/src/datatypes/tile.h index 3f820471..e21b988a 100644 --- a/src/datatypes/tile.h +++ b/src/datatypes/tile.h @@ -10,6 +10,7 @@ #include "../includes.h" #include "../utils/dyn_array.h" +#include "../utils/platform/mutex.h" #include "vector.h" @@ -44,9 +45,14 @@ struct render_tile { typedef struct render_tile render_tile; dyn_array_def(render_tile); -/// Quantize the render plane into an array of tiles, with properties as specified in the parameters below -unsigned tile_quantize(struct render_tile_arr *tiles, unsigned width, unsigned height, unsigned tileWidth, unsigned tileHeight, enum render_order tileOrder); +struct tile_set { + struct render_tile_arr tiles; + struct cr_mutex *tile_mutex; +}; + +struct tile_set tile_quantize(unsigned width, unsigned height, unsigned tile_w, unsigned tile_h, enum render_order order); +void tile_set_free(struct tile_set *set); -struct render_tile *tile_next(struct renderer *r); +struct render_tile *tile_next(struct renderer *r, struct tile_set *set); -struct render_tile *tile_next_interactive(struct renderer *r); +struct render_tile *tile_next_interactive(struct renderer *r, struct tile_set *set); diff --git a/src/renderer/renderer.c b/src/renderer/renderer.c index 0388fd33..44120814 100644 --- a/src/renderer/renderer.c +++ b/src/renderer/renderer.c @@ -72,13 +72,13 @@ void *renderThreadInteractive(void *arg); //FIXME: Statistics computation is a gigantic mess. It will also break in the case //where a worker node disconnects during a render, so maybe fix that next. -void update_cb_info(struct renderer *r, struct cr_renderer_cb_info *i) { +void update_cb_info(struct renderer *r, struct tile_set *set, struct cr_renderer_cb_info *i) { 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, r->state.tiles.items, sizeof(*i->tiles) * i->tiles_count); + memcpy((struct cr_tile *)i->tiles, set->tiles.items, sizeof(*i->tiles) * i->tiles_count); if (!r->state.workers.count) return; //Gather and maintain this average constantly. size_t remote_threads = 0; @@ -97,7 +97,7 @@ void update_cb_info(struct renderer *r, struct cr_renderer_cb_info *i) { for (size_t t = 0; t < r->state.workers.count; ++t) { completed_samples += r->state.workers.items[t].totalSamples; } - uint64_t remainingTileSamples = (r->state.tiles.count * r->prefs.sampleCount) - completed_samples; + uint64_t remainingTileSamples = (set->tiles.count * r->prefs.sampleCount) - completed_samples; uint64_t eta_ms_till_done = (avg_tile_pass_us * remainingTileSamples) / 1000; eta_ms_till_done /= (r->prefs.threads + remote_threads); uint64_t sps = (1000000 / avg_per_ray_us) * (r->prefs.threads + remote_threads); @@ -108,7 +108,7 @@ void update_cb_info(struct renderer *r, struct cr_renderer_cb_info *i) { i->eta_ms = eta_ms_till_done; i->completion = r->prefs.iterative ? ((double)r->state.finishedPasses / (double)r->prefs.sampleCount) : - ((double)r->state.finishedTileCount / (double)r->state.tiles.count); + ((double)r->state.finishedTileCount / (double)set->tiles.count); } @@ -146,13 +146,7 @@ struct texture *renderFrame(struct renderer *r) { KNRM, PLURAL(r->prefs.threads)); - //Quantize image into renderTiles - tile_quantize(&r->state.tiles, - camera.width, - camera.height, - r->prefs.tileWidth, - r->prefs.tileHeight, - r->prefs.tileOrder); + struct tile_set set = tile_quantize(camera.width, camera.height, r->prefs.tileWidth, r->prefs.tileHeight, r->prefs.tileOrder); // Do some pre-render preparations // Compute BVH acceleration structures for all meshes in the scene @@ -168,25 +162,25 @@ struct texture *renderFrame(struct renderer *r) { printSceneStats(r->scene, timer_get_ms(timer)); - for (size_t i = 0; i < r->state.tiles.count; ++i) - r->state.tiles.items[i].total_samples = r->prefs.sampleCount; + for (size_t i = 0; i < set.tiles.count; ++i) + set.tiles.items[i].total_samples = r->prefs.sampleCount; //Print a useful warning to user if the defined tile size results in less renderThreads - if (r->state.tiles.count < r->prefs.threads) { + if (set.tiles.count < r->prefs.threads) { logr(warning, "WARNING: Rendering with a less than optimal thread count due to large tile size!\n"); - logr(warning, "Reducing thread count from %zu to %zu\n", r->prefs.threads, r->state.tiles.count); - r->prefs.threads = r->state.tiles.count; + logr(warning, "Reducing thread count from %zu to %zu\n", r->prefs.threads, set.tiles.count); + r->prefs.threads = set.tiles.count; } struct texture *output = newTexture(char_p, camera.width, camera.height, 3); - struct cr_tile *info_tiles = calloc(r->state.tiles.count, sizeof(*info_tiles)); + struct cr_tile *info_tiles = calloc(set.tiles.count, sizeof(*info_tiles)); struct cr_renderer_cb_info cb_info = { .tiles = info_tiles, - .tiles_count = r->state.tiles.count + .tiles_count = set.tiles.count }; cb_info.fb = output; if (r->state.cb.cr_renderer_on_start) { - update_cb_info(r, &cb_info); + update_cb_info(r, &set, &cb_info); r->state.cb.cr_renderer_on_start(&cb_info); } @@ -196,12 +190,7 @@ struct texture *renderFrame(struct renderer *r) { r->state.render_aborted = false; r->state.saveImage = true; // Set to false if user presses X - size_t remoteThreads = 0; - for (size_t i = 0; i < r->state.clients.count; ++i) { - remoteThreads += r->state.clients.items[i].available_threads; - } - - 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), remoteThreads, PLURAL(remoteThreads)); + 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)); // Select the appropriate renderer type for local use void *(*localRenderThread)(void *) = renderThread; @@ -239,6 +228,7 @@ struct texture *renderFrame(struct renderer *r) { } for (size_t w = 0; w < r->state.workers.count; ++w) { r->state.workers.items[w].thread.user_data = &r->state.workers.items[w]; + r->state.workers.items[w].tiles = &set; if (thread_start(&r->state.workers.items[w].thread)) logr(error, "Failed to start worker %zu\n", w); } @@ -251,7 +241,7 @@ struct texture *renderFrame(struct renderer *r) { } if (r->state.cb.cr_renderer_status) { - update_cb_info(r, &cb_info); + update_cb_info(r, &set, &cb_info); r->state.cb.cr_renderer_status(&cb_info); } @@ -269,11 +259,12 @@ struct texture *renderFrame(struct renderer *r) { thread_wait(&r->state.workers.items[w].thread); } if (r->state.cb.cr_renderer_on_stop) { - update_cb_info(r, &cb_info); + update_cb_info(r, &set, &cb_info); r->state.cb.cr_renderer_on_stop(&cb_info); } if (info_tiles) free(info_tiles); destroyTexture(render_buf); + tile_set_free(&set); return output; } @@ -290,7 +281,7 @@ void *renderThreadInteractive(void *arg) { struct camera *cam = threadState->cam; //First time setup for each thread - struct render_tile *tile = tile_next_interactive(r); + struct render_tile *tile = tile_next_interactive(r, threadState->tiles); threadState->currentTile = tile; struct timeval timer = {0}; @@ -345,7 +336,7 @@ void *renderThreadInteractive(void *arg) { tile->state = finished; threadState->currentTile = NULL; threadState->completedSamples = r->state.finishedPasses; - tile = tile_next_interactive(r); + tile = tile_next_interactive(r, threadState->tiles); threadState->currentTile = tile; } exit: @@ -373,7 +364,7 @@ void *renderThread(void *arg) { struct camera *cam = threadState->cam; //First time setup for each thread - struct render_tile *tile = tile_next(r); + struct render_tile *tile = tile_next(r, threadState->tiles); threadState->currentTile = tile; struct timeval timer = {0}; @@ -429,7 +420,7 @@ void *renderThread(void *arg) { tile->state = finished; threadState->currentTile = NULL; threadState->completedSamples = 1; - tile = tile_next(r); + tile = tile_next(r, threadState->tiles); threadState->currentTile = tile; } exit: @@ -462,8 +453,6 @@ struct renderer *renderer_new() { r->prefs = defaults(); r->state.finishedPasses = 1; - r->state.tile_mutex = mutex_create(); - r->scene = calloc(1, sizeof(*r->scene)); r->scene->storage.node_pool = newBlock(NULL, 1024); r->scene->storage.node_table = newHashtable(compareNodes, &r->scene->storage.node_pool); @@ -473,9 +462,7 @@ struct renderer *renderer_new() { void renderer_destroy(struct renderer *r) { if (!r) return; scene_destroy(r->scene); - render_tile_arr_free(&r->state.tiles); worker_arr_free(&r->state.workers); - free(r->state.tile_mutex); render_client_arr_free(&r->state.clients); if (r->state.file_cache) { cache_destroy(r->state.file_cache); diff --git a/src/renderer/renderer.h b/src/renderer/renderer.h index 2099d104..bd0b06ce 100644 --- a/src/renderer/renderer.h +++ b/src/renderer/renderer.h @@ -23,6 +23,7 @@ struct worker { bool paused; //SDL listens for P key pressed, which sets these, one for each thread. //Share info about the current tile with main thread + struct tile_set *tiles; struct render_tile *currentTile; size_t completedSamples; //FIXME: Remove uint64_t totalSamples; @@ -40,8 +41,6 @@ dyn_array_def(worker); /// Renderer state data struct state { - struct render_tile_arr tiles; - struct cr_mutex *tile_mutex; size_t finishedTileCount; size_t finishedPasses; // For interactive mode diff --git a/src/utils/protocol/server.c b/src/utils/protocol/server.c index c0b2d806..d5aa08ce 100644 --- a/src/utils/protocol/server.c +++ b/src/utils/protocol/server.c @@ -135,7 +135,7 @@ static struct render_client_arr build_client_list(const char *node_list) { static cJSON *handle_get_work(struct worker *state, const cJSON *json) { (void)state; (void)json; - struct render_tile *tile = tile_next(state->renderer); + struct render_tile *tile = tile_next(state->renderer, state->tiles); if (!tile) return newAction("renderComplete"); tile->network_renderer = true; cJSON *response = newAction("newWork"); @@ -148,8 +148,8 @@ static cJSON *handle_submit_work(struct worker *state, const cJSON *json) { struct texture *texture = decodeTexture(result); cJSON *tile_json = cJSON_GetObjectItem(json, "tile"); struct render_tile tile = decodeTile(tile_json); - state->renderer->state.tiles.items[tile.index] = tile; - state->renderer->state.tiles.items[tile.index].state = finished; // FIXME: Remove + state->tiles->tiles.items[tile.index] = tile; + state->tiles->tiles.items[tile.index].state = finished; // FIXME: Remove for (int y = tile.end.y - 1; y > tile.begin.y - 1; --y) { for (int x = tile.begin.x; x < tile.end.x; ++x) { struct color value = textureGetPixel(texture, x - tile.begin.x, y - tile.begin.y, false); @@ -231,7 +231,7 @@ void *client_connection_thread(void *arg) { cJSON *tile = NULL; cJSON_ArrayForEach(tile, array) { struct render_tile t = decodeTile(tile); - r->state.tiles.items[t.index] = t; + state->tiles->tiles.items[t.index] = t; //r->state.renderTiles[t.tileNum].completed_samples = t.completed_samples; } } diff --git a/src/utils/protocol/worker.c b/src/utils/protocol/worker.c index f50ad04e..628f8c21 100644 --- a/src/utils/protocol/worker.c +++ b/src/utils/protocol/worker.c @@ -57,6 +57,7 @@ struct workerThreadState { size_t completedSamples; long avgSampleTime; struct render_tile *current; + struct tile_set *tiles; }; static cJSON *validateHandshake(cJSON *in) { @@ -100,7 +101,7 @@ static cJSON *receiveScene(const cJSON *json) { } // Tilenum of -1 communicates that it failed to get work, signaling the work thread to exit -static struct render_tile *getWork(int connectionSocket) { +static struct render_tile *getWork(int connectionSocket, struct tile_set *tiles) { if (!sendJSON(connectionSocket, newAction("getWork"), NULL)) { return NULL; } @@ -118,9 +119,9 @@ static struct render_tile *getWork(int connectionSocket) { // we can just keep track of indices, and compute the tile dims cJSON *tileJson = cJSON_GetObjectItem(response, "tile"); struct render_tile tile = decodeTile(tileJson); - g_worker_renderer->state.tiles.items[tile.index] = tile; + tiles->tiles.items[tile.index] = tile; cJSON_Delete(response); - return &g_worker_renderer->state.tiles.items[tile.index]; + return &tiles->tiles.items[tile.index]; } static bool submitWork(int sock, struct texture *work, struct render_tile *forTile) { @@ -141,7 +142,7 @@ static void *workerThread(void *arg) { //Fetch initial task mutex_lock(sockMutex); - thread->current = getWork(sock); + thread->current = getWork(sock, thread->tiles); mutex_release(sockMutex); struct texture *tileBuffer = newTexture(float_p, thread->current->width, thread->current->height, 3); sampler *sampler = newSampler(); @@ -205,7 +206,7 @@ static void *workerThread(void *arg) { mutex_release(sockMutex); thread->completedSamples = 1; mutex_lock(sockMutex); - thread->current = getWork(sock); + thread->current = getWork(sock, thread->tiles); mutex_release(sockMutex); tex_clear(tileBuffer); } @@ -243,12 +244,7 @@ static cJSON *startRender(int connectionSocket, size_t thread_limit) { KNRM, PLURAL(r->prefs.threads)); //Quantize image into renderTiles - tile_quantize(&r->state.tiles, - selected_cam.width, - selected_cam.height, - r->prefs.tileWidth, - r->prefs.tileHeight, - r->prefs.tileOrder); + struct tile_set set = tile_quantize(selected_cam.width, selected_cam.height, r->prefs.tileWidth, r->prefs.tileHeight, r->prefs.tileOrder); logr(info, "%u x %u tiles\n", r->prefs.tileWidth, r->prefs.tileHeight); // Do some pre-render preparations @@ -263,14 +259,14 @@ static cJSON *startRender(int connectionSocket, size_t thread_limit) { printSmartTime(timer_get_ms(timer)); logr(plain, "\n"); - for (size_t i = 0; i < r->state.tiles.count; ++i) - r->state.tiles.items[i].total_samples = r->prefs.sampleCount; + for (size_t i = 0; i < set.tiles.count; ++i) + set.tiles.items[i].total_samples = r->prefs.sampleCount; //Print a useful warning to user if the defined tile size results in less renderThreads - if (r->state.tiles.count < r->prefs.threads) { + if (set.tiles.count < r->prefs.threads) { logr(warning, "WARNING: Rendering with a less than optimal thread count due to large tile size!\n"); - logr(warning, "Reducing thread count from %zu to %zu\n", r->prefs.threads, r->state.tiles.count); - r->prefs.threads = r->state.tiles.count; + logr(warning, "Reducing thread count from %zu to %zu\n", r->prefs.threads, set.tiles.count); + r->prefs.threads = set.tiles.count; } //Create render threads (Nonblocking) @@ -280,6 +276,7 @@ static cJSON *startRender(int connectionSocket, size_t thread_limit) { .connectionSocket = connectionSocket, .socketMutex = g_worker_socket_mutex, .renderer = g_worker_renderer, + .tiles = &set, .cam = &selected_cam}; worker_threads[t] = (struct cr_thread){.thread_fn = workerThread, .user_data = &workerThreadStates[t]}; if (thread_start(&worker_threads[t]))