forked from Mirror/Ryujinx
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 Barrier();
|
||||||
|
|
||||||
|
void BeginTransformFeedback(PrimitiveTopology topology);
|
||||||
|
|
||||||
void ClearRenderTargetColor(int index, uint componentMask, ColorF color);
|
void ClearRenderTargetColor(int index, uint componentMask, ColorF color);
|
||||||
|
|
||||||
void ClearRenderTargetDepthStencil(
|
void ClearRenderTargetDepthStencil(
|
||||||
|
@ -27,6 +29,8 @@ namespace Ryujinx.Graphics.GAL
|
||||||
int firstVertex,
|
int firstVertex,
|
||||||
int firstInstance);
|
int firstInstance);
|
||||||
|
|
||||||
|
void EndTransformFeedback();
|
||||||
|
|
||||||
void SetBlendState(int index, BlendDescriptor blend);
|
void SetBlendState(int index, BlendDescriptor blend);
|
||||||
|
|
||||||
void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp);
|
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 SetTexture(int index, ShaderStage stage, ITexture texture);
|
||||||
|
|
||||||
|
void SetTransformFeedbackBuffer(int index, BufferRange buffer);
|
||||||
void SetUniformBuffer(int index, ShaderStage stage, BufferRange buffer);
|
void SetUniformBuffer(int index, ShaderStage stage, BufferRange buffer);
|
||||||
|
|
||||||
void SetUserClipDistance(int index, bool enableClip);
|
void SetUserClipDistance(int index, bool enableClip);
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.GAL
|
||||||
|
|
||||||
BufferHandle CreateBuffer(int size);
|
BufferHandle CreateBuffer(int size);
|
||||||
|
|
||||||
IProgram CreateProgram(IShader[] shaders);
|
IProgram CreateProgram(IShader[] shaders, TransformFeedbackDescriptor[] transformFeedbackDescriptors);
|
||||||
|
|
||||||
ISampler CreateSampler(SamplerCreateInfo info);
|
ISampler CreateSampler(SamplerCreateInfo info);
|
||||||
ITexture CreateTexture(TextureCreateInfo info, float scale);
|
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>
|
/// </remarks>
|
||||||
public const int TotalGpStorageBuffers = 16;
|
public const int TotalGpStorageBuffers = 16;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum number of transform feedback buffers.
|
||||||
|
/// </summary>
|
||||||
|
public const int TotalTransformFeedbackBuffers = 4;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum number of render target color buffers.
|
/// Maximum number of render target color buffers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -61,8 +61,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
/// <param name="type">Counter to be written to memory</param>
|
/// <param name="type">Counter to be written to memory</param>
|
||||||
private void ReportCounter(GpuState state, ReportCounterType type)
|
private void ReportCounter(GpuState state, ReportCounterType type)
|
||||||
{
|
{
|
||||||
CounterData counterData = new CounterData();
|
|
||||||
|
|
||||||
var rs = state.Get<SemaphoreState>(MethodOffset.ReportState);
|
var rs = state.Get<SemaphoreState>(MethodOffset.ReportState);
|
||||||
|
|
||||||
ulong gpuVa = rs.Address.Pack();
|
ulong gpuVa = rs.Address.Pack();
|
||||||
|
@ -80,16 +78,14 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
|
||||||
EventHandler<ulong> resultHandler = (object evt, ulong result) =>
|
EventHandler<ulong> resultHandler = (object evt, ulong result) =>
|
||||||
{
|
{
|
||||||
|
CounterData counterData = new CounterData();
|
||||||
|
|
||||||
counterData.Counter = result;
|
counterData.Counter = result;
|
||||||
counterData.Timestamp = ticks;
|
counterData.Timestamp = ticks;
|
||||||
|
|
||||||
Span<CounterData> counterDataSpan = MemoryMarshal.CreateSpan(ref counterData, 1);
|
|
||||||
|
|
||||||
Span<byte> data = MemoryMarshal.Cast<CounterData, byte>(counterDataSpan);
|
|
||||||
|
|
||||||
if (counter?.Invalid != true)
|
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 _forceShaderUpdate;
|
||||||
|
|
||||||
|
private bool _prevTfEnable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the GPU methods class.
|
/// Creates a new instance of the GPU methods class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -124,6 +126,14 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
/// <param name="state">Guest GPU state</param>
|
/// <param name="state">Guest GPU state</param>
|
||||||
private void UpdateState(GpuState state)
|
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
|
// Shaders must be the first one to be updated if modified, because
|
||||||
// some of the other state depends on information from the currently
|
// some of the other state depends on information from the currently
|
||||||
// bound shaders.
|
// bound shaders.
|
||||||
|
@ -134,6 +144,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
UpdateShaderState(state);
|
UpdateShaderState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.QueryModified(MethodOffset.TfBufferState))
|
||||||
|
{
|
||||||
|
UpdateTfBufferState(state);
|
||||||
|
}
|
||||||
|
|
||||||
if (state.QueryModified(MethodOffset.ClipDistanceEnable))
|
if (state.QueryModified(MethodOffset.ClipDistanceEnable))
|
||||||
{
|
{
|
||||||
UpdateUserClipState(state);
|
UpdateUserClipState(state);
|
||||||
|
@ -258,6 +273,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
}
|
}
|
||||||
|
|
||||||
CommitBindings();
|
CommitBindings();
|
||||||
|
|
||||||
|
if (tfEnable && !_prevTfEnable)
|
||||||
|
{
|
||||||
|
_context.Renderer.Pipeline.BeginTransformFeedback(PrimitiveType.Convert());
|
||||||
|
_prevTfEnable = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1003,6 +1024,27 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
_context.Renderer.Pipeline.SetProgram(gs.HostProgram);
|
_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>
|
/// <summary>
|
||||||
/// Updates user-defined clipping based on the guest GPU state.
|
/// Updates user-defined clipping based on the guest GPU state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -24,8 +24,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
private Buffer[] _bufferOverlaps;
|
private Buffer[] _bufferOverlaps;
|
||||||
|
|
||||||
private IndexBuffer _indexBuffer;
|
private IndexBuffer _indexBuffer;
|
||||||
|
|
||||||
private VertexBuffer[] _vertexBuffers;
|
private VertexBuffer[] _vertexBuffers;
|
||||||
|
private BufferBounds[] _transformFeedbackBuffers;
|
||||||
|
|
||||||
private class BuffersPerStage
|
private class BuffersPerStage
|
||||||
{
|
{
|
||||||
|
@ -56,6 +56,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
private bool _indexBufferDirty;
|
private bool _indexBufferDirty;
|
||||||
private bool _vertexBuffersDirty;
|
private bool _vertexBuffersDirty;
|
||||||
private uint _vertexBuffersEnableMask;
|
private uint _vertexBuffersEnableMask;
|
||||||
|
private bool _transformFeedbackBuffersDirty;
|
||||||
|
|
||||||
private bool _rebind;
|
private bool _rebind;
|
||||||
|
|
||||||
|
@ -73,6 +74,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
|
||||||
_vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
|
_vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
|
||||||
|
|
||||||
|
_transformFeedbackBuffers = new BufferBounds[Constants.TotalTransformFeedbackBuffers];
|
||||||
|
|
||||||
_cpStorageBuffers = new BuffersPerStage(Constants.TotalCpStorageBuffers);
|
_cpStorageBuffers = new BuffersPerStage(Constants.TotalCpStorageBuffers);
|
||||||
_cpUniformBuffers = new BuffersPerStage(Constants.TotalCpUniformBuffers);
|
_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>
|
/// <summary>
|
||||||
/// Sets a storage buffer on the compute pipeline.
|
/// Sets a storage buffer on the compute pipeline.
|
||||||
/// Storage buffers can be read and written to on shaders.
|
/// 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)
|
if (_gpStorageBuffersDirty || _rebind)
|
||||||
{
|
{
|
||||||
_gpStorageBuffersDirty = false;
|
_gpStorageBuffersDirty = false;
|
||||||
|
|
|
@ -63,11 +63,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="gpuVa">GPU virtual address to write the value into</param>
|
/// <param name="gpuVa">GPU virtual address to write the value into</param>
|
||||||
/// <param name="value">The value to be written</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);
|
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>
|
/// <summary>
|
||||||
|
|
|
@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
|
|
||||||
shader.HostShader = _context.Renderer.CompileShader(shader.Program);
|
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);
|
ShaderBundle cpShader = new ShaderBundle(hostProgram, shader);
|
||||||
|
|
||||||
|
@ -150,6 +150,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tfd = GetTransformFeedbackDescriptors(state);
|
||||||
|
|
||||||
IShader hostShader = _context.Renderer.CompileShader(program);
|
IShader hostShader = _context.Renderer.CompileShader(program);
|
||||||
|
|
||||||
shaders[stage].HostShader = hostShader;
|
shaders[stage].HostShader = hostShader;
|
||||||
|
@ -157,7 +159,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
hostShaders.Add(hostShader);
|
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);
|
ShaderBundle gpShaders = new ShaderBundle(hostProgram, shaders);
|
||||||
|
|
||||||
|
@ -173,6 +175,36 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
return gpShaders;
|
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>
|
/// <summary>
|
||||||
/// Checks if compute shader code in memory is equal to the cached shader.
|
/// Checks if compute shader code in memory is equal to the cached shader.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -346,7 +346,7 @@ namespace Ryujinx.Graphics.Gpu.State
|
||||||
/// <param name="offset">Register offset</param>
|
/// <param name="offset">Register offset</param>
|
||||||
/// <param name="index">Index for indexed data</param>
|
/// <param name="index">Index for indexed data</param>
|
||||||
/// <returns>The data at the specified location</returns>
|
/// <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];
|
Register register = _registers[(int)offset];
|
||||||
|
|
||||||
|
@ -364,11 +364,22 @@ namespace Ryujinx.Graphics.Gpu.State
|
||||||
/// <typeparam name="T">Type of the data</typeparam>
|
/// <typeparam name="T">Type of the data</typeparam>
|
||||||
/// <param name="offset">Register offset</param>
|
/// <param name="offset">Register offset</param>
|
||||||
/// <returns>The data at the specified location</returns>
|
/// <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];
|
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>
|
/// <summary>
|
||||||
/// Sets indexed data to a given register offset.
|
/// Sets indexed data to a given register offset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -376,7 +387,7 @@ namespace Ryujinx.Graphics.Gpu.State
|
||||||
/// <param name="offset">Register offset</param>
|
/// <param name="offset">Register offset</param>
|
||||||
/// <param name="index">Index for indexed data</param>
|
/// <param name="index">Index for indexed data</param>
|
||||||
/// <param name="data">The data to set</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];
|
Register register = _registers[(int)offset];
|
||||||
|
|
||||||
|
@ -394,7 +405,7 @@ namespace Ryujinx.Graphics.Gpu.State
|
||||||
/// <typeparam name="T">Type of the data</typeparam>
|
/// <typeparam name="T">Type of the data</typeparam>
|
||||||
/// <param name="offset">Register offset</param>
|
/// <param name="offset">Register offset</param>
|
||||||
/// <param name="data">The data to set</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));
|
ReadOnlySpan<int> intSpan = MemoryMarshal.Cast<T, int>(MemoryMarshal.CreateReadOnlySpan(ref data, 1));
|
||||||
intSpan.CopyTo(_memory.AsSpan().Slice((int)offset, intSpan.Length));
|
intSpan.CopyTo(_memory.AsSpan().Slice((int)offset, intSpan.Length));
|
||||||
|
|
|
@ -53,32 +53,34 @@ namespace Ryujinx.Graphics.Gpu.State
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static TableItem[] Table = new TableItem[]
|
public static TableItem[] Table = new TableItem[]
|
||||||
{
|
{
|
||||||
new TableItem(MethodOffset.RtColorState, typeof(RtColorState), Constants.TotalRenderTargets),
|
new TableItem(MethodOffset.TfBufferState, typeof(TfBufferState), Constants.TotalTransformFeedbackBuffers),
|
||||||
new TableItem(MethodOffset.ViewportTransform, typeof(ViewportTransform), Constants.TotalViewports),
|
new TableItem(MethodOffset.TfState, typeof(TfState), Constants.TotalTransformFeedbackBuffers),
|
||||||
new TableItem(MethodOffset.ViewportExtents, typeof(ViewportExtents), Constants.TotalViewports),
|
new TableItem(MethodOffset.RtColorState, typeof(RtColorState), Constants.TotalRenderTargets),
|
||||||
new TableItem(MethodOffset.VertexBufferDrawState, typeof(VertexBufferDrawState), 1),
|
new TableItem(MethodOffset.ViewportTransform, typeof(ViewportTransform), Constants.TotalViewports),
|
||||||
new TableItem(MethodOffset.DepthBiasState, typeof(DepthBiasState), 1),
|
new TableItem(MethodOffset.ViewportExtents, typeof(ViewportExtents), Constants.TotalViewports),
|
||||||
new TableItem(MethodOffset.ScissorState, typeof(ScissorState), Constants.TotalViewports),
|
new TableItem(MethodOffset.VertexBufferDrawState, typeof(VertexBufferDrawState), 1),
|
||||||
new TableItem(MethodOffset.StencilBackMasks, typeof(StencilBackMasks), 1),
|
new TableItem(MethodOffset.DepthBiasState, typeof(DepthBiasState), 1),
|
||||||
new TableItem(MethodOffset.RtDepthStencilState, typeof(RtDepthStencilState), 1),
|
new TableItem(MethodOffset.ScissorState, typeof(ScissorState), Constants.TotalViewports),
|
||||||
new TableItem(MethodOffset.VertexAttribState, typeof(VertexAttribState), 16),
|
new TableItem(MethodOffset.StencilBackMasks, typeof(StencilBackMasks), 1),
|
||||||
new TableItem(MethodOffset.RtDepthStencilSize, typeof(Size3D), 1),
|
new TableItem(MethodOffset.RtDepthStencilState, typeof(RtDepthStencilState), 1),
|
||||||
new TableItem(MethodOffset.BlendEnable, typeof(Boolean32), Constants.TotalRenderTargets),
|
new TableItem(MethodOffset.VertexAttribState, typeof(VertexAttribState), Constants.TotalVertexAttribs),
|
||||||
new TableItem(MethodOffset.StencilTestState, typeof(StencilTestState), 1),
|
new TableItem(MethodOffset.RtDepthStencilSize, typeof(Size3D), 1),
|
||||||
new TableItem(MethodOffset.SamplerPoolState, typeof(PoolState), 1),
|
new TableItem(MethodOffset.BlendEnable, typeof(Boolean32), Constants.TotalRenderTargets),
|
||||||
new TableItem(MethodOffset.TexturePoolState, typeof(PoolState), 1),
|
new TableItem(MethodOffset.StencilTestState, typeof(StencilTestState), 1),
|
||||||
new TableItem(MethodOffset.StencilBackTestState, typeof(StencilBackTestState), 1),
|
new TableItem(MethodOffset.SamplerPoolState, typeof(PoolState), 1),
|
||||||
new TableItem(MethodOffset.ShaderBaseAddress, typeof(GpuVa), 1),
|
new TableItem(MethodOffset.TexturePoolState, typeof(PoolState), 1),
|
||||||
new TableItem(MethodOffset.PrimitiveRestartState, typeof(PrimitiveRestartState), 1),
|
new TableItem(MethodOffset.StencilBackTestState, typeof(StencilBackTestState), 1),
|
||||||
new TableItem(MethodOffset.IndexBufferState, typeof(IndexBufferState), 1),
|
new TableItem(MethodOffset.ShaderBaseAddress, typeof(GpuVa), 1),
|
||||||
new TableItem(MethodOffset.VertexBufferInstanced, typeof(Boolean32), 16),
|
new TableItem(MethodOffset.PrimitiveRestartState, typeof(PrimitiveRestartState), 1),
|
||||||
new TableItem(MethodOffset.FaceState, typeof(FaceState), 1),
|
new TableItem(MethodOffset.IndexBufferState, typeof(IndexBufferState), 1),
|
||||||
new TableItem(MethodOffset.RtColorMask, typeof(RtColorMask), Constants.TotalRenderTargets),
|
new TableItem(MethodOffset.VertexBufferInstanced, typeof(Boolean32), Constants.TotalVertexBuffers),
|
||||||
new TableItem(MethodOffset.VertexBufferState, typeof(VertexBufferState), 16),
|
new TableItem(MethodOffset.FaceState, typeof(FaceState), 1),
|
||||||
new TableItem(MethodOffset.BlendConstant, typeof(ColorF), 1),
|
new TableItem(MethodOffset.RtColorMask, typeof(RtColorMask), Constants.TotalRenderTargets),
|
||||||
new TableItem(MethodOffset.BlendState, typeof(BlendState), Constants.TotalRenderTargets),
|
new TableItem(MethodOffset.VertexBufferState, typeof(VertexBufferState), Constants.TotalVertexBuffers),
|
||||||
new TableItem(MethodOffset.VertexBufferEndAddress, typeof(GpuVa), 16),
|
new TableItem(MethodOffset.BlendConstant, typeof(ColorF), 1),
|
||||||
new TableItem(MethodOffset.ShaderState, typeof(ShaderState), 6),
|
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,
|
SyncpointAction = 0xb2,
|
||||||
CopyBuffer = 0xc0,
|
CopyBuffer = 0xc0,
|
||||||
RasterizeEnable = 0xdf,
|
RasterizeEnable = 0xdf,
|
||||||
|
TfBufferState = 0xe0,
|
||||||
CopyBufferParams = 0x100,
|
CopyBufferParams = 0x100,
|
||||||
|
TfState = 0x1c0,
|
||||||
CopyBufferSwizzle = 0x1c2,
|
CopyBufferSwizzle = 0x1c2,
|
||||||
CopyBufferDstTexture = 0x1c3,
|
CopyBufferDstTexture = 0x1c3,
|
||||||
CopyBufferSrcTexture = 0x1ca,
|
CopyBufferSrcTexture = 0x1ca,
|
||||||
|
TfEnable = 0x1d1,
|
||||||
RtColorState = 0x200,
|
RtColorState = 0x200,
|
||||||
CopyTextureControl = 0x223,
|
CopyTextureControl = 0x223,
|
||||||
CopyRegion = 0x22c,
|
CopyRegion = 0x22c,
|
||||||
|
@ -116,6 +119,7 @@ namespace Ryujinx.Graphics.Gpu.State
|
||||||
UniformBufferBindTessEvaluation = 0x914,
|
UniformBufferBindTessEvaluation = 0x914,
|
||||||
UniformBufferBindGeometry = 0x91c,
|
UniformBufferBindGeometry = 0x91c,
|
||||||
UniformBufferBindFragment = 0x924,
|
UniformBufferBindFragment = 0x924,
|
||||||
TextureBufferIndex = 0x982
|
TextureBufferIndex = 0x982,
|
||||||
|
TfVaryingLocations = 0xa00
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,18 +11,19 @@ namespace Ryujinx.Graphics.Gpu.State
|
||||||
VertexShaderInvocations = 5,
|
VertexShaderInvocations = 5,
|
||||||
GeometryShaderInvocations = 7,
|
GeometryShaderInvocations = 7,
|
||||||
GeometryShaderPrimitives = 9,
|
GeometryShaderPrimitives = 9,
|
||||||
|
ZcullStats0 = 0xa,
|
||||||
TransformFeedbackPrimitivesWritten = 0xb,
|
TransformFeedbackPrimitivesWritten = 0xb,
|
||||||
|
ZcullStats1 = 0xc,
|
||||||
|
ZcullStats2 = 0xe,
|
||||||
ClipperInputPrimitives = 0xf,
|
ClipperInputPrimitives = 0xf,
|
||||||
|
ZcullStats3 = 0x10,
|
||||||
ClipperOutputPrimitives = 0x11,
|
ClipperOutputPrimitives = 0x11,
|
||||||
PrimitivesGenerated = 0x12,
|
PrimitivesGenerated = 0x12,
|
||||||
FragmentShaderInvocations = 0x13,
|
FragmentShaderInvocations = 0x13,
|
||||||
SamplesPassed = 0x15,
|
SamplesPassed = 0x15,
|
||||||
|
TransformFeedbackOffset = 0x1a,
|
||||||
TessControlShaderInvocations = 0x1b,
|
TessControlShaderInvocations = 0x1b,
|
||||||
TessEvaluationShaderInvocations = 0x1d,
|
TessEvaluationShaderInvocations = 0x1d,
|
||||||
TessEvaluationShaderPrimitives = 0x1f,
|
TessEvaluationShaderPrimitives = 0x1f
|
||||||
ZcullStats0 = 0x2a,
|
|
||||||
ZcullStats1 = 0x2c,
|
|
||||||
ZcullStats2 = 0x2e,
|
|
||||||
ZcullStats3 = 0x30
|
|
||||||
}
|
}
|
||||||
}
|
}
|
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;
|
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)
|
public static OpenTK.Graphics.OpenGL.StencilOp Convert(this GAL.StencilOp op)
|
||||||
{
|
{
|
||||||
switch (op)
|
switch (op)
|
||||||
|
|
|
@ -45,6 +45,8 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
|
|
||||||
private bool _scissor0Enable = false;
|
private bool _scissor0Enable = false;
|
||||||
|
|
||||||
|
private bool _tfEnabled;
|
||||||
|
|
||||||
ColorF _blendConstant = new ColorF(0, 0, 0, 0);
|
ColorF _blendConstant = new ColorF(0, 0, 0, 0);
|
||||||
|
|
||||||
internal Pipeline()
|
internal Pipeline()
|
||||||
|
@ -76,6 +78,12 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
GL.MemoryBarrier(MemoryBarrierFlags.AllBarrierBits);
|
GL.MemoryBarrier(MemoryBarrierFlags.AllBarrierBits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void BeginTransformFeedback(PrimitiveTopology topology)
|
||||||
|
{
|
||||||
|
GL.BeginTransformFeedback(topology.ConvertToTfType());
|
||||||
|
_tfEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
public void ClearRenderTargetColor(int index, uint componentMask, ColorF color)
|
public void ClearRenderTargetColor(int index, uint componentMask, ColorF color)
|
||||||
{
|
{
|
||||||
GL.ColorMask(
|
GL.ColorMask(
|
||||||
|
@ -512,6 +520,12 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void EndTransformFeedback()
|
||||||
|
{
|
||||||
|
GL.EndTransformFeedback();
|
||||||
|
_tfEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetBlendState(int index, BlendDescriptor blend)
|
public void SetBlendState(int index, BlendDescriptor blend)
|
||||||
{
|
{
|
||||||
if (!blend.Enable)
|
if (!blend.Enable)
|
||||||
|
@ -713,7 +727,17 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
public void SetProgram(IProgram program)
|
public void SetProgram(IProgram program)
|
||||||
{
|
{
|
||||||
_program = (Program)program;
|
_program = (Program)program;
|
||||||
_program.Bind();
|
|
||||||
|
if (_tfEnabled)
|
||||||
|
{
|
||||||
|
GL.PauseTransformFeedback();
|
||||||
|
_program.Bind();
|
||||||
|
GL.ResumeTransformFeedback();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_program.Bind();
|
||||||
|
}
|
||||||
|
|
||||||
SetRenderTargetScale(_fpRenderScale[0]);
|
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)
|
public void SetUniformBuffer(int index, ShaderStage stage, BufferRange buffer)
|
||||||
{
|
{
|
||||||
SetBuffer(index, stage, buffer, isStorage: false);
|
SetBuffer(index, stage, buffer, isStorage: false);
|
||||||
|
|
|
@ -2,6 +2,10 @@ using OpenTK.Graphics.OpenGL;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
|
using Ryujinx.Graphics.Shader.CodeGen.Glsl;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.OpenGL
|
namespace Ryujinx.Graphics.OpenGL
|
||||||
{
|
{
|
||||||
|
@ -31,7 +35,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
private int[] _textureUnits;
|
private int[] _textureUnits;
|
||||||
private int[] _imageUnits;
|
private int[] _imageUnits;
|
||||||
|
|
||||||
public Program(IShader[] shaders)
|
public Program(IShader[] shaders, TransformFeedbackDescriptor[] transformFeedbackDescriptors)
|
||||||
{
|
{
|
||||||
_ubBindingPoints = new int[UbsPerStage * ShaderStages];
|
_ubBindingPoints = new int[UbsPerStage * ShaderStages];
|
||||||
_sbBindingPoints = new int[SbsPerStage * ShaderStages];
|
_sbBindingPoints = new int[SbsPerStage * ShaderStages];
|
||||||
|
@ -67,6 +71,54 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
GL.AttachShader(Handle, shaderHandle);
|
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);
|
GL.LinkProgram(Handle);
|
||||||
|
|
||||||
for (int index = 0; index < shaders.Length; index++)
|
for (int index = 0; index < shaders.Length; index++)
|
||||||
|
|
|
@ -44,9 +44,9 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
return Buffer.Create(size);
|
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)
|
public ISampler CreateSampler(SamplerCreateInfo info)
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
|
|
||||||
public static void Declare(CodeGenContext context, StructuredProgramInfo info)
|
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_gpu_shader_int64 : enable");
|
||||||
context.AppendLine("#extension GL_ARB_shader_ballot : enable");
|
context.AppendLine("#extension GL_ARB_shader_ballot : enable");
|
||||||
context.AppendLine("#extension GL_ARB_shader_group_vote : enable");
|
context.AppendLine("#extension GL_ARB_shader_group_vote : enable");
|
||||||
|
@ -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++)
|
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))
|
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;
|
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;
|
int value = attr.Value;
|
||||||
|
|
||||||
string swzMask = GetSwizzleMask((value >> 2) & 3);
|
char swzMask = GetSwizzleMask((value >> 2) & 3);
|
||||||
|
|
||||||
if (value >= AttributeConsts.UserAttributeBase &&
|
if (value >= AttributeConsts.UserAttributeBase &&
|
||||||
value < AttributeConsts.UserAttributeEnd)
|
value < AttributeConsts.UserAttributeEnd)
|
||||||
|
@ -158,15 +158,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
? DefaultNames.OAttributePrefix
|
? DefaultNames.OAttributePrefix
|
||||||
: DefaultNames.IAttributePrefix;
|
: DefaultNames.IAttributePrefix;
|
||||||
|
|
||||||
string name = $"{prefix}{(value >> 4)}";
|
string name = $"{prefix}{(value >> 4)}_{swzMask}";
|
||||||
|
|
||||||
if (stage == ShaderStage.Geometry && !isOutAttr)
|
if (stage == ShaderStage.Geometry && !isOutAttr)
|
||||||
{
|
{
|
||||||
name += $"[{indexExpr}]";
|
name += $"[{indexExpr}]";
|
||||||
}
|
}
|
||||||
|
|
||||||
name += "." + swzMask;
|
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -264,9 +262,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
return _stagePrefixes[index];
|
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)
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue