Improve Vulkan initialization;

- Check for layers before enabling
- Check for instance/device extensions before enabling

Fixes unfriendly errors when running on a system without validation layers
installed.

Uses same table approach as OpenXR code.
This commit is contained in:
bjorn 2022-11-16 20:21:03 -08:00
parent 7b3e1a02eb
commit 3879ce926d
1 changed files with 101 additions and 35 deletions

View File

@ -210,6 +210,8 @@ static gpu_memory* gpu_allocate(gpu_memory_type type, VkMemoryRequirements info,
static void gpu_release(gpu_memory* memory);
static void condemn(void* handle, VkObjectType type);
static void expunge(void);
static bool hasLayer(VkLayerProperties* layers, uint32_t count, const char* layer);
static bool hasExtension(VkExtensionProperties* extensions, uint32_t count, const char* extension);
static void createSwapchain(uint32_t width, uint32_t height);
static VkRenderPass getCachedRenderPass(gpu_pass_info* pass, bool exact);
static VkFramebuffer getCachedFramebuffer(VkRenderPass pass, VkImageView images[9], uint32_t imageCount, uint32_t size[2]);
@ -226,6 +228,7 @@ static bool check(bool condition, const char* message);
// Functions that don't require an instance
#define GPU_FOREACH_ANONYMOUS(X)\
X(vkEnumerateInstanceLayerProperties)\
X(vkEnumerateInstanceExtensionProperties)\
X(vkCreateInstance)
@ -1840,51 +1843,86 @@ bool gpu_init(gpu_config* config) {
GPU_FOREACH_ANONYMOUS(GPU_LOAD_ANONYMOUS);
{ // Instance
const char* extensions[32];
uint32_t extensionCount = 0;
struct {
bool validation;
bool portability;
bool debug;
} supports = { 0 };
if (state.config.vk.getInstanceExtensions) {
const char** instanceExtensions = state.config.vk.getInstanceExtensions(&extensionCount);
CHECK(extensionCount < COUNTOF(extensions), "Too many instance extensions") return gpu_destroy(), false;
for (uint32_t i = 0; i < extensionCount; i++) {
extensions[i] = instanceExtensions[i];
// Layers
struct { const char* name; bool shouldEnable; bool* isEnabled; } layers[] = {
{ "VK_LAYER_KHRONOS_validation", config->debug, &supports.validation }
};
const char* enabledLayers[1];
uint32_t enabledLayerCount = 0;
VkLayerProperties layerInfo[32];
uint32_t count = COUNTOF(layerInfo);
VK(vkEnumerateInstanceLayerProperties(&count, layerInfo), "Failed to enumerate instance layers") return gpu_destroy(), false;
for (uint32_t i = 0; i < COUNTOF(layers); i++) {
if (!layers[i].shouldEnable) continue;
if (hasLayer(layerInfo, count, layers[i].name)) {
CHECK(enabledLayerCount < COUNTOF(enabledLayers), "Too many layers") return gpu_destroy(), false;
if (layers[i].isEnabled) *layers[i].isEnabled = true;
enabledLayers[enabledLayerCount++] = layers[i].name;
}
}
if (state.config.debug) {
CHECK(extensionCount < COUNTOF(extensions), "Too many instance extensions") return gpu_destroy(), false;
extensions[extensionCount++] = "VK_EXT_debug_utils";
// Extensions
struct { const char* name; bool shouldEnable; bool* isEnabled; } extensions[] = {
{ "VK_KHR_portability_enumeration", true, &supports.portability },
{ "VK_EXT_debug_utils", config->debug, &supports.debug }
};
const char* enabledExtensions[32];
uint32_t enabledExtensionCount = 0;
if (state.config.vk.getInstanceExtensions) {
const char** instanceExtensions = state.config.vk.getInstanceExtensions(&enabledExtensionCount);
CHECK(enabledExtensionCount < COUNTOF(enabledExtensions), "Too many instance extensions") return gpu_destroy(), false;
for (uint32_t i = 0; i < enabledExtensionCount; i++) {
enabledExtensions[i] = instanceExtensions[i];
}
}
VkExtensionProperties extensionInfo[256];
uint32_t count = COUNTOF(extensionInfo);
count = COUNTOF(extensionInfo);
VK(vkEnumerateInstanceExtensionProperties(NULL, &count, extensionInfo), "Failed to enumerate instance extensions") return gpu_destroy(), false;
VkInstanceCreateFlags instanceFlags = 0;
#ifdef VK_KHR_portability_enumeration
for (uint32_t i = 0; i < count; i++) {
if (!strcmp(extensionInfo[i].extensionName, "VK_KHR_portability_enumeration")) {
CHECK(extensionCount < COUNTOF(extensions), "Too many instance extensions") return gpu_destroy(), false;
extensions[extensionCount++] = "VK_KHR_portability_enumeration";
instanceFlags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
for (uint32_t i = 0; i < COUNTOF(extensions); i++) {
if (!extensions[i].shouldEnable) continue;
if (hasExtension(extensionInfo, count, extensions[i].name)) {
CHECK(enabledExtensionCount < COUNTOF(enabledExtensions), "Too many instance extensions") return gpu_destroy(), false;
if (extensions[i].isEnabled) *extensions[i].isEnabled = true;
enabledExtensions[enabledExtensionCount++] = extensions[i].name;
}
}
#endif
if (state.config.debug && !supports.validation && state.config.callback) {
state.config.callback(state.config.userdata, "Warning: GPU debug mode is enabled, but validation layer is not supported", false);
}
if (state.config.debug && !supports.debug) {
state.config.debug = false;
}
VkInstanceCreateInfo instanceInfo = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.flags = instanceFlags,
.flags = supports.portability ? VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR : 0,
.pApplicationInfo = &(VkApplicationInfo) {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pEngineName = config->engineName,
.engineVersion = VK_MAKE_VERSION(config->engineVersion[0], config->engineVersion[1], config->engineVersion[2]),
.apiVersion = VK_MAKE_VERSION(1, 1, 0)
},
.enabledLayerCount = state.config.debug ? 1 : 0,
.ppEnabledLayerNames = (const char*[]) { "VK_LAYER_KHRONOS_validation" },
.enabledExtensionCount = extensionCount,
.ppEnabledExtensionNames = extensions
.enabledLayerCount = enabledLayerCount,
.ppEnabledLayerNames = enabledLayers,
.enabledExtensionCount = enabledExtensionCount,
.ppEnabledExtensionNames = enabledExtensions
};
if (state.config.vk.createInstance) {
@ -2056,23 +2094,33 @@ bool gpu_init(gpu_config* config) {
}
CHECK(state.queueFamilyIndex != ~0u, "Queue selection failed") return gpu_destroy(), false;
const char* extensions[4];
uint32_t extensionCount = 0;
struct {
bool swapchain;
} supports = { 0 };
if (state.surface) {
extensions[extensionCount++] = "VK_KHR_swapchain";
}
struct { const char* name; bool shouldEnable; bool* isEnabled; } extensions[] = {
{ "VK_KHR_swapchain", state.surface, &supports.swapchain },
{ "VK_KHR_portability_subset", true, NULL }
};
const char* enabledExtensions[4];
uint32_t enabledExtensionCount = 0;
VkExtensionProperties extensionInfo[256];
uint32_t count = COUNTOF(extensionInfo);
VK(vkEnumerateDeviceExtensionProperties(state.adapter, NULL, &count, extensionInfo), "Failed to enumerate device extensions") return gpu_destroy(), false;
for (uint32_t i = 0; i < count; i++) {
if (!strcmp(extensionInfo[i].extensionName, "VK_KHR_portability_subset")) {
extensions[extensionCount++] = "VK_KHR_portability_subset";
for (uint32_t i = 0; i < COUNTOF(extensions); i++) {
if (!extensions[i].shouldEnable) continue;
if (hasExtension(extensionInfo, count, extensions[i].name)) {
CHECK(enabledExtensionCount < COUNTOF(enabledExtensions), "Too many device extensions") return gpu_destroy(), false;
if (extensions[i].isEnabled) *extensions[i].isEnabled = true;
enabledExtensions[enabledExtensionCount++] = extensions[i].name;
}
}
CHECK(supports.swapchain || !state.surface, "Swapchain extension not supported");
VkDeviceCreateInfo deviceInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = config->features ? &enabledFeatures : NULL,
@ -2083,8 +2131,8 @@ bool gpu_init(gpu_config* config) {
.pQueuePriorities = &(float) { 1.f },
.queueCount = 1
},
.enabledExtensionCount = extensionCount,
.ppEnabledExtensionNames = extensions
.enabledExtensionCount = enabledExtensionCount,
.ppEnabledExtensionNames = enabledExtensions
};
if (state.config.vk.createDevice) {
@ -2586,6 +2634,24 @@ static void expunge() {
}
}
static bool hasLayer(VkLayerProperties* layers, uint32_t count, const char* layer) {
for (uint32_t i = 0; i < count; i++) {
if (!strcmp(layers[i].layerName, layer)) {
return true;
}
}
return false;
}
static bool hasExtension(VkExtensionProperties* extensions, uint32_t count, const char* extension) {
for (uint32_t i = 0; i < count; i++) {
if (!strcmp(extensions[i].extensionName, extension)) {
return true;
}
}
return false;
}
static void createSwapchain(uint32_t width, uint32_t height) {
if (width == 0 || height == 0) {
state.swapchainValid = false;