Move GPU emulation from Ryujinx.HLE to Ryujinx.Graphics and misc changes (#402)

* Move GPU LLE emulation from HLE to Graphics

* Graphics: Move Gal/Texture to Texture

* Remove Engines/ directory and namespace

* Use tables for image formats

* Abstract OpCode decoding

* Simplify image table

* Do not leak Read* symbols in TextureReader

* Fixups

* Rename IGalFrameBuffer -> IGalRenderTarget

* Remove MaxBpp hardcoded value

* Change yet again texture data and add G8R8 flipping

* Rename GalFrameBufferFormat to GalSurfaceFormat

* Unident EnsureSetup in ImageHandler

* Add IsCompressed

* Address some feedback
This commit is contained in:
ReinUsesLisp 2018-09-08 14:51:50 -03:00 committed by gdkchan
parent a0c78f7920
commit ce1d5be212
58 changed files with 3378 additions and 3448 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,138 @@
using System;
using System.Diagnostics;
namespace Ryujinx.Graphics.Texture
{
class ASTCPixel
{
public short R { get; set; }
public short G { get; set; }
public short B { get; set; }
public short A { get; set; }
byte[] BitDepth = new byte[4];
public ASTCPixel(short _A, short _R, short _G, short _B)
{
A = _A;
R = _R;
G = _G;
B = _B;
for (int i = 0; i < 4; i++)
BitDepth[i] = 8;
}
public void ClampByte()
{
R = Math.Min(Math.Max(R, (short)0), (short)255);
G = Math.Min(Math.Max(G, (short)0), (short)255);
B = Math.Min(Math.Max(B, (short)0), (short)255);
A = Math.Min(Math.Max(A, (short)0), (short)255);
}
public short GetComponent(int Index)
{
switch(Index)
{
case 0: return A;
case 1: return R;
case 2: return G;
case 3: return B;
}
return 0;
}
public void SetComponent(int Index, int Value)
{
switch (Index)
{
case 0:
A = (short)Value;
break;
case 1:
R = (short)Value;
break;
case 2:
G = (short)Value;
break;
case 3:
B = (short)Value;
break;
}
}
public void ChangeBitDepth(byte[] Depth)
{
for(int i = 0; i< 4; i++)
{
int Value = ChangeBitDepth(GetComponent(i), BitDepth[i], Depth[i]);
SetComponent(i, Value);
BitDepth[i] = Depth[i];
}
}
short ChangeBitDepth(short Value, byte OldDepth, byte NewDepth)
{
Debug.Assert(NewDepth <= 8);
Debug.Assert(OldDepth <= 8);
if (OldDepth == NewDepth)
{
// Do nothing
return Value;
}
else if (OldDepth == 0 && NewDepth != 0)
{
return (short)((1 << NewDepth) - 1);
}
else if (NewDepth > OldDepth)
{
return (short)BitArrayStream.Replicate(Value, OldDepth, NewDepth);
}
else
{
// oldDepth > newDepth
if (NewDepth == 0)
{
return 0xFF;
}
else
{
byte BitsWasted = (byte)(OldDepth - NewDepth);
short TempValue = Value;
TempValue = (short)((TempValue + (1 << (BitsWasted - 1))) >> BitsWasted);
TempValue = Math.Min(Math.Max((short)0, TempValue), (short)((1 << NewDepth) - 1));
return (byte)(TempValue);
}
}
}
public int Pack()
{
ASTCPixel NewPixel = new ASTCPixel(A, R, G, B);
byte[] eightBitDepth = { 8, 8, 8, 8 };
NewPixel.ChangeBitDepth(eightBitDepth);
return (byte)NewPixel.A << 24 |
(byte)NewPixel.B << 16 |
(byte)NewPixel.G << 8 |
(byte)NewPixel.R << 0;
}
// Adds more precision to the blue channel as described
// in C.2.14
public static ASTCPixel BlueContract(int a, int r, int g, int b)
{
return new ASTCPixel((short)(a),
(short)((r + b) >> 1),
(short)((g + b) >> 1),
(short)(b));
}
}
}

View file

@ -0,0 +1,121 @@
using System;
using System.Collections;
namespace Ryujinx.Graphics.Texture
{
public class BitArrayStream
{
public BitArray BitsArray;
public int Position { get; private set; }
public BitArrayStream(BitArray BitArray)
{
BitsArray = BitArray;
Position = 0;
}
public short ReadBits(int Length)
{
int RetValue = 0;
for (int i = Position; i < Position + Length; i++)
{
if (BitsArray[i])
{
RetValue |= 1 << (i - Position);
}
}
Position += Length;
return (short)RetValue;
}
public int ReadBits(int Start, int End)
{
int RetValue = 0;
for (int i = Start; i <= End; i++)
{
if (BitsArray[i])
{
RetValue |= 1 << (i - Start);
}
}
return RetValue;
}
public int ReadBit(int Index)
{
return Convert.ToInt32(BitsArray[Index]);
}
public void WriteBits(int Value, int Length)
{
for (int i = Position; i < Position + Length; i++)
{
BitsArray[i] = ((Value >> (i - Position)) & 1) != 0;
}
Position += Length;
}
public byte[] ToByteArray()
{
byte[] RetArray = new byte[(BitsArray.Length + 7) / 8];
BitsArray.CopyTo(RetArray, 0);
return RetArray;
}
public static int Replicate(int Value, int NumberBits, int ToBit)
{
if (NumberBits == 0) return 0;
if (ToBit == 0) return 0;
int TempValue = Value & ((1 << NumberBits) - 1);
int RetValue = TempValue;
int ResLength = NumberBits;
while (ResLength < ToBit)
{
int Comp = 0;
if (NumberBits > ToBit - ResLength)
{
int NewShift = ToBit - ResLength;
Comp = NumberBits - NewShift;
NumberBits = NewShift;
}
RetValue <<= NumberBits;
RetValue |= TempValue >> Comp;
ResLength += NumberBits;
}
return RetValue;
}
public static int PopCnt(int Number)
{
int Counter;
for (Counter = 0; Number != 0; Counter++)
{
Number &= Number - 1;
}
return Counter;
}
public static void Swap<T>(ref T lhs, ref T rhs)
{
T Temp = lhs;
lhs = rhs;
rhs = Temp;
}
// Transfers a bit as described in C.2.14
public static void BitTransferSigned(ref int a, ref int b)
{
b >>= 1;
b |= a & 0x80;
a >>= 1;
a &= 0x3F;
if ((a & 0x20) != 0) a -= 0x40;
}
}
}

View file

@ -0,0 +1,59 @@
using System;
namespace Ryujinx.Graphics.Texture
{
class BlockLinearSwizzle : ISwizzle
{
private int BhShift;
private int BppShift;
private int BhMask;
private int XShift;
private int GobStride;
public BlockLinearSwizzle(int Width, int Bpp, int BlockHeight = 16)
{
BhMask = (BlockHeight * 8) - 1;
BhShift = CountLsbZeros(BlockHeight * 8);
BppShift = CountLsbZeros(Bpp);
int WidthInGobs = (int)MathF.Ceiling(Width * Bpp / 64f);
GobStride = 512 * BlockHeight * WidthInGobs;
XShift = CountLsbZeros(512 * BlockHeight);
}
private int CountLsbZeros(int Value)
{
int Count = 0;
while (((Value >> Count) & 1) == 0)
{
Count++;
}
return Count;
}
public int GetSwizzleOffset(int X, int Y)
{
X <<= BppShift;
int Position = (Y >> BhShift) * GobStride;
Position += (X >> 6) << XShift;
Position += ((Y & BhMask) >> 3) << 9;
Position += ((X & 0x3f) >> 5) << 8;
Position += ((Y & 0x07) >> 1) << 6;
Position += ((X & 0x1f) >> 4) << 5;
Position += ((Y & 0x01) >> 0) << 4;
Position += ((X & 0x0f) >> 0) << 0;
return Position;
}
}
}

View file

@ -0,0 +1,7 @@
namespace Ryujinx.Graphics.Texture
{
interface ISwizzle
{
int GetSwizzleOffset(int X, int Y);
}
}

View file

@ -0,0 +1,24 @@
namespace Ryujinx.Graphics.Texture
{
static class ImageConverter
{
public static byte[] G8R8ToR8G8(
byte[] Data,
int Width,
int Height,
int Depth)
{
int Texels = Width * Height * Depth;
byte[] Output = new byte[Texels * 2];
for (int Texel = 0; Texel < Texels; Texel++)
{
Output[Texel * 2 + 0] = Data[Texel * 2 + 1];
Output[Texel * 2 + 1] = Data[Texel * 2 + 0];
}
return Output;
}
}
}

View file

@ -0,0 +1,357 @@
using Ryujinx.Graphics.Gal;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Texture
{
static class ImageUtils
{
struct ImageDescriptor
{
public TextureReaderDelegate Reader;
public bool HasColor;
public bool HasDepth;
public bool HasStencil;
public bool Compressed;
public ImageDescriptor(
TextureReaderDelegate Reader,
bool HasColor,
bool HasDepth,
bool HasStencil,
bool Compressed)
{
this.Reader = Reader;
this.HasColor = HasColor;
this.HasDepth = HasDepth;
this.HasStencil = HasStencil;
this.Compressed = Compressed;
}
}
private const GalImageFormat Snorm = GalImageFormat.Snorm;
private const GalImageFormat Unorm = GalImageFormat.Unorm;
private const GalImageFormat Sint = GalImageFormat.Sint;
private const GalImageFormat Uint = GalImageFormat.Uint;
private const GalImageFormat Sfloat = GalImageFormat.Sfloat;
private static readonly Dictionary<GalTextureFormat, GalImageFormat> s_TextureTable =
new Dictionary<GalTextureFormat, GalImageFormat>()
{
{ GalTextureFormat.R32G32B32A32, GalImageFormat.R32G32B32A32 | Sint | Uint | Sfloat },
{ GalTextureFormat.R16G16B16A16, GalImageFormat.R16G16B16A16 | Snorm | Unorm | Sint | Uint | Sfloat },
{ GalTextureFormat.R32G32, GalImageFormat.R32G32 | Sint | Uint | Sfloat },
{ GalTextureFormat.A8B8G8R8, GalImageFormat.A8B8G8R8 | Snorm | Unorm | Sint | Uint },
{ GalTextureFormat.A2B10G10R10, GalImageFormat.A2B10G10R10 | Snorm | Unorm | Sint | Uint },
{ GalTextureFormat.G8R8, GalImageFormat.G8R8 | Snorm | Unorm | Sint | Uint },
{ GalTextureFormat.R16, GalImageFormat.R16 | Snorm | Unorm | Sint | Uint | Sfloat },
{ GalTextureFormat.R8, GalImageFormat.R8 | Snorm | Unorm | Sint | Uint },
{ GalTextureFormat.R32, GalImageFormat.R32 | Sint | Uint | Sfloat },
{ GalTextureFormat.A4B4G4R4, GalImageFormat.A4B4G4R4 | Unorm },
{ GalTextureFormat.A1B5G5R5, GalImageFormat.A1R5G5B5 | Unorm },
{ GalTextureFormat.B5G6R5, GalImageFormat.B5G6R5 | Unorm },
{ GalTextureFormat.BF10GF11RF11, GalImageFormat.B10G11R11 | Sfloat },
{ GalTextureFormat.Z24S8, GalImageFormat.D24_S8 | Unorm },
{ GalTextureFormat.ZF32, GalImageFormat.D32 | Sfloat },
{ GalTextureFormat.ZF32_X24S8, GalImageFormat.D32_S8 | Unorm },
//Compressed formats
{ GalTextureFormat.BC6H_SF16, GalImageFormat.BC6H_SF16 | Unorm },
{ GalTextureFormat.BC6H_UF16, GalImageFormat.BC6H_UF16 | Unorm },
{ GalTextureFormat.BC7U, GalImageFormat.BC7 | Unorm },
{ GalTextureFormat.BC1, GalImageFormat.BC1_RGBA | Unorm },
{ GalTextureFormat.BC2, GalImageFormat.BC2 | Unorm },
{ GalTextureFormat.BC3, GalImageFormat.BC3 | Unorm },
{ GalTextureFormat.BC4, GalImageFormat.BC4 | Unorm | Snorm },
{ GalTextureFormat.BC5, GalImageFormat.BC5 | Unorm | Snorm },
{ GalTextureFormat.Astc2D4x4, GalImageFormat.ASTC_4x4 | Unorm },
{ GalTextureFormat.Astc2D5x5, GalImageFormat.ASTC_5x5 | Unorm },
{ GalTextureFormat.Astc2D6x6, GalImageFormat.ASTC_6x6 | Unorm },
{ GalTextureFormat.Astc2D8x8, GalImageFormat.ASTC_8x8 | Unorm },
{ GalTextureFormat.Astc2D10x10, GalImageFormat.ASTC_10x10 | Unorm },
{ GalTextureFormat.Astc2D12x12, GalImageFormat.ASTC_12x12 | Unorm },
{ GalTextureFormat.Astc2D5x4, GalImageFormat.ASTC_5x4 | Unorm },
{ GalTextureFormat.Astc2D6x5, GalImageFormat.ASTC_6x5 | Unorm },
{ GalTextureFormat.Astc2D8x6, GalImageFormat.ASTC_8x6 | Unorm },
{ GalTextureFormat.Astc2D10x8, GalImageFormat.ASTC_10x8 | Unorm },
{ GalTextureFormat.Astc2D12x10, GalImageFormat.ASTC_12x10 | Unorm },
{ GalTextureFormat.Astc2D8x5, GalImageFormat.ASTC_8x5 | Unorm },
{ GalTextureFormat.Astc2D10x5, GalImageFormat.ASTC_10x5 | Unorm },
{ GalTextureFormat.Astc2D10x6, GalImageFormat.ASTC_10x6 | Unorm }
};
private static readonly Dictionary<GalImageFormat, ImageDescriptor> s_ImageTable =
new Dictionary<GalImageFormat, ImageDescriptor>()
{
{ GalImageFormat.R32G32B32A32, new ImageDescriptor(TextureReader.Read16Bpp, true, false, false, false) },
{ GalImageFormat.R16G16B16A16, new ImageDescriptor(TextureReader.Read8Bpp, true, false, false, false) },
{ GalImageFormat.R32G32, new ImageDescriptor(TextureReader.Read8Bpp, true, false, false, false) },
{ GalImageFormat.A8B8G8R8, new ImageDescriptor(TextureReader.Read4Bpp, true, false, false, false) },
{ GalImageFormat.A2B10G10R10, new ImageDescriptor(TextureReader.Read4Bpp, true, false, false, false) },
{ GalImageFormat.R32, new ImageDescriptor(TextureReader.Read4Bpp, true, false, false, false) },
{ GalImageFormat.A4B4G4R4, new ImageDescriptor(TextureReader.Read2Bpp, true, false, false, false) },
{ GalImageFormat.BC6H_SF16, new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4, true, false, false, true) },
{ GalImageFormat.BC6H_UF16, new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4, true, false, false, true) },
{ GalImageFormat.A1R5G5B5, new ImageDescriptor(TextureReader.Read5551, true, false, false, false) },
{ GalImageFormat.B5G6R5, new ImageDescriptor(TextureReader.Read565, true, false, false, false) },
{ GalImageFormat.BC7, new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4, true, false, false, true) },
{ GalImageFormat.R16G16, new ImageDescriptor(TextureReader.Read4Bpp, true, false, false, false) },
{ GalImageFormat.R8G8, new ImageDescriptor(TextureReader.Read2Bpp, true, false, false, false) },
{ GalImageFormat.G8R8, new ImageDescriptor(TextureReader.Read2Bpp, true, false, false, false) },
{ GalImageFormat.R16, new ImageDescriptor(TextureReader.Read2Bpp, true, false, false, false) },
{ GalImageFormat.R8, new ImageDescriptor(TextureReader.Read1Bpp, true, false, false, false) },
{ GalImageFormat.B10G11R11, new ImageDescriptor(TextureReader.Read4Bpp, true, false, false, false) },
{ GalImageFormat.A8B8G8R8_SRGB, new ImageDescriptor(TextureReader.Read4Bpp, true, false, false, false) },
{ GalImageFormat.BC1_RGBA, new ImageDescriptor(TextureReader.Read8Bpt4x4, true, false, false, true) },
{ GalImageFormat.BC2, new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4, true, false, false, true) },
{ GalImageFormat.BC3, new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4, true, false, false, true) },
{ GalImageFormat.BC4, new ImageDescriptor(TextureReader.Read8Bpt4x4, true, false, false, true) },
{ GalImageFormat.BC5, new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4, true, false, false, true) },
{ GalImageFormat.ASTC_4x4, new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4, true, false, false, true) },
{ GalImageFormat.ASTC_5x5, new ImageDescriptor(TextureReader.Read16BptCompressedTexture5x5, true, false, false, true) },
{ GalImageFormat.ASTC_6x6, new ImageDescriptor(TextureReader.Read16BptCompressedTexture6x6, true, false, false, true) },
{ GalImageFormat.ASTC_8x8, new ImageDescriptor(TextureReader.Read16BptCompressedTexture8x8, true, false, false, true) },
{ GalImageFormat.ASTC_10x10, new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x10, true, false, false, true) },
{ GalImageFormat.ASTC_12x12, new ImageDescriptor(TextureReader.Read16BptCompressedTexture12x12, true, false, false, true) },
{ GalImageFormat.ASTC_5x4, new ImageDescriptor(TextureReader.Read16BptCompressedTexture5x4, true, false, false, true) },
{ GalImageFormat.ASTC_6x5, new ImageDescriptor(TextureReader.Read16BptCompressedTexture6x5, true, false, false, true) },
{ GalImageFormat.ASTC_8x6, new ImageDescriptor(TextureReader.Read16BptCompressedTexture8x6, true, false, false, true) },
{ GalImageFormat.ASTC_10x8, new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x8, true, false, false, true) },
{ GalImageFormat.ASTC_12x10, new ImageDescriptor(TextureReader.Read16BptCompressedTexture12x10, true, false, false, true) },
{ GalImageFormat.ASTC_8x5, new ImageDescriptor(TextureReader.Read16BptCompressedTexture8x5, true, false, false, true) },
{ GalImageFormat.ASTC_10x5, new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x5, true, false, false, true) },
{ GalImageFormat.ASTC_10x6, new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x6, true, false, false, true) },
{ GalImageFormat.D24_S8, new ImageDescriptor(TextureReader.Read4Bpp, false, true, true, false) },
{ GalImageFormat.D32, new ImageDescriptor(TextureReader.Read4Bpp, false, true, false, false) },
{ GalImageFormat.D16, new ImageDescriptor(TextureReader.Read2Bpp, false, true, false, false) },
{ GalImageFormat.D32_S8, new ImageDescriptor(TextureReader.Read8Bpp, false, true, true, false) },
};
public static GalImageFormat ConvertTexture(
GalTextureFormat Format,
GalTextureType RType,
GalTextureType GType,
GalTextureType BType,
GalTextureType AType)
{
if (RType != GType || RType != BType || RType != AType)
{
throw new NotImplementedException("Per component types are not implemented");
}
if (!s_TextureTable.TryGetValue(Format, out GalImageFormat ImageFormat))
{
throw new NotImplementedException("Texture with format " + ((int)Format).ToString("x2") + " not implemented");
}
GalTextureType Type = RType;
GalImageFormat FormatType = GetFormatType(RType);
if (ImageFormat.HasFlag(FormatType))
{
return (ImageFormat & GalImageFormat.FormatMask) | FormatType;
}
else
{
throw new NotImplementedException("Texture with format " + Format +
" and component type " + Type + " is not implemented");
}
}
public static GalImageFormat ConvertSurface(GalSurfaceFormat Format)
{
switch (Format)
{
case GalSurfaceFormat.RGBA32Float: return GalImageFormat.R32G32B32A32 | Sfloat;
case GalSurfaceFormat.RGBA16Float: return GalImageFormat.R16G16B16A16 | Sfloat;
case GalSurfaceFormat.RG32Float: return GalImageFormat.R32G32 | Sfloat;
case GalSurfaceFormat.RG32Sint: return GalImageFormat.R32G32 | Sint;
case GalSurfaceFormat.RG32Uint: return GalImageFormat.R32G32 | Uint;
case GalSurfaceFormat.BGRA8Unorm: return GalImageFormat.R8G8B8A8 | Unorm; //Is this right?
case GalSurfaceFormat.BGRA8Srgb: return GalImageFormat.A8B8G8R8_SRGB; //This one might be wrong
case GalSurfaceFormat.RGB10A2Unorm: return GalImageFormat.A2B10G10R10 | Unorm;
case GalSurfaceFormat.RGBA8Unorm: return GalImageFormat.A8B8G8R8 | Unorm;
case GalSurfaceFormat.RGBA8Srgb: return GalImageFormat.A8B8G8R8_SRGB;
case GalSurfaceFormat.RGBA8Snorm: return GalImageFormat.A8B8G8R8 | Snorm;
case GalSurfaceFormat.RG16Snorm: return GalImageFormat.R16G16 | Snorm;
case GalSurfaceFormat.RG16Float: return GalImageFormat.R16G16 | Sfloat;
case GalSurfaceFormat.R11G11B10Float: return GalImageFormat.B10G11R11 | Sfloat;
case GalSurfaceFormat.R32Float: return GalImageFormat.R32 | Sfloat;
case GalSurfaceFormat.RG8Unorm: return GalImageFormat.R8G8 | Unorm;
case GalSurfaceFormat.RG8Snorm: return GalImageFormat.R8 | Snorm;
case GalSurfaceFormat.R16Float: return GalImageFormat.R16 | Sfloat;
case GalSurfaceFormat.R8Unorm: return GalImageFormat.R8 | Unorm;
}
throw new NotImplementedException(Format.ToString());
}
public static GalImageFormat ConvertZeta(GalZetaFormat Format)
{
switch (Format)
{
case GalZetaFormat.Z32Float: return GalImageFormat.D32 | Sfloat;
case GalZetaFormat.S8Z24Unorm: return GalImageFormat.D24_S8 | Unorm;
case GalZetaFormat.Z16Unorm: return GalImageFormat.D16 | Unorm;
//This one might not be Uint, change when a texture uses this format
case GalZetaFormat.Z32S8X24Float: return GalImageFormat.D32_S8 | Uint;
}
throw new NotImplementedException(Format.ToString());
}
public static TextureReaderDelegate GetReader(GalImageFormat Format)
{
return GetImageDescriptor(Format).Reader;
}
public static int GetSize(GalImage Image)
{
switch (Image.Format & GalImageFormat.FormatMask)
{
case GalImageFormat.R32G32B32A32:
return Image.Width * Image.Height * 16;
case GalImageFormat.R16G16B16A16:
case GalImageFormat.D32_S8:
case GalImageFormat.R32G32:
return Image.Width * Image.Height * 8;
case GalImageFormat.A8B8G8R8:
case GalImageFormat.A8B8G8R8_SRGB:
case GalImageFormat.A2B10G10R10:
case GalImageFormat.R16G16:
case GalImageFormat.R32:
case GalImageFormat.D32:
case GalImageFormat.B10G11R11:
case GalImageFormat.D24_S8:
return Image.Width * Image.Height * 4;
case GalImageFormat.B4G4R4A4:
case GalImageFormat.A1R5G5B5:
case GalImageFormat.B5G6R5:
case GalImageFormat.R8G8:
case GalImageFormat.R16:
case GalImageFormat.D16:
return Image.Width * Image.Height * 2;
case GalImageFormat.R8:
return Image.Width * Image.Height;
case GalImageFormat.BC1_RGBA:
case GalImageFormat.BC4:
{
return CompressedTextureSize(Image.Width, Image.Height, 4, 4, 8);
}
case GalImageFormat.BC6H_SF16:
case GalImageFormat.BC6H_UF16:
case GalImageFormat.BC7:
case GalImageFormat.BC2:
case GalImageFormat.BC3:
case GalImageFormat.BC5:
case GalImageFormat.ASTC_4x4:
return CompressedTextureSize(Image.Width, Image.Height, 4, 4, 16);
case GalImageFormat.ASTC_5x5:
return CompressedTextureSize(Image.Width, Image.Height, 5, 5, 16);
case GalImageFormat.ASTC_6x6:
return CompressedTextureSize(Image.Width, Image.Height, 6, 6, 16);
case GalImageFormat.ASTC_8x8:
return CompressedTextureSize(Image.Width, Image.Height, 8, 8, 16);
case GalImageFormat.ASTC_10x10:
return CompressedTextureSize(Image.Width, Image.Height, 10, 10, 16);
case GalImageFormat.ASTC_12x12:
return CompressedTextureSize(Image.Width, Image.Height, 12, 12, 16);
case GalImageFormat.ASTC_5x4:
return CompressedTextureSize(Image.Width, Image.Height, 5, 4, 16);
case GalImageFormat.ASTC_6x5:
return CompressedTextureSize(Image.Width, Image.Height, 6, 5, 16);
case GalImageFormat.ASTC_8x6:
return CompressedTextureSize(Image.Width, Image.Height, 8, 6, 16);
case GalImageFormat.ASTC_10x8:
return CompressedTextureSize(Image.Width, Image.Height, 10, 8, 16);
case GalImageFormat.ASTC_12x10:
return CompressedTextureSize(Image.Width, Image.Height, 12, 10, 16);
case GalImageFormat.ASTC_8x5:
return CompressedTextureSize(Image.Width, Image.Height, 8, 5, 16);
case GalImageFormat.ASTC_10x5:
return CompressedTextureSize(Image.Width, Image.Height, 10, 5, 16);
case GalImageFormat.ASTC_10x6:
return CompressedTextureSize(Image.Width, Image.Height, 10, 6, 16);
}
throw new NotImplementedException((Image.Format & GalImageFormat.FormatMask).ToString());
}
public static bool HasColor(GalImageFormat Format)
{
return GetImageDescriptor(Format).HasColor;
}
public static bool HasDepth(GalImageFormat Format)
{
return GetImageDescriptor(Format).HasDepth;
}
public static bool HasStencil(GalImageFormat Format)
{
return GetImageDescriptor(Format).HasStencil;
}
public static bool IsCompressed(GalImageFormat Format)
{
return GetImageDescriptor(Format).Compressed;
}
private static ImageDescriptor GetImageDescriptor(GalImageFormat Format)
{
GalImageFormat TypeLess = (Format & GalImageFormat.FormatMask);
if (s_ImageTable.TryGetValue(TypeLess, out ImageDescriptor Descriptor))
{
return Descriptor;
}
throw new NotImplementedException("Image with format " + TypeLess.ToString() + " not implemented");
}
private static GalImageFormat GetFormatType(GalTextureType Type)
{
switch (Type)
{
case GalTextureType.Snorm: return Snorm;
case GalTextureType.Unorm: return Unorm;
case GalTextureType.Sint: return Sint;
case GalTextureType.Uint: return Uint;
case GalTextureType.Float: return Sfloat;
default: throw new NotImplementedException(((int)Type).ToString());
}
}
private static int CompressedTextureSize(int TextureWidth, int TextureHeight, int BlockWidth, int BlockHeight, int Bpb)
{
int W = (TextureWidth + (BlockWidth - 1)) / BlockWidth;
int H = (TextureHeight + (BlockHeight - 1)) / BlockHeight;
return W * H * Bpb;
}
}
}

