Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Gaussian approximation for backbuffer mipmaps in GL Compatibility renderer #78168

Merged
merged 1 commit into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions drivers/gles3/effects/copy_effects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#ifdef GLES3_ENABLED

#include "copy_effects.h"
#include "../storage/texture_storage.h"

using namespace GLES3;

Expand Down Expand Up @@ -133,6 +134,7 @@ void CopyEffects::copy_screen() {
draw_screen_triangle();
}

// Intended for efficiently mipmapping textures.
void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region) {
GLuint framebuffers[2];
glGenFramebuffers(2, framebuffers);
Expand All @@ -158,6 +160,80 @@ void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, con
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) {
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR);
if (!success) {
Expand Down
1 change: 1 addition & 0 deletions drivers/gles3/effects/copy_effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class CopyEffects {
void copy_to_rect(const Rect2 &p_rect);
void copy_screen();
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 draw_screen_triangle();
void draw_screen_quad();
Expand Down
39 changes: 37 additions & 2 deletions drivers/gles3/shaders/copy.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,24 @@ layout(location = 0) in vec2 vertex_attrib;
out vec2 uv_interp;
/* 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;
#endif
#ifdef MODE_GAUSSIAN_BLUR
uniform highp vec4 source_section;
#endif

void main() {
uv_interp = vertex_attrib * 0.5 + 0.5;
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;
#endif
#ifdef MODE_GAUSSIAN_BLUR
uv_interp = source_section.xy + uv_interp * source_section.zw;
#endif
}

/* clang-format off */
Expand All @@ -39,6 +46,7 @@ uniform vec4 color_in;
#endif

#ifdef MODE_GAUSSIAN_BLUR
// Defined in 0-1 coords.
uniform highp vec2 pixel_size;
#endif

Expand All @@ -55,4 +63,31 @@ void main() {
#ifdef MODE_SIMPLE_COLOR
frag_color = color_in;
#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
}
14 changes: 8 additions & 6 deletions drivers/gles3/storage/texture_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1827,10 +1827,10 @@ void TextureStorage::_create_render_target_backbuffer(RenderTarget *rt) {
return;
}

// Initialize all levels to opaque Magenta.
// Initialize all levels to clear black.
for (int j = 0; j < count; 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);
}

Expand Down Expand Up @@ -2548,18 +2548,18 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons
}

glDisable(GL_BLEND);
//single texture copy for backbuffer
// Single texture copy for backbuffer.
glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, rt->color);
GLES3::CopyEffects::get_singleton()->copy_screen();

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);
}

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) {
Expand Down Expand Up @@ -2604,8 +2604,10 @@ void TextureStorage::render_target_gen_back_buffer_mipmaps(RID p_render_target,
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);
}

Expand Down