diff --git a/Editor/UIParticleEditor.cs b/Editor/UIParticleEditor.cs index c8a8d3d..1984b8c 100644 --- a/Editor/UIParticleEditor.cs +++ b/Editor/UIParticleEditor.cs @@ -7,7 +7,7 @@ using UnityEditorInternal; using UnityEngine; using UnityEngine.Profiling; using UnityEngine.UI; -using Coffee.UIParticleExtensions; +using Coffee.UIParticleInternal; #if UNITY_2021_2_OR_NEWER using UnityEditor.Overlays; #else diff --git a/Runtime/ModifiedMaterial.cs b/Runtime/ModifiedMaterial.cs deleted file mode 100644 index fe3d53b..0000000 --- a/Runtime/ModifiedMaterial.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; - -namespace Coffee.UIParticleExtensions -{ - internal class ModifiedMaterial - { - private static readonly List s_Entries = new List(); - - public static Material Add(Material baseMat, Texture texture, int id, int props) - { - MatEntry e; - for (var i = 0; i < s_Entries.Count; i++) - { - e = s_Entries[i]; - if (e.baseMat != baseMat || e.texture != texture || e.id != id || e.props != props) continue; - ++e.count; - return e.customMat; - } - - e = new MatEntry - { - count = 1, - baseMat = baseMat, - texture = texture, - id = id, - props = props, - customMat = new Material(baseMat) - { - name = $"{baseMat.name}_{id}", - hideFlags = HideFlags.DontSave | HideFlags.NotEditable, - mainTexture = texture ? texture : baseMat.mainTexture - } - }; - s_Entries.Add(e); - //Debug.LogFormat(">>>> ModifiedMaterial.Add -> count = count:{0}, mat:{1}, tex:{2}, id:{3}", s_Entries.Count, baseMat, texture, id); - return e.customMat; - } - - public static void Remove(Material customMat) - { - if (!customMat) return; - - for (var i = 0; i < s_Entries.Count; ++i) - { - var e = s_Entries[i]; - if (e.customMat != customMat) continue; - if (--e.count == 0) - { - //Debug.LogFormat(">>>> ModifiedMaterial.Remove -> count:{0}, mat:{1}, tex:{2}, id:{3}", s_Entries.Count - 1, e.customMat, e.texture, e.id); - Misc.DestroyImmediate(e.customMat); - e.customMat = null; - e.baseMat = null; - e.texture = null; - s_Entries.RemoveAt(i); - } - - break; - } - } - - private class MatEntry - { - public Material baseMat; - public int count; - public Material customMat; - public int id; - public int props; - public Texture texture; - } - } -} diff --git a/Runtime/UIParticle.cs b/Runtime/UIParticle.cs index 49409cd..5d98577 100644 --- a/Runtime/UIParticle.cs +++ b/Runtime/UIParticle.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -using Coffee.UIParticleExtensions; +using Coffee.UIParticleInternal; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Serialization; @@ -119,7 +119,6 @@ namespace Coffee.UIExtensions private readonly List _renderers = new List(); private Camera _bakeCamera; - private Canvas _canvas; private int _groupId; private bool _isScaleStored; private Vector3 _storedScale; diff --git a/Runtime/UIParticleAttractor.cs b/Runtime/UIParticleAttractor.cs index eeea43c..0593a12 100644 --- a/Runtime/UIParticleAttractor.cs +++ b/Runtime/UIParticleAttractor.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Coffee.UIParticleExtensions; +using Coffee.UIParticleInternal; using UnityEngine; using UnityEngine.Events; diff --git a/Runtime/UIParticleRenderer.cs b/Runtime/UIParticleRenderer.cs index 01e8f60..7bc9920 100644 --- a/Runtime/UIParticleRenderer.cs +++ b/Runtime/UIParticleRenderer.cs @@ -6,7 +6,7 @@ #endif using System; using System.Collections.Generic; -using Coffee.UIParticleExtensions; +using Coffee.UIParticleInternal; using UnityEditor; using UnityEngine; using UnityEngine.Profiling; @@ -21,19 +21,17 @@ namespace Coffee.UIExtensions [AddComponentMenu("")] internal class UIParticleRenderer : MaskableGraphic { - private static readonly List s_Components = new List(); private static readonly CombineInstance[] s_CombineInstances = { new CombineInstance() }; private static readonly List s_Materials = new List(2); private static MaterialPropertyBlock s_Mpb; - private static readonly List s_Renderers = new List(); private static readonly List s_Colors = new List(); private static readonly Vector3[] s_Corners = new Vector3[4]; - private Material _currentMaterialForRendering; private bool _delay; private int _index; private bool _isPrevStored; private bool _isTrail; private Bounds _lastBounds; + private Material _materialForRendering; private Material _modifiedMaterial; private UIParticle _parent; private ParticleSystem _particleSystem; @@ -92,6 +90,19 @@ namespace Coffee.UIExtensions } } + public override Material materialForRendering + { + get + { + if (!_materialForRendering) + { + _materialForRendering = base.materialForRendering; + } + + return _materialForRendering; + } + } + public void Reset(int index = -1) { if (_renderer) @@ -117,9 +128,8 @@ namespace Coffee.UIExtensions } else { - ModifiedMaterial.Remove(_modifiedMaterial); - _modifiedMaterial = null; - _currentMaterialForRendering = null; + MaterialRepository.Release(ref _modifiedMaterial); + _materialForRendering = null; } } @@ -135,17 +145,14 @@ namespace Coffee.UIExtensions hideFlags = HideFlags.HideAndDontSave }; } - - _currentMaterialForRendering = null; } protected override void OnDisable() { base.OnDisable(); - ModifiedMaterial.Remove(_modifiedMaterial); - _modifiedMaterial = null; - _currentMaterialForRendering = null; + MaterialRepository.Release(ref _modifiedMaterial); + _materialForRendering = null; _isPrevStored = false; } @@ -178,11 +185,9 @@ namespace Coffee.UIExtensions /// public override Material GetModifiedMaterial(Material baseMaterial) { - _currentMaterialForRendering = null; - if (!IsActive() || !_parent) { - ModifiedMaterial.Remove(_modifiedMaterial); + MaterialRepository.Release(ref _modifiedMaterial); _modifiedMaterial = null; return baseMaterial; } @@ -193,23 +198,29 @@ namespace Coffee.UIExtensions var texture = mainTexture; if (texture == null && _parent.m_AnimatableProperties.Length == 0) { - ModifiedMaterial.Remove(_modifiedMaterial); - _modifiedMaterial = null; + MaterialRepository.Release(ref _modifiedMaterial); return modifiedMaterial; } - // - var id = _parent.m_AnimatableProperties.Length == 0 ? 0 : GetInstanceID(); + var hash = new Hash128( + modifiedMaterial ? (uint)modifiedMaterial.GetInstanceID() : 0, + texture ? (uint)texture.GetInstanceID() : 0, + 0 < _parent.m_AnimatableProperties.Length ? (uint)GetInstanceID() : 0, #if UNITY_EDITOR - var props = EditorJsonUtility.ToJson(modifiedMaterial).GetHashCode(); + (uint)EditorJsonUtility.ToJson(modifiedMaterial).GetHashCode() #else - var props = 0; + 0 #endif - modifiedMaterial = ModifiedMaterial.Add(modifiedMaterial, texture, id, props); - ModifiedMaterial.Remove(_modifiedMaterial); - _modifiedMaterial = modifiedMaterial; + ); + if (!MaterialRepository.Valid(hash, _modifiedMaterial)) + { + MaterialRepository.Get(hash, ref _modifiedMaterial, () => new Material(modifiedMaterial) + { + hideFlags = HideFlags.HideAndDontSave + }); + } - return modifiedMaterial; + return _modifiedMaterial; } public void Set(UIParticle parent, ParticleSystem ps, bool isTrail) @@ -405,95 +416,67 @@ namespace Coffee.UIExtensions _lastBounds = bounds; // Convert linear color to gamma color. - if (QualitySettings.activeColorSpace == ColorSpace.Linear) + if (canvas.ShouldGammaToLinearInMesh()) { - Profiler.BeginSample("[UIParticleRenderer] Convert Linear to Gamma"); - workerMesh.GetColors(s_Colors); - var count_c = s_Colors.Count; - for (var i = 0; i < count_c; i++) - { - var c = s_Colors[i]; - c.r = c.r.LinearToGamma(); - c.g = c.g.LinearToGamma(); - c.b = c.b.LinearToGamma(); - s_Colors[i] = c; - } - - workerMesh.SetColors(s_Colors); - Profiler.EndSample(); + workerMesh.LinearToGamma(); } - GetComponents(typeof(IMeshModifier), s_Components); - for (var i = 0; i < s_Components.Count; i++) + var components = ListPool.Rent(); + GetComponents(typeof(IMeshModifier), components); + for (var i = 0; i < components.Count; i++) { #pragma warning disable CS0618 // Type or member is obsolete - ((IMeshModifier)s_Components[i]).ModifyMesh(workerMesh); + ((IMeshModifier)components[i]).ModifyMesh(workerMesh); #pragma warning restore CS0618 // Type or member is obsolete } - s_Components.Clear(); + ListPool.Return(ref components); } Profiler.EndSample(); - - // Get grouped renderers. - s_Renderers.Clear(); - if (_parent.useMeshSharing) - { - UIParticleUpdater.GetGroupedRenderers(_parent.groupId, _index, s_Renderers); - } - - // Set mesh to the CanvasRenderer. - Profiler.BeginSample("[UIParticleRenderer] Set Mesh"); - for (var i = 0; i < s_Renderers.Count; i++) - { - if (s_Renderers[i] == this) continue; - s_Renderers[i].canvasRenderer.SetMesh(workerMesh); - s_Renderers[i]._lastBounds = _lastBounds; - } - - if (!_parent.canRender) - { - workerMesh.Clear(); - } - - canvasRenderer.SetMesh(workerMesh); - Profiler.EndSample(); - // Update animatable material properties. Profiler.BeginSample("[UIParticleRenderer] Update Animatable Material Properties"); - -#if UNITY_EDITOR - if (_modifiedMaterial != material) - { - _renderer.GetSharedMaterials(s_Materials); - material = s_Materials[_isTrail ? 1 : 0]; - s_Materials.Clear(); - SetMaterialDirty(); - } -#endif - UpdateMaterialProperties(); + Profiler.EndSample(); + + // Get grouped renderers. + Profiler.BeginSample("[UIParticleRenderer] Set Mesh"); + var renderers = ListPool.Rent(); if (_parent.useMeshSharing) { - if (!_currentMaterialForRendering) - { - _currentMaterialForRendering = materialForRendering; - } + UIParticleUpdater.GetGroupedRenderers(_parent.groupId, _index, renderers); + } - for (var i = 0; i < s_Renderers.Count; i++) - { - if (s_Renderers[i] == this) continue; + for (var i = 0; i < renderers.Count; i++) + { + var r = renderers[i]; + if (r == this) continue; - s_Renderers[i].canvasRenderer.materialCount = 1; - s_Renderers[i].canvasRenderer.SetMaterial(_currentMaterialForRendering, 0); - } + r.canvasRenderer.SetMesh(workerMesh); + r._lastBounds = _lastBounds; + r.canvasRenderer.materialCount = 1; + r.canvasRenderer.SetMaterial(materialForRendering, 0); + } + + ListPool.Return(ref renderers); + + if (_parent.canRender) + { + canvasRenderer.SetMesh(workerMesh); + } + else + { + workerMesh.Clear(); } Profiler.EndSample(); + } - s_Renderers.Clear(); + public override void SetMaterialDirty() + { + _materialForRendering = null; + base.SetMaterialDirty(); } /// @@ -701,12 +684,12 @@ namespace Coffee.UIExtensions if (s_Mpb.isEmpty) return; // #41: Copy the value from MaterialPropertyBlock to CanvasRenderer - if (!_modifiedMaterial) return; + if (!materialForRendering) return; for (var i = 0; i < _parent.m_AnimatableProperties.Length; i++) { var ap = _parent.m_AnimatableProperties[i]; - ap.UpdateMaterialProperties(_modifiedMaterial, s_Mpb); + ap.UpdateMaterialProperties(materialForRendering, s_Mpb); } s_Mpb.Clear(); diff --git a/Runtime/UIParticleUpdater.cs b/Runtime/UIParticleUpdater.cs index c2c33f2..4df5153 100644 --- a/Runtime/UIParticleUpdater.cs +++ b/Runtime/UIParticleUpdater.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Coffee.UIParticleInternal; using UnityEditor; using UnityEngine; @@ -44,8 +45,7 @@ namespace Coffee.UIExtensions #endif private static void InitializeOnLoad() { - Canvas.willRenderCanvases -= Refresh; - Canvas.willRenderCanvases += Refresh; + UIExtraCallbacks.onAfterCanvasRebuild += Refresh; } private static void Refresh() diff --git a/Runtime/Utils.cs b/Runtime/Utils.cs deleted file mode 100644 index 4d4ac85..0000000 --- a/Runtime/Utils.cs +++ /dev/null @@ -1,308 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using UnityEngine; -using Object = UnityEngine.Object; - -namespace Coffee.UIParticleExtensions -{ - public static class Color32Extensions - { - private static byte[] s_LinearToGammaLut; - - public static byte LinearToGamma(this byte self) - { - if (s_LinearToGammaLut == null) - { - s_LinearToGammaLut = new byte[256]; - for (var i = 0; i < 256; i++) - { - s_LinearToGammaLut[i] = (byte)(Mathf.LinearToGammaSpace(i / 255f) * 255f); - } - } - - return s_LinearToGammaLut[self]; - } - } - - public static class Vector3Extensions - { - public static Vector3 Inverse(this Vector3 self) - { - self.x = Mathf.Approximately(self.x, 0) ? 1 : 1 / self.x; - self.y = Mathf.Approximately(self.y, 0) ? 1 : 1 / self.y; - self.z = Mathf.Approximately(self.z, 0) ? 1 : 1 / self.z; - return self; - } - - public static Vector3 GetScaled(this Vector3 self, Vector3 other1) - { - self.Scale(other1); - return self; - } - - public static Vector3 GetScaled(this Vector3 self, Vector3 other1, Vector3 other2) - { - self.Scale(other1); - self.Scale(other2); - return self; - } - - public static Vector3 GetScaled(this Vector3 self, Vector3 other1, Vector3 other2, Vector3 other3) - { - self.Scale(other1); - self.Scale(other2); - self.Scale(other3); - return self; - } - - public static bool IsVisible(this Vector3 self) - { - return 0 < Mathf.Abs(self.x * self.y * self.z); - } - } - - internal static class SpriteExtensions - { -#if UNITY_EDITOR - private static readonly Type s_SpriteEditorExtensionType = - Type.GetType("UnityEditor.Experimental.U2D.SpriteEditorExtension, UnityEditor") - ?? Type.GetType("UnityEditor.U2D.SpriteEditorExtension, UnityEditor"); - - private static readonly MethodInfo s_GetActiveAtlasTextureMethodInfo = s_SpriteEditorExtensionType - .GetMethod("GetActiveAtlasTexture", BindingFlags.Static | BindingFlags.NonPublic); - - public static Texture2D GetActualTexture(this Sprite self) - { - if (!self) return null; - - if (Application.isPlaying) return self.texture; - var ret = s_GetActiveAtlasTextureMethodInfo.Invoke(null, new object[] { self }) as Texture2D; - return ret - ? ret - : self.texture; - } -#else - internal static Texture2D GetActualTexture(this Sprite self) - { - return self ? self.texture : null; - } -#endif - } - - public static class ParticleSystemExtensions - { - private static ParticleSystem.Particle[] s_TmpParticles = new ParticleSystem.Particle[2048]; - - public static ParticleSystem.Particle[] GetParticleArray(int size) - { - if (s_TmpParticles.Length < size) - { - while (s_TmpParticles.Length < size) - { - size = Mathf.NextPowerOfTwo(size); - } - - s_TmpParticles = new ParticleSystem.Particle[size]; - } - - return s_TmpParticles; - } - - public static void ValidateShape(this ParticleSystem self) - { - var shape = self.shape; - if (shape.enabled && shape.alignToDirection) - { - if (Mathf.Approximately(shape.scale.x * shape.scale.y * shape.scale.z, 0)) - { - if (Mathf.Approximately(shape.scale.x, 0)) - { - shape.scale.Set(0.0001f, shape.scale.y, shape.scale.z); - } - else if (Mathf.Approximately(shape.scale.y, 0)) - { - shape.scale.Set(shape.scale.x, 0.0001f, shape.scale.z); - } - else if (Mathf.Approximately(shape.scale.z, 0)) - { - shape.scale.Set(shape.scale.x, shape.scale.y, 0.0001f); - } - } - } - } - - public static bool CanBakeMesh(this ParticleSystemRenderer self) - { - // #69: Editor crashes when mesh is set to null when `ParticleSystem.RenderMode = Mesh` - if (self.renderMode == ParticleSystemRenderMode.Mesh && self.mesh == null) return false; - - // #61: When `ParticleSystem.RenderMode = None`, an error occurs - if (self.renderMode == ParticleSystemRenderMode.None) return false; - - return true; - } - - public static ParticleSystemSimulationSpace GetActualSimulationSpace(this ParticleSystem self) - { - var main = self.main; - var space = main.simulationSpace; - if (space == ParticleSystemSimulationSpace.Custom && !main.customSimulationSpace) - { - space = ParticleSystemSimulationSpace.Local; - } - - return space; - } - - public static bool IsLocalSpace(this ParticleSystem self) - { - return GetActualSimulationSpace(self) == ParticleSystemSimulationSpace.Local; - } - - public static bool IsWorldSpace(this ParticleSystem self) - { - return GetActualSimulationSpace(self) == ParticleSystemSimulationSpace.World; - } - - public static void SortForRendering(this List self, Transform transform, bool sortByMaterial) - { - self.Sort((a, b) => - { - var aRenderer = a.GetComponent(); - var bRenderer = b.GetComponent(); - - // Render queue: ascending - var aMat = aRenderer.sharedMaterial ? aRenderer.sharedMaterial : aRenderer.trailMaterial; - var bMat = bRenderer.sharedMaterial ? bRenderer.sharedMaterial : bRenderer.trailMaterial; - if (!aMat && !bMat) return 0; - if (!aMat) return -1; - if (!bMat) return 1; - - if (sortByMaterial) - { - return aMat.GetInstanceID() - bMat.GetInstanceID(); - } - - if (aMat.renderQueue != bMat.renderQueue) - { - return aMat.renderQueue - bMat.renderQueue; - } - - // Sorting layer: ascending - if (aRenderer.sortingLayerID != bRenderer.sortingLayerID) - { - return SortingLayer.GetLayerValueFromID(aRenderer.sortingLayerID) - - SortingLayer.GetLayerValueFromID(bRenderer.sortingLayerID); - } - - // Sorting order: ascending - if (aRenderer.sortingOrder != bRenderer.sortingOrder) - { - return aRenderer.sortingOrder - bRenderer.sortingOrder; - } - - // Z position & sortingFudge: descending - var aTransform = a.transform; - var bTransform = b.transform; - var aPos = transform.InverseTransformPoint(aTransform.position).z + aRenderer.sortingFudge; - var bPos = transform.InverseTransformPoint(bTransform.position).z + bRenderer.sortingFudge; - if (!Mathf.Approximately(aPos, bPos)) - { - return (int)Mathf.Sign(bPos - aPos); - } - - return (int)Mathf.Sign(GetIndex(self, a) - GetIndex(self, b)); - }); - } - - private static int GetIndex(IList list, Object ps) - { - for (var i = 0; i < list.Count; i++) - { - if (list[i].GetInstanceID() == ps.GetInstanceID()) - { - return i; - } - } - - return 0; - } - - public static Texture2D GetTextureForSprite(this ParticleSystem self) - { - if (!self) return null; - - // Get sprite's texture. - var tsaModule = self.textureSheetAnimation; - if (!tsaModule.enabled || tsaModule.mode != ParticleSystemAnimationMode.Sprites) return null; - - for (var i = 0; i < tsaModule.spriteCount; i++) - { - var sprite = tsaModule.GetSprite(i); - if (!sprite) continue; - - return sprite.GetActualTexture(); - } - - return null; - } - - public static void Exec(this List self, Action action) - { - self.RemoveAll(p => !p); - self.ForEach(action); - } - } - - internal static class Misc - { - public static void Destroy(Object obj) - { - if (!obj) return; -#if UNITY_EDITOR - if (!Application.isPlaying) - { - Object.DestroyImmediate(obj); - } - else -#endif - { - Object.Destroy(obj); - } - } - - public static void DestroyImmediate(Object obj) - { - if (!obj) return; -#if UNITY_EDITOR - if (Application.isEditor) - { - Object.DestroyImmediate(obj); - } - else -#endif - { - Object.Destroy(obj); - } - } - -#if !UNITY_2021_2_OR_NEWER && !UNITY_2020_3_45 && !UNITY_2020_3_46 && !UNITY_2020_3_47 && !UNITY_2020_3_48 - public static T GetComponentInParent(this Component self, bool includeInactive) where T : Component - { - if (!self) return null; - if (!includeInactive) return self.GetComponentInParent(); - - var current = self.transform; - while (current) - { - var component = current.GetComponent(); - if (component) return component; - current = current.parent; - } - - return null; - } -#endif - } -}