Cycles: multithreaded export of geometry

This improves performance in scene synchronization when there are many
mesh, hair and volume objects. Sync time speedups in benchmarks:

barbershop   5.2x
bmw          1.3x
fishycat     1.5x
koro         1.0x
sponza       3.0x
victor       1.4x
wdas_cloud   0.9x

Implementation by Nicolas Lelong, and Jagannadhan Ravi (AMD).

Differential Revision: https://developer.blender.org/D9258
This commit is contained in:
Jagannadhan Ravi 2020-10-21 16:38:36 +02:00 committed by Brecht Van Lommel
parent b5803c0a24
commit bb49aa0d69
Notes: blender-bot 2023-02-14 04:40:22 +01:00
Referenced by issue #83939, Blender randomly stops responding under Linux.
3 changed files with 98 additions and 37 deletions

View File

@ -25,6 +25,7 @@
#include "blender/blender_util.h"
#include "util/util_foreach.h"
#include "util/util_task.h"
CCL_NAMESPACE_BEGIN
@ -45,7 +46,8 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
BL::Object &b_ob,
BL::Object &b_ob_instance,
bool object_updated,
bool use_particle_hair)
bool use_particle_hair,
TaskPool *task_pool)
{
/* Test if we can instance or if the object is modified. */
BL::ID b_ob_data = b_ob.data();
@ -77,8 +79,15 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
used_shaders.push_back(default_shader);
}
/* Test if we need to sync. */
/* Ensure we only sync instanced geometry once. */
Geometry *geom = geometry_map.find(key);
if (geom) {
if (geometry_synced.find(geom) != geometry_synced.end()) {
return geom;
}
}
/* Test if we need to sync. */
bool sync = true;
if (geom == NULL) {
/* Add new geometry if it did not exist yet. */
@ -125,28 +134,36 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
}
}
/* Ensure we only sync instanced geometry once. */
if (geometry_synced.find(geom) != geometry_synced.end()) {
return geom;
}
progress.set_sync_status("Synchronizing object", b_ob.name());
geometry_synced.insert(geom);
geom->name = ustring(b_ob_data.name().c_str());
if (geom_type == Geometry::HAIR) {
Hair *hair = static_cast<Hair *>(geom);
sync_hair(b_depsgraph, b_ob, hair, used_shaders);
}
else if (geom_type == Geometry::VOLUME) {
Volume *volume = static_cast<Volume *>(geom);
sync_volume(b_ob, volume, used_shaders);
auto sync_func = [=]() mutable {
if (progress.get_cancel())
return;
progress.set_sync_status("Synchronizing object", b_ob.name());
if (geom_type == Geometry::HAIR) {
Hair *hair = static_cast<Hair *>(geom);
sync_hair(b_depsgraph, b_ob, hair, used_shaders);
}
else if (geom_type == Geometry::VOLUME) {
Volume *volume = static_cast<Volume *>(geom);
sync_volume(b_ob, volume, used_shaders);
}
else {
Mesh *mesh = static_cast<Mesh *>(geom);
sync_mesh(b_depsgraph, b_ob, mesh, used_shaders);
}
};
/* Defer the actual geometry sync to the task_pool for multithreading */
if (task_pool) {
task_pool->push(sync_func);
}
else {
Mesh *mesh = static_cast<Mesh *>(geom);
sync_mesh(b_depsgraph, b_ob, mesh, used_shaders);
sync_func();
}
return geom;
@ -156,7 +173,8 @@ void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph,
BL::Object &b_ob,
Object *object,
float motion_time,
bool use_particle_hair)
bool use_particle_hair,
TaskPool *task_pool)
{
/* Ensure we only sync instanced geometry once. */
Geometry *geom = object->geometry;
@ -177,16 +195,29 @@ void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph,
return;
}
if (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) {
Hair *hair = static_cast<Hair *>(geom);
sync_hair_motion(b_depsgraph, b_ob, hair, motion_step);
}
else if (b_ob.type() == BL::Object::type_VOLUME || object_fluid_gas_domain_find(b_ob)) {
/* No volume motion blur support yet. */
auto sync_func = [=]() mutable {
if (progress.get_cancel())
return;
if (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) {
Hair *hair = static_cast<Hair *>(geom);
sync_hair_motion(b_depsgraph, b_ob, hair, motion_step);
}
else if (b_ob.type() == BL::Object::type_VOLUME || object_fluid_gas_domain_find(b_ob)) {
/* No volume motion blur support yet. */
}
else {
Mesh *mesh = static_cast<Mesh *>(geom);
sync_mesh_motion(b_depsgraph, b_ob, mesh, motion_step);
}
};
/* Defer the actual geometry sync to the task_pool for multithreading */
if (task_pool) {
task_pool->push(sync_func);
}
else {
Mesh *mesh = static_cast<Mesh *>(geom);
sync_mesh_motion(b_depsgraph, b_ob, mesh, motion_step);
sync_func();
}
}

