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
This commit is contained in:
gdkchan 2023-07-29 18:47:03 -03:00 committed by GitHub
parent eb528ae0f0
commit f95b7c5877
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 207 additions and 26 deletions

View file

@ -342,5 +342,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
Signal(); Signal();
} }
} }
/// <summary>
/// Sets the Y negate enabled state.
/// </summary>
/// <param name="enabled">True if Y negate of the fragment coordinates is enabled</param>
public void SetYNegateEnabled(bool enabled)
{
if (enabled != _graphics.YNegateEnabled)
{
_graphics.YNegateEnabled = enabled;
Signal();
}
}
} }
} }

View file

@ -37,6 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private ProgramPipelineState _pipeline; private ProgramPipelineState _pipeline;
private bool _fsReadsFragCoord;
private bool _vsUsesDrawParameters; private bool _vsUsesDrawParameters;
private bool _vtgWritesRtLayer; private bool _vtgWritesRtLayer;
private byte _vsClipDistancesWritten; private byte _vsClipDistancesWritten;
@ -692,12 +693,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
var face = _state.State.FaceState; var face = _state.State.FaceState;
bool disableTransform = _state.State.ViewportTransformEnable == 0; bool disableTransform = _state.State.ViewportTransformEnable == 0;
bool yNegate = yControl.HasFlag(YControl.NegateY);
UpdateFrontFace(yControl, face.FrontFace); UpdateFrontFace(yControl, face.FrontFace);
UpdateDepthMode(); UpdateDepthMode();
bool flipY = yControl.HasFlag(YControl.NegateY);
Span<Viewport> viewports = stackalloc Viewport[Constants.TotalViewports]; Span<Viewport> viewports = stackalloc Viewport[Constants.TotalViewports];
for (int index = 0; index < Constants.TotalViewports; index++) 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 scaleX = MathF.Abs(transform.ScaleX);
float scaleY = transform.ScaleY; float scaleY = transform.ScaleY;
if (flipY) if (yNegate)
{ {
scaleY = -scaleY; scaleY = -scaleY;
} }
@ -771,8 +771,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_channel.TextureManager.RenderTargetScale, _channel.TextureManager.RenderTargetScale,
disableTransform); 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.SetViewportTransformDisable(disableTransform);
_currentSpecState.SetDepthMode(GetDepthMode() == DepthMode.MinusOneToOne); _currentSpecState.SetDepthMode(GetDepthMode() == DepthMode.MinusOneToOne);
_currentSpecState.SetYNegateEnabled(yNegate);
} }
/// <summary> /// <summary>
@ -1415,9 +1424,41 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_currentProgramInfo[stageIndex] = info; _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); _context.Renderer.Pipeline.SetProgram(gs.HostProgram);
} }
/// <summary>
/// Updates the viewport size on the support buffer for fragment shader access.
/// </summary>
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));
}
/// <summary> /// <summary>
/// Updates bindings consumed by the shader on the texture and buffer managers. /// Updates bindings consumed by the shader on the texture and buffer managers.
/// </summary> /// </summary>

View file

@ -112,6 +112,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
MarkDirty(SupportBuffer.ViewportInverseOffset, SupportBuffer.FieldSize); MarkDirty(SupportBuffer.ViewportInverseOffset, SupportBuffer.FieldSize);
} }
/// <summary>
/// Updates the viewport size vector.
/// </summary>
/// <param name="data">Viewport size vector</param>
private void UpdateViewportSize(Vector4<float> data)
{
_data.ViewportSize = data;
MarkDirty(SupportBuffer.ViewportSizeOffset, SupportBuffer.FieldSize);
}
/// <summary> /// <summary>
/// Sets the scale of all output render targets (they should all have the same scale). /// Sets the scale of all output render targets (they should all have the same scale).
/// </summary> /// </summary>
@ -192,6 +203,25 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
} }
/// <summary>
/// Sets the viewport size, used to invert the fragment coordinates Y value.
/// </summary>
/// <param name="viewportWidth">Value used as viewport width</param>
/// <param name="viewportHeight">Value used as viewport height</param>
public void SetViewportSize(float viewportWidth, float viewportHeight)
{
if (_data.ViewportSize.X != viewportWidth || _data.ViewportSize.Y != viewportHeight)
{
UpdateViewportSize(new Vector4<float>
{
X = viewportWidth,
Y = viewportHeight,
Z = 1,
W = 0
});
}
}
/// <summary> /// <summary>
/// Submits all pending buffer updates to the GPU. /// Submits all pending buffer updates to the GPU.
/// </summary> /// </summary>

