VideoBackends:Vulkan: Move extension checking to PhysicalDeviceInfo

This commit is contained in:
TellowKrinkle 2025-08-17 01:30:42 -05:00
parent 9df286b337
commit 989e783ed1
2 changed files with 135 additions and 65 deletions

View File

@ -42,6 +42,51 @@ static void ConcatenateChains(Chain1* chain1, Chain2* chain2)
next->pNext = reinterpret_cast<VkBaseOutStructure*>(chain2);
}
static const char* ExtensionName(VulkanContext::Extension ext)
{
using Ext = VulkanContext::Extension;
// clang-format off
switch (ext)
{
case Ext::KHR_swapchain: return VK_KHR_SWAPCHAIN_EXTENSION_NAME;
case Ext::KHR_get_physical_device_properties2: return VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN
case Ext::EXT_full_screen_exclusive: return VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME;
#endif
case Ext::EXT_memory_budget: return VK_EXT_MEMORY_BUDGET_EXTENSION_NAME;
case Ext::EXT_depth_clamp_control: return VK_EXT_DEPTH_CLAMP_CONTROL_EXTENSION_NAME;
case Ext::EXT_depth_range_unrestricted: return VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME;
}
// clang-format on
return "UNKNOWN_VULKAN_EXTENSION";
}
static std::vector<VkExtensionProperties> GetExtensionProperties(VkPhysicalDevice device)
{
uint32_t count = 0;
VkResult res = vkEnumerateDeviceExtensionProperties(device, nullptr, &count, nullptr);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkEnumerateDeviceExtensionProperties failed: ");
count = 0;
}
std::vector<VkExtensionProperties> extensions(count);
if (count)
{
res = vkEnumerateDeviceExtensionProperties(device, nullptr, &count, extensions.data());
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkEnumerateDeviceExtensionProperties failed: ");
extensions.clear();
}
else if (count < extensions.size())
{
extensions.resize(count);
}
}
return extensions;
}
VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device)
{
VkPhysicalDeviceFeatures2 features2;
@ -53,6 +98,18 @@ VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device)
vkGetPhysicalDeviceFeatures(device, &features);
apiVersion = vkGetPhysicalDeviceProperties2 ? properties.apiVersion : VK_API_VERSION_1_0;
DEBUG_LOG_FMT(VIDEO, "Vulkan: Dumping extensions for {}...", properties.deviceName);
for (const VkExtensionProperties& ext : GetExtensionProperties(device))
{
DEBUG_LOG_FMT(VIDEO, " Supports {}", ext.extensionName);
for (uint32_t i = 0; i < extensions.size(); i++)
{
auto check = static_cast<Extension>(i);
if (0 == strcmp(ext.extensionName, ExtensionName(check)))
extensions[check] = true;
}
}
if (apiVersion >= VK_API_VERSION_1_1)
{
VkPhysicalDeviceSubgroupProperties properties_subgroup = {};
@ -502,6 +559,9 @@ void VulkanContext::PopulateBackendInfoFeatures(BackendInfo* backend_info, VkPhy
info.fragmentStoresAndAtomics;
backend_info->bSupportsSSAA = info.sampleRateShading;
backend_info->bSupportsLogicOp = info.logicOp;
backend_info->bSupportsUnrestrictedDepthRange =
info.extensions[Extension::EXT_depth_clamp_control] &&
info.extensions[Extension::EXT_depth_range_unrestricted];
// Metal doesn't support this.
backend_info->bSupportsLodBiasInSampler = info.driverID != VK_DRIVER_ID_MOLTENVK;
@ -548,6 +608,9 @@ void VulkanContext::PopulateBackendInfoFeatures(BackendInfo* backend_info, VkPhy
"To prevent visual glitches and artifacts, please use the Metal backend or update "
"to macOS Sonoma 14 or newer.");
}
if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DEPTH_CLAMP_CONTROL))
backend_info->bSupportsUnrestrictedDepthRange = false;
}
void VulkanContext::PopulateBackendInfoMultisampleModes(BackendInfo* backend_info,
@ -626,68 +689,72 @@ std::unique_ptr<VulkanContext> VulkanContext::Create(VkInstance instance, VkPhys
return context;
}
static bool LogExtension(const VulkanContext::PhysicalDeviceInfo& info,
VulkanContext::Extension extension, bool required, bool ignored)
{
using namespace Common::Log;
const char* name = ExtensionName(extension);
const char* type = required ? "required" : "optional";
const char* action = ignored ? "Ignoring" : "Enabling";
LogLevel level = LogLevel::LINFO;
bool enabled = info.extensions[extension];
if (!enabled)
{
action = "Missing";
if (required)
level = LogLevel::LERROR;
}
GENERIC_LOG_FMT(LogType::VIDEO, level, "Vulkan: {} {} extension {}.", action, type, name);
return enabled;
}
static bool RequiredExtension(const VulkanContext::PhysicalDeviceInfo& info,
VulkanContext::Extension extension)
{
return LogExtension(info, extension, true, false);
}
static bool OptionalExtension(const VulkanContext::PhysicalDeviceInfo& info,
VulkanContext::Extension extension)
{
return LogExtension(info, extension, false, false);
}
static void IgnoreExtension(VulkanContext::PhysicalDeviceInfo* info,
VulkanContext::Extension extension)
{
LogExtension(*info, extension, false, true);
info->extensions[extension] = false;
}
bool VulkanContext::SelectDeviceExtensions(bool enable_surface)
{
u32 extension_count = 0;
VkResult res =
vkEnumerateDeviceExtensionProperties(m_physical_device, nullptr, &extension_count, nullptr);
if (res != VK_SUCCESS)
if (enable_surface)
{
LOG_VULKAN_ERROR(res, "vkEnumerateDeviceExtensionProperties failed: ");
return false;
if (!RequiredExtension(m_device_info, Extension::KHR_swapchain))
return false;
}
if (extension_count == 0)
else
{
ERROR_LOG_FMT(VIDEO, "Vulkan: No extensions supported by device.");
return false;
m_device_info.extensions[Extension::KHR_swapchain] = false;
}
std::vector<VkExtensionProperties> available_extension_list(extension_count);
res = vkEnumerateDeviceExtensionProperties(m_physical_device, nullptr, &extension_count,
available_extension_list.data());
ASSERT(res == VK_SUCCESS);
for (const auto& extension_properties : available_extension_list)
INFO_LOG_FMT(VIDEO, "Available extension: {}", extension_properties.extensionName);
auto AddExtension = [&](const char* name, bool required) {
if (Common::Contains(available_extension_list, std::string_view{name},
&VkExtensionProperties::extensionName))
{
INFO_LOG_FMT(VIDEO, "Enabling extension: {}", name);
m_device_extensions.push_back(name);
return true;
}
if (required)
ERROR_LOG_FMT(VIDEO, "Vulkan: Missing required extension {}.", name);
return false;
};
if (enable_surface && !AddExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true))
return false;
#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN
// VK_EXT_full_screen_exclusive
if (AddExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME, true))
if (OptionalExtension(m_device_info, Extension::EXT_full_screen_exclusive))
INFO_LOG_FMT(VIDEO, "Using VK_EXT_full_screen_exclusive for exclusive fullscreen.");
#endif
AddExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false);
AddExtension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, false);
if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DEPTH_CLAMP_CONTROL))
OptionalExtension(m_device_info, Extension::KHR_get_physical_device_properties2);
OptionalExtension(m_device_info, Extension::EXT_memory_budget);
if (g_backend_info.bSupportsUnrestrictedDepthRange)
{
// Unrestricted depth range is one of the few extensions that changes the behavior
// of Vulkan just by being enabled, so we rely on lazy evaluation to ensure it is
// not enabled unless depth clamp control is supported.
g_backend_info.bSupportsUnrestrictedDepthRange =
AddExtension(VK_EXT_DEPTH_CLAMP_CONTROL_EXTENSION_NAME, false) &&
AddExtension(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, false);
OptionalExtension(m_device_info, Extension::EXT_depth_clamp_control);
OptionalExtension(m_device_info, Extension::EXT_depth_range_unrestricted);
}
else
{
IgnoreExtension(&m_device_info, Extension::EXT_depth_clamp_control);
IgnoreExtension(&m_device_info, Extension::EXT_depth_range_unrestricted);
}
return true;
}
@ -807,8 +874,9 @@ bool VulkanContext::CreateDevice(VkSurfaceKHR surface, bool enable_validation_la
// convert std::string list to a char pointer list which we can feed in
std::vector<const char*> extension_name_pointers;
for (const std::string& name : m_device_extensions)
extension_name_pointers.push_back(name.c_str());
for (size_t i = 0; i < m_device_info.extensions.size(); i++)
if (m_device_info.extensions[static_cast<Extension>(i)])
extension_name_pointers.push_back(ExtensionName(static_cast<Extension>(i)));
device_info.enabledLayerCount = 0;
device_info.ppEnabledLayerNames = nullptr;
@ -865,7 +933,7 @@ bool VulkanContext::CreateAllocator(u32 vk_api_version)
allocator_info.vulkanApiVersion = vk_api_version;
allocator_info.pTypeExternalMemoryHandleTypes = nullptr;
if (SupportsDeviceExtension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME))
if (m_device_info.extensions[Extension::EXT_memory_budget])
allocator_info.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
VkResult res = vmaCreateAllocator(&allocator_info, &m_allocator);
@ -942,12 +1010,6 @@ void VulkanContext::DisableDebugUtils()
}
}
bool VulkanContext::SupportsDeviceExtension(const char* name) const
{
return std::ranges::any_of(m_device_extensions,
[name](const std::string& extension) { return extension == name; });
}
static bool DriverIsMesa(VkDriverId driver_id)
{
switch (driver_id)
@ -1084,7 +1146,7 @@ bool VulkanContext::SupportsExclusiveFullscreen(const WindowSystemInfo& wsi, VkS
{
#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN
if (!surface || !vkGetPhysicalDeviceSurfaceCapabilities2KHR ||
!SupportsDeviceExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME))
!m_device_info.extensions[Extension::EXT_full_screen_exclusive])
{
return false;
}

View File

@ -18,6 +18,18 @@ namespace Vulkan
class VulkanContext
{
public:
enum class Extension : uint32_t
{
KHR_get_physical_device_properties2,
KHR_swapchain,
#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN
EXT_full_screen_exclusive,
#endif
EXT_memory_budget,
EXT_depth_clamp_control,
EXT_depth_range_unrestricted,
Last = EXT_depth_range_unrestricted
};
struct PhysicalDeviceInfo
{
PhysicalDeviceInfo(const PhysicalDeviceInfo&) = default;
@ -53,6 +65,7 @@ public:
bool depthClamp;
bool textureCompressionBC;
bool shaderSubgroupOperations = false;
Common::EnumMap<bool, Extension::Last> extensions = {};
};
struct DeviceFeatures
{
@ -122,9 +135,6 @@ public:
VkDeviceSize GetBufferImageGranularity() const { return m_device_info.bufferImageGranularity; }
float GetMaxSamplerAnisotropy() const { return m_device_info.maxSamplerAnisotropy; }
// Returns true if the specified extension is supported and enabled.
bool SupportsDeviceExtension(const char* name) const;
// Returns true if exclusive fullscreen is supported for the given surface.
bool SupportsExclusiveFullscreen(const WindowSystemInfo& wsi, VkSurfaceKHR surface);
@ -160,8 +170,6 @@ private:
VkDebugUtilsMessengerEXT m_debug_utils_messenger = VK_NULL_HANDLE;
PhysicalDeviceInfo m_device_info;
std::vector<std::string> m_device_extensions;
};
extern std::unique_ptr<VulkanContext> g_vulkan_context;