< Summary

Class:DCL.CombineLayerUtils
Assembly:AvatarMeshCombiner
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Avatar/AvatarMeshCombiner/CombineLayerUtils.cs
Covered lines:44
Uncovered lines:18
Coverable lines:62
Total lines:209
Line coverage:70.9% (44 of 62)
Covered branches:0
Total branches:0
Covered methods:4
Total methods:4
Method coverage:100% (4 of 4)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
CombineLayerUtils()0%110100%
TrySlice(...)0%5.015092.31%
SubsliceLayerByTextures(...)0%6.356078.57%
AddMapIds(...)0%770100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Avatar/AvatarMeshCombiner/CombineLayerUtils.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using DCL.Shaders;
 5using MainScripts.DCL.Helpers.Utils;
 6using System.Diagnostics;
 7using UnityEngine;
 8using Debug = UnityEngine.Debug;
 9using logger = DCL.MeshCombinerLogger;
 10
 11
 12namespace DCL
 13{
 14    public static class CombineLayerUtils
 15    {
 16        // This heuristic forces double-sided opaque objects to have backface culling.
 17        // As many wearables are incorrectly modeled as double-sided, this greatly increases
 18        // the cases of avatars rendered with one draw call. Temporarily disabled until some wearables are fixed.
 119        public static bool ENABLE_CULL_OPAQUE_HEURISTIC = false;
 20
 21        private const int MAX_TEXTURE_ID_COUNT = 12;
 122        private static readonly int[] textureIds = { ShaderUtils.BaseMap, ShaderUtils.EmissionMap };
 23
 24        /// <summary>
 25        /// This method takes a skinned mesh renderer list and turns it into a series of CombineLayer elements.<br/>
 26        ///
 27        /// Each CombineLayer element represents a combining group, and the renderers are grouped using a set of criteri
 28        ///
 29        /// <ul>
 30        /// <li>Each layer must correspond to renderers that share the same cull mode and blend state.</li>
 31        /// <li>Each layer can't contain renderers that sum up over MAX_TEXTURE_ID_COUNT textures.</li>
 32        /// </ul>
 33        /// Those layers can later be used to combine meshes as efficiently as possible by encoding their map
 34        /// samplers to UV attributes. This allows the usage of a special shader that can branch samplers according to
 35        /// the UV data. By encoding the samplers this way, materials that use different textures and uniform values
 36        /// can be grouped together, and thus, meshes can be further combined.
 37        /// </summary>
 38        /// <param name="renderers">List of renderers to slice.</param>
 39        /// <returns>List of CombineLayer objects that can be used to produce a highly optimized combined mesh.</returns
 40        internal static bool TrySlice(IReadOnlyList<SkinnedMeshRenderer> renderers, CombineLayersList result)
 41        {
 42            logger.Log("Slice Start!");
 43
 1444            using (var rental = PoolUtils.RentListOfDisposables<CombineLayer>())
 45            {
 1446                var rawLayers = rental.GetList();
 1447                SliceByRenderState.Execute(renderers, rawLayers, ENABLE_CULL_OPAQUE_HEURISTIC);
 48
 49                logger.Log($"Preparing slice. Found {rawLayers.Count} groups.");
 50
 51                // Now, we sub-slice the rawLayers.
 52                // A single rawLayer will be sub-sliced if the textures exceed the sampler limit (12 in this case).
 53                // Also, in this step the textureToId map is populated.
 54
 8455                for (int i = 0; i < rawLayers.Count; i++)
 56                {
 2857                    var rawLayer = rawLayers[i];
 58
 59                    logger.Log($"Processing group {i}. Renderer count: {rawLayer.Renderers.Count}. cullMode: {rawLayer.c
 2860                    SubsliceLayerByTextures(rawLayer, result);
 61                }
 1462            }
 63
 64            // No valid materials were found
 1465            if (result.Count == 1 && result[0].textureToId.Count == 0 && result[0].Renderers.Count == 0)
 66            {
 67                logger.Log("Slice End Fail!");
 068                return false;
 69            }
 70
 1471            result.Sanitize();
 72
 73            [Conditional(MeshCombinerLogger.COMPILATION_DEFINE)]
 74            static void LogLayers(CombineLayersList result)
 75            {
 076                int layInd = 0;
 77
 078                foreach (var layer in result.Layers)
 79                {
 080                    string rendererNames = layer.Renderers
 081                                                .Select((x) => $"{x.transform.parent.name}")
 082                                                .Aggregate((i, j) => i + "\n" + j);
 83
 84                    logger.Log($"Layer index: {layInd} ... renderer count: {layer.Renderers.Count} ... textures found: {
 085                    layInd++;
 86                }
 087            }
 88
 89            LogLayers(result);
 90
 91            logger.Log("Slice End Success!");
 1492            return true;
 93        }
 94
 95        /// <summary>
 96        /// <p>
 97        /// This method takes a single CombineLayer and sub-slices it according to the texture count of the
 98        /// contained renderers of the given layer.
 99        /// </p>
 100        /// <p>
 101        /// The resulting layers will have their <i>textureToId</i> field populated with the found textures.
 102        /// The <i>textureToId</i> int value is what will have to be passed over the uv attributes of the combined meshe
 103        /// </p>
 104        /// </summary>
 105        /// <param name="layer">The CombineLayer layer to subdivide and populate by the ids.</param>
 106        /// <returns>A list that at least is guaranteed to contain the given layer.
 107        /// If the given layer exceeds the max texture count, more than a layer can be returned.
 108        /// </returns>
 109        internal static void SubsliceLayerByTextures(CombineLayer layer, CombineLayersList results)
 110        {
 111            int textureId;
 112
 113            CombineLayer currentResultLayer;
 114
 115            void AddLayerToResult()
 116            {
 31117                textureId = 0;
 31118                currentResultLayer = CombineLayer.Rent(layer.cullMode, layer.isOpaque);
 31119                results.Add(currentResultLayer);
 31120            }
 121
 30122            AddLayerToResult();
 123
 262124            for (int rendererIndex = 0; rendererIndex < layer.Renderers.Count; rendererIndex++)
 125            {
 101126                var r = layer.Renderers[rendererIndex];
 127
 101128                using var materialRent = PoolUtils.RentList<Material>();
 101129                var mats = materialRent.GetList();
 130
 101131                r.GetSharedMaterials(mats);
 132
 101133                using var mapIdsToInsertRental = PoolUtils.RentDictionary<Texture2D, int>();
 101134                var mapIdsToInsert = mapIdsToInsertRental.GetDictionary();
 135
 101136                AddMapIds(
 137                    currentResultLayer.textureToId,
 138                    mapIdsToInsert,
 139                    mats,
 140                    textureId);
 141
 142                // The renderer is too big to fit in a single layer? (This should never happen).
 101143                if (mapIdsToInsert.Count > MAX_TEXTURE_ID_COUNT)
 144                {
 0145                    logger.LogWarning("The renderer is too big to fit in a single layer? (This should never happen).");
 0146                    AddLayerToResult();
 0147                    continue;
 148                }
 149
 150                // The renderer can fit in a single layer.
 151                // But can't fit in this one, as previous renderers filled this layer out.
 101152                if (textureId + mapIdsToInsert.Count > MAX_TEXTURE_ID_COUNT)
 153                {
 0154                    rendererIndex--;
 0155                    AddLayerToResult();
 0156                    continue;
 157                }
 158
 159                // put GetMapIds result into currentLayer id map.
 386160                foreach (var kvp in mapIdsToInsert)
 92161                    currentResultLayer.textureToId[kvp.Key] = kvp.Value;
 162
 101163                results.AddRenderer(currentResultLayer, r);
 164
 101165                textureId += mapIdsToInsert.Count;
 166
 101167                if (textureId >= MAX_TEXTURE_ID_COUNT)
 1168                    AddLayerToResult();
 169            }
 170
 171            [Conditional(MeshCombinerLogger.COMPILATION_DEFINE)]
 172            static void LogResults(CombineLayersList results)
 173            {
 0174                for (int i = 0; i < results.Count; i++)
 175                {
 0176                    var c = results[i];
 0177                    Debug.Log($"layer {i} - {c}");
 178                }
 0179            }
 180
 181            LogResults(results);
 30182        }
 183
 184        internal static void AddMapIds(IReadOnlyDictionary<Texture2D, int> refDict, IDictionary<Texture2D, int> candidat
 185        {
 420186            for (int i = 0; i < mats.Count; i++)
 187            {
 107188                var mat = mats[i];
 189
 107190                if (mat == null)
 191                    continue;
 192
 642193                for (int texIdIndex = 0; texIdIndex < textureIds.Length; texIdIndex++)
 194                {
 214195                    var texture = (Texture2D)mat.GetTexture(textureIds[texIdIndex]);
 196
 214197                    if (texture == null)
 198                        continue;
 199
 136200                    if (refDict.ContainsKey(texture) || candidates.ContainsKey(texture))
 201                        continue;
 202
 102203                    candidates.Add(texture, startingId);
 102204                    startingId++;
 205                }
 206            }
 103207        }
 208    }
 209}