diff --git a/src/nvapi_gpu.cpp b/src/nvapi_gpu.cpp index 5e5d79e4..bb16202d 100644 --- a/src/nvapi_gpu.cpp +++ b/src/nvapi_gpu.cpp @@ -486,6 +486,45 @@ extern "C" { return Ok(n); } + NvAPI_Status __cdecl NvAPI_GPU_GetGPUInfo(NvPhysicalGpuHandle hPhysicalGpu, NV_GPU_INFO* pGpuInfo) { + constexpr auto n = __func__; + + if (nvapiAdapterRegistry == nullptr) + return ApiNotInitialized(n); + + if (pGpuInfo == nullptr) + return InvalidArgument(n); + + if (pGpuInfo->version != NV_GPU_INFO_VER1 && pGpuInfo->version != NV_GPU_INFO_VER2) + return IncompatibleStructVersion(n); + + auto adapter = reinterpret_cast(hPhysicalGpu); + if (!nvapiAdapterRegistry->IsAdapter(adapter)) + return ExpectedPhysicalGpuHandle(n); + + auto architectureId = adapter->GetArchitectureId(); + + switch (pGpuInfo->version) { + case NV_GPU_INFO_VER1: { + auto pGpuInfoV1 = reinterpret_cast(pGpuInfo); + *pGpuInfoV1 = {}; + break; + } + case NV_GPU_INFO_VER2: + *pGpuInfo = {}; + if (architectureId >= NV_GPU_ARCHITECTURE_TU100) { + // Values are taken from RTX4080 + pGpuInfo->rayTracingCores = 76; + pGpuInfo->tensorCores = 304; + } + break; + default: + return IncompatibleStructVersion(n); + } + + return Ok(n); + } + NvAPI_Status __cdecl NvAPI_GPU_GetVbiosVersionString(NvPhysicalGpuHandle hPhysicalGpu, NvAPI_ShortString szBiosRevision) { constexpr auto n = __func__; diff --git a/src/nvapi_interface.cpp b/src/nvapi_interface.cpp index 90e410fb..3f09aad0 100644 --- a/src/nvapi_interface.cpp +++ b/src/nvapi_interface.cpp @@ -95,6 +95,7 @@ extern "C" { INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_GPU_GetLogicalGpuInfo) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_GPU_GetArchInfo) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_GPU_CudaEnumComputeCapableGpus) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_GPU_GetGPUInfo) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_GPU_GetDynamicPstatesInfoEx) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_GPU_GetThermalSettings) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_GPU_GetVbiosVersionString) diff --git a/tests/nvapi_sysinfo.cpp b/tests/nvapi_sysinfo.cpp index bad9b014..2b136d7e 100644 --- a/tests/nvapi_sysinfo.cpp +++ b/tests/nvapi_sysinfo.cpp @@ -677,6 +677,82 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { } } + SECTION("GetGPUInfo returns OK") { + struct Data { + uint32_t deviceId; + std::string extensionName; + uint32_t expectedRayTracingCores; + uint32_t expectedTensorCores; + }; + auto args = GENERATE( + Data{0x2600, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, 76, 304}, + Data{0x2000, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, 76, 304}, + Data{0x2000, VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME, 76, 304}, + Data{0x2000, VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME, 0, 0}, + Data{0x2000, VK_NV_CLIP_SPACE_W_SCALING_EXTENSION_NAME, 0, 0}, + Data{0x2000, VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME, 0, 0}, + Data{0x2000, "ext", 0, 0}); + + ALLOW_CALL(*vulkan, GetDeviceExtensions(_, _)) // NOLINT(bugprone-use-after-move) + .RETURN(std::set{ + VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, + args.extensionName}); + ALLOW_CALL(*vulkan, GetPhysicalDeviceProperties2(_, _, _)) + .SIDE_EFFECT( + ConfigureGetPhysicalDeviceProperties2(_3, + [&args](auto props, auto idProps, auto pciBusInfoProps, auto driverProps, auto fragmentShadingRateProps) { + props->deviceID = args.deviceId; + driverProps->driverID = VK_DRIVER_ID_NVIDIA_PROPRIETARY; + if (args.extensionName == VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME) + fragmentShadingRateProps->primitiveFragmentShadingRateWithMultipleViewports = VK_TRUE; + })); + + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); + REQUIRE(NvAPI_Initialize() == NVAPI_OK); + + NvPhysicalGpuHandle handle; + REQUIRE(NvAPI_SYS_GetPhysicalGpuFromDisplayId(primaryDisplayId, &handle) == NVAPI_OK); + + SECTION("GetGPUInfo (V1) returns OK") { + NV_GPU_INFO_V1 gpuInfo; + gpuInfo.version = NV_GPU_INFO_VER1; + REQUIRE(NvAPI_GPU_GetGPUInfo(handle, reinterpret_cast(&gpuInfo)) == NVAPI_OK); + } + + SECTION("GetGPUInfo (V2) returns OK") { + NV_GPU_INFO_V2 gpuInfo; + gpuInfo.version = NV_GPU_INFO_VER2; + REQUIRE(NvAPI_GPU_GetGPUInfo(handle, &gpuInfo) == NVAPI_OK); + REQUIRE(gpuInfo.rayTracingCores == args.expectedRayTracingCores); + REQUIRE(gpuInfo.tensorCores == args.expectedTensorCores); + } + } + + SECTION("GetGPUInfo with unknown struct version returns incompatible-struct-version") { + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); + REQUIRE(NvAPI_Initialize() == NVAPI_OK); + + NvPhysicalGpuHandle handle; + REQUIRE(NvAPI_SYS_GetPhysicalGpuFromDisplayId(primaryDisplayId, &handle) == NVAPI_OK); + + NV_GPU_INFO gpuInfo; + gpuInfo.version = NV_GPU_INFO_VER2 + 1; + REQUIRE(NvAPI_GPU_GetGPUInfo(handle, &gpuInfo) == NVAPI_INCOMPATIBLE_STRUCT_VERSION); + } + + SECTION("GetGPUInfo with current struct version returns not incompatible-struct-version") { + // This test should fail when a header update provides a newer not yet implemented struct version + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); + REQUIRE(NvAPI_Initialize() == NVAPI_OK); + + NvPhysicalGpuHandle handle; + REQUIRE(NvAPI_SYS_GetPhysicalGpuFromDisplayId(primaryDisplayId, &handle) == NVAPI_OK); + + NV_GPU_INFO gpuInfo; + gpuInfo.version = NV_GPU_INFO_VER; + REQUIRE(NvAPI_GPU_GetGPUInfo(handle, &gpuInfo) != NVAPI_INCOMPATIBLE_STRUCT_VERSION); + } + SECTION("GetPstates20 returns no-implementation") { SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK);