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 SetStencilTest(StencilTestDescriptor stencilTest);
void SetStorageBuffers(ReadOnlySpan<BufferRange> buffers); void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers);
void SetTexture(int binding, ITexture texture); void SetTexture(int binding, ITexture texture);
void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers); void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers);
void SetUniformBuffers(ReadOnlySpan<BufferRange> buffers); void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers);
void SetUserClipDistance(int index, bool enableClip); void SetUserClipDistance(int index, bool enableClip);

View file

@ -957,9 +957,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
UpdateUserClipState(); UpdateUserClipState();
} }
int storageBufferBindingsCount = 0;
int uniformBufferBindingsCount = 0;
for (int stage = 0; stage < Constants.ShaderStages; stage++) for (int stage = 0; stage < Constants.ShaderStages; stage++)
{ {
ShaderProgramInfo info = gs.Shaders[stage]?.Info; ShaderProgramInfo info = gs.Shaders[stage]?.Info;
@ -1015,21 +1012,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers); _channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers);
_channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers); _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); _context.Renderer.Pipeline.SetProgram(gs.HostProgram);
} }

View file

@ -5,7 +5,7 @@ using Ryujinx.Graphics.Shader;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Gpu.Memory namespace Ryujinx.Graphics.Gpu.Memory
{ {
@ -14,8 +14,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
class BufferManager class BufferManager
{ {
private const int StackToHeapThreshold = 16;
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly GpuChannel _channel; private readonly GpuChannel _channel;
@ -23,6 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
private readonly VertexBuffer[] _vertexBuffers; private readonly VertexBuffer[] _vertexBuffers;
private readonly BufferBounds[] _transformFeedbackBuffers; private readonly BufferBounds[] _transformFeedbackBuffers;
private readonly List<BufferTextureBinding> _bufferTextures; private readonly List<BufferTextureBinding> _bufferTextures;
private readonly BufferRange[] _ranges;
/// <summary> /// <summary>
/// Holds shader stage buffer state and binding information. /// Holds shader stage buffer state and binding information.
@ -88,11 +87,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
private readonly BuffersPerStage[] _gpStorageBuffers; private readonly BuffersPerStage[] _gpStorageBuffers;
private readonly BuffersPerStage[] _gpUniformBuffers; private readonly BuffersPerStage[] _gpUniformBuffers;
private int _cpStorageBufferBindings;
private int _cpUniformBufferBindings;
private int _gpStorageBufferBindings;
private int _gpUniformBufferBindings;
private bool _gpStorageBuffersDirty; private bool _gpStorageBuffersDirty;
private bool _gpUniformBuffersDirty; private bool _gpUniformBuffersDirty;
@ -130,6 +124,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
_bufferTextures = new List<BufferTextureBinding>(); _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) public void SetComputeStorageBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
{ {
_cpStorageBuffers.SetBindings(descriptors); _cpStorageBuffers.SetBindings(descriptors);
_cpStorageBufferBindings = descriptors.Count != 0 ? descriptors.Max(x => x.Binding) + 1 : 0;
} }
/// <summary> /// <summary>
@ -302,15 +297,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
_gpStorageBuffersDirty = true; _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> /// <summary>
/// Sets the binding points for the uniform buffers bound on the compute pipeline. /// Sets the binding points for the uniform buffers bound on the compute pipeline.
/// </summary> /// </summary>
@ -318,7 +304,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
public void SetComputeUniformBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors) public void SetComputeUniformBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
{ {
_cpUniformBuffers.SetBindings(descriptors); _cpUniformBuffers.SetBindings(descriptors);
_cpUniformBufferBindings = descriptors.Count != 0 ? descriptors.Max(x => x.Binding) + 1 : 0;
} }
/// <summary> /// <summary>
@ -333,15 +318,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
_gpUniformBuffersDirty = true; _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> /// <summary>
/// Gets a bit mask indicating which compute uniform buffers are currently bound. /// Gets a bit mask indicating which compute uniform buffers are currently bound.
/// </summary> /// </summary>
@ -381,7 +357,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
return mask; return mask;
} }
/// <summary> /// <summary>
/// Gets the address of the compute uniform buffer currently bound at the given index. /// Gets the address of the compute uniform buffer currently bound at the given index.
/// </summary> /// </summary>
@ -409,46 +384,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
public void CommitComputeBindings() public void CommitComputeBindings()
{ {
int sCount = _cpStorageBufferBindings; var bufferCache = _channel.MemoryManager.Physical.BufferCache;
Span<BufferRange> sRanges = sCount < StackToHeapThreshold ? stackalloc BufferRange[sCount] : new BufferRange[sCount]; BindBuffers(bufferCache, _cpStorageBuffers, isStorage: true);
BindBuffers(bufferCache, _cpUniformBuffers, isStorage: false);
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);
CommitBufferTextureBindings(); CommitBufferTextureBindings();
@ -491,20 +430,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
public void CommitGraphicsBindings() public void CommitGraphicsBindings()
{ {
var bufferCache = _channel.MemoryManager.Physical.BufferCache;
if (_indexBufferDirty || _rebind) if (_indexBufferDirty || _rebind)
{ {
_indexBufferDirty = false; _indexBufferDirty = false;
if (_indexBuffer.Address != 0) 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); _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
} }
} }
else if (_indexBuffer.Address != 0) else if (_indexBuffer.Address != 0)
{ {
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size); bufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
} }
uint vbEnableMask = _vertexBuffersEnableMask; uint vbEnableMask = _vertexBuffersEnableMask;
@ -524,7 +465,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue; 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); vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
} }
@ -542,7 +483,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue; 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; 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); _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
@ -578,7 +519,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue; 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; _gpStorageBuffersDirty = false;
BindBuffers(_gpStorageBuffers, isStorage: true); BindBuffers(bufferCache, _gpStorageBuffers, isStorage: true);
} }
else else
{ {
@ -597,7 +538,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
_gpUniformBuffersDirty = false; _gpUniformBuffersDirty = false;
BindBuffers(_gpUniformBuffers, isStorage: false); BindBuffers(bufferCache, _gpUniformBuffers, isStorage: false);
} }
else else
{ {
@ -612,13 +553,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary> /// <summary>
/// Bind respective buffer bindings on the host API. /// Bind respective buffer bindings on the host API.
/// </summary> /// </summary>
/// <param name="bindings">Bindings to bind</param> /// <param name="bufferCache">Buffer cache holding the buffers for the specified ranges</param>
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffers</param> /// <param name="bindings">Buffer memory ranges to bind</param>
private void BindBuffers(BuffersPerStage[] bindings, bool isStorage) /// <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++) for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
{ {
@ -633,20 +577,97 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (bounds.Address != 0) if (bounds.Address != 0)
{ {
var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write); var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
ranges[bindingInfo.Binding] = isStorage var range = isStorage
? _channel.MemoryManager.Physical.BufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite) ? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
: _channel.MemoryManager.Physical.BufferCache.GetBufferRange(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) if (isStorage)
{ {
_context.Renderer.Pipeline.SetStorageBuffers(ranges); _context.Renderer.Pipeline.SetStorageBuffers(first, ranges.Slice(0, count));
} }
else 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> /// <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 = 2530; private const ulong ShaderCodeGenVersion = 2494;
// Progress reporting helpers // Progress reporting helpers
private volatile int _shaderCount; private volatile int _shaderCount;

View file

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

View file

@ -13,10 +13,6 @@ namespace Ryujinx.Graphics.OpenGL
{ {
public int Handle { get; private set; } 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 public bool IsLinked
{ {
get get
@ -30,7 +26,6 @@ namespace Ryujinx.Graphics.OpenGL
} }
} }
private bool _initialized;
private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete; private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete;
private IShader[] _shaders; private IShader[] _shaders;
@ -117,15 +112,6 @@ namespace Ryujinx.Graphics.OpenGL
public void Bind() 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); GL.UseProgram(Handle);
} }

View file

@ -1,9 +1,7 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation; using Ryujinx.Graphics.Shader.Translation;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
@ -159,25 +157,40 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine(); context.AppendLine();
} }
if (context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) bool isFragment = context.Config.Stage == ShaderStage.Fragment;
if (isFragment || context.Config.Stage == ShaderStage.Compute)
{ {
if (context.Config.Stage == ShaderStage.Fragment) if (isFragment && context.Config.GpuAccessor.QueryEarlyZForce())
{
if (context.Config.GpuAccessor.QueryEarlyZForce())
{ {
context.AppendLine("layout(early_fragment_tests) in;"); context.AppendLine("layout(early_fragment_tests) in;");
context.AppendLine(); context.AppendLine();
} }
context.AppendLine($"uniform bool {DefaultNames.IsBgraName}[8];"); 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.
} }
if (DeclareRenderScale(context)) DeclareSupportUniformBlock(context, isFragment, scaleElements);
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling))
{ {
AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl");
context.AppendLine(); context.AppendLine();
} }
} }
else if (isFragment)
{
DeclareSupportUniformBlock(context, true, 0);
}
}
if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 0) if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 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); return;
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}];"); context.AppendLine($"layout (binding = 0, std140) uniform {DefaultNames.SupportBlockName}");
context.EnterScope();
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling)) 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(); context.AppendLine();
AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl");
}
return true;
}
return false;
} }
private static void AppendHelperFunction(CodeGenContext context, string filename) 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 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 BlockSuffix = "block";
public const string UniformNamePrefix = "c"; public const string UniformNamePrefix = "c";
@ -25,7 +30,5 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public const string ArgumentNamePrefix = "a"; public const string ArgumentNamePrefix = "a";
public const string UndefinedName = "undef"; public const string UndefinedName = "undef";
public const string IsBgraName = "is_bgra";
} }
} }

View file

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

View file

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

View file

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