OpenGL: Implement FXAA

This commit is contained in:
Marshall Mohror 2021-10-20 18:36:06 -05:00 committed by Fernando Sahmkow
parent 74e39ed6ee
commit 48cf376462
6 changed files with 194 additions and 35 deletions

View File

@ -13,6 +13,8 @@ set(SHADER_FILES
convert_depth_to_float.frag convert_depth_to_float.frag
convert_float_to_depth.frag convert_float_to_depth.frag
full_screen_triangle.vert full_screen_triangle.vert
fxaa.frag
fxaa.vert
opengl_copy_bc4.comp opengl_copy_bc4.comp
opengl_present.frag opengl_present.frag
opengl_present.vert opengl_present.vert

View File

@ -0,0 +1,72 @@
// Adapted from
// https://www.geeks3d.com/20110405/fxaa-fast-approximate-anti-aliasing-demo-glsl-opengl-test-radeon-geforce/3/
#version 460
#ifdef VULKAN
#define BINDING_COLOR_TEXTURE 1
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
#define BINDING_COLOR_TEXTURE 0
#endif
layout (location = 0) in vec4 posPos;
layout (location = 0) out vec4 frag_color;
layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture;
const float FXAA_SPAN_MAX = 8.0;
const float FXAA_REDUCE_MUL = 1.0 / 8.0;
const float FXAA_REDUCE_MIN = 1.0 / 128.0;
#define FxaaTexLod0(t, p) textureLod(t, p, 0.0)
#define FxaaTexOff(t, p, o) textureLodOffset(t, p, 0.0, o)
vec3 FxaaPixelShader(vec4 posPos, sampler2D tex) {
vec3 rgbNW = FxaaTexLod0(tex, posPos.zw).xyz;
vec3 rgbNE = FxaaTexOff(tex, posPos.zw, ivec2(1,0)).xyz;
vec3 rgbSW = FxaaTexOff(tex, posPos.zw, ivec2(0,1)).xyz;
vec3 rgbSE = FxaaTexOff(tex, posPos.zw, ivec2(1,1)).xyz;
vec3 rgbM = FxaaTexLod0(tex, posPos.xy).xyz;
/*---------------------------------------------------------*/
vec3 luma = vec3(0.299, 0.587, 0.114);
float lumaNW = dot(rgbNW, luma);
float lumaNE = dot(rgbNE, luma);
float lumaSW = dot(rgbSW, luma);
float lumaSE = dot(rgbSE, luma);
float lumaM = dot(rgbM, luma);
/*---------------------------------------------------------*/
float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
/*---------------------------------------------------------*/
vec2 dir;
dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));
/*---------------------------------------------------------*/
float dirReduce = max(
(lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL),
FXAA_REDUCE_MIN);
float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce);
dir = min(vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX),
max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),
dir * rcpDirMin)) / textureSize(tex, 0);
/*--------------------------------------------------------*/
vec3 rgbA = (1.0 / 2.0) * (
FxaaTexLod0(tex, posPos.xy + dir * (1.0 / 3.0 - 0.5)).xyz +
FxaaTexLod0(tex, posPos.xy + dir * (2.0 / 3.0 - 0.5)).xyz);
vec3 rgbB = rgbA * (1.0 / 2.0) + (1.0 / 4.0) * (
FxaaTexLod0(tex, posPos.xy + dir * (0.0 / 3.0 - 0.5)).xyz +
FxaaTexLod0(tex, posPos.xy + dir * (3.0 / 3.0 - 0.5)).xyz);
float lumaB = dot(rgbB, luma);
if((lumaB < lumaMin) || (lumaB > lumaMax)) return rgbA;
return rgbB;
}
void main() {
frag_color = vec4(FxaaPixelShader(posPos, input_texture), 1.0);
}

View File

@ -0,0 +1,40 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#version 460
out gl_PerVertex {
vec4 gl_Position;
};
const vec2 vertices[4] =
vec2[4](vec2(-1.0, 1.0), vec2(1.0, 1.0), vec2(-1.0, -1.0), vec2(1.0, -1.0));
layout (location = 0) out vec4 posPos;
#ifdef VULKAN
#define BINDING_COLOR_TEXTURE 1
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
#define BINDING_COLOR_TEXTURE 0
#endif
layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture;
const float FXAA_SUBPIX_SHIFT = 0;
void main() {
#ifdef VULKAN
vec2 vertex = vertices[gl_VertexIndex];
#else
vec2 vertex = vertices[gl_VertexID];
#endif
gl_Position = vec4(vertex, 0.0, 1.0);
vec2 vert_tex_coord = (vertex + 1.0) / 2.0;
posPos.xy = vert_tex_coord;
posPos.zw = vert_tex_coord - (0.5 + FXAA_SUBPIX_SHIFT) / textureSize(input_texture, 0);
}

