commit e0f9c78

shrub  ·  2026-09-02 11:40:13 +0000 UTC
parent de1782b
optimize decor rendering and transparency generally
3 files changed,  +201, -62
+97, -14
  1@@ -251,6 +251,66 @@ error0:
  2 
  3 /* Rendering {{{ */
  4 
  5+/* check the client's window buffer is opaque */
  6+static bool
  7+view_buffer_is_opaque(struct compositor_view *view)
  8+{
  9+	const struct swc_rectangle *geom = &view->base.geometry;
 10+	struct wld_buffer *buffer = view->base.buffer;
 11+	uint32_t x1, y1, width, height;
 12+
 13+	if (view->buffer_opaque_valid) {
 14+		return view->buffer_opaque;
 15+	}
 16+
 17+	view->buffer_opaque_valid = true;
 18+	view->buffer_opaque = false;
 19+	if (!buffer || buffer->format != WLD_FORMAT_ARGB8888) {
 20+		return false;
 21+	}
 22+
 23+	if (view->window) {
 24+		if (view->buffer_offset_x < 0 || view->buffer_offset_y < 0) {
 25+			return false;
 26+		}
 27+		x1 = (uint32_t)view->buffer_offset_x;
 28+		y1 = (uint32_t)view->buffer_offset_y;
 29+		width = geom->width;
 30+		height = geom->height;
 31+	} else {
 32+		x1 = 0;
 33+		y1 = 0;
 34+		width = buffer->width;
 35+		height = buffer->height;
 36+	}
 37+
 38+	if (x1 > buffer->width || y1 > buffer->height ||
 39+	    width > buffer->width - x1 || height > buffer->height - y1 ||
 40+	    !wld_map(buffer)) {
 41+		return false;
 42+	}
 43+
 44+	view->buffer_opaque = true;
 45+	for (uint32_t y = 0; y < height && view->buffer_opaque; ++y) {
 46+		const uint8_t *row = (const uint8_t *)buffer->map +
 47+		                     (size_t)(y1 + y) * buffer->pitch +
 48+		                     (size_t)x1 * 4;
 49+
 50+		for (uint32_t x = 0; x < width; ++x) {
 51+			uint32_t pixel;
 52+
 53+			memcpy(&pixel, row + (size_t)x * 4, sizeof(pixel));
 54+			if ((pixel >> 24) != 0xff) {
 55+				view->buffer_opaque = false;
 56+				break;
 57+			}
 58+		}
 59+	}
 60+	wld_unmap(buffer);
 61+
 62+	return view->buffer_opaque;
 63+}
 64+
 65 static void
 66 repaint_view(struct target *target, struct compositor_view *view,
 67              pixman_region32_t *damage)
 68@@ -313,12 +373,19 @@ repaint_view(struct target *target, struct compositor_view *view,
 69 			pixman_region32_subtract(&blend_damage, &buffer_damage,
 70 			                         &opaque_damage);
 71 
 72-			wld_copy_region(swc.backend->renderer, view->buffer,
 73-			                buf_x - target_geom->x, buf_y - target_geom->y,
 74-			                &opaque_damage);
 75-			wld_blend_region(swc.backend->renderer, view->buffer,
 76-			                 buf_x - target_geom->x, buf_y - target_geom->y,
 77-			                 &blend_damage);
 78+			if (!pixman_region32_not_empty(&blend_damage) ||
 79+			    view_buffer_is_opaque(view)) {
 80+				wld_copy_region(swc.backend->renderer, view->buffer,
 81+				                buf_x - target_geom->x,
 82+				                buf_y - target_geom->y, &buffer_damage);
 83+			} else {
 84+				wld_copy_region(swc.backend->renderer, view->buffer,
 85+				                buf_x - target_geom->x,
 86+				                buf_y - target_geom->y, &opaque_damage);
 87+				wld_blend_region(swc.backend->renderer, view->buffer,
 88+				                 buf_x - target_geom->x,
 89+				                 buf_y - target_geom->y, &blend_damage);
 90+			}
 91 
 92 			pixman_region32_fini(&blend_damage);
 93 			pixman_region32_fini(&opaque_damage);
 94@@ -460,23 +527,34 @@ static int
 95 renderer_attach(struct compositor_view *view, struct wld_buffer *client_buffer)
 96 {
 97 	struct wld_buffer *buffer;
 98+	uint32_t proxy_width, proxy_height;
 99 	bool was_proxy = view->buffer != view->base.buffer;
100 	bool needs_proxy =
101 	    client_buffer && !(wld_capabilities(swc.backend->renderer, client_buffer) &
102 	                       WLD_CAPABILITY_READ);
103-	bool resized = view->buffer && client_buffer &&
104-	               (view->buffer->width != client_buffer->width ||
105-	                view->buffer->height != client_buffer->height);
106+	bool proxy_incompatible =
107+	    view->buffer && client_buffer &&
108+	    (view->buffer->format != client_buffer->format ||
109+	     view->buffer->width < client_buffer->width ||
110+	     view->buffer->height < client_buffer->height);
111 
112 	if (client_buffer) {
113 		/* Create a proxy buffer if necessary (for example a hardware buffer
114 		 * backing a SHM buffer). */
115 		if (needs_proxy) {
116-			if (!was_proxy || resized) {
117+			if (!was_proxy || proxy_incompatible) {
118 				DEBUG("Creating a proxy buffer\n");
119+				proxy_width = client_buffer->width;
120+				proxy_height = client_buffer->height;
121+				if (proxy_width <= UINT32_MAX - 255) {
122+					proxy_width = (proxy_width + 255) & ~255U;
123+				}
124+				if (proxy_height <= UINT32_MAX - 255) {
125+					proxy_height = (proxy_height + 255) & ~255U;
126+				}
127 				buffer = wld_create_buffer(
128-				    swc.backend->context, client_buffer->width,
129-				    client_buffer->height, client_buffer->format, WLD_FLAG_MAP);
130+				    swc.backend->context, proxy_width, proxy_height,
131+				    client_buffer->format, WLD_FLAG_MAP);
132 
133 				if (!buffer) {
134 					return -ENOMEM;
135@@ -495,11 +573,13 @@ renderer_attach(struct compositor_view *view, struct wld_buffer *client_buffer)
136 	/* If we no longer need a proxy buffer, or the original buffer is of a
137 	 * different size, destroy the old proxy image. */
138 	if (view->buffer &&
139-	    ((!needs_proxy && was_proxy) || (needs_proxy && resized))) {
140+	    ((!needs_proxy && was_proxy) ||
141+	     (needs_proxy && was_proxy && proxy_incompatible))) {
142 		wld_buffer_unreference(view->buffer);
143 	}
144 
145 	view->buffer = buffer;
146+	view->buffer_opaque_valid = false;
147 
148 	return 0;
149 }
150@@ -1032,7 +1112,7 @@ move(struct view *base, int32_t x, int32_t y)
151 		if (view->visible) {
152 			/* Assume worst-case no clipping until we draw the next frame (in
153 			 * case the surface gets moved again before that). */
154-			pixman_region32_init(&view->clip);
155+			pixman_region32_clear(&view->clip);
156 
157 			view_update_screens(&view->base);
158 			damage_below_view(view);
159@@ -1355,6 +1435,8 @@ compositor_create_view(struct surface *surface)
160 	view_initialize(&view->base, &view_impl);
161 	view->surface = surface;
162 	view->buffer = NULL;
163+	view->buffer_opaque_valid = false;
164+	view->buffer_opaque = false;
165 	view->window = NULL;
166 	view->parent = NULL;
167 	view->buffer_offset_x = 0;
168@@ -1613,6 +1695,7 @@ calculate_damage(void)
169 		surface_damage = &view->surface->state.damage;
170 
171 		if (pixman_region32_not_empty(surface_damage)) {
172+			view->buffer_opaque_valid = false;
173 			renderer_flush_view(view);
174 
175 			/* Translate surface damage to global coordinates. */
+5, -0
 1@@ -37,7 +37,10 @@ struct wld_font;
 2 struct decor_part_buffer {
 3 	void *data;
 4 	struct wld_buffer *buffer;
 5+	struct wld_buffer *tiled_buffer;
 6 	uint32_t width, height, stride;
 7+	uint32_t tiled_width, tiled_height;
 8+	bool opaque;
 9 };
10 
11 struct swc_compositor {
12@@ -64,6 +67,8 @@ struct compositor_view {
13 	struct view base;
14 	struct surface *surface;
15 	struct wld_buffer *buffer;
16+	bool buffer_opaque_valid;
17+	bool buffer_opaque;
18 	struct window *window;
19 	struct compositor_view *parent;
20 	int32_t buffer_offset_x;
+99, -48
  1@@ -133,6 +133,10 @@ static void
  2 close_decor_parts(struct compositor_view *view)
  3 {
  4 	for (uint32_t i = 0; i < DECOR_PART_COUNT; ++i) {
  5+		if (view->decor.parts[i].tiled_buffer) {
  6+			wld_buffer_unreference(view->decor.parts[i].tiled_buffer);
  7+			view->decor.parts[i].tiled_buffer = NULL;
  8+		}
  9 		if (view->decor.parts[i].buffer) {
 10 			wld_buffer_unreference(view->decor.parts[i].buffer);
 11 			view->decor.parts[i].buffer = NULL;
 12@@ -142,6 +146,9 @@ close_decor_parts(struct compositor_view *view)
 13 		view->decor.parts[i].width = 0;
 14 		view->decor.parts[i].height = 0;
 15 		view->decor.parts[i].stride = 0;
 16+		view->decor.parts[i].tiled_width = 0;
 17+		view->decor.parts[i].tiled_height = 0;
 18+		view->decor.parts[i].opaque = false;
 19 	}
 20 	view->decor.parts_key = NULL;
 21 }
 22@@ -210,6 +217,20 @@ copy_decor_part(struct decor_part_buffer *dst, const struct swc_decor_part *src)
 23 	dst->width = src->width;
 24 	dst->height = src->height;
 25 	dst->stride = src->stride;
 26+	dst->opaque = true;
 27+	for (uint32_t y = 0; y < src->height && dst->opaque; ++y) {
 28+		const uint8_t *row = (const uint8_t *)src->data + (size_t)y * src->stride;
 29+
 30+		for (uint32_t x = 0; x < src->width; ++x) {
 31+			uint32_t pixel;
 32+
 33+			memcpy(&pixel, row + (size_t)x * 4, sizeof(pixel));
 34+			if ((pixel >> 24) != 0xff) {
 35+				dst->opaque = false;
 36+				break;
 37+			}
 38+		}
 39+	}
 40 	/* DRM renderers only allow read support for their native buffers not pixman/shmbuffers */
 41 	dst->buffer = wld_create_buffer(swc.backend->context, src->width, src->height,
 42 	                                WLD_FORMAT_ARGB8888, WLD_FLAG_MAP);
 43@@ -279,69 +300,99 @@ close_decor_string(struct compositor_view *view)
 44 	view->decor.string = NULL;
 45 }
 46 
 47-/* draw decor part by tiling it across the target region.
 48- * the part buffer is repeated to fill the entirety of some width x height area,
 49- * but only damaged regions are actually rendered */
 50+static struct wld_buffer *
 51+get_tiled_decor_buffer(struct decor_part_buffer *part, uint32_t width,
 52+                       uint32_t height)
 53+{
 54+	struct wld_buffer *buffer;
 55+	uint32_t buffer_width = width, buffer_height = height;
 56+	size_t row_size;
 57+
 58+	if (width == part->width && height == part->height) {
 59+		return part->buffer;
 60+	}
 61+	if (part->tiled_buffer && part->tiled_width >= width &&
 62+	    part->tiled_height >= height) {
 63+		return part->tiled_buffer;
 64+	}
 65+
 66+	if (part->tiled_buffer) {
 67+		wld_buffer_unreference(part->tiled_buffer);
 68+		part->tiled_buffer = NULL;
 69+		part->tiled_width = 0;
 70+		part->tiled_height = 0;
 71+	}
 72+
 73+	if (width != part->width && width <= UINT32_MAX - 255) {
 74+		buffer_width = (width + 255) & ~255U;
 75+	}
 76+	if (height != part->height && height <= UINT32_MAX - 255) {
 77+		buffer_height = (height + 255) & ~255U;
 78+	}
 79+
 80+	if (buffer_width > SIZE_MAX / 4) {
 81+		return NULL;
 82+	}
 83+	row_size = (size_t)buffer_width * 4;
 84+	buffer = wld_create_buffer(swc.backend->context, buffer_width, buffer_height,
 85+	                           WLD_FORMAT_ARGB8888, WLD_FLAG_MAP);
 86+	if (!buffer || buffer->pitch < row_size || !wld_map(buffer)) {
 87+		if (buffer) {
 88+			wld_buffer_unreference(buffer);
 89+		}
 90+		return NULL;
 91+	}
 92+
 93+	for (uint32_t y = 0; y < buffer_height; ++y) {
 94+		uint8_t *dst = (uint8_t *)buffer->map + (size_t)y * buffer->pitch;
 95+		const uint8_t *src = (const uint8_t *)part->data +
 96+		                     (size_t)(y % part->height) * part->stride;
 97+
 98+		for (uint32_t x = 0; x < buffer_width; x += part->width) {
 99+			uint32_t copy_width = MIN(part->width, buffer_width - x);
100+
101+			memcpy(dst + (size_t)x * 4, src, (size_t)copy_width * 4);
102+		}
103+	}
104+	wld_unmap(buffer);
105+
106+	part->tiled_buffer = buffer;
107+	part->tiled_width = buffer_width;
108+	part->tiled_height = buffer_height;
109+	return buffer;
110+}
111+
112+/* draw the decor part by tiling it across the target reigon.  
113+ * the part buffer is repeated to fill the entirety of some width x height area
114+ * but then we cache the expanded version so it can be just one op
115+ * instead of one op per every time we repeat it */
116 static void
117 draw_decor_part(struct wld_renderer *renderer,
118                 const struct swc_rectangle *target_geom,
119                 struct compositor_view *view, pixman_region32_t *damage,
120-                const struct decor_part_buffer *part, int32_t x, int32_t y,
121+                struct decor_part_buffer *part, int32_t x, int32_t y,
122                 uint32_t width, uint32_t height)
123 {
124 	pixman_region32_t region;
125-	pixman_box32_t *boxes;
126-	int nboxes;
127+	struct wld_buffer *buffer;
128 
129 	if (!part->buffer || !part->width || !part->height || !width || !height) {
130 		return;
131 	}
132+	if (!(buffer = get_tiled_decor_buffer(part, width, height))) {
133+		return;
134+	}
135 
136 	pixman_region32_init_rect(&region, x, y, width, height);
137 	pixman_region32_intersect(&region, &region, damage);
138 	pixman_region32_subtract(&region, &region, &view->clip);
139-	boxes = pixman_region32_rectangles(&region, &nboxes);
140-
141-	for (int i = 0; i < nboxes; ++i) {
142-		int32_t rx1 = boxes[i].x1;
143-		int32_t ry1 = boxes[i].y1;
144-		int32_t rx2 = boxes[i].x2;
145-		int32_t ry2 = boxes[i].y2;
146-		int32_t start_y = y + ((ry1 - y) / (int32_t)part->height) * (int32_t)part->height;
147-
148-		if (start_y > ry1) {
149-			start_y -= (int32_t)part->height;
150-		}
151-
152-		for (int32_t tile_y = start_y; tile_y < ry2; tile_y += (int32_t)part->height) {
153-			int32_t start_x =
154-			    x + ((rx1 - x) / (int32_t)part->width) * (int32_t)part->width;
155-
156-			if (start_x > rx1) {
157-				start_x -= (int32_t)part->width;
158-			}
159-
160-			for (int32_t tile_x = start_x; tile_x < rx2;
161-			     tile_x += (int32_t)part->width) {
162-				int32_t clip_x1 = MAX(tile_x, rx1);
163-				int32_t clip_y1 = MAX(tile_y, ry1);
164-				int32_t clip_x2 = MIN(tile_x + (int32_t)part->width, rx2);
165-				int32_t clip_y2 = MIN(tile_y + (int32_t)part->height, ry2);
166-
167-				if (clip_x2 > clip_x1 && clip_y2 > clip_y1) {
168-					pixman_region32_t source_region;
169-
170-					pixman_region32_init_rect(
171-					    &source_region, clip_x1 - tile_x, clip_y1 - tile_y,
172-					    (uint32_t)(clip_x2 - clip_x1),
173-					    (uint32_t)(clip_y2 - clip_y1));
174-					wld_blend_region(renderer, part->buffer,
175-					                 tile_x - target_geom->x,
176-					                 tile_y - target_geom->y, &source_region);
177-					pixman_region32_fini(&source_region);
178-				}
179-			}
180-		}
181+	pixman_region32_translate(&region, -x, -y);
182+	if (part->opaque) {
183+		wld_copy_region(renderer, buffer, x - target_geom->x,
184+		                y - target_geom->y, &region);
185+	} else {
186+		wld_blend_region(renderer, buffer, x - target_geom->x,
187+		                 y - target_geom->y, &region);
188 	}
189 
190 	pixman_region32_fini(&region);