From f0b00c1ae92a2e8fdff9c532e7f2f79ad708b184 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 2 Sep 2021 04:17:43 +0100 Subject: [PATCH] Fix TXQ for 3D textures. (#2613) * Fix TXQ for 3D textures. Assumes the texture is 3D if the component mask contains Z. This fixes a bug in UE4 games where parts of the map had garbage pointers to lighting voxels, as the lookup 3D texture was not being initialized. Most notable game is THPS1+2. May need another PR to keep image store data alive and properly flush it in order using the AutoDeleteCache. * Get sampler type for TextureSize from bound textures. --- Ryujinx.Graphics.Gpu/Image/TextureTarget.cs | 23 +++++++++++++++++++ Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 2 +- .../TextureDescriptorCapableGpuAccessor.cs | 8 +++---- Ryujinx.Graphics.Shader/IGpuAccessor.cs | 8 ++++--- .../Instructions/InstEmitTexture.cs | 17 ++++++++++---- .../TextureOperation.cs | 2 +- .../Optimizations/BindlessElimination.cs | 16 +++++++++---- .../Translation/ShaderConfig.cs | 2 +- 8 files changed, 60 insertions(+), 18 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs b/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs index 1db758fcd2..5e0a0721f1 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; namespace Ryujinx.Graphics.Gpu.Image { @@ -54,5 +55,27 @@ namespace Ryujinx.Graphics.Gpu.Image return Target.Texture1D; } + + /// + /// Converts the texture target enum to a shader sampler type. + /// + /// The target enum to convert + /// The shader sampler type + public static SamplerType ConvertSamplerType(this TextureTarget target) + { + return target switch + { + TextureTarget.Texture1D => SamplerType.Texture1D, + TextureTarget.Texture2D => SamplerType.Texture2D, + TextureTarget.Texture3D => SamplerType.Texture3D, + TextureTarget.Cubemap => SamplerType.TextureCube, + TextureTarget.Texture1DArray => SamplerType.Texture1D | SamplerType.Array, + TextureTarget.Texture2DArray => SamplerType.Texture2D | SamplerType.Array, + TextureTarget.TextureBuffer => SamplerType.TextureBuffer, + TextureTarget.Texture2DRect => SamplerType.Texture2D, + TextureTarget.CubemapArray => SamplerType.TextureCube | SamplerType.Array, + _ => SamplerType.Texture2D + }; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 0d47955964..cda3ecb32c 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Version of the codegen (to be changed when codegen or guest format change). /// - private const ulong ShaderCodeGenVersion = 2092; + private const ulong ShaderCodeGenVersion = 2613; // Progress reporting helpers private volatile int _shaderCount; diff --git a/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs index 54b4133a08..7c4eea02f8 100644 --- a/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs @@ -119,14 +119,14 @@ namespace Ryujinx.Graphics.Gpu.Shader } /// - /// Queries texture target information. + /// Queries sampler type information. /// /// Texture handle /// Constant buffer slot for the texture handle - /// True if the texture is a buffer texture, false otherwise - public bool QueryIsTextureBuffer(int handle, int cbufSlot = -1) + /// The sampler type value for the given handle + public SamplerType QuerySamplerType(int handle, int cbufSlot = -1) { - return GetTextureDescriptor(handle, cbufSlot).UnpackTextureTarget() == TextureTarget.TextureBuffer; + return GetTextureDescriptor(handle, cbufSlot).UnpackTextureTarget().ConvertSamplerType(); } /// diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 84c30479b6..9ae399f796 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -1,4 +1,6 @@ -namespace Ryujinx.Graphics.Shader +using Ryujinx.Graphics.Shader.Decoders; + +namespace Ryujinx.Graphics.Shader { public interface IGpuAccessor { @@ -79,9 +81,9 @@ return true; } - bool QueryIsTextureBuffer(int handle, int cbufSlot = -1) + SamplerType QuerySamplerType(int handle, int cbufSlot = -1) { - return false; + return SamplerType.Texture2D; } bool QueryIsTextureRectangle(int handle, int cbufSlot = -1) diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index ce63398f92..88bafd7656 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -697,7 +697,7 @@ namespace Ryujinx.Graphics.Shader.Instructions flags = ConvertTextureFlags(tldsOp.Target) | TextureFlags.IntCoords; - if (tldsOp.Target == TexelLoadTarget.Texture1DLodZero && context.Config.GpuAccessor.QueryIsTextureBuffer(tldsOp.HandleOffset)) + if (tldsOp.Target == TexelLoadTarget.Texture1DLodZero && context.Config.GpuAccessor.QuerySamplerType(tldsOp.HandleOffset) == SamplerType.TextureBuffer) { type = SamplerType.TextureBuffer; flags &= ~TextureFlags.LodLevel; @@ -1306,8 +1306,6 @@ namespace Ryujinx.Graphics.Shader.Instructions // TODO: Validate and use property. Instruction inst = Instruction.TextureSize; - SamplerType type = SamplerType.Texture2D; - TextureFlags flags = bindless ? TextureFlags.Bindless : TextureFlags.None; int raIndex = op.Ra.Index; @@ -1347,6 +1345,17 @@ namespace Ryujinx.Graphics.Shader.Instructions int handle = !bindless ? op.HandleOffset : 0; + SamplerType type; + + if (bindless) + { + type = (op.ComponentMask & 4) != 0 ? SamplerType.Texture3D : SamplerType.Texture2D; + } + else + { + type = context.Config.GpuAccessor.QuerySamplerType(handle); + } + for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) { if ((compMask & 1) != 0) @@ -1422,7 +1431,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { // For bindless, we don't have any way to know the texture type, // so we assume it's texture buffer when the sampler type is 1D, since that's more common. - bool isTypeBuffer = isBindless || context.Config.GpuAccessor.QueryIsTextureBuffer(op.HandleOffset); + bool isTypeBuffer = isBindless || context.Config.GpuAccessor.QuerySamplerType(op.HandleOffset) == SamplerType.TextureBuffer; if (isTypeBuffer) { diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs index e80f9c113d..ea9ae39ce3 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation { public const int DefaultCbufSlot = -1; - public SamplerType Type { get; private set; } + public SamplerType Type { get; set; } public TextureFormat Format { get; set; } public TextureFlags Flags { get; private set; } diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index 709668f4aa..e2f2b752a7 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -30,10 +30,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations texOp.Inst == Instruction.TextureSize) { Operand bindlessHandle = Utils.FindLastOperation(texOp.GetSource(0), block); + bool rewriteSamplerType = texOp.Inst == Instruction.TextureSize; if (bindlessHandle.Type == OperandType.ConstantBuffer) { - SetHandle(config, texOp, bindlessHandle.GetCbufOffset(), bindlessHandle.GetCbufSlot()); + SetHandle(config, texOp, bindlessHandle.GetCbufOffset(), bindlessHandle.GetCbufSlot(), rewriteSamplerType); continue; } @@ -59,7 +60,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations config, texOp, src0.GetCbufOffset() | ((src1.GetCbufOffset() + 1) << 16), - src0.GetCbufSlot() | ((src1.GetCbufSlot() + 1) << 16)); + src0.GetCbufSlot() | ((src1.GetCbufSlot() + 1) << 16), + rewriteSamplerType); } else if (texOp.Inst == Instruction.ImageLoad || texOp.Inst == Instruction.ImageStore || @@ -81,15 +83,21 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations texOp.Format = config.GetTextureFormat(cbufOffset, cbufSlot); } - SetHandle(config, texOp, cbufOffset, cbufSlot); + SetHandle(config, texOp, cbufOffset, cbufSlot, false); } } } } - private static void SetHandle(ShaderConfig config, TextureOperation texOp, int cbufOffset, int cbufSlot) + private static void SetHandle(ShaderConfig config, TextureOperation texOp, int cbufOffset, int cbufSlot, bool rewriteSamplerType) { texOp.SetHandle(cbufOffset, cbufSlot); + + if (rewriteSamplerType) + { + texOp.Type = config.GpuAccessor.QuerySamplerType(cbufOffset, cbufSlot); + } + config.SetUsedTexture(texOp.Inst, texOp.Type, texOp.Format, texOp.Flags, cbufSlot, cbufOffset); } } diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 7d3246db16..72fa7733fd 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -294,7 +294,7 @@ namespace Ryujinx.Graphics.Shader.Translation inst &= Instruction.Mask; bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore || inst == Instruction.ImageAtomic; bool isWrite = inst == Instruction.ImageStore || inst == Instruction.ImageAtomic; - bool accurateType = inst != Instruction.TextureSize && inst != Instruction.Lod; + bool accurateType = inst != Instruction.Lod; if (isImage) {