Skip to content

Commit

Permalink
[Port] [2022.3] Fix perf when disabling decals
Browse files Browse the repository at this point in the history
This is a backport of https://github.cds.internal.unity3d.com/unity/unity/pull/44365 andhttps://github.cds.internal.unity3d.com/unity/unity/pull/24079
  • Loading branch information
alelievr authored and Evergreen committed Feb 20, 2024
1 parent d43f89a commit 4285e9b
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Rendering.HighDefinition;
Expand Down Expand Up @@ -341,6 +342,10 @@ void InitMaterial()

void Reset() => InitMaterial();

#if UNITY_EDITOR
static List<DecalProjector> m_DecalProjectorInstances; // List of decal projectors that have had OnEnable() called on them
[NonSerialized] int m_EditorInstanceIndex = -1;
#endif
void OnEnable()
{
InitMaterial();
Expand All @@ -357,14 +362,32 @@ void OnEnable()
#if UNITY_EDITOR
cachedEditorLayer = gameObject.layer;
// Handle scene visibility
SceneVisibilityManager.visibilityChanged += UpdateDecalVisibility;
PrefabStage.prefabStageOpened += RegisterDecalVisibilityUpdatePrefabStage;
if (PrefabStageUtility.GetCurrentPrefabStage() != null) // In case the prefab stage is already opened when enabling the decal
RegisterDecalVisibilityUpdatePrefabStage();

// Create a list to keep track of all active decal projectors and register our event handlers that dispatch events to each one.
// We do this instead of registering event handlers individually for each instance as that generates far more garbage on the heap
if (m_DecalProjectorInstances == null)
{
m_DecalProjectorInstances = new List<DecalProjector>();
SceneVisibilityManager.visibilityChanged += UpdateDecalVisibilityDispatcher;
PrefabStage.prefabStageOpened += RegisterDecalVisibilityUpdatePrefabStage;

if (PrefabStageUtility.GetCurrentPrefabStage() != null) // In case the prefab stage is already opened when enabling the decal
RegisterDecalVisibilityUpdatePrefabStage();
}
m_EditorInstanceIndex = m_DecalProjectorInstances.Count;
m_DecalProjectorInstances.Add(this);
#endif
}

#if UNITY_EDITOR
// Handler for the SceneVisibilityManager.visibilityChanged event, calls UpdateDecalVisibility() on each active decal projector
static void UpdateDecalVisibilityDispatcher()
{
foreach (DecalProjector decalProjector in m_DecalProjectorInstances)
decalProjector.UpdateDecalVisibility();
}

// Handle the SceneVisibilityManager.visibilityChanged event for this decal projector
void UpdateDecalVisibility()
{
// This callback is called before HDRP is initialized after a domain reload
Expand All @@ -388,15 +411,27 @@ void UpdateDecalVisibility()
}
}

void RegisterDecalVisibilityUpdatePrefabStage(PrefabStage stage = null)
// Handler for the PrefabStage.prefabStageOpened event
static void RegisterDecalVisibilityUpdatePrefabStage(PrefabStage stage = null)
{
SceneView.duringSceneGui -= UpdateDecalVisibilityPrefabStage;
SceneView.duringSceneGui += UpdateDecalVisibilityPrefabStage;
SceneView.duringSceneGui -= UpdateDecalVisibilityPrefabStageDispatcher;
SceneView.duringSceneGui += UpdateDecalVisibilityPrefabStageDispatcher;
}

void UnregisterDecalVisibilityUpdatePrefabStage()
=> SceneView.duringSceneGui -= UpdateDecalVisibilityPrefabStage;
{
SceneView.duringSceneGui -= UpdateDecalVisibilityPrefabStageDispatcher;
}

// Handler for the SceneView.duringSceneGui event, calls UpdateDecalVisibilityPrefabStage() on each active decal projector
static void UpdateDecalVisibilityPrefabStageDispatcher(SceneView sv)
{
foreach (DecalProjector decalProjector in m_DecalProjectorInstances)
decalProjector.UpdateDecalVisibilityPrefabStage(sv);

}

// Handle the SceneView.duringSceneGui event for this decal projector
bool m_LastPrefabStageVisibility = true;
void UpdateDecalVisibilityPrefabStage(SceneView sv)
{
Expand Down Expand Up @@ -424,7 +459,7 @@ void UpdateDecalVisibilityPrefabStage(SceneView sv)
DecalSystem.instance.RemoveDecal(m_Handle);
m_LastPrefabStageVisibility = showDecal;
}
#endif
#endif // UNITY_EDITOR

