A trip through the Graphics Pipeline 2011, part 3

Jim Blinn's Corner - A trip down the graphics pipeline Jim Blinn's Corner - A trip down the graphics pipeline。难得的稀有资料 立即下载

A trip through the Graphics Pipeline 2011, part 3

by fgiesen on July 3, 2011

At this point, we’ve sent draw calls down from our app all the way through various driver layers and the command processor; now, finally we’re actually going to do some graphics processing on it! In this part, I’ll look at the vertex pipeline. But before we start…

Have some Alphabet Soup!

We’re now in the 3D pipeline proper, which in turn consists of several stages, each of which does one particular job. I’m gonna give names to all the stages I’ll talk about – mostly sticking with the “official” D3D10/11 names for consistency – plus the corresponding acronyms. We’ll see all of these eventually on our grand tour, but it’ll take a while (and several more parts) until we see most of them – seriously, I made a small outline of the ground I want to cover, and this series will keep me busy for at least 2 weeks! Anyway, here goes, together with a one-sentence summary of what each stage does.

  • IA — Input Assembler. Reads index and vertex data.
  • VS — Vertex shader. Gets input vertex data, writes out processed vertex data for the next stage.
  • PA — Primitive Assembly. Reads the vertices that make up a primitive and passes them on.
  • HS — Hull shader; accepts patch primitives, writes transformed (or not) patch control points, inputs for the domain shader, plus some extra data that drives tessellation.
  • TS — Tessellator stage. Creates vertices and connectivity for tessellated lines or triangles.
  • DS — Domain shader; takes shaded control points, extra data from HS and tessellated positions from TS and turns them into vertices again.
  • GS — Geometry shader; inputs primitives, optionally with adjacency information, then outputs different primitives. Also the primary hub for…
  • SO — Stream-out. Writes GS output (i.e. transformed primitives) to a buffer in memory.
  • RS — Rasterizer. Rasterizes primitives.
  • PS — Pixel shader. Gets interpolated vertex data, outputs pixel colors. Can also write to UAVs (unordered access views).
  • OM — Output merger. Gets shaded pixels from PS, does alpha blending and writes them back to the backbuffer.
  • CS — Compute shader. In its own pipeline all by itself. Only input is constant buffers+thread ID; can write to buffers and UAVs.

And now that that’s out of the way, here’s a list of the various data paths I’ll be talking about, in order: (I’ll leave out the IA, PA, RS and OM stages in here, since for our purposes they don’t actually do anything to the data, they just rearrange/reorder it – i.e. they’re essentially glue)

  1. VS→PS: Ye Olde Programmable Pipeline. In D3D9, this was all you got. Still the most important path for regular rendering by far. I’ll go through this from beginning to end then double back to the fancier paths once I’m done.
  2. VS→GS→PS: Geometry Shading (new with D3D10).
  3. VS→HS→TS→DS→PS, VS→HS→TS→DS→GS→PS: Tessellation (new in D3D11).
  4. VS→SO, VS→GS→SO, VS→HS→TS→DS→GS→SO: Stream-out (with and without tessellation).
  5. CS: Compute. New in D3D11.

And now that you know what’s coming up, let’s get started on vertex shaders!

Input Assembler stage

The very first thing that happens here is loading indices from the index buffer – if it’s an indexed batch. If not, just pretend it was an identity index buffer (0 1 2 3 4 …) and use that as index instead. If there is an index buffer, its contents are read from memory at this point – not directly though, the IA usually has a data cache to exploit locality of index/vertex buffer access. Also note that index buffer reads (in fact, all resource accesses in D3D10+) are bounds checked; if you reference elements outside the original index buffer (for example, issue a DrawIndexed with IndexCount == 6 from a 5-index buffer) all out-of-bounds reads return zero. Which (in this particular case) is completely useless, but well-defined. Similarly, you can issue a DrawIndexed with a NULL index buffer set – this behaves the same way as if you had an index buffer of size zero set, i.e. all reads are out-of-bounds and hence return zero. With D3D10+, you have to work some more to get into the realm of undefined behavior. :)

Once we have the index, we have all we need to read both per-vertex and per-instance data (the current instance ID is just another counter, fairly straightforward, at this stage anyway) from the input vertex streams. This is fairly straightforward – we have a declaration of the data layout; just read it from the cache/memory and unpack it into the float format that our shader cores want for input. However, this read isn’t done immediately; the hardware is running a cache of shaded vertices, so that if one vertex is referenced by multiple triangles (and in a fully regular closed triangle mesh, each vertex will be referenced by about 6 tris!) it doesn’t need to be shaded every time – we just reference the shaded data that’s already there!

Vertex Caching and Shading

Note: The contents of this section are, in part, guesswork. They’re based on public comments made by people “in the know” about current GPUs, but that only gives me the “what”, not the “why”, so there’s some extrapolation here. Also, I’m simply guessing some of the details here. That said, I’m not talking completely out of my ass here – I’m confident that what I’m describing here is both reasonable and works (in the general sense), I just can’t guarantee that it’s actually that way in real HW or that I didn’t miss any tricky details. :)

Anyway. For a long time (up to and including the shader model 3.0 generation of GPUs), vertex and pixel shaders were implemented with different units that had different performance trade-offs, and vertex caches were a fairly simple affair: usually just a FIFO for a small number (think one or two dozen) of vertices, with enough space for a worst-case number of output attributes, using the vertex index as a tag. As said, fairly straightforward stuff.

And then unified shaders happened. If you unify two types of shaders that used to be different, the design is necessarily going to be a compromise. So on the one hand, you have vertex shaders, which (at that time) touched maybe up to 1 million vertices a frame in normal use. On the other hand you had pixel shaders, which at 1920×1200 need to touch at least 2.3 million pixels a frame just to fill the whole screen once – and a lot more if you want to render anything interesting. So guess which of the two units ended up pulling the short straw?

Okay, so here’s the deal: instead of the vertex shader units of old that shaded more or less one vertex at a time, you now have a huge beast of a unified shader unit that’s designed for maximum throughput, not latency, and hence wants large batches of work (How large? Right now, the magic number seems to be between 16 and 64 vertices shaded in one batch).

So you need between 16-64 vertex cache misses until you can dispatch one vertex shading load, if you don’t want to shade inefficiently. But the whole FIFO thing doesn’t really play ball with this idea of batching up vertex cache misses and shading them in one go. The problem is this: if you shade a whole batch of vertices at once, that means you can only actually start assembling triangles once all those vertices have finished shading. At which point you’ve just added a whole batch (let’s just say 32 here and in the following) of vertices to the end of the FIFO, which means 32 old vertices now fell out – but each of these 32 vertices might’ve been a vertex cache hit for one of the triangles in the current batch we’re trying to assemble! Uh oh, that doesn’t work. Clearly, we can’t actually count the 32 oldest verts in the FIFO as vertex cache hits, because by the time we want to reference them they’ll be gone! Also, how big do we want to make this FIFO? If we’re shading 32 verts in a batch, it needs to be at least 32 entries large, but since we can’t use the 32 oldest entries (since we’ll be shifting them out), that means we’ll effectively start with an empty FIFO on every batch. So, make it bigger, say 64 entries? That’s pretty big. And note that every vertex cache lookup involves comparing the tag (vertex index) against all tags in the FIFO – this is fully parallel, but it also a power hog; we’re effectively implementing a fully associative cache here. Also, what do we do between dispatching a shading load of 32 vertices and receiving results – just wait? This shading will take a few hundred cycles, waiting seems like a stupid idea! Maybe have two shading loads in flight, in parallel? But now our FIFO needs to be at least 64 entries long, and we can’t count the last 64 entries as vertex cache hits, since they’ll be shifted out by the time we receive results. Also, one FIFO vs. lots of shader cores? Amdahl’s law still holds – putting one strictly serial component in a pipeline that’s otherwise completely parallel is a surefire way to make it the bottleneck.

This whole FIFO thing really doesn’t adapt well to this environment, so, well, just throw it out. Back to the drawing board. What do we actually want to do? Get a decently-sized batch of vertices to shade, and not shade vertices (much) more often than necessary.

