aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sway/config.h5
-rw-r--r--sway/config/output.c308
-rw-r--r--sway/desktop/output.c3
3 files changed, 261 insertions, 55 deletions
diff --git a/include/sway/config.h b/include/sway/config.h
index 0be1cd22..5ccc3e77 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -689,10 +689,13 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt
689struct output_config *new_output_config(const char *name); 689struct output_config *new_output_config(const char *name);
690 690
691bool apply_output_configs(struct matched_output_config *configs, 691bool apply_output_configs(struct matched_output_config *configs,
692 size_t configs_len, bool test_only); 692 size_t configs_len, bool test_only, bool degrade_to_off);
693 693
694void apply_all_output_configs(void); 694void apply_all_output_configs(void);
695 695
696void sort_output_configs_by_priority(struct matched_output_config *configs,
697 size_t configs_len);
698
696/** 699/**
697 * store_output_config stores a new output config. An output may be matched by 700 * store_output_config stores a new output config. An output may be matched by
698 * three different config types, in order of precedence: Identifier, name and 701 * three different config types, in order of precedence: Identifier, name and
diff --git a/sway/config/output.c b/sway/config/output.c
index 7e037676..3ec5d77b 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -386,22 +386,18 @@ static int compute_default_scale(struct wlr_output *output,
386 return 2; 386 return 2;
387} 387}
388 388
389/* Lists of formats to try, in order, when a specific render bit depth has 389static bool render_format_is_10bit(uint32_t render_format) {
390 * been asked for. The second to last format in each list should always 390 return render_format == DRM_FORMAT_XRGB2101010 ||
391 * be XRGB8888, as a reliable backup in case the others are not available; 391 render_format == DRM_FORMAT_XBGR2101010;
392 * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ 392}
393static const uint32_t *bit_depth_preferences[] = { 393
394 [RENDER_BIT_DEPTH_8] = (const uint32_t []){ 394static bool render_format_is_bgr(uint32_t fmt) {
395 DRM_FORMAT_XRGB8888, 395 return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888;
396 DRM_FORMAT_INVALID, 396}
397 }, 397
398 [RENDER_BIT_DEPTH_10] = (const uint32_t []){ 398static bool output_config_is_disabling(struct output_config *oc) {
399 DRM_FORMAT_XRGB2101010, 399 return oc && (!oc->enabled || oc->power == 0);
400 DRM_FORMAT_XBGR2101010, 400}
401 DRM_FORMAT_XRGB8888,
402 DRM_FORMAT_INVALID,
403 },
404};
405 401
406static void queue_output_config(struct output_config *oc, 402static void queue_output_config(struct output_config *oc,
407 struct sway_output *output, struct wlr_output_state *pending) { 403 struct sway_output *output, struct wlr_output_state *pending) {
@@ -411,7 +407,7 @@ static void queue_output_config(struct output_config *oc,
411 407
412 struct wlr_output *wlr_output = output->wlr_output; 408 struct wlr_output *wlr_output = output->wlr_output;
413 409
414 if (oc && (!oc->enabled || oc->power == 0)) { 410 if (output_config_is_disabling(oc)) {
415 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); 411 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
416 wlr_output_state_set_enabled(pending, false); 412 wlr_output_state_set_enabled(pending, false);
417 return; 413 return;
@@ -434,22 +430,6 @@ static void queue_output_config(struct output_config *oc,
434 struct wlr_output_mode *preferred_mode = 430 struct wlr_output_mode *preferred_mode =
435 wlr_output_preferred_mode(wlr_output); 431 wlr_output_preferred_mode(wlr_output);
436 wlr_output_state_set_mode(pending, preferred_mode); 432 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 } 433 }
454 434
455 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 435 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
@@ -500,25 +480,17 @@ static void queue_output_config(struct output_config *oc,
500 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, 480 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
501 oc->adaptive_sync); 481 oc->adaptive_sync);
502 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); 482 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 } 483 }
508 484
509 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { 485 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
510 const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; 486 if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 &&
511 assert(fmts); 487 render_format_is_10bit(output->wlr_output->render_format)) {
512 488 // 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++) { 489 wlr_output_state_set_render_format(pending, output->wlr_output->render_format);
514 wlr_output_state_set_render_format(pending, fmts[i]); 490 } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) {
515 if (wlr_output_test_state(wlr_output, pending)) { 491 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010);
516 break; 492 } else {
517 } 493 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 } 494 }
523 } 495 }
524} 496}
@@ -649,8 +621,227 @@ struct output_config *find_output_config(struct sway_output *sway_output) {
649 return result; 621 return result;
650} 622}
651 623
624static bool config_has_auto_mode(struct output_config *oc) {
625 if (!oc) {
626 return true;
627 }
628 if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) {
629 return true;
630 } else if (oc->width > 0 && oc->height > 0) {
631 return true;
632 }
633 return false;
634}
635
636struct search_context {
637 struct wlr_output_swapchain_manager *swapchain_mgr;
638 struct wlr_backend_output_state *states;
639 struct matched_output_config *configs;
640 size_t configs_len;
641 bool degrade_to_off;
642};
643
644static bool search_valid_config(struct search_context *ctx, size_t output_idx);
645
646static void reset_output_state(struct wlr_output_state *state) {
647 wlr_output_state_finish(state);
648 wlr_output_state_init(state);
649 state->committed = 0;
650}
651
652static void clear_later_output_states(struct wlr_backend_output_state *states,
653 size_t configs_len, size_t output_idx) {
654
655 // Clear and disable all output states after this one to avoid conflict
656 // with previous tests.
657 for (size_t idx = output_idx+1; idx < configs_len; idx++) {
658 struct wlr_backend_output_state *backend_state = &states[idx];
659 struct wlr_output_state *state = &backend_state->base;
660
661 reset_output_state(state);
662 wlr_output_state_set_enabled(state, false);
663 }
664}
665
666static bool search_finish(struct search_context *ctx, size_t output_idx) {
667 clear_later_output_states(ctx->states, ctx->configs_len, output_idx);
668 return wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) &&
669 search_valid_config(ctx, output_idx+1);
670}
671
672static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) {
673 struct matched_output_config *cfg = &ctx->configs[output_idx];
674 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
675 struct wlr_output_state *state = &backend_state->base;
676
677 if (cfg->config && cfg->config->adaptive_sync == 1) {
678 wlr_output_state_set_adaptive_sync_enabled(state, true);
679 if (search_finish(ctx, output_idx)) {
680 return true;
681 }
682 }
683 if (!cfg->config || cfg->config->adaptive_sync != -1) {
684 sway_log(SWAY_DEBUG, "Trying with adaptive sync disabled for: %s",
685 backend_state->output->name);
686 wlr_output_state_set_adaptive_sync_enabled(state, false);
687 if (search_finish(ctx, output_idx)) {
688 return true;
689 }
690 }
691 // If adaptive sync has not been set, or fallback in case we are on a
692 // backend that cannot disable adaptive sync such as the wayland backend.
693 sway_log(SWAY_DEBUG, "Trying with adaptive sync unset for: %s",
694 backend_state->output->name);
695 state->committed &= ~WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED;
696 return search_finish(ctx, output_idx);
697}
698
699static bool search_mode(struct search_context *ctx, size_t output_idx) {
700 struct matched_output_config *cfg = &ctx->configs[output_idx];
701 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
702 struct wlr_output_state *state = &backend_state->base;
703 struct wlr_output *wlr_output = backend_state->output;
704
705 if (!config_has_auto_mode(cfg->config)) {
706 return search_adaptive_sync(ctx, output_idx);
707 }
708
709 struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
710 if (preferred_mode) {
711 sway_log(SWAY_DEBUG, "Trying with preferred mode for: %s", backend_state->output->name);
712 wlr_output_state_set_mode(state, preferred_mode);
713 if (search_adaptive_sync(ctx, output_idx)) {
714 return true;
715 }
716 }
717
718 if (wl_list_empty(&wlr_output->modes)) {
719 state->committed &= ~WLR_OUTPUT_STATE_MODE;
720 return search_adaptive_sync(ctx, output_idx);
721 }
722
723 struct wlr_output_mode *mode;
724 wl_list_for_each(mode, &backend_state->output->modes, link) {
725 if (mode == preferred_mode) {
726 continue;
727 }
728 sway_log(SWAY_DEBUG, "Trying with mode %dx%d@%dmHz for: %s",
729 mode->width, mode->height, mode->refresh, backend_state->output->name);
730 wlr_output_state_set_mode(state, mode);
731 if (search_adaptive_sync(ctx, output_idx)) {
732 return true;
733 }
734 }
735
736 return false;
737}
738
739static bool search_render_format(struct search_context *ctx, size_t output_idx) {
740 struct matched_output_config *cfg = &ctx->configs[output_idx];
741 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
742 struct wlr_output_state *state = &backend_state->base;
743 struct wlr_output *wlr_output = backend_state->output;
744
745 uint32_t fmts[] = {
746 DRM_FORMAT_XRGB2101010,
747 DRM_FORMAT_XBGR2101010,
748 DRM_FORMAT_XRGB8888,
749 DRM_FORMAT_INVALID,
750 };
751 if (render_format_is_bgr(wlr_output->render_format)) {
752 // Start with BGR in the unlikely event that we previously required it.
753 fmts[0] = DRM_FORMAT_XBGR2101010;
754 fmts[1] = DRM_FORMAT_XRGB2101010;
755 }
756
757 const struct wlr_drm_format_set *primary_formats =
758 wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF);
759 bool need_10bit = cfg->config && cfg->config->render_bit_depth == RENDER_BIT_DEPTH_10;
760 for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) {
761 if (!need_10bit && render_format_is_10bit(fmts[idx])) {
762 continue;
763 }
764 if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) {
765 // This is not a supported format for this output
766 continue;
767 }
768 sway_log(SWAY_DEBUG, "Trying with render format %d for: %s", fmts[idx],
769 wlr_output->name);
770 wlr_output_state_set_render_format(state, fmts[idx]);
771 if (search_mode(ctx, output_idx)) {
772 return true;
773 }
774 }
775 return false;
776}
777
778static bool search_valid_config(struct search_context *ctx, size_t output_idx) {
779 if (output_idx >= ctx->configs_len) {
780 // We reached the end of the search, all good!
781 return true;
782 }
783
784 struct matched_output_config *cfg = &ctx->configs[output_idx];
785 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
786 struct wlr_output_state *state = &backend_state->base;
787
788 sway_log(SWAY_DEBUG, "Finding valid config for: %s",
789 backend_state->output->name);
790
791 if (!output_config_is_disabling(cfg->config)) {
792 // Search through our possible configurations, doing a depth-first
793 // through render_format, modes, adaptive_sync and the next output's
794 // config.
795 queue_output_config(cfg->config, cfg->output, &backend_state->base);
796 if (search_render_format(ctx, output_idx)) {
797 return true;
798 } else if (!ctx->degrade_to_off) {
799 return false;
800 }
801 // We could not get anything to work, try to disable this output to see
802 // if we can at least make the outputs before us work.
803 sway_log(SWAY_DEBUG, "Trying with disabled output for: %s",
804 backend_state->output->name);
805 reset_output_state(state);
806 }
807
808 wlr_output_state_set_enabled(state, false);
809 return search_finish(ctx, output_idx);
810}
811
812static int compare_matched_output_config_priority(const void *a, const void *b) {
813
814 const struct matched_output_config *amc = a;
815 const struct matched_output_config *bmc = b;
816 bool a_disabling = output_config_is_disabling(amc->config);
817 bool b_disabling = output_config_is_disabling(bmc->config);
818 bool a_enabled = amc->output->enabled;
819 bool b_enabled = bmc->output->enabled;
820
821 // We want to give priority to existing enabled outputs. To do so, we want
822 // the configuration order to be:
823 // 1. Existing, enabled outputs
824 // 2. Outputs that need to be enabled
825 // 3. Disabled or disabling outputs
826 if (a_enabled && !a_disabling) {
827 return -1;
828 } else if (b_enabled && !b_disabling) {
829 return 1;
830 } else if (b_disabling && !a_disabling) {
831 return -1;
832 } else if (a_disabling && !b_disabling) {
833 return 1;
834 }
835 return 0;
836}
837
838void sort_output_configs_by_priority(struct matched_output_config *configs,
839 size_t configs_len) {
840 qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority);
841}
842
652bool apply_output_configs(struct matched_output_config *configs, 843bool apply_output_configs(struct matched_output_config *configs,
653 size_t configs_len, bool test_only) { 844 size_t configs_len, bool test_only, bool degrade_to_off) {
654 struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); 845 struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));
655 if (!states) { 846 if (!states) {
656 return false; 847 return false;
@@ -674,8 +865,18 @@ bool apply_output_configs(struct matched_output_config *configs,
674 865
675 bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len); 866 bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len);
676 if (!ok) { 867 if (!ok) {
677 sway_log(SWAY_ERROR, "Swapchain prepare failed"); 868 sway_log(SWAY_ERROR, "Requested backend configuration failed, searching for valid fallbacks");
678 goto out; 869 struct search_context ctx = {
870 .swapchain_mgr = &swapchain_mgr,
871 .states = states,
872 .configs = configs,
873 .configs_len = configs_len,
874 .degrade_to_off = degrade_to_off,
875 };
876 if (!search_valid_config(&ctx, 0)) {
877 sway_log(SWAY_ERROR, "Search for valid config failed");
878 goto out;
879 }
679 } 880 }
680 881
681 if (test_only) { 882 if (test_only) {
@@ -761,7 +962,8 @@ void apply_all_output_configs(void) {
761 config->config = find_output_config(sway_output); 962 config->config = find_output_config(sway_output);
762 } 963 }
763 964
764 apply_output_configs(configs, configs_len, false); 965 sort_output_configs_by_priority(configs, configs_len);
966 apply_output_configs(configs, configs_len, false, true);
765 for (size_t idx = 0; idx < configs_len; idx++) { 967 for (size_t idx = 0; idx < configs_len; idx++) {
766 struct matched_output_config *cfg = &configs[idx]; 968 struct matched_output_config *cfg = &configs[idx];
767 free_output_config(cfg->config); 969 free_output_config(cfg->config);
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 70987feb..2722e556 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -609,7 +609,8 @@ static void output_manager_apply(struct sway_server *server,
609 } 609 }
610 } 610 }
611 611
612 bool ok = apply_output_configs(configs, configs_len, test_only); 612 sort_output_configs_by_priority(configs, configs_len);
613 bool ok = apply_output_configs(configs, configs_len, test_only, false);
613 for (size_t idx = 0; idx < configs_len; idx++) { 614 for (size_t idx = 0; idx < configs_len; idx++) {
614 struct matched_output_config *cfg = &configs[idx]; 615 struct matched_output_config *cfg = &configs[idx];
615 616