Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
scenefx/render/fx_renderer/fx_pass.c (59.6K)
1 #include <math.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <assert.h>
5 #include <pixman.h>
6 #include <time.h>
7 #include <unistd.h>
8 #include <wlr/render/allocator.h>
9 #include <wlr/render/drm_syncobj.h>
10 #include <wlr/util/transform.h>
11 #include <wlr/util/log.h>
12 #include <wlr/util/region.h>
13
14 #include "render/egl.h"
15 #include "render/fx_renderer/fx_renderer.h"
16 #include "render/fx_renderer/shaders.h"
17 #include "render/pass.h"
18 #include "render/tracy.h"
19 #include "scenefx/render/fx_renderer/fx_offscreen_buffers.h"
20 #include "scenefx/render/fx_renderer/fx_renderer.h"
21 #include "scenefx/types/fx/blur_data.h"
22 #include "util/env.h"
23 #include "util/matrix.h"
24
25 #define MAX_QUADS 86 // 4kb
26
27 // The blur traces below sit in the per-node, per-frame render path: at ~10 blur
28 // nodes and 60Hz that is ~600 synchronous formatted writes/second on the same
29 // thread that composites and delivers frame callbacks. Gate them.
30 static bool cce_blur_debug(void) {
31 static bool initialized = false;
32 static bool enabled = false;
33 if (!initialized) {
34 enabled = env_parse_bool("CCE_BLUR_DEBUG");
35 initialized = true;
36 }
37 return enabled;
38 }
39
40 static void read_to_buffer_offset(struct fx_gles_render_pass *pass,
41 pixman_region32_t *_region, struct fx_framebuffer *dst_buffer,
42 struct fx_framebuffer *src_buffer, int ox, int oy);
43
44 struct fx_render_texture_options fx_render_texture_options_default(
45 const struct wlr_render_texture_options *base) {
46 struct fx_render_texture_options options = {
47 .corners = {0},
48 .discard_transparent = false,
49 .clip_box = NULL,
50 .clipped_region = {0},
51 };
52 memcpy(&options.base, base, sizeof(*base));
53 return options;
54 }
55
56 struct fx_render_rect_options fx_render_rect_options_default(
57 const struct wlr_render_rect_options *base) {
58 struct fx_render_rect_options options = {
59 .base = *base,
60 .clipped_region = {
61 .area = { .0, .0, .0, .0 },
62 .corners = {0},
63 },
64 };
65 return options;
66 }
67
68 bool fx_render_pass_init_offscreen_buffers(struct wlr_render_pass *render_pass,
69 struct wlr_output *output) {
70 struct fx_gles_render_pass *pass = fx_get_render_pass(render_pass);
71 if (output == NULL) {
72 pass->fx_offscreen_buffers = NULL;
73 return false;
74 }
75 if (pass->fx_offscreen_buffers != NULL) {
76 wlr_log(WLR_ERROR, "Extra buffers called twice. Ignoring...");
77 return true;
78 }
79
80 // For per output framebuffers
81 pass->fx_offscreen_buffers = fx_offscreen_buffers_try_get(output);
82 if (pass->fx_offscreen_buffers == NULL) {
83 wlr_log(WLR_ERROR, "Failed to get/create effect framebuffers for output: %s",
84 output->name);
85 return false;
86 }
87
88 // Update the buffers if needed
89 struct fx_renderer *renderer = pass->buffer->renderer;
90 const int width = pass->buffer->buffer->width;
91 const int height = pass->buffer->buffer->height;
92 bool failed = false;
93 fx_framebuffer_get_or_create_custom(renderer, output->allocator, width, height, false,
94 &pass->fx_offscreen_buffers->blur_saved_pixels_buffer, &failed);
95 fx_framebuffer_get_or_create_custom(renderer, output->allocator, width, height, true,
96 &pass->fx_offscreen_buffers->effects_buffer, &failed);
97 fx_framebuffer_get_or_create_custom(renderer, output->allocator, width, height, true,
98 &pass->fx_offscreen_buffers->effects_buffer_swapped, &failed);
99 // The optimized-blur cache carries a quarter-output margin on every
100 // side (see fx_offscreen_buffers.cache_margin_*).
101 const int margin_x = width / 4;
102 const int margin_y = height / 4;
103 pass->fx_offscreen_buffers->cache_margin_x = margin_x;
104 pass->fx_offscreen_buffers->cache_margin_y = margin_y;
105 fx_framebuffer_get_or_create_custom(renderer, output->allocator,
106 width + 2 * margin_x, height + 2 * margin_y, false,
107 &pass->fx_offscreen_buffers->optimized_blur_buffer, &failed);
108
109 // Bind back to the default buffer
110 fx_framebuffer_bind(pass->buffer);
111
112 if (failed) {
113 fx_offscreen_buffers_destroy(pass->fx_offscreen_buffers);
114 pass->fx_offscreen_buffers = NULL;
115 wlr_log(WLR_ERROR, "Failed to create effect framebuffers");
116 return false;
117 }
118 return true;
119 }
120
121 ///
122 /// Base Wlroots pass functions
123 ///
124
125 static const struct wlr_render_pass_impl render_pass_impl;
126
127 struct fx_gles_render_pass *fx_get_render_pass(struct wlr_render_pass *render_pass) {
128 assert(render_pass->impl == &render_pass_impl);
129 struct fx_gles_render_pass *pass = wl_container_of(render_pass, pass, base);
130 return pass;
131 }
132
133 static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
134 struct fx_gles_render_pass *pass = fx_get_render_pass(wlr_pass);
135 struct fx_renderer *renderer = pass->buffer->renderer;
136 struct fx_render_timer *timer = pass->timer;
137 bool ok = false;
138
139 TRACY_BOTH_ZONES_START(pass->buffer->renderer);
140 push_fx_debug(renderer);
141
142 if (timer) {
143 // clear disjoint flag
144 GLint64 disjoint;
145 renderer->procs.glGetInteger64vEXT(GL_GPU_DISJOINT_EXT, &disjoint);
146 // set up the query
147 renderer->procs.glQueryCounterEXT(timer->id, GL_TIMESTAMP_EXT);
148 // get end-of-CPU-work time in GL time domain
149 renderer->procs.glGetInteger64vEXT(GL_TIMESTAMP_EXT, &timer->gl_cpu_end);
150 // get end-of-CPU-work time in CPU time domain
151 clock_gettime(CLOCK_MONOTONIC, &timer->cpu_end);
152 }
153
154 if (pass->signal_timeline != NULL) {
155 EGLSyncKHR sync = wlr_egl_create_sync(renderer->egl, -1);
156 if (sync == EGL_NO_SYNC_KHR) {
157 goto out;
158 }
159
160 int sync_file_fd = wlr_egl_dup_fence_fd(renderer->egl, sync);
161 wlr_egl_destroy_sync(renderer->egl, sync);
162 if (sync_file_fd < 0) {
163 goto out;
164 }
165
166 ok = wlr_drm_syncobj_timeline_import_sync_file(pass->signal_timeline, pass->signal_point, sync_file_fd);
167 close(sync_file_fd);
168 if (!ok) {
169 goto out;
170 }
171 } else {
172 glFlush();
173 }
174
175 ok = true;
176
177 out:
178 glBindFramebuffer(GL_FRAMEBUFFER, 0);
179
180 pop_fx_debug(renderer);
181 TRACY_BOTH_ZONES_END;
182 TRACY_GPU_ZONE_COLLECT(renderer);
183
184 wlr_egl_restore_context(&pass->prev_ctx);
185
186 wlr_drm_syncobj_timeline_unref(pass->signal_timeline);
187 wlr_buffer_unlock(pass->buffer->buffer);
188
189 pass->fx_offscreen_buffers = NULL;
190 pixman_region32_fini(&pass->blur_padding_region);
191
192 free(pass);
193
194 return ok;
195 }
196
197 static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
198 const struct wlr_render_texture_options *options) {
199 struct fx_gles_render_pass *pass = fx_get_render_pass(wlr_pass);
200 const struct fx_render_texture_options fx_options =
201 fx_render_texture_options_default(options);
202 // Re-use fx function but with default options
203 // TODO: Simplified version?
204 fx_render_pass_add_texture(pass, &fx_options);
205 }
206
207 static void render_pass_add_rect(struct wlr_render_pass *wlr_pass,
208 const struct wlr_render_rect_options *options) {
209 struct fx_gles_render_pass *pass = fx_get_render_pass(wlr_pass);
210 const struct fx_render_rect_options fx_options =
211 fx_render_rect_options_default(options);
212 // Re-use fx function but with default options
213 // TODO: Simplified version?
214 fx_render_pass_add_rect(pass, &fx_options);
215 }
216
217 static const struct wlr_render_pass_impl render_pass_impl = {
218 .submit = render_pass_submit,
219 .add_texture = render_pass_add_texture,
220 .add_rect = render_pass_add_rect,
221 };
222
223 ///
224 /// FX pass functions
225 ///
226
227 // TODO: REMOVE STENCILING
228
229 // Initialize the stenciling work
230 static void stencil_mask_init(void) {
231 glClearStencil(0);
232 glClear(GL_STENCIL_BUFFER_BIT);
233 glEnable(GL_STENCIL_TEST);
234
235 glStencilFunc(GL_ALWAYS, 1, 0xFF);
236 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
237 // Disable writing to color buffer
238 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
239 }
240
241 // Close the mask
242 static void stencil_mask_close(bool draw_inside_mask) {
243 // Reenable writing to color buffer
244 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
245 if (draw_inside_mask) {
246 glStencilFunc(GL_EQUAL, 1, 0xFF);
247 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
248 return;
249 }
250 glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
251 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
252 }
253
254 // Finish stenciling and clear the buffer
255 static void stencil_mask_fini(void) {
256 glClearStencil(0);
257 glClear(GL_STENCIL_BUFFER_BIT);
258 glDisable(GL_STENCIL_TEST);
259 }
260
261 static void render(const struct wlr_box *box, const pixman_region32_t *clip, GLint attrib) {
262 pixman_region32_t region;
263 pixman_region32_init_rect(®ion, box->x, box->y, box->width, box->height);
264
265 if (clip) {
266 pixman_region32_intersect(®ion, ®ion, clip);
267 }
268
269 int rects_len;
270 const pixman_box32_t *rects = pixman_region32_rectangles(®ion, &rects_len);
271 if (rects_len == 0) {
272 pixman_region32_fini(®ion);
273 return;
274 }
275
276 glEnableVertexAttribArray(attrib);
277
278 for (int i = 0; i < rects_len;) {
279 int batch = rects_len - i < MAX_QUADS ? rects_len - i : MAX_QUADS;
280 int batch_end = batch + i;
281
282 size_t vert_index = 0;
283 GLfloat verts[MAX_QUADS * 6 * 2];
284 for (; i < batch_end; i++) {
285 const pixman_box32_t *rect = &rects[i];
286
287 verts[vert_index++] = (GLfloat)(rect->x1 - box->x) / box->width;
288 verts[vert_index++] = (GLfloat)(rect->y1 - box->y) / box->height;
289 verts[vert_index++] = (GLfloat)(rect->x2 - box->x) / box->width;
290 verts[vert_index++] = (GLfloat)(rect->y1 - box->y) / box->height;
291 verts[vert_index++] = (GLfloat)(rect->x1 - box->x) / box->width;
292 verts[vert_index++] = (GLfloat)(rect->y2 - box->y) / box->height;
293 verts[vert_index++] = (GLfloat)(rect->x2 - box->x) / box->width;
294 verts[vert_index++] = (GLfloat)(rect->y1 - box->y) / box->height;
295 verts[vert_index++] = (GLfloat)(rect->x2 - box->x) / box->width;
296 verts[vert_index++] = (GLfloat)(rect->y2 - box->y) / box->height;
297 verts[vert_index++] = (GLfloat)(rect->x1 - box->x) / box->width;
298 verts[vert_index++] = (GLfloat)(rect->y2 - box->y) / box->height;
299 }
300
301 glVertexAttribPointer(attrib, 2, GL_FLOAT, GL_FALSE, 0, verts);
302 glDrawArrays(GL_TRIANGLES, 0, batch * 6);
303 }
304
305 glDisableVertexAttribArray(attrib);
306
307 pixman_region32_fini(®ion);
308 }
309
310 static void set_proj_matrix(GLint loc, float proj[9], const struct wlr_box *box) {
311 float gl_matrix[9];
312 wlr_matrix_identity(gl_matrix);
313 wlr_matrix_translate(gl_matrix, box->x, box->y);
314 wlr_matrix_scale(gl_matrix, box->width, box->height);
315 wlr_matrix_multiply(gl_matrix, proj, gl_matrix);
316 glUniformMatrix3fv(loc, 1, GL_FALSE, gl_matrix);
317 }
318
319 static void set_tex_matrix(GLint loc, enum wl_output_transform trans,
320 const struct wlr_fbox *box) {
321 float tex_matrix[9];
322 wlr_matrix_identity(tex_matrix);
323 wlr_matrix_translate(tex_matrix, box->x, box->y);
324 wlr_matrix_scale(tex_matrix, box->width, box->height);
325 wlr_matrix_translate(tex_matrix, .5, .5);
326
327 // since textures have a different origin point we have to transform
328 // differently if we are rotating
329 if (trans & WL_OUTPUT_TRANSFORM_90) {
330 wlr_matrix_transform(tex_matrix, wlr_output_transform_invert(trans));
331 } else {
332 wlr_matrix_transform(tex_matrix, trans);
333 }
334 wlr_matrix_translate(tex_matrix, -.5, -.5);
335
336 glUniformMatrix3fv(loc, 1, GL_FALSE, tex_matrix);
337 }
338
339 static void setup_blending(enum wlr_render_blend_mode mode) {
340 switch (mode) {
341 case WLR_RENDER_BLEND_MODE_PREMULTIPLIED:
342 glEnable(GL_BLEND);
343 break;
344 case WLR_RENDER_BLEND_MODE_NONE:
345 glDisable(GL_BLEND);
346 break;
347 }
348 }
349
350 static bool apply_clip_region(pixman_region32_t *clip_region,
351 const struct wlr_box *clipped_region_box, const struct fx_corner_fradii *corners) {
352 if (!wlr_box_empty(clipped_region_box)) {
353 float top = fmax(corners->top_left, corners->top_right);
354 float bottom = fmax(corners->bottom_left, corners->bottom_right);
355 float left = fmax(corners->top_left, corners->bottom_left);
356 float right = fmax(corners->top_right, corners->bottom_right);
357
358 pixman_region32_t user_clip_region;
359 pixman_region32_init_rect(
360 &user_clip_region,
361 clipped_region_box->x + (left * 0.3),
362 clipped_region_box->y + (top * 0.3),
363 fmax(clipped_region_box->width - (left + right) * 0.3, 0),
364 fmax(clipped_region_box->height - (top + bottom) * 0.3, 0)
365 );
366 pixman_region32_subtract(clip_region, clip_region, &user_clip_region);
367 pixman_region32_fini(&user_clip_region);
368 return true;
369 }
370
371 return false;
372 }
373
374 void fx_render_pass_add_texture(struct fx_gles_render_pass *pass,
375 const struct fx_render_texture_options *fx_options) {
376 const struct wlr_render_texture_options *options = &fx_options->base;
377 struct fx_renderer *renderer = pass->buffer->renderer;
378 struct fx_texture *texture = fx_get_texture(options->texture);
379
380 struct tex_shader *shader = NULL;
381
382 bool use_effects = !fx_corner_fradii_is_empty(&fx_options->corners)
383 || clipped_fregion_is_valid(&fx_options->clipped_region);
384 switch (texture->target) {
385 case GL_TEXTURE_2D:
386 if (texture->has_alpha) {
387 shader = use_effects
388 ? &renderer->shaders.tex_effects_rgba
389 : &renderer->shaders.tex_rgba;
390 } else {
391 shader = use_effects
392 ? &renderer->shaders.tex_effects_rgbx
393 : &renderer->shaders.tex_rgbx;
394 }
395 break;
396 case GL_TEXTURE_EXTERNAL_OES:
397 // EGL_EXT_image_dma_buf_import_modifiers requires
398 // GL_OES_EGL_image_external
399 assert(renderer->exts.OES_egl_image_external);
400 shader = use_effects
401 ? &renderer->shaders.tex_effects_ext
402 : &renderer->shaders.tex_ext;
403 break;
404 default:
405 abort();
406 }
407
408 struct wlr_box dst_box;
409 struct wlr_fbox src_fbox;
410 wlr_render_texture_options_get_src_box(options, &src_fbox);
411 wlr_render_texture_options_get_dst_box(options, &dst_box);
412 float alpha = wlr_render_texture_options_get_alpha(options);
413
414 const struct wlr_box *clip_box = &dst_box;
415 if (!wlr_box_empty(fx_options->clip_box)) {
416 clip_box = fx_options->clip_box;
417 }
418
419 src_fbox.x /= options->texture->width;
420 src_fbox.y /= options->texture->height;
421 src_fbox.width /= options->texture->width;
422 src_fbox.height /= options->texture->height;
423
424 TRACY_BOTH_ZONES_START(renderer);
425 TRACY_ZONE_TEXT_f("dst_box (WxH, X, Y): %dx%d, %d, %d",
426 dst_box.width, dst_box.height, dst_box.x, dst_box.y);
427 TRACY_ZONE_TEXT_f("clip_box (WxH, X, Y): %dx%d, %d, %d",
428 clip_box->width, clip_box->height, clip_box->x, clip_box->y);
429 TRACY_ZONE_TEXT_f("src_box (WxH, X, Y): %lfx%lf, %lf, %lf",
430 src_fbox.width, src_fbox.height, src_fbox.x, src_fbox.y);
431 TRACY_ZONE_TEXT_f("Shader Type: %s",
432 use_effects ? (
433 shader == &renderer->shaders.tex_effects_rgba ? "Effects RGBA"
434 : shader == &renderer->shaders.tex_effects_rgbx ? "Effects RGBX"
435 : "Effects EXT"
436 ) : (
437 shader == &renderer->shaders.tex_rgba ? "RGBA"
438 : shader == &renderer->shaders.tex_rgbx ? "RGBX"
439 : "EXT"
440 )
441 );
442 push_fx_debug(renderer);
443
444 if (options->wait_timeline != NULL) {
445 int sync_file_fd =
446 wlr_drm_syncobj_timeline_export_sync_file(options->wait_timeline, options->wait_point);
447 if (sync_file_fd < 0) {
448 TRACY_BOTH_ZONES_END_FAIL;
449 return;
450 }
451
452 EGLSyncKHR sync = wlr_egl_create_sync(renderer->egl, sync_file_fd);
453 close(sync_file_fd);
454 if (sync == EGL_NO_SYNC_KHR) {
455 TRACY_BOTH_ZONES_END_FAIL;
456 return;
457 }
458
459 bool ok = wlr_egl_wait_sync(renderer->egl, sync);
460 wlr_egl_destroy_sync(renderer->egl, sync);
461 if (!ok) {
462 TRACY_BOTH_ZONES_END_FAIL;
463 return;
464 }
465 }
466
467 bool has_alpha = texture->has_alpha || alpha < 1.0 || use_effects;
468 TRACY_ZONE_TEXT_f("Has Alpha: %d", has_alpha);
469 setup_blending(!has_alpha ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode);
470
471 pixman_region32_t clip_region;
472 if (options->clip) {
473 pixman_region32_init(&clip_region);
474 pixman_region32_copy(&clip_region, options->clip);
475 } else {
476 pixman_region32_init_rect(&clip_region, dst_box.x, dst_box.y, dst_box.width, dst_box.height);
477 }
478 const struct wlr_box clipped_region_box = fx_options->clipped_region.area;
479 struct fx_corner_fradii clipped_region_corners = fx_options->clipped_region.corners;
480 apply_clip_region(&clip_region, &clipped_region_box, &clipped_region_corners);
481
482 glUseProgram(shader->program);
483
484 glActiveTexture(GL_TEXTURE0);
485 glBindTexture(texture->target, texture->tex);
486
487 switch (options->filter_mode) {
488 case WLR_SCALE_FILTER_BILINEAR:
489 glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
490 glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
491 break;
492 case WLR_SCALE_FILTER_NEAREST:
493 glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
494 glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
495 break;
496 }
497
498 glUniform1i(shader->tex, 0);
499 glUniform1f(shader->alpha, alpha);
500 glUniform1f(shader->compress_ceil, fx_options->compress.ceil);
501 glUniform1f(shader->compress_knee, fx_options->compress.knee);
502 glUniform1i(shader->compress_invert, fx_options->compress.invert);
503
504 glUniform1f(shader->discard_transparent, fx_options->discard_transparent);
505
506 if (use_effects) {
507 struct fx_corner_fradii corners = fx_options->corners;
508
509 glUniform2f(shader->effects.size, clip_box->width, clip_box->height);
510 glUniform2f(shader->effects.position, clip_box->x, clip_box->y);
511 uniform_corner_radii_set(&shader->effects.radius, &corners);
512
513 glUniform2f(shader->effects.clip_size, clipped_region_box.width, clipped_region_box.height);
514 glUniform2f(shader->effects.clip_position, clipped_region_box.x, clipped_region_box.y);
515 uniform_corner_radii_set(&shader->effects.clip_radius, &clipped_region_corners);
516 }
517
518 set_proj_matrix(shader->proj, pass->projection_matrix, &dst_box);
519 set_tex_matrix(shader->tex_proj, options->transform, &src_fbox);
520
521 render(&dst_box, &clip_region, shader->pos_attrib);
522 pixman_region32_fini(&clip_region);
523
524 glBindTexture(texture->target, 0);
525
526 pop_fx_debug(renderer);
527 TRACY_BOTH_ZONES_END;
528 }
529
530 void fx_render_pass_add_rect(struct fx_gles_render_pass *pass,
531 const struct fx_render_rect_options *fx_options) {
532 const struct wlr_render_rect_options *options = &fx_options->base;
533
534 struct fx_renderer *renderer = pass->buffer->renderer;
535
536 const struct wlr_render_color *color = &options->color;
537 struct wlr_box box;
538 struct wlr_buffer *wlr_buffer = pass->buffer->buffer;
539 wlr_render_rect_options_get_box(options, wlr_buffer, &box);
540
541 const bool should_clip = clipped_fregion_is_valid(&fx_options->clipped_region);
542
543 enum wlr_render_blend_mode blend_mode =
544 (color->a == 1.0 && !should_clip) ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode;
545 const bool use_fast_clear =
546 blend_mode == WLR_RENDER_BLEND_MODE_NONE && // includes check for `should_clip`
547 options->clip == NULL &&
548 box.x == 0 && box.y == 0 &&
549 box.width == wlr_buffer->width &&
550 box.height == wlr_buffer->height;
551
552 TRACY_BOTH_ZONES_START(renderer);
553 TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
554 TRACY_ZONE_TEXT_f("Color RGBA: %f, %f, %f, %f", color->r, color->g, color->b, color->a);
555 TRACY_ZONE_TEXT_f("Use fast clear optimization: %d", use_fast_clear);
556
557 push_fx_debug(renderer);
558 if (use_fast_clear) {
559 glClearColor(color->r, color->g, color->b, color->a);
560 glClear(GL_COLOR_BUFFER_BIT);
561 } else {
562 const struct wlr_box *clipped_region_box = &fx_options->clipped_region.area;
563 const struct fx_corner_fradii *clipped_region_corners = &fx_options->clipped_region.corners;
564
565 TRACY_ZONE_TEXT_f("Clip Box (WxH, X, Y): %dx%d, %d, %d",
566 clipped_region_box->width, clipped_region_box->height,
567 clipped_region_box->x, clipped_region_box->y);
568 TRACY_ZONE_TEXT_f("Clip Box Corners (TL, TR, BL, BR): %f, %f, %f, %f",
569 clipped_region_corners->top_left,
570 clipped_region_corners->top_right,
571 clipped_region_corners->bottom_left,
572 clipped_region_corners->bottom_right);
573
574 pixman_region32_t clip_region;
575 if (options->clip) {
576 pixman_region32_init(&clip_region);
577 pixman_region32_copy(&clip_region, options->clip);
578 } else {
579 pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
580 }
581
582 apply_clip_region(&clip_region, clipped_region_box, clipped_region_corners);
583
584 setup_blending(blend_mode);
585 struct quad_shader *shader = should_clip
586 ? &renderer->shaders.quad_clip
587 : &renderer->shaders.quad;
588 glUseProgram(shader->program);
589 set_proj_matrix(shader->proj, pass->projection_matrix, &box);
590 glUniform4f(shader->color, color->r, color->g, color->b, color->a);
591 if (should_clip) {
592 glUniform2f(shader->effects.clip_size, clipped_region_box->width, clipped_region_box->height);
593 glUniform2f(shader->effects.clip_position, clipped_region_box->x, clipped_region_box->y);
594 uniform_corner_radii_set(&shader->effects.clip_radius, clipped_region_corners);
595 }
596 render(&box, &clip_region, shader->pos_attrib);
597
598 pixman_region32_fini(&clip_region);
599 }
600
601 pop_fx_debug(renderer);
602 TRACY_BOTH_ZONES_END;
603 }
604
605 void fx_render_pass_add_rect_grad(struct fx_gles_render_pass *pass,
606 const struct fx_render_rect_grad_options *fx_options) {
607 const struct wlr_render_rect_options *options = &fx_options->base;
608
609 struct fx_renderer *renderer = pass->buffer->renderer;
610
611 if (renderer->shaders.quad_grad.max_len <= fx_options->gradient.count) {
612 glDeleteProgram(renderer->shaders.quad_grad.program);
613 if (!link_quad_grad_program(&renderer->shaders.quad_grad, fx_options->gradient.count + 1)) {
614 wlr_log(WLR_ERROR, "Could not link quad shader after updating max_len to %d. Aborting renderer", fx_options->gradient.count + 1);
615 abort();
616 }
617 }
618
619 struct wlr_box box;
620 struct wlr_buffer *wlr_buffer = pass->buffer->buffer;
621 wlr_render_rect_options_get_box(options, wlr_buffer, &box);
622
623 TRACY_BOTH_ZONES_START(renderer);
624 TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
625 TRACY_ZONE_TEXT_f("Gradient:");
626 TRACY_ZONE_TEXT_f("\tNum Colors: %d", fx_options->gradient.count);
627 TRACY_ZONE_TEXT_f("\tBlend: %d", fx_options->gradient.blend);
628 TRACY_ZONE_TEXT_f("\tDegree: %f", fx_options->gradient.degree);
629 TRACY_ZONE_TEXT_f("\tType: %s",
630 fx_options->gradient.linear == 1 ? "Linear"
631 : fx_options->gradient.linear == 2 ? "Conic"
632 : "Unknown");
633 TRACY_ZONE_TEXT_f("\tOrigin: %fx%f",
634 fx_options->gradient.origin[0], fx_options->gradient.origin[1]);
635 TRACY_ZONE_TEXT_f("\tRange (WxH, X, Y): %dx%d, %d, %d",
636 fx_options->gradient.range.width, fx_options->gradient.range.height,
637 fx_options->gradient.range.x, fx_options->gradient.range.y);
638 // TODO: Display Colors (not really sure how it works without a scene example...)
639 push_fx_debug(renderer);
640
641 setup_blending(options->blend_mode);
642
643 struct quad_grad_shader shader = renderer->shaders.quad_grad;
644 glUseProgram(shader.program);
645
646 set_proj_matrix(shader.proj, pass->projection_matrix, &box);
647 glUniform4fv(shader.colors, fx_options->gradient.count, (GLfloat*)fx_options->gradient.colors);
648 glUniform1i(shader.count, fx_options->gradient.count);
649 glUniform2f(shader.size, fx_options->gradient.range.width, fx_options->gradient.range.height);
650 glUniform1f(shader.degree, fx_options->gradient.degree);
651 glUniform1f(shader.linear, fx_options->gradient.linear);
652 glUniform1f(shader.blend, fx_options->gradient.blend);
653 glUniform2f(shader.grad_box, fx_options->gradient.range.x, fx_options->gradient.range.y);
654 glUniform2f(shader.origin, fx_options->gradient.origin[0], fx_options->gradient.origin[1]);
655
656 render(&box, options->clip, shader.pos_attrib);
657
658 pop_fx_debug(renderer);
659 TRACY_BOTH_ZONES_END;
660 }
661
662 void fx_render_pass_add_rounded_rect(struct fx_gles_render_pass *pass,
663 const struct fx_render_rounded_rect_options *fx_options) {
664 const struct wlr_render_rect_options *options = &fx_options->base;
665
666 struct fx_renderer *renderer = pass->buffer->renderer;
667
668 const struct wlr_render_color *color = &options->color;
669 struct wlr_box box;
670 struct wlr_buffer *wlr_buffer = pass->buffer->buffer;
671 wlr_render_rect_options_get_box(options, wlr_buffer, &box);
672
673 pixman_region32_t clip_region;
674 if (options->clip) {
675 pixman_region32_init(&clip_region);
676 pixman_region32_copy(&clip_region, options->clip);
677 } else {
678 pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
679 }
680 const struct wlr_box *clipped_region_box = &fx_options->clipped_region.area;
681 const struct fx_corner_fradii *clipped_region_corners = &fx_options->clipped_region.corners;
682 apply_clip_region(&clip_region, clipped_region_box, clipped_region_corners);
683
684 TRACY_BOTH_ZONES_START(renderer);
685 TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
686 TRACY_ZONE_TEXT_f("Clip Box (WxH, X, Y): %dx%d, %d, %d",
687 clipped_region_box->width, clipped_region_box->height,
688 clipped_region_box->x, clipped_region_box->y);
689 TRACY_ZONE_TEXT_f("Clip Box Corners (TL, TR, BL, BR): %f, %f, %f, %f",
690 clipped_region_corners->top_left,
691 clipped_region_corners->top_right,
692 clipped_region_corners->bottom_left,
693 clipped_region_corners->bottom_right);
694 TRACY_ZONE_TEXT_f("Color RGBA: %f, %f, %f, %f", color->r, color->g, color->b, color->a);
695 TRACY_ZONE_TEXT_f("Corners (TL, TR, BL, BR): %f, %f, %f, %f",
696 clipped_region_corners->top_left,
697 clipped_region_corners->top_right,
698 clipped_region_corners->bottom_left,
699 clipped_region_corners->bottom_right);
700 push_fx_debug(renderer);
701
702 setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
703
704 struct quad_round_shader shader = renderer->shaders.quad_round;
705
706 glUseProgram(shader.program);
707
708 set_proj_matrix(shader.proj, pass->projection_matrix, &box);
709 glUniform4f(shader.color, color->r, color->g, color->b, color->a);
710
711 glUniform2f(shader.size, box.width, box.height);
712 glUniform2f(shader.position, box.x, box.y);
713 glUniform2f(shader.clip_size, clipped_region_box->width, clipped_region_box->height);
714 glUniform2f(shader.clip_position, clipped_region_box->x, clipped_region_box->y);
715 uniform_corner_radii_set(&shader.clip_radius, clipped_region_corners);
716
717 struct fx_corner_fradii corners = fx_options->corners;
718 uniform_corner_radii_set(&shader.radius, &corners);
719 int fade_mode = fx_options->fade_inset % 10;
720 float fade_inset = (float)(fx_options->fade_inset / 10) / 100.0f;
721 glUniform1f(shader.fade_inset, fade_inset);
722 glUniform1i(shader.fade_mode, fade_mode);
723
724 render(&box, &clip_region, renderer->shaders.quad_round.pos_attrib);
725 pixman_region32_fini(&clip_region);
726
727 pop_fx_debug(renderer);
728 TRACY_BOTH_ZONES_END;
729 }
730
731 void fx_render_pass_add_rounded_rect_grad(struct fx_gles_render_pass *pass,
732 const struct fx_render_rounded_rect_grad_options *fx_options) {
733 const struct wlr_render_rect_options *options = &fx_options->base;
734
735 struct fx_renderer *renderer = pass->buffer->renderer;
736
737 if (renderer->shaders.quad_grad_round.max_len <= fx_options->gradient.count) {
738 glDeleteProgram(renderer->shaders.quad_grad_round.program);
739 if (!link_quad_grad_round_program(&renderer->shaders.quad_grad_round, fx_options->gradient.count + 1)) {
740 wlr_log(WLR_ERROR, "Could not link quad shader after updating max_len to %d. Aborting renderer", fx_options->gradient.count + 1);
741 abort();
742 }
743 }
744
745 struct wlr_box box;
746 struct wlr_buffer *wlr_buffer = pass->buffer->buffer;
747 wlr_render_rect_options_get_box(options, wlr_buffer, &box);
748
749 TRACY_BOTH_ZONES_START(renderer);
750 TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
751 TRACY_ZONE_TEXT_f("Corners (TL, TR, BL, BR): %f, %f, %f, %f",
752 fx_options->corners.top_left,
753 fx_options->corners.top_right,
754 fx_options->corners.bottom_left,
755 fx_options->corners.bottom_right);
756 TRACY_ZONE_TEXT_f("Gradient:");
757 TRACY_ZONE_TEXT_f("\tNum Colors: %d", fx_options->gradient.count);
758 TRACY_ZONE_TEXT_f("\tBlend: %d", fx_options->gradient.blend);
759 TRACY_ZONE_TEXT_f("\tDegree: %f", fx_options->gradient.degree);
760 TRACY_ZONE_TEXT_f("\tType: %s",
761 fx_options->gradient.linear == 1 ? "Linear"
762 : fx_options->gradient.linear == 2 ? "Conic"
763 : "Unknown");
764 TRACY_ZONE_TEXT_f("\tOrigin: %fx%f",
765 fx_options->gradient.origin[0], fx_options->gradient.origin[1]);
766 TRACY_ZONE_TEXT_f("\tRange (WxH, X, Y): %dx%d, %d, %d",
767 fx_options->gradient.range.width, fx_options->gradient.range.height,
768 fx_options->gradient.range.x, fx_options->gradient.range.y);
769 // TODO: Display Colors (not really sure how it works without a scene example...)
770 push_fx_debug(renderer);
771
772 setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
773
774 struct quad_grad_round_shader shader = renderer->shaders.quad_grad_round;
775 glUseProgram(shader.program);
776
777 set_proj_matrix(shader.proj, pass->projection_matrix, &box);
778
779 glUniform2f(shader.size, box.width, box.height);
780 glUniform2f(shader.position, box.x, box.y);
781
782 glUniform4fv(shader.colors, fx_options->gradient.count, (GLfloat*)fx_options->gradient.colors);
783 glUniform1i(shader.count, fx_options->gradient.count);
784 glUniform2f(shader.grad_size, fx_options->gradient.range.width, fx_options->gradient.range.height);
785 glUniform1f(shader.degree, fx_options->gradient.degree);
786 glUniform1f(shader.linear, fx_options->gradient.linear);
787 glUniform1f(shader.blend, fx_options->gradient.blend);
788 glUniform2f(shader.grad_box, fx_options->gradient.range.x, fx_options->gradient.range.y);
789 glUniform2f(shader.origin, fx_options->gradient.origin[0], fx_options->gradient.origin[1]);
790
791 struct fx_corner_fradii corners = fx_options->corners;
792 uniform_corner_radii_set(&shader.radius, &corners);
793
794 render(&box, options->clip, shader.pos_attrib);
795
796 pop_fx_debug(renderer);
797 TRACY_BOTH_ZONES_END;
798 }
799
800 void fx_render_pass_add_box_shadow(struct fx_gles_render_pass *pass,
801 const struct fx_render_box_shadow_options *options) {
802 struct fx_renderer *renderer = pass->buffer->renderer;
803
804 struct wlr_box box = options->box;
805 assert(box.width > 0 && box.height > 0);
806
807 pixman_region32_t clip_region;
808 if (options->clip) {
809 pixman_region32_init(&clip_region);
810 pixman_region32_copy(&clip_region, options->clip);
811 } else {
812 pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
813 }
814 const struct wlr_box clipped_region_box = options->clipped_region.area;
815 struct fx_corner_fradii clipped_region_corners = options->clipped_region.corners;
816 apply_clip_region(&clip_region, &clipped_region_box, &clipped_region_corners);
817
818 TRACY_BOTH_ZONES_START(renderer);
819 TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
820 TRACY_ZONE_TEXT_f("Clip Box (WxH, X, Y): %dx%d, %d, %d",
821 clipped_region_box.width, clipped_region_box.height,
822 clipped_region_box.x, clipped_region_box.y);
823 TRACY_ZONE_TEXT_f("Clip Box Corners (TL, TR, BL, BR): %f, %f, %f, %f",
824 clipped_region_corners.top_left,
825 clipped_region_corners.top_right,
826 clipped_region_corners.bottom_left,
827 clipped_region_corners.bottom_right);
828 TRACY_ZONE_TEXT_f("Shadow Options:");
829 TRACY_ZONE_TEXT_f("\tColor RGBA: %f, %f, %f, %f",
830 options->color.r, options->color.g, options->color.b, options->color.a);
831 TRACY_ZONE_TEXT_f("\tBlur Sigma: %f", options->blur_sigma);
832 push_fx_debug(renderer);
833
834 // blending will practically always be needed (unless we have a madman
835 // who uses opaque shadows with zero sigma), so just enable it
836 setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
837 glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
838
839 glUseProgram(renderer->shaders.box_shadow.program);
840
841 const struct wlr_render_color *color = &options->color;
842 set_proj_matrix(renderer->shaders.box_shadow.proj, pass->projection_matrix, &box);
843 glUniform4f(renderer->shaders.box_shadow.color, color->r, color->g, color->b, color->a);
844 glUniform1f(renderer->shaders.box_shadow.blur_sigma, options->blur_sigma);
845 glUniform2f(renderer->shaders.box_shadow.size, box.width, box.height);
846 glUniform2f(renderer->shaders.box_shadow.position, box.x, box.y);
847 glUniform1f(renderer->shaders.box_shadow.corner_radius, options->corner_radius);
848
849 uniform_corner_radii_set(&renderer->shaders.box_shadow.clip_radius, &clipped_region_corners);
850
851 glUniform2f(renderer->shaders.box_shadow.clip_position, clipped_region_box.x, clipped_region_box.y);
852 glUniform2f(renderer->shaders.box_shadow.clip_size, clipped_region_box.width, clipped_region_box.height);
853
854 render(&box, &clip_region, renderer->shaders.box_shadow.pos_attrib);
855 pixman_region32_fini(&clip_region);
856
857 glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
858
859 pop_fx_debug(renderer);
860 TRACY_BOTH_ZONES_END;
861 }
862
863 // Renders the blur for each damaged rect and swaps the buffer
864 static void render_blur_segments(struct fx_gles_render_pass *pass,
865 struct fx_render_blur_pass_options *fx_options, struct blur_shader* shader) {
866 struct fx_render_texture_options *tex_options = &fx_options->tex_options;
867 struct wlr_render_texture_options *options = &tex_options->base;
868 struct fx_renderer *renderer = pass->buffer->renderer;
869 struct blur_data *blur_data = fx_options->blur_data;
870
871 TRACY_BOTH_ZONES_START(renderer);
872 push_fx_debug(renderer);
873
874 // Swap fbo
875 if (fx_options->current_buffer == pass->fx_offscreen_buffers->effects_buffer) {
876 fx_framebuffer_bind(pass->fx_offscreen_buffers->effects_buffer_swapped);
877 } else {
878 fx_framebuffer_bind(pass->fx_offscreen_buffers->effects_buffer);
879 }
880
881 options->texture = fx_texture_from_buffer(&renderer->wlr_renderer,
882 fx_options->current_buffer->buffer);
883 struct fx_texture *texture = fx_get_texture(options->texture);
884
885 /*
886 * Render
887 */
888
889 struct wlr_box dst_box;
890 struct wlr_fbox src_fbox;
891 wlr_render_texture_options_get_src_box(options, &src_fbox);
892 wlr_render_texture_options_get_dst_box(options, &dst_box);
893 src_fbox.x /= options->texture->width;
894 src_fbox.y /= options->texture->height;
895 src_fbox.width /= options->texture->width;
896 src_fbox.height /= options->texture->height;
897
898 glDisable(GL_BLEND);
899 glDisable(GL_STENCIL_TEST);
900
901 glUseProgram(shader->program);
902
903 glActiveTexture(GL_TEXTURE0);
904 glBindTexture(texture->target, texture->tex);
905
906 switch (options->filter_mode) {
907 case WLR_SCALE_FILTER_BILINEAR:
908 glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
909 glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
910 break;
911 case WLR_SCALE_FILTER_NEAREST:
912 abort();
913 }
914
915 glUniform1i(shader->tex, 0);
916 glUniform1f(shader->radius, blur_data->radius);
917
918 if (shader == &renderer->shaders.blur1) {
919 glUniform2f(shader->halfpixel,
920 0.5f / (options->texture->width / 2.0f),
921 0.5f / (options->texture->height / 2.0f));
922 } else {
923 glUniform2f(shader->halfpixel,
924 0.5f / (options->texture->width * 2.0f),
925 0.5f / (options->texture->height * 2.0f));
926 }
927
928 set_proj_matrix(shader->proj, pass->projection_matrix, &dst_box);
929 set_tex_matrix(shader->tex_proj, options->transform, &src_fbox);
930
931 render(&dst_box, options->clip, shader->pos_attrib);
932
933 glBindTexture(texture->target, 0);
934 pop_fx_debug(renderer);
935 TRACY_BOTH_ZONES_END;
936
937 wlr_texture_destroy(options->texture);
938
939 // Swap buffer. We don't want to draw to the same buffer
940 if (fx_options->current_buffer != pass->fx_offscreen_buffers->effects_buffer) {
941 fx_options->current_buffer = pass->fx_offscreen_buffers->effects_buffer;
942 } else {
943 fx_options->current_buffer = pass->fx_offscreen_buffers->effects_buffer_swapped;
944 }
945 }
946
947 static void render_blur_effects(struct fx_gles_render_pass *pass,
948 struct fx_render_blur_pass_options *fx_options) {
949 struct fx_render_texture_options *tex_options = &fx_options->tex_options;
950 struct wlr_render_texture_options *options = &tex_options->base;
951 struct fx_renderer *renderer = pass->buffer->renderer;
952 struct blur_data *blur_data = fx_options->blur_data;
953 struct fx_texture *texture = fx_get_texture(options->texture);
954
955 struct blur_effects_shader shader = renderer->shaders.blur_effects;
956
957 struct wlr_box dst_box;
958 struct wlr_fbox src_fbox;
959 wlr_render_texture_options_get_src_box(options, &src_fbox);
960 wlr_render_texture_options_get_dst_box(options, &dst_box);
961
962 src_fbox.x /= options->texture->width;
963 src_fbox.y /= options->texture->height;
964 src_fbox.width /= options->texture->width;
965 src_fbox.height /= options->texture->height;
966
967 glDisable(GL_BLEND);
968 glDisable(GL_STENCIL_TEST);
969
970 TRACY_BOTH_ZONES_START(renderer);
971 push_fx_debug(renderer);
972
973 glUseProgram(shader.program);
974
975 glActiveTexture(GL_TEXTURE0);
976 glBindTexture(texture->target, texture->tex);
977
978 switch (options->filter_mode) {
979 case WLR_SCALE_FILTER_BILINEAR:
980 glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
981 glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
982 break;
983 case WLR_SCALE_FILTER_NEAREST:
984 abort();
985 }
986
987 glUniform1i(shader.tex, 0);
988 glUniform1f(shader.noise, blur_data->noise);
989 glUniform1f(shader.brightness, blur_data->brightness);
990 glUniform1f(shader.contrast, blur_data->contrast);
991 glUniform1f(shader.saturation, blur_data->saturation);
992
993 set_proj_matrix(shader.proj, pass->projection_matrix, &dst_box);
994 set_tex_matrix(shader.tex_proj, options->transform, &src_fbox);
995
996 render(&dst_box, options->clip, shader.pos_attrib);
997
998 glBindTexture(texture->target, 0);
999
1000 pop_fx_debug(renderer);
1001 TRACY_BOTH_ZONES_END;
1002
1003 wlr_texture_destroy(options->texture);
1004 }
1005
1006 // Blurs the fx_options current_buffer content and returns the blurred framebuffer.
1007 // Returns NULL when the blur parameters reach 0.
1008 static struct fx_framebuffer *get_main_buffer_blur(struct fx_gles_render_pass *pass,
1009 struct fx_render_blur_pass_options *fx_options) {
1010 if (pass->fx_offscreen_buffers == NULL) {
1011 wlr_log(WLR_ERROR, "FX Pass offscreen buffers not initialized. Skipping getting blur...");
1012 return NULL;
1013 }
1014
1015 struct fx_renderer *renderer = pass->buffer->renderer;
1016 struct wlr_box buffer_bounds = {
1017 0, 0,
1018 fx_options->current_buffer->buffer->width, fx_options->current_buffer->buffer->height
1019 };
1020
1021 // We don't want to affect the reference blur_data
1022 struct blur_data blur_data = blur_data_apply_strength(fx_options->blur_data, fx_options->blur_strength);
1023 if (fx_options->blur_strength <= 0 || !is_scene_blur_enabled(&blur_data)) {
1024 return NULL;
1025 }
1026 fx_options->blur_data = &blur_data;
1027
1028 pixman_region32_t damage;
1029 pixman_region32_init(&damage);
1030 pixman_region32_copy(&damage, fx_options->tex_options.base.clip);
1031 wlr_region_transform(&damage, &damage, fx_options->tex_options.base.transform,
1032 buffer_bounds.width, buffer_bounds.height);
1033
1034 wlr_region_expand(&damage, &damage, blur_data_calc_size(&blur_data));
1035 // Make sure that the region doesn't expand past the buffer bounds
1036 pixman_region32_intersect_rect(&damage, &damage,
1037 0, 0, buffer_bounds.width, buffer_bounds.height);
1038
1039 // damage region will be scaled, make a temp
1040 pixman_region32_t scaled_damage;
1041 pixman_region32_init(&scaled_damage);
1042
1043 fx_options->tex_options.base.src_box = (struct wlr_fbox) {
1044 0, 0,
1045 buffer_bounds.width, buffer_bounds.height,
1046 };
1047 fx_options->tex_options.base.dst_box = buffer_bounds;
1048 // Clip the blur to the damage
1049 fx_options->tex_options.base.clip = &scaled_damage;
1050 // Artifacts with NEAREST filter
1051 fx_options->tex_options.base.filter_mode = WLR_SCALE_FILTER_BILINEAR;
1052
1053 TRACY_BOTH_ZONES_START(renderer);
1054 TRACY_ZONE_TEXT_f("dst_box (WxH, X, Y): %dx%d, %d, %d",
1055 fx_options->tex_options.base.dst_box.width,
1056 fx_options->tex_options.base.dst_box.height,
1057 fx_options->tex_options.base.dst_box.x,
1058 fx_options->tex_options.base.dst_box.y);
1059 TRACY_ZONE_TEXT_f("clip_box (WxH, X, Y): %dx%d, %d, %d",
1060 fx_options->tex_options.clip_box->width,
1061 fx_options->tex_options.clip_box->height,
1062 fx_options->tex_options.clip_box->x,
1063 fx_options->tex_options.clip_box->y);
1064 TRACY_ZONE_TEXT_f("Corners (TL, TR, BL, BR): %f, %f, %f, %f",
1065 fx_options->corners.top_left,
1066 fx_options->corners.top_right,
1067 fx_options->corners.bottom_left,
1068 fx_options->corners.bottom_right);
1069 TRACY_ZONE_TEXT_f("src_box (WxH, X, Y): %lfx%lf, %lf, %lf",
1070 fx_options->tex_options.base.src_box.width,
1071 fx_options->tex_options.base.src_box.height,
1072 fx_options->tex_options.base.src_box.x,
1073 fx_options->tex_options.base.src_box.y);
1074 TRACY_ZONE_TEXT_f("Ignore Transparent: %d", fx_options->ignore_transparent);
1075 TRACY_ZONE_TEXT_f("Discard Transparent: %d", fx_options->tex_options.discard_transparent);
1076 TRACY_ZONE_TEXT_f("Use Optimized Blur: %d", fx_options->use_optimized_blur);
1077 TRACY_ZONE_TEXT_f("Blur Options:");
1078 TRACY_ZONE_TEXT_f("\tNum Blur Passes: %d", fx_options->blur_data->num_passes);
1079 TRACY_ZONE_TEXT_f("\tBlur Radius: %f", fx_options->blur_data->radius);
1080 TRACY_ZONE_TEXT_f("\tBrightness: %f", fx_options->blur_data->brightness);
1081 TRACY_ZONE_TEXT_f("\tContrast: %f", fx_options->blur_data->contrast);
1082 TRACY_ZONE_TEXT_f("\tNoise: %f", fx_options->blur_data->noise);
1083 TRACY_ZONE_TEXT_f("\tSaturation: %f", fx_options->blur_data->saturation);
1084 push_fx_debug(renderer);
1085
1086 // Downscale
1087 for (int i = 0; i < blur_data.num_passes; ++i) {
1088 wlr_region_scale(&scaled_damage, &damage, 1.0f / (1 << (i + 1)));
1089 render_blur_segments(pass, fx_options, &renderer->shaders.blur1);
1090 }
1091
1092 // Upscale
1093 for (int i = blur_data.num_passes - 1; i >= 0; --i) {
1094 // when upsampling we make the region twice as big
1095 wlr_region_scale(&scaled_damage, &damage, 1.0f / (1 << i));
1096 render_blur_segments(pass, fx_options, &renderer->shaders.blur2);
1097 }
1098
1099 pixman_region32_fini(&scaled_damage);
1100
1101 // Render additional blur effects like saturation, noise, contrast, etc...
1102 if (blur_data_should_parameters_blur_effects(&blur_data)
1103 && pixman_region32_not_empty(&damage)) {
1104 if (fx_options->current_buffer == pass->fx_offscreen_buffers->effects_buffer) {
1105 fx_framebuffer_bind(pass->fx_offscreen_buffers->effects_buffer_swapped);
1106 } else {
1107 fx_framebuffer_bind(pass->fx_offscreen_buffers->effects_buffer);
1108 }
1109 fx_options->tex_options.base.clip = &damage;
1110 fx_options->tex_options.base.texture = fx_texture_from_buffer(
1111 &renderer->wlr_renderer, fx_options->current_buffer->buffer);
1112 render_blur_effects(pass, fx_options);
1113 if (fx_options->current_buffer != pass->fx_offscreen_buffers->effects_buffer) {
1114 fx_options->current_buffer = pass->fx_offscreen_buffers->effects_buffer;
1115 } else {
1116 fx_options->current_buffer = pass->fx_offscreen_buffers->effects_buffer_swapped;
1117 }
1118 }
1119
1120 pixman_region32_fini(&damage);
1121
1122 // Bind back to the default buffer
1123 fx_framebuffer_bind(pass->buffer);
1124
1125 pop_fx_debug(renderer);
1126 TRACY_BOTH_ZONES_END;
1127
1128 return fx_options->current_buffer;
1129 }
1130
1131 void fx_render_pass_add_frame(struct fx_gles_render_pass *pass,
1132 const struct fx_render_frame_options *options) {
1133 struct fx_renderer *renderer = pass->buffer->renderer;
1134
1135 struct wlr_box box = options->box;
1136 assert(box.width > 0 && box.height > 0);
1137
1138 pixman_region32_t clip_region;
1139 if (options->clip) {
1140 pixman_region32_init(&clip_region);
1141 pixman_region32_copy(&clip_region, options->clip);
1142 } else {
1143 pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
1144 }
1145
1146 push_fx_debug(renderer);
1147
1148 // Premultiplied source-over: the shader emits colour already scaled by
1149 // its coverage, the same convention the bevel pass settled on.
1150 setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
1151 glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
1152
1153 glUseProgram(renderer->shaders.frame.program);
1154
1155 const struct wlr_render_color *color = &options->color;
1156 set_proj_matrix(renderer->shaders.frame.proj, pass->projection_matrix, &box);
1157 glUniform4f(renderer->shaders.frame.color, color->r, color->g, color->b, color->a);
1158 glUniform2f(renderer->shaders.frame.size, box.width, box.height);
1159 glUniform2f(renderer->shaders.frame.position, box.x, box.y);
1160 glUniform1f(renderer->shaders.frame.corner_radius, options->corner_radius);
1161 glUniform1f(renderer->shaders.frame.corner_shape, fx_corner_shape());
1162 glUniform1f(renderer->shaders.frame.band, options->band);
1163 glUniform1f(renderer->shaders.frame.band_min, options->band_min);
1164 glUniform1f(renderer->shaders.frame.corner_len, options->corner_len);
1165 glUniform1f(renderer->shaders.frame.gap, options->gap);
1166 glUniform1f(renderer->shaders.frame.hovered, options->hovered);
1167 glUniform1f(renderer->shaders.frame.swell_curve, options->swell_curve);
1168 glUniform1f(renderer->shaders.frame.bulge, options->bulge);
1169 glUniform1f(renderer->shaders.frame.buttons, options->buttons);
1170 glUniform4f(renderer->shaders.frame.exclusion, options->exclusion[0],
1171 options->exclusion[1], options->exclusion[2], options->exclusion[3]);
1172 glUniform4f(renderer->shaders.frame.hover_color,
1173 options->hover_color[0], options->hover_color[1],
1174 options->hover_color[2], options->hover_color[3]);
1175
1176 render(&box, &clip_region, renderer->shaders.frame.pos_attrib);
1177 pixman_region32_fini(&clip_region);
1178
1179 pop_fx_debug(renderer);
1180 }
1181
1182 void fx_render_pass_add_bevel(struct fx_gles_render_pass *pass,
1183 const struct fx_render_bevel_options *options) {
1184 struct fx_renderer *renderer = pass->buffer->renderer;
1185
1186 struct wlr_box box = options->box;
1187 assert(box.width > 0 && box.height > 0);
1188
1189 pixman_region32_t clip_region;
1190 if (options->clip) {
1191 pixman_region32_init(&clip_region);
1192 pixman_region32_copy(&clip_region, options->clip);
1193 } else {
1194 pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
1195 }
1196
1197 push_fx_debug(renderer);
1198
1199 // The highlight adds light and the shade subtracts it, both premultiplied
1200 // into the same draw — ordinary source-over blending. The src factor is
1201 // GL_ONE because the shader emits premultiplied colour: with GL_SRC_ALPHA
1202 // the blend multiplied by alpha a SECOND time, squaring the rim's
1203 // intensity, and left no way for a branch to composite additively (alpha
1204 // 0 zeroed the whole contribution instead of adding it).
1205 setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
1206 glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
1207
1208 glUseProgram(renderer->shaders.bevel.program);
1209
1210 const struct wlr_render_color *color = &options->color;
1211 set_proj_matrix(renderer->shaders.bevel.proj, pass->projection_matrix, &box);
1212 glUniform4f(renderer->shaders.bevel.color, color->r, color->g, color->b, color->a);
1213 glUniform2f(renderer->shaders.bevel.size, box.width, box.height);
1214 glUniform2f(renderer->shaders.bevel.position, box.x, box.y);
1215 glUniform1f(renderer->shaders.bevel.corner_radius, options->corner_radius);
1216 // The radius the compositor sends is span-widened for this exponent, so
1217 // the rim has to read it as the same superellipse the other cuts do.
1218 glUniform1f(renderer->shaders.bevel.corner_shape, fx_corner_shape());
1219 glUniform1f(renderer->shaders.bevel.thickness, options->thickness);
1220 glUniform2f(renderer->shaders.bevel.light_dir,
1221 options->light_dir[0], options->light_dir[1]);
1222 glUniform1f(renderer->shaders.bevel.light_intensity, options->light_intensity);
1223 glUniform1f(renderer->shaders.bevel.shade_intensity, options->shade_intensity);
1224 glUniform1f(renderer->shaders.bevel.shoulder, options->shoulder);
1225 glUniform1f(renderer->shaders.bevel.focus, options->focus);
1226 glUniform3f(renderer->shaders.bevel.focus_color,
1227 options->focus_color[0], options->focus_color[1], options->focus_color[2]);
1228 glUniform1f(renderer->shaders.bevel.focus_sharpness, options->focus_sharpness);
1229
1230 render(&box, &clip_region, renderer->shaders.bevel.pos_attrib);
1231 pixman_region32_fini(&clip_region);
1232
1233 glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
1234
1235 pop_fx_debug(renderer);
1236 }
1237
1238 void fx_render_pass_add_droplet(struct fx_gles_render_pass *pass,
1239 const struct fx_render_droplet_options *options) {
1240 struct fx_renderer *renderer = pass->buffer->renderer;
1241
1242 struct wlr_box box = options->box;
1243 assert(box.width > 0 && box.height > 0);
1244
1245 // The lens samples the FRAME-SO-FAR — everything already rendered below
1246 // this node (desktop, windows, all of it), copied 1:1 into the saved-
1247 // pixels buffer just before the draw, because GL cannot sample the
1248 // framebuffer it is rendering into. The optimized-blur bake is NOT used:
1249 // it captures bottom layers only (that's the DE's frost design), so a
1250 // drop over a window would refract the desktop hiding behind that
1251 // window instead of the window itself. Without offscreen buffers there
1252 // is nothing to refract; the client's own translucent drop still
1253 // renders, so skipping is a graceful degrade.
1254 if (pass->fx_offscreen_buffers == NULL) {
1255 return;
1256 }
1257 struct fx_framebuffer *snap = pass->fx_offscreen_buffers->blur_saved_pixels_buffer;
1258 if (snap == NULL || snap->buffer == NULL) {
1259 return;
1260 }
1261
1262 // Copy the drop's neighborhood: the box grown by the refraction reach
1263 // and the inverted ghost's sampling span (0.35 x the box about its
1264 // center), clamped to the buffer.
1265 int margin = (int)(options->refr + 1.0f
1266 + 0.35f * (float)(box.width > box.height ? box.width : box.height));
1267 pixman_region32_t copy_region;
1268 pixman_region32_init_rect(©_region,
1269 box.x - margin, box.y - margin,
1270 box.width + 2 * margin, box.height + 2 * margin);
1271 pixman_region32_intersect_rect(©_region, ©_region,
1272 0, 0, pass->buffer->buffer->width, pass->buffer->buffer->height);
1273 fx_render_pass_read_to_buffer(pass, ©_region, snap, pass->buffer);
1274 pixman_region32_fini(©_region);
1275
1276 struct wlr_texture *wlr_texture =
1277 fx_texture_from_buffer(&renderer->wlr_renderer, snap->buffer);
1278 if (wlr_texture == NULL) {
1279 return;
1280 }
1281 struct fx_texture *texture = fx_get_texture(wlr_texture);
1282
1283 pixman_region32_t clip_region;
1284 if (options->clip) {
1285 pixman_region32_init(&clip_region);
1286 pixman_region32_copy(&clip_region, options->clip);
1287 } else {
1288 pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
1289 }
1290
1291 push_fx_debug(renderer);
1292
1293 // The refracted backdrop is opaque inside the silhouette (it REPLACES
1294 // what the drop covers); the feathered rim blends source-over.
1295 setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
1296
1297 struct droplet_shader *shader = &renderer->shaders.droplet;
1298 glUseProgram(shader->program);
1299
1300 glActiveTexture(GL_TEXTURE0);
1301 glBindTexture(texture->target, texture->tex);
1302 glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1303 glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1304
1305 set_proj_matrix(shader->proj, pass->projection_matrix, &box);
1306 glUniform1i(shader->tex, 0);
1307 glUniform2f(shader->tex_size, snap->buffer->width, snap->buffer->height);
1308 glUniform2f(shader->position, box.x, box.y);
1309 glUniform2f(shader->size, box.width, box.height);
1310 glUniform1f(shader->attach_r, options->attach_r);
1311 glUniform1f(shader->sheet_r, options->sheet_r);
1312 glUniform1f(shader->bow_rise, options->bow_rise);
1313 glUniform1f(shader->blend_k, options->blend_k);
1314 glUniform1f(shader->curve, options->curve);
1315 glUniform1f(shader->band_px, options->band_px);
1316 glUniform1f(shader->refr, options->refr);
1317 glUniform1f(shader->ghost, options->ghost);
1318 glUniform1f(shader->compress_ceil, options->compress.ceil);
1319 glUniform1f(shader->compress_knee, options->compress.knee);
1320 glUniform1i(shader->compress_invert, options->compress.invert);
1321
1322 render(&box, &clip_region, shader->pos_attrib);
1323
1324 glBindTexture(texture->target, 0);
1325 pixman_region32_fini(&clip_region);
1326 wlr_texture_destroy(wlr_texture);
1327
1328 pop_fx_debug(renderer);
1329 }
1330
1331 void fx_render_pass_add_blur(struct fx_gles_render_pass *pass,
1332 struct fx_render_blur_pass_options *fx_options) {
1333 if (pass->fx_offscreen_buffers == NULL) {
1334 wlr_log(WLR_ERROR, "FX Pass offscreen buffers not initialized. Skipping blur...");
1335 return;
1336 }
1337
1338 struct fx_renderer *renderer = pass->buffer->renderer;
1339 struct fx_render_texture_options *tex_options = &fx_options->tex_options;
1340
1341 TRACY_BOTH_ZONES_START(renderer);
1342 push_fx_debug(renderer);
1343
1344 // NOTE: `optimized` here is the flag saying this node SAMPLES the cached
1345 // buffer — it is not a re-bake. The bake is add_optimized_blur, below.
1346 if (cce_blur_debug()) {
1347 wlr_log(WLR_INFO, "[scenefx] add_blur dst_box: %dx%d at (%d, %d), optimized: %d strength: %f",
1348 tex_options->base.dst_box.width, tex_options->base.dst_box.height,
1349 tex_options->base.dst_box.x, tex_options->base.dst_box.y,
1350 fx_options->use_optimized_blur, fx_options->blur_strength);
1351 }
1352
1353 const bool has_strength = fx_options->blur_strength < 1.0;
1354 struct fx_framebuffer *buffer = pass->fx_offscreen_buffers->optimized_blur_buffer;
1355 TRACY_ZONE_TEXT_f("Use Optimized Blur: %d", fx_options->use_optimized_blur);
1356 TRACY_ZONE_TEXT_f("Optimized Blur Successfully Used: %d",
1357 buffer && fx_options->use_optimized_blur);
1358 if (!fx_options->use_optimized_blur || has_strength) {
1359 // Render the blur into its own buffer (a reduced-strength blur of
1360 // an optimized node too: the cache holds only the full-strength
1361 // bake, at its node's anchor).
1362 struct fx_render_blur_pass_options blur_options = *fx_options;
1363 blur_options.current_buffer = pass->buffer;
1364 buffer = get_main_buffer_blur(pass, &blur_options);
1365 }
1366 if (!buffer) {
1367 goto finish;
1368 }
1369 struct wlr_texture *wlr_texture =
1370 fx_texture_from_buffer(&renderer->wlr_renderer, buffer->buffer);
1371 struct fx_texture *blur_texture = fx_get_texture(wlr_texture);
1372
1373 // Get a stencil of the window ignoring transparent regions
1374 if (fx_options->ignore_transparent && fx_options->tex_options.base.texture) {
1375 stencil_mask_init();
1376
1377 struct fx_render_texture_options tex_options = fx_options->tex_options;
1378 tex_options.discard_transparent = true;
1379 tex_options.clipped_region = fx_options->clipped_region;
1380 // The mask is the window's own buffer, not the backdrop.
1381 tex_options.compress = (struct fx_backdrop_compress){0};
1382 fx_render_pass_add_texture(pass, &tex_options);
1383
1384 stencil_mask_close(true);
1385 }
1386
1387 // Draw the blurred texture. The shared cache (optimized, full strength)
1388 // is drawn so that screen pixel s reads cache pixel s + shift — the
1389 // node's bake at its anchor, inside the margin; a freshly blurred
1390 // buffer is output-sized and in place.
1391 const bool direct_cache = fx_options->use_optimized_blur && !has_strength;
1392 tex_options->base.dst_box = (struct wlr_box) {
1393 .x = direct_cache ? -fx_options->cache_shift_x : 0,
1394 .y = direct_cache ? -fx_options->cache_shift_y : 0,
1395 .width = buffer->buffer->width,
1396 .height = buffer->buffer->height,
1397 };
1398 tex_options->base.src_box = (struct wlr_fbox) {
1399 .x = 0,
1400 .y = 0,
1401 .width = buffer->buffer->width,
1402 .height = buffer->buffer->height,
1403 };
1404 tex_options->base.texture = &blur_texture->wlr_texture;
1405 // since we're capturing from the fbo, transform will always be normal
1406 tex_options->base.transform = WL_OUTPUT_TRANSFORM_NORMAL;
1407 tex_options->clipped_region = fx_options->clipped_region;
1408 fx_render_pass_add_texture(pass, tex_options);
1409
1410 wlr_texture_destroy(&blur_texture->wlr_texture);
1411
1412 // Finish stenciling
1413 if (fx_options->ignore_transparent && fx_options->tex_options.base.texture) {
1414 stencil_mask_fini();
1415 }
1416
1417 finish:
1418 pop_fx_debug(renderer);
1419 TRACY_BOTH_ZONES_END;
1420 }
1421
1422 bool fx_render_pass_add_optimized_blur(struct fx_gles_render_pass *pass,
1423 struct fx_render_blur_pass_options *fx_options) {
1424 if (pass->fx_offscreen_buffers == NULL) {
1425 wlr_log(WLR_ERROR, "FX Pass offscreen buffers not initialized. Skipping optimized blur...");
1426 return false;
1427 }
1428 struct fx_renderer *renderer = pass->buffer->renderer;
1429 struct wlr_box dst_box = fx_options->tex_options.base.dst_box;
1430
1431 TRACY_BOTH_ZONES_START(renderer);
1432 TRACY_ZONE_TEXT_f("dst_box (WxH, X, Y): %dx%d, %d, %d",
1433 fx_options->tex_options.base.dst_box.width,
1434 fx_options->tex_options.base.dst_box.height,
1435 fx_options->tex_options.base.dst_box.x,
1436 fx_options->tex_options.base.dst_box.y);
1437 TRACY_ZONE_TEXT_f("clip_box (WxH, X, Y): %dx%d, %d, %d",
1438 fx_options->tex_options.clip_box->width,
1439 fx_options->tex_options.clip_box->height,
1440 fx_options->tex_options.clip_box->x,
1441 fx_options->tex_options.clip_box->y);
1442 TRACY_ZONE_TEXT_f("src_box (WxH, X, Y): %lfx%lf, %lf, %lf",
1443 fx_options->tex_options.base.src_box.width,
1444 fx_options->tex_options.base.src_box.height,
1445 fx_options->tex_options.base.src_box.x,
1446 fx_options->tex_options.base.src_box.y);
1447 TRACY_ZONE_TEXT_f("Ignore Transparent: %d", fx_options->ignore_transparent);
1448 TRACY_ZONE_TEXT_f("Discard Transparent: %d", fx_options->tex_options.discard_transparent);
1449 TRACY_ZONE_TEXT_f("Use Optimized Blur: %d", fx_options->use_optimized_blur);
1450 TRACY_ZONE_TEXT_f("Blur Options:");
1451 TRACY_ZONE_TEXT_f("\tNum Blur Passes: %d", fx_options->blur_data->num_passes);
1452 TRACY_ZONE_TEXT_f("\tBlur Radius: %f", fx_options->blur_data->radius);
1453 TRACY_ZONE_TEXT_f("\tBrightness: %f", fx_options->blur_data->brightness);
1454 TRACY_ZONE_TEXT_f("\tContrast: %f", fx_options->blur_data->contrast);
1455 TRACY_ZONE_TEXT_f("\tNoise: %f", fx_options->blur_data->noise);
1456 TRACY_ZONE_TEXT_f("\tSaturation: %f", fx_options->blur_data->saturation);
1457 push_fx_debug(renderer);
1458
1459 // The actual cache bake (a whole node, or a strip it newly exposes).
1460 if (cce_blur_debug()) {
1461 wlr_log(WLR_INFO, "[scenefx] add_optimized_blur dst_box: %dx%d at (%d, %d) shift (%d, %d)",
1462 dst_box.width, dst_box.height, dst_box.x, dst_box.y,
1463 fx_options->cache_shift_x, fx_options->cache_shift_y);
1464 }
1465
1466 pixman_region32_t clip;
1467 pixman_region32_init_rect(&clip,
1468 dst_box.x, dst_box.y, dst_box.width, dst_box.height);
1469
1470 // Render the blur into its own buffer
1471 struct fx_render_blur_pass_options blur_options = *fx_options;
1472 blur_options.current_buffer = pass->buffer;
1473 blur_options.tex_options.base.clip = &clip;
1474 struct fx_framebuffer *fx_buffer = get_main_buffer_blur(pass, &blur_options);
1475 if (fx_buffer != NULL) {
1476 // Land the newly blurred content in the cache at the node's anchor.
1477 read_to_buffer_offset(pass, &clip,
1478 pass->fx_offscreen_buffers->optimized_blur_buffer, fx_buffer,
1479 fx_options->cache_shift_x, fx_options->cache_shift_y);
1480 }
1481
1482 pixman_region32_fini(&clip);
1483
1484 pop_fx_debug(renderer);
1485 TRACY_BOTH_ZONES_END;
1486 return fx_buffer != NULL;
1487 }
1488
1489 /* Copy `_region` of src_buffer into dst_buffer, landing at (+ox, +oy). */
1490 static void read_to_buffer_offset(struct fx_gles_render_pass *pass,
1491 pixman_region32_t *_region, struct fx_framebuffer *dst_buffer,
1492 struct fx_framebuffer *src_buffer, int ox, int oy) {
1493 if (!_region || !pixman_region32_not_empty(_region)) {
1494 return;
1495 }
1496 TRACY_BOTH_ZONES_START(pass->buffer->renderer);
1497
1498 pixman_region32_t region;
1499 pixman_region32_init(®ion);
1500 pixman_region32_copy(®ion, _region);
1501 pixman_region32_translate(®ion, ox, oy);
1502
1503 struct wlr_texture *src_tex =
1504 fx_texture_from_buffer(&pass->buffer->renderer->wlr_renderer, src_buffer->buffer);
1505 if (src_tex == NULL) {
1506 goto done;
1507 }
1508
1509 // Draw onto the dst_buffer. The pass's projection and viewport are the
1510 // output's; a larger destination (the margin cache) needs its own for
1511 // the duration of the copy, or everything past the output's extent is
1512 // clipped away.
1513 const int dst_w = dst_buffer->buffer->width;
1514 const int dst_h = dst_buffer->buffer->height;
1515 const int pass_w = pass->buffer->buffer->width;
1516 const int pass_h = pass->buffer->buffer->height;
1517 const bool resize = dst_w != pass_w || dst_h != pass_h;
1518 float saved_proj[9];
1519 if (resize) {
1520 memcpy(saved_proj, pass->projection_matrix, sizeof(saved_proj));
1521 matrix_projection(pass->projection_matrix, dst_w, dst_h, WL_OUTPUT_TRANSFORM_FLIPPED_180);
1522 }
1523 fx_framebuffer_bind(dst_buffer);
1524 if (resize) {
1525 glViewport(0, 0, dst_w, dst_h);
1526 }
1527 wlr_render_pass_add_texture(&pass->base, &(struct wlr_render_texture_options) {
1528 .texture = src_tex,
1529 .clip = ®ion,
1530 .transform = WL_OUTPUT_TRANSFORM_NORMAL,
1531 .blend_mode = WLR_RENDER_BLEND_MODE_NONE,
1532 .dst_box = (struct wlr_box){
1533 .x = ox,
1534 .y = oy,
1535 .width = src_buffer->buffer->width,
1536 .height = src_buffer->buffer->height,
1537 },
1538 .src_box = (struct wlr_fbox){
1539 .x = 0,
1540 .y = 0,
1541 .width = src_buffer->buffer->width,
1542 .height = src_buffer->buffer->height,
1543 },
1544 });
1545 wlr_texture_destroy(src_tex);
1546
1547 // Bind back to the main WLR buffer
1548 fx_framebuffer_bind(pass->buffer);
1549 if (resize) {
1550 memcpy(pass->projection_matrix, saved_proj, sizeof(saved_proj));
1551 glViewport(0, 0, pass_w, pass_h);
1552 }
1553
1554 done:
1555 TRACY_BOTH_ZONES_END;
1556
1557 pixman_region32_fini(®ion);
1558 }
1559
1560 void fx_render_pass_read_to_buffer(struct fx_gles_render_pass *pass,
1561 pixman_region32_t *_region, struct fx_framebuffer *dst_buffer,
1562 struct fx_framebuffer *src_buffer) {
1563 read_to_buffer_offset(pass, _region, dst_buffer, src_buffer, 0, 0);
1564 }
1565
1566 static const char *reset_status_str(GLenum status) {
1567 switch (status) {
1568 case GL_GUILTY_CONTEXT_RESET_KHR:
1569 return "guilty";
1570 case GL_INNOCENT_CONTEXT_RESET_KHR:
1571 return "innocent";
1572 case GL_UNKNOWN_CONTEXT_RESET_KHR:
1573 return "unknown";
1574 default:
1575 return "<invalid>";
1576 }
1577 }
1578
1579 struct fx_gles_render_pass *fx_begin_buffer_pass(struct fx_framebuffer *buffer,
1580 struct wlr_egl_context *prev_ctx, struct fx_render_timer *timer,
1581 struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point) {
1582 struct fx_renderer *renderer = buffer->renderer;
1583 struct wlr_buffer *wlr_buffer = buffer->buffer;
1584
1585 if (renderer->procs.glGetGraphicsResetStatusKHR) {
1586 GLenum status = renderer->procs.glGetGraphicsResetStatusKHR();
1587 if (status != GL_NO_ERROR) {
1588 wlr_log(WLR_ERROR, "GPU reset (%s)", reset_status_str(status));
1589 wl_signal_emit_mutable(&renderer->wlr_renderer.events.lost, NULL);
1590 return NULL;
1591 }
1592 }
1593
1594 GLint fbo = fx_framebuffer_get_fbo(buffer);
1595 if (!fbo) {
1596 return NULL;
1597 }
1598
1599 struct fx_gles_render_pass *pass = calloc(1, sizeof(*pass));
1600 if (pass == NULL) {
1601 return NULL;
1602 }
1603
1604 wlr_render_pass_init(&pass->base, &render_pass_impl);
1605 wlr_buffer_lock(wlr_buffer);
1606 pass->buffer = buffer;
1607 pass->timer = timer;
1608 pass->prev_ctx = *prev_ctx;
1609 if (signal_timeline != NULL) {
1610 pass->signal_timeline = wlr_drm_syncobj_timeline_ref(signal_timeline);
1611 pass->signal_point = signal_point;
1612 }
1613
1614 pass->fx_offscreen_buffers = NULL;
1615 pixman_region32_init(&pass->blur_padding_region);
1616 pass->has_blur = false;
1617
1618 matrix_projection(pass->projection_matrix, wlr_buffer->width, wlr_buffer->height,
1619 WL_OUTPUT_TRANSFORM_FLIPPED_180);
1620
1621 push_fx_debug(renderer);
1622 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1623
1624 glViewport(0, 0, wlr_buffer->width, wlr_buffer->height);
1625 glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
1626 glDisable(GL_SCISSOR_TEST);
1627
1628 pop_fx_debug(renderer);
1629 return pass;
1630 }