From a3e61eadf71e4624e218be96e83bc8d6325f7165 Mon Sep 17 00:00:00 2001 From: Valtteri Koskivuori Date: Tue, 2 Jul 2024 22:27:15 +0300 Subject: [PATCH] lib: Compute BVHs asynchronously A new cr_mesh_finalize() API function starts the BVH build task while other meshes are still being loaded to reduce initialization time. --- bindings/blender_init.py | 1 + bindings/c_ray.py | 2 ++ bindings/cray_wrap.c | 14 ++++++++++++++ include/c-ray/c-ray.h | 1 + src/lib/accelerators/bvh.c | 32 -------------------------------- src/lib/accelerators/bvh.h | 2 -- src/lib/api/c-ray.c | 26 ++++++++++++++++++++++++++ src/lib/datatypes/scene.h | 2 ++ src/lib/protocol/worker.c | 6 +++--- src/lib/renderer/renderer.c | 10 +++++++--- 10 files changed, 56 insertions(+), 40 deletions(-) diff --git a/bindings/blender_init.py b/bindings/blender_init.py index 633cc53a..abb4d0bb 100644 --- a/bindings/blender_init.py +++ b/bindings/blender_init.py @@ -323,6 +323,7 @@ def sync_scene(self, depsgraph): facebuf = (c_ray.cr_face * len(faces))(*faces) cr_mesh.bind_faces(bytearray(facebuf), len(faces)) cr_mesh.bind_vertex_buf(me) + cr_mesh.finalize() # Set background shader bl_nodetree = bpy.data.worlds[0].node_tree diff --git a/bindings/c_ray.py b/bindings/c_ray.py index f5128bda..ab6a8884 100644 --- a/bindings/c_ray.py +++ b/bindings/c_ray.py @@ -264,6 +264,8 @@ def bind_vertex_buf(self, me): _lib.mesh_bind_vertex_buf(self.scene_ptr, self.cr_idx, self.v, self.vn, self.n, self.nn, self.t, self.tn) def bind_faces(self, faces, face_count): _lib.mesh_bind_faces(self.scene_ptr, self.cr_idx, faces, face_count) + def finalize(self): + _lib.mesh_finalize(self.scene_ptr, self.cr_idx) def instance_new(self): self.instances.append(instance(self.scene_ptr, self, 0)) return self.instances[-1] diff --git a/bindings/cray_wrap.c b/bindings/cray_wrap.c index 7c66fe4d..a4a0c784 100644 --- a/bindings/cray_wrap.c +++ b/bindings/cray_wrap.c @@ -424,6 +424,19 @@ static PyObject *py_cr_mesh_bind_faces(PyObject *self, PyObject *args) { Py_RETURN_NONE; } +static PyObject *py_cr_mesh_finalize(PyObject *self, PyObject *args) { + (void)self; (void)args; + PyObject *s_ext; + cr_mesh mesh; + if (!PyArg_ParseTuple(args, "Ol", &s_ext, &mesh)) { + return NULL; + } + + struct cr_scene *s = PyCapsule_GetPointer(s_ext, "cray.cr_scene"); + cr_mesh_finalize(s, mesh); + Py_RETURN_NONE; +} + static PyObject *py_cr_scene_mesh_new(PyObject *self, PyObject *args) { (void)self; (void)args; PyObject *s_ext; @@ -732,6 +745,7 @@ static PyMethodDef cray_methods[] = { { "scene_add_sphere", py_cr_scene_add_sphere, METH_VARARGS, "" }, { "mesh_bind_vertex_buf", py_cr_mesh_bind_vertex_buf, METH_VARARGS, "" }, { "mesh_bind_faces", py_cr_mesh_bind_faces, METH_VARARGS, "" }, + { "mesh_finalize", py_cr_mesh_finalize, METH_VARARGS, "" }, { "scene_mesh_new", py_cr_scene_mesh_new, METH_VARARGS, "" }, { "scene_get_mesh", py_cr_scene_get_mesh, METH_VARARGS, "" }, { "camera_new", py_cr_camera_new, METH_VARARGS, "" }, diff --git a/include/c-ray/c-ray.h b/include/c-ray/c-ray.h index acb825d6..bc55a308 100644 --- a/include/c-ray/c-ray.h +++ b/include/c-ray/c-ray.h @@ -198,6 +198,7 @@ CR_EXPORT cr_mesh cr_scene_get_mesh(struct cr_scene *s_ext, const char *name); CR_EXPORT void cr_mesh_bind_vertex_buf(struct cr_scene *s_ext, cr_mesh mesh, struct cr_vertex_buf_param buf); CR_EXPORT void cr_mesh_bind_faces(struct cr_scene *s_ext, cr_mesh mesh, struct cr_face *faces, size_t face_count); +CR_EXPORT void cr_mesh_finalize(struct cr_scene *s_ext, cr_mesh mesh); // -- Camera -- // FIXME: Use cr_vector diff --git a/src/lib/accelerators/bvh.c b/src/lib/accelerators/bvh.c index 98c22974..d89ff003 100644 --- a/src/lib/accelerators/bvh.c +++ b/src/lib/accelerators/bvh.c @@ -16,9 +16,6 @@ #include "../datatypes/poly.h" #include "../renderer/instance.h" #include "../../common/vector.h" -#include "../../common/platform/thread.h" -#include "../../common/platform/thread_pool.h" -#include "../../common/platform/capabilities.h" #include "../../common/platform/signal.h" #include "../../common/timer.h" @@ -689,32 +686,3 @@ void destroy_bvh(struct bvh *bvh) { free(bvh); } -void bvh_build_task(void *arg) { - block_signals(); - struct mesh *mesh = (struct mesh *)arg; - struct timeval timer = { 0 }; - timer_start(&timer); - mesh->bvh = build_mesh_bvh(mesh); - if (mesh->bvh) { - logr(debug, "Built BVH for %s, took %lums\n", mesh->name, timer_get_ms(timer)); - } else { - logr(debug, "BVH build FAILED for %s\n", mesh->name); - } -} - -// FIXME: Add pthread_cancel() support -void compute_accels(struct mesh_arr meshes) { - struct cr_thread_pool *pool = thread_pool_create(sys_get_cores()); - logr(info, "Updating %zu BVHs: ", meshes.count); - struct timeval timer = { 0 }; - timer_start(&timer); - for (size_t i = 0; i < meshes.count; ++i) { - if (!meshes.items[i].bvh) thread_pool_enqueue(pool, bvh_build_task, &meshes.items[i]); - } - thread_pool_wait(pool); - - printSmartTime(timer_get_ms(timer)); - logr(plain, "\n"); - thread_pool_destroy(pool); -} - diff --git a/src/lib/accelerators/bvh.h b/src/lib/accelerators/bvh.h index c635adeb..96b829a4 100644 --- a/src/lib/accelerators/bvh.h +++ b/src/lib/accelerators/bvh.h @@ -55,5 +55,3 @@ bool traverse_bottom_level_bvh( /// Frees the memory allocated by the given BVH void destroy_bvh(struct bvh *); - -void compute_accels(struct mesh_arr meshes); diff --git a/src/lib/api/c-ray.c b/src/lib/api/c-ray.c index d48bbbb6..a7011af7 100644 --- a/src/lib/api/c-ray.c +++ b/src/lib/api/c-ray.c @@ -18,6 +18,7 @@ #include "../../common/gitsha1.h" #include "../../common/fileio.h" #include "../../common/platform/terminal.h" +#include "../../common/platform/signal.h" #include "../../common/assert.h" #include "../../common/texture.h" #include "../../common/string.h" @@ -29,6 +30,8 @@ #include "../../common/json_loader.h" #include "../protocol/protocol.h" #include "../../common/node_parse.h" +#include "../../common/platform/thread_pool.h" +#include "../accelerators/bvh.h" #ifdef CRAY_DEBUG_ENABLED #define DEBUG "D" @@ -254,6 +257,20 @@ cr_sphere cr_scene_add_sphere(struct cr_scene *s_ext, float radius) { return sphere_arr_add(&scene->spheres, (struct sphere){ .radius = radius }); } +void bvh_build_task(void *arg) { + block_signals(); + struct mesh *mesh = (struct mesh *)arg; + if (mesh->bvh) destroy_bvh(mesh->bvh); + struct timeval timer = { 0 }; + timer_start(&timer); + mesh->bvh = build_mesh_bvh(mesh); + if (mesh->bvh) { + logr(debug, "Built BVH for %s, took %lums\n", mesh->name, timer_get_ms(timer)); + } else { + logr(debug, "BVH build FAILED for %s\n", mesh->name); + } +} + void cr_mesh_bind_vertex_buf(struct cr_scene *s_ext, cr_mesh mesh, struct cr_vertex_buf_param buf) { if (!s_ext) return; struct world *scene = (struct world *)s_ext; @@ -290,6 +307,14 @@ void cr_mesh_bind_faces(struct cr_scene *s_ext, cr_mesh mesh, struct cr_face *fa } } +void cr_mesh_finalize(struct cr_scene *s_ext, cr_mesh mesh) { + if (!s_ext) return; + struct world *scene = (struct world *)s_ext; + if ((size_t)mesh > scene->meshes.count - 1) return; + struct mesh *m = &scene->meshes.items[mesh]; + thread_pool_enqueue(scene->bvh_builder, bvh_build_task, m); +} + cr_mesh cr_scene_mesh_new(struct cr_scene *s_ext, const char *name) { if (!s_ext) return -1; struct world *scene = (struct world *)s_ext; @@ -844,6 +869,7 @@ void cr_renderer_restart_interactive(struct cr_renderer *ext) { r->state.workers.items[i].totalSamples = 0; } update_toplevel_bvh(r->scene); + thread_pool_wait(r->scene->bvh_builder); mutex_release(r->state.current_set->tile_mutex); } diff --git a/src/lib/datatypes/scene.h b/src/lib/datatypes/scene.h index 6da8764f..49fa7cfc 100644 --- a/src/lib/datatypes/scene.h +++ b/src/lib/datatypes/scene.h @@ -40,6 +40,8 @@ struct world { struct cr_rwlock bvh_lock; struct bvh *topLevel; // FIXME: Move to state? bool top_level_dirty; + struct cr_thread_pool *bvh_builder; + struct sphere_arr spheres; struct camera_arr cameras; struct node_storage storage; // FIXME: Move to state? diff --git a/src/lib/protocol/worker.c b/src/lib/protocol/worker.c index 59820723..75a1cc64 100644 --- a/src/lib/protocol/worker.c +++ b/src/lib/protocol/worker.c @@ -27,6 +27,7 @@ #include "../../common/texture.h" #include "../../common/platform/mutex.h" #include "../../common/platform/thread.h" +#include "../../common/platform/thread_pool.h" #include "../../common/networking.h" #include "../../common/string.h" #include "../../common/gitsha1.h" @@ -231,9 +232,8 @@ static cJSON *startRender(int connectionSocket, size_t thread_limit) { 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 - // Compute BVH acceleration structures for all meshes in the scene - compute_accels(r->scene->meshes); + // Ensure BVHs are up to date + thread_pool_wait(r->scene->bvh_builder); // And then compute a single top-level BVH that contains all the objects logr(info, "Computing top-level BVH: "); diff --git a/src/lib/renderer/renderer.c b/src/lib/renderer/renderer.c index 4e902850..d66651d3 100644 --- a/src/lib/renderer/renderer.c +++ b/src/lib/renderer/renderer.c @@ -15,6 +15,7 @@ #include "../../common/timer.h" #include "../../common/texture.h" #include "../../common/platform/thread.h" +#include "../../common/platform/thread_pool.h" #include "../../common/platform/mutex.h" #include "../../common/platform/capabilities.h" #include "../../common/platform/signal.h" @@ -175,9 +176,10 @@ void renderer_render(struct renderer *r) { inst->bbuf = &r->scene->shader_buffers.items[inst->bbuf_idx]; } - // Do some pre-render preparations - // Compute BVH acceleration structures for all meshes in the scene - compute_accels(r->scene->meshes); + // Ensure BVHs are up to date + logr(debug, "Waiting for BVH thread pool\n"); + thread_pool_wait(r->scene->bvh_builder); + logr(debug, "Continuing\n"); // And compute an initial top-level BVH. update_toplevel_bvh(r->scene); @@ -479,11 +481,13 @@ struct renderer *renderer_new(void) { r->scene->asset_path = stringCopy("./"); r->scene->storage.node_pool = newBlock(NULL, 1024); r->scene->storage.node_table = newHashtable(compareNodes, &r->scene->storage.node_pool); + r->scene->bvh_builder = thread_pool_create(sys_get_cores()); return r; } void renderer_destroy(struct renderer *r) { if (!r) return; + thread_pool_destroy(r->scene->bvh_builder); scene_destroy(r->scene); worker_arr_free(&r->state.workers); render_client_arr_free(&r->state.clients);