Skip to content

Commit

Permalink
Help dGPU eject in Hybrid iGPU-only mode on boot (#1112)
Browse files Browse the repository at this point in the history
  • Loading branch information
BartoszCichecki authored Jan 9, 2024
1 parent fa8a16d commit a20bab7
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 0 deletions.
63 changes: 63 additions & 0 deletions LenovoLegionToolkit.Lib/Features/Hybrid/HybridModeFeature.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using LenovoLegionToolkit.Lib.Features.Hybrid.Notify;
using LenovoLegionToolkit.Lib.Utils;
Expand All @@ -11,6 +12,8 @@ public class HybridModeFeature : IFeature<HybridModeState>
private readonly IGPUModeFeature _igpuModeFeature;
private readonly DGPUNotify _dgpuNotify;

private readonly CancellationTokenSource _ensureDGPUEjectedIfNeededCancellationTokenSource = new();

public HybridModeFeature(GSyncFeature gSyncFeature, IGPUModeFeature igpuModeFeature, DGPUNotify dgpuNotify)
{
_gSyncFeature = gSyncFeature ?? throw new ArgumentNullException(nameof(gSyncFeature));
Expand Down Expand Up @@ -64,6 +67,8 @@ public async Task<HybridModeState> GetStateAsync()

public async Task SetStateAsync(HybridModeState state)
{
_ensureDGPUEjectedIfNeededCancellationTokenSource.Cancel();

var (gSync, igpuMode) = Unpack(state);

if (Log.Instance.IsTraceEnabled)
Expand Down Expand Up @@ -102,6 +107,64 @@ public async Task SetStateAsync(HybridModeState state)
Log.Instance.Trace($"State set to {state} [gSync={gSync}, igpuMode={igpuMode}]");
}

public async Task EnsureDGPUEjectedIfNeededAsync()
{
if (!await _igpuModeFeature.IsSupportedAsync().ConfigureAwait(false) || !await _dgpuNotify.IsSupportedAsync().ConfigureAwait(false))
return;

_ = Task.Run(async () =>
{
try
{
const int maxRetries = 5;
const int delay = 5 * 1000;

var retry = 1;

if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"Will make sure that dGPU is ejected. [maxRetries={maxRetries}, delay={delay}ms]");

while (retry <= maxRetries)
{
await Task.Delay(delay).ConfigureAwait(false);

if (_ensureDGPUEjectedIfNeededCancellationTokenSource.IsCancellationRequested)
{
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"Cancelled, aborting...");
break;
}

if (await _igpuModeFeature.GetStateAsync().ConfigureAwait(false) != IGPUModeState.IGPUOnly)
{
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"Not in iGPU-only mode, aborting...");
break;
}

if (!await _dgpuNotify.IsDGPUAvailableAsync().ConfigureAwait(false))
{
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"dGPU already unavailable, aborting...");
break;
}

if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"Notifying dGPU... [retry={retry}, maxRetries={maxRetries}]");

await _dgpuNotify.NotifyAsync(false).ConfigureAwait(false);

retry++;
}
}
catch (Exception ex)
{
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"Failed to ensure dGPU is ejected", ex);
}
});
}

private static (GSyncState, IGPUModeState) Unpack(HybridModeState state) => state switch
{
HybridModeState.On => (GSyncState.Off, IGPUModeState.Default),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@ public abstract class AbstractDGPUNotify : IDGPUNotify

public abstract Task<bool> IsSupportedAsync();

public async Task<bool> IsDGPUAvailableAsync()
{
try
{
var dgpuHardwareId = await GetDGPUHardwareIdAsync().ConfigureAwait(false);
var isAvailable = IsDGPUAvailable(dgpuHardwareId);
return isAvailable;
}
catch (Exception ex)
{
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"Failed to notify.", ex);
return false;
}
}

public async Task NotifyAsync(bool publish = true)
{
lock (_lock)
Expand Down
6 changes: 6 additions & 0 deletions LenovoLegionToolkit.Lib/Features/Hybrid/Notify/DGPUNotify.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ public DGPUNotify(DGPUGamezoneNotify gamezoneNotify, DGPUCapabilityNotify capabi

public async Task<bool> IsSupportedAsync() => await _lazyAsyncNotify.Value.ConfigureAwait(false) != null;

public async Task<bool> IsDGPUAvailableAsync()
{
var feature = await _lazyAsyncNotify.Value.ConfigureAwait(false) ?? throw new InvalidOperationException($"No supported feature found. [type={GetType().Name}]");
return await feature.IsDGPUAvailableAsync().ConfigureAwait(false);
}

public async Task NotifyAsync(bool publish = true)
{
var feature = await _lazyAsyncNotify.Value.ConfigureAwait(false) ?? throw new InvalidOperationException($"No supported feature found. [type={GetType().Name}]");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public interface IDGPUNotify
public event EventHandler<bool>? Notified;

Task<bool> IsSupportedAsync();
Task<bool> IsDGPUAvailableAsync();
Task NotifyAsync(bool publish = true);
Task NotifyLaterIfNeededAsync();
}
18 changes: 18 additions & 0 deletions LenovoLegionToolkit.WPF/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ private async void Application_Startup(object sender, StartupEventArgs e)
await InitSpectrumKeyboardControllerAsync();
await InitGpuOverclockControllerAsync();
await InitAutomationProcessorAsync();
await InitHybridModeAsync();

await IoCContainer.Resolve<AIController>().StartIfNeededAsync();

Expand Down Expand Up @@ -340,6 +341,23 @@ private static async Task LogSoftwareStatusAsync()
Log.Instance.Trace($"FnKeys status: {fnKeysStatus}");
}

private static async Task InitHybridModeAsync()
{
try
{
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"Initializing hybrid mode...");

var feature = IoCContainer.Resolve<HybridModeFeature>();
await feature.EnsureDGPUEjectedIfNeededAsync();
}
catch (Exception ex)
{
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"Couldn't initialize hybrid mode.", ex);
}
}

private static async Task InitAutomationProcessorAsync()
{
try
Expand Down

0 comments on commit a20bab7

Please sign in to comment.