Add kawase blur (#120)
Co-authored-by: Erik Reider <erik.reider@protonmail.com> Co-authored-by: Will McKinnon <contact@willmckinnon.com>
This commit is contained in:
parent
7d774f769c
commit
cbfb7af7fd
|
@ -19,6 +19,11 @@ Sway is an incredible window manager, and certainly one of the most well establi
|
|||
- `shadows_on_csd enable|disable` (**Note**: The shadow might not fit some windows)
|
||||
- `shadow_blur_radius <integer value 0 - 100>`
|
||||
- `shadow_color <hex color with alpha> ex, #0000007F`
|
||||
+ Window blur: *ONLY ON SWAYFX-GIT, NOT YET RELEASED*
|
||||
- `blur enable|disable`
|
||||
- `blur_xray enable|disable`
|
||||
- `blur_passes <integer value 0 - 10>`
|
||||
- `blur_radius <integer value 0 - 10>`
|
||||
+ Dim unfocused windows:
|
||||
- `default_dim_inactive <float value 0.0 - 1.0>`
|
||||
- `for_window [CRITERIA_HERE] dim_inactive <float value 0.0 - 1.0>`
|
||||
|
|
|
@ -24,6 +24,12 @@ set $menu dmenu_path | dmenu | xargs swaymsg exec --
|
|||
# window corner radius in px
|
||||
corner_radius 10
|
||||
|
||||
# Window background blur
|
||||
blur off
|
||||
blur_xray off
|
||||
blur_passes 2
|
||||
blur_radius 5
|
||||
|
||||
shadows off
|
||||
shadows_on_csd off
|
||||
shadow_blur_radius 20
|
||||
|
|
|
@ -109,6 +109,10 @@ sway_cmd cmd_bindcode;
|
|||
sway_cmd cmd_bindgesture;
|
||||
sway_cmd cmd_bindswitch;
|
||||
sway_cmd cmd_bindsym;
|
||||
sway_cmd cmd_blur;
|
||||
sway_cmd cmd_blur_passes;
|
||||
sway_cmd cmd_blur_radius;
|
||||
sway_cmd cmd_blur_xray;
|
||||
sway_cmd cmd_border;
|
||||
sway_cmd cmd_client_noop;
|
||||
sway_cmd cmd_client_focused;
|
||||
|
|
|
@ -470,23 +470,33 @@ enum xwayland_mode {
|
|||
XWAYLAND_MODE_IMMEDIATE,
|
||||
};
|
||||
|
||||
struct blur_parameters {
|
||||
int num_passes;
|
||||
int radius;
|
||||
};
|
||||
|
||||
/**
|
||||
* The configuration struct. The result of loading a config file.
|
||||
*/
|
||||
struct sway_config {
|
||||
// SwayFX config options
|
||||
int corner_radius;
|
||||
bool smart_corner_radius;
|
||||
|
||||
float default_dim_inactive;
|
||||
// dim_inactive colors
|
||||
struct {
|
||||
float unfocused[4];
|
||||
float urgent[4];
|
||||
} dim_inactive_colors;
|
||||
|
||||
bool shadow_enabled;
|
||||
bool shadows_on_csd_enabled;
|
||||
int shadow_blur_sigma;
|
||||
float shadow_color[4];
|
||||
|
||||
bool blur_enabled;
|
||||
bool blur_xray;
|
||||
struct blur_parameters blur_params;
|
||||
|
||||
bool titlebar_separator;
|
||||
bool scratchpad_minimize;
|
||||
|
||||
|
|
23
include/sway/desktop/fx_renderer/fx_framebuffer.h
Normal file
23
include/sway/desktop/fx_renderer/fx_framebuffer.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef FX_FRAMEBUFFER_H
|
||||
#define FX_FRAMEBUFFER_H
|
||||
|
||||
#include <GLES2/gl2.h>
|
||||
#include <stdbool.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
|
||||
#include "sway/desktop/fx_renderer/fx_texture.h"
|
||||
|
||||
struct fx_framebuffer {
|
||||
struct fx_texture texture;
|
||||
GLuint fb;
|
||||
};
|
||||
|
||||
void fx_framebuffer_bind(struct fx_framebuffer *buffer, GLsizei width, GLsizei height);
|
||||
|
||||
void fx_framebuffer_create(struct wlr_output *output, struct fx_framebuffer *buffer,
|
||||
bool bind);
|
||||
|
||||
void fx_framebuffer_release(struct fx_framebuffer *buffer);
|
||||
|
||||
|
||||
#endif
|
|
@ -5,6 +5,9 @@
|
|||
#include <GLES2/gl2ext.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "sway/desktop/fx_renderer/fx_framebuffer.h"
|
||||
#include "sway/desktop/fx_renderer/fx_texture.h"
|
||||
|
||||
enum corner_location { ALL, TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, NONE };
|
||||
|
||||
enum fx_tex_shader_source {
|
||||
|
@ -24,8 +27,9 @@ struct decoration_data {
|
|||
float saturation;
|
||||
int corner_radius;
|
||||
float dim;
|
||||
float* dim_color;
|
||||
float *dim_color;
|
||||
bool has_titlebar;
|
||||
bool blur;
|
||||
};
|
||||
|
||||
struct gles2_tex_shader {
|
||||
|
@ -54,13 +58,34 @@ struct rounded_quad_shader {
|
|||
GLint radius;
|
||||
};
|
||||
|
||||
struct blur_shader {
|
||||
GLuint program;
|
||||
GLint proj;
|
||||
GLint tex;
|
||||
GLint pos_attrib;
|
||||
GLint tex_attrib;
|
||||
GLint radius;
|
||||
GLint halfpixel;
|
||||
};
|
||||
|
||||
struct fx_renderer {
|
||||
struct wlr_egl *egl;
|
||||
|
||||
float projection[9];
|
||||
|
||||
struct sway_output *sway_output;
|
||||
|
||||
GLuint stencil_buffer_id;
|
||||
|
||||
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
|
||||
// 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
|
||||
|
||||
bool blur_buffer_dirty;
|
||||
|
||||
struct {
|
||||
bool OES_egl_image_external;
|
||||
} exts;
|
||||
|
@ -83,6 +108,9 @@ struct fx_renderer {
|
|||
struct rounded_quad_shader rounded_tl_quad;
|
||||
struct rounded_quad_shader rounded_tr_quad;
|
||||
|
||||
struct blur_shader blur1;
|
||||
struct blur_shader blur2;
|
||||
|
||||
struct {
|
||||
GLuint program;
|
||||
GLint proj;
|
||||
|
@ -117,19 +145,21 @@ struct fx_renderer {
|
|||
|
||||
struct fx_renderer *fx_renderer_create(struct wlr_egl *egl);
|
||||
|
||||
void fx_renderer_begin(struct fx_renderer *renderer, uint32_t width, uint32_t height);
|
||||
void fx_renderer_fini(struct fx_renderer *renderer);
|
||||
|
||||
void fx_renderer_end();
|
||||
void fx_renderer_begin(struct fx_renderer *renderer, struct sway_output *output);
|
||||
|
||||
void fx_renderer_end(struct fx_renderer *renderer);
|
||||
|
||||
void fx_renderer_clear(const float color[static 4]);
|
||||
|
||||
void fx_renderer_scissor(struct wlr_box *box);
|
||||
|
||||
bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture,
|
||||
bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct fx_texture *fx_texture,
|
||||
const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9],
|
||||
struct decoration_data deco_data);
|
||||
|
||||
bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture,
|
||||
bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct fx_texture *fx_texture,
|
||||
const struct wlr_box *dst_box, const float matrix[static 9], struct decoration_data deco_data);
|
||||
|
||||
void fx_render_rect(struct fx_renderer *renderer, const struct wlr_box *box,
|
||||
|
@ -146,4 +176,8 @@ void fx_render_border_corner(struct fx_renderer *renderer, const struct wlr_box
|
|||
void fx_render_box_shadow(struct fx_renderer *renderer, const struct wlr_box *box,
|
||||
const float color[static 4], const float projection[static 9], int radius, float blur_sigma);
|
||||
|
||||
void fx_render_blur(struct fx_renderer *renderer, struct sway_output *output,
|
||||
const float matrix[static 9], struct fx_framebuffer **buffer,
|
||||
struct blur_shader *shader, const struct wlr_box *box, int blur_radius);
|
||||
|
||||
#endif
|
||||
|
|
18
include/sway/desktop/fx_renderer/fx_texture.h
Normal file
18
include/sway/desktop/fx_renderer/fx_texture.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef FX_TEXTURE_H
|
||||
#define FX_TEXTURE_H
|
||||
|
||||
#include <GLES2/gl2.h>
|
||||
#include <stdbool.h>
|
||||
#include <wlr/render/wlr_texture.h>
|
||||
|
||||
struct fx_texture {
|
||||
GLuint target;
|
||||
GLuint id;
|
||||
bool has_alpha;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
struct fx_texture fx_texture_from_wlr_texture(struct wlr_texture* tex);
|
||||
|
||||
#endif
|
|
@ -24,6 +24,8 @@ struct sway_output {
|
|||
struct sway_server *server;
|
||||
struct wl_list link;
|
||||
|
||||
struct fx_renderer *renderer;
|
||||
|
||||
struct wl_list layers[4]; // sway_layer_surface::link
|
||||
struct wlr_box usable_area;
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@ struct sway_server {
|
|||
// secondary headless backend used for creating virtual outputs on-the-fly
|
||||
struct wlr_backend *headless_backend;
|
||||
struct wlr_renderer *wlr_renderer;
|
||||
struct fx_renderer *renderer;
|
||||
struct wlr_allocator *allocator;
|
||||
|
||||
struct wlr_compositor *compositor;
|
||||
|
|
|
@ -115,6 +115,8 @@ struct sway_container {
|
|||
|
||||
bool shadow_enabled;
|
||||
|
||||
bool blur_enabled;
|
||||
|
||||
float saturation;
|
||||
|
||||
float alpha;
|
||||
|
|
|
@ -49,6 +49,10 @@ static const struct cmd_handler handlers[] = {
|
|||
{ "bindgesture", cmd_bindgesture },
|
||||
{ "bindswitch", cmd_bindswitch },
|
||||
{ "bindsym", cmd_bindsym },
|
||||
{ "blur", cmd_blur },
|
||||
{ "blur_passes", cmd_blur_passes },
|
||||
{ "blur_radius", cmd_blur_radius },
|
||||
{ "blur_xray", cmd_blur_xray },
|
||||
{ "client.background", cmd_client_noop },
|
||||
{ "client.focused", cmd_client_focused },
|
||||
{ "client.focused_inactive", cmd_client_focused_inactive },
|
||||
|
|
31
sway/commands/blur.c
Normal file
31
sway/commands/blur.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include "sway/commands.h"
|
||||
#include "sway/config.h"
|
||||
#include "sway/output.h"
|
||||
#include "util.h"
|
||||
|
||||
struct cmd_results *cmd_blur(int argc, char **argv) {
|
||||
struct cmd_results *error = checkarg(argc, "blur", EXPECTED_AT_LEAST, 1);
|
||||
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
struct sway_container *con = config->handler_context.container;
|
||||
|
||||
bool result = parse_boolean(argv[0], config->blur_enabled);
|
||||
if (con == NULL) {
|
||||
config->blur_enabled = result;
|
||||
} else {
|
||||
con->blur_enabled = result;
|
||||
}
|
||||
|
||||
struct sway_output *output;
|
||||
wl_list_for_each(output, &root->all_outputs, link) {
|
||||
if (output->renderer) {
|
||||
output->renderer->blur_buffer_dirty = true;
|
||||
output_damage_whole(output);
|
||||
}
|
||||
}
|
||||
|
||||
return cmd_results_new(CMD_SUCCESS, NULL);
|
||||
}
|
28
sway/commands/blur_passes.c
Normal file
28
sway/commands/blur_passes.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "sway/commands.h"
|
||||
#include "sway/config.h"
|
||||
#include "sway/output.h"
|
||||
|
||||
struct cmd_results *cmd_blur_passes(int argc, char **argv) {
|
||||
struct cmd_results *error = NULL;
|
||||
if ((error = checkarg(argc, "blur_passes", EXPECTED_EQUAL_TO, 1))) {
|
||||
return error;
|
||||
}
|
||||
|
||||
char *inv;
|
||||
int value = strtol(argv[0], &inv, 10);
|
||||
if (*inv != '\0' || value < 0 || value > 10) {
|
||||
return cmd_results_new(CMD_FAILURE, "Invalid size specified");
|
||||
}
|
||||
|
||||
config->blur_params.num_passes = value;
|
||||
|
||||
struct sway_output *output;
|
||||
wl_list_for_each(output, &root->all_outputs, link) {
|
||||
if (output->renderer) {
|
||||
output->renderer->blur_buffer_dirty = true;
|
||||
output_damage_whole(output);
|
||||
}
|
||||
}
|
||||
|
||||
return cmd_results_new(CMD_SUCCESS, NULL);
|
||||
}
|
28
sway/commands/blur_radius.c
Normal file
28
sway/commands/blur_radius.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "sway/commands.h"
|
||||
#include "sway/config.h"
|
||||
#include "sway/output.h"
|
||||
|
||||
struct cmd_results *cmd_blur_radius(int argc, char **argv) {
|
||||
struct cmd_results *error = NULL;
|
||||
if ((error = checkarg(argc, "blur_radius", EXPECTED_AT_LEAST, 1))) {
|
||||
return error;
|
||||
}
|
||||
|
||||
char *inv;
|
||||
int value = strtol(argv[0], &inv, 10);
|
||||
if (*inv != '\0' || value < 0 || value > 10) {
|
||||
return cmd_results_new(CMD_FAILURE, "Invalid size specified");
|
||||
}
|
||||
|
||||
config->blur_params.radius = value;
|
||||
|
||||
struct sway_output *output;
|
||||
wl_list_for_each(output, &root->all_outputs, link) {
|
||||
if (output->renderer) {
|
||||
output->renderer->blur_buffer_dirty = true;
|
||||
output_damage_whole(output);
|
||||
}
|
||||
}
|
||||
|
||||
return cmd_results_new(CMD_SUCCESS, NULL);
|
||||
}
|
25
sway/commands/blur_xray.c
Normal file
25
sway/commands/blur_xray.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "sway/commands.h"
|
||||
#include "sway/config.h"
|
||||
#include "sway/output.h"
|
||||
#include "util.h"
|
||||
|
||||
struct cmd_results *cmd_blur_xray(int argc, char **argv) {
|
||||
struct cmd_results *error = checkarg(argc, "blur_xray", EXPECTED_AT_LEAST, 1);
|
||||
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
bool result = parse_boolean(argv[0], config->blur_xray);
|
||||
config->blur_xray = result;
|
||||
|
||||
struct sway_output *output;
|
||||
wl_list_for_each(output, &root->all_outputs, link) {
|
||||
if (output->renderer) {
|
||||
output->renderer->blur_buffer_dirty = true;
|
||||
output_damage_whole(output);
|
||||
}
|
||||
}
|
||||
|
||||
return cmd_results_new(CMD_SUCCESS, NULL);
|
||||
}
|
|
@ -336,13 +336,21 @@ static void config_defaults(struct sway_config *config) {
|
|||
// SwayFX defaults
|
||||
config->corner_radius = 0;
|
||||
config->smart_corner_radius = true;
|
||||
|
||||
config->default_dim_inactive = 0.0f;
|
||||
color_to_rgba(config->dim_inactive_colors.unfocused, 0x000000FF);
|
||||
color_to_rgba(config->dim_inactive_colors.urgent, 0x900000FF);
|
||||
|
||||
config->shadow_enabled = false;
|
||||
config->shadows_on_csd_enabled = false;
|
||||
config->shadow_blur_sigma = 20.0f;
|
||||
color_to_rgba(config->shadow_color, 0x0000007F);
|
||||
|
||||
config->blur_enabled = false;
|
||||
config->blur_xray = false;
|
||||
config->blur_params.num_passes = 2;
|
||||
config->blur_params.radius = 5;
|
||||
|
||||
config->titlebar_separator = true;
|
||||
config->scratchpad_minimize = true;
|
||||
|
||||
|
|
70
sway/desktop/fx_renderer/fx_framebuffer.c
Normal file
70
sway/desktop/fx_renderer/fx_framebuffer.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
#include "log.h"
|
||||
#include "sway/desktop/fx_renderer/fx_framebuffer.h"
|
||||
|
||||
void fx_framebuffer_bind(struct fx_framebuffer *buffer, GLsizei width, GLsizei height) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, buffer->fb);
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
void fx_framebuffer_create(struct wlr_output *output, struct fx_framebuffer *buffer, bool bind) {
|
||||
bool firstAlloc = false;
|
||||
|
||||
// Create a new framebuffer
|
||||
if (buffer->fb == (uint32_t) -1) {
|
||||
glGenFramebuffers(1, &buffer->fb);
|
||||
firstAlloc = true;
|
||||
}
|
||||
|
||||
if (buffer->texture.id == 0) {
|
||||
firstAlloc = true;
|
||||
glGenTextures(1, &buffer->texture.id);
|
||||
glBindTexture(GL_TEXTURE_2D, buffer->texture.id);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
}
|
||||
|
||||
int width, height;
|
||||
wlr_output_transformed_resolution(output, &width, &height);
|
||||
|
||||
if (firstAlloc || buffer->texture.width != width || buffer->texture.height != height) {
|
||||
glBindTexture(GL_TEXTURE_2D, buffer->texture.id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, buffer->fb);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
buffer->texture.id, 0);
|
||||
buffer->texture.target = GL_TEXTURE_2D;
|
||||
buffer->texture.has_alpha = false;
|
||||
buffer->texture.width = width;
|
||||
buffer->texture.height = height;
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
sway_log(SWAY_ERROR, "Framebuffer incomplete, couldn't create! (FB status: %i)", status);
|
||||
return;
|
||||
}
|
||||
sway_log(SWAY_DEBUG, "Framebuffer created, status %i", status);
|
||||
}
|
||||
|
||||
// Bind the default framebuffer
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
if (bind) {
|
||||
fx_framebuffer_bind(buffer, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void fx_framebuffer_release(struct fx_framebuffer *buffer) {
|
||||
if (buffer->fb != (uint32_t) -1 && buffer->fb) {
|
||||
glDeleteFramebuffers(1, &buffer->fb);
|
||||
}
|
||||
buffer->fb= -1;
|
||||
|
||||
if (buffer->texture.id) {
|
||||
glDeleteTextures(1, &buffer->texture.id);
|
||||
}
|
||||
buffer->texture.id = 0;
|
||||
buffer->texture.width = -1;
|
||||
buffer->texture.height = -1;
|
||||
}
|
|
@ -19,6 +19,8 @@
|
|||
#include "sway/server.h"
|
||||
|
||||
// shaders
|
||||
#include "blur1_frag_src.h"
|
||||
#include "blur2_frag_src.h"
|
||||
#include "box_shadow_frag_src.h"
|
||||
#include "common_vert_src.h"
|
||||
#include "corner_frag_src.h"
|
||||
|
@ -33,6 +35,33 @@ static const GLfloat verts[] = {
|
|||
0, 1, // bottom left
|
||||
};
|
||||
|
||||
static void create_stencil_buffer(struct wlr_output* output, GLuint *buffer_id) {
|
||||
if (*buffer_id != (uint32_t) -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
int width, height;
|
||||
wlr_output_transformed_resolution(output, &width, &height);
|
||||
|
||||
glGenRenderbuffers(1, buffer_id);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, *buffer_id);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, *buffer_id);
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
sway_log(SWAY_ERROR, "Stencilbuffer incomplete, couldn't create! (FB status: %i)", status);
|
||||
return;
|
||||
}
|
||||
sway_log(SWAY_DEBUG, "Stencilbuffer created, status %i", status);
|
||||
}
|
||||
|
||||
static void release_stencil_buffer(GLuint *buffer_id) {
|
||||
if (*buffer_id != (uint32_t)-1 && buffer_id) {
|
||||
glDeleteRenderbuffers(1, buffer_id);
|
||||
}
|
||||
*buffer_id = -1;
|
||||
}
|
||||
|
||||
static GLuint compile_shader(GLuint type, const GLchar *src) {
|
||||
GLuint shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &src, NULL);
|
||||
|
@ -179,6 +208,15 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) {
|
|||
// TODO: needed?
|
||||
renderer->egl = egl;
|
||||
|
||||
renderer->main_buffer.fb = -1;
|
||||
|
||||
renderer->blur_buffer.fb = -1;
|
||||
renderer->effects_buffer.fb = -1;
|
||||
renderer->effects_buffer_swapped.fb = -1;
|
||||
renderer->stencil_buffer_id = -1;
|
||||
|
||||
renderer->blur_buffer_dirty = true;
|
||||
|
||||
// get extensions
|
||||
const char *exts_str = (const char *)glGetString(GL_EXTENSIONS);
|
||||
if (exts_str == NULL) {
|
||||
|
@ -258,6 +296,32 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) {
|
|||
renderer->shaders.box_shadow.blur_sigma = glGetUniformLocation(prog, "blur_sigma");
|
||||
renderer->shaders.box_shadow.corner_radius = glGetUniformLocation(prog, "corner_radius");
|
||||
|
||||
// Blur 1
|
||||
prog = link_program(blur1_frag_src);
|
||||
renderer->shaders.blur1.program = prog;
|
||||
if (!renderer->shaders.blur1.program) {
|
||||
goto error;
|
||||
}
|
||||
renderer->shaders.blur1.proj = glGetUniformLocation(prog, "proj");
|
||||
renderer->shaders.blur1.tex = glGetUniformLocation(prog, "tex");
|
||||
renderer->shaders.blur1.pos_attrib = glGetAttribLocation(prog, "pos");
|
||||
renderer->shaders.blur1.tex_attrib = glGetAttribLocation(prog, "texcoord");
|
||||
renderer->shaders.blur1.radius = glGetUniformLocation(prog, "radius");
|
||||
renderer->shaders.blur1.halfpixel = glGetUniformLocation(prog, "halfpixel");
|
||||
|
||||
// Blur 2
|
||||
prog = link_program(blur2_frag_src);
|
||||
renderer->shaders.blur2.program = prog;
|
||||
if (!renderer->shaders.blur2.program) {
|
||||
goto error;
|
||||
}
|
||||
renderer->shaders.blur2.proj = glGetUniformLocation(prog, "proj");
|
||||
renderer->shaders.blur2.tex = glGetUniformLocation(prog, "tex");
|
||||
renderer->shaders.blur2.pos_attrib = glGetAttribLocation(prog, "pos");
|
||||
renderer->shaders.blur2.tex_attrib = glGetAttribLocation(prog, "texcoord");
|
||||
renderer->shaders.blur2.radius = glGetUniformLocation(prog, "radius");
|
||||
renderer->shaders.blur2.halfpixel = glGetUniformLocation(prog, "halfpixel");
|
||||
|
||||
// fragment shaders
|
||||
if (!link_tex_program(renderer, &renderer->shaders.tex_rgba,
|
||||
SHADER_SOURCE_TEXTURE_RGBA)) {
|
||||
|
@ -289,6 +353,8 @@ error:
|
|||
glDeleteProgram(renderer->shaders.rounded_tr_quad.program);
|
||||
glDeleteProgram(renderer->shaders.corner.program);
|
||||
glDeleteProgram(renderer->shaders.box_shadow.program);
|
||||
glDeleteProgram(renderer->shaders.blur1.program);
|
||||
glDeleteProgram(renderer->shaders.blur2.program);
|
||||
glDeleteProgram(renderer->shaders.tex_rgba.program);
|
||||
glDeleteProgram(renderer->shaders.tex_rgbx.program);
|
||||
glDeleteProgram(renderer->shaders.tex_ext.program);
|
||||
|
@ -305,27 +371,53 @@ error:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void fx_renderer_begin(struct fx_renderer *renderer, uint32_t width, uint32_t height) {
|
||||
// Create and render the stencil buffer
|
||||
if (renderer->stencil_buffer_id == 0) {
|
||||
glGenRenderbuffers(1, &renderer->stencil_buffer_id);
|
||||
}
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, renderer->stencil_buffer_id);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
|
||||
GL_RENDERBUFFER, renderer->stencil_buffer_id);
|
||||
void fx_renderer_fini(struct fx_renderer *renderer) {
|
||||
fx_framebuffer_release(&renderer->main_buffer);
|
||||
fx_framebuffer_release(&renderer->blur_buffer);
|
||||
fx_framebuffer_release(&renderer->effects_buffer);
|
||||
fx_framebuffer_release(&renderer->effects_buffer_swapped);
|
||||
release_stencil_buffer(&renderer->stencil_buffer_id);
|
||||
}
|
||||
|
||||
glViewport(0, 0, width, height);
|
||||
void fx_renderer_begin(struct fx_renderer *renderer, struct sway_output *sway_output) {
|
||||
struct wlr_output *output = sway_output->wlr_output;
|
||||
|
||||
int width, height;
|
||||
wlr_output_transformed_resolution(output, &width, &height);
|
||||
|
||||
renderer->sway_output = sway_output;
|
||||
// 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;
|
||||
|
||||
// Create the main framebuffer
|
||||
fx_framebuffer_create(output, &renderer->main_buffer, true);
|
||||
// Create the stencil buffer and attach it to our main_buffer
|
||||
create_stencil_buffer(output, &renderer->stencil_buffer_id);
|
||||
|
||||
// Create a new blur/effects framebuffers
|
||||
fx_framebuffer_create(output, &renderer->effects_buffer, false);
|
||||
fx_framebuffer_create(output, &renderer->effects_buffer_swapped, false);
|
||||
|
||||
// refresh projection matrix
|
||||
matrix_projection(renderer->projection, width, height,
|
||||
WL_OUTPUT_TRANSFORM_FLIPPED_180);
|
||||
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Bind to our main framebuffer
|
||||
fx_framebuffer_bind(&renderer->main_buffer, width, height);
|
||||
}
|
||||
|
||||
void fx_renderer_end() {
|
||||
// TODO
|
||||
void fx_renderer_end(struct fx_renderer *renderer) {
|
||||
// Release the main buffer
|
||||
fx_framebuffer_release(&renderer->main_buffer);
|
||||
release_stencil_buffer(&renderer->stencil_buffer_id);
|
||||
}
|
||||
|
||||
void fx_renderer_clear(const float color[static 4]) {
|
||||
|
@ -343,18 +435,15 @@ void fx_renderer_scissor(struct wlr_box *box) {
|
|||
}
|
||||
}
|
||||
|
||||
bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture,
|
||||
bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct fx_texture *fx_texture,
|
||||
const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9],
|
||||
struct decoration_data deco_data) {
|
||||
assert(wlr_texture_is_gles2(wlr_texture));
|
||||
struct wlr_gles2_texture_attribs texture_attrs;
|
||||
wlr_gles2_texture_get_attribs(wlr_texture, &texture_attrs);
|
||||
|
||||
struct gles2_tex_shader *shader = NULL;
|
||||
|
||||
switch (texture_attrs.target) {
|
||||
switch (fx_texture->target) {
|
||||
case GL_TEXTURE_2D:
|
||||
if (texture_attrs.has_alpha) {
|
||||
if (fx_texture->has_alpha) {
|
||||
shader = &renderer->shaders.tex_rgba;
|
||||
} else {
|
||||
shader = &renderer->shaders.tex_rgbx;
|
||||
|
@ -382,16 +471,18 @@ bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_t
|
|||
wlr_matrix_transpose(gl_matrix, gl_matrix);
|
||||
|
||||
// if there's no opacity or rounded corners we don't need to blend
|
||||
if (!texture_attrs.has_alpha && deco_data.alpha == 1.0 && !deco_data.corner_radius) {
|
||||
if (!fx_texture->has_alpha && deco_data.alpha == 1.0 && !deco_data.corner_radius) {
|
||||
glDisable(GL_BLEND);
|
||||
} else {
|
||||
glEnable(GL_BLEND);
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(texture_attrs.target, texture_attrs.tex);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glTexParameteri(texture_attrs.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(fx_texture->target, fx_texture->id);
|
||||
|
||||
glTexParameteri(fx_texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
glUseProgram(shader->program);
|
||||
|
||||
|
@ -408,10 +499,10 @@ bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_t
|
|||
glUniform1f(shader->saturation, deco_data.saturation);
|
||||
glUniform1f(shader->radius, deco_data.corner_radius);
|
||||
|
||||
const GLfloat x1 = src_box->x / wlr_texture->width;
|
||||
const GLfloat y1 = src_box->y / wlr_texture->height;
|
||||
const GLfloat x2 = (src_box->x + src_box->width) / wlr_texture->width;
|
||||
const GLfloat y2 = (src_box->y + src_box->height) / wlr_texture->height;
|
||||
const GLfloat x1 = src_box->x / fx_texture->width;
|
||||
const GLfloat y1 = src_box->y / fx_texture->height;
|
||||
const GLfloat x2 = (src_box->x + src_box->width) / fx_texture->width;
|
||||
const GLfloat y2 = (src_box->y + src_box->height) / fx_texture->height;
|
||||
const GLfloat texcoord[] = {
|
||||
x2, y1, // top right
|
||||
x1, y1, // top left
|
||||
|
@ -430,21 +521,21 @@ bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_t
|
|||
glDisableVertexAttribArray(shader->pos_attrib);
|
||||
glDisableVertexAttribArray(shader->tex_attrib);
|
||||
|
||||
glBindTexture(texture_attrs.target, 0);
|
||||
glBindTexture(fx_texture->target, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture,
|
||||
bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct fx_texture *texture,
|
||||
const struct wlr_box *dst_box, const float matrix[static 9],
|
||||
struct decoration_data deco_data) {
|
||||
struct wlr_fbox src_box = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = wlr_texture->width,
|
||||
.height = wlr_texture->height,
|
||||
.width = texture->width,
|
||||
.height = texture->height,
|
||||
};
|
||||
return fx_render_subtexture_with_matrix(renderer, wlr_texture, &src_box,
|
||||
return fx_render_subtexture_with_matrix(renderer, texture, &src_box,
|
||||
dst_box, matrix, deco_data);
|
||||
}
|
||||
|
||||
|
@ -669,3 +760,46 @@ void fx_render_box_shadow(struct fx_renderer *renderer, const struct wlr_box *bo
|
|||
glClear(GL_STENCIL_BUFFER_BIT);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
void fx_render_blur(struct fx_renderer *renderer, struct sway_output *output,
|
||||
const float matrix[static 9], struct fx_framebuffer **buffer,
|
||||
struct blur_shader *shader, const struct wlr_box *box, int blur_radius) {
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glBindTexture((*buffer)->texture.target, (*buffer)->texture.id);
|
||||
|
||||
glTexParameteri((*buffer)->texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
glUseProgram(shader->program);
|
||||
|
||||
// OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set
|
||||
// to GL_FALSE
|
||||
float gl_matrix[9];
|
||||
wlr_matrix_transpose(gl_matrix, matrix);
|
||||
glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix);
|
||||
|
||||
glUniform1i(shader->tex, 0);
|
||||
glUniform1f(shader->radius, blur_radius);
|
||||
|
||||
int width, height;
|
||||
wlr_output_transformed_resolution(output->wlr_output, &width, &height);
|
||||
if (shader == &renderer->shaders.blur1) {
|
||||
glUniform2f(shader->halfpixel, 0.5f / (width / 2.0f), 0.5f / (height / 2.0f));
|
||||
} else {
|
||||
glUniform2f(shader->halfpixel, 0.5f / (width * 2.0f), 0.5f / (height * 2.0f));
|
||||
}
|
||||
|
||||
glVertexAttribPointer(shader->pos_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts);
|
||||
glVertexAttribPointer(shader->tex_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts);
|
||||
|
||||
glEnableVertexAttribArray(shader->pos_attrib);
|
||||
glEnableVertexAttribArray(shader->tex_attrib);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
glDisableVertexAttribArray(shader->pos_attrib);
|
||||
glDisableVertexAttribArray(shader->tex_attrib);
|
||||
}
|
||||
|
|
19
sway/desktop/fx_renderer/fx_texture.c
Normal file
19
sway/desktop/fx_renderer/fx_texture.c
Normal file
|
@ -0,0 +1,19 @@
|
|||
#include <assert.h>
|
||||
#include <wlr/render/gles2.h>
|
||||
|
||||
#include "sway/desktop/fx_renderer/fx_texture.h"
|
||||
|
||||
struct fx_texture fx_texture_from_wlr_texture(struct wlr_texture* texture) {
|
||||
assert(wlr_texture_is_gles2(texture));
|
||||
|
||||
struct wlr_gles2_texture_attribs texture_attrs;
|
||||
wlr_gles2_texture_get_attribs(texture, &texture_attrs);
|
||||
|
||||
return (struct fx_texture) {
|
||||
.target = texture_attrs.target,
|
||||
.id = texture_attrs.tex,
|
||||
.has_alpha = texture_attrs.has_alpha,
|
||||
.width = texture->width,
|
||||
.height = texture->height,
|
||||
};
|
||||
}
|
18
sway/desktop/fx_renderer/shaders/blur1.frag
Normal file
18
sway/desktop/fx_renderer/shaders/blur1.frag
Normal file
|
@ -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;
|
||||
}
|
22
sway/desktop/fx_renderer/shaders/blur2.frag
Normal file
22
sway/desktop/fx_renderer/shaders/blur2.frag
Normal file
|
@ -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;
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
embed = find_program('./embed.sh', native: true)
|
||||
|
||||
shaders = [
|
||||
'blur1.frag',
|
||||
'blur2.frag',
|
||||
'box_shadow.frag',
|
||||
'common.vert',
|
||||
'corner.frag',
|
||||
|
|
|
@ -290,6 +290,12 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) {
|
|||
struct sway_output *output = wlr_output->data;
|
||||
struct wlr_box old_extent = layer->extent;
|
||||
|
||||
// Rerender the static blur on change
|
||||
if (layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND
|
||||
|| layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) {
|
||||
output->renderer->blur_buffer_dirty = true;
|
||||
}
|
||||
|
||||
bool layer_changed = false;
|
||||
if (layer_surface->current.committed != 0
|
||||
|| layer->mapped != layer_surface->mapped) {
|
||||
|
@ -366,6 +372,13 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
|
|||
struct wlr_output *wlr_output = sway_layer->layer_surface->output;
|
||||
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
|
||||
struct sway_output *output = wlr_output->data;
|
||||
|
||||
// Rerender the static blur
|
||||
if (sway_layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND
|
||||
|| sway_layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) {
|
||||
output->renderer->blur_buffer_dirty = true;
|
||||
}
|
||||
|
||||
arrange_layers(output);
|
||||
transaction_commit_dirty();
|
||||
wl_list_remove(&sway_layer->output_destroy.link);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend/drm.h>
|
||||
#include <wlr/backend/headless.h>
|
||||
#include <wlr/render/gles2.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_buffer.h>
|
||||
#include <wlr/types/wlr_drm_lease_v1.h>
|
||||
|
@ -745,12 +746,11 @@ static void damage_child_views_iterator(struct sway_container *con,
|
|||
void output_damage_whole_container(struct sway_output *output,
|
||||
struct sway_container *con) {
|
||||
// Pad the box by 1px, because the width is a double and might be a fraction
|
||||
int shadow_sigma = con->shadow_enabled ? config->shadow_blur_sigma : 0;
|
||||
struct wlr_box box = {
|
||||
.x = con->current.x - output->lx - 1 - shadow_sigma,
|
||||
.y = con->current.y - output->ly - 1 - shadow_sigma,
|
||||
.width = con->current.width + 2 + shadow_sigma * 2,
|
||||
.height = con->current.height + 2 + shadow_sigma * 2,
|
||||
.x = con->current.x - output->lx - 1,
|
||||
.y = con->current.y - output->ly - 1,
|
||||
.width = con->current.width + 2,
|
||||
.height = con->current.height + 2,
|
||||
};
|
||||
scale_box(&box, output->wlr_output->scale);
|
||||
if (wlr_damage_ring_add_box(&output->damage_ring, &box)) {
|
||||
|
@ -798,6 +798,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
|
|||
output_disable(output);
|
||||
}
|
||||
|
||||
fx_renderer_fini(output->renderer);
|
||||
|
||||
output_begin_destroy(output);
|
||||
|
||||
wl_list_remove(&output->link);
|
||||
|
@ -939,6 +941,14 @@ void handle_new_output(struct wl_listener *listener, void *data) {
|
|||
output->server = server;
|
||||
wlr_damage_ring_init(&output->damage_ring);
|
||||
|
||||
// Init FX Renderer
|
||||
struct wlr_egl *egl = wlr_gles2_renderer_get_egl(server->wlr_renderer);
|
||||
output->renderer = fx_renderer_create(egl);
|
||||
if (!output->renderer) {
|
||||
sway_log(SWAY_ERROR, "Failed to create fx_renderer");
|
||||
abort();
|
||||
}
|
||||
|
||||
wl_signal_add(&wlr_output->events.destroy, &output->destroy);
|
||||
output->destroy.notify = handle_destroy;
|
||||
wl_signal_add(&wlr_output->events.commit, &output->commit);
|
||||
|
@ -963,6 +973,10 @@ void handle_new_output(struct wl_listener *listener, void *data) {
|
|||
|
||||
transaction_commit_dirty();
|
||||
|
||||
// From sway upstream (fixes damage_ring bounds being INT_MAX)
|
||||
int width, height;
|
||||
wlr_output_transformed_resolution(output->wlr_output, &width, &height);
|
||||
wlr_damage_ring_set_bounds(&output->damage_ring, width, height);
|
||||
update_output_manager_config(server);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "sway/config.h"
|
||||
#include "sway/desktop/fx_renderer/fx_framebuffer.h"
|
||||
#include "sway/desktop/fx_renderer/fx_renderer.h"
|
||||
#include "sway/input/input-manager.h"
|
||||
#include "sway/input/seat.h"
|
||||
|
@ -37,6 +38,13 @@ struct render_data {
|
|||
struct decoration_data deco_data;
|
||||
};
|
||||
|
||||
struct workspace_effect_info {
|
||||
bool container_wants_blur;
|
||||
bool container_wants_shadow;
|
||||
bool should_render_optimized_blur;
|
||||
int expanded_size;
|
||||
};
|
||||
|
||||
struct decoration_data get_undecorated_decoration_data() {
|
||||
return (struct decoration_data) {
|
||||
.alpha = 1.0f,
|
||||
|
@ -45,9 +53,18 @@ struct decoration_data get_undecorated_decoration_data() {
|
|||
.corner_radius = 0,
|
||||
.saturation = 1.0f,
|
||||
.has_titlebar = false,
|
||||
.blur = false,
|
||||
};
|
||||
}
|
||||
|
||||
int get_blur_size() {
|
||||
return pow(2, config->blur_params.num_passes) * config->blur_params.radius;
|
||||
}
|
||||
|
||||
bool should_parameters_blur() {
|
||||
return config->blur_params.radius > 0 && config->blur_params.num_passes > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply scale to a width or height.
|
||||
*
|
||||
|
@ -67,7 +84,7 @@ static int scale_length(int length, int offset, float scale) {
|
|||
static void scissor_output(struct wlr_output *wlr_output,
|
||||
pixman_box32_t *rect) {
|
||||
struct sway_output *output = wlr_output->data;
|
||||
struct fx_renderer *renderer = output->server->renderer;
|
||||
struct fx_renderer *renderer = output->renderer;
|
||||
assert(renderer);
|
||||
|
||||
struct wlr_box box = {
|
||||
|
@ -80,30 +97,22 @@ static void scissor_output(struct wlr_output *wlr_output,
|
|||
int ow, oh;
|
||||
wlr_output_transformed_resolution(wlr_output, &ow, &oh);
|
||||
|
||||
enum wl_output_transform transform =
|
||||
wlr_output_transform_invert(wlr_output->transform);
|
||||
enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform);
|
||||
wlr_box_transform(&box, &box, transform, ow, oh);
|
||||
|
||||
fx_renderer_scissor(&box);
|
||||
}
|
||||
|
||||
static void set_scale_filter(struct wlr_output *wlr_output,
|
||||
struct wlr_texture *texture, enum scale_filter_mode scale_filter) {
|
||||
if (!wlr_texture_is_gles2(texture)) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_gles2_texture_attribs attribs;
|
||||
wlr_gles2_texture_get_attribs(texture, &attribs);
|
||||
|
||||
glBindTexture(attribs.target, attribs.tex);
|
||||
struct fx_texture *texture, enum scale_filter_mode scale_filter) {
|
||||
glBindTexture(texture->target, texture->id);
|
||||
|
||||
switch (scale_filter) {
|
||||
case SCALE_FILTER_LINEAR:
|
||||
glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
break;
|
||||
case SCALE_FILTER_NEAREST:
|
||||
glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
break;
|
||||
case SCALE_FILTER_DEFAULT:
|
||||
case SCALE_FILTER_SMART:
|
||||
|
@ -120,12 +129,19 @@ pixman_region32_t create_damage(const struct wlr_box damage_box, pixman_region32
|
|||
return damage;
|
||||
}
|
||||
|
||||
struct wlr_box get_monitor_box(struct wlr_output *output) {
|
||||
int width, height;
|
||||
wlr_output_transformed_resolution(output, &width, &height);
|
||||
struct wlr_box monitor_box = { 0, 0, width, height };
|
||||
return monitor_box;
|
||||
}
|
||||
|
||||
static void render_texture(struct wlr_output *wlr_output,
|
||||
pixman_region32_t *output_damage, struct wlr_texture *texture,
|
||||
pixman_region32_t *output_damage, struct fx_texture *texture,
|
||||
const struct wlr_fbox *src_box, const struct wlr_box *dst_box,
|
||||
const float matrix[static 9], struct decoration_data deco_data) {
|
||||
struct sway_output *output = wlr_output->data;
|
||||
struct fx_renderer *renderer = output->server->renderer;
|
||||
struct fx_renderer *renderer = output->renderer;
|
||||
|
||||
pixman_region32_t damage = create_damage(*dst_box, output_damage);
|
||||
bool damaged = pixman_region32_not_empty(&damage);
|
||||
|
@ -150,6 +166,170 @@ damage_finish:
|
|||
pixman_region32_fini(&damage);
|
||||
}
|
||||
|
||||
/* Renders the blur for each damaged rect and swaps the buffer */
|
||||
void render_blur_segments(struct fx_renderer *renderer, struct sway_output *output,
|
||||
const float matrix[static 9], pixman_region32_t* damage,
|
||||
struct fx_framebuffer **buffer, struct blur_shader* shader,
|
||||
const struct wlr_box *box, int blur_radius) {
|
||||
int width, height;
|
||||
wlr_output_transformed_resolution(output->wlr_output, &width, &height);
|
||||
|
||||
if (*buffer == &renderer->effects_buffer) {
|
||||
fx_framebuffer_bind(&renderer->effects_buffer_swapped, width, height);
|
||||
} else {
|
||||
fx_framebuffer_bind(&renderer->effects_buffer, width, height);
|
||||
}
|
||||
|
||||
if (pixman_region32_not_empty(damage)) {
|
||||
int nrects;
|
||||
pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
|
||||
for (int i = 0; i < nrects; ++i) {
|
||||
const pixman_box32_t box = rects[i];
|
||||
struct wlr_box new_box = { box.x1, box.y1, box.x2 - box.x1, box.y2 - box.y1 };
|
||||
fx_renderer_scissor(&new_box);
|
||||
fx_render_blur(renderer, output, matrix, buffer, shader, &new_box, blur_radius);
|
||||
}
|
||||
}
|
||||
|
||||
if (*buffer != &renderer->effects_buffer) {
|
||||
*buffer = &renderer->effects_buffer;
|
||||
} else {
|
||||
*buffer = &renderer->effects_buffer_swapped;
|
||||
}
|
||||
}
|
||||
|
||||
/** Blurs the main_buffer content and returns the blurred framebuffer */
|
||||
struct fx_framebuffer *get_main_buffer_blur(struct fx_renderer *renderer, struct sway_output *output,
|
||||
pixman_region32_t *original_damage, const float box_matrix[static 9], const struct wlr_box *box) {
|
||||
struct wlr_box monitor_box = get_monitor_box(output->wlr_output);
|
||||
|
||||
const enum wl_output_transform transform = wlr_output_transform_invert(output->wlr_output->transform);
|
||||
float matrix[9];
|
||||
wlr_matrix_project_box(matrix, &monitor_box, transform, 0, output->wlr_output->transform_matrix);
|
||||
|
||||
float gl_matrix[9];
|
||||
wlr_matrix_multiply(gl_matrix, renderer->projection, matrix);
|
||||
|
||||
pixman_region32_t damage;
|
||||
pixman_region32_init(&damage);
|
||||
pixman_region32_copy(&damage, original_damage);
|
||||
wlr_region_transform(&damage, &damage, wlr_output_transform_invert(output->wlr_output->transform),
|
||||
monitor_box.width, monitor_box.height);
|
||||
wlr_region_expand(&damage, &damage, get_blur_size());
|
||||
|
||||
// Initially blur main_buffer content into the effects_buffers
|
||||
struct fx_framebuffer *current_buffer = &renderer->main_buffer;
|
||||
|
||||
// Bind to blur framebuffer
|
||||
fx_framebuffer_bind(&renderer->effects_buffer, monitor_box.width, monitor_box.height);
|
||||
glBindTexture(renderer->main_buffer.texture.target, renderer->main_buffer.texture.id);
|
||||
|
||||
// damage region will be scaled, make a temp
|
||||
pixman_region32_t tempDamage;
|
||||
pixman_region32_init(&tempDamage);
|
||||
// When DOWNscaling, we make the region twice as small because it's the TARGET
|
||||
wlr_region_scale(&tempDamage, &damage, 0.5f);
|
||||
|
||||
int blur_radius = config->blur_params.radius;
|
||||
int blur_passes = config->blur_params.num_passes;
|
||||
|
||||
// First pass
|
||||
render_blur_segments(renderer, output, gl_matrix, &tempDamage, ¤t_buffer,
|
||||
&renderer->shaders.blur1, box, blur_radius);
|
||||
|
||||
// Downscale
|
||||
for (int i = 1; i < blur_passes; ++i) {
|
||||
wlr_region_scale(&tempDamage, &damage, 1.0f / (1 << (i + 1)));
|
||||
render_blur_segments(renderer, output, gl_matrix, &tempDamage, ¤t_buffer,
|
||||
&renderer->shaders.blur1, box, blur_radius);
|
||||
}
|
||||
|
||||
// Upscale
|
||||
for (int i = blur_passes - 1; i >= 0; --i) {
|
||||
// when upsampling we make the region twice as big
|
||||
wlr_region_scale(&tempDamage, &damage, 1.0f / (1 << i));
|
||||
render_blur_segments(renderer, output, gl_matrix, &tempDamage, ¤t_buffer,
|
||||
&renderer->shaders.blur2, box, blur_radius);
|
||||
}
|
||||
|
||||
pixman_region32_fini(&tempDamage);
|
||||
pixman_region32_fini(&damage);
|
||||
|
||||
// Bind back to the default buffer
|
||||
fx_framebuffer_bind(&renderer->main_buffer, monitor_box.width, monitor_box.height);
|
||||
|
||||
return current_buffer;
|
||||
}
|
||||
|
||||
void render_blur(bool optimized, struct sway_output *output,
|
||||
pixman_region32_t *output_damage, const struct wlr_fbox *src_box,
|
||||
const struct wlr_box *dst_box, pixman_region32_t *opaque_region,
|
||||
int surface_width, int surface_height, int32_t surface_scale,
|
||||
int corner_radius) {
|
||||
struct wlr_output *wlr_output = output->wlr_output;
|
||||
struct fx_renderer *renderer = output->renderer;
|
||||
|
||||
// Check if damage is inside of box rect
|
||||
pixman_region32_t damage = create_damage(*dst_box, output_damage);
|
||||
|
||||
pixman_region32_t inverse_opaque;
|
||||
pixman_region32_init(&inverse_opaque);
|
||||
|
||||
if (!pixman_region32_not_empty(&damage)) {
|
||||
goto damage_finish;
|
||||
}
|
||||
|
||||
/*
|
||||
* Capture the back_buffer and blur it
|
||||
*/
|
||||
|
||||
pixman_box32_t surface_box = {
|
||||
.x1 = 0,
|
||||
.y1 = 0,
|
||||
.x2 = dst_box->width / wlr_output->scale,
|
||||
.y2 = dst_box->height / wlr_output->scale,
|
||||
};
|
||||
|
||||
wlr_region_scale(&inverse_opaque, &inverse_opaque, wlr_output->scale);
|
||||
|
||||
// Gets the non opaque region
|
||||
pixman_region32_copy(&inverse_opaque, opaque_region);
|
||||
pixman_region32_inverse(&inverse_opaque, &inverse_opaque, &surface_box);
|
||||
pixman_region32_intersect_rect(&inverse_opaque, &inverse_opaque, 0, 0,
|
||||
surface_width * surface_scale,
|
||||
surface_height * surface_scale);
|
||||
if (!pixman_region32_not_empty(&inverse_opaque)) {
|
||||
goto damage_finish;
|
||||
}
|
||||
|
||||
wlr_region_scale(&inverse_opaque, &inverse_opaque, wlr_output->scale);
|
||||
|
||||
struct fx_framebuffer *buffer = &renderer->blur_buffer;
|
||||
if (!buffer->texture.id || (!optimized && !config->blur_xray)) {
|
||||
pixman_region32_translate(&inverse_opaque, dst_box->x, dst_box->y);
|
||||
pixman_region32_intersect(&inverse_opaque, &inverse_opaque, &damage);
|
||||
|
||||
// Render the blur into its own buffer
|
||||
buffer = get_main_buffer_blur(renderer, output, &inverse_opaque,
|
||||
wlr_output->transform_matrix, dst_box);
|
||||
}
|
||||
|
||||
// Draw the blurred texture
|
||||
struct wlr_box monitor_box = get_monitor_box(wlr_output);
|
||||
enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform);
|
||||
float matrix[9];
|
||||
wlr_matrix_project_box(matrix, &monitor_box, transform, 0.0, wlr_output->transform_matrix);
|
||||
|
||||
struct decoration_data deco_data = get_undecorated_decoration_data();
|
||||
deco_data.corner_radius = corner_radius;
|
||||
render_texture(wlr_output, &damage, &buffer->texture, src_box, dst_box, matrix, deco_data);
|
||||
|
||||
damage_finish:
|
||||
pixman_region32_fini(&damage);
|
||||
pixman_region32_fini(&inverse_opaque);
|
||||
}
|
||||
|
||||
|
||||
static void render_surface_iterator(struct sway_output *output,
|
||||
struct sway_view *view, struct wlr_surface *surface,
|
||||
struct wlr_box *_box, void *_data) {
|
||||
|
@ -162,17 +342,12 @@ static void render_surface_iterator(struct sway_output *output,
|
|||
return;
|
||||
}
|
||||
|
||||
struct wlr_fbox src_box;
|
||||
wlr_surface_get_buffer_source_box(surface, &src_box);
|
||||
|
||||
struct wlr_box proj_box = *_box;
|
||||
scale_box(&proj_box, wlr_output->scale);
|
||||
|
||||
float matrix[9];
|
||||
enum wl_output_transform transform =
|
||||
wlr_output_transform_invert(surface->current.transform);
|
||||
wlr_matrix_project_box(matrix, &proj_box, transform, 0.0,
|
||||
wlr_output->transform_matrix);
|
||||
enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform);
|
||||
wlr_matrix_project_box(matrix, &proj_box, transform, 0.0, wlr_output->transform_matrix);
|
||||
|
||||
struct wlr_box dst_box = *_box;
|
||||
struct wlr_box *clip_box = data->clip_box;
|
||||
|
@ -185,9 +360,40 @@ static void render_surface_iterator(struct sway_output *output,
|
|||
|
||||
scale_box(&dst_box, wlr_output->scale);
|
||||
|
||||
data->deco_data.corner_radius *= wlr_output->scale;
|
||||
render_texture(wlr_output, output_damage, texture, &src_box, &dst_box,
|
||||
matrix, data->deco_data);
|
||||
struct decoration_data deco_data = data->deco_data;
|
||||
deco_data.corner_radius *= wlr_output->scale;
|
||||
|
||||
// render blur (view->surface == surface excludes blurring subsurfaces)
|
||||
if (deco_data.blur && should_parameters_blur() && view->surface == surface) {
|
||||
pixman_region32_t opaque_region;
|
||||
pixman_region32_init(&opaque_region);
|
||||
|
||||
bool has_alpha = false;
|
||||
if (deco_data.alpha < 1.0) {
|
||||
has_alpha = true;
|
||||
pixman_region32_union_rect(&opaque_region, &opaque_region, 0, 0, 0, 0);
|
||||
} else {
|
||||
has_alpha = !surface->opaque;
|
||||
pixman_region32_copy(&opaque_region, &surface->opaque_region);
|
||||
}
|
||||
|
||||
if (has_alpha) {
|
||||
int width, height;
|
||||
wlr_output_transformed_resolution(wlr_output, &width, &height);
|
||||
struct wlr_fbox blur_src_box = { 0, 0, width, height };
|
||||
bool is_floating = container_is_floating(view->container);
|
||||
render_blur(!is_floating, output, output_damage, &blur_src_box, &dst_box, &opaque_region,
|
||||
surface->current.width, surface->current.height, surface->current.scale, deco_data.corner_radius);
|
||||
}
|
||||
|
||||
pixman_region32_fini(&opaque_region);
|
||||
}
|
||||
|
||||
struct wlr_fbox src_box;
|
||||
wlr_surface_get_buffer_source_box(surface, &src_box);
|
||||
struct fx_texture fx_texture = fx_texture_from_wlr_texture(texture);
|
||||
render_texture(wlr_output, output_damage, &fx_texture, &src_box, &dst_box,
|
||||
matrix, deco_data);
|
||||
|
||||
wlr_presentation_surface_sampled_on_output(server.presentation, surface,
|
||||
wlr_output);
|
||||
|
@ -235,13 +441,73 @@ static void render_drag_icons(struct sway_output *output,
|
|||
render_surface_iterator, &data);
|
||||
}
|
||||
|
||||
void render_whole_output(struct fx_renderer *renderer, pixman_region32_t *original_damage,
|
||||
struct fx_texture *texture) {
|
||||
struct wlr_output *output = renderer->sway_output->wlr_output;
|
||||
struct wlr_box monitor_box = get_monitor_box(output);
|
||||
|
||||
enum wl_output_transform transform = wlr_output_transform_invert(output->transform);
|
||||
float matrix[9];
|
||||
wlr_matrix_project_box(matrix, &monitor_box, transform, 0.0, output->transform_matrix);
|
||||
|
||||
pixman_region32_t damage;
|
||||
pixman_region32_init(&damage);
|
||||
pixman_region32_union_rect(&damage, &damage, monitor_box.x, monitor_box.y,
|
||||
monitor_box.width, monitor_box.height);
|
||||
pixman_region32_intersect(&damage, &damage, original_damage);
|
||||
bool damaged = pixman_region32_not_empty(&damage);
|
||||
if (!damaged) {
|
||||
goto damage_finish;
|
||||
}
|
||||
|
||||
int nrects;
|
||||
pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
|
||||
for (int i = 0; i < nrects; ++i) {
|
||||
scissor_output(output, &rects[i]);
|
||||
fx_render_texture_with_matrix(renderer, texture, &monitor_box, matrix, get_undecorated_decoration_data());
|
||||
}
|
||||
|
||||
damage_finish:
|
||||
pixman_region32_fini(&damage);
|
||||
}
|
||||
|
||||
void render_monitor_blur(struct sway_output *output, pixman_region32_t *damage) {
|
||||
struct wlr_output *wlr_output = output->wlr_output;
|
||||
struct fx_renderer *renderer = output->renderer;
|
||||
|
||||
struct wlr_box monitor_box = get_monitor_box(wlr_output);
|
||||
pixman_region32_t fake_damage;
|
||||
pixman_region32_init_rect(&fake_damage, 0, 0, monitor_box.width, monitor_box.height);
|
||||
|
||||
// Render the blur
|
||||
struct fx_framebuffer *buffer = get_main_buffer_blur(renderer, output, &fake_damage,
|
||||
wlr_output->transform_matrix, &monitor_box);
|
||||
|
||||
// Render the newly blurred content into the blur_buffer
|
||||
fx_framebuffer_create(wlr_output, &renderer->blur_buffer, true);
|
||||
// Clear the damaged region of the blur_buffer
|
||||
float clear_color[] = { 0, 0, 0, 0 };
|
||||
int nrects;
|
||||
pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
|
||||
for (int i = 0; i < nrects; ++i) {
|
||||
scissor_output(wlr_output, &rects[i]);
|
||||
fx_renderer_clear(clear_color);
|
||||
}
|
||||
render_whole_output(renderer, &fake_damage, &buffer->texture);
|
||||
fx_framebuffer_bind(&renderer->main_buffer, monitor_box.width, monitor_box.height);
|
||||
|
||||
pixman_region32_fini(&fake_damage);
|
||||
|
||||
renderer->blur_buffer_dirty = false;
|
||||
}
|
||||
|
||||
// _box.x and .y are expected to be layout-local
|
||||
// _box.width and .height are expected to be output-buffer-local
|
||||
void render_rect(struct sway_output *output,
|
||||
pixman_region32_t *output_damage, const struct wlr_box *_box,
|
||||
float color[static 4]) {
|
||||
struct wlr_output *wlr_output = output->wlr_output;
|
||||
struct fx_renderer *renderer = output->server->renderer;
|
||||
struct fx_renderer *renderer = output->renderer;
|
||||
|
||||
struct wlr_box box;
|
||||
memcpy(&box, _box, sizeof(struct wlr_box));
|
||||
|
@ -269,7 +535,7 @@ void render_rounded_rect(struct sway_output *output, pixman_region32_t *output_d
|
|||
const struct wlr_box *_box, float color[static 4], int corner_radius,
|
||||
enum corner_location corner_location) {
|
||||
struct wlr_output *wlr_output = output->wlr_output;
|
||||
struct fx_renderer *renderer = output->server->renderer;
|
||||
struct fx_renderer *renderer = output->renderer;
|
||||
|
||||
struct wlr_box box;
|
||||
memcpy(&box, _box, sizeof(struct wlr_box));
|
||||
|
@ -300,7 +566,7 @@ void render_border_corner(struct sway_output *output, pixman_region32_t *output_
|
|||
const struct wlr_box *_box, const float color[static 4], int corner_radius,
|
||||
int border_thickness, enum corner_location corner_location) {
|
||||
struct wlr_output *wlr_output = output->wlr_output;
|
||||
struct fx_renderer *renderer = output->server->renderer;
|
||||
struct fx_renderer *renderer = output->renderer;
|
||||
|
||||
struct wlr_box box;
|
||||
memcpy(&box, _box, sizeof(struct wlr_box));
|
||||
|
@ -331,7 +597,7 @@ void render_box_shadow(struct sway_output *output, pixman_region32_t *output_dam
|
|||
const struct wlr_box *_box, const float color[static 4],
|
||||
float blur_sigma, float corner_radius) {
|
||||
struct wlr_output *wlr_output = output->wlr_output;
|
||||
struct fx_renderer *renderer = output->server->renderer;
|
||||
struct fx_renderer *renderer = output->renderer;
|
||||
|
||||
struct wlr_box box;
|
||||
memcpy(&box, _box, sizeof(struct wlr_box));
|
||||
|
@ -448,8 +714,7 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output
|
|||
|
||||
float matrix[9];
|
||||
enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform);
|
||||
wlr_matrix_project_box(matrix, &proj_box, transform, 0,
|
||||
wlr_output->transform_matrix);
|
||||
wlr_matrix_project_box(matrix, &proj_box, transform, 0, wlr_output->transform_matrix);
|
||||
|
||||
struct sway_container_state state = view->container->current;
|
||||
dst_box.x = state.x - output->lx;
|
||||
|
@ -465,7 +730,30 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output
|
|||
scale_box(&dst_box, wlr_output->scale);
|
||||
|
||||
deco_data.corner_radius *= wlr_output->scale;
|
||||
render_texture(wlr_output, damage, saved_buf->buffer->texture,
|
||||
|
||||
// render blur
|
||||
if (deco_data.blur && should_parameters_blur()) {
|
||||
struct wlr_gles2_texture_attribs attribs;
|
||||
wlr_gles2_texture_get_attribs(saved_buf->buffer->texture, &attribs);
|
||||
|
||||
if (deco_data.alpha < 1.0 || attribs.has_alpha) {
|
||||
pixman_region32_t opaque_region;
|
||||
pixman_region32_init(&opaque_region);
|
||||
pixman_region32_union_rect(&opaque_region, &opaque_region, 0, 0, 0, 0);
|
||||
|
||||
struct wlr_box monitor_box = get_monitor_box(wlr_output);
|
||||
// TODO: contribute wlroots function to allow creating an fbox from a box?
|
||||
struct wlr_fbox src_box = { monitor_box.x, monitor_box.y, monitor_box.width, monitor_box.height };
|
||||
bool is_floating = container_is_floating(view->container);
|
||||
render_blur(!is_floating, output, damage, &src_box, &dst_box, &opaque_region,
|
||||
saved_buf->width, saved_buf->height, 1, deco_data.corner_radius);
|
||||
|
||||
pixman_region32_fini(&opaque_region);
|
||||
}
|
||||
}
|
||||
|
||||
struct fx_texture fx_texture = fx_texture_from_wlr_texture(saved_buf->buffer->texture);
|
||||
render_texture(wlr_output, damage, &fx_texture,
|
||||
&saved_buf->source_box, &dst_box, matrix, deco_data);
|
||||
}
|
||||
|
||||
|
@ -481,6 +769,9 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
|
|||
struct sway_container *con, struct border_colors *colors,
|
||||
struct decoration_data deco_data) {
|
||||
struct sway_view *view = con->view;
|
||||
struct sway_container_state *state = &con->current;
|
||||
|
||||
// render view
|
||||
if (!wl_list_empty(&view->saved_buffers)) {
|
||||
render_saved_view(view, output, damage, deco_data);
|
||||
} else if (view->surface) {
|
||||
|
@ -768,7 +1059,8 @@ static void render_titlebar(struct sway_output *output,
|
|||
if (ob_inner_width < texture_box.width) {
|
||||
texture_box.width = ob_inner_width;
|
||||
}
|
||||
render_texture(output->wlr_output, output_damage, marks_texture,
|
||||
struct fx_texture fx_texture = fx_texture_from_wlr_texture(marks_texture);
|
||||
render_texture(output->wlr_output, output_damage, &fx_texture,
|
||||
NULL, &texture_box, matrix, deco_data);
|
||||
|
||||
// Padding above
|
||||
|
@ -844,7 +1136,8 @@ static void render_titlebar(struct sway_output *output,
|
|||
texture_box.width = ob_inner_width - ob_marks_width;
|
||||
}
|
||||
|
||||
render_texture(output->wlr_output, output_damage, title_texture,
|
||||
struct fx_texture fx_texture = fx_texture_from_wlr_texture(title_texture);
|
||||
render_texture(output->wlr_output, output_damage, &fx_texture,
|
||||
NULL, &texture_box, matrix, deco_data);
|
||||
|
||||
// Padding above
|
||||
|
@ -1052,6 +1345,7 @@ static void render_containers_linear(struct sway_output *output,
|
|||
.corner_radius = corner_radius,
|
||||
.saturation = child->saturation,
|
||||
.has_titlebar = has_titlebar,
|
||||
.blur = child->blur_enabled,
|
||||
};
|
||||
render_view(output, damage, child, colors, deco_data);
|
||||
if (has_titlebar) {
|
||||
|
@ -1159,6 +1453,7 @@ static void render_containers_tabbed(struct sway_output *output,
|
|||
.corner_radius = current->corner_radius,
|
||||
.saturation = current->saturation,
|
||||
.has_titlebar = true,
|
||||
.blur = current->blur_enabled,
|
||||
};
|
||||
render_view(output, damage, current, current_colors, deco_data);
|
||||
} else {
|
||||
|
@ -1233,6 +1528,7 @@ static void render_containers_stacked(struct sway_output *output,
|
|||
.saturation = current->saturation,
|
||||
.corner_radius = current->corner_radius,
|
||||
.has_titlebar = true,
|
||||
.blur = current->blur_enabled,
|
||||
};
|
||||
render_view(output, damage, current, current_colors, deco_data);
|
||||
} else {
|
||||
|
@ -1333,6 +1629,7 @@ static void render_floating_container(struct sway_output *soutput,
|
|||
.saturation = con->saturation,
|
||||
.corner_radius = con->corner_radius,
|
||||
.has_titlebar = has_titlebar,
|
||||
.blur = con->blur_enabled,
|
||||
};
|
||||
render_view(soutput, damage, con, colors, deco_data);
|
||||
if (has_titlebar) {
|
||||
|
@ -1375,10 +1672,82 @@ static void render_seatops(struct sway_output *output,
|
|||
}
|
||||
}
|
||||
|
||||
struct find_effect_iter_data {
|
||||
struct workspace_effect_info *effect_info;
|
||||
bool blur_buffer_dirty;
|
||||
};
|
||||
|
||||
static bool find_con_effect_iterator(struct sway_container *con, void* _data) {
|
||||
struct sway_view *view = con->view;
|
||||
struct find_effect_iter_data *data = _data;
|
||||
struct workspace_effect_info *effect_info = data->effect_info;
|
||||
|
||||
if (!view) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (con->blur_enabled && !view->surface->opaque) {
|
||||
effect_info->container_wants_blur = true;
|
||||
|
||||
bool is_floating = container_is_floating(con);
|
||||
// Check if we should render optimized blur
|
||||
if (data->blur_buffer_dirty
|
||||
// Only test floating windows when xray is enabled
|
||||
&& (!is_floating || (is_floating && config->blur_xray))) {
|
||||
effect_info->should_render_optimized_blur = true;
|
||||
}
|
||||
}
|
||||
if (con->shadow_enabled) {
|
||||
effect_info->container_wants_shadow = true;
|
||||
}
|
||||
|
||||
// Stop the iteration if all of the effects have been found.
|
||||
// Ensures that no effect is skipped if returning early
|
||||
return effect_info->container_wants_blur
|
||||
&& effect_info->container_wants_shadow
|
||||
&& effect_info->should_render_optimized_blur;
|
||||
}
|
||||
|
||||
static struct workspace_effect_info get_workspace_effect_info(struct sway_output *sway_output) {
|
||||
struct fx_renderer *renderer = sway_output->renderer;
|
||||
struct sway_workspace *workspace = sway_output->current.active_workspace;
|
||||
|
||||
struct workspace_effect_info effect_info = {
|
||||
.container_wants_blur = false,
|
||||
.container_wants_shadow = false,
|
||||
.should_render_optimized_blur = false,
|
||||
.expanded_size = 0
|
||||
};
|
||||
|
||||
if (!workspace_is_visible(workspace)) {
|
||||
return effect_info;
|
||||
}
|
||||
|
||||
// Iterate through the workspace containers and check if any effects are requested
|
||||
struct find_effect_iter_data iter_data = {
|
||||
.effect_info = &effect_info,
|
||||
.blur_buffer_dirty = renderer->blur_buffer_dirty
|
||||
};
|
||||
workspace_find_container(workspace, find_con_effect_iterator, &iter_data);
|
||||
|
||||
// Set the expanded damage region
|
||||
bool shadow_enabled = effect_info.container_wants_shadow || config->shadow_enabled;
|
||||
int shadow_sigma = shadow_enabled ? config->shadow_blur_sigma : 0;
|
||||
bool blur_enabled = effect_info.container_wants_blur || config->blur_enabled;
|
||||
int blur_size = blur_enabled ? get_blur_size() : 0;
|
||||
// +1 as a margin of error
|
||||
effect_info.expanded_size = MAX(shadow_sigma, blur_size) + 1;
|
||||
|
||||
return effect_info;
|
||||
}
|
||||
|
||||
void output_render(struct sway_output *output, struct timespec *when,
|
||||
pixman_region32_t *damage) {
|
||||
struct wlr_output *wlr_output = output->wlr_output;
|
||||
struct fx_renderer *renderer = output->server->renderer;
|
||||
struct fx_renderer *renderer = output->renderer;
|
||||
|
||||
int width, height;
|
||||
wlr_output_transformed_resolution(wlr_output, &width, &height);
|
||||
|
||||
struct sway_workspace *workspace = output->current.active_workspace;
|
||||
if (workspace == NULL) {
|
||||
|
@ -1390,23 +1759,56 @@ void output_render(struct sway_output *output, struct timespec *when,
|
|||
fullscreen_con = workspace->current.fullscreen;
|
||||
}
|
||||
|
||||
fx_renderer_begin(renderer, wlr_output->width, wlr_output->height);
|
||||
|
||||
if (debug.damage == DAMAGE_RERENDER) {
|
||||
int width, height;
|
||||
wlr_output_transformed_resolution(wlr_output, &width, &height);
|
||||
pixman_region32_union_rect(damage, damage, 0, 0, width, height);
|
||||
}
|
||||
|
||||
if (!pixman_region32_not_empty(damage)) {
|
||||
// Output isn't damaged but needs buffer swap
|
||||
goto renderer_end;
|
||||
bool has_blur = false;
|
||||
bool blur_optimize_should_render = false;
|
||||
bool damage_not_empty = pixman_region32_not_empty(damage);
|
||||
pixman_region32_t extended_damage;
|
||||
pixman_region32_init(&extended_damage);
|
||||
if (!fullscreen_con && !server.session_lock.locked && damage_not_empty) {
|
||||
// Check if there are any windows to blur
|
||||
struct workspace_effect_info effect_info = get_workspace_effect_info(output);
|
||||
has_blur = effect_info.container_wants_blur || config->blur_enabled;
|
||||
if (effect_info.should_render_optimized_blur) {
|
||||
blur_optimize_should_render = true;
|
||||
// Damage the whole output
|
||||
pixman_region32_union_rect(damage, damage, 0, 0, width, height);
|
||||
}
|
||||
|
||||
if (debug.damage == DAMAGE_HIGHLIGHT) {
|
||||
// Extend the damaged region
|
||||
int expanded_size = effect_info.expanded_size;
|
||||
if (expanded_size > 0) {
|
||||
int32_t damage_width = damage->extents.x2 - damage->extents.x1;
|
||||
int32_t damage_height = damage->extents.y2 - damage->extents.y1;
|
||||
// Limit the damage extent to the size of the monitor to prevent overflow
|
||||
if (damage_width > width || damage_height > height) {
|
||||
pixman_region32_intersect_rect(damage, damage, 0, 0, width, height);
|
||||
}
|
||||
|
||||
wlr_region_expand(damage, damage, expanded_size);
|
||||
pixman_region32_copy(&extended_damage, damage);
|
||||
wlr_region_expand(damage, damage, expanded_size);
|
||||
} else {
|
||||
pixman_region32_copy(&extended_damage, damage);
|
||||
}
|
||||
} else {
|
||||
pixman_region32_copy(&extended_damage, damage);
|
||||
}
|
||||
|
||||
if (debug.damage == DAMAGE_HIGHLIGHT && damage_not_empty) {
|
||||
fx_renderer_clear((float[]){1, 1, 0, 1});
|
||||
}
|
||||
|
||||
fx_renderer_begin(renderer, output);
|
||||
|
||||
if (!damage_not_empty) {
|
||||
// Output isn't damaged but needs buffer swap
|
||||
goto renderer_end;
|
||||
}
|
||||
|
||||
if (server.session_lock.locked) {
|
||||
float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
if (server.session_lock.lock == NULL) {
|
||||
|
@ -1494,6 +1896,11 @@ void output_render(struct sway_output *output, struct timespec *when,
|
|||
render_layer_toplevel(output, damage,
|
||||
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
|
||||
|
||||
bool blur_enabled = has_blur && should_parameters_blur();
|
||||
if (blur_enabled && blur_optimize_should_render && renderer->blur_buffer_dirty) {
|
||||
render_monitor_blur(output, damage);
|
||||
}
|
||||
|
||||
render_workspace(output, damage, workspace, workspace->current.focused);
|
||||
render_floating(output, damage);
|
||||
#if HAVE_XWAYLAND
|
||||
|
@ -1524,6 +1931,7 @@ void output_render(struct sway_output *output, struct timespec *when,
|
|||
.corner_radius = 0,
|
||||
.saturation = focus->saturation,
|
||||
.has_titlebar = false,
|
||||
.blur = false,
|
||||
};
|
||||
render_view_popups(focus->view, output, damage, deco_data);
|
||||
}
|
||||
|
@ -1536,29 +1944,45 @@ render_overlay:
|
|||
render_drag_icons(output, damage, &root->drag_icons);
|
||||
|
||||
renderer_end:
|
||||
// Draw the contents of our buffer into the wlr buffer
|
||||
fx_framebuffer_bind(&renderer->wlr_buffer, width, height);
|
||||
float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
if (pixman_region32_not_empty(&extended_damage)) {
|
||||
int nrects;
|
||||
pixman_box32_t *rects = pixman_region32_rectangles(&extended_damage, &nrects);
|
||||
for (int i = 0; i < nrects; ++i) {
|
||||
scissor_output(wlr_output, &rects[i]);
|
||||
fx_renderer_clear(clear_color);
|
||||
}
|
||||
}
|
||||
render_whole_output(renderer, &extended_damage, &renderer->main_buffer.texture);
|
||||
fx_renderer_scissor(NULL);
|
||||
fx_renderer_end(renderer);
|
||||
|
||||
// Draw the software cursors
|
||||
wlr_renderer_begin(output->server->wlr_renderer, wlr_output->width, wlr_output->height);
|
||||
wlr_output_render_software_cursors(wlr_output, damage);
|
||||
wlr_renderer_end(output->server->wlr_renderer);
|
||||
fx_renderer_end();
|
||||
|
||||
int width, height;
|
||||
wlr_output_transformed_resolution(wlr_output, &width, &height);
|
||||
fx_renderer_scissor(NULL);
|
||||
|
||||
pixman_region32_t frame_damage;
|
||||
pixman_region32_init(&frame_damage);
|
||||
|
||||
enum wl_output_transform transform =
|
||||
wlr_output_transform_invert(wlr_output->transform);
|
||||
wlr_region_transform(&frame_damage, &output->damage_ring.current,
|
||||
transform, width, height);
|
||||
enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform);
|
||||
/*
|
||||
* Extend the frame damage by the blur size to properly calc damage for the
|
||||
* next buffer swap. Thanks Emersion for your excellent damage tracking blog-post!
|
||||
*/
|
||||
wlr_region_transform(&frame_damage, &extended_damage, transform, width, height);
|
||||
|
||||
if (debug.damage != DAMAGE_DEFAULT) {
|
||||
if (debug.damage != DAMAGE_DEFAULT || blur_optimize_should_render) {
|
||||
pixman_region32_union_rect(&frame_damage, &frame_damage,
|
||||
0, 0, wlr_output->width, wlr_output->height);
|
||||
}
|
||||
|
||||
wlr_output_set_damage(wlr_output, &frame_damage);
|
||||
|
||||
pixman_region32_fini(&extended_damage);
|
||||
pixman_region32_fini(&frame_damage);
|
||||
|
||||
if (!wlr_output_commit(wlr_output)) {
|
||||
|
|
|
@ -14,7 +14,9 @@ sway_sources = files(
|
|||
'xdg_decoration.c',
|
||||
|
||||
'desktop/desktop.c',
|
||||
'desktop/fx_renderer/fx_framebuffer.c',
|
||||
'desktop/fx_renderer/fx_renderer.c',
|
||||
'desktop/fx_renderer/fx_texture.c',
|
||||
'desktop/fx_renderer/matrix.c',
|
||||
'desktop/idle_inhibit_v1.c',
|
||||
'desktop/layer_shell.c',
|
||||
|
@ -48,6 +50,10 @@ sway_sources = files(
|
|||
'commands/assign.c',
|
||||
'commands/bar.c',
|
||||
'commands/bind.c',
|
||||
'commands/blur.c',
|
||||
'commands/blur_passes.c',
|
||||
'commands/blur_radius.c',
|
||||
'commands/blur_xray.c',
|
||||
'commands/border.c',
|
||||
'commands/client.c',
|
||||
'commands/corner_radius.c',
|
||||
|
|
|
@ -86,12 +86,6 @@ bool server_init(struct sway_server *server) {
|
|||
sway_log(SWAY_ERROR, "Failed to create wlr_renderer");
|
||||
return false;
|
||||
}
|
||||
struct wlr_egl *egl = wlr_gles2_renderer_get_egl(server->wlr_renderer);
|
||||
server->renderer = fx_renderer_create(egl);
|
||||
if (!server->renderer) {
|
||||
sway_log(SWAY_ERROR, "Failed to create fx_renderer");
|
||||
return false;
|
||||
}
|
||||
|
||||
wlr_renderer_init_wl_shm(server->wlr_renderer, server->wl_display);
|
||||
|
||||
|
|
|
@ -686,6 +686,21 @@ The default colors are:
|
|||
*shadow_color* <hex color with alpha>
|
||||
The shadow color. Default color: #0000007F
|
||||
|
||||
*blur* enable|disable
|
||||
Sets whether blur should be drawn. Can also be set per window with
|
||||
*for_window*.
|
||||
|
||||
*blur_xray* enable|disable
|
||||
Sets whether blur should only display the background.
|
||||
|
||||
*blur_passes* <value>
|
||||
Adjusts the blur passes of windows between 0 (disabled) and 10
|
||||
while 2 is the default value.
|
||||
|
||||
*blur_radius* <value>
|
||||
Adjusts the blur radius of windows between 0 (disabled) and 10
|
||||
while 5 is the default value.
|
||||
|
||||
*default_border* normal|none|pixel [<n>]
|
||||
Set default border style for new tiled windows.
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ struct sway_container *container_create(struct sway_view *view) {
|
|||
c->saturation = 1.0f;
|
||||
c->dim = config->default_dim_inactive;
|
||||
c->shadow_enabled = config->shadow_enabled;
|
||||
c->blur_enabled = config->blur_enabled;
|
||||
c->corner_radius = config->corner_radius;
|
||||
|
||||
if (!view) {
|
||||
|
|
Loading…
Reference in a new issue