[vlc-devel] [PATCH] DXVA2: output opaque GPU surfaces that can be used directly by D3D9

Steve Lhomme robUx4 at videolabs.io
Tue Apr 28 13:21:42 CEST 2015


also provide a filter to convert these surfaces to regular planes for use with other renderers

--
not sure this can be merged before the blending issue with D3D11 or DirectDraw is fixed

the additions in CopyFromNv12() are needed because it assumed the output was
only YV12 before but with DDraw it's I420 and with D3D11 it can be NV12 directly or I420
---
 include/vlc_fourcc.h                 |   3 +
 modules/codec/Makefile.am            |   3 +-
 modules/codec/avcodec/dxva2.c        | 153 ++++++++++++++-------
 modules/codec/avcodec/va.c           |   2 +-
 modules/video_chroma/copy.c          |  40 ++++--
 modules/video_output/Makefile.am     |   3 +-
 modules/video_output/msw/direct3d9.c | 153 ++++++++++-----------
 src/misc/fourcc.c                    |   2 +-
 src/video_output/vout_wrapper.c      |  15 ++-
 src/win32/direct3d9_pool.c           | 248 +++++++++++++++++++++++++++++++++++
 src/win32/direct3d9_pool.h           |  39 ++++++
 11 files changed, 519 insertions(+), 142 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/Makefile.am b/modules/codec/Makefile.am
index e5f7ed8..3efdcac 100644
--- a/modules/codec/Makefile.am
+++ b/modules/codec/Makefile.am
@@ -342,7 +342,8 @@ endif
 
 libdxva2_plugin_la_SOURCES = \
 	video_chroma/copy.c video_chroma/copy.h \
-	codec/avcodec/dxva2.c codec/h264_nal.c codec/h264_nal.h
+	codec/avcodec/dxva2.c codec/h264_nal.c codec/h264_nal.h \
+	../src/win32/direct3d9_pool.c ../src/win32/direct3d9_pool.h
 libdxva2_plugin_la_LIBADD = -lole32 -lshlwapi -luuid
 if HAVE_AVCODEC_DXVA2
 codec_LTLIBRARIES += libdxva2_plugin.la
diff --git a/modules/codec/avcodec/dxva2.c b/modules/codec/avcodec/dxva2.c
index 81c2a9c..1dd7a7f 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
@@ -57,12 +58,19 @@ static int Open(vlc_va_t *, AVCodecContext *, enum PixelFormat,
                 const es_format_t *, picture_sys_t *p_sys);
 static void Close(vlc_va_t *, AVCodecContext *);
 
+static int  OpenConverter( vlc_object_t * );
+static void CloseConverter( 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_("DXA9 to I420,YV12,NV12") )
+        set_capability( "video filter2", 10 )
+        set_callbacks( OpenConverter, CloseConverter )
 vlc_module_end()
 
 #include <windows.h>
@@ -367,6 +375,11 @@ struct vlc_va_sys_t
     LPDIRECT3DSURFACE9 hw_surface[VA_DXVA2_MAX_SURFACE_COUNT];
 };
 
+struct picture_sys_t
+{
+    LPDIRECT3DSURFACE9 surface;
+};
+
 /* */
 static int D3dCreateDevice(vlc_va_t *);
 static void D3dDestroyDevice(vlc_va_sys_t *);
@@ -386,9 +399,6 @@ static int DxResetVideoDecoder(vlc_va_t *);
 
 static bool profile_supported(const dxva2_mode_t *mode, const es_format_t *fmt);
 
-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)
 {
@@ -399,7 +409,6 @@ static int Setup(vlc_va_t *va, AVCodecContext *avctx, vlc_fourcc_t *chroma)
         goto ok;
 
     /* */
-    DxDestroyVideoConversion(sys);
     DxDestroyVideoDecoder(sys);
 
     avctx->hwaccel_context = NULL;
@@ -422,38 +431,32 @@ static int Setup(vlc_va_t *va, AVCodecContext *avctx, vlc_fourcc_t *chroma)
     sys->hw.surface = sys->hw_surface;
 
     /* */
-    DxCreateVideoConversion(sys);
-
-    /* */
 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;
 }
 
-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;
-
-    if (!sys->surface_cache.buffer)
-        return VLC_EGENERIC;
+    copy_cache_t *p_copy_cache = (copy_cache_t*) p_filter->p_sys;
 
-    /* */
-    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] = {
@@ -464,9 +467,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) {
@@ -474,24 +477,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;
+    }
+
     return VLC_SUCCESS;
 }
 
@@ -558,7 +586,6 @@ static void Close(vlc_va_t *va, AVCodecContext *ctx)
     vlc_va_sys_t *sys = va->sys;
 
     (void) ctx;
