Use Gaussian approximation for backbuffer mipmaps in GL Compatibility renderer

This commit is contained in:
clayjohn 2023-06-12 21:51:23 -07:00
parent 593d5ca29f
commit 9721a27427
4 changed files with 122 additions and 8 deletions

View File

@ -31,6 +31,7 @@
#ifdef GLES3_ENABLED #ifdef GLES3_ENABLED
#include "copy_effects.h" #include "copy_effects.h"
#include "../storage/texture_storage.h"
using namespace GLES3; using namespace GLES3;
@ -133,6 +134,7 @@ void CopyEffects::copy_screen() {
draw_screen_triangle(); draw_screen_triangle();
} }
// Intended for efficiently mipmapping textures.
void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region) { void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region) {
GLuint framebuffers[2]; GLuint framebuffers[2];
glGenFramebuffers(2, framebuffers); glGenFramebuffers(2, framebuffers);
@ -158,6 +160,80 @@ void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, con
glDeleteFramebuffers(2, framebuffers); glDeleteFramebuffers(2, framebuffers);
} }
// Intended for approximating a gaussian blur. Used for 2D backbuffer mipmaps. Slightly less efficient than bilinear_blur().
void CopyEffects::gaussian_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region, const Size2i &p_size) {
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, p_source_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Size2i base_size = p_size;
Rect2i source_region = p_region;
Rect2i dest_region = p_region;
Size2 float_size = Size2(p_size);
Rect2 normalized_source_region = Rect2(p_region);
normalized_source_region.position = normalized_source_region.position / float_size;
normalized_source_region.size = normalized_source_region.size / float_size;
Rect2 normalized_dest_region = Rect2(p_region);
for (int i = 1; i < p_mipmap_count; i++) {
dest_region.position.x >>= 1;
dest_region.position.y >>= 1;
dest_region.size.x = MAX(1, dest_region.size.x >> 1);
dest_region.size.y = MAX(1, dest_region.size.y >> 1);
base_size.x >>= 1;
base_size.y >>= 1;
glBindTexture(GL_TEXTURE_2D, p_source_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, i - 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i - 1);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_source_texture, i);
#ifdef DEV_ENABLED
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
WARN_PRINT("Could not bind Gaussian blur framebuffer, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
}
#endif
glViewport(0, 0, base_size.x, base_size.y);
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
if (!success) {
return;
}
float_size = Size2(base_size);
normalized_dest_region.position = Size2(dest_region.position) / float_size;
normalized_dest_region.size = Size2(dest_region.size) / float_size;
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, normalized_dest_region.position.x, normalized_dest_region.position.y, normalized_dest_region.size.x, normalized_dest_region.size.y, copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
copy.shader.version_set_uniform(CopyShaderGLES3::SOURCE_SECTION, normalized_source_region.position.x, normalized_source_region.position.y, normalized_source_region.size.x, normalized_source_region.size.y, copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
copy.shader.version_set_uniform(CopyShaderGLES3::PIXEL_SIZE, 1.0 / float_size.x, 1.0 / float_size.y, copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
draw_screen_quad();
source_region = dest_region;
normalized_source_region = normalized_dest_region;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &framebuffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, p_mipmap_count - 1);
glBindTexture(GL_TEXTURE_2D, 0);
glViewport(0, 0, p_size.x, p_size.y);
}
void CopyEffects::set_color(const Color &p_color, const Rect2i &p_region) { void CopyEffects::set_color(const Color &p_color, const Rect2i &p_region) {
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR); bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR);
if (!success) { if (!success) {

View File

@ -64,6 +64,7 @@ public:
void copy_to_rect(const Rect2 &p_rect); void copy_to_rect(const Rect2 &p_rect);
void copy_screen(); void copy_screen();
void bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region); void bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region);
void gaussian_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region, const Size2i &p_size);
void set_color(const Color &p_color, const Rect2i &p_region); void set_color(const Color &p_color, const Rect2i &p_region);
void draw_screen_triangle(); void draw_screen_triangle();
void draw_screen_quad(); void draw_screen_quad();

View File

