Skip to content

Commit def9400

Browse files
SolidAlloyxsduan
authored andcommitted
Made editor icons use shaders instead of slow color filters
Changing textures with shaders is much more faster than using custom C# methods to manage pixels. New GetTextureWithMaterial method is 16x faster than GetColorizedTexture.
1 parent 0552c5c commit def9400

File tree

3 files changed

+162
-28
lines changed

3 files changed

+162
-28
lines changed

Editor/HierarchyFolderIcon.cs

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
namespace UnityHierarchyFolders.Editor
1515
{
16-
public class HierarchyFolderIcon
16+
public static class HierarchyFolderIcon
1717
{
1818
#if UNITY_2020_1_OR_NEWER
1919
private const string _openedFolderPrefix = "FolderOpened";
@@ -30,7 +30,6 @@ public class HierarchyFolderIcon
3030
private static bool _isInitialized;
3131
private static bool _hasProcessedFrame = true;
3232

33-
3433
// Reflected members
3534
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Special naming scheme")]
3635
private static PropertyInfo prop_sceneHierarchy;
@@ -75,41 +74,43 @@ private static void Startup()
7574
EditorApplication.hierarchyWindowItemOnGUI += RefreshFolderIcons;
7675
}
7776

78-
private static Texture2D GetTintedTexture(Texture2D original, Color tint, string name = "") =>
79-
GetColorizedTexture(original, c => c * tint, name);
77+
private static Texture2D GetTintedTexture(Texture2D original, Color tint, string name)
78+
{
79+
var material = new Material(Shader.Find("UI/Default")) { color = tint };
80+
return GetTextureWithMaterial(original, material, name);
81+
}
8082

81-
private static Texture2D GetWhiteTexture(Texture2D original, string name = "") =>
82-
GetColorizedTexture(original, c => new Color(255, 255, 255, c.a), name);
83+
private static Texture2D GetWhiteTexture(Texture2D original, string name)
84+
{
85+
var material = new Material(Shader.Find("UI/Replace color")) { color = Color.white };
86+
return GetTextureWithMaterial(original, material, name);
87+
}
8388

84-
private static Texture2D GetColorizedTexture(Texture2D original, Func<Color32, Color32> colorFilter, string name = "")
89+
private static Texture2D GetTextureWithMaterial(Texture2D original, Material material, string name)
8590
{
86-
var tinted = new Texture2D(original.width, original.height,
87-
original.graphicsFormat, original.mipmapCount, TextureCreationFlags.MipChain)
91+
Texture2D newTexture;
92+
93+
using (new TextureHelper.SRGBWriteScope(true))
8894
{
89-
name = name
90-
};
95+
using (var temporary = new TextureHelper.TemporaryActiveTexture(original.width, original.height, 0))
96+
{
97+
GL.Clear(false, true, new Color(1f, 1f, 1f, 0f));
9198

92-
Graphics.CopyTexture(original, tinted);
99+
Graphics.Blit(original, temporary, material);
93100

94-
var data = tinted.GetRawTextureData<Color32>();
95-
for (int index = 0, len = data.Length; index < len; index++)
96-
{
97-
data[index] = colorFilter(data[index]);
98-
}
101+
newTexture = new Texture2D(original.width, original.width, TextureFormat.ARGB32, false, true)
102+
{
103+
name = name,
104+
filterMode = FilterMode.Bilinear
105+
};
99106

100-
int mipmapSize = tinted.width * tinted.height * 4;
101-
int offset = 0;
102-
for (int index = 0; index < tinted.mipmapCount; index++)
103-
{
104-
tinted.SetPixelData(data, index, offset);
105-
mipmapSize >>= 2;
106-
offset += mipmapSize;
107+
newTexture.ReadPixels(new Rect(0.0f, 0.0f, original.width, original.width), 0, 0);
108+
newTexture.alphaIsTransparency = true;
109+
newTexture.Apply();
110+
}
107111
}
108112

109-
tinted.hideFlags = HideFlags.DontSave;
110-
tinted.Apply();
111-
112-
return tinted;
113+
return newTexture;
113114
}
114115

115116
private static void InitIfNeeded()

Editor/TextureHelper.cs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// This software includes third-party software subject to the associated copyrights, as follows:
2+
//
3+
// Name: SolidUtilities
4+
// Repo: https://github.com/SolidAlloy/SolidUtilities
5+
// License: MIT (https://github.com/SolidAlloy/SolidUtilities/blob/master/LICENSE)
6+
// Copyright (c) 2020 SolidAlloy
7+
8+
namespace UnityHierarchyFolders.Editor
9+
{
10+
using System;
11+
using JetBrains.Annotations;
12+
using UnityEngine;
13+
14+
/// <summary>Helps to create new textures.</summary>
15+
public static class TextureHelper
16+
{
17+
/// <summary>
18+
/// Temporarily sets <see cref="GL.sRGBWrite"/> to the passed value, then returns it back.
19+
/// </summary>
20+
[PublicAPI]
21+
public readonly struct SRGBWriteScope : IDisposable
22+
{
23+
private readonly bool _previousValue;
24+
25+
/// <summary>Temporarily sets <see cref="GL.sRGBWrite"/> to <paramref name=""/>, then executes the action.</summary>
26+
/// <param name="enableWrite"> Temporary value of <see cref="GL.sRGBWrite"/>. </param>
27+
/// <example><code>
28+
/// using (new SRGBWriteScope(true))
29+
/// {
30+
/// GL.Clear(false, true, new Color(1f, 1f, 1f, 0f));
31+
/// Graphics.Blit(Default, temporary, material);
32+
/// });
33+
/// </code></example>
34+
public SRGBWriteScope(bool enableWrite)
35+
{
36+
_previousValue = GL.sRGBWrite;
37+
GL.sRGBWrite = enableWrite;
38+
}
39+
40+
public void Dispose()
41+
{
42+
GL.sRGBWrite = _previousValue;
43+
}
44+
}
45+
46+
/// <summary>
47+
/// Creates a temporary texture, sets it as active in <see cref="RenderTexture.active"/>, then removes the changes
48+
/// and sets the previous active texture back automatically.
49+
/// </summary>
50+
/// <seealso cref="TemporaryRenderTexture"/>
51+
/// <example><code>
52+
/// using (var temporaryActiveTexture = new TemporaryActiveTexture(icon.width, icon.height, 0))
53+
/// {
54+
/// Graphics.Blit(icon, temporary, material);
55+
/// });
56+
/// </code></example>
57+
[PublicAPI]
58+
public class TemporaryActiveTexture : IDisposable
59+
{
60+
private readonly RenderTexture _previousActiveTexture;
61+
private readonly TemporaryRenderTexture _value;
62+
63+
/// <summary>
64+
/// Creates a temporary texture, sets it as active in <see cref="RenderTexture.active"/>, then removes it
65+
/// and sets the previous active texture back automatically.
66+
/// </summary>
67+
/// <param name="width">Width of the temporary texture in pixels.</param>
68+
/// <param name="height">Height of the temporary texture in pixels.</param>
69+
/// <param name="depthBuffer">Depth buffer of the temporary texture.</param>
70+
/// <seealso cref="TemporaryRenderTexture"/>
71+
/// <example><code>
72+
/// using (var temporaryActiveTexture = new TemporaryActiveTexture(icon.width, icon.height, 0))
73+
/// {
74+
/// Graphics.Blit(icon, temporary, material);
75+
/// });
76+
/// </code></example>
77+
public TemporaryActiveTexture(int width, int height, int depthBuffer)
78+
{
79+
_previousActiveTexture = RenderTexture.active;
80+
_value = new TemporaryRenderTexture(width, height, depthBuffer);
81+
RenderTexture.active = _value;
82+
}
83+
84+
public static implicit operator RenderTexture(TemporaryActiveTexture temporaryTexture) => temporaryTexture._value;
85+
86+
public void Dispose()
87+
{
88+
_value.Dispose();
89+
RenderTexture.active = _previousActiveTexture;
90+
}
91+
}
92+
93+
/// <summary>Creates a temporary texture that can be used and then removed automatically.</summary>
94+
/// <seealso cref="TemporaryActiveTexture"/>
95+
/// <example><code>
96+
/// using (var temporaryTexture = new TemporaryRenderTexture(icon.width, icon.height, 0))
97+
/// {
98+
/// Graphics.Blit(icon, temporaryTexture, material);
99+
/// });
100+
/// </code></example>
101+
[PublicAPI]
102+
public class TemporaryRenderTexture : IDisposable
103+
{
104+
private readonly RenderTexture _value;
105+
106+
/// <summary>Creates a temporary texture that can be used and then removed automatically.</summary>
107+
/// <param name="width">Width of the temporary texture in pixels.</param>
108+
/// <param name="height">Height of the temporary texture in pixels.</param>
109+
/// <param name="depthBuffer">Depth buffer of the temporary texture.</param>
110+
/// <seealso cref="TemporaryActiveTexture"/>
111+
/// <example><code>
112+
/// using (var temporaryTexture = new TemporaryRenderTexture(icon.width, icon.height, 0))
113+
/// {
114+
/// Graphics.Blit(icon, temporaryTexture, material);
115+
/// });
116+
/// </code></example>
117+
public TemporaryRenderTexture(int width, int height, int depthBuffer)
118+
{
119+
_value = RenderTexture.GetTemporary(width, height, depthBuffer);
120+
}
121+
122+
public static implicit operator RenderTexture(TemporaryRenderTexture temporaryRenderTexture) => temporaryRenderTexture._value;
123+
124+
public void Dispose()
125+
{
126+
RenderTexture.ReleaseTemporary(_value);
127+
}
128+
}
129+
}
130+
}

Editor/TextureHelper.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)