diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs
index cbf1573cd..b2935a5b4 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 b4f56245e..c0c2d5b30 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 50c042fb9..b236476e0 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 7f01aca63..b5f9395e7 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 672b3b8d1..4bab165da 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 ca9c883e3..1fcc93c50 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 544e689ab..f392491c3 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 775bfb2af..b33f96c57 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 2370b49f0..2a45e23de 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 c8fcd75a1..217979757 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 1c2b28097..a47791d3f 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 1876847c4..c7bd0fd63 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 e87769bb9..f9776afc0 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 048a260ab..b7e379c6b 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 24a99345a..0b7a2edd6 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 c92d05838..6cb572381 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 5741d0288..27b46867d 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),