图形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

相关文章

Ubuntu 24.04启用root图形登录的操作流程

《Ubuntu24.04启用root图形登录的操作流程》Ubuntu默认禁用root账户的图形与SSH登录,这是为了安全,但在某些场景你可能需要直接用root登录GNOME桌面,本文以Ubuntu2... 目录一、前言二、准备工作三、设置 root 密码四、启用图形界面 root 登录1. 修改 GDM 配

Linux进程CPU绑定优化与实践过程

《Linux进程CPU绑定优化与实践过程》Linux支持进程绑定至特定CPU核心,通过sched_setaffinity系统调用和taskset工具实现,优化缓存效率与上下文切换,提升多核计算性能,适... 目录1. 多核处理器及并行计算概念1.1 多核处理器架构概述1.2 并行计算的含义及重要性1.3 并

Linux线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

golang程序打包成脚本部署到Linux系统方式

《golang程序打包成脚本部署到Linux系统方式》Golang程序通过本地编译(设置GOOS为linux生成无后缀二进制文件),上传至Linux服务器后赋权执行,使用nohup命令实现后台运行,完... 目录本地编译golang程序上传Golang二进制文件到linux服务器总结本地编译Golang程序

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

Jenkins分布式集群配置方式

《Jenkins分布式集群配置方式》:本文主要介绍Jenkins分布式集群配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1.安装jenkins2.配置集群总结Jenkins是一个开源项目,它提供了一个容易使用的持续集成系统,并且提供了大量的plugin满

Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式

《Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式》本文详细介绍如何使用Java通过JDBC连接MySQL数据库,包括下载驱动、配置Eclipse环境、检测数据库连接等关键步骤,... 目录一、下载驱动包二、放jar包三、检测数据库连接JavaJava 如何使用 JDBC 连接 mys

C#读写文本文件的多种方式详解

《C#读写文本文件的多种方式详解》这篇文章主要为大家详细介绍了C#中各种常用的文件读写方式,包括文本文件,二进制文件、CSV文件、JSON文件等,有需要的小伙伴可以参考一下... 目录一、文本文件读写1. 使用 File 类的静态方法2. 使用 StreamReader 和 StreamWriter二、二进