图形API学习工程(12):讨论当前工程里同步CPU与GPU的方式

2024-09-06 23:32

本文主要是介绍图形API学习工程(12):讨论当前工程里同步CPU与GPU的方式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

工程GIT地址:https://gitee.com/yaksue/yaksue-graphics

简单讨论CPU和GPU间的交互

《DX12龙书》在【4.2 CPU与GPU间的交互】章节中讨论了这个问题,简单来说:
为了最佳性能,CPU和GPU这两种处理器应该尽量同时工作,少“同步”。因为“同步”意味着一种处理器以空闲状态等待另一种处理器,即它破坏了“并行”。

但有时,又不得不进行二者的同步,见《DX12龙书》的【4.2.2 CPU与GPU间的同步】所讨论的问题。

当前工程,在D3D12和Vulkan的版本上有同步CPU与GPU的操作。
(D3D12代码参考DX12官方范例的【D3D12HelloWindow范例】,
Vulkan代码参考Rendering and presentation - Vulkan Tutorial)

我想就当前工程的同步方式进行讨论。但值得注意的是:当前的同步方式是最简单的,但并不是最佳的,详见最后一部分的讨论。

D3D12的方式

目前工程里D3D12方面是使用围栏实现同步。

接口介绍

所用的函数除了D3D12本身定义的之外,还有Windows定义的函数。

CreateEvent函数 (synchapi.h)

CreateEvent 将创建一个event对象,并返回这个event对象的Handle。

WaitForSingleObject函数 (synchapi.h)

调用WaitForSingleObject将使程序等待一个event对象直到它进入一个特定信号的状态(或者直到超过指定时间)。

DWORD WaitForSingleObject(HANDLE hHandle,DWORD  dwMilliseconds
);

参数中hHandle就是event对象的Handle。dwMilliseconds是超时的时间,如果设为INFINITE即表示一直等待。

ID3D12Fence接口 (d3d12.h)

ID3D12Fence代表一个围栏对象,用来同步CPU和GPU。
它的三个成员函数:

ID3D12Fence::GetCompletedValue 可以获取当前围栏对象的值。

ID3D12Fence::SetEventOnCompletion 可以设定当这个围栏对象到达某个值将会触发一个event对象。

HRESULT SetEventOnCompletion(UINT64 Value,HANDLE hEvent
);

参数中Value是围栏对象要到达的值,而hEvent是要触发的event对象的Handle。

ID3D12Fence::Signal 将设定围栏对象为指定的值。(CPU端

ID3D12CommandQueue::Signal方法

ID3D12CommandQueue的Signal方法将在队列执行完成后,在GPU端围栏对象设定为指定的值。

HRESULT Signal(ID3D12Fence *pFence,UINT64      Value
);

参数中pFence围栏对象,而Value则是会被设定的值。

使用

首先,在初始化阶段,创建围栏对象和event对象:

 //创建Fence
Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence));//创建一个event对象
m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);

WaitForPreviousFrame()表示等待这一帧GPU执行,其中内容如下:

首先,NewFenceValue表示应该设置的新的围栏值,它应该和从GetCompletedValue()获得的旧的围栏值不一样,比如+1。

const UINT64 NewFenceValue = m_fence->GetCompletedValue() + 1;

然后,CommandQueue设定:当它完成队列后,围栏将会在GPU端被设定为新的值。

CommandQueue->Signal(m_fence.Get(), NewFenceValue);

围栏自己也设定:当它到达新的值时,将会触发m_fenceEvent这个事件。

m_fence->SetEventOnCompletion(NewFenceValue, m_fenceEvent);

最后,让程序无限地等待m_fenceEvent这个事件。

WaitForSingleObject(m_fenceEvent, INFINITE);

这样,程序便会一直等待直到CommandQueue完成队列。

Vulkan的方式

目前工程里Vulkan方面相对于简单,是在VulkanInterface::Present()中使用了一些等待 设备/队列 进入闲置状态的接口。

接口介绍

vkDeviceWaitIdle:Wait for a device to become idle
vkQueueWaitIdle:Wait for a queue to become idle

使用

vkDeviceWaitIdle(Device);

或者

vkQueueWaitIdle(PresentQueue);

当前的同步方式并不是最佳

在学习过程中所参考的资料,无一不指出:当前的同步方式不是最佳的,而更佳的方式,会在后续学习。
例如:


《DX12龙书》:

这种解决方案并不完美,因为这意味着在等待GPU处理命令的时候,CPU会处于空闲状态,但在第7章以前也只能暂时使用这个简单的方法了。


DX12官方范例的【D3D12HelloWindow范例】:

