diff options
| author | Thomas Guillemard <thog@protonmail.com> | 2019-02-28 02:12:24 +0100 |
|---|---|---|
| committer | jduncanator <1518948+jduncanator@users.noreply.github.com> | 2019-02-28 12:12:24 +1100 |
| commit | 884b4e5fd3c2a54ebb796b7f995c0eda9c4d0038 (patch) | |
| tree | 35838aba68da07fda992510fede8a3920c60ccb3 /Ryujinx.Graphics/Gal | |
| parent | 81aa50feb0899e73ee62e5113b786efe0ff6b7a9 (diff) | |
Initial non 2D textures support (#525)
* Initial non 2D textures support
- Shaders still need to be changed
- Some types aren't yet implemented
* Start implementing texture instructions suffixes
Fix wrong texture type with cube and TEXS
Also support array textures in TEX and TEX.B
Clean up TEX and TEXS coords managment
Fix TEXS.LL with non-2d textures
Implement TEX.AOFFI
Get the right arguments for TEX, TEXS and TLDS
Also, store suffix operands in appropriate values to support multiple
suffix combinaisons
* Support depth in read/writeTexture
Also support WrapR and detect mipmap
* Proper cube map textures support + fix TEXS.LZ
* Implement depth compare
* some code clean up
* Implement CubeMap textures in OGLTexture.Create
* Implement TLD4 and TLD4S
* Add Texture 1D support
* updates comments
* fix some code style issues
* Fix some nits + rename some things to be less confusing
* Remove GetSuffix local functions
* AOFFI => AOffI
* TextureType => GalTextureTarget
* finish renaming TextureType to TextureTarget
* Disable LL, LZ and LB support in the decompiler
This needs more work at the GL level (GLSL implementation should be
right)
* Revert "Disable LL, LZ and LB support in the decompiler"
This reverts commit 64536c3d9f673645faff3152838d1413c3203395.
* Fix TEXS ARRAY_2D index
* ImageFormat depth should be 1 for all image format
* Fix shader build issues with sampler1DShadow and texture
* Fix DC & AOFFI combinaison with TEX/TEXS
* Support AOFFI with TLD4 and TLD4S
* Fix shader compilation error for TLD4.AOFFI with no DC
* Fix binding isuses on the 2d copy engine
TODO: support 2d array copy
* Support 2D array copy operation in the 2D engine
This make every copy right in the GPU side.
Thie CPU copy probably needs to be updated
* Implement GetGpuSize + fix somes issues with 2d engine copies
TODO: mipmap level in it
* Don't throw an exception in the layer handling
* Fix because of rebase
* Reject 2d layers of non textures in 2d copy engine
* Add 3D textures and mipmap support on BlockLinearSwizzle
* Fix naming on new BitUtils methods
* gpu cache: Make sure to invalidate textures that doesn't have the same target
* Add the concept of layer count for array instead of using depth
Also cleanup GetGpuSize as Swizzle can compute the size with mipmap
* Support multi layer with mip map in ReadTexture
* Add more check for cache invalidation & remove cubemap and cubemap array code for now
Also fix compressed 2d array
* Fix texelFetchOffset shader build error
* Start looking into cube map again
Also add some way to log write in register in engines
* fix write register log levles
* Remove debug logs in WriteRegister
* Disable AOFFI support on non NVIDIA drivers
* Fix code align
Diffstat (limited to 'Ryujinx.Graphics/Gal')
19 files changed, 1305 insertions, 136 deletions
diff --git a/Ryujinx.Graphics/Gal/GalImage.cs b/Ryujinx.Graphics/Gal/GalImage.cs index 92f43cc9..fb904b09 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 b9e5c765..1d658cea 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 00000000..bcc0c49a --- /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 f941ccd5..90cad856 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 aeecdf1a..de4ba9cb 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 8db0b8a8..5714f3d8 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 f2afe7b5..3a25fff7 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 11daeb59..52b3d0ce 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<bool> s_TextureMirrorClamp = new Lazy<bool>(() => HasExtension("GL_EXT_texture_mirror_clamp")); private static Lazy<bool> s_ViewportArray = new Lazy<bool>(() => HasExtension("GL_ARB_viewport_array")); + private static Lazy<bool> s_NvidiaDriver = new Lazy<bool>(() => 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 0d7bb3cd..8dd3b37f 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.BindTexture(Target, CachedImage.Handle); - GL.GetTexImage(TextureTarget.Texture2D, 0, Format, Type, IntPtr.Zero); + 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 10a9120d..dc168ff9 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 ef984b1e..4fef11d2 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<byte> Array = new Span<byte>(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 43923da7..f7ae34fa 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 854c827e..5f809525 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<ShaderIrInst, GetInstExpr>() { @@ -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<ShaderDeclInfo> 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 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 adcc47b9..8b4eacdf 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(); + + GalTextureTarget TextureTarget = TexToTextureTarget(OpCode.Read(28, 6), IsArray); + + bool HasDepthCompare = OpCode.Read(0x32); + + if (HasDepthCompare) + { + TextureInstructionSuffix |= TextureInstructionSuffix.DC; + } - for (int Index = 0; Index < Coords.Length; Index++) + 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); + } + + public static void Tld4(ShaderIrBlock Block, long OpCode, int Position) + { + 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) + private static void EmitTexs(ShaderIrBlock Block, + long OpCode, + ShaderIrInst Inst, + GalTextureTarget TextureTarget, + TextureInstructionSuffix TextureInstructionSuffix) { - //TODO: Support other formats. + 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 f0f92148..e241e1ca 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 35dea612..68ff214e 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 82f3bb77..72ea221a 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 177e36c3..d2bbd38c 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 ef47ca2e..ed1955cd 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) |
