diff --git a/config.c b/config.c index 64e45135..98b2c307 100644 --- a/config.c +++ b/config.c @@ -2809,12 +2809,21 @@ parse_section_tweak(struct context *ctx) else if (streq(key, "surface-bit-depth")) { _Static_assert(sizeof(conf->tweak.surface_bit_depth) == sizeof(int), - "enum is not 32-bit"); + "enum is not 32-bit"); + /* TODO: check which version PIXMAN_rgba_float16 ended up in; + guessing 0.47.0, but PR is currently stuck at 0.44.3 */ +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0, 44, 3) + return value_to_enum( + ctx, + (const char *[]){"auto", "8-bit", "10-bit", "16f-bit", NULL}, + (int *)&conf->tweak.surface_bit_depth); +#else return value_to_enum( ctx, (const char *[]){"auto", "8-bit", "10-bit", NULL}, (int *)&conf->tweak.surface_bit_depth); +#endif } else { diff --git a/config.h b/config.h index 80081906..eb5c9651 100644 --- a/config.h +++ b/config.h @@ -198,7 +198,8 @@ enum which_color_theme { enum shm_bit_depth { SHM_BITS_AUTO, SHM_BITS_8, - SHM_BITS_10 + SHM_BITS_10, + SHM_BITS_16F, }; struct config { diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 3e70074e..e59d3493 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -221,12 +221,13 @@ empty string to be set, but it must be quoted: *KEY=""*) font designer set the font weight based on incorrect rendering. In order to represent colors faithfully, higher precision image - buffers are required. By default, foot will use 10-bit color - channels, if available, when gamma-correct blending is - enabled. However, the high precision buffers are slow; if you want - to use gamma-correct blending, but prefer speed (throughput and - input latency) over accurate colors, you can force 8-bit color - channels by setting *tweak.surface-bit-depth=8-bit*. + buffers are required. By default, foot will use either 16-bit, or + 10-bit color channels, depending on availability, when + gamma-correct blending is enabled. However, the high precision + buffers are slow; if you want to use gamma-correct blending, but + prefer speed (throughput and input latency) over accurate colors, + you can force 8-bit color channels by setting + *tweak.surface-bit-depth=8-bit*. Default: _no_. @@ -2023,7 +2024,7 @@ any of these options. *surface-bit-depth* Selects which RGB bit depth to use for image buffers. One of - *auto*, *8-bit*, or *10-bit*. + *auto*, *8-bit*, *10-bit*, or *16f-bit*. *auto* chooses bit depth depending on other settings, and availability. @@ -2033,13 +2034,16 @@ any of these options. *10-bit* uses 10 bits for each RGB channel, and 2 bits for the alpha channel. Thus, it provides higher precision color channels, - but a lower precision alpha channel. It is the default when - *gamma-correct-blending=yes*, if supported by the compositor. + but a lower precision alpha channel. - Note that *10-bit* is much slower than *8-bit*; if you want to use - gamma-correct blending, and if you prefer speed (throughput and - input latency) over accurate colors, you can set - *surface-bit-depth=8-bit* explicitly. + *16f-bit* uses 16 bits (floating point) for each color channel, + alpha included. If available, this is the default when + *gamma-correct-blending=yes*. + + Note that both *10-bit* and *16f-bit* are much slower than + *8-bit*; if you want to use gamma-correct blending, and if you + prefer speed (throughput and input latency) over accurate colors, + you can set *surface-bit-depth=8-bit* explicitly. Default: _auto_ diff --git a/meson.build b/meson.build index 4bf4993c..3e87999d 100644 --- a/meson.build +++ b/meson.build @@ -131,7 +131,7 @@ endif math = cc.find_library('m') threads = [dependency('threads'), cc.find_library('stdthreads', required: false)] libepoll = dependency('epoll-shim', required: false) -pixman = dependency('pixman-1') +pixman = dependency('pixman-1', fallback: 'pixman') wayland_protocols = dependency('wayland-protocols', version: '>=1.41', fallback: 'wayland-protocols', default_options: ['tests=false']) diff --git a/shm.c b/shm.c index 38944020..a3ad5de4 100644 --- a/shm.c +++ b/shm.c @@ -338,7 +338,10 @@ get_new_buffers(struct buffer_chain *chain, size_t count, size_t total_size = 0; for (size_t i = 0; i < count; i++) { stride[i] = stride_for_format_and_width( - with_alpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8, widths[i]); + with_alpha + ? chain->pixman_fmt_with_alpha + : chain->pixman_fmt_without_alpha, + widths[i]); sizes[i] = stride[i] * heights[i]; total_size += sizes[i]; } @@ -981,8 +984,37 @@ shm_chain_new(struct wayland *wayl, bool scrollable, size_t pix_instances, enum wl_shm_format shm_fmt_with_alpha = WL_SHM_FORMAT_ARGB8888; static bool have_logged = false; + static bool have_logged_16f_fallback = false; + static bool have_logged_10_fallback = false; - if (desired_bit_depth == SHM_BITS_10) { + if (desired_bit_depth == SHM_BITS_16F) { + if (wayl->shm_have_abgr161616f && wayl->shm_have_xbgr161616f) { + pixman_fmt_without_alpha = PIXMAN_rgba_float16; + shm_fmt_without_alpha = WL_SHM_FORMAT_XBGR16161616F; + + pixman_fmt_with_alpha = PIXMAN_rgba_float16; + shm_fmt_with_alpha = WL_SHM_FORMAT_ABGR16161616F; + + if (!have_logged) { + have_logged = true; + LOG_INFO("using 16-bit (float) BGR surfaces"); + } + } else { + if (!have_logged_16f_fallback) { + have_logged_16f_fallback = true; + + LOG_WARN( + "16f-bit surfaces requested, but compositor does not " + "implement ABGR161616F+XBGR161616F. Falling back to either " + "10- or 8-bit surfaces"); + } + } + } + + if (desired_bit_depth == SHM_BITS_10 || + (desired_bit_depth == SHM_BITS_16F && + pixman_fmt_with_alpha == PIXMAN_a8r8g8b8)) + { if (wayl->shm_have_argb2101010 && wayl->shm_have_xrgb2101010) { pixman_fmt_without_alpha = PIXMAN_x2r10g10b10; shm_fmt_without_alpha = WL_SHM_FORMAT_XRGB2101010; @@ -1010,8 +1042,8 @@ shm_chain_new(struct wayland *wayl, bool scrollable, size_t pix_instances, } else { - if (!have_logged) { - have_logged = true; + if (!have_logged_10_fallback) { + have_logged_10_fallback = true; LOG_WARN( "10-bit surfaces requested, but compositor does not " @@ -1063,7 +1095,9 @@ shm_chain_bit_depth(const struct buffer_chain *chain) { const pixman_format_code_t fmt = chain->pixman_fmt_with_alpha; - return (fmt == PIXMAN_a2r10g10b10 || fmt == PIXMAN_a2b10g10r10) - ? SHM_BITS_10 - : SHM_BITS_8; + return fmt == PIXMAN_a8r8g8b8 + ? SHM_BITS_8 + : fmt == PIXMAN_rgba_float16 + ? SHM_BITS_16F + : SHM_BITS_10; } diff --git a/sixel.c b/sixel.c index 680c258f..7a056e11 100644 --- a/sixel.c +++ b/sixel.c @@ -113,7 +113,18 @@ sixel_init(struct terminal *term, int p1, int p2, int p3) term->sixel.linear_blending = wayl_do_linear_blending(term->wl, term->conf); term->sixel.pixman_fmt = PIXMAN_a8r8g8b8; - if (term->conf->tweak.surface_bit_depth == SHM_BITS_10) { + /* + * Use higher-precision sixel surfaces if we're using + * higher-precision window surfaces. + * + * This is to a) get more accurate colors when doing gamma-correct + * blending, and b) use the same pixman format as the main + * surfaces, for (hopefully) better performance. + * + * For now, don't support 16f surfaces (too much sixel logic that + * assumes 32-bit pixels). + */ + if (shm_chain_bit_depth(term->render.chains.grid) >= SHM_BITS_10) { if (term->wl->shm_have_argb2101010 && term->wl->shm_have_xrgb2101010) { term->sixel.use_10bit = true; term->sixel.pixman_fmt = PIXMAN_a2r10g10b10; diff --git a/terminal.c b/terminal.c index 793a1616..628d2431 100644 --- a/terminal.c +++ b/terminal.c @@ -1259,7 +1259,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, const enum shm_bit_depth desired_bit_depth = conf->tweak.surface_bit_depth == SHM_BITS_AUTO - ? wayl_do_linear_blending(wayl, conf) ? SHM_BITS_10 : SHM_BITS_8 + ? wayl_do_linear_blending(wayl, conf) ? SHM_BITS_16F : SHM_BITS_8 : conf->tweak.surface_bit_depth; const struct color_theme *theme = NULL; diff --git a/wayland.c b/wayland.c index 08994202..b8003d83 100644 --- a/wayland.c +++ b/wayland.c @@ -244,6 +244,8 @@ shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) case WL_SHM_FORMAT_ARGB2101010: wayl->shm_have_argb2101010 = true; break; case WL_SHM_FORMAT_XBGR2101010: wayl->shm_have_xbgr2101010 = true; break; case WL_SHM_FORMAT_ABGR2101010: wayl->shm_have_abgr2101010 = true; break; + case WL_SHM_FORMAT_XBGR16161616F: wayl->shm_have_xbgr161616f = true; break; + case WL_SHM_FORMAT_ABGR16161616F: wayl->shm_have_abgr161616f = true; break; } #if defined(_DEBUG) diff --git a/wayland.h b/wayland.h index 044b217f..9e69e4a2 100644 --- a/wayland.h +++ b/wayland.h @@ -496,6 +496,8 @@ struct wayland { bool shm_have_xrgb2101010:1; bool shm_have_abgr2101010:1; bool shm_have_xbgr2101010:1; + bool shm_have_abgr161616f:1; + bool shm_have_xbgr161616f:1; }; struct wayland *wayl_init(