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:
riperiperi 2022-01-08 17:48:48 +00:00 committed by GitHub
parent e5f7ff1eee
commit 79adba4402
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 286 additions and 112 deletions

View File

@ -104,6 +104,6 @@ namespace Ryujinx.Graphics.GAL
bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual); bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual);
void EndHostConditionalRendering(); void EndHostConditionalRendering();
void UpdateRenderScale(ShaderStage stage, ReadOnlySpan<float> scales, int textureCount, int imageCount); void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount);
} }
} }

View File

@ -6,22 +6,20 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
struct UpdateRenderScaleCommand : IGALCommand struct UpdateRenderScaleCommand : IGALCommand
{ {
public CommandType CommandType => CommandType.UpdateRenderScale; public CommandType CommandType => CommandType.UpdateRenderScale;
private ShaderStage _stage;
private SpanRef<float> _scales; private SpanRef<float> _scales;
private int _textureCount; private int _totalCount;
private int _imageCount; 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; _scales = scales;
_textureCount = textureCount; _totalCount = totalCount;
_imageCount = imageCount; _fragmentCount = fragmentCount;
} }
public static void Run(ref UpdateRenderScaleCommand command, ThreadedRenderer threaded, IRenderer renderer) 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); command._scales.Dispose(threaded);
} }
} }

View File

@ -353,9 +353,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
return false; 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(); _renderer.QueueCommand();
} }
} }

View 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);
}
}
}

View File

@ -231,24 +231,24 @@ namespace Ryujinx.Graphics.Gpu.Image
bool changed = false; bool changed = false;
if ((binding.Flags & TextureUsageFlags.NeedsScaleValue) != 0 && texture != null) if ((binding.Flags & TextureUsageFlags.NeedsScaleValue) != 0 && texture != null)
{
if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0)
{
changed = texture.ScaleMode != TextureScaleMode.Blacklisted;
texture.BlacklistScale();
}
else
{ {
switch (stage) switch (stage)
{ {
case ShaderStage.Fragment: case ShaderStage.Fragment:
if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0)
{
changed |= texture.ScaleMode != TextureScaleMode.Blacklisted;
texture.BlacklistScale();
break;
}
float scale = texture.ScaleFactor; float scale = texture.ScaleFactor;
if (scale != 1) if (scale != 1)
{ {
Texture activeTarget = _channel.TextureManager.GetAnyRenderTarget(); 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) // 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; result = -scale;
@ -259,15 +259,17 @@ namespace Ryujinx.Graphics.Gpu.Image
result = scale; result = scale;
break; break;
case ShaderStage.Compute: case ShaderStage.Vertex:
if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0) int fragmentIndex = (int)ShaderStage.Fragment - 1;
{ index += _textureBindingsCount[fragmentIndex] + _imageBindingsCount[fragmentIndex];
changed |= texture.ScaleMode != TextureScaleMode.Blacklisted;
texture.BlacklistScale();
}
result = texture.ScaleFactor; result = texture.ScaleFactor;
break; break;
case ShaderStage.Compute:
result = texture.ScaleFactor;
break;
}
} }
} }
@ -284,13 +286,29 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary> /// <summary>
/// Uploads texture and image scales to the backend when they are used. /// Uploads texture and image scales to the backend when they are used.
/// </summary> /// </summary>
/// <param name="stage">Current shader stage</param> private void CommitRenderScale()
/// <param name="stageIndex">Shader stage index</param>
private void CommitRenderScale(ShaderStage stage, int stageIndex)
{ {
if (_scaleChanged) 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; _scaleChanged = false;
} }
@ -312,8 +330,6 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
CommitTextureBindings(texturePool, ShaderStage.Compute, 0); CommitTextureBindings(texturePool, ShaderStage.Compute, 0);
CommitImageBindings (texturePool, ShaderStage.Compute, 0); CommitImageBindings (texturePool, ShaderStage.Compute, 0);
CommitRenderScale(ShaderStage.Compute, 0);
} }
else else
{ {
@ -323,10 +339,10 @@ namespace Ryujinx.Graphics.Gpu.Image
CommitTextureBindings(texturePool, stage, stageIndex); CommitTextureBindings(texturePool, stage, stageIndex);
CommitImageBindings (texturePool, stage, stageIndex); CommitImageBindings (texturePool, stage, stageIndex);
}
}
CommitRenderScale(stage, stageIndex); CommitRenderScale();
}
}
_rebind = false; _rebind = false;
} }

View File

@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary> /// <summary>
/// Version of the codegen (to be changed when codegen or guest format change). /// Version of the codegen (to be changed when codegen or guest format change).
/// </summary> /// </summary>
private const ulong ShaderCodeGenVersion = 2885; private const ulong ShaderCodeGenVersion = 2764;
// Progress reporting helpers // Progress reporting helpers
private volatile int _shaderCount; private volatile int _shaderCount;

View File

@ -43,16 +43,9 @@ namespace Ryujinx.Graphics.OpenGL
private CounterQueueEvent _activeConditionalRender; 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<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount];
private Vector4<float>[] _renderScale = new Vector4<float>[65]; private Vector4<float>[] _renderScale = new Vector4<float>[65];
private int _fragmentScaleCount;
private TextureBase _unit0Texture; private TextureBase _unit0Texture;
private Sampler _unit0Sampler; private Sampler _unit0Sampler;
@ -68,7 +61,7 @@ namespace Ryujinx.Graphics.OpenGL
private bool _tfEnabled; private bool _tfEnabled;
private TransformFeedbackPrimitiveType _tfTopology; private TransformFeedbackPrimitiveType _tfTopology;
private BufferHandle _supportBuffer; private SupportBufferUpdater _supportBuffer;
private readonly BufferHandle[] _tfbs; private readonly BufferHandle[] _tfbs;
private readonly BufferRange[] _tfbTargets; private readonly BufferRange[] _tfbTargets;
@ -95,13 +88,13 @@ namespace Ryujinx.Graphics.OpenGL
_tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers]; _tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers];
} }
public void Initialize() public void Initialize(Renderer renderer)
{ {
_supportBuffer = Buffer.Create(SupportBuffer.RequiredSize); _supportBuffer = new SupportBufferUpdater(renderer);
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, Unsafe.As<BufferHandle, int>(ref _supportBuffer)); GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, Unsafe.As<BufferHandle, int>(ref _supportBuffer.Handle));
SetSupportBufferData<Vector4<int>>(SupportBuffer.FragmentIsBgraOffset, _fpIsBgra, SupportBuffer.FragmentIsBgraCount); _supportBuffer.UpdateFragmentIsBgra(_fpIsBgra, 0, SupportBuffer.FragmentIsBgraCount);
SetSupportBufferData<Vector4<float>>(SupportBuffer.FragmentRenderScaleOffset, _renderScale, SupportBuffer.RenderScaleMaxCount); _supportBuffer.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount);
} }
public void Barrier() public void Barrier()
@ -558,6 +551,8 @@ namespace Ryujinx.Graphics.OpenGL
{ {
if (texture is TextureView view && sampler is Sampler samp) if (texture is TextureView view && sampler is Sampler samp)
{ {
_supportBuffer.Commit();
if (HwCapabilities.SupportsDrawTexture) if (HwCapabilities.SupportsDrawTexture)
{ {
GL.NV.DrawTexture( GL.NV.DrawTexture(
@ -1038,7 +1033,7 @@ namespace Ryujinx.Graphics.OpenGL
public void SetRenderTargetScale(float scale) public void SetRenderTargetScale(float scale)
{ {
_renderScale[0].X = 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) public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks)
@ -1076,7 +1071,7 @@ namespace Ryujinx.Graphics.OpenGL
if (isBgraChanged) if (isBgraChanged)
{ {
SetSupportBufferData<Vector4<int>>(SupportBuffer.FragmentIsBgraOffset, _fpIsBgra, SupportBuffer.FragmentIsBgraCount); _supportBuffer.UpdateFragmentIsBgra(_fpIsBgra, 0, SupportBuffer.FragmentIsBgraCount);
} }
TextureView depthStencilView = (TextureView)depthStencil; TextureView depthStencilView = (TextureView)depthStencil;
@ -1384,16 +1379,11 @@ namespace Ryujinx.Graphics.OpenGL
return (_boundDrawFramebuffer, _boundReadFramebuffer); 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; 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]) 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() private void PrepareForDispatch()
{ {
_unit0Texture?.Bind(0); _unit0Texture?.Bind(0);
_supportBuffer.Commit();
} }
private void PreDraw() private void PreDraw()
@ -1424,6 +1417,7 @@ namespace Ryujinx.Graphics.OpenGL
_vertexArray.Validate(); _vertexArray.Validate();
_unit0Texture?.Bind(0); _unit0Texture?.Bind(0);
_supportBuffer.Commit();
} }
private void PostDraw() private void PostDraw()
@ -1521,11 +1515,7 @@ namespace Ryujinx.Graphics.OpenGL
public void Dispose() public void Dispose()
{ {
if (_supportBuffer != BufferHandle.Null) _supportBuffer?.Dispose();
{
Buffer.Delete(_supportBuffer);
_supportBuffer = BufferHandle.Null;
}
for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++) for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++)
{ {

View File

@ -151,7 +151,7 @@ namespace Ryujinx.Graphics.OpenGL
GL.Arb.MaxShaderCompilerThreads(Math.Min(Environment.ProcessorCount, 8)); GL.Arb.MaxShaderCompilerThreads(Math.Min(Environment.ProcessorCount, 8));
} }
_pipeline.Initialize(); _pipeline.Initialize(this);
_counters.Initialize(); _counters.Initialize();
} }

View File

@ -208,7 +208,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
bool isFragment = context.Config.Stage == ShaderStage.Fragment; 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()) 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. 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)) if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling))
{ {
@ -237,7 +237,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
} }
else if (isFragment) 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};"); 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) if (!isFragment && scaleElements == 0)
{ {
return; return;
@ -601,20 +602,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine($"layout (binding = 0, std140) uniform {DefaultNames.SupportBlockName}"); context.AppendLine($"layout (binding = 0, std140) uniform {DefaultNames.SupportBlockName}");
context.EnterScope(); context.EnterScope();
if (isFragment) switch (stage)
{ {
case ShaderStage.Fragment:
case ShaderStage.Vertex:
context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};"); context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};");
context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];"); context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];");
} context.AppendLine($"int {DefaultNames.SupportBlockFragmentScaleCount};");
else break;
{ case ShaderStage.Compute:
context.AppendLine($"uint s_reserved[{SupportBuffer.ComputeRenderScaleOffset / SupportBuffer.FieldSize}];"); context.AppendLine($"uint s_reserved[{SupportBuffer.ComputeRenderScaleOffset / SupportBuffer.FieldSize}];");
break;
} }
if (scaleElements != 0) context.AppendLine($"float {DefaultNames.SupportBlockRenderScaleName}[{SupportBuffer.RenderScaleMaxCount}];");
{
context.AppendLine($"float {DefaultNames.SupportBlockRenderScaleName}[{scaleElements}];");
}
context.LeaveScope(";"); context.LeaveScope(";");
context.AppendLine(); context.AppendLine();

View File

@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public const string SupportBlockName = "support_block"; public const string SupportBlockName = "support_block";
public const string SupportBlockAlphaTestName = "s_alpha_test"; public const string SupportBlockAlphaTestName = "s_alpha_test";
public const string SupportBlockIsBgraName = "s_is_bgra"; 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 SupportBlockRenderScaleName = "s_render_scale";
public const string BlockSuffix = "block"; public const string BlockSuffix = "block";

View File

@ -7,7 +7,7 @@
} }
if (scale < 0.0) // If less than 0, try interpolate between texels by using the screen position. 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 else
{ {

View File

@ -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);
}

View File

@ -85,7 +85,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string ApplyScaling(string vector) string ApplyScaling(string vector)
{ {
if ((context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) && if ((context.Config.Stage.SupportsRenderScale()) &&
texOp.Inst == Instruction.ImageLoad && texOp.Inst == Instruction.ImageLoad &&
!isBindless && !isBindless &&
!isIndexed) !isIndexed)
@ -621,7 +621,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{ {
if (intCoords) if (intCoords)
{ {
if ((context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) && if ((context.Config.Stage.SupportsRenderScale()) &&
!isBindless && !isBindless &&
!isIndexed) !isIndexed)
{ {
@ -770,7 +770,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{ {
string texCall = $"textureSize({samplerName}, {lodExpr}){GetMask(texOp.Index)}"; 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 && !isBindless &&
!isIndexed) !isIndexed)
{ {

View File

@ -4,6 +4,10 @@
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="CodeGen\Glsl\HelperFunctions\TexelFetchScale_vp.glsl" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" /> <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
</ItemGroup> </ItemGroup>
@ -20,6 +24,7 @@
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreStorageSmallInt.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreStorageSmallInt.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.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_fp.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\TexelFetchScale_cp.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\TexelFetchScale_cp.glsl" />
</ItemGroup> </ItemGroup>

View File

@ -11,4 +11,17 @@ namespace Ryujinx.Graphics.Shader
Count 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;
}
}
} }

View File

@ -1,18 +1,55 @@
using Ryujinx.Common.Memory;
using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Shader 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 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. // One for the render target, 32 for the textures, and 8 for the images.
public const int RenderScaleMaxCount = 1 + 32 + 8; 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;
} }
} }

View File

@ -413,7 +413,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
usageFlags |= TextureUsageFlags.NeedsScaleValue; 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) if (!canScale)
{ {