aboutsummaryrefslogtreecommitdiffstats
path: root/sway/config/output.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/config/output.c')
-rw-r--r--sway/config/output.c331
1 files changed, 276 insertions, 55 deletions
diff --git a/sway/config/output.c b/sway/config/output.c
index 54af5d8e..9a447388 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -13,6 +13,7 @@
13#include "sway/config.h" 13#include "sway/config.h"
14#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
15#include "sway/output.h" 15#include "sway/output.h"
16#include "sway/server.h"
16#include "sway/tree/root.h" 17#include "sway/tree/root.h"
17#include "log.h" 18#include "log.h"
18#include "util.h" 19#include "util.h"
@@ -210,7 +211,7 @@ static void merge_output_config(struct output_config *dst, struct output_config
210void store_output_config(struct output_config *oc) { 211void store_output_config(struct output_config *oc) {
211 bool merged = false; 212 bool merged = false;
212 bool wildcard = strcmp(oc->name, "*") == 0; 213 bool wildcard = strcmp(oc->name, "*") == 0;
213 struct sway_output *output = wildcard ? NULL : output_by_name_or_id(oc->name); 214 struct sway_output *output = wildcard ? NULL : all_output_by_name_or_id(oc->name);
214 215
215 char id[128]; 216 char id[128];
216 if (output) { 217 if (output) {
@@ -386,22 +387,18 @@ static int compute_default_scale(struct wlr_output *output,
386 return 2; 387 return 2;
387} 388}
388 389
389/* Lists of formats to try, in order, when a specific render bit depth has 390static bool render_format_is_10bit(uint32_t render_format) {
390 * been asked for. The second to last format in each list should always 391 return render_format == DRM_FORMAT_XRGB2101010 ||
391 * be XRGB8888, as a reliable backup in case the others are not available; 392 render_format == DRM_FORMAT_XBGR2101010;
392 * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ 393}
393static const uint32_t *bit_depth_preferences[] = { 394
394 [RENDER_BIT_DEPTH_8] = (const uint32_t []){ 395static bool render_format_is_bgr(uint32_t fmt) {
395 DRM_FORMAT_XRGB8888, 396 return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888;
396 DRM_FORMAT_INVALID, 397}
397 }, 398
398 [RENDER_BIT_DEPTH_10] = (const uint32_t []){ 399static bool output_config_is_disabling(struct output_config *oc) {
399 DRM_FORMAT_XRGB2101010, 400 return oc && (!oc->enabled || oc->power == 0);
400 DRM_FORMAT_XBGR2101010, 401}
401 DRM_FORMAT_XRGB8888,
402 DRM_FORMAT_INVALID,
403 },
404};
405 402
406static void queue_output_config(struct output_config *oc, 403static void queue_output_config(struct output_config *oc,
407 struct sway_output *output, struct wlr_output_state *pending) { 404 struct sway_output *output, struct wlr_output_state *pending) {
@@ -411,7 +408,7 @@ static void queue_output_config(struct output_config *oc,
411 408
412 struct wlr_output *wlr_output = output->wlr_output; 409 struct wlr_output *wlr_output = output->wlr_output;
413 410
414 if (oc && (!oc->enabled || oc->power == 0)) { 411 if (output_config_is_disabling(oc)) {
415 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); 412 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
416 wlr_output_state_set_enabled(pending, false); 413 wlr_output_state_set_enabled(pending, false);
417 return; 414 return;
@@ -434,22 +431,6 @@ static void queue_output_config(struct output_config *oc,
434 struct wlr_output_mode *preferred_mode = 431 struct wlr_output_mode *preferred_mode =
435 wlr_output_preferred_mode(wlr_output); 432 wlr_output_preferred_mode(wlr_output);
436 wlr_output_state_set_mode(pending, preferred_mode); 433 wlr_output_state_set_mode(pending, preferred_mode);
437
438 if (!wlr_output_test_state(wlr_output, pending)) {
439 sway_log(SWAY_DEBUG, "Preferred mode rejected, "
440 "falling back to another mode");
441 struct wlr_output_mode *mode;
442 wl_list_for_each(mode, &wlr_output->modes, link) {
443 if (mode == preferred_mode) {
444 continue;
445 }
446
447 wlr_output_state_set_mode(pending, mode);
448 if (wlr_output_test_state(wlr_output, pending)) {
449 break;
450 }
451 }
452 }
453 } 434 }
454 435
455 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 436 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
@@ -468,7 +449,7 @@ static void queue_output_config(struct output_config *oc,
468#endif 449#endif
469 } 450 }
470 if (wlr_output->transform != tr) { 451 if (wlr_output->transform != tr) {
471 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); 452 sway_log(SWAY_DEBUG, "Set %s transform to %d", wlr_output->name, tr);
472 wlr_output_state_set_transform(pending, tr); 453 wlr_output_state_set_transform(pending, tr);
473 } 454 }
474 455
@@ -500,25 +481,17 @@ static void queue_output_config(struct output_config *oc,
500 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, 481 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
501 oc->adaptive_sync); 482 oc->adaptive_sync);
502 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); 483 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);
503 if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) {
504 sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring");
505 wlr_output_state_set_adaptive_sync_enabled(pending, false);
506 }
507 } 484 }
508 485
509 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { 486 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
510 const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; 487 if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 &&
511 assert(fmts); 488 render_format_is_10bit(output->wlr_output->render_format)) {
512 489 // 10-bit was set successfully before, try to save some tests by reusing the format
513 for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { 490 wlr_output_state_set_render_format(pending, output->wlr_output->render_format);
514 wlr_output_state_set_render_format(pending, fmts[i]); 491 } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) {
515 if (wlr_output_test_state(wlr_output, pending)) { 492 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010);
516 break; 493 } else {
517 } 494 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888);
518
519 sway_log(SWAY_DEBUG, "Preferred output format 0x%08x "
520 "failed to work, falling back to next in "
521 "list, 0x%08x", fmts[i], fmts[i + 1]);
522 } 495 }
523 } 496 }
524} 497}
@@ -649,8 +622,245 @@ struct output_config *find_output_config(struct sway_output *sway_output) {
649 return result; 622 return result;
650} 623}
651 624
625static bool config_has_auto_mode(struct output_config *oc) {
626 if (!oc) {
627 return true;
628 }
629 if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) {
630 return true;
631 } else if (oc->width > 0 && oc->height > 0) {
632 return true;
633 }
634 return false;
635}
636
637struct search_context {
638 struct wlr_output_swapchain_manager *swapchain_mgr;
639 struct wlr_backend_output_state *states;
640 struct matched_output_config *configs;
641 size_t configs_len;
642 bool degrade_to_off;
643};
644
645static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_state *state) {
646 sway_log(SWAY_DEBUG, "Output state for %s", wlr_output->name);
647 if (state->committed & WLR_OUTPUT_STATE_ENABLED) {
648 sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no");
649 }
650 if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) {
651 sway_log(SWAY_DEBUG, " render_format: %d", state->render_format);
652 }
653 if (state->committed & WLR_OUTPUT_STATE_MODE) {
654 if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) {
655 sway_log(SWAY_DEBUG, " custom mode: %dx%d@%dmHz",
656 state->custom_mode.width, state->custom_mode.height, state->custom_mode.refresh);
657 } else {
658 sway_log(SWAY_DEBUG, " mode: %dx%d@%dmHz%s",
659 state->mode->width, state->mode->height, state->mode->refresh,
660 state->mode->preferred ? " (preferred)" : "");
661 }
662 }
663 if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) {
664 sway_log(SWAY_DEBUG, " adaptive_sync: %s",
665 state->adaptive_sync_enabled ? "enabled": "disabled");
666 }
667}
668
669static bool search_valid_config(struct search_context *ctx, size_t output_idx);
670
671static void reset_output_state(struct wlr_output_state *state) {
672 wlr_output_state_finish(state);
673 wlr_output_state_init(state);
674 state->committed = 0;
675}
676
677static void clear_later_output_states(struct wlr_backend_output_state *states,
678 size_t configs_len, size_t output_idx) {
679
680 // Clear and disable all output states after this one to avoid conflict
681 // with previous tests.
682 for (size_t idx = output_idx+1; idx < configs_len; idx++) {
683 struct wlr_backend_output_state *backend_state = &states[idx];
684 struct wlr_output_state *state = &backend_state->base;
685
686 reset_output_state(state);
687 wlr_output_state_set_enabled(state, false);
688 }
689}
690
691static bool search_finish(struct search_context *ctx, size_t output_idx) {
692 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
693 struct wlr_output_state *state = &backend_state->base;
694 struct wlr_output *wlr_output = backend_state->output;
695
696 clear_later_output_states(ctx->states, ctx->configs_len, output_idx);
697 dump_output_state(wlr_output, state);
698 return wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) &&
699 search_valid_config(ctx, output_idx+1);
700}
701
702static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) {
703 struct matched_output_config *cfg = &ctx->configs[output_idx];
704 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
705 struct wlr_output_state *state = &backend_state->base;
706
707 if (cfg->config && cfg->config->adaptive_sync == 1) {
708 wlr_output_state_set_adaptive_sync_enabled(state, true);
709 if (search_finish(ctx, output_idx)) {
710 return true;
711 }
712 }
713 if (!cfg->config || cfg->config->adaptive_sync != -1) {
714 wlr_output_state_set_adaptive_sync_enabled(state, false);
715 if (search_finish(ctx, output_idx)) {
716 return true;
717 }
718 }
719 // If adaptive sync has not been set, or fallback in case we are on a
720 // backend that cannot disable adaptive sync such as the wayland backend.
721 state->committed &= ~WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED;
722 return search_finish(ctx, output_idx);
723}
724
725static bool search_mode(struct search_context *ctx, size_t output_idx) {
726 struct matched_output_config *cfg = &ctx->configs[output_idx];
727 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
728 struct wlr_output_state *state = &backend_state->base;
729 struct wlr_output *wlr_output = backend_state->output;
730
731 if (!config_has_auto_mode(cfg->config)) {
732 return search_adaptive_sync(ctx, output_idx);
733 }
734
735 struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
736 if (preferred_mode) {
737 wlr_output_state_set_mode(state, preferred_mode);
738 if (search_adaptive_sync(ctx, output_idx)) {
739 return true;
740 }
741 }
742
743 if (wl_list_empty(&wlr_output->modes)) {
744 state->committed &= ~WLR_OUTPUT_STATE_MODE;
745 return search_adaptive_sync(ctx, output_idx);
746 }
747
748 struct wlr_output_mode *mode;
749 wl_list_for_each(mode, &backend_state->output->modes, link) {
750 if (mode == preferred_mode) {
751 continue;
752 }
753 wlr_output_state_set_mode(state, mode);
754 if (search_adaptive_sync(ctx, output_idx)) {
755 return true;
756 }
757 }
758
759 return false;
760}
761
762static bool search_render_format(struct search_context *ctx, size_t output_idx) {
763 struct matched_output_config *cfg = &ctx->configs[output_idx];
764 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
765 struct wlr_output_state *state = &backend_state->base;
766 struct wlr_output *wlr_output = backend_state->output;
767
768 uint32_t fmts[] = {
769 DRM_FORMAT_XRGB2101010,
770 DRM_FORMAT_XBGR2101010,
771 DRM_FORMAT_XRGB8888,
772 DRM_FORMAT_INVALID,
773 };
774 if (render_format_is_bgr(wlr_output->render_format)) {
775 // Start with BGR in the unlikely event that we previously required it.
776 fmts[0] = DRM_FORMAT_XBGR2101010;
777 fmts[1] = DRM_FORMAT_XRGB2101010;
778 }
779
780 const struct wlr_drm_format_set *primary_formats =
781 wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF);
782 bool need_10bit = cfg->config && cfg->config->render_bit_depth == RENDER_BIT_DEPTH_10;
783 for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) {
784 if (!need_10bit && render_format_is_10bit(fmts[idx])) {
785 continue;
786 }
787 if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) {
788 // This is not a supported format for this output
789 continue;
790 }
791 wlr_output_state_set_render_format(state, fmts[idx]);
792 if (search_mode(ctx, output_idx)) {
793 return true;
794 }
795 }
796 return false;
797}
798
799static bool search_valid_config(struct search_context *ctx, size_t output_idx) {
800 if (output_idx >= ctx->configs_len) {
801 // We reached the end of the search, all good!
802 return true;
803 }
804
805 struct matched_output_config *cfg = &ctx->configs[output_idx];
806 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
807 struct wlr_output_state *state = &backend_state->base;
808 struct wlr_output *wlr_output = backend_state->output;
809
810 if (!output_config_is_disabling(cfg->config)) {
811 // Search through our possible configurations, doing a depth-first
812 // through render_format, modes, adaptive_sync and the next output's
813 // config.
814 queue_output_config(cfg->config, cfg->output, &backend_state->base);
815 if (search_render_format(ctx, output_idx)) {
816 return true;
817 } else if (!ctx->degrade_to_off) {
818 return false;
819 }
820 // We could not get anything to work, try to disable this output to see
821 // if we can at least make the outputs before us work.
822 sway_log(SWAY_DEBUG, "Unable to find valid config with output %s, disabling",
823 wlr_output->name);
824 reset_output_state(state);
825 }
826
827 wlr_output_state_set_enabled(state, false);
828 return search_finish(ctx, output_idx);
829}
830
831static int compare_matched_output_config_priority(const void *a, const void *b) {
832
833 const struct matched_output_config *amc = a;
834 const struct matched_output_config *bmc = b;
835 bool a_disabling = output_config_is_disabling(amc->config);
836 bool b_disabling = output_config_is_disabling(bmc->config);
837 bool a_enabled = amc->output->enabled;
838 bool b_enabled = bmc->output->enabled;
839
840 // We want to give priority to existing enabled outputs. To do so, we want
841 // the configuration order to be:
842 // 1. Existing, enabled outputs
843 // 2. Outputs that need to be enabled
844 // 3. Disabled or disabling outputs
845 if (a_enabled && !a_disabling) {
846 return -1;
847 } else if (b_enabled && !b_disabling) {
848 return 1;
849 } else if (b_disabling && !a_disabling) {
850 return -1;
851 } else if (a_disabling && !b_disabling) {
852 return 1;
853 }
854 return 0;
855}
856
857void sort_output_configs_by_priority(struct matched_output_config *configs,
858 size_t configs_len) {
859 qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority);
860}
861
652bool apply_output_configs(struct matched_output_config *configs, 862bool apply_output_configs(struct matched_output_config *configs,
653 size_t configs_len, bool test_only) { 863 size_t configs_len, bool test_only, bool degrade_to_off) {
654 struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); 864 struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));
655 if (!states) { 865 if (!states) {
656 return false; 866 return false;
@@ -674,8 +884,18 @@ bool apply_output_configs(struct matched_output_config *configs,
674 884
675 bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len); 885 bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len);
676 if (!ok) { 886 if (!ok) {
677 sway_log(SWAY_ERROR, "Swapchain prepare failed"); 887 sway_log(SWAY_ERROR, "Requested backend configuration failed, searching for valid fallbacks");
678 goto out; 888 struct search_context ctx = {
889 .swapchain_mgr = &swapchain_mgr,
890 .states = states,
891 .configs = configs,
892 .configs_len = configs_len,
893 .degrade_to_off = degrade_to_off,
894 };
895 if (!search_valid_config(&ctx, 0)) {
896 sway_log(SWAY_ERROR, "Search for valid config failed");
897 goto out;
898 }
679 } 899 }
680 900
681 if (test_only) { 901 if (test_only) {
@@ -761,7 +981,8 @@ void apply_all_output_configs(void) {
761 config->config = find_output_config(sway_output); 981 config->config = find_output_config(sway_output);
762 } 982 }
763 983
764 apply_output_configs(configs, configs_len, false); 984 sort_output_configs_by_priority(configs, configs_len);
985 apply_output_configs(configs, configs_len, false, true);
765 for (size_t idx = 0; idx < configs_len; idx++) { 986 for (size_t idx = 0; idx < configs_len; idx++) {
766 struct matched_output_config *cfg = &configs[idx]; 987 struct matched_output_config *cfg = &configs[idx];
767 free_output_config(cfg->config); 988 free_output_config(cfg->config);