1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-30 23:15:36 +00:00

SteamVR compatibility (Lack of depth formats). Cleanup of deprecated oculus workarounds in vrsession.cpp.

This commit is contained in:
Mads Buvik Sandvei 2020-08-02 12:34:46 +02:00
parent ef017285fb
commit c136ae682e
10 changed files with 226 additions and 194 deletions

View file

@ -32,10 +32,10 @@ namespace MWVR
return false;
}
bool OpenXRManager::frameShouldRender()
bool OpenXRManager::xrSessionCanRender()
{
if (realized())
return impl().frameShouldRender();
return impl().xrSessionCanRender();
return false;
}
@ -45,23 +45,19 @@ namespace MWVR
return impl().handleEvents();
}
long long OpenXRManager::waitFrame()
FrameInfo OpenXRManager::waitFrame()
{
if (realized())
return impl().waitFrame();
return 0;
return impl().waitFrame();
}
void OpenXRManager::beginFrame()
{
if (realized())
return impl().beginFrame();
return impl().beginFrame();
}
void OpenXRManager::endFrame(int64_t displayTime, int layerCount, const std::array<CompositionLayerProjectionView, 2>& layerStack)
void OpenXRManager::endFrame(FrameInfo frameInfo, int layerCount, const std::array<CompositionLayerProjectionView, 2>& layerStack)
{
if (realized())
return impl().endFrame(displayTime, layerCount, layerStack);
return impl().endFrame(frameInfo, layerCount, layerStack);
}
void

View file

@ -45,19 +45,19 @@ namespace MWVR
bool realized();
//! Forward call to xrWaitFrame()
long long waitFrame();
FrameInfo waitFrame();
//! Forward call to xrBeginFrame()
void beginFrame();
//! Forward call to xrEndFrame()
void endFrame(int64_t displayTime, int layerCount, const std::array<CompositionLayerProjectionView, 2>& layerStack);
void endFrame(FrameInfo frameInfo, int layerCount, const std::array<CompositionLayerProjectionView, 2>& layerStack);
//! Whether the openxr session is currently in a running state
bool xrSessionRunning();
//! Whether frames should be rendered in the current state
bool frameShouldRender();
//! Whether frames can be rendered in the current state
bool xrSessionCanRender();
//! Process all openxr events
void handleEvents();

View file

