diff --git a/Program.cs b/Program.cs index 480e5aa..1ed7801 100644 --- a/Program.cs +++ b/Program.cs @@ -571,6 +571,8 @@ public class Game : GameWindow { List allPhotos = new(); List photos = new(); HashSet loadedImages = new(); + HashSet loadingImages = new(); + readonly object loadedImagesLock = new(); int photoIndex = 0; int ribbonIndex = 0; Shader shader = new(); @@ -777,10 +779,10 @@ public class Game : GameWindow { // Load photos from a directory. // string[] files = Directory.GetFiles(@"c:\users\colin\desktop\photos-test\"); - string[] files = Directory.GetFiles(@"c:\users\colin\pictures\photos\2023\07\14\"); + // string[] files = Directory.GetFiles(@"c:\users\colin\pictures\photos\2023\07\14\"); // string[] files = Directory.GetFiles(@"G:\DCIM\100EOSR6\"); // string[] files = Directory.GetFiles(@"C:\Users\colin\Pictures\photos\2018\06\23"); - // string[] files = Directory.GetFiles(@"C:\Users\colin\Desktop\Germany all\104D7000"); + string[] files = Directory.GetFiles(@"C:\Users\colin\Desktop\Germany all\104D7000"); // string[] files = Directory.GetFiles(@"C:\Users\colin\Desktop\many-birds\"); for (int i = 0; i < files.Count(); i++) { @@ -809,43 +811,62 @@ public class Game : GameWindow { base.OnUnload(); } - private async void LoadAndUnloadImagesAsync() { - int minLoadedImage = Math.Max(0, photoIndex - 20); - int maxLoadedImage = Math.Min(photoIndex + 20, photos.Count - 1); - // First, unload images that haven't been touched in a while. - // FIXME: also cancel any of these if they still have an in-progress loading task? + private void UnloadImages() { + // Unload images that haven't been touched in a while. // FIXME: keep around thumbnail-sized textures? - while (loadedImages.Count > 60) { - long earliestTime = long.MaxValue; - Photo? earliest = null; - foreach (Photo photo in loadedImages) { - if (photo.LastTouch < earliestTime) { - earliest = photo; - earliestTime = photo.LastTouch; + lock (loadedImagesLock) { + while (loadedImages.Count > 60) { + long earliestTime = long.MaxValue; + Photo? earliest = null; + foreach (Photo photo in loadedImages) { + if (photo.LastTouch < earliestTime) { + earliest = photo; + earliestTime = photo.LastTouch; + } + } + if (earliest != null) { + // Console.WriteLine($"loadedImages.Count: {loadedImages.Count}, evicting {earliest.Filename} @ {earliestTime}"); + // TODO: we have to free textures on the GL thread, but could we do that async'ly to keep the UI responsive? + earliest.Unload(); + loadedImages.Remove(earliest); } } - if (earliest != null) { - Console.WriteLine($"loadedImages.Count: {loadedImages.Count}, evicting {earliest.Filename} @ {earliestTime}"); - // TODO: we have to free textures on the GL thread, but could we do that async'ly to keep the UI responsive? - earliest.Unload(); - loadedImages.Remove(earliest); + } + } + + private async void LoadImagesAsync() { + foreach (Photo p in loadingImages) { + if (p.Loaded) { + lock (loadedImagesLock) { + loadedImages.Add(p); + loadingImages.Remove(p); + } } } - // Then, start loading any images that are in our window but not yet loaded. + // Start loading any images that are in our window but not yet loaded. + int minLoadedImage = Math.Max(0, photoIndex - 20); + int maxLoadedImage = Math.Min(photoIndex + 20, photos.Count - 1); + List toLoad = new(); for (int i = minLoadedImage; i <= maxLoadedImage; i++) { - if (!loadedImages.Contains(photos[i])) { - Console.WriteLine("loading " + i); - loadedImages.Add(photos[i]); - await Task.Run( () => { photos[i].LoadAsync(); }); + lock (loadedImagesLock) { + if (!loadedImages.Contains(photos[i]) && !loadingImages.Contains(photos[i])) { + Console.WriteLine("loading " + i); + loadingImages.Add(photos[i]); + toLoad.Add(photos[i]); + } } } + foreach (Photo p in toLoad) { + await Task.Run( () => { p.LoadAsync(); }); + } } protected override void OnRenderFrame(FrameEventArgs e) { base.OnRenderFrame(e); fpsCounter.Update(); - LoadAndUnloadImagesAsync(); + UnloadImages(); + LoadImagesAsync(); GL.Clear(ClearBufferMask.ColorBufferBit); GL.BindBuffer(BufferTarget.ArrayBuffer, VertexBufferObject);