diff --git a/Ryujinx.Common/Utilities/BitUtils.cs b/Ryujinx.Common/Utilities/BitUtils.cs index 135b397d3d..9a933b9e56 100644 --- a/Ryujinx.Common/Utilities/BitUtils.cs +++ b/Ryujinx.Common/Utilities/BitUtils.cs @@ -34,6 +34,11 @@ namespace Ryujinx.Common return value & -(long)size; } + public static int DivRoundUp(int value, int dividend) + { + return (value + dividend - 1) / dividend; + } + public static ulong DivRoundUp(ulong value, uint dividend) { return (value + dividend - 1) / dividend; @@ -44,6 +49,24 @@ namespace Ryujinx.Common return (value + dividend - 1) / dividend; } + public static int Pow2RoundUp(int Value) + { + Value--; + + Value |= (Value >> 1); + Value |= (Value >> 2); + Value |= (Value >> 4); + Value |= (Value >> 8); + Value |= (Value >> 16); + + return ++Value; + } + + public static int Pow2RoundDown(int Value) + { + return IsPowerOfTwo32(Value) ? Value : Pow2RoundUp(Value) >> 1; + } + public static bool IsPowerOfTwo32(int value) { return value != 0 && (value & (value - 1)) == 0; @@ -85,6 +108,18 @@ namespace Ryujinx.Common return (ulong)count; } + public static int CountTrailingZeros32(int Value) + { + int Count = 0; + + while (((Value >> Count) & 1) == 0) + { + Count++; + } + + return Count; + } + public static long ReverseBits64(long value) { return (long)ReverseBits64((ulong)value); diff --git a/Ryujinx.Graphics/Gal/GalImage.cs b/Ryujinx.Graphics/Gal/GalImage.cs index a4ed8b8330..5ee6a7f237 100644 --- a/Ryujinx.Graphics/Gal/GalImage.cs +++ b/Ryujinx.Graphics/Gal/GalImage.cs @@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Gal public int Depth; public int TileWidth; public int GobBlockHeight; + public int GobBlockDepth; public int Pitch; public int MaxMipmapLevel; @@ -26,6 +27,7 @@ namespace Ryujinx.Graphics.Gal int Depth, int TileWidth, int GobBlockHeight, + int GobBlockDepth, GalMemoryLayout Layout, GalImageFormat Format, GalTextureTarget TextureTarget, @@ -40,6 +42,7 @@ namespace Ryujinx.Graphics.Gal this.Depth = Depth; this.TileWidth = TileWidth; this.GobBlockHeight = GobBlockHeight; + this.GobBlockDepth = GobBlockDepth; this.Layout = Layout; this.Format = Format; this.MaxMipmapLevel = MaxMipmapLevel; diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs index da81413334..87742ee9bd 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs @@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Graphics3d SrcWidth, SrcHeight, SrcDepth, 1, - SrcBlockHeight, + SrcBlockHeight, 1, SrcLayout, SrcImgFormat, SrcTarget); @@ -128,7 +128,7 @@ namespace Ryujinx.Graphics.Graphics3d DstWidth, DstHeight, DstDepth, 1, - DstBlockHeight, + DstBlockHeight, 1, DstLayout, DstImgFormat, DstTarget); diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs index 10f18bbdf6..14819eaba7 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs @@ -208,7 +208,7 @@ namespace Ryujinx.Graphics.Graphics3d GalImageFormat Format = ImageUtils.ConvertSurface((GalSurfaceFormat)SurfFormat); - GalImage Image = new GalImage(Width, Height, 1, 1, GobBlockHeight, Layout, Format, GalTextureTarget.TwoD); + GalImage Image = new GalImage(Width, Height, 1, 1, GobBlockHeight, 1, Layout, Format, GalTextureTarget.TwoD); Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image); @@ -263,7 +263,7 @@ namespace Ryujinx.Graphics.Graphics3d GalImageFormat Format = ImageUtils.ConvertZeta((GalZetaFormat)ZetaFormat); // TODO: Support non 2D? - GalImage Image = new GalImage(Width, Height, 1, 1, GobBlockHeight, Layout, Format, GalTextureTarget.TwoD); + GalImage Image = new GalImage(Width, Height, 1, 1, GobBlockHeight, 1, Layout, Format, GalTextureTarget.TwoD); Gpu.ResourceManager.SendZetaBuffer(Vmm, Key, Image); } diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs index 195fce1acb..5ce4fca0d2 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs @@ -129,7 +129,11 @@ namespace Ryujinx.Graphics.Graphics3d } else { - SrcSwizzle = new BlockLinearSwizzle(SrcSizeX, SrcSizeY, SrcCpp, SrcBlockHeight); + SrcSwizzle = new BlockLinearSwizzle( + SrcSizeX, + SrcSizeY, 1, + SrcBlockHeight, 1, + SrcCpp); } ISwizzle DstSwizzle; @@ -140,7 +144,11 @@ namespace Ryujinx.Graphics.Graphics3d } else { - DstSwizzle = new BlockLinearSwizzle(DstSizeX, DstSizeY, DstCpp, DstBlockHeight); + DstSwizzle = new BlockLinearSwizzle( + DstSizeX, + DstSizeY, 1, + DstBlockHeight, 1, + DstCpp); } for (int Y = 0; Y < YCount; Y++) diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs index 9111365fd9..82b98b42de 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs @@ -119,7 +119,10 @@ namespace Ryujinx.Graphics.Graphics3d } else { - BlockLinearSwizzle Swizzle = new BlockLinearSwizzle(CopyWidth, CopyHeight, 1, CopyGobBlockHeight); + BlockLinearSwizzle Swizzle = new BlockLinearSwizzle( + CopyWidth, + CopyHeight, 1, + CopyGobBlockHeight, 1, 1); int SrcOffset = 0; diff --git a/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs index 11dada692f..1be0644283 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs @@ -1,55 +1,178 @@ +using Ryujinx.Common; using System; namespace Ryujinx.Graphics.Texture { class BlockLinearSwizzle : ISwizzle { - private int BhShift; - private int BppShift; + private const int GobWidth = 64; + private const int GobHeight = 8; + + private const int GobSize = GobWidth * GobHeight; + + private int TexWidth; + private int TexHeight; + private int TexDepth; + private int TexGobBlockHeight; + private int TexGobBlockDepth; + private int TexBpp; + private int BhMask; + private int BdMask; + + private int BhShift; + private int BdShift; + private int BppShift; private int XShift; - private int GobStride; + private int RobSize; private int SliceSize; - public BlockLinearSwizzle(int Width, int Height, int Bpp, int BlockHeight) + private int BaseOffset; + + public BlockLinearSwizzle( + int Width, + int Height, + int Depth, + int GobBlockHeight, + int GobBlockDepth, + int Bpp) { - BhMask = (BlockHeight * 8) - 1; + TexWidth = Width; + TexHeight = Height; + TexDepth = Depth; + TexGobBlockHeight = GobBlockHeight; + TexGobBlockDepth = GobBlockDepth; + TexBpp = Bpp; - BhShift = CountLsbZeros(BlockHeight * 8); - BppShift = CountLsbZeros(Bpp); + BppShift = BitUtils.CountTrailingZeros32(Bpp); - int WidthInGobs = (int)MathF.Ceiling(Width * Bpp / 64f); - - GobStride = 512 * BlockHeight * WidthInGobs; - - XShift = CountLsbZeros(512 * BlockHeight); - - SliceSize = Bpp * Width * Height; + SetMipLevel(0); } - private int CountLsbZeros(int Value) + public void SetMipLevel(int Level) { - int Count = 0; + BaseOffset = GetMipOffset(Level); - while (((Value >> Count) & 1) == 0) + int Width = Math.Max(1, TexWidth >> Level); + int Height = Math.Max(1, TexHeight >> Level); + int Depth = Math.Max(1, TexDepth >> Level); + + GobBlockSizes GbSizes = AdjustGobBlockSizes(Height, Depth); + + BhMask = GbSizes.Height - 1; + BdMask = GbSizes.Depth - 1; + + BhShift = BitUtils.CountTrailingZeros32(GbSizes.Height); + BdShift = BitUtils.CountTrailingZeros32(GbSizes.Depth); + + XShift = BitUtils.CountTrailingZeros32(GobSize * GbSizes.Height * GbSizes.Depth); + + RobAndSliceSizes GsSizes = GetRobAndSliceSizes(Width, Height, GbSizes); + + RobSize = GsSizes.RobSize; + SliceSize = GsSizes.SliceSize; + } + + public int GetImageSize(int MipsCount) + { + int Size = GetMipOffset(MipsCount); + + Size = (Size + 0x1fff) & ~0x1fff; + + return Size; + } + + public int GetMipOffset(int Level) + { + int TotalSize = 0; + + for (int Index = 0; Index < Level; Index++) { - Count++; + int Width = Math.Max(1, TexWidth >> Index); + int Height = Math.Max(1, TexHeight >> Index); + int Depth = Math.Max(1, TexDepth >> Index); + + GobBlockSizes GbSizes = AdjustGobBlockSizes(Height, Depth); + + RobAndSliceSizes RsSizes = GetRobAndSliceSizes(Width, Height, GbSizes); + + TotalSize += BitUtils.DivRoundUp(Depth, GbSizes.Depth) * RsSizes.SliceSize; } - return Count; + return TotalSize; + } + + private struct GobBlockSizes + { + public int Height; + public int Depth; + + public GobBlockSizes(int GobBlockHeight, int GobBlockDepth) + { + this.Height = GobBlockHeight; + this.Depth = GobBlockDepth; + } + } + + private GobBlockSizes AdjustGobBlockSizes(int Height, int Depth) + { + int GobBlockHeight = TexGobBlockHeight; + int GobBlockDepth = TexGobBlockDepth; + + int Pow2Height = BitUtils.Pow2RoundUp(Height); + int Pow2Depth = BitUtils.Pow2RoundUp(Depth); + + while (GobBlockHeight * GobHeight > Pow2Height && GobBlockHeight > 1) + { + GobBlockHeight >>= 1; + } + + while (GobBlockDepth > Pow2Depth && GobBlockDepth > 1) + { + GobBlockDepth >>= 1; + } + + return new GobBlockSizes(GobBlockHeight, GobBlockDepth); + } + + private struct RobAndSliceSizes + { + public int RobSize; + public int SliceSize; + + public RobAndSliceSizes(int RobSize, int SliceSize) + { + this.RobSize = RobSize; + this.SliceSize = SliceSize; + } + } + + private RobAndSliceSizes GetRobAndSliceSizes(int Width, int Height, GobBlockSizes GbSizes) + { + int WidthInGobs = BitUtils.DivRoundUp(Width * TexBpp, GobWidth); + + int RobSize = GobSize * GbSizes.Height * GbSizes.Depth * WidthInGobs; + + int SliceSize = BitUtils.DivRoundUp(Height, GbSizes.Height * GobHeight) * RobSize; + + return new RobAndSliceSizes(RobSize, SliceSize); } public int GetSwizzleOffset(int X, int Y, int Z) { X <<= BppShift; - int Position = (Y >> BhShift) * GobStride; + int YH = Y / GobHeight; - Position += (X >> 6) << XShift; + int Position = (Z >> BdShift) * SliceSize + (YH >> BhShift) * RobSize; - Position += ((Y & BhMask) >> 3) << 9; + Position += (X / GobWidth) << XShift; + + Position += (YH & BhMask) * GobSize; + + Position += ((Z & BdMask) * GobSize) << BhShift; Position += ((X & 0x3f) >> 5) << 8; Position += ((Y & 0x07) >> 1) << 6; @@ -57,7 +180,7 @@ namespace Ryujinx.Graphics.Texture Position += ((Y & 0x01) >> 0) << 4; Position += ((X & 0x0f) >> 0) << 0; - return Z * SliceSize + Position; + return BaseOffset + Position; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs b/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs index 70e0b1ab68..61409823f0 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs @@ -35,14 +35,16 @@ namespace Ryujinx.Graphics.Texture Layout = GalMemoryLayout.Pitch; } - int BlockHeightLog2 = (Tic[3] >> 3) & 7; - int TileWidthLog2 = (Tic[3] >> 10) & 7; + int GobBlockHeightLog2 = (Tic[3] >> 3) & 7; + int GobBlockDepthLog2 = (Tic[3] >> 6) & 7; + int TileWidthLog2 = (Tic[3] >> 10) & 7; - int BlockHeight = 1 << BlockHeightLog2; - int TileWidth = 1 << TileWidthLog2; + int GobBlockHeight = 1 << GobBlockHeightLog2; + int GobBlockDepth = 1 << GobBlockDepthLog2; + int TileWidth = 1 << TileWidthLog2; - int Width = (Tic[4] & 0xffff) + 1; - int Height = (Tic[5] & 0xffff) + 1; + int Width = ((Tic[4] >> 0) & 0xffff) + 1; + int Height = ((Tic[5] >> 0) & 0xffff) + 1; int Depth = ((Tic[5] >> 16) & 0x3fff) + 1; if (TextureTarget == GalTextureTarget.OneD) @@ -64,7 +66,8 @@ namespace Ryujinx.Graphics.Texture Height, Depth, TileWidth, - BlockHeight, + GobBlockHeight, + GobBlockDepth, Layout, Format, TextureTarget, diff --git a/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs b/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs index 4680b29df7..33ccb0aa51 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs @@ -1,4 +1,5 @@ using ChocolArm64.Memory; +using Ryujinx.Common; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; @@ -10,10 +11,12 @@ namespace Ryujinx.Graphics.Texture { int BlockWidth = ImageUtils.GetBlockWidth (Image.Format); int BlockHeight = ImageUtils.GetBlockHeight (Image.Format); + int BlockDepth = ImageUtils.GetBlockDepth (Image.Format); int BytesPerPixel = ImageUtils.GetBytesPerPixel(Image.Format); - int Width = (Image.Width + (BlockWidth - 1)) / BlockWidth; - int Height = (Image.Height + (BlockHeight - 1)) / BlockHeight; + int Width = BitUtils.DivRoundUp(Image.Width, BlockWidth); + int Height = BitUtils.DivRoundUp(Image.Height, BlockHeight); + int Depth = BitUtils.DivRoundUp(Image.Depth, BlockDepth); if (Image.Layout == GalMemoryLayout.BlockLinear) { @@ -21,7 +24,13 @@ namespace Ryujinx.Graphics.Texture Width = (Width + AlignMask) & ~AlignMask; - return new BlockLinearSwizzle(Width, Height, BytesPerPixel, Image.GobBlockHeight); + return new BlockLinearSwizzle( + Width, + Height, + Depth, + Image.GobBlockHeight, + Image.GobBlockDepth, + BytesPerPixel); } else { diff --git a/Ryujinx.Graphics/VDec/VideoDecoder.cs b/Ryujinx.Graphics/VDec/VideoDecoder.cs index b23c657ac1..7d3a890dfe 100644 --- a/Ryujinx.Graphics/VDec/VideoDecoder.cs +++ b/Ryujinx.Graphics/VDec/VideoDecoder.cs @@ -217,7 +217,7 @@ namespace Ryujinx.Graphics.VDec GalImage Image = new GalImage( OutputConfig.SurfaceWidth, OutputConfig.SurfaceHeight, 1, 1, - OutputConfig.GobBlockHeight, + OutputConfig.GobBlockHeight, 1, GalMemoryLayout.BlockLinear, GalImageFormat.RGBA8 | GalImageFormat.Unorm, GalTextureTarget.TwoD); diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs index 519e962a63..37f0c9e09f 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs @@ -416,7 +416,7 @@ namespace Ryujinx.HLE.HOS.Services.Android { image = new GalImage( fbWidth, - fbHeight, 1, 1, BlockHeight, + fbHeight, 1, 1, BlockHeight, 1, GalMemoryLayout.BlockLinear, imageFormat, GalTextureTarget.TwoD);