Ryujinx/Ryujinx.Graphics.OpenGL/Renderer.cs
riperiperi a1f77a5b6a
Implement lazy flush-on-read for Buffers (SSBO/Copy) (#1790)
* Initial implementation of buffer flush (VERY WIP)

* Host shaders need to be rebuilt for the SSBO write flag.

* New approach with reserved regions and gl sync

* Fix a ton of buffer issues.

* Remove unused buffer unmapped behaviour

* Revert "Remove unused buffer unmapped behaviour"

This reverts commit f1700e52fb8760180ac5e0987a07d409d1e70ece.

* Delete modified ranges on unmap

Fixes potential crashes in Super Smash Bros, where a previously modified range could lie on either side of an unmap.

* Cache some more delegates.

* Dispose Sync on Close

* Also create host sync for GPFifo syncpoint increment.

* Copy buffer optimization, add docs

* Fix race condition with OpenGL Sync

* Enable read tracking on CommandBuffer, insert syncpoint on WaitForIdle

* Performance: Only flush individual pages of SSBO at a time

This avoids flushing large amounts of data when only a small amount is actually used.

* Signal Modified rather than flushing after clear

* Fix some docs and code style.

* Introduce a new test for tracking memory protection.

Sucessfully demonstrates that the bug causing write protection to be cleared by a read action has been fixed. (these tests fail on master)

* Address Comments

* Add host sync for SetReference

This ensures that any indirect draws will correctly flush any related buffer data written before them. Fixes some flashing and misplaced world geometry in MH rise.

* Make PageAlign static

* Re-enable read tracking, for reads.
2021-01-17 17:08:06 -03:00

199 lines
5.6 KiB
C#

using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.OpenGL.Image;
using Ryujinx.Graphics.OpenGL.Queries;
using Ryujinx.Graphics.Shader;
using System;
namespace Ryujinx.Graphics.OpenGL
{
public sealed class Renderer : IRenderer
{
private readonly Pipeline _pipeline;
public IPipeline Pipeline => _pipeline;
private readonly Counters _counters;
private readonly Window _window;
public IWindow Window => _window;
private TextureCopy _textureCopy;
private TextureCopy _backgroundTextureCopy;
internal TextureCopy TextureCopy => BackgroundContextWorker.InBackground ? _backgroundTextureCopy : _textureCopy;
private Sync _sync;
internal ResourcePool ResourcePool { get; }
public string GpuVendor { get; private set; }
public string GpuRenderer { get; private set; }
public string GpuVersion { get; private set; }
public Renderer()
{
_pipeline = new Pipeline();
_counters = new Counters();
_window = new Window(this);
_textureCopy = new TextureCopy(this);
_backgroundTextureCopy = new TextureCopy(this);
_sync = new Sync();
ResourcePool = new ResourcePool();
}
public IShader CompileShader(ShaderStage stage, string code)
{
return new Shader(stage, code);
}
public BufferHandle CreateBuffer(int size)
{
return Buffer.Create(size);
}
public IProgram CreateProgram(IShader[] shaders, TransformFeedbackDescriptor[] transformFeedbackDescriptors)
{
return new Program(shaders, transformFeedbackDescriptors);
}
public ISampler CreateSampler(SamplerCreateInfo info)
{
return new Sampler(info);
}
public ITexture CreateTexture(TextureCreateInfo info, float scaleFactor)
{
if (info.Target == Target.TextureBuffer)
{
return new TextureBuffer(info);
}
else
{
return ResourcePool.GetTextureOrNull(info, scaleFactor) ?? new TextureStorage(this, info, scaleFactor).CreateDefaultView();
}
}
public void DeleteBuffer(BufferHandle buffer)
{
Buffer.Delete(buffer);
}
public byte[] GetBufferData(BufferHandle buffer, int offset, int size)
{
return Buffer.GetData(buffer, offset, size);
}
public Capabilities GetCapabilities()
{
return new Capabilities(
HwCapabilities.SupportsAstcCompression,
HwCapabilities.SupportsImageLoadFormatted,
HwCapabilities.SupportsNonConstantTextureOffset,
HwCapabilities.SupportsViewportSwizzle,
HwCapabilities.MaximumComputeSharedMemorySize,
HwCapabilities.MaximumSupportedAnisotropy,
HwCapabilities.StorageBufferOffsetAlignment);
}
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
{
Buffer.SetData(buffer, offset, data);
}
public void UpdateCounters()
{
_counters.Update();
}
public void PreFrame()
{
_sync.Cleanup();
ResourcePool.Tick();
}
public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler)
{
return _counters.QueueReport(type, resultHandler, _pipeline.DrawCount);
}
public void Initialize(GraphicsDebugLevel glLogLevel)
{
Debugger.Initialize(glLogLevel);
PrintGpuInformation();
_counters.Initialize();
}
private void PrintGpuInformation()
{
GpuVendor = GL.GetString(StringName.Vendor);
GpuRenderer = GL.GetString(StringName.Renderer);
GpuVersion = GL.GetString(StringName.Version);
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
}
public void ResetCounter(CounterType type)
{
_counters.QueueReset(type);
}
public void BackgroundContextAction(Action action)
{
if (GraphicsContext.CurrentContext != null)
{
action(); // We have a context already - use that (assuming it is the main one).
}
else
{
_window.BackgroundContext.Invoke(action);
}
}
public void InitializeBackgroundContext(IGraphicsContext baseContext)
{
_window.InitializeBackgroundContext(baseContext);
}
public void Dispose()
{
_textureCopy.Dispose();
_backgroundTextureCopy.Dispose();
ResourcePool.Dispose();
_pipeline.Dispose();
_window.Dispose();
_counters.Dispose();
_sync.Dispose();
}
public IProgram LoadProgramBinary(byte[] programBinary)
{
Program program = new Program(programBinary);
if (program.IsLinked)
{
return program;
}
program.Dispose();
return null;
}
public void CreateSync(ulong id)
{
_sync.Create(id);
}
public void WaitSync(ulong id)
{
_sync.Wait(id);
}
}
}