Support linear swizzling, upload compressed textures instead of decoding on the CPU (this is a bit faster)

This commit is contained in:
gdkchan 2018-04-02 16:32:21 -03:00
parent 9da58cc655
commit 46405f6767
12 changed files with 411 additions and 74 deletions

View file

@ -18,5 +18,17 @@ namespace Ryujinx.Graphics.Gal.OpenGL
throw new ArgumentException(nameof(Type));
}
public static PixelInternalFormat GetCompressedTextureFormat(GalTextureFormat Format)
{
switch (Format)
{
case GalTextureFormat.BC1: return PixelInternalFormat.CompressedRgbaS3tcDxt1Ext;
case GalTextureFormat.BC2: return PixelInternalFormat.CompressedRgbaS3tcDxt3Ext;
case GalTextureFormat.BC3: return PixelInternalFormat.CompressedRgbaS3tcDxt5Ext;
}
throw new NotImplementedException(Format.ToString());
}
}
}

View file

@ -1,5 +1,4 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Gal.Texture;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
@ -58,18 +57,37 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
const PixelInternalFormat Pif = PixelInternalFormat.Rgba;
int W = Texture.Width;
int H = Texture.Height;
const PixelFormat Pf = PixelFormat.Rgba;
byte[] Data = Texture.Data;
const PixelType Pt = PixelType.UnsignedByte;
int Length = Data.Length;
byte[] Buffer = TextureDecoder.Decode(Texture);
if (IsCompressedTextureFormat(Texture.Format))
{
PixelInternalFormat Pif = OGLEnumConverter.GetCompressedTextureFormat(Texture.Format);
GL.TexImage2D(TextureTarget.Texture2D, 0, Pif, W, H, 0, Pf, Pt, Buffer);
GL.CompressedTexImage2D(TextureTarget.Texture2D, 0, Pif, W, H, 0, Length, Data);
}
else
{
//TODO: Get those from Texture format.
const PixelInternalFormat Pif = PixelInternalFormat.Rgba;
const PixelFormat Pf = PixelFormat.Rgba;
const PixelType Pt = PixelType.UnsignedByte;
GL.TexImage2D(TextureTarget.Texture2D, 0, Pif, W, H, 0, Pf, Pt, Data);
}
}
private static bool IsCompressedTextureFormat(GalTextureFormat Format)
{
return Format == GalTextureFormat.BC1 ||
Format == GalTextureFormat.BC2 ||
Format == GalTextureFormat.BC3;
}
private int EnsureTextureInitialized(int TextureIndex)

View file

