blob: bdffbac79804c5accbfef0b4f093d8c4b2912965 [file] [log] [blame]
/*
* Copyright 2024 Rive
*/
#include "rive_vk_bootstrap/rive_vk_bootstrap.hpp"
#ifdef __APPLE__
#include <dlfcn.h>
#endif
namespace rive_vkb
{
vkb::SystemInfo load_vulkan()
{
PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr = nullptr;
#ifdef __APPLE__
// The Vulkan SDK on Mac gets installed to /usr/local/lib, which is no longer
// on the library search path after Sonoma.
if (void* vulkanLib = dlopen("/usr/local/lib/libvulkan.1.dylib", RTLD_NOW | RTLD_LOCAL))
{
fp_vkGetInstanceProcAddr =
reinterpret_cast<PFN_vkGetInstanceProcAddr>(dlsym(vulkanLib, "vkGetInstanceProcAddr"));
}
#endif
return VKB_CHECK(vkb::SystemInfo::get_system_info(fp_vkGetInstanceProcAddr));
}
VKAPI_ATTR VkBool32 VKAPI_CALL
default_debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData)
{
switch (messageType)
{
case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT:
if (pCallbackData->messageIdNumber == 0 &&
strcmp(pCallbackData->pMessageIdName, "Loader Message") == 0 &&
strcmp(pCallbackData->pMessage, "Copying old device 0 into new device 0") == 0)
{
// Swiftshader generates this error during
// vkEnumeratePhysicalDevices. It seems fine to ignore.
break;
}
fprintf(stderr,
"PLS Vulkan error: %i: %s: %s\n",
pCallbackData->messageIdNumber,
pCallbackData->pMessageIdName,
pCallbackData->pMessage);
abort();
case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT:
fprintf(stderr,
"PLS Vulkan Validation error: %i: %s: %s\n",
pCallbackData->messageIdNumber,
pCallbackData->pMessageIdName,
pCallbackData->pMessage);
abort();
case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT:
fprintf(stderr,
"PLS Vulkan Performance warning: %i: %s: %s\n",
pCallbackData->messageIdNumber,
pCallbackData->pMessageIdName,
pCallbackData->pMessage);
break;
}
return VK_FALSE;
}
static const char* physical_device_type_name(VkPhysicalDeviceType type)
{
switch (type)
{
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
return "Integrated";
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
return "Discrete";
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
return "Virtual";
case VK_PHYSICAL_DEVICE_TYPE_CPU:
return "CPU";
default:
return "Other";
}
}
// Select a GPU name if it contains the substring 'filter' or '$RIVE_GPU'.
// Return false if 'filter' and '$RIVE_GPU' are both null.
// Abort if the filter matches more than one name.
std::tuple<vkb::PhysicalDevice, rive::pls::VulkanFeatures> select_physical_device(
vkb::PhysicalDeviceSelector& selector,
const char* gpuNameFilter)
{
if (const char* rive_gpu = getenv("RIVE_GPU"))
{
gpuNameFilter = rive_gpu;
}
if (gpuNameFilter == nullptr)
{
// No active filter. Go with a discrete GPU.
selector.allow_any_gpu_device_type(false).prefer_gpu_device_type(
vkb::PreferredDeviceType::discrete);
}
else
{
std::vector<std::string> names = VKB_CHECK(selector.select_device_names());
std::vector<std::string> matches;
for (const std::string& name : names)
{
if (strstr(name.c_str(), gpuNameFilter) != nullptr)
{
matches.push_back(name);
}
}
if (matches.size() != 1)
{
const char* filterName = gpuNameFilter != nullptr ? gpuNameFilter : "<discrete_gpu>";
const std::vector<std::string>* devicePrintList;
if (matches.size() > 1)
{
fprintf(stderr,
"Cannot select GPU\nToo many matches for filter "
"'%s'.\nMatches:\n",
filterName);
devicePrintList = &matches;
}
else
{
fprintf(stderr,
"Cannot select GPU.\nNo matches for filter "
"'%s'.\nAvailable GPUs:\n",
filterName);
devicePrintList = &names;
}
for (const std::string& name : *devicePrintList)
{
fprintf(stderr, " %s\n", name.c_str());
}
fprintf(stderr, "\nPlease update the $RIVE_GPU environment variable\n");
abort();
}
selector.set_name(matches[0]);
}
auto selectResult = selector.set_minimum_version(1, 0).allow_any_gpu_device_type(false).select(
vkb::DeviceSelectionMode::only_fully_suitable);
if (!selectResult)
{
selectResult = selector.allow_any_gpu_device_type(true).select(
vkb::DeviceSelectionMode::partially_and_fully_suitable);
}
auto physicalDevice = VKB_CHECK(selectResult);
printf("==== Vulkan GPU (%s): %s ====\n",
rive_vkb::physical_device_type_name(physicalDevice.properties.deviceType),
physicalDevice.properties.deviceName);
rive::pls::VulkanFeatures plsVulkanFeatures;
physicalDevice.enable_features_if_present({
.independentBlend = VK_TRUE,
.fillModeNonSolid = VK_TRUE,
.fragmentStoresAndAtomics = VK_TRUE,
});
printf("PLS Vulkan features: [");
if (physicalDevice.features.independentBlend)
{
plsVulkanFeatures.independentBlend = true;
printf(" independentBlend");
}
if (physicalDevice.features.fillModeNonSolid)
{
plsVulkanFeatures.fillModeNonSolid = true;
printf(" fillModeNonSolid");
}
if (physicalDevice.features.fragmentStoresAndAtomics)
{
plsVulkanFeatures.fragmentStoresAndAtomics = true;
printf(" fragmentStoresAndAtomics");
}
if (physicalDevice.enable_extension_if_present(
VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME) ||
physicalDevice.enable_extension_if_present("VK_AMD_rasterization_order_attachment_access"))
{
constexpr static VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT
rasterOrderFeatures = {
.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT,
.rasterizationOrderColorAttachmentAccess = VK_TRUE,
};
if (physicalDevice.enable_extension_features_if_present(rasterOrderFeatures))
{
plsVulkanFeatures.rasterizationOrderColorAttachmentAccess = true;
printf(" rasterizationOrderColorAttachmentAccess");
}
}
printf(" ]\n");
return {physicalDevice, plsVulkanFeatures};
}
} // namespace rive_vkb