< Summary

Class:DCL.SceneMetricsCounter
Assembly:MainScripts
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/WorldRuntime/SceneMetricsCounter.cs
Covered lines:166
Uncovered lines:24
Coverable lines:190
Total lines:413
Line coverage:87.3% (166 of 190)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
EntityMetrics()0%110100%
ToString()0%110100%
SceneMetricsCounter()0%330100%
SceneMetricsCounter(...)0%110100%
GetModel()0%110100%
Enable()0%22090.91%
Disable()0%2.012085.71%
OnWillAddRendereable(...)0%5.065086.67%
OnWillRemoveRendereable(...)0%6.26082.35%
OnEntityAdded(...)0%2.022083.33%
OnEntityRemoved(...)0%2.012085.71%
OnEntityMeshInfoUpdated(...)0%2.152066.67%
OnEntityMeshInfoCleaned(...)0%2.152066.67%
AddOrReplaceMetrics(...)0%3.073080%
SubstractMetrics(...)0%4.014091.67%
AddMetrics(...)0%5.145082.35%
CalculateMaterials(...)0%330100%
AddMaterial(...)0%4.024088.89%
RemoveEntitiesMaterial(...)0%440100%
Dispose()0%2.012085.71%
GetLimits()0%220100%
IsInsideTheLimits()0%8.817066.67%
AddExcludedEntity(...)0%220100%
RemoveExcludedEntity(...)0%220100%
SendEvent()0%220100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/WorldRuntime/SceneMetricsCounter.cs

