Skip to content

Commit

Permalink
nvapi: Report GPUs with NVIDIAs proprietary driver only
Browse files Browse the repository at this point in the history
This matches Windows behavior and may avoid reports about version number mismatch when using other vendors.
Introduce 'DXVK_NVAPI_ALLOW_OTHER_DRIVERS' environment variable to enable the old behavior.
  • Loading branch information
jp7677 committed Dec 5, 2021
1 parent fe232dc commit ca6c35b
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 18 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ While originally being developed for usage with Unreal Engine 4, most notably fo

## Requirements

This implementation is supposed to be used on Linux using Wine or derivatives like Proton. Usage on Windows is discouraged. Please do not replace `nvapi.dll`/`nvapi64.dll` on Windows from NVIDIA's driver package with this version. DXVK-NVAPI uses several DXVK and VKD3D-Proton extension points, thus using DXVK (D3D11 and DXGI) is a requirement. Using Wine's D3D11 or DXGI will fail. Usage of DXVK-NVAPI is not restricted to NVIDIA GPUs, but some entry points offer no functionality when a different GPU vendor is detected.
This implementation is supposed to be used on Linux using Wine or derivatives like Proton. Usage on Windows is discouraged. Please do not replace `nvapi.dll`/`nvapi64.dll` on Windows from NVIDIA's driver package with this version. DXVK-NVAPI uses several DXVK and VKD3D-Proton extension points, thus using DXVK (D3D11 and DXGI) is a requirement. Using Wine's D3D11 or DXGI will fail. Usage of DXVK-NVAPI is not restricted to NVIDIA GPUs, though the default behavior is to report only NVIDIA GPUs. Some entry points offer no functionality when a different GPU vendor is detected.

When available, DXVK-NVAPI uses NVIDIA's NVML management library to query temperature, utilization and others for NVIDIA GPUs. See [wine-nvml](https://github.com/Saancreed/wine-nvml) how to add NVML support to Wine/Proton.

