Merge remote-tracking branch 'remotes/origin/directx_swapchains' into openxr_vr_geometryshader_feature_branch

pull/615/head
madsbuvi 4 years ago
commit 11a3961d65

@ -247,7 +247,7 @@ if(BUILD_OPENMW_VR)
vrengine.cpp
)
add_openmw_dir (mwvr
openxraction openxractionset openxrdebug openxrinput openxrmanager openxrmanagerimpl openxrswapchain openxrswapchainimpl
openxraction openxractionset openxrdebug openxrinput openxrmanager openxrmanagerimpl openxrplatform openxrswapchain openxrswapchainimage openxrswapchainimpl
realisticcombat
vranimation vrcamera vrenvironment vrframebuffer vrgui vrinputmanager vrinput vrmetamenu vrsession vrshadow vrtypes vrview vrviewer vrvirtualkeyboard
)
@ -280,7 +280,7 @@ if(BUILD_OPENMW_VR)
# Preprocessor variable used to control code paths to vr code
if (WIN32)
target_compile_options(openmw_vr PUBLIC -DUSE_OPENXR -DXR_USE_GRAPHICS_API_OPENGL -DXR_USE_PLATFORM_WIN32)
target_compile_options(openmw_vr PUBLIC -DUSE_OPENXR -DXR_USE_GRAPHICS_API_OPENGL -DXR_USE_GRAPHICS_API_D3D11 -DXR_USE_PLATFORM_WIN32)
elseif(UNIX)
target_compile_options(openmw_vr PUBLIC -DUSE_OPENXR -DXR_USE_GRAPHICS_API_OPENGL -DXR_USE_PLATFORM_XLIB)
find_package(X11 REQUIRED)

@ -16,7 +16,6 @@
#error Unsupported platform
#endif
#include <openxr/openxr_platform.h>
#include <openxr/openxr_platform_defines.h>
#include <openxr/openxr_reflection.h>

@ -77,13 +77,13 @@ namespace MWVR
{
gc->makeCurrent();
try {
mPrivate = std::make_shared<OpenXRManagerImpl>();
mPrivate = std::make_shared<OpenXRManagerImpl>(gc);
}
catch (std::exception& e)
{
Log(Debug::Error) << "Exception thrown by OpenXR: " << e.what();
osg::ref_ptr<osg::State> state = gc->getState();
std::string error = std::string("Exception thrown while initializing OpenXR: ") + e.what();
Log(Debug::Error) << error;
throw std::runtime_error(error);
}
}
}
@ -138,6 +138,16 @@ namespace MWVR
return impl().xrExtensionIsEnabled(extensionName);
}
int64_t OpenXRManager::selectColorFormat()
{
return impl().selectColorFormat();
}
int64_t OpenXRManager::selectDepthFormat()
{
return impl().selectDepthFormat();
}
void
OpenXRManager::CleanupOperation::operator()(
osg::GraphicsContext* gc)

@ -98,6 +98,14 @@ namespace MWVR
//! Check whether a given openxr extension is enabled or not
bool xrExtensionIsEnabled(const char* extensionName) const;
//! Selects a color format from among formats offered by the runtime
//! Returns 0 if no format is supported.
int64_t selectColorFormat();
//! Selects a depth format from among formats offered by the runtime
//! Returns 0 if no format is supported.
int64_t selectDepthFormat();
OpenXRManagerImpl& impl() { return *mPrivate; }
const OpenXRManagerImpl& impl() const { return *mPrivate; }