@ -64,45 +64,14 @@ namespace Ryujinx.Graphics.Gal.Shader
EmitAluBinary(Block, OpCode, ShaderOper.Imm, ShaderIrInst.Fmul);
}
public static void Fsetp_R(ShaderIrBlock Block, long OpCode)
{
EmitFsetp(Block, OpCode, ShaderOper.RR);
}
public static void Fsetp_C(ShaderIrBlock Block, long OpCode)
{
bool Aa = ((OpCode >> 7) & 1) != 0;
bool Na = ((OpCode >> 43) & 1) != 0;
bool Ab = ((OpCode >> 44) & 1) != 0;
ShaderIrNode OperA = GetOperGpr8 (OpCode);
ShaderIrNode OperB = GetOperCbuf34(OpCode);
ShaderIrInst CmpInst = GetCmp(OpCode);
ShaderIrOp Op = new ShaderIrOp(CmpInst,
GetAluAbsNeg(OperA, Aa, Na),
GetAluAbs (OperB, Ab));
ShaderIrOperPred P0Node = GetOperPred3 (OpCode);
ShaderIrOperPred P1Node = GetOperPred0 (OpCode);
ShaderIrOperPred P2Node = GetOperPred39(OpCode);
Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
ShaderIrInst LopInst = GetBLop(OpCode);
if (LopInst == ShaderIrInst.Band &&
P1Node.Index == ShaderIrOperPred.UnusedIndex &&
P2Node.Index == ShaderIrOperPred.UnusedIndex)
{
return;
}
ShaderIrNode P2NNode = GetOperPred39N(OpCode);
Op = new ShaderIrOp(LopInst, new ShaderIrOp(ShaderIrInst.Bnot, P0Node), P2NNode);
Block.AddNode(GetPredNode(new ShaderIrAsg(P1Node, Op), OpCode));
Op = new ShaderIrOp(LopInst, P0Node, P2NNode);
Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
EmitFsetp(Block, OpCode, ShaderOper.CR);
}
public static void Ipa(ShaderIrBlock Block, long OpCode)
@ -211,6 +180,61 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
}
private static void EmitFsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{
bool Aa = ((OpCode >> 7) & 1) != 0;
bool Np = ((OpCode >> 42) & 1) != 0;
bool Na = ((OpCode >> 43) & 1) != 0;
bool Ab = ((OpCode >> 44) & 1) != 0;
ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
switch (Oper)
{
case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
case ShaderOper.Imm: OperB = GetOperImmf19_20(OpCode); break;
default: throw new ArgumentException(nameof(Oper));
}
ShaderIrInst CmpInst = GetCmp(OpCode);
ShaderIrOp Op = new ShaderIrOp(CmpInst,
GetAluAbsNeg(OperA, Aa, Na),
GetAluAbs (OperB, Ab));
ShaderIrOperPred P0Node = GetOperPred3 (OpCode);
ShaderIrOperPred P1Node = GetOperPred0 (OpCode);
ShaderIrOperPred P2Node = GetOperPred39(OpCode);
Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
ShaderIrInst LopInst = GetBLop(OpCode);
if (LopInst == ShaderIrInst.Band && P1Node.IsConst && P2Node.IsConst)
{
return;
}
ShaderIrNode P2NNode = P2Node;
if (Np)
{
P2NNode = new ShaderIrOp(ShaderIrInst.Bnot, P2NNode);
}
Op = new ShaderIrOp(ShaderIrInst.Bnot, P0Node);
Op = new ShaderIrOp(LopInst, Op, P2NNode);
Block.AddNode(GetPredNode(new ShaderIrAsg(P1Node, Op), OpCode));
Op = new ShaderIrOp(LopInst, P0Node, P2NNode);
Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
}
private static ShaderIrNode GetAluAbsNeg(ShaderIrNode Node, bool Abs, bool Neg)
{
return GetAluNeg(GetAluAbs(Node, Abs), Neg);

View file

@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Gal.Shader
Set("0101110001101x", ShaderDecode.Fmul_R);
Set("0100110001101x", ShaderDecode.Fmul_C);
Set("0011100x01101x", ShaderDecode.Fmul_Imm);
Set("010110111011xx", ShaderDecode.Fsetp_R);
Set("010010111011xx", ShaderDecode.Fsetp_C);
Set("11100000xxxxxx", ShaderDecode.Ipa);
Set("111000110011xx", ShaderDecode.Kil);

View file

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

View file

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

View file

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

View file

@ -232,37 +232,9 @@ namespace Ryujinx.Graphics.Gpu
TryGetCpuAddr(NvGpuEngine3dReg.TexHeaderPoolOffset, out long TicPosition);
int[] Tic = ReadWords(Memory, TicPosition + TicIndex * 0x20, 8);
TicPosition += TicIndex * 0x20;
GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f);
long TextureAddress = (uint)Tic[1];
TextureAddress |= (long)((ushort)Tic[2]) << 32;
TextureAddress = Gpu.GetCpuAddr(TextureAddress);
int Width = (Tic[4] & 0xffff) + 1;
int Height = (Tic[5] & 0xffff) + 1;
long TextureSize = GetTextureSize(Width, Height, Format);
byte[] Data = AMemoryHelper.ReadBytes(Memory, TextureAddress, TextureSize);
return new GalTexture(Data, Width, Height, Format);
}
private long GetTextureSize(int Width, int Height, GalTextureFormat Format)
{
switch (Format)
{
case GalTextureFormat.A8B8G8R8: return (Width * Height) << 2;
case GalTextureFormat.BC1: return (Width * Height) >> 1;
case GalTextureFormat.BC2: return Width * Height;
case GalTextureFormat.BC3: return Width * Height;
}
throw new NotImplementedException(Format.ToString());
return TextureFactory.MakeTexture(Gpu, Memory, TicPosition);
}
private int[] ReadWords(AMemory Memory, long Position, int Count)

View file

@ -0,0 +1,34 @@
using Ryujinx.Graphics.Gal;
namespace Ryujinx.Graphics.Gpu
{
struct Texture
{
public long Position { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public int BlockHeight { get; private set; }
public TextureSwizzle Swizzle { get; private set; }
public GalTextureFormat Format { get; private set; }
public Texture(
long Position,
int Width,
int Height,
int BlockHeight,
TextureSwizzle Swizzle,
GalTextureFormat Format)
{
this.Position = Position;
this.Width = Width;
this.Height = Height;
this.BlockHeight = BlockHeight;
this.Swizzle = Swizzle;
this.Format = Format;
}
}
}

View file

@ -0,0 +1,54 @@
using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal;
namespace Ryujinx.Graphics.Gpu
{
static class TextureFactory
{
public static GalTexture MakeTexture(NsGpu Gpu, AMemory Memory, long TicPosition)
{
int[] Tic = ReadWords(Memory, TicPosition, 8);
GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f);
long TextureAddress = (uint)Tic[1];
TextureAddress |= (long)((ushort)Tic[2]) << 32;
TextureAddress = Gpu.GetCpuAddr(TextureAddress);
TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7);
int BlockHeightLog2 = (Tic[3] >> 3) & 7;
int BlockHeight = 1 << BlockHeightLog2;
int Width = (Tic[4] & 0xffff) + 1;
int Height = (Tic[5] & 0xffff) + 1;
Texture Texture = new Texture(
TextureAddress,
Width,
Height,
BlockHeight,
Swizzle,
Format);
byte[] Data = TextureReader.Read(Memory, Texture);
return new GalTexture(Data, Width, Height, Format);
}
private static int[] ReadWords(AMemory Memory, long Position, int Count)
{
int[] Words = new int[Count];
for (int Index = 0; Index < Count; Index++, Position += 4)
{
Words[Index] = Memory.ReadInt32(Position);
}
return Words;
}
}
}

View file

@ -0,0 +1,127 @@
using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal;
using System;
namespace Ryujinx.Graphics.Gpu
{
static class TextureReader
{
public static byte[] Read(AMemory Memory, Texture Texture)
{
switch (Texture.Format)
{
case GalTextureFormat.A8B8G8R8: return Read4Bpp (Memory, Texture);
case GalTextureFormat.BC1: return Read8Bpt4x4 (Memory, Texture);
case GalTextureFormat.BC2: return Read16Bpt4x4(Memory, Texture);
case GalTextureFormat.BC3: return Read16Bpt4x4(Memory, Texture);
}
throw new NotImplementedException(Texture.Format.ToString());
}
private unsafe static byte[] Read4Bpp(AMemory Memory, Texture Texture)
{
int Width = Texture.Width;
int Height = Texture.Height;
byte[] Output = new byte[Width * Height * 4];
ISwizzle Swizzle = GetSwizzle(Texture.Swizzle, Width, 4, Texture.BlockHeight);
fixed (byte* BuffPtr = Output)
{
long OutOffs = 0;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
int Pixel = Memory.ReadInt32Unchecked(Texture.Position + Offset);
*(int*)(BuffPtr + OutOffs) = Pixel;
OutOffs += 4;
}
}
return Output;
}
private unsafe static byte[] Read8Bpt4x4(AMemory Memory, Texture Texture)
{
int Width = (Texture.Width + 3) / 4;
int Height = (Texture.Height + 3) / 4;
byte[] Output = new byte[Width * Height * 8];
ISwizzle Swizzle = GetSwizzle(Texture.Swizzle, Width, 8, Texture.BlockHeight);
fixed (byte* BuffPtr = Output)
{
long OutOffs = 0;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
long Tile = Memory.ReadInt64Unchecked(Texture.Position + Offset);
*(long*)(BuffPtr + OutOffs) = Tile;
OutOffs += 8;
}
}
return Output;
}
private unsafe static byte[] Read16Bpt4x4(AMemory Memory, Texture Texture)
{
int Width = (Texture.Width + 3) / 4;
int Height = (Texture.Height + 3) / 4;
byte[] Output = new byte[Width * Height * 16];
ISwizzle Swizzle = GetSwizzle(Texture.Swizzle, Width, 16, Texture.BlockHeight);
fixed (byte* BuffPtr = Output)
{
long OutOffs = 0;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
long Tile0 = Memory.ReadInt64Unchecked(Texture.Position + Offset + 0);
long Tile1 = Memory.ReadInt64Unchecked(Texture.Position + Offset + 8);
*(long*)(BuffPtr + OutOffs + 0) = Tile0;
*(long*)(BuffPtr + OutOffs + 8) = Tile1;
OutOffs += 16;
}
}
return Output;
}
private static ISwizzle GetSwizzle(TextureSwizzle Swizzle, int Width, int Bpp, int BlockHeight)
{
switch (Swizzle)
{
case TextureSwizzle.Pitch:
case TextureSwizzle.PitchColorKey:
return new LinearSwizzle(Width, Bpp);
case TextureSwizzle.BlockLinear:
case TextureSwizzle.BlockLinearColorKey:
return new BlockLinearSwizzle(Width, Bpp, BlockHeight);
}
throw new NotImplementedException(Swizzle.ToString());
}
}
}

View file

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