Merge pull request #55020 from bruvzg/vlk_device_surface_check

This commit is contained in:
Rémi Verschelde 2022-01-17 13:34:23 +01:00 committed by GitHub
commit d9a4ff7583
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 219 additions and 124 deletions

View file

@ -186,6 +186,10 @@ bool Engine::is_abort_on_gpu_errors_enabled() const {
return abort_on_gpu_errors;
}
int32_t Engine::get_gpu_index() const {
return gpu_idx;
}
bool Engine::is_validation_layers_enabled() const {
return use_validation_layers;
}

View file

@ -63,6 +63,7 @@ private:
double _physics_interpolation_fraction = 0.0f;
bool abort_on_gpu_errors = false;
bool use_validation_layers = false;
int32_t gpu_idx = -1;
uint64_t _process_frames = 0;
bool _in_physics = false;
@ -135,6 +136,7 @@ public:
bool is_abort_on_gpu_errors_enabled() const;
bool is_validation_layers_enabled() const;
int32_t get_gpu_index() const;
Engine();
virtual ~Engine() {}

View file

@ -615,7 +615,7 @@ Error VulkanContext::_check_capabilities() {
return OK;
}
Error VulkanContext::_create_physical_device() {
Error VulkanContext::_create_instance() {
/* obtain version */
_obtain_vulkan_version();
@ -678,8 +678,6 @@ Error VulkanContext::_create_physical_device() {
inst_info.pNext = &dbg_report_callback_create_info;
}
uint32_t gpu_count;
VkResult err = vkCreateInstance(&inst_info, nullptr, &inst);
ERR_FAIL_COND_V_MSG(err == VK_ERROR_INCOMPATIBLE_DRIVER, ERR_CANT_CREATE,
"Cannot find a compatible Vulkan installable client driver (ICD).\n\n"
@ -700,10 +698,85 @@ Error VulkanContext::_create_physical_device() {
volkLoadInstance(inst);
#endif
/* Make initial call to query gpu_count, then second call for gpu info*/
err = vkEnumeratePhysicalDevices(inst, &gpu_count, nullptr);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
if (enabled_debug_utils) {
// Setup VK_EXT_debug_utils function pointers always (we use them for
// debug labels and names).
CreateDebugUtilsMessengerEXT =
(PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugUtilsMessengerEXT");
DestroyDebugUtilsMessengerEXT =
(PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugUtilsMessengerEXT");
SubmitDebugUtilsMessageEXT =
(PFN_vkSubmitDebugUtilsMessageEXT)vkGetInstanceProcAddr(inst, "vkSubmitDebugUtilsMessageEXT");
CmdBeginDebugUtilsLabelEXT =
(PFN_vkCmdBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdBeginDebugUtilsLabelEXT");
CmdEndDebugUtilsLabelEXT =
(PFN_vkCmdEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdEndDebugUtilsLabelEXT");
CmdInsertDebugUtilsLabelEXT =
(PFN_vkCmdInsertDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdInsertDebugUtilsLabelEXT");
SetDebugUtilsObjectNameEXT =
(PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(inst, "vkSetDebugUtilsObjectNameEXT");
if (nullptr == CreateDebugUtilsMessengerEXT || nullptr == DestroyDebugUtilsMessengerEXT ||
nullptr == SubmitDebugUtilsMessageEXT || nullptr == CmdBeginDebugUtilsLabelEXT ||
nullptr == CmdEndDebugUtilsLabelEXT || nullptr == CmdInsertDebugUtilsLabelEXT ||
nullptr == SetDebugUtilsObjectNameEXT) {
ERR_FAIL_V_MSG(ERR_CANT_CREATE,
"GetProcAddr: Failed to init VK_EXT_debug_utils\n"
"GetProcAddr: Failure");
}
err = CreateDebugUtilsMessengerEXT(inst, &dbg_messenger_create_info, nullptr, &dbg_messenger);
switch (err) {
case VK_SUCCESS:
break;
case VK_ERROR_OUT_OF_HOST_MEMORY:
ERR_FAIL_V_MSG(ERR_CANT_CREATE,
"CreateDebugUtilsMessengerEXT: out of host memory\n"
"CreateDebugUtilsMessengerEXT Failure");
break;
default:
ERR_FAIL_V_MSG(ERR_CANT_CREATE,
"CreateDebugUtilsMessengerEXT: unknown failure\n"
"CreateDebugUtilsMessengerEXT Failure");
ERR_FAIL_V(ERR_CANT_CREATE);
break;
}
} else if (enabled_debug_report) {
CreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugReportCallbackEXT");
DebugReportMessageEXT = (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(inst, "vkDebugReportMessageEXT");
DestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugReportCallbackEXT");
if (nullptr == CreateDebugReportCallbackEXT || nullptr == DebugReportMessageEXT || nullptr == DestroyDebugReportCallbackEXT) {
ERR_FAIL_V_MSG(ERR_CANT_CREATE,
"GetProcAddr: Failed to init VK_EXT_debug_report\n"
"GetProcAddr: Failure");
}
err = CreateDebugReportCallbackEXT(inst, &dbg_report_callback_create_info, nullptr, &dbg_debug_report);
switch (err) {
case VK_SUCCESS:
break;
case VK_ERROR_OUT_OF_HOST_MEMORY:
ERR_FAIL_V_MSG(ERR_CANT_CREATE,
"CreateDebugReportCallbackEXT: out of host memory\n"
"CreateDebugReportCallbackEXT Failure");
break;
default:
ERR_FAIL_V_MSG(ERR_CANT_CREATE,
"CreateDebugReportCallbackEXT: unknown failure\n"
"CreateDebugReportCallbackEXT Failure");
ERR_FAIL_V(ERR_CANT_CREATE);
break;
}
}
return OK;
}
Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) {
/* Make initial call to query gpu_count, then second call for gpu info*/
uint32_t gpu_count = 0;
VkResult err = vkEnumeratePhysicalDevices(inst, &gpu_count, nullptr);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
ERR_FAIL_COND_V_MSG(gpu_count == 0, ERR_CANT_CREATE,
"vkEnumeratePhysicalDevices reported zero accessible devices.\n\n"
"Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
@ -716,36 +789,6 @@ Error VulkanContext::_create_physical_device() {
ERR_FAIL_V(ERR_CANT_CREATE);
}
// TODO: At least on Linux Laptops integrated GPUs fail with Vulkan in many instances.
// The device should really be a preference, but for now choosing a discrete GPU over the
// integrated one is better than the default.
// Default to first device
uint32_t device_index = 0;
for (uint32_t i = 0; i < gpu_count; ++i) {
VkPhysicalDeviceProperties props;
vkGetPhysicalDeviceProperties(physical_devices[i], &props);
if (props.deviceType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
// Prefer discrete GPU.
device_index = i;
break;
}
}
gpu = physical_devices[device_index];
free(physical_devices);
/* Look for device extensions */
uint32_t device_extension_count = 0;
VkBool32 swapchainExtFound = 0;
enabled_extension_count = 0;
memset(extension_names, 0, sizeof(extension_names));
/* Get identifier properties */
vkGetPhysicalDeviceProperties(gpu, &gpu_props);
static const struct {
uint32_t id;
const char *name;
@ -759,6 +802,119 @@ Error VulkanContext::_create_physical_device() {
{ 0x8086, "Intel" },
{ 0, nullptr },
};
// TODO: At least on Linux Laptops integrated GPUs fail with Vulkan in many instances.
// The device should really be a preference, but for now choosing a discrete GPU over the
// integrated one is better than the default.
int32_t device_index = -1;
int type_selected = -1;
print_verbose("Vulkan devices:");
for (uint32_t i = 0; i < gpu_count; ++i) {
VkPhysicalDeviceProperties props;
vkGetPhysicalDeviceProperties(physical_devices[i], &props);
bool present_supported = false;
uint32_t device_queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, nullptr);
VkQueueFamilyProperties *device_queue_props = (VkQueueFamilyProperties *)malloc(device_queue_family_count * sizeof(VkQueueFamilyProperties));
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, device_queue_props);
for (uint32_t j = 0; j < device_queue_family_count; j++) {
VkBool32 supports;
vkGetPhysicalDeviceSurfaceSupportKHR(physical_devices[i], j, p_surface, &supports);
if (supports && ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)) {
present_supported = true;
} else {
continue;
}
}
String name = props.deviceName;
String vendor = "Unknown";
String dev_type;
switch (props.deviceType) {
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: {
dev_type = "Discrete";
} break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: {
dev_type = "Integrated";
} break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: {
dev_type = "Virtual";
} break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: {
dev_type = "CPU";
} break;
default: {
dev_type = "Other";
} break;
}
uint32_t vendor_idx = 0;
while (vendor_names[vendor_idx].name != nullptr) {
if (props.vendorID == vendor_names[vendor_idx].id) {
vendor = vendor_names[vendor_idx].name;
break;
}
vendor_idx++;
}
free(device_queue_props);
print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported") + ", " + dev_type);
if (present_supported) { // Select first supported device of preffered type: Discrete > Integrated > Virtual > CPU > Other.
switch (props.deviceType) {
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: {
if (type_selected < 4) {
type_selected = 4;
device_index = i;
}
} break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: {
if (type_selected < 3) {
type_selected = 3;
device_index = i;
}
} break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: {
if (type_selected < 2) {
type_selected = 2;
device_index = i;
}
} break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: {
if (type_selected < 1) {
type_selected = 1;
device_index = i;
}
} break;
default: {
if (type_selected < 0) {
type_selected = 0;
device_index = i;
}
} break;
}
}
}
int32_t user_device_index = Engine::get_singleton()->get_gpu_index(); // Force user selected GPU.
if (user_device_index >= 0 && user_device_index < (int32_t)gpu_count) {
device_index = user_device_index;
}
ERR_FAIL_COND_V_MSG(device_index == -1, ERR_CANT_CREATE, "None of Vulkan devices supports both graphics and present queues.");
gpu = physical_devices[device_index];
free(physical_devices);
/* Look for device extensions */
uint32_t device_extension_count = 0;
VkBool32 swapchainExtFound = 0;
enabled_extension_count = 0;
memset(extension_names, 0, sizeof(extension_names));
/* Get identifier properties */
vkGetPhysicalDeviceProperties(gpu, &gpu_props);
device_name = gpu_props.deviceName;
device_type = gpu_props.deviceType;
pipeline_cache_id = String::hex_encode_buffer(gpu_props.pipelineCacheUUID, VK_UUID_SIZE);
@ -851,77 +1007,6 @@ Error VulkanContext::_create_physical_device() {
" extension.\n\nDo you have a compatible Vulkan installable client driver (ICD) installed?\n"
"vkCreateInstance Failure");
if (enabled_debug_utils) {
// Setup VK_EXT_debug_utils function pointers always (we use them for
// debug labels and names).
CreateDebugUtilsMessengerEXT =
(PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugUtilsMessengerEXT");
DestroyDebugUtilsMessengerEXT =
(PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugUtilsMessengerEXT");
SubmitDebugUtilsMessageEXT =
(PFN_vkSubmitDebugUtilsMessageEXT)vkGetInstanceProcAddr(inst, "vkSubmitDebugUtilsMessageEXT");
CmdBeginDebugUtilsLabelEXT =
(PFN_vkCmdBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdBeginDebugUtilsLabelEXT");
CmdEndDebugUtilsLabelEXT =
(PFN_vkCmdEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdEndDebugUtilsLabelEXT");
CmdInsertDebugUtilsLabelEXT =
(PFN_vkCmdInsertDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdInsertDebugUtilsLabelEXT");
SetDebugUtilsObjectNameEXT =
(PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(inst, "vkSetDebugUtilsObjectNameEXT");
if (nullptr == CreateDebugUtilsMessengerEXT || nullptr == DestroyDebugUtilsMessengerEXT ||
nullptr == SubmitDebugUtilsMessageEXT || nullptr == CmdBeginDebugUtilsLabelEXT ||
nullptr == CmdEndDebugUtilsLabelEXT || nullptr == CmdInsertDebugUtilsLabelEXT ||
nullptr == SetDebugUtilsObjectNameEXT) {
ERR_FAIL_V_MSG(ERR_CANT_CREATE,
"GetProcAddr: Failed to init VK_EXT_debug_utils\n"
"GetProcAddr: Failure");
}
err = CreateDebugUtilsMessengerEXT(inst, &dbg_messenger_create_info, nullptr, &dbg_messenger);
switch (err) {
case VK_SUCCESS:
break;
case VK_ERROR_OUT_OF_HOST_MEMORY:
ERR_FAIL_V_MSG(ERR_CANT_CREATE,
"CreateDebugUtilsMessengerEXT: out of host memory\n"
"CreateDebugUtilsMessengerEXT Failure");
break;
default:
ERR_FAIL_V_MSG(ERR_CANT_CREATE,
"CreateDebugUtilsMessengerEXT: unknown failure\n"
"CreateDebugUtilsMessengerEXT Failure");
ERR_FAIL_V(ERR_CANT_CREATE);
break;
}
} else if (enabled_debug_report) {
CreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugReportCallbackEXT");
DebugReportMessageEXT = (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(inst, "vkDebugReportMessageEXT");
DestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugReportCallbackEXT");
if (nullptr == CreateDebugReportCallbackEXT || nullptr == DebugReportMessageEXT || nullptr == DestroyDebugReportCallbackEXT) {
ERR_FAIL_V_MSG(ERR_CANT_CREATE,
"GetProcAddr: Failed to init VK_EXT_debug_report\n"
"GetProcAddr: Failure");
}
err = CreateDebugReportCallbackEXT(inst, &dbg_report_callback_create_info, nullptr, &dbg_debug_report);
switch (err) {
case VK_SUCCESS:
break;
case VK_ERROR_OUT_OF_HOST_MEMORY:
ERR_FAIL_V_MSG(ERR_CANT_CREATE,
"CreateDebugReportCallbackEXT: out of host memory\n"
"CreateDebugReportCallbackEXT Failure");
break;
default:
ERR_FAIL_V_MSG(ERR_CANT_CREATE,
"CreateDebugReportCallbackEXT: unknown failure\n"
"CreateDebugReportCallbackEXT Failure");
ERR_FAIL_V(ERR_CANT_CREATE);
break;
}
}
/* Call with nullptr data to get count */
vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_family_count, nullptr);
ERR_FAIL_COND_V(queue_family_count == 0, ERR_CANT_CREATE);
@ -957,6 +1042,7 @@ Error VulkanContext::_create_physical_device() {
}
}
device_initialized = true;
return OK;
}
@ -1209,25 +1295,17 @@ bool VulkanContext::_use_validation_layers() {
Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, VkSurfaceKHR p_surface, int p_width, int p_height) {
ERR_FAIL_COND_V(windows.has(p_window_id), ERR_INVALID_PARAMETER);
if (!device_initialized) {
Error err = _create_physical_device(p_surface);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
}
if (!queues_initialized) {
// We use a single GPU, but we need a surface to initialize the
// queues, so this process must be deferred until a surface
// is created.
Error err = _initialize_queues(p_surface);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
} else {
// make sure any of the surfaces supports present (validation layer complains if this is not done).
bool any_supports_present = false;
for (uint32_t i = 0; i < queue_family_count; i++) {
VkBool32 supports;
fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, p_surface, &supports);
if (supports) {
any_supports_present = true;
break;
}
}
ERR_FAIL_COND_V_MSG(!any_supports_present, ERR_CANT_CREATE, "Surface passed for sub-window creation does not support presenting");
}
Window window;
@ -1694,12 +1772,12 @@ Error VulkanContext::initialize() {
return FAILED;
}
#endif
Error err = _create_physical_device();
if (err) {
Error err = _create_instance();
if (err != OK) {
return err;
}
device_initialized = true;
return OK;
}

View file

@ -224,7 +224,9 @@ private:
const char *pMessage,
void *pUserData);
Error _create_physical_device();
Error _create_instance();
Error _create_physical_device(VkSurfaceKHR p_surface);
Error _initialize_queues(VkSurfaceKHR p_surface);

View file

@ -325,6 +325,7 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print("].\n");
OS::get_singleton()->print(" --rendering-driver <driver> Rendering driver (depends on display driver).\n");
OS::get_singleton()->print(" --gpu-index <device_index> Use a specific GPU (run with --verbose to get available device list).\n");
OS::get_singleton()->print(" --text-driver <driver> Text driver (Fonts, BiDi, shaping)\n");
@ -793,6 +794,14 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
} else if (I->get() == "-w" || I->get() == "--windowed") { // force windowed window
init_windowed = true;
} else if (I->get() == "--gpu-index") {
if (I->next()) {
Engine::singleton->gpu_idx = I->next()->get().to_int();
N = I->next()->next();
} else {
OS::get_singleton()->print("Missing gpu index argument, aborting.\n");
goto error;
}
} else if (I->get() == "--vk-layers") {
Engine::singleton->use_validation_layers = true;
#ifdef DEBUG_ENABLED