From 2fda20e1db7b4fa7def634005a7186e9fe7f3357 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 24 Jan 2025 12:05:25 +0100 Subject: [PATCH] Fix #132099: crash when using same geometry on objects with different material counts The core issue was that the geometry batch cache (e.g. `MeshBatchCache` or `PointCloudBatchCache`) was dependent on the object. This is problematic when the the same geometry is used with multiple different objects because the cache can't be consistent with all of them. Fortunately, the only thing that was retrieved from the object was the number of material slots, so if that can be avoided we should be fine. We can't just use the number of material slots stored on the geometry because that may have no material slots but still has material indices which are overridden on the object level. The solution is to take make the number of materials for a geometry only dependent on the actual `material_index` attribute and not on the number of available slots. More specifically, we find the maximal referenced material index and handle that many materials. This number does not depend on how many material slots there are on the object, but it still allows the object to override materials slots that the mesh references. A downside is that the maximum material index has to be computed which often requires an iteration over the mesh. Fortunately, we can cache that quite easily and the computation can be done in parallel. Also we are probably able to eagerly update the material index in many cases when it's set instead of computing it lazily. That is not implemented in this patch though. The largest part of the patch is making the maximal material index easily available on all the geometry types. Besides that, the material API is slightly replaced and the drawing code now makes use of the updated API. Pull Request: https://projects.blender.org/blender/blender/pulls/133498 --- source/blender/blenkernel/BKE_curves.hh | 8 ++++ source/blender/blenkernel/BKE_material.hh | 22 ++++++++-- source/blender/blenkernel/BKE_mesh_types.hh | 2 + source/blender/blenkernel/intern/curve.cc | 12 ++++++ .../blenkernel/intern/curves_attributes.cc | 8 +++- .../blenkernel/intern/curves_geometry.cc | 17 ++++++++ .../blenkernel/intern/grease_pencil.cc | 22 ++++++++++ source/blender/blenkernel/intern/material.cc | 42 +++++++++++++++++-- source/blender/blenkernel/intern/mesh.cc | 17 ++++++++ .../blenkernel/intern/mesh_attributes.cc | 9 +++- .../blender/blenkernel/intern/mesh_runtime.cc | 6 +++ .../blender/blenkernel/intern/pointcloud.cc | 11 +++++ source/blender/blenlib/BLI_bounds.hh | 26 ++++++++++++ .../draw/engines/eevee_next/eevee_material.cc | 2 +- .../engines/gpencil/gpencil_cache_utils.cc | 2 +- .../draw/engines/gpencil/gpencil_draw_data.cc | 2 +- .../draw/engines/overlay/overlay_armature.cc | 4 +- .../engines/overlay/overlay_next_instance.cc | 2 +- .../engines/overlay/overlay_next_prepass.hh | 2 +- .../engines/workbench/workbench_engine.cc | 2 +- source/blender/draw/intern/draw_cache.cc | 9 +--- source/blender/draw/intern/draw_cache.hh | 1 - .../draw_cache_extract_mesh_render_data.cc | 2 +- source/blender/draw/intern/draw_cache_impl.hh | 4 +- .../draw/intern/draw_cache_impl_mesh.cc | 14 +++---- .../draw/intern/draw_cache_impl_pointcloud.cc | 14 +++---- source/blender/makesdna/DNA_curve_types.h | 9 ++++ .../makesdna/DNA_grease_pencil_types.h | 3 ++ source/blender/makesdna/DNA_mesh_types.h | 5 +++ .../blender/makesdna/DNA_pointcloud_types.h | 3 ++ 30 files changed, 240 insertions(+), 42 deletions(-) diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 91e5fe007da..695484dd080 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -118,6 +118,9 @@ class CurvesGeometryRuntime { /** Normal direction vectors for each evaluated point. */ mutable SharedCache> evaluated_normal_cache; + /** The maximum of the "material_index" attribute. */ + mutable SharedCache> max_material_index_cache; + /** Stores weak references to material data blocks. */ std::unique_ptr bake_materials; @@ -305,6 +308,9 @@ class CurvesGeometry : public ::CurvesGeometry { void count_memory(MemoryCounter &memory) const; + /** Get the largest material index used by the curves or nullopt if there are none. */ + std::optional material_index_max() const; + private: /* -------------------------------------------------------------------- * Evaluation. @@ -399,6 +405,8 @@ class CurvesGeometry : public ::CurvesGeometry { * this in #finish() calls. */ void tag_radii_changed(); + /** Call after changing the "material_index" attribute. */ + void tag_material_index_changed(); void translate(const float3 &translation); void transform(const float4x4 &matrix); diff --git a/source/blender/blenkernel/BKE_material.hh b/source/blender/blenkernel/BKE_material.hh index 34be7392e74..91a46754338 100644 --- a/source/blender/blenkernel/BKE_material.hh +++ b/source/blender/blenkernel/BKE_material.hh @@ -9,6 +9,8 @@ * \brief General operations, lookup, etc. for materials. */ +#include + struct ID; struct Main; struct Material; @@ -155,11 +157,25 @@ Material *BKE_object_material_get_eval(Object *ob, short act); * This is the maximum of the number of material slots on the object and geometry. */ int BKE_object_material_count_eval(const Object *ob); + /** - * Same as #BKE_object_material_count_eval, but returns at least one. This is commonly used in - * rendering code which has to use a fallback material if there is none. + * Returns the maximum material index used by the geometry. This returns zero if the geometry is + * empty or if all material indices are negative. */ -int BKE_object_material_count_with_fallback_eval(const Object *ob); +std::optional BKE_id_material_index_max_eval(const ID &id); + +/** + * Gets the number of material slots used by the geometry. The corresponding material for each slot + * can be retrieved with #BKE_object_material_get_eval. + * + * These two functions give the same result when the mesh is provided itself, or an object that + * uses the mesh. + * + * NOTE: This may be higher or lower than the number of material slots on the object or + * object-data. However, it is always at least 1 (the fallback). + */ +int BKE_id_material_used_with_fallback_eval(const ID &id); +int BKE_object_material_used_with_fallback_eval(const Object &ob); void BKE_id_material_eval_assign(ID *id, int slot, Material *material); /** diff --git a/source/blender/blenkernel/BKE_mesh_types.hh b/source/blender/blenkernel/BKE_mesh_types.hh index afe0ee1b5b4..69bb406aa3c 100644 --- a/source/blender/blenkernel/BKE_mesh_types.hh +++ b/source/blender/blenkernel/BKE_mesh_types.hh @@ -169,6 +169,8 @@ struct MeshRuntime { SharedCache> bvh_cache_loose_edges; SharedCache> bvh_cache_loose_edges_no_hidden; + SharedCache> max_material_index; + /** Needed in case we need to lazily initialize the mesh. */ CustomData_MeshMasks cd_mask_extra = {}; diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc index a2c166527c8..9db924995b9 100644 --- a/source/blender/blenkernel/intern/curve.cc +++ b/source/blender/blenkernel/intern/curve.cc @@ -5485,6 +5485,18 @@ void BKE_curve_correct_bezpart(const float v1[2], float v2[2], float v3[2], cons } } +std::optional Curve::material_index_max() const +{ + if (BLI_listbase_is_empty(&this->nurb)) { + return std::nullopt; + } + int max_index = 0; + LISTBASE_FOREACH (const Nurb *, nurb, &this->nurb) { + max_index = std::max(max_index, nurb->mat_nr); + } + return max_index; +} + /* **** Depsgraph evaluation **** */ void BKE_curve_eval_geometry(Depsgraph *depsgraph, Curve *curve) diff --git a/source/blender/blenkernel/intern/curves_attributes.cc b/source/blender/blenkernel/intern/curves_attributes.cc index 2b6c361805b..bd05b97661a 100644 --- a/source/blender/blenkernel/intern/curves_attributes.cc +++ b/source/blender/blenkernel/intern/curves_attributes.cc @@ -46,6 +46,12 @@ static void tag_component_normals_changed(void *owner) curves.tag_normals_changed(); } +static void tag_component_material_index_changed(void *owner) +{ + CurvesGeometry &curves = *static_cast(owner); + curves.tag_material_index_changed(); +} + /** * This provider makes vertex groups available as float attributes. */ @@ -348,7 +354,7 @@ static GeometryAttributeProviders create_attribute_providers_for_curve() CD_PROP_INT32, BuiltinAttributeProvider::Deletable, curve_access, - nullptr, + tag_component_material_index_changed, AttributeValidator{&material_index_clamp}); static CurvesVertexGroupsAttributeProvider vertex_groups; diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 93f0cf1e751..9a514df2b27 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -117,6 +117,7 @@ CurvesGeometry::CurvesGeometry(const CurvesGeometry &other) other.runtime->evaluated_length_cache, other.runtime->evaluated_tangent_cache, other.runtime->evaluated_normal_cache, + other.runtime->max_material_index_cache, {}, true}); @@ -1082,6 +1083,7 @@ void CurvesGeometry::tag_topology_changed() this->tag_positions_changed(); this->runtime->evaluated_offsets_cache.tag_dirty(); this->runtime->nurbs_basis_cache.tag_dirty(); + this->runtime->max_material_index_cache.tag_dirty(); this->runtime->check_type_counts = true; } void CurvesGeometry::tag_normals_changed() @@ -1089,6 +1091,10 @@ void CurvesGeometry::tag_normals_changed() this->runtime->evaluated_normal_cache.tag_dirty(); } void CurvesGeometry::tag_radii_changed() {} +void CurvesGeometry::tag_material_index_changed() +{ + this->runtime->max_material_index_cache.tag_dirty(); +} static void translate_positions(MutableSpan positions, const float3 &translation) { @@ -1204,6 +1210,17 @@ std::optional> CurvesGeometry::bounds_min_max() const return this->runtime->bounds_cache.data(); } +std::optional CurvesGeometry::material_index_max() const +{ + this->runtime->max_material_index_cache.ensure([&](std::optional &r_max_material_index) { + r_max_material_index = blender::bounds::max( + this->attributes() + .lookup_or_default("material_index", blender::bke::AttrDomain::Curve, 0) + .varray); + }); + return this->runtime->max_material_index_cache.data(); +} + void CurvesGeometry::count_memory(MemoryCounter &memory) const { memory.add_shared(this->runtime->curve_offsets_sharing_info, this->offsets().size_in_bytes()); diff --git a/source/blender/blenkernel/intern/grease_pencil.cc b/source/blender/blenkernel/intern/grease_pencil.cc index bd1d369ee5e..f09c5406154 100644 --- a/source/blender/blenkernel/intern/grease_pencil.cc +++ b/source/blender/blenkernel/intern/grease_pencil.cc @@ -3222,6 +3222,28 @@ void GreasePencil::count_memory(blender::MemoryCounter &memory) const } } +std::optional GreasePencil::material_index_max_eval() const +{ + using namespace blender; + using namespace blender::bke; + std::optional max_index; + for (const greasepencil::Layer *layer : this->layers()) { + if (const greasepencil::Drawing *drawing = this->get_eval_drawing(*layer)) { + const bke::CurvesGeometry &curves = drawing->strokes(); + const std::optional max_index_on_layer = curves.material_index_max(); + if (max_index) { + if (max_index_on_layer) { + max_index = std::max(*max_index, *max_index_on_layer); + } + } + else { + max_index = max_index_on_layer; + } + } + } + return max_index; +} + blender::Span GreasePencil::layers() const { BLI_assert(this->runtime != nullptr); diff --git a/source/blender/blenkernel/intern/material.cc b/source/blender/blenkernel/intern/material.cc index 295f8bcf7ea..8df48c6d65d 100644 --- a/source/blender/blenkernel/intern/material.cc +++ b/source/blender/blenkernel/intern/material.cc @@ -49,6 +49,7 @@ #include "BKE_attribute.hh" #include "BKE_brush.hh" #include "BKE_curve.hh" +#include "BKE_curves.hh" #include "BKE_displist.h" #include "BKE_editmesh.hh" #include "BKE_gpencil_legacy.h" @@ -732,7 +733,7 @@ Material *BKE_object_material_get(Object *ob, short act) return ma_p ? *ma_p : nullptr; } -static const ID *get_evaluated_object_data_with_materials(Object *ob) +static const ID *get_evaluated_object_data_with_materials(const Object *ob) { const ID *data = static_cast(ob->data); /* Meshes in edit mode need special handling. */ @@ -799,10 +800,43 @@ int BKE_object_material_count_eval(const Object *ob) return std::max(ob->totcol, len_p ? *len_p : 0); } -int BKE_object_material_count_with_fallback_eval(const Object *ob) +std::optional BKE_id_material_index_max_eval(const ID &id) { - const int actual_count = BKE_object_material_count_eval(ob); - return std::max(1, actual_count); + switch (GS(id.name)) { + case ID_ME: + return reinterpret_cast(id).material_index_max(); + case ID_CU_LEGACY: + return reinterpret_cast(id).material_index_max(); + case ID_CV: + return reinterpret_cast(id).geometry.wrap().material_index_max(); + case ID_PT: + return reinterpret_cast(id).material_index_max(); + case ID_GP: + return reinterpret_cast(id).material_index_max_eval(); + case ID_VO: + case ID_MB: + /* Always use the first material. */ + return 0; + case ID_GD_LEGACY: + /* Is not rendered anymore. */ + BLI_assert_unreachable(); + return 0; + default: + break; + } + return 0; +} + +int BKE_id_material_used_with_fallback_eval(const ID &id) +{ + const int max_material_index = std::max(0, BKE_id_material_index_max_eval(id).value_or(0)); + return max_material_index + 1; +} + +int BKE_object_material_used_with_fallback_eval(const Object &ob) +{ + const ID *data = get_evaluated_object_data_with_materials(&ob); + return BKE_id_material_used_with_fallback_eval(*data); } void BKE_id_material_eval_assign(ID *id, int slot, Material *material) diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index ae17286948c..b1dc5ad98b7 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -166,6 +166,7 @@ static void mesh_copy_data(Main *bmain, mesh_dst->runtime->bvh_cache_loose_edges = mesh_src->runtime->bvh_cache_loose_edges; mesh_dst->runtime->bvh_cache_loose_edges_no_hidden = mesh_src->runtime->bvh_cache_loose_edges_no_hidden; + mesh_dst->runtime->max_material_index = mesh_src->runtime->max_material_index; if (mesh_src->runtime->bake_materials) { mesh_dst->runtime->bake_materials = std::make_unique( *mesh_src->runtime->bake_materials); @@ -1352,6 +1353,22 @@ void BKE_mesh_transform(Mesh *mesh, const float mat[4][4], bool do_keys) mesh->tag_positions_changed(); } +std::optional Mesh::material_index_max() const +{ + this->runtime->max_material_index.ensure([&](std::optional &value) { + if (this->faces_num == 0) { + value = std::nullopt; + } + else { + value = blender::bounds::max( + this->attributes() + .lookup_or_default("material_index", blender::bke::AttrDomain::Face, 0) + .varray); + } + }); + return this->runtime->max_material_index.data(); +} + static void translate_positions(MutableSpan positions, const float3 &translation) { using namespace blender; diff --git a/source/blender/blenkernel/intern/mesh_attributes.cc b/source/blender/blenkernel/intern/mesh_attributes.cc index 3ba7d37c6d7..3f0422ba2ef 100644 --- a/source/blender/blenkernel/intern/mesh_attributes.cc +++ b/source/blender/blenkernel/intern/mesh_attributes.cc @@ -724,6 +724,13 @@ static void tag_component_sharpness_changed(void *owner) } } +static void tag_material_index_changed(void *owner) +{ + if (Mesh *mesh = static_cast(owner)) { + mesh->tag_material_index_changed(); + } +} + /** * This provider makes vertex groups available as float attributes. */ @@ -904,7 +911,7 @@ static GeometryAttributeProviders create_attribute_providers_for_mesh() CD_PROP_INT32, BuiltinAttributeProvider::Deletable, face_access, - nullptr, + tag_material_index_changed, AttributeValidator{&material_index_clamp}); static const auto int2_index_clamp = mf::build::SI1_SO( diff --git a/source/blender/blenkernel/intern/mesh_runtime.cc b/source/blender/blenkernel/intern/mesh_runtime.cc index 465e64db9b6..7513fc193c0 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.cc +++ b/source/blender/blenkernel/intern/mesh_runtime.cc @@ -332,6 +332,7 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh) mesh->runtime->corner_tris_cache.data.tag_dirty(); mesh->runtime->corner_tri_faces_cache.tag_dirty(); mesh->runtime->shrinkwrap_boundary_cache.tag_dirty(); + mesh->runtime->max_material_index.tag_dirty(); mesh->runtime->subsurf_face_dot_tags.clear_and_shrink(); mesh->runtime->subsurf_optimal_display_edges.clear_and_shrink(); mesh->flag &= ~ME_NO_OVERLAPPING_TOPOLOGY; @@ -421,6 +422,11 @@ void Mesh::tag_visibility_changed() this->runtime->bvh_cache_loose_edges_no_hidden.tag_dirty(); } +void Mesh::tag_material_index_changed() +{ + this->runtime->max_material_index.tag_dirty(); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index 86ae17219e5..526dfb53357 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -279,6 +279,17 @@ std::optional> PointCloud::bounds_min_max() con return this->runtime->bounds_cache.data(); } +std::optional PointCloud::material_index_max() const +{ + if (this->totpoint == 0) { + return std::nullopt; + } + return blender::bounds::max( + this->attributes() + .lookup_or_default("material_index", blender::bke::AttrDomain::Point, 0) + .varray); +} + void PointCloud::count_memory(blender::MemoryCounter &memory) const { CustomData_count_memory(this->pdata, this->totpoint, memory); diff --git a/source/blender/blenlib/BLI_bounds.hh b/source/blender/blenlib/BLI_bounds.hh index c62c7c9f757..96f5c931cee 100644 --- a/source/blender/blenlib/BLI_bounds.hh +++ b/source/blender/blenlib/BLI_bounds.hh @@ -16,6 +16,7 @@ #include "BLI_index_mask.hh" #include "BLI_math_vector.hh" #include "BLI_task.hh" +#include "BLI_virtual_array.hh" namespace blender { @@ -150,6 +151,31 @@ template return intersect(*a, *b); } +/** + * Finds the maximum value for elements in the array. + */ +template inline std::optional max(const VArray &values) +{ + if (values.is_empty()) { + return std::nullopt; + } + if (const std::optional value = values.get_if_single()) { + return value; + } + const VArraySpan values_span = values; + return threading::parallel_reduce( + values_span.index_range(), + 2048, + std::numeric_limits::min(), + [&](const IndexRange range, int current_max) { + for (const int value : values_span.slice(range)) { + current_max = std::max(current_max, value); + } + return current_max; + }, + [](const int a, const int b) { return std::max(a, b); }); +} + } // namespace bounds namespace detail { diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc index 074503fa94d..063d7898bad 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.cc +++ b/source/blender/draw/engines/eevee_next/eevee_material.cc @@ -450,7 +450,7 @@ MaterialArray &MaterialModule::material_array_get(Object *ob, bool has_motion) material_array_.materials.clear(); material_array_.gpu_materials.clear(); - const int materials_len = DRW_cache_object_material_count_get(ob); + const int materials_len = BKE_object_material_used_with_fallback_eval(*ob); for (auto i : IndexRange(materials_len)) { ::Material *blender_mat = material_from_slot(ob, i); diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.cc b/source/blender/draw/engines/gpencil/gpencil_cache_utils.cc index 73cafeed7a8..b8e9f5fb17e 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.cc +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.cc @@ -52,7 +52,7 @@ GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd, /* Check if any material with holdout flag enabled. */ tgp_ob->do_mat_holdout = false; - const int tot_materials = BKE_object_material_count_eval(ob); + const int tot_materials = BKE_object_material_used_with_fallback_eval(*ob); for (int i = 0; i < tot_materials; i++) { MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, i + 1); if (((gp_style != nullptr) && (gp_style->flag & GP_MATERIAL_IS_STROKE_HOLDOUT)) || diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.cc b/source/blender/draw/engines/gpencil/gpencil_draw_data.cc index 19ba43f27c9..30d619f017c 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_data.cc +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.cc @@ -165,7 +165,7 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, { GPENCIL_MaterialPool *matpool = pd->last_material_pool; - int mat_len = max_ii(1, BKE_object_material_count_eval(ob)); + int mat_len = BKE_object_material_used_with_fallback_eval(*ob); bool reuse_matpool = matpool && ((matpool->used_count + mat_len) <= GPENCIL_MATERIAL_BUFFER_LEN); diff --git a/source/blender/draw/engines/overlay/overlay_armature.cc b/source/blender/draw/engines/overlay/overlay_armature.cc index eaa7d081091..47e8599960c 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.cc +++ b/source/blender/draw/engines/overlay/overlay_armature.cc @@ -402,7 +402,7 @@ static void drw_shgroup_bone_custom_solid_mesh(const Armatures::DrawContext *ctx using namespace blender::draw; /* TODO(fclem): arg... less than ideal but we never iter on this object * to assure batch cache is valid. */ - DRW_mesh_batch_cache_validate(custom, mesh); + DRW_mesh_batch_cache_validate(mesh); blender::gpu::Batch *surf = DRW_mesh_batch_cache_get_surface(mesh); blender::gpu::Batch *edges = DRW_mesh_batch_cache_get_edge_detection(mesh, nullptr); @@ -447,7 +447,7 @@ static void drw_shgroup_bone_custom_mesh_wire(const Armatures::DrawContext *ctx, using namespace blender::draw; /* TODO(fclem): arg... less than ideal but we never iter on this object * to assure batch cache is valid. */ - DRW_mesh_batch_cache_validate(custom, mesh); + DRW_mesh_batch_cache_validate(mesh); blender::gpu::Batch *geom = DRW_mesh_batch_cache_get_all_edges(mesh); if (geom) { diff --git a/source/blender/draw/engines/overlay/overlay_next_instance.cc b/source/blender/draw/engines/overlay/overlay_next_instance.cc index 9df8dc56c01..4a2261fc095 100644 --- a/source/blender/draw/engines/overlay/overlay_next_instance.cc +++ b/source/blender/draw/engines/overlay/overlay_next_instance.cc @@ -758,7 +758,7 @@ bool Instance::object_is_rendered_transparent(const Object *object, const State if (shading.color_type == V3D_SHADING_MATERIAL_COLOR) { if (object->type == OB_MESH) { - const int materials_num = BKE_object_material_count_eval(object); + const int materials_num = BKE_object_material_used_with_fallback_eval(*object); for (int i = 0; i < materials_num; i++) { Material *mat = BKE_object_material_get_eval(const_cast(object), i + 1); if (mat && mat->a < 1.0f) { diff --git a/source/blender/draw/engines/overlay/overlay_next_prepass.hh b/source/blender/draw/engines/overlay/overlay_next_prepass.hh index 24d43689397..05ba36a0803 100644 --- a/source/blender/draw/engines/overlay/overlay_next_prepass.hh +++ b/source/blender/draw/engines/overlay/overlay_next_prepass.hh @@ -216,7 +216,7 @@ class Prepass : Overlay { case OB_MESH: if (use_material_slot_selection_) { /* TODO(fclem): Improve the API. */ - const int materials_len = DRW_cache_object_material_count_get(ob_ref.object); + const int materials_len = BKE_object_material_used_with_fallback_eval(*ob_ref.object); Array materials(materials_len, nullptr); geom_list = DRW_cache_mesh_surface_shaded_get(ob_ref.object, materials); } diff --git a/source/blender/draw/engines/workbench/workbench_engine.cc b/source/blender/draw/engines/workbench/workbench_engine.cc index 56350f0909c..cb420c6a418 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.cc +++ b/source/blender/draw/engines/workbench/workbench_engine.cc @@ -259,7 +259,7 @@ class Instance { bool has_transparent_material = false; if (object_state.use_per_material_batches) { - const int material_count = DRW_cache_object_material_count_get(ob_ref.object); + const int material_count = BKE_object_material_used_with_fallback_eval(*ob_ref.object); Span batches; if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) { diff --git a/source/blender/draw/intern/draw_cache.cc b/source/blender/draw/intern/draw_cache.cc index cea7a7d2922..00c5c529564 100644 --- a/source/blender/draw/intern/draw_cache.cc +++ b/source/blender/draw/intern/draw_cache.cc @@ -935,11 +935,6 @@ blender::gpu::VertBuf *DRW_cache_object_pos_vertbuf_get(Object *ob) } } -int DRW_cache_object_material_count_get(const Object *ob) -{ - return BKE_object_material_count_with_fallback_eval(ob); -} - Span DRW_cache_object_surface_material_get( Object *ob, const Span materials) { @@ -3293,7 +3288,7 @@ void drw_batch_cache_validate(Object *ob) using namespace blender::draw; switch (ob->type) { case OB_MESH: - DRW_mesh_batch_cache_validate(*ob, *(Mesh *)ob->data); + DRW_mesh_batch_cache_validate(*(Mesh *)ob->data); break; case OB_CURVES_LEGACY: case OB_FONT: @@ -3307,7 +3302,7 @@ void drw_batch_cache_validate(Object *ob) DRW_curves_batch_cache_validate((Curves *)ob->data); break; case OB_POINTCLOUD: - DRW_pointcloud_batch_cache_validate(*ob, (PointCloud *)ob->data); + DRW_pointcloud_batch_cache_validate((PointCloud *)ob->data); break; case OB_VOLUME: DRW_volume_batch_cache_validate((Volume *)ob->data); diff --git a/source/blender/draw/intern/draw_cache.hh b/source/blender/draw/intern/draw_cache.hh index cbea353bb9a..5d93cf7e445 100644 --- a/source/blender/draw/intern/draw_cache.hh +++ b/source/blender/draw/intern/draw_cache.hh @@ -72,7 +72,6 @@ blender::gpu::Batch *DRW_cache_object_loose_edges_get(Object *ob); blender::Span DRW_cache_object_surface_material_get( Object *ob, blender::Span materials); blender::gpu::Batch *DRW_cache_object_face_wireframe_get(const Scene *scene, Object *ob); -int DRW_cache_object_material_count_get(const Object *ob); /** * Returns the vertbuf used by shaded surface batch. diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc index 78902d1c211..e8ce4cc63c7 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc @@ -533,7 +533,7 @@ std::unique_ptr mesh_render_data_create(Object &object, { std::unique_ptr mr = std::make_unique(); mr->toolsettings = ts; - mr->materials_num = BKE_object_material_count_with_fallback_eval(&object); + mr->materials_num = BKE_object_material_used_with_fallback_eval(object); mr->object_to_world = object_to_world; diff --git a/source/blender/draw/intern/draw_cache_impl.hh b/source/blender/draw/intern/draw_cache_impl.hh index b13323c6da8..51078105138 100644 --- a/source/blender/draw/intern/draw_cache_impl.hh +++ b/source/blender/draw/intern/draw_cache_impl.hh @@ -46,7 +46,7 @@ void DRW_curve_batch_cache_validate(Curve *cu); void DRW_curve_batch_cache_free(Curve *cu); void DRW_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode); -void DRW_mesh_batch_cache_validate(Object &object, Mesh &mesh); +void DRW_mesh_batch_cache_validate(Mesh &mesh); void DRW_mesh_batch_cache_free(void *batch_cache); void DRW_lattice_batch_cache_dirty_tag(Lattice *lt, int mode); @@ -61,7 +61,7 @@ void DRW_curves_batch_cache_validate(Curves *curves); void DRW_curves_batch_cache_free(Curves *curves); void DRW_pointcloud_batch_cache_dirty_tag(PointCloud *pointcloud, int mode); -void DRW_pointcloud_batch_cache_validate(Object &object, PointCloud *pointcloud); +void DRW_pointcloud_batch_cache_validate(PointCloud *pointcloud); void DRW_pointcloud_batch_cache_free(PointCloud *pointcloud); void DRW_volume_batch_cache_dirty_tag(Volume *volume, int mode); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc index ddb499b72db..f080e1b7629 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.cc +++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc @@ -530,7 +530,7 @@ BLI_INLINE void mesh_batch_cache_add_request(MeshBatchCache &cache, DRWBatchFlag /* gpu::Batch cache management. */ -static bool mesh_batch_cache_valid(Object &object, Mesh &mesh) +static bool mesh_batch_cache_valid(Mesh &mesh) { MeshBatchCache *cache = static_cast(mesh.runtime->batch_cache); @@ -548,14 +548,14 @@ static bool mesh_batch_cache_valid(Object &object, Mesh &mesh) return false; } - if (cache->mat_len != BKE_object_material_count_with_fallback_eval(&object)) { + if (cache->mat_len != BKE_id_material_used_with_fallback_eval(mesh.id)) { return false; } return true; } -static void mesh_batch_cache_init(Object &object, Mesh &mesh) +static void mesh_batch_cache_init(Mesh &mesh) { if (!mesh.runtime->batch_cache) { mesh.runtime->batch_cache = MEM_new(__func__); @@ -574,7 +574,7 @@ static void mesh_batch_cache_init(Object &object, Mesh &mesh) // cache->vert_len = mesh_render_verts_len_get(mesh); } - cache->mat_len = BKE_object_material_count_with_fallback_eval(&object); + cache->mat_len = BKE_id_material_used_with_fallback_eval(mesh.id); cache->surface_per_mat = Array(cache->mat_len, nullptr); cache->tris_per_mat = Array(cache->mat_len, nullptr); @@ -585,13 +585,13 @@ static void mesh_batch_cache_init(Object &object, Mesh &mesh) drw_mesh_weight_state_clear(&cache->weight_state); } -void DRW_mesh_batch_cache_validate(Object &object, Mesh &mesh) +void DRW_mesh_batch_cache_validate(Mesh &mesh) { - if (!mesh_batch_cache_valid(object, mesh)) { + if (!mesh_batch_cache_valid(mesh)) { if (mesh.runtime->batch_cache) { mesh_batch_cache_clear(*static_cast(mesh.runtime->batch_cache)); } - mesh_batch_cache_init(object, mesh); + mesh_batch_cache_init(mesh); } } diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.cc b/source/blender/draw/intern/draw_cache_impl_pointcloud.cc index e7106ce81a6..cbfa1108f43 100644 --- a/source/blender/draw/intern/draw_cache_impl_pointcloud.cc +++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.cc @@ -92,20 +92,20 @@ static PointCloudBatchCache *pointcloud_batch_cache_get(PointCloud &pointcloud) return static_cast(pointcloud.batch_cache); } -static bool pointcloud_batch_cache_valid(Object &object, PointCloud &pointcloud) +static bool pointcloud_batch_cache_valid(PointCloud &pointcloud) { PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); if (cache == nullptr) { return false; } - if (cache->eval_cache.mat_len != BKE_object_material_count_with_fallback_eval(&object)) { + if (cache->eval_cache.mat_len != BKE_id_material_used_with_fallback_eval(pointcloud.id)) { return false; } return cache->is_dirty == false; } -static void pointcloud_batch_cache_init(Object &object, PointCloud &pointcloud) +static void pointcloud_batch_cache_init(PointCloud &pointcloud) { PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); @@ -117,7 +117,7 @@ static void pointcloud_batch_cache_init(Object &object, PointCloud &pointcloud) cache->eval_cache = {}; } - cache->eval_cache.mat_len = BKE_object_material_count_with_fallback_eval(&object); + cache->eval_cache.mat_len = BKE_id_material_used_with_fallback_eval(pointcloud.id); cache->eval_cache.surface_per_mat = static_cast( MEM_callocN(sizeof(gpu::Batch *) * cache->eval_cache.mat_len, __func__)); @@ -171,11 +171,11 @@ static void pointcloud_batch_cache_clear(PointCloud &pointcloud) pointcloud_discard_attributes(*cache); } -void DRW_pointcloud_batch_cache_validate(Object &object, PointCloud *pointcloud) +void DRW_pointcloud_batch_cache_validate(PointCloud *pointcloud) { - if (!pointcloud_batch_cache_valid(object, *pointcloud)) { + if (!pointcloud_batch_cache_valid(*pointcloud)) { pointcloud_batch_cache_clear(*pointcloud); - pointcloud_batch_cache_init(object, *pointcloud); + pointcloud_batch_cache_init(*pointcloud); } } diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 445980e3c5e..d6ddf442a34 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -13,6 +13,10 @@ #include "DNA_listBase.h" #include "DNA_vec_types.h" +#ifdef __cplusplus +# include +#endif + /** Used in `readfile.cc` and `editfont.cc`. */ #define MAXTEXTBOX 256 @@ -315,6 +319,11 @@ typedef struct Curve { char _pad3[7]; void *batch_cache; + +#ifdef __cplusplus + /** Get the largest material index used by the curves or nullopt if there are none. */ + std::optional material_index_max() const; +#endif } Curve; #define CURVE_VFONT_ANY(cu) ((cu)->vfont), ((cu)->vfontb), ((cu)->vfonti), ((cu)->vfontbi) diff --git a/source/blender/makesdna/DNA_grease_pencil_types.h b/source/blender/makesdna/DNA_grease_pencil_types.h index 155054dd282..f22e74d1500 100644 --- a/source/blender/makesdna/DNA_grease_pencil_types.h +++ b/source/blender/makesdna/DNA_grease_pencil_types.h @@ -720,6 +720,9 @@ typedef struct GreasePencil { blender::bke::AttributeAccessor attributes() const; blender::bke::MutableAttributeAccessor attributes_for_write(); + /** Get the largest material index used by the evaluated layers or nullopt if they are empty. */ + std::optional material_index_max_eval() const; + void count_memory(blender::MemoryCounter &memory) const; /* For debugging purposes. */ diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index a8b3e210e73..0f1d42fd894 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -309,6 +309,9 @@ typedef struct Mesh { /** Set cached mesh bounds to a known-correct value to avoid their lazy calculation later on. */ void bounds_set_eager(const blender::Bounds &bounds); + /** Get the largest material index used by the mesh or nullopt if it has no faces. */ + std::optional material_index_max() const; + /** * Cached map containing the index of the face using each face corner. */ @@ -429,6 +432,8 @@ typedef struct Mesh { void tag_topology_changed(); /** Call when changing the ".hide_vert", ".hide_edge", or ".hide_poly" attributes. */ void tag_visibility_changed(); + /** Call when changing the "material_index" attribute. */ + void tag_material_index_changed(); #endif } Mesh; diff --git a/source/blender/makesdna/DNA_pointcloud_types.h b/source/blender/makesdna/DNA_pointcloud_types.h index 442ec83a2b8..389f3a407ba 100644 --- a/source/blender/makesdna/DNA_pointcloud_types.h +++ b/source/blender/makesdna/DNA_pointcloud_types.h @@ -66,6 +66,9 @@ typedef struct PointCloud { std::optional> bounds_min_max() const; + /** Get the largest material index used by the pointcloud or nullopt if it is empty. */ + std::optional material_index_max() const; + void count_memory(blender::MemoryCounter &memory) const; #endif