direct3d9.c 64.4 KB
Newer Older
1
/*****************************************************************************
2
 * direct3d9.c: Windows Direct3D9 video output module
3
 *****************************************************************************
4
 * Copyright (C) 2006-2014 VLC authors and VideoLAN
5 6
 *$Id$
 *
7 8
 * Authors: Martell Malone <martellmalone@gmail.com>,
 *          Damien Fouilleul <damienf@videolan.org>,
9 10
 *          Sasha Koruga <skoruga@gmail.com>,
 *          Felix Abecassis <felix.abecassis@gmail.com>
11
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
12 13 14
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
15 16 17 18
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
19 20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
21
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
22 23 24
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 26 27 28 29
 *****************************************************************************/

/*****************************************************************************
 * Preamble:
 *
damienf's avatar
damienf committed
30
 * This plugin will use YUV surface if supported, using YUV will result in
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
31
 * the best video quality (hardware filtering when rescaling the picture)
32 33 34 35
 * 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
damienf's avatar
damienf committed
36
 * effectively display the pictures.
37 38
 *
 *****************************************************************************/
39 40 41 42
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

43
#include <vlc_common.h>
44
#include <vlc_plugin.h>
45
#include <vlc_vout_display.h>
46
#include <vlc_charset.h> /* ToT function */
47 48 49

#include <windows.h>
#include <d3d9.h>
50
#include <d3dx9effect.h>
51
#include "../../video_chroma/d3d9_fmt.h"
52

Laurent Aimar's avatar
Laurent Aimar committed
53
#include "common.h"
54
#include "builtin_shaders.h"
55

56 57
#include <assert.h>

58 59 60
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
61
static int  Open(vlc_object_t *);
Laurent Aimar's avatar
Laurent Aimar committed
62
static void Close(vlc_object_t *);
damienf's avatar
damienf committed
63

Laurent Aimar's avatar
Laurent Aimar committed
64 65
#define DESKTOP_LONGTEXT N_(\
    "The desktop mode allows you to display the video on the desktop.")
66

67 68
#define HW_BLENDING_TEXT N_("Use hardware blending support")
#define HW_BLENDING_LONGTEXT N_(\
69
    "Try to use hardware acceleration for subtitle/OSD blending.")
70

71 72 73 74 75 76 77 78
#define PIXEL_SHADER_TEXT N_("Pixel Shader")
#define PIXEL_SHADER_LONGTEXT N_(\
        "Choose a pixel shader to apply.")
#define PIXEL_SHADER_FILE_TEXT N_("Path to HLSL file")
#define PIXEL_SHADER_FILE_LONGTEXT N_("Path to an HLSL file containing a single pixel shader.")
/* The latest option in the selection list: used for loading a shader file. */
#define SELECTED_SHADER_FILE N_("HLSL File")

79
#define D3D9_HELP N_("Recommended video output for Windows Vista and later versions")
80

81 82 83
static int FindShadersCallback(vlc_object_t *, const char *,
                               char ***, char ***);

84
vlc_module_begin ()
85 86 87
    set_shortname("Direct3D9")
    set_description(N_("Direct3D9 video output"))
    set_help(D3D9_HELP)
Laurent Aimar's avatar
Laurent Aimar committed
88 89
    set_category(CAT_VIDEO)
    set_subcategory(SUBCAT_VIDEO_VOUT)
90

91
    add_bool("direct3d9-hw-blending", true, HW_BLENDING_TEXT, HW_BLENDING_LONGTEXT, true)
92

93
    add_string("direct3d9-shader", "", PIXEL_SHADER_TEXT, PIXEL_SHADER_LONGTEXT, true)
94
        change_string_cb(FindShadersCallback)
95
    add_loadfile("direct3d9-shader-file", NULL, PIXEL_SHADER_FILE_TEXT, PIXEL_SHADER_FILE_LONGTEXT, false)
96

97
    set_capability("vout display", 280)
98
    add_shortcut("direct3d9", "direct3d")
99
    set_callbacks(Open, Close)
100

101
vlc_module_end ()
102 103

/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
104
 * Local prototypes.
105
 *****************************************************************************/
106 107 108 109 110
static const vlc_fourcc_t d3d_subpicture_chromas[] = {
    VLC_CODEC_RGBA,
    0
};

111 112 113 114 115 116 117 118 119 120
typedef struct
{
    const char   *name;
    D3DFORMAT    format;    /* D3D format */
    vlc_fourcc_t fourcc;    /* VLC fourcc */
    uint32_t     rmask;
    uint32_t     gmask;
    uint32_t     bmask;
} d3d_format_t;

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
struct vout_display_sys_t
{
    vout_display_sys_win32_t sys;

