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) {