diff --git a/Src/IO/Asset.cs b/Src/IO/Asset.cs
index 167ad7f3..65660e84 100644
--- a/Src/IO/Asset.cs
+++ b/Src/IO/Asset.cs
@@ -20,8 +20,8 @@ public abstract class Asset
/// The state of this asset.
public AssetState State { get; internal set; }
- /// The source of this asset. Can be null.
- public AssetSource Source { get; internal set; }
+ /// The file source of this asset. Can be null.
+ public AssetFileEntry? File { get; internal set; }
/// Whether or not this asset is currently being loaded.
public bool IsLoading => State == AssetState.Loading;
@@ -111,7 +111,7 @@ private async Task Load(AssetRequestMode mode)
var asyncContext = new ContinuationScheduler(this);
string extension = Path.GetExtension(AssetPath);
- var readerByExtension = Assets.ReadersByDataType.ReaderByExtension;
+ var readerByExtension = Assets.AssetTypeData.ReaderByExtension;
if (readerByExtension.Count == 0) {
throw new InvalidOperationException($"No asset reader found with a return type of '{typeof(T).Name}'.");
@@ -125,23 +125,26 @@ private async Task Load(AssetRequestMode mode)
await Task.Yield(); // This transfers the method's execution to a worker thread.
}
- using var stream = Source.OpenStream(AssetPath);
-
- Value = await assetReader.ReadFromStream(stream, AssetPath, new MainThreadCreationContext(asyncContext));
+ Value = await assetReader.ReadAsset(File, new MainThreadCreationContext(asyncContext));
State = AssetState.Loaded;
}
private void SafelyWaitForLoad(Task loadTask, bool tracked)
{
- if (State == AssetState.Loaded)
+ if (State == AssetState.Loaded) {
return;
+ }
if (!loadTask.IsCompleted && Assets.IsMainThread) {
- while (Continuation == null) {
+ while (Continuation == null && State != AssetState.Loaded) {
Thread.Yield();
}
+ if (State == AssetState.Loaded) {
+ return;
+ }
+
if (tracked) {
lock (Assets.RequestLock) {
Continuation();
diff --git a/Src/IO/AssetFileEntry.cs b/Src/IO/AssetFileEntry.cs
new file mode 100644
index 00000000..2ab75554
--- /dev/null
+++ b/Src/IO/AssetFileEntry.cs
@@ -0,0 +1,21 @@
+using System.IO;
+
+namespace Dissonance.Engine.IO
+{
+ public class AssetFileEntry
+ {
+ public readonly string Path;
+ public readonly AssetSource Source;
+
+ public AssetFileEntry(string path, AssetSource source)
+ {
+ Path = path;
+ Source = source;
+ }
+
+ public Stream OpenStream()
+ {
+ return Source.OpenStream(Path);
+ }
+ }
+}
diff --git a/Src/IO/AssetLookup.cs b/Src/IO/AssetLookup.cs
index 7d26eda5..03d29339 100644
--- a/Src/IO/AssetLookup.cs
+++ b/Src/IO/AssetLookup.cs
@@ -13,7 +13,7 @@ internal static class AssetLookup
internal static class AssetLookup
{
- private static readonly ConcurrentDictionary> lookup = new();
+ private static readonly ConcurrentDictionary asset)> lookup = new();
public static int Count => lookup.Count;
@@ -24,21 +24,49 @@ static AssetLookup()
};
}
- public static void Register(string name, Asset asset)
+ public static void Register(string name, string path, Asset asset)
{
- if (lookup.ContainsKey(name)) {
- //throw new Exception($"Cannot register two {typeof(T).Name} with the same name: {name}.");
+ if (lookup.TryGetValue(name, out var existingTuple) && existingTuple.assetPath == path) {
+ lookup[name] = (null, null); // This marks the registry as ambiguous.
+ } else {
+ lookup[name] = (path, asset);
+ }
+ }
+
+ public static Asset Get(string fullName, AssetRequestMode mode = AssetRequestMode.DoNotLoad)
+ {
+ var tuple = lookup[fullName];
- asset = null; // This marks the registry as ambiguous.
+ if (tuple.assetPath == null && tuple.asset == null) {
+ throw new ArgumentException($"Key '{fullName}' is ambiguous.");
}
- lookup[name] = asset;
+ if (tuple.asset == null) {
+ tuple.asset = Assets.Get(tuple.assetPath, mode);
+ lookup[fullName] = tuple;
+ }
+
+ return tuple.asset;
}
- public static Asset Get(string fullName)
- => lookup[fullName] ?? throw new ArgumentException($"Key '{fullName}' is ambiguous.");
+ public static bool TryGet(string fullName, out Asset result, AssetRequestMode mode = AssetRequestMode.DoNotLoad)
+ {
+ var tuple = lookup[fullName];
+
+ if (tuple.assetPath == null && tuple.asset == null) {
+ result = null;
+
+ return false;
+ }
- public static bool TryGetValue(string fullName, out Asset value)
- => lookup.TryGetValue(fullName, out value) && value != null;
+ if (tuple.asset == null) {
+ tuple.asset = Assets.Get(tuple.assetPath, mode);
+ lookup[fullName] = tuple;
+ }
+
+ result = tuple.asset;
+
+ return true;
+ }
}
}
diff --git a/Src/IO/Assets.cs b/Src/IO/Assets.cs
index 6bba291d..b946632f 100644
--- a/Src/IO/Assets.cs
+++ b/Src/IO/Assets.cs
@@ -11,10 +11,11 @@ namespace Dissonance.Engine.IO
//TODO: Make a ton more threadsafe.
public sealed class Assets : EngineModule
{
- internal static class ReadersByDataType
+ internal static class AssetTypeData
{
- internal static readonly HashSet> Readers = new();
- internal static readonly Dictionary> ReaderByExtension = new();
+ public static readonly Dictionary> Assets = new();
+ public static readonly HashSet> Readers = new();
+ public static readonly Dictionary> ReaderByExtension = new();
}
internal const string BuiltInAssetsDirectory = "BuiltInAssets";
@@ -24,7 +25,8 @@ internal static class ReadersByDataType
internal static readonly ConcurrentQueue AssetTransferQueue = new();
private static readonly HashSet sources = new();
- private static readonly Dictionary assets = new();
+ private static readonly Dictionary assetFiles = new();
+ //private static readonly Dictionary> assets = new();
internal static bool IsMainThread => Thread.CurrentThread == Game.MainThread;
@@ -32,7 +34,7 @@ protected override void Init()
{
RegisterAssetSources();
RegisterAssetReaders();
- AutoloadAssets();
+ PrepareAssets();
}
protected override void PreRenderUpdate()
@@ -45,22 +47,12 @@ protected override void PreRenderUpdate()
}
///
- /// Returns whether or not an asset the provided case-sensitive virtual path exists.
+ /// Returns whether or not an asset of the provided type and case-sensitive virtual path exists.
///
/// The path of the asset. This path is virtual and case-sensitive - system paths will not work.
- public static bool Exists(string assetPath)
+ public static bool Exists(string assetPath)
{
- if (assets.ContainsKey(assetPath)) {
- return true;
- }
-
- foreach (var source in sources) {
- if (source.HasAsset(assetPath)) {
- return true;
- }
- }
-
- return false;
+ return assetFiles.ContainsKey(assetPath);
}
///
@@ -112,7 +104,7 @@ public static bool TryGet(string assetPath, string basePath, out Asset res
assetPath = assetPath.Substring(1);
}
- return TryGet(assetPath, out result, mode);
+ return TryGet(assetPath, out result, mode);
}
///
@@ -126,28 +118,30 @@ public static bool TryGet(string assetPath, string basePath, out Asset res
/// A boolean indicating whether the operation succeeded.
public static bool TryGet(string assetPath, out Asset result, AssetRequestMode mode = AssetRequestMode.DoNotLoad)
{
- if (assets.TryGetValue(assetPath, out var cachedAsset) && cachedAsset is Asset cachedAssetResult) {
+ if (AssetTypeData.Assets.TryGetValue(assetPath, out var cachedAsset) && cachedAsset is Asset cachedAssetResult) {
if (mode != AssetRequestMode.DoNotLoad && cachedAssetResult.State == AssetState.NotLoaded) {
cachedAssetResult.Request(mode);
}
result = cachedAssetResult;
- if (mode == AssetRequestMode.ImmediateLoad)
+ if (mode == AssetRequestMode.ImmediateLoad && result.State != AssetState.Loaded) {
result.Wait();
+ }
return true;
}
- foreach (var source in sources) {
- if (!source.HasAsset(assetPath)) {
- continue;
- }
+ if (assetFiles.TryGetValue(assetPath, out var assetFile)) {
+ result = CreateAsset(assetFile);
- result = RequestFromSource(source, assetPath, mode);
+ if (mode != AssetRequestMode.DoNotLoad) {
+ result.Request(mode);
+ }
- if (mode == AssetRequestMode.ImmediateLoad)
+ if (mode == AssetRequestMode.ImmediateLoad && result.State != AssetState.Loaded) {
result.Wait();
+ }
return true;
}
@@ -165,16 +159,16 @@ public static bool TryGet(string assetPath, out Asset result, AssetRequest
/// The case-sensitive name of the asset. This is not the same as its path.
/// <> - an asset handle.
/// No registered asset could be found with the provided name.
- public static Asset Find(string assetName)
- => AssetLookup.Get(assetName);
+ public static Asset Find(string assetName, AssetRequestMode mode = AssetRequestMode.DoNotLoad)
+ => AssetLookup.Get(assetName, mode);
/// Safely attempts to find a registered asset using its case-sensitive name instead of a path.
/// The type of the asset.
/// The case-sensitive name of the asset. This is not the same as its path.
/// The resulting <> - an asset handle, if it was found.
/// A boolean indicating whether the operation succeeded.
- public static bool TryFind(string assetName, out Asset result)
- => AssetLookup.TryGetValue(assetName, out result);
+ public static bool TryFind(string assetName, out Asset result, AssetRequestMode mode = AssetRequestMode.DoNotLoad)
+ => AssetLookup.TryGet(assetName, out result, mode);
///
/// Creates, registers and returns a new pre-loaded asset object with the provided name and value.
@@ -188,7 +182,7 @@ public static Asset CreateLoaded(string name, T value)
{
var asset = CreateUntracked(name, value);
- AssetLookup.Register(name, asset);
+ AssetLookup.Register(name, null, asset);
return asset;
}
@@ -238,30 +232,28 @@ public static void AddAssetSource(AssetSource assetSource)
///
public static void AddAssetReader(IAssetReader assetReader)
{
- if (!ReadersByDataType.Readers.Add(assetReader)) {
+ if (!AssetTypeData.Readers.Add(assetReader)) {
throw new InvalidOperationException($"Asset reader '{assetReader.GetType().Name}' is already registered.");
}
ReaderAssetTypes.Add(typeof(T));
foreach (string extension in assetReader.Extensions) {
- ReadersByDataType.ReaderByExtension.Add(extension, assetReader);
+ AssetTypeData.ReaderByExtension.Add(extension, assetReader);
}
}
- private static Asset RequestFromSource(AssetSource source, string assetPath, AssetRequestMode mode = AssetRequestMode.AsyncLoad)
+ private static Asset CreateAsset(AssetFileEntry assetFile)
{
+ string assetPath = assetFile.Path;
string assetName = Path.GetFileNameWithoutExtension(assetPath);
+
var asset = new Asset(assetName) {
AssetPath = assetPath,
- Source = source
+ File = assetFile
};
- AssetLookup.Register(assetName, asset);
-
- assets[assetPath] = asset;
-
- asset.Request(mode);
+ AssetLookup.Register(assetName, assetFile.Path, asset);
return asset;
}
@@ -310,6 +302,51 @@ private static void RegisterAssetReaders()
}
}
+ private static void PrepareAssets()
+ {
+ PrepareAssetFileLists();
+ PrepareAssetLookup();
+ AutoloadAssets();
+ }
+
+ private static void PrepareAssetFileLists()
+ {
+ assetFiles.Clear();
+
+ foreach (var source in sources) {
+ foreach (string assetPath in source.EnumerateAssets()) {
+ assetFiles[assetPath] = new AssetFileEntry(assetPath, source);
+ }
+ }
+ }
+
+ private static void PrepareAssetLookup()
+ {
+ var refreshAssetLookupMethod = typeof(Assets).GetMethod(nameof(RefreshAssetLookupOfType), BindingFlags.Static | BindingFlags.NonPublic);
+
+ foreach (var type in ReaderAssetTypes) {
+ refreshAssetLookupMethod
+ .MakeGenericMethod(type)
+ .Invoke(null, null);
+ }
+ }
+
+ private static void RefreshAssetLookupOfType()
+ {
+ foreach (var assetFile in assetFiles.Values) {
+ string assetPath = assetFile.Path;
+ string assetExtension = Path.GetExtension(assetPath);
+
+ if (!AssetTypeData.ReaderByExtension.TryGetValue(assetExtension, out var assetReader)) {
+ continue;
+ }
+
+ string assetName = Path.GetFileNameWithoutExtension(assetPath);
+
+ AssetLookup.Register(assetName, assetPath, null);
+ }
+ }
+
private static void AutoloadAssets()
{
var autoloadAssetsMethod = typeof(Assets).GetMethod(nameof(AutoloadAssetsGeneric), BindingFlags.Static | BindingFlags.NonPublic);
@@ -323,18 +360,27 @@ private static void AutoloadAssets()
private static void AutoloadAssetsGeneric()
{
- foreach (var reader in ReadersByDataType.Readers) {
+ var loadingAssets = new Queue();
+
+ foreach (var reader in AssetTypeData.Readers) {
if (!reader.AutoloadAssets) {
continue;
}
- foreach (var source in sources) {
- foreach (string assetPath in source.EnumerateAssets()) {
- string extension = Path.GetExtension(assetPath);
+ foreach (var assetFile in assetFiles.Values) {
+ string extension = Path.GetExtension(assetFile.Path);
+
+ if (reader.Extensions.Contains(extension)) {
+ var asset = CreateAsset(assetFile);
+
+ asset.Request(AssetRequestMode.AsyncLoad);
+ loadingAssets.Enqueue(asset);
+ }
+ }
- if (reader.Extensions.Contains(extension)) {
- RequestFromSource(source, assetPath, AssetRequestMode.ImmediateLoad); //TODO: Use async load, then wait for all of them to finish.
- }
+ while (loadingAssets.TryDequeue(out var asset)) {
+ if (asset.State == AssetState.Loading) {
+ asset.Wait();
}
}
}
diff --git a/Src/IO/IAssetReader.cs b/Src/IO/IAssetReader.cs
index 658e816d..6a67b78b 100644
--- a/Src/IO/IAssetReader.cs
+++ b/Src/IO/IAssetReader.cs
@@ -25,6 +25,6 @@ public interface IAssetReader
/// The path of the asset that's currently being loaded.
/// Await this to switch execution of the method to the main thread.
/// A result of type .
- ValueTask ReadFromStream(Stream stream, string assetPath, MainThreadCreationContext switchToMainThread);
+ ValueTask ReadAsset(AssetFileEntry assetFile, MainThreadCreationContext switchToMainThread);
}
}
diff --git a/Src/IO/Readers/Audio/OggReader.cs b/Src/IO/Readers/Audio/OggReader.cs
index 79a04839..a2c75b59 100644
--- a/Src/IO/Readers/Audio/OggReader.cs
+++ b/Src/IO/Readers/Audio/OggReader.cs
@@ -9,18 +9,19 @@ public class OggReader : IAssetReader
{
public string[] Extensions { get; } = { ".ogg" };
- public async ValueTask ReadFromStream(Stream stream, string assetPath, MainThreadCreationContext switchToMainThread)
+ public async ValueTask ReadAsset(AssetFileEntry assetFile, MainThreadCreationContext switchToMainThread)
{
- using var r = new VorbisReader(stream, true);
+ using var stream = assetFile.OpenStream();
+ using var reader = new VorbisReader(stream, true);
- long bufferSize = r.TotalSamples * r.Channels;
+ long bufferSize = reader.TotalSamples * reader.Channels;
float[] data = new float[bufferSize];
- r.ReadSamples(data, 0, (int)bufferSize);
+ reader.ReadSamples(data, 0, (int)bufferSize);
var clip = new AudioClip();
- clip.SetData(data, r.Channels, sizeof(float), r.SampleRate);
+ clip.SetData(data, reader.Channels, sizeof(float), reader.SampleRate);
return clip;
}
diff --git a/Src/IO/Readers/Audio/WavReader.cs b/Src/IO/Readers/Audio/WavReader.cs
index 3cf33f3e..7a202402 100644
--- a/Src/IO/Readers/Audio/WavReader.cs
+++ b/Src/IO/Readers/Audio/WavReader.cs
@@ -9,8 +9,9 @@ public class WavReader : IAssetReader
{
public string[] Extensions { get; } = { ".wav" };
- public async ValueTask ReadFromStream(Stream stream, string assetPath, MainThreadCreationContext switchToMainThread)
+ public async ValueTask ReadAsset(AssetFileEntry assetFile, MainThreadCreationContext switchToMainThread)
{
+ using var stream = assetFile.OpenStream();
using var reader = new BinaryReader(stream);
// RIFF header
diff --git a/Src/IO/Readers/BytesReader.cs b/Src/IO/Readers/BytesReader.cs
index 59bc6b2f..52e5657f 100644
--- a/Src/IO/Readers/BytesReader.cs
+++ b/Src/IO/Readers/BytesReader.cs
@@ -7,8 +7,10 @@ public sealed class BytesReader : IAssetReader
{
public string[] Extensions { get; } = { ".bytes" };
- public async ValueTask ReadFromStream(Stream stream, string assetPath, MainThreadCreationContext switchToMainThread)
+ public async ValueTask ReadAsset(AssetFileEntry assetFile, MainThreadCreationContext switchToMainThread)
{
+ using var stream = assetFile.OpenStream();
+
byte[] bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
diff --git a/Src/IO/Readers/Graphics/MaterialReader.cs b/Src/IO/Readers/Graphics/MaterialReader.cs
index 02628690..e715ec78 100644
--- a/Src/IO/Readers/Graphics/MaterialReader.cs
+++ b/Src/IO/Readers/Graphics/MaterialReader.cs
@@ -29,8 +29,9 @@ private class JsonMaterial
public string[] Extensions { get; } = { ".material" };
- public async ValueTask ReadFromStream(Stream stream, string assetPath, MainThreadCreationContext switchToMainThread)
+ public async ValueTask ReadAsset(AssetFileEntry assetFile, MainThreadCreationContext switchToMainThread)
{
+ string assetPath = assetFile.Path;
string directory = Assets.FilterPath(Path.GetDirectoryName(assetPath));
var jsonMat = Assets.Get(assetPath, AssetRequestMode.ImmediateLoad).Value.ToObject();
diff --git a/Src/IO/Readers/Graphics/Models/GltfReader.cs b/Src/IO/Readers/Graphics/Models/GltfReader.cs
index 65349653..2af48bd4 100644
--- a/Src/IO/Readers/Graphics/Models/GltfReader.cs
+++ b/Src/IO/Readers/Graphics/Models/GltfReader.cs
@@ -45,10 +45,13 @@ public partial class GltfReader : IAssetReader
public string[] Extensions { get; } = { ".gltf", ".glb" };
- public async ValueTask ReadFromStream(Stream stream, string assetPath, MainThreadCreationContext switchToMainThread)
+ public async ValueTask ReadAsset(AssetFileEntry assetFile, MainThreadCreationContext switchToMainThread)
{
+ string assetPath = assetFile.Path;
var info = new GltfInfo(assetPath);
+ using var stream = assetFile.OpenStream();
+
if (assetPath.EndsWith(".gltf")) {
byte[] textBytes = new byte[stream.Length - stream.Position];
diff --git a/Src/IO/Readers/Graphics/Models/ObjReader.cs b/Src/IO/Readers/Graphics/Models/ObjReader.cs
index 16d032d5..b0ab8101 100644
--- a/Src/IO/Readers/Graphics/Models/ObjReader.cs
+++ b/Src/IO/Readers/Graphics/Models/ObjReader.cs
@@ -20,13 +20,13 @@ internal struct MeshInfo
public string[] Extensions { get; } = { ".obj" };
- public async ValueTask ReadFromStream(Stream stream, string assetPath, MainThreadCreationContext switchToMainThread)
+ public async ValueTask ReadAsset(AssetFileEntry assetFile, MainThreadCreationContext switchToMainThread)
{
- string text;
+ using var stream = assetFile.OpenStream();
+ using var reader = new StreamReader(stream);
- using (var reader = new StreamReader(stream)) {
- text = reader.ReadToEnd();
- }
+ string assetPath = assetFile.Path;
+ string text = reader.ReadToEnd();
float scale = 1f;
var meshInfo = CreateOBJInfo(text);
diff --git a/Src/IO/Readers/Graphics/ShaderReader.cs b/Src/IO/Readers/Graphics/ShaderReader.cs
index f0312ac6..5a270d5f 100644
--- a/Src/IO/Readers/Graphics/ShaderReader.cs
+++ b/Src/IO/Readers/Graphics/ShaderReader.cs
@@ -2,7 +2,6 @@
using System.IO;
using System.Threading.Tasks;
using Dissonance.Engine.Graphics;
-using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Dissonance.Engine.IO
@@ -13,9 +12,12 @@ public partial class ShaderReader : IAssetReader[]>
public bool AutoloadAssets => !Game.Instance.Flags.HasFlag(GameFlags.NoGraphics);
- public async ValueTask[]> ReadFromStream(Stream stream, string assetPath, MainThreadCreationContext switchToMainThread)
+ public async ValueTask[]> ReadAsset(AssetFileEntry assetFile, MainThreadCreationContext switchToMainThread)
{
+ string assetPath = assetFile.Path;
string directory = Assets.FilterPath(Path.GetDirectoryName(assetPath));
+
+ using var stream = assetFile.OpenStream();
using var reader = new StreamReader(stream);
string jsonText = reader.ReadToEnd();
diff --git a/Src/IO/Readers/HjsonReader.cs b/Src/IO/Readers/HjsonReader.cs
index 4be84df4..96748bf2 100644
--- a/Src/IO/Readers/HjsonReader.cs
+++ b/Src/IO/Readers/HjsonReader.cs
@@ -11,8 +11,9 @@ public class HjsonReader : IAssetReader, IAssetReader
public string[] Extensions { get; } = new[] { "*", ".hjson" };
// Newtonsoft.Json
- async ValueTask IAssetReader.ReadFromStream(Stream stream, string assetPath, MainThreadCreationContext switchToMainThread)
+ async ValueTask IAssetReader.ReadAsset(AssetFileEntry assetFile, MainThreadCreationContext switchToMainThread)
{
+ string assetPath = assetFile.Path;
string hjsonText = Assets.Get(assetPath, AssetRequestMode.ImmediateLoad).Value;
using var hjsonReader = new StringReader(hjsonText);
@@ -23,8 +24,9 @@ async ValueTask IAssetReader.ReadFromStream(Stream stream, str
}
// System.Text.Json
- async ValueTask IAssetReader.ReadFromStream(Stream stream, string assetPath, MainThreadCreationContext switchToMainThread)
+ async ValueTask IAssetReader.ReadAsset(AssetFileEntry assetFile, MainThreadCreationContext switchToMainThread)
{
+ string assetPath = assetFile.Path;
string hjsonText = Assets.Get(assetPath, AssetRequestMode.ImmediateLoad).Value;
using var hjsonReader = new StringReader(hjsonText);
diff --git a/Src/IO/Readers/Physics/ConvexCollisionMeshManager.cs b/Src/IO/Readers/Physics/ConvexCollisionMeshManager.cs
index 3ae1df86..892afdc7 100644
--- a/Src/IO/Readers/Physics/ConvexCollisionMeshManager.cs
+++ b/Src/IO/Readers/Physics/ConvexCollisionMeshManager.cs
@@ -1,8 +1,6 @@
+using System.Threading.Tasks;
using Dissonance.Engine.Graphics;
using Dissonance.Engine.Physics;
-using System;
-using System.IO;
-using System.Threading.Tasks;
namespace Dissonance.Engine.IO
{
@@ -10,8 +8,9 @@ public class ConvexCollisionMeshManager : IAssetReader
{
public string[] Extensions { get; } = { ".obj" };
- public async ValueTask ReadFromStream(Stream stream, string assetPath, MainThreadCreationContext switchToMainThread)
+ public async ValueTask ReadAsset(AssetFileEntry assetFile, MainThreadCreationContext switchToMainThread)
{
+ string assetPath = assetFile.Path;
var mesh = Assets.Get(assetPath, AssetRequestMode.ImmediateLoad).Value;
var collisionMesh = new ConvexCollisionMesh();
diff --git a/Src/IO/Readers/TextReader.cs b/Src/IO/Readers/TextReader.cs
index 25cbb809..52cd5f16 100644
--- a/Src/IO/Readers/TextReader.cs
+++ b/Src/IO/Readers/TextReader.cs
@@ -7,8 +7,9 @@ public class TextReader : IAssetReader
{
public string[] Extensions { get; } = { "*", ".txt" };
- public async ValueTask ReadFromStream(Stream stream, string assetPath, MainThreadCreationContext switchToMainThread)
+ public async ValueTask ReadAsset(AssetFileEntry assetFile, MainThreadCreationContext switchToMainThread)
{
+ using var stream = assetFile.OpenStream();
using var reader = new StreamReader(stream);
return reader.ReadToEnd();
diff --git a/Src/IO/Readers/Textures/PngReader.cs b/Src/IO/Readers/Textures/PngReader.cs
index 491076bd..3fbc451d 100644
--- a/Src/IO/Readers/Textures/PngReader.cs
+++ b/Src/IO/Readers/Textures/PngReader.cs
@@ -12,8 +12,10 @@ public class PngReader : IAssetReader
{
public string[] Extensions { get; } = { ".png" };
- public async ValueTask ReadFromStream(Stream stream, string assetPath, MainThreadCreationContext switchToMainThread)
+ public async ValueTask ReadAsset(AssetFileEntry assetFile, MainThreadCreationContext switchToMainThread)
{
+ using var stream = assetFile.OpenStream();
+
var (width, height, pixels) = LoadImageData(stream);
await switchToMainThread; // Switches context to the main thread for texture uploading.