diff --git a/include/c-ray/c-ray.h b/include/c-ray/c-ray.h index 2e9edfef..014d00f5 100644 --- a/include/c-ray/c-ray.h +++ b/include/c-ray/c-ray.h @@ -75,9 +75,6 @@ cr_camera cr_camera_new(struct cr_scene *ext); bool cr_camera_set_num_pref(struct cr_scene *ext, cr_camera c, enum cr_camera_param p, double num); bool cr_camera_update(struct cr_scene *ext, cr_camera c); -int cr_load_scene_from_file(struct cr_renderer *r, char *file_path); -int cr_load_scene_from_buf(struct cr_renderer *r, char *buf); - void cr_load_mesh_from_file(char *filePath); void cr_load_mesh_from_buf(char *buf); diff --git a/src/api/c-ray.c b/src/api/c-ray.c index f22d43d2..de44bf60 100644 --- a/src/api/c-ray.c +++ b/src/api/c-ray.c @@ -1,9 +1,9 @@ // // c-ray.c -// C-ray +// c-ray // // Created by Valtteri on 5.1.2020. -// Copyright © 2020-2022 Valtteri Koskivuori. All rights reserved. +// Copyright © 2020-2023 Valtteri Koskivuori. All rights reserved. // #include "../includes.h" @@ -331,20 +331,6 @@ void cr_write_image(struct cr_renderer *ext) { } } -int cr_load_scene_from_file(struct cr_renderer *ext, char *file_path) { - struct renderer *r = (struct renderer *)ext; - size_t bytes = 0; - char *input = load_file(file_path, &bytes, NULL); - if (input) { - if (loadScene(r, file_path) != 0) { - return -1; - } - } else { - return -1; - } - return 0; -} - void cr_load_mesh_from_file(char *file_path) { (void)file_path; ASSERT_NOT_REACHED(); @@ -355,11 +341,6 @@ void cr_load_mesh_from_buf(char *buf) { ASSERT_NOT_REACHED(); } -int cr_load_scene_from_buf(struct cr_renderer *ext, char *buf) { - struct renderer *r = (struct renderer *)ext; - return loadScene(r, buf); -} - int cr_get_thread_count(struct cr_renderer *ext) { struct renderer *r = (struct renderer *)ext; return r->prefs.threads; diff --git a/src/datatypes/color.c b/src/datatypes/color.c index d4fb31a6..d65b5ca1 100644 --- a/src/datatypes/color.c +++ b/src/datatypes/color.c @@ -9,6 +9,8 @@ #include "../includes.h" #include #include "color.h" +#include "../vendored/cJSON.h" +#include "../utils/assert.h" //Some standard colours const struct color g_red_color = { 1.0f, 0.0f, 0.0f, 1.0f }; @@ -99,3 +101,38 @@ struct color color_from_hsl(float hue, float saturation, float lightness) { void color_dump(struct color c, char *buf, size_t bufsize) { snprintf(buf, bufsize, "{ %.2f, %.2f, %.2f, %.2f }", (double)c.red, (double)c.green, (double)c.blue, (double)c.alpha); } + +struct color color_parse(const cJSON *data) { + if (cJSON_IsArray(data)) { + const float r = cJSON_IsNumber(cJSON_GetArrayItem(data, 0)) ? (float)cJSON_GetArrayItem(data, 0)->valuedouble : 0.0f; + const float g = cJSON_IsNumber(cJSON_GetArrayItem(data, 1)) ? (float)cJSON_GetArrayItem(data, 1)->valuedouble : 0.0f; + const float b = cJSON_IsNumber(cJSON_GetArrayItem(data, 2)) ? (float)cJSON_GetArrayItem(data, 2)->valuedouble : 0.0f; + const float a = cJSON_IsNumber(cJSON_GetArrayItem(data, 3)) ? (float)cJSON_GetArrayItem(data, 3)->valuedouble : 1.0f; + return (struct color){ r, g, b, a }; + } + + ASSERT(cJSON_IsObject(data)); + + const cJSON *kelvin = cJSON_GetObjectItem(data, "blackbody"); + if (cJSON_IsNumber(kelvin)) return colorForKelvin(kelvin->valuedouble); + + const cJSON *H = cJSON_GetObjectItem(data, "h"); + const cJSON *S = cJSON_GetObjectItem(data, "s"); + const cJSON *L = cJSON_GetObjectItem(data, "l"); + + if (cJSON_IsNumber(H) && cJSON_IsNumber(S) && cJSON_IsNumber(L)) { + return color_from_hsl(H->valuedouble, S->valuedouble, L->valuedouble); + } + + const cJSON *R = cJSON_GetObjectItem(data, "r"); + const cJSON *G = cJSON_GetObjectItem(data, "g"); + const cJSON *B = cJSON_GetObjectItem(data, "b"); + const cJSON *A = cJSON_GetObjectItem(data, "a"); + + return (struct color){ + cJSON_IsNumber(R) ? (float)R->valuedouble : 0.0f, + cJSON_IsNumber(G) ? (float)G->valuedouble : 0.0f, + cJSON_IsNumber(B) ? (float)B->valuedouble : 0.0f, + cJSON_IsNumber(A) ? (float)A->valuedouble : 1.0f, + }; +} diff --git a/src/datatypes/color.h b/src/datatypes/color.h index 0dbc719e..37a1d8d6 100644 --- a/src/datatypes/color.h +++ b/src/datatypes/color.h @@ -113,3 +113,7 @@ struct color colorForKelvin(float kelvin); struct color color_from_hsl(float hue, float saturation, float lightness); void color_dump(struct color c, char *buf, size_t bufsize); + +struct cJSON; + +struct color color_parse(const struct cJSON *data); diff --git a/src/datatypes/scene.c b/src/datatypes/scene.c index 80593123..74edad99 100644 --- a/src/datatypes/scene.c +++ b/src/datatypes/scene.c @@ -1,245 +1,23 @@ // // scene.c -// C-ray +// c-ray // // Created by Valtteri Koskivuori on 28/02/2015. -// Copyright © 2015-2022 Valtteri Koskivuori. All rights reserved. +// Copyright © 2015-2023 Valtteri Koskivuori. All rights reserved. // #include "../includes.h" #include "scene.h" -#include "../utils/timer.h" -#include "../utils/loaders/sceneloader.h" -#include "../utils/logging.h" -#include "image/imagefile.h" -#include "../renderer/renderer.h" -#include "image/texture.h" -#include "camera.h" #include "../accelerators/bvh.h" -#include "tile.h" -#include "mesh.h" -#include "poly.h" -#include "../utils/platform/thread.h" -#include "../utils/platform/signal.h" -#include "../renderer/instance.h" -#include "../datatypes/bbox.h" -#include "../utils/mempool.h" #include "../utils/hashtable.h" -#include "../nodes/bsdfnode.h" #include "../utils/args.h" #include "../utils/textbuffer.h" #include "../utils/dyn_array.h" - -struct bvh_build_task { - struct bvh *bvh; - const struct mesh *mesh; -}; - -void *bvh_build_thread(void *arg) { - block_signals(); - struct bvh_build_task *task = (struct bvh_build_task *)thread_user_data(arg); - task->bvh = build_mesh_bvh(task->mesh); - return NULL; -} - -static void compute_accels(struct mesh_arr meshes) { - logr(info, "Computing BVHs: "); - struct timeval timer = { 0 }; - timer_start(&timer); - struct bvh_build_task *tasks = calloc(meshes.count, sizeof(*tasks)); - struct cr_thread *build_threads = calloc(meshes.count, sizeof(*build_threads)); - for (size_t t = 0; t < meshes.count; ++t) { - tasks[t] = (struct bvh_build_task){ - .mesh = &meshes.items[t], - }; - build_threads[t] = (struct cr_thread){ - .thread_fn = bvh_build_thread, - .user_data = &tasks[t] - }; - if (thread_start(&build_threads[t])) { - logr(error, "Failed to create a bvhBuildTask\n"); - } - } - - for (size_t t = 0; t < meshes.count; ++t) { - thread_wait(&build_threads[t]); - meshes.items[t].bvh = tasks[t].bvh; - } - printSmartTime(timer_get_ms(timer)); - free(tasks); - free(build_threads); - logr(plain, "\n"); -} - -struct bvh *computeTopLevelBvh(struct instance_arr instances) { - logr(info, "Computing top-level BVH: "); - struct timeval timer = {0}; - timer_start(&timer); - struct bvh *new = build_top_level_bvh(instances); - printSmartTime(timer_get_ms(timer)); - logr(plain, "\n"); - return new; -} - -static void printSceneStats(struct world *scene, unsigned long long ms) { - logr(info, "Scene construction completed in "); - printSmartTime(ms); - uint64_t polys = 0; - uint64_t vertices = 0; - uint64_t normals = 0; - for (size_t i = 0; i < scene->instances.count; ++i) { - if (isMesh(&scene->instances.items[i])) { - const struct mesh *mesh = &scene->meshes.items[scene->instances.items[i].object_idx]; - polys += mesh->polygons.count; - vertices += mesh->vbuf->vertices.count; - normals += mesh->vbuf->normals.count; - } - } - logr(plain, "\n"); - logr(info, "Totals: %liV, %liN, %zuI, %liP, %zuS, %zuM\n", - vertices, - normals, - scene->instances.count, - polys, - scene->spheres.count, - scene->meshes.count); -} - -//Split scene loading and prefs? -//Load the scene, allocate buffers, etc -//FIXME: Rename this func and take parseJSON out to a separate call. -int loadScene(struct renderer *r, char *input) { - - struct timeval timer = {0}; - timer_start(&timer); - - cJSON *json = cJSON_Parse(input); - if (!json) { - const char *errptr = cJSON_GetErrorPtr(); - if (errptr) { - logr(warning, "Failed to parse JSON\n"); - logr(warning, "Error before: %s\n", errptr); - return -2; - } - } - // Input is potentially very large, so we free it as soon as possible - // FIXME: mmap() input - free(input); - - //Load configuration and assets - //FIXME: Rename parseJSON - //FIXME: Actually throw out all of this code in this function and rewrite - switch (parseJSON(r, json)) { - case -1: - cJSON_Delete(json); - logr(warning, "Scene builder failed due to previous error.\n"); - return -1; - case 4: - cJSON_Delete(json); - logr(warning, "Scene debug mode enabled, won't render image.\n"); - return -1; - case -2: - cJSON_Delete(json); - //JSON parser failed - return -1; - default: - break; - } - - // This is where we prepare a cache of scene data to be sent to worker nodes - // We also apply any potential command-line overrides to that cache here as well. - // FIXME: This overrides setting should be integrated with scene loading, probably. - if (args_is_set("use_clustering")) { - // Stash a cache of scene data here - // Apply overrides to the cache here - if (args_is_set("samples_override")) { - cJSON *renderer = cJSON_GetObjectItem(json, "renderer"); - if (cJSON_IsObject(renderer)) { - int samples = args_int("samples_override"); - logr(debug, "Overriding cache sample count to %i\n", samples); - if (cJSON_IsNumber(cJSON_GetObjectItem(renderer, "samples"))) { - cJSON_ReplaceItemInObject(renderer, "samples", cJSON_CreateNumber(samples)); - } else { - cJSON_AddItemToObject(renderer, "samples", cJSON_CreateNumber(samples)); - } - } - } - - if (args_is_set("dims_override")) { - cJSON *renderer = cJSON_GetObjectItem(json, "renderer"); - if (cJSON_IsObject(renderer)) { - int width = args_int("dims_width"); - int height = args_int("dims_height"); - logr(info, "Overriding cache image dimensions to %ix%i\n", width, height); - if (cJSON_IsNumber(cJSON_GetObjectItem(renderer, "width")) && cJSON_IsNumber(cJSON_GetObjectItem(renderer, "height"))) { - cJSON_ReplaceItemInObject(renderer, "width", cJSON_CreateNumber(width)); - cJSON_ReplaceItemInObject(renderer, "height", cJSON_CreateNumber(height)); - } else { - cJSON_AddItemToObject(renderer, "width", cJSON_CreateNumber(width)); - cJSON_AddItemToObject(renderer, "height", cJSON_CreateNumber(height)); - } - } - } - - if (args_is_set("tiledims_override")) { - cJSON *renderer = cJSON_GetObjectItem(json, "renderer"); - if (cJSON_IsObject(renderer)) { - int width = args_int("tile_width"); - int height = args_int("tile_height"); - logr(info, "Overriding cache tile dimensions to %ix%i\n", width, height); - if (cJSON_IsNumber(cJSON_GetObjectItem(renderer, "tileWidth")) && cJSON_IsNumber(cJSON_GetObjectItem(renderer, "tileHeight"))) { - cJSON_ReplaceItemInObject(renderer, "tileWidth", cJSON_CreateNumber(width)); - cJSON_ReplaceItemInObject(renderer, "tileHeight", cJSON_CreateNumber(height)); - } else { - cJSON_AddItemToObject(renderer, "tileWidth", cJSON_CreateNumber(width)); - cJSON_AddItemToObject(renderer, "tileHeight", cJSON_CreateNumber(height)); - } - } - } - - if (r->prefs.selected_camera != 0) { - cJSON_AddItemToObject(json, "selected_camera", cJSON_CreateNumber(r->prefs.selected_camera)); - } - - // Store cache. This is what gets sent to worker nodes. - r->sceneCache = cJSON_PrintUnformatted(json); - } - - logr(debug, "Deleting JSON...\n"); - cJSON_Delete(json); - logr(debug, "Deleting done\n"); - - if (r->prefs.threads > 0) { - // Do some pre-render preparations - // Compute BVH acceleration structures for all objects in the scene - compute_accels(r->scene->meshes); - // And then compute a single top-level BVH that contains all the objects - r->scene->topLevel = computeTopLevelBvh(r->scene->instances); - printSceneStats(r->scene, timer_get_ms(timer)); - } else { - logr(debug, "No local render threads, skipping local BVH construction.\n"); - } - - //Quantize image into renderTiles - tile_quantize(&r->state.tiles, - r->scene->cameras.items[r->prefs.selected_camera].width, - r->scene->cameras.items[r->prefs.selected_camera].height, - r->prefs.tileWidth, - r->prefs.tileHeight, - r->prefs.tileOrder); - - for (size_t i = 0; i < r->state.tiles.count; ++i) - r->state.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) { - 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; - } - return 0; -} +#include "camera.h" +#include "tile.h" +#include "mesh.h" +#include "poly.h" //Free scene data void destroyScene(struct world *scene) { diff --git a/src/datatypes/scene.h b/src/datatypes/scene.h index 060f0ace..64f0d0f4 100644 --- a/src/datatypes/scene.h +++ b/src/datatypes/scene.h @@ -1,9 +1,9 @@ // // scene.h -// C-ray +// c-ray // // Created by Valtteri Koskivuori on 28/02/2015. -// Copyright © 2015-2022 Valtteri Koskivuori. All rights reserved. +// Copyright © 2015-2023 Valtteri Koskivuori. All rights reserved. // #pragma once @@ -38,6 +38,4 @@ struct world { float backgroundOffset; }; -int loadScene(struct renderer *r, char *input); - void destroyScene(struct world *scene); diff --git a/src/main.c b/src/main.c index 901779ab..c614b955 100644 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,6 @@ // // main.c -// C-ray +// c-ray // // Created by Valtteri Koskivuori on 12/02/2015. // Copyright © 2015-2023 Valtteri Koskivuori. All rights reserved. @@ -9,10 +9,13 @@ #include #include +#include "vendored/cJSON.h" + #include "utils/logging.h" #include "utils/fileio.h" #include "utils/args.h" #include "utils/platform/terminal.h" +#include "utils/loaders/sceneloader.h" int main(int argc, char *argv[]) { term_init(); @@ -41,7 +44,20 @@ int main(int argc, char *argv[]) { goto done; } logr(info, "%zi bytes of input JSON loaded from %s, parsing.\n", bytes, args_is_set("inputFile") ? "file" : "stdin"); - if (cr_load_scene_from_buf(renderer, input) < 0) { + cJSON *scene = cJSON_Parse(input); + if (!scene) { + const char *errptr = cJSON_GetErrorPtr(); + if (errptr) { + logr(warning, "Failed to parse JSON\n"); + logr(warning, "Error before: %s\n", errptr); + goto done; + } + } + + //FIXME: mmap() input + free(input); + + if (parse_json(renderer, scene) < 0) { logr(warning, "Scene parse failed, exiting.\n"); ret = -1; goto done; diff --git a/src/nodes/colornode.c b/src/nodes/colornode.c index 48abe98b..67f74cda 100644 --- a/src/nodes/colornode.c +++ b/src/nodes/colornode.c @@ -28,7 +28,7 @@ const struct colorNode *parseTextureNode(const char *asset_path, struct file_cac if (!node) return NULL; if (cJSON_IsArray(node)) { - return newConstantTexture(s, parseColor(node)); + return newConstantTexture(s, color_parse(node)); } // Handle options first @@ -76,7 +76,7 @@ const struct colorNode *parseTextureNode(const char *asset_path, struct file_cac //FIXME: No good way to know if it's a color, so just check if it's got "r" in there. if (cJSON_HasObjectItem(node, "r")) { // This is actually still a color object. - return newConstantTexture(s, parseColor(node)); + return newConstantTexture(s, color_parse(node)); } const cJSON *type = cJSON_GetObjectItem(node, "type"); if (cJSON_IsString(type)) { diff --git a/src/utils/loaders/sceneloader.c b/src/utils/loaders/sceneloader.c index cf251f55..d8aa6d67 100644 --- a/src/utils/loaders/sceneloader.c +++ b/src/utils/loaders/sceneloader.c @@ -38,6 +38,8 @@ #include "../../utils/args.h" #include "../../utils/timer.h" #include "../../utils/string.h" +#include "../../utils/platform/signal.h" +#include "../../accelerators/bvh.h" #include "../../nodes/bsdfnode.h" #include "../../nodes/valuenode.h" #include "../../nodes/colornode.h" @@ -434,41 +436,6 @@ static void parse_cameras(struct cr_scene *scene, const cJSON *data) { } } -struct color parseColor(const cJSON *data) { - if (cJSON_IsArray(data)) { - const float r = cJSON_IsNumber(cJSON_GetArrayItem(data, 0)) ? (float)cJSON_GetArrayItem(data, 0)->valuedouble : 0.0f; - const float g = cJSON_IsNumber(cJSON_GetArrayItem(data, 1)) ? (float)cJSON_GetArrayItem(data, 1)->valuedouble : 0.0f; - const float b = cJSON_IsNumber(cJSON_GetArrayItem(data, 2)) ? (float)cJSON_GetArrayItem(data, 2)->valuedouble : 0.0f; - const float a = cJSON_IsNumber(cJSON_GetArrayItem(data, 3)) ? (float)cJSON_GetArrayItem(data, 3)->valuedouble : 1.0f; - return (struct color){ r, g, b, a }; - } - - ASSERT(cJSON_IsObject(data)); - - const cJSON *kelvin = cJSON_GetObjectItem(data, "blackbody"); - if (cJSON_IsNumber(kelvin)) return colorForKelvin(kelvin->valuedouble); - - const cJSON *H = cJSON_GetObjectItem(data, "h"); - const cJSON *S = cJSON_GetObjectItem(data, "s"); - const cJSON *L = cJSON_GetObjectItem(data, "l"); - - if (cJSON_IsNumber(H) && cJSON_IsNumber(S) && cJSON_IsNumber(L)) { - return color_from_hsl(H->valuedouble, S->valuedouble, L->valuedouble); - } - - const cJSON *R = cJSON_GetObjectItem(data, "r"); - const cJSON *G = cJSON_GetObjectItem(data, "g"); - const cJSON *B = cJSON_GetObjectItem(data, "b"); - const cJSON *A = cJSON_GetObjectItem(data, "a"); - - return (struct color){ - cJSON_IsNumber(R) ? (float)R->valuedouble : 0.0f, - cJSON_IsNumber(G) ? (float)G->valuedouble : 0.0f, - cJSON_IsNumber(B) ? (float)B->valuedouble : 0.0f, - cJSON_IsNumber(A) ? (float)A->valuedouble : 1.0f, - }; -} - //FIXME: Convert this to use parseBsdfNode static void parseAmbientColor(struct renderer *r, const cJSON *data) { const cJSON *offset = cJSON_GetObjectItem(data, "offset"); @@ -491,7 +458,7 @@ static void parseAmbientColor(struct renderer *r, const cJSON *data) { } if (down && up) { - r->scene->background = newBackground(&r->scene->storage, newGradientTexture(&r->scene->storage, parseColor(down), parseColor(up)), NULL); + r->scene->background = newBackground(&r->scene->storage, newGradientTexture(&r->scene->storage, color_parse(down), color_parse(up)), NULL); return; } @@ -777,7 +744,76 @@ static void parseScene(struct renderer *r, const cJSON *data) { parse_meshes(r, cJSON_GetObjectItem(data, "meshes")); } -int parseJSON(struct renderer *r, const cJSON *json) { +struct bvh_build_task { + struct bvh *bvh; + const struct mesh *mesh; +}; + +void *bvh_build_thread(void *arg) { + block_signals(); + struct bvh_build_task *task = (struct bvh_build_task *)thread_user_data(arg); + task->bvh = build_mesh_bvh(task->mesh); + return NULL; +} + +static void compute_accels(struct mesh_arr meshes) { + logr(info, "Computing BVHs: "); + struct timeval timer = { 0 }; + timer_start(&timer); + struct bvh_build_task *tasks = calloc(meshes.count, sizeof(*tasks)); + struct cr_thread *build_threads = calloc(meshes.count, sizeof(*build_threads)); + for (size_t t = 0; t < meshes.count; ++t) { + tasks[t] = (struct bvh_build_task){ + .mesh = &meshes.items[t], + }; + build_threads[t] = (struct cr_thread){ + .thread_fn = bvh_build_thread, + .user_data = &tasks[t] + }; + if (thread_start(&build_threads[t])) { + logr(error, "Failed to create a bvhBuildTask\n"); + } + } + + for (size_t t = 0; t < meshes.count; ++t) { + thread_wait(&build_threads[t]); + meshes.items[t].bvh = tasks[t].bvh; + } + printSmartTime(timer_get_ms(timer)); + free(tasks); + free(build_threads); + logr(plain, "\n"); +} + +static void printSceneStats(struct world *scene, unsigned long long ms) { + logr(info, "Scene construction completed in "); + printSmartTime(ms); + uint64_t polys = 0; + uint64_t vertices = 0; + uint64_t normals = 0; + for (size_t i = 0; i < scene->instances.count; ++i) { + if (isMesh(&scene->instances.items[i])) { + const struct mesh *mesh = &scene->meshes.items[scene->instances.items[i].object_idx]; + polys += mesh->polygons.count; + vertices += mesh->vbuf->vertices.count; + normals += mesh->vbuf->normals.count; + } + } + logr(plain, "\n"); + logr(info, "Totals: %liV, %liN, %zuI, %liP, %zuS, %zuM\n", + vertices, + normals, + scene->instances.count, + polys, + scene->spheres.count, + scene->meshes.count); +} + +int parse_json(struct renderer *r, cJSON *json) { + struct timeval timer = {0}; + timer_start(&timer); + + // -------------------------------------- parse_prefs((struct cr_renderer *)r, cJSON_GetObjectItem(json, "renderer")); @@ -816,6 +852,107 @@ int parseJSON(struct renderer *r, const cJSON *json) { if (r->prefs.selected_camera != 0) logr(info, "Selecting camera %li\n", r->prefs.selected_camera); parseScene(r, cJSON_GetObjectItem(json, "scene")); + + // -------------- + + // This is where we prepare a cache of scene data to be sent to worker nodes + // We also apply any potential command-line overrides to that cache here as well. + // FIXME: This overrides setting should be integrated with scene loading, probably. + if (args_is_set("use_clustering")) { + // Stash a cache of scene data here + // Apply overrides to the cache here + if (args_is_set("samples_override")) { + cJSON *renderer = cJSON_GetObjectItem(json, "renderer"); + if (cJSON_IsObject(renderer)) { + int samples = args_int("samples_override"); + logr(debug, "Overriding cache sample count to %i\n", samples); + if (cJSON_IsNumber(cJSON_GetObjectItem(renderer, "samples"))) { + cJSON_ReplaceItemInObject(renderer, "samples", cJSON_CreateNumber(samples)); + } else { + cJSON_AddItemToObject(renderer, "samples", cJSON_CreateNumber(samples)); + } + } + } + + if (args_is_set("dims_override")) { + cJSON *renderer = cJSON_GetObjectItem(json, "renderer"); + if (cJSON_IsObject(renderer)) { + int width = args_int("dims_width"); + int height = args_int("dims_height"); + logr(info, "Overriding cache image dimensions to %ix%i\n", width, height); + if (cJSON_IsNumber(cJSON_GetObjectItem(renderer, "width")) && cJSON_IsNumber(cJSON_GetObjectItem(renderer, "height"))) { + cJSON_ReplaceItemInObject(renderer, "width", cJSON_CreateNumber(width)); + cJSON_ReplaceItemInObject(renderer, "height", cJSON_CreateNumber(height)); + } else { + cJSON_AddItemToObject(renderer, "width", cJSON_CreateNumber(width)); + cJSON_AddItemToObject(renderer, "height", cJSON_CreateNumber(height)); + } + } + } + + if (args_is_set("tiledims_override")) { + cJSON *renderer = cJSON_GetObjectItem(json, "renderer"); + if (cJSON_IsObject(renderer)) { + int width = args_int("tile_width"); + int height = args_int("tile_height"); + logr(info, "Overriding cache tile dimensions to %ix%i\n", width, height); + if (cJSON_IsNumber(cJSON_GetObjectItem(renderer, "tileWidth")) && cJSON_IsNumber(cJSON_GetObjectItem(renderer, "tileHeight"))) { + cJSON_ReplaceItemInObject(renderer, "tileWidth", cJSON_CreateNumber(width)); + cJSON_ReplaceItemInObject(renderer, "tileHeight", cJSON_CreateNumber(height)); + } else { + cJSON_AddItemToObject(renderer, "tileWidth", cJSON_CreateNumber(width)); + cJSON_AddItemToObject(renderer, "tileHeight", cJSON_CreateNumber(height)); + } + } + } + + if (r->prefs.selected_camera != 0) { + cJSON_AddItemToObject(json, "selected_camera", cJSON_CreateNumber(r->prefs.selected_camera)); + } + + // Store cache. This is what gets sent to worker nodes. + r->sceneCache = cJSON_PrintUnformatted(json); + } + logr(debug, "Deleting JSON...\n"); + cJSON_Delete(json); + logr(debug, "Deleting done\n"); + + if (r->prefs.threads > 0) { + // Do some pre-render preparations + // Compute BVH acceleration structures for all meshes in the scene + compute_accels(r->scene->meshes); + + // And then compute a single top-level BVH that contains all the objects + logr(info, "Computing top-level BVH: "); + struct timeval timer = {0}; + timer_start(&timer); + r->scene->topLevel = build_top_level_bvh(r->scene->instances); + printSmartTime(timer_get_ms(timer)); + logr(plain, "\n"); + + printSceneStats(r->scene, timer_get_ms(timer)); + } else { + logr(debug, "No local render threads, skipping local BVH construction.\n"); + } + + //Quantize image into renderTiles + tile_quantize(&r->state.tiles, + r->scene->cameras.items[r->prefs.selected_camera].width, + r->scene->cameras.items[r->prefs.selected_camera].height, + r->prefs.tileWidth, + r->prefs.tileHeight, + r->prefs.tileOrder); + + for (size_t i = 0; i < r->state.tiles.count; ++i) + r->state.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) { + 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; + } + return 0; } diff --git a/src/utils/loaders/sceneloader.h b/src/utils/loaders/sceneloader.h index 8e572663..d043c9b6 100644 --- a/src/utils/loaders/sceneloader.h +++ b/src/utils/loaders/sceneloader.h @@ -12,6 +12,4 @@ struct renderer; -int parseJSON(struct renderer *r, const cJSON *json); - -struct color parseColor(const cJSON *data); \ No newline at end of file +int parse_json(struct renderer *r, cJSON *json); diff --git a/src/utils/logging.c b/src/utils/logging.c index 38995acf..b8ae803a 100644 --- a/src/utils/logging.c +++ b/src/utils/logging.c @@ -90,6 +90,7 @@ void logr(enum logType type, const char *fmt, ...) { } } +//TODO: Have this take a buf void printSmartTime(unsigned long long ms) { char buf[64]; smartTime(ms, buf); diff --git a/src/utils/protocol/worker.c b/src/utils/protocol/worker.c index b3bbe34f..cada2127 100644 --- a/src/utils/protocol/worker.c +++ b/src/utils/protocol/worker.c @@ -23,6 +23,7 @@ #include "../../renderer/pathtrace.h" #include "../../datatypes/image/texture.h" #include "../../datatypes/scene.h" +#include "../loaders/sceneloader.h" #include "../../datatypes/camera.h" #include "../platform/mutex.h" #include "../platform/thread.h" @@ -58,11 +59,12 @@ struct workerThreadState { struct render_tile *current; }; -static cJSON *validateHandshake(const cJSON *in) { +static cJSON *validateHandshake(cJSON *in) { const cJSON *version = cJSON_GetObjectItem(in, "version"); const cJSON *githash = cJSON_GetObjectItem(in, "githash"); if (!stringEquals(version->valuestring, PROTO_VERSION)) return errorResponse("Protocol version mismatch"); if (!stringEquals(githash->valuestring, gitHash())) return errorResponse("Git hash mismatch"); + cJSON_Delete(in); return newAction("startSync"); } @@ -78,14 +80,13 @@ static cJSON *receiveScene(const cJSON *json) { // And then the scene cJSON *scene = cJSON_GetObjectItem(json, "data"); - char *sceneText = cJSON_PrintUnformatted(scene); logr(info, "Received scene description\n"); g_worker_renderer = renderer_new(); g_worker_renderer->state.file_cache = cache; g_worker_socket_mutex = mutex_create(); cJSON *assetPathJson = cJSON_GetObjectItem(json, "assetPath"); g_worker_renderer->prefs.assetPath = stringCopy(assetPathJson->valuestring); - if (loadScene(g_worker_renderer, sceneText)) { + if (parse_json(g_worker_renderer, scene)) { return errorResponse("Scene parsing error"); } cache_destroy(cache); @@ -288,7 +289,7 @@ static cJSON *startRender(int connectionSocket) { } // Worker command handler -static cJSON *processCommand(int connectionSocket, const cJSON *json) { +static cJSON *processCommand(int connectionSocket, cJSON *json) { if (!json) { return errorResponse("Couldn't parse incoming JSON"); } @@ -306,9 +307,11 @@ static cJSON *processCommand(int connectionSocket, const cJSON *json) { break; case 3: // startRender contains worker event loop and blocks until render completion. + cJSON_Delete(json); return startRender(connectionSocket); break; default: + cJSON_Delete(json); return errorResponse("Unknown command"); break; } @@ -412,13 +415,11 @@ int startWorkerServer() { cJSON *myResponse = processCommand(connectionSocket, message); if (!myResponse) { if (buf) free(buf); - cJSON_Delete(message); break; } char *responseText = cJSON_PrintUnformatted(myResponse); if (!chunkedSend(connectionSocket, responseText, NULL)) { logr(debug, "chunkedSend() failed, error %s\n", strerror(errno)); - cJSON_Delete(message); break; }; free(responseText); @@ -426,11 +427,9 @@ int startWorkerServer() { buf = NULL; if (containsGoodbye(myResponse) || containsError(myResponse)) { cJSON_Delete(myResponse); - cJSON_Delete(message); break; } cJSON_Delete(myResponse); - cJSON_Delete(message); } bail: if (g_running) { diff --git a/tests/test_parser.h b/tests/test_parser.h index 9d6ec2ad..419dd589 100644 --- a/tests/test_parser.h +++ b/tests/test_parser.h @@ -17,7 +17,7 @@ bool parser_color_rgb(void) { struct color c = { 0 }; data = cJSON_Parse("{}"); - c = parseColor(data); + c = color_parse(data); roughly_equals(c.red, 0.0f); roughly_equals(c.green, 0.0f); roughly_equals(c.blue, 0.0f); @@ -25,7 +25,7 @@ bool parser_color_rgb(void) { cJSON_Delete(data); data = cJSON_Parse("{\"r\": 0.1, \"g\": 0.2, \"b\": 0.3, \"a\": 0.4}"); - c = parseColor(data); + c = color_parse(data); roughly_equals(c.red, 0.1f); roughly_equals(c.green, 0.2f); roughly_equals(c.blue, 0.3f); @@ -33,7 +33,7 @@ bool parser_color_rgb(void) { cJSON_Delete(data); data = cJSON_Parse("{\"g\": 0.2, \"b\": 0.3, \"a\": 0.4}"); - c = parseColor(data); + c = color_parse(data); roughly_equals(c.red, 0.0f); roughly_equals(c.green, 0.2f); roughly_equals(c.blue, 0.3f); @@ -41,7 +41,7 @@ bool parser_color_rgb(void) { cJSON_Delete(data); data = cJSON_Parse("{\"r\": 0.1, \"b\": 0.3, \"a\": 0.4}"); - c = parseColor(data); + c = color_parse(data); roughly_equals(c.red, 0.1f); roughly_equals(c.green, 0.0f); roughly_equals(c.blue, 0.3f); @@ -49,7 +49,7 @@ bool parser_color_rgb(void) { cJSON_Delete(data); data = cJSON_Parse("{\"r\": 0.1, \"g\": 0.2, \"a\": 0.4}"); - c = parseColor(data); + c = color_parse(data); roughly_equals(c.red, 0.1f); roughly_equals(c.green, 0.2f); roughly_equals(c.blue, 0.0f); @@ -57,7 +57,7 @@ bool parser_color_rgb(void) { cJSON_Delete(data); data = cJSON_Parse("{\"r\": 0.1, \"g\": 0.2, \"b\": 0.3}"); - c = parseColor(data); + c = color_parse(data); roughly_equals(c.red, 0.1f); roughly_equals(c.green, 0.2f); roughly_equals(c.blue, 0.3f); @@ -73,7 +73,7 @@ bool parser_color_array(void) { struct color c = { 0 }; data = cJSON_Parse("[]"); - c = parseColor(data); + c = color_parse(data); roughly_equals(c.red, 0.0f); roughly_equals(c.green, 0.0f); roughly_equals(c.blue, 0.0f); @@ -81,7 +81,7 @@ bool parser_color_array(void) { cJSON_Delete(data); data = cJSON_Parse("[0.1, 0.2, 0.3, 0.4]"); - c = parseColor(data); + c = color_parse(data); roughly_equals(c.red, 0.1f); roughly_equals(c.green, 0.2f); roughly_equals(c.blue, 0.3f); @@ -89,7 +89,7 @@ bool parser_color_array(void) { cJSON_Delete(data); data = cJSON_Parse("[0.2, 0.3, 0.4]"); - c = parseColor(data); + c = color_parse(data); roughly_equals(c.red, 0.2f); roughly_equals(c.green, 0.3f); roughly_equals(c.blue, 0.4f); @@ -97,7 +97,7 @@ bool parser_color_array(void) { cJSON_Delete(data); data = cJSON_Parse("[0.3, 0.4]"); - c = parseColor(data); + c = color_parse(data); roughly_equals(c.red, 0.3f); roughly_equals(c.green, 0.4f); roughly_equals(c.blue, 0.0f); @@ -105,7 +105,7 @@ bool parser_color_array(void) { cJSON_Delete(data); data = cJSON_Parse("[0.4]"); - c = parseColor(data); + c = color_parse(data); roughly_equals(c.red, 0.4f); roughly_equals(c.green, 0.0f); roughly_equals(c.blue, 0.0f); @@ -120,7 +120,7 @@ bool parser_color_blackbody(void) { struct color c = { 0 }; data = cJSON_Parse("{\"blackbody\": 6000}"); - c = parseColor(data); + c = color_parse(data); very_roughly_equals(c.red, 1.0f); very_roughly_equals(c.green, 0.96f); very_roughly_equals(c.blue, 0.92f); @@ -136,7 +136,7 @@ bool parser_color_hsl(void) { // Lightness 1 data = cJSON_Parse("{\"h\": 0, \"s\": 100, \"l\": 100}"); - c = parseColor(data); + c = color_parse(data); roughly_equals(c.red, 1.0f); roughly_equals(c.green, 1.0f); roughly_equals(c.blue, 1.0f); @@ -145,7 +145,7 @@ bool parser_color_hsl(void) { // Lightness 0 data = cJSON_Parse("{\"h\": 0, \"s\": 100, \"l\": 0}"); - c = parseColor(data); + c = color_parse(data); roughly_equals(c.red, 0.0f); roughly_equals(c.green, 0.0f); roughly_equals(c.blue, 0.0f); @@ -154,7 +154,7 @@ bool parser_color_hsl(void) { // Red data = cJSON_Parse("{\"h\": 0, \"s\": 100, \"l\": 50}"); - c = parseColor(data); + c = color_parse(data); roughly_equals(c.red, 1.0f); roughly_equals(c.green, 0.0f); roughly_equals(c.blue, 0.0f); @@ -163,7 +163,7 @@ bool parser_color_hsl(void) { // Green data = cJSON_Parse("{\"h\": 120, \"s\": 100, \"l\": 50}"); - c = parseColor(data); + c = color_parse(data); very_roughly_equals(c.red, 0.0f); very_roughly_equals(c.green, 1.0f); very_roughly_equals(c.blue, 0.0f); @@ -172,7 +172,7 @@ bool parser_color_hsl(void) { // Blue data = cJSON_Parse("{\"h\": 240, \"s\": 100, \"l\": 50}"); - c = parseColor(data); + c = color_parse(data); very_roughly_equals(c.red, 0.0f); very_roughly_equals(c.green, 0.0f); very_roughly_equals(c.blue, 1.0f); @@ -181,7 +181,7 @@ bool parser_color_hsl(void) { // Should be red again data = cJSON_Parse("{\"h\": 360, \"s\": 100, \"l\": 50}"); - c = parseColor(data); + c = color_parse(data); very_roughly_equals(c.red, 1.0f); very_roughly_equals(c.green, 0.0f); very_roughly_equals(c.blue, 0.0f); @@ -189,4 +189,4 @@ bool parser_color_hsl(void) { cJSON_Delete(data); return true; -} \ No newline at end of file +}