Closed RoadToDream closed 2 years ago
To clarify: You have set the mode to iGPU-only and the GPU is ejected from the system. Then you change back to Hybrid and dGPU doesn't get reconnected?
To clarify: You have set the mode to iGPU-only and the GPU is ejected from the system. Then you change back to Hybrid and dGPU doesn't get reconnected?
Hi! Thanks for the reply. Both operations will not work correctly when Vantage service is disabled. While change from iGPU-only to Hybrid may work occasionally. If Vantage service is enabled, switch between GPU mode will still probably not work. Control via Vantage directly can change GPU states correctly. I have debugged program on my laptop using Visual Studio version 2022, and no exception is thrown.
I am willing to provide more info if required.
I can see that something might be missing compared to what Vantage is doing. I made a quick patch that may or may not work - we need to do some trouble shooting.
You can try the build below, maybe it's gonna work?
You can also see the changes in the branch linked to this issue.
Thanks for the quick fix. I have just installed this beta build and it seems it doesn't fix the issue. Let me know if there is anything I may help :)
What about this one?
No. In addition, there will be a warning message shown up when first start or on exit.
I am no expert in .NET programming, but I would love to send a list of available method in WMI. By comparing list and code I can see LLT currently implement SetGSyncStatus and SetIGPUModeStatus as a general method in AbstractLenovoGamezoneWmiFeature. My quick guess is that maybe NotifyDGPUStatus also contributes to this process and may need implemented also. This function seems like a callback function that should be called to notify system dGPU changes after ACPI disabled/enabled dGPU.
Hope this may help a little.
Active
CompareTo
Container
ConvertFromDateTime
ConvertToDateTime
Disposed
Equals
GetBIOSOCMode
GetCpuFrequency
GetCPUTemp
GetDGPUHWId
GetFan1Speed
GetFan2Speed
GetFanCoolingStatus
GetFanCount
GetFanMaxSpeed
GetGpuGpsState
GetGPUOCPow
GetGPUOCType
GetGPUPow
GetGPUTemp
GetGSyncStatus
GetHardwareInfoSupportVersion
GetHashCode
GetIGPUModeStatus
GetIntelligentSubMode
GetIRTemp
GetKeyboardfeaturelist
GetKeyboardLight
GetLearningProfileCount
GetLifetimeService
GetMacrokeyCount
GetMacrokeyScancode
GetMemoryOCInfo
GetODStatus
GetPowerChargeMode
GetProductInfo
GetPropertyQualifierValue
GetPropertyValue
GetQualifierValue
GetSmartFanMode
GetSmartFanSetting
GetText
GetThermalMode
GetThermalTableID
GetTPStatus
GetTriggerTemperatureValue
GetType
GetVersion
GetWaterCoolingStatus
GetWinKeyStatus
InitializeLifetimeService
InstanceName
IsACFitForOC
IsBIOSSupportOC
IsChangedYLog
IsRestoreOCValue
IsSupportCpuOC
IsSupportDisableTP
IsSupportDisableWinKey
IsSupportFanCooling
IsSupportGpuOC
IsSupportGSync
IsSupportIGPUMode
IsSupportLightingFeature
IsSupportOD
IsSupportSmartFan
IsSupportWaterCooling
Item
NotifyDGPUStatus
Properties
PSComputerName
Qualifiers
SetBIOSOC
SetDDSControlOwner
SetFanCooling
SetGpuGpsState
SetGSyncStatus
SetIGPUModeStatus
SetIntelligentSubMode
SetKeyboardLight
SetLightControlOwner
SetODStatus
SetPropertyQualifierValue
SetPropertyValue
SetQualifierValue
SetSmartFanMode
SetThermalTableID
SetTPStatus
SetWaterCoolingStatus
SetWinKeyStatus
Site
SystemProperties
I think my previous guess is correct. Adding NotifyDGPUStatus will guarantee GPU switch.
function Toggle-iGPUState {
$LenovoWMIPath=(Get-WmiObject -Namespace "root\WMI" -Query "SELECT * FROM LENOVO_GAMEZONE_DATA").__PATH
$LenovoWMI=[WMI]$LenovoWMIPath
$CurrentState=$LenovoWMI.GetIGPUModeStatus().Data
$TargetState=[int]($CurrentState -xor 1)
$LenovoWMI.SetIGPUModeStatus($TargetState)
while ($LenovoWMI.GetIGPUModeStatus().Data -ne $TargetState)
{
Start-Sleep -Milliseconds 200
}
$LenovoWMI.NotifyDGPUStatus($TargetState)
}
Hi, I have done a quick fix for this issue, but didn't check for cross-BIOS compatibility. Just consider this pull request as a success sample for model 16IAX7. Great thanks for bringing this useful tool!
OK so this build was tested and other guys claim that it works: LenovoLegionToolkitSetup.zip
Could you please verify?
Hi, I have tested it and it works. Seems to be a little slow than my build but that may be due to ACPI. I think it's all good.
I was trying with listening for WM_DEVICECHANGE message, because that's what Vantage seems to be doing (instead I force device refresh manually) but the msg didn't arrive for some reason, idk why.
I was trying with listening for WM_DEVICECHANGE message, because that's what Vantage seems to be doing (instead I force device refresh manually) but the msg didn't arrive for some reason, idk why.
I guess there might still be some dirty IPC communicate between Vantage and services, but I am happy with the current one works (Just not sure if it also works on Gen 6 Legions).
Just out of curiosity, could you try this one too: LenovoLegionToolkitSetup_prolly_broken.zip
I would appreciate if you send a log too.
Sure I can do it. May I ask what change is introduced to this build?
Sure, here is the patch file: patch.diff.txt
And also added this:
internal class SystemEventInterceptor : NativeWindow
{
private static int WM_DEVICECHANGE = 0x0219;
public SystemEventInterceptor(IntPtr handle)
{
AssignHandle(handle);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_DEVICECHANGE)
{
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"WM_DEVICECHANGE");
NotifyDGPU();
}
base.WndProc(ref m);
}
private async void NotifyDGPU()
{
var feat = IoCContainer.Resolve<IGPUModeFeature>();
var state = await feat.GetStateAsync();
await feat.NotifyDGPUStatusAsync(state);
}
}
Stil trying to figure out the message thing
Or actually, nevermind that won't work :/
That seems working. But will have an error shown below. Here are log and error. error_2022_10_01_21_30_37.txt log_2022_10_01_21_30_25.txt
LenovoLegionToolkitSetup_test70.zip
And this one?
This one is good. It's as fast as my original build (or maybe even better).
And the "eject dGPU" appears/disappears correctly?
Yes.
Could you grab a log, just to verify?
log_2022_10_01_21_40_33.txt Sure. Logs looks all good.
Let me check power consumption to verify.
All good.
That is sooo confusing, because now only pnputil
works.... The notifydgpustate doesn't work for some reason:
internal class SystemEventInterceptor : NativeWindow
{
private static int WM_DEVICECHANGE = 0x0219;
private static int DBT_DEVTYP_HANDLE = 5;
private static Guid GUID_DISPLAY_DEVICE_ARRIVAL = new("1CA05180-A699-450A-9A0C-DE4FBE3DDD89");
public SystemEventInterceptor(IntPtr handle)
{
AssignHandle(handle);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_DEVICECHANGE)
{
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"WM_DEVICECHANGE");
if (m.LParam != IntPtr.Zero)
{
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"m.LParam != IntPtr.Zero");
var devBroadcastHdr = Marshal.PtrToStructure<DevBroadcastHdr>(m.LParam);
if (devBroadcastHdr.DeviceType == DBT_DEVTYP_HANDLE)
{
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"devBroadcastHdr.DeviceType == DBT_DEVTYP_HANDLE");
var devBroadcastDeviceInterface = Marshal.PtrToStructure<DevBroadcastDeviceInterface>(m.LParam);
if (devBroadcastDeviceInterface.ClassGuid == GUID_DISPLAY_DEVICE_ARRIVAL)
{
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"devBroadcastDeviceInterface.ClassGuid == GUID_DISPLAY_DEVICE_ARRIVAL");
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"NotifyDGPU");
NotifyDGPU();
}
}
}
}
base.WndProc(ref m);
}
private async void NotifyDGPU()
{
var feat = IoCContainer.Resolve<IGPUModeFeature>();
if (!await feat.IsSupportedAsync())
return;
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"Notifying...");
var state = await feat.GetStateAsync();
await feat.NotifyDGPUStatusAsync(state);
}
}
If you look at the log, it seems like m.LParam
is always zero pointer.
I pushed this code to bug/more-investigation-hybrid
you can take a look if you want.
I have no idea here... I will look into the code in branch you mentioned later.
One maybe unhelpful comment here: For LLT without any modifications we mentioned here, if LLT doesn't change the state successfully, then I go to Vantage to change the state. Windows will go blue screen with error code VIDEO_DXGKRNL_FATAL_ERROR.
OK, one more :D This one should handle correct messages.
No luck for this one. It's not working. (I guess pnputil may work sometimes but it is not guaranteed here) log_2022_10_01_22_30_09.txt
Just curious, does any other user experienced the same issue? It seems I am the only one that can't switch graphics card successfully, if so I guess it's due to my environment and this issue is a false alert. I can continue use my patched LLT and that's totally fine.
No you are not, the fix you made is tested on couple of other machines and it works better. The problem is we can't figure out the proper order of operations with SetIGPUModeState
and NotifyDGPUState
. There are still some problems with waking/sleeping DGPU when connecting/disconnecting screens in iGPU-only mode.
At this point I think the only thing left that we can do is either completely reverse engineer GraphicsCardSettings.dll
from Vantage/LegionZone or set up API monitor and monitor calls that Legion Zone is making (it's easier to trace Legion Zone than to trace Vantage) to figure out what to send where/when. There is still the missing piece of calling NotifyDGPUState
when WM_DEVICECHANGE
message is delivered.
If you would like to join the dicussion, join the Legion Series discord and ping me @bc
. I can loop you in all the discussions/tracing we do there.
The implementation still seems to bug out after some time, so there is def something wrong or the implementation of this is just "broken-ish". This is why I think that we need to trace the WMI calls to get an idea what is sent when.
Just curious, does any other user experienced the same issue? It seems I am the only one that can't switch graphics card successfully, if so I guess it's due to my environment and this issue is a false alert. I can continue use my patched LLT and that's totally fine.
I had the same problem as you, but I saw that there was already the same issue, so I didn't create a new one.
Then it still worth digging deeper. Let me first do a WMI logging and then take a look into Legion Zone. Vantage doesn't seems very easy as most of its functions are done in a DLL and looks like all functions are wrapped in a single factory call.
To clarify: You have set the mode to iGPU-only and the GPU is ejected from the system. Then you change back to Hybrid and dGPU doesn't get reconnected?
I'm having the same issue, 16ARH7H JUCN Bios
Done. Tested on 16IAX7, K1CN31WW bios.
Done. Tested on 16IAX7, K1CN31WW bios.
Can confirm the issue is fixed. Thank you for the all the work!
Done. Tested on 16IAX7, K1CN31WW bios.
Can confirm the issue is fixed. Thank you for the all the work!
How can I test if it's fixed?
How can I test if it's fixed?
You may compile from source before Bartosz releases a new version.
However when turn Hybrid -> Hybrid iGPU, the dGPU still enable(sleep) not disable completely
However when turn Hybrid -> Hybrid iGPU, the dGPU still enable(sleep) not disable completely
dGPU must not be in use for the switch to be done properly. Lenovo designed in a way, where sometimes it just doesn't work. Nothing I can do here.
Version
2.6.0
OS
Windows 11 22H2
Device
Legion 7i 16IAX7
BIOS version
K1CN31WW
What's wrong?
When system has only iGPU enabled, choosing Hybrid Mode will not reenable dGPU. When system has both iGPU and dGPU enabled, choosing Hybrid-iGPU only Mode will not disable dGPU.
How to reproduce the bug?
What is the behavior that you expected?
iGPU or dGPU would enable or disable according to GPU mode.
Logs
log_2022_09_28_18_16_09.txt
Do you have Lenovo software installed?
Did you disable any Lenovo software using Lenovo Legion Toolkit?
Additional information
This issue only happens when Vantage is disabled. If Vantage service is enabled, GPU states can change accordingly. May I ask if this is expected behavior?