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