[Bf-blender-cvs] [94323bb4274] master: IO: speed up import of large Alembic/USD/OBJ scenes by optimizing material assignment

Aras Pranckevicius noreply at git.blender.org
Wed Jul 6 12:30:24 CEST 2022


Commit: 94323bb427480aaaeea52f6833173866fbacf2e1
Author: Aras Pranckevicius
Date:   Wed Jul 6 13:29:59 2022 +0300
Branches: master
https://developer.blender.org/rB94323bb427480aaaeea52f6833173866fbacf2e1

IO: speed up import of large Alembic/USD/OBJ scenes by optimizing material assignment

The importer parts that were doing assignment of materials to the
imported objects/meshes were essentially having a quadratic complexity
in terms of scene object count. For each material assigned to each
object, they were scanning the whole scene, checking which other
Objects use the same Mesh data, in order to resize their material
arrays to match the size.

Performance details (Windows, Ryzen 5950X):

- Import OBJ Blender 3.0 splash scene (24k objects): 43.0s -> 32.9s
- Import USD Disney Moana scene (260k objects): saves two hours
  (~7400s). Note that later on this crashes when trying to render the
  imported result; crashes in the same way/place both in master and
  this patch.

Implementation details:

The importers were doing "scan the world" basically twice for each
object, for each material: once when creating a new material slot
(assigns an empty material), and then again when assigning the
material.

However, all these importers (USD, Alembic, OBJ) always create one
Object for one Mesh. So that whole quadratic complexity resulting
from "scan the world for possible other users of this obdata" is
completely not needed; it just never finds anything. So add a new
dedicated function BKE_object_material_assign_single_obdata that skips
the expensive part, but should only be used when the caller knows that
the obdata has exactly one user (the passed object).

Reviewed By: Bastien Montagne, Michael Kowalski
Differential Revision: https://developer.blender.org/D15145

===================================================================

M	source/blender/blenkernel/BKE_material.h
M	source/blender/blenkernel/intern/material.c
M	source/blender/io/alembic/intern/abc_reader_mesh.cc
M	source/blender/io/usd/intern/usd_reader_mesh.cc
M	source/blender/io/wavefront_obj/importer/obj_import_mesh.cc

===================================================================

diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h
index 05e502f06c2..ab14fa92514 100644
--- a/source/blender/blenkernel/BKE_material.h
+++ b/source/blender/blenkernel/BKE_material.h
@@ -87,6 +87,17 @@ struct Material *BKE_object_material_get(struct Object *ob, short act);
 void BKE_id_material_assign(struct Main *bmain, struct ID *id, struct Material *ma, short act);
 void BKE_object_material_assign(
     struct Main *bmain, struct Object *ob, struct Material *ma, short act, int assign_type);
+
+/**
+ * Similar to #BKE_object_material_assign with #BKE_MAT_ASSIGN_OBDATA type,
+ * but does not scan whole Main for other usages of the same obdata. Only
+ * use in cases where you know that the object's obdata is only used by this one
+ * object.
+ */
+void BKE_object_material_assign_single_obdata(struct Main *bmain,
+                                              struct Object *ob,
+                                              struct Material *ma,
+                                              short act);
 /**
  * \warning this calls many more update calls per object then are needed, could be optimized.
  */
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 04a07fb42be..f899901b54e 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -954,7 +954,8 @@ void BKE_id_material_assign(Main *bmain, ID *id, Material *ma, short act)
   BKE_objects_materials_test_all(bmain, id);
 }
 
-void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act, int assign_type)
+static void object_material_assign(
+    Main *bmain, Object *ob, Material *ma, short act, int assign_type, bool do_test_all)
 {
   Material *mao, **matar, ***matarar;
   short *totcolp;
@@ -1037,7 +1038,10 @@ void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act
       id_us_min(&mao->id);
     }
     (*matarar)[act - 1] = ma;