@ -56,7 +56,6 @@ namespace MWVR
{
OpenXRManagerImpl::OpenXRManagerImpl()
{
std::vector<const char*> extensions = {
XR_KHR_OPENGL_ENABLE_EXTENSION_NAME,
XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME,
@ -143,30 +142,35 @@ namespace MWVR
auto DC = wglGetCurrentDC();
auto GLRC = wglGetCurrentContext();
auto XRGLRC = wglCreateContext(DC);
auto USERGLRC = wglCreateContext(DC);
wglShareLists(GLRC, XRGLRC);
wglMakeCurrent(DC, XRGLRC);
wglShareLists(GLRC, USERGLRC);
mGraphicsBinding.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
mGraphicsBinding.next = nullptr;
mGraphicsBinding.hDC = DC;
mGraphicsBinding.hGLRC = XRGLRC;
mGraphicsBindingXr.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
mGraphicsBindingXr.next = nullptr;
mGraphicsBindingXr.hDC = DC;
mGraphicsBindingXr.hGLRC = XRGLRC;
mGraphicsBindingUser.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
mGraphicsBindingUser.next = nullptr;
mGraphicsBindingUser.hDC = DC;
mGraphicsBindingUser.hGLRC = USERGLRC;
if (!mGraphicsBinding.hDC)
std::cout << "Missing DC" << std::endl;
if (!mGraphicsBinding.hGLRC)
std::cout << "Missing GLRC" << std::endl;
if (!mGraphicsBindingXr.hDC)
Log(Debug::Warning) << "Missing DC";
if (!mGraphicsBindingXr.hGLRC)
Log(Debug::Warning) << "Missing GLRC";
XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO };
createInfo.next = &mGraphicsBinding;
createInfo.next = &mGraphicsBindingXr;
createInfo.systemId = mSystemId;
CHECK_XRCMD(xrCreateSession(mInstance, &createInfo, &mSession));
assert(mSession);
wglMakeCurrent(DC, GLRC);
}
LogLayersAndExtensions();
LogInstanceInfo();
LogReferenceSpaces();
LogSwapchainFormats();
{ // Set up reference space
XrReferenceSpaceCreateInfo createInfo{ XR_TYPE_REFERENCE_SPACE_CREATE_INFO };
@ -212,15 +216,28 @@ namespace MWVR
}
inline XrResult CheckXrResult(XrResult res, const char* originator, const char* sourceLocation) {
static bool initialized = false;
static bool sLogAllXrCalls = false;
static bool sContinueOnErrors = false;
if (!initialized)
{
initialized = true;
sLogAllXrCalls = Settings::Manager::getBool("log all openxr calls", "VR");
sContinueOnErrors = Settings::Manager::getBool("continue on errors", "VR");
}
if (XR_FAILED(res)) {
std::stringstream ss;
ss << sourceLocation << ": OpenXR[Error: " << to_string(res) << "][Thread: " << std::this_thread::get_id() << "][DC: " << wglGetCurrentDC() << "][GLRC: " << wglGetCurrentContext() << "]: " << originator;
Log(Debug::Error) << ss.str();
throw std::runtime_error(ss.str().c_str());
if (res == XR_ERROR_TIME_INVALID)
Log(Debug::Error) << "Breakpoint";
if (!sContinueOnErrors)
throw std::runtime_error(ss.str().c_str());
}
else
else if (res != XR_SUCCESS || sLogAllXrCalls)
{
// Log(Debug::Verbose) << sourceLocation << ": OpenXR[" << to_string(res) << "][" << std::this_thread::get_id() << "][" << wglGetCurrentDC() << "][" << wglGetCurrentContext() << "]: " << originator;
Log(Debug::Verbose) << sourceLocation << ": OpenXR[" << to_string(res) << "][" << std::this_thread::get_id() << "][" << wglGetCurrentDC() << "][" << wglGetCurrentContext() << "]: " << originator;
}
return res;
@ -317,7 +334,25 @@ namespace MWVR
Log(Debug::Verbose) << ss.str();
}
long long
void OpenXRManagerImpl::LogSwapchainFormats()
{
uint32_t swapchainFormatCount;
CHECK_XRCMD(xrEnumerateSwapchainFormats(xrSession(), 0, &swapchainFormatCount, nullptr));
std::vector<int64_t> swapchainFormats(swapchainFormatCount);
CHECK_XRCMD(xrEnumerateSwapchainFormats(xrSession(), (uint32_t)swapchainFormats.size(), &swapchainFormatCount, swapchainFormats.data()));
std::stringstream ss;
ss << "Available Swapchain formats: (" << swapchainFormatCount << ")" << std::endl;
for (auto format : swapchainFormats)
{
ss << " Enum=" << std::dec << format << " (0x=" << std::hex << format << ")" << std::endl;
}
Log(Debug::Verbose) << ss.str();
}
FrameInfo
OpenXRManagerImpl::waitFrame()
{
XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO };
@ -325,7 +360,23 @@ namespace MWVR
CHECK_XRCMD(xrWaitFrame(mSession, &frameWaitInfo, &frameState));
mFrameState = frameState;
return frameState.predictedDisplayTime;
FrameInfo frameInfo;
frameInfo.runtimePredictedDisplayTime = mFrameState.predictedDisplayTime;
frameInfo.runtimePredictedDisplayPeriod = mFrameState.predictedDisplayPeriod;
frameInfo.runtimeRequestsRender = !!mFrameState.shouldRender;
return frameInfo;
}
static void clearGlErrors()
{
auto error = glGetError();
while (error != GL_NO_ERROR)
{
Log(Debug::Warning) << "glGetError: " << std::dec << error << " (0x" << std::hex << error << ")";
}
}
void
@ -333,11 +384,10 @@ namespace MWVR
{
auto DC = wglGetCurrentDC();
auto GLRC = wglGetCurrentContext();
auto XRDC = mGraphicsBinding.hDC;
auto XRGLRC = mGraphicsBinding.hGLRC;
wglMakeCurrent(XRDC, XRGLRC);
wglMakeCurrent(mGraphicsBindingUser.hDC, mGraphicsBindingUser.hGLRC);
clearGlErrors();
XrFrameBeginInfo frameBeginInfo{ XR_TYPE_FRAME_BEGIN_INFO };
CHECK_XRCMD(xrBeginFrame(mSession, &frameBeginInfo));
wglMakeCurrent(DC, GLRC);
@ -356,13 +406,12 @@ namespace MWVR
}
void
OpenXRManagerImpl::endFrame(int64_t displayTime, int layerCount, const std::array<CompositionLayerProjectionView, 2>& layerStack)
OpenXRManagerImpl::endFrame(FrameInfo frameInfo, int layerCount, const std::array<CompositionLayerProjectionView, 2>& layerStack)
{
auto DC = wglGetCurrentDC();
auto GLRC = wglGetCurrentContext();
auto XRDC = mGraphicsBinding.hDC;
auto XRGLRC = mGraphicsBinding.hGLRC;
wglMakeCurrent(XRDC, XRGLRC);
wglMakeCurrent(mGraphicsBindingUser.hDC, mGraphicsBindingUser.hGLRC);
clearGlErrors();
std::array<XrCompositionLayerProjectionView, 2> compositionLayerProjectionViews{};
compositionLayerProjectionViews[(int)Side::LEFT_SIDE] = toXR(layerStack[(int)Side::LEFT_SIDE]);
@ -378,19 +427,27 @@ namespace MWVR
if (xrExtensionIsEnabled(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME))
{
auto farClip = Settings::Manager::getFloat("viewing distance", "Camera");
// All values not set here are set previously as they are constant
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];
if (compositionLayerDepth[(int)Side::LEFT_SIDE].subImage.swapchain != XR_NULL_HANDLE
&& compositionLayerDepth[(int)Side::RIGHT_SIDE].subImage.swapchain != XR_NULL_HANDLE)
{
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.displayTime = frameInfo.runtimePredictedDisplayTime;
frameEndInfo.environmentBlendMode = mEnvironmentBlendMode;
frameEndInfo.layerCount = layerCount;
frameEndInfo.layers = &xrLayerStack;
//if (frameInfo.runtimeRequestsRender)
{
frameEndInfo.layerCount = layerCount;
frameEndInfo.layers = &xrLayerStack;
}
CHECK_XRCMD(xrEndFrame(mSession, &frameEndInfo));
wglMakeCurrent(DC, GLRC);
@ -642,7 +699,7 @@ namespace MWVR
return mEnabledExtensions.count(extensionName) != 0;
}
bool OpenXRManagerImpl::frameShouldRender()
bool OpenXRManagerImpl::xrSessionCanRender()
{
return xrSessionRunning() && !xrSessionStopRequested();
}

View file

@ -55,9 +55,9 @@ namespace MWVR
OpenXRManagerImpl(void);
~OpenXRManagerImpl(void);
long long waitFrame();
FrameInfo waitFrame();
void beginFrame();
void endFrame(int64_t displayTime, int layerCount, const std::array<CompositionLayerProjectionView, 2>& layerStack);
void endFrame(FrameInfo frameInfo, int layerCount, const std::array<CompositionLayerProjectionView, 2>& layerStack);
bool xrSessionRunning() const { return mSessionRunning; }
std::array<View, 2> getPredictedViews(int64_t predictedDisplayTime, ReferenceSpace space);
MWVR::Pose getPredictedHeadPose(int64_t predictedDisplayTime, ReferenceSpace space);
@ -72,7 +72,7 @@ namespace MWVR
XrInstance xrInstance() const { return mInstance; };
bool xrExtensionIsEnabled(const char* extensionName) const;
bool xrSessionStopRequested();
bool frameShouldRender();
bool xrSessionCanRender();
void xrResourceAcquired();
void xrResourceReleased();
@ -80,6 +80,7 @@ namespace MWVR
void LogLayersAndExtensions();
void LogInstanceInfo();
void LogReferenceSpaces();
void LogSwapchainFormats();
bool xrNextEvent(XrEventDataBuffer& eventBuffer);
void xrQueueEvents();
const XrEventDataBaseHeader* nextEvent();
@ -99,7 +100,8 @@ namespace MWVR
XrViewConfigurationType mViewConfigType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
XrEnvironmentBlendMode mEnvironmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
XrSystemId mSystemId = XR_NULL_SYSTEM_ID;
XrGraphicsBindingOpenGLWin32KHR mGraphicsBinding{ XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR };
XrGraphicsBindingOpenGLWin32KHR mGraphicsBindingXr{ XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR };
XrGraphicsBindingOpenGLWin32KHR mGraphicsBindingUser{ XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR };
XrSystemProperties mSystemProperties{ XR_TYPE_SYSTEM_PROPERTIES };
std::array<XrViewConfigurationView, 2> mConfigViews{ { {XR_TYPE_VIEW_CONFIGURATION_VIEW}, {XR_TYPE_VIEW_CONFIGURATION_VIEW} } };
XrSpace mReferenceSpaceView = XR_NULL_HANDLE;

View file

@ -36,6 +36,9 @@ namespace MWVR {
constexpr int64_t RequestedColorSwapchainFormats[] = {
GL_RGBA8,
GL_RGBA8_SNORM,
GL_SRGB8_ALPHA8,
//GL_RGBA16,
//0x881A // GL_RGBA16F
};
auto swapchainFormatIt =
@ -45,20 +48,30 @@ namespace MWVR {
throw std::runtime_error("Swapchain color format not supported");
}
mSwapchainColorFormat = *swapchainFormatIt;
Log(Debug::Verbose) << "Selected depth format: " << std::dec << mSwapchainColorFormat << " (" << std::hex << mSwapchainColorFormat << ")";
// Find supported depth swapchain format.
constexpr int64_t RequestedDepthSwapchainFormats[] = {
GL_DEPTH_COMPONENT24,
GL_DEPTH_COMPONENT32F,
};
if (xr->xrExtensionIsEnabled(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME))
{
// Find supported depth swapchain format.
constexpr int64_t RequestedDepthSwapchainFormats[] = {
GL_DEPTH_COMPONENT24,
GL_DEPTH_COMPONENT32F,
};
swapchainFormatIt =
std::find_first_of(swapchainFormats.begin(), swapchainFormats.end(), std::begin(RequestedDepthSwapchainFormats),
std::end(RequestedDepthSwapchainFormats));
if (swapchainFormatIt == swapchainFormats.end()) {
throw std::runtime_error("Swapchain depth format not supported");
swapchainFormatIt =
std::find_first_of(swapchainFormats.begin(), swapchainFormats.end(), std::begin(RequestedDepthSwapchainFormats),
std::end(RequestedDepthSwapchainFormats));
if (swapchainFormatIt == swapchainFormats.end()) {
mHaveDepthSwapchain = false;
Log(Debug::Warning) << "OpenXR extension " << XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME << " enabled, but no depth formats were found";
}
else
{
mSwapchainDepthFormat = *swapchainFormatIt;
mHaveDepthSwapchain = true;
Log(Debug::Verbose) << "Selected depth format: " << std::dec << mSwapchainDepthFormat << " (" << std::hex << mSwapchainDepthFormat << ")";
}
}
mSwapchainDepthFormat = *swapchainFormatIt;
mSamples = Settings::Manager::getInt("antialiasing", "Video");
// OpenXR requires a non-zero value
@ -66,17 +79,18 @@ namespace MWVR {
mSamples = 1;
while (mSamples > 0)
XrSwapchainCreateInfo swapchainCreateInfo{ XR_TYPE_SWAPCHAIN_CREATE_INFO };
swapchainCreateInfo.arraySize = 1;
swapchainCreateInfo.width = mWidth;
swapchainCreateInfo.height = mHeight;
swapchainCreateInfo.mipCount = 1;
swapchainCreateInfo.faceCount = 1;
while (mSamples > 0 && mSwapchain == XR_NULL_HANDLE)
{
Log(Debug::Verbose) << "Creating swapchain with dimensions Width=" << mWidth << " Heigh=" << mHeight << " SampleCount=" << mSamples;
// First create the swapchain of color buffers.
XrSwapchainCreateInfo swapchainCreateInfo{ XR_TYPE_SWAPCHAIN_CREATE_INFO };
swapchainCreateInfo.arraySize = 1;
swapchainCreateInfo.format = mSwapchainColorFormat;
swapchainCreateInfo.width = mWidth;
swapchainCreateInfo.height = mHeight;
swapchainCreateInfo.mipCount = 1;
swapchainCreateInfo.faceCount = 1;
swapchainCreateInfo.sampleCount = mSamples;
swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
auto res = xrCreateSwapchain(xr->impl().xrSession(), &swapchainCreateInfo, &mSwapchain);
@ -88,34 +102,42 @@ namespace MWVR {
throw std::runtime_error(XrResultString(res));
continue;
}
// 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, &mSwapchainDepth);
if (XR_SUCCEEDED(res))
break;
else
throw std::runtime_error(XrResultString(res));
}
uint32_t imageCount = 0;
CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, 0, &imageCount, nullptr));
mSwapchainColorBuffers.resize(imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR });
CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, imageCount, &imageCount, reinterpret_cast<XrSwapchainImageBaseHeader*>(mSwapchainColorBuffers.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<XrSwapchainImageBaseHeader*>(mSwapchainDepthBuffers.data())));
for (unsigned i = 0; i < imageCount; i++)
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 };
if (mHaveDepthSwapchain)
{
// Now create the swapchain of depth buffers if applicable
if (mHaveDepthSwapchain)
{
swapchainCreateInfo.format = mSwapchainDepthFormat;
swapchainCreateInfo.sampleCount = mSamples;
swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
auto res = xrCreateSwapchain(xr->impl().xrSession(), &swapchainCreateInfo, &mSwapchainDepth);
if (!XR_SUCCEEDED(res))
throw std::runtime_error(XrResultString(res));
}
CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchainDepth, 0, &imageCount, nullptr));
mSwapchainDepthBuffers.resize(imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR });
CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchainDepth, imageCount, &imageCount, reinterpret_cast<XrSwapchainImageBaseHeader*>(mSwapchainDepthBuffers.data())));
mSubImageDepth.swapchain = mSwapchainDepth;
mSubImageDepth.imageRect.offset = { 0, 0 };
mSubImageDepth.imageRect.extent = { mWidth, mHeight };
}
for (unsigned i = 0; i < imageCount; i++)
{
uint32_t colorBuffer = mSwapchainColorBuffers[i].image;
uint32_t depthBuffer = mHaveDepthSwapchain ? mSwapchainDepthBuffers[i].image : 0;
mRenderBuffers.emplace_back(new VRFramebuffer(state, mWidth, mHeight, mSamples, colorBuffer, depthBuffer));
}
}
OpenXRSwapchainImpl::~OpenXRSwapchainImpl()
@ -167,20 +189,23 @@ namespace MWVR {
{
auto xr = Environment::get().getManager();
XrSwapchainImageAcquireInfo acquireInfo{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO };
XrSwapchainImageWaitInfo waitInfo{ XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO };
waitInfo.timeout = XR_INFINITE_DURATION;
// I am trusting that the openxr runtime won't diverge these indices so long as these are always called together.
// 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(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));
xr->xrResourceAcquired();
CHECK_XRCMD(xrWaitSwapchainImage(mSwapchainDepth, &waitInfo));
xr->xrResourceAcquired();
if (mHaveDepthSwapchain)
{
uint32_t depthIndex = 0;
CHECK_XRCMD(xrAcquireSwapchainImage(mSwapchainDepth, &acquireInfo, &depthIndex));
if (depthIndex != mAcquiredImageIndex)
Log(Debug::Warning) << "Depth and color indices diverged";
CHECK_XRCMD(xrWaitSwapchainImage(mSwapchainDepth, &waitInfo));
xr->xrResourceAcquired();
}
mIsAcquired = true;
}
@ -193,8 +218,11 @@ namespace MWVR {
XrSwapchainImageReleaseInfo releaseInfo{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchain, &releaseInfo));
xr->xrResourceReleased();
CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchainDepth, &releaseInfo));
xr->xrResourceReleased();
if (mHaveDepthSwapchain)
{
CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchainDepth, &releaseInfo));
xr->xrResourceReleased();
}
}
void OpenXRSwapchainImpl::checkAcquired() const
{

View file

@ -51,6 +51,7 @@ namespace MWVR
int32_t mSamples = -1;
int64_t mSwapchainColorFormat = -1;
int64_t mSwapchainDepthFormat = -1;
bool mHaveDepthSwapchain = false;
uint32_t mFBO = 0;
std::vector<std::unique_ptr<VRFramebuffer> > mRenderBuffers{};
int mRenderBuffer{ 0 };

View file

@ -148,32 +148,13 @@ namespace MWVR
layerStack[(int)Side::LEFT_SIDE].fov = frameMeta->mPredictedPoses.view[(int)Side::LEFT_SIDE].fov;
layerStack[(int)Side::RIGHT_SIDE].fov = frameMeta->mPredictedPoses.view[(int)Side::RIGHT_SIDE].fov;
Log(Debug::Debug) << frameMeta->mFrameNo << ": EndFrame " << std::this_thread::get_id();
xr->endFrame(frameMeta->mXrPredictedDisplayTime, 1, layerStack);
xr->endFrame(frameMeta->mFrameInfo, 1, layerStack);
xr->xrResourceReleased();
}
{
std::unique_lock<std::mutex> lock(mMutex);
// Some of these values are useless until the prediction time bug is resolved by oculus.
auto now = std::chrono::steady_clock::now();
mLastFrameInterval = std::chrono::duration_cast<std::chrono::nanoseconds>(now - mLastRenderedFrameTimestamp);
mLastRenderedFrameTimestamp = now;
mLastRenderedFrame = getFrame(FramePhase::Swap)->mFrameNo;
mLastPredictedDisplayTime = getFrame(FramePhase::Swap)->mXrPredictedDisplayTime;
mLastPredictedDisplayPeriod = xr->getLastPredictedDisplayPeriod();;
// Using this to track framerate over the course of gameplay, rather than just seeing the instantaneous
auto seconds = std::chrono::duration_cast<std::chrono::duration<double>>(now - mStart).count();
static int sBaseFrames = 0;
if (seconds > 10.f)
{
Log(Debug::Verbose) << "Fps: " << (static_cast<double>(mLastRenderedFrame - sBaseFrames) / seconds);
mStart = now;
sBaseFrames = mLastRenderedFrame;
}
mLastRenderedFrame = frameMeta->mFrameNo;
getFrame(FramePhase::Swap) = nullptr;
}
mCondition.notify_one();
@ -183,8 +164,11 @@ namespace MWVR
{
Log(Debug::Debug) << "beginPhase(" << ((int)phase) << ") " << std::this_thread::get_id();
if (getFrame(phase))
auto& frame = getFrame(phase);
if (frame)
{
// Happens once during startup but can be ignored that time.
// TODO: This issue would be cleaned up if beginPhase(Update) was called at a more appropriate location.
Log(Debug::Warning) << "advanceFramePhase called with a frame alreay in the target phase";
return;
}
@ -200,44 +184,24 @@ namespace MWVR
FramePhase previousPhase = static_cast<FramePhase>((int)phase - 1);
if (!getFrame(previousPhase))
throw std::logic_error("beginPhase called without a frame");
getFrame(phase) = std::move(getFrame(previousPhase));
frame = std::move(getFrame(previousPhase));
}
// TODO: Invokation could depend on earliest actual render rather than necessarily any specific phase.
// For example, shadows do some draw calls during cull an as such phase should be "Cull" or earlier with shadows enabled.
// But may be "Draw" without shadows.
if (phase == mXrSyncPhase && getFrame(phase)->mShouldRender)
{
if (getFrame(phase)->mXrWaitThread.joinable())
getFrame(phase)->mXrWaitThread.join();
getFrame(phase)->mXrPredictedDisplayTime = doFrameSync();
}
}
long long VRSession::doFrameSync()
{
auto begin = std::chrono::steady_clock::now();
if (phase == mXrSyncPhase && frame->mShouldRender)
{
// We may reach this point before xrEndFrame of the previous frame
// Must wait or openxr will interpret another call to xrBeginFrame() as skipping a frame
std::unique_lock<std::mutex> lock(mMutex);
while (mLastRenderedFrame != mFrames - 1)
{
mCondition.wait(lock);
}
if (frame->mShouldRender)
{
Environment::get().getManager()->beginFrame();
}
}
auto condEnd = std::chrono::steady_clock::now();
auto* xr = Environment::get().getManager();
Log(Debug::Debug) << mFrames << ": WaitFrame " << std::this_thread::get_id();
auto predictedDisplayTime = xr->getLastPredictedDisplayTime();
Log(Debug::Debug) << mFrames << ": BeginFrame " << std::this_thread::get_id();
xr->beginFrame();
auto xrSyncEnd = std::chrono::steady_clock::now();
auto condTime = std::chrono::duration_cast<std::chrono::milliseconds>(condEnd - begin);
auto xrSyncTime = std::chrono::duration_cast<std::chrono::milliseconds>(xrSyncEnd - condEnd);
Log(Debug::Debug) << "condTime: " << condTime.count() << ", xrSyncTime: " << xrSyncTime.count();
return predictedDisplayTime;
}
std::unique_ptr<VRSession::VRFrameMeta>& VRSession::getFrame(FramePhase phase)
@ -249,69 +213,44 @@ namespace MWVR
void VRSession::prepareFrame()
{
std::unique_lock<std::mutex> lock(mMutex);
mFrames++;
auto* xr = Environment::get().getManager();
xr->handleEvents();
auto& frame = getFrame(FramePhase::Update);
frame.reset(new VRFrameMeta);
auto epochTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
auto predictedDisplayTime = mLastPredictedDisplayTime;
auto predictedDisplayPeriod = mLastPredictedDisplayPeriod;
double intervalsf = static_cast<double>(mLastFrameInterval.count()) / static_cast<double>(predictedDisplayPeriod);
int intervals = std::max((int)std::roundf(intervalsf), 1);
if (predictedDisplayTime == 0)
predictedDisplayTime = epochTime;
//////////////////////// OCULUS BUG
//////////////////// Oculus will suddenly start increasing their predicted display time by precisely 1 second per frame
//////////////////// regardless of real time passed, causing predictions to go crazy due to the time difference.
//////////////////// Therefore, for the time being, i ignore oculus' predicted display time altogether.
if (mUseSteadyClock)
frame->mFrameNo = mFrames;
frame->mShouldRender = xr->xrSessionCanRender();
if (frame->mShouldRender)
{
predictedDisplayTime = epochTime + intervals * predictedDisplayPeriod;
}
else
{
predictedDisplayTime = predictedDisplayTime + intervals * (mFrames - mLastRenderedFrame) * predictedDisplayPeriod;
frame->mFrameInfo = xr->waitFrame();
xr->xrResourceAcquired();
}
frame->mPredictedDisplayTime = frame->mFrameInfo.runtimePredictedDisplayTime;
PoseSet predictedPoses{};
xr->enablePredictions();
predictedPoses.head = xr->getPredictedHeadPose(predictedDisplayTime, ReferenceSpace::STAGE) * mPlayerScale;
auto hmdViews = xr->getPredictedViews(predictedDisplayTime, ReferenceSpace::VIEW);
predictedPoses.head = xr->getPredictedHeadPose(frame->mPredictedDisplayTime, ReferenceSpace::STAGE) * mPlayerScale;
auto hmdViews = xr->getPredictedViews(frame->mPredictedDisplayTime, ReferenceSpace::VIEW);
predictedPoses.view[(int)Side::LEFT_SIDE].pose = hmdViews[(int)Side::LEFT_SIDE].pose * mPlayerScale;
predictedPoses.view[(int)Side::RIGHT_SIDE].pose = hmdViews[(int)Side::RIGHT_SIDE].pose * mPlayerScale;
predictedPoses.view[(int)Side::LEFT_SIDE].fov = hmdViews[(int)Side::LEFT_SIDE].fov;
predictedPoses.view[(int)Side::RIGHT_SIDE].fov = hmdViews[(int)Side::RIGHT_SIDE].fov;
auto stageViews = xr->getPredictedViews(predictedDisplayTime, ReferenceSpace::STAGE);
auto stageViews = xr->getPredictedViews(frame->mPredictedDisplayTime, ReferenceSpace::STAGE);
predictedPoses.eye[(int)Side::LEFT_SIDE] = stageViews[(int)Side::LEFT_SIDE].pose * mPlayerScale;
predictedPoses.eye[(int)Side::RIGHT_SIDE] = stageViews[(int)Side::RIGHT_SIDE].pose * mPlayerScale;
auto* input = Environment::get().getInputManager();
if (input)
{
predictedPoses.hands[(int)Side::LEFT_SIDE] = input->getLimbPose(predictedDisplayTime, TrackedLimb::LEFT_HAND) * mPlayerScale;
predictedPoses.hands[(int)Side::RIGHT_SIDE] = input->getLimbPose(predictedDisplayTime, TrackedLimb::RIGHT_HAND) * mPlayerScale;
predictedPoses.hands[(int)Side::LEFT_SIDE] = input->getLimbPose(frame->mPredictedDisplayTime, TrackedLimb::LEFT_HAND) * mPlayerScale;
predictedPoses.hands[(int)Side::RIGHT_SIDE] = input->getLimbPose(frame->mPredictedDisplayTime, TrackedLimb::RIGHT_HAND) * mPlayerScale;
}
xr->disablePredictions();
auto& frame = getFrame(FramePhase::Update);
frame.reset(new VRFrameMeta);
frame->mPredictedDisplayTime = predictedDisplayTime;
frame->mFrameNo = mFrames;
frame->mPredictedPoses = predictedPoses;
frame->mShouldRender = xr->frameShouldRender();
if (frame->mShouldRender)
{
// XrWaitThread is best invoked immediately to help the runtime make more accurate predictions
// but it forces a wait which is cancer for performance so delegate it to another thread
// and join before rendering.
frame->mXrWaitThread = std::thread{ [=] {xr->waitFrame(); } };
xr->xrResourceAcquired();
}
}
const PoseSet& VRSession::predictedPoses(FramePhase phase)

