diff --git a/flake.nix b/flake.nix index 221a1a9114..31880a4568 100644 --- a/flake.nix +++ b/flake.nix @@ -59,6 +59,8 @@ # Workaround a NixOS limitation on sanitizers: # See: https://github.com/NixOS/nixpkgs/issues/287763 export LD_LIBRARY_PATH+=":/run/opengl-driver/lib" + export UBSAN_OPTIONS="disable_coredump=0:unmap_shadow_on_exit=1" + export ASAN_OPTIONS="disable_coredump=0:unmap_shadow_on_exit=1" ''; }); in rec { diff --git a/src/api.c b/src/api.c index 713fece81a..8684b95cf7 100644 --- a/src/api.c +++ b/src/api.c @@ -9,9 +9,9 @@ #include #include "compiler.h" -#include "list.h" #include "log.h" -#include "utils.h" +#include "utils/list.h" +#include "utils/misc.h" struct backend_plugins { UT_hash_handle hh; diff --git a/src/atom.c b/src/atom.c index f4b97ebc17..01b09484c9 100644 --- a/src/atom.c +++ b/src/atom.c @@ -1,13 +1,16 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + #include #include #include #include "atom.h" -#include "cache.h" #include "common.h" #include "compiler.h" #include "log.h" -#include "utils.h" +#include "utils/cache.h" +#include "utils/misc.h" struct atom_entry { struct cache_handle entry; @@ -163,4 +166,4 @@ struct atom *init_mock_atoms(void) { abort(); } -#endif \ No newline at end of file +#endif diff --git a/src/atom.h b/src/atom.h index 5b94294b07..37bb870e94 100644 --- a/src/atom.h +++ b/src/atom.h @@ -1,8 +1,11 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + #pragma once +#include #include -#include "cache.h" -#include "meta.h" +#include "utils/meta.h" // clang-format off // Splitted into 2 lists because of the limitation of our macros @@ -80,4 +83,4 @@ void destroy_atoms(struct atom *a); /// secutive integers as atoms, starting from 1. Calling get_atom_name with atoms /// previously seen will result in the string that was used to create the atom; if /// the atom was never returned by get_atom, it will abort. -struct atom *init_mock_atoms(void); \ No newline at end of file +struct atom *init_mock_atoms(void); diff --git a/src/backend/backend.c b/src/backend/backend.c index f74880d134..5dcc19548e 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -6,15 +6,16 @@ #include -#include "backend/backend.h" #include "common.h" #include "compiler.h" #include "config.h" #include "log.h" #include "region.h" -#include "win.h" +#include "wm/win.h" #include "x.h" +#include "backend.h" + static struct backend_info { UT_hash_handle hh; const char *name; diff --git a/src/backend/backend_common.c b/src/backend/backend_common.c index 4554964ae0..177d372221 100644 --- a/src/backend/backend_common.c +++ b/src/backend/backend_common.c @@ -6,15 +6,16 @@ #include #include -#include "backend/backend_common.h" #include "common.h" #include "config.h" -#include "kernel.h" #include "log.h" -#include "utils.h" -#include "win.h" +#include "utils/kernel.h" +#include "utils/misc.h" +#include "wm/win.h" #include "x.h" +#include "backend_common.h" + /** * Generate a 1x1 Picture of a particular color. */ diff --git a/src/backend/driver.c b/src/backend/driver.c index 53ef1f5138..47f15a2ad9 100644 --- a/src/backend/driver.c +++ b/src/backend/driver.c @@ -6,12 +6,12 @@ #include #include -#include "backend/backend.h" -#include "backend/driver.h" #include "common.h" #include "compiler.h" #include "log.h" +#include "driver.h" + /// Apply driver specified global workarounds. It's safe to call this multiple times. void apply_driver_workarounds(struct session *ps, enum driver driver) { if (driver & DRIVER_NVIDIA) { diff --git a/src/backend/driver.h b/src/backend/driver.h index 1b0877c14b..1c20298eb5 100644 --- a/src/backend/driver.h +++ b/src/backend/driver.h @@ -8,7 +8,7 @@ #include #include "config.h" -#include "utils.h" +#include "utils/misc.h" struct session; struct backend_base; diff --git a/src/backend/dummy/dummy.c b/src/backend/dummy/dummy.c index 04ec3fff20..a6181c173d 100644 --- a/src/backend/dummy/dummy.c +++ b/src/backend/dummy/dummy.c @@ -10,8 +10,8 @@ #include "config.h" #include "log.h" #include "region.h" -#include "uthash_extra.h" -#include "utils.h" +#include "utils/misc.h" +#include "utils/uthash_extra.h" #include "x.h" struct dummy_image { diff --git a/src/backend/gl/blur.c b/src/backend/gl/blur.c index c9d85fa0eb..87830cc4bc 100644 --- a/src/backend/gl/blur.c +++ b/src/backend/gl/blur.c @@ -1,8 +1,7 @@ #include #include -#include -#include +#include "backend/backend_common.h" #include "gl_common.h" diff --git a/src/backend/gl/egl.c b/src/backend/gl/egl.c index 9b5dbbb2e5..13d30b7876 100644 --- a/src/backend/gl/egl.c +++ b/src/backend/gl/egl.c @@ -11,16 +11,17 @@ #include "backend/backend.h" #include "backend/backend_common.h" -#include "backend/gl/egl.h" -#include "backend/gl/gl_common.h" #include "common.h" #include "compiler.h" #include "config.h" #include "log.h" #include "picom.h" -#include "utils.h" +#include "utils/misc.h" #include "x.h" +#include "egl.h" +#include "gl_common.h" + struct egl_data { struct gl_data gl; EGLDisplay display; diff --git a/src/backend/gl/egl.h b/src/backend/gl/egl.h index 033e84da37..bfe1f97d20 100644 --- a/src/backend/gl/egl.h +++ b/src/backend/gl/egl.h @@ -7,11 +7,6 @@ #include #include -#include "compiler.h" -#include "log.h" -#include "utils.h" -#include "x.h" - struct eglext_info { bool initialized; bool has_EGL_MESA_query_driver; diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index e09ed8e7cb..918ee506e6 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -11,15 +11,15 @@ #include +#include "backend/backend_common.h" #include "common.h" #include "compiler.h" #include "config.h" #include "log.h" #include "region.h" -#include "utils.h" +#include "utils/misc.h" -#include "backend/backend_common.h" -#include "backend/gl/gl_common.h" +#include "gl_common.h" void gl_prepare(backend_t *base, const region_t *reg attr_unused) { auto gd = (struct gl_data *)base; diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h index 136eeb2d08..a327e50a09 100644 --- a/src/backend/gl/gl_common.h +++ b/src/backend/gl/gl_common.h @@ -7,7 +7,7 @@ #include #include "backend/backend.h" -#include "backend/backend_common.h" + #include "log.h" #include "region.h" diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c index 791f433bda..629f49f3c9 100644 --- a/src/backend/gl/glx.c +++ b/src/backend/gl/glx.c @@ -9,13 +9,13 @@ * See LICENSE-mit for more information. * */ - -#include #include -#include #include #include #include + +#include +#include #include #include #include @@ -23,17 +23,17 @@ #include "backend/backend.h" #include "backend/backend_common.h" -#include "backend/gl/gl_common.h" -#include "backend/gl/glx.h" #include "common.h" #include "compiler.h" #include "config.h" #include "log.h" #include "picom.h" -#include "utils.h" -#include "win.h" +#include "utils/misc.h" #include "x.h" +#include "gl_common.h" +#include "glx.h" + struct _glx_data { struct gl_data gl; xcb_window_t target_win; diff --git a/src/backend/gl/glx.h b/src/backend/gl/glx.h index 7167936542..cee2671cac 100644 --- a/src/backend/gl/glx.h +++ b/src/backend/gl/glx.h @@ -1,15 +1,13 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui #pragma once +#include + #include #include -#include #include #include -#include "compiler.h" -#include "log.h" -#include "utils.h" #include "x.h" struct glx_fbconfig_info { diff --git a/src/backend/gl/meson.build b/src/backend/gl/meson.build new file mode 100644 index 0000000000..dcdfde3db3 --- /dev/null +++ b/src/backend/gl/meson.build @@ -0,0 +1 @@ +srcs += [ files('blur.c', 'egl.c', 'gl_common.c', 'glx.c', 'shaders.c') ] diff --git a/src/backend/meson.build b/src/backend/meson.build index afc2d51dd1..a580006796 100644 --- a/src/backend/meson.build +++ b/src/backend/meson.build @@ -2,16 +2,13 @@ srcs += [ files( 'dummy/dummy.c', - 'xrender/xrender.c', 'backend.c', 'backend_common.c', 'driver.c', ), ] - +subdir('xrender') # enable opengl if get_option('opengl') - srcs += [ - files('gl/blur.c', 'gl/egl.c', 'gl/gl_common.c', 'gl/glx.c', 'gl/shaders.c'), - ] + subdir('gl') endif diff --git a/src/backend/xrender/meson.build b/src/backend/xrender/meson.build new file mode 100644 index 0000000000..d09056b3f1 --- /dev/null +++ b/src/backend/xrender/meson.build @@ -0,0 +1 @@ +srcs += [ files('xrender.c') ] diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index e83c4bd146..a8551f1841 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -20,12 +20,11 @@ #include "common.h" #include "compiler.h" #include "config.h" -#include "kernel.h" #include "log.h" #include "picom.h" #include "region.h" -#include "utils.h" -#include "win.h" +#include "utils/kernel.h" +#include "utils/misc.h" #include "x.h" struct xrender_image_data_inner { diff --git a/src/c2.c b/src/c2.c index 8030f92181..a883a284f8 100644 --- a/src/c2.c +++ b/src/c2.c @@ -1,14 +1,6 @@ // SPDX-License-Identifier: MIT - -/* - * Compton - a compositor for X11 - * - * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard - * - * Copyright (c) 2011-2013, Christopher Jeffrey - * See LICENSE-mit for more information. - * - */ +// Copyright (c) 2011-2013, Christopher Jeffrey +// Copyright (c) 2018 Yuxuan Shui #include #include @@ -34,11 +26,10 @@ #include "compiler.h" #include "config.h" #include "log.h" -#include "string_utils.h" #include "test.h" -#include "uthash_extra.h" -#include "utils.h" -#include "win.h" +#include "utils/str.h" +#include "utils/uthash_extra.h" +#include "wm/win.h" #include "x.h" #include "c2.h" @@ -550,6 +541,7 @@ TEST_CASE(c2_parse) { cond = c2_parse(NULL, "_NET_WM_STATE = '_NET_WM_STATE_HIDDEN'", NULL); TEST_NOTEQUAL(cond, NULL); + c2_list_free(&cond, NULL); cond = c2_parse(NULL, "1A:\n1111111111111ar1", NULL); TEST_EQUAL(cond, NULL); diff --git a/src/c2.h b/src/c2.h index 681e54a520..328a3c4c78 100644 --- a/src/c2.h +++ b/src/c2.h @@ -1,13 +1,6 @@ // SPDX-License-Identifier: MIT -/* - * Compton - a compositor for X11 - * - * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard - * - * Copyright (c) 2011-2013, Christopher Jeffrey - * See LICENSE-mit for more information. - * - */ +// Copyright (c) 2011-2013, Christopher Jeffrey +// Copyright (c) 2018 Yuxuan Shui #pragma once diff --git a/src/common.h b/src/common.h index c471214597..cd21099876 100644 --- a/src/common.h +++ b/src/common.h @@ -1,15 +1,6 @@ // SPDX-License-Identifier: MIT -/* - * Compton - a compositor for X11 - * - * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard - * - * Copyright (c) 2011-2013, Christopher Jeffrey - * Copyright (c) 2018, Yuxuan Shui - * - * See LICENSE-mit for more information. - * - */ +// Copyright (c) 2011-2013, Christopher Jeffrey +// Copyright (c) 2018, Yuxuan Shui #pragma once @@ -28,7 +19,6 @@ // === Includes === // For some special functions -#include #include #include #include @@ -36,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -57,8 +48,8 @@ #include "config.h" #include "region.h" #include "render.h" -#include "statistics.h" -#include "win_defs.h" +#include "utils/statistics.h" +#include "wm/defs.h" #include "x.h" // === Constants ===0 @@ -285,12 +276,9 @@ typedef struct session { // === Expose event related === /// Pointer to an array of XRectangle-s of exposed region. - /// XXX why do we need this array? + /// This is a reuse temporary buffer for handling root expose events. + /// This is a dynarr. rect_t *expose_rects; - /// Number of XRectangle-s in expose_rects. - int size_expose; - /// Index of the next free slot in expose_rects. - int n_expose; struct wm *wm; diff --git a/src/compiler.h b/src/compiler.h index 03674be8e3..8cdfefafe5 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2018 Yuxuan Shui + #pragma once #ifdef HAS_STDC_PREDEF_H diff --git a/src/config.c b/src/config.c index 22d71f9a34..ae8eff8f3e 100644 --- a/src/config.c +++ b/src/config.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2011-2013, Christopher Jeffrey // Copyright (c) 2013 Richard Grenville +// Copyright (c) 2018 Yuxuan Shui #include #include @@ -20,9 +21,9 @@ #include #include "common.h" -#include "kernel.h" #include "log.h" -#include "string_utils.h" +#include "utils/kernel.h" +#include "utils/str.h" #include "config.h" diff --git a/src/config.h b/src/config.h index bd1d93be1a..410132eb82 100644 --- a/src/config.h +++ b/src/config.h @@ -2,6 +2,7 @@ // Copyright (c) 2011-2013, Christopher Jeffrey // Copyright (c) 2013 Richard Grenville // Copyright (c) 2018 Yuxuan Shui + #pragma once /// Common functions and definitions for configuration parsing @@ -19,10 +20,10 @@ #include #include "compiler.h" -#include "kernel.h" -#include "list.h" #include "log.h" -#include "win_defs.h" +#include "utils/kernel.h" +#include "utils/list.h" +#include "wm/defs.h" typedef struct session session_t; @@ -352,9 +353,8 @@ typedef struct options { bool dithered_present; // === Animation === struct win_script animations[ANIMATION_TRIGGER_LAST + 1]; - /// Array of all the scripts used in `animations`. + /// Array of all the scripts used in `animations`. This is a dynarr. struct script **all_scripts; - int number_of_scripts; } options_t; extern const char *const BACKEND_STRS[NUM_BKEND + 1]; diff --git a/src/config_libconfig.c b/src/config_libconfig.c index 6673bdb03c..ceedabe330 100644 --- a/src/config_libconfig.c +++ b/src/config_libconfig.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2012-2014 Richard Grenville +// Copyright (c) 2018 Yuxuan Shui #include #include @@ -15,10 +16,11 @@ #include "common.h" #include "config.h" #include "log.h" -#include "script.h" -#include "string_utils.h" -#include "utils.h" -#include "win.h" +#include "transition/script.h" +#include "utils/dynarr.h" +#include "utils/misc.h" +#include "utils/str.h" +#include "wm/win.h" #pragma GCC diagnostic error "-Wunused-parameter" @@ -385,23 +387,17 @@ parse_animation_one(struct win_script *animations, config_setting_t *setting) { return script; } -static struct script **parse_animations(struct win_script *animations, - config_setting_t *setting, int *number_of_scripts) { - auto number_of_animations = config_setting_length(setting); - auto all_scripts = ccalloc(number_of_animations + 1, struct script *); - auto len = 0; - for (int i = 0; i < number_of_animations; i++) { +static struct script ** +parse_animations(struct win_script *animations, config_setting_t *setting) { + auto number_of_animations = (unsigned)config_setting_length(setting); + auto all_scripts = dynarr_new(struct script *, number_of_animations + 1); + for (unsigned i = 0; i < number_of_animations; i++) { auto sub = config_setting_get_elem(setting, (unsigned)i); auto script = parse_animation_one(animations, sub); if (script != NULL) { - all_scripts[len++] = script; + dynarr_push(all_scripts, script); } } - if (len == 0) { - free(all_scripts); - all_scripts = NULL; - } - *number_of_scripts = len; return all_scripts; } @@ -442,7 +438,7 @@ void generate_fading_config(struct options *opt) { size_t len = 0; enum animation_trigger trigger[2]; struct script *scripts[4]; - int number_of_scripts = 0; + unsigned number_of_scripts = 0; int number_of_triggers = 0; int output_indices[NUM_OF_WIN_SCRIPT_OUTPUTS]; @@ -531,16 +527,7 @@ void generate_fading_config(struct options *opt) { } log_debug("Generated %d scripts for fading.", number_of_scripts); - if (number_of_scripts) { - auto ptr = realloc( - opt->all_scripts, - sizeof(struct scripts * [number_of_scripts + opt->number_of_scripts])); - allocchk(ptr); - opt->all_scripts = ptr; - memcpy(&opt->all_scripts[opt->number_of_scripts], scripts, - sizeof(struct script *[number_of_scripts])); - opt->number_of_scripts += number_of_scripts; - } + dynarr_extend_from(opt->all_scripts, scripts, number_of_scripts); } static const char ** @@ -994,8 +981,10 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) { config_setting_t *animations = config_lookup(&cfg, "animations"); if (animations) { - opt->all_scripts = - parse_animations(opt->animations, animations, &opt->number_of_scripts); + opt->all_scripts = parse_animations(opt->animations, animations); + } else { + // Reserve some space for generated fading scripts. + opt->all_scripts = dynarr_new(struct script *, 4); } opt->config_file_path = path; diff --git a/src/dbus.c b/src/dbus.c index f09080fc6d..4c09b08390 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -1,13 +1,6 @@ // SPDX-License-Identifier: MIT -/* - * Compton - a compositor for X11 - * - * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard - * - * Copyright (c) 2011-2013, Christopher Jeffrey - * See LICENSE-mit for more information. - * - */ +// Copyright (c) 2011-2013, Christopher Jeffrey +// Copyright (c) 2018 Yuxuan Shui #include #include @@ -26,13 +19,13 @@ #include "common.h" #include "compiler.h" #include "config.h" -#include "list.h" #include "log.h" -#include "string_utils.h" -#include "utils.h" -#include "win.h" -#include "win_defs.h" -#include "wm.h" +#include "utils/list.h" +#include "utils/misc.h" +#include "utils/str.h" +#include "wm/defs.h" +#include "wm/win.h" +#include "wm/wm.h" #include "dbus.h" diff --git a/src/dbus.h b/src/dbus.h index 8541783a09..cd015c3fba 100644 --- a/src/dbus.h +++ b/src/dbus.h @@ -1,13 +1,6 @@ // SPDX-License-Identifier: MIT -/* - * Compton - a compositor for X11 - * - * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard - * - * Copyright (c) 2011-2013, Christopher Jeffrey - * See LICENSE-mit for more information. - * - */ +// Copyright (c) 2011-2013, Christopher Jeffrey +// Copyright (c) 2018 Yuxuan Shui #include diff --git a/src/event.c b/src/event.c index f650ece207..cd06edf308 100644 --- a/src/event.c +++ b/src/event.c @@ -22,10 +22,10 @@ #include "log.h" #include "picom.h" #include "region.h" -#include "utils.h" -#include "win.h" -#include "win_defs.h" -#include "wm.h" +#include "utils/dynarr.h" +#include "utils/misc.h" +#include "wm/defs.h" +#include "wm/wm.h" #include "x.h" /// Event handling with X is complicated. Handling events with other events possibly @@ -496,9 +496,9 @@ static inline void ev_circulate_notify(session_t *ps, xcb_circulate_notify_event } } -static inline void expose_root(session_t *ps, const rect_t *rects, int nrects) { +static inline void expose_root(session_t *ps, const rect_t *rects, size_t nrects) { region_t region; - pixman_region32_init_rects(®ion, rects, nrects); + pixman_region32_init_rects(®ion, rects, (int)nrects); add_damage(ps, ®ion); pixman_region32_fini(®ion); } @@ -506,27 +506,19 @@ static inline void expose_root(session_t *ps, const rect_t *rects, int nrects) { static inline void ev_expose(session_t *ps, xcb_expose_event_t *ev) { if (ev->window == ps->c.screen_info->root || (ps->overlay && ev->window == ps->overlay)) { - int more = ev->count + 1; - if (ps->n_expose == ps->size_expose) { - if (ps->expose_rects) { - ps->expose_rects = - crealloc(ps->expose_rects, ps->size_expose + more); - ps->size_expose += more; - } else { - ps->expose_rects = ccalloc(more, rect_t); - ps->size_expose = more; - } - } + dynarr_reserve(ps->expose_rects, ev->count + 1); - ps->expose_rects[ps->n_expose].x1 = ev->x; - ps->expose_rects[ps->n_expose].y1 = ev->y; - ps->expose_rects[ps->n_expose].x2 = ev->x + ev->width; - ps->expose_rects[ps->n_expose].y2 = ev->y + ev->height; - ps->n_expose++; + rect_t new_rect = { + .x1 = ev->x, + .y1 = ev->y, + .x2 = ev->x + ev->width, + .y2 = ev->y + ev->height, + }; + dynarr_push(ps->expose_rects, new_rect); if (ev->count == 0) { - expose_root(ps, ps->expose_rects, ps->n_expose); - ps->n_expose = 0; + expose_root(ps, ps->expose_rects, dynarr_len(ps->expose_rects)); + dynarr_clear_pod(ps->expose_rects); } } } diff --git a/src/inspect.c b/src/inspect.c index 89bd4cb416..3c5de79889 100644 --- a/src/inspect.c +++ b/src/inspect.c @@ -15,12 +15,11 @@ #include "c2.h" #include "common.h" #include "config.h" -#include "err.h" #include "log.h" #include "options.h" -#include "utils.h" -#include "win.h" -#include "win_defs.h" +#include "utils/misc.h" +#include "wm/defs.h" +#include "wm/win.h" #include "x.h" static struct managed_win * diff --git a/src/log.c b/src/log.c index 35c0fbbf07..0ac4b4f2c1 100644 --- a/src/log.c +++ b/src/log.c @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + #include #include #include @@ -10,13 +13,11 @@ #ifdef CONFIG_OPENGL #include -#include "backend/gl/gl_common.h" -#include "backend/gl/glx.h" #endif #include "compiler.h" #include "log.h" -#include "utils.h" +#include "utils/misc.h" thread_local struct log *tls_logger; diff --git a/src/meson.build b/src/meson.build index 002f9c6015..bc56a98446 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,11 +7,10 @@ base_deps = [ libev ] -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', - 'vblank.c', 'transition.c', 'wm.c', 'renderer/layout.c', 'renderer/command_builder.c', - 'renderer/renderer.c', 'renderer/damage.c', 'config_libconfig.c', 'inspect.c', 'script.c', 'api.c') ] +srcs = [ files('picom.c', 'c2.c', 'x.c', 'config.c', 'vsync.c', + 'diagnostic.c', 'render.c', 'log.c', + 'options.c', 'event.c', 'atom.c', + 'vblank.c','config_libconfig.c', 'inspect.c', 'api.c') ] picom_inc = include_directories(['.', '../include']) cflags = [] @@ -99,6 +98,10 @@ elif (host_system == 'freebsd' or host_system == 'netbsd' or endif subdir('backend') +subdir('wm') +subdir('renderer') +subdir('transition') +subdir('utils') picom = executable('picom', srcs, c_args: cflags, dependencies: [ base_deps, deps, test_h_dep ], diff --git a/src/opengl.c b/src/opengl.c index 704461fc3f..e80b209984 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -1,13 +1,5 @@ // SPDX-License-Identifier: MIT -/* - * Compton - a compositor for X11 - * - * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard - * - * Copyright (c) 2011-2013, Christopher Jeffrey - * See LICENSE-mit for more information. - * - */ +// Copyright (c) 2011-2013, Christopher Jeffrey #include #include @@ -21,14 +13,13 @@ #include "common.h" #include "compiler.h" #include "config.h" -#include "kernel.h" #include "log.h" #include "region.h" -#include "string_utils.h" -#include "uthash_extra.h" -#include "utils.h" -#include "win.h" -#include "wm.h" +#include "utils/kernel.h" +#include "utils/misc.h" +#include "utils/str.h" +#include "wm/win.h" +#include "wm/wm.h" #include "opengl.h" @@ -1538,3 +1529,13 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, return true; } + +/** + * Free GLX part of win. + */ +void free_win_res_glx(session_t *ps, struct managed_win *w) { + free_paint_glx(ps, &w->paint); + free_paint_glx(ps, &w->shadow_paint); + free_glx_bc(ps, &w->glx_blur_cache); + free_texture(ps, &w->glx_texture_bg); +} diff --git a/src/opengl.h b/src/opengl.h index 5bf92ed438..a10d960d42 100644 --- a/src/opengl.h +++ b/src/opengl.h @@ -1,24 +1,14 @@ // SPDX-License-Identifier: MIT -/* - * Compton - a compositor for X11 - * - * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard - * - * Copyright (c) 2011-2013, Christopher Jeffrey - * See LICENSE-mit for more information. - * - */ +// Copyright (c) 2011-2013, Christopher Jeffrey #pragma once #include "common.h" #include "compiler.h" -#include "log.h" #include "region.h" #include "render.h" -#include "win.h" +#include "wm/win.h" -#include #include #include #include @@ -234,11 +224,4 @@ static inline void free_paint_glx(session_t *ps, paint_t *ppaint) { /** * Free GLX part of win. */ -static inline void free_win_res_glx(session_t *ps, struct managed_win *w) { - free_paint_glx(ps, &w->paint); - free_paint_glx(ps, &w->shadow_paint); -#ifdef CONFIG_OPENGL - free_glx_bc(ps, &w->glx_blur_cache); - free_texture(ps, &w->glx_texture_bg); -#endif -} +void free_win_res_glx(session_t *ps, struct managed_win *w); diff --git a/src/options.c b/src/options.c index 9b7c763db5..b942886a63 100644 --- a/src/options.c +++ b/src/options.c @@ -13,13 +13,15 @@ #include // for xcb_render_fixed_t, XXX #include "backend/backend.h" +#include "c2.h" #include "common.h" #include "config.h" #include "log.h" #include "options.h" -#include "string_utils.h" -#include "utils.h" -#include "win.h" +#include "transition/script.h" +#include "utils/dynarr.h" +#include "utils/misc.h" +#include "utils/str.h" #include "x.h" #pragma GCC diagnostic error "-Wunused-parameter" @@ -744,6 +746,13 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all return true; } +static void script_ptr_deinit(struct script **ptr) { + if (*ptr) { + script_free(*ptr); + *ptr = NULL; + } +} + static bool sanitize_options(struct options *opt) { if (opt->use_legacy_backends) { if (opt->legacy_backend == BKEND_EGL) { @@ -781,18 +790,13 @@ static bool sanitize_options(struct options *opt) { opt->blur_method = BLUR_METHOD_NONE; } - if (opt->number_of_scripts > 0) { + if (dynarr_len(opt->all_scripts) > 0) { log_warn("Custom animations are not supported by the legacy " "backends. Disabling animations."); for (size_t i = 0; i < ARR_SIZE(opt->animations); i++) { opt->animations[i].script = NULL; } - for (int i = 0; i < opt->number_of_scripts; i++) { - script_free(opt->all_scripts[i]); - } - free(opt->all_scripts); - opt->all_scripts = NULL; - opt->number_of_scripts = 0; + dynarr_free(opt->all_scripts, script_ptr_deinit); } if (opt->window_shader_fg || opt->window_shader_fg_rules) { @@ -997,11 +1001,7 @@ void options_destroy(struct options *options) { free(options->blur_kerns); free(options->glx_fshader_win_str); - for (int i = 0; i < options->number_of_scripts; i++) { - script_free(options->all_scripts[i]); - options->all_scripts[i] = NULL; - } - free(options->all_scripts); + dynarr_free(options->all_scripts, script_ptr_deinit); memset(options->animations, 0, sizeof(options->animations)); list_foreach_safe(struct included_config_file, i, &options->included_config_files, diff --git a/src/picom.c b/src/picom.c index 73cf5b2a87..6470d62a94 100644 --- a/src/picom.c +++ b/src/picom.c @@ -40,38 +40,39 @@ #include #include -#include "api_internal.h" -#include "common.h" -#include "compiler.h" -#include "config.h" -#include "inspect.h" -#include "kernel.h" -#include "picom.h" -#include "win_defs.h" -#include "wm.h" #ifdef CONFIG_OPENGL #include "opengl.h" #endif + +#include "api_internal.h" #include "atom.h" #include "backend/backend.h" #include "c2.h" +#include "common.h" +#include "compiler.h" +#include "config.h" #include "dbus.h" #include "diagnostic.h" #include "event.h" -#include "file_watch.h" -#include "list.h" +#include "inspect.h" #include "log.h" #include "options.h" +#include "picom.h" #include "region.h" #include "render.h" #include "renderer/command_builder.h" #include "renderer/layout.h" #include "renderer/renderer.h" -#include "statistics.h" -#include "uthash_extra.h" -#include "utils.h" +#include "utils/dynarr.h" +#include "utils/file_watch.h" +#include "utils/kernel.h" +#include "utils/list.h" +#include "utils/misc.h" +#include "utils/statistics.h" +#include "utils/uthash_extra.h" #include "vblank.h" -#include "win.h" +#include "wm/defs.h" +#include "wm/wm.h" #include "x.h" /// Get session_t pointer from a pointer to a member of session_t @@ -2026,8 +2027,6 @@ static session_t *session_init(int argc, char **argv, Display *dpy, .quit = false, .expose_rects = NULL, - .size_expose = 0, - .n_expose = 0, .black_picture = XCB_NONE, .cshadow_picture = XCB_NONE, @@ -2537,6 +2536,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy, } ps->command_builder = command_builder_new(); + ps->expose_rects = dynarr_new(rect_t, 0); ps->pending_updates = true; @@ -2607,7 +2607,7 @@ static void session_destroy(session_t *ps) { free_paint(ps, &ps->tgt_buffer); pixman_region32_fini(&ps->screen_reg); - free(ps->expose_rects); + dynarr_free_pod(ps->expose_rects); x_free_monitor_info(&ps->monitors); diff --git a/src/picom.h b/src/picom.h index 269b9ce47b..f6f7b30994 100644 --- a/src/picom.h +++ b/src/picom.h @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT -// Copyright (c) +// Copyright (c) 2011-2013, Christopher Jeffrey +// Copyright (c) 2018 Yuxuan Shui // Throw everything in here. // !!! DON'T !!! @@ -20,7 +21,7 @@ #include "log.h" // XXX clean up #include "region.h" #include "render.h" -#include "win.h" +#include "wm/win.h" #include "x.h" enum root_flags { @@ -88,14 +89,6 @@ static inline bool array_wid_exists(const xcb_window_t *arr, int count, xcb_wind return false; } -#ifndef CONFIG_OPENGL -static inline void free_paint_glx(session_t *ps attr_unused, paint_t *p attr_unused) { -} -static inline void -free_win_res_glx(session_t *ps attr_unused, struct managed_win *w attr_unused) { -} -#endif - /** * Dump an drawable's info. */ diff --git a/src/region.h b/src/region.h index 838c71f0c4..1e2261f7d7 100644 --- a/src/region.h +++ b/src/region.h @@ -8,7 +8,7 @@ #include #include "log.h" -#include "utils.h" +#include "utils/misc.h" typedef struct pixman_region32 pixman_region32_t; typedef struct pixman_box32 pixman_box32_t; diff --git a/src/render.c b/src/render.c index 10a79a1da0..e2719ccf19 100644 --- a/src/render.c +++ b/src/render.c @@ -27,12 +27,12 @@ #include "compiler.h" #include "config.h" -#include "kernel.h" #include "log.h" #include "region.h" -#include "utils.h" +#include "utils/kernel.h" +#include "utils/misc.h" #include "vsync.h" -#include "win.h" +#include "wm/win.h" #include "x.h" #include "backend/backend_common.h" diff --git a/src/renderer/command_builder.c b/src/renderer/command_builder.c index a2f9af76ae..c3c86a7893 100644 --- a/src/renderer/command_builder.c +++ b/src/renderer/command_builder.c @@ -1,11 +1,12 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui -#include "command_builder.h" - #include "common.h" #include "layout.h" -#include "win.h" +#include "utils/dynarr.h" +#include "wm/win.h" + +#include "command_builder.h" /// Generate commands for rendering the body of the window in `layer`. /// @@ -205,14 +206,14 @@ command_for_blur(struct layer *layer, struct backend_command *cmd, static inline void command_builder_apply_transparent_clipping(struct layout *layout, region_t *scratch_region) { // Going from top down, apply transparent-clipping - if (layout->len == 0) { + if (dynarr_is_empty(layout->layers)) { return; } pixman_region32_clear(scratch_region); auto end = &layout->commands[layout->number_of_commands - 1]; auto begin = &layout->commands[layout->first_layer_start - 1]; - auto layer = &layout->layers[layout->len - 1]; + auto layer = &dynarr_last(layout->layers); // `layer_start` is one before the first command for this layer auto layer_start = end - layer->number_of_commands; for (auto i = end; i != begin; i--) { @@ -368,8 +369,7 @@ void command_builder_build(struct command_builder *cb, struct layout *layout, bo const struct win_option *wintype_options) { unsigned ncmds = 1; - for (unsigned i = 0; i < layout->len; i++) { - auto layer = &layout->layers[i]; + dynarr_foreach(layout->layers, layer) { auto mode = win_calc_mode_raw(layer->win); if (layer->win->blur_background && layer->blur_opacity > 0 && (force_blend || mode == WMODE_TRANS || layer->opacity < 1.0 || @@ -391,8 +391,7 @@ void command_builder_build(struct command_builder *cb, struct layout *layout, bo layout->commands = list->commands; auto cmd = &layout->commands[ncmds - 1]; - for (int i = to_int_checked(layout->len) - 1; i >= 0; i--) { - auto layer = &layout->layers[i]; + dynarr_foreach_rev(layout->layers, layer) { auto last = cmd; auto frame_region = win_get_region_frame_local_by_val(layer->win); pixman_region32_translate(&frame_region, layer->window.origin.x, diff --git a/src/renderer/command_builder.h b/src/renderer/command_builder.h index a33bb985b8..a8c9c24e1e 100644 --- a/src/renderer/command_builder.h +++ b/src/renderer/command_builder.h @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui + #pragma once #include diff --git a/src/renderer/damage.c b/src/renderer/damage.c index a7bf2a456c..e154685cd4 100644 --- a/src/renderer/damage.c +++ b/src/renderer/damage.c @@ -1,8 +1,13 @@ -#include "damage.h" +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui #include "layout.h" #include "region.h" -#include "win.h" +#include "utils/dynarr.h" +#include "wm/win.h" + +#include "damage.h" + static inline bool attr_unused layer_key_eq(const struct layer_key *a, const struct layer_key *b) { if (!a->generation || !b->generation) { @@ -157,15 +162,12 @@ void layout_manager_damage(struct layout_manager *lm, unsigned buffer_age, for (unsigned l = 0; l <= buffer_age; l++) { log_trace("Layout[%d]: ", -l); auto layout = layout_manager_layout(lm, l); - for (unsigned i = 0; i < layout->len; i++) { + dynarr_foreach(layout->layers, layer) { log_trace("\t%#010x %dx%d+%dx%d (prev %d, next %d)", - layout->layers[i].key.window, - layout->layers[i].window.size.width, - layout->layers[i].window.size.height, - layout->layers[i].window.origin.x, - layout->layers[i].window.origin.y, - layout->layers[i].prev_rank, - layout->layers[i].next_rank); + layer->key.window, layer->window.size.width, + layer->window.size.height, + layer->window.origin.x, layer->window.origin.y, + layer->prev_rank, layer->next_rank); } } } @@ -203,7 +205,7 @@ void layout_manager_damage(struct layout_manager *lm, unsigned buffer_age, // Skip layers in the past layout doesn't contain a window that has a // match in the remaining layers of the current layout; and vice versa. - while (past_layer_rank_target < past_layout->len) { + while (past_layer_rank_target < dynarr_len(past_layout->layers)) { past_layer_curr_rank = layer_next_rank(lm, buffer_age, past_layer_rank_target); if (past_layer_curr_rank >= (int)curr_layer_rank) { @@ -211,7 +213,7 @@ void layout_manager_damage(struct layout_manager *lm, unsigned buffer_age, } past_layer_rank_target++; }; - while (curr_layer_rank_target < curr_layout->len) { + while (curr_layer_rank_target < dynarr_len(curr_layout->layers)) { curr_layer_past_rank = layer_prev_rank(lm, buffer_age, curr_layer_rank_target); if (curr_layer_past_rank >= (int)past_layer_rank) { @@ -255,10 +257,11 @@ void layout_manager_damage(struct layout_manager *lm, unsigned buffer_age, curr_layer += 1; } - if (past_layer_rank >= past_layout->len || curr_layer_rank >= curr_layout->len) { + if (past_layer_rank >= dynarr_len(past_layout->layers) || + curr_layer_rank >= dynarr_len(curr_layout->layers)) { // No more matching layers left. - assert(past_layer_rank >= past_layout->len && - curr_layer_rank >= curr_layout->len); + assert(past_layer_rank >= dynarr_len(past_layout->layers) && + curr_layer_rank >= dynarr_len(curr_layout->layers)); break; } diff --git a/src/renderer/damage.h b/src/renderer/damage.h index 5111cbccb3..14e4d9c796 100644 --- a/src/renderer/damage.h +++ b/src/renderer/damage.h @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + #pragma once #include diff --git a/src/renderer/layout.c b/src/renderer/layout.c index 1c74299efe..7ee8567df5 100644 --- a/src/renderer/layout.c +++ b/src/renderer/layout.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui + #include #include @@ -7,11 +8,12 @@ #include "command_builder.h" #include "common.h" -#include "list.h" #include "region.h" -#include "utils.h" -#include "win.h" -#include "wm.h" +#include "utils/dynarr.h" +#include "utils/list.h" +#include "utils/misc.h" +#include "wm/win.h" +#include "wm/wm.h" #include "layout.h" struct layer_index { @@ -116,11 +118,16 @@ static bool layer_from_window(struct layer *out_layer, struct managed_win *w, iv return to_paint; } +static void layer_deinit(struct layer *layer) { + pixman_region32_fini(&layer->damaged); +} + +static void layer_init(struct layer *layer) { + pixman_region32_init(&layer->damaged); +} + static void layout_deinit(struct layout *layout) { - for (unsigned i = 0; i < layout->len; i++) { - pixman_region32_fini(&layout->layers[i].damaged); - } - free(layout->layers); + dynarr_free(layout->layers, layer_deinit); command_builder_command_list_free(layout->commands); *layout = (struct layout){}; } @@ -135,6 +142,7 @@ struct layout_manager *layout_manager_new(unsigned max_buffer_age) { pixman_region32_init(&planner->scratch_region); for (unsigned i = 0; i <= max_buffer_age; i++) { planner->layouts[i] = (struct layout){}; + planner->layouts[i].layers = dynarr_new(struct layer, 5); } return planner; } @@ -175,17 +183,7 @@ void layout_manager_append_layout(struct layout_manager *lm, struct wm *wm, layout->root_image_generation = root_pixmap_generation; unsigned nlayers = wm_stack_num_managed_windows(wm); - if (nlayers > layout->capacity) { - struct layer *new_layers = - realloc(layout->layers, nlayers * sizeof(struct layer)); - BUG_ON(new_layers == NULL); - for (unsigned i = layout->capacity; i < nlayers; i++) { - pixman_region32_init(&new_layers[i].damaged); - } - layout->capacity = nlayers; - layout->layers = new_layers; - } - + dynarr_resize(layout->layers, nlayers, layer_init, layer_deinit); layout->size = size; unsigned rank = 0; @@ -209,7 +207,7 @@ void layout_manager_append_layout(struct layout_manager *lm, struct wm *wm, rank++; assert(rank <= nlayers); } - layout->len = rank; + dynarr_resize(layout->layers, rank, layer_init, layer_deinit); // Update indices. If a layer exist in both prev_layout and current layout, // we could update the index using next_rank; if a layer no longer exist in @@ -224,8 +222,8 @@ void layout_manager_append_layout(struct layout_manager *lm, struct wm *wm, } // And finally, if a layer in current layout didn't exist in prev_layout, add a // new index for it. - for (unsigned i = 0; i < layout->len; i++) { - if (layout->layers[i].prev_rank != -1) { + dynarr_foreach(layout->layers, layer) { + if (layer->prev_rank != -1) { continue; } if (!list_is_empty(&lm->free_indices)) { @@ -235,8 +233,8 @@ void layout_manager_append_layout(struct layout_manager *lm, struct wm *wm, } else { index = cmalloc(struct layer_index); } - index->key = layout->layers[i].key; - index->index = i; + index->key = layer->key; + index->index = to_u32_checked(layer - layout->layers); HASH_ADD(hh, lm->layer_indices, key, sizeof(index->key), index); } } diff --git a/src/renderer/layout.h b/src/renderer/layout.h index c50a975e97..25e60503f6 100644 --- a/src/renderer/layout.h +++ b/src/renderer/layout.h @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui + #pragma once #include #include @@ -75,11 +76,7 @@ struct layout { ivec2 size; /// The root image generation, see `struct session::root_image_generation` uint64_t root_image_generation; - /// Number of layers in `layers` - unsigned len; - /// Capacity of `layers` - unsigned capacity; - /// Layers as a flat array, from bottom to top in stack order. + /// Layers as a flat array, from bottom to top in stack order. This is a dynarr. struct layer *layers; /// Number of commands in `commands` unsigned number_of_commands; diff --git a/src/renderer/meson.build b/src/renderer/meson.build new file mode 100644 index 0000000000..aaffcd3633 --- /dev/null +++ b/src/renderer/meson.build @@ -0,0 +1 @@ +srcs += [ files('command_builder.c', 'damage.c', 'layout.c', 'renderer.c') ] diff --git a/src/renderer/renderer.c b/src/renderer/renderer.c index d8a4068046..2a6807cd46 100644 --- a/src/renderer/renderer.c +++ b/src/renderer/renderer.c @@ -6,12 +6,13 @@ #include #include -#include "../picom.h" #include "backend/backend.h" #include "backend/backend_common.h" #include "command_builder.h" #include "damage.h" #include "layout.h" +#include "picom.h" +#include "utils/dynarr.h" struct renderer { /// Intermediate image to hold what will be presented to the back buffer. @@ -41,32 +42,10 @@ struct renderer { void *shadow_blur_context; struct conv *shadow_kernel; + /// A dynarr of region_t for storing culled masks region_t *culled_masks; - size_t culled_masks_capacity; }; -static void renderer_reallocate_culled_masks(struct renderer *r, size_t capacity) { - if (capacity <= r->culled_masks_capacity && - capacity > max2(1, r->culled_masks_capacity / 2)) { - return; - } - for (size_t i = capacity; i < r->culled_masks_capacity; i++) { - pixman_region32_fini(&r->culled_masks[i]); - } - void *new_storage = NULL; - if (capacity > 0) { - new_storage = realloc(r->culled_masks, sizeof(region_t[capacity])); - allocchk(new_storage); - } else { - free(r->culled_masks); - } - r->culled_masks = new_storage; - for (size_t i = r->culled_masks_capacity; i < capacity; i++) { - pixman_region32_init(&r->culled_masks[i]); - } - r->culled_masks_capacity = capacity; -} - void renderer_free(struct backend_base *backend, struct renderer *r) { if (r->white_image) { backend->ops.release_image(backend, r->white_image); @@ -101,7 +80,7 @@ void renderer_free(struct backend_base *backend, struct renderer *r) { } free(r->monitor_repaint_copy); } - renderer_reallocate_culled_masks(r, 0); + dynarr_free(r->culled_masks, pixman_region32_fini); free(r); } @@ -155,6 +134,7 @@ renderer_init(struct renderer *renderer, struct backend_base *backend, sum_kernel_preprocess(renderer->shadow_kernel); } renderer->max_buffer_age = backend->ops.max_buffer_age(backend) + 1; + renderer->culled_masks = dynarr_new(region_t, 0); return true; } @@ -573,7 +553,8 @@ bool renderer_render(struct renderer *r, struct backend_base *backend, layout_manager_damage(lm, (unsigned)buffer_age, blur_size, &damage_region); } - renderer_reallocate_culled_masks(r, layout->number_of_commands); + dynarr_resize(r->culled_masks, layout->number_of_commands, pixman_region32_init, + pixman_region32_fini); commands_cull_with_damage(layout, &damage_region, blur_size, r->culled_masks); auto now = get_time_timespec(); diff --git a/src/rtkit.c b/src/rtkit.c index ecb89a252e..186750f8c3 100644 --- a/src/rtkit.c +++ b/src/rtkit.c @@ -23,7 +23,7 @@ #include "log.h" #include "rtkit.h" -#include "utils.h" +#include "utils/misc.h" #define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1" #define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1" diff --git a/src/statistics.c b/src/statistics.c deleted file mode 100644 index 1a3b335fc4..0000000000 --- a/src/statistics.c +++ /dev/null @@ -1,85 +0,0 @@ -//! Rendering statistics -//! -//! Tracks how long it takes to render a frame, for measuring performance, and for pacing -//! the frames. - -#include "statistics.h" -#include "log.h" -#include "utils.h" - -void render_statistics_init(struct render_statistics *rs, int window_size) { - *rs = (struct render_statistics){0}; - - rolling_window_init(&rs->render_times, window_size); - rolling_quantile_init_with_tolerance(&rs->render_time_quantile, window_size, - /* q */ 0.98, /* tolerance */ 0.01); -} - -void render_statistics_add_vblank_time_sample(struct render_statistics *rs, int time_us) { - auto sample_sd = sqrt(cumulative_mean_and_var_get_var(&rs->vblank_time_us)); - auto current_estimate = render_statistics_get_vblank_time(rs); - if (current_estimate != 0 && fabs((double)time_us - current_estimate) > sample_sd * 3) { - // Deviated from the mean by more than 3 sigma (p < 0.003) - log_debug("vblank time outlier: %d %f %f", time_us, rs->vblank_time_us.mean, - cumulative_mean_and_var_get_var(&rs->vblank_time_us)); - // An outlier sample, this could mean things like refresh rate changes, so - // we reset the statistics. This could also be benign, but we like to be - // cautious. - cumulative_mean_and_var_init(&rs->vblank_time_us); - } - - if (rs->vblank_time_us.mean != 0) { - auto nframes_in_10_seconds = - (unsigned int)(10. * 1000000. / rs->vblank_time_us.mean); - if (rs->vblank_time_us.n > 20 && rs->vblank_time_us.n > nframes_in_10_seconds) { - // We collected 10 seconds worth of samples, we assume the - // estimated refresh rate is stable. We will still reset the - // statistics if we get an outlier sample though, see above. - return; - } - } - cumulative_mean_and_var_update(&rs->vblank_time_us, time_us); -} - -void render_statistics_add_render_time_sample(struct render_statistics *rs, int time_us) { - int oldest; - if (rolling_window_push_back(&rs->render_times, time_us, &oldest)) { - rolling_quantile_pop_front(&rs->render_time_quantile, oldest); - } - - rolling_quantile_push_back(&rs->render_time_quantile, time_us); -} - -/// How much time budget we should give to the backend for rendering, in microseconds. -unsigned int render_statistics_get_budget(struct render_statistics *rs) { - if (rs->render_times.nelem < rs->render_times.window_size) { - // No valid render time estimates yet. Assume maximum budget. - return UINT_MAX; - } - - // N-th percentile of render times, see render_statistics_init for N. - auto render_time_percentile = - rolling_quantile_estimate(&rs->render_time_quantile, &rs->render_times); - return (unsigned int)render_time_percentile; -} - -unsigned int render_statistics_get_vblank_time(struct render_statistics *rs) { - if (rs->vblank_time_us.n <= 20 || rs->vblank_time_us.mean < 100) { - // Not enough samples yet, or the vblank time is too short to be - // meaningful. Assume maximum budget. - return 0; - } - return (unsigned int)rs->vblank_time_us.mean; -} - -void render_statistics_reset(struct render_statistics *rs) { - rolling_window_reset(&rs->render_times); - rolling_quantile_reset(&rs->render_time_quantile); - rs->vblank_time_us = (struct cumulative_mean_and_var){0}; -} - -void render_statistics_destroy(struct render_statistics *rs) { - render_statistics_reset(rs); - rolling_window_destroy(&rs->render_times); - rolling_quantile_destroy(&rs->render_time_quantile); -} diff --git a/src/statistics.h b/src/statistics.h deleted file mode 100644 index a111486111..0000000000 --- a/src/statistics.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "utils.h" - -#define NTIERS (3) - -struct render_statistics { - /// Rolling window of rendering times (in us) and the tiers they belong to. - /// We keep track of the tiers because the vblank time estimate can change over - /// time. - struct rolling_window render_times; - /// Estimate the 95-th percentile of rendering times - struct rolling_quantile render_time_quantile; - /// Time between each vblanks - struct cumulative_mean_and_var vblank_time_us; -}; - -void render_statistics_init(struct render_statistics *rs, int window_size); -void render_statistics_reset(struct render_statistics *rs); -void render_statistics_destroy(struct render_statistics *rs); - -void render_statistics_add_vblank_time_sample(struct render_statistics *rs, int time_us); -void render_statistics_add_render_time_sample(struct render_statistics *rs, int time_us); - -/// How much time budget we should give to the backend for rendering, in microseconds. -unsigned int render_statistics_get_budget(struct render_statistics *rs); - -/// Return the measured vblank interval in microseconds. Returns 0 if not enough -/// samples have been collected yet. -unsigned int render_statistics_get_vblank_time(struct render_statistics *rs); diff --git a/src/transition.c b/src/transition/curve.c similarity index 98% rename from src/transition.c rename to src/transition/curve.c index 56c11ac4d2..812e87d564 100644 --- a/src/transition.c +++ b/src/transition/curve.c @@ -6,9 +6,10 @@ #include #include "compiler.h" -#include "string_utils.h" -#include "transition.h" -#include "utils.h" +#include "utils/misc.h" +#include "utils/str.h" + +#include "curve.h" static double curve_sample_linear(const struct curve *this attr_unused, double progress) { return progress; diff --git a/src/transition.h b/src/transition/curve.h similarity index 97% rename from src/transition.h rename to src/transition/curve.h index d6bcab299f..72b2de53ac 100644 --- a/src/transition.h +++ b/src/transition/curve.h @@ -3,7 +3,6 @@ #pragma once #include -#include "compiler.h" // ========================== Interpolators ========================== diff --git a/src/transition/meson.build b/src/transition/meson.build new file mode 100644 index 0000000000..17a618d688 --- /dev/null +++ b/src/transition/meson.build @@ -0,0 +1 @@ +srcs += [ files('curve.c', 'script.c') ] diff --git a/src/script.c b/src/transition/script.c similarity index 99% rename from src/script.c rename to src/transition/script.c index 69d87d72f6..e5b7cf5df1 100644 --- a/src/script.c +++ b/src/transition/script.c @@ -1,15 +1,18 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui -#include "script.h" -#include + #include #include + +#include #include -#include "list.h" -#include "string_utils.h" -#include "transition.h" -#include "uthash_extra.h" -#include "utils.h" + +#include "utils/list.h" +#include "utils/str.h" +#include "utils/uthash_extra.h" + +#include "curve.h" +#include "script.h" enum op { OP_ADD = 0, diff --git a/src/script.h b/src/transition/script.h similarity index 96% rename from src/script.h rename to src/transition/script.h index 8d9e9d4c09..82c3bed7fd 100644 --- a/src/script.h +++ b/src/transition/script.h @@ -1,8 +1,12 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + #pragma once #include #include #include #include + #include struct script_context_info { diff --git a/src/cache.c b/src/utils/cache.c similarity index 90% rename from src/cache.c rename to src/utils/cache.c index 1e356e5212..b4d5e1dc55 100644 --- a/src/cache.c +++ b/src/utils/cache.c @@ -1,6 +1,11 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + +#include #include #include "cache.h" +#include "misc.h" struct cache_handle *cache_get(struct cache *c, const char *key, size_t keylen) { struct cache_handle *e; diff --git a/src/cache.h b/src/utils/cache.h similarity index 95% rename from src/cache.h rename to src/utils/cache.h index c0fa8df240..3a380dfda7 100644 --- a/src/cache.h +++ b/src/utils/cache.h @@ -1,7 +1,9 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + #pragma once #include -#include "utils.h" #define cache_entry(ptr, type, member) container_of(ptr, type, member) diff --git a/src/utils/dynarr.h b/src/utils/dynarr.h new file mode 100644 index 0000000000..a51f2d1f39 --- /dev/null +++ b/src/utils/dynarr.h @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + +#pragma once + +#include + +#include "misc.h" + +/// Dynamic array implementation, design is similar to sds [0]. +/// +/// Note this is not very type safe, be sure to annotate wherever you use this. And the +/// array won't have a fixed address in memory. +/// +/// [0]: https://github.com/antirez/sds + +struct dynarr_header { + size_t len; + size_t cap; +}; + +static inline void *dynarr_new_impl(size_t size, size_t nelem) { + struct dynarr_header *ret = calloc(1, sizeof(struct dynarr_header) + size * nelem); + allocchk(ret); + ret->len = 0; + ret->cap = nelem; + return ret + 1; +} + +static inline void *dynarr_reserve_impl(size_t size, void *arr, size_t nelem) { + struct dynarr_header *hdr = (struct dynarr_header *)arr - 1; + if (hdr->len + nelem > hdr->cap) { + hdr->cap = max2(max2(1, hdr->cap * 2), hdr->len + nelem); + auto new_hdr = realloc(hdr, sizeof(struct dynarr_header) + hdr->cap * size); + allocchk(new_hdr); + hdr = new_hdr; + } + return hdr + 1; +} + +static inline void *dynarr_shrink_to_fit_impl(size_t size, void *arr) { + struct dynarr_header *hdr = (struct dynarr_header *)arr - 1; + if (hdr->len < hdr->cap) { + hdr->cap = hdr->len; + auto new_hdr = realloc(hdr, sizeof(struct dynarr_header) + hdr->cap * size); + allocchk(new_hdr); + hdr = new_hdr; + } + return hdr + 1; +} + +static inline void dynarr_remove_impl(size_t size, void *arr, size_t idx) { + struct dynarr_header *hdr = (struct dynarr_header *)arr - 1; + BUG_ON(idx >= hdr->len); + memmove((char *)arr + idx * size, (char *)arr + (idx + 1) * size, + (hdr->len - idx - 1) * size); + hdr->len--; +} + +static inline void dynarr_remove_swap_impl(size_t size, void *arr, size_t idx) { + struct dynarr_header *hdr = (struct dynarr_header *)arr - 1; + BUG_ON(idx >= hdr->len); + hdr->len--; + if (idx != hdr->len) { + memcpy((char *)arr + idx * size, (char *)arr + hdr->len * size, size); + } +} + +/// Create a new dynamic array with capacity `cap` for type `type`. +#define dynarr_new(type, cap) ((type *)dynarr_new_impl(sizeof(type), cap)) +/// Free a dynamic array, destructing each element with `dtor`. +#define dynarr_free(arr, dtor) \ + do { \ + dynarr_clear(arr, dtor); \ + free((struct dynarr_header *)(arr)-1); \ + (arr) = NULL; \ + } while (0) +#define dynarr_free_pod(arr) dynarr_free(arr, (void (*)(typeof(arr)))NULL) + +/// Expand the capacity of the array so it can hold at least `nelem` more elements +#define dynarr_reserve(arr, nelem) \ + ((arr) = dynarr_reserve_impl(sizeof(typeof(*(arr))), (void *)(arr), (nelem))) +/// Resize the array to `newlen`. If `newlen` is greater than the current length, the +/// new elements will be initialized with `init`. If `newlen` is less than the current +/// length, the excess elements will be destructed with `dtor`. +#define dynarr_resize(arr, newlen, init, dtor) \ + do { \ + BUG_ON((arr) == NULL); \ + dynarr_reserve((arr), (newlen)-dynarr_len(arr)); \ + if ((init) != NULL) { \ + for (size_t i = dynarr_len(arr); i < (newlen); i++) { \ + (init)((arr) + i); \ + } \ + } \ + if ((dtor) != NULL) { \ + for (size_t i = (newlen); i < dynarr_len(arr); i++) { \ + (dtor)((arr) + i); \ + } \ + } \ + dynarr_len(arr) = (newlen); \ + } while (0) +/// Resize the array to `newlen`, for types that can be trivially constructed and +/// destructed. +#define dynarr_resize_pod(arr, newlen) \ + dynarr_resize(arr, newlen, (void (*)(typeof(arr)))NULL, (void (*)(typeof(arr)))NULL) +/// Shrink the capacity of the array to its length. +#define dynarr_shrink_to_fit(arr) \ + ((arr) = dynarr_shrink_to_fit_impl(sizeof(typeof(*(arr))), (void *)(arr))) +/// Push an element to the end of the array +#define dynarr_push(arr, item) \ + do { \ + dynarr_reserve(arr, 1); \ + (arr)[dynarr_len(arr)++] = (item); \ + } while (0) +/// Pop an element from the end of the array +#define dynarr_pop(arr) ((arr)[--dynarr_len(arr)]) +/// Remove an element from the array by shifting the rest of the array forward. +#define dynarr_remove(arr, idx) \ + dynarr_remove_impl(sizeof(typeof(*(arr))), (void *)(arr), idx) +/// Remove an element from the array by swapping it with the last element. +#define dynarr_remove_swap(arr, idx) \ + dynarr_remove_swap_impl(sizeof(typeof(*(arr))), (void *)(arr), idx) +/// Return the length of the array +#define dynarr_len(arr) (((struct dynarr_header *)(arr)-1)->len) +/// Return the capacity of the array +#define dynarr_cap(arr) (((struct dynarr_header *)(arr)-1)->cap) +/// Return the last element of the array +#define dynarr_last(arr) ((arr)[dynarr_len(arr) - 1]) +/// Return the pointer just past the last element of the array +#define dynarr_end(arr) ((arr) + dynarr_len(arr)) +/// Return whether the array is empty +#define dynarr_is_empty(arr) (dynarr_len(arr) == 0) + +/// Clear the array, destructing each element with `dtor`. +#define dynarr_clear(arr, dtor) \ + do { \ + if ((dtor) != NULL) { \ + for (size_t i = 0; i < dynarr_len(arr); i++) { \ + (dtor)((arr) + i); \ + } \ + } \ + dynarr_len(arr) = 0; \ + } while (0) +#define dynarr_clear_pod(arr) dynarr_clear(arr, (void (*)(typeof(arr)))NULL) + +/// Extend the array by copying `n` elements from `other` +#define dynarr_extend_from(arr, other, n) \ + do { \ + if ((n) > 0) { \ + dynarr_reserve(arr, n); \ + memcpy(dynarr_end(arr), other, sizeof(typeof(*(arr))[(n)])); \ + dynarr_len(arr) += (n); \ + } \ + } while (0) +/// Extend the array by `n` elements, initializing them with `init` +#define dynarr_extend_with(arr, init, n) \ + do { \ + if ((n) > 0) { \ + dynarr_resize(arr, dynarr_len(arr) + (n), init, \ + (void (*)(typeof(arr)))NULL); \ + } \ + } while (0) + +#define dynarr_foreach(arr, i) for (typeof(arr)(i) = (arr); (i) < dynarr_end(arr); (i)++) +#define dynarr_foreach_rev(arr, i) \ + for (typeof(arr)(i) = dynarr_end(arr) - 1; (i) >= (arr); (i)--) diff --git a/src/file_watch.c b/src/utils/file_watch.c similarity index 97% rename from src/file_watch.c rename to src/utils/file_watch.c index 4532fb6173..257358a855 100644 --- a/src/file_watch.c +++ b/src/utils/file_watch.c @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + #include #include #ifdef HAS_INOTIFY @@ -17,9 +20,8 @@ #include #include "file_watch.h" -#include "list.h" #include "log.h" -#include "utils.h" +#include "misc.h" struct watched_file { int wd; diff --git a/src/file_watch.h b/src/utils/file_watch.h similarity index 72% rename from src/file_watch.h rename to src/utils/file_watch.h index c249cd2bf9..6825e8c105 100644 --- a/src/file_watch.h +++ b/src/utils/file_watch.h @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + #pragma once #include diff --git a/src/kernel.c b/src/utils/kernel.c similarity index 99% rename from src/kernel.c rename to src/utils/kernel.c index a9865c9335..527079abda 100644 --- a/src/kernel.c +++ b/src/utils/kernel.c @@ -6,8 +6,7 @@ #include "compiler.h" #include "kernel.h" -#include "log.h" -#include "utils.h" +#include "misc.h" /// Sum a region convolution kernel. Region is defined by a width x height rectangle whose /// top left corner is at (x, y) diff --git a/src/kernel.h b/src/utils/kernel.h similarity index 100% rename from src/kernel.h rename to src/utils/kernel.h diff --git a/src/list.h b/src/utils/list.h similarity index 97% rename from src/list.h rename to src/utils/list.h index d63a764611..a85389df68 100644 --- a/src/list.h +++ b/src/utils/list.h @@ -1,8 +1,8 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + #pragma once #include -#include - -#include "utils.h" struct list_node { struct list_node *next, *prev; diff --git a/src/utils/meson.build b/src/utils/meson.build new file mode 100644 index 0000000000..a6272f826d --- /dev/null +++ b/src/utils/meson.build @@ -0,0 +1 @@ +srcs += [ files('cache.c', 'file_watch.c', 'kernel.c', 'statistics.c', 'str.c', 'misc.c') ] diff --git a/src/meta.h b/src/utils/meta.h similarity index 96% rename from src/meta.h rename to src/utils/meta.h index 4314356c32..5d0690bf78 100644 --- a/src/meta.h +++ b/src/utils/meta.h @@ -34,6 +34,7 @@ /// Return the number of arguments passed in binary, handles at most 31 elements #define VA_ARGS_LENGTH(...) _ARG33(0, ##__VA_ARGS__, RIOTA32(0)) +// clang-format off #define LIST_APPLY_000000(fn, sep, ...) #define LIST_APPLY_000001(fn, sep, x, ...) fn(x) #define LIST_APPLY_000010(fn, sep, x, ...) fn(x) sep() LIST_APPLY_000001(fn, sep, __VA_ARGS__) @@ -67,8 +68,8 @@ #define LIST_APPLY_011110(fn, sep, x, ...) fn(x) sep() LIST_APPLY_011101(fn, sep, __VA_ARGS__) #define LIST_APPLY_011111(fn, sep, x, ...) fn(x) sep() LIST_APPLY_011110(fn, sep, __VA_ARGS__) #define LIST_APPLY_(N, fn, sep, ...) CONCAT(LIST_APPLY_, N)(fn, sep, __VA_ARGS__) -#define LIST_APPLY(fn, sep, ...) \ - LIST_APPLY_(VA_ARGS_LENGTH(__VA_ARGS__), fn, sep, __VA_ARGS__) +#define LIST_APPLY(fn, sep, ...) LIST_APPLY_(VA_ARGS_LENGTH(__VA_ARGS__), fn, sep, __VA_ARGS__) +// clang-format on #define SEP_COMMA() , #define SEP_COLON() ; diff --git a/src/utils/misc.c b/src/utils/misc.c new file mode 100644 index 0000000000..73dfe64628 --- /dev/null +++ b/src/utils/misc.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + +#include +#include +#include +#include + +#include "compiler.h" +#include "misc.h" +#include "rtkit.h" +#include "str.h" + +/// Report allocation failure without allocating memory +void report_allocation_failure(const char *func, const char *file, unsigned int line) { + // Since memory allocation failed, we try to print this error message without any + // memory allocation. Since logging framework allocates memory (and might even + // have not been initialized yet), so we can't use it. + char buf[11]; + int llen = uitostr(line, buf); + const char msg1[] = " has failed to allocate memory, "; + const char msg2[] = ". Aborting...\n"; + const struct iovec v[] = { + {.iov_base = (void *)func, .iov_len = strlen(func)}, + {.iov_base = "()", .iov_len = 2}, + {.iov_base = (void *)msg1, .iov_len = sizeof(msg1) - 1}, + {.iov_base = "at ", .iov_len = 3}, + {.iov_base = (void *)file, .iov_len = strlen(file)}, + {.iov_base = ":", .iov_len = 1}, + {.iov_base = buf, .iov_len = (size_t)llen}, + {.iov_base = (void *)msg2, .iov_len = sizeof(msg2) - 1}, + }; + + ssize_t _ attr_unused = writev(STDERR_FILENO, v, ARR_SIZE(v)); + abort(); + + unreachable(); +} + +/// +/// Calculates next closest power of two of 32bit integer n +/// ref: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 +/// +int next_power_of_two(int n) { + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n++; + return n; +} +// Find the k-th smallest element in an array. +int quickselect(int *elems, int nelem, int k) { + int l = 0, r = nelem; // [l, r) is the range of candidates + while (l != r) { + int pivot = elems[l]; + int i = l, j = r; + while (i < j) { + while (i < j && elems[--j] >= pivot) { + } + elems[i] = elems[j]; + while (i < j && elems[++i] <= pivot) { + } + elems[j] = elems[i]; + } + elems[i] = pivot; + + if (i == k) { + break; + } + + if (i < k) { + l = i + 1; + } else { + r = i; + } + } + return elems[k]; +} + +/// Switch to real-time scheduling policy (SCHED_RR) if possible +/// +/// Make picom realtime to reduce latency, and make rendering times more predictable to +/// help pacing. +/// +/// This requires the user to set up permissions for the real-time scheduling. e.g. by +/// setting `ulimit -r`, or giving us the CAP_SYS_NICE capability. +void set_rr_scheduling(void) { + static thread_local bool already_tried = false; + if (already_tried) { + return; + } + already_tried = true; + + int priority = sched_get_priority_min(SCHED_RR); + + if (rtkit_make_realtime(0, priority)) { + log_info("Set realtime priority to %d with rtkit.", priority); + return; + } + + // Fallback to use pthread_setschedparam + struct sched_param param; + int old_policy; + int ret = pthread_getschedparam(pthread_self(), &old_policy, ¶m); + if (ret != 0) { + log_info("Couldn't get old scheduling priority."); + return; + } + + param.sched_priority = priority; + + ret = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); + if (ret != 0) { + log_info("Couldn't set real-time scheduling priority to %d.", priority); + return; + } + + log_info("Set real-time scheduling priority to %d.", priority); +} + +// vim: set noet sw=8 ts=8 : diff --git a/src/utils.h b/src/utils/misc.h similarity index 84% rename from src/utils.h rename to src/utils/misc.h index 115ab35398..92750fcf94 100644 --- a/src/utils.h +++ b/src/utils/misc.h @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2018 Yuxuan Shui + #pragma once #include #include @@ -407,97 +408,9 @@ static inline void free_charpp(char **str) { /// int next_power_of_two(int n); -struct rolling_window { - int *elem; - int elem_head, nelem; - int window_size; -}; - -void rolling_window_destroy(struct rolling_window *rw); -void rolling_window_reset(struct rolling_window *rw); -void rolling_window_init(struct rolling_window *rw, int size); -int rolling_window_pop_front(struct rolling_window *rw); -bool rolling_window_push_back(struct rolling_window *rw, int val, int *front); - -/// Copy the contents of the rolling window to an array. The array is assumed to -/// have enough space to hold the contents of the rolling window. -static inline void attr_unused rolling_window_copy_to_array(struct rolling_window *rw, - int *arr) { - // The length from head to the end of the array - auto head_len = (size_t)(rw->window_size - rw->elem_head); - if (head_len >= (size_t)rw->nelem) { - memcpy(arr, rw->elem + rw->elem_head, sizeof(int) * (size_t)rw->nelem); - } else { - auto tail_len = (size_t)((size_t)rw->nelem - head_len); - memcpy(arr, rw->elem + rw->elem_head, sizeof(int) * head_len); - memcpy(arr + head_len, rw->elem, sizeof(int) * tail_len); - } -} - -struct rolling_max; - -struct rolling_max *rolling_max_new(int capacity); -void rolling_max_destroy(struct rolling_max *rm); -void rolling_max_reset(struct rolling_max *rm); -void rolling_max_pop_front(struct rolling_max *rm, int front); -void rolling_max_push_back(struct rolling_max *rm, int val); -int rolling_max_get_max(struct rolling_max *rm); - -/// Estimate the mean and variance of random variable X using Welford's online -/// algorithm. -struct cumulative_mean_and_var { - double mean; - double m2; - unsigned int n; -}; - -static inline attr_unused void -cumulative_mean_and_var_init(struct cumulative_mean_and_var *cmv) { - *cmv = (struct cumulative_mean_and_var){0}; -} - -static inline attr_unused void -cumulative_mean_and_var_update(struct cumulative_mean_and_var *cmv, double x) { - if (cmv->n == UINT_MAX) { - // We have too many elements, let's keep the mean and variance. - return; - } - cmv->n++; - double delta = x - cmv->mean; - cmv->mean += delta / (double)cmv->n; - cmv->m2 += delta * (x - cmv->mean); -} - -static inline attr_unused double -cumulative_mean_and_var_get_var(struct cumulative_mean_and_var *cmv) { - if (cmv->n < 2) { - return 0; - } - return cmv->m2 / (double)(cmv->n - 1); -} - // Find the k-th smallest element in an array. int quickselect(int *elems, int nelem, int k); -/// A naive quantile estimator. -/// -/// Estimates the N-th percentile of a random variable X in a sliding window. -struct rolling_quantile { - int current_rank; - int min_target_rank, max_target_rank; - int estimate; - int capacity; - int *tmp_buffer; -}; - -void rolling_quantile_init(struct rolling_quantile *rq, int capacity, int mink, int maxk); -void rolling_quantile_init_with_tolerance(struct rolling_quantile *rq, int window_size, - double target, double tolerance); -void rolling_quantile_reset(struct rolling_quantile *rq); -void rolling_quantile_destroy(struct rolling_quantile *rq); -int rolling_quantile_estimate(struct rolling_quantile *rq, struct rolling_window *elements); -void rolling_quantile_push_back(struct rolling_quantile *rq, int x); -void rolling_quantile_pop_front(struct rolling_quantile *rq, int x); void set_rr_scheduling(void); // Some versions of the Android libc do not have timespec_get(), use diff --git a/src/utils.c b/src/utils/statistics.c similarity index 62% rename from src/utils.c rename to src/utils/statistics.c index b067180787..e10be61088 100644 --- a/src/utils.c +++ b/src/utils/statistics.c @@ -1,54 +1,16 @@ -#include -#include -#include -#include - -#include "compiler.h" -#include "rtkit.h" -#include "string_utils.h" -#include "test.h" -#include "utils.h" - -/// Report allocation failure without allocating memory -void report_allocation_failure(const char *func, const char *file, unsigned int line) { - // Since memory allocation failed, we try to print this error message without any - // memory allocation. Since logging framework allocates memory (and might even - // have not been initialized yet), so we can't use it. - char buf[11]; - int llen = uitostr(line, buf); - const char msg1[] = " has failed to allocate memory, "; - const char msg2[] = ". Aborting...\n"; - const struct iovec v[] = { - {.iov_base = (void *)func, .iov_len = strlen(func)}, - {.iov_base = "()", .iov_len = 2}, - {.iov_base = (void *)msg1, .iov_len = sizeof(msg1) - 1}, - {.iov_base = "at ", .iov_len = 3}, - {.iov_base = (void *)file, .iov_len = strlen(file)}, - {.iov_base = ":", .iov_len = 1}, - {.iov_base = buf, .iov_len = (size_t)llen}, - {.iov_base = (void *)msg2, .iov_len = sizeof(msg2) - 1}, - }; - - ssize_t _ attr_unused = writev(STDERR_FILENO, v, ARR_SIZE(v)); - abort(); - - unreachable(); -} +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui +/// Rendering statistics /// -/// Calculates next closest power of two of 32bit integer n -/// ref: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 -/// -int next_power_of_two(int n) { - n--; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - n |= n >> 16; - n++; - return n; -} +/// Tracks how long it takes to render a frame, for measuring performance, and for pacing +/// the frames. + +#include + +#include "log.h" +#include "misc.h" +#include "statistics.h" void rolling_window_destroy(struct rolling_window *rw) { free(rw->elem); @@ -194,35 +156,6 @@ TEST_CASE(rolling_max_test) { #undef NELEM } -// Find the k-th smallest element in an array. -int quickselect(int *elems, int nelem, int k) { - int l = 0, r = nelem; // [l, r) is the range of candidates - while (l != r) { - int pivot = elems[l]; - int i = l, j = r; - while (i < j) { - while (i < j && elems[--j] >= pivot) { - } - elems[i] = elems[j]; - while (i < j && elems[++i] <= pivot) { - } - elems[j] = elems[i]; - } - elems[i] = pivot; - - if (i == k) { - break; - } - - if (i < k) { - l = i + 1; - } else { - r = i; - } - } - return elems[k]; -} - void rolling_quantile_init(struct rolling_quantile *rq, int capacity, int mink, int maxk) { *rq = (struct rolling_quantile){0}; rq->tmp_buffer = malloc(sizeof(int) * (size_t)capacity); @@ -274,45 +207,79 @@ void rolling_quantile_pop_front(struct rolling_quantile *rq, int x) { } } -/// Switch to real-time scheduling policy (SCHED_RR) if possible -/// -/// Make picom realtime to reduce latency, and make rendering times more predictable to -/// help pacing. -/// -/// This requires the user to set up permissions for the real-time scheduling. e.g. by -/// setting `ulimit -r`, or giving us the CAP_SYS_NICE capability. -void set_rr_scheduling(void) { - static thread_local bool already_tried = false; - if (already_tried) { - return; +void render_statistics_init(struct render_statistics *rs, int window_size) { + *rs = (struct render_statistics){0}; + + rolling_window_init(&rs->render_times, window_size); + rolling_quantile_init_with_tolerance(&rs->render_time_quantile, window_size, + /* q */ 0.98, /* tolerance */ 0.01); +} + +void render_statistics_add_vblank_time_sample(struct render_statistics *rs, int time_us) { + auto sample_sd = sqrt(cumulative_mean_and_var_get_var(&rs->vblank_time_us)); + auto current_estimate = render_statistics_get_vblank_time(rs); + if (current_estimate != 0 && fabs((double)time_us - current_estimate) > sample_sd * 3) { + // Deviated from the mean by more than 3 sigma (p < 0.003) + log_debug("vblank time outlier: %d %f %f", time_us, rs->vblank_time_us.mean, + cumulative_mean_and_var_get_var(&rs->vblank_time_us)); + // An outlier sample, this could mean things like refresh rate changes, so + // we reset the statistics. This could also be benign, but we like to be + // cautious. + cumulative_mean_and_var_init(&rs->vblank_time_us); } - already_tried = true; - int priority = sched_get_priority_min(SCHED_RR); + if (rs->vblank_time_us.mean != 0) { + auto nframes_in_10_seconds = + (unsigned int)(10. * 1000000. / rs->vblank_time_us.mean); + if (rs->vblank_time_us.n > 20 && rs->vblank_time_us.n > nframes_in_10_seconds) { + // We collected 10 seconds worth of samples, we assume the + // estimated refresh rate is stable. We will still reset the + // statistics if we get an outlier sample though, see above. + return; + } + } + cumulative_mean_and_var_update(&rs->vblank_time_us, time_us); +} - if (rtkit_make_realtime(0, priority)) { - log_info("Set realtime priority to %d with rtkit.", priority); - return; +void render_statistics_add_render_time_sample(struct render_statistics *rs, int time_us) { + int oldest; + if (rolling_window_push_back(&rs->render_times, time_us, &oldest)) { + rolling_quantile_pop_front(&rs->render_time_quantile, oldest); } - // Fallback to use pthread_setschedparam - struct sched_param param; - int old_policy; - int ret = pthread_getschedparam(pthread_self(), &old_policy, ¶m); - if (ret != 0) { - log_info("Couldn't get old scheduling priority."); - return; + rolling_quantile_push_back(&rs->render_time_quantile, time_us); +} + +/// How much time budget we should give to the backend for rendering, in microseconds. +unsigned int render_statistics_get_budget(struct render_statistics *rs) { + if (rs->render_times.nelem < rs->render_times.window_size) { + // No valid render time estimates yet. Assume maximum budget. + return UINT_MAX; } - param.sched_priority = priority; + // N-th percentile of render times, see render_statistics_init for N. + auto render_time_percentile = + rolling_quantile_estimate(&rs->render_time_quantile, &rs->render_times); + return (unsigned int)render_time_percentile; +} - ret = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); - if (ret != 0) { - log_info("Couldn't set real-time scheduling priority to %d.", priority); - return; +unsigned int render_statistics_get_vblank_time(struct render_statistics *rs) { + if (rs->vblank_time_us.n <= 20 || rs->vblank_time_us.mean < 100) { + // Not enough samples yet, or the vblank time is too short to be + // meaningful. Assume maximum budget. + return 0; } + return (unsigned int)rs->vblank_time_us.mean; +} - log_info("Set real-time scheduling priority to %d.", priority); +void render_statistics_reset(struct render_statistics *rs) { + rolling_window_reset(&rs->render_times); + rolling_quantile_reset(&rs->render_time_quantile); + rs->vblank_time_us = (struct cumulative_mean_and_var){0}; } -// vim: set noet sw=8 ts=8 : +void render_statistics_destroy(struct render_statistics *rs) { + render_statistics_reset(rs); + rolling_window_destroy(&rs->render_times); + rolling_quantile_destroy(&rs->render_time_quantile); +} diff --git a/src/utils/statistics.h b/src/utils/statistics.h new file mode 100644 index 0000000000..f92f6177e6 --- /dev/null +++ b/src/utils/statistics.h @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + +#pragma once + +#include +#include +#include + +#include "compiler.h" + +#define NTIERS (3) + +struct rolling_window { + int *elem; + int elem_head, nelem; + int window_size; +}; + +void rolling_window_destroy(struct rolling_window *rw); +void rolling_window_reset(struct rolling_window *rw); +void rolling_window_init(struct rolling_window *rw, int size); +int rolling_window_pop_front(struct rolling_window *rw); +bool rolling_window_push_back(struct rolling_window *rw, int val, int *front); + +/// Copy the contents of the rolling window to an array. The array is assumed to +/// have enough space to hold the contents of the rolling window. +static inline void attr_unused rolling_window_copy_to_array(struct rolling_window *rw, + int *arr) { + // The length from head to the end of the array + auto head_len = (size_t)(rw->window_size - rw->elem_head); + if (head_len >= (size_t)rw->nelem) { + memcpy(arr, rw->elem + rw->elem_head, sizeof(int) * (size_t)rw->nelem); + } else { + auto tail_len = (size_t)((size_t)rw->nelem - head_len); + memcpy(arr, rw->elem + rw->elem_head, sizeof(int) * head_len); + memcpy(arr + head_len, rw->elem, sizeof(int) * tail_len); + } +} + +struct rolling_max; + +struct rolling_max *rolling_max_new(int capacity); +void rolling_max_destroy(struct rolling_max *rm); +void rolling_max_reset(struct rolling_max *rm); +void rolling_max_pop_front(struct rolling_max *rm, int front); +void rolling_max_push_back(struct rolling_max *rm, int val); +int rolling_max_get_max(struct rolling_max *rm); + +/// Estimate the mean and variance of random variable X using Welford's online +/// algorithm. +struct cumulative_mean_and_var { + double mean; + double m2; + unsigned int n; +}; + +static inline attr_unused void +cumulative_mean_and_var_init(struct cumulative_mean_and_var *cmv) { + *cmv = (struct cumulative_mean_and_var){0}; +} + +static inline attr_unused void +cumulative_mean_and_var_update(struct cumulative_mean_and_var *cmv, double x) { + if (cmv->n == UINT_MAX) { + // We have too many elements, let's keep the mean and variance. + return; + } + cmv->n++; + double delta = x - cmv->mean; + cmv->mean += delta / (double)cmv->n; + cmv->m2 += delta * (x - cmv->mean); +} + +static inline attr_unused double +cumulative_mean_and_var_get_var(struct cumulative_mean_and_var *cmv) { + if (cmv->n < 2) { + return 0; + } + return cmv->m2 / (double)(cmv->n - 1); +} + +/// A naive quantile estimator. +/// +/// Estimates the N-th percentile of a random variable X in a sliding window. +struct rolling_quantile { + int current_rank; + int min_target_rank, max_target_rank; + int estimate; + int capacity; + int *tmp_buffer; +}; + +void rolling_quantile_init(struct rolling_quantile *rq, int capacity, int mink, int maxk); +void rolling_quantile_init_with_tolerance(struct rolling_quantile *rq, int window_size, + double target, double tolerance); +void rolling_quantile_reset(struct rolling_quantile *rq); +void rolling_quantile_destroy(struct rolling_quantile *rq); +int rolling_quantile_estimate(struct rolling_quantile *rq, struct rolling_window *elements); +void rolling_quantile_push_back(struct rolling_quantile *rq, int x); +void rolling_quantile_pop_front(struct rolling_quantile *rq, int x); + +struct render_statistics { + /// Rolling window of rendering times (in us) and the tiers they belong to. + /// We keep track of the tiers because the vblank time estimate can change over + /// time. + struct rolling_window render_times; + /// Estimate the 95-th percentile of rendering times + struct rolling_quantile render_time_quantile; + /// Time between each vblanks + struct cumulative_mean_and_var vblank_time_us; +}; + +void render_statistics_init(struct render_statistics *rs, int window_size); +void render_statistics_reset(struct render_statistics *rs); +void render_statistics_destroy(struct render_statistics *rs); + +void render_statistics_add_vblank_time_sample(struct render_statistics *rs, int time_us); +void render_statistics_add_render_time_sample(struct render_statistics *rs, int time_us); + +/// How much time budget we should give to the backend for rendering, in microseconds. +unsigned int render_statistics_get_budget(struct render_statistics *rs); + +/// Return the measured vblank interval in microseconds. Returns 0 if not enough +/// samples have been collected yet. +unsigned int render_statistics_get_vblank_time(struct render_statistics *rs); diff --git a/src/string_utils.c b/src/utils/str.c similarity index 98% rename from src/string_utils.c rename to src/utils/str.c index b683fdc585..7a9337bf4d 100644 --- a/src/string_utils.c +++ b/src/utils/str.c @@ -6,9 +6,7 @@ #include -#include "compiler.h" -#include "string_utils.h" -#include "utils.h" +#include "str.h" #pragma GCC diagnostic push diff --git a/src/string_utils.h b/src/utils/str.h similarity index 98% rename from src/string_utils.h rename to src/utils/str.h index 88f7d47747..9a284e43d0 100644 --- a/src/string_utils.h +++ b/src/utils/str.h @@ -1,13 +1,15 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui + #pragma once #include #include #include #include +#include #include -#include "utils.h" +#include "misc.h" #define mstrncmp(s1, s2) strncmp((s1), (s2), strlen(s1)) diff --git a/src/uthash_extra.h b/src/utils/uthash_extra.h similarity index 75% rename from src/uthash_extra.h rename to src/utils/uthash_extra.h index cbc10561b7..79ad566a2e 100644 --- a/src/uthash_extra.h +++ b/src/utils/uthash_extra.h @@ -1,6 +1,7 @@ -#pragma once +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui -#include +#pragma once #define HASH_ITER2(head, el) \ for (__typeof__(head) el = (head), __tmp = el != NULL ? el->hh.next : NULL; \ diff --git a/src/vblank.c b/src/vblank.c index 093c225126..a02619d49f 100644 --- a/src/vblank.c +++ b/src/vblank.c @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + #include #include @@ -21,8 +24,8 @@ #endif #include "compiler.h" -#include "list.h" // for container_of #include "log.h" +#include "utils/dynarr.h" #include "vblank.h" #include "x.h" @@ -35,7 +38,7 @@ struct vblank_closure { struct vblank_scheduler { struct x_connection *c; - size_t callback_capacity, callback_count; + /// List of scheduled vblank callbacks, this is a dynarr struct vblank_closure *callbacks; struct ev_loop *loop; /// Request extra vblank events even when no callbacks are scheduled. @@ -483,26 +486,16 @@ static bool vblank_scheduler_schedule_internal(struct vblank_scheduler *self) { bool vblank_scheduler_schedule(struct vblank_scheduler *self, vblank_callback_t vblank_callback, void *user_data) { - if (self->callback_count == 0 && self->wind_down == 0) { - if (!vblank_scheduler_schedule_internal(self)) { - return false; - } - } - if (self->callback_count == self->callback_capacity) { - size_t new_capacity = - self->callback_capacity ? self->callback_capacity * 2 : 1; - void *new_buffer = - realloc(self->callbacks, new_capacity * sizeof(*self->callbacks)); - if (!new_buffer) { - return false; - } - self->callbacks = new_buffer; - self->callback_capacity = new_capacity; + // Schedule a new vblank event if there are no callbacks currently scheduled. + if (dynarr_len(self->callbacks) == 0 && self->wind_down == 0 && + !vblank_scheduler_schedule_internal(self)) { + return false; } - self->callbacks[self->callback_count++] = (struct vblank_closure){ + struct vblank_closure closure = { .fn = vblank_callback, .user_data = user_data, }; + dynarr_push(self->callbacks, closure); return true; } @@ -510,7 +503,7 @@ static void vblank_scheduler_invoke_callbacks(struct vblank_scheduler *self, struct vblank_event *event) { // callbacks might be added during callback invocation, so we need to // copy the callback_count. - size_t count = self->callback_count, write_head = 0; + size_t count = dynarr_len(self->callbacks), write_head = 0; if (count == 0) { self->wind_down--; } else { @@ -531,10 +524,11 @@ vblank_scheduler_invoke_callbacks(struct vblank_scheduler *self, struct vblank_e } memset(self->callbacks + write_head, 0, (count - write_head) * sizeof(*self->callbacks)); - assert(count == self->callback_count && "callbacks should not be added when " - "callbacks are being invoked."); - self->callback_count = write_head; - if (self->callback_count || self->wind_down) { + assert(count == dynarr_len(self->callbacks) && "callbacks should not be added " + "when callbacks are being " + "invoked."); + dynarr_len(self->callbacks) = write_head; + if (write_head || self->wind_down) { vblank_scheduler_schedule_internal(self); } } @@ -545,7 +539,7 @@ void vblank_scheduler_free(struct vblank_scheduler *self) { if (fn != NULL) { fn(self); } - free(self->callbacks); + dynarr_free_pod(self->callbacks); free(self); } @@ -565,6 +559,7 @@ vblank_scheduler_new(struct ev_loop *loop, struct x_connection *c, xcb_window_t self->c = c; self->loop = loop; self->use_realtime_scheduling = use_realtime_scheduling; + self->callbacks = dynarr_new(struct vblank_closure, 1); init_fn(self); return self; } diff --git a/src/vblank.h b/src/vblank.h index 647a45f3ba..a1916dc405 100644 --- a/src/vblank.h +++ b/src/vblank.h @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + #pragma once #include diff --git a/src/vsync.h b/src/vsync.h index 076bc260c3..9afcf345be 100644 --- a/src/vsync.h +++ b/src/vsync.h @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui + #include typedef struct session session_t; diff --git a/src/win_defs.h b/src/wm/defs.h similarity index 97% rename from src/win_defs.h rename to src/wm/defs.h index 2f46ef5b21..cc2abb1dd4 100644 --- a/src/win_defs.h +++ b/src/wm/defs.h @@ -1,5 +1,7 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + #pragma once -#include typedef enum { WINTYPE_UNKNOWN = 0, diff --git a/src/wm/meson.build b/src/wm/meson.build new file mode 100644 index 0000000000..7813e193e4 --- /dev/null +++ b/src/wm/meson.build @@ -0,0 +1 @@ +srcs += [ files('win.c', 'wm.c') ] diff --git a/src/win.c b/src/wm/win.c similarity index 99% rename from src/win.c rename to src/wm/win.c index 8263855d00..1e1491be6f 100644 --- a/src/win.c +++ b/src/wm/win.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2011-2013, Christopher Jeffrey // Copyright (c) 2013 Richard Grenville +// Copyright (c) 2018 Yuxuan Shui #include #include @@ -23,20 +24,16 @@ #include "compiler.h" #include "config.h" #include "dbus.h" -#include "list.h" #include "log.h" #include "picom.h" #include "region.h" #include "render.h" -#include "utils.h" -#include "win_defs.h" -#include "wm.h" +#include "utils/list.h" +#include "utils/misc.h" #include "x.h" -#ifdef CONFIG_OPENGL -// TODO(yshui) Get rid of this include -#include "opengl.h" -#endif +#include "defs.h" +#include "wm.h" #include "win.h" @@ -1469,6 +1466,13 @@ xcb_window_t win_get_client_window(struct x_connection *c, struct wm *wm, return cw; } +#ifdef CONFIG_OPENGL +void free_win_res_glx(session_t *ps, struct managed_win *w); +#else +static inline void free_win_res_glx(session_t * /*ps*/, struct managed_win * /*w*/) { +} +#endif + /** * Free all resources in a struct _win. */ diff --git a/src/win.h b/src/wm/win.h similarity index 99% rename from src/win.h rename to src/wm/win.h index aed50488ee..f96560014e 100644 --- a/src/win.h +++ b/src/wm/win.h @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2011-2013, Christopher Jeffrey // Copyright (c) 2013 Richard Grenville +// Copyright (c) 2018 Yuxuan Shui + #pragma once #include #include @@ -12,12 +14,12 @@ #include "c2.h" #include "compiler.h" -#include "list.h" +#include "defs.h" #include "region.h" #include "render.h" -#include "script.h" -#include "utils.h" -#include "win_defs.h" +#include "transition/script.h" +#include "utils/list.h" +#include "utils/misc.h" #include "x.h" #include "xcb/xproto.h" diff --git a/src/wm.c b/src/wm/wm.c similarity index 99% rename from src/wm.c rename to src/wm/wm.c index 2d680cd639..935cb6ee3a 100644 --- a/src/wm.c +++ b/src/wm/wm.c @@ -4,12 +4,13 @@ #include #include -#include "list.h" #include "log.h" -#include "uthash_extra.h" +#include "utils/list.h" +#include "utils/uthash_extra.h" +#include "x.h" + #include "win.h" #include "wm.h" -#include "x.h" struct wm { /// Current window generation, start from 1. 0 is reserved for using as diff --git a/src/wm.h b/src/wm/wm.h similarity index 99% rename from src/wm.h rename to src/wm/wm.h index b9778c191b..13db1ce7af 100644 --- a/src/wm.h +++ b/src/wm/wm.h @@ -15,8 +15,9 @@ #include #include +#include + #include "compiler.h" -#include "utils.h" struct wm; struct managed_win; diff --git a/src/x.c b/src/x.c index d0dc460d40..15e891743e 100644 --- a/src/x.c +++ b/src/x.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2018 Yuxuan Shui + #include #include #include @@ -20,15 +21,12 @@ #include #include "atom.h" -#ifdef CONFIG_OPENGL -#include "backend/gl/glx.h" -#endif #include "common.h" #include "compiler.h" -#include "kernel.h" #include "log.h" #include "region.h" -#include "utils.h" +#include "utils/kernel.h" +#include "utils/misc.h" #include "x.h" // === Error handling === diff --git a/src/x.h b/src/x.h index be23fae043..d4e01f5a68 100644 --- a/src/x.h +++ b/src/x.h @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2018 Yuxuan Shui + #pragma once #include #include @@ -13,9 +14,9 @@ #include "atom.h" #include "compiler.h" -#include "kernel.h" #include "log.h" #include "region.h" +#include "utils/kernel.h" typedef struct session session_t; struct atom;