-    BKE_objects_materials_test_all(bmain, ob->data); /* Data may be used by several objects... */
+    /* Data may be used by several objects. */
+    if (do_test_all) {
+      BKE_objects_materials_test_all(bmain, ob->data);
+    }
   }
 
   if (ma) {
@@ -1045,6 +1049,19 @@ void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act
   }
 }
 
+void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act, int assign_type)
+{
+  object_material_assign(bmain, ob, ma, act, assign_type, true);
+}
+
+void BKE_object_material_assign_single_obdata(struct Main *bmain,
+                                              struct Object *ob,
+                                              struct Material *ma,
+                                              short act)
+{
+  object_material_assign(bmain, ob, ma, act, BKE_MAT_ASSIGN_OBDATA, false);
+}
+
 void BKE_object_material_remap(Object *ob, const unsigned int *remap)
 {
   Material ***matar = BKE_object_material_array_p(ob);
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc
index d8c48357fc0..df3559c108c 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.cc
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc
@@ -77,10 +77,8 @@ static void assign_materials(Main *bmain,
                              const std::map<std::string, int> &mat_index_map)
 {
   std::map<std::string, int>::const_iterator it;
-  for (it = mat_index_map.begin(); it != mat_index_map.end(); ++it) {
-    if (!BKE_object_material_slot_add(bmain, ob)) {
-      return;
-    }
+  if (mat_index_map.size() > MAXMAT) {
+    return;
   }
 
   std::map<std::string, Material *> matname_to_material = build_material_map(bmain);
@@ -100,7 +98,7 @@ static void assign_materials(Main *bmain,
       assigned_mat = mat_iter->second;
     }
 
-    BKE_object_material_assign(bmain, ob, assigned_mat, mat_index, BKE_MAT_ASSIGN_OBDATA);
+    BKE_object_material_assign_single_obdata(bmain, ob, assigned_mat, mat_index);
   }
 }
 
diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc
index 46749b03169..45657525527 100644
--- a/source/blender/io/usd/intern/usd_reader_mesh.cc
+++ b/source/blender/io/usd/intern/usd_reader_mesh.cc
@@ -116,24 +116,15 @@ static void assign_materials(Main *bmain,
     return;
   }
 
-  bool can_assign = true;
-  std::map<pxr::SdfPath, int>::const_iterator it = mat_index_map.begin();
-
-  int matcount = 0;
-  for (; it != mat_index_map.end(); ++it, matcount++) {
-    if (!BKE_object_material_slot_add(bmain, ob)) {
-      can_assign = false;
-      break;
-    }
-  }
-
-  if (!can_assign) {
+  if (mat_index_map.size() > MAXMAT) {
     return;
   }
 
   blender::io::usd::USDMaterialReader mat_reader(params, bmain);
 
-  for (it = mat_index_map.begin(); it != mat_index_map.end(); ++it) {
+  for (std::map<pxr::SdfPath, int>::const_iterator it = mat_index_map.begin();
+       it != mat_index_map.end();
+       ++it) {
 
     Material *assigned_mat = find_existing_material(
         it->first, params, mat_name_to_mat, usd_path_to_mat_name);
@@ -170,7 +161,7 @@ static void assign_materials(Main *bmain,
     }
 
     if (assigned_mat) {
-      BKE_object_material_assign(bmain, ob, assigned_mat, it->second, BKE_MAT_ASSIGN_OBDATA);
+      BKE_object_material_assign_single_obdata(bmain, ob, assigned_mat, it->second);
     }
     else {
       /* This shouldn't happen. */
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
index 1e0b36ae4cb..8cef0700b3b 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
@@ -306,8 +306,7 @@ void MeshFromGeometry::create_materials(Main *bmain,
     if (mat == nullptr) {
       continue;
     }
-    BKE_object_material_slot_add(bmain, obj);
-    BKE_object_material_assign(bmain, obj, mat, obj->totcol, BKE_MAT_ASSIGN_USERPREF);
+    BKE_object_material_assign_single_obdata(bmain, obj, mat, obj->totcol + 1);
   }
 }



More information about the Bf-blender-cvs mailing list