vkQuake2 doxygen  1.0 dev
vk_device.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 2018-2019 Krzysztof Kondrak
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 
19 */
20 
21 #include "vk_local.h"
22 
23 // internal helper
24 static qboolean deviceExtensionsSupported(const VkPhysicalDevice *physicalDevice)
25 {
26  uint32_t availableExtCount = 0;
27  qboolean vk_khr_swapchain_extension_available = false;
28  VK_VERIFY(vkEnumerateDeviceExtensionProperties(*physicalDevice, NULL, &availableExtCount, NULL));
29 
30  if (availableExtCount > 0)
31  {
32  VkExtensionProperties *extensions = (VkExtensionProperties *)malloc(availableExtCount * sizeof(VkExtensionProperties));
33  VK_VERIFY(vkEnumerateDeviceExtensionProperties(*physicalDevice, NULL, &availableExtCount, extensions));
34 
35  for (uint32_t i = 0; i < availableExtCount; ++i)
36  {
37  vk_khr_swapchain_extension_available |= strcmp(extensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0;
38  }
39 
40  free(extensions);
41  }
42 
43  // lack of swapchain extension disqualifies the device
44  return vk_khr_swapchain_extension_available;
45 }
46 
47 // internal helper
48 static void getBestPhysicalDevice(const VkPhysicalDevice *devices, int preferredIdx, int count)
49 {
50  VkPhysicalDeviceProperties deviceProperties;
51  VkPhysicalDeviceFeatures deviceFeatures;
52  uint32_t queueFamilyCount = 0;
53 
54  for (int i = 0; i < count; ++i)
55  {
56  vkGetPhysicalDeviceProperties(devices[i], &deviceProperties);
57  vkGetPhysicalDeviceFeatures(devices[i], &deviceFeatures);
58  vkGetPhysicalDeviceQueueFamilyProperties(devices[i], &queueFamilyCount, NULL);
59 
60  if (queueFamilyCount == 0)
61  continue;
62 
63  // prefer discrete GPU but if it's the only one available then don't be picky
64  // also - if the user specifies a preferred device, select it
65  qboolean bestProperties = deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;
66  if (preferredIdx == i || (bestProperties && preferredIdx < 0) || count == 1)
67  {
68  uint32_t formatCount = 0;
69  uint32_t presentModesCount = 0;
70 
71  // check if requested device extensions are present
72  qboolean extSupported = deviceExtensionsSupported(&devices[i]);
73 
74  // no required extensions? try next device
75  if (!extSupported || !deviceFeatures.samplerAnisotropy || !deviceFeatures.fillModeNonSolid)
76  continue;
77 
78  // if extensions are fine, query surface formats and present modes to see if the device can be used
79  VK_VERIFY(vkGetPhysicalDeviceSurfaceFormatsKHR(devices[i], vk_surface, &formatCount, NULL));
80  VK_VERIFY(vkGetPhysicalDeviceSurfacePresentModesKHR(devices[i], vk_surface, &presentModesCount, NULL));
81 
82  if (formatCount == 0 || presentModesCount == 0)
83  continue;
84 
85  VkQueueFamilyProperties *queueFamilies = (VkQueueFamilyProperties *)malloc(queueFamilyCount * sizeof(VkQueueFamilyProperties));
86  vkGetPhysicalDeviceQueueFamilyProperties(devices[i], &queueFamilyCount, queueFamilies);
87 
88  // secondary check - device is OK if there's at least on queue with VK_QUEUE_GRAPHICS_BIT set
89  for (uint32_t j = 0; j < queueFamilyCount; ++j)
90  {
91  // check if this queue family has support for presentation
92  VkBool32 presentSupported;
93  VK_VERIFY(vkGetPhysicalDeviceSurfaceSupportKHR(devices[i], j, vk_surface, &presentSupported));
94 
95  // good optimization would be to find a queue where presentIdx == gfxQueueIdx for less overhead
96  if (vk_device.presentFamilyIndex < 0 && queueFamilies[j].queueCount > 0 && presentSupported)
97  {
99  }
100 
101  if (vk_device.gfxFamilyIndex < 0 && queueFamilies[j].queueCount > 0 && (queueFamilies[j].queueFlags & VK_QUEUE_GRAPHICS_BIT))
102  {
104  }
105 
106  if (vk_device.transferFamilyIndex < 0 && queueFamilies[j].queueCount > 0 && !(queueFamilies[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) && (queueFamilies[j].queueFlags & VK_QUEUE_TRANSFER_BIT))
107  {
109  }
110  }
111 
112  free(queueFamilies);
113 
114  // accept only device that has support for presentation and drawing
116  {
118  {
120  }
121 
122  vk_device.physical = devices[i];
123  vk_device.properties = deviceProperties;
124  vk_device.features = deviceFeatures;
125  return;
126  }
127  }
128  }
129 }
130 
131 // internal helper
132 static qboolean selectPhysicalDevice(int preferredDeviceIdx)
133 {
134  uint32_t physicalDeviceCount = 0;
135  VK_VERIFY(vkEnumeratePhysicalDevices(vk_instance, &physicalDeviceCount, NULL));
136 
137  if (physicalDeviceCount == 0)
138  {
139  ri.Con_Printf(PRINT_ALL, "No Vulkan-capable devices found!\n");
140  return false;
141  }
142 
143  ri.Con_Printf(PRINT_ALL, "...found %d Vulkan-capable device(s)\n", physicalDeviceCount);
144 
145  VkPhysicalDevice *physicalDevices = (VkPhysicalDevice *)malloc(physicalDeviceCount * sizeof(VkPhysicalDevice));
146  VK_VERIFY(vkEnumeratePhysicalDevices(vk_instance, &physicalDeviceCount, physicalDevices));
147 
148  getBestPhysicalDevice(physicalDevices, preferredDeviceIdx < physicalDeviceCount ? preferredDeviceIdx : -1, physicalDeviceCount);
149  free(physicalDevices);
150 
151  if (vk_device.physical == VK_NULL_HANDLE)
152  {
153  ri.Con_Printf(PRINT_ALL, "Could not find a suitable physical device!\n");
154  return false;
155  }
156 
157  return true;
158 }
159 
160 // internal helper
161 static VkResult createLogicalDevice()
162 {
163  // at least one queue (graphics and present combined) has to be present
164  uint32_t numQueues = 1;
165  float queuePriority = 1.f;
166  VkDeviceQueueCreateInfo queueCreateInfo[3];
167  queueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
168  queueCreateInfo[0].pNext = NULL;
169  queueCreateInfo[0].flags = 0;
170  queueCreateInfo[0].queueFamilyIndex = vk_device.gfxFamilyIndex;
171  queueCreateInfo[0].queueCount = 1;
172  queueCreateInfo[0].pQueuePriorities = &queuePriority;
173  queueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
174  queueCreateInfo[1].pNext = NULL;
175  queueCreateInfo[1].flags = 0;
176  queueCreateInfo[1].queueFamilyIndex = 0;
177  queueCreateInfo[1].queueCount = 1;
178  queueCreateInfo[1].pQueuePriorities = &queuePriority;
179  queueCreateInfo[2].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
180  queueCreateInfo[2].pNext = NULL;
181  queueCreateInfo[2].flags = 0;
182  queueCreateInfo[2].queueFamilyIndex = 0;
183  queueCreateInfo[2].queueCount = 1;
184  queueCreateInfo[2].pQueuePriorities = &queuePriority;
185 
186  VkPhysicalDeviceFeatures wantedDeviceFeatures = {
187  .samplerAnisotropy = vk_device.features.samplerAnisotropy,
188  .fillModeNonSolid = vk_device.features.fillModeNonSolid, // for wireframe rendering
189  .sampleRateShading = vk_device.features.sampleRateShading, // for sample shading
190  };
191 
192  // a graphics and present queue are different - two queues have to be created
194  {
195  queueCreateInfo[numQueues++].queueFamilyIndex = vk_device.presentFamilyIndex;
196  }
197 
198  // a separate transfer queue exists that's different from present and graphics queue?
200  {
201  queueCreateInfo[numQueues++].queueFamilyIndex = vk_device.transferFamilyIndex;
202  }
203 
204  const char *deviceExtensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
205 
206  VkDeviceCreateInfo deviceCreateInfo = {
207  .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
208  .pEnabledFeatures = &wantedDeviceFeatures,
209  .ppEnabledExtensionNames = deviceExtensions,
210  .enabledExtensionCount = 1,
211  .enabledLayerCount = 0,
212  .ppEnabledLayerNames = NULL,
213  .queueCreateInfoCount = numQueues,
214  .pQueueCreateInfos = queueCreateInfo
215  };
216 
217 #if VK_HEADER_VERSION > 101
218  const char *validationLayers[] = { "VK_LAYER_KHRONOS_validation" };
219 #else
220  const char *validationLayers[] = { "VK_LAYER_LUNARG_standard_validation" };
221 #endif
222 
223  if (vk_validation->value)
224  {
225  deviceCreateInfo.enabledLayerCount = sizeof(validationLayers)/sizeof(validationLayers[0]);
226  deviceCreateInfo.ppEnabledLayerNames = validationLayers;
227  }
228 
229  return vkCreateDevice(vk_device.physical, &deviceCreateInfo, NULL, &vk_device.logical);
230 }
231 
232 // internal helper
233 static const char *deviceTypeString(VkPhysicalDeviceType dType)
234 {
235 #define DEVTYPESTR(r) case VK_ ##r: return "VK_"#r
236  switch (dType)
237  {
238  DEVTYPESTR(PHYSICAL_DEVICE_TYPE_OTHER);
239  DEVTYPESTR(PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU);
240  DEVTYPESTR(PHYSICAL_DEVICE_TYPE_DISCRETE_GPU);
241  DEVTYPESTR(PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU);
242  DEVTYPESTR(PHYSICAL_DEVICE_TYPE_CPU);
243  default: return "<unknown>";
244  }
245 #undef DEVTYPESTR
246  return "UNKNOWN DEVICE";
247 }
248 
249 // internal helper
250 static const char *vendorNameString(uint32_t vendorId)
251 {
252  switch (vendorId)
253  {
254  case 0x1002: return "AMD";
255  case 0x1010: return "ImgTec";
256  case 0x10DE: return "NVIDIA";
257  case 0x13B5: return "ARM";
258  case 0x5143: return "Qualcomm";
259  case 0x8086: return "Intel";
260  default: return "unknown";
261  }
262 }
263 
264 qboolean QVk_CreateDevice(int preferredDeviceIdx)
265 {
266  if (!selectPhysicalDevice(preferredDeviceIdx))
267  return false;
268 
271 
272  VkResult res = createLogicalDevice();
273  if (res != VK_SUCCESS)
274  {
275  ri.Con_Printf(PRINT_ALL, "Could not create Vulkan logical device: %s\n", QVk_GetError(res));
276  return false;
277  }
278 
282 
283  return true;
284 }
285 
286 // debug label related functions
287 #if defined(_DEBUG) || defined(ENABLE_DEBUG_LABELS)
288 
289 void QVk_DebugSetObjectName(uint64_t obj, VkObjectType objType, const char *objName)
290 {
291  VkDebugUtilsObjectNameInfoEXT oNameInf = {
292  .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
293  .pNext = NULL,
294  .objectType = objType,
295  .objectHandle = obj,
296  .pObjectName = objName
297  };
298 
300 }
301 
302 void QVk_DebugSetObjectTag(uint64_t obj, VkObjectType objType, uint64_t tagName, size_t tagSize, const void *tagData)
303 {
304  VkDebugUtilsObjectTagInfoEXT oTagInf = {
305  .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_TAG_INFO_EXT,
306  .pNext = NULL,
307  .objectType = objType,
308  .objectHandle = obj,
309  .tagName = tagName,
310  .tagSize = tagSize,
311  .pTag = tagData
312  };
313 
315 }
316 
317 void QVk_DebugLabelBegin(const VkCommandBuffer *cmdBuffer, const char *labelName, const float r, const float g, const float b)
318 {
319  VkDebugUtilsLabelEXT labelInfo = {
320  .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
321  .pNext = NULL,
322  .pLabelName = labelName,
323  .color = { r, g, b, 1.f }
324  };
325 
326  qvkCmdBeginDebugUtilsLabelEXT(*cmdBuffer, &labelInfo);
327 }
328 
329 void QVk_DebugLabelEnd(const VkCommandBuffer *cmdBuffer)
330 {
331  qvkCmdEndDebugUtilsLabelEXT(*cmdBuffer);
332 }
333 
334 void QVk_DebugLabelInsert(const VkCommandBuffer *cmdBuffer, const char *labelName, const float r, const float g, const float b)
335 {
336  VkDebugUtilsLabelEXT labelInfo = {
337  .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
338  .pNext = NULL,
339  .pLabelName = labelName,
340  .color = { r, g, b, 1.f }
341  };
342 
343  qvkInsertDebugUtilsLabelEXT(*cmdBuffer, &labelInfo);
344 }
345 
346 #endif
QVk_DebugSetObjectName
#define QVk_DebugSetObjectName(a, b, c)
Definition: qvk.h:317
qvkdevice_t::gfxQueue
VkQueue gfxQueue
Definition: qvk.h:43
qvkInsertDebugUtilsLabelEXT
PFN_vkCmdInsertDebugUtilsLabelEXT qvkInsertDebugUtilsLabelEXT
Definition: vk_common.c:172
qvkdevice_t::logical
VkDevice logical
Definition: qvk.h:40
vk_local.h
QVk_DebugLabelBegin
#define QVk_DebugLabelBegin(a, b, c, d, e)
Definition: qvk.h:319
vendorNameString
static const char * vendorNameString(uint32_t vendorId)
Definition: vk_device.c:250
vk_instance
VkInstance vk_instance
Definition: vk_common.c:46
vkconfig_t::device_type
const char * device_type
Definition: vk_local.h:308
ri
refimport_t ri
Definition: r_main.c:25
qvkCmdEndDebugUtilsLabelEXT
PFN_vkCmdEndDebugUtilsLabelEXT qvkCmdEndDebugUtilsLabelEXT
Definition: vk_common.c:171
qvkSetDebugUtilsObjectTagEXT
PFN_vkSetDebugUtilsObjectTagEXT qvkSetDebugUtilsObjectTagEXT
Definition: vk_common.c:169
createLogicalDevice
static VkResult createLogicalDevice()
Definition: vk_device.c:161
qvkdevice_t::properties
VkPhysicalDeviceProperties properties
Definition: qvk.h:41
qboolean
qboolean
Definition: q_shared.h:63
i
int i
Definition: q_shared.c:305
qvkdevice_t::gfxFamilyIndex
int gfxFamilyIndex
Definition: qvk.h:46
qvkSetDebugUtilsObjectNameEXT
PFN_vkSetDebugUtilsObjectNameEXT qvkSetDebugUtilsObjectNameEXT
Definition: vk_common.c:168
vk_device
qvkdevice_t vk_device
Definition: vk_common.c:51
j
GLint j
Definition: qgl_win.c:150
qvkdevice_t::presentFamilyIndex
int presentFamilyIndex
Definition: qvk.h:47
deviceExtensionsSupported
static qboolean deviceExtensionsSupported(const VkPhysicalDevice *physicalDevice)
Definition: vk_device.c:24
refimport_t::Con_Printf
void(* Con_Printf)(int print_level, char *str,...)
Definition: ref.h:202
PRINT_ALL
#define PRINT_ALL
Definition: qcommon.h:751
r
GLdouble GLdouble r
Definition: qgl_win.c:336
selectPhysicalDevice
static qboolean selectPhysicalDevice(int preferredDeviceIdx)
Definition: vk_device.c:132
QVk_DebugLabelInsert
#define QVk_DebugLabelInsert(a, b, c, d, e)
Definition: qvk.h:321
QVk_DebugLabelEnd
#define QVk_DebugLabelEnd(a)
Definition: qvk.h:320
cvar_s::value
float value
Definition: q_shared.h:331
vk_validation
cvar_t * vk_validation
Definition: vk_rmain.c:90
qvkdevice_t::features
VkPhysicalDeviceFeatures features
Definition: qvk.h:42
NULL
#define NULL
Definition: q_shared.h:67
QVk_DebugSetObjectTag
#define QVk_DebugSetObjectTag(a, b, c, d, e)
Definition: qvk.h:318
vk_config
vkconfig_t vk_config
Definition: vk_rmain.c:30
qvkCmdBeginDebugUtilsLabelEXT
PFN_vkCmdBeginDebugUtilsLabelEXT qvkCmdBeginDebugUtilsLabelEXT
Definition: vk_common.c:170
VK_VERIFY
#define VK_VERIFY(x)
Definition: vk_local.h:59
qvkdevice_t::presentQueue
VkQueue presentQueue
Definition: qvk.h:44
QVk_CreateDevice
qboolean QVk_CreateDevice(int preferredDeviceIdx)
Definition: vk_device.c:264
vkconfig_t::vendor_name
const char * vendor_name
Definition: vk_local.h:307
qvkdevice_t::transferQueue
VkQueue transferQueue
Definition: qvk.h:45
vk_surface
VkSurfaceKHR vk_surface
Definition: vk_common.c:47
qvkdevice_t::transferFamilyIndex
int transferFamilyIndex
Definition: qvk.h:48
getBestPhysicalDevice
static void getBestPhysicalDevice(const VkPhysicalDevice *devices, int preferredIdx, int count)
Definition: vk_device.c:48
DEVTYPESTR
#define DEVTYPESTR(r)
deviceTypeString
static const char * deviceTypeString(VkPhysicalDeviceType dType)
Definition: vk_device.c:233
qvkdevice_t::physical
VkPhysicalDevice physical
Definition: qvk.h:39
count
GLint GLsizei count
Definition: qgl_win.c:128
QVk_GetError
const char * QVk_GetError(VkResult errorCode)
Definition: vk_common.c:2293