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 =
|
||||
backend.IsDevicePresent(mode_device_name) && backend.RestoreMode(mode_device_name, width, height, refresh_rate);
|
||||
if (mode_handoff_pending) {
|
||||
// Restore the replacement first and the pre-handoff original last. Both
|
||||
// remain marked until both idempotent restores and cleanup succeed.
|
||||
// Restore the replacement first and the pre-handoff original last. If
|
||||
// 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 =
|
||||
backend.IsDevicePresent(previous_mode_device_name) &&
|
||||
backend.RestoreMode(previous_mode_device_name, previous_width, previous_height, previous_refresh_rate);
|
||||
if (!restored || !previous_restored ||
|
||||
!CompleteRecoveryOperation(backend, kRegModeChanged, kRegModeTakeoverEligible, kRegModeHandoffPending)) {
|
||||
RollbackRecoveryHandoff(
|
||||
backend, kRegModeTakeoverEligible, kRegModeRecoverySlot, kRegModePreviousRecoverySlot,
|
||||
kRegModeHandoffPending);
|
||||
completed = false;
|
||||
}
|
||||
} 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);
|
||||
if (!restored || !previous_restored ||
|
||||
!CompleteRecoveryOperation(backend, kRegHDRChanged, kRegHDRTakeoverEligible, kRegHDRHandoffPending)) {
|
||||
RollbackRecoveryHandoff(
|
||||
backend, kRegHDRTakeoverEligible, kRegHDRRecoverySlot, kRegHDRPreviousRecoverySlot, kRegHDRHandoffPending);
|
||||
completed = false;
|
||||
}
|
||||
} else if (
|
||||
|
||||
@@ -699,6 +699,64 @@ void TestCrashDuringTakeoverHandoffRecoversBothOriginals() {
|
||||
"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() {
|
||||
FakeRecoveryBackend backend;
|
||||
backend.SeedBoth();
|
||||
@@ -1051,6 +1109,7 @@ int main() {
|
||||
mpv::TestBadModeHDRRestoreAllowsSameKindTakeover();
|
||||
mpv::TestFailedOSApplyRollsBackTakeover();
|
||||
mpv::TestCrashDuringTakeoverHandoffRecoversBothOriginals();
|
||||
mpv::TestFailedHandoffRecoveryRollsBackForTakeover();
|
||||
mpv::TestHDRReconnectRecoversBeforeTakeover();
|
||||
mpv::TestEligibleSameOriginalReuseResetsDisposition();
|
||||
mpv::TestRepeatedModeTakeoverStagesOnlyTheInactiveSlot();
|
||||
|
||||
Reference in New Issue
Block a user