directx.c 48.2 KB
Newer Older
1
/*****************************************************************************
2
 * directx.c: Windows DirectDraw video output
3
 *****************************************************************************
4
 * Copyright (C) 2001-2009 the VideoLAN team
5
 * $Id$
6
 *
gbazin's avatar
gbazin committed
7
 * Authors: Gildas Bazin <gbazin@videolan.org>
gbazin's avatar
   
gbazin committed
8
 *
9
10
11
12
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
gbazin's avatar
   
gbazin committed
13
 *
14
15
16
17
18
19
20
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
23
24
 *****************************************************************************/

/*****************************************************************************
gbazin's avatar
   
gbazin committed
25
26
27
28
29
30
31
32
33
34
 * Preamble:
 *
 * This plugin will use YUV overlay if supported, using overlay will result in
 * the best video quality (hardware interpolation when rescaling the picture)
 * and the fastest display as it requires less processing.
 *
 * If YUV overlay is not supported this plugin will use RGB offscreen video
 * surfaces that will be blitted onto the primary surface (display) to
 * effectively display the pictures. This fallback method also enables us to
 * display video in window mode.
35
 *
36
 *****************************************************************************/
37
38
39
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
40
#include <assert.h>
41

42
#include <vlc_common.h>
43
#include <vlc_plugin.h>
44
#include <vlc_vout_display.h>
45
#include <vlc_playlist.h>   /* needed for wallpaper */
gbazin's avatar
   
gbazin committed
46

47
48
#include <windows.h>
#include <winuser.h>
gbazin's avatar
   
gbazin committed
49
#include <ddraw.h>
50
#include <commctrl.h>       /* ListView_(Get|Set)* */
gbazin's avatar
   
gbazin committed
51

52
#include "common.h"
gbazin's avatar
   
gbazin committed
53

54
55
56
57
#ifdef UNICODE
#   error "Unicode mode not supported"
#endif

58
59
60
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Christophe Massiot's avatar
Christophe Massiot committed
61
#define HW_YUV_TEXT N_("Use hardware YUV->RGB conversions")
62
#define HW_YUV_LONGTEXT N_(\
63
    "Try to use hardware acceleration for YUV->RGB conversions. " \
64
    "This option doesn't have any effect when using overlays.")
zorglub's avatar
zorglub committed
65

Christophe Massiot's avatar
Christophe Massiot committed
66
#define SYSMEM_TEXT N_("Use video buffers in system memory")
67
#define SYSMEM_LONGTEXT N_(\
68
69
70
    "Create video buffers in system memory instead of video memory. This " \
    "isn't recommended as usually using video memory allows to benefit from " \
    "more hardware acceleration (like rescaling or YUV->RGB conversions). " \
71
    "This option doesn't have any effect when using overlays.")
zorglub's avatar
zorglub committed
72

Christophe Massiot's avatar
Christophe Massiot committed
73
#define TRIPLEBUF_TEXT N_("Use triple buffering for overlays")
74
#define TRIPLEBUF_LONGTEXT N_(\
75
    "Try to use triple buffering when using YUV overlays. That results in " \
76
    "much better video quality (no flickering).")
zorglub's avatar
zorglub committed
77

gbazin's avatar
   
gbazin committed
78
#define DEVICE_TEXT N_("Name of desired display device")
79
80
81
#define DEVICE_LONGTEXT N_("In a multiple monitor configuration, you can " \
    "specify the Windows device name of the display that you want the video " \
    "window to open on. For example, \"\\\\.\\DISPLAY1\" or " \
82
    "\"\\\\.\\DISPLAY2\".")
gbazin's avatar
   
gbazin committed
83

84
85
86
#define DX_HELP N_("Recommended video output for Windows XP. " \
    "Incompatible with Vista's Aero interface" )

87
88
static const char * const device[] = { "" };
static const char * const device_text[] = { N_("Default") };
89

90
91
92
93
94
95
96
97
static int  Open (vlc_object_t *);
static void Close(vlc_object_t *);

static int  FindDevicesCallback(vlc_object_t *, char const *,
                                vlc_value_t, vlc_value_t, void *);
vlc_module_begin()
    set_shortname("DirectX")
    set_description(N_("DirectX (DirectDraw) video output"))
98
    set_help(DX_HELP)
99
100
    set_category(CAT_VIDEO)
    set_subcategory(SUBCAT_VIDEO_VOUT)
101
    add_bool("directx-hw-yuv", true, HW_YUV_TEXT, HW_YUV_LONGTEXT,
102
              true)
103
    add_bool("directx-use-sysmem", false, SYSMEM_TEXT, SYSMEM_LONGTEXT,
104
              true)
105
    add_bool("directx-3buffering", true, TRIPLEBUF_TEXT,
106
              TRIPLEBUF_LONGTEXT, true)
107
    add_string("directx-device", "", DEVICE_TEXT, DEVICE_LONGTEXT, true)
108
109
110
111
112
113
114
        change_string_list(device, device_text, FindDevicesCallback)
        change_action_add(FindDevicesCallback, N_("Refresh list"))

    set_capability("vout display", 100)
    add_shortcut("directx")
    set_callbacks(Open, Close)
vlc_module_end()
115

gbazin's avatar
   
gbazin committed
116
/*****************************************************************************
117
 * Local prototypes.
gbazin's avatar
   
gbazin committed
118
119
 *****************************************************************************/

120
121
122
struct picture_sys_t {
    LPDIRECTDRAWSURFACE2 surface;
    LPDIRECTDRAWSURFACE2 front_surface;
123
    picture_t            *fallback;
124
};
125

126
127
128
129
130
131
132
133
134
135
/*****************************************************************************
 * DirectDraw GUIDs.
 * Defining them here allows us to get rid of the dxguid library during
 * the linking stage.
 *****************************************************************************/
#include <initguid.h>
#undef GUID_EXT
#define GUID_EXT
DEFINE_GUID(IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56);
DEFINE_GUID(IID_IDirectDrawSurface2, 0x57805885,0x6eec,0x11cf,0x94,0x41,0xa8,0x23,0x03,0xc1,0x0e,0x27);
136

137
138
139
140
static picture_pool_t *Pool  (vout_display_t *, unsigned);
static void           Display(vout_display_t *, picture_t *);
static int            Control(vout_display_t *, int, va_list);
static void           Manage (vout_display_t *);
gbazin's avatar
   
gbazin committed
141

142
143
144
/* */
static int WallpaperCallback(vlc_object_t *, char const *,
                             vlc_value_t, vlc_value_t, void *);
gbazin's avatar
   
gbazin committed
145

146
147
static int  DirectXOpen(vout_display_t *, video_format_t *fmt);
static void DirectXClose(vout_display_t *);
gbazin's avatar
   
gbazin committed
148

149
150
static int  DirectXLock(picture_t *);
static void DirectXUnlock(picture_t *);
151

152
static int DirectXUpdateOverlay(vout_display_t *, LPDIRECTDRAWSURFACE2 surface);
gbazin's avatar
   
gbazin committed
153

154
static void WallpaperChange(vout_display_t *vd, bool use_wallpaper);
gbazin's avatar
   
gbazin committed
155

156
157
158
/** This function allocates and initialize the DirectX vout display.
 */
static int Open(vlc_object_t *object)
gbazin's avatar
   
gbazin committed
159
{
160
161
    vout_display_t *vd = (vout_display_t *)object;
    vout_display_sys_t *sys;
162

163
164
165
166
167
168
169
170
171
172
    /* Allocate structure */
    vd->sys = sys = calloc(1, sizeof(*sys));
    if (!sys)
        return VLC_ENOMEM;

    /* Load direct draw DLL */
    sys->hddraw_dll = LoadLibrary(_T("DDRAW.DLL"));
    if (!sys->hddraw_dll) {
        msg_Warn(vd, "DirectXInitDDraw failed loading ddraw.dll");
        free(sys);
173
174
        return VLC_EGENERIC;
    }
gbazin's avatar
   
gbazin committed
175

176
177
178
179
    /* */
    sys->use_wallpaper = var_CreateGetBool(vd, "video-wallpaper");
    /* FIXME */
    sys->use_overlay = false;//var_CreateGetBool(vd, "overlay"); /* FIXME */
180
    sys->restore_overlay = false;
181
    var_Create(vd, "directx-device", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
gbazin's avatar
   
gbazin committed
182

183
184
185
    /* Initialisation */
    if (CommonInit(vd))
        goto error;
gbazin's avatar
   
gbazin committed
186

187
188
    /* */
    video_format_t fmt = vd->fmt;
gbazin's avatar
   
gbazin committed
189

190
191
    if (DirectXOpen(vd, &fmt))
        goto error;
192

193
194
195
196
    /* */
    vout_display_info_t info = vd->info;
    info.is_slow = true;
    info.has_double_click = true;
197
    info.has_hide_mouse = false;
198
    info.has_pictures_invalid = true;
199
    info.has_event_thread = true;
200
201
202
203
204
205

    /* Interaction TODO support starting with wallpaper mode */
    vlc_mutex_init(&sys->lock);
    sys->ch_wallpaper = sys->use_wallpaper;
    sys->wallpaper_requested = sys->use_wallpaper;
    sys->use_wallpaper = false;
gbazin's avatar
   
gbazin committed
206

207
208
209
210
211
212
213
214
215
216
217
218
219
220
    vlc_value_t val;
    val.psz_string = _("Wallpaper");
    var_Change(vd, "video-wallpaper", VLC_VAR_SETTEXT, &val, NULL);
    var_AddCallback(vd, "video-wallpaper", WallpaperCallback, NULL);

    /* Setup vout_display now that everything is fine */
    vd->fmt     = fmt;
    vd->info    = info;

    vd->pool    = Pool;
    vd->prepare = NULL;
    vd->display = Display;
    vd->control = Control;
    vd->manage  = Manage;
221
    return VLC_SUCCESS;
gbazin's avatar
   
gbazin committed
222

223
error:
224
225
226
227
228
    DirectXClose(vd);
    CommonClean(vd);
    if (sys->hddraw_dll)
        FreeLibrary(sys->hddraw_dll);
    free(sys);
229
    return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
230
231
}

232
233
234
/** Terminate a vout display created by Open.
 */
static void Close(vlc_object_t *object)
235
{
236
237
238
239
240
    vout_display_t *vd = (vout_display_t *)object;
    vout_display_sys_t *sys = vd->sys;

    var_DelCallback(vd, "video-wallpaper", WallpaperCallback, NULL);
    vlc_mutex_destroy(&sys->lock);
241

242
    /* Make sure the wallpaper is restored */
243
    WallpaperChange(vd, false);
244

245
    DirectXClose(vd);
246

247
248
249
250
251
    CommonClean(vd);

    if (sys->hddraw_dll)
        FreeLibrary(sys->hddraw_dll);
    free(sys);
gbazin's avatar
   
gbazin committed
252
253
}

254
static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
gbazin's avatar
   
gbazin committed
255
{
256
257
    VLC_UNUSED(count);
    return vd->sys->pool;
gbazin's avatar
   
gbazin committed
258
}
259
static void Display(vout_display_t *vd, picture_t *picture)
gbazin's avatar
   
gbazin committed
260
{
261
    vout_display_sys_t *sys = vd->sys;
gbazin's avatar
   
gbazin committed
262

263
    assert(sys->display);
gbazin's avatar
   
gbazin committed
264

gbazin's avatar
   
gbazin committed
265
266
    /* Our surface can be lost so be sure to check this
     * and restore it if need be */
267
268
269
270
271
    if (IDirectDrawSurface2_IsLost(sys->display) == DDERR_SURFACELOST) {
        if (IDirectDrawSurface2_Restore(sys->display) == DD_OK) {
            if (sys->use_overlay)
                DirectXUpdateOverlay(vd, NULL);
        }
gbazin's avatar
   
gbazin committed
272
    }
273
274
    if (sys->restore_overlay)
        DirectXUpdateOverlay(vd, NULL);
gbazin's avatar
   
gbazin committed
275

276
277
    /* */
    DirectXUnlock(picture);
gbazin's avatar
   
gbazin committed
278

279
280
281
282
283
284
285
286
287
288
289
290
291
    if (sys->use_overlay) {
        /* Flip the overlay buffers if we are using back buffers */
        if (picture->p_sys->surface != picture->p_sys->front_surface) {
            HRESULT hr = IDirectDrawSurface2_Flip(picture->p_sys->front_surface,
                                                  NULL, DDFLIP_WAIT);
            if (hr != DD_OK)
                msg_Warn(vd, "could not flip overlay (error %li)", hr);
        }
    } else {
        /* Blit video surface to display with the NOTEARING option */
        DDBLTFX  ddbltfx;
        ZeroMemory(&ddbltfx, sizeof(ddbltfx));
        ddbltfx.dwSize = sizeof(ddbltfx);
gbazin's avatar
   
gbazin committed
292
293
        ddbltfx.dwDDFX = DDBLTFX_NOTEARING;

294
295
296
297
298
299
300
        HRESULT hr = IDirectDrawSurface2_Blt(sys->display,
                                             &sys->rect_dest_clipped,
                                             picture->p_sys->surface,
                                             &sys->rect_src_clipped,
                                             DDBLT_ASYNC, &ddbltfx);
        if (hr != DD_OK)
            msg_Warn(vd, "could not blit surface (error %li)", hr);
gbazin's avatar
   
gbazin committed
301
    }
302
303
304
305
306
307
308
309
310
    DirectXLock(picture);

    if (sys->is_first_display) {
        IDirectDraw_WaitForVerticalBlank(sys->ddobject,
                                         DDWAITVB_BLOCKBEGIN, NULL);
        if (sys->use_overlay) {
            HBRUSH brush = CreateSolidBrush(sys->i_rgb_colorkey);
            /* set the colorkey as the backgound brush for the video window */
            SetClassLongPtr(sys->hvideownd, GCLP_HBRBACKGROUND, (LONG_PTR)brush);
311
        }
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
    }
    CommonDisplay(vd);

    picture_Release(picture);
}
static int Control(vout_display_t *vd, int query, va_list args)
{
    vout_display_sys_t *sys = vd->sys;

    switch (query) {
    case VOUT_DISPLAY_RESET_PICTURES:
        DirectXClose(vd);
        /* Make sure the wallpaper is restored */
        if (sys->use_wallpaper) {
            vlc_mutex_lock(&sys->lock);
            if (!sys->ch_wallpaper) {
                sys->ch_wallpaper = true;
                sys->wallpaper_requested = true;
            }
            vlc_mutex_unlock(&sys->lock);
gbazin's avatar
   
gbazin committed
332

333
            WallpaperChange(vd, false);
334
        }
335
336
337
338
339
340
341
342
343
344
        return DirectXOpen(vd, &vd->fmt);
    default:
        return CommonControl(vd, query, args);
    }
}
static void Manage(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;

    CommonManage(vd);
gbazin's avatar
   
gbazin committed
345

346
347
348
349
    if (sys->changes & DX_POSITION_CHANGE) {
        /* Update overlay */
        if (sys->use_overlay)
            DirectXUpdateOverlay(vd, NULL);
gbazin's avatar
   
gbazin committed
350

351
        /* Check if we are still on the same monitor */
352
353
        HMONITOR hmon = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
        if (sys->hmonitor != hmon) {
354
            vout_display_SendEventPicturesInvalid(vd);
gbazin's avatar
   
gbazin committed
355
        }
356
357
        /* */
        sys->changes &= ~DX_POSITION_CHANGE;
gbazin's avatar
   
gbazin committed
358
    }
359
360
361
362
363
364
365
366
367
368

    /* Wallpaper mode change */
    vlc_mutex_lock(&sys->lock);
    const bool ch_wallpaper = sys->ch_wallpaper;
    const bool wallpaper_requested = sys->wallpaper_requested;
    sys->ch_wallpaper = false;
    vlc_mutex_unlock(&sys->lock);

    if (ch_wallpaper)
        WallpaperChange(vd, wallpaper_requested);
369
370
371
372

    /* */
    if (sys->restore_overlay)
        DirectXUpdateOverlay(vd, NULL);
gbazin's avatar
   
gbazin committed
373
374
}

375
376
377
378
379
380
/* */
static int  DirectXOpenDDraw(vout_display_t *);
static void DirectXCloseDDraw(vout_display_t *);

static int  DirectXOpenDisplay(vout_display_t *vd);
static void DirectXCloseDisplay(vout_display_t *vd);
381

382
383
384
385
static int  DirectXCreatePool(vout_display_t *, bool *, video_format_t *);
static void DirectXDestroyPool(vout_display_t *);

static int DirectXOpen(vout_display_t *vd, video_format_t *fmt)
386
{
387
    vout_display_sys_t *sys = vd->sys;
388

389
390
391
    assert(!sys->ddobject);
    assert(!sys->display);
    assert(!sys->clipper);
392

393
394
395
396
    /* Initialise DirectDraw */
    if (DirectXOpenDDraw(vd)) {
        msg_Err(vd, "cannot initialize DirectX DirectDraw");
        return VLC_EGENERIC;
397
    }
398

399
400
401
402
403
404
    /* Create the directx display */
    if (DirectXOpenDisplay(vd)) {
        msg_Err(vd, "cannot initialize DirectX DirectDraw");
        return VLC_EGENERIC;
    }
    UpdateRects(vd, NULL, NULL, true);
gbazin's avatar
   
gbazin committed
405

406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
    /* Create the picture pool */
    if (DirectXCreatePool(vd, &sys->use_overlay, fmt)) {
        msg_Err(vd, "cannot create any DirectX surface");
        return VLC_EGENERIC;
    }

    /* */
    if (sys->use_overlay)
        DirectXUpdateOverlay(vd, NULL);
    EventThreadUseOverlay(sys->event, sys->use_overlay);

    /* Change the window title bar text */
    const char *fallback;
    if (sys->use_overlay)
        fallback = VOUT_TITLE " (hardware YUV overlay DirectX output)";
    else if (vlc_fourcc_IsYUV(fmt->i_chroma))
        fallback = VOUT_TITLE " (hardware YUV DirectX output)";
    else
        fallback = VOUT_TITLE " (software RGB DirectX output)";
    EventThreadUpdateTitle(sys->event, fallback);

    return VLC_SUCCESS;
}
static void DirectXClose(vout_display_t *vd)
430
{
431
432
433
434
    DirectXDestroyPool(vd);
    DirectXCloseDisplay(vd);
    DirectXCloseDDraw(vd);
}
gbazin's avatar
   
gbazin committed
435

436
437
438
439
440
441
442
/* */
static BOOL WINAPI DirectXOpenDDrawCallback(GUID *guid, LPTSTR desc,
                                            LPTSTR drivername, VOID *context,
                                            HMONITOR hmon)
{
    vout_display_t *vd = context;
    vout_display_sys_t *sys = vd->sys;
443

444
445
446
447
448
449
450
    /* This callback function is called by DirectDraw once for each
     * available DirectDraw device.
     *
     * Returning TRUE keeps enumerating.
     */
    if (!hmon)
        return TRUE;
451

452
    msg_Dbg(vd, "DirectXEnumCallback: %s, %s", desc, drivername);
gbazin's avatar
   
gbazin committed
453

454
455
456
457
458
459
460
    char *device = var_GetString(vd, "directx-device");

    /* Check for forced device */
    if (device && *device && !strcmp(drivername, device)) {
        MONITORINFO monitor_info;
        monitor_info.cbSize = sizeof(MONITORINFO);

461
        if (GetMonitorInfoA(hmon, &monitor_info)) {
462
463
464
465
466
467
468
469
470
471
472
473
474
            RECT rect;

            /* Move window to the right screen */
            GetWindowRect(sys->hwnd, &rect);
            if (!IntersectRect(&rect, &rect, &monitor_info.rcWork)) {
                rect.left = monitor_info.rcWork.left;
                rect.top = monitor_info.rcWork.top;
                msg_Dbg(vd, "DirectXEnumCallback: setting window "
                            "position to %ld,%ld", rect.left, rect.top);
                SetWindowPos(sys->hwnd, NULL,
                             rect.left, rect.top, 0, 0,
                             SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
            }
gbazin's avatar
   
gbazin committed
475
        }
476
477
478
        sys->hmonitor = hmon;
    }
    free(device);
gbazin's avatar
   
gbazin committed
479

480
481
482
483
484
485
486
    if (hmon == sys->hmonitor) {
        msg_Dbg(vd, "selecting %s, %s", desc, drivername);

        free(sys->display_driver);
        sys->display_driver = malloc(sizeof(*guid));
        if (sys->display_driver)
            *sys->display_driver = *guid;
487
488
    }

489
    return TRUE;
490
}
491
492
493
494
495
496
497
/**
 * Probe the capabilities of the hardware
 *
 * It is nice to know which features are supported by the hardware so we can
 * find ways to optimize our rendering.
 */
static void DirectXGetDDrawCaps(vout_display_t *vd)
gbazin's avatar
   
gbazin committed
498
{
499
    vout_display_sys_t *sys = vd->sys;
gbazin's avatar
   
gbazin committed
500

501
502
503
504
505
506
507
508
509
510
    /* This is just an indication of whether or not we'll support overlay,
     * but with this test we don't know if we support YUV overlay */
    DDCAPS ddcaps;
    ZeroMemory(&ddcaps, sizeof(ddcaps));
    ddcaps.dwSize = sizeof(ddcaps);
    HRESULT hr = IDirectDraw2_GetCaps(sys->ddobject, &ddcaps, NULL);
    if (hr != DD_OK) {
        msg_Warn(vd, "cannot get caps");
        return;
    }
gbazin's avatar
   
gbazin committed
511

512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
    /* Determine if the hardware supports overlay surfaces */
    const bool has_overlay = ddcaps.dwCaps & DDCAPS_OVERLAY;
    /* Determine if the hardware supports overlay surfaces */
    const bool has_overlay_fourcc = ddcaps.dwCaps & DDCAPS_OVERLAYFOURCC;
    /* Determine if the hardware supports overlay deinterlacing */
    const bool can_deinterlace = ddcaps.dwCaps & DDCAPS2_CANFLIPODDEVEN;
    /* Determine if the hardware supports colorkeying */
    const bool has_color_key = ddcaps.dwCaps & DDCAPS_COLORKEY;
    /* Determine if the hardware supports scaling of the overlay surface */
    const bool can_stretch = ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH;
    /* Determine if the hardware supports color conversion during a blit */
    sys->can_blit_fourcc = ddcaps.dwCaps & DDCAPS_BLTFOURCC;
    /* Determine overlay source boundary alignment */
    const bool align_boundary_src  = ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYSRC;
    /* Determine overlay destination boundary alignment */
    const bool align_boundary_dest = ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYDEST;
    /* Determine overlay destination size alignment */
    const bool align_size_src  = ddcaps.dwCaps & DDCAPS_ALIGNSIZESRC;
    /* Determine overlay destination size alignment */
    const bool align_size_dest = ddcaps.dwCaps & DDCAPS_ALIGNSIZEDEST;

    msg_Dbg(vd, "DirectDraw Capabilities: overlay=%i yuvoverlay=%i "
                "can_deinterlace_overlay=%i colorkey=%i stretch=%i "
                "bltfourcc=%i",
                has_overlay, has_overlay_fourcc, can_deinterlace,
                has_color_key, can_stretch, sys->can_blit_fourcc);

    if (align_boundary_src || align_boundary_dest || align_size_src || align_size_dest) {
        if (align_boundary_src)
            vd->sys->i_align_src_boundary = ddcaps.dwAlignBoundarySrc;
        if (align_boundary_dest)
            vd->sys->i_align_dest_boundary = ddcaps.dwAlignBoundaryDest;
        if (align_size_src)
            vd->sys->i_align_src_size = ddcaps.dwAlignSizeSrc;
        if (align_size_dest)
            vd->sys->i_align_dest_size = ddcaps.dwAlignSizeDest;

        msg_Dbg(vd,
                "align_boundary_src=%i,%i align_boundary_dest=%i,%i "
                "align_size_src=%i,%i align_size_dest=%i,%i",
                align_boundary_src,  vd->sys->i_align_src_boundary,
                align_boundary_dest, vd->sys->i_align_dest_boundary,
                align_size_src,  vd->sys->i_align_src_size,
                align_size_dest, vd->sys->i_align_dest_size);
gbazin's avatar
   
gbazin committed
556
    }
557
558
559
560
561
562
563
564
565
}



/* */
static int DirectXOpenDDraw(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;
    HRESULT hr;
566

567
568
    /* */
    HRESULT (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *);
569
    OurDirectDrawCreate =
570
571
572
573
        (void *)GetProcAddress(sys->hddraw_dll, _T("DirectDrawCreate"));
    if (!OurDirectDrawCreate) {
        msg_Err(vd, "DirectXInitDDraw failed GetProcAddress");
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
574
575
    }

576
    /* */
577
578
579
580
581
582
583
584
585
586
    HRESULT (WINAPI *OurDirectDrawEnumerateEx)(LPDDENUMCALLBACKEXA, LPVOID, DWORD);
    OurDirectDrawEnumerateEx =
      (void *)GetProcAddress(sys->hddraw_dll, _T("DirectDrawEnumerateExA"));

    if (OurDirectDrawEnumerateEx) {
        char *device = var_GetString(vd, "directx-device");
        if (device) {
            msg_Dbg(vd, "directx-device: %s", device);
            free(device);
        }
587

588
        sys->hmonitor = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
gbazin's avatar
   
gbazin committed
589

590
591
592
        /* Enumerate displays */
        OurDirectDrawEnumerateEx(DirectXOpenDDrawCallback,
                                 vd, DDENUM_ATTACHEDSECONDARYDEVICES);
593
594
    }

gbazin's avatar
   
gbazin committed
595
    /* Initialize DirectDraw now */
596
597
598
599
600
    LPDIRECTDRAW ddobject;
    hr = OurDirectDrawCreate(sys->display_driver, &ddobject, NULL);
    if (hr != DD_OK) {
        msg_Err(vd, "DirectXInitDDraw cannot initialize DDraw");
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
601
602
603
    }

    /* Get the IDirectDraw2 interface */
604
605
    hr = IDirectDraw_QueryInterface(ddobject, &IID_IDirectDraw2,
                                    &sys->ddobject);
gbazin's avatar
   
gbazin committed
606
    /* Release the unused interface */
607
608
609
610
611
612
    IDirectDraw_Release(ddobject);

    if (hr != DD_OK) {
        msg_Err(vd, "cannot get IDirectDraw2 interface");
        sys->ddobject = NULL;
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
613
614
615
616
    }

    /* Set DirectDraw Cooperative level, ie what control we want over Windows
     * display */
617
618
619
620
    hr = IDirectDraw2_SetCooperativeLevel(sys->ddobject, NULL, DDSCL_NORMAL);
    if (hr != DD_OK) {
        msg_Err(vd, "cannot set direct draw cooperative level");
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
621
622
    }

623
    /* Get the size of the current display device */
624
    if (sys->hmonitor) {
625
        MONITORINFO monitor_info;
626
        monitor_info.cbSize = sizeof(MONITORINFO);
627
        GetMonitorInfoA(vd->sys->hmonitor, &monitor_info);
628
629
630
631
632
633
        sys->rect_display = monitor_info.rcMonitor;
    } else {
        sys->rect_display.left   = 0;
        sys->rect_display.top    = 0;
        sys->rect_display.right  = GetSystemMetrics(SM_CXSCREEN);
        sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
634
635
    }

636
637
638
639
640
    msg_Dbg(vd, "screen dimensions (%lix%li,%lix%li)",
            sys->rect_display.left,
            sys->rect_display.top,
            sys->rect_display.right,
            sys->rect_display.bottom);
641

gbazin's avatar
   
gbazin committed
642
    /* Probe the capabilities of the hardware */
643
    DirectXGetDDrawCaps(vd);
gbazin's avatar
   
gbazin committed
644

645
    return VLC_SUCCESS;
gbazin's avatar
   
gbazin committed
646
647
}

648
static void DirectXCloseDDraw(vout_display_t *vd)
gbazin's avatar
   
gbazin committed
649
{
650
651
652
    vout_display_sys_t *sys = vd->sys;
    if (sys->ddobject)
        IDirectDraw2_Release(sys->ddobject);
gbazin's avatar
   
gbazin committed
653

654
    sys->ddobject = NULL;
gbazin's avatar
   
gbazin committed
655

656
657
    free(sys->display_driver);
    sys->display_driver = NULL;
gbazin's avatar
   
gbazin committed
658

659
660
    sys->hmonitor = NULL;
}
gbazin's avatar
   
gbazin committed
661

662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
/**
 * Create a clipper that will be used when blitting the RGB surface to the main display.
 *
 * This clipper prevents us to modify by mistake anything on the screen
 * which doesn't belong to our window. For example when a part of our video
 * window is hidden by another window.
 */
static void DirectXCreateClipper(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;
    HRESULT hr;

    /* Create the clipper */
    hr = IDirectDraw2_CreateClipper(sys->ddobject, 0, &sys->clipper, NULL);
    if (hr != DD_OK) {
        msg_Warn(vd, "cannot create clipper (error %li)", hr);
        goto error;
gbazin's avatar
   
gbazin committed
679
680
    }

681
682
683
684
685
686
    /* Associate the clipper to the window */
    hr = IDirectDrawClipper_SetHWnd(sys->clipper, 0, sys->hvideownd);
    if (hr != DD_OK) {
        msg_Warn(vd, "cannot attach clipper to window (error %li)", hr);
        goto error;
    }
gbazin's avatar
   
gbazin committed
687

688
689
690
691
692
693
694
    /* associate the clipper with the surface */
    hr = IDirectDrawSurface_SetClipper(sys->display, sys->clipper);
    if (hr != DD_OK)
    {
        msg_Warn(vd, "cannot attach clipper to surface (error %li)", hr);
        goto error;
    }
gbazin's avatar
   
gbazin committed
695

696
    return;
gbazin's avatar
   
gbazin committed
697

698
699
700
701
error:
    if (sys->clipper)
        IDirectDrawClipper_Release(sys->clipper);
    sys->clipper = NULL;
gbazin's avatar
   
gbazin committed
702
703
}

704
705
706
707
/**
 * It finds out the 32bits RGB pixel value of the colorkey.
 */
static uint32_t DirectXFindColorkey(vout_display_t *vd, uint32_t *color)
gbazin's avatar
   
gbazin committed
708
{
709
710
711
712
713
714
715
716
717
    vout_display_sys_t *sys = vd->sys;
    HRESULT hr;

    /* */
    DDSURFACEDESC ddsd;
    ddsd.dwSize = sizeof(ddsd);
    hr = IDirectDrawSurface2_Lock(sys->display, NULL, &ddsd, DDLOCK_WAIT, NULL);
    if (hr != DD_OK)
        return 0;
gbazin's avatar
   
gbazin committed
718

719
    uint32_t backup = *(uint32_t *)ddsd.lpSurface;
gbazin's avatar
   
gbazin committed
720

721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
    switch (ddsd.ddpfPixelFormat.dwRGBBitCount) {
    case 4:
        *(uint8_t *)ddsd.lpSurface = *color | (*color << 4);
        break;
    case 8:
        *(uint8_t *)ddsd.lpSurface = *color;
        break;
    case 15:
    case 16:
        *(uint16_t *)ddsd.lpSurface = *color;
        break;
    case 24:
        /* Seems to be problematic so we'll just put black as the colorkey */
        *color = 0;
    default:
        *(uint32_t *)ddsd.lpSurface = *color;
        break;
gbazin's avatar
   
gbazin committed
738
    }
739
    IDirectDrawSurface2_Unlock(sys->display, NULL);
gbazin's avatar
   
gbazin committed
740

741
742
743
744
745
746
747
748
    /* */
    HDC hdc;
    COLORREF rgb;
    if (IDirectDrawSurface2_GetDC(sys->display, &hdc) == DD_OK) {
        rgb = GetPixel(hdc, 0, 0);
        IDirectDrawSurface2_ReleaseDC(sys->display, hdc);
    } else {
        rgb = 0;
gbazin's avatar
   
gbazin committed
749
750
    }

751
752
753
754
755
    /* Restore the pixel value */
    ddsd.dwSize = sizeof(ddsd);
    if (IDirectDrawSurface2_Lock(sys->display, NULL, &ddsd, DDLOCK_WAIT, NULL) == DD_OK) {
        *(uint32_t *)ddsd.lpSurface = backup;
        IDirectDrawSurface2_Unlock(sys->display, NULL);
756
    }
gbazin's avatar
   
gbazin committed
757

758
759
    return rgb;
}
gbazin's avatar
   
gbazin committed
760

761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
/**
 * Create and initialize display according to preferences specified in the vout
 * thread fields.
 */
static int DirectXOpenDisplay(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;
    HRESULT hr;

    /* Now get the primary surface. This surface is what you actually see
     * on your screen */
    DDSURFACEDESC ddsd;
    ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_CAPS;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

    LPDIRECTDRAWSURFACE display;
    hr = IDirectDraw2_CreateSurface(sys->ddobject, &ddsd, &display, NULL);
    if (hr != DD_OK) {
        msg_Err(vd, "cannot get primary surface (error %li)", hr);
        return VLC_EGENERIC;
783
    }
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803

    hr = IDirectDrawSurface_QueryInterface(display, &IID_IDirectDrawSurface2,
                                           &sys->display);
    /* Release the old interface */
    IDirectDrawSurface_Release(display);

    if (hr != DD_OK) {
        msg_Err(vd, "cannot query IDirectDrawSurface2 interface (error %li)", hr);
        sys->display = NULL;
        return VLC_EGENERIC;
    }

    /* The clipper will be used only in non-overlay mode */
    DirectXCreateClipper(vd);

    /* Make sure the colorkey will be painted */
    sys->i_colorkey = 1;
    sys->i_rgb_colorkey = DirectXFindColorkey(vd, &sys->i_colorkey);

    return VLC_SUCCESS;
gbazin's avatar
   
gbazin committed
804
}
805
806
807
static void DirectXCloseDisplay(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;
gbazin's avatar
   
gbazin committed
808

809
810
811
812
813
814
815
816
817
818
819
820
821
    if (sys->clipper != NULL)
        IDirectDrawClipper_Release(sys->clipper);

    if (sys->display != NULL)
        IDirectDrawSurface2_Release(sys->display);

    sys->clipper = NULL;
    sys->display = NULL;
}

/**
 * Create an YUV overlay or RGB surface for the video.
 *
gbazin's avatar
   
gbazin committed
822
823
824
 * The best method of display is with an YUV overlay because the YUV->RGB
 * conversion is done in hardware.
 * You can also create a plain RGB surface.
825
 * (Maybe we could also try an RGB overlay surface, which could have hardware
gbazin's avatar
   
gbazin committed
826
827
 * scaling and which would also be faster in window mode because you don't
 * need to do any blitting to the main display...)
828
829
830
831
832
833
834
835
 */
static int DirectXCreateSurface(vout_display_t *vd,
                                LPDIRECTDRAWSURFACE2 *surface,
                                const video_format_t *fmt,
                                DWORD fourcc,
                                bool use_overlay,
                                bool use_sysmem,
                                int backbuffer_count)
gbazin's avatar
   
gbazin committed
836
{
837
838
    vout_display_sys_t *sys = vd->sys;

gbazin's avatar
   
gbazin committed
839
840
    DDSURFACEDESC ddsd;

841
842
843
844
845
846
847
848
    ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize   = sizeof(ddsd);
    ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat);
    ddsd.dwFlags  = DDSD_HEIGHT | DDSD_WIDTH;
    ddsd.dwWidth  = fmt->i_width;
    ddsd.dwHeight = fmt->i_height;
    if (fourcc) {
        ddsd.dwFlags |= DDSD_PIXELFORMAT;
gbazin's avatar
   
gbazin committed
849
        ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
850
        ddsd.ddpfPixelFormat.dwFourCC = fourcc;
gbazin's avatar
   
gbazin committed
851
    }
852
853
854
855
856
    if (use_overlay) {
        ddsd.dwFlags |= DDSD_CAPS;
        ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
        if (backbuffer_count > 0)
            ddsd.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_FLIP;
gbazin's avatar
   
gbazin committed
857

858
859
860
861
862
863
        if (backbuffer_count > 0) {
            ddsd.dwFlags |= DDSD_BACKBUFFERCOUNT;
            ddsd.dwBackBufferCount = backbuffer_count;
        }
    } else {
        ddsd.dwFlags |= DDSD_CAPS;
gbazin's avatar
   
gbazin committed
864
        ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
865
        if (use_sysmem)
gbazin's avatar
   
gbazin committed
866
867
868
869
870
            ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
        else
            ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
    }

871
872
873
874
875
    /* Create the video surface */
    LPDIRECTDRAWSURFACE surface_v1;
    if (IDirectDraw2_CreateSurface(sys->ddobject, &ddsd, &surface_v1, NULL) != DD_OK)
        return VLC_EGENERIC;

gbazin's avatar
   
gbazin committed
876
    /* Now that the surface is created, try to get a newer DirectX interface */
877
878
879
880
881
882
    HRESULT hr = IDirectDrawSurface_QueryInterface(surface_v1,
                                                   &IID_IDirectDrawSurface2,
                                                   (LPVOID *)surface);
    IDirectDrawSurface_Release(surface_v1);
    if (hr != DD_OK) {
        msg_Err(vd, "cannot query IDirectDrawSurface2 interface (error %li)", hr);
883
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
884
885
    }

886
    if (use_overlay) {
887
888
        /* Check the overlay is useable as some graphics cards allow creating
         * several overlays but only one can be used at one time. */
889
890
891
        if (DirectXUpdateOverlay(vd, *surface)) {
            IDirectDrawSurface2_Release(*surface);
            msg_Err(vd, "overlay unuseable (might already be in use)");
892
893
894
895
            return VLC_EGENERIC;
        }
    }

896
    return VLC_SUCCESS;
gbazin's avatar
   
gbazin committed
897
898
}

899
static void DirectXDestroySurface(LPDIRECTDRAWSURFACE2 surface)
gbazin's avatar
   
gbazin committed
900
{
901
902
903
904
905
906
907
908
909
910
    IDirectDrawSurface2_Release(surface);
}
/**
 * This function locks a surface and get the surface descriptor.
 */
static int DirectXLockSurface(LPDIRECTDRAWSURFACE2 front_surface,
                              LPDIRECTDRAWSURFACE2 surface,
                              DDSURFACEDESC *ddsd)
{
    HRESULT hr;
gbazin's avatar
   
gbazin committed
911

912
913
914
    DDSURFACEDESC ddsd_dummy;
    if (!ddsd)
        ddsd = &ddsd_dummy;
gbazin's avatar
   
gbazin committed
915

916
917
918
919
920
921
922
923
924
925
926
927
    ZeroMemory(ddsd, sizeof(*ddsd));
    ddsd->dwSize = sizeof(*ddsd);
    hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL);
    if (hr != DD_OK) {
        if (hr == DDERR_INVALIDPARAMS) {
            /* DirectX 3 doesn't support the DDLOCK_NOSYSLOCK flag, resulting
             * in an invalid params error */
            hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_WAIT, NULL);
        }
        if (hr == DDERR_SURFACELOST) {
            /* Your surface can be lost so be sure
             * to check this and restore it if needed */
gbazin's avatar
gbazin committed
928

929
930
931
932
933
934
            /* When using overlays with back-buffers, we need to restore
             * the front buffer so the back-buffers get restored as well. */
            if (front_surface != surface)
                IDirectDrawSurface2_Restore(front_surface);
            else
                IDirectDrawSurface2_Restore(surface);
gbazin's avatar
gbazin committed
935

936
937
938
939
            hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_WAIT, NULL);
        }
        if (hr != DD_OK)
            return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
