Split main project into core,graphics and chocolarm4 subproject (#29)

This commit is contained in:
emmauss 2018-02-20 22:09:23 +02:00 committed by gdkchan
parent cb665bb715
commit 62b827f474
257 changed files with 415 additions and 285 deletions

468
Ryujinx.Graphics/Gpu/BCn.cs Normal file
View file

@ -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;
}
}
}

View file

@ -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);
}
}
}

View file

@ -0,0 +1,13 @@
namespace Ryujinx.Graphics.Gpu
{
enum NsGpuEngine
{
None = 0,
_2d = 0x902d,
_3d = 0xb197,
Compute = 0xb1c0,
Kepler = 0xa140,
Dma = 0xb0b5,
GpFifo = 0xb06f
}
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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;
}
}
}

View file

@ -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,
}
}

View file

@ -0,0 +1,10 @@
namespace Ryujinx.Graphics.Gpu
{
struct NsGpuTexture
{
public int Width;
public int Height;
public byte[] Data;
}
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Graphics.Gpu
{
enum NsGpuTextureFormat
{
BC1 = 0x24,
BC2 = 0x25,
BC3 = 0x26
}
}

View file

@ -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;
}
}
}