Convert texture asset and save to cache

This commit is contained in:
Simon Lübeß
2024-05-20 01:08:18 +02:00
parent 2725801db8
commit 1a43ff2971
5 changed files with 223 additions and 26 deletions
+83 -7
View File
@@ -4,6 +4,7 @@ using System.IO;
using System.Collections; using System.Collections;
using GlitchyEngine.Core; using GlitchyEngine.Core;
using GlitchyEngine.Content; using GlitchyEngine.Content;
using System.IO;
namespace GlitchyEditor.Assets; namespace GlitchyEditor.Assets;
@@ -21,7 +22,9 @@ class CachedAsset
public AssetType AssetType; public AssetType AssetType;
public int64 CompressedByteCount; public int64 CompressedByteCount;
public int64 UncompressedByteCount; public int64 UncompressedByteCount;
public String AssetIdentifier ~ delete _; public AssetIdentifier AssetIdentifier ~ delete _;
public int32 DataOffset;
public const String CacheFileExtension = ".laf"; public const String CacheFileExtension = ".laf";
} }
@@ -93,6 +96,7 @@ class AssetCache
Log.EngineLogger.Error($"Failed to read cached asset file \"filePath\"."); Log.EngineLogger.Error($"Failed to read cached asset file \"filePath\".");
delete cachedAsset; delete cachedAsset;
continue;
} }
_assets.Add(cachedAsset.Handle, cachedAsset); _assets.Add(cachedAsset.Handle, cachedAsset);
@@ -129,29 +133,101 @@ class AssetCache
cachedAsset.CompressedByteCount = Try!(stream.Read<int64>()); cachedAsset.CompressedByteCount = Try!(stream.Read<int64>());
cachedAsset.UncompressedByteCount = Try!(stream.Read<int64>()); cachedAsset.UncompressedByteCount = Try!(stream.Read<int64>());
int32 assetIdentifierByteCount = Try!(stream.Read<int32>()); int16 assetIdentifierByteCount = Try!(stream.Read<int16>());
cachedAsset.AssetIdentifier = new String(assetIdentifierByteCount); String assetIdentifier = scope String(assetIdentifierByteCount);
cachedAsset.AssetIdentifier.PadLeft(assetIdentifierByteCount); assetIdentifier.PadLeft(assetIdentifierByteCount);
Span<char8> charSpan = cachedAsset.AssetIdentifier; Span<char8> charSpan = assetIdentifier;
int readBytes = Try!(stream.TryRead(Span<uint8>((uint8*)charSpan.Ptr, charSpan.Length))); int readBytes = Try!(stream.TryRead(Span<uint8>((uint8*)charSpan.Ptr, charSpan.Length)));
cachedAsset.AssetIdentifier = new AssetIdentifier(assetIdentifier);
if (readBytes != assetIdentifierByteCount) if (readBytes != assetIdentifierByteCount)
{ {
Log.EngineLogger.Warning($"Expected to read {assetIdentifierByteCount} bytes for the asset identifier, but read {readBytes} instead."); Log.EngineLogger.Warning($"Expected to read {assetIdentifierByteCount} bytes for the asset identifier, but read {readBytes} instead.");
} }
cachedAsset.DataOffset = (.)stream.Position;
return .Ok; return .Ok;
} }
public CachedAsset GetCacheEntry(AssetHandle id) public CachedAsset GetCacheEntry(AssetHandle assetHandle)
{ {
if (!_cacheLoaded) if (!_cacheLoaded)
ReloadCache(); ReloadCache();
if (_assets.TryGetValue(id, let cachedAsset)) if (_assets.TryGetValue(assetHandle, let cachedAsset))
return cachedAsset; return cachedAsset;
return null; return null;
} }
private void GetCacheFilePath(AssetHandle handle, String outFilePath)
{
outFilePath.Append(_directory);
outFilePath.Append(Path.DirectorySeparatorChar);
outFilePath.Append(handle);
outFilePath.Append(CachedAsset.CacheFileExtension);
}
public Result<void> SaveAsset(CachedAsset assetInfo, Span<uint8> data)
{
CachedAsset asset = GetCacheEntry(assetInfo.Handle);
if (asset == null)
{
asset = new CachedAsset();
asset.Handle = assetInfo.Handle;
asset.FilePath = new String(128);
GetCacheFilePath(asset.Handle, asset.FilePath);
_assets[asset.Handle] = asset;
}
delete asset.AssetIdentifier;
asset.AssetIdentifier = new AssetIdentifier(assetInfo.AssetIdentifier);
asset.FormatVersion = FormatVersion;
asset.Compression = assetInfo.Compression;
asset.AssetType = assetInfo.AssetType;
asset.CreationTimestamp = assetInfo.CreationTimestamp;
asset.UncompressedByteCount = data.Length;
Span<uint8> dataToWrite;
switch (asset.Compression)
{
case .L4Z:
// TODO: L4Z
Runtime.NotImplemented();
// asset.CompressedByteCount = compressedData.Length;
case .None:
asset.CompressedByteCount = data.Length;
dataToWrite = data;
}
FileStream writer = scope .();
Try!(writer.Create(asset.FilePath, .Write, .None));
Try!(writer.Write<char8[3]>(CachedAsset.MagicWord));
Try!(writer.Write(FormatVersion));
Try!(writer.Write(asset.Handle));
Try!(writer.Write(asset.CreationTimestamp));
Try!(writer.Write(asset.Compression));
Try!(writer.Write(asset.AssetType));
Try!(writer.Write<int64>(asset.CompressedByteCount));
Try!(writer.Write<int64>(asset.UncompressedByteCount));
Try!(writer.Write<int16>((int16)asset.AssetIdentifier.FullIdentifier.Length));
Try!(writer.Write(asset.AssetIdentifier.FullIdentifier));
asset.DataOffset = (.)writer.Position;
Try!(writer.Write(dataToWrite));
// TODO: Notify content manager!
return .Ok;
}
} }
+59 -3
View File
@@ -1,12 +1,15 @@
using System.Collections; using System.Collections;
using GlitchyEditor.Assets.Importers; using GlitchyEditor.Assets.Importers;
using GlitchyEngine; using GlitchyEngine;
using System;
using System.IO;
using GlitchyEngine.Content;
namespace GlitchyEditor.Assets; namespace GlitchyEditor.Assets;
class AssetConverter class AssetConverter
{ {
private append Queue<AssetFile> _queue = .() ~ delete:append _; private append Queue<AssetFile> _queue = .();
private EditorContentManager _contentManager; private EditorContentManager _contentManager;
@@ -36,15 +39,68 @@ class AssetConverter
private void Process(AssetFile assetFile) private void Process(AssetFile assetFile)
{ {
IAssetImporter importer = _contentManager.GetAssetImporter(assetFile); IAssetImporter importer = _contentManager.GetAssetImporter(assetFile);
IAssetProcessor processor = _contentManager.GetAssetProcessor(assetFile);
IAssetExporter exporter = _contentManager.GetAssetExporter(assetFile);
if (importer == null) if (importer == null)
{ {
Log.EngineLogger.Error("Importer is null!"); Log.EngineLogger.Error("Importer is null!");
return;
}
if (processor == null)
{
Log.EngineLogger.Error("Processor is null!");
return;
}
if (exporter == null)
{
Log.EngineLogger.Error("Exporter is null!");
return;
} }
ImportedResource importedResource = importer.Import(assetFile.AssetFile.Path, Result<ImportedResource> importResult = importer.Import(assetFile.AssetFile.Path,
assetFile.AssetFile.Identifier, assetFile.AssetConfig.ImporterConfig); assetFile.AssetFile.Identifier, assetFile.AssetConfig.ImporterConfig);
delete importedResource; if (importResult case .Err)
{
Log.EngineLogger.Error("Failed to import asset!");
return;
}
defer { delete importResult.Value; }
Result<ProcessedResource> processResult = processor.Process(importResult.Value, assetFile.AssetConfig.ProcessorConfig);
if (processResult case .Err)
{
Log.EngineLogger.Error("Failed to process asset!");
return;
}
defer { delete processResult.Value; }
MemoryStream memoryStream = scope .();
Result<void> exportResult = exporter.Export(memoryStream, processResult.Value, assetFile.AssetConfig.ExporterConfig);
if (exportResult case .Err)
{
Log.EngineLogger.Error("Failed to export asset!");
return;
}
CachedAsset assetInfo = scope CachedAsset();
assetInfo.Handle = assetFile.AssetConfig.AssetHandle;
assetInfo.CreationTimestamp = DateTime.UtcNow;
assetInfo.Compression = assetFile.AssetConfig.ExporterConfig.Compression;
assetInfo.AssetIdentifier = new AssetIdentifier(assetFile.AssetFile.Identifier);
assetInfo.AssetType = processResult.Value.AssetType;
if (_contentManager.AssetCache.SaveAsset(assetInfo, memoryStream.Memory) case .Err)
{
Log.EngineLogger.Error("Failed to write asset to cache.");
}
} }
} }
@@ -18,7 +18,7 @@ interface IAssetProcessor
{ {
AssetProcessorConfig CreateDefaultConfig(); AssetProcessorConfig CreateDefaultConfig();
Result<Object> Process(ImportedResource importedResource, AssetProcessorConfig config); Result<ProcessedResource> Process(ImportedResource importedResource, AssetProcessorConfig config);
} }
interface IAssetExporter interface IAssetExporter
@@ -186,6 +186,9 @@ class TextureImporter : IAssetImporter
surface.SlicePitch = 0; surface.SlicePitch = 0;
surface.ArrayIndex = 0; surface.ArrayIndex = 0;
surface.MipLevel = 0; surface.MipLevel = 0;
surface.Width = width;
surface.Height = height;
surface.Depth = 1;
surfaces.Add(surface); surfaces.Add(surface);
@@ -238,12 +241,14 @@ class TextureProcessorConfig : AssetProcessorConfig
} }
} }
class ProcessedResource abstract class ProcessedResource
{ {
private AssetIdentifier _assetIdentifier ~ delete _; private AssetIdentifier _assetIdentifier ~ delete _;
public AssetIdentifier AssetIdentifier => _assetIdentifier; public AssetIdentifier AssetIdentifier => _assetIdentifier;
public abstract AssetType AssetType {get;}
public this(AssetIdentifier ownAssetIdentifier) public this(AssetIdentifier ownAssetIdentifier)
{ {
_assetIdentifier = ownAssetIdentifier; _assetIdentifier = ownAssetIdentifier;
@@ -262,6 +267,8 @@ class ProcessedTexture : ProcessedResource
public int Height = -1; public int Height = -1;
public int Depth = -1; public int Depth = -1;
public override AssetType AssetType => .Texture;
public class TextureSurface public class TextureSurface
{ {
public uint8[] PixelData; public uint8[] PixelData;
@@ -335,17 +342,23 @@ class ProcessedTexture : ProcessedResource
public void SetSurfaceCount(int arraySize, int mipMapCount) public void SetSurfaceCount(int arraySize, int mipMapCount)
{ {
Log.EngineLogger.AssertDebug(arraySize > Surfaces.GetLength(0)); Log.EngineLogger.AssertDebug(Surfaces == null || arraySize > Surfaces.GetLength(0));
Log.EngineLogger.AssertDebug(mipMapCount > Surfaces.GetLength(1)); Log.EngineLogger.AssertDebug(Surfaces == null ||mipMapCount > Surfaces.GetLength(1));
ArraySize = arraySize;
MipMapCount = mipMapCount;
TextureSurface[,] oldSurfaces = Surfaces; TextureSurface[,] oldSurfaces = Surfaces;
Surfaces = new TextureSurface[arraySize, mipMapCount]; Surfaces = new TextureSurface[arraySize, mipMapCount];
for (int i < oldSurfaces.GetLength(0)) if (oldSurfaces != null)
{ {
for (int j < oldSurfaces.GetLength(1)) for (int i < oldSurfaces.GetLength(0))
{ {
Surfaces[i, j] = oldSurfaces[i, j]; for (int j < oldSurfaces.GetLength(1))
{
Surfaces[i, j] = oldSurfaces[i, j];
}
} }
} }
} }
@@ -358,20 +371,25 @@ class TextureProcessor : IAssetProcessor
return new TextureProcessorConfig(); return new TextureProcessorConfig();
} }
public Result<Object> Process(ImportedResource importedObject, AssetProcessorConfig config) public Result<ProcessedResource> Process(ImportedResource importedObject, AssetProcessorConfig config)
{ {
Log.EngineLogger.AssertDebug(config is TextureProcessorConfig); Log.EngineLogger.AssertDebug(config is TextureProcessorConfig);
Log.EngineLogger.AssertDebug(importedObject is ImportedTexture); Log.EngineLogger.AssertDebug(importedObject is ImportedTexture);
Try!(ProcessTexture(importedObject as ImportedTexture, config as TextureProcessorConfig)); return Try!(ProcessTexture(importedObject as ImportedTexture, config as TextureProcessorConfig));
return .Ok(null);
} }
private Result<void> ProcessTexture(ImportedTexture importedTexture, TextureProcessorConfig config) private Result<ProcessedTexture> ProcessTexture(ImportedTexture importedTexture, TextureProcessorConfig config)
{ {
ProcessedTexture processedTexture = new ProcessedTexture(new AssetIdentifier(importedTexture.AssetIdentifier.FullIdentifier)); ProcessedTexture processedTexture = new ProcessedTexture(new AssetIdentifier(importedTexture.AssetIdentifier.FullIdentifier));
processedTexture.Dimension = importedTexture.TextureInfo.Dimension;
processedTexture.PixelFormat = (.)importedTexture.TextureInfo.PixelFormat;
processedTexture.IsCubeMap = importedTexture.TextureInfo.IsCubeMap;
processedTexture.Width = importedTexture.TextureInfo.Width;
processedTexture.Height = importedTexture.TextureInfo.Height;
processedTexture.Depth = importedTexture.TextureInfo.Depth;
processedTexture.SetSurfaceCount(importedTexture.TextureInfo.ArraySize, importedTexture.TextureInfo.MipMapCount); processedTexture.SetSurfaceCount(importedTexture.TextureInfo.ArraySize, importedTexture.TextureInfo.MipMapCount);
for (LoadedSurface loadedSurface in importedTexture.Surfaces) for (LoadedSurface loadedSurface in importedTexture.Surfaces)
@@ -390,7 +408,7 @@ class TextureProcessor : IAssetProcessor
// TODO: Pack to BC-Format or what ever was selected. // TODO: Pack to BC-Format or what ever was selected.
return .Ok; return processedTexture;
} }
private int CalculateMipMapCount(int width, int height, int depth) private int CalculateMipMapCount(int width, int height, int depth)
+49 -2
View File
@@ -27,7 +27,7 @@ class EditorContentManager : IContentManager
private append AssetHierarchy _assetHierarchy = .(this); private append AssetHierarchy _assetHierarchy = .(this);
private append AssetCache _assetCache = .() ~ delete:append _; private append AssetCache _assetCache = .() ~ delete:append _;
private append AssetConverter _assetConverter = .(this) ~ delete:append _; private append AssetConverter _assetConverter = .(this);
private append List<AssetHandle> _reloadQueue = .(); private append List<AssetHandle> _reloadQueue = .();
@@ -469,7 +469,14 @@ class EditorContentManager : IContentManager
Log.EngineLogger.Error($"Could not find asset with handle {handle}."); Log.EngineLogger.Error($"Could not find asset with handle {handle}.");
return .Invalid; return .Invalid;
} }
CachedAsset cacheEntry = _assetCache.GetCacheEntry(handle);
if (cacheEntry != null)
{
// TODO: Load asset with new loaders
}
String filePath = scope String(resultNode->Value.Path); String filePath = scope String(resultNode->Value.Path);
AssetNode assetNode = resultNode->Value; AssetNode assetNode = resultNode->Value;
@@ -715,6 +722,46 @@ class EditorContentManager : IContentManager
return result; return result;
} }
public IAssetProcessor GetAssetProcessor(AssetFile file)
{
IAssetProcessor result = null;
String typeName = scope .(128);
for (IAssetProcessor processor in _assetProcessors)
{
processor.GetType().GetName(typeName..Clear());
if (typeName == file.AssetConfig.Processor)
{
result = processor;
break;
}
}
return result;
}
public IAssetExporter GetAssetExporter(AssetFile file)
{
IAssetExporter result = null;
String typeName = scope .(128);
for (IAssetExporter exporter in _assetExporters)
{
exporter.GetType().GetName(typeName..Clear());
if (typeName == file.AssetConfig.Exporter)
{
result = exporter;
break;
}
}
return result;
}
public enum SaveAssetError public enum SaveAssetError
{ {
case Unknown; case Unknown;