Skip to main content

Chapter 21: Shader Model Evolution

读完本章后,读者应能把 Shader Model 看成一份可执行契约:它规定 shader 源码能够调用哪些语言能力,编译器能够生成哪类中间表示,运行时需要查询哪些 feature bit,GPU 驱动最终承诺执行哪些 shader stage 和资源访问方式。这个理解会直接影响材质系统、渲染管线配置、跨平台 fallback 和性能排查。

本章用一个贯穿材料展开:一个跨平台 PBR 材质 draw call。它需要顶点变换、法线贴图、纹理采样、光照计算、可选透明裁剪、可选 GPU 端光源列表归约、可选 ray query 阴影,以及一个 Metal / Vulkan / Direct3D / OpenGL 的能力选择路径。同一段视觉目标在不同平台上会落到不同的 shader 契约上。

Shader Model 这个词来自 Direct3D 语境,它表达的问题可以迁移到其他图形 API。Vulkan 通过 SPIR-V 能力、device features、extensions 和 limits 表达类似边界;Metal 通过 GPU family、Metal version、function constants、argument buffers、SIMD-group 操作和 feature tables 表达边界;OpenGL 通过 GLSL 版本、profile、extensions 和 implementation limits 表达边界。工程判断应直接落到这一帧要使用的 shader 行为:当前设备、驱动、编译器和 API 状态是否同时支持这些行为。

本章的结论会落到一个稳定检查顺序:先确定视觉目标需要哪些 shader 行为,再把行为拆成 stage、资源绑定、执行粒度和同步需求,然后查询 API 暴露的 feature / extension / limit,最后为能力缺失的路径设计降级方案。

21.1 Shader Model as Programmable Pipeline Contract

Shader Model 是可编程管线和 GPU 执行之间的契约。契约的输入是 shader 源码、资源声明、pipeline state 和目标 profile;契约的输出是驱动可接受的编译产物、可绑定的资源布局、可执行的 stage 集合,以及运行时能否创建 pipeline 的判断。Microsoft 的 HLSL 文档把 shader model 描述为一系列能力层级,每个新模型建立在旧模型之上,并减少限制;这条官方表述支撑了“Shader Model 是能力契约”这个阅读角度,Shader Models vs Shader Profiles 同时列出了各 model 对应的 compile profile。

贯穿材料中的 PBR draw call 至少需要两个基础 stage:vertex shader 把 position、normal、tangent、UV 从 mesh attribute 转成后续插值所需数据;pixel / fragment shader 从 base color、normal、roughness、metallic 等纹理和常量缓冲读取数据,输出 render target 颜色。这个最小路径在 OpenGL、Vulkan、Metal、Direct3D 中都能表达,但语法、绑定方式和能力查询入口不同。

把 Shader Model 看成契约时,需要同时检查四类对象。第一类是 stage,例如 vertex、pixel / fragment、compute、mesh、ray generation。第二类是资源,例如 constant buffer、texture、sampler、storage buffer、unordered access view、acceleration structure。第三类是执行粒度,例如 single invocation、quad、wave / subgroup、threadgroup。第四类是编译目标,例如 HLSL profile、SPIR-V capability、MSL language version、GLSL version。这四类对象中任意一类不匹配,shader 源码都可能编译失败、pipeline 创建失败或在运行时走到错误路径。

下面的图把这一份契约放回一次 draw call。图中没有展开具体 API 语法,只保留必须被检查的边界。

这条路径的关键点是:shader 源码本身无法独立决定 GPU 行为。源码中的 Texture2DSamplerStateWaveActiveSumgroupsharedrayQueryEXTthreadgroup 只是请求能力;API 查询、pipeline 创建和驱动编译才决定请求能否落地。Direct3D 12 中可通过 ID3D12Device::CheckFeatureSupport 查询当前 driver 支持的功能,Microsoft 文档明确说明该方法返回当前 graphics driver 支持的 feature 信息,ID3D12Device::CheckFeatureSupport 是本章后续 Direct3D 判断的工具入口。