So, well, keep it simple: Reserve enough buffer space for 32 vertices (=1 batch), and similarly cache tag space for 32 entries. Start with an empty “cache”, i.e. all entries invalid. For every primitive in the index buffer, do a lookup on all the indices; if it’s a hit in the cache, fine. If it’s a miss, allocate a slot in the current batch and add the new index to the cache tag array. Once we don’t have enough space left to add a new primitive anymore, dispatch the whole batch for vertex shading, save the cache tag array (i.e. the 32 indices of the vertices we just shaded), and start setting up the next batch, again from an empty cache – ensuring that the batches are completely independent.

Each batch will keep a shader unit busy for some while (probably at least a few hundred cycles!). But that’s no problem, because we got plenty of them – just pick a different unit to execute each batch! Presto parallelism. We’ll eventually get the results back. At which point we can use the saved cache tags and the original index buffer data to assemble primitives to be sent down the pipeline (this is what “primitive assembly” does, which I’ll cover in the later part).

By the way, when I say “get the results back”, what does that mean? Where do they end up? There’s two major choices: 1. specialized buffers or 2. some general cache/scratchpad memory. It used to be 1), with a fixed organization designed around vertex data (with space for 16 float4 vectors of attributes per vertex and so on), but lately GPUs seem to be moving towards 2), i.e. “just memory”. It’s more flexible, and has the distinct advantage that you can use this memory for other shader stages, whereas things like specialized vertex caches are fairly useless for the pixel shading or compute pipeline, to give just one example.

Update: And here’s a picture of the vertex shading dataflow as described so far.

Shader Unit internals

Short versions: It’s pretty much what you’d expect from looking at disassembled HLSL compiler output (fxc /dumpbin is your friend!). Guess what, it’s just processors that are really good at running that kind of code, and the way that kind of thing is done in hardware is building something that eats something fairly close to shader bytecode, in spirit anyway. And unlike the stuff that I’ve been talking about so far, it’s fairly well documented too – if you’re interested, just check out conference presentations from AMD and NVidia or read the documentation for the CUDA/Stream SDKs.

Anyway, here’s the executive summary: fast ALU mostly built around a FMAC (Floating Multiply-ACcumulate) unit, some HW support for (at least) reciprocal, reciprocal square root, log2, exp2, sin, cos, optimized for high throughput and high density not low latency, running a high number of threads to cover said latency, fairly small number of registers per thread (since you’re running so many of them!), very good at executing straight-line code, bad at branches (especially if they’re not coherent).

All that is common to pretty much all implementations. There’s some differences, too; AMD hardware used to stick directly with the 4-wide SIMD implied by the HLSL/GLSL and shader bytecode (even though they seem to be moving away from that lately), while NVidia decided to rather turn the 4-way SIMD into scalar instructions a while back. Again though, all that’s on the Web already!

What’s interesting to note though is the differences between the various shader stages. The short version is that really are rather few of them; for example, all the arithmetic and logic instructions are exactly the same across all stages. Some constructs (like derivative instructions and interpolated attributes in pixel shaders) only exist in some stages; but mostly, the differences are just what kind (and format) of data are passed in and out.

There’s one special bit related to shaders though that’s a big enough subject to deserve a part on its own. That bit is texture sampling (and texture units). Which, it turns out, will be our topic next time! See you then.

Closing remarks

Again, I repeat my disclaimer from the “Vertex Caching and Shading” section: Part of that is conjecture on my part, so take it with a grain of salt. Or maybe a pound. I don’t know.

I’m also not going into any detail on how scratch/cache memory is managed; the buffer sizes depend (primarily) on the size of batches you process and the number of vertex output attributes you expect. Buffer sizing and management is really important for performance, but I can’t meaningfully explain it here, nor do I want to; while interesting, this stuff is very specific to whatever hardware you’re talking about, and not really very insightful.

Jim Blinn's Corner - A trip down the graphics pipeline.djvu电子书 Jim Blinn's Corner - A trip down the graphics 经典图形学书籍,很多基础的详解。 立即下载

相关推荐

A trip through the Graphics Pipeline 2011

介绍图形管线的书籍,对想了解和学习图形管线的人有很大的帮助,可以了解图形管线的主要流程。

图形渲染管线

一条链没有比其最薄弱的一环更强大 ---匿名 本章介绍了实时图形的核心组件,即图形渲染管道,也简称为“管道”。 管道的主要功能是在给定虚拟相机,三维对象,光源等的情况下生成或渲染二维图像。 因此,渲染管道是用于实时渲染的基础工具。 使用管道的过程如图2.1所示。 图像中对象的位置和形状取决于它们的几何形状,环境的特征以及相机在该环境中的位置。 对象的外观受材质属性,光源,纹理(应用于表面的图像)和着色方程式(shading equations)的影响。 图2.1。 在左图中,虚拟摄像头位于金字...

游戏开发、游戏引擎开发、逆向破解爱好者 880

A trip through the Graphics Pipeline

Graphics Pipeline的详细描述。整理自国外牛人博客。

Real-Time Rendering[Chapter2]The Graphic Rendering Pipeline

Real-Time Rendering[Chapter2]The Graphic Rendering Pipeline2.5 Pixel Processing(像素处理)2.5.1 Pixel Shading(像素着色)2.5.2 Merging(混合) 2.5 Pixel Processing(像素处理) 在这一步,所有的三角形或者图元中的像素点都在之前的步骤中被找到。pixel process...

RedGrey1993_的博客 463

STM32实现webserver显示数据及配置参数

其实提到的将fsdata.c重命名为fsdata_custom.c,可以不重命令,将/LWIP/Target/lwipopts.h中HTTPD_USE_CUSTOM_FSDATA 定义为0,这样HTTPD_FSDATA_FILE将使用fsdata.c,避免每次生成fsdata.c需要重命令的麻烦。2、登录成功后,板卡根据板卡配置类型,返回对应的数据页面,该页面可实现数据校准、清零,以及进行到板卡参数设置页面。目前还未实现post功能以及http身份验证功能。3、板卡参数设置页面。

2691

解决 Clawdbot 对接 Minimax 出现 401 (Token is unusable) 的问题排查与修复

即便确认 API Key 有效且余额充足,该问题仍可能在 Clawdbot 中持续出现,而在其他工具(如 curl 或 Postman)中测试正常。

hampeter的博客 3075

[转] A trip through the Graphics Pipeline

转一个国外牛人的文章;深入浅出的介绍了现代GPU的方方面面,从软件到硬件都有。理解了这些对理解新一代图形API大有好处! 原文 A trip through the Graphics Pipeline 2011: Index A trip through the Graphics Pipeline 2011: Index

蓝天和白云的博客 8589

Android/AutoMotive 多用户文件操作与权限管理

本文深入探讨了Android/Automotive多用户系统的文件操作与权限管理机制。文章阐述了多用户隔离与共享的核心原理,重点介绍了如何使用adb指令精准管理不同用户的应用与数据,并提供了在Android Automotive车载场景下实现安全跨用户数据共享与权限控制的最佳实践。

weixin_29214691的博客 162

《Real-Time Rendering 4th Edition》全文翻译 - 第2章 图形渲染管线(下)2.4 ~ 2.6(20200720翻新)

实时渲染(第四版)Real-Time Rendering (Fourth Edition) 第2章图形渲染管线Chapter 2The Graphics Rendering Pipeline 2.4光栅化阶段 Rasterization 给定经过变换和投影的顶点及其关联的着色数据之后(全部来自几何处理),下一阶段的目标是查找要渲染的图元(例如三角形)内的所有像素。我们称这种过程......

西河某人的博客 6431

现代 AI Agent 编排技术全景(1)—— 分类体系与四大技术维度

本文介绍基于 LLM 的现代 Agent 分类体系与四大技术维度,形成能够刻画不同 Agent 产品架构特征的分类法

佚失的诗篇 232

SpotBugs使用

spotbugs命令行参数解析

javajingling的专栏 2946

A trip through the graphics pipeline 2011 Part 10(翻译)

之前的几篇翻译都烂尾了,这篇希望。。。。能好些,恩,还有往昔呢。 ------------------------------------------------------------- primitive 图元 tris  三角形 quad  栅格 -----------------------------------------------------------...

anfu8346的博客 263

mac(apple silicon)arm64安装spm12详细教程

3.下载matlab runtime R2023b for macOS (macA64),注意必须下载这个,只下载matlab是不可以的。默认安装目录是/Applications/MATLAB/MATLAB_Runtime/R2023b。注意最后的命令,路径根据自己的matlab_runtime安装位置进行调整,然后就可以安装成功了。5. 解压spm12.zip,解压后将文件夹拖到应用程序下(Applications)2.下载spm12,这个版本可兼容mac芯片!6.打开mac终端,输入。

