Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 111 additions & 3 deletions vibrance.GUI/AMD/AmdDynamicVibranceProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,21 @@ private void OnWinEventHook(object sender, WinEventHookEventArgs e)

if (applicationSetting != null)
{
if (ProfileToggleHelper.IsSuppressed(applicationSetting.Name))
{
// Toggled off by hotkey (upstream #143). Ignore this foreground event for
// this game entirely - deliberately NOT a fall-through to the restore branch
// below: the toggle itself already restored this display
// (ToggleForegroundProfile), and re-running the work-list restore on every
// alt-tab into a suppressed game would reach displays this game never even
// touched.
//
// Returns BEFORE "_gameScreen = screen" below: a suppressed game applies
// nothing here, so it must not become the screen a later resolution revert
// reasons about.
return;
}

Screen screen = Screen.FromHandle(e.Handle);
_gameScreen = screen;

Expand Down Expand Up @@ -263,11 +278,16 @@ private void RestoreWindowsVibranceLevel()
return;
}

// IAmdAdapter has no read-back to confirm a write landed, unlike NVIDIA's
// This restore path still has no read-back to confirm a write landed, unlike NVIDIA's
// equalsDVCLevel/setDVCLevel pair - so, unlike NvidiaDynamicVibranceProxy's
// RestoreOneDisplay, every target is written unconditionally and cleared
// RestoreOneDisplay, every target here is written unconditionally and cleared
// unconditionally, unable to tell "already correct" from "just fixed" or to retry a
// failure that has no way to be observed here.
// failure that has no way to be observed here. That is no longer true of
// IAmdAdapter.SetSaturationOnDisplay itself (upstream #143 gave it a real ADL_OK-based
// bool return) - it is just that THIS call site, deliberately, still ignores it: doing
// otherwise would make this drain conditionally, changing behaviour the pre-existing
// A1-A6 checks in VibranceRestoreFixture pin. ToggleForegroundProfile below is the one
// call site that actually reads the new return value.
List<string> targets = VibranceRestoreHelper.ComposeRestoreTargets(true, VibranceRestoreHelper.GetPrimaryDeviceName());
foreach (string deviceName in targets)
{
Expand All @@ -276,6 +296,94 @@ private void RestoreWindowsVibranceLevel()
}
}

