diff --git a/plugins/obs-qsv11/CMakeLists.txt b/plugins/obs-qsv11/CMakeLists.txt index ee05bdbd8..ded4ceeda 100644 --- a/plugins/obs-qsv11/CMakeLists.txt +++ b/plugins/obs-qsv11/CMakeLists.txt @@ -94,6 +94,8 @@ target_link_libraries( target_compile_definitions(obs-qsv11 PRIVATE DX11_D3D) if(OS_WINDOWS) + add_subdirectory(obs-qsv-test) + set(MODULE_DESCRIPTION "OBS QSV encoder") configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in obs-qsv11.rc) @@ -105,7 +107,7 @@ if(OS_WINDOWS) _CRT_NONSTDC_NO_WARNINGS) endif() -set_target_properties(obs-qsv11 PROPERTIES FOLDER "plugins") +set_target_properties(obs-qsv11 PROPERTIES FOLDER "plugins/obs-qsv11") file(GLOB _OBS_QSV11_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.c ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) diff --git a/plugins/obs-qsv11/QSV_Encoder.cpp b/plugins/obs-qsv11/QSV_Encoder.cpp index 947d1aa5b..103979fba 100644 --- a/plugins/obs-qsv11/QSV_Encoder.cpp +++ b/plugins/obs-qsv11/QSV_Encoder.cpp @@ -74,99 +74,6 @@ mfxIMPL impl = MFX_IMPL_HARDWARE_ANY; mfxVersion ver = {{0, 1}}; // for backward compatibility std::atomic is_active{false}; -bool prefer_current_or_igpu_enc(int *iGPUIndex) -{ - IDXGIAdapter *pAdapter; - bool hasIGPU = false; - bool hasDGPU = false; - bool hasCurrent = false; - - HMODULE hDXGI = LoadLibrary(L"dxgi.dll"); - if (hDXGI == NULL) { - return false; - } - - typedef HRESULT(WINAPI * LPCREATEDXGIFACTORY)(REFIID riid, - void **ppFactory); - - LPCREATEDXGIFACTORY pCreateDXGIFactory = - (LPCREATEDXGIFACTORY)GetProcAddress(hDXGI, - "CreateDXGIFactory1"); - if (pCreateDXGIFactory == NULL) { - pCreateDXGIFactory = (LPCREATEDXGIFACTORY)GetProcAddress( - hDXGI, "CreateDXGIFactory"); - - if (pCreateDXGIFactory == NULL) { - FreeLibrary(hDXGI); - return false; - } - } - - IDXGIFactory *pFactory = NULL; - if (FAILED((*pCreateDXGIFactory)(__uuidof(IDXGIFactory), - (void **)(&pFactory)))) { - FreeLibrary(hDXGI); - return false; - } - - LUID luid; - bool hasLuid = false; - obs_enter_graphics(); - { - ID3D11Device *pDevice = (ID3D11Device *)gs_get_device_obj(); - Microsoft::WRL::ComPtr dxgiDevice; - if (SUCCEEDED(pDevice->QueryInterface( - dxgiDevice.GetAddressOf()))) { - Microsoft::WRL::ComPtr dxgiAdapter; - if (SUCCEEDED(dxgiDevice->GetAdapter( - dxgiAdapter.GetAddressOf()))) { - DXGI_ADAPTER_DESC desc; - hasLuid = - SUCCEEDED(dxgiAdapter->GetDesc(&desc)); - if (hasLuid) { - luid = desc.AdapterLuid; - } - } - } - } - obs_leave_graphics(); - - // Check for i+I cases (Intel discrete + Intel integrated graphics on the same system). Default will be integrated. - for (int adapterIndex = 0; - SUCCEEDED(pFactory->EnumAdapters(adapterIndex, &pAdapter)); - ++adapterIndex) { - DXGI_ADAPTER_DESC AdapterDesc = {}; - const HRESULT hr = pAdapter->GetDesc(&AdapterDesc); - pAdapter->Release(); - - if (SUCCEEDED(hr) && (AdapterDesc.VendorId == 0x8086)) { - if (hasLuid && - (AdapterDesc.AdapterLuid.LowPart == luid.LowPart) && - (AdapterDesc.AdapterLuid.HighPart == - luid.HighPart)) { - hasCurrent = true; - *iGPUIndex = adapterIndex; - break; - } - - if (AdapterDesc.DedicatedVideoMemory <= - 512 * 1024 * 1024) { - hasIGPU = true; - if (iGPUIndex != NULL) { - *iGPUIndex = adapterIndex; - } - } else { - hasDGPU = true; - } - } - } - - pFactory->Release(); - FreeLibrary(hDXGI); - - return hasCurrent || (hasIGPU && hasDGPU); -} - void qsv_encoder_version(unsigned short *major, unsigned short *minor) { *major = ver.Major; @@ -177,12 +84,23 @@ qsv_t *qsv_encoder_open(qsv_param_t *pParams) { mfxIMPL impl_list[4] = {MFX_IMPL_HARDWARE, MFX_IMPL_HARDWARE2, MFX_IMPL_HARDWARE3, MFX_IMPL_HARDWARE4}; - int igpu_index = -1; - if (prefer_current_or_igpu_enc(&igpu_index) && - (igpu_index < _countof(impl_list))) { - impl = impl_list[igpu_index]; + + obs_video_info ovi; + obs_get_video_info(&ovi); + size_t adapter_idx = ovi.adapter; + + // Select current adapter - will be iGPU if exists due to adapter reordering + if (!adapters[adapter_idx].is_intel) { + for (size_t i = 0; i < 4; i++) { + if (adapters[i].is_intel) { + adapter_idx = i; + break; + } + } } + impl = impl_list[adapter_idx]; + QSV_Encoder_Internal *pEncoder = new QSV_Encoder_Internal(impl, ver); mfxStatus sts = pEncoder->Open(pParams); if (sts != MFX_ERR_NONE) { diff --git a/plugins/obs-qsv11/QSV_Encoder.h b/plugins/obs-qsv11/QSV_Encoder.h index e3af397f0..d466e28ef 100644 --- a/plugins/obs-qsv11/QSV_Encoder.h +++ b/plugins/obs-qsv11/QSV_Encoder.h @@ -82,6 +82,15 @@ static const char *const qsv_latency_names[] = {"ultra-low", "low", "normal", 0}; typedef struct qsv_t qsv_t; +struct adapter_info { + bool is_intel; + bool is_dgpu; +}; + +#define MAX_ADAPTERS 10 +extern struct adapter_info adapters[MAX_ADAPTERS]; +extern size_t adapter_count; + typedef struct { mfxU16 nTargetUsage; /* 1 through 7, 1 being best quality and 7 being the best speed */ diff --git a/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt b/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt new file mode 100644 index 000000000..467f23c81 --- /dev/null +++ b/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt @@ -0,0 +1,11 @@ +project(obs-qsv-test) + +include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/libobs) + +add_executable(obs-qsv-test) +target_sources(obs-qsv-test PRIVATE obs-qsv-test.cpp) +target_link_libraries(obs-qsv-test d3d11 dxgi dxguid OBS::libmfx) + +set_target_properties(obs-qsv-test PROPERTIES FOLDER "plugins/obs-qsv11") + +setup_binary_target(obs-qsv-test) diff --git a/plugins/obs-qsv11/obs-qsv-test/obs-qsv-test.cpp b/plugins/obs-qsv11/obs-qsv-test/obs-qsv-test.cpp new file mode 100644 index 000000000..856aa6c03 --- /dev/null +++ b/plugins/obs-qsv11/obs-qsv-test/obs-qsv-test.cpp @@ -0,0 +1,126 @@ +#include "mfxstructures.h" +#include "mfxadapter.h" +#include "mfxvideo++.h" +#include "../common_utils.h" + +#include + +#include +#include +#include + +#include +#include + +#ifdef _MSC_VER +extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 1; +extern "C" __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; +#endif + +#define INTEL_VENDOR_ID 0x8086 + +struct adapter_caps { + bool is_intel = false; + bool is_dgpu = false; +}; + +static std::map adapter_info; + +static bool has_encoder(mfxIMPL impl, mfxU32 codec_id) +{ + MFXVideoSession session; + mfxInitParam init_param = {}; + init_param.Implementation = impl; + init_param.Version.Major = 1; + init_param.Version.Minor = 0; + mfxStatus sts = session.InitEx(init_param); + + mfxVideoParam video_param; + memset(&video_param, 0, sizeof(video_param)); + video_param.mfx.CodecId = codec_id; + video_param.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12; + video_param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420; + video_param.mfx.FrameInfo.Width = MSDK_ALIGN16(1280); + video_param.mfx.FrameInfo.Height = MSDK_ALIGN16(720); + sts = MFXVideoENCODE_Query(session, &video_param, &video_param); + session.Close(); + + return sts == MFX_ERR_NONE; +} + +static bool get_adapter_caps(IDXGIFactory *factory, uint32_t adapter_idx) +{ + HRESULT hr; + + ComPtr adapter; + hr = factory->EnumAdapters(adapter_idx, &adapter); + if (FAILED(hr)) + return false; + + adapter_caps &caps = adapter_info[adapter_idx]; + + DXGI_ADAPTER_DESC desc; + adapter->GetDesc(&desc); + + if (desc.VendorId != INTEL_VENDOR_ID) + return true; + + bool dgpu = desc.DedicatedVideoMemory > 512 * 1024 * 1024; + mfxIMPL impl = dgpu ? MFX_IMPL_HARDWARE : MFX_IMPL_HARDWARE2; + + caps.is_intel = true; + caps.is_dgpu = dgpu; + + return true; +} + +DWORD WINAPI TimeoutThread(LPVOID param) +{ + HANDLE hMainThread = (HANDLE)param; + + DWORD ret = WaitForSingleObject(hMainThread, 5000); + if (ret == WAIT_TIMEOUT) + TerminateProcess(GetCurrentProcess(), STATUS_TIMEOUT); + + CloseHandle(hMainThread); + + return 0; +} + +int main(void) +try { + ComPtr factory; + HRESULT hr; + + HANDLE hMainThread; + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), &hMainThread, 0, FALSE, + DUPLICATE_SAME_ACCESS); + DWORD threadId; + HANDLE hThread; + hThread = + CreateThread(NULL, 0, TimeoutThread, hMainThread, 0, &threadId); + CloseHandle(hThread); + + /* --------------------------------------------------------- */ + /* query qsv support */ + + hr = CreateDXGIFactory1(__uuidof(IDXGIFactory), (void **)&factory); + if (FAILED(hr)) + throw "CreateDXGIFactory1 failed"; + + uint32_t idx = 0; + while (get_adapter_caps(factory, idx++)) + ; + + for (auto &[idx, caps] : adapter_info) { + printf("[%u]\n", idx); + printf("is_intel=%s\n", caps.is_intel ? "true" : "false"); + printf("is_dgpu=%s\n", caps.is_dgpu ? "true" : "false"); + } + + return 0; +} catch (const char *text) { + printf("[error]\nstring=%s\n", text); + return 0; +} diff --git a/plugins/obs-qsv11/obs-qsv11-plugin-main.c b/plugins/obs-qsv11/obs-qsv11-plugin-main.c index c22d77038..dc6cd0935 100644 --- a/plugins/obs-qsv11/obs-qsv11-plugin-main.c +++ b/plugins/obs-qsv11/obs-qsv11-plugin-main.c @@ -54,7 +54,11 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include -#include "mfxsession.h" +#include +#include +#include +#include +#include "QSV_Encoder.h" OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("obs-qsv11", "en-US") @@ -66,27 +70,77 @@ MODULE_EXPORT const char *obs_module_description(void) extern struct obs_encoder_info obs_qsv_encoder; extern struct obs_encoder_info obs_qsv_encoder_tex; +struct adapter_info adapters[MAX_ADAPTERS] = {0}; +size_t adapter_count = 0; + bool obs_module_load(void) { - mfxIMPL impl = MFX_IMPL_HARDWARE_ANY | MFX_IMPL_VIA_D3D11; - mfxVersion ver = {{0, 1}}; - mfxSession session; - mfxStatus sts; + char *test_exe = os_get_executable_path_ptr("obs-qsv-test.exe"); + struct dstr caps_str = {0}; + os_process_pipe_t *pp = NULL; + config_t *config = NULL; - sts = MFXInit(impl, &ver, &session); - - if (sts == MFX_ERR_NONE) { - obs_register_encoder(&obs_qsv_encoder); - obs_register_encoder(&obs_qsv_encoder_tex); - MFXClose(session); - } else { - impl = MFX_IMPL_HARDWARE_ANY | MFX_IMPL_VIA_D3D9; - sts = MFXInit(impl, &ver, &session); - if (sts == MFX_ERR_NONE) { - obs_register_encoder(&obs_qsv_encoder); - MFXClose(session); - } + pp = os_process_pipe_create(test_exe, "r"); + if (!pp) { + blog(LOG_INFO, "Failed to launch the QSV test process I guess"); + goto fail; } + for (;;) { + char data[2048]; + size_t len = + os_process_pipe_read(pp, (uint8_t *)data, sizeof(data)); + if (!len) + break; + + dstr_ncat(&caps_str, data, len); + } + + if (dstr_is_empty(&caps_str)) { + blog(LOG_INFO, "Seems the QSV test subprocess crashed. " + "Better there than here I guess. " + "Let's just skip loading QSV then I suppose."); + goto fail; + } + + if (config_open_string(&config, caps_str.array) != 0) { + blog(LOG_INFO, "Couldn't open QSV configuration string"); + goto fail; + } + + const char *error = config_get_string(config, "error", "string"); + if (error) { + blog(LOG_INFO, "Error querying QSV support: %s", error); + goto fail; + } + + adapter_count = config_num_sections(config); + bool avc_supported = false; + + if (adapter_count > MAX_ADAPTERS) + adapter_count = MAX_ADAPTERS; + + for (size_t i = 0; i < adapter_count; i++) { + char section[16]; + snprintf(section, sizeof(section), "%d", (int)i); + + struct adapter_info *adapter = &adapters[i]; + adapter->is_intel = config_get_bool(config, section, "is_intel"); + adapter->is_dgpu = config_get_bool(config, section, "is_dgpu"); + + avc_supported |= adapter->is_intel; + } + + if (avc_supported) { + obs_register_encoder(&obs_qsv_encoder_tex); + obs_register_encoder(&obs_qsv_encoder); + } + +fail: + config_close(config); + dstr_free(&caps_str); + os_process_pipe_destroy(pp); + bfree(test_exe); + return true; } diff --git a/plugins/obs-qsv11/obs-qsv11.c b/plugins/obs-qsv11/obs-qsv11.c index 69698dc10..00120c04b 100644 --- a/plugins/obs-qsv11/obs-qsv11.c +++ b/plugins/obs-qsv11/obs-qsv11.c @@ -84,6 +84,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct obs_qsv { obs_encoder_t *encoder; + enum qsv_codec codec; + qsv_param_t params; qsv_t *context; @@ -711,54 +713,15 @@ static HANDLE get_lib(const char *lib) typedef HRESULT(WINAPI *CREATEDXGIFACTORY1PROC)(REFIID, void **); -static bool is_intel_gpu_primary() +static bool is_intel_gpu_primary(void) { - HMODULE dxgi = get_lib("DXGI.dll"); - CREATEDXGIFACTORY1PROC create_dxgi; - IDXGIFactory1 *factory; - IDXGIAdapter *adapter; - DXGI_ADAPTER_DESC desc; - HRESULT hr; + struct obs_video_info ovi; + obs_get_video_info(&ovi); - if (!dxgi) { - return false; - } - create_dxgi = (CREATEDXGIFACTORY1PROC)GetProcAddress( - dxgi, "CreateDXGIFactory1"); - - if (!create_dxgi) { - blog(LOG_INFO, "Failed to load D3D11/DXGI procedures"); - return false; - } - - hr = create_dxgi(&IID_IDXGIFactory1, &factory); - if (FAILED(hr)) { - blog(LOG_INFO, "CreateDXGIFactory1 failed"); - return false; - } - - hr = factory->lpVtbl->EnumAdapters(factory, 0, &adapter); - factory->lpVtbl->Release(factory); - if (FAILED(hr)) { - blog(LOG_INFO, "EnumAdapters failed"); - return false; - } - - hr = adapter->lpVtbl->GetDesc(adapter, &desc); - adapter->lpVtbl->Release(adapter); - if (FAILED(hr)) { - blog(LOG_INFO, "GetDesc failed"); - return false; - } - - /*check whether adapter 0 is Intel*/ - if (desc.VendorId == 0x8086) { - return true; - } else { - return false; - } + return adapters[ovi.adapter].is_intel; } + static void *obs_qsv_create_tex(obs_data_t *settings, obs_encoder_t *encoder) { if (!is_intel_gpu_primary()) {