Initial HMD viewport rendering (DirectX only first)
authorJulian Eisel <eiseljulian@gmail.com>
Mon, 1 Jul 2019 13:57:32 +0000 (15:57 +0200)
committerJulian Eisel <eiseljulian@gmail.com>
Mon, 1 Jul 2019 13:57:32 +0000 (15:57 +0200)
Finally: This makes it possible to render a viewport to an HMD via
OpenXR. Pure OpenGL rendering will need some more tweaks to work.
To my great delight, performance is quite good for reasonably sized
scenes.

Had to do some hacks and marked some TODOs. Nothing too bad though.

Here are a couple of notes:
* Current initial pose is pretty useless, think it just looks downwards
  from world origin. Will change that soon.
* The rendered viewport has some issues: Too dark (bad lighting?), grid
  doesn't show up even though I told it to, lighting seems to change with
  view position/rotation, etc. Needs some polish.
* Ideally we'd just use the D3D11 Texture given to us via the OpenXR
  swapchain and blit the OpenGL framebuffer into that. However the
  NV_DX_interop extension fails doing this. Seems like this is a NVidia
  Optimus only issue, but I'm missing the hardware to confirm.
  So instead, we blit into the D3D11 back buffer first and then into the
  Texture.
* The draw-manager uses its own offscreen context so we have to get the
  render result from the draw-manager context to the VR session's
  context first. Luckily I've already added code to support blitting from
  one OpenGL context into another. But it requires blitting twice.
  Blitting should be very cheap, but still...
  Draw-manager could get a context to use passed instead.

20 files changed:
intern/ghost/GHOST_C-api.h
intern/ghost/GHOST_IContext.h
intern/ghost/intern/GHOST_C-api.cpp
intern/ghost/intern/GHOST_Context.cpp
intern/ghost/intern/GHOST_Context.h
intern/ghost/intern/GHOST_ContextD3D.cpp
intern/ghost/intern/GHOST_ContextD3D.h
intern/ghost/intern/GHOST_ContextWGL.cpp
intern/ghost/intern/GHOST_IXrGraphicsBinding.h
intern/ghost/intern/GHOST_SystemWin32.cpp
intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
intern/ghost/intern/GHOST_XrSession.cpp
source/blender/draw/DRW_engine.h
source/blender/draw/intern/draw_manager.c
source/blender/editors/space_view3d/view3d_draw.c
source/blender/makesdna/DNA_view3d_types.h
source/blender/windowmanager/intern/wm_draw.c
source/blender/windowmanager/intern/wm_surface.c
source/blender/windowmanager/intern/wm_xr.c
source/blender/windowmanager/wm_surface.h

