一种基于GPU纹理共享来获取D3D12游戏源图像的方法及系统与流程

文档序号:12120218阅读:1630来源:国知局

本发明涉及计算机领域,尤其涉及一种基于GPU纹理共享来获取D3D12游戏源图像的方法及系统。



背景技术:

随着2015年7月,微软Windows 10操作系统的发布,其全新一代的显卡图形编程接口D3D12(DirectX 12)也随之发布,随之而来的是利用D3D12来开发的游戏也越来越多;《奇点灰烬》是全球第一部用D3D12所开发的游戏。当游戏玩家在进行D3D12类型的游戏的时候,可能希望通过其他的工具来进行直播(列如在国外的Twitch或者国内的斗鱼等平台上进行直播)那么就需要通过这些直播工具来获取D3D12类型的游戏图像。然而由于D3D12刚出现不就,并且其所提供的面向开发人员的编程接口相比之前的DirectX版本(例如DirectX 11)等更为抽象、更为底层。所以目前国内各大直播平台的直播助手均没有获取D3D12游戏图像的功能,所以本文所阐述的就是在斗鱼平台的直播工具,斗鱼直播伴侣中获取D3D12游戏源图像的方法。



技术实现要素:

本发明要解决的技术问题在于针对现有技术中缺乏获取D3D12游戏图像功能的缺陷,提供一种基于GPU纹理共享来获取D3D12游戏源图像的方法及系统。

本发明解决其技术问题所采用的技术方案是:

本发明提供一种基于GPU纹理共享来获取D3D12游戏源图像的方法,包括以下步骤:

S1、将获取游戏图像的所有方法封装到一个动态链接库文件中,该动态链接库文件称为消息钩子模块,将消息钩子模块注入待获取图像的游戏进程中,使游戏运行时消息钩子模块同时运行;

S2、通过消息钩子模块获取游戏进程的多个数据交换接口,包括:交换链接口、命令队列接口、入口接口和栅栏接口;

S3、通过数据交换接口获取游戏的后台缓冲区的图像参数,并初始化用于创建资源的资源描述符,根据图像参数和资源描述符创建游戏图像的纹理资源,并将该纹理资源设置成跨进程的共享纹理资源;

S4、根据创建的共享纹理资源,将游戏图像拷贝到该共享纹理资源中,完成对D3D12游戏源图像的获取。

进一步地,本发明的步骤S1中消息钩子模块为可执行模块,通过windows系统的API函数将其注入到待获取图像的游戏进程中。

进一步地,本发明的步骤S2中的多个数据交换接口的获取方法具体为:

S21、通过消息钩子模块的内联钩子获取交换链接口;

S22、根据交换链接口的地址值加上偏移值,得到创建该交换链接口的命令队列接口的地址值,进而在该位置获取到命令队列接口;

S23、通过调用交换链接口的GetDevice方法,获取当前游戏进程的入口接口;

S24、通过调用入口接口的CreateFence方法创建栅栏接口。

进一步地,本发明的步骤S21中内联钩子用于在游戏进程中,通过消息钩子模块的PresentHook函数替换交换链接口的Present函数。

进一步地,本发明的步骤S3中创建游戏图像的纹理资源的方法具体包括:

S31、调用交换链接口的GetDesc方法,获取当前游戏的后台缓冲区的图像参数,包括宽度、高度和像素格式;

S32、初始化D3D12的API中的资源描述符,设置资源描述符的Dimension字段表示待创建的纹理资源为2D纹理,然后设置资源描述符的Width字段为当前游戏缓冲区的宽度,将资源描述符的Height字段设置为当前游戏缓冲区的高度,将资源描述符的Format字段设置为当前游戏缓冲区的像素格式,进而创建一个纹理资源;

S33、通过入口接口的CreateSharedHandle方法将纹理资源设置成跨进程的共享纹理资源。

进一步地,本发明的步骤S4中拷贝图像的方法具体包括:

S41、获取当前游戏后台缓冲区的图像资源接口;

S42、将后台缓冲区的图像资源的状态变换为可拷贝源状态;

S43、将游戏后台缓冲区的一帧图像的图像资源通过图像资源接口拷贝到共享纹理资源中;

S44、将拷贝后的图像资源的状态变换为显示状态;

S45、通过命令队列接口执行存储的命令队列表;

S46、通过命令队列接口通知栅栏接口,当命令队列表执行完成后,向栅栏接口所绑定的完成事件发送信号,实现GPU和CPU的同步,并继续进行下一帧图像的拷贝,直到完成所有图像的拷贝。

进一步地,本发明的步骤S41中获取当前游戏后台缓冲区的图像资源接口的方法具体为:

通过调用获取到的交换链接口的GetCurrentBackBufferIndex方法,获取当前后台缓冲区的图像索引;然后根据该索引调用交换链接口的GetBuffer方法,获取当前后台缓冲区的图像资源接口。

进一步地,本发明的步骤S46中实现GPU和CPU同步的方法具体为:

通过调用命令队列接口的Signal方法来通知创建的栅栏接口,当命令列表执行完后,向栅栏接口所绑定的完成事件发送信号,唤醒了等待该完成信号的CPU,从而实现了CPU与GPU的同步。

进一步地,本发明的步骤S4中获取游戏图像的方法具体为:

通过获取到的入口接口,调用该接口的CreateCommandList方法,来创建一个命令队列,通过执行该命令队列来获取游戏源图像的任务。

本发明提供一种基于GPU纹理共享来获取D3D12游戏源图像的系统,包括:

消息钩子模块注入单元,用于将获取游戏图像的所有方法封装到一个动态链接库文件中,该动态链接库文件称为消息钩子模块,将消息钩子模块注入待获取图像的游戏进程中,使游戏运行时消息钩子模块同时运行;

接口获取单元,用于通过消息钩子模块获取游戏进程的多个数据交换接口,包括:交换链接口、命令队列接口、入口接口和栅栏接口;

共享纹理资源创建单元,用于通过数据交换接口获取游戏的后台缓冲区的图像参数,并初始化用于创建资源的资源描述符,根据图像参数和资源描述符创建游戏图像的纹理资源,并将该纹理资源设置成跨进程的共享纹理资源;

游戏图像拷贝单元,用于根据创建的共享纹理资源,将游戏图像拷贝到该共享纹理资源中,完成对D3D12游戏源图像的获取。

本发明产生的有益效果是:本发明的基于GPU纹理共享来获取D3D12游戏源图像的方法,通过Windows消息钩子的方式进入到游戏进程中,获取后台缓冲区的图像参数并创建共享纹理资源,再将游戏图像拷贝到共享纹理资源中;该方法采用的是GPU纹理共享的方法,而没有直接将游戏图像的像素读取到内存中的耗时方法,从而并没有对游戏的性能产生影响。

附图说明

下面将结合附图及实施例对本发明作进一步说明,附图中:

图1是本发明实施例的方法流程图。

具体实施方式

为了使本发明的目的、技术方案及优点更加清楚明白,以下结合附图及实施例,对本发明进行进一步详细说明。应当理解,此处所描述的具体实施例仅用以解释本发明,并不用于限定本发明。

如图1所示,本发明实施例的基于GPU纹理共享来获取D3D12游戏源图像的方法,包括以下步骤:

S1、将获取游戏图像的所有方法封装到一个动态链接库文件中,该动态链接库文件称为消息钩子模块,将消息钩子模块注入待获取图像的游戏进程中,使游戏运行时消息钩子模块同时运行;消息钩子模块为可执行模块,通过windows系统的API函数将其注入到待获取图像的游戏进程中。

S2、通过消息钩子模块获取游戏进程的多个数据交换接口,包括:交换链接口、命令队列接口、入口接口和栅栏接口;

步骤S2中的多个数据交换接口的获取方法具体为:

S21、通过消息钩子模块的内联钩子获取交换链接口;

内联钩子用于在游戏进程中,通过消息钩子模块的PresentHook函数替换交换链接口的Present函数。

S22、根据交换链接口的地址值加上偏移值,得到创建该交换链接口的命令队列接口的地址值,进而在该位置获取到命令队列接口;

S23、通过调用交换链接口的GetDevice方法,获取当前游戏进程的入口接口;

S24、通过调用入口接口的CreateFence方法创建栅栏接口。

S3、通过数据交换接口获取游戏的后台缓冲区的图像参数,并初始化用于创建资源的资源描述符,根据图像参数和资源描述符创建游戏图像的纹理资源,并将该纹理资源设置成跨进程的共享纹理资源;

步骤S3中创建游戏图像的纹理资源的方法具体包括:

S31、调用交换链接口的GetDesc方法,获取当前游戏的后台缓冲区的图像参数,包括宽度、高度和像素格式;

S32、初始化D3D12的API中的资源描述符,设置资源描述符的Dimension字段表示待创建的纹理资源为2D纹理,然后设置资源描述符的Width字段为当前游戏缓冲区的宽度,将资源描述符的Height字段设置为当前游戏缓冲区的高度,将资源描述符的Format字段设置为当前游戏缓冲区的像素格式,进而创建一个纹理资源;

S33、通过入口接口的CreateSharedHandle方法将纹理资源设置成跨进程的共享纹理资源。

S4、根据创建的共享纹理资源,将游戏图像拷贝到该共享纹理资源中,完成对D3D12游戏源图像的获取。

步骤S4中拷贝图像的方法具体包括:

S41、获取当前游戏后台缓冲区的图像资源接口;

获取当前游戏后台缓冲区的图像资源接口的方法具体为:

通过调用获取到的交换链接口的GetCurrentBackBufferIndex方法,获取当前后台缓冲区的图像索引;然后根据该索引调用交换链接口的GetBuffer方法,获取当前后台缓冲区的图像资源接口。

S42、将后台缓冲区的图像资源的状态变换为可拷贝源状态;

S43、将游戏后台缓冲区的一帧图像的图像资源通过图像资源接口拷贝到共享纹理资源中;

S44、将拷贝后的图像资源的状态变换为显示状态;

S45、通过命令队列接口执行存储的命令队列表;

S46、通过命令队列接口通知栅栏接口,当命令队列表执行完成后,向栅栏接口所绑定的完成事件发送信号,实现GPU和CPU的同步,并继续进行下一帧图像的拷贝,直到完成所有图像的拷贝。

实现GPU和CPU同步的方法具体为:

通过调用命令队列接口的Signal方法来通知创建的栅栏接口,当命令列表执行完后,向栅栏接口所绑定的完成事件发送信号,唤醒了等待该完成信号的CPU,从而实现了CPU与GPU的同步。

获取游戏图像的方法具体为:

通过获取到的入口接口,调用该接口的CreateCommandList方法,来创建一个命令队列,通过执行该命令队列来获取游戏源图像的任务。

本发明实施例的基于GPU纹理共享来获取D3D12游戏源图像的系统,用于实现本发明实施例的基于GPU纹理共享来获取D3D12游戏源图像的放,包括:

消息钩子模块注入单元,用于将获取游戏图像的所有方法封装到一个动态链接库文件中,该动态链接库文件称为消息钩子模块,将消息钩子模块注入待获取图像的游戏进程中,使游戏运行时消息钩子模块同时运行;

接口获取单元,用于通过消息钩子模块获取游戏进程的多个数据交换接口,包括:交换链接口、命令队列接口、入口接口和栅栏接口;

共享纹理资源创建单元,用于通过数据交换接口获取游戏的后台缓冲区的图像参数,并初始化用于创建资源的资源描述符,根据图像参数和资源描述符创建游戏图像的纹理资源,并将该纹理资源设置成跨进程的共享纹理资源;

游戏图像拷贝单元,用于根据创建的共享纹理资源,将游戏图像拷贝到该共享纹理资源中,完成对D3D12游戏源图像的获取。

在本发明的另一个具体实施例中,结合具体实现过程中的具体接口和函数进行说明。过程中涉及到的名词解释如下:

ID3D12Device:

该接口为D3D12的入口接口,该接口是对一块显卡的抽象。可以用来创建资源接口,创建同步栅栏接口等功能。

IDXGISwapChain:

该接口代表一个交换链接口,其最重要的功能就是该接口提供的Present方法,将后台缓冲区中的图像提交到前台显示,也就是在显示器上显示。

ID3D12Resource:

该接口代表一个D3D12的资源,资源包括纹理资源,Buffer(缓冲区)资源等;与其前一个版本(D3D11)不同的是,在D3D11中纹理资源和Buffer资源有相应的接口类型来区分,而在D3D12中纹理资源和Buffer资源的接口统一为ID3D12Resource接口。

ID3D12CommandAllocator:

该接口代表一个命令分配器,用于创建图像命令列表。

ID3D12GraphicsCommandList:

该接口代表一个命令列表,用于将渲染指令存储到该队列中,等待执行。该接口类似于D3D11中的ID3D11DeviceContext接口,用于向编程人员提供设置顶点、设置视口、设置裁剪区域、绘制图元等等渲染指令的编程接口。但是与ID3D11DeviceContext的最大不同是,在D3D12中绘制指令的执行全部都是异步的,也就是说当调用ID3D12GraphicsCommandList的接口时,只不过是将这些接口所代表的命令存储到命令队列中。

ID3D12CommandQueue:

该接口代表一个命令队列,用于执行ID3D12GraphicsCommandList命令队列中的所有带执行的命令。

ID3D12Fence:

该接口是代表一个栅栏接口,用于CPU和GPU的同步。在D3D12以前的所有版本中,调用设备(D3D9的IDirect3DDevice9或者D3D11的交换链接口)的Present方法,进行图像呈现的时候,CPU都会进行同GPU的同步。也就是在调用Present方法时,CPU会等待GPU将当前渲染的帧渲染完成,才能继续往下执行。然而,在D3D12中调用交换链的Present方法时,CPU不会进行阻塞,也就是不会进行同GPU的同步。所以,就需要ID3D12Fence接口,通过绑定一个完成事件信号,当GPU绘制完成后,该信号为置信状态,唤醒CPU继续向下执行。

消息钩子注入可执行模块:

因为游戏是一个进程,而外部直播工具又是另外的一个进程,若要获取游戏的图像画面,那么就必须将一个可执行模块(DLL)注入到游戏中执行。在Windows操作系统下,可以调用SetWindowsHookEx这个API函数,将一个DLL注入到指定的进程当中。

内联钩子:

内联钩子的原理就是,将一个函数的前5个字节替换成一个跳转指令,从而在跳转到替换的地址处执行代码。

通过对上面关键名词的解释,下面阐述斗鱼直播伴侣中获取D3D12游戏的主要步骤:

1)通过消息钩子,注入模块;

为了获取D3D12游戏源图像,必须首先就获取游戏图像的所有方法封装到一个动态链接库(DLL)文件当中,在斗鱼直播伴侣中,该DLL模块的名称是hookd3d9.dll。然后通过调用Windows系统API函数SetWindowsHookEx函数,将hookd3d9.dll模块注入到游戏当中。这样,被注入的游戏进程中,便有了hookd3d9.dll这个模块在运行。成功后,便进入下面的步骤2)。

2)通过内联钩子,替换交换链的Present函数;

当hookd3d9.dll模块注入到游戏中之后,首先要做的就是找到D3D12的交换链接口IDXGISwapChain。因为只有找到该接口后,才能获取到D3D12的设备接口ID3D12Device,命令队列接口ID3D12CommandQueue。通过内联钩子,用hookd3d9.dll模块中的PresentHook函数来勾住IDXGISwapChain的Present函数,当游戏进程中调用了IDXGISwapChain的Present函数之后,便会跳转到hookd3d9.dll模块中的PresentHook函数中来。后面的步骤3)到步骤7)都是在PresentHook中执行的。

3)获取ID3D12CommandQueue接口;

通过上面的步骤2),在hookd3d9.dll模块中,已经获取到了游戏进程的IDXGISwapChain交换链接口,现在所需要的就是要获取创建该交换链的命令队列ID3D12CommandQueue接口。由于笔者在现有的接口中没有发现获取交换链所对应的命令队列接口,后来经过研究发现,在32位应用程序下,在IDXGISwapChain地址加上0x5BC的偏移处所存放的内容,正好就是创建该交换链的命令队列所在的地址值。通过这个方法,就成功的获取到了命令队列接口。

4)获取ID3D12Device接口;

通过上面的步骤2)获取到的IDXGISwapChain交换链接口,通过调用该接口所提供的GetDevice方法,便可以获取到当前游戏进程的ID3D12Devcie接口,通过获取到的接口,进入到下面的步骤5)

5)创建ID3D12GraphicsCommandList命令队列;

通过步骤4)中获取到的ID3D12Device接口,便可以调用该接口的CreateCommandList方法,来创建一个ID3D12GraphicsCommandList命令队列,来执行获取游戏源图像的任务。

6)创建ID3D12Fence栅栏接口;

通过步骤4)中获取到的ID3D12Device接口,通过调用该接口的CreateFence方法来创建一个栅栏接口,然后调用Windows API CreateEvent函数来创建一个事件,并通过调用ID3D12Fence接口所提供的SetEventOnCompletion方法,将创建的完成事件同栅栏接口绑定。

7)创建共享的ID3D12Resource纹理资源;

通过步骤4)中获取到的ID3D12Device接口,通过该接口提供的CreateCommittedResource方法来创建一个共享的纹理资源。具体的做法是

1.调用步骤2)中获取到的交换链接口的GetDesc方法,获取当前游戏的后台缓冲区宽度、高度、以及像素格式。

2.初始化D3D12_RESOURCE_DESC描述符;

该描述符是D3D12API中所提供的用于创建资源的结构体,要创建一个共享纹理资源,那么必须将该描述符的Dimension字段设置为D3D12_RESOURCE_DIMENSION_TEXTURE2D用于表示带创建的资源是一个2D纹理,然后设置该描述符的Width字段为当前游戏缓冲区的宽度,将描述符的Height字段设置为当前游戏缓冲区的高度,将Format字段设置为当前游戏缓冲区的像素格式。然后调用CreateCommittedResource来创建一个纹理资源。

