Add support for BC6 and BC7 decompression, decompress all BC formats if they are not supported by the host

This commit is contained in:
gdk 2022-05-22 00:05:14 -03:00 committed by riperiperi
parent 65cecb0c2f
commit 782a0c4e93
18 changed files with 1435 additions and 138 deletions

View file

@ -11,6 +11,9 @@ namespace Ryujinx.Graphics.GAL
public readonly bool HasVectorIndexingBug;
public readonly bool SupportsAstcCompression;
public readonly bool SupportsBc123Compression;
public readonly bool SupportsBc45Compression;
public readonly bool SupportsBc67Compression;
public readonly bool Supports3DTextureCompression;
public readonly bool SupportsBgraFormat;
public readonly bool SupportsR4G4Format;
@ -40,6 +43,9 @@ namespace Ryujinx.Graphics.GAL
bool hasFrontFacingBug,
bool hasVectorIndexingBug,
bool supportsAstcCompression,
bool supportsBc123Compression,
bool supportsBc45Compression,
bool supportsBc67Compression,
bool supports3DTextureCompression,
bool supportsBgraFormat,
bool supportsR4G4Format,
@ -66,6 +72,9 @@ namespace Ryujinx.Graphics.GAL
HasFrontFacingBug = hasFrontFacingBug;
HasVectorIndexingBug = hasVectorIndexingBug;
SupportsAstcCompression = supportsAstcCompression;
SupportsBc123Compression = supportsBc123Compression;
SupportsBc45Compression = supportsBc45Compression;
SupportsBc67Compression = supportsBc67Compression;
Supports3DTextureCompression = supports3DTextureCompression;
SupportsBgraFormat = supportsBgraFormat;
SupportsR4G4Format = supportsR4G4Format;

View file

@ -844,7 +844,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
data = PixelConverter.ConvertR4G4ToR4G4B4A4(data);
}
else if (!_context.Capabilities.Supports3DTextureCompression && Target == Target.Texture3D)
else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
{
switch (Format)
{
@ -868,6 +868,14 @@ namespace Ryujinx.Graphics.Gpu.Image
case Format.Bc5Unorm:
data = BCnDecoder.DecodeBC5(data, width, height, depth, levels, layers, Format == Format.Bc5Snorm);
break;
case Format.Bc6HSfloat:
case Format.Bc6HUfloat:
data = BCnDecoder.DecodeBC6(data, width, height, depth, levels, layers, Format == Format.Bc6HSfloat);
break;
case Format.Bc7Srgb:
case Format.Bc7Unorm:
data = BCnDecoder.DecodeBC7(data, width, height, depth, levels, layers);
break;
}
}

View file

@ -88,9 +88,9 @@ namespace Ryujinx.Graphics.Gpu.Image
return new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4);
}
if (!caps.Supports3DTextureCompression && info.Target == Target.Texture3D)
if (!HostSupportsBcFormat(info.FormatInfo.Format, info.Target, caps))
{
// The host API does not support 3D compressed formats.
// The host API does not this compressed format.
// We assume software decompression will be done for those textures,
// and so we adjust the format here to match the decompressor output.
switch (info.FormatInfo.Format)
@ -98,10 +98,12 @@ namespace Ryujinx.Graphics.Gpu.Image
case Format.Bc1RgbaSrgb:
case Format.Bc2Srgb:
case Format.Bc3Srgb:
case Format.Bc7Srgb:
return new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4);
case Format.Bc1RgbaUnorm:
case Format.Bc2Unorm:
case Format.Bc3Unorm:
case Format.Bc7Unorm:
return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
case Format.Bc4Unorm:
return new FormatInfo(Format.R8Unorm, 1, 1, 1, 1);
@ -111,12 +113,50 @@ namespace Ryujinx.Graphics.Gpu.Image
return new FormatInfo(Format.R8G8Unorm, 1, 1, 2, 2);
case Format.Bc5Snorm:
return new FormatInfo(Format.R8G8Snorm, 1, 1, 2, 2);
case Format.Bc6HSfloat:
case Format.Bc6HUfloat:
return new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8, 4);
}
}
return info.FormatInfo;
}
/// <summary>
/// Checks if the host API supports a given texture compression format of the BC family.
/// </summary>
/// <param name="format">BC format to be checked</param>
/// <param name="target">Target usage of the texture</param>
/// <param name="caps">Host GPU Capabilities</param>
/// <returns>True if the texture host supports the format with the given target usage, false otherwise</returns>
public static bool HostSupportsBcFormat(Format format, Target target, Capabilities caps)
{
bool not3DOr3DCompressionSupported = target != Target.Texture3D || caps.Supports3DTextureCompression;
switch (format)
{
case Format.Bc1RgbaSrgb:
case Format.Bc1RgbaUnorm:
case Format.Bc2Srgb:
case Format.Bc2Unorm:
case Format.Bc3Srgb:
case Format.Bc3Unorm:
return caps.SupportsBc123Compression && not3DOr3DCompressionSupported;
case Format.Bc4Unorm:
case Format.Bc4Snorm:
case Format.Bc5Unorm:
case Format.Bc5Snorm:
return caps.SupportsBc45Compression && not3DOr3DCompressionSupported;
case Format.Bc6HSfloat:
case Format.Bc6HUfloat:
case Format.Bc7Srgb:
case Format.Bc7Unorm:
return caps.SupportsBc67Compression && not3DOr3DCompressionSupported;
}
return true;
}
/// <summary>
/// Determines whether a texture can flush its data back to guest memory.
/// </summary>
@ -748,7 +788,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>True if the texture target and samples count matches, false otherwise</returns>
public static bool TargetAndSamplesCompatible(TextureInfo lhs, TextureInfo rhs)
{
return lhs.Target == rhs.Target &&
return lhs.Target == rhs.Target &&
lhs.SamplesInX == rhs.SamplesInX &&
lhs.SamplesInY == rhs.SamplesInY;
}

View file

@ -17,6 +17,9 @@ namespace Ryujinx.Graphics.OpenGL
private static readonly Lazy<bool> _supportsQuads = new Lazy<bool>(SupportsQuadsCheck);
private static readonly Lazy<bool> _supportsSeamlessCubemapPerTexture = new Lazy<bool>(() => HasExtension("GL_ARB_seamless_cubemap_per_texture"));
private static readonly Lazy<bool> _supportsShaderBallot = new Lazy<bool>(() => HasExtension("GL_ARB_shader_ballot"));
private static readonly Lazy<bool> _supportsTextureCompressionBptc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_bptc"));
private static readonly Lazy<bool> _supportsTextureCompressionRgtc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_rgtc"));
private static readonly Lazy<bool> _supportsTextureCompressionS3tc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_s3tc"));
private static readonly Lazy<bool> _supportsTextureShadowLod = new Lazy<bool>(() => HasExtension("GL_EXT_texture_shadow_lod"));
private static readonly Lazy<bool> _supportsViewportSwizzle = new Lazy<bool>(() => HasExtension("GL_NV_viewport_swizzle"));
@ -56,6 +59,9 @@ namespace Ryujinx.Graphics.OpenGL
public static bool SupportsQuads => _supportsQuads.Value;
public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value;
public static bool SupportsShaderBallot => _supportsShaderBallot.Value;
public static bool SupportsTextureCompressionBptc => _supportsTextureCompressionBptc.Value;
public static bool SupportsTextureCompressionRgtc => _supportsTextureCompressionRgtc.Value;
public static bool SupportsTextureCompressionS3tc => _supportsTextureCompressionS3tc.Value;
public static bool SupportsTextureShadowLod => _supportsTextureShadowLod.Value;
public static bool SupportsViewportSwizzle => _supportsViewportSwizzle.Value;

View file

@ -105,6 +105,9 @@ namespace Ryujinx.Graphics.OpenGL
hasFrontFacingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows,
hasVectorIndexingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows,
supportsAstcCompression: HwCapabilities.SupportsAstcCompression,
supportsBc123Compression: HwCapabilities.SupportsTextureCompressionS3tc,
supportsBc45Compression: HwCapabilities.SupportsTextureCompressionRgtc,
supportsBc67Compression: true, // Should check BPTC extension, but for some reason NVIDIA is not exposing the extension.
supports3DTextureCompression: false,
supportsBgraFormat: false,
supportsR4G4Format: false,

View file

