aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics
diff options
context:
space:
mode:
authoremmauss <emmausssss@gmail.com>2018-02-20 22:09:23 +0200
committergdkchan <gab.dark.100@gmail.com>2018-02-20 17:09:23 -0300
commit62b827f474f0aa2152dd339fcc7cf31084e16a0b (patch)
tree0e5c55b341aee4db0ccb841a084f253ec5e05657 /Ryujinx.Graphics
parentcb665bb715834526d73c9469d16114b287faaecd (diff)
Split main project into core,graphics and chocolarm4 subproject (#29)
Diffstat (limited to 'Ryujinx.Graphics')
-rw-r--r--Ryujinx.Graphics/Gal/GalPrimitiveType.cs21
-rw-r--r--Ryujinx.Graphics/Gal/GalVertexAttrib.cs33
-rw-r--r--Ryujinx.Graphics/Gal/GalVertexAttribSize.cs20
-rw-r--r--Ryujinx.Graphics/Gal/GalVertexAttribType.cs13
-rw-r--r--Ryujinx.Graphics/Gal/IGalRenderer.cs17
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs282
-rw-r--r--Ryujinx.Graphics/Gpu/BCn.cs468
-rw-r--r--Ryujinx.Graphics/Gpu/NsGpu.cs53
-rw-r--r--Ryujinx.Graphics/Gpu/NsGpuEngine.cs13
-rw-r--r--Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs204
-rw-r--r--Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs79
-rw-r--r--Ryujinx.Graphics/Gpu/NsGpuPGraph.cs276
-rw-r--r--Ryujinx.Graphics/Gpu/NsGpuRegister.cs93
-rw-r--r--Ryujinx.Graphics/Gpu/NsGpuTexture.cs10
-rw-r--r--Ryujinx.Graphics/Gpu/NsGpuTextureFormat.cs9
-rw-r--r--Ryujinx.Graphics/Gpu/SwizzleAddr.cs144
-rw-r--r--Ryujinx.Graphics/Ryujinx.Graphics.csproj15
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>