ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

UE5 ByteCount 源码级开销

UE5 ByteCount 源码级开销 1. Complex 材质公共开销if (!SubstrateMaterialComplexity.bIsSimple !SubstrateMaterialComplexity.bIsSingle !bCustomEncoding !bIsFastWaterPath) { // Packed Header SubstrateMaterialRequestedSizeByte UintByteSize; // Shared Local Bases SubstrateMaterialRequestedSizeByte UsedSharedLocalBasesCount * SUBSTRATE_PACKED_SHAREDLOCALBASIS_STRIDE_BYTES; }增加Packed Header4 Bytes Shared Local Basis UsedSharedLocalBasesCount × BasisStride触发条件不是 Simple 不是 Single 不是 Custom Encoding 不是 Fast Water这部分是 Complex 材质的公共成本不属于某一个 Feature。2. Simple 材质if (SubstrateMaterialComplexity.bIsSimple) { // Header SubstrateMaterialRequestedSizeByte UintByteSize; // Disney material SubstrateMaterialRequestedSizeByte UintByteSize; break; }增加Simple Header 4 Bytes Disney Material 4 Bytes 总计 8 Bytes3. Single 材质else if (SubstrateMaterialComplexity.bIsSingle) { // Header SubstrateMaterialRequestedSizeByte UintByteSize; }增加Single Header4 BytesF90、Fuzz、SSS 等功能可能使材质进入 Single但它们不一定各自对应一个独立的 UintByteSize。4. Custom Encoding 材质else if (bCustomEncoding) { // Header SubstrateMaterialRequestedSizeByte UintByteSize; }增加Custom Encoding Header4 BytesHair、Eye 等特殊 BSDF 会走这类路径。5. Fast Water 路径else if (bIsFastWaterPath) { // Header Data SubstrateMaterialRequestedSizeByte UintByteSize; // Data SubstrateMaterialRequestedSizeByte UintByteSize; break; }增加Water Header4 Bytes Water Data 4 Bytes 总计 8 Bytes6. Complex 材质的 Weight 数据顶层 Slabelse { // BSDF state with gray scale weight SubstrateMaterialRequestedSizeByte UintByteSize; }增加BSDF State 灰度 Weight4 Bytes非顶层 Slabelse if (bMayHaveColoredWeight) { // BSDF state SubstrateMaterialRequestedSizeByte UintByteSize; // Color weight SubstrateMaterialRequestedSizeByte UintByteSize; // Light transmittance weight SubstrateMaterialRequestedSizeByte UintByteSize; }增加BSDF State 4 Bytes Color Weight 4 Bytes Light Transmittance Weight 4 Bytes 总计 12 Bytes所以同样的 Slab放在顶部和底部Byte Count 可能不同。7. Slab 基础数据进入case SUBSTRATE_BSDF_TYPE_SLAB:后通常会先增加SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize;增加Slab 基础数据8 Bytes这不是某个 Feature 的专属成本而是 Slab 的基础 Payload。8. Edge Color、Second Roughness、Simple Clear Coatif (It.Has(ESubstrateBsdfFeature::EdgeColor) || It.Has(ESubstrateBsdfFeature::SecondRoughness) || It.Has(ESubstrateBsdfFeature::SimpleClearCoat)) { SubstrateMaterialRequestedSizeByte UintByteSize; }增加额外 Slab 参数块4 Bytes重点是这几个条件使用了||Edge Color Second Roughness Simple Clear Coat它们命中任意一个通常只增加一个数据块不是三个 Feature 分别收费。9. 底层 SSS 或非底层 MFP源码会先判断类似const bool bHasSSS It.bIsBottom It.Has(ESubstrateBsdfFeature::SSS); const bool bIsSimpleVolume !It.bIsBottom It.Has(ESubstrateBsdfFeature::MFP);然后if (bHasSSS || bIsSimpleVolume) { SubstrateMaterialRequestedSizeByte UintByteSize; }增加底层 SSS 4 Bytes 非底层 MFP Simple Volume4 Bytes但两者共用同一个条件块bHasSSS || bIsSimpleVolume所以不是SSS 4 MFP 4而是SSS 或 MFP 命中同一个数据块 → 410. Fuzzif (It.Has(ESubstrateBsdfFeature::Fuzz)) { SubstrateMaterialRequestedSizeByte UintByteSize; }增加Fuzz 数据4 Bytes但 Fuzz 还可能改变材质的 Simple/Single/Complex 分类因此实际总成本可能不只是 4 Bytes。11. Glintif (It.Has(ESubstrateBsdfFeature::Glint)) { SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; }增加Glint4 × 4 Bytes 16 BytesGlint 是当前这些 Feature 中直接增加 Payload 较多的一类。12. Specular Profileif (It.Has(ESubstrateBsdfFeature::SpecularProfile)) { SubstrateMaterialRequestedSizeByte UintByteSize; }增加Specular Profile4 Bytes但 Specular Profile 还会使材质进入 Complex Special 路径因此它的最终影响不一定只有 4 Bytes。13. Haircase SUBSTRATE_BSDF_TYPE_HAIR: { SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; break; }增加Hair Custom Data8 Bytes14. Eyecase SUBSTRATE_BSDF_TYPE_EYE: { SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; break; }增加Eye Custom Data8 Bytes如果 Eye 使用额外自定义参数还会继续增加if (It.Has(ESubstrateBsdfFeature::ExCustomParameter)) { SubstrateMaterialRequestedSizeByte UintByteSize; }即每个额外自定义数据块4 BytesUE5.7 的源码分析中也明确展示了 Eye Custom Encoding 会在HLSLMaterialTranslator.cpp中增加额外 uint 数据。UE5.7.1 源码分析15. UnlitUnlit 不会像 Slab 一样增加普通材质 Payloadcase SUBSTRATE_BSDF_TYPE_UNLIT: { // Never stored, it goes directly into the scene as emitted luminance. break; }也就是Unlit 不需要存储完整 Slab 数据汇总逻辑增加量Complex Packed Header4 BytesShared Local Basis数量 × BasisStrideSimple Header4 BytesSimple Disney Material4 BytesSingle Header4 BytesCustom Encoding Header4 BytesFast Water Header4 BytesFast Water Data4 Bytes顶层灰度 Weight4 Bytes非顶层彩色 Weight12 BytesSlab 基础数据8 BytesEdge Color / Second Roughness / Clear Coat4 Bytes底层 SSS 或非底层 MFP4 BytesFuzz4 BytesGlint16 BytesSpecular Profile4 BytesHair Custom Data8 BytesEye Custom Data8 BytesEye 额外自定义参数每项 4 Bytes不应直接理解为 Byte Count 增量的内容下面这些通常不是直接的 UintByteSize但会改变最终结果F90 Anisotropy Shared Local Basis 数量 材质是顶层还是底层 参数混合 材质简化 Simple / Single / Complex 分类 Complex Special 路径 uint32 对齐最后还会进行类似RequestedSizeInUint DivideAndRoundUp( SubstrateMaterialRequestedSizeByte, 4u);因此最终 Byte Count 的正确理解是最终 Byte Count 公共布局 BSDF 状态 Slab 基础数据 Feature 数据块 Shared Local Basis 对齐1.bCustomEncoding表示当前 BSDF 不使用通用 Slab 编码而是使用自己的专用编码方式。典型类型Substrate Hair BSDF Substrate Eye BSDF它们的参数结构和 Slab 不一样所以不能直接按普通 Slab 处理。逻辑通常类似if (BSDFType SUBSTRATE_BSDF_TYPE_HAIR || BSDFType SUBSTRATE_BSDF_TYPE_EYE) { bCustomEncoding true; }影响不走普通 Complex Header 不走普通 Slab Payload 不按 Slab 的 Fuzz / SSS / MFP 分支计算 进入 Hair / Eye 自己的编码逻辑例如case SUBSTRATE_BSDF_TYPE_EYE: UintByteSize; UintByteSize;所以bCustomEncoding不是“增加一个 Feature”而是切换到另一套 BSDF 存储格式2.bIsFastWaterPath表示当前材质是 Single Layer Water并且可以使用专用的快速水体路径。对应Substrate SingleLayerWater BSDF它不是Slab MFP而是一个独立的特殊 BSDF。它通常走else if (bIsFastWaterPath) { // Header Data SubstrateMaterialRequestedSizeByte UintByteSize; // Data SubstrateMaterialRequestedSizeByte UintByteSize; break; }也就是Water Header4 Bytes Water Data 4 Bytes 总计 8 Bytes它会跳过普通 Slab 数据 Complex Shared Local Basis 普通 Slab Feature 分支所以它是一条专门优化过的水体编码路径。注意Substrate SingleLayerWater BSDF和Slab 连接 MFP 做 Simple Volume不是同一种路径。3、Colored Weight 使用条件Colored Weight不是由 MFP 是否为 RGB 直接决定的而主要由材质树拓扑决定。会使用或预留 Colored Weight 的情况存在 Vertical Layer 并且当前 Slab 不是顶层 Slab源码逻辑通常是bMayHaveColoredWeight !It.bIsTop;非顶层 Slab 可能受到上层材质的彩色透射 颜色吸收 Diffuse / F0 颜色影响 Light Transmittance先把所有增加byte count的代码先汇总于此if (!SubstrateMaterialComplexity.bIsSimple !SubstrateMaterialComplexity.bIsSingle !bCustomEncoding !bIsFastWaterPath) // header written later, { // Packed Header SubstrateMaterialRequestedSizeByte UintByteSize; // Shared local bases between BSDFs SubstrateMaterialRequestedSizeByte UsedSharedLocalBasesCount * SUBSTRATE_PACKED_SHAREDLOCALBASIS_STRIDE_BYTES; } switch (It.OperatorType) { case SUBSTRATE_OPERATOR_BSDF: { // we have encountered a new BSDF which directly link to a single closure evaluation SubstrateMaterialClosureCount; // From the compiler side, we can only assume the top layer has gray scale luminance weight. const bool bMayHaveColoredWeight !It.bIsTop; if (SubstrateMaterialComplexity.bIsSimple) { // Header SubstrateMaterialRequestedSizeByte UintByteSize; // Disney material SubstrateMaterialRequestedSizeByte UintByteSize; break; // Stop here } else if (SubstrateMaterialComplexity.bIsSingle) { // Header SubstrateMaterialRequestedSizeByte UintByteSize; } else if (bCustomEncoding) { // Header SubstrateMaterialRequestedSizeByte UintByteSize; } else if (bIsFastWaterPath) { // Header Data SubstrateMaterialRequestedSizeByte UintByteSize; // Data SubstrateMaterialRequestedSizeByte UintByteSize; break; // Stop here } else if (bMayHaveColoredWeight) { // BSDF state SubstrateMaterialRequestedSizeByte UintByteSize; // Color weight SubstrateMaterialRequestedSizeByte UintByteSize; // Light transmittance weight SubstrateMaterialRequestedSizeByte UintByteSize; } else { // BSDF state with gray scale weight SubstrateMaterialRequestedSizeByte UintByteSize; } switch (It.BSDFType) { case SUBSTRATE_BSDF_TYPE_SLAB: { // Compute values closer to the reality for HasSSS and IsSimpleVolume, now that we know that we know the topology of the material. const bool bIsSimpleVolume !It.bIsBottom It.Has(ESubstrateBsdfFeature::MFPPluggedIn); const bool bHasSSS It.bIsBottom It.Has(ESubstrateBsdfFeature::SSS); SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; // When using blendable GBuffer, do not account for featuress byte requests. as downcasting is done SubstrateExport(). // Otherwise this would cause material to not compile as we do not demote feature during simplification. if (Substrate::IsSubstrateBlendableGBufferEnabled(ShaderPlatform)) { break; } if (It.Has(ESubstrateBsdfFeature::EdgeColor | ESubstrateBsdfFeature::SecondRoughnessOrSimpleClearCoat)) { SubstrateMaterialRequestedSizeByte UintByteSize; } if (bHasSSS || bIsSimpleVolume) { SubstrateMaterialRequestedSizeByte UintByteSize; } if (It.Has(ESubstrateBsdfFeature::Fuzz)) { SubstrateMaterialRequestedSizeByte UintByteSize; } if (It.Has(ESubstrateBsdfFeature::Glint) Substrate::IsGlintEnabled(ShaderPlatform)) { SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; } if (It.Has(ESubstrateBsdfFeature::SpecularProfile)) { SubstrateMaterialRequestedSizeByte UintByteSize; } if (It.Has(ESubstrateBsdfFeature::Eye)) { SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; } if (It.Has(ESubstrateBsdfFeature::Hair)) { SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; } break; } case SUBSTRATE_BSDF_TYPE_HAIR: { // Custom encoding SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; break; } case SUBSTRATE_BSDF_TYPE_EYE: { // Custom encoding SubstrateMaterialRequestedSizeByte UintByteSize; SubstrateMaterialRequestedSizeByte UintByteSize; break; }我来计算一下Glint的byte count首先如图一所示Glint属于Complex Special这里是要进去的所以是4 4 8然后如图二它属于else层级的只加48412如图三它属于slab所以是6*4 24 然后24 12等于36计算正确再来计算只连接anisotropy消耗多少byte count首先连接了anisotropy了过后complexity就属于complex了那么这里就会2*48的byte count消耗了这里不属于任何一个标识直接加4目前8412byte count消耗最后只有基础slab数据的开销2*48总消耗812 20byte count消耗再来看为什么glint anisotropy不增加各向异性让我们来看为什么anisotropy glint还是36呢首先anisotroyp glint还是 complex special一样要计算这里header的消耗 2*48老样子走else不涉及任何的分支标识最后这里还是只进入这几个标识6*4 24 总共2488其实最核心的就是因为glint和glintanisotropy的slab类型相同complexity都是complex special新增的anisotropy没有对应feature增加的byte count的开销所以这两个类型最终的总开销是相同的当 Glint 已经把材质推入 Complex Special并且 Shared Local Basis 数量没有增加时再加入 Anisotropy 不会带来额外的 Byte Count它只改变局部 Basis 的类型和内容。最后来一个复合类型的首先上下层覆合关系是complex的所以里面2*48的byte count消耗上面的SubstrateMaterialComplexity是整个材质的MaterialComplexity而不是单个slab的complextiy然后上层slab的operator用的else4下层属于colorweight的3*412最后首先两个slab都要有基础slab信息4*416然后上层slab强行变成了issimplevolume所以再来个4总共计算byte count841216444主要第一看tree header和local basis然后看operator最后看slab这里特别注意这个usesharedlocalbasescount和sharedlocalbasescount没关系别被坑了这里是operator有一个类型是bsdf的那就1就是说有多少没被优化的bsdf slab板实际SharedLocalBasesCount会影响视口里的Memory Transactions但不会影响材质编辑器显示的Byte Count。原因是两处使用了不同的数量。材质编辑器 Byte Count计算 Byte Count 时Normal/Tangent 还没有完成编译无法进行真正的 Basis 去重因此源码直接使用UsedSharedLocalBasesCount SubstrateMaterialEffectiveClosureCount;也就是保守假设一个有效 Closure 一个 Local Basis所以只要 Closure Count 不变实际 Local Bases 从 3 合并为 1编辑器 Byte Count 仍然不变。视口 Memory Transactions最终 Shader 编译完成后UE 已经能够比较 Normal/Tangent 的代码哈希并得到FinalUsedSharedLocalBasesCount运行时 Header 保存的是这个实际数量HEADER_SETSHAREDLOCALBASESCOUNT( Out, InHeader.SharedLocalBases.Count);视口再根据实际数量写入和读取 BasisHeader Normals Packed Header 4 Bytes SharedLocalBasesCount × 4 Bytes因此实际 Local BasesHeader Normals14 1×4 8 Bytes24 2×4 12 Bytes34 3×4 16 Bytes每合并一个 Local Basis实际Memory Transactions就减少4 Bytes / pixel前提是其他 BSDF 数据和可见 Closure 没有变化。所以性能规划里建议明确区分Editor Byte Count 编译期保守预算用于预算检查和材质简化 Viewport Memory Transactions 当前像素实际读取的 Substrate 数据量 Closure Count 实际需要求值的 BSDF 数量 SharedLocalBasesCount 实际唯一 Normal/Tangent Basis 数量你的核心结论完全正确复用相同 Normal/Tangent 是有效优化只是 UE5.7 材质编辑器的 Byte Count 暂时没有体现这项收益收益会在最终 Shader 和视口实际 Memory Transactions 中体现出来。右侧Memory Transactions - Total 44就是当前选中像素实际读取了 44 Bytes。源码用的是SubstrateAddressing.ReadBytes这张图里总差值是材质编辑器 Byte Count 76 Memory Transactions 60 总差值 16 Bytes其中Shared Local Basis 合并节省 8 Bytes 两个非顶层灰度权重节省 8 Bytes灰度 Weight 被压进BSDF.State的 10-bit 字段不需要单独存储 Color Weight。
返回列表