OBS Studio 窗口采集game-capture注入之OpenGL与D3D11的GPU资源进行互操作

OBS Studio 窗口采集game-capture注入之OpenGL与D3D11的GPU资源进行互操作


OpenGL与D3D11的GPU资源进行互操作哈 !!!

前言

云渲染客户端 采集的工作 所以分析OBS中源码


提示:以下是本篇文章正文内容,下面案例可供参考

一、 OBS中三种采集说明

OBS中采集有 桌面、窗口和游戏采集

1、桌面

dxgi

2、窗口

METHOD_AUTO, 
METHOD_BITBLT, // 位图
METHOD_WGC,  // 从GPU中抓取数据

3、游戏

其中游戏采集是独立一套注入的工具 ,因为游戏本身对渲染的帧数要求非常高, 使用,使用注入的方法,主要使用GPU资源共享的模式, 达到渲染流畅的

这个OpenGL提供接口与D3D11进行资源进行互操作 WGL_NV_DX_interop2.txt

二、游戏窗口采集 graphics-hook 的源码分析

1、 环境初始化工作

BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID unused1)
{
	if (reason == DLL_PROCESS_ATTACH) {
		wchar_t name[MAX_PATH];

		dll_inst = hinst;

		if (!init_dll()) {
			DbgOut("[OBS] Duplicate hook library");
			return false;
		}

		HANDLE cur_thread;
		bool success = DuplicateHandle(GetCurrentProcess(),
					       GetCurrentThread(),
					       GetCurrentProcess(), &cur_thread,
					       SYNCHRONIZE, false, 0);

		if (!success)
			DbgOut("[OBS] Failed to get current thread handle");
		// 1. 信号是obs中进程发送要控制图形进程的指令
		if (!init_signals()) {
			return false;
		}
		// 2. 获取系统的动态库的路径
		if (!init_system_path()) {
			return false;
		}
		// 3. 与obs控制进程同享一块内存地址 一些内存
		if (!init_hook_info()) {
			return false;
		}
		if (!init_mutexes()) {
			return false;
		}

		/* this prevents the library from being automatically unloaded
		 * by the next FreeLibrary call */
		GetModuleFileNameW(hinst, name, MAX_PATH);
		LoadLibraryW(name);

		capture_thread = CreateThread(
			NULL, 0, (LPTHREAD_START_ROUTINE)main_capture_thread,
			(LPVOID)cur_thread, 0, 0);
		if (!capture_thread) {
			CloseHandle(cur_thread);
			return false;
		}

	} else if (reason == DLL_PROCESS_DETACH) {
		if (!dup_hook_mutex) {
			return true;
		}

		if (capture_thread) {
			stop_loop = true;
			WaitForSingleObject(capture_thread, 300);
			CloseHandle(capture_thread);
		}

		free_hook();
	}

	(void)unused1;
	return true;
}

static inline bool init_signals(void)
{
	DWORD pid = GetCurrentProcessId();
	//1 、从新启动
	signal_restart = init_event(EVENT_CAPTURE_RESTART, pid);
	if (!signal_restart) {
		return false;
	}
	// 2、停止的信令
	signal_stop = init_event(EVENT_CAPTURE_STOP, pid);
	if (!signal_stop) {
		return false;
	}
	// 3、准备的信令
	signal_ready = init_event(EVENT_HOOK_READY, pid);
	if (!signal_ready) {
		return false;
	}
	// 4、退出的信令
	signal_exit = init_event(EVENT_HOOK_EXIT, pid);
	if (!signal_exit) {
		return false;
	}
	//5、初始化的信令
	signal_init = init_event(EVENT_HOOK_INIT, pid);
	if (!signal_init) {
		return false;
	}

	return true;
}

static inline bool init_hook(HANDLE thread_handle)
{
	// 等待一下
	wait_for_dll_main_finish(thread_handle);

	_snwprintf(keepalive_name, sizeof(keepalive_name) / sizeof(wchar_t),
		   L"%s%lu", WINDOW_HOOK_KEEPALIVE, GetCurrentProcessId());

	// obs要获取图形进程数据发送信令指令通过管道发送指令的哈
	init_pipe();

	init_dummy_window_thread();
	log_current_process();
	// 发送 从新启动信令
	SetEvent(signal_restart);
	return true;
}

2、 主要attempt_hook中进行hook操作抓取数据

看OpenGL中hook


bool hook_gl(void)
{
	void *wgl_dc_proc;
	void *wgl_slb_proc;
	void *wgl_sb_proc;

	gl = get_system_module("opengl32.dll");
	if (!gl) {
		return false;
	}

	/* "life is feudal: your own" somehow uses both opengl and directx at
	 * the same time, so blacklist it from capturing opengl */
	const char *process_name = get_process_name();
	if (_strcmpi(process_name, "yo_cm_client.exe") == 0 ||
	    _strcmpi(process_name, "cm_client.exe") == 0) {
		hlog("Ignoring opengl for game: %s", process_name);
		return true;
	}

	if (!gl_register_window()) {
		return true;
	}
	// win的窗口的api的两个缓冲区的地址hook
	wgl_dc_proc = base_get_proc("wglDeleteContext");
	wgl_slb_proc = base_get_proc("wglSwapLayerBuffers");
	wgl_sb_proc = base_get_proc("wglSwapBuffers");

	DetourTransactionBegin();

	RealSwapBuffers = SwapBuffers;
	DetourAttach((PVOID *)&RealSwapBuffers, hook_swap_buffers);
	if (wgl_dc_proc) {
		RealWglDeleteContext = (PFN_WglDeleteContext)wgl_dc_proc;
		DetourAttach((PVOID *)&RealWglDeleteContext,
			     hook_wgl_delete_context);
	}
	if (wgl_slb_proc) {
		RealWglSwapLayerBuffers = (PFN_WglSwapLayerBuffers)wgl_slb_proc;
		DetourAttach((PVOID *)&RealWglSwapLayerBuffers,
			     hook_wgl_swap_layer_buffers);
	}
	if (wgl_sb_proc) {
		RealWglSwapBuffers = (PFN_WglSwapBuffers)wgl_sb_proc;
		DetourAttach((PVOID *)&RealWglSwapBuffers,
			     hook_wgl_swap_buffers);
	}

	const LONG error = DetourTransactionCommit();
	const bool success = error == NO_ERROR;
	if (success) {
		hlog("Hooked SwapBuffers");
		if (RealWglDeleteContext)
			hlog("Hooked wglDeleteContext");
		if (RealWglSwapLayerBuffers)
			hlog("Hooked wglSwapLayerBuffers");
		if (RealWglSwapBuffers)
			hlog("Hooked wglSwapBuffers");
		hlog("Hooked GL");
	} else {
		RealSwapBuffers = NULL;
		RealWglDeleteContext = NULL;
		RealWglSwapLayerBuffers = NULL;
		RealWglSwapBuffers = NULL;
		hlog("Failed to attach Detours hook: %ld", error);
	}

	return success;
}

3、 gl_shtex_init 初始化d3d11与OpenGL的GPU资源访问


static bool gl_shtex_init(HWND window)
{
	// 1、 这个没有看懂  哭了
	if (!gl_shtex_init_window()) {
		return false;
	}
	// 2、创建设备与GPU交换 、交换链
	if (!gl_shtex_init_d3d11()) {
		return false;
	}
	// 3、创建ID3D11Texture2D 并设置同享模式 用于其他进程可以发送 跨进程访问   -》 这里主要是obs显示访问这个GPU的内存
	if (!gl_shtex_init_d3d11_tex()) {
		return false;
	}
	// 4、把d3d11的GPU的资源可以给OpenGL访问 
	if (!gl_shtex_init_gl_tex()) {
		return false;
	}
	// 5、分配 fbo的GPU的内存
	if (!gl_init_fbo()) {
		return false;
	}
	// 6、 把同享地址设置到共享变量中去 data.shtex_info、 data.handle
	if (!capture_init_shtex(&data.shtex_info, window, data.cx, data.cy,
				data.format, true, (uintptr_t)data.handle)) {
		return false;
	}

	hlog("gl shared texture capture successful");
	return true;
}

4、 进行拷贝GPU数据gl_shtex_capture


static void gl_shtex_capture(void)
{
	GLint last_fbo;
	GLint last_tex;

	jimglDXLockObjectsNV(data.gl_device, 1, &data.gl_dxobj);

	glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &last_fbo);
	if (gl_error("gl_shtex_capture", "failed to get last fbo")) {
		return;
	}

	glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex);
	if (gl_error("gl_shtex_capture", "failed to get last texture")) {
		return;
	}

	gl_copy_backbuffer(data.texture);

	glBindTexture(GL_TEXTURE_2D, last_tex);
	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, last_fbo);

	jimglDXUnlockObjectsNV(data.gl_device, 1, &data.gl_dxobj);

	IDXGISwapChain_Present(data.dxgi_swap, 0, 0);
	//创建渲染目标视图
	/*ID3D11Texture2D *pBackBuffer = NULL;
	IDXGISwapChain_GetBuffer(data.dxgi_swap, 0, __uuidof(ID3D11Texture2D),
				 (LPVOID *)&pBackBuffer);*/
}

