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:
parent
ef017285fb
commit
c136ae682e
10 changed files with 226 additions and 194 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue