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:
Diffstat (limited to 'source/blender')
-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.c27
-rw-r--r--source/blender/blenloader/intern/readfile.c1
-rw-r--r--source/blender/draw/DRW_engine.h9
-rw-r--r--source/blender/draw/intern/draw_manager.c40
-rw-r--r--source/blender/editors/include/ED_view3d.h18
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c82
-rw-r--r--source/blender/gpu/CMakeLists.txt1
-rw-r--r--source/blender/gpu/intern/gpu_context.cpp10
-rw-r--r--source/blender/gpu/intern/gpu_context_private.h2
-rw-r--r--source/blender/gpu/intern/gpu_matrix.c91
-rw-r--r--source/blender/gpu/intern/gpu_matrix_private.h35
-rw-r--r--source/blender/gpu/intern/gpu_viewport.c3
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h1
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h6
-rw-r--r--source/blender/windowmanager/CMakeLists.txt7
-rw-r--r--source/blender/windowmanager/WM_api.h6
-rw-r--r--source/blender/windowmanager/intern/wm.c9
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c91
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c4
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c33
-rw-r--r--source/blender/windowmanager/intern/wm_surface.c128
-rw-r--r--source/blender/windowmanager/intern/wm_window.c247
-rw-r--r--source/blender/windowmanager/intern/wm_xr.c567
-rw-r--r--source/blender/windowmanager/wm.h11
-rw-r--r--source/blender/windowmanager/wm_surface.h57
-rw-r--r--source/blender/windowmanager/wm_window.h3
29 files changed, 1386 insertions, 116 deletions
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index bee76c09cbc..de875457b2e 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -151,6 +151,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 */
};
#define G_DEBUG_ALL \
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index d9bd87d97b5..5719fc0164b 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -613,6 +613,10 @@ if(WITH_OPENVDB)
endif()
endif()
+if(WITH_OPENXR)
+ add_definitions(-DWITH_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 d5485765844..c1618061b38 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -610,6 +610,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 7cdac6b1497..3a562778af7 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -4364,6 +4364,33 @@ 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_up = tanf(angle_up);
+ const float tan_angle_down = tanf(angle_down);
+ const float Xdelta = tan_angle_right - tan_angle_left;
+ const float Ydelta = tan_angle_up - tan_angle_down;
+
+ mat[0][0] = 2 / Xdelta;
+ mat[1][1] = 2 / Ydelta;
+ mat[2][0] = (tan_angle_right + tan_angle_left) / Xdelta;
+ mat[2][1] = (tan_angle_up + tan_angle_down) / Ydelta;
+ mat[2][2] = -(farClip + nearClip) / (farClip - nearClip);
+ mat[2][3] = -1;
+ mat[3][2] = -(farClip * (nearClip + nearClip)) / (farClip - nearClip);
+
+ mat[0][1] = mat[0][2] = mat[0][3] = mat[1][0] = mat[1][2] = mat[1][3] = mat[3][0] = mat[3][1] =
+ mat[3][3] = 0.0f;
+}
+
/* 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/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 44bb07d87f8..a73ca6e896c 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -7702,6 +7702,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/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h
index be04452f079..f9963fa67b7 100644
--- a/source/blender/draw/DRW_engine.h
+++ b/source/blender/draw/DRW_engine.h
@@ -159,6 +159,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_opengl_context_get(void);
+void *DRW_gpu_context_get(void);
+
/* For garbage collection */
void DRW_cache_free_old_batches(struct Main *bmain);
@@ -166,8 +171,12 @@ void DRW_cache_free_old_batches(struct Main *bmain);
void DRW_opengl_context_enable_ex(bool restore);
void DRW_opengl_context_disable_ex(bool restore);
+void DRW_opengl_render_context_enable_ex(void *re_gl_context);
void DRW_opengl_render_context_enable(void *re_gl_context);
+void DRW_opengl_render_context_disable_ex(void *re_gl_context);
void DRW_opengl_render_context_disable(void *re_gl_context);
+void DRW_render_context_draw_begin();
+void DRW_render_context_draw_end();
void DRW_gawain_render_context_enable(void *re_gpu_context);
void DRW_gawain_render_context_disable(void *re_gpu_context);
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index a6c671a631a..4b958a74bf9 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -524,7 +524,7 @@ const float *DRW_viewport_pixelsize_get(void)
return &DST.pixsize;
}
-static void drw_viewport_cache_resize(void)
+void drw_viewport_cache_resize(void)
{
/* Release the memiter before clearing the mempools that references them */
GPU_viewport_cache_release(DST.viewport);
@@ -3010,20 +3010,50 @@ void DRW_opengl_context_disable(void)
DRW_opengl_context_disable_ex(true);
}
-void DRW_opengl_render_context_enable(void *re_gl_context)
+void *DRW_opengl_context_get(void)
+{
+ return DST.gl_context;
+}
+
+void *DRW_gpu_context_get(void)
+{
+ return DST.gpu_context;
+}
+
+void DRW_opengl_render_context_enable_ex(void *re_gl_context)
{
/* If thread is main you should use DRW_opengl_context_enable(). */
BLI_assert(!BLI_thread_is_main());
- /* TODO get rid of the blocking. Only here because of the static global DST. */
- BLI_ticket_mutex_lock(DST.gl_context_mutex);
WM_opengl_context_activate(re_gl_context);
}
+void DRW_opengl_render_context_enable(void *re_gl_context)
+{
+ DRW_opengl_render_context_enable_ex(re_gl_context);
+ DRW_render_context_draw_begin();
+}
-void DRW_opengl_render_context_disable(void *re_gl_context)
+void DRW_opengl_render_context_disable_ex(void *re_gl_context)
{
GPU_flush();
WM_opengl_context_release(re_gl_context);
+}
+void DRW_opengl_render_context_disable(void *re_gl_context)
+{
+ DRW_opengl_render_context_disable_ex(re_gl_context);
+ DRW_render_context_draw_end();
+}
+
+void DRW_render_context_draw_begin()
+{
+ BLI_assert(!BLI_thread_is_main());
+ /* TODO get rid of the blocking. */
+ BLI_ticket_mutex_lock(DST.gl_context_mutex);
+}
+
+void DRW_render_context_draw_end()
+{
+ // GPU_flush();
/* TODO get rid of the blocking. */
BLI_ticket_mutex_unlock(DST.gl_context_mutex);
}
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index afdbbeedff4..204011fe46a 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -581,6 +581,24 @@ 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,
+ float lens,
+ bool do_sky,
+ bool is_persp,
+ const char *viewname,
+ const bool do_color_management,
+ struct GPUOffScreen *ofs,
+ struct GPUViewport *viewport);
void ED_view3d_draw_setup_view(struct wmWindow *win,
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 6ea4e832b58..31d00942e8c 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -1593,6 +1593,85 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
G.f &= ~G_FLAG_RENDER_VIEWPORT;
}
+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,
+ float lens,
+ 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}}};
+
+ /* connect data */
+ 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;
+ }
+ else {
+ v3d.flag2 = V3D_HIDE_OVERLAYS;
+ }
+
+ rv3d.persp = RV3D_PERSP;
+ v3d.clip_start = clip_start;
+ v3d.clip_end = clip_end;
+ v3d.lens = lens;
+
+ 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
*
@@ -1786,6 +1865,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/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index c620644a5f8..fb7d3c1ace8 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -116,6 +116,7 @@ set(SRC
intern/gpu_batch_private.h
intern/gpu_codegen.h
intern/gpu_context_private.h
+ intern/gpu_matrix_private.h
intern/gpu_primitive_private.h
intern/gpu_private.h
intern/gpu_select_private.h
diff --git a/source/blender/gpu/intern/gpu_context.cpp b/source/blender/gpu/intern/gpu_context.cpp
index a0e03e61d5d..12a5748b198 100644
--- a/source/blender/gpu/intern/gpu_context.cpp
+++ b/source/blender/gpu/intern/gpu_context.cpp
@@ -36,6 +36,7 @@
#include "gpu_batch_private.h"
#include "gpu_context_private.h"
+#include "gpu_matrix_private.h"
#include <vector>
#include <string.h>
@@ -71,6 +72,7 @@ struct GPUContext {
std::unordered_set<GPUFrameBuffer *>
framebuffers; /* Framebuffers that have FBO from this context */
#endif
+ struct GPUMatrixState *matrix_state;
std::vector<GLuint> orphaned_vertarray_ids;
std::vector<GLuint> orphaned_framebuffer_ids;
std::mutex orphans_mutex; /* todo: try spinlock instead */
@@ -144,6 +146,7 @@ GPUContext *GPU_context_create(GLuint default_framebuffer)
GPUContext *ctx = new GPUContext;
glGenVertexArrays(1, &ctx->default_vao);
ctx->default_framebuffer = default_framebuffer;
+ ctx->matrix_state = GPU_matrix_state_create();
GPU_context_active_set(ctx);
return ctx;
}
@@ -164,6 +167,7 @@ void GPU_context_discard(GPUContext *ctx)
/* this removes the array entry */
GPU_batch_vao_cache_clear(*ctx->batches.begin());
}
+ GPU_matrix_state_discard(ctx->matrix_state);
glDeleteVertexArrays(1, &ctx->default_vao);
delete ctx;
active_ctx = NULL;
@@ -338,3 +342,9 @@ GPUFrameBuffer *gpu_context_active_framebuffer_get(GPUContext *ctx)
{
return ctx->current_fbo;
}
+
+struct GPUMatrixState *gpu_context_active_matrix_state_get()
+{
+ BLI_assert(active_ctx);
+ return active_ctx->matrix_state;
+}
diff --git a/source/blender/gpu/intern/gpu_context_private.h b/source/blender/gpu/intern/gpu_context_private.h
index 6825b67d2c8..09248e45502 100644
--- a/source/blender/gpu/intern/gpu_context_private.h
+++ b/source/blender/gpu/intern/gpu_context_private.h
@@ -59,6 +59,8 @@ void gpu_context_remove_framebuffer(GPUContext *ctx, struct GPUFrameBuffer *fb);
void gpu_context_active_framebuffer_set(GPUContext *ctx, struct GPUFrameBuffer *fb);
struct GPUFrameBuffer *gpu_context_active_framebuffer_get(GPUContext *ctx);
+struct GPUMatrixState *gpu_context_active_matrix_state_get();
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/gpu/intern/gpu_matrix.c b/source/blender/gpu/intern/gpu_matrix.c
index 58ca800a92c..fb0dffb58d1 100644
--- a/source/blender/gpu/intern/gpu_matrix.c
+++ b/source/blender/gpu/intern/gpu_matrix.c
@@ -23,6 +23,9 @@
#include "GPU_shader_interface.h"
+#include "gpu_context_private.h"
+#include "gpu_matrix_private.h"
+
#define SUPPRESS_GENERIC_MATRIX_API
#define USE_GPU_PY_MATRIX_API /* only so values are declared */
#include "GPU_matrix.h"
@@ -32,6 +35,8 @@
#include "BLI_math_rotation.h"
#include "BLI_math_vector.h"
+#include "MEM_guardedalloc.h"
+
#define DEBUG_MATRIX_BIND 0
#define MATRIX_STACK_DEPTH 32
@@ -44,7 +49,7 @@ typedef struct MatrixStack {
uint top;
} MatrixStack;
-typedef struct {
+typedef struct GPUMatrixState {
MatrixStack model_view_stack;
MatrixStack projection_stack;
@@ -56,8 +61,16 @@ typedef struct {
* TODO: separate Model from View transform? Batches/objects have model,
* camera/eye has view & projection
*/
-} MatrixState;
+} GPUMatrixState;
+
+#define ModelViewStack gpu_context_active_matrix_state_get()->model_view_stack
+#define ModelView ModelViewStack.stack[ModelViewStack.top]
+#define ProjectionStack gpu_context_active_matrix_state_get()->projection_stack
+#define Projection ProjectionStack.stack[ProjectionStack.top]
+
+GPUMatrixState *GPU_matrix_state_create(void)
+{
#define MATRIX_4X4_IDENTITY \
{ \
{1.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f, 0.0f}, \
@@ -66,27 +79,36 @@ typedef struct {
} \
}
-static MatrixState state = {
- .model_view_stack = {{MATRIX_4X4_IDENTITY}, 0},
- .projection_stack = {{MATRIX_4X4_IDENTITY}, 0},
- .dirty = true,
-};
+ GPUMatrixState *state = MEM_mallocN(sizeof(*state), __func__);
+ const MatrixStack identity_stack = {{MATRIX_4X4_IDENTITY}, 0};
+
+ state->model_view_stack = state->projection_stack = identity_stack;
+ state->dirty = true;
#undef MATRIX_4X4_IDENTITY
-#define ModelViewStack state.model_view_stack
-#define ModelView ModelViewStack.stack[ModelViewStack.top]
+ return state;
+}
-#define ProjectionStack state.projection_stack
-#define Projection ProjectionStack.stack[ProjectionStack.top]
+void GPU_matrix_state_discard(GPUMatrixState *state)
+{
+ MEM_freeN(state);
+}
+
+static void gpu_matrix_state_active_set_dirty(bool value)
+{
+ GPUMatrixState *state = gpu_context_active_matrix_state_get();
+ state->dirty = value;
+}
void GPU_matrix_reset(void)
{
- state.model_view_stack.top = 0;
- state.projection_stack.top = 0;
+ GPUMatrixState *state = gpu_context_active_matrix_state_get();
+ state->model_view_stack.top = 0;
+ state->projection_stack.top = 0;
unit_m4(ModelView);
unit_m4(Projection);
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
#ifdef WITH_GPU_SAFETY
@@ -123,7 +145,7 @@ void GPU_matrix_pop(void)
{
BLI_assert(ModelViewStack.top > 0);
ModelViewStack.top--;
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
void GPU_matrix_push_projection(void)
@@ -137,34 +159,34 @@ void GPU_matrix_pop_projection(void)
{
BLI_assert(ProjectionStack.top > 0);
ProjectionStack.top--;
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
void GPU_matrix_set(const float m[4][4])
{
copy_m4_m4(ModelView, m);
CHECKMAT(ModelView3D);
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
void GPU_matrix_identity_projection_set(void)
{
unit_m4(Projection);
CHECKMAT(Projection3D);
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
void GPU_matrix_projection_set(const float m[4][4])
{
copy_m4_m4(Projection, m);
CHECKMAT(Projection3D);
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
void GPU_matrix_identity_set(void)
{
unit_m4(ModelView);
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
void GPU_matrix_translate_2f(float x, float y)
@@ -194,7 +216,7 @@ void GPU_matrix_translate_3f(float x, float y, float z)
m[3][2] = z;
GPU_matrix_mul(m);
#endif
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
void GPU_matrix_translate_3fv(const float vec[3])
@@ -243,7 +265,7 @@ void GPU_matrix_mul(const float m[4][4])
{
mul_m4_m4_post(ModelView, m);
CHECKMAT(ModelView);
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
void GPU_matrix_rotate_2d(float deg)
@@ -272,7 +294,7 @@ void GPU_matrix_rotate_axis(float deg, char axis)
/* rotate_m4 works in place */
rotate_m4(ModelView, axis, DEG2RADF(deg));
CHECKMAT(ModelView);
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
static void mat4_ortho_set(
@@ -298,7 +320,7 @@ static void mat4_ortho_set(
m[2][3] = 0.0f;
m[3][3] = 1.0f;
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
static void mat4_frustum_set(
@@ -324,7 +346,7 @@ static void mat4_frustum_set(
m[2][3] = -1.0f;
m[3][3] = 0.0f;
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
static void mat4_look_from_origin(float m[4][4], float lookdir[3], float camup[3])
@@ -389,14 +411,14 @@ static void mat4_look_from_origin(float m[4][4], float lookdir[3], float camup[3
m[2][3] = 0.0f;
m[3][3] = 1.0f;
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
void GPU_matrix_ortho_set(float left, float right, float bottom, float top, float near, float far)
{
mat4_ortho_set(Projection, left, right, bottom, top, near, far);
CHECKMAT(Projection);
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
void GPU_matrix_ortho_2d_set(float left, float right, float bottom, float top)
@@ -404,7 +426,7 @@ void GPU_matrix_ortho_2d_set(float left, float right, float bottom, float top)
Mat4 m;
mat4_ortho_set(m, left, right, bottom, top, -1.0f, 1.0f);
CHECKMAT(Projection2D);
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
void GPU_matrix_frustum_set(
@@ -412,7 +434,7 @@ void GPU_matrix_frustum_set(
{
mat4_frustum_set(Projection, left, right, bottom, top, near, far);
CHECKMAT(Projection);
- state.dirty = true;
+ gpu_matrix_state_active_set_dirty(true);
}
void GPU_matrix_perspective_set(float fovy, float aspect, float near, float far)
@@ -678,12 +700,13 @@ void GPU_matrix_bind(const GPUShaderInterface *shaderface)
glUniformMatrix4fv(P_inv->location, 1, GL_FALSE, (const float *)m);
}
- state.dirty = false;
+ gpu_matrix_state_active_set_dirty(false);
}
bool GPU_matrix_dirty_get(void)
{
- return state.dirty;
+ GPUMatrixState *state = gpu_context_active_matrix_state_get();
+ return state->dirty;
}
/* -------------------------------------------------------------------- */
@@ -695,12 +718,14 @@ BLI_STATIC_ASSERT(GPU_PY_MATRIX_STACK_LEN + 1 == MATRIX_STACK_DEPTH, "define mis
int GPU_matrix_stack_level_get_model_view(void)
{
- return (int)state.model_view_stack.top;
+ GPUMatrixState *state = gpu_context_active_matrix_state_get();
+ return (int)state->model_view_stack.top;
}
int GPU_matrix_stack_level_get_projection(void)
{
- return (int)state.projection_stack.top;
+ GPUMatrixState *state = gpu_context_active_matrix_state_get();
+ return (int)state->projection_stack.top;
}
/** \} */
diff --git a/source/blender/gpu/intern/gpu_matrix_private.h b/source/blender/gpu/intern/gpu_matrix_private.h
new file mode 100644
index 00000000000..862ef065481
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_matrix_private.h
@@ -0,0 +1,35 @@
+/*
+ * 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 gpu
+ */
+
+#ifndef __GPU_MATRIX_PRIVATE_H__
+#define __GPU_MATRIX_PRIVATE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct GPUMatrixState *GPU_matrix_state_create(void);
+void GPU_matrix_state_discard(struct GPUMatrixState *state);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GPU_MATRIX_PRIVATE_H__ */
diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c
index e3c13b0ec14..20e9b42ee05 100644
--- a/source/blender/gpu/intern/gpu_viewport.c
+++ b/source/blender/gpu/intern/gpu_viewport.c
@@ -467,8 +467,6 @@ void GPU_viewport_bind(GPUViewport *viewport, const rcti *rect)
int rect_w = BLI_rcti_size_x(rect) + 1;
int rect_h = BLI_rcti_size_y(rect) + 1;
- DRW_opengl_context_enable();
-
if (dfbl->default_fb) {
if (rect_w != viewport->size[0] || rect_h != viewport->size[1] ||
U.ogl_multisamples != viewport->samples) {
@@ -553,7 +551,6 @@ void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect)
void GPU_viewport_unbind(GPUViewport *UNUSED(viewport))
{
GPU_framebuffer_restore();
- DRW_opengl_context_disable();
}
GPUTexture *GPU_viewport_color_texture(GPUViewport *viewport)
diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h
index e7a4f9cbd4e..a22aeb4cedf 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -590,6 +590,7 @@ enum {
V3D_OFSDRAW_NONE = (0),
V3D_OFSDRAW_SHOW_ANNOTATION = (1 << 0),
V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS = (1 << 1),
+ V3D_OFSDRAW_SHOW_GRIDFLOOR = (1 << 2),
};
#define RV3D_CAMZOOM_MIN -30
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index 8dcae41aaa2..ca7ceb048ae 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -42,6 +42,7 @@ struct wmKeyMap;
struct wmMsgBus;
struct wmOperator;
struct wmOperatorType;
+struct GHOST_XrContext;
/* forwards */
struct PointerRNA;
@@ -178,6 +179,9 @@ typedef struct wmWindowManager {
struct wmMsgBus *message_bus;
+ //#ifdef WITH_OPENXR
+ void *xr_context; /* GHOST_XrContextHandle */
+ //#endif
} wmWindowManager;
/* wmWindowManager.initialized */
@@ -201,6 +205,8 @@ typedef struct wmWindow {
/** Don't want to include ghost.h stuff. */
void *ghostwin;
+ /** Ghost context for rendering the window offscreen (usually unused). */
+ void *offscreen_context;
/** Don't want to include gpu stuff. */
void *gpuctx;
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 64f506f03a8..f05ea317832 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -71,11 +71,13 @@ set(SRC
intern/wm_playanim.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
intern/wm_utils.c
intern/wm_window.c
+ intern/wm_xr.c
gizmo/intern/wm_gizmo.c
gizmo/intern/wm_gizmo_group.c
gizmo/intern/wm_gizmo_group_type.c
@@ -97,6 +99,7 @@ set(SRC
wm_event_system.h
wm_event_types.h
wm_files.h
+ wm_surface.h
wm_window.h
gizmo/WM_gizmo_api.h
gizmo/WM_gizmo_types.h
@@ -174,4 +177,8 @@ if(WITH_COMPOSITOR)
add_definitions(-DWITH_COMPOSITOR)
endif()
+if(WITH_OPENXR)
+ add_definitions(-DWITH_OPENXR)
+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 5d6e405dd5d..76ab06e0020 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -112,6 +112,7 @@ int WM_window_pixels_x(const struct wmWindow *win);
int WM_window_pixels_y(const struct wmWindow *win);
void WM_window_rect_calc(const struct wmWindow *win, struct rcti *r_rect);
void WM_window_screen_rect_calc(const struct wmWindow *win, struct rcti *r_rect);
+bool WM_window_is_non_opengl(const wmWindow *win);
bool WM_window_is_fullscreen(struct wmWindow *win);
void WM_windows_scene_data_sync(const ListBase *win_lb, struct Scene *scene) ATTR_NONNULL();
@@ -156,6 +157,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
/* defines for 'type' WM_window_open_temp */
enum {
@@ -168,6 +173,7 @@ enum {
struct wmWindow *WM_window_open(struct bContext *C, const struct rcti *rect);
struct wmWindow *WM_window_open_temp(
struct bContext *C, int x, int y, int sizex, int sizey, int type);
+struct wmWindow *WM_window_open_directx(struct bContext *C, const rcti *rect);
void WM_window_set_dpi(wmWindow *win);
bool WM_stereo3d_enabled(struct wmWindow *win, bool only_fullscreen_test);
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index 77e17ad4687..913b939bb87 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -52,6 +52,9 @@
#include "wm_event_system.h"
#include "wm_draw.h"
#include "wm.h"
+#ifdef WITH_OPENXR
+# include "GHOST_C-api.h"
+#endif
#include "ED_screen.h"
#include "BKE_undo_system.h"
@@ -374,6 +377,12 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
WM_msgbus_destroy(wm->message_bus);
}
+#ifdef WITH_OPENXR
+ if (wm->xr_context != NULL) {
+ GHOST_XrContextDestroy(wm->xr_context);
+ }
+#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 3bcb955c2b2..4024fc628f0 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -48,6 +48,8 @@
#include "BKE_scene.h"
#include "BKE_workspace.h"
+#include "DRW_engine.h"
+
#include "GHOST_C-api.h"
#include "ED_node.h"
@@ -72,6 +74,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"
@@ -315,7 +318,7 @@ static void wm_draw_region_buffer_free(ARegion *ar)
}
}
-static void wm_draw_offscreen_texture_parameters(GPUOffScreen *offscreen)
+void wm_draw_offscreen_texture_parameters(GPUOffScreen *offscreen)
{
/* Setup offscreen color texture for drawing. */
GPUTexture *texture = GPU_offscreen_color_texture(offscreen);
@@ -399,6 +402,7 @@ static void wm_draw_region_bind(ARegion *ar, int view)
}
if (ar->draw_buffer->viewport[view]) {
+ DRW_opengl_context_enable();
GPU_viewport_bind(ar->draw_buffer->viewport[view], &ar->winrct);
}
else {
@@ -423,6 +427,7 @@ static void wm_draw_region_unbind(ARegion *ar, int view)
if (ar->draw_buffer->viewport[view]) {
GPU_viewport_unbind(ar->draw_buffer->viewport[view]);
+ DRW_opengl_context_disable();
}
else {
glDisable(GL_SCISSOR_TEST);
@@ -747,6 +752,60 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
}
}
+void wm_draw_upside_down(int sizex, int sizey)
+{
+ /* Don't use imm here, this is called from a separate thread with no imm available. */
+
+ /* wmOrtho for the screen has this same offset */
+ const float halfx = GLA_PIXEL_OFS / sizex;
+ const float halfy = GLA_PIXEL_OFS / sizey;
+
+ GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR);
+ GPU_shader_bind(shader);
+
+ glUniform1i(GPU_shader_get_uniform_ensure(shader, "image"), 0);
+ glUniform4f(GPU_shader_get_uniform_ensure(shader, "rect_icon"),
+ halfx,
+ halfy,
+ 1.0f + halfx,
+ 1.0f + halfy);
+ glUniform4f(GPU_shader_get_uniform_ensure(shader, "rect_geom"), 0, sizey, sizex, 0);
+ glUniform4f(GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR), 1.0f, 1.0f, 1.0f, 1.0f);
+
+ GPU_draw_primitive(GPU_PRIM_TRI_STRIP, 4);
+}
+
+static void wm_draw_window_upside_down_onscreen(bContext *C, wmWindow *win)
+{
+ const int width = WM_window_pixels_x(win);
+ const int height = WM_window_pixels_y(win);
+ GPUOffScreen *offscreen = GPU_offscreen_create(width, height, 0, false, false, NULL);
+
+ /* Upside down rendering only implemented for non-stereo. Easy to add but makes code messy. */
+ BLI_assert(
+ !(WM_stereo3d_enabled(win, false) && GHOST_isUpsideDownWindow(win->offscreen_context)));
+
+ if (offscreen) {
+ GPUTexture *texture = GPU_offscreen_color_texture(offscreen);
+ wm_draw_offscreen_texture_parameters(offscreen);
+
+ /* Draw view into offscreen buffer. */
+ GPU_offscreen_bind(offscreen, false);
+ wm_draw_window_onscreen(C, win, -1);
+ GPU_offscreen_unbind(offscreen, false);
+
+ /* Draw offscreen buffer to screen. */
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture));
+
+ wm_draw_upside_down(win->sizex, win->sizey);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ GPU_offscreen_free(offscreen);
+ }
+}
+
static void wm_draw_window(bContext *C, wmWindow *win)
{
bScreen *screen = WM_window_get_active_screen(win);
@@ -758,8 +817,13 @@ static void wm_draw_window(bContext *C, wmWindow *win)
/* Now we draw into the window framebuffer, in full window coordinates. */
if (!stereo) {
- /* Regular mono drawing. */
- wm_draw_window_onscreen(C, win, -1);
+ if (GHOST_isUpsideDownWindow(win->ghostwin)) {
+ wm_draw_window_upside_down_onscreen(C, win);
+ }
+ else {
+ /* Regular mono drawing. */
+ wm_draw_window_onscreen(C, win, -1);
+ }
}
else if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
/* For pageflip we simply draw to both back buffers. */
@@ -817,6 +881,22 @@ 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);
+
+ // wm_surface_present(surface);
+
+ /* Avoid interference with window drawable */
+ // wm_surface_clear_drawable();
+}
+
/****************** main update call **********************/
/* quick test to prevent changing window drawable */
@@ -939,11 +1019,14 @@ void wm_draw_update(bContext *C)
wm_draw_window(C, win);
wm_draw_update_clear_window(win);
- wm_window_swap_buffers(win);
+ wm_window_present(win);
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(ar))
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 29cb02888ac..eb3f2125ee7 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -99,6 +99,7 @@
#include "wm_event_system.h"
#include "wm.h"
#include "wm_files.h"
+#include "wm_surface.h"
#include "wm_window.h"
#include "ED_anim_api.h"
@@ -129,6 +130,8 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
+#include "wm.h"
+
#include "DRW_engine.h"
#ifdef WITH_OPENSUBDIV
@@ -526,6 +529,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
BKE_keyconfig_pref_type_free();
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 c984191076c..38600dcb018 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -75,6 +75,7 @@
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h" /* BKE_ST_MAXNAME */
+#include "BKE_workspace.h"
#include "BKE_unit.h"
#include "BKE_idcode.h"
@@ -3514,6 +3515,35 @@ static void WM_OT_stereo3d_set(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
+#ifdef WITH_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(C, 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 =
+ "Attempt to open a view for use with virtual reality headsets, or close it if already "
+ "opened";
+
+ /* callbacks */
+ ot->exec = wm_xr_session_toggle_exec;
+}
+#endif /* WITH_OPENXR */
+
void wm_operatortypes_register(void)
{
WM_operatortype_append(WM_OT_window_close);
@@ -3551,6 +3581,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_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..38057370b92
--- /dev/null
+++ b/source/blender/windowmanager/intern/wm_surface.c
@@ -0,0 +1,128 @@
+/*
+ * 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 0
+ if (g_drawable) {
+ BLF_batch_reset();
+ gpu_batch_presets_reset();
+ immDeactivate();
+
+ g_drawable = NULL;
+ }
+#endif
+}
+
+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_present(wmSurface *surface)
+{
+ // GHOST_SwapContextBuffers(surface->ghost_ctx);
+ if (surface->secondary_ghost_ctx) {
+ // GHOST_SwapContextBuffers(surface->secondary_ghost_ctx);
+ }
+}
+
+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 d17b8817691..42c372855ca 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -95,6 +95,12 @@
# include "BLI_threads.h"
#endif
+/* We may want to open non-OpenGL windows in certain cases and do all drawing into an offscreen
+ * OpenGL context then. This flag is useful for testing the blitting. It forces OpenGL windows
+ * only, but still does the offscreen rendering and blitting (if supported by GHOST context).
+ * See GHOST_BlitOpenGLOffscreenContext. */
+// #define USE_FORCE_OPENGL_FOR_NON_OPENGL_WIN
+
/* the global to talk to ghost */
static GHOST_SystemHandle g_system = NULL;
@@ -157,7 +163,7 @@ void wm_get_desktopsize(int *r_width, int *r_height)
/* keeps offset and size within monitor bounds */
/* XXX solve dual screen... */
-static void wm_window_check_position(rcti *rect)
+void wm_window_check_position(rcti *rect)
{
int width, height, d;
@@ -190,6 +196,17 @@ static void wm_window_check_position(rcti *rect)
}
}
+static void wm_window_drawing_context_activate(wmWindow *win)
+{
+ if (win->offscreen_context) {
+ /* In rare cases we may want to draw to an offscreen context. */
+ GHOST_ActivateOpenGLContext(win->offscreen_context);
+ }
+ else {
+ GHOST_ActivateWindowDrawingContext(win->ghostwin);
+ }
+}
+
static void wm_ghostwindow_destroy(wmWindowManager *wm, wmWindow *win)
{
if (win->ghostwin) {
@@ -204,7 +221,7 @@ static void wm_ghostwindow_destroy(wmWindowManager *wm, wmWindow *win)
}
/* We need this window's opengl context active to discard it. */
- GHOST_ActivateWindowDrawingContext(win->ghostwin);
+ wm_window_drawing_context_activate(win);
GPU_context_active_set(win->gpuctx);
/* Delete local gpu context. */
@@ -550,7 +567,10 @@ static void wm_window_ensure_eventstate(wmWindow *win)
}
/* belongs to below */
-static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wmWindow *win)
+static void wm_window_ghostwindow_add(wmWindowManager *wm,
+ const char *title,
+ wmWindow *win,
+ GHOST_TDrawingContextType context_type)
{
GHOST_WindowHandle ghostwin;
GHOST_GLSettings glSettings = {0};
@@ -579,14 +599,29 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
win->sizex,
win->sizey,
(GHOST_TWindowState)win->windowstate,
+#ifdef USE_FORCE_OPENGL_FOR_NON_OPENGL_WIN
GHOST_kDrawingContextTypeOpenGL,
+#else
+ context_type,
+#endif
glSettings);
if (ghostwin) {
GHOST_RectangleHandle bounds;
+ GLuint default_fb;
- GLuint default_fb = GHOST_GetDefaultOpenGLFramebuffer(ghostwin);
- win->gpuctx = GPU_context_create(default_fb);
+ if (context_type == GHOST_kDrawingContextTypeOpenGL) {
+ default_fb = GHOST_GetDefaultOpenGLFramebuffer(ghostwin);
+ win->gpuctx = GPU_context_create(default_fb);
+ }
+ else {
+ /* Drawing into a non-OpenGL window -> create an offscreen OpenGL context to draw into. */
+ win->offscreen_context = WM_opengl_context_create();
+ WM_opengl_context_activate(win->offscreen_context);
+
+ default_fb = GHOST_GetContextDefaultOpenGLFramebuffer(win->offscreen_context);
+ win->gpuctx = GPU_context_create(default_fb);
+ }
/* needed so we can detect the graphics card below */
GPU_init();
@@ -626,7 +661,7 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
/* needed here, because it's used before it reads userdef */
WM_window_set_dpi(win);
- wm_window_swap_buffers(win);
+ wm_window_present(win);
// GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified);
@@ -638,23 +673,11 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
}
}
-/**
- * Initialize #wmWindow without ghostwin, open these and clear.
- *
- * window size is read from window, if 0 it uses prefsize
- * called in #WM_check, also inits stuff after file read.
- *
- * \warning
- * After running, 'win->ghostwin' can be NULL in rare cases
- * (where OpenGL driver fails to create a context for eg).
- * We could remove them with #wm_window_ghostwindows_remove_invalid
- * but better not since caller may continue to use.
- * Instead, caller needs to handle the error case and cleanup.
- */
-void wm_window_ghostwindows_ensure(wmWindowManager *wm)
+static void wm_window_ghostwindow_ensure(wmWindowManager *wm,
+ wmWindow *win,
+ GHOST_TDrawingContextType context_type)
{
wmKeyMap *keymap;
- wmWindow *win;
BLI_assert(G.background == false);
@@ -685,63 +708,83 @@ void wm_window_ghostwindows_ensure(wmWindowManager *wm)
#endif
}
- for (win = wm->windows.first; win; win = win->next) {
- if (win->ghostwin == NULL) {
- if ((win->sizex == 0) || (wm_init_state.override_flag & WIN_OVERRIDE_GEOM)) {
- win->posx = wm_init_state.start_x;
- win->posy = wm_init_state.start_y;
- win->sizex = wm_init_state.size_x;
- win->sizey = wm_init_state.size_y;
-
- if (wm_init_state.override_flag & WIN_OVERRIDE_GEOM) {
- win->windowstate = GHOST_kWindowStateNormal;
- wm_init_state.override_flag &= ~WIN_OVERRIDE_GEOM;
- }
- else {
- win->windowstate = GHOST_WINDOW_STATE_DEFAULT;
- }
- }
+ if (win->ghostwin == NULL) {
+ if ((win->sizex == 0) || (wm_init_state.override_flag & WIN_OVERRIDE_GEOM)) {
+ win->posx = wm_init_state.start_x;
+ win->posy = wm_init_state.start_y;
+ win->sizex = wm_init_state.size_x;
+ win->sizey = wm_init_state.size_y;
- if (wm_init_state.override_flag & WIN_OVERRIDE_WINSTATE) {
- win->windowstate = wm_init_state.windowstate;
- wm_init_state.override_flag &= ~WIN_OVERRIDE_WINSTATE;
+ if (wm_init_state.override_flag & WIN_OVERRIDE_GEOM) {
+ win->windowstate = GHOST_kWindowStateNormal;
+ wm_init_state.override_flag &= ~WIN_OVERRIDE_GEOM;
}
-
- /* without this, cursor restore may fail, T45456 */
- if (win->cursor == 0) {
- win->cursor = CURSOR_STD;
+ else {
+ win->windowstate = GHOST_WINDOW_STATE_DEFAULT;
}
-
- wm_window_ghostwindow_add(wm, "Blender", win);
}
- if (win->ghostwin != NULL) {
- /* If we have no ghostwin this is a buggy window that should be removed.
- * However we still need to initialize it correctly so the screen doesn't hang. */
+ if (wm_init_state.override_flag & WIN_OVERRIDE_WINSTATE) {
+ win->windowstate = wm_init_state.windowstate;
+ wm_init_state.override_flag &= ~WIN_OVERRIDE_WINSTATE;
+ }
- /* happens after fileread */
- wm_window_ensure_eventstate(win);
+ /* without this, cursor restore may fail, T45456 */
+ if (win->cursor == 0) {
+ win->cursor = CURSOR_STD;
}
- /* add keymap handlers (1 handler for all keys in map!) */
- keymap = WM_keymap_ensure(wm->defaultconf, "Window", 0, 0);
- WM_event_add_keymap_handler(&win->handlers, keymap);
+ wm_window_ghostwindow_add(wm, "Blender", win, context_type);
+ }
- keymap = WM_keymap_ensure(wm->defaultconf, "Screen", 0, 0);
- WM_event_add_keymap_handler(&win->handlers, keymap);
+ if (win->ghostwin != NULL) {
+ /* If we have no ghostwin this is a buggy window that should be removed.
+ * However we still need to initialize it correctly so the screen doesn't hang. */
- keymap = WM_keymap_ensure(wm->defaultconf, "Screen Editing", 0, 0);
- WM_event_add_keymap_handler(&win->modalhandlers, keymap);
+ /* happens after fileread */
+ wm_window_ensure_eventstate(win);
+ }
- /* add drop boxes */
- {
- ListBase *lb = WM_dropboxmap_find("Window", 0, 0);
- WM_event_add_dropbox_handler(&win->handlers, lb);
- }
- wm_window_title(wm, win);
+ /* add keymap handlers (1 handler for all keys in map!) */
+ keymap = WM_keymap_ensure(wm->defaultconf, "Window", 0, 0);
+ WM_event_add_keymap_handler(&win->handlers, keymap);
- /* add topbar */
- ED_screen_global_areas_refresh(win);
+ keymap = WM_keymap_ensure(wm->defaultconf, "Screen", 0, 0);
+ WM_event_add_keymap_handler(&win->handlers, keymap);
+
+ keymap = WM_keymap_ensure(wm->defaultconf, "Screen Editing", 0, 0);
+ WM_event_add_keymap_handler(&win->modalhandlers, keymap);
+
+ /* add drop boxes */
+ {
+ ListBase *lb = WM_dropboxmap_find("Window", 0, 0);
+ WM_event_add_dropbox_handler(&win->handlers, lb);
+ }
+ wm_window_title(wm, win);
+
+ /* add topbar */
+ ED_screen_global_areas_refresh(win);
+}
+
+/**
+ * Initialize #wmWindow without ghostwin, open these and clear.
+ *
+ * window size is read from window, if 0 it uses prefsize
+ * called in #WM_check, also inits stuff after file read.
+ *
+ * \warning
+ * After running, 'win->ghostwin' can be NULL in rare cases
+ * (where OpenGL driver fails to create a context for eg).
+ * We could remove them with #wm_window_ghostwindows_remove_invalid
+ * but better not since caller may continue to use.
+ * Instead, caller needs to handle the error case and cleanup.
+ */
+void wm_window_ghostwindows_ensure(wmWindowManager *wm)
+{
+ BLI_assert(G.background == false);
+
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ wm_window_ghostwindow_ensure(wm, win, GHOST_kDrawingContextTypeOpenGL);
}
}
@@ -769,8 +812,11 @@ void wm_window_ghostwindows_remove_invalid(bContext *C, wmWindowManager *wm)
* \note area-rip calls this.
* \return the window or NULL.
*/
-wmWindow *WM_window_open(bContext *C, const rcti *rect)
+static wmWindow *wm_window_open_ex(bContext *C,
+ const rcti *rect,
+ GHOST_TDrawingContextType context_type)
{
+ wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win_prev = CTX_wm_window(C);
wmWindow *win = wm_window_new(C, win_prev);
@@ -779,18 +825,31 @@ wmWindow *WM_window_open(bContext *C, const rcti *rect)
win->sizex = BLI_rcti_size_x(rect);
win->sizey = BLI_rcti_size_y(rect);
+ wm_window_ghostwindow_ensure(wm, win, context_type);
+
WM_check(C);
if (win->ghostwin) {
return win;
}
else {
- wm_window_close(C, CTX_wm_manager(C), win);
+ wm_window_close(C, wm, win);
CTX_wm_window_set(C, win_prev);
return NULL;
}
}
+wmWindow *WM_window_open(bContext *C, const rcti *rect)
+{
+ return wm_window_open_ex(C, rect, GHOST_kDrawingContextTypeOpenGL);
+}
+#ifdef WIN32
+wmWindow *WM_window_open_directx(bContext *C, const rcti *rect)
+{
+ return wm_window_open_ex(C, rect, GHOST_kDrawingContextTypeD3D);
+}
+#endif
+
/**
* Uses `screen->temp` tag to define what to do, currently it limits
* to only one "temp" window for render out, preferences, filewindow, etc...
@@ -1066,8 +1125,9 @@ static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool acti
wm->windrawable = win;
if (activate) {
- GHOST_ActivateWindowDrawingContext(win->ghostwin);
+ wm_window_drawing_context_activate(win);
}
+
GPU_context_active_set(win->gpuctx);
immActivate();
}
@@ -1606,6 +1666,9 @@ void wm_window_process_events(const bContext *C)
GHOST_DispatchEvents(g_system);
}
hasevent |= wm_window_timer(C);
+#ifdef WITH_OPENXR
+ hasevent |= GHOST_XrEventsHandle(CTX_wm_manager(C)->xr_context);
+#endif
/* no event, we sleep 5 milliseconds */
if (hasevent == 0) {
@@ -1926,8 +1989,22 @@ void wm_window_raise(wmWindow *win)
/** \name Window Buffers
* \{ */
-void wm_window_swap_buffers(wmWindow *win)
+/**
+ * \brief Push rendered buffer to the screen.
+ * In most cases, just swaps the buffer. However a window offscreen context may have been used to
+ * inject a layer of control in-between the OpenGL context and the window. We use this to support
+ * drawing with OpenGL into a DirectX window for the rare cases we need this (Windows Mixed
+ * Reality OpenXR runtime, which doesn't support OpenGL).
+ */
+void wm_window_present(wmWindow *win)
{
+ if (win->offscreen_context) {
+ /* The window may be a non-OpenGL window (unlikely though). In that case it's given the
+ * chance to blit the offscreen buffer to its onscreen context. Just a simple interop
+ * layer. */
+ GHOST_BlitOpenGLOffscreenContext(win->ghostwin, win->offscreen_context);
+ GHOST_SwapContextBuffers(win->offscreen_context);
+ }
GHOST_SwapWindowBuffers(win->ghostwin);
}
@@ -2179,6 +2256,15 @@ void WM_window_screen_rect_calc(const wmWindow *win, rcti *r_rect)
*r_rect = screen_rect;
}
+/**
+ * For some specific use cases (VR views for DirectX only OpenXR runtime) non-OpenGL windows are
+ * supported. These are treated really specially though.
+ */
+bool WM_window_is_non_opengl(const wmWindow *win)
+{
+ return GHOST_GetDrawingContextType(win->ghostwin) != GHOST_kDrawingContextTypeOpenGL;
+}
+
bool WM_window_is_fullscreen(wmWindow *win)
{
return win->windowstate == GHOST_kWindowStateFullScreen;
@@ -2427,3 +2513,24 @@ void WM_opengl_context_release(void *context)
}
/** \} */
+
+#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..e20bc39ba1f
--- /dev/null
+++ b/source/blender/windowmanager/intern/wm_xr.c
@@ -0,0 +1,567 @@
+/*
+ * 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 "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+#include "BKE_screen.h"
+
+#include "BLI_math_geom.h"
+#include "BLI_math_matrix.h"
+#include "BLI_threads.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_debug.h"
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_view3d_types.h"
+
+#include "DRW_engine.h"
+
+#include "ED_view3d.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"
+
+#ifdef WIN32 /* Only WIN32 supported now */
+//# define USE_FORCE_WINDOWED_SESSION
+#endif
+
+#ifdef USE_FORCE_WINDOWED_SESSION
+static void xr_session_window_create(bContext *C);
+#endif
+
+static wmSurface *g_xr_surface = NULL;
+static Depsgraph *g_depsgraph = NULL;
+ListBase g_threadpool;
+
+typedef struct {
+ GHOST_TXrGraphicsBinding gpu_binding_type;
+ GPUOffScreen *offscreen;
+ GPUViewport *viewport;
+} wmXrSurfaceData;
+
+typedef struct {
+ wmWindowManager *wm;
+ bContext *evil_C;
+} wmXrErrorHandlerData;
+
+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();
+ UI_popup_menu_reports(handler_data->evil_C, &wm->reports);
+
+ 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(bContext *C, wmWindowManager *wm)
+{
+ if (wm->xr_context) {
+ return true;
+ }
+ static wmXrErrorHandlerData error_customdata;
+
+ /* Set up error handling */
+ error_customdata.wm = wm;
+ error_customdata.evil_C = C;
+ 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;
+ }
+
+ wm->xr_context = GHOST_XrContextCreate(&create_info);
+ }
+
+ return wm->xr_context != NULL;
+}
+
+static void wm_xr_session_surface_draw(bContext *C)
+{
+#if 0
+ wmWindowManager *wm = CTX_wm_manager(C);
+
+ if (!GHOST_XrSessionIsRunning(wm->xr_context)) {
+ return;
+ }
+ GHOST_XrSessionDrawViews(wm->xr_context, C);
+#endif
+}
+
+static void wm_xr_session_free_data(wmSurface *surface)
+{
+ wmXrSurfaceData *data = surface->customdata;
+
+ if (surface->secondary_ghost_ctx) {
+#ifdef WIN32
+ if (data->gpu_binding_type == GHOST_kXrGraphicsD3D11) {
+ WM_directx_context_dispose(surface->secondary_ghost_ctx);
+ }
+#endif
+ }
+ WM_opengl_context_activate(surface->ghost_ctx);
+ GPU_context_active_set(surface->gpu_ctx);
+
+ if (data->viewport) {
+ GPU_viewport_clear_from_offscreen(data->viewport);
+ GPU_viewport_free(data->viewport);
+ }
+ if (data->offscreen) {
+ GPU_offscreen_free(data->offscreen);
+ }
+ GPU_context_discard(g_xr_surface->gpu_ctx);
+ GPU_context_active_set(NULL);
+ WM_opengl_context_release(surface->ghost_ctx);
+ WM_opengl_context_dispose(surface->ghost_ctx);
+
+ DEG_graph_free(g_depsgraph);
+
+ MEM_freeN(surface->customdata);
+
+ g_xr_surface = NULL;
+}
+
+static 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 = WM_opengl_context_create();
+ WM_opengl_context_activate(surface->ghost_ctx);
+
+ switch (gpu_binding_type) {
+ case GHOST_kXrGraphicsOpenGL:
+ break;
+#ifdef WIN32
+ case GHOST_kXrGraphicsD3D11:
+ surface->secondary_ghost_ctx = WM_directx_context_create();
+ break;
+#endif
+ }
+
+ WM_opengl_context_release(surface->ghost_ctx);
+ GPU_context_active_set(NULL);
+ wm_window_reset_drawable();
+
+ BLI_threadpool_end(&g_threadpool);
+
+ g_xr_surface = surface;
+
+ return surface;
+}
+
+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 bool wm_xr_session_surface_offscreen_ensure(const GHOST_XrDrawViewInfo *draw_view)
+{
+ wmXrSurfaceData *surface_data = g_xr_surface->customdata;
+ char err_out[256] = "unknown";
+ bool failure = false;
+
+ if (surface_data->offscreen &&
+ (GPU_offscreen_width(surface_data->offscreen) == draw_view->width) &&
+ (GPU_offscreen_height(surface_data->offscreen) == draw_view->height)) {
+ BLI_assert(surface_data->viewport);
+ return true;
+ }
+
+ if (surface_data->offscreen) {
+ GPU_viewport_clear_from_offscreen(surface_data->viewport);
+ 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 == false) &&
+ !(surface_data->viewport = GPU_viewport_create_from_offscreen(surface_data->offscreen))) {
+ GPU_offscreen_free(surface_data->offscreen);
+ failure = true;
+ }
+
+ if (failure) {
+ fprintf(stderr, "%s: failed to get buffer, %s\n", __func__, err_out);
+ return false;
+ }
+
+ return true;
+}
+
+static GHOST_ContextHandle wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
+{
+ bContext *C = customdata;
+ wmXrSurfaceData *surface_data = g_xr_surface->customdata;
+ const float clip_start = 0.01, clip_end = 500.0f;
+ const float lens = 50.0f; /* TODO get from OpenXR */
+ const rcti rect = {
+ .xmin = 0, .ymin = 0, .xmax = draw_view->width - 1, .ymax = draw_view->height - 1};
+
+ GPUOffScreen *offscreen;
+ GPUViewport *viewport;
+ View3DShading shading;
+ float viewmat[4][4], winmat[4][4];
+
+ wm_xr_draw_matrices_create(CTX_data_scene(C), draw_view, clip_start, clip_end, viewmat, winmat);
+
+ BKE_scene_graph_evaluated_ensure(g_depsgraph, CTX_data_main(C));
+
+ DRW_render_context_draw_begin();
+
+ if (!wm_xr_session_surface_offscreen_ensure(draw_view)) {
+ // TODO disable correctly.
+ return NULL;
+ }
+
+ offscreen = surface_data->offscreen;
+ viewport = surface_data->viewport;
+ GPU_viewport_bind(viewport, &rect);
+ glClear(GL_DEPTH_BUFFER_BIT);
+
+ BKE_screen_view3d_shading_init(&shading);
+ shading.flag |= V3D_SHADING_WORLD_ORIENTATION;
+ shading.background_type = V3D_SHADING_BACKGROUND_WORLD;
+ ED_view3d_draw_offscreen_simple(g_depsgraph,
+ DEG_get_evaluated_scene(g_depsgraph),
+ &shading,
+ OB_SOLID,
+ draw_view->width,
+ draw_view->height,
+ /* Draw floor for better orientation */
+ V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS | V3D_OFSDRAW_SHOW_GRIDFLOOR,
+ viewmat,
+ winmat,
+ clip_start,
+ clip_end,
+ lens,
+ true,
+ true,
+ NULL,
+ false,
+ offscreen,
+ viewport);
+
+ GPUTexture *texture = GPU_offscreen_color_texture(offscreen);
+
+ wm_draw_offscreen_texture_parameters(offscreen);
+
+ wmViewport(&rect);
+
+ if (g_xr_surface->secondary_ghost_ctx &&
+ GHOST_isUpsideDownContext(g_xr_surface->secondary_ghost_ctx)) {
+ GPU_texture_bind(texture, 0);
+ wm_draw_upside_down(draw_view->width, draw_view->height);
+ GPU_texture_unbind(texture);
+ }
+ else {
+ GPU_viewport_draw_to_screen(viewport, &rect);
+ }
+
+ GPU_viewport_unbind(viewport);
+
+ DRW_render_context_draw_end();
+
+ return g_xr_surface->ghost_ctx;
+}
+
+static void *wm_xr_session_gpu_binding_context_create(GHOST_TXrGraphicsBinding graphics_binding)
+{
+#ifndef USE_FORCE_WINDOWED_SESSION
+ wmSurface *surface = wm_xr_session_surface_create(G_MAIN->wm.first, graphics_binding);
+
+ wm_surface_add(surface);
+
+ return surface->secondary_ghost_ctx ? surface->secondary_ghost_ctx : surface->ghost_ctx;
+#else
+# ifdef WIN32
+ if (graphics_binding == GHOST_kXrGraphicsD3D11) {
+ wmWindowManager *wm = G_MAIN->wm.first;
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ /* TODO better lookup? For now only one D3D window possible, but later? */
+ if (GHOST_GetDrawingContextType(win->ghostwin) == GHOST_kDrawingContextTypeD3D) {
+ return GHOST_GetWindowContext(win->ghostwin);
+ }
+ }
+ }
+# endif
+ return NULL;
+#endif
+}
+
+static void wm_xr_session_gpu_binding_context_destroy(
+ GHOST_TXrGraphicsBinding UNUSED(graphics_lib), void *UNUSED(context))
+{
+#ifndef USE_FORCE_WINDOWED_SESSION
+ if (g_xr_surface) { /* Might have been freed already */
+ wm_surface_remove(g_xr_surface);
+ }
+#endif
+
+ wm_window_reset_drawable();
+}
+
+static Depsgraph *wm_xr_session_depsgraph_create(Main *bmain, Scene *scene, ViewLayer *viewlayer)
+{
+ Depsgraph *deg = DEG_graph_new(scene, viewlayer, DAG_EVAL_VIEWPORT);
+
+ DEG_debug_name_set(deg, "VR SESSION");
+ DEG_graph_build_from_view_layer(deg, bmain, scene, viewlayer);
+ BKE_scene_graph_evaluated_ensure(deg, bmain);
+ /* XXX Why do we have to call this? Depsgraph should handle. */
+ BKE_scene_base_flag_to_objects(DEG_get_evaluated_view_layer(deg));
+
+ return deg;
+}
+
+static void *wm_xr_session_drawthread_main(void *data)
+{
+ bContext *C = data;
+ wmWindowManager *wm = CTX_wm_manager(C);
+
+ g_depsgraph = wm_xr_session_depsgraph_create(
+ CTX_data_main(C), CTX_data_scene(C), CTX_data_view_layer(C));
+
+ WM_opengl_context_activate(g_xr_surface->ghost_ctx);
+ g_xr_surface->gpu_ctx = GPU_context_create(
+ GHOST_GetContextDefaultOpenGLFramebuffer(g_xr_surface->ghost_ctx));
+ WM_opengl_context_release(g_xr_surface->ghost_ctx);
+
+ DRW_opengl_render_context_enable_ex(g_xr_surface->ghost_ctx);
+ DRW_gawain_render_context_enable(g_xr_surface->gpu_ctx);
+
+ /* Sort of the session's main loop. */
+ while (GHOST_XrHasSession(wm->xr_context)) {
+ if (!GHOST_XrSessionShouldRunDrawLoop(wm->xr_context)) {
+ continue;
+ }
+
+ GHOST_XrSessionDrawViews(wm->xr_context, C);
+ }
+
+ DRW_gawain_render_context_disable(g_xr_surface->gpu_ctx);
+ DRW_opengl_render_context_disable_ex(g_xr_surface->ghost_ctx);
+
+ return NULL;
+}
+
+static void wm_xr_session_drawthread_spawn(bContext *C)
+{
+ BLI_threadpool_init(&g_threadpool, wm_xr_session_drawthread_main, 1);
+ BLI_threadpool_insert(&g_threadpool, C);
+}
+
+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);
+ /* TODO will only work if rotmode is euler */
+ 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_XrHasSession(xr_context)) {
+ GHOST_XrSessionEnd(xr_context);
+ }
+ else {
+ GHOST_XrSessionBeginInfo begin_info;
+
+#if defined(USE_FORCE_WINDOWED_SESSION)
+ xr_session_window_create(C);
+#endif
+ wm_xr_session_begin_info_create(CTX_data_scene(C), &begin_info);
+
+ GHOST_XrGraphicsContextBindFuncs(xr_context,
+ wm_xr_session_gpu_binding_context_create,
+ wm_xr_session_gpu_binding_context_destroy);
+ GHOST_XrDrawViewFunc(xr_context, wm_xr_draw_view);
+
+ GHOST_XrSessionStart(xr_context, &begin_info);
+ wm_xr_session_drawthread_spawn(C);
+ }
+}
+
+#if defined(USE_FORCE_WINDOWED_SESSION)
+
+# include "BLI_rect.h"
+# include "DNA_workspace_types.h"
+# include "BKE_workspace.h"
+# include "ED_screen.h"
+# include "BLI_string.h"
+
+static void xr_session_window_create(bContext *C)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ wmWindow *win_prev = CTX_wm_window(C);
+
+ rcti rect;
+ wmWindow *win;
+ bScreen *screen;
+ ScrArea *sa;
+ int screen_size[2];
+
+ wm_get_screensize(&screen_size[0], &screen_size[1]);
+ BLI_rcti_init(&rect, 0, screen_size[0], 0, screen_size[1]);
+ BLI_rcti_scale(&rect, 0.8f);
+ wm_window_check_position(&rect);
+
+ win = WM_window_open_directx(C, &rect);
+
+ if (WM_window_get_active_workspace(win) == NULL) {
+ WorkSpace *workspace = WM_window_get_active_workspace(win_prev);
+ BKE_workspace_active_set(win->workspace_hook, workspace);
+ }
+
+ /* add new screen layout */
+ WorkSpace *workspace = WM_window_get_active_workspace(win);
+ WorkSpaceLayout *layout = ED_workspace_layout_add(bmain, workspace, win, "VR Session");
+
+ screen = BKE_workspace_layout_screen_get(layout);
+ WM_window_set_active_layout(win, workspace, layout);
+
+ /* Set scene and view layer to match original window. */
+ STRNCPY(win->view_layer_name, view_layer->name);
+ if (WM_window_get_active_scene(win) != scene) {
+ ED_screen_scene_change(C, win, scene);
+ }
+
+ WM_check(C);
+
+ CTX_wm_window_set(C, win);
+ sa = screen->areabase.first;
+ CTX_wm_area_set(C, sa);
+ ED_area_newspace(C, sa, SPACE_VIEW3D, false);
+
+ ED_screen_change(C, screen);
+ ED_screen_refresh(CTX_wm_manager(C), win); /* test scale */
+
+ if (win->ghostwin) {
+ GHOST_SetTitle(win->ghostwin, "Blender VR Session View");
+ }
+ else {
+ /* very unlikely! but opening a new window can fail */
+ wm_window_close(C, CTX_wm_manager(C), win);
+ CTX_wm_window_set(C, win_prev);
+ }
+}
+#endif /* USE_FORCE_WINDOWED_SESSION */
diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h
index fa375efb469..f28552916f2 100644
--- a/source/blender/windowmanager/wm.h
+++ b/source/blender/windowmanager/wm.h
@@ -95,4 +95,15 @@ 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);
+/* wm_draw.c */
+struct GPUOffScreen;
+void wm_draw_offscreen_texture_parameters(struct GPUOffScreen *offscreen);
+void wm_draw_upside_down(int sizex, int sizey);
+
+#ifdef WITH_OPENXR
+/* wm_xr.c */
+bool wm_xr_context_ensure(bContext *C, 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..584e4c4f8b5
--- /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
+ */
+
+#ifndef __WM_SURFACE_H__
+#define __WM_SURFACE_H__
+
+struct bContext;
+
+typedef struct wmSurface {
+ struct wmSurface *next, *prev;
+
+ GHOST_ContextHandle ghost_ctx;
+ /** Some surfaces may want to use a secondary context. E.g. a DirectX one to draw OpenGL
+ * resources using DirectX (compatibility layer using shared resources). */
+ GHOST_ContextHandle secondary_ghost_ctx;
+ struct GPUContext *gpu_ctx;
+
+ void *customdata;
+
+ void (*draw)(struct bContext *);
+ /** Free data, 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);
+void wm_surface_present(wmSurface *surface);
+
+#endif /* __WM_SURFACE_H__ */
diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h
index 90c580818c9..4d21dbf22e7 100644
--- a/source/blender/windowmanager/wm_window.h
+++ b/source/blender/windowmanager/wm_window.h
@@ -61,8 +61,9 @@ void wm_window_reset_drawable(void);
void wm_window_raise(wmWindow *win);
void wm_window_lower(wmWindow *win);
void wm_window_set_size(wmWindow *win, int width, int height);
+void wm_window_check_position(rcti *rect);
void wm_window_get_position(wmWindow *win, int *r_pos_x, int *r_pos_y);
-void wm_window_swap_buffers(wmWindow *win);
+void wm_window_present(wmWindow *win);
void wm_window_set_swap_interval(wmWindow *win, int interval);
bool wm_window_get_swap_interval(wmWindow *win, int *intervalOut);