#define WIN32_LEAN_AND_MEAN #include #include #include #include "nvsdk_ngx_vk.h" #include #include #include #include #include template Target copy_function_pointer(Source source) { static_assert(sizeof(Target) == sizeof(Source), "function pointer sizes differ"); Target target{}; std::memcpy(&target, &source, sizeof(target)); return target; } template Function load_export(HMODULE module, const char* name) { FARPROC raw = GetProcAddress(module, name); return raw ? copy_function_pointer(raw) : nullptr; } static const char* ngx_result_name(NVSDK_NGX_Result result) { switch (result) { case NVSDK_NGX_Result_Success: return "Success"; case NVSDK_NGX_Result_FAIL_FeatureNotSupported: return "FeatureNotSupported"; case NVSDK_NGX_Result_FAIL_PlatformError: return "PlatformError"; case NVSDK_NGX_Result_FAIL_InvalidParameter: return "InvalidParameter"; case NVSDK_NGX_Result_FAIL_NotInitialized: return "NotInitialized"; case NVSDK_NGX_Result_FAIL_UnableToInitializeFeature: return "UnableToInitializeFeature"; case NVSDK_NGX_Result_FAIL_OutOfDate: return "OutOfDate"; default: return "Other"; } } static void print_ngx_result(const char* stage, NVSDK_NGX_Result result) { std::printf("%s: result=0x%08x (%s)\n", stage, static_cast(result), ngx_result_name(result)); } static std::wstring utf8_to_wide(const char* input) { const int required = MultiByteToWideChar(CP_UTF8, 0, input, -1, nullptr, 0); if (required <= 1) return {}; std::wstring output(static_cast(required), L'\0'); MultiByteToWideChar(CP_UTF8, 0, input, -1, output.data(), required); output.resize(static_cast(required - 1)); return output; } int main(int argc, char** argv) { if (argc != 4) { std::fprintf(stderr, "usage: ngx_vulkan_probe.exe PATH_TO_NGX_CORE_DLL PATH_TO_NVNGX_DLSS_DLL PATH_TO_WRITABLE_APPDATA\n"); return 2; } SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); const char* ngxCorePath = argv[1]; const char* featureDllPath = argv[2]; std::wstring appData = utf8_to_wide(argv[3]); std::wstring dllSearchDirectory = utf8_to_wide(featureDllPath); if (appData.empty() || dllSearchDirectory.empty()) { std::fprintf(stderr, "Invalid UTF-8 feature-DLL path or appdata path argument\n"); return 2; } const size_t separator = dllSearchDirectory.find_last_of(L"\\/"); if (separator == std::wstring::npos) dllSearchDirectory = L"."; else dllSearchDirectory.resize(separator); const wchar_t* featurePaths[] = {dllSearchDirectory.c_str()}; NVSDK_NGX_FeatureCommonInfo featureCommonInfo{}; featureCommonInfo.PathListInfo.Path = featurePaths; featureCommonInfo.PathListInfo.Length = 1; HMODULE ngxCore = LoadLibraryA(ngxCorePath); if (ngxCore == nullptr) { std::fprintf(stderr, "LoadLibraryA(%s) failed: GetLastError=%lu\n", ngxCorePath, GetLastError()); return 1; } HMODULE ngxFeature = LoadLibraryA(featureDllPath); if (ngxFeature == nullptr) { std::fprintf(stderr, "LoadLibraryA(%s) failed: GetLastError=%lu\n", featureDllPath, GetLastError()); FreeLibrary(ngxCore); return 1; } std::printf("Loaded NGX core: %s\n", ngxCorePath); std::printf("DLSS SR feature search directory: %ls\n", dllSearchDirectory.c_str()); auto getRequirements = load_export( ngxFeature, "NVSDK_NGX_VULKAN_GetFeatureRequirements"); auto getInstanceExtensions = load_export( ngxFeature, "NVSDK_NGX_VULKAN_GetFeatureInstanceExtensionRequirements"); auto getDeviceExtensions = load_export( ngxFeature, "NVSDK_NGX_VULKAN_GetFeatureDeviceExtensionRequirements"); if (getRequirements == nullptr || getInstanceExtensions == nullptr || getDeviceExtensions == nullptr) { std::fprintf(stderr, "The DLL does not expose the required Vulkan discovery exports\n"); FreeLibrary(ngxFeature); FreeLibrary(ngxCore); return 1; } NVSDK_NGX_FeatureDiscoveryInfo discovery{}; discovery.SDKVersion = static_cast(0x13); // Version resource in tested DLLs reports API 0x13. discovery.FeatureID = NVSDK_NGX_Feature_SuperSampling; discovery.Identifier.IdentifierType = NVSDK_NGX_Application_Identifier_Type_Application_Id; discovery.Identifier.v.ApplicationId = 241534723ULL; // NGXAppId value in this DLL's VERSIONINFO resource. discovery.ApplicationDataPath = appData.c_str(); discovery.FeatureInfo = &featureCommonInfo; std::printf("Feature discovery: SDK=0x13 appId=%llu searchPath=%ls appData=%ls\n", discovery.Identifier.v.ApplicationId, dllSearchDirectory.c_str(), appData.c_str()); uint32_t instanceExtensionCount = 0; VkExtensionProperties* instanceExtensions = nullptr; NVSDK_NGX_Result result = getInstanceExtensions(&discovery, &instanceExtensionCount, &instanceExtensions); print_ngx_result("VULKAN_GetFeatureInstanceExtensionRequirements", result); std::printf("required instance extensions: %u\n", instanceExtensionCount); for (uint32_t i = 0; i < instanceExtensionCount && instanceExtensions != nullptr; ++i) std::printf(" %s (%u)\n", instanceExtensions[i].extensionName, instanceExtensions[i].specVersion); HMODULE vulkan = LoadLibraryA("vulkan-1.dll"); if (vulkan == nullptr) { std::fprintf(stderr, "LoadLibraryA(vulkan-1.dll) failed: GetLastError=%lu\n", GetLastError()); FreeLibrary(ngxCore); FreeLibrary(ngxFeature); return 1; } auto getInstanceProcAddr = load_export(vulkan, "vkGetInstanceProcAddr"); if (getInstanceProcAddr == nullptr) { std::fprintf(stderr, "vulkan-1.dll has no vkGetInstanceProcAddr export\n"); FreeLibrary(vulkan); FreeLibrary(ngxFeature); FreeLibrary(ngxCore); return 1; } auto createInstance = copy_function_pointer( getInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance")); if (createInstance == nullptr) { std::fprintf(stderr, "vkCreateInstance is unavailable\n"); FreeLibrary(vulkan); FreeLibrary(ngxFeature); FreeLibrary(ngxCore); return 1; } VkApplicationInfo application{}; application.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; application.pApplicationName = "d4r NGX Vulkan discovery probe"; application.applicationVersion = 1; application.pEngineName = "d4r"; application.engineVersion = 1; application.apiVersion = VK_API_VERSION_1_0; VkInstanceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pApplicationInfo = &application; std::vector enabledInstanceExtensions; for (uint32_t i = 0; i < instanceExtensionCount && instanceExtensions != nullptr; ++i) enabledInstanceExtensions.push_back(instanceExtensions[i].extensionName); createInfo.enabledExtensionCount = static_cast(enabledInstanceExtensions.size()); createInfo.ppEnabledExtensionNames = enabledInstanceExtensions.data(); VkInstance instance = VK_NULL_HANDLE; const VkResult vkCreateResult = createInstance(&createInfo, nullptr, &instance); std::printf("vkCreateInstance: result=%d\n", static_cast(vkCreateResult)); if (vkCreateResult != VK_SUCCESS) { FreeLibrary(vulkan); FreeLibrary(ngxFeature); FreeLibrary(ngxCore); return 1; } auto enumeratePhysicalDevices = copy_function_pointer( getInstanceProcAddr(instance, "vkEnumeratePhysicalDevices")); auto enumerateDeviceExtensions = copy_function_pointer( getInstanceProcAddr(instance, "vkEnumerateDeviceExtensionProperties")); auto getPhysicalDeviceProperties = copy_function_pointer( getInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties")); auto destroyInstance = copy_function_pointer( getInstanceProcAddr(instance, "vkDestroyInstance")); auto getQueueFamilyProperties = copy_function_pointer( getInstanceProcAddr(instance, "vkGetPhysicalDeviceQueueFamilyProperties")); auto createDevice = copy_function_pointer(getInstanceProcAddr(instance, "vkCreateDevice")); auto getDeviceProcAddr = copy_function_pointer( getInstanceProcAddr(instance, "vkGetDeviceProcAddr")); if (enumeratePhysicalDevices == nullptr || enumerateDeviceExtensions == nullptr || getPhysicalDeviceProperties == nullptr || destroyInstance == nullptr || getQueueFamilyProperties == nullptr || createDevice == nullptr || getDeviceProcAddr == nullptr) { std::fprintf(stderr, "Required Vulkan instance functions are unavailable\n"); FreeLibrary(vulkan); FreeLibrary(ngxFeature); FreeLibrary(ngxCore); return 1; } uint32_t deviceCount = 0; VkResult enumerateResult = enumeratePhysicalDevices(instance, &deviceCount, nullptr); if (enumerateResult != VK_SUCCESS || deviceCount == 0) { std::fprintf(stderr, "vkEnumeratePhysicalDevices(count) failed: result=%d count=%u\n", static_cast(enumerateResult), deviceCount); destroyInstance(instance, nullptr); FreeLibrary(vulkan); FreeLibrary(ngxFeature); FreeLibrary(ngxCore); return 1; } std::vector devices(deviceCount); enumerateResult = enumeratePhysicalDevices(instance, &deviceCount, devices.data()); if (enumerateResult != VK_SUCCESS) { std::fprintf(stderr, "vkEnumeratePhysicalDevices(list) failed: result=%d\n", static_cast(enumerateResult)); destroyInstance(instance, nullptr); FreeLibrary(vulkan); FreeLibrary(ngxCore); return 1; } VkPhysicalDevice amdDevice = VK_NULL_HANDLE; for (VkPhysicalDevice device : devices) { VkPhysicalDeviceProperties properties{}; getPhysicalDeviceProperties(device, &properties); std::printf("Vulkan device: vendor=0x%04x device=0x%04x type=%d name=%s\n", properties.vendorID, properties.deviceID, static_cast(properties.deviceType), properties.deviceName); if (properties.vendorID == 0x1002 && amdDevice == VK_NULL_HANDLE) amdDevice = device; } if (amdDevice == VK_NULL_HANDLE) { std::fprintf(stderr, "No AMD Vulkan physical device was enumerated\n"); destroyInstance(instance, nullptr); FreeLibrary(vulkan); FreeLibrary(ngxCore); return 1; } NVSDK_NGX_FeatureRequirement requirement{}; result = getRequirements(instance, amdDevice, &discovery, &requirement); print_ngx_result("VULKAN_GetFeatureRequirements(DLSS SR)", result); if (result == NVSDK_NGX_Result_Success) { std::printf("feature support flags: 0x%08x\n", static_cast(requirement.FeatureSupported)); std::printf("minimum hardware architecture: %u\n", requirement.MinHWArchitecture); std::printf("minimum OS version: %s\n", requirement.MinOSVersion); } uint32_t deviceExtensionCount = 0; VkExtensionProperties* deviceExtensions = nullptr; VkExtensionProperties deviceExtensionStorage[32]{}; deviceExtensionCount = 32; deviceExtensions = deviceExtensionStorage; result = getDeviceExtensions(instance, amdDevice, &discovery, &deviceExtensionCount, &deviceExtensions); print_ngx_result("VULKAN_GetFeatureDeviceExtensionRequirements", result); if (result != NVSDK_NGX_Result_Success) deviceExtensionCount = 0; std::printf("required device extensions: %u, properties pointer=%p\n", deviceExtensionCount, static_cast(deviceExtensions)); for (uint32_t i = 0; i < deviceExtensionCount && i < 32 && deviceExtensions != nullptr; ++i) std::printf(" %s (%u)\n", deviceExtensions[i].extensionName, deviceExtensions[i].specVersion); uint32_t availableExtensionCount = 0; VkResult extensionResult = enumerateDeviceExtensions(amdDevice, nullptr, &availableExtensionCount, nullptr); std::vector availableExtensions(availableExtensionCount); if (extensionResult == VK_SUCCESS && availableExtensionCount > 0) extensionResult = enumerateDeviceExtensions(amdDevice, nullptr, &availableExtensionCount, availableExtensions.data()); std::printf("AMD device-extension enumeration: result=%d count=%u\n", static_cast(extensionResult), availableExtensionCount); for (uint32_t i = 0; i < deviceExtensionCount && i < 32 && deviceExtensions != nullptr; ++i) { bool supported = false; for (const auto& extension : availableExtensions) { if (std::strcmp(extension.extensionName, deviceExtensions[i].extensionName) == 0) { supported = true; break; } } std::printf(" requirement availability: %-42s %s\n", deviceExtensions[i].extensionName, supported ? "supported" : "missing"); } std::vector enabledDeviceExtensions; for (uint32_t i = 0; i < deviceExtensionCount && i < 32 && deviceExtensions != nullptr; ++i) { for (const auto& extension : availableExtensions) { if (std::strcmp(extension.extensionName, deviceExtensions[i].extensionName) == 0) { enabledDeviceExtensions.push_back(deviceExtensions[i].extensionName); break; } } } uint32_t queueFamilyCount = 0; getQueueFamilyProperties(amdDevice, &queueFamilyCount, nullptr); std::vector queueFamilies(queueFamilyCount); getQueueFamilyProperties(amdDevice, &queueFamilyCount, queueFamilies.data()); uint32_t queueFamilyIndex = UINT32_MAX; for (uint32_t i = 0; i < queueFamilyCount; ++i) { const VkQueueFlags requiredQueueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; if ((queueFamilies[i].queueFlags & requiredQueueFlags) == requiredQueueFlags && queueFamilies[i].queueCount > 0) { queueFamilyIndex = i; break; } } if (queueFamilyIndex == UINT32_MAX) { std::fprintf(stderr, "No graphics+compute Vulkan queue family was found\n"); destroyInstance(instance, nullptr); FreeLibrary(vulkan); FreeLibrary(ngxFeature); FreeLibrary(ngxCore); return 1; } const float queuePriority = 1.0f; VkDeviceQueueCreateInfo queueInfo{}; queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueInfo.queueFamilyIndex = queueFamilyIndex; queueInfo.queueCount = 1; queueInfo.pQueuePriorities = &queuePriority; VkDeviceCreateInfo deviceInfo{}; deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceInfo.queueCreateInfoCount = 1; deviceInfo.pQueueCreateInfos = &queueInfo; deviceInfo.enabledExtensionCount = static_cast(enabledDeviceExtensions.size()); deviceInfo.ppEnabledExtensionNames = enabledDeviceExtensions.data(); VkDevice device = VK_NULL_HANDLE; const VkResult vkDeviceResult = createDevice(amdDevice, &deviceInfo, nullptr, &device); std::printf("vkCreateDevice (only available NGX-required extensions): result=%d enabled-extension-count=%u\n", static_cast(vkDeviceResult), deviceInfo.enabledExtensionCount); if (vkDeviceResult != VK_SUCCESS) { destroyInstance(instance, nullptr); FreeLibrary(vulkan); FreeLibrary(ngxFeature); FreeLibrary(ngxCore); return 1; } auto destroyDevice = copy_function_pointer(getDeviceProcAddr(device, "vkDestroyDevice")); auto init = load_export(ngxCore, "NVSDK_NGX_VULKAN_Init"); auto shutdown = load_export(ngxCore, "NVSDK_NGX_VULKAN_Shutdown1"); if (init == nullptr || destroyDevice == nullptr) { std::fprintf(stderr, "NGX Vulkan init or Vulkan device cleanup export is unavailable\n"); if (destroyDevice != nullptr) destroyDevice(device, nullptr); destroyInstance(instance, nullptr); FreeLibrary(vulkan); FreeLibrary(ngxFeature); FreeLibrary(ngxCore); return 1; } result = init(discovery.Identifier.v.ApplicationId, discovery.ApplicationDataPath, instance, amdDevice, device, getInstanceProcAddr, getDeviceProcAddr, &featureCommonInfo, discovery.SDKVersion); print_ngx_result("VULKAN_Init(DLSS SR)", result); if (result == NVSDK_NGX_Result_Success && shutdown != nullptr) { NVSDK_NGX_Result shutdownResult = shutdown(device); print_ngx_result("VULKAN_Shutdown1", shutdownResult); } destroyDevice(device, nullptr); destroyInstance(instance, nullptr); FreeLibrary(vulkan); FreeLibrary(ngxFeature); FreeLibrary(ngxCore); return 0; }