ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Slang 标准库内部机制:Core Module 内建指令(Intrinsics)与属性系统全解析

Slang 标准库内部机制:Core Module 内建指令(Intrinsics)与属性系统全解析 Slang 标准库内部机制Core Module 内建指令Intrinsics与属性系统全解析【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang导读本文聚焦 Slang 编译器中标准库尤其是核心模块core如何借助一组以__前缀标记的内部机制intrinsics 与 attributes为 HLSL、GLSL/SPIR-V、CUDA、C 等不同目标平台注入目标特定target-specific特性。你将掌握__target_intrinsic展开规则、$()/${{{{代码生成机制、attribute_syntax属性定义语法以及__glsl_version、vk::spirv_instruction等平台专用修饰符的实际用法与源码级原理从而有能力读懂甚至扩展 Slang 的.meta.slang标准库代码。稳定性警告原文档原文要点这些特性不应被视为稳定 API。它们可以用于普通 Slang 代码以增加特性但任何 Slang 版本升级都可能使其失效且其实现往往高度贴合特定功能需求并非在所有场景下都按预期工作。由于这些特性仍在快速演进本文档也可能落后于 Slang 代码库中当前可用的特性。若想为某个目标平台添加特性支持将其实现为 Slang 标准模块的一部分通常是推进该工作的好途径——尽管部分扩展/特性无法仅靠标准模块修改完成但绝大多数目标特定扩展的支持都至少涉及 Slang 标准模块包括核心模块的改动且通常会用到这里描述的机制。核心模块Core Module与.meta.slang文件这些特性最集中的使用场所是 Slang 核心模块它由source/slang目录下的一组 Slang 文件实现core.meta.slang—— 核心类型系统、接口如IComparable、IArithmetic、内置属性定义hlsl.meta.slang—— HLSL 风格类型与 D3D 内建函数绑定约 3.4 万行是最大的 meta 文件diff.meta.slang—— 自动微分相关的内部声明glsl.meta.slang—— GLSL 类型别名vec2/mat4等与 GLSL 内建映射。查看这些文件即可直观体会本文所述特性在实战中的用法。多数 intrinsics 与 attributes 的名称带有__前缀用以表明它们并非面向普通用户代码。同样的约定也适用于spirv_asm块内命名的结果寄存器内部寄存器必须使用__前缀例如%__result因为 SPIR-V 发射器会把spirv_asm中每个具名寄存器转换为输出中的OpName此前缀可避免这些调试名称被误认为用户符号解析器会对核心模块代码强制执行此断言。详见 coding-conventions.md 中 Internalspirv_asmresult registers 一节。${{{{/}}}}用 C 程序化生成 Slang 代码.meta.slang文件看起来与普通 Slang 源码基本一致但其内容也可以用 C 代码程序化生成一段代码若以${{{{开头、以}}}}结束中间即为 C 代码段。该机制通常用于批量生成一系列结构相似的代码。C 代码中计算出的值可通过$()引用括号内是可由 C 侧计算的表达式。例如要生成一个值为 0 到 9 的数组可以这样写// Slang code ${{{{ // C code, calling out to a C function getTime, the result is held in variable time int cppTime getTime(); }}}} // Back to Slang code, can access the C variable previously defined as cppTime. Due to $(). // The code inside the $() is executed on the C side, so can do calculations. In practice it would be easier // to just use call $(getTime() 1), but this demonstrates variables are accessible. int slangTime $(cppTime 1);实际的标准库中$()最常见的用途是引用 C 枚举值例如core.meta.slang第 92 行__builtin_requirement($((int)BuiltinRequirementKind::DefaultInitializableConstructor))以及__intrinsic_type($(kIROp_ForwardDiffFuncType))见 core.meta.slang——IR opcode 是 C 侧可见的枚举值因此可以直接展开为整数。属性Attributes[__readNone]标注一个函数其结果严格基于参数值计算不会通过任何指针参数读写也不会读写调用方可观察到的其他状态。可视为对该函数纯函数性质的声明供编译器做更激进的优化推断。[__NoSideEffect]指定某函数声明没有任何可观察的副作用。[__unsafeForceInlineEarly]在非常早期的编译阶段内联其所含代码。更早的内联时机允许某些普通内联无法完成的变换得以进行但也意味着必须小心使用——在更复杂的场景下它可能产生意料之外的结果。[__NonCopyableType]将某类型标记为不可复制使 SSA静态单赋值pass 跳过将该类型变量转为 SSA 值的处理。[__AlwaysFoldIntoUseSiteAttribute]对被修饰函数的调用应始终折叠fold进其使用点。[KnownBuiltin(name)][KnownBuiltin(name)]属性允许编译器在编译期间识别该声明即使混淆obfuscation或链接优化已移除相关符号。Intrinsics__target_intrinsic(target, expansion)这是使用最广泛、也最复杂的 intrinsic。置于声明上描述该声明应如何针对某个目标平台发射。复杂性在于expansion通过一系列规则被应用。target是一个目标能力target capability常见取值即该 intrinsic 的发射目标hlsl—— HLSLglsl—— GLSLcuda—— CUDAcpp—— C 输出用于 exe、共享库或 host-callablespirv—— Slang 的 SPIR-V 直发机制函数定义可以同时拥有target_intrinsic和函数体此时函数体用于未定义target_intrinsic的目标平台。如果 intrinsic 可以原样发射expansion可以省略如果只需改名参数可原样传递则只需给出要展开成的名字且不带()此时也不必写成带引号的字符串直接写标识符名即可。目前 HLSL 有特殊处理假设只要存在某个声明它就可以逐字verbatim发射到 HLSL。target也可以是能力原子capability atom原子列表见 slang-capability-defs.h。这里值得注意的一点是对某个特定目标同一效果可能有多种实现途径——例如GL_NV_ray_tracing与GL_EXT_ray_tracing是 Vulkan 通过 GLSL 可用的两个不同光线追踪扩展。-profile选项可以消歧义出实际想要哪个扩展而target_intrinsic上使用该能力名即可指定针对该特定扩展的实现方式。展开机制在slang-intrinsic-expand.cpp中实现声明见 slang-intrinsic-expand.h该文件是最新的权威参考。expansion值可以是字符串或标识符。若是标识符则原样发射替换掉被修饰声明的名字。expansion字符串中需要被替换的部分以$前缀标记占位符含义$0-9该索引处的参数对于方法调用$0是this$T0-9该索引参数的类型若类型是纹理资源派生类型返回其元素类型$TR返回类型$G0-9被该索引处的特化specialization类型/值替换$S0-9该索引处泛型generic的标量类型$p用于纹理操作按 GLSL 需要生成组合纹理采样器参数$q$p在仅纹理查询如GetDimensions上的对应物当组合Sampler2D风格的值被降级为{texture, sampler}对时会在索引 1 处注入一个额外的 sampler 操作数$q使位置性$NN 1访问器跳过它从而让输出对齐。它不发射任何内容对普通非组合纹理是 no-op$C根据资源关联类型与后备ImageFormat之间是否需要格式转换来改变调用名目前仅 CUDA 实现那里有执行格式转换的 RWTexture 写入专用版本$E有时访问需要缩放。例如 CUDA 中 surface 访问的 x 坐标是按字节寻址的$E返回后备元素的字节大小$cGLSL 纹理访问结果可能需要强制转换。若底层纹理基于halfGLSL 只能以 float 读写因此输出时需要转回 half 类型写入纹理时值必须是 half但无需转换因为 half 可以无问题地强制转换为 float$z对形如t.Foo(s, ...)的 D3D 纹理操作t是TextureT此步负责把等效 GLSL 调用的输出正确 swizzle 成所需形状$N0-9从向量参数提取元素个数用于构造表达式$V0-9将某个标量/向量类型的参数填充为同元素类型的 4 向量$z的逆操作$P基于类型的前缀用于 CUDA 与 C 目标int8_t为I8、float为F32等$[0-9]访问__intrinsic_asm中格式字符串之后显式传递的额外类型/值操作数。类型操作数如泛型类型参数T发射类型名值操作数如let N : int泛型整数参数发射值表达式。额外操作数以逗号分隔序列列在格式字符串字面量之后索引从 0 开始关于$q与$p的实现可参考 slang-intrinsic-expand.hm_argIndexOffset处理字符串编号中缺失 sampler 槽位的情形$pm_skipCombinedSamplerOperand处理lowering 注入 sampler 后字符串未编号的情形$q。真实的__target_intrinsic使用示例来自 hlsl.meta.slang__target_intrinsic(cuda, RayDesc) struct RayDesc { __target_intrinsic(cuda, Origin) float3 Origin; __target_intrinsic(cuda, TMin) float TMin; __target_intrinsic(cuda, Direction) float3 Direction; __target_intrinsic(cuda, TMax) float TMax; };__attributeTarget(astClassName)用于属性attribute指定该属性可以应用于哪个 AST 类及其派生类。__builtin标识该声明是 builtin。__builtin_requirement(requirementKind)一种修饰符指示内建关联类型需求例如Differential。需求值是BuiltinRequirementKind枚举之一可直接通过$()机制指定如 core.meta.slang__magic_type(DefaultInitializableType) interface IDefaultInitializable { __builtin_requirement($((int)BuiltinRequirementKind::DefaultInitializableConstructor)) __init(); }__builtin_type(tag)指定一个内建类型——即BaseType枚举之一的整数值。__magic_type(clsName, tag)用于类型声明之前。clsName是在 SlangC代码中表示该类型的 AST 类名tag是可选整数值在类类型上下文中有附加含义。例如core.meta.slang中__magic_type(DefaultInitializableType)、__magic_type(DifferentiableType)见 core.meta.slang。__intrinsic_type(op)用于指定类型关联的 IR opcode。IR opcode 通常写成$(kIROp_HLSLByteAddressBufferType)这样的形式展开为该 opcode 的整数值因为 opcode 值是 C 可见的枚举值。也可以直接写 opcode 数字但一般不建议op 的 id 并不稳定若 Slang C 代码改动新增或删除了 opcode手写数字很可能出错。核心模块中的示例ByteAddressBuffer__magic_type(HLSLByteAddressBufferType) __intrinsic_type($(kIROp_HLSLByteAddressBufferType)) struct ByteAddressBuffer { // ... };实际代码中同样模式可见于 core.meta.slang__intrinsic_type($(kIROp_ForwardDiffFuncType))与__intrinsic_type($(kIROp_RawPointerType))L1378等。通用机制General__generic用于指定声明为泛型的另一种语法。更常用的形式是在声明名后用列出泛型参数。attribute_syntaxattribute_syntax提供在 Slang 中引入属性类型的机制。基本形式为attribute_syntax [name(parmName: paramType, ...)] : syntaxClass;属性可关联 0 个或多个参数若无参数则不需要()。name要定义的属性名paramName使用属性时指定的参数名paramType参数关联值的类型syntaxClass该属性在检查checked后应创建的 AST 节点类名。示例一无参数__attributeTarget(FuncDecl) attribute_syntax [CudaDeviceExport] : CudaDeviceExportAttribute;定义属性CudaDeviceExport只能应用于FuncDecl或其派生 AST 类型语义检查后它会被转换为 AST 中的CudaDeviceExportAttribute。示例二带参数__attributeTarget(InterfaceDecl) attribute_syntax [anyValueSize(size:int)] : AnyValueSizeAttribute;定义属性anyValueSize可应用于InterfaceDecl及其派生类型它接受一个名为size、类型为int的参数。标准库中的真实用法core.meta.slang__attributeTarget(ModuleDecl) attribute_syntax [ExperimentalModule] : ExperimentalModuleAttribute; __attributeTarget(DeclBase) attribute_syntax [TreatAsDifferentiable] : TreatAsDifferentiableAttribute;RefT允许按引用by reference返回或传递一个值。GLSL/Vulkan 专用机制__glsl_version(version)用于指定后续声明所需的 GLSL 版本号。Slang 发射 GLSL 源码时文件开头的版本号将是所看到的最大版本即所发射代码用到的版本。例如__glsl_version(430)__glsl_extension指定该声明正常工作所需的 GLSL 扩展。带此 intrinsic 的声明输出到 GLSL 时会额外向 GLSL 或 SPIR-V 输出添加#extension。如果多种实现方式可以以相同方式发射参见上文 target 相关小节可以对一个修饰同时应用多个扩展。__spirv_version当声明用于 SPIR-V 目标时取所看到的最高值作为所需 SPIR-V 版本。若经由 GLSLANG 编译该值会被下传给 GLSLANG 以指定所针对的 SPIR-V。示例__spirv_version(1.3)vk::spirv_instruction提供对GL_EXT_spirv_intrinsics扩展的有限使用方式vk::spirv_instruction(op, set)op是 op 的整数值set是可选字符串指定该 op 关联的指令集。示例与__specialized_for_target(glsl)组合用于debugBreak__specialized_for_target(glsl) [[vk::spirv_instruction(1, NonSemantic.DebugBreak)]] void debugBreak();这正是 hlsl.meta.slang 中__glslDebugBreak的真实实现[require(glsl)] __specialized_for_target(glsl) [[vk::spirv_instruction(1, NonSemantic.DebugBreak)]] void __glslDebugBreak();随后debugBreak()通过__target_switch在 HLSL/CUDA/C/LLVM 等目标上分别展开为各自平台的断点内建见 hlsl.meta.slang。CUDA 专用机制__cuda_sm_version声明带有此 intrinsic 并在 CUDA 目标下使用时所看到的最高 shader model 会被下传给下游 CUDA 编译NVRTC。NVAPI 专用机制[__requiresNVAPI]若声明在适用目标D3D11/12的编译中被触达将表明该声明正常工作需要 NVAPI 支持。总结与实践建议回顾整个机制体系可以归纳出几条实用的工程经验先看.meta.slang再动手source/slang/下的core.meta.slang、hlsl.meta.slang、glsl.meta.slang、diff.meta.slang是全部内部机制的活教材阅读它们比看文档更快理解约定。__target_intrinsic是跨平台发射的核心记住$占位符族参数、类型、swizzle、字节缩放、格式化类型前缀等遇到平台差异时优先用它而非写死目标代码。$()让 C 与 Slang 双向互通opcode、BuiltinRequirementKind等 C 枚举都能安全地以$(...)形式嵌入避免手写不稳定数字。新增属性走attribute_syntax结合__attributeTarget限定适用范围语义检查后自动落到对应 AST 属性类。平台版本与扩展用专用修饰符__glsl_version、__glsl_extension、__spirv_version、__cuda_sm_version让发射器自动汇总最大版本要求并注入#extension。最后再次强调原文档的告诫以上机制均处于演进之中、不被视为稳定接口普通 Slang 代码中使用它们需承担随版本升级而失效的风险如需为某目标添加特性优先考虑以标准模块改动的方式推进并始终以slang-intrinsic-expand.cpp与.meta.slang源码为最新事实依据。【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表