[Bf-blender-cvs] [637b803b975] soc-2019-openxr: Support drawing the VR view into a DirectX window!

Julian Eisel noreply at git.blender.org
Sun Jun 16 21:48:09 CEST 2019


Commit: 637b803b975547ccec4b598975bc27d27b8e4ec6
Author: Julian Eisel
Date:   Sun Jun 16 20:07:58 2019 +0200
Branches: soc-2019-openxr
https://developer.blender.org/rB637b803b975547ccec4b598975bc27d27b8e4ec6

Support drawing the VR view into a DirectX window!

Phew! That a fight. But this is also a pretty important feature as it
allows interfacing with Windows Mixed Reality OpenXR runtime and the
HMDs supported by it.

Important remaining issue: The rendered viewport is upside down :) That
is of course because DirectX has the opposite vertical direction than
OpenGL.

When creating a DirectX window, we also create an OpenGL offscreen
context to use for all drawing. Just before swapping framebuffers, the
DirectX window blits the framebuffer of the offscreen context into its
swapchain. This requires the WGL_NV_DX_interop and WGL_NV_DX_interop2
extensions.

For testing/dev purposes, also adds:
* Version of the offscreen to onscreen blitting that's OpenGL only. So
  it blits the default framebuffer of the offscreen context into the one
  of the onscreen context.
  This is disabled by a #define.
* Code to draw a colored triangle using DirectX, also for testing.
  Requires the D3DCompiler.lib to be available at compile time. This is
  also disabled using a #define.

===================================================================

M	intern/ghost/GHOST_C-api.h
M	intern/ghost/GHOST_IContext.h
M	intern/ghost/intern/GHOST_C-api.cpp
M	intern/ghost/intern/GHOST_Context.cpp
M	intern/ghost/intern/GHOST_Context.h
M	intern/ghost/intern/GHOST_ContextCGL.mm
M	intern/ghost/intern/GHOST_ContextD3D.cpp
M	intern/ghost/intern/GHOST_ContextD3D.h
M	intern/ghost/intern/GHOST_ContextEGL.cpp
M	intern/ghost/intern/GHOST_ContextGLX.cpp
M	intern/ghost/intern/GHOST_ContextNone.h
M	intern/ghost/intern/GHOST_ContextSDL.cpp
M	intern/ghost/intern/GHOST_ContextWGL.cpp
M	intern/ghost/intern/GHOST_ContextWGL.h
M	intern/ghost/intern/GHOST_SystemWin32.cpp
M	intern/ghost/intern/GHOST_Window.cpp
M	source/blender/windowmanager/intern/wm_operators.c
M	source/blender/windowmanager/intern/wm_window.c

===================================================================

diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index f6fcc60a696..2ae94318ed8 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -682,6 +682,13 @@ extern GHOST_TSuccess GHOST_SetWindowOrder(GHOST_WindowHandle windowhandle,
  */
 extern GHOST_TSuccess GHOST_SwapWindowBuffers(GHOST_WindowHandle windowhandle);
 
+/**
+ * Swaps front and back buffers of a context.
+ * \param contexthandle The handle to the context
+ * \return A success indicator.
+ */
+extern GHOST_TSuccess GHOST_SwapContextBuffers(GHOST_ContextHandle contexthandle);
+
 /**
  * Sets the swap interval for swapBuffers.
  * \param interval The swap interval to use.
diff --git a/intern/ghost/GHOST_IContext.h b/intern/ghost/GHOST_IContext.h
index 5f213601e6b..b83cea95404 100644
--- a/intern/ghost/GHOST_IContext.h
+++ b/intern/ghost/GHOST_IContext.h
@@ -58,6 +58,8 @@ class GHOST_IContext {
 
   virtual unsigned int getDefaultFramebuffer() = 0;
 
+  virtual GHOST_TSuccess swapBuffers() = 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 ebb574288a3..1de5fee30c7 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -613,6 +613,13 @@ GHOST_TSuccess GHOST_SwapWindowBuffers(GHOST_WindowHandle windowhandle)
   return window->swapBuffers();
 }
 
+GHOST_TSuccess GHOST_SwapContextBuffers(GHOST_ContextHandle contexthandle)
+{
+  GHOST_IContext *context = (GHOST_IContext *)contexthandle;
+
+  return context->swapBuffers();
+}
+
 GHOST_TSuccess GHOST_SetSwapInterval(GHOST_WindowHandle windowhandle, int interval)
 {
   GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
diff --git a/intern/ghost/intern/GHOST_Context.cpp b/intern/ghost/intern/GHOST_Context.cpp
index 0d167209909..d9c1475b675 100644
--- a/intern/ghost/intern/GHOST_Context.cpp
+++ b/intern/ghost/intern/GHOST_Context.cpp
@@ -149,3 +149,65 @@ void GHOST_Context::initClearGL()
   glClear(GL_COLOR_BUFFER_BIT);
   glClearColor(0.000, 0.000, 0.000, 0.000);
 }
+
+GHOST_TSuccess GHOST_Context::blitOpenGLOffscreenContext(GHOST_Context *offscreen,
+                                                         GHOST_TInt32 width,
+                                                         GHOST_TInt32 height)
+{
+  GLuint fbo_offscreen;
+  GLuint fbo_onscreen;
+  GLuint render_buf_shared;
+
+  if ((m_type != GHOST_kDrawingContextTypeOpenGL) ||
+      (offscreen->m_type != GHOST_kDrawingContextTypeOpenGL)) {
+    return GHOST_kFailure;
+  }
+
+  offscreen->setDefaultFramebufferSize(width, height);
+
+  /* Logic here:
+   * We can't simply blit from one context's framebuffer into the other. Unlike Framebuffers/FBOs,
+   * Renderbuffers can be shared though. So create one, and blit the offscreen context framebuffer
+   * contents into it. To share it, an FBO has to be created for each context though, as the
+   * default framebuffer doesn't allow any attachments. */
+
+  offscreen->activateDrawingContext();
+
+  /* Create shared renderbuffer */
+  glGenRenderbuffers(1, &render_buf_shared);
+  glBindRenderbuffer(GL_RENDERBUFFER, render_buf_shared);
+  glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
+
+  /* Create offscreen FBO and assign renderbuffer */
+  glGenFramebuffers(1, &fbo_offscreen);
+  glBindFramebuffer(GL_FRAMEBUFFER, fbo_offscreen);
+  glFramebufferRenderbuffer(
+      GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, render_buf_shared);
+
+  /* Blit offscreen framebuffer into renderbuffer */
+  glBindFramebuffer(GL_READ_FRAMEBUFFER, offscreen->getDefaultFramebuffer());
+  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_offscreen);
+  glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+
+  glDeleteFramebuffers(1, &fbo_offscreen); /* Can delete already. Do before context change. */
+
+  activateDrawingContext();
+
+  /* Create onscreen FBO and assign renderbuffer */
+  glGenFramebuffers(1, &fbo_onscreen);
+  glBindFramebuffer(GL_FRAMEBUFFER, fbo_onscreen);
+  glFramebufferRenderbuffer(
+      GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, render_buf_shared);
+
+  glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_onscreen);
+  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, getDefaultFramebuffer());
+  /* Finally, blit to onscreen buffer. */
+  glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+
+  glDeleteFramebuffers(1, &fbo_onscreen);
+  glDeleteRenderbuffers(1, &render_buf_shared);
+
+  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+  return GHOST_kSuccess;
+}
diff --git a/intern/ghost/intern/GHOST_Context.h b/intern/ghost/intern/GHOST_Context.h
index 8858c6cb0b3..5127aed6c15 100644
--- a/intern/ghost/intern/GHOST_Context.h
+++ b/intern/ghost/intern/GHOST_Context.h
@@ -38,7 +38,8 @@ class GHOST_Context : public GHOST_IContext {
    * Constructor.
    * \param stereoVisual      Stereo visual for quad buffered stereo.
    */
-  GHOST_Context(bool stereoVisual) : m_stereoVisual(stereoVisual)
+  GHOST_Context(GHOST_TDrawingContextType type, bool stereoVisual)
+      : m_type(type), m_stereoVisual(stereoVisual)
   {
   }
 
@@ -128,15 +129,26 @@ class GHOST_Context : public GHOST_IContext {
     return 0;
   }
 
-  virtual GHOST_TSuccess blitOpenGLOffscreenContext(GHOST_Context *offscreen)
+  /**
+   * For offscreen rendering, we create an invisible window. So this can be used to update the
+   * offscreen buffer size by changing the size of this context's window.
+   *
+   * \note This actually changes the window size! That is the only way to change the default
+   *       framebuffer size. Better only use for offscreen contexts.
+   */
+  virtual GHOST_TSuccess setDefaultFramebufferSize(GHOST_TUns32 width, GHOST_TUns32 height)
   {
-    /* Not implemented. Could just implement as activateDrawingContext(offscreen)? */
     return GHOST_kFailure;
   }
 
+  virtual GHOST_TSuccess blitOpenGLOffscreenContext(GHOST_Context *offscreen,
+                                                    GHOST_TInt32 width,
+                                                    GHOST_TInt32 height);
+
  protected:
   void initContextGLEW();
 
+  GHOST_TDrawingContextType m_type;
   bool m_stereoVisual;
 
   static void initClearGL();
diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm
index 12c340ffe97..5448c85e1f5 100644
--- a/intern/ghost/intern/GHOST_ContextCGL.mm
+++ b/intern/ghost/intern/GHOST_ContextCGL.mm
@@ -55,7 +55,7 @@ GHOST_ContextCGL::GHOST_ContextCGL(bool stereoVisual,
                                    NSView *metalView,
                                    CAMetalLayer *metalLayer,
                                    NSOpenGLView *openGLView)
-    : GHOST_Context(stereoVisual),
+    : GHOST_Context(GHOST_kDrawingContextTypeOpenGL, stereoVisual),
       m_metalView(metalView),
       m_metalLayer(metalLayer),
       m_metalCmdQueue(nil),
diff --git a/intern/ghost/intern/GHOST_ContextD3D.cpp b/intern/ghost/intern/GHOST_ContextD3D.cpp
index 7b5e73088d0..2f3e28a28b2 100644
--- a/intern/ghost/intern/GHOST_ContextD3D.cpp
+++ b/intern/ghost/intern/GHOST_ContextD3D.cpp
@@ -18,18 +18,110 @@
  * \ingroup GHOST
  */
 
+#include <iostream>
+#include <string>
+
+#include <GL/glew.h>
+#include <GL/wglew.h>
+
+#include "GHOST_ContextWGL.h" /* For shared drawing */
 #include "GHOST_ContextD3D.h"
 
+// #define USE_DRAW_D3D_TEST_TRIANGLE
+
 HMODULE GHOST_ContextD3D::s_d3d_lib = NULL;
 PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN GHOST_ContextD3D::s_D3D11CreateDeviceAndSwapChainFn = NULL;
 
+#ifdef USE_DRAW_D3D_TEST_TRIANGLE
+static void drawTestTriangle(ID3D11Device *m_device,
+                             ID3D11DeviceContext *m_device_ctx,
+                             HWND hwnd,
+                             GHOST_TInt32 width,
+                             GHOST_TInt32 height);
+#endif
+
+class SharedOpenGLContext {
+  GHOST_ContextD3D *m_d3d_ctx;
+  GHOST_ContextWGL *m_wgl_ctx;
+  GLuint m_gl_render_buf;
+
+ public:
+  struct SharedData {
+    HANDLE device;
+    GLuint fbo;
+    HANDLE render_buf;
+  } m_shared;
+
+  SharedOpenGLContext(GHOST_ContextD3D *d3d_ctx, GHOST_ContextWGL *wgl_ctx)
+      : m_d3d_ctx(d3d_ctx), m_wgl_ctx(wgl_ctx)
+  {
+  }
+  ~SharedOpenGLContext()
+  {
+    wglDXUnregisterObjectNV(m_shared.device, m_shared.render_buf);
+    wglDXCloseDeviceNV(m_shared.device);
+    glDeleteFramebuffers(1, &m_shared.fbo);
+    glDeleteRenderbuffers(1, &m_gl_render_buf);
+  }
+
+  GHOST_TSuccess initialize()
+  {
+    ID3D11Resource *color_buf_res;
+    ID3D11Texture2D *color_buf_tex;
+
+    m_wgl_ctx->activateDrawingContext();
+
+    m_shared.device = wglDXOpenDeviceNV(m_d3d_ctx->m_device.Get());
+    if (m_shared.device == NULL) {
+      fprintf(stderr, "Error opening shared device using wglDXOpenDeviceNV()\n");
+      return GHOST_kFailure;
+    }
+
+    /* NV_DX_interop2 requires ID3D11Texture2D as backbuffer when sharing with GL_RENDERBUFFER */
+    m_d3d_ctx->m_backbuffer_view->GetResource(&color_buf_res);
+    color_buf_res->QueryInterface<ID3D11Texture2D>(&color_buf_tex);
+
+    /* Build the renderbuffer. */
+    glGenRenderbuffers(1, &m_gl_render_buf);
+    glBindRenderbuffer(GL_RENDERBUFFER, m_gl_render_buf);
+
+    m_shared.render_buf = wglDXRegisterObjectNV(m_shared.device,
+                                                color_buf_tex,
+                                                m_gl_render_buf,
+                                                GL_RENDERBUFFER,
+                                                WGL_ACCESS_READ_WRITE_NV);
+
+    /* Build the framebuffer */
+    glGenFramebuffers(1, &m_shared.fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, m_shared.fbo);
+    glFramebufferRenderbuffer(
+        GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_gl_render_buf);
+
+    return GHOST_kSuccess;
+  }
+  void update(int width, int height)
+  {
+    m_wgl_ctx->setDefaultFramebufferSize(width, height);
+  }
+
+  void beginGLOnly()
+  {
+    wglDXLockObjectsNV(m_shared.device, 1, &m_shared.render_buf);
+  }
+  void endGLOnly()
+  {
+    wglDXUnlockObjectsNV(m_shared.device, 1, &m_shared.render_b

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list