Skip to content

Commit

Permalink
lib: Make render result buffer presistent
Browse files Browse the repository at this point in the history
We now only have a single float-precision buffer in the renderer, which
gets updated with cr_renderer_render(). Bit of a clearer API now.

With this change, I'm also finally removing the duplicate setPixel() +
sRGB conversion stuff from the core render threads, which was used for a
long time to facilitate smooth update of the render preview, at the a
cost to render performance.
Now, we only update the internal linear color data, and that data is
then converted on the main thread before passing it to SDL. Much better
design - I haven't measured how much this speeds up the actual
rendering, but removing that logic from the render threads is a good
change.
  • Loading branch information
vkoskiv committed Dec 19, 2023
1 parent 902f412 commit b0a0b9c
Show file tree
Hide file tree
Showing 12 changed files with 94 additions and 62 deletions.
4 changes: 3 additions & 1 deletion bindings/blender_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ def sync_scene(self, renderer, depsgraph, b_scene):
else:
cr_cam.set_param(c_ray.cam_param.focus_distance, bl_cam.dof.focus_distance)

# Convert materials
cr_materials = {}
for bl_mat in bpy.data.materials:
print("Converting {}".format(bl_mat.name))
Expand Down Expand Up @@ -259,7 +260,8 @@ def render(self, depsgraph):
renderer.prefs.tile_y = b_scene.c_ray.tile_size
renderer.prefs.bounces = b_scene.c_ray.bounces
renderer.prefs.node_list = b_scene.c_ray.node_list
bm = renderer.render()
renderer.render()
bm = renderer.get_result()
self.display_bitmap(bm)
del(renderer)

Expand Down
5 changes: 4 additions & 1 deletion bindings/c_ray.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,10 @@ def toggle_pause():
_lib.renderer_toggle_pause(self.obj_ptr)

def render(self):
ret = _lib.renderer_render(self.obj_ptr)
_lib.renderer_render(self.obj_ptr)

def get_result(self):
ret = _lib.renderer_get_result(self.obj_ptr)
# I saw this in several places, and suggested by a token predictor. Ehh?
ct.pythonapi.PyCapsule_GetPointer.restype = ct.c_void_p
ct.pythonapi.PyCapsule_GetPointer.argtypes = [ct.py_object, ct.c_char_p]
Expand Down
18 changes: 12 additions & 6 deletions bindings/cray_wrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,15 @@ static PyObject *py_cr_renderer_get_num_pref(PyObject *self, PyObject *args) {
return PyLong_FromUnsignedLong(ret);
}

