ryujinx/Ryujinx.Graphics.OpenGL/Program.cs
riperiperi 484eb645ae
Implement Zero-Configuration Resolution Scaling (#1365)
* Initial implementation of Render Target Scaling

Works with most games I have. No GUI option right now, it is hardcoded.

Missing handling for texelFetch operation.

* Realtime Configuration, refactoring.

* texelFetch scaling on fragment shader (WIP)

* Improve Shader-Side changes.

* Fix potential crash when no color/depth bound

* Workaround random uses of textures in compute.

This was blacklisting textures in a few games despite causing no bugs. Will eventually add full support so this doesn't break anything.

* Fix scales oscillating when changing between non-native scales.

* Scaled textures on compute, cleanup, lazier uniform update.

* Cleanup.

* Fix stupidity

* Address Thog Feedback.

* Cover most of GDK's feedback (two comments remain)

* Fix bad rename

* Move IsDepthStencil to FormatExtensions, add docs.

* Fix default config, square texture detection.

* Three final fixes:

- Nearest copy when texture is integer format.
- Texture2D -> Texture3D copy correctly blacklists the texture before trying an unscaled copy (caused driver error)
- Discount small textures.

* Remove scale threshold.

Not needed right now - we'll see if we run into problems.

* All CPU modification blacklists scale.

* Fix comment.
2020-07-07 04:41:07 +02:00

224 lines
6.6 KiB
C#

using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
namespace Ryujinx.Graphics.OpenGL
{
class Program : IProgram
{
private const int ShaderStages = 6;
private const int UbStageShift = 5;
private const int SbStageShift = 4;
private const int TexStageShift = 5;
private const int ImgStageShift = 3;
private const int UbsPerStage = 1 << UbStageShift;
private const int SbsPerStage = 1 << SbStageShift;
private const int TexsPerStage = 1 << TexStageShift;
private const int ImgsPerStage = 1 << ImgStageShift;
public int Handle { get; private set; }
public int FragmentRenderScaleUniform { get; }
public int ComputeRenderScaleUniform { get; }
public bool IsLinked { get; private set; }
private int[] _ubBindingPoints;
private int[] _sbBindingPoints;
private int[] _textureUnits;
private int[] _imageUnits;
public Program(IShader[] shaders)
{
_ubBindingPoints = new int[UbsPerStage * ShaderStages];
_sbBindingPoints = new int[SbsPerStage * ShaderStages];
_textureUnits = new int[TexsPerStage * ShaderStages];
_imageUnits = new int[ImgsPerStage * ShaderStages];
for (int index = 0; index < _ubBindingPoints.Length; index++)
{
_ubBindingPoints[index] = -1;
}
for (int index = 0; index < _sbBindingPoints.Length; index++)
{
_sbBindingPoints[index] = -1;
}
for (int index = 0; index < _textureUnits.Length; index++)
{
_textureUnits[index] = -1;
}
for (int index = 0; index < _imageUnits.Length; index++)
{
_imageUnits[index] = -1;
}
Handle = GL.CreateProgram();
for (int index = 0; index < shaders.Length; index++)
{
int shaderHandle = ((Shader)shaders[index]).Handle;
GL.AttachShader(Handle, shaderHandle);
}
GL.LinkProgram(Handle);
for (int index = 0; index < shaders.Length; index++)
{
int shaderHandle = ((Shader)shaders[index]).Handle;
GL.DetachShader(Handle, shaderHandle);
}
CheckProgramLink();
Bind();
int ubBindingPoint = 0;
int sbBindingPoint = 0;
int textureUnit = 0;
int imageUnit = 0;
for (int index = 0; index < shaders.Length; index++)
{
Shader shader = (Shader)shaders[index];
foreach (BufferDescriptor descriptor in shader.Info.CBuffers)
{
int location = GL.GetUniformBlockIndex(Handle, descriptor.Name);
if (location < 0)
{
continue;
}
GL.UniformBlockBinding(Handle, location, ubBindingPoint);
int bpIndex = (int)shader.Stage << UbStageShift | descriptor.Slot;
_ubBindingPoints[bpIndex] = ubBindingPoint;
ubBindingPoint++;
}
foreach (BufferDescriptor descriptor in shader.Info.SBuffers)
{
int location = GL.GetProgramResourceIndex(Handle, ProgramInterface.ShaderStorageBlock, descriptor.Name);
if (location < 0)
{
continue;
}
GL.ShaderStorageBlockBinding(Handle, location, sbBindingPoint);
int bpIndex = (int)shader.Stage << SbStageShift | descriptor.Slot;
_sbBindingPoints[bpIndex] = sbBindingPoint;
sbBindingPoint++;
}
int samplerIndex = 0;
foreach (TextureDescriptor descriptor in shader.Info.Textures)
{
int location = GL.GetUniformLocation(Handle, descriptor.Name);
if (location < 0)
{
continue;
}
GL.Uniform1(location, textureUnit);
int uIndex = (int)shader.Stage << TexStageShift | samplerIndex++;
_textureUnits[uIndex] = textureUnit;
textureUnit++;
}
int imageIndex = 0;
foreach (TextureDescriptor descriptor in shader.Info.Images)
{
int location = GL.GetUniformLocation(Handle, descriptor.Name);
if (location < 0)
{
continue;
}
GL.Uniform1(location, imageUnit);
int uIndex = (int)shader.Stage << ImgStageShift | imageIndex++;
_imageUnits[uIndex] = imageUnit;
imageUnit++;
}
}
FragmentRenderScaleUniform = GL.GetUniformLocation(Handle, "fp_renderScale");
ComputeRenderScaleUniform = GL.GetUniformLocation(Handle, "cp_renderScale");
}
public void Bind()
{
GL.UseProgram(Handle);
}
public int GetUniformBufferBindingPoint(ShaderStage stage, int index)
{
return _ubBindingPoints[(int)stage << UbStageShift | index];
}
public int GetStorageBufferBindingPoint(ShaderStage stage, int index)
{
return _sbBindingPoints[(int)stage << SbStageShift | index];
}
public int GetTextureUnit(ShaderStage stage, int index)
{
return _textureUnits[(int)stage << TexStageShift | index];
}
public int GetImageUnit(ShaderStage stage, int index)
{
return _imageUnits[(int)stage << ImgStageShift | index];
}
private void CheckProgramLink()
{
GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out int status);
if (status == 0)
{
// Use GL.GetProgramInfoLog(Handle), it may be too long to print on the log.
Logger.PrintDebug(LogClass.Gpu, "Shader linking failed.");
}
else
{
IsLinked = true;
}
}
public void Dispose()
{
if (Handle != 0)
{
GL.DeleteProgram(Handle);
Handle = 0;
}
}
}
}