1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2020 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 "qeglfsvulkaninstance_p.h" |
41 | #include "qeglfswindow_p.h" |
42 | #include "qeglfshooks_p.h" |
43 | #include <QLoggingCategory> |
44 | |
45 | QT_BEGIN_NAMESPACE |
46 | |
47 | Q_DECLARE_LOGGING_CATEGORY(qLcEglDevDebug) |
48 | |
49 | QEglFSVulkanInstance::QEglFSVulkanInstance(QVulkanInstance *instance) |
50 | : m_instance(instance) |
51 | { |
52 | loadVulkanLibrary(QStringLiteral("vulkan" )); |
53 | } |
54 | |
55 | void QEglFSVulkanInstance::createOrAdoptInstance() |
56 | { |
57 | qCDebug(qLcEglDevDebug, "Creating Vulkan instance for VK_KHR_display" ); |
58 | |
59 | const QByteArray extName = QByteArrayLiteral("VK_KHR_display" ); |
60 | initInstance(instance: m_instance, extraExts: { extName }); |
61 | if (!m_vkInst) |
62 | return; |
63 | if (!enabledExtensions().contains(t: extName)) { |
64 | qWarning(msg: "Failed to enable VK_KHR_display extension" ); |
65 | return; |
66 | } |
67 | |
68 | #if VK_KHR_display |
69 | m_getPhysicalDeviceDisplayPropertiesKHR = (PFN_vkGetPhysicalDeviceDisplayPropertiesKHR) |
70 | m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceDisplayPropertiesKHR" ); |
71 | m_getDisplayModePropertiesKHR = (PFN_vkGetDisplayModePropertiesKHR) |
72 | m_vkGetInstanceProcAddr(m_vkInst, "vkGetDisplayModePropertiesKHR" ); |
73 | m_getPhysicalDeviceDisplayPlanePropertiesKHR = (PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR) |
74 | m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR" ); |
75 | |
76 | m_getDisplayPlaneSupportedDisplaysKHR = (PFN_vkGetDisplayPlaneSupportedDisplaysKHR) |
77 | m_vkGetInstanceProcAddr(m_vkInst, "vkGetDisplayPlaneSupportedDisplaysKHR" ); |
78 | m_getDisplayPlaneCapabilitiesKHR = (PFN_vkGetDisplayPlaneCapabilitiesKHR) |
79 | m_vkGetInstanceProcAddr(m_vkInst, "vkGetDisplayPlaneCapabilitiesKHR" ); |
80 | |
81 | m_createDisplayPlaneSurfaceKHR = (PFN_vkCreateDisplayPlaneSurfaceKHR) |
82 | m_vkGetInstanceProcAddr(m_vkInst, "vkCreateDisplayPlaneSurfaceKHR" ); |
83 | #endif |
84 | |
85 | m_enumeratePhysicalDevices = (PFN_vkEnumeratePhysicalDevices) |
86 | m_vkGetInstanceProcAddr(m_vkInst, "vkEnumeratePhysicalDevices" ); |
87 | |
88 | // Use for first physical device, unless overridden by QT_VK_PHYSICAL_DEVICE_INDEX. |
89 | // This behavior matches what the Vulkan backend of QRhi would do. |
90 | |
91 | uint32_t physDevCount = 0; |
92 | m_enumeratePhysicalDevices(m_vkInst, &physDevCount, nullptr); |
93 | if (!physDevCount) { |
94 | qWarning(msg: "No physical devices" ); |
95 | return; |
96 | } |
97 | QVarLengthArray<VkPhysicalDevice, 4> physDevs(physDevCount); |
98 | VkResult err = m_enumeratePhysicalDevices(m_vkInst, &physDevCount, physDevs.data()); |
99 | if (err != VK_SUCCESS || !physDevCount) { |
100 | qWarning(msg: "Failed to enumerate physical devices: %d" , err); |
101 | return; |
102 | } |
103 | |
104 | if (qEnvironmentVariableIsSet(varName: "QT_VK_PHYSICAL_DEVICE_INDEX" )) { |
105 | int requestedPhysDevIndex = qEnvironmentVariableIntValue(varName: "QT_VK_PHYSICAL_DEVICE_INDEX" ); |
106 | if (requestedPhysDevIndex >= 0 && uint32_t(requestedPhysDevIndex) < physDevCount) |
107 | m_physDev = physDevs[requestedPhysDevIndex]; |
108 | } |
109 | |
110 | if (m_physDev == VK_NULL_HANDLE) |
111 | m_physDev = physDevs[0]; |
112 | } |
113 | |
114 | bool QEglFSVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice, |
115 | uint32_t queueFamilyIndex, |
116 | QWindow *window) |
117 | { |
118 | Q_UNUSED(physicalDevice); |
119 | Q_UNUSED(queueFamilyIndex); |
120 | Q_UNUSED(window); |
121 | return true; |
122 | } |
123 | |
124 | VkSurfaceKHR QEglFSVulkanInstance::createSurface(QEglFSWindow *window) |
125 | { |
126 | #if VK_KHR_display |
127 | qCDebug(qLcEglDevDebug, "Creating VkSurfaceKHR via VK_KHR_display for window %p" , (void *) window); |
128 | |
129 | if (!m_physDev) { |
130 | qWarning(msg: "No physical device, cannot create surface" ); |
131 | return VK_NULL_HANDLE; |
132 | } |
133 | |
134 | uint32_t displayCount = 0; |
135 | VkResult err = m_getPhysicalDeviceDisplayPropertiesKHR(m_physDev, &displayCount, nullptr); |
136 | if (err != VK_SUCCESS) { |
137 | qWarning(msg: "Failed to get display properties: %d" , err); |
138 | return VK_NULL_HANDLE; |
139 | } |
140 | |
141 | qCDebug(qLcEglDevDebug, "Display count: %u" , displayCount); |
142 | |
143 | QVarLengthArray<VkDisplayPropertiesKHR, 4> displayProps(displayCount); |
144 | m_getPhysicalDeviceDisplayPropertiesKHR(m_physDev, &displayCount, displayProps.data()); |
145 | |
146 | VkDisplayKHR display = VK_NULL_HANDLE; |
147 | VkDisplayModeKHR displayMode = VK_NULL_HANDLE; |
148 | uint32_t width = 0; |
149 | uint32_t height = 0; |
150 | |
151 | for (uint32_t i = 0; i < displayCount; ++i) { |
152 | const VkDisplayPropertiesKHR &disp(displayProps[i]); |
153 | qCDebug(qLcEglDevDebug, "Display #%u:\n display: %p\n name: %s\n dimensions: %ux%u\n resolution: %ux%u" , |
154 | i, (void *) disp.display, disp.displayName, |
155 | disp.physicalDimensions.width, disp.physicalDimensions.height, |
156 | disp.physicalResolution.width, disp.physicalResolution.height); |
157 | |
158 | // Just pick the first display and the first mode. |
159 | if (i == 0) |
160 | display = disp.display; |
161 | |
162 | uint32_t modeCount = 0; |
163 | if (m_getDisplayModePropertiesKHR(m_physDev, disp.display, &modeCount, nullptr) != VK_SUCCESS) { |
164 | qWarning(msg: "Failed to get modes for display" ); |
165 | continue; |
166 | } |
167 | QVarLengthArray<VkDisplayModePropertiesKHR, 16> modeProps(modeCount); |
168 | m_getDisplayModePropertiesKHR(m_physDev, disp.display, &modeCount, modeProps.data()); |
169 | for (uint32_t j = 0; j < modeCount; ++j) { |
170 | const VkDisplayModePropertiesKHR &mode(modeProps[j]); |
171 | qCDebug(qLcEglDevDebug, " Mode #%u:\n mode: %p\n visibleRegion: %ux%u\n refreshRate: %u" , |
172 | j, (void *) mode.displayMode, |
173 | mode.parameters.visibleRegion.width, mode.parameters.visibleRegion.height, |
174 | mode.parameters.refreshRate); |
175 | if (j == 0) { |
176 | displayMode = mode.displayMode; |
177 | width = mode.parameters.visibleRegion.width; |
178 | height = mode.parameters.visibleRegion.height; |
179 | } |
180 | } |
181 | } |
182 | |
183 | if (display == VK_NULL_HANDLE || displayMode == VK_NULL_HANDLE) { |
184 | qWarning(msg: "Failed to choose display and mode" ); |
185 | return VK_NULL_HANDLE; |
186 | } |
187 | uint32_t planeCount = 0; |
188 | err = m_getPhysicalDeviceDisplayPlanePropertiesKHR(m_physDev, &planeCount, nullptr); |
189 | if (err != VK_SUCCESS) { |
190 | qWarning(msg: "Failed to get plane properties: %d" , err); |
191 | return VK_NULL_HANDLE; |
192 | } |
193 | |
194 | qCDebug(qLcEglDevDebug, "Plane count: %u" , planeCount); |
195 | |
196 | QVarLengthArray<VkDisplayPlanePropertiesKHR, 4> planeProps(planeCount); |
197 | m_getPhysicalDeviceDisplayPlanePropertiesKHR(m_physDev, &planeCount, planeProps.data()); |
198 | |
199 | uint32_t planeIndex = UINT_MAX; |
200 | for (uint32_t i = 0; i < planeCount; ++i) { |
201 | uint32_t supportedDisplayCount = 0; |
202 | err = m_getDisplayPlaneSupportedDisplaysKHR(m_physDev, i, &supportedDisplayCount, nullptr); |
203 | if (err != VK_SUCCESS) { |
204 | qWarning(msg: "Failed to query supported displays for plane: %d" , err); |
205 | return VK_NULL_HANDLE; |
206 | } |
207 | |
208 | QVarLengthArray<VkDisplayKHR, 4> supportedDisplays(supportedDisplayCount); |
209 | m_getDisplayPlaneSupportedDisplaysKHR(m_physDev, i, &supportedDisplayCount, supportedDisplays.data()); |
210 | qCDebug(qLcEglDevDebug, "Plane #%u supports %u displays, currently bound to display %p" , |
211 | i, supportedDisplayCount, (void *) planeProps[i].currentDisplay); |
212 | |
213 | VkDisplayPlaneCapabilitiesKHR caps; |
214 | err = m_getDisplayPlaneCapabilitiesKHR(m_physDev, displayMode, i, &caps); |
215 | if (err != VK_SUCCESS) { |
216 | qWarning(msg: "Failed to query plane capabilities: %d" , err); |
217 | return VK_NULL_HANDLE; |
218 | } |
219 | |
220 | qCDebug(qLcEglDevDebug, " supportedAlpha: %d (1=no, 2=global, 4=per pixel, 8=per pixel premul)\n" |
221 | " minSrc=%d, %d %ux%u\n" |
222 | " maxSrc=%d, %d %ux%u\n" |
223 | " minDst=%d, %d %ux%u\n" |
224 | " maxDst=%d, %d %ux%u" , |
225 | int(caps.supportedAlpha), |
226 | caps.minSrcPosition.x, caps.minSrcPosition.y, caps.minSrcExtent.width, caps.minSrcExtent.height, |
227 | caps.maxSrcPosition.x, caps.maxSrcPosition.y, caps.maxSrcExtent.width, caps.maxSrcExtent.height, |
228 | caps.minDstPosition.x, caps.minDstPosition.y, caps.minDstExtent.width, caps.minDstExtent.height, |
229 | caps.maxDstPosition.x, caps.maxDstPosition.y, caps.maxDstExtent.width, caps.maxDstExtent.height); |
230 | |
231 | // if the plane is not in use and supports our chosen display, use that plane |
232 | if (supportedDisplays.contains(t: display) |
233 | && (planeProps[i].currentDisplay == VK_NULL_HANDLE || planeProps[i].currentDisplay == display)) |
234 | { |
235 | planeIndex = i; |
236 | } |
237 | } |
238 | |
239 | if (planeIndex == UINT_MAX) { |
240 | qWarning(msg: "Failed to find a suitable plane" ); |
241 | return VK_NULL_HANDLE; |
242 | } |
243 | |
244 | qCDebug(qLcEglDevDebug, "Using plane #%u" , planeIndex); |
245 | |
246 | VkDisplaySurfaceCreateInfoKHR surfaceCreateInfo = {}; |
247 | surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR; |
248 | surfaceCreateInfo.displayMode = displayMode; |
249 | surfaceCreateInfo.planeIndex = planeIndex; |
250 | surfaceCreateInfo.planeStackIndex = planeProps[planeIndex].currentStackIndex; |
251 | surfaceCreateInfo.transform = VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR; |
252 | surfaceCreateInfo.globalAlpha = 1.0f; |
253 | surfaceCreateInfo.alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; |
254 | surfaceCreateInfo.imageExtent = { .width: width, .height: height }; |
255 | |
256 | VkSurfaceKHR surface = VK_NULL_HANDLE; |
257 | err = m_createDisplayPlaneSurfaceKHR(m_vkInst, &surfaceCreateInfo, nullptr, &surface); |
258 | if (err != VK_SUCCESS || surface == VK_NULL_HANDLE) { |
259 | qWarning(msg: "Failed to create surface: %d" , err); |
260 | return VK_NULL_HANDLE; |
261 | } |
262 | |
263 | qCDebug(qLcEglDevDebug, "Created surface %p" , (void *) surface); |
264 | |
265 | return surface; |
266 | |
267 | #else |
268 | Q_UNUSED(window); |
269 | qWarning("VK_KHR_display support was not compiled in, cannot create surface" ); |
270 | return VK_NULL_HANDLE; |
271 | #endif |
272 | } |
273 | |
274 | void QEglFSVulkanInstance::presentAboutToBeQueued(QWindow *window) |
275 | { |
276 | // support QT_QPA_EGLFS_FORCEVSYNC (i.MX8 with eglfs_viv) |
277 | qt_egl_device_integration()->waitForVSync(surface: window->handle()); |
278 | } |
279 | |
280 | QT_END_NAMESPACE |
281 | |