@ -16,17 +16,24 @@ layout(location = 0) in vec2 vertex_attrib;
out vec2 uv_interp; out vec2 uv_interp;
/* clang-format on */ /* clang-format on */
#ifdef USE_COPY_SECTION #if defined(USE_COPY_SECTION) || defined(MODE_GAUSSIAN_BLUR)
// Defined in 0-1 coords.
uniform highp vec4 copy_section; uniform highp vec4 copy_section;
#endif #endif
#ifdef MODE_GAUSSIAN_BLUR
uniform highp vec4 source_section;
#endif
void main() { void main() {
uv_interp = vertex_attrib * 0.5 + 0.5; uv_interp = vertex_attrib * 0.5 + 0.5;
gl_Position = vec4(vertex_attrib, 1.0, 1.0); gl_Position = vec4(vertex_attrib, 1.0, 1.0);
#ifdef USE_COPY_SECTION #if defined(USE_COPY_SECTION) || defined(MODE_GAUSSIAN_BLUR)
gl_Position.xy = (copy_section.xy + uv_interp.xy * copy_section.zw) * 2.0 - 1.0; gl_Position.xy = (copy_section.xy + uv_interp.xy * copy_section.zw) * 2.0 - 1.0;
#endif #endif
#ifdef MODE_GAUSSIAN_BLUR
uv_interp = source_section.xy + uv_interp * source_section.zw;
#endif
} }
/* clang-format off */ /* clang-format off */
@ -39,6 +46,7 @@ uniform vec4 color_in;
#endif #endif
#ifdef MODE_GAUSSIAN_BLUR #ifdef MODE_GAUSSIAN_BLUR
// Defined in 0-1 coords.
uniform highp vec2 pixel_size; uniform highp vec2 pixel_size;
#endif #endif
@ -55,4 +63,31 @@ void main() {
#ifdef MODE_SIMPLE_COLOR #ifdef MODE_SIMPLE_COLOR
frag_color = color_in; frag_color = color_in;
#endif #endif
// Efficient box filter from Jimenez: http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
// Approximates a Gaussian in a single pass.
#ifdef MODE_GAUSSIAN_BLUR
vec4 A = textureLod(source, uv_interp + pixel_size * vec2(-1.0, -1.0), 0.0);
vec4 B = textureLod(source, uv_interp + pixel_size * vec2(0.0, -1.0), 0.0);
vec4 C = textureLod(source, uv_interp + pixel_size * vec2(1.0, -1.0), 0.0);
vec4 D = textureLod(source, uv_interp + pixel_size * vec2(-0.5, -0.5), 0.0);
vec4 E = textureLod(source, uv_interp + pixel_size * vec2(0.5, -0.5), 0.0);
vec4 F = textureLod(source, uv_interp + pixel_size * vec2(-1.0, 0.0), 0.0);
vec4 G = textureLod(source, uv_interp, 0.0);
vec4 H = textureLod(source, uv_interp + pixel_size * vec2(1.0, 0.0), 0.0);
vec4 I = textureLod(source, uv_interp + pixel_size * vec2(-0.5, 0.5), 0.0);
vec4 J = textureLod(source, uv_interp + pixel_size * vec2(0.5, 0.5), 0.0);
vec4 K = textureLod(source, uv_interp + pixel_size * vec2(-1.0, 1.0), 0.0);
vec4 L = textureLod(source, uv_interp + pixel_size * vec2(0.0, 1.0), 0.0);
vec4 M = textureLod(source, uv_interp + pixel_size * vec2(1.0, 1.0), 0.0);
float weight = 0.5 / 4.0;
float lesser_weight = 0.125 / 4.0;
frag_color = (D + E + I + J) * weight;
frag_color += (A + B + G + F) * lesser_weight;
frag_color += (B + C + H + G) * lesser_weight;
frag_color += (F + G + L + K) * lesser_weight;
frag_color += (G + H + M + L) * lesser_weight;
#endif
} }

View File

@ -1827,10 +1827,10 @@ void TextureStorage::_create_render_target_backbuffer(RenderTarget *rt) {
return; return;
} }
// Initialize all levels to opaque Magenta. // Initialize all levels to clear black.
for (int j = 0; j < count; j++) { for (int j = 0; j < count; j++) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->backbuffer, j); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->backbuffer, j);
glClearColor(1.0, 0.0, 1.0, 1.0); glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
} }
@ -2548,18 +2548,18 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons
} }
glDisable(GL_BLEND); glDisable(GL_BLEND);
//single texture copy for backbuffer // Single texture copy for backbuffer.
glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo); glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, rt->color); glBindTexture(GL_TEXTURE_2D, rt->color);
GLES3::CopyEffects::get_singleton()->copy_screen(); GLES3::CopyEffects::get_singleton()->copy_screen();
if (p_gen_mipmaps) { if (p_gen_mipmaps) {
GLES3::CopyEffects::get_singleton()->bilinear_blur(rt->backbuffer, rt->mipmap_count, region); GLES3::CopyEffects::get_singleton()->gaussian_blur(rt->backbuffer, rt->mipmap_count, region, rt->size);
glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo); glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo);
} }
glEnable(GL_BLEND); // 2D almost always uses blend. glEnable(GL_BLEND); // 2D starts with blend enabled.
} }
void TextureStorage::render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color) { void TextureStorage::render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color) {
@ -2604,8 +2604,10 @@ void TextureStorage::render_target_gen_back_buffer_mipmaps(RID p_render_target,
return; //nothing to do return; //nothing to do
} }
} }
glDisable(GL_BLEND);
GLES3::CopyEffects::get_singleton()->gaussian_blur(rt->backbuffer, rt->mipmap_count, region, rt->size);
glEnable(GL_BLEND); // 2D starts with blend enabled.
GLES3::CopyEffects::get_singleton()->bilinear_blur(rt->backbuffer, rt->mipmap_count, region);
glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo); glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo);
} }