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>
26#include <QtCore/qvarlengtharray.h>
28#include <TargetConditionals.h>
41 static bool foveationDisabled = qEnvironmentVariableIntValue(
"QT_QUICK3D_XR_DISABLE_FOVEATION") != 0;
42 return foveationDisabled;
53 static const MTLPixelFormat preferredFormats[] = { MTLPixelFormatBGRA8Unorm_sRGB, MTLPixelFormatRGBA8Unorm_sRGB };
55 const size_t count = cp_layer_renderer_capabilities_supported_color_formats_count_with_options(capabilities, cp_supported_color_formats_options_none);
56 QVarLengthArray<MTLPixelFormat, 8> supportedFormats;
57 for (size_t i = 0; i < count; ++i)
58 supportedFormats.append(cp_layer_renderer_capabilities_supported_color_format_with_options(capabilities, cp_supported_color_formats_options_none, i));
60 for (MTLPixelFormat format : preferredFormats) {
61 if (supportedFormats.contains(format))
65 qCWarning(lcQuick3DXr,
"The compositor layer supports no 8-bit sRGB color format, falling back to RGBA16Float. "
66 "Colors will be gamma-encoded twice and appear too bright.");
67 return MTLPixelFormatRGBA16Float;
73 case MTLPixelFormatBGRA8Unorm_sRGB:
74 return MTLPixelFormatBGRA8Unorm;
75 case MTLPixelFormatRGBA8Unorm_sRGB:
76 return MTLPixelFormatRGBA8Unorm;
108 if (!
std::isfinite(near) || near <= 0.0f)
110 if (!
std::isfinite(far) || far <= near)
121 m_xrManager =
nullptr;
122 [m_layerRenderer release];
123 m_layerRenderer =
nullptr;
124 m_worldTrackingProvider =
nullptr;
125 m_arSession =
nullptr;
126 m_initialized =
false;
140 static bool supportsLayoutType(cp_layer_renderer_capabilities_t capabilities, cp_layer_renderer_layout layout)
142 const size_t layoutCount = cp_layer_renderer_capabilities_supported_layouts_count(capabilities, cp_supported_layouts_options_none);
144 for (size_t i = 0; i < layoutCount && !found; ++i)
145 found = (layout == cp_layer_renderer_capabilities_supported_layout(capabilities, cp_supported_layouts_options_none, i));
150 void configure(cp_layer_renderer_capabilities_t capabilities, cp_layer_renderer_configuration_t configuration)
const override
158 m_multiviewSupported = supportsLayoutType(capabilities, cp_layer_renderer_layout_layered);
159 m_multiviewEnabled = m_multiviewEnabled && m_multiviewSupported;
162 m_foveationSupported = cp_layer_renderer_capabilities_supports_foveation(capabilities);
169 cp_layer_renderer_layout textureLayout = m_multiviewEnabled ? cp_layer_renderer_layout_layered
170 : cp_layer_renderer_layout_dedicated;
172 cp_layer_renderer_configuration_set_layout(configuration, textureLayout);
173 cp_layer_renderer_configuration_set_foveation_enabled(configuration, m_foveationEnabled);
174 m_colorFormat = qssgPreferredColorFormat(capabilities);
175 cp_layer_renderer_configuration_set_color_format(configuration, m_colorFormat);
178 const simd_float2 systemDepthRange = cp_layer_renderer_configuration_get_default_depth_range(configuration);
179 qssgSanitizeDepthRange(systemDepthRange, m_depthRange[0], m_depthRange[1]);
182 cp_layer_renderer_configuration_set_default_depth_range(configuration, simd_make_float2(m_depthRange[1], m_depthRange[0]));
184 qCDebug(lcQuick3DXr) <<
"Configuring with the following settings:"
185 <<
"\n\tMultiview supported: " << m_multiviewSupported
186 <<
"\n\tMultiview enabled: " << m_multiviewEnabled
187 <<
"\n\tFoveation supported: " << m_foveationSupported
188 <<
"\n\tFoveation enabled: " << m_foveationEnabled
189 <<
"\n\tColor format: " << NSUInteger(m_colorFormat)
190 <<
"\n\tSystem depth range: " << systemDepthRange.y <<
" to: " << systemDepthRange.x
191 <<
"\n\tDepth Range: " << m_depthRange[0] <<
" to: " << m_depthRange[1];
194 void render(cp_layer_renderer_t renderer)
override
196 if (m_layerRenderer != renderer) {
197 m_layerRenderer = renderer;
198 emit layerRendererChanged();
201 if (m_layerRenderer) {
206 [m_layerRenderer retain];
208 emit layerRendererReady();
216 m_xrManager->processSpatialEvents(events);
228 return m_layerRenderer;
233 near = m_depthRange[0];
234 far = m_depthRange[1];
240 Q_ASSERT(qApp->thread() == QThread::currentThread());
241 QSSG_ASSERT(!m_initialized,
return);
243 m_xrManager = xrManager;
244 runWorldTrackingARSession();
245 m_initialized =
true;
255 Q_ASSERT(qApp->thread() == QThread::currentThread());
260 qCDebug(lcQuick3DXr,
"Stopping AR session");
264 ar_session_set_data_provider_state_change_handler_f(m_arSession, dispatch_get_main_queue(),
this, &onArStateChanged);
265 ar_session_stop(m_arSession);
266 m_arSession =
nullptr;
274 return m_waitCondition.wait(&m_mutex);
279 QSSG_ASSERT(mngr !=
nullptr,
return);
281 qCDebug(lcQuick3DXr) <<
"Destroying compositor layer";
283 if (
auto *visionOSApplicaton = qGuiApp->nativeInterface<QNativeInterface::QVisionOSApplication>())
284 visionOSApplicaton->setImmersiveSpaceCompositorLayer(
nullptr);
294 auto *d = QQuickWindowPrivate::get(window);
295 d->cleanupNodesOnShutdown();
296 mngr->releaseResources();
297 if (
auto *rc = d->renderControl)
303 compositorLayer->deleteLater();
321 qCDebug(lcQuick3DXr) <<
"Teardown in progress, skipping event handling";
324 if (event->type() == QEvent::DeferredDelete)
326 return QObject::event(event);
329 switch (
static_cast<
Event>(event->type())) {
332 const bool success = renderFrame(locker);
337 qCDebug(lcQuick3DXr) <<
"Waking up the GUI thread, rendering returned early...";
338 m_xrManager->m_syncDone =
false;
339 m_waitCondition.wakeOne();
355 return QObject::event(event);
362 static void onArStateChanged(
void *context,
363 ar_data_providers_t data_providers,
364 ar_data_provider_state_t new_state,
366 ar_data_provider_t failed_data_provider)
369 Q_UNUSED(data_providers);
371 Q_UNUSED(failed_data_provider);
378 QSSG_ASSERT_X(that->m_arSession !=
nullptr,
"AR session is not running, skipping state change!",
return);
380 const auto oldState = that->m_arTrackingState;
382 case ar_data_provider_state_initialized:
385 case ar_data_provider_state_running:
388 case ar_data_provider_state_paused:
391 case ar_data_provider_state_stopped:
397 if (oldState != that->m_arTrackingState) {
399 emit that->arStateChanged(that->m_arTrackingState);
403 enum class RenderStateAction
409 template<RenderStateAction Action = RenderStateAction::WaitUntilRunning>
410 void checkRenderState()
412 QSSG_ASSERT(m_layerRenderer !=
nullptr,
return);
414 const auto oldState = m_renderState;
415 switch (cp_layer_renderer_get_state(m_layerRenderer)) {
416 case cp_layer_renderer_state_paused:
417 m_renderState = QQuick3DXrManagerPrivate::RenderState::Paused;
419 case cp_layer_renderer_state_running:
420 m_renderState = QQuick3DXrManagerPrivate::RenderState::Running;
422 case cp_layer_renderer_state_invalidated:
423 m_renderState = QQuick3DXrManagerPrivate::RenderState::Invalidated;
427 if (oldState != m_renderState)
428 emit renderStateChanged(m_renderState);
430 if (m_renderState == QQuick3DXrManagerPrivate::RenderState::Paused) {
431 if constexpr (Action == RenderStateAction::WaitUntilRunning) {
432 qCDebug(lcQuick3DXr) <<
"Waiting for rendering to resume...";
433 cp_layer_renderer_wait_until_running(m_layerRenderer);
434 QCoreApplication::postEvent(
this,
new QEvent(asQEvent(CompositorLayer::Event::Pulse)));
439 ar_device_anchor_t createPoseForTiming(cp_frame_timing_t timing)
441 QSSG_ASSERT(m_worldTrackingProvider !=
nullptr,
return nullptr);
443 ar_device_anchor_t outAnchor = ar_device_anchor_create();
444 cp_time_t presentationTime = cp_frame_timing_get_presentation_time(timing);
445 CFTimeInterval queryTime = cp_time_to_cf_time_interval(presentationTime);
446 ar_device_anchor_query_status_t status = ar_world_tracking_provider_query_device_anchor_at_timestamp(m_worldTrackingProvider, queryTime, outAnchor);
447 if (status != ar_device_anchor_query_status_success) {
448 NSLog(@
"Failed to get estimated pose from world tracking provider for presentation timestamp %0.3f", queryTime);
453 void runWorldTrackingARSession()
457 ar_world_tracking_configuration_t worldTrackingConfiguration = ar_world_tracking_configuration_create();
458 m_worldTrackingProvider = ar_world_tracking_provider_create(worldTrackingConfiguration);
460 ar_data_providers_t dataProviders = ar_data_providers_create();
461 ar_data_providers_add_data_provider(dataProviders, m_worldTrackingProvider);
468 if (QSSG_GUARD_X(inputManager !=
nullptr,
"No InputManager available!")) {
470 if (QSSG_GUARD(pim !=
nullptr))
474 if (QSSG_GUARD_X(anchorManager !=
nullptr,
"No AnchorManager available!"))
475 QQuick3DXrManagerPrivate::prepareAnchorManager(anchorManager, dataProviders);
477 m_arSession = ar_session_create();
478 ar_session_set_data_provider_state_change_handler_f(m_arSession, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
this, &onArStateChanged);
479 ar_session_run(m_arSession, dataProviders);
488 qCDebug(lcQuick3DXr,
"Pausing rendering");
493 qCDebug(lcQuick3DXr,
"Cleaning up");
495 m_xrManager->releaseResources();
498 [[nodiscard]]
bool renderFrame(
QMutexLocker<QMutex> &locker)
500 Q_ASSERT(locker.isLocked());
503 if (m_renderState != QQuick3DXrManagerPrivate::RenderState::Running) {
504 qCDebug(lcQuick3DXr,
"Rendering is not running, skipping frame rendering!");
512 qCDebug(lcQuick3DXr,
"AR tracking is not running, skipping frame rendering!");
520 return m_xrManager->renderFrameImpl(locker, m_waitCondition);
523 ar_world_tracking_provider_t worldTrackingProvider()
const
525 return m_worldTrackingProvider;
529 mutable QMutex m_mutex;
530 QWaitCondition m_waitCondition;
532 mutable QMutex m_compositorLayerMtx;
535 mutable QMutex m_arSessionMtx;
539 cp_layer_renderer_t m_layerRenderer =
nullptr;
540 ar_world_tracking_provider_t m_worldTrackingProvider =
nullptr;
541 ar_session_t m_arSession =
nullptr;
542 mutable float m_depthRange[2] {1.0f, 10000.0f};
545 bool m_initialized =
false;
546 bool m_teardown =
false;
547 mutable bool m_multiviewSupported =
true;
548 mutable bool m_multiviewEnabled =
true;
549 mutable bool m_foveationSupported =
true;
550 mutable bool m_foveationEnabled =
true;
567 const simd_float2 reportedRange = cp_drawable_get_depth_range(drawable);
568 qssgSanitizeDepthRange(reportedRange, clipNear, clipFar);
571 if (reportedRange.y != clipNear || reportedRange.x != clipFar)
572 cp_drawable_set_depth_range(drawable, simd_make_float2(clipFar, clipNear));
576void QQuick3DXrManagerPrivate::updateCameraImp(simd_float4x4 headTransform, cp_drawable_t drawable, QQuick3DXrOrigin *xrOrigin,
int i)
578 cp_view_t view = cp_drawable_get_view(drawable, i);
581 float clipNear, clipFar;
582 qssgUpdateDrawableDepthRange(drawable, clipNear, clipFar);
584 xrOrigin->eyeCamera(i)->setClipNear(clipNear);
585 xrOrigin->eyeCamera(i)->setClipFar(clipFar);
588 simd_float4x4 projection = cp_drawable_compute_projection(drawable, cp_axis_direction_convention_right_up_forward, i);
589 QMatrix4x4 proj{projection.columns[0].x, projection.columns[1].x, projection.columns[2].x, projection.columns[3].x,
590 projection.columns[0].y, projection.columns[1].y, projection.columns[2].y, projection.columns[3].y,
591 projection.columns[0].z, projection.columns[1].z, projection.columns[2].z, projection.columns[3].z,
592 projection.columns[0].w, projection.columns[1].w, projection.columns[2].w, projection.columns[3].w};
593 xrOrigin->eyeCamera(i)->setProjection(proj);
595 simd_float4x4 localEyeTransform = cp_view_get_transform(view);
596 simd_float4x4 eyeCameraTransform = simd_mul(headTransform, localEyeTransform);
598 QMatrix4x4 transform{eyeCameraTransform.columns[0].x, eyeCameraTransform.columns[1].x, eyeCameraTransform.columns[2].x, eyeCameraTransform.columns[3].x * 100,
599 eyeCameraTransform.columns[0].y, eyeCameraTransform.columns[1].y, eyeCameraTransform.columns[2].y, eyeCameraTransform.columns[3].y * 100,
600 eyeCameraTransform.columns[0].z, eyeCameraTransform.columns[1].z, eyeCameraTransform.columns[2].z, eyeCameraTransform.columns[3].z * 100,
601 0.0f, 0.0f, 0.0f, 1.0f};
602 QQuick3DNodePrivate::get(xrOrigin->eyeCamera(i))->setLocalTransform(transform);
605void QQuick3DXrManagerPrivate::updateCamera(QQuick3DViewport *xrViewport, simd_float4x4 headTransform, cp_drawable_t drawable, QQuick3DXrOrigin *xrOrigin,
int i)
607 updateCameraImp(headTransform, drawable, xrOrigin, i);
608 xrViewport->setCamera(xrOrigin->eyeCamera(i));
611void QQuick3DXrManagerPrivate::updateCameraMultiview(QQuick3DViewport *xrViewport, simd_float4x4 headTransform, cp_drawable_t drawable, QQuick3DXrOrigin *xrOrigin)
613 QQuick3DCamera *cameras[2] {xrOrigin->eyeCamera(0), xrOrigin->eyeCamera(1)};
615 for (
int i = 0; i < 2; ++i)
616 updateCameraImp(headTransform, drawable, xrOrigin, i);
618 xrViewport->setMultiViewCameras(cameras);
633 QSSG_ASSERT(manager !=
nullptr,
return nullptr);
634 return manager->d_func();
639 Q_Q(QQuick3DXrManager);
644 if (!m_compositorLayer) {
645 m_compositorLayer = s_compositorLayer->instance;
646 if (!m_compositorLayer) {
648 s_compositorLayer->instance = m_compositorLayer;
653 m_inputManager = QQuick3DXrInputManager::instance();
654 if (!m_anchorManager)
655 m_anchorManager = QQuick3DXrAnchorManager::instance();
659 if (
auto *visionOSApplicaton = qGuiApp->nativeInterface<QNativeInterface::QVisionOSApplication>()) {
660 visionOSApplicaton->setImmersiveSpaceCompositorLayer(m_compositorLayer);
662 QObject::connect(m_compositorLayer, &CompositorLayer::layerRendererReady, q, &QQuick3DXrManager::initialized, Qt::ConnectionType(Qt::SingleShotConnection | Qt::QueuedConnection));
663 QObject::connect(m_compositorLayer, &CompositorLayer::renderStateChanged, q, [q](QQuick3DXrManagerPrivate::RenderState state) {
665 case QQuick3DXrManagerPrivate::RenderState::Uninitialized:
666 qCDebug(lcQuick3DXr,
"Render state: Uninitialized");
667 QQuick3DXrManagerPrivate::get(q)->m_running =
false;
669 case QQuick3DXrManagerPrivate::RenderState::Running:
670 qCDebug(lcQuick3DXr,
"Render state: Running");
671 QQuick3DXrManagerPrivate::get(q)->m_running =
true;
672 QCoreApplication::postEvent(q,
new QEvent(QEvent::UpdateRequest));
674 case QQuick3DXrManagerPrivate::RenderState::Invalidated:
675 qCDebug(lcQuick3DXr,
"Render state: Invalidated");
676 QQuick3DXrManagerPrivate::get(q)->m_running =
false;
677 emit q->sessionEnded();
679 case QQuick3DXrManagerPrivate::RenderState::Paused:
680 QQuick3DXrManagerPrivate::get(q)->m_running =
false;
681 qCDebug(lcQuick3DXr,
"Render state: Paused");
684 }, Qt::DirectConnection);
686 QObject::connect(m_compositorLayer, &CompositorLayer::arStateChanged, q, [q](QQuick3DXrManagerPrivate::ArTrackingState state) {
688 case QQuick3DXrManagerPrivate::ArTrackingState::Uninitialized:
689 qCDebug(lcQuick3DXr,
"AR state: Uninitialized");
690 QQuick3DXrManagerPrivate::get(q)->m_arRunning =
false;
692 case QQuick3DXrManagerPrivate::ArTrackingState::Initialized:
693 qCDebug(lcQuick3DXr,
"AR state: Initialized");
694 QQuick3DXrManagerPrivate::get(q)->m_arRunning =
false;
696 case QQuick3DXrManagerPrivate::ArTrackingState::Running:
697 qCDebug(lcQuick3DXr,
"AR state: Running");
698 QQuick3DXrManagerPrivate::get(q)->m_arRunning =
true;
699 QCoreApplication::postEvent(q,
new QEvent(QEvent::UpdateRequest));
701 case QQuick3DXrManagerPrivate::ArTrackingState::Paused:
702 qCDebug(lcQuick3DXr,
"AR state: Paused");
703 QQuick3DXrManagerPrivate::get(q)->m_arRunning =
false;
705 case QQuick3DXrManagerPrivate::ArTrackingState::Stopped:
706 qCDebug(lcQuick3DXr,
"AR state: Stopped");
707 QQuick3DXrManagerPrivate::get(q)->m_arRunning =
false;
710 }, Qt::DirectConnection);
723 qWarning(
"QQuick3DXrManagerPrivate: Window is null!");
727 QSSG_ASSERT_X(m_compositorLayer !=
nullptr,
"No composition layer!",
return);
729 cp_layer_renderer_t renderer = m_compositorLayer->layerRenderer();
731 qWarning(
"QQuick3DXrManagerPrivate: Layer renderer is not available.");
735 auto device = cp_layer_renderer_get_device(renderer);
736 auto commandQueue = [device newCommandQueue];
738 auto qqGraphicsDevice = QQuickGraphicsDevice::fromDeviceAndCommandQueue(
static_cast<MTLDevice*>(device),
static_cast<MTLCommandQueue *>(commandQueue));
740 window->setGraphicsDevice(qqGraphicsDevice);
747 m_isGraphicsInitialized =
true;
748 return m_isGraphicsInitialized;
753 return m_compositorLayer && (m_compositorLayer->layerRenderer() !=
nullptr);
758 return m_isGraphicsInitialized;
763 QSSG_ASSERT(window !=
nullptr && m_compositorLayer !=
nullptr,
return false);
766 if (!m_renderThread) {
767 m_renderThread =
new QThread;
768 m_renderThread->setObjectName(QLatin1StringView(s_renderThreadName));
769 m_renderThread->start();
772 if (m_compositorLayer->thread() != m_renderThread)
773 m_compositorLayer->moveToThread(m_renderThread);
775 Q_ASSERT(m_renderThread !=
nullptr);
776 QQuickWindowPrivate::get(window)->renderControl->prepareThread(m_renderThread);
792 Q_Q(QQuick3DXrManager);
794 qCDebug(lcQuick3DXr) <<
"Tearing down XR session";
797 if (m_compositorLayer)
798 locker = QMutexLocker<QMutex>{ &m_compositorLayer
->renderLock() };
806 m_anchorManager->teardown();
808 if (m_compositorLayer) {
816 Qt::ConnectionType connection = (m_compositorLayer->thread() == QThread::currentThread())
817 ? Qt::DirectConnection : Qt::BlockingQueuedConnection;
818 QMetaObject::invokeMethod(m_compositorLayer, &CompositorLayer::destroy, connection,
this, q->m_quickWindow, m_compositorLayer);
819 m_compositorLayer =
nullptr;
822 if (m_renderThread) {
823 m_renderThread->quit();
824 m_renderThread->wait();
825 delete m_renderThread;
826 m_renderThread =
nullptr;
833 qCWarning(lcQuick3DXr) <<
"Changing multiview rendering is not supported at runtime on visionOS!";
838 return m_multiviewRenderingEnabled;
844 qCWarning(lcQuick3DXr) <<
"Changing passthrough is not supported at runtime on visionOS!";
851 return QtQuick3DXr::ReferenceSpace::ReferenceSpaceLocalFloor;
856 Q_UNUSED(newReferenceSpace);
857 qCWarning(lcQuick3DXr) <<
"Changing reference space is not supported at runtime on visionOS!";
863 qCWarning(lcQuick3DXr,
"Depth submission is required on visionOS");
868 Q_Q(QQuick3DXrManager);
871 QMutexLocker<QMutex> locker { &m_compositorLayer->renderLock() };
873 if (!m_running || !m_arRunning || !m_isGraphicsInitialized) {
874 qCDebug(lcQuick3DXr,
"Not running, skipping update");
880 q->m_renderControl->polishItems();
882 QCoreApplication::postEvent(m_compositorLayer,
new QEvent(CompositorLayer::asQEvent(CompositorLayer::Event::Render)));
887 QQuick3DXrAnimationDriver *animationDriver = q->m_animationDriver;
889 if (Q_LIKELY(waitCompleted && m_syncDone && animationDriver)) {
890 animationDriver->setStep(m_nextStepSize);
891 animationDriver->advance();
893 qCDebug(lcQuick3DXr) <<
"Failed to wait for sync to complete" <<
"\nWaitCompleted:" << waitCompleted <<
"\nsyncDone" << m_syncDone;
896 [[maybe_unused]]
bool ret = m_compositorLayer->renderFrame(locker);
899 QCoreApplication::postEvent(q,
new QEvent(QEvent::UpdateRequest));
909 Q_Q(QQuick3DXrManager);
910 QSSG_ASSERT(q->m_vrViewport !=
nullptr,
return);
911 QQuick3DXrInputManagerPrivate::processSpatialEvents(*q->m_vrViewport, events);
916 anchorManager->prepareAnchorManager(dataProviders);
921 anchorManager->initAnchorManager();
932 qCWarning(lcQuick3DXr) <<
"Changing sample count is not supported on visionOS!";
937 return QStringLiteral(
"visionOS");
942 static const auto versionNumber = QOperatingSystemVersion::current().version();
943 return versionNumber;
953 if (QRhiSwapChain *swapchain = window->swapChain()) {
954 swapchain->setShadingRateMap(srm);
955 if (!m_srmRenderPassDesc) {
956 qCDebug(lcQuick3DXr) <<
"Creating render pass descriptor suitable for shading rate map use";
957 m_srmRenderPassDesc = swapchain->newCompatibleRenderPassDescriptor();
958 swapchain->setRenderPassDescriptor(m_srmRenderPassDesc);
961 QSGRendererInterface *rif = window->rendererInterface();
962 QRhiTextureRenderTarget *rt =
static_cast<QRhiTextureRenderTarget *>(rif->getResource(window, QSGRendererInterface::RhiRedirectRenderTarget));
963 if (QSSG_GUARD_X(rt,
"No render target!")) {
965 desc.setShadingRateMap(srm);
966 rt->setDescription(desc);
967 if (!m_srmRenderPassDesc) {
968 qCDebug(lcQuick3DXr) <<
"Creating render pass descriptor suitable for shading rate map use";
969 QRhiRenderPassDescriptor *rpd = rt->renderPassDescriptor();
970 m_srmRenderPassDesc = rpd->newCompatibleRenderPassDescriptor();
971 rt->setRenderPassDescriptor(m_srmRenderPassDesc);
980 qCDebug(lcQuick3DXr) <<
"Releasing resources";
981 Q_ASSERT((m_renderThread ==
nullptr) || QThread::currentThread() == m_renderThread);
982 delete m_srmRenderPassDesc;
983 m_srmRenderPassDesc =
nullptr;
984 delete m_rhiDepthTexture;
985 m_rhiDepthTexture =
nullptr;
986 for (size_t i = 0, end = std::size(m_srm); i < end; ++i) {
994 Q_Q(QQuick3DXrManager);
997 QQuickWindow *window = q->m_quickWindow;
998 QQuickRenderControl *renderControl = q->m_renderControl;
999 QQuick3DXrOrigin *xrOrigin = q->m_xrOrigin;
1000 QQuick3DViewport *xrViewport = q->m_vrViewport;
1001 QQuick3DXrAnimationDriver *animationDriver = q->m_animationDriver;
1003 QSSG_ASSERT_X(window && renderControl && xrViewport && xrOrigin && animationDriver,
"Invalid state, rendering aborted",
return false);
1007 Q_ASSERT(!multiviewRenderingEnabled || QThread::currentThread() != window->thread());
1009 auto layerRenderer = m_compositorLayer->layerRenderer();
1010 cp_frame_t frame = cp_layer_renderer_query_next_frame(layerRenderer);
1011 if (Q_UNLIKELY(frame ==
nullptr)) {
1012 qWarning(
"Failed to get next frame");
1016 cp_frame_timing_t timing = cp_frame_predict_timing(frame);
1017 if (Q_UNLIKELY(timing ==
nullptr)) {
1018 qWarning(
"Failed to get timing for frame");
1022 cp_frame_start_update(frame);
1024 cp_frame_end_update(frame);
1026 cp_time_t optimalInputTime = cp_frame_timing_get_optimal_input_time(timing);
1027 cp_time_wait_until(optimalInputTime);
1029 cp_frame_start_submission(frame);
1041 cp_drawable_array_t drawables = cp_frame_query_drawables(frame);
1042 const size_t drawableCount = cp_drawable_array_get_count(drawables);
1043 if (Q_UNLIKELY(drawableCount == 0)) {
1045 qCDebug(lcQuick3DXr,
"No drawables for frame, skipping");
1049 cp_drawable_t drawable =
nullptr;
1050 for (size_t d = 0; d < drawableCount && !drawable; ++d) {
1051 cp_drawable_t candidate = cp_drawable_array_get_drawable(drawables, d);
1052 if (cp_drawable_get_target(candidate) == cp_drawable_target_built_in)
1053 drawable = candidate;
1055 if (Q_UNLIKELY(!drawable)) {
1056 qCWarning(lcQuick3DXr,
"No built-in drawable for frame, using the first one");
1057 drawable = cp_drawable_array_get_drawable(drawables, 0);
1059 if (drawableCount > 1) {
1060 static bool extraDrawablesWarningShown =
false;
1061 if (!extraDrawablesWarningShown) {
1062 extraDrawablesWarningShown =
true;
1063 qCWarning(lcQuick3DXr) <<
"Frame has" << drawableCount
1064 <<
"drawables; only the built-in one is rendered";
1068 cp_frame_timing_t actualTiming = cp_drawable_get_frame_timing(drawable);
1069 ar_device_anchor_t anchor = m_compositorLayer->createPoseForTiming(actualTiming);
1070 cp_drawable_set_device_anchor(drawable, anchor);
1073 simd_float4x4 headTransform = ar_anchor_get_origin_from_anchor_transform(anchor);
1076 QMatrix4x4 qtHeadTransform{headTransform.columns[0].x, headTransform.columns[1].x, headTransform.columns[2].x, headTransform.columns[3].x * 100,
1077 headTransform.columns[0].y, headTransform.columns[1].y, headTransform.columns[2].y, headTransform.columns[3].y * 100,
1078 headTransform.columns[0].z, headTransform.columns[1].z, headTransform.columns[2].z, headTransform.columns[3].z * 100,
1079 0.0f, 0.0f, 0.0f, 1.0f};
1080 xrOrigin->updateTrackedCamera(qtHeadTransform);
1083 if (QSSG_GUARD(m_inputManager !=
nullptr))
1088 enum : size_t { DisplayPeriod = 0, DisplayDelta };
1089 qint64 stepSizes[2] {0, 0};
1090 auto &[displayPeriodMS, displayDeltaMS] = stepSizes;
1091 displayPeriodMS = qint64(cp_time_to_cf_time_interval(optimalInputTime) * 1000.0);
1092 displayDeltaMS = ((qint64(cp_time_to_cf_time_interval(cp_frame_timing_get_optimal_input_time(actualTiming)) * 1000.0)) - m_previousTime);
1093 const size_t selector = ((m_previousTime == 0) || (displayDeltaMS > displayPeriodMS)) ? DisplayPeriod : DisplayDelta;
1094 m_nextStepSize = stepSizes[selector];
1095 m_previousTime = displayPeriodMS;
1097 if (!multiviewRenderingEnabled) {
1098 animationDriver->setStep(m_nextStepSize);
1099 animationDriver->advance();
1102 QRhi *rhi = renderControl->rhi();
1104 const auto viewCount = cp_drawable_get_view_count(drawable);
1105 const auto textureCount = cp_drawable_get_texture_count(drawable);
1106 const auto renderCalls = textureCount;
1109 Q_ASSERT(!foveationEnabled || rhi->isFeatureSupported(QRhi::VariableRateShadingMap));
1111 if (foveationEnabled) {
1112 const size_t rrmapcount = cp_drawable_get_rasterization_rate_map_count(drawable);
1113 const bool validMapCount = (rrmapcount == 1 || rrmapcount == 2);
1114 QSSG_GUARD_X(validMapCount,
"Invalid rate map count!");
1115 foveationEnabled = validMapCount;
1117 if (foveationEnabled) {
1118 for (size_t i = 0; i < rrmapcount; ++i) {
1120 m_srm[i] = rhi->newShadingRateMap();
1121 id<MTLRasterizationRateMap> rrm = cp_drawable_get_rasterization_rate_map(drawable, i);
1122 const bool wasCreated = m_srm[i]->createFrom({ quint64(rrm) });
1123 QSSG_GUARD_X(wasCreated,
"Failed to create shading rate map!");
1124 foveationEnabled = (foveationEnabled && wasCreated);
1138 const int viewsPerTexture = multiviewRenderingEnabled ?
int(viewCount) : 1;
1139 const bool multiviewTarget = viewsPerTexture >= 2;
1140 QSSG_ASSERT_X(viewsPerTexture <= 2,
"Unsupported number of views per texture",
return false);
1142 for (size_t i = 0, end = renderCalls; i != end ; ++i) {
1144 id<MTLTexture> colorMetalTexture = cp_drawable_get_color_texture(drawable, i);
1149 const MTLPixelFormat colorFormat = [colorMetalTexture pixelFormat];
1150 const MTLPixelFormat colorViewFormat = qssgNonSrgbFormat(colorFormat);
1157 cp_view_t view = cp_drawable_get_view(drawable, i);
1158 cp_view_texture_map_t textureMap = cp_view_get_view_texture_map(view);
1159 const MTLViewport viewport = cp_view_texture_map_get_viewport(textureMap);
1160 const QSize renderSize(
int(viewport.width),
int(viewport.height));
1164 const QSize textureSize = QSize([colorMetalTexture width], [colorMetalTexture height]);
1167 QQuickRenderTarget renderTarget = QQuickRenderTarget::fromMetalTexture(
static_cast<MTLTexture*>(colorMetalTexture),
1175 auto depthMetalTexture = cp_drawable_get_depth_texture(drawable, i);
1176 const QSize depthTextureSize = QSize([depthMetalTexture width], [depthMetalTexture height]);
1177 MTLPixelFormat depthTextureFormat = [depthMetalTexture pixelFormat];
1178 static const auto convertFormat = [](MTLPixelFormat format) -> QRhiTexture::Format {
1180 case MTLPixelFormatDepth16Unorm:
1181 return QRhiTexture::D16;
1182 case MTLPixelFormatDepth32Float:
1183 return QRhiTexture::D32F;
1185 qWarning(
"Unsupported depth texture format");
1186 return QRhiTexture::UnknownFormat;
1189 auto depthFormat = convertFormat(depthTextureFormat);
1190 if (depthFormat != QRhiTexture::UnknownFormat) {
1191 if (m_rhiDepthTexture && (m_rhiDepthTexture->format() != depthFormat || m_rhiDepthTexture->pixelSize() != depthTextureSize)) {
1192 delete m_rhiDepthTexture;
1193 m_rhiDepthTexture =
nullptr;
1196 if (!m_rhiDepthTexture) {
1197 if (multiviewTarget)
1198 m_rhiDepthTexture = rhi->newTextureArray(depthFormat, viewsPerTexture, depthTextureSize, 1, QRhiTexture::RenderTarget);
1200 m_rhiDepthTexture = rhi->newTexture(depthFormat, depthTextureSize, 1, QRhiTexture::RenderTarget);
1204 m_rhiDepthTexture->createFrom({ quint64(
static_cast<MTLTexture*>(depthMetalTexture)), 0});
1205 renderTarget.setDepthTexture(m_rhiDepthTexture);
1208 window->setRenderTarget(renderTarget);
1211 window->setGeometry(0,
1214 renderSize.height());
1215 window->contentItem()->setSize(renderSize);
1220 xrViewport->setSize(renderSize);
1223 if (QSSG_GUARD(xrOrigin)) {
1224 if (multiviewTarget)
1225 updateCameraMultiview(xrViewport, headTransform, drawable, xrOrigin);
1227 updateCamera(xrViewport, headTransform, drawable, xrOrigin, i);
1233 if (multiviewRenderingEnabled) {
1235 renderControl->beginFrame();
1237 m_syncDone = renderControl->sync();
1239 if (Q_UNLIKELY(!m_syncDone))
1242 if (foveationEnabled)
1243 setupShadingRateMap(window, m_srm[i]);
1246 waitCondition.wakeOne();
1250 renderControl->render();
1252 renderControl->endFrame();
1254 renderControl->polishItems();
1255 renderControl->beginFrame();
1256 renderControl->sync();
1257 if (foveationEnabled)
1258 setupShadingRateMap(window, m_srm[i]);
1259 renderControl->render();
1260 renderControl->endFrame();
1263#if TARGET_OS_SIMULATOR == 1
1267 QRhiRenderTarget *rt = QQuickWindowPrivate::get(window)->activeCustomRhiRenderTarget();
1268 if (rt->resourceType() == QRhiResource::TextureRenderTarget &&
static_cast<QRhiTextureRenderTarget *>(rt)->description().colorAttachmentAt(0)->texture())
1269 emit q->frameReady();
1273 id<MTLCommandBuffer> commandBuffer = [
static_cast<
const QRhiMetalNativeHandles*>(renderControl->rhi()->nativeHandles())->cmdQueue commandBuffer];
1275 cp_drawable_encode_present(drawable, commandBuffer);
1276 [commandBuffer commit];
1278 cp_frame_end_submission(frame);
1285#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 MTLPixelFormat qssgNonSrgbFormat(MTLPixelFormat format)
static MTLPixelFormat qssgPreferredColorFormat(cp_layer_renderer_capabilities_t capabilities)
static bool qssgDisableFoveation()
static void qssgSanitizeDepthRange(simd_float2 depthRange, float &near, float &far)
QPointer< CompositorLayer > instance