1 /*****************************************************************************
2  * direct3d9.c: Windows Direct3D9 video output module
3  *****************************************************************************
4  * Copyright (C) 2006-2014 VLC authors and VideoLAN
5  *$Id: 669af0f27d06b2ac0714e2b2673eddf5a89ddd61 $
6  *
7  * Authors: Martell Malone <martellmalone@gmail.com>,
8  *          Damien Fouilleul <damienf@videolan.org>,
9  *          Sasha Koruga <skoruga@gmail.com>,
10  *          Felix Abecassis <felix.abecassis@gmail.com>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26 
27 /*****************************************************************************
28  * Preamble:
29  *
30  * This plugin will use YUV surface if supported, using YUV will result in
31  * the best video quality (hardware filtering when rescaling the picture)
32  * and the fastest display as it requires less processing.
33  *
34  * If YUV overlay is not supported this plugin will use RGB offscreen video
35  * surfaces that will be blitted onto the primary surface (display) to
36  * effectively display the pictures.
37  *
38  *****************************************************************************/
39 #ifdef HAVE_CONFIG_H
40 # include "config.h"
41 #endif
42 
43 // For dynamic use of DXVA-HD
44 #if _WIN32_WINNT < 0x0601 // _WIN32_WINNT_WIN7
45 # undef _WIN32_WINNT
46 # define _WIN32_WINNT _WIN32_WINNT_WIN7
47 #endif
48 
49 #include <vlc_common.h>
50 #include <vlc_plugin.h>
51 #include <vlc_vout_display.h>
52 #include <vlc_charset.h> /* ToT function */
53 
54 #include <windows.h>
55 #include <d3d9.h>
56 #ifdef HAVE_D3DX9EFFECT_H
57 #include <d3dx9effect.h>
58 #endif
59 #include "../../video_chroma/d3d9_fmt.h"
60 #include <dxvahd.h>
61 
62 #include "common.h"
63 #include "builtin_shaders.h"
64 
65 #include <assert.h>
66 
67 /*****************************************************************************
68  * Module descriptor
69  *****************************************************************************/
70 static int  Open(vlc_object_t *);
71 static void Close(vlc_object_t *);
72 
73 static int  GLConvOpen(vlc_object_t *);
74 static void GLConvClose(vlc_object_t *);
75 
76 #define DESKTOP_LONGTEXT N_(\
77     "The desktop mode allows you to display the video on the desktop.")
78 
79 #define HW_BLENDING_TEXT N_("Use hardware blending support")
80 #define HW_BLENDING_LONGTEXT N_(\
81     "Try to use hardware acceleration for subtitle/OSD blending.")
82 
83 #define PIXEL_SHADER_TEXT N_("Pixel Shader")
84 #define PIXEL_SHADER_LONGTEXT N_(\
85         "Choose a pixel shader to apply.")
86 #define PIXEL_SHADER_FILE_TEXT N_("Path to HLSL file")
87 #define PIXEL_SHADER_FILE_LONGTEXT N_("Path to an HLSL file containing a single pixel shader.")
88 /* The latest option in the selection list: used for loading a shader file. */
89 #define SELECTED_SHADER_FILE N_("HLSL File")
90 
91 #define D3D9_HELP N_("Recommended video output for Windows Vista and later versions")
92 
93 static int FindShadersCallback(vlc_object_t *, const char *,
94                                char ***, char ***);
95 
96 vlc_module_begin ()
97     set_shortname("Direct3D9")
98     set_description(N_("Direct3D9 video output"))
99     set_help(D3D9_HELP)
100     set_category(CAT_VIDEO)
101     set_subcategory(SUBCAT_VIDEO_VOUT)
102 
103     add_bool("direct3d9-hw-blending", true, HW_BLENDING_TEXT, HW_BLENDING_LONGTEXT, true)
104 
105     add_string("direct3d9-shader", "", PIXEL_SHADER_TEXT, PIXEL_SHADER_LONGTEXT, true)
106         change_string_cb(FindShadersCallback)
107     add_loadfile("direct3d9-shader-file", NULL, PIXEL_SHADER_FILE_TEXT, PIXEL_SHADER_FILE_LONGTEXT, false)
108 
109     set_capability("vout display", 280)
110     add_shortcut("direct3d9", "direct3d")
111     set_callbacks(Open, Close)
112 
113 #ifdef HAVE_GL
114     add_submodule()
115     set_description("DX OpenGL surface converter for D3D9")
116     set_capability("glconv", 1)
117     set_callbacks(GLConvOpen, GLConvClose)
118 #endif
119 vlc_module_end ()
120 
121 /*****************************************************************************
122  * Local prototypes.
123  *****************************************************************************/
124 static const vlc_fourcc_t d3d_subpicture_chromas[] = {
125     VLC_CODEC_RGBA,
126     0
127 };
128 
129 typedef struct
130 {
131     const char   *name;
132     D3DFORMAT    format;    /* D3D format */
133     vlc_fourcc_t fourcc;    /* VLC fourcc */
134     uint32_t     rmask;
135     uint32_t     gmask;
136     uint32_t     bmask;
137 } d3d9_format_t;
138 
139 struct vout_display_sys_t
140 {
141     vout_display_sys_win32_t sys;
142 
143     bool allow_hw_yuv;    /* Should we use hardware YUV->RGB conversions */
144     struct {
145         bool is_fullscreen;
146         bool is_on_top;
147         RECT win;
148     } desktop_save;
149     vout_display_cfg_t cfg_saved; /* configuration used before going into desktop mode */
150 
151     // core objects
152     d3d9_handle_t           hd3d;
153     HINSTANCE               hxdll;      /* handle of the opened d3d9x dll */
154     IDirect3DPixelShader9*  d3dx_shader;
155     d3d9_device_t           d3d_dev;
156 
157     // scene objects
158     LPDIRECT3DTEXTURE9      d3dtex;
159     LPDIRECT3DVERTEXBUFFER9 d3dvtc;
160     D3DFORMAT               d3dregion_format;    /* Backbuffer output format */
161     int                     d3dregion_count;
162     struct d3d_region_t     *d3dregion;
163     const d3d9_format_t      *d3dtexture_format;  /* Rendering texture(s) format */
164 
165     /* */
166     bool                    reset_device;
167     bool                    reopen_device;
168     bool                    lost_not_ready;
169     bool                    clear_scene;
170 
171     /* It protects the following variables */
172     vlc_mutex_t    lock;
173     bool           ch_desktop;
174     bool           desktop_requested;
175 
176     /* range converter */
177     struct {
178         HMODULE                 dll;
179         IDXVAHD_VideoProcessor *proc;
180     } processor;
181 };
182 
183 static const d3d9_format_t *Direct3DFindFormat(vout_display_t *vd, vlc_fourcc_t chroma, D3DFORMAT target);
184 static const d3d9_format_t *FindBufferFormat(vout_display_t *, D3DFORMAT);
185 
186 static int  Open(vlc_object_t *);
187 
188 static picture_pool_t *Direct3D9CreatePicturePool  (vlc_object_t *, d3d9_device_t *,
189      const d3d9_format_t *, const video_format_t *, unsigned);
190 
191 static void           Prepare(vout_display_t *, picture_t *, subpicture_t *subpicture);
192 static void           Display(vout_display_t *, picture_t *, subpicture_t *subpicture);
193 static picture_pool_t*DisplayPool(vout_display_t *, unsigned);
194 static int            Control(vout_display_t *, int, va_list);
195 static void           Manage (vout_display_t *);
196 
197 static int  Direct3D9Reset  (vout_display_t *);
198 static void Direct3D9Destroy(vout_display_sys_t *);
199 
200 static int  Direct3D9Open (vout_display_t *, video_format_t *);
201 static void Direct3D9Close(vout_display_t *);
202 
203 /* */
204 typedef struct
205 {
206     FLOAT       x,y,z;      // vertex untransformed position
207     FLOAT       rhw;        // eye distance
208     D3DCOLOR    diffuse;    // diffuse color
209     FLOAT       tu, tv;     // texture relative coordinates
210 } CUSTOMVERTEX;
211 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
212 
213 typedef struct d3d_region_t {
214     D3DFORMAT          format;
215     unsigned           width;
216     unsigned           height;
217     CUSTOMVERTEX       vertex[4];
218     LPDIRECT3DTEXTURE9 texture;
219 } d3d_region_t;
220 
221 static void Direct3D9DeleteRegions(int, d3d_region_t *);
222 
223 static int  Direct3D9ImportPicture(vout_display_t *vd, d3d_region_t *, LPDIRECT3DSURFACE9 surface);
224 static void Direct3D9ImportSubpicture(vout_display_t *vd, int *, d3d_region_t **, subpicture_t *);
225 
226 static void Direct3D9RenderScene(vout_display_t *vd, d3d_region_t *, int, d3d_region_t *);
227 
228 /* */
229 static int DesktopCallback(vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void *);
230 
is_d3d9_opaque(vlc_fourcc_t chroma)231 static bool is_d3d9_opaque(vlc_fourcc_t chroma)
232 {
233     switch (chroma)
234     {
235     case VLC_CODEC_D3D9_OPAQUE:
236     case VLC_CODEC_D3D9_OPAQUE_10B:
237         return true;
238     default:
239         return false;
240     }
241 }
242 
Direct3D9LoadShaderLibrary(void)243 static HINSTANCE Direct3D9LoadShaderLibrary(void)
244 {
245     HINSTANCE instance = NULL;
246     for (int i = 43; i > 23; --i) {
247         TCHAR filename[16];
248         _sntprintf(filename, 16, TEXT("D3dx9_%d.dll"), i);
249         instance = LoadLibrary(filename);
250         if (instance)
251             break;
252     }
253     return instance;
254 }
255 
256 /**
257  * It creates a Direct3D vout display.
258  */
Open(vlc_object_t * object)259 static int Open(vlc_object_t *object)
260 {
261     vout_display_t *vd = (vout_display_t *)object;
262     vout_display_sys_t *sys;
263 
264     if ( !vd->obj.force && vd->source.projection_mode != PROJECTION_MODE_RECTANGULAR)
265         return VLC_EGENERIC; /* let a module who can handle it do it */
266 
267     if ( !vd->obj.force && vd->source.mastering.max_luminance != 0)
268         return VLC_EGENERIC; /* let a module who can handle it do it */
269 
270 #if !VLC_WINSTORE_APP
271     /* do not use D3D9 on XP unless forced */
272     if (!vd->obj.force)
273     {
274         bool isVistaOrGreater = false;
275         HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32.dll"));
276         if (likely(hKernel32 != NULL))
277             isVistaOrGreater = GetProcAddress(hKernel32, "EnumResourceLanguagesExW") != NULL;
278         if (!isVistaOrGreater)
279             return VLC_EGENERIC;
280     }
281 #endif
282 
283     /* Allocate structure */
284     vd->sys = sys = calloc(1, sizeof(vout_display_sys_t));
285     if (!sys)
286         return VLC_ENOMEM;
287 
288     if (D3D9_Create(vd, &sys->hd3d)) {
289         msg_Err(vd, "Direct3D9 could not be initialized");
290         free(sys);
291         return VLC_EGENERIC;
292     }
293 
294     sys->hxdll = Direct3D9LoadShaderLibrary();
295     if (!sys->hxdll)
296         msg_Warn(object, "cannot load Direct3D9 Shader Library; HLSL pixel shading will be disabled.");
297 
298     sys->sys.use_desktop = var_CreateGetBool(vd, "video-wallpaper");
299     sys->reset_device = false;
300     sys->reopen_device = false;
301     sys->lost_not_ready = false;
302     sys->allow_hw_yuv = var_CreateGetBool(vd, "directx-hw-yuv");
303     sys->desktop_save.is_fullscreen = vd->cfg->is_fullscreen;
304     sys->desktop_save.is_on_top     = false;
305     sys->desktop_save.win.left      = var_InheritInteger(vd, "video-x");
306     sys->desktop_save.win.right     = vd->cfg->display.width;
307     sys->desktop_save.win.top       = var_InheritInteger(vd, "video-y");
308     sys->desktop_save.win.bottom    = vd->cfg->display.height;
309 
310     if (CommonInit(vd))
311         goto error;
312 
313     /* */
314     video_format_t fmt;
315     if (Direct3D9Open(vd, &fmt)) {
316         msg_Err(vd, "Direct3D9 could not be opened");
317         goto error;
318     }
319 
320     /* */
321     vout_display_info_t info = vd->info;
322     info.is_slow = !is_d3d9_opaque(fmt.i_chroma);
323     info.has_double_click = true;
324     info.has_pictures_invalid = !is_d3d9_opaque(fmt.i_chroma);
325     if (var_InheritBool(vd, "direct3d9-hw-blending") &&
326         sys->d3dregion_format != D3DFMT_UNKNOWN &&
327         (sys->d3d_dev.caps.SrcBlendCaps  & D3DPBLENDCAPS_SRCALPHA) &&
328         (sys->d3d_dev.caps.DestBlendCaps & D3DPBLENDCAPS_INVSRCALPHA) &&
329         (sys->d3d_dev.caps.TextureCaps   & D3DPTEXTURECAPS_ALPHA) &&
330         (sys->d3d_dev.caps.TextureOpCaps & D3DTEXOPCAPS_SELECTARG1) &&
331         (sys->d3d_dev.caps.TextureOpCaps & D3DTEXOPCAPS_MODULATE))
332         info.subpicture_chromas = d3d_subpicture_chromas;
333     else
334         info.subpicture_chromas = NULL;
335 
336     /* Interaction */
337     vlc_mutex_init(&sys->lock);
338     sys->ch_desktop = false;
339     sys->desktop_requested = sys->sys.use_desktop;
340 
341     vlc_value_t val;
342     val.psz_string = _("Desktop");
343     var_Change(vd, "video-wallpaper", VLC_VAR_SETTEXT, &val, NULL);
344     var_AddCallback(vd, "video-wallpaper", DesktopCallback, NULL);
345 
346     /* Setup vout_display now that everything is fine */
347     video_format_Clean(&vd->fmt);
348     video_format_Copy(&vd->fmt, &fmt);
349     vd->info = info;
350 
351     vd->pool = DisplayPool;
352     vd->prepare = Prepare;
353     vd->display = Display;
354     vd->control = Control;
355     vd->manage  = Manage;
356 
357     /* Fix state in case of desktop mode */
358     if (sys->sys.use_desktop && vd->cfg->is_fullscreen)
359         vout_display_SendEventFullscreen(vd, false, false);
360 
361     return VLC_SUCCESS;
362 error:
363     Direct3D9Close(vd);
364     CommonClean(vd);
365     Direct3D9Destroy(sys);
366     free(vd->sys);
367     return VLC_EGENERIC;
368 }
369 
370 /**
371  * It destroyes a Direct3D vout display.
372  */
Close(vlc_object_t * object)373 static void Close(vlc_object_t *object)
374 {
375     vout_display_t * vd = (vout_display_t *)object;
376 
377     var_DelCallback(vd, "video-wallpaper", DesktopCallback, NULL);
378     vlc_mutex_destroy(&vd->sys->lock);
379 
380     Direct3D9Close(vd);
381 
382     CommonClean(vd);
383 
384     Direct3D9Destroy(vd->sys);
385 
386     free(vd->sys);
387 }
388 
DestroyPicture(picture_t * picture)389 static void DestroyPicture(picture_t *picture)
390 {
391     ReleasePictureSys(picture->p_sys);
392 
393     free(picture->p_sys);
394     free(picture);
395 }
396 
397 /**
398  * It locks the surface associated to the picture and get the surface
399  * descriptor which amongst other things has the pointer to the picture
400  * data and its pitch.
401  */
Direct3D9LockSurface(picture_t * picture)402 static int Direct3D9LockSurface(picture_t *picture)
403 {
404     /* Lock the surface to get a valid pointer to the picture buffer */
405     D3DLOCKED_RECT d3drect;
406     HRESULT hr = IDirect3DSurface9_LockRect(picture->p_sys->surface, &d3drect, NULL, 0);
407     if (FAILED(hr)) {
408         return VLC_EGENERIC;
409     }
410 
411     CommonUpdatePicture(picture, NULL, d3drect.pBits, d3drect.Pitch);
412     return VLC_SUCCESS;
413 }
414 /**
415  * It unlocks the surface associated to the picture.
416  */
Direct3D9UnlockSurface(picture_t * picture)417 static void Direct3D9UnlockSurface(picture_t *picture)
418 {
419     /* Unlock the Surface */
420     HRESULT hr = IDirect3DSurface9_UnlockRect(picture->p_sys->surface);
421     if (FAILED(hr)) {
422         //msg_Dbg(vd, "Failed IDirect3DSurface9_UnlockRect: 0x%0lx", hr);
423     }
424 }
425 
426 /* */
Direct3D9CreatePicturePool(vlc_object_t * o,d3d9_device_t * p_d3d9_dev,const d3d9_format_t * default_d3dfmt,const video_format_t * fmt,unsigned count)427 static picture_pool_t *Direct3D9CreatePicturePool(vlc_object_t *o,
428     d3d9_device_t *p_d3d9_dev, const d3d9_format_t *default_d3dfmt, const video_format_t *fmt, unsigned count)
429 {
430     picture_pool_t*   pool = NULL;
431     picture_t**       pictures = NULL;
432     unsigned          picture_count = 0;
433 
434     pictures = calloc(count, sizeof(*pictures));
435     if (!pictures)
436         goto error;
437 
438     D3DFORMAT format;
439     switch (fmt->i_chroma)
440     {
441     case VLC_CODEC_D3D9_OPAQUE_10B:
442         format = MAKEFOURCC('P','0','1','0');
443         break;
444     case VLC_CODEC_D3D9_OPAQUE:
445         format = MAKEFOURCC('N','V','1','2');
446         break;
447     default:
448         if (!default_d3dfmt)
449             goto error;
450         format = default_d3dfmt->format;
451         break;
452     }
453 
454     for (picture_count = 0; picture_count < count; ++picture_count)
455     {
456         picture_sys_t *picsys = malloc(sizeof(*picsys));
457         if (unlikely(picsys == NULL))
458             goto error;
459         memset(picsys, 0, sizeof(*picsys));
460 
461         HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(p_d3d9_dev->dev,
462                                                           fmt->i_width,
463                                                           fmt->i_height,
464                                                           format,
465                                                           D3DPOOL_DEFAULT,
466                                                           &picsys->surface,
467                                                           NULL);
468         if (FAILED(hr)) {
469            msg_Err(o, "Failed to allocate surface %d (hr=0x%0lx)", picture_count, hr);
470            free(picsys);
471            goto error;
472         }
473 
474         picture_resource_t resource = {
475             .p_sys = picsys,
476             .pf_destroy = DestroyPicture,
477         };
478 
479         picture_t *picture = picture_NewFromResource(fmt, &resource);
480         if (unlikely(picture == NULL)) {
481             free(picsys);
482             goto error;
483         }
484 
485         pictures[picture_count] = picture;
486     }
487 
488     picture_pool_configuration_t pool_cfg;
489     memset(&pool_cfg, 0, sizeof(pool_cfg));
490     pool_cfg.picture_count = count;
491     pool_cfg.picture       = pictures;
492     if( !is_d3d9_opaque( fmt->i_chroma ) )
493     {
494         pool_cfg.lock = Direct3D9LockSurface;
495         pool_cfg.unlock = Direct3D9UnlockSurface;
496     }
497 
498     pool = picture_pool_NewExtended( &pool_cfg );
499 
500 error:
501     if (pool == NULL && pictures) {
502         for (unsigned i=0;i<picture_count; ++i)
503             DestroyPicture(pictures[i]);
504     }
505     free(pictures);
506     return pool;
507 }
508 
DisplayPool(vout_display_t * vd,unsigned count)509 static picture_pool_t *DisplayPool(vout_display_t *vd, unsigned count)
510 {
511     if ( vd->sys->sys.pool != NULL )
512         return vd->sys->sys.pool;
513     vd->sys->sys.pool = Direct3D9CreatePicturePool(VLC_OBJECT(vd), &vd->sys->d3d_dev,
514         vd->sys->d3dtexture_format, &vd->fmt, count);
515     return vd->sys->sys.pool;
516 }
517 
Prepare(vout_display_t * vd,picture_t * picture,subpicture_t * subpicture)518 static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
519 {
520     vout_display_sys_t *sys = vd->sys;
521     LPDIRECT3DSURFACE9 surface = picture->p_sys->surface;
522     d3d9_device_t *p_d3d9_dev = &sys->d3d_dev;
523 
524     /* FIXME it is a bit ugly, we need the surface to be unlocked for
525      * rendering.
526      *  The clean way would be to release the picture (and ensure that
527      * the vout doesn't keep a reference). But because of the vout
528      * wrapper, we can't */
529     if ( !is_d3d9_opaque(picture->format.i_chroma) )
530         Direct3D9UnlockSurface(picture);
531     else if (picture->context)
532     {
533         const struct va_pic_context *pic_ctx = (struct va_pic_context*)picture->context;
534         if (pic_ctx->picsys.surface != surface)
535         {
536             D3DSURFACE_DESC srcDesc, dstDesc;
537             IDirect3DSurface9_GetDesc(pic_ctx->picsys.surface, &srcDesc);
538             IDirect3DSurface9_GetDesc(surface, &dstDesc);
539             if ( srcDesc.Width == dstDesc.Width && srcDesc.Height == dstDesc.Height )
540                 surface = pic_ctx->picsys.surface;
541             else
542             {
543                 HRESULT hr;
544                 RECT visibleSource;
545                 visibleSource.left = 0;
546                 visibleSource.top = 0;
547                 visibleSource.right = picture->format.i_visible_width;
548                 visibleSource.bottom = picture->format.i_visible_height;
549 
550                 hr = IDirect3DDevice9_StretchRect( p_d3d9_dev->dev, pic_ctx->picsys.surface, &visibleSource, surface, &visibleSource, D3DTEXF_NONE);
551                 if (FAILED(hr)) {
552                     msg_Err(vd, "Failed to copy the hw surface to the decoder surface (hr=0x%0lx)", hr );
553                 }
554             }
555         }
556     }
557 
558     /* check if device is still available */
559     HRESULT hr = IDirect3DDevice9_TestCooperativeLevel(p_d3d9_dev->dev);
560     if (FAILED(hr)) {
561         if (hr == D3DERR_DEVICENOTRESET && !sys->reset_device) {
562             vout_display_SendEventPicturesInvalid(vd);
563             sys->reset_device = true;
564             sys->lost_not_ready = false;
565         }
566         if (hr == D3DERR_DEVICELOST && !sys->lost_not_ready) {
567             /* Device is lost but not yet ready for reset. */
568             sys->lost_not_ready = true;
569         }
570         return;
571     }
572 
573     d3d_region_t picture_region;
574     if (!Direct3D9ImportPicture(vd, &picture_region, surface)) {
575         picture_region.width = picture->format.i_visible_width;
576         picture_region.height = picture->format.i_visible_height;
577         int subpicture_region_count     = 0;
578         d3d_region_t *subpicture_region = NULL;
579         if (subpicture)
580             Direct3D9ImportSubpicture(vd, &subpicture_region_count, &subpicture_region,
581                                      subpicture);
582 
583         Direct3D9RenderScene(vd, &picture_region,
584                             subpicture_region_count, subpicture_region);
585 
586         Direct3D9DeleteRegions(sys->d3dregion_count, sys->d3dregion);
587         sys->d3dregion_count = subpicture_region_count;
588         sys->d3dregion       = subpicture_region;
589     }
590 }
591 
Display(vout_display_t * vd,picture_t * picture,subpicture_t * subpicture)592 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
593 {
594     vout_display_sys_t *sys = vd->sys;
595     const d3d9_device_t *p_d3d9_dev = &sys->d3d_dev;
596 
597     if (sys->lost_not_ready) {
598         picture_Release(picture);
599         if (subpicture)
600             subpicture_Delete(subpicture);
601         return;
602     }
603 
604     // Present the back buffer contents to the display
605     // No stretching should happen here !
606     const RECT src = sys->sys.rect_dest_clipped;
607     const RECT dst = sys->sys.rect_dest_clipped;
608 
609     HRESULT hr;
610     if (sys->hd3d.use_ex) {
611         hr = IDirect3DDevice9Ex_PresentEx(p_d3d9_dev->devex, &src, &dst, NULL, NULL, 0);
612     } else {
613         hr = IDirect3DDevice9_Present(p_d3d9_dev->dev, &src, &dst, NULL, NULL);
614     }
615     if (FAILED(hr)) {
616         msg_Dbg(vd, "Failed IDirect3DDevice9_Present: 0x%0lx", hr);
617     }
618 
619     /* XXX See Prepare() */
620     if ( !is_d3d9_opaque(picture->format.i_chroma) )
621         Direct3D9LockSurface(picture);
622     picture_Release(picture);
623     if (subpicture)
624         subpicture_Delete(subpicture);
625 
626     CommonDisplay(vd);
627 }
628 
ControlReopenDevice(vout_display_t * vd)629 static int ControlReopenDevice(vout_display_t *vd)
630 {
631     vout_display_sys_t *sys = vd->sys;
632 
633     if (!sys->sys.use_desktop) {
634         /* Save non-desktop state */
635         sys->desktop_save.is_fullscreen = vd->cfg->is_fullscreen;
636         sys->desktop_save.is_on_top     = sys->sys.is_on_top;
637 
638         WINDOWPLACEMENT wp = { .length = sizeof(wp), };
639         GetWindowPlacement(sys->sys.hparent ? sys->sys.hparent : sys->sys.hwnd, &wp);
640         sys->desktop_save.win = wp.rcNormalPosition;
641     }
642 
643     /* */
644     Direct3D9Close(vd);
645     EventThreadStop(sys->sys.event);
646 
647     /* */
648     vlc_mutex_lock(&sys->lock);
649     sys->sys.use_desktop = sys->desktop_requested;
650     sys->ch_desktop = false;
651     vlc_mutex_unlock(&sys->lock);
652 
653     /* */
654     event_cfg_t cfg;
655     memset(&cfg, 0, sizeof(cfg));
656     cfg.use_desktop = sys->sys.use_desktop;
657     if (!sys->sys.use_desktop) {
658         cfg.x      = sys->desktop_save.win.left;
659         cfg.y      = sys->desktop_save.win.top;
660         cfg.width  = sys->desktop_save.win.right  - sys->desktop_save.win.left;
661         cfg.height = sys->desktop_save.win.bottom - sys->desktop_save.win.top;
662     }
663 
664     event_hwnd_t hwnd;
665     if (EventThreadStart(sys->sys.event, &hwnd, &cfg)) {
666         msg_Err(vd, "Failed to restart event thread");
667         return VLC_EGENERIC;
668     }
669     sys->sys.parent_window = hwnd.parent_window;
670     sys->sys.hparent       = hwnd.hparent;
671     sys->sys.hwnd          = hwnd.hwnd;
672     sys->sys.hvideownd     = hwnd.hvideownd;
673     sys->sys.hfswnd        = hwnd.hfswnd;
674     SetRectEmpty(&sys->sys.rect_parent);
675 
676     /* */
677     video_format_t fmt;
678     if (Direct3D9Open(vd, &fmt)) {
679         CommonClean(vd);
680         msg_Err(vd, "Failed to reopen device");
681         return VLC_EGENERIC;
682     }
683     vd->fmt = fmt;
684     sys->sys.is_first_display = true;
685 
686     if (sys->sys.use_desktop) {
687         /* Disable fullscreen/on_top while using desktop */
688         if (sys->desktop_save.is_fullscreen)
689             vout_display_SendEventFullscreen(vd, false, false);
690         if (sys->desktop_save.is_on_top)
691             vout_display_SendWindowState(vd, VOUT_WINDOW_STATE_NORMAL);
692     } else {
693         /* Restore fullscreen/on_top */
694         if (sys->desktop_save.is_fullscreen)
695             vout_display_SendEventFullscreen(vd, true, false);
696         if (sys->desktop_save.is_on_top)
697             vout_display_SendWindowState(vd, VOUT_WINDOW_STATE_ABOVE);
698     }
699     return VLC_SUCCESS;
700 }
Control(vout_display_t * vd,int query,va_list args)701 static int Control(vout_display_t *vd, int query, va_list args)
702 {
703     vout_display_sys_t *sys = vd->sys;
704 
705     switch (query) {
706     case VOUT_DISPLAY_RESET_PICTURES:
707         /* FIXME what to do here in case of failure */
708         if (sys->reset_device) {
709             if (Direct3D9Reset(vd)) {
710                 msg_Err(vd, "Failed to reset device");
711                 return VLC_EGENERIC;
712             }
713             sys->reset_device = false;
714         } else if(sys->reopen_device) {
715             if (ControlReopenDevice(vd)) {
716                 msg_Err(vd, "Failed to reopen device");
717                 return VLC_EGENERIC;
718             }
719             sys->reopen_device = false;
720         }
721         return VLC_SUCCESS;
722     default:
723         return CommonControl(vd, query, args);
724     }
725 }
Manage(vout_display_t * vd)726 static void Manage (vout_display_t *vd)
727 {
728     vout_display_sys_t *sys = vd->sys;
729 
730     CommonManage(vd);
731 
732     /* Desktop mode change */
733     vlc_mutex_lock(&sys->lock);
734     const bool ch_desktop = sys->ch_desktop;
735     sys->ch_desktop = false;
736     vlc_mutex_unlock(&sys->lock);
737 
738     if (ch_desktop) {
739         sys->reopen_device = true;
740         if (vd->info.has_pictures_invalid)
741             vout_display_SendEventPicturesInvalid(vd);
742     }
743 
744     /* Position Change */
745     if (sys->sys.changes & DX_POSITION_CHANGE) {
746 #if 0 /* need that when bicubic filter is available */
747         RECT rect;
748         UINT width, height;
749 
750         GetClientRect(p_sys->sys.hvideownd, &rect);
751         width  = rect.right-rect.left;
752         height = rect.bottom-rect.top;
753 
754         if (width != p_sys->pp.BackBufferWidth || height != p_sys->pp.BackBufferHeight)
755         {
756             msg_Dbg(vd, "resizing device back buffers to (%lux%lu)", width, height);
757             // need to reset D3D device to resize back buffer
758             if (VLC_SUCCESS != Direct3D9ResetDevice(vd, width, height))
759                 return VLC_EGENERIC;
760         }
761 #endif
762         sys->clear_scene = true;
763         sys->sys.changes &= ~DX_POSITION_CHANGE;
764     }
765 }
766 
767 /**
768  * It releases an instance of Direct3D9
769  */
Direct3D9Destroy(vout_display_sys_t * sys)770 static void Direct3D9Destroy(vout_display_sys_t *sys)
771 {
772     if (sys->processor.proc)
773     {
774         IDXVAHD_VideoProcessor_Release(sys->processor.proc);
775         FreeLibrary(sys->processor.dll);
776     }
777     D3D9_Destroy( &sys->hd3d );
778 
779     if (sys->hxdll)
780     {
781         FreeLibrary(sys->hxdll);
782         sys->hxdll = NULL;
783     }
784 }
785 
786 /* */
787 static int  Direct3D9CreateResources (vout_display_t *, video_format_t *);
788 static void Direct3D9DestroyResources(vout_display_t *);
789 
SetupProcessorInput(vout_display_t * vd,const video_format_t * fmt,const d3d9_format_t * d3dfmt)790 static void SetupProcessorInput(vout_display_t *vd, const video_format_t *fmt, const d3d9_format_t *d3dfmt)
791 {
792     vout_display_sys_t *sys = vd->sys;
793     HRESULT hr;
794     DXVAHD_STREAM_STATE_D3DFORMAT_DATA d3dformat = { d3dfmt->format };
795     hr = IDXVAHD_VideoProcessor_SetVideoProcessStreamState( sys->processor.proc, 0, DXVAHD_STREAM_STATE_D3DFORMAT, sizeof(d3dformat), &d3dformat );
796 
797     DXVAHD_STREAM_STATE_FRAME_FORMAT_DATA frame_format = { DXVAHD_FRAME_FORMAT_PROGRESSIVE };
798     hr = IDXVAHD_VideoProcessor_SetVideoProcessStreamState( sys->processor.proc, 0, DXVAHD_STREAM_STATE_FRAME_FORMAT, sizeof(frame_format), &frame_format );
799 
800     DXVAHD_STREAM_STATE_INPUT_COLOR_SPACE_DATA colorspace = { 0 };
801     colorspace.RGB_Range = fmt->b_color_range_full ? 0 : 1;
802     colorspace.YCbCr_xvYCC = fmt->b_color_range_full ? 1 : 0;
803     colorspace.YCbCr_Matrix = fmt->space == COLOR_SPACE_BT601 ? 0 : 1;
804     hr = IDXVAHD_VideoProcessor_SetVideoProcessStreamState( sys->processor.proc, 0, DXVAHD_STREAM_STATE_INPUT_COLOR_SPACE, sizeof(colorspace), &colorspace );
805 
806     DXVAHD_STREAM_STATE_SOURCE_RECT_DATA srcRect;
807     srcRect.Enable = TRUE;
808     srcRect.SourceRect = (RECT) {
809         .left   = vd->source.i_x_offset,
810         .right  = vd->source.i_x_offset + vd->source.i_visible_width,
811         .top    = vd->source.i_y_offset,
812         .bottom = vd->source.i_y_offset + vd->source.i_visible_height,
813     };;
814     hr = IDXVAHD_VideoProcessor_SetVideoProcessStreamState( sys->processor.proc, 0, DXVAHD_STREAM_STATE_SOURCE_RECT, sizeof(srcRect), &srcRect );
815 
816     DXVAHD_BLT_STATE_TARGET_RECT_DATA dstRect;
817     dstRect.Enable = TRUE;
818     dstRect.TargetRect = (RECT) {
819         .left   = 0,
820         .right  = vd->source.i_visible_width,
821         .top    = 0,
822         .bottom = vd->source.i_visible_height,
823     };
824     hr = IDXVAHD_VideoProcessor_SetVideoProcessBltState( sys->processor.proc, DXVAHD_BLT_STATE_TARGET_RECT, sizeof(dstRect), &dstRect);
825 }
826 
GetFrameRate(DXVAHD_RATIONAL * r,const video_format_t * fmt)827 static void GetFrameRate(DXVAHD_RATIONAL *r, const video_format_t *fmt)
828 {
829     if (fmt->i_frame_rate && fmt->i_frame_rate_base)
830     {
831         r->Numerator   = fmt->i_frame_rate;
832         r->Denominator = fmt->i_frame_rate_base;
833     }
834     else
835     {
836         r->Numerator   = 0;
837         r->Denominator = 0;
838     }
839 }
840 
InitRangeProcessor(vout_display_t * vd,const d3d9_format_t * d3dfmt)841 static int InitRangeProcessor(vout_display_t *vd, const d3d9_format_t *d3dfmt)
842 {
843     vout_display_sys_t *sys = vd->sys;
844 
845     HRESULT hr;
846 
847     sys->processor.dll = LoadLibrary(TEXT("DXVA2.DLL"));
848     if (!sys->processor.dll)
849         return VLC_EGENERIC;
850 
851     D3DFORMAT *formatsList = NULL;
852     DXVAHD_VPCAPS *capsList = NULL;
853     IDXVAHD_Device *hd_device = NULL;
854 
855     HRESULT (WINAPI *CreateDevice)(IDirect3DDevice9Ex *,const DXVAHD_CONTENT_DESC *,DXVAHD_DEVICE_USAGE,PDXVAHDSW_Plugin,IDXVAHD_Device **);
856     CreateDevice = (void *)GetProcAddress(sys->processor.dll, "DXVAHD_CreateDevice");
857     if (CreateDevice == NULL)
858     {
859         msg_Err(vd, "Can't create HD device (not Windows 7+)");
860         goto error;
861     }
862 
863     DXVAHD_CONTENT_DESC desc;
864     desc.InputFrameFormat = DXVAHD_FRAME_FORMAT_PROGRESSIVE;
865     GetFrameRate( &desc.InputFrameRate, &vd->source );
866     desc.InputWidth       = vd->source.i_visible_width;
867     desc.InputHeight      = vd->source.i_visible_height;
868     desc.OutputFrameRate  = desc.InputFrameRate;
869     desc.OutputWidth      = vd->source.i_visible_width;
870     desc.OutputHeight     = vd->source.i_visible_height;
871 
872     hr = CreateDevice(sys->d3d_dev.devex, &desc, DXVAHD_DEVICE_USAGE_PLAYBACK_NORMAL, NULL, &hd_device);
873     if (FAILED(hr))
874     {
875         msg_Dbg(vd, "Failed to create the device (error 0x%lX)", hr);
876         goto error;
877     }
878 
879     DXVAHD_VPDEVCAPS devcaps = { 0 };
880     hr = IDXVAHD_Device_GetVideoProcessorDeviceCaps( hd_device, &devcaps );
881     if (unlikely(FAILED(hr)))
882     {
883         msg_Err(vd, "Failed to get the device capabilities (error 0x%lX)", hr);
884         goto error;
885     }
886     if (devcaps.VideoProcessorCount == 0)
887     {
888         msg_Warn(vd, "No good video processor found for range conversion");
889         goto error;
890     }
891 
892     formatsList = malloc(devcaps.InputFormatCount * sizeof(*formatsList));
893     if (unlikely(formatsList == NULL))
894         goto error;
895 
896     hr = IDXVAHD_Device_GetVideoProcessorInputFormats( hd_device, devcaps.InputFormatCount, formatsList);
897     UINT i;
898     for (i=0; i<devcaps.InputFormatCount; i++)
899     {
900         if (formatsList[i] == d3dfmt->format)
901             break;
902     }
903     if (i == devcaps.InputFormatCount)
904     {
905         msg_Warn(vd, "Input format %s not supported for range conversion", d3dfmt->name);
906         goto error;
907     }
908 
909     free(formatsList);
910     formatsList = malloc(devcaps.OutputFormatCount * sizeof(*formatsList));
911     if (unlikely(formatsList == NULL))
912         goto error;
913 
914     hr = IDXVAHD_Device_GetVideoProcessorOutputFormats( hd_device, devcaps.OutputFormatCount, formatsList);
915     for (i=0; i<devcaps.OutputFormatCount; i++)
916     {
917         if (formatsList[i] == sys->d3d_dev.pp.BackBufferFormat)
918             break;
919     }
920     if (i == devcaps.OutputFormatCount)
921     {
922         msg_Warn(vd, "Output format %s not supported for range conversion", d3dfmt->name);
923         goto error;
924     }
925 
926     capsList = malloc(devcaps.VideoProcessorCount * sizeof(*capsList));
927     if (unlikely(capsList == NULL))
928         goto error;
929     hr = IDXVAHD_Device_GetVideoProcessorCaps( hd_device, devcaps.VideoProcessorCount, capsList);
930     if (FAILED(hr))
931     {
932         msg_Dbg(vd, "Failed to get the processor caps (error 0x%lX)", hr);
933         goto error;
934     }
935 
936     hr = IDXVAHD_Device_CreateVideoProcessor( hd_device, &capsList->VPGuid, &sys->processor.proc );
937     if (FAILED(hr))
938     {
939         msg_Dbg(vd, "Failed to create the processor (error 0x%lX)", hr);
940         goto error;
941     }
942     IDXVAHD_Device_Release( hd_device );
943 
944     SetupProcessorInput(vd, &vd->source, d3dfmt);
945 
946     DXVAHD_BLT_STATE_OUTPUT_COLOR_SPACE_DATA colorspace;
947     colorspace.Usage = 0; // playback
948     colorspace.RGB_Range = 0; // full range display
949     colorspace.YCbCr_xvYCC = 1;
950     colorspace.YCbCr_Matrix = 1; // BT.709
951     hr = IDXVAHD_VideoProcessor_SetVideoProcessBltState( sys->processor.proc, DXVAHD_BLT_STATE_OUTPUT_COLOR_SPACE, sizeof(colorspace), &colorspace);
952 
953     return VLC_SUCCESS;
954 
955 error:
956     free(capsList);
957     free(formatsList);
958     if (hd_device)
959         IDXVAHD_Device_Release(hd_device);
960     FreeLibrary(sys->processor.dll);
961     return VLC_EGENERIC;
962 }
963 
964 /**
965  * It creates a Direct3D9 device and the associated resources.
966  */
Direct3D9Open(vout_display_t * vd,video_format_t * fmt)967 static int Direct3D9Open(vout_display_t *vd, video_format_t *fmt)
968 {
969     vout_display_sys_t *sys = vd->sys;
970 
971     if (FAILED(D3D9_CreateDevice(vd, &sys->hd3d, sys->sys.hvideownd,
972                                  &vd->source, &sys->d3d_dev)))
973         return VLC_EGENERIC;
974 
975     const d3d9_device_t *p_d3d9_dev = &sys->d3d_dev;
976     /* */
977     RECT *display = &vd->sys->sys.rect_display;
978     display->left   = 0;
979     display->top    = 0;
980     display->right  = p_d3d9_dev->pp.BackBufferWidth;
981     display->bottom = p_d3d9_dev->pp.BackBufferHeight;
982 
983     *fmt = vd->source;
984 
985     /* Find the appropriate D3DFORMAT for the render chroma, the format will be the closest to
986      * the requested chroma which is usable by the hardware in an offscreen surface, as they
987      * typically support more formats than textures */
988     const d3d9_format_t *d3dfmt = Direct3DFindFormat(vd, fmt->i_chroma, p_d3d9_dev->pp.BackBufferFormat);
989     if (!d3dfmt) {
990         msg_Err(vd, "surface pixel format is not supported.");
991         goto error;
992     }
993     const d3d9_format_t *d3dbuffer = FindBufferFormat(vd, p_d3d9_dev->pp.BackBufferFormat);
994     if (!d3dbuffer)
995         msg_Warn(vd, "Unknown back buffer format 0x%X", p_d3d9_dev->pp.BackBufferFormat);
996     else if (!vd->source.b_color_range_full && d3dbuffer->rmask && !d3dfmt->rmask)
997     {
998         D3DADAPTER_IDENTIFIER9 d3dai;
999         if (sys->hd3d.use_ex && SUCCEEDED(IDirect3D9Ex_GetAdapterIdentifier(sys->hd3d.objex, sys->d3d_dev.adapterId, 0, &d3dai)) &&
1000             d3dai.VendorId == GPU_MANUFACTURER_NVIDIA) {
1001 
1002             // NVIDIA bug, YUV to RGB internal conversion in StretchRect always converts from limited to limited range
1003             InitRangeProcessor( vd, d3dfmt );
1004         }
1005     }
1006 
1007     fmt->i_chroma = d3dfmt->fourcc;
1008     fmt->i_rmask  = d3dfmt->rmask;
1009     fmt->i_gmask  = d3dfmt->gmask;
1010     fmt->i_bmask  = d3dfmt->bmask;
1011     sys->d3dtexture_format = d3dfmt;
1012 
1013     UpdateRects(vd, NULL, true);
1014 
1015     if (Direct3D9CreateResources(vd, fmt)) {
1016         msg_Err(vd, "Failed to allocate resources");
1017         goto error;
1018     }
1019 
1020     /* Change the window title bar text */
1021     EventThreadUpdateTitle(sys->sys.event, VOUT_TITLE " (Direct3D9 output)");
1022 
1023     msg_Dbg(vd, "Direct3D9 device adapter successfully initialized");
1024     return VLC_SUCCESS;
1025 
1026 error:
1027     D3D9_ReleaseDevice(&sys->d3d_dev);
1028     return VLC_EGENERIC;
1029 }
1030 
1031 /**
1032  * It releases the Direct3D9 device and its resources.
1033  */
Direct3D9Close(vout_display_t * vd)1034 static void Direct3D9Close(vout_display_t *vd)
1035 {
1036     vout_display_sys_t *sys = vd->sys;
1037 
1038     Direct3D9DestroyResources(vd);
1039     D3D9_ReleaseDevice(&sys->d3d_dev);
1040 }
1041 
1042 /**
1043  * It reset the Direct3D9 device and its resources.
1044  */
Direct3D9Reset(vout_display_t * vd)1045 static int Direct3D9Reset(vout_display_t *vd)
1046 {
1047     vout_display_sys_t *sys = vd->sys;
1048     d3d9_device_t *p_d3d9_dev = &sys->d3d_dev;
1049 
1050     if (D3D9_FillPresentationParameters(&sys->hd3d, &vd->source, p_d3d9_dev))
1051     {
1052         msg_Err(vd, "Could not presentation parameters to reset device");
1053         return VLC_EGENERIC;
1054     }
1055 
1056     /* release all D3D objects */
1057     Direct3D9DestroyResources(vd);
1058 
1059     /* */
1060     HRESULT hr;
1061     if (sys->hd3d.use_ex){
1062         hr = IDirect3DDevice9Ex_ResetEx(p_d3d9_dev->devex, &p_d3d9_dev->pp, NULL);
1063     } else {
1064         hr = IDirect3DDevice9_Reset(p_d3d9_dev->dev, &p_d3d9_dev->pp);
1065     }
1066     if (FAILED(hr)) {
1067         msg_Err(vd, "IDirect3DDevice9_Reset failed! (hr=0x%0lx)", hr);
1068         return VLC_EGENERIC;
1069     }
1070 
1071     UpdateRects(vd, NULL, true);
1072 
1073     /* re-create them */
1074     if (Direct3D9CreateResources(vd, &vd->fmt)) {
1075         msg_Dbg(vd, "Direct3D9CreateResources failed !");
1076         return VLC_EGENERIC;
1077     }
1078     return VLC_SUCCESS;
1079 }
1080 
1081 /* */
1082 static int  Direct3D9CreateScene(vout_display_t *vd, const video_format_t *fmt);
1083 static void Direct3D9DestroyScene(vout_display_t *vd);
1084 
1085 static int  Direct3D9CreateShaders(vout_display_t *vd);
1086 static void Direct3D9DestroyShaders(vout_display_t *vd);
1087 
1088 /**
1089  * It creates the picture and scene resources.
1090  */
Direct3D9CreateResources(vout_display_t * vd,video_format_t * fmt)1091 static int Direct3D9CreateResources(vout_display_t *vd, video_format_t *fmt)
1092 {
1093     vout_display_sys_t *sys = vd->sys;
1094 
1095     if (Direct3D9CreateScene(vd, fmt)) {
1096         msg_Err(vd, "Direct3D scene initialization failed !");
1097         return VLC_EGENERIC;
1098     }
1099     if (Direct3D9CreateShaders(vd)) {
1100         /* Failing to initialize shaders is not fatal. */
1101         msg_Warn(vd, "Direct3D shaders initialization failed !");
1102     }
1103 
1104     sys->d3dregion_format = D3DFMT_UNKNOWN;
1105     for (int i = 0; i < 2; i++) {
1106         D3DFORMAT dfmt = i == 0 ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8;
1107         if (SUCCEEDED(IDirect3D9_CheckDeviceFormat(sys->hd3d.obj,
1108                                                    D3DADAPTER_DEFAULT,
1109                                                    D3DDEVTYPE_HAL,
1110                                                    sys->d3d_dev.pp.BackBufferFormat,
1111                                                    D3DUSAGE_DYNAMIC,
1112                                                    D3DRTYPE_TEXTURE,
1113                                                    dfmt))) {
1114             sys->d3dregion_format = dfmt;
1115             break;
1116         }
1117     }
1118     return VLC_SUCCESS;
1119 }
1120 /**
1121  * It destroys the picture and scene resources.
1122  */
Direct3D9DestroyResources(vout_display_t * vd)1123 static void Direct3D9DestroyResources(vout_display_t *vd)
1124 {
1125     Direct3D9DestroyScene(vd);
1126     if (vd->sys->sys.pool)
1127     {
1128         picture_pool_Release(vd->sys->sys.pool);
1129         vd->sys->sys.pool = NULL;
1130     }
1131     Direct3D9DestroyShaders(vd);
1132 }
1133 
1134 /**
1135  * It tests if the conversion from src to dst is supported.
1136  */
Direct3D9CheckConversion(vout_display_t * vd,D3DFORMAT src,D3DFORMAT dst)1137 static int Direct3D9CheckConversion(vout_display_t *vd,
1138                                    D3DFORMAT src, D3DFORMAT dst)
1139 {
1140     vout_display_sys_t *sys = vd->sys;
1141     LPDIRECT3D9 d3dobj = sys->hd3d.obj;
1142     HRESULT hr;
1143 
1144     /* test whether device can create a surface of that format */
1145     hr = IDirect3D9_CheckDeviceFormat(d3dobj, D3DADAPTER_DEFAULT,
1146                                       D3DDEVTYPE_HAL, dst, 0,
1147                                       D3DRTYPE_SURFACE, src);
1148     if (SUCCEEDED(hr)) {
1149         /* test whether device can perform color-conversion
1150         ** from that format to target format
1151         */
1152         hr = IDirect3D9_CheckDeviceFormatConversion(d3dobj,
1153                                                     D3DADAPTER_DEFAULT,
1154                                                     D3DDEVTYPE_HAL,
1155                                                     src, dst);
1156     }
1157     if (!SUCCEEDED(hr)) {
1158         if (D3DERR_NOTAVAILABLE != hr)
1159             msg_Err(vd, "Could not query adapter supported formats. (hr=0x%0lx)", hr);
1160         return VLC_EGENERIC;
1161     }
1162     return VLC_SUCCESS;
1163 }
1164 
1165 static const d3d9_format_t d3d_formats[] = {
1166     /* YV12 is always used for planar 420, the planes are then swapped in Lock() */
1167     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_YV12,  0,0,0 },
1168     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_I420,  0,0,0 },
1169     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_J420,  0,0,0 },
1170     { "NV12",       MAKEFOURCC('N','V','1','2'),    VLC_CODEC_NV12,  0,0,0 },
1171     { "DXA9",       MAKEFOURCC('N','V','1','2'),    VLC_CODEC_D3D9_OPAQUE,  0,0,0 },
1172     { "DXA9_10",    MAKEFOURCC('P','0','1','0'),    VLC_CODEC_D3D9_OPAQUE_10B,  0,0,0 },
1173     { "UYVY",       D3DFMT_UYVY,    VLC_CODEC_UYVY,  0,0,0 },
1174     { "YUY2",       D3DFMT_YUY2,    VLC_CODEC_YUYV,  0,0,0 },
1175     { "X8R8G8B8",   D3DFMT_X8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
1176     { "A8R8G8B8",   D3DFMT_A8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
1177     { "8G8B8",      D3DFMT_R8G8B8,  VLC_CODEC_RGB24, 0xff0000, 0x00ff00, 0x0000ff },
1178     { "R5G6B5",     D3DFMT_R5G6B5,  VLC_CODEC_RGB16, 0x1f<<11, 0x3f<<5,  0x1f<<0 },
1179     { "X1R5G5B5",   D3DFMT_X1R5G5B5,VLC_CODEC_RGB15, 0x1f<<10, 0x1f<<5,  0x1f<<0 },
1180 
1181     { NULL, 0, 0, 0,0,0}
1182 };
1183 
FindBufferFormat(vout_display_t * vd,D3DFORMAT fmt)1184 static const d3d9_format_t *FindBufferFormat(vout_display_t *vd, D3DFORMAT fmt)
1185 {
1186     for (unsigned j = 0; d3d_formats[j].name; j++) {
1187         const d3d9_format_t *format = &d3d_formats[j];
1188 
1189         if (format->format != fmt)
1190             continue;
1191 
1192         return format;
1193     }
1194     return NULL;
1195 }
1196 
1197 /**
1198  * It returns the format (closest to chroma) that can be converted to target */
Direct3DFindFormat(vout_display_t * vd,vlc_fourcc_t chroma,D3DFORMAT target)1199 static const d3d9_format_t *Direct3DFindFormat(vout_display_t *vd, vlc_fourcc_t chroma, D3DFORMAT target)
1200 {
1201     vout_display_sys_t *sys = vd->sys;
1202     bool hardware_scale_ok = !(vd->fmt.i_visible_width & 1) && !(vd->fmt.i_visible_height & 1);
1203     if( !hardware_scale_ok )
1204         msg_Warn( vd, "Disabling hardware chroma conversion due to odd dimensions" );
1205 
1206     for (unsigned pass = 0; pass < 2; pass++) {
1207         const vlc_fourcc_t *list;
1208         const vlc_fourcc_t dxva_chroma[] = {chroma, 0};
1209 
1210         if (pass == 0 && is_d3d9_opaque(chroma))
1211             list = dxva_chroma;
1212         else if (pass == 0 && hardware_scale_ok && sys->allow_hw_yuv && vlc_fourcc_IsYUV(chroma))
1213             list = vlc_fourcc_GetYUVFallback(chroma);
1214         else if (pass == 1)
1215             list = vlc_fourcc_GetRGBFallback(chroma);
1216         else
1217             continue;
1218 
1219         for (unsigned i = 0; list[i] != 0; i++) {
1220             for (unsigned j = 0; d3d_formats[j].name; j++) {
1221                 const d3d9_format_t *format = &d3d_formats[j];
1222 
1223                 if (format->fourcc != list[i])
1224                     continue;
1225 
1226                 msg_Warn(vd, "trying surface pixel format: %s",
1227                          format->name);
1228                 if (!Direct3D9CheckConversion(vd, format->format, target)) {
1229                     msg_Dbg(vd, "selected surface pixel format is %s",
1230                             format->name);
1231                     return format;
1232                 }
1233             }
1234         }
1235     }
1236     return NULL;
1237 }
1238 
1239 /**
1240  * It allocates and initializes the resources needed to render the scene.
1241  */
Direct3D9CreateScene(vout_display_t * vd,const video_format_t * fmt)1242 static int Direct3D9CreateScene(vout_display_t *vd, const video_format_t *fmt)
1243 {
1244     vout_display_sys_t *sys = vd->sys;
1245     const d3d9_device_t *p_d3d9_dev = &sys->d3d_dev;
1246     LPDIRECT3DDEVICE9       d3ddev = p_d3d9_dev->dev;
1247     HRESULT hr;
1248 
1249     /*
1250      * Create a texture for use when rendering a scene
1251      * for performance reason, texture format is identical to backbuffer
1252      * which would usually be a RGB format
1253      */
1254     LPDIRECT3DTEXTURE9 d3dtex;
1255     hr = IDirect3DDevice9_CreateTexture(d3ddev,
1256                                         fmt->i_width,
1257                                         fmt->i_height,
1258                                         1,
1259                                         D3DUSAGE_RENDERTARGET,
1260                                         p_d3d9_dev->pp.BackBufferFormat,
1261                                         D3DPOOL_DEFAULT,
1262                                         &d3dtex,
1263                                         NULL);
1264     if (FAILED(hr)) {
1265         msg_Err(vd, "Failed to create texture. (hr=0x%lx)", hr);
1266         return VLC_EGENERIC;
1267     }
1268 
1269 #ifndef NDEBUG
1270     msg_Dbg(vd, "Direct3D created texture: %ix%i",
1271                 fmt->i_width, fmt->i_height);
1272 #endif
1273 
1274     /*
1275     ** Create a vertex buffer for use when rendering scene
1276     */
1277     LPDIRECT3DVERTEXBUFFER9 d3dvtc;
1278     hr = IDirect3DDevice9_CreateVertexBuffer(d3ddev,
1279                                              sizeof(CUSTOMVERTEX)*4,
1280                                              D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
1281                                              D3DFVF_CUSTOMVERTEX,
1282                                              D3DPOOL_DEFAULT,
1283                                              &d3dvtc,
1284                                              NULL);
1285     if (FAILED(hr)) {
1286         msg_Err(vd, "Failed to create vertex buffer. (hr=0x%lx)", hr);
1287         IDirect3DTexture9_Release(d3dtex);
1288         return VLC_EGENERIC;
1289     }
1290 
1291     /* */
1292     sys->d3dtex = d3dtex;
1293     sys->d3dvtc = d3dvtc;
1294 
1295     sys->d3dregion_count = 0;
1296     sys->d3dregion       = NULL;
1297 
1298     sys->clear_scene = true;
1299 
1300     // Texture coordinates outside the range [0.0, 1.0] are set
1301     // to the texture color at 0.0 or 1.0, respectively.
1302     IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
1303     IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
1304 
1305     // Set linear filtering quality
1306     if (sys->d3d_dev.caps.TextureFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) {
1307         //msg_Dbg(vd, "Using D3DTEXF_LINEAR for minification");
1308         IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
1309     } else {
1310         //msg_Dbg(vd, "Using D3DTEXF_POINT for minification");
1311         IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
1312     }
1313     if (sys->d3d_dev.caps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR) {
1314         //msg_Dbg(vd, "Using D3DTEXF_LINEAR for magnification");
1315         IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
1316     } else {
1317         //msg_Dbg(vd, "Using D3DTEXF_POINT for magnification");
1318         IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
1319     }
1320 
1321     // set maximum ambient light
1322     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));
1323 
1324     // Turn off culling
1325     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_CULLMODE, D3DCULL_NONE);
1326 
1327     // Turn off the zbuffer
1328     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ZENABLE, D3DZB_FALSE);
1329 
1330     // Turn off lights
1331     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_LIGHTING, FALSE);
1332 
1333     // Enable dithering
1334     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DITHERENABLE, TRUE);
1335 
1336     // disable stencil
1337     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_STENCILENABLE, FALSE);
1338 
1339     // manage blending
1340     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, FALSE);
1341     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
1342     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
1343 
1344     if (sys->d3d_dev.caps.AlphaCmpCaps & D3DPCMPCAPS_GREATER) {
1345         IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHATESTENABLE,TRUE);
1346         IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAREF, 0x00);
1347         IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER);
1348     }
1349 
1350     // Set texture states
1351     IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLOROP,D3DTOP_SELECTARG1);
1352     IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE);
1353 
1354     IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
1355     IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
1356     IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);
1357 
1358     msg_Dbg(vd, "Direct3D9 scene created successfully");
1359 
1360     return VLC_SUCCESS;
1361 }
1362 
1363 /**
1364  * It releases the scene resources.
1365  */
Direct3D9DestroyScene(vout_display_t * vd)1366 static void Direct3D9DestroyScene(vout_display_t *vd)
1367 {
1368     vout_display_sys_t *sys = vd->sys;
1369 
1370     Direct3D9DeleteRegions(sys->d3dregion_count, sys->d3dregion);
1371 
1372     LPDIRECT3DVERTEXBUFFER9 d3dvtc = sys->d3dvtc;
1373     if (d3dvtc)
1374         IDirect3DVertexBuffer9_Release(d3dvtc);
1375 
1376     LPDIRECT3DTEXTURE9 d3dtex = sys->d3dtex;
1377     if (d3dtex)
1378         IDirect3DTexture9_Release(d3dtex);
1379 
1380     sys->d3dvtc = NULL;
1381     sys->d3dtex = NULL;
1382 
1383     sys->d3dregion_count = 0;
1384     sys->d3dregion       = NULL;
1385 
1386     msg_Dbg(vd, "Direct3D9 scene released successfully");
1387 }
1388 
Direct3D9CompileShader(vout_display_t * vd,const char * shader_source,size_t source_length)1389 static int Direct3D9CompileShader(vout_display_t *vd, const char *shader_source, size_t source_length)
1390 {
1391 #ifdef HAVE_D3DX9EFFECT_H
1392     vout_display_sys_t *sys = vd->sys;
1393 
1394     HRESULT (WINAPI * OurD3DXCompileShader)(
1395             LPCSTR pSrcData,
1396             UINT srcDataLen,
1397             const D3DXMACRO *pDefines,
1398             LPD3DXINCLUDE pInclude,
1399             LPCSTR pFunctionName,
1400             LPCSTR pProfile,
1401             DWORD Flags,
1402             LPD3DXBUFFER *ppShader,
1403             LPD3DXBUFFER *ppErrorMsgs,
1404             LPD3DXCONSTANTTABLE *ppConstantTable);
1405 
1406     OurD3DXCompileShader = (void*)GetProcAddress(sys->hxdll, "D3DXCompileShader");
1407     if (!OurD3DXCompileShader) {
1408         msg_Warn(vd, "Cannot locate reference to D3DXCompileShader; pixel shading will be disabled");
1409         return VLC_EGENERIC;
1410     }
1411 
1412     LPD3DXBUFFER error_msgs = NULL;
1413     LPD3DXBUFFER compiled_shader = NULL;
1414 
1415     DWORD shader_flags = 0;
1416     HRESULT hr = OurD3DXCompileShader(shader_source, source_length, NULL, NULL,
1417                 "main", "ps_3_0", shader_flags, &compiled_shader, &error_msgs, NULL);
1418 
1419     if (FAILED(hr)) {
1420         msg_Warn(vd, "D3DXCompileShader Error (hr=0x%0lx)", hr);
1421         if (error_msgs) {
1422             msg_Warn(vd, "HLSL Compilation Error: %s", (char*)ID3DXBuffer_GetBufferPointer(error_msgs));
1423             ID3DXBuffer_Release(error_msgs);
1424     }
1425         return VLC_EGENERIC;
1426     }
1427 
1428     hr = IDirect3DDevice9_CreatePixelShader(sys->d3d_dev.dev,
1429             ID3DXBuffer_GetBufferPointer(compiled_shader),
1430             &sys->d3dx_shader);
1431 
1432     if (compiled_shader)
1433         ID3DXBuffer_Release(compiled_shader);
1434     if (error_msgs)
1435         ID3DXBuffer_Release(error_msgs);
1436 
1437     if (FAILED(hr)) {
1438         msg_Warn(vd, "IDirect3DDevice9_CreatePixelShader error (hr=0x%0lx)", hr);
1439         return VLC_EGENERIC;
1440     }
1441     return VLC_SUCCESS;
1442 #else
1443     return VLC_EGENERIC;
1444 #endif
1445 }
1446 
1447 #define MAX_SHADER_FILE_SIZE 1024*1024
1448 
Direct3D9CreateShaders(vout_display_t * vd)1449 static int Direct3D9CreateShaders(vout_display_t *vd)
1450 {
1451     vout_display_sys_t *sys = vd->sys;
1452 
1453     if (!sys->hxdll)
1454         return VLC_EGENERIC;
1455 
1456     /* Find which shader was selected in the list. */
1457     char *selected_shader = var_InheritString(vd, "direct3d9-shader");
1458     if (!selected_shader)
1459         return VLC_SUCCESS; /* Nothing to do */
1460 
1461     const char *shader_source_builtin = NULL;
1462     char *shader_source_file = NULL;
1463     FILE *fs = NULL;
1464 
1465     for (size_t i = 0; i < BUILTIN_SHADERS_COUNT; ++i) {
1466         if (!strcmp(selected_shader, builtin_shaders[i].name)) {
1467             shader_source_builtin = builtin_shaders[i].code;
1468             break;
1469         }
1470     }
1471 
1472     if (shader_source_builtin) {
1473         /* A builtin shader was selected. */
1474         int err = Direct3D9CompileShader(vd, shader_source_builtin, strlen(shader_source_builtin));
1475         if (err)
1476             goto error;
1477     } else {
1478         if (strcmp(selected_shader, SELECTED_SHADER_FILE))
1479             goto error; /* Unrecognized entry in the list. */
1480         /* The source code of the shader needs to be read from a file. */
1481         char *filepath = var_InheritString(vd, "direct3d9-shader-file");
1482         if (!filepath || !*filepath)
1483         {
1484             free(filepath);
1485             goto error;
1486         }
1487         /* Open file, find its size with fseek/ftell and read its content in a buffer. */
1488         fs = fopen(filepath, "rb");
1489         if (!fs)
1490             goto error;
1491         int ret = fseek(fs, 0, SEEK_END);
1492         if (ret == -1)
1493             goto error;
1494         long length = ftell(fs);
1495         if (length == -1 || length >= MAX_SHADER_FILE_SIZE)
1496             goto error;
1497         rewind(fs);
1498         shader_source_file = vlc_alloc(length, sizeof(*shader_source_file));
1499         if (!shader_source_file)
1500             goto error;
1501         ret = fread(shader_source_file, length, 1, fs);
1502         if (ret != 1)
1503             goto error;
1504         ret = Direct3D9CompileShader(vd, shader_source_file, length);
1505         if (ret)
1506             goto error;
1507     }
1508 
1509     free(selected_shader);
1510     free(shader_source_file);
1511     fclose(fs);
1512 
1513     return VLC_SUCCESS;
1514 
1515 error:
1516     Direct3D9DestroyShaders(vd);
1517     free(selected_shader);
1518     free(shader_source_file);
1519     if (fs)
1520         fclose(fs);
1521     return VLC_EGENERIC;
1522 }
1523 
Direct3D9DestroyShaders(vout_display_t * vd)1524 static void Direct3D9DestroyShaders(vout_display_t *vd)
1525 {
1526     vout_display_sys_t *sys = vd->sys;
1527 
1528     if (sys->d3dx_shader)
1529         IDirect3DPixelShader9_Release(sys->d3dx_shader);
1530     sys->d3dx_shader = NULL;
1531 }
1532 
1533 /**
1534  * Compute the vertex ordering needed to rotate the video. Without
1535  * rotation, the vertices of the rectangle are defined in a clockwise
1536  * order. This function computes a remapping of the coordinates to
1537  * implement the rotation, given fixed texture coordinates.
1538  * The unrotated order is the following:
1539  * 0--1
1540  * |  |
1541  * 3--2
1542  * For a 180 degrees rotation it should like this:
1543  * 2--3
1544  * |  |
1545  * 1--0
1546  * Vertex 0 should be assigned coordinates at index 2 from the
1547  * unrotated order and so on, thus yielding order: 2 3 0 1.
1548  */
orientationVertexOrder(video_orientation_t orientation,int vertex_order[static4])1549 static void orientationVertexOrder(video_orientation_t orientation, int vertex_order[static 4])
1550 {
1551     switch (orientation) {
1552         case ORIENT_ROTATED_90:      /* ORIENT_RIGHT_TOP */
1553             vertex_order[0] = 1;
1554             vertex_order[1] = 2;
1555             vertex_order[2] = 3;
1556             vertex_order[3] = 0;
1557             break;
1558         case ORIENT_ROTATED_270:     /* ORIENT_LEFT_BOTTOM */
1559             vertex_order[0] = 3;
1560             vertex_order[1] = 0;
1561             vertex_order[2] = 1;
1562             vertex_order[3] = 2;
1563             break;
1564         case ORIENT_ROTATED_180:     /* ORIENT_BOTTOM_RIGHT */
1565             vertex_order[0] = 2;
1566             vertex_order[1] = 3;
1567             vertex_order[2] = 0;
1568             vertex_order[3] = 1;
1569             break;
1570         case ORIENT_TRANSPOSED:      /* ORIENT_LEFT_TOP */
1571             vertex_order[0] = 0;
1572             vertex_order[1] = 3;
1573             vertex_order[2] = 2;
1574             vertex_order[3] = 1;
1575             break;
1576         case ORIENT_HFLIPPED:        /* ORIENT_TOP_RIGHT */
1577             vertex_order[0] = 1;
1578             vertex_order[1] = 0;
1579             vertex_order[2] = 3;
1580             vertex_order[3] = 2;
1581             break;
1582         case ORIENT_VFLIPPED:        /* ORIENT_BOTTOM_LEFT */
1583             vertex_order[0] = 3;
1584             vertex_order[1] = 2;
1585             vertex_order[2] = 1;
1586             vertex_order[3] = 0;
1587             break;
1588         case ORIENT_ANTI_TRANSPOSED: /* ORIENT_RIGHT_BOTTOM */
1589             vertex_order[0] = 2;
1590             vertex_order[1] = 1;
1591             vertex_order[2] = 0;
1592             vertex_order[3] = 3;
1593             break;
1594        default:
1595             vertex_order[0] = 0;
1596             vertex_order[1] = 1;
1597             vertex_order[2] = 2;
1598             vertex_order[3] = 3;
1599             break;
1600     }
1601 }
1602 
Direct3D9SetupVertices(CUSTOMVERTEX * vertices,const RECT * src,const RECT * src_clipped,const RECT * dst,int alpha,video_orientation_t orientation)1603 static void  Direct3D9SetupVertices(CUSTOMVERTEX *vertices,
1604                                   const RECT *src, const RECT *src_clipped,
1605                                   const RECT *dst,
1606                                   int alpha,
1607                                   video_orientation_t orientation)
1608 {
1609     /* Vertices of the dst rectangle in the unrotated (clockwise) order. */
1610     const int vertices_coords[4][2] = {
1611         { dst->left,  dst->top    },
1612         { dst->right, dst->top    },
1613         { dst->right, dst->bottom },
1614         { dst->left,  dst->bottom },
1615     };
1616 
1617     /* Compute index remapping necessary to implement the rotation. */
1618     int vertex_order[4];
1619     orientationVertexOrder(orientation, vertex_order);
1620 
1621     for (int i = 0; i < 4; ++i) {
1622         vertices[i].x  = vertices_coords[vertex_order[i]][0];
1623         vertices[i].y  = vertices_coords[vertex_order[i]][1];
1624     }
1625 
1626     float right = (float)src_clipped->right / (float)src->right;
1627     float left = (float)src_clipped->left / (float)src->right;
1628     float top = (float)src_clipped->top / (float)src->bottom;
1629     float bottom = (float)src_clipped->bottom / (float)src->bottom;
1630 
1631     vertices[0].tu = left;
1632     vertices[0].tv = top;
1633 
1634     vertices[1].tu = right;
1635     vertices[1].tv = top;
1636 
1637     vertices[2].tu = right;
1638     vertices[2].tv = bottom;
1639 
1640     vertices[3].tu = left;
1641     vertices[3].tv = bottom;
1642 
1643     for (int i = 0; i < 4; i++) {
1644         /* -0.5f is a "feature" of DirectX and it seems to apply to Direct3d also */
1645         /* http://www.sjbrown.co.uk/2003/05/01/fix-directx-rasterisation/ */
1646         vertices[i].x -= 0.5;
1647         vertices[i].y -= 0.5;
1648 
1649         vertices[i].z       = 0.0f;
1650         vertices[i].rhw     = 1.0f;
1651         vertices[i].diffuse = D3DCOLOR_ARGB(alpha, 255, 255, 255);
1652     }
1653 }
1654 
1655 /**
1656  * It copies picture surface into a texture and setup the associated d3d_region_t.
1657  */
Direct3D9ImportPicture(vout_display_t * vd,d3d_region_t * region,LPDIRECT3DSURFACE9 source)1658 static int Direct3D9ImportPicture(vout_display_t *vd,
1659                                  d3d_region_t *region,
1660                                  LPDIRECT3DSURFACE9 source)
1661 {
1662     vout_display_sys_t *sys = vd->sys;
1663     HRESULT hr;
1664 
1665     if (!source) {
1666         msg_Dbg(vd, "no surface to render?");
1667         return VLC_EGENERIC;
1668     }
1669 
1670     /* retrieve texture top-level surface */
1671     LPDIRECT3DSURFACE9 destination;
1672     hr = IDirect3DTexture9_GetSurfaceLevel(sys->d3dtex, 0, &destination);
1673     if (FAILED(hr)) {
1674         msg_Dbg(vd, "Failed IDirect3DTexture9_GetSurfaceLevel: 0x%0lx", hr);
1675         return VLC_EGENERIC;
1676     }
1677 
1678     if (sys->processor.proc)
1679     {
1680         DXVAHD_STREAM_DATA inputStream = { 0 };
1681         inputStream.Enable = TRUE;
1682         inputStream.pInputSurface = source;
1683         hr = IDXVAHD_VideoProcessor_VideoProcessBltHD( sys->processor.proc, destination, 0, 1, &inputStream );
1684     }
1685     else
1686     {
1687         /* Copy picture surface into texture surface
1688         * color space conversion happen here */
1689         RECT copy_rect = sys->sys.rect_src_clipped;
1690         // On nVidia & AMD, StretchRect will fail if the visible size isn't even.
1691         // When copying the entire buffer, the margin end up being blended in the actual picture
1692         // on nVidia (regardless of even/odd dimensions)
1693         if ( copy_rect.right & 1 ) copy_rect.right++;
1694         if ( copy_rect.left & 1 ) copy_rect.left--;
1695         if ( copy_rect.bottom & 1 ) copy_rect.bottom++;
1696         if ( copy_rect.top & 1 ) copy_rect.top--;
1697         hr = IDirect3DDevice9_StretchRect(sys->d3d_dev.dev, source, &copy_rect, destination,
1698                                         &copy_rect, D3DTEXF_NONE);
1699     }
1700     IDirect3DSurface9_Release(destination);
1701     if (FAILED(hr)) {
1702         msg_Dbg(vd, "Failed IDirect3DDevice9_StretchRect: source 0x%p 0x%0lx",
1703                 (LPVOID)source, hr);
1704         return VLC_EGENERIC;
1705     }
1706 
1707     /* */
1708     region->texture = sys->d3dtex;
1709     Direct3D9SetupVertices(region->vertex, &vd->sys->sys.rect_src, &vd->sys->sys.rect_src_clipped,
1710                            &vd->sys->sys.rect_dest_clipped, 255, vd->fmt.orientation);
1711     return VLC_SUCCESS;
1712 }
1713 
Direct3D9DeleteRegions(int count,d3d_region_t * region)1714 static void Direct3D9DeleteRegions(int count, d3d_region_t *region)
1715 {
1716     for (int i = 0; i < count; i++) {
1717         if (region[i].texture)
1718             IDirect3DTexture9_Release(region[i].texture);
1719     }
1720     free(region);
1721 }
1722 
Direct3D9ImportSubpicture(vout_display_t * vd,int * count_ptr,d3d_region_t ** region,subpicture_t * subpicture)1723 static void Direct3D9ImportSubpicture(vout_display_t *vd,
1724                                      int *count_ptr, d3d_region_t **region,
1725                                      subpicture_t *subpicture)
1726 {
1727     vout_display_sys_t *sys = vd->sys;
1728 
1729     int count = 0;
1730     for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
1731         count++;
1732 
1733     *count_ptr = count;
1734     *region    = calloc(count, sizeof(**region));
1735     if (*region == NULL) {
1736         *count_ptr = 0;
1737         return;
1738     }
1739 
1740     int i = 0;
1741     for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next, i++) {
1742         d3d_region_t *d3dr = &(*region)[i];
1743         HRESULT hr;
1744 
1745         d3dr->texture = NULL;
1746         for (int j = 0; j < sys->d3dregion_count; j++) {
1747             d3d_region_t *cache = &sys->d3dregion[j];
1748             if (cache->texture &&
1749                 cache->format == sys->d3dregion_format &&
1750                 cache->width  == r->fmt.i_width &&
1751                 cache->height == r->fmt.i_height) {
1752                 *d3dr = *cache;
1753                 memset(cache, 0, sizeof(*cache));
1754                 break;
1755             }
1756         }
1757         if (!d3dr->texture) {
1758             d3dr->format = sys->d3dregion_format;
1759             d3dr->width  = r->fmt.i_width;
1760             d3dr->height = r->fmt.i_height;
1761             hr = IDirect3DDevice9_CreateTexture(sys->d3d_dev.dev,
1762                                                 d3dr->width, d3dr->height,
1763                                                 1,
1764                                                 D3DUSAGE_DYNAMIC,
1765                                                 d3dr->format,
1766                                                 D3DPOOL_DEFAULT,
1767                                                 &d3dr->texture,
1768                                                 NULL);
1769             if (FAILED(hr)) {
1770                 d3dr->texture = NULL;
1771                 msg_Err(vd, "Failed to create %dx%d texture for OSD (hr=0x%0lx)",
1772                         d3dr->width, d3dr->height, hr);
1773                 continue;
1774             }
1775 #ifndef NDEBUG
1776             msg_Dbg(vd, "Created %dx%d texture for OSD",
1777                     r->fmt.i_width, r->fmt.i_height);
1778 #endif
1779         }
1780 
1781         D3DLOCKED_RECT lock;
1782         hr = IDirect3DTexture9_LockRect(d3dr->texture, 0, &lock, NULL, 0);
1783         if (SUCCEEDED(hr)) {
1784             uint8_t  *dst_data   = lock.pBits;
1785             int       dst_pitch  = lock.Pitch;
1786             uint8_t  *src_data   = r->p_picture->p->p_pixels;
1787             int       src_pitch  = r->p_picture->p->i_pitch;
1788 
1789             if (d3dr->format == D3DFMT_A8B8G8R8) {
1790                 if (dst_pitch == r->p_picture->p->i_pitch) {
1791                     memcpy(dst_data, src_data, r->fmt.i_height * dst_pitch);
1792                 } else {
1793                     int copy_pitch = __MIN(dst_pitch, r->p_picture->p->i_pitch);
1794                     for (unsigned y = 0; y < r->fmt.i_height; y++) {
1795                         memcpy(&dst_data[y * dst_pitch], &src_data[y * src_pitch], copy_pitch);
1796                     }
1797                 }
1798             } else {
1799                 int copy_pitch = __MIN(dst_pitch, r->p_picture->p->i_pitch);
1800                 for (unsigned y = 0; y < r->fmt.i_height; y++) {
1801                     for (int x = 0; x < copy_pitch; x += 4) {
1802                         dst_data[y * dst_pitch + x + 0] = src_data[y * src_pitch + x + 2];
1803                         dst_data[y * dst_pitch + x + 1] = src_data[y * src_pitch + x + 1];
1804                         dst_data[y * dst_pitch + x + 2] = src_data[y * src_pitch + x + 0];
1805                         dst_data[y * dst_pitch + x + 3] = src_data[y * src_pitch + x + 3];
1806                     }
1807                 }
1808             }
1809             hr = IDirect3DTexture9_UnlockRect(d3dr->texture, 0);
1810             if (FAILED(hr))
1811                 msg_Err(vd, "Failed to unlock the texture");
1812         } else {
1813             msg_Err(vd, "Failed to lock the texture");
1814         }
1815 
1816         /* Map the subpicture to sys->sys.rect_dest */
1817         const RECT video = sys->sys.rect_dest;
1818         const float scale_w = (float)(video.right  - video.left) / subpicture->i_original_picture_width;
1819         const float scale_h = (float)(video.bottom - video.top)  / subpicture->i_original_picture_height;
1820 
1821         RECT dst;
1822         dst.left   = video.left + scale_w * r->i_x,
1823         dst.right  = dst.left + scale_w * r->fmt.i_visible_width,
1824         dst.top    = video.top  + scale_h * r->i_y,
1825         dst.bottom = dst.top  + scale_h * r->fmt.i_visible_height;
1826 
1827         RECT src;
1828         src.left = 0;
1829         src.right = r->fmt.i_width;
1830         src.top = 0;
1831         src.bottom = r->fmt.i_height;
1832 
1833         RECT src_clipped;
1834         src_clipped.left = r->fmt.i_x_offset;
1835         src_clipped.right = r->fmt.i_x_offset + r->fmt.i_visible_width;
1836         src_clipped.top = r->fmt.i_y_offset;
1837         src_clipped.bottom = r->fmt.i_y_offset + r->fmt.i_visible_height;
1838 
1839         Direct3D9SetupVertices(d3dr->vertex, &src, &src_clipped,
1840                               &dst, subpicture->i_alpha * r->i_alpha / 255, ORIENT_NORMAL);
1841     }
1842 }
1843 
Direct3D9RenderRegion(vout_display_t * vd,d3d_region_t * region,bool use_pixel_shader)1844 static int Direct3D9RenderRegion(vout_display_t *vd,
1845                                 d3d_region_t *region,
1846                                 bool use_pixel_shader)
1847 {
1848     vout_display_sys_t *sys = vd->sys;
1849 
1850     LPDIRECT3DDEVICE9 d3ddev = vd->sys->d3d_dev.dev;
1851 
1852     LPDIRECT3DVERTEXBUFFER9 d3dvtc = sys->d3dvtc;
1853     LPDIRECT3DTEXTURE9      d3dtex = region->texture;
1854 
1855     HRESULT hr;
1856 
1857     /* Import vertices */
1858     void *vertex;
1859     hr = IDirect3DVertexBuffer9_Lock(d3dvtc, 0, 0, &vertex, D3DLOCK_DISCARD);
1860     if (FAILED(hr)) {
1861         msg_Dbg(vd, "Failed IDirect3DVertexBuffer9_Lock: 0x%0lx", hr);
1862         return -1;
1863     }
1864     memcpy(vertex, region->vertex, sizeof(region->vertex));
1865     hr = IDirect3DVertexBuffer9_Unlock(d3dvtc);
1866     if (FAILED(hr)) {
1867         msg_Dbg(vd, "Failed IDirect3DVertexBuffer9_Unlock: 0x%0lx", hr);
1868         return -1;
1869     }
1870 
1871     // Setup our texture. Using textures introduces the texture stage states,
1872     // which govern how textures get blended together (in the case of multiple
1873     // textures) and lighting information. In this case, we are modulating
1874     // (blending) our texture with the diffuse color of the vertices.
1875     hr = IDirect3DDevice9_SetTexture(d3ddev, 0, (LPDIRECT3DBASETEXTURE9)d3dtex);
1876     if (FAILED(hr)) {
1877         msg_Dbg(vd, "Failed IDirect3DDevice9_SetTexture: 0x%0lx", hr);
1878         return -1;
1879     }
1880 
1881     if (sys->d3dx_shader) {
1882         if (use_pixel_shader)
1883         {
1884             hr = IDirect3DDevice9_SetPixelShader(d3ddev, sys->d3dx_shader);
1885             float shader_data[4] = { region->width, region->height, 0, 0 };
1886             hr = IDirect3DDevice9_SetPixelShaderConstantF(d3ddev, 0, shader_data, 1);
1887             if (FAILED(hr)) {
1888                 msg_Dbg(vd, "Failed IDirect3DDevice9_SetPixelShaderConstantF: 0x%0lx", hr);
1889                 return -1;
1890             }
1891         }
1892         else /* Disable any existing pixel shader. */
1893             hr = IDirect3DDevice9_SetPixelShader(d3ddev, NULL);
1894         if (FAILED(hr)) {
1895             msg_Dbg(vd, "Failed IDirect3DDevice9_SetPixelShader: 0x%0lx", hr);
1896             return -1;
1897         }
1898     }
1899 
1900     // Render the vertex buffer contents
1901     hr = IDirect3DDevice9_SetStreamSource(d3ddev, 0, d3dvtc, 0, sizeof(CUSTOMVERTEX));
1902     if (FAILED(hr)) {
1903         msg_Dbg(vd, "Failed IDirect3DDevice9_SetStreamSource: 0x%0lx", hr);
1904         return -1;
1905     }
1906 
1907     // we use FVF instead of vertex shader
1908     hr = IDirect3DDevice9_SetFVF(d3ddev, D3DFVF_CUSTOMVERTEX);
1909     if (FAILED(hr)) {
1910         msg_Dbg(vd, "Failed IDirect3DDevice9_SetFVF: 0x%0lx", hr);
1911         return -1;
1912     }
1913 
1914     // draw rectangle
1915     hr = IDirect3DDevice9_DrawPrimitive(d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
1916     if (FAILED(hr)) {
1917         msg_Dbg(vd, "Failed IDirect3DDevice9_DrawPrimitive: 0x%0lx", hr);
1918         return -1;
1919     }
1920     return 0;
1921 }
1922 
1923 /**
1924  * It renders the scene.
1925  *
1926  * This function is intented for higher end 3D cards, with pixel shader support
1927  * and at least 64 MiB of video RAM.
1928  */
Direct3D9RenderScene(vout_display_t * vd,d3d_region_t * picture,int subpicture_count,d3d_region_t * subpicture)1929 static void Direct3D9RenderScene(vout_display_t *vd,
1930                                 d3d_region_t *picture,
1931                                 int subpicture_count,
1932                                 d3d_region_t *subpicture)
1933 {
1934     vout_display_sys_t *sys = vd->sys;
1935     LPDIRECT3DDEVICE9 d3ddev = sys->d3d_dev.dev;
1936     HRESULT hr;
1937 
1938     if (sys->clear_scene) {
1939         /* Clear the backbuffer and the zbuffer */
1940         hr = IDirect3DDevice9_Clear(d3ddev, 0, NULL, D3DCLEAR_TARGET,
1941                                   D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
1942         if (FAILED(hr)) {
1943             msg_Dbg(vd, "Failed IDirect3DDevice9_Clear: 0x%0lx", hr);
1944             return;
1945         }
1946         sys->clear_scene = false;
1947     }
1948 
1949     // Begin the scene
1950     hr = IDirect3DDevice9_BeginScene(d3ddev);
1951     if (FAILED(hr)) {
1952         msg_Dbg(vd, "Failed IDirect3DDevice9_BeginScene: 0x%0lx", hr);
1953         return;
1954     }
1955 
1956     Direct3D9RenderRegion(vd, picture, true);
1957 
1958     if (subpicture_count > 0)
1959         IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, TRUE);
1960     for (int i = 0; i < subpicture_count; i++) {
1961         d3d_region_t *r = &subpicture[i];
1962         if (r->texture)
1963             Direct3D9RenderRegion(vd, r, false);
1964     }
1965     if (subpicture_count > 0)
1966         IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, FALSE);
1967 
1968     // End the scene
1969     hr = IDirect3DDevice9_EndScene(d3ddev);
1970     if (FAILED(hr)) {
1971         msg_Dbg(vd, "Failed IDirect3DDevice9_EndScene: 0x%0lx", hr);
1972         return;
1973     }
1974 }
1975 
1976 /*****************************************************************************
1977  * DesktopCallback: desktop mode variable callback
1978  *****************************************************************************/
DesktopCallback(vlc_object_t * object,char const * psz_cmd,vlc_value_t oldval,vlc_value_t newval,void * p_data)1979 static int DesktopCallback(vlc_object_t *object, char const *psz_cmd,
1980                             vlc_value_t oldval, vlc_value_t newval,
1981                             void *p_data)
1982 {
1983     vout_display_t *vd = (vout_display_t *)object;
1984     vout_display_sys_t *sys = vd->sys;
1985     VLC_UNUSED(psz_cmd);
1986     VLC_UNUSED(oldval);
1987     VLC_UNUSED(p_data);
1988 
1989     vlc_mutex_lock(&sys->lock);
1990     const bool ch_desktop = !sys->desktop_requested != !newval.b_bool;
1991     sys->ch_desktop |= ch_desktop;
1992     sys->desktop_requested = newval.b_bool;
1993     vlc_mutex_unlock(&sys->lock);
1994     return VLC_SUCCESS;
1995 }
1996 
1997 typedef struct
1998 {
1999     char **values;
2000     char **descs;
2001     size_t count;
2002 } enum_context_t;
2003 
ListShaders(enum_context_t * ctx)2004 static void ListShaders(enum_context_t *ctx)
2005 {
2006     size_t num_shaders = BUILTIN_SHADERS_COUNT;
2007     ctx->values = xrealloc(ctx->values, (ctx->count + num_shaders + 1) * sizeof(char *));
2008     ctx->descs = xrealloc(ctx->descs, (ctx->count + num_shaders + 1) * sizeof(char *));
2009     for (size_t i = 0; i < num_shaders; ++i) {
2010         ctx->values[ctx->count] = strdup(builtin_shaders[i].name);
2011         ctx->descs[ctx->count] = strdup(builtin_shaders[i].name);
2012         ctx->count++;
2013     }
2014     ctx->values[ctx->count] = strdup(SELECTED_SHADER_FILE);
2015     ctx->descs[ctx->count] = strdup(SELECTED_SHADER_FILE);
2016     ctx->count++;
2017 }
2018 
2019 /* Populate the list of available shader techniques in the options */
FindShadersCallback(vlc_object_t * object,const char * name,char *** values,char *** descs)2020 static int FindShadersCallback(vlc_object_t *object, const char *name,
2021                                char ***values, char ***descs)
2022 {
2023     VLC_UNUSED(object);
2024     VLC_UNUSED(name);
2025 
2026     enum_context_t ctx = { NULL, NULL, 0 };
2027 
2028     ListShaders(&ctx);
2029 
2030     *values = ctx.values;
2031     *descs = ctx.descs;
2032     return ctx.count;
2033 
2034 }
2035 
2036 #ifdef HAVE_GL
2037 #include "../opengl/converter.h"
2038 #include <GL/wglew.h>
2039 
2040 struct wgl_vt {
2041     PFNWGLDXSETRESOURCESHAREHANDLENVPROC DXSetResourceShareHandleNV;
2042     PFNWGLDXOPENDEVICENVPROC             DXOpenDeviceNV;
2043     PFNWGLDXCLOSEDEVICENVPROC            DXCloseDeviceNV;
2044     PFNWGLDXREGISTEROBJECTNVPROC         DXRegisterObjectNV;
2045     PFNWGLDXUNREGISTEROBJECTNVPROC       DXUnregisterObjectNV;
2046     PFNWGLDXLOCKOBJECTSNVPROC            DXLockObjectsNV;
2047     PFNWGLDXUNLOCKOBJECTSNVPROC          DXUnlockObjectsNV;
2048 };
2049 struct glpriv
2050 {
2051     struct wgl_vt vt;
2052     d3d9_handle_t hd3d;
2053     d3d9_device_t d3d_dev;
2054     HANDLE gl_handle_d3d;
2055     HANDLE gl_render;
2056     IDirect3DSurface9 *dx_render;
2057 };
2058 
2059 static int
GLConvUpdate(const opengl_tex_converter_t * tc,GLuint * textures,const GLsizei * tex_width,const GLsizei * tex_height,picture_t * pic,const size_t * plane_offset)2060 GLConvUpdate(const opengl_tex_converter_t *tc, GLuint *textures,
2061              const GLsizei *tex_width, const GLsizei *tex_height,
2062              picture_t *pic, const size_t *plane_offset)
2063 {
2064     VLC_UNUSED(textures); VLC_UNUSED(tex_width); VLC_UNUSED(tex_height); VLC_UNUSED(plane_offset);
2065     struct glpriv *priv = tc->priv;
2066     HRESULT hr;
2067 
2068     picture_sys_t *picsys = ActivePictureSys(pic);
2069     if (unlikely(!picsys || !priv->gl_render))
2070         return VLC_EGENERIC;
2071 
2072     if (!priv->vt.DXUnlockObjectsNV(priv->gl_handle_d3d, 1, &priv->gl_render))
2073     {
2074         msg_Warn(tc->gl, "DXUnlockObjectsNV failed");
2075         return VLC_EGENERIC;
2076     }
2077 
2078     const RECT rect = {
2079         .left = 0,
2080         .top = 0,
2081         .right = pic->format.i_visible_width,
2082         .bottom = pic->format.i_visible_height
2083     };
2084     hr = IDirect3DDevice9Ex_StretchRect(priv->d3d_dev.devex, picsys->surface,
2085                                         &rect, priv->dx_render, NULL, D3DTEXF_NONE);
2086     if (FAILED(hr))
2087     {
2088         msg_Warn(tc->gl, "IDirect3DDevice9Ex_StretchRect failed");
2089         return VLC_EGENERIC;
2090     }
2091 
2092     if (!priv->vt.DXLockObjectsNV(priv->gl_handle_d3d, 1, &priv->gl_render))
2093     {
2094         msg_Warn(tc->gl, "DXLockObjectsNV failed");
2095         priv->vt.DXUnregisterObjectNV(priv->gl_handle_d3d, priv->gl_render);
2096         priv->gl_render = NULL;
2097         return VLC_EGENERIC;
2098     }
2099 
2100     return VLC_SUCCESS;
2101 }
2102 
2103 static picture_pool_t *
GLConvGetPool(const opengl_tex_converter_t * tc,unsigned requested_count)2104 GLConvGetPool(const opengl_tex_converter_t *tc, unsigned requested_count)
2105 {
2106     struct glpriv *priv = tc->priv;
2107     return Direct3D9CreatePicturePool(VLC_OBJECT(tc->gl), &priv->d3d_dev, NULL,
2108                                       &tc->fmt, requested_count);
2109 }
2110 
2111 static int
GLConvAllocateTextures(const opengl_tex_converter_t * tc,GLuint * textures,const GLsizei * tex_width,const GLsizei * tex_height)2112 GLConvAllocateTextures(const opengl_tex_converter_t *tc, GLuint *textures,
2113                        const GLsizei *tex_width, const GLsizei *tex_height)
2114 {
2115     VLC_UNUSED(tex_width); VLC_UNUSED(tex_height);
2116     struct glpriv *priv = tc->priv;
2117 
2118     priv->gl_render =
2119         priv->vt.DXRegisterObjectNV(priv->gl_handle_d3d, priv->dx_render,
2120                                     textures[0], GL_TEXTURE_2D, WGL_ACCESS_WRITE_DISCARD_NV);
2121     if (!priv->gl_render)
2122     {
2123         msg_Warn(tc->gl, "DXRegisterObjectNV failed: %lu", GetLastError());
2124         return VLC_EGENERIC;
2125     }
2126 
2127     if (!priv->vt.DXLockObjectsNV(priv->gl_handle_d3d, 1, &priv->gl_render))
2128     {
2129         msg_Warn(tc->gl, "DXLockObjectsNV failed");
2130         priv->vt.DXUnregisterObjectNV(priv->gl_handle_d3d, priv->gl_render);
2131         priv->gl_render = NULL;
2132         return VLC_EGENERIC;
2133     }
2134 
2135     return VLC_SUCCESS;
2136 }
2137 
2138 static void
GLConvClose(vlc_object_t * obj)2139 GLConvClose(vlc_object_t *obj)
2140 {
2141     opengl_tex_converter_t *tc = (void *)obj;
2142     struct glpriv *priv = tc->priv;
2143 
2144     if (priv->gl_handle_d3d)
2145     {
2146         if (priv->gl_render)
2147         {
2148             priv->vt.DXUnlockObjectsNV(priv->gl_handle_d3d, 1, &priv->gl_render);
2149             priv->vt.DXUnregisterObjectNV(priv->gl_handle_d3d, priv->gl_render);
2150         }
2151 
2152         priv->vt.DXCloseDeviceNV(priv->gl_handle_d3d);
2153     }
2154 
2155     if (priv->dx_render)
2156         IDirect3DSurface9_Release(priv->dx_render);
2157 
2158     D3D9_ReleaseDevice(&priv->d3d_dev);
2159     D3D9_Destroy(&priv->hd3d);
2160     free(tc->priv);
2161 }
2162 
2163 static int
GLConvOpen(vlc_object_t * obj)2164 GLConvOpen(vlc_object_t *obj)
2165 {
2166     opengl_tex_converter_t *tc = (void *) obj;
2167 
2168     if (tc->fmt.i_chroma != VLC_CODEC_D3D9_OPAQUE
2169      && tc->fmt.i_chroma != VLC_CODEC_D3D9_OPAQUE_10B)
2170         return VLC_EGENERIC;
2171 
2172     if (tc->gl->ext != VLC_GL_EXT_WGL || !tc->gl->wgl.getExtensionsString)
2173         return VLC_EGENERIC;
2174 
2175     const char *wglExt = tc->gl->wgl.getExtensionsString(tc->gl);
2176 
2177     if (wglExt == NULL || !HasExtension(wglExt, "WGL_NV_DX_interop"))
2178         return VLC_EGENERIC;
2179 
2180     struct wgl_vt vt;
2181 #define LOAD_EXT(name, type) do { \
2182     vt.name = (type) vlc_gl_GetProcAddress(tc->gl, "wgl" #name); \
2183     if (!vt.name) { \
2184         msg_Warn(obj, "'wgl " #name "' could not be loaded"); \
2185         return VLC_EGENERIC; \
2186     } \
2187 } while(0)
2188 
2189     LOAD_EXT(DXSetResourceShareHandleNV, PFNWGLDXSETRESOURCESHAREHANDLENVPROC);
2190     LOAD_EXT(DXOpenDeviceNV, PFNWGLDXOPENDEVICENVPROC);
2191     LOAD_EXT(DXCloseDeviceNV, PFNWGLDXCLOSEDEVICENVPROC);
2192     LOAD_EXT(DXRegisterObjectNV, PFNWGLDXREGISTEROBJECTNVPROC);
2193     LOAD_EXT(DXUnregisterObjectNV, PFNWGLDXUNREGISTEROBJECTNVPROC);
2194     LOAD_EXT(DXLockObjectsNV, PFNWGLDXLOCKOBJECTSNVPROC);
2195     LOAD_EXT(DXUnlockObjectsNV, PFNWGLDXUNLOCKOBJECTSNVPROC);
2196 
2197     struct glpriv *priv = calloc(1, sizeof(struct glpriv));
2198     if (!priv)
2199         return VLC_ENOMEM;
2200     tc->priv = priv;
2201     priv->vt = vt;
2202 
2203     if (D3D9_Create(obj, &priv->hd3d) != VLC_SUCCESS)
2204         goto error;
2205 
2206     if (!priv->hd3d.use_ex)
2207     {
2208         msg_Warn(obj, "DX/GL interrop only working on d3d9x");
2209         goto error;
2210     }
2211 
2212     if (FAILED(D3D9_CreateDevice(obj, &priv->hd3d, tc->gl->surface->handle.hwnd,
2213                                  &tc->fmt, &priv->d3d_dev)))
2214         goto error;
2215 
2216     HRESULT hr;
2217     HANDLE shared_handle = NULL;
2218     hr = IDirect3DDevice9Ex_CreateRenderTarget(priv->d3d_dev.devex,
2219                                                tc->fmt.i_visible_width,
2220                                                tc->fmt.i_visible_height,
2221                                                D3DFMT_X8R8G8B8,
2222                                                D3DMULTISAMPLE_NONE, 0, FALSE,
2223                                                &priv->dx_render, &shared_handle);
2224     if (FAILED(hr))
2225     {
2226         msg_Warn(obj, "IDirect3DDevice9_CreateOffscreenPlainSurface failed");
2227         goto error;
2228     }
2229 
2230    if (shared_handle)
2231         priv->vt.DXSetResourceShareHandleNV(priv->dx_render, shared_handle);
2232 
2233     priv->gl_handle_d3d = priv->vt.DXOpenDeviceNV(priv->d3d_dev.dev);
2234     if (!priv->gl_handle_d3d)
2235     {
2236         msg_Warn(obj, "DXOpenDeviceNV failed: %lu", GetLastError());
2237         goto error;
2238     }
2239 
2240     tc->pf_update  = GLConvUpdate;
2241     tc->pf_get_pool = GLConvGetPool;
2242     tc->pf_allocate_textures = GLConvAllocateTextures;
2243 
2244     tc->fshader = opengl_fragment_shader_init(tc, GL_TEXTURE_2D, VLC_CODEC_RGB32,
2245                                               COLOR_SPACE_UNDEF);
2246     if (tc->fshader == 0)
2247         goto error;
2248 
2249     return VLC_SUCCESS;
2250 
2251 error:
2252     GLConvClose(obj);
2253     return VLC_EGENERIC;
2254 }
2255 #endif
2256