diff --git a/include/render/fx_renderer/fx_renderer.h b/include/render/fx_renderer/fx_renderer.h index 4a95634..83ac305 100644 --- a/include/render/fx_renderer/fx_renderer.h +++ b/include/render/fx_renderer/fx_renderer.h @@ -13,7 +13,6 @@ #include "render/fx_renderer/shaders.h" #include "render/pass.h" -#include "scenefx/types/fx/shadow_data.h" struct fx_pixel_format { uint32_t drm_format; @@ -48,13 +47,15 @@ struct fx_framebuffer { struct wlr_addon addon; }; +/** Should only be used with custom fbs */ +void fx_framebuffer_get_or_create_custom(struct fx_renderer *fx_renderer, + struct wlr_output *output, struct fx_framebuffer **fx_buffer); + struct fx_framebuffer *fx_framebuffer_get_or_create(struct fx_renderer *renderer, struct wlr_buffer *wlr_buffer); void fx_framebuffer_bind(struct fx_framebuffer *buffer); -void fx_framebuffer_bind_wlr_fbo(struct fx_renderer *renderer); - void fx_framebuffer_destroy(struct fx_framebuffer *buffer); /// @@ -148,6 +149,9 @@ struct fx_renderer { struct tex_shader tex_ext; struct box_shadow_shader box_shadow; struct stencil_mask_shader stencil_mask; + struct blur_shader blur1; + struct blur_shader blur2; + struct blur_effects_shader blur_effects; } shaders; struct wl_list buffers; // fx_framebuffer.link @@ -155,6 +159,21 @@ struct fx_renderer { struct fx_framebuffer *current_buffer; uint32_t viewport_width, viewport_height; + + // Contains the blurred background for tiled windows + struct fx_framebuffer *optimized_blur_buffer; + // Contains the original pixels to draw over the areas where artifact are visible + struct fx_framebuffer *blur_saved_pixels_buffer; + // Blur swaps between the two effects buffers everytime it scales the image + // Buffer used for effects + struct fx_framebuffer *effects_buffer; + // Swap buffer used for effects + struct fx_framebuffer *effects_buffer_swapped; + + // The region where there's blur + pixman_region32_t blur_padding_region; + + bool blur_buffer_dirty; }; bool wlr_renderer_is_fx(struct wlr_renderer *wlr_renderer); diff --git a/include/render/fx_renderer/shaders.h b/include/render/fx_renderer/shaders.h index 92a14d5..226dd36 100644 --- a/include/render/fx_renderer/shaders.h +++ b/include/render/fx_renderer/shaders.h @@ -27,6 +27,8 @@ struct quad_shader { GLint pos_attrib; }; +bool link_quad_program(struct quad_shader *shader); + struct tex_shader { GLuint program; GLint proj; @@ -37,8 +39,11 @@ struct tex_shader { GLint size; GLint position; GLint radius; + GLint discard_transparent; }; +bool link_tex_program(struct tex_shader *shader, enum fx_tex_shader_source source); + struct stencil_mask_shader { GLuint program; GLint proj; @@ -49,6 +54,8 @@ struct stencil_mask_shader { GLint radius; }; +bool link_stencil_mask_program(struct stencil_mask_shader *shader); + struct box_shadow_shader { GLuint program; GLint proj; @@ -60,6 +67,33 @@ struct box_shadow_shader { GLint corner_radius; }; -bool link_shaders(struct fx_renderer *renderer); +bool link_box_shadow_program(struct box_shadow_shader *shader); + +struct blur_shader { + GLuint program; + GLint proj; + GLint tex_proj; + GLint tex; + GLint pos_attrib; + GLint radius; + GLint halfpixel; +}; + +bool link_blur1_program(struct blur_shader *shader); +bool link_blur2_program(struct blur_shader *shader); + +struct blur_effects_shader { + GLuint program; + GLint proj; + GLint tex_proj; + GLint tex; + GLint pos_attrib; + GLfloat noise; + GLfloat brightness; + GLfloat contrast; + GLfloat saturation; +}; + +bool link_blur_effects_program(struct blur_effects_shader *shader); #endif diff --git a/include/render/pass.h b/include/render/pass.h index 52666fe..db33ff6 100644 --- a/include/render/pass.h +++ b/include/render/pass.h @@ -20,14 +20,16 @@ struct fx_gles_render_pass { * Callers must call wlr_render_pass_submit() once they are done with the * render pass. */ -struct fx_gles_render_pass *fx_renderer_begin_buffer_pass(struct wlr_renderer *renderer, - struct wlr_buffer *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_render_texture_options { struct wlr_render_texture_options base; float scale; struct wlr_box *clip_box; // Used to clip csd. Ignored if NULL int corner_radius; + bool discard_transparent; }; struct fx_render_texture_options fx_render_texture_options_default( @@ -41,6 +43,17 @@ struct fx_render_rect_options { struct fx_render_rect_options fx_render_rect_options_default( const struct wlr_render_rect_options *base); +struct fx_render_blur_pass_options { + struct fx_render_texture_options tex_options; + pixman_region32_t *opaque_region; + struct wlr_output *output; + struct wlr_box monitor_box; + struct fx_framebuffer *current_buffer; + struct blur_data *blur_data; + bool use_optimized_blur; + bool ignore_transparent; +}; + /** * Render a fx texture. */ @@ -66,4 +79,23 @@ void fx_render_pass_add_box_shadow(struct fx_gles_render_pass *pass, const struct fx_render_rect_options *fx_options, int corner_radius, struct shadow_data *shadow_data); +/** + * Render blur. + */ +void fx_render_pass_add_blur(struct fx_gles_render_pass *pass, + struct fx_render_blur_pass_options *fx_options); + +/** + * Render optimized blur. + */ +void fx_render_pass_add_optimized_blur(struct fx_gles_render_pass *pass, + struct fx_render_blur_pass_options *fx_options); + +/** + * Render from one buffer to another + */ +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); + #endif diff --git a/include/scenefx/types/fx/blur_data.h b/include/scenefx/types/fx/blur_data.h new file mode 100644 index 0000000..e6a7c29 --- /dev/null +++ b/include/scenefx/types/fx/blur_data.h @@ -0,0 +1,24 @@ +#ifndef TYPES_FX_BLUR_DATA_H +#define TYPES_FX_BLUR_DATA_H + +#include +#include + +struct blur_data { + int num_passes; + int radius; + float noise; + float brightness; + float contrast; + float saturation; +}; + +struct blur_data blur_data_get_default(void); + +bool blur_data_should_parameters_blur_effects(struct blur_data *blur_data); + +bool blur_data_cmp(struct blur_data *a, struct blur_data *b); + +int blur_data_calc_size(struct blur_data *blur_data); + +#endif diff --git a/include/scenefx/types/wlr_scene.h b/include/scenefx/types/wlr_scene.h index 21558d8..5925931 100644 --- a/include/scenefx/types/wlr_scene.h +++ b/include/scenefx/types/wlr_scene.h @@ -179,6 +179,7 @@ struct wlr_scene_buffer { float opacity; int corner_radius; struct shadow_data shadow_data; + enum wlr_scale_filter_mode filter_mode; struct wlr_fbox src_box; int dst_width, dst_height; 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 #include #include +#include #include #include +#include #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; } diff --git a/types/fx/blur_data.c b/types/fx/blur_data.c new file mode 100644 index 0000000..3aa618b --- /dev/null +++ b/types/fx/blur_data.c @@ -0,0 +1,32 @@ +#include "scenefx/types/fx/blur_data.h" + +struct blur_data blur_data_get_default(void) { + return (struct blur_data) { + .radius = 5, + .num_passes = 3, + .noise = 0.02, + .brightness = 0.9, + .contrast = 0.9, + .saturation = 1.1, + }; +} + +bool blur_data_should_parameters_blur_effects(struct blur_data *blur_data) { + return blur_data->brightness != 1.0f + || blur_data->saturation != 1.0f + || blur_data->contrast != 1.0f + || blur_data->noise > 0.0f; +} + +bool blur_data_cmp(struct blur_data *a, struct blur_data *b) { + return a->radius == b->radius && + a->num_passes && b->num_passes && + a->noise == b->noise && + a->brightness == b->brightness && + a->contrast == b->contrast && + a->saturation == b->saturation; +} + +int blur_data_calc_size(struct blur_data *blur_data) { + return pow(2, blur_data->num_passes + 1) * blur_data->radius; +} diff --git a/types/fx/meson.build b/types/fx/meson.build index b7f0207..2170f35 100644 --- a/types/fx/meson.build +++ b/types/fx/meson.build @@ -1,3 +1,4 @@ wlr_files += files( - 'shadow_data.c' + 'shadow_data.c', + 'blur_data.c', ) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 266dc63..0545e70 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1312,6 +1312,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren .clip_box = &xdg_box, .corner_radius = scene_buffer->corner_radius * data->scale, }; + fx_render_pass_add_texture(data->render_pass, &tex_options); struct wlr_scene_output_sample_event sample_event = { @@ -1919,8 +1920,8 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, } struct fx_gles_render_pass *render_pass = - fx_renderer_begin_buffer_pass(output->renderer, buffer, - &(struct wlr_buffer_pass_options){ + fx_renderer_begin_buffer_pass(output->renderer, buffer, output, + &(struct wlr_buffer_pass_options) { .timer = timer ? timer->render_timer : NULL, } );