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
45QT_BEGIN_NAMESPACE
46
47Q_DECLARE_LOGGING_CATEGORY(qLcEglDevDebug)
48
49QEglFSVulkanInstance::QEglFSVulkanInstance(QVulkanInstance *instance)
50 : m_instance(instance)
51{
52 loadVulkanLibrary(QStringLiteral("vulkan"));
53}
54
55void QEglFSVulkanInstance::createOrAdoptInstance()
56{
57 qCDebug(qLcEglDevDebug, "Creating Vulkan instance for VK_KHR_display");
58
59 const QByteArray extName = QByteArrayLiteral("VK_KHR_display");
60 initInstance(m_instance, { extName });
61 if (!m_vkInst)
62 return;
63 if (!enabledExtensions().contains(extName)) {
64 qWarning("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("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("Failed to enumerate physical devices: %d", err);
101 return;
102 }
103
104 if (qEnvironmentVariableIsSet("QT_VK_PHYSICAL_DEVICE_INDEX")) {
105 int requestedPhysDevIndex = qEnvironmentVariableIntValue("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
114bool 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
124VkSurfaceKHR 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("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("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("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("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("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("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("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(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("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, 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("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
274void QEglFSVulkanInstance::presentAboutToBeQueued(QWindow *window)
275{
276 // support QT_QPA_EGLFS_FORCEVSYNC (i.MX8 with eglfs_viv)
277 qt_egl_device_integration()->waitForVSync(window->handle());
278}
279
280QT_END_NAMESPACE
281