unload Photos by least-recently-used
This commit is contained in:
parent
dc909a80f4
commit
ca5b2d94f5
54
Program.cs
54
Program.cs
@ -172,6 +172,7 @@ void main() {
|
|||||||
public class Photo {
|
public class Photo {
|
||||||
public string Filename;
|
public string Filename;
|
||||||
public bool Loaded = false;
|
public bool Loaded = false;
|
||||||
|
public long LastTouch = 0;
|
||||||
public Vector2i Size;
|
public Vector2i Size;
|
||||||
public DateTime DateTimeOriginal;
|
public DateTime DateTimeOriginal;
|
||||||
public string CameraModel = "";
|
public string CameraModel = "";
|
||||||
@ -183,6 +184,7 @@ public class Photo {
|
|||||||
public int Rating = 0;
|
public int Rating = 0;
|
||||||
public ushort Orientation = 1;
|
public ushort Orientation = 1;
|
||||||
|
|
||||||
|
private static long touchCounter = 0;
|
||||||
private Texture texture;
|
private Texture texture;
|
||||||
private Texture placeholder;
|
private Texture placeholder;
|
||||||
private Image<Rgba32>? image = null;
|
private Image<Rgba32>? image = null;
|
||||||
@ -204,6 +206,7 @@ public class Photo {
|
|||||||
// We don't assign to this.image until Load() is done, because we might
|
// We don't assign to this.image until Load() is done, because we might
|
||||||
// edit the image due to rotation (etc) and don't want to try generating
|
// edit the image due to rotation (etc) and don't want to try generating
|
||||||
// a texture for it until that's already happened.
|
// a texture for it until that's already happened.
|
||||||
|
LastTouch = touchCounter++;
|
||||||
Image<Rgba32> tmp = await Image.LoadAsync<Rgba32>(Filename);
|
Image<Rgba32> tmp = await Image.LoadAsync<Rgba32>(Filename);
|
||||||
Util.RotateImageFromExif(tmp, Orientation);
|
Util.RotateImageFromExif(tmp, Orientation);
|
||||||
image = tmp;
|
image = tmp;
|
||||||
@ -348,6 +351,7 @@ public class Photo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Texture Texture() {
|
public Texture Texture() {
|
||||||
|
LastTouch = touchCounter++;
|
||||||
if (texture == placeholder && image != null) {
|
if (texture == placeholder && image != null) {
|
||||||
// The texture needs to be created on the GL thread, so we instantiate
|
// The texture needs to be created on the GL thread, so we instantiate
|
||||||
// it here (since this is called from OnRenderFrame), as long as the
|
// it here (since this is called from OnRenderFrame), as long as the
|
||||||
@ -371,6 +375,9 @@ public class Texture : IDisposable {
|
|||||||
public int Handle;
|
public int Handle;
|
||||||
public Vector2i Size;
|
public Vector2i Size;
|
||||||
|
|
||||||
|
private static int maxHandle = -1;
|
||||||
|
private bool disposedValue = false;
|
||||||
|
|
||||||
public Texture(Image<Rgba32> image) {
|
public Texture(Image<Rgba32> image) {
|
||||||
Size = new Vector2i(image.Width, image.Height);
|
Size = new Vector2i(image.Width, image.Height);
|
||||||
byte[] pixelBytes = new byte[Size.X * Size.Y * Unsafe.SizeOf<Rgba32>()];
|
byte[] pixelBytes = new byte[Size.X * Size.Y * Unsafe.SizeOf<Rgba32>()];
|
||||||
@ -395,9 +402,6 @@ public class Texture : IDisposable {
|
|||||||
//GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
|
//GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int maxHandle = -1;
|
|
||||||
private bool disposedValue = false;
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing) {
|
protected virtual void Dispose(bool disposing) {
|
||||||
if (!disposedValue) {
|
if (!disposedValue) {
|
||||||
GL.DeleteTexture(Handle);
|
GL.DeleteTexture(Handle);
|
||||||
@ -565,7 +569,7 @@ public class Game : GameWindow {
|
|||||||
int VertexArrayObject;
|
int VertexArrayObject;
|
||||||
List<Photo> allPhotos = new();
|
List<Photo> allPhotos = new();
|
||||||
List<Photo> photos = new();
|
List<Photo> photos = new();
|
||||||
HashSet<string> loadedImages = new();
|
HashSet<Photo> loadedImages = new();
|
||||||
int photoIndex = 0;
|
int photoIndex = 0;
|
||||||
int ribbonIndex = 0;
|
int ribbonIndex = 0;
|
||||||
Shader shader = new();
|
Shader shader = new();
|
||||||
@ -711,6 +715,7 @@ public class Game : GameWindow {
|
|||||||
void FilterByRating(int rating) {
|
void FilterByRating(int rating) {
|
||||||
Console.WriteLine("filter to " + rating);
|
Console.WriteLine("filter to " + rating);
|
||||||
photos = allPhotos.Where(p => p.Rating >= rating).ToList();
|
photos = allPhotos.Where(p => p.Rating >= rating).ToList();
|
||||||
|
// TODO: put this closest to wherever the previously active photo was.
|
||||||
photoIndex = 0;
|
photoIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -752,10 +757,10 @@ public class Game : GameWindow {
|
|||||||
|
|
||||||
// Load photos from a directory.
|
// Load photos from a directory.
|
||||||
// string[] files = Directory.GetFiles(@"c:\users\colin\desktop\photos-test\");
|
// 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(@"G:\DCIM\100EOSR6\");
|
||||||
// string[] files = Directory.GetFiles(@"C:\Users\colin\Pictures\photos\2018\06\23");
|
// 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\");
|
// string[] files = Directory.GetFiles(@"C:\Users\colin\Desktop\many-birds\");
|
||||||
|
|
||||||
for (int i = 0; i < files.Count(); i++) {
|
for (int i = 0; i < files.Count(); i++) {
|
||||||
@ -785,28 +790,31 @@ public class Game : GameWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async void LoadAndUnloadImagesAsync() {
|
private async void LoadAndUnloadImagesAsync() {
|
||||||
int minUnloadedImage = Math.Max(0, photoIndex - 20);
|
int minLoadedImage = Math.Max(0, photoIndex - 20);
|
||||||
int maxUnloadedImage = Math.Min(photoIndex + 30, photos.Count - 1);
|
|
||||||
int minLoadedImage = Math.Max(0, photoIndex - 10);
|
|
||||||
int maxLoadedImage = Math.Min(photoIndex + 20, photos.Count - 1);
|
int maxLoadedImage = Math.Min(photoIndex + 20, photos.Count - 1);
|
||||||
// First, unload images that are far outside our window.
|
// First, unload images that haven't been touched in a while.
|
||||||
// FIXME: also cancel any in-progress loading tasks that have moved outside our window.
|
// FIXME: also cancel any of these if they still have an in-progress loading task -- I suspect this is the source of a memory leak.
|
||||||
// FIXME: keep around thumbnail-sized textures?
|
// FIXME: keep around thumbnail-sized textures?
|
||||||
// FIXME: turn unloading back on, using an LRU cache for evicting images.
|
while (loadedImages.Count > 60) {
|
||||||
/*
|
long earliestTime = long.MaxValue;
|
||||||
foreach (int i in loadedImages) {
|
Photo? earliest = null;
|
||||||
if (i < minUnloadedImage || i > maxUnloadedImage) {
|
foreach (Photo photo in loadedImages) {
|
||||||
// Console.WriteLine("unloading " + i);
|
if (photo.LastTouch < earliestTime) {
|
||||||
loadedImages.Remove(i);
|
earliest = photo;
|
||||||
photos[i].UnloadAsync();
|
earliestTime = photo.LastTouch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
if (earliest != null) {
|
||||||
// Then, start loading any images that aren't in our window.
|
Console.WriteLine($"loadedImages.Count: {loadedImages.Count}, evicting {earliest.Filename} @ {earliestTime}");
|
||||||
|
earliest.UnloadAsync();
|
||||||
|
loadedImages.Remove(earliest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Then, start loading any images that are in our window but not yet loaded.
|
||||||
for (int i = minLoadedImage; i <= maxLoadedImage; i++) {
|
for (int i = minLoadedImage; i <= maxLoadedImage; i++) {
|
||||||
if (!loadedImages.Contains(photos[i].Filename)) {
|
if (!loadedImages.Contains(photos[i])) {
|
||||||
// Console.WriteLine("loading " + i);
|
Console.WriteLine("loading " + i);
|
||||||
loadedImages.Add(photos[i].Filename);
|
loadedImages.Add(photos[i]);
|
||||||
await Task.Run( () => { photos[i].LoadAsync(); });
|
await Task.Run( () => { photos[i].LoadAsync(); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user