weixin_62332001的博客 2020

A trip through the Graphics Pipeline 2011, part 1

http://fgiesen.wordpress.com/2011/07/01/a-trip-through-the-graphics-pipeline-2011-part-1/ A trip through the Graphics Pipeline 2011, part 1 by fgiesen on July 1, 2011 It’s been awhile since

killcpp的专栏 1025

从零构建个性化AI助手:基于Mem0与本地大模型的记忆化实践

本文详细介绍了如何利用Mem0与本地大模型构建个性化AI助手,实现记忆化应用结构的实践方法。从开发环境搭建、记忆存储机制到个性化交互实现,提供了完整的解决方案和代码示例,帮助开发者在本地环境中创建能记住用户习惯和偏好的智能助手。

weixin_29214335的博客 464

Echarts - 去掉图表上的横线和竖线

效果图 默认效果: 变为如下图所示效果: 实现方法 官方配置文档:https://echarts.apache.org/zh/option.html#radar.splitLine 在 xAxis yAxis 配置中分别加入以下代码: splitLine: { show:false }

🏆 前端领域优质创作者 1万+

A trip through the Graphics Pipeline 2011, part 10

A trip through the Graphics Pipeline 2011, part 10 by fgiesen on July 20, 2011 Welcome back. Last time, we dove into bottom end of the pixel pipeline. This time, we’ll switch back to the midd

killcpp的专栏 1593

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

A trip through the Graphics Pipeline 2011, part 2

A trip through the Graphics Pipeline 2011, part 2 by fgiesen on July 2, 2011 Not so fast. In the previous part I explained the various stages that your 3D rendering commands go through on a P

killcpp的专栏 1104

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

A trip through the Graphics Pipeline 2011, part 8

A trip through the Graphics Pipeline 2011, part 8 by fgiesen on July 10, 2011 In this part, I’ll be dealing with the first half of pixel processing: dispatch and actual pixel shading. In fact

killcpp的专栏 871

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

A trip through the Graphics Pipeline 2011, part 13

A trip through the Graphics Pipeline 2011, part 13 by fgiesen Welcome back to what’s going to be the last “official” part of this series – I’ll do more GPU-related posts in the future, but th

killcpp的专栏 1074

A trip through the Graphics Pipeline 2011, part 9

A trip through the Graphics Pipeline 2011, part 9 by fgiesen on July 12, 2011 Welcome back! This post deals with the second half of pixel processing, the “join phase”. The previous phase was al

killcpp的专栏 767

A trip through the Graphics Pipeline 2011, part 11

A trip through the Graphics Pipeline 2011, part 11 by fgiesen on August 14, 2011 Welcome back! This time, the focus is going to be on Stream-Out (SO). This is a facility for storing the Outpu

killcpp的专栏 750

A trip through the Graphics Pipeline 2011, part 6

A trip through the Graphics Pipeline 2011, part 6 by fgiesen on July 6, 2011 Welcome back. This time we’re actually gonna see triangles being rasterized – finally! But before we can rasterize t

killcpp的专栏 646

A trip through the Graphics Pipeline 2011, part 4

A trip through the Graphics Pipeline 2011, part 4 by fgiesen on July 4, 2011 Welcome back. Last part was about vertex shaders, with some coverage of GPU shader units in general. Mostly, they’

killcpp的专栏 877

A trip through the Graphics Pipeline 2011, part 12

A trip through the Graphics Pipeline 2011, part 12 by fgiesen on September 6, 2011 Welcome back! This time, we’ll look into what is perhaps the “poster boy” feature introduced with the D3D11 /

killcpp的专栏 918

A trip through the Graphics Pipeline 2011, part 7

A trip through the Graphics Pipeline 2011, part 7 by fgiesen on July 8, 2011 In this installment, I’ll be talking about the (early) Z pipeline and how it interacts with rasterization. Like

killcpp的专栏 936
上一篇: A trip through the Graphics Pipeline 2011: Index
下一篇: A trip through the Graphics Pipeline 2011, part 4
killcpp
博客等级 码龄20年 13粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值