From 5cb900d772c380bf6a97c28dbdc828bace052747 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 12 Jun 2024 21:04:41 -0400 Subject: [PATCH 01/11] what if coroutine --- NewHorizons/Utility/Files/ImageUtilities.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Utility/Files/ImageUtilities.cs b/NewHorizons/Utility/Files/ImageUtilities.cs index ea0ceaf6..ed8b348c 100644 --- a/NewHorizons/Utility/Files/ImageUtilities.cs +++ b/NewHorizons/Utility/Files/ImageUtilities.cs @@ -450,10 +450,7 @@ namespace NewHorizons.Utility.Files void Start() { imageLoadedEvent.AddListener(OnImageLoaded); - foreach (var (index, path) in PathsToLoad) - { - StartCoroutine(DownloadTexture(path, index)); - } + StartCoroutine(DownloadTextures()); } private void OnImageLoaded(Texture texture, int index) @@ -470,6 +467,15 @@ namespace NewHorizons.Utility.Files } } + IEnumerator DownloadTextures() + { + foreach (var (index, path) in PathsToLoad) + { + yield return StartCoroutine(DownloadTexture(path, index)); + yield return new WaitForSeconds(0.5f); + } + } + IEnumerator DownloadTexture(string url, int index) { var key = GetKey(url); From 69fcc2b462e3c1043214a588b622703c8eea96eb Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 12 Jun 2024 22:00:11 -0400 Subject: [PATCH 02/11] im fast as fuck boi --- NewHorizons/Utility/Files/ImageUtilities.cs | 45 +++++++++------------ 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/NewHorizons/Utility/Files/ImageUtilities.cs b/NewHorizons/Utility/Files/ImageUtilities.cs index ed8b348c..13949619 100644 --- a/NewHorizons/Utility/Files/ImageUtilities.cs +++ b/NewHorizons/Utility/Files/ImageUtilities.cs @@ -6,8 +6,10 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using UnityEngine; using UnityEngine.Events; +using UnityEngine.InputSystem; using UnityEngine.Networking; namespace NewHorizons.Utility.Files @@ -472,21 +474,17 @@ namespace NewHorizons.Utility.Files foreach (var (index, path) in PathsToLoad) { yield return StartCoroutine(DownloadTexture(path, index)); - yield return new WaitForSeconds(0.5f); } } IEnumerator DownloadTexture(string url, int index) { var key = GetKey(url); - lock (_textureCache) + if (_textureCache.TryGetValue(key, out var existingTexture)) { - if (_textureCache.TryGetValue(key, out var existingTexture)) - { - NHLogger.LogVerbose($"Already loaded image {index}:{url}"); - imageLoadedEvent?.Invoke((Texture2D)existingTexture, index); - yield break; - } + NHLogger.LogVerbose($"Already loaded image {index}:{url}"); + imageLoadedEvent?.Invoke((Texture2D)existingTexture, index); + yield break; } using UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url); @@ -501,28 +499,23 @@ namespace NewHorizons.Utility.Files } else { - var texture = new Texture2D(2, 2, TextureFormat.RGBA32, false); - texture.name = key; - texture.wrapMode = TextureWrapMode.Clamp; - var handler = (DownloadHandlerTexture)uwr.downloadHandler; - texture.LoadImage(handler.data); - lock (_textureCache) + var texture = DownloadHandlerTexture.GetContent(uwr); + + if (_textureCache.TryGetValue(key, out existingTexture)) { - if (_textureCache.TryGetValue(key, out var existingTexture)) - { - NHLogger.LogVerbose($"Already loaded image {index}:{url}"); - Destroy(texture); - texture = (Texture2D)existingTexture; - } - else - { - _textureCache.Add(key, texture); - } - - imageLoadedEvent?.Invoke(texture, index); + NHLogger.LogVerbose($"Already loaded image {index}:{url}"); + Destroy(texture); + texture = (Texture2D)existingTexture; } + else + { + _textureCache.Add(key, texture); + } + + yield return null; + imageLoadedEvent?.Invoke(texture, index); } } } From 493365155fdc6a654e91af80b9a7ebedf1be4bb9 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 12 Jun 2024 22:05:49 -0400 Subject: [PATCH 03/11] the guys --- NewHorizons/Utility/Files/ImageUtilities.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NewHorizons/Utility/Files/ImageUtilities.cs b/NewHorizons/Utility/Files/ImageUtilities.cs index 13949619..69ed30d1 100644 --- a/NewHorizons/Utility/Files/ImageUtilities.cs +++ b/NewHorizons/Utility/Files/ImageUtilities.cs @@ -502,6 +502,8 @@ namespace NewHorizons.Utility.Files var handler = (DownloadHandlerTexture)uwr.downloadHandler; var texture = DownloadHandlerTexture.GetContent(uwr); + texture.name = key; + texture.wrapMode = TextureWrapMode.Clamp; if (_textureCache.TryGetValue(key, out existingTexture)) { From 540a501b9a2e0f2a05012e1279d4f266855946a2 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 13 Jun 2024 01:13:25 -0400 Subject: [PATCH 04/11] Cache inverted images --- .../Builder/Props/ProjectionBuilder.cs | 39 +++++++-- NewHorizons/Utility/Files/ImageUtilities.cs | 86 ++++++++++++++++--- 2 files changed, 106 insertions(+), 19 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index ed9d136a..9c06023a 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -150,10 +150,12 @@ namespace NewHorizons.Builder.Props // to avoid doing a "is every element in the array `textures` not null" check every time a texture finishes loading int displaySlidesLoaded = 0; imageLoader.imageLoadedEvent.AddListener( - (Texture2D tex, int index) => + (Texture2D tex, int index, string originalPath) => { - slideCollection.slides[index]._image = ImageUtilities.Invert(tex); + var time = DateTime.Now; + slideCollection.slides[index]._image = ImageUtilities.Invert(mod, tex, originalPath); + NHLogger.LogVerbose($"Slide reel make reel invert texture {(DateTime.Now - time).TotalMilliseconds}ms"); // Track the first 15 to put on the slide reel object if (index < textures.Length) { @@ -174,6 +176,8 @@ namespace NewHorizons.Builder.Props slidesFront.material.name = reelTexture.name; } } + + NHLogger.LogVerbose($"Slide reel make reel texture {(DateTime.Now - time).TotalMilliseconds}ms"); } ); @@ -301,7 +305,12 @@ namespace NewHorizons.Builder.Props slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null var imageLoader = AddAsyncLoader(projectorObj, mod, info.slides, ref slideCollection); - imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index) => { slideCollection.slides[index]._image = ImageUtilities.Invert(tex); }); + imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) => + { + var time = DateTime.Now; + slideCollection.slides[index]._image = ImageUtilities.Invert(mod, tex, originalPath); + NHLogger.LogVerbose($"Slide reel invert time {(DateTime.Now - time).TotalMilliseconds}ms"); + }); slideCollectionContainer.slideCollection = slideCollection; @@ -340,7 +349,12 @@ namespace NewHorizons.Builder.Props slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null var imageLoader = AddAsyncLoader(g, mod, info.slides, ref slideCollection); - imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index) => { slideCollection.slides[index]._image = tex; }); + imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) => + { + var time = DateTime.Now; + slideCollection.slides[index]._image = tex; + NHLogger.LogVerbose($"Slide reel set time {(DateTime.Now - time).TotalMilliseconds}ms"); + }); // attach a component to store all the data for the slides that play when a vision torch scans this target var target = g.AddComponent(); @@ -395,8 +409,9 @@ namespace NewHorizons.Builder.Props // to avoid doing a "is every element in the array `slideCollection.slides` not null" check every time a texture finishes loading int displaySlidesLoaded = 0; imageLoader.imageLoadedEvent.AddListener( - (Texture2D tex, int index) => - { + (Texture2D tex, int index, string originalPath) => + { + var time = DateTime.Now; slideCollection.slides[index]._image = tex; if (Interlocked.Increment(ref displaySlidesLoaded) == slides.Length) @@ -404,6 +419,7 @@ namespace NewHorizons.Builder.Props mindSlideProjector.enabled = true; visionBeamEffect.SetActive(true); } + NHLogger.LogVerbose($"Slide reel another set time {(DateTime.Now - time).TotalMilliseconds}ms"); } ); @@ -429,6 +445,7 @@ namespace NewHorizons.Builder.Props private static ImageUtilities.AsyncImageLoader AddAsyncLoader(GameObject gameObject, IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection) { + var invertedImageLoader = gameObject.AddComponent(); var imageLoader = gameObject.AddComponent(); for (int i = 0; i < slides.Length; i++) { @@ -438,10 +455,16 @@ namespace NewHorizons.Builder.Props if (string.IsNullOrEmpty(slideInfo.imagePath)) { - imageLoader.imageLoadedEvent?.Invoke(Texture2D.blackTexture, i); + imageLoader.imageLoadedEvent?.Invoke(Texture2D.blackTexture, i, null); } else { + invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, "invertedSlideReelCache", slideInfo.imagePath))); + invertedImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) => + { + var path = "\\" + Path.Combine(Path.GetFileName(Path.GetDirectoryName(mod.ModHelper.Manifest.ModFolderPath)), slides[i].imagePath) + " > invert"; + ImageUtilities.TrackCachedTexture(path, t); + }); imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath))); } @@ -449,6 +472,8 @@ namespace NewHorizons.Builder.Props slideCollection.slides[i] = slide; } + invertedImageLoader.Start(); + imageLoader.Start(); return imageLoader; } diff --git a/NewHorizons/Utility/Files/ImageUtilities.cs b/NewHorizons/Utility/Files/ImageUtilities.cs index 69ed30d1..bc795ebb 100644 --- a/NewHorizons/Utility/Files/ImageUtilities.cs +++ b/NewHorizons/Utility/Files/ImageUtilities.cs @@ -1,6 +1,7 @@ using HarmonyLib; using NewHorizons.Utility.OWML; using OWML.Common; +using OWML.ModHelper; using System; using System.Collections; using System.Collections.Generic; @@ -19,14 +20,14 @@ namespace NewHorizons.Utility.Files // key is path + applied effects private static readonly Dictionary _textureCache = new(); public static bool CheckCachedTexture(string key, out Texture existingTexture) => _textureCache.TryGetValue(key, out existingTexture); - public static void TrackCachedTexture(string key, Texture texture) => _textureCache.Add(key, texture); + public static void TrackCachedTexture(string key, Texture texture) => _textureCache[key] = texture; - private static string GetKey(string path) => path.Substring(Main.Instance.ModHelper.OwmlConfig.ModsPath.Length); + public static string GetKey(string path) => path.Substring(Main.Instance.ModHelper.OwmlConfig.ModsPath.Length); public static bool IsTextureLoaded(IModBehaviour mod, string filename) { var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, filename); - var key = GetKey(path); + var key = GetKey(path).Replace('\\', '/'); return _textureCache.ContainsKey(key); } @@ -101,7 +102,7 @@ namespace NewHorizons.Utility.Files /// used specifically for projected slides. /// also adds a border (to prevent weird visual bug) and makes the texture linear (otherwise the projected image is too bright). /// - public static Texture2D Invert(Texture2D texture) + public static Texture2D Invert(IModBehaviour mod, Texture2D texture, string originalPath) { var key = $"{texture.name} > invert"; if (_textureCache.TryGetValue(key, out var existingTexture)) return (Texture2D)existingTexture; @@ -137,6 +138,14 @@ namespace NewHorizons.Utility.Files _textureCache.Add(key, newTexture); + if (!string.IsNullOrEmpty(originalPath)) + { + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, "invertedSlideReelCache", originalPath.Replace(mod.ModHelper.Manifest.ModFolderPath, "")); + NHLogger.LogVerbose($"Caching inverted image to {path}"); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + File.WriteAllBytes(path, newTexture.EncodeToPNG()); + } + return newTexture; } @@ -432,12 +441,49 @@ namespace NewHorizons.Utility.Files return sprite; } + public class SingletonAsyncImageLoader : MonoBehaviour + { + public static SingletonAsyncImageLoader Instance { get; private set; } + + private Queue loaders = new(); + + private bool _isLoading; + + public void Awake() + { + Instance = this; + } + + public void Load(AsyncImageLoader loader) + { + loaders.Enqueue(loader); + if (!_isLoading) + { + StartCoroutine(Run()); + } + } + + private IEnumerator Run() + { + NHLogger.Log("Loading slide reels"); + _isLoading = true; + while (loaders.Count > 0) + { + var loader = loaders.Dequeue(); + yield return loader.DownloadTextures(); + NHLogger.Log($"Finished a slide reel, {loaders.Count} left"); + } + _isLoading = false; + NHLogger.Log("Done loading slide reels"); + } + } + // Modified from https://stackoverflow.com/a/69141085/9643841 public class AsyncImageLoader : MonoBehaviour { public List<(int index, string path)> PathsToLoad { get; private set; } = new(); - public class ImageLoadedEvent : UnityEvent { } + public class ImageLoadedEvent : UnityEvent { } public ImageLoadedEvent imageLoadedEvent = new(); private readonly object _lockObj = new(); @@ -449,13 +495,25 @@ namespace NewHorizons.Utility.Files // and make use of that for at least for projector stuff (require player to be in the same sector as the slides // for them to start loading, and unload when the player leaves) - void Start() + private bool _started; + + public void Start() { + if (_started) return; + + _started = true; + + if (SingletonAsyncImageLoader.Instance == null) + { + Main.Instance.gameObject.AddComponent(); + } + + NHLogger.LogVerbose("Loading new slide reel"); imageLoadedEvent.AddListener(OnImageLoaded); - StartCoroutine(DownloadTextures()); + SingletonAsyncImageLoader.Instance.Load(this); } - private void OnImageLoaded(Texture texture, int index) + private void OnImageLoaded(Texture texture, int index, string originalPath) { lock (_lockObj) { @@ -469,11 +527,13 @@ namespace NewHorizons.Utility.Files } } - IEnumerator DownloadTextures() + internal IEnumerator DownloadTextures() { foreach (var (index, path) in PathsToLoad) { - yield return StartCoroutine(DownloadTexture(path, index)); + NHLogger.Log($"Loaded slide reel {index} of {PathsToLoad.Count}"); + + yield return DownloadTexture(path, index); } } @@ -483,7 +543,7 @@ namespace NewHorizons.Utility.Files if (_textureCache.TryGetValue(key, out var existingTexture)) { NHLogger.LogVerbose($"Already loaded image {index}:{url}"); - imageLoadedEvent?.Invoke((Texture2D)existingTexture, index); + imageLoadedEvent?.Invoke((Texture2D)existingTexture, index, url); yield break; } @@ -517,7 +577,9 @@ namespace NewHorizons.Utility.Files } yield return null; - imageLoadedEvent?.Invoke(texture, index); + var time = DateTime.Now; + imageLoadedEvent?.Invoke(texture, index, url); + NHLogger.LogVerbose($"Slide reel event? {(DateTime.Now - time).TotalMilliseconds}ms"); } } } From 31ada350b49879298f77cc80c23f4d770732a86e Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 13 Jun 2024 13:07:51 -0400 Subject: [PATCH 05/11] Cache atlas, works first loop --- .../Builder/Props/ProjectionBuilder.cs | 43 ++++- NewHorizons/Utility/Files/ImageUtilities.cs | 172 ++---------------- .../Files/SlideReelAsyncImageLoader.cs | 162 +++++++++++++++++ 3 files changed, 212 insertions(+), 165 deletions(-) create mode 100644 NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 9c06023a..014c5d0b 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -11,11 +11,18 @@ using System.IO; using System.Threading; using UnityEngine; using static NewHorizons.Main; +using System.Linq; +using HarmonyLib; +using UnityEngine.InputSystem; +using Epic.OnlineServices.Presence; namespace NewHorizons.Builder.Props { public static class ProjectionBuilder { + public const string INVERTED_SLIDE_CACHE_FOLDER = "SlideReelCache/Inverted"; + public const string ATLAS_SLIDE_CACHE_FOLDER = "SlideReelCache/Atlas"; + public static GameObject SlideReelWholePrefab { get; private set; } public static GameObject SlideReelWholePristinePrefab { get; private set; } public static GameObject SlideReelWholeRustedPrefab { get; private set; } @@ -113,6 +120,8 @@ namespace NewHorizons.Builder.Props } } + public static string GetUniqueSlideReelID(IModBehaviour mod, SlideInfo[] slides) => $"{mod.ModHelper.Manifest.UniqueName}{slides.Join(x => x.imagePath)}".GetHashCode().ToString(); + private static GameObject MakeSlideReel(GameObject planetGO, Sector sector, ProjectionInfo info, IModBehaviour mod) { InitPrefabs(); @@ -144,6 +153,8 @@ namespace NewHorizons.Builder.Props var imageLoader = AddAsyncLoader(slideReelObj, mod, info.slides, ref slideCollection); + var key = GetUniqueSlideReelID(mod, info.slides); + // this variable just lets us track how many of the first 15 slides have been loaded. // this way as soon as the last one is loaded (due to async loading, this may be // slide 7, or slide 3, or whatever), we can build the slide reel texture. This allows us @@ -154,7 +165,7 @@ namespace NewHorizons.Builder.Props { var time = DateTime.Now; - slideCollection.slides[index]._image = ImageUtilities.Invert(mod, tex, originalPath); + slideCollection.slides[index]._image = ImageUtilities.InvertSlideReel(mod, tex, originalPath); NHLogger.LogVerbose($"Slide reel make reel invert texture {(DateTime.Now - time).TotalMilliseconds}ms"); // Track the first 15 to put on the slide reel object if (index < textures.Length) @@ -167,7 +178,7 @@ namespace NewHorizons.Builder.Props var slidesFront = slideReelObj.GetComponentInChildren().transform.Find("Slides_Front").GetComponent(); // Now put together the textures into a 4x4 thing for the materials - var reelTexture = ImageUtilities.MakeReelTexture(textures); + var reelTexture = ImageUtilities.MakeReelTexture(mod, textures, key); slidesBack.material.mainTexture = reelTexture; slidesBack.material.SetTexture(EmissionMap, reelTexture); slidesBack.material.name = reelTexture.name; @@ -308,7 +319,7 @@ namespace NewHorizons.Builder.Props imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) => { var time = DateTime.Now; - slideCollection.slides[index]._image = ImageUtilities.Invert(mod, tex, originalPath); + slideCollection.slides[index]._image = ImageUtilities.InvertSlideReel(mod, tex, originalPath); NHLogger.LogVerbose($"Slide reel invert time {(DateTime.Now - time).TotalMilliseconds}ms"); }); @@ -443,10 +454,21 @@ namespace NewHorizons.Builder.Props return standingTorch; } - private static ImageUtilities.AsyncImageLoader AddAsyncLoader(GameObject gameObject, IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection) + private static SlideReelAsyncImageLoader AddAsyncLoader(GameObject gameObject, IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection) { - var invertedImageLoader = gameObject.AddComponent(); - var imageLoader = gameObject.AddComponent(); + var invertedImageLoader = new SlideReelAsyncImageLoader(); + var atlasImageLoader = new SlideReelAsyncImageLoader(); + var imageLoader = new SlideReelAsyncImageLoader(); + + var textureNames = slides.Select(x => Path.GetFileNameWithoutExtension(x.imagePath)).ToArray(); + var atlasKey = GetUniqueSlideReelID(mod, slides); + + atlasImageLoader.PathsToLoad.Add((0, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER, $"{atlasKey}.png"))); + atlasImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) => + { + NHLogger.Log($"SLIDE REEL ATLAS from {slides.First().imagePath}: {s}"); + ImageUtilities.TrackCachedTexture(atlasKey, t); + }); for (int i = 0; i < slides.Length; i++) { var slide = new Slide(); @@ -459,7 +481,7 @@ namespace NewHorizons.Builder.Props } else { - invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, "invertedSlideReelCache", slideInfo.imagePath))); + invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, INVERTED_SLIDE_CACHE_FOLDER, slideInfo.imagePath))); invertedImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) => { var path = "\\" + Path.Combine(Path.GetFileName(Path.GetDirectoryName(mod.ModHelper.Manifest.ModFolderPath)), slides[i].imagePath) + " > invert"; @@ -472,8 +494,11 @@ namespace NewHorizons.Builder.Props slideCollection.slides[i] = slide; } - invertedImageLoader.Start(); - imageLoader.Start(); + // Loaders go sequentually - Load the inverted textures to the cache so that ImageUtilities will reuse them later + invertedImageLoader.Start(true); + // Atlas texture next so that the normal iamgeLoader knows not to regenerate them unless they were missing + atlasImageLoader.Start(false); + imageLoader.Start(true); return imageLoader; } diff --git a/NewHorizons/Utility/Files/ImageUtilities.cs b/NewHorizons/Utility/Files/ImageUtilities.cs index bc795ebb..bf7cebea 100644 --- a/NewHorizons/Utility/Files/ImageUtilities.cs +++ b/NewHorizons/Utility/Files/ImageUtilities.cs @@ -1,17 +1,12 @@ using HarmonyLib; +using NewHorizons.Builder.Props; using NewHorizons.Utility.OWML; using OWML.Common; -using OWML.ModHelper; using System; -using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading.Tasks; using UnityEngine; -using UnityEngine.Events; -using UnityEngine.InputSystem; -using UnityEngine.Networking; namespace NewHorizons.Utility.Files { @@ -102,7 +97,7 @@ namespace NewHorizons.Utility.Files /// used specifically for projected slides. /// also adds a border (to prevent weird visual bug) and makes the texture linear (otherwise the projected image is too bright). /// - public static Texture2D Invert(IModBehaviour mod, Texture2D texture, string originalPath) + public static Texture2D InvertSlideReel(IModBehaviour mod, Texture2D texture, string originalPath) { var key = $"{texture.name} > invert"; if (_textureCache.TryGetValue(key, out var existingTexture)) return (Texture2D)existingTexture; @@ -138,9 +133,11 @@ namespace NewHorizons.Utility.Files _textureCache.Add(key, newTexture); + // Since doing this is expensive we cache the results to the disk + // Preloading cached values is done in ProjectionBuilder if (!string.IsNullOrEmpty(originalPath)) { - var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, "invertedSlideReelCache", originalPath.Replace(mod.ModHelper.Manifest.ModFolderPath, "")); + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.INVERTED_SLIDE_CACHE_FOLDER, originalPath.Replace(mod.ModHelper.Manifest.ModFolderPath, "")); NHLogger.LogVerbose($"Caching inverted image to {path}"); Directory.CreateDirectory(Path.GetDirectoryName(path)); File.WriteAllBytes(path, newTexture.EncodeToPNG()); @@ -149,15 +146,14 @@ namespace NewHorizons.Utility.Files return newTexture; } - public static Texture2D MakeReelTexture(Texture2D[] textures) + public static Texture2D MakeReelTexture(IModBehaviour mod, Texture2D[] textures, string uniqueSlideReelID) { - var key = $"SlideReelAtlas of {textures.Join(x => x.name)}"; - if (_textureCache.TryGetValue(key, out var existingTexture)) return (Texture2D)existingTexture; + if (_textureCache.TryGetValue(uniqueSlideReelID, out var existingTexture)) return (Texture2D)existingTexture; var size = 256; var texture = new Texture2D(size * 4, size * 4, TextureFormat.ARGB32, false); - texture.name = key; + texture.name = uniqueSlideReelID; var fillPixels = new Color[size * size * 4 * 4]; for (int xIndex = 0; xIndex < 4; xIndex++) @@ -199,7 +195,14 @@ namespace NewHorizons.Utility.Files texture.SetPixels(fillPixels); texture.Apply(); - _textureCache.Add(key, texture); + _textureCache.Add(uniqueSlideReelID, texture); + + // Since doing this is expensive we cache the results to the disk + // Preloading cached values is done in ProjectionBuilder + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.ATLAS_SLIDE_CACHE_FOLDER, $"{uniqueSlideReelID}.png"); + NHLogger.LogVerbose($"Caching atlas image to {path}"); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + File.WriteAllBytes(path, texture.EncodeToPNG()); return texture; } @@ -440,148 +443,5 @@ namespace NewHorizons.Utility.Files sprite.name = texture.name; return sprite; } - - public class SingletonAsyncImageLoader : MonoBehaviour - { - public static SingletonAsyncImageLoader Instance { get; private set; } - - private Queue loaders = new(); - - private bool _isLoading; - - public void Awake() - { - Instance = this; - } - - public void Load(AsyncImageLoader loader) - { - loaders.Enqueue(loader); - if (!_isLoading) - { - StartCoroutine(Run()); - } - } - - private IEnumerator Run() - { - NHLogger.Log("Loading slide reels"); - _isLoading = true; - while (loaders.Count > 0) - { - var loader = loaders.Dequeue(); - yield return loader.DownloadTextures(); - NHLogger.Log($"Finished a slide reel, {loaders.Count} left"); - } - _isLoading = false; - NHLogger.Log("Done loading slide reels"); - } - } - - // Modified from https://stackoverflow.com/a/69141085/9643841 - public class AsyncImageLoader : MonoBehaviour - { - public List<(int index, string path)> PathsToLoad { get; private set; } = new(); - - public class ImageLoadedEvent : UnityEvent { } - public ImageLoadedEvent imageLoadedEvent = new(); - - private readonly object _lockObj = new(); - - public bool FinishedLoading { get; private set; } - private int _loadedCount = 0; - - // TODO: set up an optional “StartLoading” and “StartUnloading” condition on AsyncTextureLoader, - // and make use of that for at least for projector stuff (require player to be in the same sector as the slides - // for them to start loading, and unload when the player leaves) - - private bool _started; - - public void Start() - { - if (_started) return; - - _started = true; - - if (SingletonAsyncImageLoader.Instance == null) - { - Main.Instance.gameObject.AddComponent(); - } - - NHLogger.LogVerbose("Loading new slide reel"); - imageLoadedEvent.AddListener(OnImageLoaded); - SingletonAsyncImageLoader.Instance.Load(this); - } - - private void OnImageLoaded(Texture texture, int index, string originalPath) - { - lock (_lockObj) - { - _loadedCount++; - - if (_loadedCount >= PathsToLoad.Count) - { - NHLogger.LogVerbose($"Finished loading all textures for {gameObject.name} (one was {PathsToLoad.FirstOrDefault()}"); - FinishedLoading = true; - } - } - } - - internal IEnumerator DownloadTextures() - { - foreach (var (index, path) in PathsToLoad) - { - NHLogger.Log($"Loaded slide reel {index} of {PathsToLoad.Count}"); - - yield return DownloadTexture(path, index); - } - } - - IEnumerator DownloadTexture(string url, int index) - { - var key = GetKey(url); - if (_textureCache.TryGetValue(key, out var existingTexture)) - { - NHLogger.LogVerbose($"Already loaded image {index}:{url}"); - imageLoadedEvent?.Invoke((Texture2D)existingTexture, index, url); - yield break; - } - - using UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url); - - yield return uwr.SendWebRequest(); - - var hasError = uwr.error != null && uwr.error != ""; - - if (hasError) - { - NHLogger.LogError($"Failed to load {index}:{url} - {uwr.error}"); - } - else - { - var handler = (DownloadHandlerTexture)uwr.downloadHandler; - - var texture = DownloadHandlerTexture.GetContent(uwr); - texture.name = key; - texture.wrapMode = TextureWrapMode.Clamp; - - if (_textureCache.TryGetValue(key, out existingTexture)) - { - NHLogger.LogVerbose($"Already loaded image {index}:{url}"); - Destroy(texture); - texture = (Texture2D)existingTexture; - } - else - { - _textureCache.Add(key, texture); - } - - yield return null; - var time = DateTime.Now; - imageLoadedEvent?.Invoke(texture, index, url); - NHLogger.LogVerbose($"Slide reel event? {(DateTime.Now - time).TotalMilliseconds}ms"); - } - } - } } } diff --git a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs new file mode 100644 index 00000000..50a749f3 --- /dev/null +++ b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs @@ -0,0 +1,162 @@ +using NewHorizons.Utility.OWML; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.Networking; + +namespace NewHorizons.Utility.Files; + +// Modified from https://stackoverflow.com/a/69141085/9643841 +// Having more than one SlideReelAsyncImageLoader running at the same time is LAGGY. While loading the image data from the disk is async, nothing else is! +// It will load the images async and then do tens to hundreds of callbacks to imageLoadedEvent all running at around the same time and lagging out the game +// This is why we do it sequentially using SingletonAsyncImageLoader +public class SlideReelAsyncImageLoader +{ + public List<(int index, string path)> PathsToLoad { get; private set; } = new(); + + public class ImageLoadedEvent : UnityEvent { } + public ImageLoadedEvent imageLoadedEvent = new(); + + private readonly object _lockObj = new(); + + public bool FinishedLoading { get; private set; } + private int _loadedCount = 0; + + // TODO: set up an optional “StartLoading” and “StartUnloading” condition on AsyncTextureLoader, + // and make use of that for at least for projector stuff (require player to be in the same sector as the slides + // for them to start loading, and unload when the player leaves) + + private bool _started; + private bool _clamp; + + public void Start(bool clamp) + { + if (_started) return; + + _clamp = clamp; + + _started = true; + + if (SingletonSlideReelAsyncImageLoader.Instance == null) + { + Main.Instance.gameObject.AddComponent(); + } + + NHLogger.LogVerbose("Loading new slide reel"); + imageLoadedEvent.AddListener(OnImageLoaded); + SingletonSlideReelAsyncImageLoader.Instance.Load(this); + } + + private void OnImageLoaded(Texture texture, int index, string originalPath) + { + lock (_lockObj) + { + _loadedCount++; + + if (_loadedCount >= PathsToLoad.Count) + { + NHLogger.LogVerbose($"Finished loading all textures for a slide reel (one was {PathsToLoad.FirstOrDefault()}"); + FinishedLoading = true; + } + } + } + + internal IEnumerator DownloadTextures() + { + foreach (var (index, path) in PathsToLoad) + { + NHLogger.LogVerbose($"Loaded slide reel {index} of {PathsToLoad.Count}"); + + yield return DownloadTexture(path, index); + } + } + + IEnumerator DownloadTexture(string url, int index) + { + var key = ImageUtilities.GetKey(url); + if (ImageUtilities.CheckCachedTexture(key, out var existingTexture)) + { + NHLogger.LogVerbose($"Already loaded image {index}:{url}"); + imageLoadedEvent?.Invoke((Texture2D)existingTexture, index, url); + yield break; + } + + using UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url); + + yield return uwr.SendWebRequest(); + + var hasError = uwr.error != null && uwr.error != ""; + + if (hasError) + { + NHLogger.LogError($"Failed to load {index}:{url} - {uwr.error}"); + } + else + { + var handler = (DownloadHandlerTexture)uwr.downloadHandler; + + var texture = DownloadHandlerTexture.GetContent(uwr); + texture.name = key; + if (_clamp) + { + texture.wrapMode = TextureWrapMode.Clamp; + } + + if (ImageUtilities.CheckCachedTexture(key, out existingTexture)) + { + NHLogger.LogVerbose($"Already loaded image {index}:{url}"); + GameObject.Destroy(texture); + texture = (Texture2D)existingTexture; + } + else + { + ImageUtilities.TrackCachedTexture(key, texture); + } + + yield return null; + var time = DateTime.Now; + imageLoadedEvent?.Invoke(texture, index, url); + NHLogger.LogVerbose($"Slide reel event took: {(DateTime.Now - time).TotalMilliseconds}ms"); + } + } + + private class SingletonSlideReelAsyncImageLoader : MonoBehaviour + { + public static SingletonSlideReelAsyncImageLoader Instance { get; private set; } + + private Queue _loaders = new(); + + private bool _isLoading; + + public void Awake() + { + Instance = this; + } + + public void Load(SlideReelAsyncImageLoader loader) + { + _loaders.Enqueue(loader); + if (!_isLoading) + { + StartCoroutine(Run()); + } + } + + private IEnumerator Run() + { + NHLogger.Log("Loading slide reels"); + _isLoading = true; + while (_loaders.Count > 0) + { + var loader = _loaders.Dequeue(); + yield return loader.DownloadTextures(); + NHLogger.Log($"Finished a slide reel, {_loaders.Count} left"); + } + _isLoading = false; + NHLogger.Log("Done loading slide reels"); + } + } +} \ No newline at end of file From c9d4125f95460f18bf9001d26d682474e222f0a5 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 13 Jun 2024 13:40:13 -0400 Subject: [PATCH 06/11] Fix it breaking between loops if it didnt finish --- NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs index 50a749f3..c28535dc 100644 --- a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs +++ b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs @@ -6,6 +6,7 @@ using System.Linq; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; +using UnityEngine.SceneManagement; namespace NewHorizons.Utility.Files; @@ -134,6 +135,14 @@ public class SlideReelAsyncImageLoader public void Awake() { Instance = this; + SceneManager.sceneUnloaded += OnSceneUnloaded; + } + + private void OnSceneUnloaded(Scene _) + { + StopAllCoroutines(); + _loaders.Clear(); + _isLoading = false; } public void Load(SlideReelAsyncImageLoader loader) From de8470e59534679017f73495ad7cb1d49dc9d554 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 13 Jun 2024 19:44:45 -0700 Subject: [PATCH 07/11] comment all the async stuff, STOP USING LOCKS AND ATOMICS --- .../Builder/Props/ProjectionBuilder.cs | 34 +++++++++++-------- NewHorizons/Utility/Files/ImageUtilities.cs | 5 +-- .../Files/SlideReelAsyncImageLoader.cs | 34 ++++++++----------- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 014c5d0b..43a4fada 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -148,10 +148,10 @@ namespace NewHorizons.Builder.Props var slideCollection = new SlideCollection(slidesCount); slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null - // The base game ones only have 15 slides max - var textures = new Texture2D[slidesCount >= 15 ? 15 : slidesCount]; + // We can fit 16 slides max into an atlas + var textures = new Texture2D[slidesCount > 16 ? 16 : slidesCount]; - var imageLoader = AddAsyncLoader(slideReelObj, mod, info.slides, ref slideCollection); + var imageLoader = StartAsyncLoader(mod, info.slides, ref slideCollection); var key = GetUniqueSlideReelID(mod, info.slides); @@ -167,11 +167,12 @@ namespace NewHorizons.Builder.Props slideCollection.slides[index]._image = ImageUtilities.InvertSlideReel(mod, tex, originalPath); NHLogger.LogVerbose($"Slide reel make reel invert texture {(DateTime.Now - time).TotalMilliseconds}ms"); - // Track the first 15 to put on the slide reel object + // Track the first 16 to put on the slide reel object if (index < textures.Length) { textures[index] = tex; - if (Interlocked.Increment(ref displaySlidesLoaded) >= textures.Length) + displaySlidesLoaded++; + if (displaySlidesLoaded == textures.Length) { // all textures required to build the reel's textures have been loaded var slidesBack = slideReelObj.GetComponentInChildren().transform.Find("Slides_Back").GetComponent(); @@ -315,7 +316,7 @@ namespace NewHorizons.Builder.Props var slideCollection = new SlideCollection(slidesCount); slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null - var imageLoader = AddAsyncLoader(projectorObj, mod, info.slides, ref slideCollection); + var imageLoader = StartAsyncLoader(mod, info.slides, ref slideCollection); imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) => { var time = DateTime.Now; @@ -359,7 +360,7 @@ namespace NewHorizons.Builder.Props var slideCollection = new SlideCollection(slidesCount); // TODO: uh I think that info.slides[i].playTimeDuration is not being read here... note to self for when I implement support for that: 0.7 is what to default to if playTimeDuration turns out to be 0 slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null - var imageLoader = AddAsyncLoader(g, mod, info.slides, ref slideCollection); + var imageLoader = StartAsyncLoader(mod, info.slides, ref slideCollection); imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) => { var time = DateTime.Now; @@ -406,13 +407,13 @@ namespace NewHorizons.Builder.Props visionBeamEffect.SetActive(false); // Set up slides - // The number of slides is unlimited, 15 is only for texturing the actual slide reel item. This is not a slide reel item + // The number of slides is unlimited, 16 is only for texturing the actual slide reel item. This is not a slide reel item var slides = info.slides; var slidesCount = slides.Length; var slideCollection = new SlideCollection(slidesCount); slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null - var imageLoader = AddAsyncLoader(standingTorch, mod, slides, ref slideCollection); + var imageLoader = StartAsyncLoader(mod, slides, ref slideCollection); // This variable just lets us track how many of the slides have been loaded. // This way as soon as the last one is loaded (due to async loading, this may be @@ -425,7 +426,8 @@ namespace NewHorizons.Builder.Props var time = DateTime.Now; slideCollection.slides[index]._image = tex; - if (Interlocked.Increment(ref displaySlidesLoaded) == slides.Length) + displaySlidesLoaded++; + if (displaySlidesLoaded == slides.Length) { mindSlideProjector.enabled = true; visionBeamEffect.SetActive(true); @@ -454,21 +456,22 @@ namespace NewHorizons.Builder.Props return standingTorch; } - private static SlideReelAsyncImageLoader AddAsyncLoader(GameObject gameObject, IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection) + private static SlideReelAsyncImageLoader StartAsyncLoader(IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection) { var invertedImageLoader = new SlideReelAsyncImageLoader(); var atlasImageLoader = new SlideReelAsyncImageLoader(); var imageLoader = new SlideReelAsyncImageLoader(); - var textureNames = slides.Select(x => Path.GetFileNameWithoutExtension(x.imagePath)).ToArray(); var atlasKey = GetUniqueSlideReelID(mod, slides); + // attempt to load atlas guy from disk and precache so theres a cache hit when using ImageUtilities atlasImageLoader.PathsToLoad.Add((0, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER, $"{atlasKey}.png"))); atlasImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) => { NHLogger.Log($"SLIDE REEL ATLAS from {slides.First().imagePath}: {s}"); ImageUtilities.TrackCachedTexture(atlasKey, t); }); + for (int i = 0; i < slides.Length; i++) { var slide = new Slide(); @@ -481,12 +484,15 @@ namespace NewHorizons.Builder.Props } else { + // attempt to load inverted guy from disk and precache so theres a cache hit when using ImageUtilities invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, INVERTED_SLIDE_CACHE_FOLDER, slideInfo.imagePath))); invertedImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) => { - var path = "\\" + Path.Combine(Path.GetFileName(Path.GetDirectoryName(mod.ModHelper.Manifest.ModFolderPath)), slides[i].imagePath) + " > invert"; - ImageUtilities.TrackCachedTexture(path, t); + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slides[i].imagePath); + var key = $"{ImageUtilities.GetKey(path)} > invert"; + ImageUtilities.TrackCachedTexture(key, t); }); + imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath))); } diff --git a/NewHorizons/Utility/Files/ImageUtilities.cs b/NewHorizons/Utility/Files/ImageUtilities.cs index bf7cebea..ab001c49 100644 --- a/NewHorizons/Utility/Files/ImageUtilities.cs +++ b/NewHorizons/Utility/Files/ImageUtilities.cs @@ -17,12 +17,13 @@ namespace NewHorizons.Utility.Files public static bool CheckCachedTexture(string key, out Texture existingTexture) => _textureCache.TryGetValue(key, out existingTexture); public static void TrackCachedTexture(string key, Texture texture) => _textureCache[key] = texture; - public static string GetKey(string path) => path.Substring(Main.Instance.ModHelper.OwmlConfig.ModsPath.Length); + public static string GetKey(string path) => + path.Substring(Main.Instance.ModHelper.OwmlConfig.ModsPath.Length + 1).Replace('\\', '/'); public static bool IsTextureLoaded(IModBehaviour mod, string filename) { var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, filename); - var key = GetKey(path).Replace('\\', '/'); + var key = GetKey(path); return _textureCache.ContainsKey(key); } diff --git a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs index c28535dc..36e8a8f9 100644 --- a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs +++ b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs @@ -10,10 +10,12 @@ using UnityEngine.SceneManagement; namespace NewHorizons.Utility.Files; -// Modified from https://stackoverflow.com/a/69141085/9643841 -// Having more than one SlideReelAsyncImageLoader running at the same time is LAGGY. While loading the image data from the disk is async, nothing else is! -// It will load the images async and then do tens to hundreds of callbacks to imageLoadedEvent all running at around the same time and lagging out the game -// This is why we do it sequentially using SingletonAsyncImageLoader +/// +/// Modified from https://stackoverflow.com/a/69141085/9643841 +/// Having more than one SlideReelAsyncImageLoader running at the same time is LAGGY. While loading the image data from the disk is async, nothing else is! +/// It will load the images async and then do tens to hundreds of callbacks to imageLoadedEvent all running at around the same time and lagging out the game +/// This is why we do it sequentially using SingletonAsyncImageLoader +/// public class SlideReelAsyncImageLoader { public List<(int index, string path)> PathsToLoad { get; private set; } = new(); @@ -21,14 +23,13 @@ public class SlideReelAsyncImageLoader public class ImageLoadedEvent : UnityEvent { } public ImageLoadedEvent imageLoadedEvent = new(); - private readonly object _lockObj = new(); - public bool FinishedLoading { get; private set; } private int _loadedCount = 0; // TODO: set up an optional “StartLoading” and “StartUnloading” condition on AsyncTextureLoader, // and make use of that for at least for projector stuff (require player to be in the same sector as the slides // for them to start loading, and unload when the player leaves) + // also remember this for ship logs!!! lol private bool _started; private bool _clamp; @@ -53,19 +54,16 @@ public class SlideReelAsyncImageLoader private void OnImageLoaded(Texture texture, int index, string originalPath) { - lock (_lockObj) - { - _loadedCount++; + _loadedCount++; - if (_loadedCount >= PathsToLoad.Count) - { - NHLogger.LogVerbose($"Finished loading all textures for a slide reel (one was {PathsToLoad.FirstOrDefault()}"); - FinishedLoading = true; - } + if (_loadedCount >= PathsToLoad.Count) + { + NHLogger.LogVerbose($"Finished loading all textures for a slide reel (one was {PathsToLoad.FirstOrDefault()}"); + FinishedLoading = true; } } - internal IEnumerator DownloadTextures() + private IEnumerator DownloadTextures() { foreach (var (index, path) in PathsToLoad) { @@ -75,7 +73,7 @@ public class SlideReelAsyncImageLoader } } - IEnumerator DownloadTexture(string url, int index) + private IEnumerator DownloadTexture(string url, int index) { var key = ImageUtilities.GetKey(url); if (ImageUtilities.CheckCachedTexture(key, out var existingTexture)) @@ -97,8 +95,6 @@ public class SlideReelAsyncImageLoader } else { - var handler = (DownloadHandlerTexture)uwr.downloadHandler; - var texture = DownloadHandlerTexture.GetContent(uwr); texture.name = key; if (_clamp) @@ -108,6 +104,7 @@ public class SlideReelAsyncImageLoader if (ImageUtilities.CheckCachedTexture(key, out existingTexture)) { + // the image could be loaded by something else by the time we're done doing async stuff NHLogger.LogVerbose($"Already loaded image {index}:{url}"); GameObject.Destroy(texture); texture = (Texture2D)existingTexture; @@ -117,7 +114,6 @@ public class SlideReelAsyncImageLoader ImageUtilities.TrackCachedTexture(key, texture); } - yield return null; var time = DateTime.Now; imageLoadedEvent?.Invoke(texture, index, url); NHLogger.LogVerbose($"Slide reel event took: {(DateTime.Now - time).TotalMilliseconds}ms"); From 23feed8b6ff5a444cb6ac96bbe60139356036c4b Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 13 Jun 2024 20:03:16 -0700 Subject: [PATCH 08/11] make it match the path below --- NewHorizons/Builder/Props/ProjectionBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 43a4fada..2e5894eb 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -488,7 +488,7 @@ namespace NewHorizons.Builder.Props invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, INVERTED_SLIDE_CACHE_FOLDER, slideInfo.imagePath))); invertedImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) => { - var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slides[i].imagePath); + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath); var key = $"{ImageUtilities.GetKey(path)} > invert"; ImageUtilities.TrackCachedTexture(key, t); }); From bcf0c2d0d9019696ffc250721c28ce222d1e0a3d Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 14 Jun 2024 12:02:07 -0400 Subject: [PATCH 09/11] Revert "make it match the path below" This reverts commit 23feed8b6ff5a444cb6ac96bbe60139356036c4b. --- NewHorizons/Builder/Props/ProjectionBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 2e5894eb..43a4fada 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -488,7 +488,7 @@ namespace NewHorizons.Builder.Props invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, INVERTED_SLIDE_CACHE_FOLDER, slideInfo.imagePath))); invertedImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) => { - var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath); + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slides[i].imagePath); var key = $"{ImageUtilities.GetKey(path)} > invert"; ImageUtilities.TrackCachedTexture(key, t); }); From 79c5c464036aa3bc152ab7648f44e3b1f8542290 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 14 Jun 2024 12:14:36 -0400 Subject: [PATCH 10/11] Should not have been adding a listener per slide --- .../Builder/Props/ProjectionBuilder.cs | 25 ++++++++----------- NewHorizons/manifest.json | 2 +- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 43a4fada..3ba8e91d 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -1,3 +1,4 @@ +using HarmonyLib; using NewHorizons.External.Modules.Props; using NewHorizons.External.Modules.Props.EchoesOfTheEye; using NewHorizons.Handlers; @@ -8,13 +9,10 @@ using OWML.Common; using System; using System.Collections.Generic; using System.IO; -using System.Threading; -using UnityEngine; -using static NewHorizons.Main; using System.Linq; -using HarmonyLib; +using UnityEngine; using UnityEngine.InputSystem; -using Epic.OnlineServices.Presence; +using static NewHorizons.Main; namespace NewHorizons.Builder.Props { @@ -471,7 +469,13 @@ namespace NewHorizons.Builder.Props NHLogger.Log($"SLIDE REEL ATLAS from {slides.First().imagePath}: {s}"); ImageUtilities.TrackCachedTexture(atlasKey, t); }); - + invertedImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) => + { + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slides[i].imagePath); + var key = $"{ImageUtilities.GetKey(path)} > invert"; + ImageUtilities.TrackCachedTexture(key, t); + }); + for (int i = 0; i < slides.Length; i++) { var slide = new Slide(); @@ -485,14 +489,7 @@ namespace NewHorizons.Builder.Props else { // attempt to load inverted guy from disk and precache so theres a cache hit when using ImageUtilities - invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, INVERTED_SLIDE_CACHE_FOLDER, slideInfo.imagePath))); - invertedImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) => - { - var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slides[i].imagePath); - var key = $"{ImageUtilities.GetKey(path)} > invert"; - ImageUtilities.TrackCachedTexture(key, t); - }); - + invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, INVERTED_SLIDE_CACHE_FOLDER, slideInfo.imagePath))); imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath))); } diff --git a/NewHorizons/manifest.json b/NewHorizons/manifest.json index 1a7cdab3..7f1a3787 100644 --- a/NewHorizons/manifest.json +++ b/NewHorizons/manifest.json @@ -4,7 +4,7 @@ "author": "xen, Bwc9876, JohnCorby, MegaPiggy, Clay, Trifid, and friends", "name": "New Horizons", "uniqueName": "xen.NewHorizons", - "version": "1.21.2", + "version": "1.21.3", "owmlVersion": "2.12.1", "dependencies": [ "JohnCorby.VanillaFix", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ], "conflicts": [ "PacificEngine.OW_CommonResources" ], From 5b830ceef1cd69e2c29608dbf815f11799a7deda Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 14 Jun 2024 12:27:42 -0400 Subject: [PATCH 11/11] Remove and sort usings --- NewHorizons/Utility/Files/ImageUtilities.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/NewHorizons/Utility/Files/ImageUtilities.cs b/NewHorizons/Utility/Files/ImageUtilities.cs index ab001c49..3c01bd16 100644 --- a/NewHorizons/Utility/Files/ImageUtilities.cs +++ b/NewHorizons/Utility/Files/ImageUtilities.cs @@ -1,11 +1,9 @@ -using HarmonyLib; using NewHorizons.Builder.Props; using NewHorizons.Utility.OWML; using OWML.Common; using System; using System.Collections.Generic; using System.IO; -using System.Linq; using UnityEngine; namespace NewHorizons.Utility.Files