Skip to content

Commit

Permalink
Tighten scope of render tiles
Browse files Browse the repository at this point in the history
No need to have it live in renderer state, instead compute it where
needed.
The whole tile thing could use a rewrite, it's very old and quite
overbuilt. But it's staying in, for now.
  • Loading branch information
vkoskiv committed Nov 22, 2023
1 parent 0ecabe7 commit 02501e5
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 104 deletions.
94 changes: 51 additions & 43 deletions src/datatypes/tile.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -56,58 +56,66 @@ 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;

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 };

Expand Down
14 changes: 10 additions & 4 deletions src/datatypes/tile.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "../includes.h"
#include "../utils/dyn_array.h"
#include "../utils/platform/mutex.h"

#include "vector.h"

Expand Down Expand Up @@ -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);
57 changes: 22 additions & 35 deletions src/renderer/renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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);

}

Expand Down Expand Up @@ -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
Expand All @@ -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);
}

Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}

Expand All @@ -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;
}

Expand All @@ -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};
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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};
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
3 changes: 1 addition & 2 deletions src/renderer/renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
Loading

0 comments on commit 02501e5

Please sign in to comment.