Просмотр исходного кода

obs-qsv11: Add startup process to test QSV support

This simplifies encoder initialization and prevents it from requiring
any sort of unnecessary Direct3D or encoder initiazation tests while OBS
is actually running.
Jim 3 лет назад
Родитель
Сommit
eaf243bca7

+ 3 - 1
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)

+ 15 - 97
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<bool> 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<IDXGIDevice> dxgiDevice;
-		if (SUCCEEDED(pDevice->QueryInterface<IDXGIDevice>(
-			    dxgiDevice.GetAddressOf()))) {
-			Microsoft::WRL::ComPtr<IDXGIAdapter> 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) {

+ 9 - 0
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 */

+ 11 - 0
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)

+ 126 - 0
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 <util/windows/ComPtr.hpp>
+
+#include <dxgi.h>
+#include <d3d11.h>
+#include <d3d11_1.h>
+
+#include <string>
+#include <map>
+
+#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<uint32_t, adapter_caps> 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<IDXGIAdapter> 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<IDXGIFactory> 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;
+}

+ 70 - 16
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 <obs-module.h>
-#include "mfxsession.h"
+#include <util/config-file.h>
+#include <util/platform.h>
+#include <util/pipe.h>
+#include <util/dstr.h>
+#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;
+
+	pp = os_process_pipe_create(test_exe, "r");
+	if (!pp) {
+		blog(LOG_INFO, "Failed to launch the QSV test process I guess");
+		goto fail;
+	}
 
-	sts = MFXInit(impl, &ver, &session);
+	for (;;) {
+		char data[2048];
+		size_t len =
+			os_process_pipe_read(pp, (uint8_t *)data, sizeof(data));
+		if (!len)
+			break;
 
-	if (sts == MFX_ERR_NONE) {
-		obs_register_encoder(&obs_qsv_encoder);
+		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);
-		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);
-		}
+		obs_register_encoder(&obs_qsv_encoder);
 	}
 
+fail:
+	config_close(config);
+	dstr_free(&caps_str);
+	os_process_pipe_destroy(pp);
+	bfree(test_exe);
+
 	return true;
 }

+ 7 - 44
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;
-
-	if (!dxgi) {
-		return false;
-	}
-	create_dxgi = (CREATEDXGIFACTORY1PROC)GetProcAddress(
-		dxgi, "CreateDXGIFactory1");
-
-	if (!create_dxgi) {
-		blog(LOG_INFO, "Failed to load D3D11/DXGI procedures");
-		return false;
-	}
+	struct obs_video_info ovi;
+	obs_get_video_info(&ovi);
 
-	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()) {