View file

@ -247,6 +247,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return _oldSpecState.GraphicsState.ViewportTransformDisable; return _oldSpecState.GraphicsState.ViewportTransformDisable;
} }
/// <inheritdoc/>
public bool QueryYNegateEnabled()
{
return _oldSpecState.GraphicsState.YNegateEnabled;
}
/// <inheritdoc/> /// <inheritdoc/>
public void RegisterTexture(int handle, int cbufSlot) public void RegisterTexture(int handle, int cbufSlot)
{ {

View file

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; 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 SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";
@ -140,6 +140,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// </summary> /// </summary>
public ShaderStage Stage; public ShaderStage Stage;
/// <summary>
/// Indicates if the fragment shader accesses the fragment coordinate built-in variable.
/// </summary>
public bool UsesFragCoord;
/// <summary> /// <summary>
/// Indicates if the shader accesses the Instance ID built-in variable. /// Indicates if the shader accesses the Instance ID built-in variable.
/// </summary> /// </summary>
@ -781,6 +786,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
ShaderIdentification.None, ShaderIdentification.None,
0, 0,
dataInfo.Stage, dataInfo.Stage,
dataInfo.UsesFragCoord,
dataInfo.UsesInstanceId, dataInfo.UsesInstanceId,
dataInfo.UsesDrawParameters, dataInfo.UsesDrawParameters,
dataInfo.UsesRtLayer, dataInfo.UsesRtLayer,
@ -807,6 +813,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
TexturesCount = (ushort)info.Textures.Count, TexturesCount = (ushort)info.Textures.Count,
ImagesCount = (ushort)info.Images.Count, ImagesCount = (ushort)info.Images.Count,
Stage = info.Stage, Stage = info.Stage,
UsesFragCoord = info.UsesFragCoord,
UsesInstanceId = info.UsesInstanceId, UsesInstanceId = info.UsesInstanceId,
UsesDrawParameters = info.UsesDrawParameters, UsesDrawParameters = info.UsesDrawParameters,
UsesRtLayer = info.UsesRtLayer, UsesRtLayer = info.UsesRtLayer,

View file

@ -113,6 +113,13 @@ namespace Ryujinx.Graphics.Gpu.Shader
return _state.GraphicsState.AttributeTypes[location]; return _state.GraphicsState.AttributeTypes[location];
} }
/// <inheritdoc/>
public bool QueryEarlyZForce()
{
_state.SpecializationState?.RecordEarlyZForce();
return _state.GraphicsState.EarlyZForce;
}
/// <inheritdoc/> /// <inheritdoc/>
public AttributeType QueryFragmentOutputType(int location) public AttributeType QueryFragmentOutputType(int location)
{ {
@ -275,19 +282,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
return _state.TransformFeedbackDescriptors[bufferIndex].Stride; return _state.TransformFeedbackDescriptors[bufferIndex].Stride;
} }
/// <inheritdoc/>
public bool QueryEarlyZForce()
{
_state.SpecializationState?.RecordEarlyZForce();
return _state.GraphicsState.EarlyZForce;
}
/// <inheritdoc/> /// <inheritdoc/>
public bool QueryViewportTransformDisable() public bool QueryViewportTransformDisable()
{ {
return _state.GraphicsState.ViewportTransformDisable; return _state.GraphicsState.ViewportTransformDisable;
} }
/// <inheritdoc/>
public bool QueryYNegateEnabled()
{
return _state.GraphicsState.YNegateEnabled;
}
/// <inheritdoc/> /// <inheritdoc/>
public void RegisterTexture(int handle, int cbufSlot) public void RegisterTexture(int handle, int cbufSlot)
{ {

View file

@ -97,6 +97,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary> /// </summary>
public bool DualSourceBlendEnable; public bool DualSourceBlendEnable;
/// <summary>
/// Indicates whether Y negate of the fragment coordinates is enabled.
/// </summary>
public bool YNegateEnabled;
/// <summary> /// <summary>
/// Creates a new GPU graphics state. /// Creates a new GPU graphics state.
/// </summary> /// </summary>
@ -116,7 +121,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param> /// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param>
/// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param> /// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param>
/// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param> /// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param>
/// <param name="dualSourceBlendEnable">Type of the vertex attributes consumed by the shader</param> /// <param name="dualSourceBlendEnable">Indicates whether dual source blend is enabled</param>
/// <param name="yNegateEnabled">Indicates whether Y negate of the fragment coordinates is enabled</param>
public GpuChannelGraphicsState( public GpuChannelGraphicsState(
bool earlyZForce, bool earlyZForce,
PrimitiveTopology topology, PrimitiveTopology topology,
@ -134,7 +140,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
bool hasConstantBufferDrawParameters, bool hasConstantBufferDrawParameters,
bool hasUnalignedStorageBuffer, bool hasUnalignedStorageBuffer,
ref Array8<AttributeType> fragmentOutputTypes, ref Array8<AttributeType> fragmentOutputTypes,
bool dualSourceBlendEnable) bool dualSourceBlendEnable,
bool yNegateEnabled)
{ {
EarlyZForce = earlyZForce; EarlyZForce = earlyZForce;
Topology = topology; Topology = topology;
@ -153,6 +160,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
FragmentOutputTypes = fragmentOutputTypes; FragmentOutputTypes = fragmentOutputTypes;
DualSourceBlendEnable = dualSourceBlendEnable; DualSourceBlendEnable = dualSourceBlendEnable;
YNegateEnabled = yNegateEnabled;
} }
} }
} }

