1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "qbasicvulkanplatforminstance_p.h"
41 #include <QLibrary>
42 #include <QCoreApplication>
43 #include <QVector>
44 #include <QLoggingCategory>
45
46 QT_BEGIN_NAMESPACE
47
48 Q_LOGGING_CATEGORY(lcPlatVk, "qt.vulkan")
49
50 /*!
51 \class QBasicPlatformVulkanInstance
52 \brief A generic platform Vulkan instance implementation.
53 \since 5.10
54 \internal
55 \ingroup qpa
56
57 Implements QPlatformVulkanInstance, serving as a base for platform-specific
58 implementations. The library loading and any WSI-specifics are excluded.
59
60 Subclasses are expected to call init() from their constructor and
61 initInstance() from their createOrAdoptInstance() implementation.
62 */
63
QBasicPlatformVulkanInstance()64 QBasicPlatformVulkanInstance::QBasicPlatformVulkanInstance()
65 : m_vkInst(VK_NULL_HANDLE),
66 m_vkGetInstanceProcAddr(nullptr),
67 m_ownsVkInst(false),
68 m_errorCode(VK_SUCCESS),
69 m_debugCallback(0)
70 {
71 }
72
~QBasicPlatformVulkanInstance()73 QBasicPlatformVulkanInstance::~QBasicPlatformVulkanInstance()
74 {
75 if (!m_vkInst)
76 return;
77
78 if (m_debugCallback && m_vkDestroyDebugReportCallbackEXT)
79 m_vkDestroyDebugReportCallbackEXT(m_vkInst, m_debugCallback, nullptr);
80
81 if (m_ownsVkInst)
82 m_vkDestroyInstance(m_vkInst, nullptr);
83 }
84
loadVulkanLibrary(const QString & defaultLibraryName)85 void QBasicPlatformVulkanInstance::loadVulkanLibrary(const QString &defaultLibraryName)
86 {
87 if (qEnvironmentVariableIsSet("QT_VULKAN_LIB"))
88 m_vulkanLib.setFileName(QString::fromUtf8(qgetenv("QT_VULKAN_LIB")));
89 else
90 m_vulkanLib.setFileName(defaultLibraryName);
91
92 if (!m_vulkanLib.load()) {
93 qWarning("Failed to load %s: %s", qPrintable(m_vulkanLib.fileName()), qPrintable(m_vulkanLib.errorString()));
94 return;
95 }
96
97 init(&m_vulkanLib);
98 }
99
init(QLibrary * lib)100 void QBasicPlatformVulkanInstance::init(QLibrary *lib)
101 {
102 if (m_vkGetInstanceProcAddr)
103 return;
104
105 qCDebug(lcPlatVk, "Vulkan init (%s)", qPrintable(lib->fileName()));
106
107 // While not strictly required with every implementation, try to follow the spec
108 // and do not rely on core functions being exported.
109 //
110 // 1. dlsym vkGetInstanceProcAddr
111 // 2. with a special null instance resolve vkCreateInstance and vkEnumerateInstance*
112 // 3. all other core functions are resolved with the created instance
113
114 m_vkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(lib->resolve("vkGetInstanceProcAddr"));
115 if (!m_vkGetInstanceProcAddr) {
116 qWarning("Failed to find vkGetInstanceProcAddr");
117 return;
118 }
119
120 m_vkCreateInstance = reinterpret_cast<PFN_vkCreateInstance>(m_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance"));
121 if (!m_vkCreateInstance) {
122 qWarning("Failed to find vkCreateInstance");
123 return;
124 }
125 m_vkEnumerateInstanceLayerProperties = reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(
126 m_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties"));
127 if (!m_vkEnumerateInstanceLayerProperties) {
128 qWarning("Failed to find vkEnumerateInstanceLayerProperties");
129 return;
130 }
131 m_vkEnumerateInstanceExtensionProperties = reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>(
132 m_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties"));
133 if (!m_vkEnumerateInstanceExtensionProperties) {
134 qWarning("Failed to find vkEnumerateInstanceExtensionProperties");
135 return;
136 }
137
138 uint32_t layerCount = 0;
139 m_vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
140 if (layerCount) {
141 QVector<VkLayerProperties> layerProps(layerCount);
142 m_vkEnumerateInstanceLayerProperties(&layerCount, layerProps.data());
143 m_supportedLayers.reserve(layerCount);
144 for (const VkLayerProperties &p : qAsConst(layerProps)) {
145 QVulkanLayer layer;
146 layer.name = p.layerName;
147 layer.version = p.implementationVersion;
148 layer.specVersion = QVersionNumber(VK_VERSION_MAJOR(p.specVersion),
149 VK_VERSION_MINOR(p.specVersion),
150 VK_VERSION_PATCH(p.specVersion));
151 layer.description = p.description;
152 m_supportedLayers.append(layer);
153 }
154 }
155 qCDebug(lcPlatVk) << "Supported Vulkan instance layers:" << m_supportedLayers;
156
157 uint32_t extCount = 0;
158 m_vkEnumerateInstanceExtensionProperties(nullptr, &extCount, nullptr);
159 if (extCount) {
160 QVector<VkExtensionProperties> extProps(extCount);
161 m_vkEnumerateInstanceExtensionProperties(nullptr, &extCount, extProps.data());
162 m_supportedExtensions.reserve(extCount);
163 for (const VkExtensionProperties &p : qAsConst(extProps)) {
164 QVulkanExtension ext;
165 ext.name = p.extensionName;
166 ext.version = p.specVersion;
167 m_supportedExtensions.append(ext);
168 }
169 }
170 qDebug(lcPlatVk) << "Supported Vulkan instance extensions:" << m_supportedExtensions;
171 }
172
supportedLayers() const173 QVulkanInfoVector<QVulkanLayer> QBasicPlatformVulkanInstance::supportedLayers() const
174 {
175 return m_supportedLayers;
176 }
177
supportedExtensions() const178 QVulkanInfoVector<QVulkanExtension> QBasicPlatformVulkanInstance::supportedExtensions() const
179 {
180 return m_supportedExtensions;
181 }
182
initInstance(QVulkanInstance * instance,const QByteArrayList & extraExts)183 void QBasicPlatformVulkanInstance::initInstance(QVulkanInstance *instance, const QByteArrayList &extraExts)
184 {
185 if (!m_vkGetInstanceProcAddr) {
186 qWarning("initInstance: No Vulkan library available");
187 return;
188 }
189
190 m_vkInst = instance->vkInstance(); // when non-null we are adopting an existing instance
191
192 QVulkanInstance::Flags flags = instance->flags();
193 m_enabledLayers = instance->layers();
194 m_enabledExtensions = instance->extensions();
195
196 if (!m_vkInst) {
197 VkApplicationInfo appInfo;
198 memset(&appInfo, 0, sizeof(appInfo));
199 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
200 QByteArray appName = QCoreApplication::applicationName().toUtf8();
201 appInfo.pApplicationName = appName.constData();
202 const QVersionNumber apiVersion = instance->apiVersion();
203 if (!apiVersion.isNull()) {
204 appInfo.apiVersion = VK_MAKE_VERSION(apiVersion.majorVersion(),
205 apiVersion.minorVersion(),
206 apiVersion.microVersion());
207 }
208
209 if (!flags.testFlag(QVulkanInstance::NoDebugOutputRedirect))
210 m_enabledExtensions.append("VK_EXT_debug_report");
211
212 m_enabledExtensions.append("VK_KHR_surface");
213
214 for (const QByteArray &ext : extraExts)
215 m_enabledExtensions.append(ext);
216
217 QByteArray envExts = qgetenv("QT_VULKAN_INSTANCE_EXTENSIONS");
218 if (!envExts.isEmpty()) {
219 QByteArrayList envExtList = envExts.split(';');
220 for (auto ext : m_enabledExtensions)
221 envExtList.removeAll(ext);
222 m_enabledExtensions.append(envExtList);
223 }
224
225 QByteArray envLayers = qgetenv("QT_VULKAN_INSTANCE_LAYERS");
226 if (!envLayers.isEmpty()) {
227 QByteArrayList envLayerList = envLayers.split(';');
228 for (auto ext : m_enabledLayers)
229 envLayerList.removeAll(ext);
230 m_enabledLayers.append(envLayerList);
231 }
232
233 // No clever stuff with QSet and friends: the order for layers matters
234 // and the user-provided order must be kept.
235 for (int i = 0; i < m_enabledLayers.count(); ++i) {
236 const QByteArray &layerName(m_enabledLayers[i]);
237 if (!m_supportedLayers.contains(layerName))
238 m_enabledLayers.removeAt(i--);
239 }
240 qDebug(lcPlatVk) << "Enabling Vulkan instance layers:" << m_enabledLayers;
241 for (int i = 0; i < m_enabledExtensions.count(); ++i) {
242 const QByteArray &extName(m_enabledExtensions[i]);
243 if (!m_supportedExtensions.contains(extName))
244 m_enabledExtensions.removeAt(i--);
245 }
246 qDebug(lcPlatVk) << "Enabling Vulkan instance extensions:" << m_enabledExtensions;
247
248 VkInstanceCreateInfo instInfo;
249 memset(&instInfo, 0, sizeof(instInfo));
250 instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
251 instInfo.pApplicationInfo = &appInfo;
252
253 QVector<const char *> layerNameVec;
254 for (const QByteArray &ba : qAsConst(m_enabledLayers))
255 layerNameVec.append(ba.constData());
256 if (!layerNameVec.isEmpty()) {
257 instInfo.enabledLayerCount = layerNameVec.count();
258 instInfo.ppEnabledLayerNames = layerNameVec.constData();
259 }
260
261 QVector<const char *> extNameVec;
262 for (const QByteArray &ba : qAsConst(m_enabledExtensions))
263 extNameVec.append(ba.constData());
264 if (!extNameVec.isEmpty()) {
265 instInfo.enabledExtensionCount = extNameVec.count();
266 instInfo.ppEnabledExtensionNames = extNameVec.constData();
267 }
268
269 m_errorCode = m_vkCreateInstance(&instInfo, nullptr, &m_vkInst);
270 if (m_errorCode != VK_SUCCESS || !m_vkInst) {
271 qWarning("Failed to create Vulkan instance: %d", m_errorCode);
272 return;
273 }
274
275 m_vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(m_vkGetInstanceProcAddr(m_vkInst, "vkDestroyInstance"));
276 if (!m_vkDestroyInstance) {
277 qWarning("Failed to find vkDestroyInstance");
278 m_vkInst = VK_NULL_HANDLE;
279 return;
280 }
281
282 m_ownsVkInst = true;
283 }
284
285 m_getPhysDevSurfaceSupport = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>(
286 m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceSurfaceSupportKHR"));
287 if (!m_getPhysDevSurfaceSupport)
288 qWarning("Failed to find vkGetPhysicalDeviceSurfaceSupportKHR");
289
290 m_destroySurface = reinterpret_cast<PFN_vkDestroySurfaceKHR>(
291 m_vkGetInstanceProcAddr(m_vkInst, "vkDestroySurfaceKHR"));
292 if (!m_destroySurface)
293 qWarning("Failed to find vkDestroySurfaceKHR");
294
295 if (!flags.testFlag(QVulkanInstance::NoDebugOutputRedirect))
296 setupDebugOutput();
297 }
298
isValid() const299 bool QBasicPlatformVulkanInstance::isValid() const
300 {
301 return m_vkInst != VK_NULL_HANDLE;
302 }
303
errorCode() const304 VkResult QBasicPlatformVulkanInstance::errorCode() const
305 {
306 return m_errorCode;
307 }
308
vkInstance() const309 VkInstance QBasicPlatformVulkanInstance::vkInstance() const
310 {
311 return m_vkInst;
312 }
313
enabledLayers() const314 QByteArrayList QBasicPlatformVulkanInstance::enabledLayers() const
315 {
316 return m_enabledLayers;
317 }
318
enabledExtensions() const319 QByteArrayList QBasicPlatformVulkanInstance::enabledExtensions() const
320 {
321 return m_enabledExtensions;
322 }
323
getInstanceProcAddr(const char * name)324 PFN_vkVoidFunction QBasicPlatformVulkanInstance::getInstanceProcAddr(const char *name)
325 {
326 if (!name)
327 return nullptr;
328
329 const bool needsNullInstance = !strcmp(name, "vkEnumerateInstanceLayerProperties")
330 || !strcmp(name, "vkEnumerateInstanceExtensionProperties");
331
332 return m_vkGetInstanceProcAddr(needsNullInstance ? 0 : m_vkInst, name);
333 }
334
supportsPresent(VkPhysicalDevice physicalDevice,uint32_t queueFamilyIndex,QWindow * window)335 bool QBasicPlatformVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice,
336 uint32_t queueFamilyIndex,
337 QWindow *window)
338 {
339 if (!m_getPhysDevSurfaceSupport)
340 return true;
341
342 VkSurfaceKHR surface = QVulkanInstance::surfaceForWindow(window);
343 VkBool32 supported = false;
344 m_getPhysDevSurfaceSupport(physicalDevice, queueFamilyIndex, surface, &supported);
345
346 return supported;
347 }
348
setDebugFilters(const QVector<QVulkanInstance::DebugFilter> & filters)349 void QBasicPlatformVulkanInstance::setDebugFilters(const QVector<QVulkanInstance::DebugFilter> &filters)
350 {
351 m_debugFilters = filters;
352 }
353
destroySurface(VkSurfaceKHR surface) const354 void QBasicPlatformVulkanInstance::destroySurface(VkSurfaceKHR surface) const
355 {
356 if (m_destroySurface && surface)
357 m_destroySurface(m_vkInst, surface, nullptr);
358 }
359
defaultDebugCallbackFunc(VkDebugReportFlagsEXT flags,VkDebugReportObjectTypeEXT objectType,uint64_t object,size_t location,int32_t messageCode,const char * pLayerPrefix,const char * pMessage,void * pUserData)360 static VKAPI_ATTR VkBool32 VKAPI_CALL defaultDebugCallbackFunc(VkDebugReportFlagsEXT flags,
361 VkDebugReportObjectTypeEXT objectType,
362 uint64_t object,
363 size_t location,
364 int32_t messageCode,
365 const char *pLayerPrefix,
366 const char *pMessage,
367 void *pUserData)
368 {
369 QBasicPlatformVulkanInstance *self = static_cast<QBasicPlatformVulkanInstance *>(pUserData);
370 for (QVulkanInstance::DebugFilter filter : *self->debugFilters()) {
371 if (filter(flags, objectType, object, location, messageCode, pLayerPrefix, pMessage))
372 return VK_FALSE;
373 }
374
375 // not categorized, just route to plain old qDebug
376 qDebug("vkDebug: %s: %d: %s", pLayerPrefix, messageCode, pMessage);
377
378 return VK_FALSE;
379 }
380
setupDebugOutput()381 void QBasicPlatformVulkanInstance::setupDebugOutput()
382 {
383 if (!m_enabledExtensions.contains("VK_EXT_debug_report"))
384 return;
385
386 PFN_vkCreateDebugReportCallbackEXT createDebugReportCallback = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(
387 m_vkGetInstanceProcAddr(m_vkInst, "vkCreateDebugReportCallbackEXT"));
388 m_vkDestroyDebugReportCallbackEXT = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(
389 m_vkGetInstanceProcAddr(m_vkInst, "vkDestroyDebugReportCallbackEXT"));
390
391 VkDebugReportCallbackCreateInfoEXT dbgCallbackInfo;
392 memset(&dbgCallbackInfo, 0, sizeof(dbgCallbackInfo));
393 dbgCallbackInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
394 dbgCallbackInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT
395 | VK_DEBUG_REPORT_WARNING_BIT_EXT
396 | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
397 dbgCallbackInfo.pfnCallback = defaultDebugCallbackFunc;
398 dbgCallbackInfo.pUserData = this;
399
400 VkResult err = createDebugReportCallback(m_vkInst, &dbgCallbackInfo, nullptr, &m_debugCallback);
401 if (err != VK_SUCCESS)
402 qWarning("Failed to create debug report callback: %d", err);
403 }
404
405 QT_END_NAMESPACE
406