View File

@ -166,7 +166,7 @@ void OGLFramebuffer::Create() {
return; return;
MICROPROFILE_SCOPE(OpenGL_ResourceCreation); MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
glGenFramebuffers(1, &handle); glCreateFramebuffers(1, &handle);
} }
void OGLFramebuffer::Release() { void OGLFramebuffer::Release() {

View File

@ -21,6 +21,8 @@
#include "core/memory.h" #include "core/memory.h"
#include "core/perf_stats.h" #include "core/perf_stats.h"
#include "core/telemetry_session.h" #include "core/telemetry_session.h"
#include "video_core/host_shaders/fxaa_frag.h"
#include "video_core/host_shaders/fxaa_vert.h"
#include "video_core/host_shaders/opengl_present_frag.h" #include "video_core/host_shaders/opengl_present_frag.h"
#include "video_core/host_shaders/opengl_present_vert.h" #include "video_core/host_shaders/opengl_present_vert.h"
#include "video_core/host_shaders/present_bicubic_frag.h" #include "video_core/host_shaders/present_bicubic_frag.h"
@ -254,6 +256,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
void RendererOpenGL::InitOpenGLObjects() { void RendererOpenGL::InitOpenGLObjects() {
// Create shader programs // Create shader programs
fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER);
fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER);
present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER); present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
present_bilinear_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER); present_bilinear_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER);
present_bicubic_fragment = CreateProgram(HostShaders::PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER); present_bicubic_fragment = CreateProgram(HostShaders::PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER);
@ -287,6 +291,8 @@ void RendererOpenGL::InitOpenGLObjects() {
// Clear screen to black // Clear screen to black
LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture); LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture);
fxaa_framebuffer.Create();
} }
void RendererOpenGL::AddTelemetryFields() { void RendererOpenGL::AddTelemetryFields() {
@ -338,14 +344,83 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
texture.resource.Release(); texture.resource.Release();
texture.resource.Create(GL_TEXTURE_2D); texture.resource.Create(GL_TEXTURE_2D);
glTextureStorage2D(texture.resource.handle, 1, internal_format, texture.width, texture.height); glTextureStorage2D(texture.resource.handle, 1, internal_format, texture.width, texture.height);
fxaa_texture.Release();
fxaa_texture.Create(GL_TEXTURE_2D);
glTextureStorage2D(fxaa_texture.handle, 1, GL_RGBA16F, texture.width, texture.height);
glNamedFramebufferTexture(fxaa_framebuffer.handle, GL_COLOR_ATTACHMENT0, fxaa_texture.handle,
0);
} }
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
// TODO: Signal state tracker about these changes
state_tracker.NotifyScreenDrawVertexArray();
state_tracker.NotifyPolygonModes();
state_tracker.NotifyViewport0();
state_tracker.NotifyScissor0();
state_tracker.NotifyColorMask(0);
state_tracker.NotifyBlend0();
state_tracker.NotifyFramebuffer();
state_tracker.NotifyFrontFace();
state_tracker.NotifyCullTest();
state_tracker.NotifyDepthTest();
state_tracker.NotifyStencilTest();
state_tracker.NotifyPolygonOffset();
state_tracker.NotifyRasterizeEnable();
state_tracker.NotifyFramebufferSRGB();
state_tracker.NotifyLogicOp();
state_tracker.NotifyClipControl();
state_tracker.NotifyAlphaTest();
state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
// Update background color before drawing // Update background color before drawing
glClearColor(Settings::values.bg_red.GetValue() / 255.0f, glClearColor(Settings::values.bg_red.GetValue() / 255.0f,
Settings::values.bg_green.GetValue() / 255.0f, Settings::values.bg_green.GetValue() / 255.0f,
Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); Settings::values.bg_blue.GetValue() / 255.0f, 1.0f);
glEnable(GL_CULL_FACE);
glDisable(GL_COLOR_LOGIC_OP);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_POLYGON_OFFSET_FILL);
glDisable(GL_RASTERIZER_DISCARD);
glDisable(GL_ALPHA_TEST);
glDisablei(GL_BLEND, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glCullFace(GL_BACK);
glFrontFace(GL_CW);
glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glBindTextureUnit(0, screen_info.display_texture);
if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa) {
program_manager.BindPresentPrograms(fxaa_vertex.handle, fxaa_fragment.handle);
glEnablei(GL_SCISSOR_TEST, 0);
glScissorIndexed(0, 0, 0,
framebuffer_crop_rect.GetWidth() != 0 ? framebuffer_crop_rect.GetWidth()
: screen_info.texture.width,
framebuffer_crop_rect.GetHeight() != 0 ? framebuffer_crop_rect.GetHeight()
: screen_info.texture.height);
glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(screen_info.texture.width),
static_cast<GLfloat>(screen_info.texture.height));
glDepthRangeIndexed(0, 0.0, 0.0);
glBindSampler(0, present_sampler.handle);
GLint old_read_fb;
GLint old_draw_fb;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fxaa_framebuffer.handle);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
glBindTextureUnit(0, fxaa_texture.handle);
}
// Set projection matrix // Set projection matrix
const std::array ortho_matrix = const std::array ortho_matrix =
MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
@ -422,47 +497,14 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
}; };
glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
// TODO: Signal state tracker about these changes
state_tracker.NotifyScreenDrawVertexArray();
state_tracker.NotifyPolygonModes();
state_tracker.NotifyViewport0();
state_tracker.NotifyScissor0();
state_tracker.NotifyColorMask(0);
state_tracker.NotifyBlend0();
state_tracker.NotifyFramebuffer();
state_tracker.NotifyFrontFace();
state_tracker.NotifyCullTest();
state_tracker.NotifyDepthTest();
state_tracker.NotifyStencilTest();
state_tracker.NotifyPolygonOffset();
state_tracker.NotifyRasterizeEnable();
state_tracker.NotifyFramebufferSRGB();
state_tracker.NotifyLogicOp();
state_tracker.NotifyClipControl();
state_tracker.NotifyAlphaTest();
state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
glEnable(GL_CULL_FACE);
if (screen_info.display_srgb) { if (screen_info.display_srgb) {
glEnable(GL_FRAMEBUFFER_SRGB); glEnable(GL_FRAMEBUFFER_SRGB);
} else { } else {
glDisable(GL_FRAMEBUFFER_SRGB); glDisable(GL_FRAMEBUFFER_SRGB);
} }
glDisable(GL_COLOR_LOGIC_OP);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_POLYGON_OFFSET_FILL);
glDisable(GL_RASTERIZER_DISCARD);
glDisable(GL_ALPHA_TEST);
glDisablei(GL_BLEND, 0);
glDisablei(GL_SCISSOR_TEST, 0); glDisablei(GL_SCISSOR_TEST, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glCullFace(GL_BACK);
glFrontFace(GL_CW);
glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width), glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
static_cast<GLfloat>(layout.height)); static_cast<GLfloat>(layout.height));
glDepthRangeIndexed(0, 0.0, 0.0);
glEnableVertexAttribArray(PositionLocation); glEnableVertexAttribArray(PositionLocation);
glEnableVertexAttribArray(TexCoordLocation); glEnableVertexAttribArray(TexCoordLocation);
@ -482,7 +524,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
} }
glBindTextureUnit(0, screen_info.display_texture);
if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) { if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) {
glBindSampler(0, present_sampler.handle); glBindSampler(0, present_sampler.handle);
} else { } else {

View File

@ -111,6 +111,8 @@ private:
OGLSampler present_sampler; OGLSampler present_sampler;
OGLSampler present_sampler_nn; OGLSampler present_sampler_nn;
OGLBuffer vertex_buffer; OGLBuffer vertex_buffer;
OGLProgram fxaa_vertex;
OGLProgram fxaa_fragment;
OGLProgram present_vertex; OGLProgram present_vertex;
OGLProgram present_bilinear_fragment; OGLProgram present_bilinear_fragment;
OGLProgram present_bicubic_fragment; OGLProgram present_bicubic_fragment;
@ -123,6 +125,8 @@ private:
/// Display information for Switch screen /// Display information for Switch screen
ScreenInfo screen_info; ScreenInfo screen_info;
OGLTexture fxaa_texture;
OGLFramebuffer fxaa_framebuffer;
/// OpenGL framebuffer data /// OpenGL framebuffer data
std::vector<u8> gl_framebuffer_data; std::vector<u8> gl_framebuffer_data;