    bool allow_hw_yuv;    /* Should we use hardware YUV->RGB conversions */
    struct {
        bool is_fullscreen;
        bool is_on_top;
        RECT win;
    } desktop_save;
    vout_display_cfg_t cfg_saved; /* configuration used before going into desktop mode */

    // core objects
    HINSTANCE               hd3d9_dll;       /* handle of the opened d3d9 dll */
    HINSTANCE               hd3d9x_dll;      /* handle of the opened d3d9x dll */
    IDirect3DPixelShader9*  d3dx_shader;
    LPDIRECT3D9             d3dobj;
    D3DCAPS9                d3dcaps;
    LPDIRECT3DDEVICE9       d3ddev;
    D3DPRESENT_PARAMETERS   d3dpp;
    bool                    use_d3d9ex;

    // scene objects
    LPDIRECT3DTEXTURE9      d3dtex;
    LPDIRECT3DVERTEXBUFFER9 d3dvtc;
146
    D3DFORMAT               d3dregion_format;    /* Backbuffer output format */
147 148
    int                     d3dregion_count;
    struct d3d_region_t     *d3dregion;
149
    const d3d_format_t      *d3dtexture_format;  /* Rendering texture(s) format */
150 151 152 153 154 155 156 157 158 159 160 161 162

    /* */
    bool                    reset_device;
    bool                    reopen_device;
    bool                    lost_not_ready;
    bool                    clear_scene;

    /* It protects the following variables */
    vlc_mutex_t    lock;
    bool           ch_desktop;
    bool           desktop_requested;
};

163 164
static const d3d_format_t *Direct3DFindFormat(vout_display_t *vd, vlc_fourcc_t chroma, D3DFORMAT target);

165
static int  Open(vlc_object_t *);
Laurent Aimar's avatar
Laurent Aimar committed
166

167
static picture_pool_t *Direct3D9CreatePicturePool  (vout_display_t *, unsigned);
168

169 170
static void           Prepare(vout_display_t *, picture_t *, subpicture_t *subpicture);
static void           Display(vout_display_t *, picture_t *, subpicture_t *subpicture);
171 172
static int            Control(vout_display_t *, int, va_list);
static void           Manage (vout_display_t *);
Laurent Aimar's avatar
Laurent Aimar committed
173

174 175 176
static int  Direct3D9Create (vout_display_t *);
static int  Direct3D9Reset  (vout_display_t *);
static void Direct3D9Destroy(vout_display_t *);
Laurent Aimar's avatar
Laurent Aimar committed
177

178 179
static int  Direct3D9Open (vout_display_t *, video_format_t *);
static void Direct3D9Close(vout_display_t *);
Laurent Aimar's avatar
Laurent Aimar committed
180

181 182 183 184 185 186 187 188 189 190
/* */
typedef struct
{
    FLOAT       x,y,z;      // vertex untransformed position
    FLOAT       rhw;        // eye distance
    D3DCOLOR    diffuse;    // diffuse color
    FLOAT       tu, tv;     // texture relative coordinates
} CUSTOMVERTEX;
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)

191 192 193 194
typedef struct d3d_region_t {
    D3DFORMAT          format;
    unsigned           width;
    unsigned           height;
195 196 197 198
    CUSTOMVERTEX       vertex[4];
    LPDIRECT3DTEXTURE9 texture;
} d3d_region_t;

199
static void Direct3D9DeleteRegions(int, d3d_region_t *);
200

201 202
static int  Direct3D9ImportPicture(vout_display_t *vd, d3d_region_t *, LPDIRECT3DSURFACE9 surface);
static void Direct3D9ImportSubpicture(vout_display_t *vd, int *, d3d_region_t **, subpicture_t *);
203

204
static void Direct3D9RenderScene(vout_display_t *vd, d3d_region_t *, int, d3d_region_t *);
Laurent Aimar's avatar
Laurent Aimar committed
205

206
/* */
Laurent Aimar's avatar
Laurent Aimar committed
207 208
static int DesktopCallback(vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void *);

209 210 211 212 213 214 215 216 217 218 219 220
static bool is_d3d9_opaque(vlc_fourcc_t chroma)
{
    switch (chroma)
    {
    case VLC_CODEC_D3D9_OPAQUE:
    case VLC_CODEC_D3D9_OPAQUE_10B:
        return true;
    default:
        return false;
    }
}

Laurent Aimar's avatar
Laurent Aimar committed
221 222 223 224
/**
 * It creates a Direct3D vout display.
 */
static int Open(vlc_object_t *object)
225
{
226 227
    vout_display_t *vd = (vout_display_t *)object;
    vout_display_sys_t *sys;
228

229 230
    OSVERSIONINFO winVer;
    winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
231
    if(GetVersionEx(&winVer) && winVer.dwMajorVersion < 6 && !object->obj.force)
232 233
        return VLC_EGENERIC;

234
    /* Allocate structure */
235 236
    vd->sys = sys = calloc(1, sizeof(vout_display_sys_t));
    if (!sys)
237
        return VLC_ENOMEM;
238

239 240 241
    if (Direct3D9Create(vd)) {
        msg_Err(vd, "Direct3D9 could not be initialized");
        Direct3D9Destroy(vd);
242
        free(sys);
243
        return VLC_EGENERIC;
244 245
    }

246
    sys->sys.use_desktop = var_CreateGetBool(vd, "video-wallpaper");
247
    sys->reset_device = false;
flx42's avatar
flx42 committed
248
    sys->reopen_device = false;
249
    sys->lost_not_ready = false;
250 251 252
    sys->allow_hw_yuv = var_CreateGetBool(vd, "directx-hw-yuv");
    sys->desktop_save.is_fullscreen = vd->cfg->is_fullscreen;
    sys->desktop_save.is_on_top     = false;
253
    sys->desktop_save.win.left      = var_InheritInteger(vd, "video-x");
254
    sys->desktop_save.win.right     = vd->cfg->display.width;
255
    sys->desktop_save.win.top       = var_InheritInteger(vd, "video-y");
256 257 258 259
    sys->desktop_save.win.bottom    = vd->cfg->display.height;

    if (CommonInit(vd))
        goto error;
260

261 262
    /* */
    video_format_t fmt;
263 264
    if (Direct3D9Open(vd, &fmt)) {
        msg_Err(vd, "Direct3D9 could not be opened");
265
        goto error;
266
    }
267

268 269
    /* */
    vout_display_info_t info = vd->info;
270
    info.is_slow = !is_d3d9_opaque(fmt.i_chroma);
271
    info.has_double_click = true;
272
    info.has_hide_mouse = false;
273
    info.has_pictures_invalid = !is_d3d9_opaque(fmt.i_chroma);
274
    if (var_InheritBool(vd, "direct3d9-hw-blending") &&
275
        sys->d3dregion_format != D3DFMT_UNKNOWN &&
276 277 278 279 280 281 282 283
        (sys->d3dcaps.SrcBlendCaps  & D3DPBLENDCAPS_SRCALPHA) &&
        (sys->d3dcaps.DestBlendCaps & D3DPBLENDCAPS_INVSRCALPHA) &&
        (sys->d3dcaps.TextureCaps   & D3DPTEXTURECAPS_ALPHA) &&
        (sys->d3dcaps.TextureOpCaps & D3DTEXOPCAPS_SELECTARG1) &&
        (sys->d3dcaps.TextureOpCaps & D3DTEXOPCAPS_MODULATE))
        info.subpicture_chromas = d3d_subpicture_chromas;
    else
        info.subpicture_chromas = NULL;
284 285 286 287

    /* Interaction */
    vlc_mutex_init(&sys->lock);
    sys->ch_desktop = false;
288
    sys->desktop_requested = sys->sys.use_desktop;
damienf's avatar
damienf committed
289

Laurent Aimar's avatar
Laurent Aimar committed
290
    vlc_value_t val;
291
    val.psz_string = _("Desktop");
292 293
    var_Change(vd, "video-wallpaper", VLC_VAR_SETTEXT, &val, NULL);
    var_AddCallback(vd, "video-wallpaper", DesktopCallback, NULL);
294

295
    /* Setup vout_display now that everything is fine */
296 297
    video_format_Clean(&vd->fmt);
    video_format_Copy(&vd->fmt, &fmt);
298
    vd->info = info;
299

300
    vd->pool = Direct3D9CreatePicturePool;
301 302 303 304 305 306
    vd->prepare = Prepare;
    vd->display = Display;
    vd->control = Control;
    vd->manage  = Manage;

    /* Fix state in case of desktop mode */
307
    if (sys->sys.use_desktop && vd->cfg->is_fullscreen)
308 309 310
        vout_display_SendEventFullscreen(vd, false);

    return VLC_SUCCESS;
311
error:
312
    Direct3D9Close(vd);
313
    CommonClean(vd);
314
    Direct3D9Destroy(vd);
315
    free(vd->sys);
316
    return VLC_EGENERIC;
317 318
}

Laurent Aimar's avatar
Laurent Aimar committed
319 320 321 322 323
/**
 * It destroyes a Direct3D vout display.
 */
static void Close(vlc_object_t *object)
{
324 325
    vout_display_t * vd = (vout_display_t *)object;

326
    var_DelCallback(vd, "video-wallpaper", DesktopCallback, NULL);
327
    vlc_mutex_destroy(&vd->sys->lock);
Laurent Aimar's avatar
Laurent Aimar committed
328

329
    Direct3D9Close(vd);
Laurent Aimar's avatar
Laurent Aimar committed
330

331
    CommonClean(vd);
332

333
    Direct3D9Destroy(vd);
334 335

    free(vd->sys);
336 337
}

338 339
static void DestroyPicture(picture_t *picture)
{
340
    ReleasePictureSys(picture->p_sys);
341 342 343 344 345

    free(picture->p_sys);
    free(picture);
}

346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
/**
 * It locks the surface associated to the picture and get the surface
 * descriptor which amongst other things has the pointer to the picture
 * data and its pitch.
 */
static int Direct3D9LockSurface(picture_t *picture)
{
    /* Lock the surface to get a valid pointer to the picture buffer */
    D3DLOCKED_RECT d3drect;
    HRESULT hr = IDirect3DSurface9_LockRect(picture->p_sys->surface, &d3drect, NULL, 0);
    if (FAILED(hr)) {
        return VLC_EGENERIC;
    }

    CommonUpdatePicture(picture, NULL, d3drect.pBits, d3drect.Pitch);
    return VLC_SUCCESS;
}
/**
 * It unlocks the surface associated to the picture.
 */
static void Direct3D9UnlockSurface(picture_t *picture)
{
    /* Unlock the Surface */
    HRESULT hr = IDirect3DSurface9_UnlockRect(picture->p_sys->surface);
    if (FAILED(hr)) {
        //msg_Dbg(vd, "Failed IDirect3DSurface9_UnlockRect: 0x%0lx", hr);
    }
}

375
/* */
376
static picture_pool_t *Direct3D9CreatePicturePool(vout_display_t *vd, unsigned count)
377
{
378 379 380
    picture_t**       pictures = NULL;
    unsigned          picture_count = 0;

381 382 383
    if ( vd->sys->sys.pool != NULL )
        return vd->sys->sys.pool;

384 385 386
    pictures = calloc(count, sizeof(*pictures));
    if (!pictures)
        goto error;
387

388 389 390 391 392 393 394 395 396 397 398 399 400 401
    D3DFORMAT format;
    switch (vd->fmt.i_chroma)
    {
    case VLC_CODEC_D3D9_OPAQUE_10B:
        format = MAKEFOURCC('P','0','1','0');
        break;
    case VLC_CODEC_D3D9_OPAQUE:
        format = MAKEFOURCC('N','V','1','2');
        break;
    default:
        format = vd->sys->d3dtexture_format->format;
        break;
    }

402 403 404 405 406 407 408
    for (picture_count = 0; picture_count < count; ++picture_count)
    {
        picture_sys_t *picsys = malloc(sizeof(*picsys));
        if (unlikely(picsys == NULL))
            goto error;

        HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(vd->sys->d3ddev,
409 410
                                                          vd->fmt.i_width,
                                                          vd->fmt.i_height,
411
                                                          format,
412 413 414 415 416
                                                          D3DPOOL_DEFAULT,
                                                          &picsys->surface,
                                                          NULL);
        if (FAILED(hr)) {
           msg_Err(vd, "Failed to allocate surface %d (hr=0x%0lx)", picture_count, hr);
417
           free(picsys);
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
           goto error;
        }

        picture_resource_t resource = {
            .p_sys = picsys,
            .pf_destroy = DestroyPicture,
        };

        picture_t *picture = picture_NewFromResource(&vd->fmt, &resource);
        if (unlikely(picture == NULL)) {
            free(picsys);
            goto error;
        }

        pictures[picture_count] = picture;
    }

    picture_pool_configuration_t pool_cfg;
    memset(&pool_cfg, 0, sizeof(pool_cfg));
    pool_cfg.picture_count = count;
    pool_cfg.picture       = pictures;
439 440 441 442 443
    if( !is_d3d9_opaque( vd->fmt.i_chroma ) )
    {
        pool_cfg.lock = Direct3D9LockSurface;
        pool_cfg.unlock = Direct3D9UnlockSurface;
    }
444

445
    vd->sys->sys.pool = picture_pool_NewExtended( &pool_cfg );
446 447

error:
448
    if (vd->sys->sys.pool == NULL && pictures) {
449 450 451
        for (unsigned i=0;i<picture_count; ++i)
            DestroyPicture(pictures[i]);
    }
ssbssa's avatar
ssbssa committed
452
    free(pictures);
453
    return vd->sys->sys.pool;
454
}
455

456
static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
457
{
458
    vout_display_sys_t *sys = vd->sys;
459
    LPDIRECT3DSURFACE9 surface = picture->p_sys->surface;
460

461 462 463 464 465
    /* FIXME it is a bit ugly, we need the surface to be unlocked for
     * rendering.
     *  The clean way would be to release the picture (and ensure that
     * the vout doesn't keep a reference). But because of the vout
     * wrapper, we can't */
466
    if ( !is_d3d9_opaque(picture->format.i_chroma) )
467
        Direct3D9UnlockSurface(picture);
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
    else if (picture->context)
    {
        const struct va_pic_context *pic_ctx = (struct va_pic_context*)picture->context;
        if (picture->p_sys && pic_ctx->picsys.surface != picture->p_sys->surface)
        {
            HRESULT hr;
            RECT visibleSource;
            visibleSource.left = 0;
            visibleSource.top = 0;
            visibleSource.right = picture->format.i_visible_width;
            visibleSource.bottom = picture->format.i_visible_height;
            hr = IDirect3DDevice9_StretchRect( sys->d3ddev, pic_ctx->picsys.surface, &visibleSource, surface, &visibleSource, D3DTEXF_NONE);
            if (FAILED(hr)) {
                msg_Err(vd, "Failed to copy the hw surface to the decoder surface (hr=0x%0lx)", hr );
            }
        }
    }
485 486 487 488 489 490 491

    /* check if device is still available */
    HRESULT hr = IDirect3DDevice9_TestCooperativeLevel(sys->d3ddev);
    if (FAILED(hr)) {
        if (hr == D3DERR_DEVICENOTRESET && !sys->reset_device) {
            vout_display_SendEventPicturesInvalid(vd);
            sys->reset_device = true;
492 493 494 495 496
            sys->lost_not_ready = false;
        }
        if (hr == D3DERR_DEVICELOST && !sys->lost_not_ready) {
            /* Device is lost but not yet ready for reset. */
            sys->lost_not_ready = true;
497 498 499 500
        }
        return;
    }

501
    d3d_region_t picture_region;
502
    if (!Direct3D9ImportPicture(vd, &picture_region, surface)) {
503 504
        picture_region.width = picture->format.i_visible_width;
        picture_region.height = picture->format.i_visible_height;
505 506 507
        int subpicture_region_count     = 0;
        d3d_region_t *subpicture_region = NULL;
        if (subpicture)
508
            Direct3D9ImportSubpicture(vd, &subpicture_region_count, &subpicture_region,
509 510
                                     subpicture);

511
        Direct3D9RenderScene(vd, &picture_region,
512 513
                            subpicture_region_count, subpicture_region);

514
        Direct3D9DeleteRegions(sys->d3dregion_count, sys->d3dregion);
515 516
        sys->d3dregion_count = subpicture_region_count;
        sys->d3dregion       = subpicture_region;
517
    }
518 519
}

520
static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
521 522 523
{
    vout_display_sys_t *sys = vd->sys;

524 525 526 527 528 529 530
    if (sys->lost_not_ready) {
        picture_Release(picture);
        if (subpicture)
            subpicture_Delete(subpicture);
        return;
    }

531 532
    // Present the back buffer contents to the display
    // No stretching should happen here !
533 534
    const RECT src = sys->sys.rect_dest_clipped;
    const RECT dst = sys->sys.rect_dest_clipped;
535 536 537 538 539 540 541 542 543

    HRESULT hr;
    if (sys->use_d3d9ex) {
        LPDIRECT3DDEVICE9EX d3ddev = (LPDIRECT3DDEVICE9EX)sys->d3ddev;
        hr = IDirect3DDevice9Ex_PresentEx(d3ddev, &src, &dst, NULL, NULL, 0);
    } else {
        LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
        hr = IDirect3DDevice9_Present(d3ddev, &src, &dst, NULL, NULL);
    }
544
    if (FAILED(hr)) {
545
        msg_Dbg(vd, "Failed IDirect3DDevice9_Present: 0x%0lx", hr);
546
    }
547

548
    /* XXX See Prepare() */
549
    if ( !is_d3d9_opaque(picture->format.i_chroma) )
550
        Direct3D9LockSurface(picture);
551
    picture_Release(picture);
552 553
    if (subpicture)
        subpicture_Delete(subpicture);
Laurent Aimar's avatar
Laurent Aimar committed
554 555

    CommonDisplay(vd);
556
}
557

