44 const XrViewConfigurationType
view_type = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
74 m_oxr->swapchains.clear();
76 if (m_oxr->reference_space != XR_NULL_HANDLE) {
79 if (m_oxr->view_space != XR_NULL_HANDLE) {
82 if (m_oxr->session != XR_NULL_HANDLE) {
86 m_oxr->session = XR_NULL_HANDLE;
87 m_oxr->session_state = XR_SESSION_STATE_UNKNOWN;
96 void GHOST_XrSession::initSystem()
99 assert(m_oxr->system_id == XR_NULL_SYSTEM_ID);
101 XrSystemGetInfo system_info = {};
102 system_info.type = XR_TYPE_SYSTEM_GET_INFO;
103 system_info.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
106 "Failed to get device information. Is a device plugged in?");
118 XrReferenceSpaceCreateInfo create_info = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO};
119 create_info.poseInReferenceSpace.orientation.w = 1.0f;
121 create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
134 create_info.poseInReferenceSpace.position.x = base_pose->position[0];
135 create_info.poseInReferenceSpace.position.y = base_pose->position[1];
136 create_info.poseInReferenceSpace.position.z = base_pose->position[2];
137 create_info.poseInReferenceSpace.orientation.x = base_pose->orientation_quat[1];
138 create_info.poseInReferenceSpace.orientation.y = base_pose->orientation_quat[2];
139 create_info.poseInReferenceSpace.orientation.z = base_pose->orientation_quat[3];
140 create_info.poseInReferenceSpace.orientation.w = base_pose->orientation_quat[0];
146 "Failed to create reference space.");
148 create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW;
150 "Failed to create view reference space.");
155 assert(m_context->
getInstance() != XR_NULL_HANDLE);
156 assert(m_oxr->session == XR_NULL_HANDLE);
159 "Invalid API usage: No way to bind graphics context to the XR session. Call "
160 "GHOST_XrGraphicsContextBindFuncs() with valid parameters before starting the "
161 "session (through GHOST_XrSessionStart()).");
166 bindGraphicsContext();
167 if (m_gpu_ctx ==
nullptr) {
169 "Invalid API usage: No graphics context returned through the callback set with "
170 "GHOST_XrGraphicsContextBindFuncs(). This is required for session starting (through "
171 "GHOST_XrSessionStart()).");
174 std::string requirement_str;
177 if (!m_gpu_binding->checkVersionRequirements(
178 *m_gpu_ctx, m_context->
getInstance(), m_oxr->system_id, &requirement_str)) {
179 std::ostringstream strstream;
180 strstream <<
"Available graphics context version does not meet the following requirements: "
184 m_gpu_binding->initFromGhostContext(*m_gpu_ctx);
186 XrSessionCreateInfo create_info = {};
187 create_info.type = XR_TYPE_SESSION_CREATE_INFO;
188 create_info.systemId = m_oxr->system_id;
189 create_info.next = &m_gpu_binding->oxr_binding;
192 "Failed to create VR session. The OpenXR runtime may have additional requirements for "
193 "the graphics driver that are not met. Other causes are possible too however.\nTip: "
194 "The --debug-xr command line option for Blender might allow the runtime to output "
195 "detailed error information to the command line.");
203 xrRequestExitSession(m_oxr->session);
206 void GHOST_XrSession::beginSession()
208 XrSessionBeginInfo begin_info = {XR_TYPE_SESSION_BEGIN_INFO};
209 begin_info.primaryViewConfigurationType = m_oxr->view_type;
210 CHECK_XR(xrBeginSession(m_oxr->session, &begin_info),
"Failed to cleanly begin the VR session.");
213 void GHOST_XrSession::endSession()
215 assert(m_oxr->session != XR_NULL_HANDLE);
216 CHECK_XR(xrEndSession(m_oxr->session),
"Failed to cleanly end the VR session.");
220 const XrEventDataSessionStateChanged &lifecycle)
222 m_oxr->session_state = lifecycle.state;
225 assert(m_oxr->session == XR_NULL_HANDLE || m_oxr->session == lifecycle.session);
227 switch (lifecycle.state) {
228 case XR_SESSION_STATE_READY: {
232 case XR_SESSION_STATE_STOPPING:
235 case XR_SESSION_STATE_EXITING:
236 case XR_SESSION_STATE_LOSS_PENDING:
251 void GHOST_XrSession::prepareDrawing()
253 std::vector<XrViewConfigurationView> view_configs;
257 xrEnumerateViewConfigurationViews(
258 m_context->
getInstance(), m_oxr->system_id, m_oxr->view_type, 0, &view_count,
nullptr),
259 "Failed to get count of view configurations.");
260 view_configs.resize(view_count, {XR_TYPE_VIEW_CONFIGURATION_VIEW});
266 view_configs.data()),
267 "Failed to get count of view configurations.");
269 for (
const XrViewConfigurationView &view_config : view_configs) {
270 m_oxr->swapchains.emplace_back(*m_gpu_binding, m_oxr->session, view_config);
273 m_oxr->views.resize(view_count, {XR_TYPE_VIEW});
275 m_draw_info = std::make_unique<GHOST_XrDrawInfo>();
278 void GHOST_XrSession::beginFrameDrawing()
280 XrFrameWaitInfo wait_info = {XR_TYPE_FRAME_WAIT_INFO};
281 XrFrameBeginInfo begin_info = {XR_TYPE_FRAME_BEGIN_INFO};
282 XrFrameState frame_state = {XR_TYPE_FRAME_STATE};
285 CHECK_XR(xrWaitFrame(m_oxr->session, &wait_info, &frame_state),
286 "Failed to synchronize frame rates between Blender and the device.");
288 CHECK_XR(xrBeginFrame(m_oxr->session, &begin_info),
289 "Failed to submit frame rendering start state.");
291 m_draw_info->frame_state = frame_state;
294 m_draw_info->frame_begin_time = std::chrono::high_resolution_clock::now();
301 std::chrono::duration<double, std::milli> duration = std::chrono::high_resolution_clock::now() -
303 const double duration_ms = duration.count();
304 const int avg_frame_count = 8;
305 double avg_ms_tot = 0.0;
313 avg_ms_tot += ms_iter;
316 printf(
"VR frame render time: %.0fms - %.2f FPS (%.2f FPS 8 frames average)\n",
318 1000.0 / duration_ms,
322 void GHOST_XrSession::endFrameDrawing(std::vector<XrCompositionLayerBaseHeader *> &layers)
324 XrFrameEndInfo end_info = {XR_TYPE_FRAME_END_INFO};
326 end_info.displayTime = m_draw_info->frame_state.predictedDisplayTime;
327 end_info.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
328 end_info.layerCount = layers.size();
329 end_info.layers = layers.data();
331 CHECK_XR(xrEndFrame(m_oxr->session, &end_info),
"Failed to submit rendered frame.");
340 std::vector<XrCompositionLayerProjectionView>
341 projection_layer_views;
342 XrCompositionLayerProjection proj_layer;
343 std::vector<XrCompositionLayerBaseHeader *> layers;
347 if (m_draw_info->frame_state.shouldRender) {
348 proj_layer = drawLayer(projection_layer_views, draw_customdata);
349 layers.push_back(
reinterpret_cast<XrCompositionLayerBaseHeader *
>(&proj_layer));
352 endFrameDrawing(layers);
358 r_ghost_pose.position[0] = oxr_pose.position.x;
359 r_ghost_pose.position[1] = oxr_pose.position.y;
360 r_ghost_pose.position[2] = oxr_pose.position.z;
361 r_ghost_pose.orientation_quat[0] = oxr_pose.orientation.w;
362 r_ghost_pose.orientation_quat[1] = oxr_pose.orientation.x;
363 r_ghost_pose.orientation_quat[2] = oxr_pose.orientation.y;
364 r_ghost_pose.orientation_quat[3] = oxr_pose.orientation.z;
372 r_info.fov.angle_left =
view.fov.angleLeft;
373 r_info.fov.angle_right =
view.fov.angleRight;
374 r_info.fov.angle_up =
view.fov.angleUp;
375 r_info.fov.angle_down =
view.fov.angleDown;
379 XrCompositionLayerProjectionView &r_proj_layer_view,
380 XrSpaceLocation &view_location,
382 void *draw_customdata)
385 GHOST_XrDrawViewInfo draw_view_info = {};
387 r_proj_layer_view.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
388 r_proj_layer_view.pose =
view.pose;
389 r_proj_layer_view.fov =
view.fov;
392 draw_view_info.expects_srgb_buffer = swapchain.
isBufferSRGB();
393 draw_view_info.ofsx = r_proj_layer_view.subImage.imageRect.offset.x;
394 draw_view_info.ofsy = r_proj_layer_view.subImage.imageRect.offset.y;
395 draw_view_info.width = r_proj_layer_view.subImage.imageRect.extent.width;
396 draw_view_info.height = r_proj_layer_view.subImage.imageRect.extent.height;
402 m_gpu_binding->submitToSwapchainImage(*swapchain_image, draw_view_info);
407 XrCompositionLayerProjection GHOST_XrSession::drawLayer(
408 std::vector<XrCompositionLayerProjectionView> &r_proj_layer_views,
void *draw_customdata)
410 XrViewLocateInfo viewloc_info = {XR_TYPE_VIEW_LOCATE_INFO};
411 XrViewState view_state = {XR_TYPE_VIEW_STATE};
412 XrCompositionLayerProjection layer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION};
413 XrSpaceLocation view_location{XR_TYPE_SPACE_LOCATION};
416 viewloc_info.viewConfigurationType = m_oxr->view_type;
417 viewloc_info.displayTime = m_draw_info->frame_state.predictedDisplayTime;
418 viewloc_info.space = m_oxr->reference_space;
420 CHECK_XR(xrLocateViews(m_oxr->session,
425 m_oxr->views.data()),
426 "Failed to query frame view and projection state.");
427 assert(m_oxr->swapchains.size() == view_count);
431 m_oxr->view_space, m_oxr->reference_space, viewloc_info.displayTime, &view_location),
432 "Failed to query frame view space");
434 r_proj_layer_views.resize(view_count);
436 for (
uint32_t view_idx = 0; view_idx < view_count; view_idx++) {
437 drawView(m_oxr->swapchains[view_idx],
438 r_proj_layer_views[view_idx],
440 m_oxr->views[view_idx],
444 layer.space = m_oxr->reference_space;
445 layer.viewCount = r_proj_layer_views.size();
446 layer.views = r_proj_layer_views.data();
453 return m_gpu_binding && m_gpu_binding->needsUpsideDownDrawing(*m_gpu_ctx);
465 if (m_oxr->session == XR_NULL_HANDLE) {
468 switch (m_oxr->session_state) {
469 case XR_SESSION_STATE_READY:
470 case XR_SESSION_STATE_SYNCHRONIZED:
471 case XR_SESSION_STATE_VISIBLE:
472 case XR_SESSION_STATE_FOCUSED:
491 void GHOST_XrSession::bindGraphicsContext()
GHOST C-API function and type declarations.
std::unique_ptr< GHOST_IXrGraphicsBinding > GHOST_XrGraphicsBindingCreateFromType(GHOST_TXrGraphicsBinding type, GHOST_Context &ghost_ctx)
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 void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose &base_pose)
static 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)
Main GHOST container to manage OpenXR through.
XrInstance getInstance() const
GHOST_TXrGraphicsBinding getGraphicsBindingType() const
const GHOST_XrCustomFuncs & getCustomFuncs() const
bool isDebugTimeMode() const
void draw(void *draw_customdata)
GHOST_XrSession(GHOST_XrContext &xr_context)
LifeExpectancy handleStateChangeEvent(const XrEventDataSessionStateChanged &lifecycle)
bool needsUpsideDownDrawing() const
void start(const GHOST_XrSessionBeginInfo *begin_info)
void unbindGraphicsContext()
void updateCompositionLayerProjectViewSubImage(XrSwapchainSubImage &r_sub_image)
XrSwapchainImageBaseHeader * acquireDrawableSwapchainImage()
GHOST_XrDrawViewFn draw_view_fn
GHOST_XrSessionExitFn session_exit_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
XrSessionState session_state
const XrViewConfigurationType view_type
std::vector< GHOST_XrSwapchain > swapchains
std::vector< XrView > views