void OnDisable()
{
Expand All @@ -434,9 +469,21 @@ void OnDisable()
m_Handle = null;
}
#if UNITY_EDITOR
SceneVisibilityManager.visibilityChanged -= UpdateDecalVisibility;
UnregisterDecalVisibilityUpdatePrefabStage();
PrefabStage.prefabStageOpened -= RegisterDecalVisibilityUpdatePrefabStage;
// Remove this instance from the list tracking active decal projectors.
// We do this by swapping the last element of the array into the slot we are deleting rather than shuffling the entire contents above the slot down as it's faster and the actual ordering is not significant
var movedInstance = m_DecalProjectorInstances[m_EditorInstanceIndex] = m_DecalProjectorInstances[m_DecalProjectorInstances.Count - 1];
movedInstance.m_EditorInstanceIndex = m_EditorInstanceIndex;
m_EditorInstanceIndex = -1;
m_DecalProjectorInstances.RemoveAt(m_DecalProjectorInstances.Count - 1);

// If the list of active instances is now empty delete it and un-register our event handlers
if (m_DecalProjectorInstances.Count == 0)
{
SceneVisibilityManager.visibilityChanged -= UpdateDecalVisibilityDispatcher;
PrefabStage.prefabStageOpened -= RegisterDecalVisibilityUpdatePrefabStage;
UnregisterDecalVisibilityUpdatePrefabStage();
m_DecalProjectorInstances = null;
}
#endif
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ public void ResolveUpdateJob()
m_BoundingSpheres.CopyTo(m_CachedBoundingSpheres);
}

private void ResizeJobArrays(int newCapacity)
private void GrowJobArrays(int growByAmount)
{
m_CachedTransforms.ResizeArray(newCapacity);
int newCapacity = m_DecalsCount + growByAmount;

m_CachedTransforms.capacity = newCapacity;

m_Positions.ResizeArray(newCapacity);
m_Rotations.ResizeArray(newCapacity);
m_Scales.ResizeArray(newCapacity);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ public Camera CurrentCamera
}

private const int kDecalBlockSize = 128;
private const int kDecalBlockGrowthPercentage = 20;
private const int kDecalMaxBlockSize = 2048;

// to work on Vulkan Mobile?
// Core\CoreRP\ShaderLibrary\UnityInstancing.hlsl
Expand Down Expand Up @@ -565,11 +567,12 @@ public DecalHandle AddDecal(int materialID, DecalProjector decalProjector)
// increase array size if no space left
if (m_DecalsCount == m_Handles.Length)
{
int newCapacity = m_DecalsCount + kDecalBlockSize;
int growByAmount = Math.Min(Math.Max(m_DecalsCount * kDecalBlockGrowthPercentage / 100, kDecalBlockSize), kDecalMaxBlockSize);
int newCapacity = m_DecalsCount + growByAmount;

m_ResultIndices = new int[newCapacity];

ResizeJobArrays(newCapacity);
GrowJobArrays(growByAmount);

ArrayExtensions.ResizeArray(ref m_Handles, newCapacity);
ArrayExtensions.ResizeArray(ref m_CachedDrawDistances, newCapacity);
Expand Down Expand Up @@ -977,12 +980,12 @@ void SetupMipStreamingSettings(Material material, bool allMips)
public DecalHandle AddDecal(DecalProjector decalProjector)
{
var material = decalProjector.material;
SetupMipStreamingSettings(material, true);

DecalSet decalSet = null;
int key = material != null ? material.GetInstanceID() : kNullMaterialIndex;
if (!m_DecalSets.TryGetValue(key, out decalSet))
{
SetupMipStreamingSettings(material, true);
decalSet = new DecalSet(material);
m_DecalSets.Add(key, decalSet);
}
Expand Down Expand Up @@ -1162,10 +1165,10 @@ public void UpdateTextureAtlas(CommandBuffer cmd)
AddTexture(cmd, textureScaleBias);
}

#if UNITY_EDITOR || DEVELOPMENT_BUILD
if (!m_AllocationSuccess && m_PrevAllocationSuccess) // still failed to allocate, decal atlas size needs to increase, debounce so that we don't spam the console with warnings
{
Debug.LogWarning(s_AtlasSizeWarningMessage);
}
#endif
}
m_PrevAllocationSuccess = m_AllocationSuccess;
// now that textures have been stored in the atlas we can update their location info in decal data
Expand Down

0 comments on commit 4285e9b

Please sign in to comment.