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(®ion, x, y, width, height);
137 pixman_region32_intersect(®ion, ®ion, damage);
138 pixman_region32_subtract(®ion, ®ion, &view->clip);
139- boxes = pixman_region32_rectangles(®ion, &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(®ion, -x, -y);
182+ if (part->opaque) {
183+ wld_copy_region(renderer, buffer, x - target_geom->x,
184+ y - target_geom->y, ®ion);
185+ } else {
186+ wld_blend_region(renderer, buffer, x - target_geom->x,
187+ y - target_geom->y, ®ion);
188 }
189
190 pixman_region32_fini(®ion);