1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 */
16
17 /** \file
18 * \ingroup wm
19 *
20 * All XR functionality is accessed through a #GHOST_XrContext handle.
21 * The lifetime of this context also determines the lifetime of the OpenXR instance, which is the
22 * representation of the OpenXR runtime connection within the application.
23 */
24
25 #include "BKE_global.h"
26 #include "BKE_idprop.h"
27 #include "BKE_report.h"
28
29 #include "DNA_scene_types.h"
30 #include "DNA_windowmanager_types.h"
31
32 #include "DEG_depsgraph.h"
33
34 #include "MEM_guardedalloc.h"
35
36 #include "GHOST_C-api.h"
37
38 #include "WM_api.h"
39
40 #include "wm_surface.h"
41 #include "wm_xr_intern.h"
42
43 typedef struct {
44 wmWindowManager *wm;
45 } wmXrErrorHandlerData;
46
47 /* -------------------------------------------------------------------- */
48
wm_xr_error_handler(const GHOST_XrError * error)49 static void wm_xr_error_handler(const GHOST_XrError *error)
50 {
51 wmXrErrorHandlerData *handler_data = error->customdata;
52 wmWindowManager *wm = handler_data->wm;
53
54 BKE_reports_clear(&wm->reports);
55 WM_report(RPT_ERROR, error->user_message);
56 WM_report_banner_show();
57
58 if (wm->xr.runtime) {
59 /* Just play safe and destroy the entire runtime data, including context. */
60 wm_xr_runtime_data_free(&wm->xr.runtime);
61 }
62 }
63
wm_xr_init(wmWindowManager * wm)64 bool wm_xr_init(wmWindowManager *wm)
65 {
66 if (wm->xr.runtime && wm->xr.runtime->context) {
67 return true;
68 }
69 static wmXrErrorHandlerData error_customdata;
70
71 /* Set up error handling */
72 error_customdata.wm = wm;
73 GHOST_XrErrorHandler(wm_xr_error_handler, &error_customdata);
74
75 {
76 const GHOST_TXrGraphicsBinding gpu_bindings_candidates[] = {
77 GHOST_kXrGraphicsOpenGL,
78 #ifdef WIN32
79 GHOST_kXrGraphicsD3D11,
80 #endif
81 };
82 GHOST_XrContextCreateInfo create_info = {
83 .gpu_binding_candidates = gpu_bindings_candidates,
84 .gpu_binding_candidates_count = ARRAY_SIZE(gpu_bindings_candidates),
85 };
86 GHOST_XrContextHandle context;
87
88 if (G.debug & G_DEBUG_XR) {
89 create_info.context_flag |= GHOST_kXrContextDebug;
90 }
91 if (G.debug & G_DEBUG_XR_TIME) {
92 create_info.context_flag |= GHOST_kXrContextDebugTime;
93 }
94
95 if (!(context = GHOST_XrContextCreate(&create_info))) {
96 return false;
97 }
98
99 /* Set up context callbacks */
100 GHOST_XrGraphicsContextBindFuncs(context,
101 wm_xr_session_gpu_binding_context_create,
102 wm_xr_session_gpu_binding_context_destroy);
103 GHOST_XrDrawViewFunc(context, wm_xr_draw_view);
104
105 if (!wm->xr.runtime) {
106 wm->xr.runtime = wm_xr_runtime_data_create();
107 wm->xr.runtime->context = context;
108 }
109 }
110 BLI_assert(wm->xr.runtime && wm->xr.runtime->context);
111
112 return true;
113 }
114
wm_xr_exit(wmWindowManager * wm)115 void wm_xr_exit(wmWindowManager *wm)
116 {
117 if (wm->xr.runtime != NULL) {
118 wm_xr_runtime_data_free(&wm->xr.runtime);
119 }
120 if (wm->xr.session_settings.shading.prop) {
121 IDP_FreeProperty(wm->xr.session_settings.shading.prop);
122 wm->xr.session_settings.shading.prop = NULL;
123 }
124 }
125
wm_xr_events_handle(wmWindowManager * wm)126 bool wm_xr_events_handle(wmWindowManager *wm)
127 {
128 if (wm->xr.runtime && wm->xr.runtime->context) {
129 GHOST_XrEventsHandle(wm->xr.runtime->context);
130
131 /* wm_window_process_events() uses the return value to determine if it can put the main thread
132 * to sleep for some milliseconds. We never want that to happen while the VR session runs on
133 * the main thread. So always return true. */
134 return true;
135 }
136 return false;
137 }
138
139 /* -------------------------------------------------------------------- */
140 /** \name XR Runtime Data
141 *
142 * \{ */
143
wm_xr_runtime_data_create(void)144 wmXrRuntimeData *wm_xr_runtime_data_create(void)
145 {
146 wmXrRuntimeData *runtime = MEM_callocN(sizeof(*runtime), __func__);
147 return runtime;
148 }
149
wm_xr_runtime_data_free(wmXrRuntimeData ** runtime)150 void wm_xr_runtime_data_free(wmXrRuntimeData **runtime)
151 {
152 /* Note that this function may be called twice, because of an indirect recursion: If a session is
153 * running while WM-XR calls this function, calling GHOST_XrContextDestroy() will call this
154 * again, because it's also set as the session exit callback. So NULL-check and NULL everything
155 * that is freed here. */
156
157 /* We free all runtime XR data here, so if the context is still alive, destroy it. */
158 if ((*runtime)->context != NULL) {
159 GHOST_XrContextHandle context = (*runtime)->context;
160 /* Prevent recursive GHOST_XrContextDestroy() call by NULL'ing the context pointer before the
161 * first call, see comment above. */
162 (*runtime)->context = NULL;
163 GHOST_XrContextDestroy(context);
164 }
165 MEM_SAFE_FREE(*runtime);
166 }
167
168 /** \} */ /* XR Runtime Data */
169