这份契约也解释了为什么同一段 HLSL 在不同设备上需要不同 target。ps_5_0 能表达 PBR 材质的传统像素计算,但 wave-level light list reduction 需要 Shader Model 6 的 wave intrinsics。Microsoft 的 Shader Model 6.0 文档说明,早期 model 在语言层面只暴露单个线程执行,而 6.0 开始提供 wave-level operations,用来显式利用同一 core 上 lockstep 执行的多个线程;这对应本章中的执行粒度升级,HLSL Shader Model 6.0 给出了 wave query、vote、broadcast、reduction 和 prefix 等 intrinsic 分类。

从工程角度看,Shader Model 版本号只回答“语言和编译目标能表达什么”。它还需要和 API feature level、driver、OS、SDK、GPU family 组合判断。一个材质系统写成“开启 SM6 就使用所有现代 shader 功能”会把多个层级混成一个开关;稳定做法是把材质特性拆成独立 capability,例如 wave reduction、bindless-style descriptor indexing、ray query、mesh shader、native 16-bit arithmetic、barycentric coordinate,再分别建立查询和 fallback。

下面的最小 HLSL 片段展示一个能力契约的边界。第一段传统 pixel shader 只依赖纹理采样和常量输入。第二段加入 wave reduction 后,shader 需要 SM6 相关能力;运行时还要确认设备支持 wave operation。

// Traditional material path, suitable for a broad SM5-style profile.
Texture2D baseColorTexture : register(t0);
SamplerState linearSampler : register(s0);

float4 main_ps(float2 uv : TEXCOORD0) : SV_Target0
{
float3 baseColor = baseColorTexture.Sample(linearSampler, uv).rgb;
return float4(baseColor, 1.0);
}
// Wave-level path, gated by SM6 and device feature checks.
float ComputeTileIntensity(float localIntensity)
{
float waveSum = WaveActiveSum(localIntensity);
return waveSum / WaveGetLaneCount();
}

这两个代码片段的视觉目标可能都只是“给材质算一个颜色”,但契约完全不同。第一段需要纹理、采样器和 pixel shader profile;第二段把多个 lane 的数据交换纳入 shader 行为,排查时必须检查 wave size、supported stages、编译 profile 和 driver feature。后续章节讨论具体 shader stage 时,会继续拆开这些输入、输出和性能风险。

21.2 固定函数阶段到可编程管线的转变

固定函数管线把变换、光照、纹理组合和若干像素操作封装成 API 状态。开发者设置矩阵、光源、材质参数、texture environment 和 render state,驱动按照预设路径执行。这种模式适合早期硬件:GPU 只需要执行少量固定单元,API 状态表就能覆盖常见效果。问题在于视觉需求一旦超过预设组合,开发者只能用更多 pass、更多纹理阶段或 CPU 预处理绕过限制。

可编程管线把同一条数据路径改成 shader 控制。矩阵变换进入 vertex shader,材质组合进入 pixel / fragment shader,几何放大曾由 geometry shader 和 tessellation 承担,通用并行任务进入 compute shader,现代 Direct3D / Vulkan 又加入 mesh shader 和 ray tracing 相关 stage。OpenGL GLSL 4.60 规范仍以 processor / shader stage 的形式组织 vertex、tessellation、geometry、fragment 和 compute 处理器,The OpenGL Shading Language 4.60.8 体现了可编程阶段成为现代图形管线基本表达方式。

回到 PBR draw call,固定函数时代的光照路径通常由 API 状态描述。例如材质有 diffuse、specular、shininess,光源有 position、color、attenuation,纹理阶段按固定组合参与最终颜色。PBR 材质要求 normal map、roughness、metallic、F0、环境贴图、IBL lookup、shadow term 和 color space 处理,这些组合很难塞进固定状态表。shader 接管后,材质系统可以把这些数据组织成 buffer 和 texture,再由 pixel shader 按 BRDF 公式读取和计算。