@ -0,0 +1,819 @@
using Ryujinx.Graphics.Texture.Utils;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Texture
{
static class BC6Decoder
{
private const int HalfOne = 0x3C00;
public static void Decode(Span<byte> output, ReadOnlySpan<byte> data, int width, int height, bool signed)
{
ReadOnlySpan<Block> blocks = MemoryMarshal.Cast<byte, Block>(data);
Span<ulong> output64 = MemoryMarshal.Cast<byte, ulong>(output);
int wInBlocks = (width + 3) / 4;
int hInBlocks = (height + 3) / 4;
for (int y = 0; y < hInBlocks; y++)
{
int y2 = y * 4;
int bh = Math.Min(4, height - y2);
for (int x = 0; x < wInBlocks; x++)
{
int x2 = x * 4;
int bw = Math.Min(4, width - x2);
DecodeBlock(blocks[y * wInBlocks + x], output64.Slice(y2 * width + x2), bw, bh, width, signed);
}
}
}
private static void DecodeBlock(Block block, Span<ulong> output, int w, int h, int width, bool signed)
{
int mode = (int)(block.Low & 3);
if ((mode & 2) != 0)
{
mode = (int)(block.Low & 0x1f);
}
Span<RgbaColor32> endPoints = stackalloc RgbaColor32[4];
int subsetCount = DecodeEndPoints(ref block, endPoints, mode, signed);
if (subsetCount == 0)
{
// Mode is invalid, the spec mandates that hardware fills the block with
// a opaque black color.
for (int ty = 0; ty < h; ty++)
{
int baseOffs = ty * width;
for (int tx = 0; tx < w; tx++)
{
output[baseOffs + tx] = (ulong)HalfOne << 48;
}
}
return;
}
int partition;
int indexBitCount;
ulong indices;
if (subsetCount > 1)
{
partition = (int)((block.High >> 13) & 0x1F);
indexBitCount = 3;
int fixUpIndex = BC67Tables.FixUpIndices[subsetCount - 1][partition][1] * 3;
ulong lowMask = (ulong.MaxValue >> (65 - fixUpIndex)) << 3;
ulong highMask = ulong.MaxValue << (fixUpIndex + 3);
indices = ((block.High >> 16) & highMask) | ((block.High >> 17) & lowMask) | ((block.High >> 18) & 3);
}
else
{
partition = 0;
indexBitCount = 4;
indices = (block.High & ~0xFUL) | ((block.High >> 1) & 7);
}
ulong indexMask = (1UL << indexBitCount) - 1;
for (int ty = 0; ty < h; ty++)
{
int baseOffs = ty * width;
for (int tx = 0; tx < w; tx++)
{
int offs = baseOffs + tx;
int index = (int)(indices & indexMask);
int endPointBase = BC67Tables.PartitionTable[subsetCount - 1][partition][ty * 4 + tx] << 1;
RgbaColor32 color1 = endPoints[endPointBase];
RgbaColor32 color2 = endPoints[endPointBase + 1];
RgbaColor32 color = BC67Utils.Interpolate(color1, color2, index, indexBitCount);
output[offs] =
(ulong)FinishUnquantize(color.R, signed) |
((ulong)FinishUnquantize(color.G, signed) << 16) |
((ulong)FinishUnquantize(color.B, signed) << 32) |
((ulong)HalfOne << 48);
indices >>= indexBitCount;
}
}
}
private static int DecodeEndPoints(ref Block block, Span<RgbaColor32> endPoints, int mode, bool signed)
{
ulong low = block.Low;
ulong high = block.High;
int r0 = 0, g0 = 0, b0 = 0, r1 = 0, g1 = 0, b1 = 0, r2 = 0, g2 = 0, b2 = 0, r3 = 0, g3 = 0, b3 = 0;
int subsetCount;
switch (mode)
{
case 0:
r0 = (int)(low >> 5) & 0x3FF;
g0 = (int)(low >> 15) & 0x3FF;
b0 = (int)(low >> 25) & 0x3FF;
if (signed)
{
r0 = SignExtend(r0, 10);
g0 = SignExtend(g0, 10);
b0 = SignExtend(b0, 10);
}
r1 = r0 + SignExtend((int)(low >> 35), 5);
g1 = g0 + SignExtend((int)(low >> 45), 5);
b1 = b0 + SignExtend((int)(low >> 55), 5);
r2 = r0 + SignExtend((int)(high >> 1), 5);
g2 = g0 + SignExtend((int)(((low << 2) & 0x10) | ((low >> 41) & 0xF)), 5);
b2 = b0 + SignExtend((int)(((low << 1) & 0x10) | ((high << 3) & 0x08) | (low >> 61)), 5);
r3 = r0 + SignExtend((int)(high >> 7), 5);
g3 = g0 + SignExtend((int)(((low >> 36) & 0x10) | ((low >> 51) & 0xF)), 5);
b3 = b0 + SignExtend((int)(
((low) & 0x10) |
((high >> 9) & 0x08) |
((high >> 4) & 0x04) |
((low >> 59) & 0x02) |
((low >> 50) & 0x01)), 5);
r0 = Unquantize(r0, 10, signed);
g0 = Unquantize(g0, 10, signed);
b0 = Unquantize(b0, 10, signed);
r1 = Unquantize(r1 & 0x3FF, 10, signed);
g1 = Unquantize(g1 & 0x3FF, 10, signed);
b1 = Unquantize(b1 & 0x3FF, 10, signed);
r2 = Unquantize(r2 & 0x3FF, 10, signed);
g2 = Unquantize(g2 & 0x3FF, 10, signed);
b2 = Unquantize(b2 & 0x3FF, 10, signed);
r3 = Unquantize(r3 & 0x3FF, 10, signed);
g3 = Unquantize(g3 & 0x3FF, 10, signed);
b3 = Unquantize(b3 & 0x3FF, 10, signed);
subsetCount = 2;
break;
case 1:
r0 = (int)(low >> 5) & 0x7F;
g0 = (int)(low >> 15) & 0x7F;
b0 = (int)(low >> 25) & 0x7F;
if (signed)
{
r0 = SignExtend(r0, 7);
g0 = SignExtend(g0, 7);
b0 = SignExtend(b0, 7);
}
r1 = r0 + SignExtend((int)(low >> 35), 6);
g1 = g0 + SignExtend((int)(low >> 45), 6);
b1 = b0 + SignExtend((int)(low >> 55), 6);
r2 = r0 + SignExtend((int)(high >> 1), 6);
g2 = g0 + SignExtend((int)(((low << 3) & 0x20) | ((low >> 20) & 0x10) | ((low >> 41) & 0x0F)), 6);
b2 = b0 + SignExtend((int)(
((low >> 17) & 0x20) |
((low >> 10) & 0x10) |
((high << 3) & 0x08) |
(low >> 61)), 6);
r3 = r0 + SignExtend((int)(high >> 7), 6);
g3 = g0 + SignExtend((int)(((low << 1) & 0x30) | ((low >> 51) & 0xF)), 6);
b3 = b0 + SignExtend((int)(
((low >> 28) & 0x20) |
((low >> 30) & 0x10) |
((low >> 29) & 0x08) |
((low >> 21) & 0x04) |
((low >> 12) & 0x03)), 6);
r0 = Unquantize(r0, 7, signed);
g0 = Unquantize(g0, 7, signed);
b0 = Unquantize(b0, 7, signed);
r1 = Unquantize(r1 & 0x7F, 7, signed);
g1 = Unquantize(g1 & 0x7F, 7, signed);
b1 = Unquantize(b1 & 0x7F, 7, signed);
r2 = Unquantize(r2 & 0x7F, 7, signed);
g2 = Unquantize(g2 & 0x7F, 7, signed);
b2 = Unquantize(b2 & 0x7F, 7, signed);
r3 = Unquantize(r3 & 0x7F, 7, signed);
g3 = Unquantize(g3 & 0x7F, 7, signed);
b3 = Unquantize(b3 & 0x7F, 7, signed);
subsetCount = 2;
break;
case 2:
r0 = (int)(((low >> 30) & 0x400) | ((low >> 5) & 0x3FF));
g0 = (int)(((low >> 39) & 0x400) | ((low >> 15) & 0x3FF));
b0 = (int)(((low >> 49) & 0x400) | ((low >> 25) & 0x3FF));
if (signed)
{
r0 = SignExtend(r0, 11);
g0 = SignExtend(g0, 11);
b0 = SignExtend(b0, 11);
}
r1 = r0 + SignExtend((int)(low >> 35), 5);
g1 = g0 + SignExtend((int)(low >> 45), 4);
b1 = b0 + SignExtend((int)(low >> 55), 4);
r2 = r0 + SignExtend((int)(high >> 1), 5);
g2 = g0 + SignExtend((int)(low >> 41), 4);
b2 = b0 + SignExtend((int)(((high << 3) & 8) | (low >> 61)), 4);
r3 = r0 + SignExtend((int)(high >> 7), 5);
g3 = g0 + SignExtend((int)(low >> 51), 4);
b3 = b0 + SignExtend((int)(
((high >> 9) & 8) |
((high >> 4) & 4) |
((low >> 59) & 2) |
((low >> 50) & 1)), 4);
r0 = Unquantize(r0, 11, signed);
g0 = Unquantize(g0, 11, signed);
b0 = Unquantize(b0, 11, signed);
r1 = Unquantize(r1 & 0x7FF, 11, signed);
g1 = Unquantize(g1 & 0x7FF, 11, signed);
b1 = Unquantize(b1 & 0x7FF, 11, signed);
r2 = Unquantize(r2 & 0x7FF, 11, signed);
g2 = Unquantize(g2 & 0x7FF, 11, signed);
b2 = Unquantize(b2 & 0x7FF, 11, signed);
r3 = Unquantize(r3 & 0x7FF, 11, signed);
g3 = Unquantize(g3 & 0x7FF, 11, signed);
b3 = Unquantize(b3 & 0x7FF, 11, signed);
subsetCount = 2;
break;
case 3:
r0 = (int)(low >> 5) & 0x3FF;
g0 = (int)(low >> 15) & 0x3FF;
b0 = (int)(low >> 25) & 0x3FF;
r1 = (int)(low >> 35) & 0x3FF;
g1 = (int)(low >> 45) & 0x3FF;
b1 = (int)(((high << 9) & 0x200) | (low >> 55));
if (signed)
{
r0 = SignExtend(r0, 10);
g0 = SignExtend(g0, 10);
b0 = SignExtend(b0, 10);
r1 = SignExtend(r1, 10);
g1 = SignExtend(g1, 10);
b1 = SignExtend(b1, 10);
}
r0 = Unquantize(r0, 10, signed);
g0 = Unquantize(g0, 10, signed);
b0 = Unquantize(b0, 10, signed);
r1 = Unquantize(r1, 10, signed);
g1 = Unquantize(g1, 10, signed);
b1 = Unquantize(b1, 10, signed);
subsetCount = 1;
break;
case 6:
r0 = (int)(((low >> 29) & 0x400) | ((low >> 5) & 0x3FF));
g0 = (int)(((low >> 40) & 0x400) | ((low >> 15) & 0x3FF));
b0 = (int)(((low >> 49) & 0x400) | ((low >> 25) & 0x3FF));
if (signed)
{
r0 = SignExtend(r0, 11);
g0 = SignExtend(g0, 11);
b0 = SignExtend(b0, 11);
}
r1 = r0 + SignExtend((int)(low >> 35), 4);
g1 = g0 + SignExtend((int)(low >> 45), 5);
b1 = b0 + SignExtend((int)(low >> 55), 4);
r2 = r0 + SignExtend((int)(high >> 1), 4);
g2 = g0 + SignExtend((int)(((high >> 7) & 0x10) | ((low >> 41) & 0x0F)), 5);
b2 = b0 + SignExtend((int)(((high << 3) & 0x08) | ((low >> 61))), 4);
r3 = r0 + SignExtend((int)(high >> 7), 4);
g3 = g0 + SignExtend((int)(((low >> 36) & 0x10) | ((low >> 51) & 0x0F)), 5);
b3 = b0 + SignExtend((int)(
((high >> 9) & 8) |
((high >> 4) & 4) |
((low >> 59) & 2) |
((high >> 5) & 1)), 4);
r0 = Unquantize(r0, 11, signed);
g0 = Unquantize(g0, 11, signed);
b0 = Unquantize(b0, 11, signed);
r1 = Unquantize(r1 & 0x7FF, 11, signed);
g1 = Unquantize(g1 & 0x7FF, 11, signed);
b1 = Unquantize(b1 & 0x7FF, 11, signed);
r2 = Unquantize(r2 & 0x7FF, 11, signed);
g2 = Unquantize(g2 & 0x7FF, 11, signed);
b2 = Unquantize(b2 & 0x7FF, 11, signed);
r3 = Unquantize(r3 & 0x7FF, 11, signed);
g3 = Unquantize(g3 & 0x7FF, 11, signed);
b3 = Unquantize(b3 & 0x7FF, 11, signed);
subsetCount = 2;
break;
case 7:
r0 = (int)(((low >> 34) & 0x400) | ((low >> 5) & 0x3FF));
g0 = (int)(((low >> 44) & 0x400) | ((low >> 15) & 0x3FF));
b0 = (int)(((high << 10) & 0x400) | ((low >> 25) & 0x3FF));
if (signed)
{
r0 = SignExtend(r0, 11);
g0 = SignExtend(g0, 11);
b0 = SignExtend(b0, 11);
}
r1 = (r0 + SignExtend((int)(low >> 35), 9)) & 0x7FF;
g1 = (g0 + SignExtend((int)(low >> 45), 9)) & 0x7FF;
b1 = (b0 + SignExtend((int)(low >> 55), 9)) & 0x7FF;
r0 = Unquantize(r0, 11, signed);
g0 = Unquantize(g0, 11, signed);
b0 = Unquantize(b0, 11, signed);
r1 = Unquantize(r1, 11, signed);
g1 = Unquantize(g1, 11, signed);
b1 = Unquantize(b1, 11, signed);
subsetCount = 1;
break;
case 10:
r0 = (int)(((low >> 29) & 0x400) | ((low >> 5) & 0x3FF));
g0 = (int)(((low >> 39) & 0x400) | ((low >> 15) & 0x3FF));
b0 = (int)(((low >> 50) & 0x400) | ((low >> 25) & 0x3FF));
if (signed)
{
r0 = SignExtend(r0, 11);
g0 = SignExtend(g0, 11);
b0 = SignExtend(b0, 11);
}
r1 = r0 + SignExtend((int)(low >> 35), 4);
g1 = g0 + SignExtend((int)(low >> 45), 4);
b1 = b0 + SignExtend((int)(low >> 55), 5);
r2 = r0 + SignExtend((int)(high >> 1), 4);
g2 = g0 + SignExtend((int)(low >> 41), 4);
b2 = b0 + SignExtend((int)(((low >> 36) & 0x10) | ((high << 3) & 8) | (low >> 61)), 5);
r3 = r0 + SignExtend((int)(high >> 7), 4);
g3 = g0 + SignExtend((int)(low >> 51), 4);
b3 = b0 + SignExtend((int)(
((high >> 7) & 0x10) |
((high >> 9) & 0x08) |
((high >> 4) & 0x06) |
((low >> 50) & 0x01)), 5);
r0 = Unquantize(r0, 11, signed);
g0 = Unquantize(g0, 11, signed);
b0 = Unquantize(b0, 11, signed);
r1 = Unquantize(r1 & 0x7FF, 11, signed);
g1 = Unquantize(g1 & 0x7FF, 11, signed);
b1 = Unquantize(b1 & 0x7FF, 11, signed);
r2 = Unquantize(r2 & 0x7FF, 11, signed);
g2 = Unquantize(g2 & 0x7FF, 11, signed);
b2 = Unquantize(b2 & 0x7FF, 11, signed);
r3 = Unquantize(r3 & 0x7FF, 11, signed);
g3 = Unquantize(g3 & 0x7FF, 11, signed);
b3 = Unquantize(b3 & 0x7FF, 11, signed);
subsetCount = 2;
break;
case 11:
r0 = (int)(((low >> 32) & 0x800) | ((low >> 34) & 0x400) | ((low >> 5) & 0x3FF));
g0 = (int)(((low >> 42) & 0x800) | ((low >> 44) & 0x400) | ((low >> 15) & 0x3FF));
b0 = (int)(((low >> 52) & 0x800) | ((high << 10) & 0x400) | ((low >> 25) & 0x3FF));
if (signed)
{
r0 = SignExtend(r0, 12);
g0 = SignExtend(g0, 12);
b0 = SignExtend(b0, 12);
}
r1 = (r0 + SignExtend((int)(low >> 35), 8)) & 0xFFF;
g1 = (g0 + SignExtend((int)(low >> 45), 8)) & 0xFFF;
b1 = (b0 + SignExtend((int)(low >> 55), 8)) & 0xFFF;
r0 = Unquantize(r0, 12, signed);
g0 = Unquantize(g0, 12, signed);
b0 = Unquantize(b0, 12, signed);
r1 = Unquantize(r1, 12, signed);
g1 = Unquantize(g1, 12, signed);
b1 = Unquantize(b1, 12, signed);
subsetCount = 1;
break;
case 14:
r0 = (int)(low >> 5) & 0x1FF;
g0 = (int)(low >> 15) & 0x1FF;
b0 = (int)(low >> 25) & 0x1FF;
if (signed)
{
r0 = SignExtend(r0, 9);
g0 = SignExtend(g0, 9);
b0 = SignExtend(b0, 9);
}
r1 = r0 + SignExtend((int)(low >> 35), 5);
g1 = g0 + SignExtend((int)(low >> 45), 5);
b1 = b0 + SignExtend((int)(low >> 55), 5);
r2 = r0 + SignExtend((int)(high >> 1), 5);
g2 = g0 + SignExtend((int)(((low >> 20) & 0x10) | ((low >> 41) & 0xF)), 5);
b2 = b0 + SignExtend((int)(((low >> 10) & 0x10) | ((high << 3) & 8) | (low >> 61)), 5);
r3 = r0 + SignExtend((int)(high >> 7), 5);
g3 = g0 + SignExtend((int)(((low >> 36) & 0x10) | ((low >> 51) & 0xF)), 5);
b3 = b0 + SignExtend((int)(
((low >> 30) & 0x10) |
((high >> 9) & 0x08) |
((high >> 4) & 0x04) |
((low >> 59) & 0x02) |
((low >> 50) & 0x01)), 5);
r0 = Unquantize(r0, 9, signed);
g0 = Unquantize(g0, 9, signed);
b0 = Unquantize(b0, 9, signed);
r1 = Unquantize(r1 & 0x1FF, 9, signed);
g1 = Unquantize(g1 & 0x1FF, 9, signed);
b1 = Unquantize(b1 & 0x1FF, 9, signed);
r2 = Unquantize(r2 & 0x1FF, 9, signed);
g2 = Unquantize(g2 & 0x1FF, 9, signed);
b2 = Unquantize(b2 & 0x1FF, 9, signed);
r3 = Unquantize(r3 & 0x1FF, 9, signed);
g3 = Unquantize(g3 & 0x1FF, 9, signed);
b3 = Unquantize(b3 & 0x1FF, 9, signed);
subsetCount = 2;
break;
case 15:
r0 = (BitReverse6((int)(low >> 39) & 0x3F) << 10) | ((int)(low >> 5) & 0x3FF);
g0 = (BitReverse6((int)(low >> 49) & 0x3F) << 10) | ((int)(low >> 15) & 0x3FF);
b0 = ((BitReverse6((int)(low >> 59)) | (int)(high & 1)) << 10) | ((int)(low >> 25) & 0x3FF);
if (signed)
{
r0 = SignExtend(r0, 16);
g0 = SignExtend(g0, 16);
b0 = SignExtend(b0, 16);
}
r1 = (r0 + SignExtend((int)(low >> 35), 4)) & 0xFFFF;
g1 = (g0 + SignExtend((int)(low >> 45), 4)) & 0xFFFF;
b1 = (b0 + SignExtend((int)(low >> 55), 4)) & 0xFFFF;
subsetCount = 1;
break;
case 18:
r0 = (int)(low >> 5) & 0xFF;
g0 = (int)(low >> 15) & 0xFF;
b0 = (int)(low >> 25) & 0xFF;
if (signed)
{
r0 = SignExtend(r0, 8);
g0 = SignExtend(g0, 8);
b0 = SignExtend(b0, 8);
}
r1 = r0 + SignExtend((int)(low >> 35), 6);
g1 = g0 + SignExtend((int)(low >> 45), 5);
b1 = b0 + SignExtend((int)(low >> 55), 5);
r2 = r0 + SignExtend((int)(high >> 1), 6);
g2 = g0 + SignExtend((int)(((low >> 20) & 0x10) | ((low >> 41) & 0xF)), 5);
b2 = b0 + SignExtend((int)(((low >> 10) & 0x10) | ((high << 3) & 8) | (low >> 61)), 5);
r3 = r0 + SignExtend((int)(high >> 7), 6);
g3 = g0 + SignExtend((int)(((low >> 9) & 0x10) | ((low >> 51) & 0xF)), 5);
b3 = b0 + SignExtend((int)(
((low >> 30) & 0x18) |
((low >> 21) & 0x04) |
((low >> 59) & 0x02) |
((low >> 50) & 0x01)), 5);
r0 = Unquantize(r0, 8, signed);
g0 = Unquantize(g0, 8, signed);
b0 = Unquantize(b0, 8, signed);
r1 = Unquantize(r1 & 0xFF, 8, signed);
g1 = Unquantize(g1 & 0xFF, 8, signed);
b1 = Unquantize(b1 & 0xFF, 8, signed);
r2 = Unquantize(r2 & 0xFF, 8, signed);
g2 = Unquantize(g2 & 0xFF, 8, signed);
b2 = Unquantize(b2 & 0xFF, 8, signed);
r3 = Unquantize(r3 & 0xFF, 8, signed);
g3 = Unquantize(g3 & 0xFF, 8, signed);
b3 = Unquantize(b3 & 0xFF, 8, signed);
subsetCount = 2;
break;
case 22:
r0 = (int)(low >> 5) & 0xFF;
g0 = (int)(low >> 15) & 0xFF;
b0 = (int)(low >> 25) & 0xFF;
if (signed)
{
r0 = SignExtend(r0, 8);
g0 = SignExtend(g0, 8);
b0 = SignExtend(b0, 8);
}
r1 = r0 + SignExtend((int)(low >> 35), 5);
g1 = g0 + SignExtend((int)(low >> 45), 6);
b1 = b0 + SignExtend((int)(low >> 55), 5);
r2 = r0 + SignExtend((int)(high >> 1), 5);
g2 = g0 + SignExtend((int)(((low >> 18) & 0x20) | ((low >> 20) & 0x10) | ((low >> 41) & 0xF)), 6);
b2 = b0 + SignExtend((int)(((low >> 10) & 0x10) | ((high << 3) & 0x08) | (low >> 61)), 5);
r3 = r0 + SignExtend((int)(high >> 7), 5);
g3 = g0 + SignExtend((int)(((low >> 28) & 0x20) | ((low >> 36) & 0x10) | ((low >> 51) & 0x0F)), 6);
b3 = b0 + SignExtend((int)(
((low >> 30) & 0x10) |
((high >> 9) & 0x08) |
((high >> 4) & 0x04) |
((low >> 59) & 0x02) |
((low >> 13) & 0x01)), 5);
r0 = Unquantize(r0, 8, signed);
g0 = Unquantize(g0, 8, signed);
b0 = Unquantize(b0, 8, signed);
r1 = Unquantize(r1 & 0xFF, 8, signed);
g1 = Unquantize(g1 & 0xFF, 8, signed);
b1 = Unquantize(b1 & 0xFF, 8, signed);
r2 = Unquantize(r2 & 0xFF, 8, signed);
g2 = Unquantize(g2 & 0xFF, 8, signed);
b2 = Unquantize(b2 & 0xFF, 8, signed);
r3 = Unquantize(r3 & 0xFF, 8, signed);
g3 = Unquantize(g3 & 0xFF, 8, signed);
b3 = Unquantize(b3 & 0xFF, 8, signed);
subsetCount = 2;
break;
case 26:
r0 = (int)(low >> 5) & 0xFF;
g0 = (int)(low >> 15) & 0xFF;
b0 = (int)(low >> 25) & 0xFF;
if (signed)
{
r0 = SignExtend(r0, 8);
g0 = SignExtend(g0, 8);
b0 = SignExtend(b0, 8);
}
r1 = r0 + SignExtend((int)(low >> 35), 5);
g1 = g0 + SignExtend((int)(low >> 45), 5);
b1 = b0 + SignExtend((int)(low >> 55), 6);
r2 = r0 + SignExtend((int)(high >> 1), 5);
g2 = g0 + SignExtend((int)(((low >> 20) & 0x10) | ((low >> 41) & 0xF)), 5);
b2 = b0 + SignExtend((int)(
((low >> 18) & 0x20) |
((low >> 10) & 0x10) |
((high << 3) & 0x08) |
(low >> 61)), 6);
r3 = r0 + SignExtend((int)(high >> 7), 5);
g3 = g0 + SignExtend((int)(((low >> 36) & 0x10) | ((low >> 51) & 0xF)), 5);
b3 = b0 + SignExtend((int)(
((low >> 28) & 0x20) |
((low >> 30) & 0x10) |
((high >> 9) & 0x08) |
((high >> 4) & 0x04) |
((low >> 12) & 0x02) |
((low >> 50) & 0x01)), 6);
r0 = Unquantize(r0, 8, signed);
g0 = Unquantize(g0, 8, signed);
b0 = Unquantize(b0, 8, signed);
r1 = Unquantize(r1 & 0xFF, 8, signed);
g1 = Unquantize(g1 & 0xFF, 8, signed);
b1 = Unquantize(b1 & 0xFF, 8, signed);
r2 = Unquantize(r2 & 0xFF, 8, signed);
g2 = Unquantize(g2 & 0xFF, 8, signed);
b2 = Unquantize(b2 & 0xFF, 8, signed);
r3 = Unquantize(r3 & 0xFF, 8, signed);
g3 = Unquantize(g3 & 0xFF, 8, signed);
b3 = Unquantize(b3 & 0xFF, 8, signed);
subsetCount = 2;
break;
case 30:
r0 = (int)(low >> 5) & 0x3F;
g0 = (int)(low >> 15) & 0x3F;
b0 = (int)(low >> 25) & 0x3F;
r1 = (int)(low >> 35) & 0x3F;
g1 = (int)(low >> 45) & 0x3F;
b1 = (int)(low >> 55) & 0x3F;
r2 = (int)(high >> 1) & 0x3F;
g2 = (int)(((low >> 16) & 0x20) | ((low >> 20) & 0x10) | ((low >> 41) & 0xF));
b2 = (int)(((low >> 17) & 0x20) | ((low >> 10) & 0x10) | ((high << 3) & 0x08) | (low >> 61));
r3 = (int)(high >> 7) & 0x3F;
g3 = (int)(((low >> 26) & 0x20) | ((low >> 7) & 0x10) | ((low >> 51) & 0xF));
b3 = (int)(
((low >> 28) & 0x20) |
((low >> 30) & 0x10) |
((low >> 29) & 0x08) |
((low >> 21) & 0x04) |
((low >> 12) & 0x03));
if (signed)
{
r0 = SignExtend(r0, 6);
g0 = SignExtend(g0, 6);
b0 = SignExtend(b0, 6);
r1 = SignExtend(r1, 6);
g1 = SignExtend(g1, 6);
b1 = SignExtend(b1, 6);
r2 = SignExtend(r2, 6);
g2 = SignExtend(g2, 6);
b2 = SignExtend(b2, 6);
r3 = SignExtend(r3, 6);
g3 = SignExtend(g3, 6);
b3 = SignExtend(b3, 6);
}
r0 = Unquantize(r0, 6, signed);
g0 = Unquantize(g0, 6, signed);
b0 = Unquantize(b0, 6, signed);
r1 = Unquantize(r1, 6, signed);
g1 = Unquantize(g1, 6, signed);
b1 = Unquantize(b1, 6, signed);
r2 = Unquantize(r2, 6, signed);
g2 = Unquantize(g2, 6, signed);
b2 = Unquantize(b2, 6, signed);
r3 = Unquantize(r3, 6, signed);
g3 = Unquantize(g3, 6, signed);
b3 = Unquantize(b3, 6, signed);
subsetCount = 2;
break;
default:
subsetCount = 0;
break;
}
if (subsetCount > 0)
{
endPoints[0] = new RgbaColor32(r0, g0, b0, HalfOne);
endPoints[1] = new RgbaColor32(r1, g1, b1, HalfOne);
if (subsetCount > 1)
{
endPoints[2] = new RgbaColor32(r2, g2, b2, HalfOne);
endPoints[3] = new RgbaColor32(r3, g3, b3, HalfOne);
}
}
return subsetCount;
}
private static int SignExtend(int value, int bits)
{
int shift = 32 - bits;
return (value << shift) >> shift;
}
private static int Unquantize(int value, int bits, bool signed)
{
if (signed)
{
if (bits >= 16)
{
return value;
}
else
{
bool sign = value < 0;
if (sign)
{
value = -value;
}
if (value == 0)
{
return value;
}
else if (value >= ((1 << (bits - 1)) - 1))
{
value = 0x7FFF;
}
else
{
value = ((value << 15) + 0x4000) >> (bits - 1);
}
if (sign)
{
value = -value;
}
}
}
else
{
if (bits >= 15 || value == 0)
{
return value;
}
else if (value == ((1 << bits) - 1))
{
return 0xFFFF;
}
else
{
return ((value << 16) + 0x8000) >> bits;
}
}
return value;
}
private static ushort FinishUnquantize(int value, bool signed)
{
if (signed)
{
value = value < 0 ? -((-value * 31) >> 5) : (value * 31) >> 5;
int sign = 0;
if (value < 0)
{
sign = 0x8000;
value = -value;
}
return (ushort)(sign | value);
}
else
{
return (ushort)((value * 31) >> 6);
}
}
private static int BitReverse6(int value)
{
value = ((value >> 1) & 0x55) | ((value << 1) & 0xaa);
value = ((value >> 2) & 0x33) | ((value << 2) & 0xcc);
value = ((value >> 4) & 0x0f) | ((value << 4) & 0xf0);
return value >> 2;
}
}
}

