mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-08-19 23:28:42 +02:00
* Add support for render scale to vertex stage. Occasionally games read off textureSize on the vertex stage to inform the fragment shader what size a texture is without querying in there. Scales were not present in the vertex shader to correct the sizes, so games were providing the raw upscaled texture size to the fragment shader, which was incorrect. One downside is that the fragment and vertex support buffer description must be identical, so the full size scales array must be defined when used. I don't think this will have an impact though. Another is that the fragment texture count must be updated when vertex shader textures are used. I'd like to correct this so that the update is folded into the update for the scales. Also cleans up a bunch of things, like it making no sense to call CommitRenderScale for each stage. Fixes render scale causing a weird offset bloom in Super Mario Party and Clubhouse Games. Clubhouse Games still has a pixelated look in a number of its games due to something else it does in the shader. * Split out support buffer update, lazy updates. * Commit support buffer before compute dispatch * Remove unnecessary qualifier. * Address Feedback
229 lines
7.3 KiB
C#
229 lines
7.3 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;
|
|
|
|
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
|
|
|
|
internal PersistentBuffers PersistentBuffers { get; }
|
|
|
|
internal ResourcePool ResourcePool { get; }
|
|
|
|
internal int BufferCount { get; private set; }
|
|
|
|
public string GpuVendor { get; private set; }
|
|
public string GpuRenderer { get; private set; }
|
|
public string GpuVersion { get; private set; }
|
|
|
|
public bool PreferThreading => true;
|
|
|
|
public Renderer()
|
|
{
|
|
_pipeline = new Pipeline();
|
|
_counters = new Counters();
|
|
_window = new Window(this);
|
|
_textureCopy = new TextureCopy(this);
|
|
_backgroundTextureCopy = new TextureCopy(this);
|
|
_sync = new Sync();
|
|
PersistentBuffers = new PersistentBuffers();
|
|
ResourcePool = new ResourcePool();
|
|
}
|
|
|
|
public IShader CompileShader(ShaderStage stage, string code)
|
|
{
|
|
return new Shader(stage, code);
|
|
}
|
|
|
|
public BufferHandle CreateBuffer(int size)
|
|
{
|
|
BufferCount++;
|
|
|
|
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(this, info);
|
|
}
|
|
else
|
|
{
|
|
return ResourcePool.GetTextureOrNull(info, scaleFactor) ?? new TextureStorage(this, info, scaleFactor).CreateDefaultView();
|
|
}
|
|
}
|
|
|
|
public void DeleteBuffer(BufferHandle buffer)
|
|
{
|
|
Buffer.Delete(buffer);
|
|
}
|
|
|
|
public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
|
{
|
|
return Buffer.GetData(this, buffer, offset, size);
|
|
}
|
|
|
|
public Capabilities GetCapabilities()
|
|
{
|
|
return new Capabilities(
|
|
hasFrontFacingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows,
|
|
hasVectorIndexingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows,
|
|
supportsAstcCompression: HwCapabilities.SupportsAstcCompression,
|
|
supportsR4G4Format: false,
|
|
supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock,
|
|
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
|
|
supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted,
|
|
supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
|
|
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
|
|
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
|
|
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
|
|
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
|
|
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
|
|
maximumComputeSharedMemorySize: HwCapabilities.MaximumComputeSharedMemorySize,
|
|
maximumSupportedAnisotropy: HwCapabilities.MaximumSupportedAnisotropy,
|
|
storageBufferOffsetAlignment: 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, bool hostReserved)
|
|
{
|
|
return _counters.QueueReport(type, resultHandler, _pipeline.DrawCount, hostReserved);
|
|
}
|
|
|
|
public void Initialize(GraphicsDebugLevel glLogLevel)
|
|
{
|
|
Debugger.Initialize(glLogLevel);
|
|
|
|
PrintGpuInformation();
|
|
|
|
if (HwCapabilities.SupportsParallelShaderCompile)
|
|
{
|
|
GL.Arb.MaxShaderCompilerThreads(Math.Min(Environment.ProcessorCount, 8));
|
|
}
|
|
|
|
_pipeline.Initialize(this);
|
|
_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, bool alwaysBackground = false)
|
|
{
|
|
// alwaysBackground is ignored, since we cannot switch from the current context.
|
|
|
|
if (IOpenGLContext.HasContext())
|
|
{
|
|
action(); // We have a context already - use that (assuming it is the main one).
|
|
}
|
|
else
|
|
{
|
|
_window.BackgroundContext.Invoke(action);
|
|
}
|
|
}
|
|
|
|
public void InitializeBackgroundContext(IOpenGLContext baseContext)
|
|
{
|
|
_window.InitializeBackgroundContext(baseContext);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_textureCopy.Dispose();
|
|
_backgroundTextureCopy.Dispose();
|
|
PersistentBuffers.Dispose();
|
|
ResourcePool.Dispose();
|
|
_pipeline.Dispose();
|
|
_window.Dispose();
|
|
_counters.Dispose();
|
|
_sync.Dispose();
|
|
}
|
|
|
|
public IProgram LoadProgramBinary(byte[] programBinary)
|
|
{
|
|
return new Program(programBinary);
|
|
}
|
|
|
|
public void CreateSync(ulong id)
|
|
{
|
|
_sync.Create(id);
|
|
}
|
|
|
|
public void WaitSync(ulong id)
|
|
{
|
|
_sync.Wait(id);
|
|
}
|
|
|
|
public void Screenshot()
|
|
{
|
|
_window.ScreenCaptureRequested = true;
|
|
}
|
|
|
|
public void OnScreenCaptured(ScreenCaptureImageInfo bitmap)
|
|
{
|
|
ScreenCaptured?.Invoke(this, bitmap);
|
|
}
|
|
}
|
|
}
|