@ -1,5 +1,6 @@
#include "openxrmanagerimpl.hpp"
#include "openxrdebug.hpp"
#include "openxrplatform.hpp"
#include "openxrswapchain.hpp"
#include "openxrswapchainimpl.hpp"
#include "vrenvironment.hpp"
@ -18,22 +19,6 @@
#include "../mwworld/player.hpp"
#include "../mwworld/esmstore.hpp"
// The OpenXR SDK's platform headers assume we've included platform headers
#ifdef _WIN32
#include <Windows.h>
#include <objbase.h>
#elif __linux__
#include <X11/Xlib.h>
#include <GL/glx.h>
#undef None
#else
#error Unsupported platform
#endif
#include <openxr/openxr_platform.h>
#include <openxr/openxr_platform_defines.h>
#include <openxr/openxr_reflection.h>
#include <osg/Camera>
@ -61,126 +46,41 @@ MAKE_TO_STRING_FUNC(XrStructureType);
namespace MWVR
{
OpenXRManagerImpl::OpenXRManagerImpl()
OpenXRManagerImpl::OpenXRManagerImpl(osg::GraphicsContext* gc)
: mPlatform(gc)
{
logLayersAndExtensions();
setupExtensionsAndLayers();
std::vector<const char*> extensions;
for (auto& extension : mEnabledExtensions)
extensions.push_back(extension.c_str());
{ // Create Instance
XrInstanceCreateInfo createInfo{ XR_TYPE_INSTANCE_CREATE_INFO };
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));
assert(mInstance);
}
mInstance = mPlatform.createXrInstance("openmw_vr");
setupDebugMessenger();
// Layer depth is enabled, cache the invariant values
if (xrExtensionIsEnabled(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME))
{
// 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");
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;
}
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;
CHECK_XRCMD(xrGetSystem(mInstance, &systemInfo, &mSystemId));
assert(mSystemId);
}
{ // Initialize OpenGL device
// Despite its name, xrGetOpenGLGraphicsRequirementsKHR is a required function call that sets up an opengl instance.
PFN_xrGetOpenGLGraphicsRequirementsKHR p_getRequirements = nullptr;
xrGetInstanceProcAddr(mInstance, "xrGetOpenGLGraphicsRequirementsKHR", reinterpret_cast<PFN_xrVoidFunction*>(&p_getRequirements));
XrGraphicsRequirementsOpenGLKHR requirements{ XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR };
CHECK_XRCMD(p_getRequirements(mInstance, mSystemId, &requirements));
const XrVersion desiredApiVersion = XR_MAKE_VERSION(4, 6, 0);
if (requirements.minApiVersionSupported > desiredApiVersion) {
std::cout << "Runtime does not support desired Graphics API and/or version" << std::endl;
}
}
// Get system ID
XrSystemGetInfo systemInfo{ XR_TYPE_SYSTEM_GET_INFO };
systemInfo.formFactor = mFormFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
CHECK_XRCMD(xrGetSystem(mInstance, &systemInfo, &mSystemId));
assert(mSystemId);
#ifdef _WIN32
{ // Create Session
auto DC = wglGetCurrentDC();
auto GLRC = wglGetCurrentContext();
auto XRGLRC = wglCreateContext(DC);
wglShareLists(GLRC, XRGLRC);
XrGraphicsBindingOpenGLWin32KHR graphicsBindings;
graphicsBindings.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
graphicsBindings.next = nullptr;
graphicsBindings.hDC = DC;
graphicsBindings.hGLRC = XRGLRC;
if (!graphicsBindings.hDC)
Log(Debug::Warning) << "Missing DC";
XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO };
createInfo.next = &graphicsBindings;
createInfo.systemId = mSystemId;
CHECK_XRCMD(xrCreateSession(mInstance, &createInfo, &mSession));
assert(mSession);
}
#elif __linux__
{ // Create Session
Display* xDisplay = XOpenDisplay(NULL);
GLXContext glxContext = glXGetCurrentContext();
GLXDrawable glxDrawable = glXGetCurrentDrawable();
// TODO: runtimes don't actually care (yet)
GLXFBConfig glxFBConfig = 0;
uint32_t visualid = 0;
XrGraphicsBindingOpenGLXlibKHR graphicsBindings;
graphicsBindings.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
graphicsBindings.next = nullptr;
graphicsBindings.xDisplay = xDisplay;
graphicsBindings.glxContext = glxContext;
graphicsBindings.glxDrawable = glxDrawable;
graphicsBindings.glxFBConfig = glxFBConfig;
graphicsBindings.visualid = visualid;
if (!graphicsBindings.glxContext)
Log(Debug::Warning) << "Missing glxContext";
if (!graphicsBindings.glxDrawable)
Log(Debug::Warning) << "Missing glxDrawable";
XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO };
createInfo.next = &graphicsBindings;
createInfo.systemId = mSystemId;
CHECK_XRCMD(xrCreateSession(mInstance, &createInfo, &mSession));
assert(mSession);
}
#endif
// Create session
mSession = mPlatform.createXrSession(mInstance, mSystemId);
LogInstanceInfo();
LogReferenceSpaces();
LogSwapchainFormats();
{ // Set up reference space
XrReferenceSpaceCreateInfo createInfo{ XR_TYPE_REFERENCE_SPACE_CREATE_INFO };
@ -218,42 +118,6 @@ namespace MWVR
}
}
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 Debug");
sContinueOnErrors = Settings::Manager::getBool("continue on errors", "VR Debug");
}
if (XR_FAILED(res)) {
std::stringstream ss;
#ifdef _WIN32
ss << sourceLocation << ": OpenXR[Error: " << to_string(res) << "][Thread: " << std::this_thread::get_id() << "]: " << originator;
#elif __linux__
ss << sourceLocation << ": OpenXR[Error: " << to_string(res) << "][Thread: " << std::this_thread::get_id() << "]: " << originator;
#endif
Log(Debug::Error) << ss.str();
if (res == XR_ERROR_TIME_INVALID)
Log(Debug::Error) << "Breakpoint";
if (!sContinueOnErrors)
throw std::runtime_error(ss.str().c_str());
}
else if (res != XR_SUCCESS || sLogAllXrCalls)
{
#ifdef _WIN32
Log(Debug::Verbose) << sourceLocation << ": OpenXR[" << to_string(res) << "][" << std::this_thread::get_id() << "]: " << originator;
#elif __linux__
Log(Debug::Verbose) << sourceLocation << ": OpenXR[" << to_string(res) << "][" << std::this_thread::get_id() << "]: " << originator;
#endif
}
return res;
}
std::string XrResultString(XrResult res)
{
return to_string(res);
@ -264,82 +128,10 @@ namespace MWVR
}
static std::vector<std::string> enumerateExtensions(const char* layerName, bool log = false, int logIndent = 2)
{
uint32_t extensionCount = 0;
std::vector<XrExtensionProperties> availableExtensions;
xrEnumerateInstanceExtensionProperties(layerName, 0, &extensionCount, nullptr);
availableExtensions.resize(extensionCount, XrExtensionProperties{ XR_TYPE_EXTENSION_PROPERTIES });
xrEnumerateInstanceExtensionProperties(layerName, availableExtensions.size(), &extensionCount, availableExtensions.data());
std::vector<std::string> extensionNames;
const std::string indentStr(logIndent, ' ');
for (auto& extension : availableExtensions)
{
extensionNames.push_back(extension.extensionName);
if (log)
Log(Debug::Verbose) << indentStr << "Name=" << extension.extensionName << " SpecVersion=" << extension.extensionVersion;
}
return extensionNames;
}
#if !XR_KHR_composition_layer_depth \
|| !XR_KHR_opengl_enable \
|| !XR_EXT_hp_mixed_reality_controller \
|| !XR_EXT_debug_utils \
|| !XR_HTC_vive_cosmos_controller_interaction \
|| !XR_HUAWEI_controller_interaction
#error "OpenXR extensions missing. Please upgrade your copy of the OpenXR SDK to 1.0.13 minimum"
#endif
void OpenXRManagerImpl::setupExtensionsAndLayers()
{
std::vector<const char*> requiredExtensions = {
XR_KHR_OPENGL_ENABLE_EXTENSION_NAME,
};
std::vector<const char*> optionalExtensions = {
XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME,
XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME,
XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME,
XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME
};
if (Settings::Manager::getBool("enable XR_EXT_debug_utils", "VR Debug"))
optionalExtensions.emplace_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME);
for (auto& extension : enumerateExtensions(nullptr))
mAvailableExtensions.insert(extension);
Log(Debug::Verbose) << "Using extensions:";
for (auto requiredExtension : requiredExtensions)
enableExtension(requiredExtension, false);
for (auto optionalExtension : optionalExtensions)
enableExtension(optionalExtension, true);
}
void OpenXRManagerImpl::enableExtension(const std::string& extension, bool optional)
{
if (mAvailableExtensions.count(extension) > 0)
{
Log(Debug::Verbose) << " " << extension << ": enabled";
mEnabledExtensions.insert(extension);
}
else
{
Log(Debug::Verbose) << " " << extension << ": disabled (not supported)";
if (!optional)
{
throw std::runtime_error(std::string("Required OpenXR extension ") + extension + " not supported by the runtime");
}
}
}
static XrBool32 xrDebugCallback(
XrDebugUtilsMessageSeverityFlagsEXT messageSeverity,
XrDebugUtilsMessageTypeFlagsEXT messageType,
@ -419,28 +211,6 @@ namespace MWVR
}
}
void
OpenXRManagerImpl::logLayersAndExtensions() {
// Log layers and any of their extensions.
uint32_t layerCount;
CHECK_XRCMD(xrEnumerateApiLayerProperties(0, &layerCount, nullptr));
std::vector<XrApiLayerProperties> layers(layerCount, XrApiLayerProperties{ XR_TYPE_API_LAYER_PROPERTIES });
CHECK_XRCMD(xrEnumerateApiLayerProperties((uint32_t)layers.size(), &layerCount, layers.data()));
Log(Debug::Verbose) << "Available Extensions: ";
enumerateExtensions(nullptr, true, 2);
Log(Debug::Verbose) << "Available Layers: ";
if (layers.size() == 0)
{
Log(Debug::Verbose) << " No layers available";
}
for (const XrApiLayerProperties& layer : layers) {
Log(Debug::Verbose) << " Name=" << layer.layerName << " SpecVersion=" << layer.layerVersion;
enumerateExtensions(layer.layerName, true, 4);
}
}
void
OpenXRManagerImpl::LogInstanceInfo() {
@ -465,24 +235,6 @@ namespace MWVR
Log(Debug::Verbose) << ss.str();
}
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::dec << std::endl;
}
Log(Debug::Verbose) << ss.str();
}
FrameInfo
OpenXRManagerImpl::waitFrame()
{
@ -501,15 +253,6 @@ namespace MWVR
return frameInfo;
}
static void clearGlErrors()
{
auto error = glGetError();
while (error != GL_NO_ERROR)
{
Log(Debug::Warning) << "glGetError: " << std::dec << error << " (0x" << std::hex << error << ")" << std::dec;
}
}
void
OpenXRManagerImpl::beginFrame()
{
@ -859,7 +602,7 @@ namespace MWVR
bool OpenXRManagerImpl::xrExtensionIsEnabled(const char* extensionName) const
{
return mEnabledExtensions.count(extensionName) != 0;
return mPlatform.extensionEnabled(extensionName);
}
void OpenXRManagerImpl::xrResourceAcquired()
@ -891,6 +634,18 @@ namespace MWVR
return function;
}
int64_t OpenXRManagerImpl::selectColorFormat()
{
// Find supported color swapchain format.
return mPlatform.selectColorFormat();
}
int64_t OpenXRManagerImpl::selectDepthFormat()
{
// Find supported depth swapchain format.
return mPlatform.selectDepthFormat();
}
void OpenXRManagerImpl::enablePredictions()
{
mPredictionsEnabled = true;

@ -2,6 +2,7 @@
#define OPENXR_MANAGER_IMPL_HPP
#include "openxrmanager.hpp"
#include "openxrplatform.hpp"
#include "../mwinput/inputmanagerimp.hpp"
#include <components/debug/debuglog.hpp>
@ -19,16 +20,6 @@
namespace MWVR
{
// Error management macros and functions. Should be used on every openxr call.
#define CHK_STRINGIFY(x) #x
#define TOSTRING(x) CHK_STRINGIFY(x)
#define FILE_AND_LINE __FILE__ ":" TOSTRING(__LINE__)
#define CHECK_XRCMD(cmd) CheckXrResult(cmd, #cmd, FILE_AND_LINE)
#define CHECK_XRRESULT(res, cmdStr) CheckXrResult(res, cmdStr, FILE_AND_LINE)
XrResult CheckXrResult(XrResult res, const char* originator = nullptr, const char* sourceLocation = nullptr);
std::string XrResultString(XrResult res);
/// Conversion methods from openxr types to osg/mwvr types. Includes managing the differing conventions.
MWVR::Pose fromXR(XrPosef pose);
MWVR::FieldOfView fromXR(XrFovf fov);
@ -45,9 +36,10 @@ namespace MWVR
XrSwapchainSubImage toXR(MWVR::SubImage, bool depthImage);
/// \brief Implementation of OpenXRManager
struct OpenXRManagerImpl
class OpenXRManagerImpl
{
OpenXRManagerImpl(void);
public:
OpenXRManagerImpl(osg::GraphicsContext* gc);
~OpenXRManagerImpl(void);
FrameInfo waitFrame();
@ -72,15 +64,15 @@ namespace MWVR
void xrResourceReleased();
void xrUpdateNames();
PFN_xrVoidFunction xrGetFunction(const std::string& name);
int64_t selectColorFormat();
int64_t selectDepthFormat();
OpenXRPlatform& platform() { return mPlatform; };
protected:
void setupExtensionsAndLayers();
void enableExtension(const std::string& extension, bool optional);
void setupDebugMessenger(void);
void logLayersAndExtensions();
void LogInstanceInfo();
void LogReferenceSpaces();
void LogSwapchainFormats();
bool xrNextEvent(XrEventDataBuffer& eventBuffer);
void xrQueueEvents();
const XrEventDataBaseHeader* nextEvent();
@ -108,6 +100,8 @@ namespace MWVR
XrSessionState mSessionState = XR_SESSION_STATE_UNKNOWN;
XrDebugUtilsMessengerEXT mDebugMessenger{ nullptr };
OpenXRPlatform mPlatform;
bool mXrSessionShouldStop = false;
bool mAppShouldSyncFrameLoop = false;
bool mAppShouldRender = false;
@ -115,8 +109,6 @@ namespace MWVR
uint32_t mAcquiredResources = 0;
std::mutex mMutex{};
std::set<std::string> mAvailableExtensions;
std::set<std::string> mEnabledExtensions;
std::queue<XrEventDataBuffer> mEventQueue;
std::array<XrCompositionLayerDepthInfoKHR, 2> mLayerDepth;

@ -0,0 +1,656 @@
#include "openxrswapchainimage.hpp"
#include "openxrmanagerimpl.hpp"
#include "openxrplatform.hpp"
#include "vrenvironment.hpp"
// The OpenXR SDK's platform headers assume we've included platform headers
#ifdef _WIN32
#include <Windows.h>
#include <objbase.h>
#ifdef XR_USE_GRAPHICS_API_D3D11
#include <d3d11_1.h>
#endif
#elif __linux__
#include <X11/Xlib.h>
#include <GL/glx.h>
#undef None
#else
#error Unsupported platform
#endif
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#include <openxr/openxr_platform_defines.h>
#include <openxr/openxr_reflection.h>
#include <stdexcept>
#include <deque>
#include <cassert>
namespace MWVR
{
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 Debug");
sContinueOnErrors = Settings::Manager::getBool("continue on errors", "VR Debug");
}
auto resultString = XrResultString(res);
if (XR_FAILED(res)) {
std::stringstream ss;
#ifdef _WIN32
ss << sourceLocation << ": OpenXR[Error: " << resultString << "][Thread: " << std::this_thread::get_id() << "]: " << originator;
#elif __linux__
ss << sourceLocation << ": OpenXR[Error: " << resultString << "][Thread: " << std::this_thread::get_id() << "]: " << originator;
#endif
Log(Debug::Error) << ss.str();
if (res == XR_ERROR_TIME_INVALID)
Log(Debug::Error) << "Breakpoint";
if (!sContinueOnErrors)
throw std::runtime_error(ss.str().c_str());
}
else if (res != XR_SUCCESS || sLogAllXrCalls)
{
#ifdef _WIN32
Log(Debug::Verbose) << sourceLocation << ": OpenXR[" << resultString << "][" << std::this_thread::get_id() << "]: " << originator;
#elif __linux__
Log(Debug::Verbose) << sourceLocation << ": OpenXR[" << resultString << "][" << std::this_thread::get_id() << "]: " << originator;
#endif
}
return res;
}
void OpenXRPlatform::enumerateExtensions(const char* layerName, int logIndent)
{
uint32_t extensionCount = 0;
std::vector<XrExtensionProperties> availableExtensions;
xrEnumerateInstanceExtensionProperties(layerName, 0, &extensionCount, nullptr);
availableExtensions.resize(extensionCount, XrExtensionProperties{ XR_TYPE_EXTENSION_PROPERTIES });
xrEnumerateInstanceExtensionProperties(layerName, availableExtensions.size(), &extensionCount, availableExtensions.data());
std::vector<std::string> extensionNames;
const std::string indentStr(logIndent, ' ');
for (auto& extension : availableExtensions)
{
if (layerName)
mAvailableLayerExtensions[layerName][extension.extensionName] = extension;
else
mAvailableExtensions[extension.extensionName] = extension;
Log(Debug::Verbose) << indentStr << "Name=" << extension.extensionName << " SpecVersion=" << extension.extensionVersion;
}
}
struct OpenXRPlatformPrivate
{
OpenXRPlatformPrivate(osg::GraphicsContext* gc);
~OpenXRPlatformPrivate();
#ifdef XR_USE_GRAPHICS_API_D3D11
typedef BOOL(WINAPI* P_wglDXSetResourceShareHandleNV)(void* dxObject, HANDLE shareHandle);
typedef HANDLE(WINAPI* P_wglDXOpenDeviceNV)(void* dxDevice);
typedef BOOL(WINAPI* P_wglDXCloseDeviceNV)(HANDLE hDevice);
typedef HANDLE(WINAPI* P_wglDXRegisterObjectNV)(HANDLE hDevice, void* dxObject,
GLuint name, GLenum type, GLenum access);
typedef BOOL(WINAPI* P_wglDXUnregisterObjectNV)(HANDLE hDevice, HANDLE hObject);
typedef BOOL(WINAPI* P_wglDXObjectAccessNV)(HANDLE hObject, GLenum access);
typedef BOOL(WINAPI* P_wglDXLockObjectsNV)(HANDLE hDevice, GLint count, HANDLE* hObjects);
typedef BOOL(WINAPI* P_wglDXUnlockObjectsNV)(HANDLE hDevice, GLint count, HANDLE* hObjects);
void initializeD3D11(XrGraphicsRequirementsD3D11KHR requirements)
{
mD3D11Dll = LoadLibrary("D3D11.dll");
if (!mD3D11Dll)
throw std::runtime_error("Current OpenXR runtime requires DirectX >= 11.0 but D3D11.dll was not found.");
pD3D11CreateDevice = reinterpret_cast<PFN_D3D11_CREATE_DEVICE>(GetProcAddress(mD3D11Dll, "D3D11CreateDevice"));
if (!pD3D11CreateDevice)
throw std::runtime_error("Symbol 'D3D11CreateDevice' not found in D3D11.dll");
// Create the device and device context objects
pD3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
0,
nullptr,
0,
D3D11_SDK_VERSION,
&mD3D11Device,
nullptr,
&mD3D11ImmediateContext);
mD3D11bindings.device = mD3D11Device;
//typedef HANDLE (WINAPI* P_wglDXOpenDeviceNV)(void* dxDevice);
//P_wglDXOpenDeviceNV wglDXOpenDeviceNV = reinterpret_cast<P_wglDXOpenDeviceNV>(wglGetProcAddress("wglDXOpenDeviceNV"));
//P_wglDXOpenDeviceNV wglDXOpenDeviceNV = reinterpret_cast<P_wglDXOpenDeviceNV>(wglGetProcAddress("wglDXOpenDeviceNV"));
#define LOAD_WGL(a) a = reinterpret_cast<decltype(a)>(wglGetProcAddress(#a)); if(!a) throw std::runtime_error("Extension WGL_NV_DX_interop2 required to run OpenMW VR via DirectX missing expected symbol '" #a "'.")
LOAD_WGL(wglDXSetResourceShareHandleNV);
LOAD_WGL(wglDXOpenDeviceNV);
LOAD_WGL(wglDXCloseDeviceNV);
LOAD_WGL(wglDXRegisterObjectNV);
LOAD_WGL(wglDXUnregisterObjectNV);
LOAD_WGL(wglDXObjectAccessNV);
LOAD_WGL(wglDXLockObjectsNV);
LOAD_WGL(wglDXUnlockObjectsNV);
#undef LOAD_WGL
wglDXDevice = wglDXOpenDeviceNV(mD3D11Device);
}
bool mWGL_NV_DX_interop2 = false;
XrGraphicsBindingD3D11KHR mD3D11bindings{ XR_TYPE_GRAPHICS_BINDING_D3D11_KHR };
ID3D11Device* mD3D11Device = nullptr;
ID3D11DeviceContext* mD3D11ImmediateContext = nullptr;
HMODULE mD3D11Dll = nullptr;
PFN_D3D11_CREATE_DEVICE pD3D11CreateDevice = nullptr;
P_wglDXSetResourceShareHandleNV wglDXSetResourceShareHandleNV = nullptr;
P_wglDXOpenDeviceNV wglDXOpenDeviceNV = nullptr;
P_wglDXCloseDeviceNV wglDXCloseDeviceNV = nullptr;
P_wglDXRegisterObjectNV wglDXRegisterObjectNV = nullptr;
P_wglDXUnregisterObjectNV wglDXUnregisterObjectNV = nullptr;
P_wglDXObjectAccessNV wglDXObjectAccessNV = nullptr;
P_wglDXLockObjectsNV wglDXLockObjectsNV = nullptr;
P_wglDXUnlockObjectsNV wglDXUnlockObjectsNV = nullptr;
HANDLE wglDXDevice = nullptr;
#endif
};
OpenXRPlatformPrivate::OpenXRPlatformPrivate(osg::GraphicsContext* gc)
{
#ifdef XR_USE_GRAPHICS_API_D3D11
typedef const char* (WINAPI* PFNWGLGETEXTENSIONSSTRINGARBPROC) (HDC hdc);
PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = 0;
wglGetExtensionsStringARB = reinterpret_cast<PFNWGLGETEXTENSIONSSTRINGARBPROC>(wglGetProcAddress("wglGetExtensionsStringARB"));
if (wglGetExtensionsStringARB)
{
std::string wglExtensions = wglGetExtensionsStringARB(wglGetCurrentDC());
Log(Debug::Verbose) << "WGL Extensions: " << wglExtensions;
mWGL_NV_DX_interop2 = wglExtensions.find("WGL_NV_DX_interop2") != std::string::npos;
}
else
Log(Debug::Verbose) << "Unable to query WGL extensions";
#endif
}
OpenXRPlatformPrivate::~OpenXRPlatformPrivate()
{
#ifdef XR_USE_GRAPHICS_API_D3D11
if (wglDXDevice)
wglDXCloseDeviceNV(wglDXDevice);
if (mD3D11ImmediateContext)
mD3D11ImmediateContext->Release();
if (mD3D11Device)
mD3D11Device->Release();
if (mD3D11Dll)
FreeLibrary(mD3D11Dll);
#endif
}
OpenXRPlatform::OpenXRPlatform(osg::GraphicsContext* gc)
: mPrivate(new OpenXRPlatformPrivate(gc))
{
// Enumerate layers and their extensions.
uint32_t layerCount;
CHECK_XRCMD(xrEnumerateApiLayerProperties(0, &layerCount, nullptr));
std::vector<XrApiLayerProperties> layers(layerCount, XrApiLayerProperties{ XR_TYPE_API_LAYER_PROPERTIES });
CHECK_XRCMD(xrEnumerateApiLayerProperties((uint32_t)layers.size(), &layerCount, layers.data()));
Log(Debug::Verbose) << "Available Extensions: ";
enumerateExtensions(nullptr, 2);
Log(Debug::Verbose) << "Available Layers: ";
if (layers.size() == 0)
{
Log(Debug::Verbose) << " No layers available";
}
for (const XrApiLayerProperties& layer : layers) {
Log(Debug::Verbose) << " Name=" << layer.layerName << " SpecVersion=" << layer.layerVersion;
mAvailableLayers[layer.layerName] = layer;
enumerateExtensions(layer.layerName, 4);
}
setupExtensions();
}
OpenXRPlatform::~OpenXRPlatform()
{
}
#if !XR_KHR_composition_layer_depth \
|| !XR_EXT_hp_mixed_reality_controller \
|| !XR_EXT_debug_utils \
|| !XR_HTC_vive_cosmos_controller_interaction \
|| !XR_HUAWEI_controller_interaction
#error "OpenXR extensions missing. Please upgrade your copy of the OpenXR SDK to 1.0.13 minimum"
#endif
void OpenXRPlatform::setupExtensions()
{
std::vector<const char*> optionalExtensions = {
XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME,
XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME,
XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME,
XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME
};
if (Settings::Manager::getBool("enable XR_EXT_debug_utils", "VR Debug"))
optionalExtensions.emplace_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME);
selectGraphicsAPIExtension();
Log(Debug::Verbose) << "Using extensions:";
auto* graphicsAPIExtension = graphicsAPIExtensionName();
if (!graphicsAPIExtension || !enableExtension(graphicsAPIExtension, true))
{
throw std::runtime_error("No graphics APIs supported by openmw are supported by the OpenXR runtime.");
}
for (auto optionalExtension : optionalExtensions)
enableExtension(optionalExtension, true);
}
bool OpenXRPlatform::selectDirectX()
{
#ifdef XR_USE_GRAPHICS_API_D3D11
if (mPrivate->mWGL_NV_DX_interop2)
{
if (mAvailableExtensions.count(XR_KHR_D3D11_ENABLE_EXTENSION_NAME))
{
mGraphicsAPIExtension = XR_KHR_D3D11_ENABLE_EXTENSION_NAME;
return true;
}
else
Log(Debug::Warning) << "Warning: Failed to select DirectX swapchains: OpenXR runtime does not support essential extension '" << XR_KHR_D3D11_ENABLE_EXTENSION_NAME << "'";
}
else
Log(Debug::Warning) << "Warning: Failed to select DirectX swapchains: Essential WGL extension 'WGL_NV_DX_interop2' not supported by the graphics driver.";
#endif
return false;
}
bool OpenXRPlatform::selectOpenGL()
{
#ifdef XR_USE_GRAPHICS_API_OPENGL
if (mAvailableExtensions.count(XR_KHR_OPENGL_ENABLE_EXTENSION_NAME))
{
mGraphicsAPIExtension = XR_KHR_OPENGL_ENABLE_EXTENSION_NAME;
return true;
}
else
Log(Debug::Warning) << "Warning: Failed to select OpenGL swapchains: OpenXR runtime does not support essential extension '" << XR_KHR_OPENGL_ENABLE_EXTENSION_NAME << "'";
#endif
return false;
}
#ifdef XR_USE_GRAPHICS_API_OPENGL
#if !XR_KHR_opengl_enable
#error "OpenXR extensions missing. Please upgrade your copy of the OpenXR SDK to 1.0.13 minimum"
#endif
#endif
#ifdef XR_USE_GRAPHICS_API_D3D11
#if !XR_KHR_D3D11_enable
#error "OpenXR extensions missing. Please upgrade your copy of the OpenXR SDK to 1.0.13 minimum"
#endif
#endif
const char* OpenXRPlatform::graphicsAPIExtensionName()
{
return mGraphicsAPIExtension;
}
void OpenXRPlatform::selectGraphicsAPIExtension()
{
bool preferDirectX = Settings::Manager::getBool("Prefer DirectX swapchains", "VR");
if (preferDirectX)
if (selectDirectX() || selectOpenGL())
return;
if (selectOpenGL() || selectDirectX())
return;
Log(Debug::Verbose) << "Error: No graphics API supported by OpenMW VR is supported by the OpenXR runtime.";
throw std::runtime_error("Error: No graphics API supported by OpenMW VR is supported by the OpenXR runtime.");
}
bool OpenXRPlatform::supportsExtension(const std::string& extensionName) const
{
return mAvailableExtensions.count(extensionName) > 0;
}
bool OpenXRPlatform::supportsExtension(const std::string& extensionName, uint32_t minimumVersion) const
{
auto it = mAvailableExtensions.find(extensionName);
return it != mAvailableExtensions.end() && it->second.extensionVersion > minimumVersion;
}
bool OpenXRPlatform::supportsLayer(const std::string& layerName) const
{
return mAvailableLayers.count(layerName) > 0;
}
bool OpenXRPlatform::supportsLayer(const std::string& layerName, uint32_t minimumVersion) const
{
auto it = mAvailableLayers.find(layerName);
return it != mAvailableLayers.end() && it->second.layerVersion > minimumVersion;
}
bool OpenXRPlatform::enableExtension(const std::string& extensionName, bool optional)
{
auto it = mAvailableExtensions.find(extensionName);
if (it != mAvailableExtensions.end())
{
Log(Debug::Verbose) << " " << extensionName << ": enabled";
mEnabledExtensions.push_back(it->second.extensionName);
return true;
}
else
{
Log(Debug::Verbose) << " " << extensionName << ": disabled (not supported)";
if (!optional)
{
throw std::runtime_error(std::string("Required OpenXR extension ") + extensionName + " not supported by the runtime");
}
return false;
}
}
bool OpenXRPlatform::enableExtension(const std::string& extensionName, bool optional, uint32_t minimumVersion)
{
auto it = mAvailableExtensions.find(extensionName);
if (it != mAvailableExtensions.end() && it->second.extensionVersion > minimumVersion)
{
Log(Debug::Verbose) << " " << extensionName << ": enabled";
mEnabledExtensions.push_back(it->second.extensionName);
return true;
}
else
{
Log(Debug::Verbose) << " " << extensionName << ": disabled (not supported)";
if (!optional)
{
throw std::runtime_error(std::string("Required OpenXR extension ") + extensionName + " not supported by the runtime");
}
return false;
}
}
bool OpenXRPlatform::extensionEnabled(const std::string& extensionName) const
{
for (auto* extension : mEnabledExtensions)
if (extension == extensionName)
return true;
return false;
}
XrInstance OpenXRPlatform::createXrInstance(const std::string& name)
{
XrInstance instance = XR_NULL_HANDLE;
XrInstanceCreateInfo createInfo{ XR_TYPE_INSTANCE_CREATE_INFO };
createInfo.next = nullptr;
createInfo.enabledExtensionCount = mEnabledExtensions.size();
createInfo.enabledExtensionNames = mEnabledExtensions.data();
strcpy(createInfo.applicationInfo.applicationName, "openmw_vr");
createInfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
CHECK_XRCMD(xrCreateInstance(&createInfo, &instance));
return instance;
}
XrSession OpenXRPlatform::createXrSession(XrInstance instance, XrSystemId systemId)
{
XrSession session = XR_NULL_HANDLE;
#ifdef _WIN32
std::string graphicsAPIExtension = graphicsAPIExtensionName();
if(graphicsAPIExtension == XR_KHR_OPENGL_ENABLE_EXTENSION_NAME)
{
// Get system requirements
PFN_xrGetOpenGLGraphicsRequirementsKHR p_getRequirements = nullptr;
xrGetInstanceProcAddr(instance, "xrGetOpenGLGraphicsRequirementsKHR", reinterpret_cast<PFN_xrVoidFunction*>(&p_getRequirements));
XrGraphicsRequirementsOpenGLKHR requirements{ XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR };
CHECK_XRCMD(p_getRequirements(instance, systemId, &requirements));
// TODO: Actually get system version
const XrVersion systemApiVersion = XR_MAKE_VERSION(4, 6, 0);
if (requirements.minApiVersionSupported > systemApiVersion) {
std::cout << "Runtime does not support desired Graphics API and/or version" << std::endl;
}
// Create Session
auto DC = wglGetCurrentDC();
auto GLRC = wglGetCurrentContext();
XrGraphicsBindingOpenGLWin32KHR graphicsBindings;
graphicsBindings.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
graphicsBindings.next = nullptr;
graphicsBindings.hDC = DC;
graphicsBindings.hGLRC = GLRC;
if (!graphicsBindings.hDC)
Log(Debug::Warning) << "Missing DC";
XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO };
createInfo.next = &graphicsBindings;
createInfo.systemId = systemId;
CHECK_XRCMD(xrCreateSession(instance, &createInfo, &session));
}
#ifdef XR_USE_GRAPHICS_API_D3D11
else if(graphicsAPIExtension == XR_KHR_D3D11_ENABLE_EXTENSION_NAME)
{
PFN_xrGetD3D11GraphicsRequirementsKHR p_getRequirements = nullptr;
xrGetInstanceProcAddr(instance, "xrGetD3D11GraphicsRequirementsKHR", reinterpret_cast<PFN_xrVoidFunction*>(&p_getRequirements));
XrGraphicsRequirementsD3D11KHR requirements{ XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR };
CHECK_XRCMD(p_getRequirements(instance, systemId, &requirements));
mPrivate->initializeD3D11(requirements);
XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO };
createInfo.next = &mPrivate->mD3D11bindings;
createInfo.systemId = systemId;
CHECK_XRCMD(xrCreateSession(instance, &createInfo, &session));
}
#endif
else
{
throw std::logic_error("Enum value not implemented");
}
#elif __linux__
{ // Create Session
Display* xDisplay = XOpenDisplay(NULL);
GLXContext glxContext = glXGetCurrentContext();
GLXDrawable glxDrawable = glXGetCurrentDrawable();
// TODO: runtimes don't actually care (yet)
GLXFBConfig glxFBConfig = 0;
uint32_t visualid = 0;
XrGraphicsBindingOpenGLXlibKHR graphicsBindings;
graphicsBindings.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
graphicsBindings.next = nullptr;
graphicsBindings.xDisplay = xDisplay;
graphicsBindings.glxContext = glxContext;
graphicsBindings.glxDrawable = glxDrawable;
graphicsBindings.glxFBConfig = glxFBConfig;
graphicsBindings.visualid = visualid;
if (!graphicsBindings.glxContext)
Log(Debug::Warning) << "Missing glxContext";
if (!graphicsBindings.glxDrawable)
Log(Debug::Warning) << "Missing glxDrawable";
XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO };
createInfo.next = &graphicsBindings;
createInfo.systemId = systemId;
CHECK_XRCMD(xrCreateSession(instance, &createInfo, &session));
}
#endif
assert(session);
uint32_t swapchainFormatCount;
CHECK_XRCMD(xrEnumerateSwapchainFormats(session, 0, &swapchainFormatCount, nullptr));
mSwapchainFormats.resize(swapchainFormatCount);
CHECK_XRCMD(xrEnumerateSwapchainFormats(session, (uint32_t)mSwapchainFormats.size(), &swapchainFormatCount, mSwapchainFormats.data()));
std::stringstream ss;
ss << "Available Swapchain formats: (" << swapchainFormatCount << ")" << std::endl;
for (auto format : mSwapchainFormats)
{
ss << " Enum=" << std::dec << format << " (0x=" << std::hex << format << ")" << std::dec << std::endl;
}
Log(Debug::Verbose) << ss.str();
return session;
}
/*
* For reference: These are the DXGI formats offered by WMR when using D3D11:
Enum=29 //(0x=1d) DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
Enum=91 //(0x=5b) DXGI_FORMAT_B8G8R8A8_UNORM_SRGB
Enum=28 //(0x=1c) DXGI_FORMAT_R8G8B8A8_UNORM
Enum=87 //(0x=57) DXGI_FORMAT_B8G8R8A8_UNORM
Enum=40 //(0x=28) DXGI_FORMAT_D32_FLOAT
Enum=20 //(0x=14) DXGI_FORMAT_D32_FLOAT_S8X24_UINT
Enum=45 //(0x=2d) DXGI_FORMAT_D24_UNORM_S8_UINT
Enum=55 //(0x=37) DXGI_FORMAT_D16_UNORM
* And these extra formats are offered by SteamVR:
0xa , // DXGI_FORMAT_R16G16B16A16_FLOAT
0x18 , // DXGI_FORMAT_R10G10B10A2_UNORM
*/
int64_t OpenXRPlatform::selectColorFormat()
{
std::string graphicsAPIExtension = graphicsAPIExtensionName();
if (graphicsAPIExtension == XR_KHR_OPENGL_ENABLE_EXTENSION_NAME)
{
// Find supported color swapchain format.
std::vector<int64_t> requestedColorSwapchainFormats = {
0x8058, // GL_RGBA8
0x8F97, // GL_RGBA8_SNORM
0x881A, // GL_RGBA16F
0x881B, // GL_RGB16F
// Offered by SteamVR but is broken: // 0x805B, // GL_RGBA16
0x8C3A, // GL_R11F_G11F_B10F
// Don't need srgb: 0x8C43, // GL_SRGB8_ALPHA8
// Don't need srgb: 0x8C41, // GL_SRGB8
};
return selectFormat(requestedColorSwapchainFormats);
}
#ifdef XR_USE_GRAPHICS_API_D3D11
else if (graphicsAPIExtension == XR_KHR_D3D11_ENABLE_EXTENSION_NAME)
{
// Find supported color swapchain format.
std::vector<int64_t> requestedColorSwapchainFormats = {
0x1c, // DXGI_FORMAT_R8G8B8A8_UNORM
0x57, // DXGI_FORMAT_B8G8R8A8_UNORM
0xa , // DXGI_FORMAT_R16G16B16A16_FLOAT
0x18, // DXGI_FORMAT_R10G10B10A2_UNORM
// Don't need srgb: 0x1d, // DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
// Don't need srgb: 0x5b, // DXGI_FORMAT_B8G8R8A8_UNORM_SRGB
};
return selectFormat(requestedColorSwapchainFormats);
}
#endif
else
{
throw std::logic_error("Enum value not implemented");
}
}
int64_t OpenXRPlatform::selectDepthFormat()
{
std::string graphicsAPIExtension = graphicsAPIExtensionName();
if (graphicsAPIExtension == XR_KHR_OPENGL_ENABLE_EXTENSION_NAME)
{
// Find supported depth swapchain format.
std::vector<int64_t> requestedDepthSwapchainFormats = {
0x88F0, // GL_DEPTH24_STENCIL8
0x8CAC, // GL_DEPTH_COMPONENT32F
0x81A7, // GL_DEPTH_COMPONENT32
0x8DAB, // GL_DEPTH_COMPONENT32F_NV
0x8CAD, // GL_DEPTH32_STENCIL8
0x81A6, // GL_DEPTH_COMPONENT24
// Need 32bit minimum: // 0x81A5, // GL_DEPTH_COMPONENT16
};
return selectFormat(requestedDepthSwapchainFormats);
}
#ifdef XR_USE_GRAPHICS_API_D3D11
else if (graphicsAPIExtension == XR_KHR_D3D11_ENABLE_EXTENSION_NAME)
{
// Find supported color swapchain format.
std::vector<int64_t> requestedDepthSwapchainFormats = {
0x2d, // DXGI_FORMAT_D24_UNORM_S8_UINT
0x14, // DXGI_FORMAT_D32_FLOAT_S8X24_UINT
0x28, // DXGI_FORMAT_D32_FLOAT
// Need 32bit minimum: 0x37, // DXGI_FORMAT_D16_UNORM
};
return selectFormat(requestedDepthSwapchainFormats);
}
#endif
else
{
throw std::logic_error("Enum value not implemented");
}
}
int64_t OpenXRPlatform::selectFormat(const std::vector<int64_t>& requestedFormats)
{
auto it =
std::find_first_of(std::begin(requestedFormats), std::end(requestedFormats),
mSwapchainFormats.begin(), mSwapchainFormats.end());
if (it == std::end(requestedFormats))
{
return 0;
}
return *it;
}
void* OpenXRPlatform::DXRegisterObject(void* dxResource, uint32_t glName, uint32_t glType, bool discard, void* ntShareHandle)
{
#ifdef XR_USE_GRAPHICS_API_D3D11
if (ntShareHandle)
{
mPrivate->wglDXSetResourceShareHandleNV(dxResource, ntShareHandle);
}
return mPrivate->wglDXRegisterObjectNV(mPrivate->wglDXDevice, dxResource, glName, glType, 1);
#else
return nullptr;
#endif
}
void OpenXRPlatform::DXUnregisterObject(void* dxResourceShareHandle)
{
#ifdef XR_USE_GRAPHICS_API_D3D11
mPrivate->wglDXUnregisterObjectNV(mPrivate->wglDXDevice, dxResourceShareHandle);
#endif
}
void OpenXRPlatform::DXLockObject(void* dxResourceShareHandle)
{
#ifdef XR_USE_GRAPHICS_API_D3D11
mPrivate->wglDXLockObjectsNV(mPrivate->wglDXDevice, 1, &dxResourceShareHandle);
#endif
}
void OpenXRPlatform::DXUnlockObject(void* dxResourceShareHandle)
{
#ifdef XR_USE_GRAPHICS_API_D3D11
mPrivate->wglDXUnlockObjectsNV(mPrivate->wglDXDevice, 1, &dxResourceShareHandle);
#endif
}
}

@ -0,0 +1,82 @@
#ifndef OPENXR_PLATFORM_HPP
#define OPENXR_PLATFORM_HPP
#include <vector>
#include <memory>
#include <set>
#include <string>
#include <openxr/openxr.h>
namespace MWVR
{
// Error management macros and functions. Should be used on every openxr call.
#define CHK_STRINGIFY(x) #x
#define TOSTRING(x) CHK_STRINGIFY(x)
#define FILE_AND_LINE __FILE__ ":" TOSTRING(__LINE__)
#define CHECK_XRCMD(cmd) CheckXrResult(cmd, #cmd, FILE_AND_LINE)
#define CHECK_XRRESULT(res, cmdStr) CheckXrResult(res, cmdStr, FILE_AND_LINE)
XrResult CheckXrResult(XrResult res, const char* originator = nullptr, const char* sourceLocation = nullptr);
std::string XrResultString(XrResult res);
struct OpenXRPlatformPrivate;
class OpenXRPlatform
{
public:
using ExtensionMap = std::map<std::string, XrExtensionProperties>;
using LayerMap = std::map<std::string, XrApiLayerProperties>;
using LayerExtensionMap = std::map<std::string, ExtensionMap>;
public:
OpenXRPlatform(osg::GraphicsContext* gc);
~OpenXRPlatform();
const char* graphicsAPIExtensionName();
bool supportsExtension(const std::string& extensionName) const;
bool supportsExtension(const std::string& extensionName, uint32_t minimumVersion) const;
bool supportsLayer(const std::string& layerName) const;
bool supportsLayer(const std::string& layerName, uint32_t minimumVersion) const;
bool enableExtension(const std::string& extensionName, bool optional);
bool enableExtension(const std::string& extensionName, bool optional, uint32_t minimumVersion);
bool extensionEnabled(const std::string& extensionName) const;
XrInstance createXrInstance(const std::string& name);
XrSession createXrSession(XrInstance instance, XrSystemId systemId);
int64_t selectColorFormat();
int64_t selectDepthFormat();
int64_t selectFormat(const std::vector<int64_t>& requestedFormats);
std::vector<int64_t> mSwapchainFormats{};
/// Registers an object for sharing as if calling wglDXRegisterObjectNV requesting write access.
/// If ntShareHandle is not null, wglDXSetResourceShareHandleNV is called first to register the share handle
void* DXRegisterObject(void* dxResource, uint32_t glName, uint32_t glType, bool discard, void* ntShareHandle);
/// Unregisters an object from sharing as if calling wglDXUnregisterObjectNV
void DXUnregisterObject(void* dxResourceShareHandle);
/// Locks a DX object for use by OpenGL as if calling wglDXLockObjectsNV
void DXLockObject(void* dxResourceShareHandle);
/// Unlocks a DX object for use by DirectX as if calling wglDXUnlockObjectsNV
void DXUnlockObject(void* dxResourceShareHandle);
private:
void enumerateExtensions(const char* layerName, int logIndent);
void setupExtensions();
void selectGraphicsAPIExtension();
bool selectDirectX();
bool selectOpenGL();
ExtensionMap mAvailableExtensions;
LayerMap mAvailableLayers;
LayerExtensionMap mAvailableLayerExtensions;
std::vector<const char*> mEnabledExtensions;
const char* mGraphicsAPIExtension = nullptr;
std::unique_ptr< OpenXRPlatformPrivate > mPrivate;
};
}
#endif

