diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 13114aff5d..3a450606eb 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -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()); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs index 66ada4a989..b8a5963995 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs @@ -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) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index d4c3e16b54..0405625ef9 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -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); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index d9c85f3697..7a2313a4b6 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -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); diff --git a/Ryujinx.Graphics/Gpu/BlockLinearSwizzle.cs b/Ryujinx.Graphics/Gpu/BlockLinearSwizzle.cs new file mode 100644 index 0000000000..d2cbb14432 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/BlockLinearSwizzle.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/ISwizzle.cs b/Ryujinx.Graphics/Gpu/ISwizzle.cs new file mode 100644 index 0000000000..755051d0c4 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/ISwizzle.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.Gpu +{ + interface ISwizzle + { + int GetSwizzleOffset(int X, int Y); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/LinearSwizzle.cs b/Ryujinx.Graphics/Gpu/LinearSwizzle.cs new file mode 100644 index 0000000000..01f09f8160 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/LinearSwizzle.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs index 26664acefa..de009fdca2 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs @@ -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) diff --git a/Ryujinx.Graphics/Gpu/Texture.cs b/Ryujinx.Graphics/Gpu/Texture.cs new file mode 100644 index 0000000000..c8d4e5274d --- /dev/null +++ b/Ryujinx.Graphics/Gpu/Texture.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/TextureFactory.cs b/Ryujinx.Graphics/Gpu/TextureFactory.cs new file mode 100644 index 0000000000..2f42772070 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/TextureFactory.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/TextureReader.cs b/Ryujinx.Graphics/Gpu/TextureReader.cs new file mode 100644 index 0000000000..ce66e991d6 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/TextureReader.cs @@ -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()); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/TextureSwizzle.cs b/Ryujinx.Graphics/Gpu/TextureSwizzle.cs new file mode 100644 index 0000000000..2142e2c20a --- /dev/null +++ b/Ryujinx.Graphics/Gpu/TextureSwizzle.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Graphics.Gpu +{ + enum TextureSwizzle + { + _1dBuffer = 0, + PitchColorKey = 1, + Pitch = 2, + BlockLinear = 3, + BlockLinearColorKey = 4 + } +} \ No newline at end of file