View file

@ -45,10 +45,9 @@ namespace MWVR
{
long long mFrameNo{ 0 };
long long mPredictedDisplayTime{ 0 };
long long mXrPredictedDisplayTime{ 0 };
PoseSet mPredictedPoses{};
bool mShouldRender{ false };
std::thread mXrWaitThread{};
FrameInfo mFrameInfo{};
};
public:
@ -62,9 +61,6 @@ namespace MWVR
//! Starts a new frame
void prepareFrame();
//! Synchronize with openxr
long long doFrameSync();
//! Angles to be used for overriding movement direction
void movementAngles(float& yaw, float& pitch);

View file

@ -107,6 +107,13 @@ namespace MWVR
uint32_t maxSamples = -1;
};
struct FrameInfo
{
long long runtimePredictedDisplayTime;
long long runtimePredictedDisplayPeriod;
bool runtimeRequestsRender;
};
// Serialization methods for VR types.
std::ostream& operator <<(std::ostream& os, const Pose& pose);
std::ostream& operator <<(std::ostream& os, const FieldOfView& fov);

View file

@ -910,7 +910,7 @@ real height = 1.85
mirror texture = true
# Openmw will sync with openxr at the beginning of this phase in the rendering pipeline. From early to late in the pipeline the options are update, cull, draw, and swap in that order. If you experience visual glitches such as frames jittering across your vision, try changing this to an earlier phase. Earlier phases reduce glitches, at the cost of performance, while later phases may significantly boost framerate at the cost of introducing glitches. With shadows enabled, i recommend 'cull'. With shadows disabled, 'draw' may be a playable choice. 'swap' will almost certainly tear your eyes out.
openxr sync phase = cull
openxr sync phase = draw
# Determines how quickly you have to move your hand minimum, in meters/second, to perform an attack
realistic combat minimum swing velocity = 1.0
@ -926,4 +926,10 @@ flip mirror texture order = false
# Work around for some preview openxr runtimes whose display time predictions do not work
# Use this if tracking seems crazy.
use steady clock = false
use steady clock = false
# Log all calls to openxr, not just ones that fail. Useful for debugging.
log all openxr calls = false
# If false, openmw will quit with an exception if an openxr call fails for any reason
continue on errors = true