From 657da122aba29932533e98c76ba3ace787a0db1c Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Thu, 10 Aug 2023 21:18:47 +0200 Subject: [PATCH] Removed the need for our own main FBO, also fixes some damage bugs --- .../sway/desktop/fx_renderer/fx_renderer.h | 22 +++-- include/sway/tree/workspace.h | 3 + sway/desktop/fx_renderer/fx_framebuffer.c | 10 +- sway/desktop/fx_renderer/fx_renderer.c | 45 +++++---- sway/desktop/output.c | 32 +------ sway/desktop/render.c | 95 +++++++++++++------ sway/tree/workspace.c | 48 ++++++++++ 7 files changed, 164 insertions(+), 91 deletions(-) diff --git a/include/sway/desktop/fx_renderer/fx_renderer.h b/include/sway/desktop/fx_renderer/fx_renderer.h index 624bd030..805553a1 100644 --- a/include/sway/desktop/fx_renderer/fx_renderer.h +++ b/include/sway/desktop/fx_renderer/fx_renderer.h @@ -120,12 +120,22 @@ struct fx_renderer { int viewport_width, viewport_height; - struct fx_framebuffer wlr_buffer; // Just the framebuffer used by wlroots - struct fx_framebuffer main_buffer; // The main FB used for rendering - struct fx_framebuffer blur_buffer; // Contains the blurred background for tiled windows + struct wlr_output *wlr_output; + + // The framebuffer used by wlroots + struct fx_framebuffer wlr_buffer; + // Contains the blurred background for tiled windows + struct fx_framebuffer 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 - struct fx_framebuffer effects_buffer; // Buffer used for effects - struct fx_framebuffer effects_buffer_swapped; // Swap buffer used for effects + // 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; @@ -155,7 +165,7 @@ struct fx_renderer { } shaders; }; -struct fx_renderer *fx_renderer_create(struct wlr_egl *egl); +struct fx_renderer *fx_renderer_create(struct wlr_egl *egl, struct wlr_output *output); void fx_renderer_fini(struct fx_renderer *renderer); diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 2ff51ea3..0de93d8b 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -94,6 +94,9 @@ void workspace_detect_urgent(struct sway_workspace *workspace); bool should_workspace_have_blur(struct sway_workspace *ws); +void workspace_get_blur_region(struct sway_workspace *ws, + pixman_region32_t *blur_region); + void workspace_for_each_container(struct sway_workspace *ws, void (*f)(struct sway_container *con, void *data), void *data); diff --git a/sway/desktop/fx_renderer/fx_framebuffer.c b/sway/desktop/fx_renderer/fx_framebuffer.c index 3ef3129a..dd8c27b1 100644 --- a/sway/desktop/fx_renderer/fx_framebuffer.c +++ b/sway/desktop/fx_renderer/fx_framebuffer.c @@ -67,17 +67,11 @@ void fx_framebuffer_add_stencil_buffer(struct fx_framebuffer *buffer, int width, if (first_alloc || buffer->stencil_buffer.width != width || buffer->stencil_buffer.height != height) { glBindRenderbuffer(GL_RENDERBUFFER, buffer->stencil_buffer.rb); glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer->stencil_buffer.rb); buffer->stencil_buffer.width = width; buffer->stencil_buffer.height = height; - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - sway_log(SWAY_ERROR, "Stencil buffer incomplete, couldn't create! (FB status: %i)", status); - return; - } - sway_log(SWAY_DEBUG, "Stencil buffer created, status %i", status); } + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer->stencil_buffer.rb); } void fx_framebuffer_release(struct fx_framebuffer *buffer) { diff --git a/sway/desktop/fx_renderer/fx_renderer.c b/sway/desktop/fx_renderer/fx_renderer.c index bfe4a7ae..8d2d058e 100644 --- a/sway/desktop/fx_renderer/fx_renderer.c +++ b/sway/desktop/fx_renderer/fx_renderer.c @@ -16,6 +16,8 @@ #include "log.h" #include "sway/desktop/fx_renderer/fx_framebuffer.h" #include "sway/desktop/fx_renderer/fx_renderer.h" +#include "sway/desktop/fx_renderer/fx_stencilbuffer.h" +#include "sway/desktop/fx_renderer/fx_texture.h" #include "sway/desktop/fx_renderer/matrix.h" #include "sway/server.h" @@ -252,12 +254,14 @@ static void load_gl_proc(void *proc_ptr, const char *name) { *(void **)proc_ptr = proc; } -struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) { +struct fx_renderer *fx_renderer_create(struct wlr_egl *egl, struct wlr_output *wlr_output) { struct fx_renderer *renderer = calloc(1, sizeof(struct fx_renderer)); if (renderer == NULL) { return NULL; } + renderer->wlr_output = wlr_output; + // TODO: wlr_egl_make_current or eglMakeCurrent? // TODO: assert instead of conditional statement? if (!eglMakeCurrent(wlr_egl_get_display(egl), EGL_NO_SURFACE, EGL_NO_SURFACE, @@ -266,8 +270,9 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) { return NULL; } - renderer->main_buffer = fx_framebuffer_create(); + renderer->wlr_buffer = fx_framebuffer_create(); renderer->blur_buffer = fx_framebuffer_create(); + renderer->blur_saved_pixels_buffer = fx_framebuffer_create(); renderer->effects_buffer = fx_framebuffer_create(); renderer->effects_buffer_swapped = fx_framebuffer_create(); @@ -386,8 +391,8 @@ error: } void fx_renderer_fini(struct fx_renderer *renderer) { - fx_framebuffer_release(&renderer->main_buffer); fx_framebuffer_release(&renderer->blur_buffer); + fx_framebuffer_release(&renderer->blur_saved_pixels_buffer); fx_framebuffer_release(&renderer->effects_buffer); fx_framebuffer_release(&renderer->effects_buffer_swapped); } @@ -397,23 +402,26 @@ void fx_renderer_begin(struct fx_renderer *renderer, int width, int height) { renderer->viewport_width = width; renderer->viewport_height = height; - // Store the wlr framebuffer - GLint wlr_fb = -1; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &wlr_fb); - if (wlr_fb < 0) { - sway_log(SWAY_ERROR, "Failed to get wlr framebuffer!"); - abort(); - } - renderer->wlr_buffer.fb = wlr_fb; + // Store the wlr FBO + renderer->wlr_buffer.fb = + wlr_gles2_renderer_get_current_fbo(renderer->wlr_output->renderer); + // Get the fx_texture + struct wlr_texture *wlr_texture = wlr_texture_from_buffer( + renderer->wlr_output->renderer, renderer->wlr_output->back_buffer); + renderer->wlr_buffer.texture = fx_texture_from_wlr_texture(wlr_texture); + wlr_texture_destroy(wlr_texture); + // Add the stencil to the wlr fbo + fx_framebuffer_add_stencil_buffer(&renderer->wlr_buffer, width, height); // Create the framebuffers - fx_framebuffer_update(&renderer->main_buffer, width, height); + fx_framebuffer_update(&renderer->blur_saved_pixels_buffer, width, height); fx_framebuffer_update(&renderer->effects_buffer, width, height); fx_framebuffer_update(&renderer->effects_buffer_swapped, width, height); // Add a stencil buffer to the main buffer & bind the main buffer - fx_framebuffer_bind(&renderer->main_buffer); - fx_framebuffer_add_stencil_buffer(&renderer->main_buffer, width, height); + fx_framebuffer_bind(&renderer->wlr_buffer); + + pixman_region32_init(&renderer->blur_padding_region); // refresh projection matrix matrix_projection(renderer->projection, width, height, @@ -423,8 +431,7 @@ void fx_renderer_begin(struct fx_renderer *renderer, int width, int height) { } void fx_renderer_end(struct fx_renderer *renderer) { - // Draw the contents of our buffer into the wlr buffer - fx_framebuffer_bind(&renderer->wlr_buffer); + pixman_region32_fini(&renderer->blur_padding_region); } void fx_renderer_clear(const float color[static 4]) { @@ -876,11 +883,11 @@ struct fx_framebuffer *fx_render_main_buffer_blur(struct fx_renderer *renderer, glDisable(GL_BLEND); glDisable(GL_STENCIL_TEST); - struct fx_framebuffer *current_buffer = &renderer->main_buffer; + struct fx_framebuffer *current_buffer = &renderer->wlr_buffer; // Bind to blur framebuffer fx_framebuffer_bind(&renderer->effects_buffer); - glBindTexture(renderer->main_buffer.texture.target, renderer->main_buffer.texture.id); + glBindTexture(renderer->wlr_buffer.texture.target, renderer->wlr_buffer.texture.id); float gl_matrix[9]; wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); @@ -908,7 +915,7 @@ struct fx_framebuffer *fx_render_main_buffer_blur(struct fx_renderer *renderer, pixman_region32_fini(&temp_damage); // Bind back to the default buffer - fx_framebuffer_bind(&renderer->main_buffer); + fx_framebuffer_bind(&renderer->wlr_buffer); return current_buffer; } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 5d61186f..911bffbb 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -697,36 +697,6 @@ static void damage_surface_iterator(struct sway_output *output, } pixman_region32_translate(&damage, box.x, box.y); - // Extend view/layer damage size - int effect_size = 0; - if (view && view->container->blur_enabled) { - // Don't check for shadow, gets extended in `output_damage_whole_container` - effect_size = config_get_blur_size(); - } else if (wlr_surface_is_layer_surface(surface)) { - struct wlr_layer_surface_v1 *layer = wlr_layer_surface_v1_from_wlr_surface(surface); - struct sway_layer_surface *sway_layer = layer_from_wlr_layer_surface_v1(layer); - int blur_size = sway_layer->has_blur? config_get_blur_size(): 0; - int shadow_sigma = sway_layer->has_shadow? config->shadow_blur_sigma: 0; - effect_size = MAX(blur_size, shadow_sigma); - } - if (effect_size > 0) { - if (pixman_region32_not_empty(&damage)) { - int output_width, output_height; - wlr_output_transformed_resolution(output->wlr_output, &output_width, &output_height); - int32_t damage_width = damage.extents.x2 - damage.extents.x1; - int32_t damage_height = damage.extents.y2 - damage.extents.y1; - if (damage_width > output_width || damage_height > output_height) { - pixman_region32_intersect_rect(&damage, &damage, 0, 0, output_width, output_height); - } else { - wlr_region_expand(&damage, &damage, effect_size); - } - } - box.x -= effect_size; - box.y -= effect_size; - box.width += effect_size * 2; - box.height += effect_size * 2; - } - if (wlr_damage_ring_add(&output->damage_ring, &damage)) { wlr_output_schedule_frame(output->wlr_output); } @@ -983,7 +953,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { // Init FX Renderer struct wlr_egl *egl = wlr_gles2_renderer_get_egl(server->wlr_renderer); - output->renderer = fx_renderer_create(egl); + output->renderer = fx_renderer_create(egl, wlr_output); if (!output->renderer) { sway_log(SWAY_ERROR, "Failed to create fx_renderer"); abort(); diff --git a/sway/desktop/render.c b/sway/desktop/render.c index e6232e0c..4e530116 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -479,7 +479,7 @@ void render_output_blur(struct sway_output *output, pixman_region32_t *damage) { fx_renderer_clear(clear_color); } render_whole_output(renderer, wlr_output, &fake_damage, &buffer->texture); - fx_framebuffer_bind(&renderer->main_buffer); + fx_framebuffer_bind(&renderer->wlr_buffer); pixman_region32_fini(&fake_damage); @@ -1690,13 +1690,6 @@ void output_render(struct sway_output *output, struct timespec *when, return; } - /* we need to track extended damage for blur (as it is expanded in output.c), - before we expand it again later in this function - */ - pixman_region32_t original_damage; - pixman_region32_init(&original_damage); - pixman_region32_copy(&original_damage, damage); - struct sway_container *fullscreen_con = root->fullscreen_global; if (!fullscreen_con) { fullscreen_con = workspace->current.fullscreen; @@ -1714,7 +1707,6 @@ void output_render(struct sway_output *output, struct timespec *when, if (debug.damage == DAMAGE_RERENDER) { pixman_region32_union_rect(damage, damage, 0, 0, output_width, output_height); - pixman_region32_copy(&original_damage, damage); } if (!pixman_region32_not_empty(damage)) { @@ -1723,9 +1715,7 @@ void output_render(struct sway_output *output, struct timespec *when, } if (debug.damage == DAMAGE_HIGHLIGHT) { - fx_framebuffer_bind(&renderer->wlr_buffer); fx_renderer_clear((float[]){1, 1, 0, 1}); - fx_framebuffer_bind(&renderer->main_buffer); } if (server.session_lock.locked) { @@ -1802,21 +1792,66 @@ void output_render(struct sway_output *output, struct timespec *when, #endif } else { bool workspace_has_blur = should_workspace_have_blur(workspace); - if (workspace_has_blur) { - if (config_should_parameters_blur() && renderer->blur_buffer_dirty) { + if (workspace_has_blur && config_should_parameters_blur()) { + // Skip all of the blur artifact prevention if we're damaging the + // whole viewport + if (renderer->blur_buffer_dirty) { // Needs to be extended before clearing - pixman_region32_union_rect(damage, damage, 0, 0, output_width, output_height); - pixman_region32_union_rect(&original_damage, &original_damage, 0, 0, output_width, output_height); - } - - // ensure that the damage isn't expanding past the output's size - int32_t damage_width = damage->extents.x2 - damage->extents.x1; - int32_t damage_height = damage->extents.y2 - damage->extents.y1; - if (damage_width > output_width || damage_height > output_height) { - pixman_region32_intersect_rect(damage, damage, 0, 0, output_width, output_height); - pixman_region32_intersect_rect(&original_damage, &original_damage, 0, 0, output_width, output_height); + pixman_region32_union_rect(damage, damage, + 0, 0, output_width, output_height); } else { - wlr_region_expand(damage, damage, config_get_blur_size()); + // To remove blur edge artifacts we need to copy the surrounding + // content where the blur would display artifacts and draw it + // above the artifacts, i.e resetting the affected areas to look + // like they did in the previous frame (these areas haven't changed + // so we don't have to worry about that). + + // ensure that the damage isn't expanding past the output's size + int32_t damage_width = damage->extents.x2 - damage->extents.x1; + int32_t damage_height = damage->extents.y2 - damage->extents.y1; + if (damage_width > output_width || damage_height > output_height) { + pixman_region32_intersect_rect(damage, damage, + 0, 0, output_width, output_height); + } else { + // Expand the original damage to compensate for surrounding + // blurred views to avoid sharp edges between damage regions + wlr_region_expand(damage, damage, config_get_blur_size()); + } + + pixman_region32_t blur_region; + pixman_region32_init(&blur_region); + pixman_region32_t extended_damage; + pixman_region32_init(&extended_damage); + + // Gather the whole region where blur is drawn (all surfaces on + // the focused workspace) + workspace_get_blur_region(workspace, &blur_region); + + pixman_region32_intersect(&extended_damage, damage, &blur_region); + // Expand the region to compensate for blur artifacts + wlr_region_expand(&extended_damage, &extended_damage, config_get_blur_size()); + // Limit to the monitors viewport + pixman_region32_intersect_rect(&extended_damage, &extended_damage, + 0, 0, output_width, output_height); + + // Make sure that we ONLY capture the padding pixels around the + // blur (around the expanded region) where the artifacts will + // be drawn + pixman_region32_subtract(&renderer->blur_padding_region, + &extended_damage, damage); + // Combine into the surface damage (we need to redraw the padding + // area as well) + pixman_region32_union(damage, damage, &extended_damage); + + // Capture the padding pixels before blur for later use + fx_framebuffer_bind(&renderer->blur_saved_pixels_buffer); + // TODO: Investigate blitting instead + render_whole_output(renderer, wlr_output, + &renderer->blur_padding_region, &renderer->wlr_buffer.texture); + fx_framebuffer_bind(&renderer->wlr_buffer); + + pixman_region32_fini(&blur_region); + pixman_region32_fini(&extended_damage); } } @@ -1883,8 +1918,15 @@ render_overlay: render_drag_icons(output, damage, &root->drag_icons); renderer_end: + // Not needed if we damaged the whole viewport + if (!renderer->blur_buffer_dirty) { + // Render the saved pixels over the blur artifacts + // TODO: Investigate blitting instead + render_whole_output(renderer, wlr_output, &renderer->blur_padding_region, + &renderer->blur_saved_pixels_buffer.texture); + } + fx_renderer_end(output->renderer); - render_whole_output(renderer, wlr_output, &original_damage, &renderer->main_buffer.texture); fx_renderer_scissor(NULL); // Draw the software cursors @@ -1896,8 +1938,7 @@ renderer_end: pixman_region32_init(&frame_damage); enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - wlr_region_transform(&frame_damage, &original_damage, transform, output_width, output_height); - pixman_region32_fini(&original_damage); + wlr_region_transform(&frame_damage, damage, transform, output_width, output_height); if (debug.damage != DAMAGE_DEFAULT) { pixman_region32_union_rect(&frame_damage, &frame_damage, diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 8bc62f3f..eadc2eb5 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -725,6 +725,54 @@ bool should_workspace_have_blur(struct sway_workspace *ws) { return false; } +struct blur_region_data { + struct sway_workspace *ws; + pixman_region32_t *blur_region; +}; + +static void find_blurred_region_iterator(struct sway_container *con, void *data) { + struct sway_view *view = con->view; + if (!view) { + return; + } + + struct blur_region_data *region_data = data; + struct sway_workspace *ws = region_data->ws; + pixman_region32_t *blur_region = region_data->blur_region; + + if (con->blur_enabled && !view->surface->opaque) { + pixman_region32_union_rect(blur_region, blur_region, + floor(con->current.x) - ws->output->lx, + floor(con->current.y) - ws->output->ly, + con->current.width, con->current.height); + } +} + +void workspace_get_blur_region(struct sway_workspace *ws, pixman_region32_t *blur_region) { + if (!workspace_is_visible(ws)) { + return; + } + + // Each toplevel + struct blur_region_data data = { ws, blur_region }; + workspace_for_each_container(ws, find_blurred_region_iterator, &data); + + // Each Layer + struct sway_output *sway_output = ws->output; + size_t len = sizeof(sway_output->layers) / sizeof(sway_output->layers[0]); + for (size_t i = 0; i < len; ++i) { + struct sway_layer_surface *lsurface; + wl_list_for_each(lsurface, &sway_output->layers[i], link) { + if (lsurface->has_blur && !lsurface->layer_surface->surface->opaque + && lsurface->layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) { + struct wlr_box *geo = &lsurface->geo; + pixman_region32_union_rect(blur_region, blur_region, + geo->x, geo->y, geo->width, geo->height); + } + } + } +} + void workspace_for_each_container(struct sway_workspace *ws, void (*f)(struct sway_container *con, void *data), void *data) { // Tiling