From b08340f1831005ae227577899a64408cc939a12d Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 10 Jun 2022 19:43:14 +0300 Subject: SL-17475 Remap models before simplification --- indra/newview/llmodelpreview.cpp | 81 ++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 27 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 859d987fc3..707a8b970f 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1223,6 +1223,7 @@ void LLModelPreview::restoreNormals() // returns -1 in case of failure F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_decimator, F32 error_threshold, bool sloppy) { + // I. Weld faces together // Figure out buffer size S32 size_indices = 0; S32 size_vertices = 0; @@ -1281,7 +1282,35 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe indices_idx_shift += face.mNumVertices; } - // Now that we have buffers, optimize + // II. Remap. + std::vector remap(size_indices); + S32 size_remap_vertices = LLMeshOptimizer::generateRemapMulti(&remap[0], + combined_indices, + size_indices, + combined_positions, + combined_normals, + combined_tex_coords, + size_vertices); + + // Allocate new buffers + U32* remap_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32)); + + S32 remap_tc_bytes_size = ((size_remap_vertices * sizeof(LLVector2)) + 0xF) & ~0xF; + LLVector4a* remap_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_remap_vertices + remap_tc_bytes_size); + LLVector4a* remap_normals = remap_positions + size_remap_vertices; + LLVector2* remap_tex_coords = (LLVector2*)(remap_normals + size_remap_vertices); + + // fill the buffers + LLMeshOptimizer::remapIndexBufferU32(remap_indices, combined_indices, size_indices, &remap[0]); + LLMeshOptimizer::remapPositionsBuffer(remap_positions, combined_positions, size_vertices, &remap[0]); + LLMeshOptimizer::remapNormalsBuffer(remap_normals, combined_normals, size_vertices, &remap[0]); + LLMeshOptimizer::remapUVBuffer(remap_tex_coords, combined_tex_coords, size_vertices, &remap[0]); + + // free unused buffers + ll_aligned_free<64>(combined_positions); + ll_aligned_free_32(combined_indices); + + // III. Simplify S32 target_indices = 0; F32 result_error = 0; // how far from original the model is, 1 == 100% S32 new_indices = 0; @@ -1294,19 +1323,19 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe { target_indices = 3; } + new_indices = LLMeshOptimizer::simplifyU32( output_indices, - combined_indices, + remap_indices, size_indices, - combined_positions, - size_vertices, + remap_positions, + size_remap_vertices, LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX], target_indices, error_threshold, sloppy, &result_error); - if (result_error < 0) { LL_WARNS() << "Negative result error from meshoptimizer for model " << target_model->mLabel @@ -1315,24 +1344,25 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe << " original count: " << size_indices << LL_ENDL; } + ll_aligned_free_32(remap_indices); + if (new_indices < 3) { // Model should have at least one visible triangle - ll_aligned_free<64>(combined_positions); + ll_aligned_free<64>(remap_positions); ll_aligned_free_32(output_indices); - ll_aligned_free_32(combined_indices); return -1; } - // repack back into individual faces + // IV. Repack back into individual faces - LLVector4a* buffer_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_vertices + tc_bytes_size); - LLVector4a* buffer_normals = buffer_positions + size_vertices; - LLVector2* buffer_tex_coords = (LLVector2*)(buffer_normals + size_vertices); + LLVector4a* buffer_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_remap_vertices + tc_bytes_size); + LLVector4a* buffer_normals = buffer_positions + size_remap_vertices; + LLVector2* buffer_tex_coords = (LLVector2*)(buffer_normals + size_remap_vertices); S32 buffer_idx_size = (size_indices * sizeof(U16) + 0xF) & ~0xF; U16* buffer_indices = (U16*)ll_aligned_malloc_16(buffer_idx_size); - S32* old_to_new_positions_map = new S32[size_vertices]; + S32* old_to_new_positions_map = new S32[size_remap_vertices]; S32 buf_positions_copied = 0; S32 buf_indices_copied = 0; @@ -1350,7 +1380,7 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe bool copy_triangle = false; S32 range = indices_idx_shift + face.mNumVertices; - for (S32 i = 0; i < size_vertices; i++) + for (S32 i = 0; i < size_remap_vertices; i++) { old_to_new_positions_map[i] = -1; } @@ -1408,9 +1438,9 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe } // Copy vertice, normals, tcs - buffer_positions[buf_positions_copied] = combined_positions[idx]; - buffer_normals[buf_positions_copied] = combined_normals[idx]; - buffer_tex_coords[buf_positions_copied] = combined_tex_coords[idx]; + buffer_positions[buf_positions_copied] = remap_positions[idx]; + buffer_normals[buf_positions_copied] = remap_normals[idx]; + buffer_tex_coords[buf_positions_copied] = remap_tex_coords[idx]; old_to_new_positions_map[idx] = buf_positions_copied; @@ -1465,11 +1495,10 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe } delete[]old_to_new_positions_map; - ll_aligned_free<64>(combined_positions); + ll_aligned_free<64>(remap_positions); ll_aligned_free<64>(buffer_positions); ll_aligned_free_32(output_indices); ll_aligned_free_16(buffer_indices); - ll_aligned_free_32(combined_indices); if (new_indices < 3 || valid_faces == 0) { @@ -1488,10 +1517,9 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target { return -1; } - // todo: do not allocate per each face, add one large buffer somewhere - // faces have limited amount of indices + S32 size = (size_indices * sizeof(U16) + 0xF) & ~0xF; - U16* output = (U16*)ll_aligned_malloc_16(size); + U16* output_indices = (U16*)ll_aligned_malloc_16(size); S32 target_indices = 0; F32 result_error = 0; // how far from original the model is, 1 == 100% @@ -1505,8 +1533,9 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target { target_indices = 3; } + new_indices = LLMeshOptimizer::simplify( - output, + output_indices, face.mIndices, size_indices, face.mPositions, @@ -1517,7 +1546,6 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target sloppy, &result_error); - if (result_error < 0) { LL_WARNS() << "Negative result error from meshoptimizer for face " << face_idx @@ -1534,7 +1562,6 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target // Copy old values new_face = face; - if (new_indices < 3) { if (!sloppy) @@ -1563,13 +1590,13 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target // Assign new values new_face.resizeIndices(new_indices); // will wipe out mIndices, so new_face can't substitute output S32 idx_size = (new_indices * sizeof(U16) + 0xF) & ~0xF; - LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)output, idx_size); + LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)output_indices, idx_size); // clear unused values new_face.optimize(); } - ll_aligned_free_16(output); + ll_aligned_free_16(output_indices); if (new_indices < 3) { @@ -1711,7 +1738,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // Ideally this should run not per model, // but combine all submodels with origin model as well - if (model_meshopt_mode == MESH_OPTIMIZER_COMBINE) + if (model_meshopt_mode == MESH_OPTIMIZER_PRECISE) { // Run meshoptimizer for each model/object, up to 8 faces in one model. -- cgit v1.2.3 From 45bcefd981e268b158d11d59f2ba9063293986a6 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 14 Jun 2022 16:39:46 +0300 Subject: SL-17475 fix remap causing an assert --- indra/newview/llmodelpreview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 707a8b970f..ef791fd80d 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1284,7 +1284,7 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe // II. Remap. std::vector remap(size_indices); - S32 size_remap_vertices = LLMeshOptimizer::generateRemapMulti(&remap[0], + S32 size_remap_vertices = LLMeshOptimizer::generateRemapMultiU32(&remap[0], combined_indices, size_indices, combined_positions, -- cgit v1.2.3 From b1cbf369cf63f270cb810f640a14420be53035ed Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 14 Jun 2022 18:33:50 +0300 Subject: SL-17475 Use a shadow indices buffer before simplification --- indra/newview/llmodelpreview.cpp | 225 ++++++++++++++++++++++++--------------- 1 file changed, 138 insertions(+), 87 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index ef791fd80d..4ec2d82350 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1221,7 +1221,7 @@ void LLModelPreview::restoreNormals() // Runs per object, but likely it is a better way to run per model+submodels // returns a ratio of base model indices to resulting indices // returns -1 in case of failure -F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_decimator, F32 error_threshold, bool sloppy) +F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_decimator, F32 error_threshold, eSimplificationMode simplification_mode) { // I. Weld faces together // Figure out buffer size @@ -1258,20 +1258,21 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe { const LLVolumeFace &face = base_model->getVolumeFace(face_idx); - // vertices + // Vertices S32 copy_bytes = face.mNumVertices * sizeof(LLVector4a); LLVector4a::memcpyNonAliased16((F32*)(combined_positions + combined_positions_shift), (F32*)face.mPositions, copy_bytes); - // normals + // Normals LLVector4a::memcpyNonAliased16((F32*)(combined_normals + combined_positions_shift), (F32*)face.mNormals, copy_bytes); - // tex coords + // Tex coords copy_bytes = face.mNumVertices * sizeof(LLVector2); memcpy((void*)(combined_tex_coords + combined_positions_shift), (void*)face.mTexCoords, copy_bytes); combined_positions_shift += face.mNumVertices; - // indices, sadly can't do dumb memcpy for indices, need to adjust each value + // Indices + // Sadly can't do dumb memcpy for indices, need to adjust each value for (S32 i = 0; i < face.mNumIndices; ++i) { U16 idx = face.mIndices[i]; @@ -1282,38 +1283,42 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe indices_idx_shift += face.mNumVertices; } - // II. Remap. - std::vector remap(size_indices); - S32 size_remap_vertices = LLMeshOptimizer::generateRemapMultiU32(&remap[0], - combined_indices, - size_indices, - combined_positions, - combined_normals, - combined_tex_coords, - size_vertices); - - // Allocate new buffers - U32* remap_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32)); + // II. Generate a shadow buffer if nessesary. + // Welds together vertices if possible - S32 remap_tc_bytes_size = ((size_remap_vertices * sizeof(LLVector2)) + 0xF) & ~0xF; - LLVector4a* remap_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_remap_vertices + remap_tc_bytes_size); - LLVector4a* remap_normals = remap_positions + size_remap_vertices; - LLVector2* remap_tex_coords = (LLVector2*)(remap_normals + size_remap_vertices); - - // fill the buffers - LLMeshOptimizer::remapIndexBufferU32(remap_indices, combined_indices, size_indices, &remap[0]); - LLMeshOptimizer::remapPositionsBuffer(remap_positions, combined_positions, size_vertices, &remap[0]); - LLMeshOptimizer::remapNormalsBuffer(remap_normals, combined_normals, size_vertices, &remap[0]); - LLMeshOptimizer::remapUVBuffer(remap_tex_coords, combined_tex_coords, size_vertices, &remap[0]); + U32* shadow_indices = NULL; + // if MESH_OPTIMIZER_FULL, just leave as is, since generateShadowIndexBufferU32 + // won't do anything new, model was remaped on a per face basis. + // Similar for MESH_OPTIMIZER_NO_TOPOLOGY, it's pointless + // since 'simplifySloppy' ignores all topology, including normals and uvs. + // Note: simplifySloppy can affect UVs significantly. + if (simplification_mode == MESH_OPTIMIZER_NO_NORMALS) + { + // strip normals, reflections should restore relatively correctly + shadow_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32)); + LLMeshOptimizer::generateShadowIndexBufferU32(shadow_indices, combined_indices, size_indices, combined_positions, NULL, combined_tex_coords, size_vertices); + } + if (simplification_mode == MESH_OPTIMIZER_NO_UVS) + { + // strip uvs, can heavily affect textures + shadow_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32)); + LLMeshOptimizer::generateShadowIndexBufferU32(shadow_indices, combined_indices, size_indices, combined_positions, NULL, NULL, size_vertices); + } - // free unused buffers - ll_aligned_free<64>(combined_positions); - ll_aligned_free_32(combined_indices); + U32* source_indices = NULL; + if (shadow_indices) + { + source_indices = shadow_indices; + } + else + { + source_indices = combined_indices; + } // III. Simplify S32 target_indices = 0; F32 result_error = 0; // how far from original the model is, 1 == 100% - S32 new_indices = 0; + S32 size_new_indices = 0; if (indices_decimator > 0) { @@ -1324,32 +1329,36 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe target_indices = 3; } - new_indices = LLMeshOptimizer::simplifyU32( + size_new_indices = LLMeshOptimizer::simplifyU32( output_indices, - remap_indices, + source_indices, size_indices, - remap_positions, - size_remap_vertices, + combined_positions, + size_vertices, LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX], target_indices, error_threshold, - sloppy, + simplification_mode == MESH_OPTIMIZER_NO_TOPOLOGY, &result_error); if (result_error < 0) { LL_WARNS() << "Negative result error from meshoptimizer for model " << target_model->mLabel << " target Indices: " << target_indices - << " new Indices: " << new_indices + << " new Indices: " << size_new_indices << " original count: " << size_indices << LL_ENDL; } - ll_aligned_free_32(remap_indices); + // free unused buffers + ll_aligned_free_32(combined_indices); + ll_aligned_free_32(shadow_indices); + combined_indices = NULL; + shadow_indices = NULL; - if (new_indices < 3) + if (size_new_indices < 3) { // Model should have at least one visible triangle - ll_aligned_free<64>(remap_positions); + ll_aligned_free<64>(combined_positions); ll_aligned_free_32(output_indices); return -1; @@ -1357,12 +1366,12 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe // IV. Repack back into individual faces - LLVector4a* buffer_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_remap_vertices + tc_bytes_size); - LLVector4a* buffer_normals = buffer_positions + size_remap_vertices; - LLVector2* buffer_tex_coords = (LLVector2*)(buffer_normals + size_remap_vertices); + LLVector4a* buffer_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_vertices + tc_bytes_size); + LLVector4a* buffer_normals = buffer_positions + size_vertices; + LLVector2* buffer_tex_coords = (LLVector2*)(buffer_normals + size_vertices); S32 buffer_idx_size = (size_indices * sizeof(U16) + 0xF) & ~0xF; U16* buffer_indices = (U16*)ll_aligned_malloc_16(buffer_idx_size); - S32* old_to_new_positions_map = new S32[size_remap_vertices]; + S32* old_to_new_positions_map = new S32[size_vertices]; S32 buf_positions_copied = 0; S32 buf_indices_copied = 0; @@ -1380,13 +1389,13 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe bool copy_triangle = false; S32 range = indices_idx_shift + face.mNumVertices; - for (S32 i = 0; i < size_remap_vertices; i++) + for (S32 i = 0; i < size_vertices; i++) { old_to_new_positions_map[i] = -1; } // Copy relevant indices and vertices - for (S32 i = 0; i < new_indices; ++i) + for (S32 i = 0; i < size_new_indices; ++i) { U32 idx = output_indices[i]; @@ -1409,19 +1418,19 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe LL_WARNS() << "Over triangle limit. Failed to optimize in 'per object' mode, falling back to per face variant for" << " model " << target_model->mLabel << " target Indices: " << target_indices - << " new Indices: " << new_indices + << " new Indices: " << size_new_indices << " original count: " << size_indices << " error treshold: " << error_threshold << LL_ENDL; // U16 vertices overflow shouldn't happen, but just in case - new_indices = 0; + size_new_indices = 0; valid_faces = 0; for (U32 face_idx = 0; face_idx < base_model->getNumVolumeFaces(); ++face_idx) { - genMeshOptimizerPerFace(base_model, target_model, face_idx, indices_decimator, error_threshold, false); + genMeshOptimizerPerFace(base_model, target_model, face_idx, indices_decimator, error_threshold, simplification_mode); const LLVolumeFace &face = target_model->getVolumeFace(face_idx); - new_indices += face.mNumIndices; + size_new_indices += face.mNumIndices; if (face.mNumIndices >= 3) { valid_faces++; @@ -1429,7 +1438,7 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe } if (valid_faces) { - return (F32)size_indices / (F32)new_indices; + return (F32)size_indices / (F32)size_new_indices; } else { @@ -1438,9 +1447,9 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe } // Copy vertice, normals, tcs - buffer_positions[buf_positions_copied] = remap_positions[idx]; - buffer_normals[buf_positions_copied] = remap_normals[idx]; - buffer_tex_coords[buf_positions_copied] = remap_tex_coords[idx]; + buffer_positions[buf_positions_copied] = combined_positions[idx]; + buffer_normals[buf_positions_copied] = combined_normals[idx]; + buffer_tex_coords[buf_positions_copied] = combined_tex_coords[idx]; old_to_new_positions_map[idx] = buf_positions_copied; @@ -1495,21 +1504,21 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe } delete[]old_to_new_positions_map; - ll_aligned_free<64>(remap_positions); + ll_aligned_free<64>(combined_positions); ll_aligned_free<64>(buffer_positions); ll_aligned_free_32(output_indices); ll_aligned_free_16(buffer_indices); - if (new_indices < 3 || valid_faces == 0) + if (size_new_indices < 3 || valid_faces == 0) { // Model should have at least one visible triangle return -1; } - return (F32)size_indices / (F32)new_indices; + return (F32)size_indices / (F32)size_new_indices; } -F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_decimator, F32 error_threshold, bool sloppy) +F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_decimator, F32 error_threshold, eSimplificationMode simplification_mode) { const LLVolumeFace &face = base_model->getVolumeFace(face_idx); S32 size_indices = face.mNumIndices; @@ -1521,9 +1530,36 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target S32 size = (size_indices * sizeof(U16) + 0xF) & ~0xF; U16* output_indices = (U16*)ll_aligned_malloc_16(size); + U16* shadow_indices = NULL; + // if MESH_OPTIMIZER_FULL, just leave as is, since generateShadowIndexBufferU32 + // won't do anything new, model was remaped on a per face basis. + // Similar for MESH_OPTIMIZER_NO_TOPOLOGY, it's pointless + // since 'simplifySloppy' ignores all topology, including normals and uvs. + if (simplification_mode == MESH_OPTIMIZER_NO_NORMALS) + { + U16* shadow_indices = (U16*)ll_aligned_malloc_16(size); + LLMeshOptimizer::generateShadowIndexBufferU16(shadow_indices, face.mIndices, size_indices, face.mPositions, NULL, face.mTexCoords, face.mNumVertices); + } + if (simplification_mode == MESH_OPTIMIZER_NO_UVS) + { + U16* shadow_indices = (U16*)ll_aligned_malloc_16(size); + LLMeshOptimizer::generateShadowIndexBufferU16(shadow_indices, face.mIndices, size_indices, face.mPositions, NULL, NULL, face.mNumVertices); + } + // Don't run ShadowIndexBuffer for MESH_OPTIMIZER_NO_TOPOLOGY, it's pointless + + U16* source_indices = NULL; + if (shadow_indices) + { + source_indices = shadow_indices; + } + else + { + source_indices = face.mIndices; + } + S32 target_indices = 0; F32 result_error = 0; // how far from original the model is, 1 == 100% - S32 new_indices = 0; + S32 size_new_indices = 0; if (indices_decimator > 0) { @@ -1534,16 +1570,16 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target target_indices = 3; } - new_indices = LLMeshOptimizer::simplify( + size_new_indices = LLMeshOptimizer::simplify( output_indices, - face.mIndices, + source_indices, size_indices, face.mPositions, face.mNumVertices, LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX], target_indices, error_threshold, - sloppy, + simplification_mode == MESH_OPTIMIZER_NO_TOPOLOGY, &result_error); if (result_error < 0) @@ -1551,7 +1587,7 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target LL_WARNS() << "Negative result error from meshoptimizer for face " << face_idx << " of model " << target_model->mLabel << " target Indices: " << target_indices - << " new Indices: " << new_indices + << " new Indices: " << size_new_indices << " original count: " << size_indices << " error treshold: " << error_threshold << LL_ENDL; @@ -1562,9 +1598,9 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target // Copy old values new_face = face; - if (new_indices < 3) + if (size_new_indices < 3) { - if (!sloppy) + if (simplification_mode != MESH_OPTIMIZER_NO_TOPOLOGY) { // meshopt_optimizeSloppy() can optimize triangles away even if target_indices is > 2, // but optimize() isn't supposed to @@ -1588,23 +1624,24 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target else { // Assign new values - new_face.resizeIndices(new_indices); // will wipe out mIndices, so new_face can't substitute output - S32 idx_size = (new_indices * sizeof(U16) + 0xF) & ~0xF; + new_face.resizeIndices(size_new_indices); // will wipe out mIndices, so new_face can't substitute output + S32 idx_size = (size_new_indices * sizeof(U16) + 0xF) & ~0xF; LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)output_indices, idx_size); - // clear unused values + // Clear unused values new_face.optimize(); } ll_aligned_free_16(output_indices); + ll_aligned_free_16(shadow_indices); - if (new_indices < 3) + if (size_new_indices < 3) { // At least one triangle is needed return -1; } - return (F32)size_indices / (F32)new_indices; + return (F32)size_indices / (F32)size_new_indices; } void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 decimation, bool enforce_tri_limit) @@ -1740,14 +1777,17 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // but combine all submodels with origin model as well if (model_meshopt_mode == MESH_OPTIMIZER_PRECISE) { - // Run meshoptimizer for each model/object, up to 8 faces in one model. - - // Ideally this should run not per model, - // but combine all submodels with origin model as well - F32 res = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); - if (res < 0) + // Run meshoptimizer for each face + for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) { - target_model->copyVolumeFaces(base); + F32 res = genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL); + if (res < 0) + { + // Mesh optimizer failed and returned an invalid model + const LLVolumeFace &face = base->getVolumeFace(face_idx); + LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); + new_face = face; + } } } @@ -1756,19 +1796,29 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // Run meshoptimizer for each face for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) { - if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, true) < 0) + if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY) < 0) { // Sloppy failed and returned an invalid model - genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); + genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL); } } } if (model_meshopt_mode == MESH_OPTIMIZER_AUTO) { - // Switches between 'combine' method and 'sloppy' based on combine's result. - F32 allowed_ratio_drift = 2.f; - F32 precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); + // Remove progressively more data if we can't reach the target. + F32 allowed_ratio_drift = 1.8f; + F32 precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL); + + if (precise_ratio < 0 || (precise_ratio * allowed_ratio_drift < indices_decimator)) + { + precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_NORMALS); + } + + if (precise_ratio < 0 || (precise_ratio * allowed_ratio_drift < indices_decimator)) + { + precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_UVS); + } if (precise_ratio < 0 || (precise_ratio * allowed_ratio_drift < indices_decimator)) { @@ -1776,10 +1826,11 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // Sloppy variant can fail entirely and has issues with precision, // so code needs to do multiple attempts with different decimators. // Todo: this is a bit of a mess, needs to be refined and improved + F32 last_working_decimator = 0.f; F32 last_working_ratio = F32_MAX; - F32 sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true); + F32 sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY); if (sloppy_ratio > 0) { @@ -1802,13 +1853,13 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // side due to overal lack of precision, and we don't need an ideal result, which // likely does not exist, just a better one, so a partial correction is enough. F32 sloppy_decimator = indices_decimator * (indices_decimator / sloppy_ratio + 1) / 2; - sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, true); + sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY); } if (last_working_decimator > 0 && sloppy_ratio < last_working_ratio) { // Compensation didn't work, return back to previous decimator - sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true); + sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY); } if (sloppy_ratio < 0) @@ -1841,7 +1892,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d && sloppy_decimator > precise_ratio && sloppy_decimator > 1)// precise_ratio isn't supposed to be below 1, but check just in case { - sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, true); + sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY); sloppy_decimator = sloppy_decimator / sloppy_decimation_step; } } @@ -1861,7 +1912,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d else { // Fallback to normal method - precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); + precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL); } LL_INFOS() << "Model " << target_model->getName() -- cgit v1.2.3 From 429274c4681da05d2e1ea7eb37b5d031fb4b1347 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 4 Jul 2022 20:19:50 +0300 Subject: SL-17545 Recalculate lods when replacing high lod with something incompatible --- indra/newview/llmodelpreview.cpp | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 4ec2d82350..d8131171bd 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -816,8 +816,10 @@ void LLModelPreview::clearIncompatible(S32 lod) // at this point we don't care about sub-models, // different amount of sub-models means face count mismatch, not incompatibility U32 lod_size = countRootModels(mModel[lod]); + bool replaced_base_model = (lod == LLModel::LOD_HIGH); for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) - { //clear out any entries that aren't compatible with this model + { + // Clear out any entries that aren't compatible with this model if (i != lod) { if (countRootModels(mModel[i]) != lod_size) @@ -831,10 +833,45 @@ void LLModelPreview::clearIncompatible(S32 lod) mBaseModel = mModel[lod]; mBaseScene = mScene[lod]; mVertexBuffer[5].clear(); + replaced_base_model = true; } } } } + + if (replaced_base_model) + { + // In case base was replaced, we might need to restart generation + bool subscribe_for_generation = mLodsQuery.empty(); + if (lod == LLModel::LOD_HIGH) + { + mLodsQuery.clear(); + } + + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (!fmp) return; + + for (S32 i = LLModel::LOD_HIGH; i >= 0; --i) + { + if (mModel[i].empty()) + { + // Base model was replaced, regenerate this lod if applicable + LLComboBox* lod_combo = mFMP->findChild("lod_source_" + lod_name[i]); + if (!lod_combo) return; + + S32 lod_mode = lod_combo->getCurrentIndex(); + if (lod_mode != LOD_FROM_FILE) + { + mLodsQuery.push_back(i); + } + } + } + + if (!mLodsQuery.empty() && subscribe_for_generation) + { + doOnIdleRepeating(lodQueryCallback); + } + } } void LLModelPreview::loadModelCallback(S32 loaded_lod) -- cgit v1.2.3 From 3dcdc4c85a33437f4de9ce474728ef4ab99eb5c2 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 6 Jul 2022 15:57:58 +0300 Subject: SL-17545 fix double-scheduling of calculations --- indra/newview/llmodelpreview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index d8131171bd..2920521d31 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -839,7 +839,7 @@ void LLModelPreview::clearIncompatible(S32 lod) } } - if (replaced_base_model) + if (replaced_base_model && !mGenLOD) { // In case base was replaced, we might need to restart generation bool subscribe_for_generation = mLodsQuery.empty(); -- cgit v1.2.3 From 2cf9f046a6135edd970001a2820c2e8250d763a0 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 7 Jul 2022 20:50:56 +0300 Subject: SL-17545 lodQueryCallback should quit as soon as it is done --- indra/newview/llmodelpreview.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'indra/newview/llmodelpreview.cpp') diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 2920521d31..3bca4fde83 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -842,15 +842,17 @@ void LLModelPreview::clearIncompatible(S32 lod) if (replaced_base_model && !mGenLOD) { // In case base was replaced, we might need to restart generation + + // Check if already started bool subscribe_for_generation = mLodsQuery.empty(); - if (lod == LLModel::LOD_HIGH) - { - mLodsQuery.clear(); - } + + // Remove previously scheduled work + mLodsQuery.clear(); LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; if (!fmp) return; + // Schedule new work for (S32 i = LLModel::LOD_HIGH; i >= 0; --i) { if (mModel[i].empty()) @@ -867,6 +869,7 @@ void LLModelPreview::clearIncompatible(S32 lod) } } + // Subscribe if we have pending work and not subscribed yet if (!mLodsQuery.empty() && subscribe_for_generation) { doOnIdleRepeating(lodQueryCallback); @@ -3860,7 +3863,7 @@ bool LLModelPreview::lodQueryCallback() } // return false to continue cycle - return false; + return preview->mLodsQuery.empty(); } } // nothing to process -- cgit v1.2.3