[vlc-devel] [PATCH 10/12] direct3d11: use a video processor when the decoder format cannot be displayed

Steve Lhomme robux4 at gmail.com
Fri Apr 28 17:46:24 CEST 2017


From: Steve Lhomme <robux4 at videolabs.io>

On Windows 7 DXVA can decode to NV12 but it cannot be rendered. We use an
internal VideoProcessor in the vout to convert the NV12 to something it can
display.
The legacy shader (no Texture2DArray) is used in this case as Windows 7 doesn't
support Texture2DArray display either.
---
 modules/video_output/win32/direct3d11.c | 237 ++++++++++++++++++++++++++++++--
 1 file changed, 225 insertions(+), 12 deletions(-)

diff --git a/modules/video_output/win32/direct3d11.c b/modules/video_output/win32/direct3d11.c
index 8b24ad4870..67232fabaf 100644
--- a/modules/video_output/win32/direct3d11.c
+++ b/modules/video_output/win32/direct3d11.c
@@ -162,6 +162,18 @@ struct vout_display_sys_t
      * Uses a Texture2D with slices rather than a Texture2DArray for the decoder */
     bool                     legacy_shader;
 
+    /* processor to convert decoder textures to display textures */
+    const d3d_format_t       *decoderFormat;
+    ID3D11VideoDevice        *d3dviddev;
+    ID3D11VideoContext       *d3dvidctx;
+    ID3D11VideoProcessor     *decoderProcessor;
+    ID3D11VideoProcessorEnumerator *processorEnumerator;
+    union {
+        ID3D11Texture2D            *decoderProcTexture;
+        ID3D11Resource             *decoderProcResource;
+    };
+    ID3D11VideoProcessorOutputView *decoderProcOutput;
+
     // SPU
     vlc_fourcc_t             pSubpictureChromas[2];
     ID3D11PixelShader        *pSPUPixelShader;
@@ -679,7 +691,7 @@ static int AllocateTextures(vout_display_t *vd, const d3d_format_t *cfg,
         }
     }
 
-    if (!is_d3d11_opaque(fmt->i_chroma)) {
+    if (!is_d3d11_opaque(fmt->i_chroma) && !processor_input) {
         D3D11_MAPPED_SUBRESOURCE mappedResource;
         const vlc_chroma_description_t *p_chroma_desc = vlc_fourcc_GetChromaDescription( fmt->i_chroma );
 
@@ -729,8 +741,8 @@ static picture_pool_t *Pool(vout_display_t *vd, unsigned pool_size)
         surface_fmt.i_height = (surface_fmt.i_height + 0x7F) & ~0x7F;
     }
 
-    if (AllocateTextures(vd, sys->picQuadConfig, &surface_fmt, pool_size, textures,
-                         true, false))
+    if (AllocateTextures(vd, sys->decoderFormat, &surface_fmt, pool_size, textures,
+                         sys->decoderFormat == sys->picQuadConfig, false))
         goto error;
 
     if (vd->info.is_slow) {
@@ -794,8 +806,9 @@ static picture_pool_t *Pool(vout_display_t *vd, unsigned pool_size)
         /* we need a staging texture */
         video_format_t staging_fmt;
         video_format_Copy(&staging_fmt, &surface_fmt);
+        staging_fmt.i_chroma = sys->picQuadConfig->fourcc;
         if (AllocateTextures(vd, sys->picQuadConfig, &staging_fmt, 1, textures, true,
-                             false))
+                             sys->decoderFormat != sys->picQuadConfig))
             goto error;
 
         sys->picQuad.i_x_offset = 0;
@@ -808,6 +821,25 @@ static picture_pool_t *Pool(vout_display_t *vd, unsigned pool_size)
 
         if (AllocateShaderView(vd, sys->picQuadConfig, 0, &sys->stagingSys))
             goto error;