View file

@ -540,6 +540,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
return false; return false;
} }
if (graphicsState.YNegateEnabled != GraphicsState.YNegateEnabled)
{
return false;
}
return Matches(channel, ref poolState, checkTextures, isCompute: false); return Matches(channel, ref poolState, checkTextures, isCompute: false);
} }

View file

@ -188,10 +188,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine(); 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;"); if (context.Config.GpuAccessor.QueryEarlyZForce())
context.AppendLine(); {
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) if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0)

View file

@ -251,7 +251,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
} }
else if (context.Config.Stage == ShaderStage.Fragment) 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.OriginUpperLeft
: ExecutionMode.OriginLowerLeft); : ExecutionMode.OriginLowerLeft);

View file

@ -178,6 +178,15 @@ namespace Ryujinx.Graphics.Shader
return 0; return 0;
} }
/// <summary>
/// Queries if host state forces early depth testing.
/// </summary>
/// <returns>True if early depth testing is forced</returns>
bool QueryEarlyZForce()
{
return false;
}
/// <summary> /// <summary>
/// Queries whenever the current draw has written the base vertex and base instance into Constant Buffer 0. /// Queries whenever the current draw has written the base vertex and base instance into Constant Buffer 0.
/// </summary> /// </summary>
@ -534,19 +543,19 @@ namespace Ryujinx.Graphics.Shader
} }
/// <summary> /// <summary>
/// Queries if host state forces early depth testing. /// Queries if host state disables the viewport transform.
/// </summary> /// </summary>
/// <returns>True if early depth testing is forced</returns> /// <returns>True if the viewport transform is disabled</returns>
bool QueryEarlyZForce() bool QueryViewportTransformDisable()
{ {
return false; return false;
} }
/// <summary> /// <summary>
/// Queries if host state disables the viewport transform. /// Queries Y negate enable state.
/// </summary> /// </summary>
/// <returns>True if the viewport transform is disabled</returns> /// <returns>True if Y negate of the fragment coordinates is enabled, false otherwise</returns>
bool QueryViewportTransformDisable() bool QueryYNegateEnabled()
{ {
return false; return false;
} }

View file