View File

@ -32,6 +32,7 @@
#include "util/util_foreach.h"
#include "util/util_hash.h"
#include "util/util_logging.h"
#include "util/util_task.h"
CCL_NAMESPACE_BEGIN
@ -103,7 +104,8 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
bool use_particle_hair,
bool show_lights,
BlenderObjectCulling &culling,
bool *use_portal)
bool *use_portal,
TaskPool *geom_task_pool)
{
const bool is_instance = b_instance.is_instance();
BL::Object b_ob = b_instance.object();
@ -181,6 +183,10 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
return NULL;
}
/* Use task pool only for non-instances, since sync_dupli_particle accesses
* geometry. This restriction should be removed for better performance. */
TaskPool *object_geom_task_pool = (is_instance) ? NULL : geom_task_pool;
/* key to lookup object */
ObjectKey key(b_parent, persistent_id, b_ob_instance, use_particle_hair);
Object *object;
@ -198,7 +204,12 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
/* mesh deformation */
if (object->geometry)
sync_geometry_motion(b_depsgraph, b_ob, object, motion_time, use_particle_hair);
sync_geometry_motion(b_depsgraph,
b_ob_instance,
object,
motion_time,
use_particle_hair,
object_geom_task_pool);
}
return object;
@ -211,8 +222,15 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
object_updated = true;
/* mesh sync */
object->geometry = sync_geometry(
b_depsgraph, b_ob, b_ob_instance, object_updated, use_particle_hair);
/* b_ob is owned by the iterator and will go out of scope at the end of the block.
* b_ob_instance is the original object and will remain valid for deferred geometry
* sync. */
object->geometry = sync_geometry(b_depsgraph,
b_ob_instance,
b_ob_instance,
object_updated,
use_particle_hair,
object_geom_task_pool);
/* special case not tracked by object update flags */
@ -331,6 +349,9 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
BL::SpaceView3D &b_v3d,
float motion_time)
{
/* Task pool for multithreaded geometry sync. */
TaskPool geom_task_pool;
/* layer data */
bool motion = motion_time != 0.0f;
@ -355,8 +376,8 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
const bool show_lights = BlenderViewportParameters(b_v3d).use_scene_lights;
BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
BL::Depsgraph::object_instances_iterator b_instance_iter;
for (b_depsgraph.object_instances.begin(b_instance_iter);
b_instance_iter != b_depsgraph.object_instances.end() && !cancel;
++b_instance_iter) {
@ -381,7 +402,8 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
false,
show_lights,
culling,
&use_portal);
&use_portal,
&geom_task_pool);
}
/* Particle hair as separate object. */
@ -393,12 +415,15 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
true,
show_lights,
culling,
&use_portal);
&use_portal,
&geom_task_pool);
}
cancel = progress.get_cancel();
}
geom_task_pool.wait_work();
progress.set_sync_status("");
if (!cancel && !motion) {

View File

@ -50,6 +50,7 @@ class ViewLayer;
class Shader;
class ShaderGraph;
class ShaderNode;
class TaskPool;
class BlenderSync {
public:
@ -145,7 +146,8 @@ class BlenderSync {
bool use_particle_hair,
bool show_lights,
BlenderObjectCulling &culling,
bool *use_portal);
bool *use_portal,
TaskPool *geom_task_pool);
/* Volume */
void sync_volume(BL::Object &b_ob, Volume *volume, const vector<Shader *> &used_shaders);
@ -177,12 +179,15 @@ class BlenderSync {
BL::Object &b_ob,
BL::Object &b_ob_instance,
bool object_updated,
bool use_particle_hair);
bool use_particle_hair,
TaskPool *task_pool);
void sync_geometry_motion(BL::Depsgraph &b_depsgraph,
BL::Object &b_ob,
Object *object,
float motion_time,
bool use_particle_hair);
bool use_particle_hair,
TaskPool *task_pool);
/* Light */
void sync_light(BL::Object &b_parent,