Compare commits
352 Commits
node-group
...
soc-2019-o
Author | SHA1 | Date | |
---|---|---|---|
a640587e25 | |||
892637f60a | |||
c333f57603 | |||
99eb95337f | |||
5d30598e3b | |||
1a7135b12f | |||
ae14649583 | |||
a75a79272e | |||
ddbbd9928e | |||
d2ef342b2a | |||
b86be9b214 | |||
cb6cec904f | |||
dd8ef89633 | |||
5cca2f041d | |||
00f83ec125 | |||
a2d04deda7 | |||
8f327fb070 | |||
f8c3ccf398 | |||
7c11e45f5f | |||
792335cfab | |||
b381eba6c7 | |||
066d755e47 | |||
f2018ba492 | |||
8126e4ac14 | |||
cfa4302570 | |||
e7ceb041cf | |||
![]() |
c02ba3653a | ||
9abf5ba1d5 | |||
1cc81d39c4 | |||
e38cd1741e | |||
27bc3388da | |||
8b8142c13a | |||
d60c71f33d | |||
916efac0c8 | |||
ef20e1d378 | |||
f73a6c517c | |||
6e13062ddb | |||
73791a755a | |||
![]() |
28321b064a | ||
![]() |
10d563baf4 | ||
![]() |
812c4f45f5 | ||
![]() |
75163f2a7d | ||
![]() |
3c0c28afe1 | ||
![]() |
4ecea3dd3e | ||
![]() |
68cec61dae | ||
![]() |
c822556e27 | ||
![]() |
e62a012f90 | ||
![]() |
be7b41df3f | ||
![]() |
6f977c5e6d | ||
![]() |
a74af5f6f0 | ||
![]() |
3e41ea03be | ||
![]() |
489da13b94 | ||
![]() |
f9a3a42412 | ||
![]() |
202623419e | ||
![]() |
49f7848589 | ||
![]() |
6fda295034 | ||
![]() |
2b7e94e35e | ||
![]() |
65ceb8ee5d | ||
![]() |
9b5a1edbc4 | ||
![]() |
6cceecb82f | ||
![]() |
166c1f9a92 | ||
![]() |
3da9ecc6c1 | ||
![]() |
0f979f3388 | ||
![]() |
72ad66b101 | ||
![]() |
f25a348915 | ||
![]() |
af00df3469 | ||
![]() |
80c6f6eb4e | ||
![]() |
62db972f0f | ||
![]() |
e9036242fa | ||
![]() |
83aebf759e | ||
![]() |
5169f9e975 | ||
![]() |
d9c05e6997 | ||
![]() |
404cc8d1fc | ||
![]() |
af928a2fb1 | ||
![]() |
b1c9434332 | ||
![]() |
4c2ec7c5ec | ||
![]() |
79069884b1 | ||
![]() |
7254d1ae49 | ||
![]() |
a18c254c6d | ||
![]() |
27bbbc9c57 | ||
![]() |
6dbe2d781d | ||
![]() |
19f3fdad97 | ||
![]() |
8d5996595f | ||
![]() |
47baf42e2c | ||
![]() |
cdfe5449df | ||
![]() |
f0786dfc52 | ||
![]() |
de2d03ace9 | ||
![]() |
0a27ab2e93 | ||
![]() |
d930ff7f4f | ||
![]() |
f3e9351111 | ||
![]() |
2a259497ae | ||
![]() |
eecf3021d7 | ||
![]() |
0173800572 | ||
![]() |
bc38898cf4 | ||
![]() |
1a812145e0 | ||
![]() |
f430f6fa18 | ||
![]() |
2f6ebe1b99 | ||
![]() |
c706834749 | ||
![]() |
11ee2f1d09 | ||
![]() |
fcc6e94897 | ||
![]() |
d6cce8bc8e | ||
![]() |
4332e2b5fb | ||
![]() |
3ff91e97d9 | ||
![]() |
06138de13a | ||
![]() |
0c7ac28b8a | ||
![]() |
43cfa0b682 | ||
![]() |
f426d3a002 | ||
![]() |
a1499f7f7d | ||
![]() |
530a8d1ab1 | ||
![]() |
2232406394 | ||
![]() |
8f6b3d27d0 | ||
![]() |
ffa16f677a | ||
![]() |
85ccec8639 | ||
![]() |
fc83dfdba1 | ||
![]() |
2448eb6136 | ||
![]() |
90df65892f | ||
![]() |
f07e361829 | ||
![]() |
779b3f0cff | ||
![]() |
561f4665a6 | ||
![]() |
58f95e9e4d | ||
![]() |
17121b2afd | ||
![]() |
fc26797e72 | ||
![]() |
fa9707a712 | ||
![]() |
8d45b4f75c | ||
![]() |
03ac9de0a9 | ||
![]() |
4456dbcded | ||
![]() |
cc23a8c84d | ||
![]() |
120d9c91dc | ||
![]() |
e1db5cc2c7 | ||
![]() |
a347ec08bc | ||
![]() |
ee8e4b7db8 | ||
![]() |
8c7be58e72 | ||
![]() |
1b39eb1060 | ||
![]() |
0d98e665ea | ||
![]() |
7f1fadba67 | ||
![]() |
8537cdc71d | ||
![]() |
6ae2979d05 | ||
![]() |
b698a926af | ||
![]() |
fac53c7bc2 | ||
![]() |
88f234e27f | ||
![]() |
0615321ca0 | ||
![]() |
3bed8a7338 | ||
![]() |
ff03e3b7e0 | ||
![]() |
350b7b378d | ||
![]() |
51af7510fb | ||
![]() |
927c300c02 | ||
![]() |
fa9a841d43 | ||
![]() |
b057298021 | ||
![]() |
1ada9f5bf9 | ||
![]() |
52c9d33cc2 | ||
![]() |
db6657aa15 | ||
![]() |
941e1f5a98 | ||
![]() |
e9832f053b | ||
![]() |
cddf043cca | ||
![]() |
6af2f222ca | ||
![]() |
639dffa43a | ||
![]() |
9baa9e2bd3 | ||
![]() |
3c38e67765 | ||
![]() |
1849e4fe19 | ||
2ef7a1f24d | |||
![]() |
bc8186b5d1 | ||
![]() |
e0bb3f9286 | ||
![]() |
d0b4ec00f6 | ||
![]() |
7b6514c222 | ||
![]() |
c506acf2fc | ||
![]() |
31b8350b01 | ||
![]() |
d7a216704b | ||
![]() |
4b67477732 | ||
![]() |
4a039158e5 | ||
![]() |
61014e1dd9 | ||
![]() |
3441314e40 | ||
![]() |
f175dcc35f | ||
![]() |
756b676076 | ||
![]() |
7462fca737 | ||
![]() |
7c9e18c193 | ||
![]() |
6b69b5349b | ||
![]() |
daba8140c2 | ||
![]() |
599d0611b0 | ||
![]() |
e88970db82 | ||
![]() |
20b0b36479 | ||
![]() |
2c77f2deac | ||
![]() |
fec7ac7c51 | ||
![]() |
2a1ec8ec2a | ||
1548682cde | |||
![]() |
588dea6751 | ||
![]() |
ad4f9a4d98 | ||
![]() |
84466c7d71 | ||
![]() |
de037b031d | ||
![]() |
a21ae28f96 | ||
![]() |
b2424bd781 | ||
![]() |
d0177fc541 | ||
![]() |
4cf8740790 | ||
![]() |
a53b90329a | ||
![]() |
2c41ffa380 | ||
![]() |
cdc2768f65 | ||
![]() |
eb477c67a1 | ||
![]() |
0dd3f3925b | ||
![]() |
9e7c48575a | ||
![]() |
8138099fa9 | ||
![]() |
02de1b9860 | ||
![]() |
57712625f2 | ||
![]() |
96a34d7f50 | ||
![]() |
873223f38d | ||
![]() |
716c6b0c01 | ||
![]() |
62bbd2e37d | ||
![]() |
ba223dfac8 | ||
![]() |
8557997e81 | ||
![]() |
bcc8f7c6be | ||
![]() |
fe7b01a08f | ||
![]() |
5350015d51 | ||
![]() |
3483aa57b3 | ||
![]() |
44a220f721 | ||
![]() |
5274c5cce6 | ||
![]() |
0d70afed19 | ||
![]() |
03b09dbe32 | ||
![]() |
f04a5ad1e4 | ||
![]() |
ab1455e972 | ||
![]() |
b94af38c31 | ||
![]() |
40db778de3 | ||
![]() |
22966f4d35 | ||
![]() |
e8f66ff060 | ||
![]() |
9ac33e56a1 | ||
![]() |
091cc94379 | ||
![]() |
fc31be5ab0 | ||
![]() |
aff49f607a | ||
![]() |
57b77cd193 | ||
![]() |
d58eb8d29d | ||
![]() |
a4310ba85f | ||
![]() |
bd42740ef1 | ||
![]() |
679a4c34fc | ||
![]() |
55362436d9 | ||
![]() |
c1d164070b | ||
![]() |
3a4034a741 | ||
![]() |
a0be113d2e | ||
![]() |
151fb129e7 | ||
![]() |
165c5a5259 | ||
![]() |
74fc1db25b | ||
![]() |
03ff2a8dd5 | ||
d99d15f48d | |||
![]() |
6a998833cd | ||
![]() |
fca4826c7f | ||
![]() |
b0845fc133 | ||
![]() |
70cf8bd9c6 | ||
![]() |
3a2e459f58 | ||
![]() |
330f062099 | ||
![]() |
09872df1c7 | ||
![]() |
67d6399da6 | ||
![]() |
c29724912b | ||
![]() |
99560d9657 | ||
![]() |
b8a7b87873 | ||
![]() |
c9f6c1a054 | ||
![]() |
dd0fcb50b4 | ||
![]() |
c1e9cf00ac | ||
![]() |
84d609f67b | ||
![]() |
86178548d5 | ||
![]() |
d711540a85 | ||
![]() |
6e3158cb00 | ||
![]() |
b216690505 | ||
![]() |
b98896ddd9 | ||
![]() |
a05104d61f | ||
![]() |
7fe1cc8d24 | ||
![]() |
70e84a2c20 | ||
![]() |
084a33ca94 | ||
![]() |
37f619aea3 | ||
![]() |
6904ab10f4 | ||
![]() |
146ef0dd1e | ||
![]() |
5891e86420 | ||
![]() |
2cedad990a | ||
![]() |
109be29e42 | ||
![]() |
13442da69f | ||
![]() |
231dbd53bf | ||
![]() |
d3e48fb096 | ||
![]() |
d544a0b41c | ||
![]() |
cf4799e299 | ||
![]() |
57d9f004aa | ||
![]() |
62cde7a9ef | ||
![]() |
e4fcf25fc5 | ||
![]() |
215c919b33 | ||
![]() |
da44a02d00 | ||
![]() |
867007d9d7 | ||
![]() |
e9ea814233 | ||
![]() |
6b43c82cb5 | ||
![]() |
afbb62fecd | ||
![]() |
8a676e8fa9 | ||
![]() |
c52111e983 | ||
![]() |
d749e8a2c4 | ||
![]() |
8663aa80e6 | ||
![]() |
88b39444a0 | ||
![]() |
4cfc3caa09 | ||
![]() |
28428b3462 | ||
![]() |
24146563a0 | ||
![]() |
2ee9c60c20 | ||
![]() |
fb64675209 | ||
![]() |
76385139cf | ||
![]() |
b18f5a6a81 | ||
![]() |
9d29373b06 | ||
![]() |
f2f1e4dfed | ||
![]() |
0da0f29ade | ||
![]() |
a8519dbac2 | ||
![]() |
d15d07c4f6 | ||
![]() |
d356787a65 | ||
![]() |
a284fa3abb | ||
![]() |
4eeb7523e9 | ||
![]() |
f30fcd6c85 | ||
![]() |
47e8133520 | ||
![]() |
3fa7d59a04 | ||
![]() |
df8dc43946 | ||
![]() |
3b0a2fecfc | ||
![]() |
80af6d534d | ||
![]() |
71c7d61808 | ||
![]() |
637b803b97 | ||
![]() |
314eef0df8 | ||
![]() |
34fa0f8ac6 | ||
![]() |
16366724bd | ||
![]() |
fc8127d070 | ||
![]() |
3ac37fb552 | ||
![]() |
8e51a75772 | ||
![]() |
ba2dab402d | ||
![]() |
796994d222 | ||
![]() |
66e90e9179 | ||
![]() |
1404edbe3a | ||
![]() |
2f68a76409 | ||
![]() |
304d9a20fc | ||
![]() |
b169e327bf | ||
![]() |
7238bbecc4 | ||
![]() |
a49b4ca402 | ||
![]() |
eb0424c946 | ||
![]() |
5d6da57aa3 | ||
![]() |
e968bbc5d8 | ||
![]() |
b1b0e05c54 | ||
![]() |
e547026dfe | ||
![]() |
f816380af5 | ||
![]() |
5e642bfea1 | ||
![]() |
0d1a22f74f | ||
![]() |
20a1af936d | ||
![]() |
c9c1b90532 | ||
![]() |
4192281e14 | ||
![]() |
329deadc9e | ||
![]() |
53845c554d | ||
![]() |
35c9e3beed | ||
![]() |
60f66768cf | ||
![]() |
3e88974a80 | ||
![]() |
09e7d6cd26 | ||
![]() |
5d5ad5d6dd | ||
![]() |
e70894c360 | ||
![]() |
4c11886c4e | ||
![]() |
41e2f2e75f | ||
![]() |
864abbd425 | ||
![]() |
f333e7eb56 | ||
![]() |
a2ac2ec3c3 | ||
![]() |
06d52afcda | ||
![]() |
e65ba62c37 |
@@ -357,6 +357,53 @@ elseif(WIN32)
|
||||
|
||||
endif()
|
||||
|
||||
if(WITH_XR_OPENXR)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_Xr.cpp
|
||||
intern/GHOST_XrContext.cpp
|
||||
intern/GHOST_XrEvent.cpp
|
||||
intern/GHOST_XrGraphicsBinding.cpp
|
||||
intern/GHOST_XrSession.cpp
|
||||
intern/GHOST_XrSwapchain.cpp
|
||||
|
||||
GHOST_IXrContext.h
|
||||
intern/GHOST_IXrGraphicsBinding.h
|
||||
intern/GHOST_Xr_intern.h
|
||||
intern/GHOST_Xr_openxr_includes.h
|
||||
intern/GHOST_XrContext.h
|
||||
intern/GHOST_XrSession.h
|
||||
intern/GHOST_XrSwapchain.h
|
||||
)
|
||||
list(APPEND INC_SYS
|
||||
${XR_OPENXR_SDK_INCLUDE_DIR}
|
||||
)
|
||||
list(APPEND LIB
|
||||
${XR_OPENXR_SDK_LIBRARIES}
|
||||
)
|
||||
|
||||
set(XR_PLATFORM_DEFINES -DXR_USE_GRAPHICS_API_OPENGL)
|
||||
|
||||
# Add compiler defines as required by the OpenXR specification.
|
||||
if(WIN32)
|
||||
list(APPEND XR_PLATFORM_DEFINES
|
||||
-DXR_USE_PLATFORM_WIN32
|
||||
-DXR_USE_GRAPHICS_API_D3D11
|
||||
)
|
||||
list(APPEND LIB
|
||||
shlwapi
|
||||
)
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
list(APPEND XR_PLATFORM_DEFINES
|
||||
-DXR_OS_LINUX
|
||||
-DXR_USE_PLATFORM_XLIB
|
||||
)
|
||||
endif()
|
||||
|
||||
add_definitions(-DWITH_XR_OPENXR ${XR_PLATFORM_DEFINES})
|
||||
|
||||
unset(XR_PLATFORM_DEFINES)
|
||||
endif()
|
||||
|
||||
add_definitions(${GL_DEFINITIONS})
|
||||
|
||||
blender_add_lib(bf_intern_ghost "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
@@ -30,21 +30,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Creates a "handle" for a C++ GHOST object.
|
||||
* A handle is just an opaque pointer to an empty struct.
|
||||
* In the API the pointer is cast to the actual C++ class.
|
||||
* The 'name' argument to the macro is the name of the handle to create.
|
||||
*/
|
||||
|
||||
GHOST_DECLARE_HANDLE(GHOST_SystemHandle);
|
||||
GHOST_DECLARE_HANDLE(GHOST_TimerTaskHandle);
|
||||
GHOST_DECLARE_HANDLE(GHOST_WindowHandle);
|
||||
GHOST_DECLARE_HANDLE(GHOST_EventHandle);
|
||||
GHOST_DECLARE_HANDLE(GHOST_RectangleHandle);
|
||||
GHOST_DECLARE_HANDLE(GHOST_EventConsumerHandle);
|
||||
GHOST_DECLARE_HANDLE(GHOST_ContextHandle);
|
||||
|
||||
/**
|
||||
* Definition of a callback routine that receives events.
|
||||
* \param event The event received.
|
||||
@@ -771,6 +756,18 @@ extern GHOST_TSuccess GHOST_ActivateOpenGLContext(GHOST_ContextHandle contexthan
|
||||
*/
|
||||
extern GHOST_TSuccess GHOST_ReleaseOpenGLContext(GHOST_ContextHandle contexthandle);
|
||||
|
||||
/**
|
||||
* Get the OpenGL framebuffer handle that serves as a default framebuffer.
|
||||
*/
|
||||
extern unsigned int GHOST_GetContextDefaultOpenGLFramebuffer(GHOST_ContextHandle contexthandle);
|
||||
|
||||
/**
|
||||
* Returns whether a context is rendered upside down compared to OpenGL. This only needs to be
|
||||
* called if there's a non-OpenGL context, which is really the exception.
|
||||
* So generally, this does not need to be called.
|
||||
*/
|
||||
extern int GHOST_isUpsideDownContext(GHOST_ContextHandle contexthandle);
|
||||
|
||||
/**
|
||||
* Get the OpenGL framebuffer handle that serves as a default framebuffer.
|
||||
*/
|
||||
@@ -1006,6 +1003,91 @@ extern void GHOST_BeginIME(GHOST_WindowHandle windowhandle,
|
||||
*/
|
||||
extern void GHOST_EndIME(GHOST_WindowHandle windowhandle);
|
||||
|
||||
#ifdef WITH_XR_OPENXR
|
||||
|
||||
/* XR-context */
|
||||
|
||||
/**
|
||||
* Set a custom callback to be executed whenever an error occurs. Should be set before calling
|
||||
* #GHOST_XrContextCreate() to get error handling during context creation too.
|
||||
*
|
||||
* \param customdata: Handle to some data that will get passed to \a handler_fn should an error be
|
||||
* thrown.
|
||||
*/
|
||||
void GHOST_XrErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *customdata);
|
||||
|
||||
/**
|
||||
* \brief Initialize the Ghost XR-context.
|
||||
*
|
||||
* Includes setting up the OpenXR runtime link, querying available extensions and API layers,
|
||||
* enabling extensions and API layers.
|
||||
*
|
||||
* \param create_info: Options for creating the XR-context, e.g. debug-flags and ordered array of
|
||||
* graphics bindings to try enabling.
|
||||
*/
|
||||
GHOST_XrContextHandle GHOST_XrContextCreate(const GHOST_XrContextCreateInfo *create_info);
|
||||
/**
|
||||
* Free a XR-context involving OpenXR runtime link destruction and freeing of all internal data.
|
||||
*/
|
||||
void GHOST_XrContextDestroy(GHOST_XrContextHandle xr_context);
|
||||
|
||||
/**
|
||||
* Set callbacks for binding and unbinding a graphics context for a session. The binding callback
|
||||
* may create a new graphics context thereby. In fact that's the sole reason for this callback
|
||||
* approach to binding. Just make sure to have an unbind function set that properly destructs.
|
||||
*
|
||||
* \param bind_fn: Function to retrieve (possibly create) a graphics context.
|
||||
* \param unbind_fn: Function to release (possibly free) a graphics context.
|
||||
*/
|
||||
void GHOST_XrGraphicsContextBindFuncs(GHOST_XrContextHandle xr_context,
|
||||
GHOST_XrGraphicsContextBindFn bind_fn,
|
||||
GHOST_XrGraphicsContextUnbindFn unbind_fn);
|
||||
|
||||
/**
|
||||
* Set the drawing callback for views. A view would typically be either the left or the right eye,
|
||||
* although other configurations are possible. When #GHOST_XrSessionDrawViews() is called to draw
|
||||
* an XR frame, \a draw_view_fn is executed for each view.
|
||||
*
|
||||
* \param draw_view_fn: The callback to draw a single view for an XR frame.
|
||||
*/
|
||||
void GHOST_XrDrawViewFunc(GHOST_XrContextHandle xr_context, GHOST_XrDrawViewFn draw_view_fn);
|
||||
|
||||
/* sessions */
|
||||
/**
|
||||
* Create internal session data for \a xr_context and ask the OpenXR runtime to invoke a session.
|
||||
*
|
||||
* \param begin_info: Options for the session creation.
|
||||
*/
|
||||
void GHOST_XrSessionStart(GHOST_XrContextHandle xr_context,
|
||||
const GHOST_XrSessionBeginInfo *begin_info);
|
||||
/**
|
||||
* Destruct internal session data for \a xr_context and ask the OpenXR runtime to stop a session.
|
||||
*/
|
||||
void GHOST_XrSessionEnd(GHOST_XrContextHandle xr_context);
|
||||
/**
|
||||
* Draw a single frame by calling the view drawing callback defined by #GHOST_XrDrawViewFunc() for
|
||||
* each view and submit it to the OpenXR runtime.
|
||||
*
|
||||
* \param customdata: Handle to some data that will get passed to the view drawing callback.
|
||||
*/
|
||||
void GHOST_XrSessionDrawViews(GHOST_XrContextHandle xr_context, void *customdata);
|
||||
/**
|
||||
* Check if a \a xr_context has a session that, according to the OpenXR definition would be
|
||||
* considered to be 'running'
|
||||
* (https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#session_running).
|
||||
*/
|
||||
int GHOST_XrSessionIsRunning(const GHOST_XrContextHandle xr_context);
|
||||
|
||||
/* events */
|
||||
/**
|
||||
* Invoke handling of all OpenXR events for \a xr_context. Should be called on every main-loop
|
||||
* iteration and will early-exit if \a xr_context is NULL (so caller doesn't have to check).
|
||||
*
|
||||
* \returns GHOST_kSuccess if any event was handled, otherwise GHOST_kFailure.
|
||||
*/
|
||||
GHOST_TSuccess GHOST_XrEventsHandle(GHOST_XrContextHandle xr_context);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -56,6 +56,15 @@ class GHOST_IContext {
|
||||
*/
|
||||
virtual GHOST_TSuccess releaseDrawingContext() = 0;
|
||||
|
||||
virtual unsigned int getDefaultFramebuffer() = 0;
|
||||
|
||||
virtual GHOST_TSuccess swapBuffers() = 0;
|
||||
|
||||
/**
|
||||
* Returns if the window is rendered upside down compared to OpenGL.
|
||||
*/
|
||||
virtual bool isUpsideDown() const = 0;
|
||||
|
||||
#ifdef WITH_CXX_GUARDEDALLOC
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_IContext")
|
||||
#endif
|
||||
|
42
intern/ghost/GHOST_IXrContext.h
Normal file
42
intern/ghost/GHOST_IXrContext.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#ifndef __GHOST_IXRCONTEXT_H__
|
||||
#define __GHOST_IXRCONTEXT_H__
|
||||
|
||||
#include "GHOST_Types.h"
|
||||
|
||||
class GHOST_IXrContext {
|
||||
public:
|
||||
virtual ~GHOST_IXrContext() = default;
|
||||
|
||||
virtual void startSession(const GHOST_XrSessionBeginInfo *begin_info) = 0;
|
||||
virtual void endSession() = 0;
|
||||
virtual bool isSessionRunning() const = 0;
|
||||
virtual void drawSessionViews(void *draw_customdata) = 0;
|
||||
|
||||
virtual void dispatchErrorMessage(const class GHOST_XrException *) const = 0;
|
||||
|
||||
virtual void setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn,
|
||||
GHOST_XrGraphicsContextUnbindFn unbind_fn) = 0;
|
||||
virtual void setDrawViewFunc(GHOST_XrDrawViewFn draw_view_fn) = 0;
|
||||
};
|
||||
|
||||
#endif // __GHOST_IXRCONTEXT_H__
|
@@ -41,6 +41,22 @@
|
||||
} * name
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Creates a "handle" for a C++ GHOST object.
|
||||
* A handle is just an opaque pointer to an empty struct.
|
||||
* In the API the pointer is cast to the actual C++ class.
|
||||
* The 'name' argument to the macro is the name of the handle to create.
|
||||
*/
|
||||
|
||||
GHOST_DECLARE_HANDLE(GHOST_SystemHandle);
|
||||
GHOST_DECLARE_HANDLE(GHOST_TimerTaskHandle);
|
||||
GHOST_DECLARE_HANDLE(GHOST_WindowHandle);
|
||||
GHOST_DECLARE_HANDLE(GHOST_EventHandle);
|
||||
GHOST_DECLARE_HANDLE(GHOST_RectangleHandle);
|
||||
GHOST_DECLARE_HANDLE(GHOST_EventConsumerHandle);
|
||||
GHOST_DECLARE_HANDLE(GHOST_ContextHandle);
|
||||
GHOST_DECLARE_HANDLE(GHOST_XrContextHandle);
|
||||
|
||||
typedef char GHOST_TInt8;
|
||||
typedef unsigned char GHOST_TUns8;
|
||||
typedef short GHOST_TInt16;
|
||||
@@ -580,4 +596,78 @@ struct GHOST_TimerTaskHandle__;
|
||||
typedef void (*GHOST_TimerProcPtr)(struct GHOST_TimerTaskHandle__ *task, GHOST_TUns64 time);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_XR_OPENXR
|
||||
|
||||
/**
|
||||
* The XR view (i.e. the OpenXR runtime) may require a different graphics library than OpenGL. An
|
||||
* offscreen texture of the viewport will then be drawn into using OpenGL, but the final texture
|
||||
* draw call will happen through another lib (say DirectX).
|
||||
*
|
||||
* This enum defines the possible graphics bindings to attempt to enable.
|
||||
*/
|
||||
typedef enum {
|
||||
GHOST_kXrGraphicsUnknown = 0,
|
||||
GHOST_kXrGraphicsOpenGL,
|
||||
# ifdef WIN32
|
||||
GHOST_kXrGraphicsD3D11,
|
||||
# endif
|
||||
/* For later */
|
||||
// GHOST_kXrGraphicsVulkan,
|
||||
} GHOST_TXrGraphicsBinding;
|
||||
/* An array of GHOST_TXrGraphicsBinding items defining the candidate bindings to use. The first
|
||||
* available candidate will be chosen, so order defines priority. */
|
||||
typedef const GHOST_TXrGraphicsBinding *GHOST_XrGraphicsBindingCandidates;
|
||||
|
||||
typedef struct {
|
||||
float position[3];
|
||||
/* Blender convention (w, x, y, z) */
|
||||
float orientation_quat[4];
|
||||
} GHOST_XrPose;
|
||||
|
||||
enum {
|
||||
GHOST_kXrContextDebug = (1 << 0),
|
||||
GHOST_kXrContextDebugTime = (1 << 1),
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const GHOST_XrGraphicsBindingCandidates gpu_binding_candidates;
|
||||
unsigned int gpu_binding_candidates_count;
|
||||
|
||||
unsigned int context_flag;
|
||||
} GHOST_XrContextCreateInfo;
|
||||
|
||||
typedef struct {
|
||||
GHOST_XrPose base_pose;
|
||||
} GHOST_XrSessionBeginInfo;
|
||||
|
||||
typedef struct {
|
||||
int ofsx, ofsy;
|
||||
int width, height;
|
||||
|
||||
GHOST_XrPose pose;
|
||||
|
||||
struct {
|
||||
float angle_left, angle_right;
|
||||
float angle_up, angle_down;
|
||||
} fov;
|
||||
|
||||
/** Set if the buffer should be submitted with a srgb transfer applied. */
|
||||
char expects_srgb_buffer;
|
||||
} GHOST_XrDrawViewInfo;
|
||||
|
||||
typedef struct {
|
||||
const char *user_message;
|
||||
|
||||
void *customdata;
|
||||
} GHOST_XrError;
|
||||
|
||||
typedef void (*GHOST_XrErrorHandlerFn)(const GHOST_XrError *);
|
||||
|
||||
typedef void *(*GHOST_XrGraphicsContextBindFn)(GHOST_TXrGraphicsBinding graphics_lib);
|
||||
typedef void (*GHOST_XrGraphicsContextUnbindFn)(GHOST_TXrGraphicsBinding graphics_lib,
|
||||
void *graphics_context);
|
||||
typedef void (*GHOST_XrDrawViewFn)(const GHOST_XrDrawViewInfo *draw_view, void *customdata);
|
||||
|
||||
#endif
|
||||
|
||||
#endif // __GHOST_TYPES_H__
|
||||
|
@@ -30,7 +30,11 @@
|
||||
#include "GHOST_ISystem.h"
|
||||
#include "GHOST_IEvent.h"
|
||||
#include "GHOST_IEventConsumer.h"
|
||||
#ifdef WITH_XR_OPENXR
|
||||
# include "GHOST_IXrContext.h"
|
||||
#endif
|
||||
#include "intern/GHOST_CallbackEventConsumer.h"
|
||||
#include "intern/GHOST_XrException.h"
|
||||
|
||||
GHOST_SystemHandle GHOST_CreateSystem(void)
|
||||
{
|
||||
@@ -705,6 +709,20 @@ GHOST_TSuccess GHOST_ReleaseOpenGLContext(GHOST_ContextHandle contexthandle)
|
||||
return context->releaseDrawingContext();
|
||||
}
|
||||
|
||||
unsigned int GHOST_GetContextDefaultOpenGLFramebuffer(GHOST_ContextHandle contexthandle)
|
||||
{
|
||||
GHOST_IContext *context = (GHOST_IContext *)contexthandle;
|
||||
|
||||
return context->getDefaultFramebuffer();
|
||||
}
|
||||
|
||||
int GHOST_isUpsideDownContext(GHOST_ContextHandle contexthandle)
|
||||
{
|
||||
GHOST_IContext *context = (GHOST_IContext *)contexthandle;
|
||||
|
||||
return context->isUpsideDown();
|
||||
}
|
||||
|
||||
unsigned int GHOST_GetDefaultOpenGLFramebuffer(GHOST_WindowHandle windowhandle)
|
||||
{
|
||||
GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
|
||||
@@ -914,3 +932,63 @@ void GHOST_EndIME(GHOST_WindowHandle windowhandle)
|
||||
}
|
||||
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
||||
#ifdef WITH_XR_OPENXR
|
||||
|
||||
# define GHOST_XR_CAPI_CALL(call, ctx) \
|
||||
try { \
|
||||
call; \
|
||||
} \
|
||||
catch (GHOST_XrException & e) { \
|
||||
(ctx)->dispatchErrorMessage(&e); \
|
||||
}
|
||||
|
||||
# define GHOST_XR_CAPI_CALL_RET(call, ctx) \
|
||||
try { \
|
||||
return call; \
|
||||
} \
|
||||
catch (GHOST_XrException & e) { \
|
||||
(ctx)->dispatchErrorMessage(&e); \
|
||||
}
|
||||
|
||||
void GHOST_XrSessionStart(GHOST_XrContextHandle xr_contexthandle,
|
||||
const GHOST_XrSessionBeginInfo *begin_info)
|
||||
{
|
||||
GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
|
||||
GHOST_XR_CAPI_CALL(xr_context->startSession(begin_info), xr_context);
|
||||
}
|
||||
|
||||
void GHOST_XrSessionEnd(GHOST_XrContextHandle xr_contexthandle)
|
||||
{
|
||||
GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
|
||||
GHOST_XR_CAPI_CALL(xr_context->endSession(), xr_context);
|
||||
}
|
||||
|
||||
void GHOST_XrSessionDrawViews(GHOST_XrContextHandle xr_contexthandle, void *draw_customdata)
|
||||
{
|
||||
GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
|
||||
GHOST_XR_CAPI_CALL(xr_context->drawSessionViews(draw_customdata), xr_context);
|
||||
}
|
||||
|
||||
int GHOST_XrSessionIsRunning(const GHOST_XrContextHandle xr_contexthandle)
|
||||
{
|
||||
const GHOST_IXrContext *xr_context = (const GHOST_IXrContext *)xr_contexthandle;
|
||||
GHOST_XR_CAPI_CALL_RET(xr_context->isSessionRunning(), xr_context);
|
||||
return 0; /* Only reached if exception is thrown. */
|
||||
}
|
||||
|
||||
void GHOST_XrGraphicsContextBindFuncs(GHOST_XrContextHandle xr_contexthandle,
|
||||
GHOST_XrGraphicsContextBindFn bind_fn,
|
||||
GHOST_XrGraphicsContextUnbindFn unbind_fn)
|
||||
{
|
||||
GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
|
||||
GHOST_XR_CAPI_CALL(xr_context->setGraphicsContextBindFuncs(bind_fn, unbind_fn), xr_context);
|
||||
}
|
||||
|
||||
void GHOST_XrDrawViewFunc(GHOST_XrContextHandle xr_contexthandle, GHOST_XrDrawViewFn draw_view_fn)
|
||||
{
|
||||
GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
|
||||
GHOST_XR_CAPI_CALL(xr_context->setDrawViewFunc(draw_view_fn), xr_context);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -119,6 +119,14 @@ class GHOST_Context : public GHOST_IContext {
|
||||
return m_stereoVisual;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the window is rendered upside down compared to OpenGL.
|
||||
*/
|
||||
inline bool isUpsideDown() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the OpenGL framebuffer associated with the OpenGL context
|
||||
* \return The ID of an OpenGL framebuffer object.
|
||||
|
@@ -30,6 +30,9 @@
|
||||
#include "GHOST_Context.h"
|
||||
|
||||
class GHOST_ContextD3D : public GHOST_Context {
|
||||
/* XR code needs low level graphics data to send to OpenXR. */
|
||||
friend class GHOST_XrGraphicsBindingD3D;
|
||||
|
||||
public:
|
||||
GHOST_ContextD3D(bool stereoVisual, HWND hWnd);
|
||||
~GHOST_ContextD3D();
|
||||
|
@@ -273,6 +273,7 @@ GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext()
|
||||
m_window = (Window)glXCreatePbuffer(m_display, framebuffer_config[0], pbuffer_attribs);
|
||||
}
|
||||
|
||||
m_fbconfig = framebuffer_config[0];
|
||||
XFree(framebuffer_config);
|
||||
}
|
||||
}
|
||||
|
@@ -38,6 +38,9 @@
|
||||
#endif
|
||||
|
||||
class GHOST_ContextGLX : public GHOST_Context {
|
||||
/* XR code needs low level graphics data to send to OpenXR. */
|
||||
friend class GHOST_XrGraphicsBindingOpenGL;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
|
@@ -35,6 +35,9 @@
|
||||
#endif
|
||||
|
||||
class GHOST_ContextWGL : public GHOST_Context {
|
||||
/* XR code needs low level graphics data to send to OpenXR. */
|
||||
friend class GHOST_XrGraphicsBindingOpenGL;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
|
71
intern/ghost/intern/GHOST_IXrGraphicsBinding.h
Normal file
71
intern/ghost/intern/GHOST_IXrGraphicsBinding.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#ifndef __GHOST_IXRGRAPHICSBINDING_H__
|
||||
#define __GHOST_IXRGRAPHICSBINDING_H__
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "GHOST_Xr_openxr_includes.h"
|
||||
|
||||
class GHOST_IXrGraphicsBinding {
|
||||
friend std::unique_ptr<GHOST_IXrGraphicsBinding> GHOST_XrGraphicsBindingCreateFromType(
|
||||
GHOST_TXrGraphicsBinding type);
|
||||
|
||||
public:
|
||||
union {
|
||||
#if defined(WITH_X11)
|
||||
XrGraphicsBindingOpenGLXlibKHR glx;
|
||||
#elif defined(WIN32)
|
||||
XrGraphicsBindingOpenGLWin32KHR wgl;
|
||||
XrGraphicsBindingD3D11KHR d3d11;
|
||||
#endif
|
||||
} oxr_binding;
|
||||
|
||||
/**
|
||||
* Does __not__ require this object is initialized (can be called prior to
|
||||
* #initFromGhostContext). It's actually meant to be called first.
|
||||
*
|
||||
* \param r_requirement_info Return argument to retrieve an informal string on the requirements
|
||||
* to be met. Useful for error/debug messages.
|
||||
*/
|
||||
virtual bool checkVersionRequirements(class GHOST_Context *ghost_ctx,
|
||||
XrInstance instance,
|
||||
XrSystemId system_id,
|
||||
std::string *r_requirement_info) const = 0;
|
||||
virtual void initFromGhostContext(class GHOST_Context *ghost_ctx) = 0;
|
||||
virtual bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
|
||||
int64_t *r_result) const = 0;
|
||||
virtual std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(
|
||||
uint32_t image_count) = 0;
|
||||
virtual void submitToSwapchainImage(XrSwapchainImageBaseHeader *swapchain_image,
|
||||
const GHOST_XrDrawViewInfo *draw_info) = 0;
|
||||
|
||||
protected:
|
||||
/* Use GHOST_XrGraphicsBindingCreateFromType! */
|
||||
GHOST_IXrGraphicsBinding() = default;
|
||||
};
|
||||
|
||||
std::unique_ptr<GHOST_IXrGraphicsBinding> GHOST_XrGraphicsBindingCreateFromType(
|
||||
GHOST_TXrGraphicsBinding type);
|
||||
|
||||
#endif /* __GHOST_IXRGRAPHICSBINDING_H__ */
|
59
intern/ghost/intern/GHOST_Xr.cpp
Normal file
59
intern/ghost/intern/GHOST_Xr.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*
|
||||
* Abstraction for XR (VR, AR, MR, ..) access via OpenXR.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
#include "GHOST_C-api.h"
|
||||
|
||||
#include "GHOST_Xr_intern.h"
|
||||
#include "GHOST_XrContext.h"
|
||||
#include "GHOST_XrException.h"
|
||||
|
||||
GHOST_XrContextHandle GHOST_XrContextCreate(const GHOST_XrContextCreateInfo *create_info)
|
||||
{
|
||||
GHOST_XrContext *xr_context = new GHOST_XrContext(create_info);
|
||||
|
||||
/* TODO GHOST_XrContext's should probably be owned by the GHOST_System, which will handle context
|
||||
* creation and destruction. Try-catch logic can be moved to C-API then. */
|
||||
try {
|
||||
xr_context->initialize(create_info);
|
||||
}
|
||||
catch (GHOST_XrException &e) {
|
||||
xr_context->dispatchErrorMessage(&e);
|
||||
delete xr_context;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return (GHOST_XrContextHandle)xr_context;
|
||||
}
|
||||
|
||||
void GHOST_XrContextDestroy(GHOST_XrContextHandle xr_contexthandle)
|
||||
{
|
||||
delete (GHOST_XrContext *)xr_contexthandle;
|
||||
}
|
||||
|
||||
void GHOST_XrErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *customdata)
|
||||
{
|
||||
GHOST_XrContext::setErrorHandler(handler_fn, customdata);
|
||||
}
|
550
intern/ghost/intern/GHOST_XrContext.cpp
Normal file
550
intern/ghost/intern/GHOST_XrContext.cpp
Normal file
@@ -0,0 +1,550 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*
|
||||
* Abstraction for XR (VR, AR, MR, ..) access via OpenXR.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "GHOST_Types.h"
|
||||
#include "GHOST_Xr_intern.h"
|
||||
#include "GHOST_XrException.h"
|
||||
#include "GHOST_XrSession.h"
|
||||
|
||||
#include "GHOST_XrContext.h"
|
||||
|
||||
struct OpenXRInstanceData {
|
||||
XrInstance instance = XR_NULL_HANDLE;
|
||||
XrInstanceProperties instance_properties = {};
|
||||
|
||||
std::vector<XrExtensionProperties> extensions;
|
||||
std::vector<XrApiLayerProperties> layers;
|
||||
|
||||
static PFN_xrCreateDebugUtilsMessengerEXT s_xrCreateDebugUtilsMessengerEXT_fn;
|
||||
static PFN_xrDestroyDebugUtilsMessengerEXT s_xrDestroyDebugUtilsMessengerEXT_fn;
|
||||
|
||||
XrDebugUtilsMessengerEXT debug_messenger = XR_NULL_HANDLE;
|
||||
};
|
||||
|
||||
PFN_xrCreateDebugUtilsMessengerEXT OpenXRInstanceData::s_xrCreateDebugUtilsMessengerEXT_fn =
|
||||
nullptr;
|
||||
PFN_xrDestroyDebugUtilsMessengerEXT OpenXRInstanceData::s_xrDestroyDebugUtilsMessengerEXT_fn =
|
||||
nullptr;
|
||||
|
||||
GHOST_XrErrorHandlerFn GHOST_XrContext::s_error_handler = nullptr;
|
||||
void *GHOST_XrContext::s_error_handler_customdata = nullptr;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Create, Initialize and Destruct
|
||||
*
|
||||
* \{ */
|
||||
|
||||
GHOST_XrContext::GHOST_XrContext(const GHOST_XrContextCreateInfo *create_info)
|
||||
: m_oxr(new OpenXRInstanceData()),
|
||||
m_debug(create_info->context_flag & GHOST_kXrContextDebug),
|
||||
m_debug_time(create_info->context_flag & GHOST_kXrContextDebugTime)
|
||||
{
|
||||
}
|
||||
|
||||
GHOST_XrContext::~GHOST_XrContext()
|
||||
{
|
||||
/* Destroy session data first. Otherwise xrDestroyInstance will implicitly do it, before the
|
||||
* session had a chance to do so explicitly. */
|
||||
m_session = nullptr;
|
||||
|
||||
if (m_oxr->debug_messenger != XR_NULL_HANDLE) {
|
||||
assert(m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn != nullptr);
|
||||
m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn(m_oxr->debug_messenger);
|
||||
}
|
||||
if (m_oxr->instance != XR_NULL_HANDLE) {
|
||||
CHECK_XR_ASSERT(xrDestroyInstance(m_oxr->instance));
|
||||
m_oxr->instance = XR_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_XrContext::initialize(const GHOST_XrContextCreateInfo *create_info)
|
||||
{
|
||||
initApiLayers();
|
||||
initExtensions();
|
||||
if (isDebugMode()) {
|
||||
printAvailableAPILayersAndExtensionsInfo();
|
||||
}
|
||||
|
||||
m_gpu_binding_type = determineGraphicsBindingTypeToEnable(create_info);
|
||||
|
||||
assert(m_oxr->instance == XR_NULL_HANDLE);
|
||||
createOpenXRInstance();
|
||||
storeInstanceProperties();
|
||||
printInstanceInfo();
|
||||
if (isDebugMode()) {
|
||||
initDebugMessenger();
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_XrContext::createOpenXRInstance()
|
||||
{
|
||||
XrInstanceCreateInfo create_info = {XR_TYPE_INSTANCE_CREATE_INFO};
|
||||
|
||||
std::string("Blender").copy(create_info.applicationInfo.applicationName,
|
||||
XR_MAX_APPLICATION_NAME_SIZE);
|
||||
create_info.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
|
||||
|
||||
getAPILayersToEnable(m_enabled_layers);
|
||||
getExtensionsToEnable(m_enabled_extensions);
|
||||
create_info.enabledApiLayerCount = m_enabled_layers.size();
|
||||
create_info.enabledApiLayerNames = m_enabled_layers.data();
|
||||
create_info.enabledExtensionCount = m_enabled_extensions.size();
|
||||
create_info.enabledExtensionNames = m_enabled_extensions.data();
|
||||
if (isDebugMode()) {
|
||||
printExtensionsAndAPILayersToEnable();
|
||||
}
|
||||
|
||||
CHECK_XR(xrCreateInstance(&create_info, &m_oxr->instance),
|
||||
"Failed to connect to an OpenXR runtime.");
|
||||
}
|
||||
|
||||
void GHOST_XrContext::storeInstanceProperties()
|
||||
{
|
||||
const std::map<std::string, GHOST_TXrOpenXRRuntimeID> runtime_map = {
|
||||
{"Monado(XRT) by Collabora et al", OPENXR_RUNTIME_MONADO},
|
||||
{"Oculus", OPENXR_RUNTIME_OCULUS},
|
||||
{"Windows Mixed Reality Runtime", OPENXR_RUNTIME_WMR}};
|
||||
decltype(runtime_map)::const_iterator runtime_map_iter;
|
||||
|
||||
m_oxr->instance_properties.type = XR_TYPE_INSTANCE_PROPERTIES;
|
||||
CHECK_XR(xrGetInstanceProperties(m_oxr->instance, &m_oxr->instance_properties),
|
||||
"Failed to get OpenXR runtime information. Do you have an active runtime set up?");
|
||||
|
||||
runtime_map_iter = runtime_map.find(m_oxr->instance_properties.runtimeName);
|
||||
if (runtime_map_iter != runtime_map.end()) {
|
||||
m_runtime_id = runtime_map_iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */ /* Create, Initialize and Destruct */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debug Printing
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void GHOST_XrContext::printInstanceInfo()
|
||||
{
|
||||
assert(m_oxr->instance != XR_NULL_HANDLE);
|
||||
|
||||
printf("Connected to OpenXR runtime: %s (Version %u.%u.%u)\n",
|
||||
m_oxr->instance_properties.runtimeName,
|
||||
XR_VERSION_MAJOR(m_oxr->instance_properties.runtimeVersion),
|
||||
XR_VERSION_MINOR(m_oxr->instance_properties.runtimeVersion),
|
||||
XR_VERSION_PATCH(m_oxr->instance_properties.runtimeVersion));
|
||||
}
|
||||
|
||||
void GHOST_XrContext::printAvailableAPILayersAndExtensionsInfo()
|
||||
{
|
||||
puts("Available OpenXR API-layers/extensions:");
|
||||
for (XrApiLayerProperties &layer_info : m_oxr->layers) {
|
||||
printf("Layer: %s\n", layer_info.layerName);
|
||||
}
|
||||
for (XrExtensionProperties &ext_info : m_oxr->extensions) {
|
||||
printf("Extension: %s\n", ext_info.extensionName);
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_XrContext::printExtensionsAndAPILayersToEnable()
|
||||
{
|
||||
for (const char *layer_name : m_enabled_layers) {
|
||||
printf("Enabling OpenXR API-Layer: %s\n", layer_name);
|
||||
}
|
||||
for (const char *ext_name : m_enabled_extensions) {
|
||||
printf("Enabling OpenXR Extension: %s\n", ext_name);
|
||||
}
|
||||
}
|
||||
|
||||
static XrBool32 debug_messenger_func(XrDebugUtilsMessageSeverityFlagsEXT /*messageSeverity*/,
|
||||
XrDebugUtilsMessageTypeFlagsEXT /*messageTypes*/,
|
||||
const XrDebugUtilsMessengerCallbackDataEXT *callbackData,
|
||||
void * /*userData*/)
|
||||
{
|
||||
puts("OpenXR Debug Message:");
|
||||
puts(callbackData->message);
|
||||
return XR_FALSE; /* OpenXR spec suggests always returning false. */
|
||||
}
|
||||
|
||||
void GHOST_XrContext::initDebugMessenger()
|
||||
{
|
||||
XrDebugUtilsMessengerCreateInfoEXT create_info = {XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT};
|
||||
|
||||
/* Extension functions need to be obtained through xrGetInstanceProcAddr(). */
|
||||
if (XR_FAILED(xrGetInstanceProcAddr(
|
||||
m_oxr->instance,
|
||||
"xrCreateDebugUtilsMessengerEXT",
|
||||
(PFN_xrVoidFunction *)&m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn)) ||
|
||||
XR_FAILED(xrGetInstanceProcAddr(
|
||||
m_oxr->instance,
|
||||
"xrDestroyDebugUtilsMessengerEXT",
|
||||
(PFN_xrVoidFunction *)&m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn))) {
|
||||
m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn = nullptr;
|
||||
m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn = nullptr;
|
||||
|
||||
fprintf(stderr,
|
||||
"Could not use XR_EXT_debug_utils to enable debug prints. Not a fatal error, "
|
||||
"continuing without the messenger.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
create_info.messageSeverities = XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
|
||||
XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
|
||||
XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||
XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||
create_info.messageTypes = XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
||||
XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
||||
XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||
create_info.userCallback = debug_messenger_func;
|
||||
|
||||
if (XR_FAILED(m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn(
|
||||
m_oxr->instance, &create_info, &m_oxr->debug_messenger))) {
|
||||
fprintf(stderr,
|
||||
"Failed to create OpenXR debug messenger. Not a fatal error, continuing without the "
|
||||
"messenger.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */ /* Debug Printing */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Error handling
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void GHOST_XrContext::dispatchErrorMessage(const GHOST_XrException *exception) const
|
||||
{
|
||||
GHOST_XrError error;
|
||||
|
||||
error.user_message = exception->m_msg;
|
||||
error.customdata = s_error_handler_customdata;
|
||||
|
||||
if (isDebugMode()) {
|
||||
fprintf(stderr,
|
||||
"Error: \t%s\n\tOpenXR error value: %i\n",
|
||||
error.user_message,
|
||||
exception->m_result);
|
||||
}
|
||||
|
||||
/* Potentially destroys GHOST_XrContext */
|
||||
s_error_handler(&error);
|
||||
}
|
||||
|
||||
void GHOST_XrContext::setErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *customdata)
|
||||
{
|
||||
s_error_handler = handler_fn;
|
||||
s_error_handler_customdata = customdata;
|
||||
}
|
||||
|
||||
/** \} */ /* Error handling */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name OpenXR API-Layers and Extensions
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \param layer_name May be NULL for extensions not belonging to a specific layer.
|
||||
*/
|
||||
void GHOST_XrContext::initExtensionsEx(std::vector<XrExtensionProperties> &extensions,
|
||||
const char *layer_name)
|
||||
{
|
||||
uint32_t extension_count = 0;
|
||||
|
||||
/* Get count for array creation/init first. */
|
||||
CHECK_XR(xrEnumerateInstanceExtensionProperties(layer_name, 0, &extension_count, nullptr),
|
||||
"Failed to query OpenXR runtime information. Do you have an active runtime set up?");
|
||||
|
||||
if (extension_count == 0) {
|
||||
/* Extensions are optional, can successfully exit. */
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < extension_count; i++) {
|
||||
XrExtensionProperties ext = {XR_TYPE_EXTENSION_PROPERTIES};
|
||||
extensions.push_back(ext);
|
||||
}
|
||||
|
||||
/* Actually get the extensions. */
|
||||
CHECK_XR(xrEnumerateInstanceExtensionProperties(
|
||||
layer_name, extension_count, &extension_count, extensions.data()),
|
||||
"Failed to query OpenXR runtime information. Do you have an active runtime set up?");
|
||||
}
|
||||
|
||||
void GHOST_XrContext::initExtensions()
|
||||
{
|
||||
initExtensionsEx(m_oxr->extensions, nullptr);
|
||||
}
|
||||
|
||||
void GHOST_XrContext::initApiLayers()
|
||||
{
|
||||
uint32_t layer_count = 0;
|
||||
|
||||
/* Get count for array creation/init first. */
|
||||
CHECK_XR(xrEnumerateApiLayerProperties(0, &layer_count, nullptr),
|
||||
"Failed to query OpenXR runtime information. Do you have an active runtime set up?");
|
||||
|
||||
if (layer_count == 0) {
|
||||
/* Layers are optional, can safely exit. */
|
||||
return;
|
||||
}
|
||||
|
||||
m_oxr->layers = std::vector<XrApiLayerProperties>(layer_count);
|
||||
for (XrApiLayerProperties &layer : m_oxr->layers) {
|
||||
layer.type = XR_TYPE_API_LAYER_PROPERTIES;
|
||||
}
|
||||
|
||||
/* Actually get the layers. */
|
||||
CHECK_XR(xrEnumerateApiLayerProperties(layer_count, &layer_count, m_oxr->layers.data()),
|
||||
"Failed to query OpenXR runtime information. Do you have an active runtime set up?");
|
||||
for (XrApiLayerProperties &layer : m_oxr->layers) {
|
||||
/* Each layer may have own extensions. */
|
||||
initExtensionsEx(m_oxr->extensions, layer.layerName);
|
||||
}
|
||||
}
|
||||
|
||||
static bool openxr_layer_is_available(const std::vector<XrApiLayerProperties> layers_info,
|
||||
const std::string &layer_name)
|
||||
{
|
||||
for (const XrApiLayerProperties &layer_info : layers_info) {
|
||||
if (layer_info.layerName == layer_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool openxr_extension_is_available(const std::vector<XrExtensionProperties> extensions_info,
|
||||
const std::string &extension_name)
|
||||
{
|
||||
for (const XrExtensionProperties &ext_info : extensions_info) {
|
||||
if (ext_info.extensionName == extension_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather an array of names for the API-layers to enable.
|
||||
*/
|
||||
void GHOST_XrContext::getAPILayersToEnable(std::vector<const char *> &r_ext_names)
|
||||
{
|
||||
static std::vector<std::string> try_layers;
|
||||
|
||||
try_layers.clear();
|
||||
|
||||
if (isDebugMode()) {
|
||||
try_layers.push_back("XR_APILAYER_LUNARG_core_validation");
|
||||
}
|
||||
|
||||
r_ext_names.reserve(try_layers.size());
|
||||
|
||||
for (const std::string &layer : try_layers) {
|
||||
if (openxr_layer_is_available(m_oxr->layers, layer)) {
|
||||
r_ext_names.push_back(layer.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *openxr_ext_name_from_wm_gpu_binding(GHOST_TXrGraphicsBinding binding)
|
||||
{
|
||||
switch (binding) {
|
||||
case GHOST_kXrGraphicsOpenGL:
|
||||
return XR_KHR_OPENGL_ENABLE_EXTENSION_NAME;
|
||||
#ifdef WIN32
|
||||
case GHOST_kXrGraphicsD3D11:
|
||||
return XR_KHR_D3D11_ENABLE_EXTENSION_NAME;
|
||||
#endif
|
||||
case GHOST_kXrGraphicsUnknown:
|
||||
assert(!"Could not identify graphics binding to choose.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather an array of names for the extensions to enable.
|
||||
*/
|
||||
void GHOST_XrContext::getExtensionsToEnable(std::vector<const char *> &r_ext_names)
|
||||
{
|
||||
assert(m_gpu_binding_type != GHOST_kXrGraphicsUnknown);
|
||||
|
||||
const char *gpu_binding = openxr_ext_name_from_wm_gpu_binding(m_gpu_binding_type);
|
||||
static std::vector<std::string> try_ext;
|
||||
|
||||
try_ext.clear();
|
||||
|
||||
/* Try enabling debug extension. */
|
||||
#ifndef WIN32
|
||||
if (isDebugMode()) {
|
||||
try_ext.push_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
}
|
||||
#endif
|
||||
|
||||
r_ext_names.reserve(try_ext.size() + 1); /* + 1 for graphics binding extension. */
|
||||
|
||||
/* Add graphics binding extension. */
|
||||
assert(gpu_binding);
|
||||
assert(openxr_extension_is_available(m_oxr->extensions, gpu_binding));
|
||||
r_ext_names.push_back(gpu_binding);
|
||||
|
||||
for (const std::string &ext : try_ext) {
|
||||
if (openxr_extension_is_available(m_oxr->extensions, ext)) {
|
||||
r_ext_names.push_back(ext.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide which graphics binding extension to use based on
|
||||
* #GHOST_XrContextCreateInfo.gpu_binding_candidates and available extensions.
|
||||
*/
|
||||
GHOST_TXrGraphicsBinding GHOST_XrContext::determineGraphicsBindingTypeToEnable(
|
||||
const GHOST_XrContextCreateInfo *create_info)
|
||||
{
|
||||
assert(create_info->gpu_binding_candidates != NULL);
|
||||
assert(create_info->gpu_binding_candidates_count > 0);
|
||||
|
||||
for (uint32_t i = 0; i < create_info->gpu_binding_candidates_count; i++) {
|
||||
assert(create_info->gpu_binding_candidates[i] != GHOST_kXrGraphicsUnknown);
|
||||
const char *ext_name = openxr_ext_name_from_wm_gpu_binding(
|
||||
create_info->gpu_binding_candidates[i]);
|
||||
if (openxr_extension_is_available(m_oxr->extensions, ext_name)) {
|
||||
return create_info->gpu_binding_candidates[i];
|
||||
}
|
||||
}
|
||||
|
||||
return GHOST_kXrGraphicsUnknown;
|
||||
}
|
||||
|
||||
/** \} */ /* OpenXR API-Layers and Extensions */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Session management
|
||||
*
|
||||
* Manage session lifetime and delegate public calls to #GHOST_XrSession.
|
||||
* \{ */
|
||||
|
||||
void GHOST_XrContext::startSession(const GHOST_XrSessionBeginInfo *begin_info)
|
||||
{
|
||||
if (m_session == nullptr) {
|
||||
m_session = std::unique_ptr<GHOST_XrSession>(new GHOST_XrSession(this));
|
||||
}
|
||||
|
||||
m_session->start(begin_info);
|
||||
}
|
||||
|
||||
void GHOST_XrContext::endSession()
|
||||
{
|
||||
m_session->requestEnd();
|
||||
}
|
||||
|
||||
bool GHOST_XrContext::isSessionRunning() const
|
||||
{
|
||||
return m_session && m_session->isRunning();
|
||||
}
|
||||
|
||||
void GHOST_XrContext::drawSessionViews(void *draw_customdata)
|
||||
{
|
||||
m_session->draw(draw_customdata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates event to session, allowing context to destruct the session if needed.
|
||||
*/
|
||||
void GHOST_XrContext::handleSessionStateChange(const XrEventDataSessionStateChanged *lifecycle)
|
||||
{
|
||||
if (m_session &&
|
||||
m_session->handleStateChangeEvent(lifecycle) == GHOST_XrSession::SESSION_DESTROY) {
|
||||
m_session = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */ /* Session Management */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public Accessors and Mutators
|
||||
*
|
||||
* Public as in, exposed in the Ghost API.
|
||||
* \{ */
|
||||
|
||||
void GHOST_XrContext::setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn,
|
||||
GHOST_XrGraphicsContextUnbindFn unbind_fn)
|
||||
{
|
||||
if (m_session) {
|
||||
m_session->unbindGraphicsContext();
|
||||
}
|
||||
m_custom_funcs.gpu_ctx_bind_fn = bind_fn;
|
||||
m_custom_funcs.gpu_ctx_unbind_fn = unbind_fn;
|
||||
}
|
||||
|
||||
void GHOST_XrContext::setDrawViewFunc(GHOST_XrDrawViewFn draw_view_fn)
|
||||
{
|
||||
m_custom_funcs.draw_view_fn = draw_view_fn;
|
||||
}
|
||||
|
||||
/** \} */ /* Public Accessors and Mutators */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Ghost Internal Accessors and Mutators
|
||||
*
|
||||
* \{ */
|
||||
|
||||
GHOST_TXrOpenXRRuntimeID GHOST_XrContext::getOpenXRRuntimeID() const
|
||||
{
|
||||
return m_runtime_id;
|
||||
}
|
||||
|
||||
const GHOST_XrCustomFuncs &GHOST_XrContext::getCustomFuncs() const
|
||||
{
|
||||
return m_custom_funcs;
|
||||
}
|
||||
|
||||
GHOST_TXrGraphicsBinding GHOST_XrContext::getGraphicsBindingType() const
|
||||
{
|
||||
return m_gpu_binding_type;
|
||||
}
|
||||
|
||||
XrInstance GHOST_XrContext::getInstance() const
|
||||
{
|
||||
return m_oxr->instance;
|
||||
}
|
||||
|
||||
bool GHOST_XrContext::isDebugMode() const
|
||||
{
|
||||
return m_debug;
|
||||
}
|
||||
|
||||
bool GHOST_XrContext::isDebugTimeMode() const
|
||||
{
|
||||
return m_debug_time;
|
||||
}
|
||||
|
||||
/** \} */ /* Ghost Internal Accessors and Mutators */
|
127
intern/ghost/intern/GHOST_XrContext.h
Normal file
127
intern/ghost/intern/GHOST_XrContext.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#ifndef __GHOST_XRCONTEXT_H__
|
||||
#define __GHOST_XRCONTEXT_H__
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "GHOST_IXrContext.h"
|
||||
|
||||
struct OpenXRInstanceData;
|
||||
|
||||
struct GHOST_XrCustomFuncs {
|
||||
/** Function to retrieve (possibly create) a graphics context. */
|
||||
GHOST_XrGraphicsContextBindFn gpu_ctx_bind_fn = nullptr;
|
||||
/** Function to release (possibly free) a graphics context. */
|
||||
GHOST_XrGraphicsContextUnbindFn gpu_ctx_unbind_fn = nullptr;
|
||||
|
||||
/** Custom per-view draw function for Blender side drawing. */
|
||||
GHOST_XrDrawViewFn draw_view_fn = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* In some occasions, runtime specific handling is needed, e.g. to work around runtime bugs.
|
||||
*/
|
||||
enum GHOST_TXrOpenXRRuntimeID {
|
||||
OPENXR_RUNTIME_MONADO,
|
||||
OPENXR_RUNTIME_OCULUS,
|
||||
OPENXR_RUNTIME_WMR, /* Windows Mixed Reality */
|
||||
|
||||
OPENXR_RUNTIME_UNKNOWN
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Main GHOST container to manage OpenXR through.
|
||||
*
|
||||
* Creating a context using #GHOST_XrContextCreate involves dynamically connecting to the OpenXR
|
||||
* runtime, likely reading the OS OpenXR configuration (i.e. active_runtime.json). So this is
|
||||
* something that should better be done using lazy-initialization.
|
||||
*/
|
||||
class GHOST_XrContext : public GHOST_IXrContext {
|
||||
public:
|
||||
GHOST_XrContext(const GHOST_XrContextCreateInfo *create_info);
|
||||
~GHOST_XrContext();
|
||||
void initialize(const GHOST_XrContextCreateInfo *create_info);
|
||||
|
||||
void startSession(const GHOST_XrSessionBeginInfo *begin_info) override;
|
||||
void endSession() override;
|
||||
bool isSessionRunning() const override;
|
||||
void drawSessionViews(void *draw_customdata) override;
|
||||
|
||||
static void setErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *customdata);
|
||||
void dispatchErrorMessage(const class GHOST_XrException *exception) const override;
|
||||
|
||||
void setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn,
|
||||
GHOST_XrGraphicsContextUnbindFn unbind_fn) override;
|
||||
void setDrawViewFunc(GHOST_XrDrawViewFn draw_view_fn) override;
|
||||
|
||||
void handleSessionStateChange(const XrEventDataSessionStateChanged *lifecycle);
|
||||
|
||||
GHOST_TXrOpenXRRuntimeID getOpenXRRuntimeID() const;
|
||||
const GHOST_XrCustomFuncs &getCustomFuncs() const;
|
||||
GHOST_TXrGraphicsBinding getGraphicsBindingType() const;
|
||||
XrInstance getInstance() const;
|
||||
bool isDebugMode() const;
|
||||
bool isDebugTimeMode() const;
|
||||
|
||||
private:
|
||||
static GHOST_XrErrorHandlerFn s_error_handler;
|
||||
static void *s_error_handler_customdata;
|
||||
|
||||
std::unique_ptr<OpenXRInstanceData> m_oxr;
|
||||
|
||||
GHOST_TXrOpenXRRuntimeID m_runtime_id = OPENXR_RUNTIME_UNKNOWN;
|
||||
|
||||
/* The active GHOST XR Session. Null while no session runs. */
|
||||
std::unique_ptr<class GHOST_XrSession> m_session;
|
||||
|
||||
/** Active graphics binding type. */
|
||||
GHOST_TXrGraphicsBinding m_gpu_binding_type = GHOST_kXrGraphicsUnknown;
|
||||
|
||||
/** Names of enabled extensions. */
|
||||
std::vector<const char *> m_enabled_extensions;
|
||||
/** Names of enabled API-layers. */
|
||||
std::vector<const char *> m_enabled_layers;
|
||||
|
||||
GHOST_XrCustomFuncs m_custom_funcs;
|
||||
|
||||
/** Enable debug message prints and OpenXR API validation layers. */
|
||||
bool m_debug = false;
|
||||
bool m_debug_time = false;
|
||||
|
||||
void createOpenXRInstance();
|
||||
void storeInstanceProperties();
|
||||
void initDebugMessenger();
|
||||
|
||||
void printInstanceInfo();
|
||||
void printAvailableAPILayersAndExtensionsInfo();
|
||||
void printExtensionsAndAPILayersToEnable();
|
||||
|
||||
void initApiLayers();
|
||||
void initExtensions();
|
||||
void initExtensionsEx(std::vector<XrExtensionProperties> &extensions, const char *layer_name);
|
||||
void getAPILayersToEnable(std::vector<const char *> &r_ext_names);
|
||||
void getExtensionsToEnable(std::vector<const char *> &r_ext_names);
|
||||
GHOST_TXrGraphicsBinding determineGraphicsBindingTypeToEnable(
|
||||
const GHOST_XrContextCreateInfo *create_info);
|
||||
};
|
||||
|
||||
#endif // __GHOST_XRCONTEXT_H__
|
64
intern/ghost/intern/GHOST_XrEvent.cpp
Normal file
64
intern/ghost/intern/GHOST_XrEvent.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "GHOST_C-api.h"
|
||||
#include "GHOST_Xr_intern.h"
|
||||
#include "GHOST_XrContext.h"
|
||||
|
||||
static bool GHOST_XrEventPollNext(XrInstance instance, XrEventDataBuffer &r_event_data)
|
||||
{
|
||||
/* (Re-)initialize as required by specification. */
|
||||
r_event_data.type = XR_TYPE_EVENT_DATA_BUFFER;
|
||||
r_event_data.next = nullptr;
|
||||
|
||||
return (xrPollEvent(instance, &r_event_data) == XR_SUCCESS);
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_XrEventsHandle(GHOST_XrContextHandle xr_contexthandle)
|
||||
{
|
||||
GHOST_XrContext *xr_context = (GHOST_XrContext *)xr_contexthandle;
|
||||
XrEventDataBuffer event_buffer; /* Structure big enough to hold all possible events. */
|
||||
|
||||
if (xr_context == NULL) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
while (GHOST_XrEventPollNext(xr_context->getInstance(), event_buffer)) {
|
||||
XrEventDataBaseHeader *event = (XrEventDataBaseHeader *)&event_buffer;
|
||||
|
||||
switch (event->type) {
|
||||
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
|
||||
xr_context->handleSessionStateChange((XrEventDataSessionStateChanged *)event);
|
||||
return GHOST_kSuccess;
|
||||
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
|
||||
GHOST_XrContextDestroy(xr_contexthandle);
|
||||
return GHOST_kSuccess;
|
||||
default:
|
||||
if (xr_context->isDebugMode()) {
|
||||
printf("Unhandled event: %i\n", event->type);
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
}
|
||||
|
||||
return GHOST_kFailure;
|
||||
}
|
45
intern/ghost/intern/GHOST_XrException.h
Normal file
45
intern/ghost/intern/GHOST_XrException.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#ifndef __GHOST_XREXCEPTION_H__
|
||||
#define __GHOST_XREXCEPTION_H__
|
||||
|
||||
#include <exception>
|
||||
|
||||
class GHOST_XrException : public std::exception {
|
||||
friend class GHOST_XrContext;
|
||||
|
||||
public:
|
||||
GHOST_XrException(const char *msg, int result = 0)
|
||||
: std::exception(), m_msg(msg), m_result(result)
|
||||
{
|
||||
}
|
||||
|
||||
const char *what() const noexcept override
|
||||
{
|
||||
return m_msg;
|
||||
}
|
||||
|
||||
private:
|
||||
const char *m_msg;
|
||||
int m_result;
|
||||
};
|
||||
|
||||
#endif // __GHOST_XREXCEPTION_H__
|
316
intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
Normal file
316
intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
Normal file
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
|
||||
#if defined(WITH_X11)
|
||||
# include "GHOST_ContextGLX.h"
|
||||
#elif defined(WIN32)
|
||||
# include "GHOST_ContextWGL.h"
|
||||
# include "GHOST_ContextD3D.h"
|
||||
#endif
|
||||
#include "GHOST_C-api.h"
|
||||
#include "GHOST_Xr_intern.h"
|
||||
|
||||
#include "GHOST_IXrGraphicsBinding.h"
|
||||
|
||||
static bool choose_swapchain_format_from_candidates(std::vector<int64_t> gpu_binding_formats,
|
||||
std::vector<int64_t> runtime_formats,
|
||||
int64_t *r_result)
|
||||
{
|
||||
if (gpu_binding_formats.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto res = std::find_first_of(gpu_binding_formats.begin(),
|
||||
gpu_binding_formats.end(),
|
||||
runtime_formats.begin(),
|
||||
runtime_formats.end());
|
||||
if (res == gpu_binding_formats.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*r_result = *res;
|
||||
return true;
|
||||
}
|
||||
|
||||
class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding {
|
||||
public:
|
||||
~GHOST_XrGraphicsBindingOpenGL()
|
||||
{
|
||||
if (m_fbo != 0) {
|
||||
glDeleteFramebuffers(1, &m_fbo);
|
||||
}
|
||||
}
|
||||
|
||||
bool checkVersionRequirements(GHOST_Context *ghost_ctx,
|
||||
XrInstance instance,
|
||||
XrSystemId system_id,
|
||||
std::string *r_requirement_info) const override
|
||||
{
|
||||
#if defined(WITH_X11)
|
||||
GHOST_ContextGLX *ctx_gl = static_cast<GHOST_ContextGLX *>(ghost_ctx);
|
||||
#else
|
||||
GHOST_ContextWGL *ctx_gl = static_cast<GHOST_ContextWGL *>(ghost_ctx);
|
||||
#endif
|
||||
static PFN_xrGetOpenGLGraphicsRequirementsKHR s_xrGetOpenGLGraphicsRequirementsKHR_fn =
|
||||
nullptr;
|
||||
XrGraphicsRequirementsOpenGLKHR gpu_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR};
|
||||
const XrVersion gl_version = XR_MAKE_VERSION(
|
||||
ctx_gl->m_contextMajorVersion, ctx_gl->m_contextMinorVersion, 0);
|
||||
|
||||
if (!s_xrGetOpenGLGraphicsRequirementsKHR_fn &&
|
||||
XR_FAILED(xrGetInstanceProcAddr(
|
||||
instance,
|
||||
"xrGetOpenGLGraphicsRequirementsKHR",
|
||||
(PFN_xrVoidFunction *)&s_xrGetOpenGLGraphicsRequirementsKHR_fn))) {
|
||||
s_xrGetOpenGLGraphicsRequirementsKHR_fn = nullptr;
|
||||
}
|
||||
|
||||
s_xrGetOpenGLGraphicsRequirementsKHR_fn(instance, system_id, &gpu_requirements);
|
||||
|
||||
if (r_requirement_info) {
|
||||
std::ostringstream strstream;
|
||||
strstream << "Min OpenGL version "
|
||||
<< XR_VERSION_MAJOR(gpu_requirements.minApiVersionSupported) << "."
|
||||
<< XR_VERSION_MINOR(gpu_requirements.minApiVersionSupported) << std::endl;
|
||||
strstream << "Max OpenGL version "
|
||||
<< XR_VERSION_MAJOR(gpu_requirements.maxApiVersionSupported) << "."
|
||||
<< XR_VERSION_MINOR(gpu_requirements.maxApiVersionSupported) << std::endl;
|
||||
|
||||
*r_requirement_info = strstream.str();
|
||||
}
|
||||
|
||||
return (gl_version >= gpu_requirements.minApiVersionSupported) &&
|
||||
(gl_version <= gpu_requirements.maxApiVersionSupported);
|
||||
}
|
||||
|
||||
void initFromGhostContext(GHOST_Context *ghost_ctx) override
|
||||
{
|
||||
#if defined(WITH_X11)
|
||||
GHOST_ContextGLX *ctx_glx = static_cast<GHOST_ContextGLX *>(ghost_ctx);
|
||||
XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx->m_display, ctx_glx->m_fbconfig);
|
||||
|
||||
oxr_binding.glx.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
|
||||
oxr_binding.glx.xDisplay = ctx_glx->m_display;
|
||||
oxr_binding.glx.glxFBConfig = ctx_glx->m_fbconfig;
|
||||
oxr_binding.glx.glxDrawable = ctx_glx->m_window;
|
||||
oxr_binding.glx.glxContext = ctx_glx->m_context;
|
||||
oxr_binding.glx.visualid = visual_info->visualid;
|
||||
#elif defined(WIN32)
|
||||
GHOST_ContextWGL *ctx_wgl = static_cast<GHOST_ContextWGL *>(ghost_ctx);
|
||||
|
||||
oxr_binding.wgl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
|
||||
oxr_binding.wgl.hDC = ctx_wgl->m_hDC;
|
||||
oxr_binding.wgl.hGLRC = ctx_wgl->m_hGLRC;
|
||||
#endif
|
||||
|
||||
/* Generate a framebuffer to use for blitting into the texture. */
|
||||
glGenFramebuffers(1, &m_fbo);
|
||||
}
|
||||
|
||||
bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
|
||||
int64_t *r_result) const override
|
||||
{
|
||||
std::vector<int64_t> gpu_binding_formats = {GL_RGBA8};
|
||||
return choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result);
|
||||
}
|
||||
|
||||
std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
|
||||
{
|
||||
std::vector<XrSwapchainImageOpenGLKHR> ogl_images(image_count);
|
||||
std::vector<XrSwapchainImageBaseHeader *> base_images;
|
||||
|
||||
/* Need to return vector of base header pointers, so of a different type. Need to build a new
|
||||
* list with this type, and keep the initial one alive. */
|
||||
for (XrSwapchainImageOpenGLKHR &image : ogl_images) {
|
||||
image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
|
||||
base_images.push_back(reinterpret_cast<XrSwapchainImageBaseHeader *>(&image));
|
||||
}
|
||||
|
||||
/* Keep alive. */
|
||||
m_image_cache.push_back(std::move(ogl_images));
|
||||
|
||||
return base_images;
|
||||
}
|
||||
|
||||
void submitToSwapchainImage(XrSwapchainImageBaseHeader *swapchain_image,
|
||||
const GHOST_XrDrawViewInfo *draw_info) override
|
||||
{
|
||||
XrSwapchainImageOpenGLKHR *ogl_swapchain_image = reinterpret_cast<XrSwapchainImageOpenGLKHR *>(
|
||||
swapchain_image);
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);
|
||||
|
||||
glFramebufferTexture2D(
|
||||
GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ogl_swapchain_image->image, 0);
|
||||
|
||||
glBlitFramebuffer(draw_info->ofsx,
|
||||
draw_info->ofsy,
|
||||
draw_info->ofsx + draw_info->width,
|
||||
draw_info->ofsy + draw_info->height,
|
||||
draw_info->ofsx,
|
||||
draw_info->ofsy,
|
||||
draw_info->ofsx + draw_info->width,
|
||||
draw_info->ofsy + draw_info->height,
|
||||
GL_COLOR_BUFFER_BIT,
|
||||
GL_LINEAR);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<std::vector<XrSwapchainImageOpenGLKHR>> m_image_cache;
|
||||
GLuint m_fbo = 0;
|
||||
};
|
||||
|
||||
#ifdef WIN32
|
||||
class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
|
||||
public:
|
||||
~GHOST_XrGraphicsBindingD3D()
|
||||
{
|
||||
if (m_shared_resource) {
|
||||
m_ghost_ctx->disposeSharedOpenGLResource(m_shared_resource);
|
||||
}
|
||||
}
|
||||
|
||||
bool checkVersionRequirements(GHOST_Context *ghost_ctx,
|
||||
XrInstance instance,
|
||||
XrSystemId system_id,
|
||||
std::string *r_requirement_info) const override
|
||||
{
|
||||
GHOST_ContextD3D *ctx_dx = static_cast<GHOST_ContextD3D *>(ghost_ctx);
|
||||
static PFN_xrGetD3D11GraphicsRequirementsKHR s_xrGetD3D11GraphicsRequirementsKHR_fn = nullptr;
|
||||
XrGraphicsRequirementsD3D11KHR gpu_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR};
|
||||
|
||||
if (!s_xrGetD3D11GraphicsRequirementsKHR_fn &&
|
||||
XR_FAILED(xrGetInstanceProcAddr(
|
||||
instance,
|
||||
"xrGetD3D11GraphicsRequirementsKHR",
|
||||
(PFN_xrVoidFunction *)&s_xrGetD3D11GraphicsRequirementsKHR_fn))) {
|
||||
s_xrGetD3D11GraphicsRequirementsKHR_fn = nullptr;
|
||||
}
|
||||
|
||||
s_xrGetD3D11GraphicsRequirementsKHR_fn(instance, system_id, &gpu_requirements);
|
||||
|
||||
if (r_requirement_info) {
|
||||
std::ostringstream strstream;
|
||||
strstream << "Minimum DirectX 11 Feature Level " << gpu_requirements.minFeatureLevel
|
||||
<< std::endl;
|
||||
|
||||
*r_requirement_info = std::move(strstream.str());
|
||||
}
|
||||
|
||||
return ctx_dx->m_device->GetFeatureLevel() >= gpu_requirements.minFeatureLevel;
|
||||
}
|
||||
|
||||
void initFromGhostContext(GHOST_Context *ghost_ctx) override
|
||||
{
|
||||
GHOST_ContextD3D *ctx_d3d = static_cast<GHOST_ContextD3D *>(ghost_ctx);
|
||||
|
||||
oxr_binding.d3d11.type = XR_TYPE_GRAPHICS_BINDING_D3D11_KHR;
|
||||
oxr_binding.d3d11.device = ctx_d3d->m_device;
|
||||
m_ghost_ctx = ctx_d3d;
|
||||
}
|
||||
|
||||
bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
|
||||
int64_t *r_result) const override
|
||||
{
|
||||
std::vector<int64_t> gpu_binding_formats = {DXGI_FORMAT_R8G8B8A8_UNORM};
|
||||
return choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result);
|
||||
}
|
||||
|
||||
std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
|
||||
{
|
||||
std::vector<XrSwapchainImageD3D11KHR> d3d_images(image_count);
|
||||
std::vector<XrSwapchainImageBaseHeader *> base_images;
|
||||
|
||||
/* Need to return vector of base header pointers, so of a different type. Need to build a new
|
||||
* list with this type, and keep the initial one alive. */
|
||||
for (XrSwapchainImageD3D11KHR &image : d3d_images) {
|
||||
image.type = XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR;
|
||||
base_images.push_back(reinterpret_cast<XrSwapchainImageBaseHeader *>(&image));
|
||||
}
|
||||
|
||||
/* Keep alive. */
|
||||
m_image_cache.push_back(std::move(d3d_images));
|
||||
|
||||
return base_images;
|
||||
}
|
||||
|
||||
void submitToSwapchainImage(XrSwapchainImageBaseHeader *swapchain_image,
|
||||
const GHOST_XrDrawViewInfo *draw_info) override
|
||||
{
|
||||
XrSwapchainImageD3D11KHR *d3d_swapchain_image = reinterpret_cast<XrSwapchainImageD3D11KHR *>(
|
||||
swapchain_image);
|
||||
|
||||
# if 0
|
||||
/* Ideally we'd just create a render target view for the OpenXR swapchain image texture and
|
||||
* blit from the OpenGL context into it. The NV_DX_interop extension doesn't want to work with
|
||||
* this though. At least not with Optimus hardware. See:
|
||||
* https://github.com/mpv-player/mpv/issues/2949#issuecomment-197262807.
|
||||
*/
|
||||
|
||||
ID3D11RenderTargetView *rtv;
|
||||
CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D,
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM);
|
||||
|
||||
m_ghost_ctx->m_device->CreateRenderTargetView(d3d_swapchain_image->texture, &rtv_desc, &rtv);
|
||||
if (!m_shared_resource) {
|
||||
m_shared_resource = m_ghost_ctx->createSharedOpenGLResource(
|
||||
draw_info->width, draw_info->height, rtv);
|
||||
}
|
||||
m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height);
|
||||
# else
|
||||
if (!m_shared_resource) {
|
||||
m_shared_resource = m_ghost_ctx->createSharedOpenGLResource(draw_info->width,
|
||||
draw_info->height);
|
||||
}
|
||||
m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height);
|
||||
|
||||
m_ghost_ctx->m_device_ctx->OMSetRenderTargets(0, nullptr, nullptr);
|
||||
m_ghost_ctx->m_device_ctx->CopyResource(d3d_swapchain_image->texture,
|
||||
m_ghost_ctx->getSharedTexture2D(m_shared_resource));
|
||||
# endif
|
||||
}
|
||||
|
||||
private:
|
||||
GHOST_ContextD3D *m_ghost_ctx;
|
||||
GHOST_SharedOpenGLResource *m_shared_resource;
|
||||
std::list<std::vector<XrSwapchainImageD3D11KHR>> m_image_cache;
|
||||
};
|
||||
#endif // WIN32
|
||||
|
||||
std::unique_ptr<GHOST_IXrGraphicsBinding> GHOST_XrGraphicsBindingCreateFromType(
|
||||
GHOST_TXrGraphicsBinding type)
|
||||
{
|
||||
switch (type) {
|
||||
case GHOST_kXrGraphicsOpenGL:
|
||||
return std::unique_ptr<GHOST_XrGraphicsBindingOpenGL>(new GHOST_XrGraphicsBindingOpenGL());
|
||||
#ifdef WIN32
|
||||
case GHOST_kXrGraphicsD3D11:
|
||||
return std::unique_ptr<GHOST_XrGraphicsBindingD3D>(new GHOST_XrGraphicsBindingD3D());
|
||||
#endif
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
487
intern/ghost/intern/GHOST_XrSession.cpp
Normal file
487
intern/ghost/intern/GHOST_XrSession.cpp
Normal file
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
|
||||
#include "GHOST_C-api.h"
|
||||
|
||||
#include "GHOST_IXrGraphicsBinding.h"
|
||||
#include "GHOST_Xr_intern.h"
|
||||
#include "GHOST_XrContext.h"
|
||||
#include "GHOST_XrException.h"
|
||||
#include "GHOST_XrSwapchain.h"
|
||||
|
||||
#include "GHOST_XrSession.h"
|
||||
|
||||
struct OpenXRSessionData {
|
||||
XrSystemId system_id = XR_NULL_SYSTEM_ID;
|
||||
XrSession session = XR_NULL_HANDLE;
|
||||
XrSessionState session_state = XR_SESSION_STATE_UNKNOWN;
|
||||
|
||||
/* Only stereo rendering supported now. */
|
||||
const XrViewConfigurationType view_type = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
|
||||
XrSpace reference_space;
|
||||
std::vector<XrView> views;
|
||||
std::vector<std::unique_ptr<GHOST_XrSwapchain>> swapchains;
|
||||
};
|
||||
|
||||
struct GHOST_XrDrawInfo {
|
||||
XrFrameState frame_state;
|
||||
|
||||
/** Time at frame start to benchmark frame render durations. */
|
||||
std::chrono::high_resolution_clock::time_point frame_begin_time;
|
||||
/* Time previous frames took for rendering (in ms). */
|
||||
std::list<double> last_frame_times;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Create, Initialize and Destruct
|
||||
*
|
||||
* \{ */
|
||||
|
||||
GHOST_XrSession::GHOST_XrSession(GHOST_XrContext *xr_context)
|
||||
: m_context(xr_context), m_oxr(new OpenXRSessionData())
|
||||
{
|
||||
}
|
||||
|
||||
GHOST_XrSession::~GHOST_XrSession()
|
||||
{
|
||||
unbindGraphicsContext();
|
||||
|
||||
m_oxr->swapchains.clear();
|
||||
|
||||
if (m_oxr->reference_space != XR_NULL_HANDLE) {
|
||||
CHECK_XR_ASSERT(xrDestroySpace(m_oxr->reference_space));
|
||||
}
|
||||
if (m_oxr->session != XR_NULL_HANDLE) {
|
||||
CHECK_XR_ASSERT(xrDestroySession(m_oxr->session));
|
||||
}
|
||||
|
||||
m_oxr->session = XR_NULL_HANDLE;
|
||||
m_oxr->session_state = XR_SESSION_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* A system in OpenXR the combination of some sort of HMD plus controllers and whatever other
|
||||
* devices are managed through OpenXR. So this attempts to init the HMD and the other devices.
|
||||
*/
|
||||
void GHOST_XrSession::initSystem()
|
||||
{
|
||||
assert(m_context->getInstance() != XR_NULL_HANDLE);
|
||||
assert(m_oxr->system_id == XR_NULL_SYSTEM_ID);
|
||||
|
||||
XrSystemGetInfo system_info = {};
|
||||
system_info.type = XR_TYPE_SYSTEM_GET_INFO;
|
||||
system_info.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
|
||||
|
||||
CHECK_XR(xrGetSystem(m_context->getInstance(), &system_info, &m_oxr->system_id),
|
||||
"Failed to get device information. Is a device plugged in?");
|
||||
}
|
||||
|
||||
/** \} */ /* Create, Initialize and Destruct */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name State Management
|
||||
*
|
||||
* \{ */
|
||||
|
||||
static void create_reference_space(OpenXRSessionData *oxr, const GHOST_XrPose *base_pose)
|
||||
{
|
||||
XrReferenceSpaceCreateInfo create_info = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO};
|
||||
create_info.poseInReferenceSpace.orientation.w = 1.0f;
|
||||
|
||||
create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
|
||||
#if 0
|
||||
/* TODO
|
||||
*
|
||||
* Proper reference space set up is not supported yet. We simply hand OpenXR
|
||||
* the global space as reference space and apply its pose onto the active
|
||||
* camera matrix to get a basic viewing experience going. If there's no active
|
||||
* camera with stick to the world origin.
|
||||
*
|
||||
* Once we have proper reference space set up (i.e. a way to define origin, up-
|
||||
* direction and an initial view rotation perpendicular to the up-direction),
|
||||
* we can hand OpenXR a proper reference pose/space.
|
||||
*/
|
||||
create_info.poseInReferenceSpace.position.x = base_pose->position[0];
|
||||
create_info.poseInReferenceSpace.position.y = base_pose->position[1];
|
||||
create_info.poseInReferenceSpace.position.z = base_pose->position[2];
|
||||
create_info.poseInReferenceSpace.orientation.x = base_pose->orientation_quat[1];
|
||||
create_info.poseInReferenceSpace.orientation.y = base_pose->orientation_quat[2];
|
||||
create_info.poseInReferenceSpace.orientation.z = base_pose->orientation_quat[3];
|
||||
create_info.poseInReferenceSpace.orientation.w = base_pose->orientation_quat[0];
|
||||
#else
|
||||
(void)base_pose;
|
||||
#endif
|
||||
|
||||
CHECK_XR(xrCreateReferenceSpace(oxr->session, &create_info, &oxr->reference_space),
|
||||
"Failed to create reference space.");
|
||||
}
|
||||
|
||||
void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info)
|
||||
{
|
||||
assert(m_context->getInstance() != XR_NULL_HANDLE);
|
||||
assert(m_oxr->session == XR_NULL_HANDLE);
|
||||
if (m_context->getCustomFuncs().gpu_ctx_bind_fn == nullptr) {
|
||||
throw GHOST_XrException(
|
||||
"Invalid API usage: No way to bind graphics context to the XR session. Call "
|
||||
"GHOST_XrGraphicsContextBindFuncs() with valid parameters before starting the "
|
||||
"session (through GHOST_XrSessionStart()).");
|
||||
}
|
||||
|
||||
initSystem();
|
||||
|
||||
bindGraphicsContext();
|
||||
if (m_gpu_ctx == nullptr) {
|
||||
throw GHOST_XrException(
|
||||
"Invalid API usage: No graphics context returned through the callback set with "
|
||||
"GHOST_XrGraphicsContextBindFuncs(). This is required for session starting (through "
|
||||
"GHOST_XrSessionStart()).");
|
||||
}
|
||||
|
||||
std::string requirement_str;
|
||||
m_gpu_binding = GHOST_XrGraphicsBindingCreateFromType(m_context->getGraphicsBindingType());
|
||||
if (!m_gpu_binding->checkVersionRequirements(
|
||||
m_gpu_ctx, m_context->getInstance(), m_oxr->system_id, &requirement_str)) {
|
||||
std::ostringstream strstream;
|
||||
strstream << "Available graphics context version does not meet the following requirements: "
|
||||
<< requirement_str;
|
||||
throw GHOST_XrException(strstream.str().c_str());
|
||||
}
|
||||
m_gpu_binding->initFromGhostContext(m_gpu_ctx);
|
||||
|
||||
XrSessionCreateInfo create_info = {};
|
||||
create_info.type = XR_TYPE_SESSION_CREATE_INFO;
|
||||
create_info.systemId = m_oxr->system_id;
|
||||
create_info.next = &m_gpu_binding->oxr_binding;
|
||||
|
||||
CHECK_XR(xrCreateSession(m_context->getInstance(), &create_info, &m_oxr->session),
|
||||
"Failed to create VR session. The OpenXR runtime may have additional requirements for "
|
||||
"the graphics driver that are not met. Other causes are possible too however.\nTip: "
|
||||
"The --debug-xr command line option for Blender might allow the runtime to output "
|
||||
"detailed error information to the command line.");
|
||||
|
||||
prepareDrawing();
|
||||
create_reference_space(m_oxr.get(), &begin_info->base_pose);
|
||||
}
|
||||
|
||||
void GHOST_XrSession::requestEnd()
|
||||
{
|
||||
xrRequestExitSession(m_oxr->session);
|
||||
}
|
||||
|
||||
void GHOST_XrSession::end()
|
||||
{
|
||||
assert(m_oxr->session != XR_NULL_HANDLE);
|
||||
|
||||
CHECK_XR(xrEndSession(m_oxr->session), "Failed to cleanly end the VR session.");
|
||||
unbindGraphicsContext();
|
||||
m_draw_info = nullptr;
|
||||
}
|
||||
|
||||
GHOST_XrSession::LifeExpectancy GHOST_XrSession::handleStateChangeEvent(
|
||||
const XrEventDataSessionStateChanged *lifecycle)
|
||||
{
|
||||
m_oxr->session_state = lifecycle->state;
|
||||
|
||||
/* Runtime may send events for apparently destroyed session. Our handle should be NULL then. */
|
||||
assert((m_oxr->session == XR_NULL_HANDLE) || (m_oxr->session == lifecycle->session));
|
||||
|
||||
switch (lifecycle->state) {
|
||||
case XR_SESSION_STATE_READY: {
|
||||
XrSessionBeginInfo begin_info = {XR_TYPE_SESSION_BEGIN_INFO};
|
||||
|
||||
begin_info.primaryViewConfigurationType = m_oxr->view_type;
|
||||
CHECK_XR(xrBeginSession(m_oxr->session, &begin_info),
|
||||
"Failed to cleanly begin the VR session.");
|
||||
break;
|
||||
}
|
||||
case XR_SESSION_STATE_STOPPING:
|
||||
/* Runtime will change state to STATE_EXITING, don't destruct session yet. */
|
||||
end();
|
||||
break;
|
||||
case XR_SESSION_STATE_EXITING:
|
||||
case XR_SESSION_STATE_LOSS_PENDING:
|
||||
return SESSION_DESTROY;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SESSION_KEEP_ALIVE;
|
||||
}
|
||||
/** \} */ /* State Management */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Drawing
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void GHOST_XrSession::prepareDrawing()
|
||||
{
|
||||
std::vector<XrViewConfigurationView> view_configs;
|
||||
uint32_t view_count;
|
||||
|
||||
CHECK_XR(
|
||||
xrEnumerateViewConfigurationViews(
|
||||
m_context->getInstance(), m_oxr->system_id, m_oxr->view_type, 0, &view_count, nullptr),
|
||||
"Failed to get count of view configurations.");
|
||||
view_configs.resize(view_count, {XR_TYPE_VIEW_CONFIGURATION_VIEW});
|
||||
CHECK_XR(xrEnumerateViewConfigurationViews(m_context->getInstance(),
|
||||
m_oxr->system_id,
|
||||
m_oxr->view_type,
|
||||
view_configs.size(),
|
||||
&view_count,
|
||||
view_configs.data()),
|
||||
"Failed to get count of view configurations.");
|
||||
|
||||
for (const XrViewConfigurationView &view_config : view_configs) {
|
||||
m_oxr->swapchains.push_back(std::unique_ptr<GHOST_XrSwapchain>(
|
||||
new GHOST_XrSwapchain(*m_gpu_binding, m_oxr->session, view_config)));
|
||||
}
|
||||
|
||||
m_oxr->views.resize(view_count, {XR_TYPE_VIEW});
|
||||
|
||||
m_draw_info = std::unique_ptr<GHOST_XrDrawInfo>(new GHOST_XrDrawInfo());
|
||||
}
|
||||
|
||||
void GHOST_XrSession::beginFrameDrawing()
|
||||
{
|
||||
XrFrameWaitInfo wait_info = {XR_TYPE_FRAME_WAIT_INFO};
|
||||
XrFrameBeginInfo begin_info = {XR_TYPE_FRAME_BEGIN_INFO};
|
||||
XrFrameState frame_state = {XR_TYPE_FRAME_STATE};
|
||||
|
||||
/* TODO Blocking call. Drawing should run on a separate thread to avoid interferences. */
|
||||
CHECK_XR(xrWaitFrame(m_oxr->session, &wait_info, &frame_state),
|
||||
"Failed to synchronize frame rates between Blender and the device.");
|
||||
|
||||
CHECK_XR(xrBeginFrame(m_oxr->session, &begin_info),
|
||||
"Failed to submit frame rendering start state.");
|
||||
|
||||
m_draw_info->frame_state = frame_state;
|
||||
|
||||
if (m_context->isDebugTimeMode()) {
|
||||
m_draw_info->frame_begin_time = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
static void print_debug_timings(GHOST_XrDrawInfo *draw_info)
|
||||
{
|
||||
/** Render time of last 8 frames (in ms) to calculate an average. */
|
||||
std::chrono::duration<double, std::milli> duration = std::chrono::high_resolution_clock::now() -
|
||||
draw_info->frame_begin_time;
|
||||
const double duration_ms = duration.count();
|
||||
const int avg_frame_count = 8;
|
||||
double avg_ms_tot = 0.0;
|
||||
|
||||
if (draw_info->last_frame_times.size() >= avg_frame_count) {
|
||||
draw_info->last_frame_times.pop_front();
|
||||
assert(draw_info->last_frame_times.size() == avg_frame_count - 1);
|
||||
}
|
||||
draw_info->last_frame_times.push_back(duration_ms);
|
||||
for (double ms_iter : draw_info->last_frame_times) {
|
||||
avg_ms_tot += ms_iter;
|
||||
}
|
||||
|
||||
printf("VR frame render time: %.0fms - %.2f FPS (%.2f FPS 8 frames average)\n",
|
||||
duration_ms,
|
||||
1000.0 / duration_ms,
|
||||
1000.0 / (avg_ms_tot / draw_info->last_frame_times.size()));
|
||||
}
|
||||
|
||||
void GHOST_XrSession::endFrameDrawing(std::vector<XrCompositionLayerBaseHeader *> *layers)
|
||||
{
|
||||
XrFrameEndInfo end_info = {XR_TYPE_FRAME_END_INFO};
|
||||
|
||||
end_info.displayTime = m_draw_info->frame_state.predictedDisplayTime;
|
||||
end_info.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
|
||||
end_info.layerCount = layers->size();
|
||||
end_info.layers = layers->data();
|
||||
|
||||
CHECK_XR(xrEndFrame(m_oxr->session, &end_info), "Failed to submit rendered frame.");
|
||||
|
||||
if (m_context->isDebugTimeMode()) {
|
||||
print_debug_timings(m_draw_info.get());
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_XrSession::draw(void *draw_customdata)
|
||||
{
|
||||
std::vector<XrCompositionLayerProjectionView>
|
||||
projection_layer_views; /* Keep alive until xrEndFrame() call! */
|
||||
XrCompositionLayerProjection proj_layer;
|
||||
std::vector<XrCompositionLayerBaseHeader *> layers;
|
||||
|
||||
beginFrameDrawing();
|
||||
|
||||
if (m_draw_info->frame_state.shouldRender) {
|
||||
proj_layer = drawLayer(projection_layer_views, draw_customdata);
|
||||
layers.push_back(reinterpret_cast<XrCompositionLayerBaseHeader *>(&proj_layer));
|
||||
}
|
||||
|
||||
endFrameDrawing(&layers);
|
||||
}
|
||||
|
||||
static void ghost_xr_draw_view_info_from_view(const XrView &view, GHOST_XrDrawViewInfo &r_info)
|
||||
{
|
||||
/* Set and convert to Blender coodinate space. */
|
||||
r_info.pose.position[0] = view.pose.position.x;
|
||||
r_info.pose.position[1] = view.pose.position.y;
|
||||
r_info.pose.position[2] = view.pose.position.z;
|
||||
r_info.pose.orientation_quat[0] = view.pose.orientation.w;
|
||||
r_info.pose.orientation_quat[1] = view.pose.orientation.x;
|
||||
r_info.pose.orientation_quat[2] = view.pose.orientation.y;
|
||||
r_info.pose.orientation_quat[3] = view.pose.orientation.z;
|
||||
|
||||
r_info.fov.angle_left = view.fov.angleLeft;
|
||||
r_info.fov.angle_right = view.fov.angleRight;
|
||||
r_info.fov.angle_up = view.fov.angleUp;
|
||||
r_info.fov.angle_down = view.fov.angleDown;
|
||||
}
|
||||
|
||||
static bool ghost_xr_draw_view_expects_srgb_buffer(const GHOST_XrContext *context)
|
||||
{
|
||||
/* Monado seems to be faulty and doesn't do OETF transform correctly. So expect a SRGB buffer to
|
||||
* compensate. You get way too dark rendering without this, it's pretty obvious (even in the
|
||||
* default startup scene). */
|
||||
return (context->getOpenXRRuntimeID() == OPENXR_RUNTIME_MONADO);
|
||||
}
|
||||
|
||||
void GHOST_XrSession::drawView(GHOST_XrSwapchain &swapchain,
|
||||
XrCompositionLayerProjectionView &r_proj_layer_view,
|
||||
XrView &view,
|
||||
void *draw_customdata)
|
||||
{
|
||||
XrSwapchainImageBaseHeader *swapchain_image = swapchain.acquireDrawableSwapchainImage();
|
||||
GHOST_XrDrawViewInfo draw_view_info = {};
|
||||
|
||||
r_proj_layer_view.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
|
||||
r_proj_layer_view.pose = view.pose;
|
||||
r_proj_layer_view.fov = view.fov;
|
||||
swapchain.updateCompositionLayerProjectViewSubImage(r_proj_layer_view.subImage);
|
||||
|
||||
draw_view_info.expects_srgb_buffer = ghost_xr_draw_view_expects_srgb_buffer(m_context);
|
||||
draw_view_info.ofsx = r_proj_layer_view.subImage.imageRect.offset.x;
|
||||
draw_view_info.ofsy = r_proj_layer_view.subImage.imageRect.offset.y;
|
||||
draw_view_info.width = r_proj_layer_view.subImage.imageRect.extent.width;
|
||||
draw_view_info.height = r_proj_layer_view.subImage.imageRect.extent.height;
|
||||
ghost_xr_draw_view_info_from_view(view, draw_view_info);
|
||||
|
||||
/* Draw! */
|
||||
m_context->getCustomFuncs().draw_view_fn(&draw_view_info, draw_customdata);
|
||||
m_gpu_binding->submitToSwapchainImage(swapchain_image, &draw_view_info);
|
||||
|
||||
swapchain.releaseImage();
|
||||
}
|
||||
|
||||
XrCompositionLayerProjection GHOST_XrSession::drawLayer(
|
||||
std::vector<XrCompositionLayerProjectionView> &r_proj_layer_views, void *draw_customdata)
|
||||
{
|
||||
XrViewLocateInfo viewloc_info = {XR_TYPE_VIEW_LOCATE_INFO};
|
||||
XrViewState view_state = {XR_TYPE_VIEW_STATE};
|
||||
XrCompositionLayerProjection layer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION};
|
||||
uint32_t view_count;
|
||||
|
||||
viewloc_info.viewConfigurationType = m_oxr->view_type;
|
||||
viewloc_info.displayTime = m_draw_info->frame_state.predictedDisplayTime;
|
||||
viewloc_info.space = m_oxr->reference_space;
|
||||
|
||||
CHECK_XR(xrLocateViews(m_oxr->session,
|
||||
&viewloc_info,
|
||||
&view_state,
|
||||
m_oxr->views.size(),
|
||||
&view_count,
|
||||
m_oxr->views.data()),
|
||||
"Failed to query frame view and projection state.");
|
||||
assert(m_oxr->swapchains.size() == view_count);
|
||||
|
||||
r_proj_layer_views.resize(view_count);
|
||||
|
||||
for (uint32_t view_idx = 0; view_idx < view_count; view_idx++) {
|
||||
drawView(*m_oxr->swapchains[view_idx],
|
||||
r_proj_layer_views[view_idx],
|
||||
m_oxr->views[view_idx],
|
||||
draw_customdata);
|
||||
}
|
||||
|
||||
layer.space = m_oxr->reference_space;
|
||||
layer.viewCount = r_proj_layer_views.size();
|
||||
layer.views = r_proj_layer_views.data();
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
/** \} */ /* Drawing */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name State Queries
|
||||
*
|
||||
* \{ */
|
||||
|
||||
bool GHOST_XrSession::isRunning() const
|
||||
{
|
||||
if (m_oxr->session == XR_NULL_HANDLE) {
|
||||
return false;
|
||||
}
|
||||
switch (m_oxr->session_state) {
|
||||
case XR_SESSION_STATE_READY:
|
||||
case XR_SESSION_STATE_SYNCHRONIZED:
|
||||
case XR_SESSION_STATE_VISIBLE:
|
||||
case XR_SESSION_STATE_FOCUSED:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */ /* State Queries */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Graphics Context Injection
|
||||
*
|
||||
* Sessions need access to Ghost graphics context information. Additionally, this API allows
|
||||
* creating contexts on the fly (created on start, destructed on end). For this, callbacks to bind
|
||||
* (potentially create) and unbind (potentially destruct) a Ghost graphics context have to be set,
|
||||
* which will be called on session start and end respectively.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void GHOST_XrSession::bindGraphicsContext()
|
||||
{
|
||||
const GHOST_XrCustomFuncs &custom_funcs = m_context->getCustomFuncs();
|
||||
assert(custom_funcs.gpu_ctx_bind_fn);
|
||||
m_gpu_ctx = static_cast<GHOST_Context *>(
|
||||
custom_funcs.gpu_ctx_bind_fn(m_context->getGraphicsBindingType()));
|
||||
}
|
||||
|
||||
void GHOST_XrSession::unbindGraphicsContext()
|
||||
{
|
||||
const GHOST_XrCustomFuncs &custom_funcs = m_context->getCustomFuncs();
|
||||
if (custom_funcs.gpu_ctx_unbind_fn) {
|
||||
custom_funcs.gpu_ctx_unbind_fn(m_context->getGraphicsBindingType(), m_gpu_ctx);
|
||||
}
|
||||
m_gpu_ctx = nullptr;
|
||||
}
|
||||
|
||||
/** \} */ /* Graphics Context Injection */
|
83
intern/ghost/intern/GHOST_XrSession.h
Normal file
83
intern/ghost/intern/GHOST_XrSession.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#ifndef __GHOST_XRSESSION_H__
|
||||
#define __GHOST_XRSESSION_H__
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
class GHOST_XrContext;
|
||||
struct OpenXRSessionData;
|
||||
struct GHOST_XrDrawInfo;
|
||||
struct GHOST_XrSwapchain;
|
||||
|
||||
class GHOST_XrSession {
|
||||
public:
|
||||
enum LifeExpectancy {
|
||||
SESSION_KEEP_ALIVE,
|
||||
SESSION_DESTROY,
|
||||
};
|
||||
|
||||
GHOST_XrSession(GHOST_XrContext *xr_context);
|
||||
~GHOST_XrSession();
|
||||
|
||||
void start(const GHOST_XrSessionBeginInfo *begin_info);
|
||||
void requestEnd();
|
||||
|
||||
LifeExpectancy handleStateChangeEvent(const XrEventDataSessionStateChanged *lifecycle);
|
||||
|
||||
bool isRunning() const;
|
||||
|
||||
void unbindGraphicsContext(); /* Public so context can ensure it's unbound as needed. */
|
||||
|
||||
void draw(void *draw_customdata);
|
||||
|
||||
private:
|
||||
/** Pointer back to context managing this session. Would be nice to avoid, but needed to access
|
||||
* custom callbacks set before session start. */
|
||||
class GHOST_XrContext *m_context;
|
||||
|
||||
std::unique_ptr<OpenXRSessionData> m_oxr; /* Could use stack, but PImpl is preferable. */
|
||||
|
||||
/** Active Ghost graphic context. Owned by Blender, not GHOST. */
|
||||
class GHOST_Context *m_gpu_ctx = nullptr;
|
||||
std::unique_ptr<class GHOST_IXrGraphicsBinding> m_gpu_binding;
|
||||
|
||||
/** Rendering information. Set when drawing starts. */
|
||||
std::unique_ptr<GHOST_XrDrawInfo> m_draw_info;
|
||||
|
||||
void initSystem();
|
||||
void end();
|
||||
|
||||
void bindGraphicsContext();
|
||||
|
||||
void prepareDrawing();
|
||||
XrCompositionLayerProjection drawLayer(
|
||||
std::vector<XrCompositionLayerProjectionView> &r_proj_layer_views, void *draw_customdata);
|
||||
void drawView(GHOST_XrSwapchain &swapchain,
|
||||
XrCompositionLayerProjectionView &r_proj_layer_view,
|
||||
XrView &view,
|
||||
void *draw_customdata);
|
||||
void beginFrameDrawing();
|
||||
void endFrameDrawing(std::vector<XrCompositionLayerBaseHeader *> *layers);
|
||||
};
|
||||
|
||||
#endif /* GHOST_XRSESSION_H__ */
|
131
intern/ghost/intern/GHOST_XrSwapchain.cpp
Normal file
131
intern/ghost/intern/GHOST_XrSwapchain.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "GHOST_C-api.h"
|
||||
|
||||
#include "GHOST_IXrGraphicsBinding.h"
|
||||
#include "GHOST_Xr_intern.h"
|
||||
#include "GHOST_XrException.h"
|
||||
#include "GHOST_XrSession.h"
|
||||
|
||||
#include "GHOST_XrSwapchain.h"
|
||||
|
||||
struct OpenXRSwapchainData {
|
||||
using ImageVec = std::vector<XrSwapchainImageBaseHeader *>;
|
||||
|
||||
XrSwapchain swapchain = XR_NULL_HANDLE;
|
||||
ImageVec swapchain_images;
|
||||
};
|
||||
|
||||
static OpenXRSwapchainData::ImageVec swapchain_images_create(XrSwapchain swapchain,
|
||||
GHOST_IXrGraphicsBinding &gpu_binding)
|
||||
{
|
||||
std::vector<XrSwapchainImageBaseHeader *> images;
|
||||
uint32_t image_count;
|
||||
|
||||
CHECK_XR(xrEnumerateSwapchainImages(swapchain, 0, &image_count, nullptr),
|
||||
"Failed to get count of swapchain images to create for the VR session.");
|
||||
images = gpu_binding.createSwapchainImages(image_count);
|
||||
CHECK_XR(xrEnumerateSwapchainImages(swapchain, images.size(), &image_count, images[0]),
|
||||
"Failed to create swapchain images for the VR session.");
|
||||
|
||||
return images;
|
||||
}
|
||||
|
||||
GHOST_XrSwapchain::GHOST_XrSwapchain(GHOST_IXrGraphicsBinding &gpu_binding,
|
||||
const XrSession &session,
|
||||
const XrViewConfigurationView &view_config)
|
||||
: m_oxr(new OpenXRSwapchainData())
|
||||
{
|
||||
XrSwapchainCreateInfo create_info = {XR_TYPE_SWAPCHAIN_CREATE_INFO};
|
||||
uint32_t format_count = 0;
|
||||
int64_t chosen_format;
|
||||
|
||||
CHECK_XR(xrEnumerateSwapchainFormats(session, 0, &format_count, nullptr),
|
||||
"Failed to get count of swapchain image formats.");
|
||||
std::vector<int64_t> swapchain_formats(format_count);
|
||||
CHECK_XR(xrEnumerateSwapchainFormats(
|
||||
session, swapchain_formats.size(), &format_count, swapchain_formats.data()),
|
||||
"Failed to get swapchain image formats.");
|
||||
assert(swapchain_formats.size() == format_count);
|
||||
|
||||
if (!gpu_binding.chooseSwapchainFormat(swapchain_formats, &chosen_format)) {
|
||||
throw GHOST_XrException(
|
||||
"Error: No format matching OpenXR runtime supported swapchain formats found.");
|
||||
}
|
||||
|
||||
create_info.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT |
|
||||
XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
create_info.format = chosen_format;
|
||||
create_info.sampleCount = view_config.recommendedSwapchainSampleCount;
|
||||
create_info.width = view_config.recommendedImageRectWidth;
|
||||
create_info.height = view_config.recommendedImageRectHeight;
|
||||
create_info.faceCount = 1;
|
||||
create_info.arraySize = 1;
|
||||
create_info.mipCount = 1;
|
||||
|
||||
CHECK_XR(xrCreateSwapchain(session, &create_info, &m_oxr->swapchain),
|
||||
"Failed to create OpenXR swapchain.");
|
||||
|
||||
m_image_width = create_info.width;
|
||||
m_image_height = create_info.height;
|
||||
|
||||
m_oxr->swapchain_images = swapchain_images_create(m_oxr->swapchain, gpu_binding);
|
||||
}
|
||||
|
||||
GHOST_XrSwapchain::~GHOST_XrSwapchain()
|
||||
{
|
||||
if (m_oxr->swapchain != XR_NULL_HANDLE) {
|
||||
CHECK_XR_ASSERT(xrDestroySwapchain(m_oxr->swapchain));
|
||||
}
|
||||
}
|
||||
|
||||
XrSwapchainImageBaseHeader *GHOST_XrSwapchain::acquireDrawableSwapchainImage()
|
||||
|
||||
{
|
||||
XrSwapchainImageAcquireInfo acquire_info = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO};
|
||||
XrSwapchainImageWaitInfo wait_info = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO};
|
||||
uint32_t image_idx;
|
||||
|
||||
CHECK_XR(xrAcquireSwapchainImage(m_oxr->swapchain, &acquire_info, &image_idx),
|
||||
"Failed to acquire swapchain image for the VR session.");
|
||||
wait_info.timeout = XR_INFINITE_DURATION;
|
||||
CHECK_XR(xrWaitSwapchainImage(m_oxr->swapchain, &wait_info),
|
||||
"Failed to acquire swapchain image for the VR session.");
|
||||
|
||||
return m_oxr->swapchain_images[image_idx];
|
||||
}
|
||||
|
||||
void GHOST_XrSwapchain::updateCompositionLayerProjectViewSubImage(XrSwapchainSubImage &r_sub_image)
|
||||
{
|
||||
r_sub_image.swapchain = m_oxr->swapchain;
|
||||
r_sub_image.imageRect.offset = {0, 0};
|
||||
r_sub_image.imageRect.extent = {m_image_width, m_image_height};
|
||||
}
|
||||
|
||||
void GHOST_XrSwapchain::releaseImage()
|
||||
{
|
||||
XrSwapchainImageReleaseInfo release_info = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO};
|
||||
|
||||
CHECK_XR(xrReleaseSwapchainImage(m_oxr->swapchain, &release_info),
|
||||
"Failed to release swapchain image used to submit VR session frame.");
|
||||
}
|
45
intern/ghost/intern/GHOST_XrSwapchain.h
Normal file
45
intern/ghost/intern/GHOST_XrSwapchain.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#ifndef __GHOST_XRSWAPCHAIN_H__
|
||||
#define __GHOST_XRSWAPCHAIN_H__
|
||||
|
||||
#include <memory>
|
||||
|
||||
struct OpenXRSwapchainData;
|
||||
|
||||
class GHOST_XrSwapchain {
|
||||
public:
|
||||
GHOST_XrSwapchain(GHOST_IXrGraphicsBinding &gpu_binding,
|
||||
const XrSession &session,
|
||||
const XrViewConfigurationView &view_config);
|
||||
~GHOST_XrSwapchain();
|
||||
|
||||
XrSwapchainImageBaseHeader *acquireDrawableSwapchainImage();
|
||||
void releaseImage();
|
||||
|
||||
void updateCompositionLayerProjectViewSubImage(XrSwapchainSubImage &r_sub_image);
|
||||
|
||||
private:
|
||||
std::unique_ptr<OpenXRSwapchainData> m_oxr; /* Could use stack, but PImpl is preferable. */
|
||||
int32_t m_image_width, m_image_height;
|
||||
};
|
||||
|
||||
#endif // GHOST_XRSWAPCHAIN_H
|
50
intern/ghost/intern/GHOST_Xr_intern.h
Normal file
50
intern/ghost/intern/GHOST_Xr_intern.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#ifndef __GHOST_XR_INTERN_H__
|
||||
#define __GHOST_XR_INTERN_H__
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GHOST_Xr_openxr_includes.h"
|
||||
|
||||
#define CHECK_XR(call, error_msg) \
|
||||
{ \
|
||||
XrResult _res = call; \
|
||||
if (XR_FAILED(_res)) { \
|
||||
throw GHOST_XrException(error_msg, _res); \
|
||||
} \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
/**
|
||||
* Variation of CHECK_XR() that doesn't throw, but asserts for success. Especially useful for
|
||||
* destructors, which shouldn't throw.
|
||||
*/
|
||||
#define CHECK_XR_ASSERT(call) \
|
||||
{ \
|
||||
XrResult _res = call; \
|
||||
assert(_res == XR_SUCCESS); \
|
||||
(void)_res; \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
#endif /* __GHOST_XR_INTERN_H__ */
|
52
intern/ghost/intern/GHOST_Xr_openxr_includes.h
Normal file
52
intern/ghost/intern/GHOST_Xr_openxr_includes.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*
|
||||
* \note This is taken mostly from the OpenXR SDK, but with modified D3D versions (e.g. d3d11_4.h
|
||||
* -> d3d11.h). Take care for that when updating, we don't want to require newest Win SDKs to be
|
||||
* installed.
|
||||
*/
|
||||
|
||||
#ifndef __GHOST_XR_SYSTEM_INCLUDES_H__
|
||||
#define __GHOST_XR_SYSTEM_INCLUDES_H__
|
||||
|
||||
/* Platform headers */
|
||||
#ifdef XR_USE_PLATFORM_WIN32
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# define NOMINMAX
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
/* Graphics headers */
|
||||
#ifdef XR_USE_GRAPHICS_API_D3D10
|
||||
# include <d3d10_1.h>
|
||||
#endif
|
||||
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||
# include <d3d11.h>
|
||||
#endif
|
||||
#ifdef XR_USE_GRAPHICS_API_D3D12
|
||||
# include <d3d12.h>
|
||||
#endif
|
||||
#ifdef WITH_X11
|
||||
# include <GL/glxew.h>
|
||||
#endif
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
#include <openxr/openxr_platform.h>
|
||||
|
||||
#endif /* __GHOST_XR_SYSTEM_INCLUDES_H__ */
|
14
release/windows/batch/blender_oculus.cmd
Normal file
14
release/windows/batch/blender_oculus.cmd
Normal file
@@ -0,0 +1,14 @@
|
||||
@echo off
|
||||
|
||||
REM Helper setting hints to get the OpenXR preview support enabled for Oculus.
|
||||
REM Of course this is not meant as a permanent solution. Oculus will likely provide a better setup at some point.
|
||||
|
||||
echo Starting Blender with Oculus OpenXR support. This assumes the Oculus runtime
|
||||
echo is installed in the default location. If this is not the case, please adjust
|
||||
echo the path inside oculus.json.
|
||||
echo.
|
||||
echo Note that OpenXR support in Oculus is considered a preview. Use with care!
|
||||
echo.
|
||||
pause
|
||||
set XR_RUNTIME_JSON=%~dp0oculus.json
|
||||
blender
|
9
release/windows/batch/oculus.json
Normal file
9
release/windows/batch/oculus.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"file_format_version": "1.0.0",
|
||||
"runtime":
|
||||
{
|
||||
"api_version": "1.0",
|
||||
"name": "Oculus OpenXR",
|
||||
"library_path": "c:\\Program Files\\Oculus\\Support\\oculus-runtime\\LibOVRRT64_1.dll"
|
||||
}
|
||||
}
|
@@ -90,6 +90,7 @@ set(SRC_DNA_INC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_windowmanager_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_workspace_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_world_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_xr_types.h
|
||||
)
|
||||
|
||||
add_subdirectory(datatoc)
|
||||
|
@@ -153,6 +153,8 @@ enum {
|
||||
G_DEBUG_IO = (1 << 17), /* IO Debugging (for Collada, ...)*/
|
||||
G_DEBUG_GPU_SHADERS = (1 << 18), /* GLSL shaders */
|
||||
G_DEBUG_GPU_FORCE_WORKAROUNDS = (1 << 19), /* force gpu workarounds bypassing detections. */
|
||||
G_DEBUG_XR = (1 << 20), /* XR/OpenXR messages */
|
||||
G_DEBUG_XR_TIME = (1 << 21), /* XR/OpenXR timing messages */
|
||||
|
||||
G_DEBUG_GHOST = (1 << 20), /* Debug GHOST module. */
|
||||
};
|
||||
|
@@ -667,6 +667,10 @@ if(WITH_TBB)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_XR_OPENXR)
|
||||
add_definitions(-DWITH_XR_OPENXR)
|
||||
endif()
|
||||
|
||||
# # Warnings as errors, this is too strict!
|
||||
# if(MSVC)
|
||||
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX")
|
||||
|
@@ -636,6 +636,13 @@ void perspective_m4(float mat[4][4],
|
||||
const float top,
|
||||
const float nearClip,
|
||||
const float farClip);
|
||||
void perspective_m4_fov(float mat[4][4],
|
||||
const float angle_left,
|
||||
const float angle_right,
|
||||
const float angle_up,
|
||||
const float angle_down,
|
||||
const float nearClip,
|
||||
const float farClip);
|
||||
void orthographic_m4(float mat[4][4],
|
||||
const float left,
|
||||
const float right,
|
||||
|
@@ -4709,6 +4709,25 @@ void perspective_m4(float mat[4][4],
|
||||
mat[3][3] = 0.0f;
|
||||
}
|
||||
|
||||
void perspective_m4_fov(float mat[4][4],
|
||||
const float angle_left,
|
||||
const float angle_right,
|
||||
const float angle_up,
|
||||
const float angle_down,
|
||||
const float nearClip,
|
||||
const float farClip)
|
||||
{
|
||||
const float tan_angle_left = tanf(angle_left);
|
||||
const float tan_angle_right = tanf(angle_right);
|
||||
const float tan_angle_bottom = tanf(angle_up);
|
||||
const float tan_angle_top = tanf(angle_down);
|
||||
|
||||
perspective_m4(
|
||||
mat, tan_angle_left, tan_angle_right, tan_angle_top, tan_angle_bottom, nearClip, farClip);
|
||||
mat[0][0] /= nearClip;
|
||||
mat[1][1] /= nearClip;
|
||||
}
|
||||
|
||||
/* translate a matrix created by orthographic_m4 or perspective_m4 in XY coords
|
||||
* (used to jitter the view) */
|
||||
void window_translate_m4(float winmat[4][4], float perspmat[4][4], const float x, const float y)
|
||||
|
@@ -92,6 +92,10 @@ if(WITH_ALEMBIC)
|
||||
add_definitions(-DWITH_ALEMBIC)
|
||||
endif()
|
||||
|
||||
if(WITH_XR_OPENXR)
|
||||
add_definitions(-DWITH_XR_OPENXR)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_blenloader "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
# needed so writefile.c can use dna_type_offsets.h
|
||||
|
@@ -7723,6 +7723,7 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm)
|
||||
wm->undo_stack = NULL;
|
||||
|
||||
wm->message_bus = NULL;
|
||||
wm->xr.context = NULL;
|
||||
|
||||
BLI_listbase_clear(&wm->jobs);
|
||||
BLI_listbase_clear(&wm->drags);
|
||||
|
@@ -30,6 +30,8 @@
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_defaults.h"
|
||||
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_camera_types.h"
|
||||
@@ -4845,5 +4847,17 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!DNA_struct_find(fd->filesdna, "XrSessionSettings")) {
|
||||
for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
|
||||
const View3D *v3d_default = DNA_struct_default_get(View3D);
|
||||
|
||||
wm->xr.session_settings.shading_type = OB_SOLID;
|
||||
wm->xr.session_settings.draw_flags = (V3D_OFSDRAW_SHOW_GRIDFLOOR |
|
||||
V3D_OFSDRAW_SHOW_ANNOTATION);
|
||||
wm->xr.session_settings.clip_start = v3d_default->clip_start;
|
||||
wm->xr.session_settings.clip_end = v3d_default->clip_end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -390,4 +390,8 @@ if(WITH_FREESTYLE)
|
||||
add_definitions(-DWITH_FREESTYLE)
|
||||
endif()
|
||||
|
||||
if(WITH_XR_OPENXR)
|
||||
add_definitions(-DWITH_XR_OPENXR)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_draw "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
@@ -139,6 +139,11 @@ void DRW_opengl_context_destroy(void);
|
||||
void DRW_opengl_context_enable(void);
|
||||
void DRW_opengl_context_disable(void);
|
||||
|
||||
/* Not nice to expose these. Code to render offscreen viewports can save expensive context switches
|
||||
* by using this directly however. */
|
||||
void *DRW_xr_opengl_context_get(void);
|
||||
void *DRW_xr_gpu_context_get(void);
|
||||
|
||||
/* For garbage collection */
|
||||
void DRW_cache_free_old_batches(struct Main *bmain);
|
||||
|
||||
|
@@ -2794,6 +2794,30 @@ void DRW_opengl_context_disable(void)
|
||||
DRW_opengl_context_disable_ex(true);
|
||||
}
|
||||
|
||||
#ifdef WITH_XR_OPENXR
|
||||
|
||||
/* XXX
|
||||
* There should really be no such getter, but for VR we currently can't easily avoid it. OpenXR
|
||||
* needs some low level info for the OpenGL context that will be used for submitting the
|
||||
* final framebuffer. VR could in theory create its own context, but that would mean we have to
|
||||
* switch to it just to submit the final frame, which has notable performance impact.
|
||||
*
|
||||
* We could "inject" a context through DRW_opengl_render_context_enable(), but that would have to
|
||||
* work from the main thread, which is tricky to get working too. The preferable solution would be
|
||||
* using a separate thread for VR drawing where a single context can stay active. */
|
||||
void *DRW_xr_opengl_context_get(void)
|
||||
{
|
||||
return DST.gl_context;
|
||||
}
|
||||
|
||||
/* XXX See comment on DRW_xr_opengl_context_get(). */
|
||||
void *DRW_xr_gpu_context_get(void)
|
||||
{
|
||||
return DST.gpu_context;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void DRW_opengl_render_context_enable(void *re_gl_context)
|
||||
{
|
||||
/* If thread is main you should use DRW_opengl_context_enable(). */
|
||||
|
@@ -57,6 +57,23 @@ void ED_view3d_draw_offscreen(struct Depsgraph *depsgraph,
|
||||
const bool do_color_management,
|
||||
struct GPUOffScreen *ofs,
|
||||
struct GPUViewport *viewport);
|
||||
void ED_view3d_draw_offscreen_simple(struct Depsgraph *depsgraph,
|
||||
struct Scene *scene,
|
||||
struct View3DShading *shading_override,
|
||||
int drawtype,
|
||||
int winx,
|
||||
int winy,
|
||||
unsigned int draw_flags,
|
||||
float viewmat[4][4],
|
||||
float winmat[4][4],
|
||||
float clip_start,
|
||||
float clip_end,
|
||||
bool do_sky,
|
||||
bool is_persp,
|
||||
const char *viewname,
|
||||
const bool do_color_management,
|
||||
struct GPUOffScreen *ofs,
|
||||
struct GPUViewport *viewport);
|
||||
|
||||
struct ImBuf *ED_view3d_draw_offscreen_imbuf(struct Depsgraph *depsgraph,
|
||||
struct Scene *scene,
|
||||
|
@@ -1706,6 +1706,89 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
|
||||
G.f &= ~G_FLAG_RENDER_VIEWPORT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates own fake 3d views (wrapping #ED_view3d_draw_offscreen). Similar too
|
||||
* #ED_view_draw_offscreen_imbuf_simple, but takes view/projection matrices as arguments.
|
||||
*/
|
||||
void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph,
|
||||
Scene *scene,
|
||||
View3DShading *shading_override,
|
||||
int drawtype,
|
||||
int winx,
|
||||
int winy,
|
||||
uint draw_flags,
|
||||
float viewmat[4][4],
|
||||
float winmat[4][4],
|
||||
float clip_start,
|
||||
float clip_end,
|
||||
bool do_sky,
|
||||
bool is_persp,
|
||||
const char *viewname,
|
||||
const bool do_color_management,
|
||||
GPUOffScreen *ofs,
|
||||
GPUViewport *viewport)
|
||||
{
|
||||
View3D v3d = {NULL};
|
||||
ARegion ar = {NULL};
|
||||
RegionView3D rv3d = {{{0}}};
|
||||
|
||||
v3d.regionbase.first = v3d.regionbase.last = &ar;
|
||||
ar.regiondata = &rv3d;
|
||||
ar.regiontype = RGN_TYPE_WINDOW;
|
||||
|
||||
View3DShading *source_shading_settings = &scene->display.shading;
|
||||
if (draw_flags & V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS && shading_override != NULL) {
|
||||
source_shading_settings = shading_override;
|
||||
}
|
||||
memcpy(&v3d.shading, source_shading_settings, sizeof(View3DShading));
|
||||
v3d.shading.type = drawtype;
|
||||
|
||||
if (drawtype == OB_MATERIAL) {
|
||||
v3d.shading.flag = V3D_SHADING_SCENE_WORLD | V3D_SHADING_SCENE_LIGHTS;
|
||||
}
|
||||
|
||||
if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) {
|
||||
v3d.flag2 |= V3D_SHOW_ANNOTATION;
|
||||
}
|
||||
if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) {
|
||||
v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y;
|
||||
v3d.grid = 1.0f;
|
||||
v3d.gridlines = 16;
|
||||
v3d.gridsubdiv = 10;
|
||||
|
||||
/* Show grid, disable other overlays (set all available _HIDE_ flags). */
|
||||
v3d.overlay.flag |= V3D_OVERLAY_HIDE_CURSOR | V3D_OVERLAY_HIDE_TEXT |
|
||||
V3D_OVERLAY_HIDE_MOTION_PATHS | V3D_OVERLAY_HIDE_BONES |
|
||||
V3D_OVERLAY_HIDE_OBJECT_XTRAS | V3D_OVERLAY_HIDE_OBJECT_ORIGINS;
|
||||
v3d.flag |= V3D_HIDE_HELPLINES;
|
||||
}
|
||||
else {
|
||||
v3d.flag2 = V3D_HIDE_OVERLAYS;
|
||||
}
|
||||
|
||||
rv3d.persp = RV3D_PERSP;
|
||||
v3d.clip_start = clip_start;
|
||||
v3d.clip_end = clip_end;
|
||||
/* Actually not used since we pass in the projection matrix. */
|
||||
v3d.lens = 0;
|
||||
|
||||
ED_view3d_draw_offscreen(depsgraph,
|
||||
scene,
|
||||
drawtype,
|
||||
&v3d,
|
||||
&ar,
|
||||
winx,
|
||||
winy,
|
||||
viewmat,
|
||||
winmat,
|
||||
do_sky,
|
||||
is_persp,
|
||||
viewname,
|
||||
do_color_management,
|
||||
ofs,
|
||||
viewport);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility func for ED_view3d_draw_offscreen
|
||||
*
|
||||
@@ -1902,6 +1985,9 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph,
|
||||
if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) {
|
||||
v3d.flag2 |= V3D_SHOW_ANNOTATION;
|
||||
}
|
||||
if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) {
|
||||
v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y;
|
||||
}
|
||||
|
||||
v3d.shading.background_type = V3D_SHADING_BACKGROUND_WORLD;
|
||||
|
||||
|
@@ -532,6 +532,13 @@ static void gpu_viewport_draw_colormanaged(GPUViewport *viewport,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge and draw the buffers of \a viewport into the currently active framebuffer, performing
|
||||
* color transform to display space.
|
||||
*
|
||||
* \param rect: Coordinates to draw into. By swapping min and max values, drawing can be done with
|
||||
* inversed axis coordinates (upside down or sideways).
|
||||
*/
|
||||
void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect)
|
||||
{
|
||||
DefaultFramebufferList *dfbl = viewport->fbl;
|
||||
@@ -545,18 +552,22 @@ void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect)
|
||||
const float w = (float)GPU_texture_width(color);
|
||||
const float h = (float)GPU_texture_height(color);
|
||||
|
||||
BLI_assert(w == BLI_rcti_size_x(rect) + 1);
|
||||
BLI_assert(h == BLI_rcti_size_y(rect) + 1);
|
||||
/* We allow rects with min/max swapped, but we also need coorectly assigned coordinates. */
|
||||
rcti sanitized_rect = *rect;
|
||||
BLI_rcti_sanitize(&sanitized_rect);
|
||||
|
||||
BLI_assert(w == BLI_rcti_size_x(&sanitized_rect) + 1);
|
||||
BLI_assert(h == BLI_rcti_size_y(&sanitized_rect) + 1);
|
||||
|
||||
/* wmOrtho for the screen has this same offset */
|
||||
const float halfx = GLA_PIXEL_OFS / w;
|
||||
const float halfy = GLA_PIXEL_OFS / h;
|
||||
|
||||
rctf pos_rect = {
|
||||
.xmin = rect->xmin,
|
||||
.ymin = rect->ymin,
|
||||
.xmax = rect->xmin + w,
|
||||
.ymax = rect->ymin + h,
|
||||
.xmin = sanitized_rect.xmin,
|
||||
.ymin = sanitized_rect.ymin,
|
||||
.xmax = sanitized_rect.xmin + w,
|
||||
.ymax = sanitized_rect.ymin + h,
|
||||
};
|
||||
|
||||
rctf uv_rect = {
|
||||
@@ -565,6 +576,14 @@ void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect)
|
||||
.xmax = halfx + 1.0f,
|
||||
.ymax = halfy + 1.0f,
|
||||
};
|
||||
/* Mirror the UV rect in case axis-swapped drawing is requested (by passing a rect with min and
|
||||
* max values swapped). */
|
||||
if (BLI_rcti_size_x(rect) < 0) {
|
||||
SWAP(float, uv_rect.xmin, uv_rect.xmax);
|
||||
}
|
||||
if (BLI_rcti_size_y(rect) < 0) {
|
||||
SWAP(float, uv_rect.ymin, uv_rect.ymax);
|
||||
}
|
||||
|
||||
gpu_viewport_draw_colormanaged(viewport, &pos_rect, &uv_rect, true);
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ typedef enum eV3DOffscreenDrawFlag {
|
||||
V3D_OFSDRAW_NONE = (0),
|
||||
V3D_OFSDRAW_SHOW_ANNOTATION = (1 << 0),
|
||||
V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS = (1 << 1),
|
||||
V3D_OFSDRAW_SHOW_GRIDFLOOR = (1 << 2),
|
||||
} eV3DOffscreenDrawFlag;
|
||||
|
||||
/** #View3DShading.light */
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_vec_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "DNA_xr_types.h"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
@@ -42,6 +43,7 @@ struct wmKeyMap;
|
||||
struct wmMsgBus;
|
||||
struct wmOperator;
|
||||
struct wmOperatorType;
|
||||
struct GHOST_XrContext;
|
||||
|
||||
/* forwards */
|
||||
struct PointerRNA;
|
||||
@@ -119,6 +121,14 @@ typedef struct ReportTimerInfo {
|
||||
float widthfac;
|
||||
} ReportTimerInfo;
|
||||
|
||||
//#ifdef WITH_XR_OPENXR
|
||||
typedef struct wmXrData {
|
||||
void *context; /* GHOST_XrContextHandle */
|
||||
|
||||
XrSessionSettings session_settings;
|
||||
} wmXrData;
|
||||
//#endif
|
||||
|
||||
/* reports need to be before wmWindowManager */
|
||||
|
||||
/* windowmanager is saved, tag WMAN */
|
||||
@@ -180,6 +190,9 @@ typedef struct wmWindowManager {
|
||||
|
||||
struct wmMsgBus *message_bus;
|
||||
|
||||
//#ifdef WITH_XR_OPENXR
|
||||
wmXrData xr;
|
||||
//#endif
|
||||
} wmWindowManager;
|
||||
|
||||
/* wmWindowManager.initialized */
|
||||
|
37
source/blender/makesdna/DNA_xr_types.h
Normal file
37
source/blender/makesdna/DNA_xr_types.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup DNA
|
||||
*/
|
||||
|
||||
#ifndef __DNA_XR_TYPES_H__
|
||||
#define __DNA_XR_TYPES_H__
|
||||
|
||||
typedef struct XrSessionSettings {
|
||||
/** Shading type (OB_SOLID, ...). */
|
||||
char shading_type;
|
||||
/** View3D draw flags (V3D_OFSDRAW_NONE, V3D_OFSDRAW_SHOW_ANNOTATION, ...). */
|
||||
char draw_flags;
|
||||
char _pad[2];
|
||||
|
||||
/** Clipping distance. */
|
||||
float clip_start, clip_end;
|
||||
|
||||
char _pad2[4];
|
||||
} XrSessionSettings;
|
||||
|
||||
#endif /* __DNA_XR_TYPES_H__ */
|
@@ -132,6 +132,7 @@ static const char *includefiles[] = {
|
||||
"DNA_workspace_types.h",
|
||||
"DNA_lightprobe_types.h",
|
||||
"DNA_curveprofile_types.h",
|
||||
"DNA_xr_types.h",
|
||||
|
||||
/* see comment above before editing! */
|
||||
|
||||
@@ -1598,6 +1599,7 @@ int main(int argc, char **argv)
|
||||
#include "DNA_workspace_types.h"
|
||||
#include "DNA_lightprobe_types.h"
|
||||
#include "DNA_curveprofile_types.h"
|
||||
#include "DNA_xr_types.h"
|
||||
|
||||
/* end of list */
|
||||
|
||||
|
@@ -694,6 +694,7 @@ extern StructRNA RNA_WorkSpace;
|
||||
extern StructRNA RNA_World;
|
||||
extern StructRNA RNA_WorldLighting;
|
||||
extern StructRNA RNA_WorldMistSettings;
|
||||
extern StructRNA RNA_XrSessionSettings;
|
||||
extern StructRNA RNA_uiPopover;
|
||||
extern StructRNA RNA_wmOwnerIDs;
|
||||
|
||||
|
@@ -92,6 +92,7 @@ set(DEFSRC
|
||||
rna_wm_gizmo.c
|
||||
rna_workspace.c
|
||||
rna_world.c
|
||||
rna_xr.c
|
||||
)
|
||||
|
||||
set(APISRC
|
||||
|
@@ -4306,6 +4306,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
|
||||
{"rna_movieclip.c", NULL, RNA_def_movieclip},
|
||||
{"rna_tracking.c", NULL, RNA_def_tracking},
|
||||
{"rna_mask.c", NULL, RNA_def_mask},
|
||||
{"rna_xr.c", NULL, RNA_def_xr},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
|
@@ -205,6 +205,7 @@ void RNA_def_world(struct BlenderRNA *brna);
|
||||
void RNA_def_movieclip(struct BlenderRNA *brna);
|
||||
void RNA_def_tracking(struct BlenderRNA *brna);
|
||||
void RNA_def_mask(struct BlenderRNA *brna);
|
||||
void RNA_def_xr(struct BlenderRNA *brna);
|
||||
|
||||
/* Common Define functions */
|
||||
|
||||
|
@@ -2484,6 +2484,11 @@ static void rna_def_windowmanager(BlenderRNA *brna)
|
||||
prop, "rna_wmClipboard_get", "rna_wmClipboard_length", "rna_wmClipboard_set");
|
||||
RNA_def_property_ui_text(prop, "Text Clipboard", "");
|
||||
|
||||
prop = RNA_def_property(srna, "xr_session_settings", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "xr.session_settings");
|
||||
RNA_def_property_flag(prop, PROP_NEVER_NULL);
|
||||
RNA_def_property_ui_text(prop, "XR Session Settings", "");
|
||||
|
||||
RNA_api_wm(srna);
|
||||
}
|
||||
|
||||
|
72
source/blender/makesrna/intern/rna_xr.c
Normal file
72
source/blender/makesrna/intern/rna_xr.c
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup RNA
|
||||
*/
|
||||
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
|
||||
#include "rna_internal.h"
|
||||
|
||||
#ifdef RNA_RUNTIME
|
||||
|
||||
#else /* RNA_RUNTIME */
|
||||
|
||||
static void rna_def_xr_session_settings(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "XrSessionSettings", NULL);
|
||||
RNA_def_struct_ui_text(srna, "XR-Session Settings", "");
|
||||
|
||||
prop = RNA_def_property(srna, "shading_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, rna_enum_shading_type_items);
|
||||
RNA_def_property_ui_text(prop, "Shading Type", "Method to display/shade objects in the VR View");
|
||||
|
||||
prop = RNA_def_property(srna, "show_floor", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_SHOW_GRIDFLOOR);
|
||||
RNA_def_property_ui_text(prop, "Display Grid Floor", "Show the ground plane grid");
|
||||
|
||||
prop = RNA_def_property(srna, "show_annotation", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_SHOW_ANNOTATION);
|
||||
RNA_def_property_ui_text(prop, "Show Annotation", "Show annotations for this view");
|
||||
|
||||
prop = RNA_def_property(srna, "clip_start", PROP_FLOAT, PROP_DISTANCE);
|
||||
RNA_def_property_range(prop, 1e-6f, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 10, 3);
|
||||
RNA_def_property_ui_text(prop, "Clip Start", "VR View near clipping distance");
|
||||
|
||||
prop = RNA_def_property(srna, "clip_end", PROP_FLOAT, PROP_DISTANCE);
|
||||
RNA_def_property_range(prop, 1e-6f, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 10, 3);
|
||||
RNA_def_property_ui_text(prop, "Clip End", "VR View far clipping distance");
|
||||
}
|
||||
|
||||
void RNA_def_xr(BlenderRNA *brna)
|
||||
{
|
||||
RNA_define_animate_sdna(false);
|
||||
|
||||
rna_def_xr_session_settings(brna);
|
||||
|
||||
RNA_define_animate_sdna(true);
|
||||
}
|
||||
|
||||
#endif /* RNA_RUNTIME */
|
@@ -75,6 +75,7 @@ set(SRC
|
||||
intern/wm_splash_screen.c
|
||||
intern/wm_stereo.c
|
||||
intern/wm_subwindow.c
|
||||
intern/wm_surface.c
|
||||
intern/wm_toolsystem.c
|
||||
intern/wm_tooltip.c
|
||||
intern/wm_uilist_type.c
|
||||
@@ -101,6 +102,7 @@ set(SRC
|
||||
wm_event_system.h
|
||||
wm_event_types.h
|
||||
wm_files.h
|
||||
wm_surface.h
|
||||
wm_window.h
|
||||
intern/wm_platform_support.h
|
||||
intern/wm_window_private.h
|
||||
@@ -187,4 +189,11 @@ if(WITH_COMPOSITOR)
|
||||
add_definitions(-DWITH_COMPOSITOR)
|
||||
endif()
|
||||
|
||||
if(WITH_XR_OPENXR)
|
||||
add_definitions(-DWITH_XR_OPENXR)
|
||||
list(APPEND SRC
|
||||
intern/wm_xr.c
|
||||
)
|
||||
endif()
|
||||
|
||||
blender_add_lib_nolist(bf_windowmanager "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
@@ -156,6 +156,10 @@ void *WM_opengl_context_create(void);
|
||||
void WM_opengl_context_dispose(void *context);
|
||||
void WM_opengl_context_activate(void *context);
|
||||
void WM_opengl_context_release(void *context);
|
||||
#ifdef WIN32
|
||||
void *WM_directx_context_create(void);
|
||||
void WM_directx_context_dispose(void *context);
|
||||
#endif
|
||||
|
||||
struct wmWindow *WM_window_open(struct bContext *C, const struct rcti *rect);
|
||||
struct wmWindow *WM_window_open_temp(struct bContext *C,
|
||||
|
@@ -399,6 +399,10 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
|
||||
WM_msgbus_destroy(wm->message_bus);
|
||||
}
|
||||
|
||||
#ifdef WITH_XR_OPENXR
|
||||
wm_xr_context_destroy(wm);
|
||||
#endif
|
||||
|
||||
BLI_freelistN(&wm->paintcursors);
|
||||
|
||||
WM_drag_free_list(&wm->drags);
|
||||
|
@@ -67,6 +67,7 @@
|
||||
#include "wm_draw.h"
|
||||
#include "wm_window.h"
|
||||
#include "wm_event_system.h"
|
||||
#include "wm_surface.h"
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
# include "BKE_subsurf.h"
|
||||
@@ -836,6 +837,20 @@ static void wm_draw_window(bContext *C, wmWindow *win)
|
||||
screen->do_draw = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw offscreen contexts not bound to a specific window.
|
||||
*/
|
||||
static void wm_draw_surface(bContext *C, wmSurface *surface)
|
||||
{
|
||||
wm_window_clear_drawable(CTX_wm_manager(C));
|
||||
wm_surface_make_drawable(surface);
|
||||
|
||||
surface->draw(C);
|
||||
|
||||
/* Avoid interference with window drawable */
|
||||
wm_surface_clear_drawable();
|
||||
}
|
||||
|
||||
/****************** main update call **********************/
|
||||
|
||||
/* quick test to prevent changing window drawable */
|
||||
@@ -963,6 +978,9 @@ void wm_draw_update(bContext *C)
|
||||
CTX_wm_window_set(C, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw non-windows (surfaces) */
|
||||
wm_surfaces_iter(C, wm_draw_surface);
|
||||
}
|
||||
|
||||
void wm_draw_region_clear(wmWindow *win, ARegion *UNUSED(region))
|
||||
|
@@ -98,6 +98,7 @@
|
||||
#include "wm_event_system.h"
|
||||
#include "wm.h"
|
||||
#include "wm_files.h"
|
||||
#include "wm_surface.h"
|
||||
#include "wm_window.h"
|
||||
#include "wm_platform_support.h"
|
||||
|
||||
@@ -534,6 +535,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
|
||||
BKE_materials_exit();
|
||||
|
||||
wm_operatortype_free();
|
||||
wm_surfaces_free();
|
||||
wm_dropbox_free();
|
||||
WM_menutype_free();
|
||||
WM_uilisttype_free();
|
||||
|
@@ -3645,6 +3645,39 @@ static void WM_OT_stereo3d_set(wmOperatorType *ot)
|
||||
|
||||
/** \} */
|
||||
|
||||
#ifdef WITH_XR_OPENXR
|
||||
static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
|
||||
/* Lazy-create xr context - tries to dynlink to the runtime, reading active_runtime.json. */
|
||||
if (wm_xr_context_ensure(wm) == false) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
wm_xr_session_toggle(C, wm->xr.context);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void WM_OT_xr_session_toggle(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Toggle VR Session";
|
||||
ot->idname = "WM_OT_xr_session_toggle";
|
||||
ot->description =
|
||||
"Open a view for use with virtual reality headsets, or close it if already "
|
||||
"opened";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = wm_xr_session_toggle_exec;
|
||||
|
||||
/* XXX INTERNAL just to hide it from the search menu by default, an Add-on will expose it in the
|
||||
* UI instead. Not meant as a permanent solution. */
|
||||
ot->flag = OPTYPE_INTERNAL;
|
||||
}
|
||||
#endif /* WITH_XR_OPENXR */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Operator Registration & Keymaps
|
||||
* \{ */
|
||||
@@ -3686,6 +3719,9 @@ void wm_operatortypes_register(void)
|
||||
WM_operatortype_append(WM_OT_call_panel);
|
||||
WM_operatortype_append(WM_OT_radial_control);
|
||||
WM_operatortype_append(WM_OT_stereo3d_set);
|
||||
#ifdef WITH_XR_OPENXR
|
||||
WM_operatortype_append(WM_OT_xr_session_toggle);
|
||||
#endif
|
||||
#if defined(WIN32)
|
||||
WM_operatortype_append(WM_OT_console_toggle);
|
||||
#endif
|
||||
|
118
source/blender/windowmanager/intern/wm_surface.c
Normal file
118
source/blender/windowmanager/intern/wm_surface.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#include "BKE_context.h"
|
||||
|
||||
#include "BLF_api.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "GHOST_C-api.h"
|
||||
|
||||
#include "GPU_batch_presets.h"
|
||||
#include "GPU_framebuffer.h"
|
||||
#include "GPU_immediate.h"
|
||||
#include "GPU_context.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "WM_types.h"
|
||||
#include "WM_api.h"
|
||||
#include "wm.h"
|
||||
|
||||
#include "wm_surface.h"
|
||||
|
||||
static ListBase global_surface_list = {NULL, NULL};
|
||||
static wmSurface *g_drawable = NULL;
|
||||
|
||||
void wm_surfaces_iter(bContext *C, void (*cb)(bContext *C, wmSurface *))
|
||||
{
|
||||
for (wmSurface *surf = global_surface_list.first; surf; surf = surf->next) {
|
||||
cb(C, surf);
|
||||
}
|
||||
}
|
||||
|
||||
void wm_surface_clear_drawable(void)
|
||||
{
|
||||
if (g_drawable) {
|
||||
BLF_batch_reset();
|
||||
gpu_batch_presets_reset();
|
||||
immDeactivate();
|
||||
|
||||
g_drawable = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void wm_surface_set_drawable(wmSurface *surface, bool activate)
|
||||
{
|
||||
BLI_assert(ELEM(g_drawable, NULL, surface));
|
||||
|
||||
g_drawable = surface;
|
||||
if (activate) {
|
||||
GHOST_ActivateOpenGLContext(surface->ghost_ctx);
|
||||
}
|
||||
|
||||
GPU_context_active_set(surface->gpu_ctx);
|
||||
immActivate();
|
||||
}
|
||||
|
||||
void wm_surface_make_drawable(wmSurface *surface)
|
||||
{
|
||||
BLI_assert(GPU_framebuffer_active_get() == NULL);
|
||||
|
||||
if (surface != g_drawable) {
|
||||
wm_surface_clear_drawable();
|
||||
wm_surface_set_drawable(surface, true);
|
||||
}
|
||||
}
|
||||
|
||||
void wm_surface_reset_drawable(void)
|
||||
{
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
BLI_assert(GPU_framebuffer_active_get() == NULL);
|
||||
|
||||
if (g_drawable) {
|
||||
wm_surface_clear_drawable();
|
||||
wm_surface_set_drawable(g_drawable, true);
|
||||
}
|
||||
}
|
||||
|
||||
void wm_surface_add(wmSurface *surface)
|
||||
{
|
||||
BLI_addtail(&global_surface_list, surface);
|
||||
}
|
||||
|
||||
void wm_surface_remove(wmSurface *surface)
|
||||
{
|
||||
BLI_remlink(&global_surface_list, surface);
|
||||
surface->free_data(surface);
|
||||
MEM_freeN(surface);
|
||||
}
|
||||
|
||||
void wm_surfaces_free(void)
|
||||
{
|
||||
for (wmSurface *surf = global_surface_list.first, *surf_next; surf; surf = surf_next) {
|
||||
surf_next = surf->next;
|
||||
wm_surface_remove(surf);
|
||||
}
|
||||
|
||||
BLI_assert(BLI_listbase_is_empty(&global_surface_list));
|
||||
}
|
@@ -1631,6 +1631,9 @@ void wm_window_process_events(const bContext *C)
|
||||
GHOST_DispatchEvents(g_system);
|
||||
}
|
||||
hasevent |= wm_window_timer(C);
|
||||
#ifdef WITH_XR_OPENXR
|
||||
hasevent |= GHOST_XrEventsHandle(CTX_wm_manager(C)->xr.context);
|
||||
#endif
|
||||
|
||||
/* no event, we sleep 5 milliseconds */
|
||||
if (hasevent == 0) {
|
||||
@@ -1957,6 +1960,9 @@ void wm_window_raise(wmWindow *win)
|
||||
/** \name Window Buffers
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \brief Push rendered buffer to the screen.
|
||||
*/
|
||||
void wm_window_swap_buffers(wmWindow *win)
|
||||
{
|
||||
GHOST_SwapWindowBuffers(win->ghostwin);
|
||||
@@ -2446,3 +2452,24 @@ void WM_ghost_show_message_box(const char *title,
|
||||
GHOST_ShowMessageBox(g_system, title, message, help_label, continue_label, link, dialog_options);
|
||||
}
|
||||
/** \} */
|
||||
|
||||
#ifdef WIN32
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Direct DirectX Context Management
|
||||
* \{ */
|
||||
|
||||
void *WM_directx_context_create(void)
|
||||
{
|
||||
BLI_assert(GPU_framebuffer_active_get() == NULL);
|
||||
return GHOST_CreateDirectXContext(g_system);
|
||||
}
|
||||
|
||||
void WM_directx_context_dispose(void *context)
|
||||
{
|
||||
BLI_assert(GPU_framebuffer_active_get() == NULL);
|
||||
GHOST_DisposeDirectXContext(g_system, context);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
#endif
|
||||
|
484
source/blender/windowmanager/intern/wm_xr.c
Normal file
484
source/blender/windowmanager/intern/wm_xr.c
Normal file
@@ -0,0 +1,484 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* \name Window-Manager XR API
|
||||
*
|
||||
* Implements Blender specific functionality for the GHOST_Xr API.
|
||||
*/
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_screen.h"
|
||||
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
#include "DNA_xr_types.h"
|
||||
|
||||
#include "DRW_engine.h"
|
||||
|
||||
#include "ED_view3d.h"
|
||||
#include "ED_view3d_offscreen.h"
|
||||
|
||||
#include "GHOST_C-api.h"
|
||||
|
||||
#include "GPU_context.h"
|
||||
#include "GPU_draw.h"
|
||||
#include "GPU_matrix.h"
|
||||
#include "GPU_viewport.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
|
||||
#include "WM_types.h"
|
||||
#include "WM_api.h"
|
||||
|
||||
#include "wm.h"
|
||||
#include "wm_surface.h"
|
||||
#include "wm_window.h"
|
||||
|
||||
static wmSurface *g_xr_surface = NULL;
|
||||
static CLG_LogRef LOG = {"wm.xr"};
|
||||
|
||||
typedef struct {
|
||||
GHOST_TXrGraphicsBinding gpu_binding_type;
|
||||
GPUOffScreen *offscreen;
|
||||
GPUViewport *viewport;
|
||||
|
||||
GHOST_ContextHandle secondary_ghost_ctx;
|
||||
} wmXrSurfaceData;
|
||||
|
||||
typedef struct {
|
||||
wmWindowManager *wm;
|
||||
} wmXrErrorHandlerData;
|
||||
|
||||
void wm_xr_draw_view(const GHOST_XrDrawViewInfo *, void *);
|
||||
void *wm_xr_session_gpu_binding_context_create(GHOST_TXrGraphicsBinding);
|
||||
void wm_xr_session_gpu_binding_context_destroy(GHOST_TXrGraphicsBinding, void *);
|
||||
wmSurface *wm_xr_session_surface_create(wmWindowManager *, unsigned int);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name XR-Context
|
||||
*
|
||||
* All XR functionality is accessed through a #GHOST_XrContext handle.
|
||||
* The lifetime of this context also determines the lifetime of the OpenXR instance, which is the
|
||||
* representation of the OpenXR runtime connection within the application.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
static void wm_xr_error_handler(const GHOST_XrError *error)
|
||||
{
|
||||
wmXrErrorHandlerData *handler_data = error->customdata;
|
||||
wmWindowManager *wm = handler_data->wm;
|
||||
|
||||
BKE_reports_clear(&wm->reports);
|
||||
WM_report(RPT_ERROR, error->user_message);
|
||||
WM_report_banner_show();
|
||||
|
||||
if (wm->xr.context) {
|
||||
/* Just play safe and destroy the entire context. */
|
||||
GHOST_XrContextDestroy(wm->xr.context);
|
||||
wm->xr.context = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool wm_xr_context_ensure(wmWindowManager *wm)
|
||||
{
|
||||
if (wm->xr.context) {
|
||||
return true;
|
||||
}
|
||||
static wmXrErrorHandlerData error_customdata;
|
||||
|
||||
/* Set up error handling */
|
||||
error_customdata.wm = wm;
|
||||
GHOST_XrErrorHandler(wm_xr_error_handler, &error_customdata);
|
||||
|
||||
{
|
||||
const GHOST_TXrGraphicsBinding gpu_bindings_candidates[] = {
|
||||
GHOST_kXrGraphicsOpenGL,
|
||||
#ifdef WIN32
|
||||
GHOST_kXrGraphicsD3D11,
|
||||
#endif
|
||||
};
|
||||
GHOST_XrContextCreateInfo create_info = {
|
||||
.gpu_binding_candidates = gpu_bindings_candidates,
|
||||
.gpu_binding_candidates_count = ARRAY_SIZE(gpu_bindings_candidates)};
|
||||
|
||||
if (G.debug & G_DEBUG_XR) {
|
||||
create_info.context_flag |= GHOST_kXrContextDebug;
|
||||
}
|
||||
if (G.debug & G_DEBUG_XR_TIME) {
|
||||
create_info.context_flag |= GHOST_kXrContextDebugTime;
|
||||
}
|
||||
|
||||
if (!(wm->xr.context = GHOST_XrContextCreate(&create_info))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set up context callbacks */
|
||||
GHOST_XrGraphicsContextBindFuncs(wm->xr.context,
|
||||
wm_xr_session_gpu_binding_context_create,
|
||||
wm_xr_session_gpu_binding_context_destroy);
|
||||
GHOST_XrDrawViewFunc(wm->xr.context, wm_xr_draw_view);
|
||||
}
|
||||
BLI_assert(wm->xr.context != NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void wm_xr_context_destroy(wmWindowManager *wm)
|
||||
{
|
||||
if (wm->xr.context != NULL) {
|
||||
GHOST_XrContextDestroy(wm->xr.context);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */ /* XR-Context */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name XR-Session
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void *wm_xr_session_gpu_binding_context_create(GHOST_TXrGraphicsBinding graphics_binding)
|
||||
{
|
||||
wmSurface *surface = wm_xr_session_surface_create(G_MAIN->wm.first, graphics_binding);
|
||||
wmXrSurfaceData *data = surface->customdata;
|
||||
|
||||
wm_surface_add(surface);
|
||||
|
||||
return data->secondary_ghost_ctx ? data->secondary_ghost_ctx : surface->ghost_ctx;
|
||||
}
|
||||
|
||||
void wm_xr_session_gpu_binding_context_destroy(GHOST_TXrGraphicsBinding UNUSED(graphics_lib),
|
||||
void *UNUSED(context))
|
||||
{
|
||||
if (g_xr_surface) { /* Might have been freed already */
|
||||
wm_surface_remove(g_xr_surface);
|
||||
}
|
||||
|
||||
wm_window_reset_drawable();
|
||||
}
|
||||
|
||||
static void wm_xr_session_begin_info_create(const Scene *scene,
|
||||
GHOST_XrSessionBeginInfo *begin_info)
|
||||
{
|
||||
if (scene->camera) {
|
||||
copy_v3_v3(begin_info->base_pose.position, scene->camera->loc);
|
||||
if (ELEM(scene->camera->rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) {
|
||||
axis_angle_to_quat(
|
||||
begin_info->base_pose.orientation_quat, scene->camera->rotAxis, scene->camera->rotAngle);
|
||||
}
|
||||
else if (scene->camera->rotmode == ROT_MODE_QUAT) {
|
||||
copy_v4_v4(begin_info->base_pose.orientation_quat, scene->camera->quat);
|
||||
}
|
||||
else {
|
||||
eul_to_quat(begin_info->base_pose.orientation_quat, scene->camera->rot);
|
||||
}
|
||||
}
|
||||
else {
|
||||
copy_v3_fl(begin_info->base_pose.position, 0.0f);
|
||||
unit_qt(begin_info->base_pose.orientation_quat);
|
||||
}
|
||||
}
|
||||
|
||||
void wm_xr_session_toggle(bContext *C, void *xr_context_ptr)
|
||||
{
|
||||
GHOST_XrContextHandle xr_context = xr_context_ptr;
|
||||
|
||||
if (xr_context && GHOST_XrSessionIsRunning(xr_context)) {
|
||||
GHOST_XrSessionEnd(xr_context);
|
||||
}
|
||||
else {
|
||||
GHOST_XrSessionBeginInfo begin_info;
|
||||
|
||||
wm_xr_session_begin_info_create(CTX_data_scene(C), &begin_info);
|
||||
|
||||
GHOST_XrSessionStart(xr_context, &begin_info);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */ /* XR-Session */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name XR-Session Surface
|
||||
*
|
||||
* A wmSurface is used to manage drawing of the VR viewport. It's created and destroyed with the
|
||||
* session.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \brief Call Ghost-XR to draw a frame
|
||||
*
|
||||
* Draw callback for the XR-session surface. It's expected to be called on each main loop iteration
|
||||
* and tells Ghost-XR to submit a new frame by drawing its views. Note that for drawing each view,
|
||||
* #wm_xr_draw_view() will be called through Ghost-XR (see GHOST_XrDrawViewFunc()).
|
||||
*/
|
||||
static void wm_xr_session_surface_draw(bContext *C)
|
||||
{
|
||||
wmXrSurfaceData *surface_data = g_xr_surface->customdata;
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
|
||||
if (!GHOST_XrSessionIsRunning(wm->xr.context)) {
|
||||
return;
|
||||
}
|
||||
GHOST_XrSessionDrawViews(wm->xr.context, C);
|
||||
|
||||
GPU_offscreen_unbind(surface_data->offscreen, false);
|
||||
}
|
||||
|
||||
static void wm_xr_session_free_data(wmSurface *surface)
|
||||
{
|
||||
wmXrSurfaceData *data = surface->customdata;
|
||||
|
||||
if (data->secondary_ghost_ctx) {
|
||||
#ifdef WIN32
|
||||
if (data->gpu_binding_type == GHOST_kXrGraphicsD3D11) {
|
||||
WM_directx_context_dispose(data->secondary_ghost_ctx);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (data->viewport) {
|
||||
GPU_viewport_free(data->viewport);
|
||||
}
|
||||
if (data->offscreen) {
|
||||
GPU_offscreen_free(data->offscreen);
|
||||
}
|
||||
|
||||
MEM_freeN(surface->customdata);
|
||||
|
||||
g_xr_surface = NULL;
|
||||
}
|
||||
|
||||
static bool wm_xr_session_surface_offscreen_ensure(const GHOST_XrDrawViewInfo *draw_view)
|
||||
{
|
||||
wmXrSurfaceData *surface_data = g_xr_surface->customdata;
|
||||
const bool size_changed = surface_data->offscreen &&
|
||||
(GPU_offscreen_width(surface_data->offscreen) != draw_view->width) &&
|
||||
(GPU_offscreen_height(surface_data->offscreen) != draw_view->height);
|
||||
char err_out[256] = "unknown";
|
||||
bool failure = false;
|
||||
|
||||
if (surface_data->offscreen) {
|
||||
BLI_assert(surface_data->viewport);
|
||||
|
||||
if (!size_changed) {
|
||||
return true;
|
||||
}
|
||||
GPU_viewport_free(surface_data->viewport);
|
||||
GPU_offscreen_free(surface_data->offscreen);
|
||||
}
|
||||
|
||||
if (!(surface_data->offscreen = GPU_offscreen_create(
|
||||
draw_view->width, draw_view->height, 0, true, false, err_out))) {
|
||||
failure = true;
|
||||
}
|
||||
|
||||
if (failure) {
|
||||
/* Pass. */
|
||||
}
|
||||
else if (!(surface_data->viewport = GPU_viewport_create())) {
|
||||
GPU_offscreen_free(surface_data->offscreen);
|
||||
failure = true;
|
||||
}
|
||||
|
||||
if (failure) {
|
||||
CLOG_ERROR(&LOG, "Failed to get buffer, %s\n", err_out);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wmSurface *wm_xr_session_surface_create(wmWindowManager *UNUSED(wm), unsigned int gpu_binding_type)
|
||||
{
|
||||
if (g_xr_surface) {
|
||||
BLI_assert(false);
|
||||
return g_xr_surface;
|
||||
}
|
||||
|
||||
wmSurface *surface = MEM_callocN(sizeof(*surface), __func__);
|
||||
wmXrSurfaceData *data = MEM_callocN(sizeof(*data), "XrSurfaceData");
|
||||
|
||||
#ifndef WIN32
|
||||
BLI_assert(gpu_binding_type == GHOST_kXrGraphicsOpenGL);
|
||||
#endif
|
||||
|
||||
surface->draw = wm_xr_session_surface_draw;
|
||||
surface->free_data = wm_xr_session_free_data;
|
||||
|
||||
data->gpu_binding_type = gpu_binding_type;
|
||||
surface->customdata = data;
|
||||
|
||||
surface->ghost_ctx = DRW_xr_opengl_context_get();
|
||||
|
||||
switch (gpu_binding_type) {
|
||||
case GHOST_kXrGraphicsOpenGL:
|
||||
break;
|
||||
#ifdef WIN32
|
||||
case GHOST_kXrGraphicsD3D11:
|
||||
data->secondary_ghost_ctx = WM_directx_context_create();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
surface->gpu_ctx = DRW_xr_gpu_context_get();
|
||||
|
||||
g_xr_surface = surface;
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
/** \} */ /* XR-Session Surface */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name XR Drawing
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Proper reference space set up is not supported yet. We simply hand OpenXR the global space as
|
||||
* reference space and apply its pose onto the active camera matrix to get a basic viewing
|
||||
* experience going. If there's no active camera with stick to the world origin.
|
||||
*/
|
||||
static void wm_xr_draw_matrices_create(const Scene *scene,
|
||||
const GHOST_XrDrawViewInfo *draw_view,
|
||||
const float clip_start,
|
||||
const float clip_end,
|
||||
float r_view_mat[4][4],
|
||||
float r_proj_mat[4][4])
|
||||
{
|
||||
float scalemat[4][4], quat[4];
|
||||
float temp[4][4];
|
||||
|
||||
perspective_m4_fov(r_proj_mat,
|
||||
draw_view->fov.angle_left,
|
||||
draw_view->fov.angle_right,
|
||||
draw_view->fov.angle_up,
|
||||
draw_view->fov.angle_down,
|
||||
clip_start,
|
||||
clip_end);
|
||||
|
||||
scale_m4_fl(scalemat, 1.0f);
|
||||
invert_qt_qt_normalized(quat, draw_view->pose.orientation_quat);
|
||||
quat_to_mat4(temp, quat);
|
||||
translate_m4(temp,
|
||||
-draw_view->pose.position[0],
|
||||
-draw_view->pose.position[1],
|
||||
-draw_view->pose.position[2]);
|
||||
|
||||
if (scene->camera) {
|
||||
invert_m4_m4(scene->camera->imat, scene->camera->obmat);
|
||||
mul_m4_m4m4(r_view_mat, temp, scene->camera->imat);
|
||||
}
|
||||
else {
|
||||
copy_m4_m4(r_view_mat, temp);
|
||||
}
|
||||
}
|
||||
|
||||
static void wm_xr_draw_viewport_buffers_to_active_framebuffer(
|
||||
const wmXrSurfaceData *surface_data, const GHOST_XrDrawViewInfo *draw_view)
|
||||
{
|
||||
const bool is_upside_down = surface_data->secondary_ghost_ctx &&
|
||||
GHOST_isUpsideDownContext(surface_data->secondary_ghost_ctx);
|
||||
rcti rect = {.xmin = 0, .ymin = 0, .xmax = draw_view->width - 1, .ymax = draw_view->height - 1};
|
||||
|
||||
wmViewport(&rect);
|
||||
|
||||
/* For upside down contexts, draw with inverted y-values. */
|
||||
if (is_upside_down) {
|
||||
SWAP(int, rect.ymin, rect.ymax);
|
||||
}
|
||||
GPU_viewport_draw_to_screen(surface_data->viewport, &rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Draw a viewport for a single eye.
|
||||
*
|
||||
* This is the main viewport drawing function for VR sessions. It's assigned to Ghost-XR as a
|
||||
* callback (see GHOST_XrDrawViewFunc()) and executed for each view (read: eye).
|
||||
*/
|
||||
void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
|
||||
{
|
||||
bContext *C = customdata;
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
wmXrSurfaceData *surface_data = g_xr_surface->customdata;
|
||||
XrSessionSettings *settings = &wm->xr.session_settings;
|
||||
const float display_flags = V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS | settings->draw_flags;
|
||||
|
||||
View3DShading shading;
|
||||
float viewmat[4][4], winmat[4][4];
|
||||
|
||||
wm_xr_draw_matrices_create(
|
||||
CTX_data_scene(C), draw_view, settings->clip_start, settings->clip_end, viewmat, winmat);
|
||||
|
||||
if (!wm_xr_session_surface_offscreen_ensure(draw_view)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* In case a framebuffer is still bound from drawing the last eye. */
|
||||
GPU_framebuffer_restore();
|
||||
|
||||
BKE_screen_view3d_shading_init(&shading);
|
||||
shading.flag |= V3D_SHADING_WORLD_ORIENTATION;
|
||||
shading.flag &= ~V3D_SHADING_SPECULAR_HIGHLIGHT;
|
||||
shading.background_type = V3D_SHADING_BACKGROUND_WORLD;
|
||||
|
||||
/* Draws the view into the surface_data->viewport's framebuffers */
|
||||
ED_view3d_draw_offscreen_simple(CTX_data_ensure_evaluated_depsgraph(C),
|
||||
CTX_data_scene(C),
|
||||
&shading,
|
||||
wm->xr.session_settings.shading_type,
|
||||
draw_view->width,
|
||||
draw_view->height,
|
||||
display_flags,
|
||||
viewmat,
|
||||
winmat,
|
||||
settings->clip_start,
|
||||
settings->clip_end,
|
||||
true,
|
||||
true,
|
||||
NULL,
|
||||
false,
|
||||
surface_data->offscreen,
|
||||
surface_data->viewport);
|
||||
|
||||
/* The draw-manager uses both GPUOffscreen and GPUViewport to manage frame and texture buffers. A
|
||||
* call to GPU_viewport_draw_to_screen() is still needed to get the final result from the
|
||||
* viewport buffers composited together and potentially color managed for display on screen.
|
||||
* It needs a bound framebuffer to draw into, for which we simply reuse the GPUOffscreen one.
|
||||
*
|
||||
* In a next step, Ghost-XR will use the the currently bound framebuffer to retrieve the image to
|
||||
* be submitted to the OpenXR swapchain. So do not un-bind the offscreen yet! */
|
||||
|
||||
GPU_offscreen_bind(surface_data->offscreen, false);
|
||||
|
||||
wm_xr_draw_viewport_buffers_to_active_framebuffer(surface_data, draw_view);
|
||||
}
|
||||
|
||||
/** \} */ /* XR Drawing */
|
@@ -98,4 +98,11 @@ void wm_stereo3d_set_cancel(bContext *C, wmOperator *op);
|
||||
void wm_open_init_load_ui(wmOperator *op, bool use_prefs);
|
||||
void wm_open_init_use_scripts(wmOperator *op, bool use_prefs);
|
||||
|
||||
#ifdef WITH_XR_OPENXR
|
||||
/* wm_xr.c */
|
||||
bool wm_xr_context_ensure(wmWindowManager *wm);
|
||||
void wm_xr_context_destroy(wmWindowManager *wm);
|
||||
void wm_xr_session_toggle(bContext *C, void *xr_context);
|
||||
#endif
|
||||
|
||||
#endif /* __WM_H__ */
|
||||
|
57
source/blender/windowmanager/wm_surface.h
Normal file
57
source/blender/windowmanager/wm_surface.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* \name WM-Surface
|
||||
*
|
||||
* Container to manage painting in an offscreen context.
|
||||
*/
|
||||
|
||||
#ifndef __WM_SURFACE_H__
|
||||
#define __WM_SURFACE_H__
|
||||
|
||||
struct bContext;
|
||||
|
||||
typedef struct wmSurface {
|
||||
struct wmSurface *next, *prev;
|
||||
|
||||
GHOST_ContextHandle ghost_ctx;
|
||||
struct GPUContext *gpu_ctx;
|
||||
|
||||
void *customdata;
|
||||
|
||||
void (*draw)(struct bContext *);
|
||||
/** Free customdata, not the surface itself (done by wm_surface API) */
|
||||
void (*free_data)(struct wmSurface *);
|
||||
} wmSurface;
|
||||
|
||||
/* Create/Free */
|
||||
void wm_surface_add(wmSurface *surface);
|
||||
void wm_surface_remove(wmSurface *surface);
|
||||
void wm_surfaces_free(void);
|
||||
|
||||
/* Utils */
|
||||
void wm_surfaces_iter(struct bContext *C, void (*cb)(bContext *, wmSurface *));
|
||||
|
||||
/* Drawing */
|
||||
void wm_surface_make_drawable(wmSurface *surface);
|
||||
void wm_surface_clear_drawable(void);
|
||||
void wm_surface_set_drawable(wmSurface *surface, bool activate);
|
||||
void wm_surface_reset_drawable(void);
|
||||
|
||||
#endif /* __WM_SURFACE_H__ */
|
@@ -111,6 +111,10 @@ if(WITH_FREESTYLE)
|
||||
add_definitions(-DWITH_FREESTYLE)
|
||||
endif()
|
||||
|
||||
if(WITH_XR_OPENXR)
|
||||
add_definitions(-DWITH_XR_OPENXR)
|
||||
endif()
|
||||
|
||||
# Setup the exe sources and buildinfo
|
||||
set(SRC
|
||||
creator.c
|
||||
@@ -856,6 +860,8 @@ elseif(WIN32)
|
||||
${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_gpu_glitchworkaround.cmd
|
||||
${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_log.cmd
|
||||
${CMAKE_SOURCE_DIR}/release/windows/batch/blender_factory_startup.cmd
|
||||
${CMAKE_SOURCE_DIR}/release/windows/batch/blender_oculus.cmd
|
||||
${CMAKE_SOURCE_DIR}/release/windows/batch/oculus.json
|
||||
DESTINATION "."
|
||||
)
|
||||
|
||||
|
@@ -603,6 +603,10 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo
|
||||
BLI_argsPrintArgDoc(ba, "--debug-gpu-shaders");
|
||||
BLI_argsPrintArgDoc(ba, "--debug-gpu-force-workarounds");
|
||||
BLI_argsPrintArgDoc(ba, "--debug-wm");
|
||||
# ifdef WITH_XR_OPENXR
|
||||
BLI_argsPrintArgDoc(ba, "--debug-xr");
|
||||
BLI_argsPrintArgDoc(ba, "--debug-xr-time");
|
||||
# endif
|
||||
BLI_argsPrintArgDoc(ba, "--debug-all");
|
||||
BLI_argsPrintArgDoc(ba, "--debug-io");
|
||||
|
||||
@@ -940,6 +944,16 @@ static const char arg_handle_debug_mode_generic_set_doc_wm[] =
|
||||
"\n\t"
|
||||
"Enable debug messages for the window manager, shows all operators in search, shows "
|
||||
"keymap errors.";
|
||||
# ifdef WITH_XR_OPENXR
|
||||
static const char arg_handle_debug_mode_generic_set_doc_xr[] =
|
||||
"\n\t"
|
||||
"Enable debug messages for virtual reality contexts.\n"
|
||||
"\tEnables the OpenXR API validation layer, (OpenXR) debug messages and general information "
|
||||
"prints.";
|
||||
static const char arg_handle_debug_mode_generic_set_doc_xr_time[] =
|
||||
"\n\t"
|
||||
"Enable debug messages for virtual reality frame rendering times.";
|
||||
# endif
|
||||
static const char arg_handle_debug_mode_generic_set_doc_jobs[] =
|
||||
"\n\t"
|
||||
"Enable time profiling for background jobs.";
|
||||
@@ -2091,6 +2105,16 @@ void main_args_setup(bContext *C, bArgs *ba)
|
||||
(void *)G_DEBUG_HANDLERS);
|
||||
BLI_argsAdd(
|
||||
ba, 1, NULL, "--debug-wm", CB_EX(arg_handle_debug_mode_generic_set, wm), (void *)G_DEBUG_WM);
|
||||
# ifdef WITH_XR_OPENXR
|
||||
BLI_argsAdd(
|
||||
ba, 1, NULL, "--debug-xr", CB_EX(arg_handle_debug_mode_generic_set, xr), (void *)G_DEBUG_XR);
|
||||
BLI_argsAdd(ba,
|
||||
1,
|
||||
NULL,
|
||||
"--debug-xr-time",
|
||||
CB_EX(arg_handle_debug_mode_generic_set, xr_time),
|
||||
(void *)G_DEBUG_XR_TIME);
|
||||
# endif
|
||||
BLI_argsAdd(ba,
|
||||
1,
|
||||
NULL,
|
||||
|
@@ -50,6 +50,9 @@ def _init_addon_blacklist():
|
||||
# netrender has known problems re-registering
|
||||
BLACKLIST_ADDONS.add("netrender")
|
||||
|
||||
if not bpy.app.build_options.xr_openxr:
|
||||
BLACKLIST_ADDONS.add("viewport_vr_preview")
|
||||
|
||||
for mod in addon_utils.modules():
|
||||
if addon_utils.module_bl_info(mod)['blender'] < (2, 80, 0):
|
||||
BLACKLIST_ADDONS.add(mod.__name__)
|
||||
|
@@ -56,6 +56,9 @@ MODULE_SYS_PATHS = {
|
||||
if not bpy.app.build_options.freestyle:
|
||||
BLACKLIST.add("render_freestyle_svg")
|
||||
|
||||
if not bpy.app.build_options.xr_openxr:
|
||||
BLACKLIST.add("viewport_vr_preview")
|
||||
|
||||
BLACKLIST_DIRS = (
|
||||
os.path.join(bpy.utils.resource_path('USER'), "scripts"),
|
||||
) + tuple(addon_utils.paths()[1:])
|
||||
|
Reference in New Issue
Block a user