Просмотр исходного кода

Support shared color mask, implement more shader instructions

Support shared color masks (used by Nouveau and maybe the NVIDIA
driver).
Support draw buffers (also required by OpenGL).
Support viewport transform disable (disabled for now as it breaks some
games).
Fix instanced rendering draw being ignored for multi draw.
Fix IADD and IADD3 immediate shader encodings, that was not matching
some ops.
Implement FFMA32I shader instruction.
Implement IMAD shader instruction.
gdkchan 6 лет назад
Родитель
Сommit
cb171f6ebf
32 измененных файлов с 313 добавлено и 92 удалено
  1. 3 0
      Ryujinx.Graphics.GAL/Capabilities.cs
  2. 3 1
      Ryujinx.Graphics.Gpu/Engine/MethodClear.cs
  3. 13 11
      Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs
  4. 0 6
      Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
  5. 60 22
      Ryujinx.Graphics.Gpu/Engine/Methods.cs
  6. 2 0
      Ryujinx.Graphics.Gpu/NvGpuFifo.cs
  7. 20 13
      Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
  8. 7 1
      Ryujinx.Graphics.Gpu/State/GpuState.cs
  9. 3 0
      Ryujinx.Graphics.Gpu/State/MethodOffset.cs
  10. 17 0
      Ryujinx.Graphics.Gpu/State/RtControl.cs
  11. 2 0
      Ryujinx.Graphics.OpenGL/HwCapabilities.cs
  12. 1 0
      Ryujinx.Graphics.OpenGL/Renderer.cs
  13. 13 0
      Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
  14. 3 0
      Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs
  15. 3 0
      Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs
  16. 7 0
      Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl
  17. 7 0
      Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighU32.glsl
  18. 2 0
      Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
  19. 0 1
      Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
  20. 7 2
      Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs
  21. 44 0
      Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs
  22. 20 0
      Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs
  23. 2 0
      Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
  24. 2 0
      Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj
  25. 6 2
      Ryujinx.Graphics.Shader/ShaderCapabilities.cs
  26. 7 5
      Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs
  27. 2 0
      Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
  28. 6 0
      Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
  29. 25 7
      Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
  30. 10 0
      Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
  31. 2 1
      Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs
  32. 14 20
      Ryujinx.Graphics.Shader/Translation/Translator.cs

+ 3 - 0
Ryujinx.Graphics.GAL/Capabilities.cs

@@ -4,13 +4,16 @@ namespace Ryujinx.Graphics.GAL
     {
     {
         public bool SupportsAstcCompression { get; }
         public bool SupportsAstcCompression { get; }
 
 
+        public int MaximumViewportDimensions    { get; }
         public int StorageBufferOffsetAlignment { get; }
         public int StorageBufferOffsetAlignment { get; }
 
 
         public Capabilities(
         public Capabilities(
             bool supportsAstcCompression,
             bool supportsAstcCompression,
+            int  maximumViewportDimensions,
             int  storageBufferOffsetAlignment)
             int  storageBufferOffsetAlignment)
         {
         {
             SupportsAstcCompression      = supportsAstcCompression;
             SupportsAstcCompression      = supportsAstcCompression;
+            MaximumViewportDimensions    = maximumViewportDimensions;
             StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
             StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
         }
         }
     }
     }

+ 3 - 1
Ryujinx.Graphics.Gpu/Engine/MethodClear.cs

@@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
     {
     {
         private void Clear(GpuState state, int argument)
         private void Clear(GpuState state, int argument)
         {
         {
-            UpdateRenderTargetStateIfNeeded(state);
+            UpdateRenderTargetState(state, useControl: false);
 
 
             _textureManager.CommitGraphicsBindings();
             _textureManager.CommitGraphicsBindings();
 
 
@@ -49,6 +49,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
                     stencilValue,
                     stencilValue,
                     stencilMask);
                     stencilMask);
             }
             }
+
+            UpdateRenderTargetState(state, useControl: true);
         }
         }
     }
     }
 }
 }

+ 13 - 11
Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs

@@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
         private int _firstIndex;
         private int _firstIndex;
         private int _indexCount;
         private int _indexCount;
 
 
-        private bool _instancedHasState;
+        private bool _instancedDrawPending;
         private bool _instancedIndexed;
         private bool _instancedIndexed;
 
 
         private int _instancedFirstIndex;
         private int _instancedFirstIndex;
