From 1e25ff05a60c95a07694476ee1f47d18f66330ac Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Wed, 16 Jan 2019 17:07:11 +0100 Subject: [PATCH] [unity] Fixed removing unreferenced SlotBlendMode material entries, closes #1249. --- .../Modules/SlotBlendModes/SlotBlendModes.cs | 128 ++++++++++++++---- 1 file changed, 100 insertions(+), 28 deletions(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/SlotBlendModes/SlotBlendModes.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/SlotBlendModes/SlotBlendModes.cs index 567b0625d..2e2a56c35 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/SlotBlendModes/SlotBlendModes.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/SlotBlendModes/SlotBlendModes.cs @@ -42,28 +42,70 @@ namespace Spine.Unity.Modules { public Material material; } - static Dictionary materialTable; - internal static Dictionary MaterialTable { + internal class MaterialWithRefcount { + public Material materialClone; + public int refcount = 1; + + public MaterialWithRefcount(Material mat) { + this.materialClone = mat; + } + } + static Dictionary materialTable; + internal static Dictionary MaterialTable { get { - if (materialTable == null) materialTable = new Dictionary(); + if (materialTable == null) materialTable = new Dictionary(); return materialTable; } } - internal static Material GetMaterialFor (Material materialSource, Texture2D texture) { + internal struct SlotMaterialTextureTuple { + public Slot slot; + public Texture2D texture2D; + public Material material; + + public SlotMaterialTextureTuple(Slot slot, Material material, Texture2D texture) { + this.slot = slot; + this.material = material; + this.texture2D = texture; + } + } + + internal static Material GetOrAddMaterialFor(Material materialSource, Texture2D texture) { if (materialSource == null || texture == null) return null; var mt = SlotBlendModes.MaterialTable; - Material m; + MaterialWithRefcount matWithRefcount; var key = new MaterialTexturePair { material = materialSource, texture2D = texture }; - if (!mt.TryGetValue(key, out m)) { - m = new Material(materialSource); + if (!mt.TryGetValue(key, out matWithRefcount)) { + matWithRefcount = new MaterialWithRefcount(new Material(materialSource)); + var m = matWithRefcount.materialClone; m.name = "(Clone)" + texture.name + "-" + materialSource.name; m.mainTexture = texture; - mt[key] = m; + mt[key] = matWithRefcount; } + else { + matWithRefcount.refcount++; + } + return matWithRefcount.materialClone; + } - return m; + internal static MaterialWithRefcount GetExistingMaterialFor(Material materialSource, Texture2D texture) + { + if (materialSource == null || texture == null) return null; + + var mt = SlotBlendModes.MaterialTable; + MaterialWithRefcount matWithRefcount; + var key = new MaterialTexturePair { material = materialSource, texture2D = texture }; + if (!mt.TryGetValue(key, out matWithRefcount)) { + return null; + } + return matWithRefcount; + } + + internal static void RemoveMaterialFromTable(Material materialSource, Texture2D texture) { + var mt = SlotBlendModes.MaterialTable; + var key = new MaterialTexturePair { material = materialSource, texture2D = texture }; + mt.Remove(key); } #endregion @@ -74,17 +116,19 @@ namespace Spine.Unity.Modules { Texture2D texture; #endregion + SlotMaterialTextureTuple[] slotsWithCustomMaterial = new SlotMaterialTextureTuple[0]; + public bool Applied { get; private set; } - void Start () { + void Start() { if (!Applied) Apply(); } - void OnDestroy () { + void OnDestroy() { if (Applied) Remove(); } - public void Apply () { + public void Apply() { GetTexture(); if (texture == null) return; @@ -93,13 +137,36 @@ namespace Spine.Unity.Modules { var slotMaterials = skeletonRenderer.CustomSlotMaterials; + int numSlotsWithCustomMaterial = 0; foreach (var s in skeletonRenderer.Skeleton.Slots) { switch (s.data.blendMode) { case BlendMode.Multiply: - if (multiplyMaterialSource != null) slotMaterials[s] = GetMaterialFor(multiplyMaterialSource, texture); + if (multiplyMaterialSource != null) { + slotMaterials[s] = GetOrAddMaterialFor(multiplyMaterialSource, texture); + ++numSlotsWithCustomMaterial; + } break; case BlendMode.Screen: - if (screenMaterialSource != null) slotMaterials[s] = GetMaterialFor(screenMaterialSource, texture); + if (screenMaterialSource != null) { + slotMaterials[s] = GetOrAddMaterialFor(screenMaterialSource, texture); + ++numSlotsWithCustomMaterial; + } + break; + } + } + slotsWithCustomMaterial = new SlotMaterialTextureTuple[numSlotsWithCustomMaterial]; + int storedSlotIndex = 0; + foreach (var s in skeletonRenderer.Skeleton.Slots) { + switch (s.data.blendMode) { + case BlendMode.Multiply: + if (multiplyMaterialSource != null) { + slotsWithCustomMaterial[storedSlotIndex++] = new SlotMaterialTextureTuple(s, multiplyMaterialSource, texture); + } + break; + case BlendMode.Screen: + if (screenMaterialSource != null) { + slotsWithCustomMaterial[storedSlotIndex++] = new SlotMaterialTextureTuple(s, screenMaterialSource, texture); + } break; } } @@ -108,7 +175,7 @@ namespace Spine.Unity.Modules { skeletonRenderer.LateUpdate(); } - public void Remove () { + public void Remove() { GetTexture(); if (texture == null) return; @@ -117,26 +184,32 @@ namespace Spine.Unity.Modules { var slotMaterials = skeletonRenderer.CustomSlotMaterials; - foreach (var s in skeletonRenderer.Skeleton.Slots) { - Material m = null; + foreach (var slotWithCustomMat in slotsWithCustomMaterial) { - switch (s.data.blendMode) { - case BlendMode.Multiply: - if (slotMaterials.TryGetValue(s, out m) && Material.ReferenceEquals(m, GetMaterialFor(multiplyMaterialSource, texture))) + Slot s = slotWithCustomMat.slot; + Material storedMaterialSource = slotWithCustomMat.material; + Texture2D storedTexture = slotWithCustomMat.texture2D; + + var matWithRefcount = GetExistingMaterialFor(storedMaterialSource, storedTexture); + if (--matWithRefcount.refcount == 0) { + RemoveMaterialFromTable(storedMaterialSource, storedTexture); + } + // we don't want to remove slotMaterials[s] if it has been changed in the meantime. + Material m; + if (slotMaterials.TryGetValue(s, out m)) { + var existingMat = matWithRefcount == null ? null : matWithRefcount.materialClone; + if (Material.ReferenceEquals(m, existingMat)) { slotMaterials.Remove(s); - break; - case BlendMode.Screen: - if (slotMaterials.TryGetValue(s, out m) && Material.ReferenceEquals(m, GetMaterialFor(screenMaterialSource, texture))) - slotMaterials.Remove(s); - break; + } } } - + slotsWithCustomMaterial = null; + Applied = false; if (skeletonRenderer.valid) skeletonRenderer.LateUpdate(); } - public void GetTexture () { + public void GetTexture() { if (texture == null) { var sr = GetComponent(); if (sr == null) return; var sda = sr.skeletonDataAsset; if (sda == null) return; @@ -146,7 +219,6 @@ namespace Spine.Unity.Modules { } } - } }