19#include <QtQuick3DUtils/private/qssgassert_p.h>
20#include <QtQuick3D/private/qquick3dviewport_p.h>
22#include <QtQuick/qquickwindow.h>
23#include <QtQuick/qquickrendercontrol.h>
25#include <QtGui/qquaternion.h>
27#include <QtCore/qobject.h>
29#include <openxr/openxr_reflection.h>
31#ifdef XR_USE_GRAPHICS_API_VULKAN
32# include "qopenxrgraphics_vulkan_p.h"
35#ifdef XR_USE_GRAPHICS_API_D3D11
36# include "qopenxrgraphics_d3d11_p.h"
39#ifdef XR_USE_GRAPHICS_API_D3D12
40# include "qopenxrgraphics_d3d12_p.h"
43#ifdef XR_USE_GRAPHICS_API_OPENGL
44# include "qopenxrgraphics_opengl_p.h"
47#ifdef XR_USE_PLATFORM_ANDROID
48# include <QtCore/qnativeinterface.h>
49# include <QtCore/QJniEnvironment>
50# include <QtCore/QJniObject>
51# ifdef XR_USE_GRAPHICS_API_OPENGL_ES
52# include "qopenxrgraphics_opengles_p.h"
56#ifdef XR_USE_GRAPHICS_API_METAL
57# include "qopenxrgraphics_metal_p.h"
60static XrReferenceSpaceType getXrReferenceSpaceType(QtQuick3DXr::ReferenceSpace referenceSpace)
62 switch (referenceSpace) {
63 case QtQuick3DXr::ReferenceSpace::ReferenceSpaceLocal:
64 return XR_REFERENCE_SPACE_TYPE_LOCAL;
65 case QtQuick3DXr::ReferenceSpace::ReferenceSpaceStage:
66 return XR_REFERENCE_SPACE_TYPE_STAGE;
67 case QtQuick3DXr::ReferenceSpace::ReferenceSpaceLocalFloor:
68 return XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;
70 return XR_REFERENCE_SPACE_TYPE_LOCAL;
76 switch (referenceSpace) {
77 case XR_REFERENCE_SPACE_TYPE_LOCAL:
79 case XR_REFERENCE_SPACE_TYPE_STAGE:
81 case XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT:
89#define ENUM_CASE_STR(name, val) case name: return #name;
90#define MAKE_TO_STRING_FUNC(enumType)
91 static inline const char* to_string(enumType e) {
94 default: return "Unknown " #enumType;
104static bool isExtensionSupported(
const char *extensionName,
const QVector<XrExtensionProperties> &instanceExtensionProperties, uint32_t *extensionVersion =
nullptr)
106 for (
const auto &extensionProperty : instanceExtensionProperties) {
107 if (!strcmp(extensionName, extensionProperty.extensionName)) {
108 if (extensionVersion)
109 *extensionVersion = extensionProperty.extensionVersion;
116static bool isApiLayerSupported(
const char *layerName,
const QVector<XrApiLayerProperties> &apiLayerProperties)
118 for (
const auto &prop : apiLayerProperties) {
119 if (!strcmp(layerName, prop.layerName))
129#ifdef XR_EXT_debug_utils
130static XRAPI_ATTR XrBool32 XRAPI_CALL defaultDebugCallbackFunc(XrDebugUtilsMessageSeverityFlagsEXT messageSeverity,
131 XrDebugUtilsMessageTypeFlagsEXT messageType,
132 const XrDebugUtilsMessengerCallbackDataEXT *callbackData,
135 Q_UNUSED(messageSeverity);
136 Q_UNUSED(messageType);
139 qDebug(
"xrDebug [QOpenXRManager %p] %s", self, callbackData->message);
146 XrFrameWaitInfo frameWaitInfo {};
147 frameWaitInfo.type = XR_TYPE_FRAME_WAIT_INFO;
148 XrFrameState frameState {};
149 frameState.type = XR_TYPE_FRAME_STATE;
150 auto result = xrWaitFrame(session, &frameWaitInfo, &frameState);
151 return { result, frameState };
176 return static_cast<QEvent::Type>(event);
181 QCoreApplication::postEvent(
this,
new QEvent(QQuick3DOpenXRThreadWorker::asQEvent(QQuick3DOpenXRThreadWorker::Event::WaitForFrame)));
187 switch (
static_cast<
Event>(event->type())) {
189 auto result = waitForNextFrame(m_session);
190 emit frameWaited(result.first, result.second);
194 return QObject::event(event);
202 XrSession m_session = XR_NULL_HANDLE;
207 if (m_runtimeName.isEmpty())
208 m_errorString = QObject::tr(
"%1 failed: no OpenXR runtime found").arg(QLatin1StringView(callName));
210 m_errorString = QObject::tr(
"%1 for runtime %2 %3 failed with %4.")
211 .arg(QLatin1StringView(callName),
213 m_runtimeVersion.toString(),
214 OpenXRHelpers::getXrResultAsString(result, m_instance));
215 if (result == XR_ERROR_FORM_FACTOR_UNAVAILABLE)
216 m_errorString += QObject::tr(
"\nThe OpenXR runtime has no connection to the headset; check if connection is active and functional.");
232 QSSG_ASSERT(manager !=
nullptr,
return nullptr);
233 return manager->d_func();
236void QQuick3DXrManagerPrivate::updateCameraHelper(QQuick3DXrEyeCamera *camera,
const XrCompositionLayerProjectionView &layerView)
238 camera->setLeftTangent(qTan(layerView.fov.angleLeft));
239 camera->setRightTangent(qTan(layerView.fov.angleRight));
240 camera->setUpTangent(qTan(layerView.fov.angleUp));
241 camera->setDownTangent(qTan(layerView.fov.angleDown));
243 camera->setPosition(QVector3D(layerView.pose.position.x,
244 layerView.pose.position.y,
245 layerView.pose.position.z) * 100.0f);
247 camera->setRotation(QQuaternion(layerView.pose.orientation.w,
248 layerView.pose.orientation.x,
249 layerView.pose.orientation.y,
250 layerView.pose.orientation.z));
257 Q_Q(QQuick3DXrManager);
259 QQuick3DXrOrigin *xrOrigin = q->m_xrOrigin;
261 QQuick3DXrEyeCamera *eyeCamera = xrOrigin ? xrOrigin->eyeCamera(eye) :
nullptr;
264 updateCameraHelper(eyeCamera, layerView);
266 q->m_vrViewport->setCamera(eyeCamera);
272 Q_Q(QQuick3DXrManager);
274 QQuick3DXrOrigin *xrOrigin = q->m_xrOrigin;
275 QQuick3DViewport *vrViewport = q->m_vrViewport;
277 QVarLengthArray<QQuick3DCamera *, 4> cameras;
278 for (
int i = projectionLayerViewStartIndex; i < projectionLayerViewStartIndex + count; ++i) {
279 QQuick3DXrEyeCamera *eyeCamera = xrOrigin ? xrOrigin->eyeCamera(i) :
nullptr;
281 updateCameraHelper(eyeCamera, m_projectionLayerViews[i]);
282 cameras.append(eyeCamera);
284 vrViewport->setMultiViewCameras(cameras.data(), cameras.count());
289 return m_blendModes.contains(XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND)
290 || m_blendModes.contains(XR_ENVIRONMENT_BLEND_MODE_ADDITIVE);
295 bool supported =
false;
296 XrSystemPassthroughProperties2FB passthroughSystemProperties{};
297 passthroughSystemProperties.type = XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES2_FB;
299 XrSystemProperties systemProperties{};
300 systemProperties.type = XR_TYPE_SYSTEM_PROPERTIES;
301 systemProperties.next = &passthroughSystemProperties;
303 XrSystemGetInfo systemGetInfo{};
304 systemGetInfo.type = XR_TYPE_SYSTEM_GET_INFO;
305 systemGetInfo.formFactor = m_formFactor;
307 XrSystemId systemId = XR_NULL_SYSTEM_ID;
308 xrGetSystem(m_instance, &systemGetInfo, &systemId);
309 xrGetSystemProperties(m_instance, systemId, &systemProperties);
311 supported = (passthroughSystemProperties.capabilities & XR_PASSTHROUGH_CAPABILITY_BIT_FB) == XR_PASSTHROUGH_CAPABILITY_BIT_FB;
317 XrSystemPassthroughPropertiesFB oldPassthroughSystemProperties{};
318 oldPassthroughSystemProperties.type = XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES_FB;
319 systemProperties.next = &oldPassthroughSystemProperties;
320 xrGetSystemProperties(m_instance, systemId, &systemProperties);
321 supported = oldPassthroughSystemProperties.supportsPassthrough;
336 QSSG_ASSERT(window !=
nullptr,
return);
338 m_graphics->setupWindow(window);
343 return m_graphics && m_graphics->rhi();
348 QSSG_ASSERT(window !=
nullptr,
return false);
349 QSSG_ASSERT(m_graphics !=
nullptr,
return false);
351 return m_graphics->setupGraphics(m_instance, m_systemId, window->graphicsConfiguration());
356 Q_Q(QQuick3DXrManager);
357 if (m_waitingForFrame) {
362 QCoreApplication::postEvent(q,
new QEvent(QEvent::UpdateRequest));
367 Q_Q(QQuick3DXrManager);
369 bool exitRenderLoop =
false;
370 bool requestrestart =
false;
371 pollEvents(&exitRenderLoop, &requestrestart);
374 emit q->sessionEnded();
376 if (m_sessionRunning && m_inputManager) {
384 for (
const Swapchain &swapchain : m_swapchains)
385 xrDestroySwapchain(swapchain.handle);
387 m_swapchains.clear();
388 m_swapchainImages.clear();
389 m_configViews.clear();
391 for (
const Swapchain &swapchain : m_depthSwapchains)
392 xrDestroySwapchain(swapchain.handle);
394 m_depthSwapchains.clear();
395 m_depthSwapchainImages.clear();
400 Q_ASSERT(m_session != XR_NULL_HANDLE);
402 if (m_waitingForFrame)
404 m_waitingForFrame =
true;
410 auto result = waitForNextFrame(m_session);
411 onFrameWaitCompleted(result.first, result.second);
417 if (!m_waitingForFrame)
419 m_waitingForFrame =
false;
421 if (!checkXrResult(result)) {
422 qWarning(
"xrWaitFrame failed");
424 doRenderFrameAferWait(frameState);
428 m_wantUpdate =
false;
435 Q_ASSERT(m_session != XR_NULL_HANDLE);
437 XrFrameBeginInfo frameBeginInfo{};
438 frameBeginInfo.type = XR_TYPE_FRAME_BEGIN_INFO;
439 if (!checkXrResult(xrBeginFrame(m_session, &frameBeginInfo))) {
440 qWarning(
"xrBeginFrame failed");
444 QVector<XrCompositionLayerBaseHeader*> layers;
446 XrCompositionLayerPassthroughFB passthroughCompLayer{};
447 passthroughCompLayer.type = XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB;
448 if (m_fbPassthroughEnabled) {
449 if (m_passthroughLayer == XR_NULL_HANDLE)
450 createMetaQuestPassthroughLayer();
451 passthroughCompLayer.layerHandle = m_passthroughLayer;
452 passthroughCompLayer.flags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
453 passthroughCompLayer.space = XR_NULL_HANDLE;
454 layers.push_back(
reinterpret_cast<XrCompositionLayerBaseHeader*>(&passthroughCompLayer));
457 XrCompositionLayerProjection layer{};
458 layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
459 layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
460 layer.layerFlags |= XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT;
461 layer.layerFlags |= XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT;
463 if (frameState.shouldRender == XR_TRUE) {
464 if (renderLayer(frameState.predictedDisplayTime, frameState.predictedDisplayPeriod, layer)) {
465 layers.push_back(
reinterpret_cast<XrCompositionLayerBaseHeader*>(&layer));
469 XrFrameEndInfo frameEndInfo{};
470 frameEndInfo.type = XR_TYPE_FRAME_END_INFO;
471 frameEndInfo.displayTime = frameState.predictedDisplayTime;
472 if (!m_fbPassthroughEnabled)
473 frameEndInfo.environmentBlendMode = m_environmentBlendMode;
475 frameEndInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
476 frameEndInfo.layerCount = (uint32_t)layers.size();
477 frameEndInfo.layers = layers.data();
478 if (!checkXrResult(xrEndFrame(m_session, &frameEndInfo)))
479 qWarning(
"xrEndFrame failed");
484 QSSG_ASSERT(rhi !=
nullptr && m_graphics !=
nullptr,
return false);
486 if (m_multiviewRendering && !rhi->isFeatureSupported(QRhi::MultiView)) {
487 qCDebug(lcQuick3DXr) <<
"Multiview rendering is not supported with the current graphics API";
488 m_multiviewRendering =
false;
491#if QT_CONFIG(graphicsframecapture)
492 if (m_frameCapture) {
493 m_frameCapture->setCapturePath(QLatin1String(
"."));
494 m_frameCapture->setCapturePrefix(QLatin1String(
"quick3dxr"));
495 m_frameCapture->setRhi(rhi);
496 if (!m_frameCapture->isLoaded()) {
497 qWarning(
"Quick 3D XR: Frame capture was requested but QGraphicsFrameCapture is not initialized"
498 " (or has no backends enabled in the Qt build)");
500 qCDebug(lcQuick3DXr,
"Quick 3D XR: Frame capture initialized");
505 m_isGraphicsInitialized = m_graphics->finializeGraphics(rhi);
506 return m_isGraphicsInitialized;
511 Q_Q(QQuick3DXrManager);
517 if (qEnvironmentVariableIntValue(
"QT_QUICK3D_XR_FRAME_CAPTURE")) {
518#if QT_CONFIG(graphicsframecapture)
519 m_frameCapture.reset(
new QGraphicsFrameCapture);
521 qWarning(
"Quick 3D XR: Frame capture was requested, but Qt is built without QGraphicsFrameCapture");
525#ifdef XR_USE_PLATFORM_ANDROID
527 PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR;
528 xrGetInstanceProcAddr(
529 XR_NULL_HANDLE,
"xrInitializeLoaderKHR", (PFN_xrVoidFunction*)&xrInitializeLoaderKHR);
530 if (xrInitializeLoaderKHR != NULL) {
531 m_javaVM = QJniEnvironment::javaVM();
532 m_androidActivity = QNativeInterface::QAndroidApplication::context();
534 XrLoaderInitInfoAndroidKHR loaderInitializeInfoAndroid;
535 memset(&loaderInitializeInfoAndroid, 0,
sizeof(loaderInitializeInfoAndroid));
536 loaderInitializeInfoAndroid.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR;
537 loaderInitializeInfoAndroid.next = NULL;
538 loaderInitializeInfoAndroid.applicationVM = m_javaVM;
539 loaderInitializeInfoAndroid.applicationContext = m_androidActivity.object();
540 XrResult xrResult = xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*)&loaderInitializeInfoAndroid);
541 if (xrResult != XR_SUCCESS) {
542 qWarning(
"Failed to initialize OpenXR Loader: %s", to_string(xrResult));
549 auto graphicsAPI = QQuickWindow::graphicsApi();
551 m_graphics =
nullptr;
552#ifdef XR_USE_GRAPHICS_API_VULKAN
553 if (graphicsAPI == QSGRendererInterface::Vulkan)
554 m_graphics =
new QOpenXRGraphicsVulkan;
556#ifdef XR_USE_GRAPHICS_API_D3D11
557 if (graphicsAPI == QSGRendererInterface::Direct3D11)
558 m_graphics =
new QOpenXRGraphicsD3D11;
560#ifdef XR_USE_GRAPHICS_API_D3D12
561 if (graphicsAPI == QSGRendererInterface::Direct3D12)
562 m_graphics =
new QOpenXRGraphicsD3D12;
564#ifdef XR_USE_GRAPHICS_API_OPENGL
565 if (graphicsAPI == QSGRendererInterface::OpenGL)
566 m_graphics =
new QOpenXRGraphicsOpenGL;
568#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
569 if (graphicsAPI == QSGRendererInterface::OpenGL)
570 m_graphics =
new QOpenXRGraphicsOpenGLES;
572#ifdef XR_USE_GRAPHICS_API_METAL
573 if (graphicsAPI == QSGRendererInterface::Metal)
574 m_graphics =
new QOpenXRGraphicsMetal;
578 qWarning() <<
"The Qt Quick Scenegraph is not using a supported RHI mode:" << graphicsAPI;
583 checkXrExtensions(
nullptr);
586 m_spaceExtension = QQuick3DXrAnchorManager::instance();
589 XrResult result = createXrInstance();
590 if (result != XR_SUCCESS) {
591 setErrorString(result,
"xrCreateInstance");
593 m_graphics =
nullptr;
600 setupDebugMessenger();
603 result = initializeSystem();
604 if (result != XR_SUCCESS) {
605 setErrorString(result,
"xrGetSystem");
607 m_graphics =
nullptr;
612 if (!q->setupGraphics()) {
613 m_errorString = QObject::tr(
"Failed to set up 3D API integration");
615 m_graphics =
nullptr;
620 XrSessionCreateInfo xrSessionInfo{};
621 xrSessionInfo.type = XR_TYPE_SESSION_CREATE_INFO;
622 xrSessionInfo.systemId = m_systemId;
624 auto overlayLayerPlacement = qEnvironmentVariableIntValue(
"QT_QUICK3D_XR_OVERLAY_PLACEMENT");
625 if (m_overlayExtensionSupported && overlayLayerPlacement > 0) {
626 XrSessionCreateInfoOverlayEXTX overlayInfo {};
627 overlayInfo.type = XR_TYPE_SESSION_CREATE_INFO_OVERLAY_EXTX;
628 overlayInfo.sessionLayersPlacement = overlayLayerPlacement;
629 overlayInfo.next = m_graphics->handle();
630 xrSessionInfo.next = &overlayInfo;
631 qCDebug(lcQuick3DXr,
"requesting overlay placement: %d", overlayLayerPlacement);
633 xrSessionInfo.next = m_graphics->handle();
636 result = xrCreateSession(m_instance, &xrSessionInfo, &m_session);
637 if (result != XR_SUCCESS) {
638 setErrorString(result,
"xrCreateSession");
640 m_graphics =
nullptr;
644 const bool disableAsyncWait = qEnvironmentVariableIntValue(
"QT_QUICK3D_XR_DISABLE_ASYNC_WAIT") != 0;
645 if (Q_LIKELY(!disableAsyncWait))
649 if (m_colorspaceExtensionSupported)
650 setupMetaQuestColorSpaces();
651 if (m_displayRefreshRateExtensionSupported)
652 setupMetaQuestRefreshRates();
653 if (m_spaceExtensionSupported)
654 m_spaceExtension->initialize(m_instance, m_session);
656 checkReferenceSpaces();
659 m_inputManager = QQuick3DXrInputManager::instance();
660 if (QSSG_GUARD(m_inputManager !=
nullptr))
663 if (!setupAppSpace())
665 if (!setupViewSpace())
668 if (!createSwapchains())
676 m_waitingForFrame =
false;
677 m_wantUpdate =
false;
678 destroyWorkerThread();
680 if (m_inputManager) {
682 m_inputManager =
nullptr;
685 if (m_spaceExtension) {
687 m_spaceExtension =
nullptr;
690 if (m_passthroughLayer)
691 destroyMetaQuestPassthroughLayer();
692 if (m_passthroughFeature)
693 destroyMetaQuestPassthrough();
697 if (m_appSpace != XR_NULL_HANDLE) {
698 xrDestroySpace(m_appSpace);
701 if (m_viewSpace != XR_NULL_HANDLE)
702 xrDestroySpace(m_viewSpace);
704 xrDestroySession(m_session);
706#ifdef XR_EXT_debug_utils
707 if (m_debugMessenger) {
708 m_xrDestroyDebugUtilsMessengerEXT(m_debugMessenger);
709 m_debugMessenger = XR_NULL_HANDLE;
713 xrDestroyInstance(m_instance);
718 m_graphics->releaseResources();
723 Q_Q(QQuick3DXrManager);
725 qCDebug(lcQuick3DXr,
"Initializing OpenXR worker thread");
726 m_workerThread =
new QThread();
729 m_worker =
new QQuick3DOpenXRThreadWorker(m_session);
730 m_worker->moveToThread(m_workerThread);
734 &QQuick3DOpenXRThreadWorker::frameWaited,
736 [
this](XrResult result,
const XrFrameState &frameState) { onFrameWaitCompleted(result, frameState); },
737 Qt::QueuedConnection);
739 QObject::connect(m_workerThread, &QThread::finished, m_worker, &QObject::deleteLater);
741 m_workerThread->start();
746 if (m_workerThread) {
747 m_workerThread->quit();
748 m_workerThread->wait();
749 delete m_workerThread;
750 m_workerThread =
nullptr;
752 qCDebug(lcQuick3DXr,
"OpenXR worker thread destroyed");
758 Q_ASSERT(m_session != XR_NULL_HANDLE);
761 if (!checkXrResult(xrEnumerateReferenceSpaces(m_session, 0, &spaceCount,
nullptr))) {
762 qWarning(
"Failed to enumerate reference spaces");
765 m_availableReferenceSpace.resize(spaceCount);
766 if (!checkXrResult(xrEnumerateReferenceSpaces(m_session, spaceCount, &spaceCount, m_availableReferenceSpace.data()))) {
767 qWarning(
"Failed to enumerate reference spaces");
771 qCDebug(lcQuick3DXr,
"Available reference spaces: %d", spaceCount);
772 for (XrReferenceSpaceType space : m_availableReferenceSpace) {
773 qCDebug(lcQuick3DXr,
" Name: %s", to_string(space));
779 return m_availableReferenceSpace.contains(type);
784 Q_Q(QQuick3DXrManager);
786 Q_ASSERT(m_session != XR_NULL_HANDLE);
788 XrPosef identityPose;
789 identityPose.orientation.w = 1;
790 identityPose.orientation.x = 0;
791 identityPose.orientation.y = 0;
792 identityPose.orientation.z = 0;
793 identityPose.position.x = 0;
794 identityPose.position.y = 0;
795 identityPose.position.z = 0;
797 XrReferenceSpaceType newReferenceSpace;
798 XrSpace newAppSpace = XR_NULL_HANDLE;
799 m_isEmulatingLocalFloor =
false;
801 if (isReferenceSpaceAvailable(m_requestedReferenceSpace)) {
802 newReferenceSpace = m_requestedReferenceSpace;
803 }
else if (m_requestedReferenceSpace == XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT &&
804 isReferenceSpaceAvailable(XR_REFERENCE_SPACE_TYPE_STAGE)) {
805 m_isEmulatingLocalFloor =
true;
806 m_isFloorResetPending =
true;
807 newReferenceSpace = XR_REFERENCE_SPACE_TYPE_LOCAL;
809 qWarning(
"Requested reference space is not available");
810 newReferenceSpace = XR_REFERENCE_SPACE_TYPE_LOCAL;
814 qCDebug(lcQuick3DXr,
"Creating new reference space for app space: %s", to_string(newReferenceSpace));
815 XrReferenceSpaceCreateInfo referenceSpaceCreateInfo{};
816 referenceSpaceCreateInfo.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
817 referenceSpaceCreateInfo.poseInReferenceSpace = identityPose;
818 referenceSpaceCreateInfo.referenceSpaceType = newReferenceSpace;
819 if (!checkXrResult(xrCreateReferenceSpace(m_session, &referenceSpaceCreateInfo, &newAppSpace))) {
820 qWarning(
"Failed to create app space");
825 xrDestroySpace(m_appSpace);
827 m_appSpace = newAppSpace;
828 m_referenceSpace = newReferenceSpace;
831 if (!m_isFloorResetPending)
832 emit q->referenceSpaceChanged();
840 Q_Q(QQuick3DXrManager);
844 if (m_requestedReferenceSpace != m_referenceSpace && !m_isFloorResetPending) {
845 if (!setupAppSpace()) {
847 qWarning(
"Setting requested reference space failed");
848 m_requestedReferenceSpace = m_referenceSpace;
857 if (m_isFloorResetPending) {
858 if (!resetEmulatedFloorHeight(predictedDisplayTime)) {
860 m_requestedReferenceSpace = XR_REFERENCE_SPACE_TYPE_LOCAL;
861 emit q->referenceSpaceChanged();
870 Q_ASSERT(m_session != XR_NULL_HANDLE);
872 XrPosef identityPose;
873 identityPose.orientation.w = 1;
874 identityPose.orientation.x = 0;
875 identityPose.orientation.y = 0;
876 identityPose.orientation.z = 0;
877 identityPose.position.x = 0;
878 identityPose.position.y = 0;
879 identityPose.position.z = 0;
881 XrSpace newViewSpace = XR_NULL_HANDLE;
883 XrReferenceSpaceCreateInfo referenceSpaceCreateInfo{};
884 referenceSpaceCreateInfo.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
885 referenceSpaceCreateInfo.poseInReferenceSpace = identityPose;
886 referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW;
887 if (!checkXrResult(xrCreateReferenceSpace(m_session, &referenceSpaceCreateInfo, &newViewSpace))) {
888 qWarning(
"Failed to create view space");
892 if (m_viewSpace != XR_NULL_HANDLE)
893 xrDestroySpace(m_viewSpace);
895 m_viewSpace = newViewSpace;
902 Q_Q(QQuick3DXrManager);
904 Q_ASSERT(m_isEmulatingLocalFloor);
906 m_isFloorResetPending =
false;
908 XrPosef identityPose;
909 identityPose.orientation.w = 1;
910 identityPose.orientation.x = 0;
911 identityPose.orientation.y = 0;
912 identityPose.orientation.z = 0;
913 identityPose.position.x = 0;
914 identityPose.position.y = 0;
915 identityPose.position.z = 0;
917 XrSpace localSpace = XR_NULL_HANDLE;
918 XrSpace stageSpace = XR_NULL_HANDLE;
920 XrReferenceSpaceCreateInfo createInfo{};
921 createInfo.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
922 createInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
923 createInfo.poseInReferenceSpace = identityPose;
925 if (!checkXrResult(xrCreateReferenceSpace(m_session, &createInfo, &localSpace))) {
926 qWarning(
"Failed to create local space (for emulated LOCAL_FLOOR space)");
930 createInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
931 if (!checkXrResult(xrCreateReferenceSpace(m_session, &createInfo, &stageSpace))) {
932 qWarning(
"Failed to create stage space (for emulated LOCAL_FLOOR space)");
933 xrDestroySpace(localSpace);
937 XrSpaceLocation stageLocation{};
938 stageLocation.type = XR_TYPE_SPACE_LOCATION;
939 stageLocation.pose = identityPose;
941 if (!checkXrResult(xrLocateSpace(stageSpace, localSpace, predictedDisplayTime, &stageLocation))) {
942 qWarning(
"Failed to locate STAGE space in LOCAL space, in order to emulate LOCAL_FLOOR");
943 xrDestroySpace(localSpace);
944 xrDestroySpace(stageSpace);
948 xrDestroySpace(localSpace);
949 xrDestroySpace(stageSpace);
951 XrSpace newAppSpace = XR_NULL_HANDLE;
952 createInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
953 createInfo.poseInReferenceSpace.position.y = stageLocation.pose.position.y;
954 if (!checkXrResult(xrCreateReferenceSpace(m_session, &createInfo, &newAppSpace))) {
955 qWarning(
"Failed to recreate emulated LOCAL_FLOOR play space with latest floor estimate");
959 xrDestroySpace(m_appSpace);
960 m_appSpace = newAppSpace;
961 m_referenceSpace = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;
962 emit q->referenceSpaceChanged();
969 Q_ASSERT(m_session != XR_NULL_HANDLE);
970 Q_ASSERT(m_configViews.isEmpty());
971 Q_ASSERT(m_swapchains.isEmpty());
973 XrSystemProperties systemProperties{};
974 systemProperties.type = XR_TYPE_SYSTEM_PROPERTIES;
976 XrSystemHandTrackingPropertiesEXT handTrackingSystemProperties{};
977 handTrackingSystemProperties.type = XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT;
978 systemProperties.next = &handTrackingSystemProperties;
980 if (!checkXrResult(xrGetSystemProperties(m_instance, m_systemId, &systemProperties))) {
981 qWarning(
"Failed to get OpenXR system properties");
984 qCDebug(lcQuick3DXr,
"System Properties: Name=%s VendorId=%d", systemProperties.systemName, systemProperties.vendorId);
985 qCDebug(lcQuick3DXr,
"System Graphics Properties: MaxWidth=%d MaxHeight=%d MaxLayers=%d",
986 systemProperties.graphicsProperties.maxSwapchainImageWidth,
987 systemProperties.graphicsProperties.maxSwapchainImageHeight,
988 systemProperties.graphicsProperties.maxLayerCount);
989 qCDebug(lcQuick3DXr,
"System Tracking Properties: OrientationTracking=%s PositionTracking=%s",
990 systemProperties.trackingProperties.orientationTracking == XR_TRUE ?
"True" :
"False",
991 systemProperties.trackingProperties.positionTracking == XR_TRUE ?
"True" :
"False");
992 qCDebug(lcQuick3DXr,
"System Hand Tracking Properties: handTracking=%s",
993 handTrackingSystemProperties.supportsHandTracking == XR_TRUE ?
"True" :
"False");
997 if (!checkXrResult(xrEnumerateViewConfigurationViews(m_instance,
1004 qWarning(
"Failed to enumerate view configurations");
1007 m_configViews.resize(viewCount, {XR_TYPE_VIEW_CONFIGURATION_VIEW,
nullptr, 0, 0, 0, 0, 0, 0});
1008 if (!checkXrResult(xrEnumerateViewConfigurationViews(m_instance,
1013 m_configViews.data())))
1015 qWarning(
"Failed to enumerate view configurations");
1018 m_views.resize(viewCount, {XR_TYPE_VIEW,
nullptr, {}, {}});
1019 m_projectionLayerViews.resize(viewCount, {});
1020 m_layerDepthInfos.resize(viewCount, {});
1023 if (viewCount > 0) {
1025 uint32_t swapchainFormatCount;
1026 if (!checkXrResult(xrEnumerateSwapchainFormats(m_session, 0, &swapchainFormatCount,
nullptr))) {
1027 qWarning(
"Failed to enumerate swapchain formats");
1030 QVector<int64_t> swapchainFormats(swapchainFormatCount);
1031 if (!checkXrResult(xrEnumerateSwapchainFormats(m_session,
1032 swapchainFormats.size(),
1033 &swapchainFormatCount,
1034 swapchainFormats.data())))
1036 qWarning(
"Failed to enumerate swapchain formats");
1040 Q_ASSERT(
static_cast<qsizetype>(swapchainFormatCount) == swapchainFormats.size());
1041 m_colorSwapchainFormat = m_graphics->colorSwapchainFormat(swapchainFormats);
1042 if (m_compositionLayerDepthSupported)
1043 m_depthSwapchainFormat = m_graphics->depthSwapchainFormat(swapchainFormats);
1047 QString swapchainFormatsString;
1048 for (int64_t format : swapchainFormats) {
1049 const bool selectedColor = format == m_colorSwapchainFormat;
1050 const bool selectedDepth = format == m_depthSwapchainFormat;
1051 swapchainFormatsString += u" ";
1053 swapchainFormatsString += u"[";
1054 else if (selectedDepth)
1055 swapchainFormatsString += u"<";
1056 swapchainFormatsString += QString::number(format);
1058 swapchainFormatsString += u"]";
1059 else if (selectedDepth)
1060 swapchainFormatsString += u">";
1062 qCDebug(lcQuick3DXr,
"Swapchain formats: %s", qPrintable(swapchainFormatsString));
1065 const XrViewConfigurationView &vp = m_configViews[0];
1076 if (m_multiviewRendering) {
1078 XrSwapchainCreateInfo swapchainCreateInfo{};
1079 swapchainCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO;
1080 swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT;
1081 swapchainCreateInfo.format = m_colorSwapchainFormat;
1082 swapchainCreateInfo.sampleCount = 1;
1083 swapchainCreateInfo.width = vp.recommendedImageRectWidth;
1084 swapchainCreateInfo.height = vp.recommendedImageRectHeight;
1085 swapchainCreateInfo.faceCount = 1;
1086 swapchainCreateInfo.arraySize = viewCount;
1087 swapchainCreateInfo.mipCount = 1;
1089 qCDebug(lcQuick3DXr,
"Creating multiview swapchain for %u view(s) with dimensions Width=%d Height=%d SampleCount=%d Format=%llx",
1091 vp.recommendedImageRectWidth,
1092 vp.recommendedImageRectHeight,
1094 static_cast<
long long unsigned int>(m_colorSwapchainFormat));
1096 Swapchain swapchain;
1097 swapchain.width = swapchainCreateInfo.width;
1098 swapchain.height = swapchainCreateInfo.height;
1099 swapchain.arraySize = swapchainCreateInfo.arraySize;
1100 if (checkXrResult(xrCreateSwapchain(m_session, &swapchainCreateInfo, &swapchain.handle))) {
1101 uint32_t imageCount = 0;
1102 if (!checkXrResult(xrEnumerateSwapchainImages(swapchain.handle, 0, &imageCount,
nullptr))) {
1103 qWarning(
"Failed to enumerate swapchain images");
1107 auto swapchainImages = m_graphics->allocateSwapchainImages(imageCount, swapchain.handle);
1108 if (!checkXrResult(xrEnumerateSwapchainImages(swapchain.handle, imageCount, &imageCount, swapchainImages[0]))) {
1109 qWarning(
"Failed to enumerate swapchain images");
1113 m_swapchains.append(swapchain);
1114 m_swapchainImages.insert(swapchain.handle, swapchainImages);
1116 qWarning(
"xrCreateSwapchain failed (multiview)");
1124 if (m_compositionLayerDepthSupported && m_depthSwapchainFormat > 0) {
1125 swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
1126 swapchainCreateInfo.format = m_depthSwapchainFormat;
1127 if (checkXrResult(xrCreateSwapchain(m_session, &swapchainCreateInfo, &swapchain.handle))) {
1128 uint32_t imageCount = 0;
1129 if (!checkXrResult(xrEnumerateSwapchainImages(swapchain.handle, 0, &imageCount,
nullptr))) {
1130 qWarning(
"Failed to enumerate depth swapchain images");
1134 auto swapchainImages = m_graphics->allocateSwapchainImages(imageCount, swapchain.handle);
1135 if (!checkXrResult(xrEnumerateSwapchainImages(swapchain.handle, imageCount, &imageCount, swapchainImages[0]))) {
1136 qWarning(
"Failed to enumerate depth swapchain images");
1140 m_depthSwapchains.append(swapchain);
1141 m_depthSwapchainImages.insert(swapchain.handle, swapchainImages);
1143 qWarning(
"xrCreateSwapchain failed for depth swapchain (multiview)");
1149 for (uint32_t i = 0; i < viewCount; i++) {
1150 qCDebug(lcQuick3DXr,
"Creating swapchain for view %u with dimensions Width=%d Height=%d SampleCount=%d Format=%llx",
1152 vp.recommendedImageRectWidth,
1153 vp.recommendedImageRectHeight,
1155 static_cast<
long long unsigned int>(m_colorSwapchainFormat));
1158 XrSwapchainCreateInfo swapchainCreateInfo{};
1159 swapchainCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO;
1160 swapchainCreateInfo.arraySize = 1;
1161 swapchainCreateInfo.format = m_colorSwapchainFormat;
1162 swapchainCreateInfo.width = vp.recommendedImageRectWidth;
1163 swapchainCreateInfo.height = vp.recommendedImageRectHeight;
1164 swapchainCreateInfo.mipCount = 1;
1165 swapchainCreateInfo.faceCount = 1;
1166 swapchainCreateInfo.sampleCount = 1;
1167 swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
1168 Swapchain swapchain;
1169 swapchain.width = swapchainCreateInfo.width;
1170 swapchain.height = swapchainCreateInfo.height;
1171 if (checkXrResult(xrCreateSwapchain(m_session, &swapchainCreateInfo, &swapchain.handle))) {
1172 uint32_t imageCount = 0;
1173 if (!checkXrResult(xrEnumerateSwapchainImages(swapchain.handle, 0, &imageCount,
nullptr))) {
1174 qWarning(
"Failed to enumerate swapchain images");
1178 auto swapchainImages = m_graphics->allocateSwapchainImages(imageCount, swapchain.handle);
1179 if (!checkXrResult(xrEnumerateSwapchainImages(swapchain.handle, imageCount, &imageCount, swapchainImages[0]))) {
1180 qWarning(
"Failed to enumerate swapchain images");
1184 m_swapchains.append(swapchain);
1185 m_swapchainImages.insert(swapchain.handle, swapchainImages);
1187 qWarning(
"xrCreateSwapchain failed (view %u)", viewCount);
1191 if (m_compositionLayerDepthSupported && m_depthSwapchainFormat > 0) {
1192 swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
1193 swapchainCreateInfo.format = m_depthSwapchainFormat;
1194 if (checkXrResult(xrCreateSwapchain(m_session, &swapchainCreateInfo, &swapchain.handle))) {
1195 uint32_t imageCount = 0;
1196 if (!checkXrResult(xrEnumerateSwapchainImages(swapchain.handle, 0, &imageCount,
nullptr))) {
1197 qWarning(
"Failed to enumerate depth swapchain images");
1201 auto swapchainImages = m_graphics->allocateSwapchainImages(imageCount, swapchain.handle);
1202 if (!checkXrResult(xrEnumerateSwapchainImages(swapchain.handle, imageCount, &imageCount, swapchainImages[0]))) {
1203 qWarning(
"Failed to enumerate depth swapchain images");
1207 m_depthSwapchains.append(swapchain);
1208 m_depthSwapchainImages.insert(swapchain.handle, swapchainImages);
1210 qWarning(
"xrCreateSwapchain failed for depth swapchain (view %u)", viewCount);
1217 if (m_multiviewRendering) {
1218 if (m_swapchains.isEmpty())
1220 if (m_compositionLayerDepthSupported && m_depthSwapchains.isEmpty())
1223 if (m_swapchains.count() != qsizetype(viewCount))
1225 if (m_compositionLayerDepthSupported && m_depthSwapchains.count() != qsizetype(viewCount))
1230 for (uint32_t i = 0; i < viewCount; ++i) {
1231 m_projectionLayerViews[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
1232 m_projectionLayerViews[i].next =
nullptr;
1233 m_projectionLayerViews[i].subImage.swapchain = m_swapchains[0].handle;
1234 m_projectionLayerViews[i].subImage.imageArrayIndex = i;
1235 m_projectionLayerViews[i].subImage.imageRect.offset.x = 0;
1236 m_projectionLayerViews[i].subImage.imageRect.offset.y = 0;
1237 m_projectionLayerViews[i].subImage.imageRect.extent.width = vp.recommendedImageRectWidth;
1238 m_projectionLayerViews[i].subImage.imageRect.extent.height = vp.recommendedImageRectHeight;
1240 if (m_compositionLayerDepthSupported) {
1241 m_layerDepthInfos[i].type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR;
1242 m_layerDepthInfos[i].next =
nullptr;
1243 m_layerDepthInfos[i].subImage.swapchain = m_depthSwapchains[0].handle;
1244 m_layerDepthInfos[i].subImage.imageArrayIndex = i;
1245 m_layerDepthInfos[i].subImage.imageRect.offset.x = 0;
1246 m_layerDepthInfos[i].subImage.imageRect.offset.y = 0;
1247 m_layerDepthInfos[i].subImage.imageRect.extent.width = vp.recommendedImageRectWidth;
1248 m_layerDepthInfos[i].subImage.imageRect.extent.height = vp.recommendedImageRectHeight;
1253 if (m_foveationExtensionSupported)
1254 setupMetaQuestFoveation();
1261 if (m_samples == samples)
1264 m_samples = samples;
1272 return m_enabledExtensions;
1277 return m_runtimeName;
1282 return m_runtimeVersion;
1287 Q_Q(QQuick3DXrManager);
1288 QRhi *rhi = q->m_renderControl->rhi();
1289 if (m_multiviewRendering == enable || !rhi)
1291 if (enable && !rhi->isFeatureSupported(QRhi::MultiView)) {
1292 qWarning(
"Quick 3D XR: Multiview rendering was enabled, but is reported as unsupported from the current QRhi backend (%s)",
1293 rhi->backendName());
1296 m_multiviewRendering = enable;
1297 qCDebug(lcQuick3DXr,
"Multiview rendering %s", m_multiviewRendering ?
"enabled" :
"disabled");
1298 if (!m_swapchains.isEmpty()) {
1299 qCDebug(lcQuick3DXr,
"OpenXR swapchain already exists, creating new one due to change in multiview mode");
1303 emit q->multiViewRenderingEnabledChanged();
1310 if (m_fbPassthroughEnabled)
1314 return m_environmentBlendMode == XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND
1315 || m_environmentBlendMode == XR_ENVIRONMENT_BLEND_MODE_ADDITIVE;
1320 m_fbPassthroughEnabled = enable;
1322 if (m_passthroughFeature == XR_NULL_HANDLE)
1323 createMetaQuestPassthrough();
1325 startMetaQuestPassthrough();
1327 if (m_passthroughLayer == XR_NULL_HANDLE)
1328 createMetaQuestPassthroughLayer();
1330 resumeMetaQuestPassthroughLayer();
1332 if (m_passthroughLayer)
1333 pauseMetaQuestPassthroughLayer();
1334 if (m_passthroughFeature)
1335 pauseMetaQuestPassthrough();
1342 if (m_blendModes.contains(XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND))
1343 m_environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND;
1344 else if (m_blendModes.contains(XR_ENVIRONMENT_BLEND_MODE_ADDITIVE))
1345 m_environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_ADDITIVE;
1349 if (m_blendModes.contains(XR_ENVIRONMENT_BLEND_MODE_OPAQUE))
1350 m_environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
1354 qCDebug(lcQuick3DXr,
"Environment blend mode set to: %s", to_string(m_environmentBlendMode));
1370 if (m_submitLayerDepth == enable)
1373 if (m_compositionLayerDepthSupported) {
1375 qCDebug(lcQuick3DXr,
"Enabling submitLayerDepth");
1377 m_submitLayerDepth = enable;
1383 XrReferenceSpaceType referenceSpace = getXrReferenceSpaceType(newReferenceSpace);
1384 if (m_referenceSpace == referenceSpace)
1387 m_requestedReferenceSpace = referenceSpace;
1395 return getReferenceSpaceType(m_referenceSpace);
1406 *exitRenderLoop =
false;
1407 *requestRestart =
false;
1409 auto readNextEvent = [
this]() {
1412 XrEventDataBaseHeader* baseHeader =
reinterpret_cast<XrEventDataBaseHeader*>(&m_eventDataBuffer);
1413 *baseHeader = {XR_TYPE_EVENT_DATA_BUFFER,
nullptr};
1414 const XrResult xr = xrPollEvent(m_instance, &m_eventDataBuffer);
1415 if (xr == XR_SUCCESS) {
1416 if (baseHeader->type == XR_TYPE_EVENT_DATA_EVENTS_LOST) {
1417 const XrEventDataEventsLost*
const eventsLost =
reinterpret_cast<
const XrEventDataEventsLost*>(baseHeader);
1418 qCDebug(lcQuick3DXr,
"%d events lost", eventsLost->lostEventCount);
1424 return static_cast<XrEventDataBaseHeader*>(
nullptr);
1427 auto handleSessionStateChangedEvent = [
this](
const XrEventDataSessionStateChanged& stateChangedEvent,
1428 bool* exitRenderLoop,
1429 bool* requestRestart) {
1430 const XrSessionState oldState = m_sessionState;
1431 m_sessionState = stateChangedEvent.state;
1433 qCDebug(lcQuick3DXr,
"XrEventDataSessionStateChanged: state %s->%s time=%lld",
1434 to_string(oldState),
1435 to_string(m_sessionState),
1436 static_cast<
long long int>(stateChangedEvent.time));
1438 if ((stateChangedEvent.session != XR_NULL_HANDLE) && (stateChangedEvent.session != m_session)) {
1439 qCDebug(lcQuick3DXr,
"XrEventDataSessionStateChanged for unknown session");
1443 switch (m_sessionState) {
1444 case XR_SESSION_STATE_READY: {
1445 Q_ASSERT(m_session != XR_NULL_HANDLE);
1446 XrSessionBeginInfo sessionBeginInfo{};
1447 sessionBeginInfo.type = XR_TYPE_SESSION_BEGIN_INFO;
1448 sessionBeginInfo.primaryViewConfigurationType = m_viewConfigType;
1449 if (!checkXrResult(xrBeginSession(m_session, &sessionBeginInfo))) {
1450 qWarning(
"xrBeginSession failed");
1453 m_sessionRunning =
true;
1456 case XR_SESSION_STATE_STOPPING: {
1457 Q_ASSERT(m_session != XR_NULL_HANDLE);
1458 m_sessionRunning =
false;
1459 if (!checkXrResult(xrEndSession(m_session)))
1460 qWarning(
"xrEndSession failed");
1463 case XR_SESSION_STATE_EXITING: {
1464 *exitRenderLoop =
true;
1466 *requestRestart =
false;
1469 case XR_SESSION_STATE_LOSS_PENDING: {
1470 *exitRenderLoop =
true;
1472 *requestRestart =
true;
1481 while (
const XrEventDataBaseHeader* event = readNextEvent()) {
1482 switch (event->type) {
1483 case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: {
1484 const auto& instanceLossPending = *
reinterpret_cast<
const XrEventDataInstanceLossPending*>(event);
1485 qCDebug(lcQuick3DXr,
"XrEventDataInstanceLossPending by %lld",
static_cast<
long long int>(instanceLossPending.lossTime));
1486 *exitRenderLoop =
true;
1487 *requestRestart =
true;
1490 case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: {
1491 auto sessionStateChangedEvent = *
reinterpret_cast<
const XrEventDataSessionStateChanged*>(event);
1492 handleSessionStateChangedEvent(sessionStateChangedEvent, exitRenderLoop, requestRestart);
1495 case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED:
1497 case XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB:
1498 case XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB:
1499 case XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB:
1500 case XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB:
1502 if (m_spaceExtension)
1503 m_spaceExtension->handleEvent(event);
1505 case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING:
1507 qCDebug(lcQuick3DXr,
"Ignoring event type %d", event->type);
1515 XrDuration predictedDisplayPeriod,
1516 XrCompositionLayerProjection &layer)
1518 Q_Q(QQuick3DXrManager);
1519 auto *xrOrigin = q->m_xrOrigin;
1520 auto *animationDriver = q->m_animationDriver;
1521 auto *renderControl = q->m_renderControl;
1525 XrViewState viewState{};
1526 viewState.type = XR_TYPE_VIEW_STATE;
1527 quint32 viewCapacityInput = m_views.size();
1528 quint32 viewCountOutput;
1531 updateAppSpace(predictedDisplayTime);
1533 XrViewLocateInfo viewLocateInfo{};
1534 viewLocateInfo.type = XR_TYPE_VIEW_LOCATE_INFO;
1535 viewLocateInfo.viewConfigurationType = m_viewConfigType;
1536 viewLocateInfo.displayTime = predictedDisplayTime;
1537 viewLocateInfo.space = m_appSpace;
1539 res = xrLocateViews(m_session, &viewLocateInfo, &viewState, viewCapacityInput, &viewCountOutput, m_views.data());
1540 if (XR_UNQUALIFIED_SUCCESS(res)) {
1541 Q_ASSERT(viewCountOutput == viewCapacityInput);
1542 Q_ASSERT(
static_cast<qsizetype>(viewCountOutput) == m_configViews.size());
1543 Q_ASSERT(
static_cast<qsizetype>(viewCountOutput) == m_projectionLayerViews.size());
1544 Q_ASSERT(m_multiviewRendering ? viewCountOutput == m_swapchains[0].arraySize :
static_cast<qsizetype>(viewCountOutput) == m_swapchains.size());
1547 XrSpaceLocation location{};
1548 location.type = XR_TYPE_SPACE_LOCATION;
1549 if (checkXrResult(xrLocateSpace(m_viewSpace, m_appSpace, predictedDisplayTime, &location))) {
1550 QVector3D position = QVector3D(location.pose.position.x,
1551 location.pose.position.y,
1552 location.pose.position.z) * 100.0f;
1553 QQuaternion rotation(location.pose.orientation.w,
1554 location.pose.orientation.x,
1555 location.pose.orientation.y,
1556 location.pose.orientation.z);
1558 xrOrigin->updateTrackedCamera(position, rotation);
1562 if (QSSG_GUARD(m_inputManager !=
nullptr))
1566 if (m_spaceExtension)
1567 m_spaceExtension->updateAnchors(predictedDisplayTime, m_appSpace);
1569 if (m_handtrackingExtensionSupported && m_inputManager)
1575 const qint64 displayPeriodMS = predictedDisplayPeriod / 1000000;
1576 const qint64 displayDeltaMS = (predictedDisplayTime - m_previousTime) / 1000000;
1578 if (m_previousTime == 0 || !animationDriver->isRunning()) {
1579 animationDriver->setStep(displayPeriodMS);
1581 if (displayDeltaMS > displayPeriodMS)
1582 animationDriver->setStep(displayPeriodMS);
1584 animationDriver->setStep(displayDeltaMS);
1585 animationDriver->advance();
1587 m_previousTime = predictedDisplayTime;
1589#if QT_CONFIG(graphicsframecapture)
1591 m_frameCapture->startCaptureFrame();
1594 if (m_submitLayerDepth && m_samples > 1) {
1595 if (!renderControl->rhi()->isFeatureSupported(QRhi::ResolveDepthStencil)) {
1596 static bool warned =
false;
1599 qWarning(
"Quick3D XR: Submitting depth buffer with MSAA cannot be enabled"
1600 " when depth-stencil resolve is not supported by the underlying 3D API (%s)",
1601 renderControl->rhi()->backendName());
1603 m_submitLayerDepth =
false;
1607 if (m_multiviewRendering) {
1608 const Swapchain swapchain = m_swapchains[0];
1611 XrSwapchainImageAcquireInfo acquireInfo{};
1612 acquireInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO;
1613 uint32_t swapchainImageIndex = 0;
1614 if (!checkXrResult(xrAcquireSwapchainImage(swapchain.handle, &acquireInfo, &swapchainImageIndex))) {
1615 qWarning(
"Failed to acquire swapchain image (multiview)");
1618 XrSwapchainImageWaitInfo waitInfo{};
1619 waitInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO;
1620 waitInfo.timeout = XR_INFINITE_DURATION;
1621 if (!checkXrResult(xrWaitSwapchainImage(swapchain.handle, &waitInfo))) {
1622 qWarning(
"Failed to wait for swapchain image (multiview)");
1625 XrSwapchainImageBaseHeader *swapchainImage = m_swapchainImages[swapchain.handle][swapchainImageIndex];
1627 XrSwapchainImageBaseHeader *depthSwapchainImage =
nullptr;
1628 if (m_submitLayerDepth && !m_depthSwapchains.isEmpty()) {
1629 if (checkXrResult(xrAcquireSwapchainImage(m_depthSwapchains[0].handle, &acquireInfo, &swapchainImageIndex))) {
1630 if (checkXrResult(xrWaitSwapchainImage(m_depthSwapchains[0].handle, &waitInfo)))
1631 depthSwapchainImage = m_depthSwapchainImages[m_depthSwapchains[0].handle][swapchainImageIndex];
1633 qWarning(
"Failed to wait for depth swapchain image (multiview)");
1635 qWarning(
"Failed to acquire depth swapchain image (multiview)");
1642 for (uint32_t i = 0; i < viewCountOutput; i++) {
1644 m_projectionLayerViews[i].pose = m_views[i].pose;
1645 m_projectionLayerViews[i].fov = m_views[i].fov;
1647 updateCameraMultiview(0, viewCountOutput);
1653 doRender(m_projectionLayerViews[0].subImage,
1655 depthSwapchainImage);
1657 for (uint32_t i = 0; i < viewCountOutput; i++) {
1658 if (m_submitLayerDepth) {
1659 m_layerDepthInfos[i].minDepth = 0;
1660 m_layerDepthInfos[i].maxDepth = 1;
1661 QQuick3DXrEyeCamera *cam = xrOrigin ? xrOrigin->eyeCamera(i) :
nullptr;
1662 m_layerDepthInfos[i].nearZ = cam ? cam->clipNear() : 1.0f;
1663 m_layerDepthInfos[i].farZ = cam ? cam->clipFar() : 10000.0f;
1664 m_projectionLayerViews[i].next = &m_layerDepthInfos[i];
1666 m_projectionLayerViews[i].next =
nullptr;
1671 XrSwapchainImageReleaseInfo releaseInfo{};
1672 releaseInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO;
1673 if (!checkXrResult(xrReleaseSwapchainImage(swapchain.handle, &releaseInfo)))
1674 qWarning(
"Failed to release swapchain image");
1675 if (depthSwapchainImage) {
1676 if (!checkXrResult(xrReleaseSwapchainImage(m_depthSwapchains[0].handle, &releaseInfo)))
1677 qWarning(
"Failed to release depth swapchain image");
1680 for (uint32_t i = 0; i < viewCountOutput; i++) {
1682 const Swapchain viewSwapchain = m_swapchains[i];
1685 XrSwapchainImageAcquireInfo acquireInfo{};
1686 acquireInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO;
1687 uint32_t swapchainImageIndex = 0;
1688 if (!checkXrResult(xrAcquireSwapchainImage(viewSwapchain.handle, &acquireInfo, &swapchainImageIndex))) {
1689 qWarning(
"Failed to acquire swapchain image");
1692 XrSwapchainImageWaitInfo waitInfo{};
1693 waitInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO;
1694 waitInfo.timeout = XR_INFINITE_DURATION;
1695 if (!checkXrResult(xrWaitSwapchainImage(viewSwapchain.handle, &waitInfo))) {
1696 qWarning(
"Failed to wait for swapchain image");
1699 XrSwapchainImageBaseHeader *swapchainImage = m_swapchainImages[viewSwapchain.handle][swapchainImageIndex];
1701 XrSwapchainImageBaseHeader *depthSwapchainImage =
nullptr;
1702 if (m_submitLayerDepth && !m_depthSwapchains.isEmpty()) {
1703 if (checkXrResult(xrAcquireSwapchainImage(m_depthSwapchains[i].handle, &acquireInfo, &swapchainImageIndex))) {
1704 if (checkXrResult(xrWaitSwapchainImage(m_depthSwapchains[i].handle, &waitInfo)))
1705 depthSwapchainImage = m_depthSwapchainImages[m_depthSwapchains[i].handle][swapchainImageIndex];
1707 qWarning(
"Failed to wait for depth swapchain image");
1709 qWarning(
"Failed to acquire depth swapchain image");
1713 m_projectionLayerViews[i].subImage.swapchain = viewSwapchain.handle;
1714 m_projectionLayerViews[i].subImage.imageArrayIndex = 0;
1715 m_projectionLayerViews[i].pose = m_views[i].pose;
1716 m_projectionLayerViews[i].fov = m_views[i].fov;
1718 updateCameraNonMultiview(i, m_projectionLayerViews[i]);
1720 doRender(m_projectionLayerViews[i].subImage,
1722 depthSwapchainImage);
1724 if (depthSwapchainImage) {
1725 m_layerDepthInfos[i].subImage.swapchain = m_depthSwapchains[i].handle;
1726 m_layerDepthInfos[i].subImage.imageArrayIndex = 0;
1727 m_layerDepthInfos[i].minDepth = 0;
1728 m_layerDepthInfos[i].maxDepth = 1;
1729 QQuick3DXrEyeCamera *cam = xrOrigin ? xrOrigin->eyeCamera(i) :
nullptr;
1730 m_layerDepthInfos[i].nearZ = cam ? cam->clipNear() : 1.0f;
1731 m_layerDepthInfos[i].farZ = cam ? cam->clipFar() : 10000.0f;
1732 m_projectionLayerViews[i].next = &m_layerDepthInfos[i];
1734 m_projectionLayerViews[i].next =
nullptr;
1737 XrSwapchainImageReleaseInfo releaseInfo{};
1738 releaseInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO;
1739 if (!checkXrResult(xrReleaseSwapchainImage(viewSwapchain.handle, &releaseInfo)))
1740 qWarning(
"Failed to release swapchain image");
1741 if (depthSwapchainImage) {
1742 if (!checkXrResult(xrReleaseSwapchainImage(m_depthSwapchains[i].handle, &releaseInfo)))
1743 qWarning(
"Failed to release depth swapchain image");
1748#if QT_CONFIG(graphicsframecapture)
1750 m_frameCapture->endCaptureFrame();
1753 layer.space = m_appSpace;
1754 layer.viewCount = (uint32_t)m_projectionLayerViews.size();
1755 layer.views = m_projectionLayerViews.data();
1759 qCDebug(lcQuick3DXr,
"xrLocateViews returned qualified success code: %s", to_string(res));
1764 const XrSwapchainImageBaseHeader *swapchainImage,
1765 const XrSwapchainImageBaseHeader *depthSwapchainImage)
1767 Q_Q(QQuick3DXrManager);
1769 auto *quickWindow = q->m_quickWindow;
1770 auto *renderControl = q->m_renderControl;
1772 const int arraySize = m_multiviewRendering ? m_swapchains[0].arraySize : 1;
1773 quickWindow->setRenderTarget(m_graphics->renderTarget(subImage,
1775 m_colorSwapchainFormat,
1778 depthSwapchainImage,
1779 m_depthSwapchainFormat));
1781 quickWindow->setGeometry(0,
1783 subImage.imageRect.extent.width,
1784 subImage.imageRect.extent.height);
1785 quickWindow->contentItem()->setSize(QSizeF(subImage.imageRect.extent.width,
1786 subImage.imageRect.extent.height));
1788 renderControl->polishItems();
1789 renderControl->beginFrame();
1790 renderControl->sync();
1791 renderControl->render();
1792 renderControl->endFrame();
1797 QRhiRenderTarget *rt = QQuickWindowPrivate::get(quickWindow)->activeCustomRhiRenderTarget();
1798 if (rt->resourceType() == QRhiResource::TextureRenderTarget &&
static_cast<QRhiTextureRenderTarget *>(rt)->description().colorAttachmentAt(0)->texture())
1799 emit q->frameReady();
1804 PFN_xrEnumerateColorSpacesFB pfnxrEnumerateColorSpacesFB = NULL;
1805 OpenXRHelpers::resolveXrFunction(m_instance,
"xrEnumerateColorSpacesFB", (PFN_xrVoidFunction*)(&pfnxrEnumerateColorSpacesFB));
1806 if (!pfnxrEnumerateColorSpacesFB)
1809 uint32_t colorSpaceCountOutput = 0;
1810 if (!checkXrResult(pfnxrEnumerateColorSpacesFB(m_session, 0, &colorSpaceCountOutput,
nullptr))) {
1811 qWarning(
"Failed to enumerate color spaces");
1815 XrColorSpaceFB* colorSpaces =
1816 (XrColorSpaceFB*)malloc(colorSpaceCountOutput *
sizeof(XrColorSpaceFB));
1818 if (!checkXrResult(pfnxrEnumerateColorSpacesFB(m_session, colorSpaceCountOutput, &colorSpaceCountOutput, colorSpaces))) {
1819 qWarning(
"Failed to enumerate color spaces");
1823 qCDebug(lcQuick3DXr,
"Supported color spaces:");
1824 for (uint32_t i = 0; i < colorSpaceCountOutput; i++) {
1825 qCDebug(lcQuick3DXr,
"%d:%d", i, colorSpaces[i]);
1828 const XrColorSpaceFB requestColorSpace = XR_COLOR_SPACE_QUEST_FB;
1830 PFN_xrSetColorSpaceFB pfnxrSetColorSpaceFB = NULL;
1831 OpenXRHelpers::resolveXrFunction(m_instance,
"xrSetColorSpaceFB", (PFN_xrVoidFunction*)(&pfnxrSetColorSpaceFB));
1833 if (!checkXrResult(pfnxrSetColorSpaceFB(m_session, requestColorSpace)))
1834 qWarning(
"Failed to set color space");
1841 PFN_xrEnumerateDisplayRefreshRatesFB pfnxrEnumerateDisplayRefreshRatesFB = NULL;
1842 OpenXRHelpers::resolveXrFunction(m_instance,
"xrEnumerateDisplayRefreshRatesFB", (PFN_xrVoidFunction*)(&pfnxrEnumerateDisplayRefreshRatesFB));
1843 if (!pfnxrEnumerateDisplayRefreshRatesFB)
1846 uint32_t numSupportedDisplayRefreshRates;
1847 QVector<
float> supportedDisplayRefreshRates;
1849 if (!checkXrResult(pfnxrEnumerateDisplayRefreshRatesFB(m_session, 0, &numSupportedDisplayRefreshRates,
nullptr))) {
1850 qWarning(
"Failed to enumerate display refresh rates");
1854 supportedDisplayRefreshRates.resize(numSupportedDisplayRefreshRates);
1856 if (!checkXrResult(pfnxrEnumerateDisplayRefreshRatesFB(
1858 numSupportedDisplayRefreshRates,
1859 &numSupportedDisplayRefreshRates,
1860 supportedDisplayRefreshRates.data())))
1862 qWarning(
"Failed to enumerate display refresh rates");
1866 qCDebug(lcQuick3DXr,
"Supported Refresh Rates:");
1867 for (uint32_t i = 0; i < numSupportedDisplayRefreshRates; i++) {
1868 qCDebug(lcQuick3DXr,
"%d:%f", i, supportedDisplayRefreshRates[i]);
1871 PFN_xrGetDisplayRefreshRateFB pfnGetDisplayRefreshRate;
1872 OpenXRHelpers::resolveXrFunction(m_instance,
"xrGetDisplayRefreshRateFB", (PFN_xrVoidFunction*)(&pfnGetDisplayRefreshRate));
1874 float currentDisplayRefreshRate = 0.0f;
1875 if (!checkXrResult(pfnGetDisplayRefreshRate(m_session, ¤tDisplayRefreshRate)))
1876 qWarning(
"Failed to get display refresh rate");
1878 qCDebug(lcQuick3DXr,
"Current System Display Refresh Rate: %f", currentDisplayRefreshRate);
1880 PFN_xrRequestDisplayRefreshRateFB pfnRequestDisplayRefreshRate;
1881 OpenXRHelpers::resolveXrFunction(m_instance,
"xrRequestDisplayRefreshRateFB", (PFN_xrVoidFunction*)(&pfnRequestDisplayRefreshRate));
1884 if (!checkXrResult(pfnRequestDisplayRefreshRate(m_session, 0.0f)))
1885 qWarning(
"Failed to request display refresh rate");
1887 qCDebug(lcQuick3DXr,
"Requesting system default display refresh rate");
1892 PFN_xrCreateFoveationProfileFB pfnCreateFoveationProfileFB;
1893 OpenXRHelpers::resolveXrFunction(m_instance,
"xrCreateFoveationProfileFB", (PFN_xrVoidFunction*)(&pfnCreateFoveationProfileFB));
1894 if (!pfnCreateFoveationProfileFB)
1897 PFN_xrDestroyFoveationProfileFB pfnDestroyFoveationProfileFB;
1898 OpenXRHelpers::resolveXrFunction(m_instance,
"xrDestroyFoveationProfileFB", (PFN_xrVoidFunction*)(&pfnDestroyFoveationProfileFB));
1900 PFN_xrUpdateSwapchainFB pfnUpdateSwapchainFB;
1901 OpenXRHelpers::resolveXrFunction(m_instance,
"xrUpdateSwapchainFB", (PFN_xrVoidFunction*)(&pfnUpdateSwapchainFB));
1903 for (
auto swapchain : m_swapchains) {
1904 XrFoveationLevelProfileCreateInfoFB levelProfileCreateInfo = {};
1905 levelProfileCreateInfo.type = XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB;
1906 levelProfileCreateInfo.level = m_foveationLevel;
1907 levelProfileCreateInfo.verticalOffset = 0;
1908 levelProfileCreateInfo.dynamic = XR_FOVEATION_DYNAMIC_DISABLED_FB;
1910 XrFoveationProfileCreateInfoFB profileCreateInfo = {};
1911 profileCreateInfo.type = XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB;
1912 profileCreateInfo.next = &levelProfileCreateInfo;
1914 XrFoveationProfileFB foveationProfile;
1915 pfnCreateFoveationProfileFB(m_session, &profileCreateInfo, &foveationProfile);
1917 XrSwapchainStateFoveationFB foveationUpdateState = {};
1918 memset(&foveationUpdateState, 0,
sizeof(foveationUpdateState));
1919 foveationUpdateState.type = XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB;
1920 foveationUpdateState.profile = foveationProfile;
1922 XrResult updateSwapchainFBResult = OpenXRHelpers::safeCall(pfnUpdateSwapchainFB, swapchain.handle, (
const XrSwapchainStateBaseHeaderFB*)(&foveationUpdateState));
1923 if (updateSwapchainFBResult == XR_ERROR_FUNCTION_UNSUPPORTED)
1926 XrResult destroyFoveationProfileFBResult = OpenXRHelpers::safeCall(pfnDestroyFoveationProfileFB, foveationProfile);
1927 if (destroyFoveationProfileFBResult == XR_ERROR_FUNCTION_UNSUPPORTED)
1930 qCDebug(lcQuick3DXr,
"Fixed foveated rendering requested with level %d",
int(m_foveationLevel));
1939 Q_ASSERT(m_fbPassthroughEnabled);
1941 PFN_xrCreatePassthroughFB pfnXrCreatePassthroughFBX =
nullptr;
1942 OpenXRHelpers::resolveXrFunction(m_instance,
"xrCreatePassthroughFB", (PFN_xrVoidFunction*)(&pfnXrCreatePassthroughFBX));
1944 XrPassthroughCreateInfoFB passthroughCreateInfo{};
1945 passthroughCreateInfo.type = XR_TYPE_PASSTHROUGH_CREATE_INFO_FB;
1946 passthroughCreateInfo.flags = XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB;
1948 XrResult xrCreatePassthroughFBXResult = OpenXRHelpers::safeCall(pfnXrCreatePassthroughFBX, m_session,
static_cast<
const XrPassthroughCreateInfoFB*>(&passthroughCreateInfo), &m_passthroughFeature);
1949 if (!checkXrResult(xrCreatePassthroughFBXResult))
1950 qWarning(
"Failed to create passthrough object");
1955 PFN_xrDestroyPassthroughFB pfnXrDestroyPassthroughFBX =
nullptr;
1956 OpenXRHelpers::resolveXrFunction(m_instance,
"xrDestroyPassthroughFB", (PFN_xrVoidFunction*)(&pfnXrDestroyPassthroughFBX));
1958 XrResult xrDestroyPassthroughFBXResult = OpenXRHelpers::safeCall(pfnXrDestroyPassthroughFBX, m_passthroughFeature);
1959 if (!checkXrResult(xrDestroyPassthroughFBXResult))
1960 qWarning(
"Failed to destroy passthrough object");
1962 m_passthroughFeature = XR_NULL_HANDLE;
1967 PFN_xrPassthroughStartFB pfnXrPassthroughStartFBX =
nullptr;
1968 OpenXRHelpers::resolveXrFunction(m_instance,
"xrPassthroughStartFB", (PFN_xrVoidFunction*)(&pfnXrPassthroughStartFBX));
1970 XrResult xrPassthroughStartFBXResult = OpenXRHelpers::safeCall(pfnXrPassthroughStartFBX, m_passthroughFeature);
1971 if (!checkXrResult(xrPassthroughStartFBXResult))
1972 qWarning(
"Failed to start passthrough");
1977 PFN_xrPassthroughPauseFB pfnXrPassthroughPauseFBX =
nullptr;
1978 OpenXRHelpers::resolveXrFunction(m_instance,
"xrPassthroughPauseFB", (PFN_xrVoidFunction*)(&pfnXrPassthroughPauseFBX));
1980 XrResult xrPassthroughPauseFBXResult = OpenXRHelpers::safeCall(pfnXrPassthroughPauseFBX, m_passthroughFeature);
1981 if (!checkXrResult(xrPassthroughPauseFBXResult))
1982 qWarning(
"Failed to pause passthrough");
1987 PFN_xrCreatePassthroughLayerFB pfnXrCreatePassthroughLayerFBX =
nullptr;
1988 OpenXRHelpers::resolveXrFunction(m_instance,
"xrCreatePassthroughLayerFB", (PFN_xrVoidFunction*)(&pfnXrCreatePassthroughLayerFBX));
1990 XrPassthroughLayerCreateInfoFB layerCreateInfo{};
1991 layerCreateInfo.type = XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB;
1992 layerCreateInfo.passthrough = m_passthroughFeature;
1993 layerCreateInfo.purpose = XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB;
1994 if (m_fbPassthroughEnabled)
1995 layerCreateInfo.flags = XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB;
1997 XrResult xrCreatePassthroughLayerFBXResult = OpenXRHelpers::safeCall(pfnXrCreatePassthroughLayerFBX, m_session,
static_cast<
const XrPassthroughLayerCreateInfoFB*>(&layerCreateInfo), &m_passthroughLayer);
1998 if (!checkXrResult(xrCreatePassthroughLayerFBXResult))
1999 qWarning(
"Failed to create passthrough layer");
2004 PFN_xrDestroyPassthroughLayerFB pfnXrDestroyPassthroughLayerFBX =
nullptr;
2005 OpenXRHelpers::resolveXrFunction(m_instance,
"xrDestroyPassthroughLayerFB", (PFN_xrVoidFunction*)(&pfnXrDestroyPassthroughLayerFBX));
2007 XrResult xrDestroyPassthroughLayerFBXResult = OpenXRHelpers::safeCall(pfnXrDestroyPassthroughLayerFBX, m_passthroughLayer);
2008 if (!checkXrResult(xrDestroyPassthroughLayerFBXResult))
2009 qWarning(
"Failed to destroy passthrough layer");
2011 m_passthroughLayer = XR_NULL_HANDLE;
2016 PFN_xrPassthroughLayerPauseFB pfnXrPassthroughLayerPauseFBX =
nullptr;
2017 OpenXRHelpers::resolveXrFunction(m_instance,
"xrPassthroughLayerPauseFB", (PFN_xrVoidFunction*)(&pfnXrPassthroughLayerPauseFBX));
2019 XrResult xrPassthroughLayerPauseFBXResult = OpenXRHelpers::safeCall(pfnXrPassthroughLayerPauseFBX, m_passthroughLayer);
2020 if (!checkXrResult(xrPassthroughLayerPauseFBXResult))
2021 qWarning(
"Failed to pause passthrough layer");
2026 PFN_xrPassthroughLayerResumeFB pfnXrPassthroughLayerResumeFBX =
nullptr;
2027 OpenXRHelpers::resolveXrFunction(m_instance,
"xrPassthroughLayerResumeFB", (PFN_xrVoidFunction*)(&pfnXrPassthroughLayerResumeFBX));
2029 XrResult xrPassthroughLayerResumeFBXResult = OpenXRHelpers::safeCall(pfnXrPassthroughLayerResumeFBX, m_passthroughLayer);
2030 if (!checkXrResult(xrPassthroughLayerResumeFBXResult))
2031 qWarning(
"Failed to resume passthrough layer");
2036 quint32 instanceExtensionCount;
2037 if (!checkXrResult(xrEnumerateInstanceExtensionProperties(layerName, 0, &instanceExtensionCount,
nullptr))) {
2038 qWarning(
"Failed to enumerate instance extension properties");
2042 QVector<XrExtensionProperties> extensions(instanceExtensionCount);
2043 for (XrExtensionProperties& extension : extensions) {
2044 extension.type = XR_TYPE_EXTENSION_PROPERTIES;
2045 extension.next =
nullptr;
2048 if (!checkXrResult(xrEnumerateInstanceExtensionProperties(layerName,
2049 quint32(extensions.size()),
2050 &instanceExtensionCount,
2051 extensions.data())))
2053 qWarning(
"Failed to enumerate instance extension properties");
2056 const QByteArray indentStr(indent,
' ');
2057 qCDebug(lcQuick3DXr,
"%sAvailable Extensions: (%d)", indentStr.data(), instanceExtensionCount);
2058 for (
const XrExtensionProperties& extension : extensions) {
2059 qCDebug(lcQuick3DXr,
"%s Name=%s Version=%d.%d.%d",
2061 extension.extensionName,
2062 XR_VERSION_MAJOR(extension.extensionVersion),
2063 XR_VERSION_MINOR(extension.extensionVersion),
2064 XR_VERSION_PATCH(extension.extensionVersion));
2071 if (!checkXrResult(xrEnumerateApiLayerProperties(0, &layerCount,
nullptr))) {
2072 qWarning(
"Failed to enumerate API layer properties");
2076 QVector<XrApiLayerProperties> layers(layerCount);
2077 for (XrApiLayerProperties& layer : layers) {
2078 layer.type = XR_TYPE_API_LAYER_PROPERTIES;
2079 layer.next =
nullptr;
2082 if (!checkXrResult(xrEnumerateApiLayerProperties(quint32(layers.size()), &layerCount, layers.data()))) {
2083 qWarning(
"Failed to enumerate API layer properties");
2087 qCDebug(lcQuick3DXr,
"Available Layers: (%d)", layerCount);
2088 for (
const XrApiLayerProperties& layer : layers) {
2089 qCDebug(lcQuick3DXr,
" Name=%s SpecVersion=%d.%d.%d LayerVersion=%d.%d.%d Description=%s",
2091 XR_VERSION_MAJOR(layer.specVersion),
2092 XR_VERSION_MINOR(layer.specVersion),
2093 XR_VERSION_PATCH(layer.specVersion),
2094 XR_VERSION_MAJOR(layer.layerVersion),
2095 XR_VERSION_MINOR(layer.layerVersion),
2096 XR_VERSION_PATCH(layer.layerVersion),
2098 checkXrExtensions(layer.layerName, 4);
2105 XrApplicationInfo appInfo;
2106 strcpy(appInfo.applicationName, QCoreApplication::applicationName().toUtf8());
2107 appInfo.applicationVersion = 7;
2108 strcpy(appInfo.engineName, QStringLiteral(
"Qt").toUtf8());
2109 appInfo.engineVersion = 6;
2114 appInfo.apiVersion = XR_MAKE_VERSION(1, 0, 34);
2117 uint32_t apiLayerCount = 0;
2118 xrEnumerateApiLayerProperties(0, &apiLayerCount,
nullptr);
2119 QVector<XrApiLayerProperties> apiLayerProperties(apiLayerCount);
2120 for (uint32_t i = 0; i < apiLayerCount; i++) {
2121 apiLayerProperties[i].type = XR_TYPE_API_LAYER_PROPERTIES;
2122 apiLayerProperties[i].next =
nullptr;
2124 xrEnumerateApiLayerProperties(apiLayerCount, &apiLayerCount, apiLayerProperties.data());
2127 QVector<
const char*> enabledApiLayers;
2132 const bool wantsValidationLayer = qEnvironmentVariableIntValue(
"QSG_RHI_DEBUG_LAYER");
2133 if (wantsValidationLayer) {
2134 if (isApiLayerSupported(
"XR_APILAYER_LUNARG_core_validation", apiLayerProperties))
2135 enabledApiLayers.append(
"XR_APILAYER_LUNARG_core_validation");
2137 qCDebug(lcQuick3DXr,
"OpenXR validation layer requested, but not available");
2140 qCDebug(lcQuick3DXr) <<
"Requesting to enable XR API layers:" << enabledApiLayers;
2142 m_enabledApiLayers.clear();
2143 for (
const char *layer : enabledApiLayers)
2144 m_enabledApiLayers.append(QString::fromLatin1(layer));
2147 uint32_t extensionCount = 0;
2148 xrEnumerateInstanceExtensionProperties(
nullptr, 0, &extensionCount,
nullptr);
2149 QVector<XrExtensionProperties> extensionProperties(extensionCount);
2150 for (uint32_t i = 0; i < extensionCount; i++) {
2153 extensionProperties[i].type = XR_TYPE_EXTENSION_PROPERTIES;
2154 extensionProperties[i].next =
nullptr;
2156 xrEnumerateInstanceExtensionProperties(
nullptr, extensionCount, &extensionCount, extensionProperties.data());
2158 QVector<
const char*> enabledExtensions;
2159 if (!m_graphics->initialize(extensionProperties)) {
2160 qWarning(
"Failed to initialize OpenXR graphics module");
2161 return XR_ERROR_GRAPHICS_DEVICE_INVALID;
2163 enabledExtensions.append(m_graphics->getRequiredExtensions());
2165 if (isExtensionSupported(
"XR_EXT_debug_utils", extensionProperties))
2166 enabledExtensions.append(
"XR_EXT_debug_utils");
2168 if (isExtensionSupported(XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME, extensionProperties))
2169 enabledExtensions.append(XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME);
2171 m_handtrackingExtensionSupported = isExtensionSupported(XR_EXT_HAND_TRACKING_EXTENSION_NAME, extensionProperties);
2172 if (m_handtrackingExtensionSupported)
2173 enabledExtensions.append(XR_EXT_HAND_TRACKING_EXTENSION_NAME);
2175 m_compositionLayerDepthSupported = isExtensionSupported(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME, extensionProperties);
2176 if (m_compositionLayerDepthSupported) {
2179 enabledExtensions.append(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME);
2180 m_submitLayerDepth = qEnvironmentVariableIntValue(
"QT_QUICK3D_XR_SUBMIT_DEPTH");
2181 if (m_submitLayerDepth)
2182 qCDebug(lcQuick3DXr,
"submitLayerDepth defaults to true due to env.var.");
2184 m_submitLayerDepth =
false;
2189 m_handtrackingAimExtensionSupported = isExtensionSupported(XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME, extensionProperties);
2190 if (m_handtrackingAimExtensionSupported)
2191 enabledExtensions.append(XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME);
2193 if (isExtensionSupported(XR_MSFT_HAND_INTERACTION_EXTENSION_NAME, extensionProperties))
2194 enabledExtensions.append(XR_MSFT_HAND_INTERACTION_EXTENSION_NAME);
2196 if (isExtensionSupported(XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME, extensionProperties))
2197 enabledExtensions.append(XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME);
2201 uint32_t passthroughSpecVersion = 0;
2202 if (isExtensionSupported(XR_FB_PASSTHROUGH_EXTENSION_NAME, extensionProperties, &passthroughSpecVersion)) {
2203 qCDebug(lcQuick3DXr,
"Passthrough extension is supported, spec version %u", passthroughSpecVersion);
2204 enabledExtensions.append(XR_FB_PASSTHROUGH_EXTENSION_NAME);
2206 qCDebug(lcQuick3DXr,
"Passthrough extension is NOT supported");
2209 if (isExtensionSupported(XR_FB_TRIANGLE_MESH_EXTENSION_NAME, extensionProperties))
2210 enabledExtensions.append(XR_FB_TRIANGLE_MESH_EXTENSION_NAME);
2212 m_displayRefreshRateExtensionSupported = isExtensionSupported(XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME, extensionProperties);
2213 if (m_displayRefreshRateExtensionSupported)
2214 enabledExtensions.append(XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME);
2216 m_colorspaceExtensionSupported = isExtensionSupported(XR_FB_COLOR_SPACE_EXTENSION_NAME, extensionProperties);
2217 if (m_colorspaceExtensionSupported)
2218 enabledExtensions.append(XR_FB_COLOR_SPACE_EXTENSION_NAME);
2220 if (isExtensionSupported(XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME, extensionProperties))
2221 enabledExtensions.append(XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME);
2223 m_foveationExtensionSupported = isExtensionSupported(XR_FB_FOVEATION_EXTENSION_NAME, extensionProperties);
2224 if (m_foveationExtensionSupported)
2225 enabledExtensions.append(XR_FB_FOVEATION_EXTENSION_NAME);
2227 if (isExtensionSupported(XR_FB_FOVEATION_CONFIGURATION_EXTENSION_NAME, extensionProperties))
2228 enabledExtensions.append(XR_FB_FOVEATION_CONFIGURATION_EXTENSION_NAME);
2230 if (m_spaceExtension) {
2231 const auto requiredExtensions = m_spaceExtension->requiredExtensions();
2232 bool isSupported =
true;
2233 for (
const auto extension : requiredExtensions) {
2234 isSupported = isExtensionSupported(extension, extensionProperties) && isSupported;
2238 m_spaceExtensionSupported = isSupported;
2240 enabledExtensions.append(requiredExtensions);
2243 if (isExtensionSupported(XR_EXTX_OVERLAY_EXTENSION_NAME, extensionProperties)) {
2244 m_overlayExtensionSupported =
true;
2245 enabledExtensions.append(XR_EXTX_OVERLAY_EXTENSION_NAME);
2249 if (isExtensionSupported(XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME, extensionProperties))
2250 enabledExtensions.append(XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME);
2252 m_androidCreateInstanceExtensionSupported = isExtensionSupported(XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME, extensionProperties);
2253 if (m_androidCreateInstanceExtensionSupported)
2254 enabledExtensions.append(XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME);
2256 auto graphicsAPI = QQuickWindow::graphicsApi();
2257 if (graphicsAPI == QSGRendererInterface::Vulkan) {
2258 if (isExtensionSupported(XR_FB_SWAPCHAIN_UPDATE_STATE_VULKAN_EXTENSION_NAME, extensionProperties))
2259 enabledExtensions.append(XR_FB_SWAPCHAIN_UPDATE_STATE_VULKAN_EXTENSION_NAME);
2260 }
else if (graphicsAPI == QSGRendererInterface::OpenGL) {
2261 if (isExtensionSupported(XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME, extensionProperties))
2262 enabledExtensions.append(XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME);
2266 qCDebug(lcQuick3DXr) <<
"Requesting to enable XR extensions:" << enabledExtensions;
2268 m_enabledExtensions.clear();
2269 for (
const char *extension : enabledExtensions)
2270 m_enabledExtensions.append(QString::fromLatin1(extension));
2273 XrInstanceCreateInfo xrInstanceInfo{};
2274 xrInstanceInfo.type = XR_TYPE_INSTANCE_CREATE_INFO;
2277 XrInstanceCreateInfoAndroidKHR xrInstanceCreateInfoAndroid {};
2278 if (m_androidCreateInstanceExtensionSupported) {
2279 xrInstanceCreateInfoAndroid.type = XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR;
2280 xrInstanceCreateInfoAndroid.applicationVM = m_javaVM;
2281 xrInstanceCreateInfoAndroid.applicationActivity = m_androidActivity.object();
2283 xrInstanceInfo.next = &xrInstanceCreateInfoAndroid;
2288 xrInstanceInfo.createFlags = 0;
2289 xrInstanceInfo.applicationInfo = appInfo;
2290 xrInstanceInfo.enabledApiLayerCount = enabledApiLayers.count();
2291 xrInstanceInfo.enabledApiLayerNames = enabledApiLayers.constData();
2292 xrInstanceInfo.enabledExtensionCount = enabledExtensions.count();
2293 xrInstanceInfo.enabledExtensionNames = enabledExtensions.constData();
2295 return xrCreateInstance(&xrInstanceInfo, &m_instance);
2300 Q_ASSERT(m_instance != XR_NULL_HANDLE);
2301 XrInstanceProperties instanceProperties{};
2302 instanceProperties.type = XR_TYPE_INSTANCE_PROPERTIES;
2303 if (!checkXrResult(xrGetInstanceProperties(m_instance, &instanceProperties))) {
2304 qWarning(
"Failed to get instance properties");
2308 m_runtimeName = QString::fromUtf8(instanceProperties.runtimeName);
2309 m_runtimeVersion = QVersionNumber(XR_VERSION_MAJOR(instanceProperties.runtimeVersion),
2310 XR_VERSION_MINOR(instanceProperties.runtimeVersion),
2311 XR_VERSION_PATCH(instanceProperties.runtimeVersion));
2313 qCDebug(lcQuick3DXr,
"Instance RuntimeName=%s RuntimeVersion=%d.%d.%d",
2314 qPrintable(m_runtimeName),
2315 m_runtimeVersion.majorVersion(),
2316 m_runtimeVersion.minorVersion(),
2317 m_runtimeVersion.microVersion());
2322 if (!m_enabledExtensions.contains(QString::fromUtf8(
"XR_EXT_debug_utils"))) {
2323 qCDebug(lcQuick3DXr,
"No debug utils extension, message redirection not set up");
2327#ifdef XR_EXT_debug_utils
2328 PFN_xrCreateDebugUtilsMessengerEXT xrCreateDebugUtilsMessengerEXT =
nullptr;
2329 OpenXRHelpers::resolveXrFunction(m_instance,
"xrCreateDebugUtilsMessengerEXT",
reinterpret_cast<PFN_xrVoidFunction *>(&xrCreateDebugUtilsMessengerEXT));
2330 if (!xrCreateDebugUtilsMessengerEXT)
2333 OpenXRHelpers::resolveXrFunction(m_instance,
"xrDestroyDebugUtilsMessengerEXT",
reinterpret_cast<PFN_xrVoidFunction *>(&m_xrDestroyDebugUtilsMessengerEXT));
2335 XrDebugUtilsMessengerCreateInfoEXT messengerInfo = {};
2336 messengerInfo.type = XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
2337 messengerInfo.messageSeverities = XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
2338 | XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
2339 messengerInfo.messageTypes = XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
2340 | XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
2341 | XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT
2342 | XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT;
2343 messengerInfo.userCallback = defaultDebugCallbackFunc;
2344 messengerInfo.userData =
this;
2346 XrResult err = xrCreateDebugUtilsMessengerEXT(m_instance, &messengerInfo, &m_debugMessenger);
2347 if (!checkXrResult(err))
2348 qWarning(
"Quick 3D XR: Failed to create debug report callback, OpenXR messages will not get redirected (%d)", err);
2354 Q_ASSERT(m_instance != XR_NULL_HANDLE);
2355 Q_ASSERT(m_systemId == XR_NULL_SYSTEM_ID);
2357 XrSystemGetInfo hmdInfo{};
2358 hmdInfo.type = XR_TYPE_SYSTEM_GET_INFO;
2359 hmdInfo.next =
nullptr;
2360 hmdInfo.formFactor = m_formFactor;
2362 const XrResult result = xrGetSystem(m_instance, &hmdInfo, &m_systemId);
2363 const bool success = checkXrResult(result);
2369 checkViewConfiguration();
2376 quint32 viewConfigTypeCount;
2377 if (!checkXrResult(xrEnumerateViewConfigurations(m_instance,
2380 &viewConfigTypeCount,
2383 qWarning(
"Failed to enumerate view configurations");
2386 QVector<XrViewConfigurationType> viewConfigTypes(viewConfigTypeCount);
2387 if (!checkXrResult(xrEnumerateViewConfigurations(m_instance,
2389 viewConfigTypeCount,
2390 &viewConfigTypeCount,
2391 viewConfigTypes.data())))
2393 qWarning(
"Failed to enumerate view configurations");
2397 qCDebug(lcQuick3DXr,
"Available View Configuration Types: (%d)", viewConfigTypeCount);
2398 for (XrViewConfigurationType viewConfigType : viewConfigTypes) {
2399 qCDebug(lcQuick3DXr,
" View Configuration Type: %s %s", to_string(viewConfigType), viewConfigType == m_viewConfigType ?
"(Selected)" :
"");
2400 XrViewConfigurationProperties viewConfigProperties{};
2401 viewConfigProperties.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES;
2402 if (!checkXrResult(xrGetViewConfigurationProperties(m_instance,
2405 &viewConfigProperties)))
2407 qWarning(
"Failed to get view configuration properties");
2411 qCDebug(lcQuick3DXr,
" View configuration FovMutable=%s", viewConfigProperties.fovMutable == XR_TRUE ?
"True" :
"False");
2414 if (!checkXrResult(xrEnumerateViewConfigurationViews(m_instance,
2421 qWarning(
"Failed to enumerate configuration views");
2425 if (viewCount > 0) {
2426 QVector<XrViewConfigurationView> views(viewCount, {XR_TYPE_VIEW_CONFIGURATION_VIEW,
nullptr, 0, 0, 0, 0, 0, 0});
2427 if (!checkXrResult(xrEnumerateViewConfigurationViews(m_instance,
2434 qWarning(
"Failed to enumerate configuration views");
2438 for (
int i = 0; i < views.size(); ++i) {
2439 const XrViewConfigurationView& view = views[i];
2440 qCDebug(lcQuick3DXr,
" View [%d]: Recommended Width=%d Height=%d SampleCount=%d",
2442 view.recommendedImageRectWidth,
2443 view.recommendedImageRectHeight,
2444 view.recommendedSwapchainSampleCount);
2445 qCDebug(lcQuick3DXr,
" View [%d]: Maximum Width=%d Height=%d SampleCount=%d",
2447 view.maxImageRectWidth,
2448 view.maxImageRectHeight,
2449 view.maxSwapchainSampleCount);
2452 qCDebug(lcQuick3DXr,
"Empty view configuration type");
2454 checkEnvironmentBlendMode(viewConfigType);
2460 return OpenXRHelpers::checkXrResult(result, m_instance);
2466 if (!checkXrResult(xrEnumerateEnvironmentBlendModes(m_instance,
2473 qWarning(
"Failed to enumerate blend modes");
2477 qCDebug(lcQuick3DXr,
"Available Environment Blend Mode count : (%d)", count);
2479 QVector<XrEnvironmentBlendMode> blendModes(count);
2480 if (!checkXrResult(xrEnumerateEnvironmentBlendModes(m_instance,
2485 blendModes.data())))
2487 qWarning(
"Failed to enumerate blend modes");
2491 m_blendModes = blendModes;
2500#include "qquick3dxrmanager_openxr.moc"
bool event(QEvent *event) override
This virtual function receives events to an object and should return true if the event e was recogniz...
void startWaitingForFrame()
static constexpr QEvent::Type asQEvent(QQuick3DOpenXRThreadWorker::Event event)
bool supportsFBPassthrough() const
QQuick3DXrManagerPrivate(QQuick3DXrManager &manager)
void setupWindow(QQuickWindow *window)
bool finalizeGraphics(QRhi *rhi)
QVersionNumber runtimeVersion() const
QtQuick3DXr::ReferenceSpace getReferenceSpace() const
bool isPassthroughEnabled() const
QStringList enabledExtensions() const
bool setTransparentBlendMode(bool enable)
~QQuick3DXrManagerPrivate()
bool supportsPassthrough() const
bool setupGraphics(QQuickWindow *window)
void setMultiViewRenderingEnabled(bool enable)
bool supportsTransparentBlendMode() const
void setDepthSubmissionEnabled(bool enable)
void setFBPassthroughEnabled(bool enable)
void setSamples(int samples)
void getDefaultClipDistances(float &nearClip, float &farClip) const
bool isGraphicsInitialized() const
bool setPassthroughEnabled(bool enable)
QString runtimeName() const
static bool isMultiviewRenderingDisabled()
@ ReferenceSpaceLocalFloor
QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQIORing)
#define MAKE_TO_STRING_FUNC(enumType)
static bool isApiLayerSupported(const char *layerName, const QVector< XrApiLayerProperties > &apiLayerProperties)
#define ENUM_CASE_STR(name, val)
static const char s_workerThreadName[]
static QtQuick3DXr::ReferenceSpace getReferenceSpaceType(XrReferenceSpaceType referenceSpace)
static std::pair< XrResult, XrFrameState > waitForNextFrame(XrSession session)
static bool isExtensionSupported(const char *extensionName, const QVector< XrExtensionProperties > &instanceExtensionProperties, uint32_t *extensionVersion=nullptr)