Direct3D 12——计算着色器——计算着色器概念

news/2025/3/14 16:55:40/

计算着色器虽然是一种可编程的着色器,但Direct3D并没有将它直接归为渲染流水线中的一部分。虽然如此,但位于流水线之外的计算着色器却可以读写GPU资源。从本质上来说,计算着 色器能够使我们访问GPU来实现数据并行算法,而不必渲染出任何图形。由于计算着色器是Direct3D的组成部分,也可以读写Direct3D资源, 由此我们就可以将其输出的数据直接绑定到渲染流水线上。

线程与线程组

在GPU编程的过程中,根据程序具体的执行需求,可将线程划分为由线程组(thread group )构成 的网格(grid )o 一个线程组运行于一个多处理器之上。因此,对于拥有16个多处理器的GPU来说,我 们至少应将任务分解为16个线程组,以此令每个多处理器都充分地运转起来。但是,要获得更佳的性能, 我们还应当令每个多处理器至少拥有两个线程组,使它能够切换到不同的线程组进行处理,以连续不停地 工作[FunglO](线程组在运行的过程中可能会发生停顿,例如,着色器在继续执行下一个指令之前会等待 纹理的处理结果,此时即可切换至另一个线程组)。
每个线程组中都有一块共享内存,供组内的线程访问。但是,线程并不能访问其他组中的共享内存。 同理,同组内的线程间能够进行同步操作,不同组的线程间却不能实现这一点。事实上,我们也无法控 制不同线程组间的处理W序,因为这些线程组可能正运行在不同的多处理器上。
一个线程组中含有n个线程。硬件实际上会将这些线程分为多个warp (每个warp中有32个线程), 而且多处理器会以SIMD32的方式(即32个线程同时执行相同的指令序列)来处理warpo每个CUDA核 心都可处理一个线程,前面也提到了,“Fermi”架构中的每个多处理器都具有32个CUDA核心(因此, CUDA核匚、就像一条专设的SIMD “计算通道” (lane))o在Direct3D中,我们能够以非32的倍数值来指 定线程组的大小。但是出于性能的原因,我们应当总是将线程组的大小设置为warp尺寸的整数倍[FunglO]。
对于各种型号的图形硬件来说,线程数为256的线程组是一种普遍适于工作的初始设置。我们可以 以此值为基础,再根据具体需求尝试将其调整为其他大小。值得注意的是,修改每个线程组中的线程数 量也会对线程组的分派(dispatch,调度)次数产生影响。

NVIDIA公司生产的图形硬件所用的warp单位共有32个线程。而ATI公司(已被AMD公司收购)采用的 "wavefront”单位则具有64个线程,且建议为其分配的线程组大小应总为wavefront尺寸 的整数倍[Bilodeau 10]。另夕卜,值得一提的是,不管是warp还是wavefront,它们的大小在 未来几代中都有可能发生改变。

在Direct3D中可通过调用下列方法来启动线程组:

void ID3D12GraphicsCommandList::Dispatch(
UINT ThreadGroupCountX,
UINT ThreadGroupCountY,
UINT ThreadGroupCountZ);

此方法可开启一个由线程组构成的3D网格,但是我们在本书中仅关注线程组2D网格。下面的调用 示例会分派一个在x方向上为3、y方向上为2,即总数为3x2 = 6个线程组的网格(见图13.3)。

cmdList->Dispatch(3, 2, 1);

在这里插入图片描述

分派一个规模为3x2的线程组。此例假设每个线程组都有8x8条线程

一个简单的计算着色器

以下是将两个纹理进行简单累加的计算着色器示例,假设所有的纹理都具有相同的大小。虽然该着 色器有点索然无味,却五脏俱全,能详细地展示出计算着色器的基本套路语法。

cbuffer cbSettings
{
//计算着色器能访问的常量缓冲区数据
)//数据源及着色器的输出
Texture2D glnputA;
Texture2D glnputB;
RWTexture2D<float4> gOutput;
//线程组中的线程数。组中的线程可以被设置为ID、2D或3D的网格布局
[numthreads(16, 16, 1)]
void CS(int3 dispatchThreadID : SV_DispatchThreadID) // 线程 ID
{
//对两种源像素中横纵坐标分别为x、y处的纹素进行求和,并将结果保存到相应的gOutput纹素中 
gOutput[dispatchThreadID.xy]=glnputA[dispatchThreadID.xy] + glnputB[dispatchThreadlD.xy];
}

可见,一个计算着色器由下列要素构成:

1.通过常量缓冲区访问的全局变量。
2. 输入与输出资源。
3. [numthreads (X, Y, Z)]属性,指定3D线程网格中的线程数量。
4. 每个线程都要执行的着色器指令。
5. 线程ID系统值参数。

不难看出,我们能够根据需求定义岀不同的线程组布局。例如,可以定义一个具有X个线程的单行 线程组[numthreads (X, 1, 1)]或内含Y个线程的单列线程组[numthreads (1 ,Y, 1)]。抑或通 过将维度z设为1来定义规模为X x Y的2D线程组,形如[numthreads (X, Y, 1) ] 。我们应结合所遇到的具体问题来选择适当的线程组布局。如同前一节中提到的那样:针对NVIDIA品牌的显卡来说, 线程组中的总线程数应为warp大小(32 )的整数倍,而ATI公司生产的显卡应为wavefront尺寸(64 ) 的整数倍庁又因wavefront大小的倍数(64x„)必为warp尺寸的倍数(32xm),因此,以前者的线程数 为基础进行设置对两种显卡都适用。

计算流水线状态对象

为了开启计算着色器,我们还需使用其特定的“计算流水线状态描述”。此描述中的字段远少于 D3D12_GRAPHICS_PIPELINE_STATE_DESC结构体。这是因为计算着色器位列图形流水线之外,因 此所有的图形流水线状态都不适用于计算着色器,也就无须以此对它进行设置。下面给岀一个创建计算流水线状态对象的示例:

	D3D12_COMPUTE_PIPELINE_STATE_DESC wavesUpdatePSO = {};wavesUpdatePSO.pRootSignature = mWavesRootSignature.Get();wavesUpdatePSO.CS ={reinterpret_cast<BYTE*>(mShaders["wavesUpdateCS"]->GetBufferPointer()),mShaders["wavesUpdateCS"]->GetBufferSize()};wavesUpdatePSO.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;ThrowIfFailed(md3dDevice->CreateComputePipelineState(&wavesUpdatePSO, IID_PPV_ARGS(&mPSOs["wavesUpdate"])));

根签名定义了什么参数才是着色器所期望的输入(CBV、SRV等)。而cs (即compute shader的缩 写)字段就是所指定的计算着色器。下列代码展示了一个将着色器编译为字节码的示例:

	mShaders["wavesUpdateCS"] = d3dUtil::CompileShader(L"Shaders\\WaveSim.hlsl", nullptr, "UpdateWavesCS", "cs_5_0");mShaders["wavesDisturbCS"] = d3dUtil::CompileShader(L"Shaders\\WaveSim.hlsl", nullptr, "DisturbWavesCS", "cs_5_0");

http://www.ppmy.cn/news/46840.html

相关文章

5.Spring Cloud (Hoxton.SR8) 实战笔记—项目中细节实现 约束 注意事项、模块难点总结

本文目录如下&#xff1a; 二、项目中细节实现 & 约束 & 注意事项判断字符串是否为空&#xff1f;入参 Num 字段转换为 num 的问题&#xff1f;通过 Java 获取时间 (Date类型) 并插入数据库&#xff1f;神坑: baseMapper.selectById(String str)之坑?AES 实现加密函数…

[Eigen中文文档] 切片和索引

专栏总目录 本文目录 概述基本的切片编译时的大小和步长倒序索引序列自定义索引列表 英文原文(Slicing and Indexing) 本文介绍了如何使用操作运算符operator()索引行和列的子集。该 API 在 Eigen 3.4 中引入。它支持 block API 提供的所有功能。特别是&#xff0c;它支持切片…

查询练习:条件加组筛选

查询 student 表中至少有 2 名男生的 class 。 -- 查看学生表信息 SELECT * FROM student; ---------------------------------------- | no | name | sex | birthday | class | ---------------------------------------- | 101 | 曾华 | 男 | 1977-09-01 | 95…

vue 动态组件

一见如故 // 一般配合<keep-alive>组件&#xff0c;避免反复重新渲染dom <keep-alive><component :is"com"></component> </keep-alive> <script> import Left from ./LeftComponent.vue import Right from ./RightComponent.v…

Ae:自动定向

Ae 菜单&#xff1a;图层/变换/自动定向 Auto-Orient 快捷键&#xff1a;Ctrl Alt O 自动定向 Auto-Orient是 Ae 图层中的一个附加的、隐藏实现&#xff08;不会在时间轴面板上更改属性的值&#xff09;的功能&#xff0c;它可以使得图层自动旋转或改变方向以朝向指定的运动路…

【Linux阅读笔记】LinuxC一站式编程1-程序基本概念与gdb调试

目录 程序基本概念配置开发环境第一个程序C 复习 gdb单步执行与跟踪断点观察点段错误 程序基本概念 使用 ubuntu22.0 作为演示环境&#xff08;vmware 虚拟机搭设&#xff09; 配置开发环境 配置完基础开发环境后&#xff0c;可以直接下载一个 vscode 作为初始 LDE 使用&#x…

机器学习 day03(成本函数,简化后的和一般情况下的成本函数)

1. 成本函数 平方误差成本函数是最通常用于线性回归的成本函数最终&#xff0c;我们要找到一组w和b&#xff0c;让j函数的值最小误差&#xff1a;ŷ - y 2. 简化后的平方误差成本函数&#xff0c;即b 0 当w 1时&#xff0c;f(x) x&#xff0c;J(1) 0 左侧为f(x)函数&am…

Python 图像处理实用指南:11~12

原文&#xff1a;Hands-On Image Processing with Python 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 计算机视觉 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 当别人说你没有底线的时候&#xff0c;…