View file

@ -0,0 +1,269 @@
using System.Collections;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Texture
{
public struct IntegerEncoded
{
public enum EIntegerEncoding
{
JustBits,
Quint,
Trit
}
EIntegerEncoding Encoding;
public int NumberBits { get; private set; }
public int BitValue { get; private set; }
public int TritValue { get; private set; }
public int QuintValue { get; private set; }
public IntegerEncoded(EIntegerEncoding _Encoding, int NumBits)
{
Encoding = _Encoding;
NumberBits = NumBits;
BitValue = 0;
TritValue = 0;
QuintValue = 0;
}
public bool MatchesEncoding(IntegerEncoded Other)
{
return Encoding == Other.Encoding && NumberBits == Other.NumberBits;
}
public EIntegerEncoding GetEncoding()
{
return Encoding;
}
public int GetBitLength(int NumberVals)
{
int TotalBits = NumberBits * NumberVals;
if (Encoding == EIntegerEncoding.Trit)
{
TotalBits += (NumberVals * 8 + 4) / 5;
}
else if (Encoding == EIntegerEncoding.Quint)
{
TotalBits += (NumberVals * 7 + 2) / 3;
}
return TotalBits;
}
public static IntegerEncoded CreateEncoding(int MaxVal)
{
while (MaxVal > 0)
{
int Check = MaxVal + 1;
// Is maxVal a power of two?
if ((Check & (Check - 1)) == 0)
{
return new IntegerEncoded(EIntegerEncoding.JustBits, BitArrayStream.PopCnt(MaxVal));
}
// Is maxVal of the type 3*2^n - 1?
if ((Check % 3 == 0) && ((Check / 3) & ((Check / 3) - 1)) == 0)
{
return new IntegerEncoded(EIntegerEncoding.Trit, BitArrayStream.PopCnt(Check / 3 - 1));
}
// Is maxVal of the type 5*2^n - 1?
if ((Check % 5 == 0) && ((Check / 5) & ((Check / 5) - 1)) == 0)
{
return new IntegerEncoded(EIntegerEncoding.Quint, BitArrayStream.PopCnt(Check / 5 - 1));
}
// Apparently it can't be represented with a bounded integer sequence...
// just iterate.
MaxVal--;
}
return new IntegerEncoded(EIntegerEncoding.JustBits, 0);
}
public static void DecodeTritBlock(
BitArrayStream BitStream,
List<IntegerEncoded> ListIntegerEncoded,
int NumberBitsPerValue)
{
// Implement the algorithm in section C.2.12
int[] m = new int[5];
int[] t = new int[5];
int T;
// Read the trit encoded block according to
// table C.2.14
m[0] = BitStream.ReadBits(NumberBitsPerValue);
T = BitStream.ReadBits(2);
m[1] = BitStream.ReadBits(NumberBitsPerValue);
T |= BitStream.ReadBits(2) << 2;
m[2] = BitStream.ReadBits(NumberBitsPerValue);
T |= BitStream.ReadBits(1) << 4;
m[3] = BitStream.ReadBits(NumberBitsPerValue);
T |= BitStream.ReadBits(2) << 5;
m[4] = BitStream.ReadBits(NumberBitsPerValue);
T |= BitStream.ReadBits(1) << 7;
int C = 0;
BitArrayStream Tb = new BitArrayStream(new BitArray(new int[] { T }));
if (Tb.ReadBits(2, 4) == 7)
{
C = (Tb.ReadBits(5, 7) << 2) | Tb.ReadBits(0, 1);
t[4] = t[3] = 2;
}
else
{
C = Tb.ReadBits(0, 4);
if (Tb.ReadBits(5, 6) == 3)
{
t[4] = 2;
t[3] = Tb.ReadBit(7);
}
else
{
t[4] = Tb.ReadBit(7);
t[3] = Tb.ReadBits(5, 6);
}
}
BitArrayStream Cb = new BitArrayStream(new BitArray(new int[] { C }));
if (Cb.ReadBits(0, 1) == 3)
{
t[2] = 2;
t[1] = Cb.ReadBit(4);
t[0] = (Cb.ReadBit(3) << 1) | (Cb.ReadBit(2) & ~Cb.ReadBit(3));
}
else if (Cb.ReadBits(2, 3) == 3)
{
t[2] = 2;
t[1] = 2;
t[0] = Cb.ReadBits(0, 1);
}
else
{
t[2] = Cb.ReadBit(4);
t[1] = Cb.ReadBits(2, 3);
t[0] = (Cb.ReadBit(1) << 1) | (Cb.ReadBit(0) & ~Cb.ReadBit(1));
}
for (int i = 0; i < 5; i++)
{
IntegerEncoded IntEncoded = new IntegerEncoded(EIntegerEncoding.Trit, NumberBitsPerValue)
{
BitValue = m[i],
TritValue = t[i]
};
ListIntegerEncoded.Add(IntEncoded);
}
}
public static void DecodeQuintBlock(
BitArrayStream BitStream,
List<IntegerEncoded> ListIntegerEncoded,
int NumberBitsPerValue)
{
// Implement the algorithm in section C.2.12
int[] m = new int[3];
int[] q = new int[3];
int Q;
// Read the trit encoded block according to
// table C.2.15
m[0] = BitStream.ReadBits(NumberBitsPerValue);
Q = BitStream.ReadBits(3);
m[1] = BitStream.ReadBits(NumberBitsPerValue);
Q |= BitStream.ReadBits(2) << 3;
m[2] = BitStream.ReadBits(NumberBitsPerValue);
Q |= BitStream.ReadBits(2) << 5;
BitArrayStream Qb = new BitArrayStream(new BitArray(new int[] { Q }));
if (Qb.ReadBits(1, 2) == 3 && Qb.ReadBits(5, 6) == 0)
{
q[0] = q[1] = 4;
q[2] = (Qb.ReadBit(0) << 2) | ((Qb.ReadBit(4) & ~Qb.ReadBit(0)) << 1) | (Qb.ReadBit(3) & ~Qb.ReadBit(0));
}
else
{
int C = 0;
if (Qb.ReadBits(1, 2) == 3)
{
q[2] = 4;
C = (Qb.ReadBits(3, 4) << 3) | ((~Qb.ReadBits(5, 6) & 3) << 1) | Qb.ReadBit(0);
}
else
{
q[2] = Qb.ReadBits(5, 6);
C = Qb.ReadBits(0, 4);
}
BitArrayStream Cb = new BitArrayStream(new BitArray(new int[] { C }));
if (Cb.ReadBits(0, 2) == 5)
{
q[1] = 4;
q[0] = Cb.ReadBits(3, 4);
}
else
{
q[1] = Cb.ReadBits(3, 4);
q[0] = Cb.ReadBits(0, 2);
}
}
for (int i = 0; i < 3; i++)
{
IntegerEncoded IntEncoded = new IntegerEncoded(EIntegerEncoding.Quint, NumberBitsPerValue)
{
BitValue = m[i],
QuintValue = q[i]
};
ListIntegerEncoded.Add(IntEncoded);
}
}
public static void DecodeIntegerSequence(
List<IntegerEncoded> DecodeIntegerSequence,
BitArrayStream BitStream,
int MaxRange,
int NumberValues)
{
// Determine encoding parameters
IntegerEncoded IntEncoded = CreateEncoding(MaxRange);
// Start decoding
int NumberValuesDecoded = 0;
while (NumberValuesDecoded < NumberValues)
{
switch (IntEncoded.GetEncoding())
{
case EIntegerEncoding.Quint:
{
DecodeQuintBlock(BitStream, DecodeIntegerSequence, IntEncoded.NumberBits);
NumberValuesDecoded += 3;
break;
}
case EIntegerEncoding.Trit:
{
DecodeTritBlock(BitStream, DecodeIntegerSequence, IntEncoded.NumberBits);
NumberValuesDecoded += 5;
break;
}
case EIntegerEncoding.JustBits:
{
IntEncoded.BitValue = BitStream.ReadBits(IntEncoded.NumberBits);
DecodeIntegerSequence.Add(IntEncoded);
NumberValuesDecoded++;
break;
}
}
}
}
}
}