558 559 560 561
static int ControlReopenDevice(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;

562
    if (!sys->sys.use_desktop) {
563 564
        /* Save non-desktop state */
        sys->desktop_save.is_fullscreen = vd->cfg->is_fullscreen;
565
        sys->desktop_save.is_on_top     = sys->sys.is_on_top;
566 567

        WINDOWPLACEMENT wp = { .length = sizeof(wp), };
568
        GetWindowPlacement(sys->sys.hparent ? sys->sys.hparent : sys->sys.hwnd, &wp);
569
        sys->desktop_save.win = wp.rcNormalPosition;
570 571
    }

572
    /* */
573
    Direct3D9Close(vd);
574
    EventThreadStop(sys->sys.event);
575 576 577

    /* */
    vlc_mutex_lock(&sys->lock);
578
    sys->sys.use_desktop = sys->desktop_requested;
579 580
    sys->ch_desktop = false;
    vlc_mutex_unlock(&sys->lock);
damienf's avatar
damienf committed
581

582 583 584
    /* */
    event_cfg_t cfg;
    memset(&cfg, 0, sizeof(cfg));
585 586
    cfg.use_desktop = sys->sys.use_desktop;
    if (!sys->sys.use_desktop) {
587 588 589 590
        cfg.x      = sys->desktop_save.win.left;
        cfg.y      = sys->desktop_save.win.top;
        cfg.width  = sys->desktop_save.win.right  - sys->desktop_save.win.left;
        cfg.height = sys->desktop_save.win.bottom - sys->desktop_save.win.top;
591 592 593
    }

    event_hwnd_t hwnd;
594
    if (EventThreadStart(sys->sys.event, &hwnd, &cfg)) {
595 596 597
        msg_Err(vd, "Failed to restart event thread");
        return VLC_EGENERIC;
    }
598 599 600 601 602 603
    sys->sys.parent_window = hwnd.parent_window;
    sys->sys.hparent       = hwnd.hparent;
    sys->sys.hwnd          = hwnd.hwnd;
    sys->sys.hvideownd     = hwnd.hvideownd;
    sys->sys.hfswnd        = hwnd.hfswnd;
    SetRectEmpty(&sys->sys.rect_parent);
604 605 606

    /* */
    video_format_t fmt;
607
    if (Direct3D9Open(vd, &fmt)) {
608 609 610 611 612
        CommonClean(vd);
        msg_Err(vd, "Failed to reopen device");
        return VLC_EGENERIC;
    }
    vd->fmt = fmt;
613
    sys->sys.is_first_display = true;
614

615
    if (sys->sys.use_desktop) {
616 617 618 619
        /* Disable fullscreen/on_top while using desktop */
        if (sys->desktop_save.is_fullscreen)
            vout_display_SendEventFullscreen(vd, false);
        if (sys->desktop_save.is_on_top)
620
            vout_display_SendWindowState(vd, VOUT_WINDOW_STATE_NORMAL);
621 622 623 624 625
    } else {
        /* Restore fullscreen/on_top */
        if (sys->desktop_save.is_fullscreen)
            vout_display_SendEventFullscreen(vd, true);
        if (sys->desktop_save.is_on_top)
626
            vout_display_SendWindowState(vd, VOUT_WINDOW_STATE_ABOVE);
627
    }
628 629
    return VLC_SUCCESS;
}
630
static int Control(vout_display_t *vd, int query, va_list args)
631
{
632 633 634 635 636 637
    vout_display_sys_t *sys = vd->sys;

    switch (query) {
    case VOUT_DISPLAY_RESET_PICTURES:
        /* FIXME what to do here in case of failure */
        if (sys->reset_device) {
638
            if (Direct3D9Reset(vd)) {
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
                msg_Err(vd, "Failed to reset device");
                return VLC_EGENERIC;
            }
            sys->reset_device = false;
        } else if(sys->reopen_device) {
            if (ControlReopenDevice(vd)) {
                msg_Err(vd, "Failed to reopen device");
                return VLC_EGENERIC;
            }
            sys->reopen_device = false;
        }
        return VLC_SUCCESS;
    default:
        return CommonControl(vd, query, args);
    }
654
}
655
static void Manage (vout_display_t *vd)
656
{
657 658 659
    vout_display_sys_t *sys = vd->sys;

    CommonManage(vd);
660

661 662 663 664 665 666 667 668 669 670
    /* Desktop mode change */
    vlc_mutex_lock(&sys->lock);
    const bool ch_desktop = sys->ch_desktop;
    sys->ch_desktop = false;
    vlc_mutex_unlock(&sys->lock);

    if (ch_desktop) {
        sys->reopen_device = true;
        vout_display_SendEventPicturesInvalid(vd);
    }
671

672
    /* Position Change */
673
    if (sys->sys.changes & DX_POSITION_CHANGE) {
674 675 676 677
#if 0 /* need that when bicubic filter is available */
        RECT rect;
        UINT width, height;

678
        GetClientRect(p_sys->sys.hvideownd, &rect);
679 680 681
        width  = rect.right-rect.left;
        height = rect.bottom-rect.top;

Laurent Aimar's avatar
Laurent Aimar committed
682
        if (width != p_sys->d3dpp.BackBufferWidth || height != p_sys->d3dpp.BackBufferHeight)
683
        {
684
            msg_Dbg(vd, "resizing device back buffers to (%lux%lu)", width, height);
685
            // need to reset D3D device to resize back buffer
686
            if (VLC_SUCCESS != Direct3D9ResetDevice(vd, width, height))
687 688 689
                return VLC_EGENERIC;
        }
#endif
690
        sys->clear_scene = true;
691
        sys->sys.changes &= ~DX_POSITION_CHANGE;
692
    }
693 694
}

