Practical Rendering and Computation with Direct3D 11
+ -

3.2管道执行

2024-06-18 2 0

固定功能阶段与可编程阶段相结合,提供了一个有趣而多样的管道,我们可以用它来渲染图像。
执行管道的过程包括配置所有管道阶段状态,将输入和输出资源绑定到管道,然后调用其中一个Draw方法开始执行。所有这些任务都是通过ID3DllDeviceContext接口的方法执行的。为生成渲染帧而执行的管道执行次数取决于应用程序和当前场景,但我们可以假设每个帧至少执行一次管道执行。

与渲染相关的操作,都是ID3DllDeviceContext接口负责的。

一旦用这些绘制方法之一调用了管道,数据就会从输入内存资源读取到管道的第一阶段。从输入资源读取的数据量取决于用于调用管道的draw调用的类型和参数。每一段数据都会被处理并传递到下一阶段,直到它到达管道的末端,在那里它将被写入输出内存资源。一旦处理了来自这些绘制调用的所有数据,就可以将生成的输出资源(通常是2D渲染目标)呈现给输出窗口或保存到文件中。

在本章中,我们将了解如何配置管道的每个阶段,以及了解每种绘制方法之间的差异。有七种不同的绘制方法可用于调用渲染管道。这里列出它们是为了便于参考,并证明调用渲染管道的方法种类繁多。

  • Draw(…)
  • DrawAuto(…)
  • Drawlndexed(…)
  • DrawIndexedInstanced(…)
  • DrawInstanced(…)
  • DrawIndexedInstancedIndirect(…)
  • DrawInstancedIndirect(…)

这些方法中的每一个都指示管道以不同的方式解释其输入数据。每种方法还提供了不同的参数,以进一步配置要处理的输入数据的数量和内容。一旦启动了管道执行,在draw方法中指定的输入数据在被引入管道时会被逐个处理。

在规划一个管道执行期间将执行的工作时,最重要的考虑因素之一是每个阶段(固定和可编程)都有不同的输入和输出类型。此外,使用这些类型的语义具有相对较大的性能影响。例如,在一个简单的渲染序列中,我们可以将四个顶点作为两个三角形提交到管道。这将导致四个顶点着色器调用,这些调用将把它们的输出传递到光栅化器阶段,该阶段将生成许多片段。然后将这些片段传递给像素着色器,最后传递给输出合并器。如果三角形靠近查看器,则可能会生成许多片段,从而导致相应的大量像素着色器调用。在这种情况下,顶点的数量是恒定的,而片段的数量取决于场景的条件。也可能出现相反的情况,即曲面阶段产生要光栅化的可变数量的顶点,但对象在屏幕上保持相同的大小,因此像素数量保持不变。在设计算法时必须考虑这一点,因为这对于平衡GPU上的工作负载至关重要。

在硬件上实际执行流水线期间,每个单独的流水线阶段必须在可用的GPU硬件上尽快执行其计算。在许多情况下,GPU可以在不同的处理器组中同时执行多种类型的计算。在上面的例子中,GPU可以处理第一个三角形并光栅化它以生成片段。然后,它可以使用一些处理核心来处理像素着色器调用,也可以使用一些来执行剩余的顶点着色器工作。另一方面,它可以先处理所有顶点,然后切换到处理所有片段。无法保证GPU将以何种顺序处理数据,除非它将尊重管道中呈现的逻辑顺序。

3.2.1阶段间通讯

由于数据由各个管道阶段处理,因此必须从一个阶段传输到下一个阶段。为此,每个可编程阶段都为其着色器程序定义其所需的输入和输出。我们将这些输入和输出称为属性,由向量变量(从1到4个元素)组成。每个向量中的所有元素都由一种标量类型组成(整数和浮点类型都可用)。我们也可以将这些属性称为语义或绑定语义,这是每个输入/输出属性的基于文本的标识符的名称。一般来说,当我们用以下任一名称来指代这些类型的阶段到阶段变量时,应该从使用它们的上下文中清楚地看到。

着色器程序的输入属性定义了该程序执行所需的信息。确切地说,输入属性是着色器程序函数的输入参数。同样,函数的返回值定义其输出属性。因此,每个前一阶段都必须产生与下一阶段所需输入参数相匹配的输出参数。这些输入和输出属性是通过我们在“着色器核心架构”部分中讨论的输入和输出寄存器实现的。清单3.1中的简短示例提供了两个着色器函数的示例声明,每个函数定义其输入和输出。请注意,第一个函数的输出与第二个函数的输入相匹配。

struct VS_INPUT
{
    float3 position : POSITION;
    float2 coords : TEXCOORDS;
    uint vertexID : SV_VertexID;
};
struct VS_OUTPUT
{
    float4 position : SV_POSITION;
    float4 color : COLOR;
} ;

VS_OUTPUT VSMAIN( in VS_INPUT v )
{
// Perform vertex processing here...
}

//这里应是VS_OUTPUT,而不是VSJXJTPUT吧
float4 PSMAIN(in VSJXJTPUT  input) : SV_Target
{
// Perform pixel processing here...
}

两个着色器函数定义其必须匹配的输入和输出属性.

在每个函数的输入和输出的声明中,使用结构定义将所有输入和输出分组在一起。输入和输出结构的每个属性都有一个相关的语义,一个在阶段之间将两个数据项链接在一起的文本名称。语义位于属性名称之后,后跟分号。您还可以在每个组件的名称左侧看到它们的类型声明,正如您从C/++C中的类似声明中所期望的那样.

从本例中可以看出,第一个着色器函数的输出与第二个函数的输入几乎匹配,但vertex ID参数除外。此特定参数旁边列出的语义是SV_VertexID。除了清单3.1中所示的用户定义的语义属性之外,还有另一组参数可供可编程着色器在其输入/输出签名中使用。这些被称为系统值语义。它们表示运行时生成或使用的属性,这取决于它们的声明位置以及使用的系统值语义。这些系统值语义为HLSL程序提供了有用的信息,也用于向管道指示所需的值。系统值总是以SV为前缀,表示它们具有特殊意义,并且不是用户定义的。随着管道的进展,我们将看到每个系统值,以及它们在每个管道阶段是如何使用的.

了解数据是如何提供给管道的,以及了解每个管道阶段的状态如何影响发生的处理类型,是成功使用渲染管道的关键。在以下章节中,我们将按照管道中出现的顺序,详细检查每个管道阶段。

0 篇笔记 写笔记

作者信息
站长漫谈
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!