1 /*
2  * This file is part of mpv.
3  *
4  * mpv is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * mpv is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <windows.h>
19 #include <versionhelpers.h>
20 #include <d3d9.h>
21 #include <dwmapi.h>
22 #include "osdep/windows_utils.h"
23 #include "video/out/w32_common.h"
24 #include "context.h"
25 #include "utils.h"
26 
27 // For WGL_ACCESS_WRITE_DISCARD_NV, etc.
28 #include <GL/wglext.h>
29 
30 EXTERN_C IMAGE_DOS_HEADER __ImageBase;
31 #define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
32 
33 // mingw-w64 header typo?
34 #ifndef IDirect3DSwapChain9Ex_GetBackBuffer
35 #define IDirect3DSwapChain9Ex_GetBackBuffer IDirect3DSwapChain9EX_GetBackBuffer
36 #endif
37 
38 struct priv {
39     GL gl;
40 
41     HMODULE d3d9_dll;
42     HRESULT (WINAPI *Direct3DCreate9Ex)(UINT SDKVersion, IDirect3D9Ex **ppD3D);
43 
44     // Direct3D9 device and resources
45     IDirect3D9Ex *d3d9ex;
46     IDirect3DDevice9Ex *device;
47     HANDLE device_h;
48     IDirect3DSwapChain9Ex *swapchain;
49     IDirect3DSurface9 *backbuffer;
50     IDirect3DSurface9 *rtarget;
51     HANDLE rtarget_h;
52 
53     // OpenGL offscreen context
54     HWND os_wnd;
55     HDC os_dc;
56     HGLRC os_ctx;
57 
58     // OpenGL resources
59     GLuint texture;
60     GLuint main_fb;
61 
62     // Did we lose the device?
63     bool lost_device;
64 
65     // Requested and current parameters
66     int requested_swapinterval;
67     int width, height, swapinterval;
68 };
69 
70 static __thread struct ra_ctx *current_ctx;
71 
pump_message_loop(void)72 static void pump_message_loop(void)
73 {
74     // We have a hidden window on this thread (for the OpenGL context,) so pump
75     // its message loop at regular intervals to be safe
76     MSG message;
77     while (PeekMessageW(&message, NULL, 0, 0, PM_REMOVE))
78         DispatchMessageW(&message);
79 }
80 
w32gpa(const GLubyte * procName)81 static void *w32gpa(const GLubyte *procName)
82 {
83     HMODULE oglmod;
84     void *res = wglGetProcAddress(procName);
85     if (res)
86         return res;
87     oglmod = GetModuleHandleW(L"opengl32.dll");
88     return GetProcAddress(oglmod, procName);
89 }
90 
os_ctx_create(struct ra_ctx * ctx)91 static int os_ctx_create(struct ra_ctx *ctx)
92 {
93     static const wchar_t os_wnd_class[] = L"mpv offscreen gl";
94     struct priv *p = ctx->priv;
95     GL *gl = &p->gl;
96     HGLRC legacy_context = NULL;
97 
98     RegisterClassExW(&(WNDCLASSEXW) {
99         .cbSize = sizeof(WNDCLASSEXW),
100         .style = CS_OWNDC,
101         .lpfnWndProc = DefWindowProc,
102         .hInstance = HINST_THISCOMPONENT,
103         .lpszClassName = os_wnd_class,
104     });
105 
106     // Create a hidden window for an offscreen OpenGL context. It might also be
107     // possible to use the VO window, but MSDN recommends against drawing to
108     // the same window with flip mode present and other APIs, so play it safe.
109     p->os_wnd = CreateWindowExW(0, os_wnd_class, os_wnd_class, 0, 0, 0, 200,
110         200, NULL, NULL, HINST_THISCOMPONENT, NULL);
111     p->os_dc = GetDC(p->os_wnd);
112     if (!p->os_dc) {
113         MP_FATAL(ctx->vo, "Couldn't create window for offscreen rendering\n");
114         goto fail;
115     }
116 
117     // Choose a pixel format. It probably doesn't matter what this is because
118     // the primary framebuffer will not be used.
119     PIXELFORMATDESCRIPTOR pfd = {
120         .nSize = sizeof pfd,
121         .nVersion = 1,
122         .dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
123         .iPixelType = PFD_TYPE_RGBA,
124         .cColorBits = 24,
125         .iLayerType = PFD_MAIN_PLANE,
126     };
127     int pf = ChoosePixelFormat(p->os_dc, &pfd);
128     if (!pf) {
129         MP_FATAL(ctx->vo,
130                  "Couldn't choose pixelformat for offscreen rendering: %s\n",
131                  mp_LastError_to_str());
132         goto fail;
133     }
134     SetPixelFormat(p->os_dc, pf, &pfd);
135 
136     legacy_context = wglCreateContext(p->os_dc);
137     if (!legacy_context || !wglMakeCurrent(p->os_dc, legacy_context)) {
138         MP_FATAL(ctx->vo, "Couldn't create OpenGL context for offscreen rendering: %s\n",
139                  mp_LastError_to_str());
140         goto fail;
141     }
142 
143     const char *(GLAPIENTRY *wglGetExtensionsStringARB)(HDC hdc)
144         = w32gpa((const GLubyte*)"wglGetExtensionsStringARB");
145     if (!wglGetExtensionsStringARB) {
146         MP_FATAL(ctx->vo, "The OpenGL driver does not support OpenGL 3.x\n");
147         goto fail;
148     }
149 
150     const char *wgl_exts = wglGetExtensionsStringARB(p->os_dc);
151     if (!strstr(wgl_exts, "WGL_ARB_create_context")) {
152         MP_FATAL(ctx->vo, "The OpenGL driver does not support OpenGL 3.x\n");
153         goto fail;
154     }
155 
156     HGLRC (GLAPIENTRY *wglCreateContextAttribsARB)(HDC hDC, HGLRC hShareContext,
157                                                    const int *attribList)
158         = w32gpa((const GLubyte*)"wglCreateContextAttribsARB");
159     if (!wglCreateContextAttribsARB) {
160         MP_FATAL(ctx->vo, "The OpenGL driver does not support OpenGL 3.x\n");
161         goto fail;
162     }
163 
164     int attribs[] = {
165         WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
166         WGL_CONTEXT_MINOR_VERSION_ARB, 0,
167         WGL_CONTEXT_FLAGS_ARB, 0,
168         WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
169         0
170     };
171 
172     p->os_ctx = wglCreateContextAttribsARB(p->os_dc, 0, attribs);
173     if (!p->os_ctx) {
174         // NVidia, instead of ignoring WGL_CONTEXT_FLAGS_ARB, will error out if
175         // it's present on pre-3.2 contexts.
176         // Remove it from attribs and retry the context creation.
177         attribs[6] = attribs[7] = 0;
178         p->os_ctx = wglCreateContextAttribsARB(p->os_dc, 0, attribs);
179     }
180     if (!p->os_ctx) {
181         MP_FATAL(ctx->vo,
182                  "Couldn't create OpenGL 3.x context for offscreen rendering: %s\n",
183                  mp_LastError_to_str());
184         goto fail;
185     }
186 
187     wglMakeCurrent(p->os_dc, NULL);
188     wglDeleteContext(legacy_context);
189     legacy_context = NULL;
190 
191     if (!wglMakeCurrent(p->os_dc, p->os_ctx)) {
192         MP_FATAL(ctx->vo,
193                  "Couldn't activate OpenGL 3.x context for offscreen rendering: %s\n",
194                  mp_LastError_to_str());
195         goto fail;
196     }
197 
198     mpgl_load_functions(gl, w32gpa, wgl_exts, ctx->vo->log);
199     if (!(gl->mpgl_caps & MPGL_CAP_DXINTEROP)) {
200         MP_FATAL(ctx->vo, "WGL_NV_DX_interop is not supported\n");
201         goto fail;
202     }
203 
204     return 0;
205 fail:
206     if (legacy_context) {
207         wglMakeCurrent(p->os_dc, NULL);
208         wglDeleteContext(legacy_context);
209     }
210     return -1;
211 }
212 
os_ctx_destroy(struct ra_ctx * ctx)213 static void os_ctx_destroy(struct ra_ctx *ctx)
214 {
215     struct priv *p = ctx->priv;
216 
217     if (p->os_ctx) {
218         wglMakeCurrent(p->os_dc, NULL);
219         wglDeleteContext(p->os_ctx);
220     }
221     if (p->os_dc)
222         ReleaseDC(p->os_wnd, p->os_dc);
223     if (p->os_wnd)
224         DestroyWindow(p->os_wnd);
225 }
226 
d3d_size_dependent_create(struct ra_ctx * ctx)227 static int d3d_size_dependent_create(struct ra_ctx *ctx)
228 {
229     struct priv *p = ctx->priv;
230     GL *gl = &p->gl;
231     HRESULT hr;
232 
233     IDirect3DSwapChain9 *sw9;
234     hr = IDirect3DDevice9Ex_GetSwapChain(p->device, 0, &sw9);
235     if (FAILED(hr)) {
236         MP_ERR(ctx->vo, "Couldn't get swap chain: %s\n", mp_HRESULT_to_str(hr));
237         return -1;
238     }
239 
240     hr = IDirect3DSwapChain9_QueryInterface(sw9, &IID_IDirect3DSwapChain9Ex,
241         (void**)&p->swapchain);
242     if (FAILED(hr)) {
243         SAFE_RELEASE(sw9);
244         MP_ERR(ctx->vo, "Obtained swap chain is not IDirect3DSwapChain9Ex: %s\n",
245                mp_HRESULT_to_str(hr));
246         return -1;
247     }
248     SAFE_RELEASE(sw9);
249 
250     hr = IDirect3DSwapChain9Ex_GetBackBuffer(p->swapchain, 0,
251         D3DBACKBUFFER_TYPE_MONO, &p->backbuffer);
252     if (FAILED(hr)) {
253         MP_ERR(ctx->vo, "Couldn't get backbuffer: %s\n", mp_HRESULT_to_str(hr));
254         return -1;
255     }
256 
257     // Get the format of the backbuffer
258     D3DSURFACE_DESC bb_desc = { 0 };
259     IDirect3DSurface9_GetDesc(p->backbuffer, &bb_desc);
260 
261     MP_VERBOSE(ctx->vo, "DX_interop backbuffer size: %ux%u\n",
262         (unsigned)bb_desc.Width, (unsigned)bb_desc.Height);
263     MP_VERBOSE(ctx->vo, "DX_interop backbuffer format: %u\n",
264         (unsigned)bb_desc.Format);
265 
266     // Create a rendertarget with the same format as the backbuffer for
267     // rendering from OpenGL
268     HANDLE share_handle = NULL;
269     hr = IDirect3DDevice9Ex_CreateRenderTarget(p->device, bb_desc.Width,
270         bb_desc.Height, bb_desc.Format, D3DMULTISAMPLE_NONE, 0, FALSE,
271         &p->rtarget, &share_handle);
272     if (FAILED(hr)) {
273         MP_ERR(ctx->vo, "Couldn't create rendertarget: %s\n", mp_HRESULT_to_str(hr));
274         return -1;
275     }
276 
277     // Register the share handle with WGL_NV_DX_interop. Nvidia does not
278     // require the use of share handles, but Intel does.
279     if (share_handle)
280         gl->DXSetResourceShareHandleNV(p->rtarget, share_handle);
281 
282     // Create the OpenGL-side texture
283     gl->GenTextures(1, &p->texture);
284 
285     // Now share the rendertarget with OpenGL as a texture
286     p->rtarget_h = gl->DXRegisterObjectNV(p->device_h, p->rtarget, p->texture,
287         GL_TEXTURE_2D, WGL_ACCESS_WRITE_DISCARD_NV);
288     if (!p->rtarget_h) {
289         MP_ERR(ctx->vo, "Couldn't share rendertarget with OpenGL: %s\n",
290                mp_LastError_to_str());
291         return -1;
292     }
293 
294     // Lock the rendertarget for use from OpenGL. This will only be unlocked in
295     // swap_buffers() when it is blitted to the real Direct3D backbuffer.
296     if (!gl->DXLockObjectsNV(p->device_h, 1, &p->rtarget_h)) {
297         MP_ERR(ctx->vo, "Couldn't lock rendertarget: %s\n",
298                mp_LastError_to_str());
299         return -1;
300     }
301 
302     gl->BindFramebuffer(GL_FRAMEBUFFER, p->main_fb);
303     gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
304         GL_TEXTURE_2D, p->texture, 0);
305     gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
306 
307     return 0;
308 }
309 
d3d_size_dependent_destroy(struct ra_ctx * ctx)310 static void d3d_size_dependent_destroy(struct ra_ctx *ctx)
311 {
312     struct priv *p = ctx->priv;
313     GL *gl = &p->gl;
314 
315     if (p->rtarget_h) {
316         gl->DXUnlockObjectsNV(p->device_h, 1, &p->rtarget_h);
317         gl->DXUnregisterObjectNV(p->device_h, p->rtarget_h);
318     }
319     p->rtarget_h = 0;
320     if (p->texture)
321         gl->DeleteTextures(1, &p->texture);
322     p->texture = 0;
323 
324     SAFE_RELEASE(p->rtarget);
325     SAFE_RELEASE(p->backbuffer);
326     SAFE_RELEASE(p->swapchain);
327 }
328 
fill_presentparams(struct ra_ctx * ctx,D3DPRESENT_PARAMETERS * pparams)329 static void fill_presentparams(struct ra_ctx *ctx,
330                                D3DPRESENT_PARAMETERS *pparams)
331 {
332     struct priv *p = ctx->priv;
333 
334     // Present intervals other than IMMEDIATE and ONE don't seem to work. It's
335     // possible that they're not compatible with FLIPEX.
336     UINT presentation_interval;
337     switch (p->requested_swapinterval) {
338     case 0:  presentation_interval = D3DPRESENT_INTERVAL_IMMEDIATE; break;
339     case 1:  presentation_interval = D3DPRESENT_INTERVAL_ONE;       break;
340     default: presentation_interval = D3DPRESENT_INTERVAL_ONE;       break;
341     }
342 
343     *pparams = (D3DPRESENT_PARAMETERS) {
344         .Windowed = TRUE,
345         .BackBufferWidth = ctx->vo->dwidth ? ctx->vo->dwidth : 1,
346         .BackBufferHeight = ctx->vo->dheight ? ctx->vo->dheight : 1,
347         // Add one frame for the backbuffer and one frame of "slack" to reduce
348         // contention with the window manager when acquiring the backbuffer
349         .BackBufferCount = ctx->vo->opts->swapchain_depth + 2,
350         .SwapEffect = IsWindows7OrGreater() ? D3DSWAPEFFECT_FLIPEX : D3DSWAPEFFECT_FLIP,
351         // Automatically get the backbuffer format from the display format
352         .BackBufferFormat = D3DFMT_UNKNOWN,
353         .PresentationInterval = presentation_interval,
354         .hDeviceWindow = vo_w32_hwnd(ctx->vo),
355     };
356 }
357 
d3d_create(struct ra_ctx * ctx)358 static int d3d_create(struct ra_ctx *ctx)
359 {
360     struct priv *p = ctx->priv;
361     GL *gl = &p->gl;
362     HRESULT hr;
363 
364     p->d3d9_dll = LoadLibraryW(L"d3d9.dll");
365     if (!p->d3d9_dll) {
366         MP_FATAL(ctx->vo, "Failed to load \"d3d9.dll\": %s\n",
367                  mp_LastError_to_str());
368         return -1;
369     }
370 
371     // WGL_NV_dx_interop requires Direct3D 9Ex on WDDM systems. Direct3D 9Ex
372     // also enables flip mode present for efficient rendering with the DWM.
373     p->Direct3DCreate9Ex = (void*)GetProcAddress(p->d3d9_dll,
374         "Direct3DCreate9Ex");
375     if (!p->Direct3DCreate9Ex) {
376         MP_FATAL(ctx->vo, "Direct3D 9Ex not supported\n");
377         return -1;
378     }
379 
380     hr = p->Direct3DCreate9Ex(D3D_SDK_VERSION, &p->d3d9ex);
381     if (FAILED(hr)) {
382         MP_FATAL(ctx->vo, "Couldn't create Direct3D9Ex: %s\n",
383                  mp_HRESULT_to_str(hr));
384         return -1;
385     }
386 
387     D3DPRESENT_PARAMETERS pparams;
388     fill_presentparams(ctx, &pparams);
389 
390     hr = IDirect3D9Ex_CreateDeviceEx(p->d3d9ex, D3DADAPTER_DEFAULT,
391         D3DDEVTYPE_HAL, vo_w32_hwnd(ctx->vo),
392         D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE |
393         D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED |
394         D3DCREATE_NOWINDOWCHANGES,
395         &pparams, NULL, &p->device);
396     if (FAILED(hr)) {
397         MP_FATAL(ctx->vo, "Couldn't create device: %s\n", mp_HRESULT_to_str(hr));
398         return -1;
399     }
400 
401     IDirect3DDevice9Ex_SetMaximumFrameLatency(p->device, ctx->vo->opts->swapchain_depth);
402 
403     // Register the Direct3D device with WGL_NV_dx_interop
404     p->device_h = gl->DXOpenDeviceNV(p->device);
405     if (!p->device_h) {
406         MP_FATAL(ctx->vo, "Couldn't open Direct3D device from OpenGL: %s\n",
407                  mp_LastError_to_str());
408         return -1;
409     }
410 
411     return 0;
412 }
413 
d3d_destroy(struct ra_ctx * ctx)414 static void d3d_destroy(struct ra_ctx *ctx)
415 {
416     struct priv *p = ctx->priv;
417     GL *gl = &p->gl;
418 
419     if (p->device_h)
420         gl->DXCloseDeviceNV(p->device_h);
421     SAFE_RELEASE(p->device);
422     SAFE_RELEASE(p->d3d9ex);
423     if (p->d3d9_dll)
424         FreeLibrary(p->d3d9_dll);
425 }
426 
dxgl_uninit(struct ra_ctx * ctx)427 static void dxgl_uninit(struct ra_ctx *ctx)
428 {
429     ra_gl_ctx_uninit(ctx);
430     d3d_size_dependent_destroy(ctx);
431     d3d_destroy(ctx);
432     os_ctx_destroy(ctx);
433     vo_w32_uninit(ctx->vo);
434     DwmEnableMMCSS(FALSE);
435     pump_message_loop();
436 }
437 
dxgl_reset(struct ra_ctx * ctx)438 static void dxgl_reset(struct ra_ctx *ctx)
439 {
440     struct priv *p = ctx->priv;
441     HRESULT hr;
442 
443     // Check if the device actually needs to be reset
444     if (ctx->vo->dwidth == p->width && ctx->vo->dheight == p->height &&
445         p->requested_swapinterval == p->swapinterval && !p->lost_device)
446         return;
447 
448     d3d_size_dependent_destroy(ctx);
449 
450     D3DPRESENT_PARAMETERS pparams;
451     fill_presentparams(ctx, &pparams);
452 
453     hr = IDirect3DDevice9Ex_ResetEx(p->device, &pparams, NULL);
454     if (FAILED(hr)) {
455         p->lost_device = true;
456         MP_ERR(ctx->vo, "Couldn't reset device: %s\n", mp_HRESULT_to_str(hr));
457         return;
458     }
459 
460     if (d3d_size_dependent_create(ctx) < 0) {
461         p->lost_device = true;
462         MP_ERR(ctx->vo, "Couldn't recreate Direct3D objects after reset\n");
463         return;
464     }
465 
466     MP_VERBOSE(ctx->vo, "Direct3D device reset\n");
467     p->width = ctx->vo->dwidth;
468     p->height = ctx->vo->dheight;
469     p->swapinterval = p->requested_swapinterval;
470     p->lost_device = false;
471 }
472 
dxgl_swap_interval(int interval)473 static int GLAPIENTRY dxgl_swap_interval(int interval)
474 {
475     if (!current_ctx)
476         return 0;
477     struct priv *p = current_ctx->priv;
478 
479     p->requested_swapinterval = interval;
480     dxgl_reset(current_ctx);
481     return 1;
482 }
483 
dxgl_swap_buffers(struct ra_ctx * ctx)484 static void dxgl_swap_buffers(struct ra_ctx *ctx)
485 {
486     struct priv *p = ctx->priv;
487     GL *gl = &p->gl;
488     HRESULT hr;
489 
490     pump_message_loop();
491 
492     // If the device is still lost, try to reset it again
493     if (p->lost_device)
494         dxgl_reset(ctx);
495     if (p->lost_device)
496         return;
497 
498     if (!gl->DXUnlockObjectsNV(p->device_h, 1, &p->rtarget_h)) {
499         MP_ERR(ctx->vo, "Couldn't unlock rendertarget for present: %s\n",
500                mp_LastError_to_str());
501         return;
502     }
503 
504     // Blit the OpenGL rendertarget to the backbuffer
505     hr = IDirect3DDevice9Ex_StretchRect(p->device, p->rtarget, NULL,
506                                         p->backbuffer, NULL, D3DTEXF_NONE);
507     if (FAILED(hr)) {
508         MP_ERR(ctx->vo, "Couldn't stretchrect for present: %s\n",
509                mp_HRESULT_to_str(hr));
510         return;
511     }
512 
513     hr = IDirect3DDevice9Ex_PresentEx(p->device, NULL, NULL, NULL, NULL, 0);
514     switch (hr) {
515     case D3DERR_DEVICELOST:
516     case D3DERR_DEVICEHUNG:
517         MP_VERBOSE(ctx->vo, "Direct3D device lost! Resetting.\n");
518         p->lost_device = true;
519         dxgl_reset(ctx);
520         return;
521     default:
522         if (FAILED(hr))
523             MP_ERR(ctx->vo, "Failed to present: %s\n", mp_HRESULT_to_str(hr));
524     }
525 
526     if (!gl->DXLockObjectsNV(p->device_h, 1, &p->rtarget_h)) {
527         MP_ERR(ctx->vo, "Couldn't lock rendertarget after present: %s\n",
528                mp_LastError_to_str());
529     }
530 }
531 
dxgl_init(struct ra_ctx * ctx)532 static bool dxgl_init(struct ra_ctx *ctx)
533 {
534     struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
535     GL *gl = &p->gl;
536 
537     p->requested_swapinterval = 1;
538 
539     if (!vo_w32_init(ctx->vo))
540         goto fail;
541     if (os_ctx_create(ctx) < 0)
542         goto fail;
543 
544     // Create the shared framebuffer
545     gl->GenFramebuffers(1, &p->main_fb);
546 
547     current_ctx = ctx;
548     gl->SwapInterval = dxgl_swap_interval;
549 
550     if (d3d_create(ctx) < 0)
551         goto fail;
552     if (d3d_size_dependent_create(ctx) < 0)
553         goto fail;
554 
555     static const struct ra_swapchain_fns empty_swapchain_fns = {0};
556     struct ra_gl_ctx_params params = {
557         .swap_buffers = dxgl_swap_buffers,
558         .flipped = true,
559         .external_swapchain = &empty_swapchain_fns,
560     };
561 
562     if (!ra_gl_ctx_init(ctx, gl, params))
563         goto fail;
564 
565     ra_add_native_resource(ctx->ra, "IDirect3DDevice9Ex", p->device);
566     ra_add_native_resource(ctx->ra, "dxinterop_device_HANDLE", p->device_h);
567 
568     DwmEnableMMCSS(TRUE);
569     return true;
570 fail:
571     dxgl_uninit(ctx);
572     return false;
573 }
574 
resize(struct ra_ctx * ctx)575 static void resize(struct ra_ctx *ctx)
576 {
577     struct priv *p = ctx->priv;
578     dxgl_reset(ctx);
579     ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, p->main_fb);
580 }
581 
dxgl_reconfig(struct ra_ctx * ctx)582 static bool dxgl_reconfig(struct ra_ctx *ctx)
583 {
584     vo_w32_config(ctx->vo);
585     resize(ctx);
586     return true;
587 }
588 
dxgl_control(struct ra_ctx * ctx,int * events,int request,void * arg)589 static int dxgl_control(struct ra_ctx *ctx, int *events, int request,
590                              void *arg)
591 {
592     int ret = vo_w32_control(ctx->vo, events, request, arg);
593     if (*events & VO_EVENT_RESIZE)
594         resize(ctx);
595     return ret;
596 }
597 
598 const struct ra_ctx_fns ra_ctx_dxgl = {
599     .type         = "opengl",
600     .name         = "dxinterop",
601     .init         = dxgl_init,
602     .reconfig     = dxgl_reconfig,
603     .control      = dxgl_control,
604     .uninit       = dxgl_uninit,
605 };
606