Add Support for Post Processing Effects (#3616)
* Add Post Processing Effects * fix events and shader issues * fix gtk upscale slider value * fix bgra games * don't swap swizzle if already swapped * restore opengl texture state after effects run * addressed review * use single pipeline for smaa and fsr * call finish on all pipelines * addressed review * attempt fix file case * attempt fixing file case * fix filter level tick frequency * adjust filter slider margins * replace fxaa shaders with original shader * addressed review
This commit is contained in:
177
Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs
Normal file
177
Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs
Normal file
@ -0,0 +1,177 @@
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.OpenGL.Image;
|
||||
using System;
|
||||
using static Ryujinx.Graphics.OpenGL.Effects.ShaderHelper;
|
||||
|
||||
namespace Ryujinx.Graphics.OpenGL.Effects
|
||||
{
|
||||
internal class FsrScalingFilter : IScalingFilter
|
||||
{
|
||||
private readonly OpenGLRenderer _renderer;
|
||||
private int _inputUniform;
|
||||
private int _outputUniform;
|
||||
private int _sharpeningUniform;
|
||||
private int _srcX0Uniform;
|
||||
private int _srcX1Uniform;
|
||||
private int _srcY0Uniform;
|
||||
private int _scalingShaderProgram;
|
||||
private int _sharpeningShaderProgram;
|
||||
private float _scale = 1;
|
||||
private int _srcY1Uniform;
|
||||
private int _dstX0Uniform;
|
||||
private int _dstX1Uniform;
|
||||
private int _dstY0Uniform;
|
||||
private int _dstY1Uniform;
|
||||
private int _scaleXUniform;
|
||||
private int _scaleYUniform;
|
||||
private TextureStorage _intermediaryTexture;
|
||||
|
||||
public float Level
|
||||
{
|
||||
get => _scale;
|
||||
set
|
||||
{
|
||||
_scale = MathF.Max(0.01f, value);
|
||||
}
|
||||
}
|
||||
|
||||
public FsrScalingFilter(OpenGLRenderer renderer, IPostProcessingEffect filter)
|
||||
{
|
||||
Initialize();
|
||||
|
||||
_renderer = renderer;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_scalingShaderProgram != 0)
|
||||
{
|
||||
GL.DeleteProgram(_scalingShaderProgram);
|
||||
GL.DeleteProgram(_sharpeningShaderProgram);
|
||||
}
|
||||
|
||||
_intermediaryTexture?.Dispose();
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
var scalingShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl");
|
||||
var sharpeningShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl");
|
||||
var fsrA = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h");
|
||||
var fsr1 = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h");
|
||||
|
||||
scalingShader = scalingShader.Replace("#include \"ffx_a.h\"", fsrA);
|
||||
scalingShader = scalingShader.Replace("#include \"ffx_fsr1.h\"", fsr1);
|
||||
sharpeningShader = sharpeningShader.Replace("#include \"ffx_a.h\"", fsrA);
|
||||
sharpeningShader = sharpeningShader.Replace("#include \"ffx_fsr1.h\"", fsr1);
|
||||
|
||||
_scalingShaderProgram = CompileProgram(scalingShader, ShaderType.ComputeShader);
|
||||
_sharpeningShaderProgram = CompileProgram(sharpeningShader, ShaderType.ComputeShader);
|
||||
|
||||
_inputUniform = GL.GetUniformLocation(_scalingShaderProgram, "Source");
|
||||
_outputUniform = GL.GetUniformLocation(_scalingShaderProgram, "imgOutput");
|
||||
_sharpeningUniform = GL.GetUniformLocation(_sharpeningShaderProgram, "sharpening");
|
||||
|
||||
_srcX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX0");
|
||||
_srcX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX1");
|
||||
_srcY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY0");
|
||||
_srcY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY1");
|
||||
_dstX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX0");
|
||||
_dstX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX1");
|
||||
_dstY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY0");
|
||||
_dstY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY1");
|
||||
_scaleXUniform = GL.GetUniformLocation(_scalingShaderProgram, "scaleX");
|
||||
_scaleYUniform = GL.GetUniformLocation(_scalingShaderProgram, "scaleY");
|
||||
}
|
||||
|
||||
public void Run(
|
||||
TextureView view,
|
||||
TextureView destinationTexture,
|
||||
int width,
|
||||
int height,
|
||||
Extents2D source,
|
||||
Extents2D destination)
|
||||
{
|
||||
if (_intermediaryTexture == null || _intermediaryTexture.Info.Width != width || _intermediaryTexture.Info.Height != height)
|
||||
{
|
||||
_intermediaryTexture?.Dispose();
|
||||
var originalInfo = view.Info;
|
||||
var info = new TextureCreateInfo(width,
|
||||
height,
|
||||
originalInfo.Depth,
|
||||
originalInfo.Levels,
|
||||
originalInfo.Samples,
|
||||
originalInfo.BlockWidth,
|
||||
originalInfo.BlockHeight,
|
||||
originalInfo.BytesPerPixel,
|
||||
originalInfo.Format,
|
||||
originalInfo.DepthStencilMode,
|
||||
originalInfo.Target,
|
||||
originalInfo.SwizzleR,
|
||||
originalInfo.SwizzleG,
|
||||
originalInfo.SwizzleB,
|
||||
originalInfo.SwizzleA);
|
||||
|
||||
_intermediaryTexture = new TextureStorage(_renderer, info, view.ScaleFactor);
|
||||
_intermediaryTexture.CreateDefaultView();
|
||||
}
|
||||
|
||||
var textureView = _intermediaryTexture.CreateView(_intermediaryTexture.Info, 0, 0) as TextureView;
|
||||
|
||||
int previousProgram = GL.GetInteger(GetPName.CurrentProgram);
|
||||
int previousUnit = GL.GetInteger(GetPName.ActiveTexture);
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
int previousTextureBinding = GL.GetInteger(GetPName.TextureBinding2D);
|
||||
|
||||
GL.BindImageTexture(0, textureView.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||
|
||||
int threadGroupWorkRegionDim = 16;
|
||||
int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||
|
||||
// Scaling pass
|
||||
float srcWidth = Math.Abs(source.X2 - source.X1);
|
||||
float srcHeight = Math.Abs(source.Y2 - source.Y1);
|
||||
float scaleX = srcWidth / view.Width;
|
||||
float scaleY = srcHeight / view.Height;
|
||||
GL.UseProgram(_scalingShaderProgram);
|
||||
view.Bind(0);
|
||||
GL.Uniform1(_inputUniform, 0);
|
||||
GL.Uniform1(_outputUniform, 0);
|
||||
GL.Uniform1(_srcX0Uniform, (float)source.X1);
|
||||
GL.Uniform1(_srcX1Uniform, (float)source.X2);
|
||||
GL.Uniform1(_srcY0Uniform, (float)source.Y1);
|
||||
GL.Uniform1(_srcY1Uniform, (float)source.Y2);
|
||||
GL.Uniform1(_dstX0Uniform, (float)destination.X1);
|
||||
GL.Uniform1(_dstX1Uniform, (float)destination.X2);
|
||||
GL.Uniform1(_dstY0Uniform, (float)destination.Y1);
|
||||
GL.Uniform1(_dstY1Uniform, (float)destination.Y2);
|
||||
GL.Uniform1(_scaleXUniform, scaleX);
|
||||
GL.Uniform1(_scaleYUniform, scaleY);
|
||||
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
|
||||
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||
|
||||
// Sharpening Pass
|
||||
GL.UseProgram(_sharpeningShaderProgram);
|
||||
GL.BindImageTexture(0, destinationTexture.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||
textureView.Bind(0);
|
||||
GL.Uniform1(_inputUniform, 0);
|
||||
GL.Uniform1(_outputUniform, 0);
|
||||
GL.Uniform1(_sharpeningUniform, 1.5f - (Level * 0.01f * 1.5f));
|
||||
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
|
||||
GL.UseProgram(previousProgram);
|
||||
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||
|
||||
(_renderer.Pipeline as Pipeline).RestoreImages1And2();
|
||||
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding);
|
||||
|
||||
GL.ActiveTexture((TextureUnit)previousUnit);
|
||||
}
|
||||
}
|
||||
}
|
81
Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs
Normal file
81
Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.OpenGL.Image;
|
||||
|
||||
namespace Ryujinx.Graphics.OpenGL.Effects
|
||||
{
|
||||
internal class FxaaPostProcessingEffect : IPostProcessingEffect
|
||||
{
|
||||
private readonly OpenGLRenderer _renderer;
|
||||
private int _resolutionUniform;
|
||||
private int _inputUniform;
|
||||
private int _outputUniform;
|
||||
private int _shaderProgram;
|
||||
private TextureStorage _textureStorage;
|
||||
|
||||
public FxaaPostProcessingEffect(OpenGLRenderer renderer)
|
||||
{
|
||||
Initialize();
|
||||
|
||||
_renderer = renderer;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_shaderProgram != 0)
|
||||
{
|
||||
GL.DeleteProgram(_shaderProgram);
|
||||
_textureStorage?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
_shaderProgram = ShaderHelper.CompileProgram(EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl"), ShaderType.ComputeShader);
|
||||
|
||||
_resolutionUniform = GL.GetUniformLocation(_shaderProgram, "invResolution");
|
||||
_inputUniform = GL.GetUniformLocation(_shaderProgram, "inputTexture");
|
||||
_outputUniform = GL.GetUniformLocation(_shaderProgram, "imgOutput");
|
||||
}
|
||||
|
||||
public TextureView Run(TextureView view, int width, int height)
|
||||
{
|
||||
if (_textureStorage == null || _textureStorage.Info.Width != view.Width || _textureStorage.Info.Height != view.Height)
|
||||
{
|
||||
_textureStorage?.Dispose();
|
||||
_textureStorage = new TextureStorage(_renderer, view.Info, view.ScaleFactor);
|
||||
_textureStorage.CreateDefaultView();
|
||||
}
|
||||
|
||||
var textureView = _textureStorage.CreateView(view.Info, 0, 0) as TextureView;
|
||||
|
||||
int previousProgram = GL.GetInteger(GetPName.CurrentProgram);
|
||||
int previousUnit = GL.GetInteger(GetPName.ActiveTexture);
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
int previousTextureBinding = GL.GetInteger(GetPName.TextureBinding2D);
|
||||
|
||||
GL.BindImageTexture(0, textureView.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||
GL.UseProgram(_shaderProgram);
|
||||
|
||||
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
||||
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
||||
|
||||
view.Bind(0);
|
||||
GL.Uniform1(_inputUniform, 0);
|
||||
GL.Uniform1(_outputUniform, 0);
|
||||
GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height);
|
||||
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
GL.UseProgram(previousProgram);
|
||||
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||
|
||||
(_renderer.Pipeline as Pipeline).RestoreImages1And2();
|
||||
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding);
|
||||
|
||||
GL.ActiveTexture((TextureUnit)previousUnit);
|
||||
|
||||
return textureView;
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs
Normal file
11
Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using Ryujinx.Graphics.OpenGL.Image;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.OpenGL.Effects
|
||||
{
|
||||
internal interface IPostProcessingEffect : IDisposable
|
||||
{
|
||||
const int LocalGroupSize = 64;
|
||||
TextureView Run(TextureView view, int width, int height);
|
||||
}
|
||||
}
|
18
Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs
Normal file
18
Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.OpenGL.Image;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.OpenGL.Effects
|
||||
{
|
||||
internal interface IScalingFilter : IDisposable
|
||||
{
|
||||
float Level { get; set; }
|
||||
void Run(
|
||||
TextureView view,
|
||||
TextureView destinationTexture,
|
||||
int width,
|
||||
int height,
|
||||
Extents2D source,
|
||||
Extents2D destination);
|
||||
}
|
||||
}
|
40
Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs
Normal file
40
Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.OpenGL.Effects
|
||||
{
|
||||
internal static class ShaderHelper
|
||||
{
|
||||
public static int CompileProgram(string shaderCode, ShaderType shaderType)
|
||||
{
|
||||
var shader = GL.CreateShader(shaderType);
|
||||
GL.ShaderSource(shader, shaderCode);
|
||||
GL.CompileShader(shader);
|
||||
|
||||
var program = GL.CreateProgram();
|
||||
GL.AttachShader(program, shader);
|
||||
GL.LinkProgram(program);
|
||||
|
||||
GL.DetachShader(program, shader);
|
||||
GL.DeleteShader(shader);
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
public static int CompileProgram(string[] shaders, ShaderType shaderType)
|
||||
{
|
||||
var shader = GL.CreateShader(shaderType);
|
||||
GL.ShaderSource(shader, shaders.Length, shaders, (int[])null);
|
||||
GL.CompileShader(shader);
|
||||
|
||||
var program = GL.CreateProgram();
|
||||
GL.AttachShader(program, shader);
|
||||
GL.LinkProgram(program);
|
||||
|
||||
GL.DetachShader(program, shader);
|
||||
GL.DeleteShader(shader);
|
||||
|
||||
return program;
|
||||
}
|
||||
}
|
||||
}
|
2656
Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h
Normal file
2656
Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h
Normal file
File diff suppressed because it is too large
Load Diff
1199
Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h
Normal file
1199
Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h
Normal file
File diff suppressed because it is too large
Load Diff
88
Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl
Normal file
88
Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl
Normal file
@ -0,0 +1,88 @@
|
||||
#version 430 core
|
||||
precision mediump float;
|
||||
layout (local_size_x = 64) in;
|
||||
layout(rgba8, binding = 0, location=0) uniform image2D imgOutput;
|
||||
layout( location=1 ) uniform sampler2D Source;
|
||||
layout( location=2 ) uniform float srcX0;
|
||||
layout( location=3 ) uniform float srcX1;
|
||||
layout( location=4 ) uniform float srcY0;
|
||||
layout( location=5 ) uniform float srcY1;
|
||||
layout( location=6 ) uniform float dstX0;
|
||||
layout( location=7 ) uniform float dstX1;
|
||||
layout( location=8 ) uniform float dstY0;
|
||||
layout( location=9 ) uniform float dstY1;
|
||||
layout( location=10 ) uniform float scaleX;
|
||||
layout( location=11 ) uniform float scaleY;
|
||||
|
||||
#define A_GPU 1
|
||||
#define A_GLSL 1
|
||||
#include "ffx_a.h"
|
||||
|
||||
#define FSR_EASU_F 1
|
||||
AU4 con0, con1, con2, con3;
|
||||
float srcW, srcH, dstW, dstH;
|
||||
vec2 bLeft, tRight;
|
||||
|
||||
AF2 translate(AF2 pos) {
|
||||
return AF2(pos.x * scaleX, pos.y * scaleY);
|
||||
}
|
||||
|
||||
void setBounds(vec2 bottomLeft, vec2 topRight) {
|
||||
bLeft = bottomLeft;
|
||||
tRight = topRight;
|
||||
}
|
||||
|
||||
AF2 translateDest(AF2 pos) {
|
||||
AF2 translatedPos = AF2(pos.x, pos.y);
|
||||
translatedPos.x = dstX1 < dstX0 ? dstX1 - translatedPos.x : translatedPos.x;
|
||||
translatedPos.y = dstY0 > dstY1 ? dstY0 + dstY1 - translatedPos.y - 1: translatedPos.y;
|
||||
return translatedPos;
|
||||
}
|
||||
|
||||
AF4 FsrEasuRF(AF2 p) { AF4 res = textureGather(Source, translate(p), 0); return res; }
|
||||
AF4 FsrEasuGF(AF2 p) { AF4 res = textureGather(Source, translate(p), 1); return res; }
|
||||
AF4 FsrEasuBF(AF2 p) { AF4 res = textureGather(Source, translate(p), 2); return res; }
|
||||
|
||||
#include "ffx_fsr1.h"
|
||||
|
||||
float insideBox(vec2 v) {
|
||||
vec2 s = step(bLeft, v) - step(tRight, v);
|
||||
return s.x * s.y;
|
||||
}
|
||||
|
||||
void CurrFilter(AU2 pos)
|
||||
{
|
||||
if((insideBox(vec2(pos.x, pos.y))) == 0) {
|
||||
imageStore(imgOutput, ASU2(pos.x, pos.y), AF4(0,0,0,1));
|
||||
return;
|
||||
}
|
||||
AF3 c;
|
||||
FsrEasuF(c, AU2(pos.x - bLeft.x, pos.y - bLeft.y), con0, con1, con2, con3);
|
||||
imageStore(imgOutput, ASU2(translateDest(pos)), AF4(c, 1));
|
||||
}
|
||||
|
||||
void main() {
|
||||
srcW = abs(srcX1 - srcX0);
|
||||
srcH = abs(srcY1 - srcY0);
|
||||
dstW = abs(dstX1 - dstX0);
|
||||
dstH = abs(dstY1 - dstY0);
|
||||
|
||||
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u);
|
||||
|
||||
setBounds(vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1),
|
||||
vec2(dstX1 > dstX0 ? dstX1 : dstX0, dstY1 > dstY0 ? dstY1 : dstY0));
|
||||
|
||||
// Upscaling
|
||||
FsrEasuCon(con0, con1, con2, con3,
|
||||
srcW, srcH, // Viewport size (top left aligned) in the input image which is to be scaled.
|
||||
srcW, srcH, // The size of the input image.
|
||||
dstW, dstH); // The output resolution.
|
||||
|
||||
CurrFilter(gxy);
|
||||
gxy.x += 8u;
|
||||
CurrFilter(gxy);
|
||||
gxy.y += 8u;
|
||||
CurrFilter(gxy);
|
||||
gxy.x -= 8u;
|
||||
CurrFilter(gxy);
|
||||
}
|
37
Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl
Normal file
37
Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl
Normal file
@ -0,0 +1,37 @@
|
||||
#version 430 core
|
||||
precision mediump float;
|
||||
layout (local_size_x = 64) in;
|
||||
layout(rgba8, binding = 0, location=0) uniform image2D imgOutput;
|
||||
layout( location=1 ) uniform sampler2D source;
|
||||
layout( location=2 ) uniform float sharpening;
|
||||
|
||||
#define A_GPU 1
|
||||
#define A_GLSL 1
|
||||
#include "ffx_a.h"
|
||||
|
||||
#define FSR_RCAS_F 1
|
||||
AU4 con0;
|
||||
|
||||
AF4 FsrRcasLoadF(ASU2 p) { return AF4(texelFetch(source, p, 0)); }
|
||||
void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
|
||||
|
||||
#include "ffx_fsr1.h"
|
||||
|
||||
void CurrFilter(AU2 pos)
|
||||
{
|
||||
AF3 c;
|
||||
FsrRcasF(c.r, c.g, c.b, pos, con0);
|
||||
imageStore(imgOutput, ASU2(pos), AF4(c, 1));
|
||||
}
|
||||
|
||||
void main() {
|
||||
FsrRcasCon(con0, sharpening);
|
||||
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u);
|
||||
CurrFilter(gxy);
|
||||
gxy.x += 8u;
|
||||
CurrFilter(gxy);
|
||||
gxy.y += 8u;
|
||||
CurrFilter(gxy);
|
||||
gxy.x -= 8u;
|
||||
CurrFilter(gxy);
|
||||
}
|
1174
Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl
Normal file
1174
Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl
Normal file
File diff suppressed because it is too large
Load Diff
1361
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl
Normal file
1361
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl
Normal file
File diff suppressed because it is too large
Load Diff
26
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl
Normal file
26
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl
Normal file
@ -0,0 +1,26 @@
|
||||
layout(rgba8, binding = 0) uniform image2D imgOutput;
|
||||
|
||||
uniform sampler2D inputTexture;
|
||||
layout( location=0 ) uniform vec2 invResolution;
|
||||
uniform sampler2D samplerArea;
|
||||
uniform sampler2D samplerSearch;
|
||||
|
||||
void main() {
|
||||
ivec2 loc = ivec2(gl_GlobalInvocationID.x * 4, gl_GlobalInvocationID.y * 4);
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
for(int j = 0; j < 4; j++)
|
||||
{
|
||||
ivec2 texelCoord = ivec2(loc.x + i, loc.y + j);
|
||||
vec2 coord = (texelCoord + vec2(0.5)) / invResolution;
|
||||
vec2 pixCoord;
|
||||
vec4 offset[3];
|
||||
|
||||
SMAABlendingWeightCalculationVS(coord, pixCoord, offset);
|
||||
|
||||
vec4 oColor = SMAABlendingWeightCalculationPS(coord, pixCoord, offset, inputTexture, samplerArea, samplerSearch, ivec4(0));
|
||||
|
||||
imageStore(imgOutput, texelCoord, oColor);
|
||||
}
|
||||
}
|
||||
}
|
24
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl
Normal file
24
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl
Normal file
@ -0,0 +1,24 @@
|
||||
layout(rgba8, binding = 0) uniform image2D imgOutput;
|
||||
|
||||
uniform sampler2D inputTexture;
|
||||
layout( location=0 ) uniform vec2 invResolution;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 loc = ivec2(gl_GlobalInvocationID.x * 4, gl_GlobalInvocationID.y * 4);
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
for(int j = 0; j < 4; j++)
|
||||
{
|
||||
ivec2 texelCoord = ivec2(loc.x + i, loc.y + j);
|
||||
vec2 coord = (texelCoord + vec2(0.5)) / invResolution;
|
||||
vec4 offset[3];
|
||||
SMAAEdgeDetectionVS(coord, offset);
|
||||
vec2 oColor = SMAAColorEdgeDetectionPS(coord, offset, inputTexture);
|
||||
if (oColor != float2(-2.0, -2.0))
|
||||
{
|
||||
imageStore(imgOutput, texelCoord, vec4(oColor, 0.0, 1.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl
Normal file
26
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl
Normal file
@ -0,0 +1,26 @@
|
||||
layout(rgba8, binding = 0) uniform image2D imgOutput;
|
||||
|
||||
uniform sampler2D inputTexture;
|
||||
layout( location=0 ) uniform vec2 invResolution;
|
||||
uniform sampler2D samplerBlend;
|
||||
|
||||
void main() {
|
||||
vec2 loc = ivec2(gl_GlobalInvocationID.x * 4, gl_GlobalInvocationID.y * 4);
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
for(int j = 0; j < 4; j++)
|
||||
{
|
||||
ivec2 texelCoord = ivec2(loc.x + i, loc.y + j);
|
||||
vec2 coord = (texelCoord + vec2(0.5)) / invResolution;
|
||||
vec2 pixCoord;
|
||||
vec4 offset;
|
||||
|
||||
SMAANeighborhoodBlendingVS(coord, offset);
|
||||
|
||||
vec4 oColor = SMAANeighborhoodBlendingPS(coord, offset, inputTexture, samplerBlend);
|
||||
|
||||
imageStore(imgOutput, texelCoord, oColor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
261
Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs
Normal file
261
Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs
Normal file
@ -0,0 +1,261 @@
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.OpenGL.Image;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.OpenGL.Effects.Smaa
|
||||
{
|
||||
internal partial class SmaaPostProcessingEffect : IPostProcessingEffect
|
||||
{
|
||||
public const int AreaWidth = 160;
|
||||
public const int AreaHeight = 560;
|
||||
public const int SearchWidth = 64;
|
||||
public const int SearchHeight = 16;
|
||||
|
||||
private readonly OpenGLRenderer _renderer;
|
||||
private TextureStorage _outputTexture;
|
||||
private TextureStorage _searchTexture;
|
||||
private TextureStorage _areaTexture;
|
||||
private int[] _edgeShaderPrograms;
|
||||
private int[] _blendShaderPrograms;
|
||||
private int[] _neighbourShaderPrograms;
|
||||
private TextureStorage _edgeOutputTexture;
|
||||
private TextureStorage _blendOutputTexture;
|
||||
private string[] _qualities;
|
||||
private int _inputUniform;
|
||||
private int _outputUniform;
|
||||
private int _samplerAreaUniform;
|
||||
private int _samplerSearchUniform;
|
||||
private int _samplerBlendUniform;
|
||||
private int _resolutionUniform;
|
||||
private int _quality = 1;
|
||||
|
||||
public int Quality
|
||||
{
|
||||
get => _quality; set
|
||||
{
|
||||
_quality = Math.Clamp(value, 0, _qualities.Length - 1);
|
||||
}
|
||||
}
|
||||
public SmaaPostProcessingEffect(OpenGLRenderer renderer, int quality)
|
||||
{
|
||||
_renderer = renderer;
|
||||
|
||||
_edgeShaderPrograms = Array.Empty<int>();
|
||||
_blendShaderPrograms = Array.Empty<int>();
|
||||
_neighbourShaderPrograms = Array.Empty<int>();
|
||||
|
||||
_qualities = new string[] { "SMAA_PRESET_LOW", "SMAA_PRESET_MEDIUM", "SMAA_PRESET_HIGH", "SMAA_PRESET_ULTRA" };
|
||||
|
||||
Quality = quality;
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_searchTexture?.Dispose();
|
||||
_areaTexture?.Dispose();
|
||||
_outputTexture?.Dispose();
|
||||
_edgeOutputTexture?.Dispose();
|
||||
_blendOutputTexture?.Dispose();
|
||||
|
||||
DeleteShaders();
|
||||
}
|
||||
|
||||
private void DeleteShaders()
|
||||
{
|
||||
for (int i = 0; i < _edgeShaderPrograms.Length; i++)
|
||||
{
|
||||
GL.DeleteProgram(_edgeShaderPrograms[i]);
|
||||
GL.DeleteProgram(_blendShaderPrograms[i]);
|
||||
GL.DeleteProgram(_neighbourShaderPrograms[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void RecreateShaders(int width, int height)
|
||||
{
|
||||
string baseShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl");
|
||||
var pixelSizeDefine = $"#define SMAA_RT_METRICS float4(1.0 / {width}.0, 1.0 / {height}.0, {width}, {height}) \n";
|
||||
|
||||
_edgeShaderPrograms = new int[_qualities.Length];
|
||||
_blendShaderPrograms = new int[_qualities.Length];
|
||||
_neighbourShaderPrograms = new int[_qualities.Length];
|
||||
|
||||
for (int i = 0; i < +_edgeShaderPrograms.Length; i++)
|
||||
{
|
||||
var presets = $"#version 430 core \n#define {_qualities[i]} 1 \n{pixelSizeDefine}#define SMAA_GLSL_4 1 \nlayout (local_size_x = 16, local_size_y = 16) in;\n{baseShader}";
|
||||
|
||||
var edgeShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl");
|
||||
var blendShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl");
|
||||
var neighbourShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl");
|
||||
|
||||
var shaders = new string[] { presets, edgeShaderData };
|
||||
var edgeProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader);
|
||||
|
||||
shaders[1] = blendShaderData;
|
||||
var blendProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader);
|
||||
|
||||
shaders[1] = neighbourShaderData;
|
||||
var neighbourProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader);
|
||||
|
||||
_edgeShaderPrograms[i] = edgeProgram;
|
||||
_blendShaderPrograms[i] = blendProgram;
|
||||
_neighbourShaderPrograms[i] = neighbourProgram;
|
||||
}
|
||||
|
||||
_inputUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "inputTexture");
|
||||
_outputUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "imgOutput");
|
||||
_samplerAreaUniform = GL.GetUniformLocation(_blendShaderPrograms[0], "samplerArea");
|
||||
_samplerSearchUniform = GL.GetUniformLocation(_blendShaderPrograms[0], "samplerSearch");
|
||||
_samplerBlendUniform = GL.GetUniformLocation(_neighbourShaderPrograms[0], "samplerBlend");
|
||||
_resolutionUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "invResolution");
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
var areaInfo = new TextureCreateInfo(AreaWidth,
|
||||
AreaHeight,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
Format.R8G8Unorm,
|
||||
DepthStencilMode.Depth,
|
||||
Target.Texture2D,
|
||||
SwizzleComponent.Red,
|
||||
SwizzleComponent.Green,
|
||||
SwizzleComponent.Blue,
|
||||
SwizzleComponent.Alpha);
|
||||
|
||||
var searchInfo = new TextureCreateInfo(SearchWidth,
|
||||
SearchHeight,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
Format.R8Unorm,
|
||||
DepthStencilMode.Depth,
|
||||
Target.Texture2D,
|
||||
SwizzleComponent.Red,
|
||||
SwizzleComponent.Green,
|
||||
SwizzleComponent.Blue,
|
||||
SwizzleComponent.Alpha);
|
||||
|
||||
_areaTexture = new TextureStorage(_renderer, areaInfo, 1);
|
||||
_searchTexture = new TextureStorage(_renderer, searchInfo, 1);
|
||||
|
||||
var areaTexture = EmbeddedResources.Read("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin");
|
||||
var searchTexture = EmbeddedResources.Read("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin");
|
||||
|
||||
var areaView = _areaTexture.CreateDefaultView();
|
||||
var searchView = _searchTexture.CreateDefaultView();
|
||||
|
||||
areaView.SetData(areaTexture);
|
||||
searchView.SetData(searchTexture);
|
||||
}
|
||||
|
||||
public TextureView Run(TextureView view, int width, int height)
|
||||
{
|
||||
if (_outputTexture == null || _outputTexture.Info.Width != view.Width || _outputTexture.Info.Height != view.Height)
|
||||
{
|
||||
_outputTexture?.Dispose();
|
||||
_outputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor);
|
||||
_outputTexture.CreateDefaultView();
|
||||
_edgeOutputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor);
|
||||
_edgeOutputTexture.CreateDefaultView();
|
||||
_blendOutputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor);
|
||||
_blendOutputTexture.CreateDefaultView();
|
||||
|
||||
DeleteShaders();
|
||||
|
||||
RecreateShaders(view.Width, view.Height);
|
||||
}
|
||||
|
||||
var textureView = _outputTexture.CreateView(view.Info, 0, 0) as TextureView;
|
||||
var edgeOutput = _edgeOutputTexture.DefaultView as TextureView;
|
||||
var blendOutput = _blendOutputTexture.DefaultView as TextureView;
|
||||
var areaTexture = _areaTexture.DefaultView as TextureView;
|
||||
var searchTexture = _searchTexture.DefaultView as TextureView;
|
||||
|
||||
var previousFramebuffer = GL.GetInteger(GetPName.FramebufferBinding);
|
||||
int previousUnit = GL.GetInteger(GetPName.ActiveTexture);
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
int previousTextureBinding0 = GL.GetInteger(GetPName.TextureBinding2D);
|
||||
GL.ActiveTexture(TextureUnit.Texture1);
|
||||
int previousTextureBinding1 = GL.GetInteger(GetPName.TextureBinding2D);
|
||||
GL.ActiveTexture(TextureUnit.Texture2);
|
||||
int previousTextureBinding2 = GL.GetInteger(GetPName.TextureBinding2D);
|
||||
|
||||
var framebuffer = new Framebuffer();
|
||||
framebuffer.Bind();
|
||||
framebuffer.AttachColor(0, edgeOutput);
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||
GL.ClearColor(0, 0, 0, 0);
|
||||
framebuffer.AttachColor(0, blendOutput);
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||
GL.ClearColor(0, 0, 0, 0);
|
||||
|
||||
GL.BindFramebuffer(FramebufferTarget.Framebuffer, previousFramebuffer);
|
||||
|
||||
framebuffer.Dispose();
|
||||
|
||||
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
||||
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
||||
|
||||
int previousProgram = GL.GetInteger(GetPName.CurrentProgram);
|
||||
GL.BindImageTexture(0, edgeOutput.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||
GL.UseProgram(_edgeShaderPrograms[Quality]);
|
||||
view.Bind(0);
|
||||
GL.Uniform1(_inputUniform, 0);
|
||||
GL.Uniform1(_outputUniform, 0);
|
||||
GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height);
|
||||
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||
|
||||
GL.BindImageTexture(0, blendOutput.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||
GL.UseProgram(_blendShaderPrograms[Quality]);
|
||||
edgeOutput.Bind(0);
|
||||
areaTexture.Bind(1);
|
||||
searchTexture.Bind(2);
|
||||
GL.Uniform1(_inputUniform, 0);
|
||||
GL.Uniform1(_outputUniform, 0);
|
||||
GL.Uniform1(_samplerAreaUniform, 1);
|
||||
GL.Uniform1(_samplerSearchUniform, 2);
|
||||
GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height);
|
||||
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||
|
||||
GL.BindImageTexture(0, textureView.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||
GL.UseProgram(_neighbourShaderPrograms[Quality]);
|
||||
view.Bind(0);
|
||||
blendOutput.Bind(1);
|
||||
GL.Uniform1(_inputUniform, 0);
|
||||
GL.Uniform1(_outputUniform, 0);
|
||||
GL.Uniform1(_samplerBlendUniform, 1);
|
||||
GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height);
|
||||
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||
|
||||
(_renderer.Pipeline as Pipeline).RestoreImages1And2();
|
||||
|
||||
GL.UseProgram(previousProgram);
|
||||
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding0);
|
||||
GL.ActiveTexture(TextureUnit.Texture1);
|
||||
GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding1);
|
||||
GL.ActiveTexture(TextureUnit.Texture2);
|
||||
GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding2);
|
||||
|
||||
GL.ActiveTexture((TextureUnit)previousUnit);
|
||||
|
||||
return textureView;
|
||||
}
|
||||
}
|
||||
}
|
BIN
Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin
Normal file
BIN
Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin
Normal file
Binary file not shown.
BIN
Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin
Normal file
BIN
Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin
Normal file
Binary file not shown.
@ -9,6 +9,20 @@
|
||||
<PackageReference Include="OpenTK.Graphics" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Effects\Textures\SmaaAreaTexture.bin" />
|
||||
<EmbeddedResource Include="Effects\Textures\SmaaSearchTexture.bin" />
|
||||
<EmbeddedResource Include="Effects\Shaders\fsr_sharpening.glsl" />
|
||||
<EmbeddedResource Include="Effects\Shaders\fxaa.glsl" />
|
||||
<EmbeddedResource Include="Effects\Shaders\smaa.hlsl" />
|
||||
<EmbeddedResource Include="Effects\Shaders\smaa_blend.glsl" />
|
||||
<EmbeddedResource Include="Effects\Shaders\smaa_edge.glsl" />
|
||||
<EmbeddedResource Include="Effects\Shaders\smaa_neighbour.glsl" />
|
||||
<EmbeddedResource Include="Effects\Shaders\ffx_fsr1.h" />
|
||||
<EmbeddedResource Include="Effects\Shaders\ffx_a.h" />
|
||||
<EmbeddedResource Include="Effects\Shaders\fsr_scaling.glsl" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
|
||||
|
@ -1,5 +1,7 @@
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.OpenGL.Effects;
|
||||
using Ryujinx.Graphics.OpenGL.Effects.Smaa;
|
||||
using Ryujinx.Graphics.OpenGL.Image;
|
||||
using System;
|
||||
|
||||
@ -7,14 +9,24 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
{
|
||||
class Window : IWindow, IDisposable
|
||||
{
|
||||
private const int TextureCount = 3;
|
||||
private readonly OpenGLRenderer _renderer;
|
||||
|
||||
private bool _initialized;
|
||||
|
||||
private int _width;
|
||||
private int _height;
|
||||
private bool _updateSize;
|
||||
private int _copyFramebufferHandle;
|
||||
private IPostProcessingEffect _antiAliasing;
|
||||
private IScalingFilter _scalingFilter;
|
||||
private bool _isLinear;
|
||||
private AntiAliasing _currentAntiAliasing;
|
||||
private bool _updateEffect;
|
||||
private ScalingFilter _currentScalingFilter;
|
||||
private float _scalingFilterLevel;
|
||||
private bool _updateScalingFilter;
|
||||
private bool _isBgra;
|
||||
private TextureView _upscaledTexture;
|
||||
|
||||
internal BackgroundContextWorker BackgroundContext { get; private set; }
|
||||
|
||||
@ -48,6 +60,8 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
|
||||
_updateSize = true;
|
||||
}
|
||||
|
||||
private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop, Action swapBuffersCallback)
|
||||
@ -57,6 +71,32 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
|
||||
TextureView viewConverted = view.Format.IsBgr() ? _renderer.TextureCopy.BgraSwap(view) : view;
|
||||
|
||||
UpdateEffect();
|
||||
|
||||
if (_antiAliasing != null)
|
||||
{
|
||||
var oldView = viewConverted;
|
||||
|
||||
viewConverted = _antiAliasing.Run(viewConverted, _width, _height);
|
||||
|
||||
if (viewConverted.Format.IsBgr())
|
||||
{
|
||||
var swappedView = _renderer.TextureCopy.BgraSwap(viewConverted);
|
||||
|
||||
viewConverted?.Dispose();
|
||||
|
||||
viewConverted = swappedView;
|
||||
}
|
||||
|
||||
if (viewConverted != oldView && oldView != view)
|
||||
{
|
||||
oldView.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer);
|
||||
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer);
|
||||
|
||||
GL.FramebufferTexture(
|
||||
FramebufferTarget.ReadFramebuffer,
|
||||
FramebufferAttachment.ColorAttachment0,
|
||||
@ -71,12 +111,12 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||
|
||||
int srcX0, srcX1, srcY0, srcY1;
|
||||
float scale = view.ScaleFactor;
|
||||
float scale = viewConverted.ScaleFactor;
|
||||
|
||||
if (crop.Left == 0 && crop.Right == 0)
|
||||
{
|
||||
srcX0 = 0;
|
||||
srcX1 = (int)(view.Width / scale);
|
||||
srcX1 = (int)(viewConverted.Width / scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -87,7 +127,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
if (crop.Top == 0 && crop.Bottom == 0)
|
||||
{
|
||||
srcY0 = 0;
|
||||
srcY1 = (int)(view.Height / scale);
|
||||
srcY1 = (int)(viewConverted.Height / scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -125,6 +165,42 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
ScreenCaptureRequested = false;
|
||||
}
|
||||
|
||||
if (_scalingFilter != null)
|
||||
{
|
||||
if (viewConverted.Format.IsBgr() && !_isBgra)
|
||||
{
|
||||
RecreateUpscalingTexture(true);
|
||||
}
|
||||
|
||||
_scalingFilter.Run(
|
||||
viewConverted,
|
||||
_upscaledTexture,
|
||||
_width,
|
||||
_height,
|
||||
new Extents2D(
|
||||
srcX0,
|
||||
srcY0,
|
||||
srcX1,
|
||||
srcY1),
|
||||
new Extents2D(
|
||||
dstX0,
|
||||
dstY0,
|
||||
dstX1,
|
||||
dstY1)
|
||||
);
|
||||
|
||||
srcX0 = dstX0;
|
||||
srcY0 = dstY0;
|
||||
srcX1 = dstX1;
|
||||
srcY1 = dstY1;
|
||||
|
||||
GL.FramebufferTexture(
|
||||
FramebufferTarget.ReadFramebuffer,
|
||||
FramebufferAttachment.ColorAttachment0,
|
||||
_upscaledTexture.Handle,
|
||||
0);
|
||||
}
|
||||
|
||||
GL.BlitFramebuffer(
|
||||
srcX0,
|
||||
srcY0,
|
||||
@ -135,7 +211,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
dstX1,
|
||||
dstY1,
|
||||
ClearBufferMask.ColorBufferBit,
|
||||
BlitFramebufferFilter.Linear);
|
||||
_isLinear ? BlitFramebufferFilter.Linear : BlitFramebufferFilter.Nearest);
|
||||
|
||||
// Remove Alpha channel
|
||||
GL.ColorMask(false, false, false, true);
|
||||
@ -209,6 +285,135 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
|
||||
_copyFramebufferHandle = 0;
|
||||
}
|
||||
|
||||
_antiAliasing?.Dispose();
|
||||
_scalingFilter?.Dispose();
|
||||
_upscaledTexture?.Dispose();
|
||||
}
|
||||
|
||||
public void SetAntiAliasing(AntiAliasing effect)
|
||||
{
|
||||
if (_currentAntiAliasing == effect && _antiAliasing != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_currentAntiAliasing = effect;
|
||||
|
||||
_updateEffect = true;
|
||||
}
|
||||
|
||||
public void SetScalingFilter(ScalingFilter type)
|
||||
{
|
||||
if (_currentScalingFilter == type && _antiAliasing != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_currentScalingFilter = type;
|
||||
|
||||
_updateScalingFilter = true;
|
||||
}
|
||||
|
||||
private void UpdateEffect()
|
||||
{
|
||||
if (_updateEffect)
|
||||
{
|
||||
_updateEffect = false;
|
||||
|
||||
switch (_currentAntiAliasing)
|
||||
{
|
||||
case AntiAliasing.Fxaa:
|
||||
_antiAliasing?.Dispose();
|
||||
_antiAliasing = new FxaaPostProcessingEffect(_renderer);
|
||||
break;
|
||||
case AntiAliasing.None:
|
||||
_antiAliasing?.Dispose();
|
||||
_antiAliasing = null;
|
||||
break;
|
||||
case AntiAliasing.SmaaLow:
|
||||
case AntiAliasing.SmaaMedium:
|
||||
case AntiAliasing.SmaaHigh:
|
||||
case AntiAliasing.SmaaUltra:
|
||||
var quality = _currentAntiAliasing - AntiAliasing.SmaaLow;
|
||||
if (_antiAliasing is SmaaPostProcessingEffect smaa)
|
||||
{
|
||||
smaa.Quality = quality;
|
||||
}
|
||||
else
|
||||
{
|
||||
_antiAliasing?.Dispose();
|
||||
_antiAliasing = new SmaaPostProcessingEffect(_renderer, quality);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_updateSize && !_updateScalingFilter)
|
||||
{
|
||||
RecreateUpscalingTexture();
|
||||
}
|
||||
|
||||
_updateSize = false;
|
||||
|
||||
if (_updateScalingFilter)
|
||||
{
|
||||
_updateScalingFilter = false;
|
||||
|
||||
switch (_currentScalingFilter)
|
||||
{
|
||||
case ScalingFilter.Bilinear:
|
||||
case ScalingFilter.Nearest:
|
||||
_scalingFilter?.Dispose();
|
||||
_scalingFilter = null;
|
||||
_isLinear = _currentScalingFilter == ScalingFilter.Bilinear;
|
||||
_upscaledTexture?.Dispose();
|
||||
_upscaledTexture = null;
|
||||
break;
|
||||
case ScalingFilter.Fsr:
|
||||
if (_scalingFilter is not FsrScalingFilter)
|
||||
{
|
||||
_scalingFilter?.Dispose();
|
||||
_scalingFilter = new FsrScalingFilter(_renderer, _antiAliasing);
|
||||
}
|
||||
_isLinear = false;
|
||||
_scalingFilter.Level = _scalingFilterLevel;
|
||||
|
||||
RecreateUpscalingTexture();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RecreateUpscalingTexture(bool forceBgra = false)
|
||||
{
|
||||
_upscaledTexture?.Dispose();
|
||||
|
||||
var info = new TextureCreateInfo(
|
||||
_width,
|
||||
_height,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
Format.R8G8B8A8Unorm,
|
||||
DepthStencilMode.Depth,
|
||||
Target.Texture2D,
|
||||
forceBgra ? SwizzleComponent.Blue : SwizzleComponent.Red,
|
||||
SwizzleComponent.Green,
|
||||
forceBgra ? SwizzleComponent.Red : SwizzleComponent.Blue,
|
||||
SwizzleComponent.Alpha);
|
||||
|
||||
_isBgra = forceBgra;
|
||||
_upscaledTexture = _renderer.CreateTexture(info, 1) as TextureView;
|
||||
}
|
||||
|
||||
public void SetScalingFilterLevel(float level)
|
||||
{
|
||||
_scalingFilterLevel = level;
|
||||
_updateScalingFilter = true;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user