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

source code of qtbase/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayvulkaninstance.cpp