git.lucas.co / cce-compositor
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(&region, box->x, box->y, box->width, box->height);
 264 
 265 	if (clip) {
 266 		pixman_region32_intersect(&region, &region, clip);
 267 	}
 268 
 269 	int rects_len;
 270 	const pixman_box32_t *rects = pixman_region32_rectangles(&region, &rects_len);
 271 	if (rects_len == 0) {
 272 		pixman_region32_fini(&region);
 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(&region);
 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(&copy_region,
1269 			box.x - margin, box.y - margin,
1270 			box.width + 2 * margin, box.height + 2 * margin);
1271 	pixman_region32_intersect_rect(&copy_region, &copy_region,
1272 			0, 0, pass->buffer->buffer->width, pass->buffer->buffer->height);
1273 	fx_render_pass_read_to_buffer(pass, &copy_region, snap, pass->buffer);
1274 	pixman_region32_fini(&copy_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(&region);
1500 	pixman_region32_copy(&region, _region);
1501 	pixman_region32_translate(&region, 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 = &region,
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(&region);
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 }