View file

@ -0,0 +1,19 @@
namespace Ryujinx.Graphics.Texture
{
class LinearSwizzle : ISwizzle
{
private int Pitch;
private int Bpp;
public LinearSwizzle(int Pitch, int Bpp)
{
this.Pitch = Pitch;
this.Bpp = Bpp;
}
public int GetSwizzleOffset(int X, int Y)
{
return X * Bpp + Y * Pitch;
}
}
}

View file

@ -0,0 +1,132 @@
using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Memory;
using System;
namespace Ryujinx.Graphics.Texture
{
static class TextureFactory
{
public static GalImage MakeTexture(NvGpuVmm Vmm, long TicPosition)
{
int[] Tic = ReadWords(Vmm, TicPosition, 8);
GalImageFormat Format = GetImageFormat(Tic);
GalTextureSource XSource = (GalTextureSource)((Tic[0] >> 19) & 7);
GalTextureSource YSource = (GalTextureSource)((Tic[0] >> 22) & 7);
GalTextureSource ZSource = (GalTextureSource)((Tic[0] >> 25) & 7);
GalTextureSource WSource = (GalTextureSource)((Tic[0] >> 28) & 7);
int Width = (Tic[4] & 0xffff) + 1;
int Height = (Tic[5] & 0xffff) + 1;
return new GalImage(
Width,
Height,
Format,
XSource,
YSource,
ZSource,
WSource);
}
public static byte[] GetTextureData(NvGpuVmm Vmm, long TicPosition)
{
int[] Tic = ReadWords(Vmm, TicPosition, 8);
GalImageFormat Format = GetImageFormat(Tic);
long TextureAddress = (uint)Tic[1];
TextureAddress |= (long)((ushort)Tic[2]) << 32;
TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7);
if (Swizzle == TextureSwizzle.BlockLinear ||
Swizzle == TextureSwizzle.BlockLinearColorKey)
{
TextureAddress &= ~0x1ffL;
}
else if (Swizzle == TextureSwizzle.Pitch ||
Swizzle == TextureSwizzle.PitchColorKey)
{
TextureAddress &= ~0x1fL;
}
int Pitch = (Tic[3] & 0xffff) << 5;
int BlockHeightLog2 = (Tic[3] >> 3) & 7;
int TileWidthLog2 = (Tic[3] >> 10) & 7;
int BlockHeight = 1 << BlockHeightLog2;
int TileWidth = 1 << TileWidthLog2;
int Width = (Tic[4] & 0xffff) + 1;
int Height = (Tic[5] & 0xffff) + 1;
TextureInfo Texture = new TextureInfo(
TextureAddress,
Width,
Height,
Pitch,
BlockHeight,
TileWidth,
Swizzle,
Format);
return TextureReader.Read(Vmm, Texture);
}
public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition)
{
int[] Tsc = ReadWords(Vmm, TscPosition, 8);
GalTextureWrap AddressU = (GalTextureWrap)((Tsc[0] >> 0) & 7);
GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7);
GalTextureWrap AddressP = (GalTextureWrap)((Tsc[0] >> 6) & 7);
GalTextureFilter MagFilter = (GalTextureFilter) ((Tsc[1] >> 0) & 3);
GalTextureFilter MinFilter = (GalTextureFilter) ((Tsc[1] >> 4) & 3);
GalTextureMipFilter MipFilter = (GalTextureMipFilter)((Tsc[1] >> 6) & 3);
GalColorF BorderColor = new GalColorF(
BitConverter.Int32BitsToSingle(Tsc[4]),
BitConverter.Int32BitsToSingle(Tsc[5]),
BitConverter.Int32BitsToSingle(Tsc[6]),
BitConverter.Int32BitsToSingle(Tsc[7]));
return new GalTextureSampler(
AddressU,
AddressV,
AddressP,
MinFilter,
MagFilter,
MipFilter,
BorderColor);
}
private static GalImageFormat GetImageFormat(int[] Tic)
{
GalTextureType RType = (GalTextureType)((Tic[0] >> 7) & 7);
GalTextureType GType = (GalTextureType)((Tic[0] >> 10) & 7);
GalTextureType BType = (GalTextureType)((Tic[0] >> 13) & 7);
GalTextureType AType = (GalTextureType)((Tic[0] >> 16) & 7);
GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f);
return ImageUtils.ConvertTexture(Format, RType, GType, BType, AType);
}
private static int[] ReadWords(NvGpuVmm Vmm, long Position, int Count)
{
int[] Words = new int[Count];
for (int Index = 0; Index < Count; Index++, Position += 4)
{
Words[Index] = Vmm.ReadInt32(Position);
}
return Words;
}
}
}