View file

@ -0,0 +1,220 @@
using Ryujinx.Graphics.Texture.Utils;
using System.Diagnostics;
using System;
using System.Numerics;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Texture
{
static class BC7Decoder
{
public static void Decode(Span<byte> output, ReadOnlySpan<byte> data, int width, int height)
{
ReadOnlySpan<Block> blocks = MemoryMarshal.Cast<byte, Block>(data);
Span<uint> output32 = MemoryMarshal.Cast<byte, uint>(output);
int wInBlocks = (width + 3) / 4;
int hInBlocks = (height + 3) / 4;
for (int y = 0; y < hInBlocks; y++)
{
int y2 = y * 4;
int bh = Math.Min(4, height - y2);
for (int x = 0; x < wInBlocks; x++)
{
int x2 = x * 4;
int bw = Math.Min(4, width - x2);
DecodeBlock(blocks[y * wInBlocks + x], output32.Slice(y2 * width + x2), bw, bh, width);
}
}
}
private static void DecodeBlock(Block block, Span<uint> output, int w, int h, int width)
{
int mode = BitOperations.TrailingZeroCount((byte)block.Low | 0x100);
if (mode == 8)
{
// Mode is invalid, the spec mandates that hardware fills the block with
// a transparent black color.
for (int ty = 0; ty < h; ty++)
{
int baseOffs = ty * width;
for (int tx = 0; tx < w; tx++)
{
int offs = baseOffs + tx;
output[offs] = 0;
}
}
return;
}
BC7ModeInfo modeInfo = BC67Tables.BC7ModeInfos[mode];
int offset = mode + 1;
int partition = (int)block.Decode(ref offset, modeInfo.PartitionBitCount);
int rotation = (int)block.Decode(ref offset, modeInfo.RotationBitCount);
int indexMode = (int)block.Decode(ref offset, modeInfo.IndexModeBitCount);
Debug.Assert(partition < 64);
Debug.Assert(rotation < 4);
Debug.Assert(indexMode < 2);
int endPointCount = modeInfo.SubsetCount * 2;
Span<RgbaColor32> endPoints = stackalloc RgbaColor32[endPointCount];
Span<byte> pValues = stackalloc byte[modeInfo.PBits];
endPoints.Fill(new RgbaColor32(0, 0, 0, 255));
for (int i = 0; i < endPointCount; i++)
{
endPoints[i].R = (int)block.Decode(ref offset, modeInfo.ColorDepth);
}
for (int i = 0; i < endPointCount; i++)
{
endPoints[i].G = (int)block.Decode(ref offset, modeInfo.ColorDepth);
}
for (int i = 0; i < endPointCount; i++)
{
endPoints[i].B = (int)block.Decode(ref offset, modeInfo.ColorDepth);
}
if (modeInfo.AlphaDepth != 0)
{
for (int i = 0; i < endPointCount; i++)
{
endPoints[i].A = (int)block.Decode(ref offset, modeInfo.AlphaDepth);
}
}
for (int i = 0; i < modeInfo.PBits; i++)
{
pValues[i] = (byte)block.Decode(ref offset, 1);
}
for (int i = 0; i < endPointCount; i++)
{
int pBit = -1;
if (modeInfo.PBits != 0)
{
int pIndex = (i * modeInfo.PBits) / endPointCount;
pBit = pValues[pIndex];
}
Unquantize(ref endPoints[i], modeInfo.ColorDepth, modeInfo.AlphaDepth, pBit);
}
byte[] partitionTable = BC67Tables.PartitionTable[modeInfo.SubsetCount - 1][partition];
byte[] fixUpTable = BC67Tables.FixUpIndices[modeInfo.SubsetCount - 1][partition];
Span<byte> colorIndices = stackalloc byte[16];
for (int i = 0; i < 16; i++)
{
byte subset = partitionTable[i];
int bitCount = i == fixUpTable[subset] ? modeInfo.ColorIndexBitCount - 1 : modeInfo.ColorIndexBitCount;
colorIndices[i] = (byte)block.Decode(ref offset, bitCount);
Debug.Assert(colorIndices[i] < 16);
}
Span<byte> alphaIndices = stackalloc byte[16];
if (modeInfo.AlphaIndexBitCount != 0)
{
for (int i = 0; i < 16; i++)
{
int bitCount = i != 0 ? modeInfo.AlphaIndexBitCount : modeInfo.AlphaIndexBitCount - 1;
alphaIndices[i] = (byte)block.Decode(ref offset, bitCount);
Debug.Assert(alphaIndices[i] < 16);
}
}
for (int ty = 0; ty < h; ty++)
{
int baseOffs = ty * width;
for (int tx = 0; tx < w; tx++)
{
int i = ty * 4 + tx;
RgbaColor32 color;
byte subset = partitionTable[i];
RgbaColor32 color1 = endPoints[subset * 2];
RgbaColor32 color2 = endPoints[subset * 2 + 1];
if (modeInfo.AlphaIndexBitCount != 0)
{
if (indexMode == 0)
{
color = BC67Utils.Interpolate(color1, color2, colorIndices[i], alphaIndices[i], modeInfo.ColorIndexBitCount, modeInfo.AlphaIndexBitCount);
}
else
{
color = BC67Utils.Interpolate(color1, color2, alphaIndices[i], colorIndices[i], modeInfo.AlphaIndexBitCount, modeInfo.ColorIndexBitCount);
}
}
else
{
color = BC67Utils.Interpolate(color1, color2, colorIndices[i], colorIndices[i], modeInfo.ColorIndexBitCount, modeInfo.ColorIndexBitCount);
}
if (rotation != 0)
{
int a = color.A;
switch (rotation)
{
case 1: color.A = color.R; color.R = a; break;
case 2: color.A = color.G; color.G = a; break;
case 3: color.A = color.B; color.B = a; break;
}
}
RgbaColor8 color8 = color.GetColor8();
output[baseOffs + tx] = color8.ToUInt32();
}
}
}
private static void Unquantize(ref RgbaColor32 color, int colorDepth, int alphaDepth, int pBit)
{
color.R = UnquantizeComponent(color.R, colorDepth, pBit);
color.G = UnquantizeComponent(color.G, colorDepth, pBit);
color.B = UnquantizeComponent(color.B, colorDepth, pBit);
color.A = alphaDepth != 0 ? UnquantizeComponent(color.A, alphaDepth, pBit) : 255;
}
private static int UnquantizeComponent(int component, int bits, int pBit)
{
int shift = 8 - bits;
int value = component << shift;
if (pBit >= 0)
{
Debug.Assert(pBit <= 1);
value |= value >> (bits + 1);
value |= pBit << (shift - 1);
}
else
{
value |= value >> bits;
}
return value;
}
}
}

