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
This commit is contained in:
parent
e5f7ff1eee
commit
79adba4402
17 changed files with 286 additions and 112 deletions
|
@ -104,6 +104,6 @@ namespace Ryujinx.Graphics.GAL
|
|||
bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual);
|
||||
void EndHostConditionalRendering();
|
||||
|
||||
void UpdateRenderScale(ShaderStage stage, ReadOnlySpan<float> scales, int textureCount, int imageCount);
|
||||
void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,22 +6,20 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
|||
struct UpdateRenderScaleCommand : IGALCommand
|
||||
{
|
||||
public CommandType CommandType => CommandType.UpdateRenderScale;
|
||||
private ShaderStage _stage;
|
||||
private SpanRef<float> _scales;
|
||||
private int _textureCount;
|
||||
private int _imageCount;
|
||||
private int _totalCount;
|
||||
private int _fragmentCount;
|
||||
|
||||
public void Set(ShaderStage stage, SpanRef<float> scales, int textureCount, int imageCount)
|
||||
public void Set(SpanRef<float> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -353,9 +353,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||
return false;
|
||||
}
|
||||
|
||||
public void UpdateRenderScale(ShaderStage stage, ReadOnlySpan<float> scales, int textureCount, int imageCount)
|
||||
public void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount)
|
||||
{
|
||||
_renderer.New<UpdateRenderScaleCommand>().Set(stage, _renderer.CopySpan(scales.Slice(0, textureCount + imageCount)), textureCount, imageCount);
|
||||
_renderer.New<UpdateRenderScaleCommand>().Set(_renderer.CopySpan(scales.Slice(0, totalCount)), totalCount, fragmentCount);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
}
|
||||
|
|
93
Ryujinx.Graphics.GAL/SupportBufferUpdater.cs
Normal file
93
Ryujinx.Graphics.GAL/SupportBufferUpdater.cs
Normal file
|
@ -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<T>(int baseOffset, ReadOnlySpan<T> data, Span<T> target, int offset, int count) where T : unmanaged
|
||||
{
|
||||
data.Slice(0, count).CopyTo(target.Slice(offset));
|
||||
|
||||
int elemSize = Unsafe.SizeOf<T>();
|
||||
|
||||
MarkDirty(baseOffset + offset * elemSize, count * elemSize);
|
||||
}
|
||||
|
||||
public void UpdateRenderScale(ReadOnlySpan<Vector4<float>> data, int offset, int count)
|
||||
{
|
||||
UpdateGenericField(SupportBuffer.GraphicsRenderScaleOffset, data, Data.RenderScale.ToSpan(), offset, count);
|
||||
}
|
||||
|
||||
public void UpdateFragmentIsBgra(ReadOnlySpan<Vector4<int>> data, int offset, int count)
|
||||
{
|
||||
UpdateGenericField(SupportBuffer.FragmentIsBgraOffset, data, Data.FragmentIsBgra.ToSpan(), offset, count);
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
{
|
||||
if (_startOffset != -1)
|
||||
{
|
||||
ReadOnlySpan<byte> data = MemoryMarshal.Cast<SupportBuffer, byte>(MemoryMarshal.CreateSpan(ref Data, 1));
|
||||
|
||||
_renderer.SetBufferData(Handle, _startOffset, data.Slice(_startOffset, _endOffset - _startOffset));
|
||||
|
||||
_startOffset = -1;
|
||||
_endOffset = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_renderer.DeleteBuffer(Handle);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -231,24 +231,24 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
bool changed = false;
|
||||
|
||||
if ((binding.Flags & TextureUsageFlags.NeedsScaleValue) != 0 && texture != null)
|
||||
{
|
||||
if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0)
|
||||
{
|
||||
changed = texture.ScaleMode != TextureScaleMode.Blacklisted;
|
||||
texture.BlacklistScale();
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (stage)
|
||||
{
|
||||
case ShaderStage.Fragment:
|
||||
if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0)
|
||||
{
|
||||
changed |= texture.ScaleMode != TextureScaleMode.Blacklisted;
|
||||
texture.BlacklistScale();
|
||||
break;
|
||||
}
|
||||
|
||||
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 (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;
|
||||
|
@ -259,15 +259,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
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;
|
||||
|
||||
case ShaderStage.Compute:
|
||||
result = texture.ScaleFactor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,13 +286,29 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// <summary>
|
||||
/// Uploads texture and image scales to the backend when they are used.
|
||||
/// </summary>
|
||||
/// <param name="stage">Current shader stage</param>
|
||||
/// <param name="stageIndex">Shader stage index</param>
|
||||
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,10 +339,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
|
||||
CommitTextureBindings(texturePool, stage, stageIndex);
|
||||
CommitImageBindings (texturePool, stage, stageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
CommitRenderScale(stage, stageIndex);
|
||||
}
|
||||
}
|
||||
CommitRenderScale();
|
||||
|
||||
_rebind = false;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <summary>
|
||||
/// Version of the codegen (to be changed when codegen or guest format change).
|
||||
/// </summary>
|
||||
private const ulong ShaderCodeGenVersion = 2885;
|
||||
private const ulong ShaderCodeGenVersion = 2764;
|
||||
|
||||
// Progress reporting helpers
|
||||
private volatile int _shaderCount;
|
||||
|
|
|
@ -43,16 +43,9 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
|
||||
private CounterQueueEvent _activeConditionalRender;
|
||||
|
||||
private struct Vector4<T>
|
||||
{
|
||||
public T X;
|
||||
public T Y;
|
||||
public T Z;
|
||||
public T W;
|
||||
}
|
||||
|
||||
private Vector4<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount];
|
||||
private Vector4<float>[] _renderScale = new Vector4<float>[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<BufferHandle, int>(ref _supportBuffer));
|
||||
_supportBuffer = new SupportBufferUpdater(renderer);
|
||||
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, Unsafe.As<BufferHandle, int>(ref _supportBuffer.Handle));
|
||||
|
||||
SetSupportBufferData<Vector4<int>>(SupportBuffer.FragmentIsBgraOffset, _fpIsBgra, SupportBuffer.FragmentIsBgraCount);
|
||||
SetSupportBufferData<Vector4<float>>(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<Vector4<float>>(SupportBuffer.FragmentRenderScaleOffset, _renderScale, 1); // Just the first element.
|
||||
_supportBuffer.UpdateRenderScale(_renderScale, 0, 1); // Just the first element.
|
||||
}
|
||||
|
||||
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks)
|
||||
|
@ -1076,7 +1071,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
|
||||
if (isBgraChanged)
|
||||
{
|
||||
SetSupportBufferData<Vector4<int>>(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<float> scales, int textureCount, int imageCount)
|
||||
public void UpdateRenderScale(ReadOnlySpan<float> 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
|
|||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
// Only update fragment count if there are scales after it for the vertex stage.
|
||||
if (fragmentCount != totalCount && fragmentCount != _fragmentScaleCount)
|
||||
{
|
||||
SetSupportBufferData<Vector4<float>>(SupportBuffer.FragmentRenderScaleOffset, _renderScale, 1 + textureCount + imageCount);
|
||||
}
|
||||
_fragmentScaleCount = fragmentCount;
|
||||
_supportBuffer.UpdateFragmentRenderScaleCount(_fragmentScaleCount);
|
||||
}
|
||||
|
||||
private void SetSupportBufferData<T>(int offset, ReadOnlySpan<T> data, int count) where T : unmanaged
|
||||
if (changed)
|
||||
{
|
||||
Buffer.SetData(_supportBuffer, offset, MemoryMarshal.Cast<T, byte>(data.Slice(0, count)));
|
||||
_supportBuffer.UpdateRenderScale(_renderScale, 0, 1 + totalCount);
|
||||
}
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
|
|
|
@ -151,7 +151,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
GL.Arb.MaxShaderCompilerThreads(Math.Min(Environment.ProcessorCount, 8));
|
||||
}
|
||||
|
||||
_pipeline.Initialize();
|
||||
_pipeline.Initialize(this);
|
||||
_counters.Initialize();
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
case ShaderStage.Fragment:
|
||||
case ShaderStage.Vertex:
|
||||
context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};");
|
||||
context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];");
|
||||
}
|
||||
else
|
||||
{
|
||||
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();
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="CodeGen\Glsl\HelperFunctions\TexelFetchScale_vp.glsl" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
@ -20,6 +24,7 @@
|
|||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreStorageSmallInt.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\TexelFetchScale_vp.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\TexelFetchScale_fp.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\TexelFetchScale_cp.glsl" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -11,4 +11,17 @@ namespace Ryujinx.Graphics.Shader
|
|||
|
||||
Count
|
||||
}
|
||||
|
||||
public static class ShaderStageExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the shader stage supports render scale.
|
||||
/// </summary>
|
||||
/// <param name="stage">Shader stage</param>
|
||||
/// <returns>True if the shader stage supports render scale, false otherwise</returns>
|
||||
public static bool SupportsRenderScale(this ShaderStage stage)
|
||||
{
|
||||
return stage == ShaderStage.Vertex || stage == ShaderStage.Fragment || stage == ShaderStage.Compute;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,55 @@
|
|||
using Ryujinx.Common.Memory;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader
|
||||
{
|
||||
public static class SupportBuffer
|
||||
public struct Vector4<T>
|
||||
{
|
||||
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<T>(ref SupportBuffer storage, ref T target)
|
||||
{
|
||||
return (int)Unsafe.ByteOffset(ref Unsafe.As<SupportBuffer, T>(ref storage), ref target);
|
||||
}
|
||||
|
||||
static SupportBuffer()
|
||||
{
|
||||
FieldSize = Unsafe.SizeOf<Vector4<float>>();
|
||||
RequiredSize = Unsafe.SizeOf<SupportBuffer>();
|
||||
|
||||
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<int> FragmentAlphaTest;
|
||||
public Array8<Vector4<int>> FragmentIsBgra;
|
||||
public Vector4<int> FragmentRenderScaleCount;
|
||||
|
||||
// Render scale max count: 1 + 32 + 8. First scale is fragment output scale, others are textures/image inputs.
|
||||
public Array41<Vector4<float>> RenderScale;
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue