ue4.26 Assertion failed: MeshCommands.Num() == MeshCommandsCSM.Num() 解法
在ue4.26的pc端添加了自定义meshpass xxxPass,切到移动端后产生如下错误:
Assertion failed: MeshCommands.Num() == MeshCommandsCSM.Num()
VisibleMeshDrawCommands of BasePass and MobileBasePassCSM are expected to match.
为了看清楚状况,做一个最简场景进行复现,一个材质名为floor的绿色平面,一个材质名为cartoonTransp的球体,pc平台效果如下:
切换为移动平台后,触发asset fail断点如下:
查看断点数据,可见MeshCommands和MeshCommandsCSM元素数量分别为1和2。
进一步展开细看,可见MeshCommands中的1个元素为floor,MeshCommandsCSM中的两个元素为floor和cartoonTransp:
此种mesh command与阴影comand对不上的情况不会发生在pc端,因为由下面代码可见,pc端直接GenerateDynamicMeshDrawCommands,而移动端在GenerateDynamicMeshDrawCommands之前多一个MergeMobileBasePassMeshDrawCommands过程,在其中会asset两种command数量相等。
void AnyThreadTask() { ... if (bMobileShadingBasePass) { MergeMobileBasePassMeshDrawCommands( ... Context.MeshDrawCommands, Context.MobileBasePassCSMMeshDrawCommands ); GenerateMobileBasePassDynamicMeshDrawCommands( ... Context.MeshDrawCommands, ... ); } else { GenerateDynamicMeshDrawCommands( ... Context.MeshDrawCommands, ... ); } ... }
我们知道GenerateDynamicMeshDrawCommands是完成将meshbatch转为meshdrawcommand的过程,也就是说GenerateDynamicMeshDrawCommands之前还不存在meshdrawcommand,那MergeMobileBasePassMeshDrawCommands中的Context.MeshDrawCommands和Context.MobileBasePassCSMMeshDrawCommands是从哪来的呢?
其实这里的Context.MeshDrawCommands和Context.MobileBasePassCSMMeshDrawCommands根本不是FMeshDrawCommand类型,而是FVisibleMeshDrawCommand类型(书写时偷懒了)。
FVisibleMeshDrawCommand定义如下:
即首先SceneVisibility.cpp中由MeshBatch生成FVisibleMeshDrawCommand,然后是GenerateDynamicMeshDrawCommands由FVisibleMeshDrawCommand和MeshBatch生成FMeshDrawCommand。
由于在SceneVisibility.cpp中,我只让球体对xxxPass生成visibleMeshDrawCommand,所以球体对MobileBasePass不生成visibleMeshDrawCmmand,这是对的。
但由于SceneVisibility.cpp中默认有一句:
if (ShadingPath == EShadingPath::Mobile) { DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::MobileBasePassCSM); }
所以球体会对MobileBasePassCSM这个pass生成visibleMeshDrawCommand。于是产生不匹配。
解法就是让球体对MobileBasePassCSM不生成visibleMeshDrawCommand,即改为:
SceneVisibility.cpp
if (ShadingPath == EShadingPath::Mobile) {
if (!StaticMesh.MaterialRenderProxy->GetMaterial(View.GetFeatureLevel())->GetShadingModels().HasOnlyShadingModel(EMaterialShadingModel::MSM_Toon_01_TR)) {
DrawCommandPacket.AddCommandsForMesh(PrimitiveIndex, PrimitiveSceneInfo, StaticMeshRelevance, StaticMesh, Scene, bCanCache, EMeshPass::MobileBasePassCSM); } }
再运行切移动端,就不会asset fail了,不过球体有影子但本身不可见,如下:
原因是我只在FDeferredShadingSceneRenderer::Render中调用了RenderxxxPass,但没在FMobileSceneRenderer::RenderDeferred中调用RenderxxxPass。