diff --git a/include/scenefx/render/fx_renderer/fx_effect_framebuffers.h b/include/scenefx/render/fx_renderer/fx_effect_framebuffers.h index f05b46ba..26b137fd 100644 --- a/include/scenefx/render/fx_renderer/fx_effect_framebuffers.h +++ b/include/scenefx/render/fx_renderer/fx_effect_framebuffers.h @@ -14,6 +14,9 @@ struct fx_effect_framebuffers { // Contains the blurred background for tiled windows struct fx_framebuffer *optimized_blur_buffer; + // Contains the non-blurred background for tiled windows. Used for blurring + // optimized surfaces with an alpha. Just as inefficient as the regular blur. + struct fx_framebuffer *optimized_no_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 diff --git a/include/scenefx/render/pass.h b/include/scenefx/render/pass.h index 491fabc9..ce1caf9c 100644 --- a/include/scenefx/render/pass.h +++ b/include/scenefx/render/pass.h @@ -102,6 +102,7 @@ struct fx_render_blur_pass_options { struct blur_data *blur_data; bool use_optimized_blur; bool ignore_transparent; + float blur_strength; }; /** diff --git a/include/scenefx/types/fx/blur_data.h b/include/scenefx/types/fx/blur_data.h index 82b3c982..967c2a0f 100644 --- a/include/scenefx/types/fx/blur_data.h +++ b/include/scenefx/types/fx/blur_data.h @@ -6,7 +6,7 @@ struct blur_data { int num_passes; - int radius; + float radius; float noise; float brightness; float contrast; @@ -21,4 +21,6 @@ bool blur_data_should_parameters_blur_effects(struct blur_data *blur_data); int blur_data_calc_size(struct blur_data *blur_data); +struct blur_data blur_data_apply_strength(struct blur_data *blur_data, float strength); + #endif diff --git a/include/scenefx/types/wlr_scene.h b/include/scenefx/types/wlr_scene.h index f1908011..ca1e05a8 100644 --- a/include/scenefx/types/wlr_scene.h +++ b/include/scenefx/types/wlr_scene.h @@ -151,6 +151,8 @@ struct wlr_scene_rect { enum corner_location corners; bool backdrop_blur; bool backdrop_blur_optimized; + float backdrop_blur_strength; + float backdrop_blur_alpha; bool accepts_input; struct clipped_region clipped_region; @@ -215,6 +217,8 @@ struct wlr_scene_buffer { bool backdrop_blur; bool backdrop_blur_optimized; bool backdrop_blur_ignore_transparent; + float backdrop_blur_strength; + float backdrop_blur_alpha; enum corner_location corners; float opacity; @@ -534,6 +538,27 @@ void wlr_scene_rect_set_backdrop_blur(struct wlr_scene_rect *rect, void wlr_scene_rect_set_backdrop_blur_optimized(struct wlr_scene_rect *rect, bool enabled); +/** + * Sets the blur strength from 1.0f -> 0.0f. This adjusts how strong the blur is + * relative to the base 1.0 value. + * + * Can be used combined with backdrop_blur_alpha to create a good looking + * fade-out effect. + */ +void wlr_scene_rect_set_backdrop_blur_strength(struct wlr_scene_rect *rect, + float strength); + +/** + * Sets the blur alpha from 1.0f -> 0.0f. This adjusts the actual alpha of the blur. + * Default is 1.0f. + * + * Lower values without also adjusting the backdrop_blur_strength will look off. + * + * Can be used combined with backdrop_blur_strength to create a good looking + * fade-out effect. + */ +void wlr_scene_rect_set_backdrop_blur_alpha(struct wlr_scene_rect *rect, + float alpha); /** * Add a node displaying a shadow to the scene-graph. */ @@ -720,6 +745,28 @@ void wlr_scene_buffer_set_backdrop_blur_optimized(struct wlr_scene_buffer *scene void wlr_scene_buffer_set_backdrop_blur_ignore_transparent( struct wlr_scene_buffer *scene_buffer, bool enabled); +/** + * Sets the blur strength from 1.0f -> 0.0f. This adjusts how strong the blur is + * relative to the base 1.0 value. + * + * Can be used combined with backdrop_blur_alpha to create a good looking + * fade-out effect. + */ +void wlr_scene_buffer_set_backdrop_blur_strength(struct wlr_scene_buffer *scene_buffer, + float strength); + +/** + * Sets the blur alpha from 1.0f -> 0.0f. This adjusts the actual alpha of the blur. + * Default is 1.0f. + * + * Lower values without also adjusting the backdrop_blur_strength will look off. + * + * Can be used combined with backdrop_blur_strength to create a good looking + * fade-out effect. + */ +void wlr_scene_buffer_set_backdrop_blur_alpha(struct wlr_scene_buffer *scene_buffer, + float alpha); + /** * Calls the buffer's frame_done signal. */ diff --git a/render/fx_renderer/fx_pass.c b/render/fx_renderer/fx_pass.c index 0592c4d4..f3ec78b7 100644 --- a/render/fx_renderer/fx_pass.c +++ b/render/fx_renderer/fx_pass.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -828,20 +829,27 @@ static void render_blur_effects(struct fx_gles_render_pass *pass, wlr_texture_destroy(options->texture); } -// Blurs the main_buffer content and returns the blurred framebuffer +// Blurs the main_buffer content and returns the blurred framebuffer. +// Returns NULL when the blur parameters reach 0. 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; struct wlr_box monitor_box = get_monitor_box(pass->output); + // We don't want to affect the reference blur_data + struct blur_data blur_data = blur_data_apply_strength(fx_options->blur_data, fx_options->blur_strength); + if (fx_options->blur_strength <= 0 || blur_data.num_passes <= 0 || blur_data.radius <= 0) { + return NULL; + } + fx_options->blur_data = &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, monitor_box.width, monitor_box.height); - wlr_region_expand(&damage, &damage, blur_data_calc_size(fx_options->blur_data)); + wlr_region_expand(&damage, &damage, blur_data_calc_size(&blur_data)); // damage region will be scaled, make a temp pixman_region32_t scaled_damage; @@ -860,13 +868,13 @@ static struct fx_framebuffer *get_main_buffer_blur(struct fx_gles_render_pass *p fx_options->tex_options.base.filter_mode = WLR_SCALE_FILTER_BILINEAR; // Downscale - for (int i = 0; i < blur_data->num_passes; ++i) { + 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) { + 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); @@ -875,7 +883,7 @@ static struct fx_framebuffer *get_main_buffer_blur(struct fx_gles_render_pass *p pixman_region32_fini(&scaled_damage); // Render additional blur effects like saturation, noise, contrast, etc... - if (blur_data_should_parameters_blur_effects(blur_data) + if (blur_data_should_parameters_blur_effects(&blur_data) && pixman_region32_not_empty(&damage)) { if (fx_options->current_buffer == pass->fx_effect_framebuffers->effects_buffer) { fx_framebuffer_bind(pass->fx_effect_framebuffers->effects_buffer_swapped); @@ -926,8 +934,9 @@ void fx_render_pass_add_blur(struct fx_gles_render_pass *pass, goto damage_finish; } + const bool has_strength = fx_options->blur_strength < 1.0; struct fx_framebuffer *buffer = pass->fx_effect_framebuffers->optimized_blur_buffer; - if (!buffer || !fx_options->use_optimized_blur) { + if (!buffer || !fx_options->use_optimized_blur || has_strength) { if (!buffer) { wlr_log(WLR_ERROR, "Warning: Failed to use optimized blur"); } @@ -937,8 +946,19 @@ void fx_render_pass_add_blur(struct fx_gles_render_pass *pass, // 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; + if (fx_options->use_optimized_blur && has_strength + // If the optimized blur hasn't been rendered yet + && pass->fx_effect_framebuffers->optimized_no_blur_buffer) { + // Re-blur the saved non-blurred version of the optimized blur. + // Isn't as efficient as just using the optimized blur buffer + blur_options.current_buffer = pass->fx_effect_framebuffers->optimized_no_blur_buffer; + } else { + blur_options.current_buffer = pass->buffer; + } buffer = get_main_buffer_blur(pass, &blur_options); + if (!buffer) { + goto damage_finish; + } } struct wlr_texture *wlr_texture = fx_texture_from_buffer(&renderer->wlr_renderer, buffer->buffer); @@ -1002,6 +1022,8 @@ bool fx_render_pass_add_optimized_blur(struct fx_gles_render_pass *pass, bool failed = false; fx_framebuffer_get_or_create_custom(renderer, pass->output, NULL, &pass->fx_effect_framebuffers->optimized_blur_buffer, &failed); + fx_framebuffer_get_or_create_custom(renderer, pass->output, NULL, + &pass->fx_effect_framebuffers->optimized_no_blur_buffer, &failed); if (failed) { goto finish; } @@ -1010,6 +1032,10 @@ bool fx_render_pass_add_optimized_blur(struct fx_gles_render_pass *pass, fx_renderer_read_to_buffer(pass, &clip, pass->fx_effect_framebuffers->optimized_blur_buffer, buffer); + // Save the current scene pass state + fx_renderer_read_to_buffer(pass, &clip, + pass->fx_effect_framebuffers->optimized_no_blur_buffer, pass->buffer); + finish: pixman_region32_fini(&clip); return !failed; diff --git a/types/fx/blur_data.c b/types/fx/blur_data.c index 423c02af..fcc8c748 100644 --- a/types/fx/blur_data.c +++ b/types/fx/blur_data.c @@ -25,3 +25,39 @@ bool blur_data_should_parameters_blur_effects(struct blur_data *blur_data) { int blur_data_calc_size(struct blur_data *blur_data) { return pow(2, blur_data->num_passes + 1) * blur_data->radius; } + +static float lerp(float a, float b, float f) { + return (a * (1.0 - f)) + (b * f); +} + +static void adjust_post_effects(struct blur_data *blur_data, float strength) { + blur_data->brightness = lerp(1.0, blur_data->brightness, strength); + blur_data->contrast = lerp(1.0, blur_data->contrast, strength); + blur_data->noise = lerp(0.0, blur_data->noise, strength); + blur_data->saturation = lerp(1.0, blur_data->saturation, strength); +} + +struct blur_data blur_data_apply_strength(struct blur_data *ref_blur_data, float strength) { + struct blur_data blur_data = *ref_blur_data; + + if (strength >= 1.0f) { + adjust_post_effects(&blur_data, 1.0f); + return blur_data; + } else if (strength <= 0.0f) { + adjust_post_effects(&blur_data, 0.0f); + blur_data.num_passes = 0; + blur_data.radius = 0.0f; + return blur_data; + } + + // Calculate the new blur strength from the new alpha multiplied blur size + // Also make sure that the values are never larger than the initial values + int new_size = blur_data_calc_size(ref_blur_data) * strength; + blur_data.num_passes = fmax(0, fmin(ref_blur_data->num_passes, ceilf(log2(new_size / ref_blur_data->radius)))); + blur_data.radius = fmax(0, fmin(ref_blur_data->radius, new_size / pow(2, blur_data.num_passes + 1))); + + adjust_post_effects(&blur_data, strength); + + return blur_data; +} + diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 312f532b..5973537c 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -828,6 +828,8 @@ struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, scene_rect->clipped_region = clipped_region_get_default(); scene_rect->backdrop_blur = false; scene_rect->backdrop_blur_optimized = false; + scene_rect->backdrop_blur_strength = 1.0f; + scene_rect->backdrop_blur_alpha = 1.0f; scene_node_update(&scene_rect->node, NULL); @@ -873,6 +875,25 @@ void wlr_scene_rect_set_backdrop_blur_optimized(struct wlr_scene_rect *rect, scene_node_update(&rect->node, NULL); } +void wlr_scene_rect_set_backdrop_blur_strength(struct wlr_scene_rect *rect, + float strength) { + if (rect->backdrop_blur_strength == strength) { + return; + } + rect->backdrop_blur_strength = strength; + scene_node_update(&rect->node, NULL); +} + +void wlr_scene_rect_set_backdrop_blur_alpha(struct wlr_scene_rect *rect, + float alpha) { + if (rect->backdrop_blur_alpha == alpha) { + return; + } + + rect->backdrop_blur_alpha = alpha; + scene_node_update(&rect->node, NULL); +} + static void scene_buffer_handle_buffer_release(struct wl_listener *listener, void *data) { struct wlr_scene_buffer *scene_buffer = @@ -1205,6 +1226,8 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, scene_buffer->backdrop_blur = false; scene_buffer->backdrop_blur_optimized = false; scene_buffer->backdrop_blur_ignore_transparent = true; + scene_buffer->backdrop_blur_strength = 1.0f; + scene_buffer->backdrop_blur_alpha = 1.0f; scene_buffer->corners = CORNER_LOCATION_NONE; scene_buffer_set_buffer(scene_buffer, buffer); @@ -1511,6 +1534,25 @@ void wlr_scene_buffer_set_backdrop_blur_ignore_transparent( scene_node_update(&scene_buffer->node, NULL); } +void wlr_scene_buffer_set_backdrop_blur_strength(struct wlr_scene_buffer *scene_buffer, + float strength) { + if (scene_buffer->backdrop_blur_strength == strength) { + return; + } + scene_buffer->backdrop_blur_strength = strength; + scene_node_update(&scene_buffer->node, NULL); +} + +void wlr_scene_buffer_set_backdrop_blur_alpha(struct wlr_scene_buffer *scene_buffer, + float alpha) { + if (scene_buffer->backdrop_blur_alpha == alpha) { + return; + } + + scene_buffer->backdrop_blur_alpha = alpha; + scene_node_update(&scene_buffer->node, NULL); +} + static struct wlr_texture *scene_buffer_get_texture( struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) { if (scene_buffer->buffer == NULL || scene_buffer->texture != NULL) { @@ -1846,11 +1888,6 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren scene_node_opaque_region(node, x, y, &opaque_region); logical_to_buffer_coords(&opaque_region, data, false); - /* TODO: should this be configurable? Borked when not 1.0, probably due to - lack of premultiplication in the frag shader - */ - float blur_alpha = 1.0; - struct fx_render_blur_pass_options blur_options = { .tex_options = { .base = (struct wlr_render_texture_options) { @@ -1859,7 +1896,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren .dst_box = dst_box, .transform = WL_OUTPUT_TRANSFORM_NORMAL, .clip = &render_region, - .alpha = &blur_alpha, + .alpha = &scene_rect->backdrop_blur_alpha, .filter_mode = WLR_SCALE_FILTER_BILINEAR, .blend_mode = WLR_RENDER_BLEND_MODE_PREMULTIPLIED, }, @@ -1872,6 +1909,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren .use_optimized_blur = scene_rect->backdrop_blur_optimized, .blur_data = &scene->blur_data, .ignore_transparent = false, + .blur_strength = scene_rect->backdrop_blur_strength, }; // Render the actual blur behind the surface fx_render_pass_add_blur(data->render_pass, &blur_options); @@ -1942,6 +1980,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren .discard_transparent = false, }, .blur_data = &scene->blur_data, + .blur_strength = 1.0f, }; bool result = fx_render_pass_add_optimized_blur(data->render_pass, &blur_options); if (result) { @@ -2031,8 +2070,6 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren pixman_region32_translate(&opaque_region, -scene_buffer->src_box.x, -scene_buffer->src_box.y); - // TODO: should I be configurable? We should probably move blur to a node - float blur_alpha = 1.0; struct fx_render_blur_pass_options blur_options = { .tex_options = { .base = (struct wlr_render_texture_options) { @@ -2041,7 +2078,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren .dst_box = dst_box, .transform = WL_OUTPUT_TRANSFORM_NORMAL, .clip = &render_region, // Render with the smaller region, clipping CSD - .alpha = &blur_alpha, + .alpha = &scene_buffer->backdrop_blur_alpha, .filter_mode = WLR_SCALE_FILTER_BILINEAR, }, .clip_box = &dst_box, @@ -2053,6 +2090,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren .use_optimized_blur = scene_buffer->backdrop_blur_optimized, .blur_data = &scene->blur_data, .ignore_transparent = scene_buffer->backdrop_blur_ignore_transparent, + .blur_strength = scene_buffer->backdrop_blur_strength, }; // Render the actual blur behind the surface fx_render_pass_add_blur(data->render_pass, &blur_options); @@ -2975,7 +3013,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, // No need to compensate for blur artifacts when the damage spans // the whole output whole_output_blur_damaged = true; - }else { + } else { // copy the surrounding content where the blur would display artifacts // and draw it above the artifacts pixman_region32_t extended_damage;