#LineLine coverage
 1using System;
 2using DCL.Controllers;
 3using DCL.Interface;
 4using DCL.Models;
 5using System.Collections.Generic;
 6using System.Linq;
 7using UnityEngine;
 8using UnityEngine.Assertions;
 9
 10namespace DCL
 11{
 12    [System.Serializable]
 13    public sealed class SceneMetricsCounter : ISceneMetricsCounter
 14    {
 15        public static class LimitsConfig
 16        {
 17            // number of entities
 18            public const int entities = 200;
 19
 20            // Number of faces (per parcel)
 21            public const int triangles = 10000;
 22            public const int bodies = 300;
 23            public const int textures = 10;
 24            public const int materials = 20;
 25            public const int meshes = 200;
 26
 27            public const float height = 20;
 28            public const float visibleRadius = 10;
 29        }
 30
 31        protected class EntityMetrics
 32        {
 55033            public Dictionary<Material, int> materials = new Dictionary<Material, int>();
 34            public int bodies = 0;
 35            public int triangles = 0;
 36            public int textures = 0;
 37
 38            public override string ToString()
 39            {
 55040                return $"materials: {materials.Count}, bodies: {bodies}, triangles: {triangles}, textures: {textures}";
 41            }
 42        }
 43
 144        private static bool VERBOSE = false;
 145        private static ILogger logger = new Logger(Debug.unityLogger.logHandler) { filterLogType = VERBOSE ? LogType.Log
 46
 142047        public IParcelScene scene { get; private set; }
 48
 71049        public HashSet<string> excludedEntities = new HashSet<string>();
 50
 51        SceneMetricsModel cachedModel = null;
 52
 53        private SceneMetricsModel model;
 54
 71055        private Dictionary<Material, int> uniqueMaterialsRefCount = new Dictionary<Material, int>();
 71056        private Dictionary<Mesh, int> uniqueMeshesRefCount = new Dictionary<Mesh, int>();
 71057        private Dictionary<IDCLEntity, EntityMetrics> entitiesMetrics = new Dictionary<IDCLEntity, EntityMetrics>();
 58
 59        private WorldSceneObjectsTrackingHelper sceneObjectsTrackingHelper;
 60        public event Action<ISceneMetricsCounter> OnMetricsUpdated;
 61
 062        public bool isDirty { get; private set; }
 63
 3564        public SceneMetricsModel GetModel() { return model.Clone(); }
 65
 71066        public SceneMetricsCounter(IParcelScene sceneOwner)
 67        {
 71068            Assert.IsTrue( !string.IsNullOrEmpty(sceneOwner.sceneData.id), "Scene must have an ID!" );
 71069            this.scene = sceneOwner;
 70
 71071            model = new SceneMetricsModel();
 71072            sceneObjectsTrackingHelper = new WorldSceneObjectsTrackingHelper(DataStore.i, scene.sceneData.id);
 73
 71074            logger.Log("Start ScenePerformanceLimitsController...");
 71075        }
 76
 77        public void Enable()
 78        {
 71079            if (scene == null)
 080                return;
 81
 71082            sceneObjectsTrackingHelper.OnWillAddRendereable -= OnWillAddRendereable;
 71083            sceneObjectsTrackingHelper.OnWillRemoveRendereable -= OnWillRemoveRendereable;
 84
 71085            sceneObjectsTrackingHelper.OnWillAddRendereable += OnWillAddRendereable;
 71086            sceneObjectsTrackingHelper.OnWillRemoveRendereable += OnWillRemoveRendereable;
 87
 71088            scene.OnEntityAdded -= OnEntityAdded;
 71089            scene.OnEntityRemoved -= OnEntityRemoved;
 71090            scene.OnEntityAdded += OnEntityAdded;
 71091            scene.OnEntityRemoved += OnEntityRemoved;
 71092        }
 93
 94        public void Disable()
 95        {
 71096            if (scene == null)
 097                return;
 98
 71099            sceneObjectsTrackingHelper.OnWillAddRendereable -= OnWillAddRendereable;
 710100            sceneObjectsTrackingHelper.OnWillRemoveRendereable -= OnWillRemoveRendereable;
 101
 710102            scene.OnEntityAdded -= OnEntityAdded;
 710103            scene.OnEntityRemoved -= OnEntityRemoved;
 710104        }
 105
 106        private void OnWillAddRendereable(Rendereable rendereable)
 107        {
 278108            if (excludedEntities.Contains(rendereable.ownerId))
 0109                return;
 110
 278111            int trianglesToAdd = rendereable.totalTriangleCount / 3;
 278112            model.triangles += trianglesToAdd;
 113
 1342114            for ( int i = 0; i < rendereable.meshes.Count; i++)
 115            {
 393116                Mesh mesh = rendereable.meshes[i];
 117
 393118                if (uniqueMeshesRefCount.ContainsKey(mesh))
 119                {
 57120                    uniqueMeshesRefCount[mesh]++;
 57121                }
 122                else
 123                {
 336124                    uniqueMeshesRefCount.Add(mesh, 1);
 336125                    model.meshes++;
 126                }
 127            }
 128
 278129            OnMetricsUpdated?.Invoke(this);
 0130        }
 131
 132        private void OnWillRemoveRendereable(Rendereable rendereable)
 133        {
 101134            if (excludedEntities.Contains(rendereable.ownerId))
 0135                return;
 136
 101137            int trianglesToRemove = rendereable.totalTriangleCount / 3;
 101138            model.triangles -= trianglesToRemove;
 139
 428140            for ( int i = 0; i < rendereable.meshes.Count; i++)
 141            {
 113142                Mesh mesh = rendereable.meshes[i];
 143
 113144                if (uniqueMeshesRefCount.ContainsKey(mesh))
 145                {
 113146                    uniqueMeshesRefCount[mesh]--;
 147
 113148                    if (uniqueMeshesRefCount[mesh] == 0)
 149                    {
 86150                        model.meshes--;
 86151                        uniqueMeshesRefCount.Remove(mesh);
 152                    }
 86153                }
 154                else
 155                {
 0156                    logger.LogWarning("OnWillRemoveRendereable", "Trying to remove mesh that never was added");
 157                }
 158            }
 159
 101160            OnMetricsUpdated?.Invoke(this);
 0161        }
 162
 163        private void OnEntityAdded(IDCLEntity e)
 164        {
 546165            e.OnMeshesInfoUpdated += OnEntityMeshInfoUpdated;
 546166            e.OnMeshesInfoCleaned += OnEntityMeshInfoCleaned;
 546167            model.entities++;
 546168            isDirty = true;
 546169            OnMetricsUpdated?.Invoke(this);
 0170        }
 171
 172        private void OnEntityRemoved(IDCLEntity e)
 173        {
 174            // TODO(Brian): When all the code is migrated to the Rendereable counting, remove this call
 38175            SubstractMetrics(e);
 176
 38177            e.OnMeshesInfoUpdated -= OnEntityMeshInfoUpdated;
 38178            e.OnMeshesInfoCleaned -= OnEntityMeshInfoCleaned;
 38179            model.entities--;
 38180            isDirty = true;
 38181            OnMetricsUpdated?.Invoke(this);
 0182        }
 183
 184        // TODO(Brian): When all the code is migrated to the Rendereable counting, remove this method
 185        private void OnEntityMeshInfoUpdated(IDCLEntity entity)
 186        {
 552187            AddOrReplaceMetrics(entity);
 552188            OnMetricsUpdated?.Invoke(this);
 0189        }
 190
 191        // TODO(Brian): When all the code is migrated to the Rendereable counting, remove this method
 192        private void OnEntityMeshInfoCleaned(IDCLEntity entity)
 193        {
 213194            SubstractMetrics(entity);
 213195            OnMetricsUpdated?.Invoke(this);
 0196        }
 197
 198        // TODO(Brian): When all the code is migrated to the Rendereable counting, remove this method
 199        private void AddOrReplaceMetrics(IDCLEntity entity)
 200        {
 552201            if (entitiesMetrics.ContainsKey(entity))
 202            {
 308203                SubstractMetrics(entity);
 204            }
 205
 552206            AddMetrics(entity);
 552207            OnMetricsUpdated?.Invoke(this);
 0208        }
 209
 210        // TODO(Brian): Move all this counting on OnWillRemoveRendereable instead
 211        [System.Obsolete]
 212        protected void SubstractMetrics(IDCLEntity entity)
 213        {
 559214            if (excludedEntities.Contains(entity.entityId))
 0215                return;
 216
 559217            if (!entitiesMetrics.ContainsKey(entity))
 22218                return;
 219
 537220            EntityMetrics entityMetrics = entitiesMetrics[entity];
 221
 537222            RemoveEntitiesMaterial(entityMetrics);
 223
 537224            model.materials = uniqueMaterialsRefCount.Count;
 537225            model.bodies -= entityMetrics.bodies;
 226
 537227            if (entitiesMetrics.ContainsKey(entity))
 228            {
 537229                entitiesMetrics.Remove(entity);
 230            }
 231
 537232            isDirty = true;
 537233        }
 234
 235        // TODO(Brian): Move all this counting on OnWillAddRendereable instead
 236        [System.Obsolete]
 237        protected void AddMetrics(IDCLEntity entity)
 238        {
 552239            if (excludedEntities.Contains(entity.entityId))
 0240                return;
 241
 552242            if (entity.meshRootGameObject == null)
 0243                return;
 244
 245            // If the mesh is being loaded we should skip the evaluation (it will be triggered again later when the load
 552246            if (entity.meshRootGameObject.GetComponent<MaterialTransitionController>())
 2247                return;
 248
 550249            EntityMetrics entityMetrics = new EntityMetrics();
 250
 550251            CalculateMaterials(entity, entityMetrics);
 252
 253            // TODO(Brian): We should move bodies and materials to DataStore_WorldObjects later
 550254            entityMetrics.bodies = entity.meshesInfo.meshFilters.Length;
 255
 550256            model.materials = uniqueMaterialsRefCount.Count;
 550257            model.bodies += entityMetrics.bodies;
 258
 550259            if (!entitiesMetrics.ContainsKey(entity))
 550260                entitiesMetrics.Add(entity, entityMetrics);
 261            else
 0262                entitiesMetrics[entity] = entityMetrics;
 263
 550264            logger.Log("SceneMetrics: entity " + entity.entityId + " metrics " + entityMetrics.ToString());
 550265            isDirty = true;
 550266        }
 267
 268        // TODO(Brian): Move all this counting on OnWillAddRendereable instead
 269        [System.Obsolete]
 270        void CalculateMaterials(IDCLEntity entity, EntityMetrics entityMetrics)
 271        {
 272            // TODO(Brian): Find a way to remove this dependency
 550273            var originalMaterials = Environment.i.world.sceneBoundsChecker.GetOriginalMaterials(entity.meshesInfo);
 274
 550275            if (originalMaterials == null)
 276            {
 20277                logger.Log($"SceneMetrics: material null of entity {entity.entityId} -- (style: {Environment.i.world.sce
 20278                return;
 279            }
 280
 530281            int originalMaterialsCount = originalMaterials.Count;
 282
 2066283            for (int i = 0; i < originalMaterialsCount; i++)
 284            {
 503285                AddMaterial(entityMetrics, originalMaterials[i]);
 503286                logger.Log($"SceneMetrics: material {originalMaterials[i].name} of entity {entity.entityId} -- (style: {
 287            }
 530288        }
 289
 290        // TODO(Brian): Put this into OnWillAddRendereable instead
 291        [System.Obsolete]
 292        void AddMaterial(EntityMetrics entityMetrics, Material material)
 293        {
 503294            if (material == null)
 0295                return;
 296
 503297            if (!uniqueMaterialsRefCount.ContainsKey(material))
 405298                uniqueMaterialsRefCount.Add(material, 1);
 299            else
 98300                uniqueMaterialsRefCount[material] += 1;
 301
 503302            if (!entityMetrics.materials.ContainsKey(material))
 465303                entityMetrics.materials.Add(material, 1);
 304            else
 38305                entityMetrics.materials[material] += 1;
 38306        }
 307
 308        // TODO(Brian): Put this into OnWillRemoveRendereable instead
 309        [System.Obsolete]
 310        void RemoveEntitiesMaterial(EntityMetrics entityMetrics)
 311        {
 537312            var entityMaterials = entityMetrics.materials;
 537313            using (var iterator = entityMaterials.GetEnumerator())
 314            {
 994315                while (iterator.MoveNext())
 316                {
 457317                    if (uniqueMaterialsRefCount.ContainsKey(iterator.Current.Key))
 318                    {
 457319                        uniqueMaterialsRefCount[iterator.Current.Key] -= iterator.Current.Value;
 320
 457321                        if (uniqueMaterialsRefCount[iterator.Current.Key] <= 0)
 397322                            uniqueMaterialsRefCount.Remove(iterator.Current.Key);
 323                    }
 324                }
 537325            }
 537326        }
 327
 328        public void Dispose()
 329        {
 710330            if (scene == null)
 0331                return;
 332
 710333            sceneObjectsTrackingHelper.Dispose();
 334
 710335            scene.OnEntityAdded -= OnEntityAdded;
 710336            scene.OnEntityRemoved -= OnEntityRemoved;
 337
 710338            logger.Log("Disposing...");
 710339        }
 340
 341
 342        public SceneMetricsModel GetLimits()
 343        {
 1577344            if (cachedModel == null)
 345            {
 673346                cachedModel = new SceneMetricsModel();
 347
 673348                int parcelCount = scene.sceneData.parcels.Length;
 673349                float log = Mathf.Log(parcelCount + 1, 2);
 673350                float lineal = parcelCount;
 351
 673352                cachedModel.triangles = (int) (lineal * LimitsConfig.triangles);
 673353                cachedModel.bodies = (int) (lineal * LimitsConfig.bodies);
 673354                cachedModel.entities = (int) (lineal * LimitsConfig.entities);
 673355                cachedModel.materials = (int) (log * LimitsConfig.materials);
 673356                cachedModel.textures = (int) (log * LimitsConfig.textures);
 673357                cachedModel.meshes = (int) (log * LimitsConfig.meshes);
 673358                cachedModel.sceneHeight = (int) (log * LimitsConfig.height);
 359            }
 360
 1577361            return cachedModel;
 362        }
 363
 364        public bool IsInsideTheLimits()
 365        {
 4366            SceneMetricsModel limits = GetLimits();
 4367            SceneMetricsModel usage = GetModel();
 368
 4369            if (usage.triangles > limits.triangles)
 0370                return false;
 371
 4372            if (usage.bodies > limits.bodies)
 0373                return false;
 374
 4375            if (usage.entities > limits.entities)
 1376                return false;
 377
 3378            if (usage.materials > limits.materials)
 0379                return false;
 380
 3381            if (usage.textures > limits.textures)
 0382                return false;
 383
 3384            if (usage.meshes > limits.meshes)
 0385                return false;
 386
 3387            return true;
 388        }
 389
 390        public void AddExcludedEntity(string entityId)
 391        {
 3392            if ( !excludedEntities.Contains(entityId))
 3393                excludedEntities.Add(entityId);
 3394        }
 395
 396        public void RemoveExcludedEntity(string entityId)
 397        {
 3398            if ( excludedEntities.Contains(entityId))
 3399                excludedEntities.Remove(entityId);
 3400        }
 401
 402        public void SendEvent()
 403        {
 1031404            if (!isDirty)
 888405                return;
 406
 143407            isDirty = false;
 408
 143409            Interface.WebInterface.ReportOnMetricsUpdate(scene.sceneData.id,
 410                model.ToMetricsModel(), GetLimits().ToMetricsModel());
 143411        }
 412    }
 413}