From 63e50cf265d6f17785ffdf0d0a7220237b054050 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Thu, 27 May 2021 14:36:18 +0200 Subject: Fix T88499: Copy data path operator does not consider library affiliation When using the operator `ui.copy_data_path_button(full_path=True)` ({key ctrl shift Alt C} on hover) the copied path does not consider the library origin. That means that when there is a name clash the data path is not accurate and refers to the local item instead. This patch adds the library (if the ID is linked) of the returned string from RNA_path_full_ID_py. bpy.data.objects["Cube", "//library.blend"] instead of bpy.data.objects["Cube"] note: parsing this happens in pyrna_prop_collection_subscript_str_lib_pair_ptr Maniphest Tasks: T88499 Differential Revision: https://developer.blender.org/D11412 --- source/blender/makesrna/intern/rna_access.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 150a455f1c7..948fef1b51e 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -6115,13 +6115,25 @@ char *RNA_path_full_ID_py(Main *bmain, ID *id) path = ""; } - char id_esc[(sizeof(id->name) - 2) * 2]; + char lib_filepath_esc[(sizeof(id->lib->filepath) * 2) + 4]; + if (id->lib != NULL) { + int ofs = 0; + memcpy(lib_filepath_esc, ", \"", 3); + ofs += 3; + ofs += BLI_str_escape(lib_filepath_esc + ofs, id->lib->filepath, sizeof(lib_filepath_esc)); + memcpy(lib_filepath_esc + ofs, "\"", 2); + } + else { + lib_filepath_esc[0] = '\0'; + } + char id_esc[(sizeof(id->name) - 2) * 2]; BLI_str_escape(id_esc, id->name + 2, sizeof(id_esc)); - return BLI_sprintfN("bpy.data.%s[\"%s\"]%s%s", + return BLI_sprintfN("bpy.data.%s[\"%s\"%s]%s%s", BKE_idtype_idcode_to_name_plural(GS(id->name)), id_esc, + lib_filepath_esc, path[0] ? "." : "", path); } -- cgit v1.2.3 From 97ccd592ce6cda32171054e28d36ea62a95afe64 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 28 May 2021 15:33:04 +0200 Subject: Fix crash in liboverride resync. Reported by studio (@andy), thanks. --- source/blender/blenkernel/intern/lib_override.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 0e2317c72de..83bd2b6abb7 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -252,6 +252,13 @@ bool BKE_lib_override_library_is_user_edited(struct ID *id) return false; } + /* A bit weird, but those embedded IDs are handled by their owner ID anyway, so we can just + * assume they are never user-edited, actual proper detection will happen from their owner check. + */ + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + return false; + } + LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) { LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) { -- cgit v1.2.3 From adafd7257d0ed1bcca9eae63ed1e87a891c44975 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 28 May 2021 13:38:08 +0200 Subject: Fix T88635: VSE: Select Linked gives unpredictable results Caused by {rB66923031e6f2}. Code would process unselected sequences and skip selected, needs to be the other way around. Maniphest Tasks: T88635 Differential Revision: https://developer.blender.org/D11424 --- source/blender/editors/space_sequencer/sequencer_select.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index a9f8a70d61e..a2bb9bb9ac8 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -808,7 +808,7 @@ static bool select_linked_internal(Scene *scene) bool changed = false; LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { - if ((seq->flag & SELECT) != 0) { + if ((seq->flag & SELECT) == 0) { continue; } /* Only get unselected neighbors. */ -- cgit v1.2.3 From 418888f1c9a3c3ca8facc04d271d48ad37e5f5b2 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Fri, 28 May 2021 07:57:21 -0600 Subject: MSVC: Fix build error with 16.10/11 Not entirely sure why this was not an issue for 16.9 but TBB includes the Windows.h header which by default will define min and max macro's These collide with the stl versions in This patch requests Windows.h not to define the problematic macro's, resolving the conflict. --- source/blender/modifiers/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 91327b97fe4..0138dd0c3ad 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -195,7 +195,11 @@ endif() if(WITH_TBB) add_definitions(-DWITH_TBB) - + if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) + endif() list(APPEND INC_SYS ${TBB_INCLUDE_DIRS} ) -- cgit v1.2.3 From 080623b8ac4d21988718a7c9e107d4d5fa29bdce Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 28 May 2021 14:02:24 +0200 Subject: Fix incorrect Denoise node SSE 4.1 warning on macOS Intel --- source/blender/editors/space_node/drawnode.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 5110c14ef4d..510fcd5e887 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -2751,11 +2751,10 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po #else /* Always supported through Accelerate framework BNNS on macOS. */ # ifndef __APPLE__ - if (!BLI_cpu_support_sse41()) -# endif - { + if (!BLI_cpu_support_sse41()) { uiItemL(layout, IFACE_("Disabled, CPU with SSE4.1 is required"), ICON_ERROR); } +# endif #endif uiItemR(layout, ptr, "use_hdr", DEFAULT_FLAGS, NULL, ICON_NONE); -- cgit v1.2.3 From 11e32332ddfdcacb7f992d9fb25025b53c5037c1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 28 May 2021 10:42:22 -0400 Subject: Geometry Nodes: Add Mesh to Curve Node This node creates poly curve splines from mesh edges. A selection attribute input allows only using some of the edges from the mesh. The node builds cyclic splines from branchless groups of edges where possible, but when there is a three-way intersection, the spline stops. The node also transfers all attributes from the mesh to the resulting control points. In the future we could add a way to limit that to a subset of the attributes to improve performance. The algorithm is from Animation Nodes, written by @OmarSquircleArt. I added the ability to use a selection, attribute transferring, and used different variable names, etc, but other than that the algorithm is the same. Differential Revision: https://developer.blender.org/D11265 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/geometry/nodes/node_geo_mesh_to_curve.cc | 318 +++++++++++++++++++++ 7 files changed, 324 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 4bff18cd1be..05c7ef756c7 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -503,6 +503,7 @@ geometry_node_categories = [ GeometryNodeCategory("GEO_CURVE", "Curve", items=[ NodeItem("GeometryNodeCurveToMesh"), NodeItem("GeometryNodeCurveResample"), + NodeItem("GeometryNodeMeshToCurve"), ]), GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[ NodeItem("GeometryNodeBoundBox"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 448f4ae48ad..b3247a751bf 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1426,6 +1426,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MATERIAL_ASSIGN 1049 #define GEO_NODE_INPUT_MATERIAL 1050 #define GEO_NODE_MATERIAL_REPLACE 1051 +#define GEO_NODE_MESH_TO_CURVE 1052 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 3377f5c69dc..d0864e85373 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5066,6 +5066,7 @@ static void registerGeometryNodes() register_node_type_geo_mesh_primitive_ico_sphere(); register_node_type_geo_mesh_primitive_line(); register_node_type_geo_mesh_primitive_uv_sphere(); + register_node_type_geo_mesh_to_curve(); register_node_type_geo_object_info(); register_node_type_geo_point_distribute(); register_node_type_geo_point_instance(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 9d21ff19f46..24085b31fc3 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -178,6 +178,7 @@ set(SRC geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc geometry/nodes/node_geo_mesh_primitive_line.cc geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc + geometry/nodes/node_geo_mesh_to_curve.cc geometry/nodes/node_geo_object_info.cc geometry/nodes/node_geo_point_distribute.cc geometry/nodes/node_geo_point_instance.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index d2a702c30a6..eadfed26be1 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -66,6 +66,7 @@ void register_node_type_geo_mesh_primitive_grid(void); void register_node_type_geo_mesh_primitive_ico_sphere(void); void register_node_type_geo_mesh_primitive_line(void); void register_node_type_geo_mesh_primitive_uv_sphere(void); +void register_node_type_geo_mesh_to_curve(void); void register_node_type_geo_object_info(void); void register_node_type_geo_point_distribute(void); void register_node_type_geo_point_instance(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index ce1813fdac3..ef5f25e7b57 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -305,6 +305,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", Me DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") +DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "") DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc new file mode 100644 index 00000000000..b852f929b5f --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -0,0 +1,318 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +using blender::Array; + +static bNodeSocketTemplate geo_node_mesh_to_curve_in[] = { + {SOCK_GEOMETRY, N_("Mesh")}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_to_curve_out[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +namespace blender::nodes { + +template +static void copy_attribute_to_points(const VArray &source_data, + Span map, + MutableSpan dest_data) +{ + for (const int point_index : map.index_range()) { + const int vert_index = map[point_index]; + dest_data[point_index] = source_data[vert_index]; + } +} + +static void copy_attributes_to_points(CurveEval &curve, + const MeshComponent &mesh_component, + Span> point_to_vert_maps) +{ + MutableSpan splines = curve.splines(); + Set source_attribute_names = mesh_component.attribute_names(); + + /* Copy builtin control point attributes. */ + if (source_attribute_names.contains_as("tilt")) { + const GVArray_Typed tilt_attribute = mesh_component.attribute_get_for_read( + "tilt", ATTR_DOMAIN_POINT, 0.0f); + parallel_for(splines.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + copy_attribute_to_points( + *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); + } + }); + source_attribute_names.remove_contained_as("tilt"); + } + if (source_attribute_names.contains_as("radius")) { + const GVArray_Typed radius_attribute = mesh_component.attribute_get_for_read( + "radius", ATTR_DOMAIN_POINT, 1.0f); + parallel_for(splines.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + copy_attribute_to_points( + *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); + } + }); + source_attribute_names.remove_contained_as("radius"); + } + + /* Don't copy other builtin control point attributes. */ + source_attribute_names.remove_as("position"); + + /* Copy dynamic control point attributes. */ + for (const StringRef name : source_attribute_names) { + const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(name, + ATTR_DOMAIN_POINT); + /* Some attributes might not exist if they were builtin attribute on domains that don't + * have any elements, i.e. a face attribute on the output of the line primitive node. */ + if (!mesh_attribute) { + continue; + } + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type()); + + parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + /* Create attribute on the spline points. */ + splines[i]->attributes.create(name, data_type); + std::optional spline_attribute = splines[i]->attributes.get_for_write(name); + BLI_assert(spline_attribute); + + /* Copy attribute based on the map for this spline. */ + attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) { + using T = decltype(dummy); + copy_attribute_to_points( + mesh_attribute->typed(), point_to_vert_maps[i], spline_attribute->typed()); + }); + } + }); + } + + curve.assert_valid_point_attributes(); +} + +struct CurveFromEdgesOutput { + std::unique_ptr curve; + Vector> point_to_vert_maps; +}; + +static CurveFromEdgesOutput mesh_to_curve(Span verts, Span> edges) +{ + std::unique_ptr curve = std::make_unique(); + Vector> point_to_vert_maps; + + /* Compute the number of edges connecting to each vertex. */ + Array neighbor_count(verts.size(), 0); + for (const std::pair &edge : edges) { + neighbor_count[edge.first]++; + neighbor_count[edge.second]++; + } + + /* Compute an offset into the array of neighbor edges based on the counts. */ + Array neighbor_offsets(verts.size()); + int start = 0; + for (const int i : verts.index_range()) { + neighbor_offsets[i] = start; + start += neighbor_count[i]; + } + + /* Use as an index into the "neighbor group" for each vertex. */ + Array used_slots(verts.size(), 0); + /* Calculate the indices of each vertex's neighboring edges. */ + Array neighbors(edges.size() * 2); + for (const int i : edges.index_range()) { + const int v1 = edges[i].first; + const int v2 = edges[i].second; + neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2; + neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1; + used_slots[v1]++; + used_slots[v2]++; + } + + /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */ + Array unused_edges = std::move(used_slots); + + for (const int start_vert : verts.index_range()) { + /* The vertex will be part of a cyclic spline. */ + if (neighbor_count[start_vert] == 2) { + continue; + } + + /* The vertex has no connected edges, or they were already used. */ + if (unused_edges[start_vert] == 0) { + continue; + } + + for (const int i : IndexRange(neighbor_count[start_vert])) { + int current_vert = start_vert; + int next_vert = neighbors[neighbor_offsets[current_vert] + i]; + + if (unused_edges[next_vert] == 0) { + continue; + } + + std::unique_ptr spline = std::make_unique(); + Vector point_to_vert_map; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + + /* Follow connected edges until we read a vertex with more than two connected edges. */ + while (true) { + int last_vert = current_vert; + current_vert = next_vert; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + unused_edges[current_vert]--; + unused_edges[last_vert]--; + + if (neighbor_count[current_vert] != 2) { + break; + } + + const int offset = neighbor_offsets[current_vert]; + const int next_a = neighbors[offset]; + const int next_b = neighbors[offset + 1]; + next_vert = (last_vert == next_a) ? next_b : next_a; + } + + spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); + point_to_vert_maps.append(std::move(point_to_vert_map)); + } + } + + /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */ + for (const int start_vert : verts.index_range()) { + if (unused_edges[start_vert] != 2) { + continue; + } + + int current_vert = start_vert; + int next_vert = neighbors[neighbor_offsets[current_vert]]; + + std::unique_ptr spline = std::make_unique(); + Vector point_to_vert_map; + spline->set_cyclic(true); + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + + /* Follow connected edges until we loop back to the start vertex. */ + while (next_vert != start_vert) { + const int last_vert = current_vert; + current_vert = next_vert; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + unused_edges[current_vert]--; + unused_edges[last_vert]--; + + const int offset = neighbor_offsets[current_vert]; + const int next_a = neighbors[offset]; + const int next_b = neighbors[offset + 1]; + next_vert = (last_vert == next_a) ? next_b : next_a; + } + + spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); + point_to_vert_maps.append(std::move(point_to_vert_map)); + } + + curve->attributes.reallocate(curve->splines().size()); + return {std::move(curve), std::move(point_to_vert_maps)}; +} + +/** + * Get a separate array of the indices for edges in a selection (a boolean attribute). + * This helps to make the above algorithm simpler by removing the need to check for selection + * in many places. + */ +static Vector> get_selected_edges(GeoNodeExecParams params, + const MeshComponent &component) +{ + const Mesh &mesh = *component.get_for_read(); + const std::string selection_name = params.extract_input("Selection"); + if (!selection_name.empty() && !component.attribute_exists(selection_name)) { + params.error_message_add(NodeWarningType::Error, + TIP_("No attribute with name \"") + selection_name + "\""); + } + GVArray_Typed selection = component.attribute_get_for_read( + selection_name, ATTR_DOMAIN_EDGE, true); + + Vector> selected_edges; + for (const int i : IndexRange(mesh.totedge)) { + if (selection[i]) { + selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2}); + } + } + + return selected_edges; +} + +static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Mesh"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_mesh()) { + params.set_output("Curve", GeometrySet()); + return; + } + + const MeshComponent &component = *geometry_set.get_component_for_read(); + const Mesh &mesh = *component.get_for_read(); + Span verts = Span{mesh.mvert, mesh.totvert}; + Span edges = Span{mesh.medge, mesh.totedge}; + if (edges.size() == 0) { + params.set_output("Curve", GeometrySet()); + return; + } + + Vector> selected_edges = get_selected_edges(params, component); + + CurveFromEdgesOutput output = mesh_to_curve(verts, selected_edges); + copy_attributes_to_points(*output.curve, component, output.point_to_vert_maps); + + params.set_output("Curve", GeometrySet::create_with_curve(output.curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_to_curve() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_mesh_to_curve_in, geo_node_mesh_to_curve_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec; + nodeRegisterType(&ntype); +} -- cgit v1.2.3 From c65f4b3d76deb47017a2e661290481e5e613b5db Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 28 May 2021 11:51:05 -0300 Subject: DrawManager: Early return for buffer cache creation No real functional changes. This is useful for benchmark cases when `cache->uv_cage` is passed but has no buffers are requested. --- source/blender/draw/intern/draw_cache_extract_mesh.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index f167ea3d540..fe162e6ea6d 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -5956,12 +5956,14 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, eMRDataType data_flag = 0; const bool do_lines_loose_subbuffer = mbc.ibo.lines_loose != NULL; + bool do_extract = false; #define TEST_ASSIGN(type, type_lowercase, name) \ do { \ if (DRW_TEST_ASSIGN_##type(mbc.type_lowercase.name)) { \ iter_flag |= mesh_extract_iter_type(&extract_##name); \ data_flag |= extract_##name.data_flag; \ + do_extract = true; \ } \ } while (0) @@ -6000,6 +6002,10 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, TEST_ASSIGN(IBO, ibo, edituv_points); TEST_ASSIGN(IBO, ibo, edituv_fdots); + if (!do_extract) { + return; + } + if (do_lines_loose_subbuffer) { iter_flag |= MR_ITER_LEDGE; } -- cgit v1.2.3 From a4700549e065e86c3d42c93e73f76106f4054512 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 28 May 2021 17:14:01 +0200 Subject: GPencil: Fix unreported random rotation for single point with texture When using ``Path`` alignment, if the stroke has one point the texture rotates randomly when move the viewport. This was because with one point is impossible to calculate a path. Now, if the stroke has only one point, the texture for this stroke is aligned to Object. --- source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl index 7412959a30b..ac48b94fea9 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl @@ -442,6 +442,10 @@ void stroke_vertex() if (is_dot) { # ifdef GP_MATERIAL_BUFFER_LEN int alignement = GP_FLAG(m) & GP_STROKE_ALIGNMENT; + /* For one point strokes use object aligment. */ + if (ma.x == -1 && ma2.x == -1 && alignement == GP_STROKE_ALIGNMENT_STROKE) { + alignement = GP_STROKE_ALIGNMENT_OBJECT; + } # endif vec2 x_axis; -- cgit v1.2.3 From 20e250dae307f9cb833dadc52059193dc1e1b5e5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 28 May 2021 12:04:52 -0400 Subject: Fix T88601: Attribute Compare boolean doesn't expose socket While we could make this node work for boolean inputs in the future, currently it's really just designed to compare "float-like" inputs. Many comparison modes don't even make sense for boolean inputs. Therefore, the simplest fix for this bug is just to disable the boolean attribute input modes for this node. Differential Revision: https://developer.blender.org/D11427 --- source/blender/makesrna/intern/rna_nodetree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b1b2e9738c1..9ef94a0df2f 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9148,12 +9148,12 @@ static void def_geo_attribute_attribute_compare(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean); RNA_def_property_ui_text(prop, "Input Type A", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean); RNA_def_property_ui_text(prop, "Input Type B", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -- cgit v1.2.3 From c36938297753ac45316f0b10b7d3e2f5307a6aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Fri, 28 May 2021 18:02:45 +0200 Subject: EEVEE: Fix NaN caused by ensure_valid_reflection() This was caused by unsafe sqrt calls. Fixes T86578 white artifacts in EEVEE Reviewed By: brecht, dfelinto Differential Revision: https://developer.blender.org/D11428 --- source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl | 6 +++--- source/blender/draw/intern/shaders/common_math_lib.glsl | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index c8eaa06094e..05496ad4ab0 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -164,8 +164,8 @@ vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N) vec2 N_new; if (valid1 && valid2) { /* If both are possible, do the expensive reflection-based check. */ - vec2 N1 = vec2(sqrt(1.0 - N1_z2), sqrt(N1_z2)); - vec2 N2 = vec2(sqrt(1.0 - N2_z2), sqrt(N2_z2)); + vec2 N1 = vec2(safe_sqrt(1.0 - N1_z2), safe_sqrt(N1_z2)); + vec2 N2 = vec2(safe_sqrt(1.0 - N2_z2), safe_sqrt(N2_z2)); float R1 = 2.0 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz; float R2 = 2.0 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz; @@ -181,7 +181,7 @@ vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N) } else if (valid1 || valid2) { float Nz2 = valid1 ? N1_z2 : N2_z2; - N_new = vec2(sqrt(1.0 - Nz2), sqrt(Nz2)); + N_new = vec2(safe_sqrt(1.0 - Nz2), safe_sqrt(Nz2)); } else { return Ng; diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl index 33deae0b0a1..479f9cd1827 100644 --- a/source/blender/draw/intern/shaders/common_math_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_lib.glsl @@ -86,6 +86,8 @@ float safe_rcp(float a) { return (a != 0.0) ? (1.0 / a) : 0.0; } vec2 safe_rcp(vec2 a) { return mix(vec2(0.0), (1.0 / a), notEqual(a, vec2(0.0))); } vec4 safe_rcp(vec4 a) { return mix(vec4(0.0), (1.0 / a), notEqual(a, vec4(0.0))); } +float safe_sqrt(float a) { return sqrt(max(a, 0.0)); } + float sqr(float a) { return a * a; } vec2 sqr(vec2 a) { return a * a; } vec3 sqr(vec3 a) { return a * a; } -- cgit v1.2.3 From 653bbaa246e3fa57b0b7846704fe5ce432659bb9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 28 May 2021 12:17:04 -0400 Subject: Geometry Nodes: Polish switch node UI Based on the task T88006, there are a few simple changes to make to improve the switch node: - Change the label to "False" / "True" for clarity - Change default to geometry, as it's the basic data container in geometry nodes. - Change node class to `NODE_CLASS_CONVERTOR`, which was an oversight in the original patch. I will add the new socket types (material and texture) in a separate commit. Thanks to @EitanSomething for the original patch. Differential Revision: https://developer.blender.org/D11165 --- source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenloader/intern/versioning_300.c | 53 ++++++++++++++++++---- .../nodes/geometry/nodes/node_geo_switch.cc | 44 +++++++++--------- 3 files changed, 66 insertions(+), 33 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 0bab980cfcd..eb937ac9608 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 1 +#define BLENDER_FILE_SUBVERSION 2 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 8c5e86eadd3..c175714c537 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -22,6 +22,7 @@ #include "BLI_listbase.h" #include "BLI_math_vector.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "DNA_brush_types.h" @@ -85,6 +86,32 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } +static void version_switch_node_input_prefix(Main *bmain) +{ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_GEOMETRY) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == GEO_NODE_SWITCH) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + /* Skip the "switch" socket. */ + if (socket == node->inputs.first) { + continue; + } + strcpy(socket->name, socket->name[0] == 'A' ? "False" : "True"); + + /* Replace "A" and "B", but keep the unique number suffix at the end. */ + char number_suffix[8]; + BLI_strncpy(number_suffix, socket->identifier + 1, sizeof(number_suffix)); + strcpy(socket->identifier, socket->name); + strcat(socket->identifier, number_suffix); + } + } + } + } + } + FOREACH_NODETREE_END; +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -110,6 +137,22 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 2)) { + version_switch_node_input_prefix(bmain); + + if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale_xyz[3]")) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->pose == NULL) { + continue; + } + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + copy_v3_fl(pchan->custom_scale_xyz, pchan->custom_scale); + } + } + } + } + /** * Versioning code until next subversion bump goes here. * @@ -121,15 +164,5 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ - if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale_xyz[3]")) { - LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - if (ob->pose == NULL) { - continue; - } - LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - copy_v3_fl(pchan->custom_scale_xyz, pchan->custom_scale); - } - } - } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index 049ba5d3143..742fafba9e6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -22,24 +22,24 @@ static bNodeSocketTemplate geo_node_switch_in[] = { {SOCK_BOOLEAN, N_("Switch")}, - {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_INT, N_("A"), 0, 0, 0, 0, -100000, 100000}, - {SOCK_INT, N_("B"), 0, 0, 0, 0, -100000, 100000}, - {SOCK_BOOLEAN, N_("A")}, - {SOCK_BOOLEAN, N_("B")}, - {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_RGBA, N_("A"), 0.8, 0.8, 0.8, 1.0}, - {SOCK_RGBA, N_("B"), 0.8, 0.8, 0.8, 1.0}, - {SOCK_STRING, N_("A")}, - {SOCK_STRING, N_("B")}, - {SOCK_GEOMETRY, N_("A")}, - {SOCK_GEOMETRY, N_("B")}, - {SOCK_OBJECT, N_("A")}, - {SOCK_OBJECT, N_("B")}, - {SOCK_COLLECTION, N_("A")}, - {SOCK_COLLECTION, N_("B")}, + {SOCK_FLOAT, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_FLOAT, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_INT, N_("False"), 0, 0, 0, 0, -100000, 100000}, + {SOCK_INT, N_("True"), 0, 0, 0, 0, -100000, 100000}, + {SOCK_BOOLEAN, N_("False")}, + {SOCK_BOOLEAN, N_("True")}, + {SOCK_VECTOR, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_VECTOR, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_RGBA, N_("False"), 0.8, 0.8, 0.8, 1.0}, + {SOCK_RGBA, N_("True"), 0.8, 0.8, 0.8, 1.0}, + {SOCK_STRING, N_("False")}, + {SOCK_STRING, N_("True")}, + {SOCK_GEOMETRY, N_("False")}, + {SOCK_GEOMETRY, N_("True")}, + {SOCK_OBJECT, N_("False")}, + {SOCK_OBJECT, N_("True")}, + {SOCK_COLLECTION, N_("False")}, + {SOCK_COLLECTION, N_("True")}, {-1, ""}, }; @@ -64,7 +64,7 @@ static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), Pointe static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node) { NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__); - data->input_type = SOCK_FLOAT; + data->input_type = SOCK_GEOMETRY; node->storage = data; } @@ -91,8 +91,8 @@ static void output_input(GeoNodeExecParams ¶ms, const StringRef input_suffix, const StringRef output_identifier) { - const std::string name_a = "A" + input_suffix; - const std::string name_b = "B" + input_suffix; + const std::string name_a = "False" + input_suffix; + const std::string name_b = "True" + input_suffix; if (input) { params.set_input_unused(name_a); if (params.lazy_require_input(name_b)) { @@ -165,7 +165,7 @@ void register_node_type_geo_switch() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out); node_type_init(&ntype, geo_node_switch_init); node_type_update(&ntype, blender::nodes::geo_node_switch_update); -- cgit v1.2.3 From 33113506701fac51b8b4dc8f51975200f885e50a Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Fri, 28 May 2021 18:35:26 +0200 Subject: Fix T87932: Failure to build movie strip proxy We didn't initialize the scaled proxy frame properly. This would lead to issues in ffmpeg 4.4 as they are more strict that the API is properly used. Now we initialize the size and format of the frame. --- source/blender/imbuf/intern/indexer.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 11ce77e3091..26f332bd575 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -466,13 +466,6 @@ struct proxy_output_ctx { struct anim *anim; }; -// work around stupid swscaler 16 bytes alignment bug... - -static int round_up(int x, int mod) -{ - return x + ((mod - (x % mod)) % mod); -} - static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( struct anim *anim, AVStream *st, int proxy_size, int width, int height, int quality) { @@ -595,15 +588,19 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( if (st->codecpar->width != width || st->codecpar->height != height || st->codecpar->format != rv->c->pix_fmt) { rv->frame = av_frame_alloc(); - av_image_fill_arrays( - rv->frame->data, - rv->frame->linesize, - MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, round_up(width, 16), height, 1), - "alloc proxy output frame"), - rv->c->pix_fmt, - round_up(width, 16), - height, - 1); + + av_image_fill_arrays(rv->frame->data, + rv->frame->linesize, + MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, width, height, 1), + "alloc proxy output frame"), + rv->c->pix_fmt, + width, + height, + 1); + + rv->frame->format = rv->c->pix_fmt; + rv->frame->width = width; + rv->frame->height = height; rv->sws_ctx = sws_getContext(st->codecpar->width, rv->orig_height, -- cgit v1.2.3 From 9225fe933ae9904ea7bb646110308a921b37fc86 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Fri, 28 May 2021 18:37:36 +0200 Subject: Make encoded video fps correct with ffmpeg < 4.4 Before the FFmpeg commit: github.com/FFmpeg/FFmpeg/commit/1c0885334dda9ee8652e60c586fa2e3674056586 FFmpeg would use deprecated variables to calculate the video fps. We don't use these deprecated variables anymore, so ensure that the duration is correct in ffmpeg versions without this fix. Reviewed By: Sergey, Richard Antalik Differential Revision: http://developer.blender.org/D11417 --- intern/ffmpeg/ffmpeg_compat.h | 49 ++++++++++++++++++++++++++ source/blender/blenkernel/intern/writeffmpeg.c | 16 ++++++--- source/blender/imbuf/intern/indexer.c | 3 ++ 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/intern/ffmpeg/ffmpeg_compat.h b/intern/ffmpeg/ffmpeg_compat.h index 54a004d53e2..0c22cf82688 100644 --- a/intern/ffmpeg/ffmpeg_compat.h +++ b/intern/ffmpeg/ffmpeg_compat.h @@ -43,6 +43,55 @@ # define FFMPEG_INLINE static inline #endif +#if (LIBAVFORMAT_VERSION_MAJOR < 58) || \ + ((LIBAVFORMAT_VERSION_MAJOR == 58) && (LIBAVFORMAT_VERSION_MINOR < 76)) +# define FFMPEG_USE_DURATION_WORKAROUND 1 + +/* Before ffmpeg 4.4, package duration calculation used depricated variables to calculate the + * packet duration. Use the function from commit + * github.com/FFmpeg/FFmpeg/commit/1c0885334dda9ee8652e60c586fa2e3674056586 + * to calculate the correct framerate for ffmpeg < 4.4. + */ + +FFMPEG_INLINE +void my_guess_pkt_duration(AVFormatContext *s, AVStream *st, AVPacket *pkt) +{ + if (pkt->duration < 0 && st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) { + av_log(s, + AV_LOG_WARNING, + "Packet with invalid duration %" PRId64 " in stream %d\n", + pkt->duration, + pkt->stream_index); + pkt->duration = 0; + } + + if (pkt->duration) { + return; + } + + switch (st->codecpar->codec_type) { + case AVMEDIA_TYPE_VIDEO: + if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0) { + pkt->duration = av_rescale_q(1, av_inv_q(st->avg_frame_rate), st->time_base); + } + else if (st->time_base.num * 1000LL > st->time_base.den) { + pkt->duration = 1; + } + break; + case AVMEDIA_TYPE_AUDIO: { + int frame_size = av_get_audio_frame_duration2(st->codecpar, pkt->size); + if (frame_size && st->codecpar->sample_rate) { + pkt->duration = av_rescale_q( + frame_size, (AVRational){1, st->codecpar->sample_rate}, st->time_base); + } + break; + } + default: + break; + } +} +#endif + FFMPEG_INLINE void my_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp) { diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 39f65d76e3c..3408f990fd1 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -205,12 +205,11 @@ static int write_audio_frame(FFMpegContext *context) success = -1; } - av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base); - if (pkt->duration > 0) { - pkt->duration = av_rescale_q(pkt->duration, c->time_base, context->audio_stream->time_base); - } - pkt->stream_index = context->audio_stream->index; + av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->audio_stream, pkt); +# endif pkt->flags |= AV_PKT_FLAG_KEY; @@ -349,6 +348,10 @@ static int write_video_frame(FFMpegContext *context, int cfra, AVFrame *frame, R packet->stream_index = context->video_stream->index; av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->video_stream, packet); +# endif + if (av_interleaved_write_frame(context->outfile, packet) != 0) { success = -1; break; @@ -1181,6 +1184,9 @@ static void flush_ffmpeg(FFMpegContext *context) packet->stream_index = context->video_stream->index; av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->video_stream, packet); +# endif int write_ret = av_interleaved_write_frame(context->outfile, packet); if (write_ret != 0) { diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 26f332bd575..453df6078ce 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -683,6 +683,9 @@ static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *fr packet->stream_index = ctx->st->index; av_packet_rescale_ts(packet, ctx->c->time_base, ctx->st->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(ctx->of, ctx->st, packet); +# endif int write_ret = av_interleaved_write_frame(ctx->of, packet); if (write_ret != 0) { -- cgit v1.2.3 From 3c02e648f395f8b37eecc70e7abc2ce05528c09f Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 28 May 2021 17:14:01 +0200 Subject: GPencil: Fix unreported random rotation for single point with texture When using ``Path`` alignment, if the stroke has one point the texture rotates randomly when move the viewport. This was because with one point is impossible to calculate a path. Now, if the stroke has only one point, the texture for this stroke is aligned to Object. --- source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl index 7412959a30b..ac48b94fea9 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl @@ -442,6 +442,10 @@ void stroke_vertex() if (is_dot) { # ifdef GP_MATERIAL_BUFFER_LEN int alignement = GP_FLAG(m) & GP_STROKE_ALIGNMENT; + /* For one point strokes use object aligment. */ + if (ma.x == -1 && ma2.x == -1 && alignement == GP_STROKE_ALIGNMENT_STROKE) { + alignement = GP_STROKE_ALIGNMENT_OBJECT; + } # endif vec2 x_axis; -- cgit v1.2.3 From 30f7acfffa427f6de42aefbf829a408cb3411b19 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Fri, 28 May 2021 15:32:12 -0400 Subject: Docs: Add relevant OCIO envvars to Blender's help message --- source/creator/CMakeLists.txt | 4 ++++ source/creator/creator_args.c | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 6a768106d9e..92cc4ae297a 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -119,6 +119,10 @@ if(WITH_GMP) add_definitions(-DWITH_GMP) endif() +if(WITH_OPENCOLORIO) + add_definitions(-DWITH_OCIO) +endif() + # Setup the exe sources and buildinfo set(SRC creator.c diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 36fdaef507b..f9492f5bb62 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -674,6 +674,15 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo printf(" $BLENDER_USER_DATAFILES Directory for user data files (icons, translations, ..).\n"); printf(" $BLENDER_SYSTEM_DATAFILES Directory for system wide data files.\n"); printf(" $BLENDER_SYSTEM_PYTHON Directory for system Python libraries.\n"); +# ifdef WITH_OCIO + printf(" $OCIO Path to override the OpenColorIO config file.\n"); + printf( + " $OCIO_ACTIVE_DISPLAYS Overrides the active_displays list from the config file and " + "reorders them. Colon-separated list of displays, e.g 'sRGB:P3'.\n"); + printf( + " $OCIO_ACTIVE_VIEWS Overrides the active_views list from the config file and " + "reorders them. Colon-separated list of view names, e.g 'internal:client:DI'.\n"); +# endif # ifdef WIN32 printf(" $TEMP Store temporary files here.\n"); # else -- cgit v1.2.3 From 6f86d50b9271e23d3d2ca3beba91665078860289 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sat, 29 May 2021 11:21:29 -0400 Subject: UI: Match tooltip with interface name --- release/scripts/startup/bl_operators/uvcalc_lightmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_operators/uvcalc_lightmap.py b/release/scripts/startup/bl_operators/uvcalc_lightmap.py index 29c17711c2a..6ba8750e9df 100644 --- a/release/scripts/startup/bl_operators/uvcalc_lightmap.py +++ b/release/scripts/startup/bl_operators/uvcalc_lightmap.py @@ -628,7 +628,7 @@ class LightMapPack(Operator): name="New Image", description=( "Assign new images for every mesh (only one if " - "shared tex space enabled)" + "Share Texture Space is enabled)" ), default=False, ) -- cgit v1.2.3 From 870aaf3fb645a2894dd1c0fb34f23b650ec7cab3 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sat, 29 May 2021 11:22:17 -0400 Subject: Docs: Add documentation for 'material_index' Fixes T88485 --- source/blender/makesrna/intern/rna_curve.c | 4 ++-- source/blender/makesrna/intern/rna_gpencil.c | 2 +- source/blender/makesrna/intern/rna_mesh.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index abc96ddc820..e5e7564f578 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -1376,7 +1376,7 @@ static void rna_def_charinfo(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); // RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this character"); RNA_def_property_int_funcs(prop, "rna_ChariInfo_material_index_get", "rna_ChariInfo_material_index_set", @@ -2068,7 +2068,7 @@ static void rna_def_curve_nurb(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this curve"); RNA_def_property_int_funcs(prop, NULL, NULL, "rna_Curve_material_index_range"); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 5f865f9d4cd..19ed5f960cf 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -1625,7 +1625,7 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna) /* Material Index */ prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", "Index of material used in this stroke"); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this stroke"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Settings */ diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index f772f9b5573..d5a1047d287 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1823,7 +1823,7 @@ static void rna_def_mlooptri(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_int_funcs(prop, "rna_MeshLoopTriangle_material_index_get", NULL, NULL); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this triangle"); prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -1933,7 +1933,7 @@ static void rna_def_mpolygon(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this polygon"); # if 0 RNA_def_property_int_funcs(prop, NULL, NULL, "rna_MeshPoly_material_index_range"); # endif -- cgit v1.2.3 From f5d14e36e8a0974f90780924683b9db4909d8754 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sat, 29 May 2021 12:16:13 -0400 Subject: PyDoc: Use em dash instead of comma for enum items --- doc/python_api/sphinx_doc_gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 032f8a86bd5..64a8ef64b23 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1246,7 +1246,7 @@ def pyrna_enum2sphinx(prop, use_empty_descriptions=False): "%s.\n" % ( identifier, # Account for multi-line enum descriptions, allowing this to be a block of text. - indent(", ".join(escape_rst(val) for val in (name, description) if val) or "Undocumented", " "), + indent(" -- ".join(escape_rst(val) for val in (name, description) if val) or "Undocumented", " "), ) for identifier, name, description in prop.enum_items ]) -- cgit v1.2.3 From 7b5796dcaa95ebb83973539a2400654165375e1d Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sun, 30 May 2021 11:07:38 -0400 Subject: Docs: clarify description and usage of 'bpy.app.version_file' Fixes T88669 --- source/blender/python/intern/bpy_app.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 927ec11c376..4de6063098b 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -82,7 +82,10 @@ static PyTypeObject BlenderAppType; static PyStructSequence_Field app_info_fields[] = { {"version", "The Blender version as a tuple of 3 numbers. eg. (2, 83, 1)"}, - {"version_file", "The blend file version, compatible with ``bpy.data.version``"}, + {"version_file", + "The Blender version, as a tuple, last used to save a .blend file, compatible with " + "``bpy.data.version``. This value should be used for handling compatibility changes between " + "Blender versions"}, {"version_string", "The Blender version formatted as a string"}, {"version_cycle", "The release status of this build alpha/beta/rc/release"}, {"version_char", "Deprecated, always an empty string"}, -- cgit v1.2.3 From e6a69f76535b3a0c65b88265d614007deb9fe760 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sun, 30 May 2021 11:09:01 -0400 Subject: Docs: Capitalize first word of sentence --- doc/python_api/sphinx_doc_gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 64a8ef64b23..4be27e0f0e8 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -954,7 +954,7 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): # constant, not much fun we can do here except to list it. # TODO, figure out some way to document these! fw(".. data:: %s\n\n" % attribute) - write_indented_lines(" ", fw, "constant value %s" % repr(value), False) + write_indented_lines(" ", fw, "Constant value %s" % repr(value), False) fw("\n") else: BPY_LOGGER.debug("\tnot documenting %s.%s of %r type" % (module_name, attribute, value_type.__name__)) -- cgit v1.2.3 From a1556fa05ca96e43b70aa4f6b6284eb581701e4d Mon Sep 17 00:00:00 2001 From: Howard Trickey Date: Sun, 30 May 2021 16:37:49 -0400 Subject: Boolean: applying patch D11431 to speed up hole-tolerant raycast. This patch from Erik Abrahamsson uses a parallel_for to speed up the case where the input is not manifold and the "hole_tolerant" option is set. In a test case on a 24 core (48 thread) machine, this sped up a the boolean part on an object with 221k triangles from 12.06s to 0.46s. --- source/blender/blenlib/intern/mesh_boolean.cc | 88 ++++++++++++++++----------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 25291b8c3b0..431720761bc 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -41,6 +41,7 @@ # include "BLI_set.hh" # include "BLI_span.hh" # include "BLI_stack.hh" +# include "BLI_task.hh" # include "BLI_vector.hh" # include "BLI_vector_set.hh" @@ -48,6 +49,10 @@ # include "BLI_mesh_boolean.hh" +# ifdef WITH_TBB +# include "tbb/spin_mutex.h" +# endif + // # define PERFDEBUG namespace blender::meshintersect { @@ -2567,47 +2572,58 @@ static IMesh raycast_tris_boolean(const IMesh &tm, BVHTree *tree = raycast_tree(tm); Vector out_faces; out_faces.reserve(tm.face_size()); - Array in_shape(nshapes, 0); - Array winding(nshapes, 0); - for (int t : tm.face_index_range()) { - Face &tri = *tm.face(t); - int shape = shape_fn(tri.orig); - if (dbg_level > 0) { - std::cout << "process triangle " << t << " = " << &tri << "\n"; - std::cout << "shape = " << shape << "\n"; - } - test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape); - for (int other_shape = 0; other_shape < nshapes; ++other_shape) { - if (other_shape == shape) { - continue; - } - /* The in_shape array has a confidence value for "insideness". - * For most operations, even a hint of being inside - * gives good results, but when shape is a cutter in a Difference - * operation, we want to be pretty sure that the point is inside other_shape. - * E.g., T75827. - * Also, when the operation is intersection, we also want high confidence. - */ - bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) || - op == BoolOpType::Intersect; - bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f); +# ifdef WITH_TBB + tbb::spin_mutex mtx; +# endif + const int grainsize = 256; + parallel_for(IndexRange(tm.face_size()), grainsize, [&](IndexRange range) { + Array in_shape(nshapes, 0); + Array winding(nshapes, 0); + for (int t : range) { + Face &tri = *tm.face(t); + int shape = shape_fn(tri.orig); if (dbg_level > 0) { - std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape " - << other_shape << " val = " << in_shape[other_shape] << "\n"; + std::cout << "process triangle " << t << " = " << &tri << "\n"; + std::cout << "shape = " << shape << "\n"; } - winding[other_shape] = inside; - } - bool do_flip; - bool do_remove = raycast_test_remove(op, winding, shape, &do_flip); - if (!do_remove) { - if (!do_flip) { - out_faces.append(&tri); + test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape); + for (int other_shape = 0; other_shape < nshapes; ++other_shape) { + if (other_shape == shape) { + continue; + } + /* The in_shape array has a confidence value for "insideness". + * For most operations, even a hint of being inside + * gives good results, but when shape is a cutter in a Difference + * operation, we want to be pretty sure that the point is inside other_shape. + * E.g., T75827. + * Also, when the operation is intersection, we also want high confidence. + */ + bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) || + op == BoolOpType::Intersect; + bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f); + if (dbg_level > 0) { + std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape " + << other_shape << " val = " << in_shape[other_shape] << "\n"; + } + winding[other_shape] = inside; } - else { - raycast_add_flipped(out_faces, tri, arena); + bool do_flip; + bool do_remove = raycast_test_remove(op, winding, shape, &do_flip); + { +# ifdef WITH_TBB + tbb::spin_mutex::scoped_lock lock(mtx); +# endif + if (!do_remove) { + if (!do_flip) { + out_faces.append(&tri); + } + else { + raycast_add_flipped(out_faces, tri, arena); + } + } } } - } + }); BLI_bvhtree_free(tree); ans.set_faces(out_faces); return ans; -- cgit v1.2.3 From 5f749a03ca1d34296adc84fc251e15790f583021 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 31 May 2021 09:32:37 +0200 Subject: Fix T88456: DrawManager: Keep subset RenderMeshData around when geometry does not change. Reuse loose geometry during selection (and other operations) from previous calculation. Loose geometry stays the same, but was recalculated to determine the size of GPU buffers. This patch would reuse the previous loose geometry when geometry wasn't changed. Although not the main bottleneck during selection it is measurable. Master. `rdata 46ms iter 55ms (frame 410ms)` This patch. `rdata 5ms iter 52ms (frame 342ms)` Reviewed By: mano-wii Differential Revision: https://developer.blender.org/D11339 --- source/blender/draw/intern/draw_cache_extract.h | 17 +++ .../blender/draw/intern/draw_cache_extract_mesh.c | 140 +++++++++++---------- source/blender/draw/intern/draw_cache_impl_mesh.c | 36 ++++-- 3 files changed, 121 insertions(+), 72 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index a3e43ff2ec5..14cbba82fba 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -150,6 +150,18 @@ typedef struct MeshBufferCache { GPUIndexBuf **tris_per_mat; } MeshBufferCache; +/** + * Data that are kept around between extractions to reduce rebuilding time. + * + * - Loose geometry. + */ +typedef struct MeshBufferExtractionCache { + int edge_loose_len; + int vert_loose_len; + int *lverts; + int *ledges; +} MeshBufferExtractionCache; + typedef enum DRWBatchFlag { MBC_SURFACE = (1 << 0), MBC_SURFACE_WEIGHTS = (1 << 1), @@ -195,6 +207,10 @@ typedef enum DRWBatchFlag { typedef struct MeshBatchCache { MeshBufferCache final, cage, uv_cage; + MeshBufferExtractionCache final_extraction_cache; + MeshBufferExtractionCache cage_extraction_cache; + MeshBufferExtractionCache uv_cage_extraction_cache; + struct { /* Surfaces / Render */ GPUBatch *surface; @@ -271,6 +287,7 @@ typedef struct MeshBatchCache { void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, MeshBufferCache mbc, + MeshBufferExtractionCache *extraction_cache, Mesh *me, const bool is_editmode, const bool is_paint_mode, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index fe162e6ea6d..62d8040e88b 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -134,79 +134,85 @@ typedef struct MeshRenderData { int *lverts, *ledges; } MeshRenderData; -static void mesh_render_data_update_loose_geom(MeshRenderData *mr, - const eMRIterType iter_type, - const eMRDataType UNUSED(data_flag)) +static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtractionCache *cache) { + mr->ledges = cache->ledges; + mr->lverts = cache->lverts; + mr->vert_loose_len = cache->vert_loose_len; + mr->edge_loose_len = cache->edge_loose_len; + + mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2); +} + +static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr, + MeshBufferExtractionCache *cache) +{ + /* Early exit: Are loose geometry already available. Only checking for loose verts as loose edges + * and verts are calculated at the same time.*/ + if (cache->lverts) { + return; + } + + cache->vert_loose_len = 0; + cache->edge_loose_len = 0; + if (mr->extract_type != MR_EXTRACT_BMESH) { /* Mesh */ - if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { - mr->vert_loose_len = 0; - mr->edge_loose_len = 0; - BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); + BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); - mr->ledges = MEM_mallocN(mr->edge_len * sizeof(int), __func__); - const MEdge *med = mr->medge; - for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { - if (med->flag & ME_LOOSEEDGE) { - mr->ledges[mr->edge_loose_len++] = med_index; - } - /* Tag verts as not loose. */ - BLI_BITMAP_ENABLE(lvert_map, med->v1); - BLI_BITMAP_ENABLE(lvert_map, med->v2); - } - if (mr->edge_loose_len < mr->edge_len) { - mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges)); + cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); + const MEdge *med = mr->medge; + for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { + if (med->flag & ME_LOOSEEDGE) { + cache->ledges[cache->edge_loose_len++] = med_index; } + /* Tag verts as not loose. */ + BLI_BITMAP_ENABLE(lvert_map, med->v1); + BLI_BITMAP_ENABLE(lvert_map, med->v2); + } + if (cache->edge_loose_len < mr->edge_len) { + cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); + } - mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); - for (int v = 0; v < mr->vert_len; v++) { - if (!BLI_BITMAP_TEST(lvert_map, v)) { - mr->lverts[mr->vert_loose_len++] = v; - } + cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); + for (int v = 0; v < mr->vert_len; v++) { + if (!BLI_BITMAP_TEST(lvert_map, v)) { + cache->lverts[cache->vert_loose_len++] = v; } - if (mr->vert_loose_len < mr->vert_len) { - mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts)); - } - - MEM_freeN(lvert_map); - - mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2); } + if (cache->vert_loose_len < mr->vert_len) { + cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); + } + + MEM_freeN(lvert_map); } else { /* #BMesh */ BMesh *bm = mr->bm; - if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { - int elem_id; - BMIter iter; - BMVert *eve; - BMEdge *ede; - mr->vert_loose_len = 0; - mr->edge_loose_len = 0; - - mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { - if (eve->e == NULL) { - mr->lverts[mr->vert_loose_len++] = elem_id; - } - } - if (mr->vert_loose_len < mr->vert_len) { - mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts)); - } + int elem_id; + BMIter iter; + BMVert *eve; + BMEdge *ede; - mr->ledges = MEM_mallocN(mr->edge_len * sizeof(*mr->ledges), __func__); - BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { - if (ede->l == NULL) { - mr->ledges[mr->edge_loose_len++] = elem_id; - } - } - if (mr->edge_loose_len < mr->edge_len) { - mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges)); + cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*cache->lverts), __func__); + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { + if (eve->e == NULL) { + cache->lverts[cache->vert_loose_len++] = elem_id; } + } + if (cache->vert_loose_len < mr->vert_len) { + cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); + } - mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2; + cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); + BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { + if (ede->l == NULL) { + cache->ledges[cache->edge_loose_len++] = elem_id; + } + } + if (cache->edge_loose_len < mr->edge_len) { + cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); } } } @@ -317,6 +323,7 @@ static void mesh_render_data_update_normals(MeshRenderData *mr, * otherwise don't use modifiers as they are not from this object. */ static MeshRenderData *mesh_render_data_create(Mesh *me, + MeshBufferExtractionCache *cache, const bool is_editmode, const bool is_paint_mode, const bool is_mode_active, @@ -325,8 +332,7 @@ static MeshRenderData *mesh_render_data_create(Mesh *me, const bool do_uvedit, const DRW_MeshCDMask *UNUSED(cd_used), const ToolSettings *ts, - const eMRIterType iter_type, - const eMRDataType data_flag) + const eMRIterType iter_type) { MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); mr->toolsettings = ts; @@ -435,7 +441,11 @@ static MeshRenderData *mesh_render_data_create(Mesh *me, mr->poly_len = bm->totface; mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); } - mesh_render_data_update_loose_geom(mr, iter_type, data_flag); + + if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { + mesh_render_data_loose_geom_ensure(mr, cache); + mesh_render_data_loose_geom_load(mr, cache); + } return mr; } @@ -446,8 +456,9 @@ static void mesh_render_data_free(MeshRenderData *mr) MEM_SAFE_FREE(mr->poly_normals); MEM_SAFE_FREE(mr->loop_normals); - MEM_SAFE_FREE(mr->lverts); - MEM_SAFE_FREE(mr->ledges); + /* Loose geometry are owned by MeshBufferExtractionCache. */ + mr->ledges = NULL; + mr->lverts = NULL; MEM_freeN(mr); } @@ -5908,6 +5919,7 @@ static void extract_task_create(struct TaskGraph *task_graph, void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, MeshBufferCache mbc, + MeshBufferExtractionCache *extraction_cache, Mesh *me, const bool is_editmode, @@ -6017,6 +6029,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, #endif MeshRenderData *mr = mesh_render_data_create(me, + extraction_cache, is_editmode, is_paint_mode, is_mode_active, @@ -6025,8 +6038,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, do_uvedit, cd_layer_used, ts, - iter_flag, - data_flag); + iter_flag); mr->use_hide = use_hide; mr->use_subsurf_fdots = use_subsurf_fdots; mr->use_final_mesh = do_final; diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 8d5fdcc5276..c2dc9b3ad8d 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -707,6 +707,26 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) } } +static void mesh_buffer_cache_clear(MeshBufferCache *mbufcache) +{ + GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo; + GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo; + for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); i++) { + GPU_VERTBUF_DISCARD_SAFE(vbos[i]); + } + for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) { + GPU_INDEXBUF_DISCARD_SAFE(ibos[i]); + } +} + +static void mesh_buffer_extraction_cache_clear(MeshBufferExtractionCache *extraction_cache) +{ + MEM_SAFE_FREE(extraction_cache->lverts); + MEM_SAFE_FREE(extraction_cache->ledges); + extraction_cache->edge_loose_len = 0; + extraction_cache->vert_loose_len = 0; +} + static void mesh_batch_cache_clear(Mesh *me) { MeshBatchCache *cache = me->runtime.batch_cache; @@ -714,16 +734,13 @@ static void mesh_batch_cache_clear(Mesh *me) return; } FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { - GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo; - GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo; - for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); i++) { - GPU_VERTBUF_DISCARD_SAFE(vbos[i]); - } - for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) { - GPU_INDEXBUF_DISCARD_SAFE(ibos[i]); - } + mesh_buffer_cache_clear(mbufcache); } + mesh_buffer_extraction_cache_clear(&cache->final_extraction_cache); + mesh_buffer_extraction_cache_clear(&cache->cage_extraction_cache); + mesh_buffer_extraction_cache_clear(&cache->uv_cage_extraction_cache); + for (int i = 0; i < cache->mat_len; i++) { GPU_INDEXBUF_DISCARD_SAFE(cache->final.tris_per_mat[i]); } @@ -1543,6 +1560,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, cache->uv_cage, + &cache->uv_cage_extraction_cache, me, is_editmode, is_paint_mode, @@ -1561,6 +1579,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, cache->cage, + &cache->cage_extraction_cache, me, is_editmode, is_paint_mode, @@ -1578,6 +1597,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, cache->final, + &cache->final_extraction_cache, me, is_editmode, is_paint_mode, -- cgit v1.2.3 From 421c0b45e5015547e98112016bc550da5296e334 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 31 May 2021 10:20:23 +0200 Subject: Fix (studio-reported) crash in collection management code. Code checking for potential collection loop dependencies can be called in cases where we cannot guarantee that there is no NULL pointers, so we need to check those. Was already done for objects. --- source/blender/blenkernel/intern/collection.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index d8fbdf26d93..be827cd338d 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -1423,7 +1423,8 @@ static bool collection_instance_find_recursive(Collection *collection, } LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) { - if (collection_instance_find_recursive(collection_child->collection, instance_collection)) { + if (collection_child->collection != NULL && + collection_instance_find_recursive(collection_child->collection, instance_collection)) { return true; } } -- cgit v1.2.3 From 26fb7b9474c46c8e1fb64230d66610ebf3b29b7f Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 31 May 2021 11:12:39 +0200 Subject: Geometry Nodes: do not create unnecessary geometry components Previously, making instances real would always create an (empty) volume and curve component, even when not necessary. This also fixes T88653. --- .../blenkernel/intern/geometry_set_instances.cc | 26 +++++++++++++++------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 9abd00c2b4f..69840ba1612 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -544,9 +544,9 @@ static void join_attributes(Span set_groups, } } -static void join_curve_splines(Span set_groups, CurveComponent &result) +static CurveEval *join_curve_splines(Span set_groups) { - CurveEval *new_curve = new CurveEval(); + Vector new_splines; for (const GeometryInstanceGroup &set_group : set_groups) { const GeometrySet &set = set_group.geometry_set; if (!set.has_curve()) { @@ -558,10 +558,18 @@ static void join_curve_splines(Span set_groups, CurveComp for (const float4x4 &transform : set_group.transforms) { SplinePtr new_spline = source_spline->copy(); new_spline->transform(transform); - new_curve->add_spline(std::move(new_spline)); + new_splines.append(std::move(new_spline)); } } } + if (new_splines.is_empty()) { + return nullptr; + } + + CurveEval *new_curve = new CurveEval(); + for (SplinePtr &new_spline : new_splines) { + new_curve->add_spline(std::move(new_spline)); + } for (SplinePtr &spline : new_curve->splines()) { /* Spline instances should have no custom attributes, since they always come @@ -573,8 +581,7 @@ static void join_curve_splines(Span set_groups, CurveComp } new_curve->attributes.reallocate(new_curve->splines().size()); - - result.replace(new_curve); + return new_curve; } static void join_instance_groups_mesh(Span set_groups, @@ -639,14 +646,17 @@ static void join_instance_groups_volume(Span set_groups, { /* Not yet supported. Joining volume grids with the same name requires resampling of at least * one of the grids. The cell size of the resulting volume has to be determined somehow. */ - VolumeComponent &dst_component = result.get_component_for_write(); - UNUSED_VARS(set_groups, dst_component); + UNUSED_VARS(set_groups, result); } static void join_instance_groups_curve(Span set_groups, GeometrySet &result) { + CurveEval *curve = join_curve_splines(set_groups); + if (curve == nullptr) { + return; + } CurveComponent &dst_component = result.get_component_for_write(); - join_curve_splines(set_groups, dst_component); + dst_component.replace(curve); } GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set) -- cgit v1.2.3 From 632bfee0a5ea92d213b5c9d010483c16f0baa636 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 31 May 2021 11:14:40 +0200 Subject: Added v2.93 pipeline config for new buildbot. --- build_files/config/pipeline_config.json | 91 +++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 build_files/config/pipeline_config.json diff --git a/build_files/config/pipeline_config.json b/build_files/config/pipeline_config.json new file mode 100644 index 00000000000..1301ff1862d --- /dev/null +++ b/build_files/config/pipeline_config.json @@ -0,0 +1,91 @@ +{ + "update-code": { + "git": { + "submodules": [ + { + "path": "release/scripts/addons", + "branch": "blender-v2.93-release", + "commit_id": "HEAD" + }, + { + "path": "release/scripts/addons_contrib", + "branch": "blender-v2.93-release", + "commit_id": "HEAD" + }, + { + "path": "release/datafiles/locale", + "branch": "blender-v2.93-release", + "commit_id": "HEAD" + }, + { + "path": "source/tools", + "branch": "blender-v2.93-release", + "commit_id": "HEAD" + } + ] + }, + "svn": { + "tests": { + "path": "lib/tests", + "branch": "tags/blender-2.93-release", + "commit_id": "HEAD" + }, + "libraries": { + "darwin-x86_64": { + "path": "lib/darwin", + "branch": "tags/blender-2.93-release", + "commit_id": "HEAD" + }, + "darwin-arm64": { + "path": "lib/darwin_arm64", + "branch": "tags/blender-2.93-release", + "commit_id": "HEAD" + }, + "linux-x86_64": { + "path": "lib/linux_centos7_x86_64", + "branch": "tags/blender-2.93-release", + "commit_id": "HEAD" + }, + "windows-amd64": { + "path": "lib/win64_vc15", + "branch": "tags/blender-2.93-release", + "commit_id": "HEAD" + } + } + } + }, + "buildbot": { + "gcc": { + "version": "9.0" + }, + "sdks": { + "optix": { + "version": "7.1.0" + }, + "cuda10": { + "version": "10.1" + }, + "cuda11": { + "version": "11.3" + } + }, + "cmake": { + "default": { + "version": "any", + "overrides": {} + }, + "darwin-x86_64": { + "overrides": {} + }, + "darwin-arm64": { + "overrides": {} + }, + "linux-x86_64": { + "overrides": {} + }, + "windows-amd64": { + "overrides": {} + } + } + } +} \ No newline at end of file -- cgit v1.2.3 From 2534609262aa358d1a921c2f76a1aea6ff780f50 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 31 May 2021 11:16:13 +0200 Subject: Revert "Added v2.93 pipeline config for new buildbot." This reverts commit 632bfee0a5ea92d213b5c9d010483c16f0baa636. This config is only intended for 2.93. Master will get its own config file after testing that 2.93 is correct. --- build_files/config/pipeline_config.json | 91 --------------------------------- 1 file changed, 91 deletions(-) delete mode 100644 build_files/config/pipeline_config.json diff --git a/build_files/config/pipeline_config.json b/build_files/config/pipeline_config.json deleted file mode 100644 index 1301ff1862d..00000000000 --- a/build_files/config/pipeline_config.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "update-code": { - "git": { - "submodules": [ - { - "path": "release/scripts/addons", - "branch": "blender-v2.93-release", - "commit_id": "HEAD" - }, - { - "path": "release/scripts/addons_contrib", - "branch": "blender-v2.93-release", - "commit_id": "HEAD" - }, - { - "path": "release/datafiles/locale", - "branch": "blender-v2.93-release", - "commit_id": "HEAD" - }, - { - "path": "source/tools", - "branch": "blender-v2.93-release", - "commit_id": "HEAD" - } - ] - }, - "svn": { - "tests": { - "path": "lib/tests", - "branch": "tags/blender-2.93-release", - "commit_id": "HEAD" - }, - "libraries": { - "darwin-x86_64": { - "path": "lib/darwin", - "branch": "tags/blender-2.93-release", - "commit_id": "HEAD" - }, - "darwin-arm64": { - "path": "lib/darwin_arm64", - "branch": "tags/blender-2.93-release", - "commit_id": "HEAD" - }, - "linux-x86_64": { - "path": "lib/linux_centos7_x86_64", - "branch": "tags/blender-2.93-release", - "commit_id": "HEAD" - }, - "windows-amd64": { - "path": "lib/win64_vc15", - "branch": "tags/blender-2.93-release", - "commit_id": "HEAD" - } - } - } - }, - "buildbot": { - "gcc": { - "version": "9.0" - }, - "sdks": { - "optix": { - "version": "7.1.0" - }, - "cuda10": { - "version": "10.1" - }, - "cuda11": { - "version": "11.3" - } - }, - "cmake": { - "default": { - "version": "any", - "overrides": {} - }, - "darwin-x86_64": { - "overrides": {} - }, - "darwin-arm64": { - "overrides": {} - }, - "linux-x86_64": { - "overrides": {} - }, - "windows-amd64": { - "overrides": {} - } - } - } -} \ No newline at end of file -- cgit v1.2.3 From 2161840d07ae0a934a734ac6198aa7e5cc4b6fcf Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 31 May 2021 10:20:23 +0200 Subject: Fix (studio-reported) crash in collection management code. Code checking for potential collection loop dependencies can be called in cases where we cannot guarantee that there is no NULL pointers, so we need to check those. Was already done for objects. NOTE: doubled-checked by @jbakker, thanks. --- source/blender/blenkernel/intern/collection.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 3170c3aa65c..2813f535eb5 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -1425,7 +1425,8 @@ static bool collection_instance_find_recursive(Collection *collection, } LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) { - if (collection_instance_find_recursive(collection_child->collection, instance_collection)) { + if (collection_child->collection != NULL && + collection_instance_find_recursive(collection_child->collection, instance_collection)) { return true; } } -- cgit v1.2.3 From ce649c73446ea710ffdf7383495b52b0827a5893 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Mon, 31 May 2021 11:25:12 +0200 Subject: Fix T88623, T87044: Make encoded videos play correctly in VLC The issue was two fold. We didn't properly: 1. Initialize the codec default values which would lead to VLC complaining because of garbage/wrong codec settings. 2.Calculate the time base for the video. FFmpeg would happily accept this but VLC seems to assume the time base value is at least somewhat correct and couldn't properly display the frames as the internal time base was huge. We are talking about 90k ticks (tbn) for one second of video! This patch initializes all codecs to use their default values and fixes the time base calculation so it follows the guidelines from ffmpeg. Reviewed By: Sergey, Richard Antalik Differential Revision: http://developer.blender.org/D11426 --- source/blender/blenkernel/intern/writeffmpeg.c | 175 ++++++++++++++----------- source/blender/imbuf/intern/indexer.c | 14 +- 2 files changed, 103 insertions(+), 86 deletions(-) diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 3408f990fd1..7c8eba20a28 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -518,6 +518,48 @@ static void set_ffmpeg_properties(RenderData *rd, } } +static AVRational calc_time_base(uint den, double num, int codec_id) +{ + /* Convert the input 'num' to an integer. Simply shift the decimal places until we get an integer + * (within a floating point error range). + * For example if we have den = 3 and num = 0.1 then the fps is: den/num = 30 fps. + * When converthing this to a ffmpeg time base, we want num to be an integer. + * So we simply move the decimal places of both numbers. IE den = 30, num = 1.*/ + float eps = FLT_EPSILON; + const uint DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1; + + /* Calculate the precision of the initial floating point number. */ + if (num > 1.0) { + const uint num_integer_bits = log2_floor_u((unsigned int)num); + + /* Formula for calculating the epsilon value: (power of two range) / (pow mantissa bits) + * For example, a float has 23 manitissa bits and the float value 3.5f as a pow2 range of + * (4-2=2): + * (2) / pow2(23) = floating point precision for 3.5f + */ + eps = (float)(1 << num_integer_bits) * FLT_EPSILON; + } + + /* Calculate how many decimal shifts we can do until we run out of precision. */ + const int max_num_shift = fabsf(log10f(eps)); + /* Calculate how many times we can shift the denominator. */ + const int max_den_shift = log10f(DENUM_MAX) - log10f(den); + const int max_iter = min_ii(max_num_shift, max_den_shift); + + for (int i = 0; i < max_iter && fabs(num - round(num)) > eps; i++) { + /* Increase the number and denominator until both are integers. */ + num *= 10; + den *= 10; + eps *= 10; + } + + AVRational time_base; + time_base.den = den; + time_base.num = (int)num; + + return time_base; +} + /* prepare a video stream for the output file */ static AVStream *alloc_video_stream(FFMpegContext *context, @@ -548,13 +590,24 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->codec_id = codec_id; c->codec_type = AVMEDIA_TYPE_VIDEO; + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { + fprintf(stderr, "Couldn't find valid video codec\n"); + avcodec_free_context(&c); + context->video_codec = NULL; + return NULL; + } + + /* Load codec defaults into 'c'. */ + avcodec_get_context_defaults3(c, codec); + /* Get some values from the current render settings */ c->width = rectx; c->height = recty; - /* FIXME: Really bad hack (tm) for NTSC support */ if (context->ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) { + /* FIXME: Really bad hack (tm) for NTSC support */ c->time_base.den = 2997; c->time_base.num = 100; } @@ -562,21 +615,23 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->time_base.den = rd->frs_sec; c->time_base.num = (int)rd->frs_sec_base; } - else if (compare_ff(rd->frs_sec_base, 1.001f, 0.000001f)) { - /* This converts xx/1.001 (which is used in presets) to xx000/1001 (which is used in the rest - * of the world, including FFmpeg). */ - c->time_base.den = (int)(rd->frs_sec * 1000); - c->time_base.num = (int)(rd->frs_sec_base * 1000); - } else { - /* This calculates a fraction (DENUM_MAX / num) which approximates the scene frame rate - * (frs_sec / frs_sec_base). It uses the maximum denominator allowed by FFmpeg. - */ - const double DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1; - const double num = (DENUM_MAX / (double)rd->frs_sec) * rd->frs_sec_base; + c->time_base = calc_time_base(rd->frs_sec, rd->frs_sec_base, codec_id); + } - c->time_base.den = (int)DENUM_MAX; - c->time_base.num = (int)num; + /* As per the timebase documentation here: + * https://www.ffmpeg.org/ffmpeg-codecs.html#Codec-Options + * We want to set the time base to (1 / fps) for fixed frame rate video. + * If it is not possible, we want to set the timebase numbers to something as + * small as possible. + */ + if (c->time_base.num != 1) { + AVRational new_time_base; + if (av_reduce( + &new_time_base.num, &new_time_base.den, c->time_base.num, c->time_base.den, INT_MAX)) { + /* Exact reduction was possible. Use the new value. */ + c->time_base = new_time_base; + } } st->time_base = c->time_base; @@ -588,6 +643,11 @@ static AVStream *alloc_video_stream(FFMpegContext *context, ffmpeg_dict_set_int(&opts, "lossless", 1); } else if (context->ffmpeg_crf >= 0) { + /* As per https://trac.ffmpeg.org/wiki/Encode/VP9 we must set the bit rate to zero when + * encoding with vp9 in crf mode. + * Set this to always be zero for other codecs as well. + * We don't care about bit rate in crf mode. */ + c->bit_rate = 0; ffmpeg_dict_set_int(&opts, "crf", context->ffmpeg_crf); } else { @@ -627,12 +687,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } } - codec = avcodec_find_encoder(c->codec_id); - if (!codec) { - avcodec_free_context(&c); - return NULL; - } - /* Be sure to use the correct pixel format(e.g. RGB, YUV) */ if (codec->pix_fmts) { @@ -649,12 +703,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->codec_tag = (('D' << 24) + ('I' << 16) + ('V' << 8) + 'X'); } - if (codec_id == AV_CODEC_ID_H264) { - /* correct wrong default ffmpeg param which crash x264 */ - c->qmin = 10; - c->qmax = 51; - } - /* Keep lossless encodes in the RGB domain. */ if (codec_id == AV_CODEC_ID_HUFFYUV) { if (rd->im_format.planes == R_IMF_PLANES_RGBA) { @@ -714,10 +762,14 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->thread_type = FF_THREAD_SLICE; } - if (avcodec_open2(c, codec, &opts) < 0) { + int ret = avcodec_open2(c, codec, &opts); + + if (ret < 0) { + fprintf(stderr, "Couldn't initialize video codec: %s\n", av_err2str(ret)); BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size); av_dict_free(&opts); avcodec_free_context(&c); + context->video_codec = NULL; return NULL; } av_dict_free(&opts); @@ -777,6 +829,17 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, c->codec_id = codec_id; c->codec_type = AVMEDIA_TYPE_AUDIO; + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { + fprintf(stderr, "Couldn't find valid audio codec\n"); + avcodec_free_context(&c); + context->audio_codec = NULL; + return NULL; + } + + /* Load codec defaults into 'c'. */ + avcodec_get_context_defaults3(c, codec); + c->sample_rate = rd->ffcodecdata.audio_mixrate; c->bit_rate = context->ffmpeg_audio_bitrate * 1000; c->sample_fmt = AV_SAMPLE_FMT_S16; @@ -806,13 +869,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, c->sample_fmt = AV_SAMPLE_FMT_FLT; } - codec = avcodec_find_encoder(c->codec_id); - if (!codec) { - // XXX error("Couldn't find a valid audio codec"); - avcodec_free_context(&c); - return NULL; - } - if (codec->sample_fmts) { /* Check if the preferred sample format for this codec is supported. * this is because, depending on the version of libav, @@ -852,11 +908,14 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, set_ffmpeg_properties(rd, c, "audio", &opts); - if (avcodec_open2(c, codec, &opts) < 0) { - // XXX error("Couldn't initialize audio codec"); + int ret = avcodec_open2(c, codec, &opts); + + if (ret < 0) { + fprintf(stderr, "Couldn't initialize audio codec: %s\n", av_err2str(ret)); BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size); av_dict_free(&opts); avcodec_free_context(&c); + context->audio_codec = NULL; return NULL; } av_dict_free(&opts); @@ -1636,49 +1695,7 @@ static void ffmpeg_set_expert_options(RenderData *rd) IDP_FreePropertyContent(rd->ffcodecdata.properties); } - if (codec_id == AV_CODEC_ID_H264) { - /* - * All options here are for x264, but must be set via ffmpeg. - * The names are therefore different - Search for "x264 to FFmpeg option mapping" - * to get a list. - */ - - /* - * Use CABAC coder. Using "coder:1", which should be equivalent, - * crashes Blender for some reason. Either way - this is no big deal. - */ - BKE_ffmpeg_property_add_string(rd, "video", "coder:vlc"); - - /* - * The other options were taken from the libx264-default.preset - * included in the ffmpeg distribution. - */ - - /* This breaks compatibility for QT. */ - // BKE_ffmpeg_property_add_string(rd, "video", "flags:loop"); - BKE_ffmpeg_property_add_string(rd, "video", "cmp:chroma"); - BKE_ffmpeg_property_add_string(rd, "video", "partitions:parti4x4"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "partitions:partp8x8"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "partitions:partb8x8"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "me:hex"); - BKE_ffmpeg_property_add_string(rd, "video", "subq:6"); - BKE_ffmpeg_property_add_string(rd, "video", "me_range:16"); - BKE_ffmpeg_property_add_string(rd, "video", "qdiff:4"); - BKE_ffmpeg_property_add_string(rd, "video", "keyint_min:25"); - BKE_ffmpeg_property_add_string(rd, "video", "sc_threshold:40"); - BKE_ffmpeg_property_add_string(rd, "video", "i_qfactor:0.71"); - BKE_ffmpeg_property_add_string(rd, "video", "b_strategy:1"); - BKE_ffmpeg_property_add_string(rd, "video", "bf:3"); - BKE_ffmpeg_property_add_string(rd, "video", "refs:2"); - BKE_ffmpeg_property_add_string(rd, "video", "qcomp:0.6"); - - BKE_ffmpeg_property_add_string(rd, "video", "trellis:0"); - BKE_ffmpeg_property_add_string(rd, "video", "weightb:1"); - BKE_ffmpeg_property_add_string(rd, "video", "8x8dct:1"); - BKE_ffmpeg_property_add_string(rd, "video", "fast-pskip:1"); - BKE_ffmpeg_property_add_string(rd, "video", "wpredp:2"); - } - else if (codec_id == AV_CODEC_ID_DNXHD) { + if (codec_id == AV_CODEC_ID_DNXHD) { if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) { BKE_ffmpeg_property_add_string(rd, "video", "mbd:rd"); } diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 453df6078ce..e1e6cc677ed 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -492,13 +492,6 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( rv->c = avcodec_alloc_context3(NULL); rv->c->codec_type = AVMEDIA_TYPE_VIDEO; rv->c->codec_id = AV_CODEC_ID_H264; - rv->c->width = width; - rv->c->height = height; - rv->c->gop_size = 10; - rv->c->max_b_frames = 0; - /* Correct wrong default ffmpeg param which crash x264. */ - rv->c->qmin = 10; - rv->c->qmax = 51; rv->of->oformat->video_codec = rv->c->codec_id; rv->codec = avcodec_find_encoder(rv->c->codec_id); @@ -513,6 +506,13 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( return NULL; } + avcodec_get_context_defaults3(rv->c, rv->codec); + + rv->c->width = width; + rv->c->height = height; + rv->c->gop_size = 10; + rv->c->max_b_frames = 0; + if (rv->codec->pix_fmts) { rv->c->pix_fmt = rv->codec->pix_fmts[0]; } -- cgit v1.2.3 From e9f2f17e8518f31706756b4ebe7c38a3582a3612 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Mon, 31 May 2021 12:26:46 +0200 Subject: Fix (unreported): TextureOperation inputs have no resolution When compositor node tree has a texture node, TextureOperation vector inputs has always {0, 0} resolution instead of having same resolution as TextureOperation which is the expected behaviour for resolutions propagation. Current TextureOperation determineResolution implementation doesn't determine inputs resolution, breaking propagation of preferred resolution and that's the reason why they are always 0. Setting scene resolution always would mean it is its own resolution and could make sense, but setting it only when preferred resolution is 0, breaks preferred resolution logic affecting other operations as explained in D10972. In any case scene resolution is already the default preferred resolution on viewer and compositor nodes. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D11381 --- .../compositor/operations/COM_TextureOperation.cc | 17 +++++++---------- .../compositor/operations/COM_TextureOperation.h | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index e94c457f981..7517ff8a137 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -75,16 +75,13 @@ void TextureBaseOperation::deinitExecution() void TextureBaseOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { - if (preferredResolution[0] == 0 || preferredResolution[1] == 0) { - int width = this->m_rd->xsch * this->m_rd->size / 100; - int height = this->m_rd->ysch * this->m_rd->size / 100; - resolution[0] = width; - resolution[1] = height; - } - else { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; - } + /* Determine inputs resolutions. */ + unsigned int temp[2]; + NodeOperation::determineResolution(temp, preferredResolution); + + /* We don't use inputs resolutions because they are only used as parameters, not image data. */ + resolution[0] = preferredResolution[0]; + resolution[1] = preferredResolution[1]; } void TextureAlphaOperation::executePixelSampled(float output[4], diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h index e1e04611c6c..e5f56673694 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.h +++ b/source/blender/compositor/operations/COM_TextureOperation.h @@ -44,7 +44,7 @@ class TextureBaseOperation : public NodeOperation { protected: /** - * Determine the output resolution. The resolution is retrieved from the Renderer + * Determine the output resolution. */ void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; -- cgit v1.2.3 From e0a1c3da46ada31058471a0c40bcc8fd56929846 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 31 May 2021 14:32:39 +0200 Subject: Fix T88666: Cryptomatte: EXR sequence does not update when scrubbing the timeline. Cause is that initializing the cryptomatte session would reset the current frame of an image sequence. The solution is to always use the scene current frame so it resets to the correct frame. This was a todo that wasn't solved after it landed in master. Needs to be backported to 2.93. --- source/blender/blenkernel/BKE_node.h | 11 +- .../compositor/nodes/COM_CryptomatteNode.cc | 8 +- .../editors/interface/interface_eyedropper_color.c | 98 +++++++++------- source/blender/makesrna/intern/rna_nodetree.c | 6 +- .../composite/nodes/node_composite_cryptomatte.cc | 124 ++++++++++++--------- 5 files changed, 143 insertions(+), 104 deletions(-) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index b3247a751bf..fb6647cb68d 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1305,15 +1305,18 @@ void ntreeCompositOutputFileUniqueLayer(struct ListBase *list, void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node); void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node); -void ntreeCompositCryptomatteSyncFromAdd(bNode *node); +void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node); void ntreeCompositCryptomatteSyncFromRemove(bNode *node); bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node); int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node); -void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len); +void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, + const bNode *node, + char *r_prefix, + size_t prefix_len); /* Update the runtime layer names with the cryptomatte layer names of the references * render layer or image. */ -void ntreeCompositCryptomatteUpdateLayerNames(bNode *node); -struct CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node); +void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node); +struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node); /** \} */ diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index d97e4371dc6..3beb3aa2917 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -77,10 +77,10 @@ void CryptomatteBaseNode::convertToOperations(NodeConverter &converter, /** \name Cryptomatte V2 * \{ */ -static std::string prefix_from_node(const bNode &node) +static std::string prefix_from_node(const CompositorContext &context, const bNode &node) { char prefix[MAX_NAME]; - ntreeCompositCryptomatteLayerPrefix(&node, prefix, sizeof(prefix)); + ntreeCompositCryptomatteLayerPrefix(context.getScene(), &node, prefix, sizeof(prefix)); return std::string(prefix, BLI_strnlen(prefix, sizeof(prefix))); } @@ -119,7 +119,7 @@ void CryptomatteNode::input_operations_from_render_source( } const short cryptomatte_layer_id = 0; - const std::string prefix = prefix_from_node(node); + const std::string prefix = prefix_from_node(context, node); LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name); if (render_layer) { @@ -177,7 +177,7 @@ void CryptomatteNode::input_operations_from_image_source( } } - const std::string prefix = prefix_from_node(node); + const std::string prefix = prefix_from_node(context, node); int layer_index; LISTBASE_FOREACH_INDEX (RenderLayer *, render_layer, &image->rr->layers, layer_index) { if (!blender::StringRef(prefix).startswith(blender::StringRef( diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index d5fb0e4e744..dd8b1e6a23e 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -118,7 +118,8 @@ static bool eyedropper_init(bContext *C, wmOperator *op) RNA_property_float_get_array(&eye->ptr, eye->prop, col); if (eye->ptr.type == &RNA_CompositorNodeCryptomatteV2) { eye->crypto_node = (bNode *)eye->ptr.data; - eye->cryptomatte_session = ntreeCompositCryptomatteSession(eye->crypto_node); + eye->cryptomatte_session = ntreeCompositCryptomatteSession(CTX_data_scene(C), + eye->crypto_node); eye->draw_handle_sample_text = WM_draw_cb_activate(CTX_wm_window(C), eyedropper_draw_cb, eye); } @@ -199,6 +200,57 @@ static bool eyedropper_cryptomatte_sample_renderlayer_fl(RenderLayer *render_lay return false; } +static bool eyedropper_cryptomatte_sample_render_fl(const bNode *node, + const char *prefix, + const float fpos[2], + float r_col[3]) +{ + bool success = false; + Scene *scene = (Scene *)node->id; + BLI_assert(GS(scene->id.name) == ID_SCE); + Render *re = RE_GetSceneRender(scene); + + if (re) { + RenderResult *rr = RE_AcquireResultRead(re); + if (rr) { + LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { + RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name); + success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col); + if (success) { + break; + } + } + } + RE_ReleaseResult(re); + } + return success; +} + +static bool eyedropper_cryptomatte_sample_image_fl(const bNode *node, + NodeCryptomatte *crypto, + const char *prefix, + const float fpos[2], + float r_col[3]) +{ + bool success = false; + Image *image = (Image *)node->id; + BLI_assert(GS(image->id.name) == ID_IM); + ImageUser *iuser = &crypto->iuser; + + if (image && image->type == IMA_TYPE_MULTILAYER) { + ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL); + if (image->rr) { + LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) { + success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col); + if (success) { + break; + } + } + } + BKE_image_release_ibuf(image, ibuf, NULL); + } + return success; +} static bool eyedropper_cryptomatte_sample_fl( bContext *C, Eyedropper *eye, int mx, int my, float r_col[3]) @@ -255,53 +307,19 @@ static bool eyedropper_cryptomatte_sample_fl( return false; } - bool success = false; /* TODO(jbakker): Migrate this file to cc and use std::string as return param. */ char prefix[MAX_NAME + 1]; - ntreeCompositCryptomatteLayerPrefix(node, prefix, sizeof(prefix) - 1); + const Scene *scene = CTX_data_scene(C); + ntreeCompositCryptomatteLayerPrefix(scene, node, prefix, sizeof(prefix) - 1); prefix[MAX_NAME] = '\0'; if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) { - Scene *scene = (Scene *)node->id; - BLI_assert(GS(scene->id.name) == ID_SCE); - Render *re = RE_GetSceneRender(scene); - - if (re) { - RenderResult *rr = RE_AcquireResultRead(re); - if (rr) { - LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { - RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name); - success = eyedropper_cryptomatte_sample_renderlayer_fl( - render_layer, prefix, fpos, r_col); - if (success) { - break; - } - } - } - RE_ReleaseResult(re); - } + return eyedropper_cryptomatte_sample_render_fl(node, prefix, fpos, r_col); } else if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) { - Image *image = (Image *)node->id; - BLI_assert(GS(image->id.name) == ID_IM); - ImageUser *iuser = &crypto->iuser; - - if (image && image->type == IMA_TYPE_MULTILAYER) { - ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL); - if (image->rr) { - LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) { - success = eyedropper_cryptomatte_sample_renderlayer_fl( - render_layer, prefix, fpos, r_col); - if (success) { - break; - } - } - } - BKE_image_release_ibuf(image, ibuf, NULL); - } + return eyedropper_cryptomatte_sample_image_fl(node, crypto, prefix, fpos, r_col); } - - return success; + return false; } /** diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b3ba111dc61..0ca275eb58b 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -3973,7 +3973,7 @@ static void rna_NodeCryptomatte_layer_name_set(PointerRNA *ptr, int new_value) } } -static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *UNUSED(C), +static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) @@ -3984,7 +3984,7 @@ static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *UN EnumPropertyItem template = {0, "", 0, "", ""}; int totitem = 0; - ntreeCompositCryptomatteUpdateLayerNames(node); + ntreeCompositCryptomatteUpdateLayerNames(CTX_data_scene(C), node); int layer_index; LISTBASE_FOREACH_INDEX (CryptomatteLayer *, layer, &storage->runtime.layers, layer_index) { template.value = layer_index; @@ -4076,7 +4076,7 @@ static void rna_NodeCryptomatte_matte_set(PointerRNA *ptr, const char *value) static void rna_NodeCryptomatte_update_add(Main *bmain, Scene *scene, PointerRNA *ptr) { - ntreeCompositCryptomatteSyncFromAdd(ptr->data); + ntreeCompositCryptomatteSyncFromAdd(scene, ptr->data); rna_Node_update(bmain, scene, ptr); } diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index d9b36924516..dca6dc59ca2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -40,61 +40,74 @@ /** \name Cryptomatte * \{ */ +static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_render( + const bNode &node, const bool use_meta_data) +{ + blender::bke::cryptomatte::CryptomatteSessionPtr session; + + Scene *scene = (Scene *)node.id; + if (!scene) { + return session; + } + BLI_assert(GS(scene->id.name) == ID_SCE); + + if (use_meta_data) { + Render *render = (scene) ? RE_GetSceneRender(scene) : nullptr; + RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr; + if (render_result) { + session = blender::bke::cryptomatte::CryptomatteSessionPtr( + BKE_cryptomatte_init_from_render_result(render_result)); + } + if (render) { + RE_ReleaseResult(render); + } + } + + if (session == nullptr) { + session = blender::bke::cryptomatte::CryptomatteSessionPtr( + BKE_cryptomatte_init_from_scene(scene)); + } + return session; +} + +static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_image( + const Scene &scene, const bNode &node) +{ + blender::bke::cryptomatte::CryptomatteSessionPtr session; + Image *image = (Image *)node.id; + if (!image) { + return session; + } + BLI_assert(GS(image->id.name) == ID_IM); + + NodeCryptomatte *node_cryptomatte = static_cast(node.storage); + ImageUser *iuser = &node_cryptomatte->iuser; + BKE_image_user_frame_calc(image, iuser, scene.r.cfra); + ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr); + RenderResult *render_result = image->rr; + if (render_result) { + session = blender::bke::cryptomatte::CryptomatteSessionPtr( + BKE_cryptomatte_init_from_render_result(render_result)); + } + BKE_image_release_ibuf(image, ibuf, nullptr); + return session; +} static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node( - const bNode &node, const int frame_number, const bool use_meta_data) + const Scene &scene, const bNode &node, const bool use_meta_data) { blender::bke::cryptomatte::CryptomatteSessionPtr session; if (node.type != CMP_NODE_CRYPTOMATTE) { return session; } - NodeCryptomatte *node_cryptomatte = static_cast(node.storage); switch (node.custom1) { case CMP_CRYPTOMATTE_SRC_RENDER: { - Scene *scene = (Scene *)node.id; - if (!scene) { - return session; - } - BLI_assert(GS(scene->id.name) == ID_SCE); - - if (use_meta_data) { - Render *render = (scene) ? RE_GetSceneRender(scene) : nullptr; - RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr; - if (render_result) { - session = blender::bke::cryptomatte::CryptomatteSessionPtr( - BKE_cryptomatte_init_from_render_result(render_result)); - } - if (render) { - RE_ReleaseResult(render); - } - } - - if (session == nullptr) { - session = blender::bke::cryptomatte::CryptomatteSessionPtr( - BKE_cryptomatte_init_from_scene(scene)); - } - - break; + return cryptomatte_init_from_node_render(node, use_meta_data); } case CMP_CRYPTOMATTE_SRC_IMAGE: { - Image *image = (Image *)node.id; - if (!image) { - break; - } - BLI_assert(GS(image->id.name) == ID_IM); - - ImageUser *iuser = &node_cryptomatte->iuser; - BKE_image_user_frame_calc(image, iuser, frame_number); - ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr); - RenderResult *render_result = image->rr; - if (render_result) { - session = blender::bke::cryptomatte::CryptomatteSessionPtr( - BKE_cryptomatte_init_from_render_result(render_result)); - } - BKE_image_release_ibuf(image, ibuf, nullptr); - break; + return cryptomatte_init_from_node_image(scene, node); } } return session; @@ -111,7 +124,10 @@ static CryptomatteEntry *cryptomatte_find(const NodeCryptomatte &n, float encode return nullptr; } -static void cryptomatte_add(bNode &node, NodeCryptomatte &node_cryptomatte, float encoded_hash) +static void cryptomatte_add(const Scene &scene, + bNode &node, + NodeCryptomatte &node_cryptomatte, + float encoded_hash) { /* Check if entry already exist. */ if (cryptomatte_find(node_cryptomatte, encoded_hash)) { @@ -121,9 +137,8 @@ static void cryptomatte_add(bNode &node, NodeCryptomatte &node_cryptomatte, floa CryptomatteEntry *entry = static_cast( MEM_callocN(sizeof(CryptomatteEntry), __func__)); entry->encoded_hash = encoded_hash; - /* TODO(jbakker): Get current frame from scene. */ blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node( - node, 0, true); + scene, node, true); if (session) { BKE_cryptomatte_find_name(session.get(), encoded_hash, entry->name, sizeof(entry->name)); } @@ -151,12 +166,12 @@ static bNodeSocketTemplate cmp_node_cryptomatte_out[] = { {-1, ""}, }; -void ntreeCompositCryptomatteSyncFromAdd(bNode *node) +void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node) { BLI_assert(ELEM(node->type, CMP_NODE_CRYPTOMATTE, CMP_NODE_CRYPTOMATTE_LEGACY)); NodeCryptomatte *n = static_cast(node->storage); if (n->runtime.add[0] != 0.0f) { - cryptomatte_add(*node, *n, n->runtime.add[0]); + cryptomatte_add(*scene, *node, *n, n->runtime.add[0]); zero_v3(n->runtime.add); } } @@ -170,14 +185,14 @@ void ntreeCompositCryptomatteSyncFromRemove(bNode *node) zero_v3(n->runtime.remove); } } -void ntreeCompositCryptomatteUpdateLayerNames(bNode *node) +void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node) { BLI_assert(node->type == CMP_NODE_CRYPTOMATTE); NodeCryptomatte *n = static_cast(node->storage); BLI_freelistN(&n->runtime.layers); blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node( - *node, 0, false); + *scene, *node, false); if (session) { for (blender::StringRef layer_name : @@ -190,12 +205,15 @@ void ntreeCompositCryptomatteUpdateLayerNames(bNode *node) } } -void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len) +void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, + const bNode *node, + char *r_prefix, + size_t prefix_len) { BLI_assert(node->type == CMP_NODE_CRYPTOMATTE); NodeCryptomatte *node_cryptomatte = (NodeCryptomatte *)node->storage; blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node( - *node, 0, false); + *scene, *node, false); std::string first_layer_name; if (session) { @@ -216,10 +234,10 @@ void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size BLI_strncpy(r_prefix, cstr, prefix_len); } -CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node) +CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node) { blender::bke::cryptomatte::CryptomatteSessionPtr session_ptr = cryptomatte_init_from_node( - *node, 0, true); + *scene, *node, true); return session_ptr.release(); } -- cgit v1.2.3 From d94ba979d890e0a89a3ff9fa5ac8676f389533b5 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 31 May 2021 13:17:23 +0200 Subject: Fix T88569: UI VSE: Menu-based range change, doesn't update the Timeline scrollbar width Use the appropriate notifier, listeners were already doing the rest properly. Maniphest Tasks: T88569 Differential Revision: https://developer.blender.org/D11436 --- source/blender/editors/space_sequencer/sequencer_edit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index ebd4c0090b4..53ddc818cd1 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -3053,7 +3053,7 @@ static int sequencer_set_range_to_strips_exec(bContext *C, wmOperator *op) scene->r.efra = efra; } - WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); + WM_event_add_notifier(C, NC_SCENE | ND_FRAME_RANGE, scene); return OPERATOR_FINISHED; } -- cgit v1.2.3 From d67c13ca761a865a208d6c9ad4ee2504aa577e94 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 31 May 2021 15:15:37 +0200 Subject: Cleanup: else-after-return --- source/blender/editors/interface/interface_eyedropper_color.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index dd8b1e6a23e..6583fa31dbf 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -316,7 +316,7 @@ static bool eyedropper_cryptomatte_sample_fl( if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) { return eyedropper_cryptomatte_sample_render_fl(node, prefix, fpos, r_col); } - else if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) { + if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) { return eyedropper_cryptomatte_sample_image_fl(node, crypto, prefix, fpos, r_col); } return false; -- cgit v1.2.3 From aebeb85fe013a671f71f47d6f210c192598f408f Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Mon, 31 May 2021 08:56:57 -0600 Subject: Windows: Clean-up win 8/8.1 API use For 2.93 we bumped the minimum windows requirement to windows 8.1, but did not do any clean-up of any win 8/8.1 API usage we dynamically accessed though LoadLibrary/GetProcAddress. This patch bumps _WIN32_WINNT to 0x0603 (win 8.1) and cleans up any API use that was accessed in a more convoluted way than necessary Differential Revision: https://developer.blender.org/D11331 Reviewed by: harley, nicholas_rishel --- build_files/cmake/platform/platform_win32.cmake | 6 +- intern/ghost/intern/GHOST_SystemWin32.cpp | 73 +--------- intern/ghost/intern/GHOST_WindowWin32.cpp | 35 +---- intern/ghost/intern/GHOST_WindowWin32.h | 168 ----------------------- release/windows/manifest/blender.exe.manifest.in | 6 - 5 files changed, 9 insertions(+), 279 deletions(-) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index b6ec016aa95..497fd179aaf 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -119,7 +119,7 @@ string(APPEND CMAKE_MODULE_LINKER_FLAGS " /SAFESEH:NO /ignore:4099") list(APPEND PLATFORM_LINKLIBS ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 Comctl32 version advapi32 shfolder shell32 ole32 oleaut32 uuid psapi Dbghelp Shlwapi - pathcch + pathcch Shcore ) if(WITH_INPUT_IME) @@ -144,8 +144,8 @@ add_definitions(-D_ALLOW_KEYWORD_MACROS) # that both /GR and /GR- are specified. remove_cc_flag("/GR") -# We want to support Windows 7 level ABI -add_definitions(-D_WIN32_WINNT=0x601) +# Make the Windows 8.1 API available for use. +add_definitions(-D_WIN32_WINNT=0x603) include(build_files/cmake/platform/platform_win32_bundle_crt.cmake) remove_cc_flag("/MDd" "/MD" "/Zi") diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 38dea9c1142..fc3cb81e26a 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -97,41 +98,6 @@ # define VK_GR_LESS 0xE2 #endif // VK_GR_LESS -#ifndef VK_MEDIA_NEXT_TRACK -# define VK_MEDIA_NEXT_TRACK 0xB0 -#endif // VK_MEDIA_NEXT_TRACK -#ifndef VK_MEDIA_PREV_TRACK -# define VK_MEDIA_PREV_TRACK 0xB1 -#endif // VK_MEDIA_PREV_TRACK -#ifndef VK_MEDIA_STOP -# define VK_MEDIA_STOP 0xB2 -#endif // VK_MEDIA_STOP -#ifndef VK_MEDIA_PLAY_PAUSE -# define VK_MEDIA_PLAY_PAUSE 0xB3 -#endif // VK_MEDIA_PLAY_PAUSE - -// Window message newer than Windows 7 -#ifndef WM_DPICHANGED -# define WM_DPICHANGED 0x02E0 -#endif // WM_DPICHANGED - -// WM_POINTER API messages minimum Windows 7 -#ifndef WM_POINTERENTER -# define WM_POINTERENTER 0x0249 -#endif // WM_POINTERENTER -#ifndef WM_POINTERDOWN -# define WM_POINTERDOWN 0x0246 -#endif // WM_POINTERDOWN -#ifndef WM_POINTERUPDATE -# define WM_POINTERUPDATE 0x0245 -#endif // WM_POINTERUPDATE -#ifndef WM_POINTERUP -# define WM_POINTERUP 0x0247 -#endif // WM_POINTERUP -#ifndef WM_POINTERLEAVE -# define WM_POINTERLEAVE 0x024A -#endif // WM_POINTERLEAVE - /* Workaround for some laptop touchpads, some of which seems to * have driver issues which makes it so window function receives * the message, but PeekMessage doesn't pick those messages for @@ -173,24 +139,6 @@ static void initRawInput() #undef DEVICE_COUNT } -#ifndef DPI_ENUMS_DECLARED -typedef enum PROCESS_DPI_AWARENESS { - PROCESS_DPI_UNAWARE = 0, - PROCESS_SYSTEM_DPI_AWARE = 1, - PROCESS_PER_MONITOR_DPI_AWARE = 2 -} PROCESS_DPI_AWARENESS; - -typedef enum MONITOR_DPI_TYPE { - MDT_EFFECTIVE_DPI = 0, - MDT_ANGULAR_DPI = 1, - MDT_RAW_DPI = 2, - MDT_DEFAULT = MDT_EFFECTIVE_DPI -} MONITOR_DPI_TYPE; - -# define USER_DEFAULT_SCREEN_DPI 96 - -# define DPI_ENUMS_DECLARED -#endif typedef HRESULT(API *GHOST_WIN32_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); typedef BOOL(API *GHOST_WIN32_EnableNonClientDpiScaling)(HWND); @@ -205,15 +153,7 @@ GHOST_SystemWin32::GHOST_SystemWin32() // Tell Windows we are per monitor DPI aware. This disables the default // blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI. - HMODULE m_shcore = ::LoadLibrary("Shcore.dll"); - if (m_shcore) { - GHOST_WIN32_SetProcessDpiAwareness fpSetProcessDpiAwareness = - (GHOST_WIN32_SetProcessDpiAwareness)::GetProcAddress(m_shcore, "SetProcessDpiAwareness"); - - if (fpSetProcessDpiAwareness) { - fpSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); - } - } + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); // Check if current keyboard layout uses AltGr and save keylayout ID for // specialized handling if keys like VK_OEM_*. I.e. french keylayout @@ -581,14 +521,7 @@ GHOST_TSuccess GHOST_SystemWin32::init() InitCommonControls(); /* Disable scaling on high DPI displays on Vista */ - HMODULE - user32 = ::LoadLibraryA("user32.dll"); - typedef BOOL(WINAPI * LPFNSETPROCESSDPIAWARE)(); - LPFNSETPROCESSDPIAWARE SetProcessDPIAware = (LPFNSETPROCESSDPIAWARE)GetProcAddress( - user32, "SetProcessDPIAware"); - if (SetProcessDPIAware) - SetProcessDPIAware(); - FreeLibrary(user32); + SetProcessDPIAware(); initRawInput(); m_lfstart = ::GetTickCount(); diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 3af92153e8b..eeafe333633 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -84,9 +84,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_wantAlphaBackground(alphaBackground), m_normal_state(GHOST_kWindowStateNormal), m_user32(NULL), - m_fpGetPointerInfoHistory(NULL), - m_fpGetPointerPenInfoHistory(NULL), - m_fpGetPointerTouchInfoHistory(NULL), m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP), m_debug_context(is_debug) { @@ -153,19 +150,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_user32 = ::LoadLibrary("user32.dll"); if (m_hWnd) { - if (m_user32) { - // Touch enabled screens with pen support by default have gestures - // enabled, which results in a delay between the pointer down event - // and the first move when using the stylus. RegisterTouchWindow - // disables the new gesture architecture enabling the events to be - // sent immediately to the application rather than being absorbed by - // the gesture API. - GHOST_WIN32_RegisterTouchWindow pRegisterTouchWindow = (GHOST_WIN32_RegisterTouchWindow) - GetProcAddress(m_user32, "RegisterTouchWindow"); - if (pRegisterTouchWindow) { - pRegisterTouchWindow(m_hWnd, 0); - } - } + RegisterTouchWindow(m_hWnd, 0); // Register this window as a droptarget. Requires m_hWnd to be valid. // Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32. @@ -232,16 +217,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, } } - // Initialize Windows Ink - if (m_user32) { - m_fpGetPointerInfoHistory = (GHOST_WIN32_GetPointerInfoHistory)::GetProcAddress( - m_user32, "GetPointerInfoHistory"); - m_fpGetPointerPenInfoHistory = (GHOST_WIN32_GetPointerPenInfoHistory)::GetProcAddress( - m_user32, "GetPointerPenInfoHistory"); - m_fpGetPointerTouchInfoHistory = (GHOST_WIN32_GetPointerTouchInfoHistory)::GetProcAddress( - m_user32, "GetPointerTouchInfoHistory"); - } - // Initialize Wintab m_wintab.handle = ::LoadLibrary("Wintab32.dll"); if (m_wintab.handle && m_system->getTabletAPI() != GHOST_kTabletNative) { @@ -326,9 +301,6 @@ GHOST_WindowWin32::~GHOST_WindowWin32() if (m_user32) { FreeLibrary(m_user32); m_user32 = NULL; - m_fpGetPointerInfoHistory = NULL; - m_fpGetPointerPenInfoHistory = NULL; - m_fpGetPointerTouchInfoHistory = NULL; } if (m_customCursor) { @@ -950,15 +922,14 @@ GHOST_TSuccess GHOST_WindowWin32::getPointerInfo( GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem(); GHOST_TUns32 outCount; - if (!(m_fpGetPointerInfoHistory && m_fpGetPointerInfoHistory(pointerId, &outCount, NULL))) { + if (!(GetPointerInfoHistory(pointerId, &outCount, NULL))) { return GHOST_kFailure; } auto pointerPenInfo = std::vector(outCount); outPointerInfo.resize(outCount); - if (!(m_fpGetPointerPenInfoHistory && - m_fpGetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) { + if (!(GetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) { return GHOST_kFailure; } diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index b004f7e7b19..a13bd876667 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -53,177 +53,12 @@ typedef BOOL(API *GHOST_WIN32_WTPacket)(HCTX, UINT, LPVOID); typedef BOOL(API *GHOST_WIN32_WTEnable)(HCTX, BOOL); typedef BOOL(API *GHOST_WIN32_WTOverlap)(HCTX, BOOL); -// typedef to user32 functions to disable gestures on windows -typedef BOOL(API *GHOST_WIN32_RegisterTouchWindow)(HWND hwnd, ULONG ulFlags); - // typedefs for user32 functions to allow dynamic loading of Windows 10 DPI scaling functions typedef UINT(API *GHOST_WIN32_GetDpiForWindow)(HWND); #ifndef USER_DEFAULT_SCREEN_DPI # define USER_DEFAULT_SCREEN_DPI 96 #endif // USER_DEFAULT_SCREEN_DPI -// typedefs for user32 functions to allow pointer functions -enum tagPOINTER_INPUT_TYPE { - PT_POINTER = 1, // Generic pointer - PT_TOUCH = 2, // Touch - PT_PEN = 3, // Pen - PT_MOUSE = 4, // Mouse -#if (WINVER >= 0x0603) - PT_TOUCHPAD = 5, // Touchpad -#endif /* WINVER >= 0x0603 */ -}; - -typedef enum tagPOINTER_BUTTON_CHANGE_TYPE { - POINTER_CHANGE_NONE, - POINTER_CHANGE_FIRSTBUTTON_DOWN, - POINTER_CHANGE_FIRSTBUTTON_UP, - POINTER_CHANGE_SECONDBUTTON_DOWN, - POINTER_CHANGE_SECONDBUTTON_UP, - POINTER_CHANGE_THIRDBUTTON_DOWN, - POINTER_CHANGE_THIRDBUTTON_UP, - POINTER_CHANGE_FOURTHBUTTON_DOWN, - POINTER_CHANGE_FOURTHBUTTON_UP, - POINTER_CHANGE_FIFTHBUTTON_DOWN, - POINTER_CHANGE_FIFTHBUTTON_UP, -} POINTER_BUTTON_CHANGE_TYPE; - -typedef DWORD POINTER_INPUT_TYPE; -typedef UINT32 POINTER_FLAGS; - -#define POINTER_FLAG_NONE 0x00000000 -#define POINTER_FLAG_NEW 0x00000001 -#define POINTER_FLAG_INRANGE 0x00000002 -#define POINTER_FLAG_INCONTACT 0x00000004 -#define POINTER_FLAG_FIRSTBUTTON 0x00000010 -#define POINTER_FLAG_SECONDBUTTON 0x00000020 -#define POINTER_FLAG_THIRDBUTTON 0x00000040 -#define POINTER_FLAG_FOURTHBUTTON 0x00000080 -#define POINTER_FLAG_FIFTHBUTTON 0x00000100 -#define POINTER_FLAG_PRIMARY 0x00002000 -#define POINTER_FLAG_CONFIDENCE 0x000004000 -#define POINTER_FLAG_CANCELED 0x000008000 -#define POINTER_FLAG_DOWN 0x00010000 -#define POINTER_FLAG_UPDATE 0x00020000 -#define POINTER_FLAG_UP 0x00040000 -#define POINTER_FLAG_WHEEL 0x00080000 -#define POINTER_FLAG_HWHEEL 0x00100000 -#define POINTER_FLAG_CAPTURECHANGED 0x00200000 -#define POINTER_FLAG_HASTRANSFORM 0x00400000 - -typedef struct tagPOINTER_INFO { - POINTER_INPUT_TYPE pointerType; - UINT32 pointerId; - UINT32 frameId; - POINTER_FLAGS pointerFlags; - HANDLE sourceDevice; - HWND hwndTarget; - POINT ptPixelLocation; - POINT ptHimetricLocation; - POINT ptPixelLocationRaw; - POINT ptHimetricLocationRaw; - DWORD dwTime; - UINT32 historyCount; - INT32 InputData; - DWORD dwKeyStates; - UINT64 PerformanceCount; - POINTER_BUTTON_CHANGE_TYPE ButtonChangeType; -} POINTER_INFO; - -typedef UINT32 PEN_FLAGS; -#define PEN_FLAG_NONE 0x00000000 // Default -#define PEN_FLAG_BARREL 0x00000001 // The barrel button is pressed -#define PEN_FLAG_INVERTED 0x00000002 // The pen is inverted -#define PEN_FLAG_ERASER 0x00000004 // The eraser button is pressed - -typedef UINT32 PEN_MASK; -#define PEN_MASK_NONE 0x00000000 // Default - none of the optional fields are valid -#define PEN_MASK_PRESSURE 0x00000001 // The pressure field is valid -#define PEN_MASK_ROTATION 0x00000002 // The rotation field is valid -#define PEN_MASK_TILT_X 0x00000004 // The tiltX field is valid -#define PEN_MASK_TILT_Y 0x00000008 // The tiltY field is valid - -typedef struct tagPOINTER_PEN_INFO { - POINTER_INFO pointerInfo; - PEN_FLAGS penFlags; - PEN_MASK penMask; - UINT32 pressure; - UINT32 rotation; - INT32 tiltX; - INT32 tiltY; -} POINTER_PEN_INFO; - -/* - * Flags that appear in pointer input message parameters - */ -#define POINTER_MESSAGE_FLAG_NEW 0x00000001 // New pointer -#define POINTER_MESSAGE_FLAG_INRANGE 0x00000002 // Pointer has not departed -#define POINTER_MESSAGE_FLAG_INCONTACT 0x00000004 // Pointer is in contact -#define POINTER_MESSAGE_FLAG_FIRSTBUTTON 0x00000010 // Primary action -#define POINTER_MESSAGE_FLAG_SECONDBUTTON 0x00000020 // Secondary action -#define POINTER_MESSAGE_FLAG_THIRDBUTTON 0x00000040 // Third button -#define POINTER_MESSAGE_FLAG_FOURTHBUTTON 0x00000080 // Fourth button -#define POINTER_MESSAGE_FLAG_FIFTHBUTTON 0x00000100 // Fifth button -#define POINTER_MESSAGE_FLAG_PRIMARY 0x00002000 // Pointer is primary -#define POINTER_MESSAGE_FLAG_CONFIDENCE \ - 0x00004000 // Pointer is considered unlikely to be accidental -#define POINTER_MESSAGE_FLAG_CANCELED 0x00008000 // Pointer is departing in an abnormal manner - -typedef UINT32 TOUCH_FLAGS; -#define TOUCH_FLAG_NONE 0x00000000 // Default - -typedef UINT32 TOUCH_MASK; -#define TOUCH_MASK_NONE 0x00000000 // Default - none of the optional fields are valid -#define TOUCH_MASK_CONTACTAREA 0x00000001 // The rcContact field is valid -#define TOUCH_MASK_ORIENTATION 0x00000002 // The orientation field is valid -#define TOUCH_MASK_PRESSURE 0x00000004 // The pressure field is valid - -typedef struct tagPOINTER_TOUCH_INFO { - POINTER_INFO pointerInfo; - TOUCH_FLAGS touchFlags; - TOUCH_MASK touchMask; - RECT rcContact; - RECT rcContactRaw; - UINT32 orientation; - UINT32 pressure; -} POINTER_TOUCH_INFO; - -/* - * Macros to retrieve information from pointer input message parameters - */ -#define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam)) -#define IS_POINTER_FLAG_SET_WPARAM(wParam, flag) (((DWORD)HIWORD(wParam) & (flag)) == (flag)) -#define IS_POINTER_NEW_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_NEW) -#define IS_POINTER_INRANGE_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INRANGE) -#define IS_POINTER_INCONTACT_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INCONTACT) -#define IS_POINTER_FIRSTBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIRSTBUTTON) -#define IS_POINTER_SECONDBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_SECONDBUTTON) -#define IS_POINTER_THIRDBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_THIRDBUTTON) -#define IS_POINTER_FOURTHBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FOURTHBUTTON) -#define IS_POINTER_FIFTHBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIFTHBUTTON) -#define IS_POINTER_PRIMARY_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_PRIMARY) -#define HAS_POINTER_CONFIDENCE_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CONFIDENCE) -#define IS_POINTER_CANCELED_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CANCELED) - -typedef BOOL(WINAPI *GHOST_WIN32_GetPointerInfoHistory)(UINT32 pointerId, - UINT32 *entriesCount, - POINTER_INFO *pointerInfo); -typedef BOOL(WINAPI *GHOST_WIN32_GetPointerPenInfoHistory)(UINT32 pointerId, - UINT32 *entriesCount, - POINTER_PEN_INFO *penInfo); -typedef BOOL(WINAPI *GHOST_WIN32_GetPointerTouchInfoHistory)(UINT32 pointerId, - UINT32 *entriesCount, - POINTER_TOUCH_INFO *touchInfo); - struct GHOST_PointerInfoWin32 { GHOST_TInt32 pointerId; GHOST_TInt32 isPrimary; @@ -576,9 +411,6 @@ class GHOST_WindowWin32 : public GHOST_Window { /** `user32.dll` handle */ HMODULE m_user32; - GHOST_WIN32_GetPointerInfoHistory m_fpGetPointerInfoHistory; - GHOST_WIN32_GetPointerPenInfoHistory m_fpGetPointerPenInfoHistory; - GHOST_WIN32_GetPointerTouchInfoHistory m_fpGetPointerTouchInfoHistory; HWND m_parentWindowHwnd; diff --git a/release/windows/manifest/blender.exe.manifest.in b/release/windows/manifest/blender.exe.manifest.in index e73ddf3267b..b516efe24cb 100644 --- a/release/windows/manifest/blender.exe.manifest.in +++ b/release/windows/manifest/blender.exe.manifest.in @@ -13,12 +13,6 @@ - - - - - - -- cgit v1.2.3 From 44d2479dc36fe3409afc660eea369bea8a517987 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 31 May 2021 17:11:01 +0200 Subject: Refactor: DRW Mesh Extractor: Join the extractors in a same loop This patch replaces / redoes the entire MeshExtractors system. Although they were useful and facilitated the addition of new buffers, they made it difficult to control the threads and added a lot of threading overhead. Part of the problem was in traversing the same loop type in different threads. The concurrent access of the BMesh Elements slowed the reading. This patch simplifies the use of threads by merging all the old callbacks from the extracts into a single series of iteration functions. The type of extraction can be chosen using flags. This optimized the process by around 34%. Initial idea and implementation By @mano-wii. Fine-tuning, cleanup by @atmind. MASTER: large_mesh_editing: - rdata 9ms iter 50ms (frame 155ms) - Average: 6.462874 FPS PATCH: large_mesh_editing: - rdata 9ms iter 34ms (frame 136ms) - Average: 7.379491 FPS Differential Revision: https://developer.blender.org/D11425 --- source/blender/draw/intern/draw_cache_extract.h | 2 +- .../blender/draw/intern/draw_cache_extract_mesh.c | 2756 +++++++++++--------- .../blender/draw/intern/draw_cache_impl_curve.cc | 6 + .../blender/draw/intern/draw_cache_impl_displist.c | 4 + source/blender/draw/intern/draw_cache_impl_mesh.c | 6 +- source/blender/draw/intern/draw_cache_inline.h | 4 - 6 files changed, 1527 insertions(+), 1251 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 14cbba82fba..e6b7fb9ddf5 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -286,7 +286,7 @@ typedef struct MeshBatchCache { void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, - MeshBufferCache mbc, + MeshBufferCache *mbc, MeshBufferExtractionCache *extraction_cache, Mesh *me, const bool is_editmode, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index 62d8040e88b..1a78ce71d74 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -78,6 +78,13 @@ # include "PIL_time_utildefines.h" #endif +#define CHUNK_SIZE 8192 + +/* + * Max number of extractions types. + */ +#define M_EXTRACT_LEN 38 + /* ---------------------------------------------------------------------- */ /** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). * \{ */ @@ -528,7 +535,8 @@ typedef struct ExtractTriBMesh_Params { int tri_range[2]; } ExtractTriBMesh_Params; typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr, - const ExtractTriBMesh_Params *params, + BMLoop **elt, + const int elt_index, void *data); #define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elem_tri, index_tri, params) \ @@ -545,7 +553,8 @@ typedef struct ExtractTriMesh_Params { int tri_range[2]; } ExtractTriMesh_Params; typedef void(ExtractTriMeshFn)(const MeshRenderData *mr, - const ExtractTriMesh_Params *params, + const MLoopTri *mlt, + const int elt_index, void *data); #define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(elem_tri, index_tri, params) \ @@ -568,7 +577,8 @@ typedef struct ExtractPolyBMesh_Params { int poly_range[2]; } ExtractPolyBMesh_Params; typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int f_index, void *data); #define EXTRACT_POLY_FOREACH_BM_BEGIN(elem_poly, index_poly, params, mr) \ @@ -613,7 +623,8 @@ typedef struct ExtractPolyMesh_Params { int poly_range[2]; } ExtractPolyMesh_Params; typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data); #define EXTRACT_POLY_FOREACH_MESH_BEGIN(elem_poly, index_poly, params, mr) \ @@ -662,7 +673,8 @@ typedef struct ExtractLEdgeBMesh_Params { int ledge_range[2]; } ExtractLEdgeBMesh_Params; typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *data); #define EXTRACT_LEDGE_FOREACH_BM_BEGIN(elem_edge, index_ledge, params) \ @@ -687,7 +699,8 @@ typedef struct ExtractLEdgeMesh_Params { int ledge_range[2]; } ExtractLEdgeMesh_Params; typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *data); #define EXTRACT_LEDGE_FOREACH_MESH_BEGIN(elem_edge, index_ledge, params, mr) \ @@ -717,7 +730,8 @@ typedef struct ExtractLVertBMesh_Params { int lvert_range[2]; } ExtractLVertBMesh_Params; typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *data); #define EXTRACT_LVERT_FOREACH_BM_BEGIN(elem_vert, index_lvert, params) \ @@ -742,7 +756,8 @@ typedef struct ExtractLVertMesh_Params { int lvert_range[2]; } ExtractLVertMesh_Params; typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *mv, + const int lvert_index, void *data); #define EXTRACT_LVERT_FOREACH_MESH_BEGIN(elem, index_lvert, params, mr) \ @@ -793,8 +808,98 @@ typedef struct MeshExtract { const eMRDataType data_flag; /** Used to know if the element callbacks are thread-safe and can be parallelized. */ const bool use_threading; + /** + * Offset in bytes of the buffer inside a MeshBufferCache instance. Points to a vertex or index + * buffer. + */ + const size_t mesh_buffer_offset; } MeshExtract; +static void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc) +{ + /* NOTE: POINTER_OFFSET on windows platforms casts internally to `void *`, but on GCC/CLANG to + * `MeshBufferCache *`. What shows a different usage versus intent. */ + void **buffer_ptr = (void **)POINTER_OFFSET(mbc, extractor->mesh_buffer_offset); + void *buffer = *buffer_ptr; + BLI_assert(buffer); + return buffer; +} + +typedef struct MeshExtractRunData { + const MeshExtract *extractor; + void *buffer; + void *user_data; +} MeshExtractRunData; + +typedef struct MeshExtractRunDataArray { + int len; + MeshExtractRunData items[M_EXTRACT_LEN]; +} MeshExtractRunDataArray; + +static void mesh_extract_run_data_array_init(MeshExtractRunDataArray *array) +{ + array->len = 0; +} + +static void mesh_extract_run_data_array_add_ex(MeshExtractRunDataArray *array, + const MeshExtractRunData *run_data) +{ + array->items[array->len] = *run_data; + array->len++; +} + +static void mesh_extract_run_data_array_add(MeshExtractRunDataArray *array, + const MeshExtract *extractor) +{ + MeshExtractRunData run_data; + run_data.extractor = extractor; + run_data.buffer = NULL; + run_data.user_data = NULL; + mesh_extract_run_data_array_add_ex(array, &run_data); +} + +static void mesh_extract_run_data_array_filter_iter_type(const MeshExtractRunDataArray *src, + MeshExtractRunDataArray *dst, + eMRIterType iter_type) +{ + for (int i = 0; i < src->len; i++) { + + const MeshExtractRunData *data = &src->items[i]; + const MeshExtract *extractor = data->extractor; + if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { + BLI_assert(extractor->iter_looptri_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; + } + if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { + BLI_assert(extractor->iter_poly_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; + } + if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { + BLI_assert(extractor->iter_ledge_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; + } + if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { + BLI_assert(extractor->iter_lvert_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; + } + } +} + +static void mesh_extract_run_data_array_filter_threading( + const MeshExtractRunDataArray *src, MeshExtractRunDataArray *dst_multi_threaded) +{ + for (int i = 0; i < src->len; i++) { + const MeshExtract *extractor = src->items[i].extractor; + if (extractor->use_threading) { + mesh_extract_run_data_array_add(dst_multi_threaded, extractor); + } + } +} + BLI_INLINE eMRIterType mesh_extract_iter_type(const MeshExtract *ext) { eMRIterType type = 0; @@ -866,69 +971,65 @@ static void *extract_tris_init(const MeshRenderData *mr, } static void extract_tris_iter_looptri_bm(const MeshRenderData *mr, - const struct ExtractTriBMesh_Params *params, + BMLoop **elt, + const int UNUSED(elt_index), void *_data) { MeshExtract_Tri_Data *data = _data; const int mat_last = mr->mat_len - 1; - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params) - { - if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { - int *mat_tri_ofs = data->tri_mat_end; - const int mat = min_ii(elt[0]->f->mat_nr, mat_last); - GPU_indexbuf_set_tri_verts(&data->elb, - mat_tri_ofs[mat]++, - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2])); - } + + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + int *mat_tri_ofs = data->tri_mat_end; + const int mat = min_ii(elt[0]->f->mat_nr, mat_last); + GPU_indexbuf_set_tri_verts(&data->elb, + mat_tri_ofs[mat]++, + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; } static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr, - const struct ExtractTriMesh_Params *params, + const MLoopTri *mlt, + const int UNUSED(elt_index), void *_data) { MeshExtract_Tri_Data *data = _data; const int mat_last = mr->mat_len - 1; - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params) - { - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - int *mat_tri_ofs = data->tri_mat_end; - const int mat = min_ii(mp->mat_nr, mat_last); - GPU_indexbuf_set_tri_verts( - &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]); - } + const MPoly *mp = &mr->mpoly[mlt->poly]; + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + int *mat_tri_ofs = data->tri_mat_end; + const int mat = min_ii(mp->mat_nr, mat_last); + GPU_indexbuf_set_tri_verts( + &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]); } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } static void extract_tris_finish(const MeshRenderData *mr, struct MeshBatchCache *cache, - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_Tri_Data *data = _data; GPU_indexbuf_build_in_place(&data->elb, ibo); /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch * is created before the surfaces-per-material. */ if (mr->use_final_mesh && cache->final.tris_per_mat) { - MeshBufferCache *mbc = &cache->final; + MeshBufferCache *mbc_final = &cache->final; for (int i = 0; i < mr->mat_len; i++) { /* These IBOs have not been queried yet but we create them just in case they are needed * later since they are not tracked by mesh_buffer_cache_create_requested(). */ - if (mbc->tris_per_mat[i] == NULL) { - mbc->tris_per_mat[i] = GPU_indexbuf_calloc(); + if (mbc_final->tris_per_mat[i] == NULL) { + mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc(); } /* Multiply by 3 because these are triangle indices. */ const int mat_start = data->tri_mat_start[i]; const int mat_end = data->tri_mat_end[i]; const int start = mat_start * 3; const int len = (mat_end - mat_start) * 3; - GPU_indexbuf_create_subrange_in_place(mbc->tris_per_mat[i], ibo, start, len); + GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len); } } MEM_freeN(data->tri_mat_start); @@ -943,7 +1044,7 @@ static const MeshExtract extract_tris = { .finish = extract_tris_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris)}; /** \} */ @@ -962,120 +1063,104 @@ static void *extract_lines_init(const MeshRenderData *mr, return elb; } -static void extract_lines_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *elb) { - /* Using poly & loop iterator would complicate accessing the adjacent loop. */ - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - BMLoop *l_iter, *l_first; - /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */ - l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev; - do { - if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_line_verts(elb, - BM_elem_index_get(l_iter->e), - BM_elem_index_get(l_iter), - BM_elem_index_get(l_iter->next)); - } - else { - GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e)); - } - } while ((l_iter = l_iter->next) != l_first); - } - EXTRACT_POLY_FOREACH_BM_END; + BMLoop *l_iter, *l_first; + /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */ + l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev; + do { + if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_line_verts(elb, + BM_elem_index_get(l_iter->e), + BM_elem_index_get(l_iter), + BM_elem_index_get(l_iter->next)); + } + else { + GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e)); + } + } while ((l_iter = l_iter->next) != l_first); } static void extract_lines_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *elb) { /* Using poly & loop iterator would complicate accessing the adjacent loop. */ const MLoop *mloop = mr->mloop; const MEdge *medge = mr->medge; if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != NULL)) { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - const int ml_index_last = mp->loopstart + (mp->totloop - 1); - int ml_index = ml_index_last, ml_index_next = mp->loopstart; - do { - const MLoop *ml = &mloop[ml_index]; - const MEdge *med = &medge[ml->e]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) { - GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); - } - else { - GPU_indexbuf_set_line_restart(elb, ml->e); - } - } while ((ml_index = ml_index_next++) != ml_index_last); - } - EXTRACT_POLY_FOREACH_MESH_END; + const int ml_index_last = mp->loopstart + (mp->totloop - 1); + int ml_index = ml_index_last, ml_index_next = mp->loopstart; + do { + const MLoop *ml = &mloop[ml_index]; + const MEdge *med = &medge[ml->e]; + if (!((mr->use_hide && (med->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) { + GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); + } + else { + GPU_indexbuf_set_line_restart(elb, ml->e); + } + } while ((ml_index = ml_index_next++) != ml_index_last); } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - const int ml_index_last = mp->loopstart + (mp->totloop - 1); - int ml_index = ml_index_last, ml_index_next = mp->loopstart; - do { - const MLoop *ml = &mloop[ml_index]; - GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); - } while ((ml_index = ml_index_next++) != ml_index_last); - } - EXTRACT_POLY_FOREACH_MESH_END; + const int ml_index_last = mp->loopstart + (mp->totloop - 1); + int ml_index = ml_index_last, ml_index_next = mp->loopstart; + do { + const MLoop *ml = &mloop[ml_index]; + GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); + } while ((ml_index = ml_index_next++) != ml_index_last); } } static void extract_lines_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - const int l_index_offset = mr->edge_len + ledge_index; - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - const int l_index = mr->loop_len + ledge_index * 2; - GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); - } - else { - GPU_indexbuf_set_line_restart(elb, l_index_offset); - } - /* Don't render the edge twice. */ - GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed)); + const int l_index_offset = mr->edge_len + ledge_index; + if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { + const int l_index = mr->loop_len + ledge_index * 2; + GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); } - EXTRACT_LEDGE_FOREACH_BM_END; + else { + GPU_indexbuf_set_line_restart(elb, l_index_offset); + } + /* Don't render the edge twice. */ + GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed)); } static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int l_index_offset = mr->edge_len + ledge_index; - const int e_index = mr->ledges[ledge_index]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { - const int l_index = mr->loop_len + ledge_index * 2; - GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); - } - else { - GPU_indexbuf_set_line_restart(elb, l_index_offset); - } - /* Don't render the edge twice. */ - GPU_indexbuf_set_line_restart(elb, e_index); + const int l_index_offset = mr->edge_len + ledge_index; + const int e_index = mr->ledges[ledge_index]; + if (!((mr->use_hide && (med->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { + const int l_index = mr->loop_len + ledge_index * 2; + GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); } - EXTRACT_LEDGE_FOREACH_MESH_END; + else { + GPU_indexbuf_set_line_restart(elb, l_index_offset); + } + /* Don't render the edge twice. */ + GPU_indexbuf_set_line_restart(elb, e_index); } static void extract_lines_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); } @@ -1089,7 +1174,8 @@ static const MeshExtract extract_lines = { .finish = extract_lines_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; + /** \} */ /* ---------------------------------------------------------------------- */ @@ -1109,9 +1195,10 @@ static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshB static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, struct MeshBatchCache *cache, - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); extract_lines_loose_subbuffer(mr, cache); MEM_freeN(elb); @@ -1126,7 +1213,7 @@ static const MeshExtract extract_lines_with_lines_loose = { .finish = extract_lines_with_lines_loose_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; /** \} */ @@ -1170,81 +1257,76 @@ BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb, } } -static void extract_points_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *elb) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - vert_set_bm(elb, l->v, l_index); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + vert_set_bm(elb, l_iter->v, l_index); + } while ((l_iter = l_iter->next) != l_first); } static void extract_points_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *elb) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; vert_set_mesh(elb, mr, ml->v, ml_index); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_points_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2)); - vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1); - } - EXTRACT_LEDGE_FOREACH_BM_END; + vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2)); + vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1); } static void extract_points_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2)); - vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1); - } - EXTRACT_LEDGE_FOREACH_MESH_END; + vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2)); + vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1); } static void extract_points_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *elb) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - vert_set_bm(elb, eve, offset + lvert_index); - } - EXTRACT_LVERT_FOREACH_BM_END; + vert_set_bm(elb, eve, offset + lvert_index); } static void extract_points_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *UNUSED(mv), + const int lvert_index, void *elb) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index); - } - EXTRACT_LVERT_FOREACH_MESH_END; + + vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index); } static void extract_points_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); } @@ -1260,7 +1342,7 @@ static const MeshExtract extract_points = { .finish = extract_points_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points)}; /** \} */ @@ -1277,59 +1359,54 @@ static void *extract_fdots_init(const MeshRenderData *mr, return elb; } -static void extract_fdots_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, void *elb) { - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_point_vert(elb, f_index, f_index); - } - else { - GPU_indexbuf_set_point_restart(elb, f_index); - } + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_point_vert(elb, f_index, f_index); + } + else { + GPU_indexbuf_set_point_restart(elb, f_index); } - EXTRACT_POLY_FOREACH_BM_END; } static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *elb) { if (mr->use_subsurf_fdots) { /* Check #ME_VERT_FACEDOT. */ - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; const MVert *mv = &mr->mvert[ml->v]; if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) { GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); - } - else { - GPU_indexbuf_set_point_restart(elb, mp_index); + return; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; + GPU_indexbuf_set_point_restart(elb, mp_index); } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); - } - else { - GPU_indexbuf_set_point_restart(elb, mp_index); - } + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); + } + else { + GPU_indexbuf_set_point_restart(elb, mp_index); } - EXTRACT_POLY_FOREACH_MESH_END; } } static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); } @@ -1341,7 +1418,7 @@ static const MeshExtract extract_fdots = { .finish = extract_fdots_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots)}; /** \} */ @@ -1357,7 +1434,7 @@ typedef struct MeshExtract_LinePaintMask_Data { static void *extract_lines_paint_mask_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) + void *UNUSED(ibo)) { size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len); MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__); @@ -1366,12 +1443,16 @@ static void *extract_lines_paint_mask_init(const MeshRenderData *mr, } static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_LinePaintMask_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const int e_index = ml->e; const MEdge *me = &mr->medge[e_index]; if (!((mr->use_hide && (me->flag & ME_HIDE)) || @@ -1401,15 +1482,15 @@ static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, GPU_indexbuf_set_line_restart(&data->elb, e_index); } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } + static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_LinePaintMask_Data *data = _data; - GPU_indexbuf_build_in_place(&data->elb, ibo); MEM_freeN(data); } @@ -1420,7 +1501,7 @@ static const MeshExtract extract_lines_paint_mask = { .finish = extract_lines_paint_mask_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask)}; /** \} */ @@ -1498,49 +1579,44 @@ BLI_INLINE void lines_adjacency_triangle( } static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr), - const struct ExtractTriBMesh_Params *params, + BMLoop **elt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params) - { - if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { - lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), - BM_elem_index_get(elt[1]->v), - BM_elem_index_get(elt[2]->v), - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2]), - data); - } + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), + BM_elem_index_get(elt[1]->v), + BM_elem_index_get(elt[2]->v), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2]), + data); } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; } static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr, - const struct ExtractTriMesh_Params *params, + const MLoopTri *mlt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params) - { - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, - mr->mloop[mlt->tri[1]].v, - mr->mloop[mlt->tri[2]].v, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2], - data); - } + const MPoly *mp = &mr->mpoly[mlt->poly]; + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, + mr->mloop[mlt->tri[1]].v, + mr->mloop[mlt->tri[2]].v, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2], + data); } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *cache, - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_LineAdjacency_Data *data = _data; /* Create edges for remaining non manifold edges. */ EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh); @@ -1577,7 +1653,7 @@ static const MeshExtract extract_lines_adjacency = { .finish = extract_lines_adjacency_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency)}; /** \} */ @@ -1609,43 +1685,38 @@ BLI_INLINE void edituv_tri_add( } static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr), - const struct ExtractTriBMesh_Params *params, + BMLoop **elt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params) - { - edituv_tri_add(data, - BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2])); - } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; + edituv_tri_add(data, + BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), + BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); } static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr, - const struct ExtractTriMesh_Params *params, + const MLoopTri *mlt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params) - { - const MPoly *mp = &mr->mpoly[mlt->poly]; - edituv_tri_add(data, - (mp->flag & ME_HIDE) != 0, - (mp->flag & ME_FACE_SEL) != 0, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2]); - } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; + const MPoly *mp = &mr->mpoly[mlt->poly]; + edituv_tri_add(data, + (mp->flag & ME_HIDE) != 0, + (mp->flag & ME_FACE_SEL) != 0, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2]); } static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); MEM_freeN(extract_data); @@ -1658,7 +1729,7 @@ static const MeshExtract extract_edituv_tris = { .finish = extract_edituv_tris_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris)}; /** \} */ @@ -1685,27 +1756,34 @@ BLI_INLINE void edituv_edge_add( } } -static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + edituv_edge_add(data, - BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(loop->f, BM_ELEM_SELECT), + BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), + BM_elem_flag_test_bool(f, BM_ELEM_SELECT), l_index, - BM_elem_index_get(loop->next)); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop); + BM_elem_index_get(l_iter->next)); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const int ml_index_last = mp->totloop + mp->loopstart - 1; const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); const bool real_edge = (mr->e_origindex == NULL || mr->e_origindex[ml->e] != ORIGINDEX_NONE); @@ -1715,14 +1793,14 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, ml_index, ml_index_next); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); MEM_freeN(extract_data); @@ -1735,7 +1813,7 @@ static const MeshExtract extract_edituv_lines = { .finish = extract_edituv_lines_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines)}; /** \} */ @@ -1764,39 +1842,44 @@ BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data, } } -static void extract_edituv_points_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - edituv_point_add(data, - BM_elem_flag_test(l->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(l->f, BM_ELEM_SELECT), - l_index); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + edituv_point_add( + data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), l_index); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && mr->v_origindex[ml->v] != ORIGINDEX_NONE); edituv_point_add( data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); MEM_freeN(extract_data); @@ -1809,7 +1892,7 @@ static const MeshExtract extract_edituv_points = { .finish = extract_edituv_points_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points)}; /** \} */ @@ -1841,26 +1924,29 @@ BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data, } } -static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, void *data) { - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - edituv_facedot_add( - data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), f_index); - } - EXTRACT_POLY_FOREACH_BM_END; + edituv_facedot_add(data, + BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), + BM_elem_flag_test_bool(f, BM_ELEM_SELECT), + f_index); } static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { if (mr->use_subsurf_fdots) { /* Check #ME_VERT_FACEDOT. */ - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && mr->p_origindex[mp_index] != ORIGINDEX_NONE); const bool subd_fdot = (!mr->use_subsurf_fdots || @@ -1870,27 +1956,21 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, (mp->flag & ME_FACE_SEL) != 0, mp_index); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[mp_index] != ORIGINDEX_NONE); - edituv_facedot_add(data, - ((mp->flag & ME_HIDE) != 0) || !real_fdot, - (mp->flag & ME_FACE_SEL) != 0, - mp_index); - } - EXTRACT_POLY_FOREACH_MESH_END; + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[mp_index] != ORIGINDEX_NONE); + edituv_facedot_add( + data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); } } static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *data = _data; GPU_indexbuf_build_in_place(&data->elb, ibo); MEM_freeN(data); @@ -1903,7 +1983,7 @@ static const MeshExtract extract_edituv_fdots = { .finish = extract_edituv_fdots_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots)}; /** \} */ @@ -1925,6 +2005,7 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING Adjust #PosNorLoop struct accordingly. */ @@ -1932,7 +2013,6 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "vnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -1960,28 +2040,34 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, } static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v)); - vert->nor = data->normals[BM_elem_index_get(l->v)].low; - BMFace *efa = l->f; - vert->nor.w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); + vert->nor = data->normals[BM_elem_index_get(l_iter->v)].low; + vert->nor.w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; + } while ((l_iter = l_iter->next) != l_first); } static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + PosNorLoop *vert = &data->vbo_data[ml_index]; const MVert *mv = &mr->mvert[ml->v]; copy_v3_v3(vert->pos, mv->co); @@ -1999,79 +2085,69 @@ static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, vert->nor.w = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - int l_index = mr->loop_len + ledge_index * 2; - PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); - copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); - vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low; - vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low; - } - EXTRACT_LEDGE_FOREACH_BM_END; + + int l_index = mr->loop_len + ledge_index * 2; + PosNorLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); + copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); + vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low; + vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low; } static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int ml_index = mr->loop_len + ledge_index * 2; - PosNorLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); - copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); - vert[0].nor = data->normals[med->v1].low; - vert[1].nor = data->normals[med->v2].low; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + const int ml_index = mr->loop_len + ledge_index * 2; + PosNorLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); + copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); + vert[0].nor = data->normals[med->v1].low; + vert[1].nor = data->normals[med->v2].low; } static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *_data) { MeshExtract_PosNor_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - const int l_index = offset + lvert_index; - PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); - vert->nor = data->normals[BM_elem_index_get(eve)].low; - } - EXTRACT_LVERT_FOREACH_BM_END; + + const int l_index = offset + lvert_index; + PosNorLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); + vert->nor = data->normals[BM_elem_index_get(eve)].low; } static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *mv, + const int lvert_index, void *_data) { MeshExtract_PosNor_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - const int ml_index = offset + lvert_index; - const int v_index = mr->lverts[lvert_index]; - PosNorLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert->pos, mv->co); - vert->nor = data->normals[v_index].low; - } - EXTRACT_LVERT_FOREACH_MESH_END; + + const int ml_index = offset + lvert_index; + const int v_index = mr->lverts[lvert_index]; + PosNorLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert->pos, mv->co); + vert->nor = data->normals[v_index].low; } static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *UNUSED(vbo), + void *UNUSED(buf), void *data) { MEM_freeN(data); @@ -2088,7 +2164,8 @@ static const MeshExtract extract_pos_nor = { .finish = extract_pos_nor_finish, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; + /** \} */ /* ---------------------------------------------------------------------- */ @@ -2109,6 +2186,7 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING Adjust #PosNorHQLoop struct accordingly. */ @@ -2116,7 +2194,6 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "vnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -2144,29 +2221,35 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr, } static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v)); - copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l->v)].high); + copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); + copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l_iter->v)].high); - BMFace *efa = l->f; + BMFace *efa = l_iter->f; vert->nor[3] = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + } while ((l_iter = l_iter->next) != l_first); } static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; const MVert *mv = &mr->mvert[ml->v]; copy_v3_v3(vert->pos, mv->co); @@ -2185,85 +2268,74 @@ static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, vert->nor[3] = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_pos_nor_hq_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - int l_index = mr->loop_len + ledge_index * 2; - PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); - copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); - copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high); - vert[0].nor[3] = 0; - copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high); - vert[1].nor[3] = 0; - } - EXTRACT_LEDGE_FOREACH_BM_END; + int l_index = mr->loop_len + ledge_index * 2; + PosNorHQLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); + copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); + copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high); + vert[0].nor[3] = 0; + copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high); + vert[1].nor[3] = 0; } static void extract_pos_nor_hq_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int ml_index = mr->loop_len + ledge_index * 2; - PosNorHQLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); - copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); - copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high); - vert[0].nor[3] = 0; - copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high); - vert[1].nor[3] = 0; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + const int ml_index = mr->loop_len + ledge_index * 2; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); + copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); + copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high); + vert[0].nor[3] = 0; + copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high); + vert[1].nor[3] = 0; } static void extract_pos_nor_hq_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - const int l_index = offset + lvert_index; - PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); - copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high); - vert->nor[3] = 0; - } - EXTRACT_LVERT_FOREACH_BM_END; + + const int l_index = offset + lvert_index; + PosNorHQLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); + copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high); + vert->nor[3] = 0; } static void extract_pos_nor_hq_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *mv, + const int lvert_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - const int ml_index = offset + lvert_index; - const int v_index = mr->lverts[lvert_index]; - PosNorHQLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert->pos, mv->co); - copy_v3_v3_short(vert->nor, data->normals[v_index].high); - vert->nor[3] = 0; - } - EXTRACT_LVERT_FOREACH_MESH_END; + + const int ml_index = offset + lvert_index; + const int v_index = mr->lverts[lvert_index]; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert->pos, mv->co); + copy_v3_v3_short(vert->nor, data->normals[v_index].high); + vert->nor[3] = 0; } static void extract_pos_nor_hq_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *UNUSED(vbo), + void *UNUSED(buf), void *data) { MEM_freeN(data); @@ -2280,7 +2352,7 @@ static const MeshExtract extract_pos_nor_hq = { .finish = extract_pos_nor_hq_finish, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; /** \} */ /* ---------------------------------------------------------------------- */ @@ -2295,12 +2367,12 @@ static void *extract_lnor_hq_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "lnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -2308,36 +2380,37 @@ static void *extract_lnor_hq_init(const MeshRenderData *mr, } static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *data) { - if (mr->loop_normals) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(_l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (mr->loop_normals) { normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, mr->loop_normals[l_index]); } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(_l); - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH)) { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l->v)); + else { + if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { + normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l_iter->v)); } else { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, l->f)); + normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, f)); } } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } + } while ((l_iter = l_iter->next) != l_first); } static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; gpuHQNor *lnor_data = &((gpuHQNor *)data)[ml_index]; if (mr->loop_normals) { normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[ml_index]); @@ -2363,7 +2436,6 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, lnor_data->w = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static const MeshExtract extract_lnor_hq = { @@ -2372,7 +2444,7 @@ static const MeshExtract extract_lnor_hq = { .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh, .data_flag = MR_DATA_LOOP_NOR, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; /** \} */ /* ---------------------------------------------------------------------- */ @@ -2383,12 +2455,12 @@ static void *extract_lnor_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "lnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -2396,40 +2468,39 @@ static void *extract_lnor_init(const MeshRenderData *mr, } static void extract_lnor_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *data) { - if (mr->loop_normals) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (mr->loop_normals) { ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]); - BMFace *efa = l->f; - ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH)) { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, l->v)); + else { + if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { + ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3( + bm_vert_no_get(mr, l_iter->v)); } else { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, l->f)); + ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, f)); } - BMFace *efa = l->f; - ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } + ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; + } while ((l_iter = l_iter->next) != l_first); } static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; GPUPackedNormal *lnor_data = &((GPUPackedNormal *)data)[ml_index]; if (mr->loop_normals) { *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[ml_index]); @@ -2455,7 +2526,6 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, lnor_data->w = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static const MeshExtract extract_lnor = { @@ -2464,7 +2534,7 @@ static const MeshExtract extract_lnor = { .iter_poly_mesh = extract_lnor_iter_poly_mesh, .data_flag = MR_DATA_LOOP_NOR, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; /** \} */ @@ -2474,6 +2544,7 @@ static const MeshExtract extract_lnor = { static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { + GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; GPU_vertformat_deinterleave(&format); @@ -2523,7 +2594,6 @@ static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *ca v_len = 1; } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, v_len); @@ -2556,11 +2626,10 @@ static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *ca return NULL; } -static const MeshExtract extract_uv = { - .init = extract_uv_init, - .data_flag = 0, - .use_threading = false, -}; +static const MeshExtract extract_uv = {.init = extract_uv_init, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv)}; /** \} */ @@ -2568,10 +2637,10 @@ static const MeshExtract extract_uv = { /** \name Extract Tangent layers * \{ */ -static void extract_tan_ex(const MeshRenderData *mr, - struct MeshBatchCache *cache, - GPUVertBuf *vbo, - const bool do_hq) +static void extract_tan_ex_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + GPUVertBuf *vbo, + const bool do_hq) { GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10; GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT; @@ -2742,15 +2811,15 @@ static void extract_tan_ex(const MeshRenderData *mr, static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { - extract_tan_ex(mr, cache, buf, false); + extract_tan_ex_init(mr, cache, buf, false); return NULL; } -static const MeshExtract extract_tan = { - .init = extract_tan_init, - .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, - .use_threading = false, -}; +static const MeshExtract extract_tan = {.init = extract_tan_init, + .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | + MR_DATA_LOOPTRI, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan)}; /** \} */ @@ -2760,7 +2829,7 @@ static const MeshExtract extract_tan = { static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { - extract_tan_ex(mr, cache, buf, true); + extract_tan_ex_init(mr, cache, buf, true); return NULL; } @@ -2780,6 +2849,7 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; @@ -2794,7 +2864,6 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -2868,7 +2937,7 @@ static const MeshExtract extract_sculpt_data = { .data_flag = 0, /* TODO: enable threading. */ .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data)}; /** \} */ @@ -2878,6 +2947,7 @@ static const MeshExtract extract_sculpt_data = { static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { + GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; GPU_vertformat_deinterleave(&format); @@ -2939,7 +3009,6 @@ static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache * } } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3016,7 +3085,7 @@ static const MeshExtract extract_vcol = { .init = extract_vcol_init, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol)}; /** \} */ @@ -3033,6 +3102,7 @@ static void *extract_orco_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex @@ -3042,7 +3112,6 @@ static void *extract_orco_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3056,32 +3125,36 @@ static void *extract_orco_init(const MeshRenderData *mr, return data; } -static void extract_orco_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_orco_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); float *loop_orco = orco_data->vbo_data[l_index]; - copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(loop->v)]); + copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(l_iter->v)]); loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop); + } while ((l_iter = l_iter->next) != l_first); } static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; float *loop_orco = orco_data->vbo_data[ml_index]; copy_v3_v3(loop_orco, orco_data->orco[ml->v]); loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_orco_finish(const MeshRenderData *UNUSED(mr), @@ -3099,7 +3172,7 @@ static const MeshExtract extract_orco = { .finish = extract_orco_finish, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco)}; /** \} */ @@ -3135,11 +3208,12 @@ static void *extract_edge_fac_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -3170,43 +3244,47 @@ static void *extract_edge_fac_init(const MeshRenderData *mr, } static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_EdgeFac_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - if (BM_edge_is_manifold(l->e)) { - float ratio = loop_edge_factor_get(bm_face_no_get(mr, l->f), - bm_vert_co_get(mr, l->v), - bm_vert_no_get(mr, l->v), - bm_vert_co_get(mr, l->next->v)); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + if (BM_edge_is_manifold(l_iter->e)) { + float ratio = loop_edge_factor_get(bm_face_no_get(mr, f), + bm_vert_co_get(mr, l_iter->v), + bm_vert_no_get(mr, l_iter->v), + bm_vert_co_get(mr, l_iter->next->v)); data->vbo_data[l_index] = ratio * 253 + 1; } else { data->vbo_data[l_index] = 255; } - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data; - if (data->use_edge_render) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + if (data->use_edge_render) { const MEdge *med = &mr->medge[ml->e]; data->vbo_data[ml_index] = (med->flag & ME_EDGERENDER) ? 255 : 0; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + else { + /* Count loop per edge to detect non-manifold. */ if (data->edge_loop_count[ml->e] < 3) { data->edge_loop_count[ml->e]++; @@ -3228,34 +3306,28 @@ static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr, data->vbo_data[ml_index] = 255; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } } static void extract_edge_fac_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *UNUSED(eed), + const int ledge_index, void *_data) { MeshExtract_EdgeFac_Data *data = _data; - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255; - data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255; - } - EXTRACT_LEDGE_FOREACH_BM_END; + data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255; + data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255; } static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *UNUSED(med), + const uint ledge_index, void *_data) { MeshExtract_EdgeFac_Data *data = _data; - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255; - data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + + data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255; + data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255; } static void extract_edge_fac_finish(const MeshRenderData *mr, @@ -3263,10 +3335,10 @@ static void extract_edge_fac_finish(const MeshRenderData *mr, void *buf, void *_data) { + GPUVertBuf *vbo = buf; MeshExtract_EdgeFac_Data *data = _data; if (GPU_crappy_amd_driver()) { - GPUVertBuf *vbo = (GPUVertBuf *)buf; /* Some AMD drivers strangely crash with VBO's with a one byte format. * To workaround we reinitialize the VBO with another format and convert * all bytes to floats. */ @@ -3301,7 +3373,7 @@ static const MeshExtract extract_edge_fac = { .finish = extract_edge_fac_finish, .data_flag = MR_DATA_POLY_NOR, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac)}; /** \} */ /* ---------------------------------------------------------------------- */ @@ -3372,11 +3444,11 @@ static void *extract_weights_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -3400,48 +3472,44 @@ static void *extract_weights_init(const MeshRenderData *mr, return data; } -static void extract_weights_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_weights_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_Weight_Data *data = _data; - if (data->cd_ofs != -1) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l->v, data->cd_ofs); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (data->cd_ofs != -1) { + const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l_iter->v, data->cd_ofs); data->vbo_data[l_index] = evaluate_vertex_weight(dvert, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + else { data->vbo_data[l_index] = evaluate_vertex_weight(NULL, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } + } while ((l_iter = l_iter->next) != l_first); } static void extract_weights_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_Weight_Data *data = _data; - if (data->dvert != NULL) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (data->dvert != NULL) { const MDeformVert *dvert = &data->dvert[ml->v]; data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - const MDeformVert *dvert = NULL; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + else { + const MDeformVert *dvert = NULL; data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } } @@ -3460,7 +3528,7 @@ static const MeshExtract extract_weights = { .finish = extract_weights_finish, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights)}; /** \} */ @@ -3610,40 +3678,45 @@ static void *extract_edit_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING: Adjust #EditLoopData struct accordingly. */ GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); GPU_vertformat_alias_add(&format, "flag"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); return GPU_vertbuf_get_data(vbo); } static void extract_edit_data_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { EditLoopData *data = (EditLoopData *)_data + l_index; memset(data, 0x0, sizeof(*data)); - mesh_render_data_face_flag(mr, l->f, -1, data); - mesh_render_data_edge_flag(mr, l->e, data); - mesh_render_data_vert_flag(mr, l->v, data); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + mesh_render_data_face_flag(mr, f, -1, data); + mesh_render_data_edge_flag(mr, l_iter->e, data); + mesh_render_data_vert_flag(mr, l_iter->v, data); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; EditLoopData *data = (EditLoopData *)_data + ml_index; memset(data, 0x0, sizeof(*data)); BMFace *efa = bm_original_face_get(mr, mp_index); @@ -3659,81 +3732,69 @@ static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr, mesh_render_data_vert_flag(mr, eve, data); } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edit_data_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *_data) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2); - memset(data, 0x0, sizeof(*data) * 2); - mesh_render_data_edge_flag(mr, eed, &data[0]); - data[1] = data[0]; - mesh_render_data_vert_flag(mr, eed->v1, &data[0]); - mesh_render_data_vert_flag(mr, eed->v2, &data[1]); - } - EXTRACT_LEDGE_FOREACH_BM_END; + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2); + memset(data, 0x0, sizeof(*data) * 2); + mesh_render_data_edge_flag(mr, eed, &data[0]); + data[1] = data[0]; + mesh_render_data_vert_flag(mr, eed->v1, &data[0]); + mesh_render_data_vert_flag(mr, eed->v2, &data[1]); } static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *_data) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2; - memset(data, 0x0, sizeof(*data) * 2); - const int e_index = mr->ledges[ledge_index]; - BMEdge *eed = bm_original_edge_get(mr, e_index); - BMVert *eve1 = bm_original_vert_get(mr, med->v1); - BMVert *eve2 = bm_original_vert_get(mr, med->v2); - if (eed) { - mesh_render_data_edge_flag(mr, eed, &data[0]); - data[1] = data[0]; - } - if (eve1) { - mesh_render_data_vert_flag(mr, eve1, &data[0]); - } - if (eve2) { - mesh_render_data_vert_flag(mr, eve2, &data[1]); - } + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2; + memset(data, 0x0, sizeof(*data) * 2); + const int e_index = mr->ledges[ledge_index]; + BMEdge *eed = bm_original_edge_get(mr, e_index); + BMVert *eve1 = bm_original_vert_get(mr, med->v1); + BMVert *eve2 = bm_original_vert_get(mr, med->v2); + if (eed) { + mesh_render_data_edge_flag(mr, eed, &data[0]); + data[1] = data[0]; + } + if (eve1) { + mesh_render_data_vert_flag(mr, eve1, &data[0]); + } + if (eve2) { + mesh_render_data_vert_flag(mr, eve2, &data[1]); } - EXTRACT_LEDGE_FOREACH_MESH_END; } static void extract_edit_data_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *_data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; - memset(data, 0x0, sizeof(*data)); - mesh_render_data_vert_flag(mr, eve, data); - } - EXTRACT_LVERT_FOREACH_BM_END; + EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; + memset(data, 0x0, sizeof(*data)); + mesh_render_data_vert_flag(mr, eve, data); } static void extract_edit_data_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *UNUSED(mv), + const int lvert_index, void *_data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; - memset(data, 0x0, sizeof(*data)); - const int v_index = mr->lverts[lvert_index]; - BMVert *eve = bm_original_vert_get(mr, v_index); - if (eve) { - mesh_render_data_vert_flag(mr, eve, data); - } + + EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; + memset(data, 0x0, sizeof(*data)); + const int v_index = mr->lverts[lvert_index]; + BMVert *eve = bm_original_vert_get(mr, v_index); + if (eve) { + mesh_render_data_vert_flag(mr, eve, data); } - EXTRACT_LVERT_FOREACH_MESH_END; } static const MeshExtract extract_edit_data = { @@ -3746,7 +3807,7 @@ static const MeshExtract extract_edit_data = { .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data)}; /** \} */ @@ -3763,6 +3824,7 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING: Adjust #EditLoopData struct accordingly. */ @@ -3770,7 +3832,6 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, GPU_vertformat_alias_add(&format, "flag"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3783,28 +3844,34 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, } static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); MeshExtract_EditUVData_Data *data = _data; EditLoopData *eldata = &data->vbo_data[l_index]; memset(eldata, 0x0, sizeof(*eldata)); - mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata); - mesh_render_data_face_flag(mr, l->f, data->cd_ofs, eldata); - mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + mesh_render_data_loop_flag(mr, l_iter, data->cd_ofs, eldata); + mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); + mesh_render_data_loop_edge_flag(mr, l_iter, data->cd_ofs, eldata); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { MeshExtract_EditUVData_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + EditLoopData *eldata = &data->vbo_data[ml_index]; memset(eldata, 0x0, sizeof(*eldata)); BMFace *efa = bm_original_face_get(mr, mp_index); @@ -3834,7 +3901,6 @@ static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, } } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr), @@ -3852,7 +3918,7 @@ static const MeshExtract extract_edituv_data = { .finish = extract_edituv_data_finish, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data)}; /** \} */ @@ -3864,12 +3930,12 @@ static void *extract_edituv_stretch_area_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "ratio", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3891,11 +3957,12 @@ BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_t return (ratio > 1.0f) ? (1.0f / ratio) : ratio; } -static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf, - void *UNUSED(data)) +static void extract_edituv_stretch_area_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(data)) { + GPUVertBuf *vbo = buf; float tot_area = 0.0f, tot_uv_area = 0.0f; float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__); @@ -3937,7 +4004,6 @@ static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr, } /* Copy face data for each loop. */ - GPUVertBuf *vbo = buf; uint16_t *loop_stretch = (uint16_t *)GPU_vertbuf_get_data(vbo); if (mr->extract_type == MR_EXTRACT_BMESH) { @@ -3965,10 +4031,10 @@ static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr, static const MeshExtract extract_edituv_stretch_area = { .init = extract_edituv_stretch_area_init, - .finish = mesh_edituv_stretch_area_finish, + .finish = extract_edituv_stretch_area_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area)}; /** \} */ @@ -4034,6 +4100,7 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* Waning: adjust #UVStretchAngle struct accordingly. */ @@ -4041,7 +4108,6 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -4060,21 +4126,24 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, } static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_StretchAngle_Data *data = _data; float(*auv)[2] = data->auv, *last_auv = data->last_auv; float(*av)[3] = data->av, *last_av = data->last_av; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + const MLoopUV *luv, *luv_next; - BMLoop *l_next = l->next; - BMFace *efa = l->f; - if (l == BM_FACE_FIRST_LOOP(efa)) { + BMLoop *l_next = l_iter->next; + if (l_iter == BM_FACE_FIRST_LOOP(f)) { /* First loop in face. */ - BMLoop *l_tmp = l->prev; - BMLoop *l_next_tmp = l; + BMLoop *l_tmp = l_iter->prev; + BMLoop *l_next_tmp = l_iter; luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs); luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs); compute_normalize_edge_vectors(auv, @@ -4087,7 +4156,7 @@ static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, copy_v2_v2(last_auv, auv[1]); copy_v3_v3(last_av, av[1]); } - if (l_next == BM_FACE_FIRST_LOOP(efa)) { + if (l_next == BM_FACE_FIRST_LOOP(f)) { /* Move previous edge. */ copy_v2_v2(auv[0], auv[1]); copy_v3_v3(av[0], av[1]); @@ -4096,27 +4165,31 @@ static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, copy_v3_v3(av[1], last_av); } else { - luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs); + luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs); - compute_normalize_edge_vectors( - auv, av, luv->uv, luv_next->uv, bm_vert_co_get(mr, l->v), bm_vert_co_get(mr, l_next->v)); + compute_normalize_edge_vectors(auv, + av, + luv->uv, + luv_next->uv, + bm_vert_co_get(mr, l_iter->v), + bm_vert_co_get(mr, l_next->v)); } edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[l_index]); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_StretchAngle_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { float(*auv)[2] = data->auv, *last_auv = data->last_auv; float(*av)[3] = data->av, *last_av = data->last_av; - int l_next = ml_index + 1, ml_index_end = mp->loopstart + mp->totloop; + int l_next = ml_index + 1; const MVert *v, *v_next; if (ml_index == mp->loopstart) { /* First loop in face. */ @@ -4147,7 +4220,6 @@ static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr } edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[ml_index]); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_stretch_angle_finish(const MeshRenderData *UNUSED(mr), @@ -4165,7 +4237,7 @@ static const MeshExtract extract_edituv_stretch_angle = { .finish = extract_edituv_stretch_angle_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle)}; /** \} */ @@ -4177,12 +4249,12 @@ static void *extract_mesh_analysis_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -4736,14 +4808,14 @@ static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp) MEM_freeN(vert_angles); } -static void extract_mesh_analysis_finish(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *UNUSED(data)) +static void extract_analysis_iter_finish_mesh(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(data)) { + GPUVertBuf *vbo = buf; BLI_assert(mr->edit_bmesh); - GPUVertBuf *vbo = buf; float *l_weight = (float *)GPU_vertbuf_get_data(vbo); switch (mr->toolsettings->statvis.type) { @@ -4767,12 +4839,12 @@ static void extract_mesh_analysis_finish(const MeshRenderData *mr, static const MeshExtract extract_mesh_analysis = { .init = extract_mesh_analysis_init, - .finish = extract_mesh_analysis_finish, + .finish = extract_analysis_iter_finish_mesh, /* This is not needed for all visualization types. * * Maybe split into different extract. */ .data_flag = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis)}; /** \} */ @@ -4784,69 +4856,65 @@ static void *extract_fdots_pos_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); return GPU_vertbuf_get_data(vbo); } static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int f_index, void *data) { float(*center)[3] = data; - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - float *co = center[f_index]; - zero_v3(co); + float *co = center[f_index]; + zero_v3(co); - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - add_v3_v3(co, bm_vert_co_get(mr, l_iter->v)); - } while ((l_iter = l_iter->next) != l_first); - mul_v3_fl(co, 1.0f / (float)f->len); - } - EXTRACT_POLY_FOREACH_BM_END; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + add_v3_v3(co, bm_vert_co_get(mr, l_iter->v)); + } while ((l_iter = l_iter->next) != l_first); + mul_v3_fl(co, 1.0f / (float)f->len); } static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { float(*center)[3] = (float(*)[3])data; + float *co = center[mp_index]; + zero_v3(co); + const MVert *mvert = mr->mvert; const MLoop *mloop = mr->mloop; - if (mr->use_subsurf_fdots) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (mr->use_subsurf_fdots) { const MVert *mv = &mr->mvert[ml->v]; if (mv->flag & ME_VERT_FACEDOT) { copy_v3_v3(center[mp_index], mv->co); + break; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - float *co = center[mp_index]; - zero_v3(co); - - const MLoop *ml = &mloop[mp->loopstart]; - for (int i = 0; i < mp->totloop; i++, ml++) { - const MVert *mv = &mvert[ml->v]; - add_v3_v3(center[mp_index], mv->co); - } - mul_v3_fl(co, 1.0f / (float)mp->totloop); + else { + const MVert *mv = &mvert[ml->v]; + add_v3_v3(center[mp_index], mv->co); } - EXTRACT_POLY_FOREACH_MESH_END; + } + + if (!mr->use_subsurf_fdots) { + mul_v3_fl(co, 1.0f / (float)mp->totloop); } } @@ -4856,7 +4924,7 @@ static const MeshExtract extract_fdots_pos = { .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos)}; /** \} */ @@ -4872,11 +4940,12 @@ static void *extract_fdots_nor_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -4888,8 +4957,8 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) { - static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; GPUVertBuf *vbo = buf; + static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; GPUPackedNormal *nor = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo); BMFace *efa; @@ -4937,7 +5006,7 @@ static const MeshExtract extract_fdots_nor = { .finish = extract_fdots_nor_finish, .data_flag = MR_DATA_POLY_NOR, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; /** \} */ @@ -4948,11 +5017,12 @@ static void *extract_fdots_nor_hq_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -4964,8 +5034,8 @@ static void extract_fdots_nor_hq_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) { - static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; GPUVertBuf *vbo = buf; + static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; short *nor = (short *)GPU_vertbuf_get_data(vbo); BMFace *efa; @@ -5013,7 +5083,7 @@ static const MeshExtract extract_fdots_nor_hq = { .finish = extract_fdots_nor_hq_finish, .data_flag = MR_DATA_POLY_NOR, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; /** \} */ @@ -5031,13 +5101,14 @@ static void *extract_fdots_uv_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); GPU_vertformat_alias_add(&format, "au"); GPU_vertformat_alias_add(&format, "pos"); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -5058,42 +5129,41 @@ static void *extract_fdots_uv_init(const MeshRenderData *mr, return data; } -static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_FdotUV_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - float w = 1.0f / (float)l->f->len; - const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs); - madd_v2_v2fl(data->vbo_data[BM_elem_index_get(l->f)], luv->uv, w); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + float w = 1.0f / (float)f->len; + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); + madd_v2_v2fl(data->vbo_data[BM_elem_index_get(f)], luv->uv, w); + } while ((l_iter = l_iter->next) != l_first); } static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { MeshExtract_FdotUV_Data *data = _data; - if (mr->use_subsurf_fdots) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (mr->use_subsurf_fdots) { const MVert *mv = &mr->mvert[ml->v]; if (mv->flag & ME_VERT_FACEDOT) { copy_v2_v2(data->vbo_data[mp_index], data->uv_data[ml_index].uv); } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + else { float w = 1.0f / (float)mp->totloop; madd_v2_v2fl(data->vbo_data[mp_index], data->uv_data[ml_index].uv, w); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } } @@ -5112,7 +5182,8 @@ static const MeshExtract extract_fdots_uv = { .finish = extract_fdots_uv_finish, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv)}; + /** \} */ /* ---------------------------------------------------------------------- */ @@ -5128,11 +5199,12 @@ static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "flag", GPU_COMP_U8, 4, GPU_FETCH_INT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -5143,34 +5215,28 @@ static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, } static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_EditUVFdotData_Data *data = _data; - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)]; - memset(eldata, 0x0, sizeof(*eldata)); - mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); - } - EXTRACT_POLY_FOREACH_BM_END; + EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)]; + memset(eldata, 0x0, sizeof(*eldata)); + mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); } static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *UNUSED(mp), + const int mp_index, void *_data) { MeshExtract_EditUVFdotData_Data *data = _data; - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - EditLoopData *eldata = &data->vbo_data[mp_index]; - memset(eldata, 0x0, sizeof(*eldata)); - BMFace *efa = bm_original_face_get(mr, mp_index); - if (efa) { - mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata); - } + EditLoopData *eldata = &data->vbo_data[mp_index]; + memset(eldata, 0x0, sizeof(*eldata)); + BMFace *efa = bm_original_face_get(mr, mp_index); + if (efa) { + mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata); } - EXTRACT_POLY_FOREACH_MESH_END; } static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr), @@ -5188,7 +5254,8 @@ static const MeshExtract extract_fdots_edituv_data = { .finish = extract_fdots_edituv_data_finish, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data)}; + /** \} */ /* ---------------------------------------------------------------------- */ @@ -5204,6 +5271,7 @@ static void *extract_skin_roots_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; /* Exclusively for edit mode. */ BLI_assert(mr->bm); @@ -5212,7 +5280,7 @@ static void *extract_skin_roots_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); GPU_vertformat_attr_add(&format, "local_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->bm->totvert); @@ -5243,7 +5311,7 @@ static const MeshExtract extract_skin_roots = { .init = extract_skin_roots_init, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots)}; /** \} */ @@ -5255,12 +5323,12 @@ static void *extract_select_idx_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* TODO rename "color" to something more descriptive. */ GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); return GPU_vertbuf_get_data(vbo); @@ -5271,148 +5339,142 @@ static void *extract_select_idx_init(const MeshRenderData *mr, * index VBO's. We could upload the p/e/v_origindex as a buffer texture and sample it inside the * shader to output original index. */ -static void extract_poly_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_poly_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - ((uint32_t *)data)[l_index] = BM_elem_index_get(l->f); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = f_index; + } while ((l_iter = l_iter->next) != l_first); } -static void extract_edge_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edge_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - ((uint32_t *)data)[l_index] = BM_elem_index_get(l->e); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->e); + } while ((l_iter = l_iter->next) != l_first); } -static void extract_vert_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_vert_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - ((uint32_t *)data)[l_index] = BM_elem_index_get(l->v); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->v); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edge_idx_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed); - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed); - } - EXTRACT_LEDGE_FOREACH_BM_END; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed); + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed); } static void extract_vert_idx_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1); - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2); - } - EXTRACT_LEDGE_FOREACH_BM_END; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1); + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2); } static void extract_vert_idx_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve); - } - EXTRACT_LVERT_FOREACH_BM_END; + + ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve); } static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { ((uint32_t *)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; ((uint32_t *)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; ((uint32_t *)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *UNUSED(med), + const uint ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int e_index = mr->ledges[ledge_index]; - const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + const int e_index = mr->ledges[ledge_index]; + const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig; } static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1; - int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1; + int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig; } static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *UNUSED(mv), + const int lvert_index, void *data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(med, lvert_index, params, mr) - { - const int v_index = mr->lverts[lvert_index]; - const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index; - ((uint32_t *)data)[offset + lvert_index] = v_orig; - } - EXTRACT_LVERT_FOREACH_MESH_END; + + const int v_index = mr->lverts[lvert_index]; + const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index; + ((uint32_t *)data)[offset + lvert_index] = v_orig; } static const MeshExtract extract_poly_idx = { @@ -5421,7 +5483,7 @@ static const MeshExtract extract_poly_idx = { .iter_poly_mesh = extract_poly_idx_iter_poly_mesh, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx)}; static const MeshExtract extract_edge_idx = { .init = extract_select_idx_init, @@ -5431,7 +5493,7 @@ static const MeshExtract extract_edge_idx = { .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx)}; static const MeshExtract extract_vert_idx = { .init = extract_select_idx_init, @@ -5443,133 +5505,382 @@ static const MeshExtract extract_vert_idx = { .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx)}; -static void *extract_select_fdot_idx_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void *extract_fdot_idx_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* TODO rename "color" to something more descriptive. */ GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); return GPU_vertbuf_get_data(vbo); } -static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *UNUSED(f), + const int f_index, void *data) { - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - ((uint32_t *)data)[f_index] = f_index; - } - EXTRACT_POLY_FOREACH_BM_END; + ((uint32_t *)data)[f_index] = f_index; } static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *UNUSED(mp), + const int mp_index, void *data) { if (mr->p_origindex != NULL) { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index]; - } - EXTRACT_POLY_FOREACH_MESH_END; + ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index]; } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - ((uint32_t *)data)[mp_index] = mp_index; - } - EXTRACT_POLY_FOREACH_MESH_END; + ((uint32_t *)data)[mp_index] = mp_index; } } static const MeshExtract extract_fdot_idx = { - .init = extract_select_fdot_idx_init, + .init = extract_fdot_idx_init, .iter_poly_bm = extract_fdot_idx_iter_poly_bm, .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)}; + +/* ---------------------------------------------------------------------- */ +/** \name Extract + * \{ */ + +static void extracts_flags_get(const MeshExtractRunDataArray *extractors, + eMRIterType *r_iter_type, + eMRDataType *r_data_flag) +{ + eMRIterType iter_type = 0; + eMRDataType data_flag = 0; + + for (int i = 0; i < extractors->len; i++) { + const MeshExtract *extractor = extractors->items[i].extractor; + iter_type |= mesh_extract_iter_type(extractor); + data_flag |= extractor->data_flag; + } + + if (r_iter_type) { + *r_iter_type = iter_type; + } + if (r_data_flag) { + *r_data_flag = data_flag; + } +} + +BLI_INLINE void extract_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + MeshExtractRunDataArray *extractors, + MeshBufferCache *mbc) +{ + /* Multi thread. */ + for (int i = 0; i < extractors->len; i++) { + MeshExtractRunData *run_data = &extractors->items[i]; + const MeshExtract *extractor = run_data->extractor; + run_data->buffer = mesh_extract_buffer_get(extractor, mbc); + run_data->user_data = extractor->init(mr, cache, run_data->buffer); + } +} + +BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr, + const ExtractTriBMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LOOPTRI); + + EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, elt_index, params) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_looptri_bm(mr, elt, elt_index, run_data->user_data); + } + } + EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr, + const ExtractTriMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LOOPTRI); + + EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, mlt_index, params) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_looptri_mesh(mr, mlt, mlt_index, run_data->user_data); + } + } + EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr, + const ExtractPolyBMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_POLY); + + EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_poly_bm(mr, f, f_index, run_data->user_data); + } + } + EXTRACT_POLY_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr, + const ExtractPolyMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_POLY); + + EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_poly_mesh(mr, mp, mp_index, run_data->user_data); + } + } + EXTRACT_POLY_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr, + const ExtractLEdgeBMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LEDGE); + + EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_ledge_bm(mr, eed, ledge_index, run_data->user_data); + } + } + EXTRACT_LEDGE_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr, + const ExtractLEdgeMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LEDGE); + + EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_ledge_mesh(mr, med, ledge_index, run_data->user_data); + } + } + EXTRACT_LEDGE_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr, + const ExtractLVertBMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LVERT); + + EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_lvert_bm(mr, eve, lvert_index, run_data->user_data); + } + } + EXTRACT_LVERT_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr, + const ExtractLVertMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LVERT); + + EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_lvert_mesh(mr, mv, lvert_index, run_data->user_data); + } + } + EXTRACT_LVERT_FOREACH_MESH_END; +} + +BLI_INLINE void extract_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + const MeshExtractRunDataArray *extractors) +{ + for (int i = 0; i < extractors->len; i++) { + const MeshExtractRunData *run_data = &extractors->items[i]; + const MeshExtract *extractor = run_data->extractor; + if (extractor->finish) { + extractor->finish(mr, cache, run_data->buffer, run_data->user_data); + } + } +} + +/* Single Thread. */ +BLI_INLINE void extract_run_and_finish_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + MeshExtractRunDataArray *extractors, + eMRIterType iter_type, + MeshBufferCache *mbc) +{ + extract_init(mr, cache, extractors, mbc); + + bool is_mesh = mr->extract_type != MR_EXTRACT_BMESH; + if (iter_type & MR_ITER_LOOPTRI) { + if (is_mesh) { + extract_iter_looptri_mesh(mr, + &(const ExtractTriMesh_Params){ + .mlooptri = mr->mlooptri, + .tri_range = {0, mr->tri_len}, + }, + extractors); + } + else { + extract_iter_looptri_bm(mr, + &(const ExtractTriBMesh_Params){ + .looptris = mr->edit_bmesh->looptris, + .tri_range = {0, mr->tri_len}, + }, + extractors); + } + } + if (iter_type & MR_ITER_POLY) { + if (is_mesh) { + extract_iter_poly_mesh(mr, + &(const ExtractPolyMesh_Params){ + .poly_range = {0, mr->poly_len}, + }, + extractors); + } + else { + extract_iter_poly_bm(mr, + &(const ExtractPolyBMesh_Params){ + .poly_range = {0, mr->poly_len}, + }, + extractors); + } + } + if (iter_type & MR_ITER_LEDGE) { + if (is_mesh) { + extract_iter_ledge_mesh(mr, + &(const ExtractLEdgeMesh_Params){ + .ledge = mr->ledges, + .ledge_range = {0, mr->edge_loose_len}, + }, + extractors); + } + else { + extract_iter_ledge_bm(mr, + &(const ExtractLEdgeBMesh_Params){ + .ledge = mr->ledges, + .ledge_range = {0, mr->edge_loose_len}, + }, + extractors); + } + } + if (iter_type & MR_ITER_LVERT) { + if (is_mesh) { + extract_iter_lvert_mesh(mr, + &(const ExtractLVertMesh_Params){ + .lvert = mr->lverts, + .lvert_range = {0, mr->vert_loose_len}, + }, + extractors); + } + else { + extract_iter_lvert_bm(mr, + &(const ExtractLVertBMesh_Params){ + .lvert = mr->lverts, + .lvert_range = {0, mr->vert_loose_len}, + }, + extractors); + } + } + extract_finish(mr, cache, extractors); +} /** \} */ /* ---------------------------------------------------------------------- */ /** \name ExtractTaskData * \{ */ -typedef struct ExtractUserData { - void *user_data; -} ExtractUserData; - -typedef enum ExtractTaskDataType { - EXTRACT_MESH_EXTRACT, - EXTRACT_LINES_LOOSE, -} ExtractTaskDataType; - typedef struct ExtractTaskData { void *next, *prev; const MeshRenderData *mr; struct MeshBatchCache *cache; - const MeshExtract *extract; - ExtractTaskDataType tasktype; + MeshExtractRunDataArray *extractors; eMRIterType iter_type; int start, end; /** Decremented each time a task is finished. */ int32_t *task_counter; - void *buf; - ExtractUserData *user_data; + MeshBufferCache *mbc; } ExtractTaskData; -static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderData *mr, - struct MeshBatchCache *cache, - const MeshExtract *extract, - void *buf, - int32_t *task_counter) +static ExtractTaskData *extract_extract_iter_task_data_create_mesh( + const MeshRenderData *mr, + struct MeshBatchCache *cache, + MeshExtractRunDataArray *extractors, + MeshBufferCache *mbc, + int32_t *task_counter) { ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), __func__); taskdata->next = NULL; taskdata->prev = NULL; - taskdata->tasktype = EXTRACT_MESH_EXTRACT; taskdata->mr = mr; taskdata->cache = cache; - taskdata->extract = extract; - taskdata->buf = buf; + taskdata->mbc = mbc; - /* #ExtractUserData is shared between the iterations as it holds counters to detect if the + /* #UserData is shared between the iterations as it holds counters to detect if the * extraction is finished. To make sure the duplication of the user_data does not create a new * instance of the counters we allocate the user_data in its own container. * * This structure makes sure that when extract_init is called, that the user data of all * iterations are updated. */ - taskdata->user_data = MEM_callocN(sizeof(ExtractUserData), __func__); - taskdata->iter_type = mesh_extract_iter_type(extract); + taskdata->extractors = extractors; taskdata->task_counter = task_counter; + + extracts_flags_get(extractors, &taskdata->iter_type, NULL); taskdata->start = 0; taskdata->end = INT_MAX; return taskdata; } -static ExtractTaskData *extract_task_data_create_lines_loose(const MeshRenderData *mr, - struct MeshBatchCache *cache) -{ - ExtractTaskData *taskdata = MEM_callocN(sizeof(*taskdata), __func__); - taskdata->tasktype = EXTRACT_LINES_LOOSE; - taskdata->mr = mr; - taskdata->cache = cache; - return taskdata; -} - static void extract_task_data_free(void *data) { ExtractTaskData *task_data = data; - MEM_SAFE_FREE(task_data->user_data); + MEM_SAFE_FREE(task_data->extractors); MEM_freeN(task_data); } @@ -5577,113 +5888,100 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, const eMRIterType iter_type, int start, int end, - const MeshExtract *extract, - void *user_data) + MeshExtractRunDataArray *extractors) { switch (mr->extract_type) { case MR_EXTRACT_BMESH: if (iter_type & MR_ITER_LOOPTRI) { - extract->iter_looptri_bm(mr, - &(const ExtractTriBMesh_Params){ - .looptris = mr->edit_bmesh->looptris, - .tri_range = {start, min_ii(mr->tri_len, end)}, - }, - user_data); + extract_iter_looptri_bm(mr, + &(const ExtractTriBMesh_Params){ + .looptris = mr->edit_bmesh->looptris, + .tri_range = {start, min_ii(mr->tri_len, end)}, + }, + extractors); } if (iter_type & MR_ITER_POLY) { - extract->iter_poly_bm(mr, - &(const ExtractPolyBMesh_Params){ - .poly_range = {start, min_ii(mr->poly_len, end)}, - }, - user_data); + extract_iter_poly_bm(mr, + &(const ExtractPolyBMesh_Params){ + .poly_range = {start, min_ii(mr->poly_len, end)}, + }, + extractors); } if (iter_type & MR_ITER_LEDGE) { - extract->iter_ledge_bm(mr, - &(const ExtractLEdgeBMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, - }, - user_data); + extract_iter_ledge_bm(mr, + &(const ExtractLEdgeBMesh_Params){ + .ledge = mr->ledges, + .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, + }, + extractors); } if (iter_type & MR_ITER_LVERT) { - extract->iter_lvert_bm(mr, - &(const ExtractLVertBMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, - }, - user_data); + extract_iter_lvert_bm(mr, + &(const ExtractLVertBMesh_Params){ + .lvert = mr->lverts, + .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, + }, + extractors); } break; case MR_EXTRACT_MAPPED: case MR_EXTRACT_MESH: if (iter_type & MR_ITER_LOOPTRI) { - extract->iter_looptri_mesh(mr, - &(const ExtractTriMesh_Params){ - .mlooptri = mr->mlooptri, - .tri_range = {start, min_ii(mr->tri_len, end)}, - }, - user_data); + extract_iter_looptri_mesh(mr, + &(const ExtractTriMesh_Params){ + .mlooptri = mr->mlooptri, + .tri_range = {start, min_ii(mr->tri_len, end)}, + }, + extractors); } if (iter_type & MR_ITER_POLY) { - extract->iter_poly_mesh(mr, - &(const ExtractPolyMesh_Params){ - .poly_range = {start, min_ii(mr->poly_len, end)}, - }, - user_data); + extract_iter_poly_mesh(mr, + &(const ExtractPolyMesh_Params){ + .poly_range = {start, min_ii(mr->poly_len, end)}, + }, + extractors); } if (iter_type & MR_ITER_LEDGE) { - extract->iter_ledge_mesh(mr, - &(const ExtractLEdgeMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, - }, - user_data); + extract_iter_ledge_mesh(mr, + &(const ExtractLEdgeMesh_Params){ + .ledge = mr->ledges, + .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, + }, + extractors); } if (iter_type & MR_ITER_LVERT) { - extract->iter_lvert_mesh(mr, - &(const ExtractLVertMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, - }, - user_data); + extract_iter_lvert_mesh(mr, + &(const ExtractLVertMesh_Params){ + .lvert = mr->lverts, + .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, + }, + extractors); } break; } } -static void extract_init(ExtractTaskData *data) +static void extract_task_init(ExtractTaskData *data) { - if (data->tasktype == EXTRACT_MESH_EXTRACT) { - data->user_data->user_data = data->extract->init(data->mr, data->cache, data->buf); - } + extract_init(data->mr, data->cache, data->extractors, data->mbc); } -static void extract_run(void *__restrict taskdata) +static void extract_task_run(void *__restrict taskdata) { ExtractTaskData *data = (ExtractTaskData *)taskdata; - if (data->tasktype == EXTRACT_MESH_EXTRACT) { - mesh_extract_iter(data->mr, - data->iter_type, - data->start, - data->end, - data->extract, - data->user_data->user_data); + mesh_extract_iter(data->mr, data->iter_type, data->start, data->end, data->extractors); - /* If this is the last task, we do the finish function. */ - int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); - if (remainin_tasks == 0 && data->extract->finish != NULL) { - data->extract->finish(data->mr, data->cache, data->buf, data->user_data->user_data); - } - } - else if (data->tasktype == EXTRACT_LINES_LOOSE) { - extract_lines_loose_subbuffer(data->mr, data->cache); + /* If this is the last task, we do the finish function. */ + int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); + if (remainin_tasks == 0) { + extract_finish(data->mr, data->cache, data->extractors); } } -static void extract_init_and_run(void *__restrict taskdata) +static void extract_task_init_and_run(void *__restrict taskdata) { - extract_init((ExtractTaskData *)taskdata); - extract_run(taskdata); + ExtractTaskData *data = (ExtractTaskData *)taskdata; + extract_run_and_finish_init(data->mr, data->cache, data->extractors, data->iter_type, data->mbc); } /** \} */ @@ -5740,36 +6038,15 @@ static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *t /* ---------------------------------------------------------------------- */ /** \name Task Node - Extract Single Threaded * \{ */ -typedef struct ExtractSingleThreadedTaskData { - ListBase task_datas; -} ExtractSingleThreadedTaskData; - -static void extract_single_threaded_task_data_free(ExtractSingleThreadedTaskData *taskdata) -{ - BLI_assert(taskdata); - LISTBASE_FOREACH_MUTABLE (ExtractTaskData *, td, &taskdata->task_datas) { - extract_task_data_free(td); - } - BLI_listbase_clear(&taskdata->task_datas); - MEM_freeN(taskdata); -} - -static void extract_single_threaded_task_node_exec(void *__restrict task_data) -{ - ExtractSingleThreadedTaskData *extract_task_data = task_data; - LISTBASE_FOREACH (ExtractTaskData *, td, &extract_task_data->task_datas) { - extract_init_and_run(td); - } -} -static struct TaskNode *extract_single_threaded_task_node_create( - struct TaskGraph *task_graph, ExtractSingleThreadedTaskData *task_data) +static struct TaskNode *extract_single_threaded_task_node_create(struct TaskGraph *task_graph, + ExtractTaskData *task_data) { struct TaskNode *task_node = BLI_task_graph_node_create( task_graph, - extract_single_threaded_task_node_exec, + extract_task_init_and_run, task_data, - (TaskGraphNodeFreeFunction)extract_single_threaded_task_data_free); + (TaskGraphNodeFreeFunction)extract_task_data_free); return task_node; } @@ -5779,28 +6056,23 @@ static struct TaskNode *extract_single_threaded_task_node_create( /** \name Task Node - UserData Initializer * \{ */ typedef struct UserDataInitTaskData { - ListBase task_datas; - int32_t *task_counters; + ExtractTaskData *td; + int32_t task_counter; } UserDataInitTaskData; static void user_data_init_task_data_free(UserDataInitTaskData *taskdata) { BLI_assert(taskdata); - LISTBASE_FOREACH_MUTABLE (ExtractTaskData *, td, &taskdata->task_datas) { - extract_task_data_free(td); - } - BLI_listbase_clear(&taskdata->task_datas); - MEM_SAFE_FREE(taskdata->task_counters); + extract_task_data_free(taskdata->td); MEM_freeN(taskdata); } static void user_data_init_task_data_exec(void *__restrict task_data) { UserDataInitTaskData *extract_task_data = task_data; - LISTBASE_FOREACH (ExtractTaskData *, td, &extract_task_data->task_datas) { - extract_init(td); - } + ExtractTaskData *taskdata_base = extract_task_data->td; + extract_task_init(taskdata_base); } static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_graph, @@ -5815,6 +6087,53 @@ static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_g } /** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Override extractors + * Extractors can be overridden. When overridden a specialized version is used. The next functions + * would check for any needed overrides and usage of the specialized version. + * \{ */ + +static const MeshExtract *mesh_extract_override_hq_normals(const MeshExtract *extractor) +{ + if (extractor == &extract_pos_nor) { + return &extract_pos_nor_hq; + } + if (extractor == &extract_lnor) { + return &extract_lnor_hq; + } + if (extractor == &extract_tan) { + return &extract_tan_hq; + } + if (extractor == &extract_fdots_nor) { + return &extract_fdots_nor_hq; + } + return extractor; +} + +static const MeshExtract *mesh_extract_override_loose_lines(const MeshExtract *extractor) +{ + if (extractor == &extract_lines) { + return &extract_lines_with_lines_loose; + } + return extractor; +} + +static const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, + const bool do_hq_normals, + const bool do_lines_loose_subbuffer) +{ + if (do_hq_normals) { + extractor = mesh_extract_override_hq_normals(extractor); + } + if (do_lines_loose_subbuffer) { + extractor = mesh_extract_override_loose_lines(extractor); + } + return extractor; +} + +/** \} */ + /* ---------------------------------------------------------------------- */ /** \name Extract Loop * \{ */ @@ -5832,93 +6151,79 @@ static void extract_range_task_create(struct TaskGraph *task_graph, taskdata->start = start; taskdata->end = start + length; struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, extract_run, taskdata, MEM_freeN); + task_graph, extract_task_run, taskdata, MEM_freeN); BLI_task_graph_edge_create(task_node_user_data_init, task_node); } -static void extract_task_create(struct TaskGraph *task_graph, - struct TaskNode *task_node_mesh_render_data, - struct TaskNode *task_node_user_data_init, - ListBase *single_threaded_task_datas, - ListBase *user_data_init_task_datas, - const Scene *scene, - const MeshRenderData *mr, - MeshBatchCache *cache, - const MeshExtract *extract, - void *buf, - int32_t *task_counter) +static int extract_range_task_num_elements_get(const MeshRenderData *mr, + const eMRIterType iter_type) { - BLI_assert(scene != NULL); - const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || - GPU_use_hq_normals_workaround(); - if (do_hq_normals) { - if (extract == &extract_lnor) { - extract = &extract_lnor_hq; - } - else if (extract == &extract_pos_nor) { - extract = &extract_pos_nor_hq; - } - else if (extract == &extract_tan) { - extract = &extract_tan_hq; - } - else if (extract == &extract_fdots_nor) { - extract = &extract_fdots_nor_hq; - } + /* Divide task into sensible chunks. */ + int iter_len = 0; + if (iter_type & MR_ITER_LOOPTRI) { + iter_len += mr->tri_len; } + if (iter_type & MR_ITER_POLY) { + iter_len += mr->poly_len; + } + if (iter_type & MR_ITER_LEDGE) { + iter_len += mr->edge_loose_len; + } + if (iter_type & MR_ITER_LVERT) { + iter_len += mr->vert_loose_len; + } + return iter_len; +} - /* Divide extraction of the VBO/IBO into sensible chunks of works. */ - ExtractTaskData *taskdata = extract_task_data_create_mesh_extract( - mr, cache, extract, buf, task_counter); +static int extract_range_task_chunk_size_get(const MeshRenderData *mr, + const eMRIterType iter_type, + const int num_threads) +{ + /* Divide task into sensible chunks. */ + const int num_elements = extract_range_task_num_elements_get(mr, iter_type); + int range_len = (num_elements + num_threads) / num_threads; + CLAMP_MIN(range_len, CHUNK_SIZE); + return range_len; +} - /* Simple heuristic. */ - const int chunk_size = 8192; - const bool use_thread = (mr->loop_len + mr->loop_loose_len) > chunk_size; - if (use_thread && extract->use_threading) { - - /* Divide task into sensible chunks. */ - if (taskdata->iter_type & MR_ITER_LOOPTRI) { - for (int i = 0; i < mr->tri_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_LOOPTRI, i, chunk_size); - } - } - if (taskdata->iter_type & MR_ITER_POLY) { - for (int i = 0; i < mr->poly_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_POLY, i, chunk_size); - } - } - if (taskdata->iter_type & MR_ITER_LEDGE) { - for (int i = 0; i < mr->edge_loose_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_LEDGE, i, chunk_size); - } +static void extract_task_in_ranges_create(struct TaskGraph *task_graph, + struct TaskNode *task_node_user_data_init, + ExtractTaskData *taskdata_base, + const int num_threads) +{ + const MeshRenderData *mr = taskdata_base->mr; + const int range_len = extract_range_task_chunk_size_get( + mr, taskdata_base->iter_type, num_threads); + + if (taskdata_base->iter_type & MR_ITER_LOOPTRI) { + for (int i = 0; i < mr->tri_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LOOPTRI, i, range_len); } - if (taskdata->iter_type & MR_ITER_LVERT) { - for (int i = 0; i < mr->vert_loose_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_LVERT, i, chunk_size); - } + } + if (taskdata_base->iter_type & MR_ITER_POLY) { + for (int i = 0; i < mr->poly_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_POLY, i, range_len); } - BLI_addtail(user_data_init_task_datas, taskdata); } - else if (use_thread) { - /* One task for the whole VBO. */ - (*task_counter)++; - struct TaskNode *one_task = BLI_task_graph_node_create( - task_graph, extract_init_and_run, taskdata, extract_task_data_free); - BLI_task_graph_edge_create(task_node_mesh_render_data, one_task); + if (taskdata_base->iter_type & MR_ITER_LEDGE) { + for (int i = 0; i < mr->edge_loose_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LEDGE, i, range_len); + } } - else { - /* Single threaded extraction. */ - (*task_counter)++; - BLI_addtail(single_threaded_task_datas, taskdata); + if (taskdata_base->iter_type & MR_ITER_LVERT) { + for (int i = 0; i < mr->vert_loose_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LVERT, i, range_len); + } } } void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, - MeshBufferCache mbc, + MeshBufferCache *mbc, MeshBufferExtractionCache *extraction_cache, Mesh *me, @@ -5935,8 +6240,8 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const bool use_hide) { /* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph. - * This sub-graph starts with an extract_render_data_node. This fills/converts the required data - * from Mesh. + * This sub-graph starts with an extract_render_data_node. This fills/converts the required + * data from Mesh. * * Small extractions and extractions that can't be multi-threaded are grouped in a single * `extract_single_threaded_task_node`. @@ -5964,69 +6269,67 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, * +-----> | extract_task2_loop_3 | * +----------------------+ */ - eMRIterType iter_flag = 0; - eMRDataType data_flag = 0; + const bool do_lines_loose_subbuffer = mbc->ibo.lines_loose != NULL; + const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || + GPU_use_hq_normals_workaround(); - const bool do_lines_loose_subbuffer = mbc.ibo.lines_loose != NULL; - bool do_extract = false; + /* Create an array containing all the extractors that needs to be executed. */ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); -#define TEST_ASSIGN(type, type_lowercase, name) \ +#define EXTRACT_ADD_REQUESTED(type, type_lowercase, name) \ do { \ - if (DRW_TEST_ASSIGN_##type(mbc.type_lowercase.name)) { \ - iter_flag |= mesh_extract_iter_type(&extract_##name); \ - data_flag |= extract_##name.data_flag; \ - do_extract = true; \ + if (DRW_##type_lowercase##_requested(mbc->type_lowercase.name)) { \ + const MeshExtract *extractor = mesh_extract_override_get( \ + &extract_##name, do_hq_normals, do_lines_loose_subbuffer); \ + mesh_extract_run_data_array_add(&extractors, extractor); \ } \ } while (0) - TEST_ASSIGN(VBO, vbo, pos_nor); - TEST_ASSIGN(VBO, vbo, lnor); - TEST_ASSIGN(VBO, vbo, uv); - TEST_ASSIGN(VBO, vbo, tan); - TEST_ASSIGN(VBO, vbo, vcol); - TEST_ASSIGN(VBO, vbo, sculpt_data); - TEST_ASSIGN(VBO, vbo, orco); - TEST_ASSIGN(VBO, vbo, edge_fac); - TEST_ASSIGN(VBO, vbo, weights); - TEST_ASSIGN(VBO, vbo, edit_data); - TEST_ASSIGN(VBO, vbo, edituv_data); - TEST_ASSIGN(VBO, vbo, edituv_stretch_area); - TEST_ASSIGN(VBO, vbo, edituv_stretch_angle); - TEST_ASSIGN(VBO, vbo, mesh_analysis); - TEST_ASSIGN(VBO, vbo, fdots_pos); - TEST_ASSIGN(VBO, vbo, fdots_nor); - TEST_ASSIGN(VBO, vbo, fdots_uv); - TEST_ASSIGN(VBO, vbo, fdots_edituv_data); - TEST_ASSIGN(VBO, vbo, poly_idx); - TEST_ASSIGN(VBO, vbo, edge_idx); - TEST_ASSIGN(VBO, vbo, vert_idx); - TEST_ASSIGN(VBO, vbo, fdot_idx); - TEST_ASSIGN(VBO, vbo, skin_roots); - - TEST_ASSIGN(IBO, ibo, tris); - TEST_ASSIGN(IBO, ibo, lines); - TEST_ASSIGN(IBO, ibo, points); - TEST_ASSIGN(IBO, ibo, fdots); - TEST_ASSIGN(IBO, ibo, lines_paint_mask); - TEST_ASSIGN(IBO, ibo, lines_adjacency); - TEST_ASSIGN(IBO, ibo, edituv_tris); - TEST_ASSIGN(IBO, ibo, edituv_lines); - TEST_ASSIGN(IBO, ibo, edituv_points); - TEST_ASSIGN(IBO, ibo, edituv_fdots); - - if (!do_extract) { + EXTRACT_ADD_REQUESTED(VBO, vbo, pos_nor); + EXTRACT_ADD_REQUESTED(VBO, vbo, lnor); + EXTRACT_ADD_REQUESTED(VBO, vbo, uv); + EXTRACT_ADD_REQUESTED(VBO, vbo, tan); + EXTRACT_ADD_REQUESTED(VBO, vbo, vcol); + EXTRACT_ADD_REQUESTED(VBO, vbo, sculpt_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, orco); + EXTRACT_ADD_REQUESTED(VBO, vbo, edge_fac); + EXTRACT_ADD_REQUESTED(VBO, vbo, weights); + EXTRACT_ADD_REQUESTED(VBO, vbo, edit_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_area); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_angle); + EXTRACT_ADD_REQUESTED(VBO, vbo, mesh_analysis); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_pos); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_nor); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_uv); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_edituv_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, poly_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, edge_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, vert_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdot_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, skin_roots); + + EXTRACT_ADD_REQUESTED(IBO, ibo, tris); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines); + EXTRACT_ADD_REQUESTED(IBO, ibo, points); + EXTRACT_ADD_REQUESTED(IBO, ibo, fdots); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines_paint_mask); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines_adjacency); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_tris); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_lines); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_points); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_fdots); + +#undef EXTRACT_ADD_REQUESTED + + if (extractors.len == 0) { return; } - if (do_lines_loose_subbuffer) { - iter_flag |= MR_ITER_LEDGE; - } - -#undef TEST_ASSIGN - -#ifdef DEBUG_TIME - double rdata_start = PIL_check_seconds_timer(); -#endif + eMRIterType iter_type; + eMRDataType data_flag; + extracts_flags_get(&extractors, &iter_type, &data_flag); MeshRenderData *mr = mesh_render_data_create(me, extraction_cache, @@ -6038,124 +6341,91 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, do_uvedit, cd_layer_used, ts, - iter_flag); + iter_type); mr->use_hide = use_hide; mr->use_subsurf_fdots = use_subsurf_fdots; mr->use_final_mesh = do_final; #ifdef DEBUG_TIME - double rdata_end = PIL_check_seconds_timer(); + rdata_end = PIL_check_seconds_timer(); #endif - size_t counters_size = (sizeof(mbc) / sizeof(void *)) * sizeof(int32_t); - int32_t *task_counters = MEM_callocN(counters_size, __func__); - int counter_used = 0; - struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( - task_graph, mr, iter_flag, data_flag); - ExtractSingleThreadedTaskData *single_threaded_task_data = MEM_callocN( - sizeof(ExtractSingleThreadedTaskData), __func__); - UserDataInitTaskData *user_data_init_task_data = MEM_callocN(sizeof(UserDataInitTaskData), - __func__); - user_data_init_task_data->task_counters = task_counters; - struct TaskNode *task_node_user_data_init = user_data_init_task_node_create( - task_graph, user_data_init_task_data); - -#define EXTRACT(buf, name) \ - if (mbc.buf.name) { \ - extract_task_create(task_graph, \ - task_node_mesh_render_data, \ - task_node_user_data_init, \ - &single_threaded_task_data->task_datas, \ - &user_data_init_task_data->task_datas, \ - scene, \ - mr, \ - cache, \ - &extract_##name, \ - mbc.buf.name, \ - &task_counters[counter_used++]); \ - } \ - ((void)0) - - EXTRACT(vbo, pos_nor); - EXTRACT(vbo, lnor); - EXTRACT(vbo, uv); - EXTRACT(vbo, tan); - EXTRACT(vbo, vcol); - EXTRACT(vbo, sculpt_data); - EXTRACT(vbo, orco); - EXTRACT(vbo, edge_fac); - EXTRACT(vbo, weights); - EXTRACT(vbo, edit_data); - EXTRACT(vbo, edituv_data); - EXTRACT(vbo, edituv_stretch_area); - EXTRACT(vbo, edituv_stretch_angle); - EXTRACT(vbo, mesh_analysis); - EXTRACT(vbo, fdots_pos); - EXTRACT(vbo, fdots_nor); - EXTRACT(vbo, fdots_uv); - EXTRACT(vbo, fdots_edituv_data); - EXTRACT(vbo, poly_idx); - EXTRACT(vbo, edge_idx); - EXTRACT(vbo, vert_idx); - EXTRACT(vbo, fdot_idx); - EXTRACT(vbo, skin_roots); - - EXTRACT(ibo, tris); - if (mbc.ibo.lines) { - /* When `lines` and `lines_loose` are requested, schedule lines extraction that also creates - * the `lines_loose` sub-buffer. */ - const MeshExtract *lines_extractor = do_lines_loose_subbuffer ? - &extract_lines_with_lines_loose : - &extract_lines; - extract_task_create(task_graph, - task_node_mesh_render_data, - task_node_user_data_init, - &single_threaded_task_data->task_datas, - &user_data_init_task_data->task_datas, - scene, - mr, - cache, - lines_extractor, - mbc.ibo.lines, - &task_counters[counter_used++]); - } - else { - if (do_lines_loose_subbuffer) { - ExtractTaskData *taskdata = extract_task_data_create_lines_loose(mr, cache); - BLI_addtail(&single_threaded_task_data->task_datas, taskdata); - } - } - EXTRACT(ibo, points); - EXTRACT(ibo, fdots); - EXTRACT(ibo, lines_paint_mask); - EXTRACT(ibo, lines_adjacency); - EXTRACT(ibo, edituv_tris); - EXTRACT(ibo, edituv_lines); - EXTRACT(ibo, edituv_points); - EXTRACT(ibo, edituv_fdots); - - /* Only create the edge when there is user data that needs to be initialized. - * The task is still part of the graph so the task_data will be freed when the graph is freed. - */ - if (!BLI_listbase_is_empty(&user_data_init_task_data->task_datas)) { - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init); - } + task_graph, mr, iter_type, data_flag); - if (!BLI_listbase_is_empty(&single_threaded_task_data->task_datas)) { - struct TaskNode *task_node = extract_single_threaded_task_node_create( - task_graph, single_threaded_task_data); - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); + /* Simple heuristic. */ + const bool use_thread = (mr->loop_len + mr->loop_loose_len) > CHUNK_SIZE; + + if (use_thread) { + uint threads_to_use = 0; + + /* First run the requested extractors that do not support asynchronous ranges. */ + for (int i = 0; i < extractors.len; i++) { + const MeshExtract *extractor = extractors.items[i].extractor; + if (!extractor->use_threading) { + MeshExtractRunDataArray *single_threaded_extractors = MEM_callocN( + sizeof(MeshExtractRunDataArray), + "mesh_buffer_cache_create_requested.single_threaded_extractors"); + mesh_extract_run_data_array_add(single_threaded_extractors, extractor); + ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( + mr, cache, single_threaded_extractors, mbc, NULL); + struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, + taskdata); + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); + } + threads_to_use++; + } + + /* Distribute the remaining extractors into ranges per core. */ + MeshExtractRunDataArray *multi_threaded_extractors = MEM_callocN( + sizeof(MeshExtractRunDataArray), + "mesh_buffer_cache_create_requested.multi_threaded_extractors"); + mesh_extract_run_data_array_filter_threading(&extractors, multi_threaded_extractors); + if (multi_threaded_extractors->len) { + /* + * Determine the number of thread to use for multithreading. + * Thread can be used for single threaded tasks. These typically take longer to execute so + * fill the rest of the threads for range operations. + */ + int num_threads = BLI_task_scheduler_num_threads(); + if (threads_to_use < num_threads) { + num_threads -= threads_to_use; + } + + UserDataInitTaskData *user_data_init_task_data = MEM_callocN( + sizeof(UserDataInitTaskData), + "mesh_buffer_cache_create_requested.user_data_init_task_data"); + struct TaskNode *task_node_user_data_init = user_data_init_task_node_create( + task_graph, user_data_init_task_data); + + user_data_init_task_data->td = extract_extract_iter_task_data_create_mesh( + mr, cache, multi_threaded_extractors, mbc, &user_data_init_task_data->task_counter); + + extract_task_in_ranges_create( + task_graph, task_node_user_data_init, user_data_init_task_data->td, num_threads); + + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init); + } + else { + /* No tasks created freeing extractors list. */ + MEM_freeN(multi_threaded_extractors); + } } else { - extract_single_threaded_task_data_free(single_threaded_task_data); + /* Run all requests on the same thread. */ + MeshExtractRunDataArray *extractors_copy = MEM_mallocN( + sizeof(MeshExtractRunDataArray), "mesh_buffer_cache_create_requested.extractors_copy"); + memcpy(extractors_copy, &extractors, sizeof(MeshExtractRunDataArray)); + ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( + mr, cache, extractors_copy, mbc, NULL); + + struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata); + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); } /* Trigger the sub-graph for this mesh. */ BLI_task_graph_node_push_work(task_node_mesh_render_data); -#undef EXTRACT - #ifdef DEBUG_TIME BLI_task_graph_work_and_wait(task_graph); double end = PIL_check_seconds_timer(); diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 5cf99db5485..ee6a47e3dc6 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -843,6 +843,9 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata, int edges_len_capacity = curve_render_data_overlay_edges_len_get(rdata) * 2; int vbo_len_used = 0; +#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL)) +#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : NULL)) + if (DRW_TEST_ASSIGN_VBO(vbo_pos)) { GPU_vertbuf_init_with_format(vbo_pos, &format_pos); GPU_vertbuf_data_alloc(vbo_pos, verts_len_capacity); @@ -863,6 +866,9 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata, GPU_indexbuf_init(elbp_lines, GPU_PRIM_LINES, edges_len_capacity, verts_len_capacity); } +#undef DRW_TEST_ASSIGN_VBO +#undef DRW_TEST_ASSIGN_IBO + int nu_id = 0; for (Nurb *nu = (Nurb *)rdata->nurbs->first; nu; nu = nu->next, nu_id++) { const BezTriple *bezt = nu->bezt; diff --git a/source/blender/draw/intern/draw_cache_impl_displist.c b/source/blender/draw/intern/draw_cache_impl_displist.c index d606f70db9e..ee16cb1a022 100644 --- a/source/blender/draw/intern/draw_cache_impl_displist.c +++ b/source/blender/draw/intern/draw_cache_impl_displist.c @@ -532,6 +532,8 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan(ListBase *lb, GPUVertBufRaw uv_step = {0}; GPUVertBufRaw tan_step = {0}; +#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL)) + if (DRW_TEST_ASSIGN_VBO(vbo_pos_nor)) { GPU_vertbuf_init_with_format(vbo_pos_nor, do_hq_normals ? &format_pos_nor_hq : &format_pos_nor); @@ -550,6 +552,8 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan(ListBase *lb, GPU_vertbuf_attr_get_raw_data(vbo_tan, tan_id, &tan_step); } +#undef DRW_TEST_ASSIGN_VBO + BKE_displist_normals_add(lb); LISTBASE_FOREACH (const DispList *, dl, lb) { diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index c2dc9b3ad8d..0d91432d4e5 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -1559,7 +1559,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (do_uvcage) { mesh_buffer_cache_create_requested(task_graph, cache, - cache->uv_cage, + &cache->uv_cage, &cache->uv_cage_extraction_cache, me, is_editmode, @@ -1578,7 +1578,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (do_cage) { mesh_buffer_cache_create_requested(task_graph, cache, - cache->cage, + &cache->cage, &cache->cage_extraction_cache, me, is_editmode, @@ -1596,7 +1596,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, - cache->final, + &cache->final, &cache->final_extraction_cache, me, is_editmode, diff --git a/source/blender/draw/intern/draw_cache_inline.h b/source/blender/draw/intern/draw_cache_inline.h index bfc714e5d6a..6e537a3bffa 100644 --- a/source/blender/draw/intern/draw_cache_inline.h +++ b/source/blender/draw/intern/draw_cache_inline.h @@ -40,10 +40,6 @@ (flag |= DRW_ibo_requested(ibo) ? (value) : 0) #endif -/* Test and assign NULL if test fails */ -#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL)) -#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : NULL)) - BLI_INLINE GPUBatch *DRW_batch_request(GPUBatch **batch) { /* XXX TODO(fclem): We are writing to batch cache here. Need to make this thread safe. */ -- cgit v1.2.3 From b862916eafc809cc1d93ffc8cdfc48402df30df0 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 31 May 2021 17:27:02 +0200 Subject: VSE: Fix missing cache invalidation Fixes T88606 --- source/blender/makesrna/intern/rna_color.c | 2 +- source/blender/makesrna/intern/rna_space.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 206ebc2cb14..54f9a93d90a 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -650,7 +650,7 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain, seq->strip->proxy->anim = NULL; } - SEQ_relations_invalidate_cache_preprocessed(scene, seq); + SEQ_relations_invalidate_cache_raw(scene, seq); } else { SEQ_ALL_BEGIN (scene->ed, seq) { diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index d744f67c6f6..0af2572a4bd 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -5466,7 +5466,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_PROXIES); RNA_def_property_ui_text( prop, "Use Proxies", "Use optimized files for faster scrubbing when available"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, "rna_SequenceEditor_update_cache"); /* grease pencil */ prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE); -- cgit v1.2.3 From 46a14bd6a317ead29dfdbf038bd30d49a656249e Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 31 May 2021 17:24:35 +0200 Subject: Fix T88670: Load Previous Settings does not copy symlinks The same code existed in 2.82 and earlier so this should be safe. Removing the custom implementation of shutil.copytree in f34d5d9 did not correctly add back the option to copy symlinks. --- release/scripts/startup/bl_operators/userpref.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py index 7547184dc04..564a7b69957 100644 --- a/release/scripts/startup/bl_operators/userpref.py +++ b/release/scripts/startup/bl_operators/userpref.py @@ -144,7 +144,7 @@ class PREFERENCES_OT_copy_prev(Operator): def execute(self, _context): import shutil - shutil.copytree(self._old_path(), self._new_path(), dirs_exist_ok=True) + shutil.copytree(self._old_path(), self._new_path(), dirs_exist_ok=True, symlinks=True) # reload preferences and recent-files.txt bpy.ops.wm.read_userpref() -- cgit v1.2.3 From d647e730fbc23233b572594eeb6083978bdb882d Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Mon, 31 May 2021 09:59:29 -0600 Subject: Win: Fix warnings as errors being off for bmesh bf_bmesh historically always build with the /WX flag on windows making all warnings errors, somewhere along the way this has broken for msbuild, ninja still exhibits the expected behaviour. The flags are still passed to the target, and I've validated they are there when the add_library call fires, but they somehow never make it to the generated msbuild project files. I suspect this is a cmake bug but I'm seemingly unable to extract a repro case to file a bug upstream. Setting the same options target_compile_options seems to work, I'm not happy about the unexplained nature of the breakage but this will have to do for now. --- source/blender/bmesh/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index c215cf69e3a..92064b3d040 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -182,10 +182,6 @@ set(LIB extern_rangetree ) -if(MSVC AND NOT MSVC_CLANG) - string(APPEND CMAKE_C_FLAGS " /WX /wd4101") -endif() - if(WITH_BULLET) list(APPEND INC_SYS ${BULLET_INCLUDE_DIRS} @@ -225,6 +221,10 @@ endif() blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") +if(MSVC AND NOT MSVC_CLANG) + target_compile_options(bf_bmesh PRIVATE /WX /wd4101) +endif() + if(WITH_GTESTS) set(TEST_SRC tests/bmesh_core_test.cc -- cgit v1.2.3 From 261a10edb0a9da53b2554ca64bcf445a8b9b3d9f Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Mon, 31 May 2021 18:12:44 +0200 Subject: Display source video fps in the VSE Now FPS is displayed in the video source for videos to provide easy access. Reviewed By: Richard Antalik Differential Revision: http://developer.blender.org/D11441 --- release/scripts/startup/bl_ui/space_sequencer.py | 12 ++++++++++-- source/blender/makesdna/DNA_sequence_types.h | 1 + source/blender/makesrna/intern/rna_sequencer.c | 5 +++++ source/blender/sequencer/intern/render.c | 6 ++++++ source/blender/sequencer/intern/strip_add.c | 13 ++++++++++++- 5 files changed, 34 insertions(+), 3 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index b24b6e84939..e9bfe6cd4e2 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1398,8 +1398,8 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): box.template_image_stereo_3d(strip.stereo_3d_format) # Resolution. - col = layout.column(align=True) - col = col.box() + col = layout.box() + col = col.column(align=True) split = col.split(factor=0.5, align=False) split.alignment = 'RIGHT' split.label(text="Resolution") @@ -1409,6 +1409,14 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): split.label(text="%dx%d" % size, translate=False) else: split.label(text="None") + #FPS + if elem.orig_fps: + split = col.split(factor=0.5, align=False) + split.alignment = 'RIGHT' + split.label(text="FPS") + split.alignment = 'LEFT' + split.label(text="%.2f" % elem.orig_fps, translate=False) + class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel): diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 4b95dd41b30..f59f51ea28a 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -57,6 +57,7 @@ typedef struct StripElem { char name[256]; /** Ignore when zeroed. */ int orig_width, orig_height; + float orig_fps; } StripElem; typedef struct StripCrop { diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 9ba92431723..8fbad449cf6 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1346,6 +1346,11 @@ static void rna_def_strip_element(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "orig_height"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Orig Height", "Original image height"); + + prop = RNA_def_property(srna, "orig_fps", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "orig_fps"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Orig FPS", "Original frames per second"); } static void rna_def_strip_crop(BlenderRNA *brna) diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index d881c90a1e0..8ed769880a4 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -1237,6 +1237,12 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, } if (*r_is_proxy_image == false) { + if (sanim && sanim->anim) { + short fps_denom; + float fps_num; + IMB_anim_get_fps(sanim->anim, &fps_denom, &fps_num, true); + seq->strip->stripdata->orig_fps = fps_denom / fps_num; + } seq->strip->stripdata->orig_width = ibuf->x; seq->strip->stripdata->orig_height = ibuf->y; } diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index 5ec2269b993..64671aeb265 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -548,15 +548,25 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */ + float video_fps = 0.0f; + if (anim_arr[0] != NULL) { seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]); seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); IMB_anim_load_metadata(anim_arr[0]); + short fps_denom; + float fps_num; + + IMB_anim_get_fps(anim_arr[0], &fps_denom, &fps_num, true); + + video_fps = fps_denom / fps_num; + /* Adjust scene's frame rate settings to match. */ if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) { - IMB_anim_get_fps(anim_arr[0], &scene->r.frs_sec, &scene->r.frs_sec_base, true); + scene->r.frs_sec = fps_denom; + scene->r.frs_sec_base = fps_num; } /* Set initial scale based on load_data->fit_method. */ @@ -577,6 +587,7 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem"); strip->stripdata->orig_width = orig_width; strip->stripdata->orig_height = orig_height; + strip->stripdata->orig_fps = video_fps; BLI_split_dirfile(load_data->path, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name)); seq_add_set_view_transform(scene, seq, load_data); -- cgit v1.2.3 From 875a8a6c79b0c5da1715b70eaa58b12f9b816767 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Mon, 31 May 2021 10:06:38 -0700 Subject: Cleanup: Replace fseek() calls with BLI_fseek() The fseek() function on Windows only accepts a 32-bit long offset argument. Because of this we have our own version, BLI_fseek(), which will use 64-bit _fseeki64() on Windows. This patch just replaces some fseek() calls with BLI_fseek(). Differential Revision: https://developer.blender.org/D11430 Reviewed by Brecht Van Lommel --- source/blender/blenfont/intern/blf_font_win32_compat.c | 6 +++--- source/blender/blenkernel/intern/customdata_file.c | 8 ++++---- source/blender/blenlib/intern/fileops.c | 4 ++-- source/blender/sequencer/intern/image_cache.c | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font_win32_compat.c b/source/blender/blenfont/intern/blf_font_win32_compat.c index 7d130204c07..e573d3bd224 100644 --- a/source/blender/blenfont/intern/blf_font_win32_compat.c +++ b/source/blender/blenfont/intern/blf_font_win32_compat.c @@ -66,7 +66,7 @@ static unsigned long ft_ansi_stream_io(FT_Stream stream, file = STREAM_FILE(stream); if (stream->pos != offset) { - fseek(file, offset, SEEK_SET); + BLI_fseek(file, offset, SEEK_SET); } return fread(buffer, 1, count, file); @@ -93,7 +93,7 @@ static FT_Error FT_Stream_Open__win32_compat(FT_Stream stream, const char *filep return FT_THROW(Cannot_Open_Resource); } - fseek(file, 0, SEEK_END); + BLI_fseek(file, 0LL, SEEK_END); stream->size = ftell(file); if (!stream->size) { fprintf(stderr, @@ -104,7 +104,7 @@ static FT_Error FT_Stream_Open__win32_compat(FT_Stream stream, const char *filep return FT_THROW(Cannot_Open_Stream); } - fseek(file, 0, SEEK_SET); + BLI_fseek(file, 0LL, SEEK_SET); stream->descriptor.pointer = file; stream->read = ft_ansi_stream_io; diff --git a/source/blender/blenkernel/intern/customdata_file.c b/source/blender/blenkernel/intern/customdata_file.c index 470c2f2d246..4aaecc26eff 100644 --- a/source/blender/blenkernel/intern/customdata_file.c +++ b/source/blender/blenkernel/intern/customdata_file.c @@ -167,7 +167,7 @@ static bool cdf_read_header(CDataFile *cdf) offset += header->structbytes; header->structbytes = sizeof(CDataFileHeader); - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } @@ -201,7 +201,7 @@ static bool cdf_read_header(CDataFile *cdf) mesh->structbytes = sizeof(CDataFileMeshHeader); } - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } @@ -233,7 +233,7 @@ static bool cdf_read_header(CDataFile *cdf) offset += layer->structbytes; layer->structbytes = sizeof(CDataFileLayer); - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } } @@ -321,7 +321,7 @@ bool cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay) offset += cdf->layer[a].datasize; } - return (fseek(cdf->readf, offset, SEEK_SET) == 0); + return (BLI_fseek(cdf->readf, offset, SEEK_SET) == 0); } bool cdf_read_data(CDataFile *cdf, unsigned int size, void *data) diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 106bd5bc793..107c27da6a2 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -171,7 +171,7 @@ size_t BLI_gzip_mem_to_file_at_pos( z_stream strm; unsigned char out[CHUNK]; - fseek(file, gz_stream_offset, 0); + BLI_fseek(file, gz_stream_offset, 0); strm.zalloc = Z_NULL; strm.zfree = Z_NULL; @@ -217,7 +217,7 @@ size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t g size_t chunk = 256 * 1024; unsigned char in[CHUNK]; - fseek(file, gz_stream_offset, 0); + BLI_fseek(file, gz_stream_offset, 0); strm.zalloc = Z_NULL; strm.zfree = Z_NULL; diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c index a0c95c1c197..5ccf2a027b0 100644 --- a/source/blender/sequencer/intern/image_cache.c +++ b/source/blender/sequencer/intern/image_cache.c @@ -518,7 +518,7 @@ static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntr static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) { - fseek(file, 0, 0); + BLI_fseek(file, 0LL, SEEK_SET); const size_t num_items_read = fread(header, sizeof(*header), 1, file); if (num_items_read < 1) { BLI_assert(!"unable to read disk cache header"); @@ -540,7 +540,7 @@ static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) static size_t seq_disk_cache_write_header(FILE *file, DiskCacheHeader *header) { - fseek(file, 0, 0); + BLI_fseek(file, 0LL, SEEK_SET); return fwrite(header, sizeof(*header), 1, file); } -- cgit v1.2.3 From 25316ef9d735281a83b41f9e14fa3c712b1ada82 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 31 May 2021 16:21:24 +0200 Subject: Cycles: optimize 3D viewport rendering with camera passepartout If the area outside the camera is fully opaque, don't render it. Contributed by Kdaf. Differential Revision: https://developer.blender.org/D11182 --- intern/cycles/blender/blender_camera.cpp | 27 ++++++++++++++++++--------- intern/cycles/blender/blender_session.cpp | 13 ++++++------- intern/cycles/blender/blender_sync.h | 3 +-- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index b31841801d8..6954c5c2f26 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -83,6 +83,8 @@ struct BlenderCamera { BoundBox2D pano_viewplane; BoundBox2D viewport_camera_border; + float passepartout_alpha; + Transform matrix; float offscreen_dicing_scale; @@ -125,6 +127,7 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende bcam->pano_viewplane.top = 1.0f; bcam->viewport_camera_border.right = 1.0f; bcam->viewport_camera_border.top = 1.0f; + bcam->passepartout_alpha = 0.5f; bcam->offscreen_dicing_scale = 1.0f; bcam->matrix = transform_identity(); @@ -212,6 +215,8 @@ static void blender_camera_from_object(BlenderCamera *bcam, bcam->lens = b_camera.lens(); + bcam->passepartout_alpha = b_camera.show_passepartout() ? b_camera.passepartout_alpha() : 0.0f; + if (b_camera.dof().use_dof()) { /* allow f/stop number to change aperture_size but still * give manual control over aperture radius */ @@ -834,15 +839,19 @@ static void blender_camera_border(BlenderCamera *bcam, full_border, &bcam->viewport_camera_border); - if (!b_render.use_border()) { + if (b_render.use_border()) { + bcam->border.left = b_render.border_min_x(); + bcam->border.right = b_render.border_max_x(); + bcam->border.bottom = b_render.border_min_y(); + bcam->border.top = b_render.border_max_y(); + } + else if (bcam->passepartout_alpha == 1.0f) { + bcam->border = full_border; + } + else { return; } - bcam->border.left = b_render.border_min_x(); - bcam->border.right = b_render.border_max_x(); - bcam->border.bottom = b_render.border_min_y(); - bcam->border.top = b_render.border_max_y(); - /* Determine viewport subset matching camera border. */ blender_camera_border_subset(b_engine, b_render, @@ -885,8 +894,7 @@ void BlenderSync::sync_view(BL::SpaceView3D &b_v3d, } } -BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render, - BL::SpaceView3D &b_v3d, +BufferParams BlenderSync::get_buffer_params(BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, int width, @@ -902,7 +910,8 @@ BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render, if (b_v3d && b_rv3d && b_rv3d.view_perspective() != BL::RegionView3D::view_perspective_CAMERA) use_border = b_v3d.use_render_border(); else - use_border = b_render.use_border(); + /* the camera can always have a passepartout */ + use_border = true; if (use_border) { /* border render */ diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 89854f6a0e5..29de886e4ff 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -155,7 +155,7 @@ void BlenderSession::create_session() /* set buffer parameters */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); session->reset(buffer_params, session_params.samples); b_engine.use_highlight_tiles(session_params.progressive_refine == false); @@ -242,8 +242,7 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL); BL::RegionView3D b_null_region_view3d(PointerRNA_NULL); - BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, - b_null_space_view3d, + BufferParams buffer_params = BlenderSync::get_buffer_params(b_null_space_view3d, b_null_region_view3d, scene->camera, width, @@ -486,7 +485,7 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background, b_view_layer); BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); /* temporary render result to find needed passes and views */ BL::RenderResult b_rr = begin_render_result( @@ -810,7 +809,7 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) /* get buffer parameters */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); if (!buffer_params.denoising_data_pass) { session_params.denoising.use = false; @@ -889,7 +888,7 @@ bool BlenderSession::draw(int w, int h) SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); bool session_pause = BlenderSync::get_session_pause(b_scene, background); if (session_pause == false) { @@ -907,7 +906,7 @@ bool BlenderSession::draw(int w, int h) /* draw */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use); DeviceDrawParams draw_params; if (session->params.display_buffer_linear) { diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 8cd65f13f70..1c98e529190 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -104,8 +104,7 @@ class BlenderSync { bool background, BL::ViewLayer b_view_layer = BL::ViewLayer(PointerRNA_NULL)); static bool get_session_pause(BL::Scene &b_scene, bool background); - static BufferParams get_buffer_params(BL::RenderSettings &b_render, - BL::SpaceView3D &b_v3d, + static BufferParams get_buffer_params(BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, int width, -- cgit v1.2.3 From 73967e2047b7d46168355a7158cd1c40d20afc4e Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 31 May 2021 16:28:00 -0300 Subject: Fix undeclared identifiers with 'DEBUG_TIME' These identifiers were accidentally removed in rB44d2479dc36f. --- source/blender/draw/intern/draw_cache_extract_mesh.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index 1a78ce71d74..a2ec3e4e94e 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -6327,6 +6327,10 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, return; } +#ifdef DEBUG_TIME + double rdata_start = PIL_check_seconds_timer(); +#endif + eMRIterType iter_type; eMRDataType data_flag; extracts_flags_get(&extractors, &iter_type, &data_flag); @@ -6347,7 +6351,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, mr->use_final_mesh = do_final; #ifdef DEBUG_TIME - rdata_end = PIL_check_seconds_timer(); + double rdata_end = PIL_check_seconds_timer(); #endif struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( -- cgit v1.2.3 From 8180d478e1aefbbe538bd54b42dda388b482abf5 Mon Sep 17 00:00:00 2001 From: Erick Abrahammson Date: Mon, 31 May 2021 17:03:48 -0400 Subject: Speedup exact boolean by avoiding some mallocs and frees. This is from patch D11432 from Erik Abrahamsson. He found that in some mpq3 functions called frequently from loops, passing in buffers for termporary mpq3 values can save substantial time. On my machine, his example in that patch went from 9.48s to 7.50s for the boolean part of the calculation. On his machine, a running time went from 17s to 10.3s. --- source/blender/blenlib/BLI_mpq3.hh | 16 +++++ source/blender/blenlib/intern/mesh_boolean.cc | 94 +++++++++++++++++-------- source/blender/blenlib/intern/mesh_intersect.cc | 91 +++++++++++++++++------- 3 files changed, 146 insertions(+), 55 deletions(-) diff --git a/source/blender/blenlib/BLI_mpq3.hh b/source/blender/blenlib/BLI_mpq3.hh index fb5e2b61cdb..b9eda2ad7e1 100644 --- a/source/blender/blenlib/BLI_mpq3.hh +++ b/source/blender/blenlib/BLI_mpq3.hh @@ -218,6 +218,15 @@ struct mpq3 { return a.x * b.x + a.y * b.y + a.z * b.z; } + static mpq_class dot_with_buffer(const mpq3 &a, const mpq3 &b, mpq3 &buffer) + { + buffer = a; + buffer *= b; + buffer.x += buffer.y; + buffer.x += buffer.z; + return buffer.x; + } + static mpq3 cross(const mpq3 &a, const mpq3 &b) { return mpq3(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]); @@ -246,6 +255,13 @@ struct mpq3 { return mpq3::dot(diff, diff); } + static mpq_class distance_squared_with_buffer(const mpq3 &a, const mpq3 &b, mpq3 &buffer) + { + buffer = a; + buffer -= b; + return mpq3::dot(buffer, buffer); + } + static mpq3 interpolate(const mpq3 &a, const mpq3 &b, mpq_class t) { mpq_class s = 1 - t; diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 431720761bc..cc27e9238c3 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -1695,9 +1695,24 @@ static int find_containing_cell(const Vert *v, * If the closest point is on an edge, return 0, 1, or 2 * for edges ab, bc, or ca in *r_edge; else -1. * (Adapted from #closest_on_tri_to_point_v3()). + * The arguments ab, ac, ..., r are used as temporaries + * in this routine. Passing them in from the caller can + * avoid many allocs and frees of temporary mpq3 values + * and the mpq_class values within them. */ -static mpq_class closest_on_tri_to_point( - const mpq3 &p, const mpq3 &a, const mpq3 &b, const mpq3 &c, int *r_edge, int *r_vert) +static mpq_class closest_on_tri_to_point(const mpq3 &p, + const mpq3 &a, + const mpq3 &b, + const mpq3 &c, + mpq3 &ab, + mpq3 &ac, + mpq3 &ap, + mpq3 &bp, + mpq3 &cp, + mpq3 &m, + mpq3 &r, + int *r_edge, + int *r_vert) { constexpr int dbg_level = 0; if (dbg_level > 0) { @@ -1705,11 +1720,15 @@ static mpq_class closest_on_tri_to_point( std::cout << " a = " << a << ", b = " << b << ", c = " << c << "\n"; } /* Check if p in vertex region outside a. */ - mpq3 ab = b - a; - mpq3 ac = c - a; - mpq3 ap = p - a; - mpq_class d1 = mpq3::dot(ab, ap); - mpq_class d2 = mpq3::dot(ac, ap); + ab = b; + ab -= a; + ac = c; + ac -= a; + ap = p; + ap -= a; + + mpq_class d1 = mpq3::dot_with_buffer(ab, ap, m); + mpq_class d2 = mpq3::dot_with_buffer(ac, ap, m); if (d1 <= 0 && d2 <= 0) { /* Barycentric coordinates (1,0,0). */ *r_edge = -1; @@ -1717,12 +1736,13 @@ static mpq_class closest_on_tri_to_point( if (dbg_level > 0) { std::cout << " answer = a\n"; } - return mpq3::distance_squared(p, a); + return mpq3::distance_squared_with_buffer(p, a, m); } /* Check if p in vertex region outside b. */ - mpq3 bp = p - b; - mpq_class d3 = mpq3::dot(ab, bp); - mpq_class d4 = mpq3::dot(ac, bp); + bp = p; + bp -= b; + mpq_class d3 = mpq3::dot_with_buffer(ab, bp, m); + mpq_class d4 = mpq3::dot_with_buffer(ac, bp, m); if (d3 >= 0 && d4 <= d3) { /* Barycentric coordinates (0,1,0). */ *r_edge = -1; @@ -1730,25 +1750,28 @@ static mpq_class closest_on_tri_to_point( if (dbg_level > 0) { std::cout << " answer = b\n"; } - return mpq3::distance_squared(p, b); + return mpq3::distance_squared_with_buffer(p, b, m); } /* Check if p in region of ab. */ mpq_class vc = d1 * d4 - d3 * d2; if (vc <= 0 && d1 >= 0 && d3 <= 0) { mpq_class v = d1 / (d1 - d3); /* Barycentric coordinates (1-v,v,0). */ - mpq3 r = a + v * ab; + r = ab; + r *= v; + r += a; *r_vert = -1; *r_edge = 0; if (dbg_level > 0) { std::cout << " answer = on ab at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } /* Check if p in vertex region outside c. */ - mpq3 cp = p - c; - mpq_class d5 = mpq3::dot(ab, cp); - mpq_class d6 = mpq3::dot(ac, cp); + cp = p; + cp -= c; + mpq_class d5 = mpq3::dot_with_buffer(ab, cp, m); + mpq_class d6 = mpq3::dot_with_buffer(ac, cp, m); if (d6 >= 0 && d5 <= d6) { /* Barycentric coordinates (0,0,1). */ *r_edge = -1; @@ -1756,49 +1779,54 @@ static mpq_class closest_on_tri_to_point( if (dbg_level > 0) { std::cout << " answer = c\n"; } - return mpq3::distance_squared(p, c); + return mpq3::distance_squared_with_buffer(p, c, m); } /* Check if p in edge region of ac. */ mpq_class vb = d5 * d2 - d1 * d6; if (vb <= 0 && d2 >= 0 && d6 <= 0) { mpq_class w = d2 / (d2 - d6); /* Barycentric coordinates (1-w,0,w). */ - mpq3 r = a + w * ac; + r = ac; + r *= w; + r += a; *r_vert = -1; *r_edge = 2; if (dbg_level > 0) { std::cout << " answer = on ac at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } /* Check if p in edge region of bc. */ mpq_class va = d3 * d6 - d5 * d4; if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) { mpq_class w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); /* Barycentric coordinates (0,1-w,w). */ - mpq3 r = c - b; - r = w * r; - r = r + b; + r = c; + r -= b; + r *= w; + r += b; *r_vert = -1; *r_edge = 1; if (dbg_level > 0) { std::cout << " answer = on bc at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } /* p inside face region. Compute barycentric coordinates (u,v,w). */ mpq_class denom = 1 / (va + vb + vc); mpq_class v = vb * denom; mpq_class w = vc * denom; - ac = w * ac; - mpq3 r = a + v * ab; - r = r + ac; + ac *= w; + r = ab; + r *= v; + r += a; + r += ac; *r_vert = -1; *r_edge = -1; if (dbg_level > 0) { std::cout << " answer = inside at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } struct ComponentContainer { @@ -1837,6 +1865,9 @@ static Vector find_component_containers(int comp, if (dbg_level > 0) { std::cout << "test vertex in comp: " << test_v << "\n"; } + + mpq3 buf[7]; + for (int comp_other : components.index_range()) { if (comp == comp_other) { continue; @@ -1861,6 +1892,13 @@ static Vector find_component_containers(int comp, tri[0]->co_exact, tri[1]->co_exact, tri[2]->co_exact, + buf[0], + buf[1], + buf[2], + buf[3], + buf[4], + buf[5], + buf[6], &close_edge, &close_vert); if (dbg_level > 1) { diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc index 636209883c3..97f856476c5 100644 --- a/source/blender/blenlib/intern/mesh_intersect.cc +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -1165,13 +1165,19 @@ static int filter_plane_side(const double3 &p, * Assumes ab is not perpendicular to n. * This works because the ratio of the projections of ab and ac onto n is the same as * the ratio along the line ab of the intersection point to the whole of ab. + * The ab, ac, and dotbuf arguments are used as a temporaries; declaring them + * in the caller can avoid many allocs and frees of mpq3 and mpq_class structures. */ -static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n) -{ - mpq3 ab = a - b; - mpq_class den = mpq3::dot(ab, n); +static inline mpq3 tti_interp( + const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n, mpq3 &ab, mpq3 &ac, mpq3 &dotbuf) +{ + ab = a; + ab -= b; + ac = a; + ac -= c; + mpq_class den = mpq3::dot_with_buffer(ab, n, dotbuf); BLI_assert(den != 0); - mpq_class alpha = mpq3::dot(a - c, n) / den; + mpq_class alpha = mpq3::dot_with_buffer(ac, n, dotbuf) / den; return a - alpha * ab; } @@ -1179,11 +1185,28 @@ static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const * Return +1, 0, -1 as a + ad is above, on, or below the oriented plane containing a, b, c in CCW * order. This is the same as -oriented(a, b, c, a + ad), but uses fewer arithmetic operations. * TODO: change arguments to `const Vert *` and use floating filters. + * The ba, ca, n, and dotbuf arguments are used as temporaries; declaring them + * in the caller can avoid many allocs and frees of mpq3 and mpq_class structures. */ -static inline int tti_above(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &ad) +static inline int tti_above(const mpq3 &a, + const mpq3 &b, + const mpq3 &c, + const mpq3 &ad, + mpq3 &ba, + mpq3 &ca, + mpq3 &n, + mpq3 &dotbuf) { - mpq3 n = mpq3::cross(b - a, c - a); - return sgn(mpq3::dot(ad, n)); + ba = b; + ba -= a; + ca = c; + ca -= a; + + n.x = ba.y * ca.z - ba.z * ca.y; + n.y = ba.z * ca.x - ba.x * ca.z; + n.z = ba.x * ca.y - ba.y * ca.x; + + return sgn(mpq3::dot_with_buffer(ad, n, dotbuf)); } /** @@ -1227,20 +1250,21 @@ static ITT_value itt_canon2(const mpq3 &p1, mpq3 p1p2 = p2 - p1; mpq3 intersect_1; mpq3 intersect_2; + mpq3 buf[4]; bool no_overlap = false; /* Top test in classification tree. */ - if (tti_above(p1, q1, r2, p1p2) > 0) { + if (tti_above(p1, q1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) { /* Middle right test in classification tree. */ - if (tti_above(p1, r1, r2, p1p2) <= 0) { + if (tti_above(p1, r1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) <= 0) { /* Bottom right test in classification tree. */ - if (tti_above(p1, r1, q2, p1p2) > 0) { + if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) { /* Overlap is [k [i l] j]. */ if (dbg_level > 0) { std::cout << "overlap [k [i l] j]\n"; } /* i is intersect with p1r1. l is intersect with p2r2. */ - intersect_1 = tti_interp(p1, r1, p2, n2); - intersect_2 = tti_interp(p2, r2, p1, n1); + intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]); } else { /* Overlap is [i [k l] j]. */ @@ -1248,8 +1272,8 @@ static ITT_value itt_canon2(const mpq3 &p1, std::cout << "overlap [i [k l] j]\n"; } /* k is intersect with p2q2. l is intersect is p2r2. */ - intersect_1 = tti_interp(p2, q2, p1, n1); - intersect_2 = tti_interp(p2, r2, p1, n1); + intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]); } } else { @@ -1262,7 +1286,7 @@ static ITT_value itt_canon2(const mpq3 &p1, } else { /* Middle left test in classification tree. */ - if (tti_above(p1, q1, q2, p1p2) < 0) { + if (tti_above(p1, q1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) < 0) { /* No overlap: [i j] [k l]. */ if (dbg_level > 0) { std::cout << "no overlap: [i j] [k l]\n"; @@ -1271,14 +1295,14 @@ static ITT_value itt_canon2(const mpq3 &p1, } else { /* Bottom left test in classification tree. */ - if (tti_above(p1, r1, q2, p1p2) >= 0) { + if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) >= 0) { /* Overlap is [k [i j] l]. */ if (dbg_level > 0) { std::cout << "overlap [k [i j] l]\n"; } /* i is intersect with p1r1. j is intersect with p1q1. */ - intersect_1 = tti_interp(p1, r1, p2, n2); - intersect_2 = tti_interp(p1, q1, p2, n2); + intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]); } else { /* Overlap is [i [k j] l]. */ @@ -1286,8 +1310,8 @@ static ITT_value itt_canon2(const mpq3 &p1, std::cout << "overlap [i [k j] l]\n"; } /* k is intersect with p2q2. j is intersect with p1q1. */ - intersect_1 = tti_interp(p2, q2, p1, n1); - intersect_2 = tti_interp(p1, q1, p2, n2); + intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]); } } } @@ -1438,6 +1462,7 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2) return ITT_value(INONE); } + mpq3 buf[2]; const mpq3 &p1 = vp1->co_exact; const mpq3 &q1 = vq1->co_exact; const mpq3 &r1 = vr1->co_exact; @@ -1447,13 +1472,19 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2) const mpq3 &n2 = tri2.plane->norm_exact; if (sp1 == 0) { - sp1 = sgn(mpq3::dot(p1 - r2, n2)); + buf[0] = p1; + buf[0] -= r2; + sp1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1])); } if (sq1 == 0) { - sq1 = sgn(mpq3::dot(q1 - r2, n2)); + buf[0] = q1; + buf[0] -= r2; + sq1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1])); } if (sr1 == 0) { - sr1 = sgn(mpq3::dot(r1 - r2, n2)); + buf[0] = r1; + buf[0] -= r2; + sr1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1])); } if (dbg_level > 1) { @@ -1473,13 +1504,19 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2) /* Repeat for signs of t2's vertices with respect to plane of t1. */ const mpq3 &n1 = tri1.plane->norm_exact; if (sp2 == 0) { - sp2 = sgn(mpq3::dot(p2 - r1, n1)); + buf[0] = p2; + buf[0] -= r1; + sp2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1])); } if (sq2 == 0) { - sq2 = sgn(mpq3::dot(q2 - r1, n1)); + buf[0] = q2; + buf[0] -= r1; + sq2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1])); } if (sr2 == 0) { - sr2 = sgn(mpq3::dot(r2 - r1, n1)); + buf[0] = r2; + buf[0] -= r1; + sr2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1])); } if (dbg_level > 1) { -- cgit v1.2.3 From f253e5922150984ac2685d6ca4d3a29f7b25b1bb Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Mon, 31 May 2021 19:18:38 -0400 Subject: Docs: Limit the OCIO env vars that we document Brecht mentioned that these are a bit obscure and don't make much sense to override these. --- source/creator/creator_args.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index f9492f5bb62..8b1ac05f086 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -676,12 +676,6 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo printf(" $BLENDER_SYSTEM_PYTHON Directory for system Python libraries.\n"); # ifdef WITH_OCIO printf(" $OCIO Path to override the OpenColorIO config file.\n"); - printf( - " $OCIO_ACTIVE_DISPLAYS Overrides the active_displays list from the config file and " - "reorders them. Colon-separated list of displays, e.g 'sRGB:P3'.\n"); - printf( - " $OCIO_ACTIVE_VIEWS Overrides the active_views list from the config file and " - "reorders them. Colon-separated list of view names, e.g 'internal:client:DI'.\n"); # endif # ifdef WIN32 printf(" $TEMP Store temporary files here.\n"); -- cgit v1.2.3 From c59d2c739d078f4a7a613a05995002cfdbdca303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Tue, 1 Jun 2021 01:45:17 +0200 Subject: Cleanup: spelling in comments --- intern/cycles/render/geometry.cpp | 2 +- intern/cycles/util/util_task.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 1c4b360750f..ce76658acb6 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -2063,7 +2063,7 @@ void GeometryManager::device_update(Device *device, * for meshes with correct bounding boxes. * * This wouldn't cause wrong results, just true - * displacement might be less optimal ot calculate. + * displacement might be less optimal to calculate. */ scene->object_manager->need_flags_update = old_need_object_flags_update; } diff --git a/intern/cycles/util/util_task.h b/intern/cycles/util/util_task.h index 7c39ed675b5..906bf420756 100644 --- a/intern/cycles/util/util_task.h +++ b/intern/cycles/util/util_task.h @@ -32,7 +32,7 @@ typedef function TaskRunFunction; /* Task Pool * - * Pool of tasks that will be executed by the central TaskScheduler.For each + * Pool of tasks that will be executed by the central TaskScheduler. For each * pool, we can wait for all tasks to be done, or cancel them before they are * done. * @@ -77,7 +77,7 @@ class TaskPool { /* Task Scheduler * - * Central scheduler that holds running threads ready to execute tasks. A singe + * Central scheduler that holds running threads ready to execute tasks. A single * queue holds the task from all pools. */ class TaskScheduler { -- cgit v1.2.3 From e7b8a3cb0a1429a642c14a2dabc77e3e20ecdf79 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 12:49:18 +1000 Subject: Cleanup: spelling in comments --- source/blender/blenkernel/intern/writeffmpeg.c | 12 ++++++------ source/blender/draw/intern/draw_hair.c | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 7c8eba20a28..60c216a8401 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -522,9 +522,9 @@ static AVRational calc_time_base(uint den, double num, int codec_id) { /* Convert the input 'num' to an integer. Simply shift the decimal places until we get an integer * (within a floating point error range). - * For example if we have den = 3 and num = 0.1 then the fps is: den/num = 30 fps. - * When converthing this to a ffmpeg time base, we want num to be an integer. - * So we simply move the decimal places of both numbers. IE den = 30, num = 1.*/ + * For example if we have `den = 3` and `num = 0.1` then the fps is: `den/num = 30` fps. + * When converting this to a FFMPEG time base, we want num to be an integer. + * So we simply move the decimal places of both numbers. i.e. `den = 30`, `num = 1`. */ float eps = FLT_EPSILON; const uint DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1; @@ -533,7 +533,7 @@ static AVRational calc_time_base(uint den, double num, int codec_id) const uint num_integer_bits = log2_floor_u((unsigned int)num); /* Formula for calculating the epsilon value: (power of two range) / (pow mantissa bits) - * For example, a float has 23 manitissa bits and the float value 3.5f as a pow2 range of + * For example, a float has 23 mantissa bits and the float value 3.5f as a pow2 range of * (4-2=2): * (2) / pow2(23) = floating point precision for 3.5f */ @@ -619,10 +619,10 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->time_base = calc_time_base(rd->frs_sec, rd->frs_sec_base, codec_id); } - /* As per the timebase documentation here: + /* As per the time-base documentation here: * https://www.ffmpeg.org/ffmpeg-codecs.html#Codec-Options * We want to set the time base to (1 / fps) for fixed frame rate video. - * If it is not possible, we want to set the timebase numbers to something as + * If it is not possible, we want to set the time-base numbers to something as * small as possible. */ if (c->time_base.num != 1) { diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index bf3b10bccd3..585e171adc5 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -88,8 +88,8 @@ extern char datatoc_common_hair_refine_comp_glsl[]; extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; /* TODO(jbakker): move shader creation to `draw_shaders` and add test cases. */ -/* TODO(jbakker): replace defines with `constexpr` to check compilation on all OSs. Currently the - * APPLE codepath does not compile on other platforms and vice versa. */ +/* TODO(jbakker): replace defines with `constexpr` to check compilation on all OS's. + * Currently the `__APPLE__` code-path does not compile on other platforms and vice versa. */ #ifdef USE_COMPUTE_SHADERS static GPUShader *hair_refine_shader_compute_create(ParticleRefineShader UNUSED(refinement)) { -- cgit v1.2.3 From c145cb799897d7db89b1c75dc46fb71c33abfe00 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 12:49:20 +1000 Subject: Fix buffer overrun in paint_line_strokes_spacing Error in 87cafe92ce2f99d8da620b80e1c26f8078554f93 --- source/blender/editors/sculpt_paint/paint_stroke.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index b093f07226e..3f1fed5e5ea 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -1191,7 +1191,7 @@ static void paint_line_strokes_spacing(bContext *C, const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode); - float mouse[2], dmouse[2]; + float mouse[3], dmouse[2]; float length; float d_world_space_position[3] = {0.0f}; float world_space_position_old[3], world_space_position_new[3]; -- cgit v1.2.3 From f71cf996165a81030a92e3e26610bf2e6fa2fc44 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 12:49:20 +1000 Subject: GPU: add 2D projection function When projecting into screen space Z value isn't always needed. Add 2D projection functions, renaming them to avoid accidents happening again. - Add GPU_matrix_project_2fv - Add ED_view3d_project_v2 - Rename ED_view3d_project to ED_view3d_project_v3 - Use the 2D versions of these functions when the Z value isn't used. --- source/blender/editors/curve/editcurve_paint.c | 2 +- source/blender/editors/include/ED_view3d.h | 17 +++++--- source/blender/editors/mesh/editmesh_knife.c | 6 +-- source/blender/editors/object/object_remesh.c | 4 +- source/blender/editors/object/object_transform.c | 4 +- source/blender/editors/physics/particle_edit.c | 2 +- source/blender/editors/sculpt_paint/paint_cursor.c | 2 +- source/blender/editors/sculpt_paint/paint_stroke.c | 8 ++-- source/blender/editors/space_view3d/view3d_edit.c | 7 ++-- .../space_view3d/view3d_gizmo_preselect_type.c | 4 +- .../blender/editors/space_view3d/view3d_project.c | 15 +++++-- source/blender/editors/space_view3d/view3d_utils.c | 20 ++++----- source/blender/gpu/GPU_matrix.h | 36 ++++++++++------- source/blender/gpu/intern/gpu_matrix.cc | 47 +++++++++++++++------- .../windowmanager/gizmo/intern/wm_gizmo_map.c | 4 +- 15 files changed, 108 insertions(+), 70 deletions(-) diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 8842274e017..e4f2de1f741 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -210,7 +210,7 @@ static bool stroke_elem_project(const struct CurveDrawData *cdd, ED_view3d_depth_read_cached(depths, mval_i, 0, &depth_fl); const double depth = (double)depth_fl; if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { - if (ED_view3d_depth_unproject(region, mval_i, depth, r_location_world)) { + if (ED_view3d_depth_unproject_v3(region, mval_i, depth, r_location_world)) { is_location_world_set = true; if (r_normal_world) { zero_v3(r_normal_world); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 66ec57c8a31..52d69d12253 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -162,10 +162,10 @@ bool ED_view3d_depth_read_cached(const ViewDepths *vd, bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, const int mval[2], float r_normal[3]); -bool ED_view3d_depth_unproject(const struct ARegion *region, - const int mval[2], - const double depth, - float r_location_world[3]); +bool ED_view3d_depth_unproject_v3(const struct ARegion *region, + const int mval[2], + const double depth, + float r_location_world[3]); void ED_view3d_depth_tag_update(struct RegionView3D *rv3d); /* Projection */ @@ -410,8 +410,13 @@ void ED_view3d_ob_project_mat_get_from_obmat(const struct RegionView3D *rv3d, const float obmat[4][4], float r_pmat[4][4]); -void ED_view3d_project(const struct ARegion *region, const float world[3], float r_region_co[3]); -bool ED_view3d_unproject( +void ED_view3d_project_v3(const struct ARegion *region, + const float world[3], + float r_region_co[3]); +void ED_view3d_project_v2(const struct ARegion *region, + const float world[3], + float r_region_co[2]); +bool ED_view3d_unproject_v3( const struct ARegion *region, float regionx, float regiony, float regionz, float world[3]); /* end */ diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index b5cd9c7f60d..825b7d11aef 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -578,8 +578,8 @@ static void knife_input_ray_segment(KnifeTool_OpData *kcd, float r_origin_ofs[3]) { /* unproject to find view ray */ - ED_view3d_unproject(kcd->vc.region, mval[0], mval[1], 0.0f, r_origin); - ED_view3d_unproject(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs); + ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], 0.0f, r_origin); + ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs); /* transform into object space */ mul_m4_v3(kcd->ob_imat, r_origin); @@ -1745,7 +1745,7 @@ static bool point_is_visible(KnifeTool_OpData *kcd, float view[3], p_ofs[3]; /* TODO: I think there's a simpler way to get the required raycast ray */ - ED_view3d_unproject(kcd->vc.region, s[0], s[1], 0.0f, view); + ED_view3d_unproject_v3(kcd->vc.region, s[0], s[1], 0.0f, view); mul_m4_v3(kcd->ob_imat, view); diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index 9ef2cce875f..1ff576504ce 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -525,7 +525,7 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev float d_a[3], d_b[3]; float d_a_proj[2], d_b_proj[2]; - float preview_plane_proj[4][3]; + float preview_plane_proj[4][2]; const float y_axis_proj[2] = {0.0f, 1.0f}; mid_v3_v3v3(text_pos, cd->preview_plane[0], cd->preview_plane[2]); @@ -534,7 +534,7 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev for (int i = 0; i < 4; i++) { float preview_plane_world_space[3]; mul_v3_m4v3(preview_plane_world_space, active_object->obmat, cd->preview_plane[i]); - ED_view3d_project(region, preview_plane_world_space, preview_plane_proj[i]); + ED_view3d_project_v2(region, preview_plane_world_space, preview_plane_proj[i]); } /* Get the initial X and Y axis of the basis from the edges of the Bounding Box face. */ diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index a87b5054efa..b9a3bc87e19 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -1654,7 +1654,7 @@ static void object_transform_axis_target_calc_depth_init(struct XFormAxisData *x if (center_tot) { mul_v3_fl(center, 1.0f / center_tot); float center_proj[3]; - ED_view3d_project(xfd->vc.region, center, center_proj); + ED_view3d_project_v3(xfd->vc.region, center, center_proj); xfd->prev.depth = center_proj[2]; xfd->prev.is_depth_valid = true; } @@ -1890,7 +1890,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { xfd->prev.depth = depth_fl; xfd->prev.is_depth_valid = true; - if (ED_view3d_depth_unproject(region, event->mval, depth, location_world)) { + if (ED_view3d_depth_unproject_v3(region, event->mval, depth, location_world)) { if (is_translate) { float normal[3]; diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 5b545784e5b..97994b65f40 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -609,7 +609,7 @@ static bool key_test_depth(const PEData *data, const float co[3], const int scre } float win[3]; - ED_view3d_project(data->vc.region, co, win); + ED_view3d_project_v3(data->vc.region, co, win); if (win[2] - 0.00001f > depth) { return 0; diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 3829aeebbeb..7e111905883 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1039,7 +1039,7 @@ static void cursor_draw_point_screen_space(const uint gpuattr, float translation_vertex_cursor[3], location[3]; copy_v3_v3(location, true_location); mul_m4_v3(obmat, location); - ED_view3d_project(region, location, translation_vertex_cursor); + ED_view3d_project_v3(region, location, translation_vertex_cursor); /* Do not draw points behind the view. Z [near, far] is mapped to [-1, 1]. */ if (translation_vertex_cursor[2] <= 1.0f) { imm_draw_circle_fill_3d( diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 3f1fed5e5ea..59a5ad63f0e 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -846,7 +846,7 @@ static int paint_space_stroke(bContext *C, while (length > 0.0f) { float spacing = paint_space_stroke_spacing_variable( C, scene, stroke, pressure, dpressure, length); - float mouse[3]; + float mouse[2]; if (length >= spacing) { if (use_scene_spacing) { @@ -856,7 +856,7 @@ static int paint_space_stroke(bContext *C, add_v3_v3v3(final_world_space_position, stroke->last_world_space_position, final_world_space_position); - ED_view3d_project(region, final_world_space_position, mouse); + ED_view3d_project_v2(region, final_world_space_position, mouse); } else { mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; @@ -1191,7 +1191,7 @@ static void paint_line_strokes_spacing(bContext *C, const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode); - float mouse[3], dmouse[2]; + float mouse[2], dmouse[2]; float length; float d_world_space_position[3] = {0.0f}; float world_space_position_old[3], world_space_position_new[3]; @@ -1240,7 +1240,7 @@ static void paint_line_strokes_spacing(bContext *C, mul_v3_v3fl(final_world_space_position, d_world_space_position, spacing_final); add_v3_v3v3( final_world_space_position, world_space_position_old, final_world_space_position); - ED_view3d_project(region, final_world_space_position, mouse); + ED_view3d_project_v2(region, final_world_space_position, mouse); } else { mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final; diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 967ad966320..8b6d0e9ee04 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -3666,8 +3666,8 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } /* convert border to 3d coordinates */ - if ((!ED_view3d_unproject(region, cent[0], cent[1], depth_close, p)) || - (!ED_view3d_unproject(region, rect.xmin, rect.ymin, depth_close, p_corner))) { + if ((!ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) || + (!ED_view3d_unproject_v3(region, rect.xmin, rect.ymin, depth_close, p_corner))) { return OPERATOR_CANCELLED; } @@ -3690,7 +3690,8 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) new_dist = rv3d->dist; /* convert the drawn rectangle into 3d space */ - if (depth_close != FLT_MAX && ED_view3d_unproject(region, cent[0], cent[1], depth_close, p)) { + if (depth_close != FLT_MAX && + ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) { negate_v3_v3(new_ofs, p); } else { diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c index 298a2a7a824..07c3b6bd1d8 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c @@ -154,10 +154,10 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int * Only pre-select a vertex when the cursor is really close to it. */ if (eve_test) { BMVert *vert = (BMVert *)eve_test; - float vert_p_co[3], vert_co[3]; + float vert_p_co[2], vert_co[3]; const float mval_f[2] = {UNPACK2(vc.mval)}; mul_v3_m4v3(vert_co, gz_ele->bases[base_index_vert]->object->obmat, vert->co); - ED_view3d_project(vc.region, vert_co, vert_p_co); + ED_view3d_project_v2(vc.region, vert_co, vert_p_co); float len = len_v2v2(vert_p_co, mval_f); if (len < 35) { best.ele = (BMElem *)eve_test; diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index 24d34e514c5..7547f8ee434 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -809,23 +809,30 @@ void ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d, /** * Convert between region relative coordinates (x,y) and depth component z and * a point in world space. */ -void ED_view3d_project(const struct ARegion *region, const float world[3], float r_region_co[3]) +void ED_view3d_project_v3(const struct ARegion *region, const float world[3], float r_region_co[3]) { /* Viewport is set up to make coordinates relative to the region, not window. */ RegionView3D *rv3d = region->regiondata; const int viewport[4] = {0, 0, region->winx, region->winy}; + GPU_matrix_project_3fv(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co); +} - GPU_matrix_project(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co); +void ED_view3d_project_v2(const struct ARegion *region, const float world[3], float r_region_co[2]) +{ + /* Viewport is set up to make coordinates relative to the region, not window. */ + RegionView3D *rv3d = region->regiondata; + const int viewport[4] = {0, 0, region->winx, region->winy}; + GPU_matrix_project_2fv(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co); } -bool ED_view3d_unproject( +bool ED_view3d_unproject_v3( const struct ARegion *region, float regionx, float regiony, float regionz, float world[3]) { RegionView3D *rv3d = region->regiondata; const int viewport[4] = {0, 0, region->winx, region->winy}; const float region_co[3] = {regionx, regiony, regionz}; - return GPU_matrix_unproject(region_co, rv3d->viewmat, rv3d->winmat, viewport, world); + return GPU_matrix_unproject_3fv(region_co, rv3d->viewmat, rv3d->winmat, viewport, world); } /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index f96c17d7cff..8ae5d4a29e9 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -299,8 +299,8 @@ void ED_view3d_clipping_calc( float xs = (ELEM(val, 0, 3)) ? rect->xmin : rect->xmax; float ys = (ELEM(val, 0, 1)) ? rect->ymin : rect->ymax; - ED_view3d_unproject(region, xs, ys, 0.0, bb->vec[val]); - ED_view3d_unproject(region, xs, ys, 1.0, bb->vec[4 + val]); + ED_view3d_unproject_v3(region, xs, ys, 0.0, bb->vec[val]); + ED_view3d_unproject_v3(region, xs, ys, 1.0, bb->vec[4 + val]); } /* optionally transform to object space */ @@ -1057,7 +1057,7 @@ bool ED_view3d_autodist(Depsgraph *depsgraph, float centx = (float)mval[0] + 0.5f; float centy = (float)mval[1] + 0.5f; - if (ED_view3d_unproject(region, centx, centy, depth_close, mouse_worldloc)) { + if (ED_view3d_unproject_v3(region, centx, centy, depth_close, mouse_worldloc)) { return true; } } @@ -1091,7 +1091,7 @@ bool ED_view3d_autodist_simple(ARegion *region, float centx = (float)mval[0] + 0.5f; float centy = (float)mval[1] + 0.5f; - return ED_view3d_unproject(region, centx, centy, depth, mouse_worldloc); + return ED_view3d_unproject_v3(region, centx, centy, depth, mouse_worldloc); } bool ED_view3d_autodist_depth(ARegion *region, const int mval[2], int margin, float *depth) @@ -1716,7 +1716,7 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, ED_view3d_depth_read_cached(depths, mval_ofs, 0, &depth_fl); const double depth = (double)depth_fl; if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { - if (ED_view3d_depth_unproject(region, mval_ofs, depth, coords[i])) { + if (ED_view3d_depth_unproject_v3(region, mval_ofs, depth, coords[i])) { depths_valid[i] = true; } } @@ -1751,14 +1751,14 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, return false; } -bool ED_view3d_depth_unproject(const ARegion *region, - const int mval[2], - const double depth, - float r_location_world[3]) +bool ED_view3d_depth_unproject_v3(const ARegion *region, + const int mval[2], + const double depth, + float r_location_world[3]) { float centx = (float)mval[0] + 0.5f; float centy = (float)mval[1] + 0.5f; - return ED_view3d_unproject(region, centx, centy, depth, r_location_world); + return ED_view3d_unproject_v3(region, centx, centy, depth, r_location_world); } void ED_view3d_depth_tag_update(RegionView3D *rv3d) diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h index aad6ae9e2ba..e073263f352 100644 --- a/source/blender/gpu/GPU_matrix.h +++ b/source/blender/gpu/GPU_matrix.h @@ -118,21 +118,27 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *unproj_prec const float proj[4][4], const int view[4]); -void GPU_matrix_project(const float world[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_win[3]); - -bool GPU_matrix_unproject(const float win[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_world[3]); - -void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc, - const float win[3], - float r_world[3]); +void GPU_matrix_project_3fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_win[3]); + +void GPU_matrix_project_2fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_win[2]); + +bool GPU_matrix_unproject_3fv(const float win[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_world[3]); + +void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc, + const float win[3], + float r_world[3]); /* 2D Projection Matrix */ diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index 569b51a407a..6eb9cb823d5 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -474,11 +474,11 @@ void GPU_matrix_look_at(float eyeX, GPU_matrix_translate_3f(-eyeX, -eyeY, -eyeZ); } -void GPU_matrix_project(const float world[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float win[3]) +void GPU_matrix_project_3fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float win[3]) { float v[4]; @@ -494,6 +494,25 @@ void GPU_matrix_project(const float world[3], win[2] = (v[2] + 1) * 0.5f; } +void GPU_matrix_project_2fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float win[2]) +{ + float v[4]; + + mul_v4_m4v3(v, model, world); + mul_m4_v4(proj, v); + + if (v[3] != 0.0f) { + mul_v2_fl(v, 1.0f / v[3]); + } + + win[0] = view[0] + (view[2] * (v[0] + 1)) * 0.5f; + win[1] = view[1] + (view[3] * (v[1] + 1)) * 0.5f; +} + /** * The same result could be obtained as follows: * @@ -556,9 +575,9 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc, return true; } -void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc, - const float win[3], - float r_world[3]) +void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc, + const float win[3], + float r_world[3]) { float in[3] = { (win[0] - precalc->view[0]) / precalc->view[2], @@ -569,18 +588,18 @@ void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc * mul_v3_m4v3(r_world, precalc->model_inverted, in); } -bool GPU_matrix_unproject(const float win[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_world[3]) +bool GPU_matrix_unproject_3fv(const float win[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_world[3]) { struct GPUMatrixUnproject_Precalc precalc; if (!GPU_matrix_unproject_precalc(&precalc, model, proj, view)) { zero_v3(r_world); return false; } - GPU_matrix_unproject_with_precalc(&precalc, win, r_world); + GPU_matrix_unproject_3fv_with_precalc(&precalc, win, r_world); return true; } diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 611a9cba000..2ffa04bd8ae 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -620,7 +620,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, struct GPUMatrixUnproject_Precalc unproj_precalc; GPU_matrix_unproject_precalc(&unproj_precalc, rv3d->viewmat, rv3d->winmat, viewport); - GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d_origin); + GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d_origin); uint *buf_iter = buffer; int hit_found = -1; @@ -631,7 +631,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, wmGizmo *gz = visible_gizmos[buf_iter[3] >> 8]; float co_3d[3]; co_screen[2] = int_as_float(buf_iter[1]); - GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d); + GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d); float select_bias = gz->select_bias; if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) { select_bias *= gz->scale_final; -- cgit v1.2.3 From f8ce744c83500b86dad4d5a62a0b733bf9fd4b59 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 12:49:21 +1000 Subject: Cleanup: correct sculpt quat argument size --- source/blender/editors/sculpt_paint/sculpt.c | 4 ++-- source/blender/editors/sculpt_paint/sculpt_intern.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 2e1dd928f96..d6d54a1985d 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1854,7 +1854,7 @@ static void flip_v3(float v[3], const ePaintSymmetryFlags symm) flip_v3_v3(v, v, symm); } -static void flip_qt(float quat[3], const ePaintSymmetryFlags symm) +static void flip_qt(float quat[4], const ePaintSymmetryFlags symm) { flip_qt_qt(quat, quat, symm); } @@ -4196,7 +4196,7 @@ void SCULPT_flip_v3_by_symm_area(float v[3], } } -void SCULPT_flip_quat_by_symm_area(float quat[3], +void SCULPT_flip_quat_by_symm_area(float quat[4], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3]) diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 087cb6dd94a..e2ee4c9fed3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -278,7 +278,7 @@ void SCULPT_flip_v3_by_symm_area(float v[3], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3]); -void SCULPT_flip_quat_by_symm_area(float quat[3], +void SCULPT_flip_quat_by_symm_area(float quat[4], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3]); -- cgit v1.2.3 From 5e7fb77dc4d27df74a8e839b8a4eb7a1fe43475b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 12:49:22 +1000 Subject: BMesh: remove checks for tessellating 2 sided faces 2 sided faces aren't supported and will cause problems in many areas of Blender's code. Removing (implied) support for faces with fewer than 3 sides means the total number of triangles is known ahead of time. This simplifies adding support for multi-threading and partial updates to an existing tessellation - as the face and loop indices can be used to access the range of triangles associated with a face. Also correct outdated comments. --- source/blender/blenkernel/intern/editmesh.c | 3 ++- source/blender/bmesh/intern/bmesh_polygon.c | 28 +++++++--------------- source/blender/bmesh/intern/bmesh_polygon.h | 4 ++-- source/blender/editors/sculpt_paint/paint_mask.c | 5 ++-- source/blender/modifiers/intern/MOD_boolean.cc | 5 ++-- source/blender/python/bmesh/bmesh_py_types.c | 7 +++--- .../blender/python/mathutils/mathutils_bvhtree.c | 5 +--- 7 files changed, 21 insertions(+), 36 deletions(-) diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index 0fa3bb29ccd..bd76357617a 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -127,9 +127,10 @@ static void editmesh_tessface_calc_intern(BMEditMesh *em) } em->looptris = looptris; + em->tottri = looptris_tot; /* after allocating the em->looptris, we're ready to tessellate */ - BM_mesh_calc_tessellation(em->bm, em->looptris, &em->tottri); + BM_mesh_calc_tessellation(em->bm, em->looptris); } void BKE_editmesh_looptri_calc(BMEditMesh *em) diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index 4ae2cc67140..5e6635cc929 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -1530,14 +1530,11 @@ void BM_face_as_array_loop_quad(BMFace *f, BMLoop *r_loops[4]) * * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count */ -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot) +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) { - /* use this to avoid locking pthread for _every_ polygon - * and calling the fill function */ + /* Avoid polygon filling logic for 3-4 sided faces. */ #define USE_TESSFACE_SPEEDUP - /* this assumes all faces can be scan-filled, which isn't always true, - * worst case we over alloc a little which is acceptable */ #ifndef NDEBUG const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); #endif @@ -1549,9 +1546,10 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptri MemArena *arena = NULL; BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BLI_assert(efa->len >= 3); /* don't consider two-edged faces */ - if (UNLIKELY(efa->len < 3)) { - /* do nothing */ + if (0) { + /* do nothing (needed for else statements below) */ } #ifdef USE_TESSFACE_SPEEDUP @@ -1664,8 +1662,6 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptri arena = NULL; } - *r_looptris_tot = i; - BLI_assert(i <= looptris_tot); #undef USE_TESSFACE_SPEEDUP @@ -1674,10 +1670,8 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptri /** * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles. */ -void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot) +void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) { - /* this assumes all faces can be scan-filled, which isn't always true, - * worst case we over alloc a little which is acceptable */ #ifndef NDEBUG const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); #endif @@ -1692,11 +1686,9 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_ Heap *pf_heap = NULL; BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - /* don't consider two-edged faces */ - if (UNLIKELY(efa->len < 3)) { - /* do nothing */ - } - else if (efa->len == 3) { + BLI_assert(efa->len >= 3); + + if (efa->len == 3) { BMLoop *l; BMLoop **l_ptr = looptris[i++]; l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); @@ -1805,7 +1797,5 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_ BLI_heap_free(pf_heap, NULL); } - *r_looptris_tot = i; - BLI_assert(i <= looptris_tot); } diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h index 8c2b9ee0bff..48837d4d0c6 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.h +++ b/source/blender/bmesh/intern/bmesh_polygon.h @@ -24,8 +24,8 @@ struct Heap; #include "BLI_compiler_attrs.h" -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot); -void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot); +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]); +void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]); void BM_face_calc_tessellation(const BMFace *f, const bool use_fixed_quad, diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index b6ae6f8bee7..da34723eed4 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -1239,10 +1239,9 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) })); const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); - int tottri; BMLoop *(*looptris)[3]; looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__); - BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri); + BM_mesh_calc_tessellation_beauty(bm, looptris); BMIter iter; int i; @@ -1290,7 +1289,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) break; } BM_mesh_boolean( - bm, looptris, tottri, bm_face_isect_pair, NULL, 2, true, true, false, boolean_mode); + bm, looptris, looptris_tot, bm_face_isect_pair, NULL, 2, true, true, false, boolean_mode); } MEM_freeN(looptris); diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index 9b8782737c3..4b9b24e4e47 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -283,11 +283,10 @@ static void BMD_mesh_intersection(BMesh *bm, /* main bmesh intersection setup */ /* create tessface & intersect */ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); - int tottri; BMLoop *(*looptris)[3] = (BMLoop * (*)[3]) MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__); - BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri); + BM_mesh_calc_tessellation_beauty(bm, looptris); /* postpone this until after tessellating * so we can use the original normals before the vertex are moved */ @@ -364,7 +363,7 @@ static void BMD_mesh_intersection(BMesh *bm, BM_mesh_intersect(bm, looptris, - tottri, + looptris_tot, bm_face_isect_pair, nullptr, false, diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index f1a8d450ea5..598640f8f68 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1385,7 +1385,6 @@ static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self) BMesh *bm; int looptris_tot; - int tottri; BMLoop *(*looptris)[3]; PyObject *ret; @@ -1398,10 +1397,10 @@ static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self) looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); looptris = PyMem_MALLOC(sizeof(*looptris) * looptris_tot); - BM_mesh_calc_tessellation(bm, looptris, &tottri); + BM_mesh_calc_tessellation(bm, looptris); - ret = PyList_New(tottri); - for (i = 0; i < tottri; i++) { + ret = PyList_New(looptris_tot); + for (i = 0; i < looptris_tot; i++) { PyList_SET_ITEM(ret, i, BPy_BMLoop_Array_As_Tuple(bm, looptris[i], 3)); } diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c index 1acbcc006ca..79ed9e68420 100644 --- a/source/blender/python/mathutils/mathutils_bvhtree.c +++ b/source/blender/python/mathutils/mathutils_bvhtree.c @@ -961,8 +961,6 @@ static PyObject *C_BVHTree_FromBMesh(PyObject *UNUSED(cls), PyObject *args, PyOb /* Get data for tessellation */ { - int tris_len_dummy; - coords_len = (uint)bm->totvert; tris_len = (uint)poly_to_tri_count(bm->totface, bm->totloop); @@ -971,8 +969,7 @@ static PyObject *C_BVHTree_FromBMesh(PyObject *UNUSED(cls), PyObject *args, PyOb looptris = MEM_mallocN(sizeof(*looptris) * (size_t)tris_len, __func__); - BM_mesh_calc_tessellation(bm, looptris, &tris_len_dummy); - BLI_assert(tris_len_dummy == (int)tris_len); + BM_mesh_calc_tessellation(bm, looptris); } { -- cgit v1.2.3 From 3a18e304be5ce6f800b997879a3c5c0fe357b330 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 12:49:22 +1000 Subject: Cleanup: remove disabled face tessellation logic This was kept since these blocks are easier to follow. Remove as the overall result wasn't so readable (especially with nested ifdef's). Replace disabled code with comment on the indices used for quads/tris. --- source/blender/bmesh/intern/bmesh_polygon.c | 46 ++++------------------------- 1 file changed, 5 insertions(+), 41 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index 5e6635cc929..f1e6da9b746 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -1547,51 +1547,17 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { BLI_assert(efa->len >= 3); - /* don't consider two-edged faces */ - if (0) { - /* do nothing (needed for else statements below) */ - } - #ifdef USE_TESSFACE_SPEEDUP - - /* no need to ensure the loop order, we know its ok */ - - else if (efa->len == 3) { -# if 0 - int j; - BM_ITER_ELEM_INDEX(l, &liter, efa, BM_LOOPS_OF_FACE, j) { - looptris[i][j] = l; - } - i += 1; -# else - /* more cryptic but faster */ + if (efa->len == 3) { + /* `0 1 2` -> `0 1 2` */ BMLoop *l; BMLoop **l_ptr = looptris[i++]; l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); l_ptr[1] = l = l->next; l_ptr[2] = l->next; -# endif } else if (efa->len == 4) { -# if 0 - BMLoop *ltmp[4]; - int j; - BLI_array_grow_items(looptris, 2); - BM_ITER_ELEM_INDEX(l, &liter, efa, BM_LOOPS_OF_FACE, j) { - ltmp[j] = l; - } - - looptris[i][0] = ltmp[0]; - looptris[i][1] = ltmp[1]; - looptris[i][2] = ltmp[2]; - i += 1; - - looptris[i][0] = ltmp[0]; - looptris[i][1] = ltmp[2]; - looptris[i][2] = ltmp[3]; - i += 1; -# else - /* more cryptic but faster */ + /* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */ BMLoop *l; BMLoop **l_ptr_a = looptris[i++]; BMLoop **l_ptr_b = looptris[i++]; @@ -1599,7 +1565,6 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) (l_ptr_a[1] = l = l->next); (l_ptr_a[2] = l_ptr_b[1] = l = l->next); (l_ptr_b[2] = l->next); -# endif if (UNLIKELY(is_quad_flip_v3_first_third_fast( l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) { @@ -1608,10 +1573,9 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) l_ptr_b[0] = l_ptr_a[1]; } } - + else #endif /* USE_TESSFACE_SPEEDUP */ - - else { + { int j; BMLoop *l_iter; -- cgit v1.2.3 From b8d0f28f700ff6bbba5a03d69f19f6931981bbc5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 12:58:52 +1000 Subject: Cleanup: split bmesh tessellation into it's own file Prepare for further refactoring for these functions. --- source/blender/bmesh/CMakeLists.txt | 2 + source/blender/bmesh/bmesh.h | 1 + .../blender/bmesh/intern/bmesh_mesh_tessellate.c | 277 +++++++++++++++++++++ .../blender/bmesh/intern/bmesh_mesh_tessellate.h | 24 ++ source/blender/bmesh/intern/bmesh_polygon.c | 243 +----------------- source/blender/bmesh/intern/bmesh_polygon.h | 3 - 6 files changed, 305 insertions(+), 245 deletions(-) create mode 100644 source/blender/bmesh/intern/bmesh_mesh_tessellate.c create mode 100644 source/blender/bmesh/intern/bmesh_mesh_tessellate.h diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 92064b3d040..af47b9557ef 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -102,6 +102,8 @@ set(SRC intern/bmesh_mesh_convert.h intern/bmesh_mesh_duplicate.c intern/bmesh_mesh_duplicate.h + intern/bmesh_mesh_tessellate.c + intern/bmesh_mesh_tessellate.h intern/bmesh_mesh_validate.c intern/bmesh_mesh_validate.h intern/bmesh_mods.c diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h index 4441ccc0c88..a7e4e011ae2 100644 --- a/source/blender/bmesh/bmesh.h +++ b/source/blender/bmesh/bmesh.h @@ -215,6 +215,7 @@ extern "C" { #include "intern/bmesh_mesh.h" #include "intern/bmesh_mesh_convert.h" #include "intern/bmesh_mesh_duplicate.h" +#include "intern/bmesh_mesh_tessellate.h" #include "intern/bmesh_mesh_validate.h" #include "intern/bmesh_mods.h" #include "intern/bmesh_operators.h" diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c new file mode 100644 index 00000000000..bde2c75a079 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c @@ -0,0 +1,277 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bmesh + * + * This file contains code for polygon tessellation + * (creating triangles from polygons). + */ + +#include "DNA_meshdata_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_alloca.h" +#include "BLI_heap.h" +#include "BLI_linklist.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_polyfill_2d.h" +#include "BLI_polyfill_2d_beautify.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +/** + * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh + * \param looptris: + * + * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count + */ +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) +{ + /* Avoid polygon filling logic for 3-4 sided faces. */ +#define USE_TESSFACE_SPEEDUP + +#ifndef NDEBUG + const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); +#endif + + BMIter iter; + BMFace *efa; + int i = 0; + + MemArena *arena = NULL; + + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BLI_assert(efa->len >= 3); +#ifdef USE_TESSFACE_SPEEDUP + if (efa->len == 3) { + /* `0 1 2` -> `0 1 2` */ + BMLoop *l; + BMLoop **l_ptr = looptris[i++]; + l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); + l_ptr[1] = l = l->next; + l_ptr[2] = l->next; + } + else if (efa->len == 4) { + /* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */ + BMLoop *l; + BMLoop **l_ptr_a = looptris[i++]; + BMLoop **l_ptr_b = looptris[i++]; + (l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa)); + (l_ptr_a[1] = l = l->next); + (l_ptr_a[2] = l_ptr_b[1] = l = l->next); + (l_ptr_b[2] = l->next); + + if (UNLIKELY(is_quad_flip_v3_first_third_fast( + l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) { + /* flip out of degenerate 0-2 state. */ + l_ptr_a[2] = l_ptr_b[2]; + l_ptr_b[0] = l_ptr_a[1]; + } + } + else +#endif /* USE_TESSFACE_SPEEDUP */ + { + int j; + + BMLoop *l_iter; + BMLoop *l_first; + BMLoop **l_arr; + + float axis_mat[3][3]; + float(*projverts)[2]; + uint(*tris)[3]; + + const int totfilltri = efa->len - 2; + + if (UNLIKELY(arena == NULL)) { + arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + } + + tris = BLI_memarena_alloc(arena, sizeof(*tris) * totfilltri); + l_arr = BLI_memarena_alloc(arena, sizeof(*l_arr) * efa->len); + projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * efa->len); + + axis_dominant_v3_to_m3_negate(axis_mat, efa->no); + + j = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + l_arr[j] = l_iter; + mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); + j++; + } while ((l_iter = l_iter->next) != l_first); + + BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, arena); + + for (j = 0; j < totfilltri; j++) { + BMLoop **l_ptr = looptris[i++]; + uint *tri = tris[j]; + + l_ptr[0] = l_arr[tri[0]]; + l_ptr[1] = l_arr[tri[1]]; + l_ptr[2] = l_arr[tri[2]]; + } + + BLI_memarena_clear(arena); + } + } + + if (arena) { + BLI_memarena_free(arena); + arena = NULL; + } + + BLI_assert(i <= looptris_tot); + +#undef USE_TESSFACE_SPEEDUP +} + +/** + * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles. + */ +void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) +{ +#ifndef NDEBUG + const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); +#endif + + BMIter iter; + BMFace *efa; + int i = 0; + + MemArena *pf_arena = NULL; + + /* use_beauty */ + Heap *pf_heap = NULL; + + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BLI_assert(efa->len >= 3); + + if (efa->len == 3) { + BMLoop *l; + BMLoop **l_ptr = looptris[i++]; + l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); + l_ptr[1] = l = l->next; + l_ptr[2] = l->next; + } + else if (efa->len == 4) { + BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa); + BMLoop *l_v2 = l_v1->next; + BMLoop *l_v3 = l_v2->next; + BMLoop *l_v4 = l_v1->prev; + + /* #BM_verts_calc_rotate_beauty performs excessive checks we don't need! + * It's meant for rotating edges, it also calculates a new normal. + * + * Use #BLI_polyfill_beautify_quad_rotate_calc since we have the normal. + */ +#if 0 + const bool split_13 = (BM_verts_calc_rotate_beauty( + l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) < 0.0f); +#else + float axis_mat[3][3], v_quad[4][2]; + axis_dominant_v3_to_m3(axis_mat, efa->no); + mul_v2_m3v3(v_quad[0], axis_mat, l_v1->v->co); + mul_v2_m3v3(v_quad[1], axis_mat, l_v2->v->co); + mul_v2_m3v3(v_quad[2], axis_mat, l_v3->v->co); + mul_v2_m3v3(v_quad[3], axis_mat, l_v4->v->co); + + const bool split_13 = BLI_polyfill_beautify_quad_rotate_calc( + v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f; +#endif + + BMLoop **l_ptr_a = looptris[i++]; + BMLoop **l_ptr_b = looptris[i++]; + if (split_13) { + l_ptr_a[0] = l_v1; + l_ptr_a[1] = l_v2; + l_ptr_a[2] = l_v3; + + l_ptr_b[0] = l_v1; + l_ptr_b[1] = l_v3; + l_ptr_b[2] = l_v4; + } + else { + l_ptr_a[0] = l_v1; + l_ptr_a[1] = l_v2; + l_ptr_a[2] = l_v4; + + l_ptr_b[0] = l_v2; + l_ptr_b[1] = l_v3; + l_ptr_b[2] = l_v4; + } + } + else { + int j; + + BMLoop *l_iter; + BMLoop *l_first; + BMLoop **l_arr; + + float axis_mat[3][3]; + float(*projverts)[2]; + unsigned int(*tris)[3]; + + const int totfilltri = efa->len - 2; + + if (UNLIKELY(pf_arena == NULL)) { + pf_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); + } + + tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * totfilltri); + l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len); + projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len); + + axis_dominant_v3_to_m3_negate(axis_mat, efa->no); + + j = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + l_arr[j] = l_iter; + mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); + j++; + } while ((l_iter = l_iter->next) != l_first); + + BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena); + + BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap); + + for (j = 0; j < totfilltri; j++) { + BMLoop **l_ptr = looptris[i++]; + unsigned int *tri = tris[j]; + + l_ptr[0] = l_arr[tri[0]]; + l_ptr[1] = l_arr[tri[1]]; + l_ptr[2] = l_arr[tri[2]]; + } + + BLI_memarena_clear(pf_arena); + } + } + + if (pf_arena) { + BLI_memarena_free(pf_arena); + + BLI_heap_free(pf_heap, NULL); + } + + BLI_assert(i <= looptris_tot); +} diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.h b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h new file mode 100644 index 00000000000..5606a5a7e02 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h @@ -0,0 +1,24 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bmesh + */ + +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]); +void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]); diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index f1e6da9b746..5397098a7f3 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -18,8 +18,7 @@ * \ingroup bmesh * * This file contains code for dealing - * with polygons (normal/area calculation, - * tessellation, etc) + * with polygons (normal/area calculation, tessellation, etc) */ #include "DNA_listBase.h" @@ -1523,243 +1522,3 @@ void BM_face_as_array_loop_quad(BMFace *f, BMLoop *r_loops[4]) l = l->next; r_loops[3] = l; } - -/** - * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh - * \param looptris: - * - * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count - */ -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) -{ - /* Avoid polygon filling logic for 3-4 sided faces. */ -#define USE_TESSFACE_SPEEDUP - -#ifndef NDEBUG - const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); -#endif - - BMIter iter; - BMFace *efa; - int i = 0; - - MemArena *arena = NULL; - - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - BLI_assert(efa->len >= 3); -#ifdef USE_TESSFACE_SPEEDUP - if (efa->len == 3) { - /* `0 1 2` -> `0 1 2` */ - BMLoop *l; - BMLoop **l_ptr = looptris[i++]; - l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); - l_ptr[1] = l = l->next; - l_ptr[2] = l->next; - } - else if (efa->len == 4) { - /* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */ - BMLoop *l; - BMLoop **l_ptr_a = looptris[i++]; - BMLoop **l_ptr_b = looptris[i++]; - (l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa)); - (l_ptr_a[1] = l = l->next); - (l_ptr_a[2] = l_ptr_b[1] = l = l->next); - (l_ptr_b[2] = l->next); - - if (UNLIKELY(is_quad_flip_v3_first_third_fast( - l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) { - /* flip out of degenerate 0-2 state. */ - l_ptr_a[2] = l_ptr_b[2]; - l_ptr_b[0] = l_ptr_a[1]; - } - } - else -#endif /* USE_TESSFACE_SPEEDUP */ - { - int j; - - BMLoop *l_iter; - BMLoop *l_first; - BMLoop **l_arr; - - float axis_mat[3][3]; - float(*projverts)[2]; - uint(*tris)[3]; - - const int totfilltri = efa->len - 2; - - if (UNLIKELY(arena == NULL)) { - arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - } - - tris = BLI_memarena_alloc(arena, sizeof(*tris) * totfilltri); - l_arr = BLI_memarena_alloc(arena, sizeof(*l_arr) * efa->len); - projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * efa->len); - - axis_dominant_v3_to_m3_negate(axis_mat, efa->no); - - j = 0; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - l_arr[j] = l_iter; - mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); - j++; - } while ((l_iter = l_iter->next) != l_first); - - BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, arena); - - for (j = 0; j < totfilltri; j++) { - BMLoop **l_ptr = looptris[i++]; - uint *tri = tris[j]; - - l_ptr[0] = l_arr[tri[0]]; - l_ptr[1] = l_arr[tri[1]]; - l_ptr[2] = l_arr[tri[2]]; - } - - BLI_memarena_clear(arena); - } - } - - if (arena) { - BLI_memarena_free(arena); - arena = NULL; - } - - BLI_assert(i <= looptris_tot); - -#undef USE_TESSFACE_SPEEDUP -} - -/** - * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles. - */ -void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) -{ -#ifndef NDEBUG - const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); -#endif - - BMIter iter; - BMFace *efa; - int i = 0; - - MemArena *pf_arena = NULL; - - /* use_beauty */ - Heap *pf_heap = NULL; - - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - BLI_assert(efa->len >= 3); - - if (efa->len == 3) { - BMLoop *l; - BMLoop **l_ptr = looptris[i++]; - l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); - l_ptr[1] = l = l->next; - l_ptr[2] = l->next; - } - else if (efa->len == 4) { - BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa); - BMLoop *l_v2 = l_v1->next; - BMLoop *l_v3 = l_v2->next; - BMLoop *l_v4 = l_v1->prev; - - /* #BM_verts_calc_rotate_beauty performs excessive checks we don't need! - * It's meant for rotating edges, it also calculates a new normal. - * - * Use #BLI_polyfill_beautify_quad_rotate_calc since we have the normal. - */ -#if 0 - const bool split_13 = (BM_verts_calc_rotate_beauty( - l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) < 0.0f); -#else - float axis_mat[3][3], v_quad[4][2]; - axis_dominant_v3_to_m3(axis_mat, efa->no); - mul_v2_m3v3(v_quad[0], axis_mat, l_v1->v->co); - mul_v2_m3v3(v_quad[1], axis_mat, l_v2->v->co); - mul_v2_m3v3(v_quad[2], axis_mat, l_v3->v->co); - mul_v2_m3v3(v_quad[3], axis_mat, l_v4->v->co); - - const bool split_13 = BLI_polyfill_beautify_quad_rotate_calc( - v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f; -#endif - - BMLoop **l_ptr_a = looptris[i++]; - BMLoop **l_ptr_b = looptris[i++]; - if (split_13) { - l_ptr_a[0] = l_v1; - l_ptr_a[1] = l_v2; - l_ptr_a[2] = l_v3; - - l_ptr_b[0] = l_v1; - l_ptr_b[1] = l_v3; - l_ptr_b[2] = l_v4; - } - else { - l_ptr_a[0] = l_v1; - l_ptr_a[1] = l_v2; - l_ptr_a[2] = l_v4; - - l_ptr_b[0] = l_v2; - l_ptr_b[1] = l_v3; - l_ptr_b[2] = l_v4; - } - } - else { - int j; - - BMLoop *l_iter; - BMLoop *l_first; - BMLoop **l_arr; - - float axis_mat[3][3]; - float(*projverts)[2]; - unsigned int(*tris)[3]; - - const int totfilltri = efa->len - 2; - - if (UNLIKELY(pf_arena == NULL)) { - pf_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); - } - - tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * totfilltri); - l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len); - projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len); - - axis_dominant_v3_to_m3_negate(axis_mat, efa->no); - - j = 0; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - l_arr[j] = l_iter; - mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); - j++; - } while ((l_iter = l_iter->next) != l_first); - - BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena); - - BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap); - - for (j = 0; j < totfilltri; j++) { - BMLoop **l_ptr = looptris[i++]; - unsigned int *tri = tris[j]; - - l_ptr[0] = l_arr[tri[0]]; - l_ptr[1] = l_arr[tri[1]]; - l_ptr[2] = l_arr[tri[2]]; - } - - BLI_memarena_clear(pf_arena); - } - } - - if (pf_arena) { - BLI_memarena_free(pf_arena); - - BLI_heap_free(pf_heap, NULL); - } - - BLI_assert(i <= looptris_tot); -} diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h index 48837d4d0c6..e7d5cb2f89d 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.h +++ b/source/blender/bmesh/intern/bmesh_polygon.h @@ -24,9 +24,6 @@ struct Heap; #include "BLI_compiler_attrs.h" -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]); -void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]); - void BM_face_calc_tessellation(const BMFace *f, const bool use_fixed_quad, BMLoop **r_loops, -- cgit v1.2.3 From 13deb5088a4dd58603661f8643bbddab1c75b516 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 15:20:35 +1000 Subject: Cleanup: split face tessellation into inline functions Prepare for multiple code-paths that recalculate tessellation. --- .../blender/bmesh/intern/bmesh_mesh_tessellate.c | 206 ++++++++++++--------- 1 file changed, 117 insertions(+), 89 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c index bde2c75a079..c8ea9429145 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c @@ -36,43 +36,29 @@ #include "bmesh.h" #include "bmesh_tools.h" -/** - * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh - * \param looptris: - * - * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count - */ -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) -{ - /* Avoid polygon filling logic for 3-4 sided faces. */ -#define USE_TESSFACE_SPEEDUP +/* -------------------------------------------------------------------- */ +/** \name Default Mesh Tessellation + * \{ */ -#ifndef NDEBUG - const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); -#endif - - BMIter iter; - BMFace *efa; - int i = 0; - - MemArena *arena = NULL; - - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - BLI_assert(efa->len >= 3); -#ifdef USE_TESSFACE_SPEEDUP - if (efa->len == 3) { +static int mesh_calc_tessellation_for_face(BMLoop *(*looptris)[3], + BMFace *efa, + MemArena **pf_arena_p) +{ + switch (efa->len) { + case 3: { /* `0 1 2` -> `0 1 2` */ BMLoop *l; - BMLoop **l_ptr = looptris[i++]; + BMLoop **l_ptr = looptris[0]; l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); l_ptr[1] = l = l->next; l_ptr[2] = l->next; + return 1; } - else if (efa->len == 4) { + case 4: { /* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */ BMLoop *l; - BMLoop **l_ptr_a = looptris[i++]; - BMLoop **l_ptr_b = looptris[i++]; + BMLoop **l_ptr_a = looptris[0]; + BMLoop **l_ptr_b = looptris[1]; (l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa)); (l_ptr_a[1] = l = l->next); (l_ptr_a[2] = l_ptr_b[1] = l = l->next); @@ -80,73 +66,65 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) if (UNLIKELY(is_quad_flip_v3_first_third_fast( l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) { - /* flip out of degenerate 0-2 state. */ + /* Flip out of degenerate 0-2 state. */ l_ptr_a[2] = l_ptr_b[2]; l_ptr_b[0] = l_ptr_a[1]; } + return 2; } - else -#endif /* USE_TESSFACE_SPEEDUP */ - { - int j; - - BMLoop *l_iter; - BMLoop *l_first; + default: { + BMLoop *l_iter, *l_first; BMLoop **l_arr; float axis_mat[3][3]; float(*projverts)[2]; uint(*tris)[3]; - const int totfilltri = efa->len - 2; + const int tris_len = efa->len - 2; - if (UNLIKELY(arena == NULL)) { - arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + MemArena *pf_arena = *pf_arena_p; + if (UNLIKELY(pf_arena == NULL)) { + pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); } - tris = BLI_memarena_alloc(arena, sizeof(*tris) * totfilltri); - l_arr = BLI_memarena_alloc(arena, sizeof(*l_arr) * efa->len); - projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * efa->len); + tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len); + l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len); + projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len); axis_dominant_v3_to_m3_negate(axis_mat, efa->no); - j = 0; + int i = 0; l_iter = l_first = BM_FACE_FIRST_LOOP(efa); do { - l_arr[j] = l_iter; - mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); - j++; + l_arr[i] = l_iter; + mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co); + i++; } while ((l_iter = l_iter->next) != l_first); - BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, arena); + BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena); - for (j = 0; j < totfilltri; j++) { - BMLoop **l_ptr = looptris[i++]; - uint *tri = tris[j]; + for (i = 0; i < tris_len; i++) { + BMLoop **l_ptr = looptris[i]; + uint *tri = tris[i]; l_ptr[0] = l_arr[tri[0]]; l_ptr[1] = l_arr[tri[1]]; l_ptr[2] = l_arr[tri[2]]; } - BLI_memarena_clear(arena); + BLI_memarena_clear(pf_arena); + return tris_len; } } - - if (arena) { - BLI_memarena_free(arena); - arena = NULL; - } - - BLI_assert(i <= looptris_tot); - -#undef USE_TESSFACE_SPEEDUP } /** - * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles. + * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh + * \param looptris: + * + * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count */ -void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) { #ifndef NDEBUG const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); @@ -158,20 +136,42 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) MemArena *pf_arena = NULL; - /* use_beauty */ - Heap *pf_heap = NULL; - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { BLI_assert(efa->len >= 3); + i += mesh_calc_tessellation_for_face(looptris + i, efa, &pf_arena); + } - if (efa->len == 3) { + if (pf_arena) { + BLI_memarena_free(pf_arena); + pf_arena = NULL; + } + + BLI_assert(i <= looptris_tot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Beauty Mesh Tessellation + * + * Avoid degenerate triangles. + * \{ */ + +static int mesh_calc_tessellation_for_face_beauty(BMLoop *(*looptris)[3], + BMFace *efa, + MemArena **pf_arena_p, + Heap **pf_heap_p) +{ + switch (efa->len) { + case 3: { BMLoop *l; - BMLoop **l_ptr = looptris[i++]; + BMLoop **l_ptr = looptris[0]; l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); l_ptr[1] = l = l->next; l_ptr[2] = l->next; + return 1; } - else if (efa->len == 4) { + case 4: { BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa); BMLoop *l_v2 = l_v1->next; BMLoop *l_v3 = l_v2->next; @@ -197,8 +197,8 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f; #endif - BMLoop **l_ptr_a = looptris[i++]; - BMLoop **l_ptr_b = looptris[i++]; + BMLoop **l_ptr_a = looptris[0]; + BMLoop **l_ptr_b = looptris[1]; if (split_13) { l_ptr_a[0] = l_v1; l_ptr_a[1] = l_v2; @@ -217,46 +217,46 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) l_ptr_b[1] = l_v3; l_ptr_b[2] = l_v4; } + return 2; } - else { - int j; + default: { + MemArena *pf_arena = *pf_arena_p; + Heap *pf_heap = *pf_heap_p; + if (UNLIKELY(pf_arena == NULL)) { + pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + pf_heap = *pf_heap_p = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); + } - BMLoop *l_iter; - BMLoop *l_first; + BMLoop *l_iter, *l_first; BMLoop **l_arr; float axis_mat[3][3]; float(*projverts)[2]; - unsigned int(*tris)[3]; - - const int totfilltri = efa->len - 2; + uint(*tris)[3]; - if (UNLIKELY(pf_arena == NULL)) { - pf_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); - } + const int tris_len = efa->len - 2; - tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * totfilltri); + tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len); l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len); projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len); axis_dominant_v3_to_m3_negate(axis_mat, efa->no); - j = 0; + int i = 0; l_iter = l_first = BM_FACE_FIRST_LOOP(efa); do { - l_arr[j] = l_iter; - mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); - j++; + l_arr[i] = l_iter; + mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co); + i++; } while ((l_iter = l_iter->next) != l_first); BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena); BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap); - for (j = 0; j < totfilltri; j++) { - BMLoop **l_ptr = looptris[i++]; - unsigned int *tri = tris[j]; + for (i = 0; i < tris_len; i++) { + BMLoop **l_ptr = looptris[i]; + uint *tri = tris[i]; l_ptr[0] = l_arr[tri[0]]; l_ptr[1] = l_arr[tri[1]]; @@ -264,8 +264,34 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) } BLI_memarena_clear(pf_arena); + + return tris_len; } } +} + +/** + * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles. + */ +void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) +{ +#ifndef NDEBUG + const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); +#endif + + BMIter iter; + BMFace *efa; + int i = 0; + + MemArena *pf_arena = NULL; + + /* use_beauty */ + Heap *pf_heap = NULL; + + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BLI_assert(efa->len >= 3); + i += mesh_calc_tessellation_for_face_beauty(looptris + i, efa, &pf_arena, &pf_heap); + } if (pf_arena) { BLI_memarena_free(pf_arena); @@ -275,3 +301,5 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) BLI_assert(i <= looptris_tot); } + +/** \} */ -- cgit v1.2.3 From 930ad9257d00a1891a948ff71756ffe8acb61686 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 1 Jun 2021 09:23:37 +0200 Subject: Cleanup: Split draw_cache_extract_mesh into multiple files. draw_cache_extract_mesh for task scheduling. Will be refactored to draw_cache_extract_mesh_scheduling later on after migrating to CPP. draw_cache_extract_mesh_render_data extraction of mesh render data from edit mesh/mesh into a more generic structure. draw_cache_extract_mesh_extractors containing all the extractors. This will be split up further into a single file per extractor. --- source/blender/draw/CMakeLists.txt | 2 + source/blender/draw/intern/draw_cache_extract.h | 10 +- .../blender/draw/intern/draw_cache_extract_mesh.c | 5608 +------------------- .../intern/draw_cache_extract_mesh_extractors.c | 4748 +++++++++++++++++ .../draw/intern/draw_cache_extract_mesh_private.h | 509 ++ .../intern/draw_cache_extract_mesh_render_data.c | 374 ++ source/blender/editors/include/ED_uvedit.h | 1 + 7 files changed, 5707 insertions(+), 5545 deletions(-) create mode 100644 source/blender/draw/intern/draw_cache_extract_mesh_extractors.c create mode 100644 source/blender/draw/intern/draw_cache_extract_mesh_private.h create mode 100644 source/blender/draw/intern/draw_cache_extract_mesh_render_data.c diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 0541aa982f3..adbe7fdf274 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -51,6 +51,8 @@ set(INC set(SRC intern/draw_cache.c + intern/draw_cache_extract_mesh_extractors.c + intern/draw_cache_extract_mesh_render_data.c intern/draw_cache_extract_mesh.c intern/draw_cache_impl_curve.cc intern/draw_cache_impl_displist.c diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index e6b7fb9ddf5..abba3aeeb70 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -24,6 +24,10 @@ struct TaskGraph; +#include "GPU_batch.h" +#include "GPU_index_buffer.h" +#include "GPU_vertex_buffer.h" + /* Vertex Group Selection and display options */ typedef struct DRW_MeshWeightState { int defgroup_active; @@ -80,12 +84,6 @@ typedef enum eMRDataType { MR_DATA_TAN_LOOP_NOR = 1 << 4, } eMRDataType; -typedef enum eMRExtractType { - MR_EXTRACT_BMESH, - MR_EXTRACT_MAPPED, - MR_EXTRACT_MESH, -} eMRExtractType; - BLI_INLINE int mesh_render_mat_len_get(Mesh *me) { /* In edit mode, the displayed mesh is stored in the edit-mesh. */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index a2ec3e4e94e..0d2a4704b1b 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -22,5536 +22,112 @@ * * \brief Extraction of Mesh data into VBO to feed to GPU. */ +#include "MEM_guardedalloc.h" -#include "MEM_guardedalloc.h" - -#include "BLI_alloca.h" -#include "BLI_bitmap.h" -#include "BLI_buffer.h" -#include "BLI_edgehash.h" -#include "BLI_jitter_2d.h" -#include "BLI_math_bits.h" -#include "BLI_math_vector.h" -#include "BLI_string.h" -#include "BLI_task.h" -#include "BLI_utildefines.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - -#include "BKE_bvhutils.h" -#include "BKE_customdata.h" -#include "BKE_deform.h" -#include "BKE_editmesh.h" -#include "BKE_editmesh_bvh.h" -#include "BKE_editmesh_cache.h" -#include "BKE_editmesh_tangent.h" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" -#include "BKE_mesh_tangent.h" -#include "BKE_modifier.h" -#include "BKE_object_deform.h" -#include "BKE_paint.h" - -#include "atomic_ops.h" - -#include "bmesh.h" - -#include "GPU_batch.h" -#include "GPU_capabilities.h" - -#include "DRW_render.h" - -#include "ED_mesh.h" -#include "ED_uvedit.h" - -#include "draw_cache_impl.h" -#include "draw_cache_inline.h" - -#include "draw_cache_extract.h" - -// #define DEBUG_TIME - -#ifdef DEBUG_TIME -# include "PIL_time_utildefines.h" -#endif - -#define CHUNK_SIZE 8192 - -/* - * Max number of extractions types. - */ -#define M_EXTRACT_LEN 38 - -/* ---------------------------------------------------------------------- */ -/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). - * \{ */ - -typedef struct MeshRenderData { - eMRExtractType extract_type; - - int poly_len, edge_len, vert_len, loop_len; - int edge_loose_len; - int vert_loose_len; - int loop_loose_len; - int tri_len; - int mat_len; - - bool use_hide; - bool use_subsurf_fdots; - bool use_final_mesh; - - /** Use for #MeshStatVis calculation which use world-space coords. */ - float obmat[4][4]; - - const ToolSettings *toolsettings; - /** Edit Mesh */ - BMEditMesh *edit_bmesh; - BMesh *bm; - EditMeshData *edit_data; - - /* For deformed edit-mesh data. */ - /* Use for #ME_WRAPPER_TYPE_BMESH. */ - const float (*bm_vert_coords)[3]; - const float (*bm_vert_normals)[3]; - const float (*bm_poly_normals)[3]; - const float (*bm_poly_centers)[3]; - - int *v_origindex, *e_origindex, *p_origindex; - int crease_ofs; - int bweight_ofs; - int freestyle_edge_ofs; - int freestyle_face_ofs; - /** Mesh */ - Mesh *me; - const MVert *mvert; - const MEdge *medge; - const MLoop *mloop; - const MPoly *mpoly; - BMVert *eve_act; - BMEdge *eed_act; - BMFace *efa_act; - BMFace *efa_act_uv; - /* Data created on-demand (usually not for #BMesh based data). */ - MLoopTri *mlooptri; - float (*loop_normals)[3]; - float (*poly_normals)[3]; - int *lverts, *ledges; -} MeshRenderData; - -static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtractionCache *cache) -{ - mr->ledges = cache->ledges; - mr->lverts = cache->lverts; - mr->vert_loose_len = cache->vert_loose_len; - mr->edge_loose_len = cache->edge_loose_len; - - mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2); -} - -static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr, - MeshBufferExtractionCache *cache) -{ - /* Early exit: Are loose geometry already available. Only checking for loose verts as loose edges - * and verts are calculated at the same time.*/ - if (cache->lverts) { - return; - } - - cache->vert_loose_len = 0; - cache->edge_loose_len = 0; - - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - - BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); - - cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); - const MEdge *med = mr->medge; - for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { - if (med->flag & ME_LOOSEEDGE) { - cache->ledges[cache->edge_loose_len++] = med_index; - } - /* Tag verts as not loose. */ - BLI_BITMAP_ENABLE(lvert_map, med->v1); - BLI_BITMAP_ENABLE(lvert_map, med->v2); - } - if (cache->edge_loose_len < mr->edge_len) { - cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); - } - - cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); - for (int v = 0; v < mr->vert_len; v++) { - if (!BLI_BITMAP_TEST(lvert_map, v)) { - cache->lverts[cache->vert_loose_len++] = v; - } - } - if (cache->vert_loose_len < mr->vert_len) { - cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); - } - - MEM_freeN(lvert_map); - } - else { - /* #BMesh */ - BMesh *bm = mr->bm; - int elem_id; - BMIter iter; - BMVert *eve; - BMEdge *ede; - - cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*cache->lverts), __func__); - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { - if (eve->e == NULL) { - cache->lverts[cache->vert_loose_len++] = elem_id; - } - } - if (cache->vert_loose_len < mr->vert_len) { - cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); - } - - cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); - BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { - if (ede->l == NULL) { - cache->ledges[cache->edge_loose_len++] = elem_id; - } - } - if (cache->edge_loose_len < mr->edge_len) { - cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); - } - } -} - -/** - * Part of the creation of the #MeshRenderData that happens in a thread. - */ -static void mesh_render_data_update_looptris(MeshRenderData *mr, - const eMRIterType iter_type, - const eMRDataType data_flag) -{ - Mesh *me = mr->me; - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { - mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI"); - BKE_mesh_recalc_looptri( - me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri); - } - } - else { - /* #BMesh */ - if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { - /* Edit mode ensures this is valid, no need to calculate. */ - BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL)); - } - } -} - -static void mesh_render_data_update_normals(MeshRenderData *mr, - const eMRIterType UNUSED(iter_type), - const eMRDataType data_flag) -{ - Mesh *me = mr->me; - const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0; - const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI; - - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) { - mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__); - BKE_mesh_calc_normals_poly((MVert *)mr->mvert, - NULL, - mr->vert_len, - mr->mloop, - mr->mpoly, - mr->loop_len, - mr->poly_len, - mr->poly_normals, - true); - } - if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { - mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); - short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL); - BKE_mesh_normals_loop_split(mr->me->mvert, - mr->vert_len, - mr->me->medge, - mr->edge_len, - mr->me->mloop, - mr->loop_normals, - mr->loop_len, - mr->me->mpoly, - mr->poly_normals, - mr->poly_len, - is_auto_smooth, - split_angle, - NULL, - clnors, - NULL); - } - } - else { - /* #BMesh */ - if (data_flag & MR_DATA_POLY_NOR) { - /* Use #BMFace.no instead. */ - } - if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { - - const float(*vert_coords)[3] = NULL; - const float(*vert_normals)[3] = NULL; - const float(*poly_normals)[3] = NULL; - - if (mr->edit_data && mr->edit_data->vertexCos) { - vert_coords = mr->bm_vert_coords; - vert_normals = mr->bm_vert_normals; - poly_normals = mr->bm_poly_normals; - } - - mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); - const int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL); - BM_loops_calc_normal_vcos(mr->bm, - vert_coords, - vert_normals, - poly_normals, - is_auto_smooth, - split_angle, - mr->loop_normals, - NULL, - NULL, - clnors_offset, - false); - } - } -} - -/** - * \param is_mode_active: When true, use the modifiers from the edit-data, - * otherwise don't use modifiers as they are not from this object. - */ -static MeshRenderData *mesh_render_data_create(Mesh *me, - MeshBufferExtractionCache *cache, - const bool is_editmode, - const bool is_paint_mode, - const bool is_mode_active, - const float obmat[4][4], - const bool do_final, - const bool do_uvedit, - const DRW_MeshCDMask *UNUSED(cd_used), - const ToolSettings *ts, - const eMRIterType iter_type) -{ - MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); - mr->toolsettings = ts; - mr->mat_len = mesh_render_mat_len_get(me); - - copy_m4_m4(mr->obmat, obmat); - - if (is_editmode) { - BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final); - mr->bm = me->edit_mesh->bm; - mr->edit_bmesh = me->edit_mesh; - mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage; - mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL; - - if (mr->edit_data) { - EditMeshData *emd = mr->edit_data; - if (emd->vertexCos) { - BKE_editmesh_cache_ensure_vert_normals(mr->edit_bmesh, emd); - BKE_editmesh_cache_ensure_poly_normals(mr->edit_bmesh, emd); - } - - mr->bm_vert_coords = mr->edit_data->vertexCos; - mr->bm_vert_normals = mr->edit_data->vertexNos; - mr->bm_poly_normals = mr->edit_data->polyNos; - mr->bm_poly_centers = mr->edit_data->polyCos; - } - - bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); - bool use_mapped = is_mode_active && - (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original); - - int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; - - BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types); - BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP); - - mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false); - mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true); - mr->eed_act = BM_mesh_active_edge_get(mr->bm); - mr->eve_act = BM_mesh_active_vert_get(mr->bm); - - mr->crease_ofs = CustomData_get_offset(&mr->bm->edata, CD_CREASE); - mr->bweight_ofs = CustomData_get_offset(&mr->bm->edata, CD_BWEIGHT); -#ifdef WITH_FREESTYLE - mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE); - mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE); -#endif - - if (use_mapped) { - mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); - mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); - mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); - - use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); - } - - mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_BMESH; - - /* Seems like the mesh_eval_final do not have the right origin indices. - * Force not mapped in this case. */ - if (has_mdata && do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) { - // mr->edit_bmesh = NULL; - mr->extract_type = MR_EXTRACT_MESH; - } - } - else { - mr->me = me; - mr->edit_bmesh = NULL; - - bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original; - if (use_mapped) { - mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); - mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); - mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); - - use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); - } - - mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_MESH; - } - - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - mr->vert_len = mr->me->totvert; - mr->edge_len = mr->me->totedge; - mr->loop_len = mr->me->totloop; - mr->poly_len = mr->me->totpoly; - mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); - - mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT); - mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE); - mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP); - mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY); - - mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); - mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); - mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); - } - else { - /* #BMesh */ - BMesh *bm = mr->bm; - - mr->vert_len = bm->totvert; - mr->edge_len = bm->totedge; - mr->loop_len = bm->totloop; - mr->poly_len = bm->totface; - mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); - } - - if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { - mesh_render_data_loose_geom_ensure(mr, cache); - mesh_render_data_loose_geom_load(mr, cache); - } - - return mr; -} - -static void mesh_render_data_free(MeshRenderData *mr) -{ - MEM_SAFE_FREE(mr->mlooptri); - MEM_SAFE_FREE(mr->poly_normals); - MEM_SAFE_FREE(mr->loop_normals); - - /* Loose geometry are owned by MeshBufferExtractionCache. */ - mr->ledges = NULL; - mr->lverts = NULL; - - MEM_freeN(mr); -} - -BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx) -{ - return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? - BM_face_at_index(mr->bm, mr->p_origindex[idx]) : - NULL; -} - -BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx) -{ - return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? - BM_edge_at_index(mr->bm, mr->e_origindex[idx]) : - NULL; -} - -BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx) -{ - return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? - BM_vert_at_index(mr->bm, mr->v_origindex[idx]) : - NULL; -} - -BLI_INLINE const float *bm_vert_co_get(const MeshRenderData *mr, const BMVert *eve) -{ - const float(*vert_coords)[3] = mr->bm_vert_coords; - if (vert_coords != NULL) { - return vert_coords[BM_elem_index_get(eve)]; - } - - UNUSED_VARS(mr); - return eve->co; -} - -BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *eve) -{ - const float(*vert_normals)[3] = mr->bm_vert_normals; - if (vert_normals != NULL) { - return vert_normals[BM_elem_index_get(eve)]; - } - - UNUSED_VARS(mr); - return eve->no; -} - -BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *efa) -{ - const float(*poly_normals)[3] = mr->bm_poly_normals; - if (poly_normals != NULL) { - return poly_normals[BM_elem_index_get(efa)]; - } - - UNUSED_VARS(mr); - return efa->no; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loop Triangles - * \{ */ - -typedef struct ExtractTriBMesh_Params { - BMLoop *(*looptris)[3]; - int tri_range[2]; -} ExtractTriBMesh_Params; -typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr, - BMLoop **elt, - const int elt_index, - void *data); - -#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elem_tri, index_tri, params) \ - CHECK_TYPE(params, const ExtractTriBMesh_Params *); \ - { \ - const int _tri_index_end = (params)->tri_range[1]; \ - BMLoop **elem_tri = (params)->looptris[(params)->tri_range[0]]; \ - for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ - index_tri += 1, elem_tri += 3) -#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END } - -typedef struct ExtractTriMesh_Params { - const MLoopTri *mlooptri; - int tri_range[2]; -} ExtractTriMesh_Params; -typedef void(ExtractTriMeshFn)(const MeshRenderData *mr, - const MLoopTri *mlt, - const int elt_index, - void *data); - -#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(elem_tri, index_tri, params) \ - CHECK_TYPE(params, const ExtractTriMesh_Params *); \ - { \ - const int _tri_index_end = (params)->tri_range[1]; \ - const MLoopTri *elem_tri = &(params)->mlooptri[(params)->tri_range[0]]; \ - for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ - index_tri += 1, elem_tri += 1) -#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Polygons, Loops - * \{ */ - -typedef struct ExtractPolyBMesh_Params { - BMLoop *(*looptris)[3]; - int poly_range[2]; -} ExtractPolyBMesh_Params; -typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr, - BMFace *f, - const int f_index, - void *data); - -#define EXTRACT_POLY_FOREACH_BM_BEGIN(elem_poly, index_poly, params, mr) \ - CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMFace **_ftable = mr->bm->ftable; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - BMFace *elem_poly = _ftable[index_poly]; \ - (void)elem_poly; - -#define EXTRACT_POLY_FOREACH_BM_END \ - } \ - } - -/* Iterate over polygon and loop. */ -#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(elem_loop, index_loop, params, mr) \ - CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMFace **_ftable = mr->bm->ftable; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - BMFace *elem_face = _ftable[index_poly]; \ - BMLoop *elem_loop, *l_first; \ - elem_loop = l_first = BM_FACE_FIRST_LOOP(elem_face); \ - do { \ - const int index_loop = BM_elem_index_get(elem_loop); \ - (void)index_loop; /* Quiet warning when unused. */ - -#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(elem_loop) \ - } \ - while ((elem_loop = elem_loop->next) != l_first) \ - ; \ - } \ - } - -typedef struct ExtractPolyMesh_Params { - int poly_range[2]; -} ExtractPolyMesh_Params; -typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *data); - -#define EXTRACT_POLY_FOREACH_MESH_BEGIN(elem_poly, index_poly, params, mr) \ - CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ - { \ - const MPoly *_mpoly = mr->mpoly; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - const MPoly *elem_poly = &_mpoly[index_poly]; \ - (void)elem_poly; - -#define EXTRACT_POLY_FOREACH_MESH_END \ - } \ - } - -/* Iterate over polygon and loop. */ -#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN( \ - elem_poly, index_poly, elem_loop, index_loop, params, mr) \ - CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ - { \ - const MPoly *_mpoly = mr->mpoly; \ - const MLoop *_mloop = mr->mloop; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - const MPoly *elem_poly = &_mpoly[index_poly]; \ - const int _index_end = elem_poly->loopstart + elem_poly->totloop; \ - for (int index_loop = elem_poly->loopstart; index_loop < _index_end; index_loop += 1) { \ - const MLoop *elem_loop = &_mloop[index_loop]; \ - (void)elem_loop; - -#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END \ - } \ - } \ - } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loose Edges - * \{ */ - -typedef struct ExtractLEdgeBMesh_Params { - const int *ledge; - int ledge_range[2]; -} ExtractLEdgeBMesh_Params; -typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *data); - -#define EXTRACT_LEDGE_FOREACH_BM_BEGIN(elem_edge, index_ledge, params) \ - CHECK_TYPE(params, const ExtractLEdgeBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_EDGE) == 0); \ - BMEdge **_etable = mr->bm->etable; \ - const int *_ledge = (params)->ledge; \ - const int _ledge_index_end = (params)->ledge_range[1]; \ - for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ - index_ledge += 1) { \ - BMEdge *elem_edge = _etable[_ledge[index_ledge]]; \ - (void)elem_edge; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LEDGE_FOREACH_BM_END \ - } \ - } \ - } - -typedef struct ExtractLEdgeMesh_Params { - const int *ledge; - int ledge_range[2]; -} ExtractLEdgeMesh_Params; -typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr, - const MEdge *med, - const uint ledge_index, - void *data); - -#define EXTRACT_LEDGE_FOREACH_MESH_BEGIN(elem_edge, index_ledge, params, mr) \ - CHECK_TYPE(params, const ExtractLEdgeMesh_Params *); \ - { \ - const MEdge *_medge = mr->medge; \ - const int *_ledge = (params)->ledge; \ - const int _ledge_index_end = (params)->ledge_range[1]; \ - for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ - index_ledge += 1) { \ - const MEdge *elem_edge = &_medge[_ledge[index_ledge]]; \ - (void)elem_edge; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LEDGE_FOREACH_MESH_END \ - } \ - } \ - } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loose Vertices - * \{ */ - -typedef struct ExtractLVertBMesh_Params { - const int *lvert; - int lvert_range[2]; -} ExtractLVertBMesh_Params; -typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr, - BMVert *eve, - const int lvert_index, - void *data); - -#define EXTRACT_LVERT_FOREACH_BM_BEGIN(elem_vert, index_lvert, params) \ - CHECK_TYPE(params, const ExtractLVertBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMVert **vtable = mr->bm->vtable; \ - const int *lverts = (params)->lvert; \ - const int _lvert_index_end = (params)->lvert_range[1]; \ - for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ - index_lvert += 1) { \ - BMVert *elem_vert = vtable[lverts[index_lvert]]; \ - (void)elem_vert; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LVERT_FOREACH_BM_END \ - } \ - } \ - } - -typedef struct ExtractLVertMesh_Params { - const int *lvert; - int lvert_range[2]; -} ExtractLVertMesh_Params; -typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr, - const MVert *mv, - const int lvert_index, - void *data); - -#define EXTRACT_LVERT_FOREACH_MESH_BEGIN(elem, index_lvert, params, mr) \ - CHECK_TYPE(params, const ExtractLVertMesh_Params *); \ - { \ - const MVert *mvert = mr->mvert; \ - const int *lverts = (params)->lvert; \ - const int _lvert_index_end = (params)->lvert_range[1]; \ - for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ - index_lvert += 1) { \ - const MVert *elem = &mvert[lverts[index_lvert]]; \ - (void)elem; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LVERT_FOREACH_MESH_END \ - } \ - } \ - } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract Struct - * \{ */ - -typedef void *(ExtractInitFn)(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buffer); -typedef void(ExtractFinishFn)(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buffer, - void *data); - -typedef struct MeshExtract { - /** Executed on main thread and return user data for iteration functions. */ - ExtractInitFn *init; - /** Executed on one (or more if use_threading) worker thread(s). */ - ExtractTriBMeshFn *iter_looptri_bm; - ExtractTriMeshFn *iter_looptri_mesh; - ExtractPolyBMeshFn *iter_poly_bm; - ExtractPolyMeshFn *iter_poly_mesh; - ExtractLEdgeBMeshFn *iter_ledge_bm; - ExtractLEdgeMeshFn *iter_ledge_mesh; - ExtractLVertBMeshFn *iter_lvert_bm; - ExtractLVertMeshFn *iter_lvert_mesh; - /** Executed on one worker thread after all elements iterations. */ - ExtractFinishFn *finish; - /** Used to request common data. */ - const eMRDataType data_flag; - /** Used to know if the element callbacks are thread-safe and can be parallelized. */ - const bool use_threading; - /** - * Offset in bytes of the buffer inside a MeshBufferCache instance. Points to a vertex or index - * buffer. - */ - const size_t mesh_buffer_offset; -} MeshExtract; - -static void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc) -{ - /* NOTE: POINTER_OFFSET on windows platforms casts internally to `void *`, but on GCC/CLANG to - * `MeshBufferCache *`. What shows a different usage versus intent. */ - void **buffer_ptr = (void **)POINTER_OFFSET(mbc, extractor->mesh_buffer_offset); - void *buffer = *buffer_ptr; - BLI_assert(buffer); - return buffer; -} - -typedef struct MeshExtractRunData { - const MeshExtract *extractor; - void *buffer; - void *user_data; -} MeshExtractRunData; - -typedef struct MeshExtractRunDataArray { - int len; - MeshExtractRunData items[M_EXTRACT_LEN]; -} MeshExtractRunDataArray; - -static void mesh_extract_run_data_array_init(MeshExtractRunDataArray *array) -{ - array->len = 0; -} - -static void mesh_extract_run_data_array_add_ex(MeshExtractRunDataArray *array, - const MeshExtractRunData *run_data) -{ - array->items[array->len] = *run_data; - array->len++; -} - -static void mesh_extract_run_data_array_add(MeshExtractRunDataArray *array, - const MeshExtract *extractor) -{ - MeshExtractRunData run_data; - run_data.extractor = extractor; - run_data.buffer = NULL; - run_data.user_data = NULL; - mesh_extract_run_data_array_add_ex(array, &run_data); -} - -static void mesh_extract_run_data_array_filter_iter_type(const MeshExtractRunDataArray *src, - MeshExtractRunDataArray *dst, - eMRIterType iter_type) -{ - for (int i = 0; i < src->len; i++) { - - const MeshExtractRunData *data = &src->items[i]; - const MeshExtract *extractor = data->extractor; - if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { - BLI_assert(extractor->iter_looptri_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { - BLI_assert(extractor->iter_poly_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { - BLI_assert(extractor->iter_ledge_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { - BLI_assert(extractor->iter_lvert_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - } -} - -static void mesh_extract_run_data_array_filter_threading( - const MeshExtractRunDataArray *src, MeshExtractRunDataArray *dst_multi_threaded) -{ - for (int i = 0; i < src->len; i++) { - const MeshExtract *extractor = src->items[i].extractor; - if (extractor->use_threading) { - mesh_extract_run_data_array_add(dst_multi_threaded, extractor); - } - } -} - -BLI_INLINE eMRIterType mesh_extract_iter_type(const MeshExtract *ext) -{ - eMRIterType type = 0; - SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri_mesh), MR_ITER_LOOPTRI); - SET_FLAG_FROM_TEST(type, (ext->iter_poly_bm || ext->iter_poly_mesh), MR_ITER_POLY); - SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge_mesh), MR_ITER_LEDGE); - SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert_mesh), MR_ITER_LVERT); - return type; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Triangles Indices - * \{ */ - -typedef struct MeshExtract_Tri_Data { - GPUIndexBufBuilder elb; - int *tri_mat_start; - int *tri_mat_end; -} MeshExtract_Tri_Data; - -static void *extract_tris_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - MeshExtract_Tri_Data *data = MEM_callocN(sizeof(*data), __func__); - - size_t mat_tri_idx_size = sizeof(int) * mr->mat_len; - data->tri_mat_start = MEM_callocN(mat_tri_idx_size, __func__); - data->tri_mat_end = MEM_callocN(mat_tri_idx_size, __func__); - - int *mat_tri_len = data->tri_mat_start; - /* Count how many triangle for each material. */ - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMIter iter; - BMFace *efa; - BM_ITER_MESH (efa, &iter, mr->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - int mat = min_ii(efa->mat_nr, mr->mat_len - 1); - mat_tri_len[mat] += efa->len - 2; - } - } - } - else { - const MPoly *mp = mr->mpoly; - for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - int mat = min_ii(mp->mat_nr, mr->mat_len - 1); - mat_tri_len[mat] += mp->totloop - 2; - } - } - } - /* Accumulate triangle lengths per material to have correct offsets. */ - int ofs = mat_tri_len[0]; - mat_tri_len[0] = 0; - for (int i = 1; i < mr->mat_len; i++) { - int tmp = mat_tri_len[i]; - mat_tri_len[i] = ofs; - ofs += tmp; - } - - memcpy(data->tri_mat_end, mat_tri_len, mat_tri_idx_size); - - int visible_tri_tot = ofs; - GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, visible_tri_tot, mr->loop_len); - - return data; -} - -static void extract_tris_iter_looptri_bm(const MeshRenderData *mr, - BMLoop **elt, - const int UNUSED(elt_index), - void *_data) -{ - MeshExtract_Tri_Data *data = _data; - const int mat_last = mr->mat_len - 1; - - if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { - int *mat_tri_ofs = data->tri_mat_end; - const int mat = min_ii(elt[0]->f->mat_nr, mat_last); - GPU_indexbuf_set_tri_verts(&data->elb, - mat_tri_ofs[mat]++, - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2])); - } -} - -static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr, - const MLoopTri *mlt, - const int UNUSED(elt_index), - void *_data) -{ - MeshExtract_Tri_Data *data = _data; - const int mat_last = mr->mat_len - 1; - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - int *mat_tri_ofs = data->tri_mat_end; - const int mat = min_ii(mp->mat_nr, mat_last); - GPU_indexbuf_set_tri_verts( - &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]); - } -} - -static void extract_tris_finish(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf, - void *_data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_Tri_Data *data = _data; - GPU_indexbuf_build_in_place(&data->elb, ibo); - - /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch - * is created before the surfaces-per-material. */ - if (mr->use_final_mesh && cache->final.tris_per_mat) { - MeshBufferCache *mbc_final = &cache->final; - for (int i = 0; i < mr->mat_len; i++) { - /* These IBOs have not been queried yet but we create them just in case they are needed - * later since they are not tracked by mesh_buffer_cache_create_requested(). */ - if (mbc_final->tris_per_mat[i] == NULL) { - mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc(); - } - /* Multiply by 3 because these are triangle indices. */ - const int mat_start = data->tri_mat_start[i]; - const int mat_end = data->tri_mat_end[i]; - const int start = mat_start * 3; - const int len = (mat_end - mat_start) * 3; - GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len); - } - } - MEM_freeN(data->tri_mat_start); - MEM_freeN(data->tri_mat_end); - MEM_freeN(data); -} - -static const MeshExtract extract_tris = { - .init = extract_tris_init, - .iter_looptri_bm = extract_tris_iter_looptri_bm, - .iter_looptri_mesh = extract_tris_iter_looptri_mesh, - .finish = extract_tris_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edges Indices - * \{ */ - -static void *extract_lines_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) -{ - GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); - /* Put loose edges at the end. */ - GPU_indexbuf_init( - elb, GPU_PRIM_LINES, mr->edge_len + mr->edge_loose_len, mr->loop_len + mr->loop_loose_len); - return elb; -} - -static void extract_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *elb) -{ - BMLoop *l_iter, *l_first; - /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */ - l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev; - do { - if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_line_verts(elb, - BM_elem_index_get(l_iter->e), - BM_elem_index_get(l_iter), - BM_elem_index_get(l_iter->next)); - } - else { - GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e)); - } - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_lines_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *elb) -{ - /* Using poly & loop iterator would complicate accessing the adjacent loop. */ - const MLoop *mloop = mr->mloop; - const MEdge *medge = mr->medge; - if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != NULL)) { - const int ml_index_last = mp->loopstart + (mp->totloop - 1); - int ml_index = ml_index_last, ml_index_next = mp->loopstart; - do { - const MLoop *ml = &mloop[ml_index]; - const MEdge *med = &medge[ml->e]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) { - GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); - } - else { - GPU_indexbuf_set_line_restart(elb, ml->e); - } - } while ((ml_index = ml_index_next++) != ml_index_last); - } - else { - const int ml_index_last = mp->loopstart + (mp->totloop - 1); - int ml_index = ml_index_last, ml_index_next = mp->loopstart; - do { - const MLoop *ml = &mloop[ml_index]; - GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); - } while ((ml_index = ml_index_next++) != ml_index_last); - } -} - -static void extract_lines_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *elb) -{ - const int l_index_offset = mr->edge_len + ledge_index; - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - const int l_index = mr->loop_len + ledge_index * 2; - GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); - } - else { - GPU_indexbuf_set_line_restart(elb, l_index_offset); - } - /* Don't render the edge twice. */ - GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed)); -} - -static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *med, - const uint ledge_index, - void *elb) -{ - const int l_index_offset = mr->edge_len + ledge_index; - const int e_index = mr->ledges[ledge_index]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { - const int l_index = mr->loop_len + ledge_index * 2; - GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); - } - else { - GPU_indexbuf_set_line_restart(elb, l_index_offset); - } - /* Don't render the edge twice. */ - GPU_indexbuf_set_line_restart(elb, e_index); -} - -static void extract_lines_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *elb) -{ - GPUIndexBuf *ibo = buf; - GPU_indexbuf_build_in_place(elb, ibo); - MEM_freeN(elb); -} - -static const MeshExtract extract_lines = { - .init = extract_lines_init, - .iter_poly_bm = extract_lines_iter_poly_bm, - .iter_poly_mesh = extract_lines_iter_poly_mesh, - .iter_ledge_bm = extract_lines_iter_ledge_bm, - .iter_ledge_mesh = extract_lines_iter_ledge_mesh, - .finish = extract_lines_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Loose Edges Sub Buffer - * \{ */ - -static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache) -{ - BLI_assert(cache->final.ibo.lines); - /* Multiply by 2 because these are edges indices. */ - const int start = mr->edge_len * 2; - const int len = mr->edge_loose_len * 2; - GPU_indexbuf_create_subrange_in_place( - cache->final.ibo.lines_loose, cache->final.ibo.lines, start, len); - cache->no_loose_wire = (len == 0); -} - -static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf, - void *elb) -{ - GPUIndexBuf *ibo = buf; - GPU_indexbuf_build_in_place(elb, ibo); - extract_lines_loose_subbuffer(mr, cache); - MEM_freeN(elb); -} - -static const MeshExtract extract_lines_with_lines_loose = { - .init = extract_lines_init, - .iter_poly_bm = extract_lines_iter_poly_bm, - .iter_poly_mesh = extract_lines_iter_poly_mesh, - .iter_ledge_bm = extract_lines_iter_ledge_bm, - .iter_ledge_mesh = extract_lines_iter_ledge_mesh, - .finish = extract_lines_with_lines_loose_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Point Indices - * \{ */ - -static void *extract_points_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) -{ - GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); - GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->vert_len, mr->loop_len + mr->loop_loose_len); - return elb; -} - -BLI_INLINE void vert_set_bm(GPUIndexBufBuilder *elb, BMVert *eve, int l_index) -{ - const int v_index = BM_elem_index_get(eve); - if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_point_vert(elb, v_index, l_index); - } - else { - GPU_indexbuf_set_point_restart(elb, v_index); - } -} - -BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb, - const MeshRenderData *mr, - const int v_index, - const int l_index) -{ - const MVert *mv = &mr->mvert[v_index]; - if (!((mr->use_hide && (mv->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && - (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) { - GPU_indexbuf_set_point_vert(elb, v_index, l_index); - } - else { - GPU_indexbuf_set_point_restart(elb, v_index); - } -} - -static void extract_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *elb) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - - vert_set_bm(elb, l_iter->v, l_index); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_points_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *elb) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - vert_set_mesh(elb, mr, ml->v, ml_index); - } -} - -static void extract_points_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *elb) -{ - vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2)); - vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1); -} - -static void extract_points_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *med, - const uint ledge_index, - void *elb) -{ - vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2)); - vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1); -} - -static void extract_points_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, - const int lvert_index, - void *elb) -{ - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - vert_set_bm(elb, eve, offset + lvert_index); -} - -static void extract_points_iter_lvert_mesh(const MeshRenderData *mr, - const MVert *UNUSED(mv), - const int lvert_index, - void *elb) -{ - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index); -} - -static void extract_points_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *elb) -{ - GPUIndexBuf *ibo = buf; - GPU_indexbuf_build_in_place(elb, ibo); - MEM_freeN(elb); -} - -static const MeshExtract extract_points = { - .init = extract_points_init, - .iter_poly_bm = extract_points_iter_poly_bm, - .iter_poly_mesh = extract_points_iter_poly_mesh, - .iter_ledge_bm = extract_points_iter_ledge_bm, - .iter_ledge_mesh = extract_points_iter_ledge_mesh, - .iter_lvert_bm = extract_points_iter_lvert_bm, - .iter_lvert_mesh = extract_points_iter_lvert_mesh, - .finish = extract_points_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Facedots Indices - * \{ */ - -static void *extract_fdots_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) -{ - GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); - GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); - return elb; -} - -static void extract_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int f_index, - void *elb) -{ - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_point_vert(elb, f_index, f_index); - } - else { - GPU_indexbuf_set_point_restart(elb, f_index); - } -} - -static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *elb) -{ - if (mr->use_subsurf_fdots) { - /* Check #ME_VERT_FACEDOT. */ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - const MVert *mv = &mr->mvert[ml->v]; - if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) { - GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); - return; - } - } - GPU_indexbuf_set_point_restart(elb, mp_index); - } - else { - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); - } - else { - GPU_indexbuf_set_point_restart(elb, mp_index); - } - } -} - -static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *elb) -{ - GPUIndexBuf *ibo = buf; - GPU_indexbuf_build_in_place(elb, ibo); - MEM_freeN(elb); -} - -static const MeshExtract extract_fdots = { - .init = extract_fdots_init, - .iter_poly_bm = extract_fdots_iter_poly_bm, - .iter_poly_mesh = extract_fdots_iter_poly_mesh, - .finish = extract_fdots_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Paint Mask Line Indices - * \{ */ - -typedef struct MeshExtract_LinePaintMask_Data { - GPUIndexBufBuilder elb; - /** One bit per edge set if face is selected. */ - BLI_bitmap select_map[0]; -} MeshExtract_LinePaintMask_Data; - -static void *extract_lines_paint_mask_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len); - MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->edge_len, mr->loop_len); - return data; -} - -static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *_data) -{ - MeshExtract_LinePaintMask_Data *data = _data; - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - const int e_index = ml->e; - const MEdge *me = &mr->medge[e_index]; - if (!((mr->use_hide && (me->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { - - const int ml_index_last = mp->totloop + mp->loopstart - 1; - const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); - if (mp->flag & ME_FACE_SEL) { - if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, e_index)) { - /* Hide edge as it has more than 2 selected loop. */ - GPU_indexbuf_set_line_restart(&data->elb, e_index); - } - else { - /* First selected loop. Set edge visible, overwriting any unselected loop. */ - GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other); - } - } - else { - /* Set these unselected loop only if this edge has no other selected loop. */ - if (!BLI_BITMAP_TEST(data->select_map, e_index)) { - GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other); - } - } - } - else { - GPU_indexbuf_set_line_restart(&data->elb, e_index); - } - } -} - -static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *_data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_LinePaintMask_Data *data = _data; - GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); -} - -static const MeshExtract extract_lines_paint_mask = { - .init = extract_lines_paint_mask_init, - .iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh, - .finish = extract_lines_paint_mask_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Line Adjacency Indices - * \{ */ - -#define NO_EDGE INT_MAX - -typedef struct MeshExtract_LineAdjacency_Data { - GPUIndexBufBuilder elb; - EdgeHash *eh; - bool is_manifold; - /* Array to convert vert index to any loop index of this vert. */ - uint vert_to_loop[0]; -} MeshExtract_LineAdjacency_Data; - -static void *extract_lines_adjacency_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) -{ - /* Similar to poly_to_tri_count(). - * There is always (loop + triangle - 1) edges inside a polygon. - * Accumulate for all polys and you get : */ - uint tess_edge_len = mr->loop_len + mr->tri_len - mr->poly_len; - - size_t vert_to_loop_size = sizeof(uint) * mr->vert_len; - - MeshExtract_LineAdjacency_Data *data = MEM_callocN(sizeof(*data) + vert_to_loop_size, __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES_ADJ, tess_edge_len, mr->loop_len); - data->eh = BLI_edgehash_new_ex(__func__, tess_edge_len); - data->is_manifold = true; - return data; -} - -BLI_INLINE void lines_adjacency_triangle( - uint v1, uint v2, uint v3, uint l1, uint l2, uint l3, MeshExtract_LineAdjacency_Data *data) -{ - GPUIndexBufBuilder *elb = &data->elb; - /* Iterate around the triangle's edges. */ - for (int e = 0; e < 3; e++) { - SHIFT3(uint, v3, v2, v1); - SHIFT3(uint, l3, l2, l1); - - bool inv_indices = (v2 > v3); - void **pval; - bool value_is_init = BLI_edgehash_ensure_p(data->eh, v2, v3, &pval); - int v_data = POINTER_AS_INT(*pval); - if (!value_is_init || v_data == NO_EDGE) { - /* Save the winding order inside the sign bit. Because the - * Edge-hash sort the keys and we need to compare winding later. */ - int value = (int)l1 + 1; /* 0 cannot be signed so add one. */ - *pval = POINTER_FROM_INT((inv_indices) ? -value : value); - /* Store loop indices for remaining non-manifold edges. */ - data->vert_to_loop[v2] = l2; - data->vert_to_loop[v3] = l3; - } - else { - /* HACK Tag as not used. Prevent overhead of BLI_edgehash_remove. */ - *pval = POINTER_FROM_INT(NO_EDGE); - bool inv_opposite = (v_data < 0); - uint l_opposite = (uint)abs(v_data) - 1; - /* TODO Make this part thread-safe. */ - if (inv_opposite == inv_indices) { - /* Don't share edge if triangles have non matching winding. */ - GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l1); - GPU_indexbuf_add_line_adj_verts(elb, l_opposite, l2, l3, l_opposite); - data->is_manifold = false; - } - else { - GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l_opposite); - } - } - } -} - -static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr), - BMLoop **elt, - const int UNUSED(elt_index), - void *data) -{ - if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { - lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), - BM_elem_index_get(elt[1]->v), - BM_elem_index_get(elt[2]->v), - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2]), - data); - } -} - -static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr, - const MLoopTri *mlt, - const int UNUSED(elt_index), - void *data) -{ - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, - mr->mloop[mlt->tri[1]].v, - mr->mloop[mlt->tri[2]].v, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2], - data); - } -} - -static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *cache, - void *buf, - void *_data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_LineAdjacency_Data *data = _data; - /* Create edges for remaining non manifold edges. */ - EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh); - for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { - uint v2, v3, l1, l2, l3; - int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi)); - if (v_data != NO_EDGE) { - BLI_edgehashIterator_getKey(ehi, &v2, &v3); - l1 = (uint)abs(v_data) - 1; - if (v_data < 0) { /* inv_opposite */ - SWAP(uint, v2, v3); - } - l2 = data->vert_to_loop[v2]; - l3 = data->vert_to_loop[v3]; - GPU_indexbuf_add_line_adj_verts(&data->elb, l1, l2, l3, l1); - data->is_manifold = false; - } - } - BLI_edgehashIterator_free(ehi); - BLI_edgehash_free(data->eh, NULL); - - cache->is_manifold = data->is_manifold; - - GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); -} - -#undef NO_EDGE - -static const MeshExtract extract_lines_adjacency = { - .init = extract_lines_adjacency_init, - .iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm, - .iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh, - .finish = extract_lines_adjacency_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Triangles Indices - * \{ */ - -typedef struct MeshExtract_EditUvElem_Data { - GPUIndexBufBuilder elb; - bool sync_selection; -} MeshExtract_EditUvElem_Data; - -static void *extract_edituv_tris_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len); - data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; -} - -BLI_INLINE void edituv_tri_add( - MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2, int v3) -{ - if (!hidden && (data->sync_selection || selected)) { - GPU_indexbuf_add_tri_verts(&data->elb, v1, v2, v3); - } -} - -static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr), - BMLoop **elt, - const int UNUSED(elt_index), - void *data) -{ - edituv_tri_add(data, - BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2])); -} - -static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr, - const MLoopTri *mlt, - const int UNUSED(elt_index), - void *data) -{ - const MPoly *mp = &mr->mpoly[mlt->poly]; - edituv_tri_add(data, - (mp->flag & ME_HIDE) != 0, - (mp->flag & ME_FACE_SEL) != 0, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2]); -} - -static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_EditUvElem_Data *extract_data = data; - GPU_indexbuf_build_in_place(&extract_data->elb, ibo); - MEM_freeN(extract_data); -} - -static const MeshExtract extract_edituv_tris = { - .init = extract_edituv_tris_init, - .iter_looptri_bm = extract_edituv_tris_iter_looptri_bm, - .iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh, - .finish = extract_edituv_tris_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Line Indices around faces - * \{ */ - -static void *extract_edituv_lines_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len); - - data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; -} - -BLI_INLINE void edituv_edge_add( - MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2) -{ - if (!hidden && (data->sync_selection || selected)) { - GPU_indexbuf_add_line_verts(&data->elb, v1, v2); - } -} - -static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - - edituv_edge_add(data, - BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), - BM_elem_flag_test_bool(f, BM_ELEM_SELECT), - l_index, - BM_elem_index_get(l_iter->next)); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - const int ml_index_last = mp->totloop + mp->loopstart - 1; - const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); - const bool real_edge = (mr->e_origindex == NULL || mr->e_origindex[ml->e] != ORIGINDEX_NONE); - edituv_edge_add(data, - (mp->flag & ME_HIDE) != 0 || !real_edge, - (mp->flag & ME_FACE_SEL) != 0, - ml_index, - ml_index_next); - } -} - -static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_EditUvElem_Data *extract_data = data; - GPU_indexbuf_build_in_place(&extract_data->elb, ibo); - MEM_freeN(extract_data); -} - -static const MeshExtract extract_edituv_lines = { - .init = extract_edituv_lines_init, - .iter_poly_bm = extract_edituv_lines_iter_poly_bm, - .iter_poly_mesh = extract_edituv_lines_iter_poly_mesh, - .finish = extract_edituv_lines_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Points Indices - * \{ */ - -static void *extract_edituv_points_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len); - - data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; -} - -BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data, - bool hidden, - bool selected, - int v1) -{ - if (!hidden && (data->sync_selection || selected)) { - GPU_indexbuf_add_point_vert(&data->elb, v1); - } -} - -static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - - edituv_point_add( - data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), l_index); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && - mr->v_origindex[ml->v] != ORIGINDEX_NONE); - edituv_point_add( - data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); - } -} - -static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_EditUvElem_Data *extract_data = data; - GPU_indexbuf_build_in_place(&extract_data->elb, ibo); - MEM_freeN(extract_data); -} - -static const MeshExtract extract_edituv_points = { - .init = extract_edituv_points_init, - .iter_poly_bm = extract_edituv_points_iter_poly_bm, - .iter_poly_mesh = extract_edituv_points_iter_poly_mesh, - .finish = extract_edituv_points_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Facedots Indices - * \{ */ - -static void *extract_edituv_fdots_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); - - data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; -} - -BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data, - bool hidden, - bool selected, - int face_index) -{ - if (!hidden && (data->sync_selection || selected)) { - GPU_indexbuf_set_point_vert(&data->elb, face_index, face_index); - } - else { - GPU_indexbuf_set_point_restart(&data->elb, face_index); - } -} - -static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int f_index, - void *data) -{ - edituv_facedot_add(data, - BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), - BM_elem_flag_test_bool(f, BM_ELEM_SELECT), - f_index); -} - -static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *data) -{ - if (mr->use_subsurf_fdots) { - /* Check #ME_VERT_FACEDOT. */ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[mp_index] != ORIGINDEX_NONE); - const bool subd_fdot = (!mr->use_subsurf_fdots || - (mr->mvert[ml->v].flag & ME_VERT_FACEDOT) != 0); - edituv_facedot_add(data, - ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot, - (mp->flag & ME_FACE_SEL) != 0, - mp_index); - } - } - else { - const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[mp_index] != ORIGINDEX_NONE); - edituv_facedot_add( - data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); - } -} - -static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *_data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_EditUvElem_Data *data = _data; - GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); -} - -static const MeshExtract extract_edituv_fdots = { - .init = extract_edituv_fdots_init, - .iter_poly_bm = extract_edituv_fdots_iter_poly_bm, - .iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh, - .finish = extract_edituv_fdots_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Position and Vertex Normal - * \{ */ - -typedef struct PosNorLoop { - float pos[3]; - GPUPackedNormal nor; -} PosNorLoop; - -typedef struct MeshExtract_PosNor_Data { - PosNorLoop *vbo_data; - GPUNormal normals[]; -} MeshExtract_PosNor_Data; - -static void *extract_pos_nor_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* WARNING Adjust #PosNorLoop struct accordingly. */ - GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - GPU_vertformat_alias_add(&format, "vnor"); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - - /* Pack normals per vert, reduce amount of computation. */ - size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len; - MeshExtract_PosNor_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__); - data->vbo_data = (PosNorLoop *)GPU_vertbuf_get_data(vbo); - - /* Quicker than doing it for each loop. */ - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMIter iter; - BMVert *eve; - int v; - BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) { - data->normals[v].low = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, eve)); - } - } - else { - const MVert *mv = mr->mvert; - for (int v = 0; v < mr->vert_len; v++, mv++) { - data->normals[v].low = GPU_normal_convert_i10_s3(mv->no); - } - } - return data; -} - -static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - MeshExtract_PosNor_Data *data = _data; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); - vert->nor = data->normals[BM_elem_index_get(l_iter->v)].low; - vert->nor.w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *_data) -{ - MeshExtract_PosNor_Data *data = _data; - - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - PosNorLoop *vert = &data->vbo_data[ml_index]; - const MVert *mv = &mr->mvert[ml->v]; - copy_v3_v3(vert->pos, mv->co); - vert->nor = data->normals[ml->v].low; - /* Flag for paint mode overlay. */ - if (mp->flag & ME_HIDE || mv->flag & ME_HIDE || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && - (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) { - vert->nor.w = -1; - } - else if (mv->flag & SELECT) { - vert->nor.w = 1; - } - else { - vert->nor.w = 0; - } - } -} - -static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *_data) -{ - MeshExtract_PosNor_Data *data = _data; - - int l_index = mr->loop_len + ledge_index * 2; - PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); - copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); - vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low; - vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low; -} - -static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *med, - const uint ledge_index, - void *_data) -{ - MeshExtract_PosNor_Data *data = _data; - const int ml_index = mr->loop_len + ledge_index * 2; - PosNorLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); - copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); - vert[0].nor = data->normals[med->v1].low; - vert[1].nor = data->normals[med->v2].low; -} - -static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, - const int lvert_index, - void *_data) -{ - MeshExtract_PosNor_Data *data = _data; - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - const int l_index = offset + lvert_index; - PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); - vert->nor = data->normals[BM_elem_index_get(eve)].low; -} - -static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr, - const MVert *mv, - const int lvert_index, - void *_data) -{ - MeshExtract_PosNor_Data *data = _data; - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - const int ml_index = offset + lvert_index; - const int v_index = mr->lverts[lvert_index]; - PosNorLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert->pos, mv->co); - vert->nor = data->normals[v_index].low; -} - -static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_pos_nor = { - .init = extract_pos_nor_init, - .iter_poly_bm = extract_pos_nor_iter_poly_bm, - .iter_poly_mesh = extract_pos_nor_iter_poly_mesh, - .iter_ledge_bm = extract_pos_nor_iter_ledge_bm, - .iter_ledge_mesh = extract_pos_nor_iter_ledge_mesh, - .iter_lvert_bm = extract_pos_nor_iter_lvert_bm, - .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh, - .finish = extract_pos_nor_finish, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Position and High Quality Vertex Normal - * \{ */ - -typedef struct PosNorHQLoop { - float pos[3]; - short nor[4]; -} PosNorHQLoop; - -typedef struct MeshExtract_PosNorHQ_Data { - PosNorHQLoop *vbo_data; - GPUNormal normals[]; -} MeshExtract_PosNorHQ_Data; - -static void *extract_pos_nor_hq_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* WARNING Adjust #PosNorHQLoop struct accordingly. */ - GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - GPU_vertformat_alias_add(&format, "vnor"); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - - /* Pack normals per vert, reduce amount of computation. */ - size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len; - MeshExtract_PosNorHQ_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__); - data->vbo_data = (PosNorHQLoop *)GPU_vertbuf_get_data(vbo); - - /* Quicker than doing it for each loop. */ - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMIter iter; - BMVert *eve; - int v; - BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) { - normal_float_to_short_v3(data->normals[v].high, bm_vert_no_get(mr, eve)); - } - } - else { - const MVert *mv = mr->mvert; - for (int v = 0; v < mr->vert_len; v++, mv++) { - copy_v3_v3_short(data->normals[v].high, mv->no); - } - } - return data; -} - -static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - MeshExtract_PosNorHQ_Data *data = _data; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); - copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l_iter->v)].high); - - BMFace *efa = l_iter->f; - vert->nor[3] = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *_data) -{ - MeshExtract_PosNorHQ_Data *data = _data; - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - PosNorHQLoop *vert = &data->vbo_data[ml_index]; - const MVert *mv = &mr->mvert[ml->v]; - copy_v3_v3(vert->pos, mv->co); - copy_v3_v3_short(vert->nor, data->normals[ml->v].high); - - /* Flag for paint mode overlay. */ - if (mp->flag & ME_HIDE || mv->flag & ME_HIDE || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && - (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) { - vert->nor[3] = -1; - } - else if (mv->flag & SELECT) { - vert->nor[3] = 1; - } - else { - vert->nor[3] = 0; - } - } -} - -static void extract_pos_nor_hq_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *_data) -{ - MeshExtract_PosNorHQ_Data *data = _data; - int l_index = mr->loop_len + ledge_index * 2; - PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); - copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); - copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high); - vert[0].nor[3] = 0; - copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high); - vert[1].nor[3] = 0; -} - -static void extract_pos_nor_hq_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *med, - const uint ledge_index, - void *_data) -{ - MeshExtract_PosNorHQ_Data *data = _data; - const int ml_index = mr->loop_len + ledge_index * 2; - PosNorHQLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); - copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); - copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high); - vert[0].nor[3] = 0; - copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high); - vert[1].nor[3] = 0; -} - -static void extract_pos_nor_hq_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, - const int lvert_index, - void *_data) -{ - MeshExtract_PosNorHQ_Data *data = _data; - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - const int l_index = offset + lvert_index; - PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); - copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high); - vert->nor[3] = 0; -} - -static void extract_pos_nor_hq_iter_lvert_mesh(const MeshRenderData *mr, - const MVert *mv, - const int lvert_index, - void *_data) -{ - MeshExtract_PosNorHQ_Data *data = _data; - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - const int ml_index = offset + lvert_index; - const int v_index = mr->lverts[lvert_index]; - PosNorHQLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert->pos, mv->co); - copy_v3_v3_short(vert->nor, data->normals[v_index].high); - vert->nor[3] = 0; -} - -static void extract_pos_nor_hq_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_pos_nor_hq = { - .init = extract_pos_nor_hq_init, - .iter_poly_bm = extract_pos_nor_hq_iter_poly_bm, - .iter_poly_mesh = extract_pos_nor_hq_iter_poly_mesh, - .iter_ledge_bm = extract_pos_nor_hq_iter_ledge_bm, - .iter_ledge_mesh = extract_pos_nor_hq_iter_ledge_mesh, - .iter_lvert_bm = extract_pos_nor_hq_iter_lvert_bm, - .iter_lvert_mesh = extract_pos_nor_hq_iter_lvert_mesh, - .finish = extract_pos_nor_hq_finish, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; - -/** \} */ -/* ---------------------------------------------------------------------- */ -/** \name Extract HQ Loop Normal - * \{ */ - -typedef struct gpuHQNor { - short x, y, z, w; -} gpuHQNor; - -static void *extract_lnor_hq_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - GPU_vertformat_alias_add(&format, "lnor"); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - return GPU_vertbuf_get_data(vbo); -} - -static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - if (mr->loop_normals) { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, mr->loop_normals[l_index]); - } - else { - if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l_iter->v)); - } - else { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, f)); - } - } - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - gpuHQNor *lnor_data = &((gpuHQNor *)data)[ml_index]; - if (mr->loop_normals) { - normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[ml_index]); - } - else if (mp->flag & ME_SMOOTH) { - copy_v3_v3_short(&lnor_data->x, mr->mvert[ml->v].no); - } - else { - normal_float_to_short_v3(&lnor_data->x, mr->poly_normals[mp_index]); - } - - /* Flag for paint mode overlay. - * Only use #MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. - * In paint mode it will use the un-mapped data to draw the wire-frame. */ - if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && - (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { - lnor_data->w = -1; - } - else if (mp->flag & ME_FACE_SEL) { - lnor_data->w = 1; - } - else { - lnor_data->w = 0; - } - } -} - -static const MeshExtract extract_lnor_hq = { - .init = extract_lnor_hq_init, - .iter_poly_bm = extract_lnor_hq_iter_poly_bm, - .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh, - .data_flag = MR_DATA_LOOP_NOR, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; - -/** \} */ -/* ---------------------------------------------------------------------- */ -/** \name Extract Loop Normal - * \{ */ - -static void *extract_lnor_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - GPU_vertformat_alias_add(&format, "lnor"); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - return GPU_vertbuf_get_data(vbo); -} - -static void extract_lnor_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - if (mr->loop_normals) { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]); - } - else { - if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3( - bm_vert_no_get(mr, l_iter->v)); - } - else { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, f)); - } - } - ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - GPUPackedNormal *lnor_data = &((GPUPackedNormal *)data)[ml_index]; - if (mr->loop_normals) { - *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[ml_index]); - } - else if (mp->flag & ME_SMOOTH) { - *lnor_data = GPU_normal_convert_i10_s3(mr->mvert[ml->v].no); - } - else { - *lnor_data = GPU_normal_convert_i10_v3(mr->poly_normals[mp_index]); - } - - /* Flag for paint mode overlay. - * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. - * In paint mode it will use the un-mapped data to draw the wire-frame. */ - if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && - (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { - lnor_data->w = -1; - } - else if (mp->flag & ME_FACE_SEL) { - lnor_data->w = 1; - } - else { - lnor_data->w = 0; - } - } -} - -static const MeshExtract extract_lnor = { - .init = extract_lnor_init, - .iter_poly_bm = extract_lnor_iter_poly_bm, - .iter_poly_mesh = extract_lnor_iter_poly_mesh, - .data_flag = MR_DATA_LOOP_NOR, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract UV layers - * \{ */ - -static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) -{ - GPUVertBuf *vbo = buf; - GPUVertFormat format = {0}; - GPU_vertformat_deinterleave(&format); - - CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - uint32_t uv_layers = cache->cd_used.uv; - /* HACK to fix T68857 */ - if (mr->extract_type == MR_EXTRACT_BMESH && cache->cd_used.edit_uv == 1) { - int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV); - if (layer != -1) { - uv_layers |= (1 << layer); - } - } - - for (int i = 0; i < MAX_MTFACE; i++) { - if (uv_layers & (1 << i)) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i); - - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - /* UV layer name. */ - BLI_snprintf(attr_name, sizeof(attr_name), "u%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - /* Auto layer name. */ - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); - GPU_vertformat_alias_add(&format, attr_name); - /* Active render layer name. */ - if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) { - GPU_vertformat_alias_add(&format, "u"); - } - /* Active display layer name. */ - if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) { - GPU_vertformat_alias_add(&format, "au"); - /* Alias to `pos` for edit uvs. */ - GPU_vertformat_alias_add(&format, "pos"); - } - /* Stencil mask uv layer name. */ - if (i == CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV)) { - GPU_vertformat_alias_add(&format, "mu"); - } - } - } - - int v_len = mr->loop_len; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - /* VBO will not be used, only allocate minimum of memory. */ - v_len = 1; - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, v_len); - - float(*uv_data)[2] = (float(*)[2])GPU_vertbuf_get_data(vbo); - for (int i = 0; i < MAX_MTFACE; i++) { - if (uv_layers & (1 << i)) { - if (mr->extract_type == MR_EXTRACT_BMESH) { - int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPUV, i); - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs); - memcpy(uv_data, luv->uv, sizeof(*uv_data)); - uv_data++; - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - MLoopUV *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, uv_data++, layer_data++) { - memcpy(uv_data, layer_data->uv, sizeof(*uv_data)); - } - } - } - } - - return NULL; -} - -static const MeshExtract extract_uv = {.init = extract_uv_init, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Tangent layers - * \{ */ - -static void extract_tan_ex_init(const MeshRenderData *mr, - struct MeshBatchCache *cache, - GPUVertBuf *vbo, - const bool do_hq) -{ - GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10; - GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT; - - GPUVertFormat format = {0}; - GPU_vertformat_deinterleave(&format); - - CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; - uint32_t tan_layers = cache->cd_used.tan; - float(*orco)[3] = CustomData_get_layer(cd_vdata, CD_ORCO); - bool orco_allocated = false; - const bool use_orco_tan = cache->cd_used.tan_orco != 0; - - int tan_len = 0; - char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; - - for (int i = 0; i < MAX_MTFACE; i++) { - if (tan_layers & (1 << i)) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - /* Tangent layer name. */ - BLI_snprintf(attr_name, sizeof(attr_name), "t%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode); - /* Active render layer name. */ - if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) { - GPU_vertformat_alias_add(&format, "t"); - } - /* Active display layer name. */ - if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) { - GPU_vertformat_alias_add(&format, "at"); - } - - BLI_strncpy(tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME); - } - } - if (use_orco_tan && orco == NULL) { - /* If `orco` is not available compute it ourselves */ - orco_allocated = true; - orco = MEM_mallocN(sizeof(*orco) * mr->vert_len, __func__); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMesh *bm = mr->bm; - for (int v = 0; v < mr->vert_len; v++) { - const BMVert *eve = BM_vert_at_index(bm, v); - /* Exceptional case where #bm_vert_co_get can be avoided, as we want the original coords. - * not the distorted ones. */ - copy_v3_v3(orco[v], eve->co); - } - } - else { - const MVert *mv = mr->mvert; - for (int v = 0; v < mr->vert_len; v++, mv++) { - copy_v3_v3(orco[v], mv->co); - } - } - BKE_mesh_orco_verts_transform(mr->me, orco, mr->vert_len, 0); - } - - /* Start Fresh */ - CustomData loop_data; - CustomData_reset(&loop_data); - if (tan_len != 0 || use_orco_tan) { - short tangent_mask = 0; - bool calc_active_tangent = false; - if (mr->extract_type == MR_EXTRACT_BMESH) { - BKE_editmesh_loop_tangent_calc(mr->edit_bmesh, - calc_active_tangent, - tangent_names, - tan_len, - mr->poly_normals, - mr->loop_normals, - orco, - &loop_data, - mr->loop_len, - &tangent_mask); - } - else { - BKE_mesh_calc_loop_tangent_ex(mr->mvert, - mr->mpoly, - mr->poly_len, - mr->mloop, - mr->mlooptri, - mr->tri_len, - cd_ldata, - calc_active_tangent, - tangent_names, - tan_len, - mr->poly_normals, - mr->loop_normals, - orco, - &loop_data, - mr->loop_len, - &tangent_mask); - } - } - - if (use_orco_tan) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(&loop_data, CD_TANGENT, 0); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - BLI_snprintf(attr_name, sizeof(*attr_name), "t%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode); - GPU_vertformat_alias_add(&format, "t"); - GPU_vertformat_alias_add(&format, "at"); - } - - if (orco_allocated) { - MEM_SAFE_FREE(orco); - } - - int v_len = mr->loop_len; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - /* VBO will not be used, only allocate minimum of memory. */ - v_len = 1; - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, v_len); - - if (do_hq) { - short(*tan_data)[4] = (short(*)[4])GPU_vertbuf_get_data(vbo); - for (int i = 0; i < tan_len; i++) { - const char *name = tangent_names[i]; - float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named( - &loop_data, CD_TANGENT, name); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { - normal_float_to_short_v3(*tan_data, layer_data[ml_index]); - (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN; - tan_data++; - } - } - if (use_orco_tan) { - float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { - normal_float_to_short_v3(*tan_data, layer_data[ml_index]); - (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN; - tan_data++; - } - } - } - else { - GPUPackedNormal *tan_data = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo); - for (int i = 0; i < tan_len; i++) { - const char *name = tangent_names[i]; - float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named( - &loop_data, CD_TANGENT, name); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { - *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]); - tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2; - tan_data++; - } - } - if (use_orco_tan) { - float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { - *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]); - tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2; - tan_data++; - } - } - } - - CustomData_free(&loop_data, mr->loop_len); -} - -static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) -{ - extract_tan_ex_init(mr, cache, buf, false); - return NULL; -} - -static const MeshExtract extract_tan = {.init = extract_tan_init, - .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | - MR_DATA_LOOPTRI, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract HQ Tangent layers - * \{ */ - -static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) -{ - extract_tan_ex_init(mr, cache, buf, true); - return NULL; -} - -static const MeshExtract extract_tan_hq = { - .init = extract_tan_hq_init, - .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, - .use_threading = false, -}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Sculpt Data - * \{ */ - -static void *extract_sculpt_data_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - GPUVertFormat format = {0}; - - CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; - CustomData *cd_pdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata; - - float *cd_mask = CustomData_get_layer(cd_vdata, CD_PAINT_MASK); - int *cd_face_set = CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); - - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "fset", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - GPU_vertformat_attr_add(&format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - typedef struct gpuSculptData { - uint8_t face_set_color[4]; - float mask; - } gpuSculptData; - - gpuSculptData *vbo_data = (gpuSculptData *)GPU_vertbuf_get_data(vbo); - MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - int cd_mask_ofs = CustomData_get_offset(cd_vdata, CD_PAINT_MASK); - int cd_face_set_ofs = CustomData_get_offset(cd_pdata, CD_SCULPT_FACE_SETS); - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - float v_mask = 0.0f; - if (cd_mask) { - v_mask = BM_ELEM_CD_GET_FLOAT(l_iter->v, cd_mask_ofs); - } - vbo_data->mask = v_mask; - uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX}; - if (cd_face_set) { - const int face_set_id = BM_ELEM_CD_GET_INT(l_iter->f, cd_face_set_ofs); - if (face_set_id != mr->me->face_sets_color_default) { - BKE_paint_face_set_overlay_color_get( - face_set_id, mr->me->face_sets_color_seed, face_set_color); - } - } - copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color); - vbo_data++; - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - int mp_loop = 0; - for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { - const MPoly *p = &mr->mpoly[mp_index]; - for (int l = 0; l < p->totloop; l++) { - float v_mask = 0.0f; - if (cd_mask) { - v_mask = cd_mask[loops[mp_loop].v]; - } - vbo_data->mask = v_mask; - - uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX}; - if (cd_face_set) { - const int face_set_id = cd_face_set[mp_index]; - /* Skip for the default color Face Set to render it white. */ - if (face_set_id != mr->me->face_sets_color_default) { - BKE_paint_face_set_overlay_color_get( - face_set_id, mr->me->face_sets_color_seed, face_set_color); - } - } - copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color); - mp_loop++; - vbo_data++; - } - } - } - - return NULL; -} - -static const MeshExtract extract_sculpt_data = { - .init = extract_sculpt_data_init, - .data_flag = 0, - /* TODO: enable threading. */ - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract VCol - * \{ */ - -static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) -{ - GPUVertBuf *vbo = buf; - GPUVertFormat format = {0}; - GPU_vertformat_deinterleave(&format); - - CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; - uint32_t vcol_layers = cache->cd_used.vcol; - uint32_t svcol_layers = cache->cd_used.sculpt_vcol; - - for (int i = 0; i < MAX_MCOL; i++) { - if (vcol_layers & (1 << i)) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - - BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - - if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL)) { - GPU_vertformat_alias_add(&format, "c"); - } - if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL)) { - GPU_vertformat_alias_add(&format, "ac"); - } - - /* Gather number of auto layers. */ - /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 && - CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) { - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); - GPU_vertformat_alias_add(&format, attr_name); - } - } - } - - /* Sculpt Vertex Colors */ - if (U.experimental.use_sculpt_vertex_colors) { - for (int i = 0; i < 8; i++) { - if (svcol_layers & (1 << i)) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - - BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - - if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "c"); - } - if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "ac"); - } - /* Gather number of auto layers. */ - /* We only do `vcols` that are not overridden by `uvs`. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); - GPU_vertformat_alias_add(&format, attr_name); - } - } - } - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - typedef struct gpuMeshVcol { - ushort r, g, b, a; - } gpuMeshVcol; - - gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo); - MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP); - - for (int i = 0; i < MAX_MCOL; i++) { - if (vcol_layers & (1 << i)) { - if (mr->extract_type == MR_EXTRACT_BMESH) { - int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPCOL, i); - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - const MLoopCol *mloopcol = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs); - vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]); - vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]); - vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]); - vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f)); - vcol_data++; - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - const MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, mloopcol++, vcol_data++) { - vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]); - vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]); - vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]); - vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f)); - } - } - } - - if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) { - if (mr->extract_type == MR_EXTRACT_BMESH) { - int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i); - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - const MPropCol *prop_col = BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs); - vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]); - vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]); - vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]); - vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]); - vcol_data++; - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - MPropCol *vcol = CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) { - vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]); - vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]); - vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]); - vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]); - } - } - } - } - return NULL; -} - -static const MeshExtract extract_vcol = { - .init = extract_vcol_init, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Orco - * \{ */ - -typedef struct MeshExtract_Orco_Data { - float (*vbo_data)[4]; - float (*orco)[3]; -} MeshExtract_Orco_Data; - -static void *extract_orco_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex - * attributes. This is a substantial waste of video-ram and should be done another way. - * Unfortunately, at the time of writing, I did not found any other "non disruptive" - * alternative. */ - GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - CustomData *cd_vdata = &mr->me->vdata; - - MeshExtract_Orco_Data *data = MEM_mallocN(sizeof(*data), __func__); - data->vbo_data = (float(*)[4])GPU_vertbuf_get_data(vbo); - data->orco = CustomData_get_layer(cd_vdata, CD_ORCO); - /* Make sure `orco` layer was requested only if needed! */ - BLI_assert(data->orco); - return data; -} - -static void extract_orco_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - float *loop_orco = orco_data->vbo_data[l_index]; - copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(l_iter->v)]); - loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; - float *loop_orco = orco_data->vbo_data[ml_index]; - copy_v3_v3(loop_orco, orco_data->orco[ml->v]); - loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ - } -} - -static void extract_orco_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_orco = { - .init = extract_orco_init, - .iter_poly_bm = extract_orco_iter_poly_bm, - .iter_poly_mesh = extract_orco_iter_poly_mesh, - .finish = extract_orco_finish, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edge Factor - * Defines how much an edge is visible. - * \{ */ - -typedef struct MeshExtract_EdgeFac_Data { - uchar *vbo_data; - bool use_edge_render; - /* Number of loop per edge. */ - uchar edge_loop_count[0]; -} MeshExtract_EdgeFac_Data; - -static float loop_edge_factor_get(const float f_no[3], - const float v_co[3], - const float v_no[3], - const float v_next_co[3]) -{ - float enor[3], evec[3]; - sub_v3_v3v3(evec, v_next_co, v_co); - cross_v3_v3v3(enor, v_no, evec); - normalize_v3(enor); - float d = fabsf(dot_v3v3(enor, f_no)); - /* Re-scale to the slider range. */ - d *= (1.0f / 0.065f); - CLAMP(d, 0.0f, 1.0f); - return d; -} - -static void *extract_edge_fac_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - - MeshExtract_EdgeFac_Data *data; - - if (mr->extract_type == MR_EXTRACT_MESH) { - size_t edge_loop_count_size = sizeof(uint32_t) * mr->edge_len; - data = MEM_callocN(sizeof(*data) + edge_loop_count_size, __func__); - - /* HACK(fclem) Detecting the need for edge render. - * We could have a flag in the mesh instead or check the modifier stack. */ - const MEdge *med = mr->medge; - for (int e_index = 0; e_index < mr->edge_len; e_index++, med++) { - if ((med->flag & ME_EDGERENDER) == 0) { - data->use_edge_render = true; - break; - } - } - } - else { - data = MEM_callocN(sizeof(*data), __func__); - /* HACK to bypass non-manifold check in mesh_edge_fac_finish(). */ - data->use_edge_render = true; - } - - data->vbo_data = GPU_vertbuf_get_data(vbo); - return data; -} - -static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - MeshExtract_EdgeFac_Data *data = _data; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - - if (BM_edge_is_manifold(l_iter->e)) { - float ratio = loop_edge_factor_get(bm_face_no_get(mr, f), - bm_vert_co_get(mr, l_iter->v), - bm_vert_no_get(mr, l_iter->v), - bm_vert_co_get(mr, l_iter->next->v)); - data->vbo_data[l_index] = ratio * 253 + 1; - } - else { - data->vbo_data[l_index] = 255; - } - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *_data) -{ - MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data; - - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - if (data->use_edge_render) { - const MEdge *med = &mr->medge[ml->e]; - data->vbo_data[ml_index] = (med->flag & ME_EDGERENDER) ? 255 : 0; - } - else { - - /* Count loop per edge to detect non-manifold. */ - if (data->edge_loop_count[ml->e] < 3) { - data->edge_loop_count[ml->e]++; - } - if (data->edge_loop_count[ml->e] == 2) { - /* Manifold */ - const int ml_index_last = mp->totloop + mp->loopstart - 1; - const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); - const MLoop *ml_next = &mr->mloop[ml_index_other]; - const MVert *v1 = &mr->mvert[ml->v]; - const MVert *v2 = &mr->mvert[ml_next->v]; - float vnor_f[3]; - normal_short_to_float_v3(vnor_f, v1->no); - float ratio = loop_edge_factor_get(mr->poly_normals[mp_index], v1->co, vnor_f, v2->co); - data->vbo_data[ml_index] = ratio * 253 + 1; - } - else { - /* Non-manifold */ - data->vbo_data[ml_index] = 255; - } - } - } -} - -static void extract_edge_fac_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *UNUSED(eed), - const int ledge_index, - void *_data) -{ - MeshExtract_EdgeFac_Data *data = _data; - data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255; - data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255; -} - -static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *UNUSED(med), - const uint ledge_index, - void *_data) -{ - MeshExtract_EdgeFac_Data *data = _data; - - data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255; - data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255; -} - -static void extract_edge_fac_finish(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *_data) -{ - GPUVertBuf *vbo = buf; - MeshExtract_EdgeFac_Data *data = _data; - - if (GPU_crappy_amd_driver()) { - /* Some AMD drivers strangely crash with VBO's with a one byte format. - * To workaround we reinitialize the VBO with another format and convert - * all bytes to floats. */ - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - } - /* We keep the data reference in data->vbo_data. */ - data->vbo_data = GPU_vertbuf_steal_data(vbo); - GPU_vertbuf_clear(vbo); - - int buf_len = mr->loop_len + mr->loop_loose_len; - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, buf_len); - - float *fdata = (float *)GPU_vertbuf_get_data(vbo); - for (int ml_index = 0; ml_index < buf_len; ml_index++, fdata++) { - *fdata = data->vbo_data[ml_index] / 255.0f; - } - /* Free old byte data. */ - MEM_freeN(data->vbo_data); - } - MEM_freeN(data); -} - -static const MeshExtract extract_edge_fac = { - .init = extract_edge_fac_init, - .iter_poly_bm = extract_edge_fac_iter_poly_bm, - .iter_poly_mesh = extract_edge_fac_iter_poly_mesh, - .iter_ledge_bm = extract_edge_fac_iter_ledge_bm, - .iter_ledge_mesh = extract_edge_fac_iter_ledge_mesh, - .finish = extract_edge_fac_finish, - .data_flag = MR_DATA_POLY_NOR, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac)}; - -/** \} */ -/* ---------------------------------------------------------------------- */ -/** \name Extract Vertex Weight - * \{ */ - -typedef struct MeshExtract_Weight_Data { - float *vbo_data; - const DRW_MeshWeightState *wstate; - const MDeformVert *dvert; /* For #Mesh. */ - int cd_ofs; /* For #BMesh. */ -} MeshExtract_Weight_Data; - -static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeightState *wstate) -{ - /* Error state. */ - if ((wstate->defgroup_active < 0) && (wstate->defgroup_len > 0)) { - return -2.0f; - } - if (dvert == NULL) { - return (wstate->alert_mode != OB_DRAW_GROUPUSER_NONE) ? -1.0f : 0.0f; - } - - float input = 0.0f; - if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) { - /* Multi-Paint feature */ - bool is_normalized = (wstate->flags & (DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE | - DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE)); - input = BKE_defvert_multipaint_collective_weight(dvert, - wstate->defgroup_len, - wstate->defgroup_sel, - wstate->defgroup_sel_count, - is_normalized); - /* make it black if the selected groups have no weight on a vertex */ - if (input == 0.0f) { - return -1.0f; - } - } - else { - /* default, non tricky behavior */ - input = BKE_defvert_find_weight(dvert, wstate->defgroup_active); - - if (input == 0.0f) { - switch (wstate->alert_mode) { - case OB_DRAW_GROUPUSER_ACTIVE: - return -1.0f; - break; - case OB_DRAW_GROUPUSER_ALL: - if (BKE_defvert_is_weight_zero(dvert, wstate->defgroup_len)) { - return -1.0f; - } - break; - } - } - } - - /* Lock-Relative: display the fraction of current weight vs total unlocked weight. */ - if (wstate->flags & DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE) { - input = BKE_defvert_lock_relative_weight( - input, dvert, wstate->defgroup_len, wstate->defgroup_locked, wstate->defgroup_unlocked); - } - - CLAMP(input, 0.0f, 1.0f); - return input; -} - -static void *extract_weights_init(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - - MeshExtract_Weight_Data *data = MEM_callocN(sizeof(*data), __func__); - data->vbo_data = (float *)GPU_vertbuf_get_data(vbo); - data->wstate = &cache->weight_state; - - if (data->wstate->defgroup_active == -1) { - /* Nothing to show. */ - data->dvert = NULL; - data->cd_ofs = -1; - } - else if (mr->extract_type == MR_EXTRACT_BMESH) { - data->dvert = NULL; - data->cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MDEFORMVERT); - } - else { - data->dvert = CustomData_get_layer(&mr->me->vdata, CD_MDEFORMVERT); - data->cd_ofs = -1; - } - return data; -} - -static void extract_weights_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - MeshExtract_Weight_Data *data = _data; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - if (data->cd_ofs != -1) { - const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l_iter->v, data->cd_ofs); - data->vbo_data[l_index] = evaluate_vertex_weight(dvert, data->wstate); - } - else { - data->vbo_data[l_index] = evaluate_vertex_weight(NULL, data->wstate); - } - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_weights_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *_data) -{ - MeshExtract_Weight_Data *data = _data; - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - if (data->dvert != NULL) { - const MDeformVert *dvert = &data->dvert[ml->v]; - data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); - } - else { - const MDeformVert *dvert = NULL; - data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); - } - } -} - -static void extract_weights_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_weights = { - .init = extract_weights_init, - .iter_poly_bm = extract_weights_iter_poly_bm, - .iter_poly_mesh = extract_weights_iter_poly_mesh, - .finish = extract_weights_finish, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit Mode Data / Flags - * \{ */ - -typedef struct EditLoopData { - uchar v_flag; - uchar e_flag; - uchar crease; - uchar bweight; -} EditLoopData; - -static void mesh_render_data_face_flag(const MeshRenderData *mr, - BMFace *efa, - const int cd_ofs, - EditLoopData *eattr) -{ - if (efa == mr->efa_act) { - eattr->v_flag |= VFLAG_FACE_ACTIVE; - } - if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - eattr->v_flag |= VFLAG_FACE_SELECTED; - } - - if (efa == mr->efa_act_uv) { - eattr->v_flag |= VFLAG_FACE_UV_ACTIVE; - } - if ((cd_ofs != -1) && uvedit_face_select_test_ex(mr->toolsettings, (BMFace *)efa, cd_ofs)) { - eattr->v_flag |= VFLAG_FACE_UV_SELECT; - } - -#ifdef WITH_FREESTYLE - if (mr->freestyle_face_ofs != -1) { - const FreestyleFace *ffa = BM_ELEM_CD_GET_VOID_P(efa, mr->freestyle_face_ofs); - if (ffa->flag & FREESTYLE_FACE_MARK) { - eattr->v_flag |= VFLAG_FACE_FREESTYLE; - } - } -#endif -} - -static void mesh_render_data_edge_flag(const MeshRenderData *mr, BMEdge *eed, EditLoopData *eattr) -{ - const ToolSettings *ts = mr->toolsettings; - const bool is_vertex_select_mode = (ts != NULL) && (ts->selectmode & SCE_SELECT_VERTEX) != 0; - const bool is_face_only_select_mode = (ts != NULL) && (ts->selectmode == SCE_SELECT_FACE); - - if (eed == mr->eed_act) { - eattr->e_flag |= VFLAG_EDGE_ACTIVE; - } - if (!is_vertex_select_mode && BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - eattr->e_flag |= VFLAG_EDGE_SELECTED; - } - if (is_vertex_select_mode && BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) && - BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) { - eattr->e_flag |= VFLAG_EDGE_SELECTED; - eattr->e_flag |= VFLAG_VERT_SELECTED; - } - if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) { - eattr->e_flag |= VFLAG_EDGE_SEAM; - } - if (!BM_elem_flag_test(eed, BM_ELEM_SMOOTH)) { - eattr->e_flag |= VFLAG_EDGE_SHARP; - } - - /* Use active edge color for active face edges because - * specular highlights make it hard to see T55456#510873. - * - * This isn't ideal since it can't be used when mixing edge/face modes - * but it's still better than not being able to see the active face. */ - if (is_face_only_select_mode) { - if (mr->efa_act != NULL) { - if (BM_edge_in_face(eed, mr->efa_act)) { - eattr->e_flag |= VFLAG_EDGE_ACTIVE; - } - } - } - - /* Use a byte for value range */ - if (mr->crease_ofs != -1) { - float crease = BM_ELEM_CD_GET_FLOAT(eed, mr->crease_ofs); - if (crease > 0) { - eattr->crease = (uchar)(crease * 255.0f); - } - } - /* Use a byte for value range */ - if (mr->bweight_ofs != -1) { - float bweight = BM_ELEM_CD_GET_FLOAT(eed, mr->bweight_ofs); - if (bweight > 0) { - eattr->bweight = (uchar)(bweight * 255.0f); - } - } -#ifdef WITH_FREESTYLE - if (mr->freestyle_edge_ofs != -1) { - const FreestyleEdge *fed = BM_ELEM_CD_GET_VOID_P(eed, mr->freestyle_edge_ofs); - if (fed->flag & FREESTYLE_EDGE_MARK) { - eattr->e_flag |= VFLAG_EDGE_FREESTYLE; - } - } -#endif -} - -static void mesh_render_data_loop_flag(const MeshRenderData *mr, - BMLoop *l, - const int cd_ofs, - EditLoopData *eattr) -{ - if (cd_ofs == -1) { - return; - } - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_ofs); - if (luv != NULL && (luv->flag & MLOOPUV_PINNED)) { - eattr->v_flag |= VFLAG_VERT_UV_PINNED; - } - if (uvedit_uv_select_test_ex(mr->toolsettings, l, cd_ofs)) { - eattr->v_flag |= VFLAG_VERT_UV_SELECT; - } -} - -static void mesh_render_data_loop_edge_flag(const MeshRenderData *mr, - BMLoop *l, - const int cd_ofs, - EditLoopData *eattr) -{ - if (cd_ofs == -1) { - return; - } - if (uvedit_edge_select_test_ex(mr->toolsettings, l, cd_ofs)) { - eattr->v_flag |= VFLAG_EDGE_UV_SELECT; - eattr->v_flag |= VFLAG_VERT_UV_SELECT; - } -} - -static void mesh_render_data_vert_flag(const MeshRenderData *mr, BMVert *eve, EditLoopData *eattr) -{ - if (eve == mr->eve_act) { - eattr->e_flag |= VFLAG_VERT_ACTIVE; - } - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - eattr->e_flag |= VFLAG_VERT_SELECTED; - } -} - -static void *extract_edit_data_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* WARNING: Adjust #EditLoopData struct accordingly. */ - GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); - GPU_vertformat_alias_add(&format, "flag"); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - return GPU_vertbuf_get_data(vbo); -} - -static void extract_edit_data_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - - EditLoopData *data = (EditLoopData *)_data + l_index; - memset(data, 0x0, sizeof(*data)); - mesh_render_data_face_flag(mr, f, -1, data); - mesh_render_data_edge_flag(mr, l_iter->e, data); - mesh_render_data_vert_flag(mr, l_iter->v, data); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *_data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - EditLoopData *data = (EditLoopData *)_data + ml_index; - memset(data, 0x0, sizeof(*data)); - BMFace *efa = bm_original_face_get(mr, mp_index); - BMEdge *eed = bm_original_edge_get(mr, ml->e); - BMVert *eve = bm_original_vert_get(mr, ml->v); - if (efa) { - mesh_render_data_face_flag(mr, efa, -1, data); - } - if (eed) { - mesh_render_data_edge_flag(mr, eed, data); - } - if (eve) { - mesh_render_data_vert_flag(mr, eve, data); - } - } -} - -static void extract_edit_data_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *_data) -{ - EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2); - memset(data, 0x0, sizeof(*data) * 2); - mesh_render_data_edge_flag(mr, eed, &data[0]); - data[1] = data[0]; - mesh_render_data_vert_flag(mr, eed->v1, &data[0]); - mesh_render_data_vert_flag(mr, eed->v2, &data[1]); -} - -static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *med, - const uint ledge_index, - void *_data) -{ - EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2; - memset(data, 0x0, sizeof(*data) * 2); - const int e_index = mr->ledges[ledge_index]; - BMEdge *eed = bm_original_edge_get(mr, e_index); - BMVert *eve1 = bm_original_vert_get(mr, med->v1); - BMVert *eve2 = bm_original_vert_get(mr, med->v2); - if (eed) { - mesh_render_data_edge_flag(mr, eed, &data[0]); - data[1] = data[0]; - } - if (eve1) { - mesh_render_data_vert_flag(mr, eve1, &data[0]); - } - if (eve2) { - mesh_render_data_vert_flag(mr, eve2, &data[1]); - } -} - -static void extract_edit_data_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, - const int lvert_index, - void *_data) -{ - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; - memset(data, 0x0, sizeof(*data)); - mesh_render_data_vert_flag(mr, eve, data); -} - -static void extract_edit_data_iter_lvert_mesh(const MeshRenderData *mr, - const MVert *UNUSED(mv), - const int lvert_index, - void *_data) -{ - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; - memset(data, 0x0, sizeof(*data)); - const int v_index = mr->lverts[lvert_index]; - BMVert *eve = bm_original_vert_get(mr, v_index); - if (eve) { - mesh_render_data_vert_flag(mr, eve, data); - } -} - -static const MeshExtract extract_edit_data = { - .init = extract_edit_data_init, - .iter_poly_bm = extract_edit_data_iter_poly_bm, - .iter_poly_mesh = extract_edit_data_iter_poly_mesh, - .iter_ledge_bm = extract_edit_data_iter_ledge_bm, - .iter_ledge_mesh = extract_edit_data_iter_ledge_mesh, - .iter_lvert_bm = extract_edit_data_iter_lvert_bm, - .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Data / Flags - * \{ */ - -typedef struct MeshExtract_EditUVData_Data { - EditLoopData *vbo_data; - int cd_ofs; -} MeshExtract_EditUVData_Data; - -static void *extract_edituv_data_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* WARNING: Adjust #EditLoopData struct accordingly. */ - GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); - GPU_vertformat_alias_add(&format, "flag"); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - - MeshExtract_EditUVData_Data *data = MEM_callocN(sizeof(*data), __func__); - data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo); - data->cd_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV); - return data; -} - -static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - MeshExtract_EditUVData_Data *data = _data; - EditLoopData *eldata = &data->vbo_data[l_index]; - memset(eldata, 0x0, sizeof(*eldata)); - mesh_render_data_loop_flag(mr, l_iter, data->cd_ofs, eldata); - mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); - mesh_render_data_loop_edge_flag(mr, l_iter, data->cd_ofs, eldata); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *_data) -{ - MeshExtract_EditUVData_Data *data = _data; - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - EditLoopData *eldata = &data->vbo_data[ml_index]; - memset(eldata, 0x0, sizeof(*eldata)); - BMFace *efa = bm_original_face_get(mr, mp_index); - if (efa) { - BMEdge *eed = bm_original_edge_get(mr, ml->e); - BMVert *eve = bm_original_vert_get(mr, ml->v); - if (eed && eve) { - /* Loop on an edge endpoint. */ - BMLoop *l = BM_face_edge_share_loop(efa, eed); - mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata); - mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata); - } - else { - if (eed == NULL) { - /* Find if the loop's vert is not part of an edit edge. - * For this, we check if the previous loop was on an edge. */ - const int ml_index_last = mp->loopstart + mp->totloop - 1; - const int l_prev = (ml_index == mp->loopstart) ? ml_index_last : (ml_index - 1); - const MLoop *ml_prev = &mr->mloop[l_prev]; - eed = bm_original_edge_get(mr, ml_prev->e); - } - if (eed) { - /* Mapped points on an edge between two edit verts. */ - BMLoop *l = BM_face_edge_share_loop(efa, eed); - mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata); - } - } - } - } -} - -static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_edituv_data = { - .init = extract_edituv_data_init, - .iter_poly_bm = extract_edituv_data_iter_poly_bm, - .iter_poly_mesh = extract_edituv_data_iter_poly_mesh, - .finish = extract_edituv_data_finish, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV area stretch - * \{ */ - -static void *extract_edituv_stretch_area_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "ratio", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - return NULL; -} - -BLI_INLINE float area_ratio_get(float area, float uvarea) -{ - if (area >= FLT_EPSILON && uvarea >= FLT_EPSILON) { - /* Tag inversion by using the sign. */ - return (area > uvarea) ? (uvarea / area) : -(area / uvarea); - } - return 0.0f; -} - -BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_tot_ratio) -{ - ratio *= (ratio > 0.0f) ? tot_ratio : -inv_tot_ratio; - return (ratio > 1.0f) ? (1.0f / ratio) : ratio; -} - -static void extract_edituv_stretch_area_finish(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf, - void *UNUSED(data)) -{ - GPUVertBuf *vbo = buf; - float tot_area = 0.0f, tot_uv_area = 0.0f; - float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - CustomData *cd_ldata = &mr->bm->ldata; - int uv_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV); - - BMFace *efa; - BMIter f_iter; - int f; - BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) { - float area = BM_face_calc_area(efa); - float uvarea = BM_face_calc_area_uv(efa, uv_ofs); - tot_area += area; - tot_uv_area += uvarea; - area_ratio[f] = area_ratio_get(area, uvarea); - } - } - else { - BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); - const MLoopUV *uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); - const MPoly *mp = mr->mpoly; - for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - float area = BKE_mesh_calc_poly_area(mp, &mr->mloop[mp->loopstart], mr->mvert); - float uvarea = BKE_mesh_calc_poly_uv_area(mp, uv_data); - tot_area += area; - tot_uv_area += uvarea; - area_ratio[mp_index] = area_ratio_get(area, uvarea); - } - } - - cache->tot_area = tot_area; - cache->tot_uv_area = tot_uv_area; - - /* Convert in place to avoid an extra allocation */ - uint16_t *poly_stretch = (uint16_t *)area_ratio; - for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { - poly_stretch[mp_index] = area_ratio[mp_index] * SHRT_MAX; - } - - /* Copy face data for each loop. */ - uint16_t *loop_stretch = (uint16_t *)GPU_vertbuf_get_data(vbo); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMFace *efa; - BMIter f_iter; - int f, l_index = 0; - BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) { - for (int i = 0; i < efa->len; i++, l_index++) { - loop_stretch[l_index] = poly_stretch[f]; - } - } - } - else { - BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); - const MPoly *mp = mr->mpoly; - for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - for (int i = 0; i < mp->totloop; i++, l_index++) { - loop_stretch[l_index] = poly_stretch[mp_index]; - } - } - } - - MEM_freeN(area_ratio); -} - -static const MeshExtract extract_edituv_stretch_area = { - .init = extract_edituv_stretch_area_init, - .finish = extract_edituv_stretch_area_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV angle stretch - * \{ */ - -typedef struct UVStretchAngle { - int16_t angle; - int16_t uv_angles[2]; -} UVStretchAngle; - -typedef struct MeshExtract_StretchAngle_Data { - UVStretchAngle *vbo_data; - MLoopUV *luv; - float auv[2][2], last_auv[2]; - float av[2][3], last_av[3]; - int cd_ofs; -} MeshExtract_StretchAngle_Data; - -static void compute_normalize_edge_vectors(float auv[2][2], - float av[2][3], - const float uv[2], - const float uv_prev[2], - const float co[3], - const float co_prev[3]) -{ - /* Move previous edge. */ - copy_v2_v2(auv[0], auv[1]); - copy_v3_v3(av[0], av[1]); - /* 2d edge */ - sub_v2_v2v2(auv[1], uv_prev, uv); - normalize_v2(auv[1]); - /* 3d edge */ - sub_v3_v3v3(av[1], co_prev, co); - normalize_v3(av[1]); -} - -static short v2_to_short_angle(const float v[2]) -{ - return atan2f(v[1], v[0]) * (float)M_1_PI * SHRT_MAX; -} - -static void edituv_get_edituv_stretch_angle(float auv[2][2], - const float av[2][3], - UVStretchAngle *r_stretch) -{ - /* Send UV's to the shader and let it compute the aspect corrected angle. */ - r_stretch->uv_angles[0] = v2_to_short_angle(auv[0]); - r_stretch->uv_angles[1] = v2_to_short_angle(auv[1]); - /* Compute 3D angle here. */ - r_stretch->angle = angle_normalized_v3v3(av[0], av[1]) * (float)M_1_PI * SHRT_MAX; - -#if 0 /* here for reference, this is done in shader now. */ - float uvang = angle_normalized_v2v2(auv0, auv1); - float ang = angle_normalized_v3v3(av0, av1); - float stretch = fabsf(uvang - ang) / (float)M_PI; - return 1.0f - pow2f(1.0f - stretch); -#endif -} - -static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* Waning: adjust #UVStretchAngle struct accordingly. */ - GPU_vertformat_attr_add(&format, "angle", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); - GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - MeshExtract_StretchAngle_Data *data = MEM_callocN(sizeof(*data), __func__); - data->vbo_data = (UVStretchAngle *)GPU_vertbuf_get_data(vbo); - - /* Special iterator needed to save about half of the computing cost. */ - if (mr->extract_type == MR_EXTRACT_BMESH) { - data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); - } - else { - BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); - data->luv = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); - } - return data; -} - -static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - MeshExtract_StretchAngle_Data *data = _data; - float(*auv)[2] = data->auv, *last_auv = data->last_auv; - float(*av)[3] = data->av, *last_av = data->last_av; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - - const MLoopUV *luv, *luv_next; - BMLoop *l_next = l_iter->next; - if (l_iter == BM_FACE_FIRST_LOOP(f)) { - /* First loop in face. */ - BMLoop *l_tmp = l_iter->prev; - BMLoop *l_next_tmp = l_iter; - luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs); - luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs); - compute_normalize_edge_vectors(auv, - av, - luv->uv, - luv_next->uv, - bm_vert_co_get(mr, l_tmp->v), - bm_vert_co_get(mr, l_next_tmp->v)); - /* Save last edge. */ - copy_v2_v2(last_auv, auv[1]); - copy_v3_v3(last_av, av[1]); - } - if (l_next == BM_FACE_FIRST_LOOP(f)) { - /* Move previous edge. */ - copy_v2_v2(auv[0], auv[1]); - copy_v3_v3(av[0], av[1]); - /* Copy already calculated last edge. */ - copy_v2_v2(auv[1], last_auv); - copy_v3_v3(av[1], last_av); - } - else { - luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); - luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs); - compute_normalize_edge_vectors(auv, - av, - luv->uv, - luv_next->uv, - bm_vert_co_get(mr, l_iter->v), - bm_vert_co_get(mr, l_next->v)); - } - edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[l_index]); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *_data) -{ - MeshExtract_StretchAngle_Data *data = _data; - - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - float(*auv)[2] = data->auv, *last_auv = data->last_auv; - float(*av)[3] = data->av, *last_av = data->last_av; - int l_next = ml_index + 1; - const MVert *v, *v_next; - if (ml_index == mp->loopstart) { - /* First loop in face. */ - const int ml_index_last = ml_index_end - 1; - const int l_next_tmp = mp->loopstart; - v = &mr->mvert[mr->mloop[ml_index_last].v]; - v_next = &mr->mvert[mr->mloop[l_next_tmp].v]; - compute_normalize_edge_vectors( - auv, av, data->luv[ml_index_last].uv, data->luv[l_next_tmp].uv, v->co, v_next->co); - /* Save last edge. */ - copy_v2_v2(last_auv, auv[1]); - copy_v3_v3(last_av, av[1]); - } - if (l_next == ml_index_end) { - l_next = mp->loopstart; - /* Move previous edge. */ - copy_v2_v2(auv[0], auv[1]); - copy_v3_v3(av[0], av[1]); - /* Copy already calculated last edge. */ - copy_v2_v2(auv[1], last_auv); - copy_v3_v3(av[1], last_av); - } - else { - v = &mr->mvert[mr->mloop[ml_index].v]; - v_next = &mr->mvert[mr->mloop[l_next].v]; - compute_normalize_edge_vectors( - auv, av, data->luv[ml_index].uv, data->luv[l_next].uv, v->co, v_next->co); - } - edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[ml_index]); - } -} - -static void extract_edituv_stretch_angle_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_edituv_stretch_angle = { - .init = extract_edituv_stretch_angle_init, - .iter_poly_bm = extract_edituv_stretch_angle_iter_poly_bm, - .iter_poly_mesh = extract_edituv_stretch_angle_iter_poly_mesh, - .finish = extract_edituv_stretch_angle_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit Mesh Analysis Colors - * \{ */ - -static void *extract_mesh_analysis_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - return NULL; -} - -static void axis_from_enum_v3(float v[3], const char axis) -{ - zero_v3(v); - if (axis < 3) { - v[axis] = 1.0f; - } - else { - v[axis - 3] = -1.0f; - } -} - -BLI_INLINE float overhang_remap(float fac, float min, float max, float minmax_irange) -{ - if (fac < min) { - fac = 1.0f; - } - else if (fac > max) { - fac = -1.0f; - } - else { - fac = (fac - min) * minmax_irange; - fac = 1.0f - fac; - CLAMP(fac, 0.0f, 1.0f); - } - return fac; -} - -static void statvis_calc_overhang(const MeshRenderData *mr, float *r_overhang) -{ - const MeshStatVis *statvis = &mr->toolsettings->statvis; - const float min = statvis->overhang_min / (float)M_PI; - const float max = statvis->overhang_max / (float)M_PI; - const char axis = statvis->overhang_axis; - BMEditMesh *em = mr->edit_bmesh; - BMIter iter; - BMesh *bm = em->bm; - BMFace *f; - float dir[3]; - const float minmax_irange = 1.0f / (max - min); - - BLI_assert(min <= max); - - axis_from_enum_v3(dir, axis); - - /* now convert into global space */ - mul_transposed_mat3_m4_v3(mr->obmat, dir); - normalize_v3(dir); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - int l_index = 0; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - float fac = angle_normalized_v3v3(bm_face_no_get(mr, f), dir) / (float)M_PI; - fac = overhang_remap(fac, min, max, minmax_irange); - for (int i = 0; i < f->len; i++, l_index++) { - r_overhang[l_index] = fac; - } - } - } - else { - const MPoly *mp = mr->mpoly; - for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - float fac = angle_normalized_v3v3(mr->poly_normals[mp_index], dir) / (float)M_PI; - fac = overhang_remap(fac, min, max, minmax_irange); - for (int i = 0; i < mp->totloop; i++, l_index++) { - r_overhang[l_index] = fac; - } - } - } -} - -/** - * Needed so we can use jitter values for face interpolation. - */ -static void uv_from_jitter_v2(float uv[2]) -{ - uv[0] += 0.5f; - uv[1] += 0.5f; - if (uv[0] + uv[1] > 1.0f) { - uv[0] = 1.0f - uv[0]; - uv[1] = 1.0f - uv[1]; - } - - clamp_v2(uv, 0.0f, 1.0f); -} - -BLI_INLINE float thickness_remap(float fac, float min, float max, float minmax_irange) -{ - /* important not '<=' */ - if (fac < max) { - fac = (fac - min) * minmax_irange; - fac = 1.0f - fac; - CLAMP(fac, 0.0f, 1.0f); - } - else { - fac = -1.0f; - } - return fac; -} - -static void statvis_calc_thickness(const MeshRenderData *mr, float *r_thickness) -{ - const float eps_offset = 0.00002f; /* values <= 0.00001 give errors */ - /* cheating to avoid another allocation */ - float *face_dists = r_thickness + (mr->loop_len - mr->poly_len); - BMEditMesh *em = mr->edit_bmesh; - const float scale = 1.0f / mat4_to_scale(mr->obmat); - const MeshStatVis *statvis = &mr->toolsettings->statvis; - const float min = statvis->thickness_min * scale; - const float max = statvis->thickness_max * scale; - const float minmax_irange = 1.0f / (max - min); - const int samples = statvis->thickness_samples; - float jit_ofs[32][2]; - BLI_assert(samples <= 32); - BLI_assert(min <= max); - - copy_vn_fl(face_dists, mr->poly_len, max); - - BLI_jitter_init(jit_ofs, samples); - for (int j = 0; j < samples; j++) { - uv_from_jitter_v2(jit_ofs[j]); - } - - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMesh *bm = em->bm; - BM_mesh_elem_index_ensure(bm, BM_FACE); - - struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false); - struct BMLoop *(*looptris)[3] = em->looptris; - for (int i = 0; i < mr->tri_len; i++) { - BMLoop **ltri = looptris[i]; - const int index = BM_elem_index_get(ltri[0]->f); - const float *cos[3] = { - bm_vert_co_get(mr, ltri[0]->v), - bm_vert_co_get(mr, ltri[1]->v), - bm_vert_co_get(mr, ltri[2]->v), - }; - float ray_co[3]; - float ray_no[3]; - - normal_tri_v3(ray_no, cos[2], cos[1], cos[0]); - - for (int j = 0; j < samples; j++) { - float dist = face_dists[index]; - interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]); - madd_v3_v3fl(ray_co, ray_no, eps_offset); - - BMFace *f_hit = BKE_bmbvh_ray_cast(bmtree, ray_co, ray_no, 0.0f, &dist, NULL, NULL); - if (f_hit && dist < face_dists[index]) { - float angle_fac = fabsf( - dot_v3v3(bm_face_no_get(mr, ltri[0]->f), bm_face_no_get(mr, f_hit))); - angle_fac = 1.0f - angle_fac; - angle_fac = angle_fac * angle_fac * angle_fac; - angle_fac = 1.0f - angle_fac; - dist /= angle_fac; - if (dist < face_dists[index]) { - face_dists[index] = dist; - } - } - } - } - BKE_bmbvh_free(bmtree); - - BMIter iter; - BMFace *f; - int l_index = 0; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - float fac = face_dists[BM_elem_index_get(f)]; - fac = thickness_remap(fac, min, max, minmax_irange); - for (int i = 0; i < f->len; i++, l_index++) { - r_thickness[l_index] = fac; - } - } - } - else { - BVHTreeFromMesh treeData = {NULL}; - - BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4); - const MLoopTri *mlooptri = mr->mlooptri; - for (int i = 0; i < mr->tri_len; i++, mlooptri++) { - const int index = mlooptri->poly; - const float *cos[3] = {mr->mvert[mr->mloop[mlooptri->tri[0]].v].co, - mr->mvert[mr->mloop[mlooptri->tri[1]].v].co, - mr->mvert[mr->mloop[mlooptri->tri[2]].v].co}; - float ray_co[3]; - float ray_no[3]; - - normal_tri_v3(ray_no, cos[2], cos[1], cos[0]); - - for (int j = 0; j < samples; j++) { - interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]); - madd_v3_v3fl(ray_co, ray_no, eps_offset); - - BVHTreeRayHit hit; - hit.index = -1; - hit.dist = face_dists[index]; - if ((BLI_bvhtree_ray_cast( - tree, ray_co, ray_no, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) && - hit.dist < face_dists[index]) { - float angle_fac = fabsf(dot_v3v3(mr->poly_normals[index], hit.no)); - angle_fac = 1.0f - angle_fac; - angle_fac = angle_fac * angle_fac * angle_fac; - angle_fac = 1.0f - angle_fac; - hit.dist /= angle_fac; - if (hit.dist < face_dists[index]) { - face_dists[index] = hit.dist; - } - } - } - } - - const MPoly *mp = mr->mpoly; - for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - float fac = face_dists[mp_index]; - fac = thickness_remap(fac, min, max, minmax_irange); - for (int i = 0; i < mp->totloop; i++, l_index++) { - r_thickness[l_index] = fac; - } - } - } -} - -struct BVHTree_OverlapData { - const Mesh *me; - const MLoopTri *mlooptri; - float epsilon; -}; - -static bool bvh_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread)) -{ - struct BVHTree_OverlapData *data = userdata; - const Mesh *me = data->me; - - const MLoopTri *tri_a = &data->mlooptri[index_a]; - const MLoopTri *tri_b = &data->mlooptri[index_b]; - - if (UNLIKELY(tri_a->poly == tri_b->poly)) { - return false; - } - - const float *tri_a_co[3] = {me->mvert[me->mloop[tri_a->tri[0]].v].co, - me->mvert[me->mloop[tri_a->tri[1]].v].co, - me->mvert[me->mloop[tri_a->tri[2]].v].co}; - const float *tri_b_co[3] = {me->mvert[me->mloop[tri_b->tri[0]].v].co, - me->mvert[me->mloop[tri_b->tri[1]].v].co, - me->mvert[me->mloop[tri_b->tri[2]].v].co}; - float ix_pair[2][3]; - int verts_shared = 0; - - verts_shared = (ELEM(tri_a_co[0], UNPACK3(tri_b_co)) + ELEM(tri_a_co[1], UNPACK3(tri_b_co)) + - ELEM(tri_a_co[2], UNPACK3(tri_b_co))); - - /* if 2 points are shared, bail out */ - if (verts_shared >= 2) { - return false; - } - - return (isect_tri_tri_v3(UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1]) && - /* if we share a vertex, check the intersection isn't a 'point' */ - ((verts_shared == 0) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) > data->epsilon))); -} - -static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect) -{ - BMEditMesh *em = mr->edit_bmesh; - - for (int l_index = 0; l_index < mr->loop_len; l_index++) { - r_intersect[l_index] = -1.0f; - } - - if (mr->extract_type == MR_EXTRACT_BMESH) { - uint overlap_len; - BMesh *bm = em->bm; - - BM_mesh_elem_index_ensure(bm, BM_FACE); - - struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false); - BVHTreeOverlap *overlap = BKE_bmbvh_overlap_self(bmtree, &overlap_len); - - if (overlap) { - for (int i = 0; i < overlap_len; i++) { - BMFace *f_hit_pair[2] = { - em->looptris[overlap[i].indexA][0]->f, - em->looptris[overlap[i].indexB][0]->f, - }; - for (int j = 0; j < 2; j++) { - BMFace *f_hit = f_hit_pair[j]; - BMLoop *l_first = BM_FACE_FIRST_LOOP(f_hit); - int l_index = BM_elem_index_get(l_first); - for (int k = 0; k < f_hit->len; k++, l_index++) { - r_intersect[l_index] = 1.0f; - } - } - } - MEM_freeN(overlap); - } - - BKE_bmbvh_free(bmtree); - } - else { - uint overlap_len; - BVHTreeFromMesh treeData = {NULL}; - - BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4); - - struct BVHTree_OverlapData data = { - .me = mr->me, .mlooptri = mr->mlooptri, .epsilon = BLI_bvhtree_get_epsilon(tree)}; - - BVHTreeOverlap *overlap = BLI_bvhtree_overlap(tree, tree, &overlap_len, bvh_overlap_cb, &data); - if (overlap) { - for (int i = 0; i < overlap_len; i++) { - const MPoly *f_hit_pair[2] = { - &mr->mpoly[mr->mlooptri[overlap[i].indexA].poly], - &mr->mpoly[mr->mlooptri[overlap[i].indexB].poly], - }; - for (int j = 0; j < 2; j++) { - const MPoly *f_hit = f_hit_pair[j]; - int l_index = f_hit->loopstart; - for (int k = 0; k < f_hit->totloop; k++, l_index++) { - r_intersect[l_index] = 1.0f; - } - } - } - MEM_freeN(overlap); - } - } -} - -BLI_INLINE float distort_remap(float fac, float min, float UNUSED(max), float minmax_irange) -{ - if (fac >= min) { - fac = (fac - min) * minmax_irange; - CLAMP(fac, 0.0f, 1.0f); - } - else { - /* fallback */ - fac = -1.0f; - } - return fac; -} - -static void statvis_calc_distort(const MeshRenderData *mr, float *r_distort) -{ - BMEditMesh *em = mr->edit_bmesh; - const MeshStatVis *statvis = &mr->toolsettings->statvis; - const float min = statvis->distort_min; - const float max = statvis->distort_max; - const float minmax_irange = 1.0f / (max - min); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMIter iter; - BMesh *bm = em->bm; - BMFace *f; - - if (mr->bm_vert_coords != NULL) { - BKE_editmesh_cache_ensure_poly_normals(em, mr->edit_data); - - /* Most likely this is already valid, ensure just in case. - * Needed for #BM_loop_calc_face_normal_safe_vcos. */ - BM_mesh_elem_index_ensure(em->bm, BM_VERT); - } - - int l_index = 0; - int f_index = 0; - BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, f_index) { - float fac = -1.0f; - - if (f->len > 3) { - BMLoop *l_iter, *l_first; - - fac = 0.0f; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const float *no_face; - float no_corner[3]; - if (mr->bm_vert_coords != NULL) { - no_face = mr->bm_poly_normals[f_index]; - BM_loop_calc_face_normal_safe_vcos(l_iter, no_face, mr->bm_vert_coords, no_corner); - } - else { - no_face = f->no; - BM_loop_calc_face_normal_safe(l_iter, no_corner); - } - - /* simple way to detect (what is most likely) concave */ - if (dot_v3v3(no_face, no_corner) < 0.0f) { - negate_v3(no_corner); - } - fac = max_ff(fac, angle_normalized_v3v3(no_face, no_corner)); - - } while ((l_iter = l_iter->next) != l_first); - fac *= 2.0f; - } - - fac = distort_remap(fac, min, max, minmax_irange); - for (int i = 0; i < f->len; i++, l_index++) { - r_distort[l_index] = fac; - } - } - } - else { - const MPoly *mp = mr->mpoly; - for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - float fac = -1.0f; - - if (mp->totloop > 3) { - float *f_no = mr->poly_normals[mp_index]; - fac = 0.0f; - - for (int i = 1; i <= mp->totloop; i++) { - const MLoop *l_prev = &mr->mloop[mp->loopstart + (i - 1) % mp->totloop]; - const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop]; - const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop]; - float no_corner[3]; - normal_tri_v3(no_corner, - mr->mvert[l_prev->v].co, - mr->mvert[l_curr->v].co, - mr->mvert[l_next->v].co); - /* simple way to detect (what is most likely) concave */ - if (dot_v3v3(f_no, no_corner) < 0.0f) { - negate_v3(no_corner); - } - fac = max_ff(fac, angle_normalized_v3v3(f_no, no_corner)); - } - fac *= 2.0f; - } - - fac = distort_remap(fac, min, max, minmax_irange); - for (int i = 0; i < mp->totloop; i++, l_index++) { - r_distort[l_index] = fac; - } - } - } -} - -BLI_INLINE float sharp_remap(float fac, float min, float UNUSED(max), float minmax_irange) -{ - /* important not '>=' */ - if (fac > min) { - fac = (fac - min) * minmax_irange; - CLAMP(fac, 0.0f, 1.0f); - } - else { - /* fallback */ - fac = -1.0f; - } - return fac; -} - -static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp) -{ - BMEditMesh *em = mr->edit_bmesh; - const MeshStatVis *statvis = &mr->toolsettings->statvis; - const float min = statvis->sharp_min; - const float max = statvis->sharp_max; - const float minmax_irange = 1.0f / (max - min); - - /* Can we avoid this extra allocation? */ - float *vert_angles = MEM_mallocN(sizeof(float) * mr->vert_len, __func__); - copy_vn_fl(vert_angles, mr->vert_len, -M_PI); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMIter iter; - BMesh *bm = em->bm; - BMFace *efa; - BMEdge *e; - /* first assign float values to verts */ - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - float angle = BM_edge_calc_face_angle_signed(e); - float *col1 = &vert_angles[BM_elem_index_get(e->v1)]; - float *col2 = &vert_angles[BM_elem_index_get(e->v2)]; - *col1 = max_ff(*col1, angle); - *col2 = max_ff(*col2, angle); - } - /* Copy vert value to loops. */ - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - int l_index = BM_elem_index_get(l_iter); - int v_index = BM_elem_index_get(l_iter->v); - r_sharp[l_index] = sharp_remap(vert_angles[v_index], min, max, minmax_irange); - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - /* first assign float values to verts */ - const MPoly *mp = mr->mpoly; - - EdgeHash *eh = BLI_edgehash_new_ex(__func__, mr->edge_len); - - for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - for (int i = 0; i < mp->totloop; i++) { - const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop]; - const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop]; - const MVert *v_curr = &mr->mvert[l_curr->v]; - const MVert *v_next = &mr->mvert[l_next->v]; - float angle; - void **pval; - bool value_is_init = BLI_edgehash_ensure_p(eh, l_curr->v, l_next->v, &pval); - if (!value_is_init) { - *pval = mr->poly_normals[mp_index]; - /* non-manifold edge, yet... */ - continue; - } - if (*pval != NULL) { - const float *f1_no = mr->poly_normals[mp_index]; - const float *f2_no = *pval; - angle = angle_normalized_v3v3(f1_no, f2_no); - angle = is_edge_convex_v3(v_curr->co, v_next->co, f1_no, f2_no) ? angle : -angle; - /* Tag as manifold. */ - *pval = NULL; - } - else { - /* non-manifold edge */ - angle = DEG2RADF(90.0f); - } - float *col1 = &vert_angles[l_curr->v]; - float *col2 = &vert_angles[l_next->v]; - *col1 = max_ff(*col1, angle); - *col2 = max_ff(*col2, angle); - } - } - /* Remaining non manifold edges. */ - EdgeHashIterator *ehi = BLI_edgehashIterator_new(eh); - for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { - if (BLI_edgehashIterator_getValue(ehi) != NULL) { - uint v1, v2; - const float angle = DEG2RADF(90.0f); - BLI_edgehashIterator_getKey(ehi, &v1, &v2); - float *col1 = &vert_angles[v1]; - float *col2 = &vert_angles[v2]; - *col1 = max_ff(*col1, angle); - *col2 = max_ff(*col2, angle); - } - } - BLI_edgehashIterator_free(ehi); - BLI_edgehash_free(eh, NULL); - - const MLoop *ml = mr->mloop; - for (int l_index = 0; l_index < mr->loop_len; l_index++, ml++) { - r_sharp[l_index] = sharp_remap(vert_angles[ml->v], min, max, minmax_irange); - } - } - - MEM_freeN(vert_angles); -} - -static void extract_analysis_iter_finish_mesh(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *UNUSED(data)) -{ - GPUVertBuf *vbo = buf; - BLI_assert(mr->edit_bmesh); - - float *l_weight = (float *)GPU_vertbuf_get_data(vbo); - - switch (mr->toolsettings->statvis.type) { - case SCE_STATVIS_OVERHANG: - statvis_calc_overhang(mr, l_weight); - break; - case SCE_STATVIS_THICKNESS: - statvis_calc_thickness(mr, l_weight); - break; - case SCE_STATVIS_INTERSECT: - statvis_calc_intersect(mr, l_weight); - break; - case SCE_STATVIS_DISTORT: - statvis_calc_distort(mr, l_weight); - break; - case SCE_STATVIS_SHARP: - statvis_calc_sharp(mr, l_weight); - break; - } -} - -static const MeshExtract extract_mesh_analysis = { - .init = extract_mesh_analysis_init, - .finish = extract_analysis_iter_finish_mesh, - /* This is not needed for all visualization types. - * * Maybe split into different extract. */ - .data_flag = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Facedots positions - * \{ */ - -static void *extract_fdots_pos_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->poly_len); - return GPU_vertbuf_get_data(vbo); -} - -static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int f_index, - void *data) -{ - float(*center)[3] = data; - - float *co = center[f_index]; - zero_v3(co); - - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - add_v3_v3(co, bm_vert_co_get(mr, l_iter->v)); - } while ((l_iter = l_iter->next) != l_first); - mul_v3_fl(co, 1.0f / (float)f->len); -} - -static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *data) -{ - float(*center)[3] = (float(*)[3])data; - float *co = center[mp_index]; - zero_v3(co); - - const MVert *mvert = mr->mvert; - const MLoop *mloop = mr->mloop; - - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - if (mr->use_subsurf_fdots) { - const MVert *mv = &mr->mvert[ml->v]; - if (mv->flag & ME_VERT_FACEDOT) { - copy_v3_v3(center[mp_index], mv->co); - break; - } - } - else { - const MVert *mv = &mvert[ml->v]; - add_v3_v3(center[mp_index], mv->co); - } - } - - if (!mr->use_subsurf_fdots) { - mul_v3_fl(co, 1.0f / (float)mp->totloop); - } -} - -static const MeshExtract extract_fdots_pos = { - .init = extract_fdots_pos_init, - .iter_poly_bm = extract_fdots_pos_iter_poly_bm, - .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos)}; - -/** \} */ +#include "atomic_ops.h" -/* ---------------------------------------------------------------------- */ -/** \name Extract Facedots Normal and edit flag - * \{ */ -#define NOR_AND_FLAG_DEFAULT 0 -#define NOR_AND_FLAG_SELECT 1 -#define NOR_AND_FLAG_ACTIVE -1 -#define NOR_AND_FLAG_HIDDEN -2 +#include "DNA_mesh_types.h" +#include "DNA_scene_types.h" -static void *extract_fdots_nor_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - } +#include "BLI_task.h" - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->poly_len); +#include "BKE_editmesh.h" - return NULL; -} +#include "GPU_capabilities.h" -static void extract_fdots_nor_finish(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *UNUSED(data)) -{ - GPUVertBuf *vbo = buf; - static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; - GPUPackedNormal *nor = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo); - BMFace *efa; +#include "draw_cache_extract.h" +#include "draw_cache_extract_mesh_private.h" +#include "draw_cache_inline.h" - /* Quicker than doing it for each loop. */ - if (mr->extract_type == MR_EXTRACT_BMESH) { - for (int f = 0; f < mr->poly_len; f++) { - efa = BM_face_at_index(mr->bm, f); - const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN); - if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[f] == ORIGINDEX_NONE)) { - nor[f] = GPU_normal_convert_i10_v3(invalid_normal); - nor[f].w = NOR_AND_FLAG_HIDDEN; - } - else { - nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa)); - /* Select / Active Flag. */ - nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? - ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : - NOR_AND_FLAG_DEFAULT); - } - } - } - else { - for (int f = 0; f < mr->poly_len; f++) { - efa = bm_original_face_get(mr, f); - const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN); - if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[f] == ORIGINDEX_NONE)) { - nor[f] = GPU_normal_convert_i10_v3(invalid_normal); - nor[f].w = NOR_AND_FLAG_HIDDEN; - } - else { - nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa)); - /* Select / Active Flag. */ - nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? - ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : - NOR_AND_FLAG_DEFAULT); - } - } - } -} +// #define DEBUG_TIME -static const MeshExtract extract_fdots_nor = { - .init = extract_fdots_nor_init, - .finish = extract_fdots_nor_finish, - .data_flag = MR_DATA_POLY_NOR, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; +#ifdef DEBUG_TIME +# include "PIL_time_utildefines.h" +#endif -/** \} */ +#define CHUNK_SIZE 8192 /* ---------------------------------------------------------------------- */ -/** \name Extract Facedots High Quality Normal and edit flag +/** \name Mesh Elements Extract Struct * \{ */ -static void *extract_fdots_nor_hq_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->poly_len); +typedef struct MeshExtractRunData { + const MeshExtract *extractor; + void *buffer; + void *user_data; +} MeshExtractRunData; - return NULL; -} +typedef struct MeshExtractRunDataArray { + int len; + MeshExtractRunData items[M_EXTRACT_LEN]; +} MeshExtractRunDataArray; -static void extract_fdots_nor_hq_finish(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *UNUSED(data)) +static void mesh_extract_run_data_array_init(MeshExtractRunDataArray *array) { - GPUVertBuf *vbo = buf; - static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; - short *nor = (short *)GPU_vertbuf_get_data(vbo); - BMFace *efa; - - /* Quicker than doing it for each loop. */ - if (mr->extract_type == MR_EXTRACT_BMESH) { - for (int f = 0; f < mr->poly_len; f++) { - efa = BM_face_at_index(mr->bm, f); - const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN); - if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[f] == ORIGINDEX_NONE)) { - normal_float_to_short_v3(&nor[f * 4], invalid_normal); - nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN; - } - else { - normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa)); - /* Select / Active Flag. */ - nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? - ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : - NOR_AND_FLAG_DEFAULT); - } - } - } - else { - for (int f = 0; f < mr->poly_len; f++) { - efa = bm_original_face_get(mr, f); - const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN); - if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[f] == ORIGINDEX_NONE)) { - normal_float_to_short_v3(&nor[f * 4], invalid_normal); - nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN; - } - else { - normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa)); - /* Select / Active Flag. */ - nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? - ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : - NOR_AND_FLAG_DEFAULT); - } - } - } + array->len = 0; } -static const MeshExtract extract_fdots_nor_hq = { - .init = extract_fdots_nor_hq_init, - .finish = extract_fdots_nor_hq_finish, - .data_flag = MR_DATA_POLY_NOR, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Facedots UV - * \{ */ - -typedef struct MeshExtract_FdotUV_Data { - float (*vbo_data)[2]; - MLoopUV *uv_data; - int cd_ofs; -} MeshExtract_FdotUV_Data; - -static void *extract_fdots_uv_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void mesh_extract_run_data_array_add_ex(MeshExtractRunDataArray *array, + const MeshExtractRunData *run_data) { - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - GPU_vertformat_alias_add(&format, "au"); - GPU_vertformat_alias_add(&format, "pos"); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->poly_len); - - if (!mr->use_subsurf_fdots) { - /* Clear so we can accumulate on it. */ - memset(GPU_vertbuf_get_data(vbo), 0x0, mr->poly_len * GPU_vertbuf_get_format(vbo)->stride); - } - - MeshExtract_FdotUV_Data *data = MEM_callocN(sizeof(*data), __func__); - data->vbo_data = (float(*)[2])GPU_vertbuf_get_data(vbo); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); - } - else { - data->uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); - } - return data; + array->items[array->len] = *run_data; + array->len++; } -static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *_data) +static void mesh_extract_run_data_array_add(MeshExtractRunDataArray *array, + const MeshExtract *extractor) { - MeshExtract_FdotUV_Data *data = _data; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - float w = 1.0f / (float)f->len; - const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); - madd_v2_v2fl(data->vbo_data[BM_elem_index_get(f)], luv->uv, w); - } while ((l_iter = l_iter->next) != l_first); + MeshExtractRunData run_data; + run_data.extractor = extractor; + run_data.buffer = NULL; + run_data.user_data = NULL; + mesh_extract_run_data_array_add_ex(array, &run_data); } -static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *_data) +static void mesh_extract_run_data_array_filter_iter_type(const MeshExtractRunDataArray *src, + MeshExtractRunDataArray *dst, + eMRIterType iter_type) { - MeshExtract_FdotUV_Data *data = _data; - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - if (mr->use_subsurf_fdots) { - const MVert *mv = &mr->mvert[ml->v]; - if (mv->flag & ME_VERT_FACEDOT) { - copy_v2_v2(data->vbo_data[mp_index], data->uv_data[ml_index].uv); - } + for (int i = 0; i < src->len; i++) { + + const MeshExtractRunData *data = &src->items[i]; + const MeshExtract *extractor = data->extractor; + if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { + BLI_assert(extractor->iter_looptri_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; } - else { - float w = 1.0f / (float)mp->totloop; - madd_v2_v2fl(data->vbo_data[mp_index], data->uv_data[ml_index].uv, w); + if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { + BLI_assert(extractor->iter_poly_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; + } + if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { + BLI_assert(extractor->iter_ledge_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; + } + if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { + BLI_assert(extractor->iter_lvert_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; } } } -static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_fdots_uv = { - .init = extract_fdots_uv_init, - .iter_poly_bm = extract_fdots_uv_iter_poly_bm, - .iter_poly_mesh = extract_fdots_uv_iter_poly_mesh, - .finish = extract_fdots_uv_finish, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Facedots Edit UV flag - * \{ */ - -typedef struct MeshExtract_EditUVFdotData_Data { - EditLoopData *vbo_data; - int cd_ofs; -} MeshExtract_EditUVFdotData_Data; - -static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "flag", GPU_COMP_U8, 4, GPU_FETCH_INT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->poly_len); - - MeshExtract_EditUVFdotData_Data *data = MEM_callocN(sizeof(*data), __func__); - data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo); - data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); - return data; -} - -static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - MeshExtract_EditUVFdotData_Data *data = _data; - EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)]; - memset(eldata, 0x0, sizeof(*eldata)); - mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); -} - -static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *UNUSED(mp), - const int mp_index, - void *_data) -{ - MeshExtract_EditUVFdotData_Data *data = _data; - EditLoopData *eldata = &data->vbo_data[mp_index]; - memset(eldata, 0x0, sizeof(*eldata)); - BMFace *efa = bm_original_face_get(mr, mp_index); - if (efa) { - mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata); - } -} - -static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_fdots_edituv_data = { - .init = extract_fdots_edituv_data_init, - .iter_poly_bm = extract_fdots_edituv_data_iter_poly_bm, - .iter_poly_mesh = extract_fdots_edituv_data_iter_poly_mesh, - .finish = extract_fdots_edituv_data_finish, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Skin Modifier Roots - * \{ */ - -typedef struct SkinRootData { - float size; - float local_pos[3]; -} SkinRootData; - -static void *extract_skin_roots_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void mesh_extract_run_data_array_filter_threading( + const MeshExtractRunDataArray *src, MeshExtractRunDataArray *dst_multi_threaded) { - GPUVertBuf *vbo = buf; - /* Exclusively for edit mode. */ - BLI_assert(mr->bm); - - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - GPU_vertformat_attr_add(&format, "local_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->bm->totvert); - - SkinRootData *vbo_data = (SkinRootData *)GPU_vertbuf_get_data(vbo); - - int root_len = 0; - int cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MVERT_SKIN); - - BMIter iter; - BMVert *eve; - BM_ITER_MESH (eve, &iter, mr->bm, BM_VERTS_OF_MESH) { - const MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_ofs); - if (vs->flag & MVERT_SKIN_ROOT) { - vbo_data->size = (vs->radius[0] + vs->radius[1]) * 0.5f; - copy_v3_v3(vbo_data->local_pos, bm_vert_co_get(mr, eve)); - vbo_data++; - root_len++; + for (int i = 0; i < src->len; i++) { + const MeshExtract *extractor = src->items[i].extractor; + if (extractor->use_threading) { + mesh_extract_run_data_array_add(dst_multi_threaded, extractor); } } - - /* It's really unlikely that all verts will be roots. Resize to avoid losing VRAM. */ - GPU_vertbuf_data_len_set(vbo, root_len); - - return NULL; } -static const MeshExtract extract_skin_roots = { - .init = extract_skin_roots_init, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots)}; - /** \} */ -/* ---------------------------------------------------------------------- */ -/** \name Extract Selection Index - * \{ */ - -static void *extract_select_idx_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* TODO rename "color" to something more descriptive. */ - GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - return GPU_vertbuf_get_data(vbo); -} - -/* TODO Use #glVertexID to get loop index and use the data structure on the CPU to retrieve the - * select element associated with this loop ID. This would remove the need for this separate - * index VBO's. We could upload the p/e/v_origindex as a buffer texture and sample it inside the - * shader to output original index. */ - -static void extract_poly_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int f_index, - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - ((uint32_t *)data)[l_index] = f_index; - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edge_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->e); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_vert_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->v); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edge_idx_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *data) -{ - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed); - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed); -} - -static void extract_vert_idx_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *data) -{ - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1); - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2); -} - -static void extract_vert_idx_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, - const int lvert_index, - void *data) -{ - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve); -} - -static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *data) -{ - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - ((uint32_t *)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index; - } -} - -static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - ((uint32_t *)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e; - } -} - -static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - ((uint32_t *)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v; - } -} - -static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *UNUSED(med), - const uint ledge_index, - void *data) -{ - const int e_index = mr->ledges[ledge_index]; - const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig; -} - -static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *med, - const uint ledge_index, - void *data) -{ - int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1; - int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig; -} - -static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr, - const MVert *UNUSED(mv), - const int lvert_index, - void *data) -{ - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - const int v_index = mr->lverts[lvert_index]; - const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index; - ((uint32_t *)data)[offset + lvert_index] = v_orig; -} - -static const MeshExtract extract_poly_idx = { - .init = extract_select_idx_init, - .iter_poly_bm = extract_poly_idx_iter_poly_bm, - .iter_poly_mesh = extract_poly_idx_iter_poly_mesh, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx)}; - -static const MeshExtract extract_edge_idx = { - .init = extract_select_idx_init, - .iter_poly_bm = extract_edge_idx_iter_poly_bm, - .iter_poly_mesh = extract_edge_idx_iter_poly_mesh, - .iter_ledge_bm = extract_edge_idx_iter_ledge_bm, - .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx)}; - -static const MeshExtract extract_vert_idx = { - .init = extract_select_idx_init, - .iter_poly_bm = extract_vert_idx_iter_poly_bm, - .iter_poly_mesh = extract_vert_idx_iter_poly_mesh, - .iter_ledge_bm = extract_vert_idx_iter_ledge_bm, - .iter_ledge_mesh = extract_vert_idx_iter_ledge_mesh, - .iter_lvert_bm = extract_vert_idx_iter_lvert_bm, - .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx)}; - -static void *extract_fdot_idx_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* TODO rename "color" to something more descriptive. */ - GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->poly_len); - return GPU_vertbuf_get_data(vbo); -} - -static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *UNUSED(f), - const int f_index, - void *data) -{ - ((uint32_t *)data)[f_index] = f_index; -} - -static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *UNUSED(mp), - const int mp_index, - void *data) -{ - if (mr->p_origindex != NULL) { - ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index]; - } - else { - ((uint32_t *)data)[mp_index] = mp_index; - } -} - -static const MeshExtract extract_fdot_idx = { - .init = extract_fdot_idx_init, - .iter_poly_bm = extract_fdot_idx_iter_poly_bm, - .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)}; - /* ---------------------------------------------------------------------- */ /** \name Extract * \{ */ @@ -6088,52 +664,6 @@ static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_g /** \} */ -/* ---------------------------------------------------------------------- */ -/** \name Override extractors - * Extractors can be overridden. When overridden a specialized version is used. The next functions - * would check for any needed overrides and usage of the specialized version. - * \{ */ - -static const MeshExtract *mesh_extract_override_hq_normals(const MeshExtract *extractor) -{ - if (extractor == &extract_pos_nor) { - return &extract_pos_nor_hq; - } - if (extractor == &extract_lnor) { - return &extract_lnor_hq; - } - if (extractor == &extract_tan) { - return &extract_tan_hq; - } - if (extractor == &extract_fdots_nor) { - return &extract_fdots_nor_hq; - } - return extractor; -} - -static const MeshExtract *mesh_extract_override_loose_lines(const MeshExtract *extractor) -{ - if (extractor == &extract_lines) { - return &extract_lines_with_lines_loose; - } - return extractor; -} - -static const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, - const bool do_hq_normals, - const bool do_lines_loose_subbuffer) -{ - if (do_hq_normals) { - extractor = mesh_extract_override_hq_normals(extractor); - } - if (do_lines_loose_subbuffer) { - extractor = mesh_extract_override_loose_lines(extractor); - } - return extractor; -} - -/** \} */ - /* ---------------------------------------------------------------------- */ /** \name Extract Loop * \{ */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c new file mode 100644 index 00000000000..cc787b5a40f --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c @@ -0,0 +1,4748 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ + +#include "MEM_guardedalloc.h" + +#include "atomic_ops.h" + +#include "DNA_object_types.h" + +#include "BLI_edgehash.h" +#include "BLI_jitter_2d.h" +#include "BLI_kdopbvh.h" +#include "BLI_string.h" + +#include "BKE_bvhutils.h" +#include "BKE_deform.h" +#include "BKE_editmesh.h" +#include "BKE_editmesh_bvh.h" +#include "BKE_editmesh_cache.h" +#include "BKE_editmesh_tangent.h" +#include "BKE_mesh.h" +#include "BKE_mesh_tangent.h" +#include "BKE_paint.h" + +#include "ED_uvedit.h" + +#include "GPU_capabilities.h" + +#include "draw_cache_extract_mesh_private.h" +#include "draw_cache_impl.h" + +void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc) +{ + /* NOTE: POINTER_OFFSET on windows platforms casts internally to `void *`, but on GCC/CLANG to + * `MeshBufferCache *`. What shows a different usage versus intent. */ + void **buffer_ptr = (void **)POINTER_OFFSET(mbc, extractor->mesh_buffer_offset); + void *buffer = *buffer_ptr; + BLI_assert(buffer); + return buffer; +} + +eMRIterType mesh_extract_iter_type(const MeshExtract *ext) +{ + eMRIterType type = 0; + SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri_mesh), MR_ITER_LOOPTRI); + SET_FLAG_FROM_TEST(type, (ext->iter_poly_bm || ext->iter_poly_mesh), MR_ITER_POLY); + SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge_mesh), MR_ITER_LEDGE); + SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert_mesh), MR_ITER_LVERT); + return type; +} + +/* ---------------------------------------------------------------------- */ +/** \name Override extractors + * Extractors can be overridden. When overridden a specialized version is used. The next functions + * would check for any needed overrides and usage of the specialized version. + * \{ */ + +static const MeshExtract *mesh_extract_override_hq_normals(const MeshExtract *extractor) +{ + if (extractor == &extract_pos_nor) { + return &extract_pos_nor_hq; + } + if (extractor == &extract_lnor) { + return &extract_lnor_hq; + } + if (extractor == &extract_tan) { + return &extract_tan_hq; + } + if (extractor == &extract_fdots_nor) { + return &extract_fdots_nor_hq; + } + return extractor; +} + +static const MeshExtract *mesh_extract_override_loose_lines(const MeshExtract *extractor) +{ + if (extractor == &extract_lines) { + return &extract_lines_with_lines_loose; + } + return extractor; +} + +const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, + const bool do_hq_normals, + const bool do_lines_loose_subbuffer) +{ + if (do_hq_normals) { + extractor = mesh_extract_override_hq_normals(extractor); + } + if (do_lines_loose_subbuffer) { + extractor = mesh_extract_override_loose_lines(extractor); + } + return extractor; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Triangles Indices + * \{ */ + +typedef struct MeshExtract_Tri_Data { + GPUIndexBufBuilder elb; + int *tri_mat_start; + int *tri_mat_end; +} MeshExtract_Tri_Data; + +static void *extract_tris_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + MeshExtract_Tri_Data *data = MEM_callocN(sizeof(*data), __func__); + + size_t mat_tri_idx_size = sizeof(int) * mr->mat_len; + data->tri_mat_start = MEM_callocN(mat_tri_idx_size, __func__); + data->tri_mat_end = MEM_callocN(mat_tri_idx_size, __func__); + + int *mat_tri_len = data->tri_mat_start; + /* Count how many triangle for each material. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMIter iter; + BMFace *efa; + BM_ITER_MESH (efa, &iter, mr->bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + int mat = min_ii(efa->mat_nr, mr->mat_len - 1); + mat_tri_len[mat] += efa->len - 2; + } + } + } + else { + const MPoly *mp = mr->mpoly; + for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + int mat = min_ii(mp->mat_nr, mr->mat_len - 1); + mat_tri_len[mat] += mp->totloop - 2; + } + } + } + /* Accumulate triangle lengths per material to have correct offsets. */ + int ofs = mat_tri_len[0]; + mat_tri_len[0] = 0; + for (int i = 1; i < mr->mat_len; i++) { + int tmp = mat_tri_len[i]; + mat_tri_len[i] = ofs; + ofs += tmp; + } + + memcpy(data->tri_mat_end, mat_tri_len, mat_tri_idx_size); + + int visible_tri_tot = ofs; + GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, visible_tri_tot, mr->loop_len); + + return data; +} + +static void extract_tris_iter_looptri_bm(const MeshRenderData *mr, + BMLoop **elt, + const int UNUSED(elt_index), + void *_data) +{ + MeshExtract_Tri_Data *data = _data; + const int mat_last = mr->mat_len - 1; + + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + int *mat_tri_ofs = data->tri_mat_end; + const int mat = min_ii(elt[0]->f->mat_nr, mat_last); + GPU_indexbuf_set_tri_verts(&data->elb, + mat_tri_ofs[mat]++, + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); + } +} + +static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr, + const MLoopTri *mlt, + const int UNUSED(elt_index), + void *_data) +{ + MeshExtract_Tri_Data *data = _data; + const int mat_last = mr->mat_len - 1; + const MPoly *mp = &mr->mpoly[mlt->poly]; + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + int *mat_tri_ofs = data->tri_mat_end; + const int mat = min_ii(mp->mat_nr, mat_last); + GPU_indexbuf_set_tri_verts( + &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]); + } +} + +static void extract_tris_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *_data) +{ + GPUIndexBuf *ibo = buf; + MeshExtract_Tri_Data *data = _data; + GPU_indexbuf_build_in_place(&data->elb, ibo); + + /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch + * is created before the surfaces-per-material. */ + if (mr->use_final_mesh && cache->final.tris_per_mat) { + MeshBufferCache *mbc_final = &cache->final; + for (int i = 0; i < mr->mat_len; i++) { + /* These IBOs have not been queried yet but we create them just in case they are needed + * later since they are not tracked by mesh_buffer_cache_create_requested(). */ + if (mbc_final->tris_per_mat[i] == NULL) { + mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc(); + } + /* Multiply by 3 because these are triangle indices. */ + const int mat_start = data->tri_mat_start[i]; + const int mat_end = data->tri_mat_end[i]; + const int start = mat_start * 3; + const int len = (mat_end - mat_start) * 3; + GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len); + } + } + MEM_freeN(data->tri_mat_start); + MEM_freeN(data->tri_mat_end); + MEM_freeN(data); +} + +const MeshExtract extract_tris = {.init = extract_tris_init, + .iter_looptri_bm = extract_tris_iter_looptri_bm, + .iter_looptri_mesh = extract_tris_iter_looptri_mesh, + .finish = extract_tris_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edges Indices + * \{ */ + +static void *extract_lines_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) +{ + GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); + /* Put loose edges at the end. */ + GPU_indexbuf_init( + elb, GPU_PRIM_LINES, mr->edge_len + mr->edge_loose_len, mr->loop_len + mr->loop_loose_len); + return elb; +} + +static void extract_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *elb) +{ + BMLoop *l_iter, *l_first; + /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */ + l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev; + do { + if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_line_verts(elb, + BM_elem_index_get(l_iter->e), + BM_elem_index_get(l_iter), + BM_elem_index_get(l_iter->next)); + } + else { + GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e)); + } + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_lines_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *elb) +{ + /* Using poly & loop iterator would complicate accessing the adjacent loop. */ + const MLoop *mloop = mr->mloop; + const MEdge *medge = mr->medge; + if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != NULL)) { + const int ml_index_last = mp->loopstart + (mp->totloop - 1); + int ml_index = ml_index_last, ml_index_next = mp->loopstart; + do { + const MLoop *ml = &mloop[ml_index]; + const MEdge *med = &medge[ml->e]; + if (!((mr->use_hide && (med->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) { + GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); + } + else { + GPU_indexbuf_set_line_restart(elb, ml->e); + } + } while ((ml_index = ml_index_next++) != ml_index_last); + } + else { + const int ml_index_last = mp->loopstart + (mp->totloop - 1); + int ml_index = ml_index_last, ml_index_next = mp->loopstart; + do { + const MLoop *ml = &mloop[ml_index]; + GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); + } while ((ml_index = ml_index_next++) != ml_index_last); + } +} + +static void extract_lines_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *elb) +{ + const int l_index_offset = mr->edge_len + ledge_index; + if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { + const int l_index = mr->loop_len + ledge_index * 2; + GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); + } + else { + GPU_indexbuf_set_line_restart(elb, l_index_offset); + } + /* Don't render the edge twice. */ + GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed)); +} + +static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *elb) +{ + const int l_index_offset = mr->edge_len + ledge_index; + const int e_index = mr->ledges[ledge_index]; + if (!((mr->use_hide && (med->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { + const int l_index = mr->loop_len + ledge_index * 2; + GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); + } + else { + GPU_indexbuf_set_line_restart(elb, l_index_offset); + } + /* Don't render the edge twice. */ + GPU_indexbuf_set_line_restart(elb, e_index); +} + +static void extract_lines_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *elb) +{ + GPUIndexBuf *ibo = buf; + GPU_indexbuf_build_in_place(elb, ibo); + MEM_freeN(elb); +} + +const MeshExtract extract_lines = {.init = extract_lines_init, + .iter_poly_bm = extract_lines_iter_poly_bm, + .iter_poly_mesh = extract_lines_iter_poly_mesh, + .iter_ledge_bm = extract_lines_iter_ledge_bm, + .iter_ledge_mesh = extract_lines_iter_ledge_mesh, + .finish = extract_lines_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Loose Edges Sub Buffer + * \{ */ + +static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache) +{ + BLI_assert(cache->final.ibo.lines); + /* Multiply by 2 because these are edges indices. */ + const int start = mr->edge_len * 2; + const int len = mr->edge_loose_len * 2; + GPU_indexbuf_create_subrange_in_place( + cache->final.ibo.lines_loose, cache->final.ibo.lines, start, len); + cache->no_loose_wire = (len == 0); +} + +static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *elb) +{ + GPUIndexBuf *ibo = buf; + GPU_indexbuf_build_in_place(elb, ibo); + extract_lines_loose_subbuffer(mr, cache); + MEM_freeN(elb); +} + +const MeshExtract extract_lines_with_lines_loose = { + .init = extract_lines_init, + .iter_poly_bm = extract_lines_iter_poly_bm, + .iter_poly_mesh = extract_lines_iter_poly_mesh, + .iter_ledge_bm = extract_lines_iter_ledge_bm, + .iter_ledge_mesh = extract_lines_iter_ledge_mesh, + .finish = extract_lines_with_lines_loose_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Point Indices + * \{ */ + +static void *extract_points_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) +{ + GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); + GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->vert_len, mr->loop_len + mr->loop_loose_len); + return elb; +} + +BLI_INLINE void vert_set_bm(GPUIndexBufBuilder *elb, BMVert *eve, int l_index) +{ + const int v_index = BM_elem_index_get(eve); + if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_point_vert(elb, v_index, l_index); + } + else { + GPU_indexbuf_set_point_restart(elb, v_index); + } +} + +BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb, + const MeshRenderData *mr, + const int v_index, + const int l_index) +{ + const MVert *mv = &mr->mvert[v_index]; + if (!((mr->use_hide && (mv->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && + (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) { + GPU_indexbuf_set_point_vert(elb, v_index, l_index); + } + else { + GPU_indexbuf_set_point_restart(elb, v_index); + } +} + +static void extract_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *elb) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + vert_set_bm(elb, l_iter->v, l_index); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_points_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *elb) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + vert_set_mesh(elb, mr, ml->v, ml_index); + } +} + +static void extract_points_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *elb) +{ + vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2)); + vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1); +} + +static void extract_points_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *elb) +{ + vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2)); + vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1); +} + +static void extract_points_iter_lvert_bm(const MeshRenderData *mr, + BMVert *eve, + const int lvert_index, + void *elb) +{ + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + vert_set_bm(elb, eve, offset + lvert_index); +} + +static void extract_points_iter_lvert_mesh(const MeshRenderData *mr, + const MVert *UNUSED(mv), + const int lvert_index, + void *elb) +{ + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index); +} + +static void extract_points_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *elb) +{ + GPUIndexBuf *ibo = buf; + GPU_indexbuf_build_in_place(elb, ibo); + MEM_freeN(elb); +} + +const MeshExtract extract_points = {.init = extract_points_init, + .iter_poly_bm = extract_points_iter_poly_bm, + .iter_poly_mesh = extract_points_iter_poly_mesh, + .iter_ledge_bm = extract_points_iter_ledge_bm, + .iter_ledge_mesh = extract_points_iter_ledge_mesh, + .iter_lvert_bm = extract_points_iter_lvert_bm, + .iter_lvert_mesh = extract_points_iter_lvert_mesh, + .finish = extract_points_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots Indices + * \{ */ + +static void *extract_fdots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) +{ + GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); + GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); + return elb; +} + +static void extract_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, + void *elb) +{ + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_point_vert(elb, f_index, f_index); + } + else { + GPU_indexbuf_set_point_restart(elb, f_index); + } +} + +static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *elb) +{ + if (mr->use_subsurf_fdots) { + /* Check #ME_VERT_FACEDOT. */ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const MVert *mv = &mr->mvert[ml->v]; + if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) { + GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); + return; + } + } + GPU_indexbuf_set_point_restart(elb, mp_index); + } + else { + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); + } + else { + GPU_indexbuf_set_point_restart(elb, mp_index); + } + } +} + +static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *elb) +{ + GPUIndexBuf *ibo = buf; + GPU_indexbuf_build_in_place(elb, ibo); + MEM_freeN(elb); +} + +const MeshExtract extract_fdots = {.init = extract_fdots_init, + .iter_poly_bm = extract_fdots_iter_poly_bm, + .iter_poly_mesh = extract_fdots_iter_poly_mesh, + .finish = extract_fdots_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Paint Mask Line Indices + * \{ */ + +typedef struct MeshExtract_LinePaintMask_Data { + GPUIndexBufBuilder elb; + /** One bit per edge set if face is selected. */ + BLI_bitmap select_map[0]; +} MeshExtract_LinePaintMask_Data; + +static void *extract_lines_paint_mask_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len); + MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->edge_len, mr->loop_len); + return data; +} + +static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *_data) +{ + MeshExtract_LinePaintMask_Data *data = _data; + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + const int e_index = ml->e; + const MEdge *me = &mr->medge[e_index]; + if (!((mr->use_hide && (me->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { + + const int ml_index_last = mp->totloop + mp->loopstart - 1; + const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); + if (mp->flag & ME_FACE_SEL) { + if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, e_index)) { + /* Hide edge as it has more than 2 selected loop. */ + GPU_indexbuf_set_line_restart(&data->elb, e_index); + } + else { + /* First selected loop. Set edge visible, overwriting any unselected loop. */ + GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other); + } + } + else { + /* Set these unselected loop only if this edge has no other selected loop. */ + if (!BLI_BITMAP_TEST(data->select_map, e_index)) { + GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other); + } + } + } + else { + GPU_indexbuf_set_line_restart(&data->elb, e_index); + } + } +} + +static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *_data) +{ + GPUIndexBuf *ibo = buf; + MeshExtract_LinePaintMask_Data *data = _data; + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +const MeshExtract extract_lines_paint_mask = { + .init = extract_lines_paint_mask_init, + .iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh, + .finish = extract_lines_paint_mask_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Line Adjacency Indices + * \{ */ + +#define NO_EDGE INT_MAX + +typedef struct MeshExtract_LineAdjacency_Data { + GPUIndexBufBuilder elb; + EdgeHash *eh; + bool is_manifold; + /* Array to convert vert index to any loop index of this vert. */ + uint vert_to_loop[0]; +} MeshExtract_LineAdjacency_Data; + +static void *extract_lines_adjacency_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) +{ + /* Similar to poly_to_tri_count(). + * There is always (loop + triangle - 1) edges inside a polygon. + * Accumulate for all polys and you get : */ + uint tess_edge_len = mr->loop_len + mr->tri_len - mr->poly_len; + + size_t vert_to_loop_size = sizeof(uint) * mr->vert_len; + + MeshExtract_LineAdjacency_Data *data = MEM_callocN(sizeof(*data) + vert_to_loop_size, __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES_ADJ, tess_edge_len, mr->loop_len); + data->eh = BLI_edgehash_new_ex(__func__, tess_edge_len); + data->is_manifold = true; + return data; +} + +BLI_INLINE void lines_adjacency_triangle( + uint v1, uint v2, uint v3, uint l1, uint l2, uint l3, MeshExtract_LineAdjacency_Data *data) +{ + GPUIndexBufBuilder *elb = &data->elb; + /* Iterate around the triangle's edges. */ + for (int e = 0; e < 3; e++) { + SHIFT3(uint, v3, v2, v1); + SHIFT3(uint, l3, l2, l1); + + bool inv_indices = (v2 > v3); + void **pval; + bool value_is_init = BLI_edgehash_ensure_p(data->eh, v2, v3, &pval); + int v_data = POINTER_AS_INT(*pval); + if (!value_is_init || v_data == NO_EDGE) { + /* Save the winding order inside the sign bit. Because the + * Edge-hash sort the keys and we need to compare winding later. */ + int value = (int)l1 + 1; /* 0 cannot be signed so add one. */ + *pval = POINTER_FROM_INT((inv_indices) ? -value : value); + /* Store loop indices for remaining non-manifold edges. */ + data->vert_to_loop[v2] = l2; + data->vert_to_loop[v3] = l3; + } + else { + /* HACK Tag as not used. Prevent overhead of BLI_edgehash_remove. */ + *pval = POINTER_FROM_INT(NO_EDGE); + bool inv_opposite = (v_data < 0); + uint l_opposite = (uint)abs(v_data) - 1; + /* TODO Make this part thread-safe. */ + if (inv_opposite == inv_indices) { + /* Don't share edge if triangles have non matching winding. */ + GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l1); + GPU_indexbuf_add_line_adj_verts(elb, l_opposite, l2, l3, l_opposite); + data->is_manifold = false; + } + else { + GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l_opposite); + } + } + } +} + +static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr), + BMLoop **elt, + const int UNUSED(elt_index), + void *data) +{ + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), + BM_elem_index_get(elt[1]->v), + BM_elem_index_get(elt[2]->v), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2]), + data); + } +} + +static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr, + const MLoopTri *mlt, + const int UNUSED(elt_index), + void *data) +{ + const MPoly *mp = &mr->mpoly[mlt->poly]; + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, + mr->mloop[mlt->tri[1]].v, + mr->mloop[mlt->tri[2]].v, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2], + data); + } +} + +static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *cache, + void *buf, + void *_data) +{ + GPUIndexBuf *ibo = buf; + MeshExtract_LineAdjacency_Data *data = _data; + /* Create edges for remaining non manifold edges. */ + EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh); + for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { + uint v2, v3, l1, l2, l3; + int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi)); + if (v_data != NO_EDGE) { + BLI_edgehashIterator_getKey(ehi, &v2, &v3); + l1 = (uint)abs(v_data) - 1; + if (v_data < 0) { /* inv_opposite */ + SWAP(uint, v2, v3); + } + l2 = data->vert_to_loop[v2]; + l3 = data->vert_to_loop[v3]; + GPU_indexbuf_add_line_adj_verts(&data->elb, l1, l2, l3, l1); + data->is_manifold = false; + } + } + BLI_edgehashIterator_free(ehi); + BLI_edgehash_free(data->eh, NULL); + + cache->is_manifold = data->is_manifold; + + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +#undef NO_EDGE + +const MeshExtract extract_lines_adjacency = { + .init = extract_lines_adjacency_init, + .iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm, + .iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh, + .finish = extract_lines_adjacency_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Triangles Indices + * \{ */ + +typedef struct MeshExtract_EditUvElem_Data { + GPUIndexBufBuilder elb; + bool sync_selection; +} MeshExtract_EditUvElem_Data; + +static void *extract_edituv_tris_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len); + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_tri_add( + MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2, int v3) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_add_tri_verts(&data->elb, v1, v2, v3); + } +} + +static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr), + BMLoop **elt, + const int UNUSED(elt_index), + void *data) +{ + edituv_tri_add(data, + BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), + BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); +} + +static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr, + const MLoopTri *mlt, + const int UNUSED(elt_index), + void *data) +{ + const MPoly *mp = &mr->mpoly[mlt->poly]; + edituv_tri_add(data, + (mp->flag & ME_HIDE) != 0, + (mp->flag & ME_FACE_SEL) != 0, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2]); +} + +static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *data) +{ + GPUIndexBuf *ibo = buf; + MeshExtract_EditUvElem_Data *extract_data = data; + GPU_indexbuf_build_in_place(&extract_data->elb, ibo); + MEM_freeN(extract_data); +} + +const MeshExtract extract_edituv_tris = { + .init = extract_edituv_tris_init, + .iter_looptri_bm = extract_edituv_tris_iter_looptri_bm, + .iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh, + .finish = extract_edituv_tris_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Line Indices around faces + * \{ */ + +static void *extract_edituv_lines_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len); + + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_edge_add( + MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_add_line_verts(&data->elb, v1, v2); + } +} + +static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + edituv_edge_add(data, + BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), + BM_elem_flag_test_bool(f, BM_ELEM_SELECT), + l_index, + BM_elem_index_get(l_iter->next)); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + const int ml_index_last = mp->totloop + mp->loopstart - 1; + const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); + const bool real_edge = (mr->e_origindex == NULL || mr->e_origindex[ml->e] != ORIGINDEX_NONE); + edituv_edge_add(data, + (mp->flag & ME_HIDE) != 0 || !real_edge, + (mp->flag & ME_FACE_SEL) != 0, + ml_index, + ml_index_next); + } +} + +static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *data) +{ + GPUIndexBuf *ibo = buf; + MeshExtract_EditUvElem_Data *extract_data = data; + GPU_indexbuf_build_in_place(&extract_data->elb, ibo); + MEM_freeN(extract_data); +} + +const MeshExtract extract_edituv_lines = { + .init = extract_edituv_lines_init, + .iter_poly_bm = extract_edituv_lines_iter_poly_bm, + .iter_poly_mesh = extract_edituv_lines_iter_poly_mesh, + .finish = extract_edituv_lines_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Points Indices + * \{ */ + +static void *extract_edituv_points_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len); + + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data, + bool hidden, + bool selected, + int v1) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_add_point_vert(&data->elb, v1); + } +} + +static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + edituv_point_add( + data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), l_index); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && + mr->v_origindex[ml->v] != ORIGINDEX_NONE); + edituv_point_add( + data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); + } +} + +static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *data) +{ + GPUIndexBuf *ibo = buf; + MeshExtract_EditUvElem_Data *extract_data = data; + GPU_indexbuf_build_in_place(&extract_data->elb, ibo); + MEM_freeN(extract_data); +} + +const MeshExtract extract_edituv_points = { + .init = extract_edituv_points_init, + .iter_poly_bm = extract_edituv_points_iter_poly_bm, + .iter_poly_mesh = extract_edituv_points_iter_poly_mesh, + .finish = extract_edituv_points_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Facedots Indices + * \{ */ + +static void *extract_edituv_fdots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); + + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data, + bool hidden, + bool selected, + int face_index) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_set_point_vert(&data->elb, face_index, face_index); + } + else { + GPU_indexbuf_set_point_restart(&data->elb, face_index); + } +} + +static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, + void *data) +{ + edituv_facedot_add(data, + BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), + BM_elem_flag_test_bool(f, BM_ELEM_SELECT), + f_index); +} + +static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *data) +{ + if (mr->use_subsurf_fdots) { + /* Check #ME_VERT_FACEDOT. */ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[mp_index] != ORIGINDEX_NONE); + const bool subd_fdot = (!mr->use_subsurf_fdots || + (mr->mvert[ml->v].flag & ME_VERT_FACEDOT) != 0); + edituv_facedot_add(data, + ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot, + (mp->flag & ME_FACE_SEL) != 0, + mp_index); + } + } + else { + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[mp_index] != ORIGINDEX_NONE); + edituv_facedot_add( + data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); + } +} + +static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *_data) +{ + GPUIndexBuf *ibo = buf; + MeshExtract_EditUvElem_Data *data = _data; + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +const MeshExtract extract_edituv_fdots = { + .init = extract_edituv_fdots_init, + .iter_poly_bm = extract_edituv_fdots_iter_poly_bm, + .iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh, + .finish = extract_edituv_fdots_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Position and Vertex Normal + * \{ */ + +typedef struct PosNorLoop { + float pos[3]; + GPUPackedNormal nor; +} PosNorLoop; + +typedef struct MeshExtract_PosNor_Data { + PosNorLoop *vbo_data; + GPUNormal normals[]; +} MeshExtract_PosNor_Data; + +static void *extract_pos_nor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* WARNING Adjust #PosNorLoop struct accordingly. */ + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_alias_add(&format, "vnor"); + } + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + + /* Pack normals per vert, reduce amount of computation. */ + size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len; + MeshExtract_PosNor_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__); + data->vbo_data = (PosNorLoop *)GPU_vertbuf_get_data(vbo); + + /* Quicker than doing it for each loop. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMIter iter; + BMVert *eve; + int v; + BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) { + data->normals[v].low = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, eve)); + } + } + else { + const MVert *mv = mr->mvert; + for (int v = 0; v < mr->vert_len; v++, mv++) { + data->normals[v].low = GPU_normal_convert_i10_s3(mv->no); + } + } + return data; +} + +static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_PosNor_Data *data = _data; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + PosNorLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); + vert->nor = data->normals[BM_elem_index_get(l_iter->v)].low; + vert->nor.w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *_data) +{ + MeshExtract_PosNor_Data *data = _data; + + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + PosNorLoop *vert = &data->vbo_data[ml_index]; + const MVert *mv = &mr->mvert[ml->v]; + copy_v3_v3(vert->pos, mv->co); + vert->nor = data->normals[ml->v].low; + /* Flag for paint mode overlay. */ + if (mp->flag & ME_HIDE || mv->flag & ME_HIDE || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && + (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) { + vert->nor.w = -1; + } + else if (mv->flag & SELECT) { + vert->nor.w = 1; + } + else { + vert->nor.w = 0; + } + } +} + +static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *_data) +{ + MeshExtract_PosNor_Data *data = _data; + + int l_index = mr->loop_len + ledge_index * 2; + PosNorLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); + copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); + vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low; + vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low; +} + +static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *_data) +{ + MeshExtract_PosNor_Data *data = _data; + const int ml_index = mr->loop_len + ledge_index * 2; + PosNorLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); + copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); + vert[0].nor = data->normals[med->v1].low; + vert[1].nor = data->normals[med->v2].low; +} + +static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr, + BMVert *eve, + const int lvert_index, + void *_data) +{ + MeshExtract_PosNor_Data *data = _data; + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + const int l_index = offset + lvert_index; + PosNorLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); + vert->nor = data->normals[BM_elem_index_get(eve)].low; +} + +static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr, + const MVert *mv, + const int lvert_index, + void *_data) +{ + MeshExtract_PosNor_Data *data = _data; + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + const int ml_index = offset + lvert_index; + const int v_index = mr->lverts[lvert_index]; + PosNorLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert->pos, mv->co); + vert->nor = data->normals[v_index].low; +} + +static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_pos_nor = {.init = extract_pos_nor_init, + .iter_poly_bm = extract_pos_nor_iter_poly_bm, + .iter_poly_mesh = extract_pos_nor_iter_poly_mesh, + .iter_ledge_bm = extract_pos_nor_iter_ledge_bm, + .iter_ledge_mesh = extract_pos_nor_iter_ledge_mesh, + .iter_lvert_bm = extract_pos_nor_iter_lvert_bm, + .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh, + .finish = extract_pos_nor_finish, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Position and High Quality Vertex Normal + * \{ */ + +typedef struct PosNorHQLoop { + float pos[3]; + short nor[4]; +} PosNorHQLoop; + +typedef struct MeshExtract_PosNorHQ_Data { + PosNorHQLoop *vbo_data; + GPUNormal normals[]; +} MeshExtract_PosNorHQ_Data; + +static void *extract_pos_nor_hq_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* WARNING Adjust #PosNorHQLoop struct accordingly. */ + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_alias_add(&format, "vnor"); + } + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + + /* Pack normals per vert, reduce amount of computation. */ + size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len; + MeshExtract_PosNorHQ_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__); + data->vbo_data = (PosNorHQLoop *)GPU_vertbuf_get_data(vbo); + + /* Quicker than doing it for each loop. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMIter iter; + BMVert *eve; + int v; + BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) { + normal_float_to_short_v3(data->normals[v].high, bm_vert_no_get(mr, eve)); + } + } + else { + const MVert *mv = mr->mvert; + for (int v = 0; v < mr->vert_len; v++, mv++) { + copy_v3_v3_short(data->normals[v].high, mv->no); + } + } + return data; +} + +static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_PosNorHQ_Data *data = _data; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + PosNorHQLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); + copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l_iter->v)].high); + + BMFace *efa = l_iter->f; + vert->nor[3] = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *_data) +{ + MeshExtract_PosNorHQ_Data *data = _data; + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + PosNorHQLoop *vert = &data->vbo_data[ml_index]; + const MVert *mv = &mr->mvert[ml->v]; + copy_v3_v3(vert->pos, mv->co); + copy_v3_v3_short(vert->nor, data->normals[ml->v].high); + + /* Flag for paint mode overlay. */ + if (mp->flag & ME_HIDE || mv->flag & ME_HIDE || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && + (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) { + vert->nor[3] = -1; + } + else if (mv->flag & SELECT) { + vert->nor[3] = 1; + } + else { + vert->nor[3] = 0; + } + } +} + +static void extract_pos_nor_hq_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *_data) +{ + MeshExtract_PosNorHQ_Data *data = _data; + int l_index = mr->loop_len + ledge_index * 2; + PosNorHQLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); + copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); + copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high); + vert[0].nor[3] = 0; + copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high); + vert[1].nor[3] = 0; +} + +static void extract_pos_nor_hq_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *_data) +{ + MeshExtract_PosNorHQ_Data *data = _data; + const int ml_index = mr->loop_len + ledge_index * 2; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); + copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); + copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high); + vert[0].nor[3] = 0; + copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high); + vert[1].nor[3] = 0; +} + +static void extract_pos_nor_hq_iter_lvert_bm(const MeshRenderData *mr, + BMVert *eve, + const int lvert_index, + void *_data) +{ + MeshExtract_PosNorHQ_Data *data = _data; + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + const int l_index = offset + lvert_index; + PosNorHQLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); + copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high); + vert->nor[3] = 0; +} + +static void extract_pos_nor_hq_iter_lvert_mesh(const MeshRenderData *mr, + const MVert *mv, + const int lvert_index, + void *_data) +{ + MeshExtract_PosNorHQ_Data *data = _data; + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + const int ml_index = offset + lvert_index; + const int v_index = mr->lverts[lvert_index]; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert->pos, mv->co); + copy_v3_v3_short(vert->nor, data->normals[v_index].high); + vert->nor[3] = 0; +} + +static void extract_pos_nor_hq_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_pos_nor_hq = { + .init = extract_pos_nor_hq_init, + .iter_poly_bm = extract_pos_nor_hq_iter_poly_bm, + .iter_poly_mesh = extract_pos_nor_hq_iter_poly_mesh, + .iter_ledge_bm = extract_pos_nor_hq_iter_ledge_bm, + .iter_ledge_mesh = extract_pos_nor_hq_iter_ledge_mesh, + .iter_lvert_bm = extract_pos_nor_hq_iter_lvert_bm, + .iter_lvert_mesh = extract_pos_nor_hq_iter_lvert_mesh, + .finish = extract_pos_nor_hq_finish, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; + +/** \} */ +/* ---------------------------------------------------------------------- */ +/** \name Extract HQ Loop Normal + * \{ */ + +typedef struct gpuHQNor { + short x, y, z, w; +} gpuHQNor; + +static void *extract_lnor_hq_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_alias_add(&format, "lnor"); + } + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + return GPU_vertbuf_get_data(vbo); +} + +static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (mr->loop_normals) { + normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, mr->loop_normals[l_index]); + } + else { + if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { + normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l_iter->v)); + } + else { + normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, f)); + } + } + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + gpuHQNor *lnor_data = &((gpuHQNor *)data)[ml_index]; + if (mr->loop_normals) { + normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[ml_index]); + } + else if (mp->flag & ME_SMOOTH) { + copy_v3_v3_short(&lnor_data->x, mr->mvert[ml->v].no); + } + else { + normal_float_to_short_v3(&lnor_data->x, mr->poly_normals[mp_index]); + } + + /* Flag for paint mode overlay. + * Only use #MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. + * In paint mode it will use the un-mapped data to draw the wire-frame. */ + if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && + (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { + lnor_data->w = -1; + } + else if (mp->flag & ME_FACE_SEL) { + lnor_data->w = 1; + } + else { + lnor_data->w = 0; + } + } +} + +const MeshExtract extract_lnor_hq = {.init = extract_lnor_hq_init, + .iter_poly_bm = extract_lnor_hq_iter_poly_bm, + .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh, + .data_flag = MR_DATA_LOOP_NOR, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; + +/** \} */ +/* ---------------------------------------------------------------------- */ +/** \name Extract Loop Normal + * \{ */ + +static void *extract_lnor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_alias_add(&format, "lnor"); + } + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + return GPU_vertbuf_get_data(vbo); +} + +static void extract_lnor_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (mr->loop_normals) { + ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]); + } + else { + if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { + ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3( + bm_vert_no_get(mr, l_iter->v)); + } + else { + ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, f)); + } + } + ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + GPUPackedNormal *lnor_data = &((GPUPackedNormal *)data)[ml_index]; + if (mr->loop_normals) { + *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[ml_index]); + } + else if (mp->flag & ME_SMOOTH) { + *lnor_data = GPU_normal_convert_i10_s3(mr->mvert[ml->v].no); + } + else { + *lnor_data = GPU_normal_convert_i10_v3(mr->poly_normals[mp_index]); + } + + /* Flag for paint mode overlay. + * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. + * In paint mode it will use the un-mapped data to draw the wire-frame. */ + if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && + (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { + lnor_data->w = -1; + } + else if (mp->flag & ME_FACE_SEL) { + lnor_data->w = 1; + } + else { + lnor_data->w = 0; + } + } +} + +const MeshExtract extract_lnor = {.init = extract_lnor_init, + .iter_poly_bm = extract_lnor_iter_poly_bm, + .iter_poly_mesh = extract_lnor_iter_poly_mesh, + .data_flag = MR_DATA_LOOP_NOR, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract UV layers + * \{ */ + +static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) +{ + GPUVertBuf *vbo = buf; + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + + CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + uint32_t uv_layers = cache->cd_used.uv; + /* HACK to fix T68857 */ + if (mr->extract_type == MR_EXTRACT_BMESH && cache->cd_used.edit_uv == 1) { + int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV); + if (layer != -1) { + uv_layers |= (1 << layer); + } + } + + for (int i = 0; i < MAX_MTFACE; i++) { + if (uv_layers & (1 << i)) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i); + + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + /* UV layer name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "u%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + /* Auto layer name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + GPU_vertformat_alias_add(&format, attr_name); + /* Active render layer name. */ + if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "u"); + } + /* Active display layer name. */ + if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "au"); + /* Alias to `pos` for edit uvs. */ + GPU_vertformat_alias_add(&format, "pos"); + } + /* Stencil mask uv layer name. */ + if (i == CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "mu"); + } + } + } + + int v_len = mr->loop_len; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + /* VBO will not be used, only allocate minimum of memory. */ + v_len = 1; + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, v_len); + + float(*uv_data)[2] = (float(*)[2])GPU_vertbuf_get_data(vbo); + for (int i = 0; i < MAX_MTFACE; i++) { + if (uv_layers & (1 << i)) { + if (mr->extract_type == MR_EXTRACT_BMESH) { + int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPUV, i); + BMIter f_iter; + BMFace *efa; + BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs); + memcpy(uv_data, luv->uv, sizeof(*uv_data)); + uv_data++; + } while ((l_iter = l_iter->next) != l_first); + } + } + else { + MLoopUV *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, uv_data++, layer_data++) { + memcpy(uv_data, layer_data->uv, sizeof(*uv_data)); + } + } + } + } + + return NULL; +} + +const MeshExtract extract_uv = {.init = extract_uv_init, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Tangent layers + * \{ */ + +static void extract_tan_ex_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + GPUVertBuf *vbo, + const bool do_hq) +{ + GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10; + GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT; + + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + + CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; + uint32_t tan_layers = cache->cd_used.tan; + float(*orco)[3] = CustomData_get_layer(cd_vdata, CD_ORCO); + bool orco_allocated = false; + const bool use_orco_tan = cache->cd_used.tan_orco != 0; + + int tan_len = 0; + char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; + + for (int i = 0; i < MAX_MTFACE; i++) { + if (tan_layers & (1 << i)) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + /* Tangent layer name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "t%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode); + /* Active render layer name. */ + if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "t"); + } + /* Active display layer name. */ + if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "at"); + } + + BLI_strncpy(tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME); + } + } + if (use_orco_tan && orco == NULL) { + /* If `orco` is not available compute it ourselves */ + orco_allocated = true; + orco = MEM_mallocN(sizeof(*orco) * mr->vert_len, __func__); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMesh *bm = mr->bm; + for (int v = 0; v < mr->vert_len; v++) { + const BMVert *eve = BM_vert_at_index(bm, v); + /* Exceptional case where #bm_vert_co_get can be avoided, as we want the original coords. + * not the distorted ones. */ + copy_v3_v3(orco[v], eve->co); + } + } + else { + const MVert *mv = mr->mvert; + for (int v = 0; v < mr->vert_len; v++, mv++) { + copy_v3_v3(orco[v], mv->co); + } + } + BKE_mesh_orco_verts_transform(mr->me, orco, mr->vert_len, 0); + } + + /* Start Fresh */ + CustomData loop_data; + CustomData_reset(&loop_data); + if (tan_len != 0 || use_orco_tan) { + short tangent_mask = 0; + bool calc_active_tangent = false; + if (mr->extract_type == MR_EXTRACT_BMESH) { + BKE_editmesh_loop_tangent_calc(mr->edit_bmesh, + calc_active_tangent, + tangent_names, + tan_len, + mr->poly_normals, + mr->loop_normals, + orco, + &loop_data, + mr->loop_len, + &tangent_mask); + } + else { + BKE_mesh_calc_loop_tangent_ex(mr->mvert, + mr->mpoly, + mr->poly_len, + mr->mloop, + mr->mlooptri, + mr->tri_len, + cd_ldata, + calc_active_tangent, + tangent_names, + tan_len, + mr->poly_normals, + mr->loop_normals, + orco, + &loop_data, + mr->loop_len, + &tangent_mask); + } + } + + if (use_orco_tan) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name(&loop_data, CD_TANGENT, 0); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + BLI_snprintf(attr_name, sizeof(*attr_name), "t%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode); + GPU_vertformat_alias_add(&format, "t"); + GPU_vertformat_alias_add(&format, "at"); + } + + if (orco_allocated) { + MEM_SAFE_FREE(orco); + } + + int v_len = mr->loop_len; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + /* VBO will not be used, only allocate minimum of memory. */ + v_len = 1; + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, v_len); + + if (do_hq) { + short(*tan_data)[4] = (short(*)[4])GPU_vertbuf_get_data(vbo); + for (int i = 0; i < tan_len; i++) { + const char *name = tangent_names[i]; + float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named( + &loop_data, CD_TANGENT, name); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { + normal_float_to_short_v3(*tan_data, layer_data[ml_index]); + (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN; + tan_data++; + } + } + if (use_orco_tan) { + float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { + normal_float_to_short_v3(*tan_data, layer_data[ml_index]); + (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN; + tan_data++; + } + } + } + else { + GPUPackedNormal *tan_data = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo); + for (int i = 0; i < tan_len; i++) { + const char *name = tangent_names[i]; + float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named( + &loop_data, CD_TANGENT, name); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { + *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]); + tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2; + tan_data++; + } + } + if (use_orco_tan) { + float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { + *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]); + tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2; + tan_data++; + } + } + } + + CustomData_free(&loop_data, mr->loop_len); +} + +static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) +{ + extract_tan_ex_init(mr, cache, buf, false); + return NULL; +} + +const MeshExtract extract_tan = {.init = extract_tan_init, + .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | + MR_DATA_LOOPTRI, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract HQ Tangent layers + * \{ */ + +static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) +{ + extract_tan_ex_init(mr, cache, buf, true); + return NULL; +} + +const MeshExtract extract_tan_hq = { + .init = extract_tan_hq_init, + .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, + .use_threading = false, +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Sculpt Data + * \{ */ + +static void *extract_sculpt_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + GPUVertFormat format = {0}; + + CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; + CustomData *cd_pdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata; + + float *cd_mask = CustomData_get_layer(cd_vdata, CD_PAINT_MASK); + int *cd_face_set = CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); + + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "fset", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_attr_add(&format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + typedef struct gpuSculptData { + uint8_t face_set_color[4]; + float mask; + } gpuSculptData; + + gpuSculptData *vbo_data = (gpuSculptData *)GPU_vertbuf_get_data(vbo); + MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + int cd_mask_ofs = CustomData_get_offset(cd_vdata, CD_PAINT_MASK); + int cd_face_set_ofs = CustomData_get_offset(cd_pdata, CD_SCULPT_FACE_SETS); + BMIter f_iter; + BMFace *efa; + BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + float v_mask = 0.0f; + if (cd_mask) { + v_mask = BM_ELEM_CD_GET_FLOAT(l_iter->v, cd_mask_ofs); + } + vbo_data->mask = v_mask; + uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX}; + if (cd_face_set) { + const int face_set_id = BM_ELEM_CD_GET_INT(l_iter->f, cd_face_set_ofs); + if (face_set_id != mr->me->face_sets_color_default) { + BKE_paint_face_set_overlay_color_get( + face_set_id, mr->me->face_sets_color_seed, face_set_color); + } + } + copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color); + vbo_data++; + } while ((l_iter = l_iter->next) != l_first); + } + } + else { + int mp_loop = 0; + for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { + const MPoly *p = &mr->mpoly[mp_index]; + for (int l = 0; l < p->totloop; l++) { + float v_mask = 0.0f; + if (cd_mask) { + v_mask = cd_mask[loops[mp_loop].v]; + } + vbo_data->mask = v_mask; + + uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX}; + if (cd_face_set) { + const int face_set_id = cd_face_set[mp_index]; + /* Skip for the default color Face Set to render it white. */ + if (face_set_id != mr->me->face_sets_color_default) { + BKE_paint_face_set_overlay_color_get( + face_set_id, mr->me->face_sets_color_seed, face_set_color); + } + } + copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color); + mp_loop++; + vbo_data++; + } + } + } + + return NULL; +} + +const MeshExtract extract_sculpt_data = { + .init = extract_sculpt_data_init, + .data_flag = 0, + /* TODO: enable threading. */ + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract VCol + * \{ */ + +static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) +{ + GPUVertBuf *vbo = buf; + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + + CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; + uint32_t vcol_layers = cache->cd_used.vcol; + uint32_t svcol_layers = cache->cd_used.sculpt_vcol; + + for (int i = 0; i < MAX_MCOL; i++) { + if (vcol_layers & (1 << i)) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + + BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + + if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL)) { + GPU_vertformat_alias_add(&format, "c"); + } + if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL)) { + GPU_vertformat_alias_add(&format, "ac"); + } + + /* Gather number of auto layers. */ + /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */ + if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 && + CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) { + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + GPU_vertformat_alias_add(&format, attr_name); + } + } + } + + /* Sculpt Vertex Colors */ + if (U.experimental.use_sculpt_vertex_colors) { + for (int i = 0; i < 8; i++) { + if (svcol_layers & (1 << i)) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + + BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + + if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) { + GPU_vertformat_alias_add(&format, "c"); + } + if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) { + GPU_vertformat_alias_add(&format, "ac"); + } + /* Gather number of auto layers. */ + /* We only do `vcols` that are not overridden by `uvs`. */ + if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + GPU_vertformat_alias_add(&format, attr_name); + } + } + } + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + typedef struct gpuMeshVcol { + ushort r, g, b, a; + } gpuMeshVcol; + + gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo); + MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP); + + for (int i = 0; i < MAX_MCOL; i++) { + if (vcol_layers & (1 << i)) { + if (mr->extract_type == MR_EXTRACT_BMESH) { + int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPCOL, i); + BMIter f_iter; + BMFace *efa; + BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + const MLoopCol *mloopcol = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs); + vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]); + vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]); + vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]); + vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f)); + vcol_data++; + } while ((l_iter = l_iter->next) != l_first); + } + } + else { + const MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, mloopcol++, vcol_data++) { + vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]); + vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]); + vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]); + vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f)); + } + } + } + + if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) { + if (mr->extract_type == MR_EXTRACT_BMESH) { + int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i); + BMIter f_iter; + BMFace *efa; + BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + const MPropCol *prop_col = BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs); + vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]); + vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]); + vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]); + vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]); + vcol_data++; + } while ((l_iter = l_iter->next) != l_first); + } + } + else { + MPropCol *vcol = CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) { + vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]); + vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]); + vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]); + vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]); + } + } + } + } + return NULL; +} + +const MeshExtract extract_vcol = {.init = extract_vcol_init, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Orco + * \{ */ + +typedef struct MeshExtract_Orco_Data { + float (*vbo_data)[4]; + float (*orco)[3]; +} MeshExtract_Orco_Data; + +static void *extract_orco_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex + * attributes. This is a substantial waste of video-ram and should be done another way. + * Unfortunately, at the time of writing, I did not found any other "non disruptive" + * alternative. */ + GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + CustomData *cd_vdata = &mr->me->vdata; + + MeshExtract_Orco_Data *data = MEM_mallocN(sizeof(*data), __func__); + data->vbo_data = (float(*)[4])GPU_vertbuf_get_data(vbo); + data->orco = CustomData_get_layer(cd_vdata, CD_ORCO); + /* Make sure `orco` layer was requested only if needed! */ + BLI_assert(data->orco); + return data; +} + +static void extract_orco_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *data) +{ + MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + float *loop_orco = orco_data->vbo_data[l_index]; + copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(l_iter->v)]); + loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; + float *loop_orco = orco_data->vbo_data[ml_index]; + copy_v3_v3(loop_orco, orco_data->orco[ml->v]); + loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ + } +} + +static void extract_orco_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_orco = {.init = extract_orco_init, + .iter_poly_bm = extract_orco_iter_poly_bm, + .iter_poly_mesh = extract_orco_iter_poly_mesh, + .finish = extract_orco_finish, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edge Factor + * Defines how much an edge is visible. + * \{ */ + +typedef struct MeshExtract_EdgeFac_Data { + uchar *vbo_data; + bool use_edge_render; + /* Number of loop per edge. */ + uchar edge_loop_count[0]; +} MeshExtract_EdgeFac_Data; + +static float loop_edge_factor_get(const float f_no[3], + const float v_co[3], + const float v_no[3], + const float v_next_co[3]) +{ + float enor[3], evec[3]; + sub_v3_v3v3(evec, v_next_co, v_co); + cross_v3_v3v3(enor, v_no, evec); + normalize_v3(enor); + float d = fabsf(dot_v3v3(enor, f_no)); + /* Re-scale to the slider range. */ + d *= (1.0f / 0.065f); + CLAMP(d, 0.0f, 1.0f); + return d; +} + +static void *extract_edge_fac_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + + MeshExtract_EdgeFac_Data *data; + + if (mr->extract_type == MR_EXTRACT_MESH) { + size_t edge_loop_count_size = sizeof(uint32_t) * mr->edge_len; + data = MEM_callocN(sizeof(*data) + edge_loop_count_size, __func__); + + /* HACK(fclem) Detecting the need for edge render. + * We could have a flag in the mesh instead or check the modifier stack. */ + const MEdge *med = mr->medge; + for (int e_index = 0; e_index < mr->edge_len; e_index++, med++) { + if ((med->flag & ME_EDGERENDER) == 0) { + data->use_edge_render = true; + break; + } + } + } + else { + data = MEM_callocN(sizeof(*data), __func__); + /* HACK to bypass non-manifold check in mesh_edge_fac_finish(). */ + data->use_edge_render = true; + } + + data->vbo_data = GPU_vertbuf_get_data(vbo); + return data; +} + +static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_EdgeFac_Data *data = _data; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + if (BM_edge_is_manifold(l_iter->e)) { + float ratio = loop_edge_factor_get(bm_face_no_get(mr, f), + bm_vert_co_get(mr, l_iter->v), + bm_vert_no_get(mr, l_iter->v), + bm_vert_co_get(mr, l_iter->next->v)); + data->vbo_data[l_index] = ratio * 253 + 1; + } + else { + data->vbo_data[l_index] = 255; + } + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *_data) +{ + MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data; + + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + if (data->use_edge_render) { + const MEdge *med = &mr->medge[ml->e]; + data->vbo_data[ml_index] = (med->flag & ME_EDGERENDER) ? 255 : 0; + } + else { + + /* Count loop per edge to detect non-manifold. */ + if (data->edge_loop_count[ml->e] < 3) { + data->edge_loop_count[ml->e]++; + } + if (data->edge_loop_count[ml->e] == 2) { + /* Manifold */ + const int ml_index_last = mp->totloop + mp->loopstart - 1; + const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); + const MLoop *ml_next = &mr->mloop[ml_index_other]; + const MVert *v1 = &mr->mvert[ml->v]; + const MVert *v2 = &mr->mvert[ml_next->v]; + float vnor_f[3]; + normal_short_to_float_v3(vnor_f, v1->no); + float ratio = loop_edge_factor_get(mr->poly_normals[mp_index], v1->co, vnor_f, v2->co); + data->vbo_data[ml_index] = ratio * 253 + 1; + } + else { + /* Non-manifold */ + data->vbo_data[ml_index] = 255; + } + } + } +} + +static void extract_edge_fac_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *UNUSED(eed), + const int ledge_index, + void *_data) +{ + MeshExtract_EdgeFac_Data *data = _data; + data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255; + data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255; +} + +static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *UNUSED(med), + const uint ledge_index, + void *_data) +{ + MeshExtract_EdgeFac_Data *data = _data; + + data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255; + data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255; +} + +static void extract_edge_fac_finish(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *_data) +{ + GPUVertBuf *vbo = buf; + MeshExtract_EdgeFac_Data *data = _data; + + if (GPU_crappy_amd_driver()) { + /* Some AMD drivers strangely crash with VBO's with a one byte format. + * To workaround we reinitialize the VBO with another format and convert + * all bytes to floats. */ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + } + /* We keep the data reference in data->vbo_data. */ + data->vbo_data = GPU_vertbuf_steal_data(vbo); + GPU_vertbuf_clear(vbo); + + int buf_len = mr->loop_len + mr->loop_loose_len; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, buf_len); + + float *fdata = (float *)GPU_vertbuf_get_data(vbo); + for (int ml_index = 0; ml_index < buf_len; ml_index++, fdata++) { + *fdata = data->vbo_data[ml_index] / 255.0f; + } + /* Free old byte data. */ + MEM_freeN(data->vbo_data); + } + MEM_freeN(data); +} + +const MeshExtract extract_edge_fac = { + .init = extract_edge_fac_init, + .iter_poly_bm = extract_edge_fac_iter_poly_bm, + .iter_poly_mesh = extract_edge_fac_iter_poly_mesh, + .iter_ledge_bm = extract_edge_fac_iter_ledge_bm, + .iter_ledge_mesh = extract_edge_fac_iter_ledge_mesh, + .finish = extract_edge_fac_finish, + .data_flag = MR_DATA_POLY_NOR, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac)}; + +/** \} */ +/* ---------------------------------------------------------------------- */ +/** \name Extract Vertex Weight + * \{ */ + +typedef struct MeshExtract_Weight_Data { + float *vbo_data; + const DRW_MeshWeightState *wstate; + const MDeformVert *dvert; /* For #Mesh. */ + int cd_ofs; /* For #BMesh. */ +} MeshExtract_Weight_Data; + +static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeightState *wstate) +{ + /* Error state. */ + if ((wstate->defgroup_active < 0) && (wstate->defgroup_len > 0)) { + return -2.0f; + } + if (dvert == NULL) { + return (wstate->alert_mode != OB_DRAW_GROUPUSER_NONE) ? -1.0f : 0.0f; + } + + float input = 0.0f; + if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) { + /* Multi-Paint feature */ + bool is_normalized = (wstate->flags & (DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE | + DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE)); + input = BKE_defvert_multipaint_collective_weight(dvert, + wstate->defgroup_len, + wstate->defgroup_sel, + wstate->defgroup_sel_count, + is_normalized); + /* make it black if the selected groups have no weight on a vertex */ + if (input == 0.0f) { + return -1.0f; + } + } + else { + /* default, non tricky behavior */ + input = BKE_defvert_find_weight(dvert, wstate->defgroup_active); + + if (input == 0.0f) { + switch (wstate->alert_mode) { + case OB_DRAW_GROUPUSER_ACTIVE: + return -1.0f; + break; + case OB_DRAW_GROUPUSER_ALL: + if (BKE_defvert_is_weight_zero(dvert, wstate->defgroup_len)) { + return -1.0f; + } + break; + } + } + } + + /* Lock-Relative: display the fraction of current weight vs total unlocked weight. */ + if (wstate->flags & DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE) { + input = BKE_defvert_lock_relative_weight( + input, dvert, wstate->defgroup_len, wstate->defgroup_locked, wstate->defgroup_unlocked); + } + + CLAMP(input, 0.0f, 1.0f); + return input; +} + +static void *extract_weights_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + } + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + + MeshExtract_Weight_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (float *)GPU_vertbuf_get_data(vbo); + data->wstate = &cache->weight_state; + + if (data->wstate->defgroup_active == -1) { + /* Nothing to show. */ + data->dvert = NULL; + data->cd_ofs = -1; + } + else if (mr->extract_type == MR_EXTRACT_BMESH) { + data->dvert = NULL; + data->cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MDEFORMVERT); + } + else { + data->dvert = CustomData_get_layer(&mr->me->vdata, CD_MDEFORMVERT); + data->cd_ofs = -1; + } + return data; +} + +static void extract_weights_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_Weight_Data *data = _data; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (data->cd_ofs != -1) { + const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l_iter->v, data->cd_ofs); + data->vbo_data[l_index] = evaluate_vertex_weight(dvert, data->wstate); + } + else { + data->vbo_data[l_index] = evaluate_vertex_weight(NULL, data->wstate); + } + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_weights_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *_data) +{ + MeshExtract_Weight_Data *data = _data; + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (data->dvert != NULL) { + const MDeformVert *dvert = &data->dvert[ml->v]; + data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); + } + else { + const MDeformVert *dvert = NULL; + data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); + } + } +} + +static void extract_weights_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_weights = {.init = extract_weights_init, + .iter_poly_bm = extract_weights_iter_poly_bm, + .iter_poly_mesh = extract_weights_iter_poly_mesh, + .finish = extract_weights_finish, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit Mode Data / Flags + * \{ */ + +typedef struct EditLoopData { + uchar v_flag; + uchar e_flag; + uchar crease; + uchar bweight; +} EditLoopData; + +static void mesh_render_data_face_flag(const MeshRenderData *mr, + BMFace *efa, + const int cd_ofs, + EditLoopData *eattr) +{ + if (efa == mr->efa_act) { + eattr->v_flag |= VFLAG_FACE_ACTIVE; + } + if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + eattr->v_flag |= VFLAG_FACE_SELECTED; + } + + if (efa == mr->efa_act_uv) { + eattr->v_flag |= VFLAG_FACE_UV_ACTIVE; + } + if ((cd_ofs != -1) && uvedit_face_select_test_ex(mr->toolsettings, (BMFace *)efa, cd_ofs)) { + eattr->v_flag |= VFLAG_FACE_UV_SELECT; + } + +#ifdef WITH_FREESTYLE + if (mr->freestyle_face_ofs != -1) { + const FreestyleFace *ffa = BM_ELEM_CD_GET_VOID_P(efa, mr->freestyle_face_ofs); + if (ffa->flag & FREESTYLE_FACE_MARK) { + eattr->v_flag |= VFLAG_FACE_FREESTYLE; + } + } +#endif +} + +static void mesh_render_data_edge_flag(const MeshRenderData *mr, BMEdge *eed, EditLoopData *eattr) +{ + const ToolSettings *ts = mr->toolsettings; + const bool is_vertex_select_mode = (ts != NULL) && (ts->selectmode & SCE_SELECT_VERTEX) != 0; + const bool is_face_only_select_mode = (ts != NULL) && (ts->selectmode == SCE_SELECT_FACE); + + if (eed == mr->eed_act) { + eattr->e_flag |= VFLAG_EDGE_ACTIVE; + } + if (!is_vertex_select_mode && BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + eattr->e_flag |= VFLAG_EDGE_SELECTED; + } + if (is_vertex_select_mode && BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) && + BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) { + eattr->e_flag |= VFLAG_EDGE_SELECTED; + eattr->e_flag |= VFLAG_VERT_SELECTED; + } + if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) { + eattr->e_flag |= VFLAG_EDGE_SEAM; + } + if (!BM_elem_flag_test(eed, BM_ELEM_SMOOTH)) { + eattr->e_flag |= VFLAG_EDGE_SHARP; + } + + /* Use active edge color for active face edges because + * specular highlights make it hard to see T55456#510873. + * + * This isn't ideal since it can't be used when mixing edge/face modes + * but it's still better than not being able to see the active face. */ + if (is_face_only_select_mode) { + if (mr->efa_act != NULL) { + if (BM_edge_in_face(eed, mr->efa_act)) { + eattr->e_flag |= VFLAG_EDGE_ACTIVE; + } + } + } + + /* Use a byte for value range */ + if (mr->crease_ofs != -1) { + float crease = BM_ELEM_CD_GET_FLOAT(eed, mr->crease_ofs); + if (crease > 0) { + eattr->crease = (uchar)(crease * 255.0f); + } + } + /* Use a byte for value range */ + if (mr->bweight_ofs != -1) { + float bweight = BM_ELEM_CD_GET_FLOAT(eed, mr->bweight_ofs); + if (bweight > 0) { + eattr->bweight = (uchar)(bweight * 255.0f); + } + } +#ifdef WITH_FREESTYLE + if (mr->freestyle_edge_ofs != -1) { + const FreestyleEdge *fed = BM_ELEM_CD_GET_VOID_P(eed, mr->freestyle_edge_ofs); + if (fed->flag & FREESTYLE_EDGE_MARK) { + eattr->e_flag |= VFLAG_EDGE_FREESTYLE; + } + } +#endif +} + +static void mesh_render_data_loop_flag(const MeshRenderData *mr, + BMLoop *l, + const int cd_ofs, + EditLoopData *eattr) +{ + if (cd_ofs == -1) { + return; + } + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_ofs); + if (luv != NULL && (luv->flag & MLOOPUV_PINNED)) { + eattr->v_flag |= VFLAG_VERT_UV_PINNED; + } + if (uvedit_uv_select_test_ex(mr->toolsettings, l, cd_ofs)) { + eattr->v_flag |= VFLAG_VERT_UV_SELECT; + } +} + +static void mesh_render_data_loop_edge_flag(const MeshRenderData *mr, + BMLoop *l, + const int cd_ofs, + EditLoopData *eattr) +{ + if (cd_ofs == -1) { + return; + } + if (uvedit_edge_select_test_ex(mr->toolsettings, l, cd_ofs)) { + eattr->v_flag |= VFLAG_EDGE_UV_SELECT; + eattr->v_flag |= VFLAG_VERT_UV_SELECT; + } +} + +static void mesh_render_data_vert_flag(const MeshRenderData *mr, BMVert *eve, EditLoopData *eattr) +{ + if (eve == mr->eve_act) { + eattr->e_flag |= VFLAG_VERT_ACTIVE; + } + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + eattr->e_flag |= VFLAG_VERT_SELECTED; + } +} + +static void *extract_edit_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* WARNING: Adjust #EditLoopData struct accordingly. */ + GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); + GPU_vertformat_alias_add(&format, "flag"); + } + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + return GPU_vertbuf_get_data(vbo); +} + +static void extract_edit_data_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + EditLoopData *data = (EditLoopData *)_data + l_index; + memset(data, 0x0, sizeof(*data)); + mesh_render_data_face_flag(mr, f, -1, data); + mesh_render_data_edge_flag(mr, l_iter->e, data); + mesh_render_data_vert_flag(mr, l_iter->v, data); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *_data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + EditLoopData *data = (EditLoopData *)_data + ml_index; + memset(data, 0x0, sizeof(*data)); + BMFace *efa = bm_original_face_get(mr, mp_index); + BMEdge *eed = bm_original_edge_get(mr, ml->e); + BMVert *eve = bm_original_vert_get(mr, ml->v); + if (efa) { + mesh_render_data_face_flag(mr, efa, -1, data); + } + if (eed) { + mesh_render_data_edge_flag(mr, eed, data); + } + if (eve) { + mesh_render_data_vert_flag(mr, eve, data); + } + } +} + +static void extract_edit_data_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *_data) +{ + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2); + memset(data, 0x0, sizeof(*data) * 2); + mesh_render_data_edge_flag(mr, eed, &data[0]); + data[1] = data[0]; + mesh_render_data_vert_flag(mr, eed->v1, &data[0]); + mesh_render_data_vert_flag(mr, eed->v2, &data[1]); +} + +static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *_data) +{ + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2; + memset(data, 0x0, sizeof(*data) * 2); + const int e_index = mr->ledges[ledge_index]; + BMEdge *eed = bm_original_edge_get(mr, e_index); + BMVert *eve1 = bm_original_vert_get(mr, med->v1); + BMVert *eve2 = bm_original_vert_get(mr, med->v2); + if (eed) { + mesh_render_data_edge_flag(mr, eed, &data[0]); + data[1] = data[0]; + } + if (eve1) { + mesh_render_data_vert_flag(mr, eve1, &data[0]); + } + if (eve2) { + mesh_render_data_vert_flag(mr, eve2, &data[1]); + } +} + +static void extract_edit_data_iter_lvert_bm(const MeshRenderData *mr, + BMVert *eve, + const int lvert_index, + void *_data) +{ + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; + memset(data, 0x0, sizeof(*data)); + mesh_render_data_vert_flag(mr, eve, data); +} + +static void extract_edit_data_iter_lvert_mesh(const MeshRenderData *mr, + const MVert *UNUSED(mv), + const int lvert_index, + void *_data) +{ + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; + memset(data, 0x0, sizeof(*data)); + const int v_index = mr->lverts[lvert_index]; + BMVert *eve = bm_original_vert_get(mr, v_index); + if (eve) { + mesh_render_data_vert_flag(mr, eve, data); + } +} + +const MeshExtract extract_edit_data = { + .init = extract_edit_data_init, + .iter_poly_bm = extract_edit_data_iter_poly_bm, + .iter_poly_mesh = extract_edit_data_iter_poly_mesh, + .iter_ledge_bm = extract_edit_data_iter_ledge_bm, + .iter_ledge_mesh = extract_edit_data_iter_ledge_mesh, + .iter_lvert_bm = extract_edit_data_iter_lvert_bm, + .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Data / Flags + * \{ */ + +typedef struct MeshExtract_EditUVData_Data { + EditLoopData *vbo_data; + int cd_ofs; +} MeshExtract_EditUVData_Data; + +static void *extract_edituv_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* WARNING: Adjust #EditLoopData struct accordingly. */ + GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); + GPU_vertformat_alias_add(&format, "flag"); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + + MeshExtract_EditUVData_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo); + data->cd_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV); + return data; +} + +static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + MeshExtract_EditUVData_Data *data = _data; + EditLoopData *eldata = &data->vbo_data[l_index]; + memset(eldata, 0x0, sizeof(*eldata)); + mesh_render_data_loop_flag(mr, l_iter, data->cd_ofs, eldata); + mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); + mesh_render_data_loop_edge_flag(mr, l_iter, data->cd_ofs, eldata); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *_data) +{ + MeshExtract_EditUVData_Data *data = _data; + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + EditLoopData *eldata = &data->vbo_data[ml_index]; + memset(eldata, 0x0, sizeof(*eldata)); + BMFace *efa = bm_original_face_get(mr, mp_index); + if (efa) { + BMEdge *eed = bm_original_edge_get(mr, ml->e); + BMVert *eve = bm_original_vert_get(mr, ml->v); + if (eed && eve) { + /* Loop on an edge endpoint. */ + BMLoop *l = BM_face_edge_share_loop(efa, eed); + mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata); + mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata); + } + else { + if (eed == NULL) { + /* Find if the loop's vert is not part of an edit edge. + * For this, we check if the previous loop was on an edge. */ + const int ml_index_last = mp->loopstart + mp->totloop - 1; + const int l_prev = (ml_index == mp->loopstart) ? ml_index_last : (ml_index - 1); + const MLoop *ml_prev = &mr->mloop[l_prev]; + eed = bm_original_edge_get(mr, ml_prev->e); + } + if (eed) { + /* Mapped points on an edge between two edit verts. */ + BMLoop *l = BM_face_edge_share_loop(efa, eed); + mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata); + } + } + } + } +} + +static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_edituv_data = { + .init = extract_edituv_data_init, + .iter_poly_bm = extract_edituv_data_iter_poly_bm, + .iter_poly_mesh = extract_edituv_data_iter_poly_mesh, + .finish = extract_edituv_data_finish, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV area stretch + * \{ */ + +static void *extract_edituv_stretch_area_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "ratio", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + return NULL; +} + +BLI_INLINE float area_ratio_get(float area, float uvarea) +{ + if (area >= FLT_EPSILON && uvarea >= FLT_EPSILON) { + /* Tag inversion by using the sign. */ + return (area > uvarea) ? (uvarea / area) : -(area / uvarea); + } + return 0.0f; +} + +BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_tot_ratio) +{ + ratio *= (ratio > 0.0f) ? tot_ratio : -inv_tot_ratio; + return (ratio > 1.0f) ? (1.0f / ratio) : ratio; +} + +static void extract_edituv_stretch_area_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(data)) +{ + GPUVertBuf *vbo = buf; + float tot_area = 0.0f, tot_uv_area = 0.0f; + float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + CustomData *cd_ldata = &mr->bm->ldata; + int uv_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV); + + BMFace *efa; + BMIter f_iter; + int f; + BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) { + float area = BM_face_calc_area(efa); + float uvarea = BM_face_calc_area_uv(efa, uv_ofs); + tot_area += area; + tot_uv_area += uvarea; + area_ratio[f] = area_ratio_get(area, uvarea); + } + } + else { + BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); + const MLoopUV *uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); + const MPoly *mp = mr->mpoly; + for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { + float area = BKE_mesh_calc_poly_area(mp, &mr->mloop[mp->loopstart], mr->mvert); + float uvarea = BKE_mesh_calc_poly_uv_area(mp, uv_data); + tot_area += area; + tot_uv_area += uvarea; + area_ratio[mp_index] = area_ratio_get(area, uvarea); + } + } + + cache->tot_area = tot_area; + cache->tot_uv_area = tot_uv_area; + + /* Convert in place to avoid an extra allocation */ + uint16_t *poly_stretch = (uint16_t *)area_ratio; + for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { + poly_stretch[mp_index] = area_ratio[mp_index] * SHRT_MAX; + } + + /* Copy face data for each loop. */ + uint16_t *loop_stretch = (uint16_t *)GPU_vertbuf_get_data(vbo); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMFace *efa; + BMIter f_iter; + int f, l_index = 0; + BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) { + for (int i = 0; i < efa->len; i++, l_index++) { + loop_stretch[l_index] = poly_stretch[f]; + } + } + } + else { + BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); + const MPoly *mp = mr->mpoly; + for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { + for (int i = 0; i < mp->totloop; i++, l_index++) { + loop_stretch[l_index] = poly_stretch[mp_index]; + } + } + } + + MEM_freeN(area_ratio); +} + +const MeshExtract extract_edituv_stretch_area = { + .init = extract_edituv_stretch_area_init, + .finish = extract_edituv_stretch_area_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV angle stretch + * \{ */ + +typedef struct UVStretchAngle { + int16_t angle; + int16_t uv_angles[2]; +} UVStretchAngle; + +typedef struct MeshExtract_StretchAngle_Data { + UVStretchAngle *vbo_data; + MLoopUV *luv; + float auv[2][2], last_auv[2]; + float av[2][3], last_av[3]; + int cd_ofs; +} MeshExtract_StretchAngle_Data; + +static void compute_normalize_edge_vectors(float auv[2][2], + float av[2][3], + const float uv[2], + const float uv_prev[2], + const float co[3], + const float co_prev[3]) +{ + /* Move previous edge. */ + copy_v2_v2(auv[0], auv[1]); + copy_v3_v3(av[0], av[1]); + /* 2d edge */ + sub_v2_v2v2(auv[1], uv_prev, uv); + normalize_v2(auv[1]); + /* 3d edge */ + sub_v3_v3v3(av[1], co_prev, co); + normalize_v3(av[1]); +} + +static short v2_to_short_angle(const float v[2]) +{ + return atan2f(v[1], v[0]) * (float)M_1_PI * SHRT_MAX; +} + +static void edituv_get_edituv_stretch_angle(float auv[2][2], + const float av[2][3], + UVStretchAngle *r_stretch) +{ + /* Send UV's to the shader and let it compute the aspect corrected angle. */ + r_stretch->uv_angles[0] = v2_to_short_angle(auv[0]); + r_stretch->uv_angles[1] = v2_to_short_angle(auv[1]); + /* Compute 3D angle here. */ + r_stretch->angle = angle_normalized_v3v3(av[0], av[1]) * (float)M_1_PI * SHRT_MAX; + +#if 0 /* here for reference, this is done in shader now. */ + float uvang = angle_normalized_v2v2(auv0, auv1); + float ang = angle_normalized_v3v3(av0, av1); + float stretch = fabsf(uvang - ang) / (float)M_PI; + return 1.0f - pow2f(1.0f - stretch); +#endif +} + +static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* Waning: adjust #UVStretchAngle struct accordingly. */ + GPU_vertformat_attr_add(&format, "angle", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + MeshExtract_StretchAngle_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (UVStretchAngle *)GPU_vertbuf_get_data(vbo); + + /* Special iterator needed to save about half of the computing cost. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); + } + else { + BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); + data->luv = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); + } + return data; +} + +static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_StretchAngle_Data *data = _data; + float(*auv)[2] = data->auv, *last_auv = data->last_auv; + float(*av)[3] = data->av, *last_av = data->last_av; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + const MLoopUV *luv, *luv_next; + BMLoop *l_next = l_iter->next; + if (l_iter == BM_FACE_FIRST_LOOP(f)) { + /* First loop in face. */ + BMLoop *l_tmp = l_iter->prev; + BMLoop *l_next_tmp = l_iter; + luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs); + luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs); + compute_normalize_edge_vectors(auv, + av, + luv->uv, + luv_next->uv, + bm_vert_co_get(mr, l_tmp->v), + bm_vert_co_get(mr, l_next_tmp->v)); + /* Save last edge. */ + copy_v2_v2(last_auv, auv[1]); + copy_v3_v3(last_av, av[1]); + } + if (l_next == BM_FACE_FIRST_LOOP(f)) { + /* Move previous edge. */ + copy_v2_v2(auv[0], auv[1]); + copy_v3_v3(av[0], av[1]); + /* Copy already calculated last edge. */ + copy_v2_v2(auv[1], last_auv); + copy_v3_v3(av[1], last_av); + } + else { + luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); + luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs); + compute_normalize_edge_vectors(auv, + av, + luv->uv, + luv_next->uv, + bm_vert_co_get(mr, l_iter->v), + bm_vert_co_get(mr, l_next->v)); + } + edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[l_index]); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *_data) +{ + MeshExtract_StretchAngle_Data *data = _data; + + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + float(*auv)[2] = data->auv, *last_auv = data->last_auv; + float(*av)[3] = data->av, *last_av = data->last_av; + int l_next = ml_index + 1; + const MVert *v, *v_next; + if (ml_index == mp->loopstart) { + /* First loop in face. */ + const int ml_index_last = ml_index_end - 1; + const int l_next_tmp = mp->loopstart; + v = &mr->mvert[mr->mloop[ml_index_last].v]; + v_next = &mr->mvert[mr->mloop[l_next_tmp].v]; + compute_normalize_edge_vectors( + auv, av, data->luv[ml_index_last].uv, data->luv[l_next_tmp].uv, v->co, v_next->co); + /* Save last edge. */ + copy_v2_v2(last_auv, auv[1]); + copy_v3_v3(last_av, av[1]); + } + if (l_next == ml_index_end) { + l_next = mp->loopstart; + /* Move previous edge. */ + copy_v2_v2(auv[0], auv[1]); + copy_v3_v3(av[0], av[1]); + /* Copy already calculated last edge. */ + copy_v2_v2(auv[1], last_auv); + copy_v3_v3(av[1], last_av); + } + else { + v = &mr->mvert[mr->mloop[ml_index].v]; + v_next = &mr->mvert[mr->mloop[l_next].v]; + compute_normalize_edge_vectors( + auv, av, data->luv[ml_index].uv, data->luv[l_next].uv, v->co, v_next->co); + } + edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[ml_index]); + } +} + +static void extract_edituv_stretch_angle_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_edituv_stretch_angle = { + .init = extract_edituv_stretch_angle_init, + .iter_poly_bm = extract_edituv_stretch_angle_iter_poly_bm, + .iter_poly_mesh = extract_edituv_stretch_angle_iter_poly_mesh, + .finish = extract_edituv_stretch_angle_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit Mesh Analysis Colors + * \{ */ + +static void *extract_mesh_analysis_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + return NULL; +} + +static void axis_from_enum_v3(float v[3], const char axis) +{ + zero_v3(v); + if (axis < 3) { + v[axis] = 1.0f; + } + else { + v[axis - 3] = -1.0f; + } +} + +BLI_INLINE float overhang_remap(float fac, float min, float max, float minmax_irange) +{ + if (fac < min) { + fac = 1.0f; + } + else if (fac > max) { + fac = -1.0f; + } + else { + fac = (fac - min) * minmax_irange; + fac = 1.0f - fac; + CLAMP(fac, 0.0f, 1.0f); + } + return fac; +} + +static void statvis_calc_overhang(const MeshRenderData *mr, float *r_overhang) +{ + const MeshStatVis *statvis = &mr->toolsettings->statvis; + const float min = statvis->overhang_min / (float)M_PI; + const float max = statvis->overhang_max / (float)M_PI; + const char axis = statvis->overhang_axis; + BMEditMesh *em = mr->edit_bmesh; + BMIter iter; + BMesh *bm = em->bm; + BMFace *f; + float dir[3]; + const float minmax_irange = 1.0f / (max - min); + + BLI_assert(min <= max); + + axis_from_enum_v3(dir, axis); + + /* now convert into global space */ + mul_transposed_mat3_m4_v3(mr->obmat, dir); + normalize_v3(dir); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + int l_index = 0; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + float fac = angle_normalized_v3v3(bm_face_no_get(mr, f), dir) / (float)M_PI; + fac = overhang_remap(fac, min, max, minmax_irange); + for (int i = 0; i < f->len; i++, l_index++) { + r_overhang[l_index] = fac; + } + } + } + else { + const MPoly *mp = mr->mpoly; + for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { + float fac = angle_normalized_v3v3(mr->poly_normals[mp_index], dir) / (float)M_PI; + fac = overhang_remap(fac, min, max, minmax_irange); + for (int i = 0; i < mp->totloop; i++, l_index++) { + r_overhang[l_index] = fac; + } + } + } +} + +/** + * Needed so we can use jitter values for face interpolation. + */ +static void uv_from_jitter_v2(float uv[2]) +{ + uv[0] += 0.5f; + uv[1] += 0.5f; + if (uv[0] + uv[1] > 1.0f) { + uv[0] = 1.0f - uv[0]; + uv[1] = 1.0f - uv[1]; + } + + clamp_v2(uv, 0.0f, 1.0f); +} + +BLI_INLINE float thickness_remap(float fac, float min, float max, float minmax_irange) +{ + /* important not '<=' */ + if (fac < max) { + fac = (fac - min) * minmax_irange; + fac = 1.0f - fac; + CLAMP(fac, 0.0f, 1.0f); + } + else { + fac = -1.0f; + } + return fac; +} + +static void statvis_calc_thickness(const MeshRenderData *mr, float *r_thickness) +{ + const float eps_offset = 0.00002f; /* values <= 0.00001 give errors */ + /* cheating to avoid another allocation */ + float *face_dists = r_thickness + (mr->loop_len - mr->poly_len); + BMEditMesh *em = mr->edit_bmesh; + const float scale = 1.0f / mat4_to_scale(mr->obmat); + const MeshStatVis *statvis = &mr->toolsettings->statvis; + const float min = statvis->thickness_min * scale; + const float max = statvis->thickness_max * scale; + const float minmax_irange = 1.0f / (max - min); + const int samples = statvis->thickness_samples; + float jit_ofs[32][2]; + BLI_assert(samples <= 32); + BLI_assert(min <= max); + + copy_vn_fl(face_dists, mr->poly_len, max); + + BLI_jitter_init(jit_ofs, samples); + for (int j = 0; j < samples; j++) { + uv_from_jitter_v2(jit_ofs[j]); + } + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMesh *bm = em->bm; + BM_mesh_elem_index_ensure(bm, BM_FACE); + + struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false); + struct BMLoop *(*looptris)[3] = em->looptris; + for (int i = 0; i < mr->tri_len; i++) { + BMLoop **ltri = looptris[i]; + const int index = BM_elem_index_get(ltri[0]->f); + const float *cos[3] = { + bm_vert_co_get(mr, ltri[0]->v), + bm_vert_co_get(mr, ltri[1]->v), + bm_vert_co_get(mr, ltri[2]->v), + }; + float ray_co[3]; + float ray_no[3]; + + normal_tri_v3(ray_no, cos[2], cos[1], cos[0]); + + for (int j = 0; j < samples; j++) { + float dist = face_dists[index]; + interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]); + madd_v3_v3fl(ray_co, ray_no, eps_offset); + + BMFace *f_hit = BKE_bmbvh_ray_cast(bmtree, ray_co, ray_no, 0.0f, &dist, NULL, NULL); + if (f_hit && dist < face_dists[index]) { + float angle_fac = fabsf( + dot_v3v3(bm_face_no_get(mr, ltri[0]->f), bm_face_no_get(mr, f_hit))); + angle_fac = 1.0f - angle_fac; + angle_fac = angle_fac * angle_fac * angle_fac; + angle_fac = 1.0f - angle_fac; + dist /= angle_fac; + if (dist < face_dists[index]) { + face_dists[index] = dist; + } + } + } + } + BKE_bmbvh_free(bmtree); + + BMIter iter; + BMFace *f; + int l_index = 0; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + float fac = face_dists[BM_elem_index_get(f)]; + fac = thickness_remap(fac, min, max, minmax_irange); + for (int i = 0; i < f->len; i++, l_index++) { + r_thickness[l_index] = fac; + } + } + } + else { + BVHTreeFromMesh treeData = {NULL}; + + BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4); + const MLoopTri *mlooptri = mr->mlooptri; + for (int i = 0; i < mr->tri_len; i++, mlooptri++) { + const int index = mlooptri->poly; + const float *cos[3] = {mr->mvert[mr->mloop[mlooptri->tri[0]].v].co, + mr->mvert[mr->mloop[mlooptri->tri[1]].v].co, + mr->mvert[mr->mloop[mlooptri->tri[2]].v].co}; + float ray_co[3]; + float ray_no[3]; + + normal_tri_v3(ray_no, cos[2], cos[1], cos[0]); + + for (int j = 0; j < samples; j++) { + interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]); + madd_v3_v3fl(ray_co, ray_no, eps_offset); + + BVHTreeRayHit hit; + hit.index = -1; + hit.dist = face_dists[index]; + if ((BLI_bvhtree_ray_cast( + tree, ray_co, ray_no, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) && + hit.dist < face_dists[index]) { + float angle_fac = fabsf(dot_v3v3(mr->poly_normals[index], hit.no)); + angle_fac = 1.0f - angle_fac; + angle_fac = angle_fac * angle_fac * angle_fac; + angle_fac = 1.0f - angle_fac; + hit.dist /= angle_fac; + if (hit.dist < face_dists[index]) { + face_dists[index] = hit.dist; + } + } + } + } + + const MPoly *mp = mr->mpoly; + for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { + float fac = face_dists[mp_index]; + fac = thickness_remap(fac, min, max, minmax_irange); + for (int i = 0; i < mp->totloop; i++, l_index++) { + r_thickness[l_index] = fac; + } + } + } +} + +struct BVHTree_OverlapData { + const Mesh *me; + const MLoopTri *mlooptri; + float epsilon; +}; + +static bool bvh_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread)) +{ + struct BVHTree_OverlapData *data = userdata; + const Mesh *me = data->me; + + const MLoopTri *tri_a = &data->mlooptri[index_a]; + const MLoopTri *tri_b = &data->mlooptri[index_b]; + + if (UNLIKELY(tri_a->poly == tri_b->poly)) { + return false; + } + + const float *tri_a_co[3] = {me->mvert[me->mloop[tri_a->tri[0]].v].co, + me->mvert[me->mloop[tri_a->tri[1]].v].co, + me->mvert[me->mloop[tri_a->tri[2]].v].co}; + const float *tri_b_co[3] = {me->mvert[me->mloop[tri_b->tri[0]].v].co, + me->mvert[me->mloop[tri_b->tri[1]].v].co, + me->mvert[me->mloop[tri_b->tri[2]].v].co}; + float ix_pair[2][3]; + int verts_shared = 0; + + verts_shared = (ELEM(tri_a_co[0], UNPACK3(tri_b_co)) + ELEM(tri_a_co[1], UNPACK3(tri_b_co)) + + ELEM(tri_a_co[2], UNPACK3(tri_b_co))); + + /* if 2 points are shared, bail out */ + if (verts_shared >= 2) { + return false; + } + + return (isect_tri_tri_v3(UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1]) && + /* if we share a vertex, check the intersection isn't a 'point' */ + ((verts_shared == 0) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) > data->epsilon))); +} + +static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect) +{ + BMEditMesh *em = mr->edit_bmesh; + + for (int l_index = 0; l_index < mr->loop_len; l_index++) { + r_intersect[l_index] = -1.0f; + } + + if (mr->extract_type == MR_EXTRACT_BMESH) { + uint overlap_len; + BMesh *bm = em->bm; + + BM_mesh_elem_index_ensure(bm, BM_FACE); + + struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false); + BVHTreeOverlap *overlap = BKE_bmbvh_overlap_self(bmtree, &overlap_len); + + if (overlap) { + for (int i = 0; i < overlap_len; i++) { + BMFace *f_hit_pair[2] = { + em->looptris[overlap[i].indexA][0]->f, + em->looptris[overlap[i].indexB][0]->f, + }; + for (int j = 0; j < 2; j++) { + BMFace *f_hit = f_hit_pair[j]; + BMLoop *l_first = BM_FACE_FIRST_LOOP(f_hit); + int l_index = BM_elem_index_get(l_first); + for (int k = 0; k < f_hit->len; k++, l_index++) { + r_intersect[l_index] = 1.0f; + } + } + } + MEM_freeN(overlap); + } + + BKE_bmbvh_free(bmtree); + } + else { + uint overlap_len; + BVHTreeFromMesh treeData = {NULL}; + + BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4); + + struct BVHTree_OverlapData data = { + .me = mr->me, .mlooptri = mr->mlooptri, .epsilon = BLI_bvhtree_get_epsilon(tree)}; + + BVHTreeOverlap *overlap = BLI_bvhtree_overlap(tree, tree, &overlap_len, bvh_overlap_cb, &data); + if (overlap) { + for (int i = 0; i < overlap_len; i++) { + const MPoly *f_hit_pair[2] = { + &mr->mpoly[mr->mlooptri[overlap[i].indexA].poly], + &mr->mpoly[mr->mlooptri[overlap[i].indexB].poly], + }; + for (int j = 0; j < 2; j++) { + const MPoly *f_hit = f_hit_pair[j]; + int l_index = f_hit->loopstart; + for (int k = 0; k < f_hit->totloop; k++, l_index++) { + r_intersect[l_index] = 1.0f; + } + } + } + MEM_freeN(overlap); + } + } +} + +BLI_INLINE float distort_remap(float fac, float min, float UNUSED(max), float minmax_irange) +{ + if (fac >= min) { + fac = (fac - min) * minmax_irange; + CLAMP(fac, 0.0f, 1.0f); + } + else { + /* fallback */ + fac = -1.0f; + } + return fac; +} + +static void statvis_calc_distort(const MeshRenderData *mr, float *r_distort) +{ + BMEditMesh *em = mr->edit_bmesh; + const MeshStatVis *statvis = &mr->toolsettings->statvis; + const float min = statvis->distort_min; + const float max = statvis->distort_max; + const float minmax_irange = 1.0f / (max - min); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMIter iter; + BMesh *bm = em->bm; + BMFace *f; + + if (mr->bm_vert_coords != NULL) { + BKE_editmesh_cache_ensure_poly_normals(em, mr->edit_data); + + /* Most likely this is already valid, ensure just in case. + * Needed for #BM_loop_calc_face_normal_safe_vcos. */ + BM_mesh_elem_index_ensure(em->bm, BM_VERT); + } + + int l_index = 0; + int f_index = 0; + BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, f_index) { + float fac = -1.0f; + + if (f->len > 3) { + BMLoop *l_iter, *l_first; + + fac = 0.0f; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const float *no_face; + float no_corner[3]; + if (mr->bm_vert_coords != NULL) { + no_face = mr->bm_poly_normals[f_index]; + BM_loop_calc_face_normal_safe_vcos(l_iter, no_face, mr->bm_vert_coords, no_corner); + } + else { + no_face = f->no; + BM_loop_calc_face_normal_safe(l_iter, no_corner); + } + + /* simple way to detect (what is most likely) concave */ + if (dot_v3v3(no_face, no_corner) < 0.0f) { + negate_v3(no_corner); + } + fac = max_ff(fac, angle_normalized_v3v3(no_face, no_corner)); + + } while ((l_iter = l_iter->next) != l_first); + fac *= 2.0f; + } + + fac = distort_remap(fac, min, max, minmax_irange); + for (int i = 0; i < f->len; i++, l_index++) { + r_distort[l_index] = fac; + } + } + } + else { + const MPoly *mp = mr->mpoly; + for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { + float fac = -1.0f; + + if (mp->totloop > 3) { + float *f_no = mr->poly_normals[mp_index]; + fac = 0.0f; + + for (int i = 1; i <= mp->totloop; i++) { + const MLoop *l_prev = &mr->mloop[mp->loopstart + (i - 1) % mp->totloop]; + const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop]; + const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop]; + float no_corner[3]; + normal_tri_v3(no_corner, + mr->mvert[l_prev->v].co, + mr->mvert[l_curr->v].co, + mr->mvert[l_next->v].co); + /* simple way to detect (what is most likely) concave */ + if (dot_v3v3(f_no, no_corner) < 0.0f) { + negate_v3(no_corner); + } + fac = max_ff(fac, angle_normalized_v3v3(f_no, no_corner)); + } + fac *= 2.0f; + } + + fac = distort_remap(fac, min, max, minmax_irange); + for (int i = 0; i < mp->totloop; i++, l_index++) { + r_distort[l_index] = fac; + } + } + } +} + +BLI_INLINE float sharp_remap(float fac, float min, float UNUSED(max), float minmax_irange) +{ + /* important not '>=' */ + if (fac > min) { + fac = (fac - min) * minmax_irange; + CLAMP(fac, 0.0f, 1.0f); + } + else { + /* fallback */ + fac = -1.0f; + } + return fac; +} + +static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp) +{ + BMEditMesh *em = mr->edit_bmesh; + const MeshStatVis *statvis = &mr->toolsettings->statvis; + const float min = statvis->sharp_min; + const float max = statvis->sharp_max; + const float minmax_irange = 1.0f / (max - min); + + /* Can we avoid this extra allocation? */ + float *vert_angles = MEM_mallocN(sizeof(float) * mr->vert_len, __func__); + copy_vn_fl(vert_angles, mr->vert_len, -M_PI); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMIter iter; + BMesh *bm = em->bm; + BMFace *efa; + BMEdge *e; + /* first assign float values to verts */ + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + float angle = BM_edge_calc_face_angle_signed(e); + float *col1 = &vert_angles[BM_elem_index_get(e->v1)]; + float *col2 = &vert_angles[BM_elem_index_get(e->v2)]; + *col1 = max_ff(*col1, angle); + *col2 = max_ff(*col2, angle); + } + /* Copy vert value to loops. */ + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + int l_index = BM_elem_index_get(l_iter); + int v_index = BM_elem_index_get(l_iter->v); + r_sharp[l_index] = sharp_remap(vert_angles[v_index], min, max, minmax_irange); + } while ((l_iter = l_iter->next) != l_first); + } + } + else { + /* first assign float values to verts */ + const MPoly *mp = mr->mpoly; + + EdgeHash *eh = BLI_edgehash_new_ex(__func__, mr->edge_len); + + for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { + for (int i = 0; i < mp->totloop; i++) { + const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop]; + const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop]; + const MVert *v_curr = &mr->mvert[l_curr->v]; + const MVert *v_next = &mr->mvert[l_next->v]; + float angle; + void **pval; + bool value_is_init = BLI_edgehash_ensure_p(eh, l_curr->v, l_next->v, &pval); + if (!value_is_init) { + *pval = mr->poly_normals[mp_index]; + /* non-manifold edge, yet... */ + continue; + } + if (*pval != NULL) { + const float *f1_no = mr->poly_normals[mp_index]; + const float *f2_no = *pval; + angle = angle_normalized_v3v3(f1_no, f2_no); + angle = is_edge_convex_v3(v_curr->co, v_next->co, f1_no, f2_no) ? angle : -angle; + /* Tag as manifold. */ + *pval = NULL; + } + else { + /* non-manifold edge */ + angle = DEG2RADF(90.0f); + } + float *col1 = &vert_angles[l_curr->v]; + float *col2 = &vert_angles[l_next->v]; + *col1 = max_ff(*col1, angle); + *col2 = max_ff(*col2, angle); + } + } + /* Remaining non manifold edges. */ + EdgeHashIterator *ehi = BLI_edgehashIterator_new(eh); + for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { + if (BLI_edgehashIterator_getValue(ehi) != NULL) { + uint v1, v2; + const float angle = DEG2RADF(90.0f); + BLI_edgehashIterator_getKey(ehi, &v1, &v2); + float *col1 = &vert_angles[v1]; + float *col2 = &vert_angles[v2]; + *col1 = max_ff(*col1, angle); + *col2 = max_ff(*col2, angle); + } + } + BLI_edgehashIterator_free(ehi); + BLI_edgehash_free(eh, NULL); + + const MLoop *ml = mr->mloop; + for (int l_index = 0; l_index < mr->loop_len; l_index++, ml++) { + r_sharp[l_index] = sharp_remap(vert_angles[ml->v], min, max, minmax_irange); + } + } + + MEM_freeN(vert_angles); +} + +static void extract_analysis_iter_finish_mesh(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(data)) +{ + GPUVertBuf *vbo = buf; + BLI_assert(mr->edit_bmesh); + + float *l_weight = (float *)GPU_vertbuf_get_data(vbo); + + switch (mr->toolsettings->statvis.type) { + case SCE_STATVIS_OVERHANG: + statvis_calc_overhang(mr, l_weight); + break; + case SCE_STATVIS_THICKNESS: + statvis_calc_thickness(mr, l_weight); + break; + case SCE_STATVIS_INTERSECT: + statvis_calc_intersect(mr, l_weight); + break; + case SCE_STATVIS_DISTORT: + statvis_calc_distort(mr, l_weight); + break; + case SCE_STATVIS_SHARP: + statvis_calc_sharp(mr, l_weight); + break; + } +} + +const MeshExtract extract_mesh_analysis = { + .init = extract_mesh_analysis_init, + .finish = extract_analysis_iter_finish_mesh, + /* This is not needed for all visualization types. + * * Maybe split into different extract. */ + .data_flag = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots positions + * \{ */ + +static void *extract_fdots_pos_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + return GPU_vertbuf_get_data(vbo); +} + +static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int f_index, + void *data) +{ + float(*center)[3] = data; + + float *co = center[f_index]; + zero_v3(co); + + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + add_v3_v3(co, bm_vert_co_get(mr, l_iter->v)); + } while ((l_iter = l_iter->next) != l_first); + mul_v3_fl(co, 1.0f / (float)f->len); +} + +static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *data) +{ + float(*center)[3] = (float(*)[3])data; + float *co = center[mp_index]; + zero_v3(co); + + const MVert *mvert = mr->mvert; + const MLoop *mloop = mr->mloop; + + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (mr->use_subsurf_fdots) { + const MVert *mv = &mr->mvert[ml->v]; + if (mv->flag & ME_VERT_FACEDOT) { + copy_v3_v3(center[mp_index], mv->co); + break; + } + } + else { + const MVert *mv = &mvert[ml->v]; + add_v3_v3(center[mp_index], mv->co); + } + } + + if (!mr->use_subsurf_fdots) { + mul_v3_fl(co, 1.0f / (float)mp->totloop); + } +} + +const MeshExtract extract_fdots_pos = { + .init = extract_fdots_pos_init, + .iter_poly_bm = extract_fdots_pos_iter_poly_bm, + .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots Normal and edit flag + * \{ */ +#define NOR_AND_FLAG_DEFAULT 0 +#define NOR_AND_FLAG_SELECT 1 +#define NOR_AND_FLAG_ACTIVE -1 +#define NOR_AND_FLAG_HIDDEN -2 + +static void *extract_fdots_nor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + + return NULL; +} + +static void extract_fdots_nor_finish(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(data)) +{ + GPUVertBuf *vbo = buf; + static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; + GPUPackedNormal *nor = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo); + BMFace *efa; + + /* Quicker than doing it for each loop. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + for (int f = 0; f < mr->poly_len; f++) { + efa = BM_face_at_index(mr->bm, f); + const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN); + if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[f] == ORIGINDEX_NONE)) { + nor[f] = GPU_normal_convert_i10_v3(invalid_normal); + nor[f].w = NOR_AND_FLAG_HIDDEN; + } + else { + nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa)); + /* Select / Active Flag. */ + nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? + ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : + NOR_AND_FLAG_DEFAULT); + } + } + } + else { + for (int f = 0; f < mr->poly_len; f++) { + efa = bm_original_face_get(mr, f); + const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN); + if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[f] == ORIGINDEX_NONE)) { + nor[f] = GPU_normal_convert_i10_v3(invalid_normal); + nor[f].w = NOR_AND_FLAG_HIDDEN; + } + else { + nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa)); + /* Select / Active Flag. */ + nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? + ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : + NOR_AND_FLAG_DEFAULT); + } + } + } +} + +const MeshExtract extract_fdots_nor = { + .init = extract_fdots_nor_init, + .finish = extract_fdots_nor_finish, + .data_flag = MR_DATA_POLY_NOR, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots High Quality Normal and edit flag + * \{ */ +static void *extract_fdots_nor_hq_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + + return NULL; +} + +static void extract_fdots_nor_hq_finish(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(data)) +{ + GPUVertBuf *vbo = buf; + static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; + short *nor = (short *)GPU_vertbuf_get_data(vbo); + BMFace *efa; + + /* Quicker than doing it for each loop. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + for (int f = 0; f < mr->poly_len; f++) { + efa = BM_face_at_index(mr->bm, f); + const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN); + if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[f] == ORIGINDEX_NONE)) { + normal_float_to_short_v3(&nor[f * 4], invalid_normal); + nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN; + } + else { + normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa)); + /* Select / Active Flag. */ + nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? + ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : + NOR_AND_FLAG_DEFAULT); + } + } + } + else { + for (int f = 0; f < mr->poly_len; f++) { + efa = bm_original_face_get(mr, f); + const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN); + if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[f] == ORIGINDEX_NONE)) { + normal_float_to_short_v3(&nor[f * 4], invalid_normal); + nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN; + } + else { + normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa)); + /* Select / Active Flag. */ + nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? + ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : + NOR_AND_FLAG_DEFAULT); + } + } + } +} + +const MeshExtract extract_fdots_nor_hq = { + .init = extract_fdots_nor_hq_init, + .finish = extract_fdots_nor_hq_finish, + .data_flag = MR_DATA_POLY_NOR, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots UV + * \{ */ + +typedef struct MeshExtract_FdotUV_Data { + float (*vbo_data)[2]; + MLoopUV *uv_data; + int cd_ofs; +} MeshExtract_FdotUV_Data; + +static void *extract_fdots_uv_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + GPU_vertformat_alias_add(&format, "au"); + GPU_vertformat_alias_add(&format, "pos"); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + + if (!mr->use_subsurf_fdots) { + /* Clear so we can accumulate on it. */ + memset(GPU_vertbuf_get_data(vbo), 0x0, mr->poly_len * GPU_vertbuf_get_format(vbo)->stride); + } + + MeshExtract_FdotUV_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (float(*)[2])GPU_vertbuf_get_data(vbo); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); + } + else { + data->uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); + } + return data; +} + +static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_FdotUV_Data *data = _data; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + float w = 1.0f / (float)f->len; + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); + madd_v2_v2fl(data->vbo_data[BM_elem_index_get(f)], luv->uv, w); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *_data) +{ + MeshExtract_FdotUV_Data *data = _data; + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (mr->use_subsurf_fdots) { + const MVert *mv = &mr->mvert[ml->v]; + if (mv->flag & ME_VERT_FACEDOT) { + copy_v2_v2(data->vbo_data[mp_index], data->uv_data[ml_index].uv); + } + } + else { + float w = 1.0f / (float)mp->totloop; + madd_v2_v2fl(data->vbo_data[mp_index], data->uv_data[ml_index].uv, w); + } + } +} + +static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_fdots_uv = { + .init = extract_fdots_uv_init, + .iter_poly_bm = extract_fdots_uv_iter_poly_bm, + .iter_poly_mesh = extract_fdots_uv_iter_poly_mesh, + .finish = extract_fdots_uv_finish, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots Edit UV flag + * \{ */ + +typedef struct MeshExtract_EditUVFdotData_Data { + EditLoopData *vbo_data; + int cd_ofs; +} MeshExtract_EditUVFdotData_Data; + +static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "flag", GPU_COMP_U8, 4, GPU_FETCH_INT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + + MeshExtract_EditUVFdotData_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo); + data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); + return data; +} + +static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_EditUVFdotData_Data *data = _data; + EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)]; + memset(eldata, 0x0, sizeof(*eldata)); + mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); +} + +static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *UNUSED(mp), + const int mp_index, + void *_data) +{ + MeshExtract_EditUVFdotData_Data *data = _data; + EditLoopData *eldata = &data->vbo_data[mp_index]; + memset(eldata, 0x0, sizeof(*eldata)); + BMFace *efa = bm_original_face_get(mr, mp_index); + if (efa) { + mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata); + } +} + +static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_fdots_edituv_data = { + .init = extract_fdots_edituv_data_init, + .iter_poly_bm = extract_fdots_edituv_data_iter_poly_bm, + .iter_poly_mesh = extract_fdots_edituv_data_iter_poly_mesh, + .finish = extract_fdots_edituv_data_finish, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Skin Modifier Roots + * \{ */ + +typedef struct SkinRootData { + float size; + float local_pos[3]; +} SkinRootData; + +static void *extract_skin_roots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + /* Exclusively for edit mode. */ + BLI_assert(mr->bm); + + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "local_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->bm->totvert); + + SkinRootData *vbo_data = (SkinRootData *)GPU_vertbuf_get_data(vbo); + + int root_len = 0; + int cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MVERT_SKIN); + + BMIter iter; + BMVert *eve; + BM_ITER_MESH (eve, &iter, mr->bm, BM_VERTS_OF_MESH) { + const MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_ofs); + if (vs->flag & MVERT_SKIN_ROOT) { + vbo_data->size = (vs->radius[0] + vs->radius[1]) * 0.5f; + copy_v3_v3(vbo_data->local_pos, bm_vert_co_get(mr, eve)); + vbo_data++; + root_len++; + } + } + + /* It's really unlikely that all verts will be roots. Resize to avoid losing VRAM. */ + GPU_vertbuf_data_len_set(vbo, root_len); + + return NULL; +} + +const MeshExtract extract_skin_roots = { + .init = extract_skin_roots_init, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Selection Index + * \{ */ + +static void *extract_select_idx_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* TODO rename "color" to something more descriptive. */ + GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); + } + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + return GPU_vertbuf_get_data(vbo); +} + +/* TODO Use #glVertexID to get loop index and use the data structure on the CPU to retrieve the + * select element associated with this loop ID. This would remove the need for this separate + * index VBO's. We could upload the p/e/v_origindex as a buffer texture and sample it inside the + * shader to output original index. */ + +static void extract_poly_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, + void *data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = f_index; + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edge_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->e); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_vert_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->v); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edge_idx_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *data) +{ + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed); + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed); +} + +static void extract_vert_idx_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *data) +{ + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1); + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2); +} + +static void extract_vert_idx_iter_lvert_bm(const MeshRenderData *mr, + BMVert *eve, + const int lvert_index, + void *data) +{ + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve); +} + +static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *data) +{ + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + ((uint32_t *)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index; + } +} + +static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + ((uint32_t *)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e; + } +} + +static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + ((uint32_t *)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v; + } +} + +static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *UNUSED(med), + const uint ledge_index, + void *data) +{ + const int e_index = mr->ledges[ledge_index]; + const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig; +} + +static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *data) +{ + int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1; + int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig; +} + +static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr, + const MVert *UNUSED(mv), + const int lvert_index, + void *data) +{ + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + const int v_index = mr->lverts[lvert_index]; + const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index; + ((uint32_t *)data)[offset + lvert_index] = v_orig; +} + +const MeshExtract extract_poly_idx = { + .init = extract_select_idx_init, + .iter_poly_bm = extract_poly_idx_iter_poly_bm, + .iter_poly_mesh = extract_poly_idx_iter_poly_mesh, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx)}; + +const MeshExtract extract_edge_idx = { + .init = extract_select_idx_init, + .iter_poly_bm = extract_edge_idx_iter_poly_bm, + .iter_poly_mesh = extract_edge_idx_iter_poly_mesh, + .iter_ledge_bm = extract_edge_idx_iter_ledge_bm, + .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx)}; + +const MeshExtract extract_vert_idx = { + .init = extract_select_idx_init, + .iter_poly_bm = extract_vert_idx_iter_poly_bm, + .iter_poly_mesh = extract_vert_idx_iter_poly_mesh, + .iter_ledge_bm = extract_vert_idx_iter_ledge_bm, + .iter_ledge_mesh = extract_vert_idx_iter_ledge_mesh, + .iter_lvert_bm = extract_vert_idx_iter_lvert_bm, + .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx)}; + +static void *extract_fdot_idx_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* TODO rename "color" to something more descriptive. */ + GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + return GPU_vertbuf_get_data(vbo); +} + +static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *UNUSED(f), + const int f_index, + void *data) +{ + ((uint32_t *)data)[f_index] = f_index; +} + +static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *UNUSED(mp), + const int mp_index, + void *data) +{ + if (mr->p_origindex != NULL) { + ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index]; + } + else { + ((uint32_t *)data)[mp_index] = mp_index; + } +} + +const MeshExtract extract_fdot_idx = { + .init = extract_fdot_idx_init, + .iter_poly_bm = extract_fdot_idx_iter_poly_bm, + .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)}; \ No newline at end of file diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h new file mode 100644 index 00000000000..3360b90139d --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -0,0 +1,509 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ + +#pragma once + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_editmesh.h" + +#include "draw_cache_extract.h" + +typedef enum eMRExtractType { + MR_EXTRACT_BMESH, + MR_EXTRACT_MAPPED, + MR_EXTRACT_MESH, +} eMRExtractType; + +typedef struct MeshRenderData { + eMRExtractType extract_type; + + int poly_len, edge_len, vert_len, loop_len; + int edge_loose_len; + int vert_loose_len; + int loop_loose_len; + int tri_len; + int mat_len; + + bool use_hide; + bool use_subsurf_fdots; + bool use_final_mesh; + + /** Use for #MeshStatVis calculation which use world-space coords. */ + float obmat[4][4]; + + const ToolSettings *toolsettings; + /** Edit Mesh */ + BMEditMesh *edit_bmesh; + BMesh *bm; + EditMeshData *edit_data; + + /* For deformed edit-mesh data. */ + /* Use for #ME_WRAPPER_TYPE_BMESH. */ + const float (*bm_vert_coords)[3]; + const float (*bm_vert_normals)[3]; + const float (*bm_poly_normals)[3]; + const float (*bm_poly_centers)[3]; + + int *v_origindex, *e_origindex, *p_origindex; + int crease_ofs; + int bweight_ofs; + int freestyle_edge_ofs; + int freestyle_face_ofs; + /** Mesh */ + Mesh *me; + const MVert *mvert; + const MEdge *medge; + const MLoop *mloop; + const MPoly *mpoly; + BMVert *eve_act; + BMEdge *eed_act; + BMFace *efa_act; + BMFace *efa_act_uv; + /* Data created on-demand (usually not for #BMesh based data). */ + MLoopTri *mlooptri; + float (*loop_normals)[3]; + float (*poly_normals)[3]; + int *lverts, *ledges; +} MeshRenderData; + +BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx) +{ + return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_face_at_index(mr->bm, mr->p_origindex[idx]) : + NULL; +} + +BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx) +{ + return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_edge_at_index(mr->bm, mr->e_origindex[idx]) : + NULL; +} + +BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx) +{ + return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_vert_at_index(mr->bm, mr->v_origindex[idx]) : + NULL; +} + +BLI_INLINE const float *bm_vert_co_get(const MeshRenderData *mr, const BMVert *eve) +{ + const float(*vert_coords)[3] = mr->bm_vert_coords; + if (vert_coords != NULL) { + return vert_coords[BM_elem_index_get(eve)]; + } + + UNUSED_VARS(mr); + return eve->co; +} + +BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *eve) +{ + const float(*vert_normals)[3] = mr->bm_vert_normals; + if (vert_normals != NULL) { + return vert_normals[BM_elem_index_get(eve)]; + } + + UNUSED_VARS(mr); + return eve->no; +} + +BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *efa) +{ + const float(*poly_normals)[3] = mr->bm_poly_normals; + if (poly_normals != NULL) { + return poly_normals[BM_elem_index_get(efa)]; + } + + UNUSED_VARS(mr); + return efa->no; +} + +/* TODO(jbakker): phase out batch iteration macros as they are only used once. */ +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Loop Triangles + * \{ */ + +typedef struct ExtractTriBMesh_Params { + BMLoop *(*looptris)[3]; + int tri_range[2]; +} ExtractTriBMesh_Params; +typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr, + BMLoop **elt, + const int elt_index, + void *data); + +#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elem_tri, index_tri, params) \ + CHECK_TYPE(params, const ExtractTriBMesh_Params *); \ + { \ + const int _tri_index_end = (params)->tri_range[1]; \ + BMLoop **elem_tri = (params)->looptris[(params)->tri_range[0]]; \ + for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ + index_tri += 1, elem_tri += 3) +#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END } + +typedef struct ExtractTriMesh_Params { + const MLoopTri *mlooptri; + int tri_range[2]; +} ExtractTriMesh_Params; +typedef void(ExtractTriMeshFn)(const MeshRenderData *mr, + const MLoopTri *mlt, + const int elt_index, + void *data); + +#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(elem_tri, index_tri, params) \ + CHECK_TYPE(params, const ExtractTriMesh_Params *); \ + { \ + const int _tri_index_end = (params)->tri_range[1]; \ + const MLoopTri *elem_tri = &(params)->mlooptri[(params)->tri_range[0]]; \ + for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ + index_tri += 1, elem_tri += 1) +#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Polygons, Loops + * \{ */ + +typedef struct ExtractPolyBMesh_Params { + BMLoop *(*looptris)[3]; + int poly_range[2]; +} ExtractPolyBMesh_Params; +typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr, + BMFace *f, + const int f_index, + void *data); + +#define EXTRACT_POLY_FOREACH_BM_BEGIN(elem_poly, index_poly, params, mr) \ + CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ + BMFace **_ftable = mr->bm->ftable; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + BMFace *elem_poly = _ftable[index_poly]; \ + (void)elem_poly; + +#define EXTRACT_POLY_FOREACH_BM_END \ + } \ + } + +/* Iterate over polygon and loop. */ +#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(elem_loop, index_loop, params, mr) \ + CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ + BMFace **_ftable = mr->bm->ftable; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + BMFace *elem_face = _ftable[index_poly]; \ + BMLoop *elem_loop, *l_first; \ + elem_loop = l_first = BM_FACE_FIRST_LOOP(elem_face); \ + do { \ + const int index_loop = BM_elem_index_get(elem_loop); \ + (void)index_loop; /* Quiet warning when unused. */ + +#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(elem_loop) \ + } \ + while ((elem_loop = elem_loop->next) != l_first) \ + ; \ + } \ + } + +typedef struct ExtractPolyMesh_Params { + int poly_range[2]; +} ExtractPolyMesh_Params; +typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *data); + +#define EXTRACT_POLY_FOREACH_MESH_BEGIN(elem_poly, index_poly, params, mr) \ + CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ + { \ + const MPoly *_mpoly = mr->mpoly; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + const MPoly *elem_poly = &_mpoly[index_poly]; \ + (void)elem_poly; + +#define EXTRACT_POLY_FOREACH_MESH_END \ + } \ + } + +/* Iterate over polygon and loop. */ +#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN( \ + elem_poly, index_poly, elem_loop, index_loop, params, mr) \ + CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ + { \ + const MPoly *_mpoly = mr->mpoly; \ + const MLoop *_mloop = mr->mloop; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + const MPoly *elem_poly = &_mpoly[index_poly]; \ + const int _index_end = elem_poly->loopstart + elem_poly->totloop; \ + for (int index_loop = elem_poly->loopstart; index_loop < _index_end; index_loop += 1) { \ + const MLoop *elem_loop = &_mloop[index_loop]; \ + (void)elem_loop; + +#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END \ + } \ + } \ + } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Loose Edges + * \{ */ + +typedef struct ExtractLEdgeBMesh_Params { + const int *ledge; + int ledge_range[2]; +} ExtractLEdgeBMesh_Params; +typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *data); + +#define EXTRACT_LEDGE_FOREACH_BM_BEGIN(elem_edge, index_ledge, params) \ + CHECK_TYPE(params, const ExtractLEdgeBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_EDGE) == 0); \ + BMEdge **_etable = mr->bm->etable; \ + const int *_ledge = (params)->ledge; \ + const int _ledge_index_end = (params)->ledge_range[1]; \ + for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ + index_ledge += 1) { \ + BMEdge *elem_edge = _etable[_ledge[index_ledge]]; \ + (void)elem_edge; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LEDGE_FOREACH_BM_END \ + } \ + } \ + } + +typedef struct ExtractLEdgeMesh_Params { + const int *ledge; + int ledge_range[2]; +} ExtractLEdgeMesh_Params; +typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *data); + +#define EXTRACT_LEDGE_FOREACH_MESH_BEGIN(elem_edge, index_ledge, params, mr) \ + CHECK_TYPE(params, const ExtractLEdgeMesh_Params *); \ + { \ + const MEdge *_medge = mr->medge; \ + const int *_ledge = (params)->ledge; \ + const int _ledge_index_end = (params)->ledge_range[1]; \ + for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ + index_ledge += 1) { \ + const MEdge *elem_edge = &_medge[_ledge[index_ledge]]; \ + (void)elem_edge; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LEDGE_FOREACH_MESH_END \ + } \ + } \ + } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Loose Vertices + * \{ */ + +typedef struct ExtractLVertBMesh_Params { + const int *lvert; + int lvert_range[2]; +} ExtractLVertBMesh_Params; +typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr, + BMVert *eve, + const int lvert_index, + void *data); + +#define EXTRACT_LVERT_FOREACH_BM_BEGIN(elem_vert, index_lvert, params) \ + CHECK_TYPE(params, const ExtractLVertBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ + BMVert **vtable = mr->bm->vtable; \ + const int *lverts = (params)->lvert; \ + const int _lvert_index_end = (params)->lvert_range[1]; \ + for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ + index_lvert += 1) { \ + BMVert *elem_vert = vtable[lverts[index_lvert]]; \ + (void)elem_vert; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LVERT_FOREACH_BM_END \ + } \ + } \ + } + +typedef struct ExtractLVertMesh_Params { + const int *lvert; + int lvert_range[2]; +} ExtractLVertMesh_Params; +typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr, + const MVert *mv, + const int lvert_index, + void *data); + +#define EXTRACT_LVERT_FOREACH_MESH_BEGIN(elem, index_lvert, params, mr) \ + CHECK_TYPE(params, const ExtractLVertMesh_Params *); \ + { \ + const MVert *mvert = mr->mvert; \ + const int *lverts = (params)->lvert; \ + const int _lvert_index_end = (params)->lvert_range[1]; \ + for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ + index_lvert += 1) { \ + const MVert *elem = &mvert[lverts[index_lvert]]; \ + (void)elem; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LVERT_FOREACH_MESH_END \ + } \ + } \ + } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract Struct + * \{ */ + +typedef void *(ExtractInitFn)(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer); +typedef void(ExtractFinishFn)(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer, + void *data); + +typedef struct MeshExtract { + /** Executed on main thread and return user data for iteration functions. */ + ExtractInitFn *init; + /** Executed on one (or more if use_threading) worker thread(s). */ + ExtractTriBMeshFn *iter_looptri_bm; + ExtractTriMeshFn *iter_looptri_mesh; + ExtractPolyBMeshFn *iter_poly_bm; + ExtractPolyMeshFn *iter_poly_mesh; + ExtractLEdgeBMeshFn *iter_ledge_bm; + ExtractLEdgeMeshFn *iter_ledge_mesh; + ExtractLVertBMeshFn *iter_lvert_bm; + ExtractLVertMeshFn *iter_lvert_mesh; + /** Executed on one worker thread after all elements iterations. */ + ExtractFinishFn *finish; + /** Used to request common data. */ + const eMRDataType data_flag; + /** Used to know if the element callbacks are thread-safe and can be parallelized. */ + const bool use_threading; + /** + * Offset in bytes of the buffer inside a MeshBufferCache instance. Points to a vertex or index + * buffer. + */ + const size_t mesh_buffer_offset; +} MeshExtract; + +/** \} */ + +/* draw_cache_extract_mesh_render_data.c */ +MeshRenderData *mesh_render_data_create(Mesh *me, + MeshBufferExtractionCache *cache, + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const DRW_MeshCDMask *cd_used, + const ToolSettings *ts, + const eMRIterType iter_type); +void mesh_render_data_free(MeshRenderData *mr); +void mesh_render_data_update_normals(MeshRenderData *mr, + const eMRIterType iter_type, + const eMRDataType data_flag); +void mesh_render_data_update_looptris(MeshRenderData *mr, + const eMRIterType iter_type, + const eMRDataType data_flag); + +/* draw_cache_extract_mesh_extractors.c */ +void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc); +eMRIterType mesh_extract_iter_type(const MeshExtract *ext); +const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, + const bool do_hq_normals, + const bool do_lines_loose_subbuffer); +/* + * Total number of extractions types. + */ +#define M_EXTRACT_LEN 38 + +extern const MeshExtract extract_tris; +extern const MeshExtract extract_lines; +extern const MeshExtract extract_lines_with_lines_loose; +extern const MeshExtract extract_points; +extern const MeshExtract extract_fdots; +extern const MeshExtract extract_lines_paint_mask; +extern const MeshExtract extract_lines_adjacency; +extern const MeshExtract extract_edituv_tris; +extern const MeshExtract extract_edituv_lines; +extern const MeshExtract extract_edituv_points; +extern const MeshExtract extract_edituv_fdots; +extern const MeshExtract extract_pos_nor; +extern const MeshExtract extract_pos_nor_hq; +extern const MeshExtract extract_lnor_hq; +extern const MeshExtract extract_lnor; +extern const MeshExtract extract_uv; +extern const MeshExtract extract_tan; +extern const MeshExtract extract_tan_hq; +extern const MeshExtract extract_sculpt_data; +extern const MeshExtract extract_vcol; +extern const MeshExtract extract_orco; +extern const MeshExtract extract_edge_fac; +extern const MeshExtract extract_weights; +extern const MeshExtract extract_edit_data; +extern const MeshExtract extract_edituv_data; +extern const MeshExtract extract_edituv_stretch_area; +extern const MeshExtract extract_edituv_stretch_angle; +extern const MeshExtract extract_mesh_analysis; +extern const MeshExtract extract_fdots_pos; +extern const MeshExtract extract_fdots_nor; +extern const MeshExtract extract_fdots_nor_hq; +extern const MeshExtract extract_fdots_uv; +extern const MeshExtract extract_fdots_edituv_data; +extern const MeshExtract extract_skin_roots; +extern const MeshExtract extract_poly_idx; +extern const MeshExtract extract_edge_idx; +extern const MeshExtract extract_vert_idx; +extern const MeshExtract extract_fdot_idx; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c new file mode 100644 index 00000000000..56b31a36fe5 --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c @@ -0,0 +1,374 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_bitmap.h" +#include "BLI_math.h" + +#include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" +#include "BKE_mesh.h" + +#include "GPU_batch.h" + +#include "ED_mesh.h" + +#include "draw_cache_extract_mesh_private.h" + +/* ---------------------------------------------------------------------- */ +/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). + * \{ */ + +static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtractionCache *cache) +{ + mr->ledges = cache->ledges; + mr->lverts = cache->lverts; + mr->vert_loose_len = cache->vert_loose_len; + mr->edge_loose_len = cache->edge_loose_len; + + mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2); +} + +static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr, + MeshBufferExtractionCache *cache) +{ + /* Early exit: Are loose geometry already available. Only checking for loose verts as loose edges + * and verts are calculated at the same time.*/ + if (cache->lverts) { + return; + } + + cache->vert_loose_len = 0; + cache->edge_loose_len = 0; + + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + + BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); + + cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); + const MEdge *med = mr->medge; + for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { + if (med->flag & ME_LOOSEEDGE) { + cache->ledges[cache->edge_loose_len++] = med_index; + } + /* Tag verts as not loose. */ + BLI_BITMAP_ENABLE(lvert_map, med->v1); + BLI_BITMAP_ENABLE(lvert_map, med->v2); + } + if (cache->edge_loose_len < mr->edge_len) { + cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); + } + + cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); + for (int v = 0; v < mr->vert_len; v++) { + if (!BLI_BITMAP_TEST(lvert_map, v)) { + cache->lverts[cache->vert_loose_len++] = v; + } + } + if (cache->vert_loose_len < mr->vert_len) { + cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); + } + + MEM_freeN(lvert_map); + } + else { + /* #BMesh */ + BMesh *bm = mr->bm; + int elem_id; + BMIter iter; + BMVert *eve; + BMEdge *ede; + + cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*cache->lverts), __func__); + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { + if (eve->e == NULL) { + cache->lverts[cache->vert_loose_len++] = elem_id; + } + } + if (cache->vert_loose_len < mr->vert_len) { + cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); + } + + cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); + BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { + if (ede->l == NULL) { + cache->ledges[cache->edge_loose_len++] = elem_id; + } + } + if (cache->edge_loose_len < mr->edge_len) { + cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); + } + } +} + +/** + * Part of the creation of the #MeshRenderData that happens in a thread. + */ +void mesh_render_data_update_looptris(MeshRenderData *mr, + const eMRIterType iter_type, + const eMRDataType data_flag) +{ + Mesh *me = mr->me; + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { + mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI"); + BKE_mesh_recalc_looptri( + me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri); + } + } + else { + /* #BMesh */ + if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { + /* Edit mode ensures this is valid, no need to calculate. */ + BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL)); + } + } +} + +void mesh_render_data_update_normals(MeshRenderData *mr, + const eMRIterType UNUSED(iter_type), + const eMRDataType data_flag) +{ + Mesh *me = mr->me; + const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0; + const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI; + + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) { + mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__); + BKE_mesh_calc_normals_poly((MVert *)mr->mvert, + NULL, + mr->vert_len, + mr->mloop, + mr->mpoly, + mr->loop_len, + mr->poly_len, + mr->poly_normals, + true); + } + if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { + mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); + short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL); + BKE_mesh_normals_loop_split(mr->me->mvert, + mr->vert_len, + mr->me->medge, + mr->edge_len, + mr->me->mloop, + mr->loop_normals, + mr->loop_len, + mr->me->mpoly, + mr->poly_normals, + mr->poly_len, + is_auto_smooth, + split_angle, + NULL, + clnors, + NULL); + } + } + else { + /* #BMesh */ + if (data_flag & MR_DATA_POLY_NOR) { + /* Use #BMFace.no instead. */ + } + if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { + + const float(*vert_coords)[3] = NULL; + const float(*vert_normals)[3] = NULL; + const float(*poly_normals)[3] = NULL; + + if (mr->edit_data && mr->edit_data->vertexCos) { + vert_coords = mr->bm_vert_coords; + vert_normals = mr->bm_vert_normals; + poly_normals = mr->bm_poly_normals; + } + + mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); + const int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL); + BM_loops_calc_normal_vcos(mr->bm, + vert_coords, + vert_normals, + poly_normals, + is_auto_smooth, + split_angle, + mr->loop_normals, + NULL, + NULL, + clnors_offset, + false); + } + } +} + +/** + * \param is_mode_active: When true, use the modifiers from the edit-data, + * otherwise don't use modifiers as they are not from this object. + */ +MeshRenderData *mesh_render_data_create(Mesh *me, + MeshBufferExtractionCache *cache, + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const DRW_MeshCDMask *UNUSED(cd_used), + const ToolSettings *ts, + const eMRIterType iter_type) +{ + MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); + mr->toolsettings = ts; + mr->mat_len = mesh_render_mat_len_get(me); + + copy_m4_m4(mr->obmat, obmat); + + if (is_editmode) { + BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final); + mr->bm = me->edit_mesh->bm; + mr->edit_bmesh = me->edit_mesh; + mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage; + mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL; + + if (mr->edit_data) { + EditMeshData *emd = mr->edit_data; + if (emd->vertexCos) { + BKE_editmesh_cache_ensure_vert_normals(mr->edit_bmesh, emd); + BKE_editmesh_cache_ensure_poly_normals(mr->edit_bmesh, emd); + } + + mr->bm_vert_coords = mr->edit_data->vertexCos; + mr->bm_vert_normals = mr->edit_data->vertexNos; + mr->bm_poly_normals = mr->edit_data->polyNos; + mr->bm_poly_centers = mr->edit_data->polyCos; + } + + bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); + bool use_mapped = is_mode_active && + (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original); + + int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; + + BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types); + BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP); + + mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false); + mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true); + mr->eed_act = BM_mesh_active_edge_get(mr->bm); + mr->eve_act = BM_mesh_active_vert_get(mr->bm); + + mr->crease_ofs = CustomData_get_offset(&mr->bm->edata, CD_CREASE); + mr->bweight_ofs = CustomData_get_offset(&mr->bm->edata, CD_BWEIGHT); +#ifdef WITH_FREESTYLE + mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE); + mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE); +#endif + + if (use_mapped) { + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + + use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); + } + + mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_BMESH; + + /* Seems like the mesh_eval_final do not have the right origin indices. + * Force not mapped in this case. */ + if (has_mdata && do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) { + // mr->edit_bmesh = NULL; + mr->extract_type = MR_EXTRACT_MESH; + } + } + else { + mr->me = me; + mr->edit_bmesh = NULL; + + bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original; + if (use_mapped) { + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + + use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); + } + + mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_MESH; + } + + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + mr->vert_len = mr->me->totvert; + mr->edge_len = mr->me->totedge; + mr->loop_len = mr->me->totloop; + mr->poly_len = mr->me->totpoly; + mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); + + mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT); + mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE); + mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP); + mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY); + + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + } + else { + /* #BMesh */ + BMesh *bm = mr->bm; + + mr->vert_len = bm->totvert; + mr->edge_len = bm->totedge; + mr->loop_len = bm->totloop; + mr->poly_len = bm->totface; + mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); + } + + if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { + mesh_render_data_loose_geom_ensure(mr, cache); + mesh_render_data_loose_geom_load(mr, cache); + } + + return mr; +} + +void mesh_render_data_free(MeshRenderData *mr) +{ + MEM_SAFE_FREE(mr->mlooptri); + MEM_SAFE_FREE(mr->poly_normals); + MEM_SAFE_FREE(mr->loop_normals); + + /* Loose geometry are owned by MeshBufferExtractionCache. */ + mr->ledges = NULL; + mr->lverts = NULL; + + MEM_freeN(mr); +} + +/** \} */ \ No newline at end of file diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 4de97411059..ea3d921f2c5 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -42,6 +42,7 @@ struct SpaceImage; struct ToolSettings; struct ViewLayer; struct bNode; +struct bNodeTree; struct wmKeyConfig; /* uvedit_ops.c */ -- cgit v1.2.3 From 9adfd278f7487798f1b0124c7e44cf9934b4ba54 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 1 Jun 2021 10:25:38 +0200 Subject: Compositor: Full-frame base system This patch adds the base code needed to make the full-frame system work for both current tiled/per-pixel implementation of operations and full-frame. Two execution models: - Tiled: Current implementation. Renders execution groups in tiles from outputs to input. Not all operations are buffered. Runs the tiled/per-pixel implementation. - FullFrame: All operations are buffered. Fully renders operations from inputs to outputs. Runs full-frame implementation of operations if available otherwise the current tiled/per-pixel. Creates output buffers on first read and free them as soon as all its readers have finished, reducing peak memory usage of complex/long trees. Operations are multi-threaded but do not run in parallel as Tiled (will be done in another patch). This should allow us to convert operations to full-frame in small steps with the system already working and solve the problem of high memory usage. FullFrame breaking changes respect Tiled system, mainly: - Translate, Rotate, Scale, and Transform take effect immediately instead of next buffered operation. - Any sampling is always done over inputs instead of last buffered operation. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D11113 --- release/scripts/startup/bl_ui/space_node.py | 4 + release/scripts/startup/bl_ui/space_userpref.py | 1 + source/blender/compositor/CMakeLists.txt | 12 + source/blender/compositor/COM_defines.h | 10 + .../compositor/intern/COM_BufferOperation.cc | 65 ++++ .../compositor/intern/COM_BufferOperation.h | 37 +++ source/blender/compositor/intern/COM_CPUDevice.cc | 23 +- .../compositor/intern/COM_CompositorContext.cc | 18 ++ .../compositor/intern/COM_CompositorContext.h | 5 + source/blender/compositor/intern/COM_Debug.cc | 37 ++- source/blender/compositor/intern/COM_Debug.h | 2 +- source/blender/compositor/intern/COM_Enums.h | 15 + .../compositor/intern/COM_ExecutionGroup.cc | 1 + .../compositor/intern/COM_ExecutionModel.cc | 48 +++ .../blender/compositor/intern/COM_ExecutionModel.h | 84 ++++++ .../compositor/intern/COM_ExecutionSystem.cc | 143 ++------- .../compositor/intern/COM_ExecutionSystem.h | 25 +- .../intern/COM_FullFrameExecutionModel.cc | 328 +++++++++++++++++++++ .../intern/COM_FullFrameExecutionModel.h | 89 ++++++ .../intern/COM_MultiThreadedOperation.cc | 26 ++ .../compositor/intern/COM_MultiThreadedOperation.h | 73 +++++ .../blender/compositor/intern/COM_NodeOperation.cc | 176 +++++++++++ .../blender/compositor/intern/COM_NodeOperation.h | 66 +++++ .../compositor/intern/COM_NodeOperationBuilder.cc | 12 +- .../intern/COM_SharedOperationBuffers.cc | 131 ++++++++ .../compositor/intern/COM_SharedOperationBuffers.h | 71 +++++ .../compositor/intern/COM_TiledExecutionModel.cc | 158 ++++++++++ .../compositor/intern/COM_TiledExecutionModel.h | 54 ++++ source/blender/compositor/intern/COM_WorkPackage.h | 13 + .../blender/compositor/intern/COM_WorkScheduler.cc | 13 +- .../blender/compositor/intern/COM_WorkScheduler.h | 2 + .../blender/compositor/nodes/COM_TranslateNode.cc | 5 +- source/blender/makesdna/DNA_node_types.h | 9 +- source/blender/makesdna/DNA_userdef_types.h | 3 +- source/blender/makesrna/intern/rna_nodetree.c | 20 ++ source/blender/makesrna/intern/rna_userdef.c | 8 + 36 files changed, 1629 insertions(+), 158 deletions(-) create mode 100644 source/blender/compositor/intern/COM_BufferOperation.cc create mode 100644 source/blender/compositor/intern/COM_BufferOperation.h create mode 100644 source/blender/compositor/intern/COM_ExecutionModel.cc create mode 100644 source/blender/compositor/intern/COM_ExecutionModel.h create mode 100644 source/blender/compositor/intern/COM_FullFrameExecutionModel.cc create mode 100644 source/blender/compositor/intern/COM_FullFrameExecutionModel.h create mode 100644 source/blender/compositor/intern/COM_MultiThreadedOperation.cc create mode 100644 source/blender/compositor/intern/COM_MultiThreadedOperation.h create mode 100644 source/blender/compositor/intern/COM_SharedOperationBuffers.cc create mode 100644 source/blender/compositor/intern/COM_SharedOperationBuffers.h create mode 100644 source/blender/compositor/intern/COM_TiledExecutionModel.cc create mode 100644 source/blender/compositor/intern/COM_TiledExecutionModel.h diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 89ce742b81e..1208ca0a64a 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -660,8 +660,12 @@ class NODE_PT_quality(bpy.types.Panel): snode = context.space_data tree = snode.node_tree + prefs = bpy.context.preferences col = layout.column() + if prefs.experimental.use_full_frame_compositor: + col.prop(tree, "execution_mode") + col.prop(tree, "render_quality", text="Render") col.prop(tree, "edit_quality", text="Edit") col.prop(tree, "chunk_size") diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index de78b88c0f6..d85fe16d654 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2256,6 +2256,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): context, ( ({"property": "use_new_hair_type"}, "T68981"), ({"property": "use_new_point_cloud_type"}, "T75717"), + ({"property": "use_full_frame_compositor"}, "T88150"), ), ) diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 65391794c12..ac59d832013 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -49,6 +49,8 @@ set(SRC COM_compositor.h COM_defines.h + intern/COM_BufferOperation.cc + intern/COM_BufferOperation.h intern/COM_CPUDevice.cc intern/COM_CPUDevice.h intern/COM_ChunkOrder.cc @@ -66,14 +68,20 @@ set(SRC intern/COM_Enums.cc intern/COM_ExecutionGroup.cc intern/COM_ExecutionGroup.h + intern/COM_ExecutionModel.cc + intern/COM_ExecutionModel.h intern/COM_ExecutionSystem.cc intern/COM_ExecutionSystem.h + intern/COM_FullFrameExecutionModel.cc + intern/COM_FullFrameExecutionModel.h intern/COM_MemoryBuffer.cc intern/COM_MemoryBuffer.h intern/COM_MemoryProxy.cc intern/COM_MemoryProxy.h intern/COM_MetaData.cc intern/COM_MetaData.h + intern/COM_MultiThreadedOperation.cc + intern/COM_MultiThreadedOperation.h intern/COM_Node.cc intern/COM_Node.h intern/COM_NodeConverter.cc @@ -86,8 +94,12 @@ set(SRC intern/COM_NodeOperationBuilder.h intern/COM_OpenCLDevice.cc intern/COM_OpenCLDevice.h + intern/COM_SharedOperationBuffers.cc + intern/COM_SharedOperationBuffers.h intern/COM_SingleThreadedOperation.cc intern/COM_SingleThreadedOperation.h + intern/COM_TiledExecutionModel.cc + intern/COM_TiledExecutionModel.h intern/COM_WorkPackage.cc intern/COM_WorkPackage.h intern/COM_WorkScheduler.cc diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index ef889807030..5a52d216117 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -20,6 +20,16 @@ namespace blender::compositor { +enum class eExecutionModel { + /** + * Operations are executed from outputs to inputs grouped in execution groups and rendered + * in tiles. + */ + Tiled, + /** Operations are fully rendered in order from inputs to outputs. */ + FullFrame +}; + /** * \brief possible data types for sockets * \ingroup Model diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc new file mode 100644 index 00000000000..c1e64142443 --- /dev/null +++ b/source/blender/compositor/intern/COM_BufferOperation.cc @@ -0,0 +1,65 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_BufferOperation.h" + +namespace blender::compositor { + +BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) : NodeOperation() +{ + buffer_ = buffer; + /* TODO: Implement a MemoryBuffer get_size() method returning a Size2d type. Shorten following + * code to: set_resolution(buffer.get_size()) */ + unsigned int resolution[2]; + resolution[0] = buffer->getWidth(); + resolution[1] = buffer->getHeight(); + setResolution(resolution); + addOutputSocket(data_type); +} + +void *BufferOperation::initializeTileData(rcti * /*rect*/) +{ + return buffer_; +} + +void BufferOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +{ + switch (sampler) { + case PixelSampler::Nearest: + buffer_->read(output, x, y); + break; + case PixelSampler::Bilinear: + default: + buffer_->readBilinear(output, x, y); + break; + case PixelSampler::Bicubic: + /* No bicubic. Same implementation as ReadBufferOperation. */ + buffer_->readBilinear(output, x, y); + break; + } +} + +void BufferOperation::executePixelFiltered( + float output[4], float x, float y, float dx[2], float dy[2]) +{ + const float uv[2] = {x, y}; + const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}}; + buffer_->readEWA(output, uv, deriv); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_BufferOperation.h b/source/blender/compositor/intern/COM_BufferOperation.h new file mode 100644 index 00000000000..f87cd4db94e --- /dev/null +++ b/source/blender/compositor/intern/COM_BufferOperation.h @@ -0,0 +1,37 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_NodeOperation.h" + +namespace blender::compositor { + +class BufferOperation : public NodeOperation { + private: + MemoryBuffer *buffer_; + + public: + BufferOperation(MemoryBuffer *buffer, DataType data_type); + + void *initializeTileData(rcti *rect) override; + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_CPUDevice.cc b/source/blender/compositor/intern/COM_CPUDevice.cc index 29a82bec636..2ca5557e278 100644 --- a/source/blender/compositor/intern/COM_CPUDevice.cc +++ b/source/blender/compositor/intern/COM_CPUDevice.cc @@ -30,11 +30,24 @@ CPUDevice::CPUDevice(int thread_id) : m_thread_id(thread_id) void CPUDevice::execute(WorkPackage *work_package) { - const unsigned int chunkNumber = work_package->chunk_number; - ExecutionGroup *executionGroup = work_package->execution_group; - - executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber); - executionGroup->finalizeChunkExecution(chunkNumber, nullptr); + switch (work_package->type) { + case eWorkPackageType::Tile: { + const unsigned int chunkNumber = work_package->chunk_number; + ExecutionGroup *executionGroup = work_package->execution_group; + + executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber); + executionGroup->finalizeChunkExecution(chunkNumber, nullptr); + break; + } + case eWorkPackageType::CustomFunction: { + work_package->execute_fn(); + break; + } + } + + if (work_package->executed_fn) { + work_package->executed_fn(); + } } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc index f70f3a8ebfc..61e299c045e 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.cc +++ b/source/blender/compositor/intern/COM_CompositorContext.cc @@ -21,6 +21,7 @@ #include #include "BLI_assert.h" +#include "DNA_userdef_types.h" namespace blender::compositor { @@ -33,6 +34,7 @@ CompositorContext::CompositorContext() this->m_fastCalculation = false; this->m_viewSettings = nullptr; this->m_displaySettings = nullptr; + this->m_bnodetree = nullptr; } int CompositorContext::getFramenumber() const @@ -41,4 +43,20 @@ int CompositorContext::getFramenumber() const return m_rd->cfra; } +eExecutionModel CompositorContext::get_execution_model() const +{ + if (U.experimental.use_full_frame_compositor) { + BLI_assert(m_bnodetree != nullptr); + switch (m_bnodetree->execution_mode) { + case 1: + return eExecutionModel::FullFrame; + case 0: + return eExecutionModel::Tiled; + default: + BLI_assert(!"Invalid execution mode"); + } + } + return eExecutionModel::Tiled; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h index e6164246bdd..56251511576 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.h +++ b/source/blender/compositor/intern/COM_CompositorContext.h @@ -281,6 +281,11 @@ class CompositorContext { { return m_rd->size * 0.01f; } + + /** + * Get active execution model. + */ + eExecutionModel get_execution_model() const; }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index dfb4f53fee5..4cf7e09a7d8 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -211,12 +211,14 @@ int DebugInfo::graphviz_legend_group( return len; } -int DebugInfo::graphviz_legend(char *str, int maxlen) +int DebugInfo::graphviz_legend(char *str, int maxlen, const bool has_execution_groups) { int len = 0; len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{\r\n"); - len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n"); + if (has_execution_groups) { + len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n"); + } len += snprintf( str + len, maxlen > len ? maxlen - len : 0, "Legend [shape=none, margin=0, label=<\r\n"); @@ -236,21 +238,24 @@ int DebugInfo::graphviz_legend(char *str, int maxlen) "Viewer", "lightskyblue3", str + len, maxlen > len ? maxlen - len : 0); len += graphviz_legend_color( "Active Viewer", "lightskyblue1", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_color( - "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_color( - "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0); + if (has_execution_groups) { + len += graphviz_legend_color( + "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0); + len += graphviz_legend_color( + "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0); + } len += graphviz_legend_color( "Input Value", "khaki1", str + len, maxlen > len ? maxlen - len : 0); - len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n"); - - len += graphviz_legend_group( - "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_group( - "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_group( - "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0); + if (has_execution_groups) { + len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n"); + len += graphviz_legend_group( + "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0); + len += graphviz_legend_group( + "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0); + len += graphviz_legend_group( + "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0); + } len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n"); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, ">];\r\n"); @@ -387,7 +392,9 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma } } - len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0); + const bool has_execution_groups = system->getContext().get_execution_model() == + eExecutionModel::Tiled; + len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0, has_execution_groups); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n"); diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h index e1aea69e481..0de3a5e39dc 100644 --- a/source/blender/compositor/intern/COM_Debug.h +++ b/source/blender/compositor/intern/COM_Debug.h @@ -129,7 +129,7 @@ class DebugInfo { const char *name, const char *color, const char *style, char *str, int maxlen); static int graphviz_legend_group( const char *name, const char *color, const char *style, char *str, int maxlen); - static int graphviz_legend(char *str, int maxlen); + static int graphviz_legend(char *str, int maxlen, bool has_execution_groups); static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen); }; diff --git a/source/blender/compositor/intern/COM_Enums.h b/source/blender/compositor/intern/COM_Enums.h index f65ce3e856e..519e7df940e 100644 --- a/source/blender/compositor/intern/COM_Enums.h +++ b/source/blender/compositor/intern/COM_Enums.h @@ -70,6 +70,21 @@ enum class eWorkPackageState { Executed = 2, }; +/** + * \brief Work type to execute. + * \ingroup Execution + */ +enum class eWorkPackageType { + /** + * \brief Executes an execution group tile. + */ + Tile = 0, + /** + * \brief Executes a custom function. + */ + CustomFunction = 1 +}; + std::ostream &operator<<(std::ostream &os, const eCompositorPriority &priority); std::ostream &operator<<(std::ostream &os, const eWorkPackageState &execution_state); diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cc b/source/blender/compositor/intern/COM_ExecutionGroup.cc index 80d453bf7f9..68bda8c70d6 100644 --- a/source/blender/compositor/intern/COM_ExecutionGroup.cc +++ b/source/blender/compositor/intern/COM_ExecutionGroup.cc @@ -157,6 +157,7 @@ void ExecutionGroup::init_work_packages() if (this->m_chunks_len != 0) { m_work_packages.resize(this->m_chunks_len); for (unsigned int index = 0; index < m_chunks_len; index++) { + m_work_packages[index].type = eWorkPackageType::Tile; m_work_packages[index].state = eWorkPackageState::NotScheduled; m_work_packages[index].execution_group = this; m_work_packages[index].chunk_number = index; diff --git a/source/blender/compositor/intern/COM_ExecutionModel.cc b/source/blender/compositor/intern/COM_ExecutionModel.cc new file mode 100644 index 00000000000..4d7f62e091b --- /dev/null +++ b/source/blender/compositor/intern/COM_ExecutionModel.cc @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_ExecutionModel.h" + +namespace blender::compositor { + +ExecutionModel::ExecutionModel(CompositorContext &context, Span operations) + : context_(context), operations_(operations) +{ + const bNodeTree *node_tree = context_.getbNodeTree(); + + const rctf *viewer_border = &node_tree->viewer_border; + border_.use_viewer_border = (node_tree->flag & NTREE_VIEWER_BORDER) && + viewer_border->xmin < viewer_border->xmax && + viewer_border->ymin < viewer_border->ymax; + border_.viewer_border = viewer_border; + + const RenderData *rd = context_.getRenderData(); + /* Case when cropping to render border happens is handled in + * compositor output and render layer nodes. */ + border_.use_render_border = context.isRendering() && (rd->mode & R_BORDER) && + !(rd->mode & R_CROP); + border_.render_border = &rd->border; +} + +bool ExecutionModel::is_breaked() const +{ + const bNodeTree *btree = context_.getbNodeTree(); + return btree->test_break(btree->tbh); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_ExecutionModel.h b/source/blender/compositor/intern/COM_ExecutionModel.h new file mode 100644 index 00000000000..9e8466b9282 --- /dev/null +++ b/source/blender/compositor/intern/COM_ExecutionModel.h @@ -0,0 +1,84 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "BLI_rect.h" +#include "BLI_vector.hh" + +#include "COM_ExecutionSystem.h" + +#include + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +class NodeOperation; + +/** + * Base class for execution models. Contains shared implementation. + */ +class ExecutionModel { + protected: + /** + * Render and viewer border info. Coordinates are normalized. + */ + struct { + bool use_render_border; + const rctf *render_border; + bool use_viewer_border; + const rctf *viewer_border; + } border_; + + /** + * Context used during execution. + */ + CompositorContext &context_; + + /** + * All operations being executed. + */ + Span operations_; + + public: + ExecutionModel(CompositorContext &context, Span operations); + + virtual ~ExecutionModel() + { + } + + virtual void execute(ExecutionSystem &exec_system) = 0; + + virtual void execute_work(const rcti &UNUSED(work_rect), + std::function UNUSED(work_func)) + { + BLI_assert(!"Method not supported by current execution model"); + } + + protected: + bool is_breaked() const; + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:BaseExecutionModel") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc index e22dc17837b..a12ec774032 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cc +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc @@ -21,16 +21,11 @@ #include "BLI_utildefines.h" #include "PIL_time.h" -#include "BKE_node.h" - -#include "BLT_translation.h" - -#include "COM_Converter.h" #include "COM_Debug.h" -#include "COM_ExecutionGroup.h" +#include "COM_FullFrameExecutionModel.h" #include "COM_NodeOperation.h" #include "COM_NodeOperationBuilder.h" -#include "COM_ReadBufferOperation.h" +#include "COM_TiledExecutionModel.h" #include "COM_WorkScheduler.h" #ifdef WITH_CXX_GUARDEDALLOC @@ -73,41 +68,23 @@ ExecutionSystem::ExecutionSystem(RenderData *rd, builder.convertToOperations(this); } - unsigned int resolution[2]; - - rctf *viewer_border = &editingtree->viewer_border; - bool use_viewer_border = (editingtree->flag & NTREE_VIEWER_BORDER) && - viewer_border->xmin < viewer_border->xmax && - viewer_border->ymin < viewer_border->ymax; - - editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Determining resolution")); - - for (ExecutionGroup *executionGroup : m_groups) { - resolution[0] = 0; - resolution[1] = 0; - executionGroup->determineResolution(resolution); - - if (rendering) { - /* case when cropping to render border happens is handled in - * compositor output and render layer nodes - */ - if ((rd->mode & R_BORDER) && !(rd->mode & R_CROP)) { - executionGroup->setRenderBorder( - rd->border.xmin, rd->border.xmax, rd->border.ymin, rd->border.ymax); - } - } - - if (use_viewer_border) { - executionGroup->setViewerBorder( - viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax); - } + switch (m_context.get_execution_model()) { + case eExecutionModel::Tiled: + execution_model_ = new TiledExecutionModel(m_context, m_operations, m_groups); + break; + case eExecutionModel::FullFrame: + execution_model_ = new FullFrameExecutionModel(m_context, active_buffers_, m_operations); + break; + default: + BLI_assert(!"Non implemented execution model"); + break; } - - // DebugInfo::graphviz(this); } ExecutionSystem::~ExecutionSystem() { + delete execution_model_; + for (NodeOperation *operation : m_operations) { delete operation; } @@ -126,100 +103,16 @@ void ExecutionSystem::set_operations(const Vector &operations, m_groups = groups; } -static void update_read_buffer_offset(Vector &operations) -{ - unsigned int order = 0; - for (NodeOperation *operation : operations) { - if (operation->get_flags().is_read_buffer_operation) { - ReadBufferOperation *readOperation = (ReadBufferOperation *)operation; - readOperation->setOffset(order); - order++; - } - } -} - -static void init_write_operations_for_execution(Vector &operations, - const bNodeTree *bTree) -{ - for (NodeOperation *operation : operations) { - if (operation->get_flags().is_write_buffer_operation) { - operation->setbNodeTree(bTree); - operation->initExecution(); - } - } -} - -static void link_write_buffers(Vector &operations) -{ - for (NodeOperation *operation : operations) { - if (operation->get_flags().is_read_buffer_operation) { - ReadBufferOperation *readOperation = static_cast(operation); - readOperation->updateMemoryBuffer(); - } - } -} - -static void init_non_write_operations_for_execution(Vector &operations, - const bNodeTree *bTree) -{ - for (NodeOperation *operation : operations) { - if (!operation->get_flags().is_write_buffer_operation) { - operation->setbNodeTree(bTree); - operation->initExecution(); - } - } -} - -static void init_execution_groups_for_execution(Vector &groups, - const int chunk_size) -{ - for (ExecutionGroup *execution_group : groups) { - execution_group->setChunksize(chunk_size); - execution_group->initExecution(); - } -} - void ExecutionSystem::execute() { - const bNodeTree *editingtree = this->m_context.getbNodeTree(); - editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution")); - DebugInfo::execute_started(this); - update_read_buffer_offset(m_operations); - - init_write_operations_for_execution(m_operations, m_context.getbNodeTree()); - link_write_buffers(m_operations); - init_non_write_operations_for_execution(m_operations, m_context.getbNodeTree()); - init_execution_groups_for_execution(m_groups, m_context.getChunksize()); - - WorkScheduler::start(this->m_context); - execute_groups(eCompositorPriority::High); - if (!this->getContext().isFastCalculation()) { - execute_groups(eCompositorPriority::Medium); - execute_groups(eCompositorPriority::Low); - } - WorkScheduler::finish(); - WorkScheduler::stop(); - - editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution")); - - for (NodeOperation *operation : m_operations) { - operation->deinitExecution(); - } - - for (ExecutionGroup *execution_group : m_groups) { - execution_group->deinitExecution(); - } + execution_model_->execute(*this); } -void ExecutionSystem::execute_groups(eCompositorPriority priority) +void ExecutionSystem::execute_work(const rcti &work_rect, + std::function work_func) { - for (ExecutionGroup *execution_group : m_groups) { - if (execution_group->get_flags().is_output && - execution_group->getRenderPriority() == priority) { - execution_group->execute(this); - } - } + execution_model_->execute_work(work_rect, work_func); } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h index e6170c48778..e106209651c 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.h +++ b/source/blender/compositor/intern/COM_ExecutionSystem.h @@ -25,6 +25,7 @@ class ExecutionGroup; #include "COM_ExecutionGroup.h" #include "COM_Node.h" #include "COM_NodeOperation.h" +#include "COM_SharedOperationBuffers.h" #include "DNA_color_types.h" #include "DNA_node_types.h" @@ -115,12 +116,20 @@ namespace blender::compositor { * \see ExecutionGroup class representing the ExecutionGroup */ +/* Forward declarations. */ +class ExecutionModel; + /** * \brief the ExecutionSystem contains the whole compositor tree. */ class ExecutionSystem { - private: + /** + * Contains operations active buffers data. Buffers will be disposed once reader operations are + * finished. + */ + SharedOperationBuffers active_buffers_; + /** * \brief the context used during execution */ @@ -136,6 +145,11 @@ class ExecutionSystem { */ Vector m_groups; + /** + * Active execution model implementation. + */ + ExecutionModel *execution_model_; + private: // methods public: /** @@ -178,9 +192,14 @@ class ExecutionSystem { return this->m_context; } - private: - void execute_groups(eCompositorPriority priority); + SharedOperationBuffers &get_active_buffers() + { + return active_buffers_; + } + + void execute_work(const rcti &work_rect, std::function work_func); + private: /* allow the DebugInfo class to look at internals */ friend class DebugInfo; diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc new file mode 100644 index 00000000000..1099aadd89d --- /dev/null +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -0,0 +1,328 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_FullFrameExecutionModel.h" +#include "COM_Debug.h" +#include "COM_ExecutionGroup.h" +#include "COM_ReadBufferOperation.h" +#include "COM_WorkScheduler.h" + +#include "BLT_translation.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context, + SharedOperationBuffers &shared_buffers, + Span operations) + : ExecutionModel(context, operations), + active_buffers_(shared_buffers), + num_operations_finished_(0), + priorities_(), + work_mutex_(), + work_finished_cond_() +{ + priorities_.append(eCompositorPriority::High); + if (!context.isFastCalculation()) { + priorities_.append(eCompositorPriority::Medium); + priorities_.append(eCompositorPriority::Low); + } + + BLI_mutex_init(&work_mutex_); + BLI_condition_init(&work_finished_cond_); +} + +FullFrameExecutionModel::~FullFrameExecutionModel() +{ + BLI_condition_end(&work_finished_cond_); + BLI_mutex_end(&work_mutex_); +} + +void FullFrameExecutionModel::execute(ExecutionSystem &exec_system) +{ + const bNodeTree *node_tree = this->context_.getbNodeTree(); + node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Initializing execution")); + + DebugInfo::graphviz(&exec_system); + + determine_areas_to_render_and_reads(); + render_operations(exec_system); +} + +void FullFrameExecutionModel::determine_areas_to_render_and_reads() +{ + const bool is_rendering = context_.isRendering(); + const bNodeTree *node_tree = context_.getbNodeTree(); + + rcti area; + for (eCompositorPriority priority : priorities_) { + for (NodeOperation *op : operations_) { + op->setbNodeTree(node_tree); + if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) { + get_output_render_area(op, area); + determine_areas_to_render(op, area); + determine_reads(op); + } + } + } +} + +void FullFrameExecutionModel::ensure_inputs_rendered(NodeOperation *op, + ExecutionSystem &exec_system) +{ + const int num_inputs = op->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = op->get_input_operation(i); + if (!active_buffers_.is_operation_rendered(input_op)) { + render_operation(input_op, exec_system); + } + } +} + +Vector FullFrameExecutionModel::get_input_buffers(NodeOperation *op) +{ + const int num_inputs = op->getNumberOfInputSockets(); + Vector inputs_buffers(num_inputs); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = op->get_input_operation(i); + inputs_buffers[i] = active_buffers_.get_rendered_buffer(input_op); + } + return inputs_buffers; +} + +MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op) +{ + rcti op_rect; + BLI_rcti_init(&op_rect, 0, op->getWidth(), 0, op->getHeight()); + + const DataType data_type = op->getOutputSocket(0)->getDataType(); + /* TODO: We should check if the operation is constant instead of is_set_operation. Finding a way + * to know if an operation is constant has to be implemented yet. */ + const bool is_a_single_elem = op->get_flags().is_set_operation; + return new MemoryBuffer(data_type, op_rect, is_a_single_elem); +} + +void FullFrameExecutionModel::render_operation(NodeOperation *op, ExecutionSystem &exec_system) +{ + if (active_buffers_.is_operation_rendered(op)) { + return; + } + + ensure_inputs_rendered(op, exec_system); + Vector input_bufs = get_input_buffers(op); + + const bool has_outputs = op->getNumberOfOutputSockets() > 0; + MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr; + Span areas = active_buffers_.get_areas_to_render(op); + op->render(op_buf, areas, input_bufs, exec_system); + active_buffers_.set_rendered_buffer(op, std::unique_ptr(op_buf)); + + operation_finished(op); +} + +/** + * Render output operations in order of priority. + */ +void FullFrameExecutionModel::render_operations(ExecutionSystem &exec_system) +{ + const bool is_rendering = context_.isRendering(); + + WorkScheduler::start(this->context_); + for (eCompositorPriority priority : priorities_) { + for (NodeOperation *op : operations_) { + if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) { + render_operation(op, exec_system); + } + } + } + WorkScheduler::stop(); +} + +/** + * Determines all input operations areas needed to render given operation area. + * \param operation: Renderer operation. + * \param render_area: Area within given operation bounds to render. + */ +void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *operation, + const rcti &render_area) +{ + if (active_buffers_.is_area_registered(operation, render_area)) { + return; + } + + active_buffers_.register_area(operation, render_area); + + const int num_inputs = operation->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = operation->get_input_operation(i); + rcti input_op_rect, input_area; + BLI_rcti_init(&input_op_rect, 0, input_op->getWidth(), 0, input_op->getHeight()); + operation->get_area_of_interest(input_op, render_area, input_area); + + /* Ensure area of interest is within operation bounds, cropping areas outside. */ + BLI_rcti_isect(&input_area, &input_op_rect, &input_area); + + determine_areas_to_render(input_op, input_area); + } +} + +/** + * Determines the reads given operation and its inputs will receive (i.e: Number of dependent + * operations each operation has). + */ +void FullFrameExecutionModel::determine_reads(NodeOperation *operation) +{ + if (active_buffers_.has_registered_reads(operation)) { + return; + } + + const int num_inputs = operation->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = operation->get_input_operation(i); + determine_reads(input_op); + active_buffers_.register_read(input_op); + } +} + +/** + * Calculates given output operation area to be rendered taking into account viewer and render + * borders. + */ +void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, rcti &r_area) +{ + BLI_assert(output_op->isOutputOperation(context_.isRendering())); + + /* By default return operation bounds (no border). */ + const int op_width = output_op->getWidth(); + const int op_height = output_op->getHeight(); + BLI_rcti_init(&r_area, 0, op_width, 0, op_height); + + const bool has_viewer_border = border_.use_viewer_border && + (output_op->get_flags().is_viewer_operation || + output_op->get_flags().is_preview_operation); + const bool has_render_border = border_.use_render_border; + if (has_viewer_border || has_render_border) { + /* Get border with normalized coordinates. */ + const rctf *norm_border = has_viewer_border ? border_.viewer_border : border_.render_border; + + /* Return de-normalized border. */ + BLI_rcti_init(&r_area, + norm_border->xmin * op_width, + norm_border->xmax * op_width, + norm_border->ymin * op_height, + norm_border->ymax * op_height); + } +} + +/** + * Multi-threadedly execute given work function passing work_rect splits as argument. + */ +void FullFrameExecutionModel::execute_work(const rcti &work_rect, + std::function work_func) +{ + if (is_breaked()) { + return; + } + + /* Split work vertically to maximize continuous memory. */ + const int work_height = BLI_rcti_size_y(&work_rect); + const int num_sub_works = MIN2(WorkScheduler::get_num_cpu_threads(), work_height); + const int split_height = num_sub_works == 0 ? 0 : work_height / num_sub_works; + int remaining_height = work_height - split_height * num_sub_works; + + Vector sub_works(num_sub_works); + int sub_work_y = work_rect.ymin; + int num_sub_works_finished = 0; + for (int i = 0; i < num_sub_works; i++) { + int sub_work_height = split_height; + + /* Distribute remaining height between sub-works. */ + if (remaining_height > 0) { + sub_work_height++; + remaining_height--; + } + + WorkPackage &sub_work = sub_works[i]; + sub_work.type = eWorkPackageType::CustomFunction; + sub_work.execute_fn = [=, &work_func, &work_rect]() { + if (is_breaked()) { + return; + } + rcti split_rect; + BLI_rcti_init( + &split_rect, work_rect.xmin, work_rect.xmax, sub_work_y, sub_work_y + sub_work_height); + work_func(split_rect); + }; + sub_work.executed_fn = [&]() { + BLI_mutex_lock(&work_mutex_); + num_sub_works_finished++; + if (num_sub_works_finished == num_sub_works) { + BLI_condition_notify_one(&work_finished_cond_); + } + BLI_mutex_unlock(&work_mutex_); + }; + WorkScheduler::schedule(&sub_work); + sub_work_y += sub_work_height; + } + BLI_assert(sub_work_y == work_rect.ymax); + + WorkScheduler::finish(); + + /* Ensure all sub-works finished. + * TODO: This a workaround for WorkScheduler::finish() not waiting all works on queue threading + * model. Sync code should be removed once it's fixed. */ + BLI_mutex_lock(&work_mutex_); + if (num_sub_works_finished < num_sub_works) { + BLI_condition_wait(&work_finished_cond_, &work_mutex_); + } + BLI_mutex_unlock(&work_mutex_); +} + +void FullFrameExecutionModel::operation_finished(NodeOperation *operation) +{ + /* Report inputs reads so that buffers may be freed/reused. */ + const int num_inputs = operation->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + active_buffers_.read_finished(operation->get_input_operation(i)); + } + + num_operations_finished_++; + update_progress_bar(); +} + +void FullFrameExecutionModel::update_progress_bar() +{ + const bNodeTree *tree = context_.getbNodeTree(); + if (tree) { + const float progress = num_operations_finished_ / static_cast(operations_.size()); + tree->progress(tree->prh, progress); + + char buf[128]; + BLI_snprintf(buf, + sizeof(buf), + TIP_("Compositing | Operation %i-%li"), + num_operations_finished_ + 1, + operations_.size()); + tree->stats_draw(tree->sdh, buf); + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h new file mode 100644 index 00000000000..2c0d5e0460a --- /dev/null +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h @@ -0,0 +1,89 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_ExecutionModel.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +/* Forward declarations. */ +class ExecutionGroup; + +/** + * Fully renders operations in order from inputs to outputs. + */ +class FullFrameExecutionModel : public ExecutionModel { + private: + /** + * Contains operations active buffers data. Buffers will be disposed once reader operations are + * finished. + */ + SharedOperationBuffers &active_buffers_; + + /** + * Number of operations finished. + */ + int num_operations_finished_; + + /** + * Order of priorities for output operations execution. + */ + Vector priorities_; + + ThreadMutex work_mutex_; + ThreadCondition work_finished_cond_; + + public: + FullFrameExecutionModel(CompositorContext &context, + SharedOperationBuffers &shared_buffers, + Span operations); + ~FullFrameExecutionModel(); + + void execute(ExecutionSystem &exec_system) override; + + void execute_work(const rcti &work_rect, + std::function work_func) override; + + private: + void determine_areas_to_render_and_reads(); + void render_operations(ExecutionSystem &exec_system); + + void ensure_inputs_rendered(NodeOperation *op, ExecutionSystem &exec_system); + Vector get_input_buffers(NodeOperation *op); + MemoryBuffer *create_operation_buffer(NodeOperation *op); + void render_operation(NodeOperation *op, ExecutionSystem &exec_system); + + void operation_finished(NodeOperation *operation); + + void get_output_render_area(NodeOperation *output_op, rcti &r_area); + void determine_areas_to_render(NodeOperation *operation, const rcti &render_area); + void determine_reads(NodeOperation *operation); + + void update_progress_bar(); + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:FullFrameExecutionModel") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc new file mode 100644 index 00000000000..c54c2edccb0 --- /dev/null +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc @@ -0,0 +1,26 @@ +#include "COM_MultiThreadedOperation.h" +#include "COM_ExecutionSystem.h" + +namespace blender::compositor { + +MultiThreadedOperation::MultiThreadedOperation() +{ + m_num_passes = 1; + flags.is_fullframe_operation = true; +} + +void MultiThreadedOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &output_area, + blender::Span inputs, + ExecutionSystem &exec_system) +{ + for (int current_pass = 0; current_pass < m_num_passes; current_pass++) { + update_memory_buffer_started(output, output_area, inputs, exec_system, current_pass); + exec_system.execute_work(output_area, [=, &exec_system](const rcti &split_rect) { + update_memory_buffer_partial(output, split_rect, inputs, exec_system, current_pass); + }); + update_memory_buffer_finished(output, output_area, inputs, exec_system, current_pass); + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.h b/source/blender/compositor/intern/COM_MultiThreadedOperation.h new file mode 100644 index 00000000000..e86b1d303f9 --- /dev/null +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.h @@ -0,0 +1,73 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_NodeOperation.h" + +namespace blender::compositor { + +class MultiThreadedOperation : public NodeOperation { + protected: + /** + * Number of execution passes. + */ + int m_num_passes; + + protected: + MultiThreadedOperation(); + + /** + * Called before an update memory buffer pass is executed. Single-threaded calls. + */ + virtual void update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(output_rect), + blender::Span UNUSED(inputs), + ExecutionSystem &UNUSED(exec_system), + int UNUSED(current_pass)) + { + } + + /** + * Executes operation updating output memory buffer on output_rect area. Multi-threaded calls. + */ + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &output_rect, + blender::Span inputs, + ExecutionSystem &exec_system, + int current_pass) = 0; + + /** + * Called after an update memory buffer pass is executed. Single-threaded calls. + */ + virtual void update_memory_buffer_finished(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(output_rect), + blender::Span UNUSED(inputs), + ExecutionSystem &UNUSED(exec_system), + int UNUSED(current_pass)) + { + } + + private: + void update_memory_buffer(MemoryBuffer *output, + const rcti &output_rect, + blender::Span inputs, + ExecutionSystem &exec_system) override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index be3ea59efa5..83de8a751c4 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -17,8 +17,10 @@ */ #include +#include #include +#include "COM_BufferOperation.h" #include "COM_ExecutionSystem.h" #include "COM_ReadBufferOperation.h" #include "COM_defines.h" @@ -175,6 +177,177 @@ bool NodeOperation::determineDependingAreaOfInterest(rcti *input, return !first; } +/* -------------------------------------------------------------------- */ +/** \name Full Frame Methods + * \{ */ + +/** + * \brief Get input operation area being read by this operation on rendering given output area. + * + * Implementation don't need to ensure r_input_area is within input operation bounds. The + * caller must clamp it. + * TODO: See if it's possible to use parameter overloading (input_id for example). + * + * \param input_op_idx: Input operation index for which we want to calculate the area being read. + * \param output_area: Area being rendered by this operation. + * \param r_input_area: Returned input operation area that needs to be read in order to render + * given output area. + */ +void NodeOperation::get_area_of_interest(const int input_op_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (get_flags().is_fullframe_operation) { + r_input_area = output_area; + } + else { + /* Non full-frame operations never implement this method. To ensure correctness assume + * whole area is used. */ + NodeOperation *input_op = getInputOperation(input_op_idx); + BLI_rcti_init(&r_input_area, 0, input_op->getWidth(), 0, input_op->getHeight()); + } +} + +void NodeOperation::get_area_of_interest(NodeOperation *input_op, + const rcti &output_area, + rcti &r_input_area) +{ + for (int i = 0; i < getNumberOfInputSockets(); i++) { + if (input_op == getInputOperation(i)) { + get_area_of_interest(i, output_area, r_input_area); + return; + } + } + BLI_assert(!"input_op is not an input operation."); +} + +/** + * Executes operation image manipulation algorithm rendering given areas. + * \param output_buf: Buffer to write result to. + * \param areas: Areas within this operation bounds to render. + * \param inputs_bufs: Inputs operations buffers. + * \param exec_system: Execution system. + */ +void NodeOperation::render(MemoryBuffer *output_buf, + Span areas, + Span inputs_bufs, + ExecutionSystem &exec_system) +{ + if (get_flags().is_fullframe_operation) { + render_full_frame(output_buf, areas, inputs_bufs, exec_system); + } + else { + render_full_frame_fallback(output_buf, areas, inputs_bufs, exec_system); + } +} + +/** + * Renders given areas using operations full frame implementation. + */ +void NodeOperation::render_full_frame(MemoryBuffer *output_buf, + Span areas, + Span inputs_bufs, + ExecutionSystem &exec_system) +{ + initExecution(); + for (const rcti &area : areas) { + update_memory_buffer(output_buf, area, inputs_bufs, exec_system); + } + deinitExecution(); +} + +/** + * Renders given areas using operations tiled implementation. + */ +void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf, + Span areas, + Span inputs_bufs, + ExecutionSystem &exec_system) +{ + Vector orig_input_links = replace_inputs_with_buffers(inputs_bufs); + + initExecution(); + const bool is_output_operation = getNumberOfOutputSockets() == 0; + if (!is_output_operation && output_buf->is_a_single_elem()) { + float *output_elem = output_buf->get_elem(0, 0); + readSampled(output_elem, 0, 0, PixelSampler::Nearest); + } + else { + for (const rcti &rect : areas) { + exec_system.execute_work(rect, [=](const rcti &split_rect) { + rcti tile_rect = split_rect; + if (is_output_operation) { + executeRegion(&tile_rect, 0); + } + else { + render_tile(output_buf, &tile_rect); + } + }); + } + } + deinitExecution(); + + remove_buffers_and_restore_original_inputs(orig_input_links); +} + +void NodeOperation::render_tile(MemoryBuffer *output_buf, rcti *tile_rect) +{ + const bool is_complex = get_flags().complex; + void *tile_data = is_complex ? initializeTileData(tile_rect) : nullptr; + const int elem_stride = output_buf->elem_stride; + for (int y = tile_rect->ymin; y < tile_rect->ymax; y++) { + float *output_elem = output_buf->get_elem(tile_rect->xmin, y); + if (is_complex) { + for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) { + read(output_elem, x, y, tile_data); + output_elem += elem_stride; + } + } + else { + for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) { + readSampled(output_elem, x, y, PixelSampler::Nearest); + output_elem += elem_stride; + } + } + } + if (tile_data) { + deinitializeTileData(tile_rect, tile_data); + } +} + +/** + * \return Replaced inputs links. + */ +Vector NodeOperation::replace_inputs_with_buffers( + Span inputs_bufs) +{ + BLI_assert(inputs_bufs.size() == getNumberOfInputSockets()); + Vector orig_links(inputs_bufs.size()); + for (int i = 0; i < inputs_bufs.size(); i++) { + NodeOperationInput *input_socket = getInputSocket(i); + BufferOperation *buffer_op = new BufferOperation(inputs_bufs[i], input_socket->getDataType()); + orig_links[i] = input_socket->getLink(); + input_socket->setLink(buffer_op->getOutputSocket()); + } + return orig_links; +} + +void NodeOperation::remove_buffers_and_restore_original_inputs( + Span original_inputs_links) +{ + BLI_assert(original_inputs_links.size() == getNumberOfInputSockets()); + for (int i = 0; i < original_inputs_links.size(); i++) { + NodeOperation *buffer_op = get_input_operation(i); + BLI_assert(buffer_op != nullptr); + BLI_assert(typeid(*buffer_op) == typeid(BufferOperation)); + NodeOperationInput *input_socket = getInputSocket(i); + input_socket->setLink(original_inputs_links[i]); + delete buffer_op; + } +} + +/** \} */ + /***************** **** OpInput **** *****************/ @@ -267,6 +440,9 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operat if (!node_operation_flags.use_datatype_conversion) { os << "no_conversion,"; } + if (node_operation_flags.is_fullframe_operation) { + os << "full_frame,"; + } return os; } diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index baf3a0878b9..01068c7f812 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -39,6 +39,7 @@ namespace blender::compositor { class OpenCLDevice; class ReadBufferOperation; class WriteBufferOperation; +class ExecutionSystem; class NodeOperation; typedef NodeOperation SocketReader; @@ -190,6 +191,10 @@ struct NodeOperationFlags { */ bool open_cl : 1; + /** + * TODO: Remove this flag and SingleThreadedOperation if tiled implemention is removed. + * Full-frame implemention doesn't need it. + */ bool single_threaded : 1; /** @@ -232,6 +237,11 @@ struct NodeOperationFlags { */ bool use_datatype_conversion : 1; + /** + * Has this operation fullframe implementation. + */ + bool is_fullframe_operation : 1; + NodeOperationFlags() { complex = false; @@ -247,6 +257,7 @@ struct NodeOperationFlags { is_viewer_operation = false; is_preview_operation = false; use_datatype_conversion = true; + is_fullframe_operation = false; } }; @@ -341,6 +352,13 @@ class NodeOperation { NodeOperationOutput *getOutputSocket(unsigned int index = 0); NodeOperationInput *getInputSocket(unsigned int index); + NodeOperation *get_input_operation(int index) + { + /* TODO: Rename protected getInputOperation to get_input_operation and make it public replacing + * this method. */ + return getInputOperation(index); + } + /** * \brief determine the resolution of this node * \note this method will not set the resolution, this is the responsibility of the caller @@ -537,6 +555,33 @@ class NodeOperation { return std::unique_ptr(); } + /* -------------------------------------------------------------------- */ + /** \name Full Frame Methods + * \{ */ + + void render(MemoryBuffer *output_buf, + Span areas, + Span inputs_bufs, + ExecutionSystem &exec_system); + + /** + * Executes operation updating output memory buffer. Single-threaded calls. + */ + virtual void update_memory_buffer(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(output_area), + Span UNUSED(inputs), + ExecutionSystem &UNUSED(exec_system)) + { + } + + /** + * Get input operation area being read by this operation on rendering given output area. + */ + virtual void get_area_of_interest(int input_op_idx, const rcti &output_area, rcti &r_input_area); + void get_area_of_interest(NodeOperation *input_op, const rcti &output_area, rcti &r_input_area); + + /** \} */ + protected: NodeOperation(); @@ -616,6 +661,27 @@ class NodeOperation { { } + private: + /* -------------------------------------------------------------------- */ + /** \name Full Frame Methods + * \{ */ + + void render_full_frame(MemoryBuffer *output_buf, + Span areas, + Span inputs_bufs, + ExecutionSystem &exec_system); + + void render_full_frame_fallback(MemoryBuffer *output_buf, + Span areas, + Span inputs, + ExecutionSystem &exec_system); + void render_tile(MemoryBuffer *output_buf, rcti *tile_rect); + Vector replace_inputs_with_buffers(Span inputs_bufs); + void remove_buffers_and_restore_original_inputs( + Span original_inputs_links); + + /** \} */ + /* allow the DebugInfo class to look at internals */ friend class DebugInfo; diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index 82eb969b752..c81a5a2bd98 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -99,8 +99,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) determineResolutions(); - /* surround complex ops with read/write buffer */ - add_complex_operation_buffers(); + if (m_context->get_execution_model() == eExecutionModel::Tiled) { + /* surround complex ops with read/write buffer */ + add_complex_operation_buffers(); + } /* links not available from here on */ /* XXX make m_links a local variable to avoid confusion! */ @@ -111,8 +113,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) /* ensure topological (link-based) order of nodes */ /*sort_operations();*/ /* not needed yet */ - /* create execution groups */ - group_operations(); + if (m_context->get_execution_model() == eExecutionModel::Tiled) { + /* create execution groups */ + group_operations(); + } /* transfer resulting operations to the system */ system->set_operations(m_operations, m_groups); diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc new file mode 100644 index 00000000000..021e948a727 --- /dev/null +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc @@ -0,0 +1,131 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_SharedOperationBuffers.h" +#include "BLI_rect.h" +#include "COM_NodeOperation.h" + +namespace blender::compositor { + +SharedOperationBuffers::SharedOperationBuffers() : buffers_() +{ +} +SharedOperationBuffers::BufferData::BufferData() + : buffer(nullptr), render_areas(), registered_reads(0), received_reads(0) +{ +} + +SharedOperationBuffers::BufferData &SharedOperationBuffers::get_buffer_data(NodeOperation *op) +{ + return buffers_.lookup_or_add_cb(op, []() { return BufferData(); }); +} + +/** + * Whether given operation area to render is already registered. + * TODO: Possibly refactor to "request_area". Current implementation is incomplete: partial + * overlapping, etc. Leading to more rendering than necessary. + */ +bool SharedOperationBuffers::is_area_registered(NodeOperation *op, const rcti &area_to_render) +{ + BufferData &buf_data = get_buffer_data(op); + for (rcti ®_rect : buf_data.render_areas) { + if (BLI_rcti_inside_rcti(®_rect, &area_to_render)) { + return true; + } + } + return false; +} + +/** + * Registers an operation area to render. + */ +void SharedOperationBuffers::register_area(NodeOperation *op, const rcti &area_to_render) +{ + get_buffer_data(op).render_areas.append(area_to_render); +} + +/** + * Whether given operation has any registered reads (other operation registered it depends on given + * operation). + */ +bool SharedOperationBuffers::has_registered_reads(NodeOperation *op) +{ + return get_buffer_data(op).registered_reads > 0; +} + +/** + * Registers an operation read (other operation depends on given operation). + */ +void SharedOperationBuffers::register_read(NodeOperation *read_op) +{ + get_buffer_data(read_op).registered_reads++; +} + +/** + * Get registered areas given operation needs to render. + */ +blender::Span SharedOperationBuffers::get_areas_to_render(NodeOperation *op) +{ + return get_buffer_data(op).render_areas.as_span(); +} + +/** + * Whether this operation buffer has already been rendered. + */ +bool SharedOperationBuffers::is_operation_rendered(NodeOperation *op) +{ + return get_buffer_data(op).buffer != nullptr; +} + +/** + * Stores given operation rendered buffer. + */ +void SharedOperationBuffers::set_rendered_buffer(NodeOperation *op, + std::unique_ptr buffer) +{ + BufferData &buf_data = get_buffer_data(op); + BLI_assert(buf_data.received_reads == 0); + BLI_assert(buf_data.buffer == nullptr); + buf_data.buffer = std::move(buffer); +} + +/** + * Get given operation rendered buffer. + */ +MemoryBuffer *SharedOperationBuffers::get_rendered_buffer(NodeOperation *op) +{ + BLI_assert(is_operation_rendered(op)); + return get_buffer_data(op).buffer.get(); +} + +/** + * Reports an operation has finished reading given operation. If all given operation dependencies + * have finished its buffer will be disposed. + */ +void SharedOperationBuffers::read_finished(NodeOperation *read_op) +{ + BufferData &buf_data = get_buffer_data(read_op); + buf_data.received_reads++; + BLI_assert(buf_data.received_reads > 0 && buf_data.received_reads <= buf_data.registered_reads); + if (buf_data.received_reads == buf_data.registered_reads) { + /* Dispose buffer. */ + buf_data.buffer = nullptr; + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h new file mode 100644 index 00000000000..9e90a06a0d3 --- /dev/null +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h @@ -0,0 +1,71 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "BLI_map.hh" +#include "BLI_span.hh" +#include "BLI_vector.hh" +#include "COM_MemoryBuffer.h" +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif +#include + +namespace blender::compositor { + +/** + * Stores and shares operations rendered buffers including render data. Buffers are + * disposed once all dependent operations have finished reading them. + */ +class SharedOperationBuffers { + private: + typedef struct BufferData { + public: + BufferData(); + std::unique_ptr buffer; + blender::Vector render_areas; + int registered_reads; + int received_reads; + } BufferData; + blender::Map buffers_; + + public: + SharedOperationBuffers(); + bool is_area_registered(NodeOperation *op, const rcti &area_to_render); + void register_area(NodeOperation *op, const rcti &area_to_render); + + bool has_registered_reads(NodeOperation *op); + void register_read(NodeOperation *read_op); + + blender::Span get_areas_to_render(NodeOperation *op); + bool is_operation_rendered(NodeOperation *op); + void set_rendered_buffer(NodeOperation *op, std::unique_ptr buffer); + MemoryBuffer *get_rendered_buffer(NodeOperation *op); + + void read_finished(NodeOperation *read_op); + + private: + BufferData &get_buffer_data(NodeOperation *op); + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:SharedOperationBuffers") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.cc b/source/blender/compositor/intern/COM_TiledExecutionModel.cc new file mode 100644 index 00000000000..d025ce53330 --- /dev/null +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.cc @@ -0,0 +1,158 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_TiledExecutionModel.h" +#include "COM_Debug.h" +#include "COM_ExecutionGroup.h" +#include "COM_ReadBufferOperation.h" +#include "COM_WorkScheduler.h" + +#include "BLT_translation.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +TiledExecutionModel::TiledExecutionModel(CompositorContext &context, + Span operations, + Span groups) + : ExecutionModel(context, operations), groups_(groups) +{ + const bNodeTree *node_tree = context.getbNodeTree(); + node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Determining resolution")); + + unsigned int resolution[2]; + for (ExecutionGroup *group : groups_) { + resolution[0] = 0; + resolution[1] = 0; + group->determineResolution(resolution); + + if (border_.use_render_border) { + const rctf *render_border = border_.viewer_border; + group->setRenderBorder( + render_border->xmin, render_border->xmax, render_border->ymin, render_border->ymax); + } + + if (border_.use_viewer_border) { + const rctf *viewer_border = border_.viewer_border; + group->setViewerBorder( + viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax); + } + } +} + +static void update_read_buffer_offset(Span operations) +{ + unsigned int order = 0; + for (NodeOperation *operation : operations) { + if (operation->get_flags().is_read_buffer_operation) { + ReadBufferOperation *readOperation = (ReadBufferOperation *)operation; + readOperation->setOffset(order); + order++; + } + } +} + +static void init_write_operations_for_execution(Span operations, + const bNodeTree *bTree) +{ + for (NodeOperation *operation : operations) { + if (operation->get_flags().is_write_buffer_operation) { + operation->setbNodeTree(bTree); + operation->initExecution(); + } + } +} + +static void link_write_buffers(Span operations) +{ + for (NodeOperation *operation : operations) { + if (operation->get_flags().is_read_buffer_operation) { + ReadBufferOperation *readOperation = static_cast(operation); + readOperation->updateMemoryBuffer(); + } + } +} + +static void init_non_write_operations_for_execution(Span operations, + const bNodeTree *bTree) +{ + for (NodeOperation *operation : operations) { + if (!operation->get_flags().is_write_buffer_operation) { + operation->setbNodeTree(bTree); + operation->initExecution(); + } + } +} + +static void init_execution_groups_for_execution(Span groups, + const int chunk_size) +{ + for (ExecutionGroup *execution_group : groups) { + execution_group->setChunksize(chunk_size); + execution_group->initExecution(); + } +} + +void TiledExecutionModel::execute(ExecutionSystem &exec_system) +{ + const bNodeTree *editingtree = this->context_.getbNodeTree(); + + editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution")); + + update_read_buffer_offset(operations_); + + init_write_operations_for_execution(operations_, context_.getbNodeTree()); + link_write_buffers(operations_); + init_non_write_operations_for_execution(operations_, context_.getbNodeTree()); + init_execution_groups_for_execution(groups_, context_.getChunksize()); + + WorkScheduler::start(context_); + execute_groups(eCompositorPriority::High, exec_system); + if (!context_.isFastCalculation()) { + execute_groups(eCompositorPriority::Medium, exec_system); + execute_groups(eCompositorPriority::Low, exec_system); + } + WorkScheduler::finish(); + WorkScheduler::stop(); + + editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution")); + + for (NodeOperation *operation : operations_) { + operation->deinitExecution(); + } + + for (ExecutionGroup *execution_group : groups_) { + execution_group->deinitExecution(); + } +} + +void TiledExecutionModel::execute_groups(eCompositorPriority priority, + ExecutionSystem &exec_system) +{ + for (ExecutionGroup *execution_group : groups_) { + if (execution_group->get_flags().is_output && + execution_group->getRenderPriority() == priority) { + execution_group->execute(&exec_system); + } + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.h b/source/blender/compositor/intern/COM_TiledExecutionModel.h new file mode 100644 index 00000000000..05a795b9f07 --- /dev/null +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.h @@ -0,0 +1,54 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_ExecutionModel.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +class ExecutionGroup; + +/** + * Operations are executed from outputs to inputs grouped in execution groups and rendered in + * tiles. + */ +class TiledExecutionModel : public ExecutionModel { + private: + Span groups_; + + public: + TiledExecutionModel(CompositorContext &context, + Span operations, + Span groups); + + void execute(ExecutionSystem &exec_system) override; + + private: + void execute_groups(eCompositorPriority priority, ExecutionSystem &exec_system); + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:TiledExecutionModel") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_WorkPackage.h b/source/blender/compositor/intern/COM_WorkPackage.h index 28aa746fdc4..4d503022120 100644 --- a/source/blender/compositor/intern/COM_WorkPackage.h +++ b/source/blender/compositor/intern/COM_WorkPackage.h @@ -22,6 +22,7 @@ #include "BLI_rect.h" +#include #include namespace blender::compositor { @@ -33,6 +34,8 @@ class ExecutionGroup; * \see WorkScheduler */ struct WorkPackage { + eWorkPackageType type; + eWorkPackageState state = eWorkPackageState::NotScheduled; /** @@ -50,6 +53,16 @@ struct WorkPackage { */ rcti rect; + /** + * Custom function to execute when work package type is CustomFunction. + */ + std::function execute_fn; + + /** + * Called when work execution is finished. + */ + std::function executed_fn; + #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:WorkPackage") #endif diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index d578ac24a4a..157ded943d6 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -98,6 +98,8 @@ static struct { bool active = false; bool initialized = false; } opencl; + + int num_cpu_threads; } g_work_scheduler; /* -------------------------------------------------------------------- */ @@ -143,7 +145,8 @@ static void opencl_start(CompositorContext &context) static bool opencl_schedule(WorkPackage *package) { - if (package->execution_group->get_flags().open_cl && g_work_scheduler.opencl.active) { + if (package->type == eWorkPackageType::Tile && package->execution_group->get_flags().open_cl && + g_work_scheduler.opencl.active) { BLI_thread_queue_push(g_work_scheduler.opencl.queue, package); return true; } @@ -532,11 +535,12 @@ void WorkScheduler::initialize(bool use_opencl, int num_cpu_threads) opencl_initialize(use_opencl); } + g_work_scheduler.num_cpu_threads = num_cpu_threads; switch (COM_threading_model()) { case ThreadingModel::SingleThreaded: + g_work_scheduler.num_cpu_threads = 1; /* Nothing to do. */ break; - case ThreadingModel::Queue: threading_model_queue_initialize(num_cpu_threads); break; @@ -568,6 +572,11 @@ void WorkScheduler::deinitialize() } } +int WorkScheduler::get_num_cpu_threads() +{ + return g_work_scheduler.num_cpu_threads; +} + int WorkScheduler::current_thread_id() { if (COM_threading_model() == ThreadingModel::SingleThreaded) { diff --git a/source/blender/compositor/intern/COM_WorkScheduler.h b/source/blender/compositor/intern/COM_WorkScheduler.h index 85b1d7e2ebf..be88859be7c 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.h +++ b/source/blender/compositor/intern/COM_WorkScheduler.h @@ -87,6 +87,8 @@ struct WorkScheduler { */ static bool has_gpu_devices(); + static int get_num_cpu_threads(); + static int current_thread_id(); #ifdef WITH_CXX_GUARDEDALLOC diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc index 922393f006a..1b2ce341a66 100644 --- a/source/blender/compositor/nodes/COM_TranslateNode.cc +++ b/source/blender/compositor/nodes/COM_TranslateNode.cc @@ -56,7 +56,10 @@ void TranslateNode::convertToOperations(NodeConverter &converter, converter.mapInputSocket(inputYSocket, operation->getInputSocket(2)); converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); - if (data->wrap_axis) { + /* FullFrame does not support using WriteBufferOperation. + * TODO: Implement TranslateOperation with wrap support in FullFrame. + */ + if (data->wrap_axis && context.get_execution_model() != eExecutionModel::FullFrame) { WriteBufferOperation *writeOperation = new WriteBufferOperation(DataType::Color); WrapOperation *wrapOperation = new WrapOperation(DataType::Color); wrapOperation->setMemoryProxy(writeOperation->getMemoryProxy()); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 58c94b6f369..0fa1d1a74ac 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -461,7 +461,6 @@ typedef struct bNodeTree { short is_updating; /** Generic temporary flag for recursion check (DFS/BFS). */ short done; - char _pad2[4]; /** Specific node type this tree is used for. */ int nodetype DNA_DEPRECATED; @@ -472,6 +471,8 @@ typedef struct bNodeTree { short render_quality; /** Tile size for compositor engine. */ int chunksize; + /** Execution mode to use for compositor engine. */ + int execution_mode; rctf viewer_border; @@ -545,6 +546,12 @@ typedef enum eNodeTreeUpdate { NTREE_UPDATE_GROUP = (NTREE_UPDATE_GROUP_IN | NTREE_UPDATE_GROUP_OUT), } eNodeTreeUpdate; +/* tree->execution_mode */ +typedef enum eNodeTreeExecutionMode { + NTREE_EXECUTION_MODE_TILED = 0, + NTREE_EXECUTION_MODE_FULL_FRAME = 1, +} eNodeTreeExecutionMode; + /* socket value structs for input buttons * DEPRECATED now using ID properties */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 1fed8e14bd6..61d2c04d98b 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -642,11 +642,12 @@ typedef struct UserDef_Experimental { * when the release cycle is not alpha. */ char use_new_hair_type; char use_new_point_cloud_type; + char use_full_frame_compositor; char use_sculpt_vertex_colors; char use_sculpt_tools_tilt; char use_asset_browser; char use_override_templates; - char _pad[6]; + char _pad[5]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 0ca275eb58b..f5cbb694554 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -127,6 +127,20 @@ static const EnumPropertyItem node_chunksize_items[] = { }; #endif +static const EnumPropertyItem rna_enum_execution_mode_items[] = { + {NTREE_EXECUTION_MODE_TILED, + "TILED", + 0, + "Tiled", + "Compositing is tiled, having as priority to display first tiles as fast as possible"}, + {NTREE_EXECUTION_MODE_FULL_FRAME, + "FULL_FRAME", + 0, + "Full Frame", + "Composites full image result as fast as possible"}, + {0, NULL, 0, NULL, NULL}, +}; + const EnumPropertyItem rna_enum_mapping_type_items[] = { {NODE_MAPPING_TYPE_POINT, "POINT", 0, "Point", "Transform a point"}, {NODE_MAPPING_TYPE_TEXTURE, @@ -11671,6 +11685,12 @@ static void rna_def_composite_nodetree(BlenderRNA *brna) RNA_def_struct_sdna(srna, "bNodeTree"); RNA_def_struct_ui_icon(srna, ICON_RENDERLAYERS); + prop = RNA_def_property(srna, "execution_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "execution_mode"); + RNA_def_property_enum_items(prop, rna_enum_execution_mode_items); + RNA_def_property_ui_text(prop, "Execution Mode", "Set how compositing is executed"); + RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update"); + prop = RNA_def_property(srna, "render_quality", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "render_quality"); RNA_def_property_enum_items(prop, node_quality_items); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index bacd3943141..6005ec96255 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6276,6 +6276,14 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_ui_text( prop, "New Point Cloud Type", "Enable the new point cloud type in the ui"); + prop = RNA_def_property(srna, "use_full_frame_compositor", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_full_frame_compositor", 1); + RNA_def_property_ui_text(prop, + "Full Frame Compositor", + "Enable compositor full frame execution mode option (no tiling, " + "reduces execution time and memory usage)"); + RNA_def_property_update(prop, 0, "rna_userdef_update"); + prop = RNA_def_property(srna, "use_new_hair_type", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_new_hair_type", 1); RNA_def_property_ui_text(prop, "New Hair Type", "Enable the new hair type in the ui"); -- cgit v1.2.3 From 27910ccce6a0a6a79b09144631309331cb557a25 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 1 Jun 2021 12:00:16 +0200 Subject: Cleanup: clang-tidy * `readability-redundant-member-init` * `readability-inconsistent-declaration-parameter-name` * Remove constructor that can be defaulted. --- source/blender/compositor/intern/COM_BufferOperation.cc | 2 +- source/blender/compositor/intern/COM_FullFrameExecutionModel.cc | 1 - source/blender/compositor/intern/COM_MultiThreadedOperation.h | 2 +- source/blender/compositor/intern/COM_SharedOperationBuffers.cc | 5 +---- source/blender/compositor/intern/COM_SharedOperationBuffers.h | 1 - 5 files changed, 3 insertions(+), 8 deletions(-) diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc index c1e64142443..8464d01801f 100644 --- a/source/blender/compositor/intern/COM_BufferOperation.cc +++ b/source/blender/compositor/intern/COM_BufferOperation.cc @@ -20,7 +20,7 @@ namespace blender::compositor { -BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) : NodeOperation() +BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) { buffer_ = buffer; /* TODO: Implement a MemoryBuffer get_size() method returning a Size2d type. Shorten following diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc index 1099aadd89d..396aa2fcf6f 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -36,7 +36,6 @@ FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context, : ExecutionModel(context, operations), active_buffers_(shared_buffers), num_operations_finished_(0), - priorities_(), work_mutex_(), work_finished_cond_() { diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.h b/source/blender/compositor/intern/COM_MultiThreadedOperation.h index e86b1d303f9..97c5fba4ead 100644 --- a/source/blender/compositor/intern/COM_MultiThreadedOperation.h +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.h @@ -65,7 +65,7 @@ class MultiThreadedOperation : public NodeOperation { private: void update_memory_buffer(MemoryBuffer *output, - const rcti &output_rect, + const rcti &output_area, blender::Span inputs, ExecutionSystem &exec_system) override; }; diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc index 021e948a727..4ce674a1c25 100644 --- a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc @@ -22,11 +22,8 @@ namespace blender::compositor { -SharedOperationBuffers::SharedOperationBuffers() : buffers_() -{ -} SharedOperationBuffers::BufferData::BufferData() - : buffer(nullptr), render_areas(), registered_reads(0), received_reads(0) + : buffer(nullptr), registered_reads(0), received_reads(0) { } diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h index 9e90a06a0d3..480a799d89f 100644 --- a/source/blender/compositor/intern/COM_SharedOperationBuffers.h +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h @@ -46,7 +46,6 @@ class SharedOperationBuffers { blender::Map buffers_; public: - SharedOperationBuffers(); bool is_area_registered(NodeOperation *op, const rcti &area_to_render); void register_area(NodeOperation *op, const rcti &area_to_render); -- cgit v1.2.3 From db15c9d1bd9dd85ef1db0df24f4ad56c0b2012fe Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 1 Jun 2021 11:39:28 +0200 Subject: ID Management: Allow unique name check for linked IDs too. This is mandatory for liboverride resync, since this feature may imply we have to create linked overrides in libraries, and there may be several copies of those. This is also a first step to a more general support of IDmanagement-editing library data. Note that this commit should have absolutely no effect on current code, as the only function allowed to check unique names for linked IDs currently is `BKE_libblock_management_main_add`, which is unused. This commit also adds some basic testing for `BKE_id_new_name_validate`. --- source/blender/blenkernel/BKE_lib_id.h | 6 ++- source/blender/blenkernel/intern/lib_id.c | 27 +++++----- source/blender/blenkernel/intern/lib_id_test.cc | 60 +++++++++++++++++++++++ source/blender/blenloader/intern/versioning_250.c | 2 +- 4 files changed, 81 insertions(+), 14 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 7ac45ac4883..e16507bf3cc 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -258,8 +258,10 @@ void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b); void id_sort_by_name(struct ListBase *lb, struct ID *id, struct ID *id_sorting_hint); void BKE_lib_id_expand_local(struct Main *bmain, struct ID *id); -bool BKE_id_new_name_validate(struct ListBase *lb, struct ID *id, const char *name) - ATTR_NONNULL(1, 2); +bool BKE_id_new_name_validate(struct ListBase *lb, + struct ID *id, + const char *name, + const bool do_linked_data) ATTR_NONNULL(1, 2); void BKE_lib_id_clear_library_data(struct Main *bmain, struct ID *id); /* Affect whole Main database. */ diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index f93bf494ee9..8a6fb5b3e9e 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -164,7 +164,7 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id) id->tag &= ~(LIB_TAG_INDIRECT | LIB_TAG_EXTERN); id->flag &= ~LIB_INDIRECT_WEAK_LINK; if (id_in_mainlist) { - if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL)) { + if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL, false)) { bmain->is_memfile_undo_written = false; } } @@ -833,7 +833,9 @@ void BKE_libblock_management_main_add(Main *bmain, void *idv) ListBase *lb = which_libbase(bmain, GS(id->name)); BKE_main_lock(bmain); BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, NULL); + /* We need to allow adding extra datablocks into libraries too, e.g. to support generating new + * overrides for recursive resync. */ + BKE_id_new_name_validate(lb, id, NULL, true); /* alphabetic insertion: is in new_id */ id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT); bmain->is_memfile_undo_written = false; @@ -989,7 +991,7 @@ void BKE_main_id_repair_duplicate_names_listbase(ListBase *lb) } for (i = 0; i < lb_len; i++) { if (!BLI_gset_add(gset, id_array[i]->name + 2)) { - BKE_id_new_name_validate(lb, id_array[i], NULL); + BKE_id_new_name_validate(lb, id_array[i], NULL, false); } } BLI_gset_free(gset, NULL); @@ -1092,7 +1094,7 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl BKE_main_lock(bmain); BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, name); + BKE_id_new_name_validate(lb, id, name, false); bmain->is_memfile_undo_written = false; /* alphabetic insertion: is in new_id */ BKE_main_unlock(bmain); @@ -1557,7 +1559,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ * and that current one is not. */ bool is_valid = false; for (id_test = lb->first; id_test; id_test = id_test->next) { - if (id != id_test && !ID_IS_LINKED(id_test)) { + if (id != id_test && id_test->lib == id->lib) { if (id_test->name[2] == final_name[0] && STREQ(final_name, id_test->name + 2)) { /* We expect final_name to not be already used, so this is a failure. */ is_valid = false; @@ -1613,7 +1615,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ for (id_test = lb->first; id_test; id_test = id_test->next) { char base_name_test[MAX_ID_NAME - 2]; int number_test; - if ((id != id_test) && !ID_IS_LINKED(id_test) && (name[0] == id_test->name[2]) && + if ((id != id_test) && (id_test->lib == id->lib) && (name[0] == id_test->name[2]) && (ELEM(id_test->name[base_name_len + 2], '.', '\0')) && STREQLEN(name, id_test->name + 2, base_name_len) && (BLI_split_name_num(base_name_test, &number_test, id_test->name + 2, '.') == @@ -1702,15 +1704,18 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ * * Only for local IDs (linked ones already have a unique ID in their library). * + * \param do_linked_data if true, also ensure a unique name in case the given \a id is linked + * (otherwise, just ensure that it is properly sorted). + * * \return true if a new name had to be created. */ -bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname) +bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const bool do_linked_data) { bool result = false; char name[MAX_ID_NAME - 2]; - /* If library, don't rename, but do ensure proper sorting. */ - if (ID_IS_LINKED(id)) { + /* If library, don't rename (unless explicitely required), but do ensure proper sorting. */ + if (!do_linked_data && ID_IS_LINKED(id)) { id_sort_by_name(lb, id, NULL); return result; @@ -2195,7 +2200,7 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) idtest = BLI_findstring(lb, name + 2, offsetof(ID, name) + 2); if (idtest != NULL) { /* BKE_id_new_name_validate also takes care of sorting. */ - BKE_id_new_name_validate(lb, idtest, NULL); + BKE_id_new_name_validate(lb, idtest, NULL, false); bmain->is_memfile_undo_written = false; } } @@ -2206,7 +2211,7 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) void BKE_libblock_rename(Main *bmain, ID *id, const char *name) { ListBase *lb = which_libbase(bmain, GS(id->name)); - if (BKE_id_new_name_validate(lb, id, name)) { + if (BKE_id_new_name_validate(lb, id, name, false)) { bmain->is_memfile_undo_written = false; } } diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc index fbe4a15da1c..8e21ae88aa6 100644 --- a/source/blender/blenkernel/intern/lib_id_test.cc +++ b/source/blender/blenkernel/intern/lib_id_test.cc @@ -20,6 +20,7 @@ #include "MEM_guardedalloc.h" #include "BLI_listbase.h" +#include "BLI_string.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" @@ -110,4 +111,63 @@ TEST(lib_id_main_sort, linked_ids_1) test_lib_id_main_sort_free(&ctx); } +TEST(lib_id_main_unique_name, local_ids_1) +{ + LibIDMainSortTestContext ctx = {nullptr}; + test_lib_id_main_sort_init(&ctx); + EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); + + ID *id_c = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); + ID *id_a = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); + ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); + test_lib_id_main_sort_check_order({id_a, id_b, id_c}); + + BLI_strncpy(id_c->name, id_a->name, sizeof(id_c->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_c, NULL, false); + EXPECT_TRUE(strcmp(id_c->name + 2, "OB_A.001") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_a); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_a, id_c, id_b}); + + test_lib_id_main_sort_free(&ctx); +} + +TEST(lib_id_main_unique_name, linked_ids_1) +{ + LibIDMainSortTestContext ctx = {nullptr}; + test_lib_id_main_sort_init(&ctx); + EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); + + Library *lib_a = static_cast(BKE_id_new(ctx.bmain, ID_LI, "LI_A")); + Library *lib_b = static_cast(BKE_id_new(ctx.bmain, ID_LI, "LI_B")); + ID *id_c = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); + ID *id_a = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); + ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); + + id_a->lib = lib_a; + id_sort_by_name(&ctx.bmain->objects, id_a, nullptr); + id_b->lib = lib_a; + id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); + BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true); + EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A.001") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_c); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + + id_b->lib = lib_b; + id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); + BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true); + EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_c); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + + test_lib_id_main_sort_free(&ctx); +} + } // namespace blender::bke::tests diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 6338dc95aba..990fc1d65d7 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -446,7 +446,7 @@ static void versions_gpencil_add_main(ListBase *lb, ID *id, const char *name) id->flag = LIB_FAKEUSER; *((short *)id->name) = ID_GD; - BKE_id_new_name_validate(lb, id, name); + BKE_id_new_name_validate(lb, id, name, false); /* alphabetic insertion: is in BKE_id_new_name_validate */ if ((id->tag & LIB_TAG_TEMP_MAIN) == 0) { -- cgit v1.2.3 From 84e16c499243f6bdeb29f684408f4d206b919ee8 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 1 Jun 2021 11:46:43 +0200 Subject: Fix `BLI_libblock_ensure_unique_name` not ignoring linked IDs. This function would considere that there was a name conflict even in case existing ID would be a linked one. This is only a (symbolic) perforance improvement and logical fix, since `BKE_id_new_name_validate` would not do that mistake anyway. --- source/blender/blenkernel/intern/lib_id.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 8a6fb5b3e9e..2dd0dc2c097 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -2198,7 +2198,7 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) /* search for id */ idtest = BLI_findstring(lb, name + 2, offsetof(ID, name) + 2); - if (idtest != NULL) { + if (idtest != NULL && !ID_IS_LINKED(idtest)) { /* BKE_id_new_name_validate also takes care of sorting. */ BKE_id_new_name_validate(lb, idtest, NULL, false); bmain->is_memfile_undo_written = false; -- cgit v1.2.3 From f25316e97a81650b85c5e2a91c90b7dd558240e5 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 1 Jun 2021 11:49:15 +0200 Subject: IDManagement: `BKE_libblock_rename`: assert we get local ID. For now at least, linked data should never be renamed that way. --- source/blender/blenkernel/intern/lib_id.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 2dd0dc2c097..490abe05169 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -2210,6 +2210,7 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) */ void BKE_libblock_rename(Main *bmain, ID *id, const char *name) { + BLI_assert(!ID_IS_LINKED(id)); ListBase *lb = which_libbase(bmain, GS(id->name)); if (BKE_id_new_name_validate(lb, id, name, false)) { bmain->is_memfile_undo_written = false; -- cgit v1.2.3 From bf8659930fe8719d0918fe7da8ed8718b6ce2d94 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 1 Jun 2021 11:56:41 +0200 Subject: Fix libOverride resync issues in some corner-cases. This commit fixes two different issues: * In some cases, when an object was added to a sub-collection and used into a different subcollection, and the root common collection would not need to be resynced, it would end up creating multiple overrides of the new object. This was affecting both normal and recursive resync. * In recurisve resync case, the barrier code to define what is part or not of a override group/hierarchy was wrong. Note that the current solution for the first issue is sub-optimal (it goes back to the root of the override hierarchy and resync the whole thing), a better solution is TODO for now. --- source/blender/blenkernel/intern/lib_override.c | 106 ++++++++++++++++++++---- 1 file changed, 90 insertions(+), 16 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 83bd2b6abb7..5af37aeb13e 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -428,7 +428,8 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, /* If other ID is a linked one, but not from the same library as our reference, then we * consider we should also remap it, as part of recursive resync. */ - if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib) { + if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib && + other_id != local_id) { BKE_libblock_relink_ex(bmain, other_id, reference_id, @@ -468,6 +469,8 @@ typedef struct LibOverrideGroupTagData { ID *id_root; uint tag; uint missing_tag; + /* Whether we are looping on override data, or their references (linked) one. */ + bool is_override; } LibOverrideGroupTagData; /* Tag all IDs in dependency relationships within an override hierarchy/group. @@ -480,6 +483,7 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa { Main *bmain = data->bmain; ID *id = data->id_root; + const bool is_override = data->is_override; MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); BLI_assert(entry != NULL); @@ -501,16 +505,16 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa } /* We only consider IDs from the same library. */ ID *to_id = *to_id_entry->id_pointer.to; - if (!ID_IS_LINKED(to_id) && !ID_IS_OVERRIDE_LIBRARY(to_id)) { - /* Pure local data is a barrier of dependency in override cases. */ + if (to_id == NULL || to_id->lib != id->lib || + (is_override && !ID_IS_OVERRIDE_LIBRARY(to_id))) { + /* IDs from different libraries, or non-override IDs in case we are processing overrides, are + * both barriers of dependency. */ continue; } - if (to_id != NULL && to_id->lib == id->lib) { - LibOverrideGroupTagData sub_data = *data; - sub_data.id_root = to_id; - if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) { - id->tag |= data->tag; - } + LibOverrideGroupTagData sub_data = *data; + sub_data.id_root = to_id; + if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) { + id->tag |= data->tag; } } @@ -522,6 +526,7 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat Main *bmain = data->bmain; ID *id_owner = data->id_root; BLI_assert(ID_IS_LINKED(id_owner)); + BLI_assert(!data->is_override); const uint tag = data->tag; const uint missing_tag = data->missing_tag; @@ -587,6 +592,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) { Main *bmain = data->bmain; ID *id_root = data->id_root; + BLI_assert(!data->is_override); if ((id_root->tag & LIB_TAG_MISSING)) { id_root->tag |= data->missing_tag; @@ -618,6 +624,7 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data Main *bmain = data->bmain; ID *id_owner = data->id_root; BLI_assert(ID_IS_OVERRIDE_LIBRARY(id_owner)); + BLI_assert(data->is_override); const uint tag = data->tag; const uint missing_tag = data->missing_tag; @@ -674,6 +681,7 @@ static void lib_override_local_group_tag(LibOverrideGroupTagData *data) { ID *id_root = data->id_root; BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); + BLI_assert(data->is_override); if ((id_root->override_library->reference->tag & LIB_TAG_MISSING)) { id_root->tag |= data->missing_tag; @@ -689,8 +697,11 @@ static void lib_override_local_group_tag(LibOverrideGroupTagData *data) static bool lib_override_library_create_do(Main *bmain, ID *id_root) { BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = false}; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -941,12 +952,16 @@ bool BKE_lib_override_library_resync(Main *bmain, ID *id_root_reference = id_root->override_library->reference; BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = true}; lib_override_local_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); data.id_root = id_root_reference; + data.is_override = false; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -1284,6 +1299,58 @@ bool BKE_lib_override_library_resync(Main *bmain, return success; } +/* Ensures parent collection (or objects) in the same override group are also tagged for resync. + * + * This is needed since otherwise, some (new) ID added in one sub-collection might be used in + * another unrelated sub-collection, if 'root' collection is not resynced separated resync of those + * sub-collections would be unaware that this is the same ID, and would re-generate several + * overrides for it. + * + * TODO: This is a sub-optimal, simple solution. At some point, we should rather find a way to + * resync a set of 'sub-roots' overrides, instead of having to 'go back' to the real root and + * resync the whole hierarchy. */ +static ID *lib_override_library_main_resync_find_root_recurse(ID *id, int *level) +{ + (*level)++; + ID *return_id = id; + + switch (GS(id->name)) { + case ID_GR: { + /* Find the highest valid collection in the parenting hierarchy. + * Note that in practice, in any decent common case there is only one well defined root + * collection anyway. */ + int max_level = *level; + Collection *collection = (Collection *)id; + LISTBASE_FOREACH (CollectionParent *, collection_parent_iter, &collection->parents) { + Collection *collection_parent = collection_parent_iter->collection; + if (ID_IS_OVERRIDE_LIBRARY_REAL(collection_parent) && + collection_parent->id.lib == id->lib) { + int tmp_level = *level; + ID *tmp_id = lib_override_library_main_resync_find_root_recurse(&collection_parent->id, + &tmp_level); + if (tmp_level > max_level) { + max_level = tmp_level; + return_id = tmp_id; + } + } + } + break; + } + case ID_OB: { + Object *object = (Object *)id; + if (object->parent != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(object->parent) && + object->parent->id.lib == id->lib) { + return_id = lib_override_library_main_resync_find_root_recurse(&object->parent->id, level); + } + break; + } + default: + break; + } + + return return_id; +} + /* Ensure resync of all overrides at one level of indirect usage. * * We need to handle each level independently, since an override at level n may be affected by @@ -1319,7 +1386,8 @@ static void lib_override_library_main_resync_on_library_indirect_level( LibOverrideGroupTagData data = {.bmain = bmain, .id_root = id->override_library->reference, .tag = LIB_TAG_DOIT, - .missing_tag = LIB_TAG_MISSING}; + .missing_tag = LIB_TAG_MISSING, + .is_override = false}; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); lib_override_hierarchy_dependencies_recursive_tag(&data); @@ -1403,6 +1471,9 @@ static void lib_override_library_main_resync_on_library_indirect_level( (!ID_IS_LINKED(id) && library_indirect_level != 0)) { continue; } + + int level = 0; + id = lib_override_library_main_resync_find_root_recurse(id, &level); BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id)); do_continue = true; @@ -1557,8 +1628,11 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) /* Tag all library overrides in the chains of dependencies from the given root one. */ BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = true}; lib_override_local_group_tag(&data); BKE_main_relations_free(bmain); -- cgit v1.2.3 From 4b57bbac2262df1a24099059450508e89ee00df1 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 1 Jun 2021 12:29:56 +0200 Subject: Cleanup: LibOverride: rename 'local' group tagging functions to 'overrides'. Since we use them on linked data now as well, 'local' does not fit them anymore. --- source/blender/blenkernel/intern/lib_override.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 5af37aeb13e..4d5085d6ad5 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -619,7 +619,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) } } -static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data) +static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *data) { Main *bmain = data->bmain; ID *id_owner = data->id_root; @@ -672,12 +672,12 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data /* Recursively process the dependencies. */ LibOverrideGroupTagData sub_data = *data; sub_data.id_root = to_id; - lib_override_local_group_tag_recursive(&sub_data); + lib_override_overrides_group_tag_recursive(&sub_data); } } /* This will tag all override IDs of an override group defined by the given `id_root`. */ -static void lib_override_local_group_tag(LibOverrideGroupTagData *data) +static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data) { ID *id_root = data->id_root; BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); @@ -691,7 +691,7 @@ static void lib_override_local_group_tag(LibOverrideGroupTagData *data) } /* Tag all local overrides in id_root's group. */ - lib_override_local_group_tag_recursive(data); + lib_override_overrides_group_tag_recursive(data); } static bool lib_override_library_create_do(Main *bmain, ID *id_root) @@ -957,7 +957,7 @@ bool BKE_lib_override_library_resync(Main *bmain, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING, .is_override = true}; - lib_override_local_group_tag(&data); + lib_override_overrides_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); data.id_root = id_root_reference; @@ -1633,7 +1633,7 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING, .is_override = true}; - lib_override_local_group_tag(&data); + lib_override_overrides_group_tag(&data); BKE_main_relations_free(bmain); -- cgit v1.2.3 From 98c662672998a16a7ac00ab283357de17f3ae54c Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 1 Jun 2021 12:12:13 +0200 Subject: DrawManager: Use CPP for Mesh Extraction Scheduling. More cleanups will come to make this more CPP-like. --- source/blender/draw/CMakeLists.txt | 2 +- source/blender/draw/intern/draw_cache_extract.h | 11 +- .../blender/draw/intern/draw_cache_extract_mesh.c | 987 ------------------- .../blender/draw/intern/draw_cache_extract_mesh.cc | 1006 ++++++++++++++++++++ .../draw/intern/draw_cache_extract_mesh_private.h | 8 + 5 files changed, 1025 insertions(+), 989 deletions(-) delete mode 100644 source/blender/draw/intern/draw_cache_extract_mesh.c create mode 100644 source/blender/draw/intern/draw_cache_extract_mesh.cc diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index adbe7fdf274..d6598bf79b0 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -53,7 +53,7 @@ set(SRC intern/draw_cache.c intern/draw_cache_extract_mesh_extractors.c intern/draw_cache_extract_mesh_render_data.c - intern/draw_cache_extract_mesh.c + intern/draw_cache_extract_mesh.cc intern/draw_cache_impl_curve.cc intern/draw_cache_impl_displist.c intern/draw_cache_impl_gpencil.c diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index abba3aeeb70..36756616ca7 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -68,13 +68,13 @@ typedef struct DRW_MeshCDMask { * bit-wise and atomic operations are used to compare and update the struct. * See `mesh_cd_layers_type_*` functions. */ BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint64_t), "DRW_MeshCDMask exceeds 64 bits") - typedef enum eMRIterType { MR_ITER_LOOPTRI = 1 << 0, MR_ITER_POLY = 1 << 1, MR_ITER_LEDGE = 1 << 2, MR_ITER_LVERT = 1 << 3, } eMRIterType; +ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT) typedef enum eMRDataType { MR_DATA_POLY_NOR = 1 << 1, @@ -83,6 +83,11 @@ typedef enum eMRDataType { /** Force loop normals calculation. */ MR_DATA_TAN_LOOP_NOR = 1 << 4, } eMRDataType; +ENUM_OPERATORS(eMRDataType, MR_DATA_TAN_LOOP_NOR) + +#ifdef __cplusplus +extern "C" { +#endif BLI_INLINE int mesh_render_mat_len_get(Mesh *me) { @@ -298,3 +303,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const Scene *scene, const ToolSettings *ts, const bool use_hide); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c deleted file mode 100644 index 0d2a4704b1b..00000000000 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ /dev/null @@ -1,987 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2017 by Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup draw - * - * \brief Extraction of Mesh data into VBO to feed to GPU. - */ -#include "MEM_guardedalloc.h" - -#include "atomic_ops.h" - -#include "DNA_mesh_types.h" -#include "DNA_scene_types.h" - -#include "BLI_task.h" - -#include "BKE_editmesh.h" - -#include "GPU_capabilities.h" - -#include "draw_cache_extract.h" -#include "draw_cache_extract_mesh_private.h" -#include "draw_cache_inline.h" - -// #define DEBUG_TIME - -#ifdef DEBUG_TIME -# include "PIL_time_utildefines.h" -#endif - -#define CHUNK_SIZE 8192 - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract Struct - * \{ */ - -typedef struct MeshExtractRunData { - const MeshExtract *extractor; - void *buffer; - void *user_data; -} MeshExtractRunData; - -typedef struct MeshExtractRunDataArray { - int len; - MeshExtractRunData items[M_EXTRACT_LEN]; -} MeshExtractRunDataArray; - -static void mesh_extract_run_data_array_init(MeshExtractRunDataArray *array) -{ - array->len = 0; -} - -static void mesh_extract_run_data_array_add_ex(MeshExtractRunDataArray *array, - const MeshExtractRunData *run_data) -{ - array->items[array->len] = *run_data; - array->len++; -} - -static void mesh_extract_run_data_array_add(MeshExtractRunDataArray *array, - const MeshExtract *extractor) -{ - MeshExtractRunData run_data; - run_data.extractor = extractor; - run_data.buffer = NULL; - run_data.user_data = NULL; - mesh_extract_run_data_array_add_ex(array, &run_data); -} - -static void mesh_extract_run_data_array_filter_iter_type(const MeshExtractRunDataArray *src, - MeshExtractRunDataArray *dst, - eMRIterType iter_type) -{ - for (int i = 0; i < src->len; i++) { - - const MeshExtractRunData *data = &src->items[i]; - const MeshExtract *extractor = data->extractor; - if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { - BLI_assert(extractor->iter_looptri_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { - BLI_assert(extractor->iter_poly_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { - BLI_assert(extractor->iter_ledge_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { - BLI_assert(extractor->iter_lvert_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - } -} - -static void mesh_extract_run_data_array_filter_threading( - const MeshExtractRunDataArray *src, MeshExtractRunDataArray *dst_multi_threaded) -{ - for (int i = 0; i < src->len; i++) { - const MeshExtract *extractor = src->items[i].extractor; - if (extractor->use_threading) { - mesh_extract_run_data_array_add(dst_multi_threaded, extractor); - } - } -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract - * \{ */ - -static void extracts_flags_get(const MeshExtractRunDataArray *extractors, - eMRIterType *r_iter_type, - eMRDataType *r_data_flag) -{ - eMRIterType iter_type = 0; - eMRDataType data_flag = 0; - - for (int i = 0; i < extractors->len; i++) { - const MeshExtract *extractor = extractors->items[i].extractor; - iter_type |= mesh_extract_iter_type(extractor); - data_flag |= extractor->data_flag; - } - - if (r_iter_type) { - *r_iter_type = iter_type; - } - if (r_data_flag) { - *r_data_flag = data_flag; - } -} - -BLI_INLINE void extract_init(const MeshRenderData *mr, - struct MeshBatchCache *cache, - MeshExtractRunDataArray *extractors, - MeshBufferCache *mbc) -{ - /* Multi thread. */ - for (int i = 0; i < extractors->len; i++) { - MeshExtractRunData *run_data = &extractors->items[i]; - const MeshExtract *extractor = run_data->extractor; - run_data->buffer = mesh_extract_buffer_get(extractor, mbc); - run_data->user_data = extractor->init(mr, cache, run_data->buffer); - } -} - -BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr, - const ExtractTriBMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LOOPTRI); - - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, elt_index, params) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_looptri_bm(mr, elt, elt_index, run_data->user_data); - } - } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; -} - -BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr, - const ExtractTriMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LOOPTRI); - - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, mlt_index, params) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_looptri_mesh(mr, mlt, mlt_index, run_data->user_data); - } - } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; -} - -BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_POLY); - - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_poly_bm(mr, f, f_index, run_data->user_data); - } - } - EXTRACT_POLY_FOREACH_BM_END; -} - -BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_POLY); - - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_poly_mesh(mr, mp, mp_index, run_data->user_data); - } - } - EXTRACT_POLY_FOREACH_MESH_END; -} - -BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LEDGE); - - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_ledge_bm(mr, eed, ledge_index, run_data->user_data); - } - } - EXTRACT_LEDGE_FOREACH_BM_END; -} - -BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LEDGE); - - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_ledge_mesh(mr, med, ledge_index, run_data->user_data); - } - } - EXTRACT_LEDGE_FOREACH_MESH_END; -} - -BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LVERT); - - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_lvert_bm(mr, eve, lvert_index, run_data->user_data); - } - } - EXTRACT_LVERT_FOREACH_BM_END; -} - -BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LVERT); - - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_lvert_mesh(mr, mv, lvert_index, run_data->user_data); - } - } - EXTRACT_LVERT_FOREACH_MESH_END; -} - -BLI_INLINE void extract_finish(const MeshRenderData *mr, - struct MeshBatchCache *cache, - const MeshExtractRunDataArray *extractors) -{ - for (int i = 0; i < extractors->len; i++) { - const MeshExtractRunData *run_data = &extractors->items[i]; - const MeshExtract *extractor = run_data->extractor; - if (extractor->finish) { - extractor->finish(mr, cache, run_data->buffer, run_data->user_data); - } - } -} - -/* Single Thread. */ -BLI_INLINE void extract_run_and_finish_init(const MeshRenderData *mr, - struct MeshBatchCache *cache, - MeshExtractRunDataArray *extractors, - eMRIterType iter_type, - MeshBufferCache *mbc) -{ - extract_init(mr, cache, extractors, mbc); - - bool is_mesh = mr->extract_type != MR_EXTRACT_BMESH; - if (iter_type & MR_ITER_LOOPTRI) { - if (is_mesh) { - extract_iter_looptri_mesh(mr, - &(const ExtractTriMesh_Params){ - .mlooptri = mr->mlooptri, - .tri_range = {0, mr->tri_len}, - }, - extractors); - } - else { - extract_iter_looptri_bm(mr, - &(const ExtractTriBMesh_Params){ - .looptris = mr->edit_bmesh->looptris, - .tri_range = {0, mr->tri_len}, - }, - extractors); - } - } - if (iter_type & MR_ITER_POLY) { - if (is_mesh) { - extract_iter_poly_mesh(mr, - &(const ExtractPolyMesh_Params){ - .poly_range = {0, mr->poly_len}, - }, - extractors); - } - else { - extract_iter_poly_bm(mr, - &(const ExtractPolyBMesh_Params){ - .poly_range = {0, mr->poly_len}, - }, - extractors); - } - } - if (iter_type & MR_ITER_LEDGE) { - if (is_mesh) { - extract_iter_ledge_mesh(mr, - &(const ExtractLEdgeMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {0, mr->edge_loose_len}, - }, - extractors); - } - else { - extract_iter_ledge_bm(mr, - &(const ExtractLEdgeBMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {0, mr->edge_loose_len}, - }, - extractors); - } - } - if (iter_type & MR_ITER_LVERT) { - if (is_mesh) { - extract_iter_lvert_mesh(mr, - &(const ExtractLVertMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {0, mr->vert_loose_len}, - }, - extractors); - } - else { - extract_iter_lvert_bm(mr, - &(const ExtractLVertBMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {0, mr->vert_loose_len}, - }, - extractors); - } - } - extract_finish(mr, cache, extractors); -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name ExtractTaskData - * \{ */ -typedef struct ExtractTaskData { - void *next, *prev; - const MeshRenderData *mr; - struct MeshBatchCache *cache; - MeshExtractRunDataArray *extractors; - eMRIterType iter_type; - int start, end; - /** Decremented each time a task is finished. */ - int32_t *task_counter; - MeshBufferCache *mbc; -} ExtractTaskData; - -static ExtractTaskData *extract_extract_iter_task_data_create_mesh( - const MeshRenderData *mr, - struct MeshBatchCache *cache, - MeshExtractRunDataArray *extractors, - MeshBufferCache *mbc, - int32_t *task_counter) -{ - ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), __func__); - taskdata->next = NULL; - taskdata->prev = NULL; - taskdata->mr = mr; - taskdata->cache = cache; - taskdata->mbc = mbc; - - /* #UserData is shared between the iterations as it holds counters to detect if the - * extraction is finished. To make sure the duplication of the user_data does not create a new - * instance of the counters we allocate the user_data in its own container. - * - * This structure makes sure that when extract_init is called, that the user data of all - * iterations are updated. */ - taskdata->extractors = extractors; - taskdata->task_counter = task_counter; - - extracts_flags_get(extractors, &taskdata->iter_type, NULL); - taskdata->start = 0; - taskdata->end = INT_MAX; - return taskdata; -} - -static void extract_task_data_free(void *data) -{ - ExtractTaskData *task_data = data; - MEM_SAFE_FREE(task_data->extractors); - MEM_freeN(task_data); -} - -BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, - const eMRIterType iter_type, - int start, - int end, - MeshExtractRunDataArray *extractors) -{ - switch (mr->extract_type) { - case MR_EXTRACT_BMESH: - if (iter_type & MR_ITER_LOOPTRI) { - extract_iter_looptri_bm(mr, - &(const ExtractTriBMesh_Params){ - .looptris = mr->edit_bmesh->looptris, - .tri_range = {start, min_ii(mr->tri_len, end)}, - }, - extractors); - } - if (iter_type & MR_ITER_POLY) { - extract_iter_poly_bm(mr, - &(const ExtractPolyBMesh_Params){ - .poly_range = {start, min_ii(mr->poly_len, end)}, - }, - extractors); - } - if (iter_type & MR_ITER_LEDGE) { - extract_iter_ledge_bm(mr, - &(const ExtractLEdgeBMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, - }, - extractors); - } - if (iter_type & MR_ITER_LVERT) { - extract_iter_lvert_bm(mr, - &(const ExtractLVertBMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, - }, - extractors); - } - break; - case MR_EXTRACT_MAPPED: - case MR_EXTRACT_MESH: - if (iter_type & MR_ITER_LOOPTRI) { - extract_iter_looptri_mesh(mr, - &(const ExtractTriMesh_Params){ - .mlooptri = mr->mlooptri, - .tri_range = {start, min_ii(mr->tri_len, end)}, - }, - extractors); - } - if (iter_type & MR_ITER_POLY) { - extract_iter_poly_mesh(mr, - &(const ExtractPolyMesh_Params){ - .poly_range = {start, min_ii(mr->poly_len, end)}, - }, - extractors); - } - if (iter_type & MR_ITER_LEDGE) { - extract_iter_ledge_mesh(mr, - &(const ExtractLEdgeMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, - }, - extractors); - } - if (iter_type & MR_ITER_LVERT) { - extract_iter_lvert_mesh(mr, - &(const ExtractLVertMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, - }, - extractors); - } - break; - } -} - -static void extract_task_init(ExtractTaskData *data) -{ - extract_init(data->mr, data->cache, data->extractors, data->mbc); -} - -static void extract_task_run(void *__restrict taskdata) -{ - ExtractTaskData *data = (ExtractTaskData *)taskdata; - mesh_extract_iter(data->mr, data->iter_type, data->start, data->end, data->extractors); - - /* If this is the last task, we do the finish function. */ - int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); - if (remainin_tasks == 0) { - extract_finish(data->mr, data->cache, data->extractors); - } -} - -static void extract_task_init_and_run(void *__restrict taskdata) -{ - ExtractTaskData *data = (ExtractTaskData *)taskdata; - extract_run_and_finish_init(data->mr, data->cache, data->extractors, data->iter_type, data->mbc); -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Task Node - Update Mesh Render Data - * \{ */ -typedef struct MeshRenderDataUpdateTaskData { - MeshRenderData *mr; - eMRIterType iter_type; - eMRDataType data_flag; -} MeshRenderDataUpdateTaskData; - -static void mesh_render_data_update_task_data_free(MeshRenderDataUpdateTaskData *taskdata) -{ - BLI_assert(taskdata); - MeshRenderData *mr = taskdata->mr; - mesh_render_data_free(mr); - MEM_freeN(taskdata); -} - -static void mesh_extract_render_data_node_exec(void *__restrict task_data) -{ - MeshRenderDataUpdateTaskData *update_task_data = task_data; - MeshRenderData *mr = update_task_data->mr; - const eMRIterType iter_type = update_task_data->iter_type; - const eMRDataType data_flag = update_task_data->data_flag; - - mesh_render_data_update_normals(mr, iter_type, data_flag); - mesh_render_data_update_looptris(mr, iter_type, data_flag); -} - -static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph, - MeshRenderData *mr, - const eMRIterType iter_type, - const eMRDataType data_flag) -{ - MeshRenderDataUpdateTaskData *task_data = MEM_mallocN(sizeof(MeshRenderDataUpdateTaskData), - __func__); - task_data->mr = mr; - task_data->iter_type = iter_type; - task_data->data_flag = data_flag; - - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, - mesh_extract_render_data_node_exec, - task_data, - (TaskGraphNodeFreeFunction)mesh_render_data_update_task_data_free); - return task_node; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Task Node - Extract Single Threaded - * \{ */ - -static struct TaskNode *extract_single_threaded_task_node_create(struct TaskGraph *task_graph, - ExtractTaskData *task_data) -{ - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, - extract_task_init_and_run, - task_data, - (TaskGraphNodeFreeFunction)extract_task_data_free); - return task_node; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Task Node - UserData Initializer - * \{ */ -typedef struct UserDataInitTaskData { - ExtractTaskData *td; - int32_t task_counter; - -} UserDataInitTaskData; - -static void user_data_init_task_data_free(UserDataInitTaskData *taskdata) -{ - BLI_assert(taskdata); - extract_task_data_free(taskdata->td); - MEM_freeN(taskdata); -} - -static void user_data_init_task_data_exec(void *__restrict task_data) -{ - UserDataInitTaskData *extract_task_data = task_data; - ExtractTaskData *taskdata_base = extract_task_data->td; - extract_task_init(taskdata_base); -} - -static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_graph, - UserDataInitTaskData *task_data) -{ - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, - user_data_init_task_data_exec, - task_data, - (TaskGraphNodeFreeFunction)user_data_init_task_data_free); - return task_node; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Loop - * \{ */ - -static void extract_range_task_create(struct TaskGraph *task_graph, - struct TaskNode *task_node_user_data_init, - ExtractTaskData *taskdata, - const eMRIterType type, - int start, - int length) -{ - taskdata = MEM_dupallocN(taskdata); - atomic_add_and_fetch_int32(taskdata->task_counter, 1); - taskdata->iter_type = type; - taskdata->start = start; - taskdata->end = start + length; - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, extract_task_run, taskdata, MEM_freeN); - BLI_task_graph_edge_create(task_node_user_data_init, task_node); -} - -static int extract_range_task_num_elements_get(const MeshRenderData *mr, - const eMRIterType iter_type) -{ - /* Divide task into sensible chunks. */ - int iter_len = 0; - if (iter_type & MR_ITER_LOOPTRI) { - iter_len += mr->tri_len; - } - if (iter_type & MR_ITER_POLY) { - iter_len += mr->poly_len; - } - if (iter_type & MR_ITER_LEDGE) { - iter_len += mr->edge_loose_len; - } - if (iter_type & MR_ITER_LVERT) { - iter_len += mr->vert_loose_len; - } - return iter_len; -} - -static int extract_range_task_chunk_size_get(const MeshRenderData *mr, - const eMRIterType iter_type, - const int num_threads) -{ - /* Divide task into sensible chunks. */ - const int num_elements = extract_range_task_num_elements_get(mr, iter_type); - int range_len = (num_elements + num_threads) / num_threads; - CLAMP_MIN(range_len, CHUNK_SIZE); - return range_len; -} - -static void extract_task_in_ranges_create(struct TaskGraph *task_graph, - struct TaskNode *task_node_user_data_init, - ExtractTaskData *taskdata_base, - const int num_threads) -{ - const MeshRenderData *mr = taskdata_base->mr; - const int range_len = extract_range_task_chunk_size_get( - mr, taskdata_base->iter_type, num_threads); - - if (taskdata_base->iter_type & MR_ITER_LOOPTRI) { - for (int i = 0; i < mr->tri_len; i += range_len) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LOOPTRI, i, range_len); - } - } - if (taskdata_base->iter_type & MR_ITER_POLY) { - for (int i = 0; i < mr->poly_len; i += range_len) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata_base, MR_ITER_POLY, i, range_len); - } - } - if (taskdata_base->iter_type & MR_ITER_LEDGE) { - for (int i = 0; i < mr->edge_loose_len; i += range_len) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LEDGE, i, range_len); - } - } - if (taskdata_base->iter_type & MR_ITER_LVERT) { - for (int i = 0; i < mr->vert_loose_len; i += range_len) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LVERT, i, range_len); - } - } -} - -void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, - MeshBatchCache *cache, - MeshBufferCache *mbc, - MeshBufferExtractionCache *extraction_cache, - Mesh *me, - - const bool is_editmode, - const bool is_paint_mode, - const bool is_mode_active, - const float obmat[4][4], - const bool do_final, - const bool do_uvedit, - const bool use_subsurf_fdots, - const DRW_MeshCDMask *cd_layer_used, - const Scene *scene, - const ToolSettings *ts, - const bool use_hide) -{ - /* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph. - * This sub-graph starts with an extract_render_data_node. This fills/converts the required - * data from Mesh. - * - * Small extractions and extractions that can't be multi-threaded are grouped in a single - * `extract_single_threaded_task_node`. - * - * Other extractions will create a node for each loop exceeding 8192 items. these nodes are - * linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the - * user_data needed for the extraction based on the data extracted from the mesh. - * counters are used to check if the finalize of a task has to be called. - * - * Mesh extraction sub graph - * - * +----------------------+ - * +-----> | extract_task1_loop_1 | - * | +----------------------+ - * +------------------+ +----------------------+ +----------------------+ - * | mesh_render_data | --> | | --> | extract_task1_loop_2 | - * +------------------+ | | +----------------------+ - * | | | +----------------------+ - * | | user_data_init | --> | extract_task2_loop_1 | - * v | | +----------------------+ - * +------------------+ | | +----------------------+ - * | single_threaded | | | --> | extract_task2_loop_2 | - * +------------------+ +----------------------+ +----------------------+ - * | +----------------------+ - * +-----> | extract_task2_loop_3 | - * +----------------------+ - */ - const bool do_lines_loose_subbuffer = mbc->ibo.lines_loose != NULL; - const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || - GPU_use_hq_normals_workaround(); - - /* Create an array containing all the extractors that needs to be executed. */ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - -#define EXTRACT_ADD_REQUESTED(type, type_lowercase, name) \ - do { \ - if (DRW_##type_lowercase##_requested(mbc->type_lowercase.name)) { \ - const MeshExtract *extractor = mesh_extract_override_get( \ - &extract_##name, do_hq_normals, do_lines_loose_subbuffer); \ - mesh_extract_run_data_array_add(&extractors, extractor); \ - } \ - } while (0) - - EXTRACT_ADD_REQUESTED(VBO, vbo, pos_nor); - EXTRACT_ADD_REQUESTED(VBO, vbo, lnor); - EXTRACT_ADD_REQUESTED(VBO, vbo, uv); - EXTRACT_ADD_REQUESTED(VBO, vbo, tan); - EXTRACT_ADD_REQUESTED(VBO, vbo, vcol); - EXTRACT_ADD_REQUESTED(VBO, vbo, sculpt_data); - EXTRACT_ADD_REQUESTED(VBO, vbo, orco); - EXTRACT_ADD_REQUESTED(VBO, vbo, edge_fac); - EXTRACT_ADD_REQUESTED(VBO, vbo, weights); - EXTRACT_ADD_REQUESTED(VBO, vbo, edit_data); - EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_data); - EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_area); - EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_angle); - EXTRACT_ADD_REQUESTED(VBO, vbo, mesh_analysis); - EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_pos); - EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_nor); - EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_uv); - EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_edituv_data); - EXTRACT_ADD_REQUESTED(VBO, vbo, poly_idx); - EXTRACT_ADD_REQUESTED(VBO, vbo, edge_idx); - EXTRACT_ADD_REQUESTED(VBO, vbo, vert_idx); - EXTRACT_ADD_REQUESTED(VBO, vbo, fdot_idx); - EXTRACT_ADD_REQUESTED(VBO, vbo, skin_roots); - - EXTRACT_ADD_REQUESTED(IBO, ibo, tris); - EXTRACT_ADD_REQUESTED(IBO, ibo, lines); - EXTRACT_ADD_REQUESTED(IBO, ibo, points); - EXTRACT_ADD_REQUESTED(IBO, ibo, fdots); - EXTRACT_ADD_REQUESTED(IBO, ibo, lines_paint_mask); - EXTRACT_ADD_REQUESTED(IBO, ibo, lines_adjacency); - EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_tris); - EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_lines); - EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_points); - EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_fdots); - -#undef EXTRACT_ADD_REQUESTED - - if (extractors.len == 0) { - return; - } - -#ifdef DEBUG_TIME - double rdata_start = PIL_check_seconds_timer(); -#endif - - eMRIterType iter_type; - eMRDataType data_flag; - extracts_flags_get(&extractors, &iter_type, &data_flag); - - MeshRenderData *mr = mesh_render_data_create(me, - extraction_cache, - is_editmode, - is_paint_mode, - is_mode_active, - obmat, - do_final, - do_uvedit, - cd_layer_used, - ts, - iter_type); - mr->use_hide = use_hide; - mr->use_subsurf_fdots = use_subsurf_fdots; - mr->use_final_mesh = do_final; - -#ifdef DEBUG_TIME - double rdata_end = PIL_check_seconds_timer(); -#endif - - struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( - task_graph, mr, iter_type, data_flag); - - /* Simple heuristic. */ - const bool use_thread = (mr->loop_len + mr->loop_loose_len) > CHUNK_SIZE; - - if (use_thread) { - uint threads_to_use = 0; - - /* First run the requested extractors that do not support asynchronous ranges. */ - for (int i = 0; i < extractors.len; i++) { - const MeshExtract *extractor = extractors.items[i].extractor; - if (!extractor->use_threading) { - MeshExtractRunDataArray *single_threaded_extractors = MEM_callocN( - sizeof(MeshExtractRunDataArray), - "mesh_buffer_cache_create_requested.single_threaded_extractors"); - mesh_extract_run_data_array_add(single_threaded_extractors, extractor); - ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( - mr, cache, single_threaded_extractors, mbc, NULL); - struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, - taskdata); - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); - } - threads_to_use++; - } - - /* Distribute the remaining extractors into ranges per core. */ - MeshExtractRunDataArray *multi_threaded_extractors = MEM_callocN( - sizeof(MeshExtractRunDataArray), - "mesh_buffer_cache_create_requested.multi_threaded_extractors"); - mesh_extract_run_data_array_filter_threading(&extractors, multi_threaded_extractors); - if (multi_threaded_extractors->len) { - /* - * Determine the number of thread to use for multithreading. - * Thread can be used for single threaded tasks. These typically take longer to execute so - * fill the rest of the threads for range operations. - */ - int num_threads = BLI_task_scheduler_num_threads(); - if (threads_to_use < num_threads) { - num_threads -= threads_to_use; - } - - UserDataInitTaskData *user_data_init_task_data = MEM_callocN( - sizeof(UserDataInitTaskData), - "mesh_buffer_cache_create_requested.user_data_init_task_data"); - struct TaskNode *task_node_user_data_init = user_data_init_task_node_create( - task_graph, user_data_init_task_data); - - user_data_init_task_data->td = extract_extract_iter_task_data_create_mesh( - mr, cache, multi_threaded_extractors, mbc, &user_data_init_task_data->task_counter); - - extract_task_in_ranges_create( - task_graph, task_node_user_data_init, user_data_init_task_data->td, num_threads); - - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init); - } - else { - /* No tasks created freeing extractors list. */ - MEM_freeN(multi_threaded_extractors); - } - } - else { - /* Run all requests on the same thread. */ - MeshExtractRunDataArray *extractors_copy = MEM_mallocN( - sizeof(MeshExtractRunDataArray), "mesh_buffer_cache_create_requested.extractors_copy"); - memcpy(extractors_copy, &extractors, sizeof(MeshExtractRunDataArray)); - ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( - mr, cache, extractors_copy, mbc, NULL); - - struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata); - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); - } - - /* Trigger the sub-graph for this mesh. */ - BLI_task_graph_node_push_work(task_node_mesh_render_data); - -#ifdef DEBUG_TIME - BLI_task_graph_work_and_wait(task_graph); - double end = PIL_check_seconds_timer(); - - static double avg = 0; - static double avg_fps = 0; - static double avg_rdata = 0; - static double end_prev = 0; - - if (end_prev == 0) { - end_prev = end; - } - - avg = avg * 0.95 + (end - rdata_end) * 0.05; - avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05; - avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05; - - printf( - "rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000); - - end_prev = end; -#endif -} - -/** \} */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc new file mode 100644 index 00000000000..b3ebce98524 --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -0,0 +1,1006 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ +#include "MEM_guardedalloc.h" + +#include "atomic_ops.h" + +#include "DNA_mesh_types.h" +#include "DNA_scene_types.h" + +#include "BLI_task.h" +#include "BLI_vector.hh" + +#include "BKE_editmesh.h" + +#include "GPU_capabilities.h" + +#include "draw_cache_extract.h" +#include "draw_cache_extract_mesh_private.h" +#include "draw_cache_inline.h" + +// #define DEBUG_TIME + +#ifdef DEBUG_TIME +# include "PIL_time_utildefines.h" +#endif + +#define CHUNK_SIZE 8192 + +namespace blender::draw { + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract Struct + * \{ */ + +struct MeshExtractRunData { + const MeshExtract *extractor; + void *buffer; + void *user_data; +}; + +using MeshExtractRunDataArray = blender::Vector; + +static void mesh_extract_run_data_array_add(MeshExtractRunDataArray &array, + const MeshExtract *extractor) +{ + MeshExtractRunData run_data; + run_data.extractor = extractor; + run_data.buffer = NULL; + run_data.user_data = NULL; + array.append(run_data); +} + +static void mesh_extract_run_data_array_filter_iter_type(const MeshExtractRunDataArray &src, + MeshExtractRunDataArray &dst, + eMRIterType iter_type) +{ + for (const MeshExtractRunData &data : src) { + const MeshExtract *extractor = data.extractor; + if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { + BLI_assert(extractor->iter_looptri_mesh); + dst.append(data); + continue; + } + if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { + BLI_assert(extractor->iter_poly_mesh); + dst.append(data); + continue; + } + if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { + BLI_assert(extractor->iter_ledge_mesh); + dst.append(data); + continue; + } + if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { + BLI_assert(extractor->iter_lvert_mesh); + dst.append(data); + continue; + } + } +} + +static void mesh_extract_run_data_array_filter_threading( + const MeshExtractRunDataArray &src, MeshExtractRunDataArray &dst_multi_threaded) +{ + for (const MeshExtractRunData &data : src) { + const MeshExtract *extractor = data.extractor; + if (extractor->use_threading) { + mesh_extract_run_data_array_add(dst_multi_threaded, extractor); + } + } +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract + * \{ */ + +static void extracts_flags_get(const MeshExtractRunDataArray &extractors, + eMRIterType *r_iter_type, + eMRDataType *r_data_flag) +{ + eMRIterType iter_type = static_cast(0); + eMRDataType data_flag = static_cast(0); + + for (const MeshExtractRunData &data : extractors) { + const MeshExtract *extractor = data.extractor; + iter_type |= mesh_extract_iter_type(extractor); + data_flag |= extractor->data_flag; + } + + if (r_iter_type) { + *r_iter_type = iter_type; + } + if (r_data_flag) { + *r_data_flag = data_flag; + } +} + +BLI_INLINE void extract_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + MeshExtractRunDataArray &extractors, + MeshBufferCache *mbc) +{ + /* Multi thread. */ + for (MeshExtractRunData &run_data : extractors) { + const MeshExtract *extractor = run_data.extractor; + run_data.buffer = mesh_extract_buffer_get(extractor, mbc); + run_data.user_data = extractor->init(mr, cache, run_data.buffer); + } +} + +BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr, + const ExtractTriBMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LOOPTRI); + + EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, elt_index, params) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_looptri_bm(mr, elt, elt_index, run_data.user_data); + } + } + EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr, + const ExtractTriMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LOOPTRI); + + EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, mlt_index, params) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_looptri_mesh(mr, mlt, mlt_index, run_data.user_data); + } + } + EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr, + const ExtractPolyBMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_POLY); + + EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_poly_bm(mr, f, f_index, run_data.user_data); + } + } + EXTRACT_POLY_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr, + const ExtractPolyMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_POLY); + + EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_poly_mesh(mr, mp, mp_index, run_data.user_data); + } + } + EXTRACT_POLY_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr, + const ExtractLEdgeBMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LEDGE); + + EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_ledge_bm(mr, eed, ledge_index, run_data.user_data); + } + } + EXTRACT_LEDGE_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr, + const ExtractLEdgeMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LEDGE); + + EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_ledge_mesh(mr, med, ledge_index, run_data.user_data); + } + } + EXTRACT_LEDGE_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr, + const ExtractLVertBMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LVERT); + + EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_lvert_bm(mr, eve, lvert_index, run_data.user_data); + } + } + EXTRACT_LVERT_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr, + const ExtractLVertMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LVERT); + + EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_lvert_mesh(mr, mv, lvert_index, run_data.user_data); + } + } + EXTRACT_LVERT_FOREACH_MESH_END; +} + +BLI_INLINE void extract_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + const MeshExtractRunDataArray &extractors) +{ + for (const MeshExtractRunData &run_data : extractors) { + const MeshExtract *extractor = run_data.extractor; + if (extractor->finish) { + extractor->finish(mr, cache, run_data.buffer, run_data.user_data); + } + } +} + +/* Single Thread. */ +BLI_INLINE void extract_run_and_finish_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + MeshExtractRunDataArray &extractors, + eMRIterType iter_type, + MeshBufferCache *mbc) +{ + extract_init(mr, cache, extractors, mbc); + + bool is_mesh = mr->extract_type != MR_EXTRACT_BMESH; + if (iter_type & MR_ITER_LOOPTRI) { + if (is_mesh) { + ExtractTriMesh_Params params; + params.mlooptri = mr->mlooptri; + params.tri_range[0] = 0; + params.tri_range[1] = mr->tri_len; + extract_iter_looptri_mesh(mr, ¶ms, extractors); + } + else { + ExtractTriBMesh_Params params; + params.looptris = mr->edit_bmesh->looptris; + params.tri_range[0] = 0; + params.tri_range[1] = mr->tri_len; + extract_iter_looptri_bm(mr, ¶ms, extractors); + } + } + if (iter_type & MR_ITER_POLY) { + if (is_mesh) { + ExtractPolyMesh_Params params; + params.poly_range[0] = 0; + params.poly_range[1] = mr->poly_len; + extract_iter_poly_mesh(mr, ¶ms, extractors); + } + else { + ExtractPolyBMesh_Params params; + params.poly_range[0] = 0; + params.poly_range[1] = mr->poly_len; + extract_iter_poly_bm(mr, ¶ms, extractors); + } + } + if (iter_type & MR_ITER_LEDGE) { + if (is_mesh) { + ExtractLEdgeMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = 0; + params.ledge_range[1] = mr->edge_loose_len; + extract_iter_ledge_mesh(mr, ¶ms, extractors); + } + else { + ExtractLEdgeBMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = 0; + params.ledge_range[1] = mr->edge_loose_len; + extract_iter_ledge_bm(mr, ¶ms, extractors); + } + } + if (iter_type & MR_ITER_LVERT) { + if (is_mesh) { + ExtractLVertMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = 0; + params.lvert_range[1] = mr->vert_loose_len; + extract_iter_lvert_mesh(mr, ¶ms, extractors); + } + else { + ExtractLVertBMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = 0; + params.lvert_range[1] = mr->vert_loose_len; + extract_iter_lvert_bm(mr, ¶ms, extractors); + } + } + extract_finish(mr, cache, extractors); +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name ExtractTaskData + * \{ */ +struct ExtractTaskData { + void *next = nullptr; + void *prev = nullptr; + + const MeshRenderData *mr = nullptr; + MeshBatchCache *cache = nullptr; + /* #UserData is shared between the iterations as it holds counters to detect if the + * extraction is finished. To make sure the duplication of the user_data does not create a new + * instance of the counters we allocate the user_data in its own container. + * + * This structure makes sure that when extract_init is called, that the user data of all + * iterations are updated. */ + + MeshExtractRunDataArray *extractors = nullptr; + MeshBufferCache *mbc = nullptr; + int32_t *task_counter = nullptr; + + eMRIterType iter_type; + int start = 0; + int end = INT_MAX; + /** Decremented each time a task is finished. */ + + ExtractTaskData(const MeshRenderData *mr, + struct MeshBatchCache *cache, + MeshExtractRunDataArray *extractors, + MeshBufferCache *mbc, + int32_t *task_counter) + : mr(mr), cache(cache), extractors(extractors), mbc(mbc), task_counter(task_counter) + { + extracts_flags_get(*extractors, &iter_type, NULL); + }; + + ExtractTaskData(const ExtractTaskData &src) = default; + + ~ExtractTaskData() + { + delete extractors; + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("DRW:ExtractTaskData") +#endif +}; + +static ExtractTaskData *extract_extract_iter_task_data_create_mesh( + const MeshRenderData *mr, + MeshBatchCache *cache, + MeshExtractRunDataArray *extractors, + MeshBufferCache *mbc, + int32_t *task_counter) + +{ + ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbc, task_counter); + return taskdata; +} + +static void extract_task_data_free(void *data) +{ + ExtractTaskData *task_data = static_cast(data); + delete task_data; +} + +static void extract_task_data_free_ex(void *data) +{ + ExtractTaskData *task_data = static_cast(data); + task_data->extractors = nullptr; + delete task_data; +} + +BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, + const eMRIterType iter_type, + int start, + int end, + MeshExtractRunDataArray &extractors) +{ + switch (mr->extract_type) { + case MR_EXTRACT_BMESH: + if (iter_type & MR_ITER_LOOPTRI) { + ExtractTriBMesh_Params params; + params.looptris = mr->edit_bmesh->looptris; + params.tri_range[0] = start; + params.tri_range[1] = min_ii(mr->tri_len, end); + extract_iter_looptri_bm(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_POLY) { + ExtractPolyBMesh_Params params; + params.poly_range[0] = start; + params.poly_range[1] = min_ii(mr->poly_len, end); + extract_iter_poly_bm(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LEDGE) { + ExtractLEdgeBMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = start; + params.ledge_range[1] = min_ii(mr->edge_loose_len, end); + extract_iter_ledge_bm(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LVERT) { + ExtractLVertBMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = start; + params.lvert_range[1] = min_ii(mr->vert_loose_len, end); + extract_iter_lvert_bm(mr, ¶ms, extractors); + } + break; + case MR_EXTRACT_MAPPED: + case MR_EXTRACT_MESH: + if (iter_type & MR_ITER_LOOPTRI) { + ExtractTriMesh_Params params; + params.mlooptri = mr->mlooptri; + params.tri_range[0] = start; + params.tri_range[1] = min_ii(mr->tri_len, end); + extract_iter_looptri_mesh(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_POLY) { + ExtractPolyMesh_Params params; + params.poly_range[0] = start; + params.poly_range[1] = min_ii(mr->poly_len, end); + extract_iter_poly_mesh(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LEDGE) { + ExtractLEdgeMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = start; + params.ledge_range[1] = min_ii(mr->edge_loose_len, end); + extract_iter_ledge_mesh(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LVERT) { + ExtractLVertMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = start; + params.lvert_range[1] = min_ii(mr->vert_loose_len, end); + extract_iter_lvert_mesh(mr, ¶ms, extractors); + } + break; + } +} + +static void extract_task_init(ExtractTaskData *data) +{ + extract_init(data->mr, data->cache, *data->extractors, data->mbc); +} + +static void extract_task_run(void *__restrict taskdata) +{ + ExtractTaskData *data = (ExtractTaskData *)taskdata; + mesh_extract_iter(data->mr, data->iter_type, data->start, data->end, *data->extractors); + + /* If this is the last task, we do the finish function. */ + int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); + if (remainin_tasks == 0) { + extract_finish(data->mr, data->cache, *data->extractors); + } +} + +static void extract_task_init_and_run(void *__restrict taskdata) +{ + ExtractTaskData *data = (ExtractTaskData *)taskdata; + extract_run_and_finish_init( + data->mr, data->cache, *data->extractors, data->iter_type, data->mbc); +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Task Node - Update Mesh Render Data + * \{ */ +struct MeshRenderDataUpdateTaskData { + MeshRenderData *mr = nullptr; + eMRIterType iter_type; + eMRDataType data_flag; + + ~MeshRenderDataUpdateTaskData() + { + mesh_render_data_free(mr); + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("DRW:MeshRenderDataUpdateTaskData") +#endif +}; + +static void mesh_render_data_update_task_data_free(MeshRenderDataUpdateTaskData *taskdata) +{ + BLI_assert(taskdata); + delete taskdata; +} + +static void mesh_extract_render_data_node_exec(void *__restrict task_data) +{ + MeshRenderDataUpdateTaskData *update_task_data = static_cast( + task_data); + MeshRenderData *mr = update_task_data->mr; + const eMRIterType iter_type = update_task_data->iter_type; + const eMRDataType data_flag = update_task_data->data_flag; + + mesh_render_data_update_normals(mr, iter_type, data_flag); + mesh_render_data_update_looptris(mr, iter_type, data_flag); +} + +static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph, + MeshRenderData *mr, + const eMRIterType iter_type, + const eMRDataType data_flag) +{ + MeshRenderDataUpdateTaskData *task_data = new (MeshRenderDataUpdateTaskData); + task_data->mr = mr; + task_data->iter_type = iter_type; + task_data->data_flag = data_flag; + + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, + mesh_extract_render_data_node_exec, + task_data, + (TaskGraphNodeFreeFunction)mesh_render_data_update_task_data_free); + return task_node; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Task Node - Extract Single Threaded + * \{ */ + +static struct TaskNode *extract_single_threaded_task_node_create(struct TaskGraph *task_graph, + ExtractTaskData *task_data) +{ + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, + extract_task_init_and_run, + task_data, + (TaskGraphNodeFreeFunction)extract_task_data_free); + return task_node; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Task Node - UserData Initializer + * \{ */ +struct UserDataInitTaskData { + ExtractTaskData *td; + int32_t task_counter = 0; + + ~UserDataInitTaskData() + { + extract_task_data_free(td); + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("DRW:UserDataInitTaskData") +#endif +}; + +static void user_data_init_task_data_free(UserDataInitTaskData *taskdata) +{ + delete taskdata; +} + +static void user_data_init_task_data_exec(void *__restrict task_data) +{ + UserDataInitTaskData *extract_task_data = static_cast(task_data); + ExtractTaskData *taskdata_base = extract_task_data->td; + extract_task_init(taskdata_base); +} + +static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_graph, + UserDataInitTaskData *task_data) +{ + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, + user_data_init_task_data_exec, + task_data, + (TaskGraphNodeFreeFunction)user_data_init_task_data_free); + return task_node; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Loop + * \{ */ + +static void extract_range_task_create(struct TaskGraph *task_graph, + struct TaskNode *task_node_user_data_init, + ExtractTaskData *taskdata, + const eMRIterType type, + int start, + int length) +{ + taskdata = new ExtractTaskData(*taskdata); + atomic_add_and_fetch_int32(taskdata->task_counter, 1); + taskdata->iter_type = type; + taskdata->start = start; + taskdata->end = start + length; + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, extract_task_run, taskdata, extract_task_data_free_ex); + BLI_task_graph_edge_create(task_node_user_data_init, task_node); +} + +static int extract_range_task_num_elements_get(const MeshRenderData *mr, + const eMRIterType iter_type) +{ + /* Divide task into sensible chunks. */ + int iter_len = 0; + if (iter_type & MR_ITER_LOOPTRI) { + iter_len += mr->tri_len; + } + if (iter_type & MR_ITER_POLY) { + iter_len += mr->poly_len; + } + if (iter_type & MR_ITER_LEDGE) { + iter_len += mr->edge_loose_len; + } + if (iter_type & MR_ITER_LVERT) { + iter_len += mr->vert_loose_len; + } + return iter_len; +} + +static int extract_range_task_chunk_size_get(const MeshRenderData *mr, + const eMRIterType iter_type, + const int num_threads) +{ + /* Divide task into sensible chunks. */ + const int num_elements = extract_range_task_num_elements_get(mr, iter_type); + int range_len = (num_elements + num_threads) / num_threads; + CLAMP_MIN(range_len, CHUNK_SIZE); + return range_len; +} + +static void extract_task_in_ranges_create(struct TaskGraph *task_graph, + struct TaskNode *task_node_user_data_init, + ExtractTaskData *taskdata_base, + const int num_threads) +{ + const MeshRenderData *mr = taskdata_base->mr; + const int range_len = extract_range_task_chunk_size_get( + mr, taskdata_base->iter_type, num_threads); + + if (taskdata_base->iter_type & MR_ITER_LOOPTRI) { + for (int i = 0; i < mr->tri_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LOOPTRI, i, range_len); + } + } + if (taskdata_base->iter_type & MR_ITER_POLY) { + for (int i = 0; i < mr->poly_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_POLY, i, range_len); + } + } + if (taskdata_base->iter_type & MR_ITER_LEDGE) { + for (int i = 0; i < mr->edge_loose_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LEDGE, i, range_len); + } + } + if (taskdata_base->iter_type & MR_ITER_LVERT) { + for (int i = 0; i < mr->vert_loose_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LVERT, i, range_len); + } + } +} + +static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, + MeshBatchCache *cache, + MeshBufferCache *mbc, + MeshBufferExtractionCache *extraction_cache, + Mesh *me, + + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const bool use_subsurf_fdots, + const DRW_MeshCDMask *cd_layer_used, + const Scene *scene, + const ToolSettings *ts, + const bool use_hide) +{ + /* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph. + * This sub-graph starts with an extract_render_data_node. This fills/converts the required + * data from Mesh. + * + * Small extractions and extractions that can't be multi-threaded are grouped in a single + * `extract_single_threaded_task_node`. + * + * Other extractions will create a node for each loop exceeding 8192 items. these nodes are + * linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the + * user_data needed for the extraction based on the data extracted from the mesh. + * counters are used to check if the finalize of a task has to be called. + * + * Mesh extraction sub graph + * + * +----------------------+ + * +-----> | extract_task1_loop_1 | + * | +----------------------+ + * +------------------+ +----------------------+ +----------------------+ + * | mesh_render_data | --> | | --> | extract_task1_loop_2 | + * +------------------+ | | +----------------------+ + * | | | +----------------------+ + * | | user_data_init | --> | extract_task2_loop_1 | + * v | | +----------------------+ + * +------------------+ | | +----------------------+ + * | single_threaded | | | --> | extract_task2_loop_2 | + * +------------------+ +----------------------+ +----------------------+ + * | +----------------------+ + * +-----> | extract_task2_loop_3 | + * +----------------------+ + */ + const bool do_lines_loose_subbuffer = mbc->ibo.lines_loose != NULL; + const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || + GPU_use_hq_normals_workaround(); + + /* Create an array containing all the extractors that needs to be executed. */ + MeshExtractRunDataArray extractors; + +#define EXTRACT_ADD_REQUESTED(type, type_lowercase, name) \ + do { \ + if (DRW_##type_lowercase##_requested(mbc->type_lowercase.name)) { \ + const MeshExtract *extractor = mesh_extract_override_get( \ + &extract_##name, do_hq_normals, do_lines_loose_subbuffer); \ + mesh_extract_run_data_array_add(extractors, extractor); \ + } \ + } while (0) + + EXTRACT_ADD_REQUESTED(VBO, vbo, pos_nor); + EXTRACT_ADD_REQUESTED(VBO, vbo, lnor); + EXTRACT_ADD_REQUESTED(VBO, vbo, uv); + EXTRACT_ADD_REQUESTED(VBO, vbo, tan); + EXTRACT_ADD_REQUESTED(VBO, vbo, vcol); + EXTRACT_ADD_REQUESTED(VBO, vbo, sculpt_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, orco); + EXTRACT_ADD_REQUESTED(VBO, vbo, edge_fac); + EXTRACT_ADD_REQUESTED(VBO, vbo, weights); + EXTRACT_ADD_REQUESTED(VBO, vbo, edit_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_area); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_angle); + EXTRACT_ADD_REQUESTED(VBO, vbo, mesh_analysis); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_pos); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_nor); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_uv); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_edituv_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, poly_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, edge_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, vert_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdot_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, skin_roots); + + EXTRACT_ADD_REQUESTED(IBO, ibo, tris); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines); + EXTRACT_ADD_REQUESTED(IBO, ibo, points); + EXTRACT_ADD_REQUESTED(IBO, ibo, fdots); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines_paint_mask); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines_adjacency); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_tris); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_lines); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_points); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_fdots); + +#undef EXTRACT_ADD_REQUESTED + + if (extractors.is_empty()) { + return; + } + +#ifdef DEBUG_TIME + double rdata_start = PIL_check_seconds_timer(); +#endif + + eMRIterType iter_type; + eMRDataType data_flag; + extracts_flags_get(extractors, &iter_type, &data_flag); + + MeshRenderData *mr = mesh_render_data_create(me, + extraction_cache, + is_editmode, + is_paint_mode, + is_mode_active, + obmat, + do_final, + do_uvedit, + cd_layer_used, + ts, + iter_type); + mr->use_hide = use_hide; + mr->use_subsurf_fdots = use_subsurf_fdots; + mr->use_final_mesh = do_final; + +#ifdef DEBUG_TIME + double rdata_end = PIL_check_seconds_timer(); +#endif + + struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( + task_graph, mr, iter_type, data_flag); + + /* Simple heuristic. */ + const bool use_thread = (mr->loop_len + mr->loop_loose_len) > CHUNK_SIZE; + + if (use_thread) { + uint threads_to_use = 0; + + /* First run the requested extractors that do not support asynchronous ranges. */ + for (const MeshExtractRunData &run_data : extractors) { + const MeshExtract *extractor = run_data.extractor; + if (!extractor->use_threading) { + MeshExtractRunDataArray *single_threaded_extractors = new MeshExtractRunDataArray(); + mesh_extract_run_data_array_add(*single_threaded_extractors, extractor); + ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( + mr, cache, single_threaded_extractors, mbc, NULL); + struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, + taskdata); + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); + } + threads_to_use++; + } + + /* Distribute the remaining extractors into ranges per core. */ + MeshExtractRunDataArray *multi_threaded_extractors = new MeshExtractRunDataArray(); + mesh_extract_run_data_array_filter_threading(extractors, *multi_threaded_extractors); + if (!multi_threaded_extractors->is_empty()) { + /* + * Determine the number of thread to use for multithreading. + * Thread can be used for single threaded tasks. These typically take longer to execute so + * fill the rest of the threads for range operations. + */ + int num_threads = BLI_task_scheduler_num_threads(); + if (threads_to_use < num_threads) { + num_threads -= threads_to_use; + } + + UserDataInitTaskData *user_data_init_task_data = new UserDataInitTaskData(); + struct TaskNode *task_node_user_data_init = user_data_init_task_node_create( + task_graph, user_data_init_task_data); + + user_data_init_task_data->td = extract_extract_iter_task_data_create_mesh( + mr, cache, multi_threaded_extractors, mbc, &user_data_init_task_data->task_counter); + + extract_task_in_ranges_create( + task_graph, task_node_user_data_init, user_data_init_task_data->td, num_threads); + + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init); + } + else { + /* No tasks created freeing extractors list. */ + delete multi_threaded_extractors; + } + } + else { + /* Run all requests on the same thread. */ + MeshExtractRunDataArray *extractors_copy = new MeshExtractRunDataArray(extractors); + ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( + mr, cache, extractors_copy, mbc, NULL); + + struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata); + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); + } + + /* Trigger the sub-graph for this mesh. */ + BLI_task_graph_node_push_work(task_node_mesh_render_data); + +#ifdef DEBUG_TIME + BLI_task_graph_work_and_wait(task_graph); + double end = PIL_check_seconds_timer(); + + static double avg = 0; + static double avg_fps = 0; + static double avg_rdata = 0; + static double end_prev = 0; + + if (end_prev == 0) { + end_prev = end; + } + + avg = avg * 0.95 + (end - rdata_end) * 0.05; + avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05; + avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05; + + printf( + "rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000); + + end_prev = end; +#endif +} + +} // namespace blender::draw + +extern "C" { +void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, + MeshBatchCache *cache, + MeshBufferCache *mbc, + MeshBufferExtractionCache *extraction_cache, + Mesh *me, + + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const bool use_subsurf_fdots, + const DRW_MeshCDMask *cd_layer_used, + const Scene *scene, + const ToolSettings *ts, + const bool use_hide) +{ + blender::draw::mesh_buffer_cache_create_requested(task_graph, + cache, + mbc, + extraction_cache, + me, + is_editmode, + is_paint_mode, + is_mode_active, + obmat, + do_final, + do_uvedit, + use_subsurf_fdots, + cd_layer_used, + scene, + ts, + use_hide); +} + +} // extern "C" + +/** \} */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h index 3360b90139d..6dd2ef4c16f 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -34,6 +34,10 @@ #include "draw_cache_extract.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef enum eMRExtractType { MR_EXTRACT_BMESH, MR_EXTRACT_MAPPED, @@ -507,3 +511,7 @@ extern const MeshExtract extract_poly_idx; extern const MeshExtract extract_edge_idx; extern const MeshExtract extract_vert_idx; extern const MeshExtract extract_fdot_idx; + +#ifdef __cplusplus +} +#endif -- cgit v1.2.3 From f0d93a71df85e91ec4e06d2b37e24fb728edf2c2 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 1 Jun 2021 12:59:15 +0200 Subject: Cleanup: API for MeshExtractRunTimeData. --- .../blender/draw/intern/draw_cache_extract_mesh.cc | 246 ++++++++++----------- .../intern/draw_cache_extract_mesh_extractors.c | 76 +++---- .../draw/intern/draw_cache_extract_mesh_private.h | 2 +- 3 files changed, 159 insertions(+), 165 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index b3ebce98524..5a086805580 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -54,63 +54,80 @@ namespace blender::draw { /** \name Mesh Elements Extract Struct * \{ */ -struct MeshExtractRunData { +struct ExtractorRunData { + /* Extractor where this run data belongs to. */ const MeshExtract *extractor; - void *buffer; - void *user_data; -}; - -using MeshExtractRunDataArray = blender::Vector; + /* During iteration the VBO/IBO that is being build. */ + void *buffer = nullptr; + /* User data during iteration. Created in MeshExtract.init and passed along to other MeshExtract + * functions. */ + void *user_data = nullptr; -static void mesh_extract_run_data_array_add(MeshExtractRunDataArray &array, - const MeshExtract *extractor) -{ - MeshExtractRunData run_data; - run_data.extractor = extractor; - run_data.buffer = NULL; - run_data.user_data = NULL; - array.append(run_data); -} + ExtractorRunData(const MeshExtract *extractor) : extractor(extractor) + { + } +}; -static void mesh_extract_run_data_array_filter_iter_type(const MeshExtractRunDataArray &src, - MeshExtractRunDataArray &dst, - eMRIterType iter_type) -{ - for (const MeshExtractRunData &data : src) { - const MeshExtract *extractor = data.extractor; - if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { - BLI_assert(extractor->iter_looptri_mesh); - dst.append(data); - continue; - } - if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { - BLI_assert(extractor->iter_poly_mesh); - dst.append(data); - continue; +class ExtractorRunDatas : public Vector { + public: + void filter_into(ExtractorRunDatas &result, eMRIterType iter_type) const + { + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { + BLI_assert(extractor->iter_looptri_mesh); + result.append(data); + continue; + } + if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { + BLI_assert(extractor->iter_poly_mesh); + result.append(data); + continue; + } + if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { + BLI_assert(extractor->iter_ledge_mesh); + result.append(data); + continue; + } + if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { + BLI_assert(extractor->iter_lvert_mesh); + result.append(data); + continue; + } } - if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { - BLI_assert(extractor->iter_ledge_mesh); - dst.append(data); - continue; + } + + void filter_threaded_extractors_into(ExtractorRunDatas &result) + { + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + if (extractor->use_threading) { + result.append(extractor); + } } - if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { - BLI_assert(extractor->iter_lvert_mesh); - dst.append(data); - continue; + } + + eMRIterType iter_types() + { + eMRIterType iter_type = static_cast(0); + + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + iter_type |= mesh_extract_iter_type(extractor); } + return iter_type; } -} -static void mesh_extract_run_data_array_filter_threading( - const MeshExtractRunDataArray &src, MeshExtractRunDataArray &dst_multi_threaded) -{ - for (const MeshExtractRunData &data : src) { - const MeshExtract *extractor = data.extractor; - if (extractor->use_threading) { - mesh_extract_run_data_array_add(dst_multi_threaded, extractor); + eMRDataType data_types() + { + eMRDataType data_type = static_cast(0); + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + data_type |= extractor->data_type; } + return data_type; } -} +}; /** \} */ @@ -118,34 +135,13 @@ static void mesh_extract_run_data_array_filter_threading( /** \name Extract * \{ */ -static void extracts_flags_get(const MeshExtractRunDataArray &extractors, - eMRIterType *r_iter_type, - eMRDataType *r_data_flag) -{ - eMRIterType iter_type = static_cast(0); - eMRDataType data_flag = static_cast(0); - - for (const MeshExtractRunData &data : extractors) { - const MeshExtract *extractor = data.extractor; - iter_type |= mesh_extract_iter_type(extractor); - data_flag |= extractor->data_flag; - } - - if (r_iter_type) { - *r_iter_type = iter_type; - } - if (r_data_flag) { - *r_data_flag = data_flag; - } -} - BLI_INLINE void extract_init(const MeshRenderData *mr, struct MeshBatchCache *cache, - MeshExtractRunDataArray &extractors, + ExtractorRunDatas &extractors, MeshBufferCache *mbc) { /* Multi thread. */ - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { const MeshExtract *extractor = run_data.extractor; run_data.buffer = mesh_extract_buffer_get(extractor, mbc); run_data.user_data = extractor->init(mr, cache, run_data.buffer); @@ -154,14 +150,14 @@ BLI_INLINE void extract_init(const MeshRenderData *mr, BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr, const ExtractTriBMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LOOPTRI); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LOOPTRI); EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, elt_index, params) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_looptri_bm(mr, elt, elt_index, run_data.user_data); } } @@ -170,14 +166,14 @@ BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr, BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr, const ExtractTriMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LOOPTRI); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LOOPTRI); EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, mlt_index, params) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_looptri_mesh(mr, mlt, mlt_index, run_data.user_data); } } @@ -186,14 +182,14 @@ BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr, BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr, const ExtractPolyBMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_POLY); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_POLY); EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_poly_bm(mr, f, f_index, run_data.user_data); } } @@ -202,14 +198,14 @@ BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr, BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr, const ExtractPolyMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_POLY); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_POLY); EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_poly_mesh(mr, mp, mp_index, run_data.user_data); } } @@ -218,14 +214,14 @@ BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr, BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr, const ExtractLEdgeBMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LEDGE); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LEDGE); EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_ledge_bm(mr, eed, ledge_index, run_data.user_data); } } @@ -234,14 +230,14 @@ BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr, BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr, const ExtractLEdgeMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LEDGE); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LEDGE); EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_ledge_mesh(mr, med, ledge_index, run_data.user_data); } } @@ -250,14 +246,14 @@ BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr, BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr, const ExtractLVertBMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LVERT); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LVERT); EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_lvert_bm(mr, eve, lvert_index, run_data.user_data); } } @@ -266,14 +262,14 @@ BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr, BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr, const ExtractLVertMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LVERT); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LVERT); EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_lvert_mesh(mr, mv, lvert_index, run_data.user_data); } } @@ -282,9 +278,9 @@ BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr, BLI_INLINE void extract_finish(const MeshRenderData *mr, struct MeshBatchCache *cache, - const MeshExtractRunDataArray &extractors) + const ExtractorRunDatas &extractors) { - for (const MeshExtractRunData &run_data : extractors) { + for (const ExtractorRunData &run_data : extractors) { const MeshExtract *extractor = run_data.extractor; if (extractor->finish) { extractor->finish(mr, cache, run_data.buffer, run_data.user_data); @@ -295,7 +291,7 @@ BLI_INLINE void extract_finish(const MeshRenderData *mr, /* Single Thread. */ BLI_INLINE void extract_run_and_finish_init(const MeshRenderData *mr, struct MeshBatchCache *cache, - MeshExtractRunDataArray &extractors, + ExtractorRunDatas &extractors, eMRIterType iter_type, MeshBufferCache *mbc) { @@ -385,7 +381,7 @@ struct ExtractTaskData { * This structure makes sure that when extract_init is called, that the user data of all * iterations are updated. */ - MeshExtractRunDataArray *extractors = nullptr; + ExtractorRunDatas *extractors = nullptr; MeshBufferCache *mbc = nullptr; int32_t *task_counter = nullptr; @@ -396,12 +392,12 @@ struct ExtractTaskData { ExtractTaskData(const MeshRenderData *mr, struct MeshBatchCache *cache, - MeshExtractRunDataArray *extractors, + ExtractorRunDatas *extractors, MeshBufferCache *mbc, int32_t *task_counter) : mr(mr), cache(cache), extractors(extractors), mbc(mbc), task_counter(task_counter) { - extracts_flags_get(*extractors, &iter_type, NULL); + iter_type = extractors->iter_types(); }; ExtractTaskData(const ExtractTaskData &src) = default; @@ -416,12 +412,11 @@ struct ExtractTaskData { #endif }; -static ExtractTaskData *extract_extract_iter_task_data_create_mesh( - const MeshRenderData *mr, - MeshBatchCache *cache, - MeshExtractRunDataArray *extractors, - MeshBufferCache *mbc, - int32_t *task_counter) +static ExtractTaskData *extract_extract_iter_task_data_create_mesh(const MeshRenderData *mr, + MeshBatchCache *cache, + ExtractorRunDatas *extractors, + MeshBufferCache *mbc, + int32_t *task_counter) { ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbc, task_counter); @@ -445,7 +440,7 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, const eMRIterType iter_type, int start, int end, - MeshExtractRunDataArray &extractors) + ExtractorRunDatas &extractors) { switch (mr->extract_type) { case MR_EXTRACT_BMESH: @@ -791,14 +786,14 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, GPU_use_hq_normals_workaround(); /* Create an array containing all the extractors that needs to be executed. */ - MeshExtractRunDataArray extractors; + ExtractorRunDatas extractors; #define EXTRACT_ADD_REQUESTED(type, type_lowercase, name) \ do { \ if (DRW_##type_lowercase##_requested(mbc->type_lowercase.name)) { \ const MeshExtract *extractor = mesh_extract_override_get( \ &extract_##name, do_hq_normals, do_lines_loose_subbuffer); \ - mesh_extract_run_data_array_add(extractors, extractor); \ + extractors.append(extractor); \ } \ } while (0) @@ -847,9 +842,8 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, double rdata_start = PIL_check_seconds_timer(); #endif - eMRIterType iter_type; - eMRDataType data_flag; - extracts_flags_get(extractors, &iter_type, &data_flag); + eMRIterType iter_type = extractors.iter_types(); + eMRDataType data_flag = extractors.data_types(); MeshRenderData *mr = mesh_render_data_create(me, extraction_cache, @@ -880,11 +874,11 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, uint threads_to_use = 0; /* First run the requested extractors that do not support asynchronous ranges. */ - for (const MeshExtractRunData &run_data : extractors) { + for (const ExtractorRunData &run_data : extractors) { const MeshExtract *extractor = run_data.extractor; if (!extractor->use_threading) { - MeshExtractRunDataArray *single_threaded_extractors = new MeshExtractRunDataArray(); - mesh_extract_run_data_array_add(*single_threaded_extractors, extractor); + ExtractorRunDatas *single_threaded_extractors = new ExtractorRunDatas(); + single_threaded_extractors->append(extractor); ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( mr, cache, single_threaded_extractors, mbc, NULL); struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, @@ -895,8 +889,8 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, } /* Distribute the remaining extractors into ranges per core. */ - MeshExtractRunDataArray *multi_threaded_extractors = new MeshExtractRunDataArray(); - mesh_extract_run_data_array_filter_threading(extractors, *multi_threaded_extractors); + ExtractorRunDatas *multi_threaded_extractors = new ExtractorRunDatas(); + extractors.filter_threaded_extractors_into(*multi_threaded_extractors); if (!multi_threaded_extractors->is_empty()) { /* * Determine the number of thread to use for multithreading. @@ -927,7 +921,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, } else { /* Run all requests on the same thread. */ - MeshExtractRunDataArray *extractors_copy = new MeshExtractRunDataArray(extractors); + ExtractorRunDatas *extractors_copy = new ExtractorRunDatas(extractors); ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( mr, cache, extractors_copy, mbc, NULL); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c index cc787b5a40f..324ebd2ec38 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c @@ -246,7 +246,7 @@ const MeshExtract extract_tris = {.init = extract_tris_init, .iter_looptri_bm = extract_tris_iter_looptri_bm, .iter_looptri_mesh = extract_tris_iter_looptri_mesh, .finish = extract_tris_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris)}; @@ -375,7 +375,7 @@ const MeshExtract extract_lines = {.init = extract_lines_init, .iter_ledge_bm = extract_lines_iter_ledge_bm, .iter_ledge_mesh = extract_lines_iter_ledge_mesh, .finish = extract_lines_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; @@ -414,7 +414,7 @@ const MeshExtract extract_lines_with_lines_loose = { .iter_ledge_bm = extract_lines_iter_ledge_bm, .iter_ledge_mesh = extract_lines_iter_ledge_mesh, .finish = extract_lines_with_lines_loose_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; @@ -542,7 +542,7 @@ const MeshExtract extract_points = {.init = extract_points_init, .iter_lvert_bm = extract_points_iter_lvert_bm, .iter_lvert_mesh = extract_points_iter_lvert_mesh, .finish = extract_points_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points)}; @@ -617,7 +617,7 @@ const MeshExtract extract_fdots = {.init = extract_fdots_init, .iter_poly_bm = extract_fdots_iter_poly_bm, .iter_poly_mesh = extract_fdots_iter_poly_mesh, .finish = extract_fdots_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots)}; @@ -700,7 +700,7 @@ const MeshExtract extract_lines_paint_mask = { .init = extract_lines_paint_mask_init, .iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh, .finish = extract_lines_paint_mask_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask)}; @@ -852,7 +852,7 @@ const MeshExtract extract_lines_adjacency = { .iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm, .iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh, .finish = extract_lines_adjacency_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency)}; @@ -928,7 +928,7 @@ const MeshExtract extract_edituv_tris = { .iter_looptri_bm = extract_edituv_tris_iter_looptri_bm, .iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh, .finish = extract_edituv_tris_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris)}; @@ -1012,7 +1012,7 @@ const MeshExtract extract_edituv_lines = { .iter_poly_bm = extract_edituv_lines_iter_poly_bm, .iter_poly_mesh = extract_edituv_lines_iter_poly_mesh, .finish = extract_edituv_lines_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines)}; @@ -1091,7 +1091,7 @@ const MeshExtract extract_edituv_points = { .iter_poly_bm = extract_edituv_points_iter_poly_bm, .iter_poly_mesh = extract_edituv_points_iter_poly_mesh, .finish = extract_edituv_points_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points)}; @@ -1182,7 +1182,7 @@ const MeshExtract extract_edituv_fdots = { .iter_poly_bm = extract_edituv_fdots_iter_poly_bm, .iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh, .finish = extract_edituv_fdots_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots)}; @@ -1362,7 +1362,7 @@ const MeshExtract extract_pos_nor = {.init = extract_pos_nor_init, .iter_lvert_bm = extract_pos_nor_iter_lvert_bm, .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh, .finish = extract_pos_nor_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; @@ -1550,7 +1550,7 @@ const MeshExtract extract_pos_nor_hq = { .iter_lvert_bm = extract_pos_nor_hq_iter_lvert_bm, .iter_lvert_mesh = extract_pos_nor_hq_iter_lvert_mesh, .finish = extract_pos_nor_hq_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; @@ -1641,7 +1641,7 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, const MeshExtract extract_lnor_hq = {.init = extract_lnor_hq_init, .iter_poly_bm = extract_lnor_hq_iter_poly_bm, .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh, - .data_flag = MR_DATA_LOOP_NOR, + .data_type = MR_DATA_LOOP_NOR, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; @@ -1730,7 +1730,7 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, const MeshExtract extract_lnor = {.init = extract_lnor_init, .iter_poly_bm = extract_lnor_iter_poly_bm, .iter_poly_mesh = extract_lnor_iter_poly_mesh, - .data_flag = MR_DATA_LOOP_NOR, + .data_type = MR_DATA_LOOP_NOR, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; @@ -1825,7 +1825,7 @@ static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *ca } const MeshExtract extract_uv = {.init = extract_uv_init, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv)}; @@ -2014,7 +2014,7 @@ static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *c } const MeshExtract extract_tan = {.init = extract_tan_init, - .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | + .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan)}; @@ -2033,7 +2033,7 @@ static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache const MeshExtract extract_tan_hq = { .init = extract_tan_hq_init, - .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, + .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, .use_threading = false, }; @@ -2132,7 +2132,7 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr, const MeshExtract extract_sculpt_data = { .init = extract_sculpt_data_init, - .data_flag = 0, + .data_type = 0, /* TODO: enable threading. */ .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data)}; @@ -2280,7 +2280,7 @@ static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache * } const MeshExtract extract_vcol = {.init = extract_vcol_init, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol)}; @@ -2366,7 +2366,7 @@ const MeshExtract extract_orco = {.init = extract_orco_init, .iter_poly_bm = extract_orco_iter_poly_bm, .iter_poly_mesh = extract_orco_iter_poly_mesh, .finish = extract_orco_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco)}; @@ -2567,7 +2567,7 @@ const MeshExtract extract_edge_fac = { .iter_ledge_bm = extract_edge_fac_iter_ledge_bm, .iter_ledge_mesh = extract_edge_fac_iter_ledge_mesh, .finish = extract_edge_fac_finish, - .data_flag = MR_DATA_POLY_NOR, + .data_type = MR_DATA_POLY_NOR, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac)}; @@ -2721,7 +2721,7 @@ const MeshExtract extract_weights = {.init = extract_weights_init, .iter_poly_bm = extract_weights_iter_poly_bm, .iter_poly_mesh = extract_weights_iter_poly_mesh, .finish = extract_weights_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights)}; @@ -3000,7 +3000,7 @@ const MeshExtract extract_edit_data = { .iter_ledge_mesh = extract_edit_data_iter_ledge_mesh, .iter_lvert_bm = extract_edit_data_iter_lvert_bm, .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data)}; @@ -3111,7 +3111,7 @@ const MeshExtract extract_edituv_data = { .iter_poly_bm = extract_edituv_data_iter_poly_bm, .iter_poly_mesh = extract_edituv_data_iter_poly_mesh, .finish = extract_edituv_data_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data)}; @@ -3227,7 +3227,7 @@ static void extract_edituv_stretch_area_finish(const MeshRenderData *mr, const MeshExtract extract_edituv_stretch_area = { .init = extract_edituv_stretch_area_init, .finish = extract_edituv_stretch_area_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area)}; @@ -3430,7 +3430,7 @@ const MeshExtract extract_edituv_stretch_angle = { .iter_poly_bm = extract_edituv_stretch_angle_iter_poly_bm, .iter_poly_mesh = extract_edituv_stretch_angle_iter_poly_mesh, .finish = extract_edituv_stretch_angle_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle)}; @@ -4037,7 +4037,7 @@ const MeshExtract extract_mesh_analysis = { .finish = extract_analysis_iter_finish_mesh, /* This is not needed for all visualization types. * * Maybe split into different extract. */ - .data_flag = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, + .data_type = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis)}; @@ -4117,7 +4117,7 @@ const MeshExtract extract_fdots_pos = { .init = extract_fdots_pos_init, .iter_poly_bm = extract_fdots_pos_iter_poly_bm, .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos)}; @@ -4199,7 +4199,7 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr, const MeshExtract extract_fdots_nor = { .init = extract_fdots_nor_init, .finish = extract_fdots_nor_finish, - .data_flag = MR_DATA_POLY_NOR, + .data_type = MR_DATA_POLY_NOR, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; @@ -4276,7 +4276,7 @@ static void extract_fdots_nor_hq_finish(const MeshRenderData *mr, const MeshExtract extract_fdots_nor_hq = { .init = extract_fdots_nor_hq_init, .finish = extract_fdots_nor_hq_finish, - .data_flag = MR_DATA_POLY_NOR, + .data_type = MR_DATA_POLY_NOR, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; @@ -4375,7 +4375,7 @@ const MeshExtract extract_fdots_uv = { .iter_poly_bm = extract_fdots_uv_iter_poly_bm, .iter_poly_mesh = extract_fdots_uv_iter_poly_mesh, .finish = extract_fdots_uv_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv)}; @@ -4447,7 +4447,7 @@ const MeshExtract extract_fdots_edituv_data = { .iter_poly_bm = extract_fdots_edituv_data_iter_poly_bm, .iter_poly_mesh = extract_fdots_edituv_data_iter_poly_mesh, .finish = extract_fdots_edituv_data_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data)}; @@ -4504,7 +4504,7 @@ static void *extract_skin_roots_init(const MeshRenderData *mr, const MeshExtract extract_skin_roots = { .init = extract_skin_roots_init, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots)}; @@ -4676,7 +4676,7 @@ const MeshExtract extract_poly_idx = { .init = extract_select_idx_init, .iter_poly_bm = extract_poly_idx_iter_poly_bm, .iter_poly_mesh = extract_poly_idx_iter_poly_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx)}; @@ -4686,7 +4686,7 @@ const MeshExtract extract_edge_idx = { .iter_poly_mesh = extract_edge_idx_iter_poly_mesh, .iter_ledge_bm = extract_edge_idx_iter_ledge_bm, .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx)}; @@ -4698,7 +4698,7 @@ const MeshExtract extract_vert_idx = { .iter_ledge_mesh = extract_vert_idx_iter_ledge_mesh, .iter_lvert_bm = extract_vert_idx_iter_lvert_bm, .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx)}; @@ -4743,6 +4743,6 @@ const MeshExtract extract_fdot_idx = { .init = extract_fdot_idx_init, .iter_poly_bm = extract_fdot_idx_iter_poly_bm, .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)}; \ No newline at end of file diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h index 6dd2ef4c16f..fbce3b17a40 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -430,7 +430,7 @@ typedef struct MeshExtract { /** Executed on one worker thread after all elements iterations. */ ExtractFinishFn *finish; /** Used to request common data. */ - const eMRDataType data_flag; + const eMRDataType data_type; /** Used to know if the element callbacks are thread-safe and can be parallelized. */ const bool use_threading; /** -- cgit v1.2.3 From 633b70905a3e8c75bf30e7a8eae447092f46b7c8 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 1 Jun 2021 13:03:42 +0200 Subject: Cleanup: NULL->nullptr. --- source/blender/draw/intern/draw_cache_extract_mesh.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 5a086805580..aa588bbce0a 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -781,7 +781,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, * +-----> | extract_task2_loop_3 | * +----------------------+ */ - const bool do_lines_loose_subbuffer = mbc->ibo.lines_loose != NULL; + const bool do_lines_loose_subbuffer = mbc->ibo.lines_loose != nullptr; const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || GPU_use_hq_normals_workaround(); @@ -880,7 +880,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, ExtractorRunDatas *single_threaded_extractors = new ExtractorRunDatas(); single_threaded_extractors->append(extractor); ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( - mr, cache, single_threaded_extractors, mbc, NULL); + mr, cache, single_threaded_extractors, mbc, nullptr); struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata); BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); @@ -923,7 +923,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, /* Run all requests on the same thread. */ ExtractorRunDatas *extractors_copy = new ExtractorRunDatas(extractors); ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( - mr, cache, extractors_copy, mbc, NULL); + mr, cache, extractors_copy, mbc, nullptr); struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata); BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); -- cgit v1.2.3 From 6f1af446953dd52495699d3043330d963c13df4f Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 1 Jun 2021 13:14:39 +0200 Subject: Cleanup: remove unused parameter. --- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- source/blender/draw/intern/draw_cache_extract.h | 1 - source/blender/draw/intern/draw_cache_extract_mesh.cc | 4 ---- source/blender/draw/intern/draw_cache_extract_mesh_private.h | 1 - source/blender/draw/intern/draw_cache_extract_mesh_render_data.c | 1 - source/blender/draw/intern/draw_cache_impl_mesh.c | 3 --- source/tools | 2 +- 8 files changed, 3 insertions(+), 13 deletions(-) diff --git a/release/scripts/addons b/release/scripts/addons index 4fcdbfe7c20..27fe7f3a4f9 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 4fcdbfe7c20edfc1204c0aa46c98ea25354abcd9 +Subproject commit 27fe7f3a4f964b53af436c4da4ddea337eff0c7e diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 7d78c8a63f2..5a82baad9f9 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 7d78c8a63f2f4b146f9327ddc0d567a5921b94ea +Subproject commit 5a82baad9f986722104280e8354a4427d8e9eab1 diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 36756616ca7..810deaec349 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -299,7 +299,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const bool do_final, const bool do_uvedit, const bool use_subsurf_fdots, - const DRW_MeshCDMask *cd_layer_used, const Scene *scene, const ToolSettings *ts, const bool use_hide); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index aa588bbce0a..4b047ec51f8 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -746,7 +746,6 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const bool do_final, const bool do_uvedit, const bool use_subsurf_fdots, - const DRW_MeshCDMask *cd_layer_used, const Scene *scene, const ToolSettings *ts, const bool use_hide) @@ -853,7 +852,6 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, obmat, do_final, do_uvedit, - cd_layer_used, ts, iter_type); mr->use_hide = use_hide; @@ -972,7 +970,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const bool do_final, const bool do_uvedit, const bool use_subsurf_fdots, - const DRW_MeshCDMask *cd_layer_used, const Scene *scene, const ToolSettings *ts, const bool use_hide) @@ -989,7 +986,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, do_final, do_uvedit, use_subsurf_fdots, - cd_layer_used, scene, ts, use_hide); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h index fbce3b17a40..7640582645f 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -451,7 +451,6 @@ MeshRenderData *mesh_render_data_create(Mesh *me, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const DRW_MeshCDMask *cd_used, const ToolSettings *ts, const eMRIterType iter_type); void mesh_render_data_free(MeshRenderData *mr); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c index 56b31a36fe5..bec8159c6ad 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c @@ -238,7 +238,6 @@ MeshRenderData *mesh_render_data_create(Mesh *me, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const DRW_MeshCDMask *UNUSED(cd_used), const ToolSettings *ts, const eMRIterType iter_type) { diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 0d91432d4e5..3cc71e47f28 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -1569,7 +1569,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, false, true, false, - &cache->cd_used, scene, ts, true); @@ -1588,7 +1587,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, false, false, use_subsurf_fdots, - &cache->cd_used, scene, ts, true); @@ -1606,7 +1604,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, true, false, use_subsurf_fdots, - &cache->cd_used, scene, ts, use_hide); diff --git a/source/tools b/source/tools index f99d29ae3e6..01f51a0e551 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit f99d29ae3e6ad44d45d79309454c45f8088781a4 +Subproject commit 01f51a0e551ab730f0934dc6488613690ac4bf8f -- cgit v1.2.3 From c078540512722e465e8cfc555dd958827b378a11 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 1 Jun 2021 13:18:04 +0200 Subject: Cleanup: remove unused parameter. --- source/blender/draw/intern/draw_cache_extract_mesh.cc | 2 +- source/blender/draw/intern/draw_cache_extract_mesh_private.h | 1 - source/blender/draw/intern/draw_cache_extract_mesh_render_data.c | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 4b047ec51f8..622ea191a0a 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -563,7 +563,7 @@ static void mesh_extract_render_data_node_exec(void *__restrict task_data) const eMRIterType iter_type = update_task_data->iter_type; const eMRDataType data_flag = update_task_data->data_flag; - mesh_render_data_update_normals(mr, iter_type, data_flag); + mesh_render_data_update_normals(mr, data_flag); mesh_render_data_update_looptris(mr, iter_type, data_flag); } diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h index 7640582645f..d3acf10b72c 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -455,7 +455,6 @@ MeshRenderData *mesh_render_data_create(Mesh *me, const eMRIterType iter_type); void mesh_render_data_free(MeshRenderData *mr); void mesh_render_data_update_normals(MeshRenderData *mr, - const eMRIterType iter_type, const eMRDataType data_flag); void mesh_render_data_update_looptris(MeshRenderData *mr, const eMRIterType iter_type, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c index bec8159c6ad..db28e4f3ba3 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c @@ -151,7 +151,6 @@ void mesh_render_data_update_looptris(MeshRenderData *mr, } void mesh_render_data_update_normals(MeshRenderData *mr, - const eMRIterType UNUSED(iter_type), const eMRDataType data_flag) { Mesh *me = mr->me; -- cgit v1.2.3 From 6899dcab5339d1f17dd2ea665a38e4ab28ab23fc Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Sun, 30 May 2021 22:26:31 +0200 Subject: Fix T88658: Force Fields of curve shape can crash if curve has only one point `bvhtree_from_mesh_edges_create_tree` can actually leave the BVHTree NULL (e.g. if no edges are present). Now dont allocate `BVHTreeFromMesh` on the `SurfaceModifierData` at all in case the tree would be NULL anyways. Places like `get_effector_data` check for `SurfaceModifierData`- >`BVHTreeFromMesh` and dont try to stuff like getting a closest point on surface, which would crash as soon as BVHNodes would need to be accessed (from the NULL BVHTree). Maniphest Tasks: T88658 Differential Revision: https://developer.blender.org/D11443 --- source/blender/modifiers/intern/MOD_surface.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index d2c011a21d3..bfd4cd81803 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -184,13 +184,17 @@ static void deformVerts(ModifierData *md, surmd->cfra = cfra; - surmd->bvhtree = MEM_callocN(sizeof(BVHTreeFromMesh), "BVHTreeFromMesh"); + const bool has_poly = surmd->mesh->totpoly > 0; + const bool has_edge = surmd->mesh->totedge > 0; + if (has_poly || has_edge) { + surmd->bvhtree = MEM_callocN(sizeof(BVHTreeFromMesh), "BVHTreeFromMesh"); - if (surmd->mesh->totpoly) { - BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_LOOPTRI, 2); - } - else { - BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_EDGES, 2); + if (has_poly) { + BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_LOOPTRI, 2); + } + else if (has_edge) { + BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_EDGES, 2); + } } } } -- cgit v1.2.3