index 3f3c25eca43b01e62365972a0c3a2c3ce1492ca6..d9f9cf93bd3ad0c6149e5da979288f3f459ad252 100644 (file)
@@ -215,6 +215,12 @@ GHOST_TSuccess GHOST_DisposeDirectXContext(GHOST_SystemHandle systemhandle,
 GHOST_TSuccess GHOST_BlitOpenGLOffscreenContext(GHOST_WindowHandle windowhandle,
                                                 GHOST_ContextHandle offscreen_contexthandle);
 
+extern GHOST_TSuccess GHOST_ContextBlitOpenGLOffscreenContext(
+    GHOST_ContextHandle onscreen_contexthandle,
+    GHOST_ContextHandle offscreen_contexthandle,
+    GHOST_TInt32 width,
+    GHOST_TInt32 height);
+
 extern GHOST_ContextHandle GHOST_GetWindowContext(GHOST_WindowHandle windowhandle);
 
 /**
@@ -1010,6 +1016,7 @@ typedef struct {
 } GHOST_XrContextCreateInfo;
 
 typedef struct {
+  int ofsx, ofsy;
   int width, height;
 
   struct {
@@ -1030,7 +1037,10 @@ void GHOST_XrContextDestroy(struct GHOST_XrContext *xr_context);
 typedef void *(*GHOST_XrGraphicsContextBindFn)(GHOST_TXrGraphicsBinding graphics_lib);
 typedef void (*GHOST_XrGraphicsContextUnbindFn)(GHOST_TXrGraphicsBinding graphics_lib,
                                                 void *graphics_context);
-typedef void (*GHOST_XrDrawViewFn)(const GHOST_XrDrawViewInfo *draw_view, void *customdata);
+/* XXX hacky: returns GHOST_ContextHandle so DirectX binding can get a handle to the OpenGL
+ * offscreen context. */
+typedef GHOST_ContextHandle (*GHOST_XrDrawViewFn)(const GHOST_XrDrawViewInfo *draw_view,
+                                                  void *customdata);
 
 void GHOST_XrGraphicsContextBindFuncs(struct GHOST_XrContext *xr_context,
                                       GHOST_XrGraphicsContextBindFn bind_fn,
index b83cea9540478e3ca254658876e462c66134beb4..cb6eb5fc76be846eff656502eb299752457afe7c 100644 (file)
@@ -56,6 +56,10 @@ class GHOST_IContext {
    */
   virtual GHOST_TSuccess releaseDrawingContext() = 0;
 
+  virtual GHOST_TSuccess blitOpenGLOffscreenContext(class GHOST_Context *offscreen,
+                                                    GHOST_TInt32 width,
+                                                    GHOST_TInt32 height) = 0;
+
   virtual unsigned int getDefaultFramebuffer() = 0;
 
   virtual GHOST_TSuccess swapBuffers() = 0;
index ddd364d63ec67d29748fffe9d9cc91fc8f24de8f..74e244e79e8acf4905e2691d020f7f0fbce350fd 100644 (file)
@@ -154,6 +154,17 @@ GHOST_TSuccess GHOST_BlitOpenGLOffscreenContext(GHOST_WindowHandle windowhandle,
   return window->blitOpenGLOffscreenContext((GHOST_IContext *)offscreen_contexthandle);
 }
 
+GHOST_TSuccess GHOST_ContextBlitOpenGLOffscreenContext(GHOST_ContextHandle onscreen_contexthandle,
+                                                       GHOST_ContextHandle offscreen_contexthandle,
+                                                       GHOST_TInt32 width,
+                                                       GHOST_TInt32 height)
+{
+  GHOST_IContext *context = (GHOST_IContext *)onscreen_contexthandle;
+
+  return context->blitOpenGLOffscreenContext(
+      (class GHOST_Context *)offscreen_contexthandle, width, height);
+}
+
 GHOST_ContextHandle GHOST_GetWindowContext(GHOST_WindowHandle windowhandle)
 {
   GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
index d9c1475b675d68966f717004486c550a8280a855..4df47006a56d077e6b32259ec1e4aa82f477c5c2 100644 (file)
@@ -157,6 +157,7 @@ GHOST_TSuccess GHOST_Context::blitOpenGLOffscreenContext(GHOST_Context *offscree
   GLuint fbo_offscreen;
   GLuint fbo_onscreen;
   GLuint render_buf_shared;
+  GLint fbo_prev_draw;
 
   if ((m_type != GHOST_kDrawingContextTypeOpenGL) ||
       (offscreen->m_type != GHOST_kDrawingContextTypeOpenGL)) {
@@ -173,6 +174,8 @@ GHOST_TSuccess GHOST_Context::blitOpenGLOffscreenContext(GHOST_Context *offscree
 
   offscreen->activateDrawingContext();
 
+  glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fbo_prev_draw);
+
   /* Create shared renderbuffer */
   glGenRenderbuffers(1, &render_buf_shared);
   glBindRenderbuffer(GL_RENDERBUFFER, render_buf_shared);
@@ -185,7 +188,7 @@ GHOST_TSuccess GHOST_Context::blitOpenGLOffscreenContext(GHOST_Context *offscree
       GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, render_buf_shared);
 
   /* Blit offscreen framebuffer into renderbuffer */
-  glBindFramebuffer(GL_READ_FRAMEBUFFER, offscreen->getDefaultFramebuffer());
+  glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_prev_draw);
   glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_offscreen);
   glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
 
index 16feb98d1b9668e1e86fc9c13de0fb7e66ebbe63..d7a720bb545dadaa979290d4a608889f18e06fa2 100644 (file)
@@ -146,9 +146,9 @@ class GHOST_Context : public GHOST_IContext {
     return GHOST_kFailure;
   }
 
-  virtual GHOST_TSuccess blitOpenGLOffscreenContext(GHOST_Context *offscreen,
-                                                    GHOST_TInt32 width,
-                                                    GHOST_TInt32 height);
+  GHOST_TSuccess blitOpenGLOffscreenContext(GHOST_Context *offscreen,
+                                            GHOST_TInt32 width,
+                                            GHOST_TInt32 height);
 
  protected:
   void initContextGLEW();
index b6c2a402e5f7aa0eb43f709d9df63b8b3172f89a..8e357a8070288820b08cc610bb4b595b866ed6fe 100644 (file)
@@ -41,55 +41,74 @@ static void drawTestTriangle(ID3D11Device *m_device,
 #endif
 
 class SharedOpenGLContext {
-  GHOST_ContextD3D *m_d3d_ctx;
+  ID3D11Device *m_d3d_device;
   GHOST_ContextWGL *m_wgl_ctx;
+  /* NV_DX_interop2 requires ID3D11Texture2D as backbuffer when sharing with GL_RENDERBUFFER */
+  ID3D11Texture2D *m_d3d_render_target;
   GLuint m_gl_render_buf;
 
  public:
   struct SharedData {
     HANDLE device;
     GLuint fbo;
-    HANDLE render_buf;
+    HANDLE render_buf{nullptr};
   } m_shared;
 
-  SharedOpenGLContext(GHOST_ContextD3D *d3d_ctx, GHOST_ContextWGL *wgl_ctx)
-      : m_d3d_ctx(d3d_ctx), m_wgl_ctx(wgl_ctx)
+  /* XXX Should have a map of render_target items to shared resource data (SharedData) to
+   * allow multiple shared surfaces in a context. Current code assumes a single one. That would be
+   * an issue if we wanted to use the OpenXR provided textures (changes for each eye and each
+   * redraw) and not the constant D3D swapchain texture like now. */
+  SharedOpenGLContext(ID3D11Device *d3d_device,
+                      GHOST_ContextWGL *wgl_ctx,
+                      ID3D11Texture2D *render_target)
+      : m_d3d_device(d3d_device), m_wgl_ctx(wgl_ctx), m_d3d_render_target(render_target)
   {
   }
   ~SharedOpenGLContext()
   {
-    wglDXUnregisterObjectNV(m_shared.device, m_shared.render_buf);
-    wglDXCloseDeviceNV(m_shared.device);
+    if (m_shared.render_buf) {
+      wglDXUnregisterObjectNV(m_shared.device, m_shared.render_buf);
+    }
+    if (m_shared.device) {
+      wglDXCloseDeviceNV(m_shared.device);
+    }
     glDeleteFramebuffers(1, &m_shared.fbo);
     glDeleteRenderbuffers(1, &m_gl_render_buf);
   }
 
-  GHOST_TSuccess initialize()
+  void reregisterSharedObject()
   {
-    ID3D11Resource *color_buf_res;
-    ID3D11Texture2D *color_buf_tex;
+    if (m_shared.render_buf) {
+      wglDXUnregisterObjectNV(m_shared.device, m_shared.render_buf);
+      m_shared.render_buf = nullptr;
+    }
+
+    m_shared.render_buf = wglDXRegisterObjectNV(m_shared.device,
+                                                m_d3d_render_target,
+                                                m_gl_render_buf,
+                                                GL_RENDERBUFFER,
+                                                WGL_ACCESS_READ_WRITE_NV);
+    if (!m_shared.render_buf) {
+      fprintf(stderr, "Error registering shared object using wglDXRegisterObjectNV()\n");
+      return;
+    }
+  }
 
+  GHOST_TSuccess initialize()
+  {
     m_wgl_ctx->activateDrawingContext();
 
-    m_shared.device = wglDXOpenDeviceNV(m_d3d_ctx->m_device);
+    m_shared.device = wglDXOpenDeviceNV(m_d3d_device);
     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);
+    reregisterSharedObject();
 
     /* Build the framebuffer */
     glGenFramebuffers(1, &m_shared.fbo);
@@ -102,6 +121,9 @@ class SharedOpenGLContext {
   void update(int width, int height)
   {
     m_wgl_ctx->setDefaultFramebufferSize(width, height);
+    m_wgl_ctx->activateDrawingContext();
+    /* TODO avoid re-registering if resource to share has not changed. */
+    reregisterSharedObject();
   }
 
   void beginGLOnly()
@@ -125,6 +147,7 @@ GHOST_ContextD3D::~GHOST_ContextD3D()
   m_swapchain->Release();
   m_backbuffer_view->Release();
   m_device->Release();
+  m_device_ctx->ClearState();
   m_device_ctx->Release();
 }
 
@@ -144,6 +167,74 @@ GHOST_TSuccess GHOST_ContextD3D::releaseDrawingContext()
   return GHOST_kFailure;
 }
 
+GHOST_TSuccess GHOST_ContextD3D::setDefaultFramebufferSize(GHOST_TUns32 width, GHOST_TUns32 height)
+{
+  RECT rect = {0, 0, (long)width, (long)height};
+  RECT winrect;
+
+  /* To use swapchain buffers/textures with custom size, the hidden window has to be resized. */
+
+  GetWindowRect(m_hWnd, &winrect);
+
+  WIN32_CHK(AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, false, 0));
+
+  width = rect.right - rect.left;
+  height = rect.bottom - rect.top;
+
+  if (((winrect.right - winrect.left) != width) || ((winrect.bottom - winrect.top) != height)) {
+    return SetWindowPos(m_hWnd,
+                        HWND_TOP,
+                        0,
+                        0,
+                        width,
+                        height,
+                        SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOMOVE | SWP_NOZORDER) ?
+               GHOST_kSuccess :
+               GHOST_kFailure;
+  }
+
+  return GHOST_kSuccess;
+}
+
+GHOST_TSuccess GHOST_ContextD3D::updateSwapchain(GHOST_TUns32 width, GHOST_TUns32 height)
+{
+  HRESULT hres;
+  DXGI_SWAP_CHAIN_DESC swapchain_desc;
+
+  m_swapchain->GetDesc(&swapchain_desc);
+
+  if ((swapchain_desc.BufferDesc.Width == width) && (swapchain_desc.BufferDesc.Height == height)) {
+    // Nothing to do.
+    return GHOST_kSuccess;
+  }
+
+#define CHECK_HRES \
+  if (hres != S_OK) { \
+    printf("Error updating swapchain (error code %x): %s line %i\n", hres, __FILE__, __LINE__); \
+  } \
+  (void)0
+
+  setDefaultFramebufferSize(width, height);
+
+  m_device_ctx->OMSetRenderTargets(0, nullptr, nullptr);
+
+  m_backbuffer_view->Release();
+  m_device_ctx->ClearState();
+
+  hres = m_swapchain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
+  CHECK_HRES;
+
+  ID3D11Texture2D *buf;
+  hres = m_swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void **)&buf);
+  CHECK_HRES;
+
+  hres = m_device->CreateRenderTargetView(buf, NULL, &m_backbuffer_view);
+  CHECK_HRES;
+  buf->Release();
+
+  return GHOST_kSuccess;
+}
+
 GHOST_TSuccess GHOST_ContextD3D::setupD3DLib()
 {
   if (s_d3d_lib == NULL) {
@@ -191,6 +282,7 @@ GHOST_TSuccess GHOST_ContextD3D::initializeDrawingContext()
   HRESULT hres = s_D3D11CreateDeviceAndSwapChainFn(NULL,
                                                    D3D_DRIVER_TYPE_HARDWARE,
                                                    NULL,
+                                                   // D3D11_CREATE_DEVICE_DEBUG,
                                                    0,
                                                    NULL,
                                                    0,
@@ -219,6 +311,7 @@ GHOST_TSuccess GHOST_ContextD3D::releaseNativeHandles()
 }
 
 GHOST_TSuccess GHOST_ContextD3D::blitOpenGLOffscreenContext(GHOST_Context *offscreen_ctx,
+                                                            ID3D11RenderTargetView *render_target,
                                                             GHOST_TInt32 width,
                                                             GHOST_TInt32 height)
 {
@@ -228,21 +321,35 @@ GHOST_TSuccess GHOST_ContextD3D::blitOpenGLOffscreenContext(GHOST_Context *offsc
             "available.");
     return GHOST_kFailure;
   }
-
-  // const float clear_col[] = {0.2f, 0.5f, 0.8f, 1.0f};
-  // m_device_ctx->ClearRenderTargetView(m_backbuffer_view, clear_col);
-  m_device_ctx->OMSetRenderTargets(1, &m_backbuffer_view, nullptr);
+  m_device_ctx->OMSetRenderTargets(1, &render_target, nullptr);
 
   offscreen_ctx->activateDrawingContext();
 
+  GLint fbo;
+  glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fbo);
+
   if (glshared == NULL) {
-    glshared = new SharedOpenGLContext(this, (GHOST_ContextWGL *)offscreen_ctx);
+    ID3D11Resource *backbuffer_res;
+    ID3D11Texture2D *backbuffer_tex;
+    render_target->GetResource(&backbuffer_res);
+    backbuffer_res->QueryInterface<ID3D11Texture2D>(&backbuffer_tex);
+
+    glshared = new SharedOpenGLContext(
+        m_device, (GHOST_ContextWGL *)offscreen_ctx, backbuffer_tex);
+    backbuffer_res->Release();
+    backbuffer_tex->Release();
+
     if (glshared->initialize() == GHOST_kFailure) {
       return GHOST_kFailure;
     }
   }
   SharedOpenGLContext::SharedData *shared = &glshared->m_shared;
 
+  glshared->update(width, height);
+
+  const float clear_col[] = {0.8f, 0.5f, 1.0f, 1.0f};
+  m_device_ctx->ClearRenderTargetView(render_target, clear_col);
+
   glshared->beginGLOnly();
 
   glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shared->fbo);
@@ -252,20 +359,15 @@ GHOST_TSuccess GHOST_ContextD3D::blitOpenGLOffscreenContext(GHOST_Context *offsc
     return GHOST_kFailure;
   }
 
-  glshared->update(width, height);
-
   /* Not needed, usefull for debugging. */
   initClearGL();
 
-  glViewport(0, 0, width, height);
-  glScissor(0, 0, width, height);
-
   /* No glBlitNamedFramebuffer, gotta be 3.3 compatible. */
-  glBindFramebuffer(GL_READ_FRAMEBUFFER, offscreen_ctx->getDefaultFramebuffer());
+  glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
   glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shared->fbo);
   glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
 
-  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+  glBindFramebuffer(GL_FRAMEBUFFER, fbo);
 
   glshared->endGLOnly();
 
@@ -276,6 +378,14 @@ GHOST_TSuccess GHOST_ContextD3D::blitOpenGLOffscreenContext(GHOST_Context *offsc
   return GHOST_kSuccess;
 }
 
+GHOST_TSuccess GHOST_ContextD3D::blitOpenGLOffscreenContext(GHOST_Context *offscreen_ctx,
+                                                            GHOST_TInt32 width,
+                                                            GHOST_TInt32 height)
+{
+  updateSwapchain(width, height);
+  return blitOpenGLOffscreenContext(offscreen_ctx, m_backbuffer_view, width, height);
+}
+
 #ifdef USE_DRAW_D3D_TEST_TRIANGLE
 #  pragma comment(lib, "D3DCompiler.lib")
 
index 430ac12abbd781704d3f599a1c40d92a71532a26..4fa58ed6fbe5127b705d0c38a21d56206d96edc0 100644 (file)
@@ -55,6 +55,8 @@ class GHOST_ContextD3D : public GHOST_Context {
    */
   GHOST_TSuccess releaseDrawingContext();
 
+  GHOST_TSuccess setDefaultFramebufferSize(GHOST_TUns32 width, GHOST_TUns32 height);
+
   /**
    * Call immediately after new to initialize.  If this fails then immediately delete the object.
    * \return Indication as to whether initialization has succeeded.
@@ -109,6 +111,10 @@ class GHOST_ContextD3D : public GHOST_Context {
   GHOST_TSuccess blitOpenGLOffscreenContext(GHOST_Context *offscreen_ctx,
                                             GHOST_TInt32 width,
                                             GHOST_TInt32 height);
+  GHOST_TSuccess blitOpenGLOffscreenContext(GHOST_Context *offscreen_ctx,
+                                            ID3D11RenderTargetView *render_target,
+                                            GHOST_TInt32 width,
+                                            GHOST_TInt32 height);
 
   bool isUpsideDown() const
   {
@@ -116,9 +122,8 @@ class GHOST_ContextD3D : public GHOST_Context {
   }
 
  private:
-  friend class SharedOpenGLContext;
-
   GHOST_TSuccess setupD3DLib();
+  GHOST_TSuccess updateSwapchain(GHOST_TUns32 width, GHOST_TUns32 height);
 
   static HMODULE s_d3d_lib;
   static PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN s_D3D11CreateDeviceAndSwapChainFn;
@@ -130,7 +135,7 @@ class GHOST_ContextD3D : public GHOST_Context {
   IDXGISwapChain *m_swapchain;
   ID3D11RenderTargetView *m_backbuffer_view;
 
-  SharedOpenGLContext *glshared{NULL};
+  class SharedOpenGLContext *glshared{nullptr};
 };
 
 #endif /* __GHOST_CONTEXTD3D_H__ */
index a3d5d6f7edfccd18f215f1fa03e7000da9f79b9d..c88b6d71609949fb88a1a5a64e221314b78fca1b 100644 (file)
@@ -145,9 +145,18 @@ GHOST_TSuccess GHOST_ContextWGL::releaseDrawingContext()
 
 GHOST_TSuccess GHOST_ContextWGL::setDefaultFramebufferSize(GHOST_TUns32 width, GHOST_TUns32 height)
 {
+  RECT rect = {0, 0, (long)width, (long)height};
   RECT winrect;
+
+  /* To get a default framebuffer with custom size, the hidden window has to be resized. */
+
   GetWindowRect(m_hWnd, &winrect);
 
+  WIN32_CHK(AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, false, 0));
+
+  width = rect.right - rect.left;
+  height = rect.bottom - rect.top;
+
   if (((winrect.right - winrect.left) != width) || ((winrect.bottom - winrect.top) != height)) {
     return SetWindowPos(m_hWnd,
                         HWND_TOP,
index eca2c70cb52d809a8bd53d88fde36dd0190d118b..6b1978bcf7dcb2594bff8ee779dcf3d49cf6a4ec 100644 (file)
@@ -45,7 +45,8 @@ class GHOST_IXrGraphicsBinding {
   virtual std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(
       uint32_t image_count) = 0;
   virtual void drawViewBegin(XrSwapchainImageBaseHeader *swapchain_image) = 0;
-  virtual void drawViewEnd(XrSwapchainImageBaseHeader *swapchain_image) = 0;
+  virtual void drawViewEnd(XrSwapchainImageBaseHeader *swapchain_image,
+                           class GHOST_Context *ogl_ctx) = 0;
 
  protected:
   /* Use GHOST_XrGraphicsBindingCreateFromType */
index 6e6c2ea219b231005e8720f3525250e34760e42f..cc6a0d8f3f1e80548021eb9ae764e36ebdbdcb14 100644 (file)
@@ -297,6 +297,29 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow(const STR_String &title,
   return window;
 }
 
+LRESULT WINAPI offscreen_s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+  LRESULT lResult = 0;
+  bool event_handled = false;
+
+  switch (msg) {
+    case WM_GETMINMAXINFO: {
+      ::DefWindowProc(hwnd, msg, wParam, lParam);
+      MINMAXINFO *mmi = (MINMAXINFO *)lParam;
+      mmi->ptMaxTrackSize.x = LONG_MAX;
+      mmi->ptMaxTrackSize.y = LONG_MAX;
+      event_handled = true;
+      break;
+    }
+  }
+
+  if (!event_handled) {
+    lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
+  }
+
+  return lResult;
+}
+
 /**
  * Create a new offscreen context.
  * Never explicitly delete the window, use #disposeContext() instead.
@@ -306,9 +329,30 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContext()
 {
   bool debug_context = false; /* TODO: inform as a parameter */
 
+  /* TODO Doesn't use STATIC class to allow offscreen context framebuffer bigger than screen size.
+   * For that, WM_GETMINMAXINFO event has to be listented to in custom WndProc callback. Need to
+   * verify this doesn't cause side effects, also, de-duplicate class definition.
+   */
   GHOST_Context *context;
+  WNDCLASSW wc = {0};
+  wc.style = CS_HREDRAW | CS_VREDRAW;
+  wc.lpfnWndProc = offscreen_s_wndProc;
+  wc.cbClsExtra = 0;
+  wc.cbWndExtra = 0;
+  wc.hInstance = ::GetModuleHandle(0);
+  wc.hCursor = ::LoadCursor(0, IDC_ARROW);
+  wc.hbrBackground =
+#ifdef INW32_COMPISITING
+      (HBRUSH)CreateSolidBrush
+#endif
+      (0x00000000);
+  wc.lpszMenuName = 0;
+  wc.lpszClassName = L"GHOST_OffscreenWindowClass";
+
+  // Use RegisterClassEx for setting small icon
+  ::RegisterClassW(&wc);
 
-  HWND wnd = CreateWindowA("STATIC",
+  HWND wnd = CreateWindowA("GHOST_OffscreenWindowClass",
                            "BlenderGLEW",
                            WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
                            0,
@@ -418,7 +462,29 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContextD3D()
   HDC prev_hdc = wglGetCurrentDC();
   HGLRC prev_context = wglGetCurrentContext();
 
-  HWND wnd = CreateWindowA("STATIC",
+  /* TODO Doesn't use STATIC class to allow offscreen context framebuffer bigger than screen size.
+   * For that, WM_GETMINMAXINFO event has to be listented to in custom WndProc callback. Need to
+   * verify this doesn't cause side effects, also, de-duplicate class definition.
+   */
+  WNDCLASSW wc = {0};
+  wc.style = CS_HREDRAW | CS_VREDRAW;
+  wc.lpfnWndProc = offscreen_s_wndProc;
+  wc.cbClsExtra = 0;
+  wc.cbWndExtra = 0;
+  wc.hInstance = ::GetModuleHandle(0);
+  wc.hCursor = ::LoadCursor(0, IDC_ARROW);
+  wc.hbrBackground =
+#ifdef INW32_COMPISITING
+      (HBRUSH)CreateSolidBrush
+#endif
+      (0x00000000);
+  wc.lpszMenuName = 0;
+  wc.lpszClassName = L"GHOST_OffscreenWindowClass";
+
+  // Use RegisterClassEx for setting small icon
+  ::RegisterClassW(&wc);
+
+  HWND wnd = CreateWindowA("GHOST_OffscreenWindowClass",
                            "BlenderD3D",
                            WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
                            0,
index 5a9a2b26959c8b8a153c5e511b9995bf8f84e4b1..aa047f92fd1a1f08fcfe884fbc6fb5196db1d356 100644 (file)
@@ -105,10 +105,11 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding {
     // TODO
     (void)swapchain_image;
   }
-  void drawViewEnd(XrSwapchainImageBaseHeader *swapchain_image) override
+  void drawViewEnd(XrSwapchainImageBaseHeader *swapchain_image, GHOST_Context *ogl_ctx) override
   {
     // TODO
     (void)swapchain_image;
+    (void)ogl_ctx;
   }
 
  private:
@@ -152,27 +153,52 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
     return base_images;
   }
 
-  void drawViewBegin(XrSwapchainImageBaseHeader *swapchain_image) override
+  void drawViewBegin(XrSwapchainImageBaseHeader * /*swapchain_image*/) override
+  {
+  }
+  void drawViewEnd(XrSwapchainImageBaseHeader *swapchain_image, GHOST_Context *ogl_ctx) override
   {
-    // Can't we simply use the backbuffer texture? Didn't work in initial test.
-
     XrSwapchainImageD3D11KHR *d3d_swapchain_image = reinterpret_cast<XrSwapchainImageD3D11KHR *>(
         swapchain_image);
-    const CD3D11_RENDER_TARGET_VIEW_DESC render_target_view_desc(D3D11_RTV_DIMENSION_TEXTURE2D,
-                                                                 DXGI_FORMAT_R8G8B8A8_UNORM);
-    ID3D11RenderTargetView *render_target_view;
-    m_ghost_ctx->m_device->CreateRenderTargetView(
-        d3d_swapchain_image->texture, &render_target_view_desc, &render_target_view);
 
-    const float clear_col[] = {0.2f, 0.5f, 0.8f, 1.0f};
-    m_ghost_ctx->m_device_ctx->ClearRenderTargetView(render_target_view, clear_col);
+#  if 0
+    /* Ideally we'd just create a render target view for the OpenXR swapchain image texture and
+     * blit from the OpenGL context into it. The NV_DX_interop extension doesn't want to work with
+     * this though. At least not with Optimus hardware. See:
+     * https://github.com/mpv-player/mpv/issues/2949#issuecomment-197262807.
+     * Note: Even if this worked, the blitting code only supports one shared resource by now, we'd
+     * need at least two (for each eye). We could also entirely re-register shared resources all
+     * the time. Also, the runtime might recreate the swapchain image so the shared resource would
+     * have to be re-registered then as well. */
 
-    render_target_view->Release();
-  }
-  void drawViewEnd(XrSwapchainImageBaseHeader *swapchain_image) override
-  {
-    // TODO
-    (void)swapchain_image;
+    ID3D11RenderTargetView *rtv;
+    CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D,
+                                            DXGI_FORMAT_R8G8B8A8_UNORM);
+    D3D11_TEXTURE2D_DESC tex_desc;
+
+    d3d_swapchain_image->texture->GetDesc(&tex_desc);
+
+    m_ghost_ctx->m_device->CreateRenderTargetView(d3d_swapchain_image->texture, &rtv_desc, &rtv);
+    m_ghost_ctx->blitOpenGLOffscreenContext(ogl_ctx, rtv, tex_desc.Width, tex_desc.Height);
+#  else
+    ID3D11Resource *res;
+    ID3D11Texture2D *tex;
+    D3D11_TEXTURE2D_DESC tex_desc;
+
+    d3d_swapchain_image->texture->GetDesc(&tex_desc);
+
+    ogl_ctx->activateDrawingContext();
+    m_ghost_ctx->blitOpenGLOffscreenContext(ogl_ctx, tex_desc.Width, tex_desc.Height);
+
+    m_ghost_ctx->m_backbuffer_view->GetResource(&res);
+    res->QueryInterface<ID3D11Texture2D>(&tex);
+
+    m_ghost_ctx->m_device_ctx->OMSetRenderTargets(0, nullptr, nullptr);
+    m_ghost_ctx->m_device_ctx->CopyResource(d3d_swapchain_image->texture, tex);
+
+    res->Release();
+    tex->Release();
+#  endif
   }
 
  private:
index 2ca2e38efd22312031c8f8dbb2dc584b7e226d42..ea26f83c91530a3ff9f4faf3aa6ffcfca0b79d79 100644 (file)
@@ -243,13 +243,14 @@ static void drawing_end(GHOST_XrContext *xr_context,
 
 static void ghost_xr_draw_view_info_from_view(const XrView &view, GHOST_XrDrawViewInfo &r_info)
 {
-  r_info.pose.position[0] = view.pose.position.x;
+  /* Set and convert to Blender coodinate space */
+  r_info.pose.position[0] = -view.pose.position.x;
   r_info.pose.position[1] = view.pose.position.y;
-  r_info.pose.position[2] = view.pose.position.z;
-  r_info.pose.quat[0] = view.pose.orientation.x;
-  r_info.pose.quat[1] = view.pose.orientation.y;
-  r_info.pose.quat[2] = view.pose.orientation.z;
-  r_info.pose.quat[3] = view.pose.orientation.w;
+  r_info.pose.position[2] = -view.pose.position.z;
+  r_info.pose.quat[0] = view.pose.orientation.w;
+  r_info.pose.quat[1] = view.pose.orientation.x;
+  r_info.pose.quat[2] = -view.pose.orientation.y;
+  r_info.pose.quat[3] = view.pose.orientation.z;
 
   r_info.fov.angle_left = view.fov.angleLeft;
   r_info.fov.angle_right = view.fov.angleRight;
@@ -269,6 +270,7 @@ static void draw_view(GHOST_XrContext *xr_context,
   XrSwapchainImageReleaseInfo release_info{XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO};
   XrSwapchainImageBaseHeader *swapchain_image;
   GHOST_XrDrawViewInfo draw_view_info{};
+  GHOST_ContextHandle draw_ctx;
   uint32_t swapchain_idx;
 
   xrAcquireSwapchainImage(swapchain, &acquire_info, &swapchain_idx);
@@ -285,13 +287,15 @@ static void draw_view(GHOST_XrContext *xr_context,
 
   swapchain_image = oxr->swapchain_images[swapchain][swapchain_idx];
 
-  draw_view_info.width = oxr->swapchain_image_width;
-  draw_view_info.height = oxr->swapchain_image_height;
+  draw_view_info.ofsx = proj_layer_view.subImage.imageRect.offset.x;
+  draw_view_info.ofsy = proj_layer_view.subImage.imageRect.offset.y;
+  draw_view_info.width = proj_layer_view.subImage.imageRect.extent.width;
+  draw_view_info.height = proj_layer_view.subImage.imageRect.extent.height;
   ghost_xr_draw_view_info_from_view(view, draw_view_info);
 
   xr_context->gpu_binding->drawViewBegin(swapchain_image);
-  xr_context->draw_view_fn(&draw_view_info, draw_customdata);
-  xr_context->gpu_binding->drawViewEnd(swapchain_image);
+  draw_ctx = xr_context->draw_view_fn(&draw_view_info, draw_customdata);
+  xr_context->gpu_binding->drawViewEnd(swapchain_image, (GHOST_Context *)draw_ctx);
 
   xrReleaseSwapchainImage(swapchain, &release_info);
 }
index 5919e100dddab0b10ef9d71f161c3931ebf5020d..ba76ae449ec2edd82a8ffb576b111684d7b1d3c8 100644 (file)
@@ -164,6 +164,8 @@ void DRW_opengl_context_destroy(void);
 void DRW_opengl_context_enable(void);
 void DRW_opengl_context_disable(void);
 
+void *DRW_opengl_context_get(void);
+
 /* For garbage collection */
 void DRW_cache_free_old_batches(struct Main *bmain);
 
index c4e3bd5cf916f0acbc3e96742d21da8032c4964a..d608593add2d6d1834d2d1d7e938d44c76c0df75 100644 (file)
@@ -3224,6 +3224,11 @@ void DRW_opengl_context_disable(void)
   DRW_opengl_context_disable_ex(true);
 }
 
+void *DRW_opengl_context_get(void)
+{
+  return DST.gl_context;
+}
+
 void DRW_opengl_render_context_enable(void *re_gl_context)
 {
   /* If thread is main you should use DRW_opengl_context_enable(). */
index add6d33cdd6acce5d4a05176c7e2558313aa65ba..cbf31dc3ce1047f211fdab25253bd51caa6b9977 100644 (file)
@@ -1634,6 +1634,9 @@ void ED_view3d_draw_offscreen_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;
 
@@ -1852,6 +1855,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;
 
index 31ffb3efebd0ac991451c8728b46f7a11a2f4a0e..9fcd920aed5c69d4493a4dec5e161e11322458ef 100644 (file)
@@ -586,6 +586,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
index cd4a906f34f3dfcbccfc7ad3d0c510b80108d85f..f57b6f6dc6df2f3d8b7c2d25cf57a07ce26595ea 100644 (file)
@@ -875,6 +875,8 @@ static void wm_draw_surface(bContext *C, wmSurface *surface)
 
   surface->draw(C);
 
+  wm_surface_present(surface);
+
   /* Avoid interference with window drawable */
   wm_surface_clear_drawable();
 }
index cb575cc8f7d9619753f4beb00ff80d538b01b77f..8b3a5e55beb53e09c08403bb285c7f870971b019 100644 (file)
@@ -95,6 +95,14 @@ void wm_surface_reset_drawable(void)
   }
 }
 
+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);
index a4ba130f9512b7deca0ff3ee012631864f5a59d8..0ed802bbb50fe420e5c3465f3d3b48c9c53e988e 100644 (file)
 #include "BKE_context.h"
 #include "BKE_global.h"
 #include "BKE_main.h"
+#include "BKE_screen.h"
 
 #include "BLI_math_geom.h"
 #include "BLI_math_matrix.h"
 
 #include "DNA_object_types.h"
+#include "DNA_view3d_types.h"
 
 #include "DRW_engine.h"
 
@@ -148,45 +150,59 @@ static wmSurface *wm_xr_session_surface_create(wmWindowManager *wm, unsigned int
   return surface;
 }
 
-static void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
+static void wm_xr_draw_matrices_create(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])
+{
+  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);
+
+  ED_view3d_to_m4(r_view_mat, draw_view->pose.position, draw_view->pose.quat, 0.0f);
+  invert_m4(r_view_mat);
+}
+
+static GHOST_ContextHandle wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
 {
   bContext *C = customdata;
-  const float ofs[] = {3.0f, 3.0f, 3.0f};
   const float clip_start = 0.01, clip_end = 500.0f;
   const float lens = 50.0f; /* TODO get from OpenXR */
 
   GPUOffScreen *offscreen;
   GPUViewport *viewport;
+  View3DShading shading;
   float viewmat[4][4], winmat[4][4];
   char err_out[256] = "unknown";
 
-  perspective_m4_fov(winmat,
-                     draw_view->fov.angle_left,
-                     draw_view->fov.angle_right,
-                     draw_view->fov.angle_up,
-                     draw_view->fov.angle_down,
-                     clip_start,
-                     clip_end);
-  ED_view3d_to_m4(viewmat, ofs, draw_view->pose.quat, 0.0f);
-  invert_m4(viewmat);
+  wm_xr_draw_matrices_create(draw_view, clip_start, clip_end, viewmat, winmat);
 
   DRW_opengl_context_enable();
   offscreen = GPU_offscreen_create(draw_view->width, draw_view->height, 0, true, false, err_out);
   if (offscreen == NULL) {
     fprintf(stderr, "%s: failed to get buffer, %s\n", __func__, err_out);
     DRW_opengl_context_disable();
-    return;
+    return NULL;
   }
   GPU_offscreen_bind(offscreen, true);
   viewport = GPU_viewport_create_from_offscreen(offscreen);
 
+  glViewport(draw_view->ofsx, draw_view->ofsy, draw_view->width, draw_view->height);
+
+  BKE_screen_view3d_shading_init(&shading);
   ED_view3d_draw_offscreen_simple(CTX_data_depsgraph(C),
                                   CTX_data_scene(C),
-                                  NULL,
+                                  &shading,
                                   OB_SOLID,
                                   draw_view->width,
                                   draw_view->height,
-                                  0,
+                                  /* Draw floor for better orientation */
+                                  V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS | V3D_OFSDRAW_SHOW_GRIDFLOOR,
                                   viewmat,
                                   winmat,
                                   clip_start,
@@ -198,14 +214,21 @@ static void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customd
                                   false,
                                   offscreen,
                                   viewport);
-
   GPU_viewport_clear_from_offscreen(viewport);
   GPU_viewport_free(viewport);
 
+  /* Blit from the DRW context into the offscreen surface context. Would be good to avoid this.
+   * Idea: Allow passing custom offscreen context to DRW? */
+  GHOST_ContextBlitOpenGLOffscreenContext(
+      g_xr_surface->ghost_ctx, DRW_opengl_context_get(), draw_view->width, draw_view->height);
+
   GPU_offscreen_unbind(offscreen, true);
+  GPU_framebuffer_restore();
+  DRW_opengl_context_disable_ex(true);
+
   GPU_offscreen_free(offscreen);
 
-  DRW_opengl_context_disable();
+  return g_xr_surface->ghost_ctx;
 }
 
 static void *wm_xr_session_gpu_binding_context_create(GHOST_TXrGraphicsBinding graphics_binding)
index 438663f02599f218f4f37b8c9555693b2ece8309..584e4c4f8b5682a0bc21cde0160976e0cfbb1559 100644 (file)
@@ -52,5 +52,6 @@ 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__ */