diff options
| author | emmauss <emmausssss@gmail.com> | 2018-02-20 22:09:23 +0200 |
|---|---|---|
| committer | gdkchan <gab.dark.100@gmail.com> | 2018-02-20 17:09:23 -0300 |
| commit | 62b827f474f0aa2152dd339fcc7cf31084e16a0b (patch) | |
| tree | 0e5c55b341aee4db0ccb841a084f253ec5e05657 /Ryujinx.Graphics | |
| parent | cb665bb715834526d73c9469d16114b287faaecd (diff) | |
Split main project into core,graphics and chocolarm4 subproject (#29)
Diffstat (limited to 'Ryujinx.Graphics')
| -rw-r--r-- | Ryujinx.Graphics/Gal/GalPrimitiveType.cs | 21 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/GalVertexAttrib.cs | 33 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/GalVertexAttribSize.cs | 20 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/GalVertexAttribType.cs | 13 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/IGalRenderer.cs | 17 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs | 282 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gpu/BCn.cs | 468 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gpu/NsGpu.cs | 53 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gpu/NsGpuEngine.cs | 13 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs | 204 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs | 79 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gpu/NsGpuPGraph.cs | 276 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gpu/NsGpuRegister.cs | 93 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gpu/NsGpuTexture.cs | 10 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gpu/NsGpuTextureFormat.cs | 9 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gpu/SwizzleAddr.cs | 144 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Ryujinx.Graphics.csproj | 15 |
17 files changed, 1750 insertions, 0 deletions
diff --git a/Ryujinx.Graphics/Gal/GalPrimitiveType.cs b/Ryujinx.Graphics/Gal/GalPrimitiveType.cs new file mode 100644 index 00000000..ce084149 --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalPrimitiveType.cs @@ -0,0 +1,21 @@ +namespace Ryujinx.Graphics.Gal +{ + public enum GalPrimitiveType + { + Points = 0x0, + Lines = 0x1, + LineLoop = 0x2, + LineStrip = 0x3, + Triangles = 0x4, + TriangleStrip = 0x5, + TriangleFan = 0x6, + Quads = 0x7, + QuadStrip = 0x8, + Polygon = 0x9, + LinesAdjacency = 0xa, + LineStripAdjacency = 0xb, + TrianglesAdjacency = 0xc, + TriangleStripAdjacency = 0xd, + Patches = 0xe + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs new file mode 100644 index 00000000..dc38c593 --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs @@ -0,0 +1,33 @@ +namespace Ryujinx.Graphics.Gal +{ + public struct GalVertexAttrib + { + public int Index { get; private set; } + public int Buffer { get; private set; } + public bool IsConst { get; private set; } + public int Offset { get; private set; } + + public GalVertexAttribSize Size { get; private set; } + public GalVertexAttribType Type { get; private set; } + + public bool IsBgra { get; private set; } + + public GalVertexAttrib( + int Index, + int Buffer, + bool IsConst, + int Offset, + GalVertexAttribSize Size, + GalVertexAttribType Type, + bool IsBgra) + { + this.Index = Index; + this.Buffer = Buffer; + this.IsConst = IsConst; + this.Offset = Offset; + this.Size = Size; + this.Type = Type; + this.IsBgra = IsBgra; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalVertexAttribSize.cs b/Ryujinx.Graphics/Gal/GalVertexAttribSize.cs new file mode 100644 index 00000000..d3ce60ac --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalVertexAttribSize.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.Gal +{ + public enum GalVertexAttribSize + { + _32_32_32_32 = 0x1, + _32_32_32 = 0x2, + _16_16_16_16 = 0x3, + _32_32 = 0x4, + _16_16_16 = 0x5, + _8_8_8_8 = 0xa, + _16_16 = 0xf, + _32 = 0x12, + _8_8_8 = 0x13, + _8_8 = 0x18, + _16 = 0x1b, + _8 = 0x1d, + _10_10_10_2 = 0x30, + _11_11_10 = 0x31 + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalVertexAttribType.cs b/Ryujinx.Graphics/Gal/GalVertexAttribType.cs new file mode 100644 index 00000000..358836fd --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalVertexAttribType.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Gal +{ + public enum GalVertexAttribType + { + Snorm = 1, + Unorm = 2, + Sint = 3, + Uint = 4, + Uscaled = 5, + Sscaled = 6, + Float = 7 + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs new file mode 100644 index 00000000..1870aca5 --- /dev/null +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -0,0 +1,17 @@ +using System; + +namespace Ryujinx.Graphics.Gal +{ + public interface IGalRenderer + { + long FrameBufferPtr { get; set; } + + void QueueAction(Action ActionMthd); + void RunActions(); + + void Render(); + void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs); + void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height); + void BindTexture(int Index); + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs new file mode 100644 index 00000000..7429569b --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -0,0 +1,282 @@ +using OpenTK.Graphics.OpenGL; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + public class OpenGLRenderer : IGalRenderer + { + private struct VertexBuffer + { + public int VaoHandle; + public int VboHandle; + + public int PrimCount; + } + + private struct Texture + { + public int Handle; + } + + private List<VertexBuffer> VertexBuffers; + + private Texture[] Textures; + + private Queue<Action> ActionsQueue; + + public long FrameBufferPtr { get; set; } + + public OpenGLRenderer() + { + VertexBuffers = new List<VertexBuffer>(); + + Textures = new Texture[8]; + + ActionsQueue = new Queue<Action>(); + } + + public void QueueAction(Action ActionMthd) + { + ActionsQueue.Enqueue(ActionMthd); + } + + public void RunActions() + { + while (ActionsQueue.Count > 0) + { + ActionsQueue.Dequeue()(); + } + } + + public void Render() + { + for (int Index = 0; Index < VertexBuffers.Count; Index++) + { + VertexBuffer Vb = VertexBuffers[Index]; + + if (Vb.VaoHandle != 0 && + Vb.PrimCount != 0) + { + GL.BindVertexArray(Vb.VaoHandle); + GL.DrawArrays(PrimitiveType.TriangleStrip, 0, Vb.PrimCount); + } + } + + } + + public void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs) + { + if (Index < 0) + { + throw new ArgumentOutOfRangeException(nameof(Index)); + } + + if (Buffer.Length == 0 || Stride == 0) + { + return; + } + + EnsureVbInitialized(Index); + + VertexBuffer Vb = VertexBuffers[Index]; + + Vb.PrimCount = Buffer.Length / Stride; + + VertexBuffers[Index] = Vb; + + IntPtr Length = new IntPtr(Buffer.Length); + + GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle); + GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw); + GL.BindBuffer(BufferTarget.ArrayBuffer, 0); + + GL.BindVertexArray(Vb.VaoHandle); + + for (int Attr = 0; Attr < 16; Attr++) + { + GL.DisableVertexAttribArray(Attr); + } + + foreach (GalVertexAttrib Attrib in Attribs) + { + if (Attrib.Index >= 3) break; + + GL.EnableVertexAttribArray(Attrib.Index); + + GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle); + + int Size = 0; + + switch (Attrib.Size) + { + case GalVertexAttribSize._8: + case GalVertexAttribSize._16: + case GalVertexAttribSize._32: + Size = 1; + break; + case GalVertexAttribSize._8_8: + case GalVertexAttribSize._16_16: + case GalVertexAttribSize._32_32: + Size = 2; + break; + case GalVertexAttribSize._8_8_8: + case GalVertexAttribSize._11_11_10: + case GalVertexAttribSize._16_16_16: + case GalVertexAttribSize._32_32_32: + Size = 3; + break; + case GalVertexAttribSize._8_8_8_8: + case GalVertexAttribSize._10_10_10_2: + case GalVertexAttribSize._16_16_16_16: + case GalVertexAttribSize._32_32_32_32: + Size = 4; + break; + } + + bool Signed = + Attrib.Type == GalVertexAttribType.Snorm || + Attrib.Type == GalVertexAttribType.Sint || + Attrib.Type == GalVertexAttribType.Sscaled; + + bool Normalize = + Attrib.Type == GalVertexAttribType.Snorm || + Attrib.Type == GalVertexAttribType.Unorm; + + VertexAttribPointerType Type = 0; + + switch (Attrib.Type) + { + case GalVertexAttribType.Snorm: + case GalVertexAttribType.Unorm: + case GalVertexAttribType.Sint: + case GalVertexAttribType.Uint: + case GalVertexAttribType.Uscaled: + case GalVertexAttribType.Sscaled: + { + switch (Attrib.Size) + { + case GalVertexAttribSize._8: + case GalVertexAttribSize._8_8: + case GalVertexAttribSize._8_8_8: + case GalVertexAttribSize._8_8_8_8: + { + Type = Signed + ? VertexAttribPointerType.Byte + : VertexAttribPointerType.UnsignedByte; + + break; + } + + case GalVertexAttribSize._16: + case GalVertexAttribSize._16_16: + case GalVertexAttribSize._16_16_16: + case GalVertexAttribSize._16_16_16_16: + { + Type = Signed + ? VertexAttribPointerType.Short + : VertexAttribPointerType.UnsignedShort; + + break; + } + + case GalVertexAttribSize._10_10_10_2: + case GalVertexAttribSize._11_11_10: + case GalVertexAttribSize._32: + case GalVertexAttribSize._32_32: + case GalVertexAttribSize._32_32_32: + case GalVertexAttribSize._32_32_32_32: + { + Type = Signed + ? VertexAttribPointerType.Int + : VertexAttribPointerType.UnsignedInt; + + break; + } + } + + break; + } + + case GalVertexAttribType.Float: + { + Type = VertexAttribPointerType.Float; + + break; + } + } + + GL.VertexAttribPointer( + Attrib.Index, + Size, + Type, + Normalize, + Stride, + Attrib.Offset); + } + + GL.BindVertexArray(0); + } + + public void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height) + { + EnsureTexInitialized(Index); + + GL.BindTexture(TextureTarget.Texture2D, Textures[Index].Handle); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); + GL.TexImage2D(TextureTarget.Texture2D, + 0, + PixelInternalFormat.Rgba, + Width, + Height, + 0, + PixelFormat.Rgba, + PixelType.UnsignedByte, + Buffer); + } + + public void BindTexture(int Index) + { + GL.ActiveTexture(TextureUnit.Texture0 + Index); + + GL.BindTexture(TextureTarget.Texture2D, Textures[Index].Handle); + } + + private void EnsureVbInitialized(int VbIndex) + { + while (VbIndex >= VertexBuffers.Count) + { + VertexBuffers.Add(new VertexBuffer()); + } + + VertexBuffer Vb = VertexBuffers[VbIndex]; + + if (Vb.VaoHandle == 0) + { + Vb.VaoHandle = GL.GenVertexArray(); + } + + if (Vb.VboHandle == 0) + { + Vb.VboHandle = GL.GenBuffer(); + } + + VertexBuffers[VbIndex] = Vb; + } + + private void EnsureTexInitialized(int TexIndex) + { + Texture Tex = Textures[TexIndex]; + + if (Tex.Handle == 0) + { + Tex.Handle = GL.GenTexture(); + } + + Textures[TexIndex] = Tex; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/BCn.cs b/Ryujinx.Graphics/Gpu/BCn.cs new file mode 100644 index 00000000..b1caf467 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/BCn.cs @@ -0,0 +1,468 @@ +using System; +using System.Drawing; + +namespace Ryujinx.Graphics.Gpu +{ + static class BCn + { + public static byte[] DecodeBC1(NsGpuTexture Tex, int Offset) + { + int W = (Tex.Width + 3) / 4; + int H = (Tex.Height + 3) / 4; + + byte[] Output = new byte[W * H * 64]; + + SwizzleAddr Swizzle = new SwizzleAddr(W, H, 8); + + for (int Y = 0; Y < H; Y++) + { + for (int X = 0; X < W; X++) + { + int IOffs = Offset + Swizzle.GetSwizzledAddress64(X, Y) * 8; + + byte[] Tile = BCnDecodeTile(Tex.Data, IOffs, true); + + int TOffset = 0; + + for (int TY = 0; TY < 4; TY++) + { + for (int TX = 0; TX < 4; TX++) + { + int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4; + + Output[OOffset + 0] = Tile[TOffset + 0]; + Output[OOffset + 1] = Tile[TOffset + 1]; + Output[OOffset + 2] = Tile[TOffset + 2]; + Output[OOffset + 3] = Tile[TOffset + 3]; + + TOffset += 4; + } + } + } + } + + return Output; + } + + public static byte[] DecodeBC2(NsGpuTexture Tex, int Offset) + { + int W = (Tex.Width + 3) / 4; + int H = (Tex.Height + 3) / 4; + + byte[] Output = new byte[W * H * 64]; + + SwizzleAddr Swizzle = new SwizzleAddr(W, H, 4); + + for (int Y = 0; Y < H; Y++) + { + for (int X = 0; X < W; X++) + { + int IOffs = Offset + Swizzle.GetSwizzledAddress128(X, Y) * 16; + + byte[] Tile = BCnDecodeTile(Tex.Data, IOffs + 8, false); + + int AlphaLow = Get32(Tex.Data, IOffs + 0); + int AlphaHigh = Get32(Tex.Data, IOffs + 4); + + ulong AlphaCh = (uint)AlphaLow | (ulong)AlphaHigh << 32; + + int TOffset = 0; + + for (int TY = 0; TY < 4; TY++) + { + for (int TX = 0; TX < 4; TX++) + { + ulong Alpha = (AlphaCh >> (TY * 16 + TX * 4)) & 0xf; + + int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4; + + Output[OOffset + 0] = Tile[TOffset + 0]; + Output[OOffset + 1] = Tile[TOffset + 1]; + Output[OOffset + 2] = Tile[TOffset + 2]; + Output[OOffset + 3] = (byte)(Alpha | (Alpha << 4)); + + TOffset += 4; + } + } + } + } + + return Output; + } + + public static byte[] DecodeBC3(NsGpuTexture Tex, int Offset) + { + int W = (Tex.Width + 3) / 4; + int H = (Tex.Height + 3) / 4; + + byte[] Output = new byte[W * H * 64]; + + SwizzleAddr Swizzle = new SwizzleAddr(W, H, 4); + + for (int Y = 0; Y < H; Y++) + { + for (int X = 0; X < W; X++) + { + int IOffs = Offset + Swizzle.GetSwizzledAddress128(X, Y) * 16; + + byte[] Tile = BCnDecodeTile(Tex.Data, IOffs + 8, false); + + byte[] Alpha = new byte[8]; + + Alpha[0] = Tex.Data[IOffs + 0]; + Alpha[1] = Tex.Data[IOffs + 1]; + + CalculateBC3Alpha(Alpha); + + int AlphaLow = Get32(Tex.Data, IOffs + 2); + int AlphaHigh = Get16(Tex.Data, IOffs + 6); + + ulong AlphaCh = (uint)AlphaLow | (ulong)AlphaHigh << 32; + + int TOffset = 0; + + for (int TY = 0; TY < 4; TY++) + { + for (int TX = 0; TX < 4; TX++) + { + int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4; + + byte AlphaPx = Alpha[(AlphaCh >> (TY * 12 + TX * 3)) & 7]; + + Output[OOffset + 0] = Tile[TOffset + 0]; + Output[OOffset + 1] = Tile[TOffset + 1]; + Output[OOffset + 2] = Tile[TOffset + 2]; + Output[OOffset + 3] = AlphaPx; + + TOffset += 4; + } + } + } + } + + return Output; + } + + public static byte[] DecodeBC4(NsGpuTexture Tex, int Offset) + { + int W = (Tex.Width + 3) / 4; + int H = (Tex.Height + 3) / 4; + + byte[] Output = new byte[W * H * 64]; + + SwizzleAddr Swizzle = new SwizzleAddr(W, H, 8); + + for (int Y = 0; Y < H; Y++) + { + for (int X = 0; X < W; X++) + { + int IOffs = Swizzle.GetSwizzledAddress64(X, Y) * 8; + + byte[] Red = new byte[8]; + + Red[0] = Tex.Data[IOffs + 0]; + Red[1] = Tex.Data[IOffs + 1]; + + CalculateBC3Alpha(Red); + + int RedLow = Get32(Tex.Data, IOffs + 2); + int RedHigh = Get16(Tex.Data, IOffs + 6); + + ulong RedCh = (uint)RedLow | (ulong)RedHigh << 32; + + int TOffset = 0; + + for (int TY = 0; TY < 4; TY++) + { + for (int TX = 0; TX < 4; TX++) + { + int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4; + + byte RedPx = Red[(RedCh >> (TY * 12 + TX * 3)) & 7]; + + Output[OOffset + 0] = RedPx; + Output[OOffset + 1] = RedPx; + Output[OOffset + 2] = RedPx; + Output[OOffset + 3] = 0xff; + + TOffset += 4; + } + } + } + } + + return Output; + } + + public static byte[] DecodeBC5(NsGpuTexture Tex, int Offset, bool SNorm) + { + int W = (Tex.Width + 3) / 4; + int H = (Tex.Height + 3) / 4; + + byte[] Output = new byte[W * H * 64]; + + SwizzleAddr Swizzle = new SwizzleAddr(W, H, 4); + + for (int Y = 0; Y < H; Y++) + { + for (int X = 0; X < W; X++) + { + int IOffs = Swizzle.GetSwizzledAddress128(X, Y) * 16; + + byte[] Red = new byte[8]; + byte[] Green = new byte[8]; + + Red[0] = Tex.Data[IOffs + 0]; + Red[1] = Tex.Data[IOffs + 1]; + + Green[0] = Tex.Data[IOffs + 8]; + Green[1] = Tex.Data[IOffs + 9]; + + if (SNorm) + { + CalculateBC3AlphaS(Red); + CalculateBC3AlphaS(Green); + } + else + { + CalculateBC3Alpha(Red); + CalculateBC3Alpha(Green); + } + + int RedLow = Get32(Tex.Data, IOffs + 2); + int RedHigh = Get16(Tex.Data, IOffs + 6); + + int GreenLow = Get32(Tex.Data, IOffs + 10); + int GreenHigh = Get16(Tex.Data, IOffs + 14); + + ulong RedCh = (uint)RedLow | (ulong)RedHigh << 32; + ulong GreenCh = (uint)GreenLow | (ulong)GreenHigh << 32; + + int TOffset = 0; + + if (SNorm) + { + for (int TY = 0; TY < 4; TY++) + { + for (int TX = 0; TX < 4; TX++) + { + int Shift = TY * 12 + TX * 3; + + int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4; + + byte RedPx = Red [(RedCh >> Shift) & 7]; + byte GreenPx = Green[(GreenCh >> Shift) & 7]; + + RedPx += 0x80; + GreenPx += 0x80; + + float NX = (RedPx / 255f) * 2 - 1; + float NY = (GreenPx / 255f) * 2 - 1; + + float NZ = (float)Math.Sqrt(1 - (NX * NX + NY * NY)); + + Output[OOffset + 0] = Clamp((NZ + 1) * 0.5f); + Output[OOffset + 1] = Clamp((NY + 1) * 0.5f); + Output[OOffset + 2] = Clamp((NX + 1) * 0.5f); + Output[OOffset + 3] = 0xff; + + TOffset += 4; + } + } + } + else + { + for (int TY = 0; TY < 4; TY++) + { + for (int TX = 0; TX < 4; TX++) + { + int Shift = TY * 12 + TX * 3; + + int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4; + + byte RedPx = Red [(RedCh >> Shift) & 7]; + byte GreenPx = Green[(GreenCh >> Shift) & 7]; + + Output[OOffset + 0] = RedPx; + Output[OOffset + 1] = RedPx; + Output[OOffset + 2] = RedPx; + Output[OOffset + 3] = GreenPx; + + TOffset += 4; + } + } + } + } + } + + return Output; + } + + private static byte Clamp(float Value) + { + if (Value > 1) + { + return 0xff; + } + else if (Value < 0) + { + return 0; + } + else + { + return (byte)(Value * 0xff); + } + } + + private static void CalculateBC3Alpha(byte[] Alpha) + { + for (int i = 2; i < 8; i++) + { + if (Alpha[0] > Alpha[1]) + { + Alpha[i] = (byte)(((8 - i) * Alpha[0] + (i - 1) * Alpha[1]) / 7); + } + else if (i < 6) + { + Alpha[i] = (byte)(((6 - i) * Alpha[0] + (i - 1) * Alpha[1]) / 7); + } + else if (i == 6) + { + Alpha[i] = 0; + } + else /* i == 7 */ + { + Alpha[i] = 0xff; + } + } + } + + private static void CalculateBC3AlphaS(byte[] Alpha) + { + for (int i = 2; i < 8; i++) + { + if ((sbyte)Alpha[0] > (sbyte)Alpha[1]) + { + Alpha[i] = (byte)(((8 - i) * (sbyte)Alpha[0] + (i - 1) * (sbyte)Alpha[1]) / 7); + } + else if (i < 6) + { + Alpha[i] = (byte)(((6 - i) * (sbyte)Alpha[0] + (i - 1) * (sbyte)Alpha[1]) / 7); + } + else if (i == 6) + { + Alpha[i] = 0x80; + } + else /* i == 7 */ + { + Alpha[i] = 0x7f; + } + } + } + + private static byte[] BCnDecodeTile( + byte[] Input, + int Offset, + bool IsBC1) + { + Color[] CLUT = new Color[4]; + + int c0 = Get16(Input, Offset + 0); + int c1 = Get16(Input, Offset + 2); + + CLUT[0] = DecodeRGB565(c0); + CLUT[1] = DecodeRGB565(c1); + CLUT[2] = CalculateCLUT2(CLUT[0], CLUT[1], c0, c1, IsBC1); + CLUT[3] = CalculateCLUT3(CLUT[0], CLUT[1], c0, c1, IsBC1); + + int Indices = Get32(Input, Offset + 4); + + int IdxShift = 0; + + byte[] Output = new byte[4 * 4 * 4]; + + int OOffset = 0; + + for (int TY = 0; TY < 4; TY++) + { + for (int TX = 0; TX < 4; TX++) + { + int Idx = (Indices >> IdxShift) & 3; + + IdxShift += 2; + + Color Pixel = CLUT[Idx]; + + Output[OOffset + 0] = Pixel.R; + Output[OOffset + 1] = Pixel.G; + Output[OOffset + 2] = Pixel.B; + Output[OOffset + 3] = Pixel.A; + + OOffset += 4; + } + } + + return Output; + } + + private static Color CalculateCLUT2(Color C0, Color C1, int c0, int c1, bool IsBC1) + { + if (c0 > c1 || !IsBC1) + { + return Color.FromArgb( + (2 * C0.R + C1.R) / 3, + (2 * C0.G + C1.G) / 3, + (2 * C0.B + C1.B) / 3); + } + else + { + return Color.FromArgb( + (C0.R + C1.R) / 2, + (C0.G + C1.G) / 2, + (C0.B + C1.B) / 2); + } + } + + private static Color CalculateCLUT3(Color C0, Color C1, int c0, int c1, bool IsBC1) + { + if (c0 > c1 || !IsBC1) + { + return + Color.FromArgb( + (2 * C1.R + C0.R) / 3, + (2 * C1.G + C0.G) / 3, + (2 * C1.B + C0.B) / 3); + } + + return Color.Transparent; + } + + private static Color DecodeRGB565(int Value) + { + int B = ((Value >> 0) & 0x1f) << 3; + int G = ((Value >> 5) & 0x3f) << 2; + int R = ((Value >> 11) & 0x1f) << 3; + + return Color.FromArgb( + R | (R >> 5), + G | (G >> 6), + B | (B >> 5)); + } + + private static int Get16(byte[] Data, int Address) + { + return + Data[Address + 0] << 0 | + Data[Address + 1] << 8; + } + + private static int Get32(byte[] Data, int Address) + { + return + Data[Address + 0] << 0 | + Data[Address + 1] << 8 | + Data[Address + 2] << 16 | + Data[Address + 3] << 24; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpu.cs b/Ryujinx.Graphics/Gpu/NsGpu.cs new file mode 100644 index 00000000..133d0af2 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NsGpu.cs @@ -0,0 +1,53 @@ +using ChocolArm64.Memory; +using Ryujinx.Graphics.Gal; + +namespace Ryujinx.Graphics.Gpu +{ + public class NsGpu + { + public IGalRenderer Renderer { get; private set; } + + internal NsGpuMemoryMgr MemoryMgr { get; private set; } + + internal NsGpuPGraph PGraph { get; private set; } + + public NsGpu(IGalRenderer Renderer) + { + this.Renderer = Renderer; + + MemoryMgr = new NsGpuMemoryMgr(); + + PGraph = new NsGpuPGraph(this); + } + + public long GetCpuAddr(long Position) + { + return MemoryMgr.GetCpuAddr(Position); + } + + public long MapMemory(long CpuAddr, long Size) + { + return MemoryMgr.Map(CpuAddr, Size); + } + + public long MapMemory(long CpuAddr, long GpuAddr, long Size) + { + return MemoryMgr.Map(CpuAddr, GpuAddr, Size); + } + + public void ProcessPushBuffer(NsGpuPBEntry[] PushBuffer, AMemory Memory) + { + PGraph.ProcessPushBuffer(PushBuffer, Memory); + } + + public long ReserveMemory(long Size, long Align) + { + return MemoryMgr.Reserve(Size, Align); + } + + public long ReserveMemory(long GpuAddr, long Size, long Align) + { + return MemoryMgr.Reserve(GpuAddr, Size, Align); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpuEngine.cs b/Ryujinx.Graphics/Gpu/NsGpuEngine.cs new file mode 100644 index 00000000..118e2b72 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NsGpuEngine.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Gpu +{ + enum NsGpuEngine + { + None = 0, + _2d = 0x902d, + _3d = 0xb197, + Compute = 0xb1c0, + Kepler = 0xa140, + Dma = 0xb0b5, + GpFifo = 0xb06f + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs b/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs new file mode 100644 index 00000000..54fabc67 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs @@ -0,0 +1,204 @@ +namespace Ryujinx.Graphics.Gpu +{ + class NsGpuMemoryMgr + { + private const long AddrSize = 1L << 40; + + private const int PTLvl0Bits = 14; + private const int PTLvl1Bits = 14; + private const int PTPageBits = 12; + + private const int PTLvl0Size = 1 << PTLvl0Bits; + private const int PTLvl1Size = 1 << PTLvl1Bits; + private const int PageSize = 1 << PTPageBits; + + private const int PTLvl0Mask = PTLvl0Size - 1; + private const int PTLvl1Mask = PTLvl1Size - 1; + private const int PageMask = PageSize - 1; + + private const int PTLvl0Bit = PTPageBits + PTLvl0Bits; + private const int PTLvl1Bit = PTPageBits; + + private const long PteUnmapped = -1; + private const long PteReserved = -2; + + private long[][] PageTable; + + public NsGpuMemoryMgr() + { + PageTable = new long[PTLvl0Size][]; + } + + public long Map(long CpuAddr, long GpuAddr, long Size) + { + CpuAddr &= ~PageMask; + GpuAddr &= ~PageMask; + + for (long Offset = 0; Offset < Size; Offset += PageSize) + { + if (GetPTAddr(GpuAddr + Offset) != PteReserved) + { + return Map(CpuAddr, Size); + } + } + + for (long Offset = 0; Offset < Size; Offset += PageSize) + { + SetPTAddr(GpuAddr + Offset, CpuAddr + Offset); + } + + return GpuAddr; + } + + public long Map(long CpuAddr, long Size) + { + CpuAddr &= ~PageMask; + + long Position = GetFreePosition(Size); + + if (Position != -1) + { + for (long Offset = 0; Offset < Size; Offset += PageSize) + { + SetPTAddr(Position + Offset, CpuAddr + Offset); + } + } + + return Position; + } + + public long Reserve(long GpuAddr, long Size, long Align) + { + for (long Offset = 0; Offset < Size; Offset += PageSize) + { + if (HasPTAddr(GpuAddr + Offset)) + { + return Reserve(Size, Align); + } + } + + for (long Offset = 0; Offset < Size; Offset += PageSize) + { + SetPTAddr(GpuAddr + Offset, PteReserved); + } + + return GpuAddr; + } + + public long Reserve(long Size, long Align) + { + long Position = GetFreePosition(Size, Align); + + if (Position != -1) + { + for (long Offset = 0; Offset < Size; Offset += PageSize) + { + SetPTAddr(Position + Offset, PteReserved); + } + } + + return Position; + } + + private long GetFreePosition(long Size, long Align = 1) + { + long Position = 0; + long FreeSize = 0; + + if (Align < 1) + { + Align = 1; + } + + Align = (Align + PageMask) & ~PageMask; + + while (Position + FreeSize < AddrSize) + { + if (!HasPTAddr(Position + FreeSize)) + { + FreeSize += PageSize; + + if (FreeSize >= Size) + { + return Position; + } + } + else + { + Position += FreeSize + PageSize; + FreeSize = 0; + + long Remainder = Position % Align; + + if (Remainder != 0) + { + Position = (Position - Remainder) + Align; + } + } + } + + return -1; + } + + public long GetCpuAddr(long Position) + { + long BasePos = GetPTAddr(Position); + + if (BasePos < 0) + { + return -1; + } + + return BasePos + (Position & PageMask); + } + + private bool HasPTAddr(long Position) + { + if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0) + { + return false; + } + + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; + long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + + if (PageTable[L0] == null) + { + return false; + } + + return PageTable[L0][L1] != PteUnmapped; + } + + private long GetPTAddr(long Position) + { + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; + long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + + if (PageTable[L0] == null) + { + return -1; + } + + return PageTable[L0][L1]; + } + + private void SetPTAddr(long Position, long TgtAddr) + { + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; + long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + + if (PageTable[L0] == null) + { + PageTable[L0] = new long[PTLvl1Size]; + + for (int Index = 0; Index < PTLvl1Size; Index++) + { + PageTable[L0][Index] = PteUnmapped; + } + } + + PageTable[L0][L1] = TgtAddr; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs b/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs new file mode 100644 index 00000000..8063651a --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; + +namespace Ryujinx.Graphics.Gpu +{ + public struct NsGpuPBEntry + { + public NsGpuRegister Register { get; private set; } + + public int SubChannel { get; private set; } + + private int[] m_Arguments; + + public ReadOnlyCollection<int> Arguments => Array.AsReadOnly(m_Arguments); + + public NsGpuPBEntry(NsGpuRegister Register, int SubChannel, params int[] Arguments) + { + this.Register = Register; + this.SubChannel = SubChannel; + this.m_Arguments = Arguments; + } + + public static NsGpuPBEntry[] DecodePushBuffer(byte[] Data) + { + using (MemoryStream MS = new MemoryStream(Data)) + { + BinaryReader Reader = new BinaryReader(MS); + + List<NsGpuPBEntry> GpFifos = new List<NsGpuPBEntry>(); + + bool CanRead() => MS.Position + 4 <= MS.Length; + + while (CanRead()) + { + int Packed = Reader.ReadInt32(); + + int Reg = (Packed << 2) & 0x7ffc; + int SubC = (Packed >> 13) & 7; + int Args = (Packed >> 16) & 0x1fff; + int Mode = (Packed >> 29) & 7; + + if (Mode == 4) + { + //Inline Mode. + GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Args)); + } + else + { + //Word mode. + if (Mode == 1) + { + //Sequential Mode. + for (int Index = 0; Index < Args && CanRead(); Index++, Reg += 4) + { + GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Reader.ReadInt32())); + } + } + else + { + //Non-Sequential Mode. + int[] Arguments = new int[Args]; + + for (int Index = 0; Index < Args && CanRead(); Index++) + { + Arguments[Index] = Reader.ReadInt32(); + } + + GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Arguments)); + } + } + } + + return GpFifos.ToArray(); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpuPGraph.cs b/Ryujinx.Graphics/Gpu/NsGpuPGraph.cs new file mode 100644 index 00000000..eb893f74 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NsGpuPGraph.cs @@ -0,0 +1,276 @@ +using ChocolArm64.Memory; +using Ryujinx.Graphics.Gal; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu +{ + class NsGpuPGraph + { + private NsGpu Gpu; + + private int[] Registers; + + public NsGpuEngine[] SubChannels; + + private Dictionary<long, int> CurrentVertexBuffers; + + public NsGpuPGraph(NsGpu Gpu) + { + this.Gpu = Gpu; + + Registers = new int[0x1000]; + + SubChannels = new NsGpuEngine[8]; + + CurrentVertexBuffers = new Dictionary<long, int>(); + } + + public void ProcessPushBuffer(NsGpuPBEntry[] PushBuffer, AMemory Memory) + { + bool HasQuery = false; + + foreach (NsGpuPBEntry Entry in PushBuffer) + { + if (Entry.Arguments.Count == 1) + { + SetRegister(Entry.Register, Entry.Arguments[0]); + } + + switch (Entry.Register) + { + case NsGpuRegister.BindChannel: + if (Entry.Arguments.Count > 0) + { + SubChannels[Entry.SubChannel] = (NsGpuEngine)Entry.Arguments[0]; + } + break; + + case NsGpuRegister._3dVertexArray0Fetch: + SendVertexBuffers(Memory); + break; + + case NsGpuRegister._3dCbData0: + if (GetRegister(NsGpuRegister._3dCbPos) == 0x20) + { + SendTexture(Memory); + } + break; + + case NsGpuRegister._3dQueryAddressHigh: + case NsGpuRegister._3dQueryAddressLow: + case NsGpuRegister._3dQuerySequence: + case NsGpuRegister._3dQueryGet: + HasQuery = true; + break; + } + } + + if (HasQuery) + { + long Position = + (long)GetRegister(NsGpuRegister._3dQueryAddressHigh) << 32 | + (long)GetRegister(NsGpuRegister._3dQueryAddressLow) << 0; + + int Seq = GetRegister(NsGpuRegister._3dQuerySequence); + int Get = GetRegister(NsGpuRegister._3dQueryGet); + + int Mode = Get & 3; + + if (Mode == 0) + { + //Write + Position = Gpu.MemoryMgr.GetCpuAddr(Position); + + if (Position != -1) + { + Gpu.Renderer.QueueAction(delegate() + { + Memory.WriteInt32(Position, Seq); + }); + } + } + } + } + + private void SendVertexBuffers(AMemory Memory) + { + long Position = + (long)GetRegister(NsGpuRegister._3dVertexArray0StartHigh) << 32 | + (long)GetRegister(NsGpuRegister._3dVertexArray0StartLow) << 0; + + long Limit = + (long)GetRegister(NsGpuRegister._3dVertexArray0LimitHigh) << 32 | + (long)GetRegister(NsGpuRegister._3dVertexArray0LimitLow) << 0; + + int VbIndex = CurrentVertexBuffers.Count; + + if (!CurrentVertexBuffers.TryAdd(Position, VbIndex)) + { + VbIndex = CurrentVertexBuffers[Position]; + } + + if (Limit != 0) + { + long Size = (Limit - Position) + 1; + + Position = Gpu.MemoryMgr.GetCpuAddr(Position); + + if (Position != -1) + { + byte[] Buffer = AMemoryHelper.ReadBytes(Memory, Position, (int)Size); + + int Stride = GetRegister(NsGpuRegister._3dVertexArray0Fetch) & 0xfff; + + List<GalVertexAttrib> Attribs = new List<GalVertexAttrib>(); + + for (int Attr = 0; Attr < 16; Attr++) + { + int Packed = GetRegister(NsGpuRegister._3dVertexAttrib0Format + Attr * 4); + + GalVertexAttrib Attrib = new GalVertexAttrib(Attr, + (Packed >> 0) & 0x1f, + ((Packed >> 6) & 0x1) != 0, + (Packed >> 7) & 0x3fff, + (GalVertexAttribSize)((Packed >> 21) & 0x3f), + (GalVertexAttribType)((Packed >> 27) & 0x7), + ((Packed >> 31) & 0x1) != 0); + + if (Attrib.Offset < Stride) + { + Attribs.Add(Attrib); + } + } + + Gpu.Renderer.QueueAction(delegate() + { + Gpu.Renderer.SendVertexBuffer(VbIndex, Buffer, Stride, Attribs.ToArray()); + }); + } + } + } + + private void SendTexture(AMemory Memory) + { + long TicPos = (long)GetRegister(NsGpuRegister._3dTicAddressHigh) << 32 | + (long)GetRegister(NsGpuRegister._3dTicAddressLow) << 0; + + int CbData = GetRegister(NsGpuRegister._3dCbData0); + + int TicIndex = (CbData >> 0) & 0xfffff; + int TscIndex = (CbData >> 20) & 0xfff; //I guess? + + TicPos = Gpu.MemoryMgr.GetCpuAddr(TicPos + TicIndex * 0x20); + + if (TicPos != -1) + { + int Word0 = Memory.ReadInt32(TicPos + 0x0); + int Word1 = Memory.ReadInt32(TicPos + 0x4); + int Word2 = Memory.ReadInt32(TicPos + 0x8); + int Word3 = Memory.ReadInt32(TicPos + 0xc); + int Word4 = Memory.ReadInt32(TicPos + 0x10); + int Word5 = Memory.ReadInt32(TicPos + 0x14); + int Word6 = Memory.ReadInt32(TicPos + 0x18); + int Word7 = Memory.ReadInt32(TicPos + 0x1c); + + long TexAddress = Word1; + + TexAddress |= (long)(Word2 & 0xff) << 32; + + TexAddress = Gpu.MemoryMgr.GetCpuAddr(TexAddress); + + if (TexAddress != -1) + { + NsGpuTextureFormat Format = (NsGpuTextureFormat)(Word0 & 0x7f); + + int Width = (Word4 & 0xffff) + 1; + int Height = (Word5 & 0xffff) + 1; + + byte[] Buffer = GetDecodedTexture(Memory, Format, TexAddress, Width, Height); + + if (Buffer != null) + { + Gpu.Renderer.QueueAction(delegate() + { + Gpu.Renderer.SendR8G8B8A8Texture(0, Buffer, Width, Height); + }); + } + } + } + } + + private static byte[] GetDecodedTexture( + AMemory Memory, + NsGpuTextureFormat Format, + long Position, + int Width, + int Height) + { + byte[] Data = null; + + switch (Format) + { + case NsGpuTextureFormat.BC1: + { + int Size = (Width * Height) >> 1; + + Data = AMemoryHelper.ReadBytes(Memory, Position, Size); + + Data = BCn.DecodeBC1(new NsGpuTexture() + { + Width = Width, + Height = Height, + Data = Data + }, 0); + + break; + } + + case NsGpuTextureFormat.BC2: + { + int Size = Width * Height; + + Data = AMemoryHelper.ReadBytes(Memory, Position, Size); + + Data = BCn.DecodeBC2(new NsGpuTexture() + { + Width = Width, + Height = Height, + Data = Data + }, 0); + + break; + } + + case NsGpuTextureFormat.BC3: + { + int Size = Width * Height; + + Data = AMemoryHelper.ReadBytes(Memory, Position, Size); + + Data = BCn.DecodeBC3(new NsGpuTexture() + { + Width = Width, + Height = Height, + Data = Data + }, 0); + + break; + } + + //default: throw new NotImplementedException(Format.ToString()); + } + + return Data; + } + + public int GetRegister(NsGpuRegister Register) + { + return Registers[((int)Register >> 2) & 0xfff]; + } + + public void SetRegister(NsGpuRegister Register, int Value) + { + Registers[((int)Register >> 2) & 0xfff] = Value; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpuRegister.cs b/Ryujinx.Graphics/Gpu/NsGpuRegister.cs new file mode 100644 index 00000000..319e2c01 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NsGpuRegister.cs @@ -0,0 +1,93 @@ +namespace Ryujinx.Graphics.Gpu +{ + public enum NsGpuRegister + { + BindChannel = 0, + + _2dClipEnable = 0x0290, + _2dOperation = 0x02ac, + + _3dGlobalBase = 0x02c8, + _3dRt0AddressHigh = 0x0800, + _3dRt0AddressLow = 0x0804, + _3dRt0Horiz = 0x0808, + _3dRt0Vert = 0x080c, + _3dRt0Format = 0x0810, + _3dRt0BlockDimensions = 0x0814, + _3dRt0ArrayMode = 0x0818, + _3dRt0LayerStride = 0x081c, + _3dRt0BaseLayer = 0x0820, + _3dViewportScaleX = 0x0a00, + _3dViewportScaleY = 0x0a04, + _3dViewportScaleZ = 0x0a08, + _3dViewportTranslateX = 0x0a0c, + _3dViewportTranslateY = 0x0a10, + _3dViewportTranslateZ = 0x0a14, + _3dViewportHoriz = 0x0c00, + _3dViewportVert = 0x0c04, + _3dDepthRangeNear = 0x0c08, + _3dDepthRangeFar = 0x0c0c, + _3dClearColorR = 0x0d80, + _3dClearColorG = 0x0d84, + _3dClearColorB = 0x0d88, + _3dClearColorA = 0x0d8c, + _3dScreenScissorHoriz = 0x0ff4, + _3dScreenScissorVert = 0x0ff8, + _3dVertexAttrib0Format = 0x1160, + _3dVertexAttrib1Format = 0x1164, + _3dVertexAttrib2Format = 0x1168, + _3dVertexAttrib3Format = 0x116c, + _3dVertexAttrib4Format = 0x1170, + _3dVertexAttrib5Format = 0x1174, + _3dVertexAttrib6Format = 0x1178, + _3dVertexAttrib7Format = 0x117c, + _3dVertexAttrib8Format = 0x1180, + _3dVertexAttrib9Format = 0x1184, + _3dVertexAttrib10Format = 0x1188, + _3dVertexAttrib11Format = 0x118c, + _3dVertexAttrib12Format = 0x1190, + _3dVertexAttrib13Format = 0x1194, + _3dVertexAttrib14Format = 0x1198, + _3dVertexAttrib15Format = 0x119c, + _3dScreenYControl = 0x13ac, + _3dTscAddressHigh = 0x155c, + _3dTscAddressLow = 0x1560, + _3dTscLimit = 0x1564, + _3dTicAddressHigh = 0x1574, + _3dTicAddressLow = 0x1578, + _3dTicLimit = 0x157c, + _3dMultiSampleMode = 0x15d0, + _3dVertexEndGl = 0x1614, + _3dVertexBeginGl = 0x1618, + _3dQueryAddressHigh = 0x1b00, + _3dQueryAddressLow = 0x1b04, + _3dQuerySequence = 0x1b08, + _3dQueryGet = 0x1b0c, + _3dVertexArray0Fetch = 0x1c00, + _3dVertexArray0StartHigh = 0x1c04, + _3dVertexArray0StartLow = 0x1c08, + _3dVertexArray1Fetch = 0x1c10, //todo: the rest + _3dVertexArray0LimitHigh = 0x1f00, + _3dVertexArray0LimitLow = 0x1f04, + _3dCbSize = 0x2380, + _3dCbAddressHigh = 0x2384, + _3dCbAddressLow = 0x2388, + _3dCbPos = 0x238c, + _3dCbData0 = 0x2390, + _3dCbData1 = 0x2394, + _3dCbData2 = 0x2398, + _3dCbData3 = 0x239c, + _3dCbData4 = 0x23a0, + _3dCbData5 = 0x23a4, + _3dCbData6 = 0x23a8, + _3dCbData7 = 0x23ac, + _3dCbData8 = 0x23b0, + _3dCbData9 = 0x23b4, + _3dCbData10 = 0x23b8, + _3dCbData11 = 0x23bc, + _3dCbData12 = 0x23c0, + _3dCbData13 = 0x23c4, + _3dCbData14 = 0x23c8, + _3dCbData15 = 0x23cc, + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpuTexture.cs b/Ryujinx.Graphics/Gpu/NsGpuTexture.cs new file mode 100644 index 00000000..aac42200 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NsGpuTexture.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Gpu +{ + struct NsGpuTexture + { + public int Width; + public int Height; + + public byte[] Data; + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpuTextureFormat.cs b/Ryujinx.Graphics/Gpu/NsGpuTextureFormat.cs new file mode 100644 index 00000000..2993840b --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NsGpuTextureFormat.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Gpu +{ + enum NsGpuTextureFormat + { + BC1 = 0x24, + BC2 = 0x25, + BC3 = 0x26 + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/SwizzleAddr.cs b/Ryujinx.Graphics/Gpu/SwizzleAddr.cs new file mode 100644 index 00000000..08e61eb5 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/SwizzleAddr.cs @@ -0,0 +1,144 @@ +using System; + +namespace Ryujinx.Graphics.Gpu +{ + class SwizzleAddr + { + private int Width; + + private int XB; + private int YB; + + public SwizzleAddr(int Width, int Height, int Pad) + { + int W = Pow2RoundUp(Width); + int H = Pow2RoundUp(Height); + + XB = CountZeros(W); + YB = CountZeros(H); + + int HH = H >> 1; + + if (!IsPow2(Height) && Height <= HH + HH / 3 && YB > 3) + { + YB--; + } + + this.Width = RoundSize(Width, Pad); + } + + private static int Pow2RoundUp(int Value) + { + Value--; + + Value |= (Value >> 1); + Value |= (Value >> 2); + Value |= (Value >> 4); + Value |= (Value >> 8); + Value |= (Value >> 16); + + return ++Value; + } + + private static bool IsPow2(int Value) + { + return Value != 0 && (Value & (Value - 1)) == 0; + } + + private static int CountZeros(int Value) + { + int Count = 0; + + for (int i = 0; i < 32; i++) + { + if ((Value & (1 << i)) != 0) + { + break; + } + + Count++; + } + + return Count; + } + + private static int RoundSize(int Size, int Pad) + { + int Mask = Pad - 1; + + if ((Size & Mask) != 0) + { + Size &= ~Mask; + Size += Pad; + } + + return Size; + } + + public int GetSwizzledAddress8(int X, int Y) + { + return GetSwizzledAddress(X, Y, 4); + } + + public int GetSwizzledAddress16(int X, int Y) + { + return GetSwizzledAddress(X, Y, 3); + } + + public int GetSwizzledAddress32(int X, int Y) + { + return GetSwizzledAddress(X, Y, 2); + } + + public int GetSwizzledAddress64(int X, int Y) + { + return GetSwizzledAddress(X, Y, 1); + } + + public int GetSwizzledAddress128(int X, int Y) + { + return GetSwizzledAddress(X, Y, 0); + } + + private int GetSwizzledAddress(int X, int Y, int XBase) + { + /* + * Examples of patterns: + * x x y x y y x y 0 0 0 0 64 x 64 dxt5 + * x x x x x y y y y x y y x y 0 0 0 0 512 x 512 dxt5 + * y x x x x x x y y y y x y y x y 0 0 0 0 1024 x 1024 dxt5 + * y y x x x x x x y y y y x y y x y x 0 0 0 2048 x 2048 dxt1 + * y y y x x x x x x y y y y x y y x y x x 0 0 1024 x 1024 rgba8888 + * + * Read from right to left, LSB first. + */ + int XCnt = XBase; + int YCnt = 1; + int XUsed = 0; + int YUsed = 0; + int Address = 0; + + while (XUsed < XBase + 2 && XUsed + XCnt < XB) + { + int XMask = (1 << XCnt) - 1; + int YMask = (1 << YCnt) - 1; + + Address |= (X & XMask) << XUsed + YUsed; + Address |= (Y & YMask) << XUsed + YUsed + XCnt; + + X >>= XCnt; + Y >>= YCnt; + + XUsed += XCnt; + YUsed += YCnt; + + XCnt = Math.Min(XB - XUsed, 1); + YCnt = Math.Min(YB - YUsed, YCnt << 1); + } + + Address |= (X + Y * (Width >> XUsed)) << (XUsed + YUsed); + + return Address; + } + } +} diff --git a/Ryujinx.Graphics/Ryujinx.Graphics.csproj b/Ryujinx.Graphics/Ryujinx.Graphics.csproj new file mode 100644 index 00000000..657beb82 --- /dev/null +++ b/Ryujinx.Graphics/Ryujinx.Graphics.csproj @@ -0,0 +1,15 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netcoreapp2.0</TargetFramework> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="OpenTK.NETCore" Version="1.1.2749.6433" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" /> + </ItemGroup> + +</Project> |