/// <summary>
/// See IVibranceProxy.ToggleForegroundProfile for the full contract. Decide (pure) picks
/// the direction from our own recorded suppression state, never from a display read-back;
/// this method is only the write plus the flip. Unlike RestoreWindowsVibranceLevel above,
/// this DOES read IAmdAdapter.SetSaturationOnDisplay's new bool return - the toggle path
/// is the one place a false success genuinely matters, since flipping suppression on a
/// write that never landed would strand the game at whatever level it was already at
/// while telling the engine (and the user) the opposite.
///
/// Branches on affectPrimaryMonitorOnly, mirroring OnWinEventHook's own apply branch
/// above - unlike NVIDIA, the AMD apply is NOT single-display with the flag off (the
/// DEFAULT): it writes every attached screen via SetSaturationOnAllDisplays and records
/// all of them. A toggle that only ever touched deviceName would write one display back
/// to the Windows level while every other monitor stayed at the game's saturation - with
/// the balloon claiming the profile was restored - for as long as the user stays in the
/// suppressed game, since the suppression gate returns early on every later event.
/// </summary>
public ProfileToggleResult ToggleForegroundProfile(IntPtr foregroundWindow, string processName, string processImagePath)
{
ProfileToggleDecision decision = ProfileToggleHelper.Decide(
_applicationSettings, processName, processImagePath, _vibranceInfo.isWindowsLevelKnown);

if (decision.Action == ProfileToggleAction.None)
{
return ProfileToggleResult.NoConfiguredGameInForeground;
}
if (decision.Action == ProfileToggleAction.EngineNotReady)
{
return ProfileToggleResult.EngineNotReady;
}

string deviceName = Screen.FromHandle(foregroundWindow).DeviceName;
string name = decision.Setting.Name;

if (decision.Action == ProfileToggleAction.ApplyGameLevel)
{
if (_vibranceInfo.affectPrimaryMonitorOnly)
{
if (!_amdAdapter.SetSaturationOnDisplay(decision.Setting.IngameLevel, deviceName))
{
return ProfileToggleResult.WriteFailed;
}
// Only the game's own screen was written - that is the only display owing a
// restore.
VibranceRestoreHelper.RecordGameLevelApplied(deviceName);
}
else
{
// The identical write SetSaturationOnAllDisplays makes internally
// (AmdAdapter32/64.cs: "SetSaturationOnDisplay(vibranceLevel, null)"), but
// through the named-display overload so the new ADL_OK-based bool return
// survives for this method to actually check - see its own header comment.
if (!_amdAdapter.SetSaturationOnDisplay(decision.Setting.IngameLevel, null))
{
return ProfileToggleResult.WriteFailed;
}
// This really did write every attached display, not just the game's own -
// every one of them is recorded as owing a restore, mirroring the automatic
// apply branch above.
foreach (Screen attachedScreen in Screen.AllScreens)
{
VibranceRestoreHelper.RecordGameLevelApplied(attachedScreen.DeviceName);
}
}
ProfileToggleHelper.SetSuppressed(name, false);
return ProfileToggleResult.ToggledOn;
}

if (_vibranceInfo.affectPrimaryMonitorOnly)
{
if (!_amdAdapter.SetSaturationOnDisplay(_vibranceInfo.userVibranceSettingDefault, deviceName))
{
return ProfileToggleResult.WriteFailed;
}
VibranceRestoreHelper.ClearGameLevelRecord(deviceName);
}
else
{
if (!_amdAdapter.SetSaturationOnDisplay(_vibranceInfo.userVibranceSettingDefault, null))
{
return ProfileToggleResult.WriteFailed;
}
VibranceRestoreHelper.ClearAllGameLevelRecords();
}
ProfileToggleHelper.SetSuppressed(name, true);
return ProfileToggleResult.ToggledOff;
}

private void RestoreWindowsColorSettings()
{
//restores every screen whose gamma ramp this application actually captured a baseline
Expand Down
31 changes: 29 additions & 2 deletions vibrance.GUI/AMD/vendor/AmdAdapter32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,21 +124,48 @@ public void SetSaturationOnAllDisplays(int vibranceLevel)
this.SetSaturationOnDisplay(vibranceLevel, null);
}

public void SetSaturationOnDisplay(int vibranceLevel, string displayName)
public bool SetSaturationOnDisplay(int vibranceLevel, string displayName)
{
// matchedAny/allSucceeded are closed over by the handler below, the same way the
// pre-existing lambda already closes over vibranceLevel/displayName - SetSaturation
// itself stays a void-returning Action, only what its handler does with the result
// changes. "No display matched" (matchedAny stays false) must report false, not the
// vacuous "true" an empty loop would otherwise imply - see IAmdAdapter's own comment.
bool matchedAny = false;
bool allSucceeded = true;
SetSaturation((adlDisplayInfo, adlAdapterInfo, adapterIndex) =>
{
int infoValue = adlDisplayInfo.DisplayID.DisplayLogicalIndex;
bool adapterIsAssociatedWithDisplay = adapterIndex == adlDisplayInfo.DisplayID.DisplayLogicalAdapterIndex;
if (adapterIsAssociatedWithDisplay && (adlAdapterInfo.DisplayName == displayName || displayName == null))
{
Adl.AdlDisplayColorSet(
matchedAny = true;

// Adl.AdlDisplayColorSet can be null - IsFunctionValid (ADLCheckLibrary.cs)
// failed to resolve "ADL_Display_Color_Set" from the driver's DLL. The
// pre-existing call below was unguarded against that (a latent NRE); guarded
// here since this line is already being touched for the status-code fix.
if (Adl.AdlDisplayColorSet == null)
{
allSucceeded = false;
return;
}

// AdlSuccess (= 0) is ADL_OK - reusing the constant this file already defines
// and already checks every other ADL return code against, rather than adding
// a second name for the same value.
int adlStatus = Adl.AdlDisplayColorSet(
adapterIndex,
infoValue,
Adl.AdlDisplayColorSaturation,
vibranceLevel);
if (adlStatus != Adl.AdlSuccess)
{
allSucceeded = false;
}
}
});
return matchedAny && allSucceeded;
}

private void SetSaturation(Action<AdlDisplayInfo, AdlAdapterInfo, int> handle)
Expand Down
31 changes: 29 additions & 2 deletions vibrance.GUI/AMD/vendor/AmdAdapter64.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,21 +124,48 @@ public void SetSaturationOnAllDisplays(int vibranceLevel)
this.SetSaturationOnDisplay(vibranceLevel, null);
}

public void SetSaturationOnDisplay(int vibranceLevel, string displayName)
public bool SetSaturationOnDisplay(int vibranceLevel, string displayName)
{
// matchedAny/allSucceeded are closed over by the handler below, the same way the
// pre-existing lambda already closes over vibranceLevel/displayName - SetSaturation
// itself stays a void-returning Action, only what its handler does with the result
// changes. "No display matched" (matchedAny stays false) must report false, not the
// vacuous "true" an empty loop would otherwise imply - see IAmdAdapter's own comment.
bool matchedAny = false;
bool allSucceeded = true;
SetSaturation((adlDisplayInfo, adlAdapterInfo, adapterIndex) =>
{
int infoValue = adlDisplayInfo.DisplayID.DisplayLogicalIndex;
bool adapterIsAssociatedWithDisplay = adapterIndex == adlDisplayInfo.DisplayID.DisplayLogicalAdapterIndex;
if (adapterIsAssociatedWithDisplay && (adlAdapterInfo.DisplayName == displayName || displayName == null))
{
Adl.AdlDisplayColorSet(
matchedAny = true;

// Adl.AdlDisplayColorSet can be null - IsFunctionValid (ADLCheckLibrary.cs)
// failed to resolve "ADL_Display_Color_Set" from the driver's DLL. The
// pre-existing call below was unguarded against that (a latent NRE); guarded
// here since this line is already being touched for the status-code fix.
if (Adl.AdlDisplayColorSet == null)
{
allSucceeded = false;
return;
}

// AdlSuccess (= 0) is ADL_OK - reusing the constant this file already defines
// and already checks every other ADL return code against, rather than adding
// a second name for the same value.
int adlStatus = Adl.AdlDisplayColorSet(
adapterIndex,
infoValue,
Adl.AdlDisplayColorSaturation,
vibranceLevel);
if (adlStatus != Adl.AdlSuccess)
{
allSucceeded = false;
}
}
});
return matchedAny && allSucceeded;
}

private void SetSaturation(Action<AdlDisplayInfo, AdlAdapterInfo, int> handle)
Expand Down
9 changes: 8 additions & 1 deletion vibrance.GUI/AMD/vendor/IAmdAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ public interface IAmdAdapter : IDisposable
{
void SetSaturationOnAllDisplays(int vibranceLevel);

void SetSaturationOnDisplay(int vibranceLevel, string displayName);
/// <summary>
/// True only when at least one display actually matched displayName (or, for the
/// SetSaturationOnAllDisplays fan-out, at least one display existed at all) AND every ADL
/// call for a matched display returned ADL_OK. "No display matched, so nothing was even
/// attempted" must report false, not true - see the implementations for why that
/// distinction was previously unbuildable (this method used to return void).
/// </summary>
bool SetSaturationOnDisplay(int vibranceLevel, string displayName);

bool IsAvailable();

Expand Down
Loading