summaryrefslogtreecommitdiff
path: root/render/fx_renderer
diff options
context:
space:
mode:
authorErik Reider <[email protected]>2024-02-27 18:05:58 +0100
committerGitHub <[email protected]>2024-02-27 18:05:58 +0100
commit7f0883b383b73af7bc68dcf8c2ee845c5eab5807 (patch)
treeff13a416200ac372d0ae303e5996bb9a22f819dd /render/fx_renderer
parent5b6862c981eb5541888f625cd93e7775cabe06b0 (diff)
[FX Renderer] Add blur (#30)
* Initial blur implementation * Added additional blur effects from SwayFX * Simplified blur pass functions to match the other pass functions * Minor fixes * Added support for optimized blur * tinywl: Don't set decoration values every frame * Updated public blur function docs * Simplified blur buffer management * Moved ignore transparent bool into a per buffer option * Clip the scene_buffer when blur is enabled * Added back corner and shadow checks in opaque_region function * Renamed fx_render_blur_options to fx_render_blur_pass_options * Fixed nits * Removed unused fx_framebuffer_bind_wlr_fbo function * Removed wlr_scene impl. Should be moved into future PR instead * Made blur impl independent of wlr_scene * Moved shader init back into fx_renderer.c * Renamed fx_framebuffer_get_or_create_bufferless to fx_framebuffer_get_or_create_custom
Diffstat (limited to 'render/fx_renderer')
-rw-r--r--render/fx_renderer/fx_framebuffer.c37
-rw-r--r--render/fx_renderer/fx_pass.c368
-rw-r--r--render/fx_renderer/fx_renderer.c84
-rw-r--r--render/fx_renderer/gles2/shaders/blur1.frag18
-rw-r--r--render/fx_renderer/gles2/shaders/blur2.frag22
-rw-r--r--render/fx_renderer/gles2/shaders/blur_effects.frag60
-rw-r--r--render/fx_renderer/gles2/shaders/meson.build3
-rw-r--r--render/fx_renderer/gles2/shaders/stencil_mask.frag14
-rw-r--r--render/fx_renderer/gles2/shaders/tex.frag5
-rw-r--r--render/fx_renderer/shaders.c74
10 files changed, 633 insertions, 52 deletions
diff --git a/render/fx_renderer/fx_framebuffer.c b/render/fx_renderer/fx_framebuffer.c
index fea101b..0c8e04c 100644
--- a/render/fx_renderer/fx_framebuffer.c
+++ b/render/fx_renderer/fx_framebuffer.c
@@ -20,6 +20,37 @@ static const struct wlr_addon_interface buffer_addon_impl = {
.destroy = handle_buffer_destroy,
};
+void fx_framebuffer_get_or_create_custom(struct fx_renderer *renderer,
+ struct wlr_output *output, struct fx_framebuffer **fx_framebuffer) {
+ struct wlr_allocator *allocator = output->allocator;
+ struct wlr_swapchain *swapchain = output->swapchain;
+ int width = output->width;
+ int height = output->height;
+ struct wlr_buffer *wlr_buffer = NULL;
+
+ if (*fx_framebuffer == NULL) {
+ wlr_buffer = wlr_allocator_create_buffer(allocator, width, height,
+ &swapchain->format);
+ if (wlr_buffer == NULL) {
+ wlr_log(WLR_ERROR, "Failed to allocate buffer");
+ return;
+ }
+ } else {
+ if ((wlr_buffer = (*fx_framebuffer)->buffer) &&
+ wlr_buffer->width == width &&
+ wlr_buffer->height == height) {
+ return;
+ }
+ // Create a new wlr_buffer if it's null or if the output size has
+ // changed
+ fx_framebuffer_destroy(*fx_framebuffer);
+ wlr_buffer_drop(wlr_buffer);
+ wlr_buffer = wlr_allocator_create_buffer(allocator,
+ width, height, &swapchain->format);
+ }
+ *fx_framebuffer = fx_framebuffer_get_or_create(renderer, wlr_buffer);
+}
+
struct fx_framebuffer *fx_framebuffer_get_or_create(struct fx_renderer *renderer,
struct wlr_buffer *wlr_buffer) {
struct wlr_addon *addon =
@@ -111,10 +142,6 @@ void fx_framebuffer_bind(struct fx_framebuffer *fx_buffer) {
glBindFramebuffer(GL_FRAMEBUFFER, fx_buffer->fbo);
}
-void fx_framebuffer_bind_wlr_fbo(struct fx_renderer *renderer) {
- glBindFramebuffer(GL_FRAMEBUFFER, renderer->current_buffer->fbo);
-}
-
void fx_framebuffer_destroy(struct fx_framebuffer *fx_buffer) {
// Release the framebuffer
wl_list_remove(&fx_buffer->link);
@@ -128,6 +155,8 @@ void fx_framebuffer_destroy(struct fx_framebuffer *fx_buffer) {
fx_buffer->fbo = -1;
glDeleteRenderbuffers(1, &fx_buffer->rbo);
fx_buffer->rbo = -1;
+ glDeleteRenderbuffers(1, &fx_buffer->sb);
+ fx_buffer->sb = -1;
wlr_egl_destroy_image(fx_buffer->renderer->egl, fx_buffer->image);
diff --git a/render/fx_renderer/fx_pass.c b/render/fx_renderer/fx_pass.c
index 231de33..f12eda5 100644
--- a/render/fx_renderer/fx_pass.c
+++ b/render/fx_renderer/fx_pass.c
@@ -4,13 +4,16 @@
#include <assert.h>
#include <pixman.h>
#include <time.h>
+#include <wlr/render/allocator.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/util/log.h>
+#include <wlr/util/region.h>
#include "render/egl.h"
#include "render/fx_renderer/fx_renderer.h"
#include "render/fx_renderer/matrix.h"
#include "render/pass.h"
+#include "scenefx/types/fx/blur_data.h"
#include "scenefx/types/fx/shadow_data.h"
#define MAX_QUADS 86 // 4kb
@@ -19,6 +22,7 @@ struct fx_render_texture_options fx_render_texture_options_default(
const struct wlr_render_texture_options *base) {
struct fx_render_texture_options options = {
.corner_radius = 0,
+ .discard_transparent = false,
.scale = 1.0f,
.clip_box = NULL,
};
@@ -74,6 +78,8 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
wlr_buffer_unlock(pass->buffer->buffer);
free(pass);
+ pixman_region32_fini(&renderer->blur_padding_region);
+
return true;
}
@@ -307,6 +313,7 @@ void fx_render_pass_add_texture(struct fx_gles_render_pass *pass,
glUniform2f(shader->size, clip_box->width, clip_box->height);
glUniform2f(shader->position, clip_box->x, clip_box->y);
glUniform1f(shader->radius, fx_options->corner_radius);
+ glUniform1f(shader->discard_transparent, fx_options->discard_transparent);
set_proj_matrix(shader->proj, pass->projection_matrix, &dst_box);
set_tex_matrix(shader->tex_proj, options->transform, &src_fbox);
@@ -432,6 +439,353 @@ void fx_render_pass_add_box_shadow(struct fx_gles_render_pass *pass,
pop_fx_debug(renderer);
}
+// Renders the blur for each damaged rect and swaps the buffer
+static void render_blur_segments(struct fx_gles_render_pass *pass,
+ struct fx_render_blur_pass_options *fx_options, struct blur_shader* shader) {
+ struct fx_render_texture_options *tex_options = &fx_options->tex_options;
+ struct wlr_render_texture_options *options = &tex_options->base;
+ struct fx_renderer *renderer = pass->buffer->renderer;
+ struct blur_data *blur_data = fx_options->blur_data;
+
+ // Swap fbo
+ if (fx_options->current_buffer == renderer->effects_buffer) {
+ fx_framebuffer_bind(renderer->effects_buffer_swapped);
+ } else {
+ fx_framebuffer_bind(renderer->effects_buffer);
+ }
+
+ options->texture = fx_texture_from_buffer(&renderer->wlr_renderer,
+ fx_options->current_buffer->buffer);
+ struct fx_texture *texture = fx_get_texture(options->texture);
+
+ /*
+ * Render
+ */
+
+ struct wlr_box dst_box;
+ struct wlr_fbox src_fbox;
+ wlr_render_texture_options_get_src_box(options, &src_fbox);
+ wlr_render_texture_options_get_dst_box(options, &dst_box);
+ src_fbox.x /= options->texture->width;
+ src_fbox.y /= options->texture->height;
+ src_fbox.width /= options->texture->width;
+ src_fbox.height /= options->texture->height;
+
+ glDisable(GL_BLEND);
+ glDisable(GL_STENCIL_TEST);
+
+ push_fx_debug(renderer);
+
+ glUseProgram(shader->program);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(texture->target, texture->tex);
+
+ switch (options->filter_mode) {
+ case WLR_SCALE_FILTER_BILINEAR:
+ glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ break;
+ case WLR_SCALE_FILTER_NEAREST:
+ glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ break;
+ }
+
+ glUniform1i(shader->tex, 0);
+ glUniform1f(shader->radius, blur_data->radius);
+
+ if (shader == &renderer->shaders.blur1) {
+ glUniform2f(shader->halfpixel,
+ 0.5f / (options->texture->width / 2.0f),
+ 0.5f / (options->texture->height / 2.0f));
+ } else {
+ glUniform2f(shader->halfpixel,
+ 0.5f / (options->texture->width * 2.0f),
+ 0.5f / (options->texture->height * 2.0f));
+ }
+
+ set_proj_matrix(shader->proj, pass->projection_matrix, &dst_box);
+ set_tex_matrix(shader->tex_proj, options->transform, &src_fbox);
+
+ render(&dst_box, options->clip, shader->pos_attrib);
+
+ glBindTexture(texture->target, 0);
+ pop_fx_debug(renderer);
+
+ wlr_texture_destroy(options->texture);
+
+ // Swap buffer. We don't want to draw to the same buffer
+ if (fx_options->current_buffer != renderer->effects_buffer) {
+ fx_options->current_buffer = renderer->effects_buffer;
+ } else {
+ fx_options->current_buffer = renderer->effects_buffer_swapped;
+ }
+}
+
+static void render_blur_effects(struct fx_gles_render_pass *pass,
+ struct fx_render_blur_pass_options *fx_options) {
+ struct fx_render_texture_options *tex_options = &fx_options->tex_options;
+ struct wlr_render_texture_options *options = &tex_options->base;
+
+ check_tex_src_box(options);
+
+ struct fx_renderer *renderer = pass->buffer->renderer;
+ struct blur_data *blur_data = fx_options->blur_data;
+ struct fx_texture *texture = fx_get_texture(options->texture);
+
+ struct blur_effects_shader shader = renderer->shaders.blur_effects;
+
+ struct wlr_box dst_box;
+ struct wlr_fbox src_fbox;
+ wlr_render_texture_options_get_src_box(options, &src_fbox);
+ wlr_render_texture_options_get_dst_box(options, &dst_box);
+
+ src_fbox.x /= options->texture->width;
+ src_fbox.y /= options->texture->height;
+ src_fbox.width /= options->texture->width;
+ src_fbox.height /= options->texture->height;
+
+ glDisable(GL_BLEND);
+ glDisable(GL_STENCIL_TEST);
+
+ push_fx_debug(renderer);
+
+ glUseProgram(shader.program);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(texture->target, texture->tex);
+
+ switch (options->filter_mode) {
+ case WLR_SCALE_FILTER_BILINEAR:
+ glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ break;
+ case WLR_SCALE_FILTER_NEAREST:
+ glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ break;
+ }
+
+ glUniform1i(shader.tex, 0);
+ glUniform1f(shader.noise, blur_data->noise);
+ glUniform1f(shader.brightness, blur_data->brightness);
+ glUniform1f(shader.contrast, blur_data->contrast);
+ glUniform1f(shader.saturation, blur_data->saturation);
+
+ set_proj_matrix(shader.proj, pass->projection_matrix, &dst_box);
+ set_tex_matrix(shader.tex_proj, options->transform, &src_fbox);
+
+ render(&dst_box, options->clip, shader.pos_attrib);
+
+ glBindTexture(texture->target, 0);
+ pop_fx_debug(renderer);
+
+ wlr_texture_destroy(options->texture);
+}
+
+// Blurs the main_buffer content and returns the blurred framebuffer
+static struct fx_framebuffer *get_main_buffer_blur(struct fx_gles_render_pass *pass,
+ struct fx_render_blur_pass_options *fx_options) {
+ struct fx_renderer *renderer = pass->buffer->renderer;
+ struct blur_data *blur_data = fx_options->blur_data;
+
+ pixman_region32_t damage;
+ pixman_region32_init(&damage);
+ pixman_region32_copy(&damage, fx_options->tex_options.base.clip);
+ wlr_region_transform(&damage, &damage, fx_options->tex_options.base.transform,
+ fx_options->monitor_box.width, fx_options->monitor_box.height);
+
+ wlr_region_expand(&damage, &damage, blur_data_calc_size(fx_options->blur_data));
+
+ // damage region will be scaled, make a temp
+ pixman_region32_t scaled_damage;
+ pixman_region32_init(&scaled_damage);
+
+ fx_options->tex_options.base.src_box = (struct wlr_fbox) {
+ fx_options->monitor_box.x,
+ fx_options->monitor_box.y,
+ fx_options->monitor_box.width,
+ fx_options->monitor_box.height,
+ };
+ fx_options->tex_options.base.dst_box = fx_options->monitor_box;
+ // Clip the blur to the damage
+ fx_options->tex_options.base.clip = &scaled_damage;
+
+ // Downscale
+ for (int i = 0; i < blur_data->num_passes; ++i) {
+ wlr_region_scale(&scaled_damage, &damage, 1.0f / (1 << (i + 1)));
+ render_blur_segments(pass, fx_options, &renderer->shaders.blur1);
+ }
+
+ // Upscale
+ for (int i = blur_data->num_passes - 1; i >= 0; --i) {
+ // when upsampling we make the region twice as big
+ wlr_region_scale(&scaled_damage, &damage, 1.0f / (1 << i));
+ render_blur_segments(pass, fx_options, &renderer->shaders.blur2);
+ }
+
+ pixman_region32_fini(&scaled_damage);
+
+ // Render additional blur effects like saturation, noise, contrast, etc...
+ if (blur_data_should_parameters_blur_effects(blur_data)
+ && pixman_region32_not_empty(&damage)) {
+ if (fx_options->current_buffer == renderer->effects_buffer) {
+ fx_framebuffer_bind(renderer->effects_buffer_swapped);
+ } else {
+ fx_framebuffer_bind(renderer->effects_buffer);
+ }
+ fx_options->tex_options.base.clip = &damage;
+ fx_options->tex_options.base.texture = fx_texture_from_buffer(
+ &renderer->wlr_renderer, fx_options->current_buffer->buffer);
+ render_blur_effects(pass, fx_options);
+ if (fx_options->current_buffer != renderer->effects_buffer) {
+ fx_options->current_buffer = renderer->effects_buffer;
+ } else {
+ fx_options->current_buffer = renderer->effects_buffer_swapped;
+ }
+ }
+
+ pixman_region32_fini(&damage);
+
+ // Bind back to the default buffer
+ fx_framebuffer_bind(pass->buffer);
+
+ return fx_options->current_buffer;
+}
+
+void fx_render_pass_add_blur(struct fx_gles_render_pass *pass,
+ struct fx_render_blur_pass_options *fx_options) {
+ struct fx_renderer *renderer = pass->buffer->renderer;
+ struct fx_render_texture_options *tex_options = &fx_options->tex_options;
+ const struct wlr_render_texture_options *options = &tex_options->base;
+
+ pixman_region32_t translucent_region;
+ pixman_region32_init(&translucent_region);
+
+ struct wlr_box dst_box;
+ wlr_render_texture_options_get_dst_box(options, &dst_box);
+
+ // Gets the translucent region
+ pixman_box32_t surface_box = { 0, 0, dst_box.width, dst_box.height };
+ pixman_region32_copy(&translucent_region, fx_options->opaque_region);
+ pixman_region32_inverse(&translucent_region, &translucent_region, &surface_box);
+ if (!pixman_region32_not_empty(&translucent_region)) {
+ goto damage_finish;
+ }
+
+ struct fx_framebuffer *buffer = renderer->optimized_blur_buffer;
+ if (!buffer || !fx_options->use_optimized_blur) {
+ pixman_region32_translate(&translucent_region, dst_box.x, dst_box.y);
+ pixman_region32_intersect(&translucent_region, &translucent_region, options->clip);
+
+ // Render the blur into its own buffer
+ struct fx_render_blur_pass_options blur_options = *fx_options;
+ blur_options.tex_options.base.clip = &translucent_region;
+ blur_options.current_buffer = pass->buffer;
+ buffer = get_main_buffer_blur(pass, &blur_options);
+ }
+ struct wlr_texture *wlr_texture =
+ fx_texture_from_buffer(&renderer->wlr_renderer, buffer->buffer);
+ struct fx_texture *blur_texture = fx_get_texture(wlr_texture);
+ blur_texture->has_alpha = true;
+
+ // Get a stencil of the window ignoring transparent regions
+ if (fx_options->ignore_transparent) {
+ stencil_mask_init();
+
+ struct fx_render_texture_options tex_options = fx_options->tex_options;
+ tex_options.discard_transparent = true;
+ fx_render_pass_add_texture(pass, &tex_options);
+
+ stencil_mask_close(true);
+ }
+
+ // Draw the blurred texture
+ tex_options->base.dst_box = fx_options->monitor_box;
+ tex_options->base.src_box = (struct wlr_fbox) {
+ .x = 0,
+ .y = 0,
+ .width = buffer->buffer->width,
+ .height = buffer->buffer->height,
+ };
+ tex_options->base.texture = &blur_texture->wlr_texture;
+ fx_render_pass_add_texture(pass, tex_options);
+
+ wlr_texture_destroy(&blur_texture->wlr_texture);
+
+ // Finish stenciling
+ if (fx_options->ignore_transparent) {
+ stencil_mask_fini();
+ }
+
+damage_finish:
+ pixman_region32_fini(&translucent_region);
+}
+
+void fx_render_pass_add_optimized_blur(struct fx_gles_render_pass *pass,
+ struct fx_render_blur_pass_options *fx_options) {
+ struct fx_renderer *renderer = pass->buffer->renderer;
+ struct wlr_box monitor_box = fx_options->monitor_box;
+
+ pixman_region32_t fake_damage;
+ pixman_region32_init_rect(&fake_damage, 0, 0, monitor_box.width, monitor_box.height);
+
+ // Render the blur into its own buffer
+ struct fx_render_blur_pass_options blur_options = *fx_options;
+ blur_options.tex_options.base.clip = &fake_damage;
+ blur_options.current_buffer = pass->buffer;
+ struct fx_framebuffer *buffer = get_main_buffer_blur(pass, &blur_options);
+
+ // Update the optimized blur buffer if invalid
+ fx_framebuffer_get_or_create_custom(renderer, fx_options->output,
+ &renderer->optimized_blur_buffer);
+
+ // Render the newly blurred content into the blur_buffer
+ fx_renderer_read_to_buffer(pass, &fake_damage, renderer->optimized_blur_buffer, buffer);
+
+ pixman_region32_fini(&fake_damage);
+
+ renderer->blur_buffer_dirty = false;
+}
+
+void fx_renderer_read_to_buffer(struct fx_gles_render_pass *pass,
+ pixman_region32_t *region, struct fx_framebuffer *dst_buffer,
+ struct fx_framebuffer *src_buffer) {
+ if (!pixman_region32_not_empty(region)) {
+ return;
+ }
+
+ struct wlr_texture *src_tex =
+ fx_texture_from_buffer(&pass->buffer->renderer->wlr_renderer, src_buffer->buffer);
+ if (src_tex == NULL) {
+ return;
+ }
+
+ // Draw onto the dst_buffer
+ fx_framebuffer_bind(dst_buffer);
+ wlr_render_pass_add_texture(&pass->base, &(struct wlr_render_texture_options) {
+ .texture = src_tex,
+ .clip = region,
+ .blend_mode = WLR_RENDER_BLEND_MODE_NONE,
+ .dst_box = (struct wlr_box){
+ .width = dst_buffer->buffer->width,
+ .height = dst_buffer->buffer->height,
+ },
+ .src_box = (struct wlr_fbox){
+ .x = 0,
+ .y = 0,
+ .width = src_buffer->buffer->width,
+ .height = src_buffer->buffer->height,
+ },
+ });
+ wlr_texture_destroy(src_tex);
+
+ // Bind back to the main WLR buffer
+ fx_framebuffer_bind(pass->buffer);
+}
+
+
static const char *reset_status_str(GLenum status) {
switch (status) {
case GL_GUILTY_CONTEXT_RESET_KHR:
@@ -483,8 +837,9 @@ static struct fx_gles_render_pass *begin_buffer_pass(struct fx_framebuffer *buff
return pass;
}
-struct fx_gles_render_pass *fx_renderer_begin_buffer_pass(struct wlr_renderer *wlr_renderer,
- struct wlr_buffer *wlr_buffer, const struct wlr_buffer_pass_options *options) {
+struct fx_gles_render_pass *fx_renderer_begin_buffer_pass(
+ struct wlr_renderer *wlr_renderer, struct wlr_buffer *wlr_buffer,
+ struct wlr_output *output, const struct wlr_buffer_pass_options *options) {
struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
if (!wlr_egl_make_current(renderer->egl)) {
return NULL;
@@ -501,6 +856,15 @@ struct fx_gles_render_pass *fx_renderer_begin_buffer_pass(struct wlr_renderer *w
return NULL;
}
+ // Update the buffers if needed
+ if (output) {
+ fx_framebuffer_get_or_create_custom(renderer, output, &renderer->blur_saved_pixels_buffer);
+ fx_framebuffer_get_or_create_custom(renderer, output, &renderer->effects_buffer);
+ fx_framebuffer_get_or_create_custom(renderer, output, &renderer->effects_buffer_swapped);
+ }
+
+ pixman_region32_init(&renderer->blur_padding_region);
+
struct fx_gles_render_pass *pass = begin_buffer_pass(buffer, timer);
if (!pass) {
return NULL;
diff --git a/render/fx_renderer/fx_renderer.c b/render/fx_renderer/fx_renderer.c
index 0b20d7e..0923555 100644
--- a/render/fx_renderer/fx_renderer.c
+++ b/render/fx_renderer/fx_renderer.c
@@ -66,7 +66,7 @@ struct fx_render_timer *fx_get_render_timer(struct wlr_render_timer *wlr_timer)
return timer;
}
-static bool fx_bind_main_buffer(struct wlr_renderer *wlr_renderer,
+static bool fx_bind_buffer(struct wlr_renderer *wlr_renderer,
struct wlr_buffer *wlr_buffer) {
struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
@@ -138,6 +138,8 @@ static bool fx_renderer_begin(struct wlr_renderer *wlr_renderer, uint32_t width,
renderer->viewport_width = width;
renderer->viewport_height = height;
+ pixman_region32_init(&renderer->blur_padding_region);
+
// refresh projection matrix
matrix_projection(renderer->projection, width, height,
WL_OUTPUT_TRANSFORM_FLIPPED_180);
@@ -153,8 +155,8 @@ static bool fx_renderer_begin(struct wlr_renderer *wlr_renderer, uint32_t width,
}
static void fx_renderer_end(struct wlr_renderer *wlr_renderer) {
- fx_get_renderer_in_context(wlr_renderer);
- // no-op
+ struct fx_renderer *renderer = fx_get_renderer_in_context(wlr_renderer);
+ pixman_region32_fini(&renderer->blur_padding_region);
}
static void fx_renderer_clear(struct wlr_renderer *wlr_renderer,
@@ -239,6 +241,7 @@ static bool fx_render_subtexture_with_matrix(
glUniform2f(shader->size, box->width, box->height);
glUniform2f(shader->position, box->x, box->y);
glUniform1f(shader->radius, 0);
+ glUniform1f(shader->discard_transparent, false);
float tex_matrix[9];
wlr_matrix_identity(tex_matrix);
@@ -451,7 +454,7 @@ static void fx_renderer_destroy(struct wlr_renderer *wlr_renderer) {
static struct wlr_render_pass *begin_buffer_pass(struct wlr_renderer *wlr_renderer,
struct wlr_buffer *wlr_buffer, const struct wlr_buffer_pass_options *options) {
struct fx_gles_render_pass *pass =
- fx_renderer_begin_buffer_pass(wlr_renderer, wlr_buffer, options);
+ fx_renderer_begin_buffer_pass(wlr_renderer, wlr_buffer, NULL, options);
if (!pass) {
return NULL;
}
@@ -530,7 +533,7 @@ static void fx_render_timer_destroy(struct wlr_render_timer *wlr_timer) {
static const struct wlr_renderer_impl renderer_impl = {
.destroy = fx_renderer_destroy,
- .bind_buffer = fx_bind_main_buffer,
+ .bind_buffer = fx_bind_buffer,
.begin = fx_renderer_begin,
.end = fx_renderer_end,
.clear = fx_renderer_clear,
@@ -629,6 +632,67 @@ struct wlr_renderer *fx_renderer_create(struct wlr_backend *backend) {
return renderer_autocreate(backend, -1);
}
+static bool link_shaders(struct fx_renderer *renderer) {
+ // quad fragment shader
+ if (!link_quad_program(&renderer->shaders.quad)) {
+ wlr_log(WLR_ERROR, "Could not link quad shader");
+ goto error;
+ }
+ // fragment shaders
+ if (!link_tex_program(&renderer->shaders.tex_rgba, SHADER_SOURCE_TEXTURE_RGBA)) {
+ wlr_log(WLR_ERROR, "Could not link tex_RGBA shader");
+ goto error;
+ }
+ if (!link_tex_program(&renderer->shaders.tex_rgbx, SHADER_SOURCE_TEXTURE_RGBX)) {
+ wlr_log(WLR_ERROR, "Could not link tex_RGBX shader");
+ goto error;
+ }
+ if (!link_tex_program(&renderer->shaders.tex_ext, SHADER_SOURCE_TEXTURE_EXTERNAL)) {
+ wlr_log(WLR_ERROR, "Could not link tex_EXTERNAL shader");
+ goto error;
+ }
+
+ // stencil mask shader
+ if (!link_stencil_mask_program(&renderer->shaders.stencil_mask)) {
+ wlr_log(WLR_ERROR, "Could not link stencil mask shader");
+ goto error;
+ }
+ // box shadow shader
+ if (!link_box_shadow_program(&renderer->shaders.box_shadow)) {
+ wlr_log(WLR_ERROR, "Could not link box shadow shader");
+ goto error;
+ }
+
+ // Blur shaders
+ if (!link_blur1_program(&renderer->shaders.blur1)) {
+ wlr_log(WLR_ERROR, "Could not link blur1 shader");
+ goto error;
+ }
+ if (!link_blur2_program(&renderer->shaders.blur2)) {
+ wlr_log(WLR_ERROR, "Could not link blur2 shader");
+ goto error;
+ }
+ if (!link_blur_effects_program(&renderer->shaders.blur_effects)) {
+ wlr_log(WLR_ERROR, "Could not link blur_effects shader");
+ goto error;
+ }
+
+ return true;
+
+error:
+ glDeleteProgram(renderer->shaders.quad.program);
+ glDeleteProgram(renderer->shaders.tex_rgba.program);
+ glDeleteProgram(renderer->shaders.tex_rgbx.program);
+ glDeleteProgram(renderer->shaders.tex_ext.program);
+ glDeleteProgram(renderer->shaders.stencil_mask.program);
+ glDeleteProgram(renderer->shaders.box_shadow.program);
+ glDeleteProgram(renderer->shaders.blur1.program);
+ glDeleteProgram(renderer->shaders.blur2.program);
+ glDeleteProgram(renderer->shaders.blur_effects.program);
+
+ return false;
+}
+
struct wlr_renderer *fx_renderer_create_egl(struct wlr_egl *egl) {
if (!wlr_egl_make_current(egl)) {
return NULL;
@@ -751,6 +815,9 @@ struct wlr_renderer *fx_renderer_create_egl(struct wlr_egl *egl) {
if (!link_shaders(renderer)) {
goto error;
}
+
+ renderer->blur_buffer_dirty = false;
+
pop_fx_debug(renderer);
wlr_log(WLR_INFO, "FX RENDERER: Shaders Initialized Successfully");
@@ -760,13 +827,6 @@ struct wlr_renderer *fx_renderer_create_egl(struct wlr_egl *egl) {
return &renderer->wlr_renderer;
error:
- glDeleteProgram(renderer->shaders.quad.program);
- glDeleteProgram(renderer->shaders.tex_rgba.program);
- glDeleteProgram(renderer->shaders.tex_rgbx.program);
- glDeleteProgram(renderer->shaders.tex_ext.program);
- glDeleteProgram(renderer->shaders.stencil_mask.program);
- glDeleteProgram(renderer->shaders.box_shadow.program);
-
pop_fx_debug(renderer);
if (renderer->exts.KHR_debug) {
diff --git a/render/fx_renderer/gles2/shaders/blur1.frag b/render/fx_renderer/gles2/shaders/blur1.frag
new file mode 100644
index 0000000..e7cb1be
--- /dev/null
+++ b/render/fx_renderer/gles2/shaders/blur1.frag
@@ -0,0 +1,18 @@
+precision mediump float;
+varying mediump vec2 v_texcoord;
+uniform sampler2D tex;
+
+uniform float radius;
+uniform vec2 halfpixel;
+
+void main() {
+ vec2 uv = v_texcoord * 2.0;
+
+ vec4 sum = texture2D(tex, uv) * 4.0;
+ sum += texture2D(tex, uv - halfpixel.xy * radius);
+ sum += texture2D(tex, uv + halfpixel.xy * radius);
+ sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius);
+ sum += texture2D(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius);
+
+ gl_FragColor = sum / 8.0;
+}
diff --git a/render/fx_renderer/gles2/shaders/blur2.frag b/render/fx_renderer/gles2/shaders/blur2.frag
new file mode 100644
index 0000000..3755a29
--- /dev/null
+++ b/render/fx_renderer/gles2/shaders/blur2.frag
@@ -0,0 +1,22 @@
+precision mediump float;
+varying mediump vec2 v_texcoord;
+uniform sampler2D tex;
+
+uniform float radius;
+uniform vec2 halfpixel;
+
+void main() {
+ vec2 uv = v_texcoord / 2.0;
+
+ vec4 sum = texture2D(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius);
+
+ sum += texture2D(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0;
+ sum += texture2D(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius);
+ sum += texture2D(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0;
+ sum += texture2D(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius);
+ sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0;
+ sum += texture2D(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius);
+ sum += texture2D(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0;
+
+ gl_FragColor = sum / 12.0;
+}
diff --git a/render/fx_renderer/gles2/shaders/blur_effects.frag b/render/fx_renderer/gles2/shaders/blur_effects.frag
new file mode 100644
index 0000000..fcead5d
--- /dev/null
+++ b/render/fx_renderer/gles2/shaders/blur_effects.frag
@@ -0,0 +1,60 @@
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
+precision mediump float;
+#endif
+
+varying vec2 v_texcoord;
+
+uniform sampler2D tex;
+
+uniform float noise;
+uniform float brightness;
+uniform float contrast;
+uniform float saturation;
+
+mat4 brightnessMatrix() {
+ float b = brightness - 1.0;
+ return mat4(1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ b, b, b, 1);
+}
+
+mat4 contrastMatrix() {
+ float t = (1.0 - contrast) / 2.0;
+ return mat4(contrast, 0, 0, 0,
+ 0, contrast, 0, 0,
+ 0, 0, contrast, 0,
+ t, t, t, 1);
+}
+
+mat4 saturationMatrix() {
+ vec3 luminance = vec3(0.3086, 0.6094, 0.0820);
+ float oneMinusSat = 1.0 - saturation;
+ vec3 red = vec3(luminance.x * oneMinusSat);
+ red+= vec3(saturation, 0, 0);
+ vec3 green = vec3(luminance.y * oneMinusSat);
+ green += vec3(0, saturation, 0);
+ vec3 blue = vec3(luminance.z * oneMinusSat);
+ blue += vec3(0, 0, saturation);
+ return mat4(red, 0,
+ green, 0,
+ blue, 0,
+ 0, 0, 0, 1);
+}
+
+// Fast generative noise function
+float hash(vec2 p) {
+ return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
+}
+
+void main() {
+ vec4 color = texture2D(tex, v_texcoord);
+ color *= brightnessMatrix() * contrastMatrix() * saturationMatrix();
+ float noiseHash = hash(v_texcoord);
+ float noiseAmount = (mod(noiseHash, 1.0) - 0.5);
+ color.rgb += noiseAmount * noise;
+
+ gl_FragColor = color;
+}
diff --git a/render/fx_renderer/gles2/shaders/meson.build b/render/fx_renderer/gles2/shaders/meson.build
index 42f066e..a751e1a 100644
--- a/render/fx_renderer/gles2/shaders/meson.build
+++ b/render/fx_renderer/gles2/shaders/meson.build
@@ -6,6 +6,9 @@ shaders = [
'tex.frag',
'box_shadow.frag',
'stencil_mask.frag',
+ 'blur1.frag',
+ 'blur2.frag',
+ 'blur_effects.frag',
]
foreach name : shaders
diff --git a/render/fx_renderer/gles2/shaders/stencil_mask.frag b/render/fx_renderer/gles2/shaders/stencil_mask.frag
index 523adc8..e1fd76a 100644
--- a/render/fx_renderer/gles2/shaders/stencil_mask.frag
+++ b/render/fx_renderer/gles2/shaders/stencil_mask.frag
@@ -11,12 +11,12 @@ uniform vec2 position;
uniform float radius;
void main() {
- vec2 q = abs(gl_FragCoord.xy - position - half_size) - half_size + radius;
- float dist = min(max(q.x,q.y), 0.0) + length(max(q, 0.0)) - radius;
- float smoothedAlpha = 1.0 - smoothstep(-1.0, 0.5, dist);
- gl_FragColor = mix(vec4(0.0), vec4(1.0), smoothedAlpha);
+ vec2 q = abs(gl_FragCoord.xy - position - half_size) - half_size + radius;
+ float dist = min(max(q.x,q.y), 0.0) + length(max(q, 0.0)) - radius;
+ float smoothedAlpha = 1.0 - smoothstep(-1.0, 0.5, dist);
+ gl_FragColor = mix(vec4(0.0), vec4(1.0), smoothedAlpha);
- if (gl_FragColor.a < 1.0) {
- discard;
- }
+ if (gl_FragColor.a < 1.0) {
+ discard;
+ }
}
diff --git a/render/fx_renderer/gles2/shaders/tex.frag b/render/fx_renderer/gles2/shaders/tex.frag
index 8c14373..b7ba3fc 100644
--- a/render/fx_renderer/gles2/shaders/tex.frag
+++ b/render/fx_renderer/gles2/shaders/tex.frag
@@ -29,6 +29,7 @@ uniform float alpha;
uniform vec2 size;
uniform vec2 position;
uniform float radius;
+uniform bool discard_transparent;
vec4 sample_texture() {
#if SOURCE == SOURCE_TEXTURE_RGBA || SOURCE == SOURCE_TEXTURE_EXTERNAL
@@ -46,4 +47,8 @@ void main() {
float smooth = smoothstep(-1.0, 0.5, d);
gl_FragColor = mix(vec4(0), gl_FragColor, smooth);
}
+
+ if (discard_transparent && gl_FragColor.a == 0.0) {
+ discard;
+ }
}
diff --git a/render/fx_renderer/shaders.c b/render/fx_renderer/shaders.c
index 9257ca3..cd15eea 100644
--- a/render/fx_renderer/shaders.c
+++ b/render/fx_renderer/shaders.c
@@ -12,6 +12,9 @@
#include "tex_frag_src.h"
#include "stencil_mask_frag_src.h"
#include "box_shadow_frag_src.h"
+#include "blur1_frag_src.h"
+#include "blur2_frag_src.h"
+#include "blur_effects_frag_src.h"
GLuint compile_shader(GLuint type, const GLchar *src) {
GLuint shader = glCreateShader(type);
@@ -96,7 +99,7 @@ void load_gl_proc(void *proc_ptr, const char *name) {
// Shaders
-static bool link_quad_program(struct quad_shader *shader) {
+bool link_quad_program(struct quad_shader *shader) {
GLuint prog;
shader->program = prog = link_program(quad_frag_src);
if (!shader->program) {
@@ -110,7 +113,7 @@ static bool link_quad_program(struct quad_shader *shader) {
return true;
}
-static bool link_tex_program(struct tex_shader *shader,
+bool link_tex_program(struct tex_shader *shader,
enum fx_tex_shader_source source) {
GLchar frag_src[2048];
snprintf(frag_src, sizeof(frag_src),
@@ -130,11 +133,12 @@ static bool link_tex_program(struct tex_shader *shader,
shader->size = glGetUniformLocation(prog, "size");
shader->position = glGetUniformLocation(prog, "position");
shader->radius = glGetUniformLocation(prog, "radius");
+ shader->discard_transparent = glGetUniformLocation(prog, "discard_transparent");
return true;
}
-static bool link_stencil_mask_program(struct stencil_mask_shader *shader) {
+bool link_stencil_mask_program(struct stencil_mask_shader *shader) {
GLuint prog;
shader->program = prog = link_program(stencil_mask_frag_src);
if (!shader->program) {
@@ -151,7 +155,7 @@ static bool link_stencil_mask_program(struct stencil_mask_shader *shader) {
return true;
}
-static bool link_box_shadow_program(struct box_shadow_shader *shader) {
+bool link_box_shadow_program(struct box_shadow_shader *shader) {
GLuint prog;
shader->program = prog = link_program(box_shadow_frag_src);
if (!shader->program) {
@@ -168,36 +172,52 @@ static bool link_box_shadow_program(struct box_shadow_shader *shader) {
return true;
}
-bool link_shaders(struct fx_renderer *renderer) {
- // quad fragment shader
- if (!link_quad_program(&renderer->shaders.quad)) {
- wlr_log(WLR_ERROR, "Could not link quad shader");
- return false;
- }
- // fragment shaders
- if (!link_tex_program(&renderer->shaders.tex_rgba, SHADER_SOURCE_TEXTURE_RGBA)) {
- wlr_log(WLR_ERROR, "Could not link tex_RGBA shader");
- return false;
- }
- if (!link_tex_program(&renderer->shaders.tex_rgbx, SHADER_SOURCE_TEXTURE_RGBX)) {
- wlr_log(WLR_ERROR, "Could not link tex_RGBX shader");
- return false;
- }
- if (!link_tex_program(&renderer->shaders.tex_ext, SHADER_SOURCE_TEXTURE_EXTERNAL)) {
- wlr_log(WLR_ERROR, "Could not link tex_EXTERNAL shader");
+bool link_blur1_program(struct blur_shader *shader) {
+ GLuint prog;
+ shader->program = prog = link_program(blur1_frag_src);
+ if (!shader->program) {
return false;
}
+ shader->proj = glGetUniformLocation(prog, "proj");
+ shader->tex = glGetUniformLocation(prog, "tex");
+ shader->pos_attrib = glGetAttribLocation(prog, "pos");
+ shader->tex_proj = glGetUniformLocation(prog, "tex_proj");
+ shader->radius = glGetUniformLocation(prog, "radius");
+ shader->halfpixel = glGetUniformLocation(prog, "halfpixel");
- // stencil mask shader
- if (!link_stencil_mask_program(&renderer->shaders.stencil_mask)) {
- wlr_log(WLR_ERROR, "Could not link stencil mask shader");
+ return true;
+}
+
+bool link_blur2_program(struct blur_shader *shader) {
+ GLuint prog;
+ shader->program = prog = link_program(blur2_frag_src);
+ if (!shader->program) {
return false;
}
- // box shadow shader
- if (!link_box_shadow_program(&renderer->shaders.box_shadow)) {
- wlr_log(WLR_ERROR, "Could not link box shadow shader");
+ shader->proj = glGetUniformLocation(prog, "proj");
+ shader->tex = glGetUniformLocation(prog, "tex");
+ shader->pos_attrib = glGetAttribLocation(prog, "pos");
+ shader->tex_proj = glGetUniformLocation(prog, "tex_proj");
+ shader->radius = glGetUniformLocation(prog, "radius");
+ shader->halfpixel = glGetUniformLocation(prog, "halfpixel");
+
+ return true;
+}
+
+bool link_blur_effects_program(struct blur_effects_shader *shader) {
+ GLuint prog;
+ shader->program = prog = link_program(blur_effects_frag_src);
+ if (!shader->program) {
return false;
}
+ shader->proj = glGetUniformLocation(prog, "proj");
+ shader->tex = glGetUniformLocation(prog, "tex");
+ shader->pos_attrib = glGetAttribLocation(prog, "pos");
+ shader->tex_proj = glGetUniformLocation(prog, "tex_proj");
+ shader->noise = glGetUniformLocation(prog, "noise");
+ shader->brightness = glGetUniformLocation(prog, "brightness");
+ shader->contrast = glGetUniformLocation(prog, "contrast");
+ shader->saturation = glGetUniformLocation(prog, "saturation");
return true;
}