1 /*****************************************************************************
2 * direct3d.c: Windows Direct3D video output module
3 *****************************************************************************
4 * Copyright (C) 2006-2009 the VideoLAN team
7 * Authors: Damien Fouilleul <damienf@videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
27 * This plugin will use YUV surface if supported, using YUV will result in
28 * the best video quality (hardware filering when rescaling the picture)
29 * and the fastest display as it requires less processing.
31 * If YUV overlay is not supported this plugin will use RGB offscreen video
32 * surfaces that will be blitted onto the primary surface (display) to
33 * effectively display the pictures.
35 *****************************************************************************/
40 #include <vlc_common.h>
41 #include <vlc_plugin.h>
42 #include <vlc_playlist.h>
43 #include <vlc_vout_display.h>
50 /*****************************************************************************
52 *****************************************************************************/
53 static int OpenVideoXP(vlc_object_t *);
54 static int OpenVideoVista(vlc_object_t *);
55 static void Close(vlc_object_t *);
57 #define DESKTOP_TEXT N_("Enable desktop mode ")
58 #define DESKTOP_LONGTEXT N_(\
59 "The desktop mode allows you to display the video on the desktop.")
62 set_shortname("Direct3D")
63 set_category(CAT_VIDEO)
64 set_subcategory(SUBCAT_VIDEO_VOUT)
66 add_bool("direct3d-desktop", false, NULL, DESKTOP_TEXT, DESKTOP_LONGTEXT, true)
68 set_description(N_("DirectX 3D video output"))
69 set_capability("vout display", 70)
70 add_shortcut("direct3d_xp")
71 set_callbacks(OpenVideoXP, Close)
73 /* FIXME: Hack to avoid unregistering our window class */
74 linked_with_a_crap_library_which_uses_atexit()
77 set_capability("vout display", 150)
78 add_shortcut("direct3d_vista")
79 set_callbacks(OpenVideoVista, Close)
83 /* check if we registered a window class because we need to
86 if (GetClassInfo(GetModuleHandle(NULL), "VLC DirectX", &wndclass))
87 UnregisterClass("VLC DirectX", GetModuleHandle(NULL));
90 /*****************************************************************************
92 *****************************************************************************/
95 LPDIRECT3DSURFACE9 surface;
98 static int Open(vlc_object_t *);
100 static picture_t *Get (vout_display_t *);
101 static void Prepare(vout_display_t *, picture_t *);
102 static void Display(vout_display_t *, picture_t *);
103 static int Control(vout_display_t *, int, va_list);
104 static void Manage (vout_display_t *);
106 static int Direct3DCreate (vout_display_t *);
107 static int Direct3DReset (vout_display_t *);
108 static void Direct3DDestroy(vout_display_t *);
110 static int Direct3DOpen (vout_display_t *, video_format_t *);
111 static void Direct3DClose(vout_display_t *);
113 static void Direct3DRenderScene(vout_display_t *vd, LPDIRECT3DSURFACE9 surface);
116 static int DesktopCallback(vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void *);
119 * It creates a Direct3D vout display.
121 static int Open(vlc_object_t *object)
123 vout_display_t *vd = (vout_display_t *)object;
124 vout_display_sys_t *sys;
126 /* Allocate structure */
127 vd->sys = sys = calloc(1, sizeof(vout_display_sys_t));
131 if (Direct3DCreate(vd)) {
132 msg_Err(vd, "Direct3D could not be initialized");
138 sys->use_desktop = var_CreateGetBool(vd, "direct3d-desktop");
139 sys->reset_device = false;
140 sys->reset_device = false;
141 sys->allow_hw_yuv = var_CreateGetBool(vd, "directx-hw-yuv");
142 sys->desktop_save.is_fullscreen = vd->cfg->is_fullscreen;
143 sys->desktop_save.is_on_top = false;
144 sys->desktop_save.win.left = 0;
145 sys->desktop_save.win.right = vd->cfg->display.width;
146 sys->desktop_save.win.top = 0;
147 sys->desktop_save.win.bottom = vd->cfg->display.height;
154 if (Direct3DOpen(vd, &fmt)) {
155 msg_Err(vd, "Direct3D could not be opened");
160 vout_display_info_t info = vd->info;
162 info.has_double_click = true;
163 info.has_hide_mouse = true;
164 info.has_pictures_invalid = true;
167 vlc_mutex_init(&sys->lock);
168 sys->ch_desktop = false;
169 sys->desktop_requested = sys->use_desktop;
172 val.psz_string = _("Desktop");
173 var_Change(vd, "direct3d-desktop", VLC_VAR_SETTEXT, &val, NULL);
174 var_AddCallback(vd, "direct3d-desktop", DesktopCallback, NULL);
176 /* Setup vout_display now that everything is fine */
181 vd->prepare = Prepare;
182 vd->display = Display;
183 vd->control = Control;
186 /* Fix state in case of desktop mode */
187 if (sys->use_desktop && vd->cfg->is_fullscreen)
188 vout_display_SendEventFullscreen(vd, false);
192 Close(VLC_OBJECT(vd));
196 static bool IsVistaOrAbove(void)
198 OSVERSIONINFO winVer;
199 winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
200 return GetVersionEx(&winVer) && winVer.dwMajorVersion > 5;
203 static int OpenVideoXP(vlc_object_t *obj)
205 /* Windows XP or lower, make sure this module isn't the default */
206 return IsVistaOrAbove() ? VLC_EGENERIC : Open(obj);
209 static int OpenVideoVista(vlc_object_t *obj)
211 /* Windows Vista or above, make this module the default */
212 return IsVistaOrAbove() ? Open(obj) : VLC_EGENERIC;
216 * It destroyes a Direct3D vout display.
218 static void Close(vlc_object_t *object)
220 vout_display_t * vd = (vout_display_t *)object;
222 var_DelCallback(vd, "direct3d-desktop", DesktopCallback, NULL);
234 static picture_t *Get(vout_display_t *vd)
236 vout_display_sys_t *sys = vd->sys;
240 return picture_pool_Get(sys->pool);
243 static int Direct3DLockSurface(picture_t *);
244 static void Direct3DUnlockSurface(picture_t *);
246 static void Prepare(vout_display_t *vd, picture_t *picture)
248 LPDIRECT3DSURFACE9 surface = picture->p_sys->surface;
250 picture_Release(picture);
251 Direct3DRenderScene(vd, surface);
253 /* FIXME it is a bit ugly, we need the surface to be unlocked for
255 * The clean way would be to release the picture (and ensure that
256 * the vout doesn't keep a reference). But because of the vout
257 * wrapper, we can't */
259 Direct3DUnlockSurface(picture);
261 Direct3DRenderScene(vd, surface);
263 Direct3DLockSurface(picture);
267 static void Display(vout_display_t *vd, picture_t *picture)
269 vout_display_sys_t *sys = vd->sys;
270 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
272 // Present the back buffer contents to the display
273 // No stretching should happen here !
274 const RECT src = sys->rect_dest_clipped;
275 const RECT dst = sys->rect_dest_clipped;
276 HRESULT hr = IDirect3DDevice9_Present(d3ddev, &src, &dst, NULL, NULL);
278 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
283 /* XXX See Prepare() */
284 picture_Release(picture);
289 static int ControlResetDevice(vout_display_t *vd)
291 return Direct3DReset(vd);
293 static int ControlReopenDevice(vout_display_t *vd)
295 vout_display_sys_t *sys = vd->sys;
297 if (!sys->use_desktop) {
298 /* Save non-desktop state */
299 sys->desktop_save.is_fullscreen = vd->cfg->is_fullscreen;
300 sys->desktop_save.is_on_top = sys->is_on_top;
302 WINDOWPLACEMENT wp = { .length = sizeof(wp), };
303 GetWindowPlacement(sys->hparent ? sys->hparent : sys->hwnd, &wp);
304 sys->desktop_save.win = wp.rcNormalPosition;
309 EventThreadStop(sys->event);
312 vlc_mutex_lock(&sys->lock);
313 sys->use_desktop = sys->desktop_requested;
314 sys->ch_desktop = false;
315 vlc_mutex_unlock(&sys->lock);
319 memset(&cfg, 0, sizeof(cfg));
320 cfg.use_desktop = sys->use_desktop;
321 if (!sys->use_desktop) {
322 cfg.win.type = VOUT_WINDOW_TYPE_HWND;
323 cfg.win.x = sys->desktop_save.win.left;
324 cfg.win.y = sys->desktop_save.win.top;
325 cfg.win.width = sys->desktop_save.win.right - sys->desktop_save.win.left;
326 cfg.win.height = sys->desktop_save.win.bottom - sys->desktop_save.win.top;
330 if (EventThreadStart(sys->event, &hwnd, &cfg)) {
331 msg_Err(vd, "Failed to restart event thread");
334 sys->parent_window = hwnd.parent_window;
335 sys->hparent = hwnd.hparent;
336 sys->hwnd = hwnd.hwnd;
337 sys->hvideownd = hwnd.hvideownd;
338 sys->hfswnd = hwnd.hfswnd;
339 SetRectEmpty(&sys->rect_parent);
343 if (Direct3DOpen(vd, &fmt)) {
345 msg_Err(vd, "Failed to reopen device");
349 sys->is_first_display = true;
351 if (sys->use_desktop) {
352 /* Disable fullscreen/on_top while using desktop */
353 if (sys->desktop_save.is_fullscreen)
354 vout_display_SendEventFullscreen(vd, false);
355 if (sys->desktop_save.is_on_top)
356 vout_display_SendEventOnTop(vd, false);
358 /* Restore fullscreen/on_top */
359 if (sys->desktop_save.is_fullscreen)
360 vout_display_SendEventFullscreen(vd, true);
361 if (sys->desktop_save.is_on_top)
362 vout_display_SendEventOnTop(vd, true);
366 static int Control(vout_display_t *vd, int query, va_list args)
368 vout_display_sys_t *sys = vd->sys;
371 case VOUT_DISPLAY_RESET_PICTURES:
372 /* FIXME what to do here in case of failure */
373 if (sys->reset_device) {
374 if (ControlResetDevice(vd)) {
375 msg_Err(vd, "Failed to reset device");
378 sys->reset_device = false;
379 } else if(sys->reopen_device) {
380 if (ControlReopenDevice(vd)) {
381 msg_Err(vd, "Failed to reopen device");
384 sys->reopen_device = false;
388 return CommonControl(vd, query, args);
391 static void Manage (vout_display_t *vd)
393 vout_display_sys_t *sys = vd->sys;
397 /* Desktop mode change */
398 vlc_mutex_lock(&sys->lock);
399 const bool ch_desktop = sys->ch_desktop;
400 sys->ch_desktop = false;
401 vlc_mutex_unlock(&sys->lock);
404 sys->reopen_device = true;
405 vout_display_SendEventPicturesInvalid(vd);
412 if (sys->changes & DX_POSITION_CHANGE) {
413 #if 0 /* need that when bicubic filter is available */
417 GetClientRect(p_sys->hvideownd, &rect);
418 width = rect.right-rect.left;
419 height = rect.bottom-rect.top;
421 if (width != p_sys->d3dpp.BackBufferWidth || height != p_sys->d3dpp.BackBufferHeight)
423 msg_Dbg(vd, "resizing device back buffers to (%lux%lu)", width, height);
424 // need to reset D3D device to resize back buffer
425 if (VLC_SUCCESS != Direct3DResetDevice(vd, width, height))
429 sys->changes &= ~DX_POSITION_CHANGE;
435 * It initializes an instance of Direct3D9
437 static int Direct3DCreate(vout_display_t *vd)
439 vout_display_sys_t *sys = vd->sys;
441 sys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
442 if (!sys->hd3d9_dll) {
443 msg_Warn(vd, "cannot load d3d9.dll, aborting");
447 LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion);
449 (void *)GetProcAddress(sys->hd3d9_dll, TEXT("Direct3DCreate9"));
450 if (!OurDirect3DCreate9) {
451 msg_Err(vd, "Cannot locate reference to Direct3DCreate9 ABI in DLL");
455 /* Create the D3D object. */
456 LPDIRECT3D9 d3dobj = OurDirect3DCreate9(D3D_SDK_VERSION);
458 msg_Err(vd, "Could not create Direct3D9 instance.");
461 sys->d3dobj = d3dobj;
464 ** Get device capabilities
467 ZeroMemory(&d3dCaps, sizeof(d3dCaps));
468 HRESULT hr = IDirect3D9_GetDeviceCaps(d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps);
470 msg_Err(vd, "Could not read adapter capabilities. (hr=0x%lX)", hr);
473 /* TODO: need to test device capabilities and select the right render function */
479 * It releases an instance of Direct3D9
481 static void Direct3DDestroy(vout_display_t *vd)
483 vout_display_sys_t *sys = vd->sys;
486 IDirect3D9_Release(sys->d3dobj);
488 FreeLibrary(sys->hd3d9_dll);
491 sys->hd3d9_dll = NULL;
496 * It setup vout_display_sys_t::d3dpp and vout_display_sys_t::rect_display
497 * from the default adapter.
499 static int Direct3DFillPresentationParameters(vout_display_t *vd)
501 vout_display_sys_t *sys = vd->sys;
504 ** Get the current desktop display mode, so we can set up a back
505 ** buffer of the same format
507 D3DDISPLAYMODE d3ddm;
508 HRESULT hr = IDirect3D9_GetAdapterDisplayMode(sys->d3dobj,
509 D3DADAPTER_DEFAULT, &d3ddm);
511 msg_Err(vd, "Could not read adapter display mode. (hr=0x%lX)", hr);
515 /* Set up the structure used to create the D3DDevice. */
516 D3DPRESENT_PARAMETERS *d3dpp = &vd->sys->d3dpp;
517 ZeroMemory(d3dpp, sizeof(D3DPRESENT_PARAMETERS));
518 d3dpp->Flags = D3DPRESENTFLAG_VIDEO;
519 d3dpp->Windowed = TRUE;
520 d3dpp->hDeviceWindow = vd->sys->hvideownd;
521 d3dpp->BackBufferWidth = d3ddm.Width;
522 d3dpp->BackBufferHeight = d3ddm.Height;
523 d3dpp->SwapEffect = D3DSWAPEFFECT_COPY;
524 d3dpp->MultiSampleType = D3DMULTISAMPLE_NONE;
525 d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
526 d3dpp->BackBufferFormat = d3ddm.Format;
527 d3dpp->BackBufferCount = 1;
528 d3dpp->EnableAutoDepthStencil = FALSE;
530 const unsigned adapter_count = IDirect3D9_GetAdapterCount(sys->d3dobj);
531 for (unsigned i = 1; i < adapter_count; i++) {
532 hr = IDirect3D9_GetAdapterDisplayMode(sys->d3dobj, i, &d3ddm);
535 d3dpp->BackBufferWidth = __MAX(d3dpp->BackBufferWidth, d3ddm.Width);
536 d3dpp->BackBufferHeight = __MAX(d3dpp->BackBufferHeight, d3ddm.Height);
540 RECT *display = &vd->sys->rect_display;
543 display->right = d3dpp->BackBufferWidth;
544 display->bottom = d3dpp->BackBufferHeight;
550 static int Direct3DCreateResources (vout_display_t *, video_format_t *);
551 static void Direct3DDestroyResources(vout_display_t *);
554 * It creates a Direct3D device and the associated resources.
556 static int Direct3DOpen(vout_display_t *vd, video_format_t *fmt)
558 vout_display_sys_t *sys = vd->sys;
559 LPDIRECT3D9 d3dobj = sys->d3dobj;
561 if (Direct3DFillPresentationParameters(vd))
564 // Create the D3DDevice
565 LPDIRECT3DDEVICE9 d3ddev;
566 HRESULT hr = IDirect3D9_CreateDevice(d3dobj, D3DADAPTER_DEFAULT,
567 D3DDEVTYPE_HAL, sys->hvideownd,
568 D3DCREATE_SOFTWARE_VERTEXPROCESSING|
569 D3DCREATE_MULTITHREADED,
570 &sys->d3dpp, &d3ddev);
572 msg_Err(vd, "Could not create the D3D device! (hr=0x%lX)", hr);
575 sys->d3ddev = d3ddev;
577 UpdateRects(vd, NULL, NULL, true);
579 if (Direct3DCreateResources(vd, fmt)) {
580 msg_Err(vd, "Failed to allocate resources");
584 /* Change the window title bar text */
585 EventThreadUpdateTitle(sys->event, VOUT_TITLE " (Direct3D output)");
587 msg_Dbg(vd, "Direct3D device adapter successfully initialized");
592 * It releases the Direct3D9 device and its resources.
594 static void Direct3DClose(vout_display_t *vd)
596 vout_display_sys_t *sys = vd->sys;
598 Direct3DDestroyResources(vd);
601 IDirect3DDevice9_Release(sys->d3ddev);
604 sys->hmonitor = NULL;
608 * It reset the Direct3D9 device and its resources.
610 static int Direct3DReset(vout_display_t *vd)
612 vout_display_sys_t *sys = vd->sys;
613 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
615 if (Direct3DFillPresentationParameters(vd))
618 /* release all D3D objects */
619 Direct3DDestroyResources(vd);
622 HRESULT hr = IDirect3DDevice9_Reset(d3ddev, &sys->d3dpp);
624 msg_Err(vd, "%s failed ! (hr=%08lX)", __FUNCTION__, hr);
628 UpdateRects(vd, NULL, NULL, true);
631 if (Direct3DCreateResources(vd, &vd->fmt)) {
632 msg_Dbg(vd, "%s failed !", __FUNCTION__);
639 static int Direct3DCreatePool(vout_display_t *vd, video_format_t *fmt);
640 static void Direct3DDestroyPool(vout_display_t *vd);
642 static int Direct3DCreateScene(vout_display_t *vd);
643 static void Direct3DDestroyScene(vout_display_t *vd);
646 * It creates the picture and scene resources.
648 static int Direct3DCreateResources(vout_display_t *vd, video_format_t *fmt)
650 if (Direct3DCreatePool(vd, fmt)) {
651 msg_Err(vd, "Direct3D picture pool initialization failed");
654 if (Direct3DCreateScene(vd)) {
655 msg_Err(vd, "Direct3D scene initialization failed !");
661 * It destroys the picture and scene resources.
663 static void Direct3DDestroyResources(vout_display_t *vd)
665 Direct3DDestroyScene(vd);
666 Direct3DDestroyPool(vd);
670 * It tests if the conversion from src to dst is supported.
672 static int Direct3DCheckConversion(vout_display_t *vd,
673 D3DFORMAT src, D3DFORMAT dst)
675 vout_display_sys_t *sys = vd->sys;
676 LPDIRECT3D9 d3dobj = sys->d3dobj;
679 /* test whether device can create a surface of that format */
680 hr = IDirect3D9_CheckDeviceFormat(d3dobj, D3DADAPTER_DEFAULT,
681 D3DDEVTYPE_HAL, dst, 0,
682 D3DRTYPE_SURFACE, src);
684 /* test whether device can perform color-conversion
685 ** from that format to target format
687 hr = IDirect3D9_CheckDeviceFormatConversion(d3dobj,
692 if (!SUCCEEDED(hr)) {
693 if (D3DERR_NOTAVAILABLE != hr)
694 msg_Err(vd, "Could not query adapter supported formats. (hr=0x%lX)", hr);
703 D3DFORMAT format; /* D3D format */
704 vlc_fourcc_t fourcc; /* VLC fourcc */
710 static const d3d_format_t d3d_formats[] = {
711 /* YV12 is always used for planar 420, the planes are then swapped in Lock() */
712 { "YV12", MAKEFOURCC('Y','V','1','2'), VLC_CODEC_YV12, 0,0,0 },
713 { "YV12", MAKEFOURCC('Y','V','1','2'), VLC_CODEC_I420, 0,0,0 },
714 { "YV12", MAKEFOURCC('Y','V','1','2'), VLC_CODEC_J420, 0,0,0 },
715 { "UYVY", D3DFMT_UYVY, VLC_CODEC_UYVY, 0,0,0 },
716 { "YUY2", D3DFMT_YUY2, VLC_CODEC_YUYV, 0,0,0 },
717 { "X8R8G8B8", D3DFMT_X8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
718 { "A8R8G8B8", D3DFMT_A8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
719 { "8G8B8", D3DFMT_R8G8B8, VLC_CODEC_RGB24, 0xff0000, 0x00ff00, 0x0000ff },
720 { "R5G6B5", D3DFMT_R5G6B5, VLC_CODEC_RGB16, 0x1f<<11, 0x3f<<5, 0x1f<<0 },
721 { "X1R5G5B5", D3DFMT_X1R5G5B5,VLC_CODEC_RGB15, 0x1f<<10, 0x1f<<5, 0x1f<<0 },
727 * It returns the format (closest to chroma) that can be converted to target */
728 static const d3d_format_t *Direct3DFindFormat(vout_display_t *vd, vlc_fourcc_t chroma, D3DFORMAT target)
730 vout_display_sys_t *sys = vd->sys;
732 for (unsigned pass = 0; pass < 2; pass++) {
733 const vlc_fourcc_t *list;
735 if (pass == 0 && sys->allow_hw_yuv && vlc_fourcc_IsYUV(chroma))
736 list = vlc_fourcc_GetYUVFallback(chroma);
738 list = vlc_fourcc_GetRGBFallback(chroma);
742 for (unsigned i = 0; list[i] != 0; i++) {
743 for (unsigned j = 0; d3d_formats[j].name; j++) {
744 const d3d_format_t *format = &d3d_formats[j];
746 if (format->fourcc != list[i])
749 msg_Warn(vd, "trying surface pixel format: %s",
751 if (!Direct3DCheckConversion(vd, format->format, target)) {
752 msg_Dbg(vd, "selected surface pixel format is %s",
763 * It locks the surface associated to the picture and get the surface
764 * descriptor which amongst other things has the pointer to the picture
765 * data and its pitch.
767 static int Direct3DLockSurface(picture_t *picture)
769 /* Lock the surface to get a valid pointer to the picture buffer */
770 D3DLOCKED_RECT d3drect;
771 HRESULT hr = IDirect3DSurface9_LockRect(picture->p_sys->surface, &d3drect, NULL, 0);
773 //msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
777 /* fill in buffer info in first plane */
778 picture->p->p_pixels = d3drect.pBits;
779 picture->p->i_pitch = d3drect.Pitch;
781 /* Fill chroma planes for planar YUV */
782 if (picture->format.i_chroma == VLC_CODEC_I420 ||
783 picture->format.i_chroma == VLC_CODEC_J420 ||
784 picture->format.i_chroma == VLC_CODEC_YV12) {
786 for (int n = 1; n < picture->i_planes; n++) {
787 const plane_t *o = &picture->p[n-1];
788 plane_t *p = &picture->p[n];
790 p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
791 p->i_pitch = d3drect.Pitch / 2;
793 /* The d3d buffer is always allocated as YV12 */
794 if (vlc_fourcc_AreUVPlanesSwapped(picture->format.i_chroma, VLC_CODEC_YV12)) {
795 uint8_t *p_tmp = picture->p[1].p_pixels;
796 picture->p[1].p_pixels = picture->p[2].p_pixels;
797 picture->p[2].p_pixels = p_tmp;
803 * It unlocks the surface associated to the picture.
805 static void Direct3DUnlockSurface(picture_t *picture)
807 /* Unlock the Surface */
808 HRESULT hr = IDirect3DSurface9_UnlockRect(picture->p_sys->surface);
810 //msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
815 * It creates the pool of picture (only 1).
817 * Each picture has an associated offscreen surface in video memory
818 * depending on hardware capabilities the picture chroma will be as close
819 * as possible to the orginal render chroma to reduce CPU conversion overhead
820 * and delegate this work to video card GPU
822 static int Direct3DCreatePool(vout_display_t *vd, video_format_t *fmt)
824 vout_display_sys_t *sys = vd->sys;
825 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
830 /* Find the appropriate D3DFORMAT for the render chroma, the format will be the closest to
831 * the requested chroma which is usable by the hardware in an offscreen surface, as they
832 * typically support more formats than textures */
833 const d3d_format_t *d3dfmt = Direct3DFindFormat(vd, fmt->i_chroma, sys->d3dpp.BackBufferFormat);
835 msg_Err(vd, "surface pixel format is not supported.");
838 fmt->i_chroma = d3dfmt->fourcc;
839 fmt->i_rmask = d3dfmt->rmask;
840 fmt->i_gmask = d3dfmt->gmask;
841 fmt->i_bmask = d3dfmt->bmask;
843 /* We create one picture.
844 * It is useless to create more as we can't be used for direct rendering */
846 /* Create a surface */
847 LPDIRECT3DSURFACE9 surface;
848 HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(d3ddev,
856 msg_Err(vd, "Failed to create picture surface. (hr=0x%lx)", hr);
859 /* fill surface with black color */
860 IDirect3DDevice9_ColorFill(d3ddev, surface, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0));
862 /* Create the associated picture */
863 picture_resource_t *rsc = &sys->resource;
864 rsc->p_sys = malloc(sizeof(*rsc->p_sys));
866 IDirect3DSurface9_Release(surface);
869 rsc->p_sys->surface = surface;
870 for (int i = 0; i < PICTURE_PLANE_MAX; i++) {
871 rsc->p[i].p_pixels = NULL;
872 rsc->p[i].i_pitch = 0;
873 rsc->p[i].i_lines = fmt->i_height / (i > 0 ? 2 : 1);
875 picture_t *picture = picture_NewFromResource(fmt, rsc);
877 IDirect3DSurface9_Release(surface);
882 /* Wrap it into a picture pool */
883 picture_pool_configuration_t pool_cfg;
884 memset(&pool_cfg, 0, sizeof(pool_cfg));
885 pool_cfg.picture_count = 1;
886 pool_cfg.picture = &picture;
887 pool_cfg.lock = Direct3DLockSurface;
888 pool_cfg.unlock = Direct3DUnlockSurface;
890 sys->pool = picture_pool_NewExtended(&pool_cfg);
892 picture_Release(picture);
893 IDirect3DSurface9_Release(surface);
899 * It destroys the pool of picture and its resources.
901 static void Direct3DDestroyPool(vout_display_t *vd)
903 vout_display_sys_t *sys = vd->sys;
906 picture_resource_t *rsc = &sys->resource;
907 IDirect3DSurface9_Release(rsc->p_sys->surface);
909 picture_pool_Delete(sys->pool);
917 FLOAT x,y,z; // vertex untransformed position
918 FLOAT rhw; // eye distance
919 D3DCOLOR diffuse; // diffuse color
920 FLOAT tu, tv; // texture relative coordinates
923 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
926 * It allocates and initializes the resources needed to render the scene.
928 static int Direct3DCreateScene(vout_display_t *vd)
930 vout_display_sys_t *sys = vd->sys;
931 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
935 * Create a texture for use when rendering a scene
936 * for performance reason, texture format is identical to backbuffer
937 * which would usually be a RGB format
939 LPDIRECT3DTEXTURE9 d3dtex;
940 hr = IDirect3DDevice9_CreateTexture(d3ddev,
941 sys->d3dpp.BackBufferWidth,
942 sys->d3dpp.BackBufferHeight,
944 D3DUSAGE_RENDERTARGET,
945 sys->d3dpp.BackBufferFormat,
950 msg_Err(vd, "Failed to create texture. (hr=0x%lx)", hr);
955 ** Create a vertex buffer for use when rendering scene
957 LPDIRECT3DVERTEXBUFFER9 d3dvtc;
958 hr = IDirect3DDevice9_CreateVertexBuffer(d3ddev,
959 sizeof(CUSTOMVERTEX)*4,
960 D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
966 msg_Err(vd, "Failed to create vertex buffer. (hr=0x%lx)", hr);
967 IDirect3DTexture9_Release(d3dtex);
972 sys->d3dtex = d3dtex;
973 sys->d3dvtc = d3dvtc;
975 // Texture coordinates outside the range [0.0, 1.0] are set
976 // to the texture color at 0.0 or 1.0, respectively.
977 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
978 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
980 // Set linear filtering quality
981 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
982 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
984 // set maximum ambient light
985 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));
988 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_CULLMODE, D3DCULL_NONE);
990 // Turn off the zbuffer
991 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ZENABLE, D3DZB_FALSE);
994 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_LIGHTING, FALSE);
997 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DITHERENABLE, TRUE);
1000 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_STENCILENABLE, FALSE);
1003 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, TRUE);
1004 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
1005 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
1006 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHATESTENABLE,TRUE);
1007 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAREF, 0x10);
1008 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER);
1010 // Set texture states
1011 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLOROP,D3DTOP_MODULATE);
1012 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE);
1013 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLORARG2,D3DTA_DIFFUSE);
1015 // turn off alpha operation
1016 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
1018 msg_Dbg(vd, "Direct3D scene created successfully");
1024 * It releases the scene resources.
1026 static void Direct3DDestroyScene(vout_display_t *vd)
1028 vout_display_sys_t *sys = vd->sys;
1030 LPDIRECT3DVERTEXBUFFER9 d3dvtc = sys->d3dvtc;
1032 IDirect3DVertexBuffer9_Release(d3dvtc);
1034 LPDIRECT3DTEXTURE9 d3dtex = sys->d3dtex;
1036 IDirect3DTexture9_Release(d3dtex);
1040 msg_Dbg(vd, "Direct3D scene released successfully");
1044 * It copies picture surface into a texture and renders into a scene.
1046 * This function is intented for higher end 3D cards, with pixel shader support
1047 * and at least 64 MB of video RAM.
1049 static void Direct3DRenderScene(vout_display_t *vd, LPDIRECT3DSURFACE9 surface)
1051 vout_display_sys_t *sys = vd->sys;
1052 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
1055 // check if device is still available
1056 hr = IDirect3DDevice9_TestCooperativeLevel(d3ddev);
1058 if (hr == D3DERR_DEVICENOTRESET && !sys->reset_device) {
1059 vout_display_SendEventPicturesInvalid(vd);
1060 sys->reset_device = true;
1065 LPDIRECT3DTEXTURE9 d3dtex = sys->d3dtex;
1066 LPDIRECT3DVERTEXBUFFER9 d3dvtc = sys->d3dvtc;
1068 /* Clear the backbuffer and the zbuffer */
1069 hr = IDirect3DDevice9_Clear(d3ddev, 0, NULL, D3DCLEAR_TARGET,
1070 D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
1072 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1075 /* retrieve picture surface */
1076 LPDIRECT3DSURFACE9 d3dsrc = surface;
1078 msg_Dbg(vd, "no surface to render ?");
1082 /* retrieve texture top-level surface */
1083 LPDIRECT3DSURFACE9 d3ddest;
1084 hr = IDirect3DTexture9_GetSurfaceLevel(d3dtex, 0, &d3ddest);
1086 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1090 /* Copy picture surface into texture surface
1091 * color space conversion and scaling happen here */
1092 RECT src = vd->sys->rect_src_clipped;
1093 RECT dst = vd->sys->rect_dest_clipped;
1095 hr = IDirect3DDevice9_StretchRect(d3ddev, d3dsrc, &src, d3ddest, &dst, D3DTEXF_LINEAR);
1096 IDirect3DSurface9_Release(d3ddest);
1098 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1102 /* Update the vertex buffer */
1103 CUSTOMVERTEX *vertices;
1104 hr = IDirect3DVertexBuffer9_Lock(d3dvtc, 0, 0, &vertices, D3DLOCK_DISCARD);
1106 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1110 /* Setup vertices */
1111 const float f_width = vd->sys->d3dpp.BackBufferWidth;
1112 const float f_height = vd->sys->d3dpp.BackBufferHeight;
1114 /* -0.5f is a "feature" of DirectX and it seems to apply to Direct3d also */
1115 /* http://www.sjbrown.co.uk/2003/05/01/fix-directx-rasterisation/ */
1116 vertices[0].x = -0.5f; // left
1117 vertices[0].y = -0.5f; // top
1118 vertices[0].z = 0.0f;
1119 vertices[0].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1120 vertices[0].rhw = 1.0f;
1121 vertices[0].tu = 0.0f;
1122 vertices[0].tv = 0.0f;
1124 vertices[1].x = f_width - 0.5f; // right
1125 vertices[1].y = -0.5f; // top
1126 vertices[1].z = 0.0f;
1127 vertices[1].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1128 vertices[1].rhw = 1.0f;
1129 vertices[1].tu = 1.0f;
1130 vertices[1].tv = 0.0f;
1132 vertices[2].x = f_width - 0.5f; // right
1133 vertices[2].y = f_height - 0.5f; // bottom
1134 vertices[2].z = 0.0f;
1135 vertices[2].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1136 vertices[2].rhw = 1.0f;
1137 vertices[2].tu = 1.0f;
1138 vertices[2].tv = 1.0f;
1140 vertices[3].x = -0.5f; // left
1141 vertices[3].y = f_height - 0.5f; // bottom
1142 vertices[3].z = 0.0f;
1143 vertices[3].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1144 vertices[3].rhw = 1.0f;
1145 vertices[3].tu = 0.0f;
1146 vertices[3].tv = 1.0f;
1148 hr= IDirect3DVertexBuffer9_Unlock(d3dvtc);
1150 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1155 hr = IDirect3DDevice9_BeginScene(d3ddev);
1157 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1161 // Setup our texture. Using textures introduces the texture stage states,
1162 // which govern how textures get blended together (in the case of multiple
1163 // textures) and lighting information. In this case, we are modulating
1164 // (blending) our texture with the diffuse color of the vertices.
1165 hr = IDirect3DDevice9_SetTexture(d3ddev, 0, (LPDIRECT3DBASETEXTURE9)d3dtex);
1167 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1168 IDirect3DDevice9_EndScene(d3ddev);
1172 // Render the vertex buffer contents
1173 hr = IDirect3DDevice9_SetStreamSource(d3ddev, 0, d3dvtc, 0, sizeof(CUSTOMVERTEX));
1175 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1176 IDirect3DDevice9_EndScene(d3ddev);
1180 // we use FVF instead of vertex shader
1181 hr = IDirect3DDevice9_SetFVF(d3ddev, D3DFVF_CUSTOMVERTEX);
1183 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1184 IDirect3DDevice9_EndScene(d3ddev);
1189 hr = IDirect3DDevice9_DrawPrimitive(d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
1191 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1192 IDirect3DDevice9_EndScene(d3ddev);
1197 hr = IDirect3DDevice9_EndScene(d3ddev);
1199 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1204 /*****************************************************************************
1205 * DesktopCallback: desktop mode variable callback
1206 *****************************************************************************/
1207 static int DesktopCallback(vlc_object_t *object, char const *psz_cmd,
1208 vlc_value_t oldval, vlc_value_t newval,
1211 vout_display_t *vd = (vout_display_t *)object;
1212 vout_display_sys_t *sys = vd->sys;
1213 VLC_UNUSED(psz_cmd);
1217 vlc_mutex_lock(&sys->lock);
1218 const bool ch_desktop = !sys->desktop_requested != !newval.b_bool;
1219 sys->ch_desktop |= ch_desktop;
1220 sys->desktop_requested = newval.b_bool;
1221 vlc_mutex_unlock(&sys->lock);
1223 /* FIXME we should have a way to export variable to be saved */
1225 playlist_t *p_playlist = pl_Hold(vd);
1227 /* Modify playlist as well because the vout might have to be
1229 var_Create(p_playlist, "direct3d-desktop", VLC_VAR_BOOL);
1230 var_SetBool(p_playlist, "direct3d-desktop", newval.b_bool);