10#include "../qquick3dxrinputmanager_p.h"
11#include "../qquick3dxranimationdriver_p.h"
13#include <QtQuick3D/private/qquick3dviewport_p.h>
14#include <QtQuick3D/private/qquick3dnode_p_p.h>
16#include <QtQuick3DUtils/private/qssgassert_p.h>
18#include <QQuickGraphicsDevice>
21#include <CompositorServices/CompositorServices.h>
22#include <QtGui/qguiapplication_platform.h>
24#include <QtCore/qoperatingsystemversion.h>
25#include <QtCore/qloggingcategory.h>
27#include <TargetConditionals.h>
40 static bool foveationDisabled = qEnvironmentVariableIntValue(
"QT_QUICK3D_XR_DISABLE_FOVEATION") != 0;
41 return foveationDisabled;
70 if (!
std::isfinite(near) || near <= 0.0f)
72 if (!
std::isfinite(far) || far <= near)
83 m_xrManager =
nullptr;
84 [m_layerRenderer release];
85 m_layerRenderer =
nullptr;
86 m_worldTrackingProvider =
nullptr;
87 m_arSession =
nullptr;
88 m_initialized =
false;
102 static bool supportsLayoutType(cp_layer_renderer_capabilities_t capabilities, cp_layer_renderer_layout layout)
104 const size_t layoutCount = cp_layer_renderer_capabilities_supported_layouts_count(capabilities, cp_supported_layouts_options_none);
106 for (size_t i = 0; i < layoutCount && !found; ++i)
107 found = (layout == cp_layer_renderer_capabilities_supported_layout(capabilities, cp_supported_layouts_options_none, i));
112 void configure(cp_layer_renderer_capabilities_t capabilities, cp_layer_renderer_configuration_t configuration)
const override
120 m_multiviewSupported = supportsLayoutType(capabilities, cp_layer_renderer_layout_layered);
121 m_multiviewEnabled = m_multiviewEnabled && m_multiviewSupported;
124 m_foveationSupported = cp_layer_renderer_capabilities_supports_foveation(capabilities);
131 cp_layer_renderer_layout textureLayout = m_multiviewEnabled ? cp_layer_renderer_layout_layered
132 : cp_layer_renderer_layout_dedicated;
134 cp_layer_renderer_configuration_set_layout(configuration, textureLayout);
135 cp_layer_renderer_configuration_set_foveation_enabled(configuration, m_foveationEnabled);
136 cp_layer_renderer_configuration_set_color_format(configuration, MTLPixelFormatRGBA16Float);
139 const simd_float2 systemDepthRange = cp_layer_renderer_configuration_get_default_depth_range(configuration);
140 qssgSanitizeDepthRange(systemDepthRange, m_depthRange[0], m_depthRange[1]);
143 cp_layer_renderer_configuration_set_default_depth_range(configuration, simd_make_float2(m_depthRange[1], m_depthRange[0]));
145 qCDebug(lcQuick3DXr) <<
"Configuring with the following settings:"
146 <<
"\n\tMultiview supported: " << m_multiviewSupported
147 <<
"\n\tMultiview enabled: " << m_multiviewEnabled
148 <<
"\n\tFoveation supported: " << m_foveationSupported
149 <<
"\n\tFoveation enabled: " << m_foveationEnabled
150 <<
"\n\tSystem depth range: " << systemDepthRange.y <<
" to: " << systemDepthRange.x
151 <<
"\n\tDepth Range: " << m_depthRange[0] <<
" to: " << m_depthRange[1];
154 void render(cp_layer_renderer_t renderer)
override
156 if (m_layerRenderer != renderer) {
157 m_layerRenderer = renderer;
158 emit layerRendererChanged();
161 if (m_layerRenderer) {
166 [m_layerRenderer retain];
168 emit layerRendererReady();
176 m_xrManager->processSpatialEvents(events);
188 return m_layerRenderer;
193 near = m_depthRange[0];
194 far = m_depthRange[1];
200 Q_ASSERT(qApp->thread() == QThread::currentThread());
201 QSSG_ASSERT(!m_initialized,
return);
203 m_xrManager = xrManager;
204 runWorldTrackingARSession();
205 m_initialized =
true;
215 Q_ASSERT(qApp->thread() == QThread::currentThread());
220 qCDebug(lcQuick3DXr,
"Stopping AR session");
224 ar_session_set_data_provider_state_change_handler_f(m_arSession, dispatch_get_main_queue(),
this, &onArStateChanged);
225 ar_session_stop(m_arSession);
226 m_arSession =
nullptr;
234 return m_waitCondition.wait(&m_mutex);
239 QSSG_ASSERT(mngr !=
nullptr,
return);
241 qCDebug(lcQuick3DXr) <<
"Destroying compositor layer";
243 if (
auto *visionOSApplicaton = qGuiApp->nativeInterface<QNativeInterface::QVisionOSApplication>())
244 visionOSApplicaton->setImmersiveSpaceCompositorLayer(
nullptr);
254 auto *d = QQuickWindowPrivate::get(window);
255 d->cleanupNodesOnShutdown();
256 mngr->releaseResources();
257 if (
auto *rc = d->renderControl)
263 compositorLayer->deleteLater();
281 qCDebug(lcQuick3DXr) <<
"Teardown in progress, skipping event handling";
284 if (event->type() == QEvent::DeferredDelete)
286 return QObject::event(event);
289 switch (
static_cast<
Event>(event->type())) {
292 const bool success = renderFrame(locker);
297 qCDebug(lcQuick3DXr) <<
"Waking up the GUI thread, rendering returned early...";
298 m_xrManager->m_syncDone =
false;
299 m_waitCondition.wakeOne();
315 return QObject::event(event);
322 static void onArStateChanged(
void *context,
323 ar_data_providers_t data_providers,
324 ar_data_provider_state_t new_state,
326 ar_data_provider_t failed_data_provider)
329 Q_UNUSED(data_providers);
331 Q_UNUSED(failed_data_provider);
338 QSSG_ASSERT_X(that->m_arSession !=
nullptr,
"AR session is not running, skipping state change!",
return);
340 const auto oldState = that->m_arTrackingState;
342 case ar_data_provider_state_initialized:
345 case ar_data_provider_state_running:
348 case ar_data_provider_state_paused:
351 case ar_data_provider_state_stopped:
357 if (oldState != that->m_arTrackingState) {
359 emit that->arStateChanged(that->m_arTrackingState);
363 enum class RenderStateAction
369 template<RenderStateAction Action = RenderStateAction::WaitUntilRunning>
370 void checkRenderState()
372 QSSG_ASSERT(m_layerRenderer !=
nullptr,
return);
374 const auto oldState = m_renderState;
375 switch (cp_layer_renderer_get_state(m_layerRenderer)) {
376 case cp_layer_renderer_state_paused:
377 m_renderState = QQuick3DXrManagerPrivate::RenderState::Paused;
379 case cp_layer_renderer_state_running:
380 m_renderState = QQuick3DXrManagerPrivate::RenderState::Running;
382 case cp_layer_renderer_state_invalidated:
383 m_renderState = QQuick3DXrManagerPrivate::RenderState::Invalidated;
387 if (oldState != m_renderState)
388 emit renderStateChanged(m_renderState);
390 if (m_renderState == QQuick3DXrManagerPrivate::RenderState::Paused) {
391 if constexpr (Action == RenderStateAction::WaitUntilRunning) {
392 qCDebug(lcQuick3DXr) <<
"Waiting for rendering to resume...";
393 cp_layer_renderer_wait_until_running(m_layerRenderer);
394 QCoreApplication::postEvent(
this,
new QEvent(asQEvent(CompositorLayer::Event::Pulse)));
399 ar_device_anchor_t createPoseForTiming(cp_frame_timing_t timing)
401 QSSG_ASSERT(m_worldTrackingProvider !=
nullptr,
return nullptr);
403 ar_device_anchor_t outAnchor = ar_device_anchor_create();
404 cp_time_t presentationTime = cp_frame_timing_get_presentation_time(timing);
405 CFTimeInterval queryTime = cp_time_to_cf_time_interval(presentationTime);
406 ar_device_anchor_query_status_t status = ar_world_tracking_provider_query_device_anchor_at_timestamp(m_worldTrackingProvider, queryTime, outAnchor);
407 if (status != ar_device_anchor_query_status_success) {
408 NSLog(@
"Failed to get estimated pose from world tracking provider for presentation timestamp %0.3f", queryTime);
413 void runWorldTrackingARSession()
417 ar_world_tracking_configuration_t worldTrackingConfiguration = ar_world_tracking_configuration_create();
418 m_worldTrackingProvider = ar_world_tracking_provider_create(worldTrackingConfiguration);
420 ar_data_providers_t dataProviders = ar_data_providers_create();
421 ar_data_providers_add_data_provider(dataProviders, m_worldTrackingProvider);
428 if (QSSG_GUARD_X(inputManager !=
nullptr,
"No InputManager available!")) {
430 if (QSSG_GUARD(pim !=
nullptr))
434 if (QSSG_GUARD_X(anchorManager !=
nullptr,
"No AnchorManager available!"))
435 QQuick3DXrManagerPrivate::prepareAnchorManager(anchorManager, dataProviders);
437 m_arSession = ar_session_create();
438 ar_session_set_data_provider_state_change_handler_f(m_arSession, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
this, &onArStateChanged);
439 ar_session_run(m_arSession, dataProviders);
448 qCDebug(lcQuick3DXr,
"Pausing rendering");
453 qCDebug(lcQuick3DXr,
"Cleaning up");
455 m_xrManager->releaseResources();
458 [[nodiscard]]
bool renderFrame(
QMutexLocker<QMutex> &locker)
460 Q_ASSERT(locker.isLocked());
463 if (m_renderState != QQuick3DXrManagerPrivate::RenderState::Running) {
464 qCDebug(lcQuick3DXr,
"Rendering is not running, skipping frame rendering!");
472 qCDebug(lcQuick3DXr,
"AR tracking is not running, skipping frame rendering!");
480 return m_xrManager->renderFrameImpl(locker, m_waitCondition);
483 ar_world_tracking_provider_t worldTrackingProvider()
const
485 return m_worldTrackingProvider;
489 mutable QMutex m_mutex;
490 QWaitCondition m_waitCondition;
492 mutable QMutex m_compositorLayerMtx;
495 mutable QMutex m_arSessionMtx;
499 cp_layer_renderer_t m_layerRenderer =
nullptr;
500 ar_world_tracking_provider_t m_worldTrackingProvider =
nullptr;
501 ar_session_t m_arSession =
nullptr;
502 mutable float m_depthRange[2] {1.0f, 10000.0f};
505 bool m_initialized =
false;
506 bool m_teardown =
false;
507 mutable bool m_multiviewSupported =
true;
508 mutable bool m_multiviewEnabled =
true;
509 mutable bool m_foveationSupported =
true;
510 mutable bool m_foveationEnabled =
true;
526 const simd_float2 reportedRange = cp_drawable_get_depth_range(drawable);
527 qssgSanitizeDepthRange(reportedRange, clipNear, clipFar);
530 if (reportedRange.y != clipNear || reportedRange.x != clipFar)
531 cp_drawable_set_depth_range(drawable, simd_make_float2(clipFar, clipNear));
535void QQuick3DXrManagerPrivate::updateCameraImp(simd_float4x4 headTransform, cp_drawable_t drawable, QQuick3DXrOrigin *xrOrigin,
int i)
537 cp_view_t view = cp_drawable_get_view(drawable, i);
540 float clipNear, clipFar;
541 qssgUpdateDrawableDepthRange(drawable, clipNear, clipFar);
543 xrOrigin->eyeCamera(i)->setClipNear(clipNear);
544 xrOrigin->eyeCamera(i)->setClipFar(clipFar);
547 simd_float4x4 projection = cp_drawable_compute_projection(drawable, cp_axis_direction_convention_right_up_forward, i);
548 QMatrix4x4 proj{projection.columns[0].x, projection.columns[1].x, projection.columns[2].x, projection.columns[3].x,
549 projection.columns[0].y, projection.columns[1].y, projection.columns[2].y, projection.columns[3].y,
550 projection.columns[0].z, projection.columns[1].z, projection.columns[2].z, projection.columns[3].z,
551 projection.columns[0].w, projection.columns[1].w, projection.columns[2].w, projection.columns[3].w};
552 xrOrigin->eyeCamera(i)->setProjection(proj);
554 simd_float4x4 localEyeTransform = cp_view_get_transform(view);
555 simd_float4x4 eyeCameraTransform = simd_mul(headTransform, localEyeTransform);
557 QMatrix4x4 transform{eyeCameraTransform.columns[0].x, eyeCameraTransform.columns[1].x, eyeCameraTransform.columns[2].x, eyeCameraTransform.columns[3].x * 100,
558 eyeCameraTransform.columns[0].y, eyeCameraTransform.columns[1].y, eyeCameraTransform.columns[2].y, eyeCameraTransform.columns[3].y * 100,
559 eyeCameraTransform.columns[0].z, eyeCameraTransform.columns[1].z, eyeCameraTransform.columns[2].z, eyeCameraTransform.columns[3].z * 100,
560 0.0f, 0.0f, 0.0f, 1.0f};
561 QQuick3DNodePrivate::get(xrOrigin->eyeCamera(i))->setLocalTransform(transform);
564void QQuick3DXrManagerPrivate::updateCamera(QQuick3DViewport *xrViewport, simd_float4x4 headTransform, cp_drawable_t drawable, QQuick3DXrOrigin *xrOrigin,
int i)
566 updateCameraImp(headTransform, drawable, xrOrigin, i);
567 xrViewport->setCamera(xrOrigin->eyeCamera(i));
570void QQuick3DXrManagerPrivate::updateCameraMultiview(QQuick3DViewport *xrViewport, simd_float4x4 headTransform, cp_drawable_t drawable, QQuick3DXrOrigin *xrOrigin)
572 QQuick3DCamera *cameras[2] {xrOrigin->eyeCamera(0), xrOrigin->eyeCamera(1)};
574 for (
int i = 0; i < 2; ++i)
575 updateCameraImp(headTransform, drawable, xrOrigin, i);
577 xrViewport->setMultiViewCameras(cameras);
592 QSSG_ASSERT(manager !=
nullptr,
return nullptr);
593 return manager->d_func();
598 Q_Q(QQuick3DXrManager);
603 if (!m_compositorLayer) {
604 m_compositorLayer = s_compositorLayer->instance;
605 if (!m_compositorLayer) {
607 s_compositorLayer->instance = m_compositorLayer;
612 m_inputManager = QQuick3DXrInputManager::instance();
613 if (!m_anchorManager)
614 m_anchorManager = QQuick3DXrAnchorManager::instance();
618 if (
auto *visionOSApplicaton = qGuiApp->nativeInterface<QNativeInterface::QVisionOSApplication>()) {
619 visionOSApplicaton->setImmersiveSpaceCompositorLayer(m_compositorLayer);
621 QObject::connect(m_compositorLayer, &CompositorLayer::layerRendererReady, q, &QQuick3DXrManager::initialized, Qt::ConnectionType(Qt::SingleShotConnection | Qt::QueuedConnection));
622 QObject::connect(m_compositorLayer, &CompositorLayer::renderStateChanged, q, [q](QQuick3DXrManagerPrivate::RenderState state) {
624 case QQuick3DXrManagerPrivate::RenderState::Uninitialized:
625 qCDebug(lcQuick3DXr,
"Render state: Uninitialized");
626 QQuick3DXrManagerPrivate::get(q)->m_running =
false;
628 case QQuick3DXrManagerPrivate::RenderState::Running:
629 qCDebug(lcQuick3DXr,
"Render state: Running");
630 QQuick3DXrManagerPrivate::get(q)->m_running =
true;
631 QCoreApplication::postEvent(q,
new QEvent(QEvent::UpdateRequest));
633 case QQuick3DXrManagerPrivate::RenderState::Invalidated:
634 qCDebug(lcQuick3DXr,
"Render state: Invalidated");
635 QQuick3DXrManagerPrivate::get(q)->m_running =
false;
636 emit q->sessionEnded();
638 case QQuick3DXrManagerPrivate::RenderState::Paused:
639 QQuick3DXrManagerPrivate::get(q)->m_running =
false;
640 qCDebug(lcQuick3DXr,
"Render state: Paused");
643 }, Qt::DirectConnection);
645 QObject::connect(m_compositorLayer, &CompositorLayer::arStateChanged, q, [q](QQuick3DXrManagerPrivate::ArTrackingState state) {
647 case QQuick3DXrManagerPrivate::ArTrackingState::Uninitialized:
648 qCDebug(lcQuick3DXr,
"AR state: Uninitialized");
649 QQuick3DXrManagerPrivate::get(q)->m_arRunning =
false;
651 case QQuick3DXrManagerPrivate::ArTrackingState::Initialized:
652 qCDebug(lcQuick3DXr,
"AR state: Initialized");
653 QQuick3DXrManagerPrivate::get(q)->m_arRunning =
false;
655 case QQuick3DXrManagerPrivate::ArTrackingState::Running:
656 qCDebug(lcQuick3DXr,
"AR state: Running");
657 QQuick3DXrManagerPrivate::get(q)->m_arRunning =
true;
658 QCoreApplication::postEvent(q,
new QEvent(QEvent::UpdateRequest));
660 case QQuick3DXrManagerPrivate::ArTrackingState::Paused:
661 qCDebug(lcQuick3DXr,
"AR state: Paused");
662 QQuick3DXrManagerPrivate::get(q)->m_arRunning =
false;
664 case QQuick3DXrManagerPrivate::ArTrackingState::Stopped:
665 qCDebug(lcQuick3DXr,
"AR state: Stopped");
666 QQuick3DXrManagerPrivate::get(q)->m_arRunning =
false;
669 }, Qt::DirectConnection);
682 qWarning(
"QQuick3DXrManagerPrivate: Window is null!");
686 QSSG_ASSERT_X(m_compositorLayer !=
nullptr,
"No composition layer!",
return);
688 cp_layer_renderer_t renderer = m_compositorLayer->layerRenderer();
690 qWarning(
"QQuick3DXrManagerPrivate: Layer renderer is not available.");
694 auto device = cp_layer_renderer_get_device(renderer);
695 auto commandQueue = [device newCommandQueue];
697 auto qqGraphicsDevice = QQuickGraphicsDevice::fromDeviceAndCommandQueue(
static_cast<MTLDevice*>(device),
static_cast<MTLCommandQueue *>(commandQueue));
699 window->setGraphicsDevice(qqGraphicsDevice);
706 m_isGraphicsInitialized =
true;
707 return m_isGraphicsInitialized;
712 return m_compositorLayer && (m_compositorLayer->layerRenderer() !=
nullptr);
717 return m_isGraphicsInitialized;
722 QSSG_ASSERT(window !=
nullptr && m_compositorLayer !=
nullptr,
return false);
725 if (!m_renderThread) {
726 m_renderThread =
new QThread;
727 m_renderThread->setObjectName(QLatin1StringView(s_renderThreadName));
728 m_renderThread->start();
731 if (m_compositorLayer->thread() != m_renderThread)
732 m_compositorLayer->moveToThread(m_renderThread);
734 Q_ASSERT(m_renderThread !=
nullptr);
735 QQuickWindowPrivate::get(window)->renderControl->prepareThread(m_renderThread);
751 Q_Q(QQuick3DXrManager);
753 qCDebug(lcQuick3DXr) <<
"Tearing down XR session";
756 if (m_compositorLayer)
757 locker = QMutexLocker<QMutex>{ &m_compositorLayer
->renderLock() };
765 m_anchorManager->teardown();
767 if (m_compositorLayer) {
775 Qt::ConnectionType connection = (m_compositorLayer->thread() == QThread::currentThread())
776 ? Qt::DirectConnection : Qt::BlockingQueuedConnection;
777 QMetaObject::invokeMethod(m_compositorLayer, &CompositorLayer::destroy, connection,
this, q->m_quickWindow, m_compositorLayer);
778 m_compositorLayer =
nullptr;
781 if (m_renderThread) {
782 m_renderThread->quit();
783 m_renderThread->wait();
784 delete m_renderThread;
785 m_renderThread =
nullptr;
792 qCWarning(lcQuick3DXr) <<
"Changing multiview rendering is not supported at runtime on visionOS!";
797 return m_multiviewRenderingEnabled;
803 qCWarning(lcQuick3DXr) <<
"Changing passthrough is not supported at runtime on visionOS!";
810 return QtQuick3DXr::ReferenceSpace::ReferenceSpaceLocalFloor;
815 Q_UNUSED(newReferenceSpace);
816 qCWarning(lcQuick3DXr) <<
"Changing reference space is not supported at runtime on visionOS!";
822 qCWarning(lcQuick3DXr,
"Depth submission is required on visionOS");
827 Q_Q(QQuick3DXrManager);
830 QMutexLocker<QMutex> locker { &m_compositorLayer->renderLock() };
832 if (!m_running || !m_arRunning || !m_isGraphicsInitialized) {
833 qCDebug(lcQuick3DXr,
"Not running, skipping update");
839 q->m_renderControl->polishItems();
841 QCoreApplication::postEvent(m_compositorLayer,
new QEvent(CompositorLayer::asQEvent(CompositorLayer::Event::Render)));
846 QQuick3DXrAnimationDriver *animationDriver = q->m_animationDriver;
848 if (Q_LIKELY(waitCompleted && m_syncDone && animationDriver)) {
849 animationDriver->setStep(m_nextStepSize);
850 animationDriver->advance();
852 qCDebug(lcQuick3DXr) <<
"Failed to wait for sync to complete" <<
"\nWaitCompleted:" << waitCompleted <<
"\nsyncDone" << m_syncDone;
855 [[maybe_unused]]
bool ret = m_compositorLayer->renderFrame(locker);
858 QCoreApplication::postEvent(q,
new QEvent(QEvent::UpdateRequest));
868 Q_Q(QQuick3DXrManager);
869 QSSG_ASSERT(q->m_vrViewport !=
nullptr,
return);
870 QQuick3DXrInputManagerPrivate::processSpatialEvents(*q->m_vrViewport, events);
875 anchorManager->prepareAnchorManager(dataProviders);
880 anchorManager->initAnchorManager();
891 qCWarning(lcQuick3DXr) <<
"Changing sample count is not supported on visionOS!";
896 return QStringLiteral(
"visionOS");
901 static const auto versionNumber = QOperatingSystemVersion::current().version();
902 return versionNumber;
912 if (QRhiSwapChain *swapchain = window->swapChain()) {
913 swapchain->setShadingRateMap(srm);
914 if (!m_srmRenderPassDesc) {
915 qCDebug(lcQuick3DXr) <<
"Creating render pass descriptor suitable for shading rate map use";
916 m_srmRenderPassDesc = swapchain->newCompatibleRenderPassDescriptor();
917 swapchain->setRenderPassDescriptor(m_srmRenderPassDesc);
920 QSGRendererInterface *rif = window->rendererInterface();
921 QRhiTextureRenderTarget *rt =
static_cast<QRhiTextureRenderTarget *>(rif->getResource(window, QSGRendererInterface::RhiRedirectRenderTarget));
922 if (QSSG_GUARD_X(rt,
"No render target!")) {
924 desc.setShadingRateMap(srm);
925 rt->setDescription(desc);
926 if (!m_srmRenderPassDesc) {
927 qCDebug(lcQuick3DXr) <<
"Creating render pass descriptor suitable for shading rate map use";
928 QRhiRenderPassDescriptor *rpd = rt->renderPassDescriptor();
929 m_srmRenderPassDesc = rpd->newCompatibleRenderPassDescriptor();
930 rt->setRenderPassDescriptor(m_srmRenderPassDesc);
939 qCDebug(lcQuick3DXr) <<
"Releasing resources";
940 Q_ASSERT((m_renderThread ==
nullptr) || QThread::currentThread() == m_renderThread);
941 delete m_srmRenderPassDesc;
942 m_srmRenderPassDesc =
nullptr;
943 delete m_rhiDepthTexture;
944 m_rhiDepthTexture =
nullptr;
945 for (size_t i = 0, end = std::size(m_srm); i < end; ++i) {
953 Q_Q(QQuick3DXrManager);
956 QQuickWindow *window = q->m_quickWindow;
957 QQuickRenderControl *renderControl = q->m_renderControl;
958 QQuick3DXrOrigin *xrOrigin = q->m_xrOrigin;
959 QQuick3DViewport *xrViewport = q->m_vrViewport;
960 QQuick3DXrAnimationDriver *animationDriver = q->m_animationDriver;
962 QSSG_ASSERT_X(window && renderControl && xrViewport && xrOrigin && animationDriver,
"Invalid state, rendering aborted",
return false);
966 Q_ASSERT(!multiviewRenderingEnabled || QThread::currentThread() != window->thread());
968 auto layerRenderer = m_compositorLayer->layerRenderer();
969 cp_frame_t frame = cp_layer_renderer_query_next_frame(layerRenderer);
970 if (Q_UNLIKELY(frame ==
nullptr)) {
971 qWarning(
"Failed to get next frame");
975 cp_frame_timing_t timing = cp_frame_predict_timing(frame);
976 if (Q_UNLIKELY(timing ==
nullptr)) {
977 qWarning(
"Failed to get timing for frame");
981 cp_frame_start_update(frame);
983 cp_frame_end_update(frame);
985 cp_time_t optimalInputTime = cp_frame_timing_get_optimal_input_time(timing);
986 cp_time_wait_until(optimalInputTime);
988 cp_frame_start_submission(frame);
1000 cp_drawable_array_t drawables = cp_frame_query_drawables(frame);
1001 const size_t drawableCount = cp_drawable_array_get_count(drawables);
1002 if (Q_UNLIKELY(drawableCount == 0)) {
1004 qCDebug(lcQuick3DXr,
"No drawables for frame, skipping");
1008 cp_drawable_t drawable =
nullptr;
1009 for (size_t d = 0; d < drawableCount && !drawable; ++d) {
1010 cp_drawable_t candidate = cp_drawable_array_get_drawable(drawables, d);
1011 if (cp_drawable_get_target(candidate) == cp_drawable_target_built_in)
1012 drawable = candidate;
1014 if (Q_UNLIKELY(!drawable)) {
1015 qCWarning(lcQuick3DXr,
"No built-in drawable for frame, using the first one");
1016 drawable = cp_drawable_array_get_drawable(drawables, 0);
1018 if (drawableCount > 1) {
1019 static bool extraDrawablesWarningShown =
false;
1020 if (!extraDrawablesWarningShown) {
1021 extraDrawablesWarningShown =
true;
1022 qCWarning(lcQuick3DXr) <<
"Frame has" << drawableCount
1023 <<
"drawables; only the built-in one is rendered";
1027 cp_frame_timing_t actualTiming = cp_drawable_get_frame_timing(drawable);
1028 ar_device_anchor_t anchor = m_compositorLayer->createPoseForTiming(actualTiming);
1029 cp_drawable_set_device_anchor(drawable, anchor);
1032 simd_float4x4 headTransform = ar_anchor_get_origin_from_anchor_transform(anchor);
1035 QMatrix4x4 qtHeadTransform{headTransform.columns[0].x, headTransform.columns[1].x, headTransform.columns[2].x, headTransform.columns[3].x * 100,
1036 headTransform.columns[0].y, headTransform.columns[1].y, headTransform.columns[2].y, headTransform.columns[3].y * 100,
1037 headTransform.columns[0].z, headTransform.columns[1].z, headTransform.columns[2].z, headTransform.columns[3].z * 100,
1038 0.0f, 0.0f, 0.0f, 1.0f};
1039 xrOrigin->updateTrackedCamera(qtHeadTransform);
1042 if (QSSG_GUARD(m_inputManager !=
nullptr))
1047 enum : size_t { DisplayPeriod = 0, DisplayDelta };
1048 qint64 stepSizes[2] {0, 0};
1049 auto &[displayPeriodMS, displayDeltaMS] = stepSizes;
1050 displayPeriodMS = qint64(cp_time_to_cf_time_interval(optimalInputTime) * 1000.0);
1051 displayDeltaMS = ((qint64(cp_time_to_cf_time_interval(cp_frame_timing_get_optimal_input_time(actualTiming)) * 1000.0)) - m_previousTime);
1052 const size_t selector = ((m_previousTime == 0) || (displayDeltaMS > displayPeriodMS)) ? DisplayPeriod : DisplayDelta;
1053 m_nextStepSize = stepSizes[selector];
1054 m_previousTime = displayPeriodMS;
1056 if (!multiviewRenderingEnabled) {
1057 animationDriver->setStep(m_nextStepSize);
1058 animationDriver->advance();
1061 QRhi *rhi = renderControl->rhi();
1063 const auto viewCount = cp_drawable_get_view_count(drawable);
1064 const auto textureCount = cp_drawable_get_texture_count(drawable);
1065 const auto renderCalls = textureCount;
1068 Q_ASSERT(!foveationEnabled || rhi->isFeatureSupported(QRhi::VariableRateShadingMap));
1070 if (foveationEnabled) {
1071 const size_t rrmapcount = cp_drawable_get_rasterization_rate_map_count(drawable);
1072 const bool validMapCount = (rrmapcount == 1 || rrmapcount == 2);
1073 QSSG_GUARD_X(validMapCount,
"Invalid rate map count!");
1074 foveationEnabled = validMapCount;
1076 if (foveationEnabled) {
1077 for (size_t i = 0; i < rrmapcount; ++i) {
1079 m_srm[i] = rhi->newShadingRateMap();
1080 id<MTLRasterizationRateMap> rrm = cp_drawable_get_rasterization_rate_map(drawable, i);
1081 const bool wasCreated = m_srm[i]->createFrom({ quint64(rrm) });
1082 QSSG_GUARD_X(wasCreated,
"Failed to create shading rate map!");
1083 foveationEnabled = (foveationEnabled && wasCreated);
1096 for (size_t i = 0, end = renderCalls; i != end ; ++i) {
1098 id<MTLTexture> colorMetalTexture = cp_drawable_get_color_texture(drawable, i);
1105 cp_view_t view = cp_drawable_get_view(drawable, i);
1106 cp_view_texture_map_t textureMap = cp_view_get_view_texture_map(view);
1107 const MTLViewport viewport = cp_view_texture_map_get_viewport(textureMap);
1108 const QSize renderSize(
int(viewport.width),
int(viewport.height));
1117 const QSize renderTargetSize = renderSize;
1119 QQuickRenderTarget renderTarget;
1121 if (multiviewRenderingEnabled)
1122 renderTarget = QQuickRenderTarget::fromMetalTexture(
static_cast<MTLTexture*>(colorMetalTexture), [colorMetalTexture pixelFormat], [colorMetalTexture pixelFormat], renderTargetSize, 1 , viewCount, {});
1124 renderTarget = QQuickRenderTarget::fromMetalTexture(
static_cast<MTLTexture*>(colorMetalTexture), [colorMetalTexture pixelFormat], renderTargetSize);
1126 auto depthMetalTexture = cp_drawable_get_depth_texture(drawable, i);
1128 const QSize depthTextureSize = renderTargetSize;
1129 MTLPixelFormat depthTextureFormat = [depthMetalTexture pixelFormat];
1130 static const auto convertFormat = [](MTLPixelFormat format) -> QRhiTexture::Format {
1132 case MTLPixelFormatDepth16Unorm:
1133 return QRhiTexture::D16;
1134 case MTLPixelFormatDepth32Float:
1135 return QRhiTexture::D32F;
1137 qWarning(
"Unsupported depth texture format");
1138 return QRhiTexture::UnknownFormat;
1141 auto depthFormat = convertFormat(depthTextureFormat);
1142 if (depthFormat != QRhiTexture::UnknownFormat) {
1143 if (m_rhiDepthTexture && (m_rhiDepthTexture->format() != depthFormat || m_rhiDepthTexture->pixelSize() != depthTextureSize)) {
1144 delete m_rhiDepthTexture;
1145 m_rhiDepthTexture =
nullptr;
1148 if (!m_rhiDepthTexture) {
1149 if (multiviewRenderingEnabled)
1150 m_rhiDepthTexture = rhi->newTextureArray(depthFormat, viewCount, depthTextureSize, 1, QRhiTexture::RenderTarget);
1152 m_rhiDepthTexture = rhi->newTexture(depthFormat, depthTextureSize, 1, QRhiTexture::RenderTarget);
1156 m_rhiDepthTexture->createFrom({ quint64(
static_cast<MTLTexture*>(depthMetalTexture)), 0});
1157 renderTarget.setDepthTexture(m_rhiDepthTexture);
1160 window->setRenderTarget(renderTarget);
1163 window->setGeometry(0,
1166 renderSize.height());
1167 window->contentItem()->setSize(renderSize);
1172 xrViewport->setSize(renderSize);
1175 if (QSSG_GUARD(xrOrigin)) {
1176 if (multiviewRenderingEnabled)
1177 updateCameraMultiview(xrViewport, headTransform, drawable, xrOrigin);
1179 updateCamera(xrViewport, headTransform, drawable, xrOrigin, i);
1185 if (multiviewRenderingEnabled) {
1187 renderControl->beginFrame();
1189 m_syncDone = renderControl->sync();
1191 if (Q_UNLIKELY(!m_syncDone))
1194 if (foveationEnabled)
1195 setupShadingRateMap(window, m_srm[i]);
1198 waitCondition.wakeOne();
1202 renderControl->render();
1204 renderControl->endFrame();
1206 renderControl->polishItems();
1207 renderControl->beginFrame();
1208 renderControl->sync();
1209 if (foveationEnabled)
1210 setupShadingRateMap(window, m_srm[i]);
1211 renderControl->render();
1212 renderControl->endFrame();
1215#if TARGET_OS_SIMULATOR == 1
1219 QRhiRenderTarget *rt = QQuickWindowPrivate::get(window)->activeCustomRhiRenderTarget();
1220 if (rt->resourceType() == QRhiResource::TextureRenderTarget &&
static_cast<QRhiTextureRenderTarget *>(rt)->description().colorAttachmentAt(0)->texture())
1221 emit q->frameReady();
1225 id<MTLCommandBuffer> commandBuffer = [
static_cast<
const QRhiMetalNativeHandles*>(renderControl->rhi()->nativeHandles())->cmdQueue commandBuffer];
1227 cp_drawable_encode_present(drawable, commandBuffer);
1228 [commandBuffer commit];
1230 cp_frame_end_submission(frame);
1237#include "qquick3dxrmanager_visionos.moc"
void arStateChanged(QQuick3DXrManagerPrivate::ArTrackingState)
bool waitForSyncToComplete()
void configure(cp_layer_renderer_capabilities_t capabilities, cp_layer_renderer_configuration_t configuration) const override
static constexpr QEvent::Type asQEvent(CompositorLayer::Event event)
friend bool QQuick3DXrManagerPrivate::renderFrameImpl(QMutexLocker< QMutex > &locker, QWaitCondition &waitCondition)
void getDefaultDepthRange(float &near, float &far) const
static Q_INVOKABLE void destroy(QQuick3DXrManagerPrivate *mngr, QQuickWindow *window, CompositorLayer *compositorLayer)
bool isMultiviewEnabled() const
void layerRendererChanged()
static bool supportsLayoutType(cp_layer_renderer_capabilities_t capabilities, cp_layer_renderer_layout layout)
bool event(QEvent *event) override
This virtual function receives events to an object and should return true if the event e was recogniz...
void renderStateChanged(QQuick3DXrManagerPrivate::RenderState)
cp_layer_renderer_t layerRenderer() const
bool isMultiviewSupported() const
bool isFoveationEnabled() const
void render(cp_layer_renderer_t renderer) override
void handleSpatialEvents(const QJsonObject &events) override
std::underlying_type_t< QEvent::Type > EventT
void init(QQuick3DXrManagerPrivate *xrManager)
bool isFoveationSupported() const
bool isInitialized() const
static QQuick3DXrAnchorManager * instance()
QQuick3DXrManagerPrivate(QQuick3DXrManager &manager)
void setupWindow(QQuickWindow *window)
QVersionNumber runtimeVersion() const
QtQuick3DXr::ReferenceSpace getReferenceSpace() const
void setReferenceSpace(QtQuick3DXr::ReferenceSpace newReferenceSpace)
~QQuick3DXrManagerPrivate()
bool supportsPassthrough() const
bool setupGraphics(QQuickWindow *window)
void setMultiViewRenderingEnabled(bool enable)
bool isMultiViewRenderingEnabled() const
void setDepthSubmissionEnabled(bool enable)
void processSpatialEvents(const QJsonObject &events)
QString errorString() const
void setSamples(int samples)
void getDefaultClipDistances(float &nearClip, float &farClip) const
bool isGraphicsInitialized() const
bool setPassthroughEnabled(bool enable)
QString runtimeName() const
static bool isMultiviewRenderingDisabled()
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQIORing)
static constexpr float s_defaultClipNear
static constexpr float s_defaultClipFar
static void qssgUpdateDrawableDepthRange(cp_drawable_t drawable, float &clipNear, float &clipFar)
static const char s_renderThreadName[]
static bool qssgDisableFoveation()
static void qssgSanitizeDepthRange(simd_float2 depthRange, float &near, float &far)
QPointer< CompositorLayer > instance