三、 OpenGL与D3D11资源共享 进行互操作

神奇扩展WGL_NV_DX_interop的 出现,使得OpenGL可以正式与D3D进行互操作

OpenGL地址


 //Example: Render to Direct3D 11 backbuffer with openGL.
    
    // create D3D11 device, context and swap chain.
    ID3D11Device *device;
    ID3D11DeviceContext *devCtx;
    IDXGISwapChain *swapChain;
    
    DXGI_SWAP_CHAIN_DESC scd;
    
    <set appropriate swap chain parameters in scd>
    
    hr = D3D11CreateDeviceAndSwapChain(NULL,                        // pAdapter
                                       D3D_DRIVER_TYPE_HARDWARE,    // DriverType
                                       NULL,                        // Software
                                       0,                           // Flags (Do not set D3D11_CREATE_DEVICE_SINGLETHREADED)
                                       NULL,                        // pFeatureLevels
                                       0,                           // FeatureLevels
                                       D3D11_SDK_VERSION,           // SDKVersion
                                       &scd,                        // pSwapChainDesc
                                       &swapChain,                  // ppSwapChain
                                       &device,                     // ppDevice
                                       NULL,                        // pFeatureLevel
                                       &devCtx);                    // ppImmediateContext

    // Fetch the swapchain backbuffer
    ID3D11Texture2D *dxColorbuffer;
    swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *)&dxColorbuffer);
    
    // Create depth stencil texture
    ID3D11Texture2D *dxDepthBuffer;
    D3D11_TEXTURE2D_DESC depthDesc;
    depthDesc.Usage = D3D11_USAGE_DEFAULT;
   // <set other depthDesc parameters appropriately>
    
    // Create Views
    ID3D11RenderTargetView *colorBufferView;
    D3D11_RENDER_TARGET_VIEW_DESC rtd;
    <set rtd parameters appropriately>
    device->CreateRenderTargetView(dxColorbuffer, &rtd, &colorBufferView);
    
    ID3D11DepthStencilView *depthBufferView;
    D3D11_DEPTH_STENCIL_VIEW_DESC dsd;
    <set dsd parameters appropriately>
    device->CreateDepthStencilView(dxDepthBuffer, &dsd, &depthBufferView);
    
    // Attach back buffer and depth texture to redertarget for the device.
    devCtx->OMSetRenderTargets(1, &colorBufferView, depthBufferView);
    
    // Register D3D11 device with GL
    HANDLE gl_handleD3D;
    // 把D3D设备注册给OpenGL
    gl_handleD3D = wglDXOpenDeviceNV(device);

    // register the Direct3D color and depth/stencil buffers as
    // renderbuffers in opengl
    GLuint gl_names[2];
    HANDLE gl_handles[2];

    glGenRenderbuffers(2, gl_names);
	 把D3Drender target注册成OpenGL纹理对象
    gl_handles[0] = wglDXRegisterObjectNV(gl_handleD3D, dxColorBuffer,
                                          gl_names[0],
                                          GL_RENDERBUFFER,
                                          WGL_ACCESS_READ_WRITE_NV);

    gl_handles[1] = wglDXRegisterObjectNV(gl_handleD3D, dxDepthBuffer,
                                          gl_names[1],
                                          GL_RENDERBUFFER,
                                          WGL_ACCESS_READ_WRITE_NV);

    // attach the Direct3D buffers to an FBO
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                              GL_RENDERBUFFER, gl_names[0]);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                              GL_RENDERBUFFER, gl_names[1]);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
                              GL_RENDERBUFFER, gl_names[1]);
//  D3D和OpenGL渲到同一个render target
    while (!done) {
    //  现在纹理就可以当成普通的OpenGL纹理来用了

//  和平常一样进行D3D渲染
          <direct3d renders to the render targets>

          // lock the render targets for GL access
          //  锁定render target,交给OpenGL
          wglDXLockObjectsNVX(handleD3D, 2, gl_handles);
//  和平常一样进行OpenGL渲染
          <opengl renders to the render targets>

          // unlock the render targets
          wglDXUnlockObjectsNVX(handleD3D, 2, gl_handles);

          <direct3d renders to the render targets and presents
           the results on the screen>
    }
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值