< Summary

Class:CullingControllerTests.CullingControllerShould
Assembly:CullingControllerTests
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Rendering/Culling/Tests/CullingControllerShould.cs
Covered lines:175
Uncovered lines:0
Coverable lines:175
Total lines:346
Line coverage:100% (175 of 175)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
SetUp()0%110100%
CreateMockedCulledController(...)0%550100%
TearDown()0%110100%
ReturnCopyWhenGetSettingsIsCalled()0%110100%
SetSettingsProperly()0%110100%
EvaluateRendererVisibility()0%110100%
EvaluateShadowVisibility()0%110100%
EvaluateSkinnedMeshesOffscreenUpdate()0%110100%
ResetObjects()0%110100%
ProcessAnimationCulling()0%11110100%
ReactToRendererState()0%440100%
ProcessProfile()0%10100100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Rendering/Culling/Tests/CullingControllerShould.cs

#LineLine coverage
 1using System.Collections;
 2using System.Collections.Generic;
 3using DCL.Rendering;
 4using NSubstitute;
 5using NSubstitute.Exceptions;
 6using NUnit.Framework;
 7using UnityEditor;
 8using UnityEngine;
 9using UnityEngine.Rendering;
 10using UnityEngine.Rendering.Universal;
 11using UnityEngine.TestTools;
 12using Assert = UnityEngine.Assertions.Assert;
 13
 14namespace CullingControllerTests
 15{
 16    public class CullingControllerShould
 17    {
 18        public CullingController cullingController;
 19
 20        [SetUp]
 1821        public void SetUp() { cullingController = CreateMockedCulledController(null, null, null); }
 22
 23        private static CullingController CreateMockedCulledController(UniversalRenderPipelineAsset urpAsset, CullingCont
 24        {
 1025            var result = Substitute.ForPartsOf<CullingController>(
 26                urpAsset == null ? GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset : urpAsset,
 27                settings ?? new CullingControllerSettings(),
 28                cullingObjectsTracker ?? new CullingObjectsTracker());
 29
 1030            result.SetSettings(new CullingControllerSettings() { maxTimeBudget = float.MaxValue });
 31
 1032            return result;
 33        }
 34
 35        [TearDown]
 1836        public void TearDown() { cullingController.Dispose(); }
 37
 38        [Test]
 39        public void ReturnCopyWhenGetSettingsIsCalled()
 40        {
 41            // Ensure settings never return null
 142            var settings = cullingController.GetSettingsCopy();
 143            Assert.IsNotNull(settings, "Settings should never be null!");
 44
 145            var prevAnimationCulling = settings.enableAnimationCulling;
 46
 47            // This is needed because SetSettings sets dirty flag and other stuff. We don't want direct access.
 48            // Settings is not a property because to make the SetSetting performance hit more obvious.
 149            settings.enableAnimationCulling = !settings.enableAnimationCulling;
 150            settings = cullingController.GetSettingsCopy();
 151            Assert.IsTrue(settings.enableAnimationCulling == prevAnimationCulling, "GetSettings should return a copy!");
 152        }
 53
 54        [Test]
 55        public void SetSettingsProperly()
 56        {
 57            // Ensure settings never return null
 158            var settings = cullingController.GetSettingsCopy();
 159            Assert.IsNotNull(settings, "Settings should never be null!");
 60
 161            var prevAnimationCulling = settings.enableAnimationCulling;
 62
 63            // Ensure SetSettings works as intended.
 164            settings.enableAnimationCulling = !settings.enableAnimationCulling;
 165            cullingController.SetSettings(settings);
 166            settings = cullingController.GetSettingsCopy();
 167            Assert.IsTrue(settings.enableAnimationCulling != prevAnimationCulling, "SetSettings should assign the settin
 168        }
 69
 70        [Test]
 71        public void EvaluateRendererVisibility()
 72        {
 73            //Arrange
 174            var profile = new CullingControllerProfile();
 175            profile.emissiveSizeThreshold = 10;
 176            profile.opaqueSizeThreshold = 20;
 177            profile.visibleDistanceThreshold = 5;
 78
 79            // Act
 80            // Truth tests
 181            var farAndBigTest = CullingControllerUtils.TestRendererVisibleRule(profile, 30, 20, false, true, true);
 182            var smallAndNearTest = CullingControllerUtils.TestRendererVisibleRule(profile, 5, 2, false, true, true);
 183            var cameraInBoundsTest = CullingControllerUtils.TestRendererVisibleRule(profile, 1, 100, true, true, true);
 184            var emissiveTest = CullingControllerUtils.TestRendererVisibleRule(profile, 15, 20, false, false, true);
 85
 86            // Falsehood tests
 187            var farAndSmallTest = CullingControllerUtils.TestRendererVisibleRule(profile, 5, 20, false, true, true);
 188            var emissiveAndFarTest = CullingControllerUtils.TestRendererVisibleRule(profile, 5, 20, false, false, true);
 189            var farAndTransparentTest = CullingControllerUtils.TestRendererVisibleRule(profile, 1, 50, false, false, fal
 90
 91            // Assert
 192            Assert.IsTrue(farAndBigTest);
 193            Assert.IsTrue(smallAndNearTest);
 194            Assert.IsTrue(cameraInBoundsTest);
 195            Assert.IsTrue(emissiveTest);
 96
 197            Assert.IsFalse(farAndSmallTest);
 198            Assert.IsFalse(emissiveAndFarTest);
 199            Assert.IsFalse(farAndTransparentTest);
 1100        }
 101
 102        [Test]
 103        public void EvaluateShadowVisibility()
 104        {
 105            // Arrange
 1106            var profile = new CullingControllerProfile();
 1107            profile.shadowMapProjectionSizeThreshold = 6;
 1108            profile.shadowRendererSizeThreshold = 20;
 1109            profile.shadowDistanceThreshold = 15;
 110
 111            // Act
 1112            var nearTest = CullingControllerUtils.TestRendererShadowRule(profile, 1, 5, 10);
 1113            var nearButSmallTexel = CullingControllerUtils.TestRendererShadowRule(profile, 1, 5, 1);
 1114            var farAndBigEnough = CullingControllerUtils.TestRendererShadowRule(profile, 30, 30, 30);
 1115            var farAndSmall = CullingControllerUtils.TestRendererShadowRule(profile, 10, 30, 30);
 1116            var farAndSmallTexel = CullingControllerUtils.TestRendererShadowRule(profile, 10, 30, 1);
 117
 118            // Assert
 1119            Assert.IsTrue(nearTest);
 1120            Assert.IsTrue(farAndBigEnough);
 1121            Assert.IsFalse(nearButSmallTexel);
 1122            Assert.IsFalse(farAndSmall);
 1123            Assert.IsFalse(farAndSmallTexel);
 1124        }
 125
 126        [Test]
 127        public void EvaluateSkinnedMeshesOffscreenUpdate()
 128        {
 129            // Arrange
 1130            var profile = new CullingControllerProfile();
 1131            profile.shadowMapProjectionSizeThreshold = 6;
 1132            profile.shadowRendererSizeThreshold = 20;
 1133            profile.shadowDistanceThreshold = 15;
 134
 1135            var settings = new CullingControllerSettings();
 1136            settings.enableAnimationCullingDistance = 20;
 1137            settings.enableAnimationCulling = true;
 138
 139            // Act
 1140            var farTest = CullingControllerUtils.TestSkinnedRendererOffscreenRule(settings, 30);
 1141            var nearTest = CullingControllerUtils.TestSkinnedRendererOffscreenRule(settings, 10);
 142
 1143            settings.enableAnimationCulling = false;
 1144            var farTestWithCullingDisabled = CullingControllerUtils.TestSkinnedRendererOffscreenRule(settings, 30);
 145
 146            // Assert
 1147            Assert.IsTrue(nearTest);
 1148            Assert.IsFalse(farTest);
 1149            Assert.IsTrue(farTestWithCullingDisabled);
 1150        }
 151
 152        [Test]
 153        public void ResetObjects()
 154        {
 155            // Arrange
 1156            GameObject go1 = new GameObject();
 1157            GameObject go2 = new GameObject();
 158
 1159            var r = go1.AddComponent<MeshRenderer>();
 1160            var skr = go2.AddComponent<SkinnedMeshRenderer>();
 1161            var anim = go2.AddComponent<Animation>();
 162
 1163            r.forceRenderingOff = true;
 1164            skr.updateWhenOffscreen = false;
 1165            anim.cullingType = AnimationCullingType.BasedOnRenderers;
 166
 1167            var mockTracker = Substitute.For<ICullingObjectsTracker>();
 1168            cullingController = CreateMockedCulledController(null, null, mockTracker);
 169
 2170            mockTracker.GetRenderers().Returns(info => go1.GetComponentsInChildren<Renderer>());
 2171            mockTracker.GetSkinnedRenderers().Returns(info => go2.GetComponentsInChildren<SkinnedMeshRenderer>());
 2172            mockTracker.GetAnimations().Returns(info => go2.GetComponentsInChildren<Animation>());
 173
 174            // Act
 1175            cullingController.ResetObjects();
 176
 177            // Assert
 1178            Assert.IsFalse(r.forceRenderingOff);
 1179            Assert.IsTrue(skr.updateWhenOffscreen);
 1180            Assert.IsTrue(anim.cullingType == AnimationCullingType.AlwaysAnimate);
 181
 182            // Annihilate
 1183            Object.Destroy(go1);
 1184            Object.Destroy(go2);
 1185        }
 186
 187        [UnityTest]
 188        public IEnumerator ProcessAnimationCulling()
 189        {
 190            // Pre-arrange
 191            const int GO_COUNT = 3;
 1192            GameObject[] gos = new GameObject[GO_COUNT];
 1193            Animation[] anims = new Animation[GO_COUNT];
 194
 8195            for (int i = 0; i < GO_COUNT; i++)
 196            {
 3197                gos[i] = new GameObject("Test " + i);
 3198                anims[i] = gos[i].AddComponent<Animation>();
 3199                anims[i].cullingType = AnimationCullingType.AlwaysAnimate;
 200            }
 201
 1202            var settings = cullingController.GetSettingsCopy();
 203
 1204            settings.enableAnimationCulling = true;
 1205            settings.enableAnimationCullingDistance = 15;
 206
 1207            cullingController.SetSettings(settings);
 208
 1209            cullingController.objectsTracker.MarkDirty();
 1210            yield return cullingController.objectsTracker.PopulateRenderersList();
 211
 212            // Test #1
 1213            gos[0].transform.position = Vector3.zero;
 1214            gos[1].transform.position = new Vector3(0, 0, 30);
 1215            gos[2].transform.position = new Vector3(0, 0, 10);
 1216            CommonScriptableObjects.playerUnityPosition.Set(Vector3.zero);
 217
 1218            yield return cullingController.ProcessAnimations();
 219
 1220            Assert.AreEqual(AnimationCullingType.AlwaysAnimate, anims[0].cullingType);
 1221            Assert.AreEqual(AnimationCullingType.BasedOnRenderers, anims[1].cullingType);
 1222            Assert.AreEqual(AnimationCullingType.AlwaysAnimate, anims[2].cullingType);
 223
 224            // Test #2
 1225            gos[2].transform.position = new Vector3(0, 0, 30);
 1226            CommonScriptableObjects.playerUnityPosition.Set(Vector3.zero);
 227
 1228            yield return cullingController.ProcessAnimations();
 229
 1230            Assert.AreEqual(AnimationCullingType.BasedOnRenderers, anims[2].cullingType);
 231
 232            // Test #3
 1233            gos[2].transform.position = new Vector3(0, 0, 10);
 1234            CommonScriptableObjects.playerUnityPosition.Set(Vector3.zero);
 235
 1236            yield return cullingController.ProcessAnimations();
 237
 238            // Test #4
 1239            settings.enableAnimationCulling = false;
 1240            CommonScriptableObjects.playerUnityPosition.Set(Vector3.one * 10000);
 241
 8242            for (int i = 0; i < GO_COUNT; i++)
 243            {
 3244                anims[i].cullingType = AnimationCullingType.AlwaysAnimate;
 245            }
 246
 1247            cullingController.SetSettings(settings);
 248
 1249            yield return cullingController.ProcessAnimations();
 250
 8251            for (int i = 0; i < GO_COUNT; i++)
 252            {
 3253                Assert.AreEqual(AnimationCullingType.AlwaysAnimate, anims[i].cullingType);
 254            }
 255
 256            // Annihilate
 8257            for (int i = 0; i < GO_COUNT; i++)
 258            {
 3259                Object.Destroy(gos[i]);
 260            }
 1261        }
 262
 263        [UnityTest]
 264        public IEnumerator ReactToRendererState()
 265        {
 1266            cullingController.Start();
 1267            yield return null;
 1268            CommonScriptableObjects.rendererState.Set(true);
 1269            CommonScriptableObjects.rendererState.Set(false);
 1270            Assert.IsFalse(cullingController.IsRunning());
 1271            CommonScriptableObjects.rendererState.Set(true);
 1272            Assert.IsTrue(cullingController.IsRunning());
 1273            CommonScriptableObjects.rendererState.Set(false);
 1274            Assert.IsFalse(cullingController.IsRunning());
 1275            cullingController.Stop();
 1276            yield return null;
 1277            CommonScriptableObjects.rendererState.Set(true);
 1278            Assert.IsFalse(cullingController.IsRunning());
 1279        }
 280
 281        [UnityTest]
 282        public IEnumerator ProcessProfile()
 283        {
 284            // Pre-arrange
 285            const int GO_COUNT = 10;
 1286            GameObject[] gos = new GameObject[GO_COUNT];
 1287            Renderer[] rends = new Renderer[GO_COUNT];
 288
 22289            for (int i = 0; i < GO_COUNT; i++)
 290            {
 10291                gos[i] = GameObject.CreatePrimitive(PrimitiveType.Cube);
 292
 10293                if (i >= GO_COUNT >> 1)
 294                {
 5295                    Object.DestroyImmediate(gos[i].GetComponent<Renderer>());
 5296                    gos[i].AddComponent<SkinnedMeshRenderer>();
 297                }
 298
 10299                rends[i] = gos[i].GetComponent<Renderer>();
 300            }
 301
 1302            var settings = cullingController.GetSettingsCopy();
 303
 1304            settings.skinnedRendererProfile.visibleDistanceThreshold = 0;
 1305            settings.skinnedRendererProfile.emissiveSizeThreshold = 0;
 1306            settings.skinnedRendererProfile.opaqueSizeThreshold = 0;
 1307            settings.skinnedRendererProfile.shadowDistanceThreshold = 0;
 1308            settings.skinnedRendererProfile.shadowRendererSizeThreshold = 0;
 1309            settings.skinnedRendererProfile.shadowMapProjectionSizeThreshold = 0;
 310
 1311            settings.rendererProfile.visibleDistanceThreshold = 0;
 1312            settings.rendererProfile.emissiveSizeThreshold = 0;
 1313            settings.rendererProfile.opaqueSizeThreshold = 0;
 1314            settings.rendererProfile.shadowDistanceThreshold = 0;
 1315            settings.rendererProfile.shadowRendererSizeThreshold = 0;
 1316            settings.rendererProfile.shadowMapProjectionSizeThreshold = 0;
 317
 1318            settings.enableObjectCulling = true;
 319
 1320            cullingController.SetSettings(settings);
 1321            cullingController.objectsTracker.MarkDirty();
 322
 1323            yield return cullingController.objectsTracker.PopulateRenderersList();
 324
 1325            yield return cullingController.ProcessProfile(settings.rendererProfile);
 326
 12327            for (int i = 0; i < 5; i++)
 328            {
 5329                cullingController.Received().SetCullingForRenderer(rends[i], true, true);
 330            }
 331
 1332            yield return cullingController.ProcessProfile(settings.skinnedRendererProfile);
 333
 12334            for (int i = 5; i < GO_COUNT; i++)
 335            {
 5336                cullingController.Received().SetCullingForRenderer(rends[i], true, true);
 337            }
 338
 339            // Annihilate
 22340            for (int i = 0; i < GO_COUNT; i++)
 341            {
 10342                Object.Destroy(gos[i]);
 343            }
 1344        }
 345    }
 346}