From 87cbdcbe7cf6249dae4acf28662504715b80bbdf Mon Sep 17 00:00:00 2001 From: Weizhen Huang Date: Tue, 4 Apr 2023 16:17:05 +0200 Subject: [PATCH] Refactor: move part of light tree logic from #LightManager to #LightTree --- intern/cycles/scene/light.cpp | 96 +++----------------- intern/cycles/scene/light.h | 1 + intern/cycles/scene/light_tree.cpp | 137 +++++++++++++++++++++++------ intern/cycles/scene/light_tree.h | 42 +++++---- 4 files changed, 148 insertions(+), 128 deletions(-) diff --git a/intern/cycles/scene/light.cpp b/intern/cycles/scene/light.cpp index 58a6f72c82a..974b2144a76 100644 --- a/intern/cycles/scene/light.cpp +++ b/intern/cycles/scene/light.cpp @@ -445,90 +445,23 @@ void LightManager::device_update_tree(Device *, /* Update light tree. */ progress.set_status("Updating Lights", "Computing tree"); - /* Add both lights and emissive triangles to this vector for light tree construction. */ - vector emitters; - emitters.reserve(kintegrator->num_distribution); - vector distant_lights; - distant_lights.reserve(kintegrator->num_distant_lights); - vector object_lookup_offsets(scene->objects.size()); - - /* When we keep track of the light index, only contributing lights will be added to the device. - * Therefore, we want to keep track of the light's index on the device. - * However, we also need the light's index in the scene when we're constructing the tree. */ - int device_light_index = 0; - int scene_light_index = 0; - foreach (Light *light, scene->lights) { - if (light->is_enabled) { - if (light->light_type == LIGHT_BACKGROUND || light->light_type == LIGHT_DISTANT) { - distant_lights.emplace_back(scene, ~device_light_index, scene_light_index); - } - else { - emitters.emplace_back(scene, ~device_light_index, scene_light_index); - } - - device_light_index++; - } - - scene_light_index++; - } - - /* Similarly, we also want to keep track of the index of triangles that are emissive. */ - size_t total_triangles = 0; - int object_id = 0; - foreach (Object *object, scene->objects) { - if (progress.get_cancel()) - return; - - if (!object->usable_as_light()) { - object_id++; - continue; - } - - object_lookup_offsets[object_id] = total_triangles; - - /* Count emissive triangles. */ - Mesh *mesh = static_cast(object->get_geometry()); - size_t mesh_num_triangles = mesh->num_triangles(); - - for (size_t i = 0; i < mesh_num_triangles; i++) { - int shader_index = mesh->get_shader()[i]; - Shader *shader = (shader_index < mesh->get_used_shaders().size()) ? - static_cast(mesh->get_used_shaders()[shader_index]) : - scene->default_surface; - - if (shader->emission_sampling != EMISSION_SAMPLING_NONE) { - emitters.emplace_back(scene, i, object_id); - } - } - - total_triangles += mesh_num_triangles; - object_id++; - } - - /* Append distant lights to the end of `emitters` */ - std::move(distant_lights.begin(), distant_lights.end(), std::back_inserter(emitters)); - - /* Update integrator state. */ - kintegrator->use_direct_light = !emitters.empty(); - /* TODO: For now, we'll start with a smaller number of max lights in a node. * More benchmarking is needed to determine what number works best. */ - LightTree light_tree(emitters, kintegrator->num_distant_lights, 8); + LightTree light_tree(scene, dscene, progress, 8); + LightTreeNode *root = light_tree.build(scene, dscene); /* We want to create separate arrays corresponding to triangles and lights, * which will be used to index back into the light tree for PDF calculations. */ - const size_t num_lights = kintegrator->num_lights; - uint *light_array = dscene->light_to_tree.alloc(num_lights); - uint *object_offsets = dscene->object_lookup_offset.alloc(object_lookup_offsets.size()); - uint *triangle_array = dscene->triangle_to_tree.alloc(total_triangles); - - for (int i = 0; i < object_lookup_offsets.size(); i++) { - object_offsets[i] = object_lookup_offsets[i]; - } + uint *light_array = dscene->light_to_tree.alloc(kintegrator->num_lights); + uint *triangle_array = dscene->triangle_to_tree.alloc(light_tree.num_triangles); /* First initialize the light tree's nodes. */ - KernelLightTreeNode *light_tree_nodes = dscene->light_tree_nodes.alloc(light_tree.size()); - KernelLightTreeEmitter *light_tree_emitters = dscene->light_tree_emitters.alloc(emitters.size()); + const size_t num_emitters = light_tree.num_emitters(); + KernelLightTreeNode *light_tree_nodes = dscene->light_tree_nodes.alloc(light_tree.num_nodes); + KernelLightTreeEmitter *light_tree_emitters = dscene->light_tree_emitters.alloc(num_emitters); + + /* Update integrator state. */ + kintegrator->use_direct_light = num_emitters > 0; /* Copy the light tree nodes to an array in the device. */ /* The nodes are arranged in a depth-first order, meaning the left child of each inner node @@ -543,8 +476,8 @@ void LightManager::device_update_tree(Device *, int left_index_stack[32]; /* `sizeof(bit_trail) * 8 == 32`. */ LightTreeNode *right_node_stack[32]; int stack_id = 0; - const LightTreeNode *node = light_tree.get_root(); - for (int node_index = 0; node_index < light_tree.size(); node_index++) { + const LightTreeNode *node = root; + for (int node_index = 0; node_index < light_tree.num_nodes; node_index++) { light_tree_nodes[node_index].energy = node->measure.energy; light_tree_nodes[node_index].bbox.min = node->measure.bbox.min; @@ -563,7 +496,7 @@ void LightManager::device_update_tree(Device *, for (int i = 0; i < node->num_emitters; i++) { int emitter_index = i + node->first_emitter_index; - LightTreeEmitter &emitter = emitters[emitter_index]; + const LightTreeEmitter &emitter = light_tree.get_emitter(emitter_index); light_tree_emitters[emitter_index].energy = emitter.measure.energy; light_tree_emitters[emitter_index].theta_o = emitter.measure.bcone.theta_o; @@ -600,7 +533,7 @@ void LightManager::device_update_tree(Device *, light_tree_emitters[emitter_index].prim_id = emitter.prim_id + mesh->prim_offset; light_tree_emitters[emitter_index].mesh_light.shader_flag = shader_flag; light_tree_emitters[emitter_index].emission_sampling = shader->emission_sampling; - triangle_array[emitter.prim_id + object_lookup_offsets[emitter.object_id]] = + triangle_array[emitter.prim_id + dscene->object_lookup_offset[emitter.object_id]] = emitter_index; } else { @@ -634,7 +567,6 @@ void LightManager::device_update_tree(Device *, dscene->light_tree_nodes.copy_to_device(); dscene->light_tree_emitters.copy_to_device(); dscene->light_to_tree.copy_to_device(); - dscene->object_lookup_offset.copy_to_device(); dscene->triangle_to_tree.copy_to_device(); } diff --git a/intern/cycles/scene/light.h b/intern/cycles/scene/light.h index 028340641b0..0e73c3c9cf1 100644 --- a/intern/cycles/scene/light.h +++ b/intern/cycles/scene/light.h @@ -81,6 +81,7 @@ class Light : public Node { bool has_contribution(Scene *scene); friend class LightManager; + friend class LightTree; }; class LightManager { diff --git a/intern/cycles/scene/light_tree.cpp b/intern/cycles/scene/light_tree.cpp index 08887b2af3d..e2fe0529815 100644 --- a/intern/cycles/scene/light_tree.cpp +++ b/intern/cycles/scene/light_tree.cpp @@ -5,6 +5,8 @@ #include "scene/mesh.h" #include "scene/object.h" +#include "util/progress.h" + CCL_NAMESPACE_BEGIN float OrientationBounds::calculate_measure() const @@ -208,43 +210,114 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene, int prim_id, int object_id) } } -LightTree::LightTree(vector &emitters, - const int &num_distant_lights, +LightTree::LightTree(Scene *scene, + DeviceScene *dscene, + Progress &progress, uint max_lights_in_leaf) + : progress_(progress), max_lights_in_leaf_(max_lights_in_leaf) { - if (emitters.empty()) { - return; + KernelIntegrator *kintegrator = &dscene->data.integrator; + + /* Add both lights and emissive triangles to this vector for light tree construction. */ + emitters_.reserve(kintegrator->num_distribution); + distant_lights_.reserve(kintegrator->num_distant_lights); + uint *object_offsets = dscene->object_lookup_offset.alloc(scene->objects.size()); + + /* When we keep track of the light index, only contributing lights will be added to the device. + * Therefore, we want to keep track of the light's index on the device. + * However, we also need the light's index in the scene when we're constructing the tree. */ + int device_light_index = 0; + int scene_light_index = 0; + for (Light *light : scene->lights) { + if (light->is_enabled) { + if (light->light_type == LIGHT_BACKGROUND || light->light_type == LIGHT_DISTANT) { + distant_lights_.emplace_back(scene, ~device_light_index, scene_light_index); + } + else { + emitters_.emplace_back(scene, ~device_light_index, scene_light_index); + } + + device_light_index++; + } + + scene_light_index++; } - max_lights_in_leaf_ = max_lights_in_leaf; - const int num_emitters = emitters.size(); - const int num_local_lights = num_emitters - num_distant_lights; + /* Similarly, we also want to keep track of the index of triangles that are emissive. */ + int object_id = 0; + for (Object *object : scene->objects) { + if (progress_.get_cancel()) { + return; + } + + if (!object->usable_as_light()) { + object_id++; + continue; + } + + object_offsets[object_id] = num_triangles; + + /* Count emissive triangles. */ + Mesh *mesh = static_cast(object->get_geometry()); + size_t mesh_num_triangles = mesh->num_triangles(); + for (size_t i = 0; i < mesh_num_triangles; i++) { + int shader_index = mesh->get_shader()[i]; + Shader *shader = (shader_index < mesh->get_used_shaders().size()) ? + static_cast(mesh->get_used_shaders()[shader_index]) : + scene->default_surface; + + if (shader->emission_sampling != EMISSION_SAMPLING_NONE) { + emitters_.emplace_back(scene, i, object_id); + } + } + num_triangles += mesh_num_triangles; + object_id++; + } + + /* Copy array to device. */ + dscene->object_lookup_offset.copy_to_device(); +} + +LightTreeNode *LightTree::build(Scene *scene, DeviceScene *dscene) +{ + if (emitters_.empty() && distant_lights_.empty()) { + return nullptr; + } + + /* At this stage `emitters_` only contains local lights, the distant lights will be merged + * into `emitters_` when Light Tree building is finished. */ + const int num_local_lights = emitters_.size(); + const int num_distant_lights = distant_lights_.size(); root_ = create_node(LightTreeMeasure::empty, 0); /* All local lights are grouped to the left child as an inner node. */ - recursive_build(left, root_.get(), 0, num_local_lights, &emitters, 0, 1); + recursive_build(left, root_.get(), 0, num_local_lights, emitters_.data(), 0, 1); task_pool.wait_work(); /* All distant lights are grouped to the right child as a leaf node. */ root_->children[right] = create_node(LightTreeMeasure::empty, 1); - for (int i = num_local_lights; i < num_emitters; i++) { - root_->children[right]->add(emitters[i]); + for (int i = 0; i < num_distant_lights; i++) { + root_->children[right]->add(distant_lights_[i]); } root_->children[right]->make_leaf(num_local_lights, num_distant_lights); + + /* Append distant lights to the end of `light_prims` */ + std::move(distant_lights_.begin(), distant_lights_.end(), std::back_inserter(emitters_)); + + return root_.get(); } void LightTree::recursive_build(const Child child, LightTreeNode *parent, const int start, const int end, - vector *emitters, + LightTreeEmitter *emitters, const uint bit_trail, const int depth) { - BoundBox centroid_bounds = BoundBox::empty; - for (int i = start; i < end; i++) { - centroid_bounds.grow((*emitters)[i].centroid); + if (progress_.get_cancel()) { + return; } parent->children[child] = create_node(LightTreeMeasure::empty, bit_trail); @@ -253,13 +326,13 @@ void LightTree::recursive_build(const Child child, /* Find the best place to split the emitters into 2 nodes. * If the best split cost is no better than making a leaf node, make a leaf instead. */ int split_dim = -1, middle; - if (should_split(*emitters, start, middle, end, node->measure, centroid_bounds, split_dim)) { + if (should_split(emitters, start, middle, end, node->measure, split_dim)) { if (split_dim != -1) { /* Partition the emitters between start and end based on the centroids. */ - std::nth_element(emitters->begin() + start, - emitters->begin() + middle, - emitters->begin() + end, + std::nth_element(emitters + start, + emitters + middle, + emitters + end, [split_dim](const LightTreeEmitter &l, const LightTreeEmitter &r) { return l.centroid[split_dim] < r.centroid[split_dim]; }); @@ -289,16 +362,27 @@ void LightTree::recursive_build(const Child child, } } -bool LightTree::should_split(const vector &emitters, +bool LightTree::should_split(LightTreeEmitter *emitters, const int start, int &middle, const int end, LightTreeMeasure &measure, - const BoundBox ¢roid_bbox, int &split_dim) { - middle = (start + end) / 2; const int num_emitters = end - start; + if (num_emitters == 1) { + /* Do not try to split if there is only one emitter. */ + measure = (emitters + start)->measure; + return false; + } + + middle = (start + end) / 2; + + BoundBox centroid_bbox = BoundBox::empty; + for (int i = start; i < end; i++) { + centroid_bbox.grow((emitters + i)->centroid); + } + const float3 extent = centroid_bbox.size(); const float max_extent = max4(extent.x, extent.y, extent.z, 0.0f); @@ -316,15 +400,15 @@ bool LightTree::should_split(const vector &emitters, /* Fill in buckets with emitters. */ std::array buckets; for (int i = start; i < end; i++) { - const LightTreeEmitter &emitter = emitters[i]; + const LightTreeEmitter *emitter = emitters + i; /* Place emitter into the appropriate bucket, where the centroid box is split into equal * partitions. */ int bucket_idx = LightTreeBucket::num_buckets * - (emitter.centroid[dim] - centroid_bbox.min[dim]) * inv_extent; + (emitter->centroid[dim] - centroid_bbox.min[dim]) * inv_extent; bucket_idx = clamp(bucket_idx, 0, LightTreeBucket::num_buckets - 1); - buckets[bucket_idx].add(emitter); + buckets[bucket_idx].add(*emitter); } /* Precompute the left bucket measure cumulatively. */ @@ -338,11 +422,6 @@ bool LightTree::should_split(const vector &emitters, /* Calculate node measure by summing up the bucket measure. */ measure = left_buckets.back().measure + buckets.back().measure; - /* Do not try to split if there are only one emitter. */ - if (num_emitters < 2) { - return false; - } - /* Degenerate case with co-located emitters. */ if (is_zero(centroid_bbox.size())) { break; diff --git a/intern/cycles/scene/light_tree.h b/intern/cycles/scene/light_tree.h index c584f315e4d..0e208cff1de 100644 --- a/intern/cycles/scene/light_tree.h +++ b/intern/cycles/scene/light_tree.h @@ -187,37 +187,46 @@ struct LightTreeNode { * and considers additional orientation and energy information */ class LightTree { unique_ptr root_; - std::atomic num_nodes_ = 0; + + vector emitters_; + vector distant_lights_; + + Progress &progress_; + uint max_lights_in_leaf_; public: + std::atomic num_nodes = 0; + size_t num_triangles = 0; + /* Left or right child of an inner node. */ enum Child { left = 0, right = 1, }; - LightTree(vector &emitters, - const int &num_distant_lights, - uint max_lights_in_leaf); + LightTree(Scene *scene, DeviceScene *dscene, Progress &progress, uint max_lights_in_leaf); - int size() const - { - return num_nodes_; - }; - - LightTreeNode *get_root() const - { - return root_.get(); - }; + /* Returns a pointer to the root node. */ + LightTreeNode *build(Scene *scene, DeviceScene *dscene); /* NOTE: Always use this function to create a new node so the number of nodes is in sync. */ unique_ptr create_node(const LightTreeMeasure &measure, const uint &bit_trial) { - num_nodes_++; + num_nodes++; return make_unique(measure, bit_trial); } + size_t num_emitters() + { + return emitters_.size(); + } + + const LightTreeEmitter &get_emitter(int index) const + { + return emitters_.at(index); + } + private: /* Thread. */ TaskPool task_pool; @@ -228,16 +237,15 @@ class LightTree { LightTreeNode *parent, int start, int end, - vector *emitters, + LightTreeEmitter *emitters, uint bit_trail, int depth); - bool should_split(const vector &emitters, + bool should_split(LightTreeEmitter *emitters, const int start, int &middle, const int end, LightTreeMeasure &measure, - const BoundBox ¢roid_bbox, int &split_dim); };