SL-16605: Report VulkanMaxApiVersion with the appropiate Vulkan 0.0, 1.0, 1.x max API version depending on if we can load the .dll and/or vkEnumerateInstanceVersion.

master
Ptolemy 2022-01-07 17:57:39 -08:00
parent 0cb712af5c
commit b24c0a6ba2
1 changed files with 108 additions and 1 deletions

View File

@ -64,6 +64,56 @@
#include "llvoicevivox.h"
#include "lluiusage.h"
#define LL_MINIMAL_VULKAN 1
#if LL_MINIMAL_VULKAN
// Calls
#if defined(_WIN32)
#define VKAPI_ATTR
#define VKAPI_CALL __stdcall
#define VKAPI_PTR VKAPI_CALL
#else
#define VKAPI_ATTR
#define VKAPI_CALL
#define VKAPI_PTR
#endif // _WIN32
// Macros
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// <variant> <-------major-------><-----------minor-----------> <--------------patch-------------->
// 0x7 0x7F 0x3FF 0xFFF
#define VK_API_VERSION_MAJOR( version) (((uint32_t)(version) >> 22) & 0x07FU) // 7 bits
#define VK_API_VERSION_MINOR( version) (((uint32_t)(version) >> 12) & 0x3FFU) // 10 bits
#define VK_API_VERSION_PATCH( version) (((uint32_t)(version) ) & 0xFFFU) // 12 bits
#define VK_API_VERSION_VARIANT(version) (((uint32_t)(version) >> 29) & 0x007U) // 3 bits
// NOTE: variant is first parameter! This is to match vulkan/vulkan_core.h
#define VK_MAKE_API_VERSION(variant, major, minor, patch) (0\
| (((uint32_t)(major & 0x07FU)) << 22) \
| (((uint32_t)(minor & 0x3FFU)) << 12) \
| (((uint32_t)(patch & 0xFFFU)) ) \
| (((uint32_t)(variant & 0x007U)) << 29) )
#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;
// Types
VK_DEFINE_HANDLE(VkInstance);
typedef enum VkResult
{
VK_SUCCESS = 0,
VK_RESULT_MAX_ENUM = 0x7FFFFFFF
} VkResult;
// Prototypes
typedef void (VKAPI_PTR *PFN_vkVoidFunction )(void);
typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_vkGetInstanceProcAddr )(VkInstance instance, const char* pName);
typedef VkResult (VKAPI_PTR *PFN_vkEnumerateInstanceVersion)(uint32_t* pApiVersion);
#else
#include <vulkan/vulkan.h>
#endif // LL_MINIMAL_VULKAN
namespace LLStatViewer
{
@ -594,19 +644,76 @@ void send_viewer_stats(bool include_preferences)
// detailed information on versions and extensions can come later.
static bool vulkan_oneshot = false;
static bool vulkan_detected = false;
static std::string vulkan_max_api_version( "0.0" ); // Unknown/None
if (!vulkan_oneshot)
{
HMODULE vulkan_loader = LoadLibraryExA("vulkan-1.dll", NULL, LOAD_LIBRARY_AS_DATAFILE);
// The 32-bit and 64-bit versions normally exist in:
// C:\Windows\System32
// C:\Windows\SysWOW64
HMODULE vulkan_loader = LoadLibraryA("vulkan-1.dll");
if (NULL != vulkan_loader)
{
vulkan_detected = true;
vulkan_max_api_version = "1.0"; // We have at least 1.0. See the note about vkEnumerateInstanceVersion() below.
// We use Run-Time Dynamic Linking (via GetProcAddress()) instead of Load-Time Dynamic Linking (via directly calling vkGetInstanceProcAddr()).
// This allows us to:
// a) not need the header: #include <vulkan/vulkan.h>
// (and not need to set the corresponding "Additional Include Directories" as long as we provide the equivalent Vulkan types/prototypes/etc.)
// b) not need to link to: vulkan-1.lib
// (and not need to set the corresponding "Additional Library Directories")
// The former will allow Second Life to start and run even if the vulkan.dll is missing.
// The latter will require us to:
// a) link with vulkan-1.lib
// b) cause a System Error at startup if the .dll is not found:
// "The code execution cannot proceed because vulkan-1.dll was not found."
//
// See:
// https://docs.microsoft.com/en-us/windows/win32/dlls/using-run-time-dynamic-linking
// https://docs.microsoft.com/en-us/windows/win32/dlls/run-time-dynamic-linking
// NOTE: Technically we can use GetProcAddress() as a replacement for vkGetInstanceProcAddr()
// but the canonical recommendation (mandate?) is to use vkGetInstanceProcAddr().
PFN_vkGetInstanceProcAddr pGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) GetProcAddress(vulkan_loader, "vkGetInstanceProcAddr");
if(pGetInstanceProcAddr)
{
// Check for vkEnumerateInstanceVersion. If it exists then we have at least 1.1 and can query the max API version.
// NOTE: Each VkPhysicalDevice that supports Vulkan has its own VkPhysicalDeviceProperties.apiVersion which is separate from the max API version!
// See: https://www.lunarg.com/wp-content/uploads/2019/02/Vulkan-1.1-Compatibility-Statement_01_19.pdf
PFN_vkEnumerateInstanceVersion pEnumerateInstanceVersion = (PFN_vkEnumerateInstanceVersion) pGetInstanceProcAddr(NULL, "vkEnumerateInstanceVersion");
if(pEnumerateInstanceVersion)
{
uint32_t version = VK_MAKE_API_VERSION(0,1,1,0); // e.g. 4202631 = 1.2.135.0
VkResult status = pEnumerateInstanceVersion( &version );
if (status != VK_SUCCESS)
{
LL_INFOS("Vulkan") << "Failed to get Vulkan version. Assuming 1.0" << LL_ENDL;
}
else
{
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#extendingvulkan-coreversions-versionnumbers
int major = VK_API_VERSION_MAJOR ( version );
int minor = VK_API_VERSION_MINOR ( version );
int patch = VK_API_VERSION_PATCH ( version );
int variant = VK_API_VERSION_VARIANT( version );
vulkan_max_api_version = llformat( "%d.%d.%d.%d", major, minor, patch, variant );
LL_INFOS("Vulkan") << "Vulkan API version: " << vulkan_max_api_version << ", Raw version: " << version << LL_ENDL;
}
}
}
else
{
LL_WARNS("Vulkan") << "FAILED to get Vulkan vkGetInstanceProcAddr()!" << LL_ENDL;
}
FreeLibrary(vulkan_loader);
}
vulkan_oneshot = true;
}
misc["string_1"] = vulkan_detected ? llformat("Vulkan driver is detected") : llformat("No Vulkan driver detected");
misc["VulkanMaxApiVersion"] = vulkan_max_api_version;
#else
misc["string_1"] = llformat("Unused");