@ -21,19 +21,9 @@ namespace MWVR {
return impl().beginFrame(gc);
}
void OpenXRSwapchain::endFrame(osg::GraphicsContext* gc)
void OpenXRSwapchain::endFrame(osg::GraphicsContext* gc, VRFramebuffer& readBuffer)
{
return impl().endFrame(gc);
}
uint32_t OpenXRSwapchain::acquiredColorTexture() const
{
return impl().acquiredColorTexture();
}
uint32_t OpenXRSwapchain::acquiredDepthTexture() const
{
return impl().acquiredDepthTexture();
return impl().endFrame(gc, readBuffer);
}
int OpenXRSwapchain::width() const
@ -55,9 +45,4 @@ namespace MWVR {
{
return impl().isAcquired();
}
VRFramebuffer* OpenXRSwapchain::renderBuffer() const
{
return impl().renderBuffer();
}
}

@ -22,13 +22,7 @@ namespace MWVR
void beginFrame(osg::GraphicsContext* gc);
//! Finalize render
void endFrame(osg::GraphicsContext* gc);
//! Currently acquired color texture
uint32_t acquiredColorTexture() const;
//! Currently acquired depth texture
uint32_t acquiredDepthTexture() const;
void endFrame(osg::GraphicsContext* gc, VRFramebuffer& readBuffer);
//! Whether subchain is currently acquired (true) or released (false)
bool isAcquired() const;
@ -42,9 +36,6 @@ namespace MWVR
//! Samples of the view surface
int samples() const;
//! Get the current texture
VRFramebuffer* renderBuffer() const;
//! Get the private implementation
OpenXRSwapchainImpl& impl() { return *mPrivate; }