View file

@ -515,6 +515,82 @@ namespace Ryujinx.Graphics.Texture
return output;
}
public static byte[] DecodeBC6(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
{
int size = 0;
for (int l = 0; l < levels; l++)
{
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 8;
}
byte[] output = new byte[size];
int inputOffset = 0;
int outputOffset = 0;
for (int l = 0; l < levels; l++)
{
int w = BitUtils.DivRoundUp(width, BlockWidth);
int h = BitUtils.DivRoundUp(height, BlockHeight);
for (int l2 = 0; l2 < layers; l2++)
{
for (int z = 0; z < depth; z++)
{
BC6Decoder.Decode(output.AsSpan().Slice(outputOffset), data.Slice(inputOffset), width, height, signed);
inputOffset += w * h * 16;
outputOffset += width * height * 8;
}
}
width = Math.Max(1, width >> 1);
height = Math.Max(1, height >> 1);
depth = Math.Max(1, depth >> 1);
}
return output;
}
public static byte[] DecodeBC7(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{
int size = 0;
for (int l = 0; l < levels; l++)
{
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
}
byte[] output = new byte[size];
int inputOffset = 0;
int outputOffset = 0;
for (int l = 0; l < levels; l++)
{
int w = BitUtils.DivRoundUp(width, BlockWidth);
int h = BitUtils.DivRoundUp(height, BlockHeight);
for (int l2 = 0; l2 < layers; l2++)
{
for (int z = 0; z < depth; z++)
{
BC7Decoder.Decode(output.AsSpan().Slice(outputOffset), data.Slice(inputOffset), width, height);
inputOffset += w * h * 16;
outputOffset += width * height * 4;
}
}
width = Math.Max(1, width >> 1);
height = Math.Max(1, height >> 1);
depth = Math.Max(1, depth >> 1);
}
return output;
}
private static ulong InterleaveBytes(uint left, uint right)
{
return InterleaveBytesWithZeros(left) | (InterleaveBytesWithZeros(right) << 8);

View file

@ -1,4 +1,5 @@
using System;
using Ryujinx.Graphics.Texture.Utils;
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -10,53 +11,6 @@ namespace Ryujinx.Graphics.Texture.Encoders
{
static class BC7Encoder
{
private struct ModeInfo
{
public readonly int SubsetCount;
public readonly int PartitionBitCount;
public readonly int PBits;
public readonly int RotationBitCount;
public readonly int IndexModeBitCount;
public readonly int ColorIndexBitCount;
public readonly int AlphaIndexBitCount;
public readonly int ColorDepth;
public readonly int AlphaDepth;
public ModeInfo(
int subsetCount,
int partitionBitsCount,
int pBits,
int rotationBitCount,
int indexModeBitCount,
int colorIndexBitCount,
int alphaIndexBitCount,
int colorDepth,
int alphaDepth)
{
SubsetCount = subsetCount;
PartitionBitCount = partitionBitsCount;
PBits = pBits;
RotationBitCount = rotationBitCount;
IndexModeBitCount = indexModeBitCount;
ColorIndexBitCount = colorIndexBitCount;
AlphaIndexBitCount = alphaIndexBitCount;
ColorDepth = colorDepth;
AlphaDepth = alphaDepth;
}
}
private static readonly ModeInfo[] _modeInfos = new ModeInfo[]
{
new ModeInfo(3, 4, 6, 0, 0, 3, 0, 4, 0),
new ModeInfo(2, 6, 2, 0, 0, 3, 0, 6, 0),
new ModeInfo(3, 6, 0, 0, 0, 2, 0, 5, 0),
new ModeInfo(2, 6, 4, 0, 0, 2, 0, 7, 0),
new ModeInfo(1, 0, 0, 2, 1, 2, 3, 5, 6),
new ModeInfo(1, 0, 0, 2, 0, 2, 2, 7, 8),
new ModeInfo(1, 0, 2, 0, 0, 4, 0, 7, 7),
new ModeInfo(2, 6, 4, 0, 0, 2, 0, 5, 5)
};
public static void Encode(Memory<byte> outputStorage, ReadOnlyMemory<byte> data, int width, int height, EncodeMode mode)
{
int widthInBlocks = (width + 3) / 4;
@ -105,32 +59,6 @@ namespace Ryujinx.Graphics.Texture.Encoders
0, 13, 2, 1, 15, 14, 10, 23
};
private struct Block
{
public ulong Low;
public ulong High;
public void Encode(ulong value, ref int offset, int bits)
{
if (offset >= 64)
{
High |= value << (offset - 64);
}
else
{
Low |= value << offset;
if (offset + bits > 64)
{
int remainder = 64 - offset;
High |= value >> remainder;
}
}
offset += bits;
}
}
private static Block CompressBlock(ReadOnlySpan<byte> data, int x, int y, int width, int height, bool fastMode)
{
int w = Math.Min(4, width - x);
@ -157,10 +85,10 @@ namespace Ryujinx.Graphics.Texture.Encoders
private static Block EncodeFast(ReadOnlySpan<uint> tile, int w, int h)
{
(RgbaColor8 minColor, RgbaColor8 maxColor) = BC7Utils.GetMinMaxColors(tile, w, h);
(RgbaColor8 minColor, RgbaColor8 maxColor) = BC67Utils.GetMinMaxColors(tile, w, h);
bool alphaNotOne = minColor.A != 255 || maxColor.A != 255;
int variance = BC7Utils.SquaredDifference(minColor.GetColor32(), maxColor.GetColor32());
int variance = BC67Utils.SquaredDifference(minColor.GetColor32(), maxColor.GetColor32());
int selectedMode;
int indexMode = 0;
@ -253,13 +181,13 @@ namespace Ryujinx.Graphics.Texture.Encoders
{
for (int im = 0; im < (m == 4 ? 2 : 1); im++)
{
for (int p = 0; p < 1 << _modeInfos[m].PartitionBitCount; p++)
for (int p = 0; p < 1 << BC67Tables.BC7ModeInfos[m].PartitionBitCount; p++)
{
Block block = Encode(m, p, r, im, fastMode: false, tile, w, h, out int maxError);
if (maxError < lowestError || (maxError == lowestError && _modeInfos[m].SubsetCount < lowestErrorSubsets))
if (maxError < lowestError || (maxError == lowestError && BC67Tables.BC7ModeInfos[m].SubsetCount < lowestErrorSubsets))
{
lowestError = maxError;
lowestErrorSubsets = _modeInfos[m].SubsetCount;
lowestErrorSubsets = BC67Tables.BC7ModeInfos[m].SubsetCount;
bestBlock = block;
}
}
@ -281,7 +209,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
int h,
out int errorSum)
{
ModeInfo modeInfo = _modeInfos[mode];
BC7ModeInfo modeInfo = BC67Tables.BC7ModeInfos[mode];
int subsetCount = modeInfo.SubsetCount;
int partitionBitCount = modeInfo.PartitionBitCount;
int rotationBitCount = modeInfo.RotationBitCount;
@ -379,7 +307,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
Span<byte> colorIndices = stackalloc byte[16];
Span<byte> alphaIndices = stackalloc byte[16];
errorSum = BC7Utils.SelectIndices(
errorSum = BC67Utils.SelectIndices(
tile,
w,
h,
@ -398,7 +326,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
if (separateAlphaIndices)
{
errorSum += BC7Utils.SelectIndices(
errorSum += BC67Utils.SelectIndices(
tile,
w,
h,
@ -420,7 +348,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
for (int i = 0; i < 3; i++)
{
colorSwapSubset[i] = colorIndices[BC7Tables.FixUpIndices[subsetCount - 1][partition][i]] >= (colorIndexCount >> 1);
colorSwapSubset[i] = colorIndices[BC67Tables.FixUpIndices[subsetCount - 1][partition][i]] >= (colorIndexCount >> 1);
}
bool alphaSwapSubset = alphaIndices[0] >= (alphaIndexCount >> 1);
@ -462,13 +390,13 @@ namespace Ryujinx.Graphics.Texture.Encoders
if (indexMode == 0 ? colorSwapSubset[subset] : alphaSwapSubset)
{
block.Encode(BC7Utils.QuantizeComponent(color1.GetComponent(rotatedComp), colorDepth, pBit1), ref offset, colorDepth);
block.Encode(BC7Utils.QuantizeComponent(color0.GetComponent(rotatedComp), colorDepth, pBit0), ref offset, colorDepth);
block.Encode(BC67Utils.QuantizeComponent(color1.GetComponent(rotatedComp), colorDepth, pBit1), ref offset, colorDepth);
block.Encode(BC67Utils.QuantizeComponent(color0.GetComponent(rotatedComp), colorDepth, pBit0), ref offset, colorDepth);
}
else
{
block.Encode(BC7Utils.QuantizeComponent(color0.GetComponent(rotatedComp), colorDepth, pBit0), ref offset, colorDepth);
block.Encode(BC7Utils.QuantizeComponent(color1.GetComponent(rotatedComp), colorDepth, pBit1), ref offset, colorDepth);
block.Encode(BC67Utils.QuantizeComponent(color0.GetComponent(rotatedComp), colorDepth, pBit0), ref offset, colorDepth);
block.Encode(BC67Utils.QuantizeComponent(color1.GetComponent(rotatedComp), colorDepth, pBit1), ref offset, colorDepth);
}
}
}
@ -496,13 +424,13 @@ namespace Ryujinx.Graphics.Texture.Encoders
if (separateAlphaIndices && indexMode == 0 ? alphaSwapSubset : colorSwapSubset[subset])
{
block.Encode(BC7Utils.QuantizeComponent(color1.GetComponent(rotatedComp), alphaDepth, pBit1), ref offset, alphaDepth);
block.Encode(BC7Utils.QuantizeComponent(color0.GetComponent(rotatedComp), alphaDepth, pBit0), ref offset, alphaDepth);
block.Encode(BC67Utils.QuantizeComponent(color1.GetComponent(rotatedComp), alphaDepth, pBit1), ref offset, alphaDepth);
block.Encode(BC67Utils.QuantizeComponent(color0.GetComponent(rotatedComp), alphaDepth, pBit0), ref offset, alphaDepth);
}
else
{
block.Encode(BC7Utils.QuantizeComponent(color0.GetComponent(rotatedComp), alphaDepth, pBit0), ref offset, alphaDepth);
block.Encode(BC7Utils.QuantizeComponent(color1.GetComponent(rotatedComp), alphaDepth, pBit1), ref offset, alphaDepth);
block.Encode(BC67Utils.QuantizeComponent(color0.GetComponent(rotatedComp), alphaDepth, pBit0), ref offset, alphaDepth);
block.Encode(BC67Utils.QuantizeComponent(color1.GetComponent(rotatedComp), alphaDepth, pBit1), ref offset, alphaDepth);
}
}
}
@ -512,11 +440,11 @@ namespace Ryujinx.Graphics.Texture.Encoders
block.Encode((ulong)pBitValues[i], ref offset, 1);
}
byte[] fixUpTable = BC7Tables.FixUpIndices[subsetCount - 1][partition];
byte[] fixUpTable = BC67Tables.FixUpIndices[subsetCount - 1][partition];
for (int i = 0; i < 16; i++)
{
int subset = BC7Tables.PartitionTable[subsetCount - 1][partition][i];
int subset = BC67Tables.PartitionTable[subsetCount - 1][partition][i];
byte index = colorIndices[i];
if (colorSwapSubset[subset])
@ -561,12 +489,12 @@ namespace Ryujinx.Graphics.Texture.Encoders
private static unsafe int GetEndPointSelectionErrorFast(ReadOnlySpan<uint> tile, int subsetCount, int partition, int w, int h, int maxError)
{
byte[] partitionTable = BC7Tables.PartitionTable[subsetCount - 1][partition];
byte[] partitionTable = BC67Tables.PartitionTable[subsetCount - 1][partition];
Span<RgbaColor8> minColors = stackalloc RgbaColor8[subsetCount];
Span<RgbaColor8> maxColors = stackalloc RgbaColor8[subsetCount];
BC7Utils.GetMinMaxColors(partitionTable, tile, w, h, minColors, maxColors, subsetCount);
BC67Utils.GetMinMaxColors(partitionTable, tile, w, h, minColors, maxColors, subsetCount);
Span<uint> endPoints0 = stackalloc uint[subsetCount];
Span<uint> endPoints1 = stackalloc uint[subsetCount];
@ -592,8 +520,8 @@ namespace Ryujinx.Graphics.Texture.Encoders
int pBit0 = GetPBit(c0, 6, 0);
int pBit1 = GetPBit(c1, 6, 0);
c0 = BC7Utils.Quantize(RgbaColor8.FromUInt32(c0), 6, 0, pBit0).ToUInt32();
c1 = BC7Utils.Quantize(RgbaColor8.FromUInt32(c1), 6, 0, pBit1).ToUInt32();
c0 = BC67Utils.Quantize(RgbaColor8.FromUInt32(c0), 6, 0, pBit0).ToUInt32();
c1 = BC67Utils.Quantize(RgbaColor8.FromUInt32(c1), 6, 0, pBit1).ToUInt32();
if (Sse41.IsSupported)
{
@ -605,7 +533,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
Vector128<byte> rWeights;
Vector128<byte> lWeights;
fixed (byte* pWeights = BC7Tables.Weights[1], pInvWeights = BC7Tables.InverseWeights[1])
fixed (byte* pWeights = BC67Tables.Weights[1], pInvWeights = BC67Tables.InverseWeights[1])
{
rWeights = Sse2.LoadScalarVector128((ulong*)pWeights).AsByte();
lWeights = Sse2.LoadScalarVector128((ulong*)pInvWeights).AsByte();
@ -670,7 +598,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
for (int i = 1; i < palette.Length - 1; i++)
{
palette[i] = BC7Utils.Interpolate(e032, e132, i, 3);
palette[i] = BC67Utils.Interpolate(e032, e132, i, 3);
}
for (int i = 0; i < tile.Length; i++)
@ -687,7 +615,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
for (int j = 0; j < palette.Length; j++)
{
int score = BC7Utils.SquaredDifference(color, palette[j]);
int score = BC67Utils.SquaredDifference(color, palette[j]);
if (score < bestMatchScore)
{
@ -723,12 +651,12 @@ namespace Ryujinx.Graphics.Texture.Encoders
uint writeMask,
bool fastMode)
{
byte[] partitionTable = BC7Tables.PartitionTable[subsetCount - 1][partition];
byte[] partitionTable = BC67Tables.PartitionTable[subsetCount - 1][partition];
Span<RgbaColor8> minColors = stackalloc RgbaColor8[subsetCount];
Span<RgbaColor8> maxColors = stackalloc RgbaColor8[subsetCount];
BC7Utils.GetMinMaxColors(partitionTable, tile, w, h, minColors, maxColors, subsetCount);
BC67Utils.GetMinMaxColors(partitionTable, tile, w, h, minColors, maxColors, subsetCount);
uint inverseMask = ~writeMask;
@ -934,8 +862,8 @@ namespace Ryujinx.Graphics.Texture.Encoders
return (default, default);
}
minValue = BC7Utils.Quantize(minValue, colorDepth, alphaDepth);
maxValue = BC7Utils.Quantize(maxValue, colorDepth, alphaDepth);
minValue = BC67Utils.Quantize(minValue, colorDepth, alphaDepth);
maxValue = BC67Utils.Quantize(maxValue, colorDepth, alphaDepth);
RgbaColor32 blockDir = maxValue.GetColor32() - minValue.GetColor32();
blockDir = RgbaColor32.DivideGuarded(blockDir << 6, new RgbaColor32(blockDir.R + blockDir.G + blockDir.B + blockDir.A), 0);
@ -946,7 +874,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
for (int i = 0; i < values.Length; i++)
{
RgbaColor8 color = values[i];
int dist = RgbaColor32.Dot(BC7Utils.Quantize(color, colorDepth, alphaDepth).GetColor32(), blockDir);
int dist = RgbaColor32.Dot(BC67Utils.Quantize(color, colorDepth, alphaDepth).GetColor32(), blockDir);
if (minDist >= dist)
{
@ -1019,7 +947,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
int pBit0 = GetPBit(candidateE0.ToUInt32(), colorDepth, alphaDepth);
int pBit1 = GetPBit(candidateE1.ToUInt32(), colorDepth, alphaDepth);
int errorSum = BC7Utils.SelectIndices(
int errorSum = BC67Utils.SelectIndices(
MemoryMarshal.Cast<RgbaColor8, uint>(values),
candidateE0.ToUInt32(),
candidateE1.ToUInt32(),

View file

@ -1,7 +1,19 @@
namespace Ryujinx.Graphics.Texture.Encoders
namespace Ryujinx.Graphics.Texture.Utils
{
static class BC7Tables
static class BC67Tables
{
public static readonly BC7ModeInfo[] BC7ModeInfos = new BC7ModeInfo[]
{
new BC7ModeInfo(3, 4, 6, 0, 0, 3, 0, 4, 0),
new BC7ModeInfo(2, 6, 2, 0, 0, 3, 0, 6, 0),
new BC7ModeInfo(3, 6, 0, 0, 0, 2, 0, 5, 0),
new BC7ModeInfo(2, 6, 4, 0, 0, 2, 0, 7, 0),
new BC7ModeInfo(1, 0, 0, 2, 1, 2, 3, 5, 6),
new BC7ModeInfo(1, 0, 0, 2, 0, 2, 2, 7, 8),
new BC7ModeInfo(1, 0, 2, 0, 0, 4, 0, 7, 7),
new BC7ModeInfo(2, 6, 4, 0, 0, 2, 0, 5, 5)
};
public static readonly byte[][] Weights =
{
new byte[] { 0, 21, 43, 64 },

View file

@ -4,14 +4,14 @@ using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace Ryujinx.Graphics.Texture.Encoders
namespace Ryujinx.Graphics.Texture.Utils
{
static class BC7Utils
static class BC67Utils
{
private static byte[][] _quantizationLut;
private static byte[][] _quantizationLutNoPBit;
static BC7Utils()
static BC67Utils()
{
_quantizationLut = new byte[5][];
_quantizationLutNoPBit = new byte[5][];
@ -322,7 +322,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
Vector128<byte> rWeights;
Vector128<byte> lWeights;
fixed (byte* pWeights = BC7Tables.Weights[0], pInvWeights = BC7Tables.InverseWeights[0])
fixed (byte* pWeights = BC67Tables.Weights[0], pInvWeights = BC67Tables.InverseWeights[0])
{
rWeights = Sse2.LoadScalarVector128((uint*)pWeights).AsByte();
lWeights = Sse2.LoadScalarVector128((uint*)pInvWeights).AsByte();
@ -394,7 +394,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
Vector128<byte> rWeights;
Vector128<byte> lWeights;
fixed (byte* pWeights = BC7Tables.Weights[1], pInvWeights = BC7Tables.InverseWeights[1])
fixed (byte* pWeights = BC67Tables.Weights[1], pInvWeights = BC67Tables.InverseWeights[1])
{
rWeights = Sse2.LoadScalarVector128((ulong*)pWeights).AsByte();
lWeights = Sse2.LoadScalarVector128((ulong*)pInvWeights).AsByte();
@ -476,7 +476,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
Vector128<byte> rWeights;
Vector128<byte> lWeights;
fixed (byte* pWeights = BC7Tables.Weights[2], pInvWeights = BC7Tables.InverseWeights[2])
fixed (byte* pWeights = BC67Tables.Weights[2], pInvWeights = BC67Tables.InverseWeights[2])
{
rWeights = Sse2.LoadVector128(pWeights);
lWeights = Sse2.LoadVector128(pInvWeights);
@ -726,7 +726,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
int pBits,
uint alphaMask)
{
byte[] partitionTable = BC7Tables.PartitionTable[subsetCount - 1][partition];
byte[] partitionTable = BC67Tables.PartitionTable[subsetCount - 1][partition];
uint alphaMaskForPalette = alphaMask;
@ -762,7 +762,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
Vector128<byte> rWeights;
Vector128<byte> lWeights;
fixed (byte* pWeights = BC7Tables.Weights[0], pInvWeights = BC7Tables.InverseWeights[0])
fixed (byte* pWeights = BC67Tables.Weights[0], pInvWeights = BC67Tables.InverseWeights[0])
{
rWeights = Sse2.LoadScalarVector128((uint*)pWeights).AsByte();
lWeights = Sse2.LoadScalarVector128((uint*)pInvWeights).AsByte();
@ -831,7 +831,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
int pBits,
uint alphaMask)
{
byte[] partitionTable = BC7Tables.PartitionTable[subsetCount - 1][partition];
byte[] partitionTable = BC67Tables.PartitionTable[subsetCount - 1][partition];
uint alphaMaskForPalette = alphaMask;
@ -867,7 +867,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
Vector128<byte> rWeights;
Vector128<byte> lWeights;
fixed (byte* pWeights = BC7Tables.Weights[1], pInvWeights = BC7Tables.InverseWeights[1])
fixed (byte* pWeights = BC67Tables.Weights[1], pInvWeights = BC67Tables.InverseWeights[1])
{
rWeights = Sse2.LoadScalarVector128((ulong*)pWeights).AsByte();
lWeights = Sse2.LoadScalarVector128((ulong*)pInvWeights).AsByte();
@ -973,7 +973,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
Vector128<byte> rWeights;
Vector128<byte> lWeights;
fixed (byte* pWeights = BC7Tables.Weights[2], pInvWeights = BC7Tables.InverseWeights[2])
fixed (byte* pWeights = BC67Tables.Weights[2], pInvWeights = BC67Tables.InverseWeights[2])
{
rWeights = Sse2.LoadVector128(pWeights);
lWeights = Sse2.LoadVector128(pInvWeights);
@ -1129,7 +1129,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
{
for (int tx = 0; tx < w; tx++)
{
int subset = BC7Tables.PartitionTable[subsetCount - 1][partition][ty * 4 + tx];
int subset = BC67Tables.PartitionTable[subsetCount - 1][partition][ty * 4 + tx];
uint color = tile[i++] | alphaMask;
int bestMatchScore = int.MaxValue;
@ -1182,6 +1182,28 @@ namespace Ryujinx.Graphics.Texture.Encoders
return (color1 * invWeightV + color2 * weightV + new RgbaColor32(32)) >> 6;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RgbaColor32 Interpolate(
RgbaColor32 color1,
RgbaColor32 color2,
int colorWeightIndex,
int alphaWeightIndex,
int colorIndexBitCount,
int alphaIndexBitCount)
{
Debug.Assert(colorIndexBitCount >= 2 && colorIndexBitCount <= 4);
Debug.Assert(alphaIndexBitCount >= 2 && alphaIndexBitCount <= 4);
int colorWeight = BC67Tables.Weights[colorIndexBitCount - 2][colorWeightIndex];
int alphaWeight = BC67Tables.Weights[alphaIndexBitCount - 2][alphaWeightIndex];
RgbaColor32 weightV = new RgbaColor32(colorWeight);
weightV.A = alphaWeight;
RgbaColor32 invWeightV = new RgbaColor32(64) - weightV;
return (color1 * invWeightV + color2 * weightV + new RgbaColor32(32)) >> 6;
}
public static RgbaColor8 Quantize(RgbaColor8 color, int colorBits, int alphaBits, int pBit = -1)
{
if (alphaBits == 0)

View file

@ -0,0 +1,37 @@
namespace Ryujinx.Graphics.Texture.Utils
{
struct BC7ModeInfo
{
public readonly int SubsetCount;
public readonly int PartitionBitCount;
public readonly int PBits;
public readonly int RotationBitCount;
public readonly int IndexModeBitCount;
public readonly int ColorIndexBitCount;
public readonly int AlphaIndexBitCount;
public readonly int ColorDepth;
public readonly int AlphaDepth;
public BC7ModeInfo(
int subsetCount,
int partitionBitsCount,
int pBits,
int rotationBitCount,
int indexModeBitCount,
int colorIndexBitCount,
int alphaIndexBitCount,
int colorDepth,
int alphaDepth)
{
SubsetCount = subsetCount;
PartitionBitCount = partitionBitsCount;
PBits = pBits;
RotationBitCount = rotationBitCount;
IndexModeBitCount = indexModeBitCount;
ColorIndexBitCount = colorIndexBitCount;
AlphaIndexBitCount = alphaIndexBitCount;
ColorDepth = colorDepth;
AlphaDepth = alphaDepth;
}
}
}

View file

@ -0,0 +1,55 @@
namespace Ryujinx.Graphics.Texture.Utils
{
struct Block
{
public ulong Low;
public ulong High;
public void Encode(ulong value, ref int offset, int bits)
{
if (offset >= 64)
{
High |= value << (offset - 64);
}
else
{
Low |= value << offset;
if (offset + bits > 64)
{
int remainder = 64 - offset;
High |= value >> remainder;
}
}
offset += bits;
}
public ulong Decode(ref int offset, int bits)
{
ulong value;
ulong mask = bits == 64 ? ulong.MaxValue : (1UL << bits) - 1;
if (offset >= 64)
{
value = (High >> (offset - 64)) & mask;
}
else
{
value = Low >> offset;
if (offset + bits > 64)
{
int remainder = 64 - offset;
value |= High << remainder;
}
value &= mask;
}
offset += bits;
return value;
}
}
}

View file

@ -3,16 +3,35 @@ using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace Ryujinx.Graphics.Texture.Encoders
namespace Ryujinx.Graphics.Texture.Utils
{
struct RgbaColor32 : IEquatable<RgbaColor32>
{
private readonly Vector128<int> _color;
private Vector128<int> _color;
public int R => _color.GetElement(0);
public int G => _color.GetElement(1);
public int B => _color.GetElement(2);
public int A => _color.GetElement(3);
public int R
{
get => _color.GetElement(0);
set => _color = _color.WithElement(0, value);
}
public int G
{
get => _color.GetElement(1);
set => _color = _color.WithElement(1, value);
}
public int B
{
get => _color.GetElement(2);
set => _color = _color.WithElement(2, value);
}
public int A
{
get => _color.GetElement(3);
set => _color = _color.WithElement(3, value);
}
public RgbaColor32(Vector128<int> color)
{
@ -193,7 +212,7 @@ namespace Ryujinx.Graphics.Texture.Encoders
return HashCode.Combine(R, G, B, A);
}
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is RgbaColor32 other && Equals(other);
}

View file

@ -3,7 +3,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace Ryujinx.Graphics.Texture.Encoders
namespace Ryujinx.Graphics.Texture.Utils
{
struct RgbaColor8 : IEquatable<RgbaColor8>
{

View file

@ -20,7 +20,20 @@ namespace Ryujinx.Graphics.Vulkan
_table = new FormatFeatureFlags[Enum.GetNames(typeof(GAL.Format)).Length];
}
public bool FormatSupports(GAL.Format format, FormatFeatureFlags flags)
public bool FormatsSupports(FormatFeatureFlags flags, params GAL.Format[] formats)
{
foreach (GAL.Format format in formats)
{
if (!FormatSupports(flags, format))
{
return false;
}
}
return true;
}
public bool FormatSupports(FormatFeatureFlags flags, GAL.Format format)
{
var formatFeatureFlags = _table[(int)format];
@ -56,7 +69,7 @@ namespace Ryujinx.Graphics.Vulkan
requiredFeatures |= FormatFeatureFlags.FormatFeatureStorageImageBit;
}
if (!FormatSupports(srcFormat, requiredFeatures) ||
if (!FormatSupports(requiredFeatures, srcFormat) ||
(srcFormat == GAL.Format.D24UnormS8Uint && VulkanConfiguration.ForceD24S8Unsupported))
{
// The format is not supported. Can we convert it to a higher precision format?

View file

@ -298,8 +298,8 @@ namespace Ryujinx.Graphics.Vulkan
return;
}
else if (_gd.FormatCapabilities.FormatSupports(srcFormat, FormatFeatureFlags.FormatFeatureBlitSrcBit) &&
_gd.FormatCapabilities.FormatSupports(dstFormat, FormatFeatureFlags.FormatFeatureBlitDstBit))
else if (_gd.FormatCapabilities.FormatSupports(FormatFeatureFlags.FormatFeatureBlitSrcBit, srcFormat) &&
_gd.FormatCapabilities.FormatSupports(FormatFeatureFlags.FormatFeatureBlitDstBit, dstFormat))
{
TextureCopy.Blit(
_gd.Api,
@ -604,8 +604,8 @@ namespace Ryujinx.Graphics.Vulkan
private bool SupportsBlitFromD32FS8ToD32FAndS8()
{
var formatFeatureFlags = FormatFeatureFlags.FormatFeatureBlitSrcBit | FormatFeatureFlags.FormatFeatureBlitDstBit;
return _gd.FormatCapabilities.FormatSupports(GAL.Format.D32Float, formatFeatureFlags) &&
_gd.FormatCapabilities.FormatSupports(GAL.Format.S8Uint, formatFeatureFlags);
return _gd.FormatCapabilities.FormatSupports(formatFeatureFlags, GAL.Format.D32Float) &&
_gd.FormatCapabilities.FormatSupports(formatFeatureFlags, GAL.Format.S8Uint);
}
public TextureView GetView(GAL.Format format)

View file

@ -315,6 +315,33 @@ namespace Ryujinx.Graphics.Vulkan
public Capabilities GetCapabilities()
{
FormatFeatureFlags compressedFormatFeatureFlags =
FormatFeatureFlags.FormatFeatureSampledImageBit |
FormatFeatureFlags.FormatFeatureSampledImageFilterLinearBit |
FormatFeatureFlags.FormatFeatureBlitSrcBit |
FormatFeatureFlags.FormatFeatureTransferSrcBit |
FormatFeatureFlags.FormatFeatureTransferDstBit;
bool supportsBc123CompressionFormat = FormatCapabilities.FormatsSupports(compressedFormatFeatureFlags,
GAL.Format.Bc1RgbaSrgb,
GAL.Format.Bc1RgbaUnorm,
GAL.Format.Bc2Srgb,
GAL.Format.Bc2Unorm,
GAL.Format.Bc3Srgb,
GAL.Format.Bc3Unorm);
bool supportsBc45CompressionFormat = FormatCapabilities.FormatsSupports(compressedFormatFeatureFlags,
GAL.Format.Bc4Snorm,
GAL.Format.Bc4Unorm,
GAL.Format.Bc5Snorm,
GAL.Format.Bc5Unorm);
bool supportsBc67CompressionFormat = FormatCapabilities.FormatsSupports(compressedFormatFeatureFlags,
GAL.Format.Bc6HSfloat,
GAL.Format.Bc6HUfloat,
GAL.Format.Bc7Srgb,
GAL.Format.Bc7Unorm);
Api.GetPhysicalDeviceFeatures(_physicalDevice, out var features);
Api.GetPhysicalDeviceProperties(_physicalDevice, out var properties);
@ -326,6 +353,9 @@ namespace Ryujinx.Graphics.Vulkan
hasFrontFacingBug: IsIntelWindows,
hasVectorIndexingBug: Vendor == Vendor.Qualcomm,
supportsAstcCompression: features.TextureCompressionAstcLdr,
supportsBc123Compression: supportsBc123CompressionFormat,
supportsBc45Compression: supportsBc45CompressionFormat,
supportsBc67Compression: supportsBc67CompressionFormat,
supports3DTextureCompression: true,
supportsBgraFormat: true,
supportsR4G4Format: false,