void py_internal_bitmap_free(PyObject *capsule) {
struct cr_bitmap *bm = PyCapsule_GetPointer(capsule, "cray.cr_bitmap");
cr_bitmap_free(bm);
static PyObject *py_cr_renderer_get_result(PyObject *self, PyObject *args) {
(void)self;
PyObject *r_ext;
if (!PyArg_ParseTuple(args, "O", &r_ext)) {
return NULL;
}
struct cr_renderer *r = PyCapsule_GetPointer(r_ext, "cray.cr_renderer");
struct cr_bitmap *bm = cr_renderer_get_result(r);
return PyCapsule_New(bm, "cray.cr_bitmap", NULL);
}

static PyObject *py_cr_renderer_render(PyObject *self, PyObject *args) {
Expand All @@ -159,11 +165,10 @@ static PyObject *py_cr_renderer_render(PyObject *self, PyObject *args) {
return NULL;
}
struct cr_renderer *r = PyCapsule_GetPointer(r_ext, "cray.cr_renderer");
struct cr_bitmap *out = NULL;
Py_BEGIN_ALLOW_THREADS
out = cr_renderer_render(r);
cr_renderer_render(r);
Py_END_ALLOW_THREADS
return PyCapsule_New(out, "cray.cr_bitmap", NULL);
Py_RETURN_NONE;
}

// TODO: Same here, not sure if we want an explicit teardown
Expand Down Expand Up @@ -585,6 +590,7 @@ static PyMethodDef cray_methods[] = {
{ "renderer_toggle_pause", py_cr_renderer_toggle_pause, METH_VARARGS, "" },
{ "renderer_get_str_pref", py_cr_renderer_get_str_pref, METH_VARARGS, "" },
{ "renderer_get_num_pref", py_cr_renderer_get_num_pref, METH_VARARGS, "" },
{ "renderer_get_result", py_cr_renderer_get_result, METH_VARARGS, "" },
{ "renderer_render", py_cr_renderer_render, METH_VARARGS, "" },
// { "bitmap_free", py_cr_bitmap_free, METH_VARARGS, "" },
{ "renderer_scene_get", py_cr_renderer_scene_get, METH_VARARGS, "" },
Expand Down
4 changes: 2 additions & 2 deletions include/c-ray/c-ray.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ struct cr_bitmap {

// TODO: Maybe redo this to have this update an internal render buffer,
// and another func to get a ptr to that? No need for awkward free() then
CR_EXPORT struct cr_bitmap *cr_renderer_render(struct cr_renderer *r);
CR_EXPORT void cr_bitmap_free(struct cr_bitmap *b);
CR_EXPORT void cr_renderer_render(struct cr_renderer *r);
CR_EXPORT struct cr_bitmap *cr_renderer_get_result(struct cr_renderer *r);

// -- Scene --

Expand Down
15 changes: 12 additions & 3 deletions src/driver/encoders/encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "../imagefile.h"
#include "../../common/logging.h"
#include "../../common/assert.h"
#include "../../common/texture.h"

#include "formats/png.h"
#include "formats/bmp.h"
Expand Down Expand Up @@ -45,19 +46,27 @@ void writeImage(struct imageFile *image) {
}
char buf[2048];
snprintf(buf, 2048 - 1, "%s%s_%04d.%s", image->filePath, image->fileName, image->count, suffix);
struct texture *tmp = newTexture(char_p, image->t->width, image->t->height, 3);
textureToSRGB((struct texture *)image->t);
for (size_t y = 0; y < tmp->height; ++y) {
for (size_t x = 0; x < tmp->width; ++x) {
setPixel(tmp, textureGetPixel((struct texture *)image->t, x, y, false), x, y);
}
}
switch (image->type) {
case png:
encodePNGFromArray(buf, image->t->data.byte_ptr, image->t->width, image->t->height, image->info);
encodePNGFromArray(buf, tmp->data.byte_p, tmp->width, tmp->height, image->info);
break;
case bmp:
encodeBMPFromArray(buf, image->t->data.byte_ptr, image->t->width, image->t->height);
encodeBMPFromArray(buf, tmp->data.byte_p, tmp->width, tmp->height);
break;
case qoi:
encode_qoi_from_array(buf, image->t->data.byte_ptr, image->t->width, image->t->height);
encode_qoi_from_array(buf, tmp->data.byte_p, tmp->width, tmp->height);
break;
case unknown:
default:
ASSERT_NOT_REACHED();
break;
}
destroyTexture(tmp);
}
5 changes: 2 additions & 3 deletions src/driver/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ int main(int argc, char *argv[]) {

struct timeval timer;
timer_start(&timer);
struct cr_bitmap *final = cr_renderer_render(renderer);
cr_renderer_render(renderer);
long ms = timer_get_ms(timer);
char buf[64] = { 0 };
logr(plain, "\n");
Expand Down Expand Up @@ -254,14 +254,13 @@ int main(int argc, char *argv[]) {
.renderTime = ms,
.threadCount = cr_renderer_get_num_pref(renderer, cr_renderer_threads)
},
.t = final
.t = cr_renderer_get_result(renderer)
};
writeImage(&file);
logr(info, "Render finished, exiting.\n");
} else {
logr(info, "Abort pressed, image won't be saved.\n");
}
cr_bitmap_free(final);

done:
cr_destroy_renderer(renderer);
Expand Down
27 changes: 26 additions & 1 deletion src/driver/sdl.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ struct sdl_window {
SDL_Renderer *renderer;
SDL_Texture *texture;
SDL_Texture *overlay_sdl;
struct texture *internal;
struct texture *overlay;
bool isBorderless;
bool isFullScreen;
Expand Down Expand Up @@ -228,6 +229,7 @@ struct sdl_window *win_try_init(struct sdl_prefs *prefs, int width, int height)
return NULL;
}
w->overlay = newTexture(char_p, width, height, 4);
w->internal = newTexture(char_p, width, height, 3);

//And set blend modes for textures too
w->sym->SDL_SetTextureBlendMode(w->texture, SDL_BLENDMODE_BLEND);
Expand All @@ -251,6 +253,7 @@ void win_destroy(struct sdl_window *w) {
w->sym->SDL_DestroyTexture(w->overlay_sdl);
w->texture = NULL;
}
destroyTexture(w->internal);
destroyTexture(w->overlay);
if (w->renderer) {
w->sym->SDL_DestroyRenderer(w->renderer);
Expand Down Expand Up @@ -348,14 +351,36 @@ static void draw_frames(struct texture *overlay, const struct cr_tile *tiles, si
}
}


struct input_state win_update(struct sdl_window *w, const struct cr_tile *tiles, size_t tile_count, const struct texture *t) {
if (!w) return (struct input_state){ 0 };
// Copy regions
ASSERT(w->internal->precision == char_p);
ASSERT(t->precision == float_p);
ASSERT(w->internal->width == t->width);
ASSERT(w->internal->height == t->height);

unsigned char *restrict dst = w->internal->data.byte_p;
float *restrict src = t->data.float_p;

const int width = w->internal->width;
for (size_t i = 0; i < tile_count; ++i) {
for (int y = 0; y < tiles[i].h; ++y) {
for (int x = 0; x < tiles[i].w; ++x) {
const int ax = x + tiles[i].start_x;
const int ay = y + tiles[i].start_y;
dst[(ax + (ay * width)) * 3 + 0] = (unsigned char)min(linearToSRGB(src[(ax + (ay * width)) * 3 + 0]) * 255.0f, 255.0f);
dst[(ax + (ay * width)) * 3 + 1] = (unsigned char)min(linearToSRGB(src[(ax + (ay * width)) * 3 + 1]) * 255.0f, 255.0f);
dst[(ax + (ay * width)) * 3 + 2] = (unsigned char)min(linearToSRGB(src[(ax + (ay * width)) * 3 + 2]) * 255.0f, 255.0f);
}
}
}
//Render frames
// TODO: if (r->prefs.iterative || r->state.clients) {
draw_frames(w->overlay, tiles, tile_count);
draw_prog_bars(w->overlay, tiles, tile_count);
//Update image data
if (t) w->sym->SDL_UpdateTexture(w->texture, NULL, t->data.byte_p, (int)w->width * 3);
if (t) w->sym->SDL_UpdateTexture(w->texture, NULL, w->internal->data.byte_p, (int)w->width * 3);
w->sym->SDL_UpdateTexture(w->overlay_sdl, NULL, w->overlay->data.byte_p, (int)w->width * 4);
w->sym->SDL_RenderCopy(w->renderer, w->texture, NULL, NULL);
w->sym->SDL_RenderCopy(w->renderer, w->overlay_sdl, NULL, NULL);
Expand Down
13 changes: 8 additions & 5 deletions src/lib/api/c-ray.c
Original file line number Diff line number Diff line change
Expand Up @@ -704,19 +704,22 @@ void cr_material_set_add(struct cr_scene *s_ext, cr_material_set set, struct cr_
bsdf_node_ptr_arr_add(&buf->bsdfs, node);
}

struct cr_bitmap *cr_renderer_render(struct cr_renderer *ext) {
void cr_renderer_render(struct cr_renderer *ext) {
if (!ext) return;
struct renderer *r = (struct renderer *)ext;
if (r->prefs.node_list) {
r->state.clients = clients_sync(r);
}
if (!r->state.clients.count && !r->prefs.threads) {
return NULL;
return;
}
return renderer_render(r);
renderer_render(r);
}

void cr_bitmap_free(struct cr_bitmap *ext) {
destroyTexture((struct texture *)ext);
struct cr_bitmap *cr_renderer_get_result(struct cr_renderer *ext) {
if (!ext) return NULL;
struct renderer *r = (struct renderer *)ext;
return (struct cr_bitmap *)r->state.result_buf;
}

void cr_start_render_worker(int port, size_t thread_limit) {
Expand Down
1 change: 1 addition & 0 deletions src/lib/datatypes/camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct camera {
struct spline *path;
float time;

// TODO: size_t
int width;
int height;

Expand Down
3 changes: 1 addition & 2 deletions src/lib/protocol/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,7 @@ static cJSON *handle_submit_work(struct worker *state, const cJSON *json) {
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);
if (state->output) value = colorToSRGB(value);
setPixel(state->output ? state->output : state->buf, value, x, y);
setPixel(state->buf, value, x, y);
}
}
destroyTexture(texture);
Expand Down
56 changes: 20 additions & 36 deletions src/lib/renderer/renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,8 @@ void update_cb_info(struct renderer *r, struct tile_set *set, struct cr_renderer

}

/// @todo Use defaultSettings state struct for this.
/// @todo Clean this up, it's ugly.
struct cr_bitmap *renderer_render(struct renderer *r) {
// TODO: Clean this up, it's ugly.
void renderer_render(struct renderer *r) {
//Check for CTRL-C
// TODO: Move signal to driver
if (registerHandler(sigint, sigHandler)) {
Expand Down Expand Up @@ -180,18 +179,26 @@ struct cr_bitmap *renderer_render(struct renderer *r) {
r->prefs.threads = set.tiles.count;
}

//Allocate memory for render buffer
//Render buffer is used to store accurate color values for the renderers' internal use
struct texture *render_buf = newTexture(float_p, camera.width, camera.height, 3);
struct texture *output = NULL;
if (!r->prefs.blender_mode) {
output = newTexture(char_p, camera.width, camera.height, 3);
// Render buffer is used to store accurate color values for the renderers' internal use
if (!r->state.result_buf) {
// Allocate
r->state.result_buf = newTexture(float_p, camera.width, camera.height, 3);
} else if (r->state.result_buf->width != (size_t)camera.width || r->state.result_buf->height != (size_t)camera.height) {
// Resize
if (r->state.result_buf) destroyTexture(r->state.result_buf);
r->state.result_buf = newTexture(float_p, camera.width, camera.height, 3);
} else {
// Clear
tex_clear(r->state.result_buf);
}

struct texture *result = r->state.result_buf;

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,
.fb = (struct cr_bitmap *)output
.fb = (struct cr_bitmap *)result,
};

struct callback start = r->state.callbacks[cr_cb_on_start];
Expand All @@ -217,8 +224,7 @@ struct cr_bitmap *renderer_render(struct renderer *r) {
for (size_t t = 0; t < r->prefs.threads; ++t) {
worker_arr_add(&r->state.workers, (struct worker){
.renderer = r,
.output = output,
.buf = render_buf,
.buf = result,
.cam = &camera,
.thread = (struct cr_thread){
.thread_fn = local_render_thread,
Expand All @@ -229,8 +235,7 @@ struct cr_bitmap *renderer_render(struct renderer *r) {
worker_arr_add(&r->state.workers, (struct worker){
.client = &r->state.clients.items[c],
.renderer = r,
.output = output,
.buf = render_buf,
.buf = result,
.cam = &camera,
.thread = (struct cr_thread){
.thread_fn = client_connection_thread
Expand Down Expand Up @@ -276,12 +281,6 @@ struct cr_bitmap *renderer_render(struct renderer *r) {
}
if (info_tiles) free(info_tiles);
tile_set_free(&set);
if (r->prefs.blender_mode) {
return (struct cr_bitmap *)render_buf;
} else {
destroyTexture(render_buf);
return (struct cr_bitmap *)output;
}
}

// An interactive render thread that progressively
Expand All @@ -290,7 +289,6 @@ void *render_thread_interactive(void *arg) {
block_signals();
struct worker *threadState = (struct worker*)thread_user_data(arg);
struct renderer *r = threadState->renderer;
struct texture *image = threadState->output;
struct texture *buf = threadState->buf;
sampler *sampler = newSampler();

Expand Down Expand Up @@ -328,13 +326,6 @@ void *render_thread_interactive(void *arg) {

//Store internal render buffer (float precision)
setPixel(buf, output, x, y);

if (image) {
//Gamma correction
output = colorToSRGB(output);
//And store the image data
setPixel(image, output, x, y);
}
}
}
//For performance metrics
Expand Down Expand Up @@ -370,7 +361,6 @@ void *render_thread(void *arg) {
block_signals();
struct worker *threadState = (struct worker*)thread_user_data(arg);
struct renderer *r = threadState->renderer;
struct texture *image = threadState->output;
struct texture *buf = threadState->buf;
sampler *sampler = newSampler();

Expand Down Expand Up @@ -408,13 +398,6 @@ void *render_thread(void *arg) {

//Store internal render buffer (float precision)
setPixel(buf, output, x, y);

if (image) {
//Gamma correction
output = colorToSRGB(output);
//And store the image data
setPixel(image, output, x, y);
}
}
}
//For performance metrics
Expand Down Expand Up @@ -479,5 +462,6 @@ void renderer_destroy(struct renderer *r) {
free(r->prefs.imgFileName);
free(r->prefs.imgFilePath);
if (r->prefs.node_list) free(r->prefs.node_list);
if (r->state.result_buf) destroyTexture(r->state.result_buf);
free(r);
}
Loading

0 comments on commit b0a0b9c

Please sign in to comment.