@@ -32,9 +32,9 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
 
             if (instanced)
             if (instanced)
             {
             {
-                if (!_instancedHasState)
+                if (!_instancedDrawPending)
                 {
                 {
-                    _instancedHasState = true;
+                    _instancedDrawPending = true;
 
 
                     _instancedIndexed = _drawIndexed;
                     _instancedIndexed = _drawIndexed;
 
 
@@ -82,20 +82,22 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
 
         private void DrawBegin(GpuState state, int argument)
         private void DrawBegin(GpuState state, int argument)
         {
         {
-            PrimitiveType type = (PrimitiveType)(argument & 0xffff);
-
-            _context.Renderer.Pipeline.SetPrimitiveTopology(type.Convert());
-
-            PrimitiveType = type;
-
             if ((argument & (1 << 26)) != 0)
             if ((argument & (1 << 26)) != 0)
             {
             {
                 _instanceIndex++;
                 _instanceIndex++;
             }
             }
             else if ((argument & (1 << 27)) == 0)
             else if ((argument & (1 << 27)) == 0)
             {
             {
+                PerformDeferredDraws();
+
                 _instanceIndex = 0;
                 _instanceIndex = 0;
             }
             }
+
+            PrimitiveType type = (PrimitiveType)(argument & 0xffff);
+
+            _context.Renderer.Pipeline.SetPrimitiveTopology(type.Convert());
+
+            PrimitiveType = type;
         }
         }
 
 
         private void SetIndexBufferCount(GpuState state, int argument)
         private void SetIndexBufferCount(GpuState state, int argument)
@@ -106,9 +108,9 @@ namespace Ryujinx.Graphics.Gpu.Engine
         public void PerformDeferredDraws()
         public void PerformDeferredDraws()
         {
         {
             // Perform any pending instanced draw.
             // Perform any pending instanced draw.
-            if (_instancedHasState)
+            if (_instancedDrawPending)
             {
             {
-                _instancedHasState = false;
+                _instancedDrawPending = false;
 
 
                 if (_instancedIndexed)
                 if (_instancedIndexed)
                 {
                 {

+ 0 - 6
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs

@@ -1,4 +1,3 @@
-using Ryujinx.Graphics.Gpu.Memory;
 using Ryujinx.Graphics.Gpu.State;
 using Ryujinx.Graphics.Gpu.State;
 
 
 namespace Ryujinx.Graphics.Gpu.Engine
 namespace Ryujinx.Graphics.Gpu.Engine
@@ -9,11 +8,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
         {
         {
             var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState);
             var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState);
 
 
-            if (_context.MemoryManager.Translate(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset) == MemoryManager.BadAddress)
-            {
-                return;
-            }
-
             _context.MemoryAccessor.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, argument);
             _context.MemoryAccessor.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, argument);
 
 
             state.SetUniformBufferOffset(uniformBuffer.Offset + 4);
             state.SetUniformBufferOffset(uniformBuffer.Offset + 4);

+ 60 - 22
Ryujinx.Graphics.Gpu/Engine/Methods.cs

@@ -86,7 +86,14 @@ namespace Ryujinx.Graphics.Gpu.Engine
                 UpdateShaderState(state);
                 UpdateShaderState(state);
             }
             }
 
 
-            UpdateRenderTargetStateIfNeeded(state);
+            if (state.QueryModified(MethodOffset.RtColorState,
+                                    MethodOffset.RtDepthStencilState,
+                                    MethodOffset.RtControl,
+                                    MethodOffset.RtDepthStencilSize,
+                                    MethodOffset.RtDepthStencilEnable))
+            {
+                UpdateRenderTargetState(state, useControl: true);
+            }
 
 
             if (state.QueryModified(MethodOffset.DepthTestEnable,
             if (state.QueryModified(MethodOffset.DepthTestEnable,
                                     MethodOffset.DepthWriteEnable,
                                     MethodOffset.DepthWriteEnable,
@@ -155,7 +162,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
                 UpdateFaceState(state);
                 UpdateFaceState(state);
             }
             }
 
 
-            if (state.QueryModified(MethodOffset.RtColorMask))
+            if (state.QueryModified(MethodOffset.RtColorMaskShared, MethodOffset.RtColorMask))
             {
             {
                 UpdateRtColorMask(state);
                 UpdateRtColorMask(state);
             }
             }
@@ -210,19 +217,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
             }
             }
         }
         }
 
 
-        private void UpdateRenderTargetStateIfNeeded(GpuState state)
+        private void UpdateRenderTargetState(GpuState state, bool useControl)
         {
         {
-            if (state.QueryModified(MethodOffset.RtColorState,
-                                             MethodOffset.RtDepthStencilState,
-                                             MethodOffset.RtDepthStencilSize,
-                                             MethodOffset.RtDepthStencilEnable))
-            {
-                UpdateRenderTargetState(state);
-            }
-        }
+            var rtControl = state.Get<RtControl>(MethodOffset.RtControl);
+
+            int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
 
 
-        private void UpdateRenderTargetState(GpuState state)
-        {
             var msaaMode = state.Get<TextureMsaaMode>(MethodOffset.RtMsaaMode);
             var msaaMode = state.Get<TextureMsaaMode>(MethodOffset.RtMsaaMode);
 
 
             int samplesInX = msaaMode.SamplesInX();
             int samplesInX = msaaMode.SamplesInX();
@@ -230,9 +230,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
 
             for (int index = 0; index < Constants.TotalRenderTargets; index++)
             for (int index = 0; index < Constants.TotalRenderTargets; index++)
             {
             {
-                var colorState = state.Get<RtColorState>(MethodOffset.RtColorState, index);
+                int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index;
 
 
-                if (!IsRtEnabled(colorState))
+                var colorState = state.Get<RtColorState>(MethodOffset.RtColorState, rtIndex);
+
+                if (index >= count || !IsRtEnabled(colorState))
                 {
                 {
                     _textureManager.SetRenderTargetColor(index, null);
                     _textureManager.SetRenderTargetColor(index, null);
 
 
@@ -292,6 +294,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
 
         private void UpdateViewportTransform(GpuState state)
         private void UpdateViewportTransform(GpuState state)
         {
         {
+            bool transformEnable = GetViewportTransformEnable(state);
+
             bool flipY = (state.Get<int>(MethodOffset.YControl) & 1) != 0;
             bool flipY = (state.Get<int>(MethodOffset.YControl) & 1) != 0;
 
 
             float yFlip = flipY ? -1 : 1;
             float yFlip = flipY ? -1 : 1;
@@ -303,13 +307,35 @@ namespace Ryujinx.Graphics.Gpu.Engine
                 var transform = state.Get<ViewportTransform>(MethodOffset.ViewportTransform, index);
                 var transform = state.Get<ViewportTransform>(MethodOffset.ViewportTransform, index);
                 var extents   = state.Get<ViewportExtents>  (MethodOffset.ViewportExtents,   index);
                 var extents   = state.Get<ViewportExtents>  (MethodOffset.ViewportExtents,   index);
 
 
-                float x = transform.TranslateX - MathF.Abs(transform.ScaleX);
-                float y = transform.TranslateY - MathF.Abs(transform.ScaleY);
+                RectangleF region;
 
 
-                float width  = transform.ScaleX * 2;
-                float height = transform.ScaleY * 2 * yFlip;
+                if (transformEnable)
+                {
+                    float x = transform.TranslateX - MathF.Abs(transform.ScaleX);
+                    float y = transform.TranslateY - MathF.Abs(transform.ScaleY);
 
 
-                RectangleF region = new RectangleF(x, y, width, height);
+                    float width  = transform.ScaleX * 2;
+                    float height = transform.ScaleY * 2 * yFlip;
+
+                    region = new RectangleF(x, y, width, height);
+                }
+                else
+                {
+                    // It's not possible to fully disable viewport transform, at least with the most
+                    // common graphics APIs, but we can effectively disable it with a dummy transform.
+                    // The transform is defined as: xw = (width / 2) * xndc + x + (width / 2)
+                    // By setting x to -(width / 2), we effectively remove the translation.
+                    // By setting the width to 2, we remove the scale since 2 / 2 = 1.
+                    // Now, the only problem is the viewport clipping, that we also can't disable.
+                    // To prevent the values from being clipped, we multiply (-1, -1, 2, 2) by
+                    // the maximum supported viewport dimensions.
+                    // This must be compensated on the shader, by dividing the vertex position
+                    // by the maximum viewport dimensions.
+                    float maxSize     = (float)_context.Capabilities.MaximumViewportDimensions;
+                    float halfMaxSize = (float)_context.Capabilities.MaximumViewportDimensions * 0.5f;
+
+                    region = new RectangleF(-halfMaxSize, -halfMaxSize, maxSize, maxSize * yFlip);
+                }
 
 
                 viewports[index] = new Viewport(
                 viewports[index] = new Viewport(
                     region,
                     region,
@@ -537,11 +563,13 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
 
         private void UpdateRtColorMask(GpuState state)
         private void UpdateRtColorMask(GpuState state)
         {
         {
+            bool rtColorMaskShared = state.Get<Boolean32>(MethodOffset.RtColorMaskShared);
+
             uint[] componentMasks = new uint[Constants.TotalRenderTargets];
             uint[] componentMasks = new uint[Constants.TotalRenderTargets];
 
 
             for (int index = 0; index < Constants.TotalRenderTargets; index++)
             for (int index = 0; index < Constants.TotalRenderTargets; index++)
             {
             {
-                var colorMask = state.Get<RtColorMask>(MethodOffset.RtColorMask, index);
+                var colorMask = state.Get<RtColorMask>(MethodOffset.RtColorMask, rtColorMaskShared ? 0 : index);
 
 
                 uint componentMask = 0;
                 uint componentMask = 0;
 
 
@@ -634,7 +662,9 @@ namespace Ryujinx.Graphics.Gpu.Engine
                 addressesArray[index] = baseAddress + shader.Offset;
                 addressesArray[index] = baseAddress + shader.Offset;
             }
             }
 
 
-            GraphicsShader gs = _shaderCache.GetGraphicsShader(addresses);
+            bool viewportTransformEnable = GetViewportTransformEnable(state);
+
+            GraphicsShader gs = _shaderCache.GetGraphicsShader(addresses, !viewportTransformEnable);
 
 
             _vsUsesInstanceId = gs.Shader[0].Program.Info.UsesInstanceId;
             _vsUsesInstanceId = gs.Shader[0].Program.Info.UsesInstanceId;
 
 
@@ -695,6 +725,14 @@ namespace Ryujinx.Graphics.Gpu.Engine
             _context.Renderer.Pipeline.BindProgram(gs.HostProgram);
             _context.Renderer.Pipeline.BindProgram(gs.HostProgram);
         }
         }
 
 
+        private bool GetViewportTransformEnable(GpuState state)
+        {
+            // FIXME: We should read ViewportTransformEnable, but it seems that some games writes 0 there?
+            // return state.Get<Boolean32>(MethodOffset.ViewportTransformEnable) != 0;
+
+            return true;
+        }
+
         private static Target GetTarget(SamplerType type)
         private static Target GetTarget(SamplerType type)
         {
         {
             type &= ~(SamplerType.Indexed | SamplerType.Shadow);
             type &= ~(SamplerType.Indexed | SamplerType.Shadow);

+ 2 - 0
Ryujinx.Graphics.Gpu/NvGpuFifo.cs

@@ -105,6 +105,8 @@ namespace Ryujinx.Graphics.Gpu
                 {
                 {
                     case NvGpuFifoMeth.WaitForIdle:
                     case NvGpuFifoMeth.WaitForIdle:
                     {
                     {
+                        _context.Methods.PerformDeferredDraws();
+
                         _context.Renderer.FlushPipelines();
                         _context.Renderer.FlushPipelines();
 
 
                         break;
                         break;

+ 20 - 13
Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs

@@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
             return cpShader;
             return cpShader;
         }
         }
 
 
-        public GraphicsShader GetGraphicsShader(ShaderAddresses addresses)
+        public GraphicsShader GetGraphicsShader(ShaderAddresses addresses, bool dividePosXY)
         {
         {
             bool isCached = _gpPrograms.TryGetValue(addresses, out List<GraphicsShader> list);
             bool isCached = _gpPrograms.TryGetValue(addresses, out List<GraphicsShader> list);
 
 
@@ -86,19 +86,28 @@ namespace Ryujinx.Graphics.Gpu.Shader
 
 
             GraphicsShader gpShaders = new GraphicsShader();
             GraphicsShader gpShaders = new GraphicsShader();
 
 
+            TranslationFlags flags =
+                TranslationFlags.DebugMode |
+                TranslationFlags.Unspecialized;
+
+            if (dividePosXY)
+            {
+                flags |= TranslationFlags.DividePosXY;
+            }
+
             if (addresses.VertexA != 0)
             if (addresses.VertexA != 0)
             {
             {
-                gpShaders.Shader[0] = TranslateGraphicsShader(addresses.Vertex, addresses.VertexA);
+                gpShaders.Shader[0] = TranslateGraphicsShader(flags, addresses.Vertex, addresses.VertexA);
             }
             }
             else
             else
             {
             {
-                gpShaders.Shader[0] = TranslateGraphicsShader(addresses.Vertex);
+                gpShaders.Shader[0] = TranslateGraphicsShader(flags, addresses.Vertex);
             }
             }
 
 
-            gpShaders.Shader[1] = TranslateGraphicsShader(addresses.TessControl);
-            gpShaders.Shader[2] = TranslateGraphicsShader(addresses.TessEvaluation);
-            gpShaders.Shader[3] = TranslateGraphicsShader(addresses.Geometry);
-            gpShaders.Shader[4] = TranslateGraphicsShader(addresses.Fragment);
+            gpShaders.Shader[1] = TranslateGraphicsShader(flags, addresses.TessControl);
+            gpShaders.Shader[2] = TranslateGraphicsShader(flags, addresses.TessEvaluation);
+            gpShaders.Shader[3] = TranslateGraphicsShader(flags, addresses.Geometry);
+            gpShaders.Shader[4] = TranslateGraphicsShader(flags, addresses.Fragment);
 
 
             BackpropQualifiers(gpShaders);
             BackpropQualifiers(gpShaders);
 
 
@@ -218,7 +227,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
             return new CachedShader(program, codeCached);
             return new CachedShader(program, codeCached);
         }
         }
 
 
-        private CachedShader TranslateGraphicsShader(ulong gpuVa, ulong gpuVaA = 0)
+        private CachedShader TranslateGraphicsShader(TranslationFlags flags, ulong gpuVa, ulong gpuVaA = 0)
         {
         {
             if (gpuVa == 0)
             if (gpuVa == 0)
             {
             {
@@ -227,10 +236,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
 
 
             ShaderProgram program;
             ShaderProgram program;
 
 
-            const TranslationFlags flags =
-                TranslationFlags.DebugMode |
-                TranslationFlags.Unspecialized;
-
             int[] codeCached = null;
             int[] codeCached = null;
 
 
             if (gpuVaA != 0)
             if (gpuVaA != 0)
@@ -345,7 +350,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
 
 
         private ShaderCapabilities GetShaderCapabilities()
         private ShaderCapabilities GetShaderCapabilities()
         {
         {
-            return new ShaderCapabilities(_context.Capabilities.StorageBufferOffsetAlignment);
+            return new ShaderCapabilities(
+                _context.Capabilities.MaximumViewportDimensions,
+                _context.Capabilities.StorageBufferOffsetAlignment);
         }
         }
     }
     }
 }
 }

+ 7 - 1
Ryujinx.Graphics.Gpu/State/GpuState.cs

@@ -94,11 +94,17 @@ namespace Ryujinx.Graphics.Gpu.State
                 _backingMemory[(int)MethodOffset.ViewportExtents + index * 4 + 3] = 0x3F800000;
                 _backingMemory[(int)MethodOffset.ViewportExtents + index * 4 + 3] = 0x3F800000;
             }
             }
 
 
+            // Viewport transform enable.
+            _backingMemory[(int)MethodOffset.ViewportTransformEnable] = 1;
+
             // Default front stencil mask.
             // Default front stencil mask.
             _backingMemory[0x4e7] = 0xff;
             _backingMemory[0x4e7] = 0xff;
 
 
             // Default color mask.
             // Default color mask.
-            _backingMemory[(int)MethodOffset.RtColorMask] = 0x1111;
+            for (int index = 0; index < Constants.TotalRenderTargets; index++)
+            {
+                _backingMemory[(int)MethodOffset.RtColorMask + index] = 0x1111;
+            }
         }
         }
 
 
         public void RegisterCallback(MethodOffset offset, int count, MethodCallback callback)
         public void RegisterCallback(MethodOffset offset, int count, MethodCallback callback)

+ 3 - 0
Ryujinx.Graphics.Gpu/State/MethodOffset.cs

@@ -29,8 +29,10 @@ namespace Ryujinx.Graphics.Gpu.State
         StencilBackMasks                = 0x3d5,
         StencilBackMasks                = 0x3d5,
         InvalidateTextures              = 0x3dd,
         InvalidateTextures              = 0x3dd,
         TextureBarrierTiled             = 0x3df,
         TextureBarrierTiled             = 0x3df,
+        RtColorMaskShared               = 0x3e4,
         RtDepthStencilState             = 0x3f8,
         RtDepthStencilState             = 0x3f8,
         VertexAttribState               = 0x458,
         VertexAttribState               = 0x458,
+        RtControl                       = 0x487,
         RtDepthStencilSize              = 0x48a,
         RtDepthStencilSize              = 0x48a,
         SamplerIndex                    = 0x48d,
         SamplerIndex                    = 0x48d,
         DepthTestEnable                 = 0x4b3,
         DepthTestEnable                 = 0x4b3,
@@ -62,6 +64,7 @@ namespace Ryujinx.Graphics.Gpu.State
         DepthBiasClamp                  = 0x61f,
         DepthBiasClamp                  = 0x61f,
         VertexBufferInstanced           = 0x620,
         VertexBufferInstanced           = 0x620,
         FaceState                       = 0x646,
         FaceState                       = 0x646,
+        ViewportTransformEnable         = 0x64b,
         Clear                           = 0x674,
         Clear                           = 0x674,
         RtColorMask                     = 0x680,
         RtColorMask                     = 0x680,
         ReportState                     = 0x6c0,
         ReportState                     = 0x6c0,

+ 17 - 0
Ryujinx.Graphics.Gpu/State/RtControl.cs

@@ -0,0 +1,17 @@
+namespace Ryujinx.Graphics.Gpu.State
+{
+    struct RtControl
+    {
+        public uint Packed;
+
+        public int UnpackCount()
+        {
+            return (int)(Packed & 0xf);
+        }
+
+        public int UnpackPermutationIndex(int index)
+        {
+            return (int)((Packed >> (4 + index * 3)) & 7);
+        }
+    }
+}

+ 2 - 0
Ryujinx.Graphics.OpenGL/HwCapabilities.cs

@@ -7,10 +7,12 @@ namespace Ryujinx.Graphics.OpenGL
     {
     {
         private static Lazy<bool> _supportsAstcCompression = new Lazy<bool>(() => HasExtension("GL_KHR_texture_compression_astc_ldr"));
         private static Lazy<bool> _supportsAstcCompression = new Lazy<bool>(() => HasExtension("GL_KHR_texture_compression_astc_ldr"));
 
 
+        private static Lazy<int> _maximumViewportDimensions    = new Lazy<int>(() => GetLimit(All.MaxViewportDims));
         private static Lazy<int> _storageBufferOffsetAlignment = new Lazy<int>(() => GetLimit(All.ShaderStorageBufferOffsetAlignment));
         private static Lazy<int> _storageBufferOffsetAlignment = new Lazy<int>(() => GetLimit(All.ShaderStorageBufferOffsetAlignment));
 
 
         public static bool SupportsAstcCompression => _supportsAstcCompression.Value;
         public static bool SupportsAstcCompression => _supportsAstcCompression.Value;
 
 
+        public static int MaximumViewportDimensions    => _maximumViewportDimensions.Value;
         public static int StorageBufferOffsetAlignment => _storageBufferOffsetAlignment.Value;
         public static int StorageBufferOffsetAlignment => _storageBufferOffsetAlignment.Value;
 
 
         private static bool HasExtension(string name)
         private static bool HasExtension(string name)

+ 1 - 0
Ryujinx.Graphics.OpenGL/Renderer.cs

@@ -63,6 +63,7 @@ namespace Ryujinx.Graphics.OpenGL
         {
         {
             return new Capabilities(
             return new Capabilities(
                 HwCapabilities.SupportsAstcCompression,
                 HwCapabilities.SupportsAstcCompression,
+                HwCapabilities.MaximumViewportDimensions,
                 HwCapabilities.StorageBufferOffsetAlignment);
                 HwCapabilities.StorageBufferOffsetAlignment);
         }
         }
 
 

+ 13 - 0
Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs

@@ -142,6 +142,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
                 context.AppendLine();
                 context.AppendLine();
             }
             }
 
 
+            if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0)
+            {
+                AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl");
+            }
+
+            if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighU32) != 0)
+            {
+                AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighU32.glsl");
+            }
+
             if ((info.HelperFunctionsMask & HelperFunctionsMask.Shuffle) != 0)
             if ((info.HelperFunctionsMask & HelperFunctionsMask.Shuffle) != 0)
             {
             {
                 AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/Shuffle.glsl");
                 AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/Shuffle.glsl");
@@ -170,6 +180,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 
 
         public static void DeclareLocals(CodeGenContext context, StructuredProgramInfo info)
         public static void DeclareLocals(CodeGenContext context, StructuredProgramInfo info)
         {
         {
+            context.AppendLine(GetVarTypeName(VariableType.S32) + " " + DefaultNames.DummyIntName + ";");
+            context.AppendLine(GetVarTypeName(VariableType.U32) + " " + DefaultNames.DummyUintName + ";");
+
             foreach (AstOperand decl in info.Locals)
             foreach (AstOperand decl in info.Locals)
             {
             {
                 string name = context.OperandManager.DeclareLocal(decl);
                 string name = context.OperandManager.DeclareLocal(decl);

+ 3 - 0
Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs

@@ -22,6 +22,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
         public const string LocalMemoryName  = "local_mem";
         public const string LocalMemoryName  = "local_mem";
         public const string SharedMemoryName = "shared_mem";
         public const string SharedMemoryName = "shared_mem";
 
 
+        public const string DummyIntName  = "dummyInt";
+        public const string DummyUintName = "dummyUint";
+
         public const string UndefinedName = "undef";
         public const string UndefinedName = "undef";
     }
     }
 }
 }

+ 3 - 0
Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs

@@ -2,6 +2,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 {
 {
     static class HelperFunctionNames
     static class HelperFunctionNames
     {
     {
+        public static string MultiplyHighS32 = "Helper_MultiplyHighS32";
+        public static string MultiplyHighU32 = "Helper_MultiplyHighU32";
+
         public static string Shuffle     = "Helper_Shuffle";
         public static string Shuffle     = "Helper_Shuffle";
         public static string ShuffleDown = "Helper_ShuffleDown";
         public static string ShuffleDown = "Helper_ShuffleDown";
         public static string ShuffleUp   = "Helper_ShuffleUp";
         public static string ShuffleUp   = "Helper_ShuffleUp";

+ 7 - 0
Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl

@@ -0,0 +1,7 @@
+int Helper_MultiplyHighS32(int x, int y)
+{
+    int msb;
+    int lsb;
+    imulExtended(x, y, msb, lsb);
+    return msb;
+}

+ 7 - 0
Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighU32.glsl

@@ -0,0 +1,7 @@
+uint Helper_MultiplyHighU32(uint x, uint y)
+{
+    uint msb;
+    uint lsb;
+    umulExtended(x, y, msb, lsb);
+    return msb;
+}

+ 2 - 0
Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs

@@ -93,6 +93,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
             Add(Instruction.Minimum,                  InstType.CallBinary,     "min");
             Add(Instruction.Minimum,                  InstType.CallBinary,     "min");
             Add(Instruction.MinimumU32,               InstType.CallBinary,     "min");
             Add(Instruction.MinimumU32,               InstType.CallBinary,     "min");
             Add(Instruction.Multiply,                 InstType.OpBinaryCom,    "*",               1);
             Add(Instruction.Multiply,                 InstType.OpBinaryCom,    "*",               1);
+            Add(Instruction.MultiplyHighS32,          InstType.CallBinary,     HelperFunctionNames.MultiplyHighS32);
+            Add(Instruction.MultiplyHighU32,          InstType.CallBinary,     HelperFunctionNames.MultiplyHighU32);
             Add(Instruction.Negate,                   InstType.OpUnary,        "-",               0);
             Add(Instruction.Negate,                   InstType.OpUnary,        "-",               0);
             Add(Instruction.ReciprocalSquareRoot,     InstType.CallUnary,      "inversesqrt");
             Add(Instruction.ReciprocalSquareRoot,     InstType.CallUnary,      "inversesqrt");
             Add(Instruction.Return,                   InstType.OpNullary,      "return");
             Add(Instruction.Return,                   InstType.OpNullary,      "return");

+ 0 - 1
Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs

@@ -1,6 +1,5 @@
 using Ryujinx.Graphics.Shader.IntermediateRepresentation;
 using Ryujinx.Graphics.Shader.IntermediateRepresentation;
 using Ryujinx.Graphics.Shader.StructuredIr;
 using Ryujinx.Graphics.Shader.StructuredIr;
-using Ryujinx.Graphics.Shader.Translation.Optimizations;
 using System;
 using System;
 
 
 using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
 using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;

+ 7 - 2
Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs

@@ -57,6 +57,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
             Set("0101110001011x", InstEmit.Fadd,    typeof(OpCodeFArithReg));
             Set("0101110001011x", InstEmit.Fadd,    typeof(OpCodeFArithReg));
             Set("010010011xxxxx", InstEmit.Ffma,    typeof(OpCodeFArithCbuf));
             Set("010010011xxxxx", InstEmit.Ffma,    typeof(OpCodeFArithCbuf));
             Set("0011001x1xxxxx", InstEmit.Ffma,    typeof(OpCodeFArithImm));
             Set("0011001x1xxxxx", InstEmit.Ffma,    typeof(OpCodeFArithImm));
+            Set("000011xxxxxxxx", InstEmit.Ffma32i, typeof(OpCodeFArithImm32));
             Set("010100011xxxxx", InstEmit.Ffma,    typeof(OpCodeFArithRegCbuf));
             Set("010100011xxxxx", InstEmit.Ffma,    typeof(OpCodeFArithRegCbuf));
             Set("010110011xxxxx", InstEmit.Ffma,    typeof(OpCodeFArithReg));
             Set("010110011xxxxx", InstEmit.Ffma,    typeof(OpCodeFArithReg));
             Set("0100110000110x", InstEmit.Flo,     typeof(OpCodeAluCbuf));
             Set("0100110000110x", InstEmit.Flo,     typeof(OpCodeAluCbuf));
@@ -102,12 +103,16 @@ namespace Ryujinx.Graphics.Shader.Decoders
             Set("0011100x11100x", InstEmit.I2I,     typeof(OpCodeAluImm));
             Set("0011100x11100x", InstEmit.I2I,     typeof(OpCodeAluImm));
             Set("0101110011100x", InstEmit.I2I,     typeof(OpCodeAluReg));
             Set("0101110011100x", InstEmit.I2I,     typeof(OpCodeAluReg));
             Set("0100110000010x", InstEmit.Iadd,    typeof(OpCodeAluCbuf));
             Set("0100110000010x", InstEmit.Iadd,    typeof(OpCodeAluCbuf));
-            Set("0011100000010x", InstEmit.Iadd,    typeof(OpCodeAluImm));
+            Set("0011100x00010x", InstEmit.Iadd,    typeof(OpCodeAluImm));
             Set("0001110x0xxxxx", InstEmit.Iadd,    typeof(OpCodeAluImm32));
             Set("0001110x0xxxxx", InstEmit.Iadd,    typeof(OpCodeAluImm32));
             Set("0101110000010x", InstEmit.Iadd,    typeof(OpCodeAluReg));
             Set("0101110000010x", InstEmit.Iadd,    typeof(OpCodeAluReg));
             Set("010011001100xx", InstEmit.Iadd3,   typeof(OpCodeAluCbuf));
             Set("010011001100xx", InstEmit.Iadd3,   typeof(OpCodeAluCbuf));
-            Set("001110001100xx", InstEmit.Iadd3,   typeof(OpCodeAluImm));
+            Set("0011100x1100xx", InstEmit.Iadd3,   typeof(OpCodeAluImm));
             Set("010111001100xx", InstEmit.Iadd3,   typeof(OpCodeAluReg));
             Set("010111001100xx", InstEmit.Iadd3,   typeof(OpCodeAluReg));
+            Set("010010100xxxxx", InstEmit.Imad,    typeof(OpCodeAluCbuf));
+            Set("0011010x0xxxxx", InstEmit.Imad,    typeof(OpCodeAluImm));
+            Set("010110100xxxxx", InstEmit.Imad,    typeof(OpCodeAluReg));
+            Set("010100100xxxxx", InstEmit.Imad,    typeof(OpCodeAluRegCbuf));
             Set("0100110000100x", InstEmit.Imnmx,   typeof(OpCodeAluCbuf));
             Set("0100110000100x", InstEmit.Imnmx,   typeof(OpCodeAluCbuf));
             Set("0011100x00100x", InstEmit.Imnmx,   typeof(OpCodeAluImm));
             Set("0011100x00100x", InstEmit.Imnmx,   typeof(OpCodeAluImm));
             Set("0101110000100x", InstEmit.Imnmx,   typeof(OpCodeAluReg));
             Set("0101110000100x", InstEmit.Imnmx,   typeof(OpCodeAluReg));

+ 44 - 0
Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs

@@ -200,6 +200,50 @@ namespace Ryujinx.Graphics.Shader.Instructions
             // TODO: CC, X, corner cases
             // TODO: CC, X, corner cases
         }
         }
 
 
+        public static void Imad(EmitterContext context)
+        {
+            OpCodeAlu op = (OpCodeAlu)context.CurrOp;
+
+            bool signedA = context.CurrOp.RawOpCode.Extract(48);
+            bool signedB = context.CurrOp.RawOpCode.Extract(53);
+            bool high    = context.CurrOp.RawOpCode.Extract(54);
+
+            Operand srcA = GetSrcA(context);
+            Operand srcB = GetSrcB(context);
+            Operand srcC = GetSrcC(context);
+
+            Operand res;
+
+            if (high)
+            {
+                if (signedA && signedB)
+                {
+                    res = context.MultiplyHighS32(srcA, srcB);
+                }
+                else
+                {
+                    res = context.MultiplyHighU32(srcA, srcB);
+
+                    if (signedA)
+                    {
+                        res = context.IAdd(res, context.IMultiply(srcB, context.ShiftRightS32(srcA, Const(31))));
+                    }
+                    else if (signedB)
+                    {
+                        res = context.IAdd(res, context.IMultiply(srcA, context.ShiftRightS32(srcB, Const(31))));
+                    }
+                }
+            }
+            else
+            {
+                res = context.IMultiply(srcA, srcB);
+            }
+
+            res = context.IAdd(res, srcC);
+
+            context.Copy(GetDest(context), res);
+        }
+
         public static void Imnmx(EmitterContext context)
         public static void Imnmx(EmitterContext context)
         {
         {
             OpCodeAlu op = (OpCodeAlu)context.CurrOp;
             OpCodeAlu op = (OpCodeAlu)context.CurrOp;

+ 20 - 0
Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs

@@ -59,6 +59,26 @@ namespace Ryujinx.Graphics.Shader.Instructions
             SetFPZnFlags(context, dest, op.SetCondCode);
             SetFPZnFlags(context, dest, op.SetCondCode);
         }
         }
 
 
+        public static void Ffma32i(EmitterContext context)
+        {
+            IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
+
+            bool saturate = op.RawOpCode.Extract(55);
+            bool negateA  = op.RawOpCode.Extract(56);
+            bool negateC  = op.RawOpCode.Extract(57);
+
+            Operand srcA = context.FPNegate(GetSrcA(context), negateA);
+            Operand srcC = context.FPNegate(GetDest(context), negateC);
+
+            Operand srcB = GetSrcB(context);
+
+            Operand dest = GetDest(context);
+
+            context.Copy(dest, context.FPSaturate(context.FPFusedMultiplyAdd(srcA, srcB, srcC), saturate));
+
+            SetFPZnFlags(context, dest, op.SetCondCode);
+        }
+
         public static void Fmnmx(EmitterContext context)
         public static void Fmnmx(EmitterContext context)
         {
         {
             IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
             IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;

+ 2 - 0
Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs

@@ -84,6 +84,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
         Minimum,
         Minimum,
         MinimumU32,
         MinimumU32,
         Multiply,
         Multiply,
+        MultiplyHighS32,
+        MultiplyHighU32,
         Negate,
         Negate,
         PackDouble2x32,
         PackDouble2x32,
         PackHalf2x16,
         PackHalf2x16,

+ 2 - 0
Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj

@@ -1,6 +1,8 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
   <ItemGroup>
   <ItemGroup>
+    <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighS32.glsl" />
+    <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighU32.glsl" />
     <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" />
     <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" />
     <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleDown.glsl" />
     <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleDown.glsl" />
     <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleUp.glsl" />
     <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleUp.glsl" />

+ 6 - 2
Ryujinx.Graphics.Shader/ShaderCapabilities.cs

@@ -2,14 +2,18 @@ namespace Ryujinx.Graphics.Shader
 {
 {
     public struct ShaderCapabilities
     public struct ShaderCapabilities
     {
     {
-        private static readonly ShaderCapabilities _default = new ShaderCapabilities(16);
+        private static readonly ShaderCapabilities _default = new ShaderCapabilities(32768, 16);
 
 
         public static ShaderCapabilities Default => _default;
         public static ShaderCapabilities Default => _default;
 
 
+        public int MaximumViewportDimensions    { get; }
         public int StorageBufferOffsetAlignment { get; }
         public int StorageBufferOffsetAlignment { get; }
 
 
-        public ShaderCapabilities(int storageBufferOffsetAlignment)
+        public ShaderCapabilities(
+            int maximumViewportDimensions,
+            int storageBufferOffsetAlignment)
         {
         {
+            MaximumViewportDimensions    = maximumViewportDimensions;
             StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
             StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
         }
         }
     }
     }

+ 7 - 5
Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs

@@ -5,10 +5,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
     [Flags]
     [Flags]
     enum HelperFunctionsMask
     enum HelperFunctionsMask
     {
     {
-        Shuffle     = 1 << 0,
-        ShuffleDown = 1 << 1,
-        ShuffleUp   = 1 << 2,
-        ShuffleXor  = 1 << 3,
-        SwizzleAdd  = 1 << 4
+        MultiplyHighS32 = 1 << 0,
+        MultiplyHighU32 = 1 << 1,
+        Shuffle         = 1 << 2,
+        ShuffleDown     = 1 << 3,
+        ShuffleUp       = 1 << 4,
+        ShuffleXor      = 1 << 5,
+        SwizzleAdd      = 1 << 6
     }
     }
 }
 }

+ 2 - 0
Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs

@@ -102,6 +102,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
             Add(Instruction.Minimum,                  VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
             Add(Instruction.Minimum,                  VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
             Add(Instruction.MinimumU32,               VariableType.U32,    VariableType.U32,    VariableType.U32);
             Add(Instruction.MinimumU32,               VariableType.U32,    VariableType.U32,    VariableType.U32);
             Add(Instruction.Multiply,                 VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
             Add(Instruction.Multiply,                 VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
+            Add(Instruction.MultiplyHighS32,          VariableType.S32,    VariableType.S32,    VariableType.S32);
+            Add(Instruction.MultiplyHighU32,          VariableType.U32,    VariableType.U32,    VariableType.U32);
             Add(Instruction.Negate,                   VariableType.Scalar, VariableType.Scalar);
             Add(Instruction.Negate,                   VariableType.Scalar, VariableType.Scalar);
             Add(Instruction.PackHalf2x16,             VariableType.U32,    VariableType.F32,    VariableType.F32);
             Add(Instruction.PackHalf2x16,             VariableType.U32,    VariableType.F32,    VariableType.F32);
             Add(Instruction.ReciprocalSquareRoot,     VariableType.Scalar, VariableType.Scalar);
             Add(Instruction.ReciprocalSquareRoot,     VariableType.Scalar, VariableType.Scalar);

+ 6 - 0
Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs

@@ -171,6 +171,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
             // decide which helper functions are needed on the final generated code.
             // decide which helper functions are needed on the final generated code.
             switch (operation.Inst)
             switch (operation.Inst)
             {
             {
+                case Instruction.MultiplyHighS32:
+                    context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32;
+                    break;
+                case Instruction.MultiplyHighU32:
+                    context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighU32;
+                    break;
                 case Instruction.Shuffle:
                 case Instruction.Shuffle:
                     context.Info.HelperFunctionsMask |= HelperFunctionsMask.Shuffle;
                     context.Info.HelperFunctionsMask |= HelperFunctionsMask.Shuffle;
                     break;
                     break;

+ 25 - 7
Ryujinx.Graphics.Shader/Translation/EmitterContext.cs

@@ -11,18 +11,25 @@ namespace Ryujinx.Graphics.Shader.Translation
         public Block  CurrBlock { get; set; }
         public Block  CurrBlock { get; set; }
         public OpCode CurrOp    { get; set; }
         public OpCode CurrOp    { get; set; }
 
 
-        private ShaderStage _stage;
-
-        private ShaderHeader _header;
+        private ShaderStage        _stage;
+        private ShaderHeader       _header;
+        private ShaderCapabilities _capabilities;
+        private TranslationFlags   _flags;
 
 
         private List<Operation> _operations;
         private List<Operation> _operations;
 
 
         private Dictionary<ulong, Operand> _labels;
         private Dictionary<ulong, Operand> _labels;
 
 
-        public EmitterContext(ShaderStage stage, ShaderHeader header)
+        public EmitterContext(
+            ShaderStage        stage,
+            ShaderHeader       header,
+            ShaderCapabilities capabilities,
+            TranslationFlags   flags)
         {
         {
-            _stage  = stage;
-            _header = header;
+            _stage        = stage;
+            _header       = header;
+            _capabilities = capabilities;
+            _flags        = flags;
 
 
             _operations = new List<Operation>();
             _operations = new List<Operation>();
 
 
@@ -62,7 +69,18 @@ namespace Ryujinx.Graphics.Shader.Translation
 
 
         public void PrepareForReturn()
         public void PrepareForReturn()
         {
         {
-            if (_stage == ShaderStage.Fragment)
+            if (_stage == ShaderStage.Vertex)
+            {
+                if ((_flags & TranslationFlags.DividePosXY) != 0)
+                {
+                    Operand posX = Attribute(AttributeConsts.PositionX);
+                    Operand posY = Attribute(AttributeConsts.PositionY);
+
+                    this.Copy(posX, this.FPDivide(posX, ConstF(_capabilities.MaximumViewportDimensions / 2)));
+                    this.Copy(posY, this.FPDivide(posY, ConstF(_capabilities.MaximumViewportDimensions / 2)));
+                }
+            }
+            else if (_stage == ShaderStage.Fragment)
             {
             {
                 if (_header.OmapDepth)
                 if (_header.OmapDepth)
                 {
                 {

+ 10 - 0
Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs

@@ -476,6 +476,16 @@ namespace Ryujinx.Graphics.Shader.Translation
             return context.Add(Instruction.LoadShared, Local(), a);
             return context.Add(Instruction.LoadShared, Local(), a);
         }
         }
 
 
+        public static Operand MultiplyHighS32(this EmitterContext context, Operand a, Operand b)
+        {
+            return context.Add(Instruction.MultiplyHighS32, Local(), a, b);
+        }
+
+        public static Operand MultiplyHighU32(this EmitterContext context, Operand a, Operand b)
+        {
+            return context.Add(Instruction.MultiplyHighU32, Local(), a, b);
+        }
+
         public static Operand PackHalf2x16(this EmitterContext context, Operand a, Operand b)
         public static Operand PackHalf2x16(this EmitterContext context, Operand a, Operand b)
         {
         {
             return context.Add(Instruction.PackHalf2x16, Local(), a, b);
             return context.Add(Instruction.PackHalf2x16, Local(), a, b);

+ 2 - 1
Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs

@@ -6,6 +6,7 @@ namespace Ryujinx.Graphics.Shader.Translation
 
 
         Compute       = 1 << 0,
         Compute       = 1 << 0,
         DebugMode     = 1 << 1,
         DebugMode     = 1 << 1,
-        Unspecialized = 1 << 2
+        Unspecialized = 1 << 2,
+        DividePosXY   = 1 << 3
     }
     }
 }
 }

+ 14 - 20
Ryujinx.Graphics.Shader/Translation/Translator.cs

@@ -49,15 +49,9 @@ namespace Ryujinx.Graphics.Shader.Translation
 
 
         public static ShaderProgram Translate(Span<byte> code, ShaderCapabilities capabilities, TranslationFlags flags)
         public static ShaderProgram Translate(Span<byte> code, ShaderCapabilities capabilities, TranslationFlags flags)
         {
         {
-            bool compute   = (flags & TranslationFlags.Compute)   != 0;
-            bool debugMode = (flags & TranslationFlags.DebugMode) != 0;
+            bool compute = (flags & TranslationFlags.Compute) != 0;
 
 
-            Operation[] ops = DecodeShader(
-                code,
-                compute,
-                debugMode,
-                out ShaderHeader header,
-                out int size);
+            Operation[] ops = DecodeShader(code, capabilities, flags, out ShaderHeader header, out int size);
 
 
             ShaderStage stage;
             ShaderStage stage;
 
 
@@ -94,8 +88,8 @@ namespace Ryujinx.Graphics.Shader.Translation
         {
         {
             bool debugMode = (flags & TranslationFlags.DebugMode) != 0;
             bool debugMode = (flags & TranslationFlags.DebugMode) != 0;
 
 
-            Operation[] vpAOps = DecodeShader(vpACode, compute: false, debugMode, out _, out _);
-            Operation[] vpBOps = DecodeShader(vpBCode, compute: false, debugMode, out ShaderHeader header, out int sizeB);
+            Operation[] vpAOps = DecodeShader(vpACode, capabilities, flags, out _, out _);
+            Operation[] vpBOps = DecodeShader(vpBCode, capabilities, flags, out ShaderHeader header, out int sizeB);
 
 
             ShaderConfig config = new ShaderConfig(
             ShaderConfig config = new ShaderConfig(
                 header.Stage,
                 header.Stage,
@@ -142,23 +136,23 @@ namespace Ryujinx.Graphics.Shader.Translation
         }
         }
 
 
         private static Operation[] DecodeShader(
         private static Operation[] DecodeShader(
-            Span<byte>       code,
-            bool             compute,
-            bool             debugMode,
-            out ShaderHeader header,
-            out int          size)
+            Span<byte>         code,
+            ShaderCapabilities capabilities,
+            TranslationFlags   flags,
+            out ShaderHeader   header,
+            out int            size)
         {
         {
             Block[] cfg;
             Block[] cfg;
 
 
             EmitterContext context;
             EmitterContext context;
 
 
-            if (compute)
+            if ((flags & TranslationFlags.Compute) != 0)
             {
             {
                 header = null;
                 header = null;
 
 
                 cfg = Decoder.Decode(code, 0);
                 cfg = Decoder.Decode(code, 0);
 
 
-                context = new EmitterContext(ShaderStage.Compute, header);
+                context = new EmitterContext(ShaderStage.Compute, header, capabilities, flags);
             }
             }
             else
             else
             {
             {
@@ -166,7 +160,7 @@ namespace Ryujinx.Graphics.Shader.Translation
 
 
                 cfg = Decoder.Decode(code, HeaderSize);
                 cfg = Decoder.Decode(code, HeaderSize);
 
 
-                context = new EmitterContext(header.Stage, header);
+                context = new EmitterContext(header.Stage, header, capabilities, flags);
             }
             }
 
 
             if (cfg == null)
             if (cfg == null)
@@ -197,7 +191,7 @@ namespace Ryujinx.Graphics.Shader.Translation
                 {
                 {
                     OpCode op = block.OpCodes[opIndex];
                     OpCode op = block.OpCodes[opIndex];
 
 
-                    if (debugMode)
+                    if ((flags & TranslationFlags.DebugMode) != 0)
                     {
                     {
                         string instName;
                         string instName;
 
 
@@ -274,7 +268,7 @@ namespace Ryujinx.Graphics.Shader.Translation
                 }
                 }
             }
             }
 
 
-            size = (int)maxEndAddress + (compute ? 0 : HeaderSize);
+            size = (int)maxEndAddress + (((flags & TranslationFlags.Compute) != 0) ? 0 : HeaderSize);
 
 
             return context.GetOperations();
             return context.GetOperations();
         }
         }