From 812e32f7753d452f5c6776fa18e2b2a26b4ff3bb Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 25 Oct 2020 17:23:42 -0300 Subject: [PATCH] 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 --- Ryujinx.Graphics.GAL/BufferRange.cs | 2 +- Ryujinx.Graphics.GAL/IPipeline.cs | 2 +- Ryujinx.Graphics.Gpu/Memory/BufferManager.cs | 11 +-- Ryujinx.Graphics.OpenGL/Buffer.cs | 11 +++ Ryujinx.Graphics.OpenGL/Constants.cs | 1 + Ryujinx.Graphics.OpenGL/Pipeline.cs | 89 +++++++++++++++----- Ryujinx.Graphics.OpenGL/Program.cs | 6 +- 7 files changed, 92 insertions(+), 30 deletions(-) diff --git a/Ryujinx.Graphics.GAL/BufferRange.cs b/Ryujinx.Graphics.GAL/BufferRange.cs index 34d523f9fc..9a030c8089 100644 --- a/Ryujinx.Graphics.GAL/BufferRange.cs +++ b/Ryujinx.Graphics.GAL/BufferRange.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.GAL { private static readonly BufferRange _empty = new BufferRange(BufferHandle.Null, 0, 0); - public BufferRange Empty => _empty; + public static BufferRange Empty => _empty; public BufferHandle Handle { get; } diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs index a7f1389387..818712c32a 100644 --- a/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/Ryujinx.Graphics.GAL/IPipeline.cs @@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.GAL void SetTexture(int index, ShaderStage stage, ITexture texture); - void SetTransformFeedbackBuffer(int index, BufferRange buffer); + void SetTransformFeedbackBuffers(ReadOnlySpan buffers); void SetUniformBuffer(int index, ShaderStage stage, BufferRange buffer); void SetUserClipDistance(int index, bool enableClip); diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index ee1be74b17..eec545b917 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -587,21 +587,22 @@ namespace Ryujinx.Graphics.Gpu.Memory { _transformFeedbackBuffersDirty = false; + Span tfbs = stackalloc BufferRange[Constants.TotalTransformFeedbackBuffers]; + 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)); - + tfbs[index] = BufferRange.Empty; continue; } - BufferRange buffer = GetBufferRange(tfb.Address, tfb.Size); - - _context.Renderer.Pipeline.SetTransformFeedbackBuffer(index, buffer); + tfbs[index] = GetBufferRange(tfb.Address, tfb.Size); } + + _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs); } else { diff --git a/Ryujinx.Graphics.OpenGL/Buffer.cs b/Ryujinx.Graphics.OpenGL/Buffer.cs index e8fd9a6bf0..89216b834c 100644 --- a/Ryujinx.Graphics.OpenGL/Buffer.cs +++ b/Ryujinx.Graphics.OpenGL/Buffer.cs @@ -6,6 +6,11 @@ namespace Ryujinx.Graphics.OpenGL { static class Buffer { + public static BufferHandle Create() + { + return Handle.FromInt32(GL.GenBuffer()); + } + public static BufferHandle Create(int size) { int handle = GL.GenBuffer(); @@ -40,6 +45,12 @@ namespace Ryujinx.Graphics.OpenGL 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 data) { GL.BindBuffer(BufferTarget.CopyWriteBuffer, buffer.ToInt32()); diff --git a/Ryujinx.Graphics.OpenGL/Constants.cs b/Ryujinx.Graphics.OpenGL/Constants.cs index 9775b240d9..8817011a97 100644 --- a/Ryujinx.Graphics.OpenGL/Constants.cs +++ b/Ryujinx.Graphics.OpenGL/Constants.cs @@ -6,5 +6,6 @@ public const int MaxViewports = 16; public const int MaxVertexAttribs = 16; public const int MaxVertexBuffers = 16; + public const int MaxTransformFeedbackBuffers = 4; } } diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 2650e9ee5a..6277fe168d 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -5,7 +5,6 @@ using Ryujinx.Graphics.OpenGL.Image; using Ryujinx.Graphics.OpenGL.Queries; using Ryujinx.Graphics.Shader; using System; -using System.Threading; namespace Ryujinx.Graphics.OpenGL { @@ -49,6 +48,10 @@ namespace Ryujinx.Graphics.OpenGL private bool _scissor0Enable = false; private bool _tfEnabled; + private TransformFeedbackPrimitiveType _tfTopology; + + private readonly BufferHandle[] _tfbs; + private readonly BufferRange[] _tfbTargets; private ColorF _blendConstant; @@ -74,6 +77,9 @@ namespace Ryujinx.Graphics.OpenGL { _cpRenderScale[index] = 1f; } + + _tfbs = new BufferHandle[Constants.MaxTransformFeedbackBuffers]; + _tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers]; } public void Barrier() @@ -83,7 +89,7 @@ namespace Ryujinx.Graphics.OpenGL public void BeginTransformFeedback(PrimitiveTopology topology) { - GL.BeginTransformFeedback(topology.ConvertToTfType()); + GL.BeginTransformFeedback(_tfTopology = topology.ConvertToTfType()); _tfEnabled = true; } @@ -175,7 +181,7 @@ namespace Ryujinx.Graphics.OpenGL return; } - PrepareForDraw(); + PreDraw(); if (_primitiveType == PrimitiveType.Quads) { @@ -190,7 +196,7 @@ namespace Ryujinx.Graphics.OpenGL DrawImpl(vertexCount, instanceCount, firstVertex, firstInstance); } - _framebuffer.SignalModified(); + PostDraw(); } private void DrawQuadsImpl( @@ -293,7 +299,7 @@ namespace Ryujinx.Graphics.OpenGL return; } - PrepareForDraw(); + PreDraw(); int indexElemSize = 1; @@ -335,7 +341,7 @@ namespace Ryujinx.Graphics.OpenGL firstInstance); } - _framebuffer.SignalModified(); + PostDraw(); } private void DrawQuadsIndexedImpl( @@ -790,9 +796,9 @@ namespace Ryujinx.Graphics.OpenGL if (_tfEnabled) { - GL.PauseTransformFeedback(); + GL.EndTransformFeedback(); _program.Bind(); - GL.ResumeTransformFeedback(); + GL.BeginTransformFeedback(_tfTopology); } else { @@ -993,19 +999,39 @@ namespace Ryujinx.Graphics.OpenGL } } - public void SetTransformFeedbackBuffer(int index, BufferRange buffer) + public void SetTransformFeedbackBuffers(ReadOnlySpan 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) { - 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); + GL.BeginTransformFeedback(_tfTopology); } } @@ -1104,7 +1130,7 @@ namespace Ryujinx.Graphics.OpenGL ? BufferRangeTarget.ShaderStorageBuffer : BufferRangeTarget.UniformBuffer; - if (buffer.Handle == null) + if (buffer.Handle == BufferHandle.Null) { GL.BindBufferRange(target, bindingPoint, 0, IntPtr.Zero, 0); return; @@ -1237,7 +1263,7 @@ namespace Ryujinx.Graphics.OpenGL } } - private void PrepareForDraw() + private void PreDraw() { _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) { GL.ColorMask( @@ -1319,6 +1361,15 @@ namespace Ryujinx.Graphics.OpenGL 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(); _vertexArray?.Dispose(); } diff --git a/Ryujinx.Graphics.OpenGL/Program.cs b/Ryujinx.Graphics.OpenGL/Program.cs index 6e253140e7..babe17a0d7 100644 --- a/Ryujinx.Graphics.OpenGL/Program.cs +++ b/Ryujinx.Graphics.OpenGL/Program.cs @@ -131,8 +131,6 @@ namespace Ryujinx.Graphics.OpenGL CheckProgramLink(); - Bind(); - int ubBindingPoint = 0; int sbBindingPoint = 0; int textureUnit = 0; @@ -189,7 +187,7 @@ namespace Ryujinx.Graphics.OpenGL continue; } - GL.Uniform1(location, textureUnit); + GL.ProgramUniform1(Handle, location, textureUnit); int uIndex = (int)shader.Stage << TexStageShift | samplerIndex++; @@ -209,7 +207,7 @@ namespace Ryujinx.Graphics.OpenGL continue; } - GL.Uniform1(location, imageUnit); + GL.ProgramUniform1(Handle, location, imageUnit); int uIndex = (int)shader.Stage << ImgStageShift | imageIndex++;