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

相关文章

Mybatis官方生成器的使用方式

《Mybatis官方生成器的使用方式》本文详细介绍了MyBatisGenerator(MBG)的使用方法,通过实际代码示例展示了如何配置Maven插件来自动化生成MyBatis项目所需的实体类、Map... 目录1. MyBATis Generator 简介2. MyBatis Generator 的功能3

Python数据处理之导入导出Excel数据方式

《Python数据处理之导入导出Excel数据方式》Python是Excel数据处理的绝佳工具,通过Pandas和Openpyxl等库可以实现数据的导入、导出和自动化处理,从基础的数据读取和清洗到复杂... 目录python导入导出Excel数据开启数据之旅:为什么Python是Excel数据处理的最佳拍档

SpringBoot项目启动后自动加载系统配置的多种实现方式

《SpringBoot项目启动后自动加载系统配置的多种实现方式》:本文主要介绍SpringBoot项目启动后自动加载系统配置的多种实现方式,并通过代码示例讲解的非常详细,对大家的学习或工作有一定的... 目录1. 使用 CommandLineRunner实现方式:2. 使用 ApplicationRunne

VUE动态绑定class类的三种常用方式及适用场景详解

《VUE动态绑定class类的三种常用方式及适用场景详解》文章介绍了在实际开发中动态绑定class的三种常见情况及其解决方案,包括根据不同的返回值渲染不同的class样式、给模块添加基础样式以及根据设... 目录前言1.动态选择class样式(对象添加:情景一)2.动态添加一个class样式(字符串添加:情

MYSQL行列转置方式

《MYSQL行列转置方式》本文介绍了如何使用MySQL和Navicat进行列转行操作,首先,创建了一个名为`grade`的表,并插入多条数据,然后,通过修改查询SQL语句,使用`CASE`和`IF`函... 目录mysql行列转置开始列转行之前的准备下面开始步入正题总结MYSQL行列转置环境准备:mysq

Linux(Centos7)安装Mysql/Redis/MinIO方式

《Linux(Centos7)安装Mysql/Redis/MinIO方式》文章总结:介绍了如何安装MySQL和Redis,以及如何配置它们为开机自启,还详细讲解了如何安装MinIO,包括配置Syste... 目录安装mysql安装Redis安装MinIO总结安装Mysql安装Redis搜索Red

Java文件上传的多种实现方式

《Java文件上传的多种实现方式》文章主要介绍了文件上传接收接口的使用方法,包括获取文件信息、创建文件夹、保存文件到本地的两种方法,以及如何使用Postman进行接口调用... 目录Java文件上传的多方式1.文件上传接收文件接口2.接口主要内容部分3.postman接口调用总结Java文件上传的多方式1

使用SpringBoot创建一个RESTful API的详细步骤

《使用SpringBoot创建一个RESTfulAPI的详细步骤》使用Java的SpringBoot创建RESTfulAPI可以满足多种开发场景,它提供了快速开发、易于配置、可扩展、可维护的优点,尤... 目录一、创建 Spring Boot 项目二、创建控制器类(Controller Class)三、运行

SSID究竟是什么? WiFi网络名称及工作方式解析

《SSID究竟是什么?WiFi网络名称及工作方式解析》SID可以看作是无线网络的名称,类似于有线网络中的网络名称或者路由器的名称,在无线网络中,设备通过SSID来识别和连接到特定的无线网络... 当提到 Wi-Fi 网络时,就避不开「SSID」这个术语。简单来说,SSID 就是 Wi-Fi 网络的名称。比如

多模块的springboot项目发布指定模块的脚本方式

《多模块的springboot项目发布指定模块的脚本方式》该文章主要介绍了如何在多模块的SpringBoot项目中发布指定模块的脚本,作者原先的脚本会清理并编译所有模块,导致发布时间过长,通过简化脚本... 目录多模块的springboot项目发布指定模块的脚本1、不计成本地全部发布2、指定模块发布总结多模