From f95b7c58779f01d9077996da67953d8d9acd058c Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 29 Jul 2023 18:47:03 -0300 Subject: [PATCH] Fix incorrect fragment origin when YNegate is enabled (#4673) * Fix incorrect fragment origin when YNegate is enabled * Shader cache version bump * Do not update support buffer if shader does not read gl_FragCoord * Pass unscaled viewport size to the support buffer --- .../Threed/SpecializationStateUpdater.cs | 14 ++++++ .../Engine/Threed/StateUpdater.cs | 47 +++++++++++++++++-- .../Memory/SupportBufferUpdater.cs | 30 ++++++++++++ .../Shader/DiskCache/DiskCacheGpuAccessor.cs | 6 +++ .../Shader/DiskCache/DiskCacheHostStorage.cs | 9 +++- .../Shader/GpuAccessor.cs | 20 +++++--- .../Shader/GpuChannelGraphicsState.cs | 12 ++++- .../Shader/ShaderSpecializationState.cs | 5 ++ .../CodeGen/Glsl/Declarations.cs | 15 ++++-- .../CodeGen/Spirv/SpirvGenerator.cs | 2 +- src/Ryujinx.Graphics.Shader/IGpuAccessor.cs | 21 ++++++--- .../Instructions/InstEmitAttribute.cs | 12 +++++ .../ShaderProgramInfo.cs | 3 ++ .../StructuredIr/ShaderProperties.cs | 7 +++ src/Ryujinx.Graphics.Shader/SupportBuffer.cs | 5 ++ .../Translation/EmitterContextInsts.cs | 5 ++ .../Translation/ShaderConfig.cs | 20 ++++++-- 17 files changed, 207 insertions(+), 26 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs index cbf1573cdd..b2935a5b45 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs @@ -342,5 +342,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Signal(); } } + + /// + /// Sets the Y negate enabled state. + /// + /// True if Y negate of the fragment coordinates is enabled + public void SetYNegateEnabled(bool enabled) + { + if (enabled != _graphics.YNegateEnabled) + { + _graphics.YNegateEnabled = enabled; + + Signal(); + } + } } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index b4f56245e1..c0c2d5b301 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -37,6 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private ProgramPipelineState _pipeline; + private bool _fsReadsFragCoord; private bool _vsUsesDrawParameters; private bool _vtgWritesRtLayer; private byte _vsClipDistancesWritten; @@ -692,12 +693,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed var face = _state.State.FaceState; bool disableTransform = _state.State.ViewportTransformEnable == 0; + bool yNegate = yControl.HasFlag(YControl.NegateY); UpdateFrontFace(yControl, face.FrontFace); UpdateDepthMode(); - bool flipY = yControl.HasFlag(YControl.NegateY); - Span viewports = stackalloc Viewport[Constants.TotalViewports]; for (int index = 0; index < Constants.TotalViewports; index++) @@ -719,7 +719,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed float scaleX = MathF.Abs(transform.ScaleX); float scaleY = transform.ScaleY; - if (flipY) + if (yNegate) { scaleY = -scaleY; } @@ -771,8 +771,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _channel.TextureManager.RenderTargetScale, disableTransform); + // Viewport size is only used on the shader when YNegate is enabled, + // and if the fragment shader accesses gl_FragCoord, + // so there's no need to update it in other cases. + if (yNegate && _fsReadsFragCoord) + { + UpdateSupportBufferViewportSize(); + } + _currentSpecState.SetViewportTransformDisable(disableTransform); _currentSpecState.SetDepthMode(GetDepthMode() == DepthMode.MinusOneToOne); + _currentSpecState.SetYNegateEnabled(yNegate); } /// @@ -1415,9 +1424,41 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _currentProgramInfo[stageIndex] = info; } + if (gs.Shaders[5]?.Info.UsesFragCoord == true) + { + // Make sure we update the viewport size on the support buffer if it will be consumed on the new shader. + + if (!_fsReadsFragCoord && _state.State.YControl.HasFlag(YControl.NegateY)) + { + UpdateSupportBufferViewportSize(); + } + + _fsReadsFragCoord = true; + } + else + { + _fsReadsFragCoord = false; + } + _context.Renderer.Pipeline.SetProgram(gs.HostProgram); } + /// + /// Updates the viewport size on the support buffer for fragment shader access. + /// + private void UpdateSupportBufferViewportSize() + { + ref var transform = ref _state.State.ViewportTransform[0]; + + float scaleX = MathF.Abs(transform.ScaleX); + float scaleY = transform.ScaleY; + + float width = scaleX * 2; + float height = scaleY * 2; + + _context.SupportBufferUpdater.SetViewportSize(width, MathF.Abs(height)); + } + /// /// Updates bindings consumed by the shader on the texture and buffer managers. /// diff --git a/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs b/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs index 50c042fb96..b236476e07 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs @@ -112,6 +112,17 @@ namespace Ryujinx.Graphics.Gpu.Memory MarkDirty(SupportBuffer.ViewportInverseOffset, SupportBuffer.FieldSize); } + /// + /// Updates the viewport size vector. + /// + /// Viewport size vector + private void UpdateViewportSize(Vector4 data) + { + _data.ViewportSize = data; + + MarkDirty(SupportBuffer.ViewportSizeOffset, SupportBuffer.FieldSize); + } + /// /// Sets the scale of all output render targets (they should all have the same scale). /// @@ -192,6 +203,25 @@ namespace Ryujinx.Graphics.Gpu.Memory } } + /// + /// Sets the viewport size, used to invert the fragment coordinates Y value. + /// + /// Value used as viewport width + /// Value used as viewport height + public void SetViewportSize(float viewportWidth, float viewportHeight) + { + if (_data.ViewportSize.X != viewportWidth || _data.ViewportSize.Y != viewportHeight) + { + UpdateViewportSize(new Vector4 + { + X = viewportWidth, + Y = viewportHeight, + Z = 1, + W = 0 + }); + } + } + /// /// Submits all pending buffer updates to the GPU. /// diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs index 7f01aca635..b5f9395e79 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs @@ -247,6 +247,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return _oldSpecState.GraphicsState.ViewportTransformDisable; } + /// + public bool QueryYNegateEnabled() + { + return _oldSpecState.GraphicsState.YNegateEnabled; + } + /// public void RegisterTexture(int handle, int cbufSlot) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 672b3b8d13..4bab165da8 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 = 5266; + private const uint CodeGenVersion = 4675; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; @@ -140,6 +140,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// public ShaderStage Stage; + /// + /// Indicates if the fragment shader accesses the fragment coordinate built-in variable. + /// + public bool UsesFragCoord; + /// /// Indicates if the shader accesses the Instance ID built-in variable. /// @@ -781,6 +786,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache ShaderIdentification.None, 0, dataInfo.Stage, + dataInfo.UsesFragCoord, dataInfo.UsesInstanceId, dataInfo.UsesDrawParameters, dataInfo.UsesRtLayer, @@ -807,6 +813,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache TexturesCount = (ushort)info.Textures.Count, ImagesCount = (ushort)info.Images.Count, Stage = info.Stage, + UsesFragCoord = info.UsesFragCoord, UsesInstanceId = info.UsesInstanceId, UsesDrawParameters = info.UsesDrawParameters, UsesRtLayer = info.UsesRtLayer, diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index ca9c883e38..1fcc93c503 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -113,6 +113,13 @@ namespace Ryujinx.Graphics.Gpu.Shader return _state.GraphicsState.AttributeTypes[location]; } + /// + public bool QueryEarlyZForce() + { + _state.SpecializationState?.RecordEarlyZForce(); + return _state.GraphicsState.EarlyZForce; + } + /// public AttributeType QueryFragmentOutputType(int location) { @@ -275,19 +282,18 @@ namespace Ryujinx.Graphics.Gpu.Shader return _state.TransformFeedbackDescriptors[bufferIndex].Stride; } - /// - public bool QueryEarlyZForce() - { - _state.SpecializationState?.RecordEarlyZForce(); - return _state.GraphicsState.EarlyZForce; - } - /// public bool QueryViewportTransformDisable() { return _state.GraphicsState.ViewportTransformDisable; } + /// + public bool QueryYNegateEnabled() + { + return _state.GraphicsState.YNegateEnabled; + } + /// public void RegisterTexture(int handle, int cbufSlot) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs index 544e689ab8..f392491c37 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs @@ -97,6 +97,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// public bool DualSourceBlendEnable; + /// + /// Indicates whether Y negate of the fragment coordinates is enabled. + /// + public bool YNegateEnabled; + /// /// Creates a new GPU graphics state. /// @@ -116,7 +121,8 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0 /// Indicates that any storage buffer use is unaligned /// Type of the fragment shader outputs - /// Type of the vertex attributes consumed by the shader + /// Indicates whether dual source blend is enabled + /// Indicates whether Y negate of the fragment coordinates is enabled public GpuChannelGraphicsState( bool earlyZForce, PrimitiveTopology topology, @@ -134,7 +140,8 @@ namespace Ryujinx.Graphics.Gpu.Shader bool hasConstantBufferDrawParameters, bool hasUnalignedStorageBuffer, ref Array8 fragmentOutputTypes, - bool dualSourceBlendEnable) + bool dualSourceBlendEnable, + bool yNegateEnabled) { EarlyZForce = earlyZForce; Topology = topology; @@ -153,6 +160,7 @@ namespace Ryujinx.Graphics.Gpu.Shader HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; FragmentOutputTypes = fragmentOutputTypes; DualSourceBlendEnable = dualSourceBlendEnable; + YNegateEnabled = yNegateEnabled; } } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index 775bfb2af0..b33f96c579 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -540,6 +540,11 @@ namespace Ryujinx.Graphics.Gpu.Shader return false; } + if (graphicsState.YNegateEnabled != GraphicsState.YNegateEnabled) + { + return false; + } + return Matches(channel, ref poolState, checkTextures, isCompute: false); } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 2370b49f01..2a45e23dec 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -188,10 +188,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } - if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryEarlyZForce()) + if (context.Config.Stage == ShaderStage.Fragment) { - context.AppendLine("layout(early_fragment_tests) in;"); - context.AppendLine(); + if (context.Config.GpuAccessor.QueryEarlyZForce()) + { + context.AppendLine("layout (early_fragment_tests) in;"); + context.AppendLine(); + } + + if (context.Config.Properties.OriginUpperLeft) + { + context.AppendLine("layout (origin_upper_left) in vec4 gl_FragCoord;"); + context.AppendLine(); + } } if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index c8fcd75a16..2179797573 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -251,7 +251,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } else if (context.Config.Stage == ShaderStage.Fragment) { - context.AddExecutionMode(spvFunc, context.Config.Options.TargetApi == TargetApi.Vulkan + context.AddExecutionMode(spvFunc, context.Config.Properties.OriginUpperLeft ? ExecutionMode.OriginUpperLeft : ExecutionMode.OriginLowerLeft); diff --git a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 1c2b280972..a47791d3f6 100644 --- a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -178,6 +178,15 @@ namespace Ryujinx.Graphics.Shader return 0; } + /// + /// Queries if host state forces early depth testing. + /// + /// True if early depth testing is forced + bool QueryEarlyZForce() + { + return false; + } + /// /// Queries whenever the current draw has written the base vertex and base instance into Constant Buffer 0. /// @@ -534,19 +543,19 @@ namespace Ryujinx.Graphics.Shader } /// - /// Queries if host state forces early depth testing. + /// Queries if host state disables the viewport transform. /// - /// True if early depth testing is forced - bool QueryEarlyZForce() + /// True if the viewport transform is disabled + bool QueryViewportTransformDisable() { return false; } /// - /// Queries if host state disables the viewport transform. + /// Queries Y negate enable state. /// - /// True if the viewport transform is disabled - bool QueryViewportTransformDisable() + /// True if Y negate of the fragment coordinates is enabled, false otherwise + bool QueryYNegateEnabled() { return false; } diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs index 1876847c41..c7bd0fd63a 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs @@ -161,6 +161,18 @@ namespace Ryujinx.Graphics.Shader.Instructions // FragCoord X/Y must be divided by the render target scale, if resolution scaling is active, // because the shader code is not expecting scaled values. res = context.FPDivide(res, context.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.RenderScale), Const(0))); + + if (op.Imm10 == AttributeConsts.PositionY && context.Config.Options.TargetApi != TargetApi.OpenGL) + { + // If YNegate is enabled, we need to flip the fragment coordinates vertically, unless + // the API supports changing the origin (only OpenGL does). + if (context.Config.GpuAccessor.QueryYNegateEnabled()) + { + Operand viewportHeight = context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.ViewportSize), Const(1)); + + res = context.FPSubtract(viewportHeight, res); + } + } } else if (op.Imm10 == AttributeConsts.FrontFacing && context.Config.GpuAccessor.QueryHostHasFrontFacingBug()) { diff --git a/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs b/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs index e87769bb9f..f9776afc04 100644 --- a/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs +++ b/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs @@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Shader public ShaderIdentification Identification { get; } public int GpLayerInputAttribute { get; } public ShaderStage Stage { get; } + public bool UsesFragCoord { get; } public bool UsesInstanceId { get; } public bool UsesDrawParameters { get; } public bool UsesRtLayer { get; } @@ -27,6 +28,7 @@ namespace Ryujinx.Graphics.Shader ShaderIdentification identification, int gpLayerInputAttribute, ShaderStage stage, + bool usesFragCoord, bool usesInstanceId, bool usesDrawParameters, bool usesRtLayer, @@ -41,6 +43,7 @@ namespace Ryujinx.Graphics.Shader Identification = identification; GpLayerInputAttribute = gpLayerInputAttribute; Stage = stage; + UsesFragCoord = usesFragCoord; UsesInstanceId = usesInstanceId; UsesDrawParameters = usesDrawParameters; UsesRtLayer = usesRtLayer; diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs index 048a260ab6..b7e379c6bc 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs @@ -18,6 +18,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public IReadOnlyDictionary LocalMemories => _localMemories; public IReadOnlyDictionary SharedMemories => _sharedMemories; + public readonly bool OriginUpperLeft; + public ShaderProperties() { _constantBuffers = new Dictionary(); @@ -28,6 +30,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr _sharedMemories = new Dictionary(); } + public ShaderProperties(bool originUpperLeft) : this() + { + OriginUpperLeft = originUpperLeft; + } + public void AddOrUpdateConstantBuffer(int binding, BufferDefinition definition) { _constantBuffers[binding] = definition; diff --git a/src/Ryujinx.Graphics.Shader/SupportBuffer.cs b/src/Ryujinx.Graphics.Shader/SupportBuffer.cs index 24a99345a8..0b7a2edd6a 100644 --- a/src/Ryujinx.Graphics.Shader/SupportBuffer.cs +++ b/src/Ryujinx.Graphics.Shader/SupportBuffer.cs @@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Shader FragmentAlphaTest, FragmentIsBgra, ViewportInverse, + ViewportSize, FragmentRenderScaleCount, RenderScale, } @@ -33,6 +34,7 @@ namespace Ryujinx.Graphics.Shader public static readonly int FragmentAlphaTestOffset; public static readonly int FragmentIsBgraOffset; public static readonly int ViewportInverseOffset; + public static readonly int ViewportSizeOffset; public static readonly int FragmentRenderScaleCountOffset; public static readonly int GraphicsRenderScaleOffset; public static readonly int ComputeRenderScaleOffset; @@ -56,6 +58,7 @@ namespace Ryujinx.Graphics.Shader FragmentAlphaTestOffset = OffsetOf(ref instance, ref instance.FragmentAlphaTest); FragmentIsBgraOffset = OffsetOf(ref instance, ref instance.FragmentIsBgra); ViewportInverseOffset = OffsetOf(ref instance, ref instance.ViewportInverse); + ViewportSizeOffset = OffsetOf(ref instance, ref instance.ViewportSize); FragmentRenderScaleCountOffset = OffsetOf(ref instance, ref instance.FragmentRenderScaleCount); GraphicsRenderScaleOffset = OffsetOf(ref instance, ref instance.RenderScale); ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize; @@ -68,6 +71,7 @@ namespace Ryujinx.Graphics.Shader new StructureField(AggregateType.U32, "s_alpha_test"), new StructureField(AggregateType.Array | AggregateType.U32, "s_is_bgra", FragmentIsBgraCount), new StructureField(AggregateType.Vector4 | AggregateType.FP32, "s_viewport_inverse"), + new StructureField(AggregateType.Vector4 | AggregateType.FP32, "s_viewport_size"), new StructureField(AggregateType.S32, "s_frag_scale_count"), new StructureField(AggregateType.Array | AggregateType.FP32, "s_render_scale", RenderScaleMaxCount), }); @@ -76,6 +80,7 @@ namespace Ryujinx.Graphics.Shader public Vector4 FragmentAlphaTest; public Array8> FragmentIsBgra; public Vector4 ViewportInverse; + public Vector4 ViewportSize; public Vector4 FragmentRenderScaleCount; // Render scale max count: 1 + 64 + 8. First scale is fragment output scale, others are textures/image inputs. diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index c92d058385..6cb572381f 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -429,6 +429,11 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.FP32 | Instruction.SquareRoot, Local(), a); } + public static Operand FPSubtract(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32) + { + return context.Add(fpType | Instruction.Subtract, Local(), a, b); + } + public static Operand FPTruncate(this EmitterContext context, Operand a, Instruction fpType = Instruction.FP32) { return context.Add(fpType | Instruction.Truncate, Local(), a); diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 5741d0288a..27b46867d8 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -123,7 +123,20 @@ namespace Ryujinx.Graphics.Shader.Translation UsedInputAttributesPerPatch = new HashSet(); UsedOutputAttributesPerPatch = new HashSet(); - ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties()); + ShaderProperties properties; + + switch (stage) + { + case ShaderStage.Fragment: + bool originUpperLeft = options.TargetApi == TargetApi.Vulkan || gpuAccessor.QueryYNegateEnabled(); + properties = new ShaderProperties(originUpperLeft); + break; + default: + properties = new ShaderProperties(); + break; + } + + ResourceManager = new ResourceManager(stage, gpuAccessor, properties); if (!gpuAccessor.QueryHostSupportsTransformFeedback() && gpuAccessor.QueryTransformFeedbackEnabled()) { @@ -135,7 +148,7 @@ namespace Ryujinx.Graphics.Shader.Translation BufferDefinition tfeInfoBuffer = new(BufferLayout.Std430, 1, Constants.TfeInfoBinding, "tfe_info", tfeInfoStruct); - Properties.AddOrUpdateStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer); + properties.AddOrUpdateStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer); StructureType tfeDataStruct = new(new StructureField[] { @@ -146,7 +159,7 @@ namespace Ryujinx.Graphics.Shader.Translation { int binding = Constants.TfeBufferBaseBinding + i; BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct); - Properties.AddOrUpdateStorageBuffer(binding, tfeDataBuffer); + properties.AddOrUpdateStorageBuffer(binding, tfeDataBuffer); } } } @@ -615,6 +628,7 @@ namespace Ryujinx.Graphics.Shader.Translation identification, GpLayerInputAttribute, Stage, + UsedFeatures.HasFlag(FeatureFlags.FragCoordXY), UsedFeatures.HasFlag(FeatureFlags.InstanceId), UsedFeatures.HasFlag(FeatureFlags.DrawParameters), UsedFeatures.HasFlag(FeatureFlags.RtLayer),