这个转变改变了四条工程路径。变换路径从“设置全局矩阵状态”转为“把矩阵写入 uniform / constant buffer,由 vertex shader 显式使用”。材质路径从“选择固定 texture combine mode”转为“在 shader 中组合多个纹理和参数”。资源路径从“有限 texture slot”转为“descriptor、argument buffer、bind group 或 sampler / texture 对象的显式绑定”。性能路径从“状态数量和 pass 数量约束”转为“shader ALU、纹理带宽、寄存器、分支、wave occupancy 和同步约束”。

以下片段用同一段材质需求展示可编程管线的表达方式。代码省略完整 PBR 公式,只保留资源输入和关键数据路径。

cbuffer CameraAndMaterial : register(b0)
{
float4x4 viewProjection;
float3 cameraPosition;
float roughnessScale;
};

Texture2D baseColorTexture : register(t0);
Texture2D normalTexture : register(t1);
Texture2D materialTexture : register(t2);
SamplerState materialSampler : register(s0);

struct PixelInput
{
float4 positionCS : SV_Position;
float2 uv : TEXCOORD0;
float3 normalWS : TEXCOORD1;
float3 tangentWS : TEXCOORD2;
};

float4 main_ps(PixelInput input) : SV_Target0
{
float3 baseColor = baseColorTexture.Sample(materialSampler, input.uv).rgb;
float3 packedNormal = normalTexture.Sample(materialSampler, input.uv).xyz * 2.0 - 1.0;
float roughness = materialTexture.Sample(materialSampler, input.uv).g * roughnessScale;
float lighting = saturate(dot(normalize(input.normalWS + packedNormal * 0.1), float3(0.0, 1.0, 0.0)));
return float4(baseColor * lighting * (1.0 - roughness * 0.25), 1.0);
}

这段代码把固定状态表无法稳定表达的材质行为放进了 shader。输入来自 constant buffer、多个 texture、sampler 和 vertex shader 输出;输出进入 render target。排查一张 PBR 材质发黑的图片时,工程师需要按数据路径检查:纹理是否绑定到正确 slot,sampler 是否匹配颜色空间和 mipmap,normal 是否在预期坐标空间,roughness 通道是否与导入约定一致,shader profile 是否支持当前资源对象和指令。

可编程化带来的另一个变化是“视觉效果”和“硬件执行”之间的距离变短。固定函数把硬件细节隐藏在 driver 内部;shader 把分支、循环、采样次数、临时变量和 group / wave 操作暴露给开发者。一个 alpha cutout 材质在视觉上只是镂空树叶,但 shader 中的 discard 可能影响 early depth、helper invocation 和 overdraw;一个 tiled light culling shader 在视觉上只是更多动态光源,但 compute shader 中的 shared memory、barrier 和 wave reduction 会影响 occupancy 和 synchronization cost。

因此,从固定函数到可编程管线的核心收益是表达能力提升,核心代价是契约检查责任转移到引擎。引擎需要记录每个材质变体依赖的 shader model、resource binding、precision、stage 和 feature bit;工具捕获时需要把 draw call 的视觉结果回溯到 shader 输入、pipeline state 和资源绑定。

21.3 Shader Model 不同版本的功能差异解析

比较 SM4、SM5、SM6 时,需要使用同一组维度:stage 范围、资源模型、并行粒度、编译产物、典型工程能力和排查证据。版本号本身只是入口;真正影响 draw call 的是这些维度如何改变数据路径。

SM4 对应 Direct3D 10 时代的 common-shader core。Microsoft 文档说明从 Windows Vista 起 Shader Model 4 是一次完整 redesign,允许在硬件约束内使用无限 instruction 和 constant,并引入更清晰的 texture sampling 对象模型。这一阶段的工程意义是:vertex、geometry、pixel 等可编程阶段共享更统一的能力,shader 从早期高度受限的 profile 进入统一管线表达。

