1
0
Fork 0
mirror of https://github.com/yshui/picom.git synced 2024-11-11 13:51:02 -05:00

config: add debug option "consistent_buffer_age"

An attempt to make rendering with damage deterministic even when
replayed from a trace.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2024-05-16 11:47:11 +01:00
parent 5ff88f516e
commit 1bd6cd4be9
No known key found for this signature in database
GPG key ID: D3A4405BE6CC17F4
3 changed files with 76 additions and 26 deletions

View file

@ -523,9 +523,10 @@ const char *vblank_scheduler_str[] = {
[LAST_VBLANK_SCHEDULER] = NULL
};
static const struct debug_options_entry debug_options_entries[] = {
{"always_rebind_pixmap", NULL , offsetof(struct debug_options, always_rebind_pixmap)},
{"always_rebind_pixmap" , NULL , offsetof(struct debug_options, always_rebind_pixmap)},
{"smart_frame_pacing" , NULL , offsetof(struct debug_options, smart_frame_pacing)},
{"force_vblank_sched" , vblank_scheduler_str, offsetof(struct debug_options, force_vblank_scheduler)},
{"consistent_buffer_age", NULL , offsetof(struct debug_options, consistent_buffer_age)},
};
// clang-format on

View file

@ -126,6 +126,14 @@ struct debug_options {
/// Useful when being traced under apitrace, to force it to pick up
/// updated contents. WARNING, extremely slow.
int always_rebind_pixmap;
/// When using damage, replaying an apitrace becomes non-deterministic, because
/// the buffer age we got when we rendered will be different from the buffer age
/// apitrace gets when it replays. When this option is enabled, we saves the
/// contents of each rendered frame, and at the beginning of each render, we
/// restore the content of the back buffer based on the buffer age we get,
/// ensuring no matter what buffer age apitrace gets during replay, the result
/// will be the same.
int consistent_buffer_age;
};
extern struct debug_options global_debug_options;

View file

@ -26,6 +26,8 @@ struct renderer {
image_handle *monitor_repaint_copy;
/// Regions painted over by monitor repaint
region_t *monitor_repaint_region;
/// Copy of the entire back buffer
image_handle *back_buffer_copy;
/// Current frame index in ring buffer
int frame_index;
int max_buffer_age;
@ -176,6 +178,13 @@ renderer_set_root_size(struct renderer *r, struct backend_base *backend, ivec2 r
if (r->back_image) {
backend->ops->release_image(backend, r->back_image);
}
if (r->back_buffer_copy) {
for (int i = 0; i < r->max_buffer_age; i++) {
backend->ops->release_image(backend, r->back_buffer_copy[i]);
}
free(r->back_buffer_copy);
r->back_buffer_copy = NULL;
}
if (r->monitor_repaint_copy) {
for (int i = 0; i < r->max_buffer_age; i++) {
backend->ops->release_image(backend, r->monitor_repaint_copy[i]);
@ -454,7 +463,9 @@ static bool renderer_prepare_commands(struct renderer *r, struct backend_base *b
return true;
}
void renderer_ensure_monitor_repaint_ready(struct renderer *r, struct backend_base *backend) {
void renderer_ensure_images_ready(struct renderer *r, struct backend_base *backend,
bool monitor_repaint) {
if (monitor_repaint) {
if (!r->monitor_repaint_pixel) {
r->monitor_repaint_pixel = backend->ops->new_image(
backend, BACKEND_IMAGE_FORMAT_PIXMAP, (ivec2){1, 1});
@ -478,6 +489,17 @@ void renderer_ensure_monitor_repaint_ready(struct renderer *r, struct backend_ba
pixman_region32_init(&r->monitor_repaint_region[i]);
}
}
}
if (global_debug_options.consistent_buffer_age && !r->back_buffer_copy) {
r->back_buffer_copy = ccalloc(r->max_buffer_age, image_handle);
for (int i = 0; i < r->max_buffer_age; i++) {
r->back_buffer_copy[i] = backend->ops->new_image(
backend, BACKEND_IMAGE_FORMAT_PIXMAP,
(ivec2){.width = r->canvas_size.width,
.height = r->canvas_size.height});
BUG_ON(!r->back_buffer_copy[i]);
}
}
}
/// @return true if a frame is rendered, false if this frame is skipped.
@ -506,9 +528,7 @@ bool renderer_render(struct renderer *r, struct backend_base *backend,
return false;
}
if (monitor_repaint) {
renderer_ensure_monitor_repaint_ready(r, backend);
}
renderer_ensure_images_ready(r, backend, monitor_repaint);
command_builder_build(cb, layout, force_blend, blur_frame, inactive_dim_fixed,
max_brightness, inactive_dim, monitors, wintype_options);
@ -539,6 +559,16 @@ bool renderer_render(struct renderer *r, struct backend_base *backend,
}
auto buffer_age =
(use_damage || monitor_repaint) ? backend->ops->buffer_age(backend) : 0;
if (buffer_age > 0 && global_debug_options.consistent_buffer_age) {
int past_frame =
(r->frame_index + r->max_buffer_age - buffer_age) % r->max_buffer_age;
region_t region;
pixman_region32_init_rect(&region, 0, 0, (unsigned)r->canvas_size.width,
(unsigned)r->canvas_size.height);
backend->ops->copy_area(backend, (ivec2){}, backend->ops->back_buffer(backend),
r->back_buffer_copy[past_frame], &region);
pixman_region32_fini(&region);
}
if (buffer_age > 0 && (unsigned)buffer_age <= layout_manager_max_buffer_age(lm)) {
layout_manager_damage(lm, (unsigned)buffer_age, blur_size, &damage_region);
}
@ -604,10 +634,21 @@ bool renderer_render(struct renderer *r, struct backend_base *backend,
backend->ops->blit(backend, (ivec2){}, r->back_image, &blit);
}
if (backend->ops->present) {
backend->ops->copy_area_quantize(backend, (ivec2){},
backend->ops->back_buffer(backend),
r->back_image, &damage_region);
if (global_debug_options.consistent_buffer_age) {
region_t region;
pixman_region32_init_rect(&region, 0, 0, (unsigned)r->canvas_size.width,
(unsigned)r->canvas_size.height);
backend->ops->copy_area(backend, (ivec2){},
r->back_buffer_copy[r->frame_index],
backend->ops->back_buffer(backend), &region);
pixman_region32_fini(&region);
}
if (backend->ops->present) {
backend->ops->present(backend);
}