-
Notifications
You must be signed in to change notification settings - Fork 225
Check Hardware Accelerated GPU Scheduling (HAGS) status on Windows #1279
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a6124ef
3dbb0fc
3b19ff6
d79fb29
2b08492
235c122
4fa00ee
25060c6
7e340af
2eebbca
3c2a1c7
066d928
a80f553
35cccf3
58e1578
8d11440
dfd4d13
a415810
7c2aa1a
85a1550
63ee3f3
43d7c8f
d6d7b57
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| // SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| // Note, this may or may not exist, but is NOT the ground truth: | ||
| // reg query "HKLM\SYSTEM\CurrentControlSet\Control\GraphicsDrivers" /v HwSchMode | ||
| // The HwSchMode registry value is only a user override (force on/off). | ||
| // If absent, Windows uses the driver's WDDM caps defaults. | ||
| // Actual HAGS state comes from D3DKMT_WDDM_2_7_CAPS, not the registry. | ||
|
|
||
| // Possibly useful for experimentation: | ||
| // reg delete "HKLM\SYSTEM\CurrentControlSet\Control\GraphicsDrivers" /v HwSchMode /f | ||
|
|
||
| #ifdef _MSC_VER | ||
| #include <windows.h> | ||
| #include <d3dkmthk.h> | ||
| #include <d3dkmdt.h> | ||
| #endif | ||
|
|
||
| int hags_status(void) | ||
| { | ||
| #ifdef _MSC_VER | ||
| DISPLAY_DEVICEW dd; | ||
| HDC hdc; | ||
| int i; | ||
| BOOL foundPrimary = FALSE; | ||
| NTSTATUS status; | ||
|
|
||
| D3DKMT_OPENADAPTERFROMHDC openData; | ||
| D3DKMT_QUERYADAPTERINFO query; | ||
| D3DKMT_WDDM_2_7_CAPS caps; | ||
| D3DKMT_CLOSEADAPTER closeData; | ||
|
|
||
| // Find the primary display device | ||
| ZeroMemory(&dd, sizeof(dd)); | ||
| dd.cb = sizeof(dd); | ||
|
|
||
| for (i = 0; EnumDisplayDevicesW(NULL, i, &dd, 0); ++i) { | ||
| if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) { | ||
| foundPrimary = TRUE; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about cases where a) CUDA compute is being done on the non-primary GPU (i.e. you've got a separate GPU with no display that you use for compute), or b) laptops with the integrated GPUs as default (that are non-NVIDIA), where the discrete GPU is the NVIDIA one.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That granularity is indeed missing in this PR, because I didn't want to add the complexity to query based on the actual device active when the How likely are those cases? Could it cause unwanted side-effects if HAGS is enabled? |
||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (!foundPrimary) | ||
| return 0; | ||
|
|
||
| hdc = CreateDCW(NULL, dd.DeviceName, NULL, NULL); | ||
| if (!hdc) | ||
| return 0; | ||
|
|
||
| ZeroMemory(&openData, sizeof(openData)); | ||
| openData.hDc = hdc; | ||
| status = D3DKMTOpenAdapterFromHdc(&openData); | ||
|
|
||
| DeleteDC(hdc); | ||
|
|
||
| if (status != 0) | ||
| return 0; | ||
|
|
||
| ZeroMemory(&caps, sizeof(caps)); | ||
| ZeroMemory(&query, sizeof(query)); | ||
|
|
||
| query.hAdapter = openData.hAdapter; | ||
| query.Type = KMTQAITYPE_WDDM_2_7_CAPS; | ||
| query.pPrivateDriverData = ∩︀ | ||
| query.PrivateDriverDataSize = sizeof(caps); | ||
|
|
||
| status = D3DKMTQueryAdapterInfo(&query); | ||
|
|
||
| ZeroMemory(&closeData, sizeof(closeData)); | ||
| closeData.hAdapter = openData.hAdapter; | ||
| D3DKMTCloseAdapter(&closeData); | ||
|
|
||
| if (status != 0) | ||
| return 0; | ||
|
|
||
| if (!caps.HwSchSupported || !caps.HwSchEnabled) | ||
| return 1; | ||
|
|
||
| return 2; | ||
| #else | ||
| return -1; | ||
| #endif | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| // SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| #pragma once | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| /* | ||
| * hags_status | ||
| * | ||
| * Return codes: | ||
| * -1 : Not available on this platform (not compiled with MSVC on Windows) | ||
| * 0 : Failure obtaining HwSchSupported/HwSchEnabled | ||
| * 1 : HwSchSupported == 0 or HwSchEnabled == 0 (HAGS not fully enabled) | ||
| * 2 : HwSchSupported == 1 and HwSchEnabled == 1 (HAGS fully enabled) | ||
| */ | ||
| int hags_status(void); | ||
|
|
||
| #ifdef __cplusplus | ||
| } /* extern "C" */ | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| // SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| // | ||
| // Query NVML for the Windows WDDM driver model, looping over all GPUs. | ||
| // | ||
| // On non-Windows platforms this always returns -1 and performs no NVML calls. | ||
| // | ||
| // Example compilation command (Windows/MSVC): | ||
| // cl /nologo /c wddm_driver_model_is_in_use.c /I"C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v13.0\\include" | ||
| // Needed for linking: | ||
| // /link /LIBPATH:"C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v13.0\\lib\\x64" nvml.lib | ||
| // | ||
| #include "wddm_driver_model_is_in_use.h" | ||
|
|
||
| #ifdef _MSC_VER | ||
|
|
||
| #include "nvml.h" // from NVIDIA GPU Computing Toolkit | ||
|
|
||
| static int wddm_driver_model_is_in_use_impl(void) | ||
| { | ||
| unsigned deviceCount = 0; | ||
| nvmlReturn_t result = nvmlDeviceGetCount_v2(&deviceCount); | ||
| if (result != NVML_SUCCESS) { | ||
| return -2; | ||
| } | ||
| for (unsigned i_dev = 0; i_dev < deviceCount; ++i_dev) { | ||
| nvmlDevice_t device; | ||
| result = nvmlDeviceGetHandleByIndex_v2(i_dev, &device); | ||
| if (result == NVML_SUCCESS) { | ||
| nvmlDriverModel_t currentModel = 0; | ||
| nvmlDriverModel_t pendingModel = 0; | ||
| result = nvmlDeviceGetDriverModel(device, ¤tModel, &pendingModel); | ||
| if (result == NVML_SUCCESS) { | ||
| if (currentModel == NVML_DRIVER_WDDM || pendingModel == NVML_DRIVER_WDDM) { | ||
| return 1; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| int wddm_driver_model_is_in_use(void) | ||
| { | ||
| nvmlReturn_t result = nvmlInit_v2(); | ||
| if (result != NVML_SUCCESS) { | ||
| return -1; | ||
| } | ||
| int return_code = wddm_driver_model_is_in_use_impl(); | ||
| nvmlShutdown(); | ||
| return return_code; | ||
| } | ||
|
|
||
| #else // !_MSC_VER | ||
|
|
||
| int wddm_driver_model_is_in_use(void) | ||
| { | ||
| // WDDM is a Windows-only concept; on non-Windows platforms we report -1 | ||
| // to indicate that the driver model could not be determined. | ||
| return -1; | ||
| } | ||
|
|
||
| #endif // _MSC_VER |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| // SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| #pragma once | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| /* | ||
| * wddm_driver_model_is_in_use | ||
| * | ||
| * Return codes: | ||
| * -2 : Failed to get device count from NVML | ||
| * -1 : Not available on this platform (not compiled with MSVC on Windows) or NVML initialization failed | ||
| * 0 : No WDDM driver model found (all devices use TCC or other driver models) | ||
| * 1 : WDDM driver model is in use (at least one device uses WDDM) | ||
| */ | ||
| int wddm_driver_model_is_in_use(void); | ||
|
|
||
| #ifdef __cplusplus | ||
| } /* extern "C" */ | ||
| #endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will throw if hardware accelerated scheduling isn't enabled, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Responding to this question in isolation:
Yes, it'll show this message: