diff --git a/Ryujinx.Common/Utilities/BitUtils.cs b/Ryujinx.Common/Utilities/BitUtils.cs index 135b397d3..b6fba4fba 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/DepthCompareFunc.cs b/Ryujinx.Graphics/DepthCompareFunc.cs new file mode 100644 index 000000000..24c8854a4 --- /dev/null +++ b/Ryujinx.Graphics/DepthCompareFunc.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics +{ + public enum DepthCompareFunc + { + Never = 0, + Less = 1, + Equal = 2, + LEqual = 3, + Greater = 4, + NotEqual = 5, + GEqual = 6, + Always = 7 + } +} diff --git a/Ryujinx.Graphics/Gal/GalImage.cs b/Ryujinx.Graphics/Gal/GalImage.cs index 92f43cc9d..fb904b092 100644 --- a/Ryujinx.Graphics/Gal/GalImage.cs +++ b/Ryujinx.Graphics/Gal/GalImage.cs @@ -6,9 +6,15 @@ namespace Ryujinx.Graphics.Gal { public int Width; public int Height; + + // FIXME: separate layer and depth + public int Depth; + public int LayerCount; public int TileWidth; public int GobBlockHeight; + public int GobBlockDepth; public int Pitch; + public int MaxMipmapLevel; public GalImageFormat Format; public GalMemoryLayout Layout; @@ -16,34 +22,45 @@ namespace Ryujinx.Graphics.Gal public GalTextureSource YSource; public GalTextureSource ZSource; public GalTextureSource WSource; + public GalTextureTarget TextureTarget; public GalImage( int Width, int Height, + int Depth, + int LayerCount, int TileWidth, int GobBlockHeight, + int GobBlockDepth, GalMemoryLayout Layout, GalImageFormat Format, - GalTextureSource XSource = GalTextureSource.Red, - GalTextureSource YSource = GalTextureSource.Green, - GalTextureSource ZSource = GalTextureSource.Blue, - GalTextureSource WSource = GalTextureSource.Alpha) + GalTextureTarget TextureTarget, + int MaxMipmapLevel = 1, + GalTextureSource XSource = GalTextureSource.Red, + GalTextureSource YSource = GalTextureSource.Green, + GalTextureSource ZSource = GalTextureSource.Blue, + GalTextureSource WSource = GalTextureSource.Alpha) { this.Width = Width; this.Height = Height; + this.LayerCount = LayerCount; + this.Depth = Depth; this.TileWidth = TileWidth; this.GobBlockHeight = GobBlockHeight; + this.GobBlockDepth = GobBlockDepth; this.Layout = Layout; this.Format = Format; + this.MaxMipmapLevel = MaxMipmapLevel; this.XSource = XSource; this.YSource = YSource; this.ZSource = ZSource; this.WSource = WSource; + this.TextureTarget = TextureTarget; Pitch = ImageUtils.GetPitch(Format, Width); } - public bool SizeMatches(GalImage Image) + public bool SizeMatches(GalImage Image, bool IgnoreLayer = false) { if (ImageUtils.GetBytesPerPixel(Format) != ImageUtils.GetBytesPerPixel(Image.Format)) @@ -57,7 +74,14 @@ namespace Ryujinx.Graphics.Gal return false; } - return Height == Image.Height; + bool Result = Height == Image.Height && Depth == Image.Depth; + + if (!IgnoreLayer) + { + Result = Result && LayerCount == Image.LayerCount; + } + + return Result; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureSampler.cs b/Ryujinx.Graphics/Gal/GalTextureSampler.cs index b9e5c7658..1d658cea8 100644 --- a/Ryujinx.Graphics/Gal/GalTextureSampler.cs +++ b/Ryujinx.Graphics/Gal/GalTextureSampler.cs @@ -12,6 +12,9 @@ namespace Ryujinx.Graphics.Gal public GalColorF BorderColor { get; private set; } + public bool DepthCompare { get; private set; } + public DepthCompareFunc DepthCompareFunc { get; private set; } + public GalTextureSampler( GalTextureWrap AddressU, GalTextureWrap AddressV, @@ -19,7 +22,9 @@ namespace Ryujinx.Graphics.Gal GalTextureFilter MinFilter, GalTextureFilter MagFilter, GalTextureMipFilter MipFilter, - GalColorF BorderColor) + GalColorF BorderColor, + bool DepthCompare, + DepthCompareFunc DepthCompareFunc) { this.AddressU = AddressU; this.AddressV = AddressV; @@ -28,6 +33,9 @@ namespace Ryujinx.Graphics.Gal this.MagFilter = MagFilter; this.MipFilter = MipFilter; this.BorderColor = BorderColor; + + this.DepthCompare = DepthCompare; + this.DepthCompareFunc = DepthCompareFunc; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureTarget.cs b/Ryujinx.Graphics/Gal/GalTextureTarget.cs new file mode 100644 index 000000000..bcc0c49a5 --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalTextureTarget.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Graphics.Gal +{ + public enum GalTextureTarget + { + OneD = 0, + TwoD = 1, + ThreeD = 2, + CubeMap = 3, + OneDArray = 4, + TwoDArray = 5, + OneDBuffer = 6, + TwoDNoMipMap = 7, + CubeArray = 8, + } +} diff --git a/Ryujinx.Graphics/Gal/IGalRenderTarget.cs b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs index f941ccd58..90cad856d 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderTarget.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs @@ -25,16 +25,20 @@ namespace Ryujinx.Graphics.Gal void Render(); void Copy( - long SrcKey, - long DstKey, - int SrcX0, - int SrcY0, - int SrcX1, - int SrcY1, - int DstX0, - int DstY0, - int DstX1, - int DstY1); + GalImage SrcImage, + GalImage DstImage, + long SrcKey, + long DstKey, + int SrcLayer, + int DstLayer, + int SrcX0, + int SrcY0, + int SrcX1, + int SrcY1, + int DstX0, + int DstY0, + int DstX1, + int DstY1); void Reinterpret(long Key, GalImage NewImage); } diff --git a/Ryujinx.Graphics/Gal/IGalTexture.cs b/Ryujinx.Graphics/Gal/IGalTexture.cs index aeecdf1ac..de4ba9cba 100644 --- a/Ryujinx.Graphics/Gal/IGalTexture.cs +++ b/Ryujinx.Graphics/Gal/IGalTexture.cs @@ -13,6 +13,6 @@ namespace Ryujinx.Graphics.Gal void Bind(long Key, int Index, GalImage Image); - void SetSampler(GalTextureSampler Sampler); + void SetSampler(GalImage Image, GalTextureSampler Sampler); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs b/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs index 8db0b8a8c..5714f3d89 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs @@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL public int Width => Image.Width; public int Height => Image.Height; + public int Depth => Image.Depth; public GalImageFormat Format => Image.Format; diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index f2afe7b55..3a25fff7a 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -189,6 +189,31 @@ namespace Ryujinx.Graphics.Gal.OpenGL throw new NotImplementedException($"{Format & GalImageFormat.FormatMask} {Format & GalImageFormat.TypeMask}"); } + public static All GetDepthCompareFunc(DepthCompareFunc DepthCompareFunc) + { + switch (DepthCompareFunc) + { + case DepthCompareFunc.LEqual: + return All.Lequal; + case DepthCompareFunc.GEqual: + return All.Gequal; + case DepthCompareFunc.Less: + return All.Less; + case DepthCompareFunc.Greater: + return All.Greater; + case DepthCompareFunc.Equal: + return All.Equal; + case DepthCompareFunc.NotEqual: + return All.Notequal; + case DepthCompareFunc.Always: + return All.Always; + case DepthCompareFunc.Never: + return All.Never; + default: + throw new ArgumentException(nameof(DepthCompareFunc) + " \"" + DepthCompareFunc + "\" is not valid!"); + } + } + public static InternalFormat GetCompressedImageFormat(GalImageFormat Format) { switch (Format) diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs index 11daeb593..52b3d0ce3 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs @@ -9,9 +9,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL private static Lazy s_TextureMirrorClamp = new Lazy(() => HasExtension("GL_EXT_texture_mirror_clamp")); private static Lazy s_ViewportArray = new Lazy(() => HasExtension("GL_ARB_viewport_array")); + private static Lazy s_NvidiaDriver = new Lazy(() => IsNvidiaDriver()); + public static bool EnhancedLayouts => s_EnhancedLayouts.Value; public static bool TextureMirrorClamp => s_TextureMirrorClamp.Value; public static bool ViewportArray => s_ViewportArray.Value; + public static bool NvidiaDrvier => s_NvidiaDriver.Value; private static bool HasExtension(string Name) { @@ -27,5 +30,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL return false; } + + private static bool IsNvidiaDriver() { + return GL.GetString(StringName.Vendor).Equals("NVIDIA Corporation"); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs index 0d7bb3cd0..8dd3b37fc 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs @@ -389,16 +389,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL } public void Copy( - long SrcKey, - long DstKey, - int SrcX0, - int SrcY0, - int SrcX1, - int SrcY1, - int DstX0, - int DstY0, - int DstX1, - int DstY1) + GalImage SrcImage, + GalImage DstImage, + long SrcKey, + long DstKey, + int SrcLayer, + int DstLayer, + int SrcX0, + int SrcY0, + int SrcX1, + int SrcY1, + int DstX0, + int DstY0, + int DstX1, + int DstY1) { if (Texture.TryGetImageHandler(SrcKey, out ImageHandler SrcTex) && Texture.TryGetImageHandler(DstKey, out ImageHandler DstTex)) @@ -425,8 +429,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL FramebufferAttachment Attachment = GetAttachment(SrcTex); - GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0); - GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0); + if (ImageUtils.IsArray(SrcImage.TextureTarget) && SrcLayer > 0) + { + GL.FramebufferTextureLayer(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0, SrcLayer); + } + else + { + GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0); + } + + if (ImageUtils.IsArray(DstImage.TextureTarget) && DstLayer > 0) + { + GL.FramebufferTextureLayer(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0, DstLayer); + } + else + { + GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0); + } + BlitFramebufferFilter Filter = BlitFramebufferFilter.Nearest; @@ -452,7 +472,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL if (NewImage.Format == OldImage.Format && NewImage.Width == OldImage.Width && - NewImage.Height == OldImage.Height) + NewImage.Height == OldImage.Height && + NewImage.Depth == OldImage.Depth && + NewImage.LayerCount == OldImage.LayerCount && + NewImage.TextureTarget == OldImage.TextureTarget) { return; } @@ -477,9 +500,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL (_, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(CachedImage.Format); - GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle); + TextureTarget Target = ImageUtils.GetTextureTarget(NewImage.TextureTarget); - GL.GetTexImage(TextureTarget.Texture2D, 0, Format, Type, IntPtr.Zero); + GL.BindTexture(Target, CachedImage.Handle); + + GL.GetTexImage(Target, 0, Format, Type, IntPtr.Zero); GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyPBO); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index 10a9120df..dc168ff91 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GlslProgram Program; - GlslDecompiler Decompiler = new GlslDecompiler(OGLLimit.MaxUboSize); + GlslDecompiler Decompiler = new GlslDecompiler(OGLLimit.MaxUboSize, OGLExtension.NvidiaDrvier); int ShaderDumpIndex = ShaderDumper.DumpIndex; diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs index ef984b1ed..4fef11d29 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs @@ -38,7 +38,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL { int Handle = GL.GenTexture(); - GL.BindTexture(TextureTarget.Texture2D, Handle); + TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget); + + GL.BindTexture(Target, Handle); const int Level = 0; //TODO: Support mipmap textures. const int Border = 0; @@ -54,23 +56,70 @@ namespace Ryujinx.Graphics.Gal.OpenGL PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format); - GL.TexImage2D( - TextureTarget.Texture2D, - Level, - InternalFmt, - Image.Width, - Image.Height, - Border, - Format, - Type, - IntPtr.Zero); + switch (Target) + { + case TextureTarget.Texture1D: + GL.TexImage1D( + Target, + Level, + InternalFmt, + Image.Width, + Border, + Format, + Type, + IntPtr.Zero); + break; + + case TextureTarget.Texture2D: + GL.TexImage2D( + Target, + Level, + InternalFmt, + Image.Width, + Image.Height, + Border, + Format, + Type, + IntPtr.Zero); + break; + case TextureTarget.Texture3D: + GL.TexImage3D( + Target, + Level, + InternalFmt, + Image.Width, + Image.Height, + Image.Depth, + Border, + Format, + Type, + IntPtr.Zero); + break; + case TextureTarget.Texture2DArray: + GL.TexImage3D( + Target, + Level, + InternalFmt, + Image.Width, + Image.Height, + Image.LayerCount, + Border, + Format, + Type, + IntPtr.Zero); + break; + default: + throw new NotImplementedException($"Unsupported texture target type: {Target}"); + } } public void Create(long Key, byte[] Data, GalImage Image) { int Handle = GL.GenTexture(); - GL.BindTexture(TextureTarget.Texture2D, Handle); + TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget); + + GL.BindTexture(Target, Handle); const int Level = 0; //TODO: Support mipmap textures. const int Border = 0; @@ -81,15 +130,56 @@ namespace Ryujinx.Graphics.Gal.OpenGL { InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format); - GL.CompressedTexImage2D( - TextureTarget.Texture2D, - Level, - InternalFmt, - Image.Width, - Image.Height, - Border, - Data.Length, - Data); + switch (Target) + { + case TextureTarget.Texture1D: + GL.CompressedTexImage1D( + Target, + Level, + InternalFmt, + Image.Width, + Border, + Data.Length, + Data); + break; + case TextureTarget.Texture2D: + GL.CompressedTexImage2D( + Target, + Level, + InternalFmt, + Image.Width, + Image.Height, + Border, + Data.Length, + Data); + break; + case TextureTarget.Texture3D: + GL.CompressedTexImage3D( + Target, + Level, + InternalFmt, + Image.Width, + Image.Height, + Image.Depth, + Border, + Data.Length, + Data); + break; + case TextureTarget.Texture2DArray: + GL.CompressedTexImage3D( + Target, + Level, + InternalFmt, + Image.Width, + Image.Height, + Image.LayerCount, + Border, + Data.Length, + Data); + break; + default: + throw new NotImplementedException($"Unsupported texture target type: {Target}"); + } } else { @@ -98,13 +188,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL { int TextureBlockWidth = ImageUtils.GetBlockWidth(Image.Format); int TextureBlockHeight = ImageUtils.GetBlockHeight(Image.Format); + int TextureBlockDepth = ImageUtils.GetBlockDepth(Image.Format); Data = ASTCDecoder.DecodeToRGBA8888( Data, TextureBlockWidth, - TextureBlockHeight, 1, + TextureBlockHeight, + TextureBlockDepth, Image.Width, - Image.Height, 1); + Image.Height, + Image.Depth); Image.Format = GalImageFormat.RGBA8 | (Image.Format & GalImageFormat.TypeMask); } @@ -113,16 +206,80 @@ namespace Ryujinx.Graphics.Gal.OpenGL PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format); - GL.TexImage2D( - TextureTarget.Texture2D, - Level, - InternalFmt, - Image.Width, - Image.Height, - Border, - Format, - Type, - Data); + + switch (Target) + { + case TextureTarget.Texture1D: + GL.TexImage1D( + Target, + Level, + InternalFmt, + Image.Width, + Border, + Format, + Type, + Data); + break; + case TextureTarget.Texture2D: + GL.TexImage2D( + Target, + Level, + InternalFmt, + Image.Width, + Image.Height, + Border, + Format, + Type, + Data); + break; + case TextureTarget.Texture3D: + GL.TexImage3D( + Target, + Level, + InternalFmt, + Image.Width, + Image.Height, + Image.Depth, + Border, + Format, + Type, + Data); + break; + case TextureTarget.Texture2DArray: + GL.TexImage3D( + Target, + Level, + InternalFmt, + Image.Width, + Image.Height, + Image.LayerCount, + Border, + Format, + Type, + Data); + break; + case TextureTarget.TextureCubeMap: + Span Array = new Span(Data); + + int FaceSize = ImageUtils.GetSize(Image) / 6; + + for (int Face = 0; Face < 6; Face++) + { + GL.TexImage2D( + TextureTarget.TextureCubeMapPositiveX + Face, + Level, + InternalFmt, + Image.Width, + Image.Height, + Border, + Format, + Type, + Array.Slice(Face * FaceSize, FaceSize).ToArray()); + } + break; + default: + throw new NotImplementedException($"Unsupported texture target type: {Target}"); + } } } @@ -165,7 +322,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GL.ActiveTexture(TextureUnit.Texture0 + Index); - GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle); + TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget); + + GL.BindTexture(Target, CachedImage.Handle); int[] SwizzleRgba = new int[] { @@ -175,23 +334,27 @@ namespace Ryujinx.Graphics.Gal.OpenGL (int)OGLEnumConverter.GetTextureSwizzle(Image.WSource) }; - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleRgba, SwizzleRgba); + GL.TexParameter(Target, TextureParameterName.TextureSwizzleRgba, SwizzleRgba); } } - public void SetSampler(GalTextureSampler Sampler) + public void SetSampler(GalImage Image, GalTextureSampler Sampler) { int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU); int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV); + int WrapR = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressP); int MinFilter = (int)OGLEnumConverter.GetTextureMinFilter(Sampler.MinFilter, Sampler.MipFilter); int MagFilter = (int)OGLEnumConverter.GetTextureMagFilter(Sampler.MagFilter); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, WrapS); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, WrapT); + TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter); + GL.TexParameter(Target, TextureParameterName.TextureWrapS, WrapS); + GL.TexParameter(Target, TextureParameterName.TextureWrapT, WrapT); + GL.TexParameter(Target, TextureParameterName.TextureWrapR, WrapR); + + GL.TexParameter(Target, TextureParameterName.TextureMinFilter, MinFilter); + GL.TexParameter(Target, TextureParameterName.TextureMagFilter, MagFilter); float[] Color = new float[] { @@ -201,7 +364,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL Sampler.BorderColor.Alpha }; - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBorderColor, Color); + GL.TexParameter(Target, TextureParameterName.TextureBorderColor, Color); + + if (Sampler.DepthCompare) + { + GL.TexParameter(Target, TextureParameterName.TextureCompareMode, (int)All.CompareRToTexture); + GL.TexParameter(Target, TextureParameterName.TextureCompareFunc, (int)OGLEnumConverter.GetDepthCompareFunc(Sampler.DepthCompareFunc)); + } + else + { + GL.TexParameter(Target, TextureParameterName.TextureCompareMode, (int)All.None); + GL.TexParameter(Target, TextureParameterName.TextureCompareFunc, (int)All.Never); + } } } } diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs index 43923da74..f7ae34faa 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs @@ -1,3 +1,5 @@ +using Ryujinx.Graphics.Gal.OpenGL; +using Ryujinx.Graphics.Texture; using System; using System.Collections.Generic; @@ -224,6 +226,7 @@ namespace Ryujinx.Graphics.Gal.Shader if (Op.Inst == ShaderIrInst.Texq || Op.Inst == ShaderIrInst.Texs || + Op.Inst == ShaderIrInst.Tld4 || Op.Inst == ShaderIrInst.Txlf) { int Handle = ((ShaderIrOperImm)Op.OperandC).Value; @@ -232,7 +235,25 @@ namespace Ryujinx.Graphics.Gal.Shader string Name = StagePrefix + TextureName + Index; - m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Handle)); + GalTextureTarget TextureTarget; + + TextureInstructionSuffix TextureInstructionSuffix; + + // TODO: Non 2D texture type for TEXQ? + if (Op.Inst == ShaderIrInst.Texq) + { + TextureTarget = GalTextureTarget.TwoD; + TextureInstructionSuffix = TextureInstructionSuffix.None; + } + else + { + ShaderIrMetaTex Meta = ((ShaderIrMetaTex)Op.MetaData); + + TextureTarget = Meta.TextureTarget; + TextureInstructionSuffix = Meta.TextureInstructionSuffix; + } + + m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Handle, false, 0, 1, TextureTarget, TextureInstructionSuffix)); } else if (Op.Inst == ShaderIrInst.Texb) { @@ -257,9 +278,10 @@ namespace Ryujinx.Graphics.Gal.Shader if (HandleSrc != null && HandleSrc is ShaderIrOperCbuf Cbuf) { + ShaderIrMetaTex Meta = ((ShaderIrMetaTex)Op.MetaData); string Name = StagePrefix + TextureName + "_cb" + Cbuf.Index + "_" + Cbuf.Pos; - m_CbTextures.Add(Op, new ShaderDeclInfo(Name, Cbuf.Pos, true, Cbuf.Index)); + m_CbTextures.Add(Op, new ShaderDeclInfo(Name, Cbuf.Pos, true, Cbuf.Index, 1, Meta.TextureTarget, Meta.TextureInstructionSuffix)); } else { diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 854c827ee..5f809525f 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -1,3 +1,5 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.Texture; using System; using System.Collections.Generic; using System.Globalization; @@ -33,7 +35,9 @@ namespace Ryujinx.Graphics.Gal.Shader public int MaxUboSize { get; } - public GlslDecompiler(int MaxUboSize) + private bool IsNvidiaDriver; + + public GlslDecompiler(int MaxUboSize, bool IsNvidiaDriver) { InstsExpr = new Dictionary() { @@ -103,6 +107,7 @@ namespace Ryujinx.Graphics.Gal.Shader { ShaderIrInst.Texb, GetTexbExpr }, { ShaderIrInst.Texq, GetTexqExpr }, { ShaderIrInst.Texs, GetTexsExpr }, + { ShaderIrInst.Tld4, GetTld4Expr }, { ShaderIrInst.Trunc, GetTruncExpr }, { ShaderIrInst.Txlf, GetTxlfExpr }, { ShaderIrInst.Utof, GetUtofExpr }, @@ -110,6 +115,7 @@ namespace Ryujinx.Graphics.Gal.Shader }; this.MaxUboSize = MaxUboSize / 16; + this.IsNvidiaDriver = IsNvidiaDriver; } public GlslProgram Decompile( @@ -219,14 +225,70 @@ namespace Ryujinx.Graphics.Gal.Shader } } + private string GetSamplerType(TextureTarget TextureTarget, bool HasShadow) + { + string Result; + + switch (TextureTarget) + { + case TextureTarget.Texture1D: + Result = "sampler1D"; + break; + case TextureTarget.Texture2D: + Result = "sampler2D"; + break; + case TextureTarget.Texture3D: + Result = "sampler3D"; + break; + case TextureTarget.TextureCubeMap: + Result = "samplerCube"; + break; + case TextureTarget.TextureRectangle: + Result = "sampler2DRect"; + break; + case TextureTarget.Texture1DArray: + Result = "sampler1DArray"; + break; + case TextureTarget.Texture2DArray: + Result = "sampler2DArray"; + break; + case TextureTarget.TextureCubeMapArray: + Result = "samplerCubeArray"; + break; + case TextureTarget.TextureBuffer: + Result = "samplerBuffer"; + break; + case TextureTarget.Texture2DMultisample: + Result = "sampler2DMS"; + break; + case TextureTarget.Texture2DMultisampleArray: + Result = "sampler2DMSArray"; + break; + default: + throw new NotSupportedException(); + } + + if (HasShadow) + Result += "Shadow"; + + return Result; + } + private void PrintDeclTextures() { foreach (ShaderDeclInfo DeclInfo in IterateCbTextures()) { - SB.AppendLine("uniform sampler2D " + DeclInfo.Name + ";"); + TextureTarget Target = ImageUtils.GetTextureTarget(DeclInfo.TextureTarget); + SB.AppendLine($"// {DeclInfo.TextureSuffix}"); + SB.AppendLine("uniform " + GetSamplerType(Target, (DeclInfo.TextureSuffix & TextureInstructionSuffix.DC) != 0) + " " + DeclInfo.Name + ";"); } - PrintDecls(Decl.Textures, "uniform sampler2D"); + foreach (ShaderDeclInfo DeclInfo in Decl.Textures.Values.OrderBy(DeclKeySelector)) + { + TextureTarget Target = ImageUtils.GetTextureTarget(DeclInfo.TextureTarget); + SB.AppendLine($"// {DeclInfo.TextureSuffix}"); + SB.AppendLine("uniform " + GetSamplerType(Target, (DeclInfo.TextureSuffix & TextureInstructionSuffix.DC) != 0) + " " + DeclInfo.Name + ";"); + } } private IEnumerable IterateCbTextures() @@ -778,6 +840,7 @@ namespace Ryujinx.Graphics.Gal.Shader case ShaderIrInst.Ipa: case ShaderIrInst.Texq: case ShaderIrInst.Texs: + case ShaderIrInst.Tld4: case ShaderIrInst.Txlf: return false; } @@ -1124,7 +1187,7 @@ namespace Ryujinx.Graphics.Gal.Shader string Ch = "rgba".Substring(Meta.Elem, 1); - return "texture(" + DeclInfo.Name + ", " + Coords + ")." + Ch; + return GetTextureOperation(Op, DeclInfo.Name, Coords, Ch); } private string GetTexqExpr(ShaderIrOp Op) @@ -1157,20 +1220,50 @@ namespace Ryujinx.Graphics.Gal.Shader string Ch = "rgba".Substring(Meta.Elem, 1); - return "texture(" + Sampler + ", " + Coords + ")." + Ch; + return GetTextureOperation(Op, Sampler, Coords, Ch); } - private string GetTxlfExpr(ShaderIrOp Op) + private string GetTld4Expr(ShaderIrOp Op) { ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; string Sampler = GetTexSamplerName(Op); + string Coords = GetTexSamplerCoords(Op); + + string Ch = "rgba".Substring(Meta.Elem, 1); + + return GetTextureGatherOperation(Op, Sampler, Coords, Ch); + } + + // TODO: support AOFFI on non nvidia drivers + private string GetTxlfExpr(ShaderIrOp Op) + { + // TODO: Support all suffixes + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + TextureInstructionSuffix Suffix = Meta.TextureInstructionSuffix; + + string Sampler = GetTexSamplerName(Op); + string Coords = GetITexSamplerCoords(Op); string Ch = "rgba".Substring(Meta.Elem, 1); - return "texelFetch(" + Sampler + ", " + Coords + ", 0)." + Ch; + string Lod = "0"; + + if (Meta.LevelOfDetail != null) + { + Lod = GetOperExpr(Op, Meta.LevelOfDetail); + } + + if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver) + { + string Offset = GetTextureOffset(Meta, GetOperExpr(Op, Meta.Offset)); + return "texelFetchOffset(" + Sampler + ", " + Coords + ", " + Lod + ", " + Offset + ")." + Ch; + } + + return "texelFetch(" + Sampler + ", " + Coords + ", " + Lod + ")." + Ch; } private string GetTruncExpr(ShaderIrOp Op) => GetUnaryCall(Op, "trunc"); @@ -1246,14 +1339,205 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetTexSamplerCoords(ShaderIrOp Op) { - return "vec2(" + GetOperExpr(Op, Op.OperandA) + ", " + - GetOperExpr(Op, Op.OperandB) + ")"; + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + bool HasDepth = (Meta.TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0; + + int Coords = ImageUtils.GetCoordsCountTextureTarget(Meta.TextureTarget); + + bool IsArray = ImageUtils.IsArray(Meta.TextureTarget); + + + string GetLastArgument(ShaderIrNode Node) + { + string Result = GetOperExpr(Op, Node); + + // array index is actually an integer so we need to pass it correctly + if (IsArray) + { + Result = "float(floatBitsToInt(" + Result + "))"; + } + + return Result; + } + + string LastArgument; + string DepthArgument = ""; + + int VecSize = Coords; + if (HasDepth && Op.Inst != ShaderIrInst.Tld4) + { + VecSize++; + DepthArgument = $", {GetOperExpr(Op, Meta.DepthCompare)}"; + } + + switch (Coords) + { + case 1: + if (HasDepth) + { + return $"vec3({GetOperExpr(Op, Meta.Coordinates[0])}, 0.0{DepthArgument})"; + } + + return GetOperExpr(Op, Meta.Coordinates[0]); + case 2: + LastArgument = GetLastArgument(Meta.Coordinates[1]); + + return $"vec{VecSize}({GetOperExpr(Op, Meta.Coordinates[0])}, {LastArgument}{DepthArgument})"; + case 3: + LastArgument = GetLastArgument(Meta.Coordinates[2]); + + return $"vec{VecSize}({GetOperExpr(Op, Meta.Coordinates[0])}, {GetOperExpr(Op, Meta.Coordinates[1])}, {LastArgument}{DepthArgument})"; + case 4: + LastArgument = GetLastArgument(Meta.Coordinates[3]); + + return $"vec4({GetOperExpr(Op, Meta.Coordinates[0])}, {GetOperExpr(Op, Meta.Coordinates[1])}, {GetOperExpr(Op, Meta.Coordinates[2])}, {LastArgument}){DepthArgument}"; + default: + throw new InvalidOperationException(); + } + + } + + private string GetTextureOffset(ShaderIrMetaTex Meta, string Oper, int Shift = 4, int Mask = 0xF) + { + string GetOffset(string Operation, int Index) + { + return $"({Operation} >> {Index * Shift}) & 0x{Mask:x}"; + } + + int Coords = ImageUtils.GetCoordsCountTextureTarget(Meta.TextureTarget); + + if (ImageUtils.IsArray(Meta.TextureTarget)) + Coords -= 1; + + switch (Coords) + { + case 1: + return GetOffset(Oper, 0); + case 2: + return "ivec2(" + GetOffset(Oper, 0) + ", " + GetOffset(Oper, 1) + ")"; + case 3: + return "ivec3(" + GetOffset(Oper, 0) + ", " + GetOffset(Oper, 1) + ", " + GetOffset(Oper, 2) + ")"; + case 4: + return "ivec4(" + GetOffset(Oper, 0) + ", " + GetOffset(Oper, 1) + ", " + GetOffset(Oper, 2) + ", " + GetOffset(Oper, 3) + ")"; + default: + throw new InvalidOperationException(); + } + } + + // TODO: support AOFFI on non nvidia drivers + private string GetTextureGatherOperation(ShaderIrOp Op, string Sampler, string Coords, string Ch) + { + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + TextureInstructionSuffix Suffix = Meta.TextureInstructionSuffix; + + string ChString = "." + Ch; + + string Comp = Meta.Component.ToString(); + + if ((Suffix & TextureInstructionSuffix.DC) != 0) + { + Comp = GetOperExpr(Op, Meta.DepthCompare); + } + + if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver) + { + string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))", 8, 0x3F); + + if ((Suffix & TextureInstructionSuffix.DC) != 0) + { + return "textureGatherOffset(" + Sampler + ", " + Coords + ", " + Comp + ", " + Offset + ")" + ChString; + } + + return "textureGatherOffset(" + Sampler + ", " + Coords + ", " + Offset + ", " + Comp + ")" + ChString; + } + // TODO: Support PTP + else if ((Suffix & TextureInstructionSuffix.PTP) != 0) + { + throw new NotImplementedException(); + } + + return "textureGather(" + Sampler + ", " + Coords + ", " + Comp + ")" + ChString; + } + + // TODO: support AOFFI on non nvidia drivers + private string GetTextureOperation(ShaderIrOp Op, string Sampler, string Coords, string Ch) + { + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + TextureInstructionSuffix Suffix = Meta.TextureInstructionSuffix; + + string ChString = "." + Ch; + + if ((Suffix & TextureInstructionSuffix.DC) != 0) + { + ChString = ""; + } + + // TODO: Support LBA and LLA + if ((Suffix & TextureInstructionSuffix.LZ) != 0) + { + if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver) + { + string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))"); + + return "textureLodOffset(" + Sampler + ", " + Coords + ", 0.0, " + Offset + ")" + ChString; + } + + return "textureLod(" + Sampler + ", " + Coords + ", 0.0)" + ChString; + } + else if ((Suffix & TextureInstructionSuffix.LB) != 0) + { + if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver) + { + string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))"); + + return "textureOffset(" + Sampler + ", " + Coords + ", " + Offset + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ")" + ChString; + } + + return "texture(" + Sampler + ", " + Coords + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ")" + ChString; + } + else if ((Suffix & TextureInstructionSuffix.LL) != 0) + { + if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver) + { + string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))"); + + return "textureLodOffset(" + Sampler + ", " + Coords + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ", " + Offset + ")" + ChString; + } + + return "textureLod(" + Sampler + ", " + Coords + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ")" + ChString; + } + else if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver) + { + string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))"); + + return "textureOffset(" + Sampler + ", " + Coords + ", " + Offset + ")" + ChString; + } + else + { + return "texture(" + Sampler + ", " + Coords + ")" + ChString; + } + throw new NotImplementedException($"Texture Suffix {Meta.TextureInstructionSuffix} is not implemented"); + } private string GetITexSamplerCoords(ShaderIrOp Op) { - return "ivec2(" + GetOperExpr(Op, Op.OperandA) + ", " + - GetOperExpr(Op, Op.OperandB) + ")"; + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + switch (ImageUtils.GetCoordsCountTextureTarget(Meta.TextureTarget)) + { + case 1: + return GetOperExpr(Op, Meta.Coordinates[0]); + case 2: + return "ivec2(" + GetOperExpr(Op, Meta.Coordinates[0]) + ", " + GetOperExpr(Op, Meta.Coordinates[1]) + ")"; + case 3: + return "ivec3(" + GetOperExpr(Op, Meta.Coordinates[0]) + ", " + GetOperExpr(Op, Meta.Coordinates[1]) + ", " + GetOperExpr(Op, Meta.Coordinates[2]) + ")"; + default: + throw new InvalidOperationException(); + } } private string GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper) @@ -1292,22 +1576,6 @@ namespace Ryujinx.Graphics.Gal.Shader } break; } - - case ShaderIrOperImm Imm: - { - //For integer immediates being used as float, - //it's better (for readability) to just return the float value. - if (DstType == OperType.F32) - { - float Value = BitConverter.Int32BitsToSingle(Imm.Value); - - if (!float.IsNaN(Value) && !float.IsInfinity(Value)) - { - return GetFloatConst(Value); - } - } - break; - } } switch (DstType) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs index adcc47b95..8b4eacdf2 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs @@ -1,3 +1,4 @@ +using Ryujinx.Graphics.Texture; using System; using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper; @@ -29,6 +30,75 @@ namespace Ryujinx.Graphics.Gal.Shader { RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ } }; + private static GalTextureTarget TexToTextureTarget(int TexType, bool IsArray) + { + switch (TexType) + { + case 0: + return IsArray ? GalTextureTarget.OneDArray : GalTextureTarget.OneD; + case 2: + return IsArray ? GalTextureTarget.TwoDArray : GalTextureTarget.TwoD; + case 4: + if (IsArray) + throw new InvalidOperationException($"ARRAY bit set on a TEX with 3D texture!"); + return GalTextureTarget.ThreeD; + case 6: + return IsArray ? GalTextureTarget.CubeArray : GalTextureTarget.CubeMap; + default: + throw new InvalidOperationException(); + } + } + + private static GalTextureTarget TexsToTextureTarget(int TexType) + { + switch (TexType) + { + case 0: + return GalTextureTarget.OneD; + case 2: + case 4: + case 6: + case 8: + case 0xa: + case 0xc: + return GalTextureTarget.TwoD; + case 0xe: + case 0x10: + case 0x12: + return GalTextureTarget.TwoDArray; + case 0x14: + case 0x16: + return GalTextureTarget.ThreeD; + case 0x18: + case 0x1a: + return GalTextureTarget.CubeMap; + default: + throw new InvalidOperationException(); + } + } + + public static GalTextureTarget TldsToTextureTarget(int TexType) + { + switch (TexType) + { + case 0: + case 2: + return GalTextureTarget.OneD; + case 4: + case 8: + case 0xa: + case 0xc: + case 0x18: + return GalTextureTarget.TwoD; + case 0x10: + return GalTextureTarget.TwoDArray; + case 0xe: + return GalTextureTarget.ThreeD; + default: + throw new InvalidOperationException(); + } + } + public static void Ld_A(ShaderIrBlock Block, long OpCode, int Position) { ShaderIrNode[] Opers = OpCode.Abuf20(); @@ -132,43 +202,166 @@ namespace Ryujinx.Graphics.Gal.Shader public static void Tex(ShaderIrBlock Block, long OpCode, int Position) { - EmitTex(Block, OpCode, GprHandle: false); + TextureInstructionSuffix Suffix; + + int RawSuffix = OpCode.Read(0x34, 0x38); + + switch (RawSuffix) + { + case 0: + Suffix = TextureInstructionSuffix.None; + break; + case 0x8: + Suffix = TextureInstructionSuffix.LZ; + break; + case 0x10: + Suffix = TextureInstructionSuffix.LB; + break; + case 0x18: + Suffix = TextureInstructionSuffix.LL; + break; + case 0x30: + Suffix = TextureInstructionSuffix.LBA; + break; + case 0x38: + Suffix = TextureInstructionSuffix.LLA; + break; + default: + throw new InvalidOperationException($"Invalid Suffix for TEX instruction {RawSuffix}"); + } + + bool IsOffset = OpCode.Read(0x36); + + if (IsOffset) + Suffix |= TextureInstructionSuffix.AOffI; + + EmitTex(Block, OpCode, Suffix, GprHandle: false); } public static void Tex_B(ShaderIrBlock Block, long OpCode, int Position) { - EmitTex(Block, OpCode, GprHandle: true); + TextureInstructionSuffix Suffix; + + int RawSuffix = OpCode.Read(0x24, 0xe); + + switch (RawSuffix) + { + case 0: + Suffix = TextureInstructionSuffix.None; + break; + case 0x2: + Suffix = TextureInstructionSuffix.LZ; + break; + case 0x4: + Suffix = TextureInstructionSuffix.LB; + break; + case 0x6: + Suffix = TextureInstructionSuffix.LL; + break; + case 0xc: + Suffix = TextureInstructionSuffix.LBA; + break; + case 0xe: + Suffix = TextureInstructionSuffix.LLA; + break; + default: + throw new InvalidOperationException($"Invalid Suffix for TEX.B instruction {RawSuffix}"); + } + + bool IsOffset = OpCode.Read(0x23); + + if (IsOffset) + Suffix |= TextureInstructionSuffix.AOffI; + + EmitTex(Block, OpCode, Suffix, GprHandle: true); } - private static void EmitTex(ShaderIrBlock Block, long OpCode, bool GprHandle) + private static void EmitTex(ShaderIrBlock Block, long OpCode, TextureInstructionSuffix TextureInstructionSuffix, bool GprHandle) { - //TODO: Support other formats. - ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[2]; + bool IsArray = OpCode.HasArray(); - for (int Index = 0; Index < Coords.Length; Index++) + GalTextureTarget TextureTarget = TexToTextureTarget(OpCode.Read(28, 6), IsArray); + + bool HasDepthCompare = OpCode.Read(0x32); + + if (HasDepthCompare) + { + TextureInstructionSuffix |= TextureInstructionSuffix.DC; + } + + ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(TextureTarget)]; + + int IndexExtraCoord = 0; + + if (IsArray) + { + IndexExtraCoord++; + + Coords[Coords.Length - 1] = OpCode.Gpr8(); + } + + + for (int Index = 0; Index < Coords.Length - IndexExtraCoord; Index++) { ShaderIrOperGpr CoordReg = OpCode.Gpr8(); CoordReg.Index += Index; + CoordReg.Index += IndexExtraCoord; + if (!CoordReg.IsValidRegister) { CoordReg.Index = ShaderIrOperGpr.ZRIndex; } - Coords[Index] = ShaderIrOperGpr.MakeTemporary(Index); - - Block.AddNode(new ShaderIrAsg(Coords[Index], CoordReg)); + Coords[Index] = CoordReg; } int ChMask = OpCode.Read(31, 0xf); + ShaderIrOperGpr LevelOfDetail = null; + ShaderIrOperGpr Offset = null; + ShaderIrOperGpr DepthCompare = null; + + // TODO: determine first argument when TEX.B is used + int OperBIndex = GprHandle ? 1 : 0; + + if ((TextureInstructionSuffix & TextureInstructionSuffix.LL) != 0 || + (TextureInstructionSuffix & TextureInstructionSuffix.LB) != 0 || + (TextureInstructionSuffix & TextureInstructionSuffix.LBA) != 0 || + (TextureInstructionSuffix & TextureInstructionSuffix.LLA) != 0) + { + LevelOfDetail = OpCode.Gpr20(); + LevelOfDetail.Index += OperBIndex; + + OperBIndex++; + } + + if ((TextureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0) + { + Offset = OpCode.Gpr20(); + Offset.Index += OperBIndex; + + OperBIndex++; + } + + if ((TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0) + { + DepthCompare = OpCode.Gpr20(); + DepthCompare.Index += OperBIndex; + + OperBIndex++; + } + + // ??? ShaderIrNode OperC = GprHandle ? (ShaderIrNode)OpCode.Gpr20() : (ShaderIrNode)OpCode.Imm13_36(); ShaderIrInst Inst = GprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs; + Coords = CoordsRegistersToTempRegisters(Block, Coords); + int RegInc = 0; for (int Ch = 0; Ch < 4; Ch++) @@ -187,9 +380,14 @@ namespace Ryujinx.Graphics.Gal.Shader continue; } - ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); + ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch, TextureTarget, TextureInstructionSuffix, Coords) + { + LevelOfDetail = LevelOfDetail, + Offset = Offset, + DepthCompare = DepthCompare + }; - ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta); + ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords.Length > 1 ? Coords[1] : null, OperC, Meta); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op))); } @@ -197,17 +395,238 @@ namespace Ryujinx.Graphics.Gal.Shader public static void Texs(ShaderIrBlock Block, long OpCode, int Position) { - EmitTexs(Block, OpCode, ShaderIrInst.Texs); + TextureInstructionSuffix Suffix; + + int RawSuffix = OpCode.Read(0x34, 0x1e); + + switch (RawSuffix) + { + case 0: + case 0x4: + case 0x10: + case 0x16: + Suffix = TextureInstructionSuffix.LZ; + break; + case 0x6: + case 0x1a: + Suffix = TextureInstructionSuffix.LL; + break; + case 0x8: + Suffix = TextureInstructionSuffix.DC; + break; + case 0x2: + case 0xe: + case 0x14: + case 0x18: + Suffix = TextureInstructionSuffix.None; + break; + case 0xa: + Suffix = TextureInstructionSuffix.LL | TextureInstructionSuffix.DC; + break; + case 0xc: + case 0x12: + Suffix = TextureInstructionSuffix.LZ | TextureInstructionSuffix.DC; + break; + default: + throw new InvalidOperationException($"Invalid Suffix for TEXS instruction {RawSuffix}"); + } + + GalTextureTarget TextureTarget = TexsToTextureTarget(OpCode.Read(52, 0x1e)); + + EmitTexs(Block, OpCode, ShaderIrInst.Texs, TextureTarget, Suffix); } public static void Tlds(ShaderIrBlock Block, long OpCode, int Position) { - EmitTexs(Block, OpCode, ShaderIrInst.Txlf); + TextureInstructionSuffix Suffix; + + int RawSuffix = OpCode.Read(0x34, 0x1e); + + switch (RawSuffix) + { + case 0: + case 0x4: + case 0x8: + Suffix = TextureInstructionSuffix.LZ | TextureInstructionSuffix.AOffI; + break; + case 0xc: + Suffix = TextureInstructionSuffix.LZ | TextureInstructionSuffix.MZ; + break; + case 0xe: + case 0x10: + Suffix = TextureInstructionSuffix.LZ; + break; + case 0x2: + case 0xa: + Suffix = TextureInstructionSuffix.LL; + break; + case 0x18: + Suffix = TextureInstructionSuffix.LL | TextureInstructionSuffix.AOffI; + break; + default: + throw new InvalidOperationException($"Invalid Suffix for TLDS instruction {RawSuffix}"); + } + + GalTextureTarget TextureTarget = TldsToTextureTarget(OpCode.Read(52, 0x1e)); + + EmitTexs(Block, OpCode, ShaderIrInst.Txlf, TextureTarget, Suffix); } - private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) + public static void Tld4(ShaderIrBlock Block, long OpCode, int Position) { - //TODO: Support other formats. + TextureInstructionSuffix Suffix; + + int RawSuffix = OpCode.Read(0x34, 0xc); + + switch (RawSuffix) + { + case 0: + Suffix = TextureInstructionSuffix.None; + break; + case 0x4: + Suffix = TextureInstructionSuffix.AOffI; + break; + case 0x8: + Suffix = TextureInstructionSuffix.PTP; + break; + default: + throw new InvalidOperationException($"Invalid Suffix for TLD4 instruction {RawSuffix}"); + } + + bool IsShadow = OpCode.Read(0x32); + + bool IsArray = OpCode.HasArray(); + int ChMask = OpCode.Read(31, 0xf); + + GalTextureTarget TextureTarget = TexToTextureTarget(OpCode.Read(28, 6), IsArray); + + if (IsShadow) + { + Suffix |= TextureInstructionSuffix.DC; + } + + EmitTld4(Block, OpCode, TextureTarget, Suffix, ChMask, OpCode.Read(0x38, 0x3), false); + } + + public static void Tld4s(ShaderIrBlock Block, long OpCode, int Position) + { + TextureInstructionSuffix Suffix = TextureInstructionSuffix.None; + + bool IsOffset = OpCode.Read(0x33); + bool IsShadow = OpCode.Read(0x32); + + if (IsOffset) + { + Suffix |= TextureInstructionSuffix.AOffI; + } + + if (IsShadow) + { + Suffix |= TextureInstructionSuffix.DC; + } + + // TLD4S seems to only support 2D textures with RGBA mask? + EmitTld4(Block, OpCode, GalTextureTarget.TwoD, Suffix, RGBA, OpCode.Read(0x34, 0x3), true); + } + + private static void EmitTexs(ShaderIrBlock Block, + long OpCode, + ShaderIrInst Inst, + GalTextureTarget TextureTarget, + TextureInstructionSuffix TextureInstructionSuffix) + { + if (Inst == ShaderIrInst.Txlf && TextureTarget == GalTextureTarget.CubeArray) + { + throw new InvalidOperationException("TLDS instructions cannot use CUBE modifier!"); + } + + bool IsArray = ImageUtils.IsArray(TextureTarget); + + ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(TextureTarget)]; + + ShaderIrOperGpr OperA = OpCode.Gpr8(); + ShaderIrOperGpr OperB = OpCode.Gpr20(); + + ShaderIrOperGpr SuffixExtra = OpCode.Gpr20(); + SuffixExtra.Index += 1; + + int CoordStartIndex = 0; + + if (IsArray) + { + CoordStartIndex++; + Coords[Coords.Length - 1] = OpCode.Gpr8(); + } + + switch (Coords.Length - CoordStartIndex) + { + case 1: + Coords[0] = OpCode.Gpr8(); + + break; + case 2: + Coords[0] = OpCode.Gpr8(); + Coords[0].Index += CoordStartIndex; + + break; + case 3: + Coords[0] = OpCode.Gpr8(); + Coords[0].Index += CoordStartIndex; + + Coords[1] = OpCode.Gpr8(); + Coords[1].Index += 1 + CoordStartIndex; + + break; + default: + throw new NotSupportedException($"{Coords.Length - CoordStartIndex} coords textures aren't supported in TEXS"); + } + + int OperBIndex = 0; + + ShaderIrOperGpr LevelOfDetail = null; + ShaderIrOperGpr Offset = null; + ShaderIrOperGpr DepthCompare = null; + + // OperB is always the last value + // Not applicable to 1d textures + if (Coords.Length - CoordStartIndex != 1) + { + Coords[Coords.Length - CoordStartIndex - 1] = OperB; + OperBIndex++; + } + + // Encoding of TEXS/TLDS is a bit special and change for 2d textures + // NOTE: OperA seems to hold at best two args. + // On 2D textures, if no suffix need an additional values, Y is stored in OperB, otherwise coords are in OperA and the additional values is in OperB. + if (TextureInstructionSuffix != TextureInstructionSuffix.None && TextureInstructionSuffix != TextureInstructionSuffix.LZ && TextureTarget == GalTextureTarget.TwoD) + { + Coords[Coords.Length - CoordStartIndex - 1] = OpCode.Gpr8(); + Coords[Coords.Length - CoordStartIndex - 1].Index += Coords.Length - CoordStartIndex - 1; + OperBIndex--; + } + + // TODO: Find what MZ does and what changes about the encoding (Maybe Multisample?) + if ((TextureInstructionSuffix & TextureInstructionSuffix.LL) != 0) + { + LevelOfDetail = OpCode.Gpr20(); + LevelOfDetail.Index += OperBIndex; + OperBIndex++; + } + + if ((TextureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0) + { + Offset = OpCode.Gpr20(); + Offset.Index += OperBIndex; + OperBIndex++; + } + + if ((TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0) + { + DepthCompare = OpCode.Gpr20(); + DepthCompare.Index += OperBIndex; + OperBIndex++; + } + int LutIndex; LutIndex = !OpCode.Gpr0().IsConst ? 1 : 0; @@ -276,12 +695,7 @@ namespace Ryujinx.Graphics.Gal.Shader } ShaderIrNode OperC = OpCode.Imm13_36(); - - ShaderIrOperGpr Coord0 = ShaderIrOperGpr.MakeTemporary(0); - ShaderIrOperGpr Coord1 = ShaderIrOperGpr.MakeTemporary(1); - - Block.AddNode(new ShaderIrAsg(Coord0, OpCode.Gpr8())); - Block.AddNode(new ShaderIrAsg(Coord1, OpCode.Gpr20())); + Coords = CoordsRegistersToTempRegisters(Block, Coords); for (int Ch = 0; Ch < 4; Ch++) { @@ -290,9 +704,13 @@ namespace Ryujinx.Graphics.Gal.Shader continue; } - ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); - - ShaderIrOp Op = new ShaderIrOp(Inst, Coord0, Coord1, OperC, Meta); + ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch, TextureTarget, TextureInstructionSuffix, Coords) + { + LevelOfDetail = LevelOfDetail, + Offset = Offset, + DepthCompare = DepthCompare + }; + ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta); ShaderIrOperGpr Dst = GetDst(); @@ -303,9 +721,156 @@ namespace Ryujinx.Graphics.Gal.Shader } } + private static void EmitTld4(ShaderIrBlock Block, long OpCode, GalTextureTarget TextureType, TextureInstructionSuffix TextureInstructionSuffix, int ChMask, int Component, bool Scalar) + { + ShaderIrOperGpr OperA = OpCode.Gpr8(); + ShaderIrOperGpr OperB = OpCode.Gpr20(); + ShaderIrOperImm OperC = OpCode.Imm13_36(); + + ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(TextureType)]; + + ShaderIrOperGpr Offset = null; + ShaderIrOperGpr DepthCompare = null; + + bool IsArray = ImageUtils.IsArray(TextureType); + + int OperBIndex = 0; + + if (Scalar) + { + int CoordStartIndex = 0; + + if (IsArray) + { + CoordStartIndex++; + Coords[Coords.Length - 1] = OperB; + } + + switch (Coords.Length - CoordStartIndex) + { + case 1: + Coords[0] = OpCode.Gpr8(); + + break; + case 2: + Coords[0] = OpCode.Gpr8(); + Coords[0].Index += CoordStartIndex; + + break; + case 3: + Coords[0] = OpCode.Gpr8(); + Coords[0].Index += CoordStartIndex; + + Coords[1] = OpCode.Gpr8(); + Coords[1].Index += 1 + CoordStartIndex; + + break; + default: + throw new NotSupportedException($"{Coords.Length - CoordStartIndex} coords textures aren't supported in TLD4S"); + } + + if (Coords.Length - CoordStartIndex != 1) + { + Coords[Coords.Length - CoordStartIndex - 1] = OperB; + OperBIndex++; + } + + if (TextureInstructionSuffix != TextureInstructionSuffix.None && TextureType == GalTextureTarget.TwoD) + { + Coords[Coords.Length - CoordStartIndex - 1] = OpCode.Gpr8(); + Coords[Coords.Length - CoordStartIndex - 1].Index += Coords.Length - CoordStartIndex - 1; + OperBIndex--; + } + } + else + { + int IndexExtraCoord = 0; + + if (IsArray) + { + IndexExtraCoord++; + + Coords[Coords.Length - 1] = OpCode.Gpr8(); + } + + for (int Index = 0; Index < Coords.Length - IndexExtraCoord; Index++) + { + Coords[Index] = OpCode.Gpr8(); + + Coords[Index].Index += Index; + + Coords[Index].Index += IndexExtraCoord; + + if (Coords[Index].Index > ShaderIrOperGpr.ZRIndex) + { + Coords[Index].Index = ShaderIrOperGpr.ZRIndex; + } + } + } + + if ((TextureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0) + { + Offset = OpCode.Gpr20(); + Offset.Index += OperBIndex; + OperBIndex++; + } + + if ((TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0) + { + DepthCompare = OpCode.Gpr20(); + DepthCompare.Index += OperBIndex; + OperBIndex++; + } + + Coords = CoordsRegistersToTempRegisters(Block, Coords); + + int RegInc = 0; + + for (int Ch = 0; Ch < 4; Ch++) + { + if (!IsChannelUsed(ChMask, Ch)) + { + continue; + } + + ShaderIrOperGpr Dst = OpCode.Gpr0(); + + Dst.Index += RegInc++; + + if (!Dst.IsValidRegister || Dst.IsConst) + { + continue; + } + + ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch, TextureType, TextureInstructionSuffix, Coords) + { + Component = Component, + Offset = Offset, + DepthCompare = DepthCompare + }; + + ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Tld4, OperA, OperB, OperC, Meta); + + Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op))); + } + } + private static bool IsChannelUsed(int ChMask, int Ch) { return (ChMask & (1 << Ch)) != 0; } + + private static ShaderIrOperGpr[] CoordsRegistersToTempRegisters(ShaderIrBlock Block, params ShaderIrOperGpr[] Registers) + { + ShaderIrOperGpr[] Res = new ShaderIrOperGpr[Registers.Length]; + + for (int Index = 0; Index < Res.Length; Index++) + { + Res[Index] = ShaderIrOperGpr.MakeTemporary(Index); + Block.AddNode(new ShaderIrAsg(Res[Index], Registers[Index])); + } + + return Res; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs index f0f92148e..e241e1ca5 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs @@ -19,6 +19,11 @@ namespace Ryujinx.Graphics.Gal.Shader return ((int)(OpCode >> 20) << 8) >> 8; } + private static bool HasArray(this long OpCode) + { + return OpCode.Read(0x1c); + } + private static ShaderIrOperAbuf[] Abuf20(this long OpCode) { int Abuf = OpCode.Read(20, 0x3ff); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs index 35dea6121..68ff214e4 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs @@ -49,6 +49,7 @@ namespace Ryujinx.Graphics.Gal.Shader Ipa, Texb, Texs, + Tld4, Trunc, F_End, diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs index 82f3bb774..72ea221ad 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs @@ -1,12 +1,24 @@ +using Ryujinx.Graphics.Texture; + namespace Ryujinx.Graphics.Gal.Shader { class ShaderIrMetaTex : ShaderIrMeta { - public int Elem { get; private set; } + public int Elem { get; private set; } + public GalTextureTarget TextureTarget { get; private set; } + public ShaderIrNode[] Coordinates { get; private set; } + public TextureInstructionSuffix TextureInstructionSuffix { get; private set; } + public ShaderIrOperGpr LevelOfDetail; + public ShaderIrOperGpr Offset; + public ShaderIrOperGpr DepthCompare; + public int Component; // for TLD4(S) - public ShaderIrMetaTex(int Elem) + public ShaderIrMetaTex(int Elem, GalTextureTarget TextureTarget, TextureInstructionSuffix TextureInstructionSuffix, params ShaderIrNode[] Coordinates) { - this.Elem = Elem; + this.Elem = Elem; + this.TextureTarget = TextureTarget; + this.TextureInstructionSuffix = TextureInstructionSuffix; + this.Coordinates = Coordinates; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index 177e36c3e..d2bbd38c6 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -122,6 +122,8 @@ namespace Ryujinx.Graphics.Gal.Shader Set("1101111101001x", ShaderDecode.Texq); Set("1101x00xxxxxxx", ShaderDecode.Texs); Set("1101101xxxxxxx", ShaderDecode.Tlds); + Set("110010xxxx111x", ShaderDecode.Tld4); + Set("1101111100xxxx", ShaderDecode.Tld4s); Set("01011111xxxxxx", ShaderDecode.Vmad); Set("0100111xxxxxxx", ShaderDecode.Xmad_CR); Set("0011011x00xxxx", ShaderDecode.Xmad_I); diff --git a/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs b/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs index ef47ca2e1..ed1955cdb 100644 --- a/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs +++ b/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs @@ -1,3 +1,5 @@ +using Ryujinx.Graphics.Texture; + namespace Ryujinx.Graphics.Gal { public class ShaderDeclInfo @@ -9,18 +11,27 @@ namespace Ryujinx.Graphics.Gal public int Cbuf { get; private set; } public int Size { get; private set; } + public GalTextureTarget TextureTarget { get; private set; } + + public TextureInstructionSuffix TextureSuffix { get; private set; } + public ShaderDeclInfo( string Name, int Index, bool IsCb = false, int Cbuf = 0, - int Size = 1) + int Size = 1, + GalTextureTarget TextureTarget = GalTextureTarget.TwoD, + TextureInstructionSuffix TextureSuffix = TextureInstructionSuffix.None) { - this.Name = Name; - this.Index = Index; - this.IsCb = IsCb; - this.Cbuf = Cbuf; - this.Size = Size; + this.Name = Name; + this.Index = Index; + this.IsCb = IsCb; + this.Cbuf = Cbuf; + this.Size = Size; + + this.TextureTarget = TextureTarget; + this.TextureSuffix = TextureSuffix; } internal void Enlarge(int NewSize) diff --git a/Ryujinx.Graphics/GpuResourceManager.cs b/Ryujinx.Graphics/GpuResourceManager.cs index d46129516..4f2d92b03 100644 --- a/Ryujinx.Graphics/GpuResourceManager.cs +++ b/Ryujinx.Graphics/GpuResourceManager.cs @@ -1,6 +1,8 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Texture; +using System; using System.Collections.Generic; namespace Ryujinx.Graphics @@ -11,6 +13,7 @@ namespace Ryujinx.Graphics { None, Texture, + TextureArrayLayer, ColorBuffer, ZetaBuffer } @@ -20,6 +23,7 @@ namespace Ryujinx.Graphics private HashSet[] UploadedKeys; private Dictionary ImageTypes; + private Dictionary MirroredTextures; public GpuResourceManager(NvGpu Gpu) { @@ -33,6 +37,7 @@ namespace Ryujinx.Graphics } ImageTypes = new Dictionary(); + MirroredTextures = new Dictionary(); } public void SendColorBuffer(NvGpuVmm Vmm, long Position, int Attachment, GalImage NewImage) @@ -70,6 +75,32 @@ namespace Ryujinx.Graphics ImageTypes[Position] = ImageType.Texture; } + public bool TryGetTextureLayer(long Position, out int LayerIndex) + { + if (MirroredTextures.TryGetValue(Position, out LayerIndex)) + { + ImageType Type = ImageTypes[Position]; + + // FIXME(thog): I'm actually unsure if we should deny all other image type, gpu testing needs to be done here. + if (Type != ImageType.Texture && Type != ImageType.TextureArrayLayer) + { + LayerIndex = -1; + return false; + } + + return true; + } + + LayerIndex = -1; + return false; + } + + public void SetTextureArrayLayer(long Position, int LayerIndex) + { + ImageTypes[Position] = ImageType.TextureArrayLayer; + MirroredTextures[Position] = LayerIndex; + } + private void PrepareSendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage) { long Size = ImageUtils.GetSize(NewImage); @@ -102,7 +133,7 @@ namespace Ryujinx.Graphics private bool TryReuse(NvGpuVmm Vmm, long Position, GalImage NewImage) { - if (Gpu.Renderer.Texture.TryGetImage(Position, out GalImage CachedImage) && CachedImage.SizeMatches(NewImage)) + if (Gpu.Renderer.Texture.TryGetImage(Position, out GalImage CachedImage) && CachedImage.TextureTarget == NewImage.TextureTarget && CachedImage.SizeMatches(NewImage)) { Gpu.Renderer.RenderTarget.Reinterpret(Position, NewImage); diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs index 55e3ebd4c..3295f6da0 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Texture; @@ -46,6 +47,8 @@ namespace Ryujinx.Graphics.Graphics3d bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0; int DstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth); int DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight); + int DstDepth = ReadRegister(NvGpuEngine2dReg.DstDepth); + int DstLayer = ReadRegister(NvGpuEngine2dReg.DstLayer); int DstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch); int DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions); @@ -53,6 +56,8 @@ namespace Ryujinx.Graphics.Graphics3d bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); int SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight); + int SrcDepth = ReadRegister(NvGpuEngine2dReg.SrcDepth); + int SrcLayer = ReadRegister(NvGpuEngine2dReg.SrcLayer); int SrcPitch = ReadRegister(NvGpuEngine2dReg.SrcPitch); int SrcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions); @@ -82,26 +87,99 @@ namespace Ryujinx.Graphics.Graphics3d long SrcKey = Vmm.GetPhysicalAddress(SrcAddress); long DstKey = Vmm.GetPhysicalAddress(DstAddress); + bool IsSrcLayered = false; + bool IsDstLayered = false; + + GalTextureTarget SrcTarget = GalTextureTarget.TwoD; + + if (SrcDepth != 0) + { + SrcTarget = GalTextureTarget.TwoDArray; + SrcDepth++; + IsSrcLayered = true; + } + else + { + SrcDepth = 1; + } + + GalTextureTarget DstTarget = GalTextureTarget.TwoD; + + if (DstDepth != 0) + { + DstTarget = GalTextureTarget.TwoDArray; + DstDepth++; + IsDstLayered = true; + } + else + { + DstDepth = 1; + } + GalImage SrcTexture = new GalImage( SrcWidth, - SrcHeight, 1, - SrcBlockHeight, + SrcHeight, + 1, SrcDepth, 1, + SrcBlockHeight, 1, SrcLayout, - SrcImgFormat); + SrcImgFormat, + SrcTarget); GalImage DstTexture = new GalImage( DstWidth, - DstHeight, 1, - DstBlockHeight, + DstHeight, + 1, DstDepth, 1, + DstBlockHeight, 1, DstLayout, - DstImgFormat); + DstImgFormat, + DstTarget); SrcTexture.Pitch = SrcPitch; DstTexture.Pitch = DstPitch; + long GetLayerOffset(GalImage Image, int Layer) + { + int TargetMipLevel = Image.MaxMipmapLevel <= 1 ? 1 : Image.MaxMipmapLevel - 1; + return ImageUtils.GetLayerOffset(Image, TargetMipLevel) * Layer; + } + + int SrcLayerIndex = -1; + + if (IsSrcLayered && Gpu.ResourceManager.TryGetTextureLayer(SrcKey, out SrcLayerIndex) && SrcLayerIndex != 0) + { + SrcKey = SrcKey - GetLayerOffset(SrcTexture, SrcLayerIndex); + } + + int DstLayerIndex = -1; + + if (IsDstLayered && Gpu.ResourceManager.TryGetTextureLayer(DstKey, out DstLayerIndex) && DstLayerIndex != 0) + { + DstKey = DstKey - GetLayerOffset(DstTexture, DstLayerIndex); + } + Gpu.ResourceManager.SendTexture(Vmm, SrcKey, SrcTexture); Gpu.ResourceManager.SendTexture(Vmm, DstKey, DstTexture); + if (IsSrcLayered && SrcLayerIndex == -1) + { + for (int Layer = 0; Layer < SrcTexture.LayerCount; Layer++) + { + Gpu.ResourceManager.SetTextureArrayLayer(SrcKey + GetLayerOffset(SrcTexture, Layer), Layer); + } + + SrcLayerIndex = 0; + } + + if (IsDstLayered && DstLayerIndex == -1) + { + for (int Layer = 0; Layer < DstTexture.LayerCount; Layer++) + { + Gpu.ResourceManager.SetTextureArrayLayer(DstKey + GetLayerOffset(DstTexture, Layer), Layer); + } + + DstLayerIndex = 0; + } + int SrcBlitX1 = (int)(SrcBlitX >> 32); int SrcBlitY1 = (int)(SrcBlitY >> 32); @@ -109,8 +187,12 @@ namespace Ryujinx.Graphics.Graphics3d int SrcBlitY2 = (int)(SrcBlitY + DstBlitH * BlitDvDy >> 32); Gpu.Renderer.RenderTarget.Copy( + SrcTexture, + DstTexture, SrcKey, DstKey, + SrcLayerIndex, + DstLayerIndex, SrcBlitX1, SrcBlitY1, SrcBlitX2, @@ -124,6 +206,8 @@ namespace Ryujinx.Graphics.Graphics3d //the texture is modified by the guest, however it doesn't //work when resources that the gpu can write to are copied, //like framebuffers. + + // FIXME: SUPPORT MULTILAYER CORRECTLY HERE (this will cause weird stuffs on the first layer) ImageUtils.CopyTexture( Vmm, SrcTexture, diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs index c1c0dba29..7747b5a3a 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs @@ -11,6 +11,7 @@ namespace Ryujinx.Graphics.Graphics3d DstWidth = 0x86, DstHeight = 0x87, DstAddress = 0x88, + DstAddressLow = 0x89, SrcFormat = 0x8c, SrcLinear = 0x8d, SrcBlockDimensions = 0x8e, @@ -20,6 +21,7 @@ namespace Ryujinx.Graphics.Graphics3d SrcWidth = 0x92, SrcHeight = 0x93, SrcAddress = 0x94, + SrcAddressLow = 0x95, ClipEnable = 0xa4, CopyOperation = 0xab, BlitControl = 0x223, diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs index 6120053da..eb6289fbd 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs @@ -1,4 +1,5 @@ using Ryujinx.Common; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Texture; @@ -190,7 +191,11 @@ namespace Ryujinx.Graphics.Graphics3d int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); - int BlockDim = ReadRegister(NvGpuEngine3dReg.FrameBufferNBlockDim + FbIndex * 0x10); + int ArrayMode = ReadRegister(NvGpuEngine3dReg.FrameBufferNArrayMode + FbIndex * 0x10); + int LayerCount = ArrayMode & 0xFFFF; + int LayerStride = ReadRegister(NvGpuEngine3dReg.FrameBufferNLayerStride + FbIndex * 0x10); + int BaseLayer = ReadRegister(NvGpuEngine3dReg.FrameBufferNBaseLayer + FbIndex * 0x10); + int BlockDim = ReadRegister(NvGpuEngine3dReg.FrameBufferNBlockDim + FbIndex * 0x10); int GobBlockHeight = 1 << ((BlockDim >> 4) & 7); @@ -210,7 +215,7 @@ namespace Ryujinx.Graphics.Graphics3d GalImageFormat Format = ImageUtils.ConvertSurface((GalSurfaceFormat)SurfFormat); - GalImage Image = new GalImage(Width, Height, 1, GobBlockHeight, Layout, Format); + GalImage Image = new GalImage(Width, Height, 1, 1, 1, GobBlockHeight, 1, Layout, Format, GalTextureTarget.TwoD); Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image); @@ -264,7 +269,8 @@ namespace Ryujinx.Graphics.Graphics3d GalImageFormat Format = ImageUtils.ConvertZeta((GalZetaFormat)ZetaFormat); - GalImage Image = new GalImage(Width, Height, 1, GobBlockHeight, Layout, Format); + // TODO: Support non 2D? + GalImage Image = new GalImage(Width, Height, 1, 1, 1, GobBlockHeight, 1, Layout, Format, GalTextureTarget.TwoD); Gpu.ResourceManager.SendZetaBuffer(Vmm, Key, Image); } @@ -600,7 +606,7 @@ namespace Ryujinx.Graphics.Graphics3d } Gpu.Renderer.Texture.Bind(Key, Index, Image); - Gpu.Renderer.Texture.SetSampler(Sampler); + Gpu.Renderer.Texture.SetSampler(Image, Sampler); } } diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs index 026b0cd19..913464640 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs @@ -2,112 +2,115 @@ namespace Ryujinx.Graphics.Graphics3d { enum NvGpuEngine3dReg { - FrameBufferNAddress = 0x200, - FrameBufferNWidth = 0x202, - FrameBufferNHeight = 0x203, - FrameBufferNFormat = 0x204, - FrameBufferNBlockDim = 0x205, - ViewportNScaleX = 0x280, - ViewportNScaleY = 0x281, - ViewportNScaleZ = 0x282, - ViewportNTranslateX = 0x283, - ViewportNTranslateY = 0x284, - ViewportNTranslateZ = 0x285, - ViewportNHoriz = 0x300, - ViewportNVert = 0x301, - DepthRangeNNear = 0x302, - DepthRangeNFar = 0x303, - VertexArrayFirst = 0x35d, - VertexArrayCount = 0x35e, - ClearNColor = 0x360, - ClearDepth = 0x364, - ClearStencil = 0x368, - ScissorEnable = 0x380, - ScissorHorizontal = 0x381, - ScissorVertical = 0x382, - StencilBackFuncRef = 0x3d5, - StencilBackMask = 0x3d6, - StencilBackFuncMask = 0x3d7, - ColorMaskCommon = 0x3e4, - RTSeparateFragData = 0x3eb, - ZetaAddress = 0x3f8, - ZetaFormat = 0x3fa, - ZetaBlockDimensions = 0x3fb, - ZetaLayerStride = 0x3fc, - VertexAttribNFormat = 0x458, - RTControl = 0x487, - ZetaHoriz = 0x48a, - ZetaVert = 0x48b, - ZetaArrayMode = 0x48c, - LinkedTsc = 0x48d, - DepthTestEnable = 0x4b3, - BlendIndependent = 0x4b9, - DepthWriteEnable = 0x4ba, - DepthTestFunction = 0x4c3, - BlendSeparateAlpha = 0x4cf, - BlendEquationRgb = 0x4d0, - BlendFuncSrcRgb = 0x4d1, - BlendFuncDstRgb = 0x4d2, - BlendEquationAlpha = 0x4d3, - BlendFuncSrcAlpha = 0x4d4, - BlendFuncDstAlpha = 0x4d6, - BlendEnable = 0x4d7, - IBlendNEnable = 0x4d8, - StencilEnable = 0x4e0, - StencilFrontOpFail = 0x4e1, - StencilFrontOpZFail = 0x4e2, - StencilFrontOpZPass = 0x4e3, - StencilFrontFuncFunc = 0x4e4, - StencilFrontFuncRef = 0x4e5, - StencilFrontFuncMask = 0x4e6, - StencilFrontMask = 0x4e7, - ScreenYControl = 0x4eb, - VertexArrayElemBase = 0x50d, - VertexArrayInstBase = 0x50e, - ZetaEnable = 0x54e, - TexHeaderPoolOffset = 0x55d, - TexSamplerPoolOffset = 0x557, - StencilTwoSideEnable = 0x565, - StencilBackOpFail = 0x566, - StencilBackOpZFail = 0x567, - StencilBackOpZPass = 0x568, - StencilBackFuncFunc = 0x569, - FrameBufferSrgb = 0x56e, - ShaderAddress = 0x582, - VertexBeginGl = 0x586, - PrimRestartEnable = 0x591, - PrimRestartIndex = 0x592, - IndexArrayAddress = 0x5f2, - IndexArrayEndAddr = 0x5f4, - IndexArrayFormat = 0x5f6, - IndexBatchFirst = 0x5f7, - IndexBatchCount = 0x5f8, - VertexArrayNInstance = 0x620, - CullFaceEnable = 0x646, - FrontFace = 0x647, - CullFace = 0x648, - ColorMaskN = 0x680, - QueryAddress = 0x6c0, - QuerySequence = 0x6c2, - QueryControl = 0x6c3, - VertexArrayNControl = 0x700, - VertexArrayNAddress = 0x701, - VertexArrayNDivisor = 0x703, - IBlendNSeparateAlpha = 0x780, - IBlendNEquationRgb = 0x781, - IBlendNFuncSrcRgb = 0x782, - IBlendNFuncDstRgb = 0x783, - IBlendNEquationAlpha = 0x784, - IBlendNFuncSrcAlpha = 0x785, - IBlendNFuncDstAlpha = 0x786, - VertexArrayNEndAddr = 0x7c0, - ShaderNControl = 0x800, - ShaderNOffset = 0x801, - ShaderNMaxGprs = 0x803, - ShaderNType = 0x804, - ConstBufferSize = 0x8e0, - ConstBufferAddress = 0x8e1, - ConstBufferOffset = 0x8e3, - TextureCbIndex = 0x982 + FrameBufferNAddress = 0x200, + FrameBufferNWidth = 0x202, + FrameBufferNHeight = 0x203, + FrameBufferNFormat = 0x204, + FrameBufferNBlockDim = 0x205, + FrameBufferNArrayMode = 0x206, + FrameBufferNLayerStride = 0x207, + FrameBufferNBaseLayer = 0x208, + ViewportNScaleX = 0x280, + ViewportNScaleY = 0x281, + ViewportNScaleZ = 0x282, + ViewportNTranslateX = 0x283, + ViewportNTranslateY = 0x284, + ViewportNTranslateZ = 0x285, + ViewportNHoriz = 0x300, + ViewportNVert = 0x301, + DepthRangeNNear = 0x302, + DepthRangeNFar = 0x303, + VertexArrayFirst = 0x35d, + VertexArrayCount = 0x35e, + ClearNColor = 0x360, + ClearDepth = 0x364, + ClearStencil = 0x368, + ScissorEnable = 0x380, + ScissorHorizontal = 0x381, + ScissorVertical = 0x382, + StencilBackFuncRef = 0x3d5, + StencilBackMask = 0x3d6, + StencilBackFuncMask = 0x3d7, + ColorMaskCommon = 0x3e4, + RTSeparateFragData = 0x3eb, + ZetaAddress = 0x3f8, + ZetaFormat = 0x3fa, + ZetaBlockDimensions = 0x3fb, + ZetaLayerStride = 0x3fc, + VertexAttribNFormat = 0x458, + RTControl = 0x487, + ZetaHoriz = 0x48a, + ZetaVert = 0x48b, + ZetaArrayMode = 0x48c, + LinkedTsc = 0x48d, + DepthTestEnable = 0x4b3, + BlendIndependent = 0x4b9, + DepthWriteEnable = 0x4ba, + DepthTestFunction = 0x4c3, + BlendSeparateAlpha = 0x4cf, + BlendEquationRgb = 0x4d0, + BlendFuncSrcRgb = 0x4d1, + BlendFuncDstRgb = 0x4d2, + BlendEquationAlpha = 0x4d3, + BlendFuncSrcAlpha = 0x4d4, + BlendFuncDstAlpha = 0x4d6, + BlendEnable = 0x4d7, + IBlendNEnable = 0x4d8, + StencilEnable = 0x4e0, + StencilFrontOpFail = 0x4e1, + StencilFrontOpZFail = 0x4e2, + StencilFrontOpZPass = 0x4e3, + StencilFrontFuncFunc = 0x4e4, + StencilFrontFuncRef = 0x4e5, + StencilFrontFuncMask = 0x4e6, + StencilFrontMask = 0x4e7, + ScreenYControl = 0x4eb, + VertexArrayElemBase = 0x50d, + VertexArrayInstBase = 0x50e, + ZetaEnable = 0x54e, + TexHeaderPoolOffset = 0x55d, + TexSamplerPoolOffset = 0x557, + StencilTwoSideEnable = 0x565, + StencilBackOpFail = 0x566, + StencilBackOpZFail = 0x567, + StencilBackOpZPass = 0x568, + StencilBackFuncFunc = 0x569, + FrameBufferSrgb = 0x56e, + ShaderAddress = 0x582, + VertexBeginGl = 0x586, + PrimRestartEnable = 0x591, + PrimRestartIndex = 0x592, + IndexArrayAddress = 0x5f2, + IndexArrayEndAddr = 0x5f4, + IndexArrayFormat = 0x5f6, + IndexBatchFirst = 0x5f7, + IndexBatchCount = 0x5f8, + VertexArrayNInstance = 0x620, + CullFaceEnable = 0x646, + FrontFace = 0x647, + CullFace = 0x648, + ColorMaskN = 0x680, + QueryAddress = 0x6c0, + QuerySequence = 0x6c2, + QueryControl = 0x6c3, + VertexArrayNControl = 0x700, + VertexArrayNAddress = 0x701, + VertexArrayNDivisor = 0x703, + IBlendNSeparateAlpha = 0x780, + IBlendNEquationRgb = 0x781, + IBlendNFuncSrcRgb = 0x782, + IBlendNFuncDstRgb = 0x783, + IBlendNEquationAlpha = 0x784, + IBlendNFuncSrcAlpha = 0x785, + IBlendNFuncDstAlpha = 0x786, + VertexArrayNEndAddr = 0x7c0, + ShaderNControl = 0x800, + ShaderNOffset = 0x801, + ShaderNMaxGprs = 0x803, + ShaderNType = 0x804, + ConstBufferSize = 0x8e0, + ConstBufferAddress = 0x8e1, + ConstBufferOffset = 0x8e3, + TextureCbIndex = 0x982 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs index d89059c0c..2f1df3d37 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Texture; using System.Collections.Generic; @@ -125,29 +126,37 @@ namespace Ryujinx.Graphics.Graphics3d if (SrcLinear) { - SrcSwizzle = new LinearSwizzle(SrcPitch, SrcCpp); + SrcSwizzle = new LinearSwizzle(SrcPitch, SrcCpp, SrcSizeX, SrcSizeY); } else { - SrcSwizzle = new BlockLinearSwizzle(SrcSizeX, SrcCpp, SrcBlockHeight); + SrcSwizzle = new BlockLinearSwizzle( + SrcSizeX, + SrcSizeY, 1, + SrcBlockHeight, 1, + SrcCpp); } ISwizzle DstSwizzle; if (DstLinear) { - DstSwizzle = new LinearSwizzle(DstPitch, DstCpp); + DstSwizzle = new LinearSwizzle(DstPitch, DstCpp, SrcSizeX, SrcSizeY); } else { - DstSwizzle = new BlockLinearSwizzle(DstSizeX, DstCpp, DstBlockHeight); + DstSwizzle = new BlockLinearSwizzle( + DstSizeX, + DstSizeY, 1, + DstBlockHeight, 1, + DstCpp); } for (int Y = 0; Y < YCount; Y++) for (int X = 0; X < XCount; X++) { - int SrcOffset = SrcSwizzle.GetSwizzleOffset(SrcPosX + X, SrcPosY + Y); - int DstOffset = DstSwizzle.GetSwizzleOffset(DstPosX + X, DstPosY + Y); + int SrcOffset = SrcSwizzle.GetSwizzleOffset(SrcPosX + X, SrcPosY + Y, 0); + int DstOffset = DstSwizzle.GetSwizzleOffset(DstPosX + X, DstPosY + Y, 0); long Src = SrcPA + (uint)SrcOffset; long Dst = DstPA + (uint)DstOffset; diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs index 681552556..62872ba15 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Texture; using System.Collections.Generic; @@ -119,14 +120,17 @@ namespace Ryujinx.Graphics.Graphics3d } else { - BlockLinearSwizzle Swizzle = new BlockLinearSwizzle(CopyWidth, 1, CopyGobBlockHeight); + BlockLinearSwizzle Swizzle = new BlockLinearSwizzle( + CopyWidth, + CopyHeight, 1, + CopyGobBlockHeight, 1, 1); int SrcOffset = 0; for (int Y = CopyStartY; Y < CopyHeight && SrcOffset < CopySize; Y++) for (int X = CopyStartX; X < CopyWidth && SrcOffset < CopySize; X++) { - int DstOffset = Swizzle.GetSwizzleOffset(X, Y); + int DstOffset = Swizzle.GetSwizzleOffset(X, Y, 0); Vmm.WriteByte(CopyAddress + DstOffset, Buffer[SrcOffset++]); } diff --git a/Ryujinx.Graphics/Graphics3d/Texture/ASTCDecoder.cs b/Ryujinx.Graphics/Graphics3d/Texture/ASTCDecoder.cs index 1efa02552..00158dc10 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/ASTCDecoder.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/ASTCDecoder.cs @@ -72,6 +72,7 @@ namespace Ryujinx.Graphics.Texture if (BlockZ != 1 || Z != 1) { + // TODO: Support 3D textures? throw new ASTCDecoderException("3D compressed textures unsupported!"); } diff --git a/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs index 9451291e9..1be064428 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs @@ -1,51 +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; - public BlockLinearSwizzle(int Width, int Bpp, int BlockHeight = 16) + private int RobSize; + private int SliceSize; + + 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); + 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; } - public int GetSwizzleOffset(int X, int Y) + 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; @@ -53,7 +180,7 @@ namespace Ryujinx.Graphics.Texture Position += ((Y & 0x01) >> 0) << 4; Position += ((X & 0x0f) >> 0) << 0; - return Position; + return BaseOffset + Position; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs index 583fc20c5..2e0e8aedd 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs @@ -2,6 +2,12 @@ namespace Ryujinx.Graphics.Texture { interface ISwizzle { - int GetSwizzleOffset(int X, int Y); + int GetSwizzleOffset(int X, int Y, int Z); + + void SetMipLevel(int Level); + + int GetMipOffset(int Level); + + int GetImageSize(int MipsCount); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs b/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs index f958e1de8..c4208935c 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs @@ -1,4 +1,6 @@ using ChocolArm64.Memory; +using OpenTK.Graphics.OpenGL; +using Ryujinx.Common; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; using System; @@ -23,14 +25,16 @@ namespace Ryujinx.Graphics.Texture public int BytesPerPixel { get; private set; } public int BlockWidth { get; private set; } public int BlockHeight { get; private set; } + public int BlockDepth { get; private set; } public TargetBuffer Target { get; private set; } - public ImageDescriptor(int BytesPerPixel, int BlockWidth, int BlockHeight, TargetBuffer Target) + public ImageDescriptor(int BytesPerPixel, int BlockWidth, int BlockHeight, int BlockDepth, TargetBuffer Target) { this.BytesPerPixel = BytesPerPixel; this.BlockWidth = BlockWidth; this.BlockHeight = BlockHeight; + this.BlockDepth = BlockDepth; this.Target = Target; } } @@ -92,52 +96,52 @@ namespace Ryujinx.Graphics.Texture private static readonly Dictionary s_ImageTable = new Dictionary() { - { GalImageFormat.RGBA32, new ImageDescriptor(16, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RGBA16, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RG32, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RGBX8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RGBA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BGRA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RGB10A2, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R32, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RGBA4, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BptcSfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BptcUfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BGR5A1, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RGB5A1, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RGB565, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BGR565, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BptcUnorm, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.RG16, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RG8, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R16, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R8, new ImageDescriptor(1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R11G11B10, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BC1, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BC2, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BC3, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BC4, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BC5, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.Astc2D4x4, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.Astc2D5x5, new ImageDescriptor(16, 5, 5, TargetBuffer.Color) }, - { GalImageFormat.Astc2D6x6, new ImageDescriptor(16, 6, 6, TargetBuffer.Color) }, - { GalImageFormat.Astc2D8x8, new ImageDescriptor(16, 8, 8, TargetBuffer.Color) }, - { GalImageFormat.Astc2D10x10, new ImageDescriptor(16, 10, 10, TargetBuffer.Color) }, - { GalImageFormat.Astc2D12x12, new ImageDescriptor(16, 12, 12, TargetBuffer.Color) }, - { GalImageFormat.Astc2D5x4, new ImageDescriptor(16, 5, 4, TargetBuffer.Color) }, - { GalImageFormat.Astc2D6x5, new ImageDescriptor(16, 6, 5, TargetBuffer.Color) }, - { GalImageFormat.Astc2D8x6, new ImageDescriptor(16, 8, 6, TargetBuffer.Color) }, - { GalImageFormat.Astc2D10x8, new ImageDescriptor(16, 10, 8, TargetBuffer.Color) }, - { GalImageFormat.Astc2D12x10, new ImageDescriptor(16, 12, 10, TargetBuffer.Color) }, - { GalImageFormat.Astc2D8x5, new ImageDescriptor(16, 8, 5, TargetBuffer.Color) }, - { GalImageFormat.Astc2D10x5, new ImageDescriptor(16, 10, 5, TargetBuffer.Color) }, - { GalImageFormat.Astc2D10x6, new ImageDescriptor(16, 10, 6, TargetBuffer.Color) }, + { GalImageFormat.RGBA32, new ImageDescriptor(16, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGBA16, new ImageDescriptor(8, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RG32, new ImageDescriptor(8, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGBX8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGBA8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BGRA8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGB10A2, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R32, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGBA4, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BptcSfloat, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.BptcUfloat, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.BGR5A1, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGB5A1, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGB565, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BGR565, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BptcUnorm, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.RG16, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RG8, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R16, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R8, new ImageDescriptor(1, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R11G11B10, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BC1, new ImageDescriptor(8, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.BC2, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.BC3, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.BC4, new ImageDescriptor(8, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.BC5, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D4x4, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D5x5, new ImageDescriptor(16, 5, 5, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D6x6, new ImageDescriptor(16, 6, 6, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D8x8, new ImageDescriptor(16, 8, 8, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x10, new ImageDescriptor(16, 10, 10, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D12x12, new ImageDescriptor(16, 12, 12, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D5x4, new ImageDescriptor(16, 5, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D6x5, new ImageDescriptor(16, 6, 5, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D8x6, new ImageDescriptor(16, 8, 6, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x8, new ImageDescriptor(16, 10, 8, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D12x10, new ImageDescriptor(16, 12, 10, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D8x5, new ImageDescriptor(16, 8, 5, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x5, new ImageDescriptor(16, 10, 5, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x6, new ImageDescriptor(16, 10, 6, 1, TargetBuffer.Color) }, - { GalImageFormat.D16, new ImageDescriptor(2, 1, 1, TargetBuffer.Depth) }, - { GalImageFormat.D24, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) }, - { GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, TargetBuffer.DepthStencil) }, - { GalImageFormat.D32, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) }, - { GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, TargetBuffer.DepthStencil) } + { GalImageFormat.D16, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Depth) }, + { GalImageFormat.D24, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Depth) }, + { GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.DepthStencil) }, + { GalImageFormat.D32, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Depth) }, + { GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, 1, TargetBuffer.DepthStencil) } }; public static GalImageFormat ConvertTexture( @@ -241,26 +245,37 @@ namespace Ryujinx.Graphics.Texture ImageDescriptor Desc = GetImageDescriptor(Image.Format); - (int Width, int Height) = GetImageSizeInBlocks(Image); + (int Width, int Height, int Depth) = GetImageSizeInBlocks(Image); int BytesPerPixel = Desc.BytesPerPixel; //Note: Each row of the texture needs to be aligned to 4 bytes. int Pitch = (Width * BytesPerPixel + 3) & ~3; - byte[] Data = new byte[Height * Pitch]; - for (int Y = 0; Y < Height; Y++) + int DataLayerSize = Height * Pitch * Depth; + byte[] Data = new byte[DataLayerSize * Image.LayerCount]; + + int TargetMipLevel = Image.MaxMipmapLevel <= 1 ? 1 : Image.MaxMipmapLevel - 1; + int LayerOffset = ImageUtils.GetLayerOffset(Image, TargetMipLevel); + + for (int Layer = 0; Layer < Image.LayerCount; Layer++) { - int OutOffs = Y * Pitch; - - for (int X = 0; X < Width; X++) + for (int Z = 0; Z < Depth; Z++) { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); + for (int Y = 0; Y < Height; Y++) + { + int OutOffs = (DataLayerSize * Layer) + Y * Pitch + (Z * Width * Height * BytesPerPixel); - CpuMemory.ReadBytes(Position + Offset, Data, OutOffs, BytesPerPixel); + for (int X = 0; X < Width; X++) + { + long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y, Z); - OutOffs += BytesPerPixel; + CpuMemory.ReadBytes(Position + (LayerOffset * Layer) + Offset, Data, OutOffs, BytesPerPixel); + + OutOffs += BytesPerPixel; + } + } } } @@ -273,16 +288,17 @@ namespace Ryujinx.Graphics.Texture ImageDescriptor Desc = GetImageDescriptor(Image.Format); - (int Width, int Height) = ImageUtils.GetImageSizeInBlocks(Image); + (int Width, int Height, int Depth) = ImageUtils.GetImageSizeInBlocks(Image); int BytesPerPixel = Desc.BytesPerPixel; int InOffs = 0; + for (int Z = 0; Z < Depth; Z++) for (int Y = 0; Y < Height; Y++) for (int X = 0; X < Width; X++) { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); + long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y, Z); Vmm.Memory.WriteBytes(Position + Offset, Data, InOffs, BytesPerPixel); @@ -290,6 +306,7 @@ namespace Ryujinx.Graphics.Texture } } + // TODO: Support non 2D public static bool CopyTexture( NvGpuVmm Vmm, GalImage SrcImage, @@ -318,8 +335,8 @@ namespace Ryujinx.Graphics.Texture for (int Y = 0; Y < Height; Y++) for (int X = 0; X < Width; X++) { - long SrcOffset = (uint)SrcSwizzle.GetSwizzleOffset(SrcX + X, SrcY + Y); - long DstOffset = (uint)DstSwizzle.GetSwizzleOffset(DstX + X, DstY + Y); + long SrcOffset = (uint)SrcSwizzle.GetSwizzleOffset(SrcX + X, SrcY + Y, 0); + long DstOffset = (uint)DstSwizzle.GetSwizzleOffset(DstX + X, DstY + Y, 0); byte[] Texel = Vmm.ReadBytes(SrcAddress + SrcOffset, BytesPerPixel); @@ -333,10 +350,41 @@ namespace Ryujinx.Graphics.Texture { ImageDescriptor Desc = GetImageDescriptor(Image.Format); + int ComponentCount = GetCoordsCountTextureTarget(Image.TextureTarget); + + if (IsArray(Image.TextureTarget)) + ComponentCount--; + int Width = DivRoundUp(Image.Width, Desc.BlockWidth); int Height = DivRoundUp(Image.Height, Desc.BlockHeight); + int Depth = DivRoundUp(Image.Depth, Desc.BlockDepth); - return Desc.BytesPerPixel * Width * Height; + switch (ComponentCount) + { + case 1: + return Desc.BytesPerPixel * Width * Image.LayerCount; + case 2: + return Desc.BytesPerPixel * Width * Height * Image.LayerCount; + case 3: + return Desc.BytesPerPixel * Width * Height * Depth * Image.LayerCount; + default: + throw new InvalidOperationException($"Invalid component count: {ComponentCount}"); + } + } + + public static int GetGpuSize(GalImage Image, bool forcePitch = false) + { + return TextureHelper.GetSwizzle(Image).GetImageSize(Image.MaxMipmapLevel) * Image.LayerCount; + } + + public static int GetLayerOffset(GalImage Image, int MipLevel) + { + if (MipLevel <= 0) + { + MipLevel = 1; + } + + return TextureHelper.GetSwizzle(Image).GetMipOffset(MipLevel); } public static int GetPitch(GalImageFormat Format, int Width) @@ -360,6 +408,11 @@ namespace Ryujinx.Graphics.Texture return GetImageDescriptor(Format).BlockHeight; } + public static int GetBlockDepth(GalImageFormat Format) + { + return GetImageDescriptor(Format).BlockDepth; + } + public static int GetAlignedWidth(GalImage Image) { ImageDescriptor Desc = GetImageDescriptor(Image.Format); @@ -378,12 +431,13 @@ namespace Ryujinx.Graphics.Texture return (Image.Width + AlignMask) & ~AlignMask; } - public static (int Width, int Height) GetImageSizeInBlocks(GalImage Image) + public static (int Width, int Height, int Depth) GetImageSizeInBlocks(GalImage Image) { ImageDescriptor Desc = GetImageDescriptor(Image.Format); return (DivRoundUp(Image.Width, Desc.BlockWidth), - DivRoundUp(Image.Height, Desc.BlockHeight)); + DivRoundUp(Image.Height, Desc.BlockHeight), + DivRoundUp(Image.Depth, Desc.BlockDepth)); } public static int GetBytesPerPixel(GalImageFormat Format) @@ -443,5 +497,66 @@ namespace Ryujinx.Graphics.Texture default: throw new NotImplementedException(((int)Type).ToString()); } } + + public static TextureTarget GetTextureTarget(GalTextureTarget GalTextureTarget) + { + switch (GalTextureTarget) + { + case GalTextureTarget.OneD: + return TextureTarget.Texture1D; + case GalTextureTarget.TwoD: + case GalTextureTarget.TwoDNoMipMap: + return TextureTarget.Texture2D; + case GalTextureTarget.ThreeD: + return TextureTarget.Texture3D; + case GalTextureTarget.OneDArray: + return TextureTarget.Texture1DArray; + case GalTextureTarget.OneDBuffer: + return TextureTarget.TextureBuffer; + case GalTextureTarget.TwoDArray: + return TextureTarget.Texture2DArray; + case GalTextureTarget.CubeMap: + return TextureTarget.TextureCubeMap; + case GalTextureTarget.CubeArray: + return TextureTarget.TextureCubeMapArray; + default: + throw new NotSupportedException($"Texture target {GalTextureTarget} currently not supported!"); + } + } + + public static bool IsArray(GalTextureTarget TextureTarget) + { + switch (TextureTarget) + { + case GalTextureTarget.OneDArray: + case GalTextureTarget.TwoDArray: + case GalTextureTarget.CubeArray: + return true; + default: + return false; + } + } + + public static int GetCoordsCountTextureTarget(GalTextureTarget TextureTarget) + { + switch (TextureTarget) + { + case GalTextureTarget.OneD: + return 1; + case GalTextureTarget.OneDArray: + case GalTextureTarget.OneDBuffer: + case GalTextureTarget.TwoD: + case GalTextureTarget.TwoDNoMipMap: + return 2; + case GalTextureTarget.ThreeD: + case GalTextureTarget.TwoDArray: + case GalTextureTarget.CubeMap: + return 3; + case GalTextureTarget.CubeArray: + return 4; + default: + throw new NotImplementedException($"TextureTarget.{TextureTarget} not implemented yet."); + } + } } } diff --git a/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs index ef468e27b..e6509baa6 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs @@ -1,3 +1,5 @@ +using System; + namespace Ryujinx.Graphics.Texture { class LinearSwizzle : ISwizzle @@ -5,15 +7,39 @@ namespace Ryujinx.Graphics.Texture private int Pitch; private int Bpp; - public LinearSwizzle(int Pitch, int Bpp) + private int SliceSize; + + public LinearSwizzle(int Pitch, int Bpp, int Width, int Height) { - this.Pitch = Pitch; - this.Bpp = Bpp; + this.Pitch = Pitch; + this.Bpp = Bpp; + SliceSize = Width * Height * Bpp; } - public int GetSwizzleOffset(int X, int Y) + public void SetMipLevel(int Level) { - return X * Bpp + Y * Pitch; + throw new NotImplementedException(); + } + + public int GetMipOffset(int Level) + { + if (Level == 1) + return SliceSize; + throw new NotImplementedException(); + } + + public int GetImageSize(int MipsCount) + { + int Size = GetMipOffset(MipsCount); + + Size = (Size + 0x1fff) & ~0x1fff; + + return Size; + } + + public int GetSwizzleOffset(int X, int Y, int Z) + { + return Z * SliceSize + X * Bpp + Y * Pitch; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs b/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs index 1f2d625ec..a2ce86f56 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs @@ -12,6 +12,8 @@ namespace Ryujinx.Graphics.Texture GalImageFormat Format = GetImageFormat(Tic); + GalTextureTarget TextureTarget = (GalTextureTarget)((Tic[4] >> 23) & 0xF); + GalTextureSource XSource = (GalTextureSource)((Tic[0] >> 19) & 7); GalTextureSource YSource = (GalTextureSource)((Tic[0] >> 22) & 7); GalTextureSource ZSource = (GalTextureSource)((Tic[0] >> 25) & 7); @@ -19,6 +21,8 @@ namespace Ryujinx.Graphics.Texture TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7); + int MaxMipmapLevel = (Tic[3] >> 28) & 0xF + 1; + GalMemoryLayout Layout; if (Swizzle == TextureSwizzle.BlockLinear || @@ -31,22 +35,61 @@ 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; + + int LayoutCount = 1; + + // TODO: check this + if (ImageUtils.IsArray(TextureTarget)) + { + LayoutCount = Depth; + Depth = 1; + } + + if (TextureTarget == GalTextureTarget.OneD) + { + Height = 1; + } + + if (TextureTarget == GalTextureTarget.TwoD || TextureTarget == GalTextureTarget.OneD) + { + Depth = 1; + } + else if (TextureTarget == GalTextureTarget.CubeMap) + { + // FIXME: This is a bit hacky but I guess it's fine for now + LayoutCount = 6; + Depth = 1; + } + else if (TextureTarget == GalTextureTarget.CubeArray) + { + // FIXME: This is a really really hacky but I guess it's fine for now + LayoutCount *= 6; + Depth = 1; + } GalImage Image = new GalImage( Width, Height, + Depth, + LayoutCount, TileWidth, - BlockHeight, + GobBlockHeight, + GobBlockDepth, Layout, Format, + TextureTarget, + MaxMipmapLevel, XSource, YSource, ZSource, @@ -68,6 +111,10 @@ namespace Ryujinx.Graphics.Texture GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7); GalTextureWrap AddressP = (GalTextureWrap)((Tsc[0] >> 6) & 7); + bool DepthCompare = ((Tsc[0] >> 9) & 1) == 1; + + DepthCompareFunc DepthCompareFunc = (DepthCompareFunc)((Tsc[0] >> 10) & 7); + GalTextureFilter MagFilter = (GalTextureFilter) ((Tsc[1] >> 0) & 3); GalTextureFilter MinFilter = (GalTextureFilter) ((Tsc[1] >> 4) & 3); GalTextureMipFilter MipFilter = (GalTextureMipFilter)((Tsc[1] >> 6) & 3); @@ -85,7 +132,9 @@ namespace Ryujinx.Graphics.Texture MinFilter, MagFilter, MipFilter, - BorderColor); + BorderColor, + DepthCompare, + DepthCompareFunc); } private static GalImageFormat GetImageFormat(int[] Tic) diff --git a/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs b/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs index 6ac91d8b5..33ccb0aa5 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; @@ -9,9 +10,13 @@ namespace Ryujinx.Graphics.Texture public static ISwizzle GetSwizzle(GalImage Image) { 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 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) { @@ -19,11 +24,17 @@ namespace Ryujinx.Graphics.Texture Width = (Width + AlignMask) & ~AlignMask; - return new BlockLinearSwizzle(Width, BytesPerPixel, Image.GobBlockHeight); + return new BlockLinearSwizzle( + Width, + Height, + Depth, + Image.GobBlockHeight, + Image.GobBlockDepth, + BytesPerPixel); } else { - return new LinearSwizzle(Image.Pitch, BytesPerPixel); + return new LinearSwizzle(Image.Pitch, BytesPerPixel, Width, Height); } } diff --git a/Ryujinx.Graphics/Texture/TextureInstructionSuffix.cs b/Ryujinx.Graphics/Texture/TextureInstructionSuffix.cs new file mode 100644 index 000000000..bcb64af0b --- /dev/null +++ b/Ryujinx.Graphics/Texture/TextureInstructionSuffix.cs @@ -0,0 +1,19 @@ +using System; + +namespace Ryujinx.Graphics.Texture +{ + [Flags] + public enum TextureInstructionSuffix + { + None = 0x00, // No Modifier + LZ = 0x02, // Load LOD Zero + LB = 0x08, // Load Bias + LL = 0x10, // Load LOD + LBA = 0x20, // Load Bias with OperA? Auto? + LLA = 0x40, // Load LOD with OperA? Auto? + DC = 0x80, // Depth Compare + AOffI = 0x100, // Offset + MZ = 0x200, // Multisample Zero? + PTP = 0x400 // ??? + } +} diff --git a/Ryujinx.Graphics/VDec/VideoDecoder.cs b/Ryujinx.Graphics/VDec/VideoDecoder.cs index 847392b0d..be53b1a02 100644 --- a/Ryujinx.Graphics/VDec/VideoDecoder.cs +++ b/Ryujinx.Graphics/VDec/VideoDecoder.cs @@ -216,10 +216,11 @@ namespace Ryujinx.Graphics.VDec GalImage Image = new GalImage( OutputConfig.SurfaceWidth, - OutputConfig.SurfaceHeight, 1, - OutputConfig.GobBlockHeight, + OutputConfig.SurfaceHeight, 1, 1, 1, + OutputConfig.GobBlockHeight, 1, GalMemoryLayout.BlockLinear, - GalImageFormat.RGBA8 | GalImageFormat.Unorm); + GalImageFormat.RGBA8 | GalImageFormat.Unorm, + GalTextureTarget.TwoD); ImageUtils.WriteTexture(Vmm, Image, Vmm.GetPhysicalAddress(OutputConfig.SurfaceLumaAddress), Frame.Data); } diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs index db04f47cd..dbf255bee 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; +using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS; using Ryujinx.HLE.HOS.Services.Nv.NvMap; @@ -415,9 +416,10 @@ namespace Ryujinx.HLE.HOS.Services.Android { image = new GalImage( fbWidth, - fbHeight, 1, BlockHeight, + fbHeight, 1, 1, 1, BlockHeight, 1, GalMemoryLayout.BlockLinear, - imageFormat); + imageFormat, + GalTextureTarget.TwoD); } context.Device.Gpu.ResourceManager.ClearPbCache(); diff --git a/Ryujinx.ShaderTools/Program.cs b/Ryujinx.ShaderTools/Program.cs index 30fa71aea..77aba0abe 100644 --- a/Ryujinx.ShaderTools/Program.cs +++ b/Ryujinx.ShaderTools/Program.cs @@ -13,7 +13,7 @@ namespace Ryujinx.ShaderTools { if (args.Length == 2) { - GlslDecompiler Decompiler = new GlslDecompiler(MaxUboSize); + GlslDecompiler Decompiler = new GlslDecompiler(MaxUboSize, true); GalShaderType ShaderType = GalShaderType.Vertex;