comment all the async stuff, STOP USING LOCKS AND ATOMICS

This commit is contained in:
JohnCorby 2024-06-13 19:44:45 -07:00
parent c9d4125f95
commit de8470e595
3 changed files with 38 additions and 35 deletions

View File

@ -148,10 +148,10 @@ namespace NewHorizons.Builder.Props
var slideCollection = new SlideCollection(slidesCount); var slideCollection = new SlideCollection(slidesCount);
slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null
// The base game ones only have 15 slides max // We can fit 16 slides max into an atlas
var textures = new Texture2D[slidesCount >= 15 ? 15 : slidesCount]; 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); var key = GetUniqueSlideReelID(mod, info.slides);
@ -167,11 +167,12 @@ namespace NewHorizons.Builder.Props
slideCollection.slides[index]._image = ImageUtilities.InvertSlideReel(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"); 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) if (index < textures.Length)
{ {
textures[index] = tex; 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 // all textures required to build the reel's textures have been loaded
var slidesBack = slideReelObj.GetComponentInChildren<TransformAnimator>().transform.Find("Slides_Back").GetComponent<MeshRenderer>(); var slidesBack = slideReelObj.GetComponentInChildren<TransformAnimator>().transform.Find("Slides_Back").GetComponent<MeshRenderer>();
@ -315,7 +316,7 @@ namespace NewHorizons.Builder.Props
var slideCollection = new SlideCollection(slidesCount); var slideCollection = new SlideCollection(slidesCount);
slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null 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) => imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) =>
{ {
var time = DateTime.Now; 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 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 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) => imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) =>
{ {
var time = DateTime.Now; var time = DateTime.Now;
@ -406,13 +407,13 @@ namespace NewHorizons.Builder.Props
visionBeamEffect.SetActive(false); visionBeamEffect.SetActive(false);
// Set up slides // 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 slides = info.slides;
var slidesCount = slides.Length; var slidesCount = slides.Length;
var slideCollection = new SlideCollection(slidesCount); var slideCollection = new SlideCollection(slidesCount);
slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null 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 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 // 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; var time = DateTime.Now;
slideCollection.slides[index]._image = tex; slideCollection.slides[index]._image = tex;
if (Interlocked.Increment(ref displaySlidesLoaded) == slides.Length) displaySlidesLoaded++;
if (displaySlidesLoaded == slides.Length)
{ {
mindSlideProjector.enabled = true; mindSlideProjector.enabled = true;
visionBeamEffect.SetActive(true); visionBeamEffect.SetActive(true);
@ -454,21 +456,22 @@ namespace NewHorizons.Builder.Props
return standingTorch; 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 invertedImageLoader = new SlideReelAsyncImageLoader();
var atlasImageLoader = new SlideReelAsyncImageLoader(); var atlasImageLoader = new SlideReelAsyncImageLoader();
var imageLoader = new SlideReelAsyncImageLoader(); var imageLoader = new SlideReelAsyncImageLoader();
var textureNames = slides.Select(x => Path.GetFileNameWithoutExtension(x.imagePath)).ToArray();
var atlasKey = GetUniqueSlideReelID(mod, slides); 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.PathsToLoad.Add((0, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER, $"{atlasKey}.png")));
atlasImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) => atlasImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) =>
{ {
NHLogger.Log($"SLIDE REEL ATLAS from {slides.First().imagePath}: {s}"); NHLogger.Log($"SLIDE REEL ATLAS from {slides.First().imagePath}: {s}");
ImageUtilities.TrackCachedTexture(atlasKey, t); ImageUtilities.TrackCachedTexture(atlasKey, t);
}); });
for (int i = 0; i < slides.Length; i++) for (int i = 0; i < slides.Length; i++)
{ {
var slide = new Slide(); var slide = new Slide();
@ -481,12 +484,15 @@ namespace NewHorizons.Builder.Props
} }
else 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.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, INVERTED_SLIDE_CACHE_FOLDER, slideInfo.imagePath)));
invertedImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) => 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"; var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slides[i].imagePath);
ImageUtilities.TrackCachedTexture(path, t); var key = $"{ImageUtilities.GetKey(path)} > invert";
ImageUtilities.TrackCachedTexture(key, t);
}); });
imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath))); imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath)));
} }

