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:
parent
93d78f9ac4
commit
f09bba82b9
65 changed files with 3912 additions and 593 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
123
src/Ryujinx.Graphics.Gpu/Memory/BufferUpdater.cs
Normal file
123
src/Ryujinx.Graphics.Gpu/Memory/BufferUpdater.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue