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:
gdkchan
2020-07-15 00:01:10 -03:00
committed by GitHub
parent 16dafe6316
commit 788ca6a411
23 changed files with 468 additions and 68 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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++)

View File

@ -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)