Replace BGRA and scale uniforms with a uniform block (#2496)

* Replace BGRA and scale uniforms with a uniform block

* Setting the data again on program change is no longer needed

* Optimize and resolve some warnings

* Avoid redundant support buffer updates

* Some optimizations to BindBuffers (now inlined)

* Unify render scale arrays
This commit is contained in:
gdkchan 2021-08-11 16:33:43 -03:00 committed by GitHub
parent b5b7e23fc4
commit 0f6ec446ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 269 additions and 226 deletions

View File

@ -73,12 +73,12 @@ namespace Ryujinx.Graphics.GAL
void SetStencilTest(StencilTestDescriptor stencilTest);
void SetStorageBuffers(ReadOnlySpan<BufferRange> buffers);
void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers);
void SetTexture(int binding, ITexture texture);
void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers);
void SetUniformBuffers(ReadOnlySpan<BufferRange> buffers);
void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers);
void SetUserClipDistance(int index, bool enableClip);

View File

@ -957,9 +957,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
UpdateUserClipState();
}
int storageBufferBindingsCount = 0;
int uniformBufferBindingsCount = 0;
for (int stage = 0; stage < Constants.ShaderStages; stage++)
{
ShaderProgramInfo info = gs.Shaders[stage]?.Info;
@ -1015,21 +1012,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers);
_channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers);
if (info.SBuffers.Count != 0)
{
storageBufferBindingsCount = Math.Max(storageBufferBindingsCount, info.SBuffers.Max(x => x.Binding) + 1);
}
if (info.CBuffers.Count != 0)
{
uniformBufferBindingsCount = Math.Max(uniformBufferBindingsCount, info.CBuffers.Max(x => x.Binding) + 1);
}
}
_channel.BufferManager.SetGraphicsStorageBufferBindingsCount(storageBufferBindingsCount);
_channel.BufferManager.SetGraphicsUniformBufferBindingsCount(uniformBufferBindingsCount);
_context.Renderer.Pipeline.SetProgram(gs.HostProgram);
}

View File

@ -5,7 +5,7 @@ using Ryujinx.Graphics.Shader;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Gpu.Memory
{
@ -14,8 +14,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
class BufferManager
{
private const int StackToHeapThreshold = 16;
private readonly GpuContext _context;
private readonly GpuChannel _channel;
@ -23,6 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
private readonly VertexBuffer[] _vertexBuffers;
private readonly BufferBounds[] _transformFeedbackBuffers;
private readonly List<BufferTextureBinding> _bufferTextures;
private readonly BufferRange[] _ranges;
/// <summary>
/// Holds shader stage buffer state and binding information.
@ -88,11 +87,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
private readonly BuffersPerStage[] _gpStorageBuffers;
private readonly BuffersPerStage[] _gpUniformBuffers;
private int _cpStorageBufferBindings;
private int _cpUniformBufferBindings;
private int _gpStorageBufferBindings;
private int _gpUniformBufferBindings;
private bool _gpStorageBuffersDirty;
private bool _gpUniformBuffersDirty;
@ -130,6 +124,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
_bufferTextures = new List<BufferTextureBinding>();
_ranges = new BufferRange[Constants.TotalGpUniformBuffers * Constants.ShaderStages];
}
@ -288,7 +284,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
public void SetComputeStorageBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
{
_cpStorageBuffers.SetBindings(descriptors);
_cpStorageBufferBindings = descriptors.Count != 0 ? descriptors.Max(x => x.Binding) + 1 : 0;
}
/// <summary>
@ -302,15 +297,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
_gpStorageBuffersDirty = true;
}
/// <summary>
/// Sets the total number of storage buffer bindings used.
/// </summary>
/// <param name="count">Number of storage buffer bindings used</param>
public void SetGraphicsStorageBufferBindingsCount(int count)
{
_gpStorageBufferBindings = count;
}
/// <summary>
/// Sets the binding points for the uniform buffers bound on the compute pipeline.
/// </summary>
@ -318,7 +304,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
public void SetComputeUniformBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
{
_cpUniformBuffers.SetBindings(descriptors);
_cpUniformBufferBindings = descriptors.Count != 0 ? descriptors.Max(x => x.Binding) + 1 : 0;
}
/// <summary>
@ -333,15 +318,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
_gpUniformBuffersDirty = true;
}
/// <summary>
/// Sets the total number of uniform buffer bindings used.
/// </summary>
/// <param name="count">Number of uniform buffer bindings used</param>
public void SetGraphicsUniformBufferBindingsCount(int count)
{
_gpUniformBufferBindings = count;
}
/// <summary>
/// Gets a bit mask indicating which compute uniform buffers are currently bound.
/// </summary>
@ -381,7 +357,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
return mask;
}
/// <summary>
/// Gets the address of the compute uniform buffer currently bound at the given index.
/// </summary>
@ -409,46 +384,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
public void CommitComputeBindings()
{
int sCount = _cpStorageBufferBindings;
var bufferCache = _channel.MemoryManager.Physical.BufferCache;
Span<BufferRange> sRanges = sCount < StackToHeapThreshold ? stackalloc BufferRange[sCount] : new BufferRange[sCount];
for (int index = 0; index < _cpStorageBuffers.Count; index++)
{
ref var bindingInfo = ref _cpStorageBuffers.Bindings[index];
BufferBounds bounds = _cpStorageBuffers.Buffers[bindingInfo.Slot];
if (bounds.Address != 0)
{
// The storage buffer size is not reliable (it might be lower than the actual size),
// so we bind the entire buffer to allow otherwise out of range accesses to work.
sRanges[bindingInfo.Binding] = _channel.MemoryManager.Physical.BufferCache.GetBufferRangeTillEnd(
bounds.Address,
bounds.Size,
bounds.Flags.HasFlag(BufferUsageFlags.Write));
}
}
_context.Renderer.Pipeline.SetStorageBuffers(sRanges);
int uCount = _cpUniformBufferBindings;
Span<BufferRange> uRanges = uCount < StackToHeapThreshold ? stackalloc BufferRange[uCount] : new BufferRange[uCount];
for (int index = 0; index < _cpUniformBuffers.Count; index++)
{
ref var bindingInfo = ref _cpUniformBuffers.Bindings[index];
BufferBounds bounds = _cpUniformBuffers.Buffers[bindingInfo.Slot];
if (bounds.Address != 0)
{
uRanges[bindingInfo.Binding] = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(bounds.Address, bounds.Size);
}
}
_context.Renderer.Pipeline.SetUniformBuffers(uRanges);
BindBuffers(bufferCache, _cpStorageBuffers, isStorage: true);
BindBuffers(bufferCache, _cpUniformBuffers, isStorage: false);
CommitBufferTextureBindings();
@ -491,20 +430,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
public void CommitGraphicsBindings()
{
var bufferCache = _channel.MemoryManager.Physical.BufferCache;
if (_indexBufferDirty || _rebind)
{
_indexBufferDirty = false;
if (_indexBuffer.Address != 0)
{
BufferRange buffer = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
}
}
else if (_indexBuffer.Address != 0)
{
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
bufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
}
uint vbEnableMask = _vertexBuffersEnableMask;
@ -524,7 +465,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
BufferRange buffer = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(vb.Address, vb.Size);
BufferRange buffer = bufferCache.GetBufferRange(vb.Address, vb.Size);
vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
}
@ -542,7 +483,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(vb.Address, vb.Size);
bufferCache.SynchronizeBufferRange(vb.Address, vb.Size);
}
}
@ -562,7 +503,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
tfbs[index] = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(tfb.Address, tfb.Size);
tfbs[index] = bufferCache.GetBufferRange(tfb.Address, tfb.Size);
}
_context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
@ -578,7 +519,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(tfb.Address, tfb.Size);
bufferCache.SynchronizeBufferRange(tfb.Address, tfb.Size);
}
}
@ -586,7 +527,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
_gpStorageBuffersDirty = false;
BindBuffers(_gpStorageBuffers, isStorage: true);
BindBuffers(bufferCache, _gpStorageBuffers, isStorage: true);
}
else
{
@ -597,7 +538,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
_gpUniformBuffersDirty = false;
BindBuffers(_gpUniformBuffers, isStorage: false);
BindBuffers(bufferCache, _gpUniformBuffers, isStorage: false);
}
else
{
@ -612,13 +553,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Bind respective buffer bindings on the host API.
/// </summary>
/// <param name="bindings">Bindings to bind</param>
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffers</param>
private void BindBuffers(BuffersPerStage[] bindings, bool isStorage)
/// <param name="bufferCache">Buffer cache holding the buffers for the specified ranges</param>
/// <param name="bindings">Buffer memory ranges to bind</param>
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage)
{
int count = isStorage ? _gpStorageBufferBindings : _gpUniformBufferBindings;
int rangesFirst = 0;
int rangesCount = 0;
Span<BufferRange> ranges = count < StackToHeapThreshold ? stackalloc BufferRange[count] : new BufferRange[count];
Span<BufferRange> ranges = _ranges;
for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
{
@ -633,20 +577,97 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (bounds.Address != 0)
{
var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
ranges[bindingInfo.Binding] = isStorage
? _channel.MemoryManager.Physical.BufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
: _channel.MemoryManager.Physical.BufferCache.GetBufferRange(bounds.Address, bounds.Size, isWrite);
var range = isStorage
? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
: bufferCache.GetBufferRange(bounds.Address, bounds.Size);
if (rangesCount == 0)
{
rangesFirst = bindingInfo.Binding;
}
else if (bindingInfo.Binding != rangesFirst + rangesCount)
{
SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
rangesFirst = bindingInfo.Binding;
rangesCount = 0;
}
ranges[rangesCount++] = range;
}
}
}
if (rangesCount != 0)
{
SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
}
}
/// <summary>
/// Bind respective buffer bindings on the host API.
/// </summary>
/// <param name="bufferCache">Buffer cache holding the buffers for the specified ranges</param>
/// <param name="buffers">Buffer memory ranges to bind</param>
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BindBuffers(BufferCache bufferCache, BuffersPerStage buffers, bool isStorage)
{
int rangesFirst = 0;
int rangesCount = 0;
Span<BufferRange> ranges = _ranges;
for (int index = 0; index < buffers.Count; index++)
{
ref var bindingInfo = ref buffers.Bindings[index];
BufferBounds bounds = buffers.Buffers[bindingInfo.Slot];
if (bounds.Address != 0)
{
var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
var range = isStorage
? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
: bufferCache.GetBufferRange(bounds.Address, bounds.Size);
if (rangesCount == 0)
{
rangesFirst = bindingInfo.Binding;
}
else if (bindingInfo.Binding != rangesFirst + rangesCount)
{
SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
rangesFirst = bindingInfo.Binding;
rangesCount = 0;
}
ranges[rangesCount++] = range;
}
}
if (rangesCount != 0)
{
SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
}
}
/// <summary>
/// Bind respective buffer bindings on the host API.
/// </summary>
/// <param name="ranges">Host buffers to bind, with their offsets and sizes</param>
/// <param name="first">First binding point</param>
/// <param name="count">Number of bindings</param>
/// <param name="isStorage">Indicates if the buffers are storage or uniform buffers</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetHostBuffers(ReadOnlySpan<BufferRange> ranges, int first, int count, bool isStorage)
{
if (isStorage)
{
_context.Renderer.Pipeline.SetStorageBuffers(ranges);
_context.Renderer.Pipeline.SetStorageBuffers(first, ranges.Slice(0, count));
}
else
{
_context.Renderer.Pipeline.SetUniformBuffers(ranges);
_context.Renderer.Pipeline.SetUniformBuffers(first, ranges.Slice(0, count));
}
}

View File

@ -38,7 +38,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 = 2530;
private const ulong ShaderCodeGenVersion = 2494;
// Progress reporting helpers
private volatile int _shaderCount;

View File

@ -5,6 +5,8 @@ using Ryujinx.Graphics.OpenGL.Image;
using Ryujinx.Graphics.OpenGL.Queries;
using Ryujinx.Graphics.Shader;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.OpenGL
{
@ -31,9 +33,16 @@ namespace Ryujinx.Graphics.OpenGL
private int _boundDrawFramebuffer;
private int _boundReadFramebuffer;
private int[] _fpIsBgra = new int[8];
private float[] _fpRenderScale = new float[65];
private float[] _cpRenderScale = new float[64];
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 TextureBase _unit0Texture;
@ -48,6 +57,7 @@ namespace Ryujinx.Graphics.OpenGL
private bool _tfEnabled;
private TransformFeedbackPrimitiveType _tfTopology;
private BufferHandle _supportBuffer;
private readonly BufferHandle[] _tfbs;
private readonly BufferRange[] _tfbTargets;
@ -66,15 +76,8 @@ namespace Ryujinx.Graphics.OpenGL
_componentMasks[index] = 0xf;
}
for (int index = 0; index < _fpRenderScale.Length; index++)
{
_fpRenderScale[index] = 1f;
}
for (int index = 0; index < _cpRenderScale.Length; index++)
{
_cpRenderScale[index] = 1f;
}
var v4Zero = new Vector4<float> { X = 0f, Y = 0f, Z = 0f, W = 0f };
new Span<Vector4<float>>(_renderScale).Fill(v4Zero);
_tfbs = new BufferHandle[Constants.MaxTransformFeedbackBuffers];
_tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers];
@ -823,9 +826,6 @@ namespace Ryujinx.Graphics.OpenGL
{
_program.Bind();
}
UpdateFpIsBgra();
SetRenderTargetScale(_fpRenderScale[0]);
}
public void SetRasterizerDiscard(bool discard)
@ -844,12 +844,8 @@ namespace Ryujinx.Graphics.OpenGL
public void SetRenderTargetScale(float scale)
{
_fpRenderScale[0] = scale;
if (_program != null && _program.FragmentRenderScaleUniform != -1)
{
GL.Uniform1(_program.FragmentRenderScaleUniform, 1, _fpRenderScale); // Just the first element.
}
_renderScale[0].X = scale;
SetSupportBufferData<Vector4<float>>(SupportBuffer.FragmentRenderScaleOffset, _renderScale, 1); // Just the first element.
}
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks)
@ -866,6 +862,8 @@ namespace Ryujinx.Graphics.OpenGL
{
EnsureFramebuffer();
bool isBgraChanged = false;
for (int index = 0; index < colors.Length; index++)
{
TextureView color = (TextureView)colors[index];
@ -874,15 +872,19 @@ namespace Ryujinx.Graphics.OpenGL
int isBgra = color != null && color.Format.IsBgra8() ? 1 : 0;
if (_fpIsBgra[index] != isBgra)
if (_fpIsBgra[index].X != isBgra)
{
_fpIsBgra[index] = isBgra;
_fpIsBgra[index].X = isBgra;
isBgraChanged = true;
RestoreComponentMask(index);
}
}
UpdateFpIsBgra();
if (isBgraChanged)
{
SetSupportBufferData<Vector4<int>>(SupportBuffer.FragmentIsBgraOffset, _fpIsBgra, SupportBuffer.FragmentIsBgraCount);
}
TextureView depthStencilView = (TextureView)depthStencil;
@ -965,9 +967,9 @@ namespace Ryujinx.Graphics.OpenGL
_stencilFrontMask = stencilTest.FrontMask;
}
public void SetStorageBuffers(ReadOnlySpan<BufferRange> buffers)
public void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers)
{
SetBuffers(buffers, isStorage: true);
SetBuffers(first, buffers, isStorage: true);
}
public void SetTexture(int binding, ITexture texture)
@ -1023,9 +1025,9 @@ namespace Ryujinx.Graphics.OpenGL
}
}
public void SetUniformBuffers(ReadOnlySpan<BufferRange> buffers)
public void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers)
{
SetBuffers(buffers, isStorage: false);
SetBuffers(first, buffers, isStorage: false);
}
public void SetUserClipDistance(int index, bool enableClip)
@ -1103,7 +1105,7 @@ namespace Ryujinx.Graphics.OpenGL
GL.MemoryBarrier(MemoryBarrierFlags.TextureFetchBarrierBit);
}
private void SetBuffers(ReadOnlySpan<BufferRange> buffers, bool isStorage)
private void SetBuffers(int first, ReadOnlySpan<BufferRange> buffers, bool isStorage)
{
BufferRangeTarget target = isStorage ? BufferRangeTarget.ShaderStorageBuffer : BufferRangeTarget.UniformBuffer;
@ -1113,11 +1115,11 @@ namespace Ryujinx.Graphics.OpenGL
if (buffer.Handle == BufferHandle.Null)
{
GL.BindBufferRange(target, index, 0, IntPtr.Zero, 0);
GL.BindBufferRange(target, first + index, 0, IntPtr.Zero, 0);
continue;
}
GL.BindBufferRange(target, index, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size);
GL.BindBufferRange(target, first + index, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size);
}
}
@ -1179,37 +1181,39 @@ namespace Ryujinx.Graphics.OpenGL
return (_boundDrawFramebuffer, _boundReadFramebuffer);
}
private void UpdateFpIsBgra()
public void UpdateRenderScale(ShaderStage stage, float[] scales, int textureCount, int imageCount)
{
if (_program != null)
if (stage != ShaderStage.Compute && stage != ShaderStage.Fragment)
{
GL.Uniform1(_program.FragmentIsBgraUniform, 8, _fpIsBgra);
return;
}
bool changed = false;
for (int index = 0; index < textureCount + imageCount; index++)
{
if (_renderScale[1 + index].X != scales[index])
{
_renderScale[1 + index].X = scales[index];
changed = true;
}
}
if (changed)
{
SetSupportBufferData<Vector4<float>>(SupportBuffer.FragmentRenderScaleOffset, _renderScale, 1 + textureCount + imageCount);
}
}
public void UpdateRenderScale(ShaderStage stage, float[] scales, int textureCount, int imageCount)
private void SetSupportBufferData<T>(int offset, ReadOnlySpan<T> data, int count) where T : unmanaged
{
if (_program != null)
if (_supportBuffer == BufferHandle.Null)
{
switch (stage)
{
case ShaderStage.Fragment:
if (_program.FragmentRenderScaleUniform != -1)
{
Array.Copy(scales, 0, _fpRenderScale, 1, textureCount + imageCount);
GL.Uniform1(_program.FragmentRenderScaleUniform, 1 + textureCount + imageCount, _fpRenderScale);
}
break;
case ShaderStage.Compute:
if (_program.ComputeRenderScaleUniform != -1)
{
Array.Copy(scales, 0, _cpRenderScale, 0, textureCount + imageCount);
GL.Uniform1(_program.ComputeRenderScaleUniform, textureCount + imageCount, _cpRenderScale);
}
break;
}
_supportBuffer = Buffer.Create(SupportBuffer.RequiredSize);
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, Unsafe.As<BufferHandle, int>(ref _supportBuffer));
}
Buffer.SetData(_supportBuffer, offset, MemoryMarshal.Cast<T, byte>(data.Slice(0, count)));
}
private void PrepareForDispatch()
@ -1249,8 +1253,8 @@ namespace Ryujinx.Graphics.OpenGL
public void RestoreComponentMask(int index)
{
// If the bound render target is bgra, swap the red and blue masks.
uint redMask = _fpIsBgra[index] == 0 ? 1u : 4u;
uint blueMask = _fpIsBgra[index] == 0 ? 4u : 1u;
uint redMask = _fpIsBgra[index].X == 0 ? 1u : 4u;
uint blueMask = _fpIsBgra[index].X == 0 ? 4u : 1u;
GL.ColorMask(
index,
@ -1322,6 +1326,12 @@ namespace Ryujinx.Graphics.OpenGL
public void Dispose()
{
if (_supportBuffer != BufferHandle.Null)
{
Buffer.Delete(_supportBuffer);
_supportBuffer = BufferHandle.Null;
}
for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++)
{
if (_tfbs[i] != BufferHandle.Null)

View File

@ -13,10 +13,6 @@ namespace Ryujinx.Graphics.OpenGL
{
public int Handle { get; private set; }
public int FragmentIsBgraUniform { get; private set; }
public int FragmentRenderScaleUniform { get; private set; }
public int ComputeRenderScaleUniform { get; private set; }
public bool IsLinked
{
get
@ -30,7 +26,6 @@ namespace Ryujinx.Graphics.OpenGL
}
}
private bool _initialized;
private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete;
private IShader[] _shaders;
@ -117,15 +112,6 @@ namespace Ryujinx.Graphics.OpenGL
public void Bind()
{
if (!_initialized)
{
FragmentIsBgraUniform = GL.GetUniformLocation(Handle, "is_bgra");
FragmentRenderScaleUniform = GL.GetUniformLocation(Handle, "fp_renderScale");
ComputeRenderScaleUniform = GL.GetUniformLocation(Handle, "cp_renderScale");
_initialized = true;
}
GL.UseProgram(Handle);
}

View File

@ -1,9 +1,7 @@
using Ryujinx.Common;
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
@ -159,23 +157,38 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine();
}
if (context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute)
{
if (context.Config.Stage == ShaderStage.Fragment)
{
if (context.Config.GpuAccessor.QueryEarlyZForce())
{
context.AppendLine("layout(early_fragment_tests) in;");
context.AppendLine();
}
bool isFragment = context.Config.Stage == ShaderStage.Fragment;
context.AppendLine($"uniform bool {DefaultNames.IsBgraName}[8];");
if (isFragment || context.Config.Stage == ShaderStage.Compute)
{
if (isFragment && context.Config.GpuAccessor.QueryEarlyZForce())
{
context.AppendLine("layout(early_fragment_tests) in;");
context.AppendLine();
}
if (DeclareRenderScale(context))
if ((context.Config.UsedFeatures & (FeatureFlags.FragCoordXY | FeatureFlags.IntegerSampling)) != 0)
{
context.AppendLine();
string stage = OperandManager.GetShaderStagePrefix(context.Config.Stage);
int scaleElements = context.Config.GetTextureDescriptors().Length + context.Config.GetImageDescriptors().Length;
if (isFragment)
{
scaleElements++; // Also includes render target scale, for gl_FragCoord.
}
DeclareSupportUniformBlock(context, isFragment, scaleElements);
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling))
{
AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl");
context.AppendLine();
}
}
else if (isFragment)
{
DeclareSupportUniformBlock(context, true, 0);
}
}
@ -498,31 +511,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}
}
private static bool DeclareRenderScale(CodeGenContext context)
private static void DeclareSupportUniformBlock(CodeGenContext context, bool isFragment, int scaleElements)
{
if ((context.Config.UsedFeatures & (FeatureFlags.FragCoordXY | FeatureFlags.IntegerSampling)) != 0)
if (!isFragment && scaleElements == 0)
{
string stage = OperandManager.GetShaderStagePrefix(context.Config.Stage);
int scaleElements = context.Config.GetTextureDescriptors().Length + context.Config.GetImageDescriptors().Length;
if (context.Config.Stage == ShaderStage.Fragment)
{
scaleElements++; // Also includes render target scale, for gl_FragCoord.
}
context.AppendLine($"uniform float {stage}_renderScale[{scaleElements}];");
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling))
{
context.AppendLine();
AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl");
}
return true;
return;
}
return false;
context.AppendLine($"layout (binding = 0, std140) uniform {DefaultNames.SupportBlockName}");
context.EnterScope();
if (isFragment)
{
context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};");
context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];");
}
else
{
context.AppendLine($"uint s_reserved[{SupportBuffer.ComputeRenderScaleOffset / SupportBuffer.FieldSize}];");
}
if (scaleElements != 0)
{
context.AppendLine($"float {DefaultNames.SupportBlockRenderScaleName}[{scaleElements}];");
}
context.LeaveScope(";");
context.AppendLine();
}
private static void AppendHelperFunction(CodeGenContext context, string filename)

View File

@ -14,6 +14,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public const string DataName = "data";
public const string SupportBlockName = "support_block";
public const string SupportBlockAlphaTestName = "s_alpha_test";
public const string SupportBlockIsBgraName = "s_is_bgra";
public const string SupportBlockRenderScaleName = "s_render_scale";
public const string BlockSuffix = "block";
public const string UniformNamePrefix = "c";
@ -25,7 +30,5 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public const string ArgumentNamePrefix = "a";
public const string UndefinedName = "undef";
public const string IsBgraName = "is_bgra";
}
}

View File

@ -1,6 +1,6 @@
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
{
float scale = cp_renderScale[samplerIndex];
float scale = s_render_scale[samplerIndex];
if (scale == 1.0)
{
return inputVec;
@ -10,7 +10,7 @@
int Helper_TextureSizeUnscale(int size, int samplerIndex)
{
float scale = cp_renderScale[samplerIndex];
float scale = s_render_scale[samplerIndex];
if (scale == 1.0)
{
return size;

View File

@ -1,6 +1,6 @@
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
{
float scale = fp_renderScale[1 + samplerIndex];
float scale = s_render_scale[1 + samplerIndex];
if (scale == 1.0)
{
return inputVec;
@ -17,7 +17,7 @@
int Helper_TextureSizeUnscale(int size, int samplerIndex)
{
float scale = abs(fp_renderScale[1 + samplerIndex]);
float scale = abs(s_render_scale[1 + samplerIndex]);
if (scale == 1.0)
{
return size;

View File

@ -68,14 +68,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ AttributeConsts.LtMask, new BuiltInAttribute("unpackUint2x32(gl_SubGroupLtMaskARB).x", VariableType.U32) },
// Support uniforms.
{ AttributeConsts.FragmentOutputIsBgraBase + 0, new BuiltInAttribute($"{DefaultNames.IsBgraName}[0]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 4, new BuiltInAttribute($"{DefaultNames.IsBgraName}[1]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 8, new BuiltInAttribute($"{DefaultNames.IsBgraName}[2]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.IsBgraName}[3]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.IsBgraName}[4]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.IsBgraName}[5]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.IsBgraName}[6]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.IsBgraName}[7]", VariableType.Bool) }
{ AttributeConsts.FragmentOutputIsBgraBase + 0, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[0]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 4, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[1]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 8, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[2]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[3]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", VariableType.Bool) }
};
private Dictionary<AstOperand, string> _locals;
@ -194,8 +194,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
switch (value & ~3)
{
case AttributeConsts.PositionX: return "(gl_FragCoord.x / fp_renderScale[0])";
case AttributeConsts.PositionY: return "(gl_FragCoord.y / fp_renderScale[0])";
case AttributeConsts.PositionX: return $"(gl_FragCoord.x / {DefaultNames.SupportBlockRenderScaleName}[0])";
case AttributeConsts.PositionY: return $"(gl_FragCoord.y / {DefaultNames.SupportBlockRenderScaleName}[0])";
case AttributeConsts.PositionZ: return "gl_FragCoord.z";
case AttributeConsts.PositionW: return "gl_FragCoord.w";
}

View File

@ -0,0 +1,18 @@
namespace Ryujinx.Graphics.Shader
{
public static class SupportBuffer
{
public const int FieldSize = 16; // Each field takes 16 bytes on default layout, even bool.
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.
private const int RenderScaleMaxCount = 1 + 32 + 8;
public const int RequiredSize = FragmentRenderScaleOffset + RenderScaleMaxCount * FieldSize;
}
}

View File

@ -7,6 +7,12 @@ namespace Ryujinx.Graphics.Shader.Translation
public int TexturesCount { get; private set; }
public int ImagesCount { get; private set; }
public TranslationCounts()
{
// The first binding is reserved for the support buffer.
UniformBuffersCount = 1;
}
internal int IncrementUniformBuffersCount()
{
return UniformBuffersCount++;