From de8470e59534679017f73495ad7cb1d49dc9d554 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 13 Jun 2024 19:44:45 -0700 Subject: [PATCH] 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");