direct3d11: only do the SwapChain Present() during Display
[vlc.git] / modules / video_output / win32 / dxva2_deinterlace.c
blobd35dc12fbf73096924da92b88c85651325f07833
1 /*****************************************************************************
2 * dxva2_deinterlace.c: DxVA2 deinterlacing filter
3 *****************************************************************************
4 * Copyright (C) 2017 Videolabs SAS
6 * Authors: Steve Lhomme <robux4@gmail.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
27 #include <stdlib.h>
28 #include <assert.h>
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_filter.h>
33 #include <vlc_picture.h>
35 #define COBJMACROS
36 #include <initguid.h>
37 #include <d3d9.h>
38 #include <dxva2api.h>
39 #include "../../video_chroma/d3d9_fmt.h"
40 #include "../../video_filter/deinterlace/common.h"
42 struct filter_sys_t
44 HINSTANCE hdecoder_dll;
45 /* keep a reference in case the vout is released first */
46 HINSTANCE d3d9_dll;
47 IDirect3DDevice9 *d3ddev;
48 IDirectXVideoProcessor *processor;
49 IDirect3DSurface9 *hw_surface;
51 DXVA2_VideoProcessorCaps decoder_caps;
53 struct deinterlace_ctx context;
54 picture_t * (*buffer_new)( filter_t * );
57 struct filter_mode_t
59 const char *psz_mode;
60 UINT i_mode;
61 deinterlace_algo settings;
63 static struct filter_mode_t filter_mode [] = {
64 { "blend", DXVA2_DeinterlaceTech_BOBLineReplicate,
65 { false, false, false, false } },
66 { "bob", DXVA2_DeinterlaceTech_BOBVerticalStretch,
67 { true, false, false, false } },
68 { "x", DXVA2_DeinterlaceTech_BOBVerticalStretch4Tap,
69 { true, true, false, false } },
70 { "ivtc", DXVA2_DeinterlaceTech_InverseTelecine,
71 { false, true, true, false } },
72 { "yadif2x", DXVA2_DeinterlaceTech_PixelAdaptive,
73 { true, true, false, false } },
76 static void Flush(filter_t *filter)
78 FlushDeinterlacing(&filter->p_sys->context);
81 static void FillSample( DXVA2_VideoSample *p_sample,
82 const struct deinterlace_ctx *p_context,
83 picture_t *p_pic,
84 const video_format_t *p_fmt,
85 const RECT *p_area,
86 int i_field )
88 picture_sys_t *p_sys_src = ActivePictureSys(p_pic);
90 p_sample->SrcSurface = p_sys_src->surface;
91 p_sample->SampleFormat.SampleFormat = p_pic->b_top_field_first ?
92 DXVA2_SampleFieldInterleavedEvenFirst :
93 DXVA2_SampleFieldInterleavedOddFirst;
94 p_sample->Start = 0;
95 p_sample->End = GetFieldDuration(p_context, p_fmt, p_pic) * 10;
96 p_sample->SampleData = DXVA2_SampleData_RFF_TFF_Present;
97 if (!i_field)
98 p_sample->SampleData |= DXVA2_SampleData_TFF;
99 else
100 p_sample->SampleData |= DXVA2_SampleData_RFF;
101 p_sample->DstRect = p_sample->SrcRect = *p_area;
102 p_sample->PlanarAlpha = DXVA2_Fixed32OpaqueAlpha();
105 static void FillBlitParams( DXVA2_VideoProcessBltParams *params, const RECT *area,
106 const DXVA2_VideoSample *samples, int order )
108 memset(params, 0, sizeof(*params));
109 params->TargetFrame = (samples->End - samples->Start) * order / 2;
110 params->TargetRect = *area;
111 params->DestData = 0;
112 params->Alpha = DXVA2_Fixed32OpaqueAlpha();
113 params->DestFormat.SampleFormat = DXVA2_SampleProgressiveFrame;
114 params->BackgroundColor.Alpha = 0xFFFF;
115 params->ConstrictionSize.cx = params->TargetRect.right;
116 params->ConstrictionSize.cy = params->TargetRect.bottom;
119 static int RenderPic( filter_t *filter, picture_t *p_outpic, picture_t *src,
120 int order, int i_field )
122 filter_sys_t *sys = filter->p_sys;
123 const int i_samples = sys->decoder_caps.NumBackwardRefSamples + 1 +
124 sys->decoder_caps.NumForwardRefSamples;
125 HRESULT hr;
126 DXVA2_VideoProcessBltParams params;
127 DXVA2_VideoSample samples[i_samples];
128 picture_t *pictures[i_samples];
129 D3DSURFACE_DESC srcDesc, dstDesc;
130 RECT area;
132 picture_t *p_prev = sys->context.pp_history[0];
133 picture_t *p_cur = sys->context.pp_history[1];
134 picture_t *p_next = sys->context.pp_history[2];
136 picture_sys_t *p_sys_src = ActivePictureSys(src);
138 hr = IDirect3DSurface9_GetDesc( p_sys_src->surface, &srcDesc );
139 if (unlikely(FAILED(hr)))
140 return VLC_EGENERIC;
141 hr = IDirect3DSurface9_GetDesc( sys->hw_surface, &dstDesc );
142 if (unlikely(FAILED(hr)))
143 return VLC_EGENERIC;
145 area.top = area.left = 0;
146 area.bottom = __MIN(srcDesc.Height, dstDesc.Height);
147 area.right = __MIN(srcDesc.Width, dstDesc.Width);
149 int idx = i_samples - 1;
150 if (p_next)
152 pictures[idx--] = p_next;
153 if (p_cur)
154 pictures[idx--] = p_cur;
155 if (p_prev)
156 pictures[idx--] = p_prev;
158 else
159 pictures[idx--] = src;
160 while (idx >= 0)
161 pictures[idx--] = NULL;
163 for (idx = 0; idx <= i_samples-1; idx++)
165 if (pictures[idx])
166 FillSample( &samples[idx], &sys->context, pictures[idx], &filter->fmt_out.video, &area, i_field);
167 else
169 FillSample( &samples[idx], &sys->context, src, &filter->fmt_out.video, &area, i_field);
170 samples[idx].SampleFormat.SampleFormat = DXVA2_SampleUnknown;
174 FillBlitParams( &params, &area, samples, order );
176 hr = IDirectXVideoProcessor_VideoProcessBlt( sys->processor,
177 sys->hw_surface,
178 &params,
179 samples,
180 i_samples, NULL );
181 if (FAILED(hr))
182 return VLC_EGENERIC;
184 hr = IDirect3DDevice9_StretchRect( sys->d3ddev,
185 sys->hw_surface, NULL,
186 p_outpic->p_sys->surface, NULL,
187 D3DTEXF_NONE);
188 if (FAILED(hr))
189 return VLC_EGENERIC;
191 return VLC_SUCCESS;
194 static int RenderSinglePic( filter_t *p_filter, picture_t *p_outpic, picture_t *p_pic )
196 return RenderPic( p_filter, p_outpic, p_pic, 0, 0 );
199 static picture_t *Deinterlace(filter_t *p_filter, picture_t *p_pic)
201 return DoDeinterlacing( p_filter, &p_filter->p_sys->context, p_pic );
204 static const struct filter_mode_t *GetFilterMode(const char *mode)
206 if ( mode == NULL || !strcmp( mode, "auto" ) )
207 mode = "x";
209 for (size_t i=0; i<ARRAY_SIZE(filter_mode); i++)
211 if( !strcmp( mode, filter_mode[i].psz_mode ) )
212 return &filter_mode[i];
215 return NULL;
218 static void d3d9_pic_context_destroy(struct picture_context_t *ctx)
220 struct va_pic_context *pic_ctx = (struct va_pic_context*)ctx;
221 ReleasePictureSys(&pic_ctx->picsys);
222 free(pic_ctx);
225 static struct picture_context_t *d3d9_pic_context_copy(struct picture_context_t *ctx)
227 struct va_pic_context *src_ctx = (struct va_pic_context*)ctx;
228 struct va_pic_context *pic_ctx = calloc(1, sizeof(*pic_ctx));
229 if (unlikely(pic_ctx==NULL))
230 return NULL;
231 pic_ctx->s.destroy = d3d9_pic_context_destroy;
232 pic_ctx->s.copy = d3d9_pic_context_copy;
233 pic_ctx->picsys = src_ctx->picsys;
234 AcquirePictureSys(&pic_ctx->picsys);
235 return &pic_ctx->s;
238 static picture_t *NewOutputPicture( filter_t *p_filter )
240 picture_t *pic = p_filter->p_sys->buffer_new( p_filter );
241 if ( !pic->context )
243 /* the picture might be duplicated for snapshots so it needs a context */
244 assert( pic->p_sys != NULL ); /* this opaque picture is wrong */
245 struct va_pic_context *pic_ctx = calloc(1, sizeof(*pic_ctx));
246 if (likely(pic_ctx!=NULL))
248 pic_ctx->s.destroy = d3d9_pic_context_destroy;
249 pic_ctx->s.copy = d3d9_pic_context_copy;
250 pic_ctx->picsys = *pic->p_sys;
251 AcquirePictureSys( &pic_ctx->picsys );
252 pic->context = &pic_ctx->s;
255 return pic;
258 static int Open(vlc_object_t *obj)
260 filter_t *filter = (filter_t *)obj;
261 filter_sys_t *sys = NULL;
262 HINSTANCE hdecoder_dll = NULL;
263 HINSTANCE d3d9_dll = NULL;
264 HRESULT hr;
265 picture_t *dst = NULL;
266 GUID *processorGUIDs = NULL;
267 GUID *processorGUID = NULL;
268 IDirectXVideoProcessorService *processor = NULL;
270 if (filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE
271 && filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE_10B)
272 return VLC_EGENERIC;
273 if (!video_format_IsSimilar(&filter->fmt_in.video, &filter->fmt_out.video))
274 return VLC_EGENERIC;
276 d3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
277 if (!d3d9_dll)
278 goto error;
280 hdecoder_dll = LoadLibrary(TEXT("DXVA2.DLL"));
281 if (!hdecoder_dll)
282 goto error;
284 dst = filter_NewPicture(filter);
285 if (dst == NULL)
286 goto error;
288 if (!dst->p_sys)
290 msg_Dbg(filter, "D3D9 opaque without a texture");
291 goto error;
294 sys = calloc(1, sizeof (*sys));
295 if (unlikely(sys == NULL))
296 goto error;
298 HRESULT (WINAPI *CreateVideoService)(IDirect3DDevice9 *,
299 REFIID riid,
300 void **ppService);
301 CreateVideoService =
302 (void *)GetProcAddress(hdecoder_dll, "DXVA2CreateVideoService");
303 if (CreateVideoService == NULL)
304 goto error;
306 hr = IDirect3DSurface9_GetDevice( dst->p_sys->surface, &sys->d3ddev );
307 if (FAILED(hr))
308 goto error;
310 D3DSURFACE_DESC dstDesc;
311 hr = IDirect3DSurface9_GetDesc( dst->p_sys->surface, &dstDesc );
312 if (unlikely(FAILED(hr)))
313 goto error;
315 hr = CreateVideoService( sys->d3ddev, &IID_IDirectXVideoProcessorService,
316 (void**)&processor);
317 if (FAILED(hr))
318 goto error;
320 DXVA2_VideoDesc dsc;
321 ZeroMemory(&dsc, sizeof(dsc));
322 dsc.SampleWidth = dstDesc.Width;
323 dsc.SampleHeight = dstDesc.Height;
324 dsc.Format = dstDesc.Format;
325 if (filter->fmt_in.video.i_frame_rate && filter->fmt_in.video.i_frame_rate_base) {
326 dsc.InputSampleFreq.Numerator = filter->fmt_in.video.i_frame_rate;
327 dsc.InputSampleFreq.Denominator = filter->fmt_in.video.i_frame_rate_base;
328 } else {
329 dsc.InputSampleFreq.Numerator = 0;
330 dsc.InputSampleFreq.Denominator = 0;
332 dsc.OutputFrameFreq = dsc.InputSampleFreq;
334 DXVA2_ExtendedFormat *pFormat = &dsc.SampleFormat;
335 pFormat->SampleFormat = dst->b_top_field_first ?
336 DXVA2_SampleFieldInterleavedEvenFirst :
337 DXVA2_SampleFieldInterleavedOddFirst;
339 UINT count = 0;
340 hr = IDirectXVideoProcessorService_GetVideoProcessorDeviceGuids( processor,
341 &dsc,
342 &count,
343 &processorGUIDs);
344 if (FAILED(hr))
345 goto error;
347 char *psz_mode = var_InheritString( filter, "deinterlace-mode" );
348 const struct filter_mode_t *p_mode = GetFilterMode(psz_mode);
349 if (p_mode == NULL)
351 msg_Dbg(filter, "unknown mode %s, trying blend", psz_mode);
352 p_mode = GetFilterMode("blend");
354 if (strcmp(p_mode->psz_mode, psz_mode))
355 msg_Dbg(filter, "using %s deinterlacing mode", p_mode->psz_mode);
357 DXVA2_VideoProcessorCaps caps, best_caps;
358 unsigned best_score = 0;
359 for (UINT i=0; i<count; ++i) {
360 hr = IDirectXVideoProcessorService_GetVideoProcessorCaps( processor,
361 processorGUIDs+i,
362 &dsc,
363 dsc.Format,
364 &caps);
365 if ( FAILED(hr) || !caps.DeinterlaceTechnology )
366 continue;
368 unsigned score = (caps.DeinterlaceTechnology & p_mode->i_mode) ? 10 : 1;
369 if (best_score < score) {
370 best_score = score;
371 best_caps = caps;
372 processorGUID = processorGUIDs + i;
376 if (processorGUID == NULL)
378 msg_Dbg(filter, "Could not find a filter to output the required format");
379 goto error;
382 hr = IDirectXVideoProcessorService_CreateVideoProcessor( processor,
383 processorGUID,
384 &dsc,
385 dsc.Format,
387 &sys->processor );
388 if (FAILED(hr))
389 goto error;
391 hr = IDirectXVideoProcessorService_CreateSurface( processor,
392 dstDesc.Width,
393 dstDesc.Height,
395 dstDesc.Format,
396 D3DPOOL_DEFAULT,
398 DXVA2_VideoProcessorRenderTarget,
399 &sys->hw_surface,
400 NULL);
401 if (FAILED(hr))
402 goto error;
404 sys->hdecoder_dll = hdecoder_dll;
405 sys->d3d9_dll = d3d9_dll;
406 sys->decoder_caps = best_caps;
408 InitDeinterlacingContext( &sys->context );
410 sys->context.settings = p_mode->settings;
411 sys->context.settings.b_use_frame_history = best_caps.NumBackwardRefSamples != 0 ||
412 best_caps.NumForwardRefSamples != 0;
413 if (sys->context.settings.b_use_frame_history != p_mode->settings.b_use_frame_history)
414 msg_Dbg( filter, "deinterlacing not using frame history as requested");
415 if (sys->context.settings.b_double_rate)
416 sys->context.pf_render_ordered = RenderPic;
417 else
418 sys->context.pf_render_single_pic = RenderSinglePic;
420 video_format_t out_fmt;
421 GetDeinterlacingOutput( &sys->context, &out_fmt, &filter->fmt_in.video );
422 if( !filter->b_allow_fmt_out_change &&
423 out_fmt.i_height != filter->fmt_in.video.i_height )
425 goto error;
428 CoTaskMemFree(processorGUIDs);
429 IDirectXVideoProcessorService_Release(processor);
430 picture_Release(dst);
432 sys->buffer_new = filter->owner.video.buffer_new;
433 filter->owner.video.buffer_new = NewOutputPicture;
434 filter->fmt_out.video = out_fmt;
435 filter->pf_video_filter = Deinterlace;
436 filter->pf_flush = Flush;
437 filter->p_sys = sys;
439 return VLC_SUCCESS;
440 error:
441 CoTaskMemFree(processorGUIDs);
442 if (sys && sys->processor)
443 IDirectXVideoProcessor_Release( sys->processor );
444 if (processor)
445 IDirectXVideoProcessorService_Release(processor);
446 if (sys && sys->d3ddev)
447 IDirect3DDevice9_Release( sys->d3ddev );
448 if (hdecoder_dll)
449 FreeLibrary(hdecoder_dll);
450 if (d3d9_dll)
451 FreeLibrary(d3d9_dll);
452 if (dst)
453 picture_Release(dst);
454 free(sys);
456 return VLC_EGENERIC;
459 static void Close(vlc_object_t *obj)
461 filter_t *filter = (filter_t *)obj;
462 filter_sys_t *sys = filter->p_sys;
464 IDirect3DSurface9_Release( sys->hw_surface );
465 IDirectXVideoProcessor_Release( sys->processor );
466 IDirect3DDevice9_Release( sys->d3ddev );
467 FreeLibrary( sys->hdecoder_dll );
468 FreeLibrary( sys->d3d9_dll );
470 free(sys);
473 vlc_module_begin()
474 set_description(N_("Direct3D9 deinterlacing filter"))
475 set_capability("video filter", 0)
476 set_category(CAT_VIDEO)
477 set_subcategory(SUBCAT_VIDEO_VFILTER)
478 set_callbacks(Open, Close)
479 add_shortcut ("deinterlace")
480 vlc_module_end()