本发明涉及D3D12接口的图像绘制技术领域,具体来讲是一种在D3D12全屏游戏源上绘制弹幕图像的方法及系统。
背景技术:
随着2015年7月,微软Windows 10操作系统的发布,其全新一代的显卡图形编程接口D3D12(DirectX 12)也随之发布。随之而来的是利用D3D12进行开发的游戏也越来越多,例如《奇点灰烬》就是全球第一部用D3D12接口所开发的游戏,且受到了广大游戏玩家的喜爱。
当游戏玩家在进行D3D12类型的游戏操作时,可能希望通过其他的工具来进行游戏直播(例如在国外的Twitch或者国内的斗鱼等平台上进行直播)。作为一种直播娱乐方式,主播和观众之间通常通过弹幕的方式进行交流和互动。在一般情况下,主播可以通过直播平台提供的弹幕助手等工具,观看到观众所发送的弹幕;但是当主播所进行的D3D12游戏进入全屏模式后,就看不到观众所发送的弹幕,这无疑会影响主播与观众之间的互动。
因此,如何在全屏的D3D12游戏源上绘制弹幕图像,从而显示观众所发送的弹幕是本领域亟待解决的问题。
技术实现要素:
本发明的目的是为了克服上述背景技术的不足,提供一种在D3D12全屏游戏源上绘制弹幕图像的方法及系统,能在D3D12全屏游戏源上绘制弹幕图像,使主播可以在全屏游戏的同时看到观众所发送的弹幕,保证了与观众的有效互动,用户体验好。
为达到以上目的,本发明采取的技术方案是:提供一种在D3D12全屏游戏源上绘制弹幕图像的方法,该方法包括以下步骤:
步骤S1:直播客户端连接弹幕服务器,获取观众发送的弹幕信息并绘制成弹幕图像保存到共享内存中,转入步骤S2;
步骤S2:直播客户端将实现弹幕图像绘制功能的方法封装成弹幕绘制功能模块;并通过消息钩子将所述弹幕绘制功能模块注入到D3D12游戏进程中,转入步骤S3;
步骤S3:直播客户端通过内联钩子将所述弹幕绘制功能模块与D3D12游戏进程中的前台显示功能函数进行绑定,D3D12游戏在执行到将后台缓冲区中的图像提交到前台显示时,自动跳转执行弹幕绘制功能模块的初始化,转入步骤S4;
步骤S4:当直播客户端中添加了D3D12的全屏游戏源后,通知所述弹幕绘制功能模块创建共享纹理,转入步骤S5;
步骤S5:待弹幕绘制功能模块创建好共享纹理后,控制弹幕绘制功能模块将保存在共享内存中的弹幕图像拷贝到创建的共享纹理中并利用该共享纹理将弹幕图像绘制到D3D12的全屏游戏源上。
本发明还提供一种在D3D12全屏游戏源上绘制弹幕图像的系统,该系统包括设于直播客户端内部的弹幕服务器连接单元、弹幕绘制功能模块封装及注入单元、弹幕绘制功能模块绑定及初始化单元、共享纹理创建控制单元和弹幕图像绘制控制单元;
所述弹幕服务器连接单元用于:连接弹幕服务器,获取观众发送的弹幕信息并绘制成弹幕图像保存到共享内存中;
所述弹幕绘制功能模块封装及注入单元用于:将实现弹幕图像绘制功能的方法封装成弹幕绘制功能模块;并通过消息钩子将所述弹幕绘制功能模块注入到D3D12游戏进程中;
所述弹幕绘制功能模块绑定及初始化单元用于:通过内联钩子将弹幕绘制功能模块与D3D12游戏进程中的前台显示功能函数进行绑定,D3D12游戏在执行到将后台缓冲区中的图像提交到前台显示时,自动跳转执行弹幕绘制功能模块的初始化;
所述共享纹理创建控制单元用于:当直播客户端中添加了D3D12的全屏游戏源后,通知弹幕绘制功能模块创建共享纹理;
所述弹幕图像绘制控制单元用于:待弹幕绘制功能模块创建好共享纹理后,控制弹幕绘制功能模块将保存在共享内存中的弹幕图像拷贝到创建的共享纹理中并利用该共享纹理将弹幕图像绘制到D3D12的全屏游戏源上。
本发明的有益效果在于:
本发明中,通过消息钩子将封装好的弹幕绘制功能模块注入到D3D12游戏进程中,并利用内联钩子将弹幕绘制功能模块与游戏进程中的前台显示功能函数进行绑定,使得D3D12游戏在执行到将后台缓冲区中的图像提交到前台显示时,将自动跳转执行弹幕绘制功能模块的初始化操作;当直播客户端中添加了D3D12的全屏游戏源后,弹幕绘制功能模块将创建一个共享纹理;通过将保存在共享内存中的弹幕图像拷贝到创建的共享纹理中并利用该共享纹理将弹幕图像绘制到D3D12的全屏游戏源上,从而实现了直播客户端在进行全屏游戏的同时依然能看到观众所发送的弹幕的效果,保证了主播与观众的有效互动,用户体验好。
附图说明
图1为本发明实施例中在D3D12全屏游戏源上绘制弹幕图像的方法的流程图;
图2为本发明实施例中在D3D12全屏游戏源上绘制弹幕图像的系统的结构框图。
具体实施方式
下面结合附图及具体实施例对本发明作进一步的详细描述。
参见图1所示,本发明实施例提供一种在D3D12全屏游戏源上绘制弹幕图像的方法,包括以下步骤:
步骤S1、连接弹幕服务器:直播客户端与弹幕服务器连接,获取观众发送的弹幕信息并将该弹幕信息绘制成弹幕图像保存到共享内存中,转入步骤S2。
步骤S2、模块封装及注入:直播客户端将实现弹幕图像绘制功能的方法封装到一个DLL(Dynamic Link Library,动态链接库)文件中,形成弹幕绘制功能模块;通过消息钩子将所述弹幕绘制功能模块注入到D3D12游戏进程中,转入步骤S3。
可以理解的是,本实施例中所述弹幕绘制功能模块的名称是hookd3d9.dll;并且,通过消息钩子将弹幕绘制功能模块注入到D3D12游戏进程中时,是通过调用Windows系统API(Application Programming Interface,应用编程接口)的SetWindowsHookEx函数,将hookd3d9.dll模块注入到游戏当中的。这样,被注入的D3D12游戏进程中便有了hookd3d9.dll这个模块在运行。
步骤S3、模块绑定及初始化:直播客户端通过内联钩子将所述弹幕绘制功能模块与D3D12游戏进程中的前台显示功能函数进行绑定,D3D12游戏在执行到将后台缓冲区中的图像提交到前台显示时,自动跳转执行弹幕绘制功能模块的初始化,转入步骤S4。
其中,步骤S3中,通过内联钩子将所述弹幕绘制功能模块与游戏进程中的前台显示功能函数进行绑定,具体包括以下流程:查找到D3D12游戏的交换链接口IDXGISwapChain;通过内联钩子,用弹幕绘制功能模块hookd3d9.dll中的弹幕绘制功能函数PresentHook来勾住交换链接口IDXGISwapChain的前台显示功能函数Present,当D3D12游戏进程中调用了前台显示功能函数Present,便会跳转到弹幕绘制功能模块hookd3d9.dll的弹幕绘制功能函数PresentHook中来。
步骤S3中,执行弹幕绘制功能模块的初始化时,具体流程如下:
步骤一、获取命令队列接口(ID3D12CommandQueue接口):根据查找到的D3D12游戏的交换链接口IDXGISwapChain,获取该交换链的命令队列接口ID3D12CommandQueue。具体来说,由于笔者在现有的接口中没有发现获取交换链所对应的命令队列接口,后来经过研究发现,在32位应用程序下,IDXGISwapChain接口地址加上0x5BC的偏移处所存放的内容,正好就是创建该交换链的命令队列所在的地址值。通过这个方法,就成功的获取到了命令队列接口。
步骤二、获取D3D12入口接口(ID3D12Device接口):根据查找到的D3D12游戏的交换链接口IDXGISwapChain,通过调用该接口所提供的入口获取接口函数GetDevice,获取到D3D12游戏的D3D12入口接口。可以理解的是,所述D3D12入口接口是对一块显卡的抽象,可以用来实现创建资源接口,创建同步栅栏接口等功能。
步骤三、创建命令队列接口(ID3D12GraphicsCommandList):根据步骤二中获取的D3D12入口接口(ID3D12Device),通过调用该接口的命令队列创建函数CreateCommandList,创建一个命令队列接口ID3D12GraphicsCommandList,来执行获取游戏源图像的任务。
步骤四、创建同步栅栏接口(ID3D12Fence接口):根据步骤二中获取的D3D12入口接口(ID3D12Device),通过调用该接口的同步栅栏创建函数CreateFence,创建一个同步栅栏接口ID3D12Fence;然后调用Windows系统API接口的事件创建函数CreateEvent创建一个事件,并通过调用同步栅栏接口ID3D12Fence所提供的事件绑定函数SetEventOnCompletion将创建的事件与同步栅栏接口绑定。
步骤五、创建渲染着色器签名接口(ID3D12RootSignature接口):对渲染着色器签名接口的描述结构进行相关参数(即HLSL着色器相关参数——顶点着色器、像素点着色器等)填写,并初始化一个渲染着色器签名接口的描述结构D3D12_ROOT_SIGNATURE_DESC;利用初始化成功的描述结构D3D12_ROOT_SIGNATURE_DESC,调用D3D12接口提供的签名接口序列化函数D3D12SerializeRootSignature进行序列化,得到一个二进制对象接口ID3DBlob;通过得到的二进制对象接口ID3DBlob调用D3D12入口接口(ID3D12Device)提供的签名接口创建函数CreateRootSignature,创建渲染着色器签名接口ID3D12RootSignature。该渲染着色器签名接口代表一个HLSL(HighLevel Shader Language,高级渲染语言脚本)中着色器的签名。该签名中包含了着色器中的很多信息,包括用到的常量资源、纹理视图资源、纹理采样器等,以及这些资源的先后排列次序等。
可以理解的是,步骤五中,为了创建一个正确的签名接口(ID3D12RootSignature),必须正确的填充签名接口的描述结构(D3D12_ROOT_SIGNATURE_DESC)。所以,为了更清晰的说明着色器与签名接口(ID3D12RootSignature)之间的关系,下面列出了本实现中所用到的HLSL着色器代码,以及如何正确的填写描述结构(D3D12_ROOT_SIGNATURE_DESC)中的相关参数。
从上面可知,这段代码的第一项MatrixViewProj是一个常量Buffer,所以必须以D3D12_DESCRIPTOR_RANGE_TYPE_CBV常量来初始化一个D3D12_ROOT_PARAMETER结构,并且该结构的索引为0;上述代码中的第二项g_texture是一个纹理资源视图ShaderResourceView,所以必须以一个D3D12_DESCRIPTOR_RANGE_TYPE_SRV常量来初始化一个D3D12_ROOT_PARAMETER,并且该结构的索引为1;该代码中的第三项是一个g_linearSampler的纹理采样器,所以必须初始化一个相应的D3D12_STATIC_SAMPLER_DESC。然后利用上面所述的相关结构来初始化D3D12_ROOT_SIGNATURE_DESC。
步骤六、创建管线状态接口(ID3D12PipelineState接口):利用创建好的渲染着色器签名接口(ID3D12RootSignature),创建相应的管线状态接口(ID3D12PipelineState)。该管线状态接口代表了一个D3D12的管线状态。一个管线状态包含了用于绘制图像的所有信息,包括顶点着色器(Vertex Shader),像素着色器(Pixel Shader),顶点输入布局(VertexInputLayout),Alpha混合状态,模板和深度混合状态等。其具体的操作步骤如下:
1)编译顶点着色器
顶点着色器代码就是步骤五中所提出的代码中包含的VertexTransform函数,该着色器的功能是对顶点进行一个坐标变换,将一个三维世界的坐标通过矩阵的方式转换成屏幕上的二维坐标。可以通过D3D(Direct3D,3D加速卡)所提供的API函数D3DCompile对该着色器进行编译,得到一个ID3DBlob(二进制对象接口)类型的顶点着色器VertexShader。
2)编译像素着色器
像素着色器代码就是步骤五中所提出的代码中包含的mainPixelShader函数,该着色器的功能是利用一个纹理采样器,对一个给定的纹理在给定的纹理坐标位置处进行采样,获取纹理图像的像素值。仍然通过D3DCompile函数对该着色器进行编译,得到一个ID3DBlob类型的像素着色器PixelShader。
3)进行顶点布局描述
顶点的布局描述就是步骤五所提出的代码中包含的VertexInput结构体。该结构体包含一个位置变量pos,语义为POSITION,位置;以及一个纹理坐标变量tex,语义为TEXCOORD,纹理坐标。所以利用这些信息来初始化相应的顶点布局的描述结构D3D12_INPUT_ELEMENT_DESC,从而完成顶点布局的描述。
4)利用初始化成功的顶点着色器、像素着色器和顶点布局创建管线状态接口
利用上述初始化成功的顶点着色器、像素着色器以及顶点布局,来初始化管线状态接口的描述结构(D3D12_GRAPHICS_PIPELINE_STATE_DESC),并将管线状态接口的光栅化状态RasterizerState设置为默认,取消背面剔除;控制Alpha混合的BlendState字段设置为开启,SampleMask字段设置为UNIT_MAX值等。然后调用D3D12入口接口(ID3D12Device)提供的管线状态创建函数CreateGraphicsPipelineState,创建管线状态接口ID3D12PipelineState。
步骤七、创建常量Buffer视图接口(ConstBufferView接口):先创建一个常量Buffer(缓冲)视图的资源描述符接口(ID3D12DescriptorHeap接口);再创建一个常量Buffer资源;最后,利用创建成功的资源描述符接口和常量Buffer资源,通过调用D3D12入口接口(ID3D12Device)提供的常量Buffer视图创建函数CreateConstantBufferView,创建常量Buffer视图接口ConstBufferView。
具体地,创建常量Buffer视图的资源描述符接口的流程如下:用已知的D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV常量初始化描述结构D3D12_DESCRIPTOR_HEAP_DESC中的Type字段;用已知的D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE常量初始化描述结构D3D12_DESCRIPTOR_HEAP_DESC中的Flags字段,然后调用D3D12入口接口(ID3D12Device)提供的资源描述符创建函数CreateDescriptorHeap,创建常量Buffer视图的资源描述符接口ID3D12DescriptorHeap。另外,创建常量Buffer资源的具体流程如下:通过调用D3D12入口接口(ID3D12Device)提供的资源创建函数CreateCommittedResource,创建一个常量Buffer资源。
步骤八、创建渲染对象视图接口(RenderTargetView接口):因为将弹幕图像绘制到游戏图像上时,需要从游戏的当前后台缓冲区上得到游戏图像,所以我们需要先获取游戏的后台缓冲区资源,并对这些资源创建相应的渲染对象视图,因此,需要创建渲染对象视图接口。先创建一个渲染对象视图的资源描述符接口(ID3D12DescriptorHeap接口);再获取游戏交换链中的纹理资源;最后,利用上述创建的资源描述符接口和获取的纹理资源,通过调用D3D12入口接口(ID3D12Device)提供的渲染对象视图创建函数CreateRenderTargetView,创建渲染对象视图接口RenderTargetView。
具体地,创建渲染对象视图的资源描述符接口的流程如下:通过调用D3D12入口接口(ID3D12Device)提供的资源描述符创建函数CreateDescriptorHeap,创建一个渲染对象视图的资源描述符接口ID3D12DescriptorHeap。另外,获取游戏交换链中的纹理资源的具体流程为:根据查找到的D3D12游戏的交换链接口IDXGISwapChain,通过调用该接口所提供的资源获取函数GetBuffer,获取该交换链中的相应纹理资源。
步骤九、创建输入顶点Buffer:由于在绘制弹幕图像时需要知道弹幕图像是要绘制在游戏画面上的什么位置的,所以需要创建相应的Buffer资源,来存储这些弹幕图像的顶点信息。具体来说,就是调用D3D12入口接口(ID3D12Device)提供的资源创建函数CreateCommittedResource,创建一个Buffer资源,并得到一个D3D12资源接口ID3D12Resource,然后利用该接口提供的映射函数Map,将顶点数据填充到创建的Buffer资源中,形成输入顶点Buffer。
步骤S4、创建共享纹理:当直播客户端中添加了D3D12的全屏游戏源后,通知所述弹幕绘制功能模块创建共享纹理,转入步骤S5。
具体来说,所述弹幕绘制功能模块创建共享纹理,具体包括以下操作:
步骤一、对共享纹理的描述结构(D3D12_RESOURCE_DESC)进行初始化:将其描述结构的Dimension字段设置为D3D12_RESOURCE_DIMENSION_TEXTURE2D,用于表示待创建的共享纹理是一个2D纹理;将描述结构的Width字段设置为共享内存中存放的图像纹理的宽度;将描述结构的Height字段设置为共享内存中存放的图像纹理的高度,将Format字段设置为DXGI_FORMAT_B8G8R8A8_UNORM像素格式。
步骤二、创建共享纹理的资源描述符接口:通过调用D3D12入口接口提供的资源描述符创建函数CreateDescriptorHeap,创建一个共享纹理的资源描述符接口ID3D12DescriptorHeap,其类型为D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV。
步骤三、创建共享纹理的资源:利用步骤一中初始化的共享纹理的描述结构(D3D12_RESOURCE_DESC),通过调用D3D12入口接口(ID3D12Device)提供的资源创建函数CreateCommittedResource,创建一个共享纹理的资源。
步骤四、创建共享纹理的Buffer资源:由于步骤三种创建的共享纹理的资源是不能够锁定的,也就是不能直接更新该纹理,这样的话,存储在共享内存中的纹理图像(如弹幕图像)便不能填充到纹理资源中。所以为了达到更新纹理的目的,还必须创建一个共享纹理的Buffer资源,用该Buffer资源来更新共享纹理的资源。具体来说,就是通过调用D3D12入口接口(ID3D12Device)提供的资源创建函数CreateCommittedResource,创建一个共享纹理的Buffer资源。
步骤五、创建共享纹理:利用步骤二、步骤三中创建的共享纹理的资源描述符接口和共享纹理的资源,通过调用D3D12入口接口(ID3D12Device)提供的纹理视图创建函数CreateShaderResourceView,创建一个共享纹理。
步骤S5、绘制弹幕图像:待弹幕绘制功能模块创建好共享纹理后,控制弹幕绘制功能模块将步骤S1中保存在共享内存中的弹幕图像拷贝到创建的共享纹理中并利用该共享纹理将弹幕图像绘制到D3D12的全屏游戏源上。
实际操作时,所述弹幕绘制功能模块将步骤S1中保存在共享内存中的弹幕图像拷贝到创建的共享纹理中并利用该共享纹理将弹幕图像绘制到D3D12的全屏游戏源上,具体包括以下操作:
步骤一、获取当前游戏后台缓冲区的图像资源(即获取游戏图像资源):通过调用交换链接口IDXGISwapChain的后台缓冲索引获取函数GetCurrentBackBufferIndex,获取当前后台缓冲区的图像索引;然后根据该索引通过调用交换链接口IDXGISwapChain的资源获取函数GetBuffer,获取当前后台缓冲区的图像资源。
步骤二、变更资源状态:对获取的图像资源进行状态变更,将图像资源由显示状态变更为渲染对象状态。由于刚获取到的图像资源为显示状态(D3D12_RESOURCE_STATE_PRESENT)该状态下的资源是不能够进行绘制的,所以要将目前的显示状态变更为渲染目标状态(D3D12_RESOURCE_STATE_RENDER_TARGET)。具体来说,通过调用弹幕绘制功能模块初始化时创建的命令队列接口ID3D12GraphicsCommandList的资源屏障函数ResourceBarrier,将后台缓冲区的资源状态从显示状态变更为渲染对象状态。
步骤三、设置渲染对象:通过调用弹幕绘制功能模块初始化时创建的命令队列接口ID3D12GraphicsCommandList提供的渲染目标设置函数OMSetRenderTargets,将获取的图像资源设置为渲染对象。具体来说,是通过渲染目标设置函数OMSetRenderTargets将步骤一中获取到的当前后台缓冲区的图像索引与弹幕绘制功能模块初始化时创建的渲染对象视图接口RenderTargetView对应,从而实现将获取的图像资源设置为渲染对象。
步骤四、设置输入顶点:通过调用弹幕绘制功能模块初始化时创建的命令队列接口ID3D12GraphicsCommandList提供的顶点缓冲设置函数IASetVertexBuffers,将弹幕绘制功能模块初始化时创建的输入顶点Buffer设置到管线中。
步骤五、设置视口和裁剪区域:通过调用弹幕绘制功能模块初始化时创建的命令队列接口ID3D12GraphicsCommandList提供的视口设置函数RSSetViewports,设置视口(视口是与设备相关的一个矩形区域,坐标单位是与设备相关的);通过调用弹幕绘制功能模块初始化时创建的命令队列接口ID3D12GraphicsCommandList的裁剪区域设置函数RSSetScissorRects,设置裁剪区域。
步骤六、设置渲染着色器签名接口:通过调用弹幕绘制功能模块初始化时创建的命令队列接口ID3D12GraphicsCommandList提供的着色器签名设置函数SetGraphicsRootSignature,将弹幕绘制功能模块初始化时创建的渲染着色器签名接口设置到管线中。
步骤七、设置管线状态接口:通过调用弹幕绘制功能模块初始化时创建的命令队列接口ID3D12GraphicsCommandList提供的管线状态设置函数SetPipelineState,将弹幕绘制功能模块初始化时创建的管线状态接口设置为当前管线。
步骤八、设置描述符参数表(RootDescriptorTable):通过调用弹幕绘制功能模块初始化时创建的命令队列接口ID3D12GraphicsCommandList提供的资源描述符设置函数SetDescriptorHeaps,将弹幕绘制功能模块初始化时创建的常量Buffer视图接口设置为描述符参数表的第一个参数,将步骤S4中弹幕绘制功能模块创建的共享纹理设置为描述符参数表的第一个参数,表示当前纹理。
步骤九、对共享内存中存放的弹幕图像的顶点进行变换,使其能显示在正确的位置处:通过对共享内存中存放的弹幕图像显示的左顶点位置,以及显示的宽、高、大小等信息,进行相应的顶点变换,从而使其能显示在正确的位置处(即弹幕图像是要绘制在游戏画面上的什么位置的)。
步骤十、绘制弹幕图像图元并将共享内存中的弹幕图像拷贝到弹幕绘制功能模块创建的共享纹理中:通过调用弹幕绘制功能模块初始化时创建的命令队列接口ID3D12GraphicsCommandList提供的图元绘制函数DrawInstanced,绘制一个矩形的图元,该矩形图元内部即为弹幕图像;通过将共享内存中的弹幕图像内容填充到创建的所述共享纹理的Buffer资源中,从而实现将弹幕图像拷贝到创建的共享纹理中。
步骤十一、复位资源状态:将上述步骤二中,变更的当前缓冲区的图像资源状态重新设置为显示状态(D3D12_RESOURCE_STATE_PRESENT)。
步骤十二、执行命令队列:由于上面所执行的操作,只是将所有的命令存储在IGraphicsCommandList命令列表中,并没有真正的执行,所以本步骤需要对命令队列中的所有命令进行执行。具体来说,通过调用弹幕绘制功能模块初始化时获取的命令队列接口ID3D12CommandQueue的命令队列执行函数ExecuteCommandLists,来执行命令队列列表中存储的所有命令,以完成当前弹幕图像绘制。
步骤十三、同步直播客户端的GPU(Graphic Processing Unit,图形处理器)与CPU(Central Processing Unit,中央处理器),以等待下次弹幕图像的绘制:由于D3D12接口采用的是异步模型,所以需要实现GPU和CPU的同步,否则当一帧图像处理完成后,进行下一帧图像的拷贝时,就会失败。具体来说,是通过调用弹幕绘制功能模块初始化时获取的命令队列接口ID3D12CommandQueue的信号量函数Signal,来通知弹幕绘制功能模块初始化时所创建的同步栅栏接口ID3D12Fence;当命令队列接口ID3D12GraphicsCommandList的命令列表执行完后,会向同步栅栏接口ID3D12Fence所绑定的完成事件发送信号,这样就唤醒了通过调用Windows API的WaitForSingleObject函数来等待该完成信号的CPU,从而实现了GPU与CPU的同步。
参见图2所示,本发明还提供了一种在D3D12全屏游戏源上绘制弹幕图像的系统,该系统包括设于直播客户端内部的弹幕服务器连接单元、弹幕绘制功能模块封装及注入单元、弹幕绘制功能模块绑定及初始化单元、共享纹理创建控制单元和弹幕图像绘制控制单元。
弹幕服务器连接单元用于:连接弹幕服务器,获取观众发送的弹幕信息并绘制成弹幕图像保存到共享内存中。
弹幕绘制功能模块封装及注入单元用于:将实现弹幕图像绘制功能的方法封装成弹幕绘制功能模块;并通过消息钩子将所述弹幕绘制功能模块注入到D3D12游戏进程中。具体来说,所述弹幕绘制功能模块封装及注入单元是通过调用Windows系统API接口的SetWindowsHookEx函数,将弹幕绘制功能模块注入到游戏当中的。
弹幕绘制功能模块绑定及初始化单元用于:通过内联钩子将弹幕绘制功能模块与D3D12游戏进程中的前台显示功能函数进行绑定,D3D12游戏在执行到将后台缓冲区中的图像提交到前台显示时,自动跳转执行弹幕绘制功能模块的初始化。
其中,所述弹幕绘制功能模块绑定及初始化单元通过内联钩子将弹幕绘制功能模块与游戏进程中的前台显示功能函数进行绑定的具体流程如下:查找到D3D12游戏的交换链接口;通过内联钩子,用弹幕绘制功能模块中的弹幕绘制功能函数来勾住交换链接口的前台显示功能函数,当D3D12游戏进程中调用了前台显示功能函数,便会跳转到弹幕绘制功能模块的弹幕绘制功能函数中来。
另外,弹幕绘制功能模块执行初始化的具体流程为:根据查找到的D3D12游戏的交换链接口,获取该交换链的命令队列接口和D3D12游戏的D3D12入口接口;通过调用获取的D3D12入口接口的命令队列创建函数,创建一个命令队列接口;通过调用获取的D3D12入口接口的同步栅栏创建函数,创建一个同步栅栏接口,并调用Windows API接口的事件创建函数创建一个事件,将创建的事件与同步栅栏接口绑定;通过调用获取的D3D12入口接口的签名接口创建函数,创建渲染着色器签名接口;根据创建好的渲染着色器签名接口,通过调用D3D12入口接口的管线状态创建函数,创建管线状态接口;通过调用获取的D3D12入口接口的常量Buffer视图创建函数,创建常量Buffer视图接口;通过调用获取的D3D12入口接口的渲染对象视图创建函数,创建渲染对象视图接口;通过调用获取的D3D12入口接口的资源创建函数,创建一个Buffer资源并得到一个D3D12资源接口,利用该接口提供的映射函数Map,将顶点数据填充到创建的Buffer资源中,形成输入顶点Buffer。
共享纹理创建控制单元用于:当直播客户端中添加了D3D12的全屏游戏源后,通知弹幕绘制功能模块创建共享纹理。具体来说,弹幕绘制功能模块创建共享纹理的过程为:对共享纹理的描述结构进行初始化;创建共享纹理的资源描述符接口;创建共享纹理的资源;创建用于更新资源的共享纹理的Buffer资源;利用创建的共享纹理的资源描述符接口和共享纹理的资源,创建共享纹理。
弹幕图像绘制控制单元用于:待弹幕绘制功能模块创建好共享纹理后,控制弹幕绘制功能模块将保存在共享内存中的弹幕图像拷贝到创建的共享纹理中并利用该共享纹理将弹幕图像绘制到D3D12的全屏游戏源上。其具体操作过程与方法步骤中相应,此处不赘述!
需要说明的是:上述实施例提供的在D3D12全屏游戏源上绘制弹幕图像的系统在实现弹幕图像绘制时,仅以上述各功能模块的划分进行举例说明,实际应用中,可根据需要将上述功能分配由不同的功能模块完成,即将系统的内部结构划分成不同的功能模块,以完成以上描述的全部或者部分功能。
本发明不局限于上述实施方式,对于本技术领域的普通技术人员来说,在不脱离本发明原理的前提下,还可以做出若干改进和润饰,这些改进和润饰也视为本发明的保护范围之内。本说明书中未作详细描述的内容属于本领域专业技术人员公知的现有技术。