From 6b7996fdd71d25c84f905dd17120b6524335e6aa Mon Sep 17 00:00:00 2001 From: drewcassidy Date: Sat, 19 Dec 2020 16:11:56 -0800 Subject: [PATCH] Text render simplification and small optimizations --- .../Plugins/ConformalDecals.dll | Bin 82432 -> 80896 bytes .../ConformalDecals/ModuleConformalDecal.cs | 45 +++-- Source/ConformalDecals/ModuleConformalText.cs | 4 +- Source/ConformalDecals/Text/TextRenderJob.cs | 35 ---- Source/ConformalDecals/Text/TextRenderer.cs | 158 +++++------------- changelog.txt | 2 + 6 files changed, 75 insertions(+), 169 deletions(-) delete mode 100644 Source/ConformalDecals/Text/TextRenderJob.cs diff --git a/GameData/ConformalDecals/Plugins/ConformalDecals.dll b/GameData/ConformalDecals/Plugins/ConformalDecals.dll index 9c632579cffea1cdeae81877f563bb56c52af25f..18c06f8ef0b0b7f8a3545cf165ed4b616124d307 100644 GIT binary patch delta 30031 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 diff --git a/Source/ConformalDecals/ModuleConformalDecal.cs b/Source/ConformalDecals/ModuleConformalDecal.cs index 64bc2a8..12e8875 100644 --- a/Source/ConformalDecals/ModuleConformalDecal.cs +++ b/Source/ConformalDecals/ModuleConformalDecal.cs @@ -89,8 +89,10 @@ namespace ConformalDecals { [KSPField] public Transform decalProjectorTransform; [KSPField] public Transform decalColliderTransform; - [KSPField] public Material backMaterial; - [KSPField] public Vector2 backTextureBaseScale; + [KSPField] public MeshRenderer boundsRenderer; + [KSPField] public MeshRenderer frontRenderer; + [KSPField] public Material backMaterial; + [KSPField] public Vector2 backTextureBaseScale; private const int DecalQueueMin = 2100; private const int DecalQueueMax = 2400; @@ -101,9 +103,8 @@ namespace ConformalDecals { private bool _isAttached; private Matrix4x4 _orthoMatrix; - private Material _decalMaterial; - private Material _previewMaterial; - private MeshRenderer _boundsRenderer; + private Material _decalMaterial; + private Material _previewMaterial; private int DecalQueue { get { @@ -132,24 +133,33 @@ namespace ConformalDecals { public override void OnLoad(ConfigNode node) { try { // SETUP TRANSFORMS - decalFrontTransform = part.FindModelTransform(decalFront); - if (decalFrontTransform == null) throw new FormatException($"Could not find decalFront transform: '{decalFront}'."); - - decalBackTransform = part.FindModelTransform(decalBack); - if (decalBackTransform == null) throw new FormatException($"Could not find decalBack transform: '{decalBack}'."); + // Projector transform, where the decal is projected from when attached + decalProjectorTransform = part.FindModelTransform(decalProjector); + if (decalProjectorTransform == null) throw new FormatException($"Could not find decalProjector transform: '{decalProjector}'."); + // Model transform, containing all visible elements of the decal when not attached decalModelTransform = part.FindModelTransform(decalModel); if (decalModelTransform == null) throw new FormatException($"Could not find decalModel transform: '{decalModel}'."); - decalProjectorTransform = part.FindModelTransform(decalProjector); - if (decalProjectorTransform == null) throw new FormatException($"Could not find decalProjector transform: '{decalProjector}'."); + // Front transform, shows a preview of the decal when unattached + decalFrontTransform = part.FindModelTransform(decalFront); + if (decalFrontTransform == null) throw new FormatException($"Could not find decalFront transform: '{decalFront}'."); + + frontRenderer = decalFrontTransform.GetComponent(); + // Collider transform, selectable area and shows where the decal is projecting onto decalColliderTransform = part.FindModelTransform(decalCollider); if (decalColliderTransform == null) throw new FormatException($"Could not find decalCollider transform: '{decalCollider}'."); - // SETUP BACK MATERIAL + boundsRenderer = decalColliderTransform.GetComponent(); + + // SETUP BACK if (updateBackScale) { + decalBackTransform = part.FindModelTransform(decalBack); + if (decalBackTransform == null) throw new FormatException($"Could not find decalBack transform: '{decalBack}'."); + var backRenderer = decalBackTransform.GetComponent(); + if (backRenderer == null) { this.LogError($"Specified decalBack transform {decalBack} has no renderer attached! Setting updateBackScale to false."); updateBackScale = false; @@ -244,7 +254,8 @@ namespace ConformalDecals { public override void OnStart(StartState state) { materialProperties.RenderQueue = DecalQueue; - _boundsRenderer = decalProjectorTransform.GetComponent(); + boundsRenderer = decalProjectorTransform.GetComponent(); + frontRenderer = decalFrontTransform.GetComponent(); // handle tweakables if (HighLogic.LoadedSceneIsEditor) { @@ -278,7 +289,7 @@ namespace ConformalDecals { if (!selectableInFlight || !DecalConfig.SelectableInFlight) { decalColliderTransform.GetComponent().enabled = false; - _boundsRenderer.enabled = false; + boundsRenderer.enabled = false; } } } @@ -440,7 +451,7 @@ namespace ConformalDecals { // update projection foreach (var target in _targets) { - target.Project(_orthoMatrix, decalProjectorTransform, _boundsRenderer.bounds, useBaseNormal); + target.Project(_orthoMatrix, decalProjectorTransform, boundsRenderer.bounds, useBaseNormal); } } else { @@ -465,7 +476,7 @@ namespace ConformalDecals { _decalMaterial = materialProperties.DecalMaterial; _previewMaterial = materialProperties.PreviewMaterial; - if (!_isAttached) decalFrontTransform.GetComponent().material = _previewMaterial; + if (!_isAttached) frontRenderer.material = _previewMaterial; } protected void UpdateTargets() { diff --git a/Source/ConformalDecals/ModuleConformalText.cs b/Source/ConformalDecals/ModuleConformalText.cs index 0d6d027..c5db737 100644 --- a/Source/ConformalDecals/ModuleConformalText.cs +++ b/Source/ConformalDecals/ModuleConformalText.cs @@ -89,7 +89,6 @@ namespace ConformalDecals { private MaterialColorProperty _outlineColorProperty; private MaterialFloatProperty _outlineWidthProperty; - private TextRenderJob _currentJob; private DecalText _currentText; public override void OnLoad(ConfigNode node) { @@ -253,7 +252,6 @@ namespace ConformalDecals { decal.charSpacing = charSpacing; decal.lineSpacing = lineSpacing; - decal._currentJob = _currentJob; decal._currentText = _currentText; decal.UpdateText(); } @@ -267,7 +265,7 @@ namespace ConformalDecals { private void UpdateText() { // Render text var newText = new DecalText(text, font, style, vertical, lineSpacing, charSpacing); - var output = TextRenderer.UpdateTextNow(_currentText, newText); + var output = TextRenderer.UpdateText(_currentText, newText); _currentText = newText; UpdateTexture(output); diff --git a/Source/ConformalDecals/Text/TextRenderJob.cs b/Source/ConformalDecals/Text/TextRenderJob.cs deleted file mode 100644 index 475de9b..0000000 --- a/Source/ConformalDecals/Text/TextRenderJob.cs +++ /dev/null @@ -1,35 +0,0 @@ -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; - 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(TextRenderOutput 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 7a527ea..a807873 100644 --- a/Source/ConformalDecals/Text/TextRenderer.cs +++ b/Source/ConformalDecals/Text/TextRenderer.cs @@ -3,16 +3,12 @@ using System.Collections.Generic; using ConformalDecals.Util; using TMPro; using UnityEngine; -using UnityEngine.Events; using UnityEngine.Rendering; +using Object = UnityEngine.Object; namespace ConformalDecals.Text { - // TODO: Testing shows the job system is unnecessary, so remove job system code. - /// Class handing text rendering. - /// Is a singleton referencing a single gameobject in the scene which contains the TextMeshPro component - [KSPAddon(KSPAddon.Startup.Instantly, true)] - public class TextRenderer : MonoBehaviour { + public static class TextRenderer { /// Texture format used for returned textures. /// Unfortunately due to how Unity textures work, this cannot be R8 or Alpha8, /// so theres always a superfluous green channel using memory @@ -22,142 +18,55 @@ namespace ConformalDecals.Text { /// Overriden below to be ARGB32 on DirectX because DirectX is dumb public static RenderTextureFormat textRenderTextureFormat = RenderTextureFormat.R8; - /// The text renderer object within the scene which contains the TextMeshPro component used for rendering. - public static TextRenderer Instance { - get { - if (!_instance._isSetup) { - _instance.Setup(); - } - - return _instance; - } - } - - /// Text Render unityevent, used with the job system to signal render completion - [Serializable] - public class TextRenderEvent : UnityEvent { } - private const string ShaderName = "ConformalDecals/Text Blit"; private const int MaxTextureSize = 4096; private const float FontSize = 100; private const float PixelDensity = 5; - private static TextRenderer _instance; - - private bool _isSetup; - private Shader _blitShader; + private static Shader _blitShader; + private static Texture2D _blankTexture; private static readonly Dictionary RenderCache = new Dictionary(); - private static readonly Queue RenderJobs = new Queue(); - private static Texture2D _blankTexture; - - /// Update text using the job queue - public static TextRenderJob UpdateText(DecalText oldText, DecalText newText, UnityAction renderFinishedCallback) { + /// Update text immediately without using job queue + public static TextRenderOutput UpdateText(DecalText oldText, DecalText newText) { if (newText == null) throw new ArgumentNullException(nameof(newText)); + Logging.Log($"Rendering text {newText}"); - var job = new TextRenderJob(oldText, newText, renderFinishedCallback); - RenderJobs.Enqueue(job); - return job; - } + if (!(oldText is null)) UnregisterText(oldText); - /// Update text immediately without using job queue - public static TextRenderOutput UpdateTextNow(DecalText oldText, DecalText newText) { - if (newText == null) throw new ArgumentNullException(nameof(newText)); + // now that all old references are handled, begin rendering the new output + if (!RenderCache.TryGetValue(newText, out var renderOutput)) { + renderOutput = RenderText(newText); + RenderCache.Add(newText, renderOutput); + } - return Instance.RunJob(new TextRenderJob(oldText, newText, null), out _); + renderOutput.UserCount++; + return renderOutput; } /// Unregister a user of a piece of text public static void UnregisterText(DecalText text) { if (text == null) throw new ArgumentNullException(nameof(text)); - + if (RenderCache.TryGetValue(text, out var renderedText)) { renderedText.UserCount--; if (renderedText.UserCount <= 0) { RenderCache.Remove(text); var texture = renderedText.Texture; - if (texture != _blankTexture) Destroy(texture); + if (texture != _blankTexture) Object.Destroy(texture); } } } - private void Start() { - if (_instance != null) { - Logging.LogError("Duplicate TextRenderer created???"); - } - - Logging.Log("Creating TextRenderer Object"); - _instance = this; - DontDestroyOnLoad(gameObject); - if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D12) { - textRenderTextureFormat = RenderTextureFormat.ARGB32; // DirectX is dumb - } - - if (!SystemInfo.SupportsTextureFormat(textTextureFormat)) { - Logging.LogError($"Text texture format {textTextureFormat} not supported on this platform."); - } - - if (!SystemInfo.SupportsRenderTextureFormat(textRenderTextureFormat)) { - Logging.LogError($"Text texture format {textRenderTextureFormat} not supported on this platform."); - } - - _blankTexture = Texture2D.blackTexture; - } - - /// Setup this text renderer instance for rendering - private void Setup() { - if (_isSetup) return; - - Logging.Log("Setting Up TextRenderer Object"); - - // _tmp = gameObject.AddComponent(); - // _tmp.renderer.enabled = false; // dont automatically render - - _blitShader = Shabby.Shabby.FindShader(ShaderName); - if (_blitShader == null) Logging.LogError($"Could not find text blit shader named '{ShaderName}'"); - - _isSetup = true; - } - - /// Run a text render job - private TextRenderOutput RunJob(TextRenderJob job, out bool renderNeeded) { - if (!job.Needed) { - renderNeeded = false; - return null; - } - - Logging.Log($"Rendering text {job.NewText}"); - - job.Start(); - if (!(job.OldText is null)) UnregisterText(job.OldText); - - // now that all old references are handled, begin rendering the new output - - if (RenderCache.TryGetValue(job.NewText, out var renderOutput)) { - renderNeeded = false; - } - else { - renderNeeded = true; - - renderOutput = RenderText(job.NewText); - RenderCache.Add(job.NewText, renderOutput); - } - - renderOutput.UserCount++; - - job.Finish(renderOutput); - return renderOutput; - } /// Render a piece of text to a given texture - public TextRenderOutput RenderText(DecalText text) { + public static TextRenderOutput RenderText(DecalText text) { + if (text == null) throw new ArgumentNullException(nameof(text)); + var tmpObject = new GameObject("Text Mesh Pro renderer"); var tmp = tmpObject.AddComponent(); - if (text == null) throw new ArgumentNullException(nameof(text)); - if (tmp == null) throw new InvalidOperationException("TextMeshPro object not yet created."); - // SETUP TMP OBJECT FOR RENDERING tmp.text = text.FormattedText; tmp.font = text.Font.FontAsset; @@ -189,7 +98,7 @@ namespace ConformalDecals.Text { meshes[i] = meshFilters[i].mesh; if (i == 0) meshes[i] = tmp.mesh; - materials[i] = Instantiate(renderer.material); + materials[i] = Object.Instantiate(renderer.material); materials[i].shader = _blitShader; if (renderer == null) throw new FormatException($"Object {meshFilters[i].gameObject.name} has filter but no renderer"); @@ -215,6 +124,7 @@ namespace ConformalDecals.Text { if (textureSize.x == 0 || textureSize.y == 0) { Logging.LogError("No text present or error in texture size calculation. Aborting."); + Object.Destroy(tmpObject); return new TextRenderOutput(_blankTexture, Rect.zero); } @@ -276,10 +186,30 @@ namespace ConformalDecals.Text { // RELEASE RENDERTEX RenderTexture.ReleaseTemporary(renderTex); - // CLEAR SUBMESHES - Destroy(tmpObject); + // DESTROY THE RENDERER OBJECT + Object.Destroy(tmpObject); return new TextRenderOutput(texture, window); } + + /// Setup shader and texture + public static void ModuleManagerPostLoad() { + if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D12) { + textRenderTextureFormat = RenderTextureFormat.ARGB32; // DirectX is dumb + } + + if (!SystemInfo.SupportsTextureFormat(textTextureFormat)) { + Logging.LogError($"Text texture format {textTextureFormat} not supported on this platform."); + } + + if (!SystemInfo.SupportsRenderTextureFormat(textRenderTextureFormat)) { + Logging.LogError($"Text texture format {textRenderTextureFormat} not supported on this platform."); + } + + _blankTexture = Texture2D.blackTexture; + _blitShader = Shabby.Shabby.FindShader(ShaderName); + if (_blitShader == null) Logging.LogError($"Could not find text blit shader named '{ShaderName}'"); + } + } } \ No newline at end of file diff --git a/changelog.txt b/changelog.txt index b9077f1..a53a127 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,8 @@ v0.2.7 - Fixes: - Fixed certain non-ascii strings not rendering correctly under certain circumstances. - Yet another attempted fix for the planet text glitch. +- Changes: + - Small optimizations and simplified text rendering. v0.2.6 ------