Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qopenxrgraphics_vulkan.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
5
6#include "qopenxrhelpers_p.h"
7#include <QtQuick/QQuickWindow>
8#include <QtQuick/QQuickGraphicsDevice>
9#include <QtQuick/QQuickGraphicsConfiguration>
10#include <QtQuick/private/qquickrendertarget_p.h>
11
12#include <rhi/qrhi.h>
13
14//#define XR_USE_GRAPHICS_API_VULKAN
15
17
19{
20 m_graphicsBinding.type = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR;
21}
22
23
24bool QOpenXRGraphicsVulkan::isExtensionSupported(const QVector<XrExtensionProperties> &extensions) const
25{
26 for (const auto &extension : extensions) {
27 if (!strcmp(XR_KHR_VULKAN_ENABLE_EXTENSION_NAME,
28 extension.extensionName))
29 return true;
30 }
31 return false;
32}
33
34
36{
37 return XR_KHR_VULKAN_ENABLE_EXTENSION_NAME;
38}
39
40
41const XrBaseInStructure *QOpenXRGraphicsVulkan::handle() const
42{
43 return reinterpret_cast<const XrBaseInStructure*>(&m_graphicsBinding);
44}
45
46
47bool QOpenXRGraphicsVulkan::setupGraphics(const XrInstance &instance, XrSystemId &systemId, const QQuickGraphicsConfiguration &quickConfig)
48{
49 // Setup Vulkan Instance.
50
51 // In hybrid applications that also show Qt Quick windows on the desktop, it
52 // is not ideal to create multiple VkInstances (as the on-screen
53 // QQuickWindow(s) will have another one), but there is nothing we can do
54 // due to the forced upfront nature of Vulkan API design. And we need to do
55 // OpenXR API calls to get the things we need to create the instance. This
56 // is hard to reconcile with Quick, that knows nothing about XrView and
57 // such, and cannot predict the future either (i.e., "guess" if the user is
58 // ever going to instantiate an XRView, and so on).
59 //
60 // This has no relevance for XR-only apps, and even the hybrid case this
61 // works in practice, so we might just live with this for now.
62
63 PFN_xrGetVulkanGraphicsRequirementsKHR pfnGetVulkanGraphicsRequirementsKHR = nullptr;
64 OpenXRHelpers::checkXrResult(xrGetInstanceProcAddr(instance,
65 "xrGetVulkanGraphicsRequirementsKHR",
66 reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanGraphicsRequirementsKHR)),
67 instance);
68
69 if (!pfnGetVulkanGraphicsRequirementsKHR) {
70 qWarning("Could not resolve xrGetVulkanGraphicsRequirementsKHR; perhaps the OpenXR implementation does not support Vulkan?");
71 return false;
72 }
73
74 PFN_xrGetVulkanInstanceExtensionsKHR pfnGetVulkanInstanceExtensionsKHR = nullptr;
75 OpenXRHelpers::checkXrResult(xrGetInstanceProcAddr(instance,
76 "xrGetVulkanInstanceExtensionsKHR",
77 reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanInstanceExtensionsKHR)),
78 instance);
79
80 XrGraphicsRequirementsVulkanKHR graphicsRequirements{};
81 graphicsRequirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR;
82 OpenXRHelpers::checkXrResult(pfnGetVulkanGraphicsRequirementsKHR(instance,
83 systemId,
84 &graphicsRequirements),
85 instance);
86
87 quint32 extensionNamesSize = 0;
88 OpenXRHelpers::checkXrResult(pfnGetVulkanInstanceExtensionsKHR(instance,
89 systemId,
90 0,
91 &extensionNamesSize,
92 nullptr),
93 instance);
94
95 QByteArray extensionNames;
96 extensionNames.resize(extensionNamesSize);
97 OpenXRHelpers::checkXrResult(pfnGetVulkanInstanceExtensionsKHR(instance,
98 systemId,
99 extensionNamesSize,
100 &extensionNamesSize,
101 extensionNames.data()),
102 instance);
103
104 // The last extension could have extra null characters but
105 // the way we handle extenions doesn't handle null terminated
106 // strings well, so we have to strip them ourselves
107 auto stripNullChars = [](const QByteArray &string) {
108 auto begin = string.begin();
109 auto end = string.end();
110 while (begin < end && end[-1] == '\x00')
111 --end;
112 return QByteArray(begin, end - begin);
113 };
114
115 QByteArrayList extensions = extensionNames.split(' ');
116 for (auto &ext : extensions)
117 ext = stripNullChars(ext);
118
119 for (auto &rhiExt : QRhiVulkanInitParams::preferredInstanceExtensions()) {
120 if (!extensions.contains(rhiExt))
121 extensions.append(rhiExt);
122 }
123
124 m_vulkanInstance.setExtensions(extensions);
125
126 // Multiview is a Vulkan 1.1 feature and won't work without setting up the instance accordingly.
127 const QVersionNumber supportedVersion = m_vulkanInstance.supportedApiVersion();
128 if (supportedVersion >= QVersionNumber(1, 3))
129 m_vulkanInstance.setApiVersion(QVersionNumber(1, 3));
130 else if (supportedVersion >= QVersionNumber(1, 2))
131 m_vulkanInstance.setApiVersion(QVersionNumber(1, 2));
132 else if (supportedVersion >= QVersionNumber(1, 1))
133 m_vulkanInstance.setApiVersion(QVersionNumber(1, 1));
134
135 if (quickConfig.isDebugLayerEnabled())
136 m_vulkanInstance.setLayers({ "VK_LAYER_LUNARG_standard_validation" });
137
138 if (!m_vulkanInstance.create()) {
139 qWarning("Quick 3D XR: Failed to create Vulkan instance");
140 return false;
141 }
142
143 // Get Vulkan device extensions
144 PFN_xrGetVulkanDeviceExtensionsKHR pfnGetVulkanDeviceExtensionsKHR = nullptr;
145 OpenXRHelpers::checkXrResult(xrGetInstanceProcAddr(instance,
146 "xrGetVulkanDeviceExtensionsKHR",
147 reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanDeviceExtensionsKHR)),
148 instance);
149
150 uint32_t deviceExtensionNamesSize = 0;
151 OpenXRHelpers::checkXrResult(pfnGetVulkanDeviceExtensionsKHR(instance,
152 systemId,
153 0,
154 &deviceExtensionNamesSize,
155 nullptr),
156 instance);
157 QByteArray deviceExtensionNames;
158 deviceExtensionNames.resize(deviceExtensionNamesSize);
159 OpenXRHelpers::checkXrResult(pfnGetVulkanDeviceExtensionsKHR(instance,
160 systemId,
161 deviceExtensionNamesSize,
162 &deviceExtensionNamesSize,
163 deviceExtensionNames.data()),
164 instance);
165
166 auto deviceExtensions = deviceExtensionNames.split(' ');
167 for (auto &ext : deviceExtensions) {
168 ext = stripNullChars(ext);
169 }
170 m_graphicsConfiguration.setDeviceExtensions(deviceExtensions);
171
172 // Get the Vulkan Graphics Device
173 PFN_xrGetVulkanGraphicsDeviceKHR pfnGetVulkanGraphicsDeviceKHR = nullptr;
174 OpenXRHelpers::checkXrResult(xrGetInstanceProcAddr(instance, "xrGetVulkanGraphicsDeviceKHR",
175 reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanGraphicsDeviceKHR)), instance);
176
177 OpenXRHelpers::checkXrResult(pfnGetVulkanGraphicsDeviceKHR(instance, systemId, m_vulkanInstance.vkInstance(), &m_vulkanPhysicalDevice), instance);
178
179 return true;
180}
181
183{
184 const QRhiVulkanNativeHandles *vulkanRhi = static_cast<const QRhiVulkanNativeHandles *>(rhi->nativeHandles());
185 m_vulkanDevice = vulkanRhi->dev;
186 Q_ASSERT(m_vulkanPhysicalDevice == vulkanRhi->physDev);
187 m_vulkanCommandQueue = vulkanRhi->gfxQueue;
188 m_queueFamilyIndex = vulkanRhi->gfxQueueFamilyIdx;
189
190 m_graphicsBinding.instance = m_vulkanInstance.vkInstance();
191 m_graphicsBinding.physicalDevice = m_vulkanPhysicalDevice;
192 m_graphicsBinding.device = m_vulkanDevice;
193 m_graphicsBinding.queueFamilyIndex = m_queueFamilyIndex;
194 m_graphicsBinding.queueIndex = 0;
195
196 m_rhi = rhi;
197
198 return true;
199}
200
201
202int64_t QOpenXRGraphicsVulkan::colorSwapchainFormat(const QVector<int64_t> &swapchainFormats) const
203{
204 // List of supported color swapchain formats.
205 constexpr int64_t supportedColorSwapchainFormats[] = {
206 VK_FORMAT_B8G8R8A8_SRGB,
207 VK_FORMAT_R8G8B8A8_SRGB,
208 VK_FORMAT_B8G8R8A8_UNORM,
209 VK_FORMAT_R8G8B8A8_UNORM
210 };
211
212 auto swapchainFormatIt = std::find_first_of(std::begin(supportedColorSwapchainFormats),
213 std::end(supportedColorSwapchainFormats),
214 swapchainFormats.begin(),
215 swapchainFormats.end());
216 return *swapchainFormatIt;
217}
218
219int64_t QOpenXRGraphicsVulkan::depthSwapchainFormat(const QVector<int64_t> &swapchainFormats) const
220{
221 // in order of preference
222 constexpr int64_t supportedDepthSwapchainFormats[] = {
223 VK_FORMAT_D24_UNORM_S8_UINT,
224 VK_FORMAT_D32_SFLOAT_S8_UINT,
225 VK_FORMAT_D32_SFLOAT,
226 VK_FORMAT_D16_UNORM
227 };
228
229 // order matters, we prefer D24S8 above all the others
230 return *std::find_first_of(std::begin(supportedDepthSwapchainFormats),
231 std::end(supportedDepthSwapchainFormats),
232 swapchainFormats.begin(),
233 swapchainFormats.end());
234}
235
236QVector<XrSwapchainImageBaseHeader*> QOpenXRGraphicsVulkan::allocateSwapchainImages(int count, XrSwapchain swapchain)
237{
238 QVector<XrSwapchainImageBaseHeader*> swapchainImages;
239 QVector<XrSwapchainImageVulkanKHR> swapchainImageBuffer(count);
240 for (XrSwapchainImageVulkanKHR& image : swapchainImageBuffer) {
241 image.type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR;
242 swapchainImages.push_back(reinterpret_cast<XrSwapchainImageBaseHeader*>(&image));
243 }
244 m_swapchainImageBuffer.insert(swapchain, swapchainImageBuffer);
245 return swapchainImages;
246}
247
248
249QQuickRenderTarget QOpenXRGraphicsVulkan::renderTarget(const XrSwapchainSubImage &subImage,
250 const XrSwapchainImageBaseHeader *swapchainImage,
251 quint64 swapchainFormat,
252 int samples,
253 int arraySize,
254 const XrSwapchainImageBaseHeader *depthSwapchainImage,
255 quint64 depthSwapchainFormat) const
256{
257 VkImage colorTexture = reinterpret_cast<const XrSwapchainImageVulkanKHR*>(swapchainImage)->image;
258
259 VkFormat viewFormat = VkFormat(swapchainFormat);
260 switch (swapchainFormat) {
261 case VK_FORMAT_R8G8B8A8_SRGB:
262 viewFormat = VK_FORMAT_R8G8B8A8_UNORM;
263 break;
264 case VK_FORMAT_B8G8R8A8_SRGB:
265 viewFormat = VK_FORMAT_B8G8R8A8_UNORM;
266 break;
267 default:
268 break;
269 }
270
271 QQuickRenderTarget::Flags flags;
272 if (samples > 1)
274
275 const QSize pixelSize(subImage.imageRect.extent.width, subImage.imageRect.extent.height);
276 QQuickRenderTarget rt = QQuickRenderTarget::fromVulkanImage(colorTexture,
277 VK_IMAGE_LAYOUT_UNDEFINED,
278 VkFormat(swapchainFormat),
279 viewFormat,
280 pixelSize,
281 samples,
282 arraySize,
283 flags);
284 if (depthSwapchainImage) {
285 // There might be issues with stencil when MSAA is not used and the
286 // format is D16 or D32F or the half-unsupported D32FS8 (because then
287 // the OpenXR-provided texture is the one and only depth-stencil buffer,
288 // perhaps without stencil). But we prefer D24S8 whenever that's
289 // available, so hopefully this problem won't come up in practice.
291 switch (depthSwapchainFormat) {
292 case VK_FORMAT_D32_SFLOAT_S8_UINT:
293 case VK_FORMAT_D32_SFLOAT:
295 break;
296 case VK_FORMAT_D16_UNORM:
298 break;
299 }
300 VkImage depthImage = reinterpret_cast<const XrSwapchainImageVulkanKHR*>(depthSwapchainImage)->image;
301 if (m_depthTexture && (m_depthTexture->format() != format || m_depthTexture->pixelSize() != pixelSize || m_depthTexture->arraySize() != arraySize)) {
302 delete m_depthTexture;
303 m_depthTexture = nullptr;
304 }
305 if (!m_depthTexture) {
306 // this is never multisample, QQuickRt takes care of resolving depth-stencil
307 if (arraySize > 1)
308 m_depthTexture = m_rhi->newTextureArray(format, arraySize, pixelSize, 1, QRhiTexture::RenderTarget);
309 else
310 m_depthTexture = m_rhi->newTexture(format, pixelSize, 1, QRhiTexture::RenderTarget);
311 }
312 m_depthTexture->createFrom({ quint64(depthImage), VK_IMAGE_LAYOUT_UNDEFINED });
313 rt.setDepthTexture(m_depthTexture);
314 }
315 return rt;
316}
317
319{
320 quickWindow->setGraphicsDevice(QQuickGraphicsDevice::fromPhysicalDevice(m_vulkanPhysicalDevice));
321 quickWindow->setGraphicsConfiguration(m_graphicsConfiguration);
322 quickWindow->setVulkanInstance(&m_vulkanInstance);
323}
324
326{
327 delete m_depthTexture;
328 m_depthTexture = nullptr;
329}
330
\inmodule QtCore
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
QList< QByteArray > split(char sep) const
Splits the byte array into subarrays wherever sep occurs, and returns the list of those arrays.
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
int64_t colorSwapchainFormat(const QVector< int64_t > &swapchainFormats) const override
const char * extensionName() const override
bool isExtensionSupported(const QVector< XrExtensionProperties > &extensions) const override
bool finializeGraphics(QRhi *rhi) override
void setupWindow(QQuickWindow *quickWindow) override
QQuickRenderTarget renderTarget(const XrSwapchainSubImage &subImage, const XrSwapchainImageBaseHeader *swapchainImage, quint64 swapchainFormat, int samples, int arraySize, const XrSwapchainImageBaseHeader *depthSwapchainImage, quint64 depthSwapchainFormat) const override
int64_t depthSwapchainFormat(const QVector< int64_t > &swapchainFormats) const override
const XrBaseInStructure * handle() const override
QVector< XrSwapchainImageBaseHeader * > allocateSwapchainImages(int count, XrSwapchain swapchain) override
bool setupGraphics(const XrInstance &instance, XrSystemId &systemId, const QQuickGraphicsConfiguration &quickConfig) override
QQuickGraphicsConfiguration controls lower level graphics settings for the QQuickWindow.
The QQuickRenderTarget class provides an opaque container for native graphics resources specifying a ...
void setDepthTexture(QRhiTexture *texture)
Requests using the given texture as the depth or depth-stencil buffer.
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
Format format() const
Definition qrhi.h:972
int arraySize() const
Definition qrhi.h:981
@ RenderTarget
Definition qrhi.h:898
Format
Specifies the texture format.
Definition qrhi.h:914
virtual bool createFrom(NativeTexture src)
Similar to create(), except that no new native textures are created.
Definition qrhi.cpp:4487
QSize pixelSize() const
Definition qrhi.h:975
\variable QRhiVulkanInitParams::inst
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
QRhiTexture * newTextureArray(QRhiTexture::Format format, int arraySize, const QSize &pixelSize, int sampleCount=1, QRhiTexture::Flags flags={})
Definition qrhi.cpp:10636
QRhiTexture * newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount=1, QRhiTexture::Flags flags={})
Definition qrhi.cpp:10562
const QRhiNativeHandles * nativeHandles()
Definition qrhi.cpp:10137
\inmodule QtCore
Definition qsize.h:25
\inmodule QtCore
void extension()
[6]
Definition dialogs.cpp:230
bool checkXrResult(XrResult result, XrInstance instance)
Combined button and popup list for selecting options.
Definition image.cpp:4
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#define qWarning
Definition qlogging.h:166
GLsizei samples
GLuint GLuint end
GLenum GLenum GLsizei count
GLbitfield flags
GLint GLsizei GLsizei GLenum format
GLsizei const GLchar *const * string
[0]
Definition qopenglext.h:694
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
unsigned int quint32
Definition qtypes.h:50
unsigned long long quint64
Definition qtypes.h:61