diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 7c1a792c57..064d4fd3d1 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -19,7 +19,6 @@ target_sources( llline.cpp llmatrix3a.cpp llmatrix4a.cpp - llmeshoptimizer.cpp llmodularmath.cpp lloctree.cpp llquaternion.cpp @@ -66,7 +65,6 @@ target_sources( llmatrix3a.h llmatrix3a.inl llmatrix4a.h - llmeshoptimizer.h llmodularmath.h lloctree.h llplane.h @@ -139,7 +137,6 @@ if(AL_BUILD_TESTS) tests/llbboxlocal_test.cpp tests/llmatrix3a_test.cpp tests/llmatrix4a_test.cpp - tests/llmeshoptimizer_test.cpp tests/llmodularmath_test.cpp tests/lloctree_test.cpp tests/llquaternion2_test.cpp diff --git a/indra/llmath/llmeshoptimizer.cpp b/indra/llmath/llmeshoptimizer.cpp deleted file mode 100644 index ce0899cfa4..0000000000 --- a/indra/llmath/llmeshoptimizer.cpp +++ /dev/null @@ -1,359 +0,0 @@ - /** -* @file llmeshoptimizer.cpp -* @brief Wrapper around meshoptimizer -* -* $LicenseInfo:firstyear=2021&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2021, Linden Research, Inc. -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; -* version 2.1 of the License only. -* -* This library 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 -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -* -* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -* $/LicenseInfo$ -*/ - -#include "linden_common.h" - -#include "llmeshoptimizer.h" - -#include - -#include "alscratcharena.h" -#include "llmath.h" -#include "v2math.h" - -namespace -{ - void* MESHOPTIMIZER_ALLOC_CALLCONV scratch_allocate(size_t bytes) - { - return ALScratchArena::allocate(bytes); - } - - void MESHOPTIMIZER_ALLOC_CALLCONV scratch_deallocate(void* block) - { - ALScratchArena::deallocate(block); - } -} // namespace - -LLMeshOptimizer::LLMeshOptimizer() -{ -} - -LLMeshOptimizer::~LLMeshOptimizer() -{ -} - -//static -void LLMeshOptimizer::initClass() -{ - meshopt_setAllocator(scratch_allocate, scratch_deallocate); -} - -//static -void LLMeshOptimizer::generateShadowIndexBufferU32(U32 *destination, - const U32 *indices, - U64 index_count, - const LLVector4a * vertex_positions, - const LLVector4a * normals, - const LLVector2 * text_coords, - U64 vertex_count -) -{ - meshopt_Stream streams[3]; - - S32 index = 0; - if (vertex_positions) - { - streams[index].data = (const float*)vertex_positions; - // Despite being LLVector4a, only x, y and z are in use - streams[index].size = sizeof(F32) * 3; - streams[index].stride = sizeof(F32) * 4; - index++; - } - if (normals) - { - streams[index].data = (const float*)normals; - streams[index].size = sizeof(F32) * 3; - streams[index].stride = sizeof(F32) * 4; - index++; - } - if (text_coords) - { - streams[index].data = (const float*)text_coords; - streams[index].size = sizeof(F32) * 2; - streams[index].stride = sizeof(F32) * 2; - index++; - } - - if (index == 0) - { - // invalid - return; - } - - meshopt_generateShadowIndexBufferMulti(destination, - indices, - index_count, - vertex_count, - streams, - index - ); -} - -//static -void LLMeshOptimizer::generateShadowIndexBufferU16(U16 *destination, - const U16 *indices, - U64 index_count, - const LLVector4a * vertex_positions, - const LLVector4a * normals, - const LLVector2 * text_coords, - U64 vertex_count -) -{ - meshopt_Stream streams[3]; - - S32 index = 0; - if (vertex_positions) - { - streams[index].data = (const float*)vertex_positions; - streams[index].size = sizeof(F32) * 3; - streams[index].stride = sizeof(F32) * 4; - index++; - } - if (normals) - { - streams[index].data = (const float*)normals; - streams[index].size = sizeof(F32) * 3; - streams[index].stride = sizeof(F32) * 4; - index++; - } - if (text_coords) - { - streams[index].data = (const float*)text_coords; - streams[index].size = sizeof(F32) * 2; - streams[index].stride = sizeof(F32) * 2; - index++; - } - - if (index == 0) - { - // invalid - return; - } - - meshopt_generateShadowIndexBufferMulti(destination, - indices, - index_count, - vertex_count, - streams, - index); -} - -void LLMeshOptimizer::optimizeVertexCacheU32(U32 * destination, const U32 * indices, U64 index_count, U64 vertex_count) -{ - meshopt_optimizeVertexCache(destination, indices, index_count, vertex_count); -} - -void LLMeshOptimizer::optimizeVertexCacheU16(U16 * destination, const U16 * indices, U64 index_count, U64 vertex_count) -{ - meshopt_optimizeVertexCache(destination, indices, index_count, vertex_count); -} - -size_t LLMeshOptimizer::generateRemapMultiU32( - unsigned int* remap, - const U32 * indices, - U64 index_count, - const LLVector4a * vertex_positions, - const LLVector4a * normals, - const LLVector2 * text_coords, - U64 vertex_count) -{ - meshopt_Stream streams[] = { - {(const float*)vertex_positions, sizeof(F32) * 3, sizeof(F32) * 4}, - {(const float*)normals, sizeof(F32) * 3, sizeof(F32) * 4}, - {(const float*)text_coords, sizeof(F32) * 2, sizeof(F32) * 2}, - }; - - // Remap can function without indices, - // but providing indices helps with removing unused vertices - U64 indeces_cmp = indices ? index_count : vertex_count; - - return meshopt_generateVertexRemapMulti(&remap[0], indices, indeces_cmp, vertex_count, streams, sizeof(streams) / sizeof(streams[0])); -} - -size_t LLMeshOptimizer::generateRemapMultiU16( - unsigned int* remap, - const U16 * indices, - U64 index_count, - const LLVector4a * vertex_positions, - const LLVector4a * normals, - const LLVector2 * text_coords, - U64 vertex_count) -{ - S32 out_of_range_count = 0; - U32* indices_u32 = NULL; - if (indices) - { - indices_u32 = (U32*)ll_aligned_malloc_32(index_count * sizeof(U32)); - for (U64 i = 0; i < index_count; i++) - { - if (indices[i] < vertex_count) - { - indices_u32[i] = (U32)indices[i]; - } - else - { - out_of_range_count++; - indices_u32[i] = 0; - } - } - } - - if (out_of_range_count) - { - LL_WARNS() << out_of_range_count << " indices are out of range." << LL_ENDL; - } - - size_t unique = generateRemapMultiU32(remap, indices_u32, index_count, vertex_positions, normals, text_coords, vertex_count); - - ll_aligned_free_32(indices_u32); - - return unique; -} - -void LLMeshOptimizer::remapIndexBufferU32(U32 * destination_indices, - const U32 * indices, - U64 index_count, - const unsigned int* remap) -{ - meshopt_remapIndexBuffer(destination_indices, indices, index_count, remap); -} - -void LLMeshOptimizer::remapIndexBufferU16(U16 * destination_indices, - const U16 * indices, - U64 index_count, - const unsigned int* remap) -{ - meshopt_remapIndexBuffer(destination_indices, indices, index_count, remap); -} - -void LLMeshOptimizer::remapPositionsBuffer(LLVector4a * destination_vertices, - const LLVector4a * vertex_positions, - U64 vertex_count, - const unsigned int* remap) -{ - meshopt_remapVertexBuffer((float*)destination_vertices, (const float*)vertex_positions, vertex_count, sizeof(LLVector4a), remap); -} - -void LLMeshOptimizer::remapNormalsBuffer(LLVector4a * destination_normalss, - const LLVector4a * normals, - U64 mormals_count, - const unsigned int* remap) -{ - meshopt_remapVertexBuffer((float*)destination_normalss, (const float*)normals, mormals_count, sizeof(LLVector4a), remap); -} - -void LLMeshOptimizer::remapUVBuffer(LLVector2 * destination_uvs, - const LLVector2 * uv_positions, - U64 uv_count, - const unsigned int* remap) -{ - meshopt_remapVertexBuffer((float*)destination_uvs, (const float*)uv_positions, uv_count, sizeof(LLVector2), remap); -} - -//static -U64 LLMeshOptimizer::simplifyU32(U32 *destination, - const U32 *indices, - U64 index_count, - const LLVector4a *vertex_positions, - U64 vertex_count, - U64 vertex_positions_stride, - U64 target_index_count, - F32 target_error, - bool sloppy, - F32* result_error -) -{ - if (sloppy) - { - return meshopt_simplifySloppy(destination, - indices, - index_count, - (const float*)vertex_positions, - vertex_count, - vertex_positions_stride, - target_index_count, - target_error, - result_error - ); - } - else - { - return meshopt_simplify(destination, - indices, - index_count, - (const float*)vertex_positions, - vertex_count, - vertex_positions_stride, - target_index_count, - target_error, - 0, - result_error - ); - } -} - -//static -U64 LLMeshOptimizer::simplify(U16 *destination, - const U16 *indices, - U64 index_count, - const LLVector4a *vertex_positions, - U64 vertex_count, - U64 vertex_positions_stride, - U64 target_index_count, - F32 target_error, - bool sloppy, - F32* result_error - ) -{ - if (sloppy) - { - return meshopt_simplifySloppy(destination, - indices, - index_count, - (const float*)vertex_positions, - vertex_count, - vertex_positions_stride, - target_index_count, - target_error, - result_error - ); - } - else - { - return meshopt_simplify(destination, - indices, - index_count, - (const float*)vertex_positions, - vertex_count, - vertex_positions_stride, - target_index_count, - target_error, - 0, - result_error - ); - } -} - diff --git a/indra/llmath/llmeshoptimizer.h b/indra/llmath/llmeshoptimizer.h deleted file mode 100644 index f5d4169941..0000000000 --- a/indra/llmath/llmeshoptimizer.h +++ /dev/null @@ -1,160 +0,0 @@ -/** -* @file llmeshoptimizer.h -* @brief Wrapper around meshoptimizer -* -* $LicenseInfo:firstyear=2021&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2021, Linden Research, Inc. -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; -* version 2.1 of the License only. -* -* This library 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 -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -* -* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -* $/LicenseInfo$ -*/ -#ifndef LLMESHOPTIMIZER_H -#define LLMESHOPTIMIZER_H - -#include "linden_common.h" - -class LLVector4a; -class LLVector2; - -class LLMeshOptimizer -{ -public: - LLMeshOptimizer(); - ~LLMeshOptimizer(); - - // Makes meshoptimizer take its scratch from the calling thread's - // ALScratchArena. Called once, before any thread calls meshoptimizer. - static void initClass(); - - static void generateShadowIndexBufferU32( - U32 *destination, - const U32 *indices, - U64 index_count, - const LLVector4a * vertex_positions, - const LLVector4a * normals, - const LLVector2 * text_coords, - U64 vertex_count); - - static void generateShadowIndexBufferU16( - U16 *destination, - const U16 *indices, - U64 index_count, - const LLVector4a * vertex_positions, - const LLVector4a * normals, - const LLVector2 * text_coords, - U64 vertex_count); - - static void optimizeVertexCacheU32( - U32 *destination, - const U32 *indices, - U64 index_count, - U64 vertex_count); - - static void optimizeVertexCacheU16( - U16 *destination, - const U16 *indices, - U64 index_count, - U64 vertex_count); - - // Remap functions - // Welds indentical vertexes together. - // Removes unused vertices if indices were provided. - - static size_t generateRemapMultiU32( - unsigned int* remap, - const U32 * indices, - U64 index_count, - const LLVector4a * vertex_positions, - const LLVector4a * normals, - const LLVector2 * text_coords, - U64 vertex_count); - - static size_t generateRemapMultiU16( - unsigned int* remap, - const U16 * indices, - U64 index_count, - const LLVector4a * vertex_positions, - const LLVector4a * normals, - const LLVector2 * text_coords, - U64 vertex_count); - - static void remapIndexBufferU32(U32 * destination_indices, - const U32 * indices, - U64 index_count, - const unsigned int* remap); - - static void remapIndexBufferU16(U16 * destination_indices, - const U16 * indices, - U64 index_count, - const unsigned int* remap); - - - static void remapPositionsBuffer(LLVector4a * destination_vertices, - const LLVector4a * vertex_positions, - U64 vertex_count, - const unsigned int* remap); - - static void remapNormalsBuffer(LLVector4a * destination_normalss, - const LLVector4a * normals, - U64 mormals_count, - const unsigned int* remap); - - static void remapUVBuffer(LLVector2 * destination_uvs, - const LLVector2 * uv_positions, - U64 uv_count, - const unsigned int* remap); - - // Simplification - - // returns amount of indices in destiantion - // sloppy engages a variant of a mechanizm that does not respect topology as much - // but is much more efective for simpler models - // result_error returns how far from original the model is in % if not NULL - // Works with U32 indices (LLFace uses U16 indices) - static U64 simplifyU32( - U32 *destination, - const U32 *indices, - U64 index_count, - const LLVector4a *vertex_positions, - U64 vertex_count, - U64 vertex_positions_stride, - U64 target_index_count, - F32 target_error, - bool sloppy, - F32* result_error); - - // Returns amount of indices in destiantion - // sloppy engages a variant of a mechanizm that does not respect topology as much - // but is much better for simpler models - // result_error returns how far from original the model is in % if not NULL - // Meant for U16 indices (LLFace uses U16 indices) - static U64 simplify( - U16 *destination, - const U16 *indices, - U64 index_count, - const LLVector4a *vertex_positions, - U64 vertex_count, - U64 vertex_positions_stride, - U64 target_index_count, - F32 target_error, - bool sloppy, - F32* result_error); -private: -}; - -#endif //LLMESHOPTIMIZER_H diff --git a/indra/llmath/llvector4a.cpp b/indra/llmath/llvector4a.cpp index 288143415a..8acd1248bb 100644 --- a/indra/llmath/llvector4a.cpp +++ b/indra/llmath/llvector4a.cpp @@ -26,17 +26,21 @@ #include "linden_common.h" +#include "alscratcharena.h" #include "llmemory.h" #include "llmath.h" #include "llquantize.h" #include "llthread.h" +#include + extern const LLVector4a LL_V4A_ZERO(0.f, 0.f, 0.f, 0.f); extern const LLVector4a LL_V4A_EPSILON(F_APPROXIMATELY_ZERO, F_APPROXIMATELY_ZERO, F_APPROXIMATELY_ZERO, F_APPROXIMATELY_ZERO); /*static */void LLVector4a::initClass() { set_thread_fp_mode(); + meshopt_setAllocator(ALScratchArena::allocate, ALScratchArena::deallocate); } /*static */void LLVector4a::memcpyNonAliased16(F32* __restrict dst, const F32* __restrict src, size_t bytes) diff --git a/indra/llmath/llvector4a.h b/indra/llmath/llvector4a.h index 44d45c8168..def450c47d 100644 --- a/indra/llmath/llvector4a.h +++ b/indra/llmath/llvector4a.h @@ -46,8 +46,10 @@ class alignas(16) LLVector4a // STATIC METHODS /////////////////////////////////// - // Sets the main thread's floating-point mode at startup; worker threads - // set their own at entry (set_thread_fp_mode in llthread.h). + // llmath's startup, once, before any other thread starts: sets the main + // thread's floating-point mode, worker threads setting their own at + // entry (set_thread_fp_mode in llthread.h), and makes meshoptimizer take + // its scratch from the calling thread's ALScratchArena. static void initClass(); // Return a vector of all zeros diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index cee1206c8c..a8cecf7f7a 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -51,7 +51,6 @@ #include "llmatrix4a.h" #include "alscratcharena.h" #include "alsimdkernels.h" -#include "llmeshoptimizer.h" #include "lltimer.h" #include "llvolumeoctree.h" #include "workqueue.h" @@ -2401,30 +2400,6 @@ bool LLVolumeFace::VertexData::operator==(const LLVolumeFace::VertexData& rhs)co mTexCoord == rhs.mTexCoord; } -bool LLVolumeFace::VertexData::compareNormal(const LLVolumeFace::VertexData& rhs, F32 angle_cutoff) const -{ - bool retval = false; - - const F32 epsilon = 0.00001f; - - if (rhs.mPosition.equals3(mPosition, epsilon) && - fabs(rhs.mTexCoord[0]-mTexCoord[0]) < epsilon && - fabs(rhs.mTexCoord[1]-mTexCoord[1]) < epsilon) - { - if (angle_cutoff > 1.f) - { - retval = (mNormal.equals3(rhs.mNormal, epsilon)); - } - else - { - F32 cur_angle = rhs.mNormal.dot3(mNormal).getF32(); - retval = cur_angle > angle_cutoff; - } - } - - return retval; -} - bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; @@ -4965,28 +4940,6 @@ bool LLVolumeFace::create(LLVolume* volume, bool partial_build) return ret ; } -void LLVolumeFace::getVertexData(U16 index, LLVolumeFace::VertexData& cv) -{ - cv.setPosition(mPositions[index]); - if (mNormals) - { - cv.setNormal(mNormals[index]); - } - else - { - cv.getNormal().clear(); - } - - if (mTexCoords) - { - cv.mTexCoord = mTexCoords[index]; - } - else - { - cv.mTexCoord.clear(); - } -} - bool LLVolumeFace::VertexMapData::operator==(const LLVolumeFace::VertexData& rhs) const { return getPosition().equals3(rhs.getPosition()) && @@ -5009,20 +4962,52 @@ bool LLVolumeFace::VertexMapData::ComparePosition::operator()(const LLVector3& a return a.mV[2] < b.mV[2]; } +// Uploads reach this, so the indices are untrusted: an index past the face's +// vertices is taken as vertex 0, with a warning, before anything reads +// through it. void LLVolumeFace::remap() { - // Generate a remap buffer - // Documentation for meshopt_generateVertexRemapMulti claims that remap should use vertice count - // but all examples use indice count. There are out of bounds crashes when using vertice count. - // To be on the safe side use bigger of the two. - std::vector remap(llmax(mNumIndices, mNumVertices), 0); - S32 remap_vertices_count = static_cast(LLMeshOptimizer::generateRemapMultiU16(&remap[0], - mIndices, - mNumIndices, - mPositions, - mNormals, - mTexCoords, - mNumVertices)); + ALScratchArena::Scope scratch; + + // The indices at 32 bits, as meshoptimizer takes them, checked as they + // go. A face without indices is welded as unindexed geometry. + U32* indices = scratch.allocate(mNumIndices); + const U32* checked = nullptr; + if (mIndices) + { + S32 out_of_range_count = 0; + for (S32 i = 0; i < mNumIndices; ++i) + { + if (mIndices[i] < mNumVertices) + { + indices[i] = mIndices[i]; + } + else + { + out_of_range_count++; + indices[i] = 0; + } + } + + if (out_of_range_count) + { + LL_WARNS() << out_of_range_count << " indices are out of range." << LL_ENDL; + } + checked = indices; + } + + // Each vertex's place among the vertices that differ in position, normal + // or texture coordinate, or ~0u where no index uses it. + const meshopt_Stream streams[] = { + { mPositions, sizeof(F32) * 3, sizeof(LLVector4a) }, + { mNormals, sizeof(F32) * 3, sizeof(LLVector4a) }, + { mTexCoords, sizeof(LLVector2), sizeof(LLVector2) }, + }; + U32* remap = scratch.allocate(mNumVertices); + S32 remap_vertices_count = static_cast(meshopt_generateVertexRemapMulti(remap, checked, checked ? mNumIndices : mNumVertices, + mNumVertices, streams, + sizeof(streams) / sizeof(streams[0]))); + meshopt_remapIndexBuffer(indices, checked, mNumIndices, remap); // Allocate new buffers S32 size = ((mNumIndices * sizeof(U16)) + 0xF) & ~0xF; @@ -5034,10 +5019,13 @@ void LLVolumeFace::remap() LLVector2* remap_tex_coords = (LLVector2*)(remap_normals + remap_vertices_count); // Fill the buffers - LLMeshOptimizer::remapIndexBufferU16(remap_indices, mIndices, mNumIndices, &remap[0]); - LLMeshOptimizer::remapPositionsBuffer(remap_positions, mPositions, mNumVertices, &remap[0]); - LLMeshOptimizer::remapNormalsBuffer(remap_normals, mNormals, mNumVertices, &remap[0]); - LLMeshOptimizer::remapUVBuffer(remap_tex_coords, mTexCoords, mNumVertices, &remap[0]); + for (S32 i = 0; i < mNumIndices; ++i) + { + remap_indices[i] = static_cast(indices[i]); + } + meshopt_remapVertexBuffer(remap_positions, mPositions, mNumVertices, sizeof(LLVector4a), remap); + meshopt_remapVertexBuffer(remap_normals, mNormals, mNumVertices, sizeof(LLVector4a), remap); + meshopt_remapVertexBuffer(remap_tex_coords, mTexCoords, mNumVertices, sizeof(LLVector2), remap); // Free unused buffers ll_aligned_free_16(mIndices); @@ -5056,373 +5044,6 @@ void LLVolumeFace::remap() mNumAllocatedVertices = remap_vertices_count; } -void LLVolumeFace::optimize(F32 angle_cutoff) -{ - LLVolumeFace new_face; - - //map of points to vector of vertices at that point - std::map > point_map; - - LLVector4a range; - range.setSub(mExtents[1],mExtents[0]); - - //remove redundant vertices - for (S32 i = 0; i < mNumIndices; ++i) - { - U16 index = mIndices[i]; - - if (index >= mNumVertices) - { - // invalid index - // replace with a valid index to avoid crashes - index = mNumVertices - 1; - mIndices[i] = index; - - // Needs better logging - LL_DEBUGS_ONCE("LLVOLUME") << "Invalid index, substituting" << LL_ENDL; - } - - LLVolumeFace::VertexData cv; - getVertexData(index, cv); - - bool found = false; - - LLVector4a pos; - pos.setSub(mPositions[index], mExtents[0]); - pos.div(range); - - U64 pos64 = 0; - - pos64 = (U16) (pos[0]*65535); - pos64 = pos64 | (((U64) (pos[1]*65535)) << 16); - pos64 = pos64 | (((U64) (pos[2]*65535)) << 32); - - std::map >::iterator point_iter = point_map.find(pos64); - - if (point_iter != point_map.end()) - { //duplicate point might exist - for (U32 j = 0; j < point_iter->second.size(); ++j) - { - LLVolumeFace::VertexData& tv = (point_iter->second)[j]; - if (tv.compareNormal(cv, angle_cutoff)) - { - found = true; - new_face.pushIndex((point_iter->second)[j].mIndex); - break; - } - } - } - - if (!found) - { - new_face.pushVertex(cv); - U16 index = (U16) new_face.mNumVertices-1; - new_face.pushIndex(index); - - VertexMapData d; - d.setPosition(cv.getPosition()); - d.mTexCoord = cv.mTexCoord; - d.setNormal(cv.getNormal()); - d.mIndex = index; - if (point_iter != point_map.end()) - { - point_iter->second.push_back(d); - } - else - { - point_map[pos64].push_back(d); - } - } - } - - - if (angle_cutoff > 1.f && !mNormals) - { - // Now alloc'd with positions - //ll_aligned_free_16(new_face.mNormals); - new_face.mNormals = NULL; - } - - if (!mTexCoords) - { - // Now alloc'd with positions - //ll_aligned_free_16(new_face.mTexCoords); - new_face.mTexCoords = NULL; - } - - // Only swap data if we've actually optimized the mesh - // - if (new_face.mNumVertices <= mNumVertices) - { - llassert(new_face.mNumIndices == mNumIndices); - swapData(new_face); - } - -} - -class LLVCacheTriangleData; - -class LLVCacheVertexData -{ -public: - S32 mIdx; - S32 mCacheTag; - F64 mScore; - U32 mActiveTriangles; - std::vector mTriangles; - - LLVCacheVertexData() - { - mCacheTag = -1; - mScore = 0.0; - mActiveTriangles = 0; - mIdx = -1; - } -}; - -class LLVCacheTriangleData -{ -public: - bool mActive; - F64 mScore; - LLVCacheVertexData* mVertex[3]; - - LLVCacheTriangleData() - { - mActive = true; - mScore = 0.0; - mVertex[0] = mVertex[1] = mVertex[2] = NULL; - } - - void complete() - { - mActive = false; - for (S32 i = 0; i < 3; ++i) - { - if (mVertex[i]) - { - llassert(mVertex[i]->mActiveTriangles > 0); - mVertex[i]->mActiveTriangles--; - } - } - } - - bool operator<(const LLVCacheTriangleData& rhs) const - { //highest score first - return rhs.mScore < mScore; - } -}; - -constexpr F64 FindVertexScore_CacheDecayPower = 1.5; -constexpr F64 FindVertexScore_LastTriScore = 0.75; -constexpr F64 FindVertexScore_ValenceBoostScale = 2.0; -constexpr F64 FindVertexScore_ValenceBoostPower = 0.5; -constexpr U32 MaxSizeVertexCache = 32; -constexpr F64 FindVertexScore_Scaler = 1.0/(MaxSizeVertexCache-3); - -F64 find_vertex_score(LLVCacheVertexData& data) -{ - F64 score = -1.0; - - score = 0.0; - - S32 cache_idx = data.mCacheTag; - - if (cache_idx < 0) - { - //not in cache - } - else - { - if (cache_idx < 3) - { //vertex was in the last triangle - score = FindVertexScore_LastTriScore; - } - else - { //more points for being higher in the cache - score = 1.0-((cache_idx-3)*FindVertexScore_Scaler); - score = pow(score, FindVertexScore_CacheDecayPower); - } - } - - //bonus points for having low valence - F64 valence_boost = pow((F64)data.mActiveTriangles, -FindVertexScore_ValenceBoostPower); - score += FindVertexScore_ValenceBoostScale * valence_boost; - - return score; -} - -class LLVCacheFIFO -{ -public: - LLVCacheVertexData* mCache[MaxSizeVertexCache]; - U32 mMisses; - - LLVCacheFIFO() - { - mMisses = 0; - for (U32 i = 0; i < MaxSizeVertexCache; ++i) - { - mCache[i] = NULL; - } - } - - void addVertex(LLVCacheVertexData* data) - { - if (data->mCacheTag == -1) - { - mMisses++; - - S32 end = MaxSizeVertexCache-1; - - if (mCache[end]) - { - mCache[end]->mCacheTag = -1; - } - - for (S32 i = end; i > 0; --i) - { - mCache[i] = mCache[i-1]; - if (mCache[i]) - { - mCache[i]->mCacheTag = i; - } - } - - mCache[0] = data; - data->mCacheTag = 0; - } - } -}; - -class LLVCacheLRU -{ -public: - LLVCacheVertexData* mCache[MaxSizeVertexCache+3]; - - LLVCacheTriangleData* mBestTriangle; - - U32 mMisses; - - LLVCacheLRU() - { - for (U32 i = 0; i < MaxSizeVertexCache+3; ++i) - { - mCache[i] = NULL; - } - - mBestTriangle = NULL; - mMisses = 0; - } - - void addVertex(LLVCacheVertexData* data) - { - S32 end = MaxSizeVertexCache+2; - if (data->mCacheTag != -1) - { //just moving a vertex to the front of the cache - end = data->mCacheTag; - } - else - { - mMisses++; - if (mCache[end]) - { //adding a new vertex, vertex at end of cache falls off - mCache[end]->mCacheTag = -1; - } - } - - for (S32 i = end; i > 0; --i) - { //adjust cache pointers and tags - mCache[i] = mCache[i-1]; - - if (mCache[i]) - { - mCache[i]->mCacheTag = i; - } - } - - mCache[0] = data; - mCache[0]->mCacheTag = 0; - } - - void addTriangle(LLVCacheTriangleData* data) - { - addVertex(data->mVertex[0]); - addVertex(data->mVertex[1]); - addVertex(data->mVertex[2]); - } - - void updateScores() - { - LLVCacheVertexData** data_iter = mCache+MaxSizeVertexCache; - LLVCacheVertexData** end_data = mCache+MaxSizeVertexCache+3; - - while(data_iter != end_data) - { - LLVCacheVertexData* data = *data_iter++; - //trailing 3 vertices aren't actually in the cache for scoring purposes - if (data) - { - data->mCacheTag = -1; - } - } - - data_iter = mCache; - end_data = mCache+MaxSizeVertexCache; - - while (data_iter != end_data) - { //update scores of vertices in cache - LLVCacheVertexData* data = *data_iter++; - if (data) - { - data->mScore = find_vertex_score(*data); - } - } - - mBestTriangle = NULL; - //update triangle scores - data_iter = mCache; - end_data = mCache+MaxSizeVertexCache+3; - - while (data_iter != end_data) - { - LLVCacheVertexData* data = *data_iter++; - if (data) - { - for (std::vector::iterator iter = data->mTriangles.begin(), end_iter = data->mTriangles.end(); iter != end_iter; ++iter) - { - LLVCacheTriangleData* tri = *iter; - if (tri->mActive) - { - tri->mScore = tri->mVertex[0] ? tri->mVertex[0]->mScore : 0; - tri->mScore += tri->mVertex[1] ? tri->mVertex[1]->mScore : 0; - tri->mScore += tri->mVertex[2] ? tri->mVertex[2]->mScore : 0; - - if (!mBestTriangle || mBestTriangle->mScore < tri->mScore) - { - mBestTriangle = tri; - } - } - } - } - } - - //knock trailing 3 vertices off the cache - data_iter = mCache+MaxSizeVertexCache; - end_data = mCache+MaxSizeVertexCache+3; - while (data_iter != end_data) - { - LLVCacheVertexData* data = *data_iter; - if (data) - { - llassert(data->mCacheTag == -1); - *data_iter = NULL; - } - ++data_iter; - } - } -}; - namespace { LLVector4a plane_axis(const LLVector4a& normal); @@ -6175,20 +5796,6 @@ LLVector4a LLVolumeFace::getPlanarTangent(const LLVector4a& normal, const LLVect return out; } - -void LLVolumeFace::swapData(LLVolumeFace& rhs) -{ - llswap(rhs.mPositions, mPositions); - llswap(rhs.mNormals, mNormals); - llswap(rhs.mTangents, mTangents); - llswap(rhs.mTangentsStale, mTangentsStale); - llswap(rhs.mTexCoords, mTexCoords); - llswap(rhs.mIndices,mIndices); - llswap(rhs.mNumVertices, mNumVertices); - llswap(rhs.mNumIndices, mNumIndices); - std::swap(rhs.mPlanarNormal, mPlanarNormal); -} - namespace { // Points closer than this are one place: far above the rounding that a @@ -7738,61 +7345,6 @@ void LLVolumeFace::resizeVertices(S32 num_verts) mJointRiggingInfoTab.clear(); } -void LLVolumeFace::pushVertex(const LLVolumeFace::VertexData& cv) -{ - pushVertex(cv.getPosition(), cv.getNormal(), cv.mTexCoord); -} - -void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc) -{ - S32 new_verts = mNumVertices+1; - - if (new_verts > mNumAllocatedVertices) - { - // double buffer size on expansion - new_verts *= 2; - - S32 new_tc_size = ((new_verts*8)+0xF) & ~0xF; - S32 old_tc_size = ((mNumVertices*8)+0xF) & ~0xF; - - S32 old_vsize = mNumVertices*16; - - S32 new_size = new_verts*16*2+new_tc_size; - - LLVector4a* old_buf = mPositions; - - mPositions = (LLVector4a*) ll_aligned_malloc<64>(new_size); - mNormals = mPositions+new_verts; - mTexCoords = (LLVector2*) (mNormals+new_verts); - - if (old_buf != NULL) - { - // copy old positions into new buffer - LLVector4a::memcpyNonAliased16((F32*)mPositions, (F32*)old_buf, old_vsize); - - // normals - LLVector4a::memcpyNonAliased16((F32*)mNormals, (F32*)(old_buf + mNumVertices), old_vsize); - - // tex coords - LLVector4a::memcpyNonAliased16((F32*)mTexCoords, (F32*)(old_buf + mNumVertices * 2), old_tc_size); - } - - // just clear tangents - ll_aligned_free_16(mTangents); - mTangents = NULL; - ll_aligned_free<64>(old_buf); - - mNumAllocatedVertices = new_verts; - - } - - mPositions[mNumVertices] = pos; - mNormals[mNumVertices] = norm; - mTexCoords[mNumVertices] = tc; - - mNumVertices++; -} - void LLVolumeFace::allocateTangents(S32 num_verts) { ll_aligned_free_16(mTangents); @@ -7848,21 +7400,6 @@ void LLVolumeFace::resizeIndices(S32 num_indices) } } -void LLVolumeFace::pushIndex(const U16& idx) -{ - S32 new_count = mNumIndices + 1; - S32 new_size = ((new_count*2)+0xF) & ~0xF; - - S32 old_size = ((mNumIndices*2)+0xF) & ~0xF; - if (new_size != old_size) - { - mIndices = (U16*) ll_aligned_realloc_16(mIndices, new_size, old_size); - ll_assert_aligned(mIndices,16); - } - - mIndices[mNumIndices++] = idx; -} - void LLVolumeFace::fillFromLegacyData(std::vector& v, std::vector& idx) { resizeVertices(static_cast(v.size())); diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index 87aa12de9b..4e41753984 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -858,9 +858,8 @@ class LLDynamicPath : public LLPath class LLVolumeFace { public: - // A vertex as the importers and the optimizer pass it around: position, - // normal and texture coordinate in one block, so a vector of them is one - // allocation. + // A vertex as the importers pass it around: position, normal and texture + // coordinate in one block, so a vector of them is one allocation. class VertexData { public: @@ -875,7 +874,6 @@ class LLVolumeFace bool operator<(const VertexData& rhs) const; bool operator==(const VertexData& rhs) const; - bool compareNormal(const VertexData& rhs, F32 angle_cutoff) const; private: LLVector4a mPosition; @@ -909,14 +907,6 @@ class LLVolumeFace void resizeIndices(S32 num_indices); void fillFromLegacyData(std::vector& v, std::vector& idx); - void pushVertex(const VertexData& cv); - void pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc); - void pushIndex(const U16& idx); - - void swapData(LLVolumeFace& rhs); - - void getVertexData(U16 indx, LLVolumeFace::VertexData& cv); - class VertexMapData : public LLVolumeFace::VertexData { public: @@ -936,7 +926,6 @@ class LLVolumeFace // normals and texture coordinates into account. void remap(); - void optimize(F32 angle_cutoff = 2.f); bool cacheOptimize(bool gen_tangents = false); // Normals made from the face's shape: smooth across an edge whose two diff --git a/indra/llmath/tests/almeshsimplifier_test.cpp b/indra/llmath/tests/almeshsimplifier_test.cpp index 1594fed00e..786bcabc53 100644 --- a/indra/llmath/tests/almeshsimplifier_test.cpp +++ b/indra/llmath/tests/almeshsimplifier_test.cpp @@ -26,8 +26,8 @@ #include "linden_common.h" #include "almeshsimplifier.h" +#include "alscratcharena.h" #include "llmath.h" -#include "llmeshoptimizer.h" #include "llvector4a.h" #include "llvolume.h" #include "v2math.h" @@ -66,7 +66,7 @@ namespace static void* MESHOPTIMIZER_ALLOC_CALLCONV newBlock(size_t bytes) { return ::operator new(bytes); } static void MESHOPTIMIZER_ALLOC_CALLCONV deleteBlock(void* block) { ::operator delete(block); } - ArenaInstalled() { LLMeshOptimizer::initClass(); } + ArenaInstalled() { meshopt_setAllocator(ALScratchArena::allocate, ALScratchArena::deallocate); } ~ArenaInstalled() { meshopt_setAllocator(newBlock, deleteBlock); } }; diff --git a/indra/llmath/tests/llmeshoptimizer_test.cpp b/indra/llmath/tests/llmeshoptimizer_test.cpp deleted file mode 100644 index b044a694aa..0000000000 --- a/indra/llmath/tests/llmeshoptimizer_test.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @file llmeshoptimizer_test.cpp - * @brief meshoptimizer's scratch from the calling thread's arena. - * - * $LicenseInfo:firstyear=2026&license=viewerlgpl$ - * Alchemy Viewer Source Code - * Copyright (C) 2026, Rye - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llmeshoptimizer.h" -#include "alscratcharena.h" - -#include -#include - -#include -#include - -namespace -{ - // meshoptimizer on the arena while it lives, and on its own default, - // operator new and delete, after. - struct ArenaInstalled - { - static void* MESHOPTIMIZER_ALLOC_CALLCONV newBlock(size_t bytes) { return ::operator new(bytes); } - static void MESHOPTIMIZER_ALLOC_CALLCONV deleteBlock(void* block) { ::operator delete(block); } - - ArenaInstalled() { LLMeshOptimizer::initClass(); } - ~ArenaInstalled() { meshopt_setAllocator(newBlock, deleteBlock); } - }; - - // Vertex i is (3i, 3i + 1, 3i + 2) modulo 97 a coordinate: 97 vertices, - // each count times over. - void repeating(size_t count, std::vector& indices, std::vector& positions) - { - indices.resize(count); - std::iota(indices.begin(), indices.end(), 0); - positions.resize(count * 3); - for (size_t i = 0; i < positions.size(); ++i) - { - positions[i] = (F32)(i % 97); - } - } - - TEST_CASE("llmeshoptimizer: meshoptimizer takes its blocks from the arena", "[llmeshoptimizer]") - { - ArenaInstalled installed; - std::vector indices; - std::vector positions; - repeating(3000, indices, positions); - std::vector remap(indices.size()); - - ALScratchArena::resetCounters(); - CHECK(meshopt_generateVertexRemap(remap.data(), indices.data(), indices.size(), positions.data(), indices.size(), sizeof(F32) * 3) == 97); - ALScratchArena::Counters counters = ALScratchArena::counters(); - CHECK(counters.arena_blocks > 0); - CHECK(counters.heap_blocks == 0); - - // A hash table for more vertices than the arena holds comes from the - // heap, and the weld still comes out. - const size_t big = ALScratchArena::CAPACITY / sizeof(U32); - repeating(big, indices, positions); - remap.resize(big); - ALScratchArena::resetCounters(); - CHECK(meshopt_generateVertexRemap(remap.data(), indices.data(), indices.size(), positions.data(), big, sizeof(F32) * 3) == 97); - counters = ALScratchArena::counters(); - CHECK(counters.heap_blocks > 0); - CHECK(remap[97] == remap[0]); - } -} diff --git a/indra/llmath/tests/llvector4a_test.cpp b/indra/llmath/tests/llvector4a_test.cpp index 3479dc3f92..27712c557f 100644 --- a/indra/llmath/tests/llvector4a_test.cpp +++ b/indra/llmath/tests/llvector4a_test.cpp @@ -42,12 +42,18 @@ #include "../llquaternion.h" #include "../m3math.h" #include "../v3math.h" +#include "alscratcharena.h" + +#include #include #include #include #include +#include #include +#include +#include namespace { @@ -760,4 +766,58 @@ namespace // a dot product comes back in the lane that is read CHECK(n_a().dot3(LLVector4a(1.f, 0.f, 0.f)).getF32() == na[0]); } + + // meshoptimizer back on its own default, operator new and delete, as + // the scope ends. + struct DefaultAllocatorAfter + { + static void* MESHOPTIMIZER_ALLOC_CALLCONV newBlock(size_t bytes) { return ::operator new(bytes); } + static void MESHOPTIMIZER_ALLOC_CALLCONV deleteBlock(void* block) { ::operator delete(block); } + + ~DefaultAllocatorAfter() { meshopt_setAllocator(newBlock, deleteBlock); } + }; + + // Vertex i is (3i, 3i + 1, 3i + 2) modulo 97 a coordinate: 97 vertices, + // each count times over. + void repeating(size_t count, std::vector& indices, std::vector& positions) + { + indices.resize(count); + std::iota(indices.begin(), indices.end(), 0); + positions.resize(count * 3); + for (size_t i = 0; i < positions.size(); ++i) + { + positions[i] = (F32)(i % 97); + } + } + + TEST_CASE("LLVector4a: initClass has meshoptimizer take its blocks from the arena", "[LLVector4a]") + { + // initClass also sets the floating-point mode of the thread it runs + // on, so it runs on a thread of its own; the allocator it sets is + // every thread's. + DefaultAllocatorAfter restore; + std::thread([] { LLVector4a::initClass(); }).join(); + + std::vector indices; + std::vector positions; + repeating(3000, indices, positions); + std::vector remap(indices.size()); + + ALScratchArena::resetCounters(); + CHECK(meshopt_generateVertexRemap(remap.data(), indices.data(), indices.size(), positions.data(), indices.size(), sizeof(F32) * 3) == 97); + ALScratchArena::Counters counters = ALScratchArena::counters(); + CHECK(counters.arena_blocks > 0); + CHECK(counters.heap_blocks == 0); + + // A hash table for more vertices than the arena holds comes from the + // heap, and the weld still comes out. + const size_t big = ALScratchArena::CAPACITY / sizeof(U32); + repeating(big, indices, positions); + remap.resize(big); + ALScratchArena::resetCounters(); + CHECK(meshopt_generateVertexRemap(remap.data(), indices.data(), indices.size(), positions.data(), big, sizeof(F32) * 3) == 97); + counters = ALScratchArena::counters(); + CHECK(counters.heap_blocks > 0); + CHECK(remap[97] == remap[0]); + } } diff --git a/indra/llmath/tests/llvolume_bench.cpp b/indra/llmath/tests/llvolume_bench.cpp index ffb1d89dc7..ee7e0436c6 100644 --- a/indra/llmath/tests/llvolume_bench.cpp +++ b/indra/llmath/tests/llvolume_bench.cpp @@ -41,7 +41,6 @@ #include "alscratcharena.h" #include "alsimdkernels.h" #include "llmath.h" -#include "llmeshoptimizer.h" #include "llquaternion.h" #include "m3math.h" #include "v2math.h" @@ -567,7 +566,7 @@ namespace static void* MESHOPTIMIZER_ALLOC_CALLCONV newBlock(size_t bytes) { return ::operator new(bytes); } static void MESHOPTIMIZER_ALLOC_CALLCONV deleteBlock(void* block) { ::operator delete(block); } - ArenaInstalled() { LLMeshOptimizer::initClass(); } + ArenaInstalled() { meshopt_setAllocator(ALScratchArena::allocate, ALScratchArena::deallocate); } ~ArenaInstalled() { meshopt_setAllocator(newBlock, deleteBlock); } }; diff --git a/indra/llmath/tests/llvolume_test.cpp b/indra/llmath/tests/llvolume_test.cpp index 63ecaba9f2..70fd3dc5aa 100644 --- a/indra/llmath/tests/llvolume_test.cpp +++ b/indra/llmath/tests/llvolume_test.cpp @@ -52,7 +52,6 @@ #include "llvector4a.h" #include "alscratcharena.h" #include "alsimdkernels.h" -#include "llmeshoptimizer.h" #include "almeshcorpus.h" #include "altestfatal.h" @@ -2523,7 +2522,7 @@ namespace static void* MESHOPTIMIZER_ALLOC_CALLCONV newBlock(size_t bytes) { return ::operator new(bytes); } static void MESHOPTIMIZER_ALLOC_CALLCONV deleteBlock(void* block) { ::operator delete(block); } - ArenaInstalled() { LLMeshOptimizer::initClass(); } + ArenaInstalled() { meshopt_setAllocator(ALScratchArena::allocate, ALScratchArena::deallocate); } ~ArenaInstalled() { meshopt_setAllocator(newBlock, deleteBlock); } }; @@ -3526,6 +3525,83 @@ namespace } } + // A vertex whose position, normal and texture coordinate each differ from + // those of a vertex made from another n. + LLVolumeFace::VertexData distinct_vertex(F32 n) + { + LLVolumeFace::VertexData v; + v.setPosition(LLVector4a(n, n + 0.25f, n + 0.5f)); + v.setNormal(LLVector4a(-n, 1.f, n * 2.f)); + v.mTexCoord = LLVector2(n * 0.125f, 1.f - n * 0.125f); + return v; + } + + // A face of these vertices and indices, as an importer hands one to remap. + LLVolumeFace face_of(std::vector vertices, std::vector indices) + { + LLVolumeFace face; + face.fillFromLegacyData(vertices, indices); + return face; + } + + LLVolumeFace::VertexData vertex_at(const LLVolumeFace& face, U16 index) + { + LLVolumeFace::VertexData v; + v.setPosition(face.mPositions[index]); + v.setNormal(face.mNormals[index]); + v.mTexCoord = face.mTexCoords[index]; + return v; + } + + // remap welds an uploaded face as weldVertices does: copies made one, + // unused vertices gone, and every corner given back the vertex it had. + TEST_CASE("llvolume: remap welds copies and drops unused vertices with every corner keeping its vertex", "[llvolume]") + { + // 5 is a copy of 0, and no index uses 6. + const std::vector vertices = { distinct_vertex(1.f), distinct_vertex(2.f), distinct_vertex(3.f), + distinct_vertex(4.f), distinct_vertex(5.f), distinct_vertex(1.f), + distinct_vertex(6.f) }; + const std::vector indices = { 3, 5, 1, 0, 2, 4 }; + + LLVolumeFace face = face_of(vertices, indices); + face.remap(); + + REQUIRE(face.mNumIndices == (S32)indices.size()); + CHECK(face.mNumVertices == 5); + CHECK(face.mIndices[1] == face.mIndices[3]); + for (size_t i = 0; i < indices.size(); ++i) + { + INFO(fmt::format("corner {}, vertex {}, welded {}", i, indices[i], face.mIndices[i])); + REQUIRE(face.mIndices[i] < face.mNumVertices); + CHECK(vertex_at(face, face.mIndices[i]) == vertices[indices[i]]); + } + } + + // An uploaded face's indices are untrusted. remap takes one past the + // face's vertices, however far past, as vertex 0, and reads its weld's + // table, one entry a vertex, only through indices it has checked. + TEST_CASE("llvolume: remap takes an index past the face's vertices as vertex 0", "[llvolume]") + { + const std::vector vertices = { distinct_vertex(1.f), distinct_vertex(2.f), distinct_vertex(3.f), + distinct_vertex(4.f) }; + // Vertex 0 is not the first the indices use, so the weld does not + // number it 0. + const std::vector indices = { 1, 2, 3, 1, 3, 5, 2, 3, 65535 }; + + LLVolumeFace face = face_of(vertices, indices); + face.remap(); + + REQUIRE(face.mNumIndices == (S32)indices.size()); + CHECK(face.mNumVertices == 4); + for (size_t i = 0; i < indices.size(); ++i) + { + const size_t vertex = indices[i] < vertices.size() ? size_t(indices[i]) : size_t(0); + INFO(fmt::format("corner {}, index {}, welded {}", i, indices[i], face.mIndices[i])); + REQUIRE(face.mIndices[i] < face.mNumVertices); + CHECK(vertex_at(face, face.mIndices[i]) == vertices[vertex]); + } + } + // A flat parallelogram as authored, P = a e1 + b e2 for a and b in // [-0.5, 0.5], whose box is 4 by 1 by 1. Its UVs are turned 45 degrees // against its edges, u = (a + b) / 2 + 0.5 and v = (b - a) / 2 + 0.5, so u diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 17456882c0..9521c82269 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -113,14 +113,6 @@ void LLModel::remapVolumeFaces() } } -void LLModel::optimizeVolumeFaces() -{ - for (S32 i = 0; i < getNumVolumeFaces(); ++i) - { - mVolumeFaces[i].optimize(); - } -} - struct MaterialBinding { int index; diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index 8b7e41e312..55adbe7412 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -218,7 +218,6 @@ class alignas(16) LLModel : public LLVolume void normalizeVolumeFacesAndWeights(); void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL); void remapVolumeFaces(); - void optimizeVolumeFaces(); void offsetMesh( const LLVector3& pivotPoint ); void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out) const; LLVector3 getTransformedCenter(const LLMatrix4& mat); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 938bd8b681..bff29fed13 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -97,7 +97,6 @@ #include "lltoast.h" #include "llsdutil_math.h" #include "lllocationhistory.h" -#include "llmeshoptimizer.h" #include "llvector4a.h" #include "llviewermenufile.h" #include "llvoicechannel.h" @@ -808,12 +807,10 @@ bool LLAppViewer::init() // the translation bridge LLWearableType reads its labels through LLTranslationBridge::ptr_t trans = std::make_shared(); - // initialize SSE options + // llmath: the floating-point mode, and meshoptimizer's scratch from each + // thread's own arena LLVector4a::initClass(); - // meshoptimizer's scratch from each thread's own arena - LLMeshOptimizer::initClass(); - //initialize particle index pool LLVOPartGroup::initClass();