From 79adba44022abb5c1c547fb41d98a4d7a6b5029a Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sat, 8 Jan 2022 17:48:48 +0000 Subject: [PATCH] Add support for render scale to vertex stage. (#2763) * Add support for render scale to vertex stage. Occasionally games read off textureSize on the vertex stage to inform the fragment shader what size a texture is without querying in there. Scales were not present in the vertex shader to correct the sizes, so games were providing the raw upscaled texture size to the fragment shader, which was incorrect. One downside is that the fragment and vertex support buffer description must be identical, so the full size scales array must be defined when used. I don't think this will have an impact though. Another is that the fragment texture count must be updated when vertex shader textures are used. I'd like to correct this so that the update is folded into the update for the scales. Also cleans up a bunch of things, like it making no sense to call CommitRenderScale for each stage. Fixes render scale causing a weird offset bloom in Super Mario Party and Clubhouse Games. Clubhouse Games still has a pixelated look in a number of its games due to something else it does in the shader. * Split out support buffer update, lazy updates. * Commit support buffer before compute dispatch * Remove unnecessary qualifier. * Address Feedback --- Ryujinx.Graphics.GAL/IPipeline.cs | 2 +- .../Commands/UpdateRenderScaleCommand.cs | 14 ++- .../Multithreading/ThreadedPipeline.cs | 4 +- Ryujinx.Graphics.GAL/SupportBufferUpdater.cs | 93 +++++++++++++++++++ .../Image/TextureBindingsManager.cs | 90 ++++++++++-------- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 2 +- Ryujinx.Graphics.OpenGL/Pipeline.cs | 58 +++++------- Ryujinx.Graphics.OpenGL/Renderer.cs | 2 +- .../CodeGen/Glsl/Declarations.cs | 31 ++++--- .../CodeGen/Glsl/DefaultNames.cs | 1 + .../HelperFunctions/TexelFetchScale_fp.glsl | 2 +- .../HelperFunctions/TexelFetchScale_vp.glsl | 20 ++++ .../Glsl/Instructions/InstGenMemory.cs | 6 +- .../Ryujinx.Graphics.Shader.csproj | 5 + Ryujinx.Graphics.Shader/ShaderStage.cs | 13 +++ Ryujinx.Graphics.Shader/SupportBuffer.cs | 53 +++++++++-- .../Translation/ShaderConfig.cs | 2 +- 17 files changed, 286 insertions(+), 112 deletions(-) create mode 100644 Ryujinx.Graphics.GAL/SupportBufferUpdater.cs create mode 100644 Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs index b7da2c216..c02f84d47 100644 --- a/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/Ryujinx.Graphics.GAL/IPipeline.cs @@ -104,6 +104,6 @@ namespace Ryujinx.Graphics.GAL bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual); void EndHostConditionalRendering(); - void UpdateRenderScale(ShaderStage stage, ReadOnlySpan scales, int textureCount, int imageCount); + void UpdateRenderScale(ReadOnlySpan scales, int totalCount, int fragmentCount); } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs index fafb52a87..7e1e66b20 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs @@ -6,22 +6,20 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands struct UpdateRenderScaleCommand : IGALCommand { public CommandType CommandType => CommandType.UpdateRenderScale; - private ShaderStage _stage; private SpanRef _scales; - private int _textureCount; - private int _imageCount; + private int _totalCount; + private int _fragmentCount; - public void Set(ShaderStage stage, SpanRef scales, int textureCount, int imageCount) + public void Set(SpanRef scales, int totalCount, int fragmentCount) { - _stage = stage; _scales = scales; - _textureCount = textureCount; - _imageCount = imageCount; + _totalCount = totalCount; + _fragmentCount = fragmentCount; } public static void Run(ref UpdateRenderScaleCommand command, ThreadedRenderer threaded, IRenderer renderer) { - renderer.Pipeline.UpdateRenderScale(command._stage, command._scales.Get(threaded), command._textureCount, command._imageCount); + renderer.Pipeline.UpdateRenderScale(command._scales.Get(threaded), command._totalCount, command._fragmentCount); command._scales.Dispose(threaded); } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index 63a29b1be..6dc8ef386 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -353,9 +353,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading return false; } - public void UpdateRenderScale(ShaderStage stage, ReadOnlySpan scales, int textureCount, int imageCount) + public void UpdateRenderScale(ReadOnlySpan scales, int totalCount, int fragmentCount) { - _renderer.New().Set(stage, _renderer.CopySpan(scales.Slice(0, textureCount + imageCount)), textureCount, imageCount); + _renderer.New().Set(_renderer.CopySpan(scales.Slice(0, totalCount)), totalCount, fragmentCount); _renderer.QueueCommand(); } } diff --git a/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs b/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs new file mode 100644 index 000000000..cb24bdd75 --- /dev/null +++ b/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs @@ -0,0 +1,93 @@ +using Ryujinx.Graphics.Shader; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.GAL +{ + public class SupportBufferUpdater : IDisposable + { + public SupportBuffer Data; + public BufferHandle Handle; + + private IRenderer _renderer; + private int _startOffset = -1; + private int _endOffset = -1; + + public SupportBufferUpdater(IRenderer renderer) + { + _renderer = renderer; + Handle = renderer.CreateBuffer(SupportBuffer.RequiredSize); + } + + private void MarkDirty(int startOffset, int byteSize) + { + int endOffset = startOffset + byteSize; + + if (_startOffset == -1) + { + _startOffset = startOffset; + _endOffset = endOffset; + } + else + { + if (startOffset < _startOffset) + { + _startOffset = startOffset; + } + + if (endOffset > _endOffset) + { + _endOffset = endOffset; + } + } + } + + public void UpdateFragmentRenderScaleCount(int count) + { + if (Data.FragmentRenderScaleCount.X != count) + { + Data.FragmentRenderScaleCount.X = count; + + MarkDirty(SupportBuffer.FragmentRenderScaleCountOffset, sizeof(int)); + } + } + + private void UpdateGenericField(int baseOffset, ReadOnlySpan data, Span target, int offset, int count) where T : unmanaged + { + data.Slice(0, count).CopyTo(target.Slice(offset)); + + int elemSize = Unsafe.SizeOf(); + + MarkDirty(baseOffset + offset * elemSize, count * elemSize); + } + + public void UpdateRenderScale(ReadOnlySpan> data, int offset, int count) + { + UpdateGenericField(SupportBuffer.GraphicsRenderScaleOffset, data, Data.RenderScale.ToSpan(), offset, count); + } + + public void UpdateFragmentIsBgra(ReadOnlySpan> data, int offset, int count) + { + UpdateGenericField(SupportBuffer.FragmentIsBgraOffset, data, Data.FragmentIsBgra.ToSpan(), offset, count); + } + + public void Commit() + { + if (_startOffset != -1) + { + ReadOnlySpan data = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref Data, 1)); + + _renderer.SetBufferData(Handle, _startOffset, data.Slice(_startOffset, _endOffset - _startOffset)); + + _startOffset = -1; + _endOffset = -1; + } + } + + public void Dispose() + { + _renderer.DeleteBuffer(Handle); + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index dea918cd4..97a9eee43 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -232,42 +232,44 @@ namespace Ryujinx.Graphics.Gpu.Image if ((binding.Flags & TextureUsageFlags.NeedsScaleValue) != 0 && texture != null) { - switch (stage) + if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0) { - case ShaderStage.Fragment: - if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0) - { - changed |= texture.ScaleMode != TextureScaleMode.Blacklisted; - texture.BlacklistScale(); - break; - } + changed = texture.ScaleMode != TextureScaleMode.Blacklisted; + texture.BlacklistScale(); + } + else + { + switch (stage) + { + case ShaderStage.Fragment: + float scale = texture.ScaleFactor; - float scale = texture.ScaleFactor; - - if (scale != 1) - { - Texture activeTarget = _channel.TextureManager.GetAnyRenderTarget(); - - if (activeTarget != null && activeTarget.Info.Width / (float)texture.Info.Width == activeTarget.Info.Height / (float)texture.Info.Height) + if (scale != 1) { - // If the texture's size is a multiple of the sampler size, enable interpolation using gl_FragCoord. (helps "invent" new integer values between scaled pixels) - result = -scale; - break; + Texture activeTarget = _channel.TextureManager.GetAnyRenderTarget(); + + if (activeTarget != null && (activeTarget.Info.Width / (float)texture.Info.Width) == (activeTarget.Info.Height / (float)texture.Info.Height)) + { + // If the texture's size is a multiple of the sampler size, enable interpolation using gl_FragCoord. (helps "invent" new integer values between scaled pixels) + result = -scale; + break; + } } - } - result = scale; - break; + result = scale; + break; - case ShaderStage.Compute: - if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0) - { - changed |= texture.ScaleMode != TextureScaleMode.Blacklisted; - texture.BlacklistScale(); - } + case ShaderStage.Vertex: + int fragmentIndex = (int)ShaderStage.Fragment - 1; + index += _textureBindingsCount[fragmentIndex] + _imageBindingsCount[fragmentIndex]; - result = texture.ScaleFactor; - break; + result = texture.ScaleFactor; + break; + + case ShaderStage.Compute: + result = texture.ScaleFactor; + break; + } } } @@ -284,13 +286,29 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Uploads texture and image scales to the backend when they are used. /// - /// Current shader stage - /// Shader stage index - private void CommitRenderScale(ShaderStage stage, int stageIndex) + private void CommitRenderScale() { if (_scaleChanged) { - _context.Renderer.Pipeline.UpdateRenderScale(stage, _scales, _textureBindingsCount[stageIndex], _imageBindingsCount[stageIndex]); + int fragmentTotal = 0; + int total; + + if (!_isCompute) + { + int fragmentIndex = (int)ShaderStage.Fragment - 1; + fragmentTotal = _textureBindingsCount[fragmentIndex] + _imageBindingsCount[fragmentIndex]; + + int vertexIndex = (int)ShaderStage.Vertex - 1; + int vertexTotal = _textureBindingsCount[vertexIndex] + _imageBindingsCount[vertexIndex]; + + total = fragmentTotal + vertexTotal; + } + else + { + total = _textureBindingsCount[0] + _imageBindingsCount[0]; + } + + _context.Renderer.Pipeline.UpdateRenderScale(_scales, total, fragmentTotal); _scaleChanged = false; } @@ -312,8 +330,6 @@ namespace Ryujinx.Graphics.Gpu.Image { CommitTextureBindings(texturePool, ShaderStage.Compute, 0); CommitImageBindings (texturePool, ShaderStage.Compute, 0); - - CommitRenderScale(ShaderStage.Compute, 0); } else { @@ -323,11 +339,11 @@ namespace Ryujinx.Graphics.Gpu.Image CommitTextureBindings(texturePool, stage, stageIndex); CommitImageBindings (texturePool, stage, stageIndex); - - CommitRenderScale(stage, stageIndex); } } + CommitRenderScale(); + _rebind = false; } diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 32a1e4bd8..acd656210 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Version of the codegen (to be changed when codegen or guest format change). /// - private const ulong ShaderCodeGenVersion = 2885; + private const ulong ShaderCodeGenVersion = 2764; // Progress reporting helpers private volatile int _shaderCount; diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index aafc4db80..6d6e07457 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -43,16 +43,9 @@ namespace Ryujinx.Graphics.OpenGL private CounterQueueEvent _activeConditionalRender; - private struct Vector4 - { - public T X; - public T Y; - public T Z; - public T W; - } - private Vector4[] _fpIsBgra = new Vector4[SupportBuffer.FragmentIsBgraCount]; private Vector4[] _renderScale = new Vector4[65]; + private int _fragmentScaleCount; private TextureBase _unit0Texture; private Sampler _unit0Sampler; @@ -68,7 +61,7 @@ namespace Ryujinx.Graphics.OpenGL private bool _tfEnabled; private TransformFeedbackPrimitiveType _tfTopology; - private BufferHandle _supportBuffer; + private SupportBufferUpdater _supportBuffer; private readonly BufferHandle[] _tfbs; private readonly BufferRange[] _tfbTargets; @@ -95,13 +88,13 @@ namespace Ryujinx.Graphics.OpenGL _tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers]; } - public void Initialize() + public void Initialize(Renderer renderer) { - _supportBuffer = Buffer.Create(SupportBuffer.RequiredSize); - GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, Unsafe.As(ref _supportBuffer)); + _supportBuffer = new SupportBufferUpdater(renderer); + GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, Unsafe.As(ref _supportBuffer.Handle)); - SetSupportBufferData>(SupportBuffer.FragmentIsBgraOffset, _fpIsBgra, SupportBuffer.FragmentIsBgraCount); - SetSupportBufferData>(SupportBuffer.FragmentRenderScaleOffset, _renderScale, SupportBuffer.RenderScaleMaxCount); + _supportBuffer.UpdateFragmentIsBgra(_fpIsBgra, 0, SupportBuffer.FragmentIsBgraCount); + _supportBuffer.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount); } public void Barrier() @@ -558,6 +551,8 @@ namespace Ryujinx.Graphics.OpenGL { if (texture is TextureView view && sampler is Sampler samp) { + _supportBuffer.Commit(); + if (HwCapabilities.SupportsDrawTexture) { GL.NV.DrawTexture( @@ -1038,7 +1033,7 @@ namespace Ryujinx.Graphics.OpenGL public void SetRenderTargetScale(float scale) { _renderScale[0].X = scale; - SetSupportBufferData>(SupportBuffer.FragmentRenderScaleOffset, _renderScale, 1); // Just the first element. + _supportBuffer.UpdateRenderScale(_renderScale, 0, 1); // Just the first element. } public void SetRenderTargetColorMasks(ReadOnlySpan componentMasks) @@ -1076,7 +1071,7 @@ namespace Ryujinx.Graphics.OpenGL if (isBgraChanged) { - SetSupportBufferData>(SupportBuffer.FragmentIsBgraOffset, _fpIsBgra, SupportBuffer.FragmentIsBgraCount); + _supportBuffer.UpdateFragmentIsBgra(_fpIsBgra, 0, SupportBuffer.FragmentIsBgraCount); } TextureView depthStencilView = (TextureView)depthStencil; @@ -1384,16 +1379,11 @@ namespace Ryujinx.Graphics.OpenGL return (_boundDrawFramebuffer, _boundReadFramebuffer); } - public void UpdateRenderScale(ShaderStage stage, ReadOnlySpan scales, int textureCount, int imageCount) + public void UpdateRenderScale(ReadOnlySpan scales, int totalCount, int fragmentCount) { - if (stage != ShaderStage.Compute && stage != ShaderStage.Fragment) - { - return; - } - bool changed = false; - for (int index = 0; index < textureCount + imageCount; index++) + for (int index = 0; index < totalCount; index++) { if (_renderScale[1 + index].X != scales[index]) { @@ -1402,20 +1392,23 @@ namespace Ryujinx.Graphics.OpenGL } } + // Only update fragment count if there are scales after it for the vertex stage. + if (fragmentCount != totalCount && fragmentCount != _fragmentScaleCount) + { + _fragmentScaleCount = fragmentCount; + _supportBuffer.UpdateFragmentRenderScaleCount(_fragmentScaleCount); + } + if (changed) { - SetSupportBufferData>(SupportBuffer.FragmentRenderScaleOffset, _renderScale, 1 + textureCount + imageCount); + _supportBuffer.UpdateRenderScale(_renderScale, 0, 1 + totalCount); } } - private void SetSupportBufferData(int offset, ReadOnlySpan data, int count) where T : unmanaged - { - Buffer.SetData(_supportBuffer, offset, MemoryMarshal.Cast(data.Slice(0, count))); - } - private void PrepareForDispatch() { _unit0Texture?.Bind(0); + _supportBuffer.Commit(); } private void PreDraw() @@ -1424,6 +1417,7 @@ namespace Ryujinx.Graphics.OpenGL _vertexArray.Validate(); _unit0Texture?.Bind(0); + _supportBuffer.Commit(); } private void PostDraw() @@ -1521,11 +1515,7 @@ namespace Ryujinx.Graphics.OpenGL public void Dispose() { - if (_supportBuffer != BufferHandle.Null) - { - Buffer.Delete(_supportBuffer); - _supportBuffer = BufferHandle.Null; - } + _supportBuffer?.Dispose(); for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++) { diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs index 0c16ec5a1..7806157dc 100644 --- a/Ryujinx.Graphics.OpenGL/Renderer.cs +++ b/Ryujinx.Graphics.OpenGL/Renderer.cs @@ -151,7 +151,7 @@ namespace Ryujinx.Graphics.OpenGL GL.Arb.MaxShaderCompilerThreads(Math.Min(Environment.ProcessorCount, 8)); } - _pipeline.Initialize(); + _pipeline.Initialize(this); _counters.Initialize(); } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 7dcd1671e..d89565672 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -208,7 +208,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl bool isFragment = context.Config.Stage == ShaderStage.Fragment; - if (isFragment || context.Config.Stage == ShaderStage.Compute) + if (isFragment || context.Config.Stage == ShaderStage.Compute || context.Config.Stage == ShaderStage.Vertex) { if (isFragment && context.Config.GpuAccessor.QueryEarlyZForce()) { @@ -227,7 +227,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl scaleElements++; // Also includes render target scale, for gl_FragCoord. } - DeclareSupportUniformBlock(context, isFragment, scaleElements); + DeclareSupportUniformBlock(context, context.Config.Stage, scaleElements); if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling)) { @@ -237,7 +237,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else if (isFragment) { - DeclareSupportUniformBlock(context, true, 0); + DeclareSupportUniformBlock(context, context.Config.Stage, 0); } } @@ -591,8 +591,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine($"patch out vec4 {name};"); } - private static void DeclareSupportUniformBlock(CodeGenContext context, bool isFragment, int scaleElements) + private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements) { + bool isFragment = stage == ShaderStage.Fragment; if (!isFragment && scaleElements == 0) { return; @@ -601,20 +602,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine($"layout (binding = 0, std140) uniform {DefaultNames.SupportBlockName}"); context.EnterScope(); - if (isFragment) + switch (stage) { - context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};"); - context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];"); - } - else - { - context.AppendLine($"uint s_reserved[{SupportBuffer.ComputeRenderScaleOffset / SupportBuffer.FieldSize}];"); + case ShaderStage.Fragment: + case ShaderStage.Vertex: + context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};"); + context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];"); + context.AppendLine($"int {DefaultNames.SupportBlockFragmentScaleCount};"); + break; + case ShaderStage.Compute: + context.AppendLine($"uint s_reserved[{SupportBuffer.ComputeRenderScaleOffset / SupportBuffer.FieldSize}];"); + break; } - if (scaleElements != 0) - { - context.AppendLine($"float {DefaultNames.SupportBlockRenderScaleName}[{scaleElements}];"); - } + context.AppendLine($"float {DefaultNames.SupportBlockRenderScaleName}[{SupportBuffer.RenderScaleMaxCount}];"); context.LeaveScope(";"); context.AppendLine(); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs index 473504080..76203522e 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 SupportBlockFragmentScaleCount = "s_frag_scale_count"; public const string SupportBlockRenderScaleName = "s_render_scale"; public const string BlockSuffix = "block"; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl index 5def1390c..6c670f91b 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl @@ -7,7 +7,7 @@ } if (scale < 0.0) // If less than 0, try interpolate between texels by using the screen position. { - return ivec2(vec2(inputVec) * (-scale) + mod(gl_FragCoord.xy, -scale)); + return ivec2(vec2(inputVec) * (-scale) + mod(gl_FragCoord.xy, 0.0 - scale)); } else { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl new file mode 100644 index 000000000..19eb119db --- /dev/null +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl @@ -0,0 +1,20 @@ +ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex) +{ + float scale = abs(s_render_scale[1 + samplerIndex + s_frag_scale_count]); + if (scale == 1.0) + { + return inputVec; + } + + return ivec2(vec2(inputVec) * scale); +} + +int Helper_TextureSizeUnscale(int size, int samplerIndex) +{ + float scale = abs(s_render_scale[1 + samplerIndex + s_frag_scale_count]); + if (scale == 1.0) + { + return size; + } + return int(float(size) / scale); +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index abca03aa5..164de7bbf 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -85,7 +85,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string ApplyScaling(string vector) { - if ((context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) && + if ((context.Config.Stage.SupportsRenderScale()) && texOp.Inst == Instruction.ImageLoad && !isBindless && !isIndexed) @@ -621,7 +621,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { if (intCoords) { - if ((context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) && + if ((context.Config.Stage.SupportsRenderScale()) && !isBindless && !isIndexed) { @@ -770,7 +770,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { string texCall = $"textureSize({samplerName}, {lodExpr}){GetMask(texOp.Index)}"; - if ((context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) && + if (context.Config.Stage.SupportsRenderScale() && !isBindless && !isIndexed) { diff --git a/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj index c604902f3..81cc0caf3 100644 --- a/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -4,6 +4,10 @@ net6.0 + + + + @@ -20,6 +24,7 @@ + diff --git a/Ryujinx.Graphics.Shader/ShaderStage.cs b/Ryujinx.Graphics.Shader/ShaderStage.cs index 63e3b0684..f16fe3281 100644 --- a/Ryujinx.Graphics.Shader/ShaderStage.cs +++ b/Ryujinx.Graphics.Shader/ShaderStage.cs @@ -11,4 +11,17 @@ namespace Ryujinx.Graphics.Shader Count } + + public static class ShaderStageExtensions + { + /// + /// Checks if the shader stage supports render scale. + /// + /// Shader stage + /// True if the shader stage supports render scale, false otherwise + public static bool SupportsRenderScale(this ShaderStage stage) + { + return stage == ShaderStage.Vertex || stage == ShaderStage.Fragment || stage == ShaderStage.Compute; + } + } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/SupportBuffer.cs b/Ryujinx.Graphics.Shader/SupportBuffer.cs index f76d2c922..47a47ea63 100644 --- a/Ryujinx.Graphics.Shader/SupportBuffer.cs +++ b/Ryujinx.Graphics.Shader/SupportBuffer.cs @@ -1,18 +1,55 @@ +using Ryujinx.Common.Memory; +using System.Runtime.CompilerServices; + namespace Ryujinx.Graphics.Shader { - public static class SupportBuffer + public struct Vector4 { - public const int FieldSize = 16; // Each field takes 16 bytes on default layout, even bool. + public T X; + public T Y; + public T Z; + public T W; + } + + public struct SupportBuffer + { + public static int FieldSize; + public static int RequiredSize; + + public static int FragmentAlphaTestOffset; + public static int FragmentIsBgraOffset; + public static int FragmentRenderScaleCountOffset; + public static int GraphicsRenderScaleOffset; + public static int ComputeRenderScaleOffset; - public const int FragmentAlphaTestOffset = 0; - public const int FragmentIsBgraOffset = FieldSize; public const int FragmentIsBgraCount = 8; - public const int FragmentRenderScaleOffset = FragmentIsBgraOffset + FragmentIsBgraCount * FieldSize; - public const int ComputeRenderScaleOffset = FragmentRenderScaleOffset + FieldSize; // Skip first scale that is used for the render target - // One for the render target, 32 for the textures, and 8 for the images. public const int RenderScaleMaxCount = 1 + 32 + 8; - public const int RequiredSize = FragmentRenderScaleOffset + RenderScaleMaxCount * FieldSize; + private static int OffsetOf(ref SupportBuffer storage, ref T target) + { + return (int)Unsafe.ByteOffset(ref Unsafe.As(ref storage), ref target); + } + + static SupportBuffer() + { + FieldSize = Unsafe.SizeOf>(); + RequiredSize = Unsafe.SizeOf(); + + SupportBuffer instance = new SupportBuffer(); + + FragmentAlphaTestOffset = OffsetOf(ref instance, ref instance.FragmentAlphaTest); + FragmentIsBgraOffset = OffsetOf(ref instance, ref instance.FragmentIsBgra); + FragmentRenderScaleCountOffset = OffsetOf(ref instance, ref instance.FragmentRenderScaleCount); + GraphicsRenderScaleOffset = OffsetOf(ref instance, ref instance.RenderScale); + ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize; + } + + public Vector4 FragmentAlphaTest; + public Array8> FragmentIsBgra; + public Vector4 FragmentRenderScaleCount; + + // Render scale max count: 1 + 32 + 8. First scale is fragment output scale, others are textures/image inputs. + public Array41> RenderScale; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index df14a5edc..9d5a40706 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -413,7 +413,7 @@ namespace Ryujinx.Graphics.Shader.Translation { usageFlags |= TextureUsageFlags.NeedsScaleValue; - var canScale = (Stage == ShaderStage.Fragment || Stage == ShaderStage.Compute) && !isIndexed && !write && dimensions == 2; + var canScale = Stage.SupportsRenderScale() && !isIndexed && !write && dimensions == 2; if (!canScale) {