@ -0,0 +1,223 @@
#include "openxrswapchainimage.hpp"
#include "openxrmanagerimpl.hpp"
#include "vrenvironment.hpp"
#include "vrframebuffer.hpp"
// The OpenXR SDK's platform headers assume we've included platform headers
#ifdef _WIN32
#include <Windows.h>
#include <objbase.h>
#ifdef XR_USE_GRAPHICS_API_D3D11
#include <d3d11.h>
#include <dxgi1_2.h>
#endif
#elif __linux__
#include <X11/Xlib.h>
#include <GL/glx.h>
#undef None
#else
#error Unsupported platform
#endif
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#include <openxr/openxr_platform_defines.h>
#include <openxr/openxr_reflection.h>
#include <stdexcept>
#define GLERR if(auto err = glGetError() != GL_NO_ERROR) Log(Debug::Verbose) << __FILE__ << "." << __LINE__ << ": " << glGetError()
namespace MWVR {
template<typename Image>
class OpenXRSwapchainImageTemplate;
template<>
class OpenXRSwapchainImageTemplate< XrSwapchainImageOpenGLKHR > : public OpenXRSwapchainImage
{
public:
static constexpr XrStructureType XrType = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
public:
OpenXRSwapchainImageTemplate(osg::GraphicsContext* gc, XrSwapchainCreateInfo swapchainCreateInfo, const XrSwapchainImageOpenGLKHR& xrImage)
: OpenXRSwapchainImage()
, mXrImage(xrImage)
, mBufferBits(0)
, mFramebuffer(nullptr)
{
mFramebuffer.reset(new VRFramebuffer(gc->getState(), swapchainCreateInfo.width, swapchainCreateInfo.height, swapchainCreateInfo.sampleCount));
if (swapchainCreateInfo.usageFlags & XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
{
mFramebuffer->setDepthBuffer(gc, mXrImage.image, false);
mBufferBits = GL_DEPTH_BUFFER_BIT;
}
else
{
mFramebuffer->setColorBuffer(gc, mXrImage.image, false);
mBufferBits = GL_COLOR_BUFFER_BIT;
}
}
void blit(osg::GraphicsContext* gc, VRFramebuffer& readBuffer, int offset_x, int offset_y) override
{
mFramebuffer->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
readBuffer.blit(gc, offset_x, offset_y, offset_x + mFramebuffer->width(), offset_y + mFramebuffer->height(), 0, 0, mFramebuffer->width(), mFramebuffer->height(), mBufferBits, GL_NEAREST);
readBuffer.bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
}
XrSwapchainImageOpenGLKHR mXrImage;
uint32_t mBufferBits;
std::unique_ptr<VRFramebuffer> mFramebuffer;
};
#ifdef XR_USE_GRAPHICS_API_D3D11
template<>
class OpenXRSwapchainImageTemplate< XrSwapchainImageD3D11KHR > : public OpenXRSwapchainImage
{
public:
static constexpr XrStructureType XrType = XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR;
public:
OpenXRSwapchainImageTemplate(osg::GraphicsContext* gc, XrSwapchainCreateInfo swapchainCreateInfo, const XrSwapchainImageD3D11KHR& xrImage)
: OpenXRSwapchainImage()
, mXrImage(xrImage)
, mBufferBits(0)
, mFramebuffer(nullptr)
{
mXrImage.texture->GetDevice(&mDevice);
mDevice->GetImmediateContext(&mDeviceContext);
mXrImage.texture->GetDesc(&mDesc);
glGenTextures(1, &mGlTextureName);
auto* xr = Environment::get().getManager();
//mDxResourceShareHandle = xr->impl().platform().DXRegisterObject(mXrImage.texture, mGlTextureName, GL_TEXTURE_2D, true, nullptr);
if (!mDxResourceShareHandle)
{
// Some OpenXR runtimes return textures that cannot be directly shared.
// So we need to make a redundant texture to use as an intermediary...
mSharedTextureDesc.Width = mDesc.Width;
mSharedTextureDesc.Height = mDesc.Height;
mSharedTextureDesc.MipLevels = mDesc.MipLevels;
mSharedTextureDesc.ArraySize = mDesc.ArraySize;
mSharedTextureDesc.Format = static_cast<DXGI_FORMAT>(swapchainCreateInfo.format);
mSharedTextureDesc.SampleDesc = mDesc.SampleDesc;
mSharedTextureDesc.Usage = D3D11_USAGE_DEFAULT;
mSharedTextureDesc.BindFlags = 0;
mSharedTextureDesc.CPUAccessFlags = 0;
mSharedTextureDesc.MiscFlags = 0;;
mDevice->CreateTexture2D(&mSharedTextureDesc, nullptr, &mSharedTexture);
mXrImage.texture->GetDesc(&mSharedTextureDesc);
mDxResourceShareHandle = xr->impl().platform().DXRegisterObject(mSharedTexture, mGlTextureName, GL_TEXTURE_2D, true, nullptr);
}
// Set up shared texture as blit target
mFramebuffer.reset(new VRFramebuffer(gc->getState(), swapchainCreateInfo.width, swapchainCreateInfo.height, swapchainCreateInfo.sampleCount));
if (swapchainCreateInfo.usageFlags & XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
{
mFramebuffer->setDepthBuffer(gc, mGlTextureName, false);
mBufferBits = GL_DEPTH_BUFFER_BIT;
}
else
{
mFramebuffer->setColorBuffer(gc, mGlTextureName, false);
mBufferBits = GL_COLOR_BUFFER_BIT;
}
}
~OpenXRSwapchainImageTemplate()
{
auto* xr = Environment::get().getManager();
if (mDxResourceShareHandle)
xr->impl().platform().DXUnregisterObject(mDxResourceShareHandle);
glDeleteTextures(1, &mGlTextureName);
}
void blit(osg::GraphicsContext* gc, VRFramebuffer& readBuffer, int offset_x, int offset_y) override
{
// Blit readBuffer into directx texture, while flipping the Y axis.
auto* xr = Environment::get().getManager();
xr->impl().platform().DXLockObject(mDxResourceShareHandle);
mFramebuffer->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
readBuffer.blit(gc, offset_x, offset_y, offset_x + mFramebuffer->width(), offset_y + mFramebuffer->height(), 0, mFramebuffer->height(), mFramebuffer->width(), 0, mBufferBits, GL_NEAREST);
xr->impl().platform().DXUnlockObject(mDxResourceShareHandle);
readBuffer.bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
// If the d3d11 texture couldn't be shared directly, blit it again.
if (mSharedTexture)
{
mDeviceContext->CopyResource(mXrImage.texture, mSharedTexture);
}
}
ID3D11Device* mDevice = nullptr;
ID3D11DeviceContext* mDeviceContext = nullptr;
D3D11_TEXTURE2D_DESC mDesc;
D3D11_TEXTURE2D_DESC mSharedTextureDesc;
ID3D11Texture2D* mSharedTexture = nullptr;
uint32_t mGlTextureName = 0;
void* mDxResourceShareHandle = nullptr;
XrSwapchainImageD3D11KHR mXrImage;
uint32_t mBufferBits;
std::unique_ptr<VRFramebuffer> mFramebuffer;
};
#endif
template< typename Image > static inline
std::vector<std::unique_ptr<OpenXRSwapchainImage> >
enumerateSwapchainImagesImpl(osg::GraphicsContext* gc, XrSwapchain swapchain, XrSwapchainCreateInfo swapchainCreateInfo)
{
using SwapchainImage = OpenXRSwapchainImageTemplate<Image>;
uint32_t imageCount = 0;
std::vector< Image > images;
CHECK_XRCMD(xrEnumerateSwapchainImages(swapchain, 0, &imageCount, nullptr));
images.resize(imageCount, { SwapchainImage::XrType });
CHECK_XRCMD(xrEnumerateSwapchainImages(swapchain, imageCount, &imageCount, reinterpret_cast<XrSwapchainImageBaseHeader*>(images.data())));
std::vector<std::unique_ptr<OpenXRSwapchainImage> > swapchainImages;
for(auto& image: images)
{
swapchainImages.emplace_back(new OpenXRSwapchainImageTemplate<Image>(gc, swapchainCreateInfo, image));
}
return swapchainImages;
}
std::vector<std::unique_ptr<OpenXRSwapchainImage> >
OpenXRSwapchainImage::enumerateSwapchainImages(osg::GraphicsContext* gc, XrSwapchain swapchain, XrSwapchainCreateInfo swapchainCreateInfo)
{
auto* xr = Environment::get().getManager();
if (xr->xrExtensionIsEnabled(XR_KHR_OPENGL_ENABLE_EXTENSION_NAME))
{
return enumerateSwapchainImagesImpl<XrSwapchainImageOpenGLKHR>(gc, swapchain, swapchainCreateInfo);
}
#ifdef XR_USE_GRAPHICS_API_D3D11
else if (xr->xrExtensionIsEnabled(XR_KHR_D3D11_ENABLE_EXTENSION_NAME))
{
return enumerateSwapchainImagesImpl<XrSwapchainImageD3D11KHR>(gc, swapchain, swapchainCreateInfo);
}
#endif
else
{
throw std::logic_error("Implementation missing for selected graphics API");
}
return std::vector<std::unique_ptr<OpenXRSwapchainImage>>();
}
OpenXRSwapchainImage::OpenXRSwapchainImage()
{
}
}

@ -0,0 +1,27 @@
#ifndef OPENXR_SWAPCHAINIMAGE_HPP
#define OPENXR_SWAPCHAINIMAGE_HPP
#include <vector>
#include <memory>
#include <openxr/openxr.h>
#include <osg/GraphicsContext>
#include "vrframebuffer.hpp"
namespace MWVR
{
class OpenXRSwapchainImage
{
public:
static std::vector< std::unique_ptr<OpenXRSwapchainImage> >
enumerateSwapchainImages(osg::GraphicsContext* gc, XrSwapchain swapchain, XrSwapchainCreateInfo swapchainCreateInfo);
OpenXRSwapchainImage();
virtual ~OpenXRSwapchainImage() {};
virtual void blit(osg::GraphicsContext* gc, VRFramebuffer& readBuffer, int offset_x, int offset_y) = 0;
};
}
#endif

@ -5,24 +5,6 @@
#include <components/debug/debuglog.hpp>
#ifdef _WIN32
#include <Windows.h>
#include <objbase.h>
#elif __linux__
#include <X11/Xlib.h>
#include <GL/glx.h>
#undef None
#else
#error Unsupported platform
#endif
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#include <openxr/openxr_platform_defines.h>
#include <openxr/openxr_reflection.h>
namespace MWVR {
OpenXRSwapchainImpl::OpenXRSwapchainImpl(osg::ref_ptr<osg::State> state, SwapchainConfig config)
: mConfig(config)
@ -51,44 +33,12 @@ namespace MWVR {
mSwapchainDepth = nullptr;
}
}
uint32_t imageCount = mSwapchain->count();
for (uint32_t i = 0; i < imageCount; i++)
{
uint32_t colorBuffer = mSwapchain->bufferAt(i);
uint32_t depthBuffer = mSwapchainDepth ? mSwapchainDepth->bufferAt(i) : 0;
mRenderBuffers.emplace_back(new VRFramebuffer(state, mSwapchain->width(), mSwapchain->height(), mSwapchain->samples(), colorBuffer, depthBuffer));
}
}
OpenXRSwapchainImpl::~OpenXRSwapchainImpl()
{
}
VRFramebuffer* OpenXRSwapchainImpl::renderBuffer() const
{
checkAcquired();
// Note that I am trusting that the openxr runtime won't diverge the indexes of the depth and color swapchains so long as these are always acquired together.
// If some dumb ass implementation decides to violate this we'll just have to work around that if it actually happens.
return mRenderBuffers[mSwapchain->acuiredIndex()].get();
}
uint32_t OpenXRSwapchainImpl::acquiredColorTexture() const
{
checkAcquired();
return mSwapchain->acuiredBuffer();
}
uint32_t OpenXRSwapchainImpl::acquiredDepthTexture() const
{
if (mSwapchainDepth)
{
checkAcquired();
return mSwapchainDepth->acuiredBuffer();
}
return 0;
}
bool OpenXRSwapchainImpl::isAcquired() const
{
return mFormallyAcquired;
@ -96,29 +46,32 @@ namespace MWVR {
void OpenXRSwapchainImpl::beginFrame(osg::GraphicsContext* gc)
{
acquire();
acquire(gc);
}
int swapCount = 0;
void OpenXRSwapchainImpl::endFrame(osg::GraphicsContext* gc)
void OpenXRSwapchainImpl::endFrame(osg::GraphicsContext* gc, VRFramebuffer& readBuffer)
{
checkAcquired();
release();
release(gc, readBuffer);
}
void OpenXRSwapchainImpl::acquire()
void OpenXRSwapchainImpl::acquire(osg::GraphicsContext* gc)
{
if (isAcquired())
throw std::logic_error("Trying to acquire already acquired swapchain");
// The openxr runtime may fail to acquire/release.
// Do not re-acquire a swapchain before having successfully released it.
// Lest the swapchain fall out of sync.
if (!mShouldRelease)
{
mSwapchain->acquire();
mSwapchain->acquire(gc);
mShouldRelease = mSwapchain->isAcquired();
if (mSwapchainDepth && mSwapchain->isAcquired())
{
mSwapchainDepth->acquire();
mSwapchainDepth->acquire(gc);
mShouldRelease = mSwapchainDepth->isAcquired();
}
}
@ -126,15 +79,17 @@ namespace MWVR {
mFormallyAcquired = true;
}
void OpenXRSwapchainImpl::release()
void OpenXRSwapchainImpl::release(osg::GraphicsContext* gc, VRFramebuffer& readBuffer)
{
// The openxr runtime may fail to acquire/release.
// Do not release a swapchain before having successfully acquire it.
if (mShouldRelease)
{
mSwapchain->release();
mSwapchain->blitAndRelease(gc, readBuffer);
mShouldRelease = mSwapchain->isAcquired();
if (mSwapchainDepth)
{
mSwapchainDepth->release();
mSwapchainDepth->blitAndRelease(gc, readBuffer);
mShouldRelease = mSwapchainDepth->isAcquired();
}
}
@ -148,69 +103,21 @@ namespace MWVR {
throw std::logic_error("Swapchain must be acquired before use. Call between OpenXRSwapchain::beginFrame() and OpenXRSwapchain::endFrame()");
}
static int64_t selectFormat(const std::vector<int64_t>& eligibleFormats, const std::vector<int64_t>& requestedFormats)
{
auto it =
std::find_first_of(std::begin(requestedFormats), std::end(requestedFormats),
eligibleFormats.begin(), eligibleFormats.end());
if (it == std::end(requestedFormats))
{
return 0;
}
return *it;
}
static int64_t selectColorFormat(const std::vector<int64_t>& eligibleFormats)
{
// Find supported color swapchain format.
std::vector<int64_t> requestedColorSwapchainFormats = {
0x8058, // GL_RGBA8
0x8F97, // GL_RGBA8_SNORM
0x881A, // GL_RGBA16F
0x881B, // GL_RGB16F
// Offered by SteamVR but is broken: // 0x805B, // GL_RGBA16
0x8C3A, // GL_R11F_G11F_B10F
// We manage gamma ourselves: 0x8C43, // GL_SRGB8_ALPHA8
// We manage gamma ourselves: 0x8C41, // GL_SRGB8
};
return selectFormat(eligibleFormats, requestedColorSwapchainFormats);
}
static int64_t selectDepthFormat(const std::vector<int64_t>& eligibleFormats)
{
// Find supported depth swapchain format.
std::vector<int64_t> requestedDepthSwapchainFormats = {
0x81A6, // GL_DEPTH_COMPONENT24
0x88F0, // GL_DEPTH24_STENCIL8
0x8CAC, // GL_DEPTH_COMPONENT32F
0x81A7, // GL_DEPTH_COMPONENT32
0x8DAB, // GL_DEPTH_COMPONENT32F_NV
0x8CAD, // GL_DEPTH32_STENCIL8
// Need 32bit minimum: // 0x81A5, // GL_DEPTH_COMPONENT16
};
return selectFormat(eligibleFormats, requestedDepthSwapchainFormats);
}
OpenXRSwapchainImpl::SwapchainPrivate::SwapchainPrivate(osg::ref_ptr<osg::State> state, SwapchainConfig config, Use use)
: mBuffers()
: mConfig(config)
, mImages()
, mWidth(config.selectedWidth)
, mHeight(config.selectedHeight)
, mSamples(config.selectedSamples)
, mSamples(1)
, mUsage(use)
{
auto* xr = Environment::get().getManager();
// Select a swapchain format.
uint32_t swapchainFormatCount;
CHECK_XRCMD(xrEnumerateSwapchainFormats(xr->impl().xrSession(), 0, &swapchainFormatCount, nullptr));
std::vector<int64_t> swapchainFormats(swapchainFormatCount);
CHECK_XRCMD(xrEnumerateSwapchainFormats(xr->impl().xrSession(), (uint32_t)swapchainFormats.size(), &swapchainFormatCount, swapchainFormats.data()));
if (use == Use::COLOR)
mFormat = selectColorFormat(swapchainFormats);
mFormat = xr->selectColorFormat();
else
mFormat = selectDepthFormat(swapchainFormats);
mFormat = xr->selectDepthFormat();
std::string typeString = use == Use::COLOR ? "color" : "depth";
if (mFormat == 0) {
@ -236,7 +143,10 @@ namespace MWVR {
// First create the swapchain of color buffers.
swapchainCreateInfo.format = mFormat;
swapchainCreateInfo.sampleCount = mSamples;
swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
if(use == Use::COLOR)
swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
else
swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
auto res = xrCreateSwapchain(xr->impl().xrSession(), &swapchainCreateInfo, &mSwapchain);
if (!XR_SUCCEEDED(res))
{
@ -249,10 +159,8 @@ namespace MWVR {
VrDebug::setName(mSwapchain, "OpenMW XR Color Swapchain " + config.name);
}
uint32_t imageCount = 0;
CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, 0, &imageCount, nullptr));
mBuffers.resize(imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR });
CHECK_XRCMD(xrEnumerateSwapchainImages(mSwapchain, imageCount, &imageCount, reinterpret_cast<XrSwapchainImageBaseHeader*>(mBuffers.data())));
// TODO: here
mImages = OpenXRSwapchainImage::enumerateSwapchainImages(state->getGraphicsContext(), mSwapchain, swapchainCreateInfo);
mSubImage.swapchain = mSwapchain;
mSubImage.imageRect.offset = { 0, 0 };
mSubImage.imageRect.extent = { mWidth, mHeight };
@ -264,27 +172,17 @@ namespace MWVR {
CHECK_XRCMD(xrDestroySwapchain(mSwapchain));
}
uint32_t OpenXRSwapchainImpl::SwapchainPrivate::bufferAt(uint32_t index) const
{
return mBuffers[index].image;
}
uint32_t OpenXRSwapchainImpl::SwapchainPrivate::count() const
{
return mBuffers.size();
return mImages.size();
}
uint32_t OpenXRSwapchainImpl::SwapchainPrivate::acuiredBuffer() const
{
checkAcquired();
return mBuffers[mAcquiredIndex].image;
}
bool OpenXRSwapchainImpl::SwapchainPrivate::isAcquired() const
{
return mIsReady;
}
void OpenXRSwapchainImpl::SwapchainPrivate::acquire()
void OpenXRSwapchainImpl::SwapchainPrivate::acquire(osg::GraphicsContext* gc)
{
auto xr = Environment::get().getManager();
XrSwapchainImageAcquireInfo acquireInfo{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO };
@ -301,13 +199,15 @@ namespace MWVR {
mIsReady = XR_SUCCEEDED(CHECK_XRCMD(xrWaitSwapchainImage(mSwapchain, &waitInfo)));
}
}
void OpenXRSwapchainImpl::SwapchainPrivate::release()
void OpenXRSwapchainImpl::SwapchainPrivate::blitAndRelease(osg::GraphicsContext* gc, VRFramebuffer& readBuffer)
{
auto xr = Environment::get().getManager();
XrSwapchainImageReleaseInfo releaseInfo{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
if (mIsReady)
{
mImages[mAcquiredIndex]->blit(gc, readBuffer, mConfig.offsetWidth, mConfig.offsetHeight);
mIsReady = !XR_SUCCEEDED(CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchain, &releaseInfo)));
if (!mIsReady)
{

@ -1,14 +1,15 @@
#ifndef OPENXR_SWAPCHAINIMPL_HPP
#define OPENXR_SWAPCHAINIMPL_HPP
#include "openxrswapchain.hpp"
#include "openxrswapchainimage.hpp"
#include "openxrmanagerimpl.hpp"
struct XrSwapchainSubImage;
struct XrSwapchainImageOpenGLKHR;
namespace MWVR
{
class VRFramebuffer;
/// \brief Implementation of OpenXRSwapchain
class OpenXRSwapchainImpl
{
@ -23,30 +24,32 @@ namespace MWVR
SwapchainPrivate(osg::ref_ptr<osg::State> state, SwapchainConfig config, Use use);
~SwapchainPrivate();
uint32_t bufferAt(uint32_t index) const;
uint32_t count() const;
uint32_t acuiredBuffer() const;
uint32_t acuiredIndex() const { return mAcquiredIndex; };
bool isAcquired() const;
uint32_t acuiredIndex() const { return mAcquiredIndex; };
XrSwapchain xrSwapchain(void) const { return mSwapchain; };
XrSwapchainSubImage xrSubImage(void) const { return mSubImage; };
int width() const { return mWidth; };
int height() const { return mHeight; };
int samples() const { return mSamples; };
void acquire();
void release();
void acquire(osg::GraphicsContext* gc);
void blitAndRelease(osg::GraphicsContext* gc, VRFramebuffer& readBuffer);
void checkAcquired() const;
protected:
private:
SwapchainConfig mConfig;
XrSwapchain mSwapchain = XR_NULL_HANDLE;
XrSwapchainSubImage mSubImage{};
std::vector<XrSwapchainImageOpenGLKHR> mBuffers;
std::vector< std::unique_ptr<OpenXRSwapchainImage> > mImages;
int32_t mWidth = -1;
int32_t mHeight = -1;
int32_t mSamples = -1;
int64_t mFormat = -1;
uint32_t mAcquiredIndex{ 0 };
Use mUsage;
bool mIsIndexAcquired{ false };
bool mIsReady{ false };
};
@ -56,11 +59,7 @@ namespace MWVR
~OpenXRSwapchainImpl();
void beginFrame(osg::GraphicsContext* gc);
void endFrame(osg::GraphicsContext* gc);
VRFramebuffer* renderBuffer() const;
uint32_t acquiredColorTexture() const;
uint32_t acquiredDepthTexture() const;
void endFrame(osg::GraphicsContext* gc, VRFramebuffer& readBuffer);
bool isAcquired() const;
XrSwapchain xrSwapchain(void) const { return mSwapchain->xrSwapchain(); };
@ -75,15 +74,14 @@ namespace MWVR
OpenXRSwapchainImpl(const OpenXRSwapchainImpl&) = delete;
void operator=(const OpenXRSwapchainImpl&) = delete;
void acquire();
void release();
void acquire(osg::GraphicsContext* gc);
void release(osg::GraphicsContext* gc, VRFramebuffer& readBuffer);
void checkAcquired() const;
private:
SwapchainConfig mConfig;
std::unique_ptr<SwapchainPrivate> mSwapchain{ nullptr };
std::unique_ptr<SwapchainPrivate> mSwapchainDepth{ nullptr };
std::vector<std::unique_ptr<VRFramebuffer> > mRenderBuffers{};
bool mFormallyAcquired{ false };
bool mShouldRelease{ false };
};

@ -11,65 +11,50 @@
namespace MWVR
{
VRFramebuffer::VRFramebuffer(osg::ref_ptr<osg::State> state, std::size_t width, std::size_t height, uint32_t msaaSamples, uint32_t colorBuffer, uint32_t depthBuffer)
VRFramebuffer::VRFramebuffer(osg::ref_ptr<osg::State> state, std::size_t width, std::size_t height, uint32_t msaaSamples)
: mState(state)
, mWidth(width)
, mHeight(height)
, mDepthBuffer(depthBuffer)
, mColorBuffer(colorBuffer)
, mDepthBuffer()
, mColorBuffer()
, mSamples(msaaSamples)
{
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
gl->glGenFramebuffers(1, &mBlitFBO);
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mBlitFBO);
gl->glGenFramebuffers(1, &mFBO);
if (mSamples <= 1)
mTextureTarget = GL_TEXTURE_2D;
else
mTextureTarget = GL_TEXTURE_2D_MULTISAMPLE;
}
if (mColorBuffer == 0)
{
glGenTextures(1, &mColorBuffer);
glBindTexture(mTextureTarget, mColorBuffer);
if (mSamples <= 1)
glTexImage2D(mTextureTarget, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_INT, nullptr);
else
gl->glTexImage2DMultisample(mTextureTarget, mSamples, GL_RGBA, mWidth, mHeight, false);
glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_ARB);
glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER_ARB);
glTexParameteri(mTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(mTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(mTextureTarget, GL_TEXTURE_MAX_LEVEL, 0);
}
if (mDepthBuffer == 0)
{
glGenTextures(1, &mDepthBuffer);
glBindTexture(mTextureTarget, mDepthBuffer);
if (mSamples <= 1)
glTexImage2D(mTextureTarget, 0, GL_DEPTH_COMPONENT24, mWidth, mHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
else
gl->glTexImage2DMultisample(mTextureTarget, mSamples, GL_DEPTH_COMPONENT, mWidth, mHeight, false);
glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(mTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(mTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(mTextureTarget, GL_TEXTURE_MAX_LEVEL, 0);
}
void VRFramebuffer::setColorBuffer(osg::GraphicsContext* gc, uint32_t colorBuffer, bool takeOwnership)
{
auto* gl = osg::GLExtensions::Get(gc->getState()->getContextID(), false);
mColorBuffer.setTexture(colorBuffer, takeOwnership);
bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, mTextureTarget, mColorBuffer.mImage, 0);
}
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, mFBO);
gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, mTextureTarget, mColorBuffer, 0);
gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, mTextureTarget, mDepthBuffer, 0);
if (gl->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT)
throw std::runtime_error("Failed to create OpenXR framebuffer");
void VRFramebuffer::setDepthBuffer(osg::GraphicsContext* gc, uint32_t depthBuffer, bool takeOwnership)
{
auto* gl = osg::GLExtensions::Get(gc->getState()->getContextID(), false);
mDepthBuffer.setTexture(depthBuffer, takeOwnership);
bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
gl->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, mTextureTarget, mDepthBuffer.mImage, 0);
}
void VRFramebuffer::createColorBuffer(osg::GraphicsContext* gc)
{
auto colorBuffer = createImage(gc, GL_RGBA8, GL_RGBA);
setColorBuffer(gc, colorBuffer, true);
}
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
void VRFramebuffer::createDepthBuffer(osg::GraphicsContext* gc)
{
auto depthBuffer = createImage(gc, GL_DEPTH24_STENCIL8_EXT, GL_DEPTH_COMPONENT);
setDepthBuffer(gc, depthBuffer, true);
}
VRFramebuffer::~VRFramebuffer()
@ -94,28 +79,65 @@ namespace MWVR
else if (mFBO)
// Without access to glDeleteFramebuffers, i'll have to leak FBOs.
Log(Debug::Warning) << "destroy() called without a State. Leaking FBO";
mFBO = 0;
if (mDepthBuffer)
glDeleteTextures(1, &mDepthBuffer);
if (mColorBuffer)
glDeleteTextures(1, &mColorBuffer);
mFBO = mDepthBuffer = mColorBuffer = 0;
mColorBuffer.delet();
mDepthBuffer.delet();
}
void VRFramebuffer::bindFramebuffer(osg::GraphicsContext* gc, uint32_t target)
{
auto state = gc->getState();
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
//if (gl->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT)
// throw std::runtime_error("Tried to bind incomplete framebuffer");
gl->glBindFramebuffer(target, mFBO);
}
void VRFramebuffer::blit(osg::GraphicsContext* gc, int x, int y, int w, int h)
void VRFramebuffer::blit(osg::GraphicsContext* gc, int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, uint32_t bits, uint32_t filter)
{
#define GLERR if(auto err = glGetError() != GL_NO_ERROR) Log(Debug::Verbose) << __FILE__ << "." << __LINE__ << ": " << glGetError()
auto* state = gc->getState();
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, mFBO);
gl->glBlitFramebuffer(0, 0, mWidth, mHeight, x, y, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR);
gl->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, bits, filter);
gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
}
uint32_t VRFramebuffer::createImage(osg::GraphicsContext* gc, uint32_t formatInternal, uint32_t format)
{
auto* gl = osg::GLExtensions::Get(gc->getState()->getContextID(), false);
uint32_t image;
glGenTextures(1, &image);
glBindTexture(mTextureTarget, image);
if (mSamples <= 1)
glTexImage2D(mTextureTarget, 0, formatInternal, mWidth, mHeight, 0, format, GL_UNSIGNED_INT, nullptr);
else
gl->glTexImage2DMultisample(mTextureTarget, mSamples, format, mWidth, mHeight, false);
glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_ARB);
glTexParameteri(mTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER_ARB);
glTexParameteri(mTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(mTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(mTextureTarget, GL_TEXTURE_MAX_LEVEL, 0);
return image;
}
void VRFramebuffer::Texture::delet()
{
if (mOwner)
glDeleteTextures(1, &mImage);
mImage = 0;
mOwner = false;
}
void VRFramebuffer::Texture::setTexture(uint32_t image, bool owner)
{
if (mImage)
delet();
mImage = image;
mOwner = owner;
}
}

@ -13,10 +13,20 @@ namespace MWVR
/// \brief Manages an opengl framebuffer
///
/// Intended for managing the vr swapchain, but is also use to manage the mirror texture as a convenience.
class VRFramebuffer : public osg::Referenced
class VRFramebuffer
{
private:
struct Texture
{
uint32_t mImage = 0;
bool mOwner = false;
void delet();
void setTexture(uint32_t image, bool owner);
};
public:
VRFramebuffer(osg::ref_ptr<osg::State> state, std::size_t width, std::size_t height, uint32_t msaaSamples, uint32_t colorBuffer = 0, uint32_t depthBuffer = 0);
VRFramebuffer(osg::ref_ptr<osg::State> state, std::size_t width, std::size_t height, uint32_t msaaSamples);
~VRFramebuffer();
void destroy(osg::State* state);
@ -27,13 +37,20 @@ namespace MWVR
void bindFramebuffer(osg::GraphicsContext* gc, uint32_t target);
uint32_t fbo(void) const { return mFBO; }
uint32_t colorBuffer(void) const { return mColorBuffer; }
void setColorBuffer(osg::GraphicsContext* gc, uint32_t colorBuffer, bool takeOwnership);
void setDepthBuffer(osg::GraphicsContext* gc, uint32_t depthBuffer, bool takeOwnership);
void createColorBuffer(osg::GraphicsContext* gc);
void createDepthBuffer(osg::GraphicsContext* gc);
//! Blit to region in currently bound draw fbo
void blit(osg::GraphicsContext* gc, int x, int y, int w, int h);
//! ref glBlitFramebuffer
void blit(osg::GraphicsContext* gc, int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, uint32_t bits, uint32_t filter = GL_LINEAR);
uint32_t colorBuffer() const { return mColorBuffer.mImage; };
uint32_t depthBuffer() const { return mDepthBuffer.mImage; };
private:
uint32_t createImage(osg::GraphicsContext* gc, uint32_t formatInternal, uint32_t format);
// Set aside a weak pointer to the constructor state to use when freeing FBOs, if no state is given to destroy()
osg::observer_ptr<osg::State> mState;
@ -43,11 +60,8 @@ namespace MWVR
// Render Target
uint32_t mFBO = 0;
uint32_t mBlitFBO = 0;
uint32_t mDepthBuffer = 0;
bool mOwnDepthBuffer = false;
uint32_t mColorBuffer = 0;
bool mOwnColorBuffer = false;
Texture mDepthBuffer;
Texture mColorBuffer;
uint32_t mSamples = 0;
uint32_t mTextureTarget = 0;
};

@ -218,7 +218,7 @@ namespace MWVR
{
std::string extension = requireAttribute(extensionElement, "Name");
auto xr = MWVR::Environment::get().getManager();
if (!xr->xrExtensionIsEnabled(XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME))
if (!xr->xrExtensionIsEnabled(extension.c_str()))
{
Log(Debug::Verbose) << " Required extension '" << extension << "' not supported. Skipping interaction profile.";
return;

@ -143,7 +143,7 @@ namespace MWVR
{
if (frameMeta->mShouldRender)
{
viewer.blitEyesToMirrorTexture(gc);
viewer.blit(gc);
gc->swapBuffersImplementation();
std::array<CompositionLayerProjectionView, 2> layerStack{};
layerStack[(int)Side::LEFT_SIDE].subImage = viewer.subImage(Side::LEFT_SIDE);
@ -239,8 +239,8 @@ namespace MWVR
xr->enablePredictions();
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].pose = hmdViews[(int)Side::LEFT_SIDE].pose * mPlayerScale * Constants::UnitsPerMeter;
predictedPoses.view[(int)Side::RIGHT_SIDE].pose = hmdViews[(int)Side::RIGHT_SIDE].pose * mPlayerScale * Constants::UnitsPerMeter;
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(frame->mPredictedDisplayTime, ReferenceSpace::STAGE);

@ -122,6 +122,8 @@ namespace MWVR
int selectedWidth = -1;
int selectedHeight = -1;
int selectedSamples = -1;
int offsetWidth = 0;
int offsetHeight = 0;
std::string name = "";
};

@ -42,10 +42,11 @@ namespace MWVR
: mViewer(viewer)
, mPreDraw(new PredrawCallback(this))
, mPostDraw(new PostdrawCallback(this))
, mUpdateViewCallback(new UpdateViewCallback(this))
, mMsaaResolveTexture{}
, mMirrorTexture{ nullptr }
, mOpenXRConfigured(false)
, mCallbacksConfigured(false)
, mMsaaResolveMirrorTexture{}
, mMirrorTexture{ nullptr }
{
mViewer->setRealizeOperation(new RealizeOperation());
}
@ -94,7 +95,7 @@ namespace MWVR
return VRViewer::MirrorTextureEye::Both;
}
void VRViewer::configureXR(osg::GraphicsContext* context)
void VRViewer::configureXR(osg::GraphicsContext* gc)
{
std::unique_lock<std::mutex> lock(mMutex);
@ -105,14 +106,14 @@ namespace MWVR
auto* xr = Environment::get().getManager();
xr->realize(context);
xr->realize(gc);
// Run through initial events to start session
// For the rest of runtime this is handled by vrsession
xr->handleEvents();
// Set up swapchain config
auto config = xr->getRecommendedSwapchainConfig();
mSwapchainConfig = xr->getRecommendedSwapchainConfig();
std::array<std::string, 2> xConfString;
std::array<std::string, 2> yConfString;
@ -126,41 +127,41 @@ namespace MWVR
{
auto name = sViewNames[i];
config[i].selectedWidth = parseResolution(xConfString[i], config[i].recommendedWidth, config[i].maxWidth);
config[i].selectedHeight = parseResolution(yConfString[i], config[i].recommendedHeight, config[i].maxHeight);
config[i].selectedSamples = Settings::Manager::getInt("antialiasing", "Video");
// OpenXR requires a non-zero value
if (config[i].selectedSamples < 1)
config[i].selectedSamples = 1;
Log(Debug::Verbose) << name << " resolution: Recommended x=" << config[i].recommendedWidth << ", y=" << config[i].recommendedHeight;
Log(Debug::Verbose) << name << " resolution: Max x=" << config[i].maxWidth << ", y=" << config[i].maxHeight;
Log(Debug::Verbose) << name << " resolution: Selected x=" << config[i].selectedWidth << ", y=" << config[i].selectedHeight;
config[i].name = sViewNames[i];
mSubImages[i].width = config[i].selectedWidth;
mSubImages[i].height = config[i].selectedHeight;
mSwapchainConfig[i].selectedWidth = parseResolution(xConfString[i], mSwapchainConfig[i].recommendedWidth, mSwapchainConfig[i].maxWidth);
mSwapchainConfig[i].selectedHeight = parseResolution(yConfString[i], mSwapchainConfig[i].recommendedHeight, mSwapchainConfig[i].maxHeight);
mSwapchainConfig[i].selectedSamples =
std::max(1, // OpenXR requires a non-zero value
std::min(mSwapchainConfig[i].maxSamples,
Settings::Manager::getInt("antialiasing", "Video")
)
);
Log(Debug::Verbose) << name << " resolution: Recommended x=" << mSwapchainConfig[i].recommendedWidth << ", y=" << mSwapchainConfig[i].recommendedHeight;
Log(Debug::Verbose) << name << " resolution: Max x=" << mSwapchainConfig[i].maxWidth << ", y=" << mSwapchainConfig[i].maxHeight;
Log(Debug::Verbose) << name << " resolution: Selected x=" << mSwapchainConfig[i].selectedWidth << ", y=" << mSwapchainConfig[i].selectedHeight;
mSwapchainConfig[i].name = sViewNames[i];
if (i > 0)
{
mSubImages[i].x = mSubImages[i - 1].x + mSubImages[i - 1].width;
}
else
{
mSubImages[i].x = 0;
}
mSubImages[i].y = 0;
}
mSwapchainConfig[i].offsetWidth = mSwapchainConfig[i].selectedWidth + mSwapchainConfig[i].offsetWidth;
mSwapchainConfig.name = "Main";
mSwapchainConfig.selectedWidth = config[0].selectedWidth + config[1].selectedWidth;
mSwapchainConfig.selectedHeight = std::max(config[0].selectedHeight, config[1].selectedHeight);
mSwapchainConfig.selectedSamples = std::max(config[0].selectedSamples, config[1].selectedSamples);
mSwapchain[i].reset(new OpenXRSwapchain(gc->getState(), mSwapchainConfig[i]));
mSubImages[i].width = mSwapchainConfig[i].selectedWidth;
mSubImages[i].height = mSwapchainConfig[i].selectedHeight;
mSubImages[i].x = mSubImages[i].y = 0;
mSubImages[i].swapchain = mSwapchain[i].get();
}
mSwapchain.reset(new OpenXRSwapchain(context->getState(), mSwapchainConfig));
int width = mSubImages[0].width + mSubImages[1].width;
int height = std::max(mSubImages[0].height, mSubImages[1].height);
int samples = std::max(mSwapchainConfig[0].selectedSamples, mSwapchainConfig[1].selectedSamples);
mSubImages[0].swapchain = mSubImages[1].swapchain = mSwapchain.get();
mFramebuffer.reset(new VRFramebuffer(gc->getState(),width,height, samples));
mFramebuffer->createColorBuffer(gc);
mFramebuffer->createDepthBuffer(gc);
mMsaaResolveTexture.reset(new VRFramebuffer(gc->getState(),width,height,0));
mMsaaResolveTexture->createColorBuffer(gc);
mMsaaResolveTexture->createDepthBuffer(gc);
mViewer->setReleaseContextAtEndOfFrameHint(false);
mViewer->getCamera()->getGraphicsContext()->setSwapCallback(new VRViewer::SwapBuffersCallback(this));
@ -176,6 +177,7 @@ namespace MWVR
return;
// Give the main camera an initial draw callback that disables camera setup (we don't want it)
Misc::StereoView::instance().setUpdateViewCallback(mUpdateViewCallback);
Misc::StereoView::instance().setInitialDrawCallback(new InitialDrawCallback(this));
Misc::StereoView::instance().setPredrawCallback(mPreDraw);
Misc::StereoView::instance().setPostdrawCallback(mPostDraw);
@ -234,12 +236,11 @@ namespace MWVR
return mSubImages[static_cast<int>(side)];
}
void VRViewer::blitEyesToMirrorTexture(osg::GraphicsContext* gc)
void VRViewer::blit(osg::GraphicsContext* gc)
{
if (mMirrorTextureShouldBeCleanedUp)
{
mMirrorTexture = nullptr;
mMsaaResolveMirrorTexture = nullptr;
mMirrorTextureShouldBeCleanedUp = false;
}
if (!mMirrorTextureEnabled)
@ -255,10 +256,7 @@ namespace MWVR
screenWidth,
screenHeight,
0));
mMsaaResolveMirrorTexture.reset(new VRFramebuffer(gc->getState(),
mSwapchain->width(),
mSwapchain->height(),
0));
mMirrorTexture->createColorBuffer(gc);
}
auto* state = gc->getState();
@ -268,22 +266,18 @@ namespace MWVR
//// Since OpenXR does not include native support for mirror textures, we have to generate them ourselves
//// which means resolving msaa twice.
mMsaaResolveMirrorTexture->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
mSwapchain->renderBuffer()->blit(gc, 0, 0, mSwapchain->width(), mSwapchain->height());
mMirrorTexture->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
mMsaaResolveMirrorTexture->blit(gc, 0, 0, screenWidth, screenHeight);
//for (unsigned i = 0; i < mMirrorTextureViews.size(); i++)
//{
// mMsaaResolveMirrorTexture->blit(gc, );
// mMsaaResolveMirrorTexture->bindFramebuffer(gc, GL_READ_FRAMEBUFFER_EXT);
// gl->glBlitFramebuffer(0, 0, mWidth, mHeight, i * mirrorWidth, 0, (i + 1) * mirrorWidth, screenHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
// gl->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
//}
mMsaaResolveTexture->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
//mSwapchain->framebuffer()->blit(gc, 0, 0, mMsaaResolveMirrorTexture->width(), mMsaaResolveMirrorTexture->height(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
mFramebuffer->blit(gc, 0, 0, mFramebuffer->width(), mFramebuffer->height(), 0, 0, mMsaaResolveTexture->width(), mMsaaResolveTexture->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
mFramebuffer->blit(gc, 0, 0, mFramebuffer->width(), mFramebuffer->height(), 0, 0, mMsaaResolveTexture->width(), mMsaaResolveTexture->height(), GL_DEPTH_BUFFER_BIT, GL_NEAREST);
//mMirrorTexture->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
gl->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
mMirrorTexture->blit(gc, 0, 0, screenWidth, screenHeight);
mMsaaResolveTexture->blit(gc, 0, 0, mMsaaResolveTexture->width(), mMsaaResolveTexture->height(), 0, 0, screenWidth, screenHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
//mMirrorTexture->blit(gc, 0, 0, screenWidth, screenHeight, GL_COLOR_BUFFER_BIT);
mSwapchain->endFrame(gc);
mSwapchain[0]->endFrame(gc, *mMsaaResolveTexture);
mSwapchain[1]->endFrame(gc, *mMsaaResolveTexture);
}
void
@ -313,8 +307,11 @@ namespace MWVR
Environment::get().getSession()->beginPhase(VRSession::FramePhase::Draw);
if (Environment::get().getSession()->getFrame(VRSession::FramePhase::Draw)->mShouldRender)
mSwapchain->beginFrame(info.getState()->getGraphicsContext());
mViewer->getCamera()->setViewport(0, 0, mSwapchainConfig.selectedWidth, mSwapchainConfig.selectedHeight);
{
mSwapchain[0]->beginFrame(info.getState()->getGraphicsContext());
mSwapchain[1]->beginFrame(info.getState()->getGraphicsContext());
}
mViewer->getCamera()->setViewport(0, 0, mFramebuffer->width(), mFramebuffer->height());
osg::GraphicsOperation* graphicsOperation = info.getCurrentCamera()->getRenderer();
osgViewer::Renderer* renderer = dynamic_cast<osgViewer::Renderer*>(graphicsOperation);
@ -330,7 +327,10 @@ namespace MWVR
void VRViewer::preDrawCallback(osg::RenderInfo& info)
{
if (Environment::get().getSession()->getFrame(VRSession::FramePhase::Draw)->mShouldRender)
mSwapchain->renderBuffer()->bindFramebuffer(info.getState()->getGraphicsContext(), GL_FRAMEBUFFER_EXT);
{
mFramebuffer->bindFramebuffer(info.getState()->getGraphicsContext(), GL_FRAMEBUFFER_EXT);
//mSwapchain->framebuffer()->bindFramebuffer(info.getState()->getGraphicsContext(), GL_FRAMEBUFFER_EXT);
}
}
void VRViewer::postDrawCallback(osg::RenderInfo& info)

@ -109,7 +109,7 @@ namespace MWVR
void initialDrawCallback(osg::RenderInfo& info);
void preDrawCallback(osg::RenderInfo& info);
void postDrawCallback(osg::RenderInfo& info);
void blitEyesToMirrorTexture(osg::GraphicsContext* gc);
void blit(osg::GraphicsContext* gc);
void configureXR(osg::GraphicsContext* gc);
void configureCallbacks();
void setupMirrorTexture();
@ -129,9 +129,9 @@ namespace MWVR
osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr;
osg::ref_ptr<PredrawCallback> mPreDraw{ nullptr };
osg::ref_ptr<PostdrawCallback> mPostDraw{ nullptr };
std::shared_ptr<UpdateViewCallback> mUpdateViewCallback{ nullptr };
bool mRenderingReady{ false };
std::unique_ptr<VRFramebuffer> mMsaaResolveMirrorTexture;
std::unique_ptr<VRFramebuffer> mMirrorTexture;
std::vector<std::string> mMirrorTextureViews;
bool mMirrorTextureShouldBeCleanedUp{ false };
@ -139,9 +139,11 @@ namespace MWVR
bool mFlipMirrorTextureOrder{ false };
MirrorTextureEye mMirrorTextureEye{ MirrorTextureEye::Both };
std::unique_ptr<OpenXRSwapchain> mSwapchain;
std::unique_ptr<VRFramebuffer> mFramebuffer;
std::unique_ptr<VRFramebuffer> mMsaaResolveTexture;
std::array<std::unique_ptr<OpenXRSwapchain>, 2> mSwapchain;
std::array<SubImage, 2> mSubImages;
SwapchainConfig mSwapchainConfig;
std::array<SwapchainConfig, 2> mSwapchainConfig;
};
}

