Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2 frames 2 pacing #1156

Merged
merged 18 commits into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions src/backend/backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,17 @@
}

/// paint all windows
void paint_all_new(session_t *ps, struct managed_win *t) {
///
/// Returns if any render command is issued. IOW if nothing on the screen has changed,
/// this function will return false.
bool paint_all_new(session_t *ps, struct managed_win *const t) {
struct timespec now = get_time_timespec();
auto paint_all_start_us =
(uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000;
if (ps->backend_data->ops->device_status &&
ps->backend_data->ops->device_status(ps->backend_data) != DEVICE_STATUS_NORMAL) {
return handle_device_reset(ps);
handle_device_reset(ps);
return false;

Check warning on line 95 in src/backend/backend.c

View check run for this annotation

Codecov / codecov/patch

src/backend/backend.c#L94-L95

Added lines #L94 - L95 were not covered by tests
}
if (ps->o.xrender_sync_fence) {
if (ps->xsync_exists && !x_fence_sync(&ps->c, ps->sync_fence)) {
Expand All @@ -114,7 +118,7 @@

if (!pixman_region32_not_empty(&reg_damage)) {
pixman_region32_fini(&reg_damage);
return;
return false;
}

#ifdef DEBUG_REPAINT
Expand Down Expand Up @@ -190,16 +194,15 @@
auto after_damage_us = (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000;
log_trace("Getting damage took %" PRIu64 " us", after_damage_us - after_sync_fence_us);
if (ps->next_render > 0) {
log_trace("Render schedule deviation: %ld us (%s) %" PRIu64 " %ld",
labs((long)after_damage_us - (long)ps->next_render),
after_damage_us < ps->next_render ? "early" : "late",
after_damage_us, ps->next_render);
log_verbose("Render schedule deviation: %ld us (%s) %" PRIu64 " %ld",
labs((long)after_damage_us - (long)ps->next_render),
after_damage_us < ps->next_render ? "early" : "late",
after_damage_us, ps->next_render);
ps->last_schedule_delay = 0;
if (after_damage_us > ps->next_render) {
ps->last_schedule_delay = after_damage_us - ps->next_render;
}
}
ps->did_render = true;

if (ps->backend_data->ops->prepare) {
ps->backend_data->ops->prepare(ps->backend_data, &reg_paint);
Expand All @@ -219,7 +222,7 @@
// on top of that window. This is used to reduce the number of pixels painted.
//
// Whether this is beneficial is to be determined XXX
for (auto w = t; w; w = w->prev_trans) {
for (struct managed_win *w = t; w; w = w->prev_trans) {
pixman_region32_subtract(&reg_visible, &ps->screen_reg, w->reg_ignore);
assert(!(w->flags & WIN_FLAGS_IMAGE_ERROR));
assert(!(w->flags & WIN_FLAGS_PIXMAP_STALE));
Expand Down Expand Up @@ -541,6 +544,7 @@
for (win *w = t; w; w = w->prev_trans)
log_trace(" %#010lx", w->id);
#endif
return true;
}

// vim: set noet sw=8 ts=8 :
6 changes: 5 additions & 1 deletion src/backend/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,4 +366,8 @@ struct backend_operations {

extern struct backend_operations *backend_list[];

void paint_all_new(session_t *ps, struct managed_win *const t) attr_nonnull(1);
/// paint all windows
///
/// Returns if any render command is issued. IOW if nothing on the screen has changed,
/// this function will return false.
bool paint_all_new(session_t *ps, struct managed_win *t) attr_nonnull(1);
7 changes: 7 additions & 0 deletions src/backend/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
}
}

enum vblank_scheduler_type choose_vblank_scheduler(enum driver driver) {
if (driver & DRIVER_NVIDIA) {
return VBLANK_SCHEDULER_SGI_VIDEO_SYNC;

Check warning on line 24 in src/backend/driver.c

View check run for this annotation

Codecov / codecov/patch

src/backend/driver.c#L22-L24

Added lines #L22 - L24 were not covered by tests
}
return VBLANK_SCHEDULER_PRESENT;

Check warning on line 26 in src/backend/driver.c

View check run for this annotation

Codecov / codecov/patch

src/backend/driver.c#L26

Added line #L26 was not covered by tests
}

enum driver detect_driver(xcb_connection_t *c, backend_t *backend_data, xcb_window_t window) {
enum driver ret = 0;
// First we try doing backend agnostic detection using RANDR
Expand Down
3 changes: 3 additions & 0 deletions src/backend/driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <stdio.h>
#include <xcb/xcb.h>

#include "config.h"
#include "utils.h"

struct session;
Expand Down Expand Up @@ -41,6 +42,8 @@ enum driver detect_driver(xcb_connection_t *, struct backend_base *, xcb_window_

/// Apply driver specified global workarounds. It's safe to call this multiple times.
void apply_driver_workarounds(struct session *ps, enum driver);
/// Choose a vblank scheduler based on the driver.
enum vblank_scheduler_type choose_vblank_scheduler(enum driver driver);

// Print driver names to stdout, for diagnostics
static inline void print_drivers(enum driver drivers) {
Expand Down
27 changes: 14 additions & 13 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,6 @@ typedef struct session {
// === Event handlers ===
/// ev_io for X connection
ev_io xiow;
/// Timer for checking DPMS power level
ev_timer dpms_check_timer;
/// Timeout for delayed unredirection.
ev_timer unredir_timer;
/// Timer for fading
Expand Down Expand Up @@ -214,26 +212,19 @@ typedef struct session {
bool first_frame;
/// Whether screen has been turned off
bool screen_is_off;
/// Event context for X Present extension.
uint32_t present_event_id;
xcb_special_event_t *present_event;
/// When last MSC event happened, in useconds.
uint64_t last_msc_instant;
/// The last MSC number
uint64_t last_msc;
/// When the currently rendered frame will be displayed.
/// 0 means there is no pending frame.
uint64_t target_msc;
/// The delay between when the last frame was scheduled to be rendered, and when
/// the render actually started.
uint64_t last_schedule_delay;
/// When do we want our next frame to start rendering.
uint64_t next_render;
/// Did we actually render the last frame. Sometimes redraw will be scheduled only
/// to find out nothing has changed. In which case this will be set to false.
bool did_render;
/// Whether we can perform frame pacing.
bool frame_pacing;
/// Vblank event scheduler
struct vblank_scheduler *vblank_scheduler;

/// Render statistics
struct render_statistics render_stats;
Expand All @@ -245,8 +236,18 @@ typedef struct session {
options_t o;
/// Whether we have hit unredirection timeout.
bool tmout_unredir_hit;
/// Whether we need to redraw the screen
bool redraw_needed;
/// If the backend is busy. This means two things:
/// Either the backend is currently rendering a frame, or a frame has been
/// rendered but has yet to be presented. In either case, we should not start
/// another render right now. As if we start issuing rendering commands now, we
/// will have to wait for either the the current render to finish, or the current
/// back buffer to be become available again. In either case, we will be wasting
/// time.
bool backend_busy;
/// Whether a render is queued. This generally means there are pending updates
/// to the screen that's neither included in the current render, nor on the
/// screen.
bool render_queued;

/// Cache a xfixes region so we don't need to allocate it every time.
/// A workaround for yshui/picom#301
Expand Down
79 changes: 79 additions & 0 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <limits.h>
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -615,6 +617,82 @@
return ret;
}

struct debug_options_entry {
const char *name;
const char **choices;
size_t offset;
};

// clang-format off
const char *vblank_scheduler_str[] = {
[VBLANK_SCHEDULER_PRESENT] = "present",
[VBLANK_SCHEDULER_SGI_VIDEO_SYNC] = "sgi_video_sync",
[LAST_VBLANK_SCHEDULER] = NULL
};
static const struct debug_options_entry debug_options_entries[] = {
{"smart_frame_pacing", NULL, offsetof(struct debug_options, smart_frame_pacing)},
{"force_vblank_sched", vblank_scheduler_str, offsetof(struct debug_options, force_vblank_scheduler)},
};
// clang-format on

void parse_debug_option_single(char *setting, struct debug_options *debug_options) {
char *equal = strchr(setting, '=');
size_t name_len = equal ? (size_t)(equal - setting) : strlen(setting);
for (size_t i = 0; i < ARR_SIZE(debug_options_entries); i++) {
if (strncmp(setting, debug_options_entries[i].name, name_len) != 0) {
continue;

Check warning on line 643 in src/config.c

View check run for this annotation

Codecov / codecov/patch

src/config.c#L638-L643

Added lines #L638 - L643 were not covered by tests
}
if (debug_options_entries[i].name[name_len] != '\0') {
continue;

Check warning on line 646 in src/config.c

View check run for this annotation

Codecov / codecov/patch

src/config.c#L645-L646

Added lines #L645 - L646 were not covered by tests
}
auto value = (int *)((void *)debug_options + debug_options_entries[i].offset);
if (equal) {
const char *const arg = equal + 1;
if (debug_options_entries[i].choices != NULL) {
for (size_t j = 0; debug_options_entries[i].choices[j]; j++) {
if (strcmp(arg, debug_options_entries[i].choices[j]) ==

Check warning on line 653 in src/config.c

View check run for this annotation

Codecov / codecov/patch

src/config.c#L648-L653

Added lines #L648 - L653 were not covered by tests
0) {
*value = (int)j;
return;

Check warning on line 656 in src/config.c

View check run for this annotation

Codecov / codecov/patch

src/config.c#L655-L656

Added lines #L655 - L656 were not covered by tests
}
}
}
if (!parse_int(arg, value)) {
log_error("Invalid value for debug option %s: %s, it "

Check warning on line 661 in src/config.c

View check run for this annotation

Codecov / codecov/patch

src/config.c#L660-L661

Added lines #L660 - L661 were not covered by tests
"will be ignored.",
debug_options_entries[i].name, arg);
}
} else if (debug_options_entries[i].choices == NULL) {
*value = 1;

Check warning on line 666 in src/config.c

View check run for this annotation

Codecov / codecov/patch

src/config.c#L665-L666

Added lines #L665 - L666 were not covered by tests
} else {
log_error(

Check warning on line 668 in src/config.c

View check run for this annotation

Codecov / codecov/patch

src/config.c#L668

Added line #L668 was not covered by tests
"Missing value for debug option %s, it will be ignored.", setting);
}
return;

Check warning on line 671 in src/config.c

View check run for this annotation

Codecov / codecov/patch

src/config.c#L671

Added line #L671 was not covered by tests
}
log_error("Invalid debug option: %s", setting);

Check warning on line 673 in src/config.c

View check run for this annotation

Codecov / codecov/patch

src/config.c#L673

Added line #L673 was not covered by tests
}

/// Parse debug options from environment variable `PICOM_DEBUG`.
void parse_debug_options(struct debug_options *debug_options) {
const char *debug = getenv("PICOM_DEBUG");
const struct debug_options default_debug_options = {
.force_vblank_scheduler = LAST_VBLANK_SCHEDULER,
};

*debug_options = default_debug_options;
if (!debug) {
return;
}

scoped_charp debug_copy = strdup(debug);
char *tmp, *needle = strtok_r(debug_copy, ";", &tmp);
while (needle) {
parse_debug_option_single(needle, debug_options);
needle = strtok_r(NULL, ";", &tmp);

Check warning on line 692 in src/config.c

View check run for this annotation

Codecov / codecov/patch

src/config.c#L688-L692

Added lines #L688 - L692 were not covered by tests
}
}

/**
* Parse a list of window shader rules.
*/
Expand Down Expand Up @@ -817,5 +895,6 @@
(void)hasneg;
(void)winopt_mask;
#endif
parse_debug_options(&opt->debug_options);
return ret;
}
23 changes: 23 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,27 @@ enum blur_method {

typedef struct _c2_lptr c2_lptr_t;

enum vblank_scheduler_type {
/// X Present extension based vblank events
VBLANK_SCHEDULER_PRESENT,
/// GLX_SGI_video_sync based vblank events
VBLANK_SCHEDULER_SGI_VIDEO_SYNC,
/// An invalid scheduler, served as a scheduler count, and
/// as a sentinel value.
LAST_VBLANK_SCHEDULER,
};

extern const char *vblank_scheduler_str[];

/// Internal, private options for debugging and development use.
struct debug_options {
/// Try to reduce frame latency by using vblank interval and render time
/// estimates. Right now it's not working well across drivers.
int smart_frame_pacing;
/// Override the vblank scheduler chosen by the compositor.
int force_vblank_scheduler;
};

/// Structure representing all options.
typedef struct options {
// === Debugging ===
Expand Down Expand Up @@ -262,6 +283,8 @@ typedef struct options {
c2_lptr_t *transparent_clipping_blacklist;

bool dithered_present;

struct debug_options debug_options;
} options_t;

extern const char *const BACKEND_STRS[NUM_BKEND + 1];
Expand Down
3 changes: 2 additions & 1 deletion src/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,8 @@ void ev_handle(session_t *ps, xcb_generic_event_t *ev) {
// XXX redraw needs to be more fine grained
queue_redraw(ps);

// the events sent from SendEvent will be ignored
// We intentionally ignore events sent via SendEvent. Those events has the 8th bit
// of response_type set, meaning they will match none of the cases below.
switch (ev->response_type) {
case FocusIn: ev_focus_in(ps, (xcb_focus_in_event_t *)ev); break;
case FocusOut: ev_focus_out(ps, (xcb_focus_out_event_t *)ev); break;
Expand Down
5 changes: 3 additions & 2 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ base_deps = [

srcs = [ files('picom.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'vsync.c', 'utils.c',
'diagnostic.c', 'string_utils.c', 'render.c', 'kernel.c', 'log.c',
'options.c', 'event.c', 'cache.c', 'atom.c', 'file_watch.c', 'statistics.c') ]
'options.c', 'event.c', 'cache.c', 'atom.c', 'file_watch.c', 'statistics.c',
'vblank.c') ]
picom_inc = include_directories('.')

cflags = []
Expand Down Expand Up @@ -58,7 +59,7 @@ endif

if get_option('opengl')
cflags += ['-DCONFIG_OPENGL', '-DGL_GLEXT_PROTOTYPES']
deps += [dependency('gl', required: true), dependency('egl', required: true)]
deps += [dependency('gl', required: true), dependency('egl', required: true), dependency('threads', required:true)]
srcs += [ 'opengl.c' ]
endif

Expand Down
Loading
Loading