From ffe8954b8d496dc046f666449c1927bec45d12b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Mon, 20 May 2024 13:35:37 +0200 Subject: [PATCH] Load pngs from cache --- GlitchyEditor/src/Assets/AssetCache.bf | 13 ++ .../src/Assets/Importers/Interfaces.bf | 5 + .../src/Assets/Importers/TextureImporter.bf | 150 ++++++++++-- GlitchyEditor/src/EditorContentManager.bf | 221 +++++++----------- 4 files changed, 235 insertions(+), 154 deletions(-) diff --git a/GlitchyEditor/src/Assets/AssetCache.bf b/GlitchyEditor/src/Assets/AssetCache.bf index 1335e5d..e8c2b0b 100644 --- a/GlitchyEditor/src/Assets/AssetCache.bf +++ b/GlitchyEditor/src/Assets/AssetCache.bf @@ -230,4 +230,17 @@ class AssetCache return .Ok; } + + public Result OpenFileStream(CachedAsset asset, FileStream fileStream) + { + if (asset == null) + return .Err; + + Try!(fileStream.Close()); + Try!(fileStream.Open(asset.FilePath, .Read, .Read)); + + fileStream.Position = asset.DataOffset; + + return .Ok; + } } \ No newline at end of file diff --git a/GlitchyEditor/src/Assets/Importers/Interfaces.bf b/GlitchyEditor/src/Assets/Importers/Interfaces.bf index 6b9af64..b82e490 100644 --- a/GlitchyEditor/src/Assets/Importers/Interfaces.bf +++ b/GlitchyEditor/src/Assets/Importers/Interfaces.bf @@ -27,3 +27,8 @@ interface IAssetExporter Result Export(Stream stream, ProcessedResource processedObject, AssetExporterConfig config); } + +interface IProcessedAssetLoader +{ + Result Load(Stream stream); +} diff --git a/GlitchyEditor/src/Assets/Importers/TextureImporter.bf b/GlitchyEditor/src/Assets/Importers/TextureImporter.bf index b0fb416..fcbfbc5 100644 --- a/GlitchyEditor/src/Assets/Importers/TextureImporter.bf +++ b/GlitchyEditor/src/Assets/Importers/TextureImporter.bf @@ -5,6 +5,7 @@ using Bon; using GlitchyEngine.Content; using GlitchyEngine; using GlitchyEngine.Renderer; +using GlitchyEngine.Math; using static GlitchyEditor.Assets.Importers.LoadedTextureInfo; namespace GlitchyEditor.Assets.Importers; @@ -233,12 +234,21 @@ class TextureProcessorConfig : AssetProcessorConfig { [BonInclude] private GenerateMipMaps _generateMipMaps; + + [BonInclude] + private SamplerStateDescription _samplerStateDescription = .(); public GenerateMipMaps GenerateMipMaps { get => _generateMipMaps; set => SetIfChanged(ref _generateMipMaps, value); } + + public SamplerStateDescription SamplerStateDescription + { + get => _samplerStateDescription; + set => SetIfChanged(ref _samplerStateDescription, value); + } } abstract class ProcessedResource @@ -267,6 +277,8 @@ class ProcessedTexture : ProcessedResource public int Height = -1; public int Depth = -1; + public SamplerStateDescription SamplerStateDescription; + public override AssetType AssetType => .Texture; public class TextureSurface @@ -277,9 +289,11 @@ class ProcessedTexture : ProcessedResource public int Depth; public int MipLevel; public int ArraySlice; + public int LinePitch; + public int SlicePitch; [AllowAppend] - public this(int width, int height, int depth, Span data, int mipLevel, int arraySlice) + public this(int width, int height, int depth, Span data, int mipLevel, int arraySlice, int linePitch, int slicePitch) { uint8[] pixelData = append uint8[data.Length]; data.CopyTo(pixelData); @@ -290,6 +304,8 @@ class ProcessedTexture : ProcessedResource Depth = depth; MipLevel = mipLevel; ArraySlice = arraySlice; + LinePitch = linePitch; + SlicePitch = slicePitch; } public uint64 LoadRaw(int x, int y, int z, Format format, ComponentInfo component) @@ -389,13 +405,15 @@ class TextureProcessor : IAssetProcessor processedTexture.Width = importedTexture.TextureInfo.Width; processedTexture.Height = importedTexture.TextureInfo.Height; processedTexture.Depth = importedTexture.TextureInfo.Depth; + + processedTexture.SamplerStateDescription = config.SamplerStateDescription; processedTexture.SetSurfaceCount(importedTexture.TextureInfo.ArraySize, importedTexture.TextureInfo.MipMapCount); for (LoadedSurface loadedSurface in importedTexture.Surfaces) { ProcessedTexture.TextureSurface surface = new .(loadedSurface.Width, loadedSurface.Height, loadedSurface.Depth, - loadedSurface.Data, loadedSurface.MipLevel, loadedSurface.ArrayIndex); + loadedSurface.Data, loadedSurface.MipLevel, loadedSurface.ArrayIndex, loadedSurface.Pitch, loadedSurface.SlicePitch); processedTexture.Surfaces[loadedSurface.ArrayIndex, loadedSurface.MipLevel] = surface; } @@ -499,7 +517,9 @@ class TextureProcessor : IAssetProcessor if (smallerLevel == null) { - smallerLevel = new ProcessedTexture.TextureSurface(width, height, depth, new uint8[width * height * depth * pixelFormat.BitsPerPixel()], largerLevel.MipLevel + 1, largerLevel.ArraySlice); + smallerLevel = new ProcessedTexture.TextureSurface(width, height, depth, + new uint8[width * height * depth * pixelFormat.BitsPerPixel()], largerLevel.MipLevel + 1, largerLevel.ArraySlice, + -1, -1); // TODO! } // TODO: Kaiser mip maps? @@ -556,6 +576,8 @@ class TextureExporter : IAssetExporter Depth of larges mip-slice (4 bytes) Array size (4 bytes) Mip map levels (4 bytes) + Is Cubemap (1 byte) + Data Byte count (8 bytes) Pixeldata { Array[0]: Mip[0] Mip[1] ... Mip[M] @@ -573,6 +595,9 @@ class TextureExporter : IAssetExporter Try!(stream.Write((uint32)processedTexture.Depth)); Try!(stream.Write((uint32)processedTexture.ArraySize)); Try!(stream.Write((uint32)processedTexture.MipMapCount)); + Try!(stream.Write(processedTexture.IsCubeMap)); + + Try!(WriteSamplerStateDescription(stream, processedTexture.SamplerStateDescription)); for (int arraySlice < processedTexture.ArraySize) { @@ -599,29 +624,122 @@ class TextureExporter : IAssetExporter if (validateDepth < 1) validateDepth = 1; - + + Try!(stream.Write((uint32)slice.LinePitch)); + Try!(stream.Write((uint32)slice.SlicePitch)); + Try!(stream.Write((uint64)slice.PixelData.Count)); Try!(stream.TryWrite(slice.PixelData)); } } return .Ok; } -} -class TextureLoader -{ - public Result Load(Stream data, AssetIdentifier assetIdentifier) + private Result WriteSamplerStateDescription(Stream stream, SamplerStateDescription sampler) { - Dimension dimension = Try!(data.Read()); - Format pixelFormat = Try!(data.Read()); - uint32 width = Try!(data.Read()); - uint32 height = Try!(data.Read()); - uint32 depth = Try!(data.Read()); - uint32 arraySize = Try!(data.Read()); - uint32 mipMapCount = Try!(data.Read()); + Try!(stream.Write(sampler.MinFilter)); + Try!(stream.Write(sampler.MagFilter)); + Try!(stream.Write(sampler.MipFilter)); + Try!(stream.Write(sampler.FilterMode)); + Try!(stream.Write(sampler.ComparisonFunction)); + Try!(stream.Write(sampler.AddressModeU)); + Try!(stream.Write(sampler.AddressModeV)); + Try!(stream.Write(sampler.AddressModeW)); + Try!(stream.Write(sampler.MipLODBias)); + Try!(stream.Write(sampler.MipMinLOD)); + Try!(stream.Write(sampler.MipMaxLOD)); + Try!(stream.Write(sampler.MaxAnisotropy)); + Try!(stream.Write(sampler.BorderColor)); + return .Ok; + } +} +class TextureLoader : IProcessedAssetLoader +{ + public Result Load(Stream dataStream) + { + Dimension dimension = Try!(dataStream.Read()); + Format pixelFormat = Try!(dataStream.Read()); + uint32 width = Try!(dataStream.Read()); + uint32 height = Try!(dataStream.Read()); + uint32 depth = Try!(dataStream.Read()); + uint32 arraySize = Try!(dataStream.Read()); + uint32 mipMapCount = Try!(dataStream.Read()); + bool isCubemap = Try!(dataStream.Read()); - return .Ok(null); + SamplerStateDescription sampler = Try!(ReadSampler(dataStream)); + + List surfaceDatas = scope .(mipMapCount * arraySize); + defer { ClearAndDeleteItems!(surfaceDatas); } + + TextureSliceData[] slices = scope TextureSliceData[mipMapCount * arraySize]; + + int index = 0; + for (int arraySlice < arraySize) + { + for (int mipSlice < mipMapCount) + { + uint32 linePitch = Try!(dataStream.Read()); + uint32 slicePitch = Try!(dataStream.Read()); + uint64 byteCount = Try!(dataStream.Read()); + + uint8[] surfaceData = new uint8[byteCount]; + Try!(dataStream.TryRead(surfaceData)); + slices[index] = .(surfaceData.Ptr, linePitch, slicePitch); + + index++; + + surfaceDatas.Add(surfaceData); + } + } + + Texture result = null; + + switch (dimension) + { + case .Texture2D: + if (isCubemap) + { + Runtime.NotImplemented(); + } + else + { + Texture2DDesc desc = .(width, height, pixelFormat, arraySize, mipMapCount, .Immutable, .None); + + Texture2D texture = new Texture2D(desc); + + texture.SetData(slices); + + result = texture; + } + default: + Runtime.NotImplemented(); + } + + result.SamplerState = SamplerStateManager.GetSampler(sampler); + + return result; + } + + private Result ReadSampler(Stream dataStream) + { + SamplerStateDescription sampler; + + sampler.MinFilter = Try!(dataStream.Read()); + sampler.MagFilter = Try!(dataStream.Read()); + sampler.MipFilter = Try!(dataStream.Read()); + sampler.FilterMode = Try!(dataStream.Read()); + sampler.ComparisonFunction = Try!(dataStream.Read()); + sampler.AddressModeU = Try!(dataStream.Read()); + sampler.AddressModeV = Try!(dataStream.Read()); + sampler.AddressModeW = Try!(dataStream.Read()); + sampler.MipLODBias = Try!(dataStream.Read()); + sampler.MipMinLOD = Try!(dataStream.Read()); + sampler.MipMaxLOD = Try!(dataStream.Read()); + sampler.MaxAnisotropy = Try!(dataStream.Read()); + sampler.BorderColor = Try!(dataStream.Read()); + + return sampler; } } diff --git a/GlitchyEditor/src/EditorContentManager.bf b/GlitchyEditor/src/EditorContentManager.bf index 010e33b..fdb5c2b 100644 --- a/GlitchyEditor/src/EditorContentManager.bf +++ b/GlitchyEditor/src/EditorContentManager.bf @@ -469,70 +469,73 @@ class EditorContentManager : IContentManager Log.EngineLogger.Error($"Could not find asset with handle {handle}."); return .Invalid; } + + AssetNode assetNode = resultNode->Value; + AssetFile file = assetNode.AssetFile; CachedAsset cacheEntry = _assetCache.GetCacheEntry(handle); + + Asset loadedAsset; + // TODO: Remove this check once we no longer need the old stuff if (cacheEntry != null) { // TODO: Load asset with new loaders - } - - String filePath = scope String(resultNode->Value.Path); - - AssetNode assetNode = resultNode->Value; - AssetFile file = assetNode.AssetFile; - - GetResourceAndSubassetName(assetNode.Identifier, let resourceName, let subassetName); - - IAssetLoader assetLoader = GetAssetLoader(file); - - //Log.EngineLogger.AssertDebug(assetLoader != null); - if (assetLoader == null) - { - Log.EngineLogger.Error($"No asset loader registered for asset \"{resultNode->Value.Identifier}\" ({handle})."); - return .Invalid; - } - - Asset loadedAsset; - - // TODO: Support lazy loading for all asset types - if (!(assetLoader is EditorTextureAssetLoader) || blocking) - { - Stream stream = OpenStream(filePath, true); - - loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config, resourceName, subassetName, this); - - delete stream; - - if (loadedAsset == null) - return .Invalid; + loadedAsset = LoadFromCache(handle, blocking); } else { - PlaceholderAsset placeholder = new PlaceholderAsset(file, assetLoader, .Loading); + // else use the old mess... + + String filePath = scope String(resultNode->Value.Path); - String filePath2 = new String(filePath); - String newResourceName = new String(resourceName); - String newSesourceName = subassetName == null ? null : new String(subassetName.Value); + GetResourceAndSubassetName(assetNode.Identifier, let resourceName, let subassetName); - placeholder.LoadingTask = new Task(new () => { - AsyncLoadAsset(placeholder, filePath2, assetLoader, file, - newResourceName, newSesourceName); - }); + IAssetLoader assetLoader = GetAssetLoader(file); - ThreadPool.QueueUserWorkItem(placeholder.LoadingTask); + //Log.EngineLogger.AssertDebug(assetLoader != null); + if (assetLoader == null) + { + Log.EngineLogger.Error($"No asset loader registered for asset \"{resultNode->Value.Identifier}\" ({handle})."); + return .Invalid; + } - loadedAsset = placeholder; + // TODO: Support lazy loading for all asset types + if (!(assetLoader is EditorTextureAssetLoader) || blocking) + { + Stream stream = OpenStream(filePath, true); + + loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config, resourceName, subassetName, this); + + delete stream; + + if (loadedAsset == null) + return .Invalid; + } + else + { + PlaceholderAsset placeholder = new PlaceholderAsset(file, assetLoader, .Loading); + + String filePath2 = new String(filePath); + String newResourceName = new String(resourceName); + String newSesourceName = subassetName == null ? null : new String(subassetName.Value); + + placeholder.LoadingTask = new Task(new () => { + AsyncLoadAsset(placeholder, filePath2, assetLoader, file, + newResourceName, newSesourceName); + }); + + ThreadPool.QueueUserWorkItem(placeholder.LoadingTask); + + loadedAsset = placeholder; + } } loadedAsset.Identifier = assetNode.Identifier; - - _handleToAsset.Add(handle, loadedAsset); - loadedAsset.[Friend]_contentManager = this; loadedAsset.[Friend]_handle = handle; - // Add to Identifier -> Handle map + _handleToAsset.Add(handle, loadedAsset); _identiferToHandle.Add(loadedAsset.Identifier, handle); file.[Friend]_loadedAsset = loadedAsset; @@ -567,99 +570,6 @@ class EditorContentManager : IContentManager return .Invalid; } - - //Result> resultNode = AssetHierarchy.GetNodeFromIdentifier(fixedIdentifier); - - //resultNode. - - /* - if (_identiferToHandle.TryGetValue(fixedIdentifier, let asset)) - return asset; - - GetResourceAndSubassetName(fixedIdentifier, let resourceName, let subassetName); - - Result> resultNode = AssetHierarchy.GetNodeFromIdentifier(fixedIdentifier); - - if (resultNode case .Err) - { - Log.EngineLogger.Error($"Could not find asset \"{fixedIdentifier}\"."); - return .Invalid; - } - - String filePath = scope String(resultNode->Value.Path); - - AssetFile file = resultNode->Value.AssetFile; - - IAssetLoader assetLoader = GetAssetLoader(file); - - // TODO: what are we supposed to do if we don't find a loader? Surely not crash... - //Log.EngineLogger.AssertDebug(assetLoader != null); - if (assetLoader == null) - { - Log.EngineLogger.Error($"No asset loader registered for asset {identifier}."); - return .Invalid; - } - - Asset loadedAsset; - - // TODO: Support lazy loading for all asset types - if (!(assetLoader is EditorTextureAssetLoader) || blocking) - { - Stream stream = OpenStream(filePath, true); - - loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config, resourceName, subassetName, this); - - delete stream; - - if (loadedAsset == null) - return .Invalid; - } - else - { - PlaceholderAsset placeholder = new PlaceholderAsset(file, assetLoader, .Loading); - - String filePath2 = new String(filePath); - String newResourceName = new String(resourceName); - String newSesourceName = subassetName == null ? null : new String(subassetName.Value); - - placeholder.LoadingTask = new Task(new () => { - AsyncLoadAsset(placeholder, filePath2, assetLoader, file, - newResourceName, newSesourceName); - }); - - ThreadPool.QueueUserWorkItem(placeholder.LoadingTask); - - loadedAsset = placeholder; - } - - loadedAsset.Identifier = fixedIdentifier; - - AssetHandle handle = .Invalid; - - if (file.AssetConfig.AssetHandle == .Invalid) - { - handle = ManageAsset(loadedAsset); - // TODO: Does this ever happen? - file.AssetConfig.AssetHandle = handle; - } - else - { - handle = file.AssetConfig.AssetHandle; - _handleToAsset.Add(handle, loadedAsset); - - loadedAsset.[Friend]_contentManager = this; - loadedAsset.[Friend]_handle = handle; - } - - // ManageAsset increases RefCount, but this scope also holds a reference. - loadedAsset.ReleaseRef(); - - // Add to Identifier -> Handle map - _identiferToHandle.Add(loadedAsset.Identifier, handle); - - file.[Friend]_loadedAsset = loadedAsset; - - return handle;*/ } private void AsyncLoadAsset(PlaceholderAsset placeholder, String filePath, IAssetLoader assetLoader, AssetFile file, String resourceName, String subassetName) @@ -998,4 +908,39 @@ class EditorContentManager : IContentManager //UnmanageAsset(asset); //ManageAsset(asset); } + + // TODO: obviously use a map or something... + TextureLoader textureLoader = new .(); + + private IProcessedAssetLoader GetLoader(AssetType assetType) + { + switch (assetType) + { + case .Texture: + return textureLoader; + default: + return null; + } + } + + private Asset LoadFromCache(AssetHandle handle, bool isBlocking) + { + FileStream file = scope .(); + + CachedAsset asset = _assetCache.GetCacheEntry(handle); + + // TODO: Actually return a placeholder, but they probably need rework too... + if (_assetCache.OpenFileStream(asset, file) case .Err) + return null; + + IProcessedAssetLoader loader = GetLoader(asset.AssetType); + + switch (loader.Load(file)) + { + case .Ok(let loadedAsset): + return loadedAsset; + case .Err: + return null; + } + } } \ No newline at end of file