695
static HINSTANCE Direct3D9LoadShaderLibrary(void)
696 697 698
{
    HINSTANCE instance = NULL;
    for (int i = 43; i > 23; --i) {
ssbssa's avatar
ssbssa committed
699 700 701
        TCHAR filename[16];
        _sntprintf(filename, 16, TEXT("D3dx9_%d.dll"), i);
        instance = LoadLibrary(filename);
702 703 704 705 706 707
        if (instance)
            break;
    }
    return instance;
}

708 709 710
/**
 * It initializes an instance of Direct3D9
 */
711
static int Direct3D9Create(vout_display_t *vd)
712
{
713
    vout_display_sys_t *sys = vd->sys;
damienf's avatar
damienf committed
714

715 716 717
    sys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
    if (!sys->hd3d9_dll) {
        msg_Warn(vd, "cannot load d3d9.dll, aborting");
damienf's avatar
damienf committed
718 719 720
        return VLC_EGENERIC;
    }

Laurent Aimar's avatar
Laurent Aimar committed
721
    LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion);
damienf's avatar
damienf committed
722
    OurDirect3DCreate9 =
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
723
        (void *)GetProcAddress(sys->hd3d9_dll, "Direct3DCreate9");
Laurent Aimar's avatar
Laurent Aimar committed
724
    if (!OurDirect3DCreate9) {
725
        msg_Err(vd, "Cannot locate reference to Direct3DCreate9 ABI in DLL");
damienf's avatar
damienf committed
726 727 728
        return VLC_EGENERIC;
    }

729 730 731 732
    HRESULT (WINAPI *OurDirect3DCreate9Ex)(UINT SDKVersion, IDirect3D9Ex **ppD3D);
    OurDirect3DCreate9Ex =
        (void *)GetProcAddress(sys->hd3d9_dll, "Direct3DCreate9Ex");

733
    /* Create the D3D object. */
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751
    if (OurDirect3DCreate9Ex) {
        LPDIRECT3D9EX d3d9exobj;
        HRESULT hr = OurDirect3DCreate9Ex(D3D_SDK_VERSION, &d3d9exobj);
        if(!FAILED(hr)) {
            msg_Dbg(vd, "Using Direct3D9 Extended API!");
            sys->d3dobj = (LPDIRECT3D9)d3d9exobj;
            sys->use_d3d9ex = true;
        }
    }

    if (!sys->d3dobj)
    {
        LPDIRECT3D9 d3dobj = OurDirect3DCreate9(D3D_SDK_VERSION);
        if (!d3dobj) {
            msg_Err(vd, "Could not create Direct3D9 instance.");
            return VLC_EGENERIC;
        }
        sys->d3dobj = d3dobj;
752 753
    }

754
    sys->hd3d9x_dll = Direct3D9LoadShaderLibrary();
755
    if (!sys->hd3d9x_dll)
756
        msg_Warn(vd, "cannot load Direct3D9 Shader Library; HLSL pixel shading will be disabled.");
757

758 759 760
    /*
    ** Get device capabilities
    */
761
    ZeroMemory(&sys->d3dcaps, sizeof(sys->d3dcaps));
762
    HRESULT hr = IDirect3D9_GetDeviceCaps(sys->d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &sys->d3dcaps);
Laurent Aimar's avatar
Laurent Aimar committed
763
    if (FAILED(hr)) {
764
       msg_Err(vd, "Could not read adapter capabilities. (hr=0x%0lx)", hr);
765 766
       return VLC_EGENERIC;
    }
767

768
    /* TODO: need to test device capabilities and select the right render function */
769
    if (!(sys->d3dcaps.DevCaps2 & D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES)) {
770 771 772
        msg_Err(vd, "Device does not support stretching from textures.");
        return VLC_EGENERIC;
    }
773

774 775 776 777 778 779 780 781 782 783
    if ( vd->fmt.i_width > sys->d3dcaps.MaxTextureWidth ||
         vd->fmt.i_height > sys->d3dcaps.MaxTextureHeight )
    {
        msg_Err(vd, "Textures too large %ux%u max possible: %ux%u",
                vd->fmt.i_width, vd->fmt.i_height,
                (unsigned) sys->d3dcaps.MaxTextureWidth,
                (unsigned) sys->d3dcaps.MaxTextureHeight);
        return VLC_EGENERIC;
    }

