mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-31 10:02:49 +01:00
4038ed2656
Thanks Github Copilot
350 lines
15 KiB
C
350 lines
15 KiB
C
#include "fastfetch.h"
|
|
#include "common/debug.h"
|
|
#include "detection/gpu/gpu.h"
|
|
#include "detection/vulkan/vulkan.h"
|
|
|
|
#ifdef FF_HAVE_VULKAN
|
|
#include "common/library.h"
|
|
#include "common/io.h"
|
|
#include "common/parsing.h"
|
|
#include "common/stringUtils.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <vulkan/vulkan.h>
|
|
|
|
static inline void applyVulkanVersion(uint32_t vulkanVersion, FFVersion* ffVersion)
|
|
{
|
|
ffVersion->major = VK_VERSION_MAJOR(vulkanVersion);
|
|
ffVersion->minor = VK_VERSION_MINOR(vulkanVersion);
|
|
ffVersion->patch = VK_VERSION_PATCH(vulkanVersion);
|
|
}
|
|
|
|
static void applyDriverName(VkPhysicalDeviceDriverPropertiesKHR* properties, FFstrbuf* result)
|
|
{
|
|
if(!ffStrSet(properties->driverName))
|
|
return;
|
|
|
|
ffStrbufAppendS(result, properties->driverName);
|
|
|
|
/*
|
|
* Some drivers (android for example) expose a multiline string as driver info.
|
|
* It contains too much info anyways, so we just don't append it.
|
|
*/
|
|
if(!ffStrSet(properties->driverInfo) || strchr(properties->driverInfo, '\n') != NULL)
|
|
return;
|
|
|
|
ffStrbufAppendS(result, " [");
|
|
ffStrbufAppendS(result, properties->driverInfo);
|
|
ffStrbufAppendC(result, ']');
|
|
}
|
|
|
|
static const char* detectVulkan(FFVulkanResult* result)
|
|
{
|
|
FF_DEBUG("Starting Vulkan detection");
|
|
|
|
FF_LIBRARY_LOAD_MESSAGE(vulkan,
|
|
#if __APPLE__
|
|
"libMoltenVK" FF_LIBRARY_EXTENSION, -1
|
|
#elif _WIN32
|
|
"vulkan-1" FF_LIBRARY_EXTENSION, -1
|
|
#else
|
|
"libvulkan" FF_LIBRARY_EXTENSION, 2
|
|
#endif
|
|
)
|
|
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(vulkan, vkGetInstanceProcAddr)
|
|
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(vulkan, vkCreateInstance)
|
|
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(vulkan, vkDestroyInstance)
|
|
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(vulkan, vkEnumeratePhysicalDevices)
|
|
|
|
//Some drivers (nvdc) print messages to stdout
|
|
//and that is the best way I found to disable that
|
|
FF_SUPPRESS_IO();
|
|
FF_DEBUG("Suppressed stdout/stderr during Vulkan probing to avoid noisy drivers");
|
|
|
|
FFVersion instanceVersion = FF_VERSION_INIT;
|
|
|
|
//We need to get the function pointer this way, because it is only provided by vulkan 1.1 and higher.
|
|
//a dlsym would fail on 1.0 implementations
|
|
PFN_vkEnumerateInstanceVersion ffvkEnumerateInstanceVersion = (PFN_vkEnumerateInstanceVersion) ffvkGetInstanceProcAddr(NULL, "vkEnumerateInstanceVersion");
|
|
if(ffvkEnumerateInstanceVersion != NULL)
|
|
{
|
|
uint32_t version;
|
|
if(ffvkEnumerateInstanceVersion(&version) == VK_SUCCESS)
|
|
{
|
|
applyVulkanVersion(version, &instanceVersion);
|
|
FF_DEBUG("Detected Vulkan instance version: %u.%u.%u", instanceVersion.major, instanceVersion.minor, instanceVersion.patch);
|
|
}
|
|
else
|
|
FF_DEBUG("vkEnumerateInstanceVersion() is available but returned a non-success status");
|
|
}
|
|
else
|
|
FF_DEBUG("vkEnumerateInstanceVersion() is unavailable (likely Vulkan 1.0 runtime)");
|
|
|
|
const uint32_t projectVersion = VK_MAKE_VERSION(
|
|
FASTFETCH_PROJECT_VERSION_MAJOR,
|
|
FASTFETCH_PROJECT_VERSION_MINOR,
|
|
FASTFETCH_PROJECT_VERSION_PATCH
|
|
);
|
|
|
|
VkInstance vkInstance;
|
|
FF_DEBUG("Creating Vulkan instance with requested API version %s", instanceVersion.minor >= 1 ? "1.1" : "1.0");
|
|
VkResult res = ffvkCreateInstance(&(VkInstanceCreateInfo) {
|
|
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
|
.pNext = NULL,
|
|
.pApplicationInfo = &(VkApplicationInfo) {
|
|
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
|
.pNext = NULL,
|
|
.pApplicationName = FASTFETCH_PROJECT_NAME,
|
|
.applicationVersion = projectVersion,
|
|
.pEngineName = "vulkanPrintGPUs",
|
|
.engineVersion = projectVersion,
|
|
|
|
// We need to request 1.1 to get physicalDeviceDriverProperties
|
|
.apiVersion = instanceVersion.minor >= 1 ? VK_API_VERSION_1_1 : VK_API_VERSION_1_0
|
|
},
|
|
.enabledLayerCount = 0,
|
|
.ppEnabledLayerNames = NULL,
|
|
.enabledExtensionCount = 0,
|
|
.ppEnabledExtensionNames = NULL,
|
|
.flags = 0
|
|
}, NULL, &vkInstance);
|
|
if(res != VK_SUCCESS)
|
|
{
|
|
FF_DEBUG("ffvkCreateInstance() failed with VkResult=%d", res);
|
|
switch (res)
|
|
{
|
|
case VK_ERROR_OUT_OF_HOST_MEMORY:
|
|
return "ffvkCreateInstance() failed: VK_ERROR_OUT_OF_HOST_MEMORY";
|
|
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
|
|
return "ffvkCreateInstance() failed: VK_ERROR_OUT_OF_DEVICE_MEMORY";
|
|
case VK_ERROR_INITIALIZATION_FAILED:
|
|
return "ffvkCreateInstance() failed: VK_ERROR_INITIALIZATION_FAILED";
|
|
case VK_ERROR_LAYER_NOT_PRESENT:
|
|
return "ffvkCreateInstance() failed: VK_ERROR_LAYER_NOT_PRESENT";
|
|
case VK_ERROR_EXTENSION_NOT_PRESENT:
|
|
return "ffvkCreateInstance() failed: VK_ERROR_EXTENSION_NOT_PRESENT";
|
|
case VK_ERROR_INCOMPATIBLE_DRIVER:
|
|
return "ffvkCreateInstance() failed: VK_ERROR_INCOMPATIBLE_DRIVER";
|
|
default:
|
|
return "ffvkCreateInstance() failed: unknown error";
|
|
}
|
|
}
|
|
FF_DEBUG("Vulkan instance created successfully");
|
|
|
|
//if instance creation succeeded, but vkEnumerateInstanceVersion didn't, this means we are running against a vulkan 1.0 implementation
|
|
//explicitly set this version, if no device is found, so we still have at least this info
|
|
if(instanceVersion.major == 0 && instanceVersion.minor == 0 && instanceVersion.patch == 0)
|
|
{
|
|
instanceVersion.major = 1;
|
|
FF_DEBUG("Falling back to Vulkan instance version 1.0 due to unavailable enumerate call");
|
|
}
|
|
|
|
VkPhysicalDevice physicalDevices[128];
|
|
uint32_t physicalDeviceCount = (uint32_t) ARRAY_SIZE(physicalDevices);
|
|
res = ffvkEnumeratePhysicalDevices(vkInstance, &physicalDeviceCount, physicalDevices);
|
|
if(res != VK_SUCCESS)
|
|
{
|
|
FF_DEBUG("ffvkEnumeratePhysicalDevices() failed with VkResult=%d", res);
|
|
ffvkDestroyInstance(vkInstance, NULL);
|
|
switch (res)
|
|
{
|
|
case VK_ERROR_OUT_OF_HOST_MEMORY:
|
|
return "ffvkEnumeratePhysicalDevices() failed: VK_ERROR_OUT_OF_HOST_MEMORY";
|
|
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
|
|
return "ffvkEnumeratePhysicalDevices() failed: VK_ERROR_OUT_OF_DEVICE_MEMORY";
|
|
case VK_ERROR_INITIALIZATION_FAILED:
|
|
return "ffvkEnumeratePhysicalDevices() failed: VK_ERROR_INITIALIZATION_FAILED";
|
|
case VK_INCOMPLETE:
|
|
return "ffvkEnumeratePhysicalDevices() failed: VK_INCOMPLETE";
|
|
default:
|
|
return "ffvkEnumeratePhysicalDevices() failed";
|
|
}
|
|
}
|
|
FF_DEBUG("Enumerated %u Vulkan physical device(s)", physicalDeviceCount);
|
|
|
|
PFN_vkGetPhysicalDeviceProperties ffvkGetPhysicalDeviceProperties = NULL;
|
|
PFN_vkGetPhysicalDeviceProperties2 ffvkGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2) ffvkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceProperties2"); // 1.1
|
|
if(!ffvkGetPhysicalDeviceProperties2)
|
|
ffvkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties) ffvkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceProperties");
|
|
|
|
FF_DEBUG("Using %s for querying physical device properties", ffvkGetPhysicalDeviceProperties2 ? "vkGetPhysicalDeviceProperties2" : "vkGetPhysicalDeviceProperties");
|
|
|
|
PFN_vkGetPhysicalDeviceMemoryProperties ffvkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties) ffvkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceMemoryProperties");
|
|
if(!ffvkGetPhysicalDeviceMemoryProperties)
|
|
{
|
|
FF_DEBUG("vkGetPhysicalDeviceMemoryProperties is unavailable");
|
|
ffvkDestroyInstance(vkInstance, NULL);
|
|
return "vkGetPhysicalDeviceMemoryProperties is not available";
|
|
}
|
|
|
|
FFVersion maxDeviceApiVersion = FF_VERSION_INIT;
|
|
FFVersion maxDeviceConformanceVersion = FF_VERSION_INIT;
|
|
|
|
for(uint32_t i = 0; i < physicalDeviceCount; i++)
|
|
{
|
|
//Get device properties.
|
|
//On VK 1.1 and up, we use vkGetPhysicalDeviceProperties2, so we can put VkPhysicalDeviceDriverProperties in the pNext chain.
|
|
//This is required to get the driver name and conformance version.
|
|
|
|
VkPhysicalDeviceDriverPropertiesKHR driverProperties = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR,
|
|
};
|
|
VkPhysicalDeviceProperties2 physicalDeviceProperties = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
|
.pNext = &driverProperties,
|
|
};
|
|
|
|
if(ffvkGetPhysicalDeviceProperties2 != NULL)
|
|
ffvkGetPhysicalDeviceProperties2(physicalDevices[i], &physicalDeviceProperties);
|
|
else
|
|
ffvkGetPhysicalDeviceProperties(physicalDevices[i], &physicalDeviceProperties.properties);
|
|
|
|
FF_DEBUG("Processing Vulkan device #%u: name='%s', vendorId=0x%04X, deviceId=0x%04X, type=%u", i,
|
|
physicalDeviceProperties.properties.deviceName,
|
|
physicalDeviceProperties.properties.vendorID,
|
|
physicalDeviceProperties.properties.deviceID,
|
|
physicalDeviceProperties.properties.deviceType);
|
|
|
|
|
|
//We don't want software rasterizers to show up as physical gpu
|
|
if(physicalDeviceProperties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU)
|
|
{
|
|
FF_DEBUG("Skipping CPU Vulkan device '%s'", physicalDeviceProperties.properties.deviceName);
|
|
continue;
|
|
}
|
|
|
|
//If the device api version is higher than the current highest device api version, overwrite it
|
|
//In this case, also use the current device driver name as the shown driver name
|
|
|
|
FFVersion deviceAPIVersion = FF_VERSION_INIT;
|
|
applyVulkanVersion(physicalDeviceProperties.properties.apiVersion, &deviceAPIVersion);
|
|
if(ffVersionCompare(&deviceAPIVersion, &maxDeviceApiVersion) > 0)
|
|
{
|
|
maxDeviceApiVersion = deviceAPIVersion;
|
|
applyDriverName(&driverProperties, &result->driver);
|
|
FF_DEBUG("Updated max Vulkan device API version to %u.%u.%u (driver='%s')",
|
|
maxDeviceApiVersion.major,
|
|
maxDeviceApiVersion.minor,
|
|
maxDeviceApiVersion.patch,
|
|
result->driver.chars);
|
|
}
|
|
|
|
//If the device conformance version is higher than the current highest device conformance version, overwrite it
|
|
if(ffvkGetPhysicalDeviceProperties2)
|
|
{
|
|
FFVersion deviceConformanceVersion = {
|
|
.major = driverProperties.conformanceVersion.major,
|
|
.minor = driverProperties.conformanceVersion.minor,
|
|
.patch = driverProperties.conformanceVersion.patch,
|
|
};
|
|
|
|
if(ffVersionCompare(&deviceConformanceVersion, &maxDeviceConformanceVersion) > 0)
|
|
{
|
|
maxDeviceConformanceVersion = deviceConformanceVersion;
|
|
FF_DEBUG("Updated max Vulkan conformance version to %u.%u.%u",
|
|
maxDeviceConformanceVersion.major,
|
|
maxDeviceConformanceVersion.minor,
|
|
maxDeviceConformanceVersion.patch);
|
|
}
|
|
}
|
|
|
|
//Add the device to the list of devices shown by the GPU module
|
|
|
|
// #456
|
|
FF_LIST_FOR_EACH(FFGPUResult, gpu, result->gpus)
|
|
{
|
|
if (gpu->deviceId == physicalDeviceProperties.properties.deviceID)
|
|
{
|
|
FF_DEBUG("Skipping duplicate Vulkan GPU entry for deviceId=0x%04X", physicalDeviceProperties.properties.deviceID);
|
|
goto next;
|
|
}
|
|
}
|
|
|
|
FFGPUResult* gpu = ffListAdd(&result->gpus);
|
|
|
|
ffStrbufInitF(&gpu->platformApi, "Vulkan %u.%u.%u", deviceAPIVersion.major, deviceAPIVersion.minor, deviceAPIVersion.patch);
|
|
gpu->deviceId = physicalDeviceProperties.properties.deviceID;
|
|
|
|
ffStrbufInitS(&gpu->name, physicalDeviceProperties.properties.deviceName);
|
|
|
|
gpu->type = physicalDeviceProperties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED;
|
|
ffStrbufInitS(&gpu->vendor, ffGPUGetVendorString(physicalDeviceProperties.properties.vendorID));
|
|
ffStrbufInitS(&gpu->driver, driverProperties.driverInfo);
|
|
ffStrbufInit(&gpu->memoryType);
|
|
FF_DEBUG("Added Vulkan GPU '%s' (vendor='%s', type=%s)",
|
|
gpu->name.chars,
|
|
gpu->vendor.chars,
|
|
gpu->type == FF_GPU_TYPE_DISCRETE ? "discrete" : "integrated");
|
|
|
|
VkPhysicalDeviceMemoryProperties memoryProperties = {};
|
|
ffvkGetPhysicalDeviceMemoryProperties(physicalDevices[i], &memoryProperties);
|
|
|
|
gpu->dedicated.total = gpu->shared.total = 0;
|
|
gpu->dedicated.used = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
|
|
for(uint32_t index = 0; index < memoryProperties.memoryHeapCount; ++index)
|
|
{
|
|
const VkMemoryHeap* heap = &memoryProperties.memoryHeaps[index];
|
|
FFGPUMemory* vmem = gpu->type == FF_GPU_TYPE_DISCRETE && (heap->flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) ? &gpu->dedicated : &gpu->shared;
|
|
vmem->total += heap->size;
|
|
}
|
|
FF_DEBUG("Computed memory for '%s': dedicatedTotal=%llu, sharedTotal=%llu",
|
|
gpu->name.chars,
|
|
(unsigned long long) gpu->dedicated.total,
|
|
(unsigned long long) gpu->shared.total);
|
|
|
|
//No way to detect those using vulkan
|
|
gpu->index = FF_GPU_INDEX_UNSET;
|
|
gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
|
|
gpu->temperature = FF_GPU_TEMP_UNSET;
|
|
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
|
|
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
|
|
|
|
next:
|
|
continue;
|
|
}
|
|
|
|
ffVersionToPretty(&instanceVersion, &result->instanceVersion);
|
|
ffVersionToPretty(&maxDeviceApiVersion, &result->apiVersion);
|
|
ffVersionToPretty(&maxDeviceConformanceVersion, &result->conformanceVersion);
|
|
|
|
FF_DEBUG("Vulkan detection finished: instanceVersion=%s, apiVersion=%s, conformanceVersion=%s, gpuCount=%u",
|
|
result->instanceVersion.chars,
|
|
result->apiVersion.chars,
|
|
result->conformanceVersion.chars,
|
|
result->gpus.length);
|
|
|
|
ffvkDestroyInstance(vkInstance, NULL);
|
|
FF_DEBUG("Destroyed Vulkan instance");
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
FFVulkanResult* ffDetectVulkan(void)
|
|
{
|
|
static FFVulkanResult result;
|
|
|
|
if (result.gpus.elementSize == 0)
|
|
{
|
|
FF_DEBUG("Initializing Vulkan detection cache");
|
|
ffStrbufInit(&result.driver);
|
|
ffStrbufInit(&result.apiVersion);
|
|
ffStrbufInit(&result.conformanceVersion);
|
|
ffStrbufInit(&result.instanceVersion);
|
|
ffListInit(&result.gpus, sizeof(FFGPUResult));
|
|
|
|
#ifdef FF_HAVE_VULKAN
|
|
result.error = detectVulkan(&result);
|
|
if (result.error)
|
|
FF_DEBUG("Vulkan detection returned error: %s", result.error);
|
|
#else
|
|
result.error = "fastfetch was compiled without vulkan support";
|
|
FF_DEBUG("Vulkan support is disabled at compile time");
|
|
#endif
|
|
}
|
|
else
|
|
FF_DEBUG("Reusing cached Vulkan detection result");
|
|
|
|
return &result;
|
|
}
|