3.通过上面的步骤2,已经创建了一个纹理资源,但是该纹理资源目前还只是一个普通的纹理,并不能够进行跨进程共享,也就是在直播伴侣中无法打开该纹理进行显示。所以在这个步骤中通过调用ID3D12Device接口的CreateSharedHandle方法,通过一个名称(斗鱼直播伴侣中通过该名称来打开该纹理,从而在斗鱼直播伴侣中显示该游戏的图像),来将上面所创建的普通纹理资源设置成一个可以跨进程使用的共享纹理资源。创建完共享的纹理资源后,便可以进入到下面的步骤8)。

8)拷贝图像;

通过步骤7)中创建的共享纹理,下面便可以通过该纹理,将游戏图像拷贝到该纹理中,从而实现获取D3D12游戏图像的功能。具体的步骤如下

1.获取当前游戏后台缓冲区图像资源;

通过调用步骤2)中获取到的交换链接口的GetCurrentBackBufferIndex方法,获取当前后台缓冲区的图像索引;然后根据该索引通过调用交换链的GetBuffer方法,获取当前后台缓冲区图像资源的ID3D12Resource接口

2.进行资源状态变换;

通过上面的步骤,获取到当前游戏后台缓冲去资源接口后,需要对该资源进行状态变更。因为目前获取到的资源,其状态为显示状态(D3D12_RESOURCE_STATE_PRESENT)该状态下的资源是不能够进行拷贝的,所以要将目前的显示状态变更为可拷贝源状态(D3D12_RESOURCE_STATE_COPY_SOURCE)。通过调用步骤5)中创建的ID3D12GraphicsCommandList接口的ResourceBarrier方法,来实现将后台缓冲区资源的状态从显示状态变更为拷贝源状态。

3.拷贝资源;

通过上面的步骤,将后台缓冲区资源状态变更为拷贝源状态后,调用ID3D12GraphicsCommandList的CopyResource接口,将游戏后台缓冲区资源拷贝到步骤7)所创建的共享纹理资源当中,实现获取游戏图像的功能。

4.进行资源状态复位;

通过上面的步骤,已经成功的将游戏图像拷贝到了步骤7)中所创建的共享纹理资源当中了,但是由于在步骤2中改变了游戏当前后台缓冲区资源的状态,如果不进行状态的恢复,那么就会影响游戏的正常显示。所以该步骤执行步骤2相反的过程,也就是将当前游戏后台缓冲区资源的状态从拷贝源(D3D12_RESOURCE_STATE_COPY_SOURCE)状态变更为显示状态(D3D12_RESOURCE_STATE_PRESENT)。

5.执行命令队列;

上面所执行的拷贝图像步骤,只是将所有的命令存储在IGraphicsCommandList命令列表中,并没有真正的执行。所以该步骤就是通过调用步骤3)中获取到的ID3D12ComanndQueue命令队列接口的ExecuteCommandLists方法,来执行IGraphicsCommandList列表中存储的所有命令。

6.GPU与CPU同步;

通过上面的执行命令队列步骤,游戏当前后台缓冲区图像已经成功的拷贝到步骤7)中所创建的共享纹理当中了,但是就如前面所述,由于D3D12采用的是异步模型,需要开发者自己实现GPU和CPU的同步,否则当拷贝完一帧图像,进行下一帧图像的拷贝时,就会失败。所以需要调用ID3D12CommandQueue命令队列的Signal方法来通知步骤6)中所创建的ID3D12Fence栅栏接口,当ID3D12GraphicsCommandList命令列表执行完后,会向ID3D12Fence接口所绑定的完成事件发送信号,这样就唤醒了通过调用Windows API WaitForSingleObject函数来等待该完成信号的CPU,从而实现了CPU与GPU的同步。

通过以上的这些步骤,便实现了通过Windows消息钩子的方式,将斗鱼直播伴侣的hookd3d9.dll模块注入到游戏进程中,通过Hook交换链的Present方法,跳转到hookd3d9.dll模块中的PresetHook函数,从而通过共享纹理的方式获取游戏进程图像的方法。该方法由于采用的是GPU纹理共享的方法,而没有直接将游戏图像的像素读取到内存中的耗时方法,从而并没有对游戏的性能产生影响。

应当理解的是,对本领域普通技术人员来说,可以根据上述说明加以改进或变换,而所有这些改进和变换都应属于本发明所附权利要求的保护范围。

当前第1页1 2 3 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1