From 693c50321300fdc26ed050c5ec52a56520820511 Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Tue, 31 May 2022 19:26:21 +0100 Subject: [PATCH] Optimized Mesh Merging Changes from PoolVector to LocalVector and pre-reserving vectors rather than push_back. --- core/local_vector.h | 33 ++++++++ scene/3d/mesh_instance.cpp | 162 +++++++++++++++++++++++-------------- scene/3d/mesh_instance.h | 6 +- 3 files changed, 139 insertions(+), 62 deletions(-) diff --git a/core/local_vector.h b/core/local_vector.h index 890a70bee..7a9a49c35 100644 --- a/core/local_vector.h +++ b/core/local_vector.h @@ -32,6 +32,7 @@ #include "core/error_macros.h" #include "core/os/memory.h" +#include "core/pool_vector.h" #include "core/sort_array.h" #include "core/vector.h" @@ -239,6 +240,17 @@ public: return ret; } + operator PoolVector() const { + PoolVector pl; + if (size()) { + pl.resize(size()); + typename PoolVector::Write w = pl.write(); + T *dest = w.ptr(); + memcpy(dest, data, sizeof(T) * count); + } + return pl; + } + Vector to_byte_array() const { //useful to pass stuff to gpu or variant Vector ret; ret.resize(count * sizeof(T)); @@ -254,6 +266,19 @@ public: data[i] = p_from.data[i]; } } + LocalVector(const Vector &p_from) { + resize(p_from.size()); + for (U i = 0; i < count; i++) { + data[i] = p_from[i]; + } + } + LocalVector(const PoolVector &p_from) { + resize(p_from.size()); + typename PoolVector::Read r = p_from.read(); + for (U i = 0; i < count; i++) { + data[i] = r[i]; + } + } inline LocalVector &operator=(const LocalVector &p_from) { resize(p_from.size()); for (U i = 0; i < p_from.count; i++) { @@ -268,6 +293,14 @@ public: } return *this; } + inline LocalVector &operator=(const PoolVector &p_from) { + resize(p_from.size()); + typename PoolVector::Read r = p_from.read(); + for (U i = 0; i < count; i++) { + data[i] = r[i]; + } + return *this; + } _FORCE_INLINE_ ~LocalVector() { if (data) { diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp index e92dbd602..6f16639f1 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -949,7 +949,7 @@ bool MeshInstance::_is_mergeable_with(const MeshInstance &p_other) const { return true; } -void MeshInstance::_merge_into_mesh_data(const MeshInstance &p_mi, const Transform &p_dest_tr_inv, int p_surface_id, PoolVector &r_verts, PoolVector &r_norms, PoolVector &r_tangents, PoolVector &r_colors, PoolVector &r_uvs, PoolVector &r_uv2s, PoolVector &r_inds) { +void MeshInstance::_merge_into_mesh_data(const MeshInstance &p_mi, const Transform &p_dest_tr_inv, int p_surface_id, LocalVector &r_verts, LocalVector &r_norms, LocalVector &r_tangents, LocalVector &r_colors, LocalVector &r_uvs, LocalVector &r_uv2s, LocalVector &r_inds) { _merge_log("\t\t\tmesh data from " + p_mi.get_name()); // get the mesh verts in local space @@ -961,13 +961,18 @@ void MeshInstance::_merge_into_mesh_data(const MeshInstance &p_mi, const Transfo Array arrays = rmesh->surface_get_arrays(p_surface_id); - PoolVector verts = arrays[VS::ARRAY_VERTEX]; - PoolVector normals = arrays[VS::ARRAY_NORMAL]; - PoolVector tangents = arrays[VS::ARRAY_TANGENT]; - PoolVector colors = arrays[VS::ARRAY_COLOR]; - PoolVector uvs = arrays[VS::ARRAY_TEX_UV]; - PoolVector uv2s = arrays[VS::ARRAY_TEX_UV2]; - PoolVector indices = arrays[VS::ARRAY_INDEX]; + LocalVector verts = PoolVector(arrays[VS::ARRAY_VERTEX]); + if (!verts.size()) { + // early out if there are no vertices, no point in doing anything else + return; + } + + LocalVector normals = PoolVector(arrays[VS::ARRAY_NORMAL]); + LocalVector tangents = PoolVector(arrays[VS::ARRAY_TANGENT]); + LocalVector colors = PoolVector(arrays[VS::ARRAY_COLOR]); + LocalVector uvs = PoolVector(arrays[VS::ARRAY_TEX_UV]); + LocalVector uv2s = PoolVector(arrays[VS::ARRAY_TEX_UV2]); + LocalVector indices = PoolVector(arrays[VS::ARRAY_INDEX]); // The attributes present must match the first mesh for the attributes // to remain in sync. Here we reject meshes with different attributes. @@ -1004,7 +1009,7 @@ void MeshInstance::_merge_into_mesh_data(const MeshInstance &p_mi, const Transfo } // the first index of this mesh is offset from the verts we already have stored in the merged mesh - int first_index = r_verts.size(); + int starting_index = r_verts.size(); // transform verts to world space Transform tr = p_mi.get_global_transform(); @@ -1018,78 +1023,117 @@ void MeshInstance::_merge_into_mesh_data(const MeshInstance &p_mi, const Transfo Basis normal_basis = tr.basis.inverse(); normal_basis.transpose(); - for (int n = 0; n < verts.size(); n++) { - Vector3 pt_world = tr.xform(verts[n]); - r_verts.push_back(pt_world); + int num_verts = verts.size(); - if (normals.size()) { + // verts + DEV_ASSERT(num_verts > 0); + int first_vert = r_verts.size(); + r_verts.resize(first_vert + num_verts); + Vector3 *dest_verts = &r_verts[first_vert]; + + for (int n = 0; n < num_verts; n++) { + Vector3 pt_world = tr.xform(verts[n]); + *dest_verts++ = pt_world; + } + + // normals + if (normals.size()) { + int first_norm = r_norms.size(); + r_norms.resize(first_norm + num_verts); + Vector3 *dest_norms = &r_norms[first_norm]; + for (int n = 0; n < num_verts; n++) { Vector3 pt_norm = normal_basis.xform(normals[n]); pt_norm.normalize(); - r_norms.push_back(pt_norm); + *dest_norms++ = pt_norm; } + } - if (tangents.size()) { + // tangents + if (tangents.size()) { + int first_tang = r_tangents.size(); + r_tangents.resize(first_tang + (num_verts * 4)); + real_t *dest_tangents = &r_tangents[first_tang]; + + for (int n = 0; n < num_verts; n++) { int tstart = n * 4; Vector3 pt_tangent = Vector3(tangents[tstart], tangents[tstart + 1], tangents[tstart + 2]); real_t fourth = tangents[tstart + 3]; pt_tangent = normal_basis.xform(pt_tangent); pt_tangent.normalize(); - r_tangents.push_back(pt_tangent.x); - r_tangents.push_back(pt_tangent.y); - r_tangents.push_back(pt_tangent.z); - r_tangents.push_back(fourth); + *dest_tangents++ = pt_tangent.x; + *dest_tangents++ = pt_tangent.y; + *dest_tangents++ = pt_tangent.z; + *dest_tangents++ = fourth; } + } - if (colors.size()) { - r_colors.push_back(colors[n]); + // colors + if (colors.size()) { + int first_col = r_colors.size(); + r_colors.resize(first_col + num_verts); + Color *dest_colors = &r_colors[first_col]; + + for (int n = 0; n < num_verts; n++) { + *dest_colors++ = colors[n]; } + } - if (uvs.size()) { - r_uvs.push_back(uvs[n]); + // uvs + if (uvs.size()) { + int first_uv = r_uvs.size(); + r_uvs.resize(first_uv + num_verts); + Vector2 *dest_uvs = &r_uvs[first_uv]; + + for (int n = 0; n < num_verts; n++) { + *dest_uvs++ = uvs[n]; } + } - if (uv2s.size()) { - r_uv2s.push_back(uv2s[n]); + // uv2s + if (uv2s.size()) { + int first_uv2 = r_uv2s.size(); + r_uv2s.resize(first_uv2 + num_verts); + Vector2 *dest_uv2s = &r_uv2s[first_uv2]; + + for (int n = 0; n < num_verts; n++) { + *dest_uv2s++ = uv2s[n]; } } // indices - for (int n = 0; n < indices.size(); n++) { - int ind = indices[n] + first_index; - r_inds.push_back(ind); + if (indices.size()) { + int first_ind = r_inds.size(); + r_inds.resize(first_ind + indices.size()); + int *dest_inds = &r_inds[first_ind]; + + for (unsigned int n = 0; n < indices.size(); n++) { + int ind = indices[n] + starting_index; + *dest_inds++ = ind; + } } } -bool MeshInstance::_ensure_indices_valid(PoolVector &r_indices, const PoolVector &p_verts) const { +bool MeshInstance::_ensure_indices_valid(LocalVector &r_indices, const PoolVector &p_verts) const { // no indices? create some if (!r_indices.size()) { _merge_log("\t\t\t\tindices are blank, creating..."); // indices are blank!! let's create some, assuming the mesh is using triangles r_indices.resize(p_verts.size()); - PoolVector::Write write = r_indices.write(); - int *pi = write.ptr(); // this is assuming each triangle vertex is unique - for (int n = 0; n < p_verts.size(); n++) { - *pi = n; - pi++; + for (unsigned int n = 0; n < r_indices.size(); n++) { + r_indices[n] = n; } } if (!_check_for_valid_indices(r_indices, p_verts, nullptr)) { - LocalVector new_inds; + LocalVector new_inds; _check_for_valid_indices(r_indices, p_verts, &new_inds); // copy the new indices - r_indices.resize(new_inds.size()); - PoolVector::Write write = r_indices.write(); - int *pi = write.ptr(); - - for (int n = 0; n < new_inds.size(); n++) { - pi[n] = new_inds[n]; - } + r_indices = new_inds; return false; } @@ -1098,7 +1142,7 @@ bool MeshInstance::_ensure_indices_valid(PoolVector &r_indices, const PoolV } // check for invalid tris, or make a list of the valid triangles, depending on whether r_inds is set -bool MeshInstance::_check_for_valid_indices(const PoolVector &p_inds, const PoolVector &p_verts, LocalVector *r_inds) const { +bool MeshInstance::_check_for_valid_indices(const LocalVector &p_inds, const PoolVector &p_verts, LocalVector *r_inds) const { int nTris = p_inds.size(); nTris /= 3; int indCount = 0; @@ -1220,13 +1264,13 @@ bool MeshInstance::_merge_meshes(Vector p_list, bool p_use_globa } for (int s = 0; s < first->get_mesh()->get_surface_count(); s++) { - PoolVector verts; - PoolVector normals; - PoolVector tangents; - PoolVector colors; - PoolVector uvs; - PoolVector uv2s; - PoolVector inds; + LocalVector verts; + LocalVector normals; + LocalVector tangents; + LocalVector colors; + LocalVector uvs; + LocalVector uv2s; + LocalVector inds; for (int n = 0; n < p_list.size(); n++) { // Ignore if the mesh is incompatible @@ -1242,9 +1286,9 @@ bool MeshInstance::_merge_meshes(Vector p_list, bool p_use_globa } // sanity check on the indices - for (int n = 0; n < inds.size(); n++) { + for (unsigned int n = 0; n < inds.size(); n++) { int i = inds[n]; - if (i >= verts.size()) { + if ((unsigned int)i >= verts.size()) { WARN_PRINT_ONCE("Mesh index out of range, invalid mesh, aborting"); return false; } @@ -1252,23 +1296,23 @@ bool MeshInstance::_merge_meshes(Vector p_list, bool p_use_globa Array arr; arr.resize(Mesh::ARRAY_MAX); - arr[Mesh::ARRAY_VERTEX] = verts; + arr[Mesh::ARRAY_VERTEX] = PoolVector(verts); if (normals.size()) { - arr[Mesh::ARRAY_NORMAL] = normals; + arr[Mesh::ARRAY_NORMAL] = PoolVector(normals); } if (tangents.size()) { - arr[Mesh::ARRAY_TANGENT] = tangents; + arr[Mesh::ARRAY_TANGENT] = PoolVector(tangents); } if (colors.size()) { - arr[Mesh::ARRAY_COLOR] = colors; + arr[Mesh::ARRAY_COLOR] = PoolVector(colors); } if (uvs.size()) { - arr[Mesh::ARRAY_TEX_UV] = uvs; + arr[Mesh::ARRAY_TEX_UV] = PoolVector(uvs); } if (uv2s.size()) { - arr[Mesh::ARRAY_TEX_UV2] = uv2s; + arr[Mesh::ARRAY_TEX_UV2] = PoolVector(uv2s); } - arr[Mesh::ARRAY_INDEX] = inds; + arr[Mesh::ARRAY_INDEX] = PoolVector(inds); am->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr, Array(), Mesh::ARRAY_COMPRESS_DEFAULT); } // for s through surfaces diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h index 692e8afcb..f5d9a87c2 100644 --- a/scene/3d/mesh_instance.h +++ b/scene/3d/mesh_instance.h @@ -100,9 +100,9 @@ private: // merging bool _merge_meshes(Vector p_list, bool p_use_global_space, bool p_check_compatibility); bool _is_mergeable_with(const MeshInstance &p_other) const; - void _merge_into_mesh_data(const MeshInstance &p_mi, const Transform &p_dest_tr_inv, int p_surface_id, PoolVector &r_verts, PoolVector &r_norms, PoolVector &r_tangents, PoolVector &r_colors, PoolVector &r_uvs, PoolVector &r_uv2s, PoolVector &r_inds); - bool _ensure_indices_valid(PoolVector &r_indices, const PoolVector &p_verts) const; - bool _check_for_valid_indices(const PoolVector &p_inds, const PoolVector &p_verts, LocalVector *r_inds) const; + void _merge_into_mesh_data(const MeshInstance &p_mi, const Transform &p_dest_tr_inv, int p_surface_id, LocalVector &r_verts, LocalVector &r_norms, LocalVector &r_tangents, LocalVector &r_colors, LocalVector &r_uvs, LocalVector &r_uv2s, LocalVector &r_inds); + bool _ensure_indices_valid(LocalVector &r_indices, const PoolVector &p_verts) const; + bool _check_for_valid_indices(const LocalVector &p_inds, const PoolVector &p_verts, LocalVector *r_inds) const; bool _triangle_is_degenerate(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, real_t p_epsilon) const; void _merge_log(String p_string) const;