-    DxDestroyVideoConversion(sys);
     DxDestroyVideoDecoder(sys);
     DxDestroyVideoService(sys);
     D3dDestroyDeviceManager(sys);
@@ -604,12 +631,20 @@ static int Open(vlc_va_t *va, AVCodecContext *ctx, enum PixelFormat pix_fmt,
     }
     msg_Dbg(va, "DLLs loaded");
 
-    /* */
-    if (D3dCreateDevice(va)) {
-        msg_Err(va, "Failed to create Direct3D device");
-        goto error;
+    sys->d3ddev = NULL;
+    if (p_sys!=NULL)
+        IDirect3DSurface9_GetDevice(p_sys->surface, &sys->d3ddev);
+
+    if (sys->d3ddev) {
+        msg_Dbg(va, "Reusing D3D9 device");
+    } else {
+        /* */
+        if (D3dCreateDevice(va)) {
+            msg_Err(va, "Failed to create Direct3D device");
+            goto error;
+        }
+        msg_Dbg(va, "D3dCreateDevice succeed");
     }
-    msg_Dbg(va, "D3dCreateDevice succeed");
 
     if (D3dCreateDeviceManager(va)) {
         msg_Err(va, "D3dCreateDeviceManager failed");
@@ -1149,21 +1184,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 OpenConverter( 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 CloseConverter( 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.c b/modules/codec/avcodec/va.c
index 413574d..f9e94e6 100644
--- a/modules/codec/avcodec/va.c
+++ b/modules/codec/avcodec/va.c
@@ -41,7 +41,7 @@ vlc_fourcc_t vlc_va_GetChroma(enum PixelFormat hwfmt, enum PixelFormat swfmt)
             return VLC_CODEC_YV12;
 
         case AV_PIX_FMT_DXVA2_VLD:
-            return VLC_CODEC_YV12;
+            return VLC_CODEC_D3D9_OPAQUE;
 #if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(53, 14, 0))
         case AV_PIX_FMT_VDA_VLD:
             return VLC_CODEC_UYVY;
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/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index 5aaeafb..371ee34 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -173,7 +173,8 @@ libdirect3d9_plugin_la_SOURCES = video_output/msw/direct3d9.c \
 	video_output/msw/common.c video_output/msw/common.h \
 	video_output/msw/events.c video_output/msw/events.h \
 	video_output/msw/builtin_shaders.h \
-	video_output/msw/win32touch.c video_output/msw/win32touch.h
+	video_output/msw/win32touch.c video_output/msw/win32touch.h \
+	../src/win32/direct3d9_pool.c ../src/win32/direct3d9_pool.h
 libdirect3d9_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) \
 	-DMODULE_NAME_IS_direct3d9
 libdirect3d9_plugin_la_LIBADD = -lgdi32 -lole32 -luuid
diff --git a/modules/video_output/msw/direct3d9.c b/modules/video_output/msw/direct3d9.c
index db96275..41609d8 100644
--- a/modules/video_output/msw/direct3d9.c
+++ b/modules/video_output/msw/direct3d9.c
@@ -50,6 +50,7 @@
 
 #include "common.h"
 #include "builtin_shaders.h"
+#include "../../src/win32/direct3d9_pool.h"
 
 /*****************************************************************************
  * Module descriptor
@@ -202,10 +203,10 @@ static int Open(vlc_object_t *object)
 
     /* */
     vout_display_info_t info = vd->info;
-    info.is_slow = true;
+    info.is_slow = fmt.i_chroma != VLC_CODEC_D3D9_OPAQUE;
     info.has_double_click = true;
     info.has_hide_mouse = false;
-    info.has_pictures_invalid = true;
+    info.has_pictures_invalid = fmt.i_chroma != VLC_CODEC_D3D9_OPAQUE;
     info.has_event_thread = true;
     if (var_InheritBool(vd, "direct3d9-hw-blending") &&
         sys->d3dregion_format != D3DFMT_UNKNOWN &&
@@ -274,7 +275,8 @@ static void Close(vlc_object_t *object)
 /* */
 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
 {
-    VLC_UNUSED(count);
+    if ( vd->sys->pool == NULL )
+        vd->sys->pool = AllocPoolD3D9Ex( VLC_OBJECT(vd), vd->sys->d3ddev, &vd->fmt, count );
     return vd->sys->pool;
 }
 
@@ -283,21 +285,18 @@ static void Direct3D9UnlockSurface(picture_t *);
 
 static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
 {
+    VLC_UNUSED(subpicture);
+
     vout_display_sys_t *sys = vd->sys;
     LPDIRECT3DSURFACE9 surface = picture->p_sys->surface;
-#if 0
-    picture_Release(picture);
-    VLC_UNUSED(subpicture);
-#else
+
     /* FIXME it is a bit ugly, we need the surface to be unlocked for
      * rendering.
      *  The clean way would be to release the picture (and ensure that
      * the vout doesn't keep a reference). But because of the vout
      * wrapper, we can't */
-
-    Direct3D9UnlockSurface(picture);
-    VLC_UNUSED(subpicture);
-#endif
+    if ( picture->format.i_chroma != VLC_CODEC_D3D9_OPAQUE )
+        Direct3D9UnlockSurface(picture);
 
     /* check if device is still available */
     HRESULT hr = IDirect3DDevice9_TestCooperativeLevel(sys->d3ddev);
@@ -335,6 +334,8 @@ static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpic
 
 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
 {
+    VLC_UNUSED(subpicture);
+
     vout_display_sys_t *sys = vd->sys;
     LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
 
@@ -354,14 +355,10 @@ static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpic
         msg_Dbg(vd, "Failed IDirect3DDevice9_Present: 0x%0lx", hr);
     }
 
-#if 0
-    VLC_UNUSED(picture);
-    VLC_UNUSED(subpicture);
-#else
     /* XXX See Prepare() */
-    Direct3D9LockSurface(picture);
+    if ( picture->format.i_chroma != VLC_CODEC_D3D9_OPAQUE )
+        Direct3D9LockSurface(picture);
     picture_Release(picture);
-#endif
     if (subpicture)
         subpicture_Delete(subpicture);
 
@@ -860,6 +857,7 @@ static const d3d_format_t d3d_formats[] = {
     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_I420,  0,0,0 },
     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_J420,  0,0,0 },
     { "NV12",       MAKEFOURCC('N','V','1','2'),    VLC_CODEC_NV12,  0,0,0 },
+    { "DXVANV12",   MAKEFOURCC('N','V','1','2'),    VLC_CODEC_D3D9_OPAQUE,  0,0,0 },
     { "UYVY",       D3DFMT_UYVY,    VLC_CODEC_UYVY,  0,0,0 },
     { "YUY2",       D3DFMT_YUY2,    VLC_CODEC_YUYV,  0,0,0 },
     { "X8R8G8B8",   D3DFMT_X8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
@@ -879,8 +877,11 @@ static const d3d_format_t *Direct3DFindFormat(vout_display_t *vd, vlc_fourcc_t c
 
     for (unsigned pass = 0; pass < 2; pass++) {
         const vlc_fourcc_t *list;
+        const vlc_fourcc_t dxva_chroma[] = {chroma, 0};
 
-        if (pass == 0 && sys->allow_hw_yuv && vlc_fourcc_IsYUV(chroma))
+        if (pass == 0 && chroma == VLC_CODEC_D3D9_OPAQUE)
+            list = dxva_chroma;
+        else if (pass == 0 && sys->allow_hw_yuv && vlc_fourcc_IsYUV(chroma))
             list = vlc_fourcc_GetYUVFallback(chroma);
         else if (pass == 1)
             list = vlc_fourcc_GetRGBFallback(chroma);
@@ -968,64 +969,66 @@ static int Direct3D9CreatePool(vout_display_t *vd, video_format_t *fmt)
 
     /* We create one picture.
      * It is useless to create more as we can't be used for direct rendering */
-
-    /* Create a surface */
-    LPDIRECT3DSURFACE9 surface;
-    HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(d3ddev,
-                                                              fmt->i_visible_width,
-                                                              fmt->i_visible_height,
-                                                              d3dfmt->format,
-                                                              D3DPOOL_DEFAULT,
-                                                              &surface,
-                                                              NULL);
-    if (FAILED(hr)) {
-        msg_Err(vd, "Failed to create picture surface. (hr=0x%lx)", hr);
-        return VLC_EGENERIC;
-    }
+    if ( fmt->i_chroma != VLC_CODEC_D3D9_OPAQUE )
+    {
+        /* Create a surface */
+        LPDIRECT3DSURFACE9 surface;
+        HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(d3ddev,
+                                                                  fmt->i_visible_width,
+                                                                  fmt->i_visible_height,
+                                                                  d3dfmt->format,
+                                                                  D3DPOOL_DEFAULT,
+                                                                  &surface,
+                                                                  NULL);
+        if (FAILED(hr)) {
+            msg_Err(vd, "Failed to create picture surface. (hr=0x%lx)", hr);
+            return VLC_EGENERIC;
+        }
 
 #ifndef NDEBUG
-    msg_Dbg(vd, "Direct3D created offscreen surface: %ix%i",
-                fmt->i_visible_width, fmt->i_visible_height);
+        msg_Dbg(vd, "Direct3D created offscreen surface: %ix%i",
+                    fmt->i_visible_width, fmt->i_visible_height);
 #endif
 
-    /* fill surface with black color */
-    IDirect3DDevice9_ColorFill(d3ddev, surface, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0));
+        /* fill surface with black color */
+        IDirect3DDevice9_ColorFill(d3ddev, surface, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0));
 
-    /* Create the associated picture */
-    picture_sys_t *picsys = malloc(sizeof(*picsys));
-    if (unlikely(picsys == NULL)) {
-        IDirect3DSurface9_Release(surface);
-        return VLC_ENOMEM;
-    }
-    picsys->surface = surface;
-    picsys->fallback = NULL;
-
-    picture_resource_t resource = { .p_sys = picsys };
-    for (int i = 0; i < PICTURE_PLANE_MAX; i++)
-        resource.p[i].i_lines = fmt->i_visible_height / (i > 0 ? 2 : 1);
-
-    picture_t *picture = picture_NewFromResource(fmt, &resource);
-    if (!picture) {
-        msg_Err(vd, "Failed to create a picture from resources.");
-        IDirect3DSurface9_Release(surface);
-        free(picsys);
-        return VLC_ENOMEM;
-    }
-    sys->picsys = picsys;
-
-    /* Wrap it into a picture pool */
-    picture_pool_configuration_t pool_cfg;
-    memset(&pool_cfg, 0, sizeof(pool_cfg));
-    pool_cfg.picture_count = 1;
-    pool_cfg.picture       = &picture;
-    pool_cfg.lock          = Direct3D9LockSurface;
-    pool_cfg.unlock        = Direct3D9UnlockSurface;
-
-    sys->pool = picture_pool_NewExtended(&pool_cfg);
-    if (!sys->pool) {
-        picture_Release(picture);
-        IDirect3DSurface9_Release(surface);
-        return VLC_ENOMEM;
+        /* Create the associated picture */
+        picture_sys_t *picsys = malloc(sizeof(*picsys));
+        if (unlikely(picsys == NULL)) {
+            IDirect3DSurface9_Release(surface);
+            return VLC_ENOMEM;
+        }
+        picsys->surface = surface;
+        picsys->fallback = NULL;
+
+        picture_resource_t resource = { .p_sys = picsys };
+        for (int i = 0; i < PICTURE_PLANE_MAX; i++)
+            resource.p[i].i_lines = fmt->i_visible_height / (i > 0 ? 2 : 1);
+
+        picture_t *picture = picture_NewFromResource(fmt, &resource);
+        if (!picture) {
+            msg_Err(vd, "Failed to create a picture from resources.");
+            IDirect3DSurface9_Release(surface);
+            free(picsys);
+            return VLC_ENOMEM;
+        }
+        sys->picsys = picsys;
+
+        /* Wrap it into a picture pool */
+        picture_pool_configuration_t pool_cfg;
+        memset(&pool_cfg, 0, sizeof(pool_cfg));
+        pool_cfg.picture_count = 1;
+        pool_cfg.picture       = &picture;
+        pool_cfg.lock      = Direct3D9LockSurface;
+        pool_cfg.unlock    = Direct3D9UnlockSurface;
+
+        sys->pool = picture_pool_NewExtended(&pool_cfg);
+        if (!sys->pool) {
+            picture_Release(picture);
+            IDirect3DSurface9_Release(surface);
+            return VLC_ENOMEM;
+        }
     }
     return VLC_SUCCESS;
 }
@@ -1038,9 +1041,11 @@ static void Direct3D9DestroyPool(vout_display_t *vd)
 
     if (sys->pool) {
         picture_sys_t *picsys = sys->picsys;
-        IDirect3DSurface9_Release(picsys->surface);
-        if (picsys->fallback)
-            picture_Release(picsys->fallback);
+        if ( picsys != NULL ) {
+            IDirect3DSurface9_Release(picsys->surface);
+            if (picsys->fallback)
+                picture_Release(picsys->fallback);
+        }
         picture_pool_Release(sys->pool);
     }
     sys->pool = NULL;
diff --git a/src/misc/fourcc.c b/src/misc/fourcc.c
index 3b5c9b3..108b860 100644
--- a/src/misc/fourcc.c
+++ b/src/misc/fourcc.c
@@ -2190,7 +2190,7 @@ static const struct
     { { VLC_CODEC_VDPAU_VIDEO_420, VLC_CODEC_VDPAU_VIDEO_422,
         VLC_CODEC_VDPAU_VIDEO_444, VLC_CODEC_VDPAU_OUTPUT },
                                                FAKE_FMT() },
-    { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE, },
+    { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE, 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 dbaae78..c64cc3b 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
@@ -145,10 +148,14 @@ 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)
+            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_ */
-- 
2.3.2





More information about the vlc-devel mailing list