diff --git a/apps/openmw/mwvr/openxrmanager.cpp b/apps/openmw/mwvr/openxrmanager.cpp index 0061049b0..213237b7b 100644 --- a/apps/openmw/mwvr/openxrmanager.cpp +++ b/apps/openmw/mwvr/openxrmanager.cpp @@ -111,6 +111,11 @@ namespace MWVR return impl().getRecommendedSwapchainConfig(); } + bool OpenXRManager::xrExtensionIsEnabled(const char* extensionName) const + { + return impl().xrExtensionIsEnabled(extensionName); + } + void OpenXRManager::CleanupOperation::operator()( osg::GraphicsContext* gc) diff --git a/apps/openmw/mwvr/openxrmanager.hpp b/apps/openmw/mwvr/openxrmanager.hpp index 3dc248f73..a7ee8d73b 100644 --- a/apps/openmw/mwvr/openxrmanager.hpp +++ b/apps/openmw/mwvr/openxrmanager.hpp @@ -83,6 +83,9 @@ namespace MWVR //! Configuration hints for instantiating swapchains, queried from openxr. std::array getRecommendedSwapchainConfig() const; + //! Check whether a given openxr extension is enabled or not + bool xrExtensionIsEnabled(const char* extensionName) const; + OpenXRManagerImpl& impl() { return *mPrivate; } const OpenXRManagerImpl& impl() const { return *mPrivate; } diff --git a/apps/openmw/mwvr/openxrmanagerimpl.cpp b/apps/openmw/mwvr/openxrmanagerimpl.cpp index 975ea76d6..a0a43b5ab 100644 --- a/apps/openmw/mwvr/openxrmanagerimpl.cpp +++ b/apps/openmw/mwvr/openxrmanagerimpl.cpp @@ -5,13 +5,16 @@ #include #include +#include + #include "../mwmechanics/actorutil.hpp" + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" + #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" #include "../mwworld/esmstore.hpp" -#include // The OpenXR SDK assumes we've included Windows.h #include @@ -45,13 +48,18 @@ MAKE_TO_STRING_FUNC(XrResult); MAKE_TO_STRING_FUNC(XrFormFactor); MAKE_TO_STRING_FUNC(XrStructureType); +#if !XR_KHR_composition_layer_depth || !XR_KHR_opengl_enable +#error "OpenXR extensions missing. Please upgrade your copy of the OpenXR SDK" +#endif namespace MWVR { OpenXRManagerImpl::OpenXRManagerImpl() { + std::vector extensions = { - XR_KHR_OPENGL_ENABLE_EXTENSION_NAME + XR_KHR_OPENGL_ENABLE_EXTENSION_NAME, + XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME, }; { // Create Instance @@ -59,13 +67,57 @@ namespace MWVR createInfo.next = nullptr; createInfo.enabledExtensionCount = extensions.size(); createInfo.enabledExtensionNames = extensions.data(); - strcpy(createInfo.applicationInfo.applicationName, "openmw_vr"); createInfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION; - CHECK_XRCMD(xrCreateInstance(&createInfo, &mInstance)); + // Iteratively strip extensions until instance creation succeeds. + XrResult result = xrCreateInstance(&createInfo, &mInstance); + while (result == XR_ERROR_EXTENSION_NOT_PRESENT) + { + createInfo.enabledExtensionCount--; + result = xrCreateInstance(&createInfo, &mInstance); + } + + mEnabledExtensions.insert(extensions.begin(), extensions.end() + createInfo.enabledExtensionCount); + + if (!xrExtensionIsEnabled(XR_KHR_OPENGL_ENABLE_EXTENSION_NAME)) + throw std::runtime_error(std::string("Required OpenXR extension ") + XR_KHR_OPENGL_ENABLE_EXTENSION_NAME + " not supported"); + + Log(Debug::Verbose) << "OpenXR Extension status:"; + for (auto* ext : extensions) + { + if (!xrExtensionIsEnabled(ext)) + { + Log(Debug::Verbose) << " " << ext << ": disabled (not supported)"; + } + else + { + Log(Debug::Verbose) << " " << ext << ": enabled"; + } + } + + assert(mInstance); } + { + // Layer depth is enabled, cache the invariant values + if (xrExtensionIsEnabled(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME)) + { + GLfloat depthRange[2] = { 0.f, 1.f }; + glGetFloatv(GL_DEPTH_RANGE, depthRange); + auto nearClip = Settings::Manager::getFloat("near clip", "Camera"); + + for (auto& layer : mLayerDepth) + { + layer.type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR; + layer.next = nullptr; + layer.minDepth = depthRange[0]; + layer.maxDepth = depthRange[1]; + layer.nearZ = nearClip; + } + } + } + { // Get system ID XrSystemGetInfo systemInfo{ XR_TYPE_SYSTEM_GET_INFO }; systemInfo.formFactor = mFormFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; @@ -321,6 +373,18 @@ namespace MWVR layer.views = compositionLayerProjectionViews.data(); auto* xrLayerStack = reinterpret_cast(&layer); + std::array compositionLayerDepth = mLayerDepth; + if (xrExtensionIsEnabled(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME)) + { + auto farClip = Settings::Manager::getFloat("viewing distance", "Camera"); + compositionLayerDepth[(int)Side::LEFT_SIDE].farZ = farClip; + compositionLayerDepth[(int)Side::RIGHT_SIDE].farZ = farClip; + compositionLayerDepth[(int)Side::LEFT_SIDE].subImage = layerStack[(int)Side::LEFT_SIDE].swapchain->impl().xrSubImageDepth(); + compositionLayerDepth[(int)Side::RIGHT_SIDE].subImage = layerStack[(int)Side::RIGHT_SIDE].swapchain->impl().xrSubImageDepth(); + compositionLayerProjectionViews[(int)Side::LEFT_SIDE].next = &compositionLayerDepth[(int)Side::LEFT_SIDE]; + compositionLayerProjectionViews[(int)Side::RIGHT_SIDE].next = &compositionLayerDepth[(int)Side::RIGHT_SIDE]; + } + XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO }; frameEndInfo.displayTime = displayTime; frameEndInfo.environmentBlendMode = mEnvironmentBlendMode; @@ -513,6 +577,11 @@ namespace MWVR return referenceSpace; } + bool OpenXRManagerImpl::xrExtensionIsEnabled(const char* extensionName) const + { + return mEnabledExtensions.count(extensionName) != 0; + } + void OpenXRManagerImpl::enablePredictions() { mPredictionsEnabled = true; diff --git a/apps/openmw/mwvr/openxrmanagerimpl.hpp b/apps/openmw/mwvr/openxrmanagerimpl.hpp index c8f26cada..cf7b69d14 100644 --- a/apps/openmw/mwvr/openxrmanagerimpl.hpp +++ b/apps/openmw/mwvr/openxrmanagerimpl.hpp @@ -69,6 +69,7 @@ namespace MWVR XrSpace getReferenceSpace(ReferenceSpace space); XrSession xrSession() const { return mSession; }; XrInstance xrInstance() const { return mInstance; }; + bool xrExtensionIsEnabled(const char* extensionName) const; protected: void LogLayersAndExtensions(); @@ -78,6 +79,7 @@ namespace MWVR void HandleSessionStateChanged(const XrEventDataSessionStateChanged& stateChangedEvent); private: + bool initialized = false; bool mPredictionsEnabled = false; XrInstance mInstance = XR_NULL_HANDLE; @@ -98,6 +100,9 @@ namespace MWVR bool mSessionRunning = false; std::mutex mFrameStateMutex{}; std::mutex mEventMutex{}; + std::set mEnabledExtensions; + + std::array mLayerDepth; }; } diff --git a/apps/openmw/mwvr/openxrswapchainimpl.cpp b/apps/openmw/mwvr/openxrswapchainimpl.cpp index ce1d0d638..dff102bc3 100644 --- a/apps/openmw/mwvr/openxrswapchainimpl.cpp +++ b/apps/openmw/mwvr/openxrswapchainimpl.cpp @@ -91,7 +91,7 @@ namespace MWVR { // Now create the swapchain of depth buffers. swapchainCreateInfo.format = mSwapchainDepthFormat; swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - res = xrCreateSwapchain(xr->impl().xrSession(), &swapchainCreateInfo, &mDepthSwapchain); + res = xrCreateSwapchain(xr->impl().xrSession(), &swapchainCreateInfo, &mSwapchainDepth); if (XR_SUCCEEDED(res)) break; else @@ -100,19 +100,22 @@ namespace MWVR { uint32_t imageCount = 0; CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, 0, &imageCount, nullptr)); - mSwapchainImageBuffers.resize(imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR }); - CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, imageCount, &imageCount, reinterpret_cast(mSwapchainImageBuffers.data()))); + mSwapchainColorBuffers.resize(imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR }); + CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, imageCount, &imageCount, reinterpret_cast(mSwapchainColorBuffers.data()))); - CHECK_XRCMD(xrEnumerateSwapchainImages(mDepthSwapchain, 0, &imageCount, nullptr)); - mDepthSwapchainImageBuffers.resize(imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR }); - CHECK_XRCMD(xrEnumerateSwapchainImages(mDepthSwapchain, imageCount, &imageCount, reinterpret_cast(mDepthSwapchainImageBuffers.data()))); + CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchainDepth, 0, &imageCount, nullptr)); + mSwapchainDepthBuffers.resize(imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR }); + CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchainDepth, imageCount, &imageCount, reinterpret_cast(mSwapchainDepthBuffers.data()))); for (unsigned i = 0; i < imageCount; i++) - mRenderBuffers.emplace_back(new VRFramebuffer(state, mWidth, mHeight, mSamples, mSwapchainImageBuffers[i].image, mDepthSwapchainImageBuffers[i].image)); + mRenderBuffers.emplace_back(new VRFramebuffer(state, mWidth, mHeight, mSamples, mSwapchainColorBuffers[i].image, mSwapchainDepthBuffers[i].image)); mSubImage.swapchain = mSwapchain; mSubImage.imageRect.offset = { 0, 0 }; mSubImage.imageRect.extent = { mWidth, mHeight }; + mSubImageDepth.swapchain = mSwapchainDepth; + mSubImageDepth.imageRect.offset = { 0, 0 }; + mSubImageDepth.imageRect.extent = { mWidth, mHeight }; } OpenXRSwapchainImpl::~OpenXRSwapchainImpl() @@ -130,13 +133,13 @@ namespace MWVR { uint32_t OpenXRSwapchainImpl::acquiredColorTexture() const { checkAcquired(); - return mSwapchainImageBuffers[mAcquiredImageIndex].image; + return mSwapchainColorBuffers[mAcquiredImageIndex].image; } uint32_t OpenXRSwapchainImpl::acquiredDepthTexture() const { checkAcquired(); - return mSwapchainImageBuffers[mAcquiredImageIndex].image; + return mSwapchainColorBuffers[mAcquiredImageIndex].image; } bool OpenXRSwapchainImpl::isAcquired() const @@ -167,14 +170,14 @@ namespace MWVR { // If some dumb ass implementation decides to violate this we'll just have to work around that if it actually happens. CHECK_XRCMD(xrAcquireSwapchainImage(mSwapchain, &acquireInfo, &mAcquiredImageIndex)); uint32_t depthIndex = 0; - CHECK_XRCMD(xrAcquireSwapchainImage(mDepthSwapchain, &acquireInfo, &depthIndex)); + CHECK_XRCMD(xrAcquireSwapchainImage(mSwapchainDepth, &acquireInfo, &depthIndex)); if (depthIndex != mAcquiredImageIndex) Log(Debug::Warning) << "Depth and color indices diverged"; XrSwapchainImageWaitInfo waitInfo{ XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO }; waitInfo.timeout = XR_INFINITE_DURATION; CHECK_XRCMD(xrWaitSwapchainImage(mSwapchain, &waitInfo)); - CHECK_XRCMD(xrWaitSwapchainImage(mDepthSwapchain, &waitInfo)); + CHECK_XRCMD(xrWaitSwapchainImage(mSwapchainDepth, &waitInfo)); mIsAcquired = true; } @@ -185,7 +188,7 @@ namespace MWVR { XrSwapchainImageReleaseInfo releaseInfo{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO }; CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchain, &releaseInfo)); - CHECK_XRCMD(xrReleaseSwapchainImage(mDepthSwapchain, &releaseInfo)); + CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchainDepth, &releaseInfo)); } void OpenXRSwapchainImpl::checkAcquired() const { diff --git a/apps/openmw/mwvr/openxrswapchainimpl.hpp b/apps/openmw/mwvr/openxrswapchainimpl.hpp index 9f7e98ab1..2447daab1 100644 --- a/apps/openmw/mwvr/openxrswapchainimpl.hpp +++ b/apps/openmw/mwvr/openxrswapchainimpl.hpp @@ -24,7 +24,9 @@ namespace MWVR bool isAcquired() const; XrSwapchain xrSwapchain(void) const { return mSwapchain; }; + XrSwapchain xrSwapchainDepth(void) const { return mSwapchainDepth; }; XrSwapchainSubImage xrSubImage(void) const { return mSubImage; }; + XrSwapchainSubImage xrSubImageDepth(void) const { return mSubImageDepth; }; int width() const { return mWidth; }; int height() const { return mHeight; }; int samples() const { return mSamples; }; @@ -39,10 +41,11 @@ namespace MWVR private: XrSwapchain mSwapchain = XR_NULL_HANDLE; - XrSwapchain mDepthSwapchain = XR_NULL_HANDLE; - std::vector mSwapchainImageBuffers{}; - std::vector mDepthSwapchainImageBuffers{}; + XrSwapchain mSwapchainDepth = XR_NULL_HANDLE; + std::vector mSwapchainColorBuffers{}; + std::vector mSwapchainDepthBuffers{}; XrSwapchainSubImage mSubImage{}; + XrSwapchainSubImage mSubImageDepth{}; int32_t mWidth = -1; int32_t mHeight = -1; int32_t mSamples = -1;