Files
Akkariin Meiko 27c4ec74a1 Update
2022-03-12 03:16:09 +08:00

601 lines
21 KiB
C++

/*############################################################################
# Copyright (C) 2012-2020 Intel Corporation
#
# SPDX-License-Identifier: MIT
############################################################################*/
#include "windows/mfx_dispatcher.h"
#include "windows/mfx_dispatcher_log.h"
#include "windows/mfx_load_dll.h"
#include <assert.h>
#include <string.h>
#include <windows.h>
#include <algorithm>
#include "vpl/mfxadapter.h"
#include "windows/mfx_dxva2_device.h"
#include "windows/mfx_vector.h"
#include "windows/mfxvideo++.h"
#if _MSC_VER
#pragma warning(disable : 4355)
#endif
MFX_DISP_HANDLE::MFX_DISP_HANDLE(const mfxVersion requiredVersion)
: _mfxSession(),
apiVersion(requiredVersion) {
actualApiVersion.Version = 0;
implType = MFX_LIB_SOFTWARE;
impl = MFX_IMPL_SOFTWARE;
loadStatus = MFX_ERR_NOT_FOUND;
dispVersion.Major = MFX_DISPATCHER_VERSION_MAJOR;
dispVersion.Minor = MFX_DISPATCHER_VERSION_MINOR;
storageID = 0;
implInterface = MFX_IMPL_HARDWARE_ANY;
hModule = (mfxModuleHandle)0;
} // MFX_DISP_HANDLE::MFX_DISP_HANDLE(const mfxVersion requiredVersion)
MFX_DISP_HANDLE::~MFX_DISP_HANDLE(void) {
Close();
} // MFX_DISP_HANDLE::~MFX_DISP_HANDLE(void)
mfxStatus MFX_DISP_HANDLE::Close(void) {
mfxStatus mfxRes;
mfxRes = UnLoadSelectedDLL();
// need to reset dispatcher state after unloading dll
if (MFX_ERR_NONE == mfxRes) {
implType = MFX_LIB_SOFTWARE;
impl = MFX_IMPL_SOFTWARE;
loadStatus = MFX_ERR_NOT_FOUND;
dispVersion.Major = MFX_DISPATCHER_VERSION_MAJOR;
dispVersion.Minor = MFX_DISPATCHER_VERSION_MINOR;
*static_cast<_mfxSession *>(this) = _mfxSession();
hModule = (mfxModuleHandle)0;
}
return mfxRes;
} // mfxStatus MFX_DISP_HANDLE::Close(void)
mfxStatus MFX_DISP_HANDLE::LoadSelectedDLL(const wchar_t *pPath,
eMfxImplType reqImplType,
mfxIMPL reqImpl,
mfxIMPL reqImplInterface,
mfxInitParam &par,
mfxInitializationParam &vplParam) {
mfxStatus mfxRes = MFX_ERR_NONE;
// check error(s)
if ((MFX_LIB_SOFTWARE != reqImplType) && (MFX_LIB_HARDWARE != reqImplType)) {
DISPATCHER_LOG_ERROR(
(("implType == %s, should be either MFX_LIB_SOFTWARE ot MFX_LIB_HARDWARE\n"),
DispatcherLog_GetMFXImplString(reqImplType).c_str()));
loadStatus = MFX_ERR_ABORTED;
return loadStatus;
}
// only exact types of implementation is allowed
if ((MFX_IMPL_SOFTWARE != reqImpl) && (MFX_IMPL_HARDWARE != reqImpl) &&
(MFX_IMPL_HARDWARE2 != reqImpl) && (MFX_IMPL_HARDWARE3 != reqImpl) &&
(MFX_IMPL_HARDWARE4 != reqImpl)) {
DISPATCHER_LOG_ERROR((("invalid implementation impl == %s\n"),
DispatcherLog_GetMFXImplString(impl).c_str()));
loadStatus = MFX_ERR_ABORTED;
return loadStatus;
}
// only mfxExtThreadsParam is allowed
if (par.NumExtParam) {
if ((par.NumExtParam > 1) || !par.ExtParam) {
loadStatus = MFX_ERR_ABORTED;
return loadStatus;
}
if ((par.ExtParam[0]->BufferId != MFX_EXTBUFF_THREADS_PARAM) ||
(par.ExtParam[0]->BufferSz != sizeof(mfxExtThreadsParam))) {
loadStatus = MFX_ERR_ABORTED;
return loadStatus;
}
}
// close the handle before initialization
Close();
// save the library's type
this->implType = reqImplType;
this->impl = reqImpl;
this->implInterface = reqImplInterface;
{
assert(hModule == (mfxModuleHandle)0);
DISPATCHER_LOG_BLOCK(("invoking LoadLibrary(%S)\n", pPath));
// load the DLL into the memory
hModule = MFX::mfx_dll_load(pPath);
if (hModule) {
int i;
DISPATCHER_LOG_OPERATION({
wchar_t modulePath[1024];
GetModuleFileNameW((HMODULE)hModule,
modulePath,
sizeof(modulePath) / sizeof(modulePath[0]));
DISPATCHER_LOG_INFO((("loaded module %S\n"), modulePath))
});
// load video functions: pointers to exposed functions
for (i = 0; i < eVideoFuncTotal; i += 1) {
mfxFunctionPointer pProc =
(mfxFunctionPointer)MFX::mfx_dll_get_addr(hModule, APIFunc[i].pName);
if (pProc) {
// function exists in the library,
// save the pointer.
callTable[i] = pProc;
}
else {
// The library doesn't contain the function
DISPATCHER_LOG_WRN((("Can't find API function \"%s\"\n"), APIFunc[i].pName));
if (apiVersion.Version >= APIFunc[i].apiVersion.Version) {
DISPATCHER_LOG_ERROR((("\"%s\" is required for API %u.%u\n"),
APIFunc[i].pName,
apiVersion.Major,
apiVersion.Minor));
mfxRes = MFX_ERR_UNSUPPORTED;
break;
}
}
}
// if version >= 2.0, load these functions as well
if (apiVersion.Major >= 2) {
for (i = 0; i < eVideoFunc2Total; i += 1) {
mfxFunctionPointer pProc =
(mfxFunctionPointer)MFX::mfx_dll_get_addr(hModule, APIVideoFunc2[i].pName);
if (pProc) {
// function exists in the library,
// save the pointer.
callVideoTable2[i] = pProc;
}
else {
// The library doesn't contain the function
DISPATCHER_LOG_WRN(
(("Can't find API function \"%s\"\n"), APIVideoFunc2[i].pName));
if (apiVersion.Version >= APIVideoFunc2[i].apiVersion.Version) {
DISPATCHER_LOG_ERROR((("\"%s\" is required for API %u.%u\n"),
APIVideoFunc2[i].pName,
apiVersion.Major,
apiVersion.Minor));
mfxRes = MFX_ERR_UNSUPPORTED;
break;
}
}
}
}
}
else {
DISPATCHER_LOG_WRN((("can't find DLL: GetLastErr()=0x%x\n"), GetLastError()))
mfxRes = MFX_ERR_UNSUPPORTED;
}
}
// initialize the loaded DLL
if (MFX_ERR_NONE == mfxRes) {
mfxVersion version(apiVersion);
if (version.Major >= 2) {
// for API >= 2.0 call MFXInitialize instead of MFXInitEx
int tableIndex = eMFXInitialize;
mfxFunctionPointer pFunc = callVideoTable2[tableIndex];
// filled-in mfxInitializationParam must be provided for MFXInitialize path
mfxRes =
(*(mfxStatus(MFX_CDECL *)(mfxInitializationParam, mfxSession *))pFunc)(vplParam,
&session);
}
else {
// Call old-style MFXInit init for older libraries
bool callOldInit =
!callTable[eMFXInitEx]; // if true call eMFXInit, if false - eMFXInitEx
int tableIndex = (callOldInit) ? eMFXInit : eMFXInitEx;
mfxFunctionPointer pFunc = callTable[tableIndex];
if (callOldInit) {
DISPATCHER_LOG_BLOCK(("MFXInit(%s,ver=%u.%u,session=0x%p)\n",
DispatcherLog_GetMFXImplString(impl | implInterface).c_str(),
apiVersion.Major,
apiVersion.Minor,
&session));
mfxRes = (*(mfxStatus(MFX_CDECL *)(mfxIMPL, mfxVersion *, mfxSession *))pFunc)(
impl | implInterface,
&version,
&session);
}
else {
DISPATCHER_LOG_BLOCK(("MFXInitEx(%s,ver=%u.%u,ExtThreads=%d,session=0x%p)\n",
DispatcherLog_GetMFXImplString(impl | implInterface).c_str(),
apiVersion.Major,
apiVersion.Minor,
par.ExternalThreads,
&session));
mfxInitParam initPar = par;
// adjusting user parameters
initPar.Implementation = impl | implInterface;
initPar.Version = version;
mfxRes =
(*(mfxStatus(MFX_CDECL *)(mfxInitParam, mfxSession *))pFunc)(initPar, &session);
}
}
if (MFX_ERR_NONE != mfxRes) {
DISPATCHER_LOG_WRN((("library can't be load. MFXInit returned %s \n"),
DispatcherLog_GetMFXStatusString(mfxRes)))
}
else {
mfxRes = MFXQueryVersion((mfxSession)this, &actualApiVersion);
if (MFX_ERR_NONE != mfxRes) {
DISPATCHER_LOG_ERROR(
(("MFXQueryVersion returned: %d, skiped this library\n"), mfxRes))
}
else {
DISPATCHER_LOG_INFO((("MFXQueryVersion returned API: %d.%d\n"),
actualApiVersion.Major,
actualApiVersion.Minor))
//special hook for applications that uses sink api to get loaded library path
DISPATCHER_LOG_LIBRARY(("%p", hModule));
DISPATCHER_LOG_INFO(("library loaded succesfully\n"))
}
}
}
loadStatus = mfxRes;
return mfxRes;
} // mfxStatus MFX_DISP_HANDLE::LoadSelectedDLL(const wchar_t* pPath, eMfxImplType reqImplType, mfxIMPL reqImpl, mfxIMPL reqImplInterface, mfxInitParam& par, mfxInitializationParam &vplParam)
mfxStatus MFX_DISP_HANDLE::UnLoadSelectedDLL(void) {
mfxStatus mfxRes = MFX_ERR_NONE;
// close the loaded DLL
if (session) {
/* check whether it is audio session or video */
int tableIndex = eMFXClose;
mfxFunctionPointer pFunc;
pFunc = callTable[tableIndex];
mfxRes = (*(mfxStatus(MFX_CDECL *)(mfxSession))pFunc)(session);
if (MFX_ERR_NONE == mfxRes) {
session = (mfxSession)0;
}
DISPATCHER_LOG_INFO((("MFXClose(0x%x) returned %d\n"), session, mfxRes));
// actually, the return value is required to pass outside only.
}
// it is possible, that there is an active child session.
// can't unload library in that case.
if ((MFX_ERR_UNDEFINED_BEHAVIOR != mfxRes) && (hModule)) {
// unload the library.
if (!MFX::mfx_dll_free(hModule)) {
mfxRes = MFX_ERR_UNDEFINED_BEHAVIOR;
}
hModule = (mfxModuleHandle)0;
}
return mfxRes;
} // mfxStatus MFX_DISP_HANDLE::UnLoadSelectedDLL(void)
MFX_DISP_HANDLE_EX::MFX_DISP_HANDLE_EX(const mfxVersion requiredVersion)
: MFX_DISP_HANDLE(requiredVersion),
mediaAdapterType(MFX_MEDIA_UNKNOWN) {}
#if (defined(_WIN64) || defined(_WIN32)) && (MFX_VERSION >= 1031)
static mfxStatus InitDummySession(mfxU32 adapter_n, MFXVideoSession &dummy_session) {
mfxInitParam initPar;
memset(&initPar, 0, sizeof(initPar));
initPar.Version.Major = 1;
initPar.Version.Minor = 0;
switch (adapter_n) {
case 0:
initPar.Implementation = MFX_IMPL_HARDWARE;
break;
case 1:
initPar.Implementation = MFX_IMPL_HARDWARE2;
break;
case 2:
initPar.Implementation = MFX_IMPL_HARDWARE3;
break;
case 3:
initPar.Implementation = MFX_IMPL_HARDWARE4;
break;
default:
// try searching on all display adapters
initPar.Implementation = MFX_IMPL_HARDWARE_ANY;
break;
}
initPar.Implementation |= MFX_IMPL_VIA_D3D11;
return dummy_session.InitEx(initPar);
}
static inline bool is_iGPU(const mfxAdapterInfo &adapter_info) {
return adapter_info.Platform.MediaAdapterType == MFX_MEDIA_INTEGRATED;
}
static inline bool is_dGPU(const mfxAdapterInfo &adapter_info) {
return adapter_info.Platform.MediaAdapterType == MFX_MEDIA_DISCRETE;
}
// This function implies that iGPU has higher priority
static inline mfxI32 iGPU_priority(const void *ll, const void *rr) {
const mfxAdapterInfo &l = *(reinterpret_cast<const mfxAdapterInfo *>(ll));
const mfxAdapterInfo &r = *(reinterpret_cast<const mfxAdapterInfo *>(rr));
if ((is_iGPU(l) && is_iGPU(r)) || (is_dGPU(l) && is_dGPU(r)))
return 0;
if (is_iGPU(l) && is_dGPU(r))
return -1;
// The only combination left is_dGPU(l) && is_iGPU(r))
return 1;
}
static void RearrangeInPriorityOrder(const mfxComponentInfo &info,
MFX::MFXVector<mfxAdapterInfo> &vec) {
(void)info;
{
// Move iGPU to top priority
qsort(vec.data(), vec.size(), sizeof(mfxAdapterInfo), &iGPU_priority);
}
}
static mfxStatus PrepareAdaptersInfo(const mfxComponentInfo *info,
MFX::MFXVector<mfxAdapterInfo> &vec,
mfxAdaptersInfo &adapters) {
// No suitable adapters on system to handle user's workload
if (vec.empty()) {
adapters.NumActual = 0;
return MFX_ERR_NOT_FOUND;
}
if (info) {
RearrangeInPriorityOrder(*info, vec);
}
mfxU32 num_to_copy = (std::min)(mfxU32(vec.size()), adapters.NumAlloc);
for (mfxU32 i = 0; i < num_to_copy; ++i) {
adapters.Adapters[i] = vec[i];
}
adapters.NumActual = num_to_copy;
if (vec.size() > adapters.NumAlloc) {
return MFX_WRN_OUT_OF_RANGE;
}
return MFX_ERR_NONE;
}
static inline bool QueryAdapterInfo(mfxU32 adapter_n, mfxU32 &VendorID, mfxU32 &DeviceID) {
MFX::DXVA2Device dxvaDevice;
if (!dxvaDevice.InitDXGI1(adapter_n))
return false;
VendorID = dxvaDevice.GetVendorID();
DeviceID = dxvaDevice.GetDeviceID();
return true;
}
static inline mfxU32 MakeVersion(mfxU16 major, mfxU16 minor) {
return major * 1000 + minor;
}
mfxStatus MFXQueryAdaptersDecode(mfxBitstream *bitstream,
mfxU32 codec_id,
mfxAdaptersInfo *adapters) {
if (!adapters || !bitstream)
return MFX_ERR_NULL_PTR;
MFX::MFXVector<mfxAdapterInfo> obtained_info;
mfxU32 adapter_n = 0, VendorID, DeviceID;
mfxComponentInfo input_info;
memset(&input_info, 0, sizeof(input_info));
input_info.Type = mfxComponentType::MFX_COMPONENT_DECODE;
input_info.Requirements.mfx.CodecId = codec_id;
for (;;) {
if (!QueryAdapterInfo(adapter_n, VendorID, DeviceID))
break;
++adapter_n;
if (VendorID != INTEL_VENDOR_ID)
continue;
// Check if requested capabilities are supported
MFXVideoSession dummy_session;
mfxStatus sts = InitDummySession(adapter_n - 1, dummy_session);
if (sts != MFX_ERR_NONE) {
continue;
}
mfxVideoParam stream_params, out;
memset(&out, 0, sizeof(out));
memset(&stream_params, 0, sizeof(stream_params));
out.mfx.CodecId = stream_params.mfx.CodecId = codec_id;
sts = MFXVideoDECODE_DecodeHeader(dummy_session.operator mfxSession(),
bitstream,
&stream_params);
if (sts != MFX_ERR_NONE) {
continue;
}
sts = MFXVideoDECODE_Query(dummy_session.operator mfxSession(), &stream_params, &out);
if (sts !=
MFX_ERR_NONE) // skip MFX_ERR_UNSUPPORTED as well as MFX_WRN_INCOMPATIBLE_VIDEO_PARAM
continue;
mfxAdapterInfo info;
memset(&info, 0, sizeof(info));
//WA for initialization when application built w/ new API, but lib w/ old one.
mfxVersion apiVersion;
sts = dummy_session.QueryVersion(&apiVersion);
if (sts != MFX_ERR_NONE)
continue;
mfxU32 version = MakeVersion(apiVersion.Major, apiVersion.Minor);
if (version >= 1019) {
sts = MFXVideoCORE_QueryPlatform(dummy_session.operator mfxSession(), &info.Platform);
if (sts != MFX_ERR_NONE) {
continue;
}
}
else {
// for API versions greater than 1.19 Device id is set inside QueryPlatform call
info.Platform.DeviceId = static_cast<mfxU16>(DeviceID);
}
info.Number = adapter_n - 1;
obtained_info.push_back(info);
}
return PrepareAdaptersInfo(&input_info, obtained_info, *adapters);
}
mfxStatus MFXQueryAdapters(mfxComponentInfo *input_info, mfxAdaptersInfo *adapters) {
if (!adapters)
return MFX_ERR_NULL_PTR;
MFX::MFXVector<mfxAdapterInfo> obtained_info;
//obtained_info.reserve(adapters->NumAdaptersAlloc);
mfxU32 adapter_n = 0, VendorID, DeviceID;
for (;;) {
if (!QueryAdapterInfo(adapter_n, VendorID, DeviceID))
break;
++adapter_n;
if (VendorID != INTEL_VENDOR_ID)
continue;
// Check if requested capabilities are supported
MFXVideoSession dummy_session;
mfxStatus sts = InitDummySession(adapter_n - 1, dummy_session);
if (sts != MFX_ERR_NONE) {
continue;
}
// If input_info is NULL just return all Intel adapters and information about them
if (input_info) {
mfxVideoParam out;
memset(&out, 0, sizeof(out));
switch (input_info->Type) {
case mfxComponentType::MFX_COMPONENT_ENCODE: {
out.mfx.CodecId = input_info->Requirements.mfx.CodecId;
sts = MFXVideoENCODE_Query(dummy_session.operator mfxSession(),
&input_info->Requirements,
&out);
} break;
case mfxComponentType::MFX_COMPONENT_DECODE: {
out.mfx.CodecId = input_info->Requirements.mfx.CodecId;
sts = MFXVideoDECODE_Query(dummy_session.operator mfxSession(),
&input_info->Requirements,
&out);
} break;
case mfxComponentType::MFX_COMPONENT_VPP: {
sts = MFXVideoVPP_Query(dummy_session.operator mfxSession(),
&input_info->Requirements,
&out);
} break;
default:
sts = MFX_ERR_UNSUPPORTED;
}
}
if (sts !=
MFX_ERR_NONE) // skip MFX_ERR_UNSUPPORTED as well as MFX_WRN_INCOMPATIBLE_VIDEO_PARAM
continue;
mfxAdapterInfo info;
memset(&info, 0, sizeof(info));
//WA for initialization when application built w/ new API, but lib w/ old one.
mfxVersion apiVersion;
sts = dummy_session.QueryVersion(&apiVersion);
if (sts != MFX_ERR_NONE)
continue;
mfxU32 version = MakeVersion(apiVersion.Major, apiVersion.Minor);
if (version >= 1019) {
sts = MFXVideoCORE_QueryPlatform(dummy_session.operator mfxSession(), &info.Platform);
if (sts != MFX_ERR_NONE) {
continue;
}
}
else {
// for API versions greater than 1.19 Device id is set inside QueryPlatform call
info.Platform.DeviceId = static_cast<mfxU16>(DeviceID);
}
info.Number = adapter_n - 1;
obtained_info.push_back(info);
}
return PrepareAdaptersInfo(input_info, obtained_info, *adapters);
}
mfxStatus MFXQueryAdaptersNumber(mfxU32 *num_adapters) {
if (!num_adapters)
return MFX_ERR_NULL_PTR;
mfxU32 intel_adapter_count = 0, VendorID, DeviceID;
for (mfxU32 cur_adapter = 0;; ++cur_adapter) {
if (!QueryAdapterInfo(cur_adapter, VendorID, DeviceID))
break;
if (VendorID == INTEL_VENDOR_ID)
++intel_adapter_count;
}
*num_adapters = intel_adapter_count;
return MFX_ERR_NONE;
}
#endif // (defined(_WIN64) || defined(_WIN32)) && (MFX_VERSION >= 1031)