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