SM5 对应 Direct3D 11 的能力扩展。Microsoft 的 SM5 文档称它是 SM4 能力的 superset,并引入 compute shader,提供高性能通用计算能力;同页还列出 cs_5_0、ds_5_0、gs_5_0、hs_5_0、ps_5_0、vs_5_0 等 profile,Shader Model 5 支撑了 tessellation、compute、structured buffer、byte address buffer 这些工程特性。对 PBR draw call 来说,SM5 让材质系统能把光源剔除、粒子更新、tile / cluster 数据准备放入 compute shader,再把结果供 pixel shader 使用。

SM6 对应 Direct3D 12 时代更细粒度的 GPU 并行表达和 DXIL 编译生态。SM6 最直接的变化是 wave intrinsics:shader 可以显式访问同一 wave 内的 lane,并做 vote、broadcast、reduction 和 prefix 操作。Microsoft 的 SM6.0 文档把 lane 定义为 single thread of execution,把 wave 定义为同时在处理器中执行的一组 lanes,并指出 wave 类似 warp / wavefront;这说明 SM6 把过去隐含在硬件调度中的部分并行结构暴露给 shader 作者。

下面的表格把三个版本放在同一组维度下比较。表格中的“典型能力”只保留工程判断时最容易影响材质系统和渲染架构的差异。

维度SM4SM5SM6
管线阶段统一 common-shader core,包含 vertex / geometry / pixel 等传统图形阶段增加 hull / domain tessellation profile,compute shader 成为核心工程入口延续传统 stage,并通过新 target 和 API option 扩展 wave、ray tracing、mesh 等现代能力
资源模型texture / sampler 对象化,constant buffer 成为常规数据入口structured buffer、byte address buffer、UAV、compute 读写路径更常用descriptor heap、root signature、bindless 风格访问、payload / acceleration structure 等能力按 API option 查询
并行粒度shader 主要按单个 invocation 编写,硬件并行由实现隐藏compute 引入 thread group、shared memory 和 barrierwave / subgroup 级操作进入语言层,lane 之间可直接协作
编译与中间表示面向 Direct3D 10 profile面向 Direct3D 11 profile,FXC 生态常见面向 DXIL 和 DXC 生态,profile 例如 ps_6_0cs_6_0
PBR 工程收益稳定表达基础材质和 per-pixel 光照GPU 端 light culling、tessellation displacement、更多 buffer 组织方式wave reduction、ray query / DXR 路径、mesh shader 数据准备、现代 descriptor 策略
排查重点profile 是否匹配 shader stage 和资源对象UAV / buffer / compute dispatch / barrier 是否正确shader model、OS runtime、driver feature、wave size、DXIL validator 和 pipeline option 是否同时满足

对本章贯穿的 PBR draw call,SM4 足以表达“每个像素读取几张纹理并计算光照”。SM5 开始适合把“这张图中哪些光源影响当前 tile”提前放入 compute shader,pixel shader 只遍历较短的 light list。SM6 进一步允许 compute 或 pixel shader 在 wave 内聚合数据,减少部分 shared memory 和 barrier 成本;它也把 ray tracing、mesh shader 等现代路径纳入 Direct3D 12 的 feature 查询体系。

下面的伪 C++ 片段说明 Direct3D 12 中的 shader model 检查需要走正式 feature 查询路径。D3D12_FEATURE_SHADER_MODEL 查询返回设备和 runtime 同时支持的最高 shader model;Microsoft 文档说明调用前要把 HighestShaderModel 初始化为应用理解的最高 model,调用成功后字段包含设备支持且不高于传入值的最高 model,D3D12_FEATURE_DATA_SHADER_MODEL 给出了这个规则。

D3D12_FEATURE_DATA_SHADER_MODEL shaderModel = {};
shaderModel.HighestShaderModel = D3D_SHADER_MODEL_6_6;

HRESULT result = device->CheckFeatureSupport(
D3D12_FEATURE_SHADER_MODEL,
&shaderModel,
sizeof(shaderModel));

if (SUCCEEDED(result) && shaderModel.HighestShaderModel >= D3D_SHADER_MODEL_6_0) {
EnableWaveShaderPath();
} else {
EnableClassicMaterialPath();
}

这段检查只能判断 shader model 上限,仍然不能自动推出所有现代 feature 可用。D3D12 的 feature enumeration 中还包含 D3D12_FEATURE_D3D12_OPTIONS1 用于 HLSL 6.0 wave operation 支持,D3D12_FEATURE_D3D12_OPTIONS5 涉及 ray tracing,D3D12_FEATURE_D3D12_OPTIONS7 涉及 mesh / amplification shader 和 sampler feedback;Microsoft 的 D3D12_FEATURE enumeration 把这些查询项分开列出。工程结论是:SM6 是进入现代 HLSL 能力的门槛之一,具体特性还要继续查询对应 option。

版本差异还会影响工具证据。RenderDoc、PIX、Nsight 或 Xcode GPU tools 中看到的 shader target、bound resources、descriptor set / root signature / argument buffer、threadgroup size、wave size、ray tracing pipeline state,都是判断契约是否满足的证据。排查时优先看“当前 draw / dispatch 实际绑定了什么”,再看“shader 编译目标声称需要什么”,最后看“设备查询返回什么”。这个顺序能把 shader 编译问题、资源绑定问题和硬件能力问题拆开。

21.4 实践:当前主流 GPU Shader 特性支持情况

当前主流 API 的实践判断应按 API 自己的能力模型查询。Direct3D 使用 feature level、shader model、D3D12 options 和 DXIL / DXC 工具链;Vulkan 使用 physical device properties、features、extensions、limits、formats 和 SPIR-V capabilities;Metal 使用 Metal version、GPU family、feature set tables、supportsFamily、argument buffer tier、function constant 和 pipeline state;OpenGL 使用 context version、profile、GLSL version、extensions 和 implementation limits。把这四套系统压成一个“支持现代 shader / 不支持现代 shader”的布尔值,会丢失工程上最需要的边界。

实践中的第一步是列出当前帧需要的 shader 特性。本章 PBR draw call 可拆成六个 feature:基础 vertex / pixel shader、PBR 纹理采样、structured light list、compute prepass、wave / subgroup reduction、可选 ray query shadow。每个 feature 对应不同查询入口。基础材质只需要常规 shader stage 和纹理能力;compute prepass 需要 compute shader、storage buffer / UAV、barrier;wave reduction 需要 wave / subgroup;ray query 需要 acceleration structure、ray query 或 ray tracing pipeline 相关 feature。

Direct3D 路径中,先查询 adapter 和 feature level,再查询 D3D12_FEATURE_SHADER_MODEL,随后查询 options。Microsoft 文档把 D3D12_FEATURE_SHADER_MODELD3D12_FEATURE_D3D12_OPTIONS1D3D12_FEATURE_D3D12_OPTIONS5D3D12_FEATURE_D3D12_OPTIONS7 等放在同一个 feature enumeration 中,说明这些能力需要按项查询。一个稳妥的材质系统会把 ClassicPBRComputeLightCullingWaveOptimizedLightCullingRayQueryShadow 分成不同 permutation key,并把每个 key 对应的查询结果记录到日志。

Vulkan 路径中,先枚举 physical device,再查询 properties、features、extensions、limits、formats。Khronos 的 Vulkan Guide 明确把这些项目列为 physical device 可查询信息,并说明 extension 可以增加 Vulkan functions、enums、structs 或 feature bits;features 描述随实现变化的功能,需要查询并在创建设备时启用,Querying Properties, Extensions, Features, Limits, and Formats 给出了这个检查框架。对 PBR 材质而言,descriptor indexing、storage buffer、subgroup、ray query、mesh shader 都应映射到具体 feature struct 或 extension。

Vulkan 的 subgroup 对应 HLSL wave 语境。Khronos Vulkan Guide 把 subgroup 定义为一组 shader invocations,它们能高效同步并共享数据;在 compute shader 中 local workgroup 是 subgroup 的超集,Subgroups 还说明 subgroup size 可能随实现变化,并给出 VkPhysicalDeviceSubgroupProperties 的查询方式。因此,Vulkan 版 wave reduction 应读取查询结果后再设置参数;固定 32 或 64 lanes 的假设会带来错误边界。shader 和 CPU 配置都应从 supported stages、supported operations 和 subgroup size 控制能力中得到边界。

Vulkan ray tracing 路径需要另一组对象。Khronos Vulkan Guide 说明 Vulkan ray tracing 由一组相互关联的 extensions 提供,包括 acceleration structure、ray tracing pipeline、ray query、pipeline library 和 deferred host operations;其中 acceleration structure 是 implementation-dependent opaque representation,用于让 ray tracing 针对已知数据布局高效执行,Ray Tracing 还把 ray tracing pipeline 的 shader domains 和 vkCmdTraceRaysKHR 路径列出来。对本章 PBR 材质来说,ray query shadow 更适合作为可选路径:设备支持时在 shader 中查询阴影,设备能力不足时回落到 shadow map。

Metal 路径中,版本和 GPU family 的关系更强。Apple 的 Metal overview 说明 Metal 是面向 Apple silicon 的现代 graphics and compute API,并包含 GPU profiling 和 debugging 工具;同页还说明 Metal 4 支持 A14 Bionic 或更新的 iPhone / iPad / Apple TV、Apple silicon Mac 和 Vision Pro,Metal Overview 给出了平台边界。Apple 的 2026 年 2 月 5 日 Metal Feature Set Tables 列出 A14、M1 起支持 Metal 3 & 4,并列出 argument buffers tier 2、SIMD-scoped reduction operations、barycentric coordinates、variable rasterization rate、vertex amplification 等按 GPU family 暴露的功能。工程上应按 GPU family 和 feature row 判断,而非只看“系统是否有 Metal”。

Metal 对应的 PBR feature 可以这样落地:基础材质使用 vertex / fragment function、texture 和 sampler;light list 使用 buffer 和 argument buffer;wave / subgroup 类逻辑对应 SIMD-group 操作;ray tracing 路径依赖 acceleration structure 和 Metal ray tracing API;工具证据来自 Xcode GPU Frame Capture、Metal debugger、Metal performance HUD 和 shader validation。Apple 的 Metal overview 明确提到从 mesh shading 到 ray tracing 到 machine learning 的调试和性能工具,这说明现代 Metal shader 支持情况应同时进入功能判断和工具验证。

OpenGL 路径的实践重点是 context version、GLSL version 和 extension。GLSL 4.60.8 是当前 OpenGL shading language 规范版本,OpenGL Shading Language 4.60.8 可作为语言能力基线资料。OpenGL 能稳定表达传统 PBR 材质、UBO / SSBO、tessellation、geometry 和 compute shader,但 ray tracing 和 mesh shader 这类现代路径通常不会作为 OpenGL 原生主线设计。跨平台引擎通常把 OpenGL profile 作为 classic material path 或兼容路径,把 Vulkan / Direct3D 12 / Metal 作为现代显式能力路径。

下面的表格把四个 API 的查询入口对齐到同一组实践问题。

