1 | // Copyright (C) 2024 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qtquick3dxrglobal_p.h" |
5 | #include "qquick3dxritem_p.h" |
6 | #include "qquick3dxrview_p.h" |
7 | #include <QQuickWindow> |
8 | #include <QQuickItem> |
9 | #include <QLoggingCategory> |
10 | |
11 | #include "qquick3dxrinputmanager_p.h" |
12 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | Q_DECLARE_LOGGING_CATEGORY(lcQuick3DXr); |
16 | |
17 | /*! |
18 | \qmltype XrView |
19 | \inherits Node |
20 | \inqmlmodule QtQuick3D.Xr |
21 | \brief Sets up the view for an Xr application. |
22 | |
23 | An XrView sets up the view for an XR application. |
24 | The following snippet is from the \l{\qxr Simple Example} and shows |
25 | how to use the type. |
26 | |
27 | \quotefromfile xr_simple/main.qml |
28 | \printto XrOrigin |
29 | |
30 | \section1 Platform notes |
31 | |
32 | \section2 Meta Quest Devices |
33 | |
34 | To \l{XrView::passthroughEnabled}{enable passthrough} you need to add the |
35 | following permisson your app's \c AndroidManifest.xml file: |
36 | |
37 | \badcode |
38 | <uses-feature android:name="com.oculus.feature.PASSTHROUGH" android:required="false"/> |
39 | \endcode |
40 | |
41 | */ |
42 | |
43 | QQuick3DXrView::QQuick3DXrView() |
44 | : m_xrRuntimeInfo(&m_xrManager) |
45 | { |
46 | init(); |
47 | } |
48 | |
49 | QQuick3DXrView::~QQuick3DXrView() |
50 | { |
51 | m_inDestructor = true; |
52 | } |
53 | |
54 | /*! |
55 | \qmlproperty XrOrigin QtQuick3D.Xr::XrView::xrOrigin |
56 | \brief Holds the active XR origin. |
57 | |
58 | The XR origin is the point in the scene that is considered the origin of the |
59 | XR coordinate system. The XR origin is used to position tracked objects like |
60 | the camera and controllers in the scene. An application can have multiple XrOrigins |
61 | but only one can be active at a time. |
62 | |
63 | \note This property must be set for the scene to be rendered in XR. |
64 | |
65 | \sa XrOrigin |
66 | */ |
67 | |
68 | QQuick3DXrOrigin *QQuick3DXrView::xrOrigin() const |
69 | { |
70 | return m_xrOrigin; |
71 | } |
72 | |
73 | /*! |
74 | \qmlproperty SceneEnvironment QtQuick3D.Xr::XrView::environment |
75 | \summary Holds the SceneEnvironment for the XR view. |
76 | */ |
77 | |
78 | QQuick3DSceneEnvironment *QQuick3DXrView::environment() const |
79 | { |
80 | return m_xrManager.m_vrViewport ? m_xrManager.m_vrViewport->environment() : nullptr; |
81 | } |
82 | |
83 | QQuick3DViewport *QQuick3DXrView::view3d() const |
84 | { |
85 | return m_xrManager.m_vrViewport; |
86 | } |
87 | |
88 | /*! |
89 | \qmlproperty bool QtQuick3D.Xr::XrView::passthroughEnabled |
90 | \summary Holds whether passthrough is enabled for the XR view. |
91 | */ |
92 | bool QQuick3DXrView::passthroughEnabled() const |
93 | { |
94 | return m_xrManager.isPassthroughEnabled(); |
95 | } |
96 | |
97 | /*! |
98 | \qmlproperty QQuick3DXrRuntimeInfo QtQuick3D.Xr::XrView::runtimeInfo |
99 | \summary Provides information about the XR runtime for the XR view. |
100 | */ |
101 | |
102 | QQuick3DXrRuntimeInfo *QQuick3DXrView::runtimeInfo() const |
103 | { |
104 | return &m_xrRuntimeInfo; |
105 | } |
106 | |
107 | void QQuick3DXrView::setEnvironment(QQuick3DSceneEnvironment *environment) |
108 | { |
109 | QQuick3DViewport *view = m_xrManager.m_vrViewport; |
110 | |
111 | // If the view is not created yet, we can't set the environment which means we need to |
112 | // set it again once the view is created... |
113 | if (!view) { |
114 | m_pendingSceneEnvironment = environment; |
115 | return; |
116 | } |
117 | |
118 | auto oldEnvironment = view->environment(); |
119 | if (oldEnvironment == environment) |
120 | return; |
121 | |
122 | if (oldEnvironment) |
123 | disconnect(receiver: oldEnvironment); |
124 | |
125 | view->setEnvironment(environment); |
126 | |
127 | // The view will always have an environment, setting the environment to null will just mean the default environment |
128 | // is used. So querying the environment from the view is always valid (and we should do it here to make sure we're |
129 | // in sync with the view). |
130 | environment = view->environment(); |
131 | |
132 | handleClearColorChanged(); |
133 | handleAAChanged(); |
134 | |
135 | connect(sender: environment, signal: &QQuick3DSceneEnvironment::backgroundModeChanged, context: this, slot: &QQuick3DXrView::handleClearColorChanged); |
136 | connect(sender: environment, signal: &QQuick3DSceneEnvironment::clearColorChanged, context: this, slot: &QQuick3DXrView::handleClearColorChanged); |
137 | connect(sender: environment, signal: &QQuick3DSceneEnvironment::antialiasingModeChanged, context: this, slot: &QQuick3DXrView::handleAAChanged); |
138 | connect(sender: environment, signal: &QQuick3DSceneEnvironment::antialiasingQualityChanged, context: this, slot: &QQuick3DXrView::handleAAChanged); |
139 | |
140 | emit environmentChanged(environment); |
141 | } |
142 | |
143 | /*! |
144 | \qmlproperty bool QtQuick3D.Xr::XrView::passthroughSupported |
145 | \summary Indicates whether passthrough is supported for the XR view. |
146 | */ |
147 | |
148 | bool QQuick3DXrView::passthroughSupported() const |
149 | { |
150 | if (!m_xrManager.isValid()) |
151 | return false; |
152 | |
153 | return m_xrManager.supportsPassthrough(); |
154 | } |
155 | |
156 | void QQuick3DXrView::setPassthroughEnabled(bool enable) |
157 | { |
158 | if (!m_xrManager.isValid()) { |
159 | qWarning(msg: "Attempted to set passthrough mode without a valid XR manager"); |
160 | return; |
161 | } |
162 | |
163 | // bail if passthrough is not supported |
164 | if (enable && !m_xrManager.supportsPassthrough()) { |
165 | qWarning(msg: "Enabling Passthrough is not supported."); |
166 | return; |
167 | } |
168 | |
169 | const bool orgPassthroughEnabled = m_xrManager.isPassthroughEnabled(); |
170 | m_xrManager.setPassthroughEnabled(enable); |
171 | |
172 | if (orgPassthroughEnabled != m_xrManager.isPassthroughEnabled()) |
173 | emit passthroughEnabledChanged(); |
174 | } |
175 | |
176 | /*! |
177 | \qmlproperty enumeration QtQuick3D.Xr::XrView::fixedFoveation |
178 | \brief Controls the level of fixed foveated rendering for the XrView. |
179 | \default XrView.HighFoveation |
180 | |
181 | Foveated rendering reduces GPU load by reducing image quality (resolution) |
182 | in areas where the difference is less perceptible to the eye. With fixed |
183 | foveated rendering, the areas with reduced visual fidelity are fixed and do |
184 | not change. On some platforms, there is no concept of fixed foveated |
185 | rendering or control over it. For example, VisionOS-based devices perform |
186 | dynamic, eye-tracked foveation; thus, the value of this property is |
187 | ignored in practice. Other devices, such as the Meta Quest 3, only |
188 | support fixed foveation, which makes this property relevant. |
189 | |
190 | The value can be one of: |
191 | \value XrView.NoFoveation 0, no foveation. |
192 | \value XrView.LowFoveation 1, low foveation. |
193 | \value XrView.MediumFoveation 2, medium foveation. |
194 | \value XrView.HighFoveation 3, high foveation. |
195 | |
196 | Where supported, the default is \c HighFoveation. Therefore, changing this |
197 | value in applications should be rarely needed in practice. |
198 | */ |
199 | |
200 | QQuick3DXrView::FoveationLevel QQuick3DXrView::fixedFoveation() const |
201 | { |
202 | return FoveationLevel(m_xrManager.getFixedFoveationLevel()); |
203 | } |
204 | |
205 | void QQuick3DXrView::setFixedFoveation(FoveationLevel level) |
206 | { |
207 | const auto orgFoviationLevel = m_xrManager.getFixedFoveationLevel(); |
208 | m_xrManager.setFixedFoveationLevel(QtQuick3DXr::FoveationLevel(level)); |
209 | if (orgFoviationLevel != m_xrManager.getFixedFoveationLevel()) |
210 | emit fixedFoveationChanged(); |
211 | } |
212 | |
213 | /*! |
214 | \qmlproperty bool QtQuick3D.Xr::XrView::isQuitOnSessionEndEnabled |
215 | \brief Holds whether the application should quit when the XR session ends. |
216 | */ |
217 | |
218 | bool QQuick3DXrView::isQuitOnSessionEndEnabled() const |
219 | { |
220 | return m_quitOnSessionEnd; |
221 | } |
222 | |
223 | void QQuick3DXrView::setQuitOnSessionEnd(bool enable) |
224 | { |
225 | if (m_quitOnSessionEnd == enable) |
226 | return; |
227 | |
228 | m_quitOnSessionEnd = enable; |
229 | emit quitOnSessionEndChanged(); |
230 | } |
231 | /*! |
232 | \qmlproperty RenderStats QtQuick3D.Xr::XrView::renderStats |
233 | \summary Holds rendering statistics for the XR view. |
234 | */ |
235 | |
236 | QQuick3DRenderStats *QQuick3DXrView::renderStats() const |
237 | { |
238 | return m_xrManager.m_vrViewport ? m_xrManager.m_vrViewport->renderStats() : nullptr; |
239 | } |
240 | |
241 | void QQuick3DXrView::updateViewportGeometry() |
242 | { |
243 | auto contentItem = m_xrManager.m_quickWindow->contentItem(); |
244 | auto viewport = m_xrManager.m_vrViewport; |
245 | if (viewport->height() != contentItem->height()) |
246 | viewport->setHeight(contentItem->height()); |
247 | if (viewport->width() != contentItem->width()) |
248 | viewport->setWidth(contentItem->width()); |
249 | if (viewport->x() != contentItem->x()) |
250 | viewport->setX(contentItem->x()); |
251 | if (viewport->y() != contentItem->y()) |
252 | viewport->setY(contentItem->y()); |
253 | } |
254 | |
255 | void QQuick3DXrView::handleSessionEnded() |
256 | { |
257 | emit sessionEnded(); |
258 | if (m_quitOnSessionEnd) |
259 | QCoreApplication::quit(); |
260 | } |
261 | |
262 | void QQuick3DXrView::handleClearColorChanged() |
263 | { |
264 | auto env = environment(); |
265 | |
266 | if (env) { |
267 | if (env->backgroundMode() == QQuick3DSceneEnvironment::Color) |
268 | m_xrManager.m_quickWindow->setColor(env->clearColor()); |
269 | else if (env->backgroundMode() == QQuick3DSceneEnvironment::Transparent) |
270 | m_xrManager.m_quickWindow->setColor(Qt::transparent); |
271 | } |
272 | } |
273 | |
274 | void QQuick3DXrView::handleAAChanged() |
275 | { |
276 | auto env = environment(); |
277 | int samples = 1; |
278 | if (env && env->antialiasingMode() == QQuick3DSceneEnvironment::MSAA) { |
279 | switch (env->antialiasingQuality()) { |
280 | case QQuick3DSceneEnvironment::Medium: |
281 | samples = 2; |
282 | break; |
283 | case QQuick3DSceneEnvironment::High: |
284 | samples = 4; |
285 | break; |
286 | case QQuick3DSceneEnvironment::VeryHigh: |
287 | samples = 8; |
288 | break; |
289 | } |
290 | } |
291 | m_xrManager.setSamples(samples); |
292 | } |
293 | |
294 | bool QQuick3DXrView::init() |
295 | { |
296 | if (m_isInitialized) { |
297 | qWarning(msg: "Already initialized!"); |
298 | return false; |
299 | } |
300 | |
301 | connect(sender: &m_xrManager, signal: &QQuick3DXrManager::sessionEnded, context: this, slot: &QQuick3DXrView::handleSessionEnded); |
302 | connect(sender: &m_xrManager, signal: &QQuick3DXrManager::frameReady, context: this, slot: &QQuick3DXrView::frameReady); |
303 | connect(sender: &m_xrManager, signal: &QQuick3DXrManager::referenceSpaceChanged, context: this, slot: &QQuick3DXrView::referenceSpaceChanged); |
304 | connect(sender: &m_xrManager, signal: &QQuick3DXrManager::multiViewRenderingEnabledChanged, context: this, slot: &QQuick3DXrView::multiViewRenderingEnabledChanged); |
305 | connect(sender: &m_xrManager, signal: &QQuick3DXrManager::initialized, context: this, slot: &QQuick3DXrView::init, type: Qt::UniqueConnection); |
306 | |
307 | if (!m_xrManager.isReady() && !m_xrManager.initialize()) { |
308 | qCDebug(lcQuick3DXr, "Waiting for XR platform to be initialized"); |
309 | |
310 | return false; |
311 | } |
312 | |
313 | if (!m_xrManager.initialize()) { |
314 | QString errorString = m_xrManager.errorString(); |
315 | if (errorString.isEmpty()) |
316 | errorString = tr(s: "Failed to initialize XR platform"); |
317 | qWarning(msg: "\n%s\n", qPrintable(errorString)); |
318 | QMetaObject::invokeMethod(obj: this, member: "initializeFailed", c: Qt::QueuedConnection, arguments&: errorString); |
319 | return false; |
320 | } |
321 | |
322 | // Create View3D |
323 | QSSG_CHECK_X(m_xrManager.m_vrViewport == nullptr, "View3D already created!"); |
324 | auto viewport = new QQuick3DViewport(QQuick3DViewport::PrivateInstanceType::XrViewInstance); |
325 | viewport->setRenderMode(QQuick3DViewport::Underlay); |
326 | auto contentItem = m_xrManager.m_quickWindow->contentItem(); |
327 | viewport->setParentItem(contentItem); |
328 | m_xrManager.m_vrViewport = viewport; |
329 | viewport->setImportScene(this); |
330 | |
331 | contentItem->forceActiveFocus(reason: Qt::MouseFocusReason); |
332 | |
333 | connect(sender: contentItem, signal: &QQuickItem::heightChanged, context: this, slot: &QQuick3DXrView::updateViewportGeometry); |
334 | connect(sender: contentItem, signal: &QQuickItem::widthChanged, context: this, slot: &QQuick3DXrView::updateViewportGeometry); |
335 | connect(sender: contentItem, signal: &QQuickItem::xChanged, context: this, slot: &QQuick3DXrView::updateViewportGeometry); |
336 | connect(sender: contentItem, signal: &QQuickItem::yChanged, context: this, slot: &QQuick3DXrView::updateViewportGeometry); |
337 | |
338 | QQuick3DSceneEnvironment *env = environment(); |
339 | if (env) { |
340 | connect(sender: env, signal: &QQuick3DSceneEnvironment::backgroundModeChanged, context: this, slot: &QQuick3DXrView::handleClearColorChanged); |
341 | connect(sender: env, signal: &QQuick3DSceneEnvironment::clearColorChanged, context: this, slot: &QQuick3DXrView::handleClearColorChanged); |
342 | connect(sender: env, signal: &QQuick3DSceneEnvironment::antialiasingModeChanged, context: this, slot: &QQuick3DXrView::handleAAChanged); |
343 | connect(sender: env, signal: &QQuick3DSceneEnvironment::antialiasingQualityChanged, context: this, slot: &QQuick3DXrView::handleAAChanged); |
344 | } |
345 | |
346 | // NOTE: If we've called async, we need to make sure the environment, etc. is set again |
347 | setEnvironment(m_pendingSceneEnvironment); |
348 | m_pendingSceneEnvironment = nullptr; |
349 | |
350 | m_xrManager.update(); |
351 | |
352 | m_isInitialized = true; |
353 | |
354 | return m_isInitialized; |
355 | } |
356 | |
357 | /*! |
358 | \qmlmethod pickResult XrView::rayPick(vector3d origin, vector3d direction) |
359 | |
360 | This method will \e shoot a ray into the scene starting at \a origin and in |
361 | \a direction and return information about the nearest intersection with an |
362 | object in the scene. |
363 | |
364 | For example, pass the position and forward vector of |
365 | any object in a scene to see what object is in front of an item. This |
366 | makes it possible to do picking from any point in the scene. |
367 | */ |
368 | QQuick3DPickResult QQuick3DXrView::rayPick(const QVector3D &origin, const QVector3D &direction) const |
369 | { |
370 | return m_xrManager.m_vrViewport->rayPick(origin, direction); |
371 | } |
372 | |
373 | /*! |
374 | \qmlmethod List<pickResult> XrView::rayPickAll(vector3d origin, vector3d direction) |
375 | |
376 | This method will \e shoot a ray into the scene starting at \a origin and in |
377 | \a direction and return a list of information about the nearest intersections with |
378 | objects in the scene. |
379 | The list is presorted by distance from the origin along the direction |
380 | vector with the nearest intersections appearing first and the furthest |
381 | appearing last. |
382 | |
383 | This can, for instance, be called with the position and forward vector of |
384 | any object in a scene to see what objects are in front of an item. This |
385 | makes it possible to do picking from any point in the scene. |
386 | */ |
387 | QList<QQuick3DPickResult> QQuick3DXrView::rayPickAll(const QVector3D &origin, const QVector3D &direction) const |
388 | { |
389 | return m_xrManager.m_vrViewport->rayPickAll(origin, direction); |
390 | } |
391 | |
392 | /*! |
393 | \qmlmethod XrView::setTouchpoint(Item target, point position, int pointId, bool pressed) |
394 | |
395 | Sends a synthetic touch event to \a target, moving the touch point with ID \a pointId to \a position, |
396 | with \a pressed determining if the point is pressed. |
397 | Also sends the appropriate touch release event if \a pointId was previously active on a different |
398 | item. |
399 | */ |
400 | |
401 | void QQuick3DXrView::setTouchpoint(QQuickItem *target, const QPointF &position, int pointId, bool pressed) |
402 | { |
403 | view3d()->setTouchpoint(target, position, pointId, active: pressed); |
404 | } |
405 | |
406 | // TODO: Maybe do a proper QQuick3DXrViewPrivate instead |
407 | struct QQuick3DXrView::XrTouchState |
408 | { |
409 | QHash<int, QQuick3DXrItem::TouchState> points; |
410 | }; |
411 | |
412 | /*! |
413 | \qmlmethod vector3d XrView::processTouch(vector3d position, int pointId) |
414 | |
415 | This method will search for an XrItem near \a position and send a virtual |
416 | touch event with touch point ID \a pointId if \a position maps to a point |
417 | on the surface. |
418 | |
419 | The return value is the offset between \a position and the touched point on |
420 | the surface. This can be used to prevent a hand model from passing through |
421 | an XrItem. |
422 | |
423 | \sa XrHandModel |
424 | |
425 | */ |
426 | |
427 | QVector3D QQuick3DXrView::processTouch(const QVector3D &pos, int pointId) |
428 | { |
429 | QVector3D offset; |
430 | if (m_xrItems.isEmpty()) |
431 | return offset; |
432 | |
433 | if (!m_touchState) |
434 | m_touchState = new XrTouchState; |
435 | QQuick3DXrItem::TouchState &state = m_touchState->points[pointId]; |
436 | state.pointId = pointId; // in case it's a new point that was default-constructed |
437 | |
438 | auto *prevTarget = state.target; |
439 | bool grabbed = false; |
440 | if (prevTarget) { |
441 | grabbed = prevTarget->handleVirtualTouch(view: this, pos, touchState: &state, offset: &offset); |
442 | } |
443 | for (auto *item : std::as_const(t&: m_xrItems)) { |
444 | if (grabbed) |
445 | break; |
446 | if (item != prevTarget) |
447 | grabbed = item->handleVirtualTouch(view: this, pos, touchState: &state, offset: &offset); |
448 | } |
449 | |
450 | return offset; |
451 | } |
452 | |
453 | /*! |
454 | \qmlmethod object XrView::touchpointState(int pointId) |
455 | |
456 | This method returns the state of the touch point with ID \a pointId. |
457 | The state is represented by a map from property names to values: |
458 | |
459 | \table |
460 | \header |
461 | \li Key |
462 | \li Type |
463 | \li Description |
464 | \row |
465 | \li \c grabbed |
466 | \li \c bool |
467 | \li Is the point grabbed by an item? If \c false, all other values are \c undefined. |
468 | \row |
469 | \li \c target |
470 | \li XrItem |
471 | \li The item that is grabbing the touch point. |
472 | \row |
473 | \li \c pressed |
474 | \li \c bool |
475 | \li Is the touch point pressed? |
476 | \row |
477 | \li \c cursorPos |
478 | \li \c point |
479 | \li The 2D position of the touch point within \c target |
480 | \row |
481 | \li \c touchDistance |
482 | \li \c real |
483 | \li The distance from the plane to the touch point. It will be \c 0 if \c pressed is \c true. |
484 | \endtable |
485 | |
486 | */ |
487 | |
488 | #define Q_TOUCHPOINT_STATE(prop) { QStringLiteral(#prop), QVariant::fromValue(it->prop) } |
489 | QVariantMap QQuick3DXrView::touchpointState(int pointId) const |
490 | { |
491 | auto constexpr end = QHash<int, QQuick3DXrItem::TouchState>::const_iterator(); |
492 | auto it = m_touchState ? m_touchState->points.constFind(key: pointId) : end; |
493 | |
494 | if (it == end) |
495 | return { { QStringLiteral("grabbed"), QVariant::fromValue(value: false) } }; |
496 | |
497 | return { Q_TOUCHPOINT_STATE(target), |
498 | Q_TOUCHPOINT_STATE(grabbed), |
499 | Q_TOUCHPOINT_STATE(pressed), |
500 | Q_TOUCHPOINT_STATE(cursorPos), |
501 | Q_TOUCHPOINT_STATE(touchDistance) }; |
502 | } |
503 | #undef Q_TOUCHPOINT_STATE |
504 | |
505 | /*! |
506 | \qmlproperty enumeration QtQuick3D.Xr::XrView::referenceSpace |
507 | \brief Gets or sets the reference space for the XR view. |
508 | |
509 | It can be one of: |
510 | \value XrView.ReferenceSpaceUnknown |
511 | \value XrView.ReferenceSpaceLocal |
512 | \value XrView.ReferenceSpaceStage |
513 | \value XrView.ReferenceSpaceLocalFloor |
514 | */ |
515 | |
516 | QQuick3DXrView::ReferenceSpace QQuick3DXrView::referenceSpace() const |
517 | { |
518 | return ReferenceSpace(m_xrManager.getReferenceSpace()); |
519 | } |
520 | |
521 | void QQuick3DXrView::setReferenceSpace(ReferenceSpace newReferenceSpace) |
522 | { |
523 | m_xrManager.setReferenceSpace(QtQuick3DXr::ReferenceSpace(newReferenceSpace)); |
524 | } |
525 | |
526 | bool QQuick3DXrView::depthSubmissionEnabled() const |
527 | { |
528 | if (!m_xrManager.isValid()) { |
529 | qWarning(msg: "Attempted to check depth submission mode without a valid XR manager"); |
530 | return false; |
531 | } |
532 | |
533 | return m_xrManager.isDepthSubmissionEnabled(); |
534 | } |
535 | |
536 | /*! |
537 | \qmlproperty bool QtQuick3D.Xr::XrView::multiViewRenderingSupported |
538 | |
539 | \brief This read-only property reports the availability of \l{Multiview Rendering}. |
540 | |
541 | \sa multiViewRenderingEnabled |
542 | */ |
543 | bool QQuick3DXrView::isMultiViewRenderingSupported() const |
544 | { |
545 | if (!m_xrManager.isValid()) |
546 | return false; |
547 | |
548 | return m_xrManager.isMultiViewRenderingSupported(); |
549 | } |
550 | |
551 | /*! |
552 | \qmlproperty bool QtQuick3D.Xr::XrView::multiViewRenderingEnabled |
553 | |
554 | \brief This is a read-only property that reports if \l{Multiview Rendering} is enabled for the XR view. |
555 | |
556 | \default true |
557 | |
558 | This property tells you if multiview rendering is actually in use at run time. |
559 | When not supported, the value will flip back to \c false. |
560 | |
561 | Enabling multiview rendering is recommended. It can improve performance and reduce |
562 | CPU and GPU power usage. It defaults to disabled to ensure maximum |
563 | compatibility. Developers are encouraged to verify that their application |
564 | renders as expected with multiViewRenderingEnabled set to \c true and then |
565 | leave it set afterward. |
566 | |
567 | \note Certain Qt Quick and Quick 3D features that involve shader code that is |
568 | provided by the application may need this code to be modified to be multiview |
569 | compatible. Examples of these are custom 2D and 3D materials and |
570 | postprocessing effects. The \l {Multiview Rendering} documentation provides |
571 | more information on this and how to disable multiview rendering. |
572 | |
573 | \sa multiViewRenderingSupported {Multiview Rendering} |
574 | */ |
575 | bool QQuick3DXrView::multiViewRenderingEnabled() const |
576 | { |
577 | if (!m_xrManager.isValid()) |
578 | return false; |
579 | |
580 | return m_xrManager.isMultiViewRenderingEnabled(); |
581 | } |
582 | |
583 | void QQuick3DXrView::registerXrItem(QQuick3DXrItem *newXrItem) |
584 | { |
585 | m_xrItems.append(t: newXrItem); |
586 | } |
587 | |
588 | void QQuick3DXrView::unregisterXrItem(QQuick3DXrItem *xrItem) |
589 | { |
590 | m_xrItems.removeAll(t: xrItem); |
591 | } |
592 | |
593 | /*! |
594 | \qmlproperty bool QtQuick3D.Xr::XrView::depthSubmissionEnabled |
595 | \brief Controls whether submitting the depth buffer to the XR compositor |
596 | is enabled. |
597 | \default false |
598 | |
599 | By default, the depth buffer used by the 3D scene in the XrView is not exposed |
600 | to the XR compositor. However, in some platforms, depth submission is implicit |
601 | and cannot be disabled or controlled by the application. An example of this is |
602 | VisionOS. Changing this property has no effect on those platforms. Elsewhere, |
603 | with OpenXR in particular, support depends on the OpenXR implementation used |
604 | at run time. |
605 | |
606 | It is always safe to set depthSubmissionEnabled to \c true. It will just have |
607 | no effect when not supported by the underlying stack. To be sure, you can |
608 | inspect the debug output to see if depth submission is in use. |
609 | Submitting the depth buffer may improve reprojections that the XR compositor |
610 | may perform. Reprojection could happen, for example, when the system cannot |
611 | maintain the target frame rate and thus has to resort to predicting frame |
612 | contents to improve and stabilize the user's perception of the |
613 | scene and reduce possible motion sickness. However, the application and |
614 | Qt have no control over data usage. It could also happen that |
615 | submitting depth data has no practical effects and is ignored by the |
616 | underlying XR runtime and compositor. |
617 | |
618 | In practice, submitting the depth buffer implies rendering into a depth |
619 | texture provided by the XR runtime instead of the intermediate texture/render buffer |
620 | created and managed by Qt. Rendering into a depth texture has certain lower-level |
621 | consequences that can have a performance impact: |
622 | |
623 | When using \l{QtQuick3D::SceneEnvironment::antialiasingMode}{multisample antialiasing} |
624 | (MSAA), enabling depth submission implies rendering into a multisample depth |
625 | texture and resolving the samples into the non-multisample depth texture provided by |
626 | the XR runtime. Without depth submission, |
627 | the resolve step would not be necessary. In addition, some 3D APIs |
628 | do not support resolving multisample depth-stencil data (see |
629 | the \l{QRhi::ResolveDepthStencil} flag for details). Without this support, |
630 | attempts to enable depth submission in combination with MSAA are gracefully ignored. |
631 | |
632 | Even when MSAA is not used, enabling depth submission triggers writing out |
633 | depth data with 3D APIs that have control over this. The store operation for |
634 | depth/stencil data is typically indicated by Qt as unnecessary, which can |
635 | have positive performance impacts on tiled GPU architectures. This is not |
636 | done with depth submission because depth data must always be written out |
637 | from Qt's perspective. |
638 | |
639 | \note We recommended that developers test their applications with depth |
640 | submission enabled, evaluate the advantages and disadvantages, and make a |
641 | conscious choice based on their testing if they wish to enable it or not. |
642 | */ |
643 | |
644 | void QQuick3DXrView::setDepthSubmissionEnabled(bool enable) |
645 | { |
646 | if (!m_xrManager.isValid()) { |
647 | qWarning(msg: "Attempted to set depth submission mode without a valid XR manager"); |
648 | return; |
649 | } |
650 | |
651 | const bool orgDepthSubmission = m_xrManager.isDepthSubmissionEnabled(); |
652 | |
653 | m_xrManager.setDepthSubmissionEnabled(enable); |
654 | |
655 | if (orgDepthSubmission != m_xrManager.isDepthSubmissionEnabled()) |
656 | emit depthSubmissionEnabledChanged(); |
657 | } |
658 | |
659 | void QQuick3DXrView::setMultiViewRenderingEnabled(bool enable) |
660 | { |
661 | Q_UNUSED(enable); |
662 | qWarning(msg: "Setting multiViewRenderingEnabled is not supported."); |
663 | } |
664 | |
665 | void QQuick3DXrView::setXROrigin(QQuick3DXrOrigin *newXrOrigin) |
666 | { |
667 | if (m_xrOrigin == newXrOrigin) |
668 | return; |
669 | |
670 | QQuick3DObjectPrivate::attachWatcher(context: this, setter: &QQuick3DXrView::setXROrigin, newO: newXrOrigin, oldO: m_xrOrigin); |
671 | |
672 | m_xrOrigin = newXrOrigin; |
673 | |
674 | // Make sure the XrOrigin has a parent item, if it hasn't, we're it. |
675 | if (m_xrOrigin && !m_xrOrigin->parentItem()) |
676 | m_xrOrigin->setParentItem(this); |
677 | |
678 | m_xrManager.setXROrigin(m_xrOrigin); |
679 | |
680 | emit xrOriginChanged(); |
681 | } |
682 | |
683 | /*! |
684 | \qmlsignal XrView::initializeFailed(const QString &errorString) |
685 | |
686 | Emitted when initialization fails, and there is a new \a errorString |
687 | describing the failure. |
688 | */ |
689 | |
690 | /*! |
691 | \qmlsignal XrView::sessionEnded() |
692 | |
693 | Emitted when the session ends. |
694 | */ |
695 | |
696 | /*! |
697 | \qmlsignal XrView::frameReady() |
698 | \internal |
699 | |
700 | Emitted when a new frame is ready. |
701 | */ |
702 | |
703 | QT_END_NAMESPACE |
704 |
Definitions
- QQuick3DXrView
- ~QQuick3DXrView
- xrOrigin
- environment
- view3d
- passthroughEnabled
- runtimeInfo
- setEnvironment
- passthroughSupported
- setPassthroughEnabled
- fixedFoveation
- setFixedFoveation
- isQuitOnSessionEndEnabled
- setQuitOnSessionEnd
- renderStats
- updateViewportGeometry
- handleSessionEnded
- handleClearColorChanged
- handleAAChanged
- init
- rayPick
- rayPickAll
- setTouchpoint
- XrTouchState
- processTouch
- touchpointState
- referenceSpace
- setReferenceSpace
- depthSubmissionEnabled
- isMultiViewRenderingSupported
- multiViewRenderingEnabled
- registerXrItem
- unregisterXrItem
- setDepthSubmissionEnabled
- setMultiViewRenderingEnabled
Learn Advanced QML with KDAB
Find out more