Removed the need for our own main FBO, also fixes some damage bugs
This commit is contained in:
parent
cf463ee9a3
commit
657da122ab
7 changed files with 164 additions and 91 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue