从硅晶圆到游戏画面:芯片底层原理与代码渲染全链路解析
芯片是什么?
集成电路(IC,Integrated Circuit)——把海量电子元件(晶体管、电阻、电容)用半导体工艺集成在一小块硅晶圆上,封装后能做特定功能的微型硬件模块
一、芯片的物理本质:从沙到“大脑”
-
原材料:高纯度单晶硅→切成薄圆片叫晶圆(Wafer)
-
光刻蚀刻:用紫外线在硅上“画电路”→形成几十亿个晶体管(CPU的ALU、GPU的SM都是晶体管组合)
-
封装:切下有用的“小方块”叫Die(裸片),外面裹塑料/金属+引脚→变成能插主板的芯片
FEOL(前段):造“晶体管”——芯片的“计算细胞”
目标是把硅晶圆变成几十亿个MOSFET晶体管,是“有源层”(能开关、能存电荷)。
核心步骤
-
衬底准备:高纯度单晶硅→切晶圆→掺杂成P型/N型硅
-
STI浅槽隔离:用SiO₂隔开不同晶体管,避免互相干扰
-
栅极制造:光刻出Gate(栅极)→控制晶体管导通(CPU的“开关”就在这)
-
源漏掺杂:在栅极两侧掺磷/硼→形成Source(源)/Drain(漏)→电流从S到D,Gate控制通断
-
High-k金属栅:现代工艺用HfO₂+金属栅→降低漏电,提升开关速度
→ 最终得到:硅上的“晶体管阵列”,CPU的ALU、GPU的SM、NPU的MAC全是晶体管组合~
“1T1C DRAM”=1个晶体管+1个电容,也是在FEOL里造的~
BEOL(后段):造“金属布线”——芯片的“神经血管”
晶体管造好后,要用铜线把它们连起来,形成电路→这是“无源层”,但决定了芯片能不能正常工作。
核心步骤
-
层间介质(ILD):在晶体管上盖SiO₂绝缘层
-
接触孔(Contact):打通晶体管Gate/Source/Drain到第一层金属
-
M1~M10多层金属:
-
底层M1连邻近晶体管(局部逻辑)
-
高层Mx连远距离模块(如CPU核心↔IMC↔PCIe)
-
现代先进制程(3nm)有10+层金属,最细线宽~几nm
-
-
通孔(Via):层与层之间打铜柱→垂直连接不同金属层
-
铜电镀+CMP:用铜填充沟槽→化学机械抛光→平整表面
→ 最终得到:晶体管的“电路网络”,CPU的Cache、IMC的总线、GPU的SM互联都靠BEOL~
FEOL=“在硅上造几十亿晶体管(计算细胞)”,BEOL=“用铜线把晶体管连成电路(神经血管)”,两者合起来才是能跑代码的芯片~
二、按功能分:你见过的都是芯片
-
计算芯片:CPU、GPU、NPU、TPU、LPU(都是“晶体管组成的计算逻辑”)
-
存储芯片:DDR4/5 DRAM、GDDR6、HBM、NAND Flash、SRAM(你学的Row Buffer就在DRAM芯片里)
-
控制芯片:主板南桥/北桥、USB控制器、网卡WiFi芯片
-
SoC(系统级芯片):手机骁龙/苹果M系列→把CPU+GPU+NPU+内存控制器+基带全集成在一颗芯片里
三、和“CPU/GPU”的关系:包含与被包含
-
芯片是大类,CPU/GPU/NPU是“计算类芯片”的具体种类
-
一颗CPU芯片=硅Die上的:核心+Cache+IMC+PCIe控制器→全集成
-
手机SoC=“CPU+GPU+NPU+调制解调器”的多功能芯片→比单独CPU更复杂
四、为什么叫“芯”?和板卡的区别
-
芯片(Chip):硅上的微型电路,是“功能核心”,不能直接用→要焊到PCB
-
板卡(如显卡/DIMM):PCB+多颗芯片+外围电路→比如RTX4090=1颗GPU芯片+12颗GDDR6芯片+供电
-
主板:更多芯片+插槽→CPU/DIMM/GPU都插在上面
DIMM上的DDR4芯片→存Row Buffer+电容阵列→主存的最小单元
CPU里的IMC→是CPU芯片内部的一个“子电路”
苹果M系列→一颗SoC芯片搞定CPU+GPU+NPU+UMA内存控制器→统一内存的基础
一层层“抽象阶梯”把“0/1电信号”翻译成“能写游戏的逻辑”?
第一层:物理层——晶体管的“开关魔法”
你刚学的FEOL造出MOSFET晶体管,本质是“用电控制的通断开关”:
-
栅极加电压→Source到Drain导通(=1);不加→断开(=0)
-
几十亿个晶体管组合成逻辑门(与/或/非)→再组合成加法器、寄存器、ALU
-
DRAM的1T1C电容存0/1,SRAM的6T存Cache→物理世界的“0和1”诞生了
这一步是硬件的底线:所有“计算”最终都是晶体管的开开关关~
第二层:指令集——给硬件“能听懂的方言”
纯晶体管太难直接控制,于是定义ISA指令集(如x86/ARM):
-
用一串0/1(机器码)表示“加、存、跳、读内存”→比如
ADD R1,R2对应固定二进制 -
CPU的译码器把机器码拆成晶体管动作→执行计算
-
这是“硬件”和“软件”的第一道桥:程序员不用管晶体管,只管发指令
第三层:OS+编译器——把“人话”变成“机器码”
你写的C#/C++不是机器码,要靠两层翻译:
-
编译器(如C#→IL→机器码,Burst编译):把
a+b翻译成“加载到寄存器→ALU加→存回内存”的指令序列 -
操作系统(Windows/macOS):管理CPU调度、内存分配、显卡调用→你
new Mesh()时,OS帮你找DDR4的连续空间,IMC发RAS/CAS拿数据
没有OS和编译器,你得手写0/1机器码——早期程序员真这么干,现在全靠工具链~
第四层:图形API+引擎——把“计算”变成“画面”
游戏要画3D,靠这两层抽象:
-
图形API(DirectX/Metal/Vulkan):封装GPU的复杂操作→你调
DrawMesh(),API自动发“光栅化、着色、写帧缓冲”的GPU指令 -
游戏引擎(Unity/Unreal):再封装成
GameObject、Rigidbody、Camera→你拖个Cube、写transform.position += speed*dt,引擎自动:-
算矩阵→传给GPU顶点着色器
-
ECS/NativeArray连续存数据→走DDR4→PCIe→GDDR帧缓冲
-
双缓冲Swap→显示器出画面
-
第五层:你的代码——只填“游戏规则”
到这一层,所有底层都被屏蔽了:
-
不用管晶体管怎么开关、Cache怎么命中、RAS/CAS怎么发
-
只写“玩家按W向前走”“敌人血<0就死”→引擎把逻辑翻译成:
-
CPU算ECS逻辑→IMC读DDR4→GPU算光照→NPU跑AI→帧缓冲上屏
-
不是“代码直接变游戏”,是晶体管→指令集→OS/编译器→图形API→引擎→你的逻辑
transform.position += Vector3.forward从C#到像素上屏的完整链路,看每一层到底干了啥?
Layer 0:你敲的代码(C#)
transform.position += Vector3.forward;
// Vector3.forward = new Vector3(0, 0, 1)
你以为这只是"加了个数"。实际上:
-
transform是个C#托管对象引用,指向一个Transform类的实例 -
position是个Vector3属性(property),背后有getter/setter -
Vector3.forward是个静态只读字段
但Unity不是纯C#运行时——Transform底层绑定了C++原生对象。你访问.position时,实际上在跨语言边界。
Layer 1:C# → C++ 跨桥(Unity的核心秘密)
Unity引擎的核心是C++写的,C#只是脚本层。这行代码触发的是:
// 伪代码,Unity内部实际做的事
Transform::set_position(
this.nativePtr, // 指向C++ Transform对象的指针
this.position + Vector3(0,0,1)
);
这一步发生了:
-
C#属性setter被调用
-
setter内部调用
UnityEngine.Transform.set_position_Injected()——这是个extern方法 -
P/Invoke跨到C++:C#运行时通过IL2CPP或Mono的interop层,把参数封送(marshaling)到原生内存4. 参数从托管堆拷贝到栈/原生内存
-
进入Unity C++引擎的Transform
Transform::SetPosition()
这一步本身就有开销。ECS/B之所以为什么要砍掉MonoBehaviour?就是因为每次C#→C++的跨桥都要花几纳秒,10000个对象就是几万次跨桥→卡顿。
Layer 2:C++层——Transform场景图的脏标记
C++的Transform::SetPosition()做了什么?
void Transform::SetPosition(const Vector3& newPos) {
m_localPosition = newPos;
m_dirty = true; // 关键:标记"我脏了"
m_dirtyFlags |= DIRTY_WORLD; // 世界坐标需要重新计算
// 通知父节点:我的子树也脏了
if (m_parent) {
m_parent->PropMarkChildrenDirty();
}
}
关键洞察:Unity不会立即重新计算世界坐标。它只是打了个脏标记(dirty flag)。
为什么?因为一个物体动了100个子物体(Mesh、Collider、Light...),全重新算太贵。等到真正需要世界坐标时才懒计算(lazy evaluation)。
)。
Layer 3:下一游戏循环——Unity的"心跳"
这行代码执行完,什么都没画。真正的画面计算发生在游戏循环的后续阶段:
FixedUpdate() → Physics → Animation → Transform Rebuild → Rendering
Transform Rebuild阶段(C++)
当渲染管线需要世界坐标时:
void TransformDispatch::SyncTransforms() {
for (auto* transform : dirtyTransforms) {
// 从localPosition算worldPosition
transform->m_worldMatrix =
transform->m_parent
? transform->m_parent->m_worldMatrix * LocalToWorld(transform->m_localPosition)
: LocalToWorld(transform->m_localPosition);
transform->m_dirty = false;
}
}
这里发生了矩阵乘法:
worldMatrix = parentWorldMatrix × Translate(localPosition) × Rotate(localRotation) × Scale(localScale)
每个×是4×4矩阵乘法× 4×4矩阵→64次浮点乘加。你那行"加个1",最终触发了64次FMA(乘加)指令。
Layer 4:CPU执行——从Cache到ALU
C++代码被编译成机器码,在CPU上跑:
; 伪汇编,示意
vmovups ymm0, [rcx+offset] ; 从内存加载4个float(Vector4,w=1)
vmulps ymm0, ymm0, [rbx] ; SIMD乘:4个float一次算完
vaddps ymm0, ymm0, [forward] ; SIMD加:+ (0,0,1,0)
vmovups [rcx+offset], ymm0 ; 写回内存
-
AVX/SSE指令:一次算4个float(128bit或256bit)
-
L1 Cache命中:
m_worldMatrix大概率在L1里(刚刚刚才访问过) -
推测执行:CPU猜预判你要循环,提前把下一组数据预取到L2
你之前学的Burst编译器干的就是这事:把C#的Vector3操作直接编译成SIMD汇编,连C++那层都省了→快3~5倍。
Layer 5:渲染线程——从CPU到GPU
Transform更新完,渲染线程醒来。Unity的主线程和渲染线程是分离的(双线程模型):
主线程:收集命令
void RenderLoop::Render() {
// 把"画这个Mesh"的指令塞进Command Buffer
CommandBuffer::DrawMesh(
mesh, // 顶点数据(在CPU内存)
worldMatrix, // 刚算完的世界矩阵
material, // 材质(shader参数)
subMeshIndex
);
}
渲染线程:提交到GPU
void GfxDevice::Submit() {
// 通过图形API(DX12/Vulkan/Metal)提交
dx12CommandList->Close();
dx12Queue->ExecuteCommandLists(1, &cmdList);
}
Command Buffer是什么?是一串GPU指令,像"用这个Shader、画这个Mesh、用这个矩阵"。CPU不用等GPU画完,继续跑下一帧。
Layer 6:GPU接收——从PCIe到GDDR
GPU的命令处理器(CP)从PCIe总线拉取Command Buffer:
CPU DDR4 → PCIe 5.0 x16 (~64GB/s) → GPU GDDR6 (~600GB/s)
顶点数据在哪里?
-
第一次:CPU把Mesh顶点从DDR4通过PCIe拷到GDDR6(叫"资源 Upload")
-
之后:顶点留在GDDR6,不用再拷(除非Mesh动态修改)
你之前学的统一内存(UMA)在这里发力:苹果M系列不用拷贝,CPU和GPU直接看同一块LPDDR5X→省掉PCIe搬运。
Layer 7:GPU渲染管线——从顶点到像素
GPU的SM(流处理器)开始干活:
顶点着色器(Vertex Shader)
// 每个顶点跑一次
#version 450
layout(location=0) in vec3 vertexPos;
layout(location=0) out vec4 worldPos;
uniform mat4 worldMatrix; // 你刚更新的Transform矩阵
void main() {
worldPos = worldMatrix * vec4(vertexPos, 1.0);
gl_Position = viewProjMatrix * worldPos;
}
这里发生了什么:
-
GPU从GDDR6读
vertexPos(Burst 64B进L1 Cache) -
从常量缓冲区读
worldMatrix(你那行代码的结果) -
SIMT执行:32个线程(warp)同时算32个顶点的
worldMatrix × vertexPos -
输出裁剪空间坐标
光栅化(Rasterization)
-
GPU把三角形投影到屏幕空间
-
决定哪些像素被覆盖
-
每个像素生成一个Fragment(片元)
像素着色器(Pixel Shader)
void main() {
outColor = vec4(1.0, 0.5, 0.2, 1.0); // 橙色Cube
}
深度测试 + 混合
-
比较Z Buffer(你之前学的Depth Buffer)
-
如果当前像素离相机更近→写入帧缓冲
Layer 8:帧缓冲→显示器
GPU把最终像素写进Back Buffer(GDDR6里的帧缓冲):
1920×1080×4(RGBA) = 8.3MB
+ Depth Buffer 8.3MB
= 16.6MB 在GDDR6里
VSync + Swap
-
GPU画完一帧→等VSync信号(60Hz = 每16.67ms一次)
-
Swap Chain:Back Buffer和Front Buffer交换指针(O(1),极快)
-
显示控制器从Front Buffer读像素→通过HDMI/DP→显示器面板
显示器面板
-
LCD/OLED像素点收到电信号→液晶偏转/LED发光
-
你看到Cube往前挪了一格
完整时间线
t=0μs 你按Play,代码执行
t=0.1μs C#→C++跨桥
t=0.5μs Transform打脏标记
t=5ms FixedUpdate结束,Transform Rebuild
t=5.1ms CPU算worldMatrix(SIMD)
t=5.5ms 渲染线程提交Command Buffer到GPU
t=5.6ms GPU顶点着色器(SIMT,32线程并行)
t=6ms 光栅化+像素着色器
t=10ms Back Buffer写完
t=16.67ms VSync触发,Swap,显示器刷新
t=16.68ms 你看到Cube移动了
从C#托管堆跨到C++引擎→打脏标记→游戏循环触发矩阵重算→CPU SIMD执行→渲染线程提交→PCIe传GPU→GDDR6顶点着色→光栅化→像素着色→帧缓冲Swap→显示器刷新,八层抽象把"硅的电信号"翻译成"屏幕上移动的Cube"。
发现单晶硅能跑游戏?
一百多年里一堆科学家+工程师接力堆出来的“技术金字塔”~
第一棒:单晶硅+半导体特性(1874~1947)
-
1874年:德国布劳恩发现半导体整流效应(硒晶体能单向导电)→ 第一次知道“硅/锗不是纯导体也不是绝缘体”
-
1918年:波兰切克劳斯基发明单晶硅拉制法(CZ法)→ 能做出纯净、无缺陷的硅晶棒→ 后来晶圆的基础
-
1947年:贝尔实验室肖克利、巴丁、布拉顿发明晶体管(点接触→结型)→ 取代笨重的真空管,计算机从房间大缩到桌子大→ 诺贝尔1956
单晶硅本身“能做电脑”的核心:硅的4价电子能精确掺杂成P/N型,实现“可控开关”~
第二棒:集成电路(1958~1960)——把多个晶体管焊一块
-
1958年:德州仪器杰克·基尔比把晶体管、电阻、电容做在同一块锗片上→ 第一颗集成电路(IC)→ 不用再手动焊电线
-
1959年:仙童半导体罗伯特·诺伊斯发明平面工艺+二氧化硅隔离→ 用光刻把电路“印”在硅上→ 现代芯片的雏形(基尔比和诺伊斯共享“IC之父”,基尔比拿诺奖2000)
这一步是关键:从“一个个晶体管手工连”→“整片硅上自动造电路”,电脑才能越做越小~
第三棒:微处理器=完整CPU在芯片上(1971)
-
1971年:Intel费德里科·费金+霍夫+马佐尔做出4004→ 全球第一颗CPU芯片(2300个晶体管)→ 电脑从“多块IC拼”变成“一颗芯片跑逻辑”
-
之后8086→x86→酷睿→手机骁龙,都是在这基础上堆晶体管~
第四棒:图形+游戏引擎(1980s~今)
-
1981年:IBM PC+Intel 8088+微软DOS→ 个人电脑普及
-
1992年:SGI+OpenGL、微软DirectX→ GPU能画3D
-
2005年:Unity成立→ 把“晶体管→指令→图形API→3D世界”封装成
GameObject→ 你写的transform.position才变成游戏~
背后的“底层逻辑”:不是“发现硅能算”,是“层层抽象”
没有人“发现单晶硅直接能跑游戏”,而是:
-
物理学家搞懂半导体物理→ 能做晶体管(开关)
-
工程师把晶体管组成逻辑门→ALU→CPU(能执行指令)
-
软件层定义指令集→编译器→OS→图形API→引擎→ 最终变成你能写的C#
-
每一层都是“在前人基础上加一层抽象”→ 百年积累成今天的电脑
为什么是硅?不是金/铜?
-
硅是地壳第二多的元素(沙子SiO₂提纯)→ 便宜
-
1.1eV带隙→ 室温下漏电低、开关稳定→ 比锗更适合做晶体管
-
能长高质量氧化层(SiO₂)→ 平面工艺的基础→ 其他材料做不到~
一个冷知识:最早“电脑”不用硅
-
1946年ENIAC:真空管(19000个,占满房间,发热惊人)
-
1958年后晶体管替代真空管→ 1960s IC→ 1971微处理器→ 才到今天的硅芯片~
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)