qt: playlist: use item title if available
[vlc.git] / modules / video_output / win32 / direct3d9.c
blob55235f63d693b239482cad5079ef769f83ca661d
1 /*****************************************************************************
2 * direct3d9.c: Windows Direct3D9 video output module
3 *****************************************************************************
4 * Copyright (C) 2006-2014 VLC authors and VideoLAN
6 * Authors: Martell Malone <martellmalone@gmail.com>,
7 * 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 /*****************************************************************************
27 * Preamble:
29 * This plugin will use YUV surface if supported, using YUV will result in
30 * the best video quality (hardware filtering 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 *****************************************************************************/
38 #ifdef HAVE_CONFIG_H
39 # include "config.h"
40 #endif
42 #include <vlc_common.h>
43 #include <vlc_plugin.h>
44 #include <vlc_vout_display.h>
45 #include <vlc_fs.h>
47 #include <vlc/libvlc.h>
48 #include <vlc/libvlc_picture.h>
49 #include <vlc/libvlc_media.h>
50 #include <vlc/libvlc_renderer_discoverer.h>
51 #include <vlc/libvlc_media_player.h>
53 #include <windows.h>
54 #include <d3d9.h>
55 #ifdef HAVE_D3DX9EFFECT_H
56 #include <d3dx9effect.h>
57 #endif
58 #include "../../video_chroma/d3d9_fmt.h"
59 #include <dxvahd.h>
61 #include "common.h"
62 #include "builtin_shaders.h"
63 #include "../../video_chroma/copy.h"
65 #include <assert.h>
67 /*****************************************************************************
68 * Module descriptor
69 *****************************************************************************/
70 static int Open(vout_display_t *, const vout_display_cfg_t *,
71 video_format_t *, vlc_video_context *);
72 static void Close(vout_display_t *);
74 #define DESKTOP_LONGTEXT N_(\
75 "The desktop mode allows you to display the video on the desktop.")
77 #define HW_BLENDING_TEXT N_("Use hardware blending support")
78 #define HW_BLENDING_LONGTEXT N_(\
79 "Try to use hardware acceleration for subtitle/OSD blending.")
80 #define HW_YUV_TEXT N_("Use hardware YUV->RGB conversions")
81 #define HW_YUV_LONGTEXT N_(\
82 "Try to use hardware acceleration for YUV->RGB conversions. " \
83 "This option doesn't have any effect when using overlays.")
85 #define PIXEL_SHADER_TEXT N_("Pixel Shader")
86 #define PIXEL_SHADER_LONGTEXT N_(\
87 "Choose a pixel shader to apply.")
88 #define PIXEL_SHADER_FILE_TEXT N_("Path to HLSL file")
89 #define PIXEL_SHADER_FILE_LONGTEXT N_("Path to an HLSL file containing a single pixel shader.")
90 /* The latest option in the selection list: used for loading a shader file. */
91 #define SELECTED_SHADER_FILE N_("HLSL File")
93 #define D3D9_HELP N_("Recommended video output for Windows Vista and later versions")
95 vlc_module_begin ()
96 set_shortname("Direct3D9")
97 set_description(N_("Direct3D9 video output"))
98 set_help(D3D9_HELP)
99 set_category(CAT_VIDEO)
100 set_subcategory(SUBCAT_VIDEO_VOUT)
102 add_bool("direct3d9-hw-blending", true, HW_BLENDING_TEXT, HW_BLENDING_LONGTEXT, true)
103 add_bool("directx-hw-yuv", true, HW_YUV_TEXT, HW_YUV_LONGTEXT, true)
105 add_string("direct3d9-shader", "", PIXEL_SHADER_TEXT, PIXEL_SHADER_LONGTEXT, true)
106 add_loadfile("direct3d9-shader-file", NULL,
107 PIXEL_SHADER_FILE_TEXT, PIXEL_SHADER_FILE_LONGTEXT)
109 add_bool("direct3d9-dxvahd", true, DXVAHD_TEXT, DXVAHD_LONGTEXT, true)
111 add_shortcut("direct3d9", "direct3d")
112 set_callback_display(Open, 280)
113 vlc_module_end ()
115 /*****************************************************************************
116 * Local prototypes.
117 *****************************************************************************/
118 static const vlc_fourcc_t d3d_subpicture_chromas[] = {
119 VLC_CODEC_RGBA,
123 typedef struct
125 const char *name;
126 D3DFORMAT format; /* D3D format */
127 vlc_fourcc_t fourcc; /* VLC fourcc */
128 uint32_t rmask;
129 uint32_t gmask;
130 uint32_t bmask;
131 } d3d9_format_t;
133 struct vout_display_sys_t
135 vout_display_sys_win32_t sys; /* only use if sys.event is not NULL */
136 display_win32_area_t area;
138 bool allow_hw_yuv; /* Should we use hardware YUV->RGB conversions */
140 // core objects
141 d3d9_decoder_device_t *d3d9_device;
142 vlc_decoder_device *dec_device; // if d3d9_decoder comes from a decoder device
144 D3DFORMAT BufferFormat;
146 HINSTANCE hxdll; /* handle of the opened d3d9x dll */
147 IDirect3DPixelShader9* d3dx_shader;
149 // scene objects
150 IDirect3DTexture9 *sceneTexture;
151 IDirect3DVertexBuffer9 *sceneVertexBuffer;
152 D3DFORMAT d3dregion_format; /* Backbuffer output format */
153 size_t d3dregion_count; /* for subpictures */
154 struct d3d_region_t *d3dregion; /* for subpictures */
156 const d3d9_format_t *sw_texture_fmt; /* Rendering texture(s) format */
157 IDirect3DSurface9 *dx_render;
159 /* */
160 bool reset_device;
161 bool lost_not_ready;
162 bool clear_scene;
164 /* outside rendering */
165 void *outside_opaque;
166 libvlc_video_update_output_cb updateOutputCb;
167 libvlc_video_swap_cb swapCb;
168 libvlc_video_makeCurrent_cb startEndRenderingCb;
170 /* range converter */
171 struct {
172 HMODULE dll;
173 IDXVAHD_VideoProcessor *proc;
174 } processor;
177 /* */
178 typedef struct
180 FLOAT x,y,z; // vertex untransformed position
181 FLOAT rhw; // eye distance
182 D3DCOLOR diffuse; // diffuse color
183 FLOAT tu, tv; // texture relative coordinates
184 } CUSTOMVERTEX;
185 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
187 typedef struct d3d_region_t {
188 D3DFORMAT format; // for subpictures
189 unsigned width; // for pixel shaders
190 unsigned height; // for pixel shaders
191 CUSTOMVERTEX vertex[4];
192 IDirect3DTexture9 *texture;
193 } d3d_region_t;
195 /* */
196 static HINSTANCE Direct3D9LoadShaderLibrary(void)
198 HINSTANCE instance = NULL;
199 for (int i = 43; i > 23; --i) {
200 WCHAR filename[16];
201 _snwprintf(filename, ARRAY_SIZE(filename), TEXT("D3dx9_%d.dll"), i);
202 instance = LoadLibrary(filename);
203 if (instance)
204 break;
206 return instance;
210 * Compute the vertex ordering needed to rotate the video. Without
211 * rotation, the vertices of the rectangle are defined in a clockwise
212 * order. This function computes a remapping of the coordinates to
213 * implement the rotation, given fixed texture coordinates.
214 * The unrotated order is the following:
215 * 0--1
216 * | |
217 * 3--2
218 * For a 180 degrees rotation it should like this:
219 * 2--3
220 * | |
221 * 1--0
222 * Vertex 0 should be assigned coordinates at index 2 from the
223 * unrotated order and so on, thus yielding order: 2 3 0 1.
225 static void orientationVertexOrder(video_orientation_t orientation, int vertex_order[static 4])
227 switch (orientation) {
228 case ORIENT_ROTATED_90: /* ORIENT_RIGHT_TOP */
229 vertex_order[0] = 1;
230 vertex_order[1] = 2;
231 vertex_order[2] = 3;
232 vertex_order[3] = 0;
233 break;
234 case ORIENT_ROTATED_270: /* ORIENT_LEFT_BOTTOM */
235 vertex_order[0] = 3;
236 vertex_order[1] = 0;
237 vertex_order[2] = 1;
238 vertex_order[3] = 2;
239 break;
240 case ORIENT_ROTATED_180: /* ORIENT_BOTTOM_RIGHT */
241 vertex_order[0] = 2;
242 vertex_order[1] = 3;
243 vertex_order[2] = 0;
244 vertex_order[3] = 1;
245 break;
246 case ORIENT_TRANSPOSED: /* ORIENT_LEFT_TOP */
247 vertex_order[0] = 0;
248 vertex_order[1] = 3;
249 vertex_order[2] = 2;
250 vertex_order[3] = 1;
251 break;
252 case ORIENT_HFLIPPED: /* ORIENT_TOP_RIGHT */
253 vertex_order[0] = 1;
254 vertex_order[1] = 0;
255 vertex_order[2] = 3;
256 vertex_order[3] = 2;
257 break;
258 case ORIENT_VFLIPPED: /* ORIENT_BOTTOM_LEFT */
259 vertex_order[0] = 3;
260 vertex_order[1] = 2;
261 vertex_order[2] = 1;
262 vertex_order[3] = 0;
263 break;
264 case ORIENT_ANTI_TRANSPOSED: /* ORIENT_RIGHT_BOTTOM */
265 vertex_order[0] = 2;
266 vertex_order[1] = 1;
267 vertex_order[2] = 0;
268 vertex_order[3] = 3;
269 break;
270 default:
271 vertex_order[0] = 0;
272 vertex_order[1] = 1;
273 vertex_order[2] = 2;
274 vertex_order[3] = 3;
275 break;
279 static void Direct3D9SetupVertices(CUSTOMVERTEX *vertices,
280 const RECT *full_texture, const RECT *visible_texture,
281 const RECT *rect_in_display,
282 int alpha,
283 video_orientation_t orientation)
285 /* Vertices of the dst rectangle in the unrotated (clockwise) order. */
286 const int vertices_coords[4][2] = {
287 { rect_in_display->left, rect_in_display->top },
288 { rect_in_display->right, rect_in_display->top },
289 { rect_in_display->right, rect_in_display->bottom },
290 { rect_in_display->left, rect_in_display->bottom },
293 /* Compute index remapping necessary to implement the rotation. */
294 int vertex_order[4];
295 orientationVertexOrder(orientation, vertex_order);
297 for (int i = 0; i < 4; ++i) {
298 vertices[i].x = vertices_coords[vertex_order[i]][0];
299 vertices[i].y = vertices_coords[vertex_order[i]][1];
302 float texture_right = (float)visible_texture->right / (float)full_texture->right;
303 float texture_left = (float)visible_texture->left / (float)full_texture->right;
304 float texture_top = (float)visible_texture->top / (float)full_texture->bottom;
305 float texture_bottom = (float)visible_texture->bottom / (float)full_texture->bottom;
307 vertices[0].tu = texture_left;
308 vertices[0].tv = texture_top;
310 vertices[1].tu = texture_right;
311 vertices[1].tv = texture_top;
313 vertices[2].tu = texture_right;
314 vertices[2].tv = texture_bottom;
316 vertices[3].tu = texture_left;
317 vertices[3].tv = texture_bottom;
319 for (int i = 0; i < 4; i++) {
320 /* -0.5f is a "feature" of DirectX and it seems to apply to Direct3d also */
321 /* http://www.sjbrown.co.uk/2003/05/01/fix-directx-rasterisation/ */
322 vertices[i].x -= 0.5;
323 vertices[i].y -= 0.5;
325 vertices[i].z = 0.0f;
326 vertices[i].rhw = 1.0f;
327 vertices[i].diffuse = D3DCOLOR_ARGB(alpha, 255, 255, 255);
332 * It copies picture surface into a texture and setup the associated d3d_region_t.
334 static int Direct3D9ImportPicture(vout_display_t *vd,
335 d3d_region_t *region,
336 IDirect3DSurface9 *source)
338 vout_display_sys_t *sys = vd->sys;
339 HRESULT hr;
341 if (!source) {
342 msg_Dbg(vd, "no surface to render?");
343 return VLC_EGENERIC;
346 /* retrieve texture top-level surface */
347 IDirect3DSurface9 *destination;
348 hr = IDirect3DTexture9_GetSurfaceLevel(sys->sceneTexture, 0, &destination);
349 if (FAILED(hr)) {
350 msg_Dbg(vd, "Failed IDirect3DTexture9_GetSurfaceLevel: 0x%lX", hr);
351 return VLC_EGENERIC;
354 if (sys->processor.proc)
356 DXVAHD_STREAM_DATA inputStream = { 0 };
357 inputStream.Enable = TRUE;
358 inputStream.pInputSurface = source;
359 hr = IDXVAHD_VideoProcessor_VideoProcessBltHD( sys->processor.proc, destination, 0, 1, &inputStream );
360 if (FAILED(hr)) {
361 D3DSURFACE_DESC srcDesc, dstDesc;
362 IDirect3DSurface9_GetDesc(source, &srcDesc);
363 IDirect3DSurface9_GetDesc(destination, &dstDesc);
365 msg_Dbg(vd, "Failed VideoProcessBltHD src:%4.4s (%d) dst:%4.4s (%d) (hr=0x%lX)",
366 (const char*)&srcDesc.Format, srcDesc.Format,
367 (const char*)&dstDesc.Format, dstDesc.Format, hr);
370 else
372 /* Copy picture surface into texture surface
373 * color space conversion happen here */
374 RECT source_visible_rect = {
375 .left = vd->source->i_x_offset,
376 .right = vd->source->i_x_offset + vd->source->i_visible_width,
377 .top = vd->source->i_y_offset,
378 .bottom = vd->source->i_y_offset + vd->source->i_visible_height,
380 RECT texture_visible_rect = {
381 .left = 0,
382 .right = vd->source->i_visible_width,
383 .top = 0,
384 .bottom = vd->source->i_visible_height,
386 // On nVidia & AMD, StretchRect will fail if the visible size isn't even.
387 // When copying the entire buffer, the margin end up being blended in the actual picture
388 // on nVidia (regardless of even/odd dimensions)
389 if (texture_visible_rect.right & 1)
391 texture_visible_rect.right++;
392 source_visible_rect.right++;
394 if (texture_visible_rect.bottom & 1)
396 texture_visible_rect.bottom++;
397 source_visible_rect.bottom++;
400 hr = IDirect3DDevice9_StretchRect(sys->d3d9_device->d3ddev.dev, source, &source_visible_rect,
401 destination, &texture_visible_rect,
402 D3DTEXF_NONE);
403 if (FAILED(hr)) {
404 msg_Dbg(vd, "Failed StretchRect: source 0x%p. (hr=0x%lX)",
405 (LPVOID)source, hr);
408 IDirect3DSurface9_Release(destination);
409 if (FAILED(hr))
410 return VLC_EGENERIC;
412 /* */
413 region->texture = sys->sceneTexture;
414 RECT texture_rect = {
415 .left = 0,
416 .right = vd->source->i_width,
417 .top = 0,
418 .bottom = vd->source->i_height,
420 RECT rect_in_display = {
421 .left = sys->area.place.x,
422 .right = sys->area.place.x + sys->area.place.width,
423 .top = sys->area.place.y,
424 .bottom = sys->area.place.y + sys->area.place.height,
426 RECT texture_visible_rect = {
427 .left = vd->source->i_x_offset,
428 .right = vd->source->i_x_offset + vd->source->i_visible_width,
429 .top = vd->source->i_y_offset,
430 .bottom = vd->source->i_y_offset + vd->source->i_visible_height,
432 Direct3D9SetupVertices(region->vertex, &texture_rect, &texture_visible_rect,
433 &rect_in_display, 255, vd->source->orientation);
434 return VLC_SUCCESS;
437 static void Direct3D9DeleteRegions(size_t count, d3d_region_t *region)
439 for (size_t i = 0; i < count; i++) {
440 if (region[i].texture)
441 IDirect3DTexture9_Release(region[i].texture);
443 free(region);
447 * It releases the scene resources.
449 static void Direct3D9DestroyScene(vout_display_t *vd)
451 vout_display_sys_t *sys = vd->sys;
453 Direct3D9DeleteRegions(sys->d3dregion_count, sys->d3dregion);
454 sys->d3dregion_count = 0;
455 sys->d3dregion = NULL;
457 if (sys->sceneVertexBuffer)
459 IDirect3DVertexBuffer9_Release(sys->sceneVertexBuffer);
460 sys->sceneVertexBuffer = NULL;
463 if (sys->sceneTexture)
465 IDirect3DTexture9_Release(sys->sceneTexture);
466 sys->sceneTexture = NULL;
469 msg_Dbg(vd, "Direct3D9 scene released successfully");
472 static void Direct3D9DestroyShaders(vout_display_t *vd)
474 vout_display_sys_t *sys = vd->sys;
476 if (sys->d3dx_shader)
478 IDirect3DPixelShader9_Release(sys->d3dx_shader);
479 sys->d3dx_shader = NULL;
484 * It destroys the picture and scene resources.
486 static void Direct3D9DestroyResources(vout_display_t *vd)
488 Direct3D9DestroyScene(vd);
489 if (vd->sys->dx_render)
491 IDirect3DSurface9_Release(vd->sys->dx_render);
492 vd->sys->dx_render = NULL;
494 Direct3D9DestroyShaders(vd);
497 static int UpdateOutput(vout_display_t *vd, const video_format_t *fmt,
498 libvlc_video_output_cfg_t *out)
500 vout_display_sys_t *sys = vd->sys;
501 libvlc_video_render_cfg_t cfg;
502 cfg.width = vd->cfg->display.width;
503 cfg.height = vd->cfg->display.height;
505 switch (fmt->i_chroma)
507 case VLC_CODEC_D3D9_OPAQUE:
508 cfg.bitdepth = 8;
509 break;
510 case VLC_CODEC_D3D9_OPAQUE_10B:
511 cfg.bitdepth = 10;
512 break;
513 default:
515 const vlc_chroma_description_t *p_format = vlc_fourcc_GetChromaDescription(fmt->i_chroma);
516 if (p_format == NULL)
518 cfg.bitdepth = 8;
520 else
522 cfg.bitdepth = p_format->pixel_bits == 0 ? 8 : p_format->pixel_bits /
523 (p_format->plane_count==1 ? p_format->pixel_size : 1);
526 break;
529 cfg.full_range = fmt->color_range == COLOR_RANGE_FULL;
530 cfg.primaries = (libvlc_video_color_primaries_t) fmt->primaries;
531 cfg.colorspace = (libvlc_video_color_space_t) fmt->space;
532 cfg.transfer = (libvlc_video_transfer_func_t) fmt->transfer;
534 cfg.device = sys->d3d9_device->d3ddev.dev;
536 libvlc_video_output_cfg_t local_out;
537 if (out == NULL)
538 out = &local_out;
539 if (!sys->updateOutputCb( sys->outside_opaque, &cfg, out ))
541 msg_Err(vd, "Failed to set the external render size");
542 return VLC_EGENERIC;
545 return VLC_SUCCESS;
549 * It allocates and initializes the resources needed to render the scene.
551 static int Direct3D9CreateScene(vout_display_t *vd, const video_format_t *fmt)
553 vout_display_sys_t *sys = vd->sys;
554 IDirect3DDevice9 *d3ddev = sys->d3d9_device->d3ddev.dev;
555 HRESULT hr;
557 UINT width = fmt->i_visible_width;
558 UINT height = fmt->i_visible_height;
559 // On nVidia & AMD, StretchRect will fail if the visible size isn't even.
560 // When copying the entire buffer, the margin end up being blended in the actual picture
561 // on nVidia (regardless of even/odd dimensions)
562 if (height & 1) height++;
563 if (width & 1) width++;
566 * Create a texture for use when rendering a scene
567 * for performance reason, texture format is identical to backbuffer
568 * which would usually be a RGB format
570 hr = IDirect3DDevice9_CreateTexture(d3ddev,
571 width,
572 height,
574 D3DUSAGE_RENDERTARGET,
575 sys->BufferFormat,
576 D3DPOOL_DEFAULT,
577 &sys->sceneTexture,
578 NULL);
579 if (FAILED(hr)) {
580 msg_Err(vd, "Failed to create texture. (hr=0x%lX)", hr);
581 return VLC_EGENERIC;
584 #ifndef NDEBUG
585 msg_Dbg(vd, "Direct3D created texture: %ix%i",
586 fmt->i_width, fmt->i_height);
587 #endif
590 ** Create a vertex buffer for use when rendering scene
592 hr = IDirect3DDevice9_CreateVertexBuffer(d3ddev,
593 sizeof(CUSTOMVERTEX)*4,
594 D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
595 D3DFVF_CUSTOMVERTEX,
596 D3DPOOL_DEFAULT,
597 &sys->sceneVertexBuffer,
598 NULL);
599 if (FAILED(hr)) {
600 msg_Err(vd, "Failed to create vertex buffer. (hr=0x%lX)", hr);
601 IDirect3DTexture9_Release(sys->sceneTexture);
602 sys->sceneTexture = NULL;
603 return VLC_EGENERIC;
606 // we use FVF instead of vertex shader
607 hr = IDirect3DDevice9_SetFVF(d3ddev, D3DFVF_CUSTOMVERTEX);
608 if (FAILED(hr)) {
609 msg_Dbg(vd, "Failed SetFVF: 0x%lX", hr);
610 return -1;
613 /* */
614 sys->d3dregion_count = 0;
615 sys->d3dregion = NULL;
617 sys->clear_scene = true;
619 // Texture coordinates outside the range [0.0, 1.0] are set
620 // to the texture color at 0.0 or 1.0, respectively.
621 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
622 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
624 // Set linear filtering quality
625 if (sys->d3d9_device->d3ddev.caps.TextureFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) {
626 //msg_Dbg(vd, "Using D3DTEXF_LINEAR for minification");
627 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
628 } else {
629 //msg_Dbg(vd, "Using D3DTEXF_POINT for minification");
630 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
632 if (sys->d3d9_device->d3ddev.caps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR) {
633 //msg_Dbg(vd, "Using D3DTEXF_LINEAR for magnification");
634 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
635 } else {
636 //msg_Dbg(vd, "Using D3DTEXF_POINT for magnification");
637 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
640 // set maximum ambient light
641 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));
643 // Turn off culling
644 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_CULLMODE, D3DCULL_NONE);
646 // Turn off the zbuffer
647 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ZENABLE, D3DZB_FALSE);
649 // Turn off lights
650 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_LIGHTING, FALSE);
652 // Enable dithering
653 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DITHERENABLE, TRUE);
655 // disable stencil
656 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_STENCILENABLE, FALSE);
658 // manage blending
659 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, FALSE);
660 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
661 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
663 if (sys->d3d9_device->d3ddev.caps.AlphaCmpCaps & D3DPCMPCAPS_GREATER) {
664 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHATESTENABLE,TRUE);
665 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAREF, 0x00);
666 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER);
669 // Set texture states
670 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLOROP,D3DTOP_SELECTARG1);
671 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE);
673 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
674 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
675 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);
677 msg_Dbg(vd, "Direct3D9 scene created successfully");
679 return VLC_SUCCESS;
682 #ifdef HAVE_D3DX9EFFECT_H
683 static int Direct3D9CompileShader(vout_display_t *vd, const char *shader_source, size_t source_length)
685 vout_display_sys_t *sys = vd->sys;
687 HRESULT (WINAPI * OurD3DXCompileShader)(
688 LPCSTR pSrcData,
689 UINT srcDataLen,
690 const D3DXMACRO *pDefines,
691 LPD3DXINCLUDE pInclude,
692 LPCSTR pFunctionName,
693 LPCSTR pProfile,
694 DWORD Flags,
695 LPD3DXBUFFER *ppShader,
696 LPD3DXBUFFER *ppErrorMsgs,
697 LPD3DXCONSTANTTABLE *ppConstantTable);
699 OurD3DXCompileShader = (void*)GetProcAddress(sys->hxdll, "D3DXCompileShader");
700 if (!OurD3DXCompileShader) {
701 msg_Warn(vd, "Cannot locate reference to D3DXCompileShader; pixel shading will be disabled");
702 return VLC_EGENERIC;
705 LPD3DXBUFFER error_msgs = NULL;
706 LPD3DXBUFFER compiled_shader = NULL;
708 DWORD shader_flags = 0;
709 HRESULT hr = OurD3DXCompileShader(shader_source, source_length, NULL, NULL,
710 "main", "ps_3_0", shader_flags, &compiled_shader, &error_msgs, NULL);
712 if (FAILED(hr)) {
713 msg_Warn(vd, "D3DXCompileShader Error (hr=0x%lX)", hr);
714 if (error_msgs) {
715 msg_Warn(vd, "HLSL Compilation Error: %s", (char*)ID3DXBuffer_GetBufferPointer(error_msgs));
716 ID3DXBuffer_Release(error_msgs);
718 return VLC_EGENERIC;
721 hr = IDirect3DDevice9_CreatePixelShader(sys->d3d9_device->d3ddev.dev,
722 ID3DXBuffer_GetBufferPointer(compiled_shader),
723 &sys->d3dx_shader);
725 if (compiled_shader)
726 ID3DXBuffer_Release(compiled_shader);
727 if (error_msgs)
728 ID3DXBuffer_Release(error_msgs);
730 if (FAILED(hr)) {
731 msg_Warn(vd, "IDirect3DDevice9_CreatePixelShader error (hr=0x%lX)", hr);
732 return VLC_EGENERIC;
734 return VLC_SUCCESS;
736 #else
737 # define Direct3D9CompileShader(a,b,c) VLC_EGENERIC
738 #endif
740 #define MAX_SHADER_FILE_SIZE (1024*1024)
742 static int Direct3D9CreateShaders(vout_display_t *vd)
744 vout_display_sys_t *sys = vd->sys;
746 if (!sys->hxdll)
747 return VLC_EGENERIC;
749 /* Find which shader was selected in the list. */
750 char *selected_shader = var_InheritString(vd, "direct3d9-shader");
751 if (!selected_shader)
752 return VLC_SUCCESS; /* Nothing to do */
754 const char *shader_source_builtin = NULL;
755 char *shader_source_file = NULL;
756 FILE *fs = NULL;
758 for (size_t i = 0; i < BUILTIN_SHADERS_COUNT; ++i) {
759 if (!strcmp(selected_shader, builtin_shaders[i].name)) {
760 shader_source_builtin = builtin_shaders[i].code;
761 break;
765 if (shader_source_builtin) {
766 /* A builtin shader was selected. */
767 int err = Direct3D9CompileShader(vd, shader_source_builtin, strlen(shader_source_builtin));
768 if (err)
769 goto error;
770 } else {
771 if (strcmp(selected_shader, SELECTED_SHADER_FILE))
772 goto error; /* Unrecognized entry in the list. */
773 /* The source code of the shader needs to be read from a file. */
774 char *filepath = var_InheritString(vd, "direct3d9-shader-file");
775 if (!filepath || !*filepath)
777 free(filepath);
778 goto error;
780 /* Open file, find its size with fseek/ftell and read its content in a buffer. */
781 fs = vlc_fopen(filepath, "rb");
782 if (!fs)
783 goto error;
784 int ret = fseek(fs, 0, SEEK_END);
785 if (ret == -1)
786 goto error;
787 long length = ftell(fs);
788 if (length == -1 || length >= MAX_SHADER_FILE_SIZE)
789 goto error;
790 rewind(fs);
791 shader_source_file = vlc_alloc(length, sizeof(*shader_source_file));
792 if (!shader_source_file)
793 goto error;
794 ret = fread(shader_source_file, length, 1, fs);
795 if (ret != 1)
796 goto error;
797 ret = Direct3D9CompileShader(vd, shader_source_file, length);
798 if (ret)
799 goto error;
802 free(selected_shader);
803 free(shader_source_file);
804 fclose(fs);
806 return VLC_SUCCESS;
808 error:
809 Direct3D9DestroyShaders(vd);
810 free(selected_shader);
811 free(shader_source_file);
812 if (fs)
813 fclose(fs);
814 return VLC_EGENERIC;
818 * It creates the picture and scene resources.
820 static int Direct3D9CreateResources(vout_display_t *vd, const video_format_t *fmt)
822 vout_display_sys_t *sys = vd->sys;
824 if (Direct3D9CreateScene(vd, fmt)) {
825 msg_Err(vd, "Direct3D scene initialization failed !");
826 return VLC_EGENERIC;
828 if (Direct3D9CreateShaders(vd)) {
829 /* Failing to initialize shaders is not fatal. */
830 msg_Warn(vd, "Direct3D shaders initialization failed !");
833 sys->d3dregion_format = D3DFMT_UNKNOWN;
834 for (int i = 0; i < 2; i++) {
835 D3DFORMAT dfmt = i == 0 ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8;
836 if (SUCCEEDED(IDirect3D9_CheckDeviceFormat(sys->d3d9_device->hd3d.obj,
837 sys->d3d9_device->d3ddev.adapterId,
838 D3DDEVTYPE_HAL,
839 sys->BufferFormat,
840 D3DUSAGE_DYNAMIC,
841 D3DRTYPE_TEXTURE,
842 dfmt))) {
843 sys->d3dregion_format = dfmt;
844 break;
848 if( !is_d3d9_opaque( fmt->i_chroma ) )
850 HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(sys->d3d9_device->d3ddev.dev,
851 fmt->i_width,
852 fmt->i_height,
853 sys->sw_texture_fmt->format,
854 D3DPOOL_DEFAULT,
855 &sys->dx_render,
856 NULL);
857 if (FAILED(hr)) {
858 msg_Err(vd, "Failed to allocate offscreen surface (hr=0x%lX)", hr);
859 return VLC_EGENERIC;
863 return VLC_SUCCESS;
867 * It reset the Direct3D9 device and its resources.
869 static int Direct3D9Reset(vout_display_t *vd, const video_format_t *fmtp)
871 vout_display_sys_t *sys = vd->sys;
873 int res = D3D9_ResetDevice( VLC_OBJECT(vd), sys->d3d9_device );
874 if (res != VLC_SUCCESS)
875 return res;
877 /* release all D3D objects */
878 Direct3D9DestroyResources(vd);
880 if (UpdateOutput(vd, fmtp, NULL) != VLC_SUCCESS)
881 return VLC_EGENERIC;
883 /* re-create them */
884 if (Direct3D9CreateResources(vd, fmtp)) {
885 msg_Dbg(vd, "Direct3D9CreateResources failed !");
886 return VLC_EGENERIC;
888 return VLC_SUCCESS;
891 static void Direct3D9ImportSubpicture(vout_display_t *vd,
892 size_t *count_ptr, d3d_region_t **region,
893 subpicture_t *subpicture)
895 vout_display_sys_t *sys = vd->sys;
897 size_t count = 0;
898 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
899 count++;
901 *count_ptr = count;
902 *region = calloc(count, sizeof(**region));
903 if (*region == NULL) {
904 *count_ptr = 0;
905 return;
908 int i = 0;
909 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next, i++) {
910 d3d_region_t *d3dr = &(*region)[i];
911 HRESULT hr;
913 d3dr->texture = NULL;
914 for (size_t j = 0; j < sys->d3dregion_count; j++) {
915 d3d_region_t *cache = &sys->d3dregion[j];
916 if (cache->texture &&
917 cache->format == sys->d3dregion_format &&
918 cache->width == r->fmt.i_width &&
919 cache->height == r->fmt.i_height) {
920 *d3dr = *cache;
921 memset(cache, 0, sizeof(*cache));
922 break;
925 if (!d3dr->texture) {
926 d3dr->format = sys->d3dregion_format;
927 d3dr->width = r->fmt.i_width;
928 d3dr->height = r->fmt.i_height;
929 hr = IDirect3DDevice9_CreateTexture(sys->d3d9_device->d3ddev.dev,
930 d3dr->width, d3dr->height,
932 D3DUSAGE_DYNAMIC,
933 d3dr->format,
934 D3DPOOL_DEFAULT,
935 &d3dr->texture,
936 NULL);
937 if (FAILED(hr)) {
938 d3dr->texture = NULL;
939 msg_Err(vd, "Failed to create %dx%d texture for OSD (hr=0x%lX)",
940 d3dr->width, d3dr->height, hr);
941 continue;
943 #ifndef NDEBUG
944 msg_Dbg(vd, "Created %dx%d texture for OSD",
945 r->fmt.i_width, r->fmt.i_height);
946 #endif
949 D3DLOCKED_RECT lock;
950 hr = IDirect3DTexture9_LockRect(d3dr->texture, 0, &lock, NULL, 0);
951 if (SUCCEEDED(hr)) {
952 uint8_t *dst_data = lock.pBits;
953 int dst_pitch = lock.Pitch;
954 uint8_t *src_data = r->p_picture->p->p_pixels;
955 int src_pitch = r->p_picture->p->i_pitch;
957 if (d3dr->format == D3DFMT_A8B8G8R8) {
958 if (dst_pitch == r->p_picture->p->i_pitch) {
959 memcpy(dst_data, src_data, r->fmt.i_height * dst_pitch);
960 } else {
961 int copy_pitch = __MIN(dst_pitch, r->p_picture->p->i_pitch);
962 for (unsigned y = 0; y < r->fmt.i_height; y++) {
963 memcpy(&dst_data[y * dst_pitch], &src_data[y * src_pitch], copy_pitch);
966 } else {
967 int copy_pitch = __MIN(dst_pitch, r->p_picture->p->i_pitch);
968 for (unsigned y = 0; y < r->fmt.i_height; y++) {
969 for (int x = 0; x < copy_pitch; x += 4) {
970 dst_data[y * dst_pitch + x + 0] = src_data[y * src_pitch + x + 2];
971 dst_data[y * dst_pitch + x + 1] = src_data[y * src_pitch + x + 1];
972 dst_data[y * dst_pitch + x + 2] = src_data[y * src_pitch + x + 0];
973 dst_data[y * dst_pitch + x + 3] = src_data[y * src_pitch + x + 3];
977 hr = IDirect3DTexture9_UnlockRect(d3dr->texture, 0);
978 if (FAILED(hr))
979 msg_Err(vd, "Failed to unlock the texture");
980 } else {
981 msg_Err(vd, "Failed to lock the texture");
984 /* Map the subpicture to sys->sys.sys.place */
985 const float scale_w = (float)(sys->area.place.width) / subpicture->i_original_picture_width;
986 const float scale_h = (float)(sys->area.place.height) / subpicture->i_original_picture_height;
988 RECT rect_in_display;
989 rect_in_display.left = scale_w * r->i_x,
990 rect_in_display.right = rect_in_display.left + scale_w * r->fmt.i_visible_width,
991 rect_in_display.top = scale_h * r->i_y,
992 rect_in_display.bottom = rect_in_display.top + scale_h * r->fmt.i_visible_height;
994 rect_in_display.left += sys->area.place.x;
995 rect_in_display.right += sys->area.place.x;
996 rect_in_display.top += sys->area.place.y;
997 rect_in_display.bottom += sys->area.place.y;
999 RECT texture_rect;
1000 texture_rect.left = 0;
1001 texture_rect.right = r->fmt.i_width;
1002 texture_rect.top = 0;
1003 texture_rect.bottom = r->fmt.i_height;
1005 RECT texture_visible_rect;
1006 texture_visible_rect.left = r->fmt.i_x_offset;
1007 texture_visible_rect.right = r->fmt.i_x_offset + r->fmt.i_visible_width;
1008 texture_visible_rect.top = r->fmt.i_y_offset;
1009 texture_visible_rect.bottom = r->fmt.i_y_offset + r->fmt.i_visible_height;
1011 Direct3D9SetupVertices(d3dr->vertex, &texture_rect, &texture_visible_rect,
1012 &rect_in_display, subpicture->i_alpha * r->i_alpha / 255, ORIENT_NORMAL);
1016 static int Direct3D9RenderRegion(vout_display_t *vd,
1017 const d3d_region_t *region,
1018 bool use_pixel_shader)
1020 vout_display_sys_t *sys = vd->sys;
1022 IDirect3DDevice9 *d3ddev = vd->sys->d3d9_device->d3ddev.dev;
1024 HRESULT hr;
1026 /* Import vertices */
1027 void *vertex;
1028 hr = IDirect3DVertexBuffer9_Lock(sys->sceneVertexBuffer, 0, 0, &vertex, D3DLOCK_DISCARD);
1029 if (FAILED(hr)) {
1030 msg_Dbg(vd, "Failed IDirect3DVertexBuffer9_Lock: 0x%lX", hr);
1031 return -1;
1033 memcpy(vertex, region->vertex, sizeof(region->vertex));
1034 hr = IDirect3DVertexBuffer9_Unlock(sys->sceneVertexBuffer);
1035 if (FAILED(hr)) {
1036 msg_Dbg(vd, "Failed IDirect3DVertexBuffer9_Unlock: 0x%lX", hr);
1037 return -1;
1040 // Render the vertex buffer contents
1041 hr = IDirect3DDevice9_SetStreamSource(d3ddev, 0, sys->sceneVertexBuffer, 0, sizeof(CUSTOMVERTEX));
1042 if (FAILED(hr)) {
1043 msg_Dbg(vd, "Failed SetStreamSource: 0x%lX", hr);
1044 return -1;
1047 // Setup our texture. Using textures introduces the texture stage states,
1048 // which govern how textures get blended together (in the case of multiple
1049 // textures) and lighting information. In this case, we are modulating
1050 // (blending) our texture with the diffuse color of the vertices.
1051 hr = IDirect3DDevice9_SetTexture(d3ddev, 0, (IDirect3DBaseTexture9*)region->texture);
1052 if (FAILED(hr)) {
1053 msg_Dbg(vd, "Failed SetTexture: 0x%lX", hr);
1054 return -1;
1057 if (sys->d3dx_shader) {
1058 if (use_pixel_shader)
1060 hr = IDirect3DDevice9_SetPixelShader(d3ddev, sys->d3dx_shader);
1061 float shader_data[4] = { region->width, region->height, 0, 0 };
1062 hr = IDirect3DDevice9_SetPixelShaderConstantF(d3ddev, 0, shader_data, 1);
1063 if (FAILED(hr)) {
1064 msg_Dbg(vd, "Failed SetPixelShaderConstantF: 0x%lX", hr);
1065 return -1;
1068 else /* Disable any existing pixel shader. */
1069 hr = IDirect3DDevice9_SetPixelShader(d3ddev, NULL);
1070 if (FAILED(hr)) {
1071 msg_Dbg(vd, "Failed SetPixelShader: 0x%lX", hr);
1072 return -1;
1076 // draw rectangle
1077 hr = IDirect3DDevice9_DrawPrimitive(d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
1078 if (FAILED(hr)) {
1079 msg_Dbg(vd, "Failed DrawPrimitive: 0x%lX", hr);
1080 return -1;
1082 return 0;
1086 * It renders the scene.
1088 * This function is intented for higher end 3D cards, with pixel shader support
1089 * and at least 64 MiB of video RAM.
1091 static void Direct3D9RenderScene(vout_display_t *vd,
1092 d3d_region_t *picture,
1093 size_t subpicture_count,
1094 d3d_region_t *subpicture)
1096 vout_display_sys_t *sys = vd->sys;
1097 IDirect3DDevice9 *d3ddev = sys->d3d9_device->d3ddev.dev;
1098 HRESULT hr;
1100 if (sys->startEndRenderingCb && !sys->startEndRenderingCb( sys->outside_opaque, true ))
1101 return;
1103 if (sys->clear_scene) {
1104 /* Clear the backbuffer and the zbuffer */
1105 hr = IDirect3DDevice9_Clear(d3ddev, 0, NULL, D3DCLEAR_TARGET,
1106 D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
1107 if (FAILED(hr)) {
1108 msg_Dbg(vd, "Failed Clear: 0x%lX", hr);
1109 return;
1111 sys->clear_scene = false;
1114 hr = IDirect3DDevice9_BeginScene(d3ddev);
1115 if (FAILED(hr)) {
1116 msg_Dbg(vd, "Failed BeginScene: 0x%lX", hr);
1117 return;
1120 Direct3D9RenderRegion(vd, picture, true);
1122 if (subpicture_count)
1124 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, TRUE);
1125 for (size_t i = 0; i < subpicture_count; i++) {
1126 d3d_region_t *r = &subpicture[i];
1127 if (r->texture)
1128 Direct3D9RenderRegion(vd, r, false);
1130 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, FALSE);
1133 hr = IDirect3DDevice9_EndScene(d3ddev);
1134 if (FAILED(hr))
1135 msg_Dbg(vd, "Failed EndScene: 0x%lX", hr);
1137 if (sys->startEndRenderingCb)
1138 sys->startEndRenderingCb( sys->outside_opaque, false );
1141 static void Prepare(vout_display_t *vd, picture_t *picture,
1142 subpicture_t *subpicture, vlc_tick_t date)
1144 VLC_UNUSED(date);
1145 vout_display_sys_t *sys = vd->sys;
1147 /* Position Change */
1148 if (sys->area.place_changed) {
1149 #if 0 /* need that when bicubic filter is available */
1150 RECT rect;
1151 UINT width, height;
1153 GetClientRect(p_sys->sys.hvideownd, &rect);
1154 width = RECTWidth(rect);
1155 height = RECTHeight(rect);
1157 if (width != p_sys->pp.BackBufferWidth || height != p_sys->pp.BackBufferHeight)
1159 msg_Dbg(vd, "resizing device back buffers to (%lux%lu)", width, height);
1160 // need to reset D3D device to resize back buffer
1161 if (VLC_SUCCESS != Direct3D9ResetDevice(vd, width, height))
1162 return VLC_EGENERIC;
1164 #endif
1165 UpdateOutput(vd, vd->fmt, NULL);
1167 sys->clear_scene = true;
1168 sys->area.place_changed = false;
1171 d3d9_device_t *p_d3d9_dev = &sys->d3d9_device->d3ddev;
1173 /* check if device is still available */
1174 HRESULT hr = IDirect3DDevice9_TestCooperativeLevel(p_d3d9_dev->dev);
1175 if (FAILED(hr)) {
1176 if (hr == D3DERR_DEVICENOTRESET && !sys->reset_device) {
1177 sys->reset_device = true;
1178 /* FIXME what to do here in case of failure */
1179 if (Direct3D9Reset(vd, vd->fmt)) {
1180 msg_Err(vd, "Failed to reset device");
1181 return;
1183 sys->reset_device = false;
1184 sys->lost_not_ready = false;
1186 if (hr == D3DERR_DEVICELOST && !sys->lost_not_ready) {
1187 /* Device is lost but not yet ready for reset. */
1188 sys->lost_not_ready = true;
1190 return;
1193 /* FIXME it is a bit ugly, we need the surface to be unlocked for
1194 * rendering.
1195 * The clean way would be to release the picture (and ensure that
1196 * the vout doesn't keep a reference). But because of the vout
1197 * wrapper, we can't */
1198 IDirect3DSurface9 *surface;
1200 if ( !is_d3d9_opaque(picture->format.i_chroma) )
1202 D3DLOCKED_RECT d3drect;
1203 surface = sys->dx_render;
1204 HRESULT hr = IDirect3DSurface9_LockRect(surface, &d3drect, NULL, 0);
1205 if (unlikely(FAILED(hr))) {
1206 msg_Err(vd, "failed to lock surface");
1207 return;
1210 picture_t fake_pic = *picture;
1211 picture_UpdatePlanes(&fake_pic, d3drect.pBits, d3drect.Pitch);
1212 picture_CopyPixels(&fake_pic, picture);
1213 IDirect3DSurface9_UnlockRect(surface);
1215 else
1217 const picture_sys_d3d9_t *picsys = ActiveD3D9PictureSys(picture);
1218 surface = picsys->surface;
1219 if (picsys->surface != surface)
1221 D3DSURFACE_DESC srcDesc, dstDesc;
1222 IDirect3DSurface9_GetDesc(picsys->surface, &srcDesc);
1223 IDirect3DSurface9_GetDesc(surface, &dstDesc);
1224 if ( srcDesc.Width == dstDesc.Width && srcDesc.Height == dstDesc.Height )
1225 surface = picsys->surface;
1226 else
1228 HRESULT hr;
1229 RECT visibleSource;
1230 visibleSource.left = 0;
1231 visibleSource.top = 0;
1232 visibleSource.right = picture->format.i_visible_width;
1233 visibleSource.bottom = picture->format.i_visible_height;
1235 hr = IDirect3DDevice9_StretchRect( p_d3d9_dev->dev, picsys->surface, &visibleSource, surface, &visibleSource, D3DTEXF_NONE);
1236 if (FAILED(hr)) {
1237 msg_Err(vd, "Failed to copy the hw surface to the decoder surface (hr=0x%lX)", hr );
1243 d3d_region_t picture_region;
1244 if (!Direct3D9ImportPicture(vd, &picture_region, surface)) {
1245 picture_region.width = picture->format.i_visible_width;
1246 picture_region.height = picture->format.i_visible_height;
1247 size_t subpicture_region_count = 0;
1248 d3d_region_t *subpicture_region = NULL;
1249 if (subpicture)
1250 Direct3D9ImportSubpicture(vd, &subpicture_region_count, &subpicture_region,
1251 subpicture);
1253 Direct3D9RenderScene(vd, &picture_region,
1254 subpicture_region_count, subpicture_region);
1256 Direct3D9DeleteRegions(sys->d3dregion_count, sys->d3dregion);
1257 sys->d3dregion_count = subpicture_region_count;
1258 sys->d3dregion = subpicture_region;
1262 static void Swap(vout_display_t *vd)
1264 vout_display_sys_t *sys = vd->sys;
1265 const d3d9_device_t *p_d3d9_dev = &sys->d3d9_device->d3ddev;
1267 // Present the back buffer contents to the display
1268 // No stretching should happen here !
1269 RECT src = {
1270 .left = 0,
1271 .right = vd->cfg->display.width,
1272 .top = 0,
1273 .bottom = vd->cfg->display.height
1276 HRESULT hr;
1277 if (sys->d3d9_device->hd3d.use_ex) {
1278 hr = IDirect3DDevice9Ex_PresentEx(p_d3d9_dev->devex, &src, &src, sys->sys.hvideownd, NULL, 0);
1279 } else {
1280 hr = IDirect3DDevice9_Present(p_d3d9_dev->dev, &src, &src, sys->sys.hvideownd, NULL);
1282 if (FAILED(hr)) {
1283 msg_Dbg(vd, "Failed Present: 0x%lX", hr);
1287 static void Display(vout_display_t *vd, picture_t *picture)
1289 VLC_UNUSED(picture);
1290 vout_display_sys_t *sys = vd->sys;
1292 if (sys->lost_not_ready)
1293 return;
1295 sys->swapCb( sys->outside_opaque );
1299 * It releases an instance of Direct3D9
1301 static void Direct3D9Destroy(vout_display_sys_t *sys)
1303 if (sys->processor.proc)
1305 IDXVAHD_VideoProcessor_Release(sys->processor.proc);
1306 FreeLibrary(sys->processor.dll);
1308 if (sys->dec_device)
1309 vlc_decoder_device_Release(sys->dec_device);
1310 else if (sys->d3d9_device)
1312 D3D9_ReleaseDevice(sys->d3d9_device);
1314 if (sys->hxdll)
1316 FreeLibrary(sys->hxdll);
1317 sys->hxdll = NULL;
1322 * It tests if the conversion from src to dst is supported.
1324 static int Direct3D9CheckConversion(vout_display_t *vd, D3DFORMAT src)
1326 vout_display_sys_t *sys = vd->sys;
1327 IDirect3D9 *d3dobj = sys->d3d9_device->hd3d.obj;
1328 D3DFORMAT dst = sys->BufferFormat;
1329 HRESULT hr;
1331 /* test whether device can create a surface of that format */
1332 hr = IDirect3D9_CheckDeviceFormat(d3dobj, sys->d3d9_device->d3ddev.adapterId,
1333 D3DDEVTYPE_HAL, dst, 0,
1334 D3DRTYPE_SURFACE, src);
1335 if (!SUCCEEDED(hr)) {
1336 if (D3DERR_NOTAVAILABLE != hr)
1337 msg_Err(vd, "Could not query adapter supported formats. (hr=0x%lX)", hr);
1338 return VLC_EGENERIC;
1340 return VLC_SUCCESS;
1343 static const d3d9_format_t d3d_formats[] = {
1344 /* YV12 is always used for planar 420, the planes are then swapped in Lock() */
1345 { "YV12", MAKEFOURCC('Y','V','1','2'), VLC_CODEC_YV12, 0,0,0 },
1346 { "YV12", MAKEFOURCC('Y','V','1','2'), VLC_CODEC_I420, 0,0,0 },
1347 { "YV12", MAKEFOURCC('Y','V','1','2'), VLC_CODEC_J420, 0,0,0 },
1348 { "NV12", MAKEFOURCC('N','V','1','2'), VLC_CODEC_NV12, 0,0,0 },
1349 { "DXA9", MAKEFOURCC('N','V','1','2'), VLC_CODEC_D3D9_OPAQUE, 0,0,0 },
1350 { "DXA9_422", MAKEFOURCC('Y','U','Y','2'), VLC_CODEC_D3D9_OPAQUE, 0,0,0 },
1351 { "DXA9_444", MAKEFOURCC('A','Y','U','V'), VLC_CODEC_D3D9_OPAQUE, 0,0,0 },
1352 { "DXA9_10", MAKEFOURCC('P','0','1','0'), VLC_CODEC_D3D9_OPAQUE_10B, 0,0,0 },
1353 { "DXA9_10_422", MAKEFOURCC('Y','2','1','0'), VLC_CODEC_D3D9_OPAQUE_10B, 0,0,0 },
1354 { "DXA9_10_444", MAKEFOURCC('Y','4','1','0'), VLC_CODEC_D3D9_OPAQUE_10B, 0,0,0 },
1355 { "UYVY", D3DFMT_UYVY, VLC_CODEC_UYVY, 0,0,0 },
1356 { "YUY2", D3DFMT_YUY2, VLC_CODEC_YUYV, 0,0,0 },
1357 { "X8R8G8B8", D3DFMT_X8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
1358 { "A8R8G8B8", D3DFMT_A8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
1359 { "8G8B8", D3DFMT_R8G8B8, VLC_CODEC_RGB24, 0xff0000, 0x00ff00, 0x0000ff },
1360 { "R5G6B5", D3DFMT_R5G6B5, VLC_CODEC_RGB16, 0x1f<<11, 0x3f<<5, 0x1f<<0 },
1361 { "X1R5G5B5", D3DFMT_X1R5G5B5,VLC_CODEC_RGB15, 0x1f<<10, 0x1f<<5, 0x1f<<0 },
1363 { NULL, 0, 0, 0,0,0}
1366 static const d3d9_format_t *FindBufferFormat(D3DFORMAT fmt)
1368 for (unsigned j = 0; d3d_formats[j].name; j++) {
1369 const d3d9_format_t *format = &d3d_formats[j];
1371 if (format->format != fmt)
1372 continue;
1374 return format;
1376 return NULL;
1380 * It returns the format (closest to chroma) that can be converted to target */
1381 static const d3d9_format_t *Direct3DFindFormat(vout_display_t *vd, const video_format_t *fmt, vlc_video_context *vctx)
1383 vout_display_sys_t *sys = vd->sys;
1384 bool hardware_scale_ok = !(fmt->i_visible_width & 1) && !(fmt->i_visible_height & 1);
1385 if( !hardware_scale_ok )
1386 msg_Warn( vd, "Disabling hardware chroma conversion due to odd dimensions" );
1388 for (unsigned pass = 0; pass < 2; pass++) {
1389 const vlc_fourcc_t *list;
1390 const vlc_fourcc_t dxva_chroma[] = {fmt->i_chroma, 0};
1391 D3DFORMAT decoder_format = D3DFMT_UNKNOWN;
1393 if (pass == 0 && is_d3d9_opaque(fmt->i_chroma))
1395 d3d9_video_context_t *vctx_sys = GetD3D9ContextPrivate(vctx);
1396 assert(vctx_sys != NULL);
1397 list = dxva_chroma;
1398 decoder_format = vctx_sys->format;
1399 msg_Dbg(vd, "favor decoder format: %4.4s (%d)", (const char*)&decoder_format, decoder_format);
1401 else if (pass == 0 && hardware_scale_ok && sys->allow_hw_yuv && vlc_fourcc_IsYUV(fmt->i_chroma))
1402 list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
1403 else if (pass == 1)
1404 list = vlc_fourcc_GetRGBFallback(fmt->i_chroma);
1405 else
1406 continue;
1408 for (unsigned i = 0; list[i] != 0; i++) {
1409 for (unsigned j = 0; d3d_formats[j].name; j++) {
1410 const d3d9_format_t *format = &d3d_formats[j];
1412 if (format->fourcc != list[i])
1413 continue;
1414 if (decoder_format != D3DFMT_UNKNOWN && format->format != decoder_format)
1415 continue; // not the Hardware format we prefer
1417 msg_Dbg(vd, "trying surface pixel format: %s", format->name);
1418 if (!Direct3D9CheckConversion(vd, format->format))
1419 return format;
1423 return NULL;
1426 static void SetupProcessorInput(vout_display_t *vd, const video_format_t *fmt, const d3d9_format_t *d3dfmt)
1428 vout_display_sys_t *sys = vd->sys;
1429 HRESULT hr;
1430 DXVAHD_STREAM_STATE_D3DFORMAT_DATA d3dformat = { d3dfmt->format };
1431 hr = IDXVAHD_VideoProcessor_SetVideoProcessStreamState( sys->processor.proc, 0, DXVAHD_STREAM_STATE_D3DFORMAT, sizeof(d3dformat), &d3dformat );
1433 DXVAHD_STREAM_STATE_FRAME_FORMAT_DATA frame_format = { DXVAHD_FRAME_FORMAT_PROGRESSIVE };
1434 hr = IDXVAHD_VideoProcessor_SetVideoProcessStreamState( sys->processor.proc, 0, DXVAHD_STREAM_STATE_FRAME_FORMAT, sizeof(frame_format), &frame_format );
1436 DXVAHD_STREAM_STATE_INPUT_COLOR_SPACE_DATA colorspace = { 0 };
1437 colorspace.RGB_Range = fmt->color_range == COLOR_RANGE_FULL ? 0 : 1;
1438 colorspace.YCbCr_xvYCC = fmt->color_range == COLOR_RANGE_FULL ? 1 : 0;
1439 colorspace.YCbCr_Matrix = fmt->space == COLOR_SPACE_BT601 ? 0 : 1;
1440 hr = IDXVAHD_VideoProcessor_SetVideoProcessStreamState( sys->processor.proc, 0, DXVAHD_STREAM_STATE_INPUT_COLOR_SPACE, sizeof(colorspace), &colorspace );
1442 DXVAHD_STREAM_STATE_SOURCE_RECT_DATA srcRect;
1443 srcRect.Enable = TRUE;
1444 srcRect.SourceRect = (RECT) {
1445 .left = vd->source->i_x_offset,
1446 .right = vd->source->i_x_offset + vd->source->i_visible_width,
1447 .top = vd->source->i_y_offset,
1448 .bottom = vd->source->i_y_offset + vd->source->i_visible_height,
1450 hr = IDXVAHD_VideoProcessor_SetVideoProcessStreamState( sys->processor.proc, 0, DXVAHD_STREAM_STATE_SOURCE_RECT, sizeof(srcRect), &srcRect );
1452 DXVAHD_BLT_STATE_TARGET_RECT_DATA dstRect;
1453 dstRect.Enable = TRUE;
1454 dstRect.TargetRect = (RECT) {
1455 .left = 0,
1456 .right = vd->source->i_visible_width,
1457 .top = 0,
1458 .bottom = vd->source->i_visible_height,
1460 hr = IDXVAHD_VideoProcessor_SetVideoProcessBltState( sys->processor.proc, DXVAHD_BLT_STATE_TARGET_RECT, sizeof(dstRect), &dstRect);
1463 static void GetFrameRate(DXVAHD_RATIONAL *r, const video_format_t *fmt)
1465 if (fmt->i_frame_rate && fmt->i_frame_rate_base)
1467 r->Numerator = fmt->i_frame_rate;
1468 r->Denominator = fmt->i_frame_rate_base;
1470 else
1472 r->Numerator = 0;
1473 r->Denominator = 0;
1477 static int InitRangeProcessor(vout_display_t *vd, const d3d9_format_t *d3dfmt,
1478 const libvlc_video_output_cfg_t *render_out)
1480 vout_display_sys_t *sys = vd->sys;
1482 HRESULT hr;
1484 sys->processor.dll = LoadLibrary(TEXT("DXVA2.DLL"));
1485 if (unlikely(!sys->processor.dll))
1487 msg_Err(vd, "Failed to load DXVA2.DLL");
1488 return VLC_EGENERIC;
1491 D3DFORMAT *formatsList = NULL;
1492 DXVAHD_VPCAPS *capsList = NULL;
1493 IDXVAHD_Device *hd_device = NULL;
1495 HRESULT (WINAPI *CreateDevice)(IDirect3DDevice9Ex *,const DXVAHD_CONTENT_DESC *,DXVAHD_DEVICE_USAGE,PDXVAHDSW_Plugin,IDXVAHD_Device **);
1496 CreateDevice = (void *)GetProcAddress(sys->processor.dll, "DXVAHD_CreateDevice");
1497 if (CreateDevice == NULL)
1499 msg_Err(vd, "Can't create HD device (not Windows 7+)");
1500 goto error;
1503 DXVAHD_CONTENT_DESC desc;
1504 desc.InputFrameFormat = DXVAHD_FRAME_FORMAT_PROGRESSIVE;
1505 GetFrameRate( &desc.InputFrameRate, vd->source );
1506 desc.InputWidth = vd->source->i_visible_width;
1507 desc.InputHeight = vd->source->i_visible_height;
1508 desc.OutputFrameRate = desc.InputFrameRate;
1509 desc.OutputWidth = vd->source->i_visible_width;
1510 desc.OutputHeight = vd->source->i_visible_height;
1512 hr = CreateDevice(sys->d3d9_device->d3ddev.devex, &desc, DXVAHD_DEVICE_USAGE_PLAYBACK_NORMAL, NULL, &hd_device);
1513 if (FAILED(hr))
1515 msg_Dbg(vd, "Failed to create the device (error 0x%lX)", hr);
1516 goto error;
1519 DXVAHD_VPDEVCAPS devcaps = { 0 };
1520 hr = IDXVAHD_Device_GetVideoProcessorDeviceCaps( hd_device, &devcaps );
1521 if (unlikely(FAILED(hr)))
1523 msg_Err(vd, "Failed to get the device capabilities (error 0x%lX)", hr);
1524 goto error;
1526 if (devcaps.VideoProcessorCount == 0)
1528 msg_Warn(vd, "No good video processor found for range conversion");
1529 goto error;
1532 formatsList = malloc(devcaps.InputFormatCount * sizeof(*formatsList));
1533 if (unlikely(formatsList == NULL))
1535 msg_Dbg(vd, "Failed to allocate %u input formats", devcaps.InputFormatCount);
1536 goto error;
1539 hr = IDXVAHD_Device_GetVideoProcessorInputFormats( hd_device, devcaps.InputFormatCount, formatsList);
1540 UINT i;
1541 for (i=0; i<devcaps.InputFormatCount; i++)
1543 if (formatsList[i] == d3dfmt->format)
1544 break;
1546 if (i == devcaps.InputFormatCount)
1548 msg_Warn(vd, "Input format %s not supported for range conversion", d3dfmt->name);
1549 goto error;
1552 free(formatsList);
1553 formatsList = malloc(devcaps.OutputFormatCount * sizeof(*formatsList));
1554 if (unlikely(formatsList == NULL))
1556 msg_Dbg(vd, "Failed to allocate %u output formats", devcaps.OutputFormatCount);
1557 goto error;
1560 hr = IDXVAHD_Device_GetVideoProcessorOutputFormats( hd_device, devcaps.OutputFormatCount, formatsList);
1561 for (i=0; i<devcaps.OutputFormatCount; i++)
1563 if (formatsList[i] == sys->BufferFormat)
1564 break;
1566 if (i == devcaps.OutputFormatCount)
1568 msg_Warn(vd, "Output format %d not supported for range conversion", sys->BufferFormat);
1569 goto error;
1572 capsList = malloc(devcaps.VideoProcessorCount * sizeof(*capsList));
1573 if (unlikely(capsList == NULL))
1575 msg_Dbg(vd, "Failed to allocate %u video processors", devcaps.VideoProcessorCount);
1576 goto error;
1578 hr = IDXVAHD_Device_GetVideoProcessorCaps( hd_device, devcaps.VideoProcessorCount, capsList);
1579 if (FAILED(hr))
1581 msg_Dbg(vd, "Failed to get the processor caps (error 0x%lX)", hr);
1582 goto error;
1585 hr = IDXVAHD_Device_CreateVideoProcessor( hd_device, &capsList->VPGuid, &sys->processor.proc );
1586 if (FAILED(hr))
1588 msg_Dbg(vd, "Failed to create the processor (error 0x%lX)", hr);
1589 goto error;
1591 IDXVAHD_Device_Release( hd_device );
1593 SetupProcessorInput(vd, vd->source, d3dfmt);
1595 DXVAHD_BLT_STATE_OUTPUT_COLOR_SPACE_DATA colorspace;
1596 colorspace.Usage = 0; // playback
1597 colorspace.RGB_Range = render_out->full_range ? 0 : 1;
1598 colorspace.YCbCr_xvYCC = render_out->full_range ? 1 : 0;
1599 colorspace.YCbCr_Matrix = render_out->colorspace == libvlc_video_colorspace_BT601 ? 0 : 1;
1600 hr = IDXVAHD_VideoProcessor_SetVideoProcessBltState( sys->processor.proc, DXVAHD_BLT_STATE_OUTPUT_COLOR_SPACE, sizeof(colorspace), &colorspace);
1602 return VLC_SUCCESS;
1604 error:
1605 free(capsList);
1606 free(formatsList);
1607 if (hd_device)
1608 IDXVAHD_Device_Release(hd_device);
1609 FreeLibrary(sys->processor.dll);
1610 return VLC_EGENERIC;
1614 * It creates a Direct3D9 device and the associated resources.
1616 static int Direct3D9Open(vout_display_t *vd, video_format_t *fmt, vlc_video_context *vctx)
1618 vout_display_sys_t *sys = vd->sys;
1620 libvlc_video_output_cfg_t render_out;
1621 if (UpdateOutput(vd, vd->source, &render_out) != VLC_SUCCESS)
1622 return VLC_EGENERIC;
1624 sys->BufferFormat = render_out.d3d9_format;
1625 const d3d9_format_t *dst_format = FindBufferFormat(sys->BufferFormat);
1626 if (unlikely(!dst_format))
1627 msg_Warn(vd, "Unknown back buffer format 0x%X", sys->BufferFormat);
1629 /* Find the appropriate D3DFORMAT for the render chroma, the format will be the closest to
1630 * the requested chroma which is usable by the hardware in an offscreen surface, as they
1631 * typically support more formats than textures */
1632 const d3d9_format_t *d3dfmt = Direct3DFindFormat(vd, vd->source, vctx);
1633 if (!d3dfmt) {
1634 msg_Err(vd, "unsupported source pixel format %4.4s", (const char*)&vd->source->i_chroma);
1635 goto error;
1637 msg_Dbg(vd, "found input surface format %s for source %4.4s", d3dfmt->name, (const char *)&vd->source->i_chroma);
1639 bool force_dxva_hd = var_InheritBool(vd, "direct3d9-dxvahd");
1640 if (force_dxva_hd || (dst_format && vd->source->color_range != COLOR_RANGE_FULL && dst_format->rmask && !d3dfmt->rmask &&
1641 sys->d3d9_device->d3ddev.identifier.VendorId == GPU_MANUFACTURER_NVIDIA))
1643 // NVIDIA bug, YUV to RGB internal conversion in StretchRect always converts from limited to limited range
1644 msg_Dbg(vd, "init DXVA-HD processor from %s to %s", d3dfmt->name, dst_format?dst_format->name:"unknown");
1645 int err = InitRangeProcessor( vd, d3dfmt, &render_out );
1646 if (err != VLC_SUCCESS)
1647 force_dxva_hd = false;
1649 if (!force_dxva_hd)
1651 // test whether device can perform color-conversion from that format to target format
1652 HRESULT hr = IDirect3D9_CheckDeviceFormatConversion(sys->d3d9_device->hd3d.obj,
1653 sys->d3d9_device->d3ddev.adapterId,
1654 D3DDEVTYPE_HAL,
1655 d3dfmt->format, sys->BufferFormat);
1656 if (FAILED(hr))
1658 msg_Dbg(vd, "Unsupported conversion from %s to %s", d3dfmt->name, dst_format?dst_format->name:"unknown" );
1659 goto error;
1661 msg_Dbg(vd, "using StrecthRect from %s to %s", d3dfmt->name, dst_format?dst_format->name:"unknown" );
1664 /* */
1665 *fmt = *vd->source;
1666 fmt->i_chroma = d3dfmt->fourcc;
1667 fmt->i_rmask = d3dfmt->rmask;
1668 fmt->i_gmask = d3dfmt->gmask;
1669 fmt->i_bmask = d3dfmt->bmask;
1670 sys->sw_texture_fmt = d3dfmt;
1672 if (Direct3D9CreateResources(vd, fmt)) {
1673 msg_Err(vd, "Failed to allocate resources");
1674 goto error;
1677 msg_Dbg(vd, "Direct3D9 display adapter successfully initialized");
1678 return VLC_SUCCESS;
1680 error:
1681 return VLC_EGENERIC;
1685 * It releases the Direct3D9 device and its resources.
1687 static void Direct3D9Close(vout_display_t *vd)
1689 Direct3D9DestroyResources(vd);
1692 static int Control(vout_display_t *vd, int query)
1694 vout_display_sys_t *sys = vd->sys;
1695 return CommonControl(vd, &sys->area, &sys->sys, query);
1698 typedef struct
1700 char **values;
1701 char **descs;
1702 size_t count;
1703 } enum_context_t;
1705 static void ListShaders(enum_context_t *ctx)
1707 size_t num_shaders = BUILTIN_SHADERS_COUNT;
1708 ctx->values = xrealloc(ctx->values, (ctx->count + num_shaders + 1) * sizeof(char *));
1709 ctx->descs = xrealloc(ctx->descs, (ctx->count + num_shaders + 1) * sizeof(char *));
1710 for (size_t i = 0; i < num_shaders; ++i) {
1711 ctx->values[ctx->count] = strdup(builtin_shaders[i].name);
1712 ctx->descs[ctx->count] = strdup(builtin_shaders[i].name);
1713 ctx->count++;
1715 ctx->values[ctx->count] = strdup(SELECTED_SHADER_FILE);
1716 ctx->descs[ctx->count] = strdup(SELECTED_SHADER_FILE);
1717 ctx->count++;
1720 /* Populate the list of available shader techniques in the options */
1721 static int FindShadersCallback(const char *name, char ***values, char ***descs)
1723 VLC_UNUSED(name);
1725 enum_context_t ctx = { NULL, NULL, 0 };
1727 ListShaders(&ctx);
1729 *values = ctx.values;
1730 *descs = ctx.descs;
1731 return ctx.count;
1735 VLC_CONFIG_STRING_ENUM(FindShadersCallback)
1737 static bool LocalSwapchainUpdateOutput( void *opaque, const libvlc_video_render_cfg_t *cfg, libvlc_video_output_cfg_t *out )
1739 VLC_UNUSED(cfg);
1740 vout_display_t *vd = opaque;
1741 vout_display_sys_t *sys = vd->sys;
1743 D3DDISPLAYMODE d3ddm;
1744 HRESULT hr = IDirect3D9_GetAdapterDisplayMode(sys->d3d9_device->hd3d.obj, sys->d3d9_device->d3ddev.adapterId, &d3ddm);
1745 if (unlikely(FAILED(hr)))
1746 return false;
1748 out->d3d9_format = d3ddm.Format;
1749 out->full_range = true;
1750 out->colorspace = libvlc_video_colorspace_BT709;
1751 out->primaries = libvlc_video_primaries_BT709;
1752 out->transfer = libvlc_video_transfer_func_SRGB;
1754 return true;
1757 static void LocalSwapchainSwap( void *opaque )
1759 vout_display_t *vd = opaque;
1760 Swap( vd );
1763 static const struct vlc_display_operations ops = {
1764 Close, Prepare, Display, Control, NULL, NULL,
1768 * It creates a Direct3D vout display.
1770 static int Open(vout_display_t *vd, const vout_display_cfg_t *cfg,
1771 video_format_t *fmtp, vlc_video_context *context)
1773 vout_display_sys_t *sys;
1775 if ( !vd->obj.force && vd->source->projection_mode != PROJECTION_MODE_RECTANGULAR)
1776 return VLC_EGENERIC; /* let a module who can handle it do it */
1778 if ( !vd->obj.force && vd->source->mastering.max_luminance != 0)
1779 return VLC_EGENERIC; /* let a module who can handle it do it */
1781 /* do not use D3D9 on XP unless forced */
1782 if (!vd->obj.force)
1784 bool isVistaOrGreater = false;
1785 HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32.dll"));
1786 if (likely(hKernel32 != NULL))
1787 isVistaOrGreater = GetProcAddress(hKernel32, "EnumResourceLanguagesExW") != NULL;
1788 if (!isVistaOrGreater)
1789 return VLC_EGENERIC;
1792 /* Allocate structure */
1793 vd->sys = sys = calloc(1, sizeof(vout_display_sys_t));
1794 if (!sys)
1795 return VLC_ENOMEM;
1797 CommonInit(&sys->area);
1799 sys->outside_opaque = var_InheritAddress( vd, "vout-cb-opaque" );
1800 sys->updateOutputCb = var_InheritAddress( vd, "vout-cb-update-output" );
1801 sys->swapCb = var_InheritAddress( vd, "vout-cb-swap" );
1802 sys->startEndRenderingCb = var_InheritAddress( vd, "vout-cb-make-current" );
1804 if ( sys->swapCb == NULL || sys->startEndRenderingCb == NULL || sys->updateOutputCb == NULL )
1806 /* use our own callbacks, since there isn't any external ones */
1807 if (CommonWindowInit(vd, &sys->area, &sys->sys, false))
1808 goto error;
1810 sys->outside_opaque = vd;
1811 sys->updateOutputCb = LocalSwapchainUpdateOutput;
1812 sys->swapCb = LocalSwapchainSwap;
1813 sys->startEndRenderingCb = NULL;
1816 sys->dec_device = context ? vlc_video_context_HoldDevice(context) : NULL;
1817 sys->d3d9_device = GetD3D9OpaqueDevice(sys->dec_device);
1818 if ( sys->d3d9_device == NULL )
1820 if (sys->dec_device)
1822 vlc_decoder_device_Release(sys->dec_device);
1823 sys->dec_device = NULL;
1826 sys->d3d9_device = D3D9_CreateDevice( vd );
1827 if (sys->d3d9_device == NULL) {
1828 msg_Err( vd, "D3D9 Creation failed!" );
1829 goto error;
1834 if ( vd->source->i_visible_width > sys->d3d9_device->d3ddev.caps.MaxTextureWidth ||
1835 vd->source->i_visible_height > sys->d3d9_device->d3ddev.caps.MaxTextureHeight )
1837 msg_Err(vd, "Textures too large %ux%u max possible: %lx%lx",
1838 vd->source->i_visible_width, vd->source->i_visible_height,
1839 sys->d3d9_device->d3ddev.caps.MaxTextureWidth,
1840 sys->d3d9_device->d3ddev.caps.MaxTextureHeight);
1841 goto error;
1844 if (sys->swapCb == LocalSwapchainSwap)
1845 CommonPlacePicture(vd, &sys->area);
1847 sys->hxdll = Direct3D9LoadShaderLibrary();
1848 if (!sys->hxdll)
1849 msg_Warn(vd, "cannot load Direct3D9 Shader Library; HLSL pixel shading will be disabled.");
1851 sys->reset_device = false;
1852 sys->lost_not_ready = false;
1853 sys->allow_hw_yuv = var_CreateGetBool(vd, "directx-hw-yuv");
1855 /* */
1856 video_format_t fmt;
1857 if (Direct3D9Open(vd, &fmt, context)) {
1858 msg_Err(vd, "Direct3D9 could not be opened");
1859 goto error;
1862 /* Setup vout_display now that everything is fine */
1863 if (var_InheritBool(vd, "direct3d9-hw-blending") &&
1864 sys->d3dregion_format != D3DFMT_UNKNOWN &&
1865 (sys->d3d9_device->d3ddev.caps.SrcBlendCaps & D3DPBLENDCAPS_SRCALPHA) &&
1866 (sys->d3d9_device->d3ddev.caps.DestBlendCaps & D3DPBLENDCAPS_INVSRCALPHA) &&
1867 (sys->d3d9_device->d3ddev.caps.TextureCaps & D3DPTEXTURECAPS_ALPHA) &&
1868 (sys->d3d9_device->d3ddev.caps.TextureOpCaps & D3DTEXOPCAPS_SELECTARG1) &&
1869 (sys->d3d9_device->d3ddev.caps.TextureOpCaps & D3DTEXOPCAPS_MODULATE))
1870 vd->info.subpicture_chromas = d3d_subpicture_chromas;
1871 else
1872 vd->info.subpicture_chromas = NULL;
1874 video_format_Clean(fmtp);
1875 video_format_Copy(fmtp, &fmt);
1877 vd->ops = &ops;
1879 /* Change the window title bar text */
1880 vout_window_SetTitle(cfg->window, VOUT_TITLE " (Direct3D9 output)");
1882 return VLC_SUCCESS;
1883 error:
1884 Direct3D9Close(vd);
1885 CommonWindowClean(&sys->sys);
1886 Direct3D9Destroy(sys);
1887 free(vd->sys);
1888 return VLC_EGENERIC;
1892 * It destroyes a Direct3D vout display.
1894 static void Close(vout_display_t *vd)
1896 Direct3D9Close(vd);
1898 CommonWindowClean(&vd->sys->sys);
1900 Direct3D9Destroy(vd->sys);
1902 free(vd->sys);