940
    }
941
    return VLC_SUCCESS;
gbazin's avatar
   
gbazin committed
942
}
943
944
static void DirectXUnlockSurface(LPDIRECTDRAWSURFACE2 front_surface,
                                 LPDIRECTDRAWSURFACE2 surface)
gbazin's avatar
   
gbazin committed
945
{
946
    VLC_UNUSED(front_surface);
947
948
949
950
951
952
953
    IDirectDrawSurface2_Unlock(surface, NULL);
}
static int DirectXCheckLockingSurface(LPDIRECTDRAWSURFACE2 front_surface,
                                      LPDIRECTDRAWSURFACE2 surface)
{
    if (DirectXLockSurface(front_surface, surface, NULL))
        return VLC_EGENERIC;
954

955
956
    DirectXUnlockSurface(front_surface, surface);
    return VLC_SUCCESS;
gbazin's avatar
   
gbazin committed
957
958
959
960
}



961
962
963
964
typedef struct {
    vlc_fourcc_t codec;
    DWORD        fourcc;
} dx_format_t;
gbazin's avatar
   
gbazin committed
965

966
static DWORD DirectXGetFourcc(vlc_fourcc_t codec)
gbazin's avatar
   
gbazin committed
967
{
968
969
970
971
972
973
974
975
976
977
978
979
980
    static const dx_format_t dx_formats[] = {
        { VLC_CODEC_YUYV, MAKEFOURCC('Y','U','Y','2') },
        { VLC_CODEC_UYVY, MAKEFOURCC('U','Y','V','Y') },
        { VLC_CODEC_YVYU, MAKEFOURCC('Y','V','Y','U') },
        { VLC_CODEC_YV12, MAKEFOURCC('Y','V','1','2') },
        { VLC_CODEC_I420, MAKEFOURCC('Y','V','1','2') },
        { VLC_CODEC_J420, MAKEFOURCC('Y','V','1','2') },
        { 0, 0 }
    };

    for (unsigned i = 0; dx_formats[i].codec != 0; i++) {
        if (dx_formats[i].codec == codec)
            return dx_formats[i].fourcc;
gbazin's avatar
   
gbazin committed
981
    }
982
    return 0;
gbazin's avatar
   
gbazin committed
983
984
}

985
986
987
static int DirectXCreatePictureResourceYuvOverlay(vout_display_t *vd,
                                                  const video_format_t *fmt,
                                                  DWORD fourcc)
gbazin's avatar
   
gbazin committed
988
{
989
    vout_display_sys_t *sys = vd->sys;
gbazin's avatar
   
gbazin committed
990

991
    bool allow_3buf    = var_InheritBool(vd, "directx-3buffering");
gbazin's avatar
   
gbazin committed
992

993
    /* The overlay surface that we create won't be used to decode directly
gbazin's avatar
   
gbazin committed
994
995
996
997
     * into it because accessing video memory directly is way to slow (remember
     * that pictures are decoded macroblock per macroblock). Instead the video
     * will be decoded in picture buffers in system memory which will then be
     * memcpy() to the overlay surface. */
998
999
1000
    LPDIRECTDRAWSURFACE2 front_surface;
    int ret = VLC_EGENERIC;
    if (allow_3buf) {
gbazin's avatar
   
gbazin committed
1001
1002
1003
        /* Triple buffering rocks! it doesn't have any processing overhead
         * (you don't have to wait for the vsync) and provides for a very nice
         * video quality (no tearing). */
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
        ret = DirectXCreateSurface(vd, &front_surface, fmt, fourcc, true, false, 2);
    }
    if (ret)
        ret = DirectXCreateSurface(vd, &front_surface, fmt, fourcc, true, false, 0);
    if (ret)
        return VLC_EGENERIC;
    msg_Dbg(vd, "YUV overlay surface created successfully");

    /* Get the back buffer */
    LPDIRECTDRAWSURFACE2 surface;
    DDSCAPS dds_caps;
    ZeroMemory(&dds_caps, sizeof(dds_caps));
    dds_caps.dwCaps = DDSCAPS_BACKBUFFER;
    if (IDirectDrawSurface2_GetAttachedSurface(front_surface, &dds_caps, &surface) != DD_OK) {
        msg_Warn(vd, "Failed to get surface back buffer");
        /* front buffer is the same as back buffer */
        surface = front_surface;
    }
gbazin's avatar
   
gbazin committed
1022

1023
1024
1025
1026
    if (DirectXCheckLockingSurface(front_surface, surface)) {
        DirectXDestroySurface(front_surface);
        return VLC_EGENERIC;
    }
gbazin's avatar
   
gbazin committed
1027

1028
1029
1030
1031
    /* */
    picture_resource_t *rsc = &sys->resource;
    rsc->p_sys->front_surface = front_surface;
    rsc->p_sys->surface       = surface;
1032
    rsc->p_sys->fallback      = NULL;
1033
1034
1035
1036
1037
1038
1039
    return VLC_SUCCESS;
}
static int DirectXCreatePictureResourceYuv(vout_display_t *vd,
                                           const video_format_t *fmt,
                                           DWORD fourcc)
{
    vout_display_sys_t *sys = vd->sys;
gbazin's avatar
   
gbazin committed
1040

1041
    bool allow_sysmem  = var_InheritBool(vd, "directx-use-sysmem");
gbazin's avatar
   
gbazin committed
1042
1043
1044
1045
1046
1047
1048
1049

    /* As we can't have an overlay, we'll try to create a plain offscreen
     * surface. This surface will reside in video memory because there's a
     * better chance then that we'll be able to use some kind of hardware
     * acceleration like rescaling, blitting or YUV->RGB conversions.
     * We then only need to blit this surface onto the main display when we
     * want to display it */

1050
1051
1052
1053
1054
1055
    /* Check if the chroma is supported first. This is required
     * because a few buggy drivers don't mind creating the surface
     * even if they don't know about the chroma. */
    DWORD count;
    if (IDirectDraw2_GetFourCCCodes(sys->ddobject, &count, NULL) != DD_OK)
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
1056

1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
    DWORD *list = calloc(count, sizeof(*list));
    if (!list)
        return VLC_ENOMEM;
    if (IDirectDraw2_GetFourCCCodes(sys->ddobject, &count, list) != DD_OK) {
        free(list);
        return VLC_EGENERIC;
    }
    unsigned index;
    for (index = 0; index < count; index++) {
        if (list[index] == fourcc)
            break;
    }
    free(list);
    if (index >= count)
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
1072

1073
1074
1075
1076
1077
    /* */
    LPDIRECTDRAWSURFACE2 surface;
    if (DirectXCreateSurface(vd, &surface, fmt, fourcc, false, allow_sysmem, 0))
        return VLC_EGENERIC;
    msg_Dbg(vd, "YUV plain surface created successfully");
gbazin's avatar
   
gbazin committed
1078

1079
1080
1081
1082
    if (DirectXCheckLockingSurface(surface, surface)) {
        DirectXDestroySurface(surface);
        return VLC_EGENERIC;
    }
gbazin's avatar
   
gbazin committed
1083

1084
1085
1086
1087
    /* */
    picture_resource_t *rsc = &sys->resource;
    rsc->p_sys->front_surface = surface;
    rsc->p_sys->surface       = surface;
1088
    rsc->p_sys->fallback      = NULL;
1089
1090
1091
1092
1093
1094
1095
    return VLC_SUCCESS;
}
static int DirectXCreatePictureResourceRgb(vout_display_t *vd,
                                           video_format_t *fmt)
{
    vout_display_sys_t *sys = vd->sys;
    bool allow_sysmem  = var_InheritBool(vd, "directx-use-sysmem");
gbazin's avatar
   
gbazin committed
1096

1097
1098
1099
1100
    /* Our last choice is to use a plain RGB surface */
    DDPIXELFORMAT ddpfPixelFormat;
    ZeroMemory(&ddpfPixelFormat, sizeof(ddpfPixelFormat));
    ddpfPixelFormat.dwSize = sizeof(ddpfPixelFormat);
gbazin's avatar
   
gbazin committed
1101

1102
1103
1104
    IDirectDrawSurface2_GetPixelFormat(sys->display, &ddpfPixelFormat);
    if ((ddpfPixelFormat.dwFlags & DDPF_RGB) == 0)
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
1105

1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
    switch (ddpfPixelFormat.dwRGBBitCount) {
    case 8:
        fmt->i_chroma = VLC_CODEC_RGB8;
        break;
    case 15:
        fmt->i_chroma = VLC_CODEC_RGB15;
        break;
    case 16:
        fmt->i_chroma = VLC_CODEC_RGB16;
        break;
    case 24:
        fmt->i_chroma = VLC_CODEC_RGB24;
        break;
    case 32:
        fmt->i_chroma = VLC_CODEC_RGB32;
        break;
    default:
        msg_Err(vd, "unknown screen depth");
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
1125
    }
1126
1127
1128
    fmt->i_rmask = ddpfPixelFormat.dwRBitMask;
    fmt->i_gmask = ddpfPixelFormat.dwGBitMask;
    fmt->i_bmask = ddpfPixelFormat.dwBBitMask;
gbazin's avatar
   
gbazin committed
1129

1130
1131
1132
1133
1134
1135
1136
1137
    /* */
    LPDIRECTDRAWSURFACE2 surface;
    int ret = DirectXCreateSurface(vd, &surface, fmt, 0, false, allow_sysmem, 0);
    if (ret && !allow_sysmem)
        ret = DirectXCreateSurface(vd, &surface, fmt, 0, false, true, 0);
    if (ret)
        return VLC_EGENERIC;
    msg_Dbg(vd, "RGB plain surface created successfully");
gbazin's avatar
   
gbazin committed
1138

1139
1140
1141
    if (DirectXCheckLockingSurface(surface, surface)) {
        DirectXDestroySurface(surface);
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
1142
1143
    }

1144
1145
1146
1147
    /* */
    picture_resource_t *rsc = &sys->resource;
    rsc->p_sys->front_surface = surface;
    rsc->p_sys->surface       = surface;
1148
    rsc->p_sys->fallback      = NULL;
1149
    return VLC_SUCCESS;
gbazin's avatar
   
gbazin committed
1150
1151
}

1152
1153
1154
static int DirectXCreatePictureResource(vout_display_t *vd,
                                        bool *use_overlay,
                                        video_format_t *fmt)
gbazin's avatar
   
gbazin committed
1155
{
1156
    vout_display_sys_t *sys = vd->sys;
gbazin's avatar
gbazin committed
1157

1158
1159
1160
1161
1162
    /* */
    picture_resource_t *rsc = &sys->resource;
    rsc->p_sys = calloc(1, sizeof(*rsc->p_sys));
    if (!rsc->p_sys)
        return VLC_ENOMEM;
gbazin's avatar
   
gbazin committed
1163

1164
1165
1166
1167
1168
1169
1170
1171