1 /*****************************************************************************
2 * direct3d.c: Windows Direct3D video output module
3 *****************************************************************************
4 * Copyright (C) 2006-2014 VLC authors and VideoLAN
7 * Authors: Damien Fouilleul <damienf@videolan.org>,
8 * Sasha Koruga <skoruga@gmail.com>,
9 * Felix Abecassis <felix.abecassis@gmail.com>
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
29 * This plugin will use YUV surface if supported, using YUV will result in
30 * the best video quality (hardware filering when rescaling the picture)
31 * and the fastest display as it requires less processing.
33 * If YUV overlay is not supported this plugin will use RGB offscreen video
34 * surfaces that will be blitted onto the primary surface (display) to
35 * effectively display the pictures.
37 *****************************************************************************/
42 #include <vlc_common.h>
43 #include <vlc_plugin.h>
44 #include <vlc_vout_display.h>
45 #include <vlc_charset.h> /* ToT function */
51 #include "builtin_shaders.h"
53 /*****************************************************************************
55 *****************************************************************************/
56 static int Open(vlc_object_t *);
57 static void Close(vlc_object_t *);
59 #define DESKTOP_LONGTEXT N_(\
60 "The desktop mode allows you to display the video on the desktop.")
62 #define HW_BLENDING_TEXT N_("Use hardware blending support")
63 #define HW_BLENDING_LONGTEXT N_(\
64 "Try to use hardware acceleration for subtitle/OSD blending.")
66 #define PIXEL_SHADER_TEXT N_("Pixel Shader")
67 #define PIXEL_SHADER_LONGTEXT N_(\
68 "Choose a pixel shader to apply.")
69 #define PIXEL_SHADER_FILE_TEXT N_("Path to HLSL file")
70 #define PIXEL_SHADER_FILE_LONGTEXT N_("Path to an HLSL file containing a single pixel shader.")
71 /* The latest option in the selection list: used for loading a shader file. */
72 #define SELECTED_SHADER_FILE N_("HLSL File")
74 #define D3D_HELP N_("Recommended video output for Windows Vista and later versions")
76 static int FindShadersCallback(vlc_object_t *, const char *,
80 set_shortname("Direct3D")
81 set_description(N_("Direct3D video output"))
83 set_category(CAT_VIDEO)
84 set_subcategory(SUBCAT_VIDEO_VOUT)
86 add_bool("direct3d-hw-blending", true, HW_BLENDING_TEXT, HW_BLENDING_LONGTEXT, true)
88 add_string("direct3d-shader", "", PIXEL_SHADER_TEXT, PIXEL_SHADER_LONGTEXT, true)
89 change_string_cb(FindShadersCallback)
90 add_loadfile("direct3d-shader-file", NULL, PIXEL_SHADER_FILE_TEXT, PIXEL_SHADER_FILE_LONGTEXT, false)
92 set_capability("vout display", 240)
93 add_shortcut("direct3d")
94 set_callbacks(Open, Close)
98 /*****************************************************************************
100 *****************************************************************************/
101 static const vlc_fourcc_t d3d_subpicture_chromas[] = {
108 LPDIRECT3DSURFACE9 surface;
112 static int Open(vlc_object_t *);
114 static picture_pool_t *Pool (vout_display_t *, unsigned);
115 static void Prepare(vout_display_t *, picture_t *, subpicture_t *subpicture);
116 static void Display(vout_display_t *, picture_t *, subpicture_t *subpicture);
117 static int Control(vout_display_t *, int, va_list);
118 static void Manage (vout_display_t *);
120 static int Direct3DCreate (vout_display_t *);
121 static int Direct3DReset (vout_display_t *);
122 static void Direct3DDestroy(vout_display_t *);
124 static int Direct3DOpen (vout_display_t *, video_format_t *);
125 static void Direct3DClose(vout_display_t *);
130 FLOAT x,y,z; // vertex untransformed position
131 FLOAT rhw; // eye distance
132 D3DCOLOR diffuse; // diffuse color
133 FLOAT tu, tv; // texture relative coordinates
135 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
137 typedef struct d3d_region_t {
141 CUSTOMVERTEX vertex[4];
142 LPDIRECT3DTEXTURE9 texture;
145 static void Direct3DDeleteRegions(int, d3d_region_t *);
147 static int Direct3DImportPicture(vout_display_t *vd, d3d_region_t *, LPDIRECT3DSURFACE9 surface);
148 static void Direct3DImportSubpicture(vout_display_t *vd, int *, d3d_region_t **, subpicture_t *);
150 static void Direct3DRenderScene(vout_display_t *vd, d3d_region_t *, int, d3d_region_t *);
153 static int DesktopCallback(vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void *);
156 * It creates a Direct3D vout display.
158 static int Open(vlc_object_t *object)
160 vout_display_t *vd = (vout_display_t *)object;
161 vout_display_sys_t *sys;
163 OSVERSIONINFO winVer;
164 winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
165 if(GetVersionEx(&winVer) && winVer.dwMajorVersion < 6 && !object->b_force)
168 /* Allocate structure */
169 vd->sys = sys = calloc(1, sizeof(vout_display_sys_t));
173 if (Direct3DCreate(vd)) {
174 msg_Err(vd, "Direct3D could not be initialized");
180 sys->use_desktop = var_CreateGetBool(vd, "video-wallpaper");
181 sys->reset_device = false;
182 sys->reopen_device = false;
183 sys->lost_not_ready = false;
184 sys->allow_hw_yuv = var_CreateGetBool(vd, "directx-hw-yuv");
185 sys->desktop_save.is_fullscreen = vd->cfg->is_fullscreen;
186 sys->desktop_save.is_on_top = false;
187 sys->desktop_save.win.left = var_InheritInteger(vd, "video-x");
188 sys->desktop_save.win.right = vd->cfg->display.width;
189 sys->desktop_save.win.top = var_InheritInteger(vd, "video-y");
190 sys->desktop_save.win.bottom = vd->cfg->display.height;
197 if (Direct3DOpen(vd, &fmt)) {
198 msg_Err(vd, "Direct3D could not be opened");
203 vout_display_info_t info = vd->info;
205 info.has_double_click = true;
206 info.has_hide_mouse = false;
207 info.has_pictures_invalid = true;
208 info.has_event_thread = true;
209 if (var_InheritBool(vd, "direct3d-hw-blending") &&
210 sys->d3dregion_format != D3DFMT_UNKNOWN &&
211 (sys->d3dcaps.SrcBlendCaps & D3DPBLENDCAPS_SRCALPHA) &&
212 (sys->d3dcaps.DestBlendCaps & D3DPBLENDCAPS_INVSRCALPHA) &&
213 (sys->d3dcaps.TextureCaps & D3DPTEXTURECAPS_ALPHA) &&
214 (sys->d3dcaps.TextureOpCaps & D3DTEXOPCAPS_SELECTARG1) &&
215 (sys->d3dcaps.TextureOpCaps & D3DTEXOPCAPS_MODULATE))
216 info.subpicture_chromas = d3d_subpicture_chromas;
218 info.subpicture_chromas = NULL;
221 vlc_mutex_init(&sys->lock);
222 sys->ch_desktop = false;
223 sys->desktop_requested = sys->use_desktop;
226 val.psz_string = _("Desktop");
227 var_Change(vd, "video-wallpaper", VLC_VAR_SETTEXT, &val, NULL);
228 var_AddCallback(vd, "video-wallpaper", DesktopCallback, NULL);
230 /* Setup vout_display now that everything is fine */
231 video_format_Clean(&vd->fmt);
232 video_format_Copy(&vd->fmt, &fmt);
236 vd->prepare = Prepare;
237 vd->display = Display;
238 vd->control = Control;
241 /* Fix state in case of desktop mode */
242 if (sys->use_desktop && vd->cfg->is_fullscreen)
243 vout_display_SendEventFullscreen(vd, false);
255 * It destroyes a Direct3D vout display.
257 static void Close(vlc_object_t *object)
259 vout_display_t * vd = (vout_display_t *)object;
261 var_DelCallback(vd, "video-wallpaper", DesktopCallback, NULL);
262 vlc_mutex_destroy(&vd->sys->lock);
274 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
277 return vd->sys->pool;
280 static int Direct3DLockSurface(picture_t *);
281 static void Direct3DUnlockSurface(picture_t *);
283 static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
285 vout_display_sys_t *sys = vd->sys;
286 LPDIRECT3DSURFACE9 surface = picture->p_sys->surface;
288 picture_Release(picture);
289 VLC_UNUSED(subpicture);
291 /* FIXME it is a bit ugly, we need the surface to be unlocked for
293 * The clean way would be to release the picture (and ensure that
294 * the vout doesn't keep a reference). But because of the vout
295 * wrapper, we can't */
297 Direct3DUnlockSurface(picture);
298 VLC_UNUSED(subpicture);
301 /* check if device is still available */
302 HRESULT hr = IDirect3DDevice9_TestCooperativeLevel(sys->d3ddev);
304 if (hr == D3DERR_DEVICENOTRESET && !sys->reset_device) {
305 vout_display_SendEventPicturesInvalid(vd);
306 sys->reset_device = true;
307 sys->lost_not_ready = false;
309 if (hr == D3DERR_DEVICELOST && !sys->lost_not_ready) {
310 /* Device is lost but not yet ready for reset. */
311 sys->lost_not_ready = true;
316 d3d_region_t picture_region;
317 if (!Direct3DImportPicture(vd, &picture_region, surface)) {
318 picture_region.width = picture->format.i_visible_width;
319 picture_region.height = picture->format.i_visible_height;
320 int subpicture_region_count = 0;
321 d3d_region_t *subpicture_region = NULL;
323 Direct3DImportSubpicture(vd, &subpicture_region_count, &subpicture_region,
326 Direct3DRenderScene(vd, &picture_region,
327 subpicture_region_count, subpicture_region);
329 Direct3DDeleteRegions(sys->d3dregion_count, sys->d3dregion);
330 sys->d3dregion_count = subpicture_region_count;
331 sys->d3dregion = subpicture_region;
335 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
337 vout_display_sys_t *sys = vd->sys;
338 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
340 if (sys->lost_not_ready) {
341 picture_Release(picture);
343 subpicture_Delete(subpicture);
347 // Present the back buffer contents to the display
348 // No stretching should happen here !
349 const RECT src = sys->rect_dest_clipped;
350 const RECT dst = sys->rect_dest_clipped;
351 HRESULT hr = IDirect3DDevice9_Present(d3ddev, &src, &dst, NULL, NULL);
353 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
358 VLC_UNUSED(subpicture);
360 /* XXX See Prepare() */
361 Direct3DLockSurface(picture);
362 picture_Release(picture);
365 subpicture_Delete(subpicture);
369 static int ControlResetDevice(vout_display_t *vd)
371 return Direct3DReset(vd);
373 static int ControlReopenDevice(vout_display_t *vd)
375 vout_display_sys_t *sys = vd->sys;
377 if (!sys->use_desktop) {
378 /* Save non-desktop state */
379 sys->desktop_save.is_fullscreen = vd->cfg->is_fullscreen;
380 sys->desktop_save.is_on_top = sys->is_on_top;
382 WINDOWPLACEMENT wp = { .length = sizeof(wp), };
383 GetWindowPlacement(sys->hparent ? sys->hparent : sys->hwnd, &wp);
384 sys->desktop_save.win = wp.rcNormalPosition;
389 EventThreadStop(sys->event);
392 vlc_mutex_lock(&sys->lock);
393 sys->use_desktop = sys->desktop_requested;
394 sys->ch_desktop = false;
395 vlc_mutex_unlock(&sys->lock);
399 memset(&cfg, 0, sizeof(cfg));
400 cfg.use_desktop = sys->use_desktop;
401 if (!sys->use_desktop) {
402 cfg.win.type = VOUT_WINDOW_TYPE_HWND;
403 cfg.win.x = sys->desktop_save.win.left;
404 cfg.win.y = sys->desktop_save.win.top;
405 cfg.win.width = sys->desktop_save.win.right - sys->desktop_save.win.left;
406 cfg.win.height = sys->desktop_save.win.bottom - sys->desktop_save.win.top;
410 if (EventThreadStart(sys->event, &hwnd, &cfg)) {
411 msg_Err(vd, "Failed to restart event thread");
414 sys->parent_window = hwnd.parent_window;
415 sys->hparent = hwnd.hparent;
416 sys->hwnd = hwnd.hwnd;
417 sys->hvideownd = hwnd.hvideownd;
418 sys->hfswnd = hwnd.hfswnd;
419 SetRectEmpty(&sys->rect_parent);
423 if (Direct3DOpen(vd, &fmt)) {
425 msg_Err(vd, "Failed to reopen device");
429 sys->is_first_display = true;
431 if (sys->use_desktop) {
432 /* Disable fullscreen/on_top while using desktop */
433 if (sys->desktop_save.is_fullscreen)
434 vout_display_SendEventFullscreen(vd, false);
435 if (sys->desktop_save.is_on_top)
436 vout_display_SendWindowState(vd, VOUT_WINDOW_STATE_NORMAL);
438 /* Restore fullscreen/on_top */
439 if (sys->desktop_save.is_fullscreen)
440 vout_display_SendEventFullscreen(vd, true);
441 if (sys->desktop_save.is_on_top)
442 vout_display_SendWindowState(vd, VOUT_WINDOW_STATE_ABOVE);
446 static int Control(vout_display_t *vd, int query, va_list args)
448 vout_display_sys_t *sys = vd->sys;
451 case VOUT_DISPLAY_RESET_PICTURES:
452 /* FIXME what to do here in case of failure */
453 if (sys->reset_device) {
454 if (ControlResetDevice(vd)) {
455 msg_Err(vd, "Failed to reset device");
458 sys->reset_device = false;
459 } else if(sys->reopen_device) {
460 if (ControlReopenDevice(vd)) {
461 msg_Err(vd, "Failed to reopen device");
464 sys->reopen_device = false;
468 return CommonControl(vd, query, args);
471 static void Manage (vout_display_t *vd)
473 vout_display_sys_t *sys = vd->sys;
477 /* Desktop mode change */
478 vlc_mutex_lock(&sys->lock);
479 const bool ch_desktop = sys->ch_desktop;
480 sys->ch_desktop = false;
481 vlc_mutex_unlock(&sys->lock);
484 sys->reopen_device = true;
485 vout_display_SendEventPicturesInvalid(vd);
488 /* Position Change */
489 if (sys->changes & DX_POSITION_CHANGE) {
490 #if 0 /* need that when bicubic filter is available */
494 GetClientRect(p_sys->hvideownd, &rect);
495 width = rect.right-rect.left;
496 height = rect.bottom-rect.top;
498 if (width != p_sys->d3dpp.BackBufferWidth || height != p_sys->d3dpp.BackBufferHeight)
500 msg_Dbg(vd, "resizing device back buffers to (%lux%lu)", width, height);
501 // need to reset D3D device to resize back buffer
502 if (VLC_SUCCESS != Direct3DResetDevice(vd, width, height))
506 sys->clear_scene = true;
507 sys->changes &= ~DX_POSITION_CHANGE;
511 static HINSTANCE Direct3DLoadShaderLibrary(void)
513 HINSTANCE instance = NULL;
514 for (int i = 43; i > 23; --i) {
516 _sntprintf(filename, 16, TEXT("D3dx9_%d.dll"), i);
517 instance = LoadLibrary(filename);
525 * It initializes an instance of Direct3D9
527 static int Direct3DCreate(vout_display_t *vd)
529 vout_display_sys_t *sys = vd->sys;
531 sys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
532 if (!sys->hd3d9_dll) {
533 msg_Warn(vd, "cannot load d3d9.dll, aborting");
537 LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion);
539 (void *)GetProcAddress(sys->hd3d9_dll, "Direct3DCreate9");
540 if (!OurDirect3DCreate9) {
541 msg_Err(vd, "Cannot locate reference to Direct3DCreate9 ABI in DLL");
545 /* Create the D3D object. */
546 LPDIRECT3D9 d3dobj = OurDirect3DCreate9(D3D_SDK_VERSION);
548 msg_Err(vd, "Could not create Direct3D9 instance.");
551 sys->d3dobj = d3dobj;
553 sys->hd3d9x_dll = Direct3DLoadShaderLibrary();
554 if (!sys->hd3d9x_dll)
555 msg_Warn(vd, "cannot load Direct3D Shader Library; HLSL pixel shading will be disabled.");
558 ** Get device capabilities
560 ZeroMemory(&sys->d3dcaps, sizeof(sys->d3dcaps));
561 HRESULT hr = IDirect3D9_GetDeviceCaps(d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &sys->d3dcaps);
563 msg_Err(vd, "Could not read adapter capabilities. (hr=0x%lX)", hr);
567 /* TODO: need to test device capabilities and select the right render function */
568 if (!(sys->d3dcaps.DevCaps2 & D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES) ||
569 !(sys->d3dcaps.TextureFilterCaps & (D3DPTFILTERCAPS_MAGFLINEAR)) ||
570 !(sys->d3dcaps.TextureFilterCaps & (D3DPTFILTERCAPS_MINFLINEAR))) {
571 msg_Err(vd, "Device does not support stretching from textures.");
579 * It releases an instance of Direct3D9
581 static void Direct3DDestroy(vout_display_t *vd)
583 vout_display_sys_t *sys = vd->sys;
586 IDirect3D9_Release(sys->d3dobj);
588 FreeLibrary(sys->hd3d9_dll);
590 FreeLibrary(sys->hd3d9x_dll);
593 sys->hd3d9_dll = NULL;
594 sys->hd3d9x_dll = NULL;
599 * It setup vout_display_sys_t::d3dpp and vout_display_sys_t::rect_display
600 * from the default adapter.
602 static int Direct3DFillPresentationParameters(vout_display_t *vd)
604 vout_display_sys_t *sys = vd->sys;
607 ** Get the current desktop display mode, so we can set up a back
608 ** buffer of the same format
610 D3DDISPLAYMODE d3ddm;
611 HRESULT hr = IDirect3D9_GetAdapterDisplayMode(sys->d3dobj,
612 D3DADAPTER_DEFAULT, &d3ddm);
614 msg_Err(vd, "Could not read adapter display mode. (hr=0x%lX)", hr);
618 /* Set up the structure used to create the D3DDevice. */
619 D3DPRESENT_PARAMETERS *d3dpp = &vd->sys->d3dpp;
620 ZeroMemory(d3dpp, sizeof(D3DPRESENT_PARAMETERS));
621 d3dpp->Flags = D3DPRESENTFLAG_VIDEO;
622 d3dpp->Windowed = TRUE;
623 d3dpp->hDeviceWindow = vd->sys->hvideownd;
624 d3dpp->BackBufferWidth = __MAX((unsigned int)GetSystemMetrics(SM_CXVIRTUALSCREEN),
626 d3dpp->BackBufferHeight = __MAX((unsigned int)GetSystemMetrics(SM_CYVIRTUALSCREEN),
628 d3dpp->SwapEffect = D3DSWAPEFFECT_COPY;
629 d3dpp->MultiSampleType = D3DMULTISAMPLE_NONE;
630 d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
631 d3dpp->BackBufferFormat = d3ddm.Format;
632 d3dpp->BackBufferCount = 1;
633 d3dpp->EnableAutoDepthStencil = FALSE;
636 RECT *display = &vd->sys->rect_display;
639 display->right = d3dpp->BackBufferWidth;
640 display->bottom = d3dpp->BackBufferHeight;
646 static int Direct3DCreateResources (vout_display_t *, video_format_t *);
647 static void Direct3DDestroyResources(vout_display_t *);
650 * It creates a Direct3D device and the associated resources.
652 static int Direct3DOpen(vout_display_t *vd, video_format_t *fmt)
654 vout_display_sys_t *sys = vd->sys;
655 LPDIRECT3D9 d3dobj = sys->d3dobj;
657 if (Direct3DFillPresentationParameters(vd))
660 // Create the D3DDevice
661 LPDIRECT3DDEVICE9 d3ddev;
663 UINT AdapterToUse = D3DADAPTER_DEFAULT;
664 D3DDEVTYPE DeviceType = D3DDEVTYPE_HAL;
667 // Look for 'NVIDIA PerfHUD' adapter
668 // If it is present, override default settings
669 for (UINT Adapter=0; Adapter< IDirect3D9_GetAdapterCount(d3dobj); ++Adapter) {
670 D3DADAPTER_IDENTIFIER9 Identifier;
671 HRESULT Res = IDirect3D9_GetAdapterIdentifier(d3dobj,Adapter,0,&Identifier);
672 if (SUCCEEDED(Res) && strstr(Identifier.Description,"PerfHUD") != 0) {
673 AdapterToUse = Adapter;
674 DeviceType = D3DDEVTYPE_REF;
681 D3DADAPTER_IDENTIFIER9 d3dai;
682 if (FAILED(IDirect3D9_GetAdapterIdentifier(d3dobj,AdapterToUse,0, &d3dai))) {
683 msg_Warn(vd, "IDirect3D9_GetAdapterIdentifier failed");
685 msg_Dbg(vd, "Direct3d Device: %s %lu %lu %lu", d3dai.Description,
686 d3dai.VendorId, d3dai.DeviceId, d3dai.Revision );
689 HRESULT hr = IDirect3D9_CreateDevice(d3dobj, AdapterToUse,
690 DeviceType, sys->hvideownd,
691 D3DCREATE_SOFTWARE_VERTEXPROCESSING|
692 D3DCREATE_MULTITHREADED,
693 &sys->d3dpp, &d3ddev);
695 msg_Err(vd, "Could not create the D3D device! (hr=0x%lX)", hr);
698 sys->d3ddev = d3ddev;
700 UpdateRects(vd, NULL, NULL, true);
702 if (Direct3DCreateResources(vd, fmt)) {
703 msg_Err(vd, "Failed to allocate resources");
707 /* Change the window title bar text */
708 EventThreadUpdateTitle(sys->event, VOUT_TITLE " (Direct3D output)");
710 msg_Dbg(vd, "Direct3D device adapter successfully initialized");
715 * It releases the Direct3D9 device and its resources.
717 static void Direct3DClose(vout_display_t *vd)
719 vout_display_sys_t *sys = vd->sys;
721 Direct3DDestroyResources(vd);
724 IDirect3DDevice9_Release(sys->d3ddev);
730 * It reset the Direct3D9 device and its resources.
732 static int Direct3DReset(vout_display_t *vd)
734 vout_display_sys_t *sys = vd->sys;
735 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
737 if (Direct3DFillPresentationParameters(vd))
740 /* release all D3D objects */
741 Direct3DDestroyResources(vd);
744 HRESULT hr = IDirect3DDevice9_Reset(d3ddev, &sys->d3dpp);
746 msg_Err(vd, "%s failed ! (hr=%08lX)", __FUNCTION__, hr);
750 UpdateRects(vd, NULL, NULL, true);
753 if (Direct3DCreateResources(vd, &vd->fmt)) {
754 msg_Dbg(vd, "%s failed !", __FUNCTION__);
761 static int Direct3DCreatePool(vout_display_t *vd, video_format_t *fmt);
762 static void Direct3DDestroyPool(vout_display_t *vd);
764 static int Direct3DCreateScene(vout_display_t *vd, const video_format_t *fmt);
765 static void Direct3DDestroyScene(vout_display_t *vd);
767 static int Direct3DCreateShaders(vout_display_t *vd);
768 static void Direct3DDestroyShaders(vout_display_t *vd);
771 * It creates the picture and scene resources.
773 static int Direct3DCreateResources(vout_display_t *vd, video_format_t *fmt)
775 vout_display_sys_t *sys = vd->sys;
777 if (Direct3DCreatePool(vd, fmt)) {
778 msg_Err(vd, "Direct3D picture pool initialization failed");
781 if (Direct3DCreateScene(vd, fmt)) {
782 msg_Err(vd, "Direct3D scene initialization failed !");
785 if (Direct3DCreateShaders(vd)) {
786 /* Failing to initialize shaders is not fatal. */
787 msg_Warn(vd, "Direct3D shaders initialization failed !");
790 sys->d3dregion_format = D3DFMT_UNKNOWN;
791 for (int i = 0; i < 2; i++) {
792 D3DFORMAT fmt = i == 0 ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8;
793 if (SUCCEEDED(IDirect3D9_CheckDeviceFormat(sys->d3dobj,
796 sys->d3dpp.BackBufferFormat,
800 sys->d3dregion_format = fmt;
807 * It destroys the picture and scene resources.
809 static void Direct3DDestroyResources(vout_display_t *vd)
811 Direct3DDestroyScene(vd);
812 Direct3DDestroyPool(vd);
813 Direct3DDestroyShaders(vd);
817 * It tests if the conversion from src to dst is supported.
819 static int Direct3DCheckConversion(vout_display_t *vd,
820 D3DFORMAT src, D3DFORMAT dst)
822 vout_display_sys_t *sys = vd->sys;
823 LPDIRECT3D9 d3dobj = sys->d3dobj;
826 /* test whether device can create a surface of that format */
827 hr = IDirect3D9_CheckDeviceFormat(d3dobj, D3DADAPTER_DEFAULT,
828 D3DDEVTYPE_HAL, dst, 0,
829 D3DRTYPE_SURFACE, src);
831 /* test whether device can perform color-conversion
832 ** from that format to target format
834 hr = IDirect3D9_CheckDeviceFormatConversion(d3dobj,
839 if (!SUCCEEDED(hr)) {
840 if (D3DERR_NOTAVAILABLE != hr)
841 msg_Err(vd, "Could not query adapter supported formats. (hr=0x%lX)", hr);
850 D3DFORMAT format; /* D3D format */
851 vlc_fourcc_t fourcc; /* VLC fourcc */
857 static const d3d_format_t d3d_formats[] = {
858 /* YV12 is always used for planar 420, the planes are then swapped in Lock() */
859 { "YV12", MAKEFOURCC('Y','V','1','2'), VLC_CODEC_YV12, 0,0,0 },
860 { "YV12", MAKEFOURCC('Y','V','1','2'), VLC_CODEC_I420, 0,0,0 },
861 { "YV12", MAKEFOURCC('Y','V','1','2'), VLC_CODEC_J420, 0,0,0 },
862 { "UYVY", D3DFMT_UYVY, VLC_CODEC_UYVY, 0,0,0 },
863 { "YUY2", D3DFMT_YUY2, VLC_CODEC_YUYV, 0,0,0 },
864 { "X8R8G8B8", D3DFMT_X8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
865 { "A8R8G8B8", D3DFMT_A8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
866 { "8G8B8", D3DFMT_R8G8B8, VLC_CODEC_RGB24, 0xff0000, 0x00ff00, 0x0000ff },
867 { "R5G6B5", D3DFMT_R5G6B5, VLC_CODEC_RGB16, 0x1f<<11, 0x3f<<5, 0x1f<<0 },
868 { "X1R5G5B5", D3DFMT_X1R5G5B5,VLC_CODEC_RGB15, 0x1f<<10, 0x1f<<5, 0x1f<<0 },
874 * It returns the format (closest to chroma) that can be converted to target */
875 static const d3d_format_t *Direct3DFindFormat(vout_display_t *vd, vlc_fourcc_t chroma, D3DFORMAT target)
877 vout_display_sys_t *sys = vd->sys;
879 for (unsigned pass = 0; pass < 2; pass++) {
880 const vlc_fourcc_t *list;
882 if (pass == 0 && sys->allow_hw_yuv && vlc_fourcc_IsYUV(chroma))
883 list = vlc_fourcc_GetYUVFallback(chroma);
885 list = vlc_fourcc_GetRGBFallback(chroma);
889 for (unsigned i = 0; list[i] != 0; i++) {
890 for (unsigned j = 0; d3d_formats[j].name; j++) {
891 const d3d_format_t *format = &d3d_formats[j];
893 if (format->fourcc != list[i])
896 msg_Warn(vd, "trying surface pixel format: %s",
898 if (!Direct3DCheckConversion(vd, format->format, target)) {
899 msg_Dbg(vd, "selected surface pixel format is %s",
910 * It locks the surface associated to the picture and get the surface
911 * descriptor which amongst other things has the pointer to the picture
912 * data and its pitch.
914 static int Direct3DLockSurface(picture_t *picture)
916 /* Lock the surface to get a valid pointer to the picture buffer */
917 D3DLOCKED_RECT d3drect;
918 HRESULT hr = IDirect3DSurface9_LockRect(picture->p_sys->surface, &d3drect, NULL, 0);
920 //msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
921 return CommonUpdatePicture(picture, &picture->p_sys->fallback, NULL, 0);
924 CommonUpdatePicture(picture, NULL, d3drect.pBits, d3drect.Pitch);
928 * It unlocks the surface associated to the picture.
930 static void Direct3DUnlockSurface(picture_t *picture)
932 /* Unlock the Surface */
933 HRESULT hr = IDirect3DSurface9_UnlockRect(picture->p_sys->surface);
935 //msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
940 * It creates the pool of picture (only 1).
942 * Each picture has an associated offscreen surface in video memory
943 * depending on hardware capabilities the picture chroma will be as close
944 * as possible to the orginal render chroma to reduce CPU conversion overhead
945 * and delegate this work to video card GPU
947 static int Direct3DCreatePool(vout_display_t *vd, video_format_t *fmt)
949 vout_display_sys_t *sys = vd->sys;
950 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
955 /* Find the appropriate D3DFORMAT for the render chroma, the format will be the closest to
956 * the requested chroma which is usable by the hardware in an offscreen surface, as they
957 * typically support more formats than textures */
958 const d3d_format_t *d3dfmt = Direct3DFindFormat(vd, fmt->i_chroma, sys->d3dpp.BackBufferFormat);
960 msg_Err(vd, "surface pixel format is not supported.");
963 fmt->i_chroma = d3dfmt->fourcc;
964 fmt->i_rmask = d3dfmt->rmask;
965 fmt->i_gmask = d3dfmt->gmask;
966 fmt->i_bmask = d3dfmt->bmask;
968 /* We create one picture.
969 * It is useless to create more as we can't be used for direct rendering */
971 /* Create a surface */
972 LPDIRECT3DSURFACE9 surface;
973 HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(d3ddev,
974 fmt->i_visible_width,
975 fmt->i_visible_height,
981 msg_Err(vd, "Failed to create picture surface. (hr=0x%lx)", hr);
984 /* fill surface with black color */
985 IDirect3DDevice9_ColorFill(d3ddev, surface, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0));
987 /* Create the associated picture */
988 picture_sys_t *picsys = malloc(sizeof(*picsys));
989 if (unlikely(picsys == NULL)) {
990 IDirect3DSurface9_Release(surface);
993 picsys->surface = surface;
994 picsys->fallback = NULL;
996 picture_resource_t resource = { .p_sys = picsys };
997 for (int i = 0; i < PICTURE_PLANE_MAX; i++)
998 resource.p[i].i_lines = fmt->i_visible_height / (i > 0 ? 2 : 1);
1000 picture_t *picture = picture_NewFromResource(fmt, &resource);
1002 IDirect3DSurface9_Release(surface);
1006 sys->picsys = picsys;
1008 /* Wrap it into a picture pool */
1009 picture_pool_configuration_t pool_cfg;
1010 memset(&pool_cfg, 0, sizeof(pool_cfg));
1011 pool_cfg.picture_count = 1;
1012 pool_cfg.picture = &picture;
1013 pool_cfg.lock = Direct3DLockSurface;
1014 pool_cfg.unlock = Direct3DUnlockSurface;
1016 sys->pool = picture_pool_NewExtended(&pool_cfg);
1018 picture_Release(picture);
1019 IDirect3DSurface9_Release(surface);
1025 * It destroys the pool of picture and its resources.
1027 static void Direct3DDestroyPool(vout_display_t *vd)
1029 vout_display_sys_t *sys = vd->sys;
1032 picture_sys_t *picsys = sys->picsys;
1033 IDirect3DSurface9_Release(picsys->surface);
1034 if (picsys->fallback)
1035 picture_Release(picsys->fallback);
1036 picture_pool_Delete(sys->pool);
1042 * It allocates and initializes the resources needed to render the scene.
1044 static int Direct3DCreateScene(vout_display_t *vd, const video_format_t *fmt)
1046 vout_display_sys_t *sys = vd->sys;
1047 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
1051 * Create a texture for use when rendering a scene
1052 * for performance reason, texture format is identical to backbuffer
1053 * which would usually be a RGB format
1055 LPDIRECT3DTEXTURE9 d3dtex;
1056 hr = IDirect3DDevice9_CreateTexture(d3ddev,
1057 fmt->i_visible_width,
1058 fmt->i_visible_height,
1060 D3DUSAGE_RENDERTARGET,
1061 sys->d3dpp.BackBufferFormat,
1066 msg_Err(vd, "Failed to create texture. (hr=0x%lx)", hr);
1067 return VLC_EGENERIC;
1071 ** Create a vertex buffer for use when rendering scene
1073 LPDIRECT3DVERTEXBUFFER9 d3dvtc;
1074 hr = IDirect3DDevice9_CreateVertexBuffer(d3ddev,
1075 sizeof(CUSTOMVERTEX)*4,
1076 D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
1077 D3DFVF_CUSTOMVERTEX,
1082 msg_Err(vd, "Failed to create vertex buffer. (hr=0x%lx)", hr);
1083 IDirect3DTexture9_Release(d3dtex);
1084 return VLC_EGENERIC;
1088 sys->d3dtex = d3dtex;
1089 sys->d3dvtc = d3dvtc;
1091 sys->d3dregion_count = 0;
1092 sys->d3dregion = NULL;
1094 sys->clear_scene = true;
1096 // Texture coordinates outside the range [0.0, 1.0] are set
1097 // to the texture color at 0.0 or 1.0, respectively.
1098 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
1099 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
1101 // Set linear filtering quality
1102 if (sys->d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) {
1103 msg_Dbg(vd, "Using D3DTEXF_LINEAR for minification");
1104 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
1106 msg_Dbg(vd, "Using D3DTEXF_POINT for minification");
1107 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
1109 if (sys->d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR) {
1110 msg_Dbg(vd, "Using D3DTEXF_LINEAR for magnification");
1111 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
1113 msg_Dbg(vd, "Using D3DTEXF_POINT for magnification");
1114 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
1117 // set maximum ambient light
1118 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));
1121 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_CULLMODE, D3DCULL_NONE);
1123 // Turn off the zbuffer
1124 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ZENABLE, D3DZB_FALSE);
1127 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_LIGHTING, FALSE);
1130 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DITHERENABLE, TRUE);
1133 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_STENCILENABLE, FALSE);
1136 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, FALSE);
1137 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
1138 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
1140 if (sys->d3dcaps.AlphaCmpCaps & D3DPCMPCAPS_GREATER) {
1141 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHATESTENABLE,TRUE);
1142 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAREF, 0x00);
1143 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER);
1146 // Set texture states
1147 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLOROP,D3DTOP_SELECTARG1);
1148 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE);
1150 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
1151 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
1152 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);
1154 msg_Dbg(vd, "Direct3D scene created successfully");
1160 * It releases the scene resources.
1162 static void Direct3DDestroyScene(vout_display_t *vd)
1164 vout_display_sys_t *sys = vd->sys;
1166 Direct3DDeleteRegions(sys->d3dregion_count, sys->d3dregion);
1168 LPDIRECT3DVERTEXBUFFER9 d3dvtc = sys->d3dvtc;
1170 IDirect3DVertexBuffer9_Release(d3dvtc);
1172 LPDIRECT3DTEXTURE9 d3dtex = sys->d3dtex;
1174 IDirect3DTexture9_Release(d3dtex);
1179 sys->d3dregion_count = 0;
1180 sys->d3dregion = NULL;
1182 msg_Dbg(vd, "Direct3D scene released successfully");
1185 static int Direct3DCompileShader(vout_display_t *vd, const char *shader_source, size_t source_length)
1187 vout_display_sys_t *sys = vd->sys;
1189 HRESULT (WINAPI * OurD3DXCompileShader)(
1192 const D3DXMACRO *pDefines,
1193 LPD3DXINCLUDE pInclude,
1194 LPCSTR pFunctionName,
1197 LPD3DXBUFFER *ppShader,
1198 LPD3DXBUFFER *ppErrorMsgs,
1199 LPD3DXCONSTANTTABLE *ppConstantTable);
1201 OurD3DXCompileShader = (void*)GetProcAddress(sys->hd3d9x_dll, "D3DXCompileShader");
1202 if (!OurD3DXCompileShader) {
1203 msg_Warn(vd, "Cannot locate reference to D3DXCompileShader; pixel shading will be disabled");
1204 return VLC_EGENERIC;
1207 LPD3DXBUFFER error_msgs = NULL;
1208 LPD3DXBUFFER compiled_shader = NULL;
1210 DWORD shader_flags = 0;
1211 HRESULT hr = OurD3DXCompileShader(shader_source, source_length, NULL, NULL,
1212 "main", "ps_3_0", shader_flags, &compiled_shader, &error_msgs, NULL);
1215 msg_Warn(vd, "D3DXCompileShader Error (hr=0x%lX)", hr);
1217 msg_Warn(vd, "HLSL Compilation Error: %s", (char*)ID3DXBuffer_GetBufferPointer(error_msgs));
1218 return VLC_EGENERIC;
1221 hr = IDirect3DDevice9_CreatePixelShader(sys->d3ddev,
1222 ID3DXBuffer_GetBufferPointer(compiled_shader),
1226 msg_Warn(vd, "IDirect3DDevice9_CreatePixelShader error (hr=0x%lX)", hr);
1227 return VLC_EGENERIC;
1232 #define MAX_SHADER_FILE_SIZE 1024*1024
1234 static int Direct3DCreateShaders(vout_display_t *vd)
1236 vout_display_sys_t *sys = vd->sys;
1238 if (!sys->hd3d9x_dll)
1239 return VLC_EGENERIC;
1241 /* Find which shader was selected in the list. */
1242 char *selected_shader = var_InheritString(vd, "direct3d-shader");
1243 if (!selected_shader)
1244 return VLC_SUCCESS; /* Nothing to do */
1246 const char *shader_source_builtin = NULL;
1247 char *shader_source_file = NULL;
1250 for (size_t i = 0; i < BUILTIN_SHADERS_COUNT; ++i) {
1251 if (!strcmp(selected_shader, builtin_shaders[i].name)) {
1252 shader_source_builtin = builtin_shaders[i].code;
1257 if (shader_source_builtin) {
1258 /* A builtin shader was selected. */
1259 int err = Direct3DCompileShader(vd, shader_source_builtin, strlen(shader_source_builtin));
1263 if (strcmp(selected_shader, SELECTED_SHADER_FILE))
1264 goto error; /* Unrecognized entry in the list. */
1265 /* The source code of the shader needs to be read from a file. */
1266 char *filepath = var_InheritString(vd, "direct3d-shader-file");
1267 if (!filepath || !*filepath)
1272 /* Open file, find its size with fseek/ftell and read its content in a buffer. */
1273 fs = fopen(filepath, "rb");
1276 int ret = fseek(fs, 0, SEEK_END);
1279 long length = ftell(fs);
1280 if (length == -1 || length >= MAX_SHADER_FILE_SIZE)
1283 shader_source_file = malloc(sizeof(*shader_source_file) * length);
1284 if (!shader_source_file)
1286 ret = fread(shader_source_file, length, 1, fs);
1289 ret = Direct3DCompileShader(vd, shader_source_file, length);
1294 free(selected_shader);
1295 free(shader_source_file);
1301 Direct3DDestroyShaders(vd);
1302 free(selected_shader);
1303 free(shader_source_file);
1306 return VLC_EGENERIC;
1309 static void Direct3DDestroyShaders(vout_display_t *vd)
1311 vout_display_sys_t *sys = vd->sys;
1313 if (sys->d3dx_shader)
1314 IDirect3DPixelShader9_Release(sys->d3dx_shader);
1315 sys->d3dx_shader = NULL;
1319 * Compute the vertex ordering needed to rotate the video. Without
1320 * rotation, the vertices of the rectangle are defined in a clockwise
1321 * order. This function computes a remapping of the coordinates to
1322 * implement the rotation, given fixed texture coordinates.
1323 * The unrotated order is the following:
1327 * For a 180 degrees rotation it should like this:
1331 * Vertex 0 should be assigned coordinates at index 2 from the
1332 * unrotated order and so on, thus yielding order: 2 3 0 1.
1334 static void orientationVertexOrder(video_orientation_t orientation, int vertex_order[static 4])
1336 switch (orientation) {
1337 case ORIENT_ROTATED_90:
1338 vertex_order[0] = 1;
1339 vertex_order[1] = 2;
1340 vertex_order[2] = 3;
1341 vertex_order[3] = 0;
1343 case ORIENT_ROTATED_270:
1344 vertex_order[0] = 3;
1345 vertex_order[1] = 0;
1346 vertex_order[2] = 1;
1347 vertex_order[3] = 2;
1349 case ORIENT_ROTATED_180:
1350 vertex_order[0] = 2;
1351 vertex_order[1] = 3;
1352 vertex_order[2] = 0;
1353 vertex_order[3] = 1;
1355 case ORIENT_TRANSPOSED:
1356 vertex_order[0] = 0;
1357 vertex_order[1] = 3;
1358 vertex_order[2] = 2;
1359 vertex_order[3] = 1;
1361 case ORIENT_HFLIPPED:
1362 vertex_order[0] = 3;
1363 vertex_order[1] = 2;
1364 vertex_order[2] = 1;
1365 vertex_order[3] = 0;
1367 case ORIENT_VFLIPPED:
1368 vertex_order[0] = 1;
1369 vertex_order[1] = 0;
1370 vertex_order[2] = 3;
1371 vertex_order[3] = 2;
1373 case ORIENT_ANTI_TRANSPOSED: /* transpose + vflip */
1374 vertex_order[0] = 1;
1375 vertex_order[1] = 2;
1376 vertex_order[2] = 3;
1377 vertex_order[3] = 0;
1380 vertex_order[0] = 0;
1381 vertex_order[1] = 1;
1382 vertex_order[2] = 2;
1383 vertex_order[3] = 3;
1388 static void Direct3DSetupVertices(CUSTOMVERTEX *vertices,
1389 const RECT src_full,
1390 const RECT src_crop,
1393 video_orientation_t orientation)
1395 const float src_full_width = src_full.right - src_full.left;
1396 const float src_full_height = src_full.bottom - src_full.top;
1398 /* Vertices of the dst rectangle in the unrotated (clockwise) order. */
1399 const int vertices_coords[4][2] = {
1400 { dst.left, dst.top },
1401 { dst.right, dst.top },
1402 { dst.right, dst.bottom },
1403 { dst.left, dst.bottom },
1406 /* Compute index remapping necessary to implement the rotation. */
1407 int vertex_order[4];
1408 orientationVertexOrder(orientation, vertex_order);
1410 for (int i = 0; i < 4; ++i) {
1411 vertices[i].x = vertices_coords[vertex_order[i]][0];
1412 vertices[i].y = vertices_coords[vertex_order[i]][1];
1415 vertices[0].tu = src_crop.left / src_full_width;
1416 vertices[0].tv = src_crop.top / src_full_height;
1418 vertices[1].tu = src_crop.right / src_full_width;
1419 vertices[1].tv = src_crop.top / src_full_height;
1421 vertices[2].tu = src_crop.right / src_full_width;
1422 vertices[2].tv = src_crop.bottom / src_full_height;
1424 vertices[3].tu = src_crop.left / src_full_width;
1425 vertices[3].tv = src_crop.bottom / src_full_height;
1427 for (int i = 0; i < 4; i++) {
1428 /* -0.5f is a "feature" of DirectX and it seems to apply to Direct3d also */
1429 /* http://www.sjbrown.co.uk/2003/05/01/fix-directx-rasterisation/ */
1430 vertices[i].x -= 0.5;
1431 vertices[i].y -= 0.5;
1433 vertices[i].z = 0.0f;
1434 vertices[i].rhw = 1.0f;
1435 vertices[i].diffuse = D3DCOLOR_ARGB(alpha, 255, 255, 255);
1440 * It copies picture surface into a texture and setup the associated d3d_region_t.
1442 static int Direct3DImportPicture(vout_display_t *vd,
1443 d3d_region_t *region,
1444 LPDIRECT3DSURFACE9 source)
1446 vout_display_sys_t *sys = vd->sys;
1450 msg_Dbg(vd, "no surface to render ?");
1451 return VLC_EGENERIC;
1454 /* retrieve texture top-level surface */
1455 LPDIRECT3DSURFACE9 destination;
1456 hr = IDirect3DTexture9_GetSurfaceLevel(sys->d3dtex, 0, &destination);
1458 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1459 return VLC_EGENERIC;
1462 /* Copy picture surface into texture surface
1463 * color space conversion happen here */
1464 hr = IDirect3DDevice9_StretchRect(sys->d3ddev, source, NULL, destination, NULL, D3DTEXF_LINEAR);
1465 IDirect3DSurface9_Release(destination);
1467 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1468 return VLC_EGENERIC;
1472 region->texture = sys->d3dtex;
1473 Direct3DSetupVertices(region->vertex,
1475 vd->sys->rect_src_clipped,
1476 vd->sys->rect_dest_clipped, 255, vd->fmt.orientation);
1480 static void Direct3DDeleteRegions(int count, d3d_region_t *region)
1482 for (int i = 0; i < count; i++) {
1483 if (region[i].texture)
1484 IDirect3DTexture9_Release(region[i].texture);
1489 static void Direct3DImportSubpicture(vout_display_t *vd,
1490 int *count_ptr, d3d_region_t **region,
1491 subpicture_t *subpicture)
1493 vout_display_sys_t *sys = vd->sys;
1496 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
1500 *region = calloc(count, sizeof(**region));
1501 if (*region == NULL) {
1507 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next, i++) {
1508 d3d_region_t *d3dr = &(*region)[i];
1511 d3dr->texture = NULL;
1512 for (int j = 0; j < sys->d3dregion_count; j++) {
1513 d3d_region_t *cache = &sys->d3dregion[j];
1514 if (cache->texture &&
1515 cache->format == sys->d3dregion_format &&
1516 cache->width == r->fmt.i_visible_width &&
1517 cache->height == r->fmt.i_visible_height) {
1519 msg_Dbg(vd, "Reusing %dx%d texture for OSD",
1520 cache->width, cache->height);
1523 memset(cache, 0, sizeof(*cache));
1527 if (!d3dr->texture) {
1528 d3dr->format = sys->d3dregion_format;
1529 d3dr->width = r->fmt.i_visible_width;
1530 d3dr->height = r->fmt.i_visible_height;
1531 hr = IDirect3DDevice9_CreateTexture(sys->d3ddev,
1532 d3dr->width, d3dr->height,
1540 d3dr->texture = NULL;
1541 msg_Err(vd, "Failed to create %dx%d texture for OSD (hr=0x%0lX)",
1542 d3dr->width, d3dr->height, hr);
1546 msg_Dbg(vd, "Created %dx%d texture for OSD",
1547 r->fmt.i_visible_width, r->fmt.i_visible_height);
1551 D3DLOCKED_RECT lock;
1552 hr = IDirect3DTexture9_LockRect(d3dr->texture, 0, &lock, NULL, 0);
1553 if (SUCCEEDED(hr)) {
1554 uint8_t *dst_data = lock.pBits;
1555 int dst_pitch = lock.Pitch;
1556 const int src_offset = r->fmt.i_y_offset * r->p_picture->p->i_pitch +
1557 r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
1558 uint8_t *src_data = &r->p_picture->p->p_pixels[src_offset];
1559 int src_pitch = r->p_picture->p->i_pitch;
1560 for (unsigned y = 0; y < r->fmt.i_visible_height; y++) {
1561 int copy_pitch = __MIN(dst_pitch, r->p_picture->p->i_visible_pitch);
1562 if (d3dr->format == D3DFMT_A8B8G8R8) {
1563 memcpy(&dst_data[y * dst_pitch], &src_data[y * src_pitch],
1566 for (int x = 0; x < copy_pitch; x += 4) {
1567 dst_data[y * dst_pitch + x + 0] = src_data[y * src_pitch + x + 2];
1568 dst_data[y * dst_pitch + x + 1] = src_data[y * src_pitch + x + 1];
1569 dst_data[y * dst_pitch + x + 2] = src_data[y * src_pitch + x + 0];
1570 dst_data[y * dst_pitch + x + 3] = src_data[y * src_pitch + x + 3];
1574 hr = IDirect3DTexture9_UnlockRect(d3dr->texture, 0);
1576 msg_Err(vd, "Failed to unlock the texture");
1578 msg_Err(vd, "Failed to lock the texture");
1581 /* Map the subpicture to sys->rect_dest */
1584 src.right = src.left + r->fmt.i_visible_width;
1586 src.bottom = src.top + r->fmt.i_visible_height;
1588 const RECT video = sys->rect_dest;
1589 const float scale_w = (float)(video.right - video.left) / subpicture->i_original_picture_width;
1590 const float scale_h = (float)(video.bottom - video.top) / subpicture->i_original_picture_height;
1593 dst.left = video.left + scale_w * r->i_x,
1594 dst.right = dst.left + scale_w * r->fmt.i_visible_width,
1595 dst.top = video.top + scale_h * r->i_y,
1596 dst.bottom = dst.top + scale_h * r->fmt.i_visible_height,
1597 Direct3DSetupVertices(d3dr->vertex,
1599 subpicture->i_alpha * r->i_alpha / 255, ORIENT_NORMAL);
1603 static int Direct3DRenderRegion(vout_display_t *vd,
1604 d3d_region_t *region,
1605 bool use_pixel_shader)
1607 vout_display_sys_t *sys = vd->sys;
1609 LPDIRECT3DDEVICE9 d3ddev = vd->sys->d3ddev;
1611 LPDIRECT3DVERTEXBUFFER9 d3dvtc = sys->d3dvtc;
1612 LPDIRECT3DTEXTURE9 d3dtex = region->texture;
1616 /* Import vertices */
1618 hr = IDirect3DVertexBuffer9_Lock(d3dvtc, 0, 0, &vertex, D3DLOCK_DISCARD);
1620 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1623 memcpy(vertex, region->vertex, sizeof(region->vertex));
1624 hr = IDirect3DVertexBuffer9_Unlock(d3dvtc);
1626 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1630 // Setup our texture. Using textures introduces the texture stage states,
1631 // which govern how textures get blended together (in the case of multiple
1632 // textures) and lighting information. In this case, we are modulating
1633 // (blending) our texture with the diffuse color of the vertices.
1634 hr = IDirect3DDevice9_SetTexture(d3ddev, 0, (LPDIRECT3DBASETEXTURE9)d3dtex);
1636 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1640 if (sys->d3dx_shader) {
1641 if (use_pixel_shader)
1643 hr = IDirect3DDevice9_SetPixelShader(d3ddev, sys->d3dx_shader);
1644 float shader_data[4] = { region->width, region->height, 0, 0 };
1645 hr = IDirect3DDevice9_SetPixelShaderConstantF(d3ddev, 0, shader_data, 1);
1647 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1651 else /* Disable any existing pixel shader. */
1652 hr = IDirect3DDevice9_SetPixelShader(d3ddev, NULL);
1654 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1659 // Render the vertex buffer contents
1660 hr = IDirect3DDevice9_SetStreamSource(d3ddev, 0, d3dvtc, 0, sizeof(CUSTOMVERTEX));
1662 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1666 // we use FVF instead of vertex shader
1667 hr = IDirect3DDevice9_SetFVF(d3ddev, D3DFVF_CUSTOMVERTEX);
1669 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1674 hr = IDirect3DDevice9_DrawPrimitive(d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
1676 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1683 * It renders the scene.
1685 * This function is intented for higher end 3D cards, with pixel shader support
1686 * and at least 64 MiB of video RAM.
1688 static void Direct3DRenderScene(vout_display_t *vd,
1689 d3d_region_t *picture,
1690 int subpicture_count,
1691 d3d_region_t *subpicture)
1693 vout_display_sys_t *sys = vd->sys;
1694 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
1697 if (sys->clear_scene) {
1698 /* Clear the backbuffer and the zbuffer */
1699 hr = IDirect3DDevice9_Clear(d3ddev, 0, NULL, D3DCLEAR_TARGET,
1700 D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
1702 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1705 sys->clear_scene = false;
1709 hr = IDirect3DDevice9_BeginScene(d3ddev);
1711 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1715 Direct3DRenderRegion(vd, picture, true);
1717 if (subpicture_count > 0)
1718 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, TRUE);
1719 for (int i = 0; i < subpicture_count; i++) {
1720 d3d_region_t *r = &subpicture[i];
1722 Direct3DRenderRegion(vd, r, false);
1724 if (subpicture_count > 0)
1725 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, FALSE);
1728 hr = IDirect3DDevice9_EndScene(d3ddev);
1730 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1735 /*****************************************************************************
1736 * DesktopCallback: desktop mode variable callback
1737 *****************************************************************************/
1738 static int DesktopCallback(vlc_object_t *object, char const *psz_cmd,
1739 vlc_value_t oldval, vlc_value_t newval,
1742 vout_display_t *vd = (vout_display_t *)object;
1743 vout_display_sys_t *sys = vd->sys;
1744 VLC_UNUSED(psz_cmd);
1748 vlc_mutex_lock(&sys->lock);
1749 const bool ch_desktop = !sys->desktop_requested != !newval.b_bool;
1750 sys->ch_desktop |= ch_desktop;
1751 sys->desktop_requested = newval.b_bool;
1752 vlc_mutex_unlock(&sys->lock);
1763 static void ListShaders(enum_context_t *ctx)
1765 size_t num_shaders = BUILTIN_SHADERS_COUNT;
1766 ctx->values = xrealloc(ctx->values, (ctx->count + num_shaders + 1) * sizeof(char *));
1767 ctx->descs = xrealloc(ctx->descs, (ctx->count + num_shaders + 1) * sizeof(char *));
1768 for (size_t i = 0; i < num_shaders; ++i) {
1769 ctx->values[ctx->count] = strdup(builtin_shaders[i].name);
1770 ctx->descs[ctx->count] = strdup(builtin_shaders[i].name);
1773 ctx->values[ctx->count] = strdup(SELECTED_SHADER_FILE);
1774 ctx->descs[ctx->count] = strdup(SELECTED_SHADER_FILE);
1778 /* Populate the list of available shader techniques in the options */
1779 static int FindShadersCallback(vlc_object_t *object, const char *name,
1780 char ***values, char ***descs)
1785 enum_context_t ctx = { NULL, NULL, 0 };
1789 *values = ctx.values;