View File

@ -17,12 +17,13 @@ namespace NewHorizons.Utility.Files
public static bool CheckCachedTexture(string key, out Texture existingTexture) => _textureCache.TryGetValue(key, out existingTexture); 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 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) public static bool IsTextureLoaded(IModBehaviour mod, string filename)
{ {
var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, filename); var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, filename);
var key = GetKey(path).Replace('\\', '/'); var key = GetKey(path);
return _textureCache.ContainsKey(key); return _textureCache.ContainsKey(key);
} }

View File

@ -10,10 +10,12 @@ using UnityEngine.SceneManagement;
namespace NewHorizons.Utility.Files; namespace NewHorizons.Utility.Files;
// Modified from https://stackoverflow.com/a/69141085/9643841 /// <summary>
// 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! /// Modified from https://stackoverflow.com/a/69141085/9643841
// 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 /// 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!
// This is why we do it sequentially using SingletonAsyncImageLoader /// 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
/// </summary>
public class SlideReelAsyncImageLoader public class SlideReelAsyncImageLoader
{ {
public List<(int index, string path)> PathsToLoad { get; private set; } = new(); public List<(int index, string path)> PathsToLoad { get; private set; } = new();
@ -21,14 +23,13 @@ public class SlideReelAsyncImageLoader
public class ImageLoadedEvent : UnityEvent<Texture2D, int, string> { } public class ImageLoadedEvent : UnityEvent<Texture2D, int, string> { }
public ImageLoadedEvent imageLoadedEvent = new(); public ImageLoadedEvent imageLoadedEvent = new();
private readonly object _lockObj = new();
public bool FinishedLoading { get; private set; } public bool FinishedLoading { get; private set; }
private int _loadedCount = 0; private int _loadedCount = 0;
// TODO: set up an optional “StartLoading” and “StartUnloading” condition on AsyncTextureLoader, // 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 // 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) // for them to start loading, and unload when the player leaves)
// also remember this for ship logs!!! lol
private bool _started; private bool _started;
private bool _clamp; private bool _clamp;
@ -52,8 +53,6 @@ public class SlideReelAsyncImageLoader
} }
private void OnImageLoaded(Texture texture, int index, string originalPath) private void OnImageLoaded(Texture texture, int index, string originalPath)
{
lock (_lockObj)
{ {
_loadedCount++; _loadedCount++;
@ -63,9 +62,8 @@ public class SlideReelAsyncImageLoader
FinishedLoading = true; FinishedLoading = true;
} }
} }
}
internal IEnumerator DownloadTextures() private IEnumerator DownloadTextures()
{ {
foreach (var (index, path) in PathsToLoad) 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); var key = ImageUtilities.GetKey(url);
if (ImageUtilities.CheckCachedTexture(key, out var existingTexture)) if (ImageUtilities.CheckCachedTexture(key, out var existingTexture))
@ -97,8 +95,6 @@ public class SlideReelAsyncImageLoader
} }
else else
{ {
var handler = (DownloadHandlerTexture)uwr.downloadHandler;
var texture = DownloadHandlerTexture.GetContent(uwr); var texture = DownloadHandlerTexture.GetContent(uwr);
texture.name = key; texture.name = key;
if (_clamp) if (_clamp)
@ -108,6 +104,7 @@ public class SlideReelAsyncImageLoader
if (ImageUtilities.CheckCachedTexture(key, out existingTexture)) 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}"); NHLogger.LogVerbose($"Already loaded image {index}:{url}");
GameObject.Destroy(texture); GameObject.Destroy(texture);
texture = (Texture2D)existingTexture; texture = (Texture2D)existingTexture;
@ -117,7 +114,6 @@ public class SlideReelAsyncImageLoader
ImageUtilities.TrackCachedTexture(key, texture); ImageUtilities.TrackCachedTexture(key, texture);
} }
yield return null;
var time = DateTime.Now; var time = DateTime.Now;
imageLoadedEvent?.Invoke(texture, index, url); imageLoadedEvent?.Invoke(texture, index, url);
NHLogger.LogVerbose($"Slide reel event took: {(DateTime.Now - time).TotalMilliseconds}ms"); NHLogger.LogVerbose($"Slide reel event took: {(DateTime.Now - time).TotalMilliseconds}ms");