1076 lines
31 KiB
C++
1076 lines
31 KiB
C++
#include "ixr.h"
|
|
#include "openxr/openxr.h"
|
|
#include "vulkan/vulkan_core.h"
|
|
#undef __cplusplus
|
|
#include "vulkan/vk_enum_string_helper.h"
|
|
#define __cplusplus 202400L
|
|
#define XR_USE_GRAPHICS_API_VULKAN
|
|
#include "openxr/openxr_platform.h"
|
|
#include "tier0/lib.h"
|
|
#include "tier1/utlvector.h"
|
|
#include "materialsystem/imaterialsystem.h"
|
|
#include "tier1/utlstring.h"
|
|
#include "tier2/fileformats/json.h"
|
|
#include "tier2/ifilesystem.h"
|
|
|
|
|
|
|
|
static XrPath OpenXrGetPath( XrInstance i, const char *szString )
|
|
{
|
|
XrPath xrPath;
|
|
xrStringToPath(i, szString, &xrPath);
|
|
return xrPath;
|
|
}
|
|
|
|
static CUtlString OpenXrGetString( XrInstance i, XrPath path )
|
|
{
|
|
uint32_t l;
|
|
char t[XR_MAX_PATH_LENGTH];
|
|
XrResult r = xrPathToString(i, path, XR_MAX_PATH_LENGTH, &l, t);
|
|
if (r)
|
|
return NULL;
|
|
return t;
|
|
|
|
}
|
|
|
|
struct EyeSwapchain_t
|
|
{
|
|
XrSwapchain m_swapchain;
|
|
uint32_t m_uWidth;
|
|
uint32_t m_uHeight;
|
|
uint32_t m_uImageIndex;
|
|
CUtlVector<IImage*> m_pImages;
|
|
IImage *m_pUserImage;
|
|
};
|
|
class COpenXRManager;
|
|
class COpenXRHeadset;
|
|
|
|
struct OpenXRAction
|
|
{
|
|
CUtlString m_szName;
|
|
XrAction m_action;
|
|
XrActionType m_eType;
|
|
XrSpace m_space;
|
|
EXRInputType_t m_eUserInputType;
|
|
};
|
|
|
|
class COpenXRController: public IXRController
|
|
{
|
|
public:
|
|
virtual EXRControllerType_t GetControllerType() override;
|
|
virtual EXRControllerSide_t GetControllerSide() override;
|
|
virtual IXRHeadset *GetHeadset() override;
|
|
virtual void SetInputCallback( XRInputCallbackFn fn ) override;
|
|
|
|
void Frame();
|
|
void ConfigureActionSets( const char *szDevice, const char *szProfile );
|
|
|
|
COpenXRHeadset *m_pHeadset;
|
|
EXRControllerType_t m_type;
|
|
EXRControllerSide_t m_side;
|
|
CUtlVector<OpenXRAction> m_actions;
|
|
XrActionSet m_actionSet = XR_NULL_HANDLE;
|
|
|
|
XRInputCallbackFn m_callback = NULL;
|
|
};
|
|
|
|
class COpenXRHeadset: public IXRHeadset, public IAppSystem
|
|
{
|
|
public:
|
|
virtual void Init() override;
|
|
virtual void Shutdown() override;
|
|
|
|
virtual uint32_t GetSurfaceCount() override;
|
|
virtual XRRenderSurface_t GetSurface( uint32_t i ) override;
|
|
virtual void SetSurfaceImage( uint32_t i, IImage *pImage ) override;
|
|
|
|
virtual uint32_t GetControllerCount() override;
|
|
virtual IXRController *GetController( uint32_t i ) override;
|
|
|
|
void Frame();
|
|
void BeforeRender();
|
|
void AfterRender();
|
|
void CopySwapchain();
|
|
void CreateSwapchainForEye( XrViewConfigurationView *pConf, VkFormat eVkFormat, EyeSwapchain_t *pEye );
|
|
|
|
COpenXRManager *m_pManager;
|
|
XrSystemId m_systemId;
|
|
XrSession m_session;
|
|
XrSpace m_space;
|
|
|
|
CUtlVector<EyeSwapchain_t> m_eyeSwapchains = {};
|
|
CUtlVector<XrCompositionLayerProjectionView> m_projviews = {};
|
|
CUtlVector<XrView> m_views = {};
|
|
float m_fPredictedTime;
|
|
|
|
COpenXRController *m_pLeftHand;
|
|
COpenXRController *m_pRightHand;
|
|
|
|
};
|
|
|
|
enum EOpenXRAPI
|
|
{
|
|
OPENXR_API_VULKAN,
|
|
OPENXR_API_VULKAN2,
|
|
};
|
|
|
|
class COpenXRManager: public IXRManager
|
|
{
|
|
public:
|
|
virtual void Init() override;
|
|
virtual void Shutdown() override;
|
|
virtual void ConnectInterface( void *pIface, const char *szName ) override;
|
|
virtual void Frame() override;
|
|
virtual void PreRender() override;
|
|
virtual void PostRender() override;
|
|
virtual void CopySwapchain() override;
|
|
|
|
virtual uint32_t GetHeadsetCount() override;
|
|
virtual IXRHeadset *GetHeadset( uint32_t i ) override;
|
|
|
|
bool bHasXR = false;
|
|
IRenderContext *m_pRenderContext;
|
|
COpenXRHeadset m_headset;
|
|
|
|
XrInstance m_instance;
|
|
|
|
bool m_bIsDebugUtilsSupported = false;
|
|
bool m_bIsVulkanSupported = false;
|
|
bool m_bIsVulkan2Supported = false;
|
|
EOpenXRAPI m_gpuAPI;
|
|
|
|
};
|
|
EXPOSE_INTERFACE(COpenXRManager, IXRManager, OPEN_XR_INTERFACE_VERSION);
|
|
|
|
EXRControllerType_t COpenXRController::GetControllerType()
|
|
{
|
|
return m_type;
|
|
}
|
|
|
|
EXRControllerSide_t COpenXRController::GetControllerSide()
|
|
{
|
|
return m_side;
|
|
|
|
}
|
|
|
|
IXRHeadset *COpenXRController::GetHeadset()
|
|
{
|
|
return m_pHeadset;
|
|
}
|
|
|
|
void COpenXRController::SetInputCallback( XRInputCallbackFn fn )
|
|
{
|
|
m_callback = fn;
|
|
}
|
|
|
|
void COpenXRController::Frame()
|
|
{
|
|
for (auto &a: m_actions)
|
|
{
|
|
switch(a.m_eType)
|
|
{
|
|
|
|
case XR_ACTION_TYPE_BOOLEAN_INPUT:
|
|
{
|
|
XrActionStateGetInfo gi = {};
|
|
gi.type = XR_TYPE_ACTION_STATE_GET_INFO;
|
|
gi.action = a.m_action;
|
|
XrActionStateBoolean b = {};
|
|
b.type = XR_TYPE_ACTION_STATE_BOOLEAN;
|
|
xrGetActionStateBoolean(m_pHeadset->m_session, &gi, &b);
|
|
EXRInputValue_t v;
|
|
v.type = k_EXRValue_Bool;
|
|
v.b.value = b.currentState;
|
|
if (m_callback)
|
|
m_callback( this, a.m_eUserInputType, NULL, k_EXRInputAction_Value, v );
|
|
break;
|
|
}
|
|
case XR_ACTION_TYPE_FLOAT_INPUT:
|
|
{
|
|
XrActionStateGetInfo gi = {};
|
|
gi.type = XR_TYPE_ACTION_STATE_GET_INFO;
|
|
gi.action = a.m_action;
|
|
XrActionStateFloat f32 = {};
|
|
f32.type = XR_TYPE_ACTION_STATE_FLOAT;
|
|
xrGetActionStateFloat(m_pHeadset->m_session, &gi, &f32);
|
|
EXRInputValue_t v;
|
|
v.type = k_EXRValue_Float;
|
|
v.f32.value = f32.currentState;
|
|
if (m_callback)
|
|
m_callback( this, a.m_eUserInputType, NULL, k_EXRInputAction_Value, v );
|
|
break;
|
|
}
|
|
case XR_ACTION_TYPE_POSE_INPUT:
|
|
{
|
|
XrActionStateGetInfo gi = {};
|
|
gi.type = XR_TYPE_ACTION_STATE_GET_INFO;
|
|
gi.action = a.m_action;
|
|
XrActionStatePose pose = {};
|
|
pose.type = XR_TYPE_ACTION_STATE_POSE;
|
|
|
|
xrGetActionStatePose(m_pHeadset->m_session, &gi, &pose);
|
|
if (pose.isActive)
|
|
{
|
|
XrSpaceLocation s = {};
|
|
s.type = XR_TYPE_SPACE_LOCATION;
|
|
xrLocateSpace(a.m_space, m_pHeadset->m_space, m_pHeadset->m_fPredictedTime, &s);
|
|
EXRInputValue_t v;
|
|
v.type = k_EXRValue_Pose;
|
|
v.pose.pos = {s.pose.position.x, s.pose.position.y, s.pose.position.z};
|
|
v.pose.rot = {s.pose.orientation.x, s.pose.orientation.y, s.pose.orientation.z, s.pose.orientation.w};
|
|
if (m_callback)
|
|
m_callback( this, a.m_eUserInputType, NULL, k_EXRInputAction_Pose, v );
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
void COpenXRController::ConfigureActionSets( const char *szDevice, const char *szProfile )
|
|
{
|
|
XrActionSetCreateInfo setci = {};
|
|
setci.type = XR_TYPE_ACTION_SET_CREATE_INFO;
|
|
strcpy(setci.actionSetName, "funny");
|
|
strcpy(setci.localizedActionSetName, "funny");
|
|
xrCreateActionSet(m_pHeadset->m_pManager->m_instance, &setci, &m_actionSet);
|
|
|
|
XrSessionActionSetsAttachInfo attachi = {};
|
|
attachi.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO;
|
|
attachi.actionSets = &m_actionSet;
|
|
attachi.countActionSets = 1;
|
|
xrAttachSessionActionSets(m_pHeadset->m_session, &attachi);
|
|
|
|
CUtlString szPath = CUtlString("game/core/openxr%s.json", szProfile);
|
|
IFileHandle *pHandle = filesystem->Open(szPath, FILEMODE_READ);
|
|
const char *sz = filesystem->ReadString(pHandle);
|
|
IJSONValue *pVal = JSONManager()->ReadString(sz);
|
|
IJSONValue *pDevice = (*pVal)[szDevice];
|
|
IJSONValue *pInputs = (*pDevice)["inputs"];
|
|
if (!pInputs)
|
|
return;
|
|
for (uint32_t i = 0; i < pInputs->GetArray()->GetCount(); i++ )
|
|
{
|
|
IJSONValue *pInput = (*pInputs)[i];
|
|
CUtlString szInputName = (*pInput)["name"]->GetStringValue();
|
|
EXRInputType_t eInputType = k_EXRInput_FromString;
|
|
if (szInputName == "trackpad")
|
|
eInputType = k_EXRInput_Trackpad;
|
|
if (szInputName == "thumbstick")
|
|
eInputType = k_EXRInput_Thumbstick;
|
|
if (szInputName == "joystick")
|
|
eInputType = k_EXRInput_Joystick;
|
|
if (szInputName == "trigger")
|
|
eInputType = k_EXRInput_Trigger;
|
|
if (szInputName == "throttle")
|
|
eInputType = k_EXRInput_Throttle;
|
|
if (szInputName == "trackball")
|
|
eInputType = k_EXRInput_Trackball;
|
|
if (szInputName == "pedal")
|
|
eInputType = k_EXRInput_Pedal;
|
|
if (szInputName == "system")
|
|
eInputType = k_EXRInput_System;
|
|
if (szInputName == "dpad_up")
|
|
eInputType = k_EXRInput_Dpad_Up;
|
|
if (szInputName == "dpad_down")
|
|
eInputType = k_EXRInput_Dpad_Down;
|
|
if (szInputName == "dpad_left")
|
|
eInputType = k_EXRInput_Dpad_Left;
|
|
if (szInputName == "dpad_right")
|
|
eInputType = k_EXRInput_Dpad_Right;
|
|
if (szInputName == "diamond_up")
|
|
eInputType = k_EXRInput_Diamond_Up;
|
|
if (szInputName == "diamond_down")
|
|
eInputType = k_EXRInput_Diamond_Down;
|
|
if (szInputName == "diamond_left")
|
|
eInputType = k_EXRInput_Diamond_Left;
|
|
if (szInputName == "diamond_right")
|
|
eInputType = k_EXRInput_Diamond_Right;
|
|
if (szInputName == "a")
|
|
eInputType = k_EXRInput_A;
|
|
if (szInputName == "b")
|
|
eInputType = k_EXRInput_B;
|
|
if (szInputName == "x")
|
|
eInputType = k_EXRInput_X;
|
|
if (szInputName == "y")
|
|
eInputType = k_EXRInput_Y;
|
|
if (szInputName == "start")
|
|
eInputType = k_EXRInput_Start;
|
|
if (szInputName == "home")
|
|
eInputType = k_EXRInput_Home;
|
|
if (szInputName == "end")
|
|
eInputType = k_EXRInput_End;
|
|
if (szInputName == "select")
|
|
eInputType = k_EXRInput_Select;
|
|
if (szInputName == "volume_up")
|
|
eInputType = k_EXRInput_Volume_Up;
|
|
if (szInputName == "volume_down")
|
|
eInputType = k_EXRInput_Volume_Down;
|
|
if (szInputName == "mute_mic")
|
|
eInputType = k_EXRInput_Mute_Mic;
|
|
if (szInputName == "play_pause")
|
|
eInputType = k_EXRInput_Play_Pause;
|
|
if (szInputName == "menu")
|
|
eInputType = k_EXRInput_Menu;
|
|
if (szInputName == "view")
|
|
eInputType = k_EXRInput_View;
|
|
if (szInputName == "back")
|
|
eInputType = k_EXRInput_Back;
|
|
if (szInputName == "thumbrest")
|
|
eInputType = k_EXRInput_ThumbRest;
|
|
if (szInputName == "shoulder")
|
|
eInputType = k_EXRInput_Shoulder;
|
|
if (szInputName == "squeeze")
|
|
eInputType = k_EXRInput_Squeeze;
|
|
if (szInputName == "wheel")
|
|
eInputType = k_EXRInput_Wheel;
|
|
if (szInputName == "grip")
|
|
eInputType = k_EXRInput_Grip;
|
|
if (szInputName == "aim")
|
|
eInputType = k_EXRInput_Aim;
|
|
|
|
|
|
IJSONValue *pActions = (*pInput)["actions"];
|
|
for (uint32_t y = 0; y < pActions->GetArray()->GetCount(); y++ )
|
|
{
|
|
IJSONValue *pAction = (*pActions)[y];
|
|
CUtlString szType = pAction->GetStringValue();
|
|
XrActionType eType = XR_ACTION_TYPE_MAX_ENUM;
|
|
if (szType == "click")
|
|
eType = XR_ACTION_TYPE_BOOLEAN_INPUT;
|
|
if (szType == "touch")
|
|
eType = XR_ACTION_TYPE_BOOLEAN_INPUT;
|
|
if (szType == "force")
|
|
eType = XR_ACTION_TYPE_FLOAT_INPUT;
|
|
if (szType == "value")
|
|
eType = XR_ACTION_TYPE_FLOAT_INPUT;
|
|
if (szType == "x")
|
|
eType = XR_ACTION_TYPE_FLOAT_INPUT;
|
|
if (szType == "y")
|
|
eType = XR_ACTION_TYPE_FLOAT_INPUT;
|
|
if (szType == "twist")
|
|
eType = XR_ACTION_TYPE_FLOAT_INPUT;
|
|
if (szType == "pose")
|
|
eType = XR_ACTION_TYPE_POSE_INPUT;
|
|
|
|
XrAction a = XR_NULL_HANDLE;
|
|
XrActionCreateInfo aci = {};
|
|
aci.type = XR_TYPE_ACTION_CREATE_INFO;
|
|
aci.actionType = eType;
|
|
CUtlString szName = CUtlString("%s/input/%s/%s", szDevice, szInputName.GetString(), szType.GetString());
|
|
V_strncpy(aci.actionName, szName.GetString(), XR_MAX_ACTION_NAME_SIZE);
|
|
V_strncpy(aci.localizedActionName, szName.GetString(), XR_MAX_LOCALIZED_ACTION_NAME_SIZE);
|
|
XrResult r = xrCreateAction(m_actionSet, &aci, &a);
|
|
|
|
OpenXRAction action = {};
|
|
action.m_szName = szName;
|
|
action.m_action = a;
|
|
action.m_eType = eType;
|
|
action.m_eUserInputType = eInputType;
|
|
if (eType == XR_ACTION_TYPE_POSE_INPUT)
|
|
{
|
|
XrActionSpaceCreateInfo asci = {};
|
|
asci.type = XR_TYPE_ACTION_SPACE_CREATE_INFO;
|
|
asci.poseInActionSpace = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}};
|
|
asci.action = a;
|
|
XrResult r = xrCreateActionSpace(m_pHeadset->m_session, &asci, &action.m_space);
|
|
}
|
|
|
|
m_actions.AppendTail(action);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
CUtlVector<XrActionSuggestedBinding> suggestions = {};
|
|
suggestions.Reserve(m_actions.GetSize());
|
|
for ( auto &a: m_actions )
|
|
{
|
|
suggestions.AppendTail({a.m_action, OpenXrGetPath(m_pHeadset->m_pManager->m_instance, a.m_szName)});
|
|
}
|
|
|
|
XrInteractionProfileSuggestedBinding binding = {};
|
|
binding.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
|
|
binding.interactionProfile = OpenXrGetPath(m_pHeadset->m_pManager->m_instance, szProfile);
|
|
binding.countSuggestedBindings = suggestions.GetSize();
|
|
binding.suggestedBindings = suggestions.GetData();
|
|
xrSuggestInteractionProfileBindings(m_pHeadset->m_pManager->m_instance, &binding);
|
|
JSONManager()->FreeValue(pVal);
|
|
V_free((void*)sz);
|
|
|
|
}
|
|
|
|
void COpenXRManager::Init()
|
|
{
|
|
PFN_xrInitializeLoaderKHR fnInitLoader = NULL;
|
|
XrResult r = xrGetInstanceProcAddr(NULL, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)&fnInitLoader);
|
|
if (r != XR_SUCCESS)
|
|
{
|
|
V_printf("Failed to load OpenXR, we are not running OpenXR\n");
|
|
return;
|
|
}
|
|
|
|
uint32_t nInstanceExtension = 0;
|
|
xrEnumerateInstanceExtensionProperties(NULL, 0, &nInstanceExtension, NULL);
|
|
CUtlVector<XrExtensionProperties> availableExts(nInstanceExtension);
|
|
for (auto &e: availableExts)
|
|
e.type = XR_TYPE_EXTENSION_PROPERTIES;
|
|
xrEnumerateInstanceExtensionProperties(NULL, nInstanceExtension, &nInstanceExtension, availableExts.GetData());
|
|
|
|
CUtlVector<const char *> extensions = {};
|
|
for ( auto &e: availableExts )
|
|
{
|
|
if (!V_strncmp(XR_EXT_DEBUG_UTILS_EXTENSION_NAME, e.extensionName, XR_MAX_EXTENSION_NAME_SIZE))
|
|
{
|
|
extensions.AppendTail(XR_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
|
m_bIsDebugUtilsSupported = false;
|
|
}
|
|
if (!V_strncmp(XR_KHR_VULKAN_ENABLE_EXTENSION_NAME, e.extensionName, XR_MAX_EXTENSION_NAME_SIZE))
|
|
{
|
|
extensions.AppendTail(XR_KHR_VULKAN_ENABLE_EXTENSION_NAME);
|
|
m_bIsVulkanSupported = true;
|
|
}
|
|
if (!V_strncmp(XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME, e.extensionName, XR_MAX_EXTENSION_NAME_SIZE))
|
|
{
|
|
extensions.AppendTail(XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME);
|
|
m_bIsVulkan2Supported = true;
|
|
}
|
|
}
|
|
|
|
if (m_pRenderContext->GetVulkanInstance() != NULL)
|
|
{
|
|
if (m_bIsVulkan2Supported) {
|
|
if (m_bIsVulkanSupported)
|
|
m_gpuAPI = OPENXR_API_VULKAN;
|
|
else
|
|
m_gpuAPI = OPENXR_API_VULKAN2;
|
|
}
|
|
if (m_bIsVulkanSupported)
|
|
{
|
|
m_gpuAPI = OPENXR_API_VULKAN;
|
|
}
|
|
}
|
|
for (auto &e: extensions)
|
|
V_printf("%s\n", e);
|
|
|
|
|
|
|
|
const char *layers[] = {
|
|
"XR_APILAYER_LUNARG_core_validation"
|
|
};
|
|
|
|
XrApplicationInfo appInfo = {};
|
|
appInfo.apiVersion = XR_API_VERSION_1_1;
|
|
V_strncpy(appInfo.applicationName, "funny", XR_MAX_APPLICATION_NAME_SIZE);
|
|
V_strncpy(appInfo.engineName, "funny", XR_MAX_ENGINE_NAME_SIZE);
|
|
|
|
XrInstanceCreateInfo createInfo = {};
|
|
createInfo.type = XR_TYPE_INSTANCE_CREATE_INFO;
|
|
createInfo.applicationInfo = appInfo;
|
|
createInfo.enabledExtensionCount = extensions.GetSize();
|
|
createInfo.enabledExtensionNames = extensions.GetData();
|
|
createInfo.enabledApiLayerCount = sizeof(layers)/sizeof(*layers);
|
|
createInfo.enabledApiLayerNames = layers;
|
|
r = xrCreateInstance(&createInfo, &m_instance);
|
|
if (r != XR_SUCCESS)
|
|
{
|
|
V_printf("xrCreateInstance failed, we are not running OpenXR\n");
|
|
return;
|
|
}
|
|
|
|
m_headset.m_pManager = this;
|
|
m_headset.Init();
|
|
|
|
XrInstanceProperties props = {};
|
|
props.type = XR_TYPE_INSTANCE_PROPERTIES;
|
|
xrGetInstanceProperties(m_instance, &props);
|
|
V_printf("OpenXR runtime: %s\n", props.runtimeName);
|
|
|
|
bHasXR = true;
|
|
|
|
}
|
|
|
|
|
|
void COpenXRManager::Shutdown()
|
|
{
|
|
|
|
}
|
|
void COpenXRManager::Frame()
|
|
{
|
|
if (bHasXR)
|
|
m_headset.Frame();
|
|
}
|
|
|
|
void COpenXRManager::PreRender()
|
|
{
|
|
if (bHasXR)
|
|
m_headset.BeforeRender();
|
|
}
|
|
|
|
void COpenXRManager::PostRender()
|
|
{
|
|
if (bHasXR)
|
|
m_headset.AfterRender();
|
|
}
|
|
void COpenXRManager::CopySwapchain()
|
|
{
|
|
if (bHasXR)
|
|
m_headset.CopySwapchain();
|
|
|
|
}
|
|
|
|
struct OpenXRProfile_t {
|
|
const char *m_szName;
|
|
EXRControllerType_t m_eType;
|
|
};
|
|
|
|
const static CUtlVector<OpenXRProfile_t> s_interactionProfiles = {
|
|
{"/interaction_profiles/khr/simple_controller", k_EXR_HandController},
|
|
{"/interaction_profiles/oculus/touch_controller", k_EXR_HandController},
|
|
};
|
|
|
|
void COpenXRHeadset::Init()
|
|
{
|
|
|
|
XrSystemGetInfo sgi = {};
|
|
XrResult r;
|
|
sgi.type = XR_TYPE_SYSTEM_GET_INFO;
|
|
sgi.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
|
|
r = xrGetSystem(m_pManager->m_instance, &sgi, &m_systemId);
|
|
if (r != XR_SUCCESS)
|
|
Plat_FatalErrorFunc("xrGetSystem\n");
|
|
|
|
/* each backend requires its own shit */
|
|
|
|
switch (m_pManager->m_gpuAPI)
|
|
{
|
|
case OPENXR_API_VULKAN:
|
|
{
|
|
XrGraphicsRequirementsVulkanKHR requirements = {};
|
|
requirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR;
|
|
PFN_xrGetVulkanGraphicsRequirementsKHR fnVulkanRequirements = NULL;
|
|
xrGetInstanceProcAddr(m_pManager->m_instance, "xrGetVulkanGraphicsRequirementsKHR", (PFN_xrVoidFunction*)&fnVulkanRequirements);
|
|
if (r != XR_SUCCESS)
|
|
Plat_FatalErrorFunc("Failed to get xrGetVulkanGraphicsRequirementsKHR\n");
|
|
fnVulkanRequirements( m_pManager->m_instance, m_systemId, &requirements);
|
|
|
|
|
|
PFN_xrGetVulkanGraphicsDeviceKHR fnVulkanDevice = NULL;
|
|
xrGetInstanceProcAddr(m_pManager->m_instance, "xrGetVulkanGraphicsDeviceKHR", (PFN_xrVoidFunction*)&fnVulkanDevice);
|
|
if (r != XR_SUCCESS)
|
|
Plat_FatalErrorFunc("Failed to get xrGetVulkanGraphicsDeviceKHR\n");
|
|
VkPhysicalDevice d = NULL;
|
|
fnVulkanDevice(m_pManager->m_instance, m_systemId, (VkInstance)m_pManager->m_pRenderContext->GetVulkanInstance(), &d);
|
|
|
|
XrGraphicsBindingVulkan2KHR vkBinding = {};
|
|
vkBinding.type = XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR;
|
|
vkBinding.instance = (VkInstance)m_pManager->m_pRenderContext->GetVulkanInstance();
|
|
vkBinding.physicalDevice = (VkPhysicalDevice)m_pManager->m_pRenderContext->GetVulkanPhysicalDevice();
|
|
vkBinding.device = (VkDevice)m_pManager->m_pRenderContext->GetVulkanDevice();
|
|
|
|
XrSessionCreateInfo sessionCreateInfo = {};
|
|
sessionCreateInfo.type = XR_TYPE_SESSION_CREATE_INFO;
|
|
sessionCreateInfo.next = &vkBinding;
|
|
sessionCreateInfo.systemId = m_systemId;
|
|
r = xrCreateSession(m_pManager->m_instance, &sessionCreateInfo, &m_session);
|
|
if (r != XR_SUCCESS)
|
|
Plat_FatalErrorFunc("xrCreateSession\n");
|
|
}
|
|
break;
|
|
|
|
case OPENXR_API_VULKAN2:
|
|
{
|
|
XrGraphicsRequirementsVulkanKHR requirements = {};
|
|
requirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR;
|
|
PFN_xrGetVulkanGraphicsRequirements2KHR fnVulkanRequirements = NULL;
|
|
xrGetInstanceProcAddr(m_pManager->m_instance, "xrGetVulkanGraphicsRequirements2KHR", (PFN_xrVoidFunction*)&fnVulkanRequirements);
|
|
if (r != XR_SUCCESS)
|
|
Plat_FatalErrorFunc("Failed to get xrGetVulkanGraphicsRequirements2KHR\n");
|
|
fnVulkanRequirements( m_pManager->m_instance, m_systemId, &requirements);
|
|
|
|
|
|
PFN_xrGetVulkanGraphicsDevice2KHR fnVulkanDevice = NULL;
|
|
xrGetInstanceProcAddr(m_pManager->m_instance, "xrGetVulkanGraphicsDevice2KHR", (PFN_xrVoidFunction*)&fnVulkanDevice);
|
|
if (r != XR_SUCCESS)
|
|
Plat_FatalErrorFunc("Failed to get xrGetVulkanGraphicsDevice2KHR\n");
|
|
VkPhysicalDevice d = NULL;
|
|
XrVulkanGraphicsDeviceGetInfoKHR dgi = {};
|
|
dgi.type = XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR;
|
|
dgi.systemId = m_systemId;
|
|
dgi.vulkanInstance = (VkInstance)m_pManager->m_pRenderContext->GetVulkanInstance();
|
|
fnVulkanDevice(m_pManager->m_instance, &dgi, &d);
|
|
|
|
XrGraphicsBindingVulkan2KHR vkBinding = {};
|
|
vkBinding.type = XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR;
|
|
vkBinding.instance = (VkInstance)m_pManager->m_pRenderContext->GetVulkanInstance();
|
|
vkBinding.physicalDevice = (VkPhysicalDevice)m_pManager->m_pRenderContext->GetVulkanPhysicalDevice();
|
|
vkBinding.device = (VkDevice)m_pManager->m_pRenderContext->GetVulkanDevice();
|
|
|
|
XrSessionCreateInfo sessionCreateInfo = {};
|
|
sessionCreateInfo.type = XR_TYPE_SESSION_CREATE_INFO;
|
|
sessionCreateInfo.next = &vkBinding;
|
|
sessionCreateInfo.systemId = m_systemId;
|
|
r = xrCreateSession(m_pManager->m_instance, &sessionCreateInfo, &m_session);
|
|
if (r != XR_SUCCESS)
|
|
Plat_FatalErrorFunc("xrCreateSession\n");
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
/* create swapchains for rendering pipeline */
|
|
|
|
uint32_t formatCount = 0;
|
|
xrEnumerateSwapchainFormats(m_session, 0, &formatCount, NULL);
|
|
|
|
CUtlVector<int64_t> formats = formatCount;
|
|
xrEnumerateSwapchainFormats(m_session, formatCount, &formatCount, formats.GetData());
|
|
|
|
const VkFormat preferedSurfaceFormats[] = {
|
|
VK_FORMAT_B8G8R8A8_UNORM,
|
|
VK_FORMAT_R8G8B8A8_UNORM,
|
|
VK_FORMAT_R8G8B8A8_SRGB,
|
|
};
|
|
|
|
VkFormat eSelectedFormat = preferedSurfaceFormats[0];
|
|
for (auto &format: formats)
|
|
{
|
|
for (int i = 0; i < sizeof(preferedSurfaceFormats)/sizeof(VkFormat); i++)
|
|
{
|
|
if (format == preferedSurfaceFormats[i])
|
|
{
|
|
eSelectedFormat = preferedSurfaceFormats[i];
|
|
goto formatPicked;
|
|
}
|
|
}
|
|
}
|
|
formatPicked:
|
|
|
|
uint32_t viewCount = 0;
|
|
xrEnumerateViewConfigurationViews(m_pManager->m_instance, m_systemId, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &viewCount, NULL);
|
|
CUtlVector<XrViewConfigurationView> views = viewCount;
|
|
for ( auto &v: views )
|
|
{
|
|
v.type = XR_TYPE_VIEW_CONFIGURATION_VIEW;
|
|
}
|
|
xrEnumerateViewConfigurationViews(m_pManager->m_instance, m_systemId, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, viewCount, &viewCount, views.GetData());
|
|
|
|
for ( auto &v: views )
|
|
{
|
|
m_eyeSwapchains.AppendTail((EyeSwapchain_t){});
|
|
CreateSwapchainForEye(&v, eSelectedFormat, m_eyeSwapchains.GetData() + m_eyeSwapchains.GetSize() - 1);
|
|
}
|
|
|
|
XrReferenceSpaceCreateInfo spaceInfo = {};
|
|
spaceInfo.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
|
|
spaceInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
|
|
spaceInfo.poseInReferenceSpace = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}};
|
|
xrCreateReferenceSpace(m_session, &spaceInfo, &m_space);
|
|
m_fPredictedTime = 0;
|
|
m_views = m_eyeSwapchains.GetSize();
|
|
m_projviews = m_eyeSwapchains.GetSize();
|
|
for ( auto &v: m_views )
|
|
{
|
|
v = {};
|
|
v.type = XR_TYPE_VIEW;
|
|
}
|
|
for ( auto &v: m_projviews )
|
|
{
|
|
v = {};
|
|
v.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
|
|
}
|
|
|
|
XrViewState viewState = {};
|
|
viewState.type = XR_TYPE_VIEW_STATE;
|
|
XrViewLocateInfo viewLocale = {};
|
|
viewLocale.type = XR_TYPE_VIEW_LOCATE_INFO;
|
|
viewLocale.displayTime = m_fPredictedTime;
|
|
viewLocale.space = m_space;
|
|
viewLocale.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
|
|
uint32_t vc = m_views.GetSize();
|
|
xrLocateViews(m_session, &viewLocale, &viewState, vc, &vc, m_views.GetData());
|
|
|
|
|
|
|
|
for (auto &p: s_interactionProfiles) {
|
|
/* load profile */
|
|
|
|
XrInteractionProfileSuggestedBinding bindings = {};
|
|
bindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
|
|
bindings.interactionProfile = OpenXrGetPath(m_pManager->m_instance, p.m_szName);
|
|
xrSuggestInteractionProfileBindings(m_pManager->m_instance, &bindings);
|
|
}
|
|
|
|
CUtlVector<const char*> possibleControllers = {
|
|
"/user/hand/left",
|
|
"/user/hand/right",
|
|
};
|
|
|
|
XrActionSet quetySet = {};
|
|
XrActionSetCreateInfo setci = {};
|
|
setci.type = XR_TYPE_ACTION_SET_CREATE_INFO;
|
|
strcpy(setci.actionSetName, "funny");
|
|
strcpy(setci.localizedActionSetName, "funny");
|
|
xrCreateActionSet(m_pManager->m_instance, &setci, &quetySet);
|
|
XrSessionActionSetsAttachInfo attachi = {};
|
|
attachi.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO;
|
|
attachi.actionSets = &quetySet;
|
|
attachi.countActionSets = 1;
|
|
xrAttachSessionActionSets(m_session, &attachi);
|
|
|
|
for ( auto &c: possibleControllers )
|
|
{
|
|
XrPath path = OpenXrGetPath(m_pManager->m_instance, c);
|
|
XrInteractionProfileState profile = {};
|
|
profile.type = XR_TYPE_INTERACTION_PROFILE_STATE;
|
|
XrResult r = xrGetCurrentInteractionProfile(m_session, path, &profile);
|
|
if (r)
|
|
{
|
|
V_printf("Controller not found: %s %i\n", c, r);
|
|
|
|
}
|
|
if (profile.interactionProfile == XR_NULL_PATH)
|
|
continue;
|
|
CUtlString s = OpenXrGetString(m_pManager->m_instance, profile.interactionProfile);
|
|
if (!V_strcmp(c, "/user/hand/left"))
|
|
{
|
|
m_pLeftHand = new COpenXRController;
|
|
m_pLeftHand->m_side = k_EXRController_Left;
|
|
m_pLeftHand->m_pHeadset = this;
|
|
for ( auto &p: s_interactionProfiles )
|
|
if (s == p.m_szName)
|
|
{
|
|
m_pLeftHand->m_type = p.m_eType;
|
|
m_pLeftHand->ConfigureActionSets(c, p.m_szName);
|
|
}
|
|
}
|
|
if (!V_strcmp(c, "/user/hand/right"))
|
|
{
|
|
m_pRightHand = new COpenXRController;
|
|
m_pRightHand->m_side = k_EXRController_Right;
|
|
m_pRightHand->m_pHeadset = this;
|
|
for ( auto &p: s_interactionProfiles )
|
|
if (s == p.m_szName)
|
|
{
|
|
m_pRightHand->m_type = p.m_eType;
|
|
m_pRightHand->ConfigureActionSets(c, p.m_szName);
|
|
}
|
|
}
|
|
}
|
|
|
|
xrDestroyActionSet(quetySet);
|
|
|
|
|
|
}
|
|
void COpenXRHeadset::Shutdown()
|
|
{
|
|
|
|
}
|
|
|
|
void COpenXRHeadset::Frame()
|
|
{
|
|
while (true)
|
|
{
|
|
XrEventDataBuffer event = {};
|
|
event.type = XR_TYPE_EVENT_DATA_BUFFER;
|
|
XrResult r = xrPollEvent(m_pManager->m_instance, &event);
|
|
if ( r != XR_SUCCESS )
|
|
break;
|
|
|
|
switch (event.type)
|
|
{
|
|
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
|
|
{
|
|
XrEventDataSessionStateChanged *e = (XrEventDataSessionStateChanged*)&event;
|
|
if ( e->state == XR_SESSION_STATE_READY )
|
|
{
|
|
XrSessionBeginInfo beginInfo = {};
|
|
beginInfo.type = XR_TYPE_SESSION_BEGIN_INFO;
|
|
beginInfo.primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
|
|
xrBeginSession(m_session, &beginInfo);
|
|
}
|
|
if ( e->state == XR_SESSION_STATE_STOPPING )
|
|
{
|
|
xrEndSession(m_session);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
XrFrameWaitInfo frameWait = {};
|
|
XrFrameState frameState = {};
|
|
frameState.type = XR_TYPE_FRAME_STATE;
|
|
frameWait.type = XR_TYPE_FRAME_WAIT_INFO;
|
|
xrWaitFrame(m_session, &frameWait, &frameState);
|
|
m_fPredictedTime = frameState.predictedDisplayTime;
|
|
|
|
|
|
XrActiveActionSet aas[2] = {};
|
|
aas[0].actionSet = m_pLeftHand->m_actionSet;
|
|
aas[1].actionSet = m_pRightHand->m_actionSet;
|
|
|
|
XrActionsSyncInfo si = {};
|
|
si.type = XR_TYPE_ACTIONS_SYNC_INFO;
|
|
si.activeActionSets = aas;
|
|
si.countActiveActionSets = 2;
|
|
xrSyncActions(m_session, &si);
|
|
|
|
m_views = m_eyeSwapchains.GetSize();
|
|
m_projviews = m_eyeSwapchains.GetSize();
|
|
for ( auto &v: m_views )
|
|
{
|
|
v = {};
|
|
v.type = XR_TYPE_VIEW;
|
|
}
|
|
for ( auto &v: m_projviews )
|
|
{
|
|
v = {};
|
|
v.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
|
|
}
|
|
|
|
XrViewState viewState = {};
|
|
viewState.type = XR_TYPE_VIEW_STATE;
|
|
XrViewLocateInfo viewLocale = {};
|
|
viewLocale.type = XR_TYPE_VIEW_LOCATE_INFO;
|
|
viewLocale.displayTime = m_fPredictedTime;
|
|
viewLocale.space = m_space;
|
|
viewLocale.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
|
|
uint32_t viewCount = m_views.GetSize();
|
|
xrLocateViews(m_session, &viewLocale, &viewState, viewCount, &viewCount, m_views.GetData());
|
|
|
|
if (m_pLeftHand)
|
|
m_pLeftHand->Frame();
|
|
if (m_pRightHand)
|
|
m_pRightHand->Frame();
|
|
}
|
|
|
|
void COpenXRHeadset::BeforeRender()
|
|
{
|
|
|
|
XrFrameBeginInfo begin = {};
|
|
begin.type = XR_TYPE_FRAME_BEGIN_INFO;
|
|
xrBeginFrame(m_session, &begin);
|
|
for ( uint32_t i = 0; i < m_views.GetSize(); i++ )
|
|
{
|
|
XrSwapchainImageAcquireInfo acq = {};
|
|
acq.type = XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO;
|
|
uint32_t idx = 0;
|
|
XrResult r = xrAcquireSwapchainImage(m_eyeSwapchains[i].m_swapchain, &acq, &idx);
|
|
m_eyeSwapchains[i].m_uImageIndex = idx;
|
|
if (r) return;
|
|
XrSwapchainImageWaitInfo wait = {};
|
|
wait.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO;
|
|
wait.timeout = XR_INFINITE_DURATION;
|
|
xrWaitSwapchainImage(m_eyeSwapchains[i].m_swapchain, &wait);
|
|
|
|
m_projviews[i].fov = m_views[i].fov;
|
|
m_projviews[i].pose = m_views[i].pose;
|
|
m_projviews[i].subImage.swapchain = m_eyeSwapchains[i].m_swapchain;
|
|
m_projviews[i].subImage.imageRect.extent.height = m_eyeSwapchains[i].m_pImages[idx]->GetImageWidth();
|
|
m_projviews[i].subImage.imageRect.extent.width = m_eyeSwapchains[i].m_pImages[idx]->GetImageHeight();
|
|
m_projviews[i].subImage.imageArrayIndex = 0;
|
|
}
|
|
|
|
}
|
|
void COpenXRHeadset::CopySwapchain()
|
|
{
|
|
IRenderContext *pCtx = m_pManager->m_pRenderContext;
|
|
IRenderCommandList *pList = pCtx->CreateCommandList();
|
|
pList->StartRecording();
|
|
for ( auto &eye: m_eyeSwapchains)
|
|
{
|
|
ImageSector_t sector = {};
|
|
sector.m_iWidth = eye.m_uWidth;
|
|
sector.m_iHeight = eye.m_uHeight;
|
|
if (eye.m_pUserImage)
|
|
pList->BlitImageToImage(eye.m_pUserImage, sector, eye.m_pImages[eye.m_uImageIndex], sector);
|
|
}
|
|
pList->EndRecording();
|
|
pCtx->SubmitCommandList(pList);
|
|
pCtx->DestroyCommandList(pList);
|
|
}
|
|
|
|
void COpenXRHeadset::AfterRender()
|
|
{
|
|
for ( uint32_t i = 0; i < m_views.GetSize(); i++ )
|
|
{
|
|
XrSwapchainImageReleaseInfo rel = {};
|
|
rel.type = XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO;
|
|
xrReleaseSwapchainImage(m_eyeSwapchains[i].m_swapchain, &rel);
|
|
}
|
|
|
|
XrCompositionLayerProjection proj = {};
|
|
proj.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
|
|
proj.space = m_space;
|
|
proj.viewCount = m_projviews.GetSize();
|
|
proj.views = m_projviews.GetData();
|
|
XrCompositionLayerBaseHeader *compositions[] = {
|
|
(XrCompositionLayerBaseHeader*)&proj
|
|
};
|
|
|
|
XrFrameEndInfo end = {};
|
|
end.type = XR_TYPE_FRAME_END_INFO;
|
|
end.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
|
|
end.layerCount = 1;
|
|
end.layers = compositions;
|
|
end.displayTime = m_fPredictedTime;
|
|
xrEndFrame(m_session, &end);
|
|
}
|
|
|
|
uint32_t COpenXRHeadset::GetSurfaceCount()
|
|
{
|
|
return m_eyeSwapchains.GetSize();
|
|
}
|
|
|
|
XRRenderSurface_t COpenXRHeadset::GetSurface( uint32_t i )
|
|
{
|
|
XRRenderSurface_t surface = {};
|
|
surface.m_fFov = m_views[i].fov.angleLeft-m_views[i].fov.angleRight;
|
|
surface.m_uWidth = m_eyeSwapchains[i].m_uWidth;
|
|
surface.m_uHeight = m_eyeSwapchains[i].m_uHeight;
|
|
|
|
surface.m_vPosition = (Vector){
|
|
m_views[i].pose.position.x,
|
|
m_views[i].pose.position.y,
|
|
m_views[i].pose.position.z
|
|
};
|
|
|
|
surface.m_vRotation = (Quat){
|
|
m_views[i].pose.orientation.x,
|
|
m_views[i].pose.orientation.y,
|
|
m_views[i].pose.orientation.z,
|
|
m_views[i].pose.orientation.w,
|
|
};
|
|
return surface;
|
|
}
|
|
|
|
void COpenXRHeadset::SetSurfaceImage( uint32_t i, IImage *pImage )
|
|
{
|
|
m_eyeSwapchains[i].m_pUserImage = pImage;
|
|
V_printf("%p\n", pImage);
|
|
|
|
}
|
|
|
|
uint32_t COpenXRHeadset::GetControllerCount()
|
|
{
|
|
return 2;
|
|
}
|
|
IXRController *COpenXRHeadset::GetController( uint32_t i )
|
|
{
|
|
if (i == 0)
|
|
return m_pLeftHand;
|
|
if (i == 1)
|
|
return m_pRightHand;
|
|
return NULL;
|
|
}
|
|
|
|
void COpenXRHeadset::CreateSwapchainForEye( XrViewConfigurationView *pConf, VkFormat eVkFormat, EyeSwapchain_t *pEye )
|
|
{
|
|
XrSwapchainCreateInfo createInfo = {};
|
|
createInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO;
|
|
createInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_TRANSFER_DST_BIT;
|
|
createInfo.format = eVkFormat;
|
|
createInfo.sampleCount = pConf->recommendedSwapchainSampleCount;
|
|
createInfo.width = pConf->recommendedImageRectWidth;
|
|
createInfo.height = pConf->recommendedImageRectHeight;
|
|
createInfo.faceCount = 1;
|
|
createInfo.arraySize = 1;
|
|
createInfo.mipCount = 1;
|
|
XrResult r = xrCreateSwapchain(m_session, &createInfo, &pEye->m_swapchain);
|
|
if (r != XR_SUCCESS)
|
|
Plat_FatalErrorFunc("xrCreateSwapchain\n");
|
|
pEye->m_uWidth = pConf->recommendedImageRectWidth;
|
|
pEye->m_uHeight = pConf->recommendedImageRectHeight;
|
|
|
|
|
|
|
|
uint32_t imageCount = 0;
|
|
CUtlVector<XrSwapchainImageVulkanKHR> images;
|
|
|
|
switch (m_pManager->m_gpuAPI)
|
|
{
|
|
case OPENXR_API_VULKAN:
|
|
{
|
|
xrEnumerateSwapchainImages(pEye->m_swapchain, 0, &imageCount, NULL);
|
|
images = imageCount;
|
|
pEye->m_pImages = imageCount;
|
|
for ( auto &i: images )
|
|
{
|
|
i.type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR;
|
|
}
|
|
xrEnumerateSwapchainImages(pEye->m_swapchain, imageCount, &imageCount, (XrSwapchainImageBaseHeader*)images.GetData());
|
|
pEye->m_pImages = imageCount;
|
|
}
|
|
break;
|
|
case OPENXR_API_VULKAN2:
|
|
{
|
|
xrEnumerateSwapchainImages(pEye->m_swapchain, 0, &imageCount, NULL);
|
|
images = imageCount;
|
|
pEye->m_pImages = imageCount;
|
|
for ( auto &i: images )
|
|
{
|
|
i.type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN2_KHR;
|
|
}
|
|
xrEnumerateSwapchainImages(pEye->m_swapchain, imageCount, &imageCount, (XrSwapchainImageBaseHeader*)images.GetData());
|
|
for ( auto &i: images )
|
|
V_printf("VkImage %p\n", i.image);
|
|
}
|
|
|
|
}
|
|
|
|
EImageFormat eFormat = IMAGE_FORMAT_RGBA8_UNORM;
|
|
switch(eVkFormat)
|
|
{
|
|
case VK_FORMAT_R8G8B8A8_UNORM: eFormat = IMAGE_FORMAT_RGBA8_UNORM; break;
|
|
case VK_FORMAT_B8G8R8A8_UNORM: eFormat = IMAGE_FORMAT_BGRA8_UNORM; break;
|
|
case VK_FORMAT_R8G8B8A8_SRGB: eFormat = IMAGE_FORMAT_RGBA8_SRGB; break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
for ( int i = 0; i < imageCount; i++ )
|
|
{
|
|
pEye->m_pImages[i] = m_pManager->m_pRenderContext->CreateImageFromVkImage(
|
|
images[i].image,
|
|
createInfo.width,
|
|
createInfo.height,
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
eFormat,
|
|
MULTISAMPLE_TYPE_1_SAMPLES);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
uint32_t COpenXRManager::GetHeadsetCount()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
IXRHeadset *COpenXRManager::GetHeadset( uint32_t i )
|
|
{
|
|
if (i)
|
|
return 0;
|
|
if(!bHasXR)
|
|
return 0;
|
|
|
|
return &m_headset;
|
|
}
|
|
|
|
void COpenXRManager::ConnectInterface( void *pIface, const char *szName )
|
|
{
|
|
if (!V_strcmp(szName, RENDER_CONTEXT_INTERFACE_VERSION))
|
|
m_pRenderContext = (IRenderContext*)pIface;
|
|
}
|
|
|