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