实践问题Direct3D 12VulkanMetalOpenGL
当前 shader model / language 能力D3D12_FEATURE_SHADER_MODEL、DXC target profileSPIR-V version、capability、GLSL / HLSL front-end targetMetal version、MSL version、GPU familyOpenGL context version、GLSL version
资源绑定能力root signature、descriptor heap、resource binding tierdescriptor set、descriptor indexing、descriptor buffer、limitsargument buffer tier、resource heaps、function argumentstexture units、UBO / SSBO limits、sampler limits
wave / subgroup 能力D3D12_FEATURE_D3D12_OPTIONS1 与 HLSL wave intrinsicVkPhysicalDeviceSubgroupProperties、subgroup feature bitsSIMD-group operation feature rowsvendor extension 或更受限路径
ray tracing 能力DXR tier、acceleration structure、raytracing pipelineVK_KHR_acceleration_structureVK_KHR_ray_tracing_pipelineVK_KHR_ray_queryMetal ray tracing API 和 GPU family通常使用独立加速库或回落路径
mesh / amplification 能力D3D12_FEATURE_D3D12_OPTIONS7 等 optionsVK_EXT_mesh_shader 或对应平台暴露mesh shading / vertex amplification feature row传统 vertex / geometry / tessellation 路径
工具证据PIX、RenderDoc、driver debug layerRenderDoc、validation layer、vulkaninfoXcode GPU tools、Metal debugger、HUDRenderDoc、API debug output、driver extension string

对于本章的 PBR 材质,推荐把运行时决策写成 feature profile,而非写成 API 名称。一个合理的 profile 可以包含 ClassicPBRComputeCulledPBRWaveOptimizedPBRRayQueryShadowPBR 四层。Direct3D、Vulkan、Metal、OpenGL 分别填充这些 profile 的支持状态;渲染器选择最高可用路径,并把 fallback 原因写入调试日志或 overlay。这样一来,用户看到阴影质量下降或动态光源数量减少时,工程师能从 profile 直接回到 feature 缺失、资源限制或 shader 编译目标。

最小检查顺序如下。先从视觉目标列 feature,例如 normal map、clustered light、wave reduction、ray query shadow。再把每个 feature 映射到 stage、resource、execution granularity 和 compile target。随后按 API 查询设备能力:Direct3D 查 shader model 和 options,Vulkan 查 features / extensions / limits,Metal 查 GPU family 和 feature table,OpenGL 查 version / extension / limits。接着创建对应 pipeline 并记录 shader target、resource layout 和 fallback key。最后用工具捕获一帧,确认 draw call 实际走到的 shader、绑定资源和输出 render target 与期望一致。

这个顺序能把“当前 GPU 支持什么”变成可复查的工程事实。它以 API 暴露的正式查询入口作为能力证明,把具体 GPU 型号和厂商宣传语降为背景信息;实际判断来自被拆小的 shader 行为和逐项查询结果。

最小自检任务

给定一个跨平台材质需求:基础 PBR 材质需要 base color、normal、roughness 三张纹理;前向渲染中每个 tile 最多 64 个动态光源;支持条件满足时使用 wave / subgroup reduction 统计 tile 内有效光源;支持条件满足时使用 ray query 计算一条硬阴影;能力不足的平台保持基础 PBR 和 shadow map 路径。请设计这个材质在 Direct3D 12、Vulkan、Metal、OpenGL 上的 shader capability 检查顺序,并说明每一步回答什么问题。

答案要点

先把材质需求拆成四层 profile:ClassicPBRComputeLightCullingWaveOptimizedLightCullingRayQueryShadowClassicPBR 检查 vertex / pixel 或 vertex / fragment stage、纹理采样、常量缓冲和 render target 输出;它回答基础材质能否渲染。ComputeLightCulling 检查 compute shader、storage buffer 或 UAV、dispatch 和同步;它回答 tile 光源列表能否在 GPU 端生成。WaveOptimizedLightCulling 检查 HLSL wave、Vulkan subgroup、Metal SIMD-group 或 OpenGL 对应扩展;它回答 lane / subgroup 内归约能否替代一部分 shared memory 和 barrier 路径。RayQueryShadow 检查 acceleration structure、ray query 或 ray tracing pipeline 相关能力;它回答硬阴影能否从 shadow map 路径升级到 ray query 路径。

