mirror of
https://github.com/drewcassidy/KSP-Conformal-Decals.git
synced 2024-09-01 18:23:54 +00:00
Text rendering job system
This commit is contained in:
parent
1c776c0969
commit
42e6b18845
Binary file not shown.
@ -90,6 +90,7 @@
|
|||||||
<Compile Include="Test/TestLayers.cs" />
|
<Compile Include="Test/TestLayers.cs" />
|
||||||
<Compile Include="Text\DecalTextStyle.cs" />
|
<Compile Include="Text\DecalTextStyle.cs" />
|
||||||
<Compile Include="Text\RenderedText.cs" />
|
<Compile Include="Text\RenderedText.cs" />
|
||||||
|
<Compile Include="Text\TextRenderJob.cs" />
|
||||||
<Compile Include="UI/ColorPickerController.cs" />
|
<Compile Include="UI/ColorPickerController.cs" />
|
||||||
<Compile Include="UI/FontMenuController.cs" />
|
<Compile Include="UI/FontMenuController.cs" />
|
||||||
<Compile Include="UI/FontMenuItem.cs" />
|
<Compile Include="UI/FontMenuItem.cs" />
|
||||||
|
@ -27,8 +27,8 @@ namespace ConformalDecals {
|
|||||||
|
|
||||||
var decalText = new DecalText("Hello World!", _font, _style);
|
var decalText = new DecalText("Hello World!", _font, _style);
|
||||||
|
|
||||||
TextRenderer.Instance.RenderText(decalText, out var texture, out var window);
|
//TextRenderer.Instance.RenderText(decalText, out var texture, out var window);
|
||||||
materialProperties.AddOrGetTextureProperty("_Decal", true).Texture = texture;
|
//materialProperties.AddOrGetTextureProperty("_Decal", true).Texture = texture;
|
||||||
UpdateMaterials();
|
UpdateMaterials();
|
||||||
UpdateScale();
|
UpdateScale();
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace ConformalDecals.Text {
|
namespace ConformalDecals.Text {
|
||||||
public class RenderedText : ScriptableObject {
|
public class RenderedText {
|
||||||
public Texture2D Texture { get; private set; }
|
public Texture2D Texture { get; private set; }
|
||||||
|
|
||||||
public Rect Window { 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
42
Source/ConformalDecals/Text/TextRenderJob.cs
Normal file
42
Source/ConformalDecals/Text/TextRenderJob.cs
Normal file
@ -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<RenderedText> 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<RenderedText> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Rendering;
|
using UnityEngine.Events;
|
||||||
|
|
||||||
namespace ConformalDecals.Text {
|
namespace ConformalDecals.Text {
|
||||||
[KSPAddon(KSPAddon.Startup.Instantly, true)]
|
[KSPAddon(KSPAddon.Startup.Instantly, true)]
|
||||||
public class TextRenderer : MonoBehaviour {
|
public class TextRenderer : MonoBehaviour {
|
||||||
|
public const TextureFormat TextTextureFormat = TextureFormat.RG16;
|
||||||
|
public const RenderTextureFormat TextRenderTextureFormat = RenderTextureFormat.R8;
|
||||||
|
|
||||||
public static TextRenderer Instance {
|
public static TextRenderer Instance {
|
||||||
get {
|
get {
|
||||||
if (!_instance._isSetup) {
|
if (!_instance._isSetup) {
|
||||||
@ -18,8 +20,8 @@ namespace ConformalDecals.Text {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public const TextureFormat TextTextureFormat = TextureFormat.RG16;
|
[Serializable]
|
||||||
public const RenderTextureFormat TextRenderTextureFormat = RenderTextureFormat.R8;
|
public class TextRenderEvent : UnityEvent<RenderedText> { }
|
||||||
|
|
||||||
private const string BlitShader = "ConformalDecals/Text Blit";
|
private const string BlitShader = "ConformalDecals/Text Blit";
|
||||||
private const int MaxTextureSize = 4096;
|
private const int MaxTextureSize = 4096;
|
||||||
@ -32,8 +34,30 @@ namespace ConformalDecals.Text {
|
|||||||
private TextMeshPro _tmp;
|
private TextMeshPro _tmp;
|
||||||
private Material _blitMaterial;
|
private Material _blitMaterial;
|
||||||
|
|
||||||
private Dictionary<DecalText, RenderedText> _renderedTextures = new Dictionary<DecalText, RenderedText>();
|
private readonly Dictionary<DecalText, RenderedText> _renderCache = new Dictionary<DecalText, RenderedText>();
|
||||||
private Texture2D _lastTexture; // to reduce the number of Texture2D objects created and destroyed, keep the last one on hand
|
private readonly Queue<TextRenderJob> _renderJobs = new Queue<TextRenderJob>();
|
||||||
|
|
||||||
|
public TextRenderJob RenderText(DecalText text, UnityAction<RenderedText> renderFinishedCallback) {
|
||||||
|
var job = new TextRenderJob(text, renderFinishedCallback);
|
||||||
|
_renderJobs.Enqueue(job);
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextRenderJob UpdateText(DecalText oldText, DecalText newText, UnityAction<RenderedText> 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() {
|
private void Start() {
|
||||||
if (_instance != null) {
|
if (_instance != null) {
|
||||||
@ -45,6 +69,16 @@ namespace ConformalDecals.Text {
|
|||||||
DontDestroyOnLoad(gameObject);
|
DontDestroyOnLoad(gameObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Update() {
|
||||||
|
TextRenderJob nextJob;
|
||||||
|
do {
|
||||||
|
if (_renderJobs.Count <= 0) return;
|
||||||
|
nextJob = _renderJobs.Dequeue();
|
||||||
|
} while (nextJob.Needed);
|
||||||
|
|
||||||
|
RunJob(nextJob);
|
||||||
|
}
|
||||||
|
|
||||||
private void Setup() {
|
private void Setup() {
|
||||||
if (_isSetup) return;
|
if (_isSetup) return;
|
||||||
|
|
||||||
@ -60,7 +94,47 @@ namespace ConformalDecals.Text {
|
|||||||
_isSetup = true;
|
_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
|
// SETUP TMP OBJECT FOR RENDERING
|
||||||
_tmp.text = text.FormattedText;
|
_tmp.text = text.FormattedText;
|
||||||
_tmp.font = text.Font.FontAsset;
|
_tmp.font = text.Font.FontAsset;
|
||||||
@ -108,20 +182,18 @@ namespace ConformalDecals.Text {
|
|||||||
float sizeRatio = Mathf.Min(textureSize.x / size.x, textureSize.y, size.y);
|
float sizeRatio = Mathf.Min(textureSize.x / size.x, textureSize.y, size.y);
|
||||||
|
|
||||||
// calculate where in the texture the used area actually is
|
// calculate where in the texture the used area actually is
|
||||||
window = new Rect {
|
var window = new Rect {
|
||||||
size = size * sizeRatio,
|
size = size * sizeRatio,
|
||||||
center = (Vector2) textureSize / 2
|
center = (Vector2) textureSize / 2
|
||||||
};
|
};
|
||||||
|
|
||||||
// GET TEXTURE
|
// SETUP TEXTURE
|
||||||
if (_lastTexture != null) {
|
if (texture == null) {
|
||||||
texture = _lastTexture;
|
|
||||||
texture.Resize(textureSize.x, textureSize.y, TextTextureFormat, false);
|
|
||||||
_lastTexture = null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
texture = new Texture2D(textureSize.x, textureSize.y, TextTextureFormat, false);
|
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
|
// GENERATE PROJECTION MATRIX
|
||||||
var halfSize = (Vector2) textureSize / PixelDensity / 2 / sizeRatio;
|
var halfSize = (Vector2) textureSize / PixelDensity / 2 / sizeRatio;
|
||||||
@ -147,6 +219,8 @@ namespace ConformalDecals.Text {
|
|||||||
|
|
||||||
// RELEASE RENDERTEX
|
// RELEASE RENDERTEX
|
||||||
RenderTexture.ReleaseTemporary(renderTex);
|
RenderTexture.ReleaseTemporary(renderTex);
|
||||||
|
|
||||||
|
return new RenderedText(texture, window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user