// WAITING FOR THE FRAME TO COMPLETE BEFORE CONTINUING IS NOT BEST PRACTICE.
// This is code implemented as such for simplicity. The D3D12HelloFrameBuffering
// sample illustrates how to use fences for efficient resource usage and to
// maximize GPU utilization.

Rendering and presentation - Vulkan Tutorial:

The easy way to solve this is to wait for work to finish right after submitting it, for example by using vkQueueWaitIdle
However, we are likely not optimally using the GPU in this way, because the whole graphics pipeline is only used for one frame at a time right now. The stages that the current frame has already progressed through are idle and could already be used for a next frame. We will now extend our application to allow for multiple frames to be in-flight while still bounding the amount of work that piles up.

这篇关于图形API学习工程(12):讨论当前工程里同步CPU与GPU的方式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1143425

相关文章

JavaWeb-WebSocket浏览器服务器双向通信方式

《JavaWeb-WebSocket浏览器服务器双向通信方式》文章介绍了WebSocket协议的工作原理和应用场景,包括与HTTP的对比,接着,详细介绍了如何在Java中使用WebSocket,包括配... 目录一、概述二、入门2.1 POM依赖2.2 编写配置类2.3 编写WebSocket服务2.4 浏

配置springboot项目动静分离打包分离lib方式

《配置springboot项目动静分离打包分离lib方式》本文介绍了如何将SpringBoot工程中的静态资源和配置文件分离出来,以减少jar包大小,方便修改配置文件,通过在jar包同级目录创建co... 目录前言1、分离配置文件原理2、pom文件配置3、使用package命令打包4、总结前言默认情况下,

Java文件与Base64之间的转化方式

《Java文件与Base64之间的转化方式》这篇文章介绍了如何使用Java将文件(如图片、视频)转换为Base64编码,以及如何将Base64编码转换回文件,通过提供具体的工具类实现,作者希望帮助读者... 目录Java文件与Base64之间的转化1、文件转Base64工具类2、Base64转文件工具类3、

java获取图片的大小、宽度、高度方式

《java获取图片的大小、宽度、高度方式》文章介绍了如何将File对象转换为MultipartFile对象的过程,并分享了个人经验,希望能为读者提供参考... 目China编程录Java获取图片的大小、宽度、高度File对象(该对象里面是图片)MultipartFile对象(该对象里面是图片)总结java获取图片

Java通过反射获取方法参数名的方式小结

《Java通过反射获取方法参数名的方式小结》这篇文章主要为大家详细介绍了Java如何通过反射获取方法参数名的方式,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、前言2、解决方式方式2.1: 添加编译参数配置 -parameters方式2.2: 使用Spring的内部工具类 -

MySQL修改密码的四种实现方式

《MySQL修改密码的四种实现方式》文章主要介绍了如何使用命令行工具修改MySQL密码,包括使用`setpassword`命令和`mysqladmin`命令,此外,还详细描述了忘记密码时的处理方法,包... 目录mysql修改密码四种方式一、set password命令二、使用mysqladmin三、修改u

JAVA调用Deepseek的api完成基本对话简单代码示例

《JAVA调用Deepseek的api完成基本对话简单代码示例》:本文主要介绍JAVA调用Deepseek的api完成基本对话的相关资料,文中详细讲解了如何获取DeepSeekAPI密钥、添加H... 获取API密钥首先,从DeepSeek平台获取API密钥,用于身份验证。添加HTTP客户端依赖使用Jav

java如何通过Kerberos认证方式连接hive

《java如何通过Kerberos认证方式连接hive》该文主要介绍了如何在数据源管理功能中适配不同数据源(如MySQL、PostgreSQL和Hive),特别是如何在SpringBoot3框架下通过... 目录Java实现Kerberos认证主要方法依赖示例续期连接hive遇到的问题分析解决方式扩展思考总

SpringMVC前后端传值的几种实现方式

《SpringMVC前后端传值的几种实现方式》本文主要介绍了SpringMVC前后端传值的方式实现,包括使用HttpServletRequest、HttpSession、Model和ModelAndV... 目录一、从Controller层到JSP界面1、使用HttpServletRequest的方式2、使

Tomcat高效部署与性能优化方式

《Tomcat高效部署与性能优化方式》本文介绍了如何高效部署Tomcat并进行性能优化,以确保Web应用的稳定运行和高效响应,高效部署包括环境准备、安装Tomcat、配置Tomcat、部署应用和启动T... 目录Tomcat高效部署与性能优化一、引言二、Tomcat高效部署三、Tomcat性能优化总结Tom