Browse Source

Merge pull request #55020 from bruvzg/vlk_device_surface_check

Rémi Verschelde 3 years ago
parent
commit
d9a4ff7583
5 changed files with 208 additions and 113 deletions
  1. 4 0
      core/config/engine.cpp
  2. 2 0
      core/config/engine.h
  3. 190 112
      drivers/vulkan/vulkan_context.cpp
  4. 3 1
      drivers/vulkan/vulkan_context.h
  5. 9 0
      main/main.cpp

+ 4 - 0
core/config/engine.cpp

@@ -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;
 }

+ 2 - 0
core/config/engine.h

@@ -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() {}

+ 190 - 112
drivers/vulkan/vulkan_context.cpp

@@ -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
 
+	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*/
-	err = vkEnumeratePhysicalDevices(inst, &gpu_count, nullptr);
+	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,24 +789,120 @@ Error VulkanContext::_create_physical_device() {
 		ERR_FAIL_V(ERR_CANT_CREATE);
 	}
 
+	static const struct {
+		uint32_t id;
+		const char *name;
+	} vendor_names[] = {
+		{ 0x1002, "AMD" },
+		{ 0x1010, "ImgTec" },
+		{ 0x106B, "Apple" },
+		{ 0x10DE, "NVIDIA" },
+		{ 0x13B5, "ARM" },
+		{ 0x5143, "Qualcomm" },
+		{ 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.
 
-	// Default to first device
-	uint32_t device_index = 0;
-
+	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);
 
-		if (props.deviceType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
-			// Prefer discrete GPU.
-			device_index = i;
-			break;
+		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);
 
@@ -746,19 +915,6 @@ Error VulkanContext::_create_physical_device() {
 	/* Get identifier properties */
 	vkGetPhysicalDeviceProperties(gpu, &gpu_props);
 
-	static const struct {
-		uint32_t id;
-		const char *name;
-	} vendor_names[] = {
-		{ 0x1002, "AMD" },
-		{ 0x1010, "ImgTec" },
-		{ 0x106B, "Apple" },
-		{ 0x10DE, "NVIDIA" },
-		{ 0x13B5, "ARM" },
-		{ 0x5143, "Qualcomm" },
-		{ 0x8086, "Intel" },
-		{ 0, nullptr },
-	};
 	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;
 }
 

+ 3 - 1
drivers/vulkan/vulkan_context.h

@@ -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);
 

+ 9 - 0
main/main.cpp

@@ -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