@ -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, // 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. // 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))); 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()) else if (op.Imm10 == AttributeConsts.FrontFacing && context.Config.GpuAccessor.QueryHostHasFrontFacingBug())
{ {

View file

@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Shader
public ShaderIdentification Identification { get; } public ShaderIdentification Identification { get; }
public int GpLayerInputAttribute { get; } public int GpLayerInputAttribute { get; }
public ShaderStage Stage { get; } public ShaderStage Stage { get; }
public bool UsesFragCoord { get; }
public bool UsesInstanceId { get; } public bool UsesInstanceId { get; }
public bool UsesDrawParameters { get; } public bool UsesDrawParameters { get; }
public bool UsesRtLayer { get; } public bool UsesRtLayer { get; }
@ -27,6 +28,7 @@ namespace Ryujinx.Graphics.Shader
ShaderIdentification identification, ShaderIdentification identification,
int gpLayerInputAttribute, int gpLayerInputAttribute,
ShaderStage stage, ShaderStage stage,
bool usesFragCoord,
bool usesInstanceId, bool usesInstanceId,
bool usesDrawParameters, bool usesDrawParameters,
bool usesRtLayer, bool usesRtLayer,
@ -41,6 +43,7 @@ namespace Ryujinx.Graphics.Shader
Identification = identification; Identification = identification;
GpLayerInputAttribute = gpLayerInputAttribute; GpLayerInputAttribute = gpLayerInputAttribute;
Stage = stage; Stage = stage;
UsesFragCoord = usesFragCoord;
UsesInstanceId = usesInstanceId; UsesInstanceId = usesInstanceId;
UsesDrawParameters = usesDrawParameters; UsesDrawParameters = usesDrawParameters;
UsesRtLayer = usesRtLayer; UsesRtLayer = usesRtLayer;

View file

@ -18,6 +18,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public IReadOnlyDictionary<int, MemoryDefinition> LocalMemories => _localMemories; public IReadOnlyDictionary<int, MemoryDefinition> LocalMemories => _localMemories;
public IReadOnlyDictionary<int, MemoryDefinition> SharedMemories => _sharedMemories; public IReadOnlyDictionary<int, MemoryDefinition> SharedMemories => _sharedMemories;
public readonly bool OriginUpperLeft;
public ShaderProperties() public ShaderProperties()
{ {
_constantBuffers = new Dictionary<int, BufferDefinition>(); _constantBuffers = new Dictionary<int, BufferDefinition>();
@ -28,6 +30,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
_sharedMemories = new Dictionary<int, MemoryDefinition>(); _sharedMemories = new Dictionary<int, MemoryDefinition>();
} }
public ShaderProperties(bool originUpperLeft) : this()
{
OriginUpperLeft = originUpperLeft;
}
public void AddOrUpdateConstantBuffer(int binding, BufferDefinition definition) public void AddOrUpdateConstantBuffer(int binding, BufferDefinition definition)
{ {
_constantBuffers[binding] = definition; _constantBuffers[binding] = definition;

View file

@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Shader
FragmentAlphaTest, FragmentAlphaTest,
FragmentIsBgra, FragmentIsBgra,
ViewportInverse, ViewportInverse,
ViewportSize,
FragmentRenderScaleCount, FragmentRenderScaleCount,
RenderScale, RenderScale,
} }
@ -33,6 +34,7 @@ namespace Ryujinx.Graphics.Shader
public static readonly int FragmentAlphaTestOffset; public static readonly int FragmentAlphaTestOffset;
public static readonly int FragmentIsBgraOffset; public static readonly int FragmentIsBgraOffset;
public static readonly int ViewportInverseOffset; public static readonly int ViewportInverseOffset;
public static readonly int ViewportSizeOffset;
public static readonly int FragmentRenderScaleCountOffset; public static readonly int FragmentRenderScaleCountOffset;
public static readonly int GraphicsRenderScaleOffset; public static readonly int GraphicsRenderScaleOffset;
public static readonly int ComputeRenderScaleOffset; public static readonly int ComputeRenderScaleOffset;
@ -56,6 +58,7 @@ namespace Ryujinx.Graphics.Shader
FragmentAlphaTestOffset = OffsetOf(ref instance, ref instance.FragmentAlphaTest); FragmentAlphaTestOffset = OffsetOf(ref instance, ref instance.FragmentAlphaTest);
FragmentIsBgraOffset = OffsetOf(ref instance, ref instance.FragmentIsBgra); FragmentIsBgraOffset = OffsetOf(ref instance, ref instance.FragmentIsBgra);
ViewportInverseOffset = OffsetOf(ref instance, ref instance.ViewportInverse); ViewportInverseOffset = OffsetOf(ref instance, ref instance.ViewportInverse);
ViewportSizeOffset = OffsetOf(ref instance, ref instance.ViewportSize);
FragmentRenderScaleCountOffset = OffsetOf(ref instance, ref instance.FragmentRenderScaleCount); FragmentRenderScaleCountOffset = OffsetOf(ref instance, ref instance.FragmentRenderScaleCount);
GraphicsRenderScaleOffset = OffsetOf(ref instance, ref instance.RenderScale); GraphicsRenderScaleOffset = OffsetOf(ref instance, ref instance.RenderScale);
ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize; ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize;
@ -68,6 +71,7 @@ namespace Ryujinx.Graphics.Shader
new StructureField(AggregateType.U32, "s_alpha_test"), new StructureField(AggregateType.U32, "s_alpha_test"),
new StructureField(AggregateType.Array | AggregateType.U32, "s_is_bgra", FragmentIsBgraCount), 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_inverse"),
new StructureField(AggregateType.Vector4 | AggregateType.FP32, "s_viewport_size"),
new StructureField(AggregateType.S32, "s_frag_scale_count"), new StructureField(AggregateType.S32, "s_frag_scale_count"),
new StructureField(AggregateType.Array | AggregateType.FP32, "s_render_scale", RenderScaleMaxCount), new StructureField(AggregateType.Array | AggregateType.FP32, "s_render_scale", RenderScaleMaxCount),
}); });
@ -76,6 +80,7 @@ namespace Ryujinx.Graphics.Shader
public Vector4<int> FragmentAlphaTest; public Vector4<int> FragmentAlphaTest;
public Array8<Vector4<int>> FragmentIsBgra; public Array8<Vector4<int>> FragmentIsBgra;
public Vector4<float> ViewportInverse; public Vector4<float> ViewportInverse;
public Vector4<float> ViewportSize;
public Vector4<int> FragmentRenderScaleCount; public Vector4<int> FragmentRenderScaleCount;
// Render scale max count: 1 + 64 + 8. First scale is fragment output scale, others are textures/image inputs. // Render scale max count: 1 + 64 + 8. First scale is fragment output scale, others are textures/image inputs.

View file

@ -429,6 +429,11 @@ namespace Ryujinx.Graphics.Shader.Translation
return context.Add(Instruction.FP32 | Instruction.SquareRoot, Local(), a); 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) public static Operand FPTruncate(this EmitterContext context, Operand a, Instruction fpType = Instruction.FP32)
{ {
return context.Add(fpType | Instruction.Truncate, Local(), a); return context.Add(fpType | Instruction.Truncate, Local(), a);

View file

@ -123,7 +123,20 @@ namespace Ryujinx.Graphics.Shader.Translation
UsedInputAttributesPerPatch = new HashSet<int>(); UsedInputAttributesPerPatch = new HashSet<int>();
UsedOutputAttributesPerPatch = new HashSet<int>(); UsedOutputAttributesPerPatch = new HashSet<int>();
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()) 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); 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[] StructureType tfeDataStruct = new(new StructureField[]
{ {
@ -146,7 +159,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
int binding = Constants.TfeBufferBaseBinding + i; int binding = Constants.TfeBufferBaseBinding + i;
BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct); 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, identification,
GpLayerInputAttribute, GpLayerInputAttribute,
Stage, Stage,
UsedFeatures.HasFlag(FeatureFlags.FragCoordXY),
UsedFeatures.HasFlag(FeatureFlags.InstanceId), UsedFeatures.HasFlag(FeatureFlags.InstanceId),
UsedFeatures.HasFlag(FeatureFlags.DrawParameters), UsedFeatures.HasFlag(FeatureFlags.DrawParameters),
UsedFeatures.HasFlag(FeatureFlags.RtLayer), UsedFeatures.HasFlag(FeatureFlags.RtLayer),