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
qquick3dxrvisionosrendermanager.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
5
8#include <QtQuick3D/private/qquick3dviewport_p.h>
9#include <QtQuick3D/private/qquick3dnode_p_p.h>
10
11#include <QQuickGraphicsDevice>
12#include <rhi/qrhi.h>
13
14#include <CompositorServices/CompositorServices.h>
15#include <QtGui/qguiapplication_platform.h>
16
18
19class CompositorLayer : public QObject, public QNativeInterface::QVisionOSApplication::ImmersiveSpaceCompositorLayer
20{
22public:
23 void configure(cp_layer_renderer_capabilities_t capabilities, cp_layer_renderer_configuration_t configuration) const override
24 {
25 // NOTE: foveation is disabled for now
26 const bool supportsFoveation = false && cp_layer_renderer_capabilities_supports_foveation(capabilities);
27
28 cp_layer_renderer_configuration_set_layout(configuration, cp_layer_renderer_layout_dedicated);
29 cp_layer_renderer_configuration_set_foveation_enabled(configuration, supportsFoveation);
30 cp_layer_renderer_configuration_set_color_format(configuration, MTLPixelFormatRGBA16Float);
31 }
32
33 void render(cp_layer_renderer_t renderer) override
34 {
35 if (m_layerRenderer != renderer) {
36 m_layerRenderer = renderer;
38 }
39
41 }
42
43 void setActive() { m_active = true; };
44 bool isActive() const { return m_active; }
45
46 cp_layer_renderer_t layerRenderer() const
47 {
48 return m_layerRenderer;
49 }
50
54
55private:
56 cp_layer_renderer_t m_layerRenderer = nullptr;
57 bool m_active = false;
58};
59
60Q_GLOBAL_STATIC(CompositorLayer, s_compositorLayer)
61
63 : QObject(parent)
64{
65 runWorldTrackingARSession();
66}
67
72
74{
75 // NOTE: Check if the compository layer proxy is already active.
76 if (!s_compositorLayer->isActive()) {
77 if (auto *visionOSApplicaton = qGuiApp->nativeInterface<QNativeInterface::QVisionOSApplication>()) {
78 visionOSApplicaton->setImmersiveSpaceCompositorLayer(&(*s_compositorLayer));
79 s_compositorLayer->setActive();
80 // FIXME: We don't actually handle the case where the rendere changes or we get multiple calls should do something.
82 }
83 return s_compositorLayer->layerRenderer() == nullptr ? false : true;
84 }
85
86 cp_layer_renderer_t renderer = layerRenderer();
87 if (!renderer) {
88 qWarning("QQuick3DXRVisionOSRenderManager: Layer renderer is not available.");
89 return false;
90 }
91
92 // Pre-setup Qt Quick
93
94 // Setup Graphics
95
96 return true;
97}
98
100{
101 if (!window) {
102 qWarning("QQuick3DXRVisionOSRenderManager: Window is null!");
103 return;
104 }
105
106 cp_layer_renderer_t renderer = layerRenderer();
107 if (!renderer) {
108 qWarning("QQuick3DXRVisionOSRenderManager: Layer renderer is not available.");
109 return;
110 }
111
112 auto device = cp_layer_renderer_get_device(renderer);
113 auto commandQueue = [device newCommandQueue];
114
115 auto qqGraphicsDevice = QQuickGraphicsDevice::fromDeviceAndCommandQueue(static_cast<MTLDevice*>(device), static_cast<MTLCommandQueue *>(commandQueue));
116
117 window->setGraphicsDevice(qqGraphicsDevice);
118}
119
121{
122 Q_UNUSED(rhi);
123 return true;
124}
125
127{
128 return (s_compositorLayer->layerRenderer() != nullptr);
129}
130
132{
133 // cp_layer_renderer_t renderer = getLayerRenderer();
134 // if (!renderer) {
135 // qWarning("QQuick3DXRVisionOSRenderManager: Layer renderer is not available.");
136 // return;
137 // }
138
139 //cp_layer_renderer_configuration_t layerConfiguration = cp_layer_renderer_get_configuration(renderer);
140 //cp_layer_renderer_layout layout = cp_layer_renderer_configuration_get_layout(layerConfiguration);
141}
142
144{
145 if (!isReady())
146 return RenderState::Paused;
147
148 cp_layer_renderer_t renderer = layerRenderer();
149 switch (cp_layer_renderer_get_state(renderer)) {
150 case cp_layer_renderer_state_paused:
151 return RenderState::Paused;
152 case cp_layer_renderer_state_running:
154 case cp_layer_renderer_state_invalidated:
156 }
158}
159
164
166{
167 return s_compositorLayer->layerRenderer();
168}
169
170ar_device_anchor_t QQuick3DXRVisionOSRenderManager::createPoseForTiming(cp_frame_timing_t timing)
171{
172 ar_device_anchor_t outAnchor = ar_device_anchor_create();
173 cp_time_t presentationTime = cp_frame_timing_get_presentation_time(timing);
174 CFTimeInterval queryTime = cp_time_to_cf_time_interval(presentationTime);
175 ar_device_anchor_query_status_t status = ar_world_tracking_provider_query_device_anchor_at_timestamp(m_worldTrackingProvider, queryTime, outAnchor);
176 if (status != ar_device_anchor_query_status_success) {
177 NSLog(@"Failed to get estimated pose from world tracking provider for presentation timestamp %0.3f", queryTime);
178 }
179 return outAnchor;
180}
181
183{
184 ar_world_tracking_configuration_t worldTrackingConfiguration = ar_world_tracking_configuration_create();
185 m_worldTrackingProvider = ar_world_tracking_provider_create(worldTrackingConfiguration);
186
187 ar_data_providers_t dataProviders = ar_data_providers_create();
188 ar_data_providers_add_data_provider(dataProviders, m_worldTrackingProvider);
189
190 m_isHandTrackingSupported = ar_hand_tracking_provider_is_supported();
191 if (m_isHandTrackingSupported) {
192 ar_hand_tracking_configuration_t handTrackingConfiguration = ar_hand_tracking_configuration_create();
193 m_handTrackingProvider = ar_hand_tracking_provider_create(handTrackingConfiguration);
194 ar_data_providers_add_data_provider(dataProviders, m_handTrackingProvider);
195 } else {
196 qWarning("Hand tracking is not supported on this device.");
197 }
198
199
200 m_arSession = ar_session_create();
201 ar_session_run(m_arSession, dataProviders);
202
203 // Create hand anchors now
204 if (m_isHandTrackingSupported) {
205 m_leftHandAnchor = ar_hand_anchor_create();
206 m_rightHandAnchor = ar_hand_anchor_create();
207 }
208}
209
211{
212 auto layerRenderer = this->layerRenderer();
213 cp_frame_t frame = cp_layer_renderer_query_next_frame(layerRenderer);
214 if (frame == nullptr) {
215 return;
216 }
217
218 cp_frame_timing_t timing = cp_frame_predict_timing(frame);
219 if (timing == nullptr)
220 return;
221
222 cp_frame_start_update(frame);
223
224 // TODO do input update here
225
226 cp_frame_end_update(frame);
227
228 cp_time_wait_until(cp_frame_timing_get_optimal_input_time(timing));
229
230 cp_frame_start_submission(frame);
231 cp_drawable_t drawable = cp_frame_query_drawable(frame);
232 if (drawable == nullptr)
233 return;
234
235 cp_frame_timing_t actualTiming = cp_drawable_get_frame_timing(drawable);
236 ar_device_anchor_t anchor = createPoseForTiming(actualTiming);
237 cp_drawable_set_device_anchor(drawable, anchor);
238
239 // Get the pose transform from the anchor
240 simd_float4x4 headTransform = ar_anchor_get_origin_from_anchor_transform(anchor);
241
242 // Update the hands
243 if (m_isHandTrackingSupported) {
244 ar_hand_tracking_provider_get_latest_anchors(m_handTrackingProvider, m_leftHandAnchor, m_rightHandAnchor);
245
246 if (ar_trackable_anchor_is_tracked(m_leftHandAnchor)) {
247
248 }
249
250 if (ar_trackable_anchor_is_tracked(m_rightHandAnchor)) {
251
252 }
253 }
254
255 QRhi *rhi = renderControl->rhi();
256
257 for (size_t i = 0, end = cp_drawable_get_view_count(drawable); i != end ; ++i) {
258 // Setup the RenderTarget based on the current drawable
259 id<MTLTexture> colorMetalTexture = cp_drawable_get_color_texture(drawable, i);
260 auto textureSize = QSize([colorMetalTexture width], [colorMetalTexture height]);
261 auto renderTarget = QQuickRenderTarget::fromMetalTexture(static_cast<MTLTexture*>(colorMetalTexture), [colorMetalTexture pixelFormat], textureSize);
262
263 auto depthMetalTexture = cp_drawable_get_depth_texture(drawable, i);
264 auto depthTextureSize = QSize([depthMetalTexture width], [depthMetalTexture height]);
265 MTLPixelFormat depthTextureFormat = [depthMetalTexture pixelFormat];
266 static const auto convertFormat = [](MTLPixelFormat format) -> QRhiTexture::Format {
267 switch (format) {
268 case MTLPixelFormatDepth16Unorm:
269 return QRhiTexture::D16;
270 case MTLPixelFormatDepth32Float:
271 return QRhiTexture::D32F;
272 default:
273 qWarning("Unsupported depth texture format");
275 }
276 };
277 auto depthFormat = convertFormat(depthTextureFormat);
278 if (depthFormat != QRhiTexture::UnknownFormat) {
279 if (m_rhiDepthTexture && (m_rhiDepthTexture->format() != depthFormat || m_rhiDepthTexture->pixelSize() != depthTextureSize)) {
280 delete m_rhiDepthTexture;
281 m_rhiDepthTexture = nullptr;
282 }
283
284 if (!m_rhiDepthTexture)
285 m_rhiDepthTexture = rhi->newTexture(depthFormat, depthTextureSize, 1, QRhiTexture::RenderTarget);
286
287
288 m_rhiDepthTexture->createFrom({ quint64(static_cast<MTLTexture*>(depthMetalTexture)), 0});
289 renderTarget.setDepthTexture(m_rhiDepthTexture);
290 }
291
292 quickWindow->setRenderTarget(renderTarget);
293
294 // Update the window size and content item size using the texture size
295 quickWindow->setGeometry(0,
296 0,
297 textureSize.width(),
298 textureSize.height());
299 quickWindow->contentItem()->setSize(QSizeF(textureSize.width(),
300 textureSize.height()));
301
302 // Update the camera pose
303 if (xrOrigin) {
304 cp_view_t view = cp_drawable_get_view(drawable, i);
305 simd_float4 tangents = cp_view_get_tangents(view);
306 const float tangentLeft = tangents[0];
307 const float tangentRight = tangents[1];
308 const float tangentUp = tangents[2];
309 const float tangentDown = tangents[3];
310 //qDebug() << "Left: " << tangentLeft << " Right: " << tangentRight << " Up: " << tangentUp << " Down: " << tangentDown;
311 simd_float2 depth_range = cp_drawable_get_depth_range(drawable);
312 const float clipNear = depth_range[1];
313 const float clipFar = depth_range[0];
314 //qDebug() << "Near: " << clipNear << " Far: " << clipFar;
315 xrOrigin->eyeCamera(i)->setLeftTangent(tangentLeft);
316 xrOrigin->eyeCamera(i)->setRightTangent(tangentRight);
317 xrOrigin->eyeCamera(i)->setUpTangent(tangentUp);
318 xrOrigin->eyeCamera(i)->setDownTangent(tangentDown);
319 xrOrigin->eyeCamera(i)->setClipNear(clipNear);
320 xrOrigin->eyeCamera(i)->setClipFar(clipFar);
321
322 simd_float4x4 localEyeTransform = cp_view_get_transform(view);
323 simd_float4x4 eyeCameraTransform = simd_mul(headTransform, localEyeTransform);
324 // NOTE: We need to convert from meters to centimeters here
325 QMatrix4x4 transform{eyeCameraTransform.columns[0].x, eyeCameraTransform.columns[1].x, eyeCameraTransform.columns[2].x, eyeCameraTransform.columns[3].x * 100,
326 eyeCameraTransform.columns[0].y, eyeCameraTransform.columns[1].y, eyeCameraTransform.columns[2].y, eyeCameraTransform.columns[3].y * 100,
327 eyeCameraTransform.columns[0].z, eyeCameraTransform.columns[1].z, eyeCameraTransform.columns[2].z, eyeCameraTransform.columns[3].z * 100,
328 0.0f, 0.0f, 0.0f, 1.0f};
329 QQuick3DNodePrivate::get(xrOrigin->eyeCamera(i))->setLocalTransform(transform);
330 xrViewport->setCamera(xrOrigin->eyeCamera(i));
331 }
332
333 renderControl->polishItems();
334 renderControl->beginFrame();
335 renderControl->sync();
336 renderControl->render();
337 renderControl->endFrame();
338 }
339
340 id<MTLCommandBuffer> commandBuffer = [static_cast<const QRhiMetalNativeHandles*>(renderControl->rhi()->nativeHandles())->cmdQueue commandBuffer];
341
342 cp_drawable_encode_present(drawable, commandBuffer);
343 [commandBuffer commit];
344
345 cp_frame_end_submission(frame);
346}
347
349
350#include "qquick3dxrvisionosrendermanager.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 render(cp_layer_renderer_t renderer) override
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 QQuick3DNodePrivate * get(QQuick3DNode *node)
ar_device_anchor_t createPoseForTiming(cp_frame_timing_t timing)
void renderFrame(QQuickWindow *quickWindow, QQuickRenderControl *renderControl, QQuick3DXrOrigin *xrOrigin, QQuick3DViewport *xrViewport)
void setDownTangent(float downTangent)
void setUpTangent(float upTangent)
void setClipNear(float clipNear)
void setRightTangent(float rightTangent)
void setClipFar(float clipFar)
void setLeftTangent(float leftTangent)
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:972
@ 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:4487
QSize pixelSize() const
Definition qrhi.h:975
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
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:208
\inmodule QtCore
Definition qsize.h:25
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:1802
Combined button and popup list for selecting options.
ConnectionType
@ SingleShotConnection
@ QueuedConnection
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qGuiApp
#define qWarning
Definition qlogging.h:166
GLint GLsizei GLsizei height
GLuint GLuint end
GLint GLint GLint GLint GLsizei GLsizei GLsizei GLboolean commit
GLint GLsizei width
GLint GLsizei GLsizei GLenum format
GLuint GLenum GLenum transform
#define Q_OBJECT
#define Q_SIGNALS
#define emit
#define Q_UNUSED(x)
unsigned long long quint64
Definition qtypes.h:61
aWidget window() -> setWindowTitle("New Window Title")
[2]
QFrame frame
[0]
QQuickView * view
[0]
QSvgRenderer * renderer
[0]