[vlc-devel] [PATCH 3/3] direct3d11: add support for I420 picture pools

Steve Lhomme robux4 at videolabs.io
Sun Jul 2 11:38:09 CEST 2017


Allow direct rendering to I420 textures directly in the GPU memory mapped to CPU
The texture to display is copied to the staging texture just before rendering.
---
 modules/video_chroma/d3d11_fmt.h        |   1 +
 modules/video_chroma/dxgi_fmt.c         |   1 +
 modules/video_output/win32/direct3d11.c | 204 +++++++++++++++++++++++++++-----
 3 files changed, 178 insertions(+), 28 deletions(-)

diff --git a/modules/video_chroma/d3d11_fmt.h b/modules/video_chroma/d3d11_fmt.h
index 3b461cb1bf..f1651ffa73 100644
--- a/modules/video_chroma/d3d11_fmt.h
+++ b/modules/video_chroma/d3d11_fmt.h
@@ -47,6 +47,7 @@ struct picture_sys_t
     ID3D11ShaderResourceView      *resourceView[D3D11_MAX_SHADER_VIEW];
     DXGI_FORMAT                   decoderFormat;
     DXGI_FORMAT                   formatTexture;
+    bool                          mapped;
 };
 
 #include "../codec/avcodec/va_surface.h"
diff --git a/modules/video_chroma/dxgi_fmt.c b/modules/video_chroma/dxgi_fmt.c
index bc462919ca..bacf329cac 100644
--- a/modules/video_chroma/dxgi_fmt.c
+++ b/modules/video_chroma/dxgi_fmt.c
@@ -80,6 +80,7 @@ static const d3d_format_t d3d_formats[] = {
     { "Y410",     DXGI_FORMAT_Y410,           VLC_CODEC_I444,         10, 1, 1, { DXGI_FORMAT_R10G10B10A2_UNORM } },
     { "NV11",     DXGI_FORMAT_NV11,           VLC_CODEC_I411,          8, 4, 1, { DXGI_FORMAT_R8_UNORM,           DXGI_FORMAT_R8G8_UNORM} },
 #endif
+    { "I420",     DXGI_FORMAT_UNKNOWN,        VLC_CODEC_I420,          8, 2, 2, { DXGI_FORMAT_R8_UNORM,      DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM } },
     { "R8G8B8A8", DXGI_FORMAT_R8G8B8A8_UNORM, VLC_CODEC_RGBA,          8, 1, 1, { DXGI_FORMAT_R8G8B8A8_UNORM } },
     { "VA_RGBA",  DXGI_FORMAT_R8G8B8A8_UNORM, VLC_CODEC_D3D11_OPAQUE,  8, 1, 1, { DXGI_FORMAT_R8G8B8A8_UNORM } },
     { "B8G8R8A8", DXGI_FORMAT_B8G8R8A8_UNORM, VLC_CODEC_BGRA,          8, 1, 1, { DXGI_FORMAT_B8G8R8A8_UNORM } },
diff --git a/modules/video_output/win32/direct3d11.c b/modules/video_output/win32/direct3d11.c
index 9b6a879ef5..c0bd23ed03 100644
--- a/modules/video_output/win32/direct3d11.c
+++ b/modules/video_output/win32/direct3d11.c
@@ -99,6 +99,7 @@ typedef struct
     UINT                       PSConstantsCount;
     ID3D11PixelShader         *d3dpixelShader;
     D3D11_VIEWPORT            cropViewport;
+    const vlc_chroma_description_t *p_chroma_sampling;
     unsigned int              i_x_offset;
     unsigned int              i_y_offset;
     unsigned int              i_width;
@@ -405,6 +406,47 @@ static void Direct3D11UnmapPoolTexture(picture_t *picture)
     ID3D11DeviceContext_Unmap(p_sys->context, p_sys->resource[KNOWN_DXGI_INDEX], 0);
 }
 