@ -485,11 +485,13 @@ namespace Misc
View right{};
double near_ = 1.f;
double far_ = 10000.f;
if (!mUpdateViewCallback)
auto updateViewCallback = mUpdateViewCallback.lock();
if (!updateViewCallback)
{
Log(Debug::Error) << "No update view callback. Stereo rendering will not work.";
Log(Debug::Error) << "StereoView: No update view callback. Stereo rendering will not work.";
return;
}
mUpdateViewCallback->updateView(left, right);
updateViewCallback->updateView(left, right);
near_ = Settings::Manager::getFloat("near clip", "Camera");
far_ = Settings::Manager::getFloat("viewing distance", "Camera");

@ -48,10 +48,10 @@ namespace Misc
//! Fov that defines all 4 angles from center
struct FieldOfView {
float angleLeft{ -osg::PI_2 };
float angleRight{ osg::PI_2 };
float angleDown{ -osg::PI_2 };
float angleUp{ osg::PI_2 };
float angleLeft{ 0.f };
float angleRight{ 0.f };
float angleUp{ 0.f };
float angleDown{ 0.f };
bool operator==(const FieldOfView& rhs) const;
@ -173,7 +173,7 @@ namespace Misc
bool flipViewOrder{ true };
// Updates stereo configuration during the update pass
std::shared_ptr<UpdateViewCallback> mUpdateViewCallback{ new DefaultUpdateViewCallback };
std::weak_ptr<UpdateViewCallback> mUpdateViewCallback{};
// OSG camera callbacks set using set*callback. StereoView manages that these are always set on the appropriate camera(s);
osg::ref_ptr<osg::NodeCallback> mCullCallback{ nullptr };

@ -963,7 +963,7 @@ stereo enabled = false
# Must be one of the following: BruteForce, GeometryShader
# BruteForce: Generates stereo using two cameras and two cull/render passes. Choose this if your game is GPU-bound.
# GeometryShader: Generates stereo in a single pass using automatically generated geometry shaders. May break custom shaders. Choose this if your game is CPU-bound.
stereo method = GeometryShader
stereo method = BruteForce
# May accelerate the BruteForce method when shadows are enabled
shared shadow maps = true
@ -1003,6 +1003,9 @@ hand directed movement = false
# Valid options are: top, wrist
left hand hud position = wrist
# If true, OpenMW will try to use DirectX swapchains instead of OpenGL swapchains. They are HW bridged to OpenGL using the WGL_NV_DX_interop2 extension.
# As the general quality of OpenXR DirectX runtimes is better than OpenGL runtimes, i default this to true.
Prefer DirectX swapchains = true
[VR Debug]
# 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.

Loading…
Cancel
Save