Initial transform feedback support (#1370)
* Initial transform feedback support * Some nits and fixes * Update ReportCounterType and Write method * Can't change shader or TFB bindings while TFB is active * Fix geometry shader input names with new naming
This commit is contained in:
parent
16dafe6316
commit
788ca6a411
23 changed files with 468 additions and 68 deletions
|
@ -7,6 +7,8 @@ namespace Ryujinx.Graphics.GAL
|
|||
{
|
||||
void Barrier();
|
||||
|
||||
void BeginTransformFeedback(PrimitiveTopology topology);
|
||||
|
||||
void ClearRenderTargetColor(int index, uint componentMask, ColorF color);
|
||||
|
||||
void ClearRenderTargetDepthStencil(
|
||||
|
@ -27,6 +29,8 @@ namespace Ryujinx.Graphics.GAL
|
|||
int firstVertex,
|
||||
int firstInstance);
|
||||
|
||||
void EndTransformFeedback();
|
||||
|
||||
void SetBlendState(int index, BlendDescriptor blend);
|
||||
|
||||
void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp);
|
||||
|
@ -73,6 +77,7 @@ namespace Ryujinx.Graphics.GAL
|
|||
|
||||
void SetTexture(int index, ShaderStage stage, ITexture texture);
|
||||
|
||||
void SetTransformFeedbackBuffer(int index, BufferRange buffer);
|
||||
void SetUniformBuffer(int index, ShaderStage stage, BufferRange buffer);
|
||||
|
||||
void SetUserClipDistance(int index, bool enableClip);
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.GAL
|
|||
|
||||
BufferHandle CreateBuffer(int size);
|
||||
|
||||
IProgram CreateProgram(IShader[] shaders);
|
||||
IProgram CreateProgram(IShader[] shaders, TransformFeedbackDescriptor[] transformFeedbackDescriptors);
|
||||
|
||||
ISampler CreateSampler(SamplerCreateInfo info);
|
||||
ITexture CreateTexture(TextureCreateInfo info, float scale);
|
||||
|
|
19
Ryujinx.Graphics.GAL/TransformFeedbackDescriptor.cs
Normal file
19
Ryujinx.Graphics.GAL/TransformFeedbackDescriptor.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public struct TransformFeedbackDescriptor
|
||||
{
|
||||
public int BufferIndex { get; }
|
||||
public int Stride { get; }
|
||||
|
||||
public byte[] VaryingLocations { get; }
|
||||
|
||||
public TransformFeedbackDescriptor(int bufferIndex, int stride, byte[] varyingLocations)
|
||||
{
|
||||
BufferIndex = bufferIndex;
|
||||
Stride = stride;
|
||||
VaryingLocations = varyingLocations ?? throw new ArgumentNullException(nameof(varyingLocations));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,6 +35,11 @@ namespace Ryujinx.Graphics.Gpu
|
|||
/// </remarks>
|
||||
public const int TotalGpStorageBuffers = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of transform feedback buffers.
|
||||
/// </summary>
|
||||
public const int TotalTransformFeedbackBuffers = 4;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of render target color buffers.
|
||||
/// </summary>
|
||||
|
|
|
@ -61,8 +61,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||
/// <param name="type">Counter to be written to memory</param>
|
||||
private void ReportCounter(GpuState state, ReportCounterType type)
|
||||
{
|
||||
CounterData counterData = new CounterData();
|
||||
|
||||
var rs = state.Get<SemaphoreState>(MethodOffset.ReportState);
|
||||
|
||||
ulong gpuVa = rs.Address.Pack();
|
||||
|
@ -80,16 +78,14 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||
|
||||
EventHandler<ulong> resultHandler = (object evt, ulong result) =>
|
||||
{
|
||||
CounterData counterData = new CounterData();
|
||||
|
||||
counterData.Counter = result;
|
||||
counterData.Timestamp = ticks;
|
||||
|
||||
Span<CounterData> counterDataSpan = MemoryMarshal.CreateSpan(ref counterData, 1);
|
||||
|
||||
Span<byte> data = MemoryMarshal.Cast<CounterData, byte>(counterDataSpan);
|
||||
|
||||
if (counter?.Invalid != true)
|
||||
{
|
||||
_context.MemoryAccessor.Write(gpuVa, data);
|
||||
_context.MemoryAccessor.Write(gpuVa, counterData);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||
|
||||
private bool _forceShaderUpdate;
|
||||
|
||||
private bool _prevTfEnable;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the GPU methods class.
|
||||
/// </summary>
|
||||
|
@ -124,6 +126,14 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||
/// <param name="state">Guest GPU state</param>
|
||||
private void UpdateState(GpuState state)
|
||||
{
|
||||
bool tfEnable = state.Get<Boolean32>(MethodOffset.TfEnable);
|
||||
|
||||
if (!tfEnable && _prevTfEnable)
|
||||
{
|
||||
_context.Renderer.Pipeline.EndTransformFeedback();
|
||||
_prevTfEnable = false;
|
||||
}
|
||||
|
||||
// Shaders must be the first one to be updated if modified, because
|
||||
// some of the other state depends on information from the currently
|
||||
// bound shaders.
|
||||
|
@ -134,6 +144,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||
UpdateShaderState(state);
|
||||
}
|
||||
|
||||
if (state.QueryModified(MethodOffset.TfBufferState))
|
||||
{
|
||||
UpdateTfBufferState(state);
|
||||
}
|
||||
|
||||
if (state.QueryModified(MethodOffset.ClipDistanceEnable))
|
||||
{
|
||||
UpdateUserClipState(state);
|
||||
|
@ -258,6 +273,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||
}
|
||||
|
||||
CommitBindings();
|
||||
|
||||
if (tfEnable && !_prevTfEnable)
|
||||
{
|
||||
_context.Renderer.Pipeline.BeginTransformFeedback(PrimitiveType.Convert());
|
||||
_prevTfEnable = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -318,7 +339,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="useControl">Use draw buffers information from render target control register</param>
|
||||
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
||||
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
||||
private void UpdateRenderTargetState(GpuState state, bool useControl, int singleUse = -1)
|
||||
{
|
||||
var rtControl = state.Get<RtControl>(MethodOffset.RtControl);
|
||||
|
@ -1003,6 +1024,27 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||
_context.Renderer.Pipeline.SetProgram(gs.HostProgram);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates transform feedback buffer state based on the guest GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdateTfBufferState(GpuState state)
|
||||
{
|
||||
for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
|
||||
{
|
||||
TfBufferState tfb = state.Get<TfBufferState>(MethodOffset.TfBufferState, index);
|
||||
|
||||
if (!tfb.Enable)
|
||||
{
|
||||
BufferManager.SetTransformFeedbackBuffer(index, 0, 0);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferManager.SetTransformFeedbackBuffer(index, tfb.Address.Pack(), (uint)tfb.Size);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates user-defined clipping based on the guest GPU state.
|
||||
/// </summary>
|
||||
|
|
|
@ -24,8 +24,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
private Buffer[] _bufferOverlaps;
|
||||
|
||||
private IndexBuffer _indexBuffer;
|
||||
|
||||
private VertexBuffer[] _vertexBuffers;
|
||||
private BufferBounds[] _transformFeedbackBuffers;
|
||||
|
||||
private class BuffersPerStage
|
||||
{
|
||||
|
@ -56,6 +56,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
private bool _indexBufferDirty;
|
||||
private bool _vertexBuffersDirty;
|
||||
private uint _vertexBuffersEnableMask;
|
||||
private bool _transformFeedbackBuffersDirty;
|
||||
|
||||
private bool _rebind;
|
||||
|
||||
|
@ -73,6 +74,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
|
||||
_vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
|
||||
|
||||
_transformFeedbackBuffers = new BufferBounds[Constants.TotalTransformFeedbackBuffers];
|
||||
|
||||
_cpStorageBuffers = new BuffersPerStage(Constants.TotalCpStorageBuffers);
|
||||
_cpUniformBuffers = new BuffersPerStage(Constants.TotalCpUniformBuffers);
|
||||
|
||||
|
@ -144,6 +147,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
}
|
||||
}
|
||||
|
||||
public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
|
||||
{
|
||||
ulong address = TranslateAndCreateBuffer(gpuVa, size);
|
||||
|
||||
_transformFeedbackBuffers[index].Address = address;
|
||||
_transformFeedbackBuffers[index].Size = size;
|
||||
|
||||
_transformFeedbackBuffersDirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a storage buffer on the compute pipeline.
|
||||
/// Storage buffers can be read and written to on shaders.
|
||||
|
@ -522,6 +535,41 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
}
|
||||
}
|
||||
|
||||
if (_transformFeedbackBuffersDirty)
|
||||
{
|
||||
_transformFeedbackBuffersDirty = false;
|
||||
|
||||
for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
|
||||
{
|
||||
BufferBounds tfb = _transformFeedbackBuffers[index];
|
||||
|
||||
if (tfb.Address == 0)
|
||||
{
|
||||
_context.Renderer.Pipeline.SetTransformFeedbackBuffer(index, new BufferRange(BufferHandle.Null, 0, 0));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferRange buffer = GetBufferRange(tfb.Address, tfb.Size);
|
||||
|
||||
_context.Renderer.Pipeline.SetTransformFeedbackBuffer(index, buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
|
||||
{
|
||||
BufferBounds tfb = _transformFeedbackBuffers[index];
|
||||
|
||||
if (tfb.Address == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SynchronizeBufferRange(tfb.Address, tfb.Size);
|
||||
}
|
||||
}
|
||||
|
||||
if (_gpStorageBuffersDirty || _rebind)
|
||||
{
|
||||
_gpStorageBuffersDirty = false;
|
||||
|
|
|
@ -63,11 +63,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
/// </summary>
|
||||
/// <param name="gpuVa">GPU virtual address to write the value into</param>
|
||||
/// <param name="value">The value to be written</param>
|
||||
public void Write(ulong gpuVa, int value)
|
||||
public void Write<T>(ulong gpuVa, T value) where T : unmanaged
|
||||
{
|
||||
ulong processVa = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
_context.PhysicalMemory.Write(processVa, BitConverter.GetBytes(value));
|
||||
_context.PhysicalMemory.Write(processVa, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
|
||||
shader.HostShader = _context.Renderer.CompileShader(shader.Program);
|
||||
|
||||
IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader });
|
||||
IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, null);
|
||||
|
||||
ShaderBundle cpShader = new ShaderBundle(hostProgram, shader);
|
||||
|
||||
|
@ -150,6 +150,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
continue;
|
||||
}
|
||||
|
||||
var tfd = GetTransformFeedbackDescriptors(state);
|
||||
|
||||
IShader hostShader = _context.Renderer.CompileShader(program);
|
||||
|
||||
shaders[stage].HostShader = hostShader;
|
||||
|
@ -157,7 +159,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
hostShaders.Add(hostShader);
|
||||
}
|
||||
|
||||
IProgram hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray());
|
||||
IProgram hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray(), GetTransformFeedbackDescriptors(state));
|
||||
|
||||
ShaderBundle gpShaders = new ShaderBundle(hostProgram, shaders);
|
||||
|
||||
|
@ -173,6 +175,36 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
return gpShaders;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets transform feedback state from the current GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <returns>Four transform feedback descriptors for the enabled TFBs, or null if TFB is disabled</returns>
|
||||
private TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(GpuState state)
|
||||
{
|
||||
bool tfEnable = state.Get<Boolean32>(MethodOffset.TfEnable);
|
||||
|
||||
if (!tfEnable)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
TransformFeedbackDescriptor[] descs = new TransformFeedbackDescriptor[Constants.TotalTransformFeedbackBuffers];
|
||||
|
||||
for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++)
|
||||
{
|
||||
var tf = state.Get<TfState>(MethodOffset.TfState, i);
|
||||
|
||||
int length = (int)Math.Min((uint)tf.VaryingsCount, 0x80);
|
||||
|
||||
var varyingLocations = state.GetSpan(MethodOffset.TfVaryingLocations + i * 0x80, length).ToArray();
|
||||
|
||||
descs[i] = new TransformFeedbackDescriptor(tf.BufferIndex, tf.Stride, varyingLocations);
|
||||
}
|
||||
|
||||
return descs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if compute shader code in memory is equal to the cached shader.
|
||||
/// </summary>
|
||||
|
|
|
@ -346,7 +346,7 @@ namespace Ryujinx.Graphics.Gpu.State
|
|||
/// <param name="offset">Register offset</param>
|
||||
/// <param name="index">Index for indexed data</param>
|
||||
/// <returns>The data at the specified location</returns>
|
||||
public T Get<T>(MethodOffset offset, int index) where T : struct
|
||||
public T Get<T>(MethodOffset offset, int index) where T : unmanaged
|
||||
{
|
||||
Register register = _registers[(int)offset];
|
||||
|
||||
|
@ -364,11 +364,22 @@ namespace Ryujinx.Graphics.Gpu.State
|
|||
/// <typeparam name="T">Type of the data</typeparam>
|
||||
/// <param name="offset">Register offset</param>
|
||||
/// <returns>The data at the specified location</returns>
|
||||
public T Get<T>(MethodOffset offset) where T : struct
|
||||
public T Get<T>(MethodOffset offset) where T : unmanaged
|
||||
{
|
||||
return MemoryMarshal.Cast<int, T>(_memory.AsSpan().Slice((int)offset))[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a span of the data at a given register offset.
|
||||
/// </summary>
|
||||
/// <param name="offset">Register offset</param>
|
||||
/// <param name="length">Length of the data in bytes</param>
|
||||
/// <returns>The data at the specified location</returns>
|
||||
public Span<byte> GetSpan(MethodOffset offset, int length)
|
||||
{
|
||||
return MemoryMarshal.Cast<int, byte>(_memory.AsSpan().Slice((int)offset)).Slice(0, length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets indexed data to a given register offset.
|
||||
/// </summary>
|
||||
|
@ -376,7 +387,7 @@ namespace Ryujinx.Graphics.Gpu.State
|
|||
/// <param name="offset">Register offset</param>
|
||||
/// <param name="index">Index for indexed data</param>
|
||||
/// <param name="data">The data to set</param>
|
||||
public void Set<T>(MethodOffset offset, int index, T data) where T : struct
|
||||
public void Set<T>(MethodOffset offset, int index, T data) where T : unmanaged
|
||||
{
|
||||
Register register = _registers[(int)offset];
|
||||
|
||||
|
@ -394,7 +405,7 @@ namespace Ryujinx.Graphics.Gpu.State
|
|||
/// <typeparam name="T">Type of the data</typeparam>
|
||||
/// <param name="offset">Register offset</param>
|
||||
/// <param name="data">The data to set</param>
|
||||
public void Set<T>(MethodOffset offset, T data) where T : struct
|
||||
public void Set<T>(MethodOffset offset, T data) where T : unmanaged
|
||||
{
|
||||
ReadOnlySpan<int> intSpan = MemoryMarshal.Cast<T, int>(MemoryMarshal.CreateReadOnlySpan(ref data, 1));
|
||||
intSpan.CopyTo(_memory.AsSpan().Slice((int)offset, intSpan.Length));
|
||||
|
|
|
@ -53,32 +53,34 @@ namespace Ryujinx.Graphics.Gpu.State
|
|||
/// </summary>
|
||||
public static TableItem[] Table = new TableItem[]
|
||||
{
|
||||
new TableItem(MethodOffset.RtColorState, typeof(RtColorState), Constants.TotalRenderTargets),
|
||||
new TableItem(MethodOffset.ViewportTransform, typeof(ViewportTransform), Constants.TotalViewports),
|
||||
new TableItem(MethodOffset.ViewportExtents, typeof(ViewportExtents), Constants.TotalViewports),
|
||||
new TableItem(MethodOffset.VertexBufferDrawState, typeof(VertexBufferDrawState), 1),
|
||||
new TableItem(MethodOffset.DepthBiasState, typeof(DepthBiasState), 1),
|
||||
new TableItem(MethodOffset.ScissorState, typeof(ScissorState), Constants.TotalViewports),
|
||||
new TableItem(MethodOffset.StencilBackMasks, typeof(StencilBackMasks), 1),
|
||||
new TableItem(MethodOffset.RtDepthStencilState, typeof(RtDepthStencilState), 1),
|
||||
new TableItem(MethodOffset.VertexAttribState, typeof(VertexAttribState), 16),
|
||||
new TableItem(MethodOffset.RtDepthStencilSize, typeof(Size3D), 1),
|
||||
new TableItem(MethodOffset.BlendEnable, typeof(Boolean32), Constants.TotalRenderTargets),
|
||||
new TableItem(MethodOffset.StencilTestState, typeof(StencilTestState), 1),
|
||||
new TableItem(MethodOffset.SamplerPoolState, typeof(PoolState), 1),
|
||||
new TableItem(MethodOffset.TexturePoolState, typeof(PoolState), 1),
|
||||
new TableItem(MethodOffset.StencilBackTestState, typeof(StencilBackTestState), 1),
|
||||
new TableItem(MethodOffset.ShaderBaseAddress, typeof(GpuVa), 1),
|
||||
new TableItem(MethodOffset.PrimitiveRestartState, typeof(PrimitiveRestartState), 1),
|
||||
new TableItem(MethodOffset.IndexBufferState, typeof(IndexBufferState), 1),
|
||||
new TableItem(MethodOffset.VertexBufferInstanced, typeof(Boolean32), 16),
|
||||
new TableItem(MethodOffset.FaceState, typeof(FaceState), 1),
|
||||
new TableItem(MethodOffset.RtColorMask, typeof(RtColorMask), Constants.TotalRenderTargets),
|
||||
new TableItem(MethodOffset.VertexBufferState, typeof(VertexBufferState), 16),
|
||||
new TableItem(MethodOffset.BlendConstant, typeof(ColorF), 1),
|
||||
new TableItem(MethodOffset.BlendState, typeof(BlendState), Constants.TotalRenderTargets),
|
||||
new TableItem(MethodOffset.VertexBufferEndAddress, typeof(GpuVa), 16),
|
||||
new TableItem(MethodOffset.ShaderState, typeof(ShaderState), 6),
|
||||
new TableItem(MethodOffset.TfBufferState, typeof(TfBufferState), Constants.TotalTransformFeedbackBuffers),
|
||||
new TableItem(MethodOffset.TfState, typeof(TfState), Constants.TotalTransformFeedbackBuffers),
|
||||
new TableItem(MethodOffset.RtColorState, typeof(RtColorState), Constants.TotalRenderTargets),
|
||||
new TableItem(MethodOffset.ViewportTransform, typeof(ViewportTransform), Constants.TotalViewports),
|
||||
new TableItem(MethodOffset.ViewportExtents, typeof(ViewportExtents), Constants.TotalViewports),
|
||||
new TableItem(MethodOffset.VertexBufferDrawState, typeof(VertexBufferDrawState), 1),
|
||||
new TableItem(MethodOffset.DepthBiasState, typeof(DepthBiasState), 1),
|
||||
new TableItem(MethodOffset.ScissorState, typeof(ScissorState), Constants.TotalViewports),
|
||||
new TableItem(MethodOffset.StencilBackMasks, typeof(StencilBackMasks), 1),
|
||||
new TableItem(MethodOffset.RtDepthStencilState, typeof(RtDepthStencilState), 1),
|
||||
new TableItem(MethodOffset.VertexAttribState, typeof(VertexAttribState), Constants.TotalVertexAttribs),
|
||||
new TableItem(MethodOffset.RtDepthStencilSize, typeof(Size3D), 1),
|
||||
new TableItem(MethodOffset.BlendEnable, typeof(Boolean32), Constants.TotalRenderTargets),
|
||||
new TableItem(MethodOffset.StencilTestState, typeof(StencilTestState), 1),
|
||||
new TableItem(MethodOffset.SamplerPoolState, typeof(PoolState), 1),
|
||||
new TableItem(MethodOffset.TexturePoolState, typeof(PoolState), 1),
|
||||
new TableItem(MethodOffset.StencilBackTestState, typeof(StencilBackTestState), 1),
|
||||
new TableItem(MethodOffset.ShaderBaseAddress, typeof(GpuVa), 1),
|
||||
new TableItem(MethodOffset.PrimitiveRestartState, typeof(PrimitiveRestartState), 1),
|
||||
new TableItem(MethodOffset.IndexBufferState, typeof(IndexBufferState), 1),
|
||||
new TableItem(MethodOffset.VertexBufferInstanced, typeof(Boolean32), Constants.TotalVertexBuffers),
|
||||
new TableItem(MethodOffset.FaceState, typeof(FaceState), 1),
|
||||
new TableItem(MethodOffset.RtColorMask, typeof(RtColorMask), Constants.TotalRenderTargets),
|
||||
new TableItem(MethodOffset.VertexBufferState, typeof(VertexBufferState), Constants.TotalVertexBuffers),
|
||||
new TableItem(MethodOffset.BlendConstant, typeof(ColorF), 1),
|
||||
new TableItem(MethodOffset.BlendState, typeof(BlendState), Constants.TotalRenderTargets),
|
||||
new TableItem(MethodOffset.VertexBufferEndAddress, typeof(GpuVa), Constants.TotalVertexBuffers),
|
||||
new TableItem(MethodOffset.ShaderState, typeof(ShaderState), Constants.ShaderStages + 1),
|
||||
};
|
||||
}
|
||||
}
|
|
@ -28,10 +28,13 @@ namespace Ryujinx.Graphics.Gpu.State
|
|||
SyncpointAction = 0xb2,
|
||||
CopyBuffer = 0xc0,
|
||||
RasterizeEnable = 0xdf,
|
||||
TfBufferState = 0xe0,
|
||||
CopyBufferParams = 0x100,
|
||||
TfState = 0x1c0,
|
||||
CopyBufferSwizzle = 0x1c2,
|
||||
CopyBufferDstTexture = 0x1c3,
|
||||
CopyBufferSrcTexture = 0x1ca,
|
||||
TfEnable = 0x1d1,
|
||||
RtColorState = 0x200,
|
||||
CopyTextureControl = 0x223,
|
||||
CopyRegion = 0x22c,
|
||||
|
@ -116,6 +119,7 @@ namespace Ryujinx.Graphics.Gpu.State
|
|||
UniformBufferBindTessEvaluation = 0x914,
|
||||
UniformBufferBindGeometry = 0x91c,
|
||||
UniformBufferBindFragment = 0x924,
|
||||
TextureBufferIndex = 0x982
|
||||
TextureBufferIndex = 0x982,
|
||||
TfVaryingLocations = 0xa00
|
||||
}
|
||||
}
|
|
@ -11,18 +11,19 @@ namespace Ryujinx.Graphics.Gpu.State
|
|||
VertexShaderInvocations = 5,
|
||||
GeometryShaderInvocations = 7,
|
||||
GeometryShaderPrimitives = 9,
|
||||
ZcullStats0 = 0xa,
|
||||
TransformFeedbackPrimitivesWritten = 0xb,
|
||||
ZcullStats1 = 0xc,
|
||||
ZcullStats2 = 0xe,
|
||||
ClipperInputPrimitives = 0xf,
|
||||
ZcullStats3 = 0x10,
|
||||
ClipperOutputPrimitives = 0x11,
|
||||
PrimitivesGenerated = 0x12,
|
||||
FragmentShaderInvocations = 0x13,
|
||||
SamplesPassed = 0x15,
|
||||
TransformFeedbackOffset = 0x1a,
|
||||
TessControlShaderInvocations = 0x1b,
|
||||
TessEvaluationShaderInvocations = 0x1d,
|
||||
TessEvaluationShaderPrimitives = 0x1f,
|
||||
ZcullStats0 = 0x2a,
|
||||
ZcullStats1 = 0x2c,
|
||||
ZcullStats2 = 0x2e,
|
||||
ZcullStats3 = 0x30
|
||||
TessEvaluationShaderPrimitives = 0x1f
|
||||
}
|
||||
}
|
18
Ryujinx.Graphics.Gpu/State/TfBufferState.cs
Normal file
18
Ryujinx.Graphics.Gpu/State/TfBufferState.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
/// <summary>
|
||||
/// Transform feedback buffer state.
|
||||
/// </summary>
|
||||
struct TfBufferState
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public Boolean32 Enable;
|
||||
public GpuVa Address;
|
||||
public int Size;
|
||||
public int Offset;
|
||||
public uint Padding0;
|
||||
public uint Padding1;
|
||||
public uint Padding2;
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
}
|
15
Ryujinx.Graphics.Gpu/State/TfState.cs
Normal file
15
Ryujinx.Graphics.Gpu/State/TfState.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
/// <summary>
|
||||
/// Transform feedback state.
|
||||
/// </summary>
|
||||
struct TfState
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public int BufferIndex;
|
||||
public int VaryingsCount;
|
||||
public int Stride;
|
||||
public uint Padding;
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
}
|
|
@ -331,6 +331,31 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
return PrimitiveType.Points;
|
||||
}
|
||||
|
||||
public static TransformFeedbackPrimitiveType ConvertToTfType(this PrimitiveTopology topology)
|
||||
{
|
||||
switch (topology)
|
||||
{
|
||||
case PrimitiveTopology.Points:
|
||||
return TransformFeedbackPrimitiveType.Points;
|
||||
case PrimitiveTopology.Lines:
|
||||
case PrimitiveTopology.LineLoop:
|
||||
case PrimitiveTopology.LineStrip:
|
||||
case PrimitiveTopology.LinesAdjacency:
|
||||
case PrimitiveTopology.LineStripAdjacency:
|
||||
return TransformFeedbackPrimitiveType.Lines;
|
||||
case PrimitiveTopology.Triangles:
|
||||
case PrimitiveTopology.TriangleStrip:
|
||||
case PrimitiveTopology.TriangleFan:
|
||||
case PrimitiveTopology.TrianglesAdjacency:
|
||||
case PrimitiveTopology.TriangleStripAdjacency:
|
||||
return TransformFeedbackPrimitiveType.Triangles;
|
||||
}
|
||||
|
||||
Logger.PrintDebug(LogClass.Gpu, $"Invalid {nameof(PrimitiveTopology)} enum value: {topology}.");
|
||||
|
||||
return TransformFeedbackPrimitiveType.Points;
|
||||
}
|
||||
|
||||
public static OpenTK.Graphics.OpenGL.StencilOp Convert(this GAL.StencilOp op)
|
||||
{
|
||||
switch (op)
|
||||
|
|
|
@ -45,6 +45,8 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
|
||||
private bool _scissor0Enable = false;
|
||||
|
||||
private bool _tfEnabled;
|
||||
|
||||
ColorF _blendConstant = new ColorF(0, 0, 0, 0);
|
||||
|
||||
internal Pipeline()
|
||||
|
@ -76,6 +78,12 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
GL.MemoryBarrier(MemoryBarrierFlags.AllBarrierBits);
|
||||
}
|
||||
|
||||
public void BeginTransformFeedback(PrimitiveTopology topology)
|
||||
{
|
||||
GL.BeginTransformFeedback(topology.ConvertToTfType());
|
||||
_tfEnabled = true;
|
||||
}
|
||||
|
||||
public void ClearRenderTargetColor(int index, uint componentMask, ColorF color)
|
||||
{
|
||||
GL.ColorMask(
|
||||
|
@ -512,6 +520,12 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
}
|
||||
}
|
||||
|
||||
public void EndTransformFeedback()
|
||||
{
|
||||
GL.EndTransformFeedback();
|
||||
_tfEnabled = false;
|
||||
}
|
||||
|
||||
public void SetBlendState(int index, BlendDescriptor blend)
|
||||
{
|
||||
if (!blend.Enable)
|
||||
|
@ -713,7 +727,17 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
public void SetProgram(IProgram program)
|
||||
{
|
||||
_program = (Program)program;
|
||||
_program.Bind();
|
||||
|
||||
if (_tfEnabled)
|
||||
{
|
||||
GL.PauseTransformFeedback();
|
||||
_program.Bind();
|
||||
GL.ResumeTransformFeedback();
|
||||
}
|
||||
else
|
||||
{
|
||||
_program.Bind();
|
||||
}
|
||||
|
||||
SetRenderTargetScale(_fpRenderScale[0]);
|
||||
}
|
||||
|
@ -904,6 +928,22 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
}
|
||||
}
|
||||
|
||||
public void SetTransformFeedbackBuffer(int index, BufferRange buffer)
|
||||
{
|
||||
const BufferRangeTarget target = BufferRangeTarget.TransformFeedbackBuffer;
|
||||
|
||||
if (_tfEnabled)
|
||||
{
|
||||
GL.PauseTransformFeedback();
|
||||
GL.BindBufferRange(target, index, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size);
|
||||
GL.ResumeTransformFeedback();
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.BindBufferRange(target, index, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUniformBuffer(int index, ShaderStage stage, BufferRange buffer)
|
||||
{
|
||||
SetBuffer(index, stage, buffer, isStorage: false);
|
||||
|
@ -1132,7 +1172,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
{
|
||||
// If the event has been flushed, then just use the values on the CPU.
|
||||
// The query object may already be repurposed for another draw (eg. begin + end).
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (compare == 0 && evt.Type == QueryTarget.SamplesPassed && evt.ClearCounter)
|
||||
|
@ -1145,7 +1185,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
// The GPU will flush the queries to CPU and evaluate the condition there instead.
|
||||
|
||||
GL.Flush(); // The thread will be stalled manually flushing the counter, so flush GL commands now.
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual)
|
||||
|
|
|
@ -2,6 +2,10 @@ using OpenTK.Graphics.OpenGL;
|
|||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using Ryujinx.Graphics.Shader.CodeGen.Glsl;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Graphics.OpenGL
|
||||
{
|
||||
|
@ -31,7 +35,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
private int[] _textureUnits;
|
||||
private int[] _imageUnits;
|
||||
|
||||
public Program(IShader[] shaders)
|
||||
public Program(IShader[] shaders, TransformFeedbackDescriptor[] transformFeedbackDescriptors)
|
||||
{
|
||||
_ubBindingPoints = new int[UbsPerStage * ShaderStages];
|
||||
_sbBindingPoints = new int[SbsPerStage * ShaderStages];
|
||||
|
@ -67,6 +71,54 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
GL.AttachShader(Handle, shaderHandle);
|
||||
}
|
||||
|
||||
if (transformFeedbackDescriptors != null)
|
||||
{
|
||||
List<string> varyings = new List<string>();
|
||||
|
||||
int cbi = 0;
|
||||
|
||||
foreach (var tfd in transformFeedbackDescriptors.OrderBy(x => x.BufferIndex))
|
||||
{
|
||||
if (tfd.VaryingLocations.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
while (cbi < tfd.BufferIndex)
|
||||
{
|
||||
varyings.Add("gl_NextBuffer");
|
||||
|
||||
cbi++;
|
||||
}
|
||||
|
||||
int stride = Math.Min(128 * 4, (tfd.Stride + 3) & ~3);
|
||||
|
||||
int j = 0;
|
||||
|
||||
for (; j < tfd.VaryingLocations.Length && j * 4 < stride; j++)
|
||||
{
|
||||
byte location = tfd.VaryingLocations[j];
|
||||
|
||||
varyings.Add(Varying.GetName(location) ?? "gl_SkipComponents1");
|
||||
|
||||
j += Varying.GetSize(location) - 1;
|
||||
}
|
||||
|
||||
int feedbackBytes = j * 4;
|
||||
|
||||
while (feedbackBytes < stride)
|
||||
{
|
||||
int bytes = Math.Min(16, stride - feedbackBytes);
|
||||
|
||||
varyings.Add($"gl_SkipComponents{(bytes / 4)}");
|
||||
|
||||
feedbackBytes += bytes;
|
||||
}
|
||||
}
|
||||
|
||||
GL.TransformFeedbackVaryings(Handle, varyings.Count, varyings.ToArray(), TransformFeedbackMode.InterleavedAttribs);
|
||||
}
|
||||
|
||||
GL.LinkProgram(Handle);
|
||||
|
||||
for (int index = 0; index < shaders.Length; index++)
|
||||
|
|
|
@ -44,9 +44,9 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
return Buffer.Create(size);
|
||||
}
|
||||
|
||||
public IProgram CreateProgram(IShader[] shaders)
|
||||
public IProgram CreateProgram(IShader[] shaders, TransformFeedbackDescriptor[] transformFeedbackDescriptors)
|
||||
{
|
||||
return new Program(shaders);
|
||||
return new Program(shaders, transformFeedbackDescriptors);
|
||||
}
|
||||
|
||||
public ISampler CreateSampler(SamplerCreateInfo info)
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
|
||||
public static void Declare(CodeGenContext context, StructuredProgramInfo info)
|
||||
{
|
||||
context.AppendLine("#version 430 core");
|
||||
context.AppendLine("#version 440 core");
|
||||
context.AppendLine("#extension GL_ARB_gpu_shader_int64 : enable");
|
||||
context.AppendLine("#extension GL_ARB_shader_ballot : enable");
|
||||
context.AppendLine("#extension GL_ARB_shader_group_vote : enable");
|
||||
|
@ -234,7 +234,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
string stage = OperandManager.GetShaderStagePrefix(context.Config.Stage);
|
||||
|
||||
int scaleElements = context.TextureDescriptors.Count;
|
||||
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Fragment)
|
||||
{
|
||||
scaleElements++; // Also includes render target scale, for gl_FragCoord.
|
||||
|
@ -424,7 +424,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
};
|
||||
}
|
||||
|
||||
context.AppendLine($"layout (location = {attr}) {iq}in vec4 {DefaultNames.IAttributePrefix}{attr}{suffix};");
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
char swzMask = "xyzw"[c];
|
||||
|
||||
context.AppendLine($"layout (location = {attr}, component = {c}) {iq}in float {DefaultNames.IAttributePrefix}{attr}_{swzMask}{suffix};");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,12 +457,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
{
|
||||
for (int attr = 0; attr < MaxAttributes; attr++)
|
||||
{
|
||||
context.AppendLine($"layout (location = {attr}) out vec4 {DefaultNames.OAttributePrefix}{attr};");
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
char swzMask = "xyzw"[c];
|
||||
|
||||
context.AppendLine($"layout (location = {attr}, component = {c}) out float {DefaultNames.OAttributePrefix}{attr}_{swzMask};");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (int attr in info.OAttributes.OrderBy(x => x).Where(x => x >= MaxAttributes))
|
||||
{
|
||||
context.AppendLine($"layout (location = {attr}) out vec4 {DefaultNames.OAttributePrefix}{attr};");
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
char swzMask = "xyzw"[c];
|
||||
|
||||
context.AppendLine($"layout (location = {attr}, component = {c}) out float {DefaultNames.OAttributePrefix}{attr}_{swzMask};");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
continue;
|
||||
}
|
||||
|
||||
context.AppendLine($"{DefaultNames.OAttributePrefix}{attr} = vec4(0);");
|
||||
context.AppendLine($"{DefaultNames.OAttributePrefix}{attr}_x = 0;");
|
||||
context.AppendLine($"{DefaultNames.OAttributePrefix}{attr}_y = 0;");
|
||||
context.AppendLine($"{DefaultNames.OAttributePrefix}{attr}_z = 0;");
|
||||
context.AppendLine($"{DefaultNames.OAttributePrefix}{attr}_w = 0;");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
{
|
||||
int value = attr.Value;
|
||||
|
||||
string swzMask = GetSwizzleMask((value >> 2) & 3);
|
||||
char swzMask = GetSwizzleMask((value >> 2) & 3);
|
||||
|
||||
if (value >= AttributeConsts.UserAttributeBase &&
|
||||
value < AttributeConsts.UserAttributeEnd)
|
||||
|
@ -158,15 +158,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
? DefaultNames.OAttributePrefix
|
||||
: DefaultNames.IAttributePrefix;
|
||||
|
||||
string name = $"{prefix}{(value >> 4)}";
|
||||
string name = $"{prefix}{(value >> 4)}_{swzMask}";
|
||||
|
||||
if (stage == ShaderStage.Geometry && !isOutAttr)
|
||||
{
|
||||
name += $"[{indexExpr}]";
|
||||
}
|
||||
|
||||
name += "." + swzMask;
|
||||
|
||||
return name;
|
||||
}
|
||||
else
|
||||
|
@ -264,9 +262,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
return _stagePrefixes[index];
|
||||
}
|
||||
|
||||
private static string GetSwizzleMask(int value)
|
||||
private static char GetSwizzleMask(int value)
|
||||
{
|
||||
return "xyzw".Substring(value, 1);
|
||||
return "xyzw"[value];
|
||||
}
|
||||
|
||||
public static VariableType GetNodeDestType(IAstNode node)
|
||||
|
|
69
Ryujinx.Graphics.Shader/CodeGen/Glsl/Varying.cs
Normal file
69
Ryujinx.Graphics.Shader/CodeGen/Glsl/Varying.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
using Ryujinx.Graphics.Shader.Translation;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
{
|
||||
public static class Varying
|
||||
{
|
||||
public static string GetName(int offset)
|
||||
{
|
||||
offset <<= 2;
|
||||
|
||||
if (offset >= AttributeConsts.UserAttributeBase &&
|
||||
offset < AttributeConsts.UserAttributeEnd)
|
||||
{
|
||||
offset -= AttributeConsts.UserAttributeBase;
|
||||
|
||||
string name = $"{ DefaultNames.OAttributePrefix}{(offset >> 4)}";
|
||||
|
||||
name += "_" + "xyzw"[(offset >> 2) & 3];
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
switch (offset)
|
||||
{
|
||||
case AttributeConsts.PositionX:
|
||||
case AttributeConsts.PositionY:
|
||||
case AttributeConsts.PositionZ:
|
||||
case AttributeConsts.PositionW:
|
||||
return "gl_Position";
|
||||
case AttributeConsts.PointSize:
|
||||
return "gl_PointSize";
|
||||
case AttributeConsts.ClipDistance0:
|
||||
return "gl_ClipDistance[0]";
|
||||
case AttributeConsts.ClipDistance1:
|
||||
return "gl_ClipDistance[1]";
|
||||
case AttributeConsts.ClipDistance2:
|
||||
return "gl_ClipDistance[2]";
|
||||
case AttributeConsts.ClipDistance3:
|
||||
return "gl_ClipDistance[3]";
|
||||
case AttributeConsts.ClipDistance4:
|
||||
return "gl_ClipDistance[4]";
|
||||
case AttributeConsts.ClipDistance5:
|
||||
return "gl_ClipDistance[5]";
|
||||
case AttributeConsts.ClipDistance6:
|
||||
return "gl_ClipDistance[6]";
|
||||
case AttributeConsts.ClipDistance7:
|
||||
return "gl_ClipDistance[7]";
|
||||
case AttributeConsts.VertexId:
|
||||
return "gl_VertexID";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int GetSize(int offset)
|
||||
{
|
||||
switch (offset << 2)
|
||||
{
|
||||
case AttributeConsts.PositionX:
|
||||
case AttributeConsts.PositionY:
|
||||
case AttributeConsts.PositionZ:
|
||||
case AttributeConsts.PositionW:
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
Reference in a new issue