+static int Direct3D11LockDirectTexture(picture_t *picture)
+{
+    picture_sys_t *p_sys = picture->p_sys;
+    D3D11_MAPPED_SUBRESOURCE mappedResource;
+    HRESULT hr;
+    D3D11_TEXTURE2D_DESC texDesc;
+    int i;
+
+    for (i = 0; i < picture->i_planes; i++) {
+        hr = ID3D11DeviceContext_Map(p_sys->context, p_sys->resource[i], 0, D3D11_MAP_WRITE, 0, &mappedResource);
+        if( FAILED(hr) )
+            break;
+        ID3D11Texture2D_GetDesc(p_sys->texture[i], &texDesc);
+        picture->p[i].p_pixels = mappedResource.pData;
+        picture->p[i].i_pitch  = mappedResource.RowPitch;
+        picture->p[i].i_lines  = texDesc.Height;
+        assert(picture->p[i].i_visible_pitch <= picture->p[i].i_pitch);
+        assert(picture->p[i].i_visible_lines <= picture->p[i].i_lines);
+    }
+
+    if( FAILED(hr) )
+    {
+        while (i-- > 0)
+            ID3D11DeviceContext_Unmap(p_sys->context, p_sys->resource[i+1], 0);
+        return VLC_EGENERIC;
+    }
+
+    p_sys->mapped = true;
+    return VLC_SUCCESS;
+}
+
+static void Direct3D11UnlockDirectTexture(picture_t *picture)
+{
+    picture_sys_t *p_sys = picture->p_sys;
+    if (p_sys->mapped) {
+        for (int i = 0; i < picture->i_planes; i++)
+            ID3D11DeviceContext_Unmap(p_sys->context, p_sys->resource[i], 0);
+        p_sys->mapped = false;
+    }
+}
+
 #if !VLC_WINSTORE_APP
 static int OpenHwnd(vout_display_t *vd)
 {
@@ -580,7 +622,8 @@ static int AllocateTextures(vout_display_t *vd, const d3d_format_t *cfg,
                             bool pool_type_display)
 {
     vout_display_sys_t *sys = vd->sys;
-    int plane;
+    plane_t planes[PICTURE_PLANE_MAX];
+    int plane, plane_count;
     HRESULT hr;
     ID3D11Texture2D *slicedTexture = NULL;
     D3D11_TEXTURE2D_DESC texDesc;
@@ -588,31 +631,93 @@ static int AllocateTextures(vout_display_t *vd, const d3d_format_t *cfg,
     texDesc.MipLevels = 1;
     texDesc.SampleDesc.Count = 1;
     texDesc.MiscFlags = 0; //D3D11_RESOURCE_MISC_SHARED;
-    texDesc.Format = cfg->formatTexture;
-    texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
-    if (is_d3d11_opaque(fmt->i_chroma)) {
-        texDesc.BindFlags |= D3D11_BIND_DECODER;
-        texDesc.Usage = D3D11_USAGE_DEFAULT;
-        texDesc.CPUAccessFlags = 0;
+    const vlc_chroma_description_t *p_chroma_desc = vlc_fourcc_GetChromaDescription( fmt->i_chroma );
+    if( !p_chroma_desc )
+        return VLC_EGENERIC;
+
+    if (cfg->formatTexture == DXGI_FORMAT_UNKNOWN) {
+        int i_width_aligned  = fmt->i_width;
+        int i_height_aligned = fmt->i_height;
+        if (p_chroma_desc->plane_count == 0)
+        {
+            msg_Dbg(vd, "failed to get the pixel format planes for %4.4s", (char *)&fmt->i_chroma);
+            return VLC_EGENERIC;
+        }
+        assert(p_chroma_desc->plane_count <= D3D11_MAX_SHADER_VIEW);
+        plane_count = p_chroma_desc->plane_count;
+
+        texDesc.Format = cfg->resourceFormat[0];
+        assert(cfg->resourceFormat[1] == cfg->resourceFormat[0]);
+        assert(cfg->resourceFormat[2] == cfg->resourceFormat[0]);
+
+        if (pool_type_display) {
+            texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+            texDesc.Usage = D3D11_USAGE_DEFAULT;
+        } else {
+            texDesc.Usage = D3D11_USAGE_STAGING;
+            texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+
+            /* align on 16 pixel boundaries */
+            i_width_aligned  = ( i_width_aligned  + 15 ) & ~15;
+            i_height_aligned = ( i_width_aligned + 15 ) & ~15;
+        }
+
+        for( int i = 0; i < plane_count; i++ )
+        {
+            plane_t *p = &planes[i];
+
+            p->i_lines         = i_height_aligned * p_chroma_desc->p[i].h.num / p_chroma_desc->p[i].h.den;
+            p->i_visible_lines = fmt->i_visible_height * p_chroma_desc->p[i].h.num / p_chroma_desc->p[i].h.den;
+            p->i_pitch         = i_width_aligned * p_chroma_desc->p[i].w.num / p_chroma_desc->p[i].w.den * p_chroma_desc->pixel_size;
+            p->i_visible_pitch = fmt->i_visible_width * p_chroma_desc->p[i].w.num / p_chroma_desc->p[i].w.den * p_chroma_desc->pixel_size;
+            p->i_pixel_pitch   = p_chroma_desc->pixel_size;
+
+            if (!pool_type_display) {
+                assert( (p->i_pitch % 16) == 0 );
+            }
+        }
+
+        texDesc.ArraySize = 1;
     } else {
-        texDesc.Usage = D3D11_USAGE_DYNAMIC;
-        texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
-    }
-    texDesc.ArraySize = pool_size;
-    texDesc.Height = fmt->i_height;
-    texDesc.Width = fmt->i_width;
+        plane_count = 1;
+        texDesc.Format = cfg->formatTexture;
+        texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+        if (is_d3d11_opaque(fmt->i_chroma)) {
+            texDesc.BindFlags |= D3D11_BIND_DECODER;
+            texDesc.Usage = D3D11_USAGE_DEFAULT;
+            texDesc.CPUAccessFlags = 0;
+        } else {
+            texDesc.Usage = D3D11_USAGE_DYNAMIC;
+            texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+        }
+        texDesc.ArraySize = pool_size;
+        texDesc.Height = fmt->i_height;
+        texDesc.Width = fmt->i_width;
 
-    hr = ID3D11Device_CreateTexture2D( sys->d3ddevice, &texDesc, NULL, &slicedTexture );
-    if (FAILED(hr)) {
-        msg_Err(vd, "CreateTexture2D failed for the %d pool. (hr=0x%0lx)", pool_size, hr);
-        goto error;
+        hr = ID3D11Device_CreateTexture2D( sys->d3ddevice, &texDesc, NULL, &slicedTexture );
+        if (FAILED(hr)) {
+            msg_Err(vd, "CreateTexture2D failed for the %d pool. (hr=0x%0lx)", pool_size, hr);
+            goto error;
+        }
     }
 
     for (unsigned picture_count = 0; picture_count < pool_size; picture_count++) {
-        textures[picture_count * D3D11_MAX_SHADER_VIEW] = slicedTexture;
-        ID3D11Texture2D_AddRef(slicedTexture);
-
-        for (plane = 1; plane < D3D11_MAX_SHADER_VIEW; plane++) {
+        for (plane = 0; plane < plane_count; plane++)
+        {
+            if (slicedTexture) {
+                textures[picture_count * D3D11_MAX_SHADER_VIEW + plane] = slicedTexture;
+                ID3D11Texture2D_AddRef(slicedTexture);
+            } else {
+                texDesc.Height = planes[plane].i_lines;
+                texDesc.Width = planes[plane].i_pitch;
+                hr = ID3D11Device_CreateTexture2D( sys->d3ddevice, &texDesc, NULL, &textures[picture_count * D3D11_MAX_SHADER_VIEW + plane] );
+                if (FAILED(hr)) {
+                    msg_Err(vd, "CreateTexture2D failed for the %d pool. (hr=0x%0lx)", pool_size, hr);
+                    goto error;
+                }
+            }
+        }
+        for (; plane < D3D11_MAX_SHADER_VIEW; plane++) {
             if (!cfg->resourceFormat[plane])
                 textures[picture_count * D3D11_MAX_SHADER_VIEW + plane] = NULL;
             else
@@ -623,10 +728,8 @@ 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) && cfg->formatTexture != DXGI_FORMAT_UNKNOWN) {
         D3D11_MAPPED_SUBRESOURCE mappedResource;
-        const vlc_chroma_description_t *p_chroma_desc = vlc_fourcc_GetChromaDescription( fmt->i_chroma );
-
         hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource*)textures[0], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
         if( FAILED(hr) ) {
             msg_Err(vd, "The texture cannot be mapped. (hr=0x%lX)", hr);
@@ -677,7 +780,7 @@ static picture_pool_t *Pool(vout_display_t *vd, unsigned pool_size)
     }
 
     if (AllocateTextures(vd, sys->picQuadConfig, &surface_fmt, pool_size, textures,
-                         true))
+                         sys->picQuadConfig->formatTexture != DXGI_FORMAT_UNKNOWN))
         goto error;
 
     if (!vd->info.is_slow) {
@@ -740,6 +843,8 @@ static picture_pool_t *Pool(vout_display_t *vd, unsigned pool_size)
         sys->picQuad.i_width    = staging_fmt.i_width;
         sys->picQuad.i_height   = staging_fmt.i_height;
 
+        sys->picQuad.p_chroma_sampling = vlc_fourcc_GetChromaDescription( staging_fmt.i_chroma );
+
         for (unsigned plane = 0; plane < D3D11_MAX_SHADER_VIEW; plane++)
             sys->stagingSys.texture[plane] = textures[plane];
 
@@ -768,7 +873,12 @@ static picture_pool_t *Pool(vout_display_t *vd, unsigned pool_size)
         return NULL;
     }
 
-    if (vd->info.is_slow) {
+    if (sys->picQuadConfig->formatTexture == DXGI_FORMAT_UNKNOWN)
+    {
+        pool_cfg.lock = Direct3D11LockDirectTexture;
+        pool_cfg.unlock = Direct3D11UnlockDirectTexture;
+    }
+    else if (vd->info.is_slow) {
         pool_cfg.lock          = Direct3D11MapPoolTexture;
         //pool_cfg.unlock        = Direct3D11UnmapPoolTexture;
     }
@@ -1106,7 +1216,38 @@ static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpic
         WaitForSingleObjectEx( sys->context_lock, INFINITE, FALSE );
 #endif
     picture_sys_t *p_sys = ActivePictureSys(picture);
-    if (!is_d3d11_opaque(picture->format.i_chroma) || sys->legacy_shader) {
+    if (picture->p_sys->formatTexture == DXGI_FORMAT_UNKNOWN)
+    {
+        Direct3D11UnlockDirectTexture(picture);
+        for (int plane = 0; plane < D3D11_MAX_SHADER_VIEW; plane++)
+        {
+            if (!p_sys->resource[plane])
+                continue;
+
+            unsigned int width  = sys->picQuad.i_width;
+            unsigned int height = sys->picQuad.i_height;
+            unsigned int x_offset = picture->format.i_x_offset;
+            unsigned int y_offset = picture->format.i_y_offset;
+            x_offset = x_offset * sys->picQuad.p_chroma_sampling->p[plane].h.num / sys->picQuad.p_chroma_sampling->p[plane].h.den;
+            y_offset = y_offset * sys->picQuad.p_chroma_sampling->p[plane].w.num / sys->picQuad.p_chroma_sampling->p[plane].w.den;
+            width  = width * sys->picQuad.p_chroma_sampling->p[plane].h.num / sys->picQuad.p_chroma_sampling->p[plane].h.den;
+            height = height * sys->picQuad.p_chroma_sampling->p[plane].w.num / sys->picQuad.p_chroma_sampling->p[plane].w.den;
+
+            D3D11_BOX box = {
+                .top = y_offset,
+                .bottom = y_offset + height,
+                .left = x_offset,
+                .right = x_offset + width,
+                .back = 1,
+            };
+            ID3D11DeviceContext_CopySubresourceRegion(sys->d3dcontext,
+                                                      sys->stagingSys.resource[plane],
+                                                      0, 0, 0, 0,
+                                                      p_sys->resource[plane],
+                                                      0, &box);
+        }
+    }
+    else if (!is_d3d11_opaque(picture->format.i_chroma) || sys->legacy_shader) {
         D3D11_TEXTURE2D_DESC texDesc;
         if (!is_d3d11_opaque(picture->format.i_chroma))
             Direct3D11UnmapPoolTexture(picture);
@@ -1745,6 +1886,13 @@ static HRESULT CompilePixelShader(vout_display_t *vd, const d3d_format_t *format
         psz_sampler =
                 "sample = shaderTexture[0].Sample(SampleType, In.Texture);";
         break;
+    case DXGI_FORMAT_UNKNOWN:
+        psz_sampler =
+               "sample.x  = shaderTexture[0].Sample(SampleType, In.Texture).x;\
+                sample.y  = shaderTexture[1].Sample(SampleType, In.Texture).x;\
+                sample.z  = shaderTexture[2].Sample(SampleType, In.Texture).x;\
+                sample.a  = 1;";
+        break;
     default:
         vlc_assert_unreachable();
     }
@@ -2108,7 +2256,7 @@ static int Direct3D11CreateResources(vout_display_t *vd, video_format_t *fmt)
     }
 
     sys->legacy_shader = !CanUseTextureArray(vd);
-    vd->info.is_slow = !is_d3d11_opaque(fmt->i_chroma);
+    vd->info.is_slow = !is_d3d11_opaque(fmt->i_chroma) && sys->picQuadConfig->formatTexture != DXGI_FORMAT_UNKNOWN;
 
     hr = CompilePixelShader(vd, sys->picQuadConfig, fmt->transfer, fmt->b_color_range_full, &sys->picQuadPixelShader);
     if (FAILED(hr))
-- 
2.13.0



More information about the vlc-devel mailing list