784 785 786
    return VLC_SUCCESS;
}

787 788 789
/**
 * It releases an instance of Direct3D9
 */
790
static void Direct3D9Destroy(vout_display_t *vd)
791
{
792
    vout_display_sys_t *sys = vd->sys;
Laurent Aimar's avatar
Laurent Aimar committed
793

794 795 796 797
    if (sys->d3dobj)
       IDirect3D9_Release(sys->d3dobj);
    if (sys->hd3d9_dll)
        FreeLibrary(sys->hd3d9_dll);
798 799
    if (sys->hd3d9x_dll)
        FreeLibrary(sys->hd3d9x_dll);
Laurent Aimar's avatar
Laurent Aimar committed
800

801 802
    sys->d3dobj = NULL;
    sys->hd3d9_dll = NULL;
803
    sys->hd3d9x_dll = NULL;
804
}
805

806 807 808 809 810

/**
 * It setup vout_display_sys_t::d3dpp and vout_display_sys_t::rect_display
 * from the default adapter.
 */
811
static int Direct3D9FillPresentationParameters(vout_display_t *vd)
812
{
813
    vout_display_sys_t *sys = vd->sys;
814 815 816 817 818

    /*
    ** Get the current desktop display mode, so we can set up a back
    ** buffer of the same format
    */
819 820 821
    D3DDISPLAYMODE d3ddm;
    HRESULT hr = IDirect3D9_GetAdapterDisplayMode(sys->d3dobj,
                                                  D3DADAPTER_DEFAULT, &d3ddm);
Laurent Aimar's avatar
Laurent Aimar committed
822
    if (FAILED(hr)) {
823
       msg_Err(vd, "Could not read adapter display mode. (hr=0x%0lx)", hr);
824 825 826 827
       return VLC_EGENERIC;
    }

    /* Set up the structure used to create the D3DDevice. */
828
    D3DPRESENT_PARAMETERS *d3dpp = &vd->sys->d3dpp;
Laurent Aimar's avatar
Laurent Aimar committed
829
    ZeroMemory(d3dpp, sizeof(D3DPRESENT_PARAMETERS));
830 831
    d3dpp->Flags                  = D3DPRESENTFLAG_VIDEO;
    d3dpp->Windowed               = TRUE;
832
    d3dpp->hDeviceWindow          = vd->sys->sys.hvideownd;
833
    d3dpp->BackBufferWidth        = __MAX((unsigned int)GetSystemMetrics(SM_CXVIRTUALSCREEN),
834
                                          vd->source.i_width);
835
    d3dpp->BackBufferHeight       = __MAX((unsigned int)GetSystemMetrics(SM_CYVIRTUALSCREEN),
836
                                          vd->source.i_height);
837
    d3dpp->SwapEffect             = D3DSWAPEFFECT_COPY;
838 839 840 841 842 843
    d3dpp->MultiSampleType        = D3DMULTISAMPLE_NONE;
    d3dpp->PresentationInterval   = D3DPRESENT_INTERVAL_DEFAULT;
    d3dpp->BackBufferFormat       = d3ddm.Format;
    d3dpp->BackBufferCount        = 1;
    d3dpp->EnableAutoDepthStencil = FALSE;

844
    /* */
845
    RECT *display = &vd->sys->sys.rect_display;
846 847 848 849 850
    display->left   = 0;
    display->top    = 0;
    display->right  = d3dpp->BackBufferWidth;
    display->bottom = d3dpp->BackBufferHeight;

851 852 853
    return VLC_SUCCESS;
}

854
/* */
855 856
static int  Direct3D9CreateResources (vout_display_t *, video_format_t *);
static void Direct3D9DestroyResources(vout_display_t *);
857 858

/**
859
 * It creates a Direct3D9 device and the associated resources.
860
 */
861
static int Direct3D9Open(vout_display_t *vd, video_format_t *fmt)
862
{
863 864
    vout_display_sys_t *sys = vd->sys;
    LPDIRECT3D9 d3dobj = sys->d3dobj;
865

866
    if (Direct3D9FillPresentationParameters(vd))
867
        return VLC_EGENERIC;
868

869 870 871 872 873 874 875 876 877

    UINT AdapterToUse = D3DADAPTER_DEFAULT;
    D3DDEVTYPE DeviceType = D3DDEVTYPE_HAL;

#ifndef NDEBUG
    // Look for 'NVIDIA PerfHUD' adapter
    // If it is present, override default settings
    for (UINT Adapter=0; Adapter< IDirect3D9_GetAdapterCount(d3dobj); ++Adapter) {
        D3DADAPTER_IDENTIFIER9 Identifier;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
878 879