Direct3D 12 中,先用 adapter / feature level 建立设备基础能力,再查询 D3D12_FEATURE_SHADER_MODEL 得到最高 shader model,继续查询 D3D12 options:wave operation 对应 D3D12_FEATURE_D3D12_OPTIONS1,ray tracing 对应 options5 或对应 DXR tier,mesh / amplification 和 sampler feedback 对应 options7。每个查询结果只打开对应 profile。Vulkan 中,先枚举 physical device,查询 properties、features、extensions、limits 和 formats;subgroup 用 VkPhysicalDeviceSubgroupProperties 检查 supported stages 和 operations;ray query 检查 VK_KHR_acceleration_structureVK_KHR_ray_query 等 extension 和 feature struct。Metal 中,先读取 Metal version 和 GPU family,再按 feature set tables 判断 argument buffers、SIMD-scoped operations、ray tracing、mesh shading 或 vertex amplification 等能力;OpenGL 中,先读取 context / GLSL version、extension string 和 UBO / SSBO / texture limits,把它作为 classic 或兼容 profile。

最终选择顺序是从最高 profile 向下尝试,但每一层都记录 fallback 原因。若 ray query 缺失,就保留 shadow map;若 wave / subgroup 缺失,就使用普通 compute reduction;若 compute 路径缺失,就使用 CPU 或较小光源列表;若基础材质路径缺失,当前渲染器应停止提交该 draw call 并报告缺失能力。工具复查时查看 shader target、resource layout、draw / dispatch 绑定、pipeline state 和输出 render target,确认实际路径与 profile 决策一致。

本章知识点总结

  • 契约视角:Shader Model 应理解为 shader 源码、编译目标、API feature、资源绑定和 GPU 执行之间的可编程契约。
  • 贯穿路径:一个 PBR draw call 会同时触发 stage、resource、execution granularity 和 compile target 四类检查。
  • 源码边界:shader 源码只表达能力请求,设备查询、pipeline 创建和驱动编译共同决定请求能否执行。
  • 固定函数:固定函数管线用 API 状态描述变换、光照和纹理组合,适合预设效果路径。
  • 可编程化:可编程管线把变换、材质、采样、光照和部分并行计算交给 shader 控制。
  • 工程代价:shader 接管视觉逻辑后,引擎必须记录材质变体依赖的 profile、资源布局、feature bit 和 fallback。
  • SM4 定位:SM4 代表 common-shader core 和更统一的传统图形阶段能力。
  • SM5 定位:SM5 扩展了 compute shader、tessellation、structured buffer 和 GPU 端数据准备路径。
  • SM6 定位:SM6 把 wave-level 并行操作暴露给 HLSL,并进入 DXIL / DXC 生态。
  • 版本边界:SM6 只说明一类语言和编译目标能力,ray tracing、mesh shader、VRS 等特性还要查对应 D3D12 options。
  • Vulkan 查询:Vulkan 应按 physical device properties、features、extensions、limits、formats 和 SPIR-V capability 逐项判断。
  • Metal 查询:Metal 应按 Metal version、GPU family、feature set tables 和 Xcode 工具证据判断 shader 能力。
  • OpenGL 查询:OpenGL 应以 context version、GLSL version、extensions 和 implementation limits 作为 classic path 边界。
  • Profile 设计:跨平台材质系统应使用 ClassicPBRComputeLightCullingWaveOptimizedPBRRayQueryShadowPBR 这类 capability profile 管理路径。
  • 排查顺序:稳定排查先列视觉 feature,再映射 stage / resource / execution / target,随后查询 API 能力,最后用工具捕获验证实际 draw 或 dispatch。