View file

@ -0,0 +1,45 @@
using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Memory;
using System;
namespace Ryujinx.Graphics.Texture
{
static class TextureHelper
{
public static ISwizzle GetSwizzle(TextureInfo Texture, int BlockWidth, int Bpp)
{
int Width = (Texture.Width + (BlockWidth - 1)) / BlockWidth;
int AlignMask = Texture.TileWidth * (64 / Bpp) - 1;
Width = (Width + AlignMask) & ~AlignMask;
switch (Texture.Swizzle)
{
case TextureSwizzle._1dBuffer:
case TextureSwizzle.Pitch:
case TextureSwizzle.PitchColorKey:
return new LinearSwizzle(Texture.Pitch, Bpp);
case TextureSwizzle.BlockLinear:
case TextureSwizzle.BlockLinearColorKey:
return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight);
}
throw new NotImplementedException(Texture.Swizzle.ToString());
}
public static (AMemory Memory, long Position) GetMemoryAndPosition(
IAMemory Memory,
long Position)
{
if (Memory is NvGpuVmm Vmm)
{
return (Vmm.Memory, Vmm.GetPhysicalAddress(Position));
}
return ((AMemory)Memory, Position);
}
}
}

View file

@ -0,0 +1,60 @@
using Ryujinx.Graphics.Gal;
namespace Ryujinx.Graphics.Texture
{
public struct TextureInfo
{
public long Position { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public int Pitch { get; private set; }
public int BlockHeight { get; private set; }
public int TileWidth { get; private set; }
public TextureSwizzle Swizzle { get; private set; }
public GalImageFormat Format { get; private set; }
public TextureInfo(
long Position,
int Width,
int Height)
{
this.Position = Position;
this.Width = Width;
this.Height = Height;
Pitch = 0;
BlockHeight = 16;
TileWidth = 1;
Swizzle = TextureSwizzle.BlockLinear;
Format = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm;
}
public TextureInfo(
long Position,
int Width,
int Height,
int Pitch,
int BlockHeight,
int TileWidth,
TextureSwizzle Swizzle,
GalImageFormat Format)
{
this.Position = Position;
this.Width = Width;
this.Height = Height;
this.Pitch = Pitch;
this.BlockHeight = BlockHeight;
this.TileWidth = TileWidth;
this.Swizzle = Swizzle;
this.Format = Format;
}
}
}

View file

@ -0,0 +1,398 @@
using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal;
using System;
namespace Ryujinx.Graphics.Texture
{
delegate byte[] TextureReaderDelegate(IAMemory Memory, TextureInfo Texture);
public static class TextureReader
{
public static byte[] Read(IAMemory Memory, TextureInfo Texture)
{
TextureReaderDelegate Reader = ImageUtils.GetReader(Texture.Format);
return Reader(Memory, Texture);
}
internal unsafe static byte[] Read1Bpp(IAMemory Memory, TextureInfo Texture)
{
int Width = Texture.Width;
int Height = Texture.Height;
byte[] Output = new byte[Width * Height];
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 1);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Output)
{
long OutOffs = 0;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
byte Pixel = CpuMem.ReadByte(Position + Offset);
*(BuffPtr + OutOffs) = Pixel;
OutOffs++;
}
}
return Output;
}
internal unsafe static byte[] Read5551(IAMemory Memory, TextureInfo Texture)
{
int Width = Texture.Width;
int Height = Texture.Height;
byte[] Output = new byte[Width * Height * 2];
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 2);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Output)
{
long OutOffs = 0;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
uint Pixel = (uint)CpuMem.ReadInt16(Position + Offset);
Pixel = (Pixel & 0x001f) << 11 |
(Pixel & 0x03e0) << 1 |
(Pixel & 0x7c00) >> 9 |
(Pixel & 0x8000) >> 15;
*(short*)(BuffPtr + OutOffs) = (short)Pixel;
OutOffs += 2;
}
}
return Output;
}
internal unsafe static byte[] Read565(IAMemory Memory, TextureInfo Texture)
{
int Width = Texture.Width;
int Height = Texture.Height;
byte[] Output = new byte[Width * Height * 2];
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 2);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Output)
{
long OutOffs = 0;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
uint Pixel = (uint)CpuMem.ReadInt16(Position + Offset);
Pixel = (Pixel & 0x001f) << 11 |
(Pixel & 0x07e0) |
(Pixel & 0xf800) >> 11;
*(short*)(BuffPtr + OutOffs) = (short)Pixel;
OutOffs += 2;
}
}
return Output;
}
internal unsafe static byte[] Read2Bpp(IAMemory Memory, TextureInfo Texture)
{
int Width = Texture.Width;
int Height = Texture.Height;
byte[] Output = new byte[Width * Height * 2];
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 2);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Output)
{
long OutOffs = 0;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
short Pixel = CpuMem.ReadInt16(Position + Offset);
*(short*)(BuffPtr + OutOffs) = Pixel;
OutOffs += 2;
}
}
return Output;
}
internal unsafe static byte[] Read4Bpp(IAMemory Memory, TextureInfo Texture)
{
int Width = Texture.Width;
int Height = Texture.Height;
byte[] Output = new byte[Width * Height * 4];
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 4);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Output)
{
long OutOffs = 0;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
int Pixel = CpuMem.ReadInt32(Position + Offset);
*(int*)(BuffPtr + OutOffs) = Pixel;
OutOffs += 4;
}
}
return Output;
}
internal unsafe static byte[] Read8Bpp(IAMemory Memory, TextureInfo Texture)
{
int Width = Texture.Width;
int Height = Texture.Height;
byte[] Output = new byte[Width * Height * 8];
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 8);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Output)
{
long OutOffs = 0;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
long Pixel = CpuMem.ReadInt64(Position + Offset);
*(long*)(BuffPtr + OutOffs) = Pixel;
OutOffs += 8;
}
}
return Output;
}
internal unsafe static byte[] Read16Bpp(IAMemory Memory, TextureInfo Texture)
{
int Width = Texture.Width;
int Height = Texture.Height;
byte[] Output = new byte[Width * Height * 16];
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 16);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Output)
{
long OutOffs = 0;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
long PxLow = CpuMem.ReadInt64(Position + Offset + 0);
long PxHigh = CpuMem.ReadInt64(Position + Offset + 8);
*(long*)(BuffPtr + OutOffs + 0) = PxLow;
*(long*)(BuffPtr + OutOffs + 8) = PxHigh;
OutOffs += 16;
}
}
return Output;
}
internal unsafe static byte[] Read8Bpt4x4(IAMemory Memory, TextureInfo Texture)
{
int Width = (Texture.Width + 3) / 4;
int Height = (Texture.Height + 3) / 4;
byte[] Output = new byte[Width * Height * 8];
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 4, 8);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Output)
{
long OutOffs = 0;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
long Tile = CpuMem.ReadInt64(Position + Offset);
*(long*)(BuffPtr + OutOffs) = Tile;
OutOffs += 8;
}
}
return Output;
}
internal unsafe static byte[] Read16BptCompressedTexture(IAMemory Memory, TextureInfo Texture, int BlockWidth, int BlockHeight)
{
int Width = (Texture.Width + (BlockWidth - 1)) / BlockWidth;
int Height = (Texture.Height + (BlockHeight - 1)) / BlockHeight;
byte[] Output = new byte[Width * Height * 16];
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, BlockWidth, 16);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Output)
{
long OutOffs = 0;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
long Tile0 = CpuMem.ReadInt64(Position + Offset + 0);
long Tile1 = CpuMem.ReadInt64(Position + Offset + 8);
*(long*)(BuffPtr + OutOffs + 0) = Tile0;
*(long*)(BuffPtr + OutOffs + 8) = Tile1;
OutOffs += 16;
}
}
return Output;
}
internal static byte[] Read16BptCompressedTexture4x4(IAMemory Memory, TextureInfo Texture)
{
return Read16BptCompressedTexture(Memory, Texture, 4, 4);
}
internal static byte[] Read16BptCompressedTexture5x5(IAMemory Memory, TextureInfo Texture)
{
return Read16BptCompressedTexture(Memory, Texture, 5, 5);
}
internal static byte[] Read16BptCompressedTexture6x6(IAMemory Memory, TextureInfo Texture)
{
return Read16BptCompressedTexture(Memory, Texture, 6, 6);
}
internal static byte[] Read16BptCompressedTexture8x8(IAMemory Memory, TextureInfo Texture)
{
return Read16BptCompressedTexture(Memory, Texture, 8, 8);
}
internal static byte[] Read16BptCompressedTexture10x10(IAMemory Memory, TextureInfo Texture)
{
return Read16BptCompressedTexture(Memory, Texture, 10, 10);
}
internal static byte[] Read16BptCompressedTexture12x12(IAMemory Memory, TextureInfo Texture)
{
return Read16BptCompressedTexture(Memory, Texture, 12, 12);
}
internal static byte[] Read16BptCompressedTexture5x4(IAMemory Memory, TextureInfo Texture)
{
return Read16BptCompressedTexture(Memory, Texture, 5, 4);
}
internal static byte[] Read16BptCompressedTexture6x5(IAMemory Memory, TextureInfo Texture)
{
return Read16BptCompressedTexture(Memory, Texture, 6, 5);
}
internal static byte[] Read16BptCompressedTexture8x6(IAMemory Memory, TextureInfo Texture)
{
return Read16BptCompressedTexture(Memory, Texture, 8, 6);
}
internal static byte[] Read16BptCompressedTexture10x8(IAMemory Memory, TextureInfo Texture)
{
return Read16BptCompressedTexture(Memory, Texture, 10, 8);
}
internal static byte[] Read16BptCompressedTexture12x10(IAMemory Memory, TextureInfo Texture)
{
return Read16BptCompressedTexture(Memory, Texture, 12, 10);
}
internal static byte[] Read16BptCompressedTexture8x5(IAMemory Memory, TextureInfo Texture)
{
return Read16BptCompressedTexture(Memory, Texture, 5, 5);
}
internal static byte[] Read16BptCompressedTexture10x5(IAMemory Memory, TextureInfo Texture)
{
return Read16BptCompressedTexture(Memory, Texture, 10, 5);
}
internal static byte[] Read16BptCompressedTexture10x6(IAMemory Memory, TextureInfo Texture)
{
return Read16BptCompressedTexture(Memory, Texture, 10, 6);
}
}
}

View file

@ -0,0 +1,11 @@
namespace Ryujinx.Graphics.Texture
{
public enum TextureSwizzle
{
_1dBuffer = 0,
PitchColorKey = 1,
Pitch = 2,
BlockLinear = 3,
BlockLinearColorKey = 4
}
}

View file

@ -0,0 +1,35 @@
using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Memory;
namespace Ryujinx.Graphics.Texture
{
static class TextureWriter
{
public unsafe static void Write(IAMemory Memory, TextureInfo Texture, byte[] Data)
{
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 4);
(AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
Memory,
Texture.Position);
fixed (byte* BuffPtr = Data)
{
long InOffs = 0;
for (int Y = 0; Y < Texture.Height; Y++)
for (int X = 0; X < Texture.Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
int Pixel = *(int*)(BuffPtr + InOffs);
CpuMem.WriteInt32(Position + Offset, Pixel);
InOffs += 4;
}
}
}
}
}