diff --git a/CUE4Parse b/CUE4Parse index badffcd..f7303f5 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit badffcd0421a468667e7253fa3387b21f451af5d +Subproject commit f7303f56da41b9bc43b983e48ecc4644a684c310 diff --git a/Editor/ImGuiFontTexture.cs b/Editor/ImGuiFontTexture.cs index b376ba6..046ba13 100644 --- a/Editor/ImGuiFontTexture.cs +++ b/Editor/ImGuiFontTexture.cs @@ -4,7 +4,7 @@ namespace Editor; -public class ImGuiFontTexture() : Texture2D(0, 0, SizedInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.UnsignedByte) +public class ImGuiFontTexture() : Texture2D(0, 0, SizedInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte) { protected override bool CanBeViewed => false; // a preview is opaque, the glyphs need their alpha diff --git a/Editor/Managers/ImGuiController.cs b/Editor/Managers/ImGuiController.cs index becc910..eb8a9b9 100644 --- a/Editor/Managers/ImGuiController.cs +++ b/Editor/Managers/ImGuiController.cs @@ -12,6 +12,7 @@ using Snooper.Core.Containers; using Snooper.Core.Containers.Buffers; using Snooper.Core.Containers.Programs; +using Snooper.Core.Containers.Resources; using Snooper.UI; using ErrorCode = OpenTK.Graphics.OpenGL4.ErrorCode; @@ -37,17 +38,11 @@ public void Load() _ebo.Allocate(45000); _vbo.Allocate(30000); - GL.VertexArrayVertexBuffer(_vao, 0, _vbo, 0, _vbo.Stride); GL.VertexArrayElementBuffer(_vao, _ebo); - GL.VertexArrayAttribFormat(_vao, 0, 2, VertexAttribType.Float, false, 0); - GL.VertexArrayAttribFormat(_vao, 1, 2, VertexAttribType.Float, false, 8); - GL.VertexArrayAttribFormat(_vao, 2, 4, VertexAttribType.UnsignedByte, true, 16); - GL.EnableVertexArrayAttrib(_vao, 0); - GL.EnableVertexArrayAttrib(_vao, 1); - GL.EnableVertexArrayAttrib(_vao, 2); - GL.VertexArrayAttribBinding(_vao, 0, 0); - GL.VertexArrayAttribBinding(_vao, 1, 0); - GL.VertexArrayAttribBinding(_vao, 2, 0); + new VertexArrayLayout(_vao, _vbo, _vbo.Stride) + .Float(0, 2) + .Float(1, 2, offset: 8) + .Float(2, 4, VertexAttribType.UnsignedByte, true, 16); _fontTexture.Generate(); diff --git a/Snooper/Core/Containers/Textures/ITextureFormatInfo.cs b/Snooper/Core/Containers/Textures/ITextureFormatInfo.cs index adaebdc..9b5cd38 100644 --- a/Snooper/Core/Containers/Textures/ITextureFormatInfo.cs +++ b/Snooper/Core/Containers/Textures/ITextureFormatInfo.cs @@ -1,30 +1,35 @@ -using OpenTK.Graphics.OpenGL4; -using Snooper.Extensions; +using CUE4Parse.UE4.Assets.Exports.Texture; +using OpenTK.Graphics.OpenGL4; namespace Snooper.Core.Containers.Textures; public interface ITextureFormatInfo { public SizedInternalFormat InternalFormat { get; } + public SizedInternalFormat LinearFormat { get; } public long GetMemorySize(int width, int height, int depth = 1); - public bool IsSrgb => InternalFormat.IsSrgb(); + public bool IsSrgb => InternalFormat != LinearFormat; } public readonly struct TextureFormatInfo : ITextureFormatInfo { public SizedInternalFormat InternalFormat { get; } + public SizedInternalFormat LinearFormat { get; } public readonly PixelFormat Format; public readonly PixelType Type; private readonly int _bytesPerPixel; + private readonly string? _name; - public TextureFormatInfo(SizedInternalFormat internalFormat, PixelFormat format, PixelType type) + public TextureFormatInfo(SizedInternalFormat internalFormat, PixelFormat format, PixelType type, SizedInternalFormat? linearFormat = null, string? name = null) { InternalFormat = internalFormat; + LinearFormat = linearFormat ?? internalFormat; Format = format; Type = type; + _name = name; _bytesPerPixel = GetBytesPerPixel(); } @@ -36,186 +41,47 @@ public long GetMemorySize(int width, int height, int depth = 1) private int GetBytesPerPixel() { - return InternalFormat switch + return LinearFormat switch { - // 8-bit formats SizedInternalFormat.R8 => 1, - SizedInternalFormat.Rg8 => 2, + SizedInternalFormat.Rg8 or SizedInternalFormat.Rg8Snorm => 2, SizedInternalFormat.Rgb8 or SizedInternalFormat.Srgb8 => 3, SizedInternalFormat.Rgba8 or SizedInternalFormat.Srgb8Alpha8 => 4, - // 16-bit formats - SizedInternalFormat.R16 => 2, - SizedInternalFormat.R16f => 2, - SizedInternalFormat.Rg16 => 4, - SizedInternalFormat.Rg16f => 4, + SizedInternalFormat.R16 or SizedInternalFormat.R16f => 2, + SizedInternalFormat.Rg16 or SizedInternalFormat.Rg16f => 4, SizedInternalFormat.Rgb16 or SizedInternalFormat.Rgb16f => 6, SizedInternalFormat.Rgba16 or SizedInternalFormat.Rgba16f => 8, - // 32-bit formats - SizedInternalFormat.R32f => 4, + SizedInternalFormat.R32f or SizedInternalFormat.R32ui => 4, SizedInternalFormat.Rg32f => 8, SizedInternalFormat.Rgb32f => 12, SizedInternalFormat.Rgba32f => 16, - // Depth and stencil formats SizedInternalFormat.DepthComponent16 => 2, SizedInternalFormat.DepthComponent24 => 3, - SizedInternalFormat.DepthComponent32f => 4, - SizedInternalFormat.Depth24Stencil8 => 4, + SizedInternalFormat.DepthComponent32f or SizedInternalFormat.Depth24Stencil8 => 4, SizedInternalFormat.Depth32fStencil8 => 5, - _ => Type switch - { - PixelType.UnsignedByte => 4, // Default RGBA8 - PixelType.Float => 16, // Default RGBA32F - PixelType.HalfFloat => 8, // Default RGBA16F - PixelType.UnsignedShort => 8, // Default RGBA16 - _ => 4 - } + _ => 0 }; } - public override string ToString() - { - var name = InternalFormat switch - { - SizedInternalFormat.R8 => "R8", - SizedInternalFormat.Rg8 => "RG8", - SizedInternalFormat.Rgb8 or SizedInternalFormat.Srgb8 => "RGB8", - SizedInternalFormat.Rgba8 or SizedInternalFormat.Srgb8Alpha8 => "RGBA8", - - SizedInternalFormat.R16 => "R16", - SizedInternalFormat.R16f => "R16F", - SizedInternalFormat.Rg16 => "RG16", - SizedInternalFormat.Rg16f => "RG16F", - SizedInternalFormat.Rgb16 => "RGB16", - SizedInternalFormat.Rgb16f => "RGB16F", - SizedInternalFormat.Rgba16 => "RGBA16", - SizedInternalFormat.Rgba16f => "RGBA16F", - - SizedInternalFormat.R32f => "R32F", - SizedInternalFormat.R32ui => "R32UI", - SizedInternalFormat.Rg32f => "RG32F", - SizedInternalFormat.Rgb32f => "RGB32F", - SizedInternalFormat.Rgba32f => "RGBA32F", - - SizedInternalFormat.DepthComponent16 => "D16", - SizedInternalFormat.DepthComponent24 => "D24", - SizedInternalFormat.DepthComponent32f => "D32F", - SizedInternalFormat.Depth24Stencil8 => "D24S8", - SizedInternalFormat.Depth32fStencil8 => "D32FS8", - - _ => InternalFormat.ToString() - }; - - return Format switch - { - PixelFormat.Bgra => $"BGRA -> {name}", - PixelFormat.Bgr => $"BGR -> {name}", - _ => name - }; - } + public override string ToString() => _name ?? InternalFormat.ToString(); } -public readonly struct CompressedTextureFormatInfo : ITextureFormatInfo +public readonly struct CompressedTextureFormatInfo(SizedInternalFormat internalFormat, SizedInternalFormat linearFormat, FPixelFormatInfo blocks) : ITextureFormatInfo { - public SizedInternalFormat InternalFormat { get; } - - private readonly int _blockSize; - private readonly int _blockWidth; - private readonly int _blockHeight; - - public CompressedTextureFormatInfo(SizedInternalFormat internalFormat) - { - InternalFormat = internalFormat; - - _blockSize = GetBlockSize(); - (_blockWidth, _blockHeight) = GetBlockDimensions(); - } + public SizedInternalFormat InternalFormat { get; } = internalFormat; + public SizedInternalFormat LinearFormat { get; } = linearFormat; public long GetMemorySize(int width, int height, int depth = 1) { - var blocksWide = (width + _blockWidth - 1) / _blockWidth; - var blocksHigh = (height + _blockHeight - 1) / _blockHeight; - - return (long)blocksWide * blocksHigh * depth * _blockSize; - } - - private int GetBlockSize() - { - return InternalFormat switch - { - SizedInternalFormat.CompressedRgbaS3tcDxt1Ext or - SizedInternalFormat.CompressedSrgbAlphaS3tcDxt1Ext or - SizedInternalFormat.CompressedRedRgtc1 => 8, - - SizedInternalFormat.CompressedRgbaS3tcDxt3Ext or - SizedInternalFormat.CompressedSrgbAlphaS3tcDxt3Ext or - SizedInternalFormat.CompressedRgbaS3tcDxt5Ext or - SizedInternalFormat.CompressedSrgbAlphaS3tcDxt5Ext or - SizedInternalFormat.CompressedRgRgtc2 or - SizedInternalFormat.CompressedRgbBptcUnsignedFloat or - SizedInternalFormat.CompressedRgbaBptcUnorm => 16, - - _ when IsAstcFormat() => 16, - - _ when IsEtc2RgbFormat() => 8, - _ when IsEtc2RgbaFormat() => 16, - - _ => 16 - }; - } - - private (int width, int height) GetBlockDimensions() - { - return InternalFormat switch - { - SizedInternalFormat.CompressedRgbaAstc4X4 or SizedInternalFormat.CompressedSrgb8Alpha8Astc4X4 => (4, 4), - SizedInternalFormat.CompressedRgbaAstc6X6 or SizedInternalFormat.CompressedSrgb8Alpha8Astc6X6 => (6, 6), - SizedInternalFormat.CompressedRgbaAstc8X8 or SizedInternalFormat.CompressedSrgb8Alpha8Astc8X8 => (8, 8), - SizedInternalFormat.CompressedRgbaAstc10X10 or SizedInternalFormat.CompressedSrgb8Alpha8Astc10X10 => (10, 10), - SizedInternalFormat.CompressedRgbaAstc12X12 or SizedInternalFormat.CompressedSrgb8Alpha8Astc12X12 => (12, 12), + var blocksWide = (width + blocks.BlockSizeX - 1) / blocks.BlockSizeX; + var blocksHigh = (height + blocks.BlockSizeY - 1) / blocks.BlockSizeY; - _ => (4, 4) - }; - } - - private bool IsAstcFormat() - { - return InternalFormat is - SizedInternalFormat.CompressedRgbaAstc4X4 or SizedInternalFormat.CompressedSrgb8Alpha8Astc4X4 or - SizedInternalFormat.CompressedRgbaAstc6X6 or SizedInternalFormat.CompressedSrgb8Alpha8Astc6X6 or - SizedInternalFormat.CompressedRgbaAstc8X8 or SizedInternalFormat.CompressedSrgb8Alpha8Astc8X8 or - SizedInternalFormat.CompressedRgbaAstc10X10 or SizedInternalFormat.CompressedSrgb8Alpha8Astc10X10 or - SizedInternalFormat.CompressedRgbaAstc12X12 or SizedInternalFormat.CompressedSrgb8Alpha8Astc12X12; + return (long)blocksWide * blocksHigh * depth * blocks.BlockBytes; } - private bool IsEtc2RgbFormat() - { - return InternalFormat is SizedInternalFormat.CompressedRgb8Etc2 or SizedInternalFormat.CompressedSrgb8Etc2; - } - - private bool IsEtc2RgbaFormat() - { - return InternalFormat is SizedInternalFormat.CompressedRgba8Etc2Eac or SizedInternalFormat.CompressedSrgb8Alpha8Etc2Eac; - } - - public override string ToString() => InternalFormat switch - { - SizedInternalFormat.CompressedRgbaS3tcDxt1Ext or SizedInternalFormat.CompressedSrgbAlphaS3tcDxt1Ext => "BC1 RGB", - SizedInternalFormat.CompressedRgbaS3tcDxt3Ext or SizedInternalFormat.CompressedSrgbAlphaS3tcDxt3Ext => "BC2 RGBA", - SizedInternalFormat.CompressedRgbaS3tcDxt5Ext or SizedInternalFormat.CompressedSrgbAlphaS3tcDxt5Ext => "BC3 RGBA", - SizedInternalFormat.CompressedRedRgtc1 => "BC4 R", - SizedInternalFormat.CompressedRgRgtc2 => "BC5 RG", - SizedInternalFormat.CompressedRgbBptcUnsignedFloat => "BC6H RGB", - SizedInternalFormat.CompressedRgbaBptcUnorm => "BC7 RGBA", - - SizedInternalFormat.CompressedRgb8Etc2 or SizedInternalFormat.CompressedSrgb8Etc2 => "ETC2 RGB", - SizedInternalFormat.CompressedRgba8Etc2Eac or SizedInternalFormat.CompressedSrgb8Alpha8Etc2Eac => "ETC2 RGBA", - - _ when IsAstcFormat() => $"ASTC {_blockWidth}x{_blockHeight} RGBA", - - _ => InternalFormat.ToString() - }; + public override string ToString() => blocks.Name; } diff --git a/Snooper/Core/Containers/Textures/Texture.cs b/Snooper/Core/Containers/Textures/Texture.cs index 6651caa..57efa9d 100644 --- a/Snooper/Core/Containers/Textures/Texture.cs +++ b/Snooper/Core/Containers/Textures/Texture.cs @@ -36,6 +36,7 @@ protected ITextureFormatInfo FormatInfo } public bool IsSrgb => FormatInfo.IsSrgb; + public SizedInternalFormat LinearFormat => FormatInfo.LinearFormat; public string FormatName { get; private set; } = string.Empty; diff --git a/Snooper/Core/Containers/Textures/Texture2D.cs b/Snooper/Core/Containers/Textures/Texture2D.cs index 0652a47..aa4f817 100644 --- a/Snooper/Core/Containers/Textures/Texture2D.cs +++ b/Snooper/Core/Containers/Textures/Texture2D.cs @@ -1,4 +1,5 @@ -using CUE4Parse_Conversion.Textures; +using System.Collections.Concurrent; +using CUE4Parse_Conversion.Textures; using CUE4Parse.UE4.Assets.Exports.Texture; using CUE4Parse.UE4.Objects.Core.Misc; using OpenTK.Graphics.OpenGL4; @@ -23,6 +24,8 @@ public class Texture2D(int width, int height, private (TextureMinFilter Min, TextureMagFilter Mag, TextureWrapMode WrapS, TextureWrapMode WrapT) _sampling; private byte[][]? _levels; + private static readonly ConcurrentDictionary _decodedFormats = new(); + public Texture2D(UTexture texture) : this(texture.PlatformData.SizeX, texture.PlatformData.SizeY, guid: texture.LightingGuid, name: texture.Name) { _owner = texture; @@ -98,7 +101,7 @@ public override void Prepare() Width = textureData.Width; Height = textureData.Height; - FormatInfo = textureData.PixelFormat.GetTextureFormat(owner.SRGB); + FormatInfo = textureData.PixelFormat.GetTextureFormat(owner.SRGB) ?? throw new NotSupportedException($"{textureData.PixelFormat} can't be uploaded."); _levels = [textureData.Data]; } else @@ -121,7 +124,6 @@ public override void Prepare() Width = mips[first].SizeX; Height = mips[first].SizeY; - FormatInfo = owner.Format.GetTextureFormat(owner.SRGB); for (var i = first + 1; i < mips.Length && !_terrain; i++) { @@ -131,6 +133,24 @@ public override void Prepare() levels.Add(data); } + var format = owner.Format; + if (format.GetTextureFormat(owner.SRGB) is not { } info) + { + var source = PixelFormatUtils.PixelFormats[format]; + for (var i = 0; i < levels.Count; i++) + { + TextureDecoder.DecodeBytes(levels[i], Math.Max(1, Width >> i), Math.Max(1, Height >> i), 1, source, owner.IsNormalMap, out var data, out format); + levels[i] = data; + } + info = format.GetTextureFormat(owner.SRGB) ?? throw new NotSupportedException($"{owner.Format} can't be decoded."); + + if (_decodedFormats.TryAdd(owner.Format, 0)) + { + Log.Information("{Format} can't be uploaded as is, decoding it to {Decoded} on the CPU", owner.Format, format); + } + } + FormatInfo = info; + _levels = [.. levels]; byte[]? Read(int index) => mips[index].BulkData?.ReadDataOnce() is { Length: > 0 } data ? data diff --git a/Snooper/Core/Containers/Textures/TextureView.cs b/Snooper/Core/Containers/Textures/TextureView.cs index d3a230b..119d721 100644 --- a/Snooper/Core/Containers/Textures/TextureView.cs +++ b/Snooper/Core/Containers/Textures/TextureView.cs @@ -1,5 +1,4 @@ using OpenTK.Graphics.OpenGL4; -using Snooper.Extensions; namespace Snooper.Core.Containers.Textures; @@ -20,7 +19,7 @@ public override void Generate() uint original = source; GL.GetTextureLevelParameter(original, 0, GetTextureParameter.TextureInternalFormat, out int format); - if (linear) format = (int) ((SizedInternalFormat) format).ToLinear(); + if (linear) format = (int) source.LinearFormat; var handle = GL.GenTexture(); GL.TextureView(handle, source.Target, (int) original, (PixelInternalFormat) format, 0, source.MipCount, 0, 1); diff --git a/Snooper/Core/Hardware/DeviceInfo.cs b/Snooper/Core/Hardware/DeviceInfo.cs index 1e174bf..2cde0f7 100644 --- a/Snooper/Core/Hardware/DeviceInfo.cs +++ b/Snooper/Core/Hardware/DeviceInfo.cs @@ -16,6 +16,7 @@ public class DeviceInfo public static bool IsIntel { get; private set; } public static bool HasFragmentBarycentric { get; private set; } + public static bool HasAstc { get; private set; } public static string GlslDefines { get; private set; } = string.Empty; public static string FragmentGlslDefines { get; private set; } = string.Empty; @@ -33,6 +34,7 @@ public void Load() IsIntel = Vendor.Contains("Intel", StringComparison.OrdinalIgnoreCase); HasFragmentBarycentric = ExtensionSupport.SupportFragmentBarycentric; + HasAstc = ExtensionSupport.SupportAstcLdr; GlslDefines = IsIntel ? "#define BINDLESS_RAW_HANDLES\n" : string.Empty; FragmentGlslDefines = HasFragmentBarycentric ? "#extension GL_NV_fragment_shader_barycentric : require\n#define WIREFRAME\n" : string.Empty; } diff --git a/Snooper/Core/Hardware/ExtensionSupport.cs b/Snooper/Core/Hardware/ExtensionSupport.cs index 3547d97..08243d4 100644 --- a/Snooper/Core/Hardware/ExtensionSupport.cs +++ b/Snooper/Core/Hardware/ExtensionSupport.cs @@ -24,6 +24,11 @@ public class ExtensionSupport /// public bool SupportFragmentBarycentric { get; private set; } + /// + /// GL_KHR_texture_compression_astc_ldr + /// + public bool SupportAstcLdr { get; private set; } + public string[] Extensions { get; private set; } = []; public void Load() @@ -46,6 +51,9 @@ public void Load() case "GL_NV_fragment_shader_barycentric": SupportFragmentBarycentric = true; break; + case "GL_KHR_texture_compression_astc_ldr": + SupportAstcLdr = true; + break; } Extensions[i] = ext; diff --git a/Snooper/Extensions/TextureExtensions.cs b/Snooper/Extensions/TextureExtensions.cs index 849c215..6eda8bb 100644 --- a/Snooper/Extensions/TextureExtensions.cs +++ b/Snooper/Extensions/TextureExtensions.cs @@ -5,6 +5,7 @@ using CUE4Parse_Conversion.Textures.BC; using OpenTK.Graphics.OpenGL4; using Snooper.Core.Containers.Textures; +using Snooper.Core.Hardware; using Snooper.Rendering.Cache; namespace Snooper.Extensions; @@ -111,16 +112,55 @@ bool Swizzle(ReadOnlySpan layout, out EMaskChannels absent) } } - public static ITextureFormatInfo GetTextureFormat(this EPixelFormat format, bool srgb) + public static ITextureFormatInfo? GetTextureFormat(this EPixelFormat format, bool srgb) { - if (format.IsCompressed()) + return format switch { - var compressed = format.GetCompressedFormat(); - return new CompressedTextureFormatInfo(srgb ? compressed.ToSrgb() : compressed); - } + EPixelFormat.PF_R8G8B8A8 => Pixels(SizedInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte, SizedInternalFormat.Srgb8Alpha8), + EPixelFormat.PF_B8G8R8A8 or EPixelFormat.PF_A8R8G8B8 => Pixels(SizedInternalFormat.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte, SizedInternalFormat.Srgb8Alpha8), + EPixelFormat.PF_G8 => Pixels(SizedInternalFormat.R8, PixelFormat.Red, PixelType.UnsignedByte), + EPixelFormat.PF_V8U8 => Pixels(SizedInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte), + EPixelFormat.PF_A32B32G32R32F => Pixels(SizedInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.Float), + EPixelFormat.PF_FloatRGB => Pixels(SizedInternalFormat.Rgb16f, PixelFormat.Rgb, PixelType.HalfFloat), + EPixelFormat.PF_FloatRGBA => Pixels(SizedInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat), + EPixelFormat.PF_R32_FLOAT => Pixels(SizedInternalFormat.R32f, PixelFormat.Red, PixelType.Float), + EPixelFormat.PF_G16R16F or EPixelFormat.PF_G16R16F_FILTER => Pixels(SizedInternalFormat.Rg16f, PixelFormat.Rg, PixelType.HalfFloat), + EPixelFormat.PF_G16R16 => Pixels(SizedInternalFormat.Rg16, PixelFormat.Rg, PixelType.UnsignedShort), + EPixelFormat.PF_G32R32F => Pixels(SizedInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float), + EPixelFormat.PF_A16B16G16R16 => Pixels(SizedInternalFormat.Rgba16, PixelFormat.Rgba, PixelType.UnsignedShort), + EPixelFormat.PF_R16F or EPixelFormat.PF_R16F_FILTER => Pixels(SizedInternalFormat.R16f, PixelFormat.Red, PixelType.HalfFloat), + EPixelFormat.PF_G16 => Pixels(SizedInternalFormat.R16, PixelFormat.Red, PixelType.UnsignedShort), + EPixelFormat.PF_R32G32B32F => Pixels(SizedInternalFormat.Rgb32f, PixelFormat.Rgb, PixelType.Float), + + EPixelFormat.PF_DXT1 => Blocks(SizedInternalFormat.CompressedRgbaS3tcDxt1Ext, SizedInternalFormat.CompressedSrgbAlphaS3tcDxt1Ext), + EPixelFormat.PF_DXT3 => Blocks(SizedInternalFormat.CompressedRgbaS3tcDxt3Ext, SizedInternalFormat.CompressedSrgbAlphaS3tcDxt3Ext), + EPixelFormat.PF_DXT5 => Blocks(SizedInternalFormat.CompressedRgbaS3tcDxt5Ext, SizedInternalFormat.CompressedSrgbAlphaS3tcDxt5Ext), + EPixelFormat.PF_BC4 => Blocks(SizedInternalFormat.CompressedRedRgtc1), + EPixelFormat.PF_BC5 => Blocks(SizedInternalFormat.CompressedRgRgtc2), + EPixelFormat.PF_BC6H => Blocks(SizedInternalFormat.CompressedRgbBptcUnsignedFloat), + EPixelFormat.PF_BC6H_Signed => Blocks(SizedInternalFormat.CompressedRgbBptcSignedFloat), + EPixelFormat.PF_BC7 => Blocks(SizedInternalFormat.CompressedRgbaBptcUnorm, SizedInternalFormat.CompressedSrgbAlphaBptcUnorm), + + EPixelFormat.PF_ASTC_4x4 when DeviceInfo.HasAstc => Blocks(SizedInternalFormat.CompressedRgbaAstc4X4, SizedInternalFormat.CompressedSrgb8Alpha8Astc4X4), + EPixelFormat.PF_ASTC_6x6 when DeviceInfo.HasAstc => Blocks(SizedInternalFormat.CompressedRgbaAstc6X6, SizedInternalFormat.CompressedSrgb8Alpha8Astc6X6), + EPixelFormat.PF_ASTC_8x5 when DeviceInfo.HasAstc => Blocks(SizedInternalFormat.CompressedRgbaAstc8X5, SizedInternalFormat.CompressedSrgb8Alpha8Astc8X5), + EPixelFormat.PF_ASTC_8x6 when DeviceInfo.HasAstc => Blocks(SizedInternalFormat.CompressedRgbaAstc8X6, SizedInternalFormat.CompressedSrgb8Alpha8Astc8X6), + EPixelFormat.PF_ASTC_8x8 when DeviceInfo.HasAstc => Blocks(SizedInternalFormat.CompressedRgbaAstc8X8, SizedInternalFormat.CompressedSrgb8Alpha8Astc8X8), + EPixelFormat.PF_ASTC_10x8 when DeviceInfo.HasAstc => Blocks(SizedInternalFormat.CompressedRgbaAstc10X8, SizedInternalFormat.CompressedSrgb8Alpha8Astc10X8), + EPixelFormat.PF_ASTC_10x10 when DeviceInfo.HasAstc => Blocks(SizedInternalFormat.CompressedRgbaAstc10X10, SizedInternalFormat.CompressedSrgb8Alpha8Astc10X10), + EPixelFormat.PF_ASTC_12x12 when DeviceInfo.HasAstc => Blocks(SizedInternalFormat.CompressedRgbaAstc12X12, SizedInternalFormat.CompressedSrgb8Alpha8Astc12X12), + + EPixelFormat.PF_ETC1 => Blocks(SizedInternalFormat.CompressedRgb8Etc2, SizedInternalFormat.CompressedSrgb8Etc2), + EPixelFormat.PF_ETC2_RGB => Blocks(SizedInternalFormat.CompressedRgb8Etc2, SizedInternalFormat.CompressedSrgb8Etc2), + EPixelFormat.PF_ETC2_RGBA => Blocks(SizedInternalFormat.CompressedRgba8Etc2Eac, SizedInternalFormat.CompressedSrgb8Alpha8Etc2Eac), + EPixelFormat.PF_ETC2_R11 or EPixelFormat.PF_ETC2_R11_EAC => Blocks(SizedInternalFormat.CompressedR11Eac), + EPixelFormat.PF_ETC2_RG11_EAC => Blocks(SizedInternalFormat.CompressedRg11Eac), + + _ => null + }; - var (internalFormat, pixelFormat, pixelType) = format.GetUncompressedFormats(); - return new TextureFormatInfo(srgb ? internalFormat.ToSrgb() : internalFormat, pixelFormat, pixelType); + ITextureFormatInfo Pixels(SizedInternalFormat linear, PixelFormat pixelFormat, PixelType type, SizedInternalFormat? srgbFormat = null) => new TextureFormatInfo(srgb ? srgbFormat ?? linear : linear, pixelFormat, type, linear, PixelFormatUtils.PixelFormats[format].Name); + ITextureFormatInfo Blocks(SizedInternalFormat linear, SizedInternalFormat? srgbFormat = null) => new CompressedTextureFormatInfo(srgb ? srgbFormat ?? linear : linear, linear, PixelFormatUtils.PixelFormats[format]); } public static PixelInternalFormat ToPixelInternalFormat(this SizedInternalFormat format) @@ -176,190 +216,6 @@ public static InternalFormat ToInternalFormat(this SizedInternalFormat format) }; } - private static bool IsCompressed(this EPixelFormat format) - => format switch - { - EPixelFormat.PF_B8G8R8A8 or - EPixelFormat.PF_R8G8B8A8 or - EPixelFormat.PF_A8R8G8B8 or - EPixelFormat.PF_G8 or - EPixelFormat.PF_V8U8 or - EPixelFormat.PF_A32B32G32R32F or - EPixelFormat.PF_FloatRGB or - EPixelFormat.PF_FloatRGBA or - EPixelFormat.PF_R32_FLOAT or - EPixelFormat.PF_G16R16F or - EPixelFormat.PF_G16R16F_FILTER or - EPixelFormat.PF_G16R16 or - EPixelFormat.PF_G32R32F or - EPixelFormat.PF_A16B16G16R16 or - EPixelFormat.PF_R16F or - EPixelFormat.PF_R16F_FILTER or - EPixelFormat.PF_G16 or - EPixelFormat.PF_R32G32B32F => false, - _ => true - }; - - private static (SizedInternalFormat, PixelFormat, PixelType) GetUncompressedFormats(this EPixelFormat format) - { - return format switch - { - EPixelFormat.PF_B8G8R8A8 => ( - SizedInternalFormat.Rgba8, - PixelFormat.Bgra, - PixelType.UnsignedByte - ), - EPixelFormat.PF_R8G8B8A8 => ( - SizedInternalFormat.Rgba8, - PixelFormat.Rgba, - PixelType.UnsignedByte - ), - EPixelFormat.PF_A8R8G8B8 => ( - SizedInternalFormat.Rgba8, - PixelFormat.Bgra, - PixelType.UnsignedByte - ), - EPixelFormat.PF_G8 => ( - SizedInternalFormat.R8, - PixelFormat.Red, - PixelType.UnsignedByte - ), - EPixelFormat.PF_V8U8 => ( - SizedInternalFormat.Rg8Snorm, - PixelFormat.Rg, - PixelType.Byte - ), - EPixelFormat.PF_A32B32G32R32F => ( - SizedInternalFormat.Rgba32f, - PixelFormat.Rgba, - PixelType.Float - ), - EPixelFormat.PF_FloatRGB => ( - SizedInternalFormat.Rgb16f, - PixelFormat.Rgb, - PixelType.HalfFloat - ), - EPixelFormat.PF_FloatRGBA => ( - SizedInternalFormat.Rgba16f, - PixelFormat.Rgba, - PixelType.HalfFloat - ), - EPixelFormat.PF_R32_FLOAT => ( - SizedInternalFormat.R32f, - PixelFormat.Red, - PixelType.Float - ), - EPixelFormat.PF_G16R16F or EPixelFormat.PF_G16R16F_FILTER => ( - SizedInternalFormat.Rg16f, - PixelFormat.Rg, - PixelType.HalfFloat - ), - EPixelFormat.PF_G16R16 => ( - SizedInternalFormat.Rg16, - PixelFormat.Rg, - PixelType.UnsignedShort - ), - EPixelFormat.PF_G32R32F => ( - SizedInternalFormat.Rg32f, - PixelFormat.Rg, - PixelType.Float - ), - EPixelFormat.PF_A16B16G16R16 => ( - SizedInternalFormat.Rgba16, - PixelFormat.Rgba, - PixelType.UnsignedShort - ), - EPixelFormat.PF_R16F or EPixelFormat.PF_R16F_FILTER => ( - SizedInternalFormat.R16f, - PixelFormat.Red, - PixelType.HalfFloat - ), - EPixelFormat.PF_G16 => ( - SizedInternalFormat.R16, - PixelFormat.Red, - PixelType.UnsignedShort - ), - EPixelFormat.PF_R32G32B32F => ( - SizedInternalFormat.Rgb32f, - PixelFormat.Rgb, - PixelType.Float - ), - _ => throw new NotImplementedException($"Unsupported pixel format: {format}") - }; - } - - private static SizedInternalFormat GetCompressedFormat(this EPixelFormat format) - { - return format switch - { - EPixelFormat.PF_DXT1 => SizedInternalFormat.CompressedRgbaS3tcDxt1Ext, - EPixelFormat.PF_DXT3 => SizedInternalFormat.CompressedRgbaS3tcDxt3Ext, - EPixelFormat.PF_DXT5 => SizedInternalFormat.CompressedRgbaS3tcDxt5Ext, - EPixelFormat.PF_BC4 => SizedInternalFormat.CompressedRedRgtc1, - EPixelFormat.PF_BC5 => SizedInternalFormat.CompressedRgRgtc2, - EPixelFormat.PF_BC6H => SizedInternalFormat.CompressedRgbBptcUnsignedFloat, - EPixelFormat.PF_BC7 => SizedInternalFormat.CompressedRgbaBptcUnorm, - - EPixelFormat.PF_ASTC_4x4 => SizedInternalFormat.CompressedRgbaAstc4X4, - EPixelFormat.PF_ASTC_6x6 => SizedInternalFormat.CompressedRgbaAstc6X6, - EPixelFormat.PF_ASTC_8x8 => SizedInternalFormat.CompressedRgbaAstc8X8, - EPixelFormat.PF_ASTC_10x10 => SizedInternalFormat.CompressedRgbaAstc10X10, - EPixelFormat.PF_ASTC_12x12 => SizedInternalFormat.CompressedRgbaAstc12X12, - - // EPixelFormat.PF_ETC1 => SizedInternalFormat.CompressedRgb8Etc2, - EPixelFormat.PF_ETC2_RGB => SizedInternalFormat.CompressedRgb8Etc2, - EPixelFormat.PF_ETC2_RGBA => SizedInternalFormat.CompressedRgba8Etc2Eac, - - _ => throw new NotImplementedException($"Unsupported pixel format: {format}") - }; - } - - private static readonly (SizedInternalFormat Linear, SizedInternalFormat Srgb)[] _srgbFormats = - [ - (SizedInternalFormat.Rgb8, SizedInternalFormat.Srgb8), - (SizedInternalFormat.Rgba8, SizedInternalFormat.Srgb8Alpha8), - (SizedInternalFormat.CompressedRgbaS3tcDxt1Ext, SizedInternalFormat.CompressedSrgbAlphaS3tcDxt1Ext), - (SizedInternalFormat.CompressedRgbaS3tcDxt3Ext, SizedInternalFormat.CompressedSrgbAlphaS3tcDxt3Ext), - (SizedInternalFormat.CompressedRgbaS3tcDxt5Ext, SizedInternalFormat.CompressedSrgbAlphaS3tcDxt5Ext), - (SizedInternalFormat.CompressedRgbaAstc4X4, SizedInternalFormat.CompressedSrgb8Alpha8Astc4X4), - (SizedInternalFormat.CompressedRgbaAstc6X6, SizedInternalFormat.CompressedSrgb8Alpha8Astc6X6), - (SizedInternalFormat.CompressedRgbaAstc8X8, SizedInternalFormat.CompressedSrgb8Alpha8Astc8X8), - (SizedInternalFormat.CompressedRgbaAstc10X10, SizedInternalFormat.CompressedSrgb8Alpha8Astc10X10), - (SizedInternalFormat.CompressedRgbaAstc12X12, SizedInternalFormat.CompressedSrgb8Alpha8Astc12X12), - (SizedInternalFormat.CompressedRgb8Etc2, SizedInternalFormat.CompressedSrgb8Etc2), - (SizedInternalFormat.CompressedRgba8Etc2Eac, SizedInternalFormat.CompressedSrgb8Alpha8Etc2Eac), - ]; - - public static bool IsSrgb(this SizedInternalFormat format) - { - foreach (var (_, srgb) in _srgbFormats) - { - if (srgb == format) return true; - } - - return false; - } - - public static SizedInternalFormat ToSrgb(this SizedInternalFormat format) - { - foreach (var (linear, srgb) in _srgbFormats) - { - if (linear == format) return srgb; - } - - return format; - } - - public static SizedInternalFormat ToLinear(this SizedInternalFormat format) - { - foreach (var (linear, srgb) in _srgbFormats) - { - if (srgb == format) return linear; - } - - return format; - } - public static bool TryGetFaceAverages(this UTextureCube texture, Span faces) { if (texture.PlatformData.Mips is not { Length: > 0 } mips || mips[^1].BulkData?.Data is not { } data) return false;