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
qquick3dxrmanager_visionos.mm
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
9
11
12#include <QtQuick3D/private/qquick3dviewport_p.h>
13#include <QtQuick3D/private/qquick3dnode_p_p.h>
14
15#include <QtQuick3DUtils/private/qssgassert_p.h>
16
17#include <QQuickGraphicsDevice>
18#include <rhi/qrhi.h>
19
20#include <CompositorServices/CompositorServices.h>
21#include <QtGui/qguiapplication_platform.h>
22
23#include <QtCore/qoperatingsystemversion.h>
24
26
27class CompositorLayer : public QObject, public QNativeInterface::QVisionOSApplication::ImmersiveSpaceCompositorLayer
28{
30public:
31 void configure(cp_layer_renderer_capabilities_t capabilities, cp_layer_renderer_configuration_t configuration) const override
32 {
33 // NOTE: foveation is disabled for now
34 const bool supportsFoveation = false && cp_layer_renderer_capabilities_supports_foveation(capabilities);
35
36 cp_layer_renderer_configuration_set_layout(configuration, cp_layer_renderer_layout_dedicated);
37 cp_layer_renderer_configuration_set_foveation_enabled(configuration, supportsFoveation);
38 cp_layer_renderer_configuration_set_color_format(configuration, MTLPixelFormatRGBA16Float);
39 }
40
41 void render(cp_layer_renderer_t renderer) override
42 {
43 if (m_layerRenderer != renderer) {
44 m_layerRenderer = renderer;
46 }
47
49 }
50
51 void handleSpatialEvents(const QJsonObject &events) override
52 {
54 }
55
56 void setActive() { m_active = true; };
57 bool isActive() const { return m_active; }
58
59 cp_layer_renderer_t layerRenderer() const
60 {
61 return m_layerRenderer;
62 }
63
68
69private:
70 cp_layer_renderer_t m_layerRenderer = nullptr;
71 bool m_active = false;
72};
73
74Q_GLOBAL_STATIC(CompositorLayer, s_compositorLayer)
75
77 : q_ptr(&manager)
78{
79}
80
82{
83 ar_session_stop(m_arSession);
84}
85
87{
88 QSSG_ASSERT(manager != nullptr, return nullptr);
89 return manager->d_func();
90}
91
92bool QQuick3DXrManagerPrivate::initialize()
93{
95
96 if (!m_worldTrackingProvider)
98
99 // NOTE: Check if the compository layer proxy is already active.
100 if (!s_compositorLayer->isActive()) {
101 if (auto *visionOSApplicaton = qGuiApp->nativeInterface<QNativeInterface::QVisionOSApplication>()) {
102 visionOSApplicaton->setImmersiveSpaceCompositorLayer(&(*s_compositorLayer));
103 s_compositorLayer->setActive();
104 // FIXME: We don't actually handle the case where the rendere changes or we get multiple calls should do something.
105
107
108 // Listen for spatial events (these are native gestures like pinch click/drag coming from SwiftUI)
109 QObject::connect(s_compositorLayer, &CompositorLayer::handleSpatialEventsRequested, q, &QQuick3DXrManager::processSpatialEvents);
110 }
111 return s_compositorLayer->layerRenderer() == nullptr ? false : true;
112 }
113
114 cp_layer_renderer_t renderer = layerRenderer();
115 if (!renderer) {
116 qWarning("QQuick3DXrManagerPrivate: Layer renderer is not available.");
117 return false;
118 }
119
120 return true;
121}
122
124{
125 if (!window) {
126 qWarning("QQuick3DXrManagerPrivate: Window is null!");
127 return;
128 }
129
130 cp_layer_renderer_t renderer = layerRenderer();
131 if (!renderer) {
132 qWarning("QQuick3DXrManagerPrivate: Layer renderer is not available.");
133 return;
134 }
135
136 auto device = cp_layer_renderer_get_device(renderer);
137 auto commandQueue = [device newCommandQueue];
138
139 auto qqGraphicsDevice = QQuickGraphicsDevice::fromDeviceAndCommandQueue(static_cast<MTLDevice*>(device), static_cast<MTLCommandQueue *>(commandQueue));
140
141 window->setGraphicsDevice(qqGraphicsDevice);
142}
143
145{
146 Q_UNUSED(rhi);
147 m_isGraphicsInitialized = true;
148 return m_isGraphicsInitialized;
149}
150
152{
153 return (s_compositorLayer->layerRenderer() != nullptr);
154}
155
157{
158 return m_isGraphicsInitialized;
159}
160
162{
163 // FIXME:
166 return true;
167}
168
169void QQuick3DXrManagerPrivate::createSwapchains()
170{
171 // cp_layer_renderer_t renderer = getLayerRenderer();
172 // if (!renderer) {
173 // qWarning("QQuick3DXrManagerPrivate: Layer renderer is not available.");
174 // return;
175 // }
176
177 //cp_layer_renderer_configuration_t layerConfiguration = cp_layer_renderer_get_configuration(renderer);
178 //cp_layer_renderer_layout layout = cp_layer_renderer_configuration_get_layout(layerConfiguration);
179}
180
182{
183 if (!isReady())
184 return RenderState::Paused;
185
186 cp_layer_renderer_t renderer = layerRenderer();
187 switch (cp_layer_renderer_get_state(renderer)) {
188 case cp_layer_renderer_state_paused:
189 return RenderState::Paused;
190 case cp_layer_renderer_state_running:
192 case cp_layer_renderer_state_invalidated:
194 }
196}
197
198void QQuick3DXrManagerPrivate::teardown()
199{
201}
202
204{
207}
208
210{
213}
214
216{
217 // FIXME: Not sure exactly what reference space is default or what is supported etc.
219}
220
222{
223 // FIXME: Not sure if it's possible to set a reference space on VisionOS
224 Q_UNUSED(newReferenceSpace);
226}
227
229{
231 if (!enable)
232 qWarning("Depth submission is required on VisionOS");
233}
234
236{
237 return s_compositorLayer->layerRenderer();
238}
239
240ar_device_anchor_t QQuick3DXrManagerPrivate::createPoseForTiming(cp_frame_timing_t timing)
241{
242 ar_device_anchor_t outAnchor = ar_device_anchor_create();
243 cp_time_t presentationTime = cp_frame_timing_get_presentation_time(timing);
244 CFTimeInterval queryTime = cp_time_to_cf_time_interval(presentationTime);
245 ar_device_anchor_query_status_t status = ar_world_tracking_provider_query_device_anchor_at_timestamp(m_worldTrackingProvider, queryTime, outAnchor);
246 if (status != ar_device_anchor_query_status_success) {
247 NSLog(@"Failed to get estimated pose from world tracking provider for presentation timestamp %0.3f", queryTime);
248 }
249 return outAnchor;
250}
251
253{
255
256 enum RenderState : quint8 {
257 Paused,
258 Running,
260 };
261 static bool logOnce[3] = {false, false, false};
264 // Wait
265 if (!logOnce[RenderState::Paused]) {
266 qDebug() << "-- Wait --";
267 logOnce[RenderState::Paused] = true;
268 logOnce[RenderState::Running] = false;
269 logOnce[RenderState::Invalidated] = false;
270 }
271 } else if (renderState == QQuick3DXrManagerPrivate::RenderState::Running) {
272 q->renderFrame();
273 if (!logOnce[RenderState::Running]) {
274 qDebug() << "-- Running --";
275 logOnce[RenderState::Paused] = false;
276 logOnce[RenderState::Running] = true;
277 logOnce[RenderState::Invalidated] = false;
278 }
279 } else if (renderState == QQuick3DXrManagerPrivate::RenderState::Invalidated) {
280 if (!logOnce[RenderState::Invalidated]) {
281 qDebug() << "-- Invalidated --";
282 logOnce[RenderState::Paused] = false;
283 logOnce[RenderState::Running] = false;
284 logOnce[RenderState::Invalidated] = true;
285 }
286 emit q->sessionEnded();
287 }
288}
289
291{
292 ar_world_tracking_configuration_t worldTrackingConfiguration = ar_world_tracking_configuration_create();
293 m_worldTrackingProvider = ar_world_tracking_provider_create(worldTrackingConfiguration);
294
295 ar_data_providers_t dataProviders = ar_data_providers_create();
296 ar_data_providers_add_data_provider(dataProviders, m_worldTrackingProvider);
297
298 if (!m_inputManager)
299 m_inputManager = QQuick3DXrInputManager::instance();
300
301 if (!m_anchorManager)
302 m_anchorManager = QQuick3DXrAnchorManager::instance();
303
304 // 1. prepare
305 QQuick3DXrInputManagerPrivate *pim = nullptr;
306 if (QSSG_GUARD_X(m_inputManager != nullptr, "No InputManager available!")) {
307 pim = QQuick3DXrInputManagerPrivate::get(m_inputManager);
308 if (QSSG_GUARD(pim != nullptr))
309 pim->prepareHandtracking(dataProviders);
310 }
311
312 if (QSSG_GUARD_X(m_anchorManager != nullptr, "No AnchorManager available!"))
313 m_anchorManager->prepareAnchorManager(dataProviders);
314
315 m_arSession = ar_session_create();
316 ar_session_run(m_arSession, dataProviders);
317
318 // 2. initialize
319 if (pim)
320 pim->initHandtracking();
321
322 if (m_anchorManager != nullptr)
323 m_anchorManager->initAnchorManager();
324}
325
327{
329 qWarning("Setting samples is not supported");
330}
331
333{
334 return QStringLiteral("VisionOS");
335}
336
338{
339 static const auto versionNumber = QOperatingSystemVersion::current().version();
340 return versionNumber;
341}
342
344{
345 return QString();
346}
347
349{
351
352 QQuickWindow *quickWindow = q->m_quickWindow;
353 QQuickRenderControl *renderControl = q->m_renderControl;
354 QQuick3DXrOrigin *xrOrigin = q->m_xrOrigin;
355 QQuick3DViewport *xrViewport = q->m_vrViewport;
356
357 QSSG_ASSERT_X(quickWindow && renderControl && xrViewport, "Invalid state, rendering aborted", return);
358
359 if (!xrOrigin) {
360 qWarning("No XR origin, trying to recover...");
361 q->checkOrigin();
362 }
363
364 auto layerRenderer = this->layerRenderer();
365 cp_frame_t frame = cp_layer_renderer_query_next_frame(layerRenderer);
366 if (frame == nullptr) {
367 qWarning("Failed to get next frame");
368 return;
369 }
370
371 cp_frame_timing_t timing = cp_frame_predict_timing(frame);
372 if (timing == nullptr) {
373 qWarning("Failed to get timing for frame");
374 return;
375 }
376
377 cp_frame_start_update(frame);
378
379 cp_frame_end_update(frame);
380
381 cp_time_wait_until(cp_frame_timing_get_optimal_input_time(timing));
382
383 cp_frame_start_submission(frame);
384 cp_drawable_t drawable = cp_frame_query_drawable(frame);
385 if (drawable == nullptr) {
386 qWarning("Failed to get drawable for frame");
387 return;
388 }
389
390 cp_frame_timing_t actualTiming = cp_drawable_get_frame_timing(drawable);
391 ar_device_anchor_t anchor = createPoseForTiming(actualTiming);
392 cp_drawable_set_device_anchor(drawable, anchor);
393
394 // Get the pose transform from the anchor
395 simd_float4x4 headTransform = ar_anchor_get_origin_from_anchor_transform(anchor);
396
397 // Update the hands
398 if (QSSG_GUARD(m_inputManager != nullptr))
399 QQuick3DXrInputManagerPrivate::get(m_inputManager)->updateHandtracking();
400
401 QRhi *rhi = renderControl->rhi();
402
403 for (size_t i = 0, end = cp_drawable_get_view_count(drawable); i != end ; ++i) {
404 // Setup the RenderTarget based on the current drawable
405 id<MTLTexture> colorMetalTexture = cp_drawable_get_color_texture(drawable, i);
406 auto textureSize = QSize([colorMetalTexture width], [colorMetalTexture height]);
407 auto renderTarget = QQuickRenderTarget::fromMetalTexture(static_cast<MTLTexture*>(colorMetalTexture), [colorMetalTexture pixelFormat], textureSize);
408
409 auto depthMetalTexture = cp_drawable_get_depth_texture(drawable, i);
410 auto depthTextureSize = QSize([depthMetalTexture width], [depthMetalTexture height]);
411 MTLPixelFormat depthTextureFormat = [depthMetalTexture pixelFormat];
412 static const auto convertFormat = [](MTLPixelFormat format) -> QRhiTexture::Format {
413 switch (format) {
414 case MTLPixelFormatDepth16Unorm:
415 return QRhiTexture::D16;
416 case MTLPixelFormatDepth32Float:
417 return QRhiTexture::D32F;
418 default:
419 qWarning("Unsupported depth texture format");
421 }
422 };
423 auto depthFormat = convertFormat(depthTextureFormat);
424 if (depthFormat != QRhiTexture::UnknownFormat) {
425 if (m_rhiDepthTexture && (m_rhiDepthTexture->format() != depthFormat || m_rhiDepthTexture->pixelSize() != depthTextureSize)) {
426 delete m_rhiDepthTexture;
427 m_rhiDepthTexture = nullptr;
428 }
429
430 if (!m_rhiDepthTexture)
431 m_rhiDepthTexture = rhi->newTexture(depthFormat, depthTextureSize, 1, QRhiTexture::RenderTarget);
432
433
434 m_rhiDepthTexture->createFrom({ quint64(static_cast<MTLTexture*>(depthMetalTexture)), 0});
435 renderTarget.setDepthTexture(m_rhiDepthTexture);
436 }
437
438 quickWindow->setRenderTarget(renderTarget);
439
440 // Update the window size and content item size using the texture size
441 quickWindow->setGeometry(0,
442 0,
443 textureSize.width(),
444 textureSize.height());
445 quickWindow->contentItem()->setSize(QSizeF(textureSize.width(),
446 textureSize.height()));
447
448 // Update the camera pose
449 if (QSSG_GUARD(xrOrigin)) {
450 cp_view_t view = cp_drawable_get_view(drawable, i);
451 simd_float4 tangents = cp_view_get_tangents(view);
452 const float tangentLeft = tangents[0];
453 const float tangentRight = tangents[1];
454 const float tangentUp = tangents[2];
455 const float tangentDown = tangents[3];
456 //qDebug() << "Left: " << tangentLeft << " Right: " << tangentRight << " Up: " << tangentUp << " Down: " << tangentDown;
457 simd_float2 depth_range = cp_drawable_get_depth_range(drawable);
458 const float clipNear = depth_range[1];
459 const float clipFar = depth_range[0];
460 //qDebug() << "Near: " << clipNear << " Far: " << clipFar;
461 xrOrigin->eyeCamera(i)->setLeftTangent(tangentLeft);
462 xrOrigin->eyeCamera(i)->setRightTangent(tangentRight);
463 xrOrigin->eyeCamera(i)->setUpTangent(tangentUp);
464 xrOrigin->eyeCamera(i)->setDownTangent(tangentDown);
465 xrOrigin->eyeCamera(i)->setClipNear(clipNear);
466 xrOrigin->eyeCamera(i)->setClipFar(clipFar);
467
468 simd_float4x4 localEyeTransform = cp_view_get_transform(view);
469 simd_float4x4 eyeCameraTransform = simd_mul(headTransform, localEyeTransform);
470 // NOTE: We need to convert from meters to centimeters here
471 QMatrix4x4 transform{eyeCameraTransform.columns[0].x, eyeCameraTransform.columns[1].x, eyeCameraTransform.columns[2].x, eyeCameraTransform.columns[3].x * 100,
472 eyeCameraTransform.columns[0].y, eyeCameraTransform.columns[1].y, eyeCameraTransform.columns[2].y, eyeCameraTransform.columns[3].y * 100,
473 eyeCameraTransform.columns[0].z, eyeCameraTransform.columns[1].z, eyeCameraTransform.columns[2].z, eyeCameraTransform.columns[3].z * 100,
474 0.0f, 0.0f, 0.0f, 1.0f};
475 QQuick3DNodePrivate::get(xrOrigin->eyeCamera(i))->setLocalTransform(transform);
476 xrViewport->setCamera(xrOrigin->eyeCamera(i));
477 }
478
479 renderControl->polishItems();
480 renderControl->beginFrame();
481 renderControl->sync();
482 renderControl->render();
483 renderControl->endFrame();
484 }
485
486 id<MTLCommandBuffer> commandBuffer = [static_cast<const QRhiMetalNativeHandles*>(renderControl->rhi()->nativeHandles())->cmdQueue commandBuffer];
487
488 cp_drawable_encode_present(drawable, commandBuffer);
490
491 cp_frame_end_submission(frame);
492}
493
495
496#include "qquick3dxrmanager_visionos.moc"
IOBluetoothDevice * device
void configure(cp_layer_renderer_capabilities_t capabilities, cp_layer_renderer_configuration_t configuration) const override
void renderingRequested()
void layerRendererChanged()
cp_layer_renderer_t layerRenderer() const
void handleSpatialEventsRequested(const QJsonObject &jsonString)
void render(cp_layer_renderer_t renderer) override
void handleSpatialEvents(const QJsonObject &events) override
\inmodule QtCore\reentrant
Definition qjsonobject.h:20
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static Q_CORE_EXPORT QOperatingSystemVersionBase current()
static QQuick3DNodePrivate * get(QQuick3DNode *node)
static QQuick3DXrAnchorManager * instance()
void setDownTangent(float downTangent)
void setUpTangent(float upTangent)
void setClipNear(float clipNear)
void setRightTangent(float rightTangent)
void setClipFar(float clipFar)
void setLeftTangent(float leftTangent)
static QQuick3DXrInputManagerPrivate * get(QQuick3DXrInputManager *inputManager)
static QQuick3DXrInputManager * instance()
QQuick3DXrManagerPrivate(QQuick3DXrManager &manager)
void setupWindow(QQuickWindow *window)
QtQuick3DXr::ReferenceSpace getReferenceSpace() const
void setReferenceSpace(QtQuick3DXr::ReferenceSpace newReferenceSpace)
cp_layer_renderer_t layerRenderer() const
ar_device_anchor_t createPoseForTiming(cp_frame_timing_t timing)
bool setupGraphics(QQuickWindow *window)
static QQuick3DXrManagerPrivate * get(QQuick3DXrManager *manager)
void setSize(const QSizeF &size)
The QQuickRenderControl class provides a mechanism for rendering the Qt Quick scenegraph onto an offs...
void endFrame()
Specifies the end of a graphics frame.
void render()
Renders the scenegraph using the current context.
void beginFrame()
Specifies the start of a graphics frame.
void polishItems()
This function should be called as late as possible before sync().
bool sync()
This function is used to synchronize the QML scene with the rendering scene graph.
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
QQuickItem * contentItem
\qmlattachedproperty Item Window::contentItem
\inmodule QtRhi
Format format() const
Definition qrhi.h:973
@ RenderTarget
Definition qrhi.h:898
Format
Specifies the texture format.
Definition qrhi.h:914
@ UnknownFormat
Definition qrhi.h:915
virtual bool createFrom(NativeTexture src)
Similar to create(), except that no new native textures are created.
Definition qrhi.cpp:4490
QSize pixelSize() const
Definition qrhi.h:976
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1805
QRhiTexture * newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount=1, QRhiTexture::Flags flags={})
Definition qrhi.cpp:10570
const QRhiNativeHandles * nativeHandles()
Definition qrhi.cpp:10145
\inmodule QtCore
Definition qsize.h:208
\inmodule QtCore
Definition qsize.h:25
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
\inmodule QtCore
void setGeometry(int posx, int posy, int w, int h)
Sets the geometry of the window, excluding its window frame, to a rectangle constructed from posx,...
Definition qwindow.cpp:1812
Combined button and popup list for selecting options.
ConnectionType
@ SingleShotConnection
@ QueuedConnection
#define Q_FUNC_INFO
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qGuiApp
#define qDebug
[1]
Definition qlogging.h:165
#define qWarning
Definition qlogging.h:167
GLsizei samples
GLint GLsizei GLsizei height
GLuint GLuint end
GLint GLint GLint GLint GLsizei GLsizei GLsizei GLboolean commit
GLint GLsizei width
GLboolean enable
GLint GLsizei GLsizei GLenum format
GLuint GLenum GLenum transform
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
struct ar_data_providers_s * ar_data_providers_t
#define QSSG_ASSERT_X(cond, msg, action)
#define QSSG_GUARD_X(cond, msg)
#define QSSG_ASSERT(cond, action)
#define QSSG_GUARD(cond)
#define QStringLiteral(str)
#define Q_OBJECT
#define Q_SIGNALS
#define emit
#define Q_UNIMPLEMENTED()
#define Q_UNUSED(x)
unsigned long long quint64
Definition qtypes.h:61
unsigned char quint8
Definition qtypes.h:46
aWidget window() -> setWindowTitle("New Window Title")
[2]
QFrame frame
[0]
QNetworkAccessManager manager
QQuickView * view
[0]
QSvgRenderer * renderer
[0]