Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
scenefx/render/fx_renderer/fx_pass.c (59K)
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
501 glUniform1f(shader->discard_transparent, fx_options->discard_transparent);
502
503 if (use_effects) {
504 struct fx_corner_fradii corners = fx_options->corners;
505
506 glUniform2f(shader->effects.size, clip_box->width, clip_box->height);
507 glUniform2f(shader->effects.position, clip_box->x, clip_box->y);
508 uniform_corner_radii_set(&shader->effects.radius, &corners);
509
510 glUniform2f(shader->effects.clip_size, clipped_region_box.width, clipped_region_box.height);
511 glUniform2f(shader->effects.clip_position, clipped_region_box.x, clipped_region_box.y);
512 uniform_corner_radii_set(&shader->effects.clip_radius, &clipped_region_corners);
513 }
514
515 set_proj_matrix(shader->proj, pass->projection_matrix, &dst_box);
516 set_tex_matrix(shader->tex_proj, options->transform, &src_fbox);
517
518 render(&dst_box, &clip_region, shader->pos_attrib);
519 pixman_region32_fini(&clip_region);
520
521 glBindTexture(texture->target, 0);
522
523 pop_fx_debug(renderer);
524 TRACY_BOTH_ZONES_END;
525 }
526
527 void fx_render_pass_add_rect(struct fx_gles_render_pass *pass,
528 const struct fx_render_rect_options *fx_options) {
529 const struct wlr_render_rect_options *options = &fx_options->base;
530
531 struct fx_renderer *renderer = pass->buffer->renderer;
532
533 const struct wlr_render_color *color = &options->color;
534 struct wlr_box box;
535 struct wlr_buffer *wlr_buffer = pass->buffer->buffer;
536 wlr_render_rect_options_get_box(options, wlr_buffer, &box);
537
538 const bool should_clip = clipped_fregion_is_valid(&fx_options->clipped_region);
539
540 enum wlr_render_blend_mode blend_mode =
541 (color->a == 1.0 && !should_clip) ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode;
542 const bool use_fast_clear =
543 blend_mode == WLR_RENDER_BLEND_MODE_NONE && // includes check for `should_clip`
544 options->clip == NULL &&
545 box.x == 0 && box.y == 0 &&
546 box.width == wlr_buffer->width &&
547 box.height == wlr_buffer->height;
548
549 TRACY_BOTH_ZONES_START(renderer);
550 TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
551 TRACY_ZONE_TEXT_f("Color RGBA: %f, %f, %f, %f", color->r, color->g, color->b, color->a);
552 TRACY_ZONE_TEXT_f("Use fast clear optimization: %d", use_fast_clear);
553
554 push_fx_debug(renderer);
555 if (use_fast_clear) {
556 glClearColor(color->r, color->g, color->b, color->a);
557 glClear(GL_COLOR_BUFFER_BIT);
558 } else {
559 const struct wlr_box *clipped_region_box = &fx_options->clipped_region.area;
560 const struct fx_corner_fradii *clipped_region_corners = &fx_options->clipped_region.corners;
561
562 TRACY_ZONE_TEXT_f("Clip Box (WxH, X, Y): %dx%d, %d, %d",
563 clipped_region_box->width, clipped_region_box->height,
564 clipped_region_box->x, clipped_region_box->y);
565 TRACY_ZONE_TEXT_f("Clip Box Corners (TL, TR, BL, BR): %f, %f, %f, %f",
566 clipped_region_corners->top_left,
567 clipped_region_corners->top_right,
568 clipped_region_corners->bottom_left,
569 clipped_region_corners->bottom_right);
570
571 pixman_region32_t clip_region;
572 if (options->clip) {
573 pixman_region32_init(&clip_region);
574 pixman_region32_copy(&clip_region, options->clip);
575 } else {
576 pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
577 }
578
579 apply_clip_region(&clip_region, clipped_region_box, clipped_region_corners);
580
581 setup_blending(blend_mode);
582 struct quad_shader *shader = should_clip
583 ? &renderer->shaders.quad_clip
584 : &renderer->shaders.quad;
585 glUseProgram(shader->program);
586 set_proj_matrix(shader->proj, pass->projection_matrix, &box);
587 glUniform4f(shader->color, color->r, color->g, color->b, color->a);
588 if (should_clip) {
589 glUniform2f(shader->effects.clip_size, clipped_region_box->width, clipped_region_box->height);
590 glUniform2f(shader->effects.clip_position, clipped_region_box->x, clipped_region_box->y);
591 uniform_corner_radii_set(&shader->effects.clip_radius, clipped_region_corners);
592 }
593 render(&box, &clip_region, shader->pos_attrib);
594
595 pixman_region32_fini(&clip_region);
596 }
597
598 pop_fx_debug(renderer);
599 TRACY_BOTH_ZONES_END;
600 }
601
602 void fx_render_pass_add_rect_grad(struct fx_gles_render_pass *pass,
603 const struct fx_render_rect_grad_options *fx_options) {
604 const struct wlr_render_rect_options *options = &fx_options->base;
605
606 struct fx_renderer *renderer = pass->buffer->renderer;
607
608 if (renderer->shaders.quad_grad.max_len <= fx_options->gradient.count) {
609 glDeleteProgram(renderer->shaders.quad_grad.program);
610 if (!link_quad_grad_program(&renderer->shaders.quad_grad, fx_options->gradient.count + 1)) {
611 wlr_log(WLR_ERROR, "Could not link quad shader after updating max_len to %d. Aborting renderer", fx_options->gradient.count + 1);
612 abort();
613 }
614 }
615
616 struct wlr_box box;
617 struct wlr_buffer *wlr_buffer = pass->buffer->buffer;
618 wlr_render_rect_options_get_box(options, wlr_buffer, &box);
619
620 TRACY_BOTH_ZONES_START(renderer);
621 TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
622 TRACY_ZONE_TEXT_f("Gradient:");
623 TRACY_ZONE_TEXT_f("\tNum Colors: %d", fx_options->gradient.count);
624 TRACY_ZONE_TEXT_f("\tBlend: %d", fx_options->gradient.blend);
625 TRACY_ZONE_TEXT_f("\tDegree: %f", fx_options->gradient.degree);
626 TRACY_ZONE_TEXT_f("\tType: %s",
627 fx_options->gradient.linear == 1 ? "Linear"
628 : fx_options->gradient.linear == 2 ? "Conic"
629 : "Unknown");
630 TRACY_ZONE_TEXT_f("\tOrigin: %fx%f",
631 fx_options->gradient.origin[0], fx_options->gradient.origin[1]);
632 TRACY_ZONE_TEXT_f("\tRange (WxH, X, Y): %dx%d, %d, %d",
633 fx_options->gradient.range.width, fx_options->gradient.range.height,
634 fx_options->gradient.range.x, fx_options->gradient.range.y);
635 // TODO: Display Colors (not really sure how it works without a scene example...)
636 push_fx_debug(renderer);
637
638 setup_blending(options->blend_mode);
639
640 struct quad_grad_shader shader = renderer->shaders.quad_grad;
641 glUseProgram(shader.program);
642
643 set_proj_matrix(shader.proj, pass->projection_matrix, &box);
644 glUniform4fv(shader.colors, fx_options->gradient.count, (GLfloat*)fx_options->gradient.colors);
645 glUniform1i(shader.count, fx_options->gradient.count);
646 glUniform2f(shader.size, fx_options->gradient.range.width, fx_options->gradient.range.height);
647 glUniform1f(shader.degree, fx_options->gradient.degree);
648 glUniform1f(shader.linear, fx_options->gradient.linear);
649 glUniform1f(shader.blend, fx_options->gradient.blend);
650 glUniform2f(shader.grad_box, fx_options->gradient.range.x, fx_options->gradient.range.y);
651 glUniform2f(shader.origin, fx_options->gradient.origin[0], fx_options->gradient.origin[1]);
652
653 render(&box, options->clip, shader.pos_attrib);
654
655 pop_fx_debug(renderer);
656 TRACY_BOTH_ZONES_END;
657 }
658
659 void fx_render_pass_add_rounded_rect(struct fx_gles_render_pass *pass,
660 const struct fx_render_rounded_rect_options *fx_options) {
661 const struct wlr_render_rect_options *options = &fx_options->base;
662
663 struct fx_renderer *renderer = pass->buffer->renderer;
664
665 const struct wlr_render_color *color = &options->color;
666 struct wlr_box box;
667 struct wlr_buffer *wlr_buffer = pass->buffer->buffer;
668 wlr_render_rect_options_get_box(options, wlr_buffer, &box);
669
670 pixman_region32_t clip_region;
671 if (options->clip) {
672 pixman_region32_init(&clip_region);
673 pixman_region32_copy(&clip_region, options->clip);
674 } else {
675 pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
676 }
677 const struct wlr_box *clipped_region_box = &fx_options->clipped_region.area;
678 const struct fx_corner_fradii *clipped_region_corners = &fx_options->clipped_region.corners;
679 apply_clip_region(&clip_region, clipped_region_box, clipped_region_corners);
680
681 TRACY_BOTH_ZONES_START(renderer);
682 TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
683 TRACY_ZONE_TEXT_f("Clip Box (WxH, X, Y): %dx%d, %d, %d",
684 clipped_region_box->width, clipped_region_box->height,
685 clipped_region_box->x, clipped_region_box->y);
686 TRACY_ZONE_TEXT_f("Clip Box Corners (TL, TR, BL, BR): %f, %f, %f, %f",
687 clipped_region_corners->top_left,
688 clipped_region_corners->top_right,
689 clipped_region_corners->bottom_left,
690 clipped_region_corners->bottom_right);
691 TRACY_ZONE_TEXT_f("Color RGBA: %f, %f, %f, %f", color->r, color->g, color->b, color->a);
692 TRACY_ZONE_TEXT_f("Corners (TL, TR, BL, BR): %f, %f, %f, %f",
693 clipped_region_corners->top_left,
694 clipped_region_corners->top_right,
695 clipped_region_corners->bottom_left,
696 clipped_region_corners->bottom_right);
697 push_fx_debug(renderer);
698
699 setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
700
701 struct quad_round_shader shader = renderer->shaders.quad_round;
702
703 glUseProgram(shader.program);
704
705 set_proj_matrix(shader.proj, pass->projection_matrix, &box);
706 glUniform4f(shader.color, color->r, color->g, color->b, color->a);
707
708 glUniform2f(shader.size, box.width, box.height);
709 glUniform2f(shader.position, box.x, box.y);
710 glUniform2f(shader.clip_size, clipped_region_box->width, clipped_region_box->height);
711 glUniform2f(shader.clip_position, clipped_region_box->x, clipped_region_box->y);
712 uniform_corner_radii_set(&shader.clip_radius, clipped_region_corners);
713
714 struct fx_corner_fradii corners = fx_options->corners;
715 uniform_corner_radii_set(&shader.radius, &corners);
716 int fade_mode = fx_options->fade_inset % 10;
717 float fade_inset = (float)(fx_options->fade_inset / 10) / 100.0f;
718 glUniform1f(shader.fade_inset, fade_inset);
719 glUniform1i(shader.fade_mode, fade_mode);
720
721 render(&box, &clip_region, renderer->shaders.quad_round.pos_attrib);
722 pixman_region32_fini(&clip_region);
723
724 pop_fx_debug(renderer);
725 TRACY_BOTH_ZONES_END;
726 }
727
728 void fx_render_pass_add_rounded_rect_grad(struct fx_gles_render_pass *pass,
729 const struct fx_render_rounded_rect_grad_options *fx_options) {
730 const struct wlr_render_rect_options *options = &fx_options->base;
731
732 struct fx_renderer *renderer = pass->buffer->renderer;
733
734 if (renderer->shaders.quad_grad_round.max_len <= fx_options->gradient.count) {
735 glDeleteProgram(renderer->shaders.quad_grad_round.program);
736 if (!link_quad_grad_round_program(&renderer->shaders.quad_grad_round, fx_options->gradient.count + 1)) {
737 wlr_log(WLR_ERROR, "Could not link quad shader after updating max_len to %d. Aborting renderer", fx_options->gradient.count + 1);
738 abort();
739 }
740 }
741
742 struct wlr_box box;
743 struct wlr_buffer *wlr_buffer = pass->buffer->buffer;
744 wlr_render_rect_options_get_box(options, wlr_buffer, &box);
745
746 TRACY_BOTH_ZONES_START(renderer);
747 TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
748 TRACY_ZONE_TEXT_f("Corners (TL, TR, BL, BR): %f, %f, %f, %f",
749 fx_options->corners.top_left,
750 fx_options->corners.top_right,
751 fx_options->corners.bottom_left,
752 fx_options->corners.bottom_right);
753 TRACY_ZONE_TEXT_f("Gradient:");
754 TRACY_ZONE_TEXT_f("\tNum Colors: %d", fx_options->gradient.count);
755 TRACY_ZONE_TEXT_f("\tBlend: %d", fx_options->gradient.blend);
756 TRACY_ZONE_TEXT_f("\tDegree: %f", fx_options->gradient.degree);
757 TRACY_ZONE_TEXT_f("\tType: %s",
758 fx_options->gradient.linear == 1 ? "Linear"
759 : fx_options->gradient.linear == 2 ? "Conic"
760 : "Unknown");
761 TRACY_ZONE_TEXT_f("\tOrigin: %fx%f",
762 fx_options->gradient.origin[0], fx_options->gradient.origin[1]);
763 TRACY_ZONE_TEXT_f("\tRange (WxH, X, Y): %dx%d, %d, %d",
764 fx_options->gradient.range.width, fx_options->gradient.range.height,
765 fx_options->gradient.range.x, fx_options->gradient.range.y);
766 // TODO: Display Colors (not really sure how it works without a scene example...)
767 push_fx_debug(renderer);
768
769 setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
770
771 struct quad_grad_round_shader shader = renderer->shaders.quad_grad_round;
772 glUseProgram(shader.program);
773
774 set_proj_matrix(shader.proj, pass->projection_matrix, &box);
775
776 glUniform2f(shader.size, box.width, box.height);
777 glUniform2f(shader.position, box.x, box.y);
778
779 glUniform4fv(shader.colors, fx_options->gradient.count, (GLfloat*)fx_options->gradient.colors);
780 glUniform1i(shader.count, fx_options->gradient.count);
781 glUniform2f(shader.grad_size, fx_options->gradient.range.width, fx_options->gradient.range.height);
782 glUniform1f(shader.degree, fx_options->gradient.degree);
783 glUniform1f(shader.linear, fx_options->gradient.linear);
784 glUniform1f(shader.blend, fx_options->gradient.blend);
785 glUniform2f(shader.grad_box, fx_options->gradient.range.x, fx_options->gradient.range.y);
786 glUniform2f(shader.origin, fx_options->gradient.origin[0], fx_options->gradient.origin[1]);
787
788 struct fx_corner_fradii corners = fx_options->corners;
789 uniform_corner_radii_set(&shader.radius, &corners);
790
791 render(&box, options->clip, shader.pos_attrib);
792
793 pop_fx_debug(renderer);
794 TRACY_BOTH_ZONES_END;
795 }
796
797 void fx_render_pass_add_box_shadow(struct fx_gles_render_pass *pass,
798 const struct fx_render_box_shadow_options *options) {
799 struct fx_renderer *renderer = pass->buffer->renderer;
800
801 struct wlr_box box = options->box;
802 assert(box.width > 0 && box.height > 0);
803
804 pixman_region32_t clip_region;
805 if (options->clip) {
806 pixman_region32_init(&clip_region);
807 pixman_region32_copy(&clip_region, options->clip);
808 } else {
809 pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
810 }
811 const struct wlr_box clipped_region_box = options->clipped_region.area;
812 struct fx_corner_fradii clipped_region_corners = options->clipped_region.corners;
813 apply_clip_region(&clip_region, &clipped_region_box, &clipped_region_corners);
814
815 TRACY_BOTH_ZONES_START(renderer);
816 TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
817 TRACY_ZONE_TEXT_f("Clip Box (WxH, X, Y): %dx%d, %d, %d",
818 clipped_region_box.width, clipped_region_box.height,
819 clipped_region_box.x, clipped_region_box.y);
820 TRACY_ZONE_TEXT_f("Clip Box Corners (TL, TR, BL, BR): %f, %f, %f, %f",
821 clipped_region_corners.top_left,
822 clipped_region_corners.top_right,
823 clipped_region_corners.bottom_left,
824 clipped_region_corners.bottom_right);
825 TRACY_ZONE_TEXT_f("Shadow Options:");
826 TRACY_ZONE_TEXT_f("\tColor RGBA: %f, %f, %f, %f",
827 options->color.r, options->color.g, options->color.b, options->color.a);
828 TRACY_ZONE_TEXT_f("\tBlur Sigma: %f", options->blur_sigma);
829 push_fx_debug(renderer);
830
831 // blending will practically always be needed (unless we have a madman
832 // who uses opaque shadows with zero sigma), so just enable it
833 setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
834 glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
835
836 glUseProgram(renderer->shaders.box_shadow.program);
837
838 const struct wlr_render_color *color = &options->color;
839 set_proj_matrix(renderer->shaders.box_shadow.proj, pass->projection_matrix, &box);
840 glUniform4f(renderer->shaders.box_shadow.color, color->r, color->g, color->b, color->a);
841 glUniform1f(renderer->shaders.box_shadow.blur_sigma, options->blur_sigma);
842 glUniform2f(renderer->shaders.box_shadow.size, box.width, box.height);
843 glUniform2f(renderer->shaders.box_shadow.position, box.x, box.y);
844 glUniform1f(renderer->shaders.box_shadow.corner_radius, options->corner_radius);
845
846 uniform_corner_radii_set(&renderer->shaders.box_shadow.clip_radius, &clipped_region_corners);
847
848 glUniform2f(renderer->shaders.box_shadow.clip_position, clipped_region_box.x, clipped_region_box.y);
849 glUniform2f(renderer->shaders.box_shadow.clip_size, clipped_region_box.width, clipped_region_box.height);
850
851 render(&box, &clip_region, renderer->shaders.box_shadow.pos_attrib);
852 pixman_region32_fini(&clip_region);
853
854 glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
855
856 pop_fx_debug(renderer);
857 TRACY_BOTH_ZONES_END;
858 }
859
860 // Renders the blur for each damaged rect and swaps the buffer
861 static void render_blur_segments(struct fx_gles_render_pass *pass,
862 struct fx_render_blur_pass_options *fx_options, struct blur_shader* shader) {
863 struct fx_render_texture_options *tex_options = &fx_options->tex_options;
864 struct wlr_render_texture_options *options = &tex_options->base;
865 struct fx_renderer *renderer = pass->buffer->renderer;
866 struct blur_data *blur_data = fx_options->blur_data;
867
868 TRACY_BOTH_ZONES_START(renderer);
869 push_fx_debug(renderer);
870
871 // Swap fbo
872 if (fx_options->current_buffer == pass->fx_offscreen_buffers->effects_buffer) {
873 fx_framebuffer_bind(pass->fx_offscreen_buffers->effects_buffer_swapped);
874 } else {
875 fx_framebuffer_bind(pass->fx_offscreen_buffers->effects_buffer);
876 }
877
878 options->texture = fx_texture_from_buffer(&renderer->wlr_renderer,
879 fx_options->current_buffer->buffer);
880 struct fx_texture *texture = fx_get_texture(options->texture);
881
882 /*
883 * Render
884 */
885
886 struct wlr_box dst_box;
887 struct wlr_fbox src_fbox;
888 wlr_render_texture_options_get_src_box(options, &src_fbox);
889 wlr_render_texture_options_get_dst_box(options, &dst_box);
890 src_fbox.x /= options->texture->width;
891 src_fbox.y /= options->texture->height;
892 src_fbox.width /= options->texture->width;
893 src_fbox.height /= options->texture->height;
894
895 glDisable(GL_BLEND);
896 glDisable(GL_STENCIL_TEST);
897
898 glUseProgram(shader->program);
899
900 glActiveTexture(GL_TEXTURE0);
901 glBindTexture(texture->target, texture->tex);
902
903 switch (options->filter_mode) {
904 case WLR_SCALE_FILTER_BILINEAR:
905 glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
906 glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
907 break;
908 case WLR_SCALE_FILTER_NEAREST:
909 abort();
910 }
911
912 glUniform1i(shader->tex, 0);
913 glUniform1f(shader->radius, blur_data->radius);
914
915 if (shader == &renderer->shaders.blur1) {
916 glUniform2f(shader->halfpixel,
917 0.5f / (options->texture->width / 2.0f),
918 0.5f / (options->texture->height / 2.0f));
919 } else {
920 glUniform2f(shader->halfpixel,
921 0.5f / (options->texture->width * 2.0f),
922 0.5f / (options->texture->height * 2.0f));
923 }
924
925 set_proj_matrix(shader->proj, pass->projection_matrix, &dst_box);
926 set_tex_matrix(shader->tex_proj, options->transform, &src_fbox);
927
928 render(&dst_box, options->clip, shader->pos_attrib);
929
930 glBindTexture(texture->target, 0);
931 pop_fx_debug(renderer);
932 TRACY_BOTH_ZONES_END;
933
934 wlr_texture_destroy(options->texture);
935
936 // Swap buffer. We don't want to draw to the same buffer
937 if (fx_options->current_buffer != pass->fx_offscreen_buffers->effects_buffer) {
938 fx_options->current_buffer = pass->fx_offscreen_buffers->effects_buffer;
939 } else {
940 fx_options->current_buffer = pass->fx_offscreen_buffers->effects_buffer_swapped;
941 }
942 }
943
944 static void render_blur_effects(struct fx_gles_render_pass *pass,
945 struct fx_render_blur_pass_options *fx_options) {
946 struct fx_render_texture_options *tex_options = &fx_options->tex_options;
947 struct wlr_render_texture_options *options = &tex_options->base;
948 struct fx_renderer *renderer = pass->buffer->renderer;
949 struct blur_data *blur_data = fx_options->blur_data;
950 struct fx_texture *texture = fx_get_texture(options->texture);
951
952 struct blur_effects_shader shader = renderer->shaders.blur_effects;
953
954 struct wlr_box dst_box;
955 struct wlr_fbox src_fbox;
956 wlr_render_texture_options_get_src_box(options, &src_fbox);
957 wlr_render_texture_options_get_dst_box(options, &dst_box);
958
959 src_fbox.x /= options->texture->width;
960 src_fbox.y /= options->texture->height;
961 src_fbox.width /= options->texture->width;
962 src_fbox.height /= options->texture->height;
963
964 glDisable(GL_BLEND);
965 glDisable(GL_STENCIL_TEST);
966
967 TRACY_BOTH_ZONES_START(renderer);
968 push_fx_debug(renderer);
969
970 glUseProgram(shader.program);
971
972 glActiveTexture(GL_TEXTURE0);
973 glBindTexture(texture->target, texture->tex);
974
975 switch (options->filter_mode) {
976 case WLR_SCALE_FILTER_BILINEAR:
977 glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
978 glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
979 break;
980 case WLR_SCALE_FILTER_NEAREST:
981 abort();
982 }
983
984 glUniform1i(shader.tex, 0);
985 glUniform1f(shader.noise, blur_data->noise);
986 glUniform1f(shader.brightness, blur_data->brightness);
987 glUniform1f(shader.contrast, blur_data->contrast);
988 glUniform1f(shader.saturation, blur_data->saturation);
989
990 set_proj_matrix(shader.proj, pass->projection_matrix, &dst_box);
991 set_tex_matrix(shader.tex_proj, options->transform, &src_fbox);
992
993 render(&dst_box, options->clip, shader.pos_attrib);
994
995 glBindTexture(texture->target, 0);
996
997 pop_fx_debug(renderer);
998 TRACY_BOTH_ZONES_END;
999
1000 wlr_texture_destroy(options->texture);
1001 }
1002
1003 // Blurs the fx_options current_buffer content and returns the blurred framebuffer.
1004 // Returns NULL when the blur parameters reach 0.
1005 static struct fx_framebuffer *get_main_buffer_blur(struct fx_gles_render_pass *pass,
1006 struct fx_render_blur_pass_options *fx_options) {
1007 if (pass->fx_offscreen_buffers == NULL) {
1008 wlr_log(WLR_ERROR, "FX Pass offscreen buffers not initialized. Skipping getting blur...");
1009 return NULL;
1010 }
1011
1012 struct fx_renderer *renderer = pass->buffer->renderer;
1013 struct wlr_box buffer_bounds = {
1014 0, 0,
1015 fx_options->current_buffer->buffer->width, fx_options->current_buffer->buffer->height
1016 };
1017
1018 // We don't want to affect the reference blur_data
1019 struct blur_data blur_data = blur_data_apply_strength(fx_options->blur_data, fx_options->blur_strength);
1020 if (fx_options->blur_strength <= 0 || !is_scene_blur_enabled(&blur_data)) {
1021 return NULL;
1022 }
1023 fx_options->blur_data = &blur_data;
1024
1025 pixman_region32_t damage;
1026 pixman_region32_init(&damage);
1027 pixman_region32_copy(&damage, fx_options->tex_options.base.clip);
1028 wlr_region_transform(&damage, &damage, fx_options->tex_options.base.transform,
1029 buffer_bounds.width, buffer_bounds.height);
1030
1031 wlr_region_expand(&damage, &damage, blur_data_calc_size(&blur_data));
1032 // Make sure that the region doesn't expand past the buffer bounds
1033 pixman_region32_intersect_rect(&damage, &damage,
1034 0, 0, buffer_bounds.width, buffer_bounds.height);
1035
1036 // damage region will be scaled, make a temp
1037 pixman_region32_t scaled_damage;
1038 pixman_region32_init(&scaled_damage);
1039
1040 fx_options->tex_options.base.src_box = (struct wlr_fbox) {
1041 0, 0,
1042 buffer_bounds.width, buffer_bounds.height,
1043 };
1044 fx_options->tex_options.base.dst_box = buffer_bounds;
1045 // Clip the blur to the damage
1046 fx_options->tex_options.base.clip = &scaled_damage;
1047 // Artifacts with NEAREST filter
1048 fx_options->tex_options.base.filter_mode = WLR_SCALE_FILTER_BILINEAR;
1049
1050 TRACY_BOTH_ZONES_START(renderer);
1051 TRACY_ZONE_TEXT_f("dst_box (WxH, X, Y): %dx%d, %d, %d",
1052 fx_options->tex_options.base.dst_box.width,
1053 fx_options->tex_options.base.dst_box.height,
1054 fx_options->tex_options.base.dst_box.x,
1055 fx_options->tex_options.base.dst_box.y);
1056 TRACY_ZONE_TEXT_f("clip_box (WxH, X, Y): %dx%d, %d, %d",
1057 fx_options->tex_options.clip_box->width,
1058 fx_options->tex_options.clip_box->height,
1059 fx_options->tex_options.clip_box->x,
1060 fx_options->tex_options.clip_box->y);
1061 TRACY_ZONE_TEXT_f("Corners (TL, TR, BL, BR): %f, %f, %f, %f",
1062 fx_options->corners.top_left,
1063 fx_options->corners.top_right,
1064 fx_options->corners.bottom_left,
1065 fx_options->corners.bottom_right);
1066 TRACY_ZONE_TEXT_f("src_box (WxH, X, Y): %lfx%lf, %lf, %lf",
1067 fx_options->tex_options.base.src_box.width,
1068 fx_options->tex_options.base.src_box.height,
1069 fx_options->tex_options.base.src_box.x,
1070 fx_options->tex_options.base.src_box.y);
1071 TRACY_ZONE_TEXT_f("Ignore Transparent: %d", fx_options->ignore_transparent);
1072 TRACY_ZONE_TEXT_f("Discard Transparent: %d", fx_options->tex_options.discard_transparent);
1073 TRACY_ZONE_TEXT_f("Use Optimized Blur: %d", fx_options->use_optimized_blur);
1074 TRACY_ZONE_TEXT_f("Blur Options:");
1075 TRACY_ZONE_TEXT_f("\tNum Blur Passes: %d", fx_options->blur_data->num_passes);
1076 TRACY_ZONE_TEXT_f("\tBlur Radius: %f", fx_options->blur_data->radius);
1077 TRACY_ZONE_TEXT_f("\tBrightness: %f", fx_options->blur_data->brightness);
1078 TRACY_ZONE_TEXT_f("\tContrast: %f", fx_options->blur_data->contrast);
1079 TRACY_ZONE_TEXT_f("\tNoise: %f", fx_options->blur_data->noise);
1080 TRACY_ZONE_TEXT_f("\tSaturation: %f", fx_options->blur_data->saturation);
1081 push_fx_debug(renderer);
1082
1083 // Downscale
1084 for (int i = 0; i < blur_data.num_passes; ++i) {
1085 wlr_region_scale(&scaled_damage, &damage, 1.0f / (1 << (i + 1)));
1086 render_blur_segments(pass, fx_options, &renderer->shaders.blur1);
1087 }
1088
1089 // Upscale
1090 for (int i = blur_data.num_passes - 1; i >= 0; --i) {
1091 // when upsampling we make the region twice as big
1092 wlr_region_scale(&scaled_damage, &damage, 1.0f / (1 << i));
1093 render_blur_segments(pass, fx_options, &renderer->shaders.blur2);
1094 }
1095
1096 pixman_region32_fini(&scaled_damage);
1097
1098 // Render additional blur effects like saturation, noise, contrast, etc...
1099 if (blur_data_should_parameters_blur_effects(&blur_data)
1100 && pixman_region32_not_empty(&damage)) {
1101 if (fx_options->current_buffer == pass->fx_offscreen_buffers->effects_buffer) {
1102 fx_framebuffer_bind(pass->fx_offscreen_buffers->effects_buffer_swapped);
1103 } else {
1104 fx_framebuffer_bind(pass->fx_offscreen_buffers->effects_buffer);
1105 }
1106 fx_options->tex_options.base.clip = &damage;
1107 fx_options->tex_options.base.texture = fx_texture_from_buffer(
1108 &renderer->wlr_renderer, fx_options->current_buffer->buffer);
1109 render_blur_effects(pass, fx_options);
1110 if (fx_options->current_buffer != pass->fx_offscreen_buffers->effects_buffer) {
1111 fx_options->current_buffer = pass->fx_offscreen_buffers->effects_buffer;
1112 } else {
1113 fx_options->current_buffer = pass->fx_offscreen_buffers->effects_buffer_swapped;
1114 }
1115 }
1116
1117 pixman_region32_fini(&damage);
1118
1119 // Bind back to the default buffer
1120 fx_framebuffer_bind(pass->buffer);
1121
1122 pop_fx_debug(renderer);
1123 TRACY_BOTH_ZONES_END;
1124
1125 return fx_options->current_buffer;
1126 }
1127
1128 void fx_render_pass_add_frame(struct fx_gles_render_pass *pass,
1129 const struct fx_render_frame_options *options) {
1130 struct fx_renderer *renderer = pass->buffer->renderer;
1131
1132 struct wlr_box box = options->box;
1133 assert(box.width > 0 && box.height > 0);
1134
1135 pixman_region32_t clip_region;
1136 if (options->clip) {
1137 pixman_region32_init(&clip_region);
1138 pixman_region32_copy(&clip_region, options->clip);
1139 } else {
1140 pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
1141 }
1142
1143 push_fx_debug(renderer);
1144
1145 // Premultiplied source-over: the shader emits colour already scaled by
1146 // its coverage, the same convention the bevel pass settled on.
1147 setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
1148 glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
1149
1150 glUseProgram(renderer->shaders.frame.program);
1151
1152 const struct wlr_render_color *color = &options->color;
1153 set_proj_matrix(renderer->shaders.frame.proj, pass->projection_matrix, &box);
1154 glUniform4f(renderer->shaders.frame.color, color->r, color->g, color->b, color->a);
1155 glUniform2f(renderer->shaders.frame.size, box.width, box.height);
1156 glUniform2f(renderer->shaders.frame.position, box.x, box.y);
1157 glUniform1f(renderer->shaders.frame.corner_radius, options->corner_radius);
1158 glUniform1f(renderer->shaders.frame.corner_shape, fx_corner_shape());
1159 glUniform1f(renderer->shaders.frame.band, options->band);
1160 glUniform1f(renderer->shaders.frame.band_min, options->band_min);
1161 glUniform1f(renderer->shaders.frame.corner_len, options->corner_len);
1162 glUniform1f(renderer->shaders.frame.gap, options->gap);
1163 glUniform1f(renderer->shaders.frame.hovered, options->hovered);
1164 glUniform1f(renderer->shaders.frame.swell_curve, options->swell_curve);
1165 glUniform1f(renderer->shaders.frame.bulge, options->bulge);
1166 glUniform4f(renderer->shaders.frame.exclusion, options->exclusion[0],
1167 options->exclusion[1], options->exclusion[2], options->exclusion[3]);
1168 glUniform4f(renderer->shaders.frame.hover_color,
1169 options->hover_color[0], options->hover_color[1],
1170 options->hover_color[2], options->hover_color[3]);
1171
1172 render(&box, &clip_region, renderer->shaders.frame.pos_attrib);
1173 pixman_region32_fini(&clip_region);
1174
1175 pop_fx_debug(renderer);
1176 }
1177
1178 void fx_render_pass_add_bevel(struct fx_gles_render_pass *pass,
1179 const struct fx_render_bevel_options *options) {
1180 struct fx_renderer *renderer = pass->buffer->renderer;
1181
1182 struct wlr_box box = options->box;
1183 assert(box.width > 0 && box.height > 0);
1184
1185 pixman_region32_t clip_region;
1186 if (options->clip) {
1187 pixman_region32_init(&clip_region);
1188 pixman_region32_copy(&clip_region, options->clip);
1189 } else {
1190 pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
1191 }
1192
1193 push_fx_debug(renderer);
1194
1195 // The highlight adds light and the shade subtracts it, both premultiplied
1196 // into the same draw — ordinary source-over blending. The src factor is
1197 // GL_ONE because the shader emits premultiplied colour: with GL_SRC_ALPHA
1198 // the blend multiplied by alpha a SECOND time, squaring the rim's
1199 // intensity, and left no way for a branch to composite additively (alpha
1200 // 0 zeroed the whole contribution instead of adding it).
1201 setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
1202 glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
1203
1204 glUseProgram(renderer->shaders.bevel.program);
1205
1206 const struct wlr_render_color *color = &options->color;
1207 set_proj_matrix(renderer->shaders.bevel.proj, pass->projection_matrix, &box);
1208 glUniform4f(renderer->shaders.bevel.color, color->r, color->g, color->b, color->a);
1209 glUniform2f(renderer->shaders.bevel.size, box.width, box.height);
1210 glUniform2f(renderer->shaders.bevel.position, box.x, box.y);
1211 glUniform1f(renderer->shaders.bevel.corner_radius, options->corner_radius);
1212 // The radius the compositor sends is span-widened for this exponent, so
1213 // the rim has to read it as the same superellipse the other cuts do.
1214 glUniform1f(renderer->shaders.bevel.corner_shape, fx_corner_shape());
1215 glUniform1f(renderer->shaders.bevel.thickness, options->thickness);
1216 glUniform2f(renderer->shaders.bevel.light_dir,
1217 options->light_dir[0], options->light_dir[1]);
1218 glUniform1f(renderer->shaders.bevel.light_intensity, options->light_intensity);
1219 glUniform1f(renderer->shaders.bevel.shade_intensity, options->shade_intensity);
1220 glUniform1f(renderer->shaders.bevel.shoulder, options->shoulder);
1221 glUniform1f(renderer->shaders.bevel.focus, options->focus);
1222 glUniform3f(renderer->shaders.bevel.focus_color,
1223 options->focus_color[0], options->focus_color[1], options->focus_color[2]);
1224 glUniform1f(renderer->shaders.bevel.focus_sharpness, options->focus_sharpness);
1225
1226 render(&box, &clip_region, renderer->shaders.bevel.pos_attrib);
1227 pixman_region32_fini(&clip_region);
1228
1229 glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
1230
1231 pop_fx_debug(renderer);
1232 }
1233
1234 void fx_render_pass_add_droplet(struct fx_gles_render_pass *pass,
1235 const struct fx_render_droplet_options *options) {
1236 struct fx_renderer *renderer = pass->buffer->renderer;
1237
1238 struct wlr_box box = options->box;
1239 assert(box.width > 0 && box.height > 0);
1240
1241 // The lens samples the FRAME-SO-FAR — everything already rendered below
1242 // this node (desktop, windows, all of it), copied 1:1 into the saved-
1243 // pixels buffer just before the draw, because GL cannot sample the
1244 // framebuffer it is rendering into. The optimized-blur bake is NOT used:
1245 // it captures bottom layers only (that's the DE's frost design), so a
1246 // drop over a window would refract the desktop hiding behind that
1247 // window instead of the window itself. Without offscreen buffers there
1248 // is nothing to refract; the client's own translucent drop still
1249 // renders, so skipping is a graceful degrade.
1250 if (pass->fx_offscreen_buffers == NULL) {
1251 return;
1252 }
1253 struct fx_framebuffer *snap = pass->fx_offscreen_buffers->blur_saved_pixels_buffer;
1254 if (snap == NULL || snap->buffer == NULL) {
1255 return;
1256 }
1257
1258 // Copy the drop's neighborhood: the box grown by the refraction reach
1259 // and the inverted ghost's sampling span (0.35 x the box about its
1260 // center), clamped to the buffer.
1261 int margin = (int)(options->refr + 1.0f
1262 + 0.35f * (float)(box.width > box.height ? box.width : box.height));
1263 pixman_region32_t copy_region;
1264 pixman_region32_init_rect(©_region,
1265 box.x - margin, box.y - margin,
1266 box.width + 2 * margin, box.height + 2 * margin);
1267 pixman_region32_intersect_rect(©_region, ©_region,
1268 0, 0, pass->buffer->buffer->width, pass->buffer->buffer->height);
1269 fx_render_pass_read_to_buffer(pass, ©_region, snap, pass->buffer);
1270 pixman_region32_fini(©_region);
1271
1272 struct wlr_texture *wlr_texture =
1273 fx_texture_from_buffer(&renderer->wlr_renderer, snap->buffer);
1274 if (wlr_texture == NULL) {
1275 return;
1276 }
1277 struct fx_texture *texture = fx_get_texture(wlr_texture);
1278
1279 pixman_region32_t clip_region;
1280 if (options->clip) {
1281 pixman_region32_init(&clip_region);
1282 pixman_region32_copy(&clip_region, options->clip);
1283 } else {
1284 pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
1285 }
1286
1287 push_fx_debug(renderer);
1288
1289 // The refracted backdrop is opaque inside the silhouette (it REPLACES
1290 // what the drop covers); the feathered rim blends source-over.
1291 setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
1292
1293 struct droplet_shader *shader = &renderer->shaders.droplet;
1294 glUseProgram(shader->program);
1295
1296 glActiveTexture(GL_TEXTURE0);
1297 glBindTexture(texture->target, texture->tex);
1298 glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1299 glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1300
1301 set_proj_matrix(shader->proj, pass->projection_matrix, &box);
1302 glUniform1i(shader->tex, 0);
1303 glUniform2f(shader->tex_size, snap->buffer->width, snap->buffer->height);
1304 glUniform2f(shader->position, box.x, box.y);
1305 glUniform2f(shader->size, box.width, box.height);
1306 glUniform1f(shader->attach_r, options->attach_r);
1307 glUniform1f(shader->sheet_r, options->sheet_r);
1308 glUniform1f(shader->bow_rise, options->bow_rise);
1309 glUniform1f(shader->blend_k, options->blend_k);
1310 glUniform1f(shader->curve, options->curve);
1311 glUniform1f(shader->band_px, options->band_px);
1312 glUniform1f(shader->refr, options->refr);
1313 glUniform1f(shader->ghost, options->ghost);
1314
1315 render(&box, &clip_region, shader->pos_attrib);
1316
1317 glBindTexture(texture->target, 0);
1318 pixman_region32_fini(&clip_region);
1319 wlr_texture_destroy(wlr_texture);
1320
1321 pop_fx_debug(renderer);
1322 }
1323
1324 void fx_render_pass_add_blur(struct fx_gles_render_pass *pass,
1325 struct fx_render_blur_pass_options *fx_options) {
1326 if (pass->fx_offscreen_buffers == NULL) {
1327 wlr_log(WLR_ERROR, "FX Pass offscreen buffers not initialized. Skipping blur...");
1328 return;
1329 }
1330
1331 struct fx_renderer *renderer = pass->buffer->renderer;
1332 struct fx_render_texture_options *tex_options = &fx_options->tex_options;
1333
1334 TRACY_BOTH_ZONES_START(renderer);
1335 push_fx_debug(renderer);
1336
1337 // NOTE: `optimized` here is the flag saying this node SAMPLES the cached
1338 // buffer — it is not a re-bake. The bake is add_optimized_blur, below.
1339 if (cce_blur_debug()) {
1340 wlr_log(WLR_INFO, "[scenefx] add_blur dst_box: %dx%d at (%d, %d), optimized: %d strength: %f",
1341 tex_options->base.dst_box.width, tex_options->base.dst_box.height,
1342 tex_options->base.dst_box.x, tex_options->base.dst_box.y,
1343 fx_options->use_optimized_blur, fx_options->blur_strength);
1344 }
1345
1346 const bool has_strength = fx_options->blur_strength < 1.0;
1347 struct fx_framebuffer *buffer = pass->fx_offscreen_buffers->optimized_blur_buffer;
1348 TRACY_ZONE_TEXT_f("Use Optimized Blur: %d", fx_options->use_optimized_blur);
1349 TRACY_ZONE_TEXT_f("Optimized Blur Successfully Used: %d",
1350 buffer && fx_options->use_optimized_blur);
1351 if (!fx_options->use_optimized_blur || has_strength) {
1352 // Render the blur into its own buffer (a reduced-strength blur of
1353 // an optimized node too: the cache holds only the full-strength
1354 // bake, at its node's anchor).
1355 struct fx_render_blur_pass_options blur_options = *fx_options;
1356 blur_options.current_buffer = pass->buffer;
1357 buffer = get_main_buffer_blur(pass, &blur_options);
1358 }
1359 if (!buffer) {
1360 goto finish;
1361 }
1362 struct wlr_texture *wlr_texture =
1363 fx_texture_from_buffer(&renderer->wlr_renderer, buffer->buffer);
1364 struct fx_texture *blur_texture = fx_get_texture(wlr_texture);
1365
1366 // Get a stencil of the window ignoring transparent regions
1367 if (fx_options->ignore_transparent && fx_options->tex_options.base.texture) {
1368 stencil_mask_init();
1369
1370 struct fx_render_texture_options tex_options = fx_options->tex_options;
1371 tex_options.discard_transparent = true;
1372 tex_options.clipped_region = fx_options->clipped_region;
1373 fx_render_pass_add_texture(pass, &tex_options);
1374
1375 stencil_mask_close(true);
1376 }
1377
1378 // Draw the blurred texture. The shared cache (optimized, full strength)
1379 // is drawn so that screen pixel s reads cache pixel s + shift — the
1380 // node's bake at its anchor, inside the margin; a freshly blurred
1381 // buffer is output-sized and in place.
1382 const bool direct_cache = fx_options->use_optimized_blur && !has_strength;
1383 tex_options->base.dst_box = (struct wlr_box) {
1384 .x = direct_cache ? -fx_options->cache_shift_x : 0,
1385 .y = direct_cache ? -fx_options->cache_shift_y : 0,
1386 .width = buffer->buffer->width,
1387 .height = buffer->buffer->height,
1388 };
1389 tex_options->base.src_box = (struct wlr_fbox) {
1390 .x = 0,
1391 .y = 0,
1392 .width = buffer->buffer->width,
1393 .height = buffer->buffer->height,
1394 };
1395 tex_options->base.texture = &blur_texture->wlr_texture;
1396 // since we're capturing from the fbo, transform will always be normal
1397 tex_options->base.transform = WL_OUTPUT_TRANSFORM_NORMAL;
1398 tex_options->clipped_region = fx_options->clipped_region;
1399 fx_render_pass_add_texture(pass, tex_options);
1400
1401 wlr_texture_destroy(&blur_texture->wlr_texture);
1402
1403 // Finish stenciling
1404 if (fx_options->ignore_transparent && fx_options->tex_options.base.texture) {
1405 stencil_mask_fini();
1406 }
1407
1408 finish:
1409 pop_fx_debug(renderer);
1410 TRACY_BOTH_ZONES_END;
1411 }
1412
1413 bool fx_render_pass_add_optimized_blur(struct fx_gles_render_pass *pass,
1414 struct fx_render_blur_pass_options *fx_options) {
1415 if (pass->fx_offscreen_buffers == NULL) {
1416 wlr_log(WLR_ERROR, "FX Pass offscreen buffers not initialized. Skipping optimized blur...");
1417 return false;
1418 }
1419 struct fx_renderer *renderer = pass->buffer->renderer;
1420 struct wlr_box dst_box = fx_options->tex_options.base.dst_box;
1421
1422 TRACY_BOTH_ZONES_START(renderer);
1423 TRACY_ZONE_TEXT_f("dst_box (WxH, X, Y): %dx%d, %d, %d",
1424 fx_options->tex_options.base.dst_box.width,
1425 fx_options->tex_options.base.dst_box.height,
1426 fx_options->tex_options.base.dst_box.x,
1427 fx_options->tex_options.base.dst_box.y);
1428 TRACY_ZONE_TEXT_f("clip_box (WxH, X, Y): %dx%d, %d, %d",
1429 fx_options->tex_options.clip_box->width,
1430 fx_options->tex_options.clip_box->height,
1431 fx_options->tex_options.clip_box->x,
1432 fx_options->tex_options.clip_box->y);
1433 TRACY_ZONE_TEXT_f("src_box (WxH, X, Y): %lfx%lf, %lf, %lf",
1434 fx_options->tex_options.base.src_box.width,
1435 fx_options->tex_options.base.src_box.height,
1436 fx_options->tex_options.base.src_box.x,
1437 fx_options->tex_options.base.src_box.y);
1438 TRACY_ZONE_TEXT_f("Ignore Transparent: %d", fx_options->ignore_transparent);
1439 TRACY_ZONE_TEXT_f("Discard Transparent: %d", fx_options->tex_options.discard_transparent);
1440 TRACY_ZONE_TEXT_f("Use Optimized Blur: %d", fx_options->use_optimized_blur);
1441 TRACY_ZONE_TEXT_f("Blur Options:");
1442 TRACY_ZONE_TEXT_f("\tNum Blur Passes: %d", fx_options->blur_data->num_passes);
1443 TRACY_ZONE_TEXT_f("\tBlur Radius: %f", fx_options->blur_data->radius);
1444 TRACY_ZONE_TEXT_f("\tBrightness: %f", fx_options->blur_data->brightness);
1445 TRACY_ZONE_TEXT_f("\tContrast: %f", fx_options->blur_data->contrast);
1446 TRACY_ZONE_TEXT_f("\tNoise: %f", fx_options->blur_data->noise);
1447 TRACY_ZONE_TEXT_f("\tSaturation: %f", fx_options->blur_data->saturation);
1448 push_fx_debug(renderer);
1449
1450 // The actual cache bake (a whole node, or a strip it newly exposes).
1451 if (cce_blur_debug()) {
1452 wlr_log(WLR_INFO, "[scenefx] add_optimized_blur dst_box: %dx%d at (%d, %d) shift (%d, %d)",
1453 dst_box.width, dst_box.height, dst_box.x, dst_box.y,
1454 fx_options->cache_shift_x, fx_options->cache_shift_y);
1455 }
1456
1457 pixman_region32_t clip;
1458 pixman_region32_init_rect(&clip,
1459 dst_box.x, dst_box.y, dst_box.width, dst_box.height);
1460
1461 // Render the blur into its own buffer
1462 struct fx_render_blur_pass_options blur_options = *fx_options;
1463 blur_options.current_buffer = pass->buffer;
1464 blur_options.tex_options.base.clip = &clip;
1465 struct fx_framebuffer *fx_buffer = get_main_buffer_blur(pass, &blur_options);
1466 if (fx_buffer != NULL) {
1467 // Land the newly blurred content in the cache at the node's anchor.
1468 read_to_buffer_offset(pass, &clip,
1469 pass->fx_offscreen_buffers->optimized_blur_buffer, fx_buffer,
1470 fx_options->cache_shift_x, fx_options->cache_shift_y);
1471 }
1472
1473 pixman_region32_fini(&clip);
1474
1475 pop_fx_debug(renderer);
1476 TRACY_BOTH_ZONES_END;
1477 return fx_buffer != NULL;
1478 }
1479
1480 /* Copy `_region` of src_buffer into dst_buffer, landing at (+ox, +oy). */
1481 static void read_to_buffer_offset(struct fx_gles_render_pass *pass,
1482 pixman_region32_t *_region, struct fx_framebuffer *dst_buffer,
1483 struct fx_framebuffer *src_buffer, int ox, int oy) {
1484 if (!_region || !pixman_region32_not_empty(_region)) {
1485 return;
1486 }
1487 TRACY_BOTH_ZONES_START(pass->buffer->renderer);
1488
1489 pixman_region32_t region;
1490 pixman_region32_init(®ion);
1491 pixman_region32_copy(®ion, _region);
1492 pixman_region32_translate(®ion, ox, oy);
1493
1494 struct wlr_texture *src_tex =
1495 fx_texture_from_buffer(&pass->buffer->renderer->wlr_renderer, src_buffer->buffer);
1496 if (src_tex == NULL) {
1497 goto done;
1498 }
1499
1500 // Draw onto the dst_buffer. The pass's projection and viewport are the
1501 // output's; a larger destination (the margin cache) needs its own for
1502 // the duration of the copy, or everything past the output's extent is
1503 // clipped away.
1504 const int dst_w = dst_buffer->buffer->width;
1505 const int dst_h = dst_buffer->buffer->height;
1506 const int pass_w = pass->buffer->buffer->width;
1507 const int pass_h = pass->buffer->buffer->height;
1508 const bool resize = dst_w != pass_w || dst_h != pass_h;
1509 float saved_proj[9];
1510 if (resize) {
1511 memcpy(saved_proj, pass->projection_matrix, sizeof(saved_proj));
1512 matrix_projection(pass->projection_matrix, dst_w, dst_h, WL_OUTPUT_TRANSFORM_FLIPPED_180);
1513 }
1514 fx_framebuffer_bind(dst_buffer);
1515 if (resize) {
1516 glViewport(0, 0, dst_w, dst_h);
1517 }
1518 wlr_render_pass_add_texture(&pass->base, &(struct wlr_render_texture_options) {
1519 .texture = src_tex,
1520 .clip = ®ion,
1521 .transform = WL_OUTPUT_TRANSFORM_NORMAL,
1522 .blend_mode = WLR_RENDER_BLEND_MODE_NONE,
1523 .dst_box = (struct wlr_box){
1524 .x = ox,
1525 .y = oy,
1526 .width = src_buffer->buffer->width,
1527 .height = src_buffer->buffer->height,
1528 },
1529 .src_box = (struct wlr_fbox){
1530 .x = 0,
1531 .y = 0,
1532 .width = src_buffer->buffer->width,
1533 .height = src_buffer->buffer->height,
1534 },
1535 });
1536 wlr_texture_destroy(src_tex);
1537
1538 // Bind back to the main WLR buffer
1539 fx_framebuffer_bind(pass->buffer);
1540 if (resize) {
1541 memcpy(pass->projection_matrix, saved_proj, sizeof(saved_proj));
1542 glViewport(0, 0, pass_w, pass_h);
1543 }
1544
1545 done:
1546 TRACY_BOTH_ZONES_END;
1547
1548 pixman_region32_fini(®ion);
1549 }
1550
1551 void fx_render_pass_read_to_buffer(struct fx_gles_render_pass *pass,
1552 pixman_region32_t *_region, struct fx_framebuffer *dst_buffer,
1553 struct fx_framebuffer *src_buffer) {
1554 read_to_buffer_offset(pass, _region, dst_buffer, src_buffer, 0, 0);
1555 }
1556
1557 static const char *reset_status_str(GLenum status) {
1558 switch (status) {
1559 case GL_GUILTY_CONTEXT_RESET_KHR:
1560 return "guilty";
1561 case GL_INNOCENT_CONTEXT_RESET_KHR:
1562 return "innocent";
1563 case GL_UNKNOWN_CONTEXT_RESET_KHR:
1564 return "unknown";
1565 default:
1566 return "<invalid>";
1567 }
1568 }
1569
1570 struct fx_gles_render_pass *fx_begin_buffer_pass(struct fx_framebuffer *buffer,
1571 struct wlr_egl_context *prev_ctx, struct fx_render_timer *timer,
1572 struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point) {
1573 struct fx_renderer *renderer = buffer->renderer;
1574 struct wlr_buffer *wlr_buffer = buffer->buffer;
1575
1576 if (renderer->procs.glGetGraphicsResetStatusKHR) {
1577 GLenum status = renderer->procs.glGetGraphicsResetStatusKHR();
1578 if (status != GL_NO_ERROR) {
1579 wlr_log(WLR_ERROR, "GPU reset (%s)", reset_status_str(status));
1580 wl_signal_emit_mutable(&renderer->wlr_renderer.events.lost, NULL);
1581 return NULL;
1582 }
1583 }
1584
1585 GLint fbo = fx_framebuffer_get_fbo(buffer);
1586 if (!fbo) {
1587 return NULL;
1588 }
1589
1590 struct fx_gles_render_pass *pass = calloc(1, sizeof(*pass));
1591 if (pass == NULL) {
1592 return NULL;
1593 }
1594
1595 wlr_render_pass_init(&pass->base, &render_pass_impl);
1596 wlr_buffer_lock(wlr_buffer);
1597 pass->buffer = buffer;
1598 pass->timer = timer;
1599 pass->prev_ctx = *prev_ctx;
1600 if (signal_timeline != NULL) {
1601 pass->signal_timeline = wlr_drm_syncobj_timeline_ref(signal_timeline);
1602 pass->signal_point = signal_point;
1603 }
1604
1605 pass->fx_offscreen_buffers = NULL;
1606 pixman_region32_init(&pass->blur_padding_region);
1607 pass->has_blur = false;
1608
1609 matrix_projection(pass->projection_matrix, wlr_buffer->width, wlr_buffer->height,
1610 WL_OUTPUT_TRANSFORM_FLIPPED_180);
1611
1612 push_fx_debug(renderer);
1613 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1614
1615 glViewport(0, 0, wlr_buffer->width, wlr_buffer->height);
1616 glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
1617 glDisable(GL_SCISSOR_TEST);
1618
1619 pop_fx_debug(renderer);
1620 return pass;
1621 }