From 43b4b34376cdea486906f8bb4058dda3be7e1bd8 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 12 May 2022 14:47:13 +0100 Subject: [PATCH] Implement Viewport Transform Disable (#3328) * Initial implementation (no specialization) * Use specialization * Fix render scale, increase code gen version * Revert accidental change * Address Feedback --- Ryujinx.Graphics.GAL/IPipeline.cs | 2 +- .../Commands/SetViewportsCommand.cs | 6 ++- .../Multithreading/ThreadedPipeline.cs | 4 +- Ryujinx.Graphics.GAL/SupportBufferUpdater.cs | 7 +++ .../Engine/Threed/StateUpdater.cs | 23 +++++++-- .../Shader/Cache/Migration.cs | 3 +- .../Shader/DiskCache/DiskCacheGpuAccessor.cs | 6 +++ .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs | 6 +++ .../Shader/GpuChannelGraphicsState.cs | 9 +++- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 8 +-- .../Shader/ShaderCacheHashTable.cs | 4 +- .../Shader/ShaderSpecializationList.cs | 9 +++- .../Shader/ShaderSpecializationState.cs | 8 ++- Ryujinx.Graphics.OpenGL/Pipeline.cs | 15 +++++- .../CodeGen/Glsl/Declarations.cs | 9 ++-- .../CodeGen/Glsl/DefaultNames.cs | 1 + .../CodeGen/Glsl/OperandManager.cs | 5 +- Ryujinx.Graphics.Shader/IGpuAccessor.cs | 9 ++++ .../Instructions/InstEmitAttribute.cs | 28 ++++++++++- Ryujinx.Graphics.Shader/SupportBuffer.cs | 3 ++ .../Translation/AttributeConsts.cs | 3 ++ .../Translation/EmitterContext.cs | 49 ++++++++++++++++++- .../Translation/ShaderConfig.cs | 7 +++ 24 files changed, 200 insertions(+), 26 deletions(-) diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs index 75c3077eb8..aec096e72e 100644 --- a/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/Ryujinx.Graphics.GAL/IPipeline.cs @@ -94,7 +94,7 @@ namespace Ryujinx.Graphics.GAL void SetVertexAttribs(ReadOnlySpan vertexAttribs); void SetVertexBuffers(ReadOnlySpan vertexBuffers); - void SetViewports(int first, ReadOnlySpan viewports); + void SetViewports(int first, ReadOnlySpan viewports, bool disableTransform); void TextureBarrier(); void TextureBarrierTiled(); diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs index e11b00e84d..b208d9fe8d 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs @@ -9,17 +9,19 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands public CommandType CommandType => CommandType.SetViewports; private int _first; private SpanRef _viewports; + private bool _disableTransform; - public void Set(int first, SpanRef viewports) + public void Set(int first, SpanRef viewports, bool disableTransform) { _first = first; _viewports = viewports; + _disableTransform = disableTransform; } public static void Run(ref SetViewportsCommand command, ThreadedRenderer threaded, IRenderer renderer) { ReadOnlySpan viewports = command._viewports.Get(threaded); - renderer.Pipeline.SetViewports(command._first, viewports); + renderer.Pipeline.SetViewports(command._first, viewports, command._disableTransform); command._viewports.Dispose(threaded); } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index b6acfaa837..010ee7e650 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -304,9 +304,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void SetViewports(int first, ReadOnlySpan viewports) + public void SetViewports(int first, ReadOnlySpan viewports, bool disableTransform) { - _renderer.New().Set(first, _renderer.CopySpan(viewports)); + _renderer.New().Set(first, _renderer.CopySpan(viewports), disableTransform); _renderer.QueueCommand(); } diff --git a/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs b/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs index cb24bdd752..da7a246135 100644 --- a/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs +++ b/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs @@ -72,6 +72,13 @@ namespace Ryujinx.Graphics.GAL UpdateGenericField(SupportBuffer.FragmentIsBgraOffset, data, Data.FragmentIsBgra.ToSpan(), offset, count); } + public void UpdateViewportInverse(Vector4 data) + { + Data.ViewportInverse = data; + + MarkDirty(SupportBuffer.ViewportInverseOffset, SupportBuffer.FieldSize); + } + public void Commit() { if (_startOffset != -1) diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 3bc15a3174..d0c3bc5ae9 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -113,7 +113,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed nameof(ThreedClassState.DepthMode), nameof(ThreedClassState.ViewportTransform), nameof(ThreedClassState.ViewportExtents), - nameof(ThreedClassState.YControl)), + nameof(ThreedClassState.YControl), + nameof(ThreedClassState.ViewportTransformEnable)), new StateUpdateCallbackEntry(UpdatePolygonMode, nameof(ThreedClassState.PolygonModeFront), @@ -200,7 +201,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed // of the shader for the new state. if (_shaderSpecState != null) { - if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState())) + if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState())) { ForceShaderUpdate(); } @@ -568,6 +569,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed var yControl = _state.State.YControl; var face = _state.State.FaceState; + bool disableTransform = _state.State.ViewportTransformEnable == 0; + UpdateFrontFace(yControl, face.FrontFace); UpdateDepthMode(); @@ -577,6 +580,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed for (int index = 0; index < Constants.TotalViewports; index++) { + if (disableTransform) + { + ref var scissor = ref _state.State.ScreenScissorState; + + float rScale = _channel.TextureManager.RenderTargetScale; + var scissorRect = new RectangleF(0, 0, (scissor.X + scissor.Width) * rScale, (scissor.Y + scissor.Height) * rScale); + + viewports[index] = new Viewport(scissorRect, ViewportSwizzle.PositiveX, ViewportSwizzle.PositiveY, ViewportSwizzle.PositiveZ, ViewportSwizzle.PositiveW, 0, 1); + continue; + } + ref var transform = ref _state.State.ViewportTransform[index]; ref var extents = ref _state.State.ViewportExtents[index]; @@ -628,7 +642,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed viewports[index] = new Viewport(region, swizzleX, swizzleY, swizzleZ, swizzleW, depthNear, depthFar); } - _context.Renderer.Pipeline.SetViewports(0, viewports); + _context.Renderer.Pipeline.SetViewports(0, viewports, disableTransform); } /// @@ -1194,7 +1208,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed return new GpuChannelGraphicsState( _state.State.EarlyZForce, _drawState.Topology, - _state.State.TessMode); + _state.State.TessMode, + _state.State.ViewportTransformEnable == 0); } /// diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs index 27fac8f379..4de6eff912 100644 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs +++ b/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs @@ -166,7 +166,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache GpuChannelGraphicsState graphicsState = new GpuChannelGraphicsState( accessorHeader.StateFlags.HasFlag(GuestGpuStateFlags.EarlyZForce), topology, - tessMode); + tessMode, + false); TransformFeedbackDescriptor[] tfdNew = null; diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs index b1c04eac0c..bc63f714d4 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs @@ -185,6 +185,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return _oldSpecState.GraphicsState.EarlyZForce; } + /// + public bool QueryViewportTransformDisable() + { + return _oldSpecState.GraphicsState.ViewportTransformDisable; + } + /// public void RegisterTexture(int handle, int cbufSlot) { diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 0028e87961..5d99957f0f 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 1; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 0; + private const uint CodeGenVersion = 1; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 192467b750..5cd966af75 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -217,6 +217,12 @@ namespace Ryujinx.Graphics.Gpu.Shader return _state.GraphicsState.EarlyZForce; } + /// + public bool QueryViewportTransformDisable() + { + return _state.GraphicsState.ViewportTransformDisable; + } + /// public void RegisterTexture(int handle, int cbufSlot) { diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs index 5eb31db699..92ec117f30 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs @@ -25,17 +25,24 @@ namespace Ryujinx.Graphics.Gpu.Shader /// public readonly TessMode TessellationMode; + /// + /// Indicates whenever the viewport transform is disabled. + /// + public readonly bool ViewportTransformDisable; + /// /// Creates a new GPU graphics state. /// /// Early Z force enable /// Primitive topology /// Tessellation mode - public GpuChannelGraphicsState(bool earlyZForce, PrimitiveTopology topology, TessMode tessellationMode) + /// Indicates whenever the viewport transform is disabled + public GpuChannelGraphicsState(bool earlyZForce, PrimitiveTopology topology, TessMode tessellationMode, bool viewportTransformDisable) { EarlyZForce = earlyZForce; Topology = topology; TessellationMode = tessellationMode; + ViewportTransformDisable = viewportTransformDisable; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 03d5ecade2..df4b9d128a 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -249,12 +249,12 @@ namespace Ryujinx.Graphics.Gpu.Shader GpuChannelGraphicsState graphicsState, ShaderAddresses addresses) { - if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, poolState, gpShaders, addresses)) + if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, poolState, graphicsState, gpShaders, addresses)) { return gpShaders; } - if (_graphicsShaderCache.TryFind(channel, poolState, addresses, out gpShaders, out var cachedGuestCode)) + if (_graphicsShaderCache.TryFind(channel, poolState, graphicsState, addresses, out gpShaders, out var cachedGuestCode)) { _gpPrograms[addresses] = gpShaders; return gpShaders; @@ -429,12 +429,14 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// GPU channel using the shader /// GPU channel state to verify shader compatibility + /// GPU channel graphics state to verify shader compatibility /// Cached graphics shaders /// GPU virtual addresses of all enabled shader stages /// True if the code is different, false otherwise private static bool IsShaderEqual( GpuChannel channel, GpuChannelPoolState poolState, + GpuChannelGraphicsState graphicsState, CachedShaderProgram gpShaders, ShaderAddresses addresses) { @@ -452,7 +454,7 @@ namespace Ryujinx.Graphics.Gpu.Shader } } - return gpShaders.SpecializationState.MatchesGraphics(channel, poolState); + return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState); } /// diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs index 065f9ba90a..3d74e53a10 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs @@ -208,6 +208,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// GPU channel /// Texture pool state + /// Graphics state /// Guest addresses of the shaders to find /// Cached host program for the given state, if found /// Cached guest code, if any found @@ -215,6 +216,7 @@ namespace Ryujinx.Graphics.Gpu.Shader public bool TryFind( GpuChannel channel, GpuChannelPoolState poolState, + GpuChannelGraphicsState graphicsState, ShaderAddresses addresses, out CachedShaderProgram program, out CachedGraphicsGuestCode guestCode) @@ -234,7 +236,7 @@ namespace Ryujinx.Graphics.Gpu.Shader if (found && _shaderPrograms.TryGetValue(idTable, out ShaderSpecializationList specList)) { - return specList.TryFindForGraphics(channel, poolState, out program); + return specList.TryFindForGraphics(channel, poolState, graphicsState, out program); } return false; diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs index 87e087544e..e3e57d7452 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs @@ -24,13 +24,18 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// GPU channel /// Texture pool state + /// Graphics state /// Cached program, if found /// True if a compatible program is found, false otherwise - public bool TryFindForGraphics(GpuChannel channel, GpuChannelPoolState poolState, out CachedShaderProgram program) + public bool TryFindForGraphics( + GpuChannel channel, + GpuChannelPoolState poolState, + GpuChannelGraphicsState graphicsState, + out CachedShaderProgram program) { foreach (var entry in _entries) { - if (entry.SpecializationState.MatchesGraphics(channel, poolState)) + if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState)) { program = entry; return true; diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index 2bbc3d2c18..418c7b1a75 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -395,9 +395,15 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// GPU channel /// Texture pool state + /// Graphics state /// True if the state matches, false otherwise - public bool MatchesGraphics(GpuChannel channel, GpuChannelPoolState poolState) + public bool MatchesGraphics(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelGraphicsState graphicsState) { + if (graphicsState.ViewportTransformDisable != GraphicsState.ViewportTransformDisable) + { + return false; + } + return Matches(channel, poolState, isCompute: false); } diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 114fa68557..0326f9809b 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -1266,7 +1266,7 @@ namespace Ryujinx.Graphics.OpenGL _vertexArray.SetVertexBuffers(vertexBuffers); } - public void SetViewports(int first, ReadOnlySpan viewports) + public void SetViewports(int first, ReadOnlySpan viewports, bool disableTransform) { Array.Resize(ref _viewportArray, viewports.Length * 4); Array.Resize(ref _depthRangeArray, viewports.Length * 2); @@ -1305,6 +1305,19 @@ namespace Ryujinx.Graphics.OpenGL GL.ViewportArray(first, viewports.Length, viewportArray); GL.DepthRangeArray(first, viewports.Length, depthRangeArray); + + float disableTransformF = disableTransform ? 1.0f : 0.0f; + if (_supportBuffer.Data.ViewportInverse.W != disableTransformF || disableTransform) + { + float scale = _renderScale[0].X; + _supportBuffer.UpdateViewportInverse(new Vector4 + { + X = scale * 2f / viewports[first].Region.Width, + Y = scale * 2f / viewports[first].Region.Height, + Z = 1, + W = disableTransformF + }); + } } public void TextureBarrier() diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index c955a6161f..59a7ccdca7 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -249,7 +249,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } } - else if (isFragment) + else if (isFragment || context.Config.Stage == ShaderStage.Vertex) { DeclareSupportUniformBlock(context, context.Config.Stage, 0); } @@ -615,8 +615,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements) { - bool isFragment = stage == ShaderStage.Fragment; - if (!isFragment && scaleElements == 0) + bool needsSupportBlock = stage == ShaderStage.Fragment || + (context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable()); + + if (!needsSupportBlock && scaleElements == 0) { return; } @@ -630,6 +632,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl case ShaderStage.Vertex: context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};"); context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];"); + context.AppendLine($"vec4 {DefaultNames.SupportBlockViewportInverse};"); context.AppendLine($"int {DefaultNames.SupportBlockFragmentScaleCount};"); break; case ShaderStage.Compute: diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs index 76203522e9..3ab4814ce8 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs @@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public const string SupportBlockName = "support_block"; public const string SupportBlockAlphaTestName = "s_alpha_test"; public const string SupportBlockIsBgraName = "s_is_bgra"; + public const string SupportBlockViewportInverse = "s_viewport_inverse"; public const string SupportBlockFragmentScaleCount = "s_frag_scale_count"; public const string SupportBlockRenderScaleName = "s_render_scale"; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 2d6607ad00..334c744d75 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -84,7 +84,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", VariableType.Bool) }, { AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", VariableType.Bool) }, { AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", VariableType.Bool) }, - { AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", VariableType.Bool) } + { AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", VariableType.Bool) }, + + { AttributeConsts.SupportBlockViewInverseX, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.x", VariableType.F32) }, + { AttributeConsts.SupportBlockViewInverseY, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.y", VariableType.F32) } }; private Dictionary _locals; diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 9c624d90d8..180fc18746 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -329,6 +329,15 @@ namespace Ryujinx.Graphics.Shader return false; } + /// + /// Queries if host state disables the viewport transform. + /// + /// True if the viewport transform is disabled + bool QueryViewportTransformDisable() + { + return false; + } + /// /// Registers a texture used by the shader. /// diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs index 1cdb384224..6ce2e53720 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs @@ -206,7 +206,33 @@ namespace Ryujinx.Graphics.Shader.Instructions if (emit) { - context.EmitVertex(); + if (context.Config.LastInVertexPipeline) + { + context.PrepareForVertexReturn(out var tempXLocal, out var tempYLocal, out var tempZLocal); + + context.EmitVertex(); + + // Restore output position value before transformation. + + if (tempXLocal != null) + { + context.Copy(Attribute(AttributeConsts.PositionX), tempXLocal); + } + + if (tempYLocal != null) + { + context.Copy(Attribute(AttributeConsts.PositionY), tempYLocal); + } + + if (tempZLocal != null) + { + context.Copy(Attribute(AttributeConsts.PositionZ), tempZLocal); + } + } + else + { + context.EmitVertex(); + } } if (cut) diff --git a/Ryujinx.Graphics.Shader/SupportBuffer.cs b/Ryujinx.Graphics.Shader/SupportBuffer.cs index 47a47ea638..28a48c2add 100644 --- a/Ryujinx.Graphics.Shader/SupportBuffer.cs +++ b/Ryujinx.Graphics.Shader/SupportBuffer.cs @@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Shader public static int FragmentAlphaTestOffset; public static int FragmentIsBgraOffset; + public static int ViewportInverseOffset; public static int FragmentRenderScaleCountOffset; public static int GraphicsRenderScaleOffset; public static int ComputeRenderScaleOffset; @@ -40,6 +41,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); FragmentRenderScaleCountOffset = OffsetOf(ref instance, ref instance.FragmentRenderScaleCount); GraphicsRenderScaleOffset = OffsetOf(ref instance, ref instance.RenderScale); ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize; @@ -47,6 +49,7 @@ namespace Ryujinx.Graphics.Shader public Vector4 FragmentAlphaTest; public Array8> FragmentIsBgra; + public Vector4 ViewportInverse; public Vector4 FragmentRenderScaleCount; // Render scale max count: 1 + 32 + 8. First scale is fragment output scale, others are textures/image inputs. diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs index 370af00973..ada60ab97d 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs @@ -67,6 +67,9 @@ namespace Ryujinx.Graphics.Shader.Translation public const int FragmentOutputIsBgraBase = 0x1000100; public const int FragmentOutputIsBgraEnd = FragmentOutputIsBgraBase + 8 * 4; + public const int SupportBlockViewInverseX = 0x1000200; + public const int SupportBlockViewInverseY = 0x1000204; + public const int ThreadIdX = 0x2000000; public const int ThreadIdY = 0x2000004; public const int ThreadIdZ = 0x2000008; diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 775f121794..ba3b551d91 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -154,9 +154,56 @@ namespace Ryujinx.Graphics.Shader.Translation return label; } + public void PrepareForVertexReturn() + { + if (Config.GpuAccessor.QueryViewportTransformDisable()) + { + Operand x = Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask); + Operand y = Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask); + Operand xScale = Attribute(AttributeConsts.SupportBlockViewInverseX); + Operand yScale = Attribute(AttributeConsts.SupportBlockViewInverseY); + Operand negativeOne = ConstF(-1.0f); + + this.Copy(Attribute(AttributeConsts.PositionX), this.FPFusedMultiplyAdd(x, xScale, negativeOne)); + this.Copy(Attribute(AttributeConsts.PositionY), this.FPFusedMultiplyAdd(y, yScale, negativeOne)); + } + } + + public void PrepareForVertexReturn(out Operand oldXLocal, out Operand oldYLocal, out Operand oldZLocal) + { + if (Config.GpuAccessor.QueryViewportTransformDisable()) + { + oldXLocal = Local(); + this.Copy(oldXLocal, Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask)); + oldYLocal = Local(); + this.Copy(oldYLocal, Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask)); + } + else + { + oldXLocal = null; + oldYLocal = null; + } + + // Will be used by Vulkan backend for depth mode emulation. + oldZLocal = null; + + PrepareForVertexReturn(); + } + public void PrepareForReturn() { - if (!IsNonMain && Config.Stage == ShaderStage.Fragment) + if (IsNonMain) + { + return; + } + + if (Config.LastInVertexPipeline && + (Config.Stage == ShaderStage.Vertex || Config.Stage == ShaderStage.TessellationEvaluation) && + (Config.Options.Flags & TranslationFlags.VertexA) == 0) + { + PrepareForVertexReturn(); + } + else if (Config.Stage == ShaderStage.Fragment) { if (Config.OmapDepth) { diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 23b8b95104..27d72cd53e 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.Shader.Translation public ShaderStage Stage { get; } public bool GpPassthrough { get; } + public bool LastInVertexPipeline { get; private set; } public int ThreadsPerInputPrimitive { get; } @@ -135,6 +136,7 @@ namespace Ryujinx.Graphics.Shader.Translation OmapSampleMask = header.OmapSampleMask; OmapDepth = header.OmapDepth; TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled(); + LastInVertexPipeline = header.Stage < ShaderStage.Fragment; } public int GetDepthRegister() @@ -274,6 +276,11 @@ namespace Ryujinx.Graphics.Shader.Translation NextInputAttributesPerPatchComponents = config.ThisInputAttributesPerPatchComponents; NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr); MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch); + + if (config.Stage != ShaderStage.Fragment) + { + LastInVertexPipeline = false; + } } public void MergeOutputUserAttributes(int mask, int maskPerPatch)