Vulkan: Separate the instance and device API versions, for extension loading purposes

This commit is contained in:
Henrik Rydgård 2024-09-25 18:36:20 +02:00
parent b2d9ac54dd
commit 8c8b34aac4
7 changed files with 64 additions and 35 deletions

View File

@ -102,11 +102,13 @@ VkResult VulkanContext::CreateInstance(const CreateInfo &info) {
// Check which Vulkan version we should request.
// Our code is fine with any version from 1.0 to 1.2, we don't know about higher versions.
vulkanApiVersion_ = VK_API_VERSION_1_0;
vulkanInstanceApiVersion_ = VK_API_VERSION_1_0;
if (vkEnumerateInstanceVersion) {
vkEnumerateInstanceVersion(&vulkanApiVersion_);
vulkanApiVersion_ &= 0xFFFFF000; // Remove patch version.
vulkanApiVersion_ = std::min(VK_API_VERSION_1_3, vulkanApiVersion_);
vkEnumerateInstanceVersion(&vulkanInstanceApiVersion_);
vulkanInstanceApiVersion_ &= 0xFFFFF000; // Remove patch version.
vulkanInstanceApiVersion_ = std::min(VK_API_VERSION_1_3, vulkanInstanceApiVersion_);
std::string versionString = FormatAPIVersion(vulkanInstanceApiVersion_);
INFO_LOG(Log::G3D, "Detected Vulkan API version: %s", versionString.c_str());
}
instance_layer_names_.clear();
@ -201,7 +203,7 @@ VkResult VulkanContext::CreateInstance(const CreateInfo &info) {
app_info.pEngineName = info.app_name;
// Let's increment this when we make major engine/context changes.
app_info.engineVersion = 2;
app_info.apiVersion = vulkanApiVersion_;
app_info.apiVersion = vulkanInstanceApiVersion_;
VkInstanceCreateInfo inst_info{ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
inst_info.flags = 0;
@ -240,7 +242,7 @@ VkResult VulkanContext::CreateInstance(const CreateInfo &info) {
return res;
}
VulkanLoadInstanceFunctions(instance_, extensionsLookup_, vulkanApiVersion_);
VulkanLoadInstanceFunctions(instance_, extensionsLookup_, vulkanInstanceApiVersion_);
if (!CheckLayers(instance_layer_properties_, instance_layer_names_)) {
WARN_LOG(Log::G3D, "CheckLayers for instance failed");
// init_error_ = "Failed to validate instance layers";
@ -560,7 +562,7 @@ int VulkanContext::GetBestPhysicalDevice() {
}
bool VulkanContext::EnableDeviceExtension(const char *extension, uint32_t coreVersion) {
if (coreVersion != 0 && vulkanApiVersion_ >= coreVersion) {
if (coreVersion != 0 && vulkanDeviceApiVersion_ >= coreVersion) {
return true;
}
for (auto &iter : device_extension_properties_) {
@ -573,7 +575,7 @@ bool VulkanContext::EnableDeviceExtension(const char *extension, uint32_t coreVe
}
bool VulkanContext::EnableInstanceExtension(const char *extension, uint32_t coreVersion) {
if (coreVersion != 0 && vulkanApiVersion_ >= coreVersion) {
if (coreVersion != 0 && vulkanInstanceApiVersion_ >= coreVersion) {
return true;
}
for (auto &iter : instance_extension_properties_) {
@ -589,6 +591,8 @@ VkResult VulkanContext::CreateDevice(int physical_device) {
physical_device_ = physical_device;
INFO_LOG(Log::G3D, "Chose physical device %d: %s", physical_device, physicalDeviceProperties_[physical_device].properties.deviceName);
vulkanDeviceApiVersion_ = physicalDeviceProperties_[physical_device].properties.apiVersion;
GetDeviceLayerProperties();
if (!CheckLayers(device_layer_properties_, device_layer_names_)) {
WARN_LOG(Log::G3D, "CheckLayers for device %d failed", physical_device);
@ -668,6 +672,7 @@ VkResult VulkanContext::CreateDevice(int physical_device) {
extensionsLookup_.KHR_maintenance1 = EnableDeviceExtension(VK_KHR_MAINTENANCE1_EXTENSION_NAME, VK_API_VERSION_1_1);
extensionsLookup_.KHR_maintenance2 = EnableDeviceExtension(VK_KHR_MAINTENANCE2_EXTENSION_NAME, VK_API_VERSION_1_1);
extensionsLookup_.KHR_maintenance3 = EnableDeviceExtension(VK_KHR_MAINTENANCE3_EXTENSION_NAME, VK_API_VERSION_1_1);
extensionsLookup_.KHR_maintenance4 = EnableDeviceExtension("VK_KHR_maintenance4", VK_API_VERSION_1_3);
extensionsLookup_.KHR_multiview = EnableDeviceExtension(VK_KHR_MULTIVIEW_EXTENSION_NAME, VK_API_VERSION_1_1);
if (EnableDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, VK_API_VERSION_1_1)) {
@ -796,9 +801,9 @@ VkResult VulkanContext::CreateDevice(int physical_device) {
VkResult res = vkCreateDevice(physical_devices_[physical_device_], &device_info, nullptr, &device_);
if (res != VK_SUCCESS) {
init_error_ = "Unable to create Vulkan device";
ERROR_LOG(Log::G3D, "Unable to create Vulkan device");
ERROR_LOG(Log::G3D, "%s", init_error_.c_str());
} else {
VulkanLoadDeviceFunctions(device_, extensionsLookup_, vulkanApiVersion_);
VulkanLoadDeviceFunctions(device_, extensionsLookup_, vulkanDeviceApiVersion_);
}
INFO_LOG(Log::G3D, "Vulkan Device created: %s", physicalDeviceProperties_[physical_device_].properties.deviceName);
@ -806,7 +811,7 @@ VkResult VulkanContext::CreateDevice(int physical_device) {
VulkanSetAvailable(true);
VmaAllocatorCreateInfo allocatorInfo = {};
allocatorInfo.vulkanApiVersion = vulkanApiVersion_;
allocatorInfo.vulkanApiVersion = std::min(vulkanDeviceApiVersion_, vulkanInstanceApiVersion_);
allocatorInfo.physicalDevice = physical_devices_[physical_device_];
allocatorInfo.device = device_;
allocatorInfo.instance = instance_;

View File

@ -360,6 +360,7 @@ public:
// out of MAX_INFLIGHT_FRAMES.
return inflightFrames_;
}
// Don't call while a frame is in progress.
void UpdateInflightFrames(int n);
@ -414,6 +415,14 @@ public:
return frame_[curFrame_].deleteList.GetLastDeleteCount();
}
u32 InstanceApiVersion() const {
return vulkanInstanceApiVersion_;
}
u32 DeviceApiVersion() const {
return vulkanDeviceApiVersion_;
}
private:
bool ChooseQueue();
@ -441,7 +450,8 @@ private:
VkDevice device_ = VK_NULL_HANDLE;
VkQueue gfx_queue_ = VK_NULL_HANDLE;
VkSurfaceKHR surface_ = VK_NULL_HANDLE;
u32 vulkanApiVersion_ = 0;
u32 vulkanInstanceApiVersion_ = 0;
u32 vulkanDeviceApiVersion_ = 0;
std::string init_error_;
std::vector<const char *> instance_layer_names_;

View File

@ -265,7 +265,7 @@ bool g_vulkanAvailabilityChecked = false;
bool g_vulkanMayBeAvailable = false;
static PFN_vkVoidFunction LoadInstanceFunc(VkInstance instance, const char *name) {
PFN_vkVoidFunction funcPtr = vkGetInstanceProcAddr(instance, name);
PFN_vkVoidFunction funcPtr = vkGetInstanceProcAddr(instance, name);
if (!funcPtr) {
INFO_LOG(Log::G3D, "Missing function (instance): %s", name);
}
@ -273,9 +273,9 @@ static PFN_vkVoidFunction LoadInstanceFunc(VkInstance instance, const char *name
}
#define LOAD_INSTANCE_FUNC(instance, x) x = (PFN_ ## x)LoadInstanceFunc(instance, #x);
static PFN_vkVoidFunction LoadInstanceFuncCore(VkInstance instance, const char *name, const char *extName, u32 min_core, u32 vulkanApiVersion) {
PFN_vkVoidFunction funcPtr = vkGetInstanceProcAddr(instance, vulkanApiVersion >= min_core ? name : extName);
if (vulkanApiVersion >= min_core && !funcPtr) {
static PFN_vkVoidFunction LoadInstanceFuncCore(VkInstance instance, const char *name, const char *extName, u32 min_core, u32 vulkanInstanceApiVersion) {
PFN_vkVoidFunction funcPtr = vkGetInstanceProcAddr(instance, vulkanInstanceApiVersion >= min_core ? name : extName);
if (vulkanInstanceApiVersion >= min_core && !funcPtr) {
// Try the ext name.
funcPtr = vkGetInstanceProcAddr(instance, extName);
}
@ -285,10 +285,10 @@ static PFN_vkVoidFunction LoadInstanceFuncCore(VkInstance instance, const char *
return funcPtr;
}
#define LOAD_INSTANCE_FUNC_CORE(instance, x, ext_x, min_core) \
x = (PFN_ ## x)LoadInstanceFuncCore(instance, #x, #ext_x, min_core, vulkanApiVersion);
x = (PFN_ ## x)LoadInstanceFuncCore(instance, #x, #ext_x, min_core, vulkanInstanceApiVersion);
static PFN_vkVoidFunction LoadDeviceFunc(VkDevice device, const char *name) {
PFN_vkVoidFunction funcPtr = vkGetDeviceProcAddr(device, name);
PFN_vkVoidFunction funcPtr = vkGetDeviceProcAddr(device, name);
if (!funcPtr) {
INFO_LOG(Log::G3D, "Missing function (device): %s", name);
}
@ -296,9 +296,9 @@ static PFN_vkVoidFunction LoadDeviceFunc(VkDevice device, const char *name) {
}
#define LOAD_DEVICE_FUNC(device, x) x = (PFN_ ## x)LoadDeviceFunc(device, #x);
static PFN_vkVoidFunction LoadDeviceFuncCore(VkDevice device, const char *name, const char *extName, u32 min_core, u32 vulkanApiVersion) {
PFN_vkVoidFunction funcPtr = vkGetDeviceProcAddr(device, vulkanApiVersion >= min_core ? name : extName);
if (vulkanApiVersion >= min_core && !funcPtr) {
static PFN_vkVoidFunction LoadDeviceFuncCore(VkDevice device, const char *name, const char *extName, u32 min_core, u32 vulkanDeviceApiVersion) {
PFN_vkVoidFunction funcPtr = vkGetDeviceProcAddr(device, vulkanDeviceApiVersion >= min_core ? name : extName);
if (vulkanDeviceApiVersion >= min_core && !funcPtr) {
funcPtr = vkGetDeviceProcAddr(device, extName);
}
if (!funcPtr) {
@ -307,7 +307,7 @@ static PFN_vkVoidFunction LoadDeviceFuncCore(VkDevice device, const char *name,
return funcPtr;
}
#define LOAD_DEVICE_FUNC_CORE(device, x, ext_x, min_core) \
x = (PFN_ ## x)LoadDeviceFuncCore(device, #x, #ext_x, min_core, vulkanApiVersion);
x = (PFN_ ## x)LoadDeviceFuncCore(device, #x, #ext_x, min_core, vulkanDeviceApiVersion);
#define LOAD_GLOBAL_FUNC(x) x = (PFN_ ## x)dlsym(vulkanLibrary, #x); if (!x) {INFO_LOG(Log::G3D,"Missing (global): %s", #x);}
#define LOAD_GLOBAL_FUNC_LOCAL(lib, x) (PFN_ ## x)dlsym(lib, #x);
@ -645,8 +645,9 @@ bool VulkanLoad(std::string *errorStr) {
#endif
}
void VulkanLoadInstanceFunctions(VkInstance instance, const VulkanExtensions &enabledExtensions, uint32_t vulkanApiVersion) {
void VulkanLoadInstanceFunctions(VkInstance instance, const VulkanExtensions &enabledExtensions, uint32_t vulkanInstanceApiVersion) {
#if !PPSSPP_PLATFORM(IOS_APP_STORE)
INFO_LOG(Log::G3D, "Loading Vulkan instance functions. Instance API version: %08x (%d.%d.%d)", vulkanInstanceApiVersion, VK_API_VERSION_MAJOR(vulkanInstanceApiVersion), VK_API_VERSION_MINOR(vulkanInstanceApiVersion), VK_API_VERSION_PATCH(vulkanInstanceApiVersion));
// OK, let's use the above functions to get the rest.
LOAD_INSTANCE_FUNC(instance, vkDestroyInstance);
LOAD_INSTANCE_FUNC(instance, vkEnumeratePhysicalDevices);
@ -720,9 +721,9 @@ void VulkanLoadInstanceFunctions(VkInstance instance, const VulkanExtensions &en
// On some implementations, loading functions (that have Device as their first parameter) via vkGetDeviceProcAddr may
// increase performance - but then these function pointers will only work on that specific device. Thus, this loader is not very
// good for multi-device - not likely we'll ever try that anyway though.
void VulkanLoadDeviceFunctions(VkDevice device, const VulkanExtensions &enabledExtensions, uint32_t vulkanApiVersion) {
void VulkanLoadDeviceFunctions(VkDevice device, const VulkanExtensions &enabledExtensions, uint32_t vulkanDeviceApiVersion) {
#if !PPSSPP_PLATFORM(IOS_APP_STORE)
INFO_LOG(Log::G3D, "Loading Vulkan device functions. Vulkan API version: %08x (%d.%d)", vulkanApiVersion, VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
INFO_LOG(Log::G3D, "Loading Vulkan device functions. Device API version: %08x (%d.%d.%d)", vulkanDeviceApiVersion, VK_API_VERSION_MAJOR(vulkanDeviceApiVersion), VK_API_VERSION_MINOR(vulkanDeviceApiVersion), VK_API_VERSION_PATCH(vulkanDeviceApiVersion));
LOAD_DEVICE_FUNC(device, vkQueueSubmit);
LOAD_DEVICE_FUNC(device, vkQueueWaitIdle);
@ -739,10 +740,8 @@ void VulkanLoadDeviceFunctions(VkDevice device, const VulkanExtensions &enabledE
LOAD_DEVICE_FUNC(device, vkBindImageMemory2);
LOAD_DEVICE_FUNC(device, vkGetBufferMemoryRequirements);
LOAD_DEVICE_FUNC(device, vkGetBufferMemoryRequirements2);
LOAD_DEVICE_FUNC(device, vkGetDeviceBufferMemoryRequirements);
LOAD_DEVICE_FUNC(device, vkGetImageMemoryRequirements);
LOAD_DEVICE_FUNC(device, vkGetImageMemoryRequirements2);
LOAD_DEVICE_FUNC(device, vkGetDeviceImageMemoryRequirements);
LOAD_DEVICE_FUNC(device, vkCreateFence);
LOAD_DEVICE_FUNC(device, vkDestroyFence);
LOAD_DEVICE_FUNC(device, vkResetFences);
@ -860,6 +859,10 @@ void VulkanLoadDeviceFunctions(VkDevice device, const VulkanExtensions &enabledE
if (enabledExtensions.KHR_create_renderpass2) {
LOAD_DEVICE_FUNC_CORE(device, vkCreateRenderPass2, vkCreateRenderPass2KHR, VK_API_VERSION_1_2);
}
if (enabledExtensions.KHR_maintenance4) {
LOAD_DEVICE_FUNC_CORE(device, vkGetDeviceBufferMemoryRequirements, vkGetDeviceBufferMemoryRequirementsKHR, VK_API_VERSION_1_3);
LOAD_DEVICE_FUNC_CORE(device, vkGetDeviceImageMemoryRequirements, vkGetDeviceImageMemoryRequirementsKHR, VK_API_VERSION_1_3);
}
#endif
}

View File

@ -255,6 +255,7 @@ struct VulkanExtensions {
bool KHR_maintenance1; // required for KHR_create_renderpass2
bool KHR_maintenance2;
bool KHR_maintenance3;
bool KHR_maintenance4;
bool KHR_multiview; // required for KHR_create_renderpass2
bool KHR_get_memory_requirements2;
bool KHR_dedicated_allocation;

View File

@ -527,10 +527,8 @@ public:
case InfoField::VENDOR: return VulkanVendorString(vulkan_->GetPhysicalDeviceProperties().properties.vendorID);
case InfoField::DRIVER: return FormatDriverVersion(vulkan_->GetPhysicalDeviceProperties().properties);
case InfoField::SHADELANGVERSION: return "N/A";;
case InfoField::APIVERSION:
{
return FormatAPIVersion(vulkan_->GetPhysicalDeviceProperties().properties.apiVersion);
}
case InfoField::APIVERSION: return FormatAPIVersion(vulkan_->InstanceApiVersion());
case InfoField::DEVICE_API_VERSION: return FormatAPIVersion(vulkan_->DeviceApiVersion());
default: return "?";
}
}
@ -881,6 +879,8 @@ VKContext::VKContext(VulkanContext *vulkan, bool useRenderThread)
VkFormat depthStencilFormat = vulkan->GetDeviceInfo().preferredDepthStencilFormat;
INFO_LOG(Log::G3D, "Determining Vulkan device caps");
caps_.setMaxFrameLatencySupported = true;
caps_.anisoSupported = vulkan->GetDeviceFeatures().enabled.standard.samplerAnisotropy != 0;
caps_.geometryShaderSupported = vulkan->GetDeviceFeatures().enabled.standard.geometryShader != 0;
@ -979,8 +979,10 @@ VKContext::VKContext(VulkanContext *vulkan, bool useRenderThread)
caps_.deviceID = deviceProps.deviceID;
if (caps_.vendor == GPUVendor::VENDOR_QUALCOMM) {
// if (caps_.deviceID < 0x6000000) // On sub 6xx series GPUs, disallow multisample.
multisampleAllowed = false; // Actually, let's disable it on them all for now. See issue #18818.
if (caps_.deviceID < 0x6000000) { // On sub 6xx series GPUs, disallow multisample.
INFO_LOG(Log::G3D, "Multisampling was disabled due to old driver version (Adreno)");
multisampleAllowed = false;
}
// Adreno 5xx devices, all known driver versions, fail to discard stencil when depth write is off.
// See: https://github.com/hrydgard/ppsspp/pull/11684

View File

@ -218,6 +218,7 @@ enum class InfoField {
VENDOR,
SHADELANGVERSION,
DRIVER,
DEVICE_API_VERSION, // Vulkan-only
};
enum class GPUVendor {

View File

@ -539,8 +539,9 @@ void SystemInfoScreen::CreateTabs() {
gpuInfo->Add(new InfoItem(si->T("Vendor (detected)"), vendor));
gpuInfo->Add(new InfoItem(si->T("Driver Version"), draw->GetInfoString(InfoField::DRIVER)));
#ifdef _WIN32
if (GetGPUBackend() != GPUBackend::VULKAN)
if (GetGPUBackend() != GPUBackend::VULKAN) {
gpuInfo->Add(new InfoItem(si->T("Driver Version"), System_GetProperty(SYSPROP_GPUDRIVER_VERSION)));
}
#if !PPSSPP_PLATFORM(UWP)
if (GetGPUBackend() == GPUBackend::DIRECT3D9) {
gpuInfo->Add(new InfoItem(si->T("D3DCompiler Version"), StringFromFormat("%d", GetD3DCompilerVersion())));
@ -644,12 +645,18 @@ void SystemInfoScreen::CreateTabs() {
} else {
apiVersion = StringFromFormat("v%d.%d.%d", gl_extensions.ver[0], gl_extensions.ver[1], gl_extensions.ver[2]);
}
versionInfo->Add(new InfoItem(si->T("API Version"), apiVersion));
} else {
apiVersion = draw->GetInfoString(InfoField::APIVERSION);
if (apiVersion.size() > 30)
apiVersion.resize(30);
versionInfo->Add(new InfoItem(si->T("API Version"), apiVersion));
if (GetGPUBackend() == GPUBackend::VULKAN) {
std::string deviceApiVersion = draw->GetInfoString(InfoField::DEVICE_API_VERSION);
versionInfo->Add(new InfoItem(si->T("Device API Version"), deviceApiVersion));
}
}
versionInfo->Add(new InfoItem(si->T("API Version"), apiVersion));
versionInfo->Add(new InfoItem(si->T("Shading Language"), draw->GetInfoString(InfoField::SHADELANGVERSION)));
#if PPSSPP_PLATFORM(ANDROID)