Created
October 1, 2024 07:50
-
-
Save mdsitton/6c8b3f706a9db21deb26832caa1f4e74 to your computer and use it in GitHub Desktop.
Sprite renderer Overhead test
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Runtime.CompilerServices; | |
using UnityEngine; | |
[DefaultExecutionOrder(-60)] | |
public class SpriteTextRenderer : Singleton<SpriteTextRenderer> | |
{ | |
public struct TextInfo | |
{ | |
public string text; | |
public SpriteFont font; | |
public Vector2Int position; | |
public Color32 color; | |
public int scale; | |
public bool bounce; | |
} | |
private int textCount = 0; | |
private TextInfo[] textLines = new TextInfo[100]; | |
private SpritePool pool; | |
protected override void Awake() | |
{ | |
base.Awake(); | |
pool = SpritePool.Instance; | |
} | |
public int AddText(string text, SpriteFont font, Vector2Int position, Color32 color, int scale = 1, bool bounce = false) | |
{ | |
if (font == null) | |
{ | |
throw new ArgumentNullException("Font cannot be null"); | |
} | |
if (disableNewText) | |
{ | |
return -1; | |
} | |
if (textCount + 1 >= textLines.Length) | |
{ | |
Array.Resize(ref textLines, textLines.Length * 2); | |
} | |
textLines[textCount] = new TextInfo | |
{ | |
text = text, | |
font = font, | |
position = position, | |
color = color, | |
scale = scale, | |
bounce = bounce | |
}; ; | |
return textCount++; | |
} | |
public ref TextInfo UpdateText(int textId) | |
{ | |
return ref textLines[textId]; | |
} | |
public void ClearText() | |
{ | |
textCount = 0; | |
} | |
bool disableNewText = false; | |
float timer = 0.0f; | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private void RenderLine(ref TextInfo textInfo) | |
{ | |
var willBounce = textInfo.bounce; | |
var text = textInfo.text; | |
var font = textInfo.font; | |
var textSpacing = font.charDistance * (willBounce ? bounce : 1.0f); | |
var position = textInfo.position; | |
var color = textInfo.color; | |
var scale = textInfo.scale; | |
var posx = position.x; | |
var posy = position.y; | |
Vector2 vector2 = new(posx, posy); | |
bool applyRandom = willBounce && timer > 0.1f; | |
char offset = applyRandom ? (char)UnityEngine.Random.Range(0, 20) : (char)0; | |
color.a = willBounce ? (byte)Mathf.Min((1.25f - bounce) * 255, 255) : (byte)255; | |
for (int i = 0; i < text.Length; i++) | |
{ | |
var c = text[i]; | |
var sprite = font.GetSprite((char)(c + offset)); | |
ref var virtualSprite = ref pool.UseSprite(); | |
if (virtualSprite.spriteIndex == SpritePool.SpritePoolSize - 1) | |
{ | |
disableNewText = true; | |
break; | |
} | |
virtualSprite.sprite = sprite; | |
vector2.x = posx + (i * (textSpacing * scale)); | |
virtualSprite.position = vector2; | |
virtualSprite.scale = new Vector2(scale, scale); | |
virtualSprite.color = color; | |
} | |
} | |
private float bounce; | |
private bool swap; | |
private float cycleTime = 0; | |
public void RenderText() | |
{ | |
cycleTime += Time.deltaTime * 10; | |
if (cycleTime >= (2 * Mathf.PI)) | |
{ | |
cycleTime -= 2 * Mathf.PI; | |
swap = !swap; | |
} | |
bounce = swap ? 1.0f : 1.0f + Mathf.Sin(cycleTime) * 0.25f; | |
timer += Time.deltaTime; | |
for (int i = 0; i < textCount; i++) | |
{ | |
RenderLine(ref textLines[i]); | |
} | |
if (timer > 0.2f) | |
{ | |
timer -= 0.2f; | |
} | |
} | |
public void Update() | |
{ | |
RenderText(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Globalization; | |
using UnityEngine; | |
using System.Runtime.CompilerServices; | |
using UnityEngine; | |
public static class UtilExtensions | |
{ | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static bool ValueEquals(this Color32 a, Color32 b) => a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; | |
} | |
/// <summary> | |
/// A virtual sprite state which is used for reducing the | |
/// amount of changes on the sprite pool between frames | |
/// </summary> | |
public struct VirtualSpriteState | |
{ | |
public Sprite sprite; | |
public Vector2 position; | |
public Vector2 scale; | |
public Color32 color; | |
public readonly int spriteIndex; | |
public bool enabled; | |
public VirtualSpriteState(int spriteIndex) | |
{ | |
sprite = null; | |
position = Vector2.zero; | |
scale = Vector2.one; | |
color = Color.white; | |
this.spriteIndex = spriteIndex; | |
enabled = false; | |
} | |
public void Defaults() | |
{ | |
sprite = null; | |
position = Vector2.zero; | |
scale = Vector2.one; | |
color = Color.white; | |
} | |
} | |
[DefaultExecutionOrder(-50)] | |
public class SpritePool : Singleton<SpritePool> | |
{ | |
public SpriteRenderer[] spriteRenderers; | |
public VirtualSpriteState[] currentSpriteStates; | |
public VirtualSpriteState[] nextSpriteStates; | |
public const int SpritePoolSize = 5000; | |
public int currentSpriteIndex = 0; | |
private int textId; | |
public SpriteFont font; | |
protected override void Awake() | |
{ | |
base.Awake(); | |
FillPool(); | |
font = Resources.Load<SpriteFont>("8x7 accent font"); | |
textId = SpriteTextRenderer.Instance.AddText("Sprite count", font, Vector2Int.up, Color.magenta, 4); | |
} | |
public void FillPool() | |
{ | |
spriteRenderers = new SpriteRenderer[SpritePoolSize]; | |
nextSpriteStates = new VirtualSpriteState[SpritePoolSize]; | |
currentSpriteStates = new VirtualSpriteState[SpritePoolSize]; | |
for (int i = 0; i < SpritePoolSize; i++) | |
{ | |
GameObject go = new("SpriteContainer"); | |
go.transform.SetParent(transform); | |
spriteRenderers[i] = go.AddComponent<SpriteRenderer>(); | |
spriteRenderers[i].enabled = false; | |
currentSpriteStates[i] = new VirtualSpriteState(i); | |
nextSpriteStates[i] = new VirtualSpriteState(i); | |
} | |
} | |
public ref VirtualSpriteState UseSprite() | |
{ | |
if (currentSpriteIndex >= SpritePoolSize) | |
{ | |
currentSpriteIndex--; | |
} | |
return ref nextSpriteStates[currentSpriteIndex++]; | |
} | |
Color32 fullColor = Color.green; | |
private void Update() | |
{ | |
ref var textInfo = ref SpriteTextRenderer.Instance.UpdateText(textId); | |
textInfo.text = $"Sprite count: {currentSpriteIndex.ToString(CultureInfo.InvariantCulture)}"; | |
if (currentSpriteIndex >= SpritePoolSize) | |
{ | |
textInfo.color = fullColor; | |
} | |
for (int i = 0; i < currentSpriteIndex; i++) | |
{ | |
ref var newSpriteState = ref nextSpriteStates[i]; | |
ref var oldSpriteState = ref currentSpriteStates[i]; | |
if (!oldSpriteState.enabled) | |
{ | |
newSpriteState.enabled = true; | |
spriteRenderers[i].enabled = newSpriteState.enabled; | |
} | |
if (newSpriteState.sprite != oldSpriteState.sprite) | |
{ | |
spriteRenderers[i].sprite = newSpriteState.sprite; | |
} | |
if (newSpriteState.position.x != oldSpriteState.position.x || newSpriteState.position.y != oldSpriteState.position.y) | |
{ | |
spriteRenderers[i].transform.position = newSpriteState.position; | |
} | |
if (newSpriteState.scale.x != oldSpriteState.scale.x || newSpriteState.scale.y != oldSpriteState.scale.y) | |
{ | |
spriteRenderers[i].transform.localScale = newSpriteState.scale; | |
} | |
if (!newSpriteState.color.ValueEquals(oldSpriteState.color)) | |
{ | |
spriteRenderers[i].color = newSpriteState.color; | |
} | |
oldSpriteState = newSpriteState; | |
} | |
// Disable the rest of the sprites | |
for (int i = currentSpriteIndex; i < SpritePoolSize; i++) | |
{ | |
spriteRenderers[i].enabled = false; | |
currentSpriteStates[i].enabled = false; | |
} | |
currentSpriteIndex = 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment