Geometry shader emulation for macOS (#5551)

* Implement vertex and geometry shader conversion to compute

* Call InitializeReservedCounts for compute too

* PR feedback

* Set clip distance mask for geometry and tessellation shaders too

* Transform feedback emulation only for vertex
This commit is contained in:
gdkchan 2023-08-29 21:10:34 -03:00 committed by GitHub
parent 93d78f9ac4
commit f09bba82b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
65 changed files with 3912 additions and 593 deletions

View file

@ -6,7 +6,6 @@ using Ryujinx.Graphics.Shader;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Memory
{
@ -15,9 +14,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
class BufferManager
{
private const int TfInfoVertexCountOffset = Constants.TotalTransformFeedbackBuffers * sizeof(int);
private const int TfInfoBufferSize = TfInfoVertexCountOffset + sizeof(int);
private readonly GpuContext _context;
private readonly GpuChannel _channel;
@ -104,9 +100,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
private readonly BuffersPerStage[] _gpStorageBuffers;
private readonly BuffersPerStage[] _gpUniformBuffers;
private BufferHandle _tfInfoBuffer;
private readonly int[] _tfInfoData;
private bool _gpStorageBuffersDirty;
private bool _gpUniformBuffersDirty;
@ -146,11 +139,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
_bufferTextures = new List<BufferTextureBinding>();
_ranges = new BufferAssignment[Constants.TotalGpUniformBuffers * Constants.ShaderStages];
if (!context.Capabilities.SupportsTransformFeedback)
{
_tfInfoData = new int[Constants.TotalTransformFeedbackBuffers];
}
}
@ -339,13 +327,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="vertexCount">Vertex count per instance</param>
public void SetInstancedDrawVertexCount(int vertexCount)
{
if (!_context.Capabilities.SupportsTransformFeedback &&
HasTransformFeedbackOutputs &&
_tfInfoBuffer != BufferHandle.Null)
if (!_context.Capabilities.SupportsTransformFeedback && HasTransformFeedbackOutputs)
{
Span<byte> data = stackalloc byte[sizeof(int)];
MemoryMarshal.Cast<byte, int>(data)[0] = vertexCount;
_context.Renderer.SetBufferData(_tfInfoBuffer, TfInfoVertexCountOffset, data);
_context.SupportBufferUpdater.SetTfeVertexCount(vertexCount);
_context.SupportBufferUpdater.Commit();
}
}
@ -607,17 +592,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
else if (HasTransformFeedbackOutputs)
{
Span<int> info = _tfInfoData.AsSpan();
Span<BufferAssignment> buffers = stackalloc BufferAssignment[Constants.TotalTransformFeedbackBuffers + 1];
bool needsDataUpdate = false;
if (_tfInfoBuffer == BufferHandle.Null)
{
_tfInfoBuffer = _context.Renderer.CreateBuffer(TfInfoBufferSize, BufferAccess.Stream);
}
buffers[0] = new BufferAssignment(0, new BufferRange(_tfInfoBuffer, 0, TfInfoBufferSize));
Span<BufferAssignment> buffers = stackalloc BufferAssignment[Constants.TotalTransformFeedbackBuffers];
int alignment = _context.Capabilities.StorageBufferOffsetAlignment;
@ -627,7 +602,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (tfb.Address == 0)
{
buffers[1 + index] = new BufferAssignment(1 + index, BufferRange.Empty);
buffers[index] = new BufferAssignment(index, BufferRange.Empty);
}
else
{
@ -637,22 +612,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
int tfeOffset = ((int)tfb.Address & (alignment - 1)) / 4;
if (info[index] != tfeOffset)
{
info[index] = tfeOffset;
needsDataUpdate = true;
}
_context.SupportBufferUpdater.SetTfeOffset(index, tfeOffset);
buffers[1 + index] = new BufferAssignment(1 + index, bufferCache.GetBufferRange(address, size, write: true));
buffers[index] = new BufferAssignment(index, bufferCache.GetBufferRange(address, size, write: true));
}
}
if (needsDataUpdate)
{
Span<byte> infoData = MemoryMarshal.Cast<int, byte>(info);
_context.Renderer.SetBufferData(_tfInfoBuffer, 0, infoData);
}
_context.Renderer.Pipeline.SetStorageBuffers(buffers);
}
}

View file

@ -0,0 +1,123 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using System;
namespace Ryujinx.Graphics.Gpu.Memory
{
/// <summary>
/// Buffer data updater.
/// </summary>
class BufferUpdater : IDisposable
{
private BufferHandle _handle;
/// <summary>
/// Handle of the buffer.
/// </summary>
public BufferHandle Handle => _handle;
private readonly IRenderer _renderer;
private int _startOffset = -1;
private int _endOffset = -1;
/// <summary>
/// Creates a new instance of the buffer updater.
/// </summary>
/// <param name="renderer">Renderer that the buffer will be used with</param>
public BufferUpdater(IRenderer renderer)
{
_renderer = renderer;
}
/// <summary>
/// Mark a region of the buffer as modified and needing to be sent to the GPU.
/// </summary>
/// <param name="startOffset">Start offset of the region in bytes</param>
/// <param name="byteSize">Size of the region in bytes</param>
protected 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;
}
}
}
/// <summary>
/// Submits all pending buffer updates to the GPU.
/// </summary>
/// <param name="data">All data that should be sent to the GPU. Only the modified regions will be updated</param>
/// <param name="binding">Optional binding to bind the buffer if a new buffer was created</param>
protected void Commit(ReadOnlySpan<byte> data, int binding = -1)
{
if (_startOffset != -1)
{
if (_handle == BufferHandle.Null)
{
_handle = _renderer.CreateBuffer(data.Length, BufferAccess.Stream);
_renderer.Pipeline.ClearBuffer(_handle, 0, data.Length, 0);
if (binding >= 0)
{
var range = new BufferRange(_handle, 0, data.Length);
_renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, range) });
}
};
_renderer.SetBufferData(_handle, _startOffset, data[_startOffset.._endOffset]);
_startOffset = -1;
_endOffset = -1;
}
}
/// <summary>
/// Gets a reference to a given element of a vector.
/// </summary>
/// <param name="vector">Vector to get the element reference from</param>
/// <param name="elementIndex">Element index</param>
/// <returns>Reference to the specified element</returns>
protected static ref T GetElementRef<T>(ref Vector4<T> vector, int elementIndex)
{
switch (elementIndex)
{
case 0:
return ref vector.X;
case 1:
return ref vector.Y;
case 2:
return ref vector.Z;
case 3:
return ref vector.W;
default:
throw new ArgumentOutOfRangeException(nameof(elementIndex));
}
}
/// <summary>
/// Destroys the buffer.
/// </summary>
public void Dispose()
{
if (_handle != BufferHandle.Null)
{
_renderer.DeleteBuffer(_handle);
_handle = BufferHandle.Null;
}
}
}
}

View file

@ -9,56 +9,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Support buffer data updater.
/// </summary>
class SupportBufferUpdater : IDisposable
class SupportBufferUpdater : BufferUpdater
{
private SupportBuffer _data;
private BufferHandle _handle;
private readonly IRenderer _renderer;
private int _startOffset = -1;
private int _endOffset = -1;
/// <summary>
/// Creates a new instance of the support buffer updater.
/// </summary>
/// <param name="renderer">Renderer that the support buffer will be used with</param>
public SupportBufferUpdater(IRenderer renderer)
public SupportBufferUpdater(IRenderer renderer) : base(renderer)
{
_renderer = renderer;
var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f };
_data.RenderScale.AsSpan().Fill(defaultScale);
DirtyRenderScale(0, SupportBuffer.RenderScaleMaxCount);
}
/// <summary>
/// Mark a region of the support buffer as modified and needing to be sent to the GPU.
/// </summary>
/// <param name="startOffset">Start offset of the region in bytes</param>
/// <param name="byteSize">Size of the region in bytes</param>
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;
}
}
}
/// <summary>
/// Marks the fragment render scale count as being modified.
/// </summary>
@ -220,40 +185,40 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
/// <summary>
/// Submits all pending buffer updates to the GPU.
/// Sets offset for the misaligned portion of a transform feedback buffer, and the buffer size, for transform feedback emulation.
/// </summary>
public void Commit()
/// <param name="bufferIndex">Index of the transform feedback buffer</param>
/// <param name="offset">Misaligned offset of the buffer</param>
public void SetTfeOffset(int bufferIndex, int offset)
{
if (_startOffset != -1)
ref int currentOffset = ref GetElementRef(ref _data.TfeOffset, bufferIndex);
if (currentOffset != offset)
{
if (_handle == BufferHandle.Null)
{
_handle = _renderer.CreateBuffer(SupportBuffer.RequiredSize, BufferAccess.Stream);
_renderer.Pipeline.ClearBuffer(_handle, 0, SupportBuffer.RequiredSize, 0);
var range = new BufferRange(_handle, 0, SupportBuffer.RequiredSize);
_renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, range) });
}
ReadOnlySpan<byte> data = MemoryMarshal.Cast<SupportBuffer, byte>(MemoryMarshal.CreateSpan(ref _data, 1));
_renderer.SetBufferData(_handle, _startOffset, data[_startOffset.._endOffset]);
_startOffset = -1;
_endOffset = -1;
currentOffset = offset;
MarkDirty(SupportBuffer.TfeOffsetOffset + bufferIndex * sizeof(int), sizeof(int));
}
}
/// <summary>
/// Destroys the support buffer.
/// Sets the vertex count used for transform feedback emulation with instanced draws.
/// </summary>
public void Dispose()
/// <param name="vertexCount">Vertex count of the instanced draw</param>
public void SetTfeVertexCount(int vertexCount)
{
if (_handle != BufferHandle.Null)
if (_data.TfeVertexCount.X != vertexCount)
{
_renderer.DeleteBuffer(_handle);
_handle = BufferHandle.Null;
_data.TfeVertexCount.X = vertexCount;
MarkDirty(SupportBuffer.TfeVertexCountOffset, sizeof(int));
}
}
/// <summary>
/// Submits all pending buffer updates to the GPU.
/// </summary>
public void Commit()
{
Commit(MemoryMarshal.Cast<SupportBuffer, byte>(MemoryMarshal.CreateSpan(ref _data, 1)), SupportBuffer.Binding);
}
}
}