Make OpenGL-only session backend work
[blender.git] / intern / ghost / intern / GHOST_XrGraphicsBinding.cpp
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup GHOST
19  */
20
21 #include <algorithm>
22 #include <list>
23 #include <sstream>
24
25 #if defined(WITH_X11)
26 #  include "GHOST_ContextGLX.h"
27 #elif defined(WIN32)
28 #  include "GHOST_ContextWGL.h"
29 #  include "GHOST_ContextD3D.h"
30 #endif
31 #include "GHOST_C-api.h"
32 #include "GHOST_Xr_intern.h"
33
34 #include "GHOST_IXrGraphicsBinding.h"
35
36 static bool choose_swapchain_format_from_candidates(std::vector<int64_t> gpu_binding_formats,
37                                                     std::vector<int64_t> runtime_formats,
38                                                     int64_t *r_result)
39 {
40   if (gpu_binding_formats.empty()) {
41     return false;
42   }
43
44   auto res = std::find_first_of(gpu_binding_formats.begin(),
45                                 gpu_binding_formats.end(),
46                                 runtime_formats.begin(),
47                                 runtime_formats.end());
48   if (res == gpu_binding_formats.end()) {
49     return false;
50   }
51
52   *r_result = *res;
53   return true;
54 }
55
56 class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding {
57  public:
58   ~GHOST_XrGraphicsBindingOpenGL()
59   {
60     if (m_fbo != 0) {
61       glDeleteFramebuffers(1, &m_fbo);
62     }
63   }
64
65   bool checkVersionRequirements(GHOST_Context *ghost_ctx,
66                                 XrInstance instance,
67                                 XrSystemId system_id,
68                                 std::string *r_requirement_info) const override
69   {
70 #if defined(WITH_X11)
71     GHOST_ContextGLX *ctx_gl = static_cast<GHOST_ContextGLX *>(ghost_ctx);
72 #else
73     GHOST_ContextWGL *ctx_gl = static_cast<GHOST_ContextWGL *>(ghost_ctx);
74 #endif
75     XrGraphicsRequirementsOpenGLKHR gpu_requirements{XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR};
76     const uint32_t gl_version = XR_MAKE_VERSION(
77         ctx_gl->m_contextMajorVersion, ctx_gl->m_contextMinorVersion, 0);
78
79     xrGetOpenGLGraphicsRequirementsKHR(instance, system_id, &gpu_requirements);
80
81     if (r_requirement_info) {
82       std::ostringstream strstream;
83       strstream << "Min OpenGL version "
84                 << XR_VERSION_MAJOR(gpu_requirements.minApiVersionSupported) << "."
85                 << XR_VERSION_MINOR(gpu_requirements.minApiVersionSupported) << std::endl;
86       strstream << "Max OpenGL version "
87                 << XR_VERSION_MAJOR(gpu_requirements.maxApiVersionSupported) << "."
88                 << XR_VERSION_MINOR(gpu_requirements.maxApiVersionSupported) << std::endl;
89
90       *r_requirement_info = std::move(strstream.str());
91     }
92
93     return (gl_version >= gpu_requirements.minApiVersionSupported) &&
94            (gl_version <= gpu_requirements.maxApiVersionSupported);
95   }
96
97   void initFromGhostContext(GHOST_Context *ghost_ctx) override
98   {
99 #if defined(WITH_X11)
100     GHOST_ContextGLX *ctx_glx = static_cast<GHOST_ContextGLX *>(ghost_ctx);
101     XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx->m_display, ctx_glx->m_fbconfig);
102
103     oxr_binding.glx.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
104     oxr_binding.glx.xDisplay = ctx_glx->m_display;
105     oxr_binding.glx.glxFBConfig = ctx_glx->m_fbconfig;
106     oxr_binding.glx.glxDrawable = ctx_glx->m_window;
107     oxr_binding.glx.glxContext = ctx_glx->m_context;
108     oxr_binding.glx.visualid = visual_info->visualid;
109 #elif defined(WIN32)
110     GHOST_ContextWGL *ctx_wgl = static_cast<GHOST_ContextWGL *>(ghost_ctx);
111
112     oxr_binding.wgl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
113     oxr_binding.wgl.hDC = ctx_wgl->m_hDC;
114     oxr_binding.wgl.hGLRC = ctx_wgl->m_hGLRC;
115 #endif
116
117     /* Generate a framebuffer to use for blitting into the texture */
118     glGenFramebuffers(1, &m_fbo);
119   }
120
121   bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
122                              int64_t *r_result) const override
123   {
124     std::vector<int64_t> gpu_binding_formats = {GL_RGBA8};
125     return choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result);
126   }
127
128   std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
129   {
130     std::vector<XrSwapchainImageOpenGLKHR> ogl_images(image_count);
131     std::vector<XrSwapchainImageBaseHeader *> base_images;
132
133     // Need to return vector of base header pointers, so of a different type. Need to build a new
134     // list with this type, and keep the initial one alive.
135     for (XrSwapchainImageOpenGLKHR &image : ogl_images) {
136       image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
137       base_images.push_back(reinterpret_cast<XrSwapchainImageBaseHeader *>(&image));
138     }
139
140     // Keep alive.
141     m_image_cache.push_back(std::move(ogl_images));
142
143     return base_images;
144   }
145
146   void drawViewBegin(XrSwapchainImageBaseHeader *swapchain_image) override
147   {
148   }
149   void drawViewEnd(XrSwapchainImageBaseHeader *swapchain_image,
150                    const GHOST_XrDrawViewInfo *draw_info,
151                    GHOST_Context *ogl_ctx) override
152   {
153     XrSwapchainImageOpenGLKHR *ogl_swapchain_image = reinterpret_cast<XrSwapchainImageOpenGLKHR *>(
154         swapchain_image);
155
156     ogl_ctx->activateDrawingContext();
157     ogl_ctx->setDefaultFramebufferSize(draw_info->width, draw_info->height);
158
159     glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
160     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);
161
162     glFramebufferTexture2D(
163         GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ogl_swapchain_image->image, 0);
164
165     glBlitFramebuffer(draw_info->ofsx,
166                       draw_info->ofsy,
167                       draw_info->ofsx + draw_info->width,
168                       draw_info->ofsy + draw_info->height,
169                       draw_info->ofsx,
170                       draw_info->ofsy,
171                       draw_info->ofsx + draw_info->width,
172                       draw_info->ofsy + draw_info->height,
173                       GL_COLOR_BUFFER_BIT,
174                       GL_LINEAR);
175
176     glBindFramebuffer(GL_FRAMEBUFFER, 0);
177   }
178
179  private:
180   std::list<std::vector<XrSwapchainImageOpenGLKHR>> m_image_cache;
181   GLuint m_fbo{0};
182 };
183
184 #ifdef WIN32
185 class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
186  public:
187   bool checkVersionRequirements(GHOST_Context *ghost_ctx,
188                                 XrInstance instance,
189                                 XrSystemId system_id,
190                                 std::string *r_requirement_info) const override
191   {
192
193     GHOST_ContextD3D *ctx_dx = static_cast<GHOST_ContextD3D *>(ghost_ctx);
194     XrGraphicsRequirementsD3D11KHR gpu_requirements{XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR};
195
196     xrGetD3D11GraphicsRequirementsKHR(instance, system_id, &gpu_requirements);
197
198     if (r_requirement_info) {
199       std::ostringstream strstream;
200       strstream << "Min DirectX 11 Feature Level " << gpu_requirements.minFeatureLevel
201                 << std::endl;
202
203       *r_requirement_info = std::move(strstream.str());
204     }
205
206     return ctx_dx->m_device->GetFeatureLevel() >= gpu_requirements.minFeatureLevel;
207   }
208
209   void initFromGhostContext(GHOST_Context *ghost_ctx) override
210   {
211     GHOST_ContextD3D *ctx_d3d = static_cast<GHOST_ContextD3D *>(ghost_ctx);
212
213     oxr_binding.d3d11.type = XR_TYPE_GRAPHICS_BINDING_D3D11_KHR;
214     oxr_binding.d3d11.device = ctx_d3d->m_device;
215     m_ghost_ctx = ctx_d3d;
216   }
217
218   bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
219                              int64_t *r_result) const override
220   {
221     std::vector<int64_t> gpu_binding_formats = {DXGI_FORMAT_R8G8B8A8_UNORM};
222     return choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result);
223   }
224
225   std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(uint32_t image_count) override
226   {
227     std::vector<XrSwapchainImageD3D11KHR> d3d_images(image_count);
228     std::vector<XrSwapchainImageBaseHeader *> base_images;
229
230     // Need to return vector of base header pointers, so of a different type. Need to build a new
231     // list with this type, and keep the initial one alive.
232     for (XrSwapchainImageD3D11KHR &image : d3d_images) {
233       image.type = XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR;
234       base_images.push_back(reinterpret_cast<XrSwapchainImageBaseHeader *>(&image));
235     }
236
237     // Keep alive.
238     m_image_cache.push_back(std::move(d3d_images));
239
240     return base_images;
241   }
242
243   void drawViewBegin(XrSwapchainImageBaseHeader * /*swapchain_image*/) override
244   {
245   }
246   void drawViewEnd(XrSwapchainImageBaseHeader *swapchain_image,
247                    const GHOST_XrDrawViewInfo *draw_info,
248                    GHOST_Context *ogl_ctx) override
249   {
250     XrSwapchainImageD3D11KHR *d3d_swapchain_image = reinterpret_cast<XrSwapchainImageD3D11KHR *>(
251         swapchain_image);
252
253 #  if 0
254     /* Ideally we'd just create a render target view for the OpenXR swapchain image texture and
255      * blit from the OpenGL context into it. The NV_DX_interop extension doesn't want to work with
256      * this though. At least not with Optimus hardware. See:
257      * https://github.com/mpv-player/mpv/issues/2949#issuecomment-197262807.
258      * Note: Even if this worked, the blitting code only supports one shared resource by now, we'd
259      * need at least two (for each eye). We could also entirely re-register shared resources all
260      * the time. Also, the runtime might recreate the swapchain image so the shared resource would
261      * have to be re-registered then as well. */
262
263     ID3D11RenderTargetView *rtv;
264     CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D,
265                                             DXGI_FORMAT_R8G8B8A8_UNORM);
266
267     m_ghost_ctx->m_device->CreateRenderTargetView(d3d_swapchain_image->texture, &rtv_desc, &rtv);
268     m_ghost_ctx->blitOpenGLOffscreenContext(ogl_ctx, rtv, draw_info->width, draw_info->height);
269 #  else
270     ID3D11Resource *res;
271     ID3D11Texture2D *tex;
272
273     ogl_ctx->activateDrawingContext();
274     m_ghost_ctx->blitOpenGLOffscreenContext(ogl_ctx, draw_info->width, draw_info->height);
275
276     m_ghost_ctx->m_backbuffer_view->GetResource(&res);
277     res->QueryInterface<ID3D11Texture2D>(&tex);
278
279     m_ghost_ctx->m_device_ctx->OMSetRenderTargets(0, nullptr, nullptr);
280     m_ghost_ctx->m_device_ctx->CopyResource(d3d_swapchain_image->texture, tex);
281
282     res->Release();
283     tex->Release();
284 #  endif
285   }
286
287  private:
288   GHOST_ContextD3D *m_ghost_ctx;
289   std::list<std::vector<XrSwapchainImageD3D11KHR>> m_image_cache;
290 };
291 #endif  // WIN32
292
293 std::unique_ptr<GHOST_IXrGraphicsBinding> GHOST_XrGraphicsBindingCreateFromType(
294     GHOST_TXrGraphicsBinding type)
295 {
296   switch (type) {
297     case GHOST_kXrGraphicsOpenGL:
298       return std::unique_ptr<GHOST_XrGraphicsBindingOpenGL>(new GHOST_XrGraphicsBindingOpenGL());
299 #ifdef WIN32
300     case GHOST_kXrGraphicsD3D11:
301       return std::unique_ptr<GHOST_XrGraphicsBindingD3D>(new GHOST_XrGraphicsBindingD3D());
302 #endif
303     default:
304       return nullptr;
305   }
306 }