From 2725801db8c0f7aea83546889a1004d144d6b461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sun, 19 May 2024 01:35:52 +0200 Subject: [PATCH] Start of asset management 2.0 --- GlitchyEditor/src/AssetFile.bf | 93 +- GlitchyEditor/src/Assets/AssetCache.bf | 157 +++ GlitchyEditor/src/Assets/AssetConverter.bf | 50 + GlitchyEditor/src/Assets/AssetHierarchy.bf | 10 +- GlitchyEditor/src/Assets/AssetNode.bf | 1 + GlitchyEditor/src/Assets/Importers/Config.bf | 49 + .../src/Assets/Importers/DdsImporter.bf | 8 + .../src/Assets/Importers/Interfaces.bf | 29 + .../src/Assets/Importers/TextureImporter.bf | 609 ++++++++++ .../src/Assets/TextureAssetLoader.bf | 4 +- GlitchyEditor/src/EditWindows/AssetViewer.bf | 4 +- .../src/EditWindows/PropertiesWindow.bf | 4 +- GlitchyEditor/src/EditorApp.bf | 7 + GlitchyEditor/src/EditorContentManager.bf | 157 ++- GlitchyEditor/src/EditorLayer.bf | 1 + GlitchyEngine/src/Content/AssetCompression.bf | 7 + GlitchyEngine/src/Content/AssetType.bf | 7 + .../src/Platform/DX11/Renderer/Dx11Texture.bf | 4 +- .../DX11/Renderer/Dx11VertexLayout.bf | 2 +- GlitchyEngine/src/Renderer/Buffer.bf | 3 - GlitchyEngine/src/Renderer/Format.bf | 1038 +++++++++++++++++ 21 files changed, 2192 insertions(+), 52 deletions(-) create mode 100644 GlitchyEditor/src/Assets/AssetCache.bf create mode 100644 GlitchyEditor/src/Assets/AssetConverter.bf create mode 100644 GlitchyEditor/src/Assets/Importers/Config.bf create mode 100644 GlitchyEditor/src/Assets/Importers/Interfaces.bf create mode 100644 GlitchyEditor/src/Assets/Importers/TextureImporter.bf create mode 100644 GlitchyEngine/src/Content/AssetCompression.bf create mode 100644 GlitchyEngine/src/Content/AssetType.bf create mode 100644 GlitchyEngine/src/Renderer/Format.bf diff --git a/GlitchyEditor/src/AssetFile.bf b/GlitchyEditor/src/AssetFile.bf index d789443..b5867f3 100644 --- a/GlitchyEditor/src/AssetFile.bf +++ b/GlitchyEditor/src/AssetFile.bf @@ -3,6 +3,8 @@ using GlitchyEngine; using System.IO; using Bon; using GlitchyEngine.Content; +using GlitchyEditor.Assets; +using GlitchyEditor.Assets.Importers; namespace GlitchyEditor; @@ -18,28 +20,39 @@ class AssetConfig [BonInclude] public AssetLoaderConfig Config ~ delete _; + [BonInclude] + public String Importer ~ delete _; + [BonInclude] + public AssetImporterConfig ImporterConfig ~ delete _; + [BonInclude] + public String Processor ~ delete _; + [BonInclude] + public AssetProcessorConfig ProcessorConfig ~ delete _; + [BonInclude] + public String Exporter ~ delete _; + [BonInclude] + public AssetExporterConfig ExporterConfig ~ delete _; + [BonInclude] public AssetHandle AssetHandle = .Invalid; } +/// Represents an unprocessed asset as it lies in the asset hierarchy. class AssetFile { private EditorContentManager _contentManager; - private String _path ~ delete:append _; - private String _identifier ~ delete:append _; private String _assetConfigPath ~ delete:append _; + private AssetNode _assetFile; private AssetConfig _assetConfig ~ delete _; - private bool _isDirectory; - private Asset _loadedAsset; - public bool IsDirectory => _isDirectory; + private DateTime _lastAssetEditTime; + private DateTime _lastConfigEditTime; - public StringView FilePath => _path; - public StringView Identifier => _identifier; + public AssetNode AssetFile => _assetFile; public StringView AssetConfigPath => _assetConfigPath; public const String ConfigFileExtension = ".ass"; @@ -48,30 +61,47 @@ class AssetFile public Asset LoadedAsset => _loadedAsset; + public EditorContentManager ContentManager => _contentManager; + [AllowAppend] - public this(EditorContentManager contentManager, StringView identifier, StringView path, bool isDirectory) + public this(EditorContentManager contentManager, AssetNode assetNode) { - String identifierBuffer = append String(identifier); - String pathBuffer = append String(path); - String configPathBuffer = append String(path.Length + ConfigFileExtension.Length); + String configPathBuffer = append String(assetNode.Path.Length + ConfigFileExtension.Length); - _identifier = identifierBuffer; - _path = pathBuffer; - - configPathBuffer..Append(path).Append(ConfigFileExtension); + configPathBuffer..Append(assetNode.Path).Append(ConfigFileExtension); _assetConfigPath = configPathBuffer; _contentManager = contentManager; - _isDirectory = isDirectory; + _assetFile = assetNode; - Log.EngineLogger.AssertDebug(File.Exists(_path), "File doesn't exist."); - - FindAssetConfig(); + _lastAssetEditTime = File.GetLastWriteTimeUtc(_assetFile.Path); } - // Loads the asset config (.ass) file or creates it. - private void FindAssetConfig() + public static AssetFile LoadOrCreateAssetFile(EditorContentManager contentManager, AssetNode assetNode) + { + AssetFile assetFile = new AssetFile(contentManager, assetNode); + + assetFile.LoadOrCreateAssetConfig(); + + // TODO: Remove this check once we only use the new processing pipeline + if (assetFile._assetConfig.ImporterConfig != null) + { + CachedAsset cacheEntry = assetFile._contentManager.AssetCache.GetCacheEntry(assetFile._assetConfig.AssetHandle); + + if (cacheEntry == null || + cacheEntry.CreationTimestamp < assetFile._lastAssetEditTime || + cacheEntry.CreationTimestamp < assetFile._lastConfigEditTime) + { + assetFile._contentManager.AssetConverter.QueueForProcessing(assetFile); + } + } + + return assetFile; + } + + /// Loads the asset config (.ass) file or creates it. + private void LoadOrCreateAssetConfig() { if (File.Exists(_assetConfigPath)) { @@ -81,6 +111,8 @@ class AssetFile { CreateDefaultAssetLoader(); } + + _lastAssetEditTime = File.GetLastWriteTimeUtc(_assetConfigPath); } private void GenerateAssetHandle() @@ -90,12 +122,18 @@ class AssetFile private void CreateDefaultAssetLoader() { - String fileExtension = Path.GetExtension(_path, .. scope .()); + String fileExtension = Path.GetExtension(_assetFile.Path, .. scope .()); _assetConfig = new AssetConfig(); GenerateAssetHandle(); + var assetPipeline = _contentManager.GetDefaultProcessors(fileExtension); + + // TODO! + //if (assetPipeline case .Err) + // return; + var assetLoader = _contentManager.GetDefaultAssetLoader(fileExtension); // We don't have a loader -> we don't need a config @@ -108,6 +146,17 @@ class AssetFile _assetConfig.Config = assetLoader?.GetDefaultConfig(); _assetConfig.Config?.[Friend]_changed = true; + _assetConfig.Importer = new String(); + assetPipeline?.Importer?.GetType()?.GetName(_assetConfig.Importer); + _assetConfig.ImporterConfig = assetPipeline?.Importer.CreateDefaultConfig(); + + _assetConfig.Processor = new String(); + assetPipeline?.Processor?.GetType()?.GetName(_assetConfig.Processor); + _assetConfig.ProcessorConfig = assetPipeline?.Processor.CreateDefaultConfig(); + + _assetConfig.Exporter = new String(); + assetPipeline?.Exporter?.GetType()?.GetName(_assetConfig.Exporter); + _assetConfig.ExporterConfig = assetPipeline?.Exporter.CreateDefaultConfig(); SaveAssetConfig(); } diff --git a/GlitchyEditor/src/Assets/AssetCache.bf b/GlitchyEditor/src/Assets/AssetCache.bf new file mode 100644 index 0000000..89f27dd --- /dev/null +++ b/GlitchyEditor/src/Assets/AssetCache.bf @@ -0,0 +1,157 @@ +using System; +using GlitchyEngine; +using System.IO; +using System.Collections; +using GlitchyEngine.Core; +using GlitchyEngine.Content; + +namespace GlitchyEditor.Assets; + +/// Represents a processed asset file as it lies in the cache-directory. +class CachedAsset +{ + public const char8[3] MagicWord = .('L', 'A', 'F'); + + public String FilePath ~ delete _; + + public uint16 FormatVersion; + public AssetHandle Handle; + public DateTime CreationTimestamp; + public AssetCompression Compression; + public AssetType AssetType; + public int64 CompressedByteCount; + public int64 UncompressedByteCount; + public String AssetIdentifier ~ delete _; + + public const String CacheFileExtension = ".laf"; +} + +/// Manages the cache for already processed assets +class AssetCache +{ + /// Current format version of loose asset file (.laf) file reader and writer. + public const uint16 FormatVersion = 1; + + private append String _directory = .() ~ delete:append _; + + private append Dictionary _assets ~ delete:append _; + + public StringView CacheDirectory => _directory; + + private bool _cacheLoaded; + + public ~this() + { + ClearCache(); + } + + private void ClearCache() + { + _cacheLoaded = false; + ClearDictionaryAndDeleteValues!(_assets); + } + + /// Sets the directory in which the processed assets are cached. + public void SetDirectory(StringView directory) + { + _directory.Set(directory); + ReloadCache(); + } + + // TODO: This might take ages for large projects and definitely shouldn't run in the main thread! + public void ReloadCache() + { + ClearCache(); + + if (!Directory.Exists(_directory)) + { + Log.EngineLogger.Info($"Asset cache directory doesn't exist, creating directory \"{_directory}\"..."); + + if (Directory.CreateDirectory(_directory) case .Err(let error)) + { + Log.EngineLogger.Critical($"Failed to create cache directory \"{_directory}\". Reason: {error}."); + Log.EngineLogger.Critical($"The engine will not function properly without the asset cache directory. Save your project and restart the engine."); + } + + // At this point we either just created the cache directory and thus it's empty, + // or we failed and can't do anything anyway. + return; + } + + String filePath = scope .(); + for (FileFindEntry file in Directory.EnumerateFiles(_directory)) + { + filePath.Clear(); + file.GetFilePath(filePath); + + if (!filePath.EndsWith(CachedAsset.CacheFileExtension, .OrdinalIgnoreCase)) + continue; + + CachedAsset cachedAsset = new .(); + if (ReadAssetFile(filePath, cachedAsset) case .Err) + { + Log.EngineLogger.Error($"Failed to read cached asset file \"filePath\"."); + + delete cachedAsset; + } + + _assets.Add(cachedAsset.Handle, cachedAsset); + } + + _cacheLoaded = true; + } + + private Result ReadAssetFile(StringView filePath, CachedAsset cachedAsset) + { + cachedAsset.FilePath = new String(filePath); + + FileStream stream = scope .(); + Try!(stream.Open(filePath)); + + // Check magic word + char8[3] magicWord = Try!(stream.Read()); + if (magicWord != CachedAsset.MagicWord) + return .Err; + + cachedAsset.FormatVersion = Try!(stream.Read()); + + // Validate format version + if (cachedAsset.FormatVersion == 0 || cachedAsset.FormatVersion > FormatVersion) + { + Log.EngineLogger.Error("The cached assets version is either invalid or too new."); + return .Err; + } + + cachedAsset.Handle = Try!(stream.Read()); + cachedAsset.CreationTimestamp = Try!(stream.Read()); + cachedAsset.Compression = Try!(stream.Read()); + cachedAsset.AssetType = Try!(stream.Read()); + cachedAsset.CompressedByteCount = Try!(stream.Read()); + cachedAsset.UncompressedByteCount = Try!(stream.Read()); + + int32 assetIdentifierByteCount = Try!(stream.Read()); + + cachedAsset.AssetIdentifier = new String(assetIdentifierByteCount); + cachedAsset.AssetIdentifier.PadLeft(assetIdentifierByteCount); + Span charSpan = cachedAsset.AssetIdentifier; + int readBytes = Try!(stream.TryRead(Span((uint8*)charSpan.Ptr, charSpan.Length))); + + if (readBytes != assetIdentifierByteCount) + { + Log.EngineLogger.Warning($"Expected to read {assetIdentifierByteCount} bytes for the asset identifier, but read {readBytes} instead."); + } + + return .Ok; + } + + public CachedAsset GetCacheEntry(AssetHandle id) + { + if (!_cacheLoaded) + ReloadCache(); + + if (_assets.TryGetValue(id, let cachedAsset)) + return cachedAsset; + + return null; + } +} \ No newline at end of file diff --git a/GlitchyEditor/src/Assets/AssetConverter.bf b/GlitchyEditor/src/Assets/AssetConverter.bf new file mode 100644 index 0000000..197f5f2 --- /dev/null +++ b/GlitchyEditor/src/Assets/AssetConverter.bf @@ -0,0 +1,50 @@ +using System.Collections; +using GlitchyEditor.Assets.Importers; +using GlitchyEngine; + +namespace GlitchyEditor.Assets; + +class AssetConverter +{ + private append Queue _queue = .() ~ delete:append _; + + private EditorContentManager _contentManager; + + public this(EditorContentManager contentManager) + { + _contentManager = contentManager; + } + + public void QueueForProcessing(AssetFile assetFile) + { + _queue.Add(assetFile); + } + + public void Update() + { + if (_queue.Count == 0) + return; + + for (AssetFile assetFile in _queue) + { + Process(assetFile); + } + + _queue.Clear(); + } + + private void Process(AssetFile assetFile) + { + IAssetImporter importer = _contentManager.GetAssetImporter(assetFile); + + if (importer == null) + { + Log.EngineLogger.Error("Importer is null!"); + } + + ImportedResource importedResource = importer.Import(assetFile.AssetFile.Path, + assetFile.AssetFile.Identifier, assetFile.AssetConfig.ImporterConfig); + + delete importedResource; + } +} \ No newline at end of file diff --git a/GlitchyEditor/src/Assets/AssetHierarchy.bf b/GlitchyEditor/src/Assets/AssetHierarchy.bf index c291c72..dfd085f 100644 --- a/GlitchyEditor/src/Assets/AssetHierarchy.bf +++ b/GlitchyEditor/src/Assets/AssetHierarchy.bf @@ -439,11 +439,11 @@ class AssetHierarchy // Only files get AssetFile and AssetHandle if (!isDirectory) { - // TODO: Subassets + // TODO: Subassets -> Happens in processor! //GrabSubAssets(node); // TODO: Apparently directories were supposed to get an AssetFile? Makes sense, we wanted to have settings for directories, too! - treeNode->AssetFile = new AssetFile(_contentManager, assetNode.Identifier, assetNode.Path, assetNode.IsDirectory); + treeNode->AssetFile = AssetFile.LoadOrCreateAssetFile(_contentManager, assetNode); _handleToAssetNode.Add(assetNode.AssetFile.AssetConfig.AssetHandle, treeNode); } @@ -598,10 +598,10 @@ class AssetHierarchy // Directories have no AssetFile? if (node->AssetFile != null) { - node->AssetFile.[Friend]_path.Set(node->Path); + node->Path.Set(newFilePath); - node->AssetFile.[Friend]_identifier.Set(newFilePath); - AssetIdentifier.Fixup(node->AssetFile.[Friend]_identifier); + delete node->Identifier; + node->Identifier = new AssetIdentifier(newFilePath); node->AssetFile.[Friend]_assetConfigPath.Set(node->Path); node->AssetFile.[Friend]_assetConfigPath.Append(AssetFile.ConfigFileExtension); diff --git a/GlitchyEditor/src/Assets/AssetNode.bf b/GlitchyEditor/src/Assets/AssetNode.bf index 58b02c2..9db6884 100644 --- a/GlitchyEditor/src/Assets/AssetNode.bf +++ b/GlitchyEditor/src/Assets/AssetNode.bf @@ -4,6 +4,7 @@ using GlitchyEngine.Renderer; using GlitchyEngine.Content; namespace GlitchyEditor.Assets; +// TODO: Why exactly are AssetFile and AssetNode separated? public class AssetNode { public String Name ~ delete _; diff --git a/GlitchyEditor/src/Assets/Importers/Config.bf b/GlitchyEditor/src/Assets/Importers/Config.bf new file mode 100644 index 0000000..0eeb680 --- /dev/null +++ b/GlitchyEditor/src/Assets/Importers/Config.bf @@ -0,0 +1,49 @@ +using Bon; +using GlitchyEngine.Content; + +namespace GlitchyEditor.Assets.Importers; + +[BonTarget, BonPolyRegister] +abstract class Config +{ + [BonIgnore] + protected bool _changed; + + public bool Changed => _changed; + + protected bool SetIfChanged(ref T field, T value) + { + if (field == value) + return false; + + field = value; + _changed = true; + + return true; + } +} + +[BonTarget, BonPolyRegister] +class AssetImporterConfig : Config +{ + +} + +[BonTarget, BonPolyRegister] +class AssetProcessorConfig : Config +{ + +} + +[BonTarget, BonPolyRegister] +class AssetExporterConfig : Config +{ + [BonInclude] + private AssetCompression _compression; + + public AssetCompression Compression + { + get => _compression; + set => SetIfChanged(ref _compression, value); + } +} diff --git a/GlitchyEditor/src/Assets/Importers/DdsImporter.bf b/GlitchyEditor/src/Assets/Importers/DdsImporter.bf index e875d01..ec7716e 100644 --- a/GlitchyEditor/src/Assets/Importers/DdsImporter.bf +++ b/GlitchyEditor/src/Assets/Importers/DdsImporter.bf @@ -16,6 +16,10 @@ public struct LoadedSurface public int ArrayIndex; public int CubeFace; public int MipLevel; + + public uint32 Width; + public uint32 Height; + public uint32 Depth; } public struct LoadedTextureInfo @@ -703,6 +707,10 @@ static class DdsImporter surface.CubeFace = cubeFace; surface.MipLevel = mipLevel; + surface.Width = width; + surface.Height = height; + surface.Depth = depth; + surfaces.Add(surface); ++index; diff --git a/GlitchyEditor/src/Assets/Importers/Interfaces.bf b/GlitchyEditor/src/Assets/Importers/Interfaces.bf new file mode 100644 index 0000000..dd79549 --- /dev/null +++ b/GlitchyEditor/src/Assets/Importers/Interfaces.bf @@ -0,0 +1,29 @@ +using System; +using System.Collections; +using System.IO; +using GlitchyEngine.Content; + +namespace GlitchyEditor.Assets.Importers; + +interface IAssetImporter +{ + static List FileExtensions {get;} + + AssetImporterConfig CreateDefaultConfig(); + + Result Import(StringView fullFileName, AssetIdentifier assetIdentifier, AssetImporterConfig config); +} + +interface IAssetProcessor +{ + AssetProcessorConfig CreateDefaultConfig(); + + Result Process(ImportedResource importedResource, AssetProcessorConfig config); +} + +interface IAssetExporter +{ + AssetExporterConfig CreateDefaultConfig(); + + Result Export(Stream stream, ProcessedResource processedObject, AssetExporterConfig config); +} diff --git a/GlitchyEditor/src/Assets/Importers/TextureImporter.bf b/GlitchyEditor/src/Assets/Importers/TextureImporter.bf new file mode 100644 index 0000000..143250e --- /dev/null +++ b/GlitchyEditor/src/Assets/Importers/TextureImporter.bf @@ -0,0 +1,609 @@ +using System; +using System.Collections; +using System.IO; +using Bon; +using GlitchyEngine.Content; +using GlitchyEngine; +using GlitchyEngine.Renderer; +using static GlitchyEditor.Assets.Importers.LoadedTextureInfo; + +namespace GlitchyEditor.Assets.Importers; + +class ImportedResource +{ + private AssetIdentifier _assetIdentifier ~ delete _; + + public AssetIdentifier AssetIdentifier => _assetIdentifier; + + public this(AssetIdentifier ownAssetIdentifier) + { + _assetIdentifier = ownAssetIdentifier; + } +} + +class ImportedTexture : ImportedResource +{ + //public TextureDimension TextureType; + private List _surfaces = new .() ~ delete _; + private LoadedTextureInfo _textureInfo ~ delete _textureInfo.PixelData; + + public List Surfaces => _surfaces; + + public ref LoadedTextureInfo TextureInfo => ref _textureInfo; + + public this(AssetIdentifier ownAssetIdentifier) : base(ownAssetIdentifier) + { + } +} + +[BonTarget, BonPolyRegister] +class TextureImporterConfig : AssetImporterConfig +{ + [BonInclude] + private bool _isSrgb; + + public bool IsSrgb + { + get => _isSrgb; + set => SetIfChanged(ref _isSrgb, value); + } +} + + +class TextureImporter : IAssetImporter +{ + private static readonly List _fileExtensions = new .(){".png", ".dds"} ~ delete _; + + public static List FileExtensions => _fileExtensions; + + public AssetImporterConfig CreateDefaultConfig() + { + return new TextureImporterConfig(); + } + + public Result Import(StringView fullFileName, AssetIdentifier assetIdentifier, AssetImporterConfig config) + { + Log.EngineLogger.AssertDebug(config is TextureImporterConfig); + + ImportedTexture importedData = new ImportedTexture(new AssetIdentifier(assetIdentifier.FullIdentifier)); + + // TODO: Get stream from asset mananger? + FileStream stream = scope FileStream(); + Try!(stream.Open(fullFileName, .Read, .Read)); + + Result importResult = ImportTexture(stream, importedData, (TextureImporterConfig)config); + + stream.Close(); + + if (importResult case .Err) + { + delete importedData; + return .Err; + } + + return importedData; + } + + const String PngMagicWord = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"; + const String DdsMagicWord = "DDS "; + + enum TextureType + { + Unknown, + DDS, + PNG + } + + private static TextureType GetTextureType(Stream data) + { + int64 position = data.Position; + + var readResult = data.Read(); + + data.Position = position; + + char8[8] magicWord; + if (readResult case .Ok(out magicWord)) + { + StringView strView = .(&magicWord, magicWord.Count); + + if (strView.StartsWith(PngMagicWord)) + { + return .PNG; + } + else if (strView.StartsWith(DdsMagicWord)) + { + return .DDS; + } + else + { + Runtime.FatalError("Unknown image format."); + } + } + + return .Unknown; + } + + private static Result ImportTexture(Stream data, ImportedTexture importedTexture, TextureImporterConfig config) + { + Debug.Profiler.ProfileResourceFunction!(); + + switch(GetTextureType(data)) + { + case .DDS: + Try!(LoadDds(data, config, importedTexture.Surfaces, out importedTexture.TextureInfo)); + case .PNG: + Try!(LoadPng(data, config, importedTexture.Surfaces, out importedTexture.TextureInfo)); + case .Unknown: + Log.EngineLogger.Error("Unknown texture format."); + return .Err; + } + + return .Ok; + } + + private static Result LoadPng(Stream data, TextureImporterConfig config, List surfaces, out LoadedTextureInfo textureInfo) + { + Debug.Profiler.ProfileResourceFunction!(); + + textureInfo = .(); + + uint8[] pngData = new:ScopedAlloc! uint8[data.Length]; + + var result = data.TryRead(pngData); + + if (result case .Err(let err)) + { + Log.EngineLogger.Error($"Failed to read data from stream. Texture: Error: {err}"); + return .Err; + } + + uint8* rawData = null; + defer + { + if (rawData != null) + LodePng.LodePng.Free(rawData); + } + + uint32 width = 0, height = 0; + + { + Debug.Profiler.ProfileResourceScope!("LodePng.LodePng.Decode32"); + uint32 errorCode = LodePng.LodePng.Decode32(&rawData, &width, &height, pngData.Ptr, (.)pngData.Count); + if (errorCode != 0) + { + Log.EngineLogger.Error($"Failed to decode PNG file {errorCode}."); + return .Err; + } + } + + uint8[] pixelData = new uint8[4 * width * height]; + Internal.MemCpy(pixelData.Ptr, rawData, pixelData.Count); + + LoadedSurface surface = .(); + surface.Data = Span(pixelData); + surface.Pitch = 4 * width; + surface.SlicePitch = 0; + surface.ArrayIndex = 0; + surface.MipLevel = 0; + + surfaces.Add(surface); + + textureInfo.PixelData = pixelData; + textureInfo.Width = width; + textureInfo.Height = height; + textureInfo.Depth = 1; + + textureInfo.ArraySize = 1; + textureInfo.MipMapCount = 1; + + textureInfo.Dimension = .Texture2D; + + textureInfo.IsCubeMap = false; + + // TODO: PNG supports multiple colordepths! (Grayscale up to 16 bit, RGB 8 or 16 bit) + textureInfo.PixelFormat = config.IsSrgb ? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm; + + return .Ok; + } + + private static Result LoadDds(Stream data, TextureImporterConfig config, List surfaces, out LoadedTextureInfo textureInfo) + { + var result = DdsImporter.LoadDds(data, config.IsSrgb, surfaces, out textureInfo); + + if (result case .Err) + return .Err; + + return .Ok; + } +} + +enum GenerateMipMaps +{ + No, + Box, + Kaiser +} + +[BonTarget, BonPolyRegister] +class TextureProcessorConfig : AssetProcessorConfig +{ + [BonInclude] + private GenerateMipMaps _generateMipMaps; + + public GenerateMipMaps GenerateMipMaps + { + get => _generateMipMaps; + set => SetIfChanged(ref _generateMipMaps, value); + } +} + +class ProcessedResource +{ + private AssetIdentifier _assetIdentifier ~ delete _; + + public AssetIdentifier AssetIdentifier => _assetIdentifier; + + public this(AssetIdentifier ownAssetIdentifier) + { + _assetIdentifier = ownAssetIdentifier; + } +} + +class ProcessedTexture : ProcessedResource +{ + public Format PixelFormat = .Unknown; + public int MipMapCount = -1; + public int ArraySize = -1; + public Dimension Dimension = .Unknown; + public bool IsCubeMap; + + public int Width = -1; + public int Height = -1; + public int Depth = -1; + + public class TextureSurface + { + public uint8[] PixelData; + public int Width; + public int Height; + public int Depth; + public int MipLevel; + public int ArraySlice; + + [AllowAppend] + public this(int width, int height, int depth, Span data, int mipLevel, int arraySlice) + { + uint8[] pixelData = append uint8[data.Length]; + data.CopyTo(pixelData); + + PixelData = pixelData; + Width = width; + Height = height; + Depth = depth; + MipLevel = mipLevel; + ArraySlice = arraySlice; + } + + public uint64 LoadRaw(int x, int y, int z, Format format, ComponentInfo component) + { + Log.EngineLogger.AssertDebug(x >= 0 && y >= 0 && z >= 0 && x < Width && y < Height && z < Depth); + + int64 pixelOffset = x + (Width * y) + (Width * Height) * z; + + int64 bitOffset = pixelOffset * format.BitsPerPixel(); + + bitOffset += component.BitSize; + + int64 byteOffset = bitOffset / 8; + int shift = bitOffset % 8; + + int bytesToRead = (component.BitSize + shift) / 8; + + Log.EngineLogger.AssertDebug(bytesToRead <= 8); + + uint64 data = 0; + + data = PixelData.Ptr[byteOffset]; + data >>= shift; + uint64 mask = (1 << component.BitSize) - 1; + data &= mask; + + return data; + } + } + + public TextureSurface[,] Surfaces; + + public this(AssetIdentifier ownAssetIdentifier) : base(ownAssetIdentifier) + { + + } + + public ~this() + { + for (int i < Surfaces?.GetLength(0) ?? 0) + { + for (int j < Surfaces.GetLength(1)) + { + delete Surfaces[i, j]; + } + } + + delete Surfaces; + } + + public void SetSurfaceCount(int arraySize, int mipMapCount) + { + Log.EngineLogger.AssertDebug(arraySize > Surfaces.GetLength(0)); + Log.EngineLogger.AssertDebug(mipMapCount > Surfaces.GetLength(1)); + + TextureSurface[,] oldSurfaces = Surfaces; + Surfaces = new TextureSurface[arraySize, mipMapCount]; + + for (int i < oldSurfaces.GetLength(0)) + { + for (int j < oldSurfaces.GetLength(1)) + { + Surfaces[i, j] = oldSurfaces[i, j]; + } + } + } +} + +class TextureProcessor : IAssetProcessor +{ + public AssetProcessorConfig CreateDefaultConfig() + { + return new TextureProcessorConfig(); + } + + public Result Process(ImportedResource importedObject, AssetProcessorConfig config) + { + Log.EngineLogger.AssertDebug(config is TextureProcessorConfig); + Log.EngineLogger.AssertDebug(importedObject is ImportedTexture); + + Try!(ProcessTexture(importedObject as ImportedTexture, config as TextureProcessorConfig)); + + return .Ok(null); + } + + private Result ProcessTexture(ImportedTexture importedTexture, TextureProcessorConfig config) + { + ProcessedTexture processedTexture = new ProcessedTexture(new AssetIdentifier(importedTexture.AssetIdentifier.FullIdentifier)); + + 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); + processedTexture.Surfaces[loadedSurface.ArrayIndex, loadedSurface.MipLevel] = surface; + } + + if (!(config.GenerateMipMaps case .No)) + { + // TODO: Unpack BC-Formats to RGBA + + GenerateMipMaps(processedTexture, config); + } + + // TODO: Pack to BC-Format or what ever was selected. + + return .Ok; + } + + private int CalculateMipMapCount(int width, int height, int depth) + { + var width, height, depth; + + int count = 0; + + while (true) + { + count++; + + if (width == 1 && height == 1 && depth == 1) + break; + + if (width > 1) + width >>= 1; + if (height > 1) + height >>= 1; + if (depth > 1) + depth >>= 1; + } + + return count; + } + + public bool CanGenerateMipMaps(Format format) + { + // TODO! + return true; + } + + private Result GenerateMipMaps(ProcessedTexture processedTexture, TextureProcessorConfig config) + { + if (!CanGenerateMipMaps(processedTexture.PixelFormat)) + { + return .Err; + } + + int mipMapCount = CalculateMipMapCount(processedTexture.Width, processedTexture.Height, processedTexture.Depth); + + if (processedTexture.MipMapCount != mipMapCount) + { + processedTexture.SetSurfaceCount(processedTexture.ArraySize, mipMapCount); + } + + for (int arraySlice < processedTexture.ArraySize) + { + ProcessedTexture.TextureSurface largerSurface = processedTexture.Surfaces[arraySlice, 0]; + + for (int mipMap = 1; mipMap < processedTexture.MipMapCount; mipMap++) + { + ref ProcessedTexture.TextureSurface surface = ref processedTexture.Surfaces[arraySlice, mipMap]; + + surface = GenerateMipLevel(processedTexture.PixelFormat, largerSurface, surface); + + largerSurface = surface; + } + } + + return .Ok; + + // TODO: Generate Mip Maps + /*int mipMapCount = CalculateMipMapCount(processedTexture.Width, processedTexture.Height, processedTexture.Depth); + + processedTexture.SetMipMapCount(mipMapCount); + + for (int slice < processedTexture.ArraySize) + { + for (int mipMap < mipMapCount) + { + if (processedTexture.Surfaces[slice, mipMap] == null) + { + processedTexture.Surfaces[slice, mipMap] = GenerateMipLevel(processedTexture.Surfaces[slice, mipMap - 1]); + } + } + } + */ + } + + private ProcessedTexture.TextureSurface GenerateMipLevel(Format pixelFormat, ProcessedTexture.TextureSurface largerLevel, ProcessedTexture.TextureSurface smallerLevel) + { + var smallerLevel; + + int width = Math.Max(largerLevel.Width / 2, 1); + int height = Math.Max(largerLevel.Height / 2, 1); + int depth = Math.Max(largerLevel.Depth / 2, 1); + + if (smallerLevel == null) + { + smallerLevel = new ProcessedTexture.TextureSurface(width, height, depth, new uint8[width * height * depth * pixelFormat.BitsPerPixel()], largerLevel.MipLevel + 1, largerLevel.ArraySlice); + } + + // TODO: Kaiser mip maps? + + FormatInfo formatInfo = default; //pixelFormat.GetFormatInfo(); + + // Simple box filter + for (int x < width) + for (int y < height) + for (int z < depth) + { + for (int channel < formatInfo.ComponentCount) + { + ComponentInfo info = formatInfo.Components[channel]; + + switch (info.DataType) + { + case .UNorm, .UInt: + + default: + } + } + } + + return smallerLevel; + } + + private void Box() where DataType : const ComponentDataType + { + + } +} + +class TextureExporter : IAssetExporter +{ + public AssetExporterConfig CreateDefaultConfig() + { + return new AssetExporterConfig(); + } + + public Result Export(Stream stream, ProcessedResource processedResource, AssetExporterConfig config) + { + Log.EngineLogger.AssertDebug(processedResource is ProcessedTexture); + + ProcessedTexture processedTexture = (.)processedResource; + + /* + + File Format: + TextureType (1 byte) + Pixel Format (4 bytes) + Width of larges mip-slice (4 bytes) + Height of larges mip-slice (4 bytes) + Depth of larges mip-slice (4 bytes) + Array size (4 bytes) + Mip map levels (4 bytes) + Pixeldata + { + Array[0]: Mip[0] Mip[1] ... Mip[M] + Array[1]: Mip[0] Mip[1] ... Mip[M] + ... + Array[N]: Mip[0] Mip[1] ... Mip[M] + } + + */ + + Try!(stream.Write(processedTexture.Dimension)); + Try!(stream.Write(processedTexture.PixelFormat)); + Try!(stream.Write((uint32)processedTexture.Width)); + Try!(stream.Write((uint32)processedTexture.Height)); + Try!(stream.Write((uint32)processedTexture.Depth)); + Try!(stream.Write((uint32)processedTexture.ArraySize)); + Try!(stream.Write((uint32)processedTexture.MipMapCount)); + + for (int arraySlice < processedTexture.ArraySize) + { + int validateWidth = processedTexture.Width; + int validateHeight = processedTexture.Height; + int validateDepth = processedTexture.Depth; + + for (int mipSlice < processedTexture.MipMapCount) + { + ProcessedTexture.TextureSurface slice = processedTexture.Surfaces[arraySlice, mipSlice]; + + Log.EngineLogger.AssertDebug(validateWidth == slice.Width); + Log.EngineLogger.AssertDebug(validateHeight == slice.Height); + Log.EngineLogger.AssertDebug(validateDepth == slice.Depth); + validateWidth /= 2; + validateHeight /= 2; + validateDepth /= 2; + + if (validateWidth < 1) + validateWidth = 1; + + if (validateHeight < 1) + validateHeight = 1; + + if (validateDepth < 1) + validateDepth = 1; + + Try!(stream.TryWrite(slice.PixelData)); + } + } + + return .Ok; + } +} + +class TextureLoader +{ + public Result Load(Stream data, AssetIdentifier assetIdentifier) + { + 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()); + + + + return .Ok(null); + } +} diff --git a/GlitchyEditor/src/Assets/TextureAssetLoader.bf b/GlitchyEditor/src/Assets/TextureAssetLoader.bf index 6fc0482..2b44122 100644 --- a/GlitchyEditor/src/Assets/TextureAssetLoader.bf +++ b/GlitchyEditor/src/Assets/TextureAssetLoader.bf @@ -343,7 +343,7 @@ class EditorTextureAssetLoader : IAssetLoader//, IReloadingAssetLoader staging.MipLevels = (.)textureInfo.MipMapCount; staging.ArraySize = (.)textureInfo.ArraySize; - staging.Format = textureInfo.PixelFormat; + staging.Format = (.)textureInfo.PixelFormat; // TODO: allow enabling read/write staging.CpuAccess = .None; @@ -370,7 +370,7 @@ class EditorTextureAssetLoader : IAssetLoader//, IReloadingAssetLoader staging.MipLevels = (.)textureInfo.MipMapCount; staging.ArraySize = (.)textureInfo.ArraySize; - staging.Format = textureInfo.PixelFormat; + staging.Format = (.)textureInfo.PixelFormat; // TODO: allow enabling read/write staging.CpuAccess = .None; diff --git a/GlitchyEditor/src/EditWindows/AssetViewer.bf b/GlitchyEditor/src/EditWindows/AssetViewer.bf index 3997786..9f6f6a3 100644 --- a/GlitchyEditor/src/EditWindows/AssetViewer.bf +++ b/GlitchyEditor/src/EditWindows/AssetViewer.bf @@ -561,7 +561,7 @@ class TexturererViewerer _renderTargetEffect.Variables["Swizzle"].SetData(int4((int32)_swizzleR, (int32)_swizzleG, (int32)_swizzleB, (int32)_swizzleA)); - if (format.IsInt()) + if (((DirectX.DXGI.Format)format).IsInt()) { // Int Texture _renderTargetEffect.Variables["Mode"].SetData(1); @@ -598,7 +598,7 @@ class TexturererViewerer { var desc = _groupIndex >= 0 ? viewedTexture.[Friend]_colorTargetDescriptions[_groupIndex] : viewedTexture.[Friend]_depthTargetDescription; - RenderTexture(viewedTexture.GetViewBinding(_groupIndex), float2(viewedTexture.Width, viewedTexture.Height), desc.Format.GetShaderViewFormat()); + RenderTexture(viewedTexture.GetViewBinding(_groupIndex), float2(viewedTexture.Width, viewedTexture.Height), (.)desc.Format.GetShaderViewFormat()); } private void RenderTexture(Texture viewedTexture) diff --git a/GlitchyEditor/src/EditWindows/PropertiesWindow.bf b/GlitchyEditor/src/EditWindows/PropertiesWindow.bf index 8ceb635..5777cdc 100644 --- a/GlitchyEditor/src/EditWindows/PropertiesWindow.bf +++ b/GlitchyEditor/src/EditWindows/PropertiesWindow.bf @@ -79,9 +79,9 @@ class PropertiesWindow : EditorWindow Asset asset = _editor.ContentManager.GetAsset(null, _currentAssetHandle); // We need the actual asset for preview and sometimes for editing - if (asset?.Identifier != assetFile.Identifier) + if (asset?.Identifier != assetFile.AssetFile.Identifier) { - _currentAssetHandle = _editor.ContentManager.LoadAsset(assetFile.Identifier); + _currentAssetHandle = _editor.ContentManager.LoadAsset(assetFile.AssetFile.Identifier); } // TODO: allow changing AssetLoader diff --git a/GlitchyEditor/src/EditorApp.bf b/GlitchyEditor/src/EditorApp.bf index 9e8529f..7541128 100644 --- a/GlitchyEditor/src/EditorApp.bf +++ b/GlitchyEditor/src/EditorApp.bf @@ -2,6 +2,7 @@ using System; using GlitchyEngine; using GlitchyEngine.Content; using GlitchyEditor.Assets; +using GlitchyEditor.Assets.Importers; namespace GlitchyEditor { @@ -36,6 +37,12 @@ namespace GlitchyEditor _contentManager.SetAsDefaultAssetLoader(".hlsl"); _contentManager.SetAssetPropertiesEditor(=> EffectAssetPropertiesEditor.Factory); + _contentManager.RegisterAssetImporter(); + _contentManager.RegisterAssetProcessor(); + _contentManager.RegisterAssetExporter(); + + _contentManager.ConfigureDefaultProcessing(".png"); + _contentManager.SetResourcesDirectory("./Resources"); return _contentManager; diff --git a/GlitchyEditor/src/EditorContentManager.bf b/GlitchyEditor/src/EditorContentManager.bf index aa6bff8..f310b8c 100644 --- a/GlitchyEditor/src/EditorContentManager.bf +++ b/GlitchyEditor/src/EditorContentManager.bf @@ -9,6 +9,9 @@ using GlitchyEditor.Assets; using GlitchyEngine; using System.Linq; using System.Threading.Tasks; +using GlitchyEditor.Assets.Importers; +using GlitchyEngine.Core; + using internal GlitchyEngine.Content.Asset; namespace GlitchyEditor; @@ -17,19 +20,22 @@ class EditorContentManager : IContentManager { private append String _resourcesDirectory = .(); private append String _assetsDirectory = .(); - - public StringView ResourcesDirectory => _resourcesDirectory; - public StringView AssetDirectory => _assetsDirectory; private append Dictionary _identiferToHandle = .(); // TODO: Check if all resources are unloaded private append Dictionary _handleToAsset = .(); private append AssetHierarchy _assetHierarchy = .(this); - - public AssetHierarchy AssetHierarchy => _assetHierarchy; + private append AssetCache _assetCache = .() ~ delete:append _; + private append AssetConverter _assetConverter = .(this) ~ delete:append _; private append List _reloadQueue = .(); + + public StringView ResourcesDirectory => _resourcesDirectory; + public StringView AssetDirectory => _assetsDirectory; + public AssetHierarchy AssetHierarchy => _assetHierarchy; + public AssetCache AssetCache => _assetCache; + public AssetConverter AssetConverter => _assetConverter; public this() { @@ -45,7 +51,7 @@ class EditorContentManager : IContentManager private void OnFileContentChanged(AssetNode assetNode) { // Asset isn't loaded so we don't need to reload it. - if (assetNode.AssetFile.LoadedAsset == null) + if (assetNode.AssetFile?.LoadedAsset == null) return; _reloadQueue.Add(assetNode.AssetFile.LoadedAsset.Handle); @@ -60,7 +66,7 @@ class EditorContentManager : IContentManager Asset asset = assetNode.AssetFile.LoadedAsset; _identiferToHandle.Remove(oldIdentifier); - asset.Identifier = assetNode.AssetFile.Identifier; + asset.Identifier = assetNode.Identifier; _identiferToHandle.Add(asset.Identifier, asset.Handle); } @@ -80,8 +86,15 @@ class EditorContentManager : IContentManager _assetHierarchy.SetAssetsDirectory(_assetsDirectory); } + public void SetAssetCacheDirectory(StringView fileName) + { + _assetCache.SetDirectory(fileName); + } + public void Update() { + _assetConverter.Update(); + SwapInLoadedAssets(); if (!_reloadQueue.IsEmpty) @@ -135,6 +148,16 @@ class EditorContentManager : IContentManager return null; } + + // TODO: This type sucks! + public Result<(IAssetImporter Importer, IAssetProcessor Processor, IAssetExporter Exporter)> GetDefaultProcessors(StringView fileExtension) + { + if (_defaultAssetProcessors.TryGetValue(fileExtension, let value)) + return value; + + return .Err; + } + private append List _supportedExtensions = .() ~ ClearAndDeleteItems!(_); private append List _assetLoaders = .() ~ ClearAndDeleteItems!(_); private append Dictionary _defaultAssetLoaders = .(); @@ -143,8 +166,14 @@ class EditorContentManager : IContentManager { delete key; } + delete:append _; }; + private append List _assetImporters = .() ~ ClearAndDeleteItems!(_); + private append List _assetProcessors = .() ~ ClearAndDeleteItems!(_); + private append List _assetExporters = .() ~ ClearAndDeleteItems!(_); + private append Dictionary _defaultAssetProcessors = .() ~ delete:append _; + public void RegisterAssetLoader() where T : new, class, IAssetLoader { // Log.EngineLogger.AssertDebug(!_assetLoaders.Any((l) => l.GetType() == typeof(T)), "Asset loader already registered."); @@ -157,6 +186,30 @@ class EditorContentManager : IContentManager _supportedExtensions.Add(new String(ext)); } + public void RegisterAssetImporter() where T : new, class, IAssetImporter + { + T assetImporter = new T(); + + _assetImporters.Add(assetImporter); + + for (StringView ext in T.FileExtensions) + _supportedExtensions.Add(new String(ext)); + } + + public void RegisterAssetProcessor() where T : new, class, IAssetProcessor + { + T assetProcessor = new T(); + + _assetProcessors.Add(assetProcessor); + } + + public void RegisterAssetExporter() where T : new, class, IAssetExporter + { + T assetExporter = new T(); + + _assetExporters.Add(assetExporter); + } + public void SetAsDefaultAssetLoader(params Span fileExtensions) where T : IAssetLoader { for (var ext in fileExtensions) @@ -185,6 +238,63 @@ class EditorContentManager : IContentManager } } } + + public void ConfigureDefaultProcessing(params Span fileExtensions) where TImport : IAssetImporter where TProcess : IAssetProcessor where TExport : IAssetExporter + { + for (var ext in fileExtensions) + { + // Find file extension in registered file extensions + String foundExtension = null; + + for (var supportedExt in _supportedExtensions) + { + if (supportedExt == ext) + { + foundExtension = supportedExt; + break; + } + } + + Log.EngineLogger.Assert(foundExtension != null, "File Extension is not registered."); + + //IAssetImporter importer = _assetImporters.Where((i) => i.GetType() == typeof(TImport)).First(); + //IAssetProcessor processor = _assetProcessors.Where((i) => i.GetType() == typeof(TProcess)).First(); + //IAssetExporter exporter = _assetExporters.Where((i) => i.GetType() == typeof(TExport)).First(); + + IAssetImporter importer = null; + IAssetProcessor processor = null; + IAssetExporter exporter = null; + + for (var i in _assetImporters) + { + if (i.GetType() == typeof(TImport)) + { + importer = i; + break; + } + } + + for (var i in _assetProcessors) + { + if (i.GetType() == typeof(TProcess)) + { + processor = i; + break; + } + } + + for (var i in _assetExporters) + { + if (i.GetType() == typeof(TExport)) + { + exporter = i; + break; + } + } + + _defaultAssetProcessors[foundExtension] = (importer, processor, exporter); + } + } public void SetAssetPropertiesEditor(Type assetLoaderType, function AssetPropertiesEditor(AssetFile) editorFactory) { @@ -275,11 +385,12 @@ class EditorContentManager : IContentManager return; } - AssetFile file = resultNode->Value.AssetFile; + AssetNode assetNode = resultNode->Value; + AssetFile file = assetNode.AssetFile; IAssetLoader assetLoader = GetAssetLoader(file); - Stream stream = OpenStream(file.FilePath, true); + Stream stream = OpenStream(assetNode.Path, true); // TODO: Add async loading! Asset loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config, resourceName, subassetName, this); @@ -361,13 +472,13 @@ class EditorContentManager : IContentManager String filePath = scope String(resultNode->Value.Path); - AssetFile file = resultNode->Value.AssetFile; + AssetNode assetNode = resultNode->Value; + AssetFile file = assetNode.AssetFile; - GetResourceAndSubassetName(file.Identifier, let resourceName, let subassetName); + GetResourceAndSubassetName(assetNode.Identifier, let resourceName, let subassetName); 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) { @@ -407,7 +518,7 @@ class EditorContentManager : IContentManager loadedAsset = placeholder; } - loadedAsset.Identifier = file.Identifier; + loadedAsset.Identifier = assetNode.Identifier; _handleToAsset.Add(handle, loadedAsset); @@ -584,6 +695,26 @@ class EditorContentManager : IContentManager return assetLoader; } + public IAssetImporter GetAssetImporter(AssetFile file) + { + IAssetImporter result = null; + + String typeName = scope .(128); + + for (IAssetImporter importer in _assetImporters) + { + importer.GetType().GetName(typeName..Clear()); + + if (typeName == file.AssetConfig.Importer) + { + result = importer; + break; + } + } + + return result; + } + public enum SaveAssetError { case Unknown; diff --git a/GlitchyEditor/src/EditorLayer.bf b/GlitchyEditor/src/EditorLayer.bf index 72ad47e..641a2e2 100644 --- a/GlitchyEditor/src/EditorLayer.bf +++ b/GlitchyEditor/src/EditorLayer.bf @@ -765,6 +765,7 @@ namespace GlitchyEditor String appAssemblyPath = scope String(); _contentManager.SetAssetDirectory(_currentProject.AssetsFolder); + _contentManager.SetAssetCacheDirectory(_currentProject.GetScopedPath!(".cache")); _currentProject.PathInProject(appAssemblyPath, scope $"bin/{_currentProject.Name}.dll"); diff --git a/GlitchyEngine/src/Content/AssetCompression.bf b/GlitchyEngine/src/Content/AssetCompression.bf new file mode 100644 index 0000000..28a39d8 --- /dev/null +++ b/GlitchyEngine/src/Content/AssetCompression.bf @@ -0,0 +1,7 @@ +namespace GlitchyEngine.Content; + +enum AssetCompression : uint8 +{ + None, + L4Z +} diff --git a/GlitchyEngine/src/Content/AssetType.bf b/GlitchyEngine/src/Content/AssetType.bf new file mode 100644 index 0000000..04c2e01 --- /dev/null +++ b/GlitchyEngine/src/Content/AssetType.bf @@ -0,0 +1,7 @@ +namespace GlitchyEngine.Content; + +enum AssetType : uint16 +{ + Unknown, + Texture +} \ No newline at end of file diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf index 52877ea..75f0761 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf @@ -56,7 +56,7 @@ namespace GlitchyEngine.Renderer public override uint32 Height => nativeDesc.Height; public override uint32 ArraySize => nativeDesc.ArraySize; public override uint32 MipLevels => nativeDesc.MipLevels; - public override Format Format => nativeDesc.Format; + public override Format Format => (.)nativeDesc.Format; /*protected override void CreateTexturePlatform(Texture2DDesc desc, bool isRenderTarget, void* data, uint32 linePitch) @@ -304,7 +304,7 @@ namespace GlitchyEngine.Renderer public override uint32 Height => nativeDesc.Height; public override uint32 ArraySize => nativeDesc.ArraySize / 6; public override uint32 MipLevels => nativeDesc.MipLevels; - public override Format Format => nativeDesc.Format; + public override Format Format => (.)nativeDesc.Format; protected override void CreateTexturePlatform(Texture2DDesc desc, bool isRenderTarget, void* data, uint32 linePitch) { diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11VertexLayout.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11VertexLayout.bf index 3f717fc..4bcd3df 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11VertexLayout.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11VertexLayout.bf @@ -33,7 +33,7 @@ namespace GlitchyEngine.Renderer Debug.Assert(input.Count == output.Count); for(int i < input.Count) - output[i] = .(input[i].SemanticName, input[i].SemanticIndex, input[i].Format, input[i].InputSlot, input[i].AlignedByteOffset, (.)input[i].InputSlotClass, input[i].InstanceDataStepRate); + output[i] = .(input[i].SemanticName, input[i].SemanticIndex, (.)input[i].Format, input[i].InputSlot, input[i].AlignedByteOffset, (.)input[i].InputSlotClass, input[i].InstanceDataStepRate); } /// Validates or gets the validated input layout for the given vertexshader. diff --git a/GlitchyEngine/src/Renderer/Buffer.bf b/GlitchyEngine/src/Renderer/Buffer.bf index 04cc11e..0b8f65d 100644 --- a/GlitchyEngine/src/Renderer/Buffer.bf +++ b/GlitchyEngine/src/Renderer/Buffer.bf @@ -160,9 +160,6 @@ namespace GlitchyEngine.Renderer //Structured = 2, } - typealias Format = DirectX.DXGI.Format; - - public struct BufferDescription { /** diff --git a/GlitchyEngine/src/Renderer/Format.bf b/GlitchyEngine/src/Renderer/Format.bf new file mode 100644 index 0000000..6ab9027 --- /dev/null +++ b/GlitchyEngine/src/Renderer/Format.bf @@ -0,0 +1,1038 @@ +using System; +using System.Reflection; +namespace GlitchyEngine.Renderer; + +struct PixelFormatInfoAttribute : Attribute +{ + public FormatInfo Info; + + public this(FormatInfo info) + { + Info = info; + } +} + +struct GeneratePixelFormatInfoAttribute : Attribute +{ + +} + +// Obvioulsy copy pasted from DirectX +/** +Resource data formats, including fully-typed and typeless formats. +*/ +enum Format : uint32 +{ + /** + The format is not known. + */ + [PixelFormatInfo(default)] + case Unknown = 0, + /** + A four-component, 128-bit typeless format that supports 32 bits per channel including alpha. + */ + R32G32B32A32_Typeless = 1, + /** + A four-component, 128-bit floating-point format that supports 32 bits per channel including alpha. + */ + R32G32B32A32_Float = 2, + /** + A four-component, 128-bit unsigned-integer format that supports 32 bits per channel including alpha. + */ + R32G32B32A32_UInt = 3, + /** + A four-component, 128-bit signed-integer format that supports 32 bits per channel including alpha. + */ + R32G32B32A32_SInt = 4, + /** + A three-component, 96-bit typeless format that supports 32 bits per color channel. + */ + R32G32B32_Typeless = 5, + /** + A three-component, 96-bit floating-point format that supports 32 bits per color channel. + */ + R32G32B32_Float = 6, + /** + A three-component, 96-bit unsigned-integer format that supports 32 bits per color channel. + */ + R32G32B32_UInt = 7, + /** + A three-component, 96-bit signed-integer format that supports 32 bits per color channel. + */ + R32G32B32_SInt = 8, + /** + A four-component, 64-bit typeless format that supports 16 bits per channel including alpha. + */ + R16G16B16A16_Typeless = 9, + /** + A four-component, 64-bit floating-point format that supports 16 bits per channel including alpha. + */ + R16G16B16A16_Float = 10, + /** + A four-component, 64-bit unsigned-normalized-integer format that supports 16 bits per channel including alpha. + */ + R16G16B16A16_UNorm = 11, + /** + A four-component, 64-bit unsigned-integer format that supports 16 bits per channel including alpha. + */ + R16G16B16A16_UInt = 12, + /** + A four-component, 64-bit signed-normalized-integer format that supports 16 bits per channel including alpha. + */ + R16G16B16A16_SNorm = 13, + /** + A four-component, 64-bit signed-integer format that supports 16 bits per channel including alpha. + */ + R16G16B16A16_SInt = 14, + /** + A two-component, 64-bit typeless format that supports 32 bits for the red channel and 32 bits for the green channel. + */ + R32G32_Typeless = 15, + /** + A two-component, 64-bit floating-point format that supports 32 bits for the red channel and 32 bits for the green channel. + */ + R32G32_Float = 16, + /** + A two-component, 64-bit unsigned-integer format that supports 32 bits for the red channel and 32 bits for the green channel. + */ + R32G32_UInt = 17, + /** + A two-component, 64-bit signed-integer format that supports 32 bits for the red channel and 32 bits for the green channel. + */ + R32G32_SInt = 18, + /** + A two-component, 64-bit typeless format that supports 32 bits for the red channel, 8 bits for the green channel, and 24 bits are unused. + */ + R32G8X24_Typeless = 19, + /** + A 32-bit floating-point component, and two unsigned-integer components (with an additional 32 bits). This format supports 32-bit depth, 8-bit stencil, and 24 bits are unused. + */ + D32_Float_S8X24_UInt = 20, + /** + A 32-bit floating-point component, and two typeless components (with an additional 32 bits). This format supports 32-bit red channel, 8 bits are unused, and 24 bits are unused. + */ + R32_Float_X8X24_Typeless = 21, + /** + A 32-bit typeless component, and two unsigned-integer components (with an additional 32 bits). This format has 32 bits unused, 8 bits for green channel, and 24 bits are unused. + */ + X32_Typeless_G8X24_UInt = 22, + /** + A four-component, 32-bit typeless format that supports 10 bits for each color and 2 bits for alpha. + */ + R10G10B10A2_Typeless = 23, + /** + A four-component, 32-bit unsigned-normalized-integer format that supports 10 bits for each color and 2 bits for alpha. + */ + R10G10B10A2_UNorm = 24, + /** + A four-component, 32-bit unsigned-integer format that supports 10 bits for each color and 2 bits for alpha. + */ + R10G10B10A2_UInt = 25, + /** + Three partial-precision floating-point numbers encoded into a single 32-bit value (a variant of s10e5, which is sign bit, 10-bit mantissa, and 5-bit biased (15) exponent). + There are no sign bits, and there is a 5-bit biased (15) exponent for each channel, 6-bit mantissa for R and G, and a 5-bit mantissa for B, + as shown in the illustration: "https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/images/r11g11b10_float.png". + */ + R11G11B10_Float = 26, + /** + A four-component, 32-bit typeless format that supports 8 bits per channel including alpha. + */ + R8G8B8A8_Typeless = 27, + /** + A four-component, 32-bit unsigned-normalized-integer format that supports 8 bits per channel including alpha. + */ + R8G8B8A8_UNorm = 28, + /** + A four-component, 32-bit unsigned-normalized integer sRGB format that supports 8 bits per channel including alpha. + */ + R8G8B8A8_UNorm_SRGB = 29, + /** + A four-component, 32-bit unsigned-integer format that supports 8 bits per channel including alpha. + */ + R8G8B8A8_UInt = 30, + /** + A four-component, 32-bit signed-normalized-integer format that supports 8 bits per channel including alpha. + */ + R8G8B8A8_SNorm = 31, + /** + A four-component, 32-bit signed-integer format that supports 8 bits per channel including alpha. + */ + R8G8B8A8_SInt = 32, + /** + A two-component, 32-bit typeless format that supports 16 bits for the red channel and 16 bits for the green channel. + */ + R16G16_Typeless = 33, + /** + A two-component, 32-bit floating-point format that supports 16 bits for the red channel and 16 bits for the green channel. + */ + R16G16_Float = 34, + /** + A two-component, 32-bit unsigned-normalized-integer format that supports 16 bits each for the green and red channels. + */ + R16G16_UNorm = 35, + /** + A two-component, 32-bit unsigned-integer format that supports 16 bits for the red channel and 16 bits for the green channel. + */ + R16G16_UInt = 36, + /** + A two-component, 32-bit signed-normalized-integer format that supports 16 bits for the red channel and 16 bits for the green channel. + */ + R16G16_SNorm = 37, + /** + A two-component, 32-bit signed-integer format that supports 16 bits for the red channel and 16 bits for the green channel. + */ + R16G16_SInt = 38, + /** + A single-component, 32-bit typeless format that supports 32 bits for the red channel. + */ + R32_Typeless = 39, + /** + A single-component, 32-bit floating-point format that supports 32 bits for depth. + */ + D32_Float = 40, + /** + A single-component, 32-bit floating-point format that supports 32 bits for the red channel. + */ + R32_Float = 41, + /** + A single-component, 32-bit unsigned-integer format that supports 32 bits for the red channel. + */ + R32_UInt = 42, + /** + A single-component, 32-bit signed-integer format that supports 32 bits for the red channel. + */ + R32_SInt = 43, + /** + A two-component, 32-bit typeless format that supports 24 bits for the red channel and 8 bits for the green channel. + */ + R24G8_Typeless = 44, + /** + A 32-bit z-buffer format that supports 24 bits for depth and 8 bits for stencil. + */ + D24_UNorm_S8_UInt = 45, + /** + A 32-bit format, that contains a 24 bit, single-component, unsigned-normalized integer, with an additional typeless 8 bits. + This format has 24 bits red channel and 8 bits unused. + */ + R24_UNorm_X8_Typeless = 46, + /** + A 32-bit format, that contains a 24 bit, single-component, typeless format, with an additional 8 bit unsigned integer component. + This format has 24 bits unused and 8 bits green channel. + */ + X24_Typeless_G8_UInt = 47, + /** + A two-component, 16-bit typeless format that supports 8 bits for the red channel and 8 bits for the green channel. + */ + R8G8_Typeless = 48, + /** + A two-component, 16-bit unsigned-normalized-integer format that supports 8 bits for the red channel and 8 bits for the green channel. + */ + R8G8_UNorm = 49, + /** + A two-component, 16-bit unsigned-integer format that supports 8 bits for the red channel and 8 bits for the green channel. + */ + R8G8_UInt = 50, + /** + A two-component, 16-bit signed-normalized-integer format that supports 8 bits for the red channel and 8 bits for the green channel. + */ + R8G8_SNorm = 51, + /** + A two-component, 16-bit signed-integer format that supports 8 bits for the red channel and 8 bits for the green channel. + */ + R8G8_SInt = 52, + /** + A single-component, 16-bit typeless format that supports 16 bits for the red channel. + */ + R16_Typeless = 53, + /** + A single-component, 16-bit floating-point format that supports 16 bits for the red channel. + */ + R16_Float = 54, + /** + A single-component, 16-bit unsigned-normalized-integer format that supports 16 bits for depth. + */ + D16_UNorm = 55, + /** + A single-component, 16-bit unsigned-normalized-integer format that supports 16 bits for the red channel. + */ + R16_UNorm = 56, + /** + A single-component, 16-bit unsigned-integer format that supports 16 bits for the red channel. + */ + R16_UInt = 57, + /** + A single-component, 16-bit signed-normalized-integer format that supports 16 bits for the red channel. + */ + R16_SNorm = 58, + /** + A single-component, 16-bit signed-integer format that supports 16 bits for the red channel. + */ + R16_SInt = 59, + /** + A single-component, 8-bit typeless format that supports 8 bits for the red channel. + */ + R8_Typeless = 60, + /** + A single-component, 8-bit unsigned-normalized-integer format that supports 8 bits for the red channel. + */ + R8_UNorm = 61, + /** + A single-component, 8-bit unsigned-integer format that supports 8 bits for the red channel. + */ + R8_UInt = 62, + /** + A single-component, 8-bit signed-normalized-integer format that supports 8 bits for the red channel. + */ + R8_SNorm = 63, + /** + A single-component, 8-bit signed-integer format that supports 8 bits for the red channel. + */ + R8_SInt = 64, + /** + A single-component, 8-bit unsigned-normalized-integer format for alpha only. + */ + A8_UNorm = 65, + /** + A single-component, 1-bit unsigned-normalized integer format that supports 1 bit for the red channel. + R1_UNorm is designed specifically for text filtering, and must be used with a format-specific, configurable 8x8 filter mode. + When calling an HLSL sampling function using this format, the address offset parameter must be set to (0,0). + */ + R1_UNorm = 66, + /** + Three partial-precision floating-point numbers encoded into a single 32-bit value all sharing the same 5-bit exponent + (variant of s10e5, which is sign bit, 10-bit mantissa, and 5-bit biased (15) exponent). + There is no sign bit, and there is a shared 5-bit biased (15) exponent and a 9-bit mantissa for each channel, + as shown in the illustration (https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/images/rgbe.png). + */ + R9G9B9E5_SHAREDEXP = 67, + /** + A four-component, 32-bit unsigned-normalized-integer format. + This packed RGB format is analogous to the UYVY format. Each 32-bit block describes a pair of pixels: + (R8, G8, B8) and (R8, G8, B8) where the R8/B8 values are repeated, and the G8 values are unique to each pixel. + + Width must be even. + */ + R8G8_B8G8_UNorm = 68, + /** + A four-component, 32-bit unsigned-normalized-integer format. + This packed RGB format is analogous to the YUY2 format. Each 32-bit block describes a pair of pixels: + (R8, G8, B8) and (R8, G8, B8) where the R8/B8 values are repeated, and the G8 values are unique to each pixel. + + Width must be even. + */ + G8R8_G8B8_UNorm = 69, + /** + Four-component typeless block-compression format. + */ + BC1_Typeless = 70, + /** + Four-component block-compression format. + */ + BC1_UNorm = 71, + /** + Four-component block-compression format for sRGB data. + */ + BC1_UNorm_SRGB = 72, + /** + Four-component typeless block-compression format. + */ + BC2_Typeless = 73, + /** + Four-component block-compression format. + */ + BC2_UNorm = 74, + /** + Four-component block-compression format for sRGB data. + */ + BC2_UNorm_SRGB = 75, + /** + Four-component typeless block-compression format. + */ + BC3_Typeless = 76, + /** + Four-component block-compression format. + */ + BC3_UNorm = 77, + /** + Four-component block-compression format for sRGB data. + */ + BC3_UNorm_SRGB = 78, + /** + One-component typeless block-compression format. + */ + BC4_Typeless = 79, + /** + One-component block-compression format. + */ + BC4_UNorm = 80, + /** + One-component block-compression format. + */ + BC4_SNorm = 81, + /** + Two-component typeless block-compression format. + */ + BC5_Typeless = 82, + /** + Two-component block-compression format. + */ + BC5_UNorm = 83, + /** + Two-component block-compression format. + */ + BC5_SNorm = 84, + /** + A three-component, 16-bit unsigned-normalized-integer format that supports 5 bits for blue, 6 bits for green, and 5 bits for red. + + Direct3D 10 through Direct3D 11: This value is defined for DXGI. + However, Direct3D 10, 10.1, or 11 devices do not support this format. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + B5G6R5_UNorm = 85, + /** + A four-component, 16-bit unsigned-normalized-integer format that supports 5 bits for each color channel and 1-bit alpha. + + Direct3D 10 through Direct3D 11: This value is defined for DXGI. + However, Direct3D 10, 10.1, or 11 devices do not support this format. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + B5G5R5A1_UNorm = 86, + /** + A four-component, 32-bit unsigned-normalized-integer format that supports 8 bits for each color channel and 8-bit alpha. + */ + B8G8R8A8_UNorm = 87, + /** + A four-component, 32-bit unsigned-normalized-integer format that supports 8 bits for each color channel and 8 bits unused. + */ + B8G8R8X8_UNorm = 88, + /** + A four-component, 32-bit 2.8-biased fixed-point format that supports 10 bits for each color channel and 2-bit alpha. + */ + R10G10B10_XR_BIAS_A2_UNorm = 89, + /** + A four-component, 32-bit typeless format that supports 8 bits for each channel including alpha. + */ + B8G8R8A8_Typeless = 90, + /** + A four-component, 32-bit unsigned-normalized standard RGB format that supports 8 bits for each channel including alpha. + */ + B8G8R8A8_UNorm_SRGB = 91, + /** + A four-component, 32-bit typeless format that supports 8 bits for each color channel, and 8 bits are unused. + */ + B8G8R8X8_Typeless = 92, + /** + A four-component, 32-bit unsigned-normalized standard RGB format that supports 8 bits for each color channel, and 8 bits are unused. + */ + B8G8R8X8_UNorm_SRGB = 93, + /** + A typeless block-compression format. + */ + BC6H_Typeless = 94, + /** + A block-compression format. + */ + BC6H_UF16 = 95, + /** + A block-compression format. + */ + BC6H_SF16 = 96, + /** + A typeless block-compression format. + */ + BC7_Typeless = 97, + /** + A block-compression format. + */ + BC7_UNorm = 98, + /** + A block-compression format. + */ + BC7_UNorm_SRGB = 99, + /** + Most common YUV 4:4:4 video resource format. Valid view formats for this video resource format are DXGI_FORMAT_R8G8B8A8_UNORM + and DXGI_FORMAT_R8G8B8A8_UINT. For UAVs, an additional valid view format is DXGI_FORMAT_R32_UINT. By using DXGI_FORMAT_R32_UINT for UAVs, you can both read and write as opposed to just write for DXGI_FORMAT_R8G8B8A8_UNORM and DXGI_FORMAT_R8G8B8A8_UINT. Supported view types are SRV, RTV, and UAV. One view provides a straightforward mapping of the entire surface. The mapping to the view channel is V->R8, + U->G8, + Y->B8, + and A->A8. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + AYUV = 100, + /** + 10-bit per channel packed YUV 4:4:4 video resource format. Valid view formats for this video resource format are + DXGI_FORMAT_R10G10B10A2_UNORM and DXGI_FORMAT_R10G10B10A2_UINT. For UAVs, an additional valid view format is DXGI_FORMAT_R32_UINT. By using DXGI_FORMAT_R32_UINT for UAVs, you can both read and write as opposed to just write for DXGI_FORMAT_R10G10B10A2_UNORM and DXGI_FORMAT_R10G10B10A2_UINT. Supported view types are SRV and UAV. One view provides a straightforward mapping of the entire surface. The mapping to the view channel is U->R10, + Y->G10, + V->B10, + and A->A2. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + Y410 = 101, + /** + 16-bit per channel packed YUV 4:4:4 video resource format. Valid view formats for this video resource format are + DXGI_FORMAT_R16G16B16A16_UNORM and DXGI_FORMAT_R16G16B16A16_UINT. Supported view types are SRV and UAV. + One view provides a straightforward mapping of the entire surface. The mapping to the view channel is U->R16, + Y->G16, + V->B16, + and A->A16. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + Y416 = 102, + /** + Most common YUV 4:2:0 video resource format. Valid luminance data view formats for this video resource format are + DXGI_FORMAT_R8_UNORM and DXGI_FORMAT_R8_UINT. Valid chrominance data view formats + (width and height are each 1/2 of luminance view) for this video resource format are + DXGI_FORMAT_R8G8_UNORM and DXGI_FORMAT_R8G8_UINT. Supported view types are SRV, RTV, and UAV. + For luminance data view, the mapping to the view channel is Y->R8. For chrominance data view, + the mapping to the view channel is U->R8 and V->G8. + + Width and height must be even. Direct3D 11 staging resources and initData parameters for this format use + (rowPitch * (height + (height / 2))) bytes. The first (SysMemPitch * height) bytes are the Y plane, + the remaining (SysMemPitch * (height / 2)) bytes are the UV plane. + + An app using the YUY 4:2:0 formats must map the luma (Y) plane separately from the chroma (UV) planes. + Developers do this by calling ID3D12Device::CreateShaderResourceView twice for the same texture and passing in + 1-channel and 2-channel formats. Passing in a 1-channel format compatible with the Y plane maps only the Y plane. + Passing in a 2-channel format compatible with the UV planes (together) maps only the U and V planes as a single resource view. + + + Direct3D 11.1: This value is not supported until Windows 8. + */ + NV12 = 103, + /** + 10-bit per channel planar YUV 4:2:0 video resource format. Valid luminance data view formats for this video resource + format are DXGI_FORMAT_R16_UNORM and DXGI_FORMAT_R16_UINT. The runtime does not enforce whether the lowest 6 bits are 0 + (given that this video resource format is a 10-bit format that uses 16 bits). If required, application shader code would + have to enforce this manually. From the runtime's point of view, DXGI_FORMAT_P010 is no different than DXGI_FORMAT_P016. + Valid chrominance data view formats (width and height are each 1/2 of luminance view) for this video resource format are + DXGI_FORMAT_R16G16_UNORM and DXGI_FORMAT_R16G16_UINT. For UAVs, an additional valid chrominance data view format is + DXGI_FORMAT_R32_UINT. By using DXGI_FORMAT_R32_UINT for UAVs, you can both read and write as opposed to just write for + DXGI_FORMAT_R16G16_UNORM and DXGI_FORMAT_R16G16_UINT. Supported view types are SRV, RTV, and UAV. For luminance data view, + the mapping to the view channel is Y->R16. For chrominance data view, the mapping to the view channel is U->R16 and V->G16. + + Width and height must be even. Direct3D 11 staging resources and initData parameters for this format use + (rowPitch * (height + (height / 2))) bytes. The first (SysMemPitch * height) bytes are the Y plane, the remaining + (SysMemPitch * (height / 2)) bytes are the UV plane. + + An app using the YUY 4:2:0 formats must map the luma (Y) plane separately from the chroma (UV) planes. Developers do + this by calling ID3D12Device::CreateShaderResourceView twice for the same texture and passing in 1-channel and 2-channel formats. + Passing in a 1-channel format compatible with the Y plane maps only the Y plane. + Passing in a 2-channel format compatible with the UV planes (together) maps only the U and V planes as a single resource view. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + P010 = 104, + /** + 16-bit per channel planar YUV 4:2:0 video resource format. Valid luminance data view formats for this video resource + format are DXGI_FORMAT_R16_UNORM and DXGI_FORMAT_R16_UINT. Valid chrominance data view formats + (width and height are each 1/2 of luminance view) for this video resource format are DXGI_FORMAT_R16G16_UNORM and + DXGI_FORMAT_R16G16_UINT. For UAVs, an additional valid chrominance data view format is DXGI_FORMAT_R32_UINT. + By using DXGI_FORMAT_R32_UINT for UAVs, you can both read and write as opposed to just write for DXGI_FORMAT_R16G16_UNORM and + DXGI_FORMAT_R16G16_UINT. Supported view types are SRV, RTV, and UAV. For luminance data view, the mapping to the view channel + is Y->R16. For chrominance data view, the mapping to the view channel is U->R16 and V->G16. + + Width and height must be even. Direct3D 11 staging resources and initData parameters for this format use + (rowPitch * (height + (height / 2))) bytes. The first (SysMemPitch * height) bytes are the Y plane, the + remaining (SysMemPitch * (height / 2)) bytes are the UV plane. + + An app using the YUY 4:2:0 formats must map the luma (Y) plane separately from the chroma (UV) planes. + Developers do this by calling ID3D12Device::CreateShaderResourceView twice for the same texture and passing in + 1-channel and 2-channel formats. Passing in a 1-channel format compatible with the Y plane maps only the Y plane. + Passing in a 2-channel format compatible with the UV planes (together) maps only the U and V planes as a single resource view. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + P016 = 105, + /** + 8-bit per channel planar YUV 4:2:0 video resource format. This format is subsampled where each pixel has its own Y value, + but each 2x2 pixel block shares a single U and V value. The runtime requires that the width and height of all resources + that are created with this format are multiples of 2. The runtime also requires that the left, right, top, and bottom + members of any RECT that are used for this format are multiples of 2. This format differs from DXGI_FORMAT_NV12 in that + the layout of the data within the resource is completely opaque to applications. Applications cannot use the CPU to map + the resource and then access the data within the resource. You cannot use shaders with this format. Because of this behavior, + legacy hardware that supports a non-NV12 4:2:0 layout (for example, YV12, and so on) can be used. Also, new hardware that has + a 4:2:0 implementation better than NV12 can be used when the application does not need the data to be in a standard layout. + + Width and height must be even. Direct3D 11 staging resources and initData parameters for this format use + (rowPitch * (height + (height / 2))) bytes. + + An app using the YUY 4:2:0 formats must map the luma (Y) plane separately from the chroma (UV) planes. + Developers do this by calling ID3D12Device::CreateShaderResourceView twice for the same texture and passing + in 1-channel and 2-channel formats. Passing in a 1-channel format compatible with the Y plane maps only the Y plane. + Passing in a 2-channel format compatible with the UV planes (together) maps only the U and V planes as a single resource view. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + OPAQUE_420 = 106, + /** + Most common YUV 4:2:2 video resource format. Valid view formats for this video resource format are DXGI_FORMAT_R8G8B8A8_UNORM + and DXGI_FORMAT_R8G8B8A8_UINT. For UAVs, an additional valid view format is DXGI_FORMAT_R32_UINT. By using DXGI_FORMAT_R32_UINT + for UAVs, you can both read and write as opposed to just write for DXGI_FORMAT_R8G8B8A8_UNORM and DXGI_FORMAT_R8G8B8A8_UINT. + Supported view types are SRV and UAV. One view provides a straightforward mapping of the entire surface. The mapping to the + view channel is Y0->R8, U0->G8, Y1->B8, and V0->A8. + + A unique valid view format for this video resource format is DXGI_FORMAT_R8G8_B8G8_UNORM. With this view format, + the width of the view appears to be twice what the DXGI_FORMAT_R8G8B8A8_UNORM or DXGI_FORMAT_R8G8B8A8_UINT + view would be when hardware reconstructs RGBA automatically on read and before filtering. + This Direct3D hardware behavior is legacy and is likely not useful any more. With this view format, + the mapping to the view channel is Y0->R8, U0->G8[0], Y1->B8, and V0->G8[1]. + + For more info about YUV formats for video rendering, see Recommended 8-Bit YUV Formats for Video Rendering. + + Width must be even. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + YUY2 = 107, + /** + 10-bit per channel packed YUV 4:2:2 video resource format. Valid view formats for this video resource format are + DXGI_FORMAT_R16G16B16A16_UNORM and DXGI_FORMAT_R16G16B16A16_UINT. The runtime does not enforce whether the lowest + 6 bits are 0 (given that this video resource format is a 10-bit format that uses 16 bits). If required, application + shader code would have to enforce this manually. From the runtime's point of view, DXGI_FORMAT_Y210 is no different + than DXGI_FORMAT_Y216. Supported view types are SRV and UAV. One view provides a straightforward mapping of the entire + surface. The mapping to the view channel is Y0->R16, U->G16, Y1->B16, and V->A16. + + Width must be even. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + Y210 = 108, + /** + 16-bit per channel packed YUV 4:2:2 video resource format. Valid view formats for this video resource format are + DXGI_FORMAT_R16G16B16A16_UNORM and DXGI_FORMAT_R16G16B16A16_UINT. Supported view types are SRV and UAV. + One view provides a straightforward mapping of the entire surface. The mapping to the view channel is Y0->R16, + U->G16, Y1->B16, and V->A16. + + Width must be even. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + Y216 = 109, + /** + Most common planar YUV 4:1:1 video resource format. Valid luminance data view formats for this video resource format are + DXGI_FORMAT_R8_UNORM and DXGI_FORMAT_R8_UINT. Valid chrominance data view formats + (width and height are each 1/4 of luminance view) for this video resource format are DXGI_FORMAT_R8G8_UNORM and + DXGI_FORMAT_R8G8_UINT. Supported view types are SRV, RTV, and UAV. For luminance data view, the mapping to the view + channel is Y->R8. For chrominance data view, the mapping to the view channel is U->R8 and V->G8. + + Width must be a multiple of 4. Direct3D11 staging resources and initData parameters for this format use + (rowPitch * height * 2) bytes. The first (SysMemPitch * height) bytes are the Y plane, the next + ((SysMemPitch / 2) * height) bytes are the UV plane, and the remainder is padding. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + NV11 = 110, + /** + 4-bit palletized YUV format that is commonly used for DVD subpicture. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + AI44 = 111, + /** + 4-bit palletized YUV format that is commonly used for DVD subpicture. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + IA44 = 112, + /** + 8-bit palletized format that is used for palletized RGB data when the processor processes ISDB-T data and + for palletized YUV data when the processor processes BluRay data. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + P8 = 113, + /** + 8-bit palletized format with 8 bits of alpha that is used for palletized YUV data when the processor processes BluRay data. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + A8P8 = 114, + /** + A four-component, 16-bit unsigned-normalized integer format that supports 4 bits for each channel including alpha. + + Direct3D 11.1: This value is not supported until Windows 8. + */ + B4G4R4A4_UNORM = 115, + /** + A video format; an 8-bit version of a hybrid planar 4:2:2 format. + */ + P208 = 130, + /** + An 8 bit YCbCrA 4:4 rendering format. + */ + V208 = 131, + /** + An 8 bit YCbCrA 4:4:4:4 rendering format. + */ + V408 = 132, + /** + Forces this enumeration to compile to 32 bits in size. + Without this value, some compilers would allow this enumeration to compile to a + size other than 32 bits. This value is not used. + */ + FORCE_UInt = 0XFFFFFFFF; + + public static implicit operator DirectX.DXGI.Format(Format format) + { + return (DirectX.DXGI.Format)(uint32)format; + } + + public bool IsInt() + { + switch(this) + { + case R32G32B32A32_SInt, R32G32B32_SInt, R16G16B16A16_SInt, R32G32_SInt, R8G8B8A8_SInt, R16G16_SInt, + R32_SInt, R8G8_SInt, R16_SInt, R8_SInt: + return true; + default: + return false; + } + } + + public bool IsUInt() + { + switch(this) + { + case R32G32B32A32_UInt, R32G32B32_UInt, R16G16B16A16_UInt, R32G32_UInt, R10G10B10A2_UInt, R8G8B8A8_UInt, R16G16_UInt, + R32_UInt, R8G8_UInt, R16_UInt, R8_UInt: + return true; + default: + return false; + } + } + + public uint32 BitsPerPixel() + { + switch (this) + { + case .R32G32B32A32_Typeless,.R32G32B32A32_Float,.R32G32B32A32_UInt,.R32G32B32A32_SInt: + return 128; + + case .R32G32B32_Typeless,.R32G32B32_Float,.R32G32B32_UInt,.R32G32B32_SInt: + return 96; + + case .R16G16B16A16_Typeless,.R16G16B16A16_Float,.R16G16B16A16_UNorm,.R16G16B16A16_UInt,.R16G16B16A16_SNorm,.R16G16B16A16_SInt,.R32G32_Typeless,.R32G32_Float,.R32G32_UInt,.R32G32_SInt,.R32G8X24_Typeless,.D32_Float_S8X24_UInt,.R32_Float_X8X24_Typeless,.X32_Typeless_G8X24_UInt,.Y416,.Y210,.Y216: + return 64; + + case .R10G10B10A2_Typeless,.R10G10B10A2_UNorm,.R10G10B10A2_UInt,.R11G11B10_Float,.R8G8B8A8_Typeless,.R8G8B8A8_UNorm,.R8G8B8A8_UNorm_SRGB,.R8G8B8A8_UInt,.R8G8B8A8_SNorm,.R8G8B8A8_SInt,.R16G16_Typeless,.R16G16_Float,.R16G16_UNorm,.R16G16_UInt,.R16G16_SNorm,.R16G16_SInt,.R32_Typeless,.D32_Float,.R32_Float,.R32_UInt,.R32_SInt,.R24G8_Typeless,.D24_UNorm_S8_UInt,.R24_UNorm_X8_Typeless,.X24_Typeless_G8_UInt,.R9G9B9E5_SHAREDEXP,.R8G8_B8G8_UNorm,.G8R8_G8B8_UNorm,.B8G8R8A8_UNorm,.B8G8R8X8_UNorm,.R10G10B10_XR_BIAS_A2_UNorm,.B8G8R8A8_Typeless,.B8G8R8A8_UNorm_SRGB,.B8G8R8X8_Typeless,.B8G8R8X8_UNorm_SRGB,.AYUV,.Y410,.YUY2: + //#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + //case .R10G10B10_7E3_A2_Float,.R10G10B10_6E4_A2_Float,.R10G10B10_SNorm_A2_UNorm: + //#endif + return 32; + + case .P010,.P016, .V408: + /*#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) + case .V408: + #endif + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + case .D16_UNorm_S8_UInt,.R16_UNorm_X8_Typeless,.X16_Typeless_G8_UInt: + #endif*/ + return 24; + + case .R8G8_Typeless,.R8G8_UNorm,.R8G8_UInt,.R8G8_SNorm,.R8G8_SInt,.R16_Typeless,.R16_Float,.D16_UNorm,.R16_UNorm,.R16_UInt,.R16_SNorm,.R16_SInt,.B5G6R5_UNorm,.B5G5R5A1_UNorm,.A8P8,.B4G4R4A4_UNORM, .P208,.V208: + /*#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) + case .P208,.V208: + #endif*/ + return 16; + + case .NV12,.OPAQUE_420,.NV11: + return 12; + + case .R8_Typeless,.R8_UNorm,.R8_UInt,.R8_SNorm,.R8_SInt,.A8_UNorm,.BC2_Typeless,.BC2_UNorm,.BC2_UNorm_SRGB,.BC3_Typeless,.BC3_UNorm,.BC3_UNorm_SRGB,.BC5_Typeless,.BC5_UNorm,.BC5_SNorm,.BC6H_Typeless,.BC6H_UF16,.BC6H_SF16,.BC7_Typeless,.BC7_UNorm,.BC7_UNorm_SRGB,.AI44,.IA44,.P8: + /*#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + case .R4G4_UNorm: + #endif*/ + return 8; + + case .R1_UNorm: + return 1; + + case .BC1_Typeless,.BC1_UNorm,.BC1_UNorm_SRGB,.BC4_Typeless,.BC4_UNorm,.BC4_SNorm: + return 4; + + //case .Unknown,.FORCE_UInt: + default: + return 0; + } + } + + /// Returns the SRGB-Format for the given Format, or the Format itself, if no SRGB-variant exists. + public Format GetSRGB() + { + switch (this) + { + case .BC1_UNorm: + return .BC1_UNorm_SRGB; + case .BC2_UNorm: + return .BC2_UNorm_SRGB; + case .BC3_UNorm: + return .BC3_UNorm_SRGB; + case .BC7_UNorm: + return .BC7_UNorm_SRGB; + case .R8G8B8A8_UNorm: + return .R8G8B8A8_UNorm_SRGB; + case .B8G8R8A8_UNorm: + return .B8G8R8A8_UNorm_SRGB; + case .B8G8R8X8_UNorm: + return .B8G8R8X8_UNorm_SRGB; + default: + return this; + } + } + + /// Returns the non-SRGB-Format for the given Format. + public Format GetNonSRGB() + { + switch (this) + { + case .BC1_UNorm_SRGB: + return .BC1_UNorm; + case .BC2_UNorm_SRGB: + return .BC2_UNorm; + case .BC3_UNorm_SRGB: + return .BC3_UNorm; + case .BC7_UNorm_SRGB: + return .BC7_UNorm; + case .R8G8B8A8_UNorm_SRGB: + return .R8G8B8A8_UNorm; + case .B8G8R8A8_UNorm_SRGB: + return .B8G8R8A8_UNorm; + case .B8G8R8X8_UNorm_SRGB: + return .B8G8R8X8_UNorm; + default: + return this; + } + } + + //[OnCompile(.TypeInit), Comptime] + /* + private static void GenerateGetFormatInfo() + { + Type type = typeof(Self); + + String emit = new String(); + + emit.Append(""" + public FormatInfo GetFormatInfo() + { + switch(this) + { + + """); + + for (FieldInfo field in type.GetFields()) + { + if (!field.[Friend]mFieldData.mFlags.HasFlag(.EnumCase)) + continue; + + Result formatResult = GetInfoInternal(field.Name); + + if (formatResult case .Err) + continue; + + FormatInfo format = formatResult.Value; + + emit.AppendF($""" + case .{field.Name}: + FormatInfo info{field.Name} = default; + info{field.Name}.ComponentCount = {format.ComponentCount}; + + """); + + for (int i < format.ComponentCount) + { + ComponentInfo component = format.Components[i]; + + emit.AppendF($""" + info{field.Name}.Components[{i}] = .("{component.Name}", {component.BitSize}, ComponentDataType.{component.DataType}, {component.IsSrgb ? "true" : "false"}, {component.BitOffset}); + + """); + } + + emit.AppendF($""" + return info{field.Name}; + + """); + } + + emit.Append(""" + default: + return default; + } + } + """); + + Compiler.EmitTypeBody(typeof(Self), emit); + } + */ + + [Comptime] + private static Result GetInfoInternal(StringView name) + { + FormatInfo info = default; + + // 0->Name, 1->Size + int decodeState = 0; + + StringView componentName = default; + StringView componentSize = default; + + int contextStart = 0; + + int totalBitSize = 0; + + for (char8 c in name) + { + if (c.IsLetter) + { + if (decodeState != 0 || componentName.IsEmpty) + { + decodeState = 0; + + if (!componentName.IsEmpty) + { + if (info.ComponentCount >= 4) + { + return .Err; + } + + ComponentInfo componentInfo = .(componentName, Try!(int.Parse(componentSize)), .Typeless, false, totalBitSize); + + info.Components[info.ComponentCount] = componentInfo; + info.ComponentCount++; + + totalBitSize += componentInfo.BitSize; + + componentName = default; + } + + componentName = name.Substring(@c.Index, 1); + } + else + { + componentName.Length += 1; + } + + if (componentName.Equals("srgb", true)) + { + for (int i = contextStart; i < info.ComponentCount; i++) + { + info.Components[i].IsSrgb = true; + } + componentName = default; + } + else if (componentName.Equals("typeless", true)) + { + for (int i = contextStart; i < info.ComponentCount; i++) + { + info.Components[i].DataType = .Typeless; + } + componentName = default; + } + else if (componentName.Equals("float", true)) + { + for (int i = contextStart; i < info.ComponentCount; i++) + { + info.Components[i].DataType = .Float; + } + componentName = default; + } + else if (componentName.Equals("uint", true)) + { + for (int i = contextStart; i < info.ComponentCount; i++) + { + info.Components[i].DataType = .UInt; + } + componentName = default; + } + else if (componentName.Equals("sint", true)) + { + for (int i = contextStart; i < info.ComponentCount; i++) + { + info.Components[i].DataType = .SInt; + } + componentName = default; + } + else if (componentName.Equals("unorm", true)) + { + for (int i = contextStart; i < info.ComponentCount; i++) + { + info.Components[i].DataType = .UNorm; + } + componentName = default; + } + else if (componentName.Equals("snorm", true)) + { + for (int i = contextStart; i < info.ComponentCount; i++) + { + info.Components[i].DataType = .SNorm; + } + componentName = default; + } + } + else if (c.IsDigit) + { + if (decodeState == 0) + { + decodeState = 1; + componentSize = name.Substring(@c.Index, 1); + } + else + { + componentSize.Length += 1; + } + } + else if (c == '_') + { + decodeState = 2; + } + } + + return info; + } +} + +struct FormatInfo +{ + public int ComponentCount; + public ComponentInfo[4] Components; + + public this(int componentCount, ComponentInfo[4] components) + { + ComponentCount = componentCount; + Components = components; + } +} + +struct ComponentInfo +{ + public StringView Name; + public int BitSize; + public ComponentDataType DataType; + public bool IsSrgb; + public int BitOffset; + + public this(StringView name, int bitSize, ComponentDataType dataType, bool isSrgb, int bitOffset) + { + Name = name; + BitSize = bitSize; + DataType = dataType; + IsSrgb = isSrgb; + BitOffset = bitOffset; + } +} + +enum ComponentDataType +{ + /// The type wasn't specified, just some bits. + Typeless, + Float, + UInt, + SInt, + UNorm, + SNorm +}