diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index b46ceb4f..1420096e 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 5682; + private const uint CodeGenVersion = 5750; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index 796eb441..6b02f2bf 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -184,8 +184,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.TextureSample: return TextureSample(context, operation); - case Instruction.TextureSize: - return TextureSize(context, operation); + case Instruction.TextureQuerySamples: + return TextureQuerySamples(context, operation); + + case Instruction.TextureQuerySize: + return TextureQuerySize(context, operation); case Instruction.UnpackDouble2x32: return UnpackDouble2x32(context, operation); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs index eb194c20..5c2d16f4 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -118,7 +118,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.Subtract, InstType.OpBinary, "-", 2); Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd); Add(Instruction.TextureSample, InstType.Special); - Add(Instruction.TextureSize, InstType.Special); + Add(Instruction.TextureQuerySamples, InstType.Special); + Add(Instruction.TextureQuerySize, InstType.Special); Add(Instruction.Truncate, InstType.CallUnary, "trunc"); Add(Instruction.UnpackDouble2x32, InstType.Special); Add(Instruction.UnpackHalf2x16, InstType.Special); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index a1f92d11..7510e887 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -517,7 +517,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return texCall; } - public static string TextureSize(CodeGenContext context, AstOperation operation) + public static string TextureQuerySamples(CodeGenContext context, AstOperation operation) + { + AstTextureOperation texOp = (AstTextureOperation)operation; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + + // TODO: Bindless texture support. For now we just return 0. + if (isBindless) + { + return NumberFormatter.FormatInt(0); + } + + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + + string indexExpr = null; + + if (isIndexed) + { + indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32); + } + + string samplerName = GetSamplerName(context.Properties, texOp, indexExpr); + + return $"textureSamples({samplerName})"; + } + + public static string TextureQuerySize(CodeGenContext context, AstOperation operation) { AstTextureOperation texOp = (AstTextureOperation)operation; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index 719ccf0c..771723c2 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -134,7 +134,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv Add(Instruction.Subtract, GenerateSubtract); Add(Instruction.SwizzleAdd, GenerateSwizzleAdd); Add(Instruction.TextureSample, GenerateTextureSample); - Add(Instruction.TextureSize, GenerateTextureSize); + Add(Instruction.TextureQuerySamples, GenerateTextureQuerySamples); + Add(Instruction.TextureQuerySize, GenerateTextureQuerySize); Add(Instruction.Truncate, GenerateTruncate); Add(Instruction.UnpackDouble2x32, GenerateUnpackDouble2x32); Add(Instruction.UnpackHalf2x16, GenerateUnpackHalf2x16); @@ -1492,7 +1493,36 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv return new OperationResult(swizzledResultType, result); } - private static OperationResult GenerateTextureSize(CodeGenContext context, AstOperation operation) + private static OperationResult GenerateTextureQuerySamples(CodeGenContext context, AstOperation operation) + { + AstTextureOperation texOp = (AstTextureOperation)operation; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + + // TODO: Bindless texture support. For now we just return 0. + if (isBindless) + { + return new OperationResult(AggregateType.S32, context.Constant(context.TypeS32(), 0)); + } + + bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; + + if (isIndexed) + { + context.GetS32(texOp.GetSource(0)); + } + + (var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding]; + + var image = context.Load(sampledImageType, sampledImageVariable); + image = context.Image(imageType, image); + + SpvInstruction result = context.ImageQuerySamples(context.TypeS32(), image); + + return new OperationResult(AggregateType.S32, result); + } + + private static OperationResult GenerateTextureQuerySize(CodeGenContext context, AstOperation operation) { AstTextureOperation texOp = (AstTextureOperation)operation; diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index bbac8038..5a231079 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -1094,7 +1094,14 @@ namespace Ryujinx.Graphics.Shader.Instructions if (isBindless) { - type = (componentMask & 4) != 0 ? SamplerType.Texture3D : SamplerType.Texture2D; + if (query == TexQuery.TexHeaderTextureType) + { + type = SamplerType.Texture2D | SamplerType.Multisample; + } + else + { + type = (componentMask & 4) != 0 ? SamplerType.Texture3D : SamplerType.Texture2D; + } } else { @@ -1102,31 +1109,69 @@ namespace Ryujinx.Graphics.Shader.Instructions } TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None; + int binding; - int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( - Instruction.TextureSize, - type, - TextureFormat.Unknown, - flags, - TextureOperation.DefaultCbufSlot, - imm); - - for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) + switch (query) { - if ((compMask & 1) != 0) - { - Operand d = GetDest(); + case TexQuery.TexHeaderDimension: + binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( + Instruction.TextureQuerySize, + type, + TextureFormat.Unknown, + flags, + TextureOperation.DefaultCbufSlot, + imm); - if (d == null) + for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) { - break; + if ((compMask & 1) != 0) + { + Operand d = GetDest(); + + if (d == null) + { + break; + } + + context.Copy(d, context.TextureQuerySize(type, flags, binding, compIndex, sources)); + } } + break; - // TODO: Validate and use query parameter. - Operand res = context.TextureSize(type, flags, binding, compIndex, sources); + case TexQuery.TexHeaderTextureType: + binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding( + Instruction.TextureQuerySamples, + type, + TextureFormat.Unknown, + flags, + TextureOperation.DefaultCbufSlot, + imm); - context.Copy(d, res); - } + if ((componentMask & 4) != 0) + { + // Skip first 2 components if necessary. + if ((componentMask & 1) != 0) + { + GetDest(); + } + + if ((componentMask & 2) != 0) + { + GetDest(); + } + + Operand d = GetDest(); + + if (d != null) + { + context.Copy(d, context.TextureQuerySamples(type, flags, binding, sources)); + } + } + break; + + default: + context.TranslatorContext.GpuAccessor.Log($"Invalid or unsupported query type \"{query}\"."); + break; } } diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index 808cc7ed..e5695ebc 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -1,10 +1,8 @@ using System; -using System.Diagnostics.CodeAnalysis; namespace Ryujinx.Graphics.Shader.IntermediateRepresentation { [Flags] - [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] enum Instruction { Absolute = 1, @@ -118,7 +116,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Subtract, SwizzleAdd, TextureSample, - TextureSize, + TextureQuerySamples, + TextureQuerySize, Truncate, UnpackDouble2x32, UnpackHalf2x16, @@ -160,7 +159,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public static bool IsTextureQuery(this Instruction inst) { inst &= Instruction.Mask; - return inst == Instruction.Lod || inst == Instruction.TextureSize; + return inst == Instruction.Lod || inst == Instruction.TextureQuerySamples || inst == Instruction.TextureQuerySize; } } } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs index 1169512e..72d0e989 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs @@ -124,7 +124,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.Subtract, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.SwizzleAdd, AggregateType.FP32, AggregateType.FP32, AggregateType.FP32, AggregateType.S32); Add(Instruction.TextureSample, AggregateType.FP32); - Add(Instruction.TextureSize, AggregateType.S32, AggregateType.S32, AggregateType.S32); + Add(Instruction.TextureQuerySamples, AggregateType.S32, AggregateType.S32); + Add(Instruction.TextureQuerySize, AggregateType.S32, AggregateType.S32, AggregateType.S32); Add(Instruction.Truncate, AggregateType.Scalar, AggregateType.Scalar); Add(Instruction.UnpackDouble2x32, AggregateType.U32, AggregateType.FP64); Add(Instruction.UnpackHalf2x16, AggregateType.FP32, AggregateType.U32); diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index afa830de..9e314c62 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -897,7 +897,21 @@ namespace Ryujinx.Graphics.Shader.Translation context.Add(new TextureOperation(Instruction.TextureSample, type, TextureFormat.Unknown, flags, binding, compMask, dests, sources)); } - public static Operand TextureSize( + public static Operand TextureQuerySamples( + this EmitterContext context, + SamplerType type, + TextureFlags flags, + int binding, + Operand[] sources) + { + Operand dest = Local(); + + context.Add(new TextureOperation(Instruction.TextureQuerySamples, type, TextureFormat.Unknown, flags, binding, 0, new[] { dest }, sources)); + + return dest; + } + + public static Operand TextureQuerySize( this EmitterContext context, SamplerType type, TextureFlags flags, @@ -907,7 +921,7 @@ namespace Ryujinx.Graphics.Shader.Translation { Operand dest = Local(); - context.Add(new TextureOperation(Instruction.TextureSize, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources)); + context.Add(new TextureOperation(Instruction.TextureQuerySize, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources)); return dest; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index 43d98d3c..19b7999a 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -27,9 +27,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - if (texOp.Inst == Instruction.Lod || - texOp.Inst == Instruction.TextureSample || - texOp.Inst == Instruction.TextureSize) + if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery()) { Operand bindlessHandle = Utils.FindLastOperation(texOp.GetSource(0), block); @@ -40,7 +38,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations // as long bindless elimination is successful and we know where the texture descriptor is located. bool rewriteSamplerType = texOp.Type == SamplerType.TextureBuffer || - texOp.Inst == Instruction.TextureSize; + texOp.Inst == Instruction.TextureQuerySamples || + texOp.Inst == Instruction.TextureQuerySize; if (bindlessHandle.Type == OperandType.ConstantBuffer) { diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index 9c487c46..83332711 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -232,8 +232,8 @@ 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.Lod && inst != Instruction.TextureSize; - bool intCoords = isImage || flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureSize; + bool accurateType = !inst.IsTextureQuery(); + bool intCoords = isImage || flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureQuerySize; bool coherent = flags.HasFlag(TextureFlags.Coherent); if (!isImage) diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs index 2479d85f..dbfe6269 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms { node = InsertCoordNormalization(context.Hfm, node, context.ResourceManager, context.GpuAccessor, context.Stage); node = InsertCoordGatherBias(node, context.ResourceManager, context.GpuAccessor); - node = InsertConstOffsets(node, context.ResourceManager, context.GpuAccessor); + node = InsertConstOffsets(node, context.GpuAccessor, context.Stage); if (texOp.Type == SamplerType.TextureBuffer && !context.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat()) { @@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; - if (texOp.Inst == Instruction.TextureSize && + if (texOp.Inst == Instruction.TextureQuerySize && texOp.Index < 2 && !isBindless && !isIndexed && @@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms } LinkedListNode textureSizeNode = node.List.AddBefore(node, new TextureOperation( - Instruction.TextureSize, + Instruction.TextureQuerySize, texOp.Type, texOp.Format, texOp.Flags, @@ -259,7 +259,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms } node.List.AddBefore(node, new TextureOperation( - Instruction.TextureSize, + Instruction.TextureQuerySize, texOp.Type, texOp.Format, texOp.Flags, @@ -287,7 +287,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms return node; } - private static LinkedListNode InsertConstOffsets(LinkedListNode node, ResourceManager resourceManager, IGpuAccessor gpuAccessor) + private static LinkedListNode InsertConstOffsets(LinkedListNode node, IGpuAccessor gpuAccessor, ShaderStage stage) { // Non-constant texture offsets are not allowed (according to the spec), // however some GPUs does support that. @@ -440,7 +440,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms sources.CopyTo(newSources, 0); - Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount); + Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount, stage); int destIndex = 0; @@ -502,7 +502,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms } else { - Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount); + Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount, stage); for (int index = 0; index < coordsCount; index++) { @@ -554,21 +554,31 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms TextureOperation texOp, Operand[] lodSources, Operand bindlessHandle, - int coordsCount) + int coordsCount, + ShaderStage stage) { Operand[] texSizes = new Operand[coordsCount]; - Operand lod = Local(); + Operand lod; - node.List.AddBefore(node, new TextureOperation( - Instruction.Lod, - texOp.Type, - texOp.Format, - texOp.Flags, - texOp.Binding, - 0, - new[] { lod }, - lodSources)); + if (stage == ShaderStage.Fragment) + { + lod = Local(); + + node.List.AddBefore(node, new TextureOperation( + Instruction.Lod, + texOp.Type, + texOp.Format, + texOp.Flags, + texOp.Binding, + 0, + new[] { lod }, + lodSources)); + } + else + { + lod = Const(0); + } for (int index = 0; index < coordsCount; index++) { @@ -586,7 +596,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms } node.List.AddBefore(node, new TextureOperation( - Instruction.TextureSize, + Instruction.TextureQuerySize, texOp.Type, texOp.Format, texOp.Flags,