Fix transform feedback errors caused by host pause/resume and multiple uses (#1634)

* Fix transform feedback errors caused by host pause/resume

* Fix TFB being used as something else issue with copies

* This is supposed to be StreamCopy
This commit is contained in:
gdkchan 2020-10-25 17:23:42 -03:00 committed by GitHub
parent cf0f0fc4e7
commit 812e32f775
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 92 additions and 30 deletions

View file

@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.GAL
{ {
private static readonly BufferRange _empty = new BufferRange(BufferHandle.Null, 0, 0); private static readonly BufferRange _empty = new BufferRange(BufferHandle.Null, 0, 0);
public BufferRange Empty => _empty; public static BufferRange Empty => _empty;
public BufferHandle Handle { get; } public BufferHandle Handle { get; }

View file

@ -75,7 +75,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 SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers);
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);

View file

@ -587,21 +587,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
_transformFeedbackBuffersDirty = false; _transformFeedbackBuffersDirty = false;
Span<BufferRange> tfbs = stackalloc BufferRange[Constants.TotalTransformFeedbackBuffers];
for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++) for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
{ {
BufferBounds tfb = _transformFeedbackBuffers[index]; BufferBounds tfb = _transformFeedbackBuffers[index];
if (tfb.Address == 0) if (tfb.Address == 0)
{ {
_context.Renderer.Pipeline.SetTransformFeedbackBuffer(index, new BufferRange(BufferHandle.Null, 0, 0)); tfbs[index] = BufferRange.Empty;
continue; continue;
} }
BufferRange buffer = GetBufferRange(tfb.Address, tfb.Size); tfbs[index] = GetBufferRange(tfb.Address, tfb.Size);
_context.Renderer.Pipeline.SetTransformFeedbackBuffer(index, buffer);
} }
_context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
} }
else else
{ {

View file

@ -6,6 +6,11 @@ namespace Ryujinx.Graphics.OpenGL
{ {
static class Buffer static class Buffer
{ {
public static BufferHandle Create()
{
return Handle.FromInt32<BufferHandle>(GL.GenBuffer());
}
public static BufferHandle Create(int size) public static BufferHandle Create(int size)
{ {
int handle = GL.GenBuffer(); int handle = GL.GenBuffer();
@ -40,6 +45,12 @@ namespace Ryujinx.Graphics.OpenGL
return data; return data;
} }
public static void Resize(BufferHandle handle, int size)
{
GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle.ToInt32());
GL.BufferData(BufferTarget.CopyWriteBuffer, size, IntPtr.Zero, BufferUsageHint.StreamCopy);
}
public static void SetData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data) public static void SetData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
{ {
GL.BindBuffer(BufferTarget.CopyWriteBuffer, buffer.ToInt32()); GL.BindBuffer(BufferTarget.CopyWriteBuffer, buffer.ToInt32());

View file

@ -6,5 +6,6 @@
public const int MaxViewports = 16; public const int MaxViewports = 16;
public const int MaxVertexAttribs = 16; public const int MaxVertexAttribs = 16;
public const int MaxVertexBuffers = 16; public const int MaxVertexBuffers = 16;
public const int MaxTransformFeedbackBuffers = 4;
} }
} }

View file

@ -5,7 +5,6 @@ using Ryujinx.Graphics.OpenGL.Image;
using Ryujinx.Graphics.OpenGL.Queries; using Ryujinx.Graphics.OpenGL.Queries;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using System; using System;
using System.Threading;
namespace Ryujinx.Graphics.OpenGL namespace Ryujinx.Graphics.OpenGL
{ {
@ -49,6 +48,10 @@ namespace Ryujinx.Graphics.OpenGL
private bool _scissor0Enable = false; private bool _scissor0Enable = false;
private bool _tfEnabled; private bool _tfEnabled;
private TransformFeedbackPrimitiveType _tfTopology;
private readonly BufferHandle[] _tfbs;
private readonly BufferRange[] _tfbTargets;
private ColorF _blendConstant; private ColorF _blendConstant;
@ -74,6 +77,9 @@ namespace Ryujinx.Graphics.OpenGL
{ {
_cpRenderScale[index] = 1f; _cpRenderScale[index] = 1f;
} }
_tfbs = new BufferHandle[Constants.MaxTransformFeedbackBuffers];
_tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers];
} }
public void Barrier() public void Barrier()
@ -83,7 +89,7 @@ namespace Ryujinx.Graphics.OpenGL
public void BeginTransformFeedback(PrimitiveTopology topology) public void BeginTransformFeedback(PrimitiveTopology topology)
{ {
GL.BeginTransformFeedback(topology.ConvertToTfType()); GL.BeginTransformFeedback(_tfTopology = topology.ConvertToTfType());
_tfEnabled = true; _tfEnabled = true;
} }
@ -175,7 +181,7 @@ namespace Ryujinx.Graphics.OpenGL
return; return;
} }
PrepareForDraw(); PreDraw();
if (_primitiveType == PrimitiveType.Quads) if (_primitiveType == PrimitiveType.Quads)
{ {
@ -190,7 +196,7 @@ namespace Ryujinx.Graphics.OpenGL
DrawImpl(vertexCount, instanceCount, firstVertex, firstInstance); DrawImpl(vertexCount, instanceCount, firstVertex, firstInstance);
} }
_framebuffer.SignalModified(); PostDraw();
} }
private void DrawQuadsImpl( private void DrawQuadsImpl(
@ -293,7 +299,7 @@ namespace Ryujinx.Graphics.OpenGL
return; return;
} }
PrepareForDraw(); PreDraw();
int indexElemSize = 1; int indexElemSize = 1;
@ -335,7 +341,7 @@ namespace Ryujinx.Graphics.OpenGL
firstInstance); firstInstance);
} }
_framebuffer.SignalModified(); PostDraw();
} }
private void DrawQuadsIndexedImpl( private void DrawQuadsIndexedImpl(
@ -790,9 +796,9 @@ namespace Ryujinx.Graphics.OpenGL
if (_tfEnabled) if (_tfEnabled)
{ {
GL.PauseTransformFeedback(); GL.EndTransformFeedback();
_program.Bind(); _program.Bind();
GL.ResumeTransformFeedback(); GL.BeginTransformFeedback(_tfTopology);
} }
else else
{ {
@ -993,19 +999,39 @@ namespace Ryujinx.Graphics.OpenGL
} }
} }
public void SetTransformFeedbackBuffer(int index, BufferRange buffer) public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
{ {
const BufferRangeTarget target = BufferRangeTarget.TransformFeedbackBuffer; if (_tfEnabled)
{
GL.EndTransformFeedback();
}
int count = Math.Min(buffers.Length, Constants.MaxTransformFeedbackBuffers);
for (int i = 0; i < count; i++)
{
BufferRange buffer = buffers[i];
_tfbTargets[i] = buffer;
if (buffer.Handle == BufferHandle.Null)
{
GL.BindBufferBase(BufferRangeTarget.TransformFeedbackBuffer, i, 0);
continue;
}
if (_tfbs[i] == BufferHandle.Null)
{
_tfbs[i] = Buffer.Create();
}
Buffer.Resize(_tfbs[i], buffer.Size);
Buffer.Copy(buffer.Handle, _tfbs[i], buffer.Offset, 0, buffer.Size);
GL.BindBufferBase(BufferRangeTarget.TransformFeedbackBuffer, i, _tfbs[i].ToInt32());
}
if (_tfEnabled) if (_tfEnabled)
{ {
GL.PauseTransformFeedback(); GL.BeginTransformFeedback(_tfTopology);
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);
} }
} }
@ -1104,7 +1130,7 @@ namespace Ryujinx.Graphics.OpenGL
? BufferRangeTarget.ShaderStorageBuffer ? BufferRangeTarget.ShaderStorageBuffer
: BufferRangeTarget.UniformBuffer; : BufferRangeTarget.UniformBuffer;
if (buffer.Handle == null) if (buffer.Handle == BufferHandle.Null)
{ {
GL.BindBufferRange(target, bindingPoint, 0, IntPtr.Zero, 0); GL.BindBufferRange(target, bindingPoint, 0, IntPtr.Zero, 0);
return; return;
@ -1237,7 +1263,7 @@ namespace Ryujinx.Graphics.OpenGL
} }
} }
private void PrepareForDraw() private void PreDraw()
{ {
_vertexArray.Validate(); _vertexArray.Validate();
@ -1247,6 +1273,22 @@ namespace Ryujinx.Graphics.OpenGL
} }
} }
private void PostDraw()
{
_framebuffer?.SignalModified();
if (_tfEnabled)
{
for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++)
{
if (_tfbTargets[i].Handle != BufferHandle.Null)
{
Buffer.Copy(_tfbs[i], _tfbTargets[i].Handle, 0, _tfbTargets[i].Offset, _tfbTargets[i].Size);
}
}
}
}
private void RestoreComponentMask(int index) private void RestoreComponentMask(int index)
{ {
GL.ColorMask( GL.ColorMask(
@ -1319,6 +1361,15 @@ namespace Ryujinx.Graphics.OpenGL
public void Dispose() public void Dispose()
{ {
for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++)
{
if (_tfbs[i] != BufferHandle.Null)
{
Buffer.Delete(_tfbs[i]);
_tfbs[i] = BufferHandle.Null;
}
}
_framebuffer?.Dispose(); _framebuffer?.Dispose();
_vertexArray?.Dispose(); _vertexArray?.Dispose();
} }

View file

@ -131,8 +131,6 @@ namespace Ryujinx.Graphics.OpenGL
CheckProgramLink(); CheckProgramLink();
Bind();
int ubBindingPoint = 0; int ubBindingPoint = 0;
int sbBindingPoint = 0; int sbBindingPoint = 0;
int textureUnit = 0; int textureUnit = 0;
@ -189,7 +187,7 @@ namespace Ryujinx.Graphics.OpenGL
continue; continue;
} }
GL.Uniform1(location, textureUnit); GL.ProgramUniform1(Handle, location, textureUnit);
int uIndex = (int)shader.Stage << TexStageShift | samplerIndex++; int uIndex = (int)shader.Stage << TexStageShift | samplerIndex++;
@ -209,7 +207,7 @@ namespace Ryujinx.Graphics.OpenGL
continue; continue;
} }
GL.Uniform1(location, imageUnit); GL.ProgramUniform1(Handle, location, imageUnit);
int uIndex = (int)shader.Stage << ImgStageShift | imageIndex++; int uIndex = (int)shader.Stage << ImgStageShift | imageIndex++;