Expand Down Expand Up @@ -62,17 +62,18 @@ Wine does not includes DXVK-NVAPI.
- Ensure that Wine uses DXVK's `dxgi.dll`, e.g. with `WINEDLLOVERRIDES=dxgi=n`.
- Disable the `nvapiHack` in DXVK with `dxgi.nvapiHack = False` set in a DXVK configuration file, see [dxvk.conf](https://github.com/doitsujin/dxvk/blob/master/dxvk.conf#L51).

## Debugging and troubleshooting
## Tweaks, debugging and troubleshooting

See [Quirks-for-usage-with-DXVK-NVAPI](https://github.com/jp7677/dxvk-nvapi/wiki/Quirks-for-usage-with-DXVK-NVAPI) for any known quirks or work arounds.

The following environment variables tweak DXVK-NVAPI's runtime behavior:

- `DXVK_NVAPI_DRIVER_VERSION` lets you override the reported driver version. Valid values are numbers between 100 and 99999. Use e.g. `DXVK_NVAPI_DRIVER_VERSION=47141` to report driver version `471.41`.
- `DXVK_NVAPI_ALLOW_OTHER_DRIVERS` - lets you use DXVK-NVAPI without running an NVIDIA card on NVIDIAs proprietary driver when set, e.g. `DXVK_NVAPI_ALLOW_OTHER_DRIVERS=1`. Useful for using e.g. D3D11 extension on a non-NVIDIA GPU.
- `DXVK_NVAPI_LOG_LEVEL` set to `info` prints log statements. The default behavior omits any logging. Please fill an issue if using log servery `info` creates log spam. There are no other log levels.
- `DXVK_NVAPI_LOG_PATH` enables file logging additionally to console output and sets the path where the log file `dxvk-nvapi.log` should be written to. Log statements are appended to an existing file. Please remove this file once in a while to prevent excessive grow. This requires `DXVK_NVAPI_LOG_LEVEL` set to `info`.

This project provides a test suite. Run the package script with `--enable-tests` (see above) to build `nvapi64-tests.exe`. Running the tests executable without arguments queries the local system and provides system information about visible GPU's:
This project provides a test suite. Run the package script with `--enable-tests` (see above) to build `nvapi64-tests.exe`. Running the tests executable without arguments queries the local system and provides system information about visible GPUs:

```bash
DXVK_LOG_LEVEL=none WINEDEBUG=-all WINEDLLOVERRIDES=dxgi,nvapi64=n wine nvapi64-tests.exe
Expand Down
10 changes: 9 additions & 1 deletion src/sysinfo/nvapi_adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace dxvk {

bool NvapiAdapter::Initialize(Com<IDXGIAdapter1>& dxgiAdapter, std::vector<NvapiOutput*>& outputs) {
constexpr auto driverVersionEnvName = "DXVK_NVAPI_DRIVER_VERSION";
constexpr auto allowOtherDriversEnvName = "DXVK_NVAPI_ALLOW_OTHER_DRIVERS";

// Get the Vulkan handle from the DXGI adapter to get access to Vulkan device properties which has some information we want.
Com<IDXGIVkInteropAdapter> dxgiVkInteropAdapter;
Expand Down Expand Up @@ -68,6 +69,13 @@ namespace dxvk {
m_vulkan.GetPhysicalDeviceMemoryProperties2(vkInstance, vkDevice, &memoryProperties2);
m_memoryProperties = memoryProperties2.memoryProperties;

auto allowOtherDrivers = env::getEnvVariable(allowOtherDriversEnvName);
if (!allowOtherDrivers.empty())
log::write(str::format(allowOtherDrivers, " is set, reporting also GPUs with non-NVIDIA proprietary driver."));

if (GetDriverId() != VK_DRIVER_ID_NVIDIA_PROPRIETARY && allowOtherDrivers.empty())
return false;

if (GetDriverId() == VK_DRIVER_ID_NVIDIA_PROPRIETARY)
// Handle NVIDIA version notation
m_vkDriverVersion = VK_MAKE_VERSION(
Expand Down Expand Up @@ -114,7 +122,7 @@ namespace dxvk {
if (std::string(end).empty() && driverVersionOverride >= 100 && driverVersionOverride <= 99999) {
std::stringstream stream;
stream << (driverVersionOverride / 100) << "." << std::setfill('0') << std::setw(2) << (driverVersionOverride % 100);
log::write(str::format(driverVersionEnvName, " is set to '", driverVersion, "', reporting driver version ", stream.str()));
log::write(str::format(driverVersionEnvName, " is set to '", driverVersion, "', reporting driver version ", stream.str(), "."));
m_driverVersionOverride = driverVersionOverride;
}
else
Expand Down
4 changes: 4 additions & 0 deletions tests/nvapi_d3d12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ TEST_CASE("D3D12 methods succeed", "[.d3d12]") {
Data{VK_DRIVER_ID_AMD_OPEN_SOURCE, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, 0, 0},
Data{VK_DRIVER_ID_NVIDIA_PROPRIETARY, "ext", 0, 0});

::SetEnvironmentVariableA("DXVK_NVAPI_ALLOW_OTHER_DRIVERS", "1");

luid->HighPart = 0x00000002;
luid->LowPart = 0x00000001;

Expand All @@ -185,6 +187,8 @@ TEST_CASE("D3D12 methods succeed", "[.d3d12]") {
REQUIRE(graphicsCaps.majorSMVersion == args.expectedMajorSMVersion);
REQUIRE(graphicsCaps.minorSMVersion == args.expectedMinorSMVersion);
REQUIRE(deviceRefCount == 0);

::SetEnvironmentVariableA("DXVK_NVAPI_ALLOW_OTHER_DRIVERS", "");
}
}

Expand Down
51 changes: 37 additions & 14 deletions tests/nvapi_sysinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,42 @@ TEST_CASE("GetErrorMessage returns OK", "[.sysinfo]") {
REQUIRE(strcmp(desc, "NVAPI_NVIDIA_DEVICE_NOT_FOUND") == 0);
}

TEST_CASE("Initialize returns device-not-found when DXVK reports no adapters", "[.sysinfo]") {
TEST_CASE("Initialize succeed", "[.sysinfo]") {
auto dxgiFactory = std::make_unique<DXGIFactory1Mock>();
auto vulkan = std::make_unique<VulkanMock>();
auto nvml = std::make_unique<NvmlMock>();
DXGIDxvkAdapterMock adapter;
DXGIOutputMock output;

auto e = ConfigureDefaultTestEnvironment(*dxgiFactory, *vulkan, *nvml, adapter, output);

ALLOW_CALL(*dxgiFactory, AddRef())
.RETURN(1);
ALLOW_CALL(*dxgiFactory, Release())
.RETURN(0);
ALLOW_CALL(*dxgiFactory, EnumAdapters1(_, _))
.RETURN(DXGI_ERROR_NOT_FOUND);
SECTION("Initialize returns OK") {
SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml));
REQUIRE(NvAPI_Initialize() == NVAPI_OK);
REQUIRE(NvAPI_Unload() == NVAPI_OK);
}

ALLOW_CALL(*vulkan, IsAvailable())
.RETURN(true);
SECTION("Initialize returns device-not-found when DXVK reports no adapters") {
ALLOW_CALL(*dxgiFactory, EnumAdapters1(_, _)) // NOLINT(bugprone-use-after-move)
.RETURN(DXGI_ERROR_NOT_FOUND);

ALLOW_CALL(*nvml, IsAvailable())
.RETURN(false);
SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml));
REQUIRE(NvAPI_Initialize() == NVAPI_NVIDIA_DEVICE_NOT_FOUND);
REQUIRE(NvAPI_Unload() == NVAPI_API_NOT_INITIALIZED);
}

SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml));
REQUIRE(NvAPI_Initialize() == NVAPI_NVIDIA_DEVICE_NOT_FOUND);
REQUIRE(NvAPI_Unload() == NVAPI_API_NOT_INITIALIZED);
SECTION("Initialize returns device-not-found when adapter with non NVIDIA driver ID has been found") {
ALLOW_CALL(*vulkan, GetPhysicalDeviceProperties2(_, _, _)) // NOLINT(bugprone-use-after-move)
.SIDE_EFFECT(
ConfigureGetPhysicalDeviceProperties2(_3,
[](auto props, auto idProps, auto pciBusInfoProps, auto driverProps, auto fragmentShadingRateProps) {
driverProps->driverID = VK_DRIVER_ID_MESA_RADV;
}));

SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml));
REQUIRE(NvAPI_Initialize() == NVAPI_NVIDIA_DEVICE_NOT_FOUND);
REQUIRE(NvAPI_Unload() == NVAPI_API_NOT_INITIALIZED);
}
}

TEST_CASE("Topology methods succeed", "[.sysinfo]") {
Expand Down Expand Up @@ -251,6 +266,8 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") {
Data{VK_DRIVER_ID_NVIDIA_PROPRIETARY, 470, 101, 1, 47099},
Data{VK_DRIVER_ID_AMD_OPEN_SOURCE, 21, 2, 3, 2102});

::SetEnvironmentVariableA("DXVK_NVAPI_ALLOW_OTHER_DRIVERS", "1");

ALLOW_CALL(*vulkan, GetPhysicalDeviceProperties2(_, _, _)) // NOLINT(bugprone-use-after-move)
.SIDE_EFFECT(
ConfigureGetPhysicalDeviceProperties2(_3,
Expand All @@ -275,6 +292,8 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") {
REQUIRE(version.drvVersion == args.expectedVersion);
REQUIRE(strcmp(version.szAdapterString, "GPU0") == 0);
REQUIRE(std::string(version.szBuildBranchString).length() > 0);

::SetEnvironmentVariableA("DXVK_NVAPI_ALLOW_OTHER_DRIVERS", "0");
}

SECTION("GetDisplayDriverVersion with version override returns OK") {
Expand Down Expand Up @@ -480,6 +499,8 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") {
}

SECTION("GetArchInfo returns device-not-found when no NVIDIA device is present") {
::SetEnvironmentVariableA("DXVK_NVAPI_ALLOW_OTHER_DRIVERS", "1");

ALLOW_CALL(*vulkan, GetPhysicalDeviceProperties2(_, _, _)) // NOLINT(bugprone-use-after-move)
.SIDE_EFFECT(
ConfigureGetPhysicalDeviceProperties2(_3,
Expand All @@ -496,6 +517,8 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") {
NV_GPU_ARCH_INFO archInfo;
archInfo.version = NV_GPU_ARCH_INFO_VER_2;
REQUIRE(NvAPI_GPU_GetArchInfo(handle, &archInfo) == NVAPI_NVIDIA_DEVICE_NOT_FOUND);

::SetEnvironmentVariableA("DXVK_NVAPI_ALLOW_OTHER_DRIVERS", "0");
}

SECTION("NVML depending methods succeed when NVML is available") {
Expand Down

0 comments on commit ca6c35b

Please sign in to comment.