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.
 
 
 
 
 
 

1074 lines
35 KiB

/*****************************************************************************
* d3d11_fmt.cpp : D3D11 helper calls
*****************************************************************************
* Copyright © 2017 VLC authors, VideoLAN and VideoLabs
*
* Authors: 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.
*****************************************************************************/
#include <process.h>
#include <winapifamily.h>
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#define BUILD_FOR_UAP 0
#else
#define BUILD_FOR_UAP 1
#endif
#if defined(WINAPI_FAMILY)
# undef WINAPI_FAMILY
#endif
#define WINAPI_FAMILY WINAPI_FAMILY_DESKTOP_APP
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#if !BUILD_FOR_UAP
#if !defined(NDEBUG) && defined(HAVE_DXGIDEBUG_H)
#define HAVE_DXGI_DEBUG 1
#endif
#endif
#include <vlc_common.h>
#include <vlc_picture.h>
#include <vlc_charset.h>
#include <vlc/libvlc.h>
#include <vlc/libvlc_picture.h>
#include <vlc/libvlc_media.h>
#include <vlc/libvlc_renderer_discoverer.h>
#include <vlc/libvlc_media_player.h>
#include <initguid.h>
#include <d3d11.h>
#include <dxgi1_2.h>
#ifdef HAVE_DXGI_DEBUG
# include <dxgidebug.h>
#endif
#include <assert.h>
#include "d3d11_fmt.h"
#include <wbemidl.h>
#define D3D11_PICCONTEXT_FROM_PICCTX(pic_ctx) \
container_of((pic_ctx), d3d11_pic_context, s)
picture_sys_d3d11_t *ActiveD3D11PictureSys(picture_t *pic)
{
assert(pic->context != NULL);
assert(pic->p_sys == NULL);
d3d11_pic_context *pic_ctx = D3D11_PICCONTEXT_FROM_PICCTX(pic->context);
return &pic_ctx->picsys;
}
void AcquireD3D11PictureSys(picture_sys_d3d11_t *p_sys)
{
for (int i=0; i<DXGI_MAX_SHADER_VIEW; i++) {
if (p_sys->renderSrc[i])
p_sys->renderSrc[i]->AddRef();
if (p_sys->texture[i])
p_sys->texture[i]->AddRef();
}
if (p_sys->processorInput)
p_sys->processorInput->AddRef();
if (p_sys->processorOutput)
p_sys->processorOutput->AddRef();
}
void ReleaseD3D11PictureSys(picture_sys_d3d11_t *p_sys)
{
for (int i=0; i<DXGI_MAX_SHADER_VIEW; i++) {
if (p_sys->renderSrc[i])
p_sys->renderSrc[i]->Release();
if (p_sys->texture[i])
p_sys->texture[i]->Release();
}
if (p_sys->processorInput)
p_sys->processorInput->Release();
if (p_sys->processorOutput)
p_sys->processorOutput->Release();
}
/* map texture planes to resource views */
int D3D11_AllocateResourceView(struct vlc_logger *obj, ID3D11Device *d3ddevice,
const d3d_format_t *format,
ID3D11Texture2D *p_texture[DXGI_MAX_SHADER_VIEW], UINT slice_index,
ID3D11ShaderResourceView *renderSrc[DXGI_MAX_SHADER_VIEW])
{
HRESULT hr;
int i;
D3D11_SHADER_RESOURCE_VIEW_DESC resviewDesc{};
D3D11_TEXTURE2D_DESC texDesc;
p_texture[0]->GetDesc(&texDesc);
assert(texDesc.BindFlags & D3D11_BIND_SHADER_RESOURCE);
if (texDesc.ArraySize == 1)
{
resviewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
resviewDesc.Texture2D.MipLevels = 1;
}
else
{
resviewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
resviewDesc.Texture2DArray.MipLevels = -1;
resviewDesc.Texture2DArray.ArraySize = 1;
resviewDesc.Texture2DArray.FirstArraySlice = slice_index;
assert(slice_index < texDesc.ArraySize);
}
for (i=0; i<DXGI_MAX_SHADER_VIEW; i++)
{
resviewDesc.Format = format->resourceFormat[i];
if (resviewDesc.Format == DXGI_FORMAT_UNKNOWN)
renderSrc[i] = NULL;
else
{
hr = d3ddevice->CreateShaderResourceView(p_texture[i], &resviewDesc, &renderSrc[i]);
if (FAILED(hr)) {
vlc_error(obj, "Could not Create the Texture ResourceView %d slice %d. (hr=0x%lX)", i, slice_index, hr);
break;
}
}
}
if (i != DXGI_MAX_SHADER_VIEW)
{
while (--i >= 0)
{
renderSrc[i]->Release();
renderSrc[i] = NULL;
}
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
static LARGE_INTEGER D3D11_GetSystemDriver(vlc_object_t *obj, d3d11_device_t *d3d_dev)
{
HRESULT hr;
LARGE_INTEGER result = {};
IWbemLocator *pLoc = NULL;
IWbemServices *pSvc = NULL;
IEnumWbemClassObject* pEnumerator = NULL;
BSTR bRootNamespace = SysAllocString(L"ROOT\\CIMV2");
BSTR bWQL = SysAllocString(L"WQL");
WCHAR lookup[256];
_snwprintf(lookup, ARRAY_SIZE(lookup),
L"SELECT * FROM Win32_VideoController WHERE PNPDeviceID LIKE 'PCI\\\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X%%'",
d3d_dev->adapterDesc.VendorId, d3d_dev->adapterDesc.DeviceId,
d3d_dev->adapterDesc.SubSysId, d3d_dev->adapterDesc.Revision);
BSTR bVideoController = SysAllocString(lookup);
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
if (FAILED(hr))
{
msg_Dbg(obj, "Unable to initialize COM library");
return {};
}
IWbemClassObject *pclsObj = NULL;
ULONG uReturn = 0;
MULTI_QI res{};
res.pIID = &IID_IWbemLocator;
#if !BUILD_FOR_UAP
hr = CoCreateInstanceEx(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, 0,
1, &res);
#else // BUILD_FOR_UAP
hr = CoCreateInstanceFromApp(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, 0,
1, &res);
#endif // BUILD_FOR_UAP
if (FAILED(hr) || FAILED(res.hr))
{
msg_Dbg(obj, "Failed to create IWbemLocator object");
goto done;
}
pLoc = static_cast<IWbemLocator *>(res.pItf);
hr = pLoc->ConnectServer(bRootNamespace,
NULL, NULL, NULL, 0, NULL, NULL, &pSvc);
if (FAILED(hr))
{
msg_Dbg(obj, "Could not connect to namespace");
goto done;
}
#if !BUILD_FOR_UAP
hr = CoSetProxyBlanket(
pSvc,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE,
NULL,
RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE
);
if (FAILED(hr))
{
msg_Dbg(obj, "Could not set proxy blanket");
goto done;
}
#endif // !BUILD_FOR_UAP
hr = pSvc->ExecQuery(bWQL, bVideoController,
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
if (FAILED(hr) || !pEnumerator)
{
msg_Dbg(obj, "Query for Win32_VideoController failed");
goto done;
}
hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if (!uReturn)
{
msg_Warn(obj, "failed to find the device driver");
goto done;
}
VARIANT vtProp;
VariantInit(&vtProp);
hr = pclsObj->Get(L"DriverVersion", 0, &vtProp, 0, 0);
if ( FAILED( hr ) )
{
msg_Warn(obj, "failed to read the driver version");
goto done;
}
int wddm, d3d_features, revision, build;
/* see https://docs.microsoft.com/en-us/windows-hardware/drivers/display/wddm-2-1-features#driver-versioning */
if (swscanf(vtProp.bstrVal, TEXT("%d.%d.%d.%d"), &wddm, &d3d_features, &revision, &build) != 4)
{
msg_Warn(obj, "the adapter DriverVersion '%ls' doesn't match the expected format", vtProp.bstrVal);
}
else
{
result.HighPart = (wddm << 16) + d3d_features;
result.LowPart = (revision << 16) + build;
}
VariantClear(&vtProp);
pclsObj->Release();
done:
SysFreeString(bRootNamespace);
SysFreeString(bWQL);
SysFreeString(bVideoController);
if (pEnumerator)
pEnumerator->Release();
if (pSvc)
pSvc->Release();
if (pLoc)
pLoc->Release();
CoUninitialize();
return result;
}
static void D3D11_GetDriverVersion(vlc_object_t *obj, d3d11_device_t *d3d_dev, IDXGIAdapter *pAdapter)
{
int wddm, d3d_features, revision, build;
HRESULT hr;
LARGE_INTEGER driver = {};
hr = pAdapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &driver);
if (FAILED(hr))
{
msg_Dbg(obj, "failed to get interface version. (hr=0x%lX)", hr);
driver = D3D11_GetSystemDriver(obj, d3d_dev);
}
else if (HIWORD(driver.HighPart) < 23)
// starting with WDDM 2.3 driver versions must be coherent
// https://learn.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgiadapter-checkinterfacesupport#parameters
{
msg_Dbg(obj, "unsupported interface version %" PRIx64, driver.QuadPart);
driver = D3D11_GetSystemDriver(obj, d3d_dev);
}
else
{
#ifndef NDEBUG
const auto check_old_way = D3D11_GetSystemDriver(obj, d3d_dev);
assert(driver.QuadPart == check_old_way.QuadPart || check_old_way.QuadPart == 0);
#endif
}
wddm = HIWORD(driver.HighPart);
d3d_features = LOWORD(driver.HighPart);
revision = HIWORD(driver.LowPart);
build = LOWORD(driver.LowPart);
msg_Dbg(obj, "%s WDDM driver %d.%d.%d.%d", DxgiVendorStr(d3d_dev->adapterDesc.VendorId), wddm, d3d_features, revision, build);
if (d3d_dev->adapterDesc.VendorId == GPU_MANUFACTURER_INTEL && revision >= 100)
{
/* new Intel driver format */
build += (revision - 100) * 1000;
}
d3d_dev->WDDM.wddm = wddm;
d3d_dev->WDDM.d3d_features = d3d_features;
d3d_dev->WDDM.revision = revision;
d3d_dev->WDDM.build = build;
}
#ifdef HAVE_DXGI_DEBUG
struct dxgi_debug_handle_t
{
HINSTANCE dxgidebug_dll;
HRESULT (WINAPI * pf_DXGIGetDebugInterface)(REFIID, void **ppDebug);
void Init()
{
dxgidebug_dll = nullptr;
pf_DXGIGetDebugInterface = nullptr;
if (IsDebuggerPresent())
{
dxgidebug_dll = LoadLibraryExA("DXGIDEBUG.DLL", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (dxgidebug_dll)
{
pf_DXGIGetDebugInterface =
reinterpret_cast<decltype(pf_DXGIGetDebugInterface)>(GetProcAddress(dxgidebug_dll, "DXGIGetDebugInterface"));
if (unlikely(!pf_DXGIGetDebugInterface))
{
FreeLibrary(dxgidebug_dll);
dxgidebug_dll = nullptr;
}
}
}
}
void Release()
{
if (dxgidebug_dll)
FreeLibrary(dxgidebug_dll);
}
void LogResource()
{
if (pf_DXGIGetDebugInterface)
{
IDXGIDebug *pDXGIDebug;
if (SUCCEEDED(pf_DXGIGetDebugInterface(IID_GRAPHICS_PPV_ARGS(&pDXGIDebug))))
{
pDXGIDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL);
pDXGIDebug->Release();
}
}
}
};
#endif
typedef struct {
struct {
void *opaque;
libvlc_video_output_cleanup_cb cleanupDeviceCb;
} external;
#ifdef HAVE_DXGI_DEBUG
dxgi_debug_handle_t dxgi_debug;
#endif
d3d11_decoder_device_t dec_device;
} d3d11_decoder_device;
void D3D11_ReleaseDevice(d3d11_decoder_device_t *dev_sys)
{
d3d11_decoder_device *sys = container_of(dev_sys, d3d11_decoder_device, dec_device);
d3d11_device_t *d3d_dev = &dev_sys->d3d_dev;
if (d3d_dev->d3dcontext)
{
d3d_dev->d3dcontext->Flush();
d3d_dev->d3dcontext->Release();
d3d_dev->d3dcontext = NULL;
}
if (d3d_dev->d3ddevice)
{
d3d_dev->d3ddevice->Release();
d3d_dev->d3ddevice = NULL;
}
if( d3d_dev->mutex_owner && d3d_dev->context_mutex != INVALID_HANDLE_VALUE )
{
CloseHandle( d3d_dev->context_mutex );
d3d_dev->context_mutex = INVALID_HANDLE_VALUE;
}
if ( sys->external.cleanupDeviceCb )
sys->external.cleanupDeviceCb( sys->external.opaque );
#ifdef HAVE_DXGI_DEBUG
sys->dxgi_debug.LogResource();
sys->dxgi_debug.Release();
#endif
}
static HRESULT D3D11_CreateDeviceExternal(vlc_object_t *obj, ID3D11DeviceContext *d3d11ctx,
HANDLE context_lock, d3d11_device_t *out)
{
if (unlikely(d3d11ctx == NULL))
{
msg_Err(obj, "missing external ID3D11DeviceContext");
return E_FAIL;
}
HRESULT hr;
d3d11ctx->GetDevice(&out->d3ddevice );
if (!(out->d3ddevice->GetCreationFlags() & D3D11_CREATE_DEVICE_VIDEO_SUPPORT))
{
msg_Warn(obj, "the provided D3D11 device doesn't support decoding");
}
IDXGIAdapter *pAdapter = D3D11DeviceAdapter(out->d3ddevice);
if (unlikely(!pAdapter))
{
msg_Warn(obj, "can't get adapter from device %p", (void*)out->d3ddevice);
out->d3ddevice->Release();
out->d3ddevice = NULL;
return E_FAIL;
}
hr = pAdapter->GetDesc(&out->adapterDesc);
if (FAILED(hr))
msg_Warn(obj, "can't get adapter description");
d3d11ctx ->AddRef();
out->d3dcontext = d3d11ctx;
out->mutex_owner = false;
out->feature_level = out->d3ddevice ->GetFeatureLevel();
out->context_mutex = context_lock;
if (context_lock == NULL || context_lock == INVALID_HANDLE_VALUE)
{
msg_Warn(obj, "external ID3D11DeviceContext mutex not provided, using internal one");
out->mutex_owner = true;
out->context_mutex = CreateMutexEx( NULL, NULL, 0, SYNCHRONIZE );
}
D3D11_GetDriverVersion(obj, out, pAdapter);
pAdapter->Release();
return S_OK;
}
static HRESULT CreateDevice(vlc_object_t *obj,
IDXGIAdapter *adapter,
bool hw_decoding, d3d11_device_t *out)
{
HRESULT hr = E_NOTIMPL;
UINT creationFlags = 0;
if (hw_decoding || !obj->force)
creationFlags |= D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
#if !defined(NDEBUG) && !defined(BUILD_FOR_UAP)
if (IsDebuggerPresent())
{
HINSTANCE sdklayer_dll = LoadLibraryExA("d3d11_1sdklayers.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (sdklayer_dll) {
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
FreeLibrary(sdklayer_dll);
}
}
#endif
static const D3D_DRIVER_TYPE driverAttempts[] = {
D3D_DRIVER_TYPE_HARDWARE,
#if 0 /* ifndef NDEBUG */
D3D_DRIVER_TYPE_REFERENCE,
#endif
};
static D3D_FEATURE_LEVEL D3D11_features[] = {
D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1
};
for (UINT driver = 0; driver < (adapter ? 1 : ARRAY_SIZE(driverAttempts)); driver++) {
hr = D3D11CreateDevice(adapter, adapter ? D3D_DRIVER_TYPE_UNKNOWN : driverAttempts[driver],
NULL, creationFlags,
D3D11_features, ARRAY_SIZE(D3D11_features), D3D11_SDK_VERSION,
&out->d3ddevice, &out->feature_level, &out->d3dcontext);
if (SUCCEEDED(hr)) {
msg_Dbg(obj, "Created the D3D11 device type %d level %x (flags %08x).",
driverAttempts[driver], out->feature_level, creationFlags);
if (adapter != NULL)
{
hr = adapter->GetDesc(&out->adapterDesc);
D3D11_GetDriverVersion( obj, out, adapter );
}
else
{
IDXGIAdapter *adap = D3D11DeviceAdapter(out->d3ddevice);
if (adap == NULL)
hr = E_FAIL;
else
{
hr = adap->GetDesc(&out->adapterDesc);
D3D11_GetDriverVersion( obj, out, adap );
adap->Release();
}
}
if (FAILED(hr))
msg_Warn(obj, "can't get adapter description");
/* we can work with legacy levels but only if forced */
if ( obj->force || out->feature_level >= D3D_FEATURE_LEVEL_11_0 )
break;
msg_Warn(obj, "Incompatible feature level %x", out->feature_level);
out->d3dcontext->Release();
out->d3ddevice->Release();
out->d3dcontext = NULL;
out->d3ddevice = NULL;
hr = E_NOTIMPL;
}
}
if (hw_decoding && SUCCEEDED(hr))
{
out->context_mutex = CreateMutexEx( NULL, NULL, 0, SYNCHRONIZE );
out->mutex_owner = true;
}
else
out->context_mutex = INVALID_HANDLE_VALUE;
return hr;
}
d3d11_decoder_device_t *(D3D11_CreateDevice)(vlc_object_t *obj,
IDXGIAdapter *adapter,
bool hw_decoding, bool forced)
{
d3d11_decoder_device *sys = static_cast<d3d11_decoder_device*>(vlc_obj_malloc(obj, sizeof(*sys)));
if (unlikely(sys==NULL))
return NULL;
#ifdef HAVE_DXGI_DEBUG
sys->dxgi_debug.Init();
#endif
sys->external.cleanupDeviceCb = NULL;
HRESULT hr = E_FAIL;
{
libvlc_video_engine_t engineType = (libvlc_video_engine_t)var_InheritInteger( obj, "vout-cb-type" );
libvlc_video_output_setup_cb setupDeviceCb = NULL;
if (engineType == libvlc_video_engine_d3d11)
setupDeviceCb = (libvlc_video_output_setup_cb)var_InheritAddress( obj, "vout-cb-setup" );
if ( setupDeviceCb != NULL)
{
/* decoder device coming from the external app */
sys->external.opaque = var_InheritAddress( obj, "vout-cb-opaque" );
sys->external.cleanupDeviceCb = (libvlc_video_output_cleanup_cb)var_InheritAddress( obj, "vout-cb-cleanup" );
libvlc_video_setup_device_cfg_t cfg = {
.hardware_decoding = true, /* always favor hardware decoding */
};
libvlc_video_setup_device_info_t out{};
if (!setupDeviceCb( &sys->external.opaque, &cfg, &out ))
{
if (sys->external.cleanupDeviceCb)
sys->external.cleanupDeviceCb( sys->external.opaque );
msg_Err(obj, "Failed to setup external D3D11 device");
goto error;
}
hr = D3D11_CreateDeviceExternal(obj, static_cast<ID3D11DeviceContext*>(out.d3d11.device_context), out.d3d11.context_mutex,
&sys->dec_device.d3d_dev);
}
else if ( engineType == libvlc_video_engine_disable ||
engineType == libvlc_video_engine_d3d11 )
{
/* internal decoder device */
#if !BUILD_FOR_UAP
if (!forced)
{
/* Allow using D3D11 automatically starting from Windows 8.1 */
bool isWin81OrGreater = false;
HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32.dll"));
if (likely(hKernel32 != NULL))
isWin81OrGreater = GetProcAddress(hKernel32, "IsProcessCritical") != NULL;
if (!isWin81OrGreater)
{
msg_Dbg(obj, "D3D11 not forced on Win7/8");
goto error;
}
}
#endif
hr = CreateDevice( obj, adapter, hw_decoding, &sys->dec_device.d3d_dev );
}
else
{
msg_Dbg(obj, "Unsupported engine type %d", engineType);
goto error;
}
}
error:
if (FAILED(hr))
{
#ifdef HAVE_DXGI_DEBUG
sys->dxgi_debug.LogResource();
sys->dxgi_debug.Release();
#endif
vlc_obj_free( obj, sys );
return NULL;
}
return &sys->dec_device;
}
IDXGIAdapter *D3D11DeviceAdapter(ID3D11Device *d3ddev)
{
IDXGIDevice *pDXGIDevice;
HRESULT hr = d3ddev->QueryInterface(IID_GRAPHICS_PPV_ARGS(&pDXGIDevice));
if (FAILED(hr)) {
return NULL;
}
IDXGIAdapter *p_adapter;
hr = pDXGIDevice->GetAdapter(&p_adapter);
pDXGIDevice->Release();
if (FAILED(hr)) {
return NULL;
}
return p_adapter;
}
bool isXboxHardware([[maybe_unused]] const d3d11_device_t *d3ddev)
{
bool result = false;
#if BUILD_FOR_UAP
if (d3ddev->adapterDesc.VendorId == 0 &&
d3ddev->adapterDesc.DeviceId == 0 &&
!wcscmp(L"ROOT\\SraKmd\\0000", d3ddev->adapterDesc.Description))
result = true;
#endif
return result;
}
/**
* Performs a check on each value of the WDDM version. Any value that is OK will
* consider the driver valid (OR on each value)
*/
int D3D11CheckDriverVersion(const d3d11_device_t *d3d_dev, UINT vendorId, const struct wddm_version *min_ver)
{
if (vendorId && d3d_dev->adapterDesc.VendorId != vendorId)
return VLC_SUCCESS;
if (min_ver->wddm)
{
if (d3d_dev->WDDM.wddm > min_ver->wddm)
return VLC_SUCCESS;
if (d3d_dev->WDDM.wddm != min_ver->wddm)
return VLC_EGENERIC;
}
if (min_ver->d3d_features)
{
if (d3d_dev->WDDM.d3d_features > min_ver->d3d_features)
return VLC_SUCCESS;
if (d3d_dev->WDDM.d3d_features != min_ver->d3d_features)
return VLC_EGENERIC;
}
if (min_ver->revision)
{
if (d3d_dev->WDDM.revision > min_ver->revision)
return VLC_SUCCESS;
if (d3d_dev->WDDM.revision != min_ver->revision)
return VLC_EGENERIC;
}
if (min_ver->build)
{
if (d3d_dev->WDDM.build > min_ver->build)
return VLC_SUCCESS;
if (d3d_dev->WDDM.build != min_ver->build)
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/* test formats that should work but sometimes have issues on some platforms */
static bool CanReallyUseFormat(vlc_object_t *obj, d3d11_device_t *d3d_dev,
vlc_fourcc_t i_chroma, DXGI_FORMAT dxgi)
{
bool result = true;
if (dxgi == DXGI_FORMAT_UNKNOWN)
return true;
if (is_d3d11_opaque(i_chroma))
return true;
ID3D11Texture2D *texture = NULL;
D3D11_TEXTURE2D_DESC texDesc;
ZeroMemory(&texDesc, sizeof(texDesc));
texDesc.MipLevels = 1;
texDesc.SampleDesc.Count = 1;
texDesc.MiscFlags = 0; //D3D11_RESOURCE_MISC_SHARED;
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
texDesc.Usage = D3D11_USAGE_DYNAMIC;
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
texDesc.ArraySize = 1;
texDesc.Format = dxgi;
texDesc.Height = 144;
texDesc.Width = 176;
HRESULT hr = d3d_dev->d3ddevice->CreateTexture2D( &texDesc, NULL, &texture );
if (FAILED(hr))
{
msg_Dbg(obj, "cannot allocate a writable texture type %s. (hr=0x%lX)", DxgiFormatToStr(dxgi), hr);
return false;
}
D3D11_MAPPED_SUBRESOURCE mappedResource;
hr = d3d_dev->d3dcontext->Map(texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if (FAILED(hr))
{
msg_Err(obj, "The texture type %s cannot be mapped. (hr=0x%lX)", DxgiFormatToStr(dxgi), hr);
result = false;
goto done;
}
d3d_dev->d3dcontext->Unmap(texture, 0);
if (dxgi == DXGI_FORMAT_YUY2)
{
const vlc_chroma_description_t *p_chroma_desc = vlc_fourcc_GetChromaDescription( i_chroma );
if( !p_chroma_desc )
{
msg_Err(obj, "No pixel format for %4.4s", (const char*)&i_chroma);
result = false;
goto done;
}
if (mappedResource.RowPitch >= 2 * (texDesc.Width * p_chroma_desc->p[0].w.num / p_chroma_desc->p[0].w.den * p_chroma_desc->pixel_size))
{
msg_Err(obj, "Bogus %4.4s pitch detected type %s. %d should be %d", (const char*)&i_chroma,
DxgiFormatToStr(dxgi), mappedResource.RowPitch,
(texDesc.Width * p_chroma_desc->p[0].w.num / p_chroma_desc->p[0].w.den * p_chroma_desc->pixel_size));
result = false;
goto done;
}
}
done:
texture->Release();
return result;
}
bool D3D11_DeviceSupportsFormat(d3d11_device_t *d3d_dev, DXGI_FORMAT format, UINT supportFlags)
{
UINT i_formatSupport;
return SUCCEEDED( d3d_dev->d3ddevice->CheckFormatSupport(format,
&i_formatSupport) )
&& ( i_formatSupport & supportFlags ) == supportFlags;
}
const d3d_format_t *(FindD3D11Format)(vlc_object_t *o,
d3d11_device_t *d3d_dev,
vlc_fourcc_t i_src_chroma,
int rgb_yuv,
uint8_t bits_per_channel,
uint8_t widthDenominator,
uint8_t heightDenominator,
int alpha_bits,
int cpu_gpu,
UINT supportFlags)
{
supportFlags |= D3D11_FORMAT_SUPPORT_TEXTURE2D;
for (const d3d_format_t *output_format = DxgiGetRenderFormatList();
output_format->name != NULL; ++output_format)
{
if (i_src_chroma && i_src_chroma != output_format->fourcc)
continue;
if (bits_per_channel && bits_per_channel > output_format->bitsPerChannel)
continue;
int cpu_gpu_fmt = is_d3d11_opaque(output_format->fourcc) ? DXGI_CHROMA_GPU : DXGI_CHROMA_CPU;
if ((cpu_gpu & cpu_gpu_fmt)==0)
continue;
int format = vlc_fourcc_IsYUV(output_format->fourcc) ? DXGI_YUV_FORMAT : DXGI_RGB_FORMAT;
if ((rgb_yuv & format)==0)
continue;
if (widthDenominator && widthDenominator < output_format->widthDenominator)
continue;
if (heightDenominator && heightDenominator < output_format->heightDenominator)
continue;
if (alpha_bits > 0 && output_format->bitsForAlpha < alpha_bits)
continue;
if (alpha_bits == 0 && output_format->bitsForAlpha != 0)
continue;
DXGI_FORMAT textureFormat;
if (output_format->formatTexture == DXGI_FORMAT_UNKNOWN)
textureFormat = output_format->resourceFormat[0];
else
textureFormat = output_format->formatTexture;
if( D3D11_DeviceSupportsFormat( d3d_dev, textureFormat, supportFlags ) &&
CanReallyUseFormat(o, d3d_dev, output_format->fourcc, output_format->formatTexture) )
return output_format;
}
return NULL;
}
void D3D11_PictureAttach(picture_t *pic, ID3D11Texture2D *slicedTexture, const d3d_format_t *cfg)
{
d3d11_pic_context *pic_ctx = D3D11_PICCONTEXT_FROM_PICCTX(pic->context);
D3D11_TEXTURE2D_DESC texDesc;
slicedTexture->GetDesc(&texDesc);
if (texDesc.CPUAccessFlags != 0)
{
const video_format_t *fmt = &pic->format;
const vlc_chroma_description_t *p_chroma_desc = vlc_fourcc_GetChromaDescription( fmt->i_chroma );
if( !p_chroma_desc )
return;
for( unsigned i = 0; i < p_chroma_desc->plane_count; i++ )
{
plane_t *p = &pic->p[i];
p->i_lines = fmt->i_height * 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 = fmt->i_width * 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;
}
}
for (unsigned plane = 0; plane < DXGI_MAX_SHADER_VIEW; plane++)
{
if (!cfg->resourceFormat[plane])
{
pic_ctx->picsys.texture[plane] = NULL;
}
else
{
pic_ctx->picsys.texture[plane] = slicedTexture;
slicedTexture->AddRef();
}
}
}
#undef AllocateTextures
int AllocateTextures( vlc_object_t *obj, d3d11_device_t *d3d_dev,
const d3d_format_t *cfg, const video_format_t *fmt, bool shared,
ID3D11Texture2D *textures[],
plane_t out_planes[] )
{
plane_t planes[PICTURE_PLANE_MAX];
unsigned plane, plane_count;
HRESULT hr;
ID3D11Texture2D *slicedTexture = NULL;
D3D11_TEXTURE2D_DESC texDesc;
ZeroMemory(&texDesc, sizeof(texDesc));
texDesc.MipLevels = 1;
texDesc.SampleDesc.Count = 1;
if (shared)
texDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_NTHANDLE;
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
texDesc.ArraySize = 1;
if (is_d3d11_opaque(fmt->i_chroma)) {
texDesc.BindFlags |= D3D11_BIND_RENDER_TARGET;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.CPUAccessFlags = 0;
} else {
texDesc.Usage = D3D11_USAGE_DYNAMIC;
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
}
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) {
if (p_chroma_desc->plane_count == 0)
{
msg_Dbg(obj, "failed to get the pixel format planes for %4.4s", (char *)&fmt->i_chroma);
return VLC_EGENERIC;
}
assert(p_chroma_desc->plane_count <= DXGI_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]);
} else {
plane_count = __MAX(1, p_chroma_desc->plane_count);
texDesc.Format = cfg->formatTexture;
texDesc.Height = fmt->i_height;
texDesc.Width = fmt->i_width;
hr = d3d_dev->d3ddevice->CreateTexture2D( &texDesc, NULL, &slicedTexture );
if (FAILED(hr)) {
msg_Err(obj, "CreateTexture2D failed. (hr=0x%lX)", hr);
goto error;
}
}
for( unsigned i = 0; i < p_chroma_desc->plane_count; i++ )
{
plane_t *p = &planes[i];
p->i_lines = fmt->i_height * 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 = fmt->i_width * 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;
}
for (plane = 0; plane < plane_count; plane++)
{
if (slicedTexture) {
textures[plane] = slicedTexture;
slicedTexture->AddRef();
} else {
texDesc.Height = planes[plane].i_lines;
texDesc.Width = planes[plane].i_pitch / p_chroma_desc->pixel_size;
hr = d3d_dev->d3ddevice->CreateTexture2D( &texDesc, NULL, &textures[plane] );
if (FAILED(hr)) {
msg_Err(obj, "CreateTexture2D failed for plane %d. (hr=0x%lX)", plane, hr);
goto error;
}
}
}
if (out_planes)
for (plane = 0; plane < p_chroma_desc->plane_count; plane++)
{
out_planes[plane] = planes[plane];
}
for (; plane < DXGI_MAX_SHADER_VIEW; plane++) {
if (!cfg->resourceFormat[plane])
textures[plane] = NULL;
else
{
textures[plane] = textures[0];
textures[plane]->AddRef();
}
}
if (slicedTexture)
slicedTexture->Release();
return VLC_SUCCESS;
error:
if (slicedTexture)
slicedTexture->Release();
return VLC_EGENERIC;
}
void D3D11_LogResources([[maybe_unused]] d3d11_decoder_device_t *dev_sys)
{
#ifdef HAVE_DXGI_DEBUG
d3d11_decoder_device *sys = container_of(dev_sys, d3d11_decoder_device, dec_device);
sys->dxgi_debug.LogResource();
#endif
}
const struct vlc_video_context_operations d3d11_vctx_ops = {
NULL,
};
vlc_video_context *D3D11CreateVideoContext(vlc_decoder_device *dec_dev, DXGI_FORMAT vctx_fmt, DXGI_FORMAT alpha)
{
vlc_video_context *vctx = vlc_video_context_Create( dec_dev, VLC_VIDEO_CONTEXT_D3D11VA,
sizeof(d3d11_video_context_t), &d3d11_vctx_ops );
if (unlikely(vctx == NULL))
return NULL;
d3d11_video_context_t *priv = GetD3D11ContextPrivate(vctx);
priv->format = vctx_fmt;
priv->secondary = alpha;
return vctx;
}
void d3d11_pic_context_destroy(picture_context_t *ctx)
{
d3d11_pic_context *pic_ctx = D3D11_PICCONTEXT_FROM_PICCTX(ctx);
ReleaseD3D11PictureSys(&pic_ctx->picsys);
if (pic_ctx->picsys.sharedHandle != INVALID_HANDLE_VALUE && pic_ctx->picsys.ownHandle)
CloseHandle(pic_ctx->picsys.sharedHandle);
free(pic_ctx);
}
picture_context_t *d3d11_pic_context_copy(picture_context_t *ctx)
{
d3d11_pic_context *pic_ctx = static_cast<d3d11_pic_context*>(calloc(1, sizeof(*pic_ctx)));
if (unlikely(pic_ctx==NULL))
return NULL;
*pic_ctx = *D3D11_PICCONTEXT_FROM_PICCTX(ctx);
vlc_video_context_Hold(pic_ctx->s.vctx);
AcquireD3D11PictureSys(&pic_ctx->picsys);
return &pic_ctx->s;
}
int D3D11_PictureFill(vlc_object_t *obj, picture_t *pic,
vlc_video_context *vctx_out,
bool shared, const d3d_format_t *cfg)
{
if (unlikely(cfg == NULL))
return VLC_EINVAL;
d3d11_pic_context *pic_ctx = static_cast<d3d11_pic_context*>(calloc(1, sizeof(*pic_ctx)));
if (unlikely(pic_ctx == NULL))
return VLC_ENOMEM;
pic_ctx->picsys.sharedHandle = INVALID_HANDLE_VALUE;
d3d11_decoder_device_t *dev_sys = GetD3D11OpaqueContext(vctx_out);
if (AllocateTextures(obj, &dev_sys->d3d_dev, cfg,
&pic->format, shared, pic_ctx->picsys.texture, NULL) != VLC_SUCCESS)
{
free(pic_ctx);
return VLC_EGENERIC;
}
D3D11_AllocateResourceView(vlc_object_logger(obj), dev_sys->d3d_dev.d3ddevice, cfg, pic_ctx->picsys.texture, 0, pic_ctx->picsys.renderSrc);
if (shared)
{
HRESULT hr;
IDXGIResource1 *sharedResource;
hr = pic_ctx->picsys.texture[0]->QueryInterface(IID_GRAPHICS_PPV_ARGS(&sharedResource));
if (likely(SUCCEEDED(hr)))
{
sharedResource->CreateSharedHandle(NULL,
DXGI_SHARED_RESOURCE_READ/*|DXGI_SHARED_RESOURCE_WRITE*/,
NULL, &pic_ctx->picsys.sharedHandle);
pic_ctx->picsys.ownHandle = true;
sharedResource->Release();
}
}
pic_ctx->s = (picture_context_t) {
d3d11_pic_context_destroy, d3d11_pic_context_copy,
vlc_video_context_Hold(vctx_out),
};
pic->context = &pic_ctx->s;
return VLC_SUCCESS;
}
picture_t *D3D11_AllocPicture(vlc_object_t *obj,
const video_format_t *fmt, vlc_video_context *vctx_out,
bool shared, const d3d_format_t *cfg)
{
picture_t *pic = picture_NewFromFormat( fmt );
if (unlikely(pic == NULL))
return NULL;
if (D3D11_PictureFill(obj, pic, vctx_out, shared, cfg) != VLC_SUCCESS)
{
picture_Release(pic);
return NULL;
}
return pic;
}