Contents

UE4变体研究 源码剖析

在编辑器打开时,会从资源包中LoadPackage,我们这里只关心的是材质问题。每一个Obj,都会调用自己的Obj->ConditionalPostLoad() 这里已经是在加载的后端了,在这里面会调用子类的PostLoad()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
for (int32 i = 0; i < ObjLoaded.Num(); i++)
{
   UObject* Obj = ObjLoaded[i];
   check(Obj);
#if WITH_EDITOR
//一顿操作
#endif
//又一顿操作
   Obj->ConditionalPostLoad();
}

我们只看 UMaterial::PostLoad,如果调用到这里,也就是说我们加载的是一个材质。这是一个非常长的函数。就不在这里展开了。

这里会调用他父类的PostLoad,而UMaterial的父类就是UMaterialInterface,而他们的区别,和主要的成员分析见Unreal Material类的关系 

UMaterialInterface::PostLoad()中又会调用UMaterial::CacheResourceShadersForRendering

  • Cache resource shaders for rendering.
  • If a matching shader map is not found in memory or the DDC, a new one will be compiled.(关于什么是DDC,听过很多次)
  • The results will be applied to this FMaterial in the renderer when they are finished compiling.
  • Note: This modifies material variables used for rendering and is assumed to be called within a FMaterialUpdateContext!
1
2
3
RebuildShadingModelField();//我们首先把ShadingModel重新计算,
FlushResourceShaderMaps();//将我们最终要渲染的MaterialResources,进行卸载
CacheShadersForResources(ShaderPlatform, ResourcesToCache, true);

这里是只对我们将渲染到的level进行cache,在cooked build中,没有着色器编译,但这仍然是注册加载的shadermap(啥?) CacheShadersForResources这是最主要的函数,我们将要进去,看它是如何进行cache,并且看它都cache的什么东西。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void UMaterial::CacheShadersForResources(……)
{
    RebuildExpressionTextureReferences();//就按字面意思理解把
//对每一个将要resource
    for (int32 ResourceIndex = 0; ResourceIndex < ResourcesToCache.Num(); ResourceIndex++)
    {
        FMaterialResource CurrentResource = ResourcesToCache[ResourceIndex];
//最终cache的地方
        const bool bSuccess = CurrentResource->CacheShaders(ShaderPlatform, bApplyCompletedShaderMapForRendering);
        if (!bSuccess)
        {
//如果出现错误的log
        }
    }
}

从上面可以看出,我们最终的cache还是要在回到FMaterialResource身上,这里只是做了保护而已,所以继续往下。

1
2
3
4
5
6
bool FMaterial::CacheShaders(……)
{
        FMaterialShaderMapId NoStaticParametersId;
        GetShaderMapId(Platform, NoStaticParametersId);
        return CacheShaders(NoStaticParametersId, Platform, bApplyCompletedShaderMapForRendering);
}

这里我们终于来到了一个新的天地,因为我们终于不在Umaterial里面了,而是在FMaterialResource的父类FMaterial里面,

这里主要有两个重要函数

一个是FMaterialResource::GetShaderMapId ,另一个是CacheShaders

FMaterialResource::GetShaderMapId

首先就是调用父类的FMaterial::GetShaderMapId(Platform, OutId);这里最终得到的是FMaterialShaderMapId 关于这个结构体和其他相关结构体的内容可以参见Material相关类  GetShaderMapId:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
if (bLoadedCookedShaderMapId)
{
  //开始做的判断是是否已经加载了这个material的shadermap,如果有的话我们还要判断是在哪个线程中就从GameThreadShaderMap获取还是RenderingThreadShaderMap获取。
}    
else
    {
#if WITH_EDITOR
        TArray<FShaderType> ShaderTypes;
        TArray<FVertexFactoryType> VFTypes;
        TArray<const FShaderPipelineType*> ShaderPipelineTypes;
        GetDependentShaderAndVFTypes(Platform, ShaderTypes, ShaderPipelineTypes, VFTypes);
    //省略赋值
        GetReferencedTexturesHash(Platform, OutId.TextureReferencesHash);
#else
   //省略赋值 log
#endif
    }

最开始做的判断是是否已经加载了这个material的shadermap,如果有的话我们还要判断是在哪个线程中就从GameThreadShaderMap获取还是RenderingThreadShaderMap获取。

GetDependentShaderAndVFTypes

否则的话,我们就需要进行ShaderMap的填充,GetDependentShaderAndVFTypes就是填充的关键。

  1. 对所有的VertexFactoryType进行判断
  2. 这个顶点工厂必须是使用在Material里的
  3. 从每一个FShaderType中得到FMeshMaterialShaderType,如果FMeshMaterialShaderType不是空&&FMeshMaterialShaderType可以cache这个VertexFactoryType,那么我们就允许增加这个AddUnique(ShaderType),并且在最后的时候会增加这个VertexFactoryType
  4. 从每一个FShaderPipelineType判断是否用于meshmaterial,并且要判断FShaderPipelineType里面所有的shadertype的,如果它里面的所有的shader都通过判断,那么我们就增加这个FShaderPipelineType
  5. 对所有的FShaderType进行判断
  6. 和上面的一样,不过判断条件VertexFactoryType改为了null
  7. 对所有的FShaderPipelineType进行判断
  8. 同上 其实我们只是对判断依据感兴趣而已。

todo:判断条件是传进来的函数指针,还没有搞清楚来源 这样,我们就讨论完了GetShaderMapId

FMaterialShaderMapId::SetShaderDependencies

下面的函数时FMaterialShaderMapId::SetShaderDependencies,主要就是记录它的hash值,然后记录下来,这里的疑问就出来了。

当我们的FMaterial::GetShaderMapId分析完后,我们就要回到 FMaterialResource::GetShaderMapId里面

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
//这是把所有的材质里使用到的函数,加到OutId.ReferencedFunctions中
Material->AppendReferencedFunctionIdsTo(OutId.ReferencedFunctions);
//这是把所有的材质里使用到的parameter collection,加到OutId.ReferencedParameterCollections中s
Material->AppendReferencedParameterCollectionIdsTo(OutId.ReferencedParameterCollections);
//添加texture的哈希值
Material->GetForceRecompileTextureIdsHash(OutId.TextureReferencesHash);
//如果其有关联的MaterialInstance不为空
if(MaterialInstance)
{
    MaterialInstance->GetBasePropertyOverridesHash(OutId.BasePropertyOverridesHash);
    FStaticParameterSet CompositedStaticParameters;
    MaterialInstance->GetStaticParameterValues(CompositedStaticParameters);
    OutId.UpdateParameterSet(CompositedStaticParameters);       
}

## FMaterial::CacheShaders
算了