Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--intern/ghost/CMakeLists.txt3
-rw-r--r--intern/ghost/GHOST_C-api.h12
-rw-r--r--intern/ghost/GHOST_IContext.h9
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp14
-rw-r--r--intern/ghost/intern/GHOST_Context.h8
-rw-r--r--release/windows/batch/blender_oculus.cmd14
-rw-r--r--release/windows/batch/oculus.json9
-rw-r--r--source/blender/CMakeLists.txt1
-rw-r--r--source/blender/blenkernel/BKE_global.h2
-rw-r--r--source/blender/blenkernel/CMakeLists.txt4
-rw-r--r--source/blender/blenlib/BLI_math_geom.h7
-rw-r--r--source/blender/blenlib/intern/math_geom.c19
-rw-r--r--source/blender/blenloader/CMakeLists.txt4
-rw-r--r--source/blender/blenloader/intern/readfile.c1
-rw-r--r--source/blender/blenloader/intern/versioning_280.c14
-rw-r--r--source/blender/draw/CMakeLists.txt4
-rw-r--r--source/blender/draw/DRW_engine.h5
-rw-r--r--source/blender/draw/intern/draw_manager.c24
-rw-r--r--source/blender/editors/include/ED_view3d_offscreen.h17
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c86
-rw-r--r--source/blender/gpu/intern/gpu_viewport.c31
-rw-r--r--source/blender/makesdna/DNA_view3d_enums.h1
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h13
-rw-r--r--source/blender/makesdna/DNA_xr_types.h37
-rw-r--r--source/blender/makesdna/intern/makesdna.c2
-rw-r--r--source/blender/makesrna/RNA_access.h1
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt1
-rw-r--r--source/blender/makesrna/intern/makesrna.c1
-rw-r--r--source/blender/makesrna/intern/rna_internal.h1
-rw-r--r--source/blender/makesrna/intern/rna_wm.c5
-rw-r--r--source/blender/makesrna/intern/rna_xr.c72
-rw-r--r--source/blender/windowmanager/CMakeLists.txt9
-rw-r--r--source/blender/windowmanager/WM_api.h4
-rw-r--r--source/blender/windowmanager/intern/wm.c4
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c18
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c2
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c36
-rw-r--r--source/blender/windowmanager/intern/wm_surface.c118
-rw-r--r--source/blender/windowmanager/intern/wm_window.c27
-rw-r--r--source/blender/windowmanager/intern/wm_xr.c484
-rw-r--r--source/blender/windowmanager/wm.h7
-rw-r--r--source/blender/windowmanager/wm_surface.h57
-rw-r--r--source/creator/CMakeLists.txt6
-rw-r--r--source/creator/creator_args.c24
-rw-r--r--tests/python/bl_load_addons.py3
-rw-r--r--tests/python/bl_load_py_modules.py3
46 files changed, 1218 insertions, 6 deletions
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 07d98475c00..611da8d6a44 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -377,6 +377,9 @@ if(WITH_XR_OPENXR)
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)
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index aafe374a93b..aba5b5f733b 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -759,6 +759,18 @@ extern GHOST_TSuccess GHOST_ReleaseOpenGLContext(GHOST_ContextHandle contexthand
/**
* 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.
+ */
extern unsigned int GHOST_GetDefaultOpenGLFramebuffer(GHOST_WindowHandle windwHandle);
/**
diff --git a/intern/ghost/GHOST_IContext.h b/intern/ghost/GHOST_IContext.h
index a341e18ca0a..33422b8e351 100644
--- a/intern/ghost/GHOST_IContext.h
+++ b/intern/ghost/GHOST_IContext.h
@@ -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
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index 60d20474a5a..d43a2637ad3 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -709,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;
diff --git a/intern/ghost/intern/GHOST_Context.h b/intern/ghost/intern/GHOST_Context.h
index bbf6d6a510d..0bd6f63d07e 100644
--- a/intern/ghost/intern/GHOST_Context.h
+++ b/intern/ghost/intern/GHOST_Context.h
@@ -120,6 +120,14 @@ class GHOST_Context : public GHOST_IContext {
}
/**
+ * 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.
*/
diff --git a/release/windows/batch/blender_oculus.cmd b/release/windows/batch/blender_oculus.cmd
new file mode 100644
index 00000000000..ffb725eb32f
--- /dev/null
+++ b/release/windows/batch/blender_oculus.cmd
@@ -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
diff --git a/release/windows/batch/oculus.json b/release/windows/batch/oculus.json
new file mode 100644
index 00000000000..ba8767f2c58
--- /dev/null
+++ b/release/windows/batch/oculus.json
@@ -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"
+ }
+}
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt
index b44b6db8804..203b6da272f 100644
--- a/source/blender/CMakeLists.txt
+++ b/source/blender/CMakeLists.txt
@@ -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)
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index fe050075bae..f6cae6d8a9c 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -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. */
};
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 923108240dd..047901b4c81 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -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")
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index 534c25f6e01..2049f368578 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -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,
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index d54ec0f1ac1..fa50bf202a8 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -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)
diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index 450d3fc2371..3963ddc179a 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -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
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index c3fba697bd3..cf708ef4f13 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -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);
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 87442a10d12..e561b4a07f7 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -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;
+ }
+ }
}
}
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index a1213be4be0..81f2214b402 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -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}")
diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h
index b4c23d5e57c..d4d015e53ac 100644
--- a/source/blender/draw/DRW_engine.h
+++ b/source/blender/draw/DRW_engine.h
@@ -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);
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index 71e2d8d9b57..8aeb90f5f36 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -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(). */
diff --git a/source/blender/editors/include/ED_view3d_offscreen.h b/source/blender/editors/include/ED_view3d_offscreen.h
index 3cbb13fa8b5..539a4d13ccd 100644
--- a/source/blender/editors/include/ED_view3d_offscreen.h
+++ b/source/blender/editors/include/ED_view3d_offscreen.h
@@ -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,
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index 4cf5ecfc4f0..dbbbbc2af54 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -1707,6 +1707,89 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
}
/**
+ * 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
*
* \param ofs: Optional off-screen buffer, can be NULL.
@@ -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;
diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c
index 8ccfa3d9898..941f5049dbf 100644
--- a/source/blender/gpu/intern/gpu_viewport.c
+++ b/source/blender/gpu/intern/gpu_viewport.c
@@ -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);
}
diff --git a/source/blender/makesdna/DNA_view3d_enums.h b/source/blender/makesdna/DNA_view3d_enums.h
index 85522ea88a2..f8c772422bb 100644
--- a/source/blender/makesdna/DNA_view3d_enums.h
+++ b/source/blender/makesdna/DNA_view3d_enums.h
@@ -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 */
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index 1eaf3777d0a..9226ff20ae6 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -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 */
diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h
new file mode 100644
index 00000000000..70927399a32
--- /dev/null
+++ b/source/blender/makesdna/DNA_xr_types.h
@@ -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__ */
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 6b4b4854515..a33f13be09a 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -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 */
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index fa8cf6903ad..4f05c1be7f4 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -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;
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index a813fe12c02..f810d81e124 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -92,6 +92,7 @@ set(DEFSRC
rna_wm_gizmo.c
rna_workspace.c
rna_world.c
+ rna_xr.c
)
set(APISRC
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index ae414489f52..7e8b888b82c 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -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},
};
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 6bda8ebdb4c..3e18e882e2b 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -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 */
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index 929540f10ac..a903833448c 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -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);
}
diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c
new file mode 100644
index 00000000000..7fdb77c3d0e
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_xr.c
@@ -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 */
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index e7a4ca9a005..a1b67216f1a 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -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}")
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 6781f055fc2..bb8b67da14e 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -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,
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index 2edef54c778..b2600ee0bf4 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.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);
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index 9ccff2a3e3d..1610bec59e7 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -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))
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index a87f0a3e42c..7b04b40e626 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -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();
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index d649b21569e..80116507c4a 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -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
diff --git a/source/blender/windowmanager/intern/wm_surface.c b/source/blender/windowmanager/intern/wm_surface.c
new file mode 100644
index 00000000000..cb575cc8f7d
--- /dev/null
+++ b/source/blender/windowmanager/intern/wm_surface.c
@@ -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));
+}
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index fe6272686bc..a6893300900 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -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
diff --git a/source/blender/windowmanager/intern/wm_xr.c b/source/blender/windowmanager/intern/wm_xr.c
new file mode 100644
index 00000000000..7600c0b350e
--- /dev/null
+++ b/source/blender/windowmanager/intern/wm_xr.c
@@ -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 */
diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h
index 5bb9de87e82..c8dc883d731 100644
--- a/source/blender/windowmanager/wm.h
+++ b/source/blender/windowmanager/wm.h
@@ -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__ */
diff --git a/source/blender/windowmanager/wm_surface.h b/source/blender/windowmanager/wm_surface.h
new file mode 100644
index 00000000000..98d67c55619
--- /dev/null
+++ b/source/blender/windowmanager/wm_surface.h
@@ -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__ */
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index c485caf2779..bbef3a4d52a 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -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 "."
)
diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c
index f265112570f..565cf60d2e1 100644
--- a/source/creator/creator_args.c
+++ b/source/creator/creator_args.c
@@ -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,
diff --git a/tests/python/bl_load_addons.py b/tests/python/bl_load_addons.py
index bb730bc362c..f0c2f3f7fdf 100644
--- a/tests/python/bl_load_addons.py
+++ b/tests/python/bl_load_addons.py
@@ -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__)
diff --git a/tests/python/bl_load_py_modules.py b/tests/python/bl_load_py_modules.py
index 2252af7a02f..5d1a5dd8ee0 100644
--- a/tests/python/bl_load_py_modules.py
+++ b/tests/python/bl_load_py_modules.py
@@ -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:])