[vlc-devel] [PATCH] [RFC] always output an opaque D3D9 surface from DXVA
Rémi Denis-Courmont
remi at remlab.net
Thu Apr 23 17:51:05 CEST 2015
Le jeudi 23 avril 2015, 17:11:19 Steve Lhomme a écrit :
> This is a test code to show how DXVA direct rendering could be done.
>
> When avcodec asks for an AVFrame, the vout and its decoder pool is already
> created. So we use the D3D9 device from the picture_t coming from that pool
> to recreate the DXVA2 decoder surface.
>
> This is working fine for normal playback, seeking, opening files of
> same/different dimensions.
>
> The decoder of D3D9 surface is created in the vout wrapper (like all the
> others) but with a dirty hack for now.
>
> This code also works with direct rendering to D3D9, not included in this
> patch.
>
> The changes to the other va->Setup callback are not done and it's a
> temporary test code. ---
> include/vlc_fourcc.h | 3 +
> modules/codec/avcodec/dxva2.c | 173 ++++++++++++++++++++++------
> modules/codec/avcodec/va.h | 6 +-
> modules/codec/avcodec/video.c | 19 +--
> modules/video_chroma/copy.c | 40 +++++--
> src/Makefile.am | 2 +
> src/misc/fourcc.c | 3 +
> src/video_output/vout_wrapper.c | 17 ++-
> src/win32/direct3d9_pool.c | 248
> ++++++++++++++++++++++++++++++++++++++++ src/win32/direct3d9_pool.h |
> 39 +++++++
> 10 files changed, 491 insertions(+), 59 deletions(-)
> create mode 100644 src/win32/direct3d9_pool.c
> create mode 100644 src/win32/direct3d9_pool.h
>
> diff --git a/include/vlc_fourcc.h b/include/vlc_fourcc.h
> index 5d30ece..df82f6f 100644
> --- a/include/vlc_fourcc.h
> +++ b/include/vlc_fourcc.h
> @@ -332,6 +332,9 @@
> /* Broadcom MMAL opaque buffer type */
> #define VLC_CODEC_MMAL_OPAQUE VLC_FOURCC('M','M','A','L')
>
> +/* DXVA2 opaque video surface for use with D3D9 */
> +#define VLC_CODEC_D3D9_OPAQUE VLC_FOURCC('D','X','A','9')
> +
> /* Image codec (video) */
> #define VLC_CODEC_PNG VLC_FOURCC('p','n','g',' ')
> #define VLC_CODEC_PPM VLC_FOURCC('p','p','m',' ')
> diff --git a/modules/codec/avcodec/dxva2.c b/modules/codec/avcodec/dxva2.c
> index db9820f..4382e24 100644
> --- a/modules/codec/avcodec/dxva2.c
> +++ b/modules/codec/avcodec/dxva2.c
> @@ -42,6 +42,7 @@
> #include <vlc_cpu.h>
> #include <vlc_plugin.h>
> #include <vlc_codecs.h>
> +#include <vlc_filter.h>
>
> #include <libavcodec/avcodec.h>
> # define DXVA2API_USE_BITFIELDS
> @@ -55,12 +56,19 @@
> static int Open(vlc_va_t *, AVCodecContext *, const es_format_t *);
> static void Close(vlc_va_t *, AVCodecContext *);
>
> +static int OpenFilter( vlc_object_t * );
> +static void CloseFilter( vlc_object_t * );
> +
> vlc_module_begin()
> set_description(N_("DirectX Video Acceleration (DXVA) 2.0"))
> set_capability("hw decoder", 0)
> set_category(CAT_INPUT)
> set_subcategory(SUBCAT_INPUT_VCODEC)
> set_callbacks(Open, Close)
> + add_submodule()
> + set_description( N_("DXVA2 surface to planes") )
> + set_capability( "video filter2", 10 )
> + set_callbacks( OpenFilter, CloseFilter )
> vlc_module_end()
>
> #include <windows.h>
> @@ -348,6 +356,11 @@ struct vlc_va_sys_t
> LPDIRECT3DSURFACE9 hw_surface[VA_DXVA2_MAX_SURFACE_COUNT];
> };
>
> +struct picture_sys_t
> +{
> + LPDIRECT3DSURFACE9 surface;
> +};
You don't need a structure to store a single pointer (assuming you won't be
adding more stuff).
> +
> /* */
> static int D3dCreateDevice(vlc_va_t *);
> static void D3dDestroyDevice(vlc_va_sys_t *);
> @@ -369,9 +382,49 @@ static void DxCreateVideoConversion(vlc_va_sys_t *);
> static void DxDestroyVideoConversion(vlc_va_sys_t *);
>
> /* */
> -static int Setup(vlc_va_t *va, AVCodecContext *avctx, vlc_fourcc_t *chroma)
> +static int Setup(vlc_va_t *va, AVCodecContext *avctx, vlc_fourcc_t
> *chroma, picture_t *p_test_output) {
> vlc_va_sys_t *sys = va->sys;
> + LPDIRECT3DDEVICE9 picd3ddev = NULL;
> + if (p_test_output!=NULL && p_test_output->p_sys)
> + IDirect3DSurface9_GetDevice(p_test_output->p_sys->surface,
> &picd3ddev);
> +
> + if ( picd3ddev != NULL && picd3ddev != sys->d3ddev )
> + {
> + /* restart the dxva decoder with the appropriate D3D9 device */
> + DxDestroyVideoDecoder(sys);
> + DxDestroyVideoService(sys);
> + D3dDestroyDeviceManager(sys);
> + D3dDestroyDevice(sys);
> +
> + if (FAILED(IDirect3DDevice9_GetDirect3D(picd3ddev, &sys->d3dobj)))
> { + msg_Err(va, "Can't retrieve the D3D from the device"); +
> goto error;
> + }
> + IDirect3D9_AddRef(sys->d3dobj);
> + IDirect3DDevice9_AddRef(picd3ddev);
> + sys->d3ddev = picd3ddev;
> + msg_Dbg(va, "D3dCreateDevice succeed");
> +
> + if (D3dCreateDeviceManager(va)) {
> + msg_Err(va, "D3dCreateDeviceManager failed");
> + goto error;
> + }
> +
> + if (DxCreateVideoService(va)) {
> + msg_Err(va, "DxCreateVideoService failed");
> + goto error;
> + }
> +
> + /* */
> + es_format_t fake_fmt;
> + memset(&fake_fmt, 0, sizeof(fake_fmt));
> + fake_fmt.i_profile = avctx->profile;
> + if (DxFindVideoServiceConversion(va, &sys->input, &sys->render,
> &fake_fmt)) { + msg_Err(va, "DxFindVideoServiceConversion
> failed");
> + goto error;
> + }
> + }
>
> if (sys->width == avctx->coded_width && sys->height ==
> avctx->coded_height && sys->decoder != NULL)
> @@ -406,33 +459,40 @@ static int Setup(vlc_va_t *va, AVCodecContext *avctx,
> vlc_fourcc_t *chroma) /* */
> ok:
> avctx->hwaccel_context = &sys->hw;
> - const d3d_format_t *output = D3dFindFormat(sys->output);
> - *chroma = output->codec;
> + *chroma = VLC_CODEC_D3D9_OPAQUE;
>
> return VLC_SUCCESS;
> +
> +error:
> + DxDestroyVideoDecoder(sys);
> + DxDestroyVideoService(sys);
> + D3dDestroyDeviceManager(sys);
> + D3dDestroyDevice(sys);
> + return VLC_EGENERIC;
> }
>
> -static int Extract(vlc_va_t *va, picture_t *picture, uint8_t *data)
> +static void DXA9_I420 (filter_t *p_filter, picture_t *src, picture_t *dst)
> {
> - vlc_va_sys_t *sys = va->sys;
> - LPDIRECT3DSURFACE9 d3d = (LPDIRECT3DSURFACE9)(uintptr_t)data;
> + copy_cache_t *p_copy_cache = (copy_cache_t*) p_filter->p_sys;
>
> if (!sys->surface_cache.buffer)
> return VLC_EGENERIC;
>
> - /* */
> - assert(sys->output == MAKEFOURCC('Y','V','1','2'));
> + LPDIRECT3DSURFACE9 d3d = src->p_sys->surface;
> + D3DSURFACE_DESC desc;
> + if (FAILED( IDirect3DSurface9_GetDesc(d3d, &desc) ))
> + return;
>
> /* */
> D3DLOCKED_RECT lock;
> if (FAILED(IDirect3DSurface9_LockRect(d3d, &lock, NULL,
> D3DLOCK_READONLY))) { - msg_Err(va, "Failed to lock surface");
> - return VLC_EGENERIC;
> + msg_Err(p_filter, "Failed to lock surface");
> + return;
> }
>
> - if (sys->render == MAKEFOURCC('Y','V','1','2') ||
> - sys->render == MAKEFOURCC('I','M','C','3')) {
> - bool imc3 = sys->render == MAKEFOURCC('I','M','C','3');
> + if (desc.Format == MAKEFOURCC('Y','V','1','2') ||
> + desc.Format == MAKEFOURCC('I','M','C','3')) {
> + bool imc3 = desc.Format == MAKEFOURCC('I','M','C','3');
> size_t chroma_pitch = imc3 ? lock.Pitch : (lock.Pitch / 2);
>
> size_t pitch[3] = {
> @@ -443,9 +503,9 @@ static int Extract(vlc_va_t *va, picture_t *picture,
> uint8_t *data)
>
> uint8_t *plane[3] = {
> (uint8_t*)lock.pBits,
> - (uint8_t*)lock.pBits + pitch[0] * sys->surface_height,
> - (uint8_t*)lock.pBits + pitch[0] * sys->surface_height
> - + pitch[1] * sys->surface_height / 2,
> + (uint8_t*)lock.pBits + pitch[0] * src->format.i_height,
> + (uint8_t*)lock.pBits + pitch[0] * src->format.i_height
> + + pitch[1] * src->format.i_height / 2,
> };
>
> if (imc3) {
> @@ -453,24 +513,49 @@ static int Extract(vlc_va_t *va, picture_t *picture,
> uint8_t *data) plane[1] = plane[2];
> plane[2] = V;
> }
> - CopyFromYv12(picture, plane, pitch, sys->width, sys->height,
> - &sys->surface_cache);
> - } else {
> - assert(sys->render == MAKEFOURCC('N','V','1','2'));
> + CopyFromYv12(dst, plane, pitch, src->format.i_width,
> src->format.i_visible_height, + p_copy_cache);
> + } else if (desc.Format == MAKEFOURCC('N','V','1','2')) {
> uint8_t *plane[2] = {
> lock.pBits,
> - (uint8_t*)lock.pBits + lock.Pitch * sys->surface_height
> + (uint8_t*)lock.pBits + lock.Pitch * src->format.i_height
> };
> size_t pitch[2] = {
> lock.Pitch,
> lock.Pitch,
> };
> - CopyFromNv12(picture, plane, pitch, sys->width, sys->height,
> - &sys->surface_cache);
> + CopyFromNv12(dst, plane, pitch, src->format.i_width,
> src->format.i_visible_height, + p_copy_cache);
> + } else {
> + msg_Err(p_filter, "Unsupported DXA9 conversion to 0x%08X",
> desc.Format); }
>
> /* */
> IDirect3DSurface9_UnlockRect(d3d);
> +}
> +
> +static int Extract(vlc_va_t *va, picture_t *picture, uint8_t *data)
> +{
> + vlc_va_sys_t *sys = va->sys;
> + LPDIRECT3DSURFACE9 d3d = (LPDIRECT3DSURFACE9)(uintptr_t)data;
> + picture_sys_t *p_sys = picture->p_sys;
> + LPDIRECT3DSURFACE9 output = p_sys->surface;
> +
> + assert(d3d != output);
> +#ifndef NDEBUG
> + LPDIRECT3DDEVICE9 srcDevice, dstDevice;
> + IDirect3DSurface9_GetDevice(d3d, &srcDevice);
> + IDirect3DSurface9_GetDevice(output, &dstDevice);
> + assert(srcDevice == dstDevice);
> +#endif
> +
> + HRESULT hr;
> + hr = IDirect3DDevice9_StretchRect( sys->d3ddev, d3d, NULL, output,
> NULL, D3DTEXF_NONE);
> + if (FAILED(hr)) {
> + msg_Err(va, "Failed to copy the hw surface to the decoder surface
> (hr=0x%0lx)", hr );
> + return VLC_EGENERIC;
> + }
> +
OK but I would expect Extract to be unnecessary when doing direct rendering
(the one in VDPAU is pretty much doing nothing).
> return VLC_SUCCESS;
> }
>
> @@ -1078,21 +1163,37 @@ static int DxResetVideoDecoder(vlc_va_t *va)
> return VLC_EGENERIC;
> }
>
> -static void DxCreateVideoConversion(vlc_va_sys_t *va)
> +VIDEO_FILTER_WRAPPER (DXA9_I420)
> +
> +static int OpenFilter( vlc_object_t *obj )
> {
> - switch (va->render) {
> - case MAKEFOURCC('N','V','1','2'):
> - case MAKEFOURCC('I','M','C','3'):
> - va->output = MAKEFOURCC('Y','V','1','2');
> - break;
> - default:
> - va->output = va->render;
> - break;
> - }
> - CopyInitCache(&va->surface_cache, va->surface_width);
> + filter_t *p_filter = (filter_t *)obj;
> + if ( p_filter->fmt_in.video.i_height !=
> p_filter->fmt_out.video.i_height + ||
> p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width ) +
> return VLC_EGENERIC;
> +
> + if ( p_filter->fmt_out.video.i_chroma != VLC_CODEC_I420
> + && p_filter->fmt_out.video.i_chroma != VLC_CODEC_YV12
> + && p_filter->fmt_out.video.i_chroma != VLC_CODEC_NV12 )
> + return VLC_EGENERIC;
> +
> + if ( p_filter->fmt_in.video.i_chroma == VLC_CODEC_D3D9_OPAQUE )
> + p_filter->pf_video_filter = DXA9_I420_Filter;
> + else
> + return VLC_EGENERIC;
> +
> + copy_cache_t *p_copy_cache = calloc(1, sizeof(*p_copy_cache));
> + CopyInitCache(p_copy_cache, p_filter->fmt_in.video.i_width );
> + p_filter->p_sys = (filter_sys_t*) p_copy_cache;
> +
> + return VLC_SUCCESS;
> }
>
> -static void DxDestroyVideoConversion(vlc_va_sys_t *va)
> +static void CloseFilter( vlc_object_t *obj )
> {
> - CopyCleanCache(&va->surface_cache);
> + filter_t *p_filter = (filter_t *)obj;
> + copy_cache_t *p_copy_cache = (copy_cache_t*) p_filter->p_sys;
> + CopyCleanCache(p_copy_cache);
> + free( p_copy_cache );
> + p_filter->p_sys = NULL;
> }
> diff --git a/modules/codec/avcodec/va.h b/modules/codec/avcodec/va.h
> index a5ffeae..cbf43e9 100644
> --- a/modules/codec/avcodec/va.h
> +++ b/modules/codec/avcodec/va.h
> @@ -37,7 +37,7 @@ struct vlc_va_t {
> const char *description;
> int pix_fmt;
>
> - int (*setup)(vlc_va_t *, AVCodecContext *, vlc_fourcc_t *output);
> + int (*setup)(vlc_va_t *, AVCodecContext *, vlc_fourcc_t *output,
> picture_t *p_test_output);
> int (*get)(vlc_va_t *, picture_t *pic, uint8_t
> **data);
> void (*release)(void *pic, uint8_t *surface);
> int (*extract)(vlc_va_t *, picture_t *pic, uint8_t *data);
> @@ -58,9 +58,9 @@ vlc_va_t *vlc_va_New(vlc_object_t *obj, AVCodecContext *,
> const es_format_t *fmt * @return VLC_SUCCESS on success, otherwise an error
> code.
> */
> static inline int vlc_va_Setup(vlc_va_t *va, AVCodecContext *avctx,
> - vlc_fourcc_t *output)
> + vlc_fourcc_t *output, picture_t
> *p_test_output) {
> - return va->setup(va, avctx, output);
> + return va->setup(va, avctx, output, p_test_output);
> }
>
> /**
> diff --git a/modules/codec/avcodec/video.c b/modules/codec/avcodec/video.c
> index 71f8fbf..30fd185 100644
> --- a/modules/codec/avcodec/video.c
> +++ b/modules/codec/avcodec/video.c
> @@ -1063,10 +1063,19 @@ static int lavc_GetFrame(struct AVCodecContext *ctx,
> AVFrame *frame, int flags) frame->opaque = NULL;
>
> wait_mt(sys);
> + /* The semaphore protects updates to fmt_out */
> + pic = ffmpeg_NewPictBuf(dec, ctx);
You need to set i_chroma first.
> + if (pic == NULL)
> + {
> + post_mt(sys);
> + return -1;
> + }
> +
> if (sys->p_va != NULL)
> { /* TODO: Move this to get_format(). We are screwed if it fails
> here. */
> - if (vlc_va_Setup(sys->p_va, ctx,
> &dec->fmt_out.video.i_chroma))
> + if (vlc_va_Setup(sys->p_va, ctx,
> &dec->fmt_out.video.i_chroma, pic)) {
And you can't change i_chroma after you've allocated a picture.
> + picture_Release(pic);
> post_mt(sys);
> msg_Err(dec, "hardware acceleration setup failed");
> return -1;
> @@ -1074,15 +1083,11 @@ static int lavc_GetFrame(struct AVCodecContext *ctx,
> AVFrame *frame, int flags) }
> else if (!sys->b_direct_rendering)
> {
> + picture_Release(pic);
> post_mt(sys);
> return avcodec_default_get_buffer2(ctx, frame, flags);
> }
> -
> - /* The semaphore protects updates to fmt_out */
> - pic = ffmpeg_NewPictBuf(dec, ctx);
> post_mt(sys);
> - if (pic == NULL)
> - return -1;
>
> if (sys->p_va != NULL)
> return lavc_va_GetFrame(ctx, frame, pic);
> @@ -1142,7 +1147,7 @@ static enum PixelFormat ffmpeg_GetFormat(
> AVCodecContext *p_context, /* We try to call vlc_va_Setup when possible to
> detect errors when * possible (later is too late) */
> if( p_context->width > 0 && p_context->height > 0
> - && vlc_va_Setup( p_va, p_context, &p_dec->fmt_out.video.i_chroma )
> ) + && vlc_va_Setup( p_va, p_context,
> &p_dec->fmt_out.video.i_chroma, NULL ) ) {
> msg_Err( p_dec, "acceleration setup failure" );
> break;
> diff --git a/modules/video_chroma/copy.c b/modules/video_chroma/copy.c
> index cc98c92..2ddf9ef 100644
> --- a/modules/video_chroma/copy.c
> +++ b/modules/video_chroma/copy.c
> @@ -326,11 +326,23 @@ static void SSE_CopyFromNv12(picture_t *dst,
> src[0], src_pitch[0],
> cache->buffer, cache->size,
> width, height, cpu);
> - SSE_SplitPlanes(dst->p[2].p_pixels, dst->p[2].i_pitch,
> - dst->p[1].p_pixels, dst->p[1].i_pitch,
> - src[1], src_pitch[1],
> - cache->buffer, cache->size,
> - (width+1)/2, (height+1)/2, cpu);
> + if( dst->format.i_chroma == VLC_CODEC_YV12 )
> + SSE_SplitPlanes(dst->p[2].p_pixels, dst->p[2].i_pitch,
> + dst->p[1].p_pixels, dst->p[1].i_pitch,
> + src[1], src_pitch[1],
> + cache->buffer, cache->size,
> + (width+1)/2, (height+1)/2, cpu);
> + else if( dst->format.i_chroma == VLC_CODEC_I420 )
> + SSE_SplitPlanes(dst->p[1].p_pixels, dst->p[1].i_pitch,
> + dst->p[2].p_pixels, dst->p[2].i_pitch,
> + src[1], src_pitch[1],
> + cache->buffer, cache->size,
> + (width+1)/2, (height+1)/2, cpu);
> + else if( dst->format.i_chroma == VLC_CODEC_NV12 )
> + SSE_CopyPlane(dst->p[1].p_pixels, dst->p[1].i_pitch,
> + src[1], src_pitch[1],
> + cache->buffer, cache->size,
> + width, height/2, cpu);
> asm volatile ("emms");
> }
>
> @@ -394,10 +406,20 @@ void CopyFromNv12(picture_t *dst, uint8_t *src[2],
> size_t src_pitch[2], CopyPlane(dst->p[0].p_pixels, dst->p[0].i_pitch,
> src[0], src_pitch[0],
> width, height);
> - SplitPlanes(dst->p[2].p_pixels, dst->p[2].i_pitch,
> - dst->p[1].p_pixels, dst->p[1].i_pitch,
> - src[1], src_pitch[1],
> - width/2, height/2);
> + if( dst->format.i_chroma == VLC_CODEC_YV12 )
> + SplitPlanes(dst->p[2].p_pixels, dst->p[2].i_pitch,
> + dst->p[1].p_pixels, dst->p[1].i_pitch,
> + src[1], src_pitch[1],
> + width/2, height/2);
> + else if( dst->format.i_chroma = VLC_CODEC_I420 )
> + SplitPlanes(dst->p[1].p_pixels, dst->p[1].i_pitch,
> + dst->p[2].p_pixels, dst->p[2].i_pitch,
> + src[1], src_pitch[1],
> + width/2, height/2);
> + else if( dst->format.i_chroma == VLC_CODEC_NV12 )
> + CopyPlane(dst->p[1].p_pixels, dst->p[1].i_pitch,
> + src[1], src_pitch[1],
> + width, height/2);
> }
>
> void CopyFromYv12(picture_t *dst, uint8_t *src[3], size_t src_pitch[3],
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 1f02494..a2489ab 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -290,6 +290,8 @@ SOURCES_libvlc_linux = \
> $(NULL)
>
> SOURCES_libvlc_win32 = \
> + win32/direct3d9_pool.c \
> + win32/direct3d9_pool.h \
> win32/dirs.c \
> win32/error.c \
> win32/filesystem.c \
> diff --git a/src/misc/fourcc.c b/src/misc/fourcc.c
> index 3b5c9b3..032a12c 100644
> --- a/src/misc/fourcc.c
> +++ b/src/misc/fourcc.c
> @@ -2193,6 +2193,9 @@ static const struct
> { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE, },
> FAKE_FMT() },
>
> + { { VLC_CODEC_D3D9_OPAQUE, },
> + FAKE_FMT() },
> +
> { { 0 }, FAKE_FMT() }
> };
>
> diff --git a/src/video_output/vout_wrapper.c
> b/src/video_output/vout_wrapper.c index d172bcc..3442bd0 100644
> --- a/src/video_output/vout_wrapper.c
> +++ b/src/video_output/vout_wrapper.c
> @@ -35,6 +35,9 @@
> #include <assert.h>
> #include "vout_internal.h"
> #include "display.h"
> +#if defined(_WIN32)
> +#include "../win32/direct3d9_pool.h"
> +#endif
>
> /**************************************************************************
> *** * Local prototypes
> @@ -139,10 +142,16 @@ int vout_InitWrapper(vout_thread_t *vout)
> sys->decoder_pool = display_pool;
> sys->display_pool = display_pool;
> } else if (!sys->decoder_pool) {
> - sys->decoder_pool =
> - picture_pool_NewFromFormat(&source,
> - __MAX(VOUT_MAX_PICTURES,
> - reserved_picture +
> decoder_picture - DISPLAY_PICTURE_COUNT)); + const unsigned
> decoder_pool_size = __MAX(VOUT_MAX_PICTURES, +
> reserved_picture + decoder_picture -
> DISPLAY_PICTURE_COUNT); +#if defined(_WIN32)
> + if (source.i_chroma == VLC_CODEC_D3D9_OPAQUE)
> + /* FIXME dirty hack for now */
> + sys->decoder_pool = AllocPoolD3D9( VLC_OBJECT(vout), &source,
> decoder_pool_size ); + else
> +#endif
> + sys->decoder_pool = picture_pool_NewFromFormat( &source,
> decoder_pool_size ); +
> if (!sys->decoder_pool)
> return VLC_EGENERIC;
> if (allow_dr) {
> diff --git a/src/win32/direct3d9_pool.c b/src/win32/direct3d9_pool.c
> new file mode 100644
> index 0000000..fd40a0a
> --- /dev/null
> +++ b/src/win32/direct3d9_pool.c
> @@ -0,0 +1,248 @@
> +/**************************************************************************
> *** + * direct3d9_pool.c: Windows Direct3D9 picture pool creation
> +
> ***************************************************************************
> ** + * Copyright (C) 2015 VLC authors and VideoLAN
> + *$Id$
> + *
> + * Authors: Steve Lhomme <robux4 at gmail.com>,
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License as published by
> + * the Free Software Foundation; either version 2.1 of the License, or + *
> (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program; if not, write to the Free Software Foundation,
> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. +
> ***************************************************************************
> **/ +
> +#ifdef HAVE_CONFIG_H
> +# include "config.h"
> +#endif
> +
> +#include "direct3d9_pool.h"
> +
> +static void DestroyPicture(picture_t *picture);
> +
> +struct picture_sys_t {
> + LPDIRECT3DSURFACE9 surface;
> + HINSTANCE hd3d9_dll;
> +};
> +
> +picture_pool_t *AllocPoolD3D9( vlc_object_t *va, const video_format_t *fmt,
> unsigned pool_size ) +{
> + HINSTANCE hd3d9_dll = NULL;
> + LPDIRECT3D9 d3dobj = NULL;
> + LPDIRECT3DDEVICE9 d3ddev = NULL;
> +
> + if (fmt->i_chroma != VLC_CODEC_D3D9_OPAQUE)
> + return picture_pool_NewFromFormat(fmt, pool_size);
> +
> + OSVERSIONINFO winVer;
> + winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
> + if(GetVersionEx(&winVer) && winVer.dwMajorVersion < 6) {
> + msg_Warn(va, "windows version not compatible with D3D9");
> + goto error;
> + }
> +
> + hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
> + if (!hd3d9_dll) {
> + msg_Warn(va, "cannot load d3d9.dll, aborting");
> + goto error;
> + }
> +
> + LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion);
> + OurDirect3DCreate9 = (void *)GetProcAddress(hd3d9_dll,
> "Direct3DCreate9"); + if (!OurDirect3DCreate9) {
> + msg_Err(va, "Cannot locate reference to Direct3DCreate9 ABI in
> DLL"); + goto error;
> + }
> +
> + /* Create the D3D object. */
> + d3dobj = OurDirect3DCreate9(D3D_SDK_VERSION);
> + if (!d3dobj) {
> + msg_Err(va, "Could not create Direct3D9 instance.");
> + goto error;
> + }
> +
> + /*
> + ** Get device capabilities
> + */
> + D3DCAPS9 d3dcaps;
> + ZeroMemory(&d3dcaps, sizeof(d3dcaps));
> + HRESULT hr = IDirect3D9_GetDeviceCaps(d3dobj, D3DADAPTER_DEFAULT,
> D3DDEVTYPE_HAL, &d3dcaps); + if (FAILED(hr)) {
> + msg_Err(va, "Could not read adapter capabilities. (hr=0x%0lx)", hr);
> + goto error;
> + }
> +
> + /* TODO: need to test device capabilities and select the right render
> function */ + if (!(d3dcaps.DevCaps2 &
> D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES) || +
> !(d3dcaps.TextureFilterCaps & (D3DPTFILTERCAPS_MAGFLINEAR)) || +
> !(d3dcaps.TextureFilterCaps & (D3DPTFILTERCAPS_MINFLINEAR))) { +
> msg_Err(va, "Device does not support stretching from textures."); +
> goto error;
> + }
> +
> + // Create the D3DDevice
> + UINT AdapterToUse = D3DADAPTER_DEFAULT;
> + D3DDEVTYPE DeviceType = D3DDEVTYPE_HAL;
> +
> +#ifndef NDEBUG
> + // Look for 'NVIDIA PerfHUD' adapter
> + // If it is present, override default settings
> + for (UINT Adapter=0; Adapter< IDirect3D9_GetAdapterCount(d3dobj);
> ++Adapter) { + D3DADAPTER_IDENTIFIER9 Identifier;
> + HRESULT Res =
> IDirect3D9_GetAdapterIdentifier(d3dobj,Adapter,0,&Identifier); + if
> (SUCCEEDED(Res) && strstr(Identifier.Description,"PerfHUD") != 0) { +
> AdapterToUse = Adapter;
> + DeviceType = D3DDEVTYPE_REF;
> + break;
> + }
> + }
> +#endif
> +
> + /* */
> + D3DADAPTER_IDENTIFIER9 d3dai;
> + if (FAILED(IDirect3D9_GetAdapterIdentifier(d3dobj,AdapterToUse,0,
> &d3dai))) { + msg_Warn(va, "IDirect3D9_GetAdapterIdentifier
> failed");
> + } else {
> + msg_Dbg(va, "Direct3d9 Device: %s %lu %lu %lu", d3dai.Description,
> + d3dai.VendorId, d3dai.DeviceId, d3dai.Revision );
> + }
> +
> + /*
> + ** Get the current desktop display mode, so we can set up a back
> + ** buffer of the same format
> + */
> + D3DDISPLAYMODE d3ddm;
> + hr = IDirect3D9_GetAdapterDisplayMode(d3dobj, D3DADAPTER_DEFAULT,
> &d3ddm); + if (FAILED(hr)) {
> + msg_Err(va, "Could not read adapter display mode. (hr=0x%0lx)", hr);
> + goto error;
> + }
> +
> + D3DPRESENT_PARAMETERS d3dpp;
> + ZeroMemory(&d3dpp, sizeof(d3dpp));
> + d3dpp.Flags = D3DPRESENTFLAG_VIDEO;
> + d3dpp.Windowed = TRUE;
> + d3dpp.hDeviceWindow = NULL;
> + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
> + d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
> + d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
> + d3dpp.BackBufferCount = 0; /* FIXME may not be right */
> + d3dpp.BackBufferFormat = d3ddm.Format;
> + d3dpp.BackBufferWidth = __MAX((unsigned
> int)GetSystemMetrics(SM_CXVIRTUALSCREEN), +
> d3ddm.Width);
> + d3dpp.BackBufferHeight = __MAX((unsigned
> int)GetSystemMetrics(SM_CYVIRTUALSCREEN), +
> d3ddm.Height);
> + d3dpp.EnableAutoDepthStencil = FALSE;
> +
> + hr = IDirect3D9_CreateDevice(d3dobj, AdapterToUse,
> + DeviceType, GetDesktopWindow(),
> +
> D3DCREATE_SOFTWARE_VERTEXPROCESSING| +
> D3DCREATE_MULTITHREADED, +
> &d3dpp, &d3ddev);
> + if (FAILED(hr)) {
> + msg_Err(va, "Could not create the D3D9 device! (hr=0x%0lx)", hr);
> + goto error;
> + }
> +
> + picture_pool_t *result = AllocPoolD3D9Ex(va, d3ddev, fmt, pool_size);
> + if (!result)
> + goto error;
> +
> + FreeLibrary(hd3d9_dll);
> + return result;
> +
> +error:
> + if (d3ddev)
> + IDirect3DDevice9_Release(d3ddev);
> + if (d3dobj)
> + IDirect3D9_Release(d3dobj);
> + if (hd3d9_dll)
> + FreeLibrary(hd3d9_dll);
> + return NULL;
> +}
> +
> +picture_pool_t *AllocPoolD3D9Ex(vlc_object_t *va, LPDIRECT3DDEVICE9 d3ddev,
> + const video_format_t *fmt, unsigned
> pool_size) +{
> + picture_t** pictures = NULL;
> + unsigned picture_count = 0;
> +
> + pictures = calloc(pool_size, sizeof(*pictures));
> + if (!pictures)
> + goto error;
> + for (picture_count = 0; picture_count < pool_size; ++picture_count)
> + {
> + picture_sys_t *picsys = malloc(sizeof(*picsys));
> + if (unlikely(picsys == NULL))
> + goto error;
> +
> + HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(d3ddev,
> + fmt->i_width,
> + fmt->i_height,
> +
> MAKEFOURCC('N','V','1','2'), /* FIXME d3ddm.Format, */ +
> D3DPOOL_DEFAULT, +
> &picsys->surface, +
> NULL);
> + if (FAILED(hr)) {
> + msg_Err(va, "Failed to allocate surface %d (hr=0x%0lx)",
> picture_count, hr); + goto error;
> + }
> +
> + picture_resource_t resource = {
> + .p_sys = picsys,
> + .pf_destroy = DestroyPicture,
> + };
> +
> + picture_t *picture = picture_NewFromResource(fmt, &resource);
> + if (unlikely(picture == NULL)) {
> + free(picsys);
> + goto error;
> + }
> +
> + pictures[picture_count] = picture;
> + /* each picture_t holds a ref to the device and release it on
> Destroy */ + IDirect3DDevice9_AddRef(d3ddev);
> + /* each picture_t holds a ref to the DLL */
> + picsys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
> + }
> +
> + /* release the system resources, they will be free'd with the pool */
> + IDirect3DDevice9_Release(d3ddev);
> +
> + picture_pool_configuration_t pool_cfg;
> + memset(&pool_cfg, 0, sizeof(pool_cfg));
> + pool_cfg.picture_count = pool_size;
> + pool_cfg.picture = pictures;
> +
> + return picture_pool_NewExtended( &pool_cfg );
> +
> +error:
> + if (pictures) {
> + for (unsigned i=0;i<picture_count; ++i)
> + DestroyPicture(pictures[i]);
> + free(pictures);
> + }
> + return NULL;
> +}
> +
> +static void DestroyPicture(picture_t *picture)
> +{
> + LPDIRECT3DDEVICE9 d3ddev;
> + if (!FAILED(IDirect3DSurface9_GetDevice(picture->p_sys->surface,
> &d3ddev))) + IDirect3DDevice9_Release(d3ddev);
> +
> + IDirect3DSurface9_Release(picture->p_sys->surface);
> +
> + FreeLibrary(picture->p_sys->hd3d9_dll);
> +
> + free(picture->p_sys);
> +}
> diff --git a/src/win32/direct3d9_pool.h b/src/win32/direct3d9_pool.h
> new file mode 100644
> index 0000000..b1b9fe2
> --- /dev/null
> +++ b/src/win32/direct3d9_pool.h
> @@ -0,0 +1,39 @@
> +/**************************************************************************
> *** + * direct3d9_pool.c: Windows Direct3D9 picture pool creation
> +
> ***************************************************************************
> ** + * Copyright (C) 2015 VLC authors and VideoLAN
> + *$Id$
> + *
> + * Authors: Steve Lhomme <robux4 at gmail.com>,
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License as published by
> + * the Free Software Foundation; either version 2.1 of the License, or + *
> (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program; if not, write to the Free Software Foundation,
> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. +
> ***************************************************************************
> **/ +
> +#ifndef WIN32_DIRECT3D9_POOL_H_
> +#define WIN32_DIRECT3D9_POOL_H_
> +
> +#include <windows.h>
> +#include <d3d9.h>
> +
> +#include <vlc_common.h>
> +#include <vlc_picture_pool.h>
> +
> +#define VA_DXVA2_MAX_SURFACE_COUNT (64)
> +
> +picture_pool_t *AllocPoolD3D9( vlc_object_t *obj, const video_format_t
> *p_fmt, unsigned pool_size ); +picture_pool_t *AllocPoolD3D9Ex(vlc_object_t
> *obj, LPDIRECT3DDEVICE9 d3ddev, + const
> video_format_t *fmt, unsigned pool_size); +
> +#endif /* WIN32_DIRECT3D9_POOL_H_ */
--
Rémi Denis-Courmont
http://www.remlab.net/
More information about the vlc-devel
mailing list