< Summary

Class:DCL.SceneMetricsCounter
Assembly:MainScripts
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/WorldRuntime/SceneMetricsCounter.cs
Covered lines:167
Uncovered lines:24
Coverable lines:191
Total lines:415
Line coverage:87.4% (167 of 191)
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%440100%
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        {
 55833            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            {
 55840                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
 94647        public IParcelScene scene { get; private set; }
 48
 47349        public HashSet<string> excludedEntities = new HashSet<string>();
 50
 51        SceneMetricsModel cachedModel = null;
 52
 53        private SceneMetricsModel model;
 54
 47355        private Dictionary<Material, int> uniqueMaterialsRefCount = new Dictionary<Material, int>();
 47356        private Dictionary<Mesh, int> uniqueMeshesRefCount = new Dictionary<Mesh, int>();
 47357        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
 3764        public SceneMetricsModel GetModel() { return model.Clone(); }
 65
 47366        public SceneMetricsCounter(IParcelScene sceneOwner)
 67        {
 47368            Assert.IsTrue( !string.IsNullOrEmpty(sceneOwner.sceneData.id), "Scene must have an ID!" );
 47369            this.scene = sceneOwner;
 70
 47371            model = new SceneMetricsModel();
 47372            sceneObjectsTrackingHelper = new WorldSceneObjectsTrackingHelper(DataStore.i, scene.sceneData.id);
 73
 47374            logger.Log("Start ScenePerformanceLimitsController...");
 47375        }
 76
 77        public void Enable()
 78        {
 47379            if (scene == null)
 080                return;
 81
 47382            sceneObjectsTrackingHelper.OnWillAddRendereable -= OnWillAddRendereable;
 47383            sceneObjectsTrackingHelper.OnWillRemoveRendereable -= OnWillRemoveRendereable;
 84
 47385            sceneObjectsTrackingHelper.OnWillAddRendereable += OnWillAddRendereable;
 47386            sceneObjectsTrackingHelper.OnWillRemoveRendereable += OnWillRemoveRendereable;
 87
 47388            scene.OnEntityAdded -= OnEntityAdded;
 47389            scene.OnEntityRemoved -= OnEntityRemoved;
 47390            scene.OnEntityAdded += OnEntityAdded;
 47391            scene.OnEntityRemoved += OnEntityRemoved;
 47392        }
 93
 94        public void Disable()
 95        {
 47396            if (scene == null)
 097                return;
 98
 47399            sceneObjectsTrackingHelper.OnWillAddRendereable -= OnWillAddRendereable;
 473100            sceneObjectsTrackingHelper.OnWillRemoveRendereable -= OnWillRemoveRendereable;
 101
 473102            scene.OnEntityAdded -= OnEntityAdded;
 473103            scene.OnEntityRemoved -= OnEntityRemoved;
 473104        }
 105
 106        private void OnWillAddRendereable(Rendereable rendereable)
 107        {
 283108            if (excludedEntities.Contains(rendereable.ownerId))
 0109                return;
 110
 283111            int trianglesToAdd = rendereable.totalTriangleCount / 3;
 283112            model.triangles += trianglesToAdd;
 113
 1364114            for ( int i = 0; i < rendereable.meshes.Count; i++)
 115            {
 399116                Mesh mesh = rendereable.meshes[i];
 117
 399118                if (uniqueMeshesRefCount.ContainsKey(mesh))
 119                {
 58120                    uniqueMeshesRefCount[mesh]++;
 58121                }
 122                else
 123                {
 341124                    uniqueMeshesRefCount.Add(mesh, 1);
 341125                    model.meshes++;
 126                }
 127            }
 128
 283129            OnMetricsUpdated?.Invoke(this);
 0130        }
 131
 132        private void OnWillRemoveRendereable(Rendereable rendereable)
 133        {
 102134            if (excludedEntities.Contains(rendereable.ownerId))
 0135                return;
 136
 102137            int trianglesToRemove = rendereable.totalTriangleCount / 3;
 102138            model.triangles -= trianglesToRemove;
 139
 432140            for ( int i = 0; i < rendereable.meshes.Count; i++)
 141            {
 114142                Mesh mesh = rendereable.meshes[i];
 143
 114144                if (uniqueMeshesRefCount.ContainsKey(mesh))
 145                {
 114146                    uniqueMeshesRefCount[mesh]--;
 147
 114148                    if (uniqueMeshesRefCount[mesh] == 0)
 149                    {
 87150                        model.meshes--;
 87151                        uniqueMeshesRefCount.Remove(mesh);
 152                    }
 87153                }
 154                else
 155                {
 0156                    logger.LogWarning("OnWillRemoveRendereable", "Trying to remove mesh that never was added");
 157                }
 158            }
 159
 102160            OnMetricsUpdated?.Invoke(this);
 0161        }
 162
 163        private void OnEntityAdded(IDCLEntity e)
 164        {
 540165            e.OnMeshesInfoUpdated += OnEntityMeshInfoUpdated;
 540166            e.OnMeshesInfoCleaned += OnEntityMeshInfoCleaned;
 540167            model.entities++;
 540168            isDirty = true;
 540169            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        {
 560187            AddOrReplaceMetrics(entity);
 560188            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        {
 177194            SubstractMetrics(entity);
 177195            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        {
 560201            if (entitiesMetrics.ContainsKey(entity))
 202            {
 313203                SubstractMetrics(entity);
 204            }
 205
 560206            AddMetrics(entity);
 560207            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        {
 528214            if (excludedEntities.Contains(entity.entityId))
 0215                return;
 216
 528217            if (!entitiesMetrics.ContainsKey(entity))
 22218                return;
 219
 506220            EntityMetrics entityMetrics = entitiesMetrics[entity];
 221
 506222            RemoveEntitiesMaterial(entityMetrics);
 223
 506224            model.materials = uniqueMaterialsRefCount.Count;
 506225            model.bodies -= entityMetrics.bodies;
 226
 506227            if (entitiesMetrics.ContainsKey(entity))
 228            {
 506229                entitiesMetrics.Remove(entity);
 230            }
 231
 506232            isDirty = true;
 506233        }
 234
 235        // TODO(Brian): Move all this counting on OnWillAddRendereable instead
 236        [System.Obsolete]
 237        protected void AddMetrics(IDCLEntity entity)
 238        {
 560239            if (excludedEntities.Contains(entity.entityId))
 0240                return;
 241
 560242            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
 560246            if (entity.meshRootGameObject.GetComponent<MaterialTransitionController>())
 2247                return;
 248
 558249            EntityMetrics entityMetrics = new EntityMetrics();
 250
 558251            CalculateMaterials(entity, entityMetrics);
 252
 253            // TODO(Brian): We should move bodies and materials to DataStore_WorldObjects later
 558254            entityMetrics.bodies = entity.meshesInfo.meshFilters.Length;
 255
 558256            model.materials = uniqueMaterialsRefCount.Count;
 558257            model.bodies += entityMetrics.bodies;
 258
 558259            if (!entitiesMetrics.ContainsKey(entity))
 558260                entitiesMetrics.Add(entity, entityMetrics);
 261            else
 0262                entitiesMetrics[entity] = entityMetrics;
 263
 558264            logger.Log("SceneMetrics: entity " + entity.entityId + " metrics " + entityMetrics.ToString());
 558265            isDirty = true;
 558266        }
 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
 558273            var originalMaterials = Environment.i.world.sceneBoundsChecker.GetOriginalMaterials(entity.meshesInfo);
 274
 558275            if (originalMaterials == null)
 276            {
 103277                logger.Log($"SceneMetrics: material null of entity {entity.entityId} -- (style: {Environment.i.world.sce
 103278                return;
 279            }
 280
 455281            int originalMaterialsCount = originalMaterials.Count;
 282
 1752283            for (int i = 0; i < originalMaterialsCount; i++)
 284            {
 421285                if (originalMaterials[i] == null)
 286                    continue;
 287
 421288                AddMaterial(entityMetrics, originalMaterials[i]);
 421289                logger.Log($"SceneMetrics: material {originalMaterials[i].name} of entity {entity.entityId} -- (style: {
 290            }
 455291        }
 292
 293        // TODO(Brian): Put this into OnWillAddRendereable instead
 294        [System.Obsolete]
 295        void AddMaterial(EntityMetrics entityMetrics, Material material)
 296        {
 421297            if (material == null)
 0298                return;
 299
 421300            if (!uniqueMaterialsRefCount.ContainsKey(material))
 361301                uniqueMaterialsRefCount.Add(material, 1);
 302            else
 60303                uniqueMaterialsRefCount[material] += 1;
 304
 421305            if (!entityMetrics.materials.ContainsKey(material))
 417306                entityMetrics.materials.Add(material, 1);
 307            else
 4308                entityMetrics.materials[material] += 1;
 4309        }
 310
 311        // TODO(Brian): Put this into OnWillRemoveRendereable instead
 312        [System.Obsolete]
 313        void RemoveEntitiesMaterial(EntityMetrics entityMetrics)
 314        {
 506315            var entityMaterials = entityMetrics.materials;
 506316            using (var iterator = entityMaterials.GetEnumerator())
 317            {
 914318                while (iterator.MoveNext())
 319                {
 408320                    if (uniqueMaterialsRefCount.ContainsKey(iterator.Current.Key))
 321                    {
 408322                        uniqueMaterialsRefCount[iterator.Current.Key] -= iterator.Current.Value;
 323
 408324                        if (uniqueMaterialsRefCount[iterator.Current.Key] <= 0)
 352325                            uniqueMaterialsRefCount.Remove(iterator.Current.Key);
 326                    }
 327                }
 506328            }
 506329        }
 330
 331        public void Dispose()
 332        {
 473333            if (scene == null)
 0334                return;
 335
 473336            sceneObjectsTrackingHelper.Dispose();
 337
 473338            scene.OnEntityAdded -= OnEntityAdded;
 473339            scene.OnEntityRemoved -= OnEntityRemoved;
 340
 473341            logger.Log("Disposing...");
 473342        }
 343
 344        public SceneMetricsModel GetLimits()
 345        {
 722346            if (cachedModel == null)
 347            {
 238348                cachedModel = new SceneMetricsModel();
 349
 238350                int parcelCount = scene.sceneData.parcels.Length;
 238351                float log = Mathf.Log(parcelCount + 1, 2);
 238352                float lineal = parcelCount;
 353
 238354                cachedModel.triangles = (int) (lineal * LimitsConfig.triangles);
 238355                cachedModel.bodies = (int) (lineal * LimitsConfig.bodies);
 238356                cachedModel.entities = (int) (lineal * LimitsConfig.entities);
 238357                cachedModel.materials = (int) (log * LimitsConfig.materials);
 238358                cachedModel.textures = (int) (log * LimitsConfig.textures);
 238359                cachedModel.meshes = (int) (log * LimitsConfig.meshes);
 238360                cachedModel.sceneHeight = (int) (log * LimitsConfig.height);
 361            }
 362
 722363            return cachedModel;
 364        }
 365
 366        public bool IsInsideTheLimits()
 367        {
 5368            SceneMetricsModel limits = GetLimits();
 5369            SceneMetricsModel usage = GetModel();
 370
 5371            if (usage.triangles > limits.triangles)
 0372                return false;
 373
 5374            if (usage.bodies > limits.bodies)
 0375                return false;
 376
 5377            if (usage.entities > limits.entities)
 1378                return false;
 379
 4380            if (usage.materials > limits.materials)
 0381                return false;
 382
 4383            if (usage.textures > limits.textures)
 0384                return false;
 385
 4386            if (usage.meshes > limits.meshes)
 0387                return false;
 388
 4389            return true;
 390        }
 391
 392        public void AddExcludedEntity(string entityId)
 393        {
 3394            if ( !excludedEntities.Contains(entityId))
 3395                excludedEntities.Add(entityId);
 3396        }
 397
 398        public void RemoveExcludedEntity(string entityId)
 399        {
 3400            if ( excludedEntities.Contains(entityId))
 3401                excludedEntities.Remove(entityId);
 3402        }
 403
 404        public void SendEvent()
 405        {
 981406            if (!isDirty)
 831407                return;
 408
 150409            isDirty = false;
 410
 150411            Interface.WebInterface.ReportOnMetricsUpdate(scene.sceneData.id,
 412                model.ToMetricsModel(), GetLimits().ToMetricsModel());
 150413        }
 414    }
 415}