fix(windows): recover failed display handoffs
This commit is contained in:
@@ -1142,13 +1142,17 @@ bool RecoverRecord(DisplayRecoveryBackend& backend, bool mode_is_live, bool hdr_
|
|||||||
const bool restored =
|
const bool restored =
|
||||||
backend.IsDevicePresent(mode_device_name) && backend.RestoreMode(mode_device_name, width, height, refresh_rate);
|
backend.IsDevicePresent(mode_device_name) && backend.RestoreMode(mode_device_name, width, height, refresh_rate);
|
||||||
if (mode_handoff_pending) {
|
if (mode_handoff_pending) {
|
||||||
// Restore the replacement first and the pre-handoff original last. Both
|
// Restore the replacement first and the pre-handoff original last. If
|
||||||
// remain marked until both idempotent restores and cleanup succeed.
|
// either restore or cleanup fails, collapse authority back to the prior
|
||||||
|
// slot so topology retries do not keep forcing the surviving replacement.
|
||||||
const bool previous_restored =
|
const bool previous_restored =
|
||||||
backend.IsDevicePresent(previous_mode_device_name) &&
|
backend.IsDevicePresent(previous_mode_device_name) &&
|
||||||
backend.RestoreMode(previous_mode_device_name, previous_width, previous_height, previous_refresh_rate);
|
backend.RestoreMode(previous_mode_device_name, previous_width, previous_height, previous_refresh_rate);
|
||||||
if (!restored || !previous_restored ||
|
if (!restored || !previous_restored ||
|
||||||
!CompleteRecoveryOperation(backend, kRegModeChanged, kRegModeTakeoverEligible, kRegModeHandoffPending)) {
|
!CompleteRecoveryOperation(backend, kRegModeChanged, kRegModeTakeoverEligible, kRegModeHandoffPending)) {
|
||||||
|
RollbackRecoveryHandoff(
|
||||||
|
backend, kRegModeTakeoverEligible, kRegModeRecoverySlot, kRegModePreviousRecoverySlot,
|
||||||
|
kRegModeHandoffPending);
|
||||||
completed = false;
|
completed = false;
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
@@ -1170,6 +1174,8 @@ bool RecoverRecord(DisplayRecoveryBackend& backend, bool mode_is_live, bool hdr_
|
|||||||
backend.RestoreHDR(previous_hdr_device_name, previous_original_hdr != 0);
|
backend.RestoreHDR(previous_hdr_device_name, previous_original_hdr != 0);
|
||||||
if (!restored || !previous_restored ||
|
if (!restored || !previous_restored ||
|
||||||
!CompleteRecoveryOperation(backend, kRegHDRChanged, kRegHDRTakeoverEligible, kRegHDRHandoffPending)) {
|
!CompleteRecoveryOperation(backend, kRegHDRChanged, kRegHDRTakeoverEligible, kRegHDRHandoffPending)) {
|
||||||
|
RollbackRecoveryHandoff(
|
||||||
|
backend, kRegHDRTakeoverEligible, kRegHDRRecoverySlot, kRegHDRPreviousRecoverySlot, kRegHDRHandoffPending);
|
||||||
completed = false;
|
completed = false;
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
|
|||||||
@@ -699,6 +699,64 @@ void TestCrashDuringTakeoverHandoffRecoversBothOriginals() {
|
|||||||
"HDR handoff recovery must restore replacement then prior and preserve its sibling");
|
"HDR handoff recovery must restore replacement then prior and preserve its sibling");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestFailedHandoffRecoveryRollsBackForTakeover() {
|
||||||
|
FakeRecoveryBackend mode;
|
||||||
|
mode.SeedBoth();
|
||||||
|
mode.dwords[kModeTakeoverEligible] = 1;
|
||||||
|
mode.dwords[kHDRTakeoverEligible] = 1;
|
||||||
|
Check(
|
||||||
|
DisplayModeManager::PrepareModeRecovery(mode, kReplacementModeDevice, 2560, 1440, 120),
|
||||||
|
"mode takeover preparation must reach its pre-apply handoff");
|
||||||
|
mode.expected_mode_restores = {{kReplacementModeDevice, 2560, 1440, 120}};
|
||||||
|
mode.device_present[kModeDevice] = false;
|
||||||
|
Check(
|
||||||
|
!DisplayModeManager::RecoverIfNeededForTesting(mode, false, true),
|
||||||
|
"a missing prior mode display must leave handoff recovery incomplete");
|
||||||
|
Check(
|
||||||
|
mode.mode_restore_attempts == 1 && mode.dwords[kModeChanged] == 1 && mode.dwords[kModeRecoverySlot] == 0 &&
|
||||||
|
mode.dwords[kModeTakeoverEligible] == 1 && mode.dwords[kModeHandoffPending] == 0,
|
||||||
|
"failed mode handoff recovery must roll authority back to a takeover-eligible prior slot");
|
||||||
|
|
||||||
|
mode.events.clear();
|
||||||
|
Check(
|
||||||
|
!DisplayModeManager::RecoverIfNeededForTesting(mode, false, true),
|
||||||
|
"the absent prior mode display must remain retryable after handoff rollback");
|
||||||
|
Check(
|
||||||
|
EventIndex(mode.events, L"restore:mode") == mode.events.size(),
|
||||||
|
"topology retries must not keep restoring the surviving replacement display");
|
||||||
|
Check(
|
||||||
|
ApplyModeAfterPreparing(mode, kReplacementModeDevice, 2560, 1440, 120),
|
||||||
|
"a later mode request must be able to consume the rolled-back handoff");
|
||||||
|
|
||||||
|
FakeRecoveryBackend hdr;
|
||||||
|
hdr.SeedBoth();
|
||||||
|
hdr.dwords[kModeTakeoverEligible] = 1;
|
||||||
|
hdr.dwords[kHDRTakeoverEligible] = 1;
|
||||||
|
Check(
|
||||||
|
DisplayModeManager::PrepareHDRRecovery(hdr, kReplacementHDRDevice, true),
|
||||||
|
"HDR takeover preparation must reach its pre-apply handoff");
|
||||||
|
hdr.expected_hdr_restores = {{kReplacementHDRDevice, true}};
|
||||||
|
hdr.device_present[kHDRDevice] = false;
|
||||||
|
Check(
|
||||||
|
!DisplayModeManager::RecoverIfNeededForTesting(hdr, true, false),
|
||||||
|
"a missing prior HDR display must leave handoff recovery incomplete");
|
||||||
|
Check(
|
||||||
|
hdr.hdr_restore_attempts == 1 && hdr.dwords[kHDRChanged] == 1 && hdr.dwords[kHDRRecoverySlot] == 0 &&
|
||||||
|
hdr.dwords[kHDRTakeoverEligible] == 1 && hdr.dwords[kHDRHandoffPending] == 0,
|
||||||
|
"failed HDR handoff recovery must roll authority back to a takeover-eligible prior slot");
|
||||||
|
|
||||||
|
hdr.events.clear();
|
||||||
|
Check(
|
||||||
|
!DisplayModeManager::RecoverIfNeededForTesting(hdr, true, false),
|
||||||
|
"the absent prior HDR display must remain retryable after handoff rollback");
|
||||||
|
Check(
|
||||||
|
EventIndex(hdr.events, L"restore:hdr") == hdr.events.size(),
|
||||||
|
"topology retries must not keep restoring the surviving replacement HDR state");
|
||||||
|
Check(
|
||||||
|
ApplyHDRAfterPreparing(hdr, kReplacementHDRDevice, true),
|
||||||
|
"a later HDR request must be able to consume the rolled-back handoff");
|
||||||
|
}
|
||||||
|
|
||||||
void TestHDRReconnectRecoversBeforeTakeover() {
|
void TestHDRReconnectRecoversBeforeTakeover() {
|
||||||
FakeRecoveryBackend backend;
|
FakeRecoveryBackend backend;
|
||||||
backend.SeedBoth();
|
backend.SeedBoth();
|
||||||
@@ -1051,6 +1109,7 @@ int main() {
|
|||||||
mpv::TestBadModeHDRRestoreAllowsSameKindTakeover();
|
mpv::TestBadModeHDRRestoreAllowsSameKindTakeover();
|
||||||
mpv::TestFailedOSApplyRollsBackTakeover();
|
mpv::TestFailedOSApplyRollsBackTakeover();
|
||||||
mpv::TestCrashDuringTakeoverHandoffRecoversBothOriginals();
|
mpv::TestCrashDuringTakeoverHandoffRecoversBothOriginals();
|
||||||
|
mpv::TestFailedHandoffRecoveryRollsBackForTakeover();
|
||||||
mpv::TestHDRReconnectRecoversBeforeTakeover();
|
mpv::TestHDRReconnectRecoversBeforeTakeover();
|
||||||
mpv::TestEligibleSameOriginalReuseResetsDisposition();
|
mpv::TestEligibleSameOriginalReuseResetsDisposition();
|
||||||
mpv::TestRepeatedModeTakeoverStagesOnlyTheInactiveSlot();
|
mpv::TestRepeatedModeTakeoverStagesOnlyTheInactiveSlot();
|
||||||
|
|||||||
Reference in New Issue
Block a user