forked from Mirror/Ryujinx
Allow swizzles to match with "undefined" components (#1538)
* Add swizzle matching rules. Improves rules which try to match incompatible formats as perfect, such as D32 float -> R32 float. Remove Format.HasOneComponent, since this information is now available via the FormatInfo struct. * Fix this rule. * Update component counts for depth formats.
This commit is contained in:
parent
5d69d9103e
commit
3d055da5fc
7 changed files with 222 additions and 205 deletions
|
@ -341,38 +341,5 @@ namespace Ryujinx.Graphics.GAL
|
|||
{
|
||||
return format.IsUint() || format.IsSint();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the texture format only has one component.
|
||||
/// </summary>
|
||||
/// <param name="format">Texture format</param>
|
||||
/// <returns>True if the texture format only has one component, false otherwise</returns>
|
||||
public static bool HasOneComponent(this Format format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case Format.R8Unorm:
|
||||
case Format.R8Snorm:
|
||||
case Format.R8Uint:
|
||||
case Format.R8Sint:
|
||||
case Format.R16Float:
|
||||
case Format.R16Unorm:
|
||||
case Format.R16Snorm:
|
||||
case Format.R16Uint:
|
||||
case Format.R16Sint:
|
||||
case Format.R32Float:
|
||||
case Format.R32Uint:
|
||||
case Format.R32Sint:
|
||||
case Format.R8Uscaled:
|
||||
case Format.R8Sscaled:
|
||||
case Format.R16Uscaled:
|
||||
case Format.R16Sscaled:
|
||||
case Format.R32Uscaled:
|
||||
case Format.R32Sscaled:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// <summary>
|
||||
/// A default, generic RGBA8 texture format.
|
||||
/// </summary>
|
||||
public static FormatInfo Default { get; } = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4);
|
||||
public static FormatInfo Default { get; } = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
|
||||
|
||||
/// <summary>
|
||||
/// The format of the texture data.
|
||||
|
@ -38,6 +38,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// </summary>
|
||||
public int BytesPerPixel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of components this format has defined (in RGBA order).
|
||||
/// </summary>
|
||||
public int Components { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whenever or not the texture format is a compressed format. Determined from block size.
|
||||
/// </summary>
|
||||
|
@ -54,12 +59,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
Format format,
|
||||
int blockWidth,
|
||||
int blockHeight,
|
||||
int bytesPerPixel)
|
||||
int bytesPerPixel,
|
||||
int components)
|
||||
{
|
||||
Format = format;
|
||||
BlockWidth = blockWidth;
|
||||
BlockHeight = blockHeight;
|
||||
BytesPerPixel = bytesPerPixel;
|
||||
Components = components;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,102 +10,102 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
{
|
||||
private static Dictionary<uint, FormatInfo> _textureFormats = new Dictionary<uint, FormatInfo>()
|
||||
{
|
||||
{ 0x2491d, new FormatInfo(Format.R8Unorm, 1, 1, 1) },
|
||||
{ 0x1249d, new FormatInfo(Format.R8Snorm, 1, 1, 1) },
|
||||
{ 0x4921d, new FormatInfo(Format.R8Uint, 1, 1, 1) },
|
||||
{ 0x36d9d, new FormatInfo(Format.R8Sint, 1, 1, 1) },
|
||||
{ 0x7ff9b, new FormatInfo(Format.R16Float, 1, 1, 2) },
|
||||
{ 0x2491b, new FormatInfo(Format.R16Unorm, 1, 1, 2) },
|
||||
{ 0x1249b, new FormatInfo(Format.R16Snorm, 1, 1, 2) },
|
||||
{ 0x4921b, new FormatInfo(Format.R16Uint, 1, 1, 2) },
|
||||
{ 0x36d9b, new FormatInfo(Format.R16Sint, 1, 1, 2) },
|
||||
{ 0x7ff8f, new FormatInfo(Format.R32Float, 1, 1, 4) },
|
||||
{ 0x4920f, new FormatInfo(Format.R32Uint, 1, 1, 4) },
|
||||
{ 0x36d8f, new FormatInfo(Format.R32Sint, 1, 1, 4) },
|
||||
{ 0x24918, new FormatInfo(Format.R8G8Unorm, 1, 1, 2) },
|
||||
{ 0x12498, new FormatInfo(Format.R8G8Snorm, 1, 1, 2) },
|
||||
{ 0x49218, new FormatInfo(Format.R8G8Uint, 1, 1, 2) },
|
||||
{ 0x36d98, new FormatInfo(Format.R8G8Sint, 1, 1, 2) },
|
||||
{ 0x7ff8c, new FormatInfo(Format.R16G16Float, 1, 1, 4) },
|
||||
{ 0x2490c, new FormatInfo(Format.R16G16Unorm, 1, 1, 4) },
|
||||
{ 0x1248c, new FormatInfo(Format.R16G16Snorm, 1, 1, 4) },
|
||||
{ 0x4920c, new FormatInfo(Format.R16G16Uint, 1, 1, 4) },
|
||||
{ 0x36d8c, new FormatInfo(Format.R16G16Sint, 1, 1, 4) },
|
||||
{ 0x7ff84, new FormatInfo(Format.R32G32Float, 1, 1, 8) },
|
||||
{ 0x49204, new FormatInfo(Format.R32G32Uint, 1, 1, 8) },
|
||||
{ 0x36d84, new FormatInfo(Format.R32G32Sint, 1, 1, 8) },
|
||||
{ 0x7ff82, new FormatInfo(Format.R32G32B32Float, 1, 1, 12) },
|
||||
{ 0x49202, new FormatInfo(Format.R32G32B32Uint, 1, 1, 12) },
|
||||
{ 0x36d82, new FormatInfo(Format.R32G32B32Sint, 1, 1, 12) },
|
||||
{ 0x24908, new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4) },
|
||||
{ 0x12488, new FormatInfo(Format.R8G8B8A8Snorm, 1, 1, 4) },
|
||||
{ 0x49208, new FormatInfo(Format.R8G8B8A8Uint, 1, 1, 4) },
|
||||
{ 0x36d88, new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4) },
|
||||
{ 0x7ff83, new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8) },
|
||||
{ 0x24903, new FormatInfo(Format.R16G16B16A16Unorm, 1, 1, 8) },
|
||||
{ 0x12483, new FormatInfo(Format.R16G16B16A16Snorm, 1, 1, 8) },
|
||||
{ 0x49203, new FormatInfo(Format.R16G16B16A16Uint, 1, 1, 8) },
|
||||
{ 0x36d83, new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8) },
|
||||
{ 0x7ff81, new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16) },
|
||||
{ 0x49201, new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16) },
|
||||
{ 0x36d81, new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16) },
|
||||
{ 0x2493a, new FormatInfo(Format.D16Unorm, 1, 1, 2) },
|
||||
{ 0x7ffaf, new FormatInfo(Format.D32Float, 1, 1, 4) },
|
||||
{ 0x24a0e, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4) },
|
||||
{ 0x24a29, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4) },
|
||||
{ 0x25385, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8) },
|
||||
{ 0x253b0, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8) },
|
||||
{ 0xa4908, new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4) },
|
||||
{ 0x24912, new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2) },
|
||||
{ 0x24914, new FormatInfo(Format.R5G5B5A1Unorm, 1, 1, 2) },
|
||||
{ 0x24915, new FormatInfo(Format.R5G6B5Unorm, 1, 1, 2) },
|
||||
{ 0x24909, new FormatInfo(Format.R10G10B10A2Unorm, 1, 1, 4) },
|
||||
{ 0x49209, new FormatInfo(Format.R10G10B10A2Uint, 1, 1, 4) },
|
||||
{ 0x7ffa1, new FormatInfo(Format.R11G11B10Float, 1, 1, 4) },
|
||||
{ 0x7ffa0, new FormatInfo(Format.R9G9B9E5Float, 1, 1, 4) },
|
||||
{ 0x24924, new FormatInfo(Format.Bc1RgbaUnorm, 4, 4, 8) },
|
||||
{ 0x24925, new FormatInfo(Format.Bc2Unorm, 4, 4, 16) },
|
||||
{ 0x24926, new FormatInfo(Format.Bc3Unorm, 4, 4, 16) },
|
||||
{ 0xa4924, new FormatInfo(Format.Bc1RgbaSrgb, 4, 4, 8) },
|
||||
{ 0xa4925, new FormatInfo(Format.Bc2Srgb, 4, 4, 16) },
|
||||
{ 0xa4926, new FormatInfo(Format.Bc3Srgb, 4, 4, 16) },
|
||||
{ 0x24927, new FormatInfo(Format.Bc4Unorm, 4, 4, 8) },
|
||||
{ 0x124a7, new FormatInfo(Format.Bc4Snorm, 4, 4, 8) },
|
||||
{ 0x24928, new FormatInfo(Format.Bc5Unorm, 4, 4, 16) },
|
||||
{ 0x124a8, new FormatInfo(Format.Bc5Snorm, 4, 4, 16) },
|
||||
{ 0x24917, new FormatInfo(Format.Bc7Unorm, 4, 4, 16) },
|
||||
{ 0xa4917, new FormatInfo(Format.Bc7Srgb, 4, 4, 16) },
|
||||
{ 0x7ff90, new FormatInfo(Format.Bc6HSfloat, 4, 4, 16) },
|
||||
{ 0x7ff91, new FormatInfo(Format.Bc6HUfloat, 4, 4, 16) },
|
||||
{ 0x24940, new FormatInfo(Format.Astc4x4Unorm, 4, 4, 16) },
|
||||
{ 0x24950, new FormatInfo(Format.Astc5x4Unorm, 5, 4, 16) },
|
||||
{ 0x24941, new FormatInfo(Format.Astc5x5Unorm, 5, 5, 16) },
|
||||
{ 0x24951, new FormatInfo(Format.Astc6x5Unorm, 6, 5, 16) },
|
||||
{ 0x24942, new FormatInfo(Format.Astc6x6Unorm, 6, 6, 16) },
|
||||
{ 0x24955, new FormatInfo(Format.Astc8x5Unorm, 8, 5, 16) },
|
||||
{ 0x24952, new FormatInfo(Format.Astc8x6Unorm, 8, 6, 16) },
|
||||
{ 0x24944, new FormatInfo(Format.Astc8x8Unorm, 8, 8, 16) },
|
||||
{ 0x24956, new FormatInfo(Format.Astc10x5Unorm, 10, 5, 16) },
|
||||
{ 0x24957, new FormatInfo(Format.Astc10x6Unorm, 10, 6, 16) },
|
||||
{ 0x24953, new FormatInfo(Format.Astc10x8Unorm, 10, 8, 16) },
|
||||
{ 0x24945, new FormatInfo(Format.Astc10x10Unorm, 10, 10, 16) },
|
||||
{ 0x24954, new FormatInfo(Format.Astc12x10Unorm, 12, 10, 16) },
|
||||
{ 0x24946, new FormatInfo(Format.Astc12x12Unorm, 12, 12, 16) },
|
||||
{ 0xa4940, new FormatInfo(Format.Astc4x4Srgb, 4, 4, 16) },
|
||||
{ 0xa4950, new FormatInfo(Format.Astc5x4Srgb, 5, 4, 16) },
|
||||
{ 0xa4941, new FormatInfo(Format.Astc5x5Srgb, 5, 5, 16) },
|
||||
{ 0xa4951, new FormatInfo(Format.Astc6x5Srgb, 6, 5, 16) },
|
||||
{ 0xa4942, new FormatInfo(Format.Astc6x6Srgb, 6, 6, 16) },
|
||||
{ 0xa4955, new FormatInfo(Format.Astc8x5Srgb, 8, 5, 16) },
|
||||
{ 0xa4952, new FormatInfo(Format.Astc8x6Srgb, 8, 6, 16) },
|
||||
{ 0xa4944, new FormatInfo(Format.Astc8x8Srgb, 8, 8, 16) },
|
||||
{ 0xa4956, new FormatInfo(Format.Astc10x5Srgb, 10, 5, 16) },
|
||||
{ 0xa4957, new FormatInfo(Format.Astc10x6Srgb, 10, 6, 16) },
|
||||
{ 0xa4953, new FormatInfo(Format.Astc10x8Srgb, 10, 8, 16) },
|
||||
{ 0xa4945, new FormatInfo(Format.Astc10x10Srgb, 10, 10, 16) },
|
||||
{ 0xa4954, new FormatInfo(Format.Astc12x10Srgb, 12, 10, 16) },
|
||||
{ 0xa4946, new FormatInfo(Format.Astc12x12Srgb, 12, 12, 16) },
|
||||
{ 0x24913, new FormatInfo(Format.A1B5G5R5Unorm, 1, 1, 2) }
|
||||
{ 0x2491d, new FormatInfo(Format.R8Unorm, 1, 1, 1, 1) },
|
||||
{ 0x1249d, new FormatInfo(Format.R8Snorm, 1, 1, 1, 1) },
|
||||
{ 0x4921d, new FormatInfo(Format.R8Uint, 1, 1, 1, 1) },
|
||||
{ 0x36d9d, new FormatInfo(Format.R8Sint, 1, 1, 1, 1) },
|
||||
{ 0x7ff9b, new FormatInfo(Format.R16Float, 1, 1, 2, 1) },
|
||||
{ 0x2491b, new FormatInfo(Format.R16Unorm, 1, 1, 2, 1) },
|
||||
{ 0x1249b, new FormatInfo(Format.R16Snorm, 1, 1, 2, 1) },
|
||||
{ 0x4921b, new FormatInfo(Format.R16Uint, 1, 1, 2, 1) },
|
||||
{ 0x36d9b, new FormatInfo(Format.R16Sint, 1, 1, 2, 1) },
|
||||
{ 0x7ff8f, new FormatInfo(Format.R32Float, 1, 1, 4, 1) },
|
||||
{ 0x4920f, new FormatInfo(Format.R32Uint, 1, 1, 4, 1) },
|
||||
{ 0x36d8f, new FormatInfo(Format.R32Sint, 1, 1, 4, 1) },
|
||||
{ 0x24918, new FormatInfo(Format.R8G8Unorm, 1, 1, 2, 2) },
|
||||
{ 0x12498, new FormatInfo(Format.R8G8Snorm, 1, 1, 2, 2) },
|
||||
{ 0x49218, new FormatInfo(Format.R8G8Uint, 1, 1, 2, 2) },
|
||||
{ 0x36d98, new FormatInfo(Format.R8G8Sint, 1, 1, 2, 2) },
|
||||
{ 0x7ff8c, new FormatInfo(Format.R16G16Float, 1, 1, 4, 2) },
|
||||
{ 0x2490c, new FormatInfo(Format.R16G16Unorm, 1, 1, 4, 2) },
|
||||
{ 0x1248c, new FormatInfo(Format.R16G16Snorm, 1, 1, 4, 2) },
|
||||
{ 0x4920c, new FormatInfo(Format.R16G16Uint, 1, 1, 4, 2) },
|
||||
{ 0x36d8c, new FormatInfo(Format.R16G16Sint, 1, 1, 4, 2) },
|
||||
{ 0x7ff84, new FormatInfo(Format.R32G32Float, 1, 1, 8, 2) },
|
||||
{ 0x49204, new FormatInfo(Format.R32G32Uint, 1, 1, 8, 2) },
|
||||
{ 0x36d84, new FormatInfo(Format.R32G32Sint, 1, 1, 8, 2) },
|
||||
{ 0x7ff82, new FormatInfo(Format.R32G32B32Float, 1, 1, 12, 3) },
|
||||
{ 0x49202, new FormatInfo(Format.R32G32B32Uint, 1, 1, 12, 3) },
|
||||
{ 0x36d82, new FormatInfo(Format.R32G32B32Sint, 1, 1, 12, 3) },
|
||||
{ 0x24908, new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4) },
|
||||
{ 0x12488, new FormatInfo(Format.R8G8B8A8Snorm, 1, 1, 4, 4) },
|
||||
{ 0x49208, new FormatInfo(Format.R8G8B8A8Uint, 1, 1, 4, 4) },
|
||||
{ 0x36d88, new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4, 4) },
|
||||
{ 0x7ff83, new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8, 4) },
|
||||
{ 0x24903, new FormatInfo(Format.R16G16B16A16Unorm, 1, 1, 8, 4) },
|
||||
{ 0x12483, new FormatInfo(Format.R16G16B16A16Snorm, 1, 1, 8, 4) },
|
||||
{ 0x49203, new FormatInfo(Format.R16G16B16A16Uint, 1, 1, 8, 4) },
|
||||
{ 0x36d83, new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8, 4) },
|
||||
{ 0x7ff81, new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16, 4) },
|
||||
{ 0x49201, new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16, 4) },
|
||||
{ 0x36d81, new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16, 4) },
|
||||
{ 0x2493a, new FormatInfo(Format.D16Unorm, 1, 1, 2, 1) },
|
||||
{ 0x7ffaf, new FormatInfo(Format.D32Float, 1, 1, 4, 1) },
|
||||
{ 0x24a0e, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
|
||||
{ 0x24a29, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
|
||||
{ 0x25385, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
|
||||
{ 0x253b0, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
|
||||
{ 0xa4908, new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4) },
|
||||
{ 0x24912, new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4) },
|
||||
{ 0x24914, new FormatInfo(Format.R5G5B5A1Unorm, 1, 1, 2, 4) },
|
||||
{ 0x24915, new FormatInfo(Format.R5G6B5Unorm, 1, 1, 2, 3) },
|
||||
{ 0x24909, new FormatInfo(Format.R10G10B10A2Unorm, 1, 1, 4, 4) },
|
||||
{ 0x49209, new FormatInfo(Format.R10G10B10A2Uint, 1, 1, 4, 4) },
|
||||
{ 0x7ffa1, new FormatInfo(Format.R11G11B10Float, 1, 1, 4, 3) },
|
||||
{ 0x7ffa0, new FormatInfo(Format.R9G9B9E5Float, 1, 1, 4, 4) },
|
||||
{ 0x24924, new FormatInfo(Format.Bc1RgbaUnorm, 4, 4, 8, 4) },
|
||||
{ 0x24925, new FormatInfo(Format.Bc2Unorm, 4, 4, 16, 4) },
|
||||
{ 0x24926, new FormatInfo(Format.Bc3Unorm, 4, 4, 16, 4) },
|
||||
{ 0xa4924, new FormatInfo(Format.Bc1RgbaSrgb, 4, 4, 8, 4) },
|
||||
{ 0xa4925, new FormatInfo(Format.Bc2Srgb, 4, 4, 16, 4) },
|
||||
{ 0xa4926, new FormatInfo(Format.Bc3Srgb, 4, 4, 16, 4) },
|
||||
{ 0x24927, new FormatInfo(Format.Bc4Unorm, 4, 4, 8, 1) },
|
||||
{ 0x124a7, new FormatInfo(Format.Bc4Snorm, 4, 4, 8, 1) },
|
||||
{ 0x24928, new FormatInfo(Format.Bc5Unorm, 4, 4, 16, 2) },
|
||||
{ 0x124a8, new FormatInfo(Format.Bc5Snorm, 4, 4, 16, 2) },
|
||||
{ 0x24917, new FormatInfo(Format.Bc7Unorm, 4, 4, 16, 4) },
|
||||
{ 0xa4917, new FormatInfo(Format.Bc7Srgb, 4, 4, 16, 4) },
|
||||
{ 0x7ff90, new FormatInfo(Format.Bc6HSfloat, 4, 4, 16, 4) },
|
||||
{ 0x7ff91, new FormatInfo(Format.Bc6HUfloat, 4, 4, 16, 4) },
|
||||
{ 0x24940, new FormatInfo(Format.Astc4x4Unorm, 4, 4, 16, 4) },
|
||||
{ 0x24950, new FormatInfo(Format.Astc5x4Unorm, 5, 4, 16, 4) },
|
||||
{ 0x24941, new FormatInfo(Format.Astc5x5Unorm, 5, 5, 16, 4) },
|
||||
{ 0x24951, new FormatInfo(Format.Astc6x5Unorm, 6, 5, 16, 4) },
|
||||
{ 0x24942, new FormatInfo(Format.Astc6x6Unorm, 6, 6, 16, 4) },
|
||||
{ 0x24955, new FormatInfo(Format.Astc8x5Unorm, 8, 5, 16, 4) },
|
||||
{ 0x24952, new FormatInfo(Format.Astc8x6Unorm, 8, 6, 16, 4) },
|
||||
{ 0x24944, new FormatInfo(Format.Astc8x8Unorm, 8, 8, 16, 4) },
|
||||
{ 0x24956, new FormatInfo(Format.Astc10x5Unorm, 10, 5, 16, 4) },
|
||||
{ 0x24957, new FormatInfo(Format.Astc10x6Unorm, 10, 6, 16, 4) },
|
||||
{ 0x24953, new FormatInfo(Format.Astc10x8Unorm, 10, 8, 16, 4) },
|
||||
{ 0x24945, new FormatInfo(Format.Astc10x10Unorm, 10, 10, 16, 4) },
|
||||
{ 0x24954, new FormatInfo(Format.Astc12x10Unorm, 12, 10, 16, 4) },
|
||||
{ 0x24946, new FormatInfo(Format.Astc12x12Unorm, 12, 12, 16, 4) },
|
||||
{ 0xa4940, new FormatInfo(Format.Astc4x4Srgb, 4, 4, 16, 4) },
|
||||
{ 0xa4950, new FormatInfo(Format.Astc5x4Srgb, 5, 4, 16, 4) },
|
||||
{ 0xa4941, new FormatInfo(Format.Astc5x5Srgb, 5, 5, 16, 4) },
|
||||
{ 0xa4951, new FormatInfo(Format.Astc6x5Srgb, 6, 5, 16, 4) },
|
||||
{ 0xa4942, new FormatInfo(Format.Astc6x6Srgb, 6, 6, 16, 4) },
|
||||
{ 0xa4955, new FormatInfo(Format.Astc8x5Srgb, 8, 5, 16, 4) },
|
||||
{ 0xa4952, new FormatInfo(Format.Astc8x6Srgb, 8, 6, 16, 4) },
|
||||
{ 0xa4944, new FormatInfo(Format.Astc8x8Srgb, 8, 8, 16, 4) },
|
||||
{ 0xa4956, new FormatInfo(Format.Astc10x5Srgb, 10, 5, 16, 4) },
|
||||
{ 0xa4957, new FormatInfo(Format.Astc10x6Srgb, 10, 6, 16, 4) },
|
||||
{ 0xa4953, new FormatInfo(Format.Astc10x8Srgb, 10, 8, 16, 4) },
|
||||
{ 0xa4945, new FormatInfo(Format.Astc10x10Srgb, 10, 10, 16, 4) },
|
||||
{ 0xa4954, new FormatInfo(Format.Astc12x10Srgb, 12, 10, 16, 4) },
|
||||
{ 0xa4946, new FormatInfo(Format.Astc12x12Srgb, 12, 12, 16, 4) },
|
||||
{ 0x24913, new FormatInfo(Format.A1B5G5R5Unorm, 1, 1, 2, 4) }
|
||||
};
|
||||
|
||||
private static Dictionary<ulong, Format> _attribFormats = new Dictionary<ulong, Format>()
|
||||
|
|
|
@ -397,6 +397,49 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
return result ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a swizzle component in two textures functionally match, taking into account if the components are defined.
|
||||
/// </summary>
|
||||
/// <param name="lhs">Texture information to compare</param>
|
||||
/// <param name="rhs">Texture information to compare with</param>
|
||||
/// <param name="swizzleLhs">Swizzle component for the first texture</param>
|
||||
/// <param name="swizzleRhs">Swizzle component for the second texture</param>
|
||||
/// <param name="component">Component index, starting at 0 for red</param>
|
||||
/// <returns>True if the swizzle components functionally match, false othersize</returns>
|
||||
private static bool SwizzleComponentMatches(TextureInfo lhs, TextureInfo rhs, SwizzleComponent swizzleLhs, SwizzleComponent swizzleRhs, int component)
|
||||
{
|
||||
int lhsComponents = lhs.FormatInfo.Components;
|
||||
int rhsComponents = rhs.FormatInfo.Components;
|
||||
|
||||
if (lhsComponents == 4 && rhsComponents == 4)
|
||||
{
|
||||
return swizzleLhs == swizzleRhs;
|
||||
}
|
||||
|
||||
// Swizzles after the number of components a format defines are "undefined".
|
||||
// We allow these to not be equal under certain circumstances.
|
||||
// This can only happen when there are less than 4 components in a format.
|
||||
// It tends to happen when float depth textures are sampled.
|
||||
|
||||
bool lhsDefined = (swizzleLhs - SwizzleComponent.Red) < lhsComponents;
|
||||
bool rhsDefined = (swizzleRhs - SwizzleComponent.Red) < rhsComponents;
|
||||
|
||||
if (lhsDefined == rhsDefined)
|
||||
{
|
||||
// If both are undefined, return true. Otherwise just check if they're equal.
|
||||
return lhsDefined ? swizzleLhs == swizzleRhs : true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SwizzleComponent defined = lhsDefined ? swizzleLhs : swizzleRhs;
|
||||
SwizzleComponent undefined = lhsDefined ? swizzleRhs : swizzleLhs;
|
||||
|
||||
// Undefined swizzle can be matched by a forced value (0, 1), exact equality, or expected value.
|
||||
// For example, R___ matches R001, RGBA but not RBGA.
|
||||
return defined == undefined || defined < SwizzleComponent.Red || defined == SwizzleComponent.Red + component;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the texture shader sampling parameters of two texture informations match.
|
||||
/// </summary>
|
||||
|
@ -406,10 +449,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
public static bool SamplerParamsMatches(TextureInfo lhs, TextureInfo rhs)
|
||||
{
|
||||
return lhs.DepthStencilMode == rhs.DepthStencilMode &&
|
||||
lhs.SwizzleR == rhs.SwizzleR &&
|
||||
lhs.SwizzleG == rhs.SwizzleG &&
|
||||
lhs.SwizzleB == rhs.SwizzleB &&
|
||||
lhs.SwizzleA == rhs.SwizzleA;
|
||||
SwizzleComponentMatches(lhs, rhs, lhs.SwizzleR, rhs.SwizzleR, 0) &&
|
||||
SwizzleComponentMatches(lhs, rhs, lhs.SwizzleG, rhs.SwizzleG, 1) &&
|
||||
SwizzleComponentMatches(lhs, rhs, lhs.SwizzleB, rhs.SwizzleB, 2) &&
|
||||
SwizzleComponentMatches(lhs, rhs, lhs.SwizzleA, rhs.SwizzleA, 3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -398,7 +398,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
// While upscaling works for all targets defined by IsUpscaleCompatible, we additionally blacklist targets here that
|
||||
// may have undesirable results (upscaling blur textures) or simply waste GPU resources (upscaling texture atlas).
|
||||
|
||||
if (!(info.FormatInfo.Format.IsDepthOrStencil() || info.FormatInfo.Format.HasOneComponent()))
|
||||
if (!(info.FormatInfo.Format.IsDepthOrStencil() || info.FormatInfo.Components == 1))
|
||||
{
|
||||
// Discount square textures that aren't depth-stencil like. (excludes game textures, cubemap faces, most 3D texture LUT, texture atlas)
|
||||
// Detect if the texture is possibly square. Widths may be aligned, so to remove the uncertainty we align both the width and height.
|
||||
|
@ -1037,11 +1037,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
{
|
||||
if (formatInfo.Format.IsAstcUnorm())
|
||||
{
|
||||
formatInfo = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4);
|
||||
formatInfo = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
|
||||
}
|
||||
else if (formatInfo.Format.IsAstcSrgb())
|
||||
{
|
||||
formatInfo = new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4);
|
||||
formatInfo = new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1052,12 +1052,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
// The shader will need the appropriate conversion code to compensate.
|
||||
switch (formatInfo.Format)
|
||||
{
|
||||
case Format.R8Snorm: formatInfo = new FormatInfo(Format.R8Sint, 1, 1, 1); break;
|
||||
case Format.R16Snorm: formatInfo = new FormatInfo(Format.R16Sint, 1, 1, 2); break;
|
||||
case Format.R8G8Snorm: formatInfo = new FormatInfo(Format.R8G8Sint, 1, 1, 2); break;
|
||||
case Format.R16G16Snorm: formatInfo = new FormatInfo(Format.R16G16Sint, 1, 1, 4); break;
|
||||
case Format.R8G8B8A8Snorm: formatInfo = new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4); break;
|
||||
case Format.R16G16B16A16Snorm: formatInfo = new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8); break;
|
||||
case Format.R8Snorm: formatInfo = new FormatInfo(Format.R8Sint, 1, 1, 1, 1); break;
|
||||
case Format.R16Snorm: formatInfo = new FormatInfo(Format.R16Sint, 1, 1, 2, 1); break;
|
||||
case Format.R8G8Snorm: formatInfo = new FormatInfo(Format.R8G8Sint, 1, 1, 2, 2); break;
|
||||
case Format.R16G16Snorm: formatInfo = new FormatInfo(Format.R16G16Sint, 1, 1, 4, 2); break;
|
||||
case Format.R8G8B8A8Snorm: formatInfo = new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4, 4); break;
|
||||
case Format.R16G16B16A16Snorm: formatInfo = new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8, 4); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -81,66 +81,66 @@ namespace Ryujinx.Graphics.Gpu.State
|
|||
{
|
||||
return format switch
|
||||
{
|
||||
RtFormat.D32Float => new FormatInfo(Format.D32Float, 1, 1, 4),
|
||||
RtFormat.D16Unorm => new FormatInfo(Format.D16Unorm, 1, 1, 2),
|
||||
RtFormat.D24UnormS8Uint => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4),
|
||||
RtFormat.D24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4),
|
||||
RtFormat.S8UintD24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4),
|
||||
RtFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1),
|
||||
RtFormat.D32FloatS8Uint => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8),
|
||||
RtFormat.R32G32B32A32Float => new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16),
|
||||
RtFormat.R32G32B32A32Sint => new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16),
|
||||
RtFormat.R32G32B32A32Uint => new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16),
|
||||
RtFormat.R32G32B32X32Float => new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16),
|
||||
RtFormat.R32G32B32X32Sint => new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16),
|
||||
RtFormat.R32G32B32X32Uint => new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16),
|
||||
RtFormat.R16G16B16X16Unorm => new FormatInfo(Format.R16G16B16A16Unorm, 1, 1, 8),
|
||||
RtFormat.R16G16B16X16Snorm => new FormatInfo(Format.R16G16B16A16Snorm, 1, 1, 8),
|
||||
RtFormat.R16G16B16X16Sint => new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8),
|
||||
RtFormat.R16G16B16X16Uint => new FormatInfo(Format.R16G16B16A16Uint, 1, 1, 8),
|
||||
RtFormat.R16G16B16A16Float => new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8),
|
||||
RtFormat.R32G32Float => new FormatInfo(Format.R32G32Float, 1, 1, 8),
|
||||
RtFormat.R32G32Sint => new FormatInfo(Format.R32G32Sint, 1, 1, 8),
|
||||
RtFormat.R32G32Uint => new FormatInfo(Format.R32G32Uint, 1, 1, 8),
|
||||
RtFormat.R16G16B16X16Float => new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8),
|
||||
RtFormat.B8G8R8A8Unorm => new FormatInfo(Format.B8G8R8A8Unorm, 1, 1, 4),
|
||||
RtFormat.B8G8R8A8Srgb => new FormatInfo(Format.B8G8R8A8Srgb, 1, 1, 4),
|
||||
RtFormat.R10G10B10A2Unorm => new FormatInfo(Format.R10G10B10A2Unorm, 1, 1, 4),
|
||||
RtFormat.R10G10B10A2Uint => new FormatInfo(Format.R10G10B10A2Uint, 1, 1, 4),
|
||||
RtFormat.R8G8B8A8Unorm => new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4),
|
||||
RtFormat.R8G8B8A8Srgb => new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4),
|
||||
RtFormat.R8G8B8X8Snorm => new FormatInfo(Format.R8G8B8A8Snorm, 1, 1, 4),
|
||||
RtFormat.R8G8B8X8Sint => new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4),
|
||||
RtFormat.R8G8B8X8Uint => new FormatInfo(Format.R8G8B8A8Uint, 1, 1, 4),
|
||||
RtFormat.R16G16Unorm => new FormatInfo(Format.R16G16Unorm, 1, 1, 4),
|
||||
RtFormat.R16G16Snorm => new FormatInfo(Format.R16G16Snorm, 1, 1, 4),
|
||||
RtFormat.R16G16Sint => new FormatInfo(Format.R16G16Sint, 1, 1, 4),
|
||||
RtFormat.R16G16Uint => new FormatInfo(Format.R16G16Uint, 1, 1, 4),
|
||||
RtFormat.R16G16Float => new FormatInfo(Format.R16G16Float, 1, 1, 4),
|
||||
RtFormat.R11G11B10Float => new FormatInfo(Format.R11G11B10Float, 1, 1, 4),
|
||||
RtFormat.R32Sint => new FormatInfo(Format.R32Sint, 1, 1, 4),
|
||||
RtFormat.R32Uint => new FormatInfo(Format.R32Uint, 1, 1, 4),
|
||||
RtFormat.R32Float => new FormatInfo(Format.R32Float, 1, 1, 4),
|
||||
RtFormat.B8G8R8X8Unorm => new FormatInfo(Format.B8G8R8A8Unorm, 1, 1, 4),
|
||||
RtFormat.B8G8R8X8Srgb => new FormatInfo(Format.B8G8R8A8Srgb, 1, 1, 4),
|
||||
RtFormat.B5G6R5Unorm => new FormatInfo(Format.B5G6R5Unorm, 1, 1, 2),
|
||||
RtFormat.B5G5R5A1Unorm => new FormatInfo(Format.B5G5R5A1Unorm, 1, 1, 2),
|
||||
RtFormat.R8G8Unorm => new FormatInfo(Format.R8G8Unorm, 1, 1, 2),
|
||||
RtFormat.R8G8Snorm => new FormatInfo(Format.R8G8Snorm, 1, 1, 2),
|
||||
RtFormat.R8G8Sint => new FormatInfo(Format.R8G8Sint, 1, 1, 2),
|
||||
RtFormat.R8G8Uint => new FormatInfo(Format.R8G8Uint, 1, 1, 2),
|
||||
RtFormat.R16Unorm => new FormatInfo(Format.R16Unorm, 1, 1, 2),
|
||||
RtFormat.R16Snorm => new FormatInfo(Format.R16Snorm, 1, 1, 2),
|
||||
RtFormat.R16Sint => new FormatInfo(Format.R16Sint, 1, 1, 2),
|
||||
RtFormat.R16Uint => new FormatInfo(Format.R16Uint, 1, 1, 2),
|
||||
RtFormat.R16Float => new FormatInfo(Format.R16Float, 1, 1, 2),
|
||||
RtFormat.R8Unorm => new FormatInfo(Format.R8Unorm, 1, 1, 1),
|
||||
RtFormat.R8Snorm => new FormatInfo(Format.R8Snorm, 1, 1, 1),
|
||||
RtFormat.R8Sint => new FormatInfo(Format.R8Sint, 1, 1, 1),
|
||||
RtFormat.R8Uint => new FormatInfo(Format.R8Uint, 1, 1, 1),
|
||||
RtFormat.B5G5R5X1Unorm => new FormatInfo(Format.B5G5R5X1Unorm, 1, 1, 2),
|
||||
RtFormat.R8G8B8X8Unorm => new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4),
|
||||
RtFormat.R8G8B8X8Srgb => new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4),
|
||||
RtFormat.D32Float => new FormatInfo(Format.D32Float, 1, 1, 4, 1),
|
||||
RtFormat.D16Unorm => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1),
|
||||
RtFormat.D24UnormS8Uint => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
|
||||
RtFormat.D24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 1),
|
||||
RtFormat.S8UintD24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
|
||||
RtFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1),
|
||||
RtFormat.D32FloatS8Uint => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2),
|
||||
RtFormat.R32G32B32A32Float => new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16, 4),
|
||||
RtFormat.R32G32B32A32Sint => new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16, 4),
|
||||
RtFormat.R32G32B32A32Uint => new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16, 4),
|
||||
RtFormat.R32G32B32X32Float => new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16, 4),
|
||||
RtFormat.R32G32B32X32Sint => new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16, 4),
|
||||
RtFormat.R32G32B32X32Uint => new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16, 4),
|
||||
RtFormat.R16G16B16X16Unorm => new FormatInfo(Format.R16G16B16A16Unorm, 1, 1, 8, 4),
|
||||
RtFormat.R16G16B16X16Snorm => new FormatInfo(Format.R16G16B16A16Snorm, 1, 1, 8, 4),
|
||||
RtFormat.R16G16B16X16Sint => new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8, 4),
|
||||
RtFormat.R16G16B16X16Uint => new FormatInfo(Format.R16G16B16A16Uint, 1, 1, 8, 4),
|
||||
RtFormat.R16G16B16A16Float => new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8, 4),
|
||||
RtFormat.R32G32Float => new FormatInfo(Format.R32G32Float, 1, 1, 8, 2),
|
||||
RtFormat.R32G32Sint => new FormatInfo(Format.R32G32Sint, 1, 1, 8, 2),
|
||||
RtFormat.R32G32Uint => new FormatInfo(Format.R32G32Uint, 1, 1, 8, 2),
|
||||
RtFormat.R16G16B16X16Float => new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8, 4),
|
||||
RtFormat.B8G8R8A8Unorm => new FormatInfo(Format.B8G8R8A8Unorm, 1, 1, 4, 4),
|
||||
RtFormat.B8G8R8A8Srgb => new FormatInfo(Format.B8G8R8A8Srgb, 1, 1, 4, 4),
|
||||
RtFormat.R10G10B10A2Unorm => new FormatInfo(Format.R10G10B10A2Unorm, 1, 1, 4, 4),
|
||||
RtFormat.R10G10B10A2Uint => new FormatInfo(Format.R10G10B10A2Uint, 1, 1, 4, 4),
|
||||
RtFormat.R8G8B8A8Unorm => new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4),
|
||||
RtFormat.R8G8B8A8Srgb => new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4),
|
||||
RtFormat.R8G8B8X8Snorm => new FormatInfo(Format.R8G8B8A8Snorm, 1, 1, 4, 4),
|
||||
RtFormat.R8G8B8X8Sint => new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4, 4),
|
||||
RtFormat.R8G8B8X8Uint => new FormatInfo(Format.R8G8B8A8Uint, 1, 1, 4, 4),
|
||||
RtFormat.R16G16Unorm => new FormatInfo(Format.R16G16Unorm, 1, 1, 4, 2),
|
||||
RtFormat.R16G16Snorm => new FormatInfo(Format.R16G16Snorm, 1, 1, 4, 2),
|
||||
RtFormat.R16G16Sint => new FormatInfo(Format.R16G16Sint, 1, 1, 4, 2),
|
||||
RtFormat.R16G16Uint => new FormatInfo(Format.R16G16Uint, 1, 1, 4, 2),
|
||||
RtFormat.R16G16Float => new FormatInfo(Format.R16G16Float, 1, 1, 4, 2),
|
||||
RtFormat.R11G11B10Float => new FormatInfo(Format.R11G11B10Float, 1, 1, 4, 3),
|
||||
RtFormat.R32Sint => new FormatInfo(Format.R32Sint, 1, 1, 4, 1),
|
||||
RtFormat.R32Uint => new FormatInfo(Format.R32Uint, 1, 1, 4, 1),
|
||||
RtFormat.R32Float => new FormatInfo(Format.R32Float, 1, 1, 4, 1),
|
||||
RtFormat.B8G8R8X8Unorm => new FormatInfo(Format.B8G8R8A8Unorm, 1, 1, 4, 4),
|
||||
RtFormat.B8G8R8X8Srgb => new FormatInfo(Format.B8G8R8A8Srgb, 1, 1, 4, 4),
|
||||
RtFormat.B5G6R5Unorm => new FormatInfo(Format.B5G6R5Unorm, 1, 1, 2, 3),
|
||||
RtFormat.B5G5R5A1Unorm => new FormatInfo(Format.B5G5R5A1Unorm, 1, 1, 2, 4),
|
||||
RtFormat.R8G8Unorm => new FormatInfo(Format.R8G8Unorm, 1, 1, 2, 2),
|
||||
RtFormat.R8G8Snorm => new FormatInfo(Format.R8G8Snorm, 1, 1, 2, 2),
|
||||
RtFormat.R8G8Sint => new FormatInfo(Format.R8G8Sint, 1, 1, 2, 2),
|
||||
RtFormat.R8G8Uint => new FormatInfo(Format.R8G8Uint, 1, 1, 2, 2),
|
||||
RtFormat.R16Unorm => new FormatInfo(Format.R16Unorm, 1, 1, 2, 1),
|
||||
RtFormat.R16Snorm => new FormatInfo(Format.R16Snorm, 1, 1, 2, 1),
|
||||
RtFormat.R16Sint => new FormatInfo(Format.R16Sint, 1, 1, 2, 1),
|
||||
RtFormat.R16Uint => new FormatInfo(Format.R16Uint, 1, 1, 2, 1),
|
||||
RtFormat.R16Float => new FormatInfo(Format.R16Float, 1, 1, 2, 1),
|
||||
RtFormat.R8Unorm => new FormatInfo(Format.R8Unorm, 1, 1, 1, 1),
|
||||
RtFormat.R8Snorm => new FormatInfo(Format.R8Snorm, 1, 1, 1, 1),
|
||||
RtFormat.R8Sint => new FormatInfo(Format.R8Sint, 1, 1, 1, 1),
|
||||
RtFormat.R8Uint => new FormatInfo(Format.R8Uint, 1, 1, 1, 1),
|
||||
RtFormat.B5G5R5X1Unorm => new FormatInfo(Format.B5G5R5X1Unorm, 1, 1, 2, 4),
|
||||
RtFormat.R8G8B8X8Unorm => new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4),
|
||||
RtFormat.R8G8B8X8Srgb => new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4),
|
||||
_ => FormatInfo.Default
|
||||
};
|
||||
}
|
||||
|
|
|
@ -112,7 +112,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||
Action<object> releaseCallback,
|
||||
object userObj)
|
||||
{
|
||||
FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel);
|
||||
FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel, 4);
|
||||
|
||||
TextureInfo info = new TextureInfo(
|
||||
address,
|
||||
|
|
Loading…
Reference in a new issue