1 /*
2  * Copyright (c) 2008 Georgi Petrov (gogothebee) <gogothebee@gmail.com>
3  *
4  * This file is part of mpv.
5  *
6  * mpv is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * mpv is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with mpv.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <windows.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <math.h>
25 #include <stdbool.h>
26 #include <assert.h>
27 #include <d3d9.h>
28 #include <inttypes.h>
29 #include <limits.h>
30 #include "config.h"
31 #include "options/options.h"
32 #include "options/m_option.h"
33 #include "sub/draw_bmp.h"
34 #include "mpv_talloc.h"
35 #include "vo.h"
36 #include "video/csputils.h"
37 #include "video/mp_image.h"
38 #include "video/img_format.h"
39 #include "common/msg.h"
40 #include "common/common.h"
41 #include "w32_common.h"
42 #include "sub/osd.h"
43 
44 #include "config.h"
45 #if !HAVE_GPL
46 #error GPL only
47 #endif
48 
49 #define DEVTYPE D3DDEVTYPE_HAL
50 //#define DEVTYPE D3DDEVTYPE_REF
51 
52 #define D3DFVF_OSD_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1)
53 
54 typedef struct {
55     float x, y, z;
56     float tu, tv;
57 } vertex_osd;
58 
59 struct d3dtex {
60     // user-requested size
61     int w, h;
62     // allocated texture size
63     int tex_w, tex_h;
64     // D3DPOOL_SYSTEMMEM (or others) texture:
65     // - can be locked in order to write (and even read) data
66     // - can _not_ (probably) be used as texture for rendering
67     // This is always non-NULL if d3dtex_allocate succeeds.
68     IDirect3DTexture9 *system;
69     // D3DPOOL_DEFAULT texture:
70     // - can't be locked (Probably.)
71     // - must be used for rendering
72     // This can be NULL if the system one can be both locked and mapped.
73     IDirect3DTexture9 *device;
74 };
75 
76 #define MAX_OSD_RECTS 64
77 
78 /* Global variables "priv" structure. I try to keep their count low.
79  */
80 typedef struct d3d_priv {
81     struct mp_log *log;
82 
83     int opt_disable_texture_align;
84     // debugging
85     int opt_force_power_of_2;
86     int opt_texture_memory;
87     int opt_swap_discard;
88     int opt_exact_backbuffer;
89 
90     struct vo *vo;
91 
92     bool have_image;
93     double osd_pts;
94 
95     D3DLOCKED_RECT locked_rect; /**< The locked offscreen surface */
96     RECT fs_movie_rect;         /**< Rect (upscaled) of the movie when displayed
97                                 in fullscreen */
98     RECT fs_panscan_rect;       /**< PanScan source surface cropping in
99                                 fullscreen */
100     int src_width;              /**< Source (movie) width */
101     int src_height;             /**< Source (movie) heigth */
102     struct mp_osd_res osd_res;
103     int image_format;           /**< mplayer image format */
104     struct mp_image_params params;
105 
106     D3DFORMAT movie_src_fmt;        /**< Movie colorspace format (depends on
107                                     the movie's codec) */
108     D3DFORMAT desktop_fmt;          /**< Desktop (screen) colorspace format.
109                                     Usually XRGB */
110 
111     HANDLE d3d9_dll;                /**< d3d9 Library HANDLE */
112     IDirect3D9 * (WINAPI *pDirect3DCreate9)(UINT); /**< pointer to Direct3DCreate9 function */
113 
114     LPDIRECT3D9        d3d_handle;  /**< Direct3D Handle */
115     LPDIRECT3DDEVICE9  d3d_device;  /**< The Direct3D Adapter */
116     bool d3d_in_scene;              /**< BeginScene was called, EndScene not */
117     IDirect3DSurface9 *d3d_surface; /**< Offscreen Direct3D Surface. MPlayer
118                                     renders inside it. Uses colorspace
119                                     priv->movie_src_fmt */
120     IDirect3DSurface9 *d3d_backbuf; /**< Video card's back buffer (used to
121                                     display next frame) */
122     int cur_backbuf_width;          /**< Current backbuffer width */
123     int cur_backbuf_height;         /**< Current backbuffer height */
124     int device_caps_power2_only;    /**< 1 = texture sizes have to be power 2
125                                     0 = texture sizes can be anything */
126     int device_caps_square_only;    /**< 1 = textures have to be square
127                                     0 = textures do not have to be square */
128     int device_texture_sys;         /**< 1 = device can texture from system memory
129                                     0 = device requires shadow */
130     int max_texture_width;          /**< from the device capabilities */
131     int max_texture_height;         /**< from the device capabilities */
132 
133     D3DMATRIX d3d_colormatrix;
134 
135     struct mp_draw_sub_cache *osd_cache;
136     struct d3dtex osd_texture;
137     int osd_num_vertices;
138     vertex_osd osd_vertices[MAX_OSD_RECTS * 6];
139 } d3d_priv;
140 
141 struct fmt_entry {
142     const unsigned int  mplayer_fmt;   /**< Given by MPlayer */
143     const D3DFORMAT     fourcc;        /**< Required by D3D's test function */
144 };
145 
146 /* Map table from reported MPlayer format to the required
147    fourcc. This is needed to perform the format query. */
148 
149 static const struct fmt_entry fmt_table[] = {
150     // planar YUV
151     {IMGFMT_420P,  MAKEFOURCC('Y','V','1','2')},
152     {IMGFMT_420P,  MAKEFOURCC('I','4','2','0')},
153     {IMGFMT_420P,  MAKEFOURCC('I','Y','U','V')},
154     {IMGFMT_NV12,  MAKEFOURCC('N','V','1','2')},
155     // packed YUV
156     {IMGFMT_UYVY,  D3DFMT_UYVY},
157     // packed RGB
158     {IMGFMT_BGR0, D3DFMT_X8R8G8B8},
159     {IMGFMT_RGB0, D3DFMT_X8B8G8R8},
160     {IMGFMT_BGR24, D3DFMT_R8G8B8}, //untested
161     {IMGFMT_RGB565, D3DFMT_R5G6B5},
162     {0},
163 };
164 
165 
166 static bool resize_d3d(d3d_priv *priv);
167 static void uninit(struct vo *vo);
168 static void flip_page(struct vo *vo);
169 static mp_image_t *get_window_screenshot(d3d_priv *priv);
170 static void draw_osd(struct vo *vo);
171 static bool change_d3d_backbuffer(d3d_priv *priv);
172 
d3d_matrix_identity(D3DMATRIX * m)173 static void d3d_matrix_identity(D3DMATRIX *m)
174 {
175     memset(m, 0, sizeof(D3DMATRIX));
176     m->_11 = m->_22 = m->_33 = m->_44 = 1.0f;
177 }
178 
d3d_matrix_ortho(D3DMATRIX * m,float left,float right,float bottom,float top)179 static void d3d_matrix_ortho(D3DMATRIX *m, float left, float right,
180                              float bottom, float top)
181 {
182     d3d_matrix_identity(m);
183     m->_11 = 2.0f / (right - left);
184     m->_22 = 2.0f / (top - bottom);
185     m->_33 = 1.0f;
186     m->_41 = -(right + left) / (right - left);
187     m->_42 = -(top + bottom) / (top - bottom);
188     m->_43 = 0;
189     m->_44 = 1.0f;
190 }
191 
192 /****************************************************************************
193  *                                                                          *
194  *                                                                          *
195  *                                                                          *
196  * Direct3D specific implementation functions                               *
197  *                                                                          *
198  *                                                                          *
199  *                                                                          *
200  ****************************************************************************/
201 
d3d_begin_scene(d3d_priv * priv)202 static bool d3d_begin_scene(d3d_priv *priv)
203 {
204     if (!priv->d3d_in_scene) {
205         if (FAILED(IDirect3DDevice9_BeginScene(priv->d3d_device))) {
206             MP_ERR(priv, "BeginScene failed.\n");
207             return false;
208         }
209         priv->d3d_in_scene = true;
210     }
211     return true;
212 }
213 
214 /** @brief Calculate scaled fullscreen movie rectangle with
215  *  preserved aspect ratio.
216  */
calc_fs_rect(d3d_priv * priv)217 static void calc_fs_rect(d3d_priv *priv)
218 {
219     struct mp_rect src_rect;
220     struct mp_rect dst_rect;
221     vo_get_src_dst_rects(priv->vo, &src_rect, &dst_rect, &priv->osd_res);
222 
223     priv->fs_movie_rect.left     = dst_rect.x0;
224     priv->fs_movie_rect.right    = dst_rect.x1;
225     priv->fs_movie_rect.top      = dst_rect.y0;
226     priv->fs_movie_rect.bottom   = dst_rect.y1;
227     priv->fs_panscan_rect.left   = src_rect.x0;
228     priv->fs_panscan_rect.right  = src_rect.x1;
229     priv->fs_panscan_rect.top    = src_rect.y0;
230     priv->fs_panscan_rect.bottom = src_rect.y1;
231 }
232 
233 // Adjust the texture size *width/*height to fit the requirements of the D3D
234 // device. The texture size is only increased.
d3d_fix_texture_size(d3d_priv * priv,int * width,int * height)235 static void d3d_fix_texture_size(d3d_priv *priv, int *width, int *height)
236 {
237     int tex_width = *width;
238     int tex_height = *height;
239 
240     // avoid nasty special cases with 0-sized textures and texture sizes
241     tex_width = MPMAX(tex_width, 1);
242     tex_height = MPMAX(tex_height, 1);
243 
244     if (priv->device_caps_power2_only) {
245         tex_width  = 1;
246         tex_height = 1;
247         while (tex_width  < *width) tex_width  <<= 1;
248         while (tex_height < *height) tex_height <<= 1;
249     }
250     if (priv->device_caps_square_only)
251         /* device only supports square textures */
252         tex_width = tex_height = MPMAX(tex_width, tex_height);
253     // better round up to a multiple of 16
254     if (!priv->opt_disable_texture_align) {
255         tex_width  = (tex_width  + 15) & ~15;
256         tex_height = (tex_height + 15) & ~15;
257     }
258 
259     *width = tex_width;
260     *height = tex_height;
261 }
262 
d3dtex_release(d3d_priv * priv,struct d3dtex * tex)263 static void d3dtex_release(d3d_priv *priv, struct d3dtex *tex)
264 {
265     if (tex->system)
266         IDirect3DTexture9_Release(tex->system);
267     tex->system = NULL;
268 
269     if (tex->device)
270         IDirect3DTexture9_Release(tex->device);
271     tex->device = NULL;
272 
273     tex->tex_w = tex->tex_h = 0;
274 }
275 
d3dtex_allocate(d3d_priv * priv,struct d3dtex * tex,D3DFORMAT fmt,int w,int h)276 static bool d3dtex_allocate(d3d_priv *priv, struct d3dtex *tex, D3DFORMAT fmt,
277                             int w, int h)
278 {
279     d3dtex_release(priv, tex);
280 
281     tex->w = w;
282     tex->h = h;
283 
284     int tw = w, th = h;
285     d3d_fix_texture_size(priv, &tw, &th);
286 
287     bool use_sh = !priv->device_texture_sys;
288     int memtype = D3DPOOL_SYSTEMMEM;
289     switch (priv->opt_texture_memory) {
290     case 1: memtype = D3DPOOL_MANAGED; use_sh = false; break;
291     case 2: memtype = D3DPOOL_DEFAULT; use_sh = false; break;
292     case 3: memtype = D3DPOOL_DEFAULT; use_sh = true; break;
293     case 4: memtype = D3DPOOL_SCRATCH; use_sh = true; break;
294     }
295 
296     if (FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device, tw, th, 1,
297         D3DUSAGE_DYNAMIC, fmt, memtype, &tex->system, NULL)))
298     {
299         MP_ERR(priv, "Allocating %dx%d texture in system RAM failed.\n", w, h);
300         goto error_exit;
301     }
302 
303     if (use_sh) {
304         if (FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device, tw, th, 1,
305             D3DUSAGE_DYNAMIC, fmt, D3DPOOL_DEFAULT, &tex->device, NULL)))
306         {
307             MP_ERR(priv, "Allocating %dx%d texture in video RAM failed.\n", w, h);
308             goto error_exit;
309         }
310     }
311 
312     tex->tex_w = tw;
313     tex->tex_h = th;
314 
315     return true;
316 
317 error_exit:
318     d3dtex_release(priv, tex);
319     return false;
320 }
321 
d3dtex_get_render_texture(d3d_priv * priv,struct d3dtex * tex)322 static IDirect3DBaseTexture9 *d3dtex_get_render_texture(d3d_priv *priv,
323                                                         struct d3dtex *tex)
324 {
325     return (IDirect3DBaseTexture9 *)(tex->device ? tex->device : tex->system);
326 }
327 
328 // Copy system texture contents to device texture.
d3dtex_update(d3d_priv * priv,struct d3dtex * tex)329 static bool d3dtex_update(d3d_priv *priv, struct d3dtex *tex)
330 {
331     if (!tex->device)
332         return true;
333     return !FAILED(IDirect3DDevice9_UpdateTexture(priv->d3d_device,
334                    (IDirect3DBaseTexture9 *)tex->system,
335                    (IDirect3DBaseTexture9 *)tex->device));
336 }
337 
d3d_unlock_video_objects(d3d_priv * priv)338 static void d3d_unlock_video_objects(d3d_priv *priv)
339 {
340     if (priv->locked_rect.pBits) {
341         if (FAILED(IDirect3DSurface9_UnlockRect(priv->d3d_surface)))
342             MP_VERBOSE(priv, "Unlocking video objects failed.\n");
343     }
344     priv->locked_rect.pBits = NULL;
345 }
346 
347 // Free video surface/textures,  etc.
d3d_destroy_video_objects(d3d_priv * priv)348 static void d3d_destroy_video_objects(d3d_priv *priv)
349 {
350     d3d_unlock_video_objects(priv);
351 
352     if (priv->d3d_surface)
353         IDirect3DSurface9_Release(priv->d3d_surface);
354     priv->d3d_surface = NULL;
355 }
356 
357 /** @brief Destroy D3D Offscreen and Backbuffer surfaces.
358  */
destroy_d3d_surfaces(d3d_priv * priv)359 static void destroy_d3d_surfaces(d3d_priv *priv)
360 {
361     MP_VERBOSE(priv, "destroy_d3d_surfaces called.\n");
362 
363     d3d_destroy_video_objects(priv);
364     d3dtex_release(priv, &priv->osd_texture);
365 
366     if (priv->d3d_backbuf)
367         IDirect3DSurface9_Release(priv->d3d_backbuf);
368     priv->d3d_backbuf = NULL;
369 
370     priv->d3d_in_scene = false;
371 }
372 
373 // Allocate video surface.
d3d_configure_video_objects(d3d_priv * priv)374 static bool d3d_configure_video_objects(d3d_priv *priv)
375 {
376     assert(priv->image_format != 0);
377 
378     if (!priv->d3d_surface &&
379         FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface(
380             priv->d3d_device, priv->src_width, priv->src_height,
381             priv->movie_src_fmt, D3DPOOL_DEFAULT, &priv->d3d_surface, NULL)))
382     {
383         MP_ERR(priv, "Allocating offscreen surface failed.\n");
384         return false;
385     }
386 
387     return true;
388 }
389 
390 // Recreate and initialize D3D objects if necessary. The amount of work that
391 // needs to be done can be quite different: it could be that full initialization
392 // is required, or that some objects need to be created, or that nothing is
393 // done.
create_d3d_surfaces(d3d_priv * priv)394 static bool create_d3d_surfaces(d3d_priv *priv)
395 {
396     MP_VERBOSE(priv, "create_d3d_surfaces called.\n");
397 
398     if (!priv->d3d_backbuf &&
399         FAILED(IDirect3DDevice9_GetBackBuffer(priv->d3d_device, 0, 0,
400                                               D3DBACKBUFFER_TYPE_MONO,
401                                               &priv->d3d_backbuf))) {
402         MP_ERR(priv, "Allocating backbuffer failed.\n");
403         return 0;
404     }
405 
406     if (!d3d_configure_video_objects(priv))
407         return 0;
408 
409     /* setup default renderstate */
410     IDirect3DDevice9_SetRenderState(priv->d3d_device,
411                                     D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
412     IDirect3DDevice9_SetRenderState(priv->d3d_device,
413                                     D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
414     IDirect3DDevice9_SetRenderState(priv->d3d_device,
415                                     D3DRS_ALPHAFUNC, D3DCMP_GREATER);
416     IDirect3DDevice9_SetRenderState(priv->d3d_device,
417                                     D3DRS_ALPHAREF, (DWORD)0x0);
418     IDirect3DDevice9_SetRenderState(priv->d3d_device,
419                                     D3DRS_LIGHTING, FALSE);
420 
421     // we use up to 3 samplers for up to 3 YUV planes
422     // TODO
423     /*
424     for (int n = 0; n < 3; n++) {
425         IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_MINFILTER,
426                                          D3DTEXF_LINEAR);
427         IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_MAGFILTER,
428                                          D3DTEXF_LINEAR);
429         IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_ADDRESSU,
430                                          D3DTADDRESS_CLAMP);
431         IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_ADDRESSV,
432                                          D3DTADDRESS_CLAMP);
433     }
434     */
435 
436     return 1;
437 }
438 
init_d3d(d3d_priv * priv)439 static bool init_d3d(d3d_priv *priv)
440 {
441     D3DDISPLAYMODE disp_mode;
442     D3DCAPS9 disp_caps;
443     DWORD texture_caps;
444     DWORD dev_caps;
445 
446     priv->d3d_handle = priv->pDirect3DCreate9(D3D_SDK_VERSION);
447     if (!priv->d3d_handle) {
448         MP_ERR(priv, "Initializing Direct3D failed.\n");
449         return false;
450     }
451 
452     if (FAILED(IDirect3D9_GetAdapterDisplayMode(priv->d3d_handle,
453                                                 D3DADAPTER_DEFAULT,
454                                                 &disp_mode))) {
455         MP_ERR(priv, "Reading display mode failed.\n");
456         return false;
457     }
458 
459     priv->desktop_fmt = disp_mode.Format;
460     priv->cur_backbuf_width = disp_mode.Width;
461     priv->cur_backbuf_height = disp_mode.Height;
462 
463     MP_VERBOSE(priv, "Setting backbuffer dimensions to (%dx%d).\n",
464                disp_mode.Width, disp_mode.Height);
465 
466     if (FAILED(IDirect3D9_GetDeviceCaps(priv->d3d_handle,
467                                         D3DADAPTER_DEFAULT,
468                                         DEVTYPE,
469                                         &disp_caps)))
470     {
471         MP_ERR(priv, "Reading display capabilities failed.\n");
472         return false;
473     }
474 
475     /* Store relevant information reguarding caps of device */
476     texture_caps                  = disp_caps.TextureCaps;
477     dev_caps                      = disp_caps.DevCaps;
478     priv->device_caps_power2_only =  (texture_caps & D3DPTEXTURECAPS_POW2) &&
479                         !(texture_caps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL);
480     priv->device_caps_square_only = texture_caps & D3DPTEXTURECAPS_SQUAREONLY;
481     priv->device_texture_sys      = dev_caps & D3DDEVCAPS_TEXTURESYSTEMMEMORY;
482     priv->max_texture_width       = disp_caps.MaxTextureWidth;
483     priv->max_texture_height      = disp_caps.MaxTextureHeight;
484 
485     if (priv->opt_force_power_of_2)
486         priv->device_caps_power2_only = 1;
487 
488     if (FAILED(IDirect3D9_CheckDeviceFormat(priv->d3d_handle,
489                         D3DADAPTER_DEFAULT,
490                         DEVTYPE,
491                         priv->desktop_fmt,
492                         D3DUSAGE_DYNAMIC | D3DUSAGE_QUERY_FILTER,
493                         D3DRTYPE_TEXTURE,
494                         D3DFMT_A8R8G8B8)))
495     {
496         MP_ERR(priv, "OSD texture format not supported.\n");
497         return false;
498     }
499 
500     if (!change_d3d_backbuffer(priv))
501         return false;
502 
503     MP_VERBOSE(priv, "device_caps_power2_only %d, device_caps_square_only %d\n"
504                "device_texture_sys %d\n"
505                "max_texture_width %d, max_texture_height %d\n",
506                priv->device_caps_power2_only, priv->device_caps_square_only,
507                priv->device_texture_sys, priv->max_texture_width,
508                priv->max_texture_height);
509 
510     return true;
511 }
512 
513 /** @brief Fill D3D Presentation parameters
514  */
fill_d3d_presentparams(d3d_priv * priv,D3DPRESENT_PARAMETERS * present_params)515 static void fill_d3d_presentparams(d3d_priv *priv,
516                                    D3DPRESENT_PARAMETERS *present_params)
517 {
518     /* Prepare Direct3D initialization parameters. */
519     memset(present_params, 0, sizeof(D3DPRESENT_PARAMETERS));
520     present_params->Windowed               = TRUE;
521     present_params->SwapEffect             =
522         priv->opt_swap_discard ? D3DSWAPEFFECT_DISCARD : D3DSWAPEFFECT_COPY;
523     present_params->Flags                  = D3DPRESENTFLAG_VIDEO;
524     present_params->hDeviceWindow          = vo_w32_hwnd(priv->vo);
525     present_params->BackBufferWidth        = priv->cur_backbuf_width;
526     present_params->BackBufferHeight       = priv->cur_backbuf_height;
527     present_params->MultiSampleType        = D3DMULTISAMPLE_NONE;
528     present_params->PresentationInterval   = D3DPRESENT_INTERVAL_ONE;
529     present_params->BackBufferFormat       = priv->desktop_fmt;
530     present_params->BackBufferCount        = 1;
531     present_params->EnableAutoDepthStencil = FALSE;
532 }
533 
534 
535 // Create a new backbuffer. Create or Reset the D3D device.
change_d3d_backbuffer(d3d_priv * priv)536 static bool change_d3d_backbuffer(d3d_priv *priv)
537 {
538     int window_w = priv->vo->dwidth;
539     int window_h = priv->vo->dheight;
540 
541     /* Grow the backbuffer in the required dimension. */
542     if (window_w > priv->cur_backbuf_width)
543         priv->cur_backbuf_width = window_w;
544 
545     if (window_h > priv->cur_backbuf_height)
546         priv->cur_backbuf_height = window_h;
547 
548     if (priv->opt_exact_backbuffer) {
549         priv->cur_backbuf_width = window_w;
550         priv->cur_backbuf_height = window_h;
551     }
552 
553     /* The grown backbuffer dimensions are ready and fill_d3d_presentparams
554      * will use them, so we can reset the device.
555      */
556     D3DPRESENT_PARAMETERS present_params;
557     fill_d3d_presentparams(priv, &present_params);
558 
559     if (!priv->d3d_device) {
560         if (FAILED(IDirect3D9_CreateDevice(priv->d3d_handle,
561                                            D3DADAPTER_DEFAULT,
562                                            DEVTYPE, vo_w32_hwnd(priv->vo),
563                                            D3DCREATE_SOFTWARE_VERTEXPROCESSING
564                                            | D3DCREATE_FPU_PRESERVE
565                                            | D3DCREATE_MULTITHREADED,
566                                            &present_params, &priv->d3d_device)))
567         {
568             MP_VERBOSE(priv, "Creating Direct3D device failed.\n");
569             return 0;
570         }
571     } else {
572         if (FAILED(IDirect3DDevice9_Reset(priv->d3d_device, &present_params))) {
573             MP_ERR(priv, "Reseting Direct3D device failed.\n");
574             return 0;
575         }
576     }
577 
578     MP_VERBOSE(priv, "New backbuffer (%dx%d), VO (%dx%d)\n",
579                present_params.BackBufferWidth, present_params.BackBufferHeight,
580                window_w, window_h);
581 
582     return 1;
583 }
584 
destroy_d3d(d3d_priv * priv)585 static void destroy_d3d(d3d_priv *priv)
586 {
587     destroy_d3d_surfaces(priv);
588 
589     if (priv->d3d_device)
590         IDirect3DDevice9_Release(priv->d3d_device);
591     priv->d3d_device = NULL;
592 
593     if (priv->d3d_handle) {
594         MP_VERBOSE(priv, "Stopping Direct3D.\n");
595         IDirect3D9_Release(priv->d3d_handle);
596     }
597     priv->d3d_handle = NULL;
598 }
599 
600 /** @brief Reconfigure the whole Direct3D. Called only
601  *  when the video adapter becomes uncooperative. ("Lost" devices)
602  *  @return 1 on success, 0 on failure
603  */
reconfigure_d3d(d3d_priv * priv)604 static int reconfigure_d3d(d3d_priv *priv)
605 {
606     MP_VERBOSE(priv, "reconfigure_d3d called.\n");
607 
608     // Force complete destruction of the D3D state.
609     // Note: this step could be omitted. The resize_d3d call below would detect
610     // that d3d_device is NULL, and would properly recreate it. I'm not sure why
611     // the following code to release and recreate the d3d_handle exists.
612     destroy_d3d(priv);
613     if (!init_d3d(priv))
614         return 0;
615 
616     // Proper re-initialization.
617     if (!resize_d3d(priv))
618         return 0;
619 
620     return 1;
621 }
622 
623 // Resize Direct3D context on window resize.
624 // This function also is called when major initializations need to be done.
resize_d3d(d3d_priv * priv)625 static bool resize_d3d(d3d_priv *priv)
626 {
627     D3DVIEWPORT9 vp = {0, 0, priv->vo->dwidth, priv->vo->dheight, 0, 1};
628 
629     MP_VERBOSE(priv, "resize_d3d %dx%d called.\n",
630                priv->vo->dwidth, priv->vo->dheight);
631 
632     /* Make sure that backbuffer is large enough to accommodate the new
633        viewport dimensions. Grow it if necessary. */
634 
635     bool backbuf_resize = priv->vo->dwidth > priv->cur_backbuf_width ||
636                           priv->vo->dheight > priv->cur_backbuf_height;
637 
638     if (priv->opt_exact_backbuffer) {
639         backbuf_resize = priv->vo->dwidth != priv->cur_backbuf_width ||
640                          priv->vo->dheight != priv->cur_backbuf_height;
641     }
642 
643     if (backbuf_resize || !priv->d3d_device)
644     {
645         destroy_d3d_surfaces(priv);
646         if (!change_d3d_backbuffer(priv))
647             return 0;
648     }
649 
650     if (!priv->d3d_device || !priv->image_format)
651         return 1;
652 
653     if (!create_d3d_surfaces(priv))
654         return 0;
655 
656     if (FAILED(IDirect3DDevice9_SetViewport(priv->d3d_device, &vp))) {
657         MP_ERR(priv, "Setting viewport failed.\n");
658         return 0;
659     }
660 
661     // so that screen coordinates map to D3D ones
662     D3DMATRIX view;
663     d3d_matrix_ortho(&view, 0.5f, vp.Width + 0.5f, vp.Height + 0.5f, 0.5f);
664     IDirect3DDevice9_SetTransform(priv->d3d_device, D3DTS_VIEW, &view);
665 
666     calc_fs_rect(priv);
667     priv->vo->want_redraw = true;
668 
669     return 1;
670 }
671 
672 /** @brief Uninitialize Direct3D and close the window.
673  */
uninit_d3d(d3d_priv * priv)674 static void uninit_d3d(d3d_priv *priv)
675 {
676     MP_VERBOSE(priv, "uninit_d3d called.\n");
677 
678     destroy_d3d(priv);
679 }
680 
d3d_draw_frame(d3d_priv * priv)681 static uint32_t d3d_draw_frame(d3d_priv *priv)
682 {
683     if (!priv->d3d_device)
684         return VO_TRUE;
685 
686     if (!d3d_begin_scene(priv))
687         return VO_ERROR;
688 
689     IDirect3DDevice9_Clear(priv->d3d_device, 0, NULL, D3DCLEAR_TARGET, 0, 0, 0);
690 
691     if (!priv->have_image)
692         goto render_osd;
693 
694     RECT rm = priv->fs_movie_rect;
695     RECT rs = priv->fs_panscan_rect;
696 
697     rs.left &= ~(ULONG)1;
698     rs.top &= ~(ULONG)1;
699     rs.right &= ~(ULONG)1;
700     rs.bottom &= ~(ULONG)1;
701     if (FAILED(IDirect3DDevice9_StretchRect(priv->d3d_device,
702                                             priv->d3d_surface,
703                                             &rs,
704                                             priv->d3d_backbuf,
705                                             &rm,
706                                             D3DTEXF_LINEAR))) {
707         MP_ERR(priv, "Copying frame to the backbuffer failed.\n");
708         return VO_ERROR;
709     }
710 
711 render_osd:
712 
713     draw_osd(priv->vo);
714 
715     return VO_TRUE;
716 }
717 
check_format(d3d_priv * priv,uint32_t movie_fmt)718 static D3DFORMAT check_format(d3d_priv *priv, uint32_t movie_fmt)
719 {
720     const struct fmt_entry *cur = &fmt_table[0];
721 
722     while (cur->mplayer_fmt) {
723         if (cur->mplayer_fmt == movie_fmt) {
724             HRESULT res;
725             /* Test conversion from Movie colorspace to
726             * display's target colorspace. */
727             res = IDirect3D9_CheckDeviceFormatConversion(priv->d3d_handle,
728                         D3DADAPTER_DEFAULT,
729                         DEVTYPE,
730                         cur->fourcc,
731                         priv->desktop_fmt);
732             if (FAILED(res)) {
733                 MP_VERBOSE(priv, "Rejected image format: %s\n",
734                            vo_format_name(cur->mplayer_fmt));
735                 return 0;
736             }
737 
738             MP_DBG(priv, "Accepted image format: %s\n",
739                    vo_format_name(cur->mplayer_fmt));
740 
741             return cur->fourcc;
742         }
743         cur++;
744     }
745 
746     return 0;
747 }
748 
749 // Return if the image format can be used. If it can, decide which rendering
750 // and conversion mode to use.
751 // If initialize is true, actually setup all variables to use the picked
752 // rendering mode.
init_rendering_mode(d3d_priv * priv,uint32_t fmt,bool initialize)753 static bool init_rendering_mode(d3d_priv *priv, uint32_t fmt, bool initialize)
754 {
755     int blit_d3dfmt = check_format(priv, fmt);
756 
757     if (!blit_d3dfmt)
758         return false;
759 
760     MP_VERBOSE(priv, "Accepted rendering methods for "
761            "format='%s': StretchRect=%#x.\n",
762            vo_format_name(fmt), blit_d3dfmt);
763 
764     if (!initialize)
765         return true;
766 
767     // initialization doesn't fail beyond this point
768 
769     priv->movie_src_fmt = 0;
770     priv->image_format = fmt;
771 
772     priv->movie_src_fmt = blit_d3dfmt;
773 
774     return true;
775 }
776 
777 /** @brief Query if movie colorspace is supported by the HW.
778  *  @return 0 on failure, device capabilities (not probed
779  *          currently) on success.
780  */
query_format(struct vo * vo,int movie_fmt)781 static int query_format(struct vo *vo, int movie_fmt)
782 {
783     d3d_priv *priv = vo->priv;
784     if (!init_rendering_mode(priv, movie_fmt, false))
785         return 0;
786 
787     return 1;
788 }
789 
790 /****************************************************************************
791  *                                                                          *
792  *                                                                          *
793  *                                                                          *
794  * libvo Control / Callback functions                                       *
795  *                                                                          *
796  *                                                                          *
797  *                                                                          *
798  ****************************************************************************/
799 
800 
801 /** @brief libvo Callback: Preinitialize the video card.
802  *  Preinit the hardware just enough to be queried about
803  *  supported formats.
804  *
805  *  @return 0 on success, -1 on failure
806  */
807 
preinit(struct vo * vo)808 static int preinit(struct vo *vo)
809 {
810     d3d_priv *priv = vo->priv;
811     priv->vo = vo;
812     priv->log = vo->log;
813 
814     priv->d3d9_dll = LoadLibraryA("d3d9.dll");
815     if (!priv->d3d9_dll) {
816         MP_ERR(priv, "Unable to dynamically load d3d9.dll\n");
817         goto err_out;
818     }
819 
820     priv->pDirect3DCreate9 = (void *)GetProcAddress(priv->d3d9_dll,
821                                                     "Direct3DCreate9");
822     if (!priv->pDirect3DCreate9) {
823         MP_ERR(priv, "Unable to find entry point of Direct3DCreate9\n");
824         goto err_out;
825     }
826 
827     /* w32_common framework call. Configures window on the screen, gets
828      * fullscreen dimensions and does other useful stuff.
829      */
830     if (!vo_w32_init(vo)) {
831         MP_VERBOSE(priv, "Configuring onscreen window failed.\n");
832         goto err_out;
833     }
834 
835     if (!init_d3d(priv))
836         goto err_out;
837 
838     return 0;
839 
840 err_out:
841     uninit(vo);
842     return -1;
843 }
844 
845 /** @brief libvo Callback: Handle control requests.
846  *  @return VO_TRUE on success, VO_NOTIMPL when not implemented
847  */
control(struct vo * vo,uint32_t request,void * data)848 static int control(struct vo *vo, uint32_t request, void *data)
849 {
850     d3d_priv *priv = vo->priv;
851 
852     switch (request) {
853     case VOCTRL_REDRAW_FRAME:
854         d3d_draw_frame(priv);
855         return VO_TRUE;
856     case VOCTRL_SET_PANSCAN:
857         calc_fs_rect(priv);
858         priv->vo->want_redraw = true;
859         return VO_TRUE;
860     case VOCTRL_SCREENSHOT_WIN:
861         *(struct mp_image **)data = get_window_screenshot(priv);
862         return VO_TRUE;
863     }
864 
865     int events = 0;
866     int r = vo_w32_control(vo, &events, request, data);
867 
868     if (events & VO_EVENT_RESIZE)
869         resize_d3d(priv);
870 
871     if (events & VO_EVENT_EXPOSE)
872         vo->want_redraw = true;
873 
874     vo_event(vo, events);
875 
876     return r;
877 }
878 
reconfig(struct vo * vo,struct mp_image_params * params)879 static int reconfig(struct vo *vo, struct mp_image_params *params)
880 {
881     d3d_priv *priv = vo->priv;
882 
883     priv->have_image = false;
884 
885     vo_w32_config(vo);
886 
887     if ((priv->image_format != params->imgfmt)
888         || (priv->src_width != params->w)
889         || (priv->src_height != params->h))
890     {
891         d3d_destroy_video_objects(priv);
892 
893         priv->src_width = params->w;
894         priv->src_height = params->h;
895         priv->params = *params;
896         init_rendering_mode(priv, params->imgfmt, true);
897     }
898 
899     if (!resize_d3d(priv))
900         return VO_ERROR;
901 
902     return 0; /* Success */
903 }
904 
905 /** @brief libvo Callback: Flip next already drawn frame on the
906  *         screen.
907  */
flip_page(struct vo * vo)908 static void flip_page(struct vo *vo)
909 {
910     d3d_priv *priv = vo->priv;
911 
912     if (priv->d3d_device && priv->d3d_in_scene) {
913         if (FAILED(IDirect3DDevice9_EndScene(priv->d3d_device))) {
914             MP_ERR(priv, "EndScene failed.\n");
915         }
916     }
917     priv->d3d_in_scene = false;
918 
919     RECT rect = {0, 0, vo->dwidth, vo->dheight};
920     if (!priv->d3d_device ||
921         FAILED(IDirect3DDevice9_Present(priv->d3d_device, &rect, 0, 0, 0))) {
922         MP_VERBOSE(priv, "Trying to reinitialize uncooperative video adapter.\n");
923         if (!reconfigure_d3d(priv)) {
924             MP_VERBOSE(priv, "Reinitialization failed.\n");
925             return;
926         } else {
927             MP_VERBOSE(priv, "Video adapter reinitialized.\n");
928         }
929     }
930 }
931 
932 /** @brief libvo Callback: Uninitializes all pointers and closes
933  *         all D3D related stuff,
934  */
uninit(struct vo * vo)935 static void uninit(struct vo *vo)
936 {
937     d3d_priv *priv = vo->priv;
938 
939     MP_VERBOSE(priv, "uninit called.\n");
940 
941     uninit_d3d(priv);
942     vo_w32_uninit(vo);
943     if (priv->d3d9_dll)
944         FreeLibrary(priv->d3d9_dll);
945     priv->d3d9_dll = NULL;
946 }
947 
948 // Lock buffers and fill out to point to them.
949 // Must call d3d_unlock_video_objects() to unlock the buffers again.
get_video_buffer(d3d_priv * priv,struct mp_image * out)950 static bool get_video_buffer(d3d_priv *priv, struct mp_image *out)
951 {
952     *out = (struct mp_image) {0};
953     mp_image_set_size(out, priv->src_width, priv->src_height);
954     mp_image_setfmt(out, priv->image_format);
955 
956     if (!priv->d3d_device)
957         return false;
958 
959     if (!priv->locked_rect.pBits) {
960         if (FAILED(IDirect3DSurface9_LockRect(priv->d3d_surface,
961                                               &priv->locked_rect, NULL, 0)))
962         {
963             MP_ERR(priv, "Surface lock failed.\n");
964             return false;
965         }
966     }
967 
968     uint8_t *base = priv->locked_rect.pBits;
969     size_t stride = priv->locked_rect.Pitch;
970 
971     out->planes[0] = base;
972     out->stride[0] = stride;
973 
974     if (out->num_planes == 2) {
975         // NV12, NV21
976         out->planes[1] = base + stride * out->h;
977         out->stride[1] = stride;
978     }
979 
980     if (out->num_planes == 3) {
981         bool swap = priv->movie_src_fmt == MAKEFOURCC('Y','V','1','2');
982 
983         size_t uv_stride = stride / 2;
984         uint8_t *u = base + out->h * stride;
985         uint8_t *v = u + (out->h / 2) * uv_stride;
986 
987         out->planes[1] = swap ? v : u;
988         out->planes[2] = swap ? u : v;
989 
990         out->stride[1] = out->stride[2] = uv_stride;
991     }
992 
993     return true;
994 }
995 
draw_image(struct vo * vo,mp_image_t * mpi)996 static void draw_image(struct vo *vo, mp_image_t *mpi)
997 {
998     d3d_priv *priv = vo->priv;
999     if (!priv->d3d_device)
1000         goto done;
1001 
1002     struct mp_image buffer;
1003     if (!get_video_buffer(priv, &buffer))
1004         goto done;
1005 
1006     mp_image_copy(&buffer, mpi);
1007 
1008     d3d_unlock_video_objects(priv);
1009 
1010     priv->have_image = true;
1011     priv->osd_pts = mpi->pts;
1012 
1013     d3d_draw_frame(priv);
1014 
1015 done:
1016     talloc_free(mpi);
1017 }
1018 
get_window_screenshot(d3d_priv * priv)1019 static mp_image_t *get_window_screenshot(d3d_priv *priv)
1020 {
1021     D3DDISPLAYMODE mode;
1022     mp_image_t *image = NULL;
1023     RECT window_rc;
1024     RECT screen_rc;
1025     RECT visible;
1026     POINT pt;
1027     D3DLOCKED_RECT locked_rect;
1028     int width, height;
1029     IDirect3DSurface9 *surface = NULL;
1030 
1031     if (FAILED(IDirect3DDevice9_GetDisplayMode(priv->d3d_device, 0, &mode))) {
1032         MP_ERR(priv, "GetDisplayMode failed.\n");
1033         goto error_exit;
1034     }
1035 
1036     if (FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface(priv->d3d_device,
1037         mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface,
1038         NULL)))
1039     {
1040         MP_ERR(priv, "Couldn't create surface.\n");
1041         goto error_exit;
1042     }
1043 
1044     if (FAILED(IDirect3DDevice9_GetFrontBufferData(priv->d3d_device, 0,
1045         surface)))
1046     {
1047         MP_ERR(priv, "Couldn't copy frontbuffer.\n");
1048         goto error_exit;
1049     }
1050 
1051     GetClientRect(vo_w32_hwnd(priv->vo), &window_rc);
1052     pt = (POINT) { 0, 0 };
1053     ClientToScreen(vo_w32_hwnd(priv->vo), &pt);
1054     window_rc.left = pt.x;
1055     window_rc.top = pt.y;
1056     window_rc.right += window_rc.left;
1057     window_rc.bottom += window_rc.top;
1058 
1059     screen_rc = (RECT) { 0, 0, mode.Width, mode.Height };
1060 
1061     if (!IntersectRect(&visible, &screen_rc, &window_rc))
1062         goto error_exit;
1063     width = visible.right - visible.left;
1064     height = visible.bottom - visible.top;
1065     if (width < 1 || height < 1)
1066         goto error_exit;
1067 
1068     image = mp_image_alloc(IMGFMT_BGR0, width, height);
1069     if (!image)
1070         goto error_exit;
1071 
1072     IDirect3DSurface9_LockRect(surface, &locked_rect, NULL, 0);
1073 
1074     memcpy_pic(image->planes[0], (char*)locked_rect.pBits + visible.top *
1075                locked_rect.Pitch + visible.left * 4, width * 4, height,
1076                image->stride[0], locked_rect.Pitch);
1077 
1078     IDirect3DSurface9_UnlockRect(surface);
1079     IDirect3DSurface9_Release(surface);
1080 
1081     return image;
1082 
1083 error_exit:
1084     if (image)
1085         talloc_free(image);
1086     if (surface)
1087         IDirect3DSurface9_Release(surface);
1088     return NULL;
1089 }
1090 
update_osd(d3d_priv * priv)1091 static void update_osd(d3d_priv *priv)
1092 {
1093     if (!priv->osd_cache)
1094         priv->osd_cache = mp_draw_sub_alloc(priv, priv->vo->global);
1095 
1096     struct sub_bitmap_list *sbs = osd_render(priv->vo->osd, priv->osd_res,
1097                                              priv->osd_pts, 0, mp_draw_sub_formats);
1098 
1099     struct mp_rect act_rc[MAX_OSD_RECTS], mod_rc[64];
1100     int num_act_rc = 0, num_mod_rc = 0;
1101 
1102     struct mp_image *osd = mp_draw_sub_overlay(priv->osd_cache, sbs,
1103                     act_rc, MP_ARRAY_SIZE(act_rc), &num_act_rc,
1104                     mod_rc, MP_ARRAY_SIZE(mod_rc), &num_mod_rc);
1105 
1106     talloc_free(sbs);
1107 
1108     if (!osd) {
1109         MP_ERR(priv, "Failed to render OSD.\n");
1110         return;
1111     }
1112 
1113     if (!num_mod_rc && priv->osd_texture.system)
1114         return; // nothing changed
1115 
1116     priv->osd_num_vertices = 0;
1117 
1118     if (osd->w > priv->osd_texture.tex_w || osd->h > priv->osd_texture.tex_h) {
1119         int new_w = osd->w;
1120         int new_h = osd->h;
1121         d3d_fix_texture_size(priv, &new_w, &new_h);
1122 
1123         MP_DBG(priv, "reallocate OSD surface to %dx%d.\n", new_w, new_h);
1124 
1125         d3dtex_release(priv, &priv->osd_texture);
1126         if (!d3dtex_allocate(priv, &priv->osd_texture, D3DFMT_A8R8G8B8,
1127                              new_w, new_h))
1128             return;
1129     }
1130 
1131     // Lazy; could/should use the bounding rect, or perform multiple lock calls.
1132     // The previous approach (fully packed texture) was more efficient.
1133     RECT dirty_rc = { 0, 0, priv->osd_texture.w, priv->osd_texture.h };
1134 
1135     D3DLOCKED_RECT locked_rect;
1136 
1137     if (FAILED(IDirect3DTexture9_LockRect(priv->osd_texture.system, 0, &locked_rect,
1138                                           &dirty_rc, 0)))
1139     {
1140         MP_ERR(priv, "OSD texture lock failed.\n");
1141         return;
1142     }
1143 
1144     for (int n = 0; n < num_mod_rc; n++) {
1145         struct mp_rect rc = mod_rc[n];
1146         int w = mp_rect_w(rc);
1147         int h = mp_rect_h(rc);
1148         void *src = mp_image_pixel_ptr(osd, 0, rc.x0, rc.y0);
1149         void *dst = (char *)locked_rect.pBits + locked_rect.Pitch * rc.y0 +
1150                     rc.x0 * 4;
1151         memcpy_pic(dst, src, w * 4, h, locked_rect.Pitch, osd->stride[0]);
1152     }
1153 
1154     if (FAILED(IDirect3DTexture9_UnlockRect(priv->osd_texture.system, 0))) {
1155         MP_ERR(priv, "OSD texture unlock failed.\n");
1156         return;
1157     }
1158 
1159     if (!d3dtex_update(priv, &priv->osd_texture))
1160         return;
1161 
1162     // We need 2 primitives per quad which makes 6 vertices.
1163     priv->osd_num_vertices = num_act_rc * 6;
1164 
1165     float tex_w = priv->osd_texture.tex_w;
1166     float tex_h = priv->osd_texture.tex_h;
1167 
1168     for (int n = 0; n < num_act_rc; n++) {
1169         struct mp_rect rc = act_rc[n];
1170 
1171         float tx0 = rc.x0 / tex_w;
1172         float ty0 = rc.y0 / tex_h;
1173         float tx1 = rc.x1 / tex_w;
1174         float ty1 = rc.y1 / tex_h;
1175 
1176         vertex_osd *v = &priv->osd_vertices[n * 6];
1177         v[0] = (vertex_osd) { rc.x0, rc.y0, 0, tx0, ty0 };
1178         v[1] = (vertex_osd) { rc.x1, rc.y0, 0, tx1, ty0 };
1179         v[2] = (vertex_osd) { rc.x0, rc.y1, 0, tx0, ty1 };
1180         v[3] = (vertex_osd) { rc.x1, rc.y1, 0, tx1, ty1 };
1181         v[4] = v[2];
1182         v[5] = v[1];
1183     }
1184 }
1185 
draw_osd(struct vo * vo)1186 static void draw_osd(struct vo *vo)
1187 {
1188     d3d_priv *priv = vo->priv;
1189     if (!priv->d3d_device)
1190         return;
1191 
1192     update_osd(priv);
1193 
1194     if (!priv->osd_num_vertices)
1195         return;
1196 
1197     d3d_begin_scene(priv);
1198 
1199     IDirect3DDevice9_SetRenderState(priv->d3d_device,
1200                                     D3DRS_ALPHABLENDENABLE, TRUE);
1201 
1202     IDirect3DDevice9_SetTexture(priv->d3d_device, 0,
1203                         d3dtex_get_render_texture(priv, &priv->osd_texture));
1204 
1205     IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_SRCBLEND,
1206                                     D3DBLEND_ONE);
1207 
1208     IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_OSD_VERTEX);
1209     IDirect3DDevice9_DrawPrimitiveUP(priv->d3d_device, D3DPT_TRIANGLELIST,
1210                                      priv->osd_num_vertices / 3,
1211                                      priv->osd_vertices, sizeof(vertex_osd));
1212 
1213     IDirect3DDevice9_SetRenderState(priv->d3d_device,
1214                                     D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
1215 
1216     IDirect3DDevice9_SetTexture(priv->d3d_device, 0, NULL);
1217 
1218     IDirect3DDevice9_SetRenderState(priv->d3d_device,
1219                                     D3DRS_ALPHABLENDENABLE, FALSE);
1220 }
1221 
1222 #define OPT_BASE_STRUCT d3d_priv
1223 
1224 static const struct m_option opts[] = {
1225     {"force-power-of-2", OPT_FLAG(opt_force_power_of_2)},
1226     {"disable-texture-align", OPT_FLAG(opt_disable_texture_align)},
1227     {"texture-memory", OPT_CHOICE(opt_texture_memory,
1228         {"default", 0},
1229         {"managed", 1},
1230         {"default-pool", 2},
1231         {"default-pool-shadow", 3},
1232         {"scratch", 4})},
1233     {"swap-discard", OPT_FLAG(opt_swap_discard)},
1234     {"exact-backbuffer", OPT_FLAG(opt_exact_backbuffer)},
1235     {0}
1236 };
1237 
1238 const struct vo_driver video_out_direct3d = {
1239     .description = "Direct3D 9 Renderer",
1240     .name = "direct3d",
1241     .preinit = preinit,
1242     .query_format = query_format,
1243     .reconfig = reconfig,
1244     .control = control,
1245     .draw_image = draw_image,
1246     .flip_page = flip_page,
1247     .uninit = uninit,
1248     .priv_size = sizeof(d3d_priv),
1249     .options = opts,
1250     .options_prefix = "vo-direct3d",
1251 };
1252