+
+        if (sys->decoderProcessor)
+        {
+            D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outDesc = {
+                .ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D,
+                .Texture2D.MipSlice = 0,
+            };
+
+            HRESULT hr = ID3D11VideoDevice_CreateVideoProcessorOutputView(sys->d3dviddev,
+                                                                 sys->stagingSys.resource[KNOWN_DXGI_INDEX],
+                                                                 sys->processorEnumerator,
+                                                                 &outDesc,
+                                                                 &sys->decoderProcOutput);
+            if (FAILED(hr))
+            {
+                msg_Dbg(vd,"Failed to create processor output. (hr=0x%lX)", hr);
+                goto error;
+            }
+        }
     } else
 #endif
     {
@@ -1176,6 +1208,46 @@ static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpic
     if( sys->context_lock != INVALID_HANDLE_VALUE )
         WaitForSingleObjectEx( sys->context_lock, INFINITE, FALSE );
 #endif
+    if (sys->decoderProcessor)
+    {
+        picture_sys_t *p_sys = picture->p_sys;
+        HRESULT hr;
+        if (!p_sys->processorInput)
+        {
+            D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inDesc = {
+                .FourCC = 0,
+                .ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D,
+                .Texture2D.MipSlice = 0,
+                .Texture2D.ArraySlice = p_sys->slice_index,
+            };
+
+            hr = ID3D11VideoDevice_CreateVideoProcessorInputView(sys->d3dviddev,
+                                                                 p_sys->resource[KNOWN_DXGI_INDEX],
+                                                                 sys->processorEnumerator,
+                                                                 &inDesc,
+                                                                 &p_sys->processorInput);
+            if (FAILED(hr))
+            {
+                msg_Err(vd,"Failed to create processor input for slice %d. (hr=0x%lX)", p_sys->slice_index, hr);
+                return;
+            }
+        }
+
+        D3D11_VIDEO_PROCESSOR_STREAM stream = {
+            .Enable = TRUE,
+            .pInputSurface = p_sys->processorInput,
+        };
+
+        hr = ID3D11VideoContext_VideoProcessorBlt(sys->d3dvidctx, sys->decoderProcessor,
+                                                  sys->decoderProcOutput,
+                                                  0, 1, &stream);
+        if (FAILED(hr))
+        {
+            msg_Err(vd,"Failed to process the decoder texture. (hr=0x%lX)", hr);
+            return;
+        }
+    }
+    else
     if (!is_d3d11_opaque(picture->format.i_chroma) || sys->legacy_shader) {
         picture_sys_t *p_sys = picture->p_sys;
         D3D11_TEXTURE2D_DESC texDesc;
@@ -1473,6 +1545,90 @@ done:
         IDXGISwapChain3_Release(dxgiswapChain3);
 }
 
+static void FindProcessor(vout_display_t *vd, const d3d_format_t *src,
+                          const d3d_format_t *dst)
+{
+    vout_display_sys_t *sys = vd->sys;
+    const video_format_t *fmt = &vd->source;
+    HRESULT hr;
+    UINT supportFlags;
+    ID3D11VideoDevice *d3dviddev = NULL;
+    ID3D11VideoContext *d3dvidctx = NULL;
+    ID3D11VideoProcessorEnumerator *processorEnumerator = NULL;
+    ID3D11VideoProcessor *videoProcessor = NULL;
+
+    hr = ID3D11Device_QueryInterface(sys->d3ddevice, &IID_ID3D11VideoDevice, (void **)&d3dviddev);
+    if (FAILED(hr)) {
+       msg_Err(vd, "Could not Query ID3D11VideoDevice Interface. (hr=0x%lX)", hr);
+       goto error;
+    }
+
+    hr = ID3D11DeviceContext_QueryInterface(sys->d3dcontext, &IID_ID3D11VideoContext, (void **)&d3dvidctx);
+    if (FAILED(hr)) {
+       msg_Err(vd, "Could not Query ID3D11VideoDevice Interface from the picture. (hr=0x%lX)", hr);
+       goto error;
+    }
+
+    D3D11_VIDEO_PROCESSOR_CONTENT_DESC processorDesc = {
+        .InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE,
+        .InputFrameRate = {
+            .Numerator   = fmt->i_frame_rate_base ? fmt->i_frame_rate : 0,
+            .Denominator = fmt->i_frame_rate_base,
+        },
+        .InputWidth   = fmt->i_width,
+        .InputHeight  = fmt->i_height,
+        .OutputWidth  = fmt->i_width,
+        .OutputHeight = fmt->i_height,
+        .OutputFrameRate = {
+            .Numerator   = fmt->i_frame_rate_base ? fmt->i_frame_rate : 0,
+            .Denominator = fmt->i_frame_rate_base,
+        },
+        .Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL,
+    };
+    hr = ID3D11VideoDevice_CreateVideoProcessorEnumerator(d3dviddev, &processorDesc, &processorEnumerator);
+    if ( processorEnumerator == NULL )
+    {
+        msg_Dbg(vd, "Can't get a video processor for the video.");
+        goto error;
+    }
+
+    hr = ID3D11VideoProcessorEnumerator_CheckVideoProcessorFormat(processorEnumerator, src->formatTexture, &supportFlags);
+    if (!SUCCEEDED(hr) || !(supportFlags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT))
+    {
+        msg_Dbg(vd, "can't read processor support for %s", DxgiFormatToStr(src->formatTexture));
+        goto error;
+    }
+
+    hr = ID3D11VideoProcessorEnumerator_CheckVideoProcessorFormat(processorEnumerator, dst->formatTexture, &supportFlags);
+    if (!SUCCEEDED(hr) || !(supportFlags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT))
+    {
+        msg_Dbg(vd, "can't write processor support for %s", DxgiFormatToStr(dst->formatTexture));
+        goto error;
+    }
+
+    hr = ID3D11VideoDevice_CreateVideoProcessor(d3dviddev, processorEnumerator, 0, &videoProcessor);
+    if (FAILED(hr)) {
+       msg_Err(vd, "Could not create the video processor for %s to %s. (hr=0x%lX)", DxgiFormatToStr(src->formatTexture), DxgiFormatToStr(dst->formatTexture), hr);
+       goto error;
+    }
+
+    sys->d3dvidctx           = d3dvidctx;
+    sys->d3dviddev           = d3dviddev;
+    sys->decoderProcessor    = videoProcessor;
+    sys->processorEnumerator = processorEnumerator;
+    return;
+
+error:
+    if (videoProcessor)
+        ID3D11VideoProcessor_Release(videoProcessor);
+    if (processorEnumerator)
+        ID3D11VideoProcessorEnumerator_Release(processorEnumerator);
+    if (d3dviddev)
+        ID3D11VideoDevice_Release(d3dviddev);
+    if (d3dvidctx)
+        ID3D11VideoContext_Release(d3dvidctx);
+}
+
 static const d3d_format_t *GetDirectRenderingFormat(vout_display_t *vd, vlc_fourcc_t i_src_chroma)
 {
     UINT supportFlags = D3D11_FORMAT_SUPPORT_SHADER_LOAD;
@@ -1481,9 +1637,18 @@ static const d3d_format_t *GetDirectRenderingFormat(vout_display_t *vd, vlc_four
     return FindD3D11Format( vd->sys->d3ddevice, i_src_chroma, 0, is_d3d11_opaque(i_src_chroma), supportFlags );
 }
 
-static const d3d_format_t *GetDisplayFormatByDepth(vout_display_t *vd, uint8_t bit_depth)
+static const d3d_format_t *GetIntermediateDecoderFormat(vout_display_t *vd, vlc_fourcc_t i_src_chroma)
+{
+    UINT supportFlags = D3D11_FORMAT_SUPPORT_DECODER_OUTPUT | D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_INPUT;
+    return FindD3D11Format( vd->sys->d3ddevice, i_src_chroma, 0, is_d3d11_opaque(i_src_chroma), supportFlags );
+}
+
+static const d3d_format_t *GetDisplayFormatByDepth(vout_display_t *vd, uint8_t bit_depth,
+                                                   bool processor_output)
 {
     UINT supportFlags = D3D11_FORMAT_SUPPORT_SHADER_LOAD;
+    if (processor_output)
+        supportFlags |= D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_OUTPUT;
     return FindD3D11Format( vd->sys->d3ddevice, 0, bit_depth, false, supportFlags );
 }
 
@@ -1591,7 +1756,8 @@ static int Direct3D11Open(vout_display_t *vd, video_format_t *fmt)
     D3D11SetColorSpace(vd);
 
     // look for the requested pixel format first
-    sys->picQuadConfig = GetDirectRenderingFormat(vd, fmt->i_chroma);
+    sys->decoderFormat = GetDirectRenderingFormat(vd, fmt->i_chroma);
+    sys->picQuadConfig = sys->decoderFormat;
 
     // look for any pixel format that we can handle with enough pixels per channel
     if ( !sys->picQuadConfig )
@@ -1613,22 +1779,44 @@ static int Direct3D11Open(vout_display_t *vd, video_format_t *fmt)
             break;
         }
 
-        sys->picQuadConfig = GetDisplayFormatByDepth(vd, bits_per_channel);
+        sys->picQuadConfig = GetDisplayFormatByDepth(vd, bits_per_channel, is_d3d11_opaque(fmt->i_chroma));
     }
 
     // look for any pixel format that we can handle
     if ( !sys->picQuadConfig )
