diff --git a/GameData/ConformalDecals/Plugins/ConformalDecals.dll b/GameData/ConformalDecals/Plugins/ConformalDecals.dll
index aacb0e2..f6aa397 100644
--- a/GameData/ConformalDecals/Plugins/ConformalDecals.dll
+++ b/GameData/ConformalDecals/Plugins/ConformalDecals.dll
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0c965b683c33c4150cdb3381bcfe81a0fff643ba3e5e3207ea704b90e8885c1e
-size 76800
+oid sha256:7f42421e78ddbe2af88112fa7fa254f870bda45f07a3436af64832a9646ebdef
+size 80896
diff --git a/Source/ConformalDecals/ConformalDecals.csproj b/Source/ConformalDecals/ConformalDecals.csproj
index f4c8b7a..b6a10d0 100644
--- a/Source/ConformalDecals/ConformalDecals.csproj
+++ b/Source/ConformalDecals/ConformalDecals.csproj
@@ -90,6 +90,7 @@
+
diff --git a/Source/ConformalDecals/ModuleConformalText.cs b/Source/ConformalDecals/ModuleConformalText.cs
index a851536..6e3615a 100644
--- a/Source/ConformalDecals/ModuleConformalText.cs
+++ b/Source/ConformalDecals/ModuleConformalText.cs
@@ -27,8 +27,8 @@ namespace ConformalDecals {
var decalText = new DecalText("Hello World!", _font, _style);
- TextRenderer.Instance.RenderText(decalText, out var texture, out var window);
- materialProperties.AddOrGetTextureProperty("_Decal", true).Texture = texture;
+ //TextRenderer.Instance.RenderText(decalText, out var texture, out var window);
+ //materialProperties.AddOrGetTextureProperty("_Decal", true).Texture = texture;
UpdateMaterials();
UpdateScale();
}
diff --git a/Source/ConformalDecals/Text/RenderedText.cs b/Source/ConformalDecals/Text/RenderedText.cs
index d00c293..fc31ae4 100644
--- a/Source/ConformalDecals/Text/RenderedText.cs
+++ b/Source/ConformalDecals/Text/RenderedText.cs
@@ -1,11 +1,16 @@
using UnityEngine;
namespace ConformalDecals.Text {
- public class RenderedText : ScriptableObject {
+ public class RenderedText {
public Texture2D Texture { get; private set; }
public Rect Window { get; private set; }
- public int UserCount { get; private set; }
+ public int UserCount { get; set; }
+
+ public RenderedText(Texture2D texture, Rect window) {
+ Texture = texture;
+ Window = window;
+ }
}
}
\ No newline at end of file
diff --git a/Source/ConformalDecals/Text/TextRenderJob.cs b/Source/ConformalDecals/Text/TextRenderJob.cs
new file mode 100644
index 0000000..0d17cbd
--- /dev/null
+++ b/Source/ConformalDecals/Text/TextRenderJob.cs
@@ -0,0 +1,42 @@
+using System;
+using UnityEngine.Events;
+
+namespace ConformalDecals.Text {
+ public class TextRenderJob {
+ public DecalText OldText { get; }
+ public DecalText NewText { get; }
+ public bool Needed { get; private set; }
+ public bool IsStarted { get; private set; }
+ public bool IsDone { get; private set; }
+
+ public readonly TextRenderer.TextRenderEvent onRenderFinished = new TextRenderer.TextRenderEvent();
+
+ public TextRenderJob(DecalText oldText, DecalText newText, UnityAction renderFinishedCallback) {
+ OldText = oldText ?? throw new ArgumentNullException(nameof(oldText));
+ NewText = newText ?? throw new ArgumentNullException(nameof(newText));
+ Needed = true;
+
+ if (renderFinishedCallback != null) onRenderFinished.AddListener(renderFinishedCallback);
+ }
+
+ public TextRenderJob( DecalText newText, UnityAction renderFinishedCallback) {
+ NewText = newText ?? throw new ArgumentNullException(nameof(newText));
+ Needed = true;
+
+ if (renderFinishedCallback != null) onRenderFinished.AddListener(renderFinishedCallback);
+ }
+
+ public void Cancel() {
+ Needed = false;
+ }
+
+ public void Start() {
+ IsStarted = true;
+ }
+
+ public void Finish(RenderedText output) {
+ IsDone = true;
+ onRenderFinished.Invoke(output);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/ConformalDecals/Text/TextRenderer.cs b/Source/ConformalDecals/Text/TextRenderer.cs
index aee2b59..8cd57df 100644
--- a/Source/ConformalDecals/Text/TextRenderer.cs
+++ b/Source/ConformalDecals/Text/TextRenderer.cs
@@ -1,13 +1,15 @@
using System;
-using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
-using UnityEngine.Rendering;
+using UnityEngine.Events;
namespace ConformalDecals.Text {
[KSPAddon(KSPAddon.Startup.Instantly, true)]
public class TextRenderer : MonoBehaviour {
+ public const TextureFormat TextTextureFormat = TextureFormat.RG16;
+ public const RenderTextureFormat TextRenderTextureFormat = RenderTextureFormat.R8;
+
public static TextRenderer Instance {
get {
if (!_instance._isSetup) {
@@ -18,8 +20,8 @@ namespace ConformalDecals.Text {
}
}
- public const TextureFormat TextTextureFormat = TextureFormat.RG16;
- public const RenderTextureFormat TextRenderTextureFormat = RenderTextureFormat.R8;
+ [Serializable]
+ public class TextRenderEvent : UnityEvent { }
private const string BlitShader = "ConformalDecals/Text Blit";
private const int MaxTextureSize = 4096;
@@ -32,8 +34,30 @@ namespace ConformalDecals.Text {
private TextMeshPro _tmp;
private Material _blitMaterial;
- private Dictionary _renderedTextures = new Dictionary();
- private Texture2D _lastTexture; // to reduce the number of Texture2D objects created and destroyed, keep the last one on hand
+ private readonly Dictionary _renderCache = new Dictionary();
+ private readonly Queue _renderJobs = new Queue();
+
+ public TextRenderJob RenderText(DecalText text, UnityAction renderFinishedCallback) {
+ var job = new TextRenderJob(text, renderFinishedCallback);
+ _renderJobs.Enqueue(job);
+ return job;
+ }
+
+ public TextRenderJob UpdateText(DecalText oldText, DecalText newText, UnityAction renderFinishedCallback) {
+ var job = new TextRenderJob(oldText, newText, renderFinishedCallback);
+ _renderJobs.Enqueue(job);
+ return job;
+ }
+
+ public void UnregisterText(DecalText text) {
+ if (_renderCache.TryGetValue(text, out var renderedText)) {
+ renderedText.UserCount--;
+ if (renderedText.UserCount <= 0) {
+ _renderCache.Remove(text);
+ Destroy(renderedText.Texture);
+ }
+ }
+ }
private void Start() {
if (_instance != null) {
@@ -45,6 +69,16 @@ namespace ConformalDecals.Text {
DontDestroyOnLoad(gameObject);
}
+ private void Update() {
+ TextRenderJob nextJob;
+ do {
+ if (_renderJobs.Count <= 0) return;
+ nextJob = _renderJobs.Dequeue();
+ } while (nextJob.Needed);
+
+ RunJob(nextJob);
+ }
+
private void Setup() {
if (_isSetup) return;
@@ -60,7 +94,47 @@ namespace ConformalDecals.Text {
_isSetup = true;
}
- public void RenderText(DecalText text, out Texture2D texture, out Rect window) {
+ private void RunJob(TextRenderJob job) {
+ Debug.Log($"Starting Text Rendering Job. queue depth = {_renderJobs.Count}, cache size = {_renderCache.Count}");
+ job.Start();
+
+ Texture2D texture = null;
+ if (job.OldText != null && _renderCache.TryGetValue(job.OldText, out var oldRender)) {
+ // old output still exists
+
+ oldRender.UserCount--;
+
+ if (oldRender.UserCount <= 0) {
+ // this is the only usage of this output, so we are free to re-render into the texture
+ Debug.Log("Render output is not shared with other users, so reusing texture and removing cache slot");
+
+ texture = oldRender.Texture;
+ _renderCache.Remove(job.OldText);
+ }
+ else {
+ // other things are using this render output, so decriment usercount, and we'll make a new entry instead
+ Debug.Log("Render output is shared with other users, so making new output");
+ }
+ }
+
+ // now that all old references are handled, begin rendering the new output
+
+ if (_renderCache.TryGetValue(job.NewText, out var cachedRender)) {
+ Debug.Log("Using Cached Render Output");
+ Debug.Log($"Finished Text Rendering Job. queue depth = {_renderJobs.Count}, cache size = {_renderCache.Count}");
+
+ cachedRender.UserCount++;
+ return;
+ }
+
+ var output = RenderText(job.NewText, texture);
+ _renderCache.Add(job.NewText, output);
+
+ job.Finish(output);
+ Debug.Log($"Finished Text Rendering Job. queue depth = {_renderJobs.Count}, cache size = {_renderCache.Count}");
+ }
+
+ public RenderedText RenderText(DecalText text, Texture2D texture) {
// SETUP TMP OBJECT FOR RENDERING
_tmp.text = text.FormattedText;
_tmp.font = text.Font.FontAsset;
@@ -108,20 +182,18 @@ namespace ConformalDecals.Text {
float sizeRatio = Mathf.Min(textureSize.x / size.x, textureSize.y, size.y);
// calculate where in the texture the used area actually is
- window = new Rect {
+ var window = new Rect {
size = size * sizeRatio,
center = (Vector2) textureSize / 2
};
- // GET TEXTURE
- if (_lastTexture != null) {
- texture = _lastTexture;
- texture.Resize(textureSize.x, textureSize.y, TextTextureFormat, false);
- _lastTexture = null;
- }
- else {
+ // SETUP TEXTURE
+ if (texture == null) {
texture = new Texture2D(textureSize.x, textureSize.y, TextTextureFormat, false);
}
+ else if (texture.width != textureSize.x || texture.height != textureSize.y || texture.format != TextTextureFormat) {
+ texture.Resize(textureSize.x, textureSize.y, TextTextureFormat, false);
+ }
// GENERATE PROJECTION MATRIX
var halfSize = (Vector2) textureSize / PixelDensity / 2 / sizeRatio;
@@ -147,6 +219,8 @@ namespace ConformalDecals.Text {
// RELEASE RENDERTEX
RenderTexture.ReleaseTemporary(renderTex);
+
+ return new RenderedText(texture, window);
}
}
}
\ No newline at end of file