Cache inverted images

This commit is contained in:
Nick 2024-06-13 01:13:25 -04:00
parent 493365155f
commit 540a501b9a
2 changed files with 106 additions and 19 deletions

View File

@ -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<VisionTorchTarget>();
@ -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<ImageUtilities.AsyncImageLoader>();
var imageLoader = gameObject.AddComponent<ImageUtilities.AsyncImageLoader>();
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;
}

View File

@ -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<string, Texture> _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).
/// </summary>
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<AsyncImageLoader> 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<Texture2D, int> { }
public class ImageLoadedEvent : UnityEvent<Texture2D, int, string> { }
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<SingletonAsyncImageLoader>();
}
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");
}
}
}