< 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        {
 59933            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            {
 59940                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
 97847        public IParcelScene scene { get; private set; }
 48
 48949        public HashSet<string> excludedEntities = new HashSet<string>();
 50
 51        SceneMetricsModel cachedModel = null;
 52
 53        private SceneMetricsModel model;
 54
 48955        private Dictionary<Material, int> uniqueMaterialsRefCount = new Dictionary<Material, int>();
 48956        private Dictionary<Mesh, int> uniqueMeshesRefCount = new Dictionary<Mesh, int>();
 48957        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
 48966        public SceneMetricsCounter(IParcelScene sceneOwner)
 67        {
 48968            Assert.IsTrue( !string.IsNullOrEmpty(sceneOwner.sceneData.id), "Scene must have an ID!" );
 48969            this.scene = sceneOwner;
 70
 48971            model = new SceneMetricsModel();
 48972            sceneObjectsTrackingHelper = new WorldSceneObjectsTrackingHelper(DataStore.i, scene.sceneData.id);
 73
 48974            logger.Log("Start ScenePerformanceLimitsController...");
 48975        }
 76
 77        public void Enable()
 78        {
 48979            if (scene == null)
 080                return;
 81
 48982            sceneObjectsTrackingHelper.OnWillAddRendereable -= OnWillAddRendereable;
 48983            sceneObjectsTrackingHelper.OnWillRemoveRendereable -= OnWillRemoveRendereable;
 84
 48985            sceneObjectsTrackingHelper.OnWillAddRendereable += OnWillAddRendereable;
 48986            sceneObjectsTrackingHelper.OnWillRemoveRendereable += OnWillRemoveRendereable;
 87
 48988            scene.OnEntityAdded -= OnEntityAdded;
 48989            scene.OnEntityRemoved -= OnEntityRemoved;
 48990            scene.OnEntityAdded += OnEntityAdded;
 48991            scene.OnEntityRemoved += OnEntityRemoved;
 48992        }
 93
 94        public void Disable()
 95        {
 48996            if (scene == null)
 097                return;
 98
 48999            sceneObjectsTrackingHelper.OnWillAddRendereable -= OnWillAddRendereable;
 489100            sceneObjectsTrackingHelper.OnWillRemoveRendereable -= OnWillRemoveRendereable;
 101
 489102            scene.OnEntityAdded -= OnEntityAdded;
 489103            scene.OnEntityRemoved -= OnEntityRemoved;
 489104        }
 105
 106        private void OnWillAddRendereable(Rendereable rendereable)
 107        {
 304108            if (excludedEntities.Contains(rendereable.ownerId))
 0109                return;
 110
 304111            int trianglesToAdd = rendereable.totalTriangleCount / 3;
 304112            model.triangles += trianglesToAdd;
 113
 1476114            for ( int i = 0; i < rendereable.meshes.Count; i++)
 115            {
 434116                Mesh mesh = rendereable.meshes[i];
 117
 434118                if (uniqueMeshesRefCount.ContainsKey(mesh))
 119                {
 61120                    uniqueMeshesRefCount[mesh]++;
 61121                }
 122                else
 123                {
 373124                    uniqueMeshesRefCount.Add(mesh, 1);
 373125                    model.meshes++;
 126                }
 127            }
 128
 304129            OnMetricsUpdated?.Invoke(this);
 0130        }
 131
 132        private void OnWillRemoveRendereable(Rendereable rendereable)
 133        {
 106134            if (excludedEntities.Contains(rendereable.ownerId))
 0135                return;
 136
 106137            int trianglesToRemove = rendereable.totalTriangleCount / 3;
 106138            model.triangles -= trianglesToRemove;
 139
 452140            for ( int i = 0; i < rendereable.meshes.Count; i++)
 141            {
 120142                Mesh mesh = rendereable.meshes[i];
 143
 120144                if (uniqueMeshesRefCount.ContainsKey(mesh))
 145                {
 120146                    uniqueMeshesRefCount[mesh]--;
 147
 120148                    if (uniqueMeshesRefCount[mesh] == 0)
 149                    {
 92150                        model.meshes--;
 92151                        uniqueMeshesRefCount.Remove(mesh);
 152                    }
 92153                }
 154                else
 155                {
 0156                    logger.LogWarning("OnWillRemoveRendereable", "Trying to remove mesh that never was added");
 157                }
 158            }
 159
 106160            OnMetricsUpdated?.Invoke(this);
 0161        }
 162
 163        private void OnEntityAdded(IDCLEntity e)
 164        {
 558165            e.OnMeshesInfoUpdated += OnEntityMeshInfoUpdated;
 558166            e.OnMeshesInfoCleaned += OnEntityMeshInfoCleaned;
 558167            model.entities++;
 558168            isDirty = true;
 558169            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        {
 601187            AddOrReplaceMetrics(entity);
 601188            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        {
 178194            SubstractMetrics(entity);
 178195            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        {
 601201            if (entitiesMetrics.ContainsKey(entity))
 202            {
 336203                SubstractMetrics(entity);
 204            }
 205
 601206            AddMetrics(entity);
 601207            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        {
 552214            if (excludedEntities.Contains(entity.entityId))
 0215                return;
 216
 552217            if (!entitiesMetrics.ContainsKey(entity))
 22218                return;
 219
 530220            EntityMetrics entityMetrics = entitiesMetrics[entity];
 221
 530222            RemoveEntitiesMaterial(entityMetrics);
 223
 530224            model.materials = uniqueMaterialsRefCount.Count;
 530225            model.bodies -= entityMetrics.bodies;
 226
 530227            if (entitiesMetrics.ContainsKey(entity))
 228            {
 530229                entitiesMetrics.Remove(entity);
 230            }
 231
 530232            isDirty = true;
 530233        }
 234
 235        // TODO(Brian): Move all this counting on OnWillAddRendereable instead
 236        [System.Obsolete]
 237        protected void AddMetrics(IDCLEntity entity)
 238        {
 601239            if (excludedEntities.Contains(entity.entityId))
 0240                return;
 241
 601242            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
 601246            if (entity.meshRootGameObject.GetComponent<MaterialTransitionController>())
 2247                return;
 248
 599249            EntityMetrics entityMetrics = new EntityMetrics();
 250
 599251            CalculateMaterials(entity, entityMetrics);
 252
 253            // TODO(Brian): We should move bodies and materials to DataStore_WorldObjects later
 599254            entityMetrics.bodies = entity.meshesInfo.meshFilters.Length;
 255
 599256            model.materials = uniqueMaterialsRefCount.Count;
 599257            model.bodies += entityMetrics.bodies;
 258
 599259            if (!entitiesMetrics.ContainsKey(entity))
 599260                entitiesMetrics.Add(entity, entityMetrics);
 261            else
 0262                entitiesMetrics[entity] = entityMetrics;
 263
 599264            logger.Log("SceneMetrics: entity " + entity.entityId + " metrics " + entityMetrics.ToString());
 599265            isDirty = true;
 599266        }
 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
 599273            var originalMaterials = Environment.i.world.sceneBoundsChecker.GetOriginalMaterials(entity.meshesInfo);
 274
 599275            if (originalMaterials == null)
 276            {
 144277                logger.Log($"SceneMetrics: material null of entity {entity.entityId} -- (style: {Environment.i.world.sce
 144278                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        {
 530315            var entityMaterials = entityMetrics.materials;
 530316            using (var iterator = entityMaterials.GetEnumerator())
 317            {
 938318                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                }
 530328            }
 530329        }
 330
 331        public void Dispose()
 332        {
 489333            if (scene == null)
 0334                return;
 335
 489336            sceneObjectsTrackingHelper.Dispose();
 337
 489338            scene.OnEntityAdded -= OnEntityAdded;
 489339            scene.OnEntityRemoved -= OnEntityRemoved;
 340
 489341            logger.Log("Disposing...");
 489342        }
 343
 344        public SceneMetricsModel GetLimits()
 345        {
 750346            if (cachedModel == null)
 347            {
 251348                cachedModel = new SceneMetricsModel();
 349
 251350                int parcelCount = scene.sceneData.parcels.Length;
 251351                float log = Mathf.Log(parcelCount + 1, 2);
 251352                float lineal = parcelCount;
 353
 251354                cachedModel.triangles = (int) (lineal * LimitsConfig.triangles);
 251355                cachedModel.bodies = (int) (lineal * LimitsConfig.bodies);
 251356                cachedModel.entities = (int) (lineal * LimitsConfig.entities);
 251357                cachedModel.materials = (int) (log * LimitsConfig.materials);
 251358                cachedModel.textures = (int) (log * LimitsConfig.textures);
 251359                cachedModel.meshes = (int) (log * LimitsConfig.meshes);
 251360                cachedModel.sceneHeight = (int) (log * LimitsConfig.height);
 361            }
 362
 750363            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        {
 1023406            if (!isDirty)
 849407                return;
 408
 174409            isDirty = false;
 410
 174411            Interface.WebInterface.ReportOnMetricsUpdate(scene.sceneData.id,
 412                model.ToMetricsModel(), GetLimits().ToMetricsModel());
 174413        }
 414    }
 415}