32 XrViewConfigurationType
view_type = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
71 m_oxr->swapchains.clear();
72 m_oxr->action_sets.clear();
74 if (m_oxr->reference_space != XR_NULL_HANDLE) {
77 if (m_oxr->view_space != XR_NULL_HANDLE) {
80 if (m_oxr->combined_eye_space != XR_NULL_HANDLE) {
83 if (m_oxr->session != XR_NULL_HANDLE) {
87 m_oxr->session = XR_NULL_HANDLE;
88 m_oxr->session_state = XR_SESSION_STATE_UNKNOWN;
97 void GHOST_XrSession::initSystem()
100 assert(m_oxr->system_id == XR_NULL_SYSTEM_ID);
102 XrSystemGetInfo system_info = {};
103 system_info.type = XR_TYPE_SYSTEM_GET_INFO;
104 system_info.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
107 "Failed to get device information. Is a device plugged in?");
117 const GHOST_XrPose &base_pose,
120 XrReferenceSpaceCreateInfo create_info = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO};
121 create_info.poseInReferenceSpace.orientation.w = 1.0f;
123 create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
136 create_info.poseInReferenceSpace.position.x = base_pose->position[0];
137 create_info.poseInReferenceSpace.position.y = base_pose->position[1];
138 create_info.poseInReferenceSpace.position.z = base_pose->position[2];
139 create_info.poseInReferenceSpace.orientation.x = base_pose->orientation_quat[1];
140 create_info.poseInReferenceSpace.orientation.y = base_pose->orientation_quat[2];
141 create_info.poseInReferenceSpace.orientation.z = base_pose->orientation_quat[3];
142 create_info.poseInReferenceSpace.orientation.w = base_pose->orientation_quat[0];
153 if (
result == XR_ERROR_REFERENCE_SPACE_UNSUPPORTED) {
156 "Warning: XR runtime does not support stage reference space, falling back to local "
157 "reference space.\n");
159 create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
161 "Failed to create local reference space.");
171 CHECK_XR(xrGetReferenceSpaceBoundsRect(oxr.
session, XR_REFERENCE_SPACE_TYPE_STAGE, &extents),
172 "Failed to get stage reference space bounds.");
173 if (extents.width == 0.0f || extents.height == 0.0f) {
176 "Warning: Invalid stage reference space bounds, falling back to local reference "
177 "space. To use the stage reference space, please define a tracking space via the XR "
185 create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
187 "Failed to create local reference space.");
191 create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW;
193 "Failed to create view reference space.");
197 create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO;
199 "Failed to create combined eye reference space.");
205 assert(m_context->
getInstance() != XR_NULL_HANDLE);
206 assert(m_oxr->session == XR_NULL_HANDLE);
209 "Invalid API usage: No way to bind graphics context to the XR session. Call "
210 "GHOST_XrGraphicsContextBindFuncs() with valid parameters before starting the "
211 "session (through GHOST_XrSessionStart()).");
216 bindGraphicsContext();
217 if (m_gpu_ctx ==
nullptr) {
219 "Invalid API usage: No graphics context returned through the callback set with "
220 "GHOST_XrGraphicsContextBindFuncs(). This is required for session starting (through "
221 "GHOST_XrSessionStart()).");
224 std::string requirement_str;
227 if (!m_gpu_binding->checkVersionRequirements(
228 *m_gpu_ctx, m_context->
getInstance(), m_oxr->system_id, &requirement_str)) {
229 std::ostringstream strstream;
230 strstream <<
"Available graphics context version does not meet the following requirements: "
234 m_gpu_binding->initFromGhostContext(*m_gpu_ctx);
236 XrSessionCreateInfo create_info = {};
237 create_info.type = XR_TYPE_SESSION_CREATE_INFO;
238 create_info.systemId = m_oxr->system_id;
239 create_info.next = &m_gpu_binding->oxr_binding;
242 "Failed to create VR session. The OpenXR runtime may have additional requirements for "
243 "the graphics driver that are not met. Other causes are possible too however.\nTip: "
244 "The --debug-xr command line option for Blender might allow the runtime to output "
245 "detailed error information to the command line.");
256 xrRequestExitSession(m_oxr->session);
259 void GHOST_XrSession::beginSession()
261 XrSessionBeginInfo begin_info = {XR_TYPE_SESSION_BEGIN_INFO};
262 begin_info.primaryViewConfigurationType = m_oxr->view_type;
263 CHECK_XR(xrBeginSession(m_oxr->session, &begin_info),
"Failed to cleanly begin the VR session.");
266 void GHOST_XrSession::endSession()
268 assert(m_oxr->session != XR_NULL_HANDLE);
269 CHECK_XR(xrEndSession(m_oxr->session),
"Failed to cleanly end the VR session.");
273 const XrEventDataSessionStateChanged &lifecycle)
275 m_oxr->session_state = lifecycle.state;
278 assert(m_oxr->session == XR_NULL_HANDLE || m_oxr->session == lifecycle.session);
280 switch (lifecycle.state) {
281 case XR_SESSION_STATE_READY:
284 case XR_SESSION_STATE_STOPPING:
287 case XR_SESSION_STATE_EXITING:
288 case XR_SESSION_STATE_LOSS_PENDING:
303 void GHOST_XrSession::prepareDrawing()
305 assert(m_context->
getInstance() != XR_NULL_HANDLE);
307 std::vector<XrViewConfigurationView> view_configs;
312 m_oxr->view_type = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO;
316 XR_VARJO_FOVEATED_RENDERING_EXTENSION_NAME);
319 xrEnumerateViewConfigurationViews(
320 m_context->
getInstance(), m_oxr->system_id, m_oxr->view_type, 0, &view_count,
nullptr),
321 "Failed to get count of view configurations.");
322 view_configs.resize(view_count, {XR_TYPE_VIEW_CONFIGURATION_VIEW});
328 view_configs.data()),
329 "Failed to get view configurations.");
332 if (m_oxr->foveation_supported) {
333 std::vector<XrFoveatedViewConfigurationViewVARJO> request_foveated_config{
334 view_count, {XR_TYPE_FOVEATED_VIEW_CONFIGURATION_VIEW_VARJO,
nullptr, XR_TRUE}};
336 auto foveated_views = std::vector<XrViewConfigurationView>(view_count,
337 {XR_TYPE_VIEW_CONFIGURATION_VIEW});
339 for (
uint32_t i = 0; i < view_count; i++) {
340 foveated_views[i].next = &request_foveated_config[i];
347 foveated_views.data()),
348 "Failed to get foveated view configurations.");
351 for (
uint32_t i = 0; i < view_count; i++) {
352 view_configs[i].recommendedImageRectWidth =
std::max(
353 view_configs[i].recommendedImageRectWidth, foveated_views[i].recommendedImageRectWidth);
354 view_configs[i].recommendedImageRectHeight =
std::max(
355 view_configs[i].recommendedImageRectHeight,
356 foveated_views[i].recommendedImageRectHeight);
360 for (
const XrViewConfigurationView &view_config : view_configs) {
361 m_oxr->swapchains.emplace_back(*m_gpu_binding, m_oxr->session, view_config);
364 m_oxr->views.resize(view_count, {XR_TYPE_VIEW});
366 m_draw_info = std::make_unique<GHOST_XrDrawInfo>();
369 void GHOST_XrSession::beginFrameDrawing()
371 XrFrameWaitInfo wait_info = {XR_TYPE_FRAME_WAIT_INFO};
372 XrFrameBeginInfo begin_info = {XR_TYPE_FRAME_BEGIN_INFO};
373 XrFrameState frame_state = {XR_TYPE_FRAME_STATE};
376 CHECK_XR(xrWaitFrame(m_oxr->session, &wait_info, &frame_state),
377 "Failed to synchronize frame rates between Blender and the device.");
380 m_draw_info->foveation_active =
false;
381 if (m_oxr->foveation_supported) {
382 XrSpaceLocation render_gaze_location{XR_TYPE_SPACE_LOCATION};
383 CHECK_XR(xrLocateSpace(m_oxr->combined_eye_space,
385 frame_state.predictedDisplayTime,
386 &render_gaze_location),
387 "Failed to locate combined eye space.");
389 m_draw_info->foveation_active = (render_gaze_location.locationFlags &
390 XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) != 0;
393 CHECK_XR(xrBeginFrame(m_oxr->session, &begin_info),
394 "Failed to submit frame rendering start state.");
396 m_draw_info->frame_state = frame_state;
399 m_draw_info->frame_begin_time = std::chrono::high_resolution_clock::now();
406 std::chrono::duration<double, std::milli> duration = std::chrono::high_resolution_clock::now() -
408 const double duration_ms = duration.count();
409 const int avg_frame_count = 8;
410 double avg_ms_tot = 0.0;
418 avg_ms_tot += ms_iter;
421 printf(
"VR frame render time: %.0fms - %.2f FPS (%.2f FPS 8 frames average)\n",
423 1000.0 / duration_ms,
427 void GHOST_XrSession::endFrameDrawing(std::vector<XrCompositionLayerBaseHeader *> &layers)
429 XrFrameEndInfo end_info = {XR_TYPE_FRAME_END_INFO};
431 end_info.displayTime = m_draw_info->frame_state.predictedDisplayTime;
432 end_info.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
433 end_info.layerCount = layers.size();
434 end_info.layers = layers.data();
436 CHECK_XR(xrEndFrame(m_oxr->session, &end_info),
"Failed to submit rendered frame.");
445 std::vector<XrCompositionLayerProjectionView>
446 projection_layer_views;
447 XrCompositionLayerProjection proj_layer;
448 std::vector<XrCompositionLayerBaseHeader *> layers;
452 if (m_draw_info->frame_state.shouldRender) {
453 proj_layer = drawLayer(projection_layer_views, draw_customdata);
454 layers.push_back(
reinterpret_cast<XrCompositionLayerBaseHeader *
>(&proj_layer));
457 endFrameDrawing(layers);
465 r_info.fov.angle_left =
view.fov.angleLeft;
466 r_info.fov.angle_right =
view.fov.angleRight;
467 r_info.fov.angle_up =
view.fov.angleUp;
468 r_info.fov.angle_down =
view.fov.angleDown;
472 XrCompositionLayerProjectionView &r_proj_layer_view,
473 XrSpaceLocation &view_location,
476 void *draw_customdata)
479 GHOST_XrDrawViewInfo draw_view_info = {};
481 r_proj_layer_view.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
482 r_proj_layer_view.pose =
view.pose;
483 r_proj_layer_view.fov =
view.fov;
486 assert(view_idx < 256);
487 draw_view_info.view_idx = (char)view_idx;
488 draw_view_info.swapchain_format = swapchain.
getFormat();
489 draw_view_info.expects_srgb_buffer = swapchain.
isBufferSRGB();
490 draw_view_info.ofsx = r_proj_layer_view.subImage.imageRect.offset.x;
491 draw_view_info.ofsy = r_proj_layer_view.subImage.imageRect.offset.y;
492 draw_view_info.width = r_proj_layer_view.subImage.imageRect.extent.width;
493 draw_view_info.height = r_proj_layer_view.subImage.imageRect.extent.height;
499 m_gpu_binding->submitToSwapchainImage(*swapchain_image, draw_view_info);
504 XrCompositionLayerProjection GHOST_XrSession::drawLayer(
505 std::vector<XrCompositionLayerProjectionView> &r_proj_layer_views,
void *draw_customdata)
507 XrViewLocateInfo viewloc_info = {XR_TYPE_VIEW_LOCATE_INFO};
508 XrViewLocateFoveatedRenderingVARJO foveated_info{
509 XR_TYPE_VIEW_LOCATE_FOVEATED_RENDERING_VARJO,
nullptr,
true};
510 XrViewState view_state = {XR_TYPE_VIEW_STATE};
511 XrCompositionLayerProjection layer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION};
512 XrSpaceLocation view_location{XR_TYPE_SPACE_LOCATION};
515 viewloc_info.viewConfigurationType = m_oxr->view_type;
516 viewloc_info.displayTime = m_draw_info->frame_state.predictedDisplayTime;
517 viewloc_info.space = m_oxr->reference_space;
519 if (m_draw_info->foveation_active) {
520 viewloc_info.next = &foveated_info;
523 CHECK_XR(xrLocateViews(m_oxr->session,
528 m_oxr->views.data()),
529 "Failed to query frame view and projection state.");
531 assert(m_oxr->swapchains.size() == view_count);
535 m_oxr->view_space, m_oxr->reference_space, viewloc_info.displayTime, &view_location),
536 "Failed to query frame view space");
538 r_proj_layer_views.resize(view_count);
540 for (
uint32_t view_idx = 0; view_idx < view_count; view_idx++) {
541 drawView(m_oxr->swapchains[view_idx],
542 r_proj_layer_views[view_idx],
544 m_oxr->views[view_idx],
549 layer.space = m_oxr->reference_space;
550 layer.viewCount = r_proj_layer_views.size();
551 layer.views = r_proj_layer_views.data();
558 return m_gpu_binding && m_gpu_binding->needsUpsideDownDrawing(*m_gpu_ctx);
569 if (m_oxr->session == XR_NULL_HANDLE) {
572 switch (m_oxr->session_state) {
573 case XR_SESSION_STATE_READY:
574 case XR_SESSION_STATE_SYNCHRONIZED:
575 case XR_SESSION_STATE_VISIBLE:
576 case XR_SESSION_STATE_FOCUSED:
595 void GHOST_XrSession::bindGraphicsContext()
620 std::map<std::string, GHOST_XrActionSet>::iterator it = oxr->
action_sets.find(action_set_name);
629 std::map<std::string, GHOST_XrActionSet> &action_sets = m_oxr->action_sets;
630 if (action_sets.find(info.name) != action_sets.end()) {
637 std::piecewise_construct, std::make_tuple(info.name), std::make_tuple(
instance, info));
644 std::map<std::string, GHOST_XrActionSet> &action_sets = m_oxr->action_sets;
645 if (action_sets.find(action_set_name) != action_sets.end()) {
646 action_sets.erase(action_set_name);
652 const GHOST_XrActionInfo *infos)
655 if (action_set ==
nullptr) {
672 const char *
const *action_names)
675 if (action_set ==
nullptr) {
686 const GHOST_XrActionProfileInfo *infos)
689 if (action_set ==
nullptr) {
694 XrSession session = m_oxr->session;
696 for (
uint32_t profile_idx = 0; profile_idx <
count; ++profile_idx) {
697 const GHOST_XrActionProfileInfo &info = infos[profile_idx];
700 if (action ==
nullptr) {
712 const char *
const *action_names,
713 const char *
const *profile_paths)
716 if (action_set ==
nullptr) {
722 if (action ==
nullptr) {
733 std::map<XrPath, std::vector<XrActionSuggestedBinding>> profile_bindings;
734 for (
auto &[name, action_set] : m_oxr->action_sets) {
735 action_set.getBindings(profile_bindings);
738 if (profile_bindings.size() < 1) {
742 XrInteractionProfileSuggestedBinding bindings_info{
743 XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING};
746 for (
auto &[profile, bindings] : profile_bindings) {
747 bindings_info.interactionProfile = profile;
748 bindings_info.countSuggestedBindings = (
uint32_t)bindings.size();
749 bindings_info.suggestedBindings = bindings.data();
752 "Failed to suggest interaction profile bindings.");
756 XrSessionActionSetsAttachInfo attach_info{XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO};
757 attach_info.countActionSets = (
uint32_t)m_oxr->action_sets.size();
760 std::vector<XrActionSet> action_sets(attach_info.countActionSets);
762 for (
auto &[name, action_set] : m_oxr->action_sets) {
763 action_sets[i++] = action_set.getActionSet();
765 attach_info.actionSets = action_sets.data();
767 CHECK_XR(xrAttachSessionActionSets(m_oxr->session, &attach_info),
768 "Failed to attach XR action sets.");
775 std::map<std::string, GHOST_XrActionSet> &action_sets = m_oxr->action_sets;
777 XrActionsSyncInfo sync_info{XR_TYPE_ACTIONS_SYNC_INFO};
778 sync_info.countActiveActionSets = (action_set_name !=
nullptr) ? 1 :
780 if (sync_info.countActiveActionSets < 1) {
784 std::vector<XrActiveActionSet> active_action_sets(sync_info.countActiveActionSets);
787 if (action_set_name !=
nullptr) {
789 if (action_set ==
nullptr) {
793 XrActiveActionSet &active_action_set = active_action_sets[0];
794 active_action_set.actionSet = action_set->
getActionSet();
795 active_action_set.subactionPath = XR_NULL_PATH;
799 for (
auto &[name, action_set] : action_sets) {
800 XrActiveActionSet &active_action_set = active_action_sets[i++];
801 active_action_set.actionSet = action_set.
getActionSet();
802 active_action_set.subactionPath = XR_NULL_PATH;
805 sync_info.activeActionSets = active_action_sets.data();
807 CHECK_XR(xrSyncActions(m_oxr->session, &sync_info),
"Failed to synchronize XR actions.");
810 XrSession session = m_oxr->session;
811 XrSpace reference_space = m_oxr->reference_space;
812 const XrTime &predicted_display_time = m_draw_info->frame_state.predictedDisplayTime;
814 if (action_set !=
nullptr) {
815 action_set->
updateStates(session, reference_space, predicted_display_time);
818 for (
auto &[name, action_set] : action_sets) {
819 action_set.
updateStates(session, reference_space, predicted_display_time);
827 const char *action_name,
828 const char *subaction_path,
830 const float &frequency,
831 const float &litude)
834 if (action_set ==
nullptr) {
839 if (action ==
nullptr) {
844 m_oxr->session, action_name, subaction_path, duration, frequency, amplitude);
850 const char *action_name,
851 const char *subaction_path)
854 if (action_set ==
nullptr) {
859 if (action ==
nullptr) {
869 if (action_set ==
nullptr) {
879 if (action_set ==
nullptr) {
884 if (action ==
nullptr) {
894 if (action_set ==
nullptr) {
902 void **r_customdata_array)
905 if (action_set ==
nullptr) {
925 XrSession session = m_oxr->session;
926 std::map<std::string, GHOST_XrControllerModel> &controller_models = m_oxr->controller_models;
927 std::map<std::string, GHOST_XrControllerModel>::iterator it = controller_models.find(
930 if (it == controller_models.end()) {
932 it = controller_models
933 .emplace(std::piecewise_construct,
934 std::make_tuple(subaction_path),
935 std::make_tuple(
instance, subaction_path))
939 it->second.load(session);
946 std::map<std::string, GHOST_XrControllerModel> &controller_models = m_oxr->controller_models;
947 if (controller_models.find(subaction_path) != controller_models.end()) {
948 controller_models.erase(subaction_path);
954 XrSession session = m_oxr->session;
955 std::map<std::string, GHOST_XrControllerModel>::iterator it = m_oxr->controller_models.find(
957 if (it == m_oxr->controller_models.end()) {
961 it->second.updateComponents(session);
967 GHOST_XrControllerModelData &r_data)
969 std::map<std::string, GHOST_XrControllerModel>::iterator it = m_oxr->controller_models.find(
971 if (it == m_oxr->controller_models.end()) {
975 it->second.getData(r_data);
GHOST C-API function and type declarations.
std::unique_ptr< GHOST_IXrGraphicsBinding > GHOST_XrGraphicsBindingCreateFromType(GHOST_TXrGraphicsBinding type, GHOST_Context &ghost_ctx)
static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose &base_pose, bool isDebugMode)
static void print_debug_timings(GHOST_XrDrawInfo &draw_info)
static void ghost_xr_draw_view_info_from_view(const XrView &view, GHOST_XrDrawViewInfo &r_info)
static GHOST_XrActionSet * find_action_set(OpenXRSessionData *oxr, const char *action_set_name)
void copy_openxr_pose_to_ghost_pose(const XrPosef &oxr_pose, GHOST_XrPose &r_ghost_pose)
#define CHECK_XR_ASSERT(call)
#define CHECK_XR(call, error_msg)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object instance
void updateStates(XrSession session, XrSpace reference_space, const XrTime &predicted_display_time)
GHOST_XrAction * findAction(const char *action_name)
bool createAction(XrInstance instance, const GHOST_XrActionInfo &info)
XrActionSet getActionSet() const
void getActionCustomdataArray(void **r_customdata_array)
uint32_t getActionCount() const
void destroyAction(const char *action_name)
bool createBinding(XrInstance instance, XrSession session, const GHOST_XrActionProfileInfo &info)
void stopHapticFeedback(XrSession session, const char *action_name, const char *subaction_path)
void applyHapticFeedback(XrSession session, const char *action_name, const char *subaction_path, const int64_t &duration, const float &frequency, const float &litude)
void destroyBinding(const char *profile_path)
Main GHOST container to manage OpenXR through.
XrInstance getInstance() const
GHOST_TXrGraphicsBinding getGraphicsBindingType() const
const GHOST_XrCustomFuncs & getCustomFuncs() const
bool isDebugTimeMode() const
bool isExtensionEnabled(const char *ext) const
void destroyActionBindings(const char *action_set_name, uint32_t count, const char *const *action_names, const char *const *profile_paths)
void destroyActionSet(const char *action_set_name)
void draw(void *draw_customdata)
bool createActionSet(const GHOST_XrActionSetInfo &info)
void * getActionCustomdata(const char *action_set_name, const char *action_name)
void unloadControllerModel(const char *subaction_path)
void * getActionSetCustomdata(const char *action_set_name)
bool updateControllerModelComponents(const char *subaction_path)
bool createActionBindings(const char *action_set_name, uint32_t count, const GHOST_XrActionProfileInfo *infos)
GHOST_XrSession(GHOST_XrContext &xr_context)
void stopHapticAction(const char *action_set_name, const char *action_name, const char *subaction_path)
LifeExpectancy handleStateChangeEvent(const XrEventDataSessionStateChanged &lifecycle)
void getActionCustomdataArray(const char *action_set_name, void **r_customdata_array)
bool loadControllerModel(const char *subaction_path)
bool syncActions(const char *action_set_name=nullptr)
bool needsUpsideDownDrawing() const
void start(const GHOST_XrSessionBeginInfo *begin_info)
void destroyActions(const char *action_set_name, uint32_t count, const char *const *action_names)
void unbindGraphicsContext()
bool applyHapticAction(const char *action_set_name, const char *action_name, const char *subaction_path, const int64_t &duration, const float &frequency, const float &litude)
uint32_t getActionCount(const char *action_set_name)
bool getControllerModelData(const char *subaction_path, GHOST_XrControllerModelData &r_data)
bool createActions(const char *action_set_name, uint32_t count, const GHOST_XrActionInfo *infos)
GHOST_TXrSwapchainFormat getFormat() const
void updateCompositionLayerProjectViewSubImage(XrSwapchainSubImage &r_sub_image)
bool isBufferSRGB() const
XrSwapchainImageBaseHeader * acquireDrawableSwapchainImage()
SyclQueue void void size_t num_bytes void
GHOST_XrDrawViewFn draw_view_fn
GHOST_XrSessionExitFn session_exit_fn
GHOST_XrSessionCreateFn session_create_fn
void * session_exit_customdata
GHOST_XrGraphicsContextUnbindFn gpu_ctx_unbind_fn
GHOST_XrGraphicsContextBindFn gpu_ctx_bind_fn
std::chrono::high_resolution_clock::time_point frame_begin_time
std::list< double > last_frame_times
XrSpace combined_eye_space
std::map< std::string, GHOST_XrControllerModel > controller_models
XrViewConfigurationType view_type
std::map< std::string, GHOST_XrActionSet > action_sets
XrSessionState session_state
std::vector< GHOST_XrSwapchain > swapchains
std::vector< XrView > views