You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
673 lines
24 KiB
673 lines
24 KiB
/*****************************************************************************
|
|
* d3d11va.c: Direct3D11 Video Acceleration decoder
|
|
*****************************************************************************
|
|
* Copyright © 2009 Geoffroy Couprie
|
|
* Copyright © 2009 Laurent Aimar
|
|
* Copyright © 2015 Steve Lhomme
|
|
* Copyright © 2015 VideoLabs
|
|
*
|
|
* Authors: Geoffroy Couprie <geal@videolan.org>
|
|
* Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
|
|
* Steve Lhomme <robux4@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.
|
|
*****************************************************************************/
|
|
|
|
/**
|
|
* See https://msdn.microsoft.com/en-us/library/windows/desktop/hh162912%28v=vs.85%29.aspx
|
|
**/
|
|
|
|
#include <process.h>
|
|
#include <winapifamily.h>
|
|
#undef WINAPI_FAMILY
|
|
#define WINAPI_FAMILY WINAPI_FAMILY_DESKTOP_APP
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
|
|
#include <vlc_common.h>
|
|
#include <vlc_picture.h>
|
|
#include <vlc_plugin.h>
|
|
#include <vlc_charset.h>
|
|
#include <vlc_codec.h>
|
|
|
|
#define COBJMACROS
|
|
#include <initguid.h>
|
|
#include <d3d11.h>
|
|
#include <libavcodec/d3d11va.h>
|
|
|
|
#include "../../video_chroma/d3d11_fmt.h"
|
|
|
|
struct d3d11va_pic_context
|
|
{
|
|
struct d3d11_pic_context ctx;
|
|
struct vlc_va_surface_t *va_surface;
|
|
};
|
|
|
|
#define D3D11VA_PICCONTEXT_FROM_PICCTX(pic_ctx) \
|
|
container_of((pic_ctx), struct d3d11va_pic_context, ctx.s)
|
|
|
|
#include "directx_va.h"
|
|
|
|
static int Open(vlc_va_t *, AVCodecContext *, enum AVPixelFormat hwfmt, const AVPixFmtDescriptor *,
|
|
const es_format_t *, vlc_decoder_device *, video_format_t *, vlc_video_context **);
|
|
|
|
vlc_module_begin()
|
|
set_description(N_("Direct3D11 Video Acceleration"))
|
|
set_subcategory(SUBCAT_INPUT_VCODEC)
|
|
set_va_callback(Open, 110)
|
|
vlc_module_end()
|
|
|
|
typedef struct
|
|
{
|
|
d3d11_device_t *d3d_dev;
|
|
|
|
vlc_video_context *vctx;
|
|
|
|
const d3d_format_t *render_fmt;
|
|
|
|
/* Video decoder */
|
|
D3D11_VIDEO_DECODER_CONFIG cfg;
|
|
const directx_va_mode_t *selected_decoder;
|
|
ID3D11VideoDevice *d3ddec;
|
|
|
|
/* avcodec internals */
|
|
struct AVD3D11VAContext hw;
|
|
|
|
/* pool */
|
|
va_pool_t *va_pool;
|
|
ID3D11VideoDecoderOutputView *hw_surface[MAX_SURFACE_COUNT];
|
|
ID3D11ShaderResourceView *renderSrc[MAX_SURFACE_COUNT * DXGI_MAX_SHADER_VIEW];
|
|
} vlc_va_sys_t;
|
|
|
|
/* */
|
|
static int D3dCreateDevice(vlc_va_t *);
|
|
|
|
static int DxGetInputList(vlc_va_t *, input_list_t *);
|
|
static int DxSetupOutput(vlc_va_t *, const directx_va_mode_t *, const video_format_t *);
|
|
|
|
static int DxCreateDecoderSurfaces(vlc_va_t *, int codec_id,
|
|
const video_format_t *fmt, size_t surface_count);
|
|
static void DxDestroySurfaces(void *);
|
|
|
|
static void SetupAVCodecContext(void *opaque, AVCodecContext *avctx)
|
|
{
|
|
vlc_va_sys_t *sys = opaque;
|
|
sys->hw.cfg = &sys->cfg;
|
|
sys->hw.surface = sys->hw_surface;
|
|
sys->hw.context_mutex = sys->d3d_dev->context_mutex;
|
|
#ifndef FF_DXVA_WORKAROUND_GONE
|
|
sys->hw.workaround = sys->selected_decoder->workaround;
|
|
#endif
|
|
avctx->hwaccel_context = &sys->hw;
|
|
}
|
|
|
|
static void d3d11va_pic_context_destroy(picture_context_t *ctx)
|
|
{
|
|
struct d3d11va_pic_context *pic_ctx = D3D11VA_PICCONTEXT_FROM_PICCTX(ctx);
|
|
struct vlc_va_surface_t *va_surface = pic_ctx->va_surface;
|
|
static_assert(offsetof(struct d3d11va_pic_context, ctx.s) == 0,
|
|
"Cast assumption failure");
|
|
d3d11_pic_context_destroy(ctx);
|
|
va_surface_Release(va_surface);
|
|
}
|
|
|
|
static picture_context_t *d3d11va_pic_context_copy(picture_context_t *ctx)
|
|
{
|
|
struct d3d11va_pic_context *src_ctx = D3D11VA_PICCONTEXT_FROM_PICCTX(ctx);
|
|
struct d3d11va_pic_context *pic_ctx = malloc(sizeof(*pic_ctx));
|
|
if (unlikely(pic_ctx==NULL))
|
|
return NULL;
|
|
*pic_ctx = *src_ctx;
|
|
vlc_video_context_Hold(pic_ctx->ctx.s.vctx);
|
|
va_surface_AddRef(pic_ctx->va_surface);
|
|
for (int i=0;i<DXGI_MAX_SHADER_VIEW; i++)
|
|
{
|
|
pic_ctx->ctx.picsys.resource[i] = src_ctx->ctx.picsys.resource[i];
|
|
pic_ctx->ctx.picsys.renderSrc[i] = src_ctx->ctx.picsys.renderSrc[i];
|
|
}
|
|
AcquireD3D11PictureSys(&pic_ctx->ctx.picsys);
|
|
return &pic_ctx->ctx.s;
|
|
}
|
|
|
|
static struct d3d11va_pic_context *CreatePicContext(ID3D11Resource *p_resource,
|
|
UINT slice,
|
|
ID3D11ShaderResourceView *renderSrc[DXGI_MAX_SHADER_VIEW],
|
|
vlc_video_context *vctx)
|
|
{
|
|
struct d3d11va_pic_context *pic_ctx = calloc(1, sizeof(*pic_ctx));
|
|
if (unlikely(pic_ctx==NULL))
|
|
return NULL;
|
|
pic_ctx->ctx.s = (picture_context_t) {
|
|
d3d11va_pic_context_destroy, d3d11va_pic_context_copy,
|
|
vlc_video_context_Hold(vctx),
|
|
};
|
|
|
|
pic_ctx->ctx.picsys.slice_index = slice;
|
|
pic_ctx->ctx.picsys.sharedHandle = INVALID_HANDLE_VALUE;
|
|
for (int i=0;i<DXGI_MAX_SHADER_VIEW; i++)
|
|
{
|
|
pic_ctx->ctx.picsys.resource[i] = p_resource;
|
|
pic_ctx->ctx.picsys.renderSrc[i] = renderSrc[i];
|
|
}
|
|
AcquireD3D11PictureSys(&pic_ctx->ctx.picsys);
|
|
return pic_ctx;
|
|
}
|
|
|
|
static picture_context_t* NewSurfacePicContext(vlc_va_t *va, vlc_va_surface_t *va_surface)
|
|
{
|
|
vlc_va_sys_t *sys = va->sys;
|
|
ID3D11VideoDecoderOutputView *surface = sys->hw_surface[va_surface_GetIndex(va_surface)];
|
|
ID3D11ShaderResourceView *resourceView[DXGI_MAX_SHADER_VIEW];
|
|
|
|
D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc;
|
|
ID3D11VideoDecoderOutputView_GetDesc(surface, &viewDesc);
|
|
|
|
for (size_t i=0; i<DXGI_MAX_SHADER_VIEW; i++)
|
|
resourceView[i] = sys->renderSrc[viewDesc.Texture2D.ArraySlice*DXGI_MAX_SHADER_VIEW + i];
|
|
|
|
ID3D11Resource *p_resource;
|
|
ID3D11VideoDecoderOutputView_GetResource(surface, &p_resource);
|
|
|
|
struct d3d11va_pic_context *pic_ctx = CreatePicContext(p_resource,
|
|
viewDesc.Texture2D.ArraySlice,
|
|
resourceView, sys->vctx);
|
|
ID3D11Resource_Release(p_resource);
|
|
if (unlikely(pic_ctx==NULL))
|
|
return NULL;
|
|
pic_ctx->va_surface = va_surface;
|
|
return &pic_ctx->ctx.s;
|
|
}
|
|
|
|
static int Get(vlc_va_t *va, picture_t *pic, AVCodecContext *ctx, AVFrame *frame)
|
|
{
|
|
(void) ctx;
|
|
|
|
vlc_va_sys_t *sys = va->sys;
|
|
vlc_va_surface_t *va_surface = va_pool_Get(sys->va_pool);
|
|
if (unlikely(va_surface == NULL))
|
|
return VLC_ENOENT;
|
|
pic->context = NewSurfacePicContext(va, va_surface);
|
|
if (unlikely(pic->context == NULL))
|
|
{
|
|
va_surface_Release(va_surface);
|
|
return VLC_ENOMEM;
|
|
}
|
|
frame->data[3] = (uint8_t*)sys->hw_surface[va_surface_GetIndex(va_surface)];
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
static void Close(vlc_va_t *va, AVCodecContext* ctx)
|
|
{
|
|
vlc_va_sys_t *sys = va->sys;
|
|
|
|
if (sys->vctx)
|
|
vlc_video_context_Release(sys->vctx);
|
|
|
|
if (sys->va_pool)
|
|
va_pool_Close(sys->va_pool);
|
|
|
|
if (ctx)
|
|
ctx->hwaccel_context = NULL;
|
|
}
|
|
|
|
static const struct vlc_va_operations ops = { Get, Close, };
|
|
|
|
static int Open(vlc_va_t *va, AVCodecContext *ctx, enum AVPixelFormat hwfmt, const AVPixFmtDescriptor *desc,
|
|
const es_format_t *fmt_in, vlc_decoder_device *dec_device,
|
|
video_format_t *fmt_out, vlc_video_context **vtcx_out)
|
|
{
|
|
int err = VLC_EGENERIC;
|
|
|
|
ctx->hwaccel_context = NULL;
|
|
|
|
if ( hwfmt != AV_PIX_FMT_D3D11VA_VLD )
|
|
return VLC_EGENERIC;
|
|
|
|
d3d11_decoder_device_t *devsys = GetD3D11OpaqueDevice( dec_device );
|
|
if ( devsys == NULL )
|
|
return VLC_EGENERIC;
|
|
|
|
if (!(ID3D11Device_GetCreationFlags(devsys->d3d_dev.d3ddevice) & D3D11_CREATE_DEVICE_VIDEO_SUPPORT))
|
|
{
|
|
msg_Err(va, "Missing D3D11_CREATE_DEVICE_VIDEO_SUPPORT");
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
vlc_va_sys_t *sys = calloc(1, sizeof (*sys));
|
|
if (unlikely(sys == NULL))
|
|
return VLC_ENOMEM;
|
|
|
|
va->sys = sys;
|
|
|
|
sys->render_fmt = NULL;
|
|
sys->d3d_dev = &devsys->d3d_dev;
|
|
if (sys->d3d_dev->context_mutex == INVALID_HANDLE_VALUE)
|
|
msg_Warn(va, "No mutex found to lock the decoder");
|
|
|
|
struct va_pool_cfg pool_cfg = {
|
|
D3dCreateDevice,
|
|
DxDestroySurfaces,
|
|
DxCreateDecoderSurfaces,
|
|
SetupAVCodecContext,
|
|
sys,
|
|
};
|
|
|
|
sys->va_pool = va_pool_Create(va, &pool_cfg);
|
|
if (sys->va_pool == NULL)
|
|
{
|
|
err = VLC_EGENERIC;
|
|
goto error;
|
|
}
|
|
|
|
video_format_t final_fmt = *fmt_out;
|
|
static const directx_sys_t dx_sys = { DxGetInputList, DxSetupOutput };
|
|
sys->selected_decoder = directx_va_Setup(va, &dx_sys, ctx, desc, fmt_in, isXboxHardware(sys->d3d_dev),
|
|
&final_fmt, &sys->hw.surface_count);
|
|
if (sys->selected_decoder == NULL)
|
|
{
|
|
err = VLC_EGENERIC;
|
|
goto error;
|
|
}
|
|
|
|
final_fmt.i_chroma = sys->render_fmt->fourcc;
|
|
err = va_pool_SetupDecoder(va, sys->va_pool, ctx, &final_fmt, sys->hw.surface_count);
|
|
if (err != VLC_SUCCESS)
|
|
goto error;
|
|
|
|
msg_Info(va, "Using D3D11VA (%ls, vendor %x(%s), device %x, revision %x)",
|
|
sys->d3d_dev->adapterDesc.Description,
|
|
sys->d3d_dev->adapterDesc.VendorId, DxgiVendorStr(sys->d3d_dev->adapterDesc.VendorId),
|
|
sys->d3d_dev->adapterDesc.DeviceId, sys->d3d_dev->adapterDesc.Revision);
|
|
|
|
sys->vctx = D3D11CreateVideoContext(dec_device, sys->render_fmt->formatTexture, sys->render_fmt->alphaTexture);
|
|
if (sys->vctx == NULL)
|
|
{
|
|
msg_Dbg(va, "no video context");
|
|
err = VLC_EGENERIC;
|
|
goto error;
|
|
}
|
|
|
|
va->ops = &ops;
|
|
*fmt_out = final_fmt;
|
|
*vtcx_out = sys->vctx;
|
|
return VLC_SUCCESS;
|
|
|
|
error:
|
|
Close(va, ctx);
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* It creates a Direct3D device usable for decoding
|
|
*/
|
|
static int D3dCreateDevice(vlc_va_t *va)
|
|
{
|
|
vlc_va_sys_t *sys = va->sys;
|
|
HRESULT hr;
|
|
|
|
assert(sys->d3d_dev->d3ddevice && sys->d3d_dev->d3dcontext);
|
|
void *d3dvidctx = NULL;
|
|
hr = ID3D11DeviceContext_QueryInterface(sys->d3d_dev->d3dcontext, &IID_ID3D11VideoContext, &d3dvidctx);
|
|
if (FAILED(hr)) {
|
|
msg_Err(va, "Could not Query ID3D11VideoContext Interface. (hr=0x%lX)", hr);
|
|
return VLC_EGENERIC;
|
|
}
|
|
sys->hw.video_context = d3dvidctx;
|
|
|
|
void *d3dviddev = NULL;
|
|
hr = ID3D11Device_QueryInterface(sys->d3d_dev->d3ddevice, &IID_ID3D11VideoDevice, &d3dviddev);
|
|
if (FAILED(hr)) {
|
|
msg_Err(va, "Could not Query ID3D11VideoDevice Interface. (hr=0x%lX)", hr);
|
|
ID3D11VideoContext_Release(sys->hw.video_context);
|
|
return VLC_EGENERIC;
|
|
}
|
|
sys->d3ddec = d3dviddev;
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
static void ReleaseInputList(input_list_t *p_list)
|
|
{
|
|
free(p_list->list);
|
|
}
|
|
|
|
static int DxGetInputList(vlc_va_t *va, input_list_t *p_list)
|
|
{
|
|
vlc_va_sys_t *sys = va->sys;
|
|
HRESULT hr;
|
|
|
|
UINT input_count = ID3D11VideoDevice_GetVideoDecoderProfileCount(sys->d3ddec);
|
|
|
|
p_list->count = input_count;
|
|
p_list->list = calloc(input_count, sizeof(*p_list->list));
|
|
if (unlikely(p_list->list == NULL)) {
|
|
return VLC_ENOMEM;
|
|
}
|
|
p_list->pf_release = ReleaseInputList;
|
|
|
|
for (unsigned i = 0; i < input_count; i++) {
|
|
hr = ID3D11VideoDevice_GetVideoDecoderProfile(sys->d3ddec, i, &p_list->list[i]);
|
|
if (FAILED(hr))
|
|
{
|
|
msg_Err(va, "GetVideoDecoderProfile %d failed. (hr=0x%lX)", i, hr);
|
|
ReleaseInputList(p_list);
|
|
return VLC_EGENERIC;
|
|
}
|
|
}
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
static int DxSetupOutput(vlc_va_t *va, const directx_va_mode_t *mode, const video_format_t *fmt)
|
|
{
|
|
vlc_va_sys_t *sys = va->sys;
|
|
HRESULT hr;
|
|
|
|
#ifndef NDEBUG
|
|
BOOL bSupported = false;
|
|
for (int format = 0; format < 188; format++) {
|
|
hr = ID3D11VideoDevice_CheckVideoDecoderFormat(sys->d3ddec, mode->guid, format, &bSupported);
|
|
if (SUCCEEDED(hr) && bSupported)
|
|
msg_Dbg(va, "format %s is supported for output", DxgiFormatToStr(format));
|
|
}
|
|
#endif
|
|
|
|
if (!directx_va_canUseDecoder(va, sys->d3d_dev->adapterDesc.VendorId, sys->d3d_dev->adapterDesc.DeviceId,
|
|
mode->guid, sys->d3d_dev->WDDM.build))
|
|
{
|
|
msg_Warn(va, "GPU blocklisted for %s codec", mode->name);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
const d3d_format_t *processorInput[4];
|
|
int idx = 0;
|
|
const d3d_format_t *decoder_format;
|
|
UINT supportFlags = D3D11_FORMAT_SUPPORT_DECODER_OUTPUT;
|
|
// enough sub-sampling+bit depth, with any alpha
|
|
decoder_format = FindD3D11Format( va, sys->d3d_dev, 0, DXGI_RGB_FORMAT|DXGI_YUV_FORMAT,
|
|
mode->bit_depth, mode->log2_chroma_h+1, mode->log2_chroma_w+1, -1,
|
|
DXGI_CHROMA_GPU, supportFlags );
|
|
if (decoder_format == NULL)
|
|
// other chroma sub-sampling
|
|
decoder_format = FindD3D11Format( va, sys->d3d_dev, 0, DXGI_RGB_FORMAT|DXGI_YUV_FORMAT,
|
|
mode->bit_depth, 0, 0, 0, DXGI_CHROMA_GPU, supportFlags );
|
|
if (decoder_format == NULL && mode->bit_depth > 10)
|
|
// 10 bits instead of 8/12/14/16
|
|
decoder_format = FindD3D11Format( va, sys->d3d_dev, 0, DXGI_RGB_FORMAT|DXGI_YUV_FORMAT,
|
|
10, 0, 0, 0, DXGI_CHROMA_GPU, supportFlags );
|
|
if (decoder_format == NULL)
|
|
// any bit depth
|
|
decoder_format = FindD3D11Format( va, sys->d3d_dev, 0, DXGI_RGB_FORMAT|DXGI_YUV_FORMAT,
|
|
0, 0, 0, 0, DXGI_CHROMA_GPU, supportFlags );
|
|
if (decoder_format != NULL)
|
|
{
|
|
msg_Dbg(va, "favor decoder format %s", decoder_format->name);
|
|
processorInput[idx++] = decoder_format;
|
|
}
|
|
|
|
if (decoder_format == NULL || decoder_format->formatTexture != DXGI_FORMAT_NV12)
|
|
processorInput[idx++] = D3D11_RenderFormat(DXGI_FORMAT_NV12, DXGI_FORMAT_UNKNOWN ,true);
|
|
processorInput[idx++] = D3D11_RenderFormat(DXGI_FORMAT_420_OPAQUE, DXGI_FORMAT_UNKNOWN ,true);
|
|
processorInput[idx++] = NULL;
|
|
|
|
/* */
|
|
for (idx = 0; processorInput[idx] != NULL; ++idx)
|
|
{
|
|
BOOL is_supported = false;
|
|
hr = ID3D11VideoDevice_CheckVideoDecoderFormat(sys->d3ddec, mode->guid, processorInput[idx]->formatTexture, &is_supported);
|
|
if (SUCCEEDED(hr) && is_supported)
|
|
msg_Dbg(va, "%s output is supported for decoder %s.", DxgiFormatToStr(processorInput[idx]->formatTexture), mode->name);
|
|
else
|
|
{
|
|
msg_Dbg(va, "Can't get a decoder output format %s for decoder %s.", DxgiFormatToStr(processorInput[idx]->formatTexture), mode->name);
|
|
continue;
|
|
}
|
|
|
|
// check if we can create render texture of that format
|
|
// check the decoder can output to that format
|
|
if ( !D3D11_DeviceSupportsFormat(sys->d3d_dev, processorInput[idx]->formatTexture,
|
|
D3D11_FORMAT_SUPPORT_SHADER_LOAD) )
|
|
{
|
|
#ifndef ID3D11VideoContext_VideoProcessorBlt
|
|
msg_Dbg(va, "Format %s needs a processor but is not supported",
|
|
DxgiFormatToStr(processorInput[idx]->formatTexture));
|
|
#else
|
|
if ( !D3D11_DeviceSupportsFormat(sys->d3d_dev, processorInput[idx]->formatTexture,
|
|
D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_INPUT) )
|
|
{
|
|
msg_Dbg(va, "Format %s needs a processor but is not available",
|
|
DxgiFormatToStr(processorInput[idx]->formatTexture));
|
|
continue;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
D3D11_VIDEO_DECODER_DESC decoderDesc;
|
|
ZeroMemory(&decoderDesc, sizeof(decoderDesc));
|
|
decoderDesc.Guid = *mode->guid;
|
|
decoderDesc.SampleWidth = fmt->i_width;
|
|
decoderDesc.SampleHeight = fmt->i_height;
|
|
decoderDesc.OutputFormat = processorInput[idx]->formatTexture;
|
|
|
|
UINT cfg_count = 0;
|
|
hr = ID3D11VideoDevice_GetVideoDecoderConfigCount( sys->d3ddec, &decoderDesc, &cfg_count );
|
|
if (FAILED(hr))
|
|
{
|
|
msg_Err( va, "Failed to get configuration for decoder %s. (hr=0x%lX)", mode->name, hr );
|
|
continue;
|
|
}
|
|
if (cfg_count == 0) {
|
|
msg_Err( va, "No decoder configuration possible for %s %dx%d",
|
|
DxgiFormatToStr(decoderDesc.OutputFormat),
|
|
decoderDesc.SampleWidth, decoderDesc.SampleHeight );
|
|
continue;
|
|
}
|
|
|
|
msg_Dbg(va, "Using output format %s for decoder %s", DxgiFormatToStr(processorInput[idx]->formatTexture), mode->name);
|
|
sys->render_fmt = processorInput[idx];
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
msg_Dbg(va, "Output format from picture source not supported.");
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
/**
|
|
* It creates a Direct3D11 decoder using the given video format
|
|
*/
|
|
static int DxCreateDecoderSurfaces(vlc_va_t *va, int codec_id,
|
|
const video_format_t *fmt, size_t surface_count)
|
|
{
|
|
vlc_va_sys_t *sys = va->sys;
|
|
HRESULT hr;
|
|
|
|
d3d11_device_lock(sys->d3d_dev);
|
|
void *pv;
|
|
hr = ID3D11Device_QueryInterface( sys->d3d_dev->d3ddevice, &IID_ID3D10Multithread, &pv);
|
|
if (SUCCEEDED(hr)) {
|
|
ID3D10Multithread *pMultithread = pv;
|
|
ID3D10Multithread_SetMultithreadProtected(pMultithread, TRUE);
|
|
ID3D10Multithread_Release(pMultithread);
|
|
}
|
|
|
|
/* On the Xbox 1/S, any decoding of H264 with one dimension over 2304
|
|
* crashes totally the device */
|
|
if (codec_id == AV_CODEC_ID_H264 &&
|
|
(fmt->i_width > 2304 || fmt->i_height > 2304) &&
|
|
isXboxHardware(sys->d3d_dev))
|
|
{
|
|
msg_Warn(va, "%dx%d resolution not supported by your hardware", fmt->i_width, fmt->i_height);
|
|
d3d11_device_unlock(sys->d3d_dev);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc;
|
|
ZeroMemory(&viewDesc, sizeof(viewDesc));
|
|
viewDesc.DecodeProfile = *sys->selected_decoder->guid;
|
|
viewDesc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
|
|
|
|
D3D11_TEXTURE2D_DESC texDesc;
|
|
ZeroMemory(&texDesc, sizeof(texDesc));
|
|
texDesc.Width = fmt->i_width;
|
|
texDesc.Height = fmt->i_height;
|
|
texDesc.MipLevels = 1;
|
|
texDesc.Format = sys->render_fmt->formatTexture;
|
|
texDesc.SampleDesc.Count = 1;
|
|
texDesc.MiscFlags = 0;
|
|
texDesc.ArraySize = surface_count;
|
|
texDesc.Usage = D3D11_USAGE_DEFAULT;
|
|
texDesc.BindFlags = D3D11_BIND_DECODER;
|
|
texDesc.CPUAccessFlags = 0;
|
|
|
|
if (D3D11_DeviceSupportsFormat(sys->d3d_dev, texDesc.Format, D3D11_FORMAT_SUPPORT_SHADER_LOAD))
|
|
texDesc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
|
|
|
|
ID3D11Texture2D *p_texture;
|
|
hr = ID3D11Device_CreateTexture2D( sys->d3d_dev->d3ddevice, &texDesc, NULL, &p_texture );
|
|
if (FAILED(hr)) {
|
|
msg_Err(va, "CreateTexture2D %zu failed. (hr=0x%lX)", surface_count, hr);
|
|
d3d11_device_unlock(sys->d3d_dev);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
unsigned surface_idx;
|
|
for (surface_idx = 0; surface_idx < surface_count; surface_idx++) {
|
|
viewDesc.Texture2D.ArraySlice = surface_idx;
|
|
|
|
hr = ID3D11VideoDevice_CreateVideoDecoderOutputView( sys->d3ddec,
|
|
(ID3D11Resource*) p_texture,
|
|
&viewDesc,
|
|
&sys->hw_surface[surface_idx] );
|
|
if (FAILED(hr)) {
|
|
msg_Err(va, "CreateVideoDecoderOutputView %d failed. (hr=0x%lX)", surface_idx, hr);
|
|
ID3D11Texture2D_Release(p_texture);
|
|
d3d11_device_unlock(sys->d3d_dev);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
if (texDesc.BindFlags & D3D11_BIND_SHADER_RESOURCE)
|
|
{
|
|
ID3D11Texture2D *textures[DXGI_MAX_SHADER_VIEW] = {p_texture, p_texture, p_texture};
|
|
D3D11_AllocateResourceView(vlc_object_logger(va), sys->d3d_dev->d3ddevice, sys->render_fmt, textures, surface_idx,
|
|
&sys->renderSrc[surface_idx * DXGI_MAX_SHADER_VIEW]);
|
|
}
|
|
}
|
|
ID3D11Texture2D_Release(p_texture);
|
|
msg_Dbg(va, "ID3D11VideoDecoderOutputView succeed with %zu surfaces (%dx%d)",
|
|
surface_count, fmt->i_width, fmt->i_height);
|
|
|
|
D3D11_VIDEO_DECODER_DESC decoderDesc;
|
|
ZeroMemory(&decoderDesc, sizeof(decoderDesc));
|
|
decoderDesc.Guid = *sys->selected_decoder->guid;
|
|
decoderDesc.SampleWidth = fmt->i_width;
|
|
decoderDesc.SampleHeight = fmt->i_height;
|
|
decoderDesc.OutputFormat = sys->render_fmt->formatTexture;
|
|
|
|
UINT cfg_count;
|
|
hr = ID3D11VideoDevice_GetVideoDecoderConfigCount( sys->d3ddec, &decoderDesc, &cfg_count );
|
|
if (FAILED(hr)) {
|
|
msg_Err(va, "GetVideoDecoderConfigCount failed. (hr=0x%lX)", hr);
|
|
d3d11_device_unlock(sys->d3d_dev);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
/* List all configurations available for the decoder */
|
|
D3D11_VIDEO_DECODER_CONFIG cfg_list[cfg_count];
|
|
for (unsigned i = 0; i < cfg_count; i++) {
|
|
hr = ID3D11VideoDevice_GetVideoDecoderConfig( sys->d3ddec, &decoderDesc, i, &cfg_list[i] );
|
|
if (FAILED(hr)) {
|
|
msg_Err(va, "GetVideoDecoderConfig failed. (hr=0x%lX)", hr);
|
|
d3d11_device_unlock(sys->d3d_dev);
|
|
return VLC_EGENERIC;
|
|
}
|
|
}
|
|
|
|
msg_Dbg(va, "we got %d decoder configurations", cfg_count);
|
|
|
|
/* Select the best decoder configuration */
|
|
int cfg_score = 0;
|
|
for (unsigned i = 0; i < cfg_count; i++) {
|
|
const D3D11_VIDEO_DECODER_CONFIG *cfg = &cfg_list[i];
|
|
|
|
/* */
|
|
msg_Dbg(va, "configuration[%d] ConfigBitstreamRaw %d",
|
|
i, cfg->ConfigBitstreamRaw);
|
|
|
|
/* */
|
|
int score;
|
|
if (cfg->ConfigBitstreamRaw == 1)
|
|
score = 1;
|
|
else if (codec_id == AV_CODEC_ID_H264 && cfg->ConfigBitstreamRaw == 2)
|
|
score = 2;
|
|
else
|
|
continue;
|
|
if (IsEqualGUID(&cfg->guidConfigBitstreamEncryption, &DXVA_NoEncrypt))
|
|
score += 16;
|
|
|
|
if (cfg_score < score) {
|
|
sys->cfg = *cfg;
|
|
cfg_score = score;
|
|
}
|
|
}
|
|
if (cfg_score <= 0) {
|
|
msg_Err(va, "Failed to find a supported decoder configuration");
|
|
d3d11_device_unlock(sys->d3d_dev);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
/* Create the decoder */
|
|
ID3D11VideoDecoder *decoder;
|
|
hr = ID3D11VideoDevice_CreateVideoDecoder( sys->d3ddec, &decoderDesc, &sys->cfg, &decoder );
|
|
if (FAILED(hr)) {
|
|
msg_Err(va, "ID3D11VideoDevice_CreateVideoDecoder failed. (hr=0x%lX)", hr);
|
|
sys->hw.decoder = NULL;
|
|
d3d11_device_unlock(sys->d3d_dev);
|
|
return VLC_EGENERIC;
|
|
}
|
|
d3d11_device_unlock(sys->d3d_dev);
|
|
sys->hw.decoder = decoder;
|
|
|
|
msg_Dbg(va, "DxCreateDecoderSurfaces succeed");
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
static void DxDestroySurfaces(void *opaque)
|
|
{
|
|
vlc_va_sys_t *sys = opaque;
|
|
if (sys->hw.decoder)
|
|
ID3D11VideoDecoder_Release(sys->hw.decoder);
|
|
if (sys->hw_surface[0]) {
|
|
for (unsigned i = 0; i < sys->hw.surface_count; i++)
|
|
{
|
|
for (int j = 0; j < DXGI_MAX_SHADER_VIEW; j++)
|
|
{
|
|
if (sys->renderSrc[i*DXGI_MAX_SHADER_VIEW + j])
|
|
ID3D11ShaderResourceView_Release(sys->renderSrc[i*DXGI_MAX_SHADER_VIEW + j]);
|
|
}
|
|
ID3D11VideoDecoderOutputView_Release( sys->hw_surface[i] );
|
|
}
|
|
}
|
|
|
|
if (sys->d3ddec)
|
|
ID3D11VideoDevice_Release(sys->d3ddec);
|
|
if (sys->hw.video_context)
|
|
ID3D11VideoContext_Release(sys->hw.video_context);
|
|
|
|
free(sys);
|
|
}
|
|
|