-        sys->picQuadConfig = GetDisplayFormatByDepth(vd, 0);
+        sys->picQuadConfig = GetDisplayFormatByDepth(vd, 0, is_d3d11_opaque(fmt->i_chroma));
+
+    if (sys->decoderFormat == NULL && is_d3d11_opaque(fmt->i_chroma)) {
+        /* we can't decode and render, try to add a processor in between */
+        /* look for the best decoder output */
+        sys->decoderFormat = GetIntermediateDecoderFormat(vd, fmt->i_chroma);
+        if (!sys->decoderFormat)
+            sys->picQuadConfig = NULL;
+        else
+        {
+            /* look for a processor to convert decoderFormat to picQuadConfig */
+            FindProcessor(vd, sys->decoderFormat, sys->picQuadConfig);
+            if (!sys->decoderProcessor)
+                sys->picQuadConfig = NULL;
+        }
+    }
+    else
+        sys->decoderFormat = sys->picQuadConfig;
 
     if ( !sys->picQuadConfig )
     {
        msg_Err(vd, "Could not get a suitable texture pixel format");
        return VLC_EGENERIC;
     }
-    msg_Dbg( vd, "Using pixel format %s for chroma %4.4s", sys->picQuadConfig->name,
+    if (sys->decoderFormat == sys->picQuadConfig)
+        msg_Dbg( vd, "Using pixel format %s for chroma %4.4s", sys->picQuadConfig->name,
                  (char *)&fmt->i_chroma );
-    fmt->i_chroma = sys->picQuadConfig->fourcc;
-    DxgiFormatMask( sys->picQuadConfig->formatTexture, fmt );
+    else
+        msg_Dbg( vd, "Using pixel format %s/%s for chroma %4.4s", sys->decoderFormat->name,
+                 sys->picQuadConfig->name, (char *)&fmt->i_chroma );
+
+    fmt->i_chroma = sys->decoderFormat->fourcc;
+    DxgiFormatMask( sys->decoderFormat->formatTexture, fmt );
 
     /* check the region pixel format */
     sys->d3dregion_format = GetBlendableFormat(vd, VLC_CODEC_RGBA);
@@ -1674,6 +1862,31 @@ static void Direct3D11Close(vout_display_t *vd)
     vout_display_sys_t *sys = vd->sys;
 
     Direct3D11DestroyResources(vd);
+    if (sys->decoderProcOutput)
+    {
+        ID3D11VideoProcessorOutputView_Release(sys->decoderProcOutput);
+        sys->decoderProcOutput = NULL;
+    }
+    if (sys->decoderProcessor)
+    {
+        ID3D11VideoProcessor_Release(sys->decoderProcessor);
+        sys->decoderProcessor = NULL;
+    }
+    if (sys->processorEnumerator)
+    {
+        ID3D11VideoProcessorEnumerator_Release(sys->processorEnumerator);
+        sys->processorEnumerator = NULL;
+    }
+    if (sys->d3dviddev)
+    {
+        ID3D11VideoDevice_Release(sys->d3dviddev);
+        sys->d3dviddev = NULL;
+    }
+    if (sys->d3dvidctx)
+    {
+        ID3D11VideoContext_Release(sys->d3dvidctx);
+        sys->d3dvidctx = NULL;
+    }
     if (sys->d3dcontext)
     {
         ID3D11DeviceContext_Flush(sys->d3dcontext);
@@ -2147,7 +2360,7 @@ static int Direct3D11CreateResources(vout_display_t *vd, video_format_t *fmt)
         ID3D11DepthStencilState_Release(pDepthStencilState);
     }
 
-    sys->legacy_shader = !CanUseTextureArray(vd);
+    sys->legacy_shader = sys->decoderFormat != sys->picQuadConfig || !CanUseTextureArray(vd);
     vd->info.is_slow = !is_d3d11_opaque(fmt->i_chroma);
 
     hr = CompilePixelShader(vd, sys->picQuadConfig, fmt->transfer, fmt->b_color_range_full, &sys